diff options
Diffstat (limited to '')
61 files changed, 54932 insertions, 0 deletions
diff --git a/lib/x509.h b/lib/x509.h new file mode 100644 index 0000000..3ad21f5 --- /dev/null +++ b/lib/x509.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2000-2012 Free Software Foundation, Inc. + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GnuTLS. + * + * The GnuTLS is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/> + * + */ + +#ifndef GNUTLS_LIB_X509_H +#define GNUTLS_LIB_X509_H + +#include <libtasn1.h> + + +int _gnutls_x509_cert_verify_peers(gnutls_session_t session, + gnutls_typed_vdata_st * data, + unsigned int elements, + unsigned int *status); + +#define PEM_CERT_SEP2 "-----BEGIN X509 CERTIFICATE" +#define PEM_CERT_SEP "-----BEGIN CERTIFICATE" +#define PEM_OCSP_RESPONSE "-----BEGIN OCSP RESPONSE" +#define BARE_PEM_OCSP_RESPONSE "OCSP RESPONSE" + +#define PEM_CRL_SEP "-----BEGIN X509 CRL" + + +int _gnutls_x509_raw_privkey_to_gkey(gnutls_privkey_t * privkey, + const gnutls_datum_t * raw_key, + gnutls_x509_crt_fmt_t type); + +#endif /* GNUTLS_LIB_X509_H */ diff --git a/lib/x509/Makefile.am b/lib/x509/Makefile.am new file mode 100644 index 0000000..5caf8f8 --- /dev/null +++ b/lib/x509/Makefile.am @@ -0,0 +1,91 @@ +## Process this file with automake to produce Makefile.in +# Copyright (C) 2003-2012 Free Software Foundation, Inc. +# +# This file is part of GnuTLS. +# +# The GnuTLS is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public License +# as published by the Free Software Foundation; either version 3 of +# the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/> + +include $(top_srcdir)/lib/common.mk + +AM_CPPFLAGS = \ + -I$(srcdir)/../../gl \ + -I$(builddir)/../../gl \ + -I$(srcdir)/../includes \ + -I$(builddir)/../includes \ + -I$(srcdir)/.. + +if ENABLE_MINITASN1 +AM_CPPFLAGS += -I$(srcdir)/../minitasn1 +endif + +EXTRA_DIST = supported_exts.gperf +BUILT_SOURCES = supported_exts.h + +noinst_LTLIBRARIES = libgnutls_x509.la + +libgnutls_x509_la_SOURCES = \ + common.c key_encode.c \ + common.h key_decode.c \ + time.c \ + crl.c \ + crl_write.c \ + crq.c \ + dn.c \ + attributes.c \ + attributes.h \ + prov-seed.c \ + prov-seed.h \ + extensions.c \ + mpi.c \ + output.c \ + pkcs12.c \ + pkcs12_bag.c \ + pkcs12_encr.c \ + pkcs7.c \ + pkcs7-attrs.c \ + pkcs7-crypt.c pkcs7_int.h \ + privkey.c \ + privkey_pkcs8.c \ + privkey_pkcs8_pbes1.c \ + privkey_openssl.c \ + hostname-verify.c \ + sign.c \ + verify.c \ + x509.c x509_dn.c \ + x509_int.h \ + x509_write.c \ + name_constraints.c \ + verify-high.c \ + verify-high2.c \ + verify-high.h \ + x509_ext.c \ + email-verify.c \ + pkcs7-output.c \ + virt-san.c \ + virt-san.h \ + spki.c \ + x509_ext_int.h \ + tls_features.c \ + krb5.c krb5.h \ + ip.c ip.h ip-in-cidr.h \ + supported_exts.h ocsp.h + +if ENABLE_OCSP +libgnutls_x509_la_SOURCES += ocsp.c ocsp_output.c +endif + +supported_exts.h: $(srcdir)/supported_exts.gperf + $(V_GPERF)$(GPERF) --global-table -t $^ > $@-tmp \ + && sed 's/^const struct supported_exts_st \*/static const struct supported_exts_st \*/' <$@-tmp >$@ \ + && rm -f $@-tmp diff --git a/lib/x509/Makefile.in b/lib/x509/Makefile.in new file mode 100644 index 0000000..84242e0 --- /dev/null +++ b/lib/x509/Makefile.in @@ -0,0 +1,2575 @@ +# Makefile.in generated by automake 1.16.5 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2021 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +# Copyright (C) 2003-2012 Free Software Foundation, Inc. +# +# This file is part of GnuTLS. +# +# The GnuTLS is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public License +# as published by the Free Software Foundation; either version 3 of +# the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/> + +VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +@ENABLE_MINITASN1_TRUE@am__append_1 = -I$(srcdir)/../minitasn1 +@ENABLE_OCSP_TRUE@am__append_2 = ocsp.c ocsp_output.c +subdir = lib/x509 +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/lib/unistring/m4/gnulib-comp.m4 \ + $(top_srcdir)/lib/unistring/m4/inline.m4 \ + $(top_srcdir)/lib/unistring/m4/libunistring-base.m4 \ + $(top_srcdir)/src/gl/m4/atoll.m4 \ + $(top_srcdir)/src/gl/m4/bison.m4 \ + $(top_srcdir)/src/gl/m4/calloc.m4 \ + $(top_srcdir)/src/gl/m4/clock_time.m4 \ + $(top_srcdir)/src/gl/m4/codeset.m4 \ + $(top_srcdir)/src/gl/m4/ctype_h.m4 \ + $(top_srcdir)/src/gl/m4/environ.m4 \ + $(top_srcdir)/src/gl/m4/error.m4 \ + $(top_srcdir)/src/gl/m4/fdopen.m4 \ + $(top_srcdir)/src/gl/m4/flexmember.m4 \ + $(top_srcdir)/src/gl/m4/fpending.m4 \ + $(top_srcdir)/src/gl/m4/fpieee.m4 \ + $(top_srcdir)/src/gl/m4/fseek.m4 \ + $(top_srcdir)/src/gl/m4/ftruncate.m4 \ + $(top_srcdir)/src/gl/m4/getaddrinfo.m4 \ + $(top_srcdir)/src/gl/m4/getcwd.m4 \ + $(top_srcdir)/src/gl/m4/getpagesize.m4 \ + $(top_srcdir)/src/gl/m4/getpass.m4 \ + $(top_srcdir)/src/gl/m4/getprogname.m4 \ + $(top_srcdir)/src/gl/m4/gettime.m4 \ + $(top_srcdir)/src/gl/m4/gnulib-comp.m4 \ + $(top_srcdir)/src/gl/m4/hostent.m4 \ + $(top_srcdir)/src/gl/m4/intl-thread-locale.m4 \ + $(top_srcdir)/src/gl/m4/inttostr.m4 \ + $(top_srcdir)/src/gl/m4/ioctl.m4 \ + $(top_srcdir)/src/gl/m4/isblank.m4 \ + $(top_srcdir)/src/gl/m4/langinfo_h.m4 \ + $(top_srcdir)/src/gl/m4/lcmessage.m4 \ + $(top_srcdir)/src/gl/m4/locale-fr.m4 \ + $(top_srcdir)/src/gl/m4/locale-ja.m4 \ + $(top_srcdir)/src/gl/m4/locale-tr.m4 \ + $(top_srcdir)/src/gl/m4/locale-zh.m4 \ + $(top_srcdir)/src/gl/m4/locale_h.m4 \ + $(top_srcdir)/src/gl/m4/localename.m4 \ + $(top_srcdir)/src/gl/m4/lstat.m4 \ + $(top_srcdir)/src/gl/m4/mktime.m4 \ + $(top_srcdir)/src/gl/m4/nanosleep.m4 \ + $(top_srcdir)/src/gl/m4/nstrftime.m4 \ + $(top_srcdir)/src/gl/m4/parse-datetime.m4 \ + $(top_srcdir)/src/gl/m4/perror.m4 \ + $(top_srcdir)/src/gl/m4/pipe.m4 \ + $(top_srcdir)/src/gl/m4/pthread-thread.m4 \ + $(top_srcdir)/src/gl/m4/pthread_h.m4 \ + $(top_srcdir)/src/gl/m4/pthread_sigmask.m4 \ + $(top_srcdir)/src/gl/m4/putenv.m4 \ + $(top_srcdir)/src/gl/m4/raise.m4 \ + $(top_srcdir)/src/gl/m4/reallocarray.m4 \ + $(top_srcdir)/src/gl/m4/sched_h.m4 \ + $(top_srcdir)/src/gl/m4/sched_yield.m4 \ + $(top_srcdir)/src/gl/m4/select.m4 \ + $(top_srcdir)/src/gl/m4/semaphore.m4 \ + $(top_srcdir)/src/gl/m4/servent.m4 \ + $(top_srcdir)/src/gl/m4/setenv.m4 \ + $(top_srcdir)/src/gl/m4/setlocale.m4 \ + $(top_srcdir)/src/gl/m4/setlocale_null.m4 \ + $(top_srcdir)/src/gl/m4/sigaction.m4 \ + $(top_srcdir)/src/gl/m4/signal_h.m4 \ + $(top_srcdir)/src/gl/m4/signalblocking.m4 \ + $(top_srcdir)/src/gl/m4/sleep.m4 \ + $(top_srcdir)/src/gl/m4/sockets.m4 \ + $(top_srcdir)/src/gl/m4/strerror.m4 \ + $(top_srcdir)/src/gl/m4/strerror_r.m4 \ + $(top_srcdir)/src/gl/m4/strtoll.m4 \ + $(top_srcdir)/src/gl/m4/symlink.m4 \ + $(top_srcdir)/src/gl/m4/sys_ioctl_h.m4 \ + $(top_srcdir)/src/gl/m4/sys_select_h.m4 \ + $(top_srcdir)/src/gl/m4/thread.m4 \ + $(top_srcdir)/src/gl/m4/time_rz.m4 \ + $(top_srcdir)/src/gl/m4/timegm.m4 \ + $(top_srcdir)/src/gl/m4/timespec.m4 \ + $(top_srcdir)/src/gl/m4/tm_gmtoff.m4 \ + $(top_srcdir)/src/gl/m4/tzset.m4 \ + $(top_srcdir)/src/gl/m4/usleep.m4 \ + $(top_srcdir)/src/gl/m4/visibility.m4 \ + $(top_srcdir)/src/gl/m4/xalloc.m4 \ + $(top_srcdir)/src/gl/m4/yield.m4 $(top_srcdir)/m4/00gnulib.m4 \ + $(top_srcdir)/m4/__inline.m4 \ + $(top_srcdir)/m4/absolute-header.m4 $(top_srcdir)/m4/alloca.m4 \ + $(top_srcdir)/m4/arpa_inet_h.m4 \ + $(top_srcdir)/m4/ax_ac_append_to_file.m4 \ + $(top_srcdir)/m4/ax_ac_print_to_file.m4 \ + $(top_srcdir)/m4/ax_add_am_macro_static.m4 \ + $(top_srcdir)/m4/ax_am_macros_static.m4 \ + $(top_srcdir)/m4/ax_check_gnu_make.m4 \ + $(top_srcdir)/m4/ax_code_coverage.m4 \ + $(top_srcdir)/m4/ax_file_escapes.m4 \ + $(top_srcdir)/m4/builtin-expect.m4 \ + $(top_srcdir)/m4/byteswap.m4 $(top_srcdir)/m4/close.m4 \ + $(top_srcdir)/m4/double-slash-root.m4 $(top_srcdir)/m4/dup2.m4 \ + $(top_srcdir)/m4/eealloc.m4 $(top_srcdir)/m4/errno_h.m4 \ + $(top_srcdir)/m4/explicit_bzero.m4 \ + $(top_srcdir)/m4/exponentd.m4 $(top_srcdir)/m4/extensions.m4 \ + $(top_srcdir)/m4/extern-inline.m4 $(top_srcdir)/m4/fcntl-o.m4 \ + $(top_srcdir)/m4/fcntl.m4 $(top_srcdir)/m4/fcntl_h.m4 \ + $(top_srcdir)/m4/float_h.m4 $(top_srcdir)/m4/fopen.m4 \ + $(top_srcdir)/m4/free.m4 $(top_srcdir)/m4/fseeko.m4 \ + $(top_srcdir)/m4/fstat.m4 $(top_srcdir)/m4/ftell.m4 \ + $(top_srcdir)/m4/ftello.m4 $(top_srcdir)/m4/func.m4 \ + $(top_srcdir)/m4/getdelim.m4 $(top_srcdir)/m4/getdtablesize.m4 \ + $(top_srcdir)/m4/getline.m4 $(top_srcdir)/m4/gettext.m4 \ + $(top_srcdir)/m4/gettimeofday.m4 \ + $(top_srcdir)/m4/gnulib-common.m4 \ + $(top_srcdir)/m4/gnulib-comp.m4 $(top_srcdir)/m4/gtk-doc.m4 \ + $(top_srcdir)/m4/guile.m4 $(top_srcdir)/m4/hooks.m4 \ + $(top_srcdir)/m4/host-cpu-c-abi.m4 $(top_srcdir)/m4/iconv.m4 \ + $(top_srcdir)/m4/include_next.m4 $(top_srcdir)/m4/inet_ntop.m4 \ + $(top_srcdir)/m4/inet_pton.m4 $(top_srcdir)/m4/intlmacosx.m4 \ + $(top_srcdir)/m4/intmax_t.m4 $(top_srcdir)/m4/inttypes.m4 \ + $(top_srcdir)/m4/inttypes_h.m4 $(top_srcdir)/m4/largefile.m4 \ + $(top_srcdir)/m4/ld-output-def.m4 \ + $(top_srcdir)/m4/ld-version-script.m4 $(top_srcdir)/m4/ldd.m4 \ + $(top_srcdir)/m4/lib-ld.m4 $(top_srcdir)/m4/lib-link.m4 \ + $(top_srcdir)/m4/lib-prefix.m4 $(top_srcdir)/m4/libtool.m4 \ + $(top_srcdir)/m4/limits-h.m4 $(top_srcdir)/m4/lock.m4 \ + $(top_srcdir)/m4/lseek.m4 $(top_srcdir)/m4/ltoptions.m4 \ + $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ + $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/m4/malloc.m4 \ + $(top_srcdir)/m4/malloca.m4 $(top_srcdir)/m4/manywarnings.m4 \ + $(top_srcdir)/m4/memchr.m4 $(top_srcdir)/m4/memmem.m4 \ + $(top_srcdir)/m4/minmax.m4 $(top_srcdir)/m4/mmap-anon.m4 \ + $(top_srcdir)/m4/mode_t.m4 $(top_srcdir)/m4/msvc-inval.m4 \ + $(top_srcdir)/m4/msvc-nothrow.m4 $(top_srcdir)/m4/multiarch.m4 \ + $(top_srcdir)/m4/netdb_h.m4 $(top_srcdir)/m4/netinet_in_h.m4 \ + $(top_srcdir)/m4/nls.m4 $(top_srcdir)/m4/off_t.m4 \ + $(top_srcdir)/m4/open-cloexec.m4 \ + $(top_srcdir)/m4/open-slash.m4 $(top_srcdir)/m4/open.m4 \ + $(top_srcdir)/m4/pathmax.m4 $(top_srcdir)/m4/pkg.m4 \ + $(top_srcdir)/m4/po.m4 $(top_srcdir)/m4/printf.m4 \ + $(top_srcdir)/m4/progtest.m4 \ + $(top_srcdir)/m4/pthread_rwlock_rdlock.m4 \ + $(top_srcdir)/m4/read-file.m4 $(top_srcdir)/m4/realloc.m4 \ + $(top_srcdir)/m4/secure_getenv.m4 $(top_srcdir)/m4/size_max.m4 \ + $(top_srcdir)/m4/snprintf.m4 $(top_srcdir)/m4/socketlib.m4 \ + $(top_srcdir)/m4/socklen.m4 $(top_srcdir)/m4/sockpfaf.m4 \ + $(top_srcdir)/m4/ssize_t.m4 $(top_srcdir)/m4/stat-time.m4 \ + $(top_srcdir)/m4/stat.m4 $(top_srcdir)/m4/stdalign.m4 \ + $(top_srcdir)/m4/stdbool.m4 $(top_srcdir)/m4/stddef_h.m4 \ + $(top_srcdir)/m4/stdint.m4 $(top_srcdir)/m4/stdint_h.m4 \ + $(top_srcdir)/m4/stdio_h.m4 $(top_srcdir)/m4/stdlib_h.m4 \ + $(top_srcdir)/m4/stpcpy.m4 $(top_srcdir)/m4/strcase.m4 \ + $(top_srcdir)/m4/strdup.m4 $(top_srcdir)/m4/string_h.m4 \ + $(top_srcdir)/m4/strings_h.m4 $(top_srcdir)/m4/strndup.m4 \ + $(top_srcdir)/m4/strnlen.m4 $(top_srcdir)/m4/strtok_r.m4 \ + $(top_srcdir)/m4/strverscmp.m4 \ + $(top_srcdir)/m4/sys_socket_h.m4 \ + $(top_srcdir)/m4/sys_stat_h.m4 $(top_srcdir)/m4/sys_time_h.m4 \ + $(top_srcdir)/m4/sys_types_h.m4 $(top_srcdir)/m4/sys_uio_h.m4 \ + $(top_srcdir)/m4/threadlib.m4 $(top_srcdir)/m4/time_h.m4 \ + $(top_srcdir)/m4/time_r.m4 $(top_srcdir)/m4/ungetc.m4 \ + $(top_srcdir)/m4/unistd_h.m4 \ + $(top_srcdir)/m4/valgrind-tests.m4 \ + $(top_srcdir)/m4/vasnprintf.m4 $(top_srcdir)/m4/vasprintf.m4 \ + $(top_srcdir)/m4/vsnprintf.m4 $(top_srcdir)/m4/warn-on-use.m4 \ + $(top_srcdir)/m4/warnings.m4 $(top_srcdir)/m4/wchar_h.m4 \ + $(top_srcdir)/m4/wchar_t.m4 $(top_srcdir)/m4/wint_t.m4 \ + $(top_srcdir)/m4/xsize.m4 $(top_srcdir)/m4/zzgnulib.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +LTLIBRARIES = $(noinst_LTLIBRARIES) +libgnutls_x509_la_LIBADD = +am__libgnutls_x509_la_SOURCES_DIST = common.c key_encode.c common.h \ + key_decode.c time.c crl.c crl_write.c crq.c dn.c attributes.c \ + attributes.h prov-seed.c prov-seed.h extensions.c mpi.c \ + output.c pkcs12.c pkcs12_bag.c pkcs12_encr.c pkcs7.c \ + pkcs7-attrs.c pkcs7-crypt.c pkcs7_int.h privkey.c \ + privkey_pkcs8.c privkey_pkcs8_pbes1.c privkey_openssl.c \ + hostname-verify.c sign.c verify.c x509.c x509_dn.c x509_int.h \ + x509_write.c name_constraints.c verify-high.c verify-high2.c \ + verify-high.h x509_ext.c email-verify.c pkcs7-output.c \ + virt-san.c virt-san.h spki.c x509_ext_int.h tls_features.c \ + krb5.c krb5.h ip.c ip.h ip-in-cidr.h supported_exts.h ocsp.h \ + ocsp.c ocsp_output.c +@ENABLE_OCSP_TRUE@am__objects_1 = ocsp.lo ocsp_output.lo +am_libgnutls_x509_la_OBJECTS = common.lo key_encode.lo key_decode.lo \ + time.lo crl.lo crl_write.lo crq.lo dn.lo attributes.lo \ + prov-seed.lo extensions.lo mpi.lo output.lo pkcs12.lo \ + pkcs12_bag.lo pkcs12_encr.lo pkcs7.lo pkcs7-attrs.lo \ + pkcs7-crypt.lo privkey.lo privkey_pkcs8.lo \ + privkey_pkcs8_pbes1.lo privkey_openssl.lo hostname-verify.lo \ + sign.lo verify.lo x509.lo x509_dn.lo x509_write.lo \ + name_constraints.lo verify-high.lo verify-high2.lo x509_ext.lo \ + email-verify.lo pkcs7-output.lo virt-san.lo spki.lo \ + tls_features.lo krb5.lo ip.lo $(am__objects_1) +libgnutls_x509_la_OBJECTS = $(am_libgnutls_x509_la_OBJECTS) +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +am__v_lt_1 = +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/build-aux/depcomp +am__maybe_remake_depfiles = depfiles +am__depfiles_remade = ./$(DEPDIR)/attributes.Plo \ + ./$(DEPDIR)/common.Plo ./$(DEPDIR)/crl.Plo \ + ./$(DEPDIR)/crl_write.Plo ./$(DEPDIR)/crq.Plo \ + ./$(DEPDIR)/dn.Plo ./$(DEPDIR)/email-verify.Plo \ + ./$(DEPDIR)/extensions.Plo ./$(DEPDIR)/hostname-verify.Plo \ + ./$(DEPDIR)/ip.Plo ./$(DEPDIR)/key_decode.Plo \ + ./$(DEPDIR)/key_encode.Plo ./$(DEPDIR)/krb5.Plo \ + ./$(DEPDIR)/mpi.Plo ./$(DEPDIR)/name_constraints.Plo \ + ./$(DEPDIR)/ocsp.Plo ./$(DEPDIR)/ocsp_output.Plo \ + ./$(DEPDIR)/output.Plo ./$(DEPDIR)/pkcs12.Plo \ + ./$(DEPDIR)/pkcs12_bag.Plo ./$(DEPDIR)/pkcs12_encr.Plo \ + ./$(DEPDIR)/pkcs7-attrs.Plo ./$(DEPDIR)/pkcs7-crypt.Plo \ + ./$(DEPDIR)/pkcs7-output.Plo ./$(DEPDIR)/pkcs7.Plo \ + ./$(DEPDIR)/privkey.Plo ./$(DEPDIR)/privkey_openssl.Plo \ + ./$(DEPDIR)/privkey_pkcs8.Plo \ + ./$(DEPDIR)/privkey_pkcs8_pbes1.Plo ./$(DEPDIR)/prov-seed.Plo \ + ./$(DEPDIR)/sign.Plo ./$(DEPDIR)/spki.Plo ./$(DEPDIR)/time.Plo \ + ./$(DEPDIR)/tls_features.Plo ./$(DEPDIR)/verify-high.Plo \ + ./$(DEPDIR)/verify-high2.Plo ./$(DEPDIR)/verify.Plo \ + ./$(DEPDIR)/virt-san.Plo ./$(DEPDIR)/x509.Plo \ + ./$(DEPDIR)/x509_dn.Plo ./$(DEPDIR)/x509_ext.Plo \ + ./$(DEPDIR)/x509_write.Plo +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) +am__v_CC_0 = @echo " CC " $@; +am__v_CC_1 = +CCLD = $(CC) +LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_@AM_V@) +am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) +am__v_CCLD_0 = @echo " CCLD " $@; +am__v_CCLD_1 = +SOURCES = $(libgnutls_x509_la_SOURCES) +DIST_SOURCES = $(am__libgnutls_x509_la_SOURCES_DIST) +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +am__DIST_COMMON = $(srcdir)/Makefile.in \ + $(top_srcdir)/build-aux/depcomp $(top_srcdir)/lib/common.mk +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +AARCH64_CCASFLAGS = @AARCH64_CCASFLAGS@ +ACLOCAL = @ACLOCAL@ +ALLOCA = @ALLOCA@ +ALLOCA_H = @ALLOCA_H@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AM_VALGRINDFLAGS = @AM_VALGRINDFLAGS@ +APPLE_UNIVERSAL_BUILD = @APPLE_UNIVERSAL_BUILD@ +AR = @AR@ +ARFLAGS = @ARFLAGS@ +ASN1PARSER = @ASN1PARSER@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BITSIZEOF_PTRDIFF_T = @BITSIZEOF_PTRDIFF_T@ +BITSIZEOF_SIG_ATOMIC_T = @BITSIZEOF_SIG_ATOMIC_T@ +BITSIZEOF_SIZE_T = @BITSIZEOF_SIZE_T@ +BITSIZEOF_WCHAR_T = @BITSIZEOF_WCHAR_T@ +BITSIZEOF_WINT_T = @BITSIZEOF_WINT_T@ +BYTESWAP_H = @BYTESWAP_H@ +CC = @CC@ +CCAS = @CCAS@ +CCASDEPMODE = @CCASDEPMODE@ +CCASFLAGS = @CCASFLAGS@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CFLAG_VISIBILITY = @CFLAG_VISIBILITY@ +CMOCKA_CFLAGS = @CMOCKA_CFLAGS@ +CMOCKA_LIBS = @CMOCKA_LIBS@ +CODE_COVERAGE_CFLAGS = @CODE_COVERAGE_CFLAGS@ +CODE_COVERAGE_CPPFLAGS = @CODE_COVERAGE_CPPFLAGS@ +CODE_COVERAGE_CXXFLAGS = @CODE_COVERAGE_CXXFLAGS@ +CODE_COVERAGE_ENABLED = @CODE_COVERAGE_ENABLED@ +CODE_COVERAGE_LIBS = @CODE_COVERAGE_LIBS@ +CONFIG_INCLUDE = @CONFIG_INCLUDE@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CRYWRAP_PATCHLEVEL = @CRYWRAP_PATCHLEVEL@ +CSCOPE = @CSCOPE@ +CTAGS = @CTAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CXX_LT_AGE = @CXX_LT_AGE@ +CXX_LT_CURRENT = @CXX_LT_CURRENT@ +CXX_LT_REVISION = @CXX_LT_REVISION@ +CYGPATH_W = @CYGPATH_W@ +DEFAULT_VALGRINDFLAGS = @DEFAULT_VALGRINDFLAGS@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DLL_SSL_VERSION = @DLL_SSL_VERSION@ +DLL_VERSION = @DLL_VERSION@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EMULTIHOP_HIDDEN = @EMULTIHOP_HIDDEN@ +EMULTIHOP_VALUE = @EMULTIHOP_VALUE@ +ENABLE_PADLOCK = @ENABLE_PADLOCK@ +ENOLINK_HIDDEN = @ENOLINK_HIDDEN@ +ENOLINK_VALUE = @ENOLINK_VALUE@ +EOVERFLOW_HIDDEN = @EOVERFLOW_HIDDEN@ +EOVERFLOW_VALUE = @EOVERFLOW_VALUE@ +ERRNO_H = @ERRNO_H@ +ETAGS = @ETAGS@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +FILECMD = @FILECMD@ +FIPS140_LIBS = @FIPS140_LIBS@ +FLOAT_H = @FLOAT_H@ +GCOV = @GCOV@ +GENHTML = @GENHTML@ +GETADDRINFO_LIB = @GETADDRINFO_LIB@ +GETTEXT_MACRO_VERSION = @GETTEXT_MACRO_VERSION@ +GL_GGL_GNULIB_ACCEPT = @GL_GGL_GNULIB_ACCEPT@ +GL_GGL_GNULIB_ACCEPT4 = @GL_GGL_GNULIB_ACCEPT4@ +GL_GGL_GNULIB_ACCESS = @GL_GGL_GNULIB_ACCESS@ +GL_GGL_GNULIB_ALIGNED_ALLOC = @GL_GGL_GNULIB_ALIGNED_ALLOC@ +GL_GGL_GNULIB_ATOLL = @GL_GGL_GNULIB_ATOLL@ +GL_GGL_GNULIB_BIND = @GL_GGL_GNULIB_BIND@ +GL_GGL_GNULIB_BTOWC = @GL_GGL_GNULIB_BTOWC@ +GL_GGL_GNULIB_CALLOC_POSIX = @GL_GGL_GNULIB_CALLOC_POSIX@ +GL_GGL_GNULIB_CANONICALIZE_FILE_NAME = @GL_GGL_GNULIB_CANONICALIZE_FILE_NAME@ +GL_GGL_GNULIB_CHDIR = @GL_GGL_GNULIB_CHDIR@ +GL_GGL_GNULIB_CHOWN = @GL_GGL_GNULIB_CHOWN@ +GL_GGL_GNULIB_CLOSE = @GL_GGL_GNULIB_CLOSE@ +GL_GGL_GNULIB_CONNECT = @GL_GGL_GNULIB_CONNECT@ +GL_GGL_GNULIB_COPY_FILE_RANGE = @GL_GGL_GNULIB_COPY_FILE_RANGE@ +GL_GGL_GNULIB_CREAT = @GL_GGL_GNULIB_CREAT@ +GL_GGL_GNULIB_CTIME = @GL_GGL_GNULIB_CTIME@ +GL_GGL_GNULIB_DPRINTF = @GL_GGL_GNULIB_DPRINTF@ +GL_GGL_GNULIB_DUP = @GL_GGL_GNULIB_DUP@ +GL_GGL_GNULIB_DUP2 = @GL_GGL_GNULIB_DUP2@ +GL_GGL_GNULIB_DUP3 = @GL_GGL_GNULIB_DUP3@ +GL_GGL_GNULIB_DUPLOCALE = @GL_GGL_GNULIB_DUPLOCALE@ +GL_GGL_GNULIB_ENVIRON = @GL_GGL_GNULIB_ENVIRON@ +GL_GGL_GNULIB_EUIDACCESS = @GL_GGL_GNULIB_EUIDACCESS@ +GL_GGL_GNULIB_EXECL = @GL_GGL_GNULIB_EXECL@ +GL_GGL_GNULIB_EXECLE = @GL_GGL_GNULIB_EXECLE@ +GL_GGL_GNULIB_EXECLP = @GL_GGL_GNULIB_EXECLP@ +GL_GGL_GNULIB_EXECV = @GL_GGL_GNULIB_EXECV@ +GL_GGL_GNULIB_EXECVE = @GL_GGL_GNULIB_EXECVE@ +GL_GGL_GNULIB_EXECVP = @GL_GGL_GNULIB_EXECVP@ +GL_GGL_GNULIB_EXECVPE = @GL_GGL_GNULIB_EXECVPE@ +GL_GGL_GNULIB_EXPLICIT_BZERO = @GL_GGL_GNULIB_EXPLICIT_BZERO@ +GL_GGL_GNULIB_FACCESSAT = @GL_GGL_GNULIB_FACCESSAT@ +GL_GGL_GNULIB_FCHDIR = @GL_GGL_GNULIB_FCHDIR@ +GL_GGL_GNULIB_FCHMODAT = @GL_GGL_GNULIB_FCHMODAT@ +GL_GGL_GNULIB_FCHOWNAT = @GL_GGL_GNULIB_FCHOWNAT@ +GL_GGL_GNULIB_FCLOSE = @GL_GGL_GNULIB_FCLOSE@ +GL_GGL_GNULIB_FCNTL = @GL_GGL_GNULIB_FCNTL@ +GL_GGL_GNULIB_FDATASYNC = @GL_GGL_GNULIB_FDATASYNC@ +GL_GGL_GNULIB_FDOPEN = @GL_GGL_GNULIB_FDOPEN@ +GL_GGL_GNULIB_FFLUSH = @GL_GGL_GNULIB_FFLUSH@ +GL_GGL_GNULIB_FFS = @GL_GGL_GNULIB_FFS@ +GL_GGL_GNULIB_FFSL = @GL_GGL_GNULIB_FFSL@ +GL_GGL_GNULIB_FFSLL = @GL_GGL_GNULIB_FFSLL@ +GL_GGL_GNULIB_FGETC = @GL_GGL_GNULIB_FGETC@ +GL_GGL_GNULIB_FGETS = @GL_GGL_GNULIB_FGETS@ +GL_GGL_GNULIB_FOPEN = @GL_GGL_GNULIB_FOPEN@ +GL_GGL_GNULIB_FPRINTF = @GL_GGL_GNULIB_FPRINTF@ +GL_GGL_GNULIB_FPRINTF_POSIX = @GL_GGL_GNULIB_FPRINTF_POSIX@ +GL_GGL_GNULIB_FPURGE = @GL_GGL_GNULIB_FPURGE@ +GL_GGL_GNULIB_FPUTC = @GL_GGL_GNULIB_FPUTC@ +GL_GGL_GNULIB_FPUTS = @GL_GGL_GNULIB_FPUTS@ +GL_GGL_GNULIB_FREAD = @GL_GGL_GNULIB_FREAD@ +GL_GGL_GNULIB_FREE_POSIX = @GL_GGL_GNULIB_FREE_POSIX@ +GL_GGL_GNULIB_FREOPEN = @GL_GGL_GNULIB_FREOPEN@ +GL_GGL_GNULIB_FSCANF = @GL_GGL_GNULIB_FSCANF@ +GL_GGL_GNULIB_FSEEK = @GL_GGL_GNULIB_FSEEK@ +GL_GGL_GNULIB_FSEEKO = @GL_GGL_GNULIB_FSEEKO@ +GL_GGL_GNULIB_FSTAT = @GL_GGL_GNULIB_FSTAT@ +GL_GGL_GNULIB_FSTATAT = @GL_GGL_GNULIB_FSTATAT@ +GL_GGL_GNULIB_FSYNC = @GL_GGL_GNULIB_FSYNC@ +GL_GGL_GNULIB_FTELL = @GL_GGL_GNULIB_FTELL@ +GL_GGL_GNULIB_FTELLO = @GL_GGL_GNULIB_FTELLO@ +GL_GGL_GNULIB_FTRUNCATE = @GL_GGL_GNULIB_FTRUNCATE@ +GL_GGL_GNULIB_FUTIMENS = @GL_GGL_GNULIB_FUTIMENS@ +GL_GGL_GNULIB_FWRITE = @GL_GGL_GNULIB_FWRITE@ +GL_GGL_GNULIB_GETADDRINFO = @GL_GGL_GNULIB_GETADDRINFO@ +GL_GGL_GNULIB_GETC = @GL_GGL_GNULIB_GETC@ +GL_GGL_GNULIB_GETCHAR = @GL_GGL_GNULIB_GETCHAR@ +GL_GGL_GNULIB_GETCWD = @GL_GGL_GNULIB_GETCWD@ +GL_GGL_GNULIB_GETDELIM = @GL_GGL_GNULIB_GETDELIM@ +GL_GGL_GNULIB_GETDOMAINNAME = @GL_GGL_GNULIB_GETDOMAINNAME@ +GL_GGL_GNULIB_GETDTABLESIZE = @GL_GGL_GNULIB_GETDTABLESIZE@ +GL_GGL_GNULIB_GETENTROPY = @GL_GGL_GNULIB_GETENTROPY@ +GL_GGL_GNULIB_GETGROUPS = @GL_GGL_GNULIB_GETGROUPS@ +GL_GGL_GNULIB_GETHOSTNAME = @GL_GGL_GNULIB_GETHOSTNAME@ +GL_GGL_GNULIB_GETLINE = @GL_GGL_GNULIB_GETLINE@ +GL_GGL_GNULIB_GETLOADAVG = @GL_GGL_GNULIB_GETLOADAVG@ +GL_GGL_GNULIB_GETLOGIN = @GL_GGL_GNULIB_GETLOGIN@ +GL_GGL_GNULIB_GETLOGIN_R = @GL_GGL_GNULIB_GETLOGIN_R@ +GL_GGL_GNULIB_GETOPT_POSIX = @GL_GGL_GNULIB_GETOPT_POSIX@ +GL_GGL_GNULIB_GETPAGESIZE = @GL_GGL_GNULIB_GETPAGESIZE@ +GL_GGL_GNULIB_GETPASS = @GL_GGL_GNULIB_GETPASS@ +GL_GGL_GNULIB_GETPEERNAME = @GL_GGL_GNULIB_GETPEERNAME@ +GL_GGL_GNULIB_GETSOCKNAME = @GL_GGL_GNULIB_GETSOCKNAME@ +GL_GGL_GNULIB_GETSOCKOPT = @GL_GGL_GNULIB_GETSOCKOPT@ +GL_GGL_GNULIB_GETSUBOPT = @GL_GGL_GNULIB_GETSUBOPT@ +GL_GGL_GNULIB_GETTIMEOFDAY = @GL_GGL_GNULIB_GETTIMEOFDAY@ +GL_GGL_GNULIB_GETUMASK = @GL_GGL_GNULIB_GETUMASK@ +GL_GGL_GNULIB_GETUSERSHELL = @GL_GGL_GNULIB_GETUSERSHELL@ +GL_GGL_GNULIB_GRANTPT = @GL_GGL_GNULIB_GRANTPT@ +GL_GGL_GNULIB_GROUP_MEMBER = @GL_GGL_GNULIB_GROUP_MEMBER@ +GL_GGL_GNULIB_IMAXABS = @GL_GGL_GNULIB_IMAXABS@ +GL_GGL_GNULIB_IMAXDIV = @GL_GGL_GNULIB_IMAXDIV@ +GL_GGL_GNULIB_INET_NTOP = @GL_GGL_GNULIB_INET_NTOP@ +GL_GGL_GNULIB_INET_PTON = @GL_GGL_GNULIB_INET_PTON@ +GL_GGL_GNULIB_IOCTL = @GL_GGL_GNULIB_IOCTL@ +GL_GGL_GNULIB_ISATTY = @GL_GGL_GNULIB_ISATTY@ +GL_GGL_GNULIB_ISBLANK = @GL_GGL_GNULIB_ISBLANK@ +GL_GGL_GNULIB_LCHMOD = @GL_GGL_GNULIB_LCHMOD@ +GL_GGL_GNULIB_LCHOWN = @GL_GGL_GNULIB_LCHOWN@ +GL_GGL_GNULIB_LINK = @GL_GGL_GNULIB_LINK@ +GL_GGL_GNULIB_LINKAT = @GL_GGL_GNULIB_LINKAT@ +GL_GGL_GNULIB_LISTEN = @GL_GGL_GNULIB_LISTEN@ +GL_GGL_GNULIB_LOCALECONV = @GL_GGL_GNULIB_LOCALECONV@ +GL_GGL_GNULIB_LOCALENAME = @GL_GGL_GNULIB_LOCALENAME@ +GL_GGL_GNULIB_LOCALTIME = @GL_GGL_GNULIB_LOCALTIME@ +GL_GGL_GNULIB_LSEEK = @GL_GGL_GNULIB_LSEEK@ +GL_GGL_GNULIB_LSTAT = @GL_GGL_GNULIB_LSTAT@ +GL_GGL_GNULIB_MALLOC_POSIX = @GL_GGL_GNULIB_MALLOC_POSIX@ +GL_GGL_GNULIB_MBRLEN = @GL_GGL_GNULIB_MBRLEN@ +GL_GGL_GNULIB_MBRTOWC = @GL_GGL_GNULIB_MBRTOWC@ +GL_GGL_GNULIB_MBSCASECMP = @GL_GGL_GNULIB_MBSCASECMP@ +GL_GGL_GNULIB_MBSCASESTR = @GL_GGL_GNULIB_MBSCASESTR@ +GL_GGL_GNULIB_MBSCHR = @GL_GGL_GNULIB_MBSCHR@ +GL_GGL_GNULIB_MBSCSPN = @GL_GGL_GNULIB_MBSCSPN@ +GL_GGL_GNULIB_MBSINIT = @GL_GGL_GNULIB_MBSINIT@ +GL_GGL_GNULIB_MBSLEN = @GL_GGL_GNULIB_MBSLEN@ +GL_GGL_GNULIB_MBSNCASECMP = @GL_GGL_GNULIB_MBSNCASECMP@ +GL_GGL_GNULIB_MBSNLEN = @GL_GGL_GNULIB_MBSNLEN@ +GL_GGL_GNULIB_MBSNRTOWCS = @GL_GGL_GNULIB_MBSNRTOWCS@ +GL_GGL_GNULIB_MBSPBRK = @GL_GGL_GNULIB_MBSPBRK@ +GL_GGL_GNULIB_MBSPCASECMP = @GL_GGL_GNULIB_MBSPCASECMP@ +GL_GGL_GNULIB_MBSRCHR = @GL_GGL_GNULIB_MBSRCHR@ +GL_GGL_GNULIB_MBSRTOWCS = @GL_GGL_GNULIB_MBSRTOWCS@ +GL_GGL_GNULIB_MBSSEP = @GL_GGL_GNULIB_MBSSEP@ +GL_GGL_GNULIB_MBSSPN = @GL_GGL_GNULIB_MBSSPN@ +GL_GGL_GNULIB_MBSSTR = @GL_GGL_GNULIB_MBSSTR@ +GL_GGL_GNULIB_MBSTOK_R = @GL_GGL_GNULIB_MBSTOK_R@ +GL_GGL_GNULIB_MBTOWC = @GL_GGL_GNULIB_MBTOWC@ +GL_GGL_GNULIB_MDA_ACCESS = @GL_GGL_GNULIB_MDA_ACCESS@ +GL_GGL_GNULIB_MDA_CHDIR = @GL_GGL_GNULIB_MDA_CHDIR@ +GL_GGL_GNULIB_MDA_CHMOD = @GL_GGL_GNULIB_MDA_CHMOD@ +GL_GGL_GNULIB_MDA_CLOSE = @GL_GGL_GNULIB_MDA_CLOSE@ +GL_GGL_GNULIB_MDA_CREAT = @GL_GGL_GNULIB_MDA_CREAT@ +GL_GGL_GNULIB_MDA_DUP = @GL_GGL_GNULIB_MDA_DUP@ +GL_GGL_GNULIB_MDA_DUP2 = @GL_GGL_GNULIB_MDA_DUP2@ +GL_GGL_GNULIB_MDA_ECVT = @GL_GGL_GNULIB_MDA_ECVT@ +GL_GGL_GNULIB_MDA_EXECL = @GL_GGL_GNULIB_MDA_EXECL@ +GL_GGL_GNULIB_MDA_EXECLE = @GL_GGL_GNULIB_MDA_EXECLE@ +GL_GGL_GNULIB_MDA_EXECLP = @GL_GGL_GNULIB_MDA_EXECLP@ +GL_GGL_GNULIB_MDA_EXECV = @GL_GGL_GNULIB_MDA_EXECV@ +GL_GGL_GNULIB_MDA_EXECVE = @GL_GGL_GNULIB_MDA_EXECVE@ +GL_GGL_GNULIB_MDA_EXECVP = @GL_GGL_GNULIB_MDA_EXECVP@ +GL_GGL_GNULIB_MDA_EXECVPE = @GL_GGL_GNULIB_MDA_EXECVPE@ +GL_GGL_GNULIB_MDA_FCLOSEALL = @GL_GGL_GNULIB_MDA_FCLOSEALL@ +GL_GGL_GNULIB_MDA_FCVT = @GL_GGL_GNULIB_MDA_FCVT@ +GL_GGL_GNULIB_MDA_FDOPEN = @GL_GGL_GNULIB_MDA_FDOPEN@ +GL_GGL_GNULIB_MDA_FILENO = @GL_GGL_GNULIB_MDA_FILENO@ +GL_GGL_GNULIB_MDA_GCVT = @GL_GGL_GNULIB_MDA_GCVT@ +GL_GGL_GNULIB_MDA_GETCWD = @GL_GGL_GNULIB_MDA_GETCWD@ +GL_GGL_GNULIB_MDA_GETPID = @GL_GGL_GNULIB_MDA_GETPID@ +GL_GGL_GNULIB_MDA_GETW = @GL_GGL_GNULIB_MDA_GETW@ +GL_GGL_GNULIB_MDA_ISATTY = @GL_GGL_GNULIB_MDA_ISATTY@ +GL_GGL_GNULIB_MDA_LSEEK = @GL_GGL_GNULIB_MDA_LSEEK@ +GL_GGL_GNULIB_MDA_MEMCCPY = @GL_GGL_GNULIB_MDA_MEMCCPY@ +GL_GGL_GNULIB_MDA_MKDIR = @GL_GGL_GNULIB_MDA_MKDIR@ +GL_GGL_GNULIB_MDA_MKTEMP = @GL_GGL_GNULIB_MDA_MKTEMP@ +GL_GGL_GNULIB_MDA_OPEN = @GL_GGL_GNULIB_MDA_OPEN@ +GL_GGL_GNULIB_MDA_PUTENV = @GL_GGL_GNULIB_MDA_PUTENV@ +GL_GGL_GNULIB_MDA_PUTW = @GL_GGL_GNULIB_MDA_PUTW@ +GL_GGL_GNULIB_MDA_READ = @GL_GGL_GNULIB_MDA_READ@ +GL_GGL_GNULIB_MDA_RMDIR = @GL_GGL_GNULIB_MDA_RMDIR@ +GL_GGL_GNULIB_MDA_STRDUP = @GL_GGL_GNULIB_MDA_STRDUP@ +GL_GGL_GNULIB_MDA_SWAB = @GL_GGL_GNULIB_MDA_SWAB@ +GL_GGL_GNULIB_MDA_TEMPNAM = @GL_GGL_GNULIB_MDA_TEMPNAM@ +GL_GGL_GNULIB_MDA_TZSET = @GL_GGL_GNULIB_MDA_TZSET@ +GL_GGL_GNULIB_MDA_UMASK = @GL_GGL_GNULIB_MDA_UMASK@ +GL_GGL_GNULIB_MDA_UNLINK = @GL_GGL_GNULIB_MDA_UNLINK@ +GL_GGL_GNULIB_MDA_WCSDUP = @GL_GGL_GNULIB_MDA_WCSDUP@ +GL_GGL_GNULIB_MDA_WRITE = @GL_GGL_GNULIB_MDA_WRITE@ +GL_GGL_GNULIB_MEMCHR = @GL_GGL_GNULIB_MEMCHR@ +GL_GGL_GNULIB_MEMMEM = @GL_GGL_GNULIB_MEMMEM@ +GL_GGL_GNULIB_MEMPCPY = @GL_GGL_GNULIB_MEMPCPY@ +GL_GGL_GNULIB_MEMRCHR = @GL_GGL_GNULIB_MEMRCHR@ +GL_GGL_GNULIB_MKDIR = @GL_GGL_GNULIB_MKDIR@ +GL_GGL_GNULIB_MKDIRAT = @GL_GGL_GNULIB_MKDIRAT@ +GL_GGL_GNULIB_MKDTEMP = @GL_GGL_GNULIB_MKDTEMP@ +GL_GGL_GNULIB_MKFIFO = @GL_GGL_GNULIB_MKFIFO@ +GL_GGL_GNULIB_MKFIFOAT = @GL_GGL_GNULIB_MKFIFOAT@ +GL_GGL_GNULIB_MKNOD = @GL_GGL_GNULIB_MKNOD@ +GL_GGL_GNULIB_MKNODAT = @GL_GGL_GNULIB_MKNODAT@ +GL_GGL_GNULIB_MKOSTEMP = @GL_GGL_GNULIB_MKOSTEMP@ +GL_GGL_GNULIB_MKOSTEMPS = @GL_GGL_GNULIB_MKOSTEMPS@ +GL_GGL_GNULIB_MKSTEMP = @GL_GGL_GNULIB_MKSTEMP@ +GL_GGL_GNULIB_MKSTEMPS = @GL_GGL_GNULIB_MKSTEMPS@ +GL_GGL_GNULIB_MKTIME = @GL_GGL_GNULIB_MKTIME@ +GL_GGL_GNULIB_NANOSLEEP = @GL_GGL_GNULIB_NANOSLEEP@ +GL_GGL_GNULIB_NL_LANGINFO = @GL_GGL_GNULIB_NL_LANGINFO@ +GL_GGL_GNULIB_NONBLOCKING = @GL_GGL_GNULIB_NONBLOCKING@ +GL_GGL_GNULIB_OBSTACK_PRINTF = @GL_GGL_GNULIB_OBSTACK_PRINTF@ +GL_GGL_GNULIB_OBSTACK_PRINTF_POSIX = @GL_GGL_GNULIB_OBSTACK_PRINTF_POSIX@ +GL_GGL_GNULIB_OPEN = @GL_GGL_GNULIB_OPEN@ +GL_GGL_GNULIB_OPENAT = @GL_GGL_GNULIB_OPENAT@ +GL_GGL_GNULIB_OVERRIDES_STRUCT_STAT = @GL_GGL_GNULIB_OVERRIDES_STRUCT_STAT@ +GL_GGL_GNULIB_PCLOSE = @GL_GGL_GNULIB_PCLOSE@ +GL_GGL_GNULIB_PERROR = @GL_GGL_GNULIB_PERROR@ +GL_GGL_GNULIB_PIPE = @GL_GGL_GNULIB_PIPE@ +GL_GGL_GNULIB_PIPE2 = @GL_GGL_GNULIB_PIPE2@ +GL_GGL_GNULIB_POPEN = @GL_GGL_GNULIB_POPEN@ +GL_GGL_GNULIB_POSIX_MEMALIGN = @GL_GGL_GNULIB_POSIX_MEMALIGN@ +GL_GGL_GNULIB_POSIX_OPENPT = @GL_GGL_GNULIB_POSIX_OPENPT@ +GL_GGL_GNULIB_PREAD = @GL_GGL_GNULIB_PREAD@ +GL_GGL_GNULIB_PRINTF = @GL_GGL_GNULIB_PRINTF@ +GL_GGL_GNULIB_PRINTF_POSIX = @GL_GGL_GNULIB_PRINTF_POSIX@ +GL_GGL_GNULIB_PSELECT = @GL_GGL_GNULIB_PSELECT@ +GL_GGL_GNULIB_PTHREAD_COND = @GL_GGL_GNULIB_PTHREAD_COND@ +GL_GGL_GNULIB_PTHREAD_MUTEX = @GL_GGL_GNULIB_PTHREAD_MUTEX@ +GL_GGL_GNULIB_PTHREAD_MUTEX_TIMEDLOCK = @GL_GGL_GNULIB_PTHREAD_MUTEX_TIMEDLOCK@ +GL_GGL_GNULIB_PTHREAD_ONCE = @GL_GGL_GNULIB_PTHREAD_ONCE@ +GL_GGL_GNULIB_PTHREAD_RWLOCK = @GL_GGL_GNULIB_PTHREAD_RWLOCK@ +GL_GGL_GNULIB_PTHREAD_SIGMASK = @GL_GGL_GNULIB_PTHREAD_SIGMASK@ +GL_GGL_GNULIB_PTHREAD_SPIN = @GL_GGL_GNULIB_PTHREAD_SPIN@ +GL_GGL_GNULIB_PTHREAD_THREAD = @GL_GGL_GNULIB_PTHREAD_THREAD@ +GL_GGL_GNULIB_PTHREAD_TSS = @GL_GGL_GNULIB_PTHREAD_TSS@ +GL_GGL_GNULIB_PTSNAME = @GL_GGL_GNULIB_PTSNAME@ +GL_GGL_GNULIB_PTSNAME_R = @GL_GGL_GNULIB_PTSNAME_R@ +GL_GGL_GNULIB_PUTC = @GL_GGL_GNULIB_PUTC@ +GL_GGL_GNULIB_PUTCHAR = @GL_GGL_GNULIB_PUTCHAR@ +GL_GGL_GNULIB_PUTENV = @GL_GGL_GNULIB_PUTENV@ +GL_GGL_GNULIB_PUTS = @GL_GGL_GNULIB_PUTS@ +GL_GGL_GNULIB_PWRITE = @GL_GGL_GNULIB_PWRITE@ +GL_GGL_GNULIB_QSORT_R = @GL_GGL_GNULIB_QSORT_R@ +GL_GGL_GNULIB_RAISE = @GL_GGL_GNULIB_RAISE@ +GL_GGL_GNULIB_RANDOM = @GL_GGL_GNULIB_RANDOM@ +GL_GGL_GNULIB_RANDOM_R = @GL_GGL_GNULIB_RANDOM_R@ +GL_GGL_GNULIB_RAWMEMCHR = @GL_GGL_GNULIB_RAWMEMCHR@ +GL_GGL_GNULIB_READ = @GL_GGL_GNULIB_READ@ +GL_GGL_GNULIB_READLINK = @GL_GGL_GNULIB_READLINK@ +GL_GGL_GNULIB_READLINKAT = @GL_GGL_GNULIB_READLINKAT@ +GL_GGL_GNULIB_REALLOCARRAY = @GL_GGL_GNULIB_REALLOCARRAY@ +GL_GGL_GNULIB_REALLOC_POSIX = @GL_GGL_GNULIB_REALLOC_POSIX@ +GL_GGL_GNULIB_REALPATH = @GL_GGL_GNULIB_REALPATH@ +GL_GGL_GNULIB_RECV = @GL_GGL_GNULIB_RECV@ +GL_GGL_GNULIB_RECVFROM = @GL_GGL_GNULIB_RECVFROM@ +GL_GGL_GNULIB_REMOVE = @GL_GGL_GNULIB_REMOVE@ +GL_GGL_GNULIB_RENAME = @GL_GGL_GNULIB_RENAME@ +GL_GGL_GNULIB_RENAMEAT = @GL_GGL_GNULIB_RENAMEAT@ +GL_GGL_GNULIB_RMDIR = @GL_GGL_GNULIB_RMDIR@ +GL_GGL_GNULIB_RPMATCH = @GL_GGL_GNULIB_RPMATCH@ +GL_GGL_GNULIB_SCANF = @GL_GGL_GNULIB_SCANF@ +GL_GGL_GNULIB_SCHED_YIELD = @GL_GGL_GNULIB_SCHED_YIELD@ +GL_GGL_GNULIB_SECURE_GETENV = @GL_GGL_GNULIB_SECURE_GETENV@ +GL_GGL_GNULIB_SELECT = @GL_GGL_GNULIB_SELECT@ +GL_GGL_GNULIB_SEND = @GL_GGL_GNULIB_SEND@ +GL_GGL_GNULIB_SENDTO = @GL_GGL_GNULIB_SENDTO@ +GL_GGL_GNULIB_SETENV = @GL_GGL_GNULIB_SETENV@ +GL_GGL_GNULIB_SETHOSTNAME = @GL_GGL_GNULIB_SETHOSTNAME@ +GL_GGL_GNULIB_SETLOCALE = @GL_GGL_GNULIB_SETLOCALE@ +GL_GGL_GNULIB_SETLOCALE_NULL = @GL_GGL_GNULIB_SETLOCALE_NULL@ +GL_GGL_GNULIB_SETSOCKOPT = @GL_GGL_GNULIB_SETSOCKOPT@ +GL_GGL_GNULIB_SHUTDOWN = @GL_GGL_GNULIB_SHUTDOWN@ +GL_GGL_GNULIB_SIGABBREV_NP = @GL_GGL_GNULIB_SIGABBREV_NP@ +GL_GGL_GNULIB_SIGACTION = @GL_GGL_GNULIB_SIGACTION@ +GL_GGL_GNULIB_SIGDESCR_NP = @GL_GGL_GNULIB_SIGDESCR_NP@ +GL_GGL_GNULIB_SIGNAL_H_SIGPIPE = @GL_GGL_GNULIB_SIGNAL_H_SIGPIPE@ +GL_GGL_GNULIB_SIGPROCMASK = @GL_GGL_GNULIB_SIGPROCMASK@ +GL_GGL_GNULIB_SLEEP = @GL_GGL_GNULIB_SLEEP@ +GL_GGL_GNULIB_SNPRINTF = @GL_GGL_GNULIB_SNPRINTF@ +GL_GGL_GNULIB_SOCKET = @GL_GGL_GNULIB_SOCKET@ +GL_GGL_GNULIB_SPRINTF_POSIX = @GL_GGL_GNULIB_SPRINTF_POSIX@ +GL_GGL_GNULIB_STAT = @GL_GGL_GNULIB_STAT@ +GL_GGL_GNULIB_STDIO_H_NONBLOCKING = @GL_GGL_GNULIB_STDIO_H_NONBLOCKING@ +GL_GGL_GNULIB_STDIO_H_SIGPIPE = @GL_GGL_GNULIB_STDIO_H_SIGPIPE@ +GL_GGL_GNULIB_STPCPY = @GL_GGL_GNULIB_STPCPY@ +GL_GGL_GNULIB_STPNCPY = @GL_GGL_GNULIB_STPNCPY@ +GL_GGL_GNULIB_STRCASESTR = @GL_GGL_GNULIB_STRCASESTR@ +GL_GGL_GNULIB_STRCHRNUL = @GL_GGL_GNULIB_STRCHRNUL@ +GL_GGL_GNULIB_STRDUP = @GL_GGL_GNULIB_STRDUP@ +GL_GGL_GNULIB_STRERROR = @GL_GGL_GNULIB_STRERROR@ +GL_GGL_GNULIB_STRERRORNAME_NP = @GL_GGL_GNULIB_STRERRORNAME_NP@ +GL_GGL_GNULIB_STRERROR_R = @GL_GGL_GNULIB_STRERROR_R@ +GL_GGL_GNULIB_STRFTIME = @GL_GGL_GNULIB_STRFTIME@ +GL_GGL_GNULIB_STRNCAT = @GL_GGL_GNULIB_STRNCAT@ +GL_GGL_GNULIB_STRNDUP = @GL_GGL_GNULIB_STRNDUP@ +GL_GGL_GNULIB_STRNLEN = @GL_GGL_GNULIB_STRNLEN@ +GL_GGL_GNULIB_STRPBRK = @GL_GGL_GNULIB_STRPBRK@ +GL_GGL_GNULIB_STRPTIME = @GL_GGL_GNULIB_STRPTIME@ +GL_GGL_GNULIB_STRSEP = @GL_GGL_GNULIB_STRSEP@ +GL_GGL_GNULIB_STRSIGNAL = @GL_GGL_GNULIB_STRSIGNAL@ +GL_GGL_GNULIB_STRSTR = @GL_GGL_GNULIB_STRSTR@ +GL_GGL_GNULIB_STRTOD = @GL_GGL_GNULIB_STRTOD@ +GL_GGL_GNULIB_STRTOIMAX = @GL_GGL_GNULIB_STRTOIMAX@ +GL_GGL_GNULIB_STRTOK_R = @GL_GGL_GNULIB_STRTOK_R@ +GL_GGL_GNULIB_STRTOL = @GL_GGL_GNULIB_STRTOL@ +GL_GGL_GNULIB_STRTOLD = @GL_GGL_GNULIB_STRTOLD@ +GL_GGL_GNULIB_STRTOLL = @GL_GGL_GNULIB_STRTOLL@ +GL_GGL_GNULIB_STRTOUL = @GL_GGL_GNULIB_STRTOUL@ +GL_GGL_GNULIB_STRTOULL = @GL_GGL_GNULIB_STRTOULL@ +GL_GGL_GNULIB_STRTOUMAX = @GL_GGL_GNULIB_STRTOUMAX@ +GL_GGL_GNULIB_STRVERSCMP = @GL_GGL_GNULIB_STRVERSCMP@ +GL_GGL_GNULIB_SYMLINK = @GL_GGL_GNULIB_SYMLINK@ +GL_GGL_GNULIB_SYMLINKAT = @GL_GGL_GNULIB_SYMLINKAT@ +GL_GGL_GNULIB_SYSTEM_POSIX = @GL_GGL_GNULIB_SYSTEM_POSIX@ +GL_GGL_GNULIB_TIMEGM = @GL_GGL_GNULIB_TIMEGM@ +GL_GGL_GNULIB_TIMESPEC_GET = @GL_GGL_GNULIB_TIMESPEC_GET@ +GL_GGL_GNULIB_TIME_R = @GL_GGL_GNULIB_TIME_R@ +GL_GGL_GNULIB_TIME_RZ = @GL_GGL_GNULIB_TIME_RZ@ +GL_GGL_GNULIB_TMPFILE = @GL_GGL_GNULIB_TMPFILE@ +GL_GGL_GNULIB_TRUNCATE = @GL_GGL_GNULIB_TRUNCATE@ +GL_GGL_GNULIB_TTYNAME_R = @GL_GGL_GNULIB_TTYNAME_R@ +GL_GGL_GNULIB_TZSET = @GL_GGL_GNULIB_TZSET@ +GL_GGL_GNULIB_UNISTD_H_NONBLOCKING = @GL_GGL_GNULIB_UNISTD_H_NONBLOCKING@ +GL_GGL_GNULIB_UNISTD_H_SIGPIPE = @GL_GGL_GNULIB_UNISTD_H_SIGPIPE@ +GL_GGL_GNULIB_UNLINK = @GL_GGL_GNULIB_UNLINK@ +GL_GGL_GNULIB_UNLINKAT = @GL_GGL_GNULIB_UNLINKAT@ +GL_GGL_GNULIB_UNLOCKPT = @GL_GGL_GNULIB_UNLOCKPT@ +GL_GGL_GNULIB_UNSETENV = @GL_GGL_GNULIB_UNSETENV@ +GL_GGL_GNULIB_USLEEP = @GL_GGL_GNULIB_USLEEP@ +GL_GGL_GNULIB_UTIMENSAT = @GL_GGL_GNULIB_UTIMENSAT@ +GL_GGL_GNULIB_VASPRINTF = @GL_GGL_GNULIB_VASPRINTF@ +GL_GGL_GNULIB_VDPRINTF = @GL_GGL_GNULIB_VDPRINTF@ +GL_GGL_GNULIB_VFPRINTF = @GL_GGL_GNULIB_VFPRINTF@ +GL_GGL_GNULIB_VFPRINTF_POSIX = @GL_GGL_GNULIB_VFPRINTF_POSIX@ +GL_GGL_GNULIB_VFSCANF = @GL_GGL_GNULIB_VFSCANF@ +GL_GGL_GNULIB_VPRINTF = @GL_GGL_GNULIB_VPRINTF@ +GL_GGL_GNULIB_VPRINTF_POSIX = @GL_GGL_GNULIB_VPRINTF_POSIX@ +GL_GGL_GNULIB_VSCANF = @GL_GGL_GNULIB_VSCANF@ +GL_GGL_GNULIB_VSNPRINTF = @GL_GGL_GNULIB_VSNPRINTF@ +GL_GGL_GNULIB_VSPRINTF_POSIX = @GL_GGL_GNULIB_VSPRINTF_POSIX@ +GL_GGL_GNULIB_WCPCPY = @GL_GGL_GNULIB_WCPCPY@ +GL_GGL_GNULIB_WCPNCPY = @GL_GGL_GNULIB_WCPNCPY@ +GL_GGL_GNULIB_WCRTOMB = @GL_GGL_GNULIB_WCRTOMB@ +GL_GGL_GNULIB_WCSCASECMP = @GL_GGL_GNULIB_WCSCASECMP@ +GL_GGL_GNULIB_WCSCAT = @GL_GGL_GNULIB_WCSCAT@ +GL_GGL_GNULIB_WCSCHR = @GL_GGL_GNULIB_WCSCHR@ +GL_GGL_GNULIB_WCSCMP = @GL_GGL_GNULIB_WCSCMP@ +GL_GGL_GNULIB_WCSCOLL = @GL_GGL_GNULIB_WCSCOLL@ +GL_GGL_GNULIB_WCSCPY = @GL_GGL_GNULIB_WCSCPY@ +GL_GGL_GNULIB_WCSCSPN = @GL_GGL_GNULIB_WCSCSPN@ +GL_GGL_GNULIB_WCSDUP = @GL_GGL_GNULIB_WCSDUP@ +GL_GGL_GNULIB_WCSFTIME = @GL_GGL_GNULIB_WCSFTIME@ +GL_GGL_GNULIB_WCSLEN = @GL_GGL_GNULIB_WCSLEN@ +GL_GGL_GNULIB_WCSNCASECMP = @GL_GGL_GNULIB_WCSNCASECMP@ +GL_GGL_GNULIB_WCSNCAT = @GL_GGL_GNULIB_WCSNCAT@ +GL_GGL_GNULIB_WCSNCMP = @GL_GGL_GNULIB_WCSNCMP@ +GL_GGL_GNULIB_WCSNCPY = @GL_GGL_GNULIB_WCSNCPY@ +GL_GGL_GNULIB_WCSNLEN = @GL_GGL_GNULIB_WCSNLEN@ +GL_GGL_GNULIB_WCSNRTOMBS = @GL_GGL_GNULIB_WCSNRTOMBS@ +GL_GGL_GNULIB_WCSPBRK = @GL_GGL_GNULIB_WCSPBRK@ +GL_GGL_GNULIB_WCSRCHR = @GL_GGL_GNULIB_WCSRCHR@ +GL_GGL_GNULIB_WCSRTOMBS = @GL_GGL_GNULIB_WCSRTOMBS@ +GL_GGL_GNULIB_WCSSPN = @GL_GGL_GNULIB_WCSSPN@ +GL_GGL_GNULIB_WCSSTR = @GL_GGL_GNULIB_WCSSTR@ +GL_GGL_GNULIB_WCSTOK = @GL_GGL_GNULIB_WCSTOK@ +GL_GGL_GNULIB_WCSWIDTH = @GL_GGL_GNULIB_WCSWIDTH@ +GL_GGL_GNULIB_WCSXFRM = @GL_GGL_GNULIB_WCSXFRM@ +GL_GGL_GNULIB_WCTOB = @GL_GGL_GNULIB_WCTOB@ +GL_GGL_GNULIB_WCTOMB = @GL_GGL_GNULIB_WCTOMB@ +GL_GGL_GNULIB_WCWIDTH = @GL_GGL_GNULIB_WCWIDTH@ +GL_GGL_GNULIB_WMEMCHR = @GL_GGL_GNULIB_WMEMCHR@ +GL_GGL_GNULIB_WMEMCMP = @GL_GGL_GNULIB_WMEMCMP@ +GL_GGL_GNULIB_WMEMCPY = @GL_GGL_GNULIB_WMEMCPY@ +GL_GGL_GNULIB_WMEMMOVE = @GL_GGL_GNULIB_WMEMMOVE@ +GL_GGL_GNULIB_WMEMPCPY = @GL_GGL_GNULIB_WMEMPCPY@ +GL_GGL_GNULIB_WMEMSET = @GL_GGL_GNULIB_WMEMSET@ +GL_GGL_GNULIB_WRITE = @GL_GGL_GNULIB_WRITE@ +GL_GGL_GNULIB__EXIT = @GL_GGL_GNULIB__EXIT@ +GL_GNULIB_ACCEPT = @GL_GNULIB_ACCEPT@ +GL_GNULIB_ACCEPT4 = @GL_GNULIB_ACCEPT4@ +GL_GNULIB_ACCESS = @GL_GNULIB_ACCESS@ +GL_GNULIB_ALIGNED_ALLOC = @GL_GNULIB_ALIGNED_ALLOC@ +GL_GNULIB_ATOLL = @GL_GNULIB_ATOLL@ +GL_GNULIB_BIND = @GL_GNULIB_BIND@ +GL_GNULIB_BTOWC = @GL_GNULIB_BTOWC@ +GL_GNULIB_CALLOC_POSIX = @GL_GNULIB_CALLOC_POSIX@ +GL_GNULIB_CANONICALIZE_FILE_NAME = @GL_GNULIB_CANONICALIZE_FILE_NAME@ +GL_GNULIB_CHDIR = @GL_GNULIB_CHDIR@ +GL_GNULIB_CHOWN = @GL_GNULIB_CHOWN@ +GL_GNULIB_CLOSE = @GL_GNULIB_CLOSE@ +GL_GNULIB_CONNECT = @GL_GNULIB_CONNECT@ +GL_GNULIB_COPY_FILE_RANGE = @GL_GNULIB_COPY_FILE_RANGE@ +GL_GNULIB_CREAT = @GL_GNULIB_CREAT@ +GL_GNULIB_CTIME = @GL_GNULIB_CTIME@ +GL_GNULIB_DPRINTF = @GL_GNULIB_DPRINTF@ +GL_GNULIB_DUP = @GL_GNULIB_DUP@ +GL_GNULIB_DUP2 = @GL_GNULIB_DUP2@ +GL_GNULIB_DUP3 = @GL_GNULIB_DUP3@ +GL_GNULIB_ENVIRON = @GL_GNULIB_ENVIRON@ +GL_GNULIB_EUIDACCESS = @GL_GNULIB_EUIDACCESS@ +GL_GNULIB_EXECL = @GL_GNULIB_EXECL@ +GL_GNULIB_EXECLE = @GL_GNULIB_EXECLE@ +GL_GNULIB_EXECLP = @GL_GNULIB_EXECLP@ +GL_GNULIB_EXECV = @GL_GNULIB_EXECV@ +GL_GNULIB_EXECVE = @GL_GNULIB_EXECVE@ +GL_GNULIB_EXECVP = @GL_GNULIB_EXECVP@ +GL_GNULIB_EXECVPE = @GL_GNULIB_EXECVPE@ +GL_GNULIB_EXPLICIT_BZERO = @GL_GNULIB_EXPLICIT_BZERO@ +GL_GNULIB_FACCESSAT = @GL_GNULIB_FACCESSAT@ +GL_GNULIB_FCHDIR = @GL_GNULIB_FCHDIR@ +GL_GNULIB_FCHMODAT = @GL_GNULIB_FCHMODAT@ +GL_GNULIB_FCHOWNAT = @GL_GNULIB_FCHOWNAT@ +GL_GNULIB_FCLOSE = @GL_GNULIB_FCLOSE@ +GL_GNULIB_FCNTL = @GL_GNULIB_FCNTL@ +GL_GNULIB_FDATASYNC = @GL_GNULIB_FDATASYNC@ +GL_GNULIB_FDOPEN = @GL_GNULIB_FDOPEN@ +GL_GNULIB_FFLUSH = @GL_GNULIB_FFLUSH@ +GL_GNULIB_FFS = @GL_GNULIB_FFS@ +GL_GNULIB_FFSL = @GL_GNULIB_FFSL@ +GL_GNULIB_FFSLL = @GL_GNULIB_FFSLL@ +GL_GNULIB_FGETC = @GL_GNULIB_FGETC@ +GL_GNULIB_FGETS = @GL_GNULIB_FGETS@ +GL_GNULIB_FOPEN = @GL_GNULIB_FOPEN@ +GL_GNULIB_FPRINTF = @GL_GNULIB_FPRINTF@ +GL_GNULIB_FPRINTF_POSIX = @GL_GNULIB_FPRINTF_POSIX@ +GL_GNULIB_FPURGE = @GL_GNULIB_FPURGE@ +GL_GNULIB_FPUTC = @GL_GNULIB_FPUTC@ +GL_GNULIB_FPUTS = @GL_GNULIB_FPUTS@ +GL_GNULIB_FREAD = @GL_GNULIB_FREAD@ +GL_GNULIB_FREE_POSIX = @GL_GNULIB_FREE_POSIX@ +GL_GNULIB_FREOPEN = @GL_GNULIB_FREOPEN@ +GL_GNULIB_FSCANF = @GL_GNULIB_FSCANF@ +GL_GNULIB_FSEEK = @GL_GNULIB_FSEEK@ +GL_GNULIB_FSEEKO = @GL_GNULIB_FSEEKO@ +GL_GNULIB_FSTAT = @GL_GNULIB_FSTAT@ +GL_GNULIB_FSTATAT = @GL_GNULIB_FSTATAT@ +GL_GNULIB_FSYNC = @GL_GNULIB_FSYNC@ +GL_GNULIB_FTELL = @GL_GNULIB_FTELL@ +GL_GNULIB_FTELLO = @GL_GNULIB_FTELLO@ +GL_GNULIB_FTRUNCATE = @GL_GNULIB_FTRUNCATE@ +GL_GNULIB_FUTIMENS = @GL_GNULIB_FUTIMENS@ +GL_GNULIB_FWRITE = @GL_GNULIB_FWRITE@ +GL_GNULIB_GETADDRINFO = @GL_GNULIB_GETADDRINFO@ +GL_GNULIB_GETC = @GL_GNULIB_GETC@ +GL_GNULIB_GETCHAR = @GL_GNULIB_GETCHAR@ +GL_GNULIB_GETCWD = @GL_GNULIB_GETCWD@ +GL_GNULIB_GETDELIM = @GL_GNULIB_GETDELIM@ +GL_GNULIB_GETDOMAINNAME = @GL_GNULIB_GETDOMAINNAME@ +GL_GNULIB_GETDTABLESIZE = @GL_GNULIB_GETDTABLESIZE@ +GL_GNULIB_GETENTROPY = @GL_GNULIB_GETENTROPY@ +GL_GNULIB_GETGROUPS = @GL_GNULIB_GETGROUPS@ +GL_GNULIB_GETHOSTNAME = @GL_GNULIB_GETHOSTNAME@ +GL_GNULIB_GETLINE = @GL_GNULIB_GETLINE@ +GL_GNULIB_GETLOADAVG = @GL_GNULIB_GETLOADAVG@ +GL_GNULIB_GETLOGIN = @GL_GNULIB_GETLOGIN@ +GL_GNULIB_GETLOGIN_R = @GL_GNULIB_GETLOGIN_R@ +GL_GNULIB_GETOPT_POSIX = @GL_GNULIB_GETOPT_POSIX@ +GL_GNULIB_GETPAGESIZE = @GL_GNULIB_GETPAGESIZE@ +GL_GNULIB_GETPASS = @GL_GNULIB_GETPASS@ +GL_GNULIB_GETPEERNAME = @GL_GNULIB_GETPEERNAME@ +GL_GNULIB_GETSOCKNAME = @GL_GNULIB_GETSOCKNAME@ +GL_GNULIB_GETSOCKOPT = @GL_GNULIB_GETSOCKOPT@ +GL_GNULIB_GETSUBOPT = @GL_GNULIB_GETSUBOPT@ +GL_GNULIB_GETTIMEOFDAY = @GL_GNULIB_GETTIMEOFDAY@ +GL_GNULIB_GETUMASK = @GL_GNULIB_GETUMASK@ +GL_GNULIB_GETUSERSHELL = @GL_GNULIB_GETUSERSHELL@ +GL_GNULIB_GRANTPT = @GL_GNULIB_GRANTPT@ +GL_GNULIB_GROUP_MEMBER = @GL_GNULIB_GROUP_MEMBER@ +GL_GNULIB_IMAXABS = @GL_GNULIB_IMAXABS@ +GL_GNULIB_IMAXDIV = @GL_GNULIB_IMAXDIV@ +GL_GNULIB_INET_NTOP = @GL_GNULIB_INET_NTOP@ +GL_GNULIB_INET_PTON = @GL_GNULIB_INET_PTON@ +GL_GNULIB_ISATTY = @GL_GNULIB_ISATTY@ +GL_GNULIB_LCHMOD = @GL_GNULIB_LCHMOD@ +GL_GNULIB_LCHOWN = @GL_GNULIB_LCHOWN@ +GL_GNULIB_LINK = @GL_GNULIB_LINK@ +GL_GNULIB_LINKAT = @GL_GNULIB_LINKAT@ +GL_GNULIB_LISTEN = @GL_GNULIB_LISTEN@ +GL_GNULIB_LOCALTIME = @GL_GNULIB_LOCALTIME@ +GL_GNULIB_LSEEK = @GL_GNULIB_LSEEK@ +GL_GNULIB_LSTAT = @GL_GNULIB_LSTAT@ +GL_GNULIB_MALLOC_POSIX = @GL_GNULIB_MALLOC_POSIX@ +GL_GNULIB_MBRLEN = @GL_GNULIB_MBRLEN@ +GL_GNULIB_MBRTOWC = @GL_GNULIB_MBRTOWC@ +GL_GNULIB_MBSCASECMP = @GL_GNULIB_MBSCASECMP@ +GL_GNULIB_MBSCASESTR = @GL_GNULIB_MBSCASESTR@ +GL_GNULIB_MBSCHR = @GL_GNULIB_MBSCHR@ +GL_GNULIB_MBSCSPN = @GL_GNULIB_MBSCSPN@ +GL_GNULIB_MBSINIT = @GL_GNULIB_MBSINIT@ +GL_GNULIB_MBSLEN = @GL_GNULIB_MBSLEN@ +GL_GNULIB_MBSNCASECMP = @GL_GNULIB_MBSNCASECMP@ +GL_GNULIB_MBSNLEN = @GL_GNULIB_MBSNLEN@ +GL_GNULIB_MBSNRTOWCS = @GL_GNULIB_MBSNRTOWCS@ +GL_GNULIB_MBSPBRK = @GL_GNULIB_MBSPBRK@ +GL_GNULIB_MBSPCASECMP = @GL_GNULIB_MBSPCASECMP@ +GL_GNULIB_MBSRCHR = @GL_GNULIB_MBSRCHR@ +GL_GNULIB_MBSRTOWCS = @GL_GNULIB_MBSRTOWCS@ +GL_GNULIB_MBSSEP = @GL_GNULIB_MBSSEP@ +GL_GNULIB_MBSSPN = @GL_GNULIB_MBSSPN@ +GL_GNULIB_MBSSTR = @GL_GNULIB_MBSSTR@ +GL_GNULIB_MBSTOK_R = @GL_GNULIB_MBSTOK_R@ +GL_GNULIB_MBTOWC = @GL_GNULIB_MBTOWC@ +GL_GNULIB_MDA_ACCESS = @GL_GNULIB_MDA_ACCESS@ +GL_GNULIB_MDA_CHDIR = @GL_GNULIB_MDA_CHDIR@ +GL_GNULIB_MDA_CHMOD = @GL_GNULIB_MDA_CHMOD@ +GL_GNULIB_MDA_CLOSE = @GL_GNULIB_MDA_CLOSE@ +GL_GNULIB_MDA_CREAT = @GL_GNULIB_MDA_CREAT@ +GL_GNULIB_MDA_DUP = @GL_GNULIB_MDA_DUP@ +GL_GNULIB_MDA_DUP2 = @GL_GNULIB_MDA_DUP2@ +GL_GNULIB_MDA_ECVT = @GL_GNULIB_MDA_ECVT@ +GL_GNULIB_MDA_EXECL = @GL_GNULIB_MDA_EXECL@ +GL_GNULIB_MDA_EXECLE = @GL_GNULIB_MDA_EXECLE@ +GL_GNULIB_MDA_EXECLP = @GL_GNULIB_MDA_EXECLP@ +GL_GNULIB_MDA_EXECV = @GL_GNULIB_MDA_EXECV@ +GL_GNULIB_MDA_EXECVE = @GL_GNULIB_MDA_EXECVE@ +GL_GNULIB_MDA_EXECVP = @GL_GNULIB_MDA_EXECVP@ +GL_GNULIB_MDA_EXECVPE = @GL_GNULIB_MDA_EXECVPE@ +GL_GNULIB_MDA_FCLOSEALL = @GL_GNULIB_MDA_FCLOSEALL@ +GL_GNULIB_MDA_FCVT = @GL_GNULIB_MDA_FCVT@ +GL_GNULIB_MDA_FDOPEN = @GL_GNULIB_MDA_FDOPEN@ +GL_GNULIB_MDA_FILENO = @GL_GNULIB_MDA_FILENO@ +GL_GNULIB_MDA_GCVT = @GL_GNULIB_MDA_GCVT@ +GL_GNULIB_MDA_GETCWD = @GL_GNULIB_MDA_GETCWD@ +GL_GNULIB_MDA_GETPID = @GL_GNULIB_MDA_GETPID@ +GL_GNULIB_MDA_GETW = @GL_GNULIB_MDA_GETW@ +GL_GNULIB_MDA_ISATTY = @GL_GNULIB_MDA_ISATTY@ +GL_GNULIB_MDA_LSEEK = @GL_GNULIB_MDA_LSEEK@ +GL_GNULIB_MDA_MEMCCPY = @GL_GNULIB_MDA_MEMCCPY@ +GL_GNULIB_MDA_MKDIR = @GL_GNULIB_MDA_MKDIR@ +GL_GNULIB_MDA_MKTEMP = @GL_GNULIB_MDA_MKTEMP@ +GL_GNULIB_MDA_OPEN = @GL_GNULIB_MDA_OPEN@ +GL_GNULIB_MDA_PUTENV = @GL_GNULIB_MDA_PUTENV@ +GL_GNULIB_MDA_PUTW = @GL_GNULIB_MDA_PUTW@ +GL_GNULIB_MDA_READ = @GL_GNULIB_MDA_READ@ +GL_GNULIB_MDA_RMDIR = @GL_GNULIB_MDA_RMDIR@ +GL_GNULIB_MDA_STRDUP = @GL_GNULIB_MDA_STRDUP@ +GL_GNULIB_MDA_SWAB = @GL_GNULIB_MDA_SWAB@ +GL_GNULIB_MDA_TEMPNAM = @GL_GNULIB_MDA_TEMPNAM@ +GL_GNULIB_MDA_TZSET = @GL_GNULIB_MDA_TZSET@ +GL_GNULIB_MDA_UMASK = @GL_GNULIB_MDA_UMASK@ +GL_GNULIB_MDA_UNLINK = @GL_GNULIB_MDA_UNLINK@ +GL_GNULIB_MDA_WCSDUP = @GL_GNULIB_MDA_WCSDUP@ +GL_GNULIB_MDA_WRITE = @GL_GNULIB_MDA_WRITE@ +GL_GNULIB_MEMCHR = @GL_GNULIB_MEMCHR@ +GL_GNULIB_MEMMEM = @GL_GNULIB_MEMMEM@ +GL_GNULIB_MEMPCPY = @GL_GNULIB_MEMPCPY@ +GL_GNULIB_MEMRCHR = @GL_GNULIB_MEMRCHR@ +GL_GNULIB_MKDIR = @GL_GNULIB_MKDIR@ +GL_GNULIB_MKDIRAT = @GL_GNULIB_MKDIRAT@ +GL_GNULIB_MKDTEMP = @GL_GNULIB_MKDTEMP@ +GL_GNULIB_MKFIFO = @GL_GNULIB_MKFIFO@ +GL_GNULIB_MKFIFOAT = @GL_GNULIB_MKFIFOAT@ +GL_GNULIB_MKNOD = @GL_GNULIB_MKNOD@ +GL_GNULIB_MKNODAT = @GL_GNULIB_MKNODAT@ +GL_GNULIB_MKOSTEMP = @GL_GNULIB_MKOSTEMP@ +GL_GNULIB_MKOSTEMPS = @GL_GNULIB_MKOSTEMPS@ +GL_GNULIB_MKSTEMP = @GL_GNULIB_MKSTEMP@ +GL_GNULIB_MKSTEMPS = @GL_GNULIB_MKSTEMPS@ +GL_GNULIB_MKTIME = @GL_GNULIB_MKTIME@ +GL_GNULIB_NANOSLEEP = @GL_GNULIB_NANOSLEEP@ +GL_GNULIB_NONBLOCKING = @GL_GNULIB_NONBLOCKING@ +GL_GNULIB_OBSTACK_PRINTF = @GL_GNULIB_OBSTACK_PRINTF@ +GL_GNULIB_OBSTACK_PRINTF_POSIX = @GL_GNULIB_OBSTACK_PRINTF_POSIX@ +GL_GNULIB_OPEN = @GL_GNULIB_OPEN@ +GL_GNULIB_OPENAT = @GL_GNULIB_OPENAT@ +GL_GNULIB_OVERRIDES_STRUCT_STAT = @GL_GNULIB_OVERRIDES_STRUCT_STAT@ +GL_GNULIB_PCLOSE = @GL_GNULIB_PCLOSE@ +GL_GNULIB_PERROR = @GL_GNULIB_PERROR@ +GL_GNULIB_PIPE = @GL_GNULIB_PIPE@ +GL_GNULIB_PIPE2 = @GL_GNULIB_PIPE2@ +GL_GNULIB_POPEN = @GL_GNULIB_POPEN@ +GL_GNULIB_POSIX_MEMALIGN = @GL_GNULIB_POSIX_MEMALIGN@ +GL_GNULIB_POSIX_OPENPT = @GL_GNULIB_POSIX_OPENPT@ +GL_GNULIB_PREAD = @GL_GNULIB_PREAD@ +GL_GNULIB_PRINTF = @GL_GNULIB_PRINTF@ +GL_GNULIB_PRINTF_POSIX = @GL_GNULIB_PRINTF_POSIX@ +GL_GNULIB_PTSNAME = @GL_GNULIB_PTSNAME@ +GL_GNULIB_PTSNAME_R = @GL_GNULIB_PTSNAME_R@ +GL_GNULIB_PUTC = @GL_GNULIB_PUTC@ +GL_GNULIB_PUTCHAR = @GL_GNULIB_PUTCHAR@ +GL_GNULIB_PUTENV = @GL_GNULIB_PUTENV@ +GL_GNULIB_PUTS = @GL_GNULIB_PUTS@ +GL_GNULIB_PWRITE = @GL_GNULIB_PWRITE@ +GL_GNULIB_QSORT_R = @GL_GNULIB_QSORT_R@ +GL_GNULIB_RANDOM = @GL_GNULIB_RANDOM@ +GL_GNULIB_RANDOM_R = @GL_GNULIB_RANDOM_R@ +GL_GNULIB_RAWMEMCHR = @GL_GNULIB_RAWMEMCHR@ +GL_GNULIB_READ = @GL_GNULIB_READ@ +GL_GNULIB_READLINK = @GL_GNULIB_READLINK@ +GL_GNULIB_READLINKAT = @GL_GNULIB_READLINKAT@ +GL_GNULIB_REALLOCARRAY = @GL_GNULIB_REALLOCARRAY@ +GL_GNULIB_REALLOC_POSIX = @GL_GNULIB_REALLOC_POSIX@ +GL_GNULIB_REALPATH = @GL_GNULIB_REALPATH@ +GL_GNULIB_RECV = @GL_GNULIB_RECV@ +GL_GNULIB_RECVFROM = @GL_GNULIB_RECVFROM@ +GL_GNULIB_REMOVE = @GL_GNULIB_REMOVE@ +GL_GNULIB_RENAME = @GL_GNULIB_RENAME@ +GL_GNULIB_RENAMEAT = @GL_GNULIB_RENAMEAT@ +GL_GNULIB_RMDIR = @GL_GNULIB_RMDIR@ +GL_GNULIB_RPMATCH = @GL_GNULIB_RPMATCH@ +GL_GNULIB_SCANF = @GL_GNULIB_SCANF@ +GL_GNULIB_SECURE_GETENV = @GL_GNULIB_SECURE_GETENV@ +GL_GNULIB_SEND = @GL_GNULIB_SEND@ +GL_GNULIB_SENDTO = @GL_GNULIB_SENDTO@ +GL_GNULIB_SETENV = @GL_GNULIB_SETENV@ +GL_GNULIB_SETHOSTNAME = @GL_GNULIB_SETHOSTNAME@ +GL_GNULIB_SETSOCKOPT = @GL_GNULIB_SETSOCKOPT@ +GL_GNULIB_SHUTDOWN = @GL_GNULIB_SHUTDOWN@ +GL_GNULIB_SIGABBREV_NP = @GL_GNULIB_SIGABBREV_NP@ +GL_GNULIB_SIGDESCR_NP = @GL_GNULIB_SIGDESCR_NP@ +GL_GNULIB_SLEEP = @GL_GNULIB_SLEEP@ +GL_GNULIB_SNPRINTF = @GL_GNULIB_SNPRINTF@ +GL_GNULIB_SOCKET = @GL_GNULIB_SOCKET@ +GL_GNULIB_SPRINTF_POSIX = @GL_GNULIB_SPRINTF_POSIX@ +GL_GNULIB_STAT = @GL_GNULIB_STAT@ +GL_GNULIB_STDIO_H_NONBLOCKING = @GL_GNULIB_STDIO_H_NONBLOCKING@ +GL_GNULIB_STDIO_H_SIGPIPE = @GL_GNULIB_STDIO_H_SIGPIPE@ +GL_GNULIB_STPCPY = @GL_GNULIB_STPCPY@ +GL_GNULIB_STPNCPY = @GL_GNULIB_STPNCPY@ +GL_GNULIB_STRCASESTR = @GL_GNULIB_STRCASESTR@ +GL_GNULIB_STRCHRNUL = @GL_GNULIB_STRCHRNUL@ +GL_GNULIB_STRDUP = @GL_GNULIB_STRDUP@ +GL_GNULIB_STRERROR = @GL_GNULIB_STRERROR@ +GL_GNULIB_STRERRORNAME_NP = @GL_GNULIB_STRERRORNAME_NP@ +GL_GNULIB_STRERROR_R = @GL_GNULIB_STRERROR_R@ +GL_GNULIB_STRFTIME = @GL_GNULIB_STRFTIME@ +GL_GNULIB_STRNCAT = @GL_GNULIB_STRNCAT@ +GL_GNULIB_STRNDUP = @GL_GNULIB_STRNDUP@ +GL_GNULIB_STRNLEN = @GL_GNULIB_STRNLEN@ +GL_GNULIB_STRPBRK = @GL_GNULIB_STRPBRK@ +GL_GNULIB_STRPTIME = @GL_GNULIB_STRPTIME@ +GL_GNULIB_STRSEP = @GL_GNULIB_STRSEP@ +GL_GNULIB_STRSIGNAL = @GL_GNULIB_STRSIGNAL@ +GL_GNULIB_STRSTR = @GL_GNULIB_STRSTR@ +GL_GNULIB_STRTOD = @GL_GNULIB_STRTOD@ +GL_GNULIB_STRTOIMAX = @GL_GNULIB_STRTOIMAX@ +GL_GNULIB_STRTOK_R = @GL_GNULIB_STRTOK_R@ +GL_GNULIB_STRTOL = @GL_GNULIB_STRTOL@ +GL_GNULIB_STRTOLD = @GL_GNULIB_STRTOLD@ +GL_GNULIB_STRTOLL = @GL_GNULIB_STRTOLL@ +GL_GNULIB_STRTOUL = @GL_GNULIB_STRTOUL@ +GL_GNULIB_STRTOULL = @GL_GNULIB_STRTOULL@ +GL_GNULIB_STRTOUMAX = @GL_GNULIB_STRTOUMAX@ +GL_GNULIB_STRVERSCMP = @GL_GNULIB_STRVERSCMP@ +GL_GNULIB_SYMLINK = @GL_GNULIB_SYMLINK@ +GL_GNULIB_SYMLINKAT = @GL_GNULIB_SYMLINKAT@ +GL_GNULIB_SYSTEM_POSIX = @GL_GNULIB_SYSTEM_POSIX@ +GL_GNULIB_TIMEGM = @GL_GNULIB_TIMEGM@ +GL_GNULIB_TIMESPEC_GET = @GL_GNULIB_TIMESPEC_GET@ +GL_GNULIB_TIME_R = @GL_GNULIB_TIME_R@ +GL_GNULIB_TIME_RZ = @GL_GNULIB_TIME_RZ@ +GL_GNULIB_TMPFILE = @GL_GNULIB_TMPFILE@ +GL_GNULIB_TRUNCATE = @GL_GNULIB_TRUNCATE@ +GL_GNULIB_TTYNAME_R = @GL_GNULIB_TTYNAME_R@ +GL_GNULIB_TZSET = @GL_GNULIB_TZSET@ +GL_GNULIB_UNISTD_H_NONBLOCKING = @GL_GNULIB_UNISTD_H_NONBLOCKING@ +GL_GNULIB_UNISTD_H_SIGPIPE = @GL_GNULIB_UNISTD_H_SIGPIPE@ +GL_GNULIB_UNLINK = @GL_GNULIB_UNLINK@ +GL_GNULIB_UNLINKAT = @GL_GNULIB_UNLINKAT@ +GL_GNULIB_UNLOCKPT = @GL_GNULIB_UNLOCKPT@ +GL_GNULIB_UNSETENV = @GL_GNULIB_UNSETENV@ +GL_GNULIB_USLEEP = @GL_GNULIB_USLEEP@ +GL_GNULIB_UTIMENSAT = @GL_GNULIB_UTIMENSAT@ +GL_GNULIB_VASPRINTF = @GL_GNULIB_VASPRINTF@ +GL_GNULIB_VDPRINTF = @GL_GNULIB_VDPRINTF@ +GL_GNULIB_VFPRINTF = @GL_GNULIB_VFPRINTF@ +GL_GNULIB_VFPRINTF_POSIX = @GL_GNULIB_VFPRINTF_POSIX@ +GL_GNULIB_VFSCANF = @GL_GNULIB_VFSCANF@ +GL_GNULIB_VPRINTF = @GL_GNULIB_VPRINTF@ +GL_GNULIB_VPRINTF_POSIX = @GL_GNULIB_VPRINTF_POSIX@ +GL_GNULIB_VSCANF = @GL_GNULIB_VSCANF@ +GL_GNULIB_VSNPRINTF = @GL_GNULIB_VSNPRINTF@ +GL_GNULIB_VSPRINTF_POSIX = @GL_GNULIB_VSPRINTF_POSIX@ +GL_GNULIB_WCPCPY = @GL_GNULIB_WCPCPY@ +GL_GNULIB_WCPNCPY = @GL_GNULIB_WCPNCPY@ +GL_GNULIB_WCRTOMB = @GL_GNULIB_WCRTOMB@ +GL_GNULIB_WCSCASECMP = @GL_GNULIB_WCSCASECMP@ +GL_GNULIB_WCSCAT = @GL_GNULIB_WCSCAT@ +GL_GNULIB_WCSCHR = @GL_GNULIB_WCSCHR@ +GL_GNULIB_WCSCMP = @GL_GNULIB_WCSCMP@ +GL_GNULIB_WCSCOLL = @GL_GNULIB_WCSCOLL@ +GL_GNULIB_WCSCPY = @GL_GNULIB_WCSCPY@ +GL_GNULIB_WCSCSPN = @GL_GNULIB_WCSCSPN@ +GL_GNULIB_WCSDUP = @GL_GNULIB_WCSDUP@ +GL_GNULIB_WCSFTIME = @GL_GNULIB_WCSFTIME@ +GL_GNULIB_WCSLEN = @GL_GNULIB_WCSLEN@ +GL_GNULIB_WCSNCASECMP = @GL_GNULIB_WCSNCASECMP@ +GL_GNULIB_WCSNCAT = @GL_GNULIB_WCSNCAT@ +GL_GNULIB_WCSNCMP = @GL_GNULIB_WCSNCMP@ +GL_GNULIB_WCSNCPY = @GL_GNULIB_WCSNCPY@ +GL_GNULIB_WCSNLEN = @GL_GNULIB_WCSNLEN@ +GL_GNULIB_WCSNRTOMBS = @GL_GNULIB_WCSNRTOMBS@ +GL_GNULIB_WCSPBRK = @GL_GNULIB_WCSPBRK@ +GL_GNULIB_WCSRCHR = @GL_GNULIB_WCSRCHR@ +GL_GNULIB_WCSRTOMBS = @GL_GNULIB_WCSRTOMBS@ +GL_GNULIB_WCSSPN = @GL_GNULIB_WCSSPN@ +GL_GNULIB_WCSSTR = @GL_GNULIB_WCSSTR@ +GL_GNULIB_WCSTOK = @GL_GNULIB_WCSTOK@ +GL_GNULIB_WCSWIDTH = @GL_GNULIB_WCSWIDTH@ +GL_GNULIB_WCSXFRM = @GL_GNULIB_WCSXFRM@ +GL_GNULIB_WCTOB = @GL_GNULIB_WCTOB@ +GL_GNULIB_WCTOMB = @GL_GNULIB_WCTOMB@ +GL_GNULIB_WCWIDTH = @GL_GNULIB_WCWIDTH@ +GL_GNULIB_WMEMCHR = @GL_GNULIB_WMEMCHR@ +GL_GNULIB_WMEMCMP = @GL_GNULIB_WMEMCMP@ +GL_GNULIB_WMEMCPY = @GL_GNULIB_WMEMCPY@ +GL_GNULIB_WMEMMOVE = @GL_GNULIB_WMEMMOVE@ +GL_GNULIB_WMEMPCPY = @GL_GNULIB_WMEMPCPY@ +GL_GNULIB_WMEMSET = @GL_GNULIB_WMEMSET@ +GL_GNULIB_WRITE = @GL_GNULIB_WRITE@ +GL_GNULIB__EXIT = @GL_GNULIB__EXIT@ +GMP_CFLAGS = @GMP_CFLAGS@ +GMP_LIBS = @GMP_LIBS@ +GMSGFMT = @GMSGFMT@ +GMSGFMT_015 = @GMSGFMT_015@ +GNULIBHEADERS_OVERRIDE_WINT_T = @GNULIBHEADERS_OVERRIDE_WINT_T@ +GNULIB_GETTIMEOFDAY = @GNULIB_GETTIMEOFDAY@ +GNUTLS_LIBS_PRIVATE = @GNUTLS_LIBS_PRIVATE@ +GNUTLS_REQUIRES_PRIVATE = @GNUTLS_REQUIRES_PRIVATE@ +GPERF = @GPERF@ +GREP = @GREP@ +GTKDOC_CHECK = @GTKDOC_CHECK@ +GTKDOC_CHECK_PATH = @GTKDOC_CHECK_PATH@ +GTKDOC_DEPS_CFLAGS = @GTKDOC_DEPS_CFLAGS@ +GTKDOC_DEPS_LIBS = @GTKDOC_DEPS_LIBS@ +GTKDOC_MKPDF = @GTKDOC_MKPDF@ +GTKDOC_REBASE = @GTKDOC_REBASE@ +GUILD = @GUILD@ +GUILE = @GUILE@ +GUILE_CFLAGS = @GUILE_CFLAGS@ +GUILE_CONFIG = @GUILE_CONFIG@ +GUILE_EFFECTIVE_VERSION = @GUILE_EFFECTIVE_VERSION@ +GUILE_EXTENSION = @GUILE_EXTENSION@ +GUILE_LDFLAGS = @GUILE_LDFLAGS@ +GUILE_LIBS = @GUILE_LIBS@ +GUILE_LTLIBS = @GUILE_LTLIBS@ +GUILE_SITE = @GUILE_SITE@ +GUILE_SITE_CCACHE = @GUILE_SITE_CCACHE@ +GUILE_TOOLS = @GUILE_TOOLS@ +HAVE_ACCEPT4 = @HAVE_ACCEPT4@ +HAVE_ALIGNED_ALLOC = @HAVE_ALIGNED_ALLOC@ +HAVE_ALLOCA_H = @HAVE_ALLOCA_H@ +HAVE_ARPA_INET_H = @HAVE_ARPA_INET_H@ +HAVE_ATOLL = @HAVE_ATOLL@ +HAVE_BTOWC = @HAVE_BTOWC@ +HAVE_C99_STDINT_H = @HAVE_C99_STDINT_H@ +HAVE_CANONICALIZE_FILE_NAME = @HAVE_CANONICALIZE_FILE_NAME@ +HAVE_CHOWN = @HAVE_CHOWN@ +HAVE_COPY_FILE_RANGE = @HAVE_COPY_FILE_RANGE@ +HAVE_CRTDEFS_H = @HAVE_CRTDEFS_H@ +HAVE_DECL_ECVT = @HAVE_DECL_ECVT@ +HAVE_DECL_ENVIRON = @HAVE_DECL_ENVIRON@ +HAVE_DECL_EXECVPE = @HAVE_DECL_EXECVPE@ +HAVE_DECL_FCHDIR = @HAVE_DECL_FCHDIR@ +HAVE_DECL_FCLOSEALL = @HAVE_DECL_FCLOSEALL@ +HAVE_DECL_FCVT = @HAVE_DECL_FCVT@ +HAVE_DECL_FDATASYNC = @HAVE_DECL_FDATASYNC@ +HAVE_DECL_FPURGE = @HAVE_DECL_FPURGE@ +HAVE_DECL_FREEADDRINFO = @HAVE_DECL_FREEADDRINFO@ +HAVE_DECL_FSEEKO = @HAVE_DECL_FSEEKO@ +HAVE_DECL_FTELLO = @HAVE_DECL_FTELLO@ +HAVE_DECL_GAI_STRERROR = @HAVE_DECL_GAI_STRERROR@ +HAVE_DECL_GCVT = @HAVE_DECL_GCVT@ +HAVE_DECL_GETADDRINFO = @HAVE_DECL_GETADDRINFO@ +HAVE_DECL_GETDELIM = @HAVE_DECL_GETDELIM@ +HAVE_DECL_GETDOMAINNAME = @HAVE_DECL_GETDOMAINNAME@ +HAVE_DECL_GETLINE = @HAVE_DECL_GETLINE@ +HAVE_DECL_GETLOADAVG = @HAVE_DECL_GETLOADAVG@ +HAVE_DECL_GETLOGIN = @HAVE_DECL_GETLOGIN@ +HAVE_DECL_GETLOGIN_R = @HAVE_DECL_GETLOGIN_R@ +HAVE_DECL_GETNAMEINFO = @HAVE_DECL_GETNAMEINFO@ +HAVE_DECL_GETPAGESIZE = @HAVE_DECL_GETPAGESIZE@ +HAVE_DECL_GETUSERSHELL = @HAVE_DECL_GETUSERSHELL@ +HAVE_DECL_IMAXABS = @HAVE_DECL_IMAXABS@ +HAVE_DECL_IMAXDIV = @HAVE_DECL_IMAXDIV@ +HAVE_DECL_INET_NTOP = @HAVE_DECL_INET_NTOP@ +HAVE_DECL_INET_PTON = @HAVE_DECL_INET_PTON@ +HAVE_DECL_INITSTATE = @HAVE_DECL_INITSTATE@ +HAVE_DECL_LOCALTIME_R = @HAVE_DECL_LOCALTIME_R@ +HAVE_DECL_MEMMEM = @HAVE_DECL_MEMMEM@ +HAVE_DECL_MEMRCHR = @HAVE_DECL_MEMRCHR@ +HAVE_DECL_OBSTACK_PRINTF = @HAVE_DECL_OBSTACK_PRINTF@ +HAVE_DECL_SETENV = @HAVE_DECL_SETENV@ +HAVE_DECL_SETHOSTNAME = @HAVE_DECL_SETHOSTNAME@ +HAVE_DECL_SETSTATE = @HAVE_DECL_SETSTATE@ +HAVE_DECL_SNPRINTF = @HAVE_DECL_SNPRINTF@ +HAVE_DECL_STRDUP = @HAVE_DECL_STRDUP@ +HAVE_DECL_STRERROR_R = @HAVE_DECL_STRERROR_R@ +HAVE_DECL_STRNCASECMP = @HAVE_DECL_STRNCASECMP@ +HAVE_DECL_STRNDUP = @HAVE_DECL_STRNDUP@ +HAVE_DECL_STRNLEN = @HAVE_DECL_STRNLEN@ +HAVE_DECL_STRSIGNAL = @HAVE_DECL_STRSIGNAL@ +HAVE_DECL_STRTOIMAX = @HAVE_DECL_STRTOIMAX@ +HAVE_DECL_STRTOK_R = @HAVE_DECL_STRTOK_R@ +HAVE_DECL_STRTOUMAX = @HAVE_DECL_STRTOUMAX@ +HAVE_DECL_TRUNCATE = @HAVE_DECL_TRUNCATE@ +HAVE_DECL_TTYNAME_R = @HAVE_DECL_TTYNAME_R@ +HAVE_DECL_UNSETENV = @HAVE_DECL_UNSETENV@ +HAVE_DECL_VSNPRINTF = @HAVE_DECL_VSNPRINTF@ +HAVE_DECL_WCSDUP = @HAVE_DECL_WCSDUP@ +HAVE_DECL_WCTOB = @HAVE_DECL_WCTOB@ +HAVE_DECL_WCWIDTH = @HAVE_DECL_WCWIDTH@ +HAVE_DPRINTF = @HAVE_DPRINTF@ +HAVE_DUP3 = @HAVE_DUP3@ +HAVE_DUPLOCALE = @HAVE_DUPLOCALE@ +HAVE_EUIDACCESS = @HAVE_EUIDACCESS@ +HAVE_EXECVPE = @HAVE_EXECVPE@ +HAVE_EXPLICIT_BZERO = @HAVE_EXPLICIT_BZERO@ +HAVE_FACCESSAT = @HAVE_FACCESSAT@ +HAVE_FCHDIR = @HAVE_FCHDIR@ +HAVE_FCHMODAT = @HAVE_FCHMODAT@ +HAVE_FCHOWNAT = @HAVE_FCHOWNAT@ +HAVE_FCNTL = @HAVE_FCNTL@ +HAVE_FDATASYNC = @HAVE_FDATASYNC@ +HAVE_FEATURES_H = @HAVE_FEATURES_H@ +HAVE_FFS = @HAVE_FFS@ +HAVE_FFSL = @HAVE_FFSL@ +HAVE_FFSLL = @HAVE_FFSLL@ +HAVE_FREELOCALE = @HAVE_FREELOCALE@ +HAVE_FSEEKO = @HAVE_FSEEKO@ +HAVE_FSTATAT = @HAVE_FSTATAT@ +HAVE_FSYNC = @HAVE_FSYNC@ +HAVE_FTELLO = @HAVE_FTELLO@ +HAVE_FTRUNCATE = @HAVE_FTRUNCATE@ +HAVE_FUTIMENS = @HAVE_FUTIMENS@ +HAVE_GETDTABLESIZE = @HAVE_GETDTABLESIZE@ +HAVE_GETENTROPY = @HAVE_GETENTROPY@ +HAVE_GETGROUPS = @HAVE_GETGROUPS@ +HAVE_GETHOSTNAME = @HAVE_GETHOSTNAME@ +HAVE_GETLOGIN = @HAVE_GETLOGIN@ +HAVE_GETPAGESIZE = @HAVE_GETPAGESIZE@ +HAVE_GETPASS = @HAVE_GETPASS@ +HAVE_GETSUBOPT = @HAVE_GETSUBOPT@ +HAVE_GETTIMEOFDAY = @HAVE_GETTIMEOFDAY@ +HAVE_GETUMASK = @HAVE_GETUMASK@ +HAVE_GRANTPT = @HAVE_GRANTPT@ +HAVE_GROUP_MEMBER = @HAVE_GROUP_MEMBER@ +HAVE_IMAXDIV_T = @HAVE_IMAXDIV_T@ +HAVE_INITSTATE = @HAVE_INITSTATE@ +HAVE_INTTYPES_H = @HAVE_INTTYPES_H@ +HAVE_ISBLANK = @HAVE_ISBLANK@ +HAVE_LANGINFO_ALTMON = @HAVE_LANGINFO_ALTMON@ +HAVE_LANGINFO_CODESET = @HAVE_LANGINFO_CODESET@ +HAVE_LANGINFO_ERA = @HAVE_LANGINFO_ERA@ +HAVE_LANGINFO_H = @HAVE_LANGINFO_H@ +HAVE_LANGINFO_T_FMT_AMPM = @HAVE_LANGINFO_T_FMT_AMPM@ +HAVE_LANGINFO_YESEXPR = @HAVE_LANGINFO_YESEXPR@ +HAVE_LCHMOD = @HAVE_LCHMOD@ +HAVE_LCHOWN = @HAVE_LCHOWN@ +HAVE_LIBCRYPTO = @HAVE_LIBCRYPTO@ +HAVE_LIBDL = @HAVE_LIBDL@ +HAVE_LIBEV = @HAVE_LIBEV@ +HAVE_LIBPTHREAD = @HAVE_LIBPTHREAD@ +HAVE_LIBRT = @HAVE_LIBRT@ +HAVE_LIBSECCOMP = @HAVE_LIBSECCOMP@ +HAVE_LIBZ = @HAVE_LIBZ@ +HAVE_LINK = @HAVE_LINK@ +HAVE_LINKAT = @HAVE_LINKAT@ +HAVE_LSTAT = @HAVE_LSTAT@ +HAVE_MAX_ALIGN_T = @HAVE_MAX_ALIGN_T@ +HAVE_MBRLEN = @HAVE_MBRLEN@ +HAVE_MBRTOWC = @HAVE_MBRTOWC@ +HAVE_MBSINIT = @HAVE_MBSINIT@ +HAVE_MBSLEN = @HAVE_MBSLEN@ +HAVE_MBSNRTOWCS = @HAVE_MBSNRTOWCS@ +HAVE_MBSRTOWCS = @HAVE_MBSRTOWCS@ +HAVE_MBTOWC = @HAVE_MBTOWC@ +HAVE_MEMPCPY = @HAVE_MEMPCPY@ +HAVE_MKDIRAT = @HAVE_MKDIRAT@ +HAVE_MKDTEMP = @HAVE_MKDTEMP@ +HAVE_MKFIFO = @HAVE_MKFIFO@ +HAVE_MKFIFOAT = @HAVE_MKFIFOAT@ +HAVE_MKNOD = @HAVE_MKNOD@ +HAVE_MKNODAT = @HAVE_MKNODAT@ +HAVE_MKOSTEMP = @HAVE_MKOSTEMP@ +HAVE_MKOSTEMPS = @HAVE_MKOSTEMPS@ +HAVE_MKSTEMP = @HAVE_MKSTEMP@ +HAVE_MKSTEMPS = @HAVE_MKSTEMPS@ +HAVE_MSVC_INVALID_PARAMETER_HANDLER = @HAVE_MSVC_INVALID_PARAMETER_HANDLER@ +HAVE_NANOSLEEP = @HAVE_NANOSLEEP@ +HAVE_NETDB_H = @HAVE_NETDB_H@ +HAVE_NETINET_IN_H = @HAVE_NETINET_IN_H@ +HAVE_NEWLOCALE = @HAVE_NEWLOCALE@ +HAVE_NL_LANGINFO = @HAVE_NL_LANGINFO@ +HAVE_OPENAT = @HAVE_OPENAT@ +HAVE_OS_H = @HAVE_OS_H@ +HAVE_PCLOSE = @HAVE_PCLOSE@ +HAVE_PIPE = @HAVE_PIPE@ +HAVE_PIPE2 = @HAVE_PIPE2@ +HAVE_POPEN = @HAVE_POPEN@ +HAVE_POSIX_MEMALIGN = @HAVE_POSIX_MEMALIGN@ +HAVE_POSIX_OPENPT = @HAVE_POSIX_OPENPT@ +HAVE_POSIX_SIGNALBLOCKING = @HAVE_POSIX_SIGNALBLOCKING@ +HAVE_PREAD = @HAVE_PREAD@ +HAVE_PSELECT = @HAVE_PSELECT@ +HAVE_PTHREAD_ATTR_DESTROY = @HAVE_PTHREAD_ATTR_DESTROY@ +HAVE_PTHREAD_ATTR_GETDETACHSTATE = @HAVE_PTHREAD_ATTR_GETDETACHSTATE@ +HAVE_PTHREAD_ATTR_INIT = @HAVE_PTHREAD_ATTR_INIT@ +HAVE_PTHREAD_ATTR_SETDETACHSTATE = @HAVE_PTHREAD_ATTR_SETDETACHSTATE@ +HAVE_PTHREAD_CONDATTR_DESTROY = @HAVE_PTHREAD_CONDATTR_DESTROY@ +HAVE_PTHREAD_CONDATTR_INIT = @HAVE_PTHREAD_CONDATTR_INIT@ +HAVE_PTHREAD_COND_BROADCAST = @HAVE_PTHREAD_COND_BROADCAST@ +HAVE_PTHREAD_COND_DESTROY = @HAVE_PTHREAD_COND_DESTROY@ +HAVE_PTHREAD_COND_INIT = @HAVE_PTHREAD_COND_INIT@ +HAVE_PTHREAD_COND_SIGNAL = @HAVE_PTHREAD_COND_SIGNAL@ +HAVE_PTHREAD_COND_TIMEDWAIT = @HAVE_PTHREAD_COND_TIMEDWAIT@ +HAVE_PTHREAD_COND_WAIT = @HAVE_PTHREAD_COND_WAIT@ +HAVE_PTHREAD_CREATE = @HAVE_PTHREAD_CREATE@ +HAVE_PTHREAD_CREATE_DETACHED = @HAVE_PTHREAD_CREATE_DETACHED@ +HAVE_PTHREAD_DETACH = @HAVE_PTHREAD_DETACH@ +HAVE_PTHREAD_EQUAL = @HAVE_PTHREAD_EQUAL@ +HAVE_PTHREAD_EXIT = @HAVE_PTHREAD_EXIT@ +HAVE_PTHREAD_GETSPECIFIC = @HAVE_PTHREAD_GETSPECIFIC@ +HAVE_PTHREAD_H = @HAVE_PTHREAD_H@ +HAVE_PTHREAD_JOIN = @HAVE_PTHREAD_JOIN@ +HAVE_PTHREAD_KEY_CREATE = @HAVE_PTHREAD_KEY_CREATE@ +HAVE_PTHREAD_KEY_DELETE = @HAVE_PTHREAD_KEY_DELETE@ +HAVE_PTHREAD_MUTEXATTR_DESTROY = @HAVE_PTHREAD_MUTEXATTR_DESTROY@ +HAVE_PTHREAD_MUTEXATTR_GETROBUST = @HAVE_PTHREAD_MUTEXATTR_GETROBUST@ +HAVE_PTHREAD_MUTEXATTR_GETTYPE = @HAVE_PTHREAD_MUTEXATTR_GETTYPE@ +HAVE_PTHREAD_MUTEXATTR_INIT = @HAVE_PTHREAD_MUTEXATTR_INIT@ +HAVE_PTHREAD_MUTEXATTR_SETROBUST = @HAVE_PTHREAD_MUTEXATTR_SETROBUST@ +HAVE_PTHREAD_MUTEXATTR_SETTYPE = @HAVE_PTHREAD_MUTEXATTR_SETTYPE@ +HAVE_PTHREAD_MUTEX_DESTROY = @HAVE_PTHREAD_MUTEX_DESTROY@ +HAVE_PTHREAD_MUTEX_INIT = @HAVE_PTHREAD_MUTEX_INIT@ +HAVE_PTHREAD_MUTEX_LOCK = @HAVE_PTHREAD_MUTEX_LOCK@ +HAVE_PTHREAD_MUTEX_RECURSIVE = @HAVE_PTHREAD_MUTEX_RECURSIVE@ +HAVE_PTHREAD_MUTEX_ROBUST = @HAVE_PTHREAD_MUTEX_ROBUST@ +HAVE_PTHREAD_MUTEX_TIMEDLOCK = @HAVE_PTHREAD_MUTEX_TIMEDLOCK@ +HAVE_PTHREAD_MUTEX_TRYLOCK = @HAVE_PTHREAD_MUTEX_TRYLOCK@ +HAVE_PTHREAD_MUTEX_UNLOCK = @HAVE_PTHREAD_MUTEX_UNLOCK@ +HAVE_PTHREAD_ONCE = @HAVE_PTHREAD_ONCE@ +HAVE_PTHREAD_PROCESS_SHARED = @HAVE_PTHREAD_PROCESS_SHARED@ +HAVE_PTHREAD_RWLOCKATTR_DESTROY = @HAVE_PTHREAD_RWLOCKATTR_DESTROY@ +HAVE_PTHREAD_RWLOCKATTR_INIT = @HAVE_PTHREAD_RWLOCKATTR_INIT@ +HAVE_PTHREAD_RWLOCK_DESTROY = @HAVE_PTHREAD_RWLOCK_DESTROY@ +HAVE_PTHREAD_RWLOCK_INIT = @HAVE_PTHREAD_RWLOCK_INIT@ +HAVE_PTHREAD_RWLOCK_RDLOCK = @HAVE_PTHREAD_RWLOCK_RDLOCK@ +HAVE_PTHREAD_RWLOCK_TIMEDRDLOCK = @HAVE_PTHREAD_RWLOCK_TIMEDRDLOCK@ +HAVE_PTHREAD_RWLOCK_TIMEDWRLOCK = @HAVE_PTHREAD_RWLOCK_TIMEDWRLOCK@ +HAVE_PTHREAD_RWLOCK_TRYRDLOCK = @HAVE_PTHREAD_RWLOCK_TRYRDLOCK@ +HAVE_PTHREAD_RWLOCK_TRYWRLOCK = @HAVE_PTHREAD_RWLOCK_TRYWRLOCK@ +HAVE_PTHREAD_RWLOCK_UNLOCK = @HAVE_PTHREAD_RWLOCK_UNLOCK@ +HAVE_PTHREAD_RWLOCK_WRLOCK = @HAVE_PTHREAD_RWLOCK_WRLOCK@ +HAVE_PTHREAD_SELF = @HAVE_PTHREAD_SELF@ +HAVE_PTHREAD_SETSPECIFIC = @HAVE_PTHREAD_SETSPECIFIC@ +HAVE_PTHREAD_SIGMASK = @HAVE_PTHREAD_SIGMASK@ +HAVE_PTHREAD_SPINLOCK_T = @HAVE_PTHREAD_SPINLOCK_T@ +HAVE_PTHREAD_SPIN_DESTROY = @HAVE_PTHREAD_SPIN_DESTROY@ +HAVE_PTHREAD_SPIN_INIT = @HAVE_PTHREAD_SPIN_INIT@ +HAVE_PTHREAD_SPIN_LOCK = @HAVE_PTHREAD_SPIN_LOCK@ +HAVE_PTHREAD_SPIN_TRYLOCK = @HAVE_PTHREAD_SPIN_TRYLOCK@ +HAVE_PTHREAD_SPIN_UNLOCK = @HAVE_PTHREAD_SPIN_UNLOCK@ +HAVE_PTHREAD_T = @HAVE_PTHREAD_T@ +HAVE_PTSNAME = @HAVE_PTSNAME@ +HAVE_PTSNAME_R = @HAVE_PTSNAME_R@ +HAVE_PWRITE = @HAVE_PWRITE@ +HAVE_QSORT_R = @HAVE_QSORT_R@ +HAVE_RAISE = @HAVE_RAISE@ +HAVE_RANDOM = @HAVE_RANDOM@ +HAVE_RANDOM_H = @HAVE_RANDOM_H@ +HAVE_RANDOM_R = @HAVE_RANDOM_R@ +HAVE_RAWMEMCHR = @HAVE_RAWMEMCHR@ +HAVE_READLINK = @HAVE_READLINK@ +HAVE_READLINKAT = @HAVE_READLINKAT@ +HAVE_REALLOCARRAY = @HAVE_REALLOCARRAY@ +HAVE_REALPATH = @HAVE_REALPATH@ +HAVE_RENAMEAT = @HAVE_RENAMEAT@ +HAVE_RPMATCH = @HAVE_RPMATCH@ +HAVE_SA_FAMILY_T = @HAVE_SA_FAMILY_T@ +HAVE_SCHED_H = @HAVE_SCHED_H@ +HAVE_SCHED_YIELD = @HAVE_SCHED_YIELD@ +HAVE_SECURE_GETENV = @HAVE_SECURE_GETENV@ +HAVE_SETENV = @HAVE_SETENV@ +HAVE_SETHOSTNAME = @HAVE_SETHOSTNAME@ +HAVE_SETSTATE = @HAVE_SETSTATE@ +HAVE_SIGABBREV_NP = @HAVE_SIGABBREV_NP@ +HAVE_SIGACTION = @HAVE_SIGACTION@ +HAVE_SIGDESCR_NP = @HAVE_SIGDESCR_NP@ +HAVE_SIGHANDLER_T = @HAVE_SIGHANDLER_T@ +HAVE_SIGINFO_T = @HAVE_SIGINFO_T@ +HAVE_SIGNED_SIG_ATOMIC_T = @HAVE_SIGNED_SIG_ATOMIC_T@ +HAVE_SIGNED_WCHAR_T = @HAVE_SIGNED_WCHAR_T@ +HAVE_SIGNED_WINT_T = @HAVE_SIGNED_WINT_T@ +HAVE_SIGSET_T = @HAVE_SIGSET_T@ +HAVE_SLEEP = @HAVE_SLEEP@ +HAVE_STDINT_H = @HAVE_STDINT_H@ +HAVE_STPCPY = @HAVE_STPCPY@ +HAVE_STPNCPY = @HAVE_STPNCPY@ +HAVE_STRCASECMP = @HAVE_STRCASECMP@ +HAVE_STRCASESTR = @HAVE_STRCASESTR@ +HAVE_STRCHRNUL = @HAVE_STRCHRNUL@ +HAVE_STRERRORNAME_NP = @HAVE_STRERRORNAME_NP@ +HAVE_STRINGS_H = @HAVE_STRINGS_H@ +HAVE_STRPBRK = @HAVE_STRPBRK@ +HAVE_STRPTIME = @HAVE_STRPTIME@ +HAVE_STRSEP = @HAVE_STRSEP@ +HAVE_STRTOD = @HAVE_STRTOD@ +HAVE_STRTOL = @HAVE_STRTOL@ +HAVE_STRTOLD = @HAVE_STRTOLD@ +HAVE_STRTOLL = @HAVE_STRTOLL@ +HAVE_STRTOUL = @HAVE_STRTOUL@ +HAVE_STRTOULL = @HAVE_STRTOULL@ +HAVE_STRUCT_ADDRINFO = @HAVE_STRUCT_ADDRINFO@ +HAVE_STRUCT_RANDOM_DATA = @HAVE_STRUCT_RANDOM_DATA@ +HAVE_STRUCT_SCHED_PARAM = @HAVE_STRUCT_SCHED_PARAM@ +HAVE_STRUCT_SIGACTION_SA_SIGACTION = @HAVE_STRUCT_SIGACTION_SA_SIGACTION@ +HAVE_STRUCT_SOCKADDR_STORAGE = @HAVE_STRUCT_SOCKADDR_STORAGE@ +HAVE_STRUCT_SOCKADDR_STORAGE_SS_FAMILY = @HAVE_STRUCT_SOCKADDR_STORAGE_SS_FAMILY@ +HAVE_STRUCT_TIMEVAL = @HAVE_STRUCT_TIMEVAL@ +HAVE_STRVERSCMP = @HAVE_STRVERSCMP@ +HAVE_SYMLINK = @HAVE_SYMLINK@ +HAVE_SYMLINKAT = @HAVE_SYMLINKAT@ +HAVE_SYS_BITYPES_H = @HAVE_SYS_BITYPES_H@ +HAVE_SYS_CDEFS_H = @HAVE_SYS_CDEFS_H@ +HAVE_SYS_INTTYPES_H = @HAVE_SYS_INTTYPES_H@ +HAVE_SYS_IOCTL_H = @HAVE_SYS_IOCTL_H@ +HAVE_SYS_LOADAVG_H = @HAVE_SYS_LOADAVG_H@ +HAVE_SYS_PARAM_H = @HAVE_SYS_PARAM_H@ +HAVE_SYS_SELECT_H = @HAVE_SYS_SELECT_H@ +HAVE_SYS_SOCKET_H = @HAVE_SYS_SOCKET_H@ +HAVE_SYS_TIME_H = @HAVE_SYS_TIME_H@ +HAVE_SYS_TYPES_H = @HAVE_SYS_TYPES_H@ +HAVE_SYS_UIO_H = @HAVE_SYS_UIO_H@ +HAVE_TIMEGM = @HAVE_TIMEGM@ +HAVE_TIMESPEC_GET = @HAVE_TIMESPEC_GET@ +HAVE_TIMEZONE_T = @HAVE_TIMEZONE_T@ +HAVE_TYPE_VOLATILE_SIG_ATOMIC_T = @HAVE_TYPE_VOLATILE_SIG_ATOMIC_T@ +HAVE_UNISTD_H = @HAVE_UNISTD_H@ +HAVE_UNLINKAT = @HAVE_UNLINKAT@ +HAVE_UNLOCKPT = @HAVE_UNLOCKPT@ +HAVE_USLEEP = @HAVE_USLEEP@ +HAVE_UTIMENSAT = @HAVE_UTIMENSAT@ +HAVE_VASPRINTF = @HAVE_VASPRINTF@ +HAVE_VDPRINTF = @HAVE_VDPRINTF@ +HAVE_VISIBILITY = @HAVE_VISIBILITY@ +HAVE_WCHAR_H = @HAVE_WCHAR_H@ +HAVE_WCHAR_T = @HAVE_WCHAR_T@ +HAVE_WCPCPY = @HAVE_WCPCPY@ +HAVE_WCPNCPY = @HAVE_WCPNCPY@ +HAVE_WCRTOMB = @HAVE_WCRTOMB@ +HAVE_WCSCASECMP = @HAVE_WCSCASECMP@ +HAVE_WCSCAT = @HAVE_WCSCAT@ +HAVE_WCSCHR = @HAVE_WCSCHR@ +HAVE_WCSCMP = @HAVE_WCSCMP@ +HAVE_WCSCOLL = @HAVE_WCSCOLL@ +HAVE_WCSCPY = @HAVE_WCSCPY@ +HAVE_WCSCSPN = @HAVE_WCSCSPN@ +HAVE_WCSDUP = @HAVE_WCSDUP@ +HAVE_WCSFTIME = @HAVE_WCSFTIME@ +HAVE_WCSLEN = @HAVE_WCSLEN@ +HAVE_WCSNCASECMP = @HAVE_WCSNCASECMP@ +HAVE_WCSNCAT = @HAVE_WCSNCAT@ +HAVE_WCSNCMP = @HAVE_WCSNCMP@ +HAVE_WCSNCPY = @HAVE_WCSNCPY@ +HAVE_WCSNLEN = @HAVE_WCSNLEN@ +HAVE_WCSNRTOMBS = @HAVE_WCSNRTOMBS@ +HAVE_WCSPBRK = @HAVE_WCSPBRK@ +HAVE_WCSRCHR = @HAVE_WCSRCHR@ +HAVE_WCSRTOMBS = @HAVE_WCSRTOMBS@ +HAVE_WCSSPN = @HAVE_WCSSPN@ +HAVE_WCSSTR = @HAVE_WCSSTR@ +HAVE_WCSTOK = @HAVE_WCSTOK@ +HAVE_WCSWIDTH = @HAVE_WCSWIDTH@ +HAVE_WCSXFRM = @HAVE_WCSXFRM@ +HAVE_WINSOCK2_H = @HAVE_WINSOCK2_H@ +HAVE_WINT_T = @HAVE_WINT_T@ +HAVE_WMEMCHR = @HAVE_WMEMCHR@ +HAVE_WMEMCMP = @HAVE_WMEMCMP@ +HAVE_WMEMCPY = @HAVE_WMEMCPY@ +HAVE_WMEMMOVE = @HAVE_WMEMMOVE@ +HAVE_WMEMPCPY = @HAVE_WMEMPCPY@ +HAVE_WMEMSET = @HAVE_WMEMSET@ +HAVE_WS2TCPIP_H = @HAVE_WS2TCPIP_H@ +HAVE_XLOCALE_H = @HAVE_XLOCALE_H@ +HAVE__BOOL = @HAVE__BOOL@ +HAVE__EXIT = @HAVE__EXIT@ +HOGWEED_CFLAGS = @HOGWEED_CFLAGS@ +HOGWEED_LIBS = @HOGWEED_LIBS@ +HOSTENT_LIB = @HOSTENT_LIB@ +HTML_DIR = @HTML_DIR@ +INCLUDE_NEXT = @INCLUDE_NEXT@ +INCLUDE_NEXT_AS_FIRST_DIRECTIVE = @INCLUDE_NEXT_AS_FIRST_DIRECTIVE@ +INET_NTOP_LIB = @INET_NTOP_LIB@ +INET_PTON_LIB = @INET_PTON_LIB@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +INT32_MAX_LT_INTMAX_MAX = @INT32_MAX_LT_INTMAX_MAX@ +INT64_MAX_EQ_LONG_MAX = @INT64_MAX_EQ_LONG_MAX@ +INTLLIBS = @INTLLIBS@ +INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@ +LCOV = @LCOV@ +LD = @LD@ +LDDPOSTPROC = @LDDPOSTPROC@ +LDDPROG = @LDDPROG@ +LDFLAGS = @LDFLAGS@ +LIBATOMIC_LIBS = @LIBATOMIC_LIBS@ +LIBBROTLIDEC_CFLAGS = @LIBBROTLIDEC_CFLAGS@ +LIBBROTLIDEC_LIBS = @LIBBROTLIDEC_LIBS@ +LIBBROTLIENC_CFLAGS = @LIBBROTLIENC_CFLAGS@ +LIBBROTLIENC_LIBS = @LIBBROTLIENC_LIBS@ +LIBCRYPTO = @LIBCRYPTO@ +LIBCRYPTO_PREFIX = @LIBCRYPTO_PREFIX@ +LIBDL = @LIBDL@ +LIBDL_PREFIX = @LIBDL_PREFIX@ +LIBEV = @LIBEV@ +LIBEV_LIBS = @LIBEV_LIBS@ +LIBEV_PREFIX = @LIBEV_PREFIX@ +LIBGNUTLS_CFLAGS = @LIBGNUTLS_CFLAGS@ +LIBGNUTLS_LIBS = @LIBGNUTLS_LIBS@ +LIBICONV = @LIBICONV@ +LIBIDN2_CFLAGS = @LIBIDN2_CFLAGS@ +LIBIDN2_LIBS = @LIBIDN2_LIBS@ +LIBINTL = @LIBINTL@ +LIBKCAPI_CFLAGS = @LIBKCAPI_CFLAGS@ +LIBKCAPI_LIBS = @LIBKCAPI_LIBS@ +LIBMULTITHREAD = @LIBMULTITHREAD@ +LIBOBJS = @LIBOBJS@ +LIBPMULTITHREAD = @LIBPMULTITHREAD@ +LIBPTHREAD = @LIBPTHREAD@ +LIBPTHREAD_PREFIX = @LIBPTHREAD_PREFIX@ +LIBRT = @LIBRT@ +LIBRT_PREFIX = @LIBRT_PREFIX@ +LIBS = @LIBS@ +LIBSECCOMP = @LIBSECCOMP@ +LIBSECCOMP_PREFIX = @LIBSECCOMP_PREFIX@ +LIBSOCKET = @LIBSOCKET@ +LIBSTDTHREAD = @LIBSTDTHREAD@ +LIBTASN1_CFLAGS = @LIBTASN1_CFLAGS@ +LIBTASN1_LIBS = @LIBTASN1_LIBS@ +LIBTESTS_LIBDEPS = @LIBTESTS_LIBDEPS@ +LIBTHREAD = @LIBTHREAD@ +LIBTOOL = @LIBTOOL@ +LIBUNISTRING = @LIBUNISTRING@ +LIBUNISTRING_UNICTYPE_H = @LIBUNISTRING_UNICTYPE_H@ +LIBUNISTRING_UNINORM_H = @LIBUNISTRING_UNINORM_H@ +LIBUNISTRING_UNISTR_H = @LIBUNISTRING_UNISTR_H@ +LIBUNISTRING_UNITYPES_H = @LIBUNISTRING_UNITYPES_H@ +LIBZ = @LIBZ@ +LIBZSTD_CFLAGS = @LIBZSTD_CFLAGS@ +LIBZSTD_LIBS = @LIBZSTD_LIBS@ +LIBZ_PC = @LIBZ_PC@ +LIBZ_PREFIX = @LIBZ_PREFIX@ +LIB_CLOCK_GETTIME = @LIB_CLOCK_GETTIME@ +LIB_NANOSLEEP = @LIB_NANOSLEEP@ +LIB_PTHREAD = @LIB_PTHREAD@ +LIB_PTHREAD_SIGMASK = @LIB_PTHREAD_SIGMASK@ +LIB_SCHED_YIELD = @LIB_SCHED_YIELD@ +LIB_SELECT = @LIB_SELECT@ +LIB_SEMAPHORE = @LIB_SEMAPHORE@ +LIB_SETLOCALE = @LIB_SETLOCALE@ +LIB_SETLOCALE_NULL = @LIB_SETLOCALE_NULL@ +LIMITS_H = @LIMITS_H@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LOCALENAME_ENHANCE_LOCALE_FUNCS = @LOCALENAME_ENHANCE_LOCALE_FUNCS@ +LOCALE_FR = @LOCALE_FR@ +LOCALE_FR_UTF8 = @LOCALE_FR_UTF8@ +LOCALE_JA = @LOCALE_JA@ +LOCALE_TR_UTF8 = @LOCALE_TR_UTF8@ +LOCALE_ZH_CN = @LOCALE_ZH_CN@ +LOG_VALGRIND = @LOG_VALGRIND@ +LTALLOCA = @LTALLOCA@ +LTLIBCRYPTO = @LTLIBCRYPTO@ +LTLIBDL = @LTLIBDL@ +LTLIBEV = @LTLIBEV@ +LTLIBICONV = @LTLIBICONV@ +LTLIBINTL = @LTLIBINTL@ +LTLIBMULTITHREAD = @LTLIBMULTITHREAD@ +LTLIBOBJS = @LTLIBOBJS@ +LTLIBPTHREAD = @LTLIBPTHREAD@ +LTLIBRT = @LTLIBRT@ +LTLIBSECCOMP = @LTLIBSECCOMP@ +LTLIBTHREAD = @LTLIBTHREAD@ +LTLIBZ = @LTLIBZ@ +LT_AGE = @LT_AGE@ +LT_CURRENT = @LT_CURRENT@ +LT_DANE_AGE = @LT_DANE_AGE@ +LT_DANE_CURRENT = @LT_DANE_CURRENT@ +LT_DANE_REVISION = @LT_DANE_REVISION@ +LT_REVISION = @LT_REVISION@ +LT_SSL_AGE = @LT_SSL_AGE@ +LT_SSL_CURRENT = @LT_SSL_CURRENT@ +LT_SSL_REVISION = @LT_SSL_REVISION@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ +LT_XSSL_AGE = @LT_XSSL_AGE@ +LT_XSSL_CURRENT = @LT_XSSL_CURRENT@ +LT_XSSL_REVISION = @LT_XSSL_REVISION@ +MAINT = @MAINT@ +MAJOR_VERSION = @MAJOR_VERSION@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MINOR_VERSION = @MINOR_VERSION@ +MKDIR_P = @MKDIR_P@ +MSGFMT = @MSGFMT@ +MSGMERGE = @MSGMERGE@ +MSGMERGE_FOR_MSGFMT_OPTION = @MSGMERGE_FOR_MSGFMT_OPTION@ +NETINET_IN_H = @NETINET_IN_H@ +NETTLE_CFLAGS = @NETTLE_CFLAGS@ +NETTLE_LIBS = @NETTLE_LIBS@ +NEXT_ARPA_INET_H = @NEXT_ARPA_INET_H@ +NEXT_AS_FIRST_DIRECTIVE_ARPA_INET_H = @NEXT_AS_FIRST_DIRECTIVE_ARPA_INET_H@ +NEXT_AS_FIRST_DIRECTIVE_CTYPE_H = @NEXT_AS_FIRST_DIRECTIVE_CTYPE_H@ +NEXT_AS_FIRST_DIRECTIVE_ERRNO_H = @NEXT_AS_FIRST_DIRECTIVE_ERRNO_H@ +NEXT_AS_FIRST_DIRECTIVE_FCNTL_H = @NEXT_AS_FIRST_DIRECTIVE_FCNTL_H@ +NEXT_AS_FIRST_DIRECTIVE_FLOAT_H = @NEXT_AS_FIRST_DIRECTIVE_FLOAT_H@ +NEXT_AS_FIRST_DIRECTIVE_INTTYPES_H = @NEXT_AS_FIRST_DIRECTIVE_INTTYPES_H@ +NEXT_AS_FIRST_DIRECTIVE_LANGINFO_H = @NEXT_AS_FIRST_DIRECTIVE_LANGINFO_H@ +NEXT_AS_FIRST_DIRECTIVE_LIMITS_H = @NEXT_AS_FIRST_DIRECTIVE_LIMITS_H@ +NEXT_AS_FIRST_DIRECTIVE_LOCALE_H = @NEXT_AS_FIRST_DIRECTIVE_LOCALE_H@ +NEXT_AS_FIRST_DIRECTIVE_NETDB_H = @NEXT_AS_FIRST_DIRECTIVE_NETDB_H@ +NEXT_AS_FIRST_DIRECTIVE_NETINET_IN_H = @NEXT_AS_FIRST_DIRECTIVE_NETINET_IN_H@ +NEXT_AS_FIRST_DIRECTIVE_PTHREAD_H = @NEXT_AS_FIRST_DIRECTIVE_PTHREAD_H@ +NEXT_AS_FIRST_DIRECTIVE_SCHED_H = @NEXT_AS_FIRST_DIRECTIVE_SCHED_H@ +NEXT_AS_FIRST_DIRECTIVE_SIGNAL_H = @NEXT_AS_FIRST_DIRECTIVE_SIGNAL_H@ +NEXT_AS_FIRST_DIRECTIVE_STDDEF_H = @NEXT_AS_FIRST_DIRECTIVE_STDDEF_H@ +NEXT_AS_FIRST_DIRECTIVE_STDINT_H = @NEXT_AS_FIRST_DIRECTIVE_STDINT_H@ +NEXT_AS_FIRST_DIRECTIVE_STDIO_H = @NEXT_AS_FIRST_DIRECTIVE_STDIO_H@ +NEXT_AS_FIRST_DIRECTIVE_STDLIB_H = @NEXT_AS_FIRST_DIRECTIVE_STDLIB_H@ +NEXT_AS_FIRST_DIRECTIVE_STRINGS_H = @NEXT_AS_FIRST_DIRECTIVE_STRINGS_H@ +NEXT_AS_FIRST_DIRECTIVE_STRING_H = @NEXT_AS_FIRST_DIRECTIVE_STRING_H@ +NEXT_AS_FIRST_DIRECTIVE_SYS_IOCTL_H = @NEXT_AS_FIRST_DIRECTIVE_SYS_IOCTL_H@ +NEXT_AS_FIRST_DIRECTIVE_SYS_SELECT_H = @NEXT_AS_FIRST_DIRECTIVE_SYS_SELECT_H@ +NEXT_AS_FIRST_DIRECTIVE_SYS_SOCKET_H = @NEXT_AS_FIRST_DIRECTIVE_SYS_SOCKET_H@ +NEXT_AS_FIRST_DIRECTIVE_SYS_STAT_H = @NEXT_AS_FIRST_DIRECTIVE_SYS_STAT_H@ +NEXT_AS_FIRST_DIRECTIVE_SYS_TIME_H = @NEXT_AS_FIRST_DIRECTIVE_SYS_TIME_H@ +NEXT_AS_FIRST_DIRECTIVE_SYS_TYPES_H = @NEXT_AS_FIRST_DIRECTIVE_SYS_TYPES_H@ +NEXT_AS_FIRST_DIRECTIVE_SYS_UIO_H = @NEXT_AS_FIRST_DIRECTIVE_SYS_UIO_H@ +NEXT_AS_FIRST_DIRECTIVE_TIME_H = @NEXT_AS_FIRST_DIRECTIVE_TIME_H@ +NEXT_AS_FIRST_DIRECTIVE_UNISTD_H = @NEXT_AS_FIRST_DIRECTIVE_UNISTD_H@ +NEXT_AS_FIRST_DIRECTIVE_WCHAR_H = @NEXT_AS_FIRST_DIRECTIVE_WCHAR_H@ +NEXT_CTYPE_H = @NEXT_CTYPE_H@ +NEXT_ERRNO_H = @NEXT_ERRNO_H@ +NEXT_FCNTL_H = @NEXT_FCNTL_H@ +NEXT_FLOAT_H = @NEXT_FLOAT_H@ +NEXT_INTTYPES_H = @NEXT_INTTYPES_H@ +NEXT_LANGINFO_H = @NEXT_LANGINFO_H@ +NEXT_LIMITS_H = @NEXT_LIMITS_H@ +NEXT_LOCALE_H = @NEXT_LOCALE_H@ +NEXT_NETDB_H = @NEXT_NETDB_H@ +NEXT_NETINET_IN_H = @NEXT_NETINET_IN_H@ +NEXT_PTHREAD_H = @NEXT_PTHREAD_H@ +NEXT_SCHED_H = @NEXT_SCHED_H@ +NEXT_SIGNAL_H = @NEXT_SIGNAL_H@ +NEXT_STDDEF_H = @NEXT_STDDEF_H@ +NEXT_STDINT_H = @NEXT_STDINT_H@ +NEXT_STDIO_H = @NEXT_STDIO_H@ +NEXT_STDLIB_H = @NEXT_STDLIB_H@ +NEXT_STRINGS_H = @NEXT_STRINGS_H@ +NEXT_STRING_H = @NEXT_STRING_H@ +NEXT_SYS_IOCTL_H = @NEXT_SYS_IOCTL_H@ +NEXT_SYS_SELECT_H = @NEXT_SYS_SELECT_H@ +NEXT_SYS_SOCKET_H = @NEXT_SYS_SOCKET_H@ +NEXT_SYS_STAT_H = @NEXT_SYS_STAT_H@ +NEXT_SYS_TIME_H = @NEXT_SYS_TIME_H@ +NEXT_SYS_TYPES_H = @NEXT_SYS_TYPES_H@ +NEXT_SYS_UIO_H = @NEXT_SYS_UIO_H@ +NEXT_TIME_H = @NEXT_TIME_H@ +NEXT_UNISTD_H = @NEXT_UNISTD_H@ +NEXT_WCHAR_H = @NEXT_WCHAR_H@ +NM = @NM@ +NMEDIT = @NMEDIT@ +NUMBER_VERSION = @NUMBER_VERSION@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +P11_KIT_CFLAGS = @P11_KIT_CFLAGS@ +P11_KIT_LIBS = @P11_KIT_LIBS@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PARSE_DATETIME_BISON = @PARSE_DATETIME_BISON@ +PATCH_VERSION = @PATCH_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PKCS12_ITER_COUNT = @PKCS12_ITER_COUNT@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +PMCCABE = @PMCCABE@ +POSUB = @POSUB@ +PRAGMA_COLUMNS = @PRAGMA_COLUMNS@ +PRAGMA_SYSTEM_HEADER = @PRAGMA_SYSTEM_HEADER@ +PRIPTR_PREFIX = @PRIPTR_PREFIX@ +PTHREAD_H_DEFINES_STRUCT_TIMESPEC = @PTHREAD_H_DEFINES_STRUCT_TIMESPEC@ +PTRDIFF_T_SUFFIX = @PTRDIFF_T_SUFFIX@ +PYTHON = @PYTHON@ +PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ +PYTHON_PLATFORM = @PYTHON_PLATFORM@ +PYTHON_PREFIX = @PYTHON_PREFIX@ +PYTHON_VERSION = @PYTHON_VERSION@ +RANLIB = @RANLIB@ +REPLACE_ACCESS = @REPLACE_ACCESS@ +REPLACE_ALIGNED_ALLOC = @REPLACE_ALIGNED_ALLOC@ +REPLACE_BTOWC = @REPLACE_BTOWC@ +REPLACE_CALLOC = @REPLACE_CALLOC@ +REPLACE_CANONICALIZE_FILE_NAME = @REPLACE_CANONICALIZE_FILE_NAME@ +REPLACE_CHOWN = @REPLACE_CHOWN@ +REPLACE_CLOSE = @REPLACE_CLOSE@ +REPLACE_CREAT = @REPLACE_CREAT@ +REPLACE_CTIME = @REPLACE_CTIME@ +REPLACE_DPRINTF = @REPLACE_DPRINTF@ +REPLACE_DUP = @REPLACE_DUP@ +REPLACE_DUP2 = @REPLACE_DUP2@ +REPLACE_DUPLOCALE = @REPLACE_DUPLOCALE@ +REPLACE_EXECL = @REPLACE_EXECL@ +REPLACE_EXECLE = @REPLACE_EXECLE@ +REPLACE_EXECLP = @REPLACE_EXECLP@ +REPLACE_EXECV = @REPLACE_EXECV@ +REPLACE_EXECVE = @REPLACE_EXECVE@ +REPLACE_EXECVP = @REPLACE_EXECVP@ +REPLACE_EXECVPE = @REPLACE_EXECVPE@ +REPLACE_FACCESSAT = @REPLACE_FACCESSAT@ +REPLACE_FCHMODAT = @REPLACE_FCHMODAT@ +REPLACE_FCHOWNAT = @REPLACE_FCHOWNAT@ +REPLACE_FCLOSE = @REPLACE_FCLOSE@ +REPLACE_FCNTL = @REPLACE_FCNTL@ +REPLACE_FDOPEN = @REPLACE_FDOPEN@ +REPLACE_FFLUSH = @REPLACE_FFLUSH@ +REPLACE_FFSLL = @REPLACE_FFSLL@ +REPLACE_FOPEN = @REPLACE_FOPEN@ +REPLACE_FPRINTF = @REPLACE_FPRINTF@ +REPLACE_FPURGE = @REPLACE_FPURGE@ +REPLACE_FREE = @REPLACE_FREE@ +REPLACE_FREELOCALE = @REPLACE_FREELOCALE@ +REPLACE_FREOPEN = @REPLACE_FREOPEN@ +REPLACE_FSEEK = @REPLACE_FSEEK@ +REPLACE_FSEEKO = @REPLACE_FSEEKO@ +REPLACE_FSTAT = @REPLACE_FSTAT@ +REPLACE_FSTATAT = @REPLACE_FSTATAT@ +REPLACE_FTELL = @REPLACE_FTELL@ +REPLACE_FTELLO = @REPLACE_FTELLO@ +REPLACE_FTRUNCATE = @REPLACE_FTRUNCATE@ +REPLACE_FUTIMENS = @REPLACE_FUTIMENS@ +REPLACE_GAI_STRERROR = @REPLACE_GAI_STRERROR@ +REPLACE_GETADDRINFO = @REPLACE_GETADDRINFO@ +REPLACE_GETCWD = @REPLACE_GETCWD@ +REPLACE_GETDELIM = @REPLACE_GETDELIM@ +REPLACE_GETDOMAINNAME = @REPLACE_GETDOMAINNAME@ +REPLACE_GETDTABLESIZE = @REPLACE_GETDTABLESIZE@ +REPLACE_GETGROUPS = @REPLACE_GETGROUPS@ +REPLACE_GETLINE = @REPLACE_GETLINE@ +REPLACE_GETLOGIN_R = @REPLACE_GETLOGIN_R@ +REPLACE_GETPAGESIZE = @REPLACE_GETPAGESIZE@ +REPLACE_GETPASS = @REPLACE_GETPASS@ +REPLACE_GETTIMEOFDAY = @REPLACE_GETTIMEOFDAY@ +REPLACE_GMTIME = @REPLACE_GMTIME@ +REPLACE_INET_NTOP = @REPLACE_INET_NTOP@ +REPLACE_INET_PTON = @REPLACE_INET_PTON@ +REPLACE_INITSTATE = @REPLACE_INITSTATE@ +REPLACE_IOCTL = @REPLACE_IOCTL@ +REPLACE_ISATTY = @REPLACE_ISATTY@ +REPLACE_ITOLD = @REPLACE_ITOLD@ +REPLACE_LCHOWN = @REPLACE_LCHOWN@ +REPLACE_LINK = @REPLACE_LINK@ +REPLACE_LINKAT = @REPLACE_LINKAT@ +REPLACE_LOCALECONV = @REPLACE_LOCALECONV@ +REPLACE_LOCALTIME = @REPLACE_LOCALTIME@ +REPLACE_LOCALTIME_R = @REPLACE_LOCALTIME_R@ +REPLACE_LSEEK = @REPLACE_LSEEK@ +REPLACE_LSTAT = @REPLACE_LSTAT@ +REPLACE_MALLOC = @REPLACE_MALLOC@ +REPLACE_MBRLEN = @REPLACE_MBRLEN@ +REPLACE_MBRTOWC = @REPLACE_MBRTOWC@ +REPLACE_MBSINIT = @REPLACE_MBSINIT@ +REPLACE_MBSNRTOWCS = @REPLACE_MBSNRTOWCS@ +REPLACE_MBSRTOWCS = @REPLACE_MBSRTOWCS@ +REPLACE_MBSTATE_T = @REPLACE_MBSTATE_T@ +REPLACE_MBTOWC = @REPLACE_MBTOWC@ +REPLACE_MEMCHR = @REPLACE_MEMCHR@ +REPLACE_MEMMEM = @REPLACE_MEMMEM@ +REPLACE_MKDIR = @REPLACE_MKDIR@ +REPLACE_MKFIFO = @REPLACE_MKFIFO@ +REPLACE_MKFIFOAT = @REPLACE_MKFIFOAT@ +REPLACE_MKNOD = @REPLACE_MKNOD@ +REPLACE_MKNODAT = @REPLACE_MKNODAT@ +REPLACE_MKSTEMP = @REPLACE_MKSTEMP@ +REPLACE_MKTIME = @REPLACE_MKTIME@ +REPLACE_NANOSLEEP = @REPLACE_NANOSLEEP@ +REPLACE_NEWLOCALE = @REPLACE_NEWLOCALE@ +REPLACE_NL_LANGINFO = @REPLACE_NL_LANGINFO@ +REPLACE_NULL = @REPLACE_NULL@ +REPLACE_OBSTACK_PRINTF = @REPLACE_OBSTACK_PRINTF@ +REPLACE_OPEN = @REPLACE_OPEN@ +REPLACE_OPENAT = @REPLACE_OPENAT@ +REPLACE_PERROR = @REPLACE_PERROR@ +REPLACE_POPEN = @REPLACE_POPEN@ +REPLACE_POSIX_MEMALIGN = @REPLACE_POSIX_MEMALIGN@ +REPLACE_PREAD = @REPLACE_PREAD@ +REPLACE_PRINTF = @REPLACE_PRINTF@ +REPLACE_PSELECT = @REPLACE_PSELECT@ +REPLACE_PTHREAD_ATTR_DESTROY = @REPLACE_PTHREAD_ATTR_DESTROY@ +REPLACE_PTHREAD_ATTR_GETDETACHSTATE = @REPLACE_PTHREAD_ATTR_GETDETACHSTATE@ +REPLACE_PTHREAD_ATTR_INIT = @REPLACE_PTHREAD_ATTR_INIT@ +REPLACE_PTHREAD_ATTR_SETDETACHSTATE = @REPLACE_PTHREAD_ATTR_SETDETACHSTATE@ +REPLACE_PTHREAD_CONDATTR_DESTROY = @REPLACE_PTHREAD_CONDATTR_DESTROY@ +REPLACE_PTHREAD_CONDATTR_INIT = @REPLACE_PTHREAD_CONDATTR_INIT@ +REPLACE_PTHREAD_COND_BROADCAST = @REPLACE_PTHREAD_COND_BROADCAST@ +REPLACE_PTHREAD_COND_DESTROY = @REPLACE_PTHREAD_COND_DESTROY@ +REPLACE_PTHREAD_COND_INIT = @REPLACE_PTHREAD_COND_INIT@ +REPLACE_PTHREAD_COND_SIGNAL = @REPLACE_PTHREAD_COND_SIGNAL@ +REPLACE_PTHREAD_COND_TIMEDWAIT = @REPLACE_PTHREAD_COND_TIMEDWAIT@ +REPLACE_PTHREAD_COND_WAIT = @REPLACE_PTHREAD_COND_WAIT@ +REPLACE_PTHREAD_CREATE = @REPLACE_PTHREAD_CREATE@ +REPLACE_PTHREAD_DETACH = @REPLACE_PTHREAD_DETACH@ +REPLACE_PTHREAD_EQUAL = @REPLACE_PTHREAD_EQUAL@ +REPLACE_PTHREAD_EXIT = @REPLACE_PTHREAD_EXIT@ +REPLACE_PTHREAD_GETSPECIFIC = @REPLACE_PTHREAD_GETSPECIFIC@ +REPLACE_PTHREAD_JOIN = @REPLACE_PTHREAD_JOIN@ +REPLACE_PTHREAD_KEY_CREATE = @REPLACE_PTHREAD_KEY_CREATE@ +REPLACE_PTHREAD_KEY_DELETE = @REPLACE_PTHREAD_KEY_DELETE@ +REPLACE_PTHREAD_MUTEXATTR_DESTROY = @REPLACE_PTHREAD_MUTEXATTR_DESTROY@ +REPLACE_PTHREAD_MUTEXATTR_GETROBUST = @REPLACE_PTHREAD_MUTEXATTR_GETROBUST@ +REPLACE_PTHREAD_MUTEXATTR_GETTYPE = @REPLACE_PTHREAD_MUTEXATTR_GETTYPE@ +REPLACE_PTHREAD_MUTEXATTR_INIT = @REPLACE_PTHREAD_MUTEXATTR_INIT@ +REPLACE_PTHREAD_MUTEXATTR_SETROBUST = @REPLACE_PTHREAD_MUTEXATTR_SETROBUST@ +REPLACE_PTHREAD_MUTEXATTR_SETTYPE = @REPLACE_PTHREAD_MUTEXATTR_SETTYPE@ +REPLACE_PTHREAD_MUTEX_DESTROY = @REPLACE_PTHREAD_MUTEX_DESTROY@ +REPLACE_PTHREAD_MUTEX_INIT = @REPLACE_PTHREAD_MUTEX_INIT@ +REPLACE_PTHREAD_MUTEX_LOCK = @REPLACE_PTHREAD_MUTEX_LOCK@ +REPLACE_PTHREAD_MUTEX_TIMEDLOCK = @REPLACE_PTHREAD_MUTEX_TIMEDLOCK@ +REPLACE_PTHREAD_MUTEX_TRYLOCK = @REPLACE_PTHREAD_MUTEX_TRYLOCK@ +REPLACE_PTHREAD_MUTEX_UNLOCK = @REPLACE_PTHREAD_MUTEX_UNLOCK@ +REPLACE_PTHREAD_ONCE = @REPLACE_PTHREAD_ONCE@ +REPLACE_PTHREAD_RWLOCKATTR_DESTROY = @REPLACE_PTHREAD_RWLOCKATTR_DESTROY@ +REPLACE_PTHREAD_RWLOCKATTR_INIT = @REPLACE_PTHREAD_RWLOCKATTR_INIT@ +REPLACE_PTHREAD_RWLOCK_DESTROY = @REPLACE_PTHREAD_RWLOCK_DESTROY@ +REPLACE_PTHREAD_RWLOCK_INIT = @REPLACE_PTHREAD_RWLOCK_INIT@ +REPLACE_PTHREAD_RWLOCK_RDLOCK = @REPLACE_PTHREAD_RWLOCK_RDLOCK@ +REPLACE_PTHREAD_RWLOCK_TIMEDRDLOCK = @REPLACE_PTHREAD_RWLOCK_TIMEDRDLOCK@ +REPLACE_PTHREAD_RWLOCK_TIMEDWRLOCK = @REPLACE_PTHREAD_RWLOCK_TIMEDWRLOCK@ +REPLACE_PTHREAD_RWLOCK_TRYRDLOCK = @REPLACE_PTHREAD_RWLOCK_TRYRDLOCK@ +REPLACE_PTHREAD_RWLOCK_TRYWRLOCK = @REPLACE_PTHREAD_RWLOCK_TRYWRLOCK@ +REPLACE_PTHREAD_RWLOCK_UNLOCK = @REPLACE_PTHREAD_RWLOCK_UNLOCK@ +REPLACE_PTHREAD_RWLOCK_WRLOCK = @REPLACE_PTHREAD_RWLOCK_WRLOCK@ +REPLACE_PTHREAD_SELF = @REPLACE_PTHREAD_SELF@ +REPLACE_PTHREAD_SETSPECIFIC = @REPLACE_PTHREAD_SETSPECIFIC@ +REPLACE_PTHREAD_SIGMASK = @REPLACE_PTHREAD_SIGMASK@ +REPLACE_PTHREAD_SPIN_DESTROY = @REPLACE_PTHREAD_SPIN_DESTROY@ +REPLACE_PTHREAD_SPIN_INIT = @REPLACE_PTHREAD_SPIN_INIT@ +REPLACE_PTHREAD_SPIN_LOCK = @REPLACE_PTHREAD_SPIN_LOCK@ +REPLACE_PTHREAD_SPIN_TRYLOCK = @REPLACE_PTHREAD_SPIN_TRYLOCK@ +REPLACE_PTHREAD_SPIN_UNLOCK = @REPLACE_PTHREAD_SPIN_UNLOCK@ +REPLACE_PTSNAME = @REPLACE_PTSNAME@ +REPLACE_PTSNAME_R = @REPLACE_PTSNAME_R@ +REPLACE_PUTENV = @REPLACE_PUTENV@ +REPLACE_PWRITE = @REPLACE_PWRITE@ +REPLACE_QSORT_R = @REPLACE_QSORT_R@ +REPLACE_RAISE = @REPLACE_RAISE@ +REPLACE_RANDOM = @REPLACE_RANDOM@ +REPLACE_RANDOM_R = @REPLACE_RANDOM_R@ +REPLACE_READ = @REPLACE_READ@ +REPLACE_READLINK = @REPLACE_READLINK@ +REPLACE_READLINKAT = @REPLACE_READLINKAT@ +REPLACE_REALLOC = @REPLACE_REALLOC@ +REPLACE_REALLOCARRAY = @REPLACE_REALLOCARRAY@ +REPLACE_REALPATH = @REPLACE_REALPATH@ +REPLACE_REMOVE = @REPLACE_REMOVE@ +REPLACE_RENAME = @REPLACE_RENAME@ +REPLACE_RENAMEAT = @REPLACE_RENAMEAT@ +REPLACE_RMDIR = @REPLACE_RMDIR@ +REPLACE_SCHED_YIELD = @REPLACE_SCHED_YIELD@ +REPLACE_SELECT = @REPLACE_SELECT@ +REPLACE_SETENV = @REPLACE_SETENV@ +REPLACE_SETLOCALE = @REPLACE_SETLOCALE@ +REPLACE_SETSTATE = @REPLACE_SETSTATE@ +REPLACE_SLEEP = @REPLACE_SLEEP@ +REPLACE_SNPRINTF = @REPLACE_SNPRINTF@ +REPLACE_SPRINTF = @REPLACE_SPRINTF@ +REPLACE_STAT = @REPLACE_STAT@ +REPLACE_STDIO_READ_FUNCS = @REPLACE_STDIO_READ_FUNCS@ +REPLACE_STDIO_WRITE_FUNCS = @REPLACE_STDIO_WRITE_FUNCS@ +REPLACE_STPNCPY = @REPLACE_STPNCPY@ +REPLACE_STRCASESTR = @REPLACE_STRCASESTR@ +REPLACE_STRCHRNUL = @REPLACE_STRCHRNUL@ +REPLACE_STRDUP = @REPLACE_STRDUP@ +REPLACE_STRERROR = @REPLACE_STRERROR@ +REPLACE_STRERRORNAME_NP = @REPLACE_STRERRORNAME_NP@ +REPLACE_STRERROR_R = @REPLACE_STRERROR_R@ +REPLACE_STRFTIME = @REPLACE_STRFTIME@ +REPLACE_STRNCAT = @REPLACE_STRNCAT@ +REPLACE_STRNDUP = @REPLACE_STRNDUP@ +REPLACE_STRNLEN = @REPLACE_STRNLEN@ +REPLACE_STRSIGNAL = @REPLACE_STRSIGNAL@ +REPLACE_STRSTR = @REPLACE_STRSTR@ +REPLACE_STRTOD = @REPLACE_STRTOD@ +REPLACE_STRTOIMAX = @REPLACE_STRTOIMAX@ +REPLACE_STRTOK_R = @REPLACE_STRTOK_R@ +REPLACE_STRTOL = @REPLACE_STRTOL@ +REPLACE_STRTOLD = @REPLACE_STRTOLD@ +REPLACE_STRTOLL = @REPLACE_STRTOLL@ +REPLACE_STRTOUL = @REPLACE_STRTOUL@ +REPLACE_STRTOULL = @REPLACE_STRTOULL@ +REPLACE_STRTOUMAX = @REPLACE_STRTOUMAX@ +REPLACE_STRUCT_LCONV = @REPLACE_STRUCT_LCONV@ +REPLACE_STRUCT_TIMEVAL = @REPLACE_STRUCT_TIMEVAL@ +REPLACE_SYMLINK = @REPLACE_SYMLINK@ +REPLACE_SYMLINKAT = @REPLACE_SYMLINKAT@ +REPLACE_TIMEGM = @REPLACE_TIMEGM@ +REPLACE_TMPFILE = @REPLACE_TMPFILE@ +REPLACE_TRUNCATE = @REPLACE_TRUNCATE@ +REPLACE_TTYNAME_R = @REPLACE_TTYNAME_R@ +REPLACE_TZSET = @REPLACE_TZSET@ +REPLACE_UNLINK = @REPLACE_UNLINK@ +REPLACE_UNLINKAT = @REPLACE_UNLINKAT@ +REPLACE_UNSETENV = @REPLACE_UNSETENV@ +REPLACE_USLEEP = @REPLACE_USLEEP@ +REPLACE_UTIMENSAT = @REPLACE_UTIMENSAT@ +REPLACE_VASPRINTF = @REPLACE_VASPRINTF@ +REPLACE_VDPRINTF = @REPLACE_VDPRINTF@ +REPLACE_VFPRINTF = @REPLACE_VFPRINTF@ +REPLACE_VPRINTF = @REPLACE_VPRINTF@ +REPLACE_VSNPRINTF = @REPLACE_VSNPRINTF@ +REPLACE_VSPRINTF = @REPLACE_VSPRINTF@ +REPLACE_WCRTOMB = @REPLACE_WCRTOMB@ +REPLACE_WCSFTIME = @REPLACE_WCSFTIME@ +REPLACE_WCSNRTOMBS = @REPLACE_WCSNRTOMBS@ +REPLACE_WCSRTOMBS = @REPLACE_WCSRTOMBS@ +REPLACE_WCSTOK = @REPLACE_WCSTOK@ +REPLACE_WCSWIDTH = @REPLACE_WCSWIDTH@ +REPLACE_WCTOB = @REPLACE_WCTOB@ +REPLACE_WCTOMB = @REPLACE_WCTOMB@ +REPLACE_WCWIDTH = @REPLACE_WCWIDTH@ +REPLACE_WRITE = @REPLACE_WRITE@ +SED = @SED@ +SERVENT_LIB = @SERVENT_LIB@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +SIG_ATOMIC_T_SUFFIX = @SIG_ATOMIC_T_SUFFIX@ +SIZE_T_SUFFIX = @SIZE_T_SUFFIX@ +STDALIGN_H = @STDALIGN_H@ +STDBOOL_H = @STDBOOL_H@ +STDDEF_H = @STDDEF_H@ +STDINT_H = @STDINT_H@ +STRIP = @STRIP@ +SYS_IOCTL_H_HAVE_WINSOCK2_H = @SYS_IOCTL_H_HAVE_WINSOCK2_H@ +SYS_IOCTL_H_HAVE_WINSOCK2_H_AND_USE_SOCKETS = @SYS_IOCTL_H_HAVE_WINSOCK2_H_AND_USE_SOCKETS@ +SYS_TIME_H_DEFINES_STRUCT_TIMESPEC = @SYS_TIME_H_DEFINES_STRUCT_TIMESPEC@ +TIME_H_DEFINES_STRUCT_TIMESPEC = @TIME_H_DEFINES_STRUCT_TIMESPEC@ +TIME_H_DEFINES_TIME_UTC = @TIME_H_DEFINES_TIME_UTC@ +TROUSERS_LIB = @TROUSERS_LIB@ +TSS2_CFLAGS = @TSS2_CFLAGS@ +TSS2_LIBS = @TSS2_LIBS@ +TSS_CFLAGS = @TSS_CFLAGS@ +TSS_LIBS = @TSS_LIBS@ +UINT32_MAX_LT_UINTMAX_MAX = @UINT32_MAX_LT_UINTMAX_MAX@ +UINT64_MAX_EQ_ULONG_MAX = @UINT64_MAX_EQ_ULONG_MAX@ +UNBOUND_CFLAGS = @UNBOUND_CFLAGS@ +UNBOUND_LIBS = @UNBOUND_LIBS@ +UNDEFINE_STRTOK_R = @UNDEFINE_STRTOK_R@ +UNISTD_H_DEFINES_STRUCT_TIMESPEC = @UNISTD_H_DEFINES_STRUCT_TIMESPEC@ +UNISTD_H_HAVE_SYS_RANDOM_H = @UNISTD_H_HAVE_SYS_RANDOM_H@ +UNISTD_H_HAVE_WINSOCK2_H = @UNISTD_H_HAVE_WINSOCK2_H@ +UNISTD_H_HAVE_WINSOCK2_H_AND_USE_SOCKETS = @UNISTD_H_HAVE_WINSOCK2_H_AND_USE_SOCKETS@ +USE_NLS = @USE_NLS@ +VALGRIND = @VALGRIND@ +VALGRINDFLAGS = @VALGRINDFLAGS@ +VALGRIND_PROGRAM = @VALGRIND_PROGRAM@ +VERSION = @VERSION@ +WARN_CFLAGS = @WARN_CFLAGS@ +WCHAR_T_SUFFIX = @WCHAR_T_SUFFIX@ +WERROR_CFLAGS = @WERROR_CFLAGS@ +WINDOWS_64_BIT_OFF_T = @WINDOWS_64_BIT_OFF_T@ +WINDOWS_64_BIT_ST_SIZE = @WINDOWS_64_BIT_ST_SIZE@ +WINDOWS_STAT_INODES = @WINDOWS_STAT_INODES@ +WINDOWS_STAT_TIMESPEC = @WINDOWS_STAT_TIMESPEC@ +WINT_T_SUFFIX = @WINT_T_SUFFIX@ +WSTACK_CFLAGS = @WSTACK_CFLAGS@ +XGETTEXT = @XGETTEXT@ +XGETTEXT_015 = @XGETTEXT_015@ +XGETTEXT_EXTRA_OPTIONS = @XGETTEXT_EXTRA_OPTIONS@ +YACC = @YACC@ +YFLAGS = @YFLAGS@ +YIELD_LIB = @YIELD_LIB@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +ac_cv_sizeof_time_t = @ac_cv_sizeof_time_t@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +ggl_LIBOBJS = @ggl_LIBOBJS@ +ggl_LTLIBOBJS = @ggl_LTLIBOBJS@ +ggltests_LIBOBJS = @ggltests_LIBOBJS@ +ggltests_LTLIBOBJS = @ggltests_LTLIBOBJS@ +ggltests_WITNESS = @ggltests_WITNESS@ +gl_LIBOBJS = @gl_LIBOBJS@ +gl_LTLIBOBJS = @gl_LTLIBOBJS@ +gltests_LIBOBJS = @gltests_LIBOBJS@ +gltests_LTLIBOBJS = @gltests_LTLIBOBJS@ +gltests_WITNESS = @gltests_WITNESS@ +gnutls_so = @gnutls_so@ +guile_snarf = @guile_snarf@ +guileextensiondir = @guileextensiondir@ +guilesiteccachedir = @guilesiteccachedir@ +guilesitedir = @guilesitedir@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +ifGNUmake = @ifGNUmake@ +ifnGNUmake = @ifnGNUmake@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +maybe_guileextensiondir = @maybe_guileextensiondir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +pkgpyexecdir = @pkgpyexecdir@ +pkgpythondir = @pkgpythondir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +pyexecdir = @pyexecdir@ +pythondir = @pythondir@ +runstatedir = @runstatedir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +unistring_LIBOBJS = @unistring_LIBOBJS@ +unistring_LTLIBOBJS = @unistring_LTLIBOBJS@ +unistringtests_LIBOBJS = @unistringtests_LIBOBJS@ +unistringtests_LTLIBOBJS = @unistringtests_LTLIBOBJS@ +unistringtests_WITNESS = @unistringtests_WITNESS@ +AM_CFLAGS = $(WERROR_CFLAGS) $(WSTACK_CFLAGS) $(WARN_CFLAGS) $(NETTLE_CFLAGS) \ + $(LIBTASN1_CFLAGS) $(LIBIDN2_CFLAGS) $(P11_KIT_CFLAGS) $(LIBZSTD_CFLAGS) \ + $(CODE_COVERAGE_CFLAGS) + +COMMON_LINK_FLAGS = $(CODE_COVERAGE_LDFLAGS) +V_GPERF = $(V_GPERF_@AM_V@) +V_GPERF_ = $(V_GPERF_@AM_DEFAULT_V@) +V_GPERF_0 = @echo " GPERF " $@; +AM_CPPFLAGS = -I$(srcdir)/../../gl -I$(builddir)/../../gl \ + -I$(srcdir)/../includes -I$(builddir)/../includes \ + -I$(srcdir)/.. $(am__append_1) +EXTRA_DIST = supported_exts.gperf +BUILT_SOURCES = supported_exts.h +noinst_LTLIBRARIES = libgnutls_x509.la +libgnutls_x509_la_SOURCES = common.c key_encode.c common.h \ + key_decode.c time.c crl.c crl_write.c crq.c dn.c attributes.c \ + attributes.h prov-seed.c prov-seed.h extensions.c mpi.c \ + output.c pkcs12.c pkcs12_bag.c pkcs12_encr.c pkcs7.c \ + pkcs7-attrs.c pkcs7-crypt.c pkcs7_int.h privkey.c \ + privkey_pkcs8.c privkey_pkcs8_pbes1.c privkey_openssl.c \ + hostname-verify.c sign.c verify.c x509.c x509_dn.c x509_int.h \ + x509_write.c name_constraints.c verify-high.c verify-high2.c \ + verify-high.h x509_ext.c email-verify.c pkcs7-output.c \ + virt-san.c virt-san.h spki.c x509_ext_int.h tls_features.c \ + krb5.c krb5.h ip.c ip.h ip-in-cidr.h supported_exts.h ocsp.h \ + $(am__append_2) +all: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(top_srcdir)/lib/common.mk $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign lib/x509/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign lib/x509/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ + esac; +$(top_srcdir)/lib/common.mk $(am__empty): + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +clean-noinstLTLIBRARIES: + -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) + @list='$(noinst_LTLIBRARIES)'; \ + locs=`for p in $$list; do echo $$p; done | \ + sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ + sort -u`; \ + test -z "$$locs" || { \ + echo rm -f $${locs}; \ + rm -f $${locs}; \ + } + +libgnutls_x509.la: $(libgnutls_x509_la_OBJECTS) $(libgnutls_x509_la_DEPENDENCIES) $(EXTRA_libgnutls_x509_la_DEPENDENCIES) + $(AM_V_CCLD)$(LINK) $(libgnutls_x509_la_OBJECTS) $(libgnutls_x509_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/attributes.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/common.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/crl.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/crl_write.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/crq.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dn.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/email-verify.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/extensions.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hostname-verify.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ip.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/key_decode.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/key_encode.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/krb5.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mpi.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/name_constraints.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ocsp.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ocsp_output.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/output.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pkcs12.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pkcs12_bag.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pkcs12_encr.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pkcs7-attrs.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pkcs7-crypt.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pkcs7-output.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pkcs7.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/privkey.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/privkey_openssl.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/privkey_pkcs8.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/privkey_pkcs8_pbes1.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/prov-seed.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sign.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/spki.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/time.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tls_features.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/verify-high.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/verify-high2.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/verify.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/virt-san.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/x509.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/x509_dn.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/x509_ext.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/x509_write.Plo@am__quote@ # am--include-marker + +$(am__depfiles_remade): + @$(MKDIR_P) $(@D) + @echo '# dummy' >$@-t && $(am__mv) $@-t $@ + +am--depfiles: $(am__depfiles_remade) + +.c.o: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< + +.c.obj: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ +@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-am +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-am + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-am + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags +distdir: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) distdir-am + +distdir-am: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) check-am +all-am: Makefile $(LTLIBRARIES) +installdirs: +install: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) install-am +install-exec: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." + -test -z "$(BUILT_SOURCES)" || rm -f $(BUILT_SOURCES) +clean: clean-am + +clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \ + mostlyclean-am + +distclean: distclean-am + -rm -f ./$(DEPDIR)/attributes.Plo + -rm -f ./$(DEPDIR)/common.Plo + -rm -f ./$(DEPDIR)/crl.Plo + -rm -f ./$(DEPDIR)/crl_write.Plo + -rm -f ./$(DEPDIR)/crq.Plo + -rm -f ./$(DEPDIR)/dn.Plo + -rm -f ./$(DEPDIR)/email-verify.Plo + -rm -f ./$(DEPDIR)/extensions.Plo + -rm -f ./$(DEPDIR)/hostname-verify.Plo + -rm -f ./$(DEPDIR)/ip.Plo + -rm -f ./$(DEPDIR)/key_decode.Plo + -rm -f ./$(DEPDIR)/key_encode.Plo + -rm -f ./$(DEPDIR)/krb5.Plo + -rm -f ./$(DEPDIR)/mpi.Plo + -rm -f ./$(DEPDIR)/name_constraints.Plo + -rm -f ./$(DEPDIR)/ocsp.Plo + -rm -f ./$(DEPDIR)/ocsp_output.Plo + -rm -f ./$(DEPDIR)/output.Plo + -rm -f ./$(DEPDIR)/pkcs12.Plo + -rm -f ./$(DEPDIR)/pkcs12_bag.Plo + -rm -f ./$(DEPDIR)/pkcs12_encr.Plo + -rm -f ./$(DEPDIR)/pkcs7-attrs.Plo + -rm -f ./$(DEPDIR)/pkcs7-crypt.Plo + -rm -f ./$(DEPDIR)/pkcs7-output.Plo + -rm -f ./$(DEPDIR)/pkcs7.Plo + -rm -f ./$(DEPDIR)/privkey.Plo + -rm -f ./$(DEPDIR)/privkey_openssl.Plo + -rm -f ./$(DEPDIR)/privkey_pkcs8.Plo + -rm -f ./$(DEPDIR)/privkey_pkcs8_pbes1.Plo + -rm -f ./$(DEPDIR)/prov-seed.Plo + -rm -f ./$(DEPDIR)/sign.Plo + -rm -f ./$(DEPDIR)/spki.Plo + -rm -f ./$(DEPDIR)/time.Plo + -rm -f ./$(DEPDIR)/tls_features.Plo + -rm -f ./$(DEPDIR)/verify-high.Plo + -rm -f ./$(DEPDIR)/verify-high2.Plo + -rm -f ./$(DEPDIR)/verify.Plo + -rm -f ./$(DEPDIR)/virt-san.Plo + -rm -f ./$(DEPDIR)/x509.Plo + -rm -f ./$(DEPDIR)/x509_dn.Plo + -rm -f ./$(DEPDIR)/x509_ext.Plo + -rm -f ./$(DEPDIR)/x509_write.Plo + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f ./$(DEPDIR)/attributes.Plo + -rm -f ./$(DEPDIR)/common.Plo + -rm -f ./$(DEPDIR)/crl.Plo + -rm -f ./$(DEPDIR)/crl_write.Plo + -rm -f ./$(DEPDIR)/crq.Plo + -rm -f ./$(DEPDIR)/dn.Plo + -rm -f ./$(DEPDIR)/email-verify.Plo + -rm -f ./$(DEPDIR)/extensions.Plo + -rm -f ./$(DEPDIR)/hostname-verify.Plo + -rm -f ./$(DEPDIR)/ip.Plo + -rm -f ./$(DEPDIR)/key_decode.Plo + -rm -f ./$(DEPDIR)/key_encode.Plo + -rm -f ./$(DEPDIR)/krb5.Plo + -rm -f ./$(DEPDIR)/mpi.Plo + -rm -f ./$(DEPDIR)/name_constraints.Plo + -rm -f ./$(DEPDIR)/ocsp.Plo + -rm -f ./$(DEPDIR)/ocsp_output.Plo + -rm -f ./$(DEPDIR)/output.Plo + -rm -f ./$(DEPDIR)/pkcs12.Plo + -rm -f ./$(DEPDIR)/pkcs12_bag.Plo + -rm -f ./$(DEPDIR)/pkcs12_encr.Plo + -rm -f ./$(DEPDIR)/pkcs7-attrs.Plo + -rm -f ./$(DEPDIR)/pkcs7-crypt.Plo + -rm -f ./$(DEPDIR)/pkcs7-output.Plo + -rm -f ./$(DEPDIR)/pkcs7.Plo + -rm -f ./$(DEPDIR)/privkey.Plo + -rm -f ./$(DEPDIR)/privkey_openssl.Plo + -rm -f ./$(DEPDIR)/privkey_pkcs8.Plo + -rm -f ./$(DEPDIR)/privkey_pkcs8_pbes1.Plo + -rm -f ./$(DEPDIR)/prov-seed.Plo + -rm -f ./$(DEPDIR)/sign.Plo + -rm -f ./$(DEPDIR)/spki.Plo + -rm -f ./$(DEPDIR)/time.Plo + -rm -f ./$(DEPDIR)/tls_features.Plo + -rm -f ./$(DEPDIR)/verify-high.Plo + -rm -f ./$(DEPDIR)/verify-high2.Plo + -rm -f ./$(DEPDIR)/verify.Plo + -rm -f ./$(DEPDIR)/virt-san.Plo + -rm -f ./$(DEPDIR)/x509.Plo + -rm -f ./$(DEPDIR)/x509_dn.Plo + -rm -f ./$(DEPDIR)/x509_ext.Plo + -rm -f ./$(DEPDIR)/x509_write.Plo + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: + +.MAKE: all check install install-am install-exec install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \ + clean-generic clean-libtool clean-noinstLTLIBRARIES \ + cscopelist-am ctags ctags-am distclean distclean-compile \ + distclean-generic distclean-libtool distclean-tags distdir dvi \ + dvi-am html html-am info info-am install install-am \ + install-data install-data-am install-dvi install-dvi-am \ + install-exec install-exec-am install-html install-html-am \ + install-info install-info-am install-man install-pdf \ + install-pdf-am install-ps install-ps-am install-strip \ + installcheck installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ + tags tags-am uninstall uninstall-am + +.PRECIOUS: Makefile + + +supported_exts.h: $(srcdir)/supported_exts.gperf + $(V_GPERF)$(GPERF) --global-table -t $^ > $@-tmp \ + && sed 's/^const struct supported_exts_st \*/static const struct supported_exts_st \*/' <$@-tmp >$@ \ + && rm -f $@-tmp + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/lib/x509/attributes.c b/lib/x509/attributes.c new file mode 100644 index 0000000..3aab65b --- /dev/null +++ b/lib/x509/attributes.c @@ -0,0 +1,299 @@ +/* + * Copyright (C) 2003-2016 Free Software Foundation, Inc. + * Copyright (C) 2012-2016 Nikos Mavrogiannopoulos + * Copyright (C) 2017 Red Hat, Inc. + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GnuTLS. + * + * The GnuTLS is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/> + * + */ + +#include "gnutls_int.h" + +#include <datum.h> +#include "errors.h" +#include <common.h> +#include <x509.h> +#include "x509_int.h" +#include "attributes.h" + +/* Functions to parse and set the PKIX1 Attributes structure. + */ + +/* Overwrite the given attribute (using the index) + * index here starts from one. + */ +static int +overwrite_attribute(asn1_node asn, const char *root, unsigned indx, + const gnutls_datum_t * ext_data) +{ + char name[MAX_NAME_SIZE], name2[MAX_NAME_SIZE]; + int result; + + snprintf(name, sizeof(name), "%s.?%u", root, indx); + + _gnutls_str_cpy(name2, sizeof(name2), name); + _gnutls_str_cat(name2, sizeof(name2), ".values.?LAST"); + + result = _gnutls_x509_write_value(asn, name2, ext_data); + if (result < 0) { + gnutls_assert(); + return result; + } + + + return 0; +} + +/* Parses an Attribute list in the asn1_struct, and searches for the + * given OID. The index indicates the attribute value to be returned. + * + * If raw==0 only printable data are returned, or + * GNUTLS_E_X509_UNSUPPORTED_ATTRIBUTE. + * + * asn1_attr_name must be a string in the form + * "certificationRequestInfo.attributes" + * + */ +int +_x509_parse_attribute(asn1_node asn1_struct, + const char *attr_name, const char *given_oid, unsigned indx, + int raw, gnutls_datum_t * out) +{ + int k1, result; + char tmpbuffer1[MAX_NAME_SIZE]; + char tmpbuffer3[MAX_NAME_SIZE]; + char value[200]; + gnutls_datum_t td; + char oid[MAX_OID_SIZE]; + int len; + + k1 = 0; + do { + + k1++; + /* create a string like "attribute.?1" + */ + if (attr_name[0] != 0) + snprintf(tmpbuffer1, sizeof(tmpbuffer1), "%s.?%d", + attr_name, k1); + else + snprintf(tmpbuffer1, sizeof(tmpbuffer1), "?%d", + k1); + + len = sizeof(value) - 1; + result = + asn1_read_value(asn1_struct, tmpbuffer1, value, &len); + + if (result == ASN1_ELEMENT_NOT_FOUND) { + gnutls_assert(); + break; + } + + if (result != ASN1_VALUE_NOT_FOUND) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + /* Move to the attribute type and values + */ + /* Read the OID + */ + _gnutls_str_cpy(tmpbuffer3, sizeof(tmpbuffer3), + tmpbuffer1); + _gnutls_str_cat(tmpbuffer3, sizeof(tmpbuffer3), ".type"); + + len = sizeof(oid) - 1; + result = + asn1_read_value(asn1_struct, tmpbuffer3, oid, &len); + + if (result == ASN1_ELEMENT_NOT_FOUND) + break; + else if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + if (strcmp(oid, given_oid) == 0) { /* Found the OID */ + + /* Read the Value + */ + snprintf(tmpbuffer3, sizeof(tmpbuffer3), + "%s.values.?%u", tmpbuffer1, indx + 1); + + len = sizeof(value) - 1; + result = + _gnutls_x509_read_value(asn1_struct, + tmpbuffer3, &td); + + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + if (raw == 0) { + result = + _gnutls_x509_dn_to_string + (oid, td.data, td.size, out); + + _gnutls_free_datum(&td); + + if (result < 0) { + gnutls_assert(); + goto cleanup; + } + return 0; + } else { /* raw!=0 */ + out->data = td.data; + out->size = td.size; + + return 0; + } + } + + } + while (1); + + gnutls_assert(); + + result = GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + + cleanup: + return result; +} + +/* This function will attempt to set the requested attribute in + * the given X509v3 certificate. + * + * Critical will be either 0 or 1. + */ +static int +add_attribute(asn1_node asn, const char *root, const char *attribute_id, + const gnutls_datum_t * ext_data) +{ + int result; + char name[MAX_NAME_SIZE]; + + snprintf(name, sizeof(name), "%s", root); + + /* Add a new attribute in the list. + */ + result = asn1_write_value(asn, name, "NEW", 1); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + snprintf(name, sizeof(name), "%s.?LAST.type", root); + + result = asn1_write_value(asn, name, attribute_id, 1); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + snprintf(name, sizeof(name), "%s.?LAST.values", root); + + result = asn1_write_value(asn, name, "NEW", 1); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + snprintf(name, sizeof(name), "%s.?LAST.values.?LAST", root); + + result = _gnutls_x509_write_value(asn, name, ext_data); + if (result < 0) { + gnutls_assert(); + return result; + } + + return 0; +} + + +int +_x509_set_attribute(asn1_node asn, const char *root, + const char *ext_id, const gnutls_datum_t * ext_data) +{ + int result; + int k, len; + char name[MAX_NAME_SIZE], name2[MAX_NAME_SIZE]; + char extnID[MAX_OID_SIZE]; + + /* Find the index of the given attribute. + */ + k = 0; + do { + k++; + + snprintf(name, sizeof(name), "%s.?%d", root, k); + + len = sizeof(extnID) - 1; + result = asn1_read_value(asn, name, extnID, &len); + + /* move to next + */ + + if (result == ASN1_ELEMENT_NOT_FOUND) { + break; + } + + do { + + _gnutls_str_cpy(name2, sizeof(name2), name); + _gnutls_str_cat(name2, sizeof(name2), ".type"); + + len = sizeof(extnID) - 1; + result = asn1_read_value(asn, name2, extnID, &len); + + if (result == ASN1_ELEMENT_NOT_FOUND) { + gnutls_assert(); + break; + } else if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + /* Handle Extension + */ + if (strcmp(extnID, ext_id) == 0) { + /* attribute was found + */ + return overwrite_attribute(asn, root, k, + ext_data); + } + + + } + while (0); + } + while (1); + + if (result == ASN1_ELEMENT_NOT_FOUND) { + return add_attribute(asn, root, ext_id, ext_data); + } else { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + + return 0; +} diff --git a/lib/x509/attributes.h b/lib/x509/attributes.h new file mode 100644 index 0000000..4ebb7dd --- /dev/null +++ b/lib/x509/attributes.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2017 Red Hat, Inc. + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GnuTLS. + * + * The GnuTLS is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/> + * + */ + +#ifndef GNUTLS_LIB_X509_ATTRIBUTES_H +#define GNUTLS_LIB_X509_ATTRIBUTES_H + +int +_x509_parse_attribute(asn1_node asn1_struct, + const char *attr_name, const char *given_oid, unsigned indx, + int raw, gnutls_datum_t * out); + +int +_x509_set_attribute(asn1_node asn, const char *root, + const char *ext_id, const gnutls_datum_t * ext_data); + +#endif /* GNUTLS_LIB_X509_ATTRIBUTES_H */ diff --git a/lib/x509/common.c b/lib/x509/common.c new file mode 100644 index 0000000..ca0b71c --- /dev/null +++ b/lib/x509/common.c @@ -0,0 +1,1948 @@ +/* + * Copyright (C) 2003-2014 Free Software Foundation, Inc. + * Copyright (C) 2015-2016 Red Hat, Inc. + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GnuTLS. + * + * The GnuTLS is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/> + * + */ + +#include "gnutls_int.h" +#include <libtasn1.h> +#include <datum.h> +#include <global.h> +#include "errors.h" +#include <str.h> +#include <x509.h> +#include <num.h> +#include <x509_b64.h> +#include <c-strcase.h> +#include "x509_int.h" +#include "extras/hex.h" +#include <common.h> + +static int +data2hex(const void *data, size_t data_size, + gnutls_datum_t *out); + +#define ENTRY(oid, ldap, asn, etype) {oid, sizeof(oid)-1, ldap, sizeof(ldap)-1, asn, etype} + +/* when there is no name description */ +#define ENTRY_ND(oid, asn, etype) {oid, sizeof(oid)-1, NULL, 0, asn, etype} + +/* This list contains all the OIDs that may be + * contained in a rdnSequence and are printable. + */ +static const struct oid_to_string _oid2str[] = { + /* PKIX + */ + ENTRY("1.3.6.1.5.5.7.9.2", "placeOfBirth", "PKIX1.DirectoryString", + ASN1_ETYPE_INVALID), + ENTRY("1.3.6.1.5.5.7.9.3", "gender", NULL, ASN1_ETYPE_PRINTABLE_STRING), + ENTRY("1.3.6.1.5.5.7.9.4", "countryOfCitizenship", NULL, + ASN1_ETYPE_PRINTABLE_STRING), + ENTRY("1.3.6.1.5.5.7.9.5", "countryOfResidence", NULL, + ASN1_ETYPE_PRINTABLE_STRING), + + ENTRY("2.5.4.6", "C", NULL, ASN1_ETYPE_PRINTABLE_STRING), + ENTRY("2.5.4.9", "street", "PKIX1.DirectoryString", ASN1_ETYPE_INVALID), + ENTRY("2.5.4.12", "title", "PKIX1.DirectoryString", ASN1_ETYPE_INVALID), + ENTRY("2.5.4.10", "O", "PKIX1.DirectoryString", ASN1_ETYPE_INVALID), + ENTRY("2.5.4.11", "OU", "PKIX1.DirectoryString", ASN1_ETYPE_INVALID), + ENTRY("2.5.4.3", "CN", "PKIX1.DirectoryString", ASN1_ETYPE_INVALID), + ENTRY("2.5.4.7", "L", "PKIX1.DirectoryString", ASN1_ETYPE_INVALID), + ENTRY("2.5.4.8", "ST", "PKIX1.DirectoryString", ASN1_ETYPE_INVALID), + ENTRY("2.5.4.13", "description", "PKIX1.DirectoryString", + ASN1_ETYPE_INVALID), + + ENTRY("2.5.4.5", "serialNumber", NULL, ASN1_ETYPE_PRINTABLE_STRING), + ENTRY("2.5.4.20", "telephoneNumber", NULL, ASN1_ETYPE_PRINTABLE_STRING), + ENTRY("2.5.4.4", "surName", "PKIX1.DirectoryString", + ASN1_ETYPE_INVALID), + ENTRY("2.5.4.43", "initials", "PKIX1.DirectoryString", + ASN1_ETYPE_INVALID), + ENTRY("2.5.4.44", "generationQualifier", "PKIX1.DirectoryString", + ASN1_ETYPE_INVALID), + ENTRY("2.5.4.42", "givenName", "PKIX1.DirectoryString", + ASN1_ETYPE_INVALID), + ENTRY("2.5.4.65", "pseudonym", "PKIX1.DirectoryString", + ASN1_ETYPE_INVALID), + ENTRY("2.5.4.46", "dnQualifier", NULL, ASN1_ETYPE_PRINTABLE_STRING), + ENTRY("2.5.4.17", "postalCode", "PKIX1.DirectoryString", + ASN1_ETYPE_INVALID), + ENTRY("2.5.4.41", "name", "PKIX1.DirectoryString", ASN1_ETYPE_INVALID), + ENTRY("2.5.4.15", "businessCategory", "PKIX1.DirectoryString", + ASN1_ETYPE_INVALID), + + ENTRY("0.9.2342.19200300.100.1.25", "DC", NULL, ASN1_ETYPE_IA5_STRING), + ENTRY("0.9.2342.19200300.100.1.1", "UID", "PKIX1.DirectoryString", + ASN1_ETYPE_INVALID), + ENTRY("1.2.840.113556.1.4.656", "userPrincipalName", "PKIX1.DirectoryString", + ASN1_ETYPE_INVALID), + + /* Extended validation + */ + ENTRY("1.3.6.1.4.1.311.60.2.1.1", + "jurisdictionOfIncorporationLocalityName", + "PKIX1.DirectoryString", ASN1_ETYPE_INVALID), + ENTRY("1.3.6.1.4.1.311.60.2.1.2", + "jurisdictionOfIncorporationStateOrProvinceName", + "PKIX1.DirectoryString", ASN1_ETYPE_INVALID), + ENTRY("1.3.6.1.4.1.311.60.2.1.3", + "jurisdictionOfIncorporationCountryName", + NULL, ASN1_ETYPE_PRINTABLE_STRING), + + /* PKCS #9 + */ + ENTRY("1.2.840.113549.1.9.1", "EMAIL", NULL, ASN1_ETYPE_IA5_STRING), + ENTRY_ND("1.2.840.113549.1.9.7", "PKIX1.pkcs-9-challengePassword", + ASN1_ETYPE_INVALID), + + /* friendly name */ + ENTRY_ND("1.2.840.113549.1.9.20", NULL, ASN1_ETYPE_BMP_STRING), + /* local key id */ + ENTRY_ND("1.2.840.113549.1.9.21", NULL, ASN1_ETYPE_OCTET_STRING), + ENTRY_ND("1.2.840.113549.1.9.4", NULL, ASN1_ETYPE_OCTET_STRING), + + /* rfc3920 section 5.1.1 */ + ENTRY("1.3.6.1.5.5.7.8.5", "XmppAddr", NULL, ASN1_ETYPE_UTF8_STRING), + + /* Russian things: https://cdnimg.rg.ru/pril/66/91/91/23041_pril.pdf */ + /* Main state registration number */ + ENTRY("1.2.643.100.1", "OGRN", NULL, ASN1_ETYPE_NUMERIC_STRING), + /* Individual insurance account number */ + ENTRY("1.2.643.100.3", "SNILS", NULL, ASN1_ETYPE_NUMERIC_STRING), + /* Main state registration number for individual enterpreneurs */ + ENTRY("1.2.643.100.5", "OGRNIP", NULL, ASN1_ETYPE_NUMERIC_STRING), + /* VAT identification number */ + ENTRY("1.2.643.3.131.1.1", "INN", NULL, ASN1_ETYPE_NUMERIC_STRING), + + {NULL, 0, NULL, 0, NULL, 0} +}; + +const struct oid_to_string *_gnutls_oid_get_entry(const struct oid_to_string *ots, const char *oid) +{ + unsigned int i = 0; + unsigned len = strlen(oid); + + do { + if (len == ots[i].oid_size && + strcmp(ots[i].oid, oid) == 0) + return &ots[i]; + i++; + } + while (ots[i].oid != NULL); + + return NULL; +} + +const char *_gnutls_oid_get_asn_desc(const char *oid) +{ + const struct oid_to_string *entry = _gnutls_oid_get_entry(_oid2str, oid); + return entry ? entry->asn_desc : NULL; +} + +const char *_gnutls_ldap_string_to_oid(const char *str, unsigned str_len) +{ + unsigned int i = 0; + + do { + if ((_oid2str[i].name_desc != NULL) && + (str_len == _oid2str[i].name_desc_size) && + (c_strncasecmp(_oid2str[i].name_desc, str, str_len) == + 0)) + return _oid2str[i].oid; + i++; + } + while (_oid2str[i].oid != NULL); + + return NULL; +} + +/* Escapes a string following the rules from RFC4514. + */ +static int str_escape(const gnutls_datum_t * str, gnutls_datum_t * escaped) +{ + unsigned int j, i; + uint8_t *buffer = NULL; + int ret; + + if (str == NULL) + return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + + /* the string will be at most twice the original */ + buffer = gnutls_malloc(str->size * 2 + 2); + if (buffer == NULL) + return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + + for (i = j = 0; i < str->size; i++) { + if (str->data[i] == 0) { + /* this is handled earlier */ + ret = gnutls_assert_val(GNUTLS_E_ASN1_DER_ERROR); + goto cleanup; + } + + if (str->data[i] == ',' || str->data[i] == '+' + || str->data[i] == '"' || str->data[i] == '\\' + || str->data[i] == '<' || str->data[i] == '>' + || str->data[i] == ';' || str->data[i] == 0) + buffer[j++] = '\\'; + else if (i == 0 && str->data[i] == '#') + buffer[j++] = '\\'; + else if (i == 0 && str->data[i] == ' ') + buffer[j++] = '\\'; + else if (i == (str->size - 1) && str->data[i] == ' ') + buffer[j++] = '\\'; + + buffer[j++] = str->data[i]; + } + + /* null terminate the string */ + buffer[j] = 0; + escaped->data = buffer; + escaped->size = j; + + return 0; + cleanup: + gnutls_free(buffer); + return ret; +} + +/** + * gnutls_x509_dn_oid_known: + * @oid: holds an Object Identifier in a null terminated string + * + * This function will inform about known DN OIDs. This is useful since + * functions like gnutls_x509_crt_set_dn_by_oid() use the information + * on known OIDs to properly encode their input. Object Identifiers + * that are not known are not encoded by these functions, and their + * input is stored directly into the ASN.1 structure. In that case of + * unknown OIDs, you have the responsibility of DER encoding your + * data. + * + * Returns: 1 on known OIDs and 0 otherwise. + **/ +int gnutls_x509_dn_oid_known(const char *oid) +{ + return _gnutls_oid_get_entry(_oid2str, oid) != NULL; +} + +/** + * gnutls_x509_dn_oid_name: + * @oid: holds an Object Identifier in a null terminated string + * @flags: 0 or GNUTLS_X509_DN_OID_* + * + * This function will return the name of a known DN OID. If + * %GNUTLS_X509_DN_OID_RETURN_OID is specified this function + * will return the given OID if no descriptive name has been + * found. + * + * Returns: A null terminated string or NULL otherwise. + * + * Since: 3.0 + **/ +const char *gnutls_x509_dn_oid_name(const char *oid, unsigned int flags) +{ + const struct oid_to_string *entry =_gnutls_oid_get_entry(_oid2str, oid); + + if (entry && entry->name_desc) + return entry->name_desc; + if (flags & GNUTLS_X509_DN_OID_RETURN_OID) + return oid; + else + return NULL; +} + +static int +make_printable_string(unsigned etype, const gnutls_datum_t * input, + gnutls_datum_t * out) +{ + int printable = 0; + int ret; + + /* empty input strings result to a null string */ + if (input->data == NULL || input->size == 0) { + out->data = gnutls_calloc(1, 1); + if (out->data == NULL) + return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + out->size = 0; + return 0; + } + + if (etype == ASN1_ETYPE_BMP_STRING) { + ret = _gnutls_ucs2_to_utf8(input->data, input->size, out, 1); + if (ret < 0) { + /* could not convert. Handle it as non-printable */ + printable = 0; + } else + printable = 1; + } else if (etype == ASN1_ETYPE_TELETEX_STRING) { + /* HACK: if the teletex string contains only ascii + * characters then treat it as printable. + */ + if (_gnutls_str_is_print((char*)input->data, input->size)) { + out->data = gnutls_malloc(input->size + 1); + if (out->data == NULL) + return + gnutls_assert_val + (GNUTLS_E_MEMORY_ERROR); + + memcpy(out->data, input->data, input->size); + out->size = input->size; + + out->data[out->size] = 0; + + printable = 1; + } + } else if (etype != ASN1_ETYPE_UNIVERSAL_STRING) /* supported but not printable */ + return GNUTLS_E_INVALID_REQUEST; + + if (printable == 0) { /* need to allocate out */ + ret = data2hex(input->data, input->size, out); + if (ret < 0) { + gnutls_assert(); + return ret; + } + } + + return 0; +} + +static int +decode_complex_string(const struct oid_to_string *oentry, void *value, + int value_size, gnutls_datum_t * out) +{ + char str[MAX_STRING_LEN], tmpname[128]; + int len = -1, result; + asn1_node tmpasn = NULL; + char asn1_err[ASN1_MAX_ERROR_DESCRIPTION_SIZE] = ""; + unsigned int etype; + gnutls_datum_t td = {NULL, 0}; + + if (oentry->asn_desc == NULL) { + gnutls_assert(); + return GNUTLS_E_INTERNAL_ERROR; + } + + if ((result = + asn1_create_element(_gnutls_get_pkix(), oentry->asn_desc, + &tmpasn)) != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + if ((result = + _asn1_strict_der_decode(&tmpasn, value, value_size, + asn1_err)) != ASN1_SUCCESS) { + gnutls_assert(); + _gnutls_debug_log("_asn1_strict_der_decode: %s\n", asn1_err); + asn1_delete_structure(&tmpasn); + return _gnutls_asn2err(result); + } + + /* Read the type of choice. + */ + len = sizeof(str) - 1; + if ((result = asn1_read_value(tmpasn, "", str, &len)) != ASN1_SUCCESS) { /* CHOICE */ + gnutls_assert(); + asn1_delete_structure(&tmpasn); + return _gnutls_asn2err(result); + } + + str[len] = 0; + + /* We set the etype on the strings that may need + * some conversion to UTF-8. The INVALID flag indicates + * no conversion needed */ + if (strcmp(str, "teletexString") == 0) + etype = ASN1_ETYPE_TELETEX_STRING; + else if (strcmp(str, "bmpString") == 0) + etype = ASN1_ETYPE_BMP_STRING; + else if (strcmp(str, "universalString") == 0) + etype = ASN1_ETYPE_UNIVERSAL_STRING; + else + etype = ASN1_ETYPE_INVALID; + + _gnutls_str_cpy(tmpname, sizeof(tmpname), str); + + result = _gnutls_x509_read_value(tmpasn, tmpname, &td); + asn1_delete_structure(&tmpasn); + if (result < 0) + return gnutls_assert_val(result); + + if (etype != ASN1_ETYPE_INVALID) { + result = make_printable_string(etype, &td, out); + + _gnutls_free_datum(&td); + + if (result < 0) + return gnutls_assert_val(result); + } else { + out->data = td.data; + out->size = td.size; + /* _gnutls_x509_read_value always null terminates */ + } + + assert(out->data != NULL); + + /* Refuse to deal with strings containing NULs. */ + if (strlen((void *) out->data) != (size_t) out->size) { + _gnutls_free_datum(out); + return gnutls_assert_val(GNUTLS_E_ASN1_EMBEDDED_NULL_IN_STRING); + } + + return 0; +} + + +/* This function will convert an attribute value, specified by the OID, + * to a string. The result will be a null terminated string. + * + * res may be null. This will just return the res_size, needed to + * hold the string. + */ +int +_gnutls_x509_dn_to_string(const char *oid, void *value, + int value_size, gnutls_datum_t * str) +{ + const struct oid_to_string *oentry; + int ret; + gnutls_datum_t tmp = {NULL, 0}; + + if (value == NULL || value_size <= 0) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + oentry = _gnutls_oid_get_entry(_oid2str, oid); + if (oentry == NULL) { /* unknown OID -> hex */ + unknown_oid: + ret = data2hex(value, value_size, str); + if (ret < 0) { + gnutls_assert(); + return ret; + } + return 0; + } + + if (oentry->asn_desc != NULL) { /* complex */ + ret = + decode_complex_string(oentry, value, value_size, &tmp); + if (ret < 0) { + /* we failed decoding -> handle it as unknown OID */ + goto unknown_oid; + } + } else { + ret = + _gnutls_x509_decode_string(oentry->etype, value, + value_size, &tmp, 0); + if (ret < 0) { + /* we failed decoding -> handle it as unknown OID */ + goto unknown_oid; + } + } + + ret = str_escape(&tmp, str); + _gnutls_free_datum(&tmp); + + if (ret < 0) + return gnutls_assert_val(ret); + + return 0; +} + +/* Converts a data string to an LDAP rfc2253 hex string + * something like '#01020304' + */ +static int +data2hex(const void *data, size_t data_size, + gnutls_datum_t *out) +{ + gnutls_datum_t tmp, td; + int ret; + size_t size; + + td.size = hex_str_size(data_size) + 1; /* +1 for '#' */ + td.data = gnutls_malloc(td.size); + if (td.data == NULL) + return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + + tmp.data = (void*)data; + tmp.size = data_size; + + td.data[0] = '#'; + size = td.size-1; /* don't include '#' */ + ret = + gnutls_hex_encode(&tmp, + (char*)&td.data[1], &size); + if (ret < 0) { + gnutls_assert(); + gnutls_free(td.data); + return GNUTLS_E_SHORT_MEMORY_BUFFER; + } + + td.size--; /* don't include null */ + + out->data = td.data; + out->size = td.size; + + return 0; +} + +gnutls_x509_subject_alt_name_t _gnutls_x509_san_find_type(char *str_type) +{ + if (strcmp(str_type, "dNSName") == 0) + return GNUTLS_SAN_DNSNAME; + if (strcmp(str_type, "rfc822Name") == 0) + return GNUTLS_SAN_RFC822NAME; + if (strcmp(str_type, "uniformResourceIdentifier") == 0) + return GNUTLS_SAN_URI; + if (strcmp(str_type, "iPAddress") == 0) + return GNUTLS_SAN_IPADDRESS; + if (strcmp(str_type, "otherName") == 0) + return GNUTLS_SAN_OTHERNAME; + if (strcmp(str_type, "directoryName") == 0) + return GNUTLS_SAN_DN; + if (strcmp(str_type, "registeredID") == 0) + return GNUTLS_SAN_REGISTERED_ID; + + return (gnutls_x509_subject_alt_name_t) - 1; +} + +/* A generic export function. Will export the given ASN.1 encoded data + * to PEM or DER raw data. + */ +int +_gnutls_x509_export_int_named(asn1_node asn1_data, const char *name, + gnutls_x509_crt_fmt_t format, + const char *pem_header, + unsigned char *output_data, + size_t * output_data_size) +{ + int ret; + gnutls_datum_t out = {NULL,0}; + size_t size; + + ret = _gnutls_x509_export_int_named2(asn1_data, name, + format, pem_header, &out); + if (ret < 0) + return gnutls_assert_val(ret); + + if (format == GNUTLS_X509_FMT_PEM) + size = out.size + 1; + else + size = out.size; + + if (*output_data_size < size) { + *output_data_size = size; + ret = gnutls_assert_val(GNUTLS_E_SHORT_MEMORY_BUFFER); + goto cleanup; + } + + *output_data_size = (size_t) out.size; + if (output_data) { + if (out.size > 0) { + memcpy(output_data, out.data, (size_t) out.size); + } + if (format == GNUTLS_X509_FMT_PEM) + output_data[out.size] = 0; + } + + ret = 0; + + cleanup: + gnutls_free(out.data); + + return ret; +} + +/* A generic export function. Will export the given ASN.1 encoded data + * to PEM or DER raw data. + */ +int +_gnutls_x509_export_int_named2(asn1_node asn1_data, const char *name, + gnutls_x509_crt_fmt_t format, + const char *pem_header, + gnutls_datum_t * out) +{ + int ret; + + if (format == GNUTLS_X509_FMT_DER) { + ret = _gnutls_x509_der_encode(asn1_data, name, out, 0); + if (ret < 0) + return gnutls_assert_val(ret); + } else { /* PEM */ + gnutls_datum_t tmp; + + ret = _gnutls_x509_der_encode(asn1_data, name, &tmp, 0); + if (ret < 0) + return gnutls_assert_val(ret); + + ret = + _gnutls_fbase64_encode(pem_header, tmp.data, tmp.size, + out); + _gnutls_free_datum(&tmp); + + if (ret < 0) + return gnutls_assert_val(ret); + } + + return 0; +} + +/* Decodes an octet string. The etype specifies the string type. + * The returned string is always null terminated (but null is not + * included in size). + */ +int +_gnutls_x509_decode_string(unsigned int etype, + const uint8_t * der, size_t der_size, + gnutls_datum_t * output, unsigned allow_ber) +{ + int ret; + uint8_t *str; + unsigned int str_size, len; + gnutls_datum_t td; + + output->data = NULL; + output->size = 0; + + if (allow_ber) + ret = + asn1_decode_simple_ber(etype, der, der_size, &str, &str_size, NULL); + else + ret = + asn1_decode_simple_der(etype, der, der_size, (const uint8_t**)&str, &str_size); + if (ret != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(ret); + return ret; + } + + td.size = str_size; + td.data = gnutls_malloc(str_size + 1); + if (td.data == NULL) + return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + + if (str_size > 0) + memcpy(td.data, str, str_size); + td.data[str_size] = 0; + + if (allow_ber) + free(str); + + ret = make_printable_string(etype, &td, output); + if (ret == GNUTLS_E_INVALID_REQUEST) { /* unsupported etype */ + output->data = td.data; + output->size = td.size; + ret = 0; + } else if (ret <= 0) { + _gnutls_free_datum(&td); + } + + /* Refuse to deal with strings containing NULs. */ + if (etype != ASN1_ETYPE_OCTET_STRING) { + if (output->data) + len = strlen((void *) output->data); + else + len = 0; + + if (len != (size_t) output->size) { + _gnutls_free_datum(output); + ret = gnutls_assert_val(GNUTLS_E_ASN1_EMBEDDED_NULL_IN_STRING); + } + } + + return ret; +} + + +/* Reads a value from an ASN1 tree, and puts the output + * in an allocated variable in the given datum. + * + * Note that this function always allocates one plus + * the required data size (and places a null byte). + */ +static int +x509_read_value(asn1_node c, const char *root, + gnutls_datum_t * ret, unsigned allow_null) +{ + int len = 0, result; + uint8_t *tmp = NULL; + unsigned int etype; + + result = asn1_read_value_type(c, root, NULL, &len, &etype); + if (result == 0 && allow_null == 0 && len == 0) { + /* don't allow null strings */ + return gnutls_assert_val(GNUTLS_E_ASN1_DER_ERROR); + } else if (result == 0 && allow_null == 0 && etype == ASN1_ETYPE_OBJECT_ID && len == 1) { + return gnutls_assert_val(GNUTLS_E_ASN1_DER_ERROR); + } + + if (result != ASN1_MEM_ERROR) { + if (result != ASN1_SUCCESS || allow_null == 0 || len != 0) { + result = _gnutls_asn2err(result); + return result; + } + } + + if (etype == ASN1_ETYPE_BIT_STRING) { + len = (len + 7) / 8; + } + + tmp = gnutls_malloc((size_t) len + 1); + if (tmp == NULL) { + gnutls_assert(); + result = GNUTLS_E_MEMORY_ERROR; + goto cleanup; + } + + if (len > 0) { + result = asn1_read_value(c, root, tmp, &len); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + switch (etype) { + case ASN1_ETYPE_BIT_STRING: + ret->size = (len + 7) / 8; + break; + case ASN1_ETYPE_OBJECT_ID: + if (len > 0) { + ret->size = len - 1; + } else { + result = gnutls_assert_val(GNUTLS_E_ASN1_DER_ERROR); + goto cleanup; + } + break; + default: + ret->size = (unsigned) len; + break; + } + } else { + ret->size = 0; + } + + tmp[ret->size] = 0; + ret->data = tmp; + + return 0; + + cleanup: + gnutls_free(tmp); + return result; +} + +int +_gnutls_x509_read_value(asn1_node c, const char *root, + gnutls_datum_t * ret) +{ + return x509_read_value(c, root, ret, 0); +} + +int +_gnutls_x509_read_null_value(asn1_node c, const char *root, + gnutls_datum_t * ret) +{ + return x509_read_value(c, root, ret, 1); +} + +/* Reads a value from an ASN1 tree, then interprets it as the provided + * type of string and returns the output in an allocated variable. + * + * Note that this function always places a null character + * at the end of a readable string value (which is not accounted into size) + */ +int +_gnutls_x509_read_string(asn1_node c, const char *root, + gnutls_datum_t * ret, unsigned int etype, unsigned int allow_ber) +{ + int len = 0, result; + size_t slen; + uint8_t *tmp = NULL; + unsigned rtype; + + result = asn1_read_value_type(c, root, NULL, &len, &rtype); + if (result != ASN1_MEM_ERROR) { + gnutls_assert(); + result = _gnutls_asn2err(result); + return result; + } + + if (rtype == ASN1_ETYPE_BIT_STRING) + len /= 8; + + tmp = gnutls_malloc((size_t) len + 1); + if (tmp == NULL) { + gnutls_assert(); + result = GNUTLS_E_MEMORY_ERROR; + goto cleanup; + } + + result = asn1_read_value(c, root, tmp, &len); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + if (rtype == ASN1_ETYPE_BIT_STRING) + len /= 8; + + /* Extract the STRING. + */ + slen = (size_t) len; + + result = _gnutls_x509_decode_string(etype, tmp, slen, ret, allow_ber); + if (result < 0) { + gnutls_assert(); + goto cleanup; + } + gnutls_free(tmp); + + return 0; + + cleanup: + gnutls_free(tmp); + return result; +} + +/* The string type should be IA5String, UTF8String etc. Leave + * null for octet string */ +int _gnutls_x509_encode_string(unsigned int etype, + const void *input_data, size_t input_size, + gnutls_datum_t * output) +{ + uint8_t tl[ASN1_MAX_TL_SIZE]; + unsigned int tl_size; + int ret; + + tl_size = sizeof(tl); + ret = + asn1_encode_simple_der(etype, input_data, input_size, tl, + &tl_size); + if (ret != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(ret); + return ret; + } + + output->data = gnutls_malloc(tl_size + input_size); + if (output->data == NULL) + return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + + memcpy(output->data, tl, tl_size); + memcpy(output->data + tl_size, input_data, input_size); + + output->size = tl_size + input_size; + + return 0; +} + +/* DER Encodes the src asn1_node and stores it to + * the given datum. If str is non zero then the data are encoded as + * an OCTET STRING. + */ +int +_gnutls_x509_der_encode(asn1_node src, const char *src_name, + gnutls_datum_t * res, int str) +{ + int size, result; + int asize; + uint8_t *data = NULL; + asn1_node c2 = NULL; + + size = 0; + result = asn1_der_coding(src, src_name, NULL, &size, NULL); + /* this check explicitly covers the case where size == 0 && result == 0 */ + if (result != ASN1_MEM_ERROR) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + /* allocate data for the der + */ + + if (str) + size += 16; /* for later to include the octet tags */ + asize = size; + + data = gnutls_malloc((size_t) size); + if (data == NULL) { + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; + } + + result = asn1_der_coding(src, src_name, data, &size, NULL); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + if (str) { + if ((result = asn1_create_element + (_gnutls_get_pkix(), "PKIX1.pkcs-7-Data", + &c2)) != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + result = asn1_write_value(c2, "", data, size); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + result = asn1_der_coding(c2, "", data, &asize, NULL); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + size = asize; + + asn1_delete_structure(&c2); + } + + res->data = data; + res->size = (unsigned) size; + return 0; + + cleanup: + gnutls_free(data); + asn1_delete_structure(&c2); + return result; + +} + +/* DER Encodes the src asn1_node and stores it to + * dest in dest_name. Useful to encode something and store it + * as OCTET. If str is non null then the data are encoded as + * an OCTET STRING. + */ +int +_gnutls_x509_der_encode_and_copy(asn1_node src, const char *src_name, + asn1_node dest, const char *dest_name, + int str) +{ + int result; + gnutls_datum_t encoded = {NULL, 0}; + + result = _gnutls_x509_der_encode(src, src_name, &encoded, str); + + if (result < 0) { + gnutls_assert(); + return result; + } + + /* Write the data. + */ + result = + asn1_write_value(dest, dest_name, encoded.data, + (int) encoded.size); + + _gnutls_free_datum(&encoded); + + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + return 0; +} + +/* Writes the value of the datum in the given asn1_node. + */ +int +_gnutls_x509_write_value(asn1_node c, const char *root, + const gnutls_datum_t * data) +{ + int ret; + + /* Write the data. + */ + ret = asn1_write_value(c, root, data->data, data->size); + if (ret != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(ret); + } + + return 0; +} + +/* Writes the value of the datum in the given asn1_node as a string. + */ +int +_gnutls_x509_write_string(asn1_node c, const char *root, + const gnutls_datum_t * data, unsigned int etype) +{ + int ret; + gnutls_datum_t val = { NULL, 0 }; + + ret = + _gnutls_x509_encode_string(etype, data->data, data->size, + &val); + if (ret < 0) + return gnutls_assert_val(ret); + + /* Write the data. + */ + ret = asn1_write_value(c, root, val.data, val.size); + if (ret != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(ret); + goto cleanup; + } + + ret = 0; + + cleanup: + _gnutls_free_datum(&val); + return ret; +} + +void +_asnstr_append_name(char *name, size_t name_size, const char *part1, + const char *part2) +{ + if (part1[0] != 0) { + _gnutls_str_cpy(name, name_size, part1); + _gnutls_str_cat(name, name_size, part2); + } else + _gnutls_str_cpy(name, name_size, + part2 + 1 /* remove initial dot */ ); +} + + + +/* Encodes and copies the private key parameters into a + * subjectPublicKeyInfo structure. + * + */ +int +_gnutls_x509_encode_and_copy_PKI_params(asn1_node dst, + const char *dst_name, + const gnutls_pk_params_st * params) +{ + const char *oid; + gnutls_datum_t der = { NULL, 0 }; + int result; + char name[128]; + + oid = gnutls_pk_get_oid(params->algo); + if (oid == NULL) { + gnutls_assert(); + return GNUTLS_E_UNKNOWN_PK_ALGORITHM; + } + + /* write the OID + */ + _asnstr_append_name(name, sizeof(name), dst_name, + ".algorithm.algorithm"); + + result = asn1_write_value(dst, name, oid, 1); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + result = + _gnutls_x509_write_pubkey_params(params, &der); + if (result < 0) { + gnutls_assert(); + return result; + } + + _asnstr_append_name(name, sizeof(name), dst_name, + ".algorithm.parameters"); + + result = asn1_write_value(dst, name, der.data, der.size); + _gnutls_free_datum(&der); + + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + result = _gnutls_x509_write_pubkey(params, &der); + if (result < 0) { + gnutls_assert(); + return result; + } + + /* Write the DER parameters. (in bits) + */ + _asnstr_append_name(name, sizeof(name), dst_name, + ".subjectPublicKey"); + result = asn1_write_value(dst, name, der.data, der.size * 8); + _gnutls_free_datum(&der); + + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + return 0; +} + +/* Encodes and public key parameters into a + * subjectPublicKeyInfo structure and stores it in der. + */ +int +_gnutls_x509_encode_PKI_params(gnutls_datum_t * der, + const gnutls_pk_params_st * params) +{ + int ret; + asn1_node tmp; + + ret = asn1_create_element(_gnutls_get_pkix(), + "PKIX1.Certificate", &tmp); + if (ret != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(ret); + } + + ret = _gnutls_x509_encode_and_copy_PKI_params(tmp, + "tbsCertificate.subjectPublicKeyInfo", + params); + if (ret != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(ret); + goto cleanup; + } + + ret = + _gnutls_x509_der_encode(tmp, + "tbsCertificate.subjectPublicKeyInfo", + der, 0); + + cleanup: + asn1_delete_structure(&tmp); + + return ret; +} + +/* Reads and returns the PK algorithm of the given certificate-like + * ASN.1 structure. src_name should be something like "tbsCertificate.subjectPublicKeyInfo". + */ +int +_gnutls_x509_get_pk_algorithm(asn1_node src, const char *src_name, + gnutls_ecc_curve_t *curve, + unsigned int *bits) +{ + int result; + int algo; + char oid[64]; + int len; + gnutls_ecc_curve_t lcurve = GNUTLS_ECC_CURVE_INVALID; + char name[128]; + + _asnstr_append_name(name, sizeof(name), src_name, + ".algorithm.algorithm"); + len = sizeof(oid); + result = asn1_read_value(src, name, oid, &len); + + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + algo = _gnutls_oid_to_pk_and_curve(oid, &lcurve); + if (algo == GNUTLS_PK_UNKNOWN) { + _gnutls_debug_log + ("%s: unknown public key algorithm: %s\n", __func__, + oid); + } + + if (curve) + *curve = lcurve; + + if (bits == NULL) { + return algo; + } + + /* Now read the parameters' bits + */ + if (lcurve != GNUTLS_ECC_CURVE_INVALID) { /* curve present */ + bits[0] = gnutls_ecc_curve_get_size(lcurve)*8; + } else { + gnutls_pk_params_st params; + gnutls_pk_params_init(¶ms); + + result = _gnutls_get_asn_mpis(src, src_name, ¶ms); + if (result < 0) + return gnutls_assert_val(result); + + bits[0] = pubkey_to_bits(¶ms); + gnutls_pk_params_release(¶ms); + } + + return algo; +} + +/* Reads the DER signed data from the certificate and allocates space and + * returns them into signed_data. + */ +int +_gnutls_x509_get_signed_data(asn1_node src, const gnutls_datum_t *der, + const char *src_name, + gnutls_datum_t * signed_data) +{ + int start, end, result; + + if (der == NULL || der->size == 0) { + return _gnutls_x509_der_encode(src, src_name, signed_data, 0); + } + + /* Get the signed data + */ + result = asn1_der_decoding_startEnd(src, der->data, der->size, + src_name, &start, &end); + if (result != ASN1_SUCCESS) { + result = _gnutls_asn2err(result); + gnutls_assert(); + goto cleanup; + } + + result = + _gnutls_set_datum(signed_data, &der->data[start], + end - start + 1); + + if (result < 0) { + gnutls_assert(); + goto cleanup; + } + + result = 0; + + cleanup: + return result; +} + +/*- + * gnutls_x509_get_signature_algorithm: + * @src: should contain an asn1_node structure + * @src_name: the description of the signature field + * + * This function will return a value of the #gnutls_sign_algorithm_t + * enumeration that is the signature algorithm that has been used to + * sign this certificate. + * + * Returns: a #gnutls_sign_algorithm_t value, or a negative error code on + * error. + -*/ +int +_gnutls_x509_get_signature_algorithm(asn1_node src, const char *src_name) +{ + int result; + char name[128]; + gnutls_datum_t sa = {NULL, 0}; + + _gnutls_str_cpy(name, sizeof(name), src_name); + _gnutls_str_cat(name, sizeof(name), ".algorithm"); + + /* Read the signature algorithm */ + result = _gnutls_x509_read_value(src, name, &sa); + + if (result < 0) { + gnutls_assert(); + return result; + } + + /* Read the signature parameters. Unless the algorithm is + * RSA-PSS, parameters are not read. They will be read from + * the issuer's certificate if needed. + */ + if (sa.data && strcmp ((char *) sa.data, PK_PKIX1_RSA_PSS_OID) == 0) { + gnutls_datum_t der = {NULL, 0}; + gnutls_x509_spki_st params; + + _gnutls_str_cpy(name, sizeof(name), src_name); + _gnutls_str_cat(name, sizeof(name), ".parameters"); + + result = _gnutls_x509_read_value(src, name, &der); + if (result < 0) { + _gnutls_free_datum(&sa); + return gnutls_assert_val(result); + } + + result = _gnutls_x509_read_rsa_pss_params(der.data, der.size, + ¶ms); + _gnutls_free_datum(&der); + + if (result == 0) + result = gnutls_pk_to_sign(params.pk, params.rsa_pss_dig); + } else if (sa.data) { + result = gnutls_oid_to_sign((char *) sa.data); + } else { + result = GNUTLS_E_UNKNOWN_ALGORITHM; + } + + _gnutls_free_datum(&sa); + + if (result == GNUTLS_SIGN_UNKNOWN) + result = GNUTLS_E_UNKNOWN_ALGORITHM; + + return result; +} + + +/* Reads the DER signature from the certificate and allocates space and + * returns them into signed_data. + */ +int +_gnutls_x509_get_signature(asn1_node src, const char *src_name, + gnutls_datum_t * signature) +{ + int result, len; + int bits; + + signature->data = NULL; + signature->size = 0; + + /* Read the signature + */ + len = 0; + result = asn1_read_value(src, src_name, NULL, &len); + + if (result != ASN1_MEM_ERROR) { + result = _gnutls_asn2err(result); + gnutls_assert(); + goto cleanup; + } + + bits = len; + if (bits % 8 != 0 || bits < 8) { + gnutls_assert(); + result = GNUTLS_E_CERTIFICATE_ERROR; + goto cleanup; + } + + len = bits / 8; + + signature->data = gnutls_malloc(len); + if (signature->data == NULL) { + gnutls_assert(); + result = GNUTLS_E_MEMORY_ERROR; + return result; + } + + /* read the bit string of the signature + */ + bits = len; + result = + asn1_read_value(src, src_name, signature->data, &bits); + + if (result != ASN1_SUCCESS) { + result = _gnutls_asn2err(result); + gnutls_assert(); + goto cleanup; + } + + signature->size = len; + + return 0; + + cleanup: + gnutls_free(signature->data); + return result; +} + +/* ASN.1 PrintableString rules */ +static int is_printable(char p) +{ + if ((p >= 'a' && p <= 'z') || (p >= 'A' && p <= 'Z') || + (p >= '0' && p <= '9') || p == ' ' || p == '(' || p == ')' || + p == '+' || p == ',' || p == '-' || p == '.' || p == '/' || + p == ':' || p == '=' || p == '?') + return 1; + + return 0; +} + +static int write_complex_string(asn1_node asn_struct, const char *where, + const struct oid_to_string *oentry, + const uint8_t * data, size_t data_size) +{ + char tmp[128]; + asn1_node c2; + int result; + const char *string_type; + unsigned int i; + + result = + asn1_create_element(_gnutls_get_pkix(), oentry->asn_desc, &c2); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + tmp[0] = 0; + + string_type = "printableString"; + + /* Check if the data is ASN.1 printable, and use + * the UTF8 string type if not. + */ + for (i = 0; i < data_size; i++) { + if (!is_printable(data[i])) { + string_type = "utf8String"; + break; + } + } + + /* if the type is a CHOICE then write the + * type we'll use. + */ + result = asn1_write_value(c2, "", string_type, 1); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto error; + } + + _gnutls_str_cpy(tmp, sizeof(tmp), string_type); + + result = asn1_write_value(c2, tmp, data, data_size); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto error; + } + + result = + _gnutls_x509_der_encode_and_copy(c2, "", asn_struct, where, 0); + if (result < 0) { + gnutls_assert(); + goto error; + } + + result = 0; + + error: + asn1_delete_structure(&c2); + return result; +} + + +/* This will encode and write the AttributeTypeAndValue field. + * 'multi' must be (0) if writing an AttributeTypeAndValue, and 1 if Attribute. + * In all cases only one value is written. + */ +int +_gnutls_x509_encode_and_write_attribute(const char *given_oid, + asn1_node asn1_struct, + const char *where, + const void *_data, + int data_size, int multi) +{ + const uint8_t *data = _data; + char tmp[128]; + int result; + const struct oid_to_string *oentry; + + oentry = _gnutls_oid_get_entry(_oid2str, given_oid); + if (oentry == NULL) { + gnutls_assert(); + _gnutls_debug_log("Cannot find OID: %s\n", given_oid); + return GNUTLS_E_X509_UNSUPPORTED_OID; + } + + /* write the data (value) + */ + + _gnutls_str_cpy(tmp, sizeof(tmp), where); + _gnutls_str_cat(tmp, sizeof(tmp), ".value"); + + if (multi != 0) { /* if not writing an AttributeTypeAndValue, but an Attribute */ + _gnutls_str_cat(tmp, sizeof(tmp), "s"); /* values */ + + result = asn1_write_value(asn1_struct, tmp, "NEW", 1); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto error; + } + + _gnutls_str_cat(tmp, sizeof(tmp), ".?LAST"); + } + + if (oentry->asn_desc != NULL) { /* write a complex string API */ + result = + write_complex_string(asn1_struct, tmp, oentry, data, + data_size); + if (result < 0) + return gnutls_assert_val(result); + } else { /* write a simple string */ + + gnutls_datum_t td; + + td.data = (void *) data; + td.size = data_size; + result = + _gnutls_x509_write_string(asn1_struct, tmp, &td, + oentry->etype); + if (result < 0) { + gnutls_assert(); + goto error; + } + } + + /* write the type + */ + _gnutls_str_cpy(tmp, sizeof(tmp), where); + _gnutls_str_cat(tmp, sizeof(tmp), ".type"); + + result = asn1_write_value(asn1_struct, tmp, given_oid, 1); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto error; + } + + result = 0; + + error: + return result; +} + +/* copies a datum to a buffer. If it doesn't fit it returns + * GNUTLS_E_SHORT_MEMORY_BUFFER. It always deinitializes the datum + * after the copy. + * + * The buffer will always be null terminated. + */ +int _gnutls_strdatum_to_buf(gnutls_datum_t * d, void *buf, + size_t * buf_size) +{ + int ret; + uint8_t *_buf = buf; + + if (buf == NULL || *buf_size < d->size + 1) { + *buf_size = d->size + 1; + ret = gnutls_assert_val(GNUTLS_E_SHORT_MEMORY_BUFFER); + goto cleanup; + } + memcpy(buf, d->data, d->size); + _buf[d->size] = 0; + + *buf_size = d->size; + ret = 0; + + cleanup: + _gnutls_free_datum(d); + + return ret; +} + +int +_gnutls_x509_get_raw_field2(asn1_node c2, const gnutls_datum_t * raw, + const char *whom, gnutls_datum_t * dn) +{ + int result, len1; + int start1, end1; + result = + asn1_der_decoding_startEnd(c2, raw->data, raw->size, + whom, &start1, &end1); + + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + len1 = end1 - start1 + 1; + + dn->data = &raw->data[start1]; + dn->size = len1; + result = 0; + + cleanup: + return result; +} + +int _gnutls_copy_string(const gnutls_datum_t* str, uint8_t *out, size_t *out_size) +{ +unsigned size_to_check; + + size_to_check = str->size + 1; + + if ((unsigned) size_to_check > *out_size) { + gnutls_assert(); + (*out_size) = size_to_check; + return GNUTLS_E_SHORT_MEMORY_BUFFER; + } + + if (out != NULL && str->data != NULL) { + memcpy(out, str->data, str->size); + out[str->size] = 0; + } else if (out != NULL) { + out[0] = 0; + } + *out_size = str->size; + + return 0; +} + +int _gnutls_copy_data(const gnutls_datum_t* str, uint8_t *out, size_t *out_size) +{ + if ((unsigned) str->size > *out_size) { + gnutls_assert(); + (*out_size) = str->size; + return GNUTLS_E_SHORT_MEMORY_BUFFER; + } + + if (out != NULL && str->data != NULL) { + memcpy(out, str->data, str->size); + } + *out_size = str->size; + + return 0; +} + +/* Converts an X.509 certificate to subjectPublicKeyInfo */ +int x509_crt_to_raw_pubkey(gnutls_x509_crt_t crt, + gnutls_datum_t * rpubkey) +{ + gnutls_pubkey_t pubkey = NULL; + int ret; + + ret = gnutls_pubkey_init(&pubkey); + if (ret < 0) + return gnutls_assert_val(ret); + + ret = gnutls_pubkey_import_x509(pubkey, crt, 0); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = + gnutls_pubkey_export2(pubkey, GNUTLS_X509_FMT_DER, rpubkey); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = 0; + + cleanup: + gnutls_pubkey_deinit(pubkey); + return ret; +} + +/* Converts an X.509 certificate to subjectPublicKeyInfo */ +int _gnutls_x509_raw_crt_to_raw_pubkey(const gnutls_datum_t * cert, + gnutls_datum_t * rpubkey) +{ + gnutls_x509_crt_t crt = NULL; + int ret; + + ret = gnutls_x509_crt_init(&crt); + if (ret < 0) + return gnutls_assert_val(ret); + + ret = gnutls_x509_crt_import(crt, cert, GNUTLS_X509_FMT_DER); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = x509_crt_to_raw_pubkey(crt, rpubkey); + cleanup: + gnutls_x509_crt_deinit(crt); + + return ret; +} + +unsigned +_gnutls_check_valid_key_id(const gnutls_datum_t *key_id, + gnutls_x509_crt_t cert, time_t now, + unsigned *has_ski) +{ + uint8_t id[MAX_KEY_ID_SIZE]; + size_t id_size; + unsigned result = 0; + + if (has_ski) + *has_ski = 0; + + if (now > gnutls_x509_crt_get_expiration_time(cert) || + now < gnutls_x509_crt_get_activation_time(cert)) { + /* don't bother, certificate is not yet activated or expired */ + gnutls_assert(); + goto out; + } + + id_size = sizeof(id); + if (gnutls_x509_crt_get_subject_key_id(cert, id, &id_size, NULL) < 0) { + gnutls_assert(); + goto out; + } + + if (has_ski) + *has_ski = 1; + + if (id_size == key_id->size && !memcmp(id, key_id->data, id_size)) + result = 1; + + out: + return result; +} + +/* Sort a certificate list in place with subject, issuer order. @clist_size must + * be equal to or less than %DEFAULT_MAX_VERIFY_DEPTH. + * + * Returns the index in clist where the initial contiguous segment ends. If the + * chain contains multiple segments separated by gaps (e.g., missing issuers), + * the caller shall call this function multiple times. + * + * For example, suppose we want the following certificate chain as a result of + * sorting: + * + * A -> (B) -> C -> D -> E -> (F) -> G -> H -> I + * + * from the input [A, C, E, D, G, I, H] (B and F are missing). + * + * On the first call of this function: + * + * _gnutls_sort_clist(clist, clist_size) + * + * it returns 1, meaning that the first segment only contains A. The content of + * @clist will remain the same [A, C, E, D, G, I, H]. + * + * Then the caller will call this function again, starting from the second + * element: + * + * _gnutls_sort_clist(&clist[1], clist_size - 1) + * + * This time it returns 3, meaning that the first segment contains [C, D, E]. + * The content of @clist will be [A, C, D, E, G, I, H]. + * + * Finally, after calling the function on the remaining elements: + * + * _gnutls_sort_clist(&clist[1 + 3], clist_size - (1 + 3)) + * + * It will return 3, meaning that the first segment contains [G, H, I]. At this + * point, sorting of @clist is complete. + */ +unsigned int _gnutls_sort_clist(gnutls_x509_crt_t *clist, + unsigned int clist_size) +{ + int prev; + unsigned int i, j, k; + int issuer[DEFAULT_MAX_VERIFY_DEPTH]; /* contain the index of the issuers */ + bool insorted[DEFAULT_MAX_VERIFY_DEPTH]; /* non zero if clist[i] used in sorted list */ + gnutls_x509_crt_t sorted[DEFAULT_MAX_VERIFY_DEPTH]; + + assert(clist_size <= DEFAULT_MAX_VERIFY_DEPTH); + + for (i = 0; i < DEFAULT_MAX_VERIFY_DEPTH; i++) { + issuer[i] = -1; + insorted[i] = 0; + } + + /* Find the issuer of each certificate and store it + * in issuer array. O(n^2) so consider that before + * increasing DEFAULT_MAX_VERIFY_DEPTH. + */ + for (i = 0; i < clist_size; i++) { + /* Self-signed certificate found in the chain; skip it + * as it should only appear in the trusted set. + */ + if (gnutls_x509_crt_check_issuer(clist[i], clist[i])) { + _gnutls_cert_log("self-signed cert found", clist[i]); + continue; + } + + for (j = 1; j < clist_size; j++) { + if (i == j) + continue; + + if (gnutls_x509_crt_check_issuer(clist[i], clist[j])) { + issuer[i] = j; + break; + } + } + } + + sorted[0] = clist[0]; + insorted[0] = 1; + + prev = 0; + for (i = 1; i < clist_size; i++) { + prev = issuer[prev]; + if (prev < 0) { /* no issuer */ + break; + } + + sorted[i] = clist[prev]; + insorted[prev] = 1; + } + + /* append the remaining certs */ + for (j = 1, k = i; j < clist_size; j++) { + if (!insorted[j]) { + sorted[k++] = clist[j]; + } + } + + /* write out the sorted list */ + assert(k == clist_size); + for (j = 0; j < clist_size; j++) { + clist[j] = sorted[j]; + } + + return i; +} + +int _gnutls_check_if_sorted(gnutls_x509_crt_t * crt, int nr) +{ + int i, ret; + + /* check if the X.509 list is ordered */ + if (nr > 1) { + for (i = 0; i < nr; i++) { + if (i > 0) { + if (!_gnutls_x509_compare_raw_dn(&crt[i]->raw_dn, + &crt[i-1]->raw_issuer_dn)) { + ret = + gnutls_assert_val + (GNUTLS_E_CERTIFICATE_LIST_UNSORTED); + goto cleanup; + } + } + } + } + ret = 0; + +cleanup: + return ret; +} + +/** + * gnutls_gost_paramset_get_name: + * @param: is a GOST 28147 param set + * + * Convert a #gnutls_gost_paramset_t value to a string. + * + * Returns: a string that contains the name of the specified GOST param set, + * or %NULL. + * + * Since: 3.6.3 + **/ +const char *gnutls_gost_paramset_get_name(gnutls_gost_paramset_t param) +{ + switch(param) { + case GNUTLS_GOST_PARAMSET_TC26_Z: + return "TC26-Z"; + case GNUTLS_GOST_PARAMSET_CP_A: + return "CryptoPro-A"; + case GNUTLS_GOST_PARAMSET_CP_B: + return "CryptoPro-B"; + case GNUTLS_GOST_PARAMSET_CP_C: + return "CryptoPro-C"; + case GNUTLS_GOST_PARAMSET_CP_D: + return "CryptoPro-D"; + default: + gnutls_assert(); + return "Unknown"; + } +} + +/** + * gnutls_gost_paramset_get_oid: + * @param: is a GOST 28147 param set + * + * Convert a #gnutls_gost_paramset_t value to its object identifier. + * + * Returns: a string that contains the object identifier of the specified GOST + * param set, or %NULL. + * + * Since: 3.6.3 + **/ +const char *gnutls_gost_paramset_get_oid(gnutls_gost_paramset_t param) +{ + switch(param) { + case GNUTLS_GOST_PARAMSET_TC26_Z: + return GOST28147_89_TC26Z_OID; + case GNUTLS_GOST_PARAMSET_CP_A: + return GOST28147_89_CPA_OID; + case GNUTLS_GOST_PARAMSET_CP_B: + return GOST28147_89_CPB_OID; + case GNUTLS_GOST_PARAMSET_CP_C: + return GOST28147_89_CPC_OID; + case GNUTLS_GOST_PARAMSET_CP_D: + return GOST28147_89_CPD_OID; + default: + gnutls_assert(); + return NULL; + } +} + +/** + * gnutls_oid_to_gost_paramset: + * @oid: is an object identifier + * + * Converts a textual object identifier to a #gnutls_gost_paramset_t value. + * + * Returns: a #gnutls_gost_paramset_get_oid of the specified GOST 28147 + * param st, or %GNUTLS_GOST_PARAMSET_UNKNOWN on failure. + * + * Since: 3.6.3 + **/ +gnutls_gost_paramset_t gnutls_oid_to_gost_paramset(const char *oid) +{ + if (!strcmp(oid, GOST28147_89_TC26Z_OID)) + return GNUTLS_GOST_PARAMSET_TC26_Z; + else if (!strcmp(oid, GOST28147_89_CPA_OID)) + return GNUTLS_GOST_PARAMSET_CP_A; + else if (!strcmp(oid, GOST28147_89_CPB_OID)) + return GNUTLS_GOST_PARAMSET_CP_B; + else if (!strcmp(oid, GOST28147_89_CPC_OID)) + return GNUTLS_GOST_PARAMSET_CP_C; + else if (!strcmp(oid, GOST28147_89_CPD_OID)) + return GNUTLS_GOST_PARAMSET_CP_D; + else + return gnutls_assert_val(GNUTLS_GOST_PARAMSET_UNKNOWN); +} + +int _gnutls_x509_get_version(asn1_node root, const char *name) +{ + uint8_t version[8]; + int len, result; + + len = sizeof(version); + result = asn1_read_value(root, name, version, &len); + if (result != ASN1_SUCCESS) { + if (result == ASN1_ELEMENT_NOT_FOUND) + return 1; /* the DEFAULT version */ + gnutls_assert(); + return _gnutls_asn2err(result); + } + + if (len != 1 || version[0] >= 0x80) + return gnutls_assert_val(GNUTLS_E_ASN1_DER_ERROR); + + return (int) version[0] + 1; +} diff --git a/lib/x509/common.h b/lib/x509/common.h new file mode 100644 index 0000000..457d4c4 --- /dev/null +++ b/lib/x509/common.h @@ -0,0 +1,308 @@ +/* + * Copyright (C) 2003-2012 Free Software Foundation, Inc. + * Copyright (C) 2017 Red Hat, Inc. + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GnuTLS. + * + * The GnuTLS is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/> + * + */ + +#ifndef GNUTLS_LIB_X509_COMMON_H +#define GNUTLS_LIB_X509_COMMON_H + +#include <algorithms.h> +#include <abstract_int.h> +#include <x509/x509_int.h> +#include <fips.h> + +#define MAX_STRING_LEN 512 + +#if defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION) +# define MAX_ITER_COUNT 10*1024 +#else +/* Set a maximum iteration count over which we refuse to + * decode a file. That is to prevent DoS. */ +# define MAX_ITER_COUNT (10*1024*1024) +#endif + +#define GNUTLS_XML_SHOW_ALL 1 + +#define PEM_CRL "X509 CRL" +#define PEM_X509_CERT "X509 CERTIFICATE" +#define PEM_X509_CERT2 "CERTIFICATE" +#define PEM_PKCS7 "PKCS7" +#define PEM_PKCS12 "PKCS12" +#define PEM_PK "PUBLIC KEY" + +/* public key algorithm's OIDs + */ +#define PK_PKIX1_RSA_OID "1.2.840.113549.1.1.1" +#define PK_PKIX1_RSA_PSS_OID "1.2.840.113549.1.1.10" +#define PK_X509_RSA_OID "2.5.8.1.1" +#define PK_DSA_OID "1.2.840.10040.4.1" +#define PK_GOST_R3410_94_OID "1.2.643.2.2.20" +#define PK_GOST_R3410_2001_OID "1.2.643.2.2.19" +#define PK_GOST_R3410_2012_256_OID "1.2.643.7.1.1.1.1" +#define PK_GOST_R3410_2012_512_OID "1.2.643.7.1.1.1.2" + +/* signature OIDs + */ +#define SIG_DSA_SHA1_OID "1.2.840.10040.4.3" +/* those two from draft-ietf-pkix-sha2-dsa-ecdsa-06 */ +#define SIG_DSA_SHA224_OID "2.16.840.1.101.3.4.3.1" +#define SIG_DSA_SHA256_OID "2.16.840.1.101.3.4.3.2" +#define SIG_DSA_SHA384_OID "2.16.840.1.101.3.4.3.3" +#define SIG_DSA_SHA512_OID "2.16.840.1.101.3.4.3.4" + +#define SIG_RSA_MD5_OID "1.2.840.113549.1.1.4" +#define SIG_RSA_MD2_OID "1.2.840.113549.1.1.2" +#define SIG_RSA_SHA1_OID "1.2.840.113549.1.1.5" +#define SIG_RSA_SHA224_OID "1.2.840.113549.1.1.14" +#define SIG_RSA_SHA256_OID "1.2.840.113549.1.1.11" +#define SIG_RSA_SHA384_OID "1.2.840.113549.1.1.12" +#define SIG_RSA_SHA512_OID "1.2.840.113549.1.1.13" +#define SIG_RSA_RMD160_OID "1.3.36.3.3.1.2" +#define SIG_GOST_R3410_94_OID "1.2.643.2.2.4" +#define SIG_GOST_R3410_2001_OID "1.2.643.2.2.3" +#define SIG_GOST_R3410_2012_256_OID "1.2.643.7.1.1.3.2" +#define SIG_GOST_R3410_2012_512_OID "1.2.643.7.1.1.3.3" +#define ISO_SIG_RSA_SHA1_OID "1.3.14.3.2.29" + +#define SIG_DSA_SHA3_224_OID "2.16.840.1.101.3.4.3.5" +#define SIG_DSA_SHA3_256_OID "2.16.840.1.101.3.4.3.6" +#define SIG_DSA_SHA3_384_OID "2.16.840.1.101.3.4.3.7" +#define SIG_DSA_SHA3_512_OID "2.16.840.1.101.3.4.3.8" + +#define SIG_ECDSA_SHA3_224_OID "2.16.840.1.101.3.4.3.9" +#define SIG_ECDSA_SHA3_256_OID "2.16.840.1.101.3.4.3.10" +#define SIG_ECDSA_SHA3_384_OID "2.16.840.1.101.3.4.3.11" +#define SIG_ECDSA_SHA3_512_OID "2.16.840.1.101.3.4.3.12" + +#define SIG_RSA_SHA3_224_OID "2.16.840.1.101.3.4.3.13" +#define SIG_RSA_SHA3_256_OID "2.16.840.1.101.3.4.3.14" +#define SIG_RSA_SHA3_384_OID "2.16.840.1.101.3.4.3.15" +#define SIG_RSA_SHA3_512_OID "2.16.840.1.101.3.4.3.16" + +#define ECDH_X25519_OID "1.3.101.110" +#define ECDH_X448_OID "1.3.101.111" + +#define SIG_EDDSA_SHA512_OID "1.3.101.112" +#define SIG_ED448_OID "1.3.101.113" + +#define XMPP_OID "1.3.6.1.5.5.7.8.5" +#define KRB5_PRINCIPAL_OID "1.3.6.1.5.2.2" +#define MSUSER_PRINCIPAL_NAME_OID "1.3.6.1.4.1.311.20.2.3" +#define PKIX1_RSA_PSS_MGF1_OID "1.2.840.113549.1.1.8" + +#define GOST28147_89_OID "1.2.643.2.2.21" +#define GOST28147_89_TC26Z_OID "1.2.643.7.1.2.5.1.1" +#define GOST28147_89_CPA_OID "1.2.643.2.2.31.1" +#define GOST28147_89_CPB_OID "1.2.643.2.2.31.2" +#define GOST28147_89_CPC_OID "1.2.643.2.2.31.3" +#define GOST28147_89_CPD_OID "1.2.643.2.2.31.4" + +#define ASN1_NULL "\x05\x00" +#define ASN1_NULL_SIZE 2 + +struct oid_to_string { + const char *oid; + unsigned oid_size; + const char *name_desc; + unsigned name_desc_size; + const char *asn_desc; /* description in the pkix file if complex type */ + unsigned int etype; /* the libtasn1 ASN1_ETYPE or INVALID + * if cannot be simply parsed */ +}; + +const struct oid_to_string *_gnutls_oid_get_entry(const struct oid_to_string *ots, const char *oid); + +const char *_gnutls_oid_get_asn_desc(const char *oid); + +int _gnutls_x509_set_time(asn1_node c2, const char *where, time_t tim, + int force_general); +int +_gnutls_x509_set_raw_time(asn1_node c2, const char *where, time_t tim); + +int _gnutls_x509_decode_string(unsigned int etype, + const uint8_t * der, size_t der_size, + gnutls_datum_t * output, + unsigned allow_ber); + +int _gnutls_x509_encode_string(unsigned int etype, + const void *input_data, size_t input_size, + gnutls_datum_t * output); + +int _gnutls_x509_dn_to_string(const char *OID, void *value, + int value_size, gnutls_datum_t * out); +const char *_gnutls_ldap_string_to_oid(const char *str, unsigned str_len); + +time_t _gnutls_x509_get_time(asn1_node c2, const char *when, int general); + +gnutls_x509_subject_alt_name_t _gnutls_x509_san_find_type(char *str_type); + +int _gnutls_x509_der_encode_and_copy(asn1_node src, const char *src_name, + asn1_node dest, const char *dest_name, + int str); +int _gnutls_x509_der_encode(asn1_node src, const char *src_name, + gnutls_datum_t * res, int str); + +#define _gnutls_x509_export_int(asn1, format, header, out, out_size) \ + _gnutls_x509_export_int_named(asn1, "", format, header, out, out_size) + +int _gnutls_x509_export_int_named(asn1_node asn1_data, const char *name, + gnutls_x509_crt_fmt_t format, + const char *pem_header, + unsigned char *output_data, + size_t * output_data_size); + +#define _gnutls_x509_export_int2(asn1, format, header, out) \ + _gnutls_x509_export_int_named2(asn1, "", format, header, out) +int _gnutls_x509_export_int_named2(asn1_node asn1_data, const char *name, + gnutls_x509_crt_fmt_t format, + const char *pem_header, + gnutls_datum_t * out); + +int _gnutls_x509_read_value(asn1_node c, const char *root, + gnutls_datum_t * ret); +int _gnutls_x509_read_null_value(asn1_node c, const char *root, + gnutls_datum_t * ret); +int _gnutls_x509_read_string(asn1_node c, const char *root, + gnutls_datum_t * ret, unsigned int etype, + unsigned allow_ber); +int _gnutls_x509_write_value(asn1_node c, const char *root, + const gnutls_datum_t * data); + +int _gnutls_x509_write_string(asn1_node c, const char *root, + const gnutls_datum_t * data, + unsigned int etype); + +int _gnutls_x509_encode_and_write_attribute(const char *given_oid, + asn1_node asn1_struct, + const char *where, + const void *data, + int sizeof_data, int multi); +int _gnutls_x509_decode_and_read_attribute(asn1_node asn1_struct, + const char *where, char *oid, + int oid_size, + gnutls_datum_t * value, + int multi, int octet); + +int _gnutls_x509_get_pk_algorithm(asn1_node src, const char *src_name, + gnutls_ecc_curve_t *curve, + unsigned int *bits); + +int +_gnutls_x509_get_signature_algorithm(asn1_node src, const char *src_name); + +int _gnutls_x509_encode_and_copy_PKI_params(asn1_node dst, + const char *dst_name, + const gnutls_pk_params_st * params); +int _gnutls_x509_encode_PKI_params(gnutls_datum_t * der, + const gnutls_pk_params_st * params); +int _gnutls_asn1_copy_node(asn1_node * dst, const char *dst_name, + asn1_node src, const char *src_name); + +int _gnutls_x509_get_signed_data(asn1_node src, const gnutls_datum_t *der, + const char *src_name, + gnutls_datum_t * signed_data); +int _gnutls_x509_get_signature(asn1_node src, const char *src_name, + gnutls_datum_t * signature); + + +int _gnutls_get_asn_mpis(asn1_node asn, const char *root, + gnutls_pk_params_st * params); + +int _gnutls_get_key_id(gnutls_pk_params_st *, + unsigned char *output_data, + size_t * output_data_size, unsigned flags); + +void _asnstr_append_name(char *name, size_t name_size, const char *part1, + const char *part2); + +/* Given a @c2 which it returns an allocated DER encoding of @whom in @out */ +inline static int +_gnutls_x509_get_raw_field(asn1_node c2, const char *whom, gnutls_datum_t *out) +{ + return _gnutls_x509_der_encode(c2, whom, out, 0); +} + +int +_gnutls_x509_get_raw_field2(asn1_node c2, const gnutls_datum_t * raw, + const char *whom, gnutls_datum_t * dn); + +unsigned +_gnutls_check_if_same_key(gnutls_x509_crt_t cert1, + gnutls_x509_crt_t cert2, + unsigned is_ca); + +unsigned +_gnutls_check_if_same_key2(gnutls_x509_crt_t cert1, + gnutls_datum_t *cert2bin); + +unsigned +_gnutls_check_valid_key_id(const gnutls_datum_t *key_id, + gnutls_x509_crt_t cert, time_t now, + unsigned *has_ski); + +unsigned _gnutls_check_key_purpose(gnutls_x509_crt_t cert, const char *purpose, unsigned no_any); + +time_t _gnutls_x509_generalTime2gtime(const char *ttime); + +int _gnutls_get_extension(asn1_node asn, const char *root, + const char *extension_id, int indx, + gnutls_datum_t * ret, unsigned int *_critical); + +int _gnutls_set_extension(asn1_node asn, const char *root, + const char *ext_id, + const gnutls_datum_t * ext_data, unsigned int critical); + +int _gnutls_strdatum_to_buf(gnutls_datum_t * d, void *buf, + size_t * sizeof_buf); + +unsigned _gnutls_is_same_dn(gnutls_x509_crt_t cert1, gnutls_x509_crt_t cert2); + +int _gnutls_copy_string(const gnutls_datum_t* str, uint8_t *out, size_t *out_size); +int _gnutls_copy_data(const gnutls_datum_t* str, uint8_t *out, size_t *out_size); + +int _gnutls_x509_decode_ext(const gnutls_datum_t *der, gnutls_x509_ext_st *out); +int _gnutls_x509_raw_crt_to_raw_pubkey(const gnutls_datum_t * cert, + gnutls_datum_t * rpubkey); + +int _gnutls_x509_get_version(asn1_node root, const char *name); + +int x509_crt_to_raw_pubkey(gnutls_x509_crt_t crt, + gnutls_datum_t * rpubkey); + +typedef void (*gnutls_cert_vfunc)(gnutls_x509_crt_t); + +unsigned int _gnutls_sort_clist(gnutls_x509_crt_t *clist, + unsigned int clist_size); + +int _gnutls_check_if_sorted(gnutls_x509_crt_t * crt, int nr); + +inline static int _asn1_strict_der_decode (asn1_node * element, const void *ider, + int len, char *errorDescription) +{ +#if defined(STRICT_DER_TIME) || !defined(ASN1_DECODE_FLAG_ALLOW_INCORRECT_TIME) +# define _ASN1_DER_FLAGS ASN1_DECODE_FLAG_STRICT_DER +#else +# define _ASN1_DER_FLAGS (ASN1_DECODE_FLAG_ALLOW_INCORRECT_TIME|ASN1_DECODE_FLAG_STRICT_DER) +#endif + return asn1_der_decoding2(element, ider, &len, _ASN1_DER_FLAGS, errorDescription); +} + +#endif /* GNUTLS_LIB_X509_COMMON_H */ diff --git a/lib/x509/crl.c b/lib/x509/crl.c new file mode 100644 index 0000000..56103e1 --- /dev/null +++ b/lib/x509/crl.c @@ -0,0 +1,1425 @@ +/* + * Copyright (C) 2003-2016 Free Software Foundation, Inc. + * Copyright (C) 2015-2016 Red Hat, Inc. + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GnuTLS. + * + * The GnuTLS is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/> + * + */ + +#include "gnutls_int.h" +#include <libtasn1.h> + +#include <datum.h> +#include <global.h> +#include "errors.h" +#include <common.h> +#include <x509_b64.h> +#include <x509_int.h> +#include <x509.h> + +static int crl_reinit(gnutls_x509_crl_t crl) +{ +int result; + + if (crl->crl) + asn1_delete_structure(&crl->crl); + + result = asn1_create_element(_gnutls_get_pkix(), + "PKIX1.CertificateList", + &crl->crl); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + crl->rcache = NULL; + crl->rcache_idx = 0; + crl->raw_issuer_dn.size = 0; + + return 0; +} + +/** + * gnutls_x509_crl_init: + * @crl: A pointer to the type to be initialized + * + * This function will initialize a CRL structure. CRL stands for + * Certificate Revocation List. A revocation list usually contains + * lists of certificate serial numbers that have been revoked by an + * Authority. The revocation lists are always signed with the + * authority's private key. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int gnutls_x509_crl_init(gnutls_x509_crl_t * crl) +{ + FAIL_IF_LIB_ERROR; + + *crl = gnutls_calloc(1, sizeof(gnutls_x509_crl_int)); + + if (*crl) { + int result = crl_reinit(*crl); + if (result < 0) { + gnutls_assert(); + gnutls_free(*crl); + return result; + } + return 0; /* success */ + } + return GNUTLS_E_MEMORY_ERROR; +} + +/** + * gnutls_x509_crl_deinit: + * @crl: The data to be deinitialized + * + * This function will deinitialize a CRL structure. + **/ +void gnutls_x509_crl_deinit(gnutls_x509_crl_t crl) +{ + if (!crl) + return; + + if (crl->crl) + asn1_delete_structure(&crl->crl); + gnutls_free(crl->der.data); + + gnutls_free(crl); +} + +/** + * gnutls_x509_crl_import: + * @crl: The data to store the parsed CRL. + * @data: The DER or PEM encoded CRL. + * @format: One of DER or PEM + * + * This function will convert the given DER or PEM encoded CRL + * to the native #gnutls_x509_crl_t format. The output will be stored in 'crl'. + * + * If the CRL is PEM encoded it should have a header of "X509 CRL". + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int +gnutls_x509_crl_import(gnutls_x509_crl_t crl, + const gnutls_datum_t * data, + gnutls_x509_crt_fmt_t format) +{ + int result = 0; + + if (crl == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + _gnutls_free_datum(&crl->der); + + /* If the CRL is in PEM format then decode it + */ + if (format == GNUTLS_X509_FMT_PEM) { + result = + _gnutls_fbase64_decode(PEM_CRL, data->data, data->size, + &crl->der); + + if (result < 0) { + gnutls_assert(); + return result; + } + } else { + result = _gnutls_set_datum(&crl->der, data->data, data->size); + if (result < 0) { + gnutls_assert(); + return result; + } + } + + if (crl->expanded) { + result = crl_reinit(crl); + if (result < 0) { + gnutls_assert(); + goto cleanup; + } + } + crl->expanded = 1; + + result = + _asn1_strict_der_decode(&crl->crl, crl->der.data, crl->der.size, NULL); + if (result != ASN1_SUCCESS) { + result = _gnutls_asn2err(result); + gnutls_assert(); + goto cleanup; + } + + result = _gnutls_x509_get_raw_field2(crl->crl, &crl->der, + "tbsCertList.issuer.rdnSequence", + &crl->raw_issuer_dn); + if (result < 0) { + gnutls_assert(); + goto cleanup; + } + + return 0; + + cleanup: + _gnutls_free_datum(&crl->der); + return result; +} + + +/** + * gnutls_x509_crl_get_issuer_dn: + * @crl: should contain a gnutls_x509_crl_t type + * @buf: a pointer to a structure to hold the peer's name (may be null) + * @sizeof_buf: initially holds the size of @buf + * + * This function will copy the name of the CRL issuer in the provided + * buffer. The name will be in the form "C=xxxx,O=yyyy,CN=zzzz" as + * described in RFC4514. The output string will be ASCII or UTF-8 + * encoded, depending on the certificate data. + * + * If buf is %NULL then only the size will be filled. + * + * This function does not output a fully RFC4514 compliant string, if + * that is required see gnutls_x509_crl_get_issuer_dn3(). + * + * Returns: %GNUTLS_E_SHORT_MEMORY_BUFFER if the provided buffer is + * not long enough, and in that case the sizeof_buf will be updated + * with the required size, and 0 on success. + * + **/ +int +gnutls_x509_crl_get_issuer_dn(gnutls_x509_crl_t crl, char *buf, + size_t * sizeof_buf) +{ + if (crl == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + return _gnutls_x509_parse_dn(crl->crl, + "tbsCertList.issuer.rdnSequence", + buf, sizeof_buf, GNUTLS_X509_DN_FLAG_COMPAT); +} + +/** + * gnutls_x509_crl_get_issuer_dn_by_oid: + * @crl: should contain a gnutls_x509_crl_t type + * @oid: holds an Object Identified in null terminated string + * @indx: In case multiple same OIDs exist in the RDN, this specifies which to send. Use (0) to get the first one. + * @raw_flag: If non-zero returns the raw DER data of the DN part. + * @buf: a pointer to a structure to hold the peer's name (may be null) + * @sizeof_buf: initially holds the size of @buf + * + * This function will extract the part of the name of the CRL issuer + * specified by the given OID. The output will be encoded as described + * in RFC4514. The output string will be ASCII or UTF-8 encoded, + * depending on the certificate data. + * + * Some helper macros with popular OIDs can be found in gnutls/x509.h + * If raw flag is (0), this function will only return known OIDs as + * text. Other OIDs will be DER encoded, as described in RFC4514 -- in + * hex format with a '#' prefix. You can check about known OIDs + * using gnutls_x509_dn_oid_known(). + * + * If buf is null then only the size will be filled. + * + * Returns: %GNUTLS_E_SHORT_MEMORY_BUFFER if the provided buffer is + * not long enough, and in that case the sizeof_buf will be updated + * with the required size, and 0 on success. + **/ +int +gnutls_x509_crl_get_issuer_dn_by_oid(gnutls_x509_crl_t crl, + const char *oid, unsigned indx, + unsigned int raw_flag, void *buf, + size_t * sizeof_buf) +{ + gnutls_datum_t td; + int ret; + + if (crl == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + ret = _gnutls_x509_parse_dn_oid(crl->crl, + "tbsCertList.issuer.rdnSequence", + oid, indx, raw_flag, &td); + if (ret < 0) + return gnutls_assert_val(ret); + + return _gnutls_strdatum_to_buf(&td, buf, sizeof_buf); +} + + +/** + * gnutls_x509_crl_get_dn_oid: + * @crl: should contain a gnutls_x509_crl_t type + * @indx: Specifies which DN OID to send. Use (0) to get the first one. + * @oid: a pointer to store the OID (may be null) + * @sizeof_oid: initially holds the size of 'oid' + * + * This function will extract the requested OID of the name of the CRL + * issuer, specified by the given index. + * + * If oid is null then only the size will be filled. + * + * Returns: %GNUTLS_E_SHORT_MEMORY_BUFFER if the provided buffer is + * not long enough, and in that case the sizeof_oid will be updated + * with the required size. On success 0 is returned. + **/ +int +gnutls_x509_crl_get_dn_oid(gnutls_x509_crl_t crl, + unsigned indx, void *oid, size_t * sizeof_oid) +{ + if (crl == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + return _gnutls_x509_get_dn_oid(crl->crl, + "tbsCertList.issuer.rdnSequence", + indx, oid, sizeof_oid); +} + +/** + * gnutls_x509_crl_get_issuer_dn2: + * @crl: should contain a #gnutls_x509_crl_t type + * @dn: a pointer to a structure to hold the name; must be freed using gnutls_free() + * + * This function will allocate buffer and copy the name of the CRL issuer. + * The name will be in the form "C=xxxx,O=yyyy,CN=zzzz" as + * described in RFC4514. The output string will be ASCII or UTF-8 + * encoded, depending on the certificate data. + * + * This function does not output a fully RFC4514 compliant string, if + * that is required see gnutls_x509_crl_get_issuer_dn3(). + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 3.1.10 + **/ +int +gnutls_x509_crl_get_issuer_dn2(gnutls_x509_crl_t crl, gnutls_datum_t * dn) +{ + if (crl == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + return _gnutls_x509_get_dn(crl->crl, + "tbsCertList.issuer.rdnSequence", + dn, GNUTLS_X509_DN_FLAG_COMPAT); +} + +/** + * gnutls_x509_crl_get_issuer_dn3: + * @crl: should contain a #gnutls_x509_crl_t type + * @dn: a pointer to a structure to hold the name; must be freed using gnutls_free() + * @flags: zero or %GNUTLS_X509_DN_FLAG_COMPAT + * + * This function will allocate buffer and copy the name of the CRL issuer. + * The name will be in the form "C=xxxx,O=yyyy,CN=zzzz" as + * described in RFC4514. The output string will be ASCII or UTF-8 + * encoded, depending on the certificate data. + * + * When the flag %GNUTLS_X509_DN_FLAG_COMPAT is specified, the output + * format will match the format output by previous to 3.5.6 versions of GnuTLS + * which was not not fully RFC4514-compliant. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 3.5.7 + **/ +int +gnutls_x509_crl_get_issuer_dn3(gnutls_x509_crl_t crl, gnutls_datum_t * dn, unsigned flags) +{ + if (crl == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + return _gnutls_x509_get_dn(crl->crl, + "tbsCertList.issuer.rdnSequence", + dn, flags); +} + +/** + * gnutls_x509_crl_get_signature_algorithm: + * @crl: should contain a #gnutls_x509_crl_t type + * + * This function will return a value of the #gnutls_sign_algorithm_t + * enumeration that is the signature algorithm. + * + * Since 3.6.0 this function never returns a negative error code. + * Error cases and unknown/unsupported signature algorithms are + * mapped to %GNUTLS_SIGN_UNKNOWN. + * + * Returns: a #gnutls_sign_algorithm_t value + **/ +int gnutls_x509_crl_get_signature_algorithm(gnutls_x509_crl_t crl) +{ + return map_errs_to_zero(_gnutls_x509_get_signature_algorithm(crl->crl, + "signatureAlgorithm")); +} + +/** + * gnutls_x509_crl_get_signature_oid: + * @crl: should contain a #gnutls_x509_crl_t type + * @oid: a pointer to a buffer to hold the OID (may be null) + * @oid_size: initially holds the size of @oid + * + * This function will return the OID of the signature algorithm + * that has been used to sign this CRL. This is function + * is useful in the case gnutls_x509_crl_get_signature_algorithm() + * returned %GNUTLS_SIGN_UNKNOWN. + * + * Returns: zero or a negative error code on error. + * + * Since: 3.5.0 + **/ +int gnutls_x509_crl_get_signature_oid(gnutls_x509_crl_t crl, char *oid, size_t *oid_size) +{ + char str[MAX_OID_SIZE]; + int len, result, ret; + gnutls_datum_t out; + + len = sizeof(str); + result = asn1_read_value(crl->crl, "signatureAlgorithm.algorithm", str, &len); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + out.data = (void*)str; + out.size = len; + + ret = _gnutls_copy_string(&out, (void*)oid, oid_size); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + return 0; +} + +/** + * gnutls_x509_crl_get_signature: + * @crl: should contain a gnutls_x509_crl_t type + * @sig: a pointer where the signature part will be copied (may be null). + * @sizeof_sig: initially holds the size of @sig + * + * This function will extract the signature field of a CRL. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int +gnutls_x509_crl_get_signature(gnutls_x509_crl_t crl, + char *sig, size_t * sizeof_sig) +{ + int result; + unsigned int bits; + int len; + + if (crl == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + len = 0; + result = asn1_read_value(crl->crl, "signature", NULL, &len); + + if (result != ASN1_MEM_ERROR) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + bits = len; + if (bits % 8 != 0) { + gnutls_assert(); + return GNUTLS_E_CERTIFICATE_ERROR; + } + + len = bits / 8; + + if (*sizeof_sig < (unsigned) len) { + *sizeof_sig = bits / 8; + return GNUTLS_E_SHORT_MEMORY_BUFFER; + } + + result = asn1_read_value(crl->crl, "signature", sig, &len); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + return 0; +} + +/** + * gnutls_x509_crl_get_version: + * @crl: should contain a #gnutls_x509_crl_t type + * + * This function will return the version of the specified CRL. + * + * Returns: The version number, or a negative error code on error. + **/ +int gnutls_x509_crl_get_version(gnutls_x509_crl_t crl) +{ + if (crl == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + return _gnutls_x509_get_version(crl->crl, "tbsCertList.version"); +} + +/** + * gnutls_x509_crl_get_this_update: + * @crl: should contain a #gnutls_x509_crl_t type + * + * This function will return the time this CRL was issued. + * + * Returns: when the CRL was issued, or (time_t)-1 on error. + **/ +time_t gnutls_x509_crl_get_this_update(gnutls_x509_crl_t crl) +{ + if (crl == NULL) { + gnutls_assert(); + return (time_t) - 1; + } + + return _gnutls_x509_get_time(crl->crl, "tbsCertList.thisUpdate", + 0); +} + +/** + * gnutls_x509_crl_get_next_update: + * @crl: should contain a #gnutls_x509_crl_t type + * + * This function will return the time the next CRL will be issued. + * This field is optional in a CRL so it might be normal to get an + * error instead. + * + * Returns: when the next CRL will be issued, or (time_t)-1 on error. + **/ +time_t gnutls_x509_crl_get_next_update(gnutls_x509_crl_t crl) +{ + if (crl == NULL) { + gnutls_assert(); + return (time_t) - 1; + } + + return _gnutls_x509_get_time(crl->crl, "tbsCertList.nextUpdate", + 0); +} + +/** + * gnutls_x509_crl_get_crt_count: + * @crl: should contain a #gnutls_x509_crl_t type + * + * This function will return the number of revoked certificates in the + * given CRL. + * + * Returns: number of certificates, a negative error code on failure. + **/ +int gnutls_x509_crl_get_crt_count(gnutls_x509_crl_t crl) +{ + + int count, result; + + if (crl == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + result = + asn1_number_of_elements(crl->crl, + "tbsCertList.revokedCertificates", + &count); + + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return 0; /* no certificates */ + } + + return count; +} + +/** + * gnutls_x509_crl_get_crt_serial: + * @crl: should contain a #gnutls_x509_crl_t type + * @indx: the index of the certificate to extract (starting from 0) + * @serial: where the serial number will be copied + * @serial_size: initially holds the size of serial + * @t: if non null, will hold the time this certificate was revoked + * + * This function will retrieve the serial number of the specified, by + * the index, revoked certificate. + * + * Note that this function will have performance issues in large sequences + * of revoked certificates. In that case use gnutls_x509_crl_iter_crt_serial(). + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int +gnutls_x509_crl_get_crt_serial(gnutls_x509_crl_t crl, unsigned indx, + unsigned char *serial, + size_t * serial_size, time_t * t) +{ + + int result, _serial_size; + char serial_name[MAX_NAME_SIZE]; + char date_name[MAX_NAME_SIZE]; + + if (crl == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + snprintf(serial_name, sizeof(serial_name), + "tbsCertList.revokedCertificates.?%u.userCertificate", + indx + 1); + snprintf(date_name, sizeof(date_name), + "tbsCertList.revokedCertificates.?%u.revocationDate", + indx + 1); + + _serial_size = *serial_size; + result = + asn1_read_value(crl->crl, serial_name, serial, &_serial_size); + + *serial_size = _serial_size; + if (result != ASN1_SUCCESS) { + gnutls_assert(); + if (result == ASN1_ELEMENT_NOT_FOUND) + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + return _gnutls_asn2err(result); + } + + if (t) { + *t = _gnutls_x509_get_time(crl->crl, date_name, 0); + } + + return 0; +} + +/** + * gnutls_x509_crl_iter_deinit: + * @iter: The iterator to be deinitialized + * + * This function will deinitialize an iterator type. + **/ +void gnutls_x509_crl_iter_deinit(gnutls_x509_crl_iter_t iter) +{ + if (!iter) + return; + + gnutls_free(iter); +} + +/** + * gnutls_x509_crl_iter_crt_serial: + * @crl: should contain a #gnutls_x509_crl_t type + * @iter: A pointer to an iterator (initially the iterator should be %NULL) + * @serial: where the serial number will be copied + * @serial_size: initially holds the size of serial + * @t: if non null, will hold the time this certificate was revoked + * + * This function performs the same as gnutls_x509_crl_get_crt_serial(), + * but reads sequentially and keeps state in the iterator + * between calls. That allows it to provide better performance in sequences + * with many elements (50000+). + * + * When past the last element is accessed %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE + * is returned and the iterator is reset. + * + * After use, the iterator must be deinitialized using gnutls_x509_crl_iter_deinit(). + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int +gnutls_x509_crl_iter_crt_serial(gnutls_x509_crl_t crl, + gnutls_x509_crl_iter_t *iter, + unsigned char *serial, + size_t * serial_size, time_t * t) +{ + + int result, _serial_size; + char serial_name[MAX_NAME_SIZE]; + char date_name[MAX_NAME_SIZE]; + + if (crl == NULL || iter == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + if (*iter == NULL) { + *iter = gnutls_calloc(1, sizeof(struct gnutls_x509_crl_iter)); + if (*iter == NULL) + return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + } + + if ((*iter)->rcache == NULL) { + (*iter)->rcache = asn1_find_node (crl->crl, "tbsCertList.revokedCertificates.?1"); + (*iter)->rcache_idx = 1; + } else { + snprintf(serial_name, sizeof(serial_name), + "?%u", (*iter)->rcache_idx); + (*iter)->rcache = asn1_find_node ((*iter)->rcache, serial_name); + } + if ((*iter)->rcache == NULL) { + /* reset */ + (*iter)->rcache = NULL; + return gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE); + } + + snprintf(serial_name, sizeof(serial_name), + "?%u.userCertificate", (*iter)->rcache_idx); + + _serial_size = *serial_size; + result = + asn1_read_value((*iter)->rcache, serial_name, serial, &_serial_size); + + *serial_size = _serial_size; + if (result != ASN1_SUCCESS) { + gnutls_assert(); + if (result == ASN1_ELEMENT_NOT_FOUND) { + /* reset */ + (*iter)->rcache = NULL; + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + } + return _gnutls_asn2err(result); + } + + if (t) { + snprintf(date_name, sizeof(date_name), + "?%u.revocationDate", (*iter)->rcache_idx); + *t = _gnutls_x509_get_time((*iter)->rcache, date_name, 0); + } + + (*iter)->rcache_idx++; + + return 0; +} + +/** + * gnutls_x509_crl_get_raw_issuer_dn: + * @crl: should contain a gnutls_x509_crl_t type + * @dn: will hold the starting point of the DN + * + * This function will return a pointer to the DER encoded DN structure + * and the length. + * + * Returns: a negative error code on error, and (0) on success. + * + * Since: 2.12.0 + **/ +int +gnutls_x509_crl_get_raw_issuer_dn(gnutls_x509_crl_t crl, + gnutls_datum_t * dn) +{ + if (crl->raw_issuer_dn.size != 0) { + return _gnutls_set_datum(dn, crl->raw_issuer_dn.data, + crl->raw_issuer_dn.size); + } else { + return _gnutls_x509_get_raw_field(crl->crl, "tbsCertList.issuer.rdnSequence", dn); + } +} + +/** + * gnutls_x509_crl_export: + * @crl: Holds the revocation list + * @format: the format of output params. One of PEM or DER. + * @output_data: will contain a private key PEM or DER encoded + * @output_data_size: holds the size of output_data (and will + * be replaced by the actual size of parameters) + * + * This function will export the revocation list to DER or PEM format. + * + * If the buffer provided is not long enough to hold the output, then + * %GNUTLS_E_SHORT_MEMORY_BUFFER will be returned. + * + * If the structure is PEM encoded, it will have a header + * of "BEGIN X509 CRL". + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int +gnutls_x509_crl_export(gnutls_x509_crl_t crl, + gnutls_x509_crt_fmt_t format, void *output_data, + size_t * output_data_size) +{ + if (crl == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + return _gnutls_x509_export_int(crl->crl, format, PEM_CRL, + output_data, output_data_size); +} + +/** + * gnutls_x509_crl_export2: + * @crl: Holds the revocation list + * @format: the format of output params. One of PEM or DER. + * @out: will contain a private key PEM or DER encoded + * + * This function will export the revocation list to DER or PEM format. + * + * The output buffer is allocated using gnutls_malloc(). + * + * If the structure is PEM encoded, it will have a header + * of "BEGIN X509 CRL". + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since 3.1.3 + **/ +int +gnutls_x509_crl_export2(gnutls_x509_crl_t crl, + gnutls_x509_crt_fmt_t format, gnutls_datum_t * out) +{ + if (crl == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + return _gnutls_x509_export_int2(crl->crl, format, PEM_CRL, out); +} + +/*- + * _gnutls_x509_crl_cpy - This function copies a gnutls_x509_crl_t type + * @dest: The data where to copy + * @src: The data to be copied + * + * This function will copy an X.509 certificate structure. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + -*/ +int _gnutls_x509_crl_cpy(gnutls_x509_crl_t dest, gnutls_x509_crl_t src) +{ + int ret; + gnutls_datum_t tmp; + + ret = gnutls_x509_crl_export2(src, GNUTLS_X509_FMT_DER, &tmp); + if (ret < 0) + return gnutls_assert_val(ret); + + ret = gnutls_x509_crl_import(dest, &tmp, GNUTLS_X509_FMT_DER); + + gnutls_free(tmp.data); + + if (ret < 0) { + gnutls_assert(); + return ret; + } + + return 0; + +} + +static int +_get_authority_key_id(gnutls_x509_crl_t cert, asn1_node * c2, + unsigned int *critical) +{ + int ret; + gnutls_datum_t id; + + *c2 = NULL; + + if (cert == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + if ((ret = + _gnutls_x509_crl_get_extension(cert, "2.5.29.35", 0, &id, + critical)) < 0) { + return gnutls_assert_val(ret); + } + + if (id.size == 0 || id.data == NULL) { + gnutls_assert(); + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + } + + ret = asn1_create_element + (_gnutls_get_pkix(), "PKIX1.AuthorityKeyIdentifier", c2); + if (ret != ASN1_SUCCESS) { + gnutls_assert(); + _gnutls_free_datum(&id); + return _gnutls_asn2err(ret); + } + + ret = _asn1_strict_der_decode(c2, id.data, id.size, NULL); + _gnutls_free_datum(&id); + + if (ret != ASN1_SUCCESS) { + gnutls_assert(); + asn1_delete_structure(c2); + return _gnutls_asn2err(ret); + } + + return 0; +} + +/** + * gnutls_x509_crl_get_authority_key_gn_serial: + * @crl: should contain a #gnutls_x509_crl_t type + * @seq: specifies the sequence number of the alt name (0 for the first one, 1 for the second etc.) + * @alt: is the place where the alternative name will be copied to + * @alt_size: holds the size of alt. + * @alt_type: holds the type of the alternative name (one of gnutls_x509_subject_alt_name_t). + * @serial: buffer to store the serial number (may be null) + * @serial_size: Holds the size of the serial field (may be null) + * @critical: will be non-zero if the extension is marked as critical (may be null) + * + * This function will return the X.509 authority key + * identifier when stored as a general name (authorityCertIssuer) + * and serial number. + * + * Because more than one general names might be stored + * @seq can be used as a counter to request them all until + * %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE is returned. + * + * Returns: Returns 0 on success, or an error code. + * + * Since: 3.0 + **/ +int +gnutls_x509_crl_get_authority_key_gn_serial(gnutls_x509_crl_t crl, + unsigned int seq, + void *alt, + size_t * alt_size, + unsigned int *alt_type, + void *serial, + size_t * serial_size, + unsigned int *critical) +{ + int ret, result, len; + asn1_node c2; + + ret = _get_authority_key_id(crl, &c2, critical); + if (ret < 0) + return gnutls_assert_val(ret); + + ret = + _gnutls_parse_general_name(c2, "authorityCertIssuer", seq, alt, + alt_size, alt_type, 0); + if (ret < 0) { + ret = gnutls_assert_val(ret); + goto fail; + } + + if (serial) { + len = *serial_size; + result = + asn1_read_value(c2, "authorityCertSerialNumber", + serial, &len); + + *serial_size = len; + + if (result < 0) { + ret = _gnutls_asn2err(result); + goto fail; + } + + } + + ret = 0; + + fail: + asn1_delete_structure(&c2); + + return ret; +} + + +/** + * gnutls_x509_crl_get_authority_key_id: + * @crl: should contain a #gnutls_x509_crl_t type + * @id: The place where the identifier will be copied + * @id_size: Holds the size of the result field. + * @critical: will be non-zero if the extension is marked as critical + * (may be null) + * + * This function will return the CRL authority's key identifier. This + * is obtained by the X.509 Authority Key identifier extension field + * (2.5.29.35). Note that this function + * only returns the keyIdentifier field of the extension and + * %GNUTLS_E_X509_UNSUPPORTED_EXTENSION, if the extension contains + * the name and serial number of the certificate. In that case + * gnutls_x509_crl_get_authority_key_gn_serial() may be used. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error code in case of an error. + * + * Since: 2.8.0 + **/ +int +gnutls_x509_crl_get_authority_key_id(gnutls_x509_crl_t crl, void *id, + size_t * id_size, + unsigned int *critical) +{ + int result, len, ret; + asn1_node c2; + + ret = _get_authority_key_id(crl, &c2, critical); + if (ret < 0) + return gnutls_assert_val(ret); + + len = *id_size; + result = asn1_read_value(c2, "keyIdentifier", id, &len); + + *id_size = len; + asn1_delete_structure(&c2); + + if (result == ASN1_VALUE_NOT_FOUND + || result == ASN1_ELEMENT_NOT_FOUND) + return + gnutls_assert_val(GNUTLS_E_X509_UNSUPPORTED_EXTENSION); + + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + return 0; +} + +/** + * gnutls_x509_crl_get_number: + * @crl: should contain a #gnutls_x509_crl_t type + * @ret: The place where the number will be copied + * @ret_size: Holds the size of the result field. + * @critical: will be non-zero if the extension is marked as critical + * (may be null) + * + * This function will return the CRL number extension. This is + * obtained by the CRL Number extension field (2.5.29.20). + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error code in case of an error. + * + * Since: 2.8.0 + **/ +int +gnutls_x509_crl_get_number(gnutls_x509_crl_t crl, void *ret, + size_t * ret_size, unsigned int *critical) +{ + int result; + gnutls_datum_t id; + + if (crl == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + if (ret) + memset(ret, 0, *ret_size); + else + *ret_size = 0; + + if ((result = + _gnutls_x509_crl_get_extension(crl, "2.5.29.20", 0, &id, + critical)) < 0) { + return result; + } + + if (id.size == 0 || id.data == NULL) { + gnutls_assert(); + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + } + + result = + _gnutls_x509_ext_extract_number(ret, ret_size, id.data, + id.size); + + _gnutls_free_datum(&id); + + if (result < 0) { + gnutls_assert(); + return result; + } + + return 0; +} + +/** + * gnutls_x509_crl_get_extension_oid: + * @crl: should contain a #gnutls_x509_crl_t type + * @indx: Specifies which extension OID to send, use (0) to get the first one. + * @oid: a pointer to store the OID (may be null) + * @sizeof_oid: initially holds the size of @oid + * + * This function will return the requested extension OID in the CRL. + * The extension OID will be stored as a string in the provided + * buffer. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error code in case of an error. If your have reached the + * last extension available %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE + * will be returned. + * + * Since: 2.8.0 + **/ +int +gnutls_x509_crl_get_extension_oid(gnutls_x509_crl_t crl, unsigned indx, + void *oid, size_t * sizeof_oid) +{ + int result; + + if (crl == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + result = + _gnutls_x509_crl_get_extension_oid(crl, indx, oid, sizeof_oid); + if (result < 0) { + return result; + } + + return 0; + +} + +/** + * gnutls_x509_crl_get_extension_info: + * @crl: should contain a #gnutls_x509_crl_t type + * @indx: Specifies which extension OID to send, use (0) to get the first one. + * @oid: a pointer to store the OID + * @sizeof_oid: initially holds the maximum size of @oid, on return + * holds actual size of @oid. + * @critical: output variable with critical flag, may be NULL. + * + * This function will return the requested extension OID in the CRL, + * and the critical flag for it. The extension OID will be stored as + * a string in the provided buffer. Use + * gnutls_x509_crl_get_extension_data() to extract the data. + * + * If the buffer provided is not long enough to hold the output, then + * *@sizeof_oid is updated and %GNUTLS_E_SHORT_MEMORY_BUFFER will be + * returned. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error code in case of an error. If your have reached the + * last extension available %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE + * will be returned. + * + * Since: 2.8.0 + **/ +int +gnutls_x509_crl_get_extension_info(gnutls_x509_crl_t crl, unsigned indx, + void *oid, size_t * sizeof_oid, + unsigned int *critical) +{ + int result; + char str_critical[10]; + char name[MAX_NAME_SIZE]; + int len; + + if (!crl) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + snprintf(name, sizeof(name), + "tbsCertList.crlExtensions.?%u.extnID", indx + 1); + + len = *sizeof_oid; + result = asn1_read_value(crl->crl, name, oid, &len); + *sizeof_oid = len; + + if (result == ASN1_ELEMENT_NOT_FOUND) + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + else if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + snprintf(name, sizeof(name), + "tbsCertList.crlExtensions.?%u.critical", indx + 1); + len = sizeof(str_critical); + result = asn1_read_value(crl->crl, name, str_critical, &len); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + if (critical) { + if (str_critical[0] == 'T') + *critical = 1; + else + *critical = 0; + } + + return 0; + +} + +/** + * gnutls_x509_crl_get_extension_data: + * @crl: should contain a #gnutls_x509_crl_t type + * @indx: Specifies which extension OID to send. Use (0) to get the first one. + * @data: a pointer to a structure to hold the data (may be null) + * @sizeof_data: initially holds the size of @oid + * + * This function will return the requested extension data in the CRL. + * The extension data will be stored as a string in the provided + * buffer. + * + * Use gnutls_x509_crl_get_extension_info() to extract the OID and + * critical flag. Use gnutls_x509_crl_get_extension_info() instead, + * if you want to get data indexed by the extension OID rather than + * sequence. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error code in case of an error. If your have reached the + * last extension available %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE + * will be returned. + * + * Since: 2.8.0 + **/ +int +gnutls_x509_crl_get_extension_data(gnutls_x509_crl_t crl, unsigned indx, + void *data, size_t * sizeof_data) +{ + int result, len; + char name[MAX_NAME_SIZE]; + + if (!crl) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + snprintf(name, sizeof(name), + "tbsCertList.crlExtensions.?%u.extnValue", indx + 1); + + len = *sizeof_data; + result = asn1_read_value(crl->crl, name, data, &len); + *sizeof_data = len; + + if (result == ASN1_ELEMENT_NOT_FOUND) + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + else if (result < 0) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + return 0; +} + +/** + * gnutls_x509_crl_list_import2: + * @crls: Will contain the parsed crl list. + * @size: It will contain the size of the list. + * @data: The PEM encoded CRL. + * @format: One of DER or PEM. + * @flags: must be (0) or an OR'd sequence of gnutls_certificate_import_flags. + * + * This function will convert the given PEM encoded CRL list + * to the native gnutls_x509_crl_t format. The output will be stored + * in @crls. They will be automatically initialized. + * + * If the Certificate is PEM encoded it should have a header of "X509 + * CRL". + * + * Returns: the number of certificates read or a negative error value. + * + * Since: 3.0 + **/ +int +gnutls_x509_crl_list_import2(gnutls_x509_crl_t ** crls, + unsigned int *size, + const gnutls_datum_t * data, + gnutls_x509_crt_fmt_t format, + unsigned int flags) +{ + unsigned int init = 1024; + int ret; + + *crls = _gnutls_reallocarray(NULL, init, sizeof(gnutls_x509_crl_t)); + if (*crls == NULL) { + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; + } + + ret = + gnutls_x509_crl_list_import(*crls, &init, data, format, + flags | GNUTLS_X509_CRT_LIST_IMPORT_FAIL_IF_EXCEED); + if (ret == GNUTLS_E_SHORT_MEMORY_BUFFER) { + *crls = _gnutls_reallocarray_fast(*crls, init, + sizeof(gnutls_x509_crl_t)); + if (*crls == NULL) { + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; + } + + ret = + gnutls_x509_crl_list_import(*crls, &init, data, format, + flags); + } + + if (ret < 0) { + gnutls_free(*crls); + *crls = NULL; + return ret; + } + + *size = init; + return 0; +} + +/** + * gnutls_x509_crl_list_import: + * @crls: Indicates where the parsed CRLs will be copied to. Must not be initialized. + * @crl_max: Initially must hold the maximum number of crls. It will be updated with the number of crls available. + * @data: The PEM encoded CRLs + * @format: One of DER or PEM. + * @flags: must be (0) or an OR'd sequence of gnutls_certificate_import_flags. + * + * This function will convert the given PEM encoded CRL list + * to the native gnutls_x509_crl_t format. The output will be stored + * in @crls. They will be automatically initialized. + * + * If the Certificate is PEM encoded it should have a header of "X509 CRL". + * + * Returns: the number of certificates read or a negative error value. + * + * Since: 3.0 + **/ +int +gnutls_x509_crl_list_import(gnutls_x509_crl_t * crls, + unsigned int *crl_max, + const gnutls_datum_t * data, + gnutls_x509_crt_fmt_t format, + unsigned int flags) +{ + int size; + const char *ptr; + gnutls_datum_t tmp; + int ret, nocopy = 0; + unsigned int count = 0, j; + + if (format == GNUTLS_X509_FMT_DER) { + if (*crl_max < 1) { + *crl_max = 1; + return GNUTLS_E_SHORT_MEMORY_BUFFER; + } + + count = 1; /* import only the first one */ + + ret = gnutls_x509_crl_init(&crls[0]); + if (ret < 0) { + gnutls_assert(); + goto error; + } + + ret = gnutls_x509_crl_import(crls[0], data, format); + if (ret < 0) { + gnutls_assert(); + goto error; + } + + *crl_max = 1; + return 1; + } + + /* move to the certificate + */ + ptr = memmem(data->data, data->size, + PEM_CRL_SEP, sizeof(PEM_CRL_SEP) - 1); + if (ptr == NULL) { + gnutls_assert(); + return GNUTLS_E_BASE64_DECODING_ERROR; + } + + count = 0; + + do { + if (count >= *crl_max) { + if (! + (flags & + GNUTLS_X509_CRT_LIST_IMPORT_FAIL_IF_EXCEED)) { + break; + } else if (nocopy == 0) { + for (j = 0; j < count; j++) + gnutls_x509_crl_deinit(crls[j]); + nocopy = 1; + } + } + + if (!nocopy) { + ret = gnutls_x509_crl_init(&crls[count]); + if (ret < 0) { + gnutls_assert(); + goto error; + } + + tmp.data = (void *) ptr; + tmp.size = + data->size - (ptr - (char *) data->data); + ret = + gnutls_x509_crl_import(crls[count], &tmp, + GNUTLS_X509_FMT_PEM); + if (ret < 0) { + gnutls_assert(); + count++; + goto error; + } + } + + /* now we move ptr after the pem header + */ + ptr++; + /* find the next certificate (if any) + */ + size = data->size - (ptr - (char *) data->data); + + if (size > 0) { + ptr = + memmem(ptr, size, PEM_CRL_SEP, + sizeof(PEM_CRL_SEP) - 1); + } else + ptr = NULL; + + count++; + } + while (ptr != NULL); + + *crl_max = count; + + if (nocopy == 0) + return count; + else + return GNUTLS_E_SHORT_MEMORY_BUFFER; + + error: + for (j = 0; j < count; j++) + gnutls_x509_crl_deinit(crls[j]); + return ret; +} diff --git a/lib/x509/crl_write.c b/lib/x509/crl_write.c new file mode 100644 index 0000000..36ca452 --- /dev/null +++ b/lib/x509/crl_write.c @@ -0,0 +1,521 @@ +/* + * Copyright (C) 2003-2012 Free Software Foundation, Inc. + * Copyright (C) 2016 Red Hat, Inc. + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GnuTLS. + * + * The GnuTLS is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/> + * + */ + +/* This file contains functions to handle CRL generation. + */ + +#include "gnutls_int.h" + +#include <datum.h> +#include <global.h> +#include "errors.h" +#include <common.h> +#include <x509.h> +#include <x509_b64.h> +#include <x509_int.h> +#include <libtasn1.h> + +static void disable_optional_stuff(gnutls_x509_crl_t crl); + +/** + * gnutls_x509_crl_set_version: + * @crl: should contain a gnutls_x509_crl_t type + * @version: holds the version number. For CRLv1 crls must be 1. + * + * This function will set the version of the CRL. This + * must be one for CRL version 1, and so on. The CRLs generated + * by gnutls should have a version number of 2. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int +gnutls_x509_crl_set_version(gnutls_x509_crl_t crl, unsigned int version) +{ + int result; + uint8_t null = version & 0xFF; + + if (crl == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + if (null > 0) + null -= 1; + + result = + asn1_write_value(crl->crl, "tbsCertList.version", &null, 1); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + return 0; +} + +/** + * gnutls_x509_crl_sign2: + * @crl: should contain a gnutls_x509_crl_t type + * @issuer: is the certificate of the certificate issuer + * @issuer_key: holds the issuer's private key + * @dig: The message digest to use. GNUTLS_DIG_SHA256 is the safe choice unless you know what you're doing. + * @flags: must be 0 + * + * This function will sign the CRL with the issuer's private key, and + * will copy the issuer's information into the CRL. + * + * This must be the last step in a certificate CRL since all + * the previously set parameters are now signed. + * + * A known limitation of this function is, that a newly-signed CRL will not + * be fully functional (e.g., for signature verification), until it + * is exported an re-imported. + * + * After GnuTLS 3.6.1 the value of @dig may be %GNUTLS_DIG_UNKNOWN, + * and in that case, a suitable but reasonable for the key algorithm will be selected. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + **/ +int +gnutls_x509_crl_sign2(gnutls_x509_crl_t crl, gnutls_x509_crt_t issuer, + gnutls_x509_privkey_t issuer_key, + gnutls_digest_algorithm_t dig, unsigned int flags) +{ + int result; + gnutls_privkey_t privkey; + + if (crl == NULL || issuer == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + result = gnutls_privkey_init(&privkey); + if (result < 0) { + gnutls_assert(); + return result; + } + + result = gnutls_privkey_import_x509(privkey, issuer_key, 0); + if (result < 0) { + gnutls_assert(); + goto fail; + } + + result = + gnutls_x509_crl_privkey_sign(crl, issuer, privkey, dig, flags); + if (result < 0) { + gnutls_assert(); + goto fail; + } + + result = 0; + + fail: + gnutls_privkey_deinit(privkey); + + return result; +} + +/** + * gnutls_x509_crl_sign: + * @crl: should contain a gnutls_x509_crl_t type + * @issuer: is the certificate of the certificate issuer + * @issuer_key: holds the issuer's private key + * + * This function is the same a gnutls_x509_crl_sign2() with no flags, + * and an appropriate hash algorithm. The hash algorithm used may + * vary between versions of GnuTLS, and it is tied to the security + * level of the issuer's public key. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + */ +int +gnutls_x509_crl_sign(gnutls_x509_crl_t crl, gnutls_x509_crt_t issuer, + gnutls_x509_privkey_t issuer_key) +{ + return gnutls_x509_crl_sign2(crl, issuer, issuer_key, + 0, 0); +} + +/** + * gnutls_x509_crl_set_this_update: + * @crl: should contain a gnutls_x509_crl_t type + * @act_time: The actual time + * + * This function will set the time this CRL was issued. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int gnutls_x509_crl_set_this_update(gnutls_x509_crl_t crl, time_t act_time) +{ + if (crl == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + return _gnutls_x509_set_time(crl->crl, "tbsCertList.thisUpdate", + act_time, 0); +} + +/** + * gnutls_x509_crl_set_next_update: + * @crl: should contain a gnutls_x509_crl_t type + * @exp_time: The actual time + * + * This function will set the time this CRL will be updated. + * This is an optional value to be set on a CRL and this call + * can be omitted when generating a CRL. + * + * Prior to GnuTLS 3.5.7, setting a nextUpdate field was required + * in order to generate a CRL. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int gnutls_x509_crl_set_next_update(gnutls_x509_crl_t crl, time_t exp_time) +{ + if (crl == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + return _gnutls_x509_set_time(crl->crl, "tbsCertList.nextUpdate", + exp_time, 0); +} + +/** + * gnutls_x509_crl_set_crt_serial: + * @crl: should contain a gnutls_x509_crl_t type + * @serial: The revoked certificate's serial number + * @serial_size: Holds the size of the serial field. + * @revocation_time: The time this certificate was revoked + * + * This function will set a revoked certificate's serial number to the CRL. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int +gnutls_x509_crl_set_crt_serial(gnutls_x509_crl_t crl, + const void *serial, size_t serial_size, + time_t revocation_time) +{ + int ret; + + if (crl == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + ret = + asn1_write_value(crl->crl, "tbsCertList.revokedCertificates", + "NEW", 1); + if (ret != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(ret); + } + + ret = + asn1_write_value(crl->crl, + "tbsCertList.revokedCertificates.?LAST.userCertificate", + serial, serial_size); + if (ret != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(ret); + } + + ret = + _gnutls_x509_set_time(crl->crl, + "tbsCertList.revokedCertificates.?LAST.revocationDate", + revocation_time, 0); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + ret = + asn1_write_value(crl->crl, + "tbsCertList.revokedCertificates.?LAST.crlEntryExtensions", + NULL, 0); + if (ret != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(ret); + } + + return 0; +} + +/** + * gnutls_x509_crl_set_crt: + * @crl: should contain a gnutls_x509_crl_t type + * @crt: a certificate of type #gnutls_x509_crt_t with the revoked certificate + * @revocation_time: The time this certificate was revoked + * + * This function will set a revoked certificate's serial number to the CRL. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int +gnutls_x509_crl_set_crt(gnutls_x509_crl_t crl, gnutls_x509_crt_t crt, + time_t revocation_time) +{ + int ret; + uint8_t serial[128]; + size_t serial_size; + + if (crl == NULL || crt == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + serial_size = sizeof(serial); + ret = gnutls_x509_crt_get_serial(crt, serial, &serial_size); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + ret = + gnutls_x509_crl_set_crt_serial(crl, serial, serial_size, + revocation_time); + if (ret < 0) { + gnutls_assert(); + return _gnutls_asn2err(ret); + } + + return 0; +} + + +/* If OPTIONAL fields have not been initialized then + * disable them. + */ +static void disable_optional_stuff(gnutls_x509_crl_t crl) +{ + time_t t; + + t = _gnutls_x509_get_time(crl->crl, "tbsCertList.nextUpdate", 0); + if (t == (time_t)-1) { + (void)asn1_write_value(crl->crl, "tbsCertList.nextUpdate", NULL, 0); + } + + if (crl->use_extensions == 0) { + (void)asn1_write_value(crl->crl, "tbsCertList.crlExtensions", + NULL, 0); + } + + return; +} + +/** + * gnutls_x509_crl_set_authority_key_id: + * @crl: a CRL of type #gnutls_x509_crl_t + * @id: The key ID + * @id_size: Holds the size of the serial field. + * + * This function will set the CRL's authority key ID extension. Only + * the keyIdentifier field can be set with this function. This may + * be used by an authority that holds multiple private keys, to distinguish + * the used key. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 2.8.0 + **/ +int +gnutls_x509_crl_set_authority_key_id(gnutls_x509_crl_t crl, + const void *id, size_t id_size) +{ + int result; + gnutls_datum_t old_id, der_data; + unsigned int critical; + + if (crl == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + /* Check if the extension already exists. + */ + result = + _gnutls_x509_crl_get_extension(crl, "2.5.29.35", 0, &old_id, + &critical); + + if (result >= 0) { + _gnutls_free_datum(&old_id); + } else if (result != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + /* generate the extension. + */ + result = _gnutls_x509_ext_gen_auth_key_id(id, id_size, &der_data); + if (result < 0) { + gnutls_assert(); + return result; + } + + result = + _gnutls_x509_crl_set_extension(crl, "2.5.29.35", &der_data, 0); + + _gnutls_free_datum(&der_data); + + if (result < 0) { + gnutls_assert(); + return result; + } + + crl->use_extensions = 1; + + return 0; +} + +/** + * gnutls_x509_crl_set_number: + * @crl: a CRL of type #gnutls_x509_crl_t + * @nr: The CRL number + * @nr_size: Holds the size of the nr field. + * + * This function will set the CRL's number extension. This + * is to be used as a unique and monotonic number assigned to + * the CRL by the authority. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 2.8.0 + **/ +int +gnutls_x509_crl_set_number(gnutls_x509_crl_t crl, + const void *nr, size_t nr_size) +{ + int result; + gnutls_datum_t old_id, der_data; + unsigned int critical; + + if (crl == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + /* Check if the extension already exists. + */ + result = + _gnutls_x509_crl_get_extension(crl, "2.5.29.20", 0, &old_id, + &critical); + + if (result >= 0) { + _gnutls_free_datum(&old_id); + } else if (result != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + /* generate the extension. + */ + result = _gnutls_x509_ext_gen_number(nr, nr_size, &der_data); + if (result < 0) { + gnutls_assert(); + return result; + } + + result = + _gnutls_x509_crl_set_extension(crl, "2.5.29.20", &der_data, 0); + + _gnutls_free_datum(&der_data); + + if (result < 0) { + gnutls_assert(); + return result; + } + + crl->use_extensions = 1; + + return 0; +} + +/** + * gnutls_x509_crl_privkey_sign: + * @crl: should contain a gnutls_x509_crl_t type + * @issuer: is the certificate of the certificate issuer + * @issuer_key: holds the issuer's private key + * @dig: The message digest to use. GNUTLS_DIG_SHA256 is the safe choice unless you know what you're doing. + * @flags: must be 0 + * + * This function will sign the CRL with the issuer's private key, and + * will copy the issuer's information into the CRL. + * + * This must be the last step in a certificate CRL since all + * the previously set parameters are now signed. + * + * A known limitation of this function is, that a newly-signed CRL will not + * be fully functional (e.g., for signature verification), until it + * is exported an re-imported. + * + * After GnuTLS 3.6.1 the value of @dig may be %GNUTLS_DIG_UNKNOWN, + * and in that case, a suitable but reasonable for the key algorithm will be selected. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since 2.12.0 + **/ +int +gnutls_x509_crl_privkey_sign(gnutls_x509_crl_t crl, + gnutls_x509_crt_t issuer, + gnutls_privkey_t issuer_key, + gnutls_digest_algorithm_t dig, + unsigned int flags) +{ + int result; + + if (crl == NULL || issuer == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + if (dig == 0) { + result = gnutls_x509_crt_get_preferred_hash_algorithm(issuer, &dig, NULL); + if (result < 0) + return gnutls_assert_val(result); + } + + /* disable all the unneeded OPTIONAL fields. + */ + disable_optional_stuff(crl); + + result = _gnutls_x509_pkix_sign(crl->crl, "tbsCertList", + dig, 0, issuer, issuer_key); + if (result < 0) { + gnutls_assert(); + return result; + } + + return 0; +} diff --git a/lib/x509/crq.c b/lib/x509/crq.c new file mode 100644 index 0000000..2603022 --- /dev/null +++ b/lib/x509/crq.c @@ -0,0 +1,3099 @@ +/* + * Copyright (C) 2003-2016 Free Software Foundation, Inc. + * Copyright (C) 2012-2016 Nikos Mavrogiannopoulos + * Copyright (C) 2016-2017 Red Hat, Inc. + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GnuTLS. + * + * The GnuTLS is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/> + * + */ + +/* This file contains functions to handle PKCS #10 certificate + requests, see RFC 2986. + */ + +#include "gnutls_int.h" + +#include <datum.h> +#include <global.h> +#include "errors.h" +#include <common.h> +#include <x509.h> +#include <x509_b64.h> +#include <gnutls/x509-ext.h> +#include "x509_int.h" +#include <libtasn1.h> +#include <pk.h> +#include "attributes.h" + +/** + * gnutls_x509_crq_init: + * @crq: A pointer to the type to be initialized + * + * This function will initialize a PKCS#10 certificate request + * structure. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int gnutls_x509_crq_init(gnutls_x509_crq_t * crq) +{ + int result; + + FAIL_IF_LIB_ERROR; + + *crq = gnutls_calloc(1, sizeof(gnutls_x509_crq_int)); + if (!*crq) + return GNUTLS_E_MEMORY_ERROR; + + result = asn1_create_element(_gnutls_get_pkix(), + "PKIX1.pkcs-10-CertificationRequest", + &((*crq)->crq)); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + gnutls_free(*crq); + return _gnutls_asn2err(result); + } + + return 0; +} + +/** + * gnutls_x509_crq_deinit: + * @crq: the type to be deinitialized + * + * This function will deinitialize a PKCS#10 certificate request + * structure. + **/ +void gnutls_x509_crq_deinit(gnutls_x509_crq_t crq) +{ + if (!crq) + return; + + if (crq->crq) + asn1_delete_structure(&crq->crq); + + gnutls_free(crq); +} + +#define PEM_CRQ "NEW CERTIFICATE REQUEST" +#define PEM_CRQ2 "CERTIFICATE REQUEST" + +/** + * gnutls_x509_crq_import: + * @crq: The data to store the parsed certificate request. + * @data: The DER or PEM encoded certificate. + * @format: One of DER or PEM + * + * This function will convert the given DER or PEM encoded certificate + * request to a #gnutls_x509_crq_t type. The output will be + * stored in @crq. + * + * If the Certificate is PEM encoded it should have a header of "NEW + * CERTIFICATE REQUEST". + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int +gnutls_x509_crq_import(gnutls_x509_crq_t crq, + const gnutls_datum_t * data, + gnutls_x509_crt_fmt_t format) +{ + int result = 0, need_free = 0; + gnutls_datum_t _data; + + if (crq == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + _data.data = data->data; + _data.size = data->size; + + /* If the Certificate is in PEM format then decode it + */ + if (format == GNUTLS_X509_FMT_PEM) { + /* Try the first header */ + result = + _gnutls_fbase64_decode(PEM_CRQ, data->data, data->size, + &_data); + + if (result < 0) /* Go for the second header */ + result = + _gnutls_fbase64_decode(PEM_CRQ2, data->data, + data->size, &_data); + + if (result < 0) { + gnutls_assert(); + return result; + } + + need_free = 1; + } + + result = + _asn1_strict_der_decode(&crq->crq, _data.data, _data.size, NULL); + if (result != ASN1_SUCCESS) { + result = _gnutls_asn2err(result); + gnutls_assert(); + goto cleanup; + } + + result = 0; + + cleanup: + if (need_free) + _gnutls_free_datum(&_data); + return result; +} + +/** + * gnutls_x509_crq_get_signature_algorithm: + * @crq: should contain a #gnutls_x509_cr_t type + * + * This function will return a value of the #gnutls_sign_algorithm_t + * enumeration that is the signature algorithm that has been used to + * sign this certificate request. + * + * Since 3.6.0 this function never returns a negative error code. + * Error cases and unknown/unsupported signature algorithms are + * mapped to %GNUTLS_SIGN_UNKNOWN. + * + * Returns: a #gnutls_sign_algorithm_t value + * + * Since: 3.4.0 + **/ +int gnutls_x509_crq_get_signature_algorithm(gnutls_x509_crq_t crq) +{ + return map_errs_to_zero(_gnutls_x509_get_signature_algorithm(crq->crq, + "signatureAlgorithm")); +} + +/** + * gnutls_x509_crq_get_private_key_usage_period: + * @crq: should contain a #gnutls_x509_crq_t type + * @activation: The activation time + * @expiration: The expiration time + * @critical: the extension status + * + * This function will return the expiration and activation + * times of the private key of the certificate. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE + * if the extension is not present, otherwise a negative error value. + **/ +int +gnutls_x509_crq_get_private_key_usage_period(gnutls_x509_crq_t crq, + time_t * activation, + time_t * expiration, + unsigned int *critical) +{ + int result, ret; + asn1_node c2 = NULL; + uint8_t buf[128]; + size_t buf_size = sizeof(buf); + + if (crq == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + ret = gnutls_x509_crq_get_extension_by_oid(crq, "2.5.29.16", 0, + buf, &buf_size, + critical); + if (ret < 0) + return gnutls_assert_val(ret); + + result = asn1_create_element + (_gnutls_get_pkix(), "PKIX1.PrivateKeyUsagePeriod", &c2); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(result); + goto cleanup; + } + + result = _asn1_strict_der_decode(&c2, buf, buf_size, NULL); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(result); + goto cleanup; + } + + if (activation) + *activation = _gnutls_x509_get_time(c2, "notBefore", 1); + + if (expiration) + *expiration = _gnutls_x509_get_time(c2, "notAfter", 1); + + ret = 0; + + cleanup: + asn1_delete_structure(&c2); + + return ret; +} + + +/** + * gnutls_x509_crq_get_dn: + * @crq: should contain a #gnutls_x509_crq_t type + * @buf: a pointer to a structure to hold the name (may be %NULL) + * @buf_size: initially holds the size of @buf + * + * This function will copy the name of the Certificate request subject + * to the provided buffer. The name will be in the form + * "C=xxxx,O=yyyy,CN=zzzz" as described in RFC 2253. The output string + * @buf will be ASCII or UTF-8 encoded, depending on the certificate + * data. + * + * This function does not output a fully RFC4514 compliant string, if + * that is required see gnutls_x509_crq_get_dn3(). + * + * Returns: %GNUTLS_E_SHORT_MEMORY_BUFFER if the provided buffer is not + * long enough, and in that case the *@buf_size will be updated with + * the required size. On success 0 is returned. + **/ +int +gnutls_x509_crq_get_dn(gnutls_x509_crq_t crq, char *buf, size_t * buf_size) +{ + if (crq == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + return _gnutls_x509_parse_dn(crq->crq, + "certificationRequestInfo.subject.rdnSequence", + buf, buf_size, GNUTLS_X509_DN_FLAG_COMPAT); +} + +/** + * gnutls_x509_crq_get_dn2: + * @crq: should contain a #gnutls_x509_crq_t type + * @dn: a pointer to a structure to hold the name; must be freed using gnutls_free() + * + * This function will allocate buffer and copy the name of the Certificate + * request. The name will be in the form "C=xxxx,O=yyyy,CN=zzzz" as + * described in RFC4514. The output string will be ASCII or UTF-8 + * encoded, depending on the certificate data. + * + * This function does not output a fully RFC4514 compliant string, if + * that is required see gnutls_x509_crq_get_dn3(). + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. and a negative error code on error. + * + * Since: 3.1.10 + **/ +int gnutls_x509_crq_get_dn2(gnutls_x509_crq_t crq, gnutls_datum_t * dn) +{ + if (crq == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + return _gnutls_x509_get_dn(crq->crq, + "certificationRequestInfo.subject.rdnSequence", + dn, GNUTLS_X509_DN_FLAG_COMPAT); +} + +/** + * gnutls_x509_crq_get_dn3: + * @crq: should contain a #gnutls_x509_crq_t type + * @dn: a pointer to a structure to hold the name; must be freed using gnutls_free() + * @flags: zero or %GNUTLS_X509_DN_FLAG_COMPAT + * + * This function will allocate buffer and copy the name of the Certificate + * request. The name will be in the form "C=xxxx,O=yyyy,CN=zzzz" as + * described in RFC4514. The output string will be ASCII or UTF-8 + * encoded, depending on the certificate data. + * + * When the flag %GNUTLS_X509_DN_FLAG_COMPAT is specified, the output + * format will match the format output by previous to 3.5.6 versions of GnuTLS + * which was not not fully RFC4514-compliant. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. and a negative error code on error. + * + * Since: 3.5.7 + **/ +int gnutls_x509_crq_get_dn3(gnutls_x509_crq_t crq, gnutls_datum_t * dn, unsigned flags) +{ + if (crq == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + return _gnutls_x509_get_dn(crq->crq, + "certificationRequestInfo.subject.rdnSequence", + dn, flags); +} + +/** + * gnutls_x509_crq_get_dn_by_oid: + * @crq: should contain a gnutls_x509_crq_t type + * @oid: holds an Object Identifier in a null terminated string + * @indx: In case multiple same OIDs exist in the RDN, this specifies + * which to get. Use (0) to get the first one. + * @raw_flag: If non-zero returns the raw DER data of the DN part. + * @buf: a pointer to a structure to hold the name (may be %NULL) + * @buf_size: initially holds the size of @buf + * + * This function will extract the part of the name of the Certificate + * request subject, specified by the given OID. The output will be + * encoded as described in RFC2253. The output string will be ASCII + * or UTF-8 encoded, depending on the certificate data. + * + * Some helper macros with popular OIDs can be found in gnutls/x509.h + * If raw flag is (0), this function will only return known OIDs as + * text. Other OIDs will be DER encoded, as described in RFC2253 -- + * in hex format with a '\#' prefix. You can check about known OIDs + * using gnutls_x509_dn_oid_known(). + * + * Returns: %GNUTLS_E_SHORT_MEMORY_BUFFER if the provided buffer is + * not long enough, and in that case the *@buf_size will be + * updated with the required size. On success 0 is returned. + **/ +int +gnutls_x509_crq_get_dn_by_oid(gnutls_x509_crq_t crq, const char *oid, + unsigned indx, unsigned int raw_flag, + void *buf, size_t * buf_size) +{ + gnutls_datum_t td; + int ret; + + if (crq == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + ret = _gnutls_x509_parse_dn_oid + (crq->crq, + "certificationRequestInfo.subject.rdnSequence", + oid, indx, raw_flag, &td); + if (ret < 0) + return gnutls_assert_val(ret); + + return _gnutls_strdatum_to_buf(&td, buf, buf_size); +} + +/** + * gnutls_x509_crq_get_dn_oid: + * @crq: should contain a gnutls_x509_crq_t type + * @indx: Specifies which DN OID to get. Use (0) to get the first one. + * @oid: a pointer to a structure to hold the name (may be %NULL) + * @sizeof_oid: initially holds the size of @oid + * + * This function will extract the requested OID of the name of the + * certificate request subject, specified by the given index. + * + * Returns: %GNUTLS_E_SHORT_MEMORY_BUFFER if the provided buffer is + * not long enough, and in that case the *@sizeof_oid will be + * updated with the required size. On success 0 is returned. + **/ +int +gnutls_x509_crq_get_dn_oid(gnutls_x509_crq_t crq, + unsigned indx, void *oid, size_t * sizeof_oid) +{ + if (crq == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + return _gnutls_x509_get_dn_oid(crq->crq, + "certificationRequestInfo.subject.rdnSequence", + indx, oid, sizeof_oid); +} + +/** + * gnutls_x509_crq_get_challenge_password: + * @crq: should contain a #gnutls_x509_crq_t type + * @pass: will hold a (0)-terminated password string + * @pass_size: Initially holds the size of @pass. + * + * This function will return the challenge password in the request. + * The challenge password is intended to be used for requesting a + * revocation of the certificate. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int +gnutls_x509_crq_get_challenge_password(gnutls_x509_crq_t crq, + char *pass, size_t * pass_size) +{ + gnutls_datum_t td; + int ret; + + if (crq == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + ret = + _x509_parse_attribute(crq->crq, + "certificationRequestInfo.attributes", + "1.2.840.113549.1.9.7", 0, 0, &td); + if (ret < 0) + return gnutls_assert_val(ret); + + return _gnutls_strdatum_to_buf(&td, pass, pass_size); +} + +/** + * gnutls_x509_crq_set_attribute_by_oid: + * @crq: should contain a #gnutls_x509_crq_t type + * @oid: holds an Object Identifier in a null-terminated string + * @buf: a pointer to a structure that holds the attribute data + * @buf_size: holds the size of @buf + * + * This function will set the attribute in the certificate request + * specified by the given Object ID. The provided attribute must be be DER + * encoded. + * + * Attributes in a certificate request is an optional set of data + * appended to the request. Their interpretation depends on the CA policy. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int +gnutls_x509_crq_set_attribute_by_oid(gnutls_x509_crq_t crq, + const char *oid, void *buf, + size_t buf_size) +{ + gnutls_datum_t data; + + data.data = buf; + data.size = buf_size; + + if (crq == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + return _x509_set_attribute(crq->crq, + "certificationRequestInfo.attributes", oid, + &data); +} + +/** + * gnutls_x509_crq_get_attribute_by_oid: + * @crq: should contain a #gnutls_x509_crq_t type + * @oid: holds an Object Identifier in null-terminated string + * @indx: In case multiple same OIDs exist in the attribute list, this + * specifies which to get, use (0) to get the first one + * @buf: a pointer to a structure to hold the attribute data (may be %NULL) + * @buf_size: initially holds the size of @buf + * + * This function will return the attribute in the certificate request + * specified by the given Object ID. The attribute will be DER + * encoded. + * + * Attributes in a certificate request is an optional set of data + * appended to the request. Their interpretation depends on the CA policy. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int +gnutls_x509_crq_get_attribute_by_oid(gnutls_x509_crq_t crq, + const char *oid, unsigned indx, void *buf, + size_t * buf_size) +{ + int ret; + gnutls_datum_t td; + + if (crq == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + ret = + _x509_parse_attribute(crq->crq, + "certificationRequestInfo.attributes", oid, + indx, 1, &td); + if (ret < 0) + return gnutls_assert_val(ret); + + return _gnutls_strdatum_to_buf(&td, buf, buf_size); +} + +/** + * gnutls_x509_crq_set_dn_by_oid: + * @crq: should contain a #gnutls_x509_crq_t type + * @oid: holds an Object Identifier in a (0)-terminated string + * @raw_flag: must be 0, or 1 if the data are DER encoded + * @data: a pointer to the input data + * @sizeof_data: holds the size of @data + * + * This function will set the part of the name of the Certificate + * request subject, specified by the given OID. The input string + * should be ASCII or UTF-8 encoded. + * + * Some helper macros with popular OIDs can be found in gnutls/x509.h + * With this function you can only set the known OIDs. You can test + * for known OIDs using gnutls_x509_dn_oid_known(). For OIDs that are + * not known (by gnutls) you should properly DER encode your data, and + * call this function with raw_flag set. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int +gnutls_x509_crq_set_dn_by_oid(gnutls_x509_crq_t crq, const char *oid, + unsigned int raw_flag, const void *data, + unsigned int sizeof_data) +{ + if (sizeof_data == 0 || data == NULL || crq == NULL) { + return GNUTLS_E_INVALID_REQUEST; + } + + return _gnutls_x509_set_dn_oid(crq->crq, + "certificationRequestInfo.subject", + oid, raw_flag, data, sizeof_data); +} + +/** + * gnutls_x509_crq_set_version: + * @crq: should contain a #gnutls_x509_crq_t type + * @version: holds the version number, for v1 Requests must be 1 + * + * This function will set the version of the certificate request. For + * version 1 requests this must be one. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int +gnutls_x509_crq_set_version(gnutls_x509_crq_t crq, unsigned int version) +{ + int result; + unsigned char null = version; + + if (crq == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + if (null > 0) + null--; + + result = + asn1_write_value(crq->crq, "certificationRequestInfo.version", + &null, 1); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + return 0; +} + +/** + * gnutls_x509_crq_get_version: + * @crq: should contain a #gnutls_x509_crq_t type + * + * This function will return the version of the specified Certificate + * request. + * + * Returns: version of certificate request, or a negative error code on + * error. + **/ +int gnutls_x509_crq_get_version(gnutls_x509_crq_t crq) +{ + uint8_t version[8]; + int len, result; + + if (crq == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + len = sizeof(version); + if ((result = + asn1_read_value(crq->crq, "certificationRequestInfo.version", + version, &len)) != ASN1_SUCCESS) { + + if (result == ASN1_ELEMENT_NOT_FOUND) + return 1; /* the DEFAULT version */ + gnutls_assert(); + return _gnutls_asn2err(result); + } + + return (int) version[0] + 1; +} + +/** + * gnutls_x509_crq_set_key: + * @crq: should contain a #gnutls_x509_crq_t type + * @key: holds a private key + * + * This function will set the public parameters from the given private + * key to the request. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int +gnutls_x509_crq_set_key(gnutls_x509_crq_t crq, gnutls_x509_privkey_t key) +{ + int result; + + if (crq == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + result = _gnutls_x509_encode_and_copy_PKI_params + (crq->crq, + "certificationRequestInfo.subjectPKInfo", + &key->params); + + if (result < 0) { + gnutls_assert(); + return result; + } + + return 0; +} + +/** + * gnutls_x509_crq_get_key_rsa_raw: + * @crq: Holds the certificate + * @m: will hold the modulus + * @e: will hold the public exponent + * + * This function will export the RSA public key's parameters found in + * the given structure. The new parameters will be allocated using + * gnutls_malloc() and will be stored in the appropriate datum. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 2.8.0 + **/ +int +gnutls_x509_crq_get_key_rsa_raw(gnutls_x509_crq_t crq, + gnutls_datum_t * m, gnutls_datum_t * e) +{ + int ret; + gnutls_pk_params_st params; + + gnutls_pk_params_init(¶ms); + + if (crq == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + ret = gnutls_x509_crq_get_pk_algorithm(crq, NULL); + if (ret != GNUTLS_PK_RSA) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + ret = _gnutls_x509_crq_get_mpis(crq, ¶ms); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + ret = _gnutls_mpi_dprint(params.params[0], m); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = _gnutls_mpi_dprint(params.params[1], e); + if (ret < 0) { + gnutls_assert(); + _gnutls_free_datum(m); + goto cleanup; + } + + ret = 0; + + cleanup: + gnutls_pk_params_release(¶ms); + return ret; +} + +/** + * gnutls_x509_crq_set_key_rsa_raw: + * @crq: should contain a #gnutls_x509_crq_t type + * @m: holds the modulus + * @e: holds the public exponent + * + * This function will set the public parameters from the given private + * key to the request. Only RSA keys are currently supported. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 2.6.0 + **/ +int +gnutls_x509_crq_set_key_rsa_raw(gnutls_x509_crq_t crq, + const gnutls_datum_t * m, + const gnutls_datum_t * e) +{ + int result, ret; + size_t siz = 0; + gnutls_pk_params_st temp_params; + + gnutls_pk_params_init(&temp_params); + + if (crq == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + memset(&temp_params, 0, sizeof(temp_params)); + + siz = m->size; + if (_gnutls_mpi_init_scan_nz(&temp_params.params[0], m->data, siz)) { + gnutls_assert(); + ret = GNUTLS_E_MPI_SCAN_FAILED; + goto error; + } + + siz = e->size; + if (_gnutls_mpi_init_scan_nz(&temp_params.params[1], e->data, siz)) { + gnutls_assert(); + ret = GNUTLS_E_MPI_SCAN_FAILED; + goto error; + } + + temp_params.params_nr = RSA_PUBLIC_PARAMS; + temp_params.algo = GNUTLS_PK_RSA; + + result = _gnutls_x509_encode_and_copy_PKI_params + (crq->crq, + "certificationRequestInfo.subjectPKInfo", + &temp_params); + + if (result < 0) { + gnutls_assert(); + ret = result; + goto error; + } + + ret = 0; + + error: + gnutls_pk_params_release(&temp_params); + return ret; +} + +/** + * gnutls_x509_crq_set_challenge_password: + * @crq: should contain a #gnutls_x509_crq_t type + * @pass: holds a (0)-terminated password + * + * This function will set a challenge password to be used when + * revoking the request. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int +gnutls_x509_crq_set_challenge_password(gnutls_x509_crq_t crq, + const char *pass) +{ + int result; + char *password = NULL; + + if (crq == NULL || pass == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + /* Add the attribute. + */ + result = + asn1_write_value(crq->crq, + "certificationRequestInfo.attributes", "NEW", + 1); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + if (pass) { + gnutls_datum_t out; + result = _gnutls_utf8_password_normalize(pass, strlen(pass), &out, 0); + if (result < 0) + return gnutls_assert_val(result); + + password = (char*)out.data; + } + + assert(password != NULL); + + result = _gnutls_x509_encode_and_write_attribute + ("1.2.840.113549.1.9.7", crq->crq, + "certificationRequestInfo.attributes.?LAST", password, + strlen(password), 1); + if (result < 0) { + gnutls_assert(); + goto cleanup; + } + + result = 0; + + cleanup: + gnutls_free(password); + return result; +} + +/** + * gnutls_x509_crq_sign2: + * @crq: should contain a #gnutls_x509_crq_t type + * @key: holds a private key + * @dig: The message digest to use, i.e., %GNUTLS_DIG_SHA256 + * @flags: must be 0 + * + * This function will sign the certificate request with a private key. + * This must be the same key as the one used in + * gnutls_x509_crt_set_key() since a certificate request is self + * signed. + * + * This must be the last step in a certificate request generation + * since all the previously set parameters are now signed. + * + * A known limitation of this function is, that a newly-signed request will not + * be fully functional (e.g., for signature verification), until it + * is exported an re-imported. + * + * After GnuTLS 3.6.1 the value of @dig may be %GNUTLS_DIG_UNKNOWN, + * and in that case, a suitable but reasonable for the key algorithm will be selected. + * + * Returns: %GNUTLS_E_SUCCESS on success, otherwise a negative error code. + * %GNUTLS_E_ASN1_VALUE_NOT_FOUND is returned if you didn't set all + * information in the certificate request (e.g., the version using + * gnutls_x509_crq_set_version()). + * + **/ +int +gnutls_x509_crq_sign2(gnutls_x509_crq_t crq, gnutls_x509_privkey_t key, + gnutls_digest_algorithm_t dig, unsigned int flags) +{ + int result; + gnutls_privkey_t privkey; + + if (crq == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + result = gnutls_privkey_init(&privkey); + if (result < 0) { + gnutls_assert(); + return result; + } + + result = gnutls_privkey_import_x509(privkey, key, 0); + if (result < 0) { + gnutls_assert(); + goto fail; + } + + result = gnutls_x509_crq_privkey_sign(crq, privkey, dig, flags); + if (result < 0) { + gnutls_assert(); + goto fail; + } + + result = 0; + + fail: + gnutls_privkey_deinit(privkey); + + return result; +} + +/** + * gnutls_x509_crq_sign: + * @crq: should contain a #gnutls_x509_crq_t type + * @key: holds a private key + * + * This function is the same a gnutls_x509_crq_sign2() with no flags, + * and an appropriate hash algorithm. The hash algorithm used may + * vary between versions of GnuTLS, and it is tied to the security + * level of the issuer's public key. + * + * A known limitation of this function is, that a newly-signed request will not + * be fully functional (e.g., for signature verification), until it + * is exported an re-imported. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + */ +int gnutls_x509_crq_sign(gnutls_x509_crq_t crq, gnutls_x509_privkey_t key) +{ + return gnutls_x509_crq_sign2(crq, key, 0, 0); +} + +/** + * gnutls_x509_crq_export: + * @crq: should contain a #gnutls_x509_crq_t type + * @format: the format of output params. One of PEM or DER. + * @output_data: will contain a certificate request PEM or DER encoded + * @output_data_size: holds the size of output_data (and will be + * replaced by the actual size of parameters) + * + * This function will export the certificate request to a PEM or DER + * encoded PKCS10 structure. + * + * If the buffer provided is not long enough to hold the output, then + * %GNUTLS_E_SHORT_MEMORY_BUFFER will be returned and + * *@output_data_size will be updated. + * + * If the structure is PEM encoded, it will have a header of "BEGIN + * NEW CERTIFICATE REQUEST". + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int +gnutls_x509_crq_export(gnutls_x509_crq_t crq, + gnutls_x509_crt_fmt_t format, void *output_data, + size_t * output_data_size) +{ + if (crq == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + return _gnutls_x509_export_int(crq->crq, format, PEM_CRQ, + output_data, output_data_size); +} + +/** + * gnutls_x509_crq_export2: + * @crq: should contain a #gnutls_x509_crq_t type + * @format: the format of output params. One of PEM or DER. + * @out: will contain a certificate request PEM or DER encoded + * + * This function will export the certificate request to a PEM or DER + * encoded PKCS10 structure. + * + * The output buffer is allocated using gnutls_malloc(). + * + * If the structure is PEM encoded, it will have a header of "BEGIN + * NEW CERTIFICATE REQUEST". + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since 3.1.3 + **/ +int +gnutls_x509_crq_export2(gnutls_x509_crq_t crq, + gnutls_x509_crt_fmt_t format, gnutls_datum_t * out) +{ + if (crq == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + return _gnutls_x509_export_int2(crq->crq, format, PEM_CRQ, out); +} + +/** + * gnutls_x509_crq_get_pk_algorithm: + * @crq: should contain a #gnutls_x509_crq_t type + * @bits: if bits is non-%NULL it will hold the size of the parameters' in bits + * + * This function will return the public key algorithm of a PKCS#10 + * certificate request. + * + * If bits is non-%NULL, it should have enough size to hold the + * parameters size in bits. For RSA the bits returned is the modulus. + * For DSA the bits returned are of the public exponent. + * + * Returns: a member of the #gnutls_pk_algorithm_t enumeration on + * success, or a negative error code on error. + **/ +int +gnutls_x509_crq_get_pk_algorithm(gnutls_x509_crq_t crq, unsigned int *bits) +{ + int result; + + if (crq == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + result = _gnutls_x509_get_pk_algorithm + (crq->crq, "certificationRequestInfo.subjectPKInfo", NULL, bits); + if (result < 0) { + gnutls_assert(); + return result; + } + + return result; +} + +/** + * gnutls_x509_crq_get_spki; + * @crq: should contain a #gnutls_x509_crq_t type + * @spki: a SubjectPublicKeyInfo structure of type #gnutls_x509_spki_t + * @flags: must be zero + * + * This function will return the public key information of a PKCS#10 + * certificate request. The provided @spki must be initialized. + * + * Returns: Zero on success, or a negative error code on error. + **/ +int +gnutls_x509_crq_get_spki(gnutls_x509_crq_t crq, + gnutls_x509_spki_t spki, + unsigned int flags) +{ + int result; + gnutls_x509_spki_st params; + + if (crq == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + memset(¶ms, 0, sizeof(params)); + + spki->pk = gnutls_x509_crq_get_pk_algorithm(crq, NULL); + + result = _gnutls_x509_crq_read_spki_params(crq, ¶ms); + if (result < 0) { + gnutls_assert(); + return result; + } + + if (params.pk == GNUTLS_PK_UNKNOWN) + return gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE); + + spki->rsa_pss_dig = params.rsa_pss_dig; + spki->salt_size = params.salt_size; + + return 0; +} + +/** + * gnutls_x509_crq_get_signature_oid: + * @crq: should contain a #gnutls_x509_crq_t type + * @oid: a pointer to a buffer to hold the OID (may be null) + * @oid_size: initially holds the size of @oid + * + * This function will return the OID of the signature algorithm + * that has been used to sign this certificate request. This function + * is useful in the case gnutls_x509_crq_get_signature_algorithm() + * returned %GNUTLS_SIGN_UNKNOWN. + * + * Returns: zero or a negative error code on error. + * + * Since: 3.5.0 + **/ +int gnutls_x509_crq_get_signature_oid(gnutls_x509_crq_t crq, char *oid, size_t *oid_size) +{ + char str[MAX_OID_SIZE]; + int len, result, ret; + gnutls_datum_t out; + + len = sizeof(str); + result = asn1_read_value(crq->crq, "signatureAlgorithm.algorithm", str, &len); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + out.data = (void*)str; + out.size = len; + + ret = _gnutls_copy_string(&out, (void*)oid, oid_size); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + return 0; +} + +/** + * gnutls_x509_crq_get_pk_oid: + * @crq: should contain a #gnutls_x509_crq_t type + * @oid: a pointer to a buffer to hold the OID (may be null) + * @oid_size: initially holds the size of @oid + * + * This function will return the OID of the public key algorithm + * on that certificate request. This function + * is useful in the case gnutls_x509_crq_get_pk_algorithm() + * returned %GNUTLS_PK_UNKNOWN. + * + * Returns: zero or a negative error code on error. + * + * Since: 3.5.0 + **/ +int gnutls_x509_crq_get_pk_oid(gnutls_x509_crq_t crq, char *oid, size_t *oid_size) +{ + char str[MAX_OID_SIZE]; + int len, result, ret; + gnutls_datum_t out; + + len = sizeof(str); + result = asn1_read_value(crq->crq, "certificationRequestInfo.subjectPKInfo.algorithm.algorithm", str, &len); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + out.data = (void*)str; + out.size = len; + + ret = _gnutls_copy_string(&out, (void*)oid, oid_size); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + return 0; +} + +/** + * gnutls_x509_crq_get_attribute_info: + * @crq: should contain a #gnutls_x509_crq_t type + * @indx: Specifies which attribute number to get. Use (0) to get the first one. + * @oid: a pointer to a structure to hold the OID + * @sizeof_oid: initially holds the maximum size of @oid, on return + * holds actual size of @oid. + * + * This function will return the requested attribute OID in the + * certificate, and the critical flag for it. The attribute OID will + * be stored as a string in the provided buffer. Use + * gnutls_x509_crq_get_attribute_data() to extract the data. + * + * If the buffer provided is not long enough to hold the output, then + * *@sizeof_oid is updated and %GNUTLS_E_SHORT_MEMORY_BUFFER will be + * returned. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error code in case of an error. If your have reached the + * last extension available %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE + * will be returned. + * + * Since: 2.8.0 + **/ +int +gnutls_x509_crq_get_attribute_info(gnutls_x509_crq_t crq, unsigned indx, + void *oid, size_t * sizeof_oid) +{ + int result; + char name[MAX_NAME_SIZE]; + int len; + + if (!crq) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + snprintf(name, sizeof(name), + "certificationRequestInfo.attributes.?%u.type", indx + 1); + + len = *sizeof_oid; + result = asn1_read_value(crq->crq, name, oid, &len); + *sizeof_oid = len; + + if (result == ASN1_ELEMENT_NOT_FOUND) + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + else if (result < 0) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + return 0; + +} + +/** + * gnutls_x509_crq_get_attribute_data: + * @crq: should contain a #gnutls_x509_crq_t type + * @indx: Specifies which attribute number to get. Use (0) to get the first one. + * @data: a pointer to a structure to hold the data (may be null) + * @sizeof_data: initially holds the size of @oid + * + * This function will return the requested attribute data in the + * certificate request. The attribute data will be stored as a string in the + * provided buffer. + * + * Use gnutls_x509_crq_get_attribute_info() to extract the OID. + * Use gnutls_x509_crq_get_attribute_by_oid() instead, + * if you want to get data indexed by the attribute OID rather than + * sequence. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error code in case of an error. If your have reached the + * last extension available %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE + * will be returned. + * + * Since: 2.8.0 + **/ +int +gnutls_x509_crq_get_attribute_data(gnutls_x509_crq_t crq, unsigned indx, + void *data, size_t * sizeof_data) +{ + int result, len; + char name[MAX_NAME_SIZE]; + + if (!crq) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + snprintf(name, sizeof(name), + "certificationRequestInfo.attributes.?%u.values.?1", + indx + 1); + + len = *sizeof_data; + result = asn1_read_value(crq->crq, name, data, &len); + *sizeof_data = len; + + if (result == ASN1_ELEMENT_NOT_FOUND) + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + else if (result < 0) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + return 0; +} + +/** + * gnutls_x509_crq_get_extension_info: + * @crq: should contain a #gnutls_x509_crq_t type + * @indx: Specifies which extension number to get. Use (0) to get the first one. + * @oid: a pointer to store the OID + * @sizeof_oid: initially holds the maximum size of @oid, on return + * holds actual size of @oid. + * @critical: output variable with critical flag, may be NULL. + * + * This function will return the requested extension OID in the + * certificate, and the critical flag for it. The extension OID will + * be stored as a string in the provided buffer. Use + * gnutls_x509_crq_get_extension_data() to extract the data. + * + * If the buffer provided is not long enough to hold the output, then + * *@sizeof_oid is updated and %GNUTLS_E_SHORT_MEMORY_BUFFER will be + * returned. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error code in case of an error. If your have reached the + * last extension available %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE + * will be returned. + * + * Since: 2.8.0 + **/ +int +gnutls_x509_crq_get_extension_info(gnutls_x509_crq_t crq, unsigned indx, + void *oid, size_t * sizeof_oid, + unsigned int *critical) +{ + int result; + char str_critical[10]; + char name[MAX_NAME_SIZE]; + char *extensions = NULL; + size_t extensions_size = 0; + asn1_node c2; + int len; + + if (!crq) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + /* read extensionRequest */ + result = + gnutls_x509_crq_get_attribute_by_oid(crq, + "1.2.840.113549.1.9.14", + 0, NULL, + &extensions_size); + if (result == GNUTLS_E_SHORT_MEMORY_BUFFER) { + extensions = gnutls_malloc(extensions_size); + if (extensions == NULL) { + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; + } + + result = gnutls_x509_crq_get_attribute_by_oid(crq, + "1.2.840.113549.1.9.14", + 0, + extensions, + &extensions_size); + } + if (result < 0) { + gnutls_assert(); + goto out; + } + + result = + asn1_create_element(_gnutls_get_pkix(), "PKIX1.Extensions", + &c2); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto out; + } + + result = _asn1_strict_der_decode(&c2, extensions, extensions_size, NULL); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + asn1_delete_structure(&c2); + result = _gnutls_asn2err(result); + goto out; + } + + snprintf(name, sizeof(name), "?%u.extnID", indx + 1); + + len = *sizeof_oid; + result = asn1_read_value(c2, name, oid, &len); + *sizeof_oid = len; + + if (result == ASN1_ELEMENT_NOT_FOUND) { + asn1_delete_structure(&c2); + result = GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + goto out; + } else if (result < 0) { + gnutls_assert(); + asn1_delete_structure(&c2); + result = _gnutls_asn2err(result); + goto out; + } + + snprintf(name, sizeof(name), "?%u.critical", indx + 1); + len = sizeof(str_critical); + result = asn1_read_value(c2, name, str_critical, &len); + + asn1_delete_structure(&c2); + + if (result < 0) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto out; + } + + if (critical) { + if (str_critical[0] == 'T') + *critical = 1; + else + *critical = 0; + } + + result = 0; + + out: + gnutls_free(extensions); + return result; +} + +/** + * gnutls_x509_crq_get_extension_data: + * @crq: should contain a #gnutls_x509_crq_t type + * @indx: Specifies which extension number to get. Use (0) to get the first one. + * @data: a pointer to a structure to hold the data (may be null) + * @sizeof_data: initially holds the size of @oid + * + * This function will return the requested extension data in the + * certificate. The extension data will be stored as a string in the + * provided buffer. + * + * Use gnutls_x509_crq_get_extension_info() to extract the OID and + * critical flag. Use gnutls_x509_crq_get_extension_by_oid() instead, + * if you want to get data indexed by the extension OID rather than + * sequence. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error code in case of an error. If your have reached the + * last extension available %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE + * will be returned. + * + * Since: 2.8.0 + **/ +int +gnutls_x509_crq_get_extension_data(gnutls_x509_crq_t crq, unsigned indx, + void *data, size_t * sizeof_data) +{ + int ret; + gnutls_datum_t raw; + + ret = gnutls_x509_crq_get_extension_data2(crq, indx, &raw); + if (ret < 0) + return gnutls_assert_val(ret); + + ret = _gnutls_copy_data(&raw, data, sizeof_data); + if (ret == GNUTLS_E_SHORT_MEMORY_BUFFER && data == NULL) + ret = 0; + gnutls_free(raw.data); + return ret; +} + +/** + * gnutls_x509_crq_get_extension_data2: + * @crq: should contain a #gnutls_x509_crq_t type + * @extension_id: An X.509 extension OID. + * @indx: Specifies which extension OID to read. Use (0) to get the first one. + * @data: will contain the extension DER-encoded data + * + * This function will return the requested extension data in the + * certificate request. The extension data will be allocated using + * gnutls_malloc(). + * + * Use gnutls_x509_crq_get_extension_info() to extract the OID. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, + * otherwise a negative error code is returned. If you have reached the + * last extension available %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE + * will be returned. + * + * Since: 3.3.0 + **/ +int +gnutls_x509_crq_get_extension_data2(gnutls_x509_crq_t crq, + unsigned indx, gnutls_datum_t * data) +{ + int ret, result; + char name[MAX_NAME_SIZE]; + unsigned char *extensions = NULL; + size_t extensions_size = 0; + asn1_node c2 = NULL; + + if (!crq) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + /* read extensionRequest */ + ret = + gnutls_x509_crq_get_attribute_by_oid(crq, + "1.2.840.113549.1.9.14", + 0, NULL, + &extensions_size); + if (ret != GNUTLS_E_SHORT_MEMORY_BUFFER) { + gnutls_assert(); + if (ret == 0) + return GNUTLS_E_INTERNAL_ERROR; + return ret; + } + + extensions = gnutls_malloc(extensions_size); + if (extensions == NULL) { + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; + } + + ret = + gnutls_x509_crq_get_attribute_by_oid(crq, + "1.2.840.113549.1.9.14", + 0, extensions, + &extensions_size); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + result = + asn1_create_element(_gnutls_get_pkix(), "PKIX1.Extensions", + &c2); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(result); + goto cleanup; + } + + result = _asn1_strict_der_decode(&c2, extensions, extensions_size, NULL); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(result); + goto cleanup; + } + + snprintf(name, sizeof(name), "?%u.extnValue", indx + 1); + + ret = _gnutls_x509_read_value(c2, name, data); + if (ret == GNUTLS_E_ASN1_ELEMENT_NOT_FOUND) { + ret = GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + goto cleanup; + } else if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = 0; + cleanup: + asn1_delete_structure(&c2); + gnutls_free(extensions); + return ret; +} + +/** + * gnutls_x509_crq_get_key_usage: + * @crq: should contain a #gnutls_x509_crq_t type + * @key_usage: where the key usage bits will be stored + * @critical: will be non-zero if the extension is marked as critical + * + * This function will return certificate's key usage, by reading the + * keyUsage X.509 extension (2.5.29.15). The key usage value will + * ORed values of the: %GNUTLS_KEY_DIGITAL_SIGNATURE, + * %GNUTLS_KEY_NON_REPUDIATION, %GNUTLS_KEY_KEY_ENCIPHERMENT, + * %GNUTLS_KEY_DATA_ENCIPHERMENT, %GNUTLS_KEY_KEY_AGREEMENT, + * %GNUTLS_KEY_KEY_CERT_SIGN, %GNUTLS_KEY_CRL_SIGN, + * %GNUTLS_KEY_ENCIPHER_ONLY, %GNUTLS_KEY_DECIPHER_ONLY. + * + * Returns: the certificate key usage, or a negative error code in case of + * parsing error. If the certificate does not contain the keyUsage + * extension %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE will be + * returned. + * + * Since: 2.8.0 + **/ +int +gnutls_x509_crq_get_key_usage(gnutls_x509_crq_t crq, + unsigned int *key_usage, + unsigned int *critical) +{ + int result; + uint8_t buf[128]; + size_t buf_size = sizeof(buf); + gnutls_datum_t bd; + + if (crq == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + result = gnutls_x509_crq_get_extension_by_oid(crq, "2.5.29.15", 0, + buf, &buf_size, + critical); + if (result < 0) { + gnutls_assert(); + return result; + } + + bd.data = buf; + bd.size = buf_size; + result = gnutls_x509_ext_import_key_usage(&bd, key_usage); + if (result < 0) { + gnutls_assert(); + return result; + } + + return 0; +} + +/** + * gnutls_x509_crq_get_basic_constraints: + * @crq: should contain a #gnutls_x509_crq_t type + * @critical: will be non-zero if the extension is marked as critical + * @ca: pointer to output integer indicating CA status, may be NULL, + * value is 1 if the certificate CA flag is set, 0 otherwise. + * @pathlen: pointer to output integer indicating path length (may be + * NULL), non-negative error codes indicate a present pathLenConstraint + * field and the actual value, -1 indicate that the field is absent. + * + * This function will read the certificate's basic constraints, and + * return the certificates CA status. It reads the basicConstraints + * X.509 extension (2.5.29.19). + * + * Returns: If the certificate is a CA a positive value will be + * returned, or (0) if the certificate does not have CA flag set. + * A negative error code may be returned in case of errors. If the + * certificate does not contain the basicConstraints extension + * %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE will be returned. + * + * Since: 2.8.0 + **/ +int +gnutls_x509_crq_get_basic_constraints(gnutls_x509_crq_t crq, + unsigned int *critical, + unsigned int *ca, int *pathlen) +{ + int result; + unsigned int tmp_ca; + uint8_t buf[256]; + size_t buf_size = sizeof(buf); + gnutls_datum_t bd; + + if (crq == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + result = gnutls_x509_crq_get_extension_by_oid(crq, "2.5.29.19", 0, + buf, &buf_size, + critical); + if (result < 0) { + gnutls_assert(); + return result; + } + + bd.data = buf; + bd.size = buf_size; + result = gnutls_x509_ext_import_basic_constraints(&bd, &tmp_ca, pathlen); + if (ca) + *ca = tmp_ca; + + if (result < 0) { + gnutls_assert(); + return result; + } + + return tmp_ca; +} + +static int +get_subject_alt_name(gnutls_x509_crq_t crq, + unsigned int seq, void *ret, + size_t * ret_size, unsigned int *ret_type, + unsigned int *critical, int othername_oid) +{ + int result; + asn1_node c2 = NULL; + gnutls_x509_subject_alt_name_t type; + gnutls_datum_t dnsname = { NULL, 0 }; + size_t dns_size = 0; + + if (crq == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + if (ret) + memset(ret, 0, *ret_size); + else + *ret_size = 0; + + /* Extract extension. + */ + result = gnutls_x509_crq_get_extension_by_oid(crq, "2.5.29.17", 0, + NULL, &dns_size, + critical); + if (result < 0) { + gnutls_assert(); + return result; + } + + dnsname.size = dns_size; + dnsname.data = gnutls_malloc(dnsname.size); + if (dnsname.data == NULL) { + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; + } + + result = gnutls_x509_crq_get_extension_by_oid(crq, "2.5.29.17", 0, + dnsname.data, + &dns_size, critical); + if (result < 0) { + gnutls_assert(); + gnutls_free(dnsname.data); + return result; + } + + result = asn1_create_element + (_gnutls_get_pkix(), "PKIX1.SubjectAltName", &c2); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + gnutls_free(dnsname.data); + return _gnutls_asn2err(result); + } + + result = _asn1_strict_der_decode(&c2, dnsname.data, dnsname.size, NULL); + gnutls_free(dnsname.data); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + asn1_delete_structure(&c2); + return _gnutls_asn2err(result); + } + + result = _gnutls_parse_general_name(c2, "", seq, ret, ret_size, + ret_type, othername_oid); + asn1_delete_structure(&c2); + if (result < 0) { + return result; + } + + type = result; + + return type; +} + +/** + * gnutls_x509_crq_get_subject_alt_name: + * @crq: should contain a #gnutls_x509_crq_t type + * @seq: specifies the sequence number of the alt name, 0 for the + * first one, 1 for the second etc. + * @ret: is the place where the alternative name will be copied to + * @ret_size: holds the size of ret. + * @ret_type: holds the #gnutls_x509_subject_alt_name_t name type + * @critical: will be non-zero if the extension is marked as critical + * (may be null) + * + * This function will return the alternative names, contained in the + * given certificate. It is the same as + * gnutls_x509_crq_get_subject_alt_name() except for the fact that it + * will return the type of the alternative name in @ret_type even if + * the function fails for some reason (i.e. the buffer provided is + * not enough). + * + * Returns: the alternative subject name type on success, one of the + * enumerated #gnutls_x509_subject_alt_name_t. It will return + * %GNUTLS_E_SHORT_MEMORY_BUFFER if @ret_size is not large enough to + * hold the value. In that case @ret_size will be updated with the + * required size. If the certificate request does not have an + * Alternative name with the specified sequence number then + * %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE is returned. + * + * Since: 2.8.0 + **/ +int +gnutls_x509_crq_get_subject_alt_name(gnutls_x509_crq_t crq, + unsigned int seq, void *ret, + size_t * ret_size, + unsigned int *ret_type, + unsigned int *critical) +{ + return get_subject_alt_name(crq, seq, ret, ret_size, ret_type, + critical, 0); +} + +/** + * gnutls_x509_crq_get_subject_alt_othername_oid: + * @crq: should contain a #gnutls_x509_crq_t type + * @seq: specifies the sequence number of the alt name (0 for the first one, 1 for the second etc.) + * @ret: is the place where the otherName OID will be copied to + * @ret_size: holds the size of ret. + * + * This function will extract the type OID of an otherName Subject + * Alternative Name, contained in the given certificate, and return + * the type as an enumerated element. + * + * This function is only useful if + * gnutls_x509_crq_get_subject_alt_name() returned + * %GNUTLS_SAN_OTHERNAME. + * + * Returns: the alternative subject name type on success, one of the + * enumerated gnutls_x509_subject_alt_name_t. For supported OIDs, + * it will return one of the virtual (GNUTLS_SAN_OTHERNAME_*) types, + * e.g. %GNUTLS_SAN_OTHERNAME_XMPP, and %GNUTLS_SAN_OTHERNAME for + * unknown OIDs. It will return %GNUTLS_E_SHORT_MEMORY_BUFFER if + * @ret_size is not large enough to hold the value. In that case + * @ret_size will be updated with the required size. If the + * certificate does not have an Alternative name with the specified + * sequence number and with the otherName type then + * %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE is returned. + * + * Since: 2.8.0 + **/ +int +gnutls_x509_crq_get_subject_alt_othername_oid(gnutls_x509_crq_t crq, + unsigned int seq, + void *ret, size_t * ret_size) +{ + return get_subject_alt_name(crq, seq, ret, ret_size, NULL, NULL, + 1); +} + +/** + * gnutls_x509_crq_get_extension_by_oid: + * @crq: should contain a #gnutls_x509_crq_t type + * @oid: holds an Object Identifier in a null terminated string + * @indx: In case multiple same OIDs exist in the extensions, this + * specifies which to get. Use (0) to get the first one. + * @buf: a pointer to a structure to hold the name (may be null) + * @buf_size: initially holds the size of @buf + * @critical: will be non-zero if the extension is marked as critical + * + * This function will return the extension specified by the OID in + * the certificate. The extensions will be returned as binary data + * DER encoded, in the provided buffer. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error code in case of an error. If the certificate does not + * contain the specified extension + * %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE will be returned. + * + * Since: 2.8.0 + **/ +int +gnutls_x509_crq_get_extension_by_oid(gnutls_x509_crq_t crq, + const char *oid, unsigned indx, + void *buf, size_t * buf_size, + unsigned int *critical) +{ + int result; + unsigned int i; + char _oid[MAX_OID_SIZE]; + size_t oid_size; + + for (i = 0;; i++) { + oid_size = sizeof(_oid); + result = + gnutls_x509_crq_get_extension_info(crq, i, _oid, + &oid_size, + critical); + if (result < 0) { + gnutls_assert(); + return result; + } + + if (strcmp(oid, _oid) == 0) { /* found */ + if (indx == 0) + return + gnutls_x509_crq_get_extension_data(crq, + i, + buf, + buf_size); + else + indx--; + } + } + + + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + +} + +/** + * gnutls_x509_crq_get_extension_by_oid2: + * @crq: should contain a #gnutls_x509_crq_t type + * @oid: holds an Object Identifier in a null terminated string + * @indx: In case multiple same OIDs exist in the extensions, this + * specifies which to get. Use (0) to get the first one. + * @output: will hold the allocated extension data + * @critical: will be non-zero if the extension is marked as critical + * + * This function will return the extension specified by the OID in + * the certificate. The extensions will be returned as binary data + * DER encoded, in the provided buffer. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error code in case of an error. If the certificate does not + * contain the specified extension + * %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE will be returned. + * + * Since: 3.3.8 + **/ +int +gnutls_x509_crq_get_extension_by_oid2(gnutls_x509_crq_t crq, + const char *oid, unsigned indx, + gnutls_datum_t *output, + unsigned int *critical) +{ + int result; + unsigned int i; + char _oid[MAX_OID_SIZE]; + size_t oid_size; + + for (i = 0;; i++) { + oid_size = sizeof(_oid); + result = + gnutls_x509_crq_get_extension_info(crq, i, _oid, + &oid_size, + critical); + if (result < 0) { + gnutls_assert(); + return result; + } + + if (strcmp(oid, _oid) == 0) { /* found */ + if (indx == 0) + return + gnutls_x509_crq_get_extension_data2(crq, + i, + output); + else + indx--; + } + } + + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + +} + +/** + * gnutls_x509_crq_set_subject_alt_name: + * @crq: a certificate request of type #gnutls_x509_crq_t + * @nt: is one of the #gnutls_x509_subject_alt_name_t enumerations + * @data: The data to be set + * @data_size: The size of data to be set + * @flags: %GNUTLS_FSAN_SET to clear previous data or + * %GNUTLS_FSAN_APPEND to append. + * + * This function will set the subject alternative name certificate + * extension. It can set the following types: + * + * %GNUTLS_SAN_DNSNAME: as a text string + * + * %GNUTLS_SAN_RFC822NAME: as a text string + * + * %GNUTLS_SAN_URI: as a text string + * + * %GNUTLS_SAN_IPADDRESS: as a binary IP address (4 or 16 bytes) + * + * %GNUTLS_SAN_OTHERNAME_XMPP: as a UTF8 string + * + * Since version 3.5.7 the %GNUTLS_SAN_RFC822NAME, %GNUTLS_SAN_DNSNAME, and + * %GNUTLS_SAN_OTHERNAME_XMPP are converted to ACE format when necessary. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 2.8.0 + **/ +int +gnutls_x509_crq_set_subject_alt_name(gnutls_x509_crq_t crq, + gnutls_x509_subject_alt_name_t nt, + const void *data, + unsigned int data_size, + unsigned int flags) +{ + int result = 0; + gnutls_datum_t der_data = { NULL, 0 }; + gnutls_datum_t prev_der_data = { NULL, 0 }; + unsigned int critical = 0; + size_t prev_data_size = 0; + + if (crq == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + /* Check if the extension already exists. + */ + if (flags & GNUTLS_FSAN_APPEND) { + result = + gnutls_x509_crq_get_extension_by_oid(crq, "2.5.29.17", + 0, NULL, + &prev_data_size, + &critical); + prev_der_data.size = prev_data_size; + + switch (result) { + case GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE: + /* Replacing non-existing data means the same as set data. */ + break; + + case GNUTLS_E_SUCCESS: + prev_der_data.data = + gnutls_malloc(prev_der_data.size); + if (prev_der_data.data == NULL) { + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; + } + + result = + gnutls_x509_crq_get_extension_by_oid(crq, + "2.5.29.17", + 0, + prev_der_data. + data, + &prev_data_size, + &critical); + if (result < 0) { + gnutls_assert(); + gnutls_free(prev_der_data.data); + return result; + } + break; + + default: + gnutls_assert(); + return result; + } + } + + /* generate the extension. + */ + result = _gnutls_x509_ext_gen_subject_alt_name(nt, NULL, data, data_size, + &prev_der_data, + &der_data); + gnutls_free(prev_der_data.data); + if (result < 0) { + gnutls_assert(); + goto finish; + } + + result = + _gnutls_x509_crq_set_extension(crq, "2.5.29.17", &der_data, + critical); + + _gnutls_free_datum(&der_data); + + if (result < 0) { + gnutls_assert(); + return result; + } + + return 0; + + finish: + return result; +} + +/** + * gnutls_x509_crq_set_subject_alt_othername: + * @crq: a certificate request of type #gnutls_x509_crq_t + * @oid: is the othername OID + * @data: The data to be set + * @data_size: The size of data to be set + * @flags: %GNUTLS_FSAN_SET to clear previous data or + * %GNUTLS_FSAN_APPEND to append. + * + * This function will set the subject alternative name certificate + * extension. It can set the following types: + * + * The values set must be binary values and must be properly DER encoded. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 3.5.0 + **/ +int +gnutls_x509_crq_set_subject_alt_othername(gnutls_x509_crq_t crq, + const char *oid, + const void *data, + unsigned int data_size, + unsigned int flags) +{ + int result = 0; + gnutls_datum_t der_data = { NULL, 0 }; + gnutls_datum_t encoded_data = { NULL, 0 }; + gnutls_datum_t prev_der_data = { NULL, 0 }; + unsigned int critical = 0; + size_t prev_data_size = 0; + + if (crq == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + /* Check if the extension already exists. + */ + if (flags & GNUTLS_FSAN_APPEND) { + result = + gnutls_x509_crq_get_extension_by_oid(crq, "2.5.29.17", + 0, NULL, + &prev_data_size, + &critical); + prev_der_data.size = prev_data_size; + + switch (result) { + case GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE: + /* Replacing non-existing data means the same as set data. */ + break; + + case GNUTLS_E_SUCCESS: + prev_der_data.data = + gnutls_malloc(prev_der_data.size); + if (prev_der_data.data == NULL) { + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; + } + + result = + gnutls_x509_crq_get_extension_by_oid(crq, + "2.5.29.17", + 0, + prev_der_data. + data, + &prev_data_size, + &critical); + if (result < 0) { + gnutls_assert(); + goto finish; + } + break; + + default: + gnutls_assert(); + return result; + } + } + + result = _gnutls_encode_othername_data(flags, data, data_size, &encoded_data); + if (result < 0) { + gnutls_assert(); + goto finish; + } + + /* generate the extension. + */ + result = _gnutls_x509_ext_gen_subject_alt_name(GNUTLS_SAN_OTHERNAME, oid, + encoded_data.data, encoded_data.size, + &prev_der_data, + &der_data); + if (result < 0) { + gnutls_assert(); + goto finish; + } + + result = + _gnutls_x509_crq_set_extension(crq, "2.5.29.17", &der_data, + critical); + + if (result < 0) { + gnutls_assert(); + goto finish; + } + + result = 0; + + finish: + _gnutls_free_datum(&prev_der_data); + _gnutls_free_datum(&der_data); + _gnutls_free_datum(&encoded_data); + return result; +} + +/** + * gnutls_x509_crq_set_basic_constraints: + * @crq: a certificate request of type #gnutls_x509_crq_t + * @ca: true(1) or false(0) depending on the Certificate authority status. + * @pathLenConstraint: non-negative error codes indicate maximum length of path, + * and negative error codes indicate that the pathLenConstraints field should + * not be present. + * + * This function will set the basicConstraints certificate extension. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 2.8.0 + **/ +int +gnutls_x509_crq_set_basic_constraints(gnutls_x509_crq_t crq, + unsigned int ca, + int pathLenConstraint) +{ + int result; + gnutls_datum_t der_data; + + if (crq == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + /* generate the extension. + */ + result = gnutls_x509_ext_export_basic_constraints(ca, pathLenConstraint, &der_data); + if (result < 0) { + gnutls_assert(); + return result; + } + + result = + _gnutls_x509_crq_set_extension(crq, "2.5.29.19", &der_data, 1); + + _gnutls_free_datum(&der_data); + + if (result < 0) { + gnutls_assert(); + return result; + } + + return 0; +} + +/** + * gnutls_x509_crq_set_key_usage: + * @crq: a certificate request of type #gnutls_x509_crq_t + * @usage: an ORed sequence of the GNUTLS_KEY_* elements. + * + * This function will set the keyUsage certificate extension. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 2.8.0 + **/ +int +gnutls_x509_crq_set_key_usage(gnutls_x509_crq_t crq, unsigned int usage) +{ + int result; + gnutls_datum_t der_data; + + if (crq == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + /* generate the extension. + */ + result = + gnutls_x509_ext_export_key_usage(usage, &der_data); + if (result < 0) { + gnutls_assert(); + return result; + } + + result = + _gnutls_x509_crq_set_extension(crq, "2.5.29.15", &der_data, 1); + + _gnutls_free_datum(&der_data); + + if (result < 0) { + gnutls_assert(); + return result; + } + + return 0; +} + +/** + * gnutls_x509_crq_get_key_purpose_oid: + * @crq: should contain a #gnutls_x509_crq_t type + * @indx: This specifies which OID to return, use (0) to get the first one + * @oid: a pointer to store the OID (may be %NULL) + * @sizeof_oid: initially holds the size of @oid + * @critical: output variable with critical flag, may be %NULL. + * + * This function will extract the key purpose OIDs of the Certificate + * specified by the given index. These are stored in the Extended Key + * Usage extension (2.5.29.37). See the GNUTLS_KP_* definitions for + * human readable names. + * + * Returns: %GNUTLS_E_SHORT_MEMORY_BUFFER if the provided buffer is + * not long enough, and in that case the *@sizeof_oid will be + * updated with the required size. On success 0 is returned. + * + * Since: 2.8.0 + **/ +int +gnutls_x509_crq_get_key_purpose_oid(gnutls_x509_crq_t crq, + unsigned indx, void *oid, + size_t * sizeof_oid, + unsigned int *critical) +{ + char tmpstr[MAX_NAME_SIZE]; + int result, len; + gnutls_datum_t prev = { NULL, 0 }; + asn1_node c2 = NULL; + size_t prev_size = 0; + + if (oid) + memset(oid, 0, *sizeof_oid); + else + *sizeof_oid = 0; + + /* Extract extension. + */ + result = gnutls_x509_crq_get_extension_by_oid(crq, "2.5.29.37", 0, + NULL, &prev_size, + critical); + prev.size = prev_size; + + if (result < 0) { + gnutls_assert(); + return result; + } + + prev.data = gnutls_malloc(prev.size); + if (prev.data == NULL) { + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; + } + + result = gnutls_x509_crq_get_extension_by_oid(crq, "2.5.29.37", 0, + prev.data, + &prev_size, + critical); + if (result < 0) { + gnutls_assert(); + gnutls_free(prev.data); + return result; + } + + result = asn1_create_element + (_gnutls_get_pkix(), "PKIX1.ExtKeyUsageSyntax", &c2); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + gnutls_free(prev.data); + return _gnutls_asn2err(result); + } + + result = _asn1_strict_der_decode(&c2, prev.data, prev.size, NULL); + gnutls_free(prev.data); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + asn1_delete_structure(&c2); + return _gnutls_asn2err(result); + } + + indx++; + /* create a string like "?1" + */ + snprintf(tmpstr, sizeof(tmpstr), "?%u", indx); + + len = *sizeof_oid; + result = asn1_read_value(c2, tmpstr, oid, &len); + + *sizeof_oid = len; + asn1_delete_structure(&c2); + + if (result == ASN1_VALUE_NOT_FOUND + || result == ASN1_ELEMENT_NOT_FOUND) { + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + } + + if (result != ASN1_SUCCESS) { + if (result != ASN1_MEM_ERROR) + gnutls_assert(); + return _gnutls_asn2err(result); + } + + return 0; +} + +/** + * gnutls_x509_crq_set_key_purpose_oid: + * @crq: a certificate of type #gnutls_x509_crq_t + * @oid: a pointer to a null-terminated string that holds the OID + * @critical: Whether this extension will be critical or not + * + * This function will set the key purpose OIDs of the Certificate. + * These are stored in the Extended Key Usage extension (2.5.29.37) + * See the GNUTLS_KP_* definitions for human readable names. + * + * Subsequent calls to this function will append OIDs to the OID list. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 2.8.0 + **/ +int +gnutls_x509_crq_set_key_purpose_oid(gnutls_x509_crq_t crq, + const void *oid, unsigned int critical) +{ + int result; + gnutls_datum_t prev = { NULL, 0 }, der_data; + asn1_node c2 = NULL; + size_t prev_size = 0; + + /* Read existing extension, if there is one. + */ + result = gnutls_x509_crq_get_extension_by_oid(crq, "2.5.29.37", 0, + NULL, &prev_size, + &critical); + prev.size = prev_size; + + switch (result) { + case GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE: + /* No existing extension, that's fine. */ + break; + + case GNUTLS_E_SUCCESS: + prev.data = gnutls_malloc(prev.size); + if (prev.data == NULL) { + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; + } + + result = + gnutls_x509_crq_get_extension_by_oid(crq, "2.5.29.37", + 0, prev.data, + &prev_size, + &critical); + if (result < 0) { + gnutls_assert(); + gnutls_free(prev.data); + return result; + } + break; + + default: + gnutls_assert(); + return result; + } + + result = asn1_create_element(_gnutls_get_pkix(), + "PKIX1.ExtKeyUsageSyntax", &c2); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + gnutls_free(prev.data); + return _gnutls_asn2err(result); + } + + if (prev.data) { + /* decode it. + */ + result = + _asn1_strict_der_decode(&c2, prev.data, prev.size, NULL); + gnutls_free(prev.data); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + asn1_delete_structure(&c2); + return _gnutls_asn2err(result); + } + } + + /* generate the extension. + */ + /* 1. create a new element. + */ + result = asn1_write_value(c2, "", "NEW", 1); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + asn1_delete_structure(&c2); + return _gnutls_asn2err(result); + } + + /* 2. Add the OID. + */ + result = asn1_write_value(c2, "?LAST", oid, 1); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + asn1_delete_structure(&c2); + return _gnutls_asn2err(result); + } + + result = _gnutls_x509_der_encode(c2, "", &der_data, 0); + asn1_delete_structure(&c2); + + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + result = _gnutls_x509_crq_set_extension(crq, "2.5.29.37", + &der_data, critical); + _gnutls_free_datum(&der_data); + if (result < 0) { + gnutls_assert(); + return result; + } + + return 0; +} + +/** + * gnutls_x509_crq_get_key_id: + * @crq: a certificate of type #gnutls_x509_crq_t + * @flags: should be one of the flags from %gnutls_keyid_flags_t + * @output_data: will contain the key ID + * @output_data_size: holds the size of output_data (and will be + * replaced by the actual size of parameters) + * + * This function will return a unique ID that depends on the public key + * parameters. This ID can be used in checking whether a certificate + * corresponds to the given private key. + * + * If the buffer provided is not long enough to hold the output, then + * *@output_data_size is updated and GNUTLS_E_SHORT_MEMORY_BUFFER will + * be returned. The output will normally be a SHA-1 hash output, + * which is 20 bytes. + * + * Returns: In case of failure a negative error code will be + * returned, and 0 on success. + * + * Since: 2.8.0 + **/ +int +gnutls_x509_crq_get_key_id(gnutls_x509_crq_t crq, unsigned int flags, + unsigned char *output_data, + size_t * output_data_size) +{ + int ret = 0; + gnutls_pk_params_st params; + + if (crq == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + ret = _gnutls_x509_crq_get_mpis(crq, ¶ms); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + ret = + _gnutls_get_key_id(¶ms, output_data, output_data_size, flags); + + gnutls_pk_params_release(¶ms); + + return ret; +} + +/** + * gnutls_x509_crq_privkey_sign: + * @crq: should contain a #gnutls_x509_crq_t type + * @key: holds a private key + * @dig: The message digest to use, i.e., %GNUTLS_DIG_SHA1 + * @flags: must be 0 + * + * This function will sign the certificate request with a private key. + * This must be the same key as the one used in + * gnutls_x509_crt_set_key() since a certificate request is self + * signed. + * + * This must be the last step in a certificate request generation + * since all the previously set parameters are now signed. + * + * A known limitation of this function is, that a newly-signed request will not + * be fully functional (e.g., for signature verification), until it + * is exported an re-imported. + * + * After GnuTLS 3.6.1 the value of @dig may be %GNUTLS_DIG_UNKNOWN, + * and in that case, a suitable but reasonable for the key algorithm will be selected. + * + * Returns: %GNUTLS_E_SUCCESS on success, otherwise a negative error code. + * %GNUTLS_E_ASN1_VALUE_NOT_FOUND is returned if you didn't set all + * information in the certificate request (e.g., the version using + * gnutls_x509_crq_set_version()). + * + * Since: 2.12.0 + **/ +int +gnutls_x509_crq_privkey_sign(gnutls_x509_crq_t crq, gnutls_privkey_t key, + gnutls_digest_algorithm_t dig, + unsigned int flags) +{ + int result; + gnutls_datum_t signature; + gnutls_datum_t tbs; + gnutls_pk_algorithm_t pk; + gnutls_x509_spki_st params; + const gnutls_sign_entry_st *se; + + if (crq == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + /* Make sure version field is set. */ + if (gnutls_x509_crq_get_version(crq) == + GNUTLS_E_ASN1_VALUE_NOT_FOUND) { + result = gnutls_x509_crq_set_version(crq, 1); + if (result < 0) { + gnutls_assert(); + return result; + } + } + + if (dig == 0) { + /* attempt to find a reasonable choice */ + gnutls_pubkey_t pubkey; + int ret; + + ret = gnutls_pubkey_init(&pubkey); + if (ret < 0) + return gnutls_assert_val(ret); + + ret = gnutls_pubkey_import_privkey(pubkey, key, 0, 0); + if (ret < 0) { + gnutls_pubkey_deinit(pubkey); + return gnutls_assert_val(ret); + } + ret = gnutls_pubkey_get_preferred_hash_algorithm(pubkey, &dig, NULL); + gnutls_pubkey_deinit(pubkey); + + if (ret < 0) + return gnutls_assert_val(ret); + } + + result = _gnutls_privkey_get_spki_params(key, ¶ms); + if (result < 0) { + gnutls_assert(); + return result; + } + + pk = gnutls_privkey_get_pk_algorithm(key, NULL); + result = _gnutls_privkey_update_spki_params(key, pk, dig, 0, ¶ms); + if (result < 0) { + gnutls_assert(); + return result; + } + + /* Step 1. Self sign the request. + */ + result = + _gnutls_x509_get_tbs(crq->crq, "certificationRequestInfo", + &tbs); + + if (result < 0) { + gnutls_assert(); + return result; + } + + se = _gnutls_pk_to_sign_entry(params.pk, dig); + if (se == NULL) + return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + + FIX_SIGN_PARAMS(params, flags, dig); + + result = privkey_sign_and_hash_data(key, se, + &tbs, &signature, ¶ms); + gnutls_free(tbs.data); + + if (result < 0) { + gnutls_assert(); + return result; + } + + /* Step 2. write the signature (bits) + */ + result = + asn1_write_value(crq->crq, "signature", signature.data, + signature.size * 8); + + _gnutls_free_datum(&signature); + + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + /* Step 3. Write the signatureAlgorithm field. + */ + result = + _gnutls_x509_write_sign_params(crq->crq, "signatureAlgorithm", + se, ¶ms); + if (result < 0) { + gnutls_assert(); + return result; + } + + return 0; +} + + +/** + * gnutls_x509_crq_verify: + * @crq: is the crq to be verified + * @flags: Flags that may be used to change the verification algorithm. Use OR of the gnutls_certificate_verify_flags enumerations. + * + * This function will verify self signature in the certificate + * request and return its status. + * + * Returns: In case of a verification failure %GNUTLS_E_PK_SIG_VERIFY_FAILED + * is returned, and zero or positive code on success. + * + * Since 2.12.0 + **/ +int gnutls_x509_crq_verify(gnutls_x509_crq_t crq, unsigned int flags) +{ + gnutls_datum_t data = { NULL, 0 }; + gnutls_datum_t signature = { NULL, 0 }; + gnutls_pk_params_st params; + gnutls_x509_spki_st sign_params; + const gnutls_sign_entry_st *se; + int ret; + + gnutls_pk_params_init(¶ms); + + ret = + _gnutls_x509_get_signed_data(crq->crq, NULL, + "certificationRequestInfo", + &data); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + ret = + _gnutls_x509_get_signature_algorithm(crq->crq, + "signatureAlgorithm"); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + se = _gnutls_sign_to_entry(ret); + if (se == NULL) { + gnutls_assert(); + ret = GNUTLS_E_UNSUPPORTED_SIGNATURE_ALGORITHM; + goto cleanup; + } + + ret = + _gnutls_x509_get_signature(crq->crq, "signature", &signature); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = _gnutls_x509_crq_get_mpis(crq, ¶ms); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = _gnutls_x509_read_sign_params(crq->crq, + "signatureAlgorithm", + &sign_params); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = + pubkey_verify_data(se, hash_to_entry(se->hash), &data, &signature, + ¶ms, &sign_params, flags); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = 0; + + cleanup: + _gnutls_free_datum(&data); + _gnutls_free_datum(&signature); + gnutls_pk_params_release(¶ms); + + return ret; +} + +/** + * gnutls_x509_crq_set_private_key_usage_period: + * @crq: a certificate of type #gnutls_x509_crq_t + * @activation: The activation time + * @expiration: The expiration time + * + * This function will set the private key usage period extension (2.5.29.16). + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int +gnutls_x509_crq_set_private_key_usage_period(gnutls_x509_crq_t crq, + time_t activation, + time_t expiration) +{ + int result; + gnutls_datum_t der_data; + asn1_node c2 = NULL; + + if (crq == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + result = + asn1_create_element(_gnutls_get_pkix(), + "PKIX1.PrivateKeyUsagePeriod", &c2); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + result = _gnutls_x509_set_time(c2, "notBefore", activation, 1); + if (result < 0) { + gnutls_assert(); + goto cleanup; + } + + result = _gnutls_x509_set_time(c2, "notAfter", expiration, 1); + if (result < 0) { + gnutls_assert(); + goto cleanup; + } + + result = _gnutls_x509_der_encode(c2, "", &der_data, 0); + if (result < 0) { + gnutls_assert(); + goto cleanup; + } + + result = _gnutls_x509_crq_set_extension(crq, "2.5.29.16", + &der_data, 0); + + _gnutls_free_datum(&der_data); + + cleanup: + asn1_delete_structure(&c2); + + return result; +} + +/** + * gnutls_x509_crq_get_tlsfeatures: + * @crq: An X.509 certificate request + * @features: If the function succeeds, the + * features will be stored in this variable. + * @flags: zero or %GNUTLS_EXT_FLAG_APPEND + * @critical: the extension status + * + * This function will get the X.509 TLS features + * extension structure from the certificate request. + * The returned structure needs to be freed using + * gnutls_x509_tlsfeatures_deinit(). + * + * When the @flags is set to %GNUTLS_EXT_FLAG_APPEND, + * then if the @features structure is empty this function will behave + * identically as if the flag was not set. Otherwise if there are elements + * in the @features structure then they will be merged with. + * + * Note that @features must be initialized prior to calling this function. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, + * otherwise a negative error value. + * + * Since: 3.5.1 + **/ +int gnutls_x509_crq_get_tlsfeatures(gnutls_x509_crq_t crq, + gnutls_x509_tlsfeatures_t features, + unsigned int flags, + unsigned int *critical) +{ + int ret; + gnutls_datum_t der = {NULL, 0}; + + if (crq == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + if ((ret = + gnutls_x509_crq_get_extension_by_oid2(crq, GNUTLS_X509EXT_OID_TLSFEATURES, 0, + &der, critical)) < 0) + { + return ret; + } + + if (der.size == 0 || der.data == NULL) { + gnutls_assert(); + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + } + + ret = gnutls_x509_ext_import_tlsfeatures(&der, features, flags); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = 0; + cleanup: + gnutls_free(der.data); + return ret; +} + +/** + * gnutls_x509_crq_set_tlsfeatures: + * @crq: An X.509 certificate request + * @features: If the function succeeds, the + * features will be added to the certificate + * request. + * + * This function will set the certificate request's + * X.509 TLS extension from the given structure. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, + * otherwise a negative error value. + * + * Since: 3.5.1 + **/ +int gnutls_x509_crq_set_tlsfeatures(gnutls_x509_crq_t crq, + gnutls_x509_tlsfeatures_t features) +{ + int ret; + gnutls_datum_t der; + + if (crq == NULL || features == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + ret = gnutls_x509_ext_export_tlsfeatures(features, &der); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + ret = _gnutls_x509_crq_set_extension(crq, GNUTLS_X509EXT_OID_TLSFEATURES, &der, 0); + + _gnutls_free_datum(&der); + + if (ret < 0) { + gnutls_assert(); + } + + return ret; +} + +/** + * gnutls_x509_crq_set_extension_by_oid: + * @crq: a certificate of type #gnutls_x509_crq_t + * @oid: holds an Object Identifier in null terminated string + * @buf: a pointer to a DER encoded data + * @sizeof_buf: holds the size of @buf + * @critical: should be non-zero if the extension is to be marked as critical + * + * This function will set an the extension, by the specified OID, in + * the certificate request. The extension data should be binary data DER + * encoded. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int +gnutls_x509_crq_set_extension_by_oid(gnutls_x509_crq_t crq, + const char *oid, const void *buf, + size_t sizeof_buf, + unsigned int critical) +{ + int result; + gnutls_datum_t der_data; + + der_data.data = (void *) buf; + der_data.size = sizeof_buf; + + if (crq == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + result = + _gnutls_x509_crq_set_extension(crq, oid, &der_data, critical); + if (result < 0) { + gnutls_assert(); + return result; + } + + return 0; + +} + +/** + * gnutls_x509_crq_set_spki: + * @crq: a certificate request of type #gnutls_x509_crq_t + * @spki: a SubjectPublicKeyInfo structure of type #gnutls_x509_spki_t + * @flags: must be zero + * + * This function will set the certificate request's subject public key + * information explicitly. This is intended to be used in the cases + * where a single public key (e.g., RSA) can be used for multiple + * signature algorithms (RSA PKCS1-1.5, and RSA-PSS). + * + * To export the public key (i.e., the SubjectPublicKeyInfo part), check + * gnutls_pubkey_import_x509(). + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 3.6.0 + **/ +int +gnutls_x509_crq_set_spki(gnutls_x509_crq_t crq, + const gnutls_x509_spki_t spki, + unsigned int flags) +{ + int ret; + gnutls_pk_algorithm_t crq_pk; + gnutls_x509_spki_st tpki; + gnutls_pk_params_st params; + unsigned bits; + + if (crq == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + ret = _gnutls_x509_crq_get_mpis(crq, ¶ms); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + bits = pubkey_to_bits(¶ms); + crq_pk = params.algo; + + if (!_gnutls_pk_are_compat(crq_pk, spki->pk)) { + ret = gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + goto cleanup; + } + + if (spki->pk != GNUTLS_PK_RSA_PSS) { + if (crq_pk == spki->pk) { + ret = 0; + goto cleanup; + } + + gnutls_assert(); + ret = GNUTLS_E_INVALID_REQUEST; + goto cleanup; + } + + memset(&tpki, 0, sizeof(gnutls_x509_spki_st)); + + if (crq_pk == GNUTLS_PK_RSA) { + const mac_entry_st *me; + + me = hash_to_entry(spki->rsa_pss_dig); + if (unlikely(me == NULL)) { + gnutls_assert(); + ret = GNUTLS_E_INVALID_REQUEST; + goto cleanup; + } + + tpki.pk = spki->pk; + tpki.rsa_pss_dig = spki->rsa_pss_dig; + + /* If salt size is zero, find the optimal salt size. */ + if (spki->salt_size == 0) { + ret = + _gnutls_find_rsa_pss_salt_size(bits, me, + spki->salt_size); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + tpki.salt_size = ret; + } else + tpki.salt_size = spki->salt_size; + } else if (crq_pk == GNUTLS_PK_RSA_PSS) { + ret = _gnutls_x509_crq_read_spki_params(crq, &tpki); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + tpki.salt_size = spki->salt_size; + tpki.rsa_pss_dig = spki->rsa_pss_dig; + } + + memcpy(¶ms.spki, &tpki, sizeof(tpki)); + ret = _gnutls_x509_check_pubkey_params(¶ms); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = _gnutls_x509_write_spki_params(crq->crq, + "certificationRequestInfo." + "subjectPKInfo." + "algorithm", + &tpki); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = 0; + cleanup: + gnutls_pk_params_release(¶ms); + return ret; +} diff --git a/lib/x509/dn.c b/lib/x509/dn.c new file mode 100644 index 0000000..8d89f56 --- /dev/null +++ b/lib/x509/dn.c @@ -0,0 +1,1051 @@ +/* + * Copyright (C) 2003-2014 Free Software Foundation, Inc. + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GnuTLS. + * + * The GnuTLS is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/> + * + */ + +#include "gnutls_int.h" +#include <libtasn1.h> +#include <datum.h> +#include <global.h> +#include "errors.h" +#include <str.h> +#include <common.h> +#include <num.h> + +/* This file includes all the required to parse an X.509 Distriguished + * Name (you need a parser just to read a name in the X.509 protocols!!!) + */ + +static int append_elements(asn1_node asn1_struct, const char *asn1_rdn_name, gnutls_buffer_st *str, int k1, unsigned last) +{ + int k2, result, max_k2; + int len; + uint8_t value[MAX_STRING_LEN]; + char tmpbuffer1[MAX_NAME_SIZE]; + char tmpbuffer2[MAX_NAME_SIZE]; + char tmpbuffer3[MAX_NAME_SIZE]; + const char *ldap_desc; + char oid[MAX_OID_SIZE]; + gnutls_datum_t td = { NULL, 0 }; + gnutls_datum_t tvd = { NULL, 0 }; + + /* create a string like "tbsCertList.issuer.rdnSequence.?1" + */ + if (asn1_rdn_name[0] != 0) + snprintf(tmpbuffer1, sizeof(tmpbuffer1), "%s.?%d", + asn1_rdn_name, k1); + else + snprintf(tmpbuffer1, sizeof(tmpbuffer1), "?%d", + k1); + + len = sizeof(value) - 1; + result = + asn1_read_value(asn1_struct, tmpbuffer1, value, &len); + + if (result != ASN1_VALUE_NOT_FOUND && result != ASN1_SUCCESS) { /* expected */ + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + k2 = 0; + + result = asn1_number_of_elements(asn1_struct, tmpbuffer1, &max_k2); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + do { /* Move to the attribute type and values + */ + k2++; + + if (tmpbuffer1[0] != 0) + snprintf(tmpbuffer2, sizeof(tmpbuffer2), + "%s.?%d", tmpbuffer1, k2); + else + snprintf(tmpbuffer2, sizeof(tmpbuffer2), + "?%d", k2); + + /* Try to read the RelativeDistinguishedName attributes. + */ + + len = sizeof(value) - 1; + result = + asn1_read_value(asn1_struct, tmpbuffer2, value, + &len); + + if (result == ASN1_ELEMENT_NOT_FOUND) + break; + if (result != ASN1_VALUE_NOT_FOUND && result != ASN1_SUCCESS) { /* expected */ + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + /* Read the OID + */ + _gnutls_str_cpy(tmpbuffer3, sizeof(tmpbuffer3), + tmpbuffer2); + _gnutls_str_cat(tmpbuffer3, sizeof(tmpbuffer3), + ".type"); + + len = sizeof(oid) - 1; + result = + asn1_read_value(asn1_struct, tmpbuffer3, oid, + &len); + + if (result == ASN1_ELEMENT_NOT_FOUND) + break; + else if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + /* Read the Value + */ + _gnutls_str_cpy(tmpbuffer3, sizeof(tmpbuffer3), + tmpbuffer2); + _gnutls_str_cat(tmpbuffer3, sizeof(tmpbuffer3), + ".value"); + + len = 0; + + result = + _gnutls_x509_read_value(asn1_struct, + tmpbuffer3, &tvd); + if (result < 0) { + gnutls_assert(); + goto cleanup; + } +#define STR_APPEND(y) if ((result=_gnutls_buffer_append_str( str, y)) < 0) { \ + gnutls_assert(); \ + goto cleanup; \ +} +#define DATA_APPEND(x,y) if ((result=_gnutls_buffer_append_data( str, x,y)) < 0) { \ + gnutls_assert(); \ + goto cleanup; \ +} + /* The encodings of adjoining RelativeDistinguishedNames are separated + * by a comma character (',' ASCII 44). + */ + + ldap_desc = + gnutls_x509_dn_oid_name(oid, + GNUTLS_X509_DN_OID_RETURN_OID); + + STR_APPEND(ldap_desc); + STR_APPEND("="); + + /* DirectoryString by definition in RFC 5280 cannot be empty. + * If asn_node.value_len = 0 the parser correctly rejects such DirectoryString. + * However, if asn_node.value contains ASN.1 TLV triplet with length = 0, + * such DirectoryString is not rejected by the parser as the node itself is not empty. + * Explicitly reject DirectoryString in such case. + */ + const char *asn_desc = _gnutls_oid_get_asn_desc(oid); + if (asn_desc && !strcmp(asn_desc, "PKIX1.DirectoryString") && tvd.data[1] == 0) { + gnutls_assert(); + result = GNUTLS_E_ASN1_VALUE_NOT_VALID; + _gnutls_debug_log("Empty DirectoryString\n"); + goto cleanup; + } + + result = + _gnutls_x509_dn_to_string(oid, tvd.data, + tvd.size, &td); + if (result < 0) { + gnutls_assert(); + _gnutls_debug_log + ("Cannot parse OID: '%s' with value '%s'\n", + oid, _gnutls_bin2hex(tvd.data, + tvd.size, + tmpbuffer3, + sizeof + (tmpbuffer3), + NULL)); + goto cleanup; + } + + DATA_APPEND(td.data, td.size); + _gnutls_free_datum(&td); + _gnutls_free_datum(&tvd); + + /* Where there is a multi-valued RDN, the outputs from adjoining + * AttributeTypeAndValues are separated by a plus ('+' ASCII 43) + * character. + */ + if (k2 < max_k2) { + STR_APPEND("+"); + } else if (!last) { + STR_APPEND(","); + } + } + while (1); + + result = 0; + + cleanup: + _gnutls_free_datum(&td); + _gnutls_free_datum(&tvd); + return result; +} + +int +_gnutls_x509_get_dn(asn1_node asn1_struct, + const char *asn1_rdn_name, gnutls_datum_t * dn, + unsigned flags) +{ + gnutls_buffer_st out_str; + int i, k1, result; + + _gnutls_buffer_init(&out_str); + + result = asn1_number_of_elements(asn1_struct, asn1_rdn_name, &k1); + if (result != ASN1_SUCCESS) { + if (result == ASN1_ELEMENT_NOT_FOUND || result == ASN1_VALUE_NOT_FOUND) { + result = gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE); + } else { + gnutls_assert(); + result = _gnutls_asn2err(result); + } + goto cleanup; + } + + if (k1 == 0) { + gnutls_assert(); + result = GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + goto cleanup; + } + + if (flags & GNUTLS_X509_DN_FLAG_COMPAT) { + for (i=0;i<k1;i++) { + result = append_elements(asn1_struct, asn1_rdn_name, &out_str, i+1, (i==(k1-1))?1:0); + if (result < 0) { + gnutls_assert(); + goto cleanup; + } + } + } else { + while (k1 > 0) { + result = append_elements(asn1_struct, asn1_rdn_name, &out_str, k1, k1==1?1:0); + if (result < 0) { + gnutls_assert(); + goto cleanup; + } + k1--; + } + } + + return _gnutls_buffer_to_datum(&out_str, dn, 1); + + cleanup: + _gnutls_buffer_clear(&out_str); + return result; + +} + + +/* Parses an X509 DN in the asn1_struct, and puts the output into + * the string buf. The output is an LDAP encoded DN. + * + * asn1_rdn_name must be a string in the form "tbsCertificate.issuer.rdnSequence". + * That is to point in the rndSequence. + */ +int +_gnutls_x509_parse_dn(asn1_node asn1_struct, + const char *asn1_rdn_name, char *buf, + size_t * buf_size, unsigned flags) +{ + int ret; + gnutls_datum_t dn = {NULL, 0}; + + if (buf_size == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + if (*buf_size > 0 && buf) + buf[0] = 0; + else + *buf_size = 0; + + ret = _gnutls_x509_get_dn(asn1_struct, asn1_rdn_name, &dn, flags); + if (ret < 0) + return gnutls_assert_val(ret); + + if (dn.size >= (unsigned int) *buf_size) { + gnutls_assert(); + *buf_size = dn.size + 1; + ret = GNUTLS_E_SHORT_MEMORY_BUFFER; + goto cleanup; + } + + assert(dn.data != NULL); + + if (buf) { + memcpy(buf, dn.data, dn.size); + buf[dn.size] = 0; + *buf_size = dn.size; + } else + *buf_size = dn.size + 1; + + ret = 0; + cleanup: + _gnutls_free_datum(&dn); + return ret; +} + +/* Parses an X509 DN in the asn1_struct, and searches for the + * given OID in the DN. + * + * If raw_flag == 0, the output will be encoded in the LDAP way. (#hex for non printable) + * Otherwise the raw DER data are returned. + * + * asn1_rdn_name must be a string in the form "tbsCertificate.issuer.rdnSequence". + * That is to point in the rndSequence. + * + * indx specifies which OID to return. Ie 0 means return the first specified + * OID found, 1 the second etc. + */ +int +_gnutls_x509_parse_dn_oid(asn1_node asn1_struct, + const char *asn1_rdn_name, + const char *given_oid, int indx, + unsigned int raw_flag, gnutls_datum_t * out) +{ + int k2, k1, result; + char tmpbuffer1[MAX_NAME_SIZE]; + char tmpbuffer2[MAX_NAME_SIZE]; + char tmpbuffer3[MAX_NAME_SIZE]; + gnutls_datum_t td; + uint8_t value[256]; + char oid[MAX_OID_SIZE]; + int len; + int i = 0; + + k1 = 0; + do { + + k1++; + /* create a string like "tbsCertList.issuer.rdnSequence.?1" + */ + if (asn1_rdn_name[0] != 0) + snprintf(tmpbuffer1, sizeof(tmpbuffer1), "%s.?%d", + asn1_rdn_name, k1); + else + snprintf(tmpbuffer1, sizeof(tmpbuffer1), "?%d", + k1); + + len = sizeof(value) - 1; + result = + asn1_read_value(asn1_struct, tmpbuffer1, value, &len); + + if (result == ASN1_ELEMENT_NOT_FOUND) { + gnutls_assert(); + break; + } + + if (result != ASN1_VALUE_NOT_FOUND) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + k2 = 0; + + do { /* Move to the attribute type and values + */ + k2++; + + if (tmpbuffer1[0] != 0) + snprintf(tmpbuffer2, sizeof(tmpbuffer2), + "%s.?%d", tmpbuffer1, k2); + else + snprintf(tmpbuffer2, sizeof(tmpbuffer2), + "?%d", k2); + + /* Try to read the RelativeDistinguishedName attributes. + */ + + len = sizeof(value) - 1; + result = + asn1_read_value(asn1_struct, tmpbuffer2, value, + &len); + + if (result == ASN1_ELEMENT_NOT_FOUND) { + break; + } + if (result != ASN1_VALUE_NOT_FOUND) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + /* Read the OID + */ + _gnutls_str_cpy(tmpbuffer3, sizeof(tmpbuffer3), + tmpbuffer2); + _gnutls_str_cat(tmpbuffer3, sizeof(tmpbuffer3), + ".type"); + + len = sizeof(oid) - 1; + result = + asn1_read_value(asn1_struct, tmpbuffer3, oid, + &len); + + if (result == ASN1_ELEMENT_NOT_FOUND) + break; + else if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + if (strcmp(oid, given_oid) == 0 && indx == i++) { /* Found the OID */ + + /* Read the Value + */ + _gnutls_str_cpy(tmpbuffer3, + sizeof(tmpbuffer3), + tmpbuffer2); + _gnutls_str_cat(tmpbuffer3, + sizeof(tmpbuffer3), + ".value"); + + result = + _gnutls_x509_read_value(asn1_struct, + tmpbuffer3, + &td); + if (result < 0) { + gnutls_assert(); + goto cleanup; + } + + if (raw_flag != 0) { + out->data = td.data; + out->size = td.size; + return 0; + + } else { /* parse data. raw_flag == 0 */ + result = + _gnutls_x509_dn_to_string(oid, + td. + data, + td. + size, + out); + + _gnutls_free_datum(&td); + if (result < 0) { + gnutls_assert(); + goto cleanup; + } + + return 0; + + } /* raw_flag == 0 */ + } + } + while (1); + + } + while (1); + + gnutls_assert(); + + result = GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + + cleanup: + return result; +} + + +/* Parses an X509 DN in the asn1_struct, and returns the requested + * DN OID. + * + * asn1_rdn_name must be a string in the form "tbsCertificate.issuer.rdnSequence". + * That is to point in the rndSequence. + * + * indx specifies which OID to return. Ie 0 means return the first specified + * OID found, 1 the second etc. + */ +int +_gnutls_x509_get_dn_oid(asn1_node asn1_struct, + const char *asn1_rdn_name, + int indx, void *_oid, size_t * sizeof_oid) +{ + int k2, k1, result; + char tmpbuffer1[MAX_NAME_SIZE]; + char tmpbuffer2[MAX_NAME_SIZE]; + char tmpbuffer3[MAX_NAME_SIZE]; + char value[256]; + char oid[MAX_OID_SIZE]; + int len; + int i = 0; + + k1 = 0; + do { + + k1++; + /* create a string like "tbsCertList.issuer.rdnSequence.?1" + */ + if (asn1_rdn_name[0] != 0) + snprintf(tmpbuffer1, sizeof(tmpbuffer1), "%s.?%d", + asn1_rdn_name, k1); + else + snprintf(tmpbuffer1, sizeof(tmpbuffer1), "?%d", + k1); + + len = sizeof(value) - 1; + result = + asn1_read_value(asn1_struct, tmpbuffer1, value, &len); + + if (result == ASN1_ELEMENT_NOT_FOUND) { + gnutls_assert(); + break; + } + + if (result != ASN1_VALUE_NOT_FOUND) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + k2 = 0; + + do { /* Move to the attribute type and values + */ + k2++; + + if (tmpbuffer1[0] != 0) + snprintf(tmpbuffer2, sizeof(tmpbuffer2), + "%s.?%d", tmpbuffer1, k2); + else + snprintf(tmpbuffer2, sizeof(tmpbuffer2), + "?%d", k2); + + /* Try to read the RelativeDistinguishedName attributes. + */ + + len = sizeof(value) - 1; + result = + asn1_read_value(asn1_struct, tmpbuffer2, value, + &len); + + if (result == ASN1_ELEMENT_NOT_FOUND) { + break; + } + if (result != ASN1_VALUE_NOT_FOUND) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + /* Read the OID + */ + _gnutls_str_cpy(tmpbuffer3, sizeof(tmpbuffer3), + tmpbuffer2); + _gnutls_str_cat(tmpbuffer3, sizeof(tmpbuffer3), + ".type"); + + len = sizeof(oid) - 1; + result = + asn1_read_value(asn1_struct, tmpbuffer3, oid, + &len); + + if (result == ASN1_ELEMENT_NOT_FOUND) + break; + else if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + if (indx == i++) { /* Found the OID */ + + len = strlen(oid) + 1; + + if (*sizeof_oid < (unsigned) len) { + *sizeof_oid = len; + gnutls_assert(); + return + GNUTLS_E_SHORT_MEMORY_BUFFER; + } + + memcpy(_oid, oid, len); + *sizeof_oid = len - 1; + + return 0; + } + } + while (1); + + } + while (1); + + gnutls_assert(); + + result = GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + + cleanup: + return result; +} + +/* This will write the AttributeTypeAndValue field. The data must be already DER encoded. + * 'multi' must be (0) if writing an AttributeTypeAndValue, and 1 if Attribute. + * In all cases only one value is written. + */ +static int +_gnutls_x509_write_attribute(const char *given_oid, + asn1_node asn1_struct, const char *where, + const void *_data, int sizeof_data) +{ + char tmp[128]; + int result; + + /* write the data (value) + */ + + _gnutls_str_cpy(tmp, sizeof(tmp), where); + _gnutls_str_cat(tmp, sizeof(tmp), ".value"); + + result = asn1_write_value(asn1_struct, tmp, _data, sizeof_data); + if (result < 0) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + /* write the type + */ + _gnutls_str_cpy(tmp, sizeof(tmp), where); + _gnutls_str_cat(tmp, sizeof(tmp), ".type"); + + result = asn1_write_value(asn1_struct, tmp, given_oid, 1); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + return 0; +} + + +/* Decodes an X.509 Attribute (if multi==1) or an AttributeTypeAndValue + * otherwise. + * + * octet_string should be non-zero if we are to decode octet strings after + * decoding. + * + * The output is allocated and stored in value. + */ +int +_gnutls_x509_decode_and_read_attribute(asn1_node asn1_struct, + const char *where, char *oid, + int oid_size, + gnutls_datum_t * value, int multi, + int octet_string) +{ + char tmpbuffer[128]; + int len, result; + + /* Read the OID + */ + _gnutls_str_cpy(tmpbuffer, sizeof(tmpbuffer), where); + _gnutls_str_cat(tmpbuffer, sizeof(tmpbuffer), ".type"); + + len = oid_size - 1; + result = asn1_read_value(asn1_struct, tmpbuffer, oid, &len); + + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + return result; + } + + /* Read the Value + */ + + _gnutls_str_cpy(tmpbuffer, sizeof(tmpbuffer), where); + _gnutls_str_cat(tmpbuffer, sizeof(tmpbuffer), ".value"); + + if (multi) + _gnutls_str_cat(tmpbuffer, sizeof(tmpbuffer), "s.?1"); /* .values.?1 */ + + if (octet_string) + result = + _gnutls_x509_read_string(asn1_struct, tmpbuffer, value, + ASN1_ETYPE_OCTET_STRING, 0); + else + result = + _gnutls_x509_read_value(asn1_struct, tmpbuffer, value); + if (result < 0) { + gnutls_assert(); + return result; + } + + return 0; + +} + +/* Sets an X509 DN in the asn1_struct, and puts the given OID in the DN. + * The input is assumed to be raw data. + * + * asn1_rdn_name must be a string in the form "tbsCertificate.issuer". + * That is to point before the rndSequence. + * + */ +int +_gnutls_x509_set_dn_oid(asn1_node asn1_struct, + const char *asn1_name, const char *given_oid, + int raw_flag, const char *name, int sizeof_name) +{ + int result; + char tmp[MAX_NAME_SIZE], asn1_rdn_name[MAX_NAME_SIZE]; + + if (sizeof_name == 0 || name == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + /* create the rdnSequence + */ + result = + asn1_write_value(asn1_struct, asn1_name, "rdnSequence", 1); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + if (asn1_name[0] != 0) { + _gnutls_str_cpy(asn1_rdn_name, sizeof(asn1_rdn_name), asn1_name); + _gnutls_str_cat(asn1_rdn_name, sizeof(asn1_rdn_name), + ".rdnSequence"); + } else { + _gnutls_str_cpy(asn1_rdn_name, sizeof(asn1_rdn_name), "rdnSequence"); + } + + /* create a new element + */ + result = asn1_write_value(asn1_struct, asn1_rdn_name, "NEW", 1); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + _gnutls_str_cpy(tmp, sizeof(tmp), asn1_rdn_name); + _gnutls_str_cat(tmp, sizeof(tmp), ".?LAST"); + + /* create the set with only one element + */ + result = asn1_write_value(asn1_struct, tmp, "NEW", 1); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + + /* Encode and write the data + */ + _gnutls_str_cpy(tmp, sizeof(tmp), asn1_rdn_name); + _gnutls_str_cat(tmp, sizeof(tmp), ".?LAST.?LAST"); + + if (!raw_flag) { + result = + _gnutls_x509_encode_and_write_attribute(given_oid, + asn1_struct, + tmp, name, + sizeof_name, + 0); + } else { + result = + _gnutls_x509_write_attribute(given_oid, asn1_struct, + tmp, name, sizeof_name); + } + + if (result < 0) { + gnutls_assert(); + return result; + } + + return 0; +} + + +/** + * gnutls_x509_rdn_get: + * @idn: should contain a DER encoded RDN sequence + * @buf: a pointer to a structure to hold the peer's name + * @buf_size: holds the size of @buf + * + * This function will return the name of the given RDN sequence. The + * name will be in the form "C=xxxx,O=yyyy,CN=zzzz" as described in + * RFC4514. + * + * This function does not output a fully RFC4514 compliant string, if + * that is required see gnutls_x509_rdn_get2(). + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, or + * %GNUTLS_E_SHORT_MEMORY_BUFFER is returned and *@buf_size is + * updated if the provided buffer is not long enough, otherwise a + * negative error value. + **/ +int +gnutls_x509_rdn_get(const gnutls_datum_t * idn, + char *buf, size_t * buf_size) +{ + int ret; + gnutls_datum_t out; + + ret = gnutls_x509_rdn_get2(idn, &out, GNUTLS_X509_DN_FLAG_COMPAT); + if (ret < 0) + return gnutls_assert_val(ret); + + ret = _gnutls_copy_string(&out, (void*)buf, buf_size); + gnutls_free(out.data); + if (ret < 0) { + gnutls_assert(); + } + + return ret; +} + +/** + * gnutls_x509_rdn_get2: + * @idn: should contain a DER encoded RDN sequence + * @str: a datum that will hold the name + * @flags: zero of %GNUTLS_X509_DN_FLAG_COMPAT + * + * This function will return the name of the given RDN sequence. The + * name will be in the form "C=xxxx,O=yyyy,CN=zzzz" as described in + * RFC4514. + * + * When the flag %GNUTLS_X509_DN_FLAG_COMPAT is specified, the output + * format will match the format output by previous to 3.5.6 versions of GnuTLS + * which was not not fully RFC4514-compliant. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, or + * %GNUTLS_E_SHORT_MEMORY_BUFFER is returned and *@buf_size is + * updated if the provided buffer is not long enough, otherwise a + * negative error value. + **/ +int +gnutls_x509_rdn_get2(const gnutls_datum_t * idn, + gnutls_datum_t *str, unsigned flags) +{ + int ret; + gnutls_x509_dn_t dn; + + ret = gnutls_x509_dn_init(&dn); + if (ret < 0) + return gnutls_assert_val(ret); + + ret = gnutls_x509_dn_import(dn, idn); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = gnutls_x509_dn_get_str2(dn, str, flags); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = 0; + cleanup: + gnutls_x509_dn_deinit(dn); + return ret; +} + +/** + * gnutls_x509_rdn_get_by_oid: + * @idn: should contain a DER encoded RDN sequence + * @oid: an Object Identifier + * @indx: In case multiple same OIDs exist in the RDN indicates which + * to send. Use 0 for the first one. + * @raw_flag: If non-zero then the raw DER data are returned. + * @buf: a pointer to a structure to hold the peer's name + * @buf_size: holds the size of @buf + * + * This function will return the name of the given Object identifier, + * of the RDN sequence. The name will be encoded using the rules + * from RFC4514. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, or + * %GNUTLS_E_SHORT_MEMORY_BUFFER is returned and *@buf_size is + * updated if the provided buffer is not long enough, otherwise a + * negative error value. + **/ +int +gnutls_x509_rdn_get_by_oid(const gnutls_datum_t * idn, const char *oid, + unsigned indx, unsigned int raw_flag, + void *buf, size_t * buf_size) +{ + int result; + asn1_node dn = NULL; + gnutls_datum_t td; + + if (buf_size == 0) { + return GNUTLS_E_INVALID_REQUEST; + } + + if ((result = + asn1_create_element(_gnutls_get_pkix(), + "PKIX1.Name", &dn)) != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + result = _asn1_strict_der_decode(&dn, idn->data, idn->size, NULL); + if (result != ASN1_SUCCESS) { + /* couldn't decode DER */ + gnutls_assert(); + asn1_delete_structure(&dn); + return _gnutls_asn2err(result); + } + + result = + _gnutls_x509_parse_dn_oid(dn, "rdnSequence", oid, indx, + raw_flag, &td); + + asn1_delete_structure(&dn); + if (result < 0) + return gnutls_assert_val(result); + + return _gnutls_strdatum_to_buf(&td, buf, buf_size); +} + +/** + * gnutls_x509_rdn_get_oid: + * @idn: should contain a DER encoded RDN sequence + * @indx: Indicates which OID to return. Use 0 for the first one. + * @buf: a pointer to a structure to hold the peer's name OID + * @buf_size: holds the size of @buf + * + * This function will return the specified Object identifier, of the + * RDN sequence. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, or + * %GNUTLS_E_SHORT_MEMORY_BUFFER is returned and *@buf_size is + * updated if the provided buffer is not long enough, otherwise a + * negative error value. + * + * Since: 2.4.0 + **/ +int +gnutls_x509_rdn_get_oid(const gnutls_datum_t * idn, + unsigned indx, void *buf, size_t * buf_size) +{ + int result; + asn1_node dn = NULL; + + if (buf_size == 0) { + return GNUTLS_E_INVALID_REQUEST; + } + + if ((result = + asn1_create_element(_gnutls_get_pkix(), + "PKIX1.Name", &dn)) != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + result = _asn1_strict_der_decode(&dn, idn->data, idn->size, NULL); + if (result != ASN1_SUCCESS) { + /* couldn't decode DER */ + gnutls_assert(); + asn1_delete_structure(&dn); + return _gnutls_asn2err(result); + } + + result = + _gnutls_x509_get_dn_oid(dn, "rdnSequence", indx, buf, + buf_size); + + asn1_delete_structure(&dn); + return result; +} + +/* + * Compares the DER encoded part of a DN. + * + * Returns 1 if the DN's match and (0) if they don't match. Otherwise + * a negative error code is returned to indicate error. + */ +int +_gnutls_x509_compare_raw_dn(const gnutls_datum_t * dn1, + const gnutls_datum_t * dn2) +{ + int ret; + gnutls_datum_t str1, str2; + + /* Simple case of completely identical? */ + + if (dn1->size == dn2->size) { + if (memcmp(dn1->data, dn2->data, dn2->size) == 0) { + return 1; + } + } + + /* RFC5280 (https://tools.ietf.org/html/rfc5280#section-7.1) + * requires that the LDAP StringPrep profile and caseIgnoreMatch + * must be used for this comparison. We do not use that but + * instead we do a simpler comparison that ignores the tags used + * such as `UTF8String` and `PrintableString`. */ + + if ((dn1->size == 0) || (dn2->size == 0)) { + gnutls_assert(); + return 0; + } + + ret = gnutls_x509_rdn_get2(dn1, &str1, 0); + if (ret < 0) { + gnutls_assert(); + return 0; + } + + ret = gnutls_x509_rdn_get2(dn2, &str2, 0); + if (ret < 0) { + gnutls_assert(); + _gnutls_free_datum(&str1); + return 0; + } + + if (str1.size != str2.size) { + ret = 0; + goto cleanup; + } + if (memcmp(str1.data, str2.data, str2.size) != 0) { + gnutls_assert(); + ret = 0; + goto cleanup; + } + + ret = 1; /* they match */ + +cleanup: + _gnutls_free_datum(&str1); + _gnutls_free_datum(&str2); + + return ret; +} diff --git a/lib/x509/email-verify.c b/lib/x509/email-verify.c new file mode 100644 index 0000000..053e512 --- /dev/null +++ b/lib/x509/email-verify.c @@ -0,0 +1,153 @@ +/* + * Copyright (C) 2003-2012 Free Software Foundation, Inc. + * Copyright (C) 2002 Andrew McDonald + * + * This file is part of GnuTLS. + * + * The GnuTLS is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/> + * + */ + +#include "gnutls_int.h" +#include <str.h> +#include <x509_int.h> +#include <common.h> +#include "errors.h" +#include <system.h> + +/** + * gnutls_x509_crt_check_email: + * @cert: should contain an gnutls_x509_crt_t type + * @email: A null terminated string that contains an email address (RFC822) + * @flags: should be zero + * + * This function will check if the given certificate's subject matches + * the given email address. + * + * Returns: non-zero for a successful match, and zero on failure. + **/ +unsigned +gnutls_x509_crt_check_email(gnutls_x509_crt_t cert, + const char *email, unsigned int flags) +{ + char rfc822name[MAX_CN]; + size_t rfc822namesize; + int found_rfc822name = 0; + int ret = 0; + int i = 0; + char *a_email; + gnutls_datum_t out; + + /* convert the provided email to ACE-Labels domain. */ + ret = _gnutls_idna_email_map(email, strlen(email), &out); + if (ret < 0) { + _gnutls_debug_log("unable to convert email %s to IDNA format\n", email); + a_email = (char*)email; + } else { + a_email = (char*)out.data; + } + + /* try matching against: + * 1) an address as an alternative name (subjectAltName) extension + * in the certificate + * 2) the EMAIL field in the certificate + * + * only try (2) if there is no subjectAltName extension of + * type RFC822Name, and there is a single EMAIL. + */ + + /* Check through all included subjectAltName extensions, comparing + * against all those of type RFC822Name. + */ + for (i = 0; !(ret < 0); i++) { + + rfc822namesize = sizeof(rfc822name); + ret = gnutls_x509_crt_get_subject_alt_name(cert, i, + rfc822name, + &rfc822namesize, + NULL); + + if (ret == GNUTLS_SAN_RFC822NAME) { + found_rfc822name = 1; + + if (_gnutls_has_embedded_null(rfc822name, rfc822namesize)) { + _gnutls_debug_log("certificate has %s with embedded null in rfc822name\n", rfc822name); + continue; + } + + if (!_gnutls_str_is_print(rfc822name, rfc822namesize)) { + _gnutls_debug_log("invalid (non-ASCII) email in certificate %.*s\n", (int)rfc822namesize, rfc822name); + continue; + } + + ret = _gnutls_hostname_compare(rfc822name, rfc822namesize, a_email, GNUTLS_VERIFY_DO_NOT_ALLOW_WILDCARDS); + if (ret != 0) { + ret = 1; + goto cleanup; + } + } + } + + if (!found_rfc822name) { + /* did not get the necessary extension, use CN instead + */ + + /* enforce the RFC6125 (§1.8) requirement that only + * a single CN must be present */ + rfc822namesize = sizeof(rfc822name); + ret = gnutls_x509_crt_get_dn_by_oid + (cert, GNUTLS_OID_PKCS9_EMAIL, 1, 0, rfc822name, + &rfc822namesize); + if (ret != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { + ret = 0; + goto cleanup; + } + + rfc822namesize = sizeof(rfc822name); + ret = gnutls_x509_crt_get_dn_by_oid + (cert, GNUTLS_OID_PKCS9_EMAIL, 0, 0, rfc822name, + &rfc822namesize); + if (ret < 0) { + ret = 0; + goto cleanup; + } + + if (_gnutls_has_embedded_null(rfc822name, rfc822namesize)) { + _gnutls_debug_log("certificate has EMAIL %s with embedded null in name\n", rfc822name); + ret = 0; + goto cleanup; + } + + if (!_gnutls_str_is_print(rfc822name, rfc822namesize)) { + _gnutls_debug_log("invalid (non-ASCII) email in certificate DN %.*s\n", (int)rfc822namesize, rfc822name); + ret = 0; + goto cleanup; + } + + ret = _gnutls_hostname_compare(rfc822name, rfc822namesize, a_email, GNUTLS_VERIFY_DO_NOT_ALLOW_WILDCARDS); + if (ret != 0) { + ret = 1; + goto cleanup; + } + } + + /* not found a matching name + */ + ret = 0; + cleanup: + if (a_email != email) { + gnutls_free(a_email); + } + return ret; +} diff --git a/lib/x509/extensions.c b/lib/x509/extensions.c new file mode 100644 index 0000000..dc333f4 --- /dev/null +++ b/lib/x509/extensions.c @@ -0,0 +1,911 @@ +/* + * Copyright (C) 2003-2014 Free Software Foundation, Inc. + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GnuTLS. + * + * The GnuTLS is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/> + * + */ + +/* Functions that relate to the X.509 extension parsing. + */ + +#include "gnutls_int.h" +#include "errors.h" +#include <global.h> +#include <libtasn1.h> +#include <common.h> +#include <gnutls/x509-ext.h> +#include <gnutls/x509.h> +#include <x509_int.h> +#include <datum.h> + +int +_gnutls_get_extension(asn1_node asn, const char *root, + const char *extension_id, int indx, + gnutls_datum_t * ret, unsigned int *_critical) +{ + int k, result, len; + char name[MAX_NAME_SIZE], name2[MAX_NAME_SIZE]; + char str_critical[10]; + int critical = 0; + char extnID[MAX_OID_SIZE]; + gnutls_datum_t value; + int indx_counter = 0; + + ret->data = NULL; + ret->size = 0; + + k = 0; + do { + k++; + + snprintf(name, sizeof(name), "%s.?%d", root, k); + + _gnutls_str_cpy(name2, sizeof(name2), name); + _gnutls_str_cat(name2, sizeof(name2), ".extnID"); + + len = sizeof(extnID) - 1; + result = asn1_read_value(asn, name2, extnID, &len); + + if (result == ASN1_ELEMENT_NOT_FOUND) { + break; + } else if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + /* Handle Extension + */ + if (strcmp(extnID, extension_id) == 0 + && indx == indx_counter++) { + /* extension was found + */ + + /* read the critical status. + */ + _gnutls_str_cpy(name2, sizeof(name2), name); + _gnutls_str_cat(name2, sizeof(name2), ".critical"); + + len = sizeof(str_critical); + result = + asn1_read_value(asn, name2, + str_critical, &len); + + if (result == ASN1_ELEMENT_NOT_FOUND) { + gnutls_assert(); + break; + } else if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + if (str_critical[0] == 'T') + critical = 1; + else + critical = 0; + + /* read the value. + */ + _gnutls_str_cpy(name2, sizeof(name2), name); + _gnutls_str_cat(name2, sizeof(name2), + ".extnValue"); + + result = + _gnutls_x509_read_value(asn, name2, &value); + if (result < 0) { + gnutls_assert(); + return result; + } + + ret->data = value.data; + ret->size = value.size; + + if (_critical) + *_critical = critical; + + return 0; + } + } + while (1); + + if (result == ASN1_ELEMENT_NOT_FOUND) { + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + } else { + gnutls_assert(); + return _gnutls_asn2err(result); + } +} + +static int +get_indx_extension(asn1_node asn, const char *root, + int indx, gnutls_datum_t * out) +{ + char name[MAX_NAME_SIZE]; + int ret; + + out->data = NULL; + out->size = 0; + + snprintf(name, sizeof(name), "%s.?%d.extnValue", root, indx+1); + + ret = _gnutls_x509_read_value(asn, name, out); + if (ret < 0) + return gnutls_assert_val(ret); + + return 0; +} + +int +_gnutls_x509_crt_get_extension(gnutls_x509_crt_t cert, + const char *extension_id, int indx, + gnutls_datum_t * data, unsigned int *critical) +{ + return _gnutls_get_extension(cert->cert, "tbsCertificate.extensions", + extension_id, indx, data, critical); +} + +/** + * gnutls_x509_crt_get_extension_data2: + * @cert: should contain a #gnutls_x509_crt_t type + * @indx: Specifies which extension OID to read. Use (0) to get the first one. + * @data: will contain the extension DER-encoded data + * + * This function will return the requested by the index extension data in the + * certificate. The extension data will be allocated using + * gnutls_malloc(). + * + * Use gnutls_x509_crt_get_extension_info() to extract the OID. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, + * otherwise a negative error code is returned. If you have reached the + * last extension available %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE + * will be returned. + **/ +int +gnutls_x509_crt_get_extension_data2(gnutls_x509_crt_t cert, + unsigned indx, + gnutls_datum_t * data) +{ + return get_indx_extension(cert->cert, "tbsCertificate.extensions", + indx, data); +} + +int +_gnutls_x509_crl_get_extension(gnutls_x509_crl_t crl, + const char *extension_id, int indx, + gnutls_datum_t * data, + unsigned int *critical) +{ + return _gnutls_get_extension(crl->crl, "tbsCertList.crlExtensions", + extension_id, indx, data, critical); +} + +/** + * gnutls_x509_crl_get_extension_data2: + * @crl: should contain a #gnutls_x509_crl_t type + * @indx: Specifies which extension OID to read. Use (0) to get the first one. + * @data: will contain the extension DER-encoded data + * + * This function will return the requested by the index extension data in the + * certificate revocation list. The extension data will be allocated using + * gnutls_malloc(). + * + * Use gnutls_x509_crt_get_extension_info() to extract the OID. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, + * otherwise a negative error code is returned. If you have reached the + * last extension available %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE + * will be returned. + **/ +int +gnutls_x509_crl_get_extension_data2(gnutls_x509_crl_t crl, + unsigned indx, + gnutls_datum_t * data) +{ + return get_indx_extension(crl->crl, "tbsCertList.crlExtensions", + indx, data); +} + +/* This function will attempt to return the requested extension OID found in + * the given X509v3 certificate. + * + * If you have passed the last extension, GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE will + * be returned. + */ +static int get_extension_oid(asn1_node asn, const char *root, + unsigned indx, void *oid, size_t * sizeof_oid) +{ + int k, result, len; + char name[MAX_NAME_SIZE], name2[MAX_NAME_SIZE]; + char extnID[MAX_OID_SIZE]; + unsigned indx_counter = 0; + + k = 0; + do { + k++; + + snprintf(name, sizeof(name), "%s.?%d", root, k); + + _gnutls_str_cpy(name2, sizeof(name2), name); + _gnutls_str_cat(name2, sizeof(name2), ".extnID"); + + len = sizeof(extnID) - 1; + result = asn1_read_value(asn, name2, extnID, &len); + + if (result == ASN1_ELEMENT_NOT_FOUND) { + gnutls_assert(); + break; + } else if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + /* Handle Extension + */ + if (indx == indx_counter++) { + len = strlen(extnID) + 1; + + if (*sizeof_oid < (unsigned) len) { + *sizeof_oid = len; + gnutls_assert(); + return GNUTLS_E_SHORT_MEMORY_BUFFER; + } + + memcpy(oid, extnID, len); + *sizeof_oid = len - 1; + + return 0; + } + + + } + while (1); + + if (result == ASN1_ELEMENT_NOT_FOUND) { + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + } else { + gnutls_assert(); + return _gnutls_asn2err(result); + } +} + +/* This function will attempt to return the requested extension OID found in + * the given X509v3 certificate. + * + * If you have passed the last extension, GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE will + * be returned. + */ +int +_gnutls_x509_crt_get_extension_oid(gnutls_x509_crt_t cert, + int indx, void *oid, + size_t * sizeof_oid) +{ + return get_extension_oid(cert->cert, "tbsCertificate.extensions", + indx, oid, sizeof_oid); +} + +int +_gnutls_x509_crl_get_extension_oid(gnutls_x509_crl_t crl, + int indx, void *oid, + size_t * sizeof_oid) +{ + return get_extension_oid(crl->crl, "tbsCertList.crlExtensions", + indx, oid, sizeof_oid); +} + +/* This function will attempt to set the requested extension in + * the given X509v3 certificate. + * + * Critical will be either 0 or 1. + */ +static int +add_extension(asn1_node asn, const char *root, const char *extension_id, + const gnutls_datum_t * ext_data, unsigned int critical) +{ + int result; + const char *str; + char name[MAX_NAME_SIZE]; + + snprintf(name, sizeof(name), "%s", root); + + /* Add a new extension in the list. + */ + result = asn1_write_value(asn, name, "NEW", 1); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + if (root[0] != 0) + snprintf(name, sizeof(name), "%s.?LAST.extnID", root); + else + snprintf(name, sizeof(name), "?LAST.extnID"); + + result = asn1_write_value(asn, name, extension_id, 1); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + if (critical == 0) + str = "FALSE"; + else + str = "TRUE"; + + if (root[0] != 0) + snprintf(name, sizeof(name), "%s.?LAST.critical", root); + else + snprintf(name, sizeof(name), "?LAST.critical"); + + result = asn1_write_value(asn, name, str, 1); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + if (root[0] != 0) + snprintf(name, sizeof(name), "%s.?LAST.extnValue", root); + else + snprintf(name, sizeof(name), "?LAST.extnValue"); + + result = _gnutls_x509_write_value(asn, name, ext_data); + if (result < 0) { + gnutls_assert(); + return result; + } + + return 0; +} + +/* Overwrite the given extension (using the index) + * index here starts from one. + */ +static int +overwrite_extension(asn1_node asn, const char *root, unsigned int indx, + const gnutls_datum_t * ext_data, unsigned int critical) +{ + char name[MAX_NAME_SIZE], name2[MAX_NAME_SIZE]; + const char *str; + int result; + + if (root[0] != 0) + snprintf(name, sizeof(name), "%s.?%u", root, indx); + else + snprintf(name, sizeof(name), "?%u", indx); + + if (critical == 0) + str = "FALSE"; + else + str = "TRUE"; + + _gnutls_str_cpy(name2, sizeof(name2), name); + _gnutls_str_cat(name2, sizeof(name2), ".critical"); + + result = asn1_write_value(asn, name2, str, 1); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + _gnutls_str_cpy(name2, sizeof(name2), name); + _gnutls_str_cat(name2, sizeof(name2), ".extnValue"); + + result = _gnutls_x509_write_value(asn, name2, ext_data); + if (result < 0) { + gnutls_assert(); + return result; + } + + return 0; +} + +int +_gnutls_set_extension(asn1_node asn, const char *root, + const char *ext_id, + const gnutls_datum_t * ext_data, unsigned int critical) +{ + int result = 0; + int k, len; + char name[MAX_NAME_SIZE], name2[MAX_NAME_SIZE]; + char extnID[MAX_OID_SIZE]; + + /* Find the index of the given extension. + */ + k = 0; + do { + k++; + + if (root[0] != 0) + snprintf(name, sizeof(name), "%s.?%d", root, k); + else + snprintf(name, sizeof(name), "?%d", k); + + len = sizeof(extnID) - 1; + result = asn1_read_value(asn, name, extnID, &len); + + /* move to next + */ + + if (result == ASN1_ELEMENT_NOT_FOUND) { + break; + } + + do { + + _gnutls_str_cpy(name2, sizeof(name2), name); + _gnutls_str_cat(name2, sizeof(name2), ".extnID"); + + len = sizeof(extnID) - 1; + result = asn1_read_value(asn, name2, extnID, &len); + + if (result == ASN1_ELEMENT_NOT_FOUND) { + gnutls_assert(); + break; + } else if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + /* Handle Extension + */ + if (strcmp(extnID, ext_id) == 0) { + /* extension was found + */ + return overwrite_extension(asn, root, k, + ext_data, + critical); + } + + + } + while (0); + } + while (1); + + if (result == ASN1_ELEMENT_NOT_FOUND) { + return add_extension(asn, root, ext_id, ext_data, + critical); + } else { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + + return 0; +} + +/* This function will attempt to overwrite the requested extension with + * the given one. + * + * Critical will be either 0 or 1. + */ +int +_gnutls_x509_crt_set_extension(gnutls_x509_crt_t cert, + const char *ext_id, + const gnutls_datum_t * ext_data, + unsigned int critical) +{ + MODIFIED(cert); + cert->use_extensions = 1; + + return _gnutls_set_extension(cert->cert, "tbsCertificate.extensions", + ext_id, ext_data, critical); +} + +int +_gnutls_x509_crl_set_extension(gnutls_x509_crl_t crl, + const char *ext_id, + const gnutls_datum_t * ext_data, + unsigned int critical) +{ + return _gnutls_set_extension(crl->crl, "tbsCertList.crlExtensions", ext_id, + ext_data, critical); +} + +int +_gnutls_x509_crq_set_extension(gnutls_x509_crq_t crq, + const char *ext_id, + const gnutls_datum_t * ext_data, + unsigned int critical) +{ + unsigned char *extensions = NULL; + size_t extensions_size = 0; + gnutls_datum_t der; + asn1_node c2; + int result; + + result = + gnutls_x509_crq_get_attribute_by_oid(crq, + "1.2.840.113549.1.9.14", + 0, NULL, + &extensions_size); + if (result == GNUTLS_E_SHORT_MEMORY_BUFFER) { + extensions = gnutls_malloc(extensions_size); + if (extensions == NULL) { + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; + } + + result = gnutls_x509_crq_get_attribute_by_oid(crq, + "1.2.840.113549.1.9.14", + 0, + extensions, + &extensions_size); + } + if (result < 0) { + if (result == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { + extensions_size = 0; + } else { + gnutls_assert(); + gnutls_free(extensions); + return result; + } + } + + result = + asn1_create_element(_gnutls_get_pkix(), "PKIX1.Extensions", + &c2); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + gnutls_free(extensions); + return _gnutls_asn2err(result); + } + + if (extensions_size > 0) { + result = + _asn1_strict_der_decode(&c2, extensions, extensions_size, + NULL); + gnutls_free(extensions); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + asn1_delete_structure(&c2); + return _gnutls_asn2err(result); + } + } + + result = _gnutls_set_extension(c2, "", ext_id, ext_data, critical); + if (result < 0) { + gnutls_assert(); + asn1_delete_structure(&c2); + return result; + } + + result = _gnutls_x509_der_encode(c2, "", &der, 0); + + asn1_delete_structure(&c2); + + if (result < 0) { + gnutls_assert(); + return result; + } + + result = + gnutls_x509_crq_set_attribute_by_oid(crq, + "1.2.840.113549.1.9.14", + der.data, der.size); + gnutls_free(der.data); + if (result < 0) { + gnutls_assert(); + return result; + } + + + return 0; +} + +/* extract an INTEGER from the DER encoded extension + */ +int +_gnutls_x509_ext_extract_number(uint8_t * number, + size_t * _nr_size, + uint8_t * extnValue, int extnValueLen) +{ + asn1_node ext = NULL; + int result; + int nr_size = *_nr_size; + + /* here it doesn't matter so much that we use CertificateSerialNumber. It is equal + * to using INTEGER. + */ + if ((result = asn1_create_element + (_gnutls_get_pkix(), "PKIX1.CertificateSerialNumber", + &ext)) != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + result = _asn1_strict_der_decode(&ext, extnValue, extnValueLen, NULL); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + asn1_delete_structure(&ext); + return _gnutls_asn2err(result); + } + + /* the default value of cA is false. + */ + result = asn1_read_value(ext, "", number, &nr_size); + if (result != ASN1_SUCCESS) + result = _gnutls_asn2err(result); + else + result = 0; + + *_nr_size = nr_size; + + asn1_delete_structure(&ext); + + return result; +} + +/* generate an INTEGER in a DER encoded extension + */ +int +_gnutls_x509_ext_gen_number(const uint8_t * number, size_t nr_size, + gnutls_datum_t * der_ext) +{ + asn1_node ext = NULL; + int result; + + result = + asn1_create_element(_gnutls_get_pkix(), + "PKIX1.CertificateSerialNumber", &ext); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + result = asn1_write_value(ext, "", number, nr_size); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + asn1_delete_structure(&ext); + return _gnutls_asn2err(result); + } + + result = _gnutls_x509_der_encode(ext, "", der_ext, 0); + + asn1_delete_structure(&ext); + + if (result < 0) { + gnutls_assert(); + return result; + } + + return 0; +} + +int +_gnutls_write_general_name(asn1_node ext, const char *ext_name, + gnutls_x509_subject_alt_name_t type, + const void *data, unsigned int data_size) +{ + const char *str; + int result; + char name[128]; + + if (data == NULL) { + if (data_size == 0) + data = (void*)""; + else + return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + } + + switch (type) { + case GNUTLS_SAN_DNSNAME: + str = "dNSName"; + break; + case GNUTLS_SAN_RFC822NAME: + str = "rfc822Name"; + break; + case GNUTLS_SAN_URI: + str = "uniformResourceIdentifier"; + break; + case GNUTLS_SAN_IPADDRESS: + str = "iPAddress"; + break; + case GNUTLS_SAN_REGISTERED_ID: + str = "registeredID"; + break; + default: + gnutls_assert(); + return GNUTLS_E_INTERNAL_ERROR; + } + + result = asn1_write_value(ext, ext_name, str, 1); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + snprintf(name, sizeof(name), "%s.%s", ext_name, str); + + result = asn1_write_value(ext, name, data, data_size); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + asn1_delete_structure(&ext); + return _gnutls_asn2err(result); + } + + return 0; +} + +int +_gnutls_write_new_general_name(asn1_node ext, const char *ext_name, + gnutls_x509_subject_alt_name_t type, + const void *data, unsigned int data_size) +{ + int result; + char name[128]; + + result = asn1_write_value(ext, ext_name, "NEW", 1); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + if (ext_name[0] == 0) { /* no dot */ + _gnutls_str_cpy(name, sizeof(name), "?LAST"); + } else { + _gnutls_str_cpy(name, sizeof(name), ext_name); + _gnutls_str_cat(name, sizeof(name), ".?LAST"); + } + + result = _gnutls_write_general_name(ext, name, type, + data, data_size); + if (result < 0) { + gnutls_assert(); + return result; + } + + return 0; +} + +int +_gnutls_write_new_othername(asn1_node ext, const char *ext_name, + const char *oid, + const void *data, unsigned int data_size) +{ + int result; + char name[128]; + char name2[128]; + + result = asn1_write_value(ext, ext_name, "NEW", 1); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + if (ext_name[0] == 0) { /* no dot */ + _gnutls_str_cpy(name, sizeof(name), "?LAST"); + } else { + _gnutls_str_cpy(name, sizeof(name), ext_name); + _gnutls_str_cat(name, sizeof(name), ".?LAST"); + } + + result = asn1_write_value(ext, name, "otherName", 1); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + snprintf(name2, sizeof(name2), "%s.otherName.type-id", name); + + result = asn1_write_value(ext, name2, oid, 1); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + asn1_delete_structure(&ext); + return _gnutls_asn2err(result); + } + + snprintf(name2, sizeof(name2), "%s.otherName.value", name); + + result = asn1_write_value(ext, name2, data, data_size); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + asn1_delete_structure(&ext); + return _gnutls_asn2err(result); + } + + return 0; +} + +/* Convert the given name to GeneralNames in a DER encoded extension. + * This is the same as subject alternative name. + */ +int +_gnutls_x509_ext_gen_subject_alt_name(gnutls_x509_subject_alt_name_t + type, + const char *othername_oid, + const void *data, + unsigned int data_size, + const gnutls_datum_t * prev_der_ext, + gnutls_datum_t * der_ext) +{ + int ret; + gnutls_subject_alt_names_t sans = NULL; + gnutls_datum_t name; + + ret = gnutls_subject_alt_names_init(&sans); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + if (prev_der_ext && prev_der_ext->data != NULL && + prev_der_ext->size != 0) { + + ret = gnutls_x509_ext_import_subject_alt_names(prev_der_ext, sans, 0); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + } + + name.data = (void*)data; + name.size = data_size; + ret = gnutls_subject_alt_names_set(sans, type, &name, othername_oid); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = gnutls_x509_ext_export_subject_alt_names(sans, der_ext); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = 0; +cleanup: + if (sans != NULL) + gnutls_subject_alt_names_deinit(sans); + + return ret; +} + +/* generate the AuthorityKeyID in a DER encoded extension + */ +int +_gnutls_x509_ext_gen_auth_key_id(const void *id, size_t id_size, + gnutls_datum_t * der_ext) +{ + gnutls_x509_aki_t aki; + int ret; + gnutls_datum_t l_id; + + ret = gnutls_x509_aki_init(&aki); + if (ret < 0) + return gnutls_assert_val(ret); + + l_id.data = (void*)id; + l_id.size = id_size; + ret = gnutls_x509_aki_set_id(aki, &l_id); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = gnutls_x509_ext_export_authority_key_id(aki, der_ext); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = 0; + + cleanup: + gnutls_x509_aki_deinit(aki); + return ret; +} diff --git a/lib/x509/hostname-verify.c b/lib/x509/hostname-verify.c new file mode 100644 index 0000000..6ef8ba0 --- /dev/null +++ b/lib/x509/hostname-verify.c @@ -0,0 +1,298 @@ +/* + * Copyright (C) 2003-2016 Free Software Foundation, Inc. + * Copyright (C) 2015-2016 Red Hat, Inc. + * Copyright (C) 2002 Andrew McDonald + * + * This file is part of GnuTLS. + * + * The GnuTLS is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/> + * + */ + +#include "gnutls_int.h" +#include <str.h> +#include <x509_int.h> +#include <common.h> +#include "errors.h" +#include <system.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +/** + * gnutls_x509_crt_check_hostname: + * @cert: should contain an gnutls_x509_crt_t type + * @hostname: A null terminated string that contains a DNS name + * + * This function will check if the given certificate's subject matches + * the given hostname. This is a basic implementation of the matching + * described in RFC6125, and takes into account wildcards, + * and the DNSName/IPAddress subject alternative name PKIX extension. + * + * For details see also gnutls_x509_crt_check_hostname2(). + * + * Returns: non-zero for a successful match, and zero on failure. + **/ +unsigned +gnutls_x509_crt_check_hostname(gnutls_x509_crt_t cert, + const char *hostname) +{ + return gnutls_x509_crt_check_hostname2(cert, hostname, 0); +} + +static int +check_ip(gnutls_x509_crt_t cert, const void *ip, unsigned ip_size) +{ + char temp[16]; + size_t temp_size; + unsigned i; + int ret = 0; + + /* try matching against: + * 1) a IPaddress alternative name (subjectAltName) extension + * in the certificate + */ + + /* Check through all included subjectAltName extensions, comparing + * against all those of type IPAddress. + */ + for (i = 0; !(ret < 0); i++) { + temp_size = sizeof(temp); + ret = gnutls_x509_crt_get_subject_alt_name(cert, i, + temp, + &temp_size, + NULL); + + if (ret == GNUTLS_SAN_IPADDRESS) { + if (temp_size == ip_size && memcmp(temp, ip, ip_size) == 0) + return 1; + } else if (ret == GNUTLS_E_SHORT_MEMORY_BUFFER) { + ret = 0; + } + } + + /* not found a matching IP + */ + return 0; +} + +/** + * gnutls_x509_crt_check_ip: + * @cert: should contain an gnutls_x509_crt_t type + * @ip: A pointer to the raw IP address + * @ip_size: the number of bytes in ip (4 or 16) + * @flags: should be zero + * + * This function will check if the IP allowed IP addresses in + * the certificate's subject alternative name match the provided + * IP address. + * + * Returns: non-zero for a successful match, and zero on failure. + **/ +unsigned +gnutls_x509_crt_check_ip(gnutls_x509_crt_t cert, + const unsigned char *ip, unsigned int ip_size, + unsigned int flags) +{ + return check_ip(cert, ip, ip_size); +} + +/* whether gnutls_x509_crt_check_hostname2() will consider these + * alternative name types. This is to satisfy RFC6125 requirement + * that we do not fallback to CN-ID if we encounter a supported name + * type. + */ +#define IS_SAN_SUPPORTED(san) (san==GNUTLS_SAN_DNSNAME||san==GNUTLS_SAN_IPADDRESS) + +/** + * gnutls_x509_crt_check_hostname2: + * @cert: should contain an gnutls_x509_crt_t type + * @hostname: A null terminated string that contains a DNS name + * @flags: gnutls_certificate_verify_flags + * + * This function will check if the given certificate's subject matches + * the given hostname. This is a basic implementation of the matching + * described in RFC6125, and takes into account wildcards, + * and the DNSName/IPAddress subject alternative name PKIX extension. + * + * IPv4 addresses are accepted by this function in the dotted-decimal + * format (e.g, ddd.ddd.ddd.ddd), and IPv6 addresses in the hexadecimal + * x:x:x:x:x:x:x:x format. For them the IPAddress subject alternative + * name extension is consulted. Previous versions to 3.6.0 of GnuTLS + * in case of a non-match would consult (in a non-standard extension) + * the DNSname and CN fields. This is no longer the case. + * + * When the flag %GNUTLS_VERIFY_DO_NOT_ALLOW_WILDCARDS is specified no + * wildcards are considered. Otherwise they are only considered if the + * domain name consists of three components or more, and the wildcard + * starts at the leftmost position. + + * When the flag %GNUTLS_VERIFY_DO_NOT_ALLOW_IP_MATCHES is specified, + * the input will be treated as a DNS name, and matching of textual IP addresses + * against the IPAddress part of the alternative name will not be allowed. + * + * The function gnutls_x509_crt_check_ip() is available for matching + * IP addresses. + * + * Returns: non-zero for a successful match, and zero on failure. + * + * Since: 3.3.0 + **/ +unsigned +gnutls_x509_crt_check_hostname2(gnutls_x509_crt_t cert, + const char *hostname, unsigned int flags) +{ + char dnsname[MAX_CN]; + size_t dnsnamesize; + int found_dnsname = 0; + int ret = 0; + int i = 0; + struct in_addr ipv4; + char *p = NULL; + char *a_hostname; + unsigned have_other_addresses = 0; + gnutls_datum_t out; + + /* check whether @hostname is an ip address */ + if (!(flags & GNUTLS_VERIFY_DO_NOT_ALLOW_IP_MATCHES) && + ((p=strchr(hostname, ':')) != NULL || inet_pton(AF_INET, hostname, &ipv4) != 0)) { + + if (p != NULL) { + struct in6_addr ipv6; + + ret = inet_pton(AF_INET6, hostname, &ipv6); + if (ret == 0) { + gnutls_assert(); + goto hostname_fallback; + } + ret = check_ip(cert, &ipv6, 16); + } else { + ret = check_ip(cert, &ipv4, 4); + } + + /* Prior to 3.6.0 we were accepting misconfigured servers, that place their IP + * in the DNS field of subjectAlternativeName. That is no longer the case. */ + return ret; + } + + hostname_fallback: + /* convert the provided hostname to ACE-Labels domain. */ + ret = gnutls_idna_map (hostname, strlen(hostname), &out, 0); + if (ret < 0) { + _gnutls_debug_log("unable to convert hostname %s to IDNA format\n", hostname); + a_hostname = (char*)hostname; + } else { + a_hostname = (char*)out.data; + } + + /* try matching against: + * 1) a DNS name as an alternative name (subjectAltName) extension + * in the certificate + * 2) the common name (CN) in the certificate, if the certificate is acceptable for TLS_WWW_SERVER purpose + * + * either of these may be of the form: *.domain.tld + * + * only try (2) if there is no subjectAltName extension of + * type dNSName, and there is a single CN. + */ + + /* Check through all included subjectAltName extensions, comparing + * against all those of type dNSName. + */ + for (i = 0; !(ret < 0); i++) { + + dnsnamesize = sizeof(dnsname); + ret = gnutls_x509_crt_get_subject_alt_name(cert, i, + dnsname, + &dnsnamesize, + NULL); + + if (ret == GNUTLS_SAN_DNSNAME) { + found_dnsname = 1; + + if (_gnutls_has_embedded_null(dnsname, dnsnamesize)) { + _gnutls_debug_log("certificate has %s with embedded null in name\n", dnsname); + continue; + } + + if (!_gnutls_str_is_print(dnsname, dnsnamesize)) { + _gnutls_debug_log("invalid (non-ASCII) name in certificate %.*s\n", (int)dnsnamesize, dnsname); + continue; + } + + ret = _gnutls_hostname_compare(dnsname, dnsnamesize, a_hostname, flags); + if (ret != 0) { + ret = 1; + goto cleanup; + } + } else { + if (IS_SAN_SUPPORTED(ret)) + have_other_addresses = 1; + } + } + + if (!have_other_addresses && !found_dnsname && _gnutls_check_key_purpose(cert, GNUTLS_KP_TLS_WWW_SERVER, 0) != 0) { + /* did not get the necessary extension, use CN instead, if the + * certificate would have been acceptable for a TLS WWW server purpose. + * That is because only for that purpose the CN is a valid field to + * store the hostname. + */ + + /* enforce the RFC6125 (§1.8) requirement that only + * a single CN must be present */ + dnsnamesize = sizeof(dnsname); + ret = gnutls_x509_crt_get_dn_by_oid + (cert, OID_X520_COMMON_NAME, 1, 0, dnsname, + &dnsnamesize); + if (ret != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { + ret = 0; + goto cleanup; + } + + dnsnamesize = sizeof(dnsname); + ret = gnutls_x509_crt_get_dn_by_oid + (cert, OID_X520_COMMON_NAME, 0, 0, dnsname, + &dnsnamesize); + if (ret < 0) { + ret = 0; + goto cleanup; + } + + if (_gnutls_has_embedded_null(dnsname, dnsnamesize)) { + _gnutls_debug_log("certificate has CN %s with embedded null in name\n", dnsname); + ret = 0; + goto cleanup; + } + + if (!_gnutls_str_is_print(dnsname, dnsnamesize)) { + _gnutls_debug_log("invalid (non-ASCII) name in certificate CN %.*s\n", (int)dnsnamesize, dnsname); + ret = 0; + goto cleanup; + } + + ret = _gnutls_hostname_compare(dnsname, dnsnamesize, a_hostname, flags); + if (ret != 0) { + ret = 1; + goto cleanup; + } + } + + /* not found a matching name + */ + ret = 0; + cleanup: + if (a_hostname != hostname) { + gnutls_free(a_hostname); + } + return ret; +} diff --git a/lib/x509/ip-in-cidr.h b/lib/x509/ip-in-cidr.h new file mode 100644 index 0000000..d5e26eb --- /dev/null +++ b/lib/x509/ip-in-cidr.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2014-2016 Free Software Foundation, Inc. + * Copyright (C) 2016 Red Hat, Inc. + * + * Authors: Nikos Mavrogiannopoulos, Daiki Ueno, Martin Ukrop + * + * This file is part of GnuTLS. + * + * The GnuTLS is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/> + * + */ + +#ifndef GNUTLS_LIB_X509_IP_IN_CIDR_H +#define GNUTLS_LIB_X509_IP_IN_CIDR_H + +/*- + * ip_in_cidr: + * @ip: IP datum (IPv4 or IPv6) + * @cidr: CIDR datum (IPv4 or IPv6) + * + * Check if @ip lies in the given @cidr range. + * The @ip version must match the @cidr version (v4/v6), + * (this is not checked). + * + * Returns: 1 if @ip lies within @cidr, 0 otherwise + -*/ +static unsigned ip_in_cidr(const gnutls_datum_t *ip, const gnutls_datum_t *cidr) +{ + unsigned byte; +#ifndef BUILD_IN_TESTS + char str_ip[48]; + char str_cidr[97]; + + _gnutls_hard_log("matching %.*s with CIDR constraint %.*s\n", + (int) sizeof(str_ip), + _gnutls_ip_to_string(ip->data, ip->size, str_ip, sizeof(str_ip)), + (int) sizeof(str_cidr), + _gnutls_cidr_to_string(cidr->data, cidr->size, str_cidr, sizeof(str_cidr))); +#endif + for (byte = 0; byte < ip->size; byte++) + if (((ip->data[byte] ^ cidr->data[byte]) & cidr->data[ip->size+byte]) != 0) + return 0; + + return 1; /* match */ +} + +#endif /* GNUTLS_LIB_X509_IP_IN_CIDR_H */ diff --git a/lib/x509/ip.c b/lib/x509/ip.c new file mode 100644 index 0000000..c4cb008 --- /dev/null +++ b/lib/x509/ip.c @@ -0,0 +1,268 @@ +/* + * Copyright (C) 2007-2016 Free Software Foundation, Inc. + * Copyright (C) 2015-2016 Red Hat, Inc. + * + * Author: Simon Josefsson, Nikos Mavrogiannopoulos, Martin Ukrop + * + * This file is part of GnuTLS. + * + * The GnuTLS is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/> + * + */ + +#include "gnutls_int.h" +#include "ip.h" +#include <gnutls/x509.h> +#include <arpa/inet.h> + +/*- + * _gnutls_mask_to_prefix: + * @mask: CIDR mask + * @mask_size: number of bytes in @mask + * + * Check for mask validity (form of 1*0*) and return its prefix numerically. + * + * Returns: Length of 1-prefix (0 -- mask_size*8), -1 in case of invalid mask + -*/ +int _gnutls_mask_to_prefix(const unsigned char *mask, unsigned mask_size) +{ + unsigned i, prefix_length = 0; + for (i=0; i<mask_size; i++) { + if (mask[i] == 0xFF) { + prefix_length += 8; + } else { + switch(mask[i]) { + case 0xFE: prefix_length += 7; break; + case 0xFC: prefix_length += 6; break; + case 0xF8: prefix_length += 5; break; + case 0xF0: prefix_length += 4; break; + case 0xE0: prefix_length += 3; break; + case 0xC0: prefix_length += 2; break; + case 0x80: prefix_length += 1; break; + case 0x00: break; + default: + return -1; + } + break; + } + } + i++; + // mask is invalid, if there follows something else than 0x00 + for ( ; i<mask_size; i++) { + if (mask[i] != 0) + return -1; + } + return prefix_length; +} + +/*- + * _gnutls_ip_to_string: + * @_ip: IP address (RFC5280 format) + * @ip_size: Size of @_ip (4 or 16) + * @out: Resulting string + * @out_size: Size of @out + * + * Transform IP address into human-readable string. + * @string must be already allocated and + * at least 16/48 bytes for IPv4/v6 address respectively. + * + * Returns: Address of result string. + -*/ +const char *_gnutls_ip_to_string(const void *_ip, unsigned int ip_size, char *out, unsigned int out_size) +{ + + if (ip_size != 4 && ip_size != 16) { + gnutls_assert(); + return NULL; + } + + if (ip_size == 4 && out_size < 16) { + gnutls_assert(); + return NULL; + } + + if (ip_size == 16 && out_size < 48) { + gnutls_assert(); + return NULL; + } + + if (ip_size == 4) + return inet_ntop(AF_INET, _ip, out, out_size); + else + return inet_ntop(AF_INET6, _ip, out, out_size); +} + +/*- + * _gnutls_cidr_to_string: + * @_ip: CIDR range (RFC5280 format) + * @ip_size: Size of @_ip (8 or 32) + * @out: Resulting string + * @out_size: Size of @out + * + * Transform CIDR IP address range into human-readable string. + * The input @_ip must be in RFC5280 format (IP address in network byte + * order, followed by its network mask which is 4 bytes in IPv4 and + * 16 bytes in IPv6). @string must be already allocated and + * at least 33/97 bytes for IPv4/v6 address respectively. + * + * Returns: Address of result string. + -*/ +const char *_gnutls_cidr_to_string(const void *_ip, unsigned int ip_size, char *out, unsigned int out_size) +{ + const unsigned char *ip = _ip; + char tmp[64]; + const char *p; + + if (ip_size != 8 && ip_size != 32) { + gnutls_assert(); + return NULL; + } + + if (ip_size == 8) { + p = inet_ntop(AF_INET, ip, tmp, sizeof(tmp)); + + if (p) + snprintf(out, out_size, "%s/%d", tmp, _gnutls_mask_to_prefix(ip+4, 4)); + } else { + p = inet_ntop(AF_INET6, ip, tmp, sizeof(tmp)); + + if (p) + snprintf(out, out_size, "%s/%d", tmp, _gnutls_mask_to_prefix(ip+16, 16)); + } + + if (p == NULL) + return NULL; + + return out; +} + +static void prefix_to_mask(unsigned prefix, unsigned char *mask, size_t mask_size) +{ + int i; + unsigned j; + memset(mask, 0, mask_size); + + for (i = prefix, j = 0; i > 0 && j < mask_size; i -= 8, j++) { + if (i >= 8) { + mask[j] = 0xff; + } else { + mask[j] = (unsigned long)(0xffU << (8 - i)); + } + } +} + +/*- + * _gnutls_mask_ip: + * @ip: IP of @ipsize bytes + * @mask: netmask of @ipsize bytes + * @ipsize: IP length (4 or 16) + * + * Mask given IP in place according to the given mask. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value. + -*/ +int _gnutls_mask_ip(unsigned char *ip, const unsigned char *mask, unsigned ipsize) +{ + unsigned i; + + if (ipsize != 4 && ipsize != 16) + return GNUTLS_E_MALFORMED_CIDR; + for (i = 0; i < ipsize; i++) + ip[i] &= mask[i]; + return GNUTLS_E_SUCCESS; +} + +/** + * gnutls_x509_cidr_to_rfc5280: + * @cidr: CIDR in RFC4632 format (IP/prefix), null-terminated + * @cidr_rfc5280: CIDR range converted to RFC5280 format + * + * This function will convert text CIDR range with prefix (such as '10.0.0.0/8') + * to RFC5280 (IP address in network byte order followed by its network mask). + * Works for both IPv4 and IPv6. + * + * The resulting object is directly usable for IP name constraints usage, + * for example in functions %gnutls_x509_name_constraints_add_permitted + * or %gnutls_x509_name_constraints_add_excluded. + * + * The data in datum needs to be deallocated using gnutls_free(). + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value. + * + * Since: 3.5.4 + */ +int gnutls_x509_cidr_to_rfc5280(const char *cidr, gnutls_datum_t *cidr_rfc5280) +{ + unsigned iplength, prefix; + int ret; + char *p; + char *p_end = NULL; + char *cidr_tmp; + + p = strchr(cidr, '/'); + if (p != NULL) { + prefix = strtol(p+1, &p_end, 10); + if (prefix == 0 && p_end == p+1) { + _gnutls_debug_log("Cannot parse prefix given in CIDR %s\n", cidr); + gnutls_assert(); + return GNUTLS_E_MALFORMED_CIDR; + } + unsigned length = p-cidr+1; + cidr_tmp = gnutls_malloc(length); + if (cidr_tmp == NULL) { + return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + } + memcpy(cidr_tmp, cidr, length); + cidr_tmp[length-1] = 0; + } else { + _gnutls_debug_log("No prefix given in CIDR %s\n", cidr); + gnutls_assert(); + return GNUTLS_E_MALFORMED_CIDR; + } + + if (strchr(cidr, ':') != 0) { /* IPv6 */ + iplength = 16; + } else { /* IPv4 */ + iplength = 4; + } + cidr_rfc5280->size = 2*iplength; + + if (prefix > iplength*8) { + _gnutls_debug_log("Invalid prefix given in CIDR %s (%d)\n", cidr, prefix); + ret = gnutls_assert_val(GNUTLS_E_MALFORMED_CIDR); + goto cleanup; + } + + cidr_rfc5280->data = gnutls_malloc(cidr_rfc5280->size); + if (cidr_rfc5280->data == NULL) { + ret = gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + goto cleanup; + } + + ret = inet_pton(iplength == 4 ? AF_INET : AF_INET6, cidr_tmp, cidr_rfc5280->data); + if (ret == 0) { + _gnutls_debug_log("Cannot parse IP from CIDR %s\n", cidr_tmp); + ret = gnutls_assert_val(GNUTLS_E_MALFORMED_CIDR); + goto cleanup; + } + + prefix_to_mask(prefix, &cidr_rfc5280->data[iplength], iplength); + _gnutls_mask_ip(cidr_rfc5280->data, &cidr_rfc5280->data[iplength], iplength); + + ret = GNUTLS_E_SUCCESS; + +cleanup: + gnutls_free(cidr_tmp); + return ret; +} diff --git a/lib/x509/ip.h b/lib/x509/ip.h new file mode 100644 index 0000000..2c8569b --- /dev/null +++ b/lib/x509/ip.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2007-2016 Free Software Foundation, Inc. + * Copyright (C) 2015-2016 Red Hat, Inc. + * + * Author: Simon Josefsson, Nikos Mavrogiannopoulos, Martin Ukrop + * + * This file is part of GnuTLS. + * + * The GnuTLS is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/> + * + */ + +#ifndef GNUTLS_LIB_X509_IP_H +#define GNUTLS_LIB_X509_IP_H + +// for documentation, see the definition +int _gnutls_mask_to_prefix(const unsigned char *mask, unsigned mask_size); + +// for documentation, see the definition +const char *_gnutls_ip_to_string(const void *_ip, unsigned int ip_size, char *out, unsigned int out_size); + +// for documentation, see the definition +const char *_gnutls_cidr_to_string(const void *_ip, unsigned int ip_size, char *out, unsigned int out_size); + +// for documentation, see the definition +int _gnutls_mask_ip(unsigned char *ip, const unsigned char *mask, unsigned ipsize); + +#endif /* GNUTLS_LIB_X509_IP_H */ diff --git a/lib/x509/key_decode.c b/lib/x509/key_decode.c new file mode 100644 index 0000000..44e4297 --- /dev/null +++ b/lib/x509/key_decode.c @@ -0,0 +1,678 @@ +/* + * Copyright (C) 2011-2012 Free Software Foundation, Inc. + * Copyright (C) 2013-2017 Red Hat + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GnuTLS. + * + * The GnuTLS is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/> + * + */ + +#include "gnutls_int.h" +#include "errors.h" +#include <global.h> +#include <libtasn1.h> +#include <datum.h> +#include "common.h" +#include "x509_int.h" +#include "pk.h" +#include <num.h> +#include <ecc.h> + +static int _gnutls_x509_read_rsa_pubkey(uint8_t * der, int dersize, + gnutls_pk_params_st * params); +static int _gnutls_x509_read_dsa_pubkey(uint8_t * der, int dersize, + gnutls_pk_params_st * params); +static int _gnutls_x509_read_ecc_pubkey(uint8_t * der, int dersize, + gnutls_pk_params_st * params); +static int _gnutls_x509_read_eddsa_pubkey(gnutls_ecc_curve_t curve, + uint8_t * der, int dersize, + gnutls_pk_params_st * params); +static int _gnutls_x509_read_ecdh_pubkey(gnutls_ecc_curve_t curve, + uint8_t * der, int dersize, + gnutls_pk_params_st * params); +static int _gnutls_x509_read_gost_pubkey(uint8_t * der, int dersize, + gnutls_pk_params_st * params); + +static int +_gnutls_x509_read_dsa_params(uint8_t * der, int dersize, + gnutls_pk_params_st * params); + +/* + * some x509 certificate parsing functions that relate to MPI parameter + * extraction. This reads the BIT STRING subjectPublicKey. + * Returns 2 parameters (m,e). It does not set params_nr. + */ +int +_gnutls_x509_read_rsa_pubkey(uint8_t * der, int dersize, + gnutls_pk_params_st * params) +{ + int result; + asn1_node spk = NULL; + + if ((result = asn1_create_element + (_gnutls_get_gnutls_asn(), "GNUTLS.RSAPublicKey", &spk)) + != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + result = asn1_der_decoding(&spk, der, dersize, NULL); + + if (result != ASN1_SUCCESS) { + gnutls_assert(); + asn1_delete_structure(&spk); + return _gnutls_asn2err(result); + } + + + if (_gnutls_x509_read_int(spk, "modulus", + ¶ms->params[0]) < 0) { + gnutls_assert(); + asn1_delete_structure(&spk); + return GNUTLS_E_ASN1_GENERIC_ERROR; + } + + if (_gnutls_x509_read_int(spk, "publicExponent", + ¶ms->params[1]) < 0) { + gnutls_assert(); + _gnutls_mpi_release(¶ms->params[0]); + asn1_delete_structure(&spk); + return GNUTLS_E_ASN1_GENERIC_ERROR; + } + + asn1_delete_structure(&spk); + + return 0; + +} + +/* + * some x509 certificate parsing functions that relate to MPI parameter + * extraction. This reads the BIT STRING subjectPublicKey. + * Returns 2 parameters (m,e). It does not set params_nr. + */ +int +_gnutls_x509_read_ecc_pubkey(uint8_t * der, int dersize, + gnutls_pk_params_st * params) +{ + /* RFC5480 defines the public key to be an ECPoint (i.e. OCTET STRING), + * Then it says that the OCTET STRING _value_ is converted to BIT STRING. + * That means that the value we place there is the raw X9.62 one. */ + return _gnutls_ecc_ansi_x962_import(der, dersize, + ¶ms->params[ECC_X], + ¶ms->params[ECC_Y]); +} + +int _gnutls_x509_read_eddsa_pubkey(gnutls_ecc_curve_t curve, + uint8_t * der, int dersize, + gnutls_pk_params_st * params) +{ + int size = gnutls_ecc_curve_get_size(curve); + if (dersize != size) + return gnutls_assert_val(GNUTLS_E_ILLEGAL_PARAMETER); + + return _gnutls_set_datum(¶ms->raw_pub, der, dersize); +} + +int _gnutls_x509_read_ecdh_pubkey(gnutls_ecc_curve_t curve, + uint8_t * der, int dersize, + gnutls_pk_params_st * params) +{ + int size = gnutls_ecc_curve_get_size(curve); + if (dersize != size) + return gnutls_assert_val(GNUTLS_E_ILLEGAL_PARAMETER); + + return _gnutls_set_datum(¶ms->raw_pub, der, dersize); +} + +/* Pubkey is a concatenation of X (in little endian) and Y (also LE) + * encoded into OCTET STRING. */ +static int +_gnutls_x509_read_gost_pubkey(uint8_t * der, int dersize, + gnutls_pk_params_st * params) +{ + int ret; + int len; + bigint_t *x = ¶ms->params[GOST_X]; + bigint_t *y = ¶ms->params[GOST_Y]; + + /* Quick and dirty parsing of OCTET STRING of 0x40 or 0x80 bytes */ + if (dersize < 1 || der[0] != ASN1_TAG_OCTET_STRING) { + return gnutls_assert_val(GNUTLS_E_PARSING_ERROR); + } + + der++; + dersize--; + + ret = asn1_get_length_der(der, dersize, &len); + if (ret <= 0 || ret % 2 != 0 || dersize != len + ret) { + return gnutls_assert_val(GNUTLS_E_PARSING_ERROR); + } + + der += len; + dersize -= len; + + /* read data */ + ret = _gnutls_mpi_init_scan_le(x, der, dersize / 2); + if (ret < 0) + return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + + ret = _gnutls_mpi_init_scan_le(y, der + dersize / 2, dersize / 2); + if (ret < 0) { + _gnutls_mpi_release(y); + return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + } + + return 0; +} + +/* reads p,q and g + * from the certificate (subjectPublicKey BIT STRING). + * params[0-2]. It does NOT set params_nr. + */ +static int +_gnutls_x509_read_dsa_params(uint8_t * der, int dersize, + gnutls_pk_params_st * params) +{ + int result; + asn1_node spk = NULL; + + if ((result = asn1_create_element + (_gnutls_get_pkix(), "PKIX1.Dss-Parms", + &spk)) != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + result = asn1_der_decoding(&spk, der, dersize, NULL); + + if (result != ASN1_SUCCESS) { + gnutls_assert(); + asn1_delete_structure(&spk); + return _gnutls_asn2err(result); + } + + /* If the parameters are not included in the certificate + * then the issuer's parameters should be used. This is not + * implemented, and is not used in practice (along with DSA). + */ + + /* Read p */ + + if (_gnutls_x509_read_int(spk, "p", ¶ms->params[0]) < 0) { + gnutls_assert(); + asn1_delete_structure(&spk); + return GNUTLS_E_ASN1_GENERIC_ERROR; + } + + /* Read q */ + + if (_gnutls_x509_read_int(spk, "q", ¶ms->params[1]) < 0) { + gnutls_assert(); + asn1_delete_structure(&spk); + _gnutls_mpi_release(¶ms->params[0]); + return GNUTLS_E_ASN1_GENERIC_ERROR; + } + + /* Read g */ + + if (_gnutls_x509_read_int(spk, "g", ¶ms->params[2]) < 0) { + gnutls_assert(); + asn1_delete_structure(&spk); + _gnutls_mpi_release(¶ms->params[0]); + _gnutls_mpi_release(¶ms->params[1]); + return GNUTLS_E_ASN1_GENERIC_ERROR; + } + + asn1_delete_structure(&spk); + + params->params_nr = 3; /* public key is missing */ + params->algo = GNUTLS_PK_DSA; + + return 0; + +} + +/* reads the curve from the certificate. + * params[0-4]. It does NOT set params_nr. + */ +int +_gnutls_x509_read_ecc_params(uint8_t * der, int dersize, + unsigned int * curve) +{ + int ret; + asn1_node spk = NULL; + char oid[MAX_OID_SIZE]; + int oid_size; + + if ((ret = asn1_create_element + (_gnutls_get_gnutls_asn(), "GNUTLS.ECParameters", + &spk)) != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(ret); + } + + ret = asn1_der_decoding(&spk, der, dersize, NULL); + + if (ret != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(ret); + goto cleanup; + } + + /* read the curve */ + oid_size = sizeof(oid); + ret = asn1_read_value(spk, "namedCurve", oid, &oid_size); + if (ret != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(ret); + goto cleanup; + } + + *curve = gnutls_oid_to_ecc_curve(oid); + if (*curve == GNUTLS_ECC_CURVE_INVALID) { + _gnutls_debug_log("Curve %s is not supported\n", oid); + gnutls_assert(); + ret = GNUTLS_E_ECC_UNSUPPORTED_CURVE; + goto cleanup; + } + + ret = 0; + + cleanup: + + asn1_delete_structure(&spk); + + return ret; + +} + +/* Reads RSA-PSS parameters. + */ +int +_gnutls_x509_read_rsa_pss_params(uint8_t * der, int dersize, + gnutls_x509_spki_st * params) +{ + int result; + asn1_node spk = NULL; + asn1_node c2 = NULL; + gnutls_digest_algorithm_t digest; + char oid[MAX_OID_SIZE] = ""; + int size; + unsigned int trailer; + gnutls_datum_t value = { NULL, 0 }; + + if ((result = asn1_create_element + (_gnutls_get_gnutls_asn(), "GNUTLS.RSAPSSParameters", &spk)) + != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + result = _asn1_strict_der_decode(&spk, der, dersize, NULL); + + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + size = sizeof(oid); + result = asn1_read_value(spk, "hashAlgorithm.algorithm", oid, &size); + if (result == ASN1_SUCCESS) + digest = gnutls_oid_to_digest(oid); + else if (result == ASN1_ELEMENT_NOT_FOUND) + /* The default hash algorithm is SHA-1 */ + digest = GNUTLS_DIG_SHA1; + else { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + if (digest == GNUTLS_DIG_UNKNOWN) { + gnutls_assert(); + _gnutls_debug_log("Unknown RSA-PSS hash: %s\n", oid); + result = GNUTLS_E_UNKNOWN_HASH_ALGORITHM; + goto cleanup; + } + + size = sizeof(oid); + result = asn1_read_value(spk, "maskGenAlgorithm.algorithm", oid, &size); + if (result == ASN1_SUCCESS) { + gnutls_digest_algorithm_t digest2; + + /* Error out if algorithm other than mgf1 is specified */ + if (strcmp(oid, PKIX1_RSA_PSS_MGF1_OID) != 0) { + gnutls_assert(); + _gnutls_debug_log("Unknown mask algorithm: %s\n", oid); + result = GNUTLS_E_UNKNOWN_ALGORITHM; + goto cleanup; + } + + /* Check if maskGenAlgorithm.parameters does exist and + * is identical to hashAlgorithm */ + result = _gnutls_x509_read_value(spk, "maskGenAlgorithm.parameters", &value); + if (result < 0) { + gnutls_assert(); + goto cleanup; + } + + if ((result = asn1_create_element + (_gnutls_get_pkix(), "PKIX1.AlgorithmIdentifier", &c2)) + != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + result = _asn1_strict_der_decode(&c2, value.data, value.size, NULL); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + size = sizeof(oid); + result = asn1_read_value(c2, "algorithm", oid, &size); + if (result == ASN1_SUCCESS) + digest2 = gnutls_oid_to_digest(oid); + else if (result == ASN1_ELEMENT_NOT_FOUND) + /* The default hash algorithm for mgf1 is SHA-1 */ + digest2 = GNUTLS_DIG_SHA1; + else { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + if (digest != digest2) { + gnutls_assert(); + result = GNUTLS_E_CONSTRAINT_ERROR; + goto cleanup; + } + } else if (result != ASN1_ELEMENT_NOT_FOUND) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + memset(params, 0, sizeof(gnutls_x509_spki_st)); + params->pk = GNUTLS_PK_RSA_PSS; + params->rsa_pss_dig = digest; + + result = _gnutls_x509_read_uint(spk, "saltLength", ¶ms->salt_size); + if (result == GNUTLS_E_ASN1_ELEMENT_NOT_FOUND || + result == GNUTLS_E_ASN1_VALUE_NOT_FOUND) + params->salt_size = 20; + else if (result < 0) { + gnutls_assert(); + goto cleanup; + } + + result = _gnutls_x509_read_uint(spk, "trailerField", &trailer); + if (result == GNUTLS_E_ASN1_VALUE_NOT_FOUND || + result == GNUTLS_E_ASN1_ELEMENT_NOT_FOUND) + trailer = 1; + else if (result < 0) { + gnutls_assert(); + goto cleanup; + } + if (trailer != 1) { + gnutls_assert(); + result = GNUTLS_E_CERTIFICATE_ERROR; + goto cleanup; + } + + result = 0; + cleanup: + _gnutls_free_datum(&value); + asn1_delete_structure(&c2); + asn1_delete_structure(&spk); + return result; +} + +/* reads the curve from the certificate. + * It does NOT set params_nr. + */ +int +_gnutls_x509_read_gost_params(uint8_t * der, int dersize, + gnutls_pk_params_st * params, + gnutls_pk_algorithm_t algo) +{ + int ret; + asn1_node spk = NULL; + char oid[MAX_OID_SIZE]; + int oid_size; + gnutls_ecc_curve_t curve; + gnutls_gost_paramset_t param; + + if ((ret = asn1_create_element(_gnutls_get_gnutls_asn(), + algo == GNUTLS_PK_GOST_01 ? + "GNUTLS.GOSTParametersOld" : + "GNUTLS.GOSTParameters", + &spk)) != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(ret); + } + + ret = _asn1_strict_der_decode(&spk, der, dersize, NULL); + + if (ret != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(ret); + goto cleanup; + } + + /* read the curve */ + oid_size = sizeof(oid); + ret = asn1_read_value(spk, "publicKeyParamSet", oid, &oid_size); + if (ret != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(ret); + goto cleanup; + } + + curve = gnutls_oid_to_ecc_curve(oid); + if (curve == GNUTLS_ECC_CURVE_INVALID) { + _gnutls_debug_log("Curve %s is not supported\n", oid); + gnutls_assert(); + ret = GNUTLS_E_ECC_UNSUPPORTED_CURVE; + goto cleanup; + } + + /* Read the digest */ + oid_size = sizeof(oid); + ret = asn1_read_value(spk, "digestParamSet", oid, &oid_size); + if (ret != ASN1_SUCCESS && + ret != ASN1_ELEMENT_NOT_FOUND) { + gnutls_assert(); + ret = _gnutls_asn2err(ret); + goto cleanup; + } + /* For now ignore the OID: we use pk OID instead */ + + oid_size = sizeof(oid); + ret = asn1_read_value(spk, "encryptionParamSet", oid, &oid_size); + if (ret != ASN1_SUCCESS && + ret != ASN1_ELEMENT_NOT_FOUND) { + gnutls_assert(); + ret = _gnutls_asn2err(ret); + goto cleanup; + } + + if (ret != ASN1_ELEMENT_NOT_FOUND) + param = gnutls_oid_to_gost_paramset(oid); + else + param = _gnutls_gost_paramset_default(algo); + + if (param == GNUTLS_GOST_PARAMSET_UNKNOWN) { + gnutls_assert(); + ret = param; + goto cleanup; + } + + params->curve = curve; + params->gost_params = param; + ret = 0; + + cleanup: + + asn1_delete_structure(&spk); + + return ret; + +} + +/* This function must be called after _gnutls_x509_read_params() + */ +int _gnutls_x509_read_pubkey(gnutls_pk_algorithm_t algo, uint8_t * der, + int dersize, gnutls_pk_params_st * params) +{ + int ret; + + switch (algo) { + case GNUTLS_PK_RSA: + case GNUTLS_PK_RSA_PSS: + ret = _gnutls_x509_read_rsa_pubkey(der, dersize, params); + if (ret >= 0) { + params->algo = algo; + params->params_nr = RSA_PUBLIC_PARAMS; + } + break; + case GNUTLS_PK_DSA: + if (params->params_nr != 3) /* _gnutls_x509_read_pubkey_params must have been called */ + return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + + ret = _gnutls_x509_read_dsa_pubkey(der, dersize, params); + if (ret >= 0) { + params->algo = GNUTLS_PK_DSA; + params->params_nr = DSA_PUBLIC_PARAMS; + } + break; + case GNUTLS_PK_ECDSA: + ret = _gnutls_x509_read_ecc_pubkey(der, dersize, params); + if (ret >= 0) { + params->algo = GNUTLS_PK_ECDSA; + params->params_nr = ECC_PUBLIC_PARAMS; + } + break; + case GNUTLS_PK_EDDSA_ED25519: + ret = _gnutls_x509_read_eddsa_pubkey(GNUTLS_ECC_CURVE_ED25519, der, dersize, params); + break; + case GNUTLS_PK_EDDSA_ED448: + ret = _gnutls_x509_read_eddsa_pubkey(GNUTLS_ECC_CURVE_ED448, der, dersize, params); + break; + case GNUTLS_PK_ECDH_X25519: + ret = _gnutls_x509_read_ecdh_pubkey(GNUTLS_ECC_CURVE_X25519, der, dersize, params); + break; + case GNUTLS_PK_ECDH_X448: + ret = _gnutls_x509_read_ecdh_pubkey(GNUTLS_ECC_CURVE_X448, der, dersize, params); + break; + case GNUTLS_PK_GOST_01: + case GNUTLS_PK_GOST_12_256: + case GNUTLS_PK_GOST_12_512: + ret = _gnutls_x509_read_gost_pubkey(der, dersize, params); + if (ret >= 0) { + params->algo = algo; + params->params_nr = GOST_PUBLIC_PARAMS; + } + break; + default: + ret = gnutls_assert_val(GNUTLS_E_UNIMPLEMENTED_FEATURE); + break; + } + return ret; +} + +/* This function must be called prior to _gnutls_x509_read_pubkey() + */ +int _gnutls_x509_read_pubkey_params(gnutls_pk_algorithm_t algo, + uint8_t * der, int dersize, + gnutls_pk_params_st * params) +{ + switch (algo) { + case GNUTLS_PK_RSA: + case GNUTLS_PK_EDDSA_ED25519: + case GNUTLS_PK_EDDSA_ED448: + return 0; + case GNUTLS_PK_RSA_PSS: + return _gnutls_x509_read_rsa_pss_params(der, dersize, ¶ms->spki); + case GNUTLS_PK_DSA: + return _gnutls_x509_read_dsa_params(der, dersize, params); + case GNUTLS_PK_EC: + return _gnutls_x509_read_ecc_params(der, dersize, ¶ms->curve); + case GNUTLS_PK_GOST_01: + case GNUTLS_PK_GOST_12_256: + case GNUTLS_PK_GOST_12_512: + return _gnutls_x509_read_gost_params(der, dersize, params, algo); + default: + return gnutls_assert_val(GNUTLS_E_UNIMPLEMENTED_FEATURE); + } +} + +/* This function must be called after _gnutls_x509_read_pubkey() + */ +int _gnutls_x509_check_pubkey_params(gnutls_pk_params_st * params) +{ + switch (params->algo) { + case GNUTLS_PK_RSA_PSS: { + unsigned bits; + const mac_entry_st *me; + size_t hash_size; + + if (params->spki.pk == GNUTLS_PK_UNKNOWN) /* no params present */ + return 0; + + bits = pubkey_to_bits(params); + + me = hash_to_entry(params->spki.rsa_pss_dig); + if (unlikely(me == NULL)) + return gnutls_assert_val(GNUTLS_E_PK_INVALID_PUBKEY_PARAMS); + + hash_size = _gnutls_hash_get_algo_len(me); + if (hash_size + params->spki.salt_size + 2 > (bits + 7) / 8) + return gnutls_assert_val(GNUTLS_E_PK_INVALID_PUBKEY_PARAMS); + return 0; + } + case GNUTLS_PK_RSA: + case GNUTLS_PK_DSA: + case GNUTLS_PK_ECDSA: + case GNUTLS_PK_EDDSA_ED25519: + case GNUTLS_PK_EDDSA_ED448: + case GNUTLS_PK_ECDH_X25519: + case GNUTLS_PK_ECDH_X448: + case GNUTLS_PK_GOST_01: + case GNUTLS_PK_GOST_12_256: + case GNUTLS_PK_GOST_12_512: + return 0; + default: + return gnutls_assert_val(GNUTLS_E_UNIMPLEMENTED_FEATURE); + } +} + +/* reads DSA's Y + * from the certificate + * only sets params[3] + */ +int +_gnutls_x509_read_dsa_pubkey(uint8_t * der, int dersize, + gnutls_pk_params_st * params) +{ + return _gnutls_x509_read_der_int(der, dersize, ¶ms->params[3]); +} diff --git a/lib/x509/key_encode.c b/lib/x509/key_encode.c new file mode 100644 index 0000000..8428cd1 --- /dev/null +++ b/lib/x509/key_encode.c @@ -0,0 +1,1087 @@ +/* + * Copyright (C) 2011-2012 Free Software Foundation, Inc. + * Copyright (C) 2013-2017 Red Hat + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GnuTLS. + * + * The GnuTLS is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/> + * + */ + +#include "gnutls_int.h" +#include "errors.h" +#include <global.h> +#include <libtasn1.h> +#include <datum.h> +#include "common.h" +#include "x509_int.h" +#include <num.h> +#include <pk.h> +#include <mpi.h> +#include <ecc.h> + +static int _gnutls_x509_write_rsa_pubkey(const gnutls_pk_params_st * params, + gnutls_datum_t * der); +static int _gnutls_x509_write_dsa_params(const gnutls_pk_params_st * params, + gnutls_datum_t * der); +static int _gnutls_x509_write_dsa_pubkey(const gnutls_pk_params_st * params, + gnutls_datum_t * der); +static int _gnutls_x509_write_gost_params(const gnutls_pk_params_st * params, + gnutls_datum_t * der); +static int _gnutls_x509_write_gost_pubkey(const gnutls_pk_params_st * params, + gnutls_datum_t * der); + +/* + * some x509 certificate functions that relate to MPI parameter + * setting. This writes the BIT STRING subjectPublicKey. + * Needs 2 parameters (m,e). + * + * Allocates the space used to store the DER data. + */ +static int +_gnutls_x509_write_rsa_pubkey(const gnutls_pk_params_st * params, + gnutls_datum_t * der) +{ + int result; + asn1_node spk = NULL; + + der->data = NULL; + der->size = 0; + + if (params->params_nr < RSA_PUBLIC_PARAMS) { + gnutls_assert(); + result = GNUTLS_E_INVALID_REQUEST; + goto cleanup; + } + + if ((result = asn1_create_element + (_gnutls_get_gnutls_asn(), "GNUTLS.RSAPublicKey", &spk)) + != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + result = + _gnutls_x509_write_int(spk, "modulus", params->params[0], 1); + if (result < 0) { + gnutls_assert(); + goto cleanup; + } + + result = + _gnutls_x509_write_int(spk, "publicExponent", + params->params[1], 1); + if (result < 0) { + gnutls_assert(); + goto cleanup; + } + + result = _gnutls_x509_der_encode(spk, "", der, 0); + if (result < 0) { + gnutls_assert(); + goto cleanup; + } + + result = 0; + + cleanup: + asn1_delete_structure(&spk); + + return result; +} + +/* + * some x509 certificate functions that relate to MPI parameter + * setting. This writes an ECPoint. + * + * Allocates the space used to store the DER data. + */ +int +_gnutls_x509_write_ecc_pubkey(const gnutls_pk_params_st * params, + gnutls_datum_t * der) +{ + int result; + + der->data = NULL; + der->size = 0; + + if (params->params_nr < ECC_PUBLIC_PARAMS) + return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + + result = + _gnutls_ecc_ansi_x962_export(params->curve, + params->params[ECC_X], + params->params[ECC_Y], /*&out */ + der); + if (result < 0) + return gnutls_assert_val(result); + + return 0; +} + +/* + * some x509 certificate functions that relate to MPI parameter + * setting. This writes a raw public key. + * + * Allocates the space used to store the data. + */ +int +_gnutls_x509_write_eddsa_pubkey(const gnutls_pk_params_st * params, + gnutls_datum_t * raw) +{ + int ret; + + raw->data = NULL; + raw->size = 0; + + if (params->raw_pub.size == 0) + return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + + if (params->curve != GNUTLS_ECC_CURVE_ED25519 && + params->curve != GNUTLS_ECC_CURVE_ED448) + return gnutls_assert_val(GNUTLS_E_ECC_UNSUPPORTED_CURVE); + + ret = _gnutls_set_datum(raw, params->raw_pub.data, params->raw_pub.size); + if (ret < 0) + return gnutls_assert_val(ret); + + return 0; +} + +/* + * some x509 certificate functions that relate to MPI parameter + * setting. This writes a raw public key. + * + * Allocates the space used to store the data. + */ +static int +_gnutls_x509_write_modern_ecdh_pubkey(const gnutls_pk_params_st * params, + gnutls_datum_t * raw) +{ + int ret; + + raw->data = NULL; + raw->size = 0; + + if (params->raw_pub.size == 0) + return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + + if (params->curve != GNUTLS_ECC_CURVE_X25519 && + params->curve != GNUTLS_ECC_CURVE_X448) + return gnutls_assert_val(GNUTLS_E_ECC_UNSUPPORTED_CURVE); + + ret = _gnutls_set_datum(raw, params->raw_pub.data, params->raw_pub.size); + if (ret < 0) + return gnutls_assert_val(ret); + + return 0; +} + +int +_gnutls_x509_write_gost_pubkey(const gnutls_pk_params_st * params, + gnutls_datum_t * der) +{ + bigint_t x, y; + int numlen; + int byte_size, ret; + size_t size; + int pos; + + der->data = NULL; + der->size = 0; + + if (params->params_nr < GOST_PUBLIC_PARAMS) + return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + + x = params->params[GOST_X]; + y = params->params[GOST_Y]; + numlen = gnutls_ecc_curve_get_size(params->curve); + + if (numlen == 0) + return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + + der->size = 1 + ASN1_MAX_LENGTH_SIZE + 2 * numlen; + + der->data = gnutls_malloc(der->size); + if (der->data == NULL) + return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + + memset(der->data, 0, der->size); + + der->data[0] = ASN1_TAG_OCTET_STRING; + asn1_length_der(2 * numlen, &der->data[1], &pos); + pos += 1; + + /* pad and store x */ + byte_size = (_gnutls_mpi_get_nbits(x) + 7) / 8; + if (numlen < byte_size) { + ret = gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + goto cleanup; + } + + size = numlen; + ret = _gnutls_mpi_print_le(x, &der->data[pos], &size); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + /* pad and store y */ + byte_size = (_gnutls_mpi_get_nbits(y) + 7) / 8; + if (numlen < byte_size) { + ret = gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + goto cleanup; + } + + size = numlen; + ret = _gnutls_mpi_print_le(y, &der->data[pos + numlen], &size); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + der->size = pos + 2 * numlen; + + return 0; + + cleanup: + _gnutls_free_datum(der); + return ret; +} + +int +_gnutls_x509_write_pubkey_params(const gnutls_pk_params_st * params, + gnutls_datum_t * der) +{ + switch (params->algo) { + case GNUTLS_PK_DSA: + return _gnutls_x509_write_dsa_params(params, der); + case GNUTLS_PK_RSA: + der->data = gnutls_malloc(ASN1_NULL_SIZE); + if (der->data == NULL) + return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + + memcpy(der->data, ASN1_NULL, ASN1_NULL_SIZE); + der->size = ASN1_NULL_SIZE; + return 0; + case GNUTLS_PK_RSA_PSS: + return _gnutls_x509_write_rsa_pss_params(¶ms->spki, der); + case GNUTLS_PK_ECDSA: + return _gnutls_x509_write_ecc_params(params->curve, der); + case GNUTLS_PK_EDDSA_ED25519: + case GNUTLS_PK_EDDSA_ED448: + case GNUTLS_PK_ECDH_X25519: + case GNUTLS_PK_ECDH_X448: + der->data = NULL; + der->size = 0; + + return 0; + case GNUTLS_PK_GOST_01: + case GNUTLS_PK_GOST_12_256: + case GNUTLS_PK_GOST_12_512: + return _gnutls_x509_write_gost_params(params, der); + default: + return gnutls_assert_val(GNUTLS_E_UNIMPLEMENTED_FEATURE); + } +} + +int +_gnutls_x509_write_pubkey(const gnutls_pk_params_st * params, + gnutls_datum_t * der) +{ + switch (params->algo) { + case GNUTLS_PK_DSA: + return _gnutls_x509_write_dsa_pubkey(params, der); + case GNUTLS_PK_RSA: + case GNUTLS_PK_RSA_PSS: + return _gnutls_x509_write_rsa_pubkey(params, der); + case GNUTLS_PK_ECDSA: + return _gnutls_x509_write_ecc_pubkey(params, der); + case GNUTLS_PK_EDDSA_ED25519: + case GNUTLS_PK_EDDSA_ED448: + return _gnutls_x509_write_eddsa_pubkey(params, der); + case GNUTLS_PK_ECDH_X25519: + case GNUTLS_PK_ECDH_X448: + return _gnutls_x509_write_modern_ecdh_pubkey(params, der); + case GNUTLS_PK_GOST_01: + case GNUTLS_PK_GOST_12_256: + case GNUTLS_PK_GOST_12_512: + return _gnutls_x509_write_gost_pubkey(params, der); + default: + return gnutls_assert_val(GNUTLS_E_UNIMPLEMENTED_FEATURE); + } +} + +/* + * This function writes the parameters for DSS keys. + * Needs 3 parameters (p,q,g). + * + * Allocates the space used to store the DER data. + */ +static int +_gnutls_x509_write_dsa_params(const gnutls_pk_params_st * params, + gnutls_datum_t * der) +{ + int result; + asn1_node spk = NULL; + + der->data = NULL; + der->size = 0; + + if (params->params_nr < DSA_PUBLIC_PARAMS - 1) { + gnutls_assert(); + result = GNUTLS_E_INVALID_REQUEST; + goto cleanup; + } + + if ((result = asn1_create_element + (_gnutls_get_gnutls_asn(), "GNUTLS.DSAParameters", &spk)) + != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + result = _gnutls_x509_write_int(spk, "p", params->params[0], 1); + if (result < 0) { + gnutls_assert(); + goto cleanup; + } + + result = _gnutls_x509_write_int(spk, "q", params->params[1], 1); + if (result < 0) { + gnutls_assert(); + goto cleanup; + } + + result = _gnutls_x509_write_int(spk, "g", params->params[2], 1); + if (result < 0) { + gnutls_assert(); + goto cleanup; + } + + result = _gnutls_x509_der_encode(spk, "", der, 0); + if (result < 0) { + gnutls_assert(); + goto cleanup; + } + + result = 0; + + cleanup: + asn1_delete_structure(&spk); + return result; +} + +/* + * This function writes the parameters for ECC keys. + * That is the ECParameters struct. + * + * Allocates the space used to store the DER data. + */ +int +_gnutls_x509_write_ecc_params(const gnutls_ecc_curve_t curve, + gnutls_datum_t * der) +{ + int result; + asn1_node spk = NULL; + const char *oid; + + der->data = NULL; + der->size = 0; + + oid = gnutls_ecc_curve_get_oid(curve); + if (oid == NULL) + return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + + + if ((result = asn1_create_element + (_gnutls_get_gnutls_asn(), "GNUTLS.ECParameters", &spk)) + != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + if ((result = + asn1_write_value(spk, "", "namedCurve", 1)) != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + if ((result = + asn1_write_value(spk, "namedCurve", oid, + 1)) != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + result = _gnutls_x509_der_encode(spk, "", der, 0); + if (result < 0) { + gnutls_assert(); + goto cleanup; + } + + result = 0; + + cleanup: + asn1_delete_structure(&spk); + return result; +} + +int +_gnutls_x509_write_rsa_pss_params(const gnutls_x509_spki_st *params, + gnutls_datum_t *der) +{ + int result; + asn1_node spk = NULL; + asn1_node c2 = NULL; + const char *oid; + gnutls_datum_t tmp = { NULL, 0 }; + + der->data = NULL; + der->size = 0; + + if (params->pk != GNUTLS_PK_RSA_PSS) + return 0; + + /* refuse to write parameters we cannot read */ + if (gnutls_pk_to_sign(GNUTLS_PK_RSA_PSS, params->rsa_pss_dig) == GNUTLS_SIGN_UNKNOWN) + return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + + if ((result = asn1_create_element + (_gnutls_get_gnutls_asn(), "GNUTLS.RSAPSSParameters", &spk)) + != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + oid = gnutls_digest_get_oid(params->rsa_pss_dig); + + if ((result = asn1_write_value(spk, "hashAlgorithm.algorithm", oid, 1)) + != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + if ((result = asn1_write_value(spk, "hashAlgorithm.parameters", NULL, 0)) + != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + if ((result = + asn1_write_value(spk, "maskGenAlgorithm.algorithm", + PKIX1_RSA_PSS_MGF1_OID, 1)) + != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + if ((result = asn1_create_element + (_gnutls_get_pkix(), "PKIX1.AlgorithmIdentifier", &c2)) + != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + if ((result = asn1_write_value(c2, "algorithm", oid, 1)) + != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + if ((result = asn1_write_value(c2, "parameters", NULL, 0)) + != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + result = _gnutls_x509_der_encode(c2, "", &tmp, 0); + if (result < 0) { + gnutls_assert(); + goto cleanup; + } + + if ((result = + asn1_write_value(spk, "maskGenAlgorithm.parameters", + tmp.data, tmp.size)) + != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + result = _gnutls_x509_write_uint32(spk, "saltLength", + params->salt_size); + if (result < 0) { + gnutls_assert(); + goto cleanup; + } + + result = _gnutls_x509_write_uint32(spk, "trailerField", 1); + if (result < 0) { + gnutls_assert(); + goto cleanup; + } + + result = _gnutls_x509_der_encode(spk, "", der, 0); + if (result < 0) { + gnutls_assert(); + goto cleanup; + } + + result = 0; + + cleanup: + _gnutls_free_datum(&tmp); + asn1_delete_structure(&c2); + asn1_delete_structure(&spk); + return result; +} + +static int +_gnutls_x509_write_gost_params(const gnutls_pk_params_st * params, + gnutls_datum_t * der) +{ + int result; + asn1_node spk = NULL; + const char *oid; + + der->data = NULL; + der->size = 0; + + oid = gnutls_ecc_curve_get_oid(params->curve); + if (oid == NULL) + return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + + + if ((result = asn1_create_element + (_gnutls_get_gnutls_asn(), + params->algo == GNUTLS_PK_GOST_01 ? + "GNUTLS.GOSTParametersOld" : + "GNUTLS.GOSTParameters", &spk)) + != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + if ((result = + asn1_write_value(spk, "publicKeyParamSet", oid, + 1)) != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + /* For compatibility per R 1323565.1.023—2018 provide digest OID only + * for GOST-2001 keys or GOST-2012 keys with CryptoPro curves. Do not + * set this optional parameter for TC26 curves */ + if (params->algo == GNUTLS_PK_GOST_01) + oid = HASH_OID_GOST_R_3411_94_CRYPTOPRO_PARAMS; + else if (params->algo == GNUTLS_PK_GOST_12_256 && + (params->curve == GNUTLS_ECC_CURVE_GOST256CPA || + params->curve == GNUTLS_ECC_CURVE_GOST256CPB || + params->curve == GNUTLS_ECC_CURVE_GOST256CPC || + params->curve == GNUTLS_ECC_CURVE_GOST256CPXA || + params->curve == GNUTLS_ECC_CURVE_GOST256CPXB)) + oid = HASH_OID_STREEBOG_256; + else if (params->algo == GNUTLS_PK_GOST_12_512 && + (params->curve == GNUTLS_ECC_CURVE_GOST512A || + params->curve == GNUTLS_ECC_CURVE_GOST512B)) + oid = HASH_OID_STREEBOG_512; + else + oid = NULL; + + if ((result = asn1_write_value(spk, "digestParamSet", oid, oid ? 1 : 0)) != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + oid = gnutls_gost_paramset_get_oid(params->gost_params); + if (oid == NULL) { + gnutls_assert(); + result = GNUTLS_E_INVALID_REQUEST; + goto cleanup; + } + + if (params->algo == GNUTLS_PK_GOST_01) { + if (params->gost_params == _gnutls_gost_paramset_default(params->algo)) + oid = NULL; + + if ((result = + asn1_write_value(spk, "encryptionParamSet", oid, + oid ? 1 : 0)) != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + } + + result = _gnutls_x509_der_encode(spk, "", der, 0); + if (result < 0) { + gnutls_assert(); + goto cleanup; + } + + result = 0; + + cleanup: + asn1_delete_structure(&spk); + return result; +} + +/* + * This function writes the public parameters for DSS keys. + * Needs 1 parameter (y). + * + * Allocates the space used to store the DER data. + */ +static int +_gnutls_x509_write_dsa_pubkey(const gnutls_pk_params_st * params, + gnutls_datum_t * der) +{ + int result; + asn1_node spk = NULL; + + der->data = NULL; + der->size = 0; + + if (params->params_nr < DSA_PUBLIC_PARAMS) { + gnutls_assert(); + result = GNUTLS_E_INVALID_REQUEST; + goto cleanup; + } + + if ((result = asn1_create_element + (_gnutls_get_gnutls_asn(), "GNUTLS.DSAPublicKey", &spk)) + != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + result = _gnutls_x509_write_int(spk, "", params->params[3], 1); + if (result < 0) { + gnutls_assert(); + goto cleanup; + } + + result = _gnutls_x509_der_encode(spk, "", der, 0); + if (result < 0) { + gnutls_assert(); + goto cleanup; + } + + result = 0; + + cleanup: + asn1_delete_structure(&spk); + return result; +} + +/* Encodes the RSA parameters into an ASN.1 RSA private key structure. + */ +static int +_gnutls_asn1_encode_rsa(asn1_node * c2, gnutls_pk_params_st * params) +{ + int result, ret; + uint8_t null = '\0'; + gnutls_pk_params_st pk_params; + + /* we do copy the parameters into a new structure to run _gnutls_pk_fixup, + * i.e., regenerate some parameters in case they were broken */ + gnutls_pk_params_init(&pk_params); + + ret = _gnutls_pk_params_copy(&pk_params, params); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + ret = + _gnutls_pk_fixup(GNUTLS_PK_RSA, GNUTLS_EXPORT, &pk_params); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + /* Ok. Now we have the data. Create the asn1 structures + */ + + /* first make sure that no previously allocated data are leaked */ + if (*c2 != NULL) { + asn1_delete_structure(c2); + *c2 = NULL; + } + + if ((result = asn1_create_element + (_gnutls_get_gnutls_asn(), "GNUTLS.RSAPrivateKey", c2)) + != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(result); + goto cleanup; + } + + /* Write PRIME + */ + ret = + _gnutls_x509_write_int(*c2, "modulus", + params->params[RSA_MODULUS], 1); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = + _gnutls_x509_write_int(*c2, "publicExponent", + params->params[RSA_PUB], 1); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = + _gnutls_x509_write_key_int(*c2, "privateExponent", + params->params[RSA_PRIV], 1); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = + _gnutls_x509_write_key_int(*c2, "prime1", + params->params[RSA_PRIME1], 1); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = + _gnutls_x509_write_key_int(*c2, "prime2", + params->params[RSA_PRIME2], 1); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = + _gnutls_x509_write_key_int(*c2, "coefficient", + params->params[RSA_COEF], 1); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = + _gnutls_x509_write_key_int(*c2, "exponent1", + params->params[RSA_E1], 1); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = + _gnutls_x509_write_key_int(*c2, "exponent2", + params->params[RSA_E2], 1); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + if ((result = asn1_write_value(*c2, "otherPrimeInfos", + NULL, 0)) != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(result); + goto cleanup; + } + + if ((result = + asn1_write_value(*c2, "version", &null, 1)) != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(result); + goto cleanup; + } + + ret = 0; + + cleanup: + if (ret < 0) + asn1_delete_structure2(c2, ASN1_DELETE_FLAG_ZEROIZE); + + gnutls_pk_params_clear(&pk_params); + gnutls_pk_params_release(&pk_params); + return ret; +} + +/* Encodes the ECC parameters into an ASN.1 ECPrivateKey structure. + */ +static int +_gnutls_asn1_encode_ecc(asn1_node * c2, gnutls_pk_params_st * params) +{ + int ret; + uint8_t one = '\x01'; + gnutls_datum_t pubkey = { NULL, 0 }; + const char *oid; + + oid = gnutls_ecc_curve_get_oid(params->curve); + if (oid == NULL) + return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + + /* first make sure that no previously allocated data are leaked */ + if (*c2 != NULL) { + asn1_delete_structure(c2); + *c2 = NULL; + } + + if ((ret = asn1_create_element + (_gnutls_get_gnutls_asn(), "GNUTLS.ECPrivateKey", c2)) + != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(ret); + goto cleanup; + } + + if ((ret = + asn1_write_value(*c2, "Version", &one, 1)) != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(ret); + goto cleanup; + } + + if (curve_is_eddsa(params->curve) || + curve_is_modern_ecdh(params->curve)) { + if (params->raw_pub.size == 0 || params->raw_priv.size == 0) + return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + ret = + asn1_write_value(*c2, "privateKey", params->raw_priv.data, params->raw_priv.size); + if (ret != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(ret); + goto cleanup; + } + + ret = + asn1_write_value(*c2, "publicKey", params->raw_pub.data, params->raw_pub.size*8); + if (ret != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(ret); + goto cleanup; + } + } else { + if (params->params_nr != ECC_PRIVATE_PARAMS) + return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + + ret = + _gnutls_ecc_ansi_x962_export(params->curve, + params->params[ECC_X], + params->params[ECC_Y], &pubkey); + if (ret < 0) + return gnutls_assert_val(ret); + + ret = + _gnutls_x509_write_key_int(*c2, "privateKey", + params->params[ECC_K], 1); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + if ((ret = + asn1_write_value(*c2, "publicKey", pubkey.data, + pubkey.size * 8)) != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(ret); + goto cleanup; + } + } + + /* write our choice */ + if ((ret = + asn1_write_value(*c2, "parameters", "namedCurve", + 1)) != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(ret); + goto cleanup; + } + + if ((ret = + asn1_write_value(*c2, "parameters.namedCurve", oid, + 1)) != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(ret); + goto cleanup; + } + + _gnutls_free_datum(&pubkey); + return 0; + +cleanup: + asn1_delete_structure2(c2, ASN1_DELETE_FLAG_ZEROIZE); + _gnutls_free_datum(&pubkey); + + return ret; +} + +static int +_gnutls_asn1_encode_gost(asn1_node * c2, gnutls_pk_params_st * params) +{ + int ret; + const char *oid; + + oid = gnutls_pk_get_oid(params->algo); + + if (params->params_nr != GOST_PRIVATE_PARAMS || oid == NULL) + return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + + /* first make sure that no previously allocated data are leaked */ + if (*c2 != NULL) { + asn1_delete_structure(c2); + *c2 = NULL; + } + + if ((ret = asn1_create_element + (_gnutls_get_gnutls_asn(), "GNUTLS.GOSTPrivateKey", c2)) + != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(ret); + goto cleanup; + } + + ret = + _gnutls_x509_write_key_int_le(*c2, "", params->params[GOST_K]); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + + return 0; + +cleanup: + asn1_delete_structure2(c2, ASN1_DELETE_FLAG_ZEROIZE); + + return ret; +} + +/* Encodes the DSA parameters into an ASN.1 DSAPrivateKey structure. + */ +static int +_gnutls_asn1_encode_dsa(asn1_node * c2, gnutls_pk_params_st * params) +{ + int result, ret; + const uint8_t null = '\0'; + + /* first make sure that no previously allocated data are leaked */ + if (*c2 != NULL) { + asn1_delete_structure(c2); + *c2 = NULL; + } + + if ((result = asn1_create_element + (_gnutls_get_gnutls_asn(), "GNUTLS.DSAPrivateKey", c2)) + != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + /* Write PRIME + */ + ret = + _gnutls_x509_write_int(*c2, "p", + params->params[DSA_P], 1); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = + _gnutls_x509_write_int(*c2, "q", + params->params[DSA_Q], 1); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = + _gnutls_x509_write_int(*c2, "g", + params->params[DSA_G], 1); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = + _gnutls_x509_write_int(*c2, "Y", + params->params[DSA_Y], 1); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = + _gnutls_x509_write_key_int(*c2, "priv", + params->params[DSA_X], 1); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + if ((result = + asn1_write_value(*c2, "version", &null, 1)) != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(result); + goto cleanup; + } + + return 0; + +cleanup: + asn1_delete_structure2(c2, ASN1_DELETE_FLAG_ZEROIZE); + + return ret; +} + +int _gnutls_asn1_encode_privkey(asn1_node * c2, + gnutls_pk_params_st * params) +{ + switch (params->algo) { + case GNUTLS_PK_RSA: + case GNUTLS_PK_RSA_PSS: + return _gnutls_asn1_encode_rsa(c2, params); + case GNUTLS_PK_DSA: + return _gnutls_asn1_encode_dsa(c2, params); + case GNUTLS_PK_ECDSA: + case GNUTLS_PK_EDDSA_ED25519: + case GNUTLS_PK_EDDSA_ED448: + case GNUTLS_PK_ECDH_X25519: + case GNUTLS_PK_ECDH_X448: + return _gnutls_asn1_encode_ecc(c2, params); + case GNUTLS_PK_GOST_01: + case GNUTLS_PK_GOST_12_256: + case GNUTLS_PK_GOST_12_512: + return _gnutls_asn1_encode_gost(c2, params); + default: + return GNUTLS_E_UNIMPLEMENTED_FEATURE; + } +} diff --git a/lib/x509/krb5.c b/lib/x509/krb5.c new file mode 100644 index 0000000..b26d07d --- /dev/null +++ b/lib/x509/krb5.c @@ -0,0 +1,338 @@ +/* + * Copyright (C) 2015 Red Hat, Inc. + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GnuTLS. + * + * The GnuTLS is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/> + * + */ + +#include <config.h> +#include <gnutls/gnutls.h> +#include <libtasn1.h> +#include <string.h> +#include <stdint.h> +#include <stdlib.h> +#include <errors.h> +#include "krb5.h" +#include "common.h" + +#define _gnutls_asn2err(x) GNUTLS_E_ASN1_DER_ERROR + +#define MAX_COMPONENTS 6 + +typedef struct krb5_principal_data { + char *realm; + char *data[MAX_COMPONENTS]; + uint32_t length; + int8_t type; +} krb5_principal_data; + +extern const asn1_static_node krb5_asn1_tab[]; + +static void cleanup_principal(krb5_principal_data * princ) +{ + unsigned i; + if (princ) { + gnutls_free(princ->realm); + for (i = 0; i < princ->length; i++) + gnutls_free(princ->data[i]); + memset(princ, 0, sizeof(*princ)); + gnutls_free(princ); + } +} + +static krb5_principal_data *name_to_principal(const char *_name) +{ + krb5_principal_data *princ; + char *p, *p2, *sp; + unsigned pos = 0; + char *name = NULL; + + princ = gnutls_calloc(1, sizeof(struct krb5_principal_data)); + if (princ == NULL) + return NULL; + + name = gnutls_strdup(_name); + if (name == NULL) { + gnutls_assert(); + goto fail; + } + + p = strrchr(name, '@'); + p2 = strchr(name, '@'); + if (p == NULL) { + /* unknown name type */ + gnutls_assert(); + goto fail; + } + + princ->realm = gnutls_strdup(p + 1); + if (princ->realm == NULL) { + gnutls_assert(); + goto fail; + } + *p = 0; + + if (p == p2) { + p = strtok_r(name, "/", &sp); + while (p) { + if (pos == MAX_COMPONENTS) { + _gnutls_debug_log + ("%s: Cannot parse names with more than %d components\n", + __func__, MAX_COMPONENTS); + goto fail; + } + + princ->data[pos] = gnutls_strdup(p); + if (princ->data[pos] == NULL) { + gnutls_assert(); + goto fail; + } + + princ->length++; + pos++; + + p = strtok_r(NULL, "/", &sp); + } + + if ((princ->length == 2) + && (strcmp(princ->data[0], "krbtgt") == 0)) { + princ->type = 2; /* KRB_NT_SRV_INST */ + } else { + princ->type = 1; /* KRB_NT_PRINCIPAL */ + } + } else { /* enterprise */ + princ->data[0] = gnutls_strdup(name); + if (princ->data[0] == NULL) { + gnutls_assert(); + goto fail; + } + + princ->length++; + princ->type = 10; /* KRB_NT_ENTERPRISE */ + } + + goto cleanup; + fail: + cleanup_principal(princ); + princ = NULL; + + cleanup: + gnutls_free(name); + return princ; +} + +int _gnutls_krb5_principal_to_der(const char *name, gnutls_datum_t * der) +{ + int ret, result; + asn1_node c2 = NULL; + krb5_principal_data *princ; + unsigned i; + + princ = name_to_principal(name); + if (princ == NULL) { + gnutls_assert(); + ret = GNUTLS_E_PARSING_ERROR; + goto cleanup; + } + + result = + asn1_create_element(_gnutls_get_gnutls_asn(), + "GNUTLS.KRB5PrincipalName", &c2); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(result); + goto cleanup; + } + + result = + asn1_write_value(c2, "realm", princ->realm, strlen(princ->realm)); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(result); + goto cleanup; + } + + result = + asn1_write_value(c2, "principalName.name-type", &princ->type, 1); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(result); + goto cleanup; + } + + for (i = 0; i < princ->length; i++) { + result = + asn1_write_value(c2, "principalName.name-string", "NEW", 1); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(result); + goto cleanup; + } + + result = + asn1_write_value(c2, + "principalName.name-string.?LAST", + princ->data[i], strlen(princ->data[i])); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(result); + goto cleanup; + } + } + + ret = _gnutls_x509_der_encode(c2, "", der, 0); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = 0; + cleanup: + cleanup_principal(princ); + asn1_delete_structure(&c2); + return ret; +} + +static int principal_to_str(asn1_node c2, gnutls_buffer_st * str) +{ + gnutls_datum_t realm = { NULL, 0 }; + gnutls_datum_t component = { NULL, 0 }; + unsigned char name_type[2]; + int ret, result, len; + unsigned i; + char val[128]; + + ret = _gnutls_x509_read_value(c2, "realm", &realm); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + len = sizeof(name_type); + result = + asn1_read_value(c2, "principalName.name-type", name_type, &len); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(result); + goto cleanup; + } + + if (len != 1 + || (name_type[0] != 1 && name_type[0] != 2 && name_type[0] != 10)) { + ret = GNUTLS_E_INVALID_REQUEST; + goto cleanup; + } + + for (i = 0;; i++) { + snprintf(val, sizeof(val), "principalName.name-string.?%u", + i + 1); + ret = _gnutls_x509_read_value(c2, val, &component); + if (ret == GNUTLS_E_ASN1_VALUE_NOT_FOUND + || ret == GNUTLS_E_ASN1_ELEMENT_NOT_FOUND) + break; + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + if (i > 0) { + ret = _gnutls_buffer_append_data(str, "/", 1); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + } + + ret = + _gnutls_buffer_append_data(str, component.data, + component.size); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + _gnutls_free_datum(&component); + } + + ret = _gnutls_buffer_append_data(str, "@", 1); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = _gnutls_buffer_append_data(str, realm.data, realm.size); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = 0; + cleanup: + _gnutls_free_datum(&component); + gnutls_free(realm.data); + return ret; +} + +int _gnutls_krb5_der_to_principal(const gnutls_datum_t * der, + gnutls_datum_t * name) +{ + int ret, result; + asn1_node c2 = NULL; + gnutls_buffer_st str; + + _gnutls_buffer_init(&str); + + result = + asn1_create_element(_gnutls_get_gnutls_asn(), + "GNUTLS.KRB5PrincipalName", &c2); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(result); + goto cleanup; + } + + result = asn1_der_decoding(&c2, der->data, der->size, NULL); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(result); + goto cleanup; + } + + ret = principal_to_str(c2, &str); + if (ret < 0) { + /* for some reason we cannot convert to a human readable string + * the principal. Then we use the #HEX format. + */ + _gnutls_buffer_reset(&str); + ret = _gnutls_buffer_append_data(&str, "#", 1); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + _gnutls_buffer_hexprint(&str, der->data, der->size); + } + + asn1_delete_structure(&c2); + return _gnutls_buffer_to_datum(&str, name, 1); + + cleanup: + _gnutls_buffer_clear(&str); + asn1_delete_structure(&c2); + return ret; +} diff --git a/lib/x509/krb5.h b/lib/x509/krb5.h new file mode 100644 index 0000000..815bb28 --- /dev/null +++ b/lib/x509/krb5.h @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2015 Red Hat, Inc. + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GnuTLS. + * + * The GnuTLS is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/> + * + */ + +#ifndef GNUTLS_LIB_X509_KRB5_H +#define GNUTLS_LIB_X509_KRB5_H + +int _gnutls_krb5_principal_to_der(const char *name, gnutls_datum_t * der); +int _gnutls_krb5_der_to_principal(const gnutls_datum_t * der, gnutls_datum_t *name); + +#endif /* GNUTLS_LIB_X509_KRB5_H */ diff --git a/lib/x509/mpi.c b/lib/x509/mpi.c new file mode 100644 index 0000000..c0751df --- /dev/null +++ b/lib/x509/mpi.c @@ -0,0 +1,451 @@ +/* + * Copyright (C) 2003-2012 Free Software Foundation, Inc. + * Copyright (C) 2015-2017 Red Hat, Inc. + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GnuTLS. + * + * The GnuTLS is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/> + * + */ + +#include "gnutls_int.h" +#include "errors.h" +#include <global.h> +#include <libtasn1.h> +#include <datum.h> +#include "common.h" +#include "x509_int.h" +#include <num.h> +#include <limits.h> + +/* Reads an Integer from the DER encoded data + */ + +int _gnutls_x509_read_der_int(uint8_t * der, int dersize, bigint_t * out) +{ + int result; + asn1_node spk = NULL; + + /* == INTEGER */ + if ((result = asn1_create_element + (_gnutls_get_gnutls_asn(), "GNUTLS.DSAPublicKey", + &spk)) != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + result = _asn1_strict_der_decode(&spk, der, dersize, NULL); + + if (result != ASN1_SUCCESS) { + gnutls_assert(); + asn1_delete_structure(&spk); + return _gnutls_asn2err(result); + } + + /* Read Y */ + + if ((result = _gnutls_x509_read_int(spk, "", out)) < 0) { + gnutls_assert(); + asn1_delete_structure(&spk); + return _gnutls_asn2err(result); + } + + asn1_delete_structure(&spk); + + return 0; + +} + +int _gnutls_x509_read_der_uint(uint8_t * der, int dersize, unsigned int *out) +{ + int result; + asn1_node spk = NULL; + + /* == INTEGER */ + if ((result = asn1_create_element + (_gnutls_get_gnutls_asn(), "GNUTLS.DSAPublicKey", + &spk)) != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + result = _asn1_strict_der_decode(&spk, der, dersize, NULL); + + if (result != ASN1_SUCCESS) { + gnutls_assert(); + asn1_delete_structure(&spk); + return _gnutls_asn2err(result); + } + + /* Read Y */ + + if ((result = _gnutls_x509_read_uint(spk, "", out)) < 0) { + gnutls_assert(); + asn1_delete_structure(&spk); + return _gnutls_asn2err(result); + } + + asn1_delete_structure(&spk); + + return 0; + +} + + +/* Extracts DSA and RSA parameters from a certificate. + */ +int +_gnutls_get_asn_mpis(asn1_node asn, const char *root, + gnutls_pk_params_st * params) +{ + int result; + char name[256]; + gnutls_datum_t tmp = { NULL, 0 }; + gnutls_pk_algorithm_t pk_algorithm; + gnutls_ecc_curve_t curve; + + gnutls_pk_params_init(params); + + result = _gnutls_x509_get_pk_algorithm(asn, root, &curve, NULL); + if (result < 0) { + gnutls_assert(); + return result; + } + + pk_algorithm = result; + params->curve = curve; + params->algo = pk_algorithm; + + /* Read the algorithm's parameters + */ + _asnstr_append_name(name, sizeof(name), root, + ".algorithm.parameters"); + + if (pk_algorithm != GNUTLS_PK_RSA && + pk_algorithm != GNUTLS_PK_EDDSA_ED25519 && pk_algorithm != GNUTLS_PK_ECDH_X25519 && + pk_algorithm != GNUTLS_PK_EDDSA_ED448 && pk_algorithm != GNUTLS_PK_ECDH_X448) { + /* RSA and EdDSA do not use parameters */ + result = _gnutls_x509_read_value(asn, name, &tmp); + if (pk_algorithm == GNUTLS_PK_RSA_PSS && + (result == GNUTLS_E_ASN1_VALUE_NOT_FOUND || result == GNUTLS_E_ASN1_ELEMENT_NOT_FOUND)) { + goto skip_params; + } + if (result < 0) { + gnutls_assert(); + goto error; + } + + result = + _gnutls_x509_read_pubkey_params(pk_algorithm, + tmp.data, tmp.size, + params); + if (result < 0) { + gnutls_assert(); + goto error; + } + + _gnutls_free_datum(&tmp); + } + + skip_params: + /* Now read the public key */ + _asnstr_append_name(name, sizeof(name), root, ".subjectPublicKey"); + + result = _gnutls_x509_read_value(asn, name, &tmp); + if (result < 0) { + gnutls_assert(); + goto error; + } + + if ((result = + _gnutls_x509_read_pubkey(pk_algorithm, tmp.data, tmp.size, + params)) < 0) { + gnutls_assert(); + goto error; + } + + result = _gnutls_x509_check_pubkey_params(params); + if (result < 0) { + gnutls_assert(); + goto error; + } + + result = 0; + + error: + if (result < 0) + gnutls_pk_params_release(params); + _gnutls_free_datum(&tmp); + return result; +} + +/* Extracts DSA and RSA parameters from a certificate. + */ +int +_gnutls_x509_crt_get_mpis(gnutls_x509_crt_t cert, + gnutls_pk_params_st * params) +{ + /* Read the algorithm's OID + */ + return _gnutls_get_asn_mpis(cert->cert, + "tbsCertificate.subjectPublicKeyInfo", + params); +} + +/* Extracts DSA and RSA parameters from a certificate. + */ +int +_gnutls_x509_crq_get_mpis(gnutls_x509_crq_t cert, + gnutls_pk_params_st * params) +{ + /* Read the algorithm's OID + */ + return _gnutls_get_asn_mpis(cert->crq, + "certificationRequestInfo.subjectPKInfo", + params); +} + +/* + * This function reads and decodes the parameters for DSS or RSA keys. + * This is the "signatureAlgorithm" fields. + */ +int +_gnutls_x509_read_pkalgo_params(asn1_node src, const char *src_name, + gnutls_x509_spki_st *spki, unsigned is_sig) +{ + int result; + char name[128]; + char oid[MAX_OID_SIZE]; + int oid_size; + + memset(spki, 0, sizeof(*spki)); + + _gnutls_str_cpy(name, sizeof(name), src_name); + _gnutls_str_cat(name, sizeof(name), ".algorithm"); + + oid_size = sizeof(oid); + result = asn1_read_value(src, name, oid, &oid_size); + + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + if (strcmp (oid, PK_PKIX1_RSA_PSS_OID) == 0) { + gnutls_datum_t tmp = { NULL, 0 }; + + _gnutls_str_cpy(name, sizeof(name), src_name); + _gnutls_str_cat(name, sizeof(name), ".parameters"); + + result = _gnutls_x509_read_value(src, name, &tmp); + if (result < 0) { + if (!is_sig) { + if (result == GNUTLS_E_ASN1_ELEMENT_NOT_FOUND || + result != GNUTLS_E_ASN1_VALUE_NOT_FOUND) { + /* it is ok to not have parameters in SPKI, but + * not in signatures */ + return 0; + } + } + + return gnutls_assert_val(result); + } + + result = _gnutls_x509_read_rsa_pss_params(tmp.data, tmp.size, + spki); + _gnutls_free_datum(&tmp); + + if (result < 0) + gnutls_assert(); + + return result; + } + + return 0; +} + +static int write_oid_and_params(asn1_node dst, const char *dst_name, const char *oid, gnutls_x509_spki_st *params) +{ + int result; + char name[128]; + + if (params == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + _gnutls_str_cpy(name, sizeof(name), dst_name); + _gnutls_str_cat(name, sizeof(name), ".algorithm"); + + /* write the OID. + */ + result = asn1_write_value(dst, name, oid, 1); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + _gnutls_str_cpy(name, sizeof(name), dst_name); + _gnutls_str_cat(name, sizeof(name), ".parameters"); + + if (params->pk == GNUTLS_PK_RSA) + result = + asn1_write_value(dst, name, ASN1_NULL, ASN1_NULL_SIZE); + else if (params->pk == GNUTLS_PK_RSA_PSS) { + gnutls_datum_t tmp = { NULL, 0 }; + + result = _gnutls_x509_write_rsa_pss_params(params, &tmp); + if (result < 0) + return gnutls_assert_val(result); + + result = asn1_write_value(dst, name, tmp.data, tmp.size); + _gnutls_free_datum(&tmp); + } else + result = asn1_write_value(dst, name, NULL, 0); + + if (result != ASN1_SUCCESS && result != ASN1_ELEMENT_NOT_FOUND) { + /* Here we ignore the element not found error, since this + * may have been disabled before. + */ + gnutls_assert(); + return _gnutls_asn2err(result); + } + + return 0; +} + +int +_gnutls_x509_write_spki_params(asn1_node dst, const char *dst_name, + gnutls_x509_spki_st *params) +{ + const char *oid; + + if (params->legacy && params->pk == GNUTLS_PK_RSA) + oid = PK_PKIX1_RSA_OID; + else if (params->pk == GNUTLS_PK_RSA_PSS) + oid = PK_PKIX1_RSA_PSS_OID; + else + oid = gnutls_pk_get_oid(params->pk); + + if (oid == NULL) { + gnutls_assert(); + _gnutls_debug_log + ("Cannot find OID for public key algorithm %s\n", + gnutls_pk_get_name(params->pk)); + return GNUTLS_E_INVALID_REQUEST; + } + + return write_oid_and_params(dst, dst_name, oid, params); +} + +int +_gnutls_x509_write_sign_params(asn1_node dst, const char *dst_name, + const gnutls_sign_entry_st *se, gnutls_x509_spki_st *params) +{ + const char *oid; + + if (params->legacy && params->pk == GNUTLS_PK_RSA) + oid = PK_PKIX1_RSA_OID; + else if (params->pk == GNUTLS_PK_RSA_PSS) + oid = PK_PKIX1_RSA_PSS_OID; + else + oid = se->oid; + + if (oid == NULL) { + gnutls_assert(); + _gnutls_debug_log + ("Cannot find OID for sign algorithm %s\n", + se->name); + return GNUTLS_E_INVALID_REQUEST; + } + + return write_oid_and_params(dst, dst_name, oid, params); +} + +/* this function reads a (small) unsigned integer + * from asn1 structs. Combines the read and the conversion + * steps. + */ +int +_gnutls_x509_read_uint(asn1_node node, const char *value, + unsigned int *ret) +{ + int len, result; + uint8_t *tmpstr; + + len = 0; + result = asn1_read_value(node, value, NULL, &len); + if (result != ASN1_MEM_ERROR) { + return _gnutls_asn2err(result); + } + + tmpstr = gnutls_malloc(len); + if (tmpstr == NULL) { + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; + } + + result = asn1_read_value(node, value, tmpstr, &len); + + if (result != ASN1_SUCCESS) { + gnutls_assert(); + gnutls_free(tmpstr); + return _gnutls_asn2err(result); + } + + if (len == 1) + *ret = tmpstr[0]; + else if (len == 2) + *ret = _gnutls_read_uint16(tmpstr); + else if (len == 3) + *ret = _gnutls_read_uint24(tmpstr); + else if (len == 4) + *ret = _gnutls_read_uint32(tmpstr); + else { + gnutls_assert(); + gnutls_free(tmpstr); + return GNUTLS_E_INTERNAL_ERROR; + } + + gnutls_free(tmpstr); + + return 0; +} + +/* Writes the specified integer into the specified node. + */ +int +_gnutls_x509_write_uint32(asn1_node node, const char *value, uint32_t num) +{ + uint8_t tmpstr[5]; + int result; + + tmpstr[0] = 0; + _gnutls_write_uint32(num, tmpstr+1); + + if (tmpstr[1] > SCHAR_MAX) { + result = asn1_write_value(node, value, tmpstr, 5); + } else { + result = asn1_write_value(node, value, tmpstr+1, 4); + } + + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + return 0; +} diff --git a/lib/x509/name_constraints.c b/lib/x509/name_constraints.c new file mode 100644 index 0000000..ba37674 --- /dev/null +++ b/lib/x509/name_constraints.c @@ -0,0 +1,1404 @@ +/* + * Copyright (C) 2014-2016 Free Software Foundation, Inc. + * Copyright (C) 2016 Red Hat, Inc. + * + * Authors: Nikos Mavrogiannopoulos, Daiki Ueno, Martin Ukrop + * + * This file is part of GnuTLS. + * + * The GnuTLS is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/> + * + */ + +/* Functions on X.509 Certificate parsing + */ + +#include "gnutls_int.h" +#include <datum.h> +#include <global.h> +#include "errors.h" +#include <common.h> +#include <x509.h> +#include <gnutls/x509-ext.h> +#include <x509_b64.h> +#include <x509_int.h> +#include <libtasn1.h> + +#include "ip.h" +#include "ip-in-cidr.h" + +// for documentation see the implementation +static int name_constraints_intersect_nodes(name_constraints_node_st * nc1, + name_constraints_node_st * nc2, + name_constraints_node_st ** intersection); + +/*- + * is_nc_empty: + * @nc: name constraints structure + * @type: type (gnutls_x509_subject_alt_name_t) + * + * Test whether given name constraints structure has any constraints (permitted + * or excluded) of a given type. @nc must be allocated (not NULL) before the call. + * + * Returns: 0 if @nc contains constraints of type @type, 1 otherwise + -*/ +static unsigned is_nc_empty(struct gnutls_name_constraints_st* nc, unsigned type) +{ + name_constraints_node_st *t; + + if (nc->permitted == NULL && nc->excluded == NULL) + return 1; + + t = nc->permitted; + while (t != NULL) { + if (t->type == type) + return 0; + t = t->next; + } + + t = nc->excluded; + while (t != NULL) { + if (t->type == type) + return 0; + t = t->next; + } + + /* no constraint for that type exists */ + return 1; +} + +/*- + * validate_name_constraints_node: + * @type: type of name constraints + * @name: datum of name constraint + * + * Check the validity of given name constraints node (@type and @name). + * The supported types are GNUTLS_SAN_DNSNAME, GNUTLS_SAN_RFC822NAME, + * GNUTLS_SAN_DN, GNUTLS_SAN_URI and GNUTLS_SAN_IPADDRESS. + * + * CIDR ranges are checked for correct length (IPv4/IPv6) and correct mask format. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value. + -*/ +static int validate_name_constraints_node(gnutls_x509_subject_alt_name_t type, + const gnutls_datum_t* name) +{ + if (type != GNUTLS_SAN_DNSNAME && type != GNUTLS_SAN_RFC822NAME && + type != GNUTLS_SAN_DN && type != GNUTLS_SAN_URI && + type != GNUTLS_SAN_IPADDRESS && + type != GNUTLS_SAN_OTHERNAME_MSUSERPRINCIPAL) { + return gnutls_assert_val(GNUTLS_E_X509_UNKNOWN_SAN); + } + + if (type == GNUTLS_SAN_IPADDRESS) { + if (name->size != 8 && name->size != 32) + return gnutls_assert_val(GNUTLS_E_ILLEGAL_PARAMETER); + int prefix = _gnutls_mask_to_prefix(name->data + name->size/2, name->size/2); + if (prefix < 0) + return gnutls_assert_val(GNUTLS_E_MALFORMED_CIDR); + } + + return GNUTLS_E_SUCCESS; +} + +int _gnutls_extract_name_constraints(asn1_node c2, const char *vstr, + name_constraints_node_st ** _nc) +{ + int ret; + char tmpstr[128]; + unsigned indx; + gnutls_datum_t tmp = { NULL, 0 }; + unsigned int type; + struct name_constraints_node_st *nc, *prev; + + prev = *_nc; + if (prev != NULL) { + while(prev->next != NULL) + prev = prev->next; + } + + for (indx=1;;indx++) { + snprintf(tmpstr, sizeof(tmpstr), "%s.?%u.base", vstr, indx); + + ret = + _gnutls_parse_general_name2(c2, tmpstr, -1, &tmp, &type, 0); + + if (ret < 0) { + gnutls_assert(); + break; + } + + if (type == GNUTLS_SAN_OTHERNAME) { + gnutls_datum_t oid = { NULL, 0 }; + gnutls_datum_t parsed_othername = { NULL, 0 }; + ret = _gnutls_parse_general_name2(c2, tmpstr, -1, &oid, &type, 1); + if(ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = gnutls_x509_othername_to_virtual((char*)oid.data, &tmp, &type, + &parsed_othername); + if(ret < 0) { + gnutls_assert(); + goto cleanup; + } + + gnutls_free(oid.data); + gnutls_free(tmp.data); + + memcpy(&tmp, &parsed_othername, sizeof(gnutls_datum_t)); + } + + ret = validate_name_constraints_node(type, &tmp); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + nc = gnutls_malloc(sizeof(struct name_constraints_node_st)); + if (nc == NULL) { + gnutls_assert(); + ret = GNUTLS_E_MEMORY_ERROR; + goto cleanup; + } + + memcpy(&nc->name, &tmp, sizeof(gnutls_datum_t)); + nc->type = type; + nc->next = NULL; + + if (prev == NULL) { + *_nc = prev = nc; + } else { + prev->next = nc; + prev = nc; + } + + tmp.data = NULL; + } + + assert(ret < 0); + if (ret != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { + gnutls_assert(); + goto cleanup; + } + + ret = 0; + cleanup: + gnutls_free(tmp.data); + return ret; +} + +/*- + * _gnutls_name_constraints_node_free: + * @node: name constraints node + * + * Deallocate a list of name constraints nodes starting at the given node. + -*/ +void _gnutls_name_constraints_node_free(name_constraints_node_st *node) +{ + name_constraints_node_st *next, *t; + + t = node; + while (t != NULL) { + next = t->next; + gnutls_free(t->name.data); + gnutls_free(t); + t = next; + } +} + +/*- + * name_constraints_node_new: + * @type: name constraints type to set (gnutls_x509_subject_alt_name_t) + * @data: name.data to set or NULL + * @size: name.size to set + * + * Allocate a new name constraints node and set its type, name size and name data. + * If @data is set to NULL, name data will be an array of \x00 (the length of @size). + * The .next pointer is set to NULL. + * + * Returns: Pointer to newly allocated node or NULL in case of memory error. + -*/ +static name_constraints_node_st* name_constraints_node_new(unsigned type, + unsigned char *data, + unsigned int size) +{ + name_constraints_node_st *tmp = gnutls_malloc(sizeof(struct name_constraints_node_st)); + if (tmp == NULL) + return NULL; + tmp->type = type; + tmp->next = NULL; + tmp->name.size = size; + tmp->name.data = NULL; + if (tmp->name.size > 0) { + + tmp->name.data = gnutls_malloc(tmp->name.size); + if (tmp->name.data == NULL) { + gnutls_free(tmp); + return NULL; + } + if (data != NULL) { + memcpy(tmp->name.data, data, size); + } else { + memset(tmp->name.data, 0, size); + } + } + return tmp; +} + +/*- + * @brief _gnutls_name_constraints_intersect: + * @_nc: first name constraints list (permitted) + * @_nc2: name constraints list to merge with (permitted) + * @_nc_excluded: Corresponding excluded name constraints list + * + * This function finds the intersection of @_nc and @_nc2. The result is placed in @_nc, + * the original @_nc is deallocated. @_nc2 is not changed. If necessary, a universal + * excluded name constraint node of the right type is added to the list provided + * in @_nc_excluded. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value. + -*/ +static +int _gnutls_name_constraints_intersect(name_constraints_node_st ** _nc, + name_constraints_node_st * _nc2, + name_constraints_node_st ** _nc_excluded) +{ + name_constraints_node_st *nc, *nc2, *t, *tmp, *dest = NULL, *prev = NULL; + int ret, type, used; + + /* temporary array to see, if we need to add universal excluded constraints + * (see phase 3 for details) + * indexed directly by (gnutls_x509_subject_alt_name_t enum - 1) */ + unsigned char types_with_empty_intersection[GNUTLS_SAN_MAX]; + memset(types_with_empty_intersection, 0, sizeof(types_with_empty_intersection)); + + if (*_nc == NULL || _nc2 == NULL) + return 0; + + /* Phase 1 + * For each name in _NC, if a _NC2 does not contain a name + * with the same type, preserve the original name. + * Do this also for node of unknown type (not DNS, email, IP */ + t = nc = *_nc; + while (t != NULL) { + name_constraints_node_st *next = t->next; + nc2 = _nc2; + while (nc2 != NULL) { + if (t->type == nc2->type) { + // check bounds (we will use 't->type' as index) + if (t->type > GNUTLS_SAN_MAX || t->type == 0) + return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); + // note the possibility of empty intersection for this type + // if we add something to the intersection in phase 2, + // we will reset this flag back to 0 then + types_with_empty_intersection[t->type - 1] = 1; + break; + } + nc2 = nc2->next; + } + if (nc2 == NULL || + (t->type != GNUTLS_SAN_DNSNAME && + t->type != GNUTLS_SAN_RFC822NAME && + t->type != GNUTLS_SAN_IPADDRESS) + ) { + /* move node from NC to DEST */ + if (prev != NULL) + prev->next = next; + else + prev = nc = next; + t->next = dest; + dest = t; + } else { + prev = t; + } + t = next; + } + + /* Phase 2 + * iterate through all combinations from nc2 and nc1 + * and create intersections of nodes with same type */ + nc2 = _nc2; + while (nc2 != NULL) { + // current nc2 node has not yet been used for any intersection + // (and is not in DEST either) + used = 0; + t = nc; + while (t != NULL) { + // save intersection of name constraints into tmp + ret = name_constraints_intersect_nodes(t, nc2, &tmp); + if (ret < 0) return gnutls_assert_val(ret); + used = 1; + // if intersection is not empty + if (tmp != NULL) { // intersection for this type is not empty + // check bounds + if (tmp->type > GNUTLS_SAN_MAX || tmp->type == 0) { + gnutls_free(tmp); + return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); + } + // we will not add universal excluded constraint for this type + types_with_empty_intersection[tmp->type - 1] = 0; + // add intersection node to DEST + tmp->next = dest; + dest = tmp; + } + t = t->next; + } + // if the node from nc2 was not used for intersection, copy it to DEST + // Beware: also copies nodes other than DNS, email, IP, + // since their counterpart may have been moved in phase 1. + if (!used) { + tmp = name_constraints_node_new(nc2->type, nc2->name.data, nc2->name.size); + if (tmp == NULL) { + _gnutls_name_constraints_node_free(dest); + return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + } + tmp->next = dest; + dest = tmp; + } + nc2 = nc2->next; + } + + /* replace the original with the new */ + _gnutls_name_constraints_node_free(nc); + *_nc = dest; + + /* Phase 3 + * For each type: If we have empty permitted name constraints now + * and we didn't have at the beginning, we have to add a new + * excluded constraint with universal wildcard + * (since the intersection of permitted is now empty). */ + for (type = 1; type <= GNUTLS_SAN_MAX; type++) { + if (types_with_empty_intersection[type-1] == 0) + continue; + _gnutls_hard_log("Adding universal excluded name constraint for type %d.\n", type); + switch (type) { + case GNUTLS_SAN_IPADDRESS: + // add universal restricted range for IPv4 + tmp = name_constraints_node_new(GNUTLS_SAN_IPADDRESS, NULL, 8); + if (tmp == NULL) { + _gnutls_name_constraints_node_free(dest); + return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + } + tmp->next = *_nc_excluded; + *_nc_excluded = tmp; + // add universal restricted range for IPv6 + tmp = name_constraints_node_new(GNUTLS_SAN_IPADDRESS, NULL, 32); + if (tmp == NULL) { + _gnutls_name_constraints_node_free(dest); + return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + } + tmp->next = *_nc_excluded; + *_nc_excluded = tmp; + break; + case GNUTLS_SAN_DNSNAME: + case GNUTLS_SAN_RFC822NAME: + tmp = name_constraints_node_new(type, NULL, 0); + if (tmp == NULL) { + _gnutls_name_constraints_node_free(dest); + return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + } + tmp->next = *_nc_excluded; + *_nc_excluded = tmp; + break; + default: // do nothing, at least one node was already moved in phase 1 + break; + } + } + return GNUTLS_E_SUCCESS; +} + +static int _gnutls_name_constraints_append(name_constraints_node_st **_nc, + name_constraints_node_st *_nc2) +{ + name_constraints_node_st *nc, *nc2; + struct name_constraints_node_st *tmp; + + if (_nc2 == NULL) + return 0; + + nc2 = _nc2; + while (nc2) { + nc = *_nc; + + tmp = name_constraints_node_new(nc2->type, nc2->name.data, nc2->name.size); + if (tmp == NULL) + return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + + tmp->next = nc; + *_nc = tmp; + + nc2 = nc2->next; + } + + return 0; +} + +/** + * gnutls_x509_crt_get_name_constraints: + * @crt: should contain a #gnutls_x509_crt_t type + * @nc: The nameconstraints intermediate type + * @flags: zero or %GNUTLS_EXT_FLAG_APPEND + * @critical: the extension status + * + * This function will return an intermediate type containing + * the name constraints of the provided CA certificate. That + * structure can be used in combination with gnutls_x509_name_constraints_check() + * to verify whether a server's name is in accordance with the constraints. + * + * When the @flags is set to %GNUTLS_EXT_FLAG_APPEND, + * then if the @nc structure is empty this function will behave + * identically as if the flag was not set. + * Otherwise if there are elements in the @nc structure then the + * constraints will be merged with the existing constraints following + * RFC5280 p6.1.4 (excluded constraints will be appended, permitted + * will be intersected). + * + * Note that @nc must be initialized prior to calling this function. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE + * if the extension is not present, otherwise a negative error value. + * + * Since: 3.3.0 + **/ +int gnutls_x509_crt_get_name_constraints(gnutls_x509_crt_t crt, + gnutls_x509_name_constraints_t nc, + unsigned int flags, + unsigned int *critical) +{ + int ret; + gnutls_datum_t der = { NULL, 0 }; + + if (crt == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + ret = + _gnutls_x509_crt_get_extension(crt, "2.5.29.30", 0, &der, + critical); + if (ret < 0) + return gnutls_assert_val(ret); + + if (der.size == 0 || der.data == NULL) + return gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE); + + ret = gnutls_x509_ext_import_name_constraints(&der, nc, flags); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = 0; + + cleanup: + _gnutls_free_datum(&der); + + return ret; + +} + +/** + * gnutls_x509_name_constraints_deinit: + * @nc: The nameconstraints + * + * This function will deinitialize a name constraints type. + * + * Since: 3.3.0 + **/ +void gnutls_x509_name_constraints_deinit(gnutls_x509_name_constraints_t nc) +{ + _gnutls_name_constraints_node_free(nc->permitted); + _gnutls_name_constraints_node_free(nc->excluded); + + gnutls_free(nc); +} + +/** + * gnutls_x509_name_constraints_init: + * @nc: The nameconstraints + * + * This function will initialize a name constraints type. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value. + * + * Since: 3.3.0 + **/ +int gnutls_x509_name_constraints_init(gnutls_x509_name_constraints_t *nc) +{ + *nc = gnutls_calloc(1, sizeof(struct gnutls_name_constraints_st)); + if (*nc == NULL) { + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; + } + + return 0; +} + +static +int name_constraints_add(gnutls_x509_name_constraints_t nc, + gnutls_x509_subject_alt_name_t type, + const gnutls_datum_t * name, + unsigned permitted) +{ + struct name_constraints_node_st * tmp, *prev = NULL; + int ret; + + ret = validate_name_constraints_node(type, name); + if (ret < 0) + return gnutls_assert_val(ret); + + if (permitted != 0) + prev = tmp = nc->permitted; + else + prev = tmp = nc->excluded; + + while(tmp != NULL) { + tmp = tmp->next; + if (tmp != NULL) + prev = tmp; + } + + tmp = name_constraints_node_new(type, name->data, name->size); + if (tmp == NULL) + return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + tmp->next = NULL; + + if (prev == NULL) { + if (permitted != 0) + nc->permitted = tmp; + else + nc->excluded = tmp; + } else + prev->next = tmp; + + return 0; +} + +/*- + * _gnutls_x509_name_constraints_merge: + * @nc: The nameconstraints + * @nc2: The name constraints to be merged with + * + * This function will merge the provided name constraints structures + * as per RFC5280 p6.1.4. That is, the excluded constraints will be appended, + * and permitted will be intersected. The intersection assumes that @nc + * is the root CA constraints. + * + * The merged constraints will be placed in @nc. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value. + * + * Since: 3.5.0 + -*/ +int _gnutls_x509_name_constraints_merge(gnutls_x509_name_constraints_t nc, + gnutls_x509_name_constraints_t nc2) +{ + int ret; + + ret = + _gnutls_name_constraints_intersect(&nc->permitted, + nc2->permitted, &nc->excluded); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + ret = + _gnutls_name_constraints_append(&nc->excluded, + nc2->excluded); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + return 0; +} + +/** + * gnutls_x509_name_constraints_add_permitted: + * @nc: The nameconstraints + * @type: The type of the constraints + * @name: The data of the constraints + * + * This function will add a name constraint to the list of permitted + * constraints. The constraints @type can be any of the following types: + * %GNUTLS_SAN_DNSNAME, %GNUTLS_SAN_RFC822NAME, %GNUTLS_SAN_DN, + * %GNUTLS_SAN_URI, %GNUTLS_SAN_IPADDRESS. For the latter, an IP address + * in network byte order is expected, followed by its network mask. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value. + * + * Since: 3.3.0 + **/ +int gnutls_x509_name_constraints_add_permitted(gnutls_x509_name_constraints_t nc, + gnutls_x509_subject_alt_name_t type, + const gnutls_datum_t * name) +{ + return name_constraints_add(nc, type, name, 1); +} + +/** + * gnutls_x509_name_constraints_add_excluded: + * @nc: The nameconstraints + * @type: The type of the constraints + * @name: The data of the constraints + * + * This function will add a name constraint to the list of excluded + * constraints. The constraints @type can be any of the following types: + * %GNUTLS_SAN_DNSNAME, %GNUTLS_SAN_RFC822NAME, %GNUTLS_SAN_DN, + * %GNUTLS_SAN_URI, %GNUTLS_SAN_IPADDRESS. For the latter, an IP address + * in network byte order is expected, followed by its network mask (which is + * 4 bytes in IPv4 or 16-bytes in IPv6). + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value. + * + * Since: 3.3.0 + **/ +int gnutls_x509_name_constraints_add_excluded(gnutls_x509_name_constraints_t nc, + gnutls_x509_subject_alt_name_t type, + const gnutls_datum_t * name) +{ + return name_constraints_add(nc, type, name, 0); +} + +/** + * gnutls_x509_crt_set_name_constraints: + * @crt: The certificate + * @nc: The nameconstraints structure + * @critical: whether this extension will be critical + * + * This function will set the provided name constraints to + * the certificate extension list. This extension is always + * marked as critical. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value. + * + * Since: 3.3.0 + **/ +int gnutls_x509_crt_set_name_constraints(gnutls_x509_crt_t crt, + gnutls_x509_name_constraints_t nc, + unsigned int critical) +{ +int ret; +gnutls_datum_t der; + + ret = gnutls_x509_ext_export_name_constraints(nc, &der); + if (ret < 0) + return gnutls_assert_val(ret); + + ret = + _gnutls_x509_crt_set_extension(crt, "2.5.29.30", &der, critical); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = 0; + crt->use_extensions = 1; + +cleanup: + _gnutls_free_datum(&der); + return ret; +} + +static +unsigned ends_with(const gnutls_datum_t * str, const gnutls_datum_t * suffix) +{ + unsigned char *tree; + unsigned int treelen; + + if (suffix->size >= str->size) + return 0; + + tree = suffix->data; + treelen = suffix->size; + if((treelen > 0) && (tree[0] == '.')) { + tree++; + treelen--; + } + + if (memcmp(str->data + str->size - treelen, tree, treelen) == 0 && + str->data[str->size - treelen -1] == '.') + return 1; /* match */ + + return 0; +} + +static +unsigned email_ends_with(const gnutls_datum_t * str, const gnutls_datum_t * suffix) +{ + if (suffix->size >= str->size) + return 0; + + if (suffix->size > 1 && suffix->data[0] == '.') { + /* .domain.com */ + if (memcmp(str->data + str->size - suffix->size, suffix->data, suffix->size) == 0) + return 1; /* match */ + } else { + if (memcmp(str->data + str->size - suffix->size, suffix->data, suffix->size) == 0 && + str->data[str->size - suffix->size -1] == '@') + return 1; /* match */ + } + + return 0; +} + +static unsigned dnsname_matches(const gnutls_datum_t *name, const gnutls_datum_t *suffix) +{ + _gnutls_hard_log("matching %.*s with DNS constraint %.*s\n", name->size, name->data, + suffix->size, suffix->data); + + if (suffix->size == name->size && memcmp(suffix->data, name->data, suffix->size) == 0) + return 1; /* match */ + + return ends_with(name, suffix); +} + +static unsigned email_matches(const gnutls_datum_t *name, const gnutls_datum_t *suffix) +{ + _gnutls_hard_log("matching %.*s with e-mail constraint %.*s\n", name->size, name->data, + suffix->size, suffix->data); + + if (suffix->size == name->size && memcmp(suffix->data, name->data, suffix->size) == 0) + return 1; /* match */ + + return email_ends_with(name, suffix); +} + +/*- + * name_constraints_intersect_nodes: + * @nc1: name constraints node 1 + * @nc2: name constraints node 2 + * @_intersection: newly allocated node with intersected constraints, + * NULL if the intersection is empty + * + * Inspect 2 name constraints nodes (of possibly different types) and allocate + * a new node with intersection of given constraints. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value. + -*/ +static int +name_constraints_intersect_nodes(name_constraints_node_st * nc1, + name_constraints_node_st * nc2, + name_constraints_node_st ** _intersection) +{ + // presume empty intersection + name_constraints_node_st *intersection = NULL; + name_constraints_node_st *to_copy = NULL; + unsigned iplength = 0; + unsigned byte; + + *_intersection = NULL; + + if (nc1->type != nc2->type) { + return GNUTLS_E_SUCCESS; + } + switch (nc1->type) { + case GNUTLS_SAN_DNSNAME: + if (!dnsname_matches(&nc2->name, &nc1->name)) + return GNUTLS_E_SUCCESS; + to_copy = nc2; + break; + case GNUTLS_SAN_RFC822NAME: + if (!email_matches(&nc2->name, &nc1->name)) + return GNUTLS_E_SUCCESS; + to_copy = nc2; + break; + case GNUTLS_SAN_IPADDRESS: + if (nc1->name.size != nc2->name.size) + return GNUTLS_E_SUCCESS; + iplength = nc1->name.size/2; + for (byte = 0; byte < iplength; byte++) { + if (((nc1->name.data[byte]^nc2->name.data[byte]) // XOR of addresses + & nc1->name.data[byte+iplength] // AND mask from nc1 + & nc2->name.data[byte+iplength]) // AND mask from nc2 + != 0) { + // CIDRS do not intersect + return GNUTLS_E_SUCCESS; + } + } + to_copy = nc2; + break; + default: + // for other types, we don't know how to do the intersection, assume empty + return GNUTLS_E_SUCCESS; + } + + // copy existing node if applicable + if (to_copy != NULL) { + *_intersection = name_constraints_node_new(to_copy->type, to_copy->name.data, to_copy->name.size); + if (*_intersection == NULL) + return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + intersection = *_intersection; + + assert(intersection->name.data != NULL); + + if (intersection->type == GNUTLS_SAN_IPADDRESS) { + // make sure both IP addresses are correctly masked + _gnutls_mask_ip(intersection->name.data, intersection->name.data+iplength, iplength); + _gnutls_mask_ip(nc1->name.data, nc1->name.data+iplength, iplength); + // update intersection, if necessary (we already know one is subset of other) + for (byte = 0; byte < 2 * iplength; byte++) { + intersection->name.data[byte] |= nc1->name.data[byte]; + } + } + } + + return GNUTLS_E_SUCCESS; +} + +/* + * Returns: true if the certification is acceptable, and false otherwise. + */ +static +unsigned check_unsupported_constraint(gnutls_x509_name_constraints_t nc, + gnutls_x509_subject_alt_name_t type) +{ +unsigned i; +int ret; +unsigned rtype; +gnutls_datum_t rname; + + /* check if there is a restrictions with that type, if + * yes, then reject the name. + */ + i = 0; + do { + ret = gnutls_x509_name_constraints_get_excluded(nc, i++, &rtype, &rname); + if (ret >= 0) { + if (rtype != type) + continue; + else + return gnutls_assert_val(0); + } + + } while(ret == 0); + + return 1; +} + +static +unsigned check_dns_constraints(gnutls_x509_name_constraints_t nc, + const gnutls_datum_t * name) +{ +unsigned i; +int ret; +unsigned rtype; +unsigned allowed_found = 0; +gnutls_datum_t rname; + + /* check restrictions */ + i = 0; + do { + ret = gnutls_x509_name_constraints_get_excluded(nc, i++, &rtype, &rname); + if (ret >= 0) { + if (rtype != GNUTLS_SAN_DNSNAME) + continue; + + /* a name of value 0 means that the CA shouldn't have issued + * a certificate with a DNSNAME. */ + if (rname.size == 0) + return gnutls_assert_val(0); + + if (dnsname_matches(name, &rname) != 0) + return gnutls_assert_val(0); /* rejected */ + } + } while(ret == 0); + + /* check allowed */ + i = 0; + do { + ret = gnutls_x509_name_constraints_get_permitted(nc, i++, &rtype, &rname); + if (ret >= 0) { + if (rtype != GNUTLS_SAN_DNSNAME) + continue; + + if (rname.size == 0) + continue; + + allowed_found = 1; + + if (dnsname_matches(name, &rname) != 0) + return 1; /* accepted */ + } + } while(ret == 0); + + if (allowed_found != 0) /* there are allowed directives but this host wasn't found */ + return gnutls_assert_val(0); + + return 1; +} + +static +unsigned check_email_constraints(gnutls_x509_name_constraints_t nc, + const gnutls_datum_t * name) +{ +unsigned i; +int ret; +unsigned rtype; +unsigned allowed_found = 0; +gnutls_datum_t rname; + + /* check restrictions */ + i = 0; + do { + ret = gnutls_x509_name_constraints_get_excluded(nc, i++, &rtype, &rname); + if (ret >= 0) { + if (rtype != GNUTLS_SAN_RFC822NAME) + continue; + + /* a name of value 0 means that the CA shouldn't have issued + * a certificate with an e-mail. */ + if (rname.size == 0) + return gnutls_assert_val(0); + + if (email_matches(name, &rname) != 0) + return gnutls_assert_val(0); /* rejected */ + } + } while(ret == 0); + + /* check allowed */ + i = 0; + do { + ret = gnutls_x509_name_constraints_get_permitted(nc, i++, &rtype, &rname); + if (ret >= 0) { + if (rtype != GNUTLS_SAN_RFC822NAME) + continue; + + if (rname.size == 0) + continue; + + allowed_found = 1; + + if (email_matches(name, &rname) != 0) + return 1; /* accepted */ + } + } while(ret == 0); + + if (allowed_found != 0) /* there are allowed directives but this host wasn't found */ + return gnutls_assert_val(0); + + return 1; +} + +static +unsigned check_ip_constraints(gnutls_x509_name_constraints_t nc, + const gnutls_datum_t * name) +{ + unsigned i; + int ret; + unsigned rtype; + unsigned allowed_found = 0; + gnutls_datum_t rname; + + /* check restrictions */ + i = 0; + do { + ret = gnutls_x509_name_constraints_get_excluded(nc, i++, &rtype, &rname); + if (ret >= 0) { + if (rtype != GNUTLS_SAN_IPADDRESS) + continue; + + /* do not check IPv4 against IPv6 constraints and vice versa */ + if (name->size != rname.size / 2) + continue; + + if (ip_in_cidr(name, &rname) != 0) + return gnutls_assert_val(0); /* rejected */ + } + } while(ret == 0); + + /* check allowed */ + i = 0; + do { + ret = gnutls_x509_name_constraints_get_permitted(nc, i++, &rtype, &rname); + if (ret >= 0) { + if (rtype != GNUTLS_SAN_IPADDRESS) + continue; + + /* do not check IPv4 against IPv6 constraints and vice versa */ + if (name->size != rname.size / 2) + continue; + + allowed_found = 1; + + if (ip_in_cidr(name, &rname) != 0) + return 1; /* accepted */ + } + } while(ret == 0); + + if (allowed_found != 0) /* there are allowed directives but this host wasn't found */ + return gnutls_assert_val(0); + + return 1; +} + +/** + * gnutls_x509_name_constraints_check: + * @nc: the extracted name constraints + * @type: the type of the constraint to check (of type gnutls_x509_subject_alt_name_t) + * @name: the name to be checked + * + * This function will check the provided name against the constraints in + * @nc using the RFC5280 rules. Currently this function is limited to DNS + * names, emails and IP addresses (of type %GNUTLS_SAN_DNSNAME, + * %GNUTLS_SAN_RFC822NAME and %GNUTLS_SAN_IPADDRESS). + * + * Returns: zero if the provided name is not acceptable, and non-zero otherwise. + * + * Since: 3.3.0 + **/ +unsigned gnutls_x509_name_constraints_check(gnutls_x509_name_constraints_t nc, + gnutls_x509_subject_alt_name_t type, + const gnutls_datum_t * name) +{ + if (type == GNUTLS_SAN_DNSNAME) + return check_dns_constraints(nc, name); + + if (type == GNUTLS_SAN_RFC822NAME) + return check_email_constraints(nc, name); + + if (type == GNUTLS_SAN_IPADDRESS) + return check_ip_constraints(nc, name); + + return check_unsupported_constraint(nc, type); +} + +/* This function checks for unsupported constraints, that we also + * know their structure. That is it will fail only if the constraint + * is present in the CA, _and_ the name in the end certificate contains + * the constrained element. + * + * Returns: true if the certification is acceptable, and false otherwise + */ +static unsigned check_unsupported_constraint2(gnutls_x509_crt_t cert, + gnutls_x509_name_constraints_t nc, + gnutls_x509_subject_alt_name_t type) +{ + unsigned idx, found_one; + char name[MAX_CN]; + size_t name_size; + unsigned san_type; + int ret; + + found_one = 0; + + for (idx=0;;idx++) { + name_size = sizeof(name); + ret = gnutls_x509_crt_get_subject_alt_name2(cert, + idx, name, &name_size, &san_type, NULL); + if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) + break; + else if (ret < 0) + return gnutls_assert_val(0); + + if (san_type != GNUTLS_SAN_URI) + continue; + + found_one = 1; + break; + } + + if (found_one != 0) + return check_unsupported_constraint(nc, type); + + /* no name was found in the certificate, so accept */ + return 1; +} + +/** + * gnutls_x509_name_constraints_check_crt: + * @nc: the extracted name constraints + * @type: the type of the constraint to check (of type gnutls_x509_subject_alt_name_t) + * @cert: the certificate to be checked + * + * This function will check the provided certificate names against the constraints in + * @nc using the RFC5280 rules. It will traverse all the certificate's names and + * alternative names. + * + * Currently this function is limited to DNS + * names and emails (of type %GNUTLS_SAN_DNSNAME and %GNUTLS_SAN_RFC822NAME). + * + * Returns: zero if the provided name is not acceptable, and non-zero otherwise. + * + * Since: 3.3.0 + **/ +unsigned gnutls_x509_name_constraints_check_crt(gnutls_x509_name_constraints_t nc, + gnutls_x509_subject_alt_name_t type, + gnutls_x509_crt_t cert) +{ +char name[MAX_CN]; +size_t name_size; +int ret; +unsigned idx, t, san_type; +gnutls_datum_t n; +unsigned found_one; + + if (is_nc_empty(nc, type) != 0) + return 1; /* shortcut; no constraints to check */ + + if (type == GNUTLS_SAN_RFC822NAME) { + found_one = 0; + for (idx=0;;idx++) { + name_size = sizeof(name); + ret = gnutls_x509_crt_get_subject_alt_name2(cert, + idx, name, &name_size, &san_type, NULL); + if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) + break; + else if (ret < 0) + return gnutls_assert_val(0); + + if (san_type != GNUTLS_SAN_RFC822NAME) + continue; + + found_one = 1; + n.data = (void*)name; + n.size = name_size; + t = gnutls_x509_name_constraints_check(nc, GNUTLS_SAN_RFC822NAME, + &n); + if (t == 0) + return gnutls_assert_val(t); + } + + /* there is at least a single e-mail. That means that the EMAIL field will + * not be used for verifying the identity of the holder. */ + if (found_one != 0) + return 1; + + do { + /* ensure there is only a single EMAIL, similarly to CN handling (rfc6125) */ + name_size = sizeof(name); + ret = gnutls_x509_crt_get_dn_by_oid(cert, GNUTLS_OID_PKCS9_EMAIL, + 1, 0, name, &name_size); + if (ret != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) + return gnutls_assert_val(0); + + name_size = sizeof(name); + ret = gnutls_x509_crt_get_dn_by_oid(cert, GNUTLS_OID_PKCS9_EMAIL, + 0, 0, name, &name_size); + if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) + break; + else if (ret < 0) + return gnutls_assert_val(0); + + found_one = 1; + n.data = (void*)name; + n.size = name_size; + t = gnutls_x509_name_constraints_check(nc, GNUTLS_SAN_RFC822NAME, &n); + if (t == 0) + return gnutls_assert_val(t); + } while(0); + + /* passed */ + if (found_one != 0) + return 1; + else { + /* no name was found. According to RFC5280: + * If no name of the type is in the certificate, the certificate is acceptable. + */ + return gnutls_assert_val(1); + } + } else if (type == GNUTLS_SAN_DNSNAME) { + found_one = 0; + for (idx=0;;idx++) { + name_size = sizeof(name); + ret = gnutls_x509_crt_get_subject_alt_name2(cert, + idx, name, &name_size, &san_type, NULL); + if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) + break; + else if (ret < 0) + return gnutls_assert_val(0); + + if (san_type != GNUTLS_SAN_DNSNAME) + continue; + + found_one = 1; + n.data = (void*)name; + n.size = name_size; + t = gnutls_x509_name_constraints_check(nc, GNUTLS_SAN_DNSNAME, + &n); + if (t == 0) + return gnutls_assert_val(t); + } + + /* there is at least a single DNS name. That means that the CN will + * not be used for verifying the identity of the holder. */ + if (found_one != 0) + return 1; + + /* verify the name constraints against the CN, if the certificate is + * not a CA. We do this check only on certificates marked as WWW server, + * because that's where the CN check is only performed. */ + if (_gnutls_check_key_purpose(cert, GNUTLS_KP_TLS_WWW_SERVER, 0) != 0) + do { + /* ensure there is only a single CN, according to rfc6125 */ + name_size = sizeof(name); + ret = gnutls_x509_crt_get_dn_by_oid(cert, GNUTLS_OID_X520_COMMON_NAME, + 1, 0, name, &name_size); + if (ret != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) + return gnutls_assert_val(0); + + name_size = sizeof(name); + ret = gnutls_x509_crt_get_dn_by_oid(cert, GNUTLS_OID_X520_COMMON_NAME, + 0, 0, name, &name_size); + if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) + break; + else if (ret < 0) + return gnutls_assert_val(0); + + found_one = 1; + n.data = (void*)name; + n.size = name_size; + t = gnutls_x509_name_constraints_check(nc, GNUTLS_SAN_DNSNAME, + &n); + if (t == 0) + return gnutls_assert_val(t); + } while(0); + + /* passed */ + if (found_one != 0) + return 1; + else { + /* no name was found. According to RFC5280: + * If no name of the type is in the certificate, the certificate is acceptable. + */ + return gnutls_assert_val(1); + } + } else if (type == GNUTLS_SAN_IPADDRESS) { + found_one = 0; + for (idx=0;;idx++) { + name_size = sizeof(name); + ret = gnutls_x509_crt_get_subject_alt_name2(cert, + idx, name, &name_size, &san_type, NULL); + if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) + break; + else if (ret < 0) + return gnutls_assert_val(0); + + if (san_type != GNUTLS_SAN_IPADDRESS) + continue; + + found_one = 1; + n.data = (void*)name; + n.size = name_size; + t = gnutls_x509_name_constraints_check(nc, GNUTLS_SAN_IPADDRESS, &n); + if (t == 0) + return gnutls_assert_val(t); + } + + /* there is at least a single IP address. */ + + if (found_one != 0) { + return 1; + } else { + /* no name was found. According to RFC5280: + * If no name of the type is in the certificate, the certificate is acceptable. + */ + return gnutls_assert_val(1); + } + } else if (type == GNUTLS_SAN_URI) { + return check_unsupported_constraint2(cert, nc, type); + } else + return check_unsupported_constraint(nc, type); +} + +/** + * gnutls_x509_name_constraints_get_permitted: + * @nc: the extracted name constraints + * @idx: the index of the constraint + * @type: the type of the constraint (of type gnutls_x509_subject_alt_name_t) + * @name: the name in the constraint (of the specific type) + * + * This function will return an intermediate type containing + * the name constraints of the provided CA certificate. That + * structure can be used in combination with gnutls_x509_name_constraints_check() + * to verify whether a server's name is in accordance with the constraints. + * + * The name should be treated as constant and valid for the lifetime of @nc. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE + * if the extension is not present, otherwise a negative error value. + * + * Since: 3.3.0 + **/ +int gnutls_x509_name_constraints_get_permitted(gnutls_x509_name_constraints_t nc, + unsigned idx, + unsigned *type, gnutls_datum_t * name) +{ + unsigned int i; + struct name_constraints_node_st * tmp = nc->permitted; + + for (i = 0; i < idx; i++) { + if (tmp == NULL) + return + gnutls_assert_val + (GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE); + + tmp = tmp->next; + } + + if (tmp == NULL) + return gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE); + + *type = tmp->type; + *name = tmp->name; + + return 0; +} + +/** + * gnutls_x509_name_constraints_get_excluded: + * @nc: the extracted name constraints + * @idx: the index of the constraint + * @type: the type of the constraint (of type gnutls_x509_subject_alt_name_t) + * @name: the name in the constraint (of the specific type) + * + * This function will return an intermediate type containing + * the name constraints of the provided CA certificate. That + * structure can be used in combination with gnutls_x509_name_constraints_check() + * to verify whether a server's name is in accordance with the constraints. + * + * The name should be treated as constant and valid for the lifetime of @nc. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE + * if the extension is not present, otherwise a negative error value. + * + * Since: 3.3.0 + **/ +int gnutls_x509_name_constraints_get_excluded(gnutls_x509_name_constraints_t nc, + unsigned idx, + unsigned *type, gnutls_datum_t * name) +{ + unsigned int i; + struct name_constraints_node_st * tmp = nc->excluded; + + for (i = 0; i < idx; i++) { + if (tmp == NULL) + return + gnutls_assert_val + (GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE); + + tmp = tmp->next; + } + + if (tmp == NULL) + return gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE); + + *type = tmp->type; + *name = tmp->name; + + return 0; +} diff --git a/lib/x509/ocsp.c b/lib/x509/ocsp.c new file mode 100644 index 0000000..81f3d7e --- /dev/null +++ b/lib/x509/ocsp.c @@ -0,0 +1,2642 @@ +/* + * Copyright (C) 2011-2012 Free Software Foundation, Inc. + * Copyright (C) 2016-2017 Red Hat, Inc. + * + * Author: Simon Josefsson, Nikos Mavrogiannopoulos + * + * This file is part of GnuTLS. + * + * The GnuTLS is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/> + * + */ + +/* Online Certificate Status Protocol - RFC 2560 + */ + +#include "gnutls_int.h" +#include <global.h> +#include "errors.h" +#include <libtasn1.h> +#include <pk.h> +#include "common.h" +#include "verify-high.h" +#include "x509.h" +#include "ocsp.h" + +#include <gnutls/ocsp.h> +#include <auth/cert.h> + +#include <assert.h> +#include "intprops.h" + +typedef struct gnutls_ocsp_req_int { + asn1_node req; + unsigned init; +} gnutls_ocsp_req_int; + +typedef struct gnutls_ocsp_resp_int { + asn1_node resp; + gnutls_datum_t response_type_oid; + asn1_node basicresp; + gnutls_datum_t der; + unsigned init; +} gnutls_ocsp_resp_int; + +#define MAX_TIME 64 + +/** + * gnutls_ocsp_req_init: + * @req: A pointer to the type to be initialized + * + * This function will initialize an OCSP request structure. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int gnutls_ocsp_req_init(gnutls_ocsp_req_t * req) +{ + gnutls_ocsp_req_t tmp = + gnutls_calloc(1, sizeof(gnutls_ocsp_req_int)); + int ret; + + if (!tmp) + return GNUTLS_E_MEMORY_ERROR; + + ret = asn1_create_element(_gnutls_get_pkix(), "PKIX1.OCSPRequest", + &tmp->req); + if (ret != ASN1_SUCCESS) { + gnutls_assert(); + gnutls_free(tmp); + return _gnutls_asn2err(ret); + } + + *req = tmp; + + return GNUTLS_E_SUCCESS; +} + +/** + * gnutls_ocsp_req_deinit: + * @req: The data to be deinitialized + * + * This function will deinitialize a OCSP request structure. + **/ +void gnutls_ocsp_req_deinit(gnutls_ocsp_req_t req) +{ + if (!req) + return; + + if (req->req) + asn1_delete_structure(&req->req); + + req->req = NULL; + gnutls_free(req); +} + +/** + * gnutls_ocsp_resp_init: + * @resp: A pointer to the type to be initialized + * + * This function will initialize an OCSP response structure. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int gnutls_ocsp_resp_init(gnutls_ocsp_resp_t * resp) +{ + gnutls_ocsp_resp_t tmp = + gnutls_calloc(1, sizeof(gnutls_ocsp_resp_int)); + int ret; + + if (!tmp) + return GNUTLS_E_MEMORY_ERROR; + + ret = asn1_create_element(_gnutls_get_pkix(), + "PKIX1.OCSPResponse", &tmp->resp); + if (ret != ASN1_SUCCESS) { + gnutls_assert(); + gnutls_free(tmp); + return _gnutls_asn2err(ret); + } + + ret = asn1_create_element(_gnutls_get_pkix(), + "PKIX1.BasicOCSPResponse", + &tmp->basicresp); + if (ret != ASN1_SUCCESS) { + gnutls_assert(); + asn1_delete_structure(&tmp->resp); + gnutls_free(tmp); + return _gnutls_asn2err(ret); + } + + *resp = tmp; + + return GNUTLS_E_SUCCESS; +} + +/** + * gnutls_ocsp_resp_deinit: + * @resp: The data to be deinitialized + * + * This function will deinitialize a OCSP response structure. + **/ +void gnutls_ocsp_resp_deinit(gnutls_ocsp_resp_t resp) +{ + if (!resp) + return; + + if (resp->resp) + asn1_delete_structure(&resp->resp); + gnutls_free(resp->response_type_oid.data); + if (resp->basicresp) + asn1_delete_structure(&resp->basicresp); + + resp->resp = NULL; + resp->basicresp = NULL; + + gnutls_free(resp->der.data); + gnutls_free(resp); +} + +/** + * gnutls_ocsp_req_import: + * @req: The data to store the parsed request. + * @data: DER encoded OCSP request. + * + * This function will convert the given DER encoded OCSP request to + * the native #gnutls_ocsp_req_t format. The output will be stored in + * @req. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int +gnutls_ocsp_req_import(gnutls_ocsp_req_t req, const gnutls_datum_t * data) +{ + int ret = 0; + + if (req == NULL || data == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + if (req->init) { + /* Any earlier _asn1_strict_der_decode will modify the ASN.1 + structure, so we need to replace it with a fresh + structure. */ + asn1_delete_structure(&req->req); + + ret = asn1_create_element(_gnutls_get_pkix(), + "PKIX1.OCSPRequest", &req->req); + if (ret != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(ret); + } + } + req->init = 1; + + ret = _asn1_strict_der_decode(&req->req, data->data, data->size, NULL); + if (ret != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(ret); + } + + return GNUTLS_E_SUCCESS; +} + +/** + * gnutls_ocsp_resp_import: + * @resp: The data to store the parsed response. + * @data: DER encoded OCSP response. + * + * This function will convert the given DER encoded OCSP response to + * the native #gnutls_ocsp_resp_t format. It also decodes the Basic + * OCSP Response part, if any. The output will be stored in @resp. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int +gnutls_ocsp_resp_import(gnutls_ocsp_resp_t resp, + const gnutls_datum_t * data) +{ + return gnutls_ocsp_resp_import2(resp, data, GNUTLS_X509_FMT_DER); +} + +/** + * gnutls_ocsp_resp_import2: + * @resp: The data to store the parsed response. + * @data: DER or PEM encoded OCSP response. + * @fmt: DER or PEM + * + * This function will convert the given OCSP response to + * the native #gnutls_ocsp_resp_t format. It also decodes the Basic + * OCSP Response part, if any. The output will be stored in @resp. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 3.6.3 + **/ +int +gnutls_ocsp_resp_import2(gnutls_ocsp_resp_t resp, + const gnutls_datum_t *data, + gnutls_x509_crt_fmt_t fmt) +{ + int ret = 0; + gnutls_datum_t der; + + if (resp == NULL || data == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + der.data = data->data; + der.size = data->size; + + if (fmt == GNUTLS_X509_FMT_PEM) { + ret = gnutls_pem_base64_decode2(BARE_PEM_OCSP_RESPONSE, data, &der); + if (ret < 0) { + return gnutls_assert_val(ret); + } + } + + if (resp->init != 0) { + /* Any earlier _asn1_strict_der_decode will modify the ASN.1 + structure, so we need to replace it with a fresh + structure. */ + asn1_delete_structure(&resp->resp); + if (resp->basicresp) + asn1_delete_structure(&resp->basicresp); + + ret = asn1_create_element(_gnutls_get_pkix(), + "PKIX1.OCSPResponse", + &resp->resp); + if (ret != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(ret); + goto cleanup; + } + + ret = asn1_create_element(_gnutls_get_pkix(), + "PKIX1.BasicOCSPResponse", + &resp->basicresp); + if (ret != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(ret); + goto cleanup; + } + + gnutls_free(resp->der.data); + } + + resp->init = 1; + ret = _asn1_strict_der_decode(&resp->resp, der.data, der.size, NULL); + if (ret != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(ret); + goto cleanup; + } + + if (gnutls_ocsp_resp_get_status(resp) != + GNUTLS_OCSP_RESP_SUCCESSFUL) { + ret = GNUTLS_E_SUCCESS; + goto cleanup; + } + + ret = + _gnutls_x509_read_value(resp->resp, + "responseBytes.responseType", + &resp->response_type_oid); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } +#define OCSP_BASIC "1.3.6.1.5.5.7.48.1.1" + + if (resp->response_type_oid.size == sizeof(OCSP_BASIC) - 1 + && memcmp(resp->response_type_oid.data, OCSP_BASIC, + resp->response_type_oid.size) == 0) { + + ret = + _gnutls_x509_read_value(resp->resp, + "responseBytes.response", &resp->der); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = + _asn1_strict_der_decode(&resp->basicresp, resp->der.data, resp->der.size, + NULL); + if (ret != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(ret); + goto cleanup; + } + } else { + asn1_delete_structure(&resp->basicresp); + resp->basicresp = NULL; + } + + ret = GNUTLS_E_SUCCESS; +cleanup: + if (der.data != data->data) + gnutls_free(der.data); + return ret; +} + +/** + * gnutls_ocsp_req_export: + * @req: Holds the OCSP request + * @data: newly allocate buffer holding DER encoded OCSP request + * + * This function will export the OCSP request to DER format. + * + * Returns: In case of failure a negative error code will be + * returned, and 0 on success. + **/ +int gnutls_ocsp_req_export(gnutls_ocsp_req_const_t req, gnutls_datum_t * data) +{ + int ret; + + if (req == NULL || data == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + /* XXX remove when we support these fields */ + (void)asn1_write_value(req->req, "tbsRequest.requestorName", NULL, 0); + (void)asn1_write_value(req->req, "optionalSignature", NULL, 0); + + /* prune extension field if we don't have any extension */ + ret = gnutls_ocsp_req_get_extension(req, 0, NULL, NULL, NULL); + if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) + (void)asn1_write_value(req->req, "tbsRequest.requestExtensions", + NULL, 0); + + return _gnutls_x509_get_raw_field(req->req, "", data); +} + +/** + * gnutls_ocsp_resp_export: + * @resp: Holds the OCSP response + * @data: newly allocate buffer holding DER encoded OCSP response + * + * This function will export the OCSP response to DER format. + * + * Returns: In case of failure a negative error code will be + * returned, and 0 on success. + **/ +int gnutls_ocsp_resp_export(gnutls_ocsp_resp_const_t resp, gnutls_datum_t * data) +{ + return gnutls_ocsp_resp_export2(resp, data, GNUTLS_X509_FMT_DER); +} + +/** + * gnutls_ocsp_resp_export2: + * @resp: Holds the OCSP response + * @data: newly allocate buffer holding DER or PEM encoded OCSP response + * @fmt: DER or PEM + * + * This function will export the OCSP response to DER or PEM format. + * + * Returns: In case of failure a negative error code will be + * returned, and 0 on success. + * + * Since: 3.6.3 + **/ +int gnutls_ocsp_resp_export2(gnutls_ocsp_resp_const_t resp, gnutls_datum_t * data, + gnutls_x509_crt_fmt_t fmt) +{ + int ret; + gnutls_datum_t der; + + if (resp == NULL || data == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + ret = _gnutls_x509_get_raw_field(resp->resp, "", &der); + if (ret < 0) + return gnutls_assert_val(ret); + + if (fmt == GNUTLS_X509_FMT_DER) { + data->data = der.data; + data->size = der.size; + return ret; + } else { + ret = gnutls_pem_base64_encode2("OCSP RESPONSE", &der, data); + gnutls_free(der.data); + if (ret < 0) + return gnutls_assert_val(ret); + + return 0; + } +} + +/** + * gnutls_ocsp_req_get_version: + * @req: should contain a #gnutls_ocsp_req_t type + * + * This function will return the version of the OCSP request. + * Typically this is always 1 indicating version 1. + * + * Returns: version of OCSP request, or a negative error code on error. + **/ +int gnutls_ocsp_req_get_version(gnutls_ocsp_req_const_t req) +{ + if (req == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + return _gnutls_x509_get_version(req->req, "tbsRequest.version"); +} + +/** + * gnutls_ocsp_req_get_cert_id: + * @req: should contain a #gnutls_ocsp_req_t type + * @indx: Specifies which extension OID to get. Use (0) to get the first one. + * @digest: output variable with #gnutls_digest_algorithm_t hash algorithm + * @issuer_name_hash: output buffer with hash of issuer's DN + * @issuer_key_hash: output buffer with hash of issuer's public key + * @serial_number: output buffer with serial number of certificate to check + * + * This function will return the certificate information of the + * @indx'ed request in the OCSP request. The information returned + * corresponds to the CertID structure: + * + * <informalexample><programlisting> + * CertID ::= SEQUENCE { + * hashAlgorithm AlgorithmIdentifier, + * issuerNameHash OCTET STRING, -- Hash of Issuer's DN + * issuerKeyHash OCTET STRING, -- Hash of Issuers public key + * serialNumber CertificateSerialNumber } + * </programlisting></informalexample> + * + * Each of the pointers to output variables may be NULL to indicate + * that the caller is not interested in that value. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error code is returned. If you have reached the last + * CertID available %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE will be + * returned. + **/ +int +gnutls_ocsp_req_get_cert_id(gnutls_ocsp_req_const_t req, + unsigned indx, + gnutls_digest_algorithm_t * digest, + gnutls_datum_t * issuer_name_hash, + gnutls_datum_t * issuer_key_hash, + gnutls_datum_t * serial_number) +{ + gnutls_datum_t sa; + char name[MAX_NAME_SIZE]; + int ret; + + if (req == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + snprintf(name, sizeof(name), + "tbsRequest.requestList.?%u.reqCert.hashAlgorithm.algorithm", + indx + 1); + ret = _gnutls_x509_read_value(req->req, name, &sa); + if (ret == GNUTLS_E_ASN1_ELEMENT_NOT_FOUND) + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + else if (ret < 0) { + gnutls_assert(); + return ret; + } + + ret = gnutls_oid_to_digest((char *) sa.data); + _gnutls_free_datum(&sa); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + if (digest) + *digest = ret; + + if (issuer_name_hash) { + snprintf(name, sizeof(name), + "tbsRequest.requestList.?%u.reqCert.issuerNameHash", + indx + 1); + ret = + _gnutls_x509_read_value(req->req, name, + issuer_name_hash); + if (ret != GNUTLS_E_SUCCESS) { + gnutls_assert(); + return ret; + } + } + + if (issuer_key_hash) { + snprintf(name, sizeof(name), + "tbsRequest.requestList.?%u.reqCert.issuerKeyHash", + indx + 1); + ret = + _gnutls_x509_read_value(req->req, name, + issuer_key_hash); + if (ret != GNUTLS_E_SUCCESS) { + gnutls_assert(); + if (issuer_name_hash) + gnutls_free(issuer_name_hash->data); + return ret; + } + } + + if (serial_number) { + snprintf(name, sizeof(name), + "tbsRequest.requestList.?%u.reqCert.serialNumber", + indx + 1); + ret = + _gnutls_x509_read_value(req->req, name, serial_number); + if (ret != GNUTLS_E_SUCCESS) { + gnutls_assert(); + if (issuer_name_hash) + gnutls_free(issuer_name_hash->data); + if (issuer_key_hash) + gnutls_free(issuer_key_hash->data); + return ret; + } + } + + return GNUTLS_E_SUCCESS; +} + +/** + * gnutls_ocsp_req_add_cert_id: + * @req: should contain a #gnutls_ocsp_req_t type + * @digest: hash algorithm, a #gnutls_digest_algorithm_t value + * @issuer_name_hash: hash of issuer's DN + * @issuer_key_hash: hash of issuer's public key + * @serial_number: serial number of certificate to check + * + * This function will add another request to the OCSP request for a + * particular certificate having the issuer name hash of + * @issuer_name_hash and issuer key hash of @issuer_key_hash (both + * hashed using @digest) and serial number @serial_number. + * + * The information needed corresponds to the CertID structure: + * + * <informalexample><programlisting> + * CertID ::= SEQUENCE { + * hashAlgorithm AlgorithmIdentifier, + * issuerNameHash OCTET STRING, -- Hash of Issuer's DN + * issuerKeyHash OCTET STRING, -- Hash of Issuers public key + * serialNumber CertificateSerialNumber } + * </programlisting></informalexample> + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error code is returned. + **/ +int +gnutls_ocsp_req_add_cert_id(gnutls_ocsp_req_t req, + gnutls_digest_algorithm_t digest, + const gnutls_datum_t * issuer_name_hash, + const gnutls_datum_t * issuer_key_hash, + const gnutls_datum_t * serial_number) +{ + int result; + const char *oid; + + if (req == NULL || issuer_name_hash == NULL + || issuer_key_hash == NULL || serial_number == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + oid = _gnutls_x509_digest_to_oid(hash_to_entry(digest)); + if (oid == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + result = + asn1_write_value(req->req, "tbsRequest.requestList", "NEW", 1); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + result = asn1_write_value + (req->req, + "tbsRequest.requestList.?LAST.reqCert.hashAlgorithm.algorithm", + oid, 1); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + /* XXX we don't support any algorithm with parameters */ + result = asn1_write_value + (req->req, + "tbsRequest.requestList.?LAST.reqCert.hashAlgorithm.parameters", + ASN1_NULL, ASN1_NULL_SIZE); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + result = asn1_write_value + (req->req, + "tbsRequest.requestList.?LAST.reqCert.issuerNameHash", + issuer_name_hash->data, issuer_name_hash->size); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + result = asn1_write_value + (req->req, + "tbsRequest.requestList.?LAST.reqCert.issuerKeyHash", + issuer_key_hash->data, issuer_key_hash->size); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + result = asn1_write_value + (req->req, "tbsRequest.requestList.?LAST.reqCert.serialNumber", + serial_number->data, serial_number->size); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + /* XXX add separate function that can add extensions too */ + result = asn1_write_value + (req->req, + "tbsRequest.requestList.?LAST.singleRequestExtensions", NULL, + 0); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + return GNUTLS_E_SUCCESS; +} + +/** + * gnutls_ocsp_req_add_cert: + * @req: should contain a #gnutls_ocsp_req_t type + * @digest: hash algorithm, a #gnutls_digest_algorithm_t value + * @issuer: issuer of @subject certificate + * @cert: certificate to request status for + * + * This function will add another request to the OCSP request for a + * particular certificate. The issuer name hash, issuer key hash, and + * serial number fields is populated as follows. The issuer name and + * the serial number is taken from @cert. The issuer key is taken + * from @issuer. The hashed values will be hashed using the @digest + * algorithm, normally %GNUTLS_DIG_SHA1. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error code is returned. + **/ +int +gnutls_ocsp_req_add_cert(gnutls_ocsp_req_t req, + gnutls_digest_algorithm_t digest, + gnutls_x509_crt_t issuer, gnutls_x509_crt_t cert) +{ + int ret; + gnutls_datum_t sn, tmp, inh, ikh; + uint8_t inh_buf[MAX_HASH_SIZE]; + uint8_t ikh_buf[MAX_HASH_SIZE]; + size_t inhlen = MAX_HASH_SIZE; + size_t ikhlen = MAX_HASH_SIZE; + + if (req == NULL || issuer == NULL || cert == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + ret = _gnutls_x509_der_encode(cert->cert, + "tbsCertificate.issuer.rdnSequence", + &tmp, 0); + if (ret != GNUTLS_E_SUCCESS) { + gnutls_assert(); + return ret; + } + + ret = gnutls_fingerprint(digest, &tmp, inh_buf, &inhlen); + gnutls_free(tmp.data); + if (ret != GNUTLS_E_SUCCESS) { + gnutls_assert(); + return ret; + } + inh.size = inhlen; + inh.data = inh_buf; + + ret = _gnutls_x509_read_value + (issuer->cert, + "tbsCertificate.subjectPublicKeyInfo.subjectPublicKey", &tmp); + if (ret != GNUTLS_E_SUCCESS) { + gnutls_assert(); + return ret; + } + + ret = gnutls_fingerprint(digest, &tmp, ikh_buf, &ikhlen); + gnutls_free(tmp.data); + if (ret != GNUTLS_E_SUCCESS) { + gnutls_assert(); + return ret; + } + ikh.size = ikhlen; + ikh.data = ikh_buf; + + ret = + _gnutls_x509_read_value(cert->cert, + "tbsCertificate.serialNumber", &sn); + if (ret != GNUTLS_E_SUCCESS) { + gnutls_assert(); + return ret; + } + + ret = gnutls_ocsp_req_add_cert_id(req, digest, &inh, &ikh, &sn); + gnutls_free(sn.data); + if (ret != GNUTLS_E_SUCCESS) { + gnutls_assert(); + return ret; + } + + return GNUTLS_E_SUCCESS; +} + +/** + * gnutls_ocsp_req_get_extension: + * @req: should contain a #gnutls_ocsp_req_t type + * @indx: Specifies which extension OID to get. Use (0) to get the first one. + * @oid: will hold newly allocated buffer with OID of extension, may be NULL + * @critical: output variable with critical flag, may be NULL. + * @data: will hold newly allocated buffer with extension data, may be NULL + * + * This function will return all information about the requested + * extension in the OCSP request. The information returned is the + * OID, the critical flag, and the data itself. The extension OID + * will be stored as a string. Any of @oid, @critical, and @data may + * be NULL which means that the caller is not interested in getting + * that information back. + * + * The caller needs to deallocate memory by calling gnutls_free() on + * @oid->data and @data->data. + * + * Since 3.7.0 @oid->size does not account for the terminating null byte. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error code is returned. If you have reached the last + * extension available %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE will + * be returned. + **/ +int +gnutls_ocsp_req_get_extension(gnutls_ocsp_req_const_t req, + unsigned indx, + gnutls_datum_t * oid, + unsigned int *critical, + gnutls_datum_t * data) +{ + int ret; + char str_critical[10]; + char name[MAX_NAME_SIZE]; + int len; + + if (!req) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + snprintf(name, sizeof(name), + "tbsRequest.requestExtensions.?%u.critical", indx + 1); + len = sizeof(str_critical); + ret = asn1_read_value(req->req, name, str_critical, &len); + if (ret == ASN1_ELEMENT_NOT_FOUND) + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + else if (ret != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(ret); + } + + if (critical) { + if (str_critical[0] == 'T') + *critical = 1; + else + *critical = 0; + } + + if (oid) { + snprintf(name, sizeof(name), + "tbsRequest.requestExtensions.?%u.extnID", + indx + 1); + ret = _gnutls_x509_read_value(req->req, name, oid); + if (ret != GNUTLS_E_SUCCESS) { + gnutls_assert(); + return ret; + } + } + + if (data) { + snprintf(name, sizeof(name), + "tbsRequest.requestExtensions.?%u.extnValue", + indx + 1); + ret = _gnutls_x509_read_value(req->req, name, data); + if (ret != GNUTLS_E_SUCCESS) { + gnutls_assert(); + if (oid) + gnutls_free(oid->data); + return ret; + } + } + + return GNUTLS_E_SUCCESS; +} + +/** + * gnutls_ocsp_req_set_extension: + * @req: should contain a #gnutls_ocsp_req_t type + * @oid: buffer with OID of extension as a string. + * @critical: critical flag, normally false. + * @data: the extension data + * + * This function will add an extension to the OCSP request. Calling + * this function multiple times for the same OID will overwrite values + * from earlier calls. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error code is returned. + **/ +int +gnutls_ocsp_req_set_extension(gnutls_ocsp_req_t req, + const char *oid, + unsigned int critical, + const gnutls_datum_t * data) +{ + if (req == NULL || oid == NULL || data == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + return _gnutls_set_extension(req->req, "tbsRequest.requestExtensions", oid, + data, critical); +} + +/** + * gnutls_ocsp_req_get_nonce: + * @req: should contain a #gnutls_ocsp_req_t type + * @critical: whether nonce extension is marked critical, or NULL + * @nonce: will hold newly allocated buffer with nonce data + * + * This function will return the OCSP request nonce extension data. + * + * The caller needs to deallocate memory by calling gnutls_free() on + * @nonce->data. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error code is returned. + **/ +int +gnutls_ocsp_req_get_nonce(gnutls_ocsp_req_const_t req, + unsigned int *critical, gnutls_datum_t * nonce) +{ + int ret; + gnutls_datum_t tmp; + + if (req == NULL || nonce == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + ret = _gnutls_get_extension(req->req, "tbsRequest.requestExtensions", + GNUTLS_OCSP_NONCE, 0, &tmp, critical); + if (ret != GNUTLS_E_SUCCESS) { + gnutls_assert(); + return ret; + } + + ret = + _gnutls_x509_decode_string(ASN1_ETYPE_OCTET_STRING, tmp.data, + (size_t) tmp.size, nonce, 0); + if (ret < 0) { + gnutls_assert(); + gnutls_free(tmp.data); + return ret; + } + + gnutls_free(tmp.data); + + return GNUTLS_E_SUCCESS; +} + +/** + * gnutls_ocsp_req_set_nonce: + * @req: should contain a #gnutls_ocsp_req_t type + * @critical: critical flag, normally false. + * @nonce: the nonce data + * + * This function will add an nonce extension to the OCSP request. + * Calling this function multiple times will overwrite values from + * earlier calls. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error code is returned. + **/ +int +gnutls_ocsp_req_set_nonce(gnutls_ocsp_req_t req, + unsigned int critical, + const gnutls_datum_t * nonce) +{ + int ret; + gnutls_datum_t dernonce; + unsigned char temp[SIZEOF_UNSIGNED_LONG_INT + 1]; + int len; + + if (req == NULL || nonce == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + asn1_length_der(nonce->size, temp, &len); + + dernonce.size = 1 + len + nonce->size; + dernonce.data = gnutls_malloc(dernonce.size); + if (dernonce.data == NULL) { + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; + } + + dernonce.data[0] = '\x04'; + memcpy(dernonce.data + 1, temp, len); + memcpy(dernonce.data + 1 + len, nonce->data, nonce->size); + + ret = _gnutls_set_extension(req->req, "tbsRequest.requestExtensions", + GNUTLS_OCSP_NONCE, &dernonce, critical); + gnutls_free(dernonce.data); + if (ret != GNUTLS_E_SUCCESS) { + gnutls_assert(); + return ret; + } + + return ret; +} + +/** + * gnutls_ocsp_req_randomize_nonce: + * @req: should contain a #gnutls_ocsp_req_t type + * + * This function will add or update an nonce extension to the OCSP + * request with a newly generated random value. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error code is returned. + **/ +int gnutls_ocsp_req_randomize_nonce(gnutls_ocsp_req_t req) +{ + int ret; + uint8_t rndbuf[23]; + gnutls_datum_t nonce = { rndbuf, sizeof(rndbuf) }; + + if (req == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + ret = gnutls_rnd(GNUTLS_RND_NONCE, rndbuf, sizeof(rndbuf)); + if (ret != GNUTLS_E_SUCCESS) { + gnutls_assert(); + return ret; + } + + ret = gnutls_ocsp_req_set_nonce(req, 0, &nonce); + if (ret != GNUTLS_E_SUCCESS) { + gnutls_assert(); + return ret; + } + + return GNUTLS_E_SUCCESS; +} + +/** + * gnutls_ocsp_resp_get_status: + * @resp: should contain a #gnutls_ocsp_resp_t type + * + * This function will return the status of a OCSP response, an + * #gnutls_ocsp_resp_status_t enumeration. + * + * Returns: status of OCSP request as a #gnutls_ocsp_resp_status_t, or + * a negative error code on error. + **/ +int gnutls_ocsp_resp_get_status(gnutls_ocsp_resp_const_t resp) +{ + uint8_t str[1]; + int len, ret; + + if (resp == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + len = sizeof(str); + ret = asn1_read_value(resp->resp, "responseStatus", str, &len); + if (ret != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(ret); + } + + if (len != 1) + return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET); + + switch (str[0]) { + case GNUTLS_OCSP_RESP_SUCCESSFUL: + case GNUTLS_OCSP_RESP_MALFORMEDREQUEST: + case GNUTLS_OCSP_RESP_INTERNALERROR: + case GNUTLS_OCSP_RESP_TRYLATER: + case GNUTLS_OCSP_RESP_SIGREQUIRED: + case GNUTLS_OCSP_RESP_UNAUTHORIZED: + break; + default: + return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET); + } + + return (int) str[0]; +} + +/** + * gnutls_ocsp_resp_get_response: + * @resp: should contain a #gnutls_ocsp_resp_t type + * @response_type_oid: newly allocated output buffer with response type OID + * @response: newly allocated output buffer with DER encoded response + * + * This function will extract the response type OID in and the + * response data from an OCSP response. Normally the + * @response_type_oid is always "1.3.6.1.5.5.7.48.1.1" which means the + * @response should be decoded as a Basic OCSP Response, but + * technically other response types could be used. + * + * This function is typically only useful when you want to extract the + * response type OID of an response for diagnostic purposes. + * Otherwise gnutls_ocsp_resp_import() will decode the basic OCSP + * response part and the caller need not worry about that aspect. + * + * Since 3.7.0 @response_type_oid->size does not account for the terminating + * null byte. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int +gnutls_ocsp_resp_get_response(gnutls_ocsp_resp_const_t resp, + gnutls_datum_t * response_type_oid, + gnutls_datum_t * response) +{ + int ret; + + if (resp == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + if (response_type_oid != NULL) { + ret = + _gnutls_x509_read_value(resp->resp, + "responseBytes.responseType", + response_type_oid); + if (ret < 0) { + gnutls_assert(); + return ret; + } + } + + if (response != NULL) { + ret = + _gnutls_x509_read_value(resp->resp, + "responseBytes.response", + response); + if (ret < 0) { + gnutls_assert(); + return ret; + } + } + + return GNUTLS_E_SUCCESS; +} + +/** + * gnutls_ocsp_resp_get_version: + * @resp: should contain a #gnutls_ocsp_resp_t type + * + * This function will return the version of the Basic OCSP Response. + * Typically this is always 1 indicating version 1. + * + * Returns: version of Basic OCSP response, or a negative error code + * on error. + **/ +int gnutls_ocsp_resp_get_version(gnutls_ocsp_resp_const_t resp) +{ + if (resp == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + return _gnutls_x509_get_version(resp->resp, "tbsResponseData.version"); +} + +/** + * gnutls_ocsp_resp_get_responder: + * @resp: should contain a #gnutls_ocsp_resp_t type + * @dn: newly allocated buffer with name + * + * This function will extract the name of the Basic OCSP Response in + * the provided buffer. The name will be in the form + * "C=xxxx,O=yyyy,CN=zzzz" as described in RFC2253. The output string + * will be ASCII or UTF-8 encoded, depending on the certificate data. + * + * If the responder ID is not a name but a hash, this function + * will return zero and the @dn elements will be set to %NULL. + * + * The caller needs to deallocate memory by calling gnutls_free() on + * @dn->data. + * + * This function does not output a fully RFC4514 compliant string, if + * that is required see gnutls_ocsp_resp_get_responder2(). + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error code is returned. When no data exist it will + * return success and set @dn elements to zero. + **/ +int +gnutls_ocsp_resp_get_responder(gnutls_ocsp_resp_const_t resp, + gnutls_datum_t * dn) +{ + int ret; + + ret = gnutls_ocsp_resp_get_responder2(resp, dn, GNUTLS_X509_DN_FLAG_COMPAT); + if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { + dn->data = NULL; + dn->size = 0; + return 0; /* for backwards compatibility */ + } + + return ret; +} + +/** + * gnutls_ocsp_resp_get_responder2: + * @resp: should contain a #gnutls_ocsp_resp_t type + * @dn: newly allocated buffer with name + * @flags: zero or %GNUTLS_X509_DN_FLAG_COMPAT + * + * This function will extract the name of the Basic OCSP Response in + * the provided buffer. The name will be in the form + * "C=xxxx,O=yyyy,CN=zzzz" as described in RFC2253. The output string + * will be ASCII or UTF-8 encoded, depending on the certificate data. + * + * If the responder ID is not a name but a hash, this function + * will return zero and the @dn elements will be set to %NULL. + * + * The caller needs to deallocate memory by calling gnutls_free() on + * @dn->data. + * + * When the flag %GNUTLS_X509_DN_FLAG_COMPAT is specified, the output + * format will match the format output by previous to 3.5.6 versions of GnuTLS + * which was not not fully RFC4514-compliant. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error code is returned. When no data exist it will return + * %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE. + **/ +int +gnutls_ocsp_resp_get_responder2(gnutls_ocsp_resp_const_t resp, + gnutls_datum_t * dn, unsigned flags) +{ + if (resp == NULL || dn == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + dn->data = NULL; + dn->size = 0; + + return _gnutls_x509_get_dn(resp->basicresp, + "tbsResponseData.responderID.byName", + dn, flags); +} + +/** + * gnutls_ocsp_resp_get_responder_by_key: + * @resp: should contain a #gnutls_ocsp_resp_t type + * @type: should be %GNUTLS_OCSP_RESP_ID_KEY or %GNUTLS_OCSP_RESP_ID_DN + * @raw: newly allocated buffer with the raw ID + * + * This function will extract the raw key (or DN) ID of the Basic OCSP Response in + * the provided buffer. If the responder ID is not a key ID then + * this function will return %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE. + * + * The caller needs to deallocate memory by calling gnutls_free() on + * @dn->data. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error code is returned. + **/ +int +gnutls_ocsp_resp_get_responder_raw_id(gnutls_ocsp_resp_const_t resp, + unsigned type, + gnutls_datum_t * raw) +{ + int ret; + + if (resp == NULL || raw == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + if (type == GNUTLS_OCSP_RESP_ID_KEY) + ret = _gnutls_x509_read_value(resp->basicresp, "tbsResponseData.responderID.byKey", raw); + else { + gnutls_datum_t tmp; + + /* simply reading a CHOICE of CHOICE value doesn't work in libtasn1 */ + ret = _gnutls_x509_get_raw_field2(resp->basicresp, &resp->der, + "tbsResponseData.responderID.byName", + &tmp); + if (ret >= 0) { + int real; + /* skip the tag */ + if (tmp.size < 2) { + gnutls_assert(); + ret = GNUTLS_E_ASN1_GENERIC_ERROR; + goto fail; + } + + tmp.data++; + tmp.size--; + + ret = asn1_get_length_der(tmp.data, tmp.size, &real); + if (ret < 0) { + gnutls_assert(); + ret = GNUTLS_E_ASN1_GENERIC_ERROR; + goto fail; + } + + if (tmp.size < (unsigned)real) { + gnutls_assert(); + ret = GNUTLS_E_ASN1_GENERIC_ERROR; + goto fail; + } + + tmp.data+=real; + tmp.size-=real; + + ret = _gnutls_set_datum(raw, tmp.data, tmp.size); + } + } + + if (ret == GNUTLS_E_ASN1_ELEMENT_NOT_FOUND || ret == GNUTLS_E_ASN1_VALUE_NOT_FOUND) + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + + fail: + return ret; +} + +/** + * gnutls_ocsp_resp_get_produced: + * @resp: should contain a #gnutls_ocsp_resp_t type + * + * This function will return the time when the OCSP response was + * signed. + * + * Returns: signing time, or (time_t)-1 on error. + **/ +time_t gnutls_ocsp_resp_get_produced(gnutls_ocsp_resp_const_t resp) +{ + char ttime[MAX_TIME]; + int len, ret; + time_t c_time; + + if (resp == NULL || resp->basicresp == NULL) { + gnutls_assert(); + return (time_t) (-1); + } + + len = sizeof(ttime) - 1; + ret = + asn1_read_value(resp->basicresp, "tbsResponseData.producedAt", + ttime, &len); + if (ret != ASN1_SUCCESS) { + gnutls_assert(); + return (time_t) (-1); + } + + c_time = _gnutls_x509_generalTime2gtime(ttime); + + return c_time; +} + +/** + * gnutls_ocsp_resp_check_crt: + * @resp: should contain a #gnutls_ocsp_resp_t type + * @indx: Specifies response number to get. Use (0) to get the first one. + * @crt: The certificate to check + * + * This function will check whether the OCSP response + * is about the provided certificate. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error code is returned. + * + * Since: 3.1.3 + **/ +int +gnutls_ocsp_resp_check_crt(gnutls_ocsp_resp_const_t resp, + unsigned int indx, gnutls_x509_crt_t crt) +{ + int ret; + gnutls_digest_algorithm_t digest; + gnutls_datum_t rdn_hash = { NULL, 0 }, rserial = { + NULL, 0}; + gnutls_datum_t cserial = { NULL, 0 }; + gnutls_datum_t dn = { NULL, 0 }; + uint8_t cdn_hash[MAX_HASH_SIZE]; + size_t t, hash_len; + + if (resp == NULL) + return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + + ret = + gnutls_ocsp_resp_get_single(resp, indx, &digest, &rdn_hash, + NULL, &rserial, NULL, NULL, NULL, + NULL, NULL); + if (ret < 0) + return gnutls_assert_val(ret); + + if (rserial.size == 0 || digest == GNUTLS_DIG_UNKNOWN) { + ret = gnutls_assert_val(GNUTLS_E_OCSP_RESPONSE_ERROR); + goto cleanup; + } + + hash_len = _gnutls_hash_get_algo_len(hash_to_entry(digest)); + if (hash_len != rdn_hash.size) { + ret = gnutls_assert_val(GNUTLS_E_OCSP_RESPONSE_ERROR); + goto cleanup; + } + + cserial.size = rserial.size; + cserial.data = gnutls_malloc(cserial.size); + if (cserial.data == NULL) { + ret = gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + goto cleanup; + } + + t = cserial.size; + ret = gnutls_x509_crt_get_serial(crt, cserial.data, &t); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + cserial.size = t; + + if (rserial.size != cserial.size + || memcmp(cserial.data, rserial.data, rserial.size) != 0) { + ret = GNUTLS_E_OCSP_RESPONSE_ERROR; + gnutls_assert(); + goto cleanup; + } + + ret = gnutls_x509_crt_get_raw_issuer_dn(crt, &dn); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = _gnutls_hash_fast(digest, dn.data, dn.size, cdn_hash); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + if (memcmp(cdn_hash, rdn_hash.data, hash_len) != 0) { + ret = GNUTLS_E_OCSP_RESPONSE_ERROR; + gnutls_assert(); + goto cleanup; + } + + ret = 0; + + cleanup: + gnutls_free(rdn_hash.data); + gnutls_free(rserial.data); + gnutls_free(cserial.data); + gnutls_free(dn.data); + + return ret; +} + +/** + * gnutls_ocsp_resp_get_single: + * @resp: should contain a #gnutls_ocsp_resp_t type + * @indx: Specifies response number to get. Use (0) to get the first one. + * @digest: output variable with #gnutls_digest_algorithm_t hash algorithm + * @issuer_name_hash: output buffer with hash of issuer's DN + * @issuer_key_hash: output buffer with hash of issuer's public key + * @serial_number: output buffer with serial number of certificate to check + * @cert_status: a certificate status, a #gnutls_ocsp_cert_status_t enum. + * @this_update: time at which the status is known to be correct. + * @next_update: when newer information will be available, or (time_t)-1 if unspecified + * @revocation_time: when @cert_status is %GNUTLS_OCSP_CERT_REVOKED, holds time of revocation. + * @revocation_reason: revocation reason, a #gnutls_x509_crl_reason_t enum. + * + * This function will return the certificate information of the + * @indx'ed response in the Basic OCSP Response @resp. The + * information returned corresponds to the OCSP SingleResponse structure + * except the final singleExtensions. + * + * Each of the pointers to output variables may be NULL to indicate + * that the caller is not interested in that value. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error code is returned. If you have reached the last + * CertID available %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE will be + * returned. + **/ +int +gnutls_ocsp_resp_get_single(gnutls_ocsp_resp_const_t resp, + unsigned indx, + gnutls_digest_algorithm_t * digest, + gnutls_datum_t * issuer_name_hash, + gnutls_datum_t * issuer_key_hash, + gnutls_datum_t * serial_number, + unsigned int *cert_status, + time_t * this_update, + time_t * next_update, + time_t * revocation_time, + unsigned int *revocation_reason) +{ + char name[MAX_NAME_SIZE]; + int ret, result; + char oidtmp[MAX_OID_SIZE]; + int len; + char ttime[MAX_TIME]; + + /* initialize any allocated values to NULL, to allow deallocation + * on error. */ + if (issuer_name_hash) + issuer_name_hash->data = NULL; + if (issuer_key_hash) + issuer_key_hash->data = NULL; + if (serial_number) + serial_number->data = NULL; + + if (digest) { + snprintf(name, sizeof(name), + "tbsResponseData.responses.?%u.certID.hashAlgorithm.algorithm", + indx + 1); + len = sizeof(oidtmp); + result = asn1_read_value(resp->basicresp, name, oidtmp, &len); + if (result == ASN1_ELEMENT_NOT_FOUND) { + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + } else if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + ret = gnutls_oid_to_digest(oidtmp); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + *digest = ret; + } + + if (issuer_name_hash) { + snprintf(name, sizeof(name), + "tbsResponseData.responses.?%u.certID.issuerNameHash", + indx + 1); + ret = _gnutls_x509_read_value(resp->basicresp, name, + issuer_name_hash); + if (ret < 0) { + gnutls_assert(); + return ret; + } + } + + if (issuer_key_hash) { + snprintf(name, sizeof(name), + "tbsResponseData.responses.?%u.certID.issuerKeyHash", + indx + 1); + ret = _gnutls_x509_read_value(resp->basicresp, name, + issuer_key_hash); + if (ret < 0) { + gnutls_assert(); + goto fail; + } + } + + if (serial_number) { + snprintf(name, sizeof(name), + "tbsResponseData.responses.?%u.certID.serialNumber", + indx + 1); + ret = _gnutls_x509_read_value(resp->basicresp, name, + serial_number); + if (ret < 0) { + gnutls_assert(); + goto fail; + } + } + + if (cert_status) { + snprintf(name, sizeof(name), + "tbsResponseData.responses.?%u.certStatus", + indx + 1); + + len = sizeof(oidtmp); + result = asn1_read_value(resp->basicresp, name, oidtmp, &len); + if (result == ASN1_ELEMENT_NOT_FOUND) { + gnutls_assert(); + ret = GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + goto fail; + } else if (result != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(result); + goto fail; + } + + if (len == 5 && memcmp(oidtmp, "good", len) == 0) + *cert_status = GNUTLS_OCSP_CERT_GOOD; + else if (len == 8 + && memcmp(oidtmp, "revoked", len) == 0) + *cert_status = GNUTLS_OCSP_CERT_REVOKED; + else if (len == 8 + && memcmp(oidtmp, "unknown", len) == 0) + *cert_status = GNUTLS_OCSP_CERT_UNKNOWN; + else { + gnutls_assert(); + ret = GNUTLS_E_ASN1_DER_ERROR; + goto fail; + } + } + + if (this_update) { + snprintf(name, sizeof(name), + "tbsResponseData.responses.?%u.thisUpdate", + indx + 1); + len = sizeof(ttime) - 1; + result = asn1_read_value(resp->basicresp, name, ttime, &len); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + ret = GNUTLS_E_ASN1_DER_ERROR; + goto fail; + } else { + *this_update = + _gnutls_x509_generalTime2gtime(ttime); + } + } + + if (next_update) { + snprintf(name, sizeof(name), + "tbsResponseData.responses.?%u.nextUpdate", + indx + 1); + len = sizeof(ttime) - 1; + result = asn1_read_value(resp->basicresp, name, ttime, &len); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + *next_update = (time_t) (-1); + } else + *next_update = + _gnutls_x509_generalTime2gtime(ttime); + } + + if (revocation_time) { + snprintf(name, sizeof(name), + "tbsResponseData.responses.?%u.certStatus." + "revoked.revocationTime", indx + 1); + len = sizeof(ttime) - 1; + result = asn1_read_value(resp->basicresp, name, ttime, &len); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + *revocation_time = (time_t) (-1); + } else + *revocation_time = + _gnutls_x509_generalTime2gtime(ttime); + } + + /* revocation_reason */ + if (revocation_reason) { + snprintf(name, sizeof(name), + "tbsResponseData.responses.?%u.certStatus." + "revoked.revocationReason", indx + 1); + + ret = _gnutls_x509_read_uint(resp->basicresp, name, + revocation_reason); + if (ret < 0) + *revocation_reason = + GNUTLS_X509_CRLREASON_UNSPECIFIED; + } + + return GNUTLS_E_SUCCESS; + fail: + if (issuer_name_hash) + gnutls_free(issuer_name_hash->data); + if (issuer_key_hash) + gnutls_free(issuer_key_hash->data); + if (serial_number) + gnutls_free(serial_number->data); + return ret; +} + +/** + * gnutls_ocsp_resp_get_extension: + * @resp: should contain a #gnutls_ocsp_resp_t type + * @indx: Specifies which extension OID to get. Use (0) to get the first one. + * @oid: will hold newly allocated buffer with OID of extension, may be NULL + * @critical: output variable with critical flag, may be NULL. + * @data: will hold newly allocated buffer with extension data, may be NULL + * + * This function will return all information about the requested + * extension in the OCSP response. The information returned is the + * OID, the critical flag, and the data itself. The extension OID + * will be stored as a string. Any of @oid, @critical, and @data may + * be NULL which means that the caller is not interested in getting + * that information back. + * + * The caller needs to deallocate memory by calling gnutls_free() on + * @oid->data and @data->data. + * + * Since 3.7.0 @oid->size does not account for the terminating null byte. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error code is returned. If you have reached the last + * extension available %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE will + * be returned. + **/ +int +gnutls_ocsp_resp_get_extension(gnutls_ocsp_resp_const_t resp, + unsigned indx, + gnutls_datum_t * oid, + unsigned int *critical, + gnutls_datum_t * data) +{ + int ret; + char str_critical[10]; + char name[MAX_NAME_SIZE]; + int len; + + if (!resp) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + snprintf(name, sizeof(name), + "tbsResponseData.responseExtensions.?%u.critical", + indx + 1); + len = sizeof(str_critical); + ret = asn1_read_value(resp->basicresp, name, str_critical, &len); + if (ret == ASN1_ELEMENT_NOT_FOUND) + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + else if (ret != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(ret); + } + + if (critical) { + if (str_critical[0] == 'T') + *critical = 1; + else + *critical = 0; + } + + if (oid) { + snprintf(name, sizeof(name), + "tbsResponseData.responseExtensions.?%u.extnID", + indx + 1); + ret = _gnutls_x509_read_value(resp->basicresp, name, oid); + if (ret != GNUTLS_E_SUCCESS) { + gnutls_assert(); + return ret; + } + } + + if (data) { + snprintf(name, sizeof(name), + "tbsResponseData.responseExtensions.?%u.extnValue", + indx + 1); + ret = _gnutls_x509_read_value(resp->basicresp, name, data); + if (ret != GNUTLS_E_SUCCESS) { + gnutls_assert(); + if (oid) + gnutls_free(oid->data); + return ret; + } + } + + return GNUTLS_E_SUCCESS; +} + +/** + * gnutls_ocsp_resp_get_nonce: + * @resp: should contain a #gnutls_ocsp_resp_t type + * @critical: whether nonce extension is marked critical + * @nonce: will hold newly allocated buffer with nonce data + * + * This function will return the Basic OCSP Response nonce extension + * data. + * + * The caller needs to deallocate memory by calling gnutls_free() on + * @nonce->data. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error code is returned. + **/ +int +gnutls_ocsp_resp_get_nonce(gnutls_ocsp_resp_const_t resp, + unsigned int *critical, gnutls_datum_t * nonce) +{ + int ret; + gnutls_datum_t tmp; + + ret = + _gnutls_get_extension(resp->basicresp, + "tbsResponseData.responseExtensions", + GNUTLS_OCSP_NONCE, 0, &tmp, critical); + if (ret != GNUTLS_E_SUCCESS) { + gnutls_assert(); + return ret; + } + + ret = + _gnutls_x509_decode_string(ASN1_ETYPE_OCTET_STRING, tmp.data, + (size_t) tmp.size, nonce, 0); + if (ret < 0) { + gnutls_assert(); + gnutls_free(tmp.data); + return ret; + } + + gnutls_free(tmp.data); + + return GNUTLS_E_SUCCESS; +} + +/** + * gnutls_ocsp_resp_get_signature_algorithm: + * @resp: should contain a #gnutls_ocsp_resp_t type + * + * This function will return a value of the #gnutls_sign_algorithm_t + * enumeration that is the signature algorithm that has been used to + * sign the OCSP response. + * + * Returns: a #gnutls_sign_algorithm_t value, or a negative error code + * on error. + **/ +int gnutls_ocsp_resp_get_signature_algorithm(gnutls_ocsp_resp_const_t resp) +{ + int ret; + gnutls_datum_t sa; + + ret = _gnutls_x509_read_value(resp->basicresp, + "signatureAlgorithm.algorithm", &sa); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + ret = gnutls_oid_to_sign((char *) sa.data); + + _gnutls_free_datum(&sa); + + return ret; +} + +/** + * gnutls_ocsp_resp_get_signature: + * @resp: should contain a #gnutls_ocsp_resp_t type + * @sig: newly allocated output buffer with signature data + * + * This function will extract the signature field of a OCSP response. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int +gnutls_ocsp_resp_get_signature(gnutls_ocsp_resp_const_t resp, + gnutls_datum_t * sig) +{ + int ret; + + if (resp == NULL || sig == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + ret = _gnutls_x509_read_value(resp->basicresp, "signature", sig); + if (ret != GNUTLS_E_SUCCESS) { + gnutls_assert(); + return ret; + } + + return GNUTLS_E_SUCCESS; +} + +/** + * gnutls_ocsp_resp_get_certs: + * @resp: should contain a #gnutls_ocsp_resp_t type + * @certs: newly allocated array with #gnutls_x509_crt_t certificates + * @ncerts: output variable with number of allocated certs. + * + * This function will extract the X.509 certificates found in the + * Basic OCSP Response. The @certs output variable will hold a newly + * allocated zero-terminated array with X.509 certificates. + * + * Every certificate in the array needs to be de-allocated with + * gnutls_x509_crt_deinit() and the array itself must be freed using + * gnutls_free(). + * + * Both the @certs and @ncerts variables may be NULL. Then the + * function will work as normal but will not return the NULL:d + * information. This can be used to get the number of certificates + * only, or to just get the certificate array without its size. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int +gnutls_ocsp_resp_get_certs(gnutls_ocsp_resp_const_t resp, + gnutls_x509_crt_t ** certs, size_t * ncerts) +{ + int ret; + size_t ctr = 0, i; + gnutls_x509_crt_t *tmpcerts = NULL, *tmpcerts2; + gnutls_datum_t c = { NULL, 0 }; + + if (resp == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + tmpcerts = gnutls_malloc(sizeof(*tmpcerts)); + if (tmpcerts == NULL) { + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; + } + + for (;;) { + char name[MAX_NAME_SIZE]; + + snprintf(name, sizeof(name), "certs.?%u", + (unsigned int) (ctr + 1)); + ret = + _gnutls_x509_der_encode(resp->basicresp, name, &c, 0); + if (ret == GNUTLS_E_ASN1_ELEMENT_NOT_FOUND) + break; + if (ret != GNUTLS_E_SUCCESS) { + gnutls_assert(); + goto error; + } + + if (unlikely(INT_ADD_OVERFLOW(ctr, 2))) { + ret = gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + goto error; + } + + tmpcerts2 = _gnutls_reallocarray_fast(tmpcerts, ctr + 2, + sizeof(*tmpcerts)); + if (tmpcerts2 == NULL) { + gnutls_assert(); + ret = GNUTLS_E_MEMORY_ERROR; + goto error; + } + tmpcerts = tmpcerts2; + + ret = gnutls_x509_crt_init(&tmpcerts[ctr]); + if (ret != GNUTLS_E_SUCCESS) { + gnutls_assert(); + goto error; + } + ctr++; + + ret = gnutls_x509_crt_import(tmpcerts[ctr - 1], &c, + GNUTLS_X509_FMT_DER); + if (ret != GNUTLS_E_SUCCESS) { + gnutls_assert(); + goto error; + } + + gnutls_free(c.data); + } + + tmpcerts[ctr] = NULL; + + if (ncerts) + *ncerts = ctr; + if (certs) + *certs = tmpcerts; + else { + /* clean up memory */ + ret = GNUTLS_E_SUCCESS; + goto error; + } + + return GNUTLS_E_SUCCESS; + + error: + gnutls_free(c.data); + for (i = 0; i < ctr; i++) + gnutls_x509_crt_deinit(tmpcerts[i]); + gnutls_free(tmpcerts); + return ret; +} + +/* Search the OCSP response for a certificate matching the responderId + mentioned in the OCSP response. */ +static gnutls_x509_crt_t find_signercert(gnutls_ocsp_resp_const_t resp) +{ + int rc; + gnutls_x509_crt_t *certs = NULL; + size_t ncerts = 0, i; + gnutls_datum_t riddn = {NULL, 0}; + gnutls_datum_t keyid = {NULL, 0}; + gnutls_x509_crt_t signercert = NULL; + + rc = gnutls_ocsp_resp_get_responder_raw_id(resp, GNUTLS_OCSP_RESP_ID_DN, &riddn); + if (rc == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { + gnutls_assert(); + rc = gnutls_ocsp_resp_get_responder_raw_id(resp, GNUTLS_OCSP_RESP_ID_KEY, &keyid); + } + if (rc != GNUTLS_E_SUCCESS) { + gnutls_assert(); + return NULL; + } + + rc = gnutls_ocsp_resp_get_certs(resp, &certs, &ncerts); + if (rc != GNUTLS_E_SUCCESS) { + gnutls_assert(); + signercert = NULL; + goto quit; + } + + for (i = 0; i < ncerts; i++) { + assert(certs[i] != NULL); + _gnutls_cert_log("checking whether signed against", certs[i]); + if (keyid.data != NULL) { + uint8_t digest[64]; /* to support longer key IDs */ + gnutls_datum_t spki; + size_t digest_size = sizeof(digest); + int len; + + _gnutls_debug_log("checking key ID against SPK identifier\n"); + + /* check subject key identifier as well, some certificates + * match that, but not the hash */ + rc = gnutls_x509_crt_get_subject_key_id(certs[i], digest, &digest_size, NULL); + if (rc >= 0 && digest_size == keyid.size && + memcmp(keyid.data, digest, digest_size) == 0) { + signercert = certs[i]; + goto quit; + } + + _gnutls_debug_log("checking key ID against SPKI hash\n"); + + /* continue with checking the hash */ + rc = _gnutls_x509_get_raw_field2(certs[i]->cert, &certs[i]->der, + "tbsCertificate.subjectPublicKeyInfo.subjectPublicKey", + &spki); + if (rc < 0 || spki.size < 6) { + gnutls_assert(); + signercert = NULL; + continue; + } + + /* For some reason the protocol requires we skip the + * tag, length and number of unused bits. + */ + if (spki.data[0] != 0x03) { /* bit string */ + gnutls_assert(); + signercert = NULL; + continue; + } + + rc = asn1_get_length_der(spki.data+1, spki.size-1, &len); + if (rc <= 0) { + gnutls_assert(); + signercert = NULL; + continue; + } + len += 1+1; /* skip unused bits as well */ + if (len >= (int)spki.size) { + gnutls_assert(); + signercert = NULL; + continue; + } + + rc = gnutls_hash_fast(GNUTLS_DIG_SHA1, spki.data+len, spki.size-len, digest); + if (rc < 0) { + gnutls_assert(); + signercert = NULL; + continue; + } + + if ((20 == keyid.size) && + memcmp(keyid.data, digest, 20) == 0) { + signercert = certs[i]; + goto quit; + } + gnutls_assert(); + } else { + _gnutls_debug_log("checking issuer DN\n"); + + assert(riddn.data != NULL); + if ((certs[i]->raw_dn.size == riddn.size) + && memcmp(riddn.data, certs[i]->raw_dn.data, riddn.size) == 0) { + signercert = certs[i]; + goto quit; + } + gnutls_assert(); + } + } + + gnutls_assert(); + signercert = NULL; + + quit: + gnutls_free(riddn.data); + gnutls_free(keyid.data); + for (i = 0; i < ncerts; i++) + if (certs[i] != signercert) + gnutls_x509_crt_deinit(certs[i]); + gnutls_free(certs); + return signercert; +} + +static int +_ocsp_resp_verify_direct(gnutls_ocsp_resp_const_t resp, + gnutls_x509_crt_t signercert, + unsigned int *verify, unsigned int flags) +{ + gnutls_datum_t sig = { NULL }; + gnutls_datum_t data = { NULL }; + gnutls_pubkey_t pubkey = NULL; + int sigalg; + int rc; + + if (resp == NULL || signercert == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + rc = gnutls_ocsp_resp_get_signature_algorithm(resp); + if (rc < 0) { + gnutls_assert(); + goto done; + } + sigalg = rc; + + rc = _gnutls_x509_get_raw_field2(resp->basicresp, &resp->der, "tbsResponseData", &data); + if (rc != GNUTLS_E_SUCCESS) { + gnutls_assert(); + goto done; + } + + rc = gnutls_pubkey_init(&pubkey); + if (rc != GNUTLS_E_SUCCESS) { + gnutls_assert(); + goto done; + } + + _gnutls_cert_log("ocsp signer", signercert); + + rc = gnutls_pubkey_import_x509(pubkey, signercert, 0); + if (rc != GNUTLS_E_SUCCESS) { + gnutls_assert(); + goto done; + } + + rc = gnutls_ocsp_resp_get_signature(resp, &sig); + if (rc != GNUTLS_E_SUCCESS) { + gnutls_assert(); + goto done; + } + + rc = gnutls_pubkey_verify_data2(pubkey, sigalg, flags, &data, &sig); + if (rc == GNUTLS_E_PK_SIG_VERIFY_FAILED) { + gnutls_assert(); + *verify = GNUTLS_OCSP_VERIFY_SIGNATURE_FAILURE; + } else if (rc < 0) { + gnutls_assert(); + goto done; + } else + *verify = 0; + + rc = GNUTLS_E_SUCCESS; + + done: + gnutls_free(sig.data); + gnutls_pubkey_deinit(pubkey); + + return rc; +} + +static inline unsigned int vstatus_to_ocsp_status(unsigned int status) +{ + unsigned int ostatus; + + if (status & GNUTLS_CERT_INSECURE_ALGORITHM) + ostatus = GNUTLS_OCSP_VERIFY_INSECURE_ALGORITHM; + else if (status & GNUTLS_CERT_NOT_ACTIVATED) + ostatus = GNUTLS_OCSP_VERIFY_CERT_NOT_ACTIVATED; + else if (status & GNUTLS_CERT_EXPIRED) + ostatus = GNUTLS_OCSP_VERIFY_CERT_EXPIRED; + else + ostatus = GNUTLS_OCSP_VERIFY_UNTRUSTED_SIGNER; + + return ostatus; +} + +static int check_ocsp_purpose(gnutls_x509_crt_t signercert) +{ + char oidtmp[MAX_OID_SIZE]; + size_t oidsize; + int indx, rc; + + for (indx = 0;; indx++) { + oidsize = sizeof(oidtmp); + rc = gnutls_x509_crt_get_key_purpose_oid(signercert, indx, + oidtmp, &oidsize, + NULL); + + if (rc == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { + gnutls_assert(); + return rc; + } else if (rc == GNUTLS_E_SHORT_MEMORY_BUFFER) { + gnutls_assert(); + continue; + } else if (rc != GNUTLS_E_SUCCESS) { + return gnutls_assert_val(rc); + } + + if (memcmp(oidtmp, GNUTLS_KP_OCSP_SIGNING, oidsize) != 0) { + gnutls_assert(); + continue; + } + break; + } + + return 0; +} + +/** + * gnutls_ocsp_resp_verify_direct: + * @resp: should contain a #gnutls_ocsp_resp_t type + * @issuer: certificate believed to have signed the response + * @verify: output variable with verification status, an #gnutls_ocsp_verify_reason_t + * @flags: verification flags from #gnutls_certificate_verify_flags + * + * Verify signature of the Basic OCSP Response against the public key + * in the @issuer certificate. + * + * The output @verify variable will hold verification status codes + * (e.g., %GNUTLS_OCSP_VERIFY_SIGNER_NOT_FOUND, + * %GNUTLS_OCSP_VERIFY_INSECURE_ALGORITHM) which are only valid if the + * function returned %GNUTLS_E_SUCCESS. + * + * Note that the function returns %GNUTLS_E_SUCCESS even when + * verification failed. The caller must always inspect the @verify + * variable to find out the verification status. + * + * The @flags variable should be 0 for now. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int +gnutls_ocsp_resp_verify_direct(gnutls_ocsp_resp_const_t resp, + gnutls_x509_crt_t issuer, + unsigned int *verify, unsigned int flags) +{ + gnutls_x509_crt_t signercert; + int rc; + + if (resp == NULL || issuer == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + signercert = find_signercert(resp); + if (!signercert) { + signercert = issuer; + } else if (!gnutls_x509_crt_equals(signercert, issuer)) { + + /* response contains a signer. Verify him */ + + unsigned int vtmp; + + rc = gnutls_x509_crt_verify(signercert, &issuer, 1, flags, + &vtmp); + if (rc != GNUTLS_E_SUCCESS) { + gnutls_assert(); + goto done; + } + + if (vtmp != 0) { + _gnutls_reason_log("cert verification", vtmp); + *verify = vstatus_to_ocsp_status(vtmp); + gnutls_assert(); + rc = GNUTLS_E_SUCCESS; + goto done; + } + + rc = check_ocsp_purpose(signercert); + if (rc < 0) { + gnutls_assert(); + *verify = GNUTLS_OCSP_VERIFY_SIGNER_KEYUSAGE_ERROR; + rc = GNUTLS_E_SUCCESS; + goto done; + } + } + + rc = _ocsp_resp_verify_direct(resp, signercert, verify, flags); + + done: + if (signercert != issuer) + gnutls_x509_crt_deinit(signercert); + + return rc; +} + +/** + * gnutls_ocsp_resp_verify: + * @resp: should contain a #gnutls_ocsp_resp_t type + * @trustlist: trust anchors as a #gnutls_x509_trust_list_t type + * @verify: output variable with verification status, an #gnutls_ocsp_verify_reason_t + * @flags: verification flags from #gnutls_certificate_verify_flags + * + * Verify signature of the Basic OCSP Response against the public key + * in the certificate of a trusted signer. The @trustlist should be + * populated with trust anchors. The function will extract the signer + * certificate from the Basic OCSP Response and will verify it against + * the @trustlist. A trusted signer is a certificate that is either + * in @trustlist, or it is signed directly by a certificate in + * @trustlist and has the id-ad-ocspSigning Extended Key Usage bit + * set. + * + * The output @verify variable will hold verification status codes + * (e.g., %GNUTLS_OCSP_VERIFY_SIGNER_NOT_FOUND, + * %GNUTLS_OCSP_VERIFY_INSECURE_ALGORITHM) which are only valid if the + * function returned %GNUTLS_E_SUCCESS. + * + * Note that the function returns %GNUTLS_E_SUCCESS even when + * verification failed. The caller must always inspect the @verify + * variable to find out the verification status. + * + * The @flags variable should be 0 for now. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int +gnutls_ocsp_resp_verify(gnutls_ocsp_resp_const_t resp, + gnutls_x509_trust_list_t trustlist, + unsigned int *verify, unsigned int flags) +{ + gnutls_x509_crt_t signercert = NULL; + int rc; + + /* Algorithm: + 1. Find signer cert. + 1a. Search in OCSP response Certificate field for responderID. + 1b. Verify that signer cert is trusted. + 2a. It is in trustlist? + 2b. It has OCSP key usage and directly signed by a CA in trustlist? + 3. Verify signature of Basic Response using public key from signer cert. + */ + + signercert = find_signercert(resp); + if (!signercert) { + gnutls_datum_t dn; + + rc = gnutls_ocsp_resp_get_responder_raw_id(resp, GNUTLS_OCSP_RESP_ID_DN, &dn); + if (rc < 0) { + gnutls_assert(); + *verify = GNUTLS_OCSP_VERIFY_SIGNER_NOT_FOUND; + rc = GNUTLS_E_SUCCESS; + goto done; + } + + rc = gnutls_x509_trust_list_get_issuer_by_dn(trustlist, &dn, &signercert, 0); + gnutls_free(dn.data); + + if (rc < 0) { + gnutls_assert(); + *verify = GNUTLS_OCSP_VERIFY_SIGNER_NOT_FOUND; + rc = GNUTLS_E_SUCCESS; + goto done; + } + } else { + /* Either the signer is directly trusted (i.e., in trustlist) or it + is directly signed by something in trustlist and has proper OCSP + extkeyusage. */ + rc = _gnutls_trustlist_inlist(trustlist, signercert); + if (rc == 0) { + /* not in trustlist, need to verify signature and bits */ + unsigned vtmp; + gnutls_typed_vdata_st vdata; + + vdata.type = GNUTLS_DT_KEY_PURPOSE_OID; + vdata.data = (void*)GNUTLS_KP_OCSP_SIGNING; + vdata.size = 0; + + gnutls_assert(); + + rc = gnutls_x509_trust_list_verify_crt2(trustlist, + &signercert, 1, + &vdata, 1, + flags, &vtmp, NULL); + if (rc != GNUTLS_E_SUCCESS) { + gnutls_assert(); + goto done; + } + + if (vtmp != 0) { + *verify = vstatus_to_ocsp_status(vtmp); + gnutls_assert(); + rc = GNUTLS_E_SUCCESS; + goto done; + } + + rc = check_ocsp_purpose(signercert); + if (rc < 0) { + gnutls_assert(); + *verify = GNUTLS_OCSP_VERIFY_SIGNER_KEYUSAGE_ERROR; + rc = GNUTLS_E_SUCCESS; + goto done; + } + } + } + + rc = _ocsp_resp_verify_direct(resp, signercert, verify, flags); + + done: + gnutls_x509_crt_deinit(signercert); + + return rc; +} + +/** + * gnutls_x509_ocsp_resp_list_import2: + * @ocsps: Will hold the parsed OCSP response list. + * @size: It will contain the size of the list. + * @resp_data: The PEM encoded OCSP list. + * @format: One of %GNUTLS_X509_FMT_PEM or %GNUTLS_X509_FMT_DER + * @flags: must be (0) or an OR'd sequence of gnutls_certificate_import_flags. + * + * This function will convert the given PEM encoded OCSP response list + * to the native gnutls_ocsp_resp_t format. The output will be stored + * in @ocsps which will be allocated and initialized. + * + * The OCSP responses should have a header of "OCSP RESPONSE". + * + * To deinitialize responses, you need to deinitialize each %gnutls_ocsp_resp_t + * structure independently, and use gnutls_free() at @ocsps. + * + * In PEM files, when no OCSP responses are detected + * %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE will be returned. + * + * Returns: the number of responses read or a negative error value. + * + * Since: 3.6.3 + **/ +int +gnutls_ocsp_resp_list_import2(gnutls_ocsp_resp_t **ocsps, + unsigned int *size, + const gnutls_datum_t *resp_data, + gnutls_x509_crt_fmt_t format, + unsigned int flags) +{ + gnutls_ocsp_resp_t resp = NULL; + gnutls_ocsp_resp_t *new_ocsps; + int ret; + unsigned i; + + + if (format == GNUTLS_X509_FMT_PEM) { + /* load multiple responses */ + gnutls_datum_t p = {resp_data->data, resp_data->size}; + + *size = 0; + *ocsps = NULL; + + p.data = memmem(p.data, p.size, PEM_OCSP_RESPONSE, + sizeof(PEM_OCSP_RESPONSE)-1); + if (p.data == NULL) { + ret = gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE); + goto cleanup; + } + + p.size -= p.data - resp_data->data; + if (p.size <= 0) { + ret = gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE); + goto cleanup; + } + + do { + ret = gnutls_ocsp_resp_init(&resp); + if (ret < 0) { + gnutls_assert(); + goto fail; + } + + ret = gnutls_ocsp_resp_import2(resp, &p, GNUTLS_X509_FMT_PEM); + if (ret < 0) { + gnutls_assert(); + goto fail; + } + + if (unlikely(INT_ADD_OVERFLOW(*size, 1))) { + ret = gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + goto fail; + } + + new_ocsps = _gnutls_reallocarray(*ocsps, + *size + 1, + sizeof(gnutls_ocsp_resp_t)); + if (new_ocsps == NULL) { + resp = NULL; + gnutls_assert(); + goto fail; + } + + new_ocsps[*size] = resp; + resp = NULL; + (*size)++; + *ocsps = new_ocsps; + + p.data++; + p.size--; + + p.data = memmem(p.data, p.size, PEM_OCSP_RESPONSE, + sizeof(PEM_OCSP_RESPONSE)-1); + if (p.data == NULL) + break; + p.size = resp_data->size - (p.data - resp_data->data); + } while(p.size > 0); + } else { + /* DER: load a single response */ + ret = gnutls_ocsp_resp_init(&resp); + if (ret < 0) { + return gnutls_assert_val(ret); + } + + ret = gnutls_ocsp_resp_import2(resp, resp_data, GNUTLS_X509_FMT_DER); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + *ocsps = gnutls_malloc(sizeof(gnutls_ocsp_resp_t)); + if (*ocsps == NULL) { + gnutls_assert(); + ret = GNUTLS_E_MEMORY_ERROR; + goto cleanup; + } + + (*ocsps)[0] = resp; + resp = NULL; + *size = 1; + } + + ret = 0; + goto cleanup; + + fail: + for (i=0;i<*size;i++) { + gnutls_ocsp_resp_deinit((*ocsps)[i]); + } + gnutls_free(*ocsps); + + cleanup: + if (resp) + gnutls_ocsp_resp_deinit(resp); + return ret; +} + +/* This returns -1 if the OCSP response is invalid (revoked) or its + * data are too old. It returns -2 if it cannot determine the expiration + * time, and would otherwise treat it as too old. + * Otherwise it returns the time after which that data is invalid. + */ +time_t _gnutls_ocsp_get_validity(gnutls_ocsp_resp_const_t resp) +{ + unsigned int cert_status; + time_t rtime, vtime, ntime, now; + int ret; + + ret = gnutls_ocsp_resp_get_single(resp, 0, NULL, NULL, NULL, NULL, + &cert_status, &vtime, &ntime, + &rtime, NULL); + if (ret < 0) { + _gnutls_debug_log("There was an error parsing the OCSP response: %s\n", + gnutls_strerror(ret)); + return gnutls_assert_val(-1); + } + + if (cert_status != GNUTLS_OCSP_CERT_GOOD && + cert_status != GNUTLS_OCSP_CERT_UNKNOWN) { + _gnutls_debug_log("The OCSP response status (%d) is invalid\n", + cert_status); + return gnutls_assert_val(-1); + } + + now = gnutls_time(0); + + if (ntime == -1) { + /* This is a problematic case, and there is no consensus on how + * to treat these responses. It doesn't contain the time after which + * the response is invalid, thus it is an OCSP response effectively + * valid forever defeating the purpose of OCSP. We set here the same + * limit we apply when verifying responses. */ + if (now - vtime > MAX_OCSP_VALIDITY_SECS) { + _gnutls_debug_log("The OCSP response is old\n"); + return gnutls_assert_val(-2); + } + + return now + MAX_OCSP_VALIDITY_SECS; + } else { + /* there is a newer OCSP answer, don't trust this one */ + if (ntime < now) { + _gnutls_debug_log("There is a newer OCSP response\n"); + return gnutls_assert_val(-1); + } + + return ntime; + } +} + +const char *_gnutls_ocsp_verify_status_to_str(gnutls_ocsp_verify_reason_t r, char out[MAX_OCSP_MSG_SIZE]) +{ + gnutls_buffer_st str; + gnutls_datum_t buf; + int ret; + + _gnutls_buffer_init(&str); + + if (r == 0) + _gnutls_buffer_append_str(&str, + _ + ("The OCSP response is trusted. ")); + + if (r & GNUTLS_OCSP_VERIFY_SIGNER_NOT_FOUND) + _gnutls_buffer_append_str(&str, + _ + ("The OCSP response's signer could not be found. ")); + + if (r & GNUTLS_OCSP_VERIFY_SIGNER_KEYUSAGE_ERROR) + _gnutls_buffer_append_str(&str, + _ + ("Error in the signer's key usageflags. ")); + + if (r & GNUTLS_OCSP_VERIFY_UNTRUSTED_SIGNER) + _gnutls_buffer_append_str(&str, + _ + ("The OCSP response's signer is not trusted. ")); + + if (r & GNUTLS_OCSP_VERIFY_INSECURE_ALGORITHM) + _gnutls_buffer_append_str(&str, + _ + ("The OCSP response depends on insecure algorithms. ")); + + if (r & GNUTLS_OCSP_VERIFY_SIGNATURE_FAILURE) + _gnutls_buffer_append_str(&str, + _ + ("The OCSP response's signature cannot be validated. ")); + + if (r & GNUTLS_OCSP_VERIFY_CERT_NOT_ACTIVATED) + _gnutls_buffer_append_str(&str, + _ + ("The OCSP response's signer's certificate is not activated. ")); + + if (r & GNUTLS_OCSP_VERIFY_CERT_EXPIRED) + _gnutls_buffer_append_str(&str, + _ + ("The OCSP response's signer's certificate is expired. ")); + + ret = _gnutls_buffer_to_datum(&str, &buf, 1); + if (ret < 0) + return _("Memory error"); + + snprintf(out, MAX_OCSP_MSG_SIZE, "%s", buf.data); + gnutls_free(buf.data); + + return out; +} diff --git a/lib/x509/ocsp.h b/lib/x509/ocsp.h new file mode 100644 index 0000000..b9c4757 --- /dev/null +++ b/lib/x509/ocsp.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2017 Red Hat, Inc. + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GnuTLS. + * + * The GnuTLS is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/> + * + */ + +#ifndef GNUTLS_LIB_X509_OCSP_H +#define GNUTLS_LIB_X509_OCSP_H + +/* Online Certificate Status Protocol - RFC 2560 + */ +#include <gnutls/ocsp.h> + +/* fifteen days */ +#define MAX_OCSP_VALIDITY_SECS (15*60*60*24) + +time_t _gnutls_ocsp_get_validity(gnutls_ocsp_resp_const_t resp); +#define MAX_OCSP_MSG_SIZE 128 +const char *_gnutls_ocsp_verify_status_to_str(gnutls_ocsp_verify_reason_t r, char out[MAX_OCSP_MSG_SIZE]); + +#endif /* GNUTLS_LIB_X509_OCSP_H */ diff --git a/lib/x509/ocsp_output.c b/lib/x509/ocsp_output.c new file mode 100644 index 0000000..8ead90c --- /dev/null +++ b/lib/x509/ocsp_output.c @@ -0,0 +1,658 @@ +/* + * Copyright (C) 2011-2012 Free Software Foundation, Inc. + * Author: Simon Josefsson + * + * This file is part of GnuTLS. + * + * The GnuTLS is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/> + * + */ + +/* Online Certificate Status Protocol - RFC 2560 + */ + +#include "gnutls_int.h" +#include <global.h> +#include "errors.h" +#include <libtasn1.h> +#include <pk.h> +#include <str.h> +#include "algorithms.h" + +#include <gnutls/ocsp.h> + +#define addf _gnutls_buffer_append_printf +#define adds _gnutls_buffer_append_str + +static void print_req(gnutls_buffer_st * str, gnutls_ocsp_req_const_t req) +{ + int ret; + unsigned indx; + + /* Version. */ + { + int version = gnutls_ocsp_req_get_version(req); + if (version < 0) + addf(str, "error: get_version: %s\n", + gnutls_strerror(version)); + else + addf(str, _("\tVersion: %d\n"), version); + } + + /* XXX requestorName */ + + /* requestList */ + addf(str, "\tRequest List:\n"); + for (indx = 0;; indx++) { + gnutls_digest_algorithm_t digest; + gnutls_datum_t in, ik, sn; + + ret = + gnutls_ocsp_req_get_cert_id(req, indx, &digest, &in, + &ik, &sn); + if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) + break; + addf(str, "\t\tCertificate ID:\n"); + if (ret != GNUTLS_E_SUCCESS) { + addf(str, "error: get_cert_id: %s\n", + gnutls_strerror(ret)); + continue; + } + addf(str, "\t\t\tHash Algorithm: %s\n", + _gnutls_digest_get_name(hash_to_entry(digest))); + + adds(str, "\t\t\tIssuer Name Hash: "); + _gnutls_buffer_hexprint(str, in.data, in.size); + adds(str, "\n"); + + adds(str, "\t\t\tIssuer Key Hash: "); + _gnutls_buffer_hexprint(str, ik.data, ik.size); + adds(str, "\n"); + + adds(str, "\t\t\tSerial Number: "); + _gnutls_buffer_hexprint(str, sn.data, sn.size); + adds(str, "\n"); + + gnutls_free(in.data); + gnutls_free(ik.data); + gnutls_free(sn.data); + + /* XXX singleRequestExtensions */ + } + + for (indx = 0;; indx++) { + gnutls_datum_t oid; + unsigned int critical; + gnutls_datum_t data; + + ret = + gnutls_ocsp_req_get_extension(req, indx, &oid, + &critical, &data); + if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) + break; + else if (ret != GNUTLS_E_SUCCESS) { + addf(str, "error: get_extension: %s\n", + gnutls_strerror(ret)); + continue; + } + if (indx == 0) + adds(str, "\tExtensions:\n"); + + if (oid.size == sizeof(GNUTLS_OCSP_NONCE) - 1 && + memcmp(oid.data, GNUTLS_OCSP_NONCE, oid.size) == 0) { + gnutls_datum_t nonce; + unsigned int ncrit; + + ret = + gnutls_ocsp_req_get_nonce(req, &ncrit, + &nonce); + if (ret != GNUTLS_E_SUCCESS) { + addf(str, "error: get_nonce: %s\n", + gnutls_strerror(ret)); + } else { + addf(str, "\t\tNonce%s: ", + ncrit ? " (critical)" : ""); + _gnutls_buffer_hexprint(str, nonce.data, + nonce.size); + adds(str, "\n"); + gnutls_free(nonce.data); + } + } else { + addf(str, "\t\tUnknown extension %s (%s):\n", + oid.data, + critical ? "critical" : "not critical"); + + adds(str, _("\t\t\tASCII: ")); + _gnutls_buffer_asciiprint(str, (char *) data.data, + data.size); + addf(str, "\n"); + + adds(str, _("\t\t\tHexdump: ")); + _gnutls_buffer_hexprint(str, (char *) data.data, + data.size); + adds(str, "\n"); + } + + gnutls_free(oid.data); + gnutls_free(data.data); + } + + /* XXX Signature */ +} + +/** + * gnutls_ocsp_req_print: + * @req: The data to be printed + * @format: Indicate the format to use + * @out: Newly allocated datum with (0) terminated string. + * + * This function will pretty print a OCSP request, suitable for + * display to a human. + * + * If the format is %GNUTLS_OCSP_PRINT_FULL then all fields of the + * request will be output, on multiple lines. + * + * The output @out->data needs to be deallocate using gnutls_free(). + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int +gnutls_ocsp_req_print(gnutls_ocsp_req_const_t req, + gnutls_ocsp_print_formats_t format, + gnutls_datum_t * out) +{ + gnutls_buffer_st str; + int rc; + + if (format != GNUTLS_OCSP_PRINT_FULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + _gnutls_buffer_init(&str); + + _gnutls_buffer_append_str(&str, _("OCSP Request Information:\n")); + + print_req(&str, req); + + rc = _gnutls_buffer_to_datum(&str, out, 1); + if (rc != GNUTLS_E_SUCCESS) { + gnutls_assert(); + return rc; + } + + return GNUTLS_E_SUCCESS; +} + +static void +print_resp(gnutls_buffer_st * str, gnutls_ocsp_resp_const_t resp, + gnutls_ocsp_print_formats_t format) +{ + int ret; + unsigned indx; + + ret = gnutls_ocsp_resp_get_status(resp); + if (ret < 0) { + addf(str, "error: ocsp_resp_get_status: %s\n", + gnutls_strerror(ret)); + return; + } + + adds(str, "\tResponse Status: "); + switch (ret) { + case GNUTLS_OCSP_RESP_SUCCESSFUL: + adds(str, "Successful\n"); + break; + + case GNUTLS_OCSP_RESP_MALFORMEDREQUEST: + adds(str, "malformedRequest\n"); + return; + + case GNUTLS_OCSP_RESP_INTERNALERROR: + adds(str, "internalError\n"); + return; + + case GNUTLS_OCSP_RESP_TRYLATER: + adds(str, "tryLater\n"); + return; + + case GNUTLS_OCSP_RESP_SIGREQUIRED: + adds(str, "sigRequired\n"); + return; + + case GNUTLS_OCSP_RESP_UNAUTHORIZED: + adds(str, "unauthorized\n"); + return; + + default: + adds(str, "unknown\n"); + return; + } + + { + gnutls_datum_t oid; + + ret = gnutls_ocsp_resp_get_response(resp, &oid, NULL); + if (ret < 0) { + addf(str, "error: get_response: %s\n", + gnutls_strerror(ret)); + return; + } + + adds(str, "\tResponse Type: "); +#define OCSP_BASIC "1.3.6.1.5.5.7.48.1.1" + + if (oid.size == sizeof(OCSP_BASIC) - 1 + && memcmp(oid.data, OCSP_BASIC, oid.size) == 0) { + adds(str, "Basic OCSP Response\n"); + gnutls_free(oid.data); + } else { + addf(str, "Unknown response type (%.*s)\n", + oid.size, oid.data); + gnutls_free(oid.data); + return; + } + } + + /* Version. */ + { + int version = gnutls_ocsp_resp_get_version(resp); + if (version < 0) + addf(str, "error: get_version: %s\n", + gnutls_strerror(version)); + else + addf(str, _("\tVersion: %d\n"), version); + } + + /* responderID */ + { + gnutls_datum_t dn = {NULL, 0}; + + ret = gnutls_ocsp_resp_get_responder2(resp, &dn, 0); + if (ret < 0) { + if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { + ret = gnutls_ocsp_resp_get_responder_raw_id(resp, GNUTLS_OCSP_RESP_ID_KEY, &dn); + + if (ret >= 0) { + addf(str, _("\tResponder Key ID: ")); + _gnutls_buffer_hexprint(str, dn.data, dn.size); + adds(str, "\n"); + } + gnutls_free(dn.data); + } else { + addf(str, "error: get_responder2: %s\n", + gnutls_strerror(ret)); + } + } else { + addf(str, _("\tResponder ID: %s\n"), dn.data); + gnutls_free(dn.data); + } + } + + { + char s[42]; + size_t max = sizeof(s); + struct tm t; + time_t tim = gnutls_ocsp_resp_get_produced(resp); + + if (tim == (time_t) - 1) + addf(str, "error: ocsp_resp_get_produced\n"); + else if (gmtime_r(&tim, &t) == NULL) + addf(str, "error: gmtime_r (%ld)\n", + (unsigned long) tim); + else if (strftime(s, max, "%a %b %d %H:%M:%S UTC %Y", &t) + == 0) + addf(str, "error: strftime (%ld)\n", + (unsigned long) tim); + else + addf(str, _("\tProduced At: %s\n"), s); + } + + addf(str, "\tResponses:\n"); + for (indx = 0;; indx++) { + gnutls_digest_algorithm_t digest; + gnutls_datum_t in, ik, sn; + unsigned int cert_status; + time_t this_update; + time_t next_update; + time_t revocation_time; + unsigned int revocation_reason; + + ret = gnutls_ocsp_resp_get_single(resp, + indx, + &digest, &in, &ik, &sn, + &cert_status, + &this_update, + &next_update, + &revocation_time, + &revocation_reason); + if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) + break; + addf(str, "\t\tCertificate ID:\n"); + if (ret != GNUTLS_E_SUCCESS) { + addf(str, "error: get_singleresponse: %s\n", + gnutls_strerror(ret)); + continue; + } + addf(str, "\t\t\tHash Algorithm: %s\n", + _gnutls_digest_get_name(hash_to_entry(digest))); + + adds(str, "\t\t\tIssuer Name Hash: "); + _gnutls_buffer_hexprint(str, in.data, in.size); + adds(str, "\n"); + + adds(str, "\t\t\tIssuer Key Hash: "); + _gnutls_buffer_hexprint(str, ik.data, ik.size); + adds(str, "\n"); + + adds(str, "\t\t\tSerial Number: "); + _gnutls_buffer_hexprint(str, sn.data, sn.size); + adds(str, "\n"); + + gnutls_free(in.data); + gnutls_free(ik.data); + gnutls_free(sn.data); + + { + const char *p = NULL; + + switch (cert_status) { + case GNUTLS_OCSP_CERT_GOOD: + p = "good"; + break; + + case GNUTLS_OCSP_CERT_REVOKED: + p = "revoked"; + break; + + case GNUTLS_OCSP_CERT_UNKNOWN: + p = "unknown"; + break; + + default: + addf(str, + "\t\tCertificate Status: unexpected value %d\n", + cert_status); + break; + } + + if (p) + addf(str, "\t\tCertificate Status: %s\n", + p); + } + + /* XXX revocation reason */ + + if (cert_status == GNUTLS_OCSP_CERT_REVOKED) { + char s[42]; + size_t max = sizeof(s); + struct tm t; + + if (revocation_time == (time_t) - 1) + addf(str, "error: revocation_time\n"); + else if (gmtime_r(&revocation_time, &t) == NULL) + addf(str, "error: gmtime_r (%ld)\n", + (unsigned long) revocation_time); + else if (strftime + (s, max, "%a %b %d %H:%M:%S UTC %Y", + &t) == 0) + addf(str, "error: strftime (%ld)\n", + (unsigned long) revocation_time); + else + addf(str, _("\t\tRevocation time: %s\n"), + s); + } + + { + char s[42]; + size_t max = sizeof(s); + struct tm t; + + if (this_update == (time_t) - 1) + addf(str, "error: this_update\n"); + else if (gmtime_r(&this_update, &t) == NULL) + addf(str, "error: gmtime_r (%ld)\n", + (unsigned long) this_update); + else if (strftime + (s, max, "%a %b %d %H:%M:%S UTC %Y", + &t) == 0) + addf(str, "error: strftime (%ld)\n", + (unsigned long) this_update); + else + addf(str, _("\t\tThis Update: %s\n"), s); + } + + { + char s[42]; + size_t max = sizeof(s); + struct tm t; + + if (next_update != (time_t) - 1) { + if (gmtime_r(&next_update, &t) == NULL) + addf(str, "error: gmtime_r (%ld)\n", + (unsigned long) next_update); + else if (strftime + (s, max, "%a %b %d %H:%M:%S UTC %Y", + &t) == 0) + addf(str, "error: strftime (%ld)\n", + (unsigned long) next_update); + else + addf(str, _("\t\tNext Update: %s\n"), s); + } + } + + /* XXX singleRequestExtensions */ + } + + adds(str, "\tExtensions:\n"); + for (indx = 0;; indx++) { + gnutls_datum_t oid; + unsigned int critical; + gnutls_datum_t data; + + ret = + gnutls_ocsp_resp_get_extension(resp, indx, &oid, + &critical, &data); + if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) + break; + else if (ret != GNUTLS_E_SUCCESS) { + addf(str, "error: get_extension: %s\n", + gnutls_strerror(ret)); + continue; + } + + if (oid.size == sizeof(GNUTLS_OCSP_NONCE) - 1 && + memcmp(oid.data, GNUTLS_OCSP_NONCE, oid.size) == 0) { + gnutls_datum_t nonce; + unsigned int ncrit; + + ret = + gnutls_ocsp_resp_get_nonce(resp, &ncrit, + &nonce); + if (ret != GNUTLS_E_SUCCESS) { + addf(str, "error: get_nonce: %s\n", + gnutls_strerror(ret)); + } else { + addf(str, "\t\tNonce%s: ", + ncrit ? " (critical)" : ""); + _gnutls_buffer_hexprint(str, nonce.data, + nonce.size); + adds(str, "\n"); + gnutls_free(nonce.data); + } + } else { + addf(str, "\t\tUnknown extension %s (%s):\n", + oid.data, + critical ? "critical" : "not critical"); + + adds(str, _("\t\t\tASCII: ")); + _gnutls_buffer_asciiprint(str, (char *) data.data, + data.size); + addf(str, "\n"); + + adds(str, _("\t\t\tHexdump: ")); + _gnutls_buffer_hexprint(str, (char *) data.data, + data.size); + adds(str, "\n"); + } + + gnutls_free(oid.data); + gnutls_free(data.data); + + } + + ret = gnutls_ocsp_resp_get_signature_algorithm(resp); + if (ret < 0) + addf(str, "error: get_signature_algorithm: %s\n", + gnutls_strerror(ret)); + else { + const char *name = + gnutls_sign_algorithm_get_name(ret); + if (name == NULL) + name = _("unknown"); + addf(str, _("\tSignature Algorithm: %s\n"), name); + } + if (ret != GNUTLS_SIGN_UNKNOWN && gnutls_sign_is_secure(ret) == 0) { + adds(str, + _("warning: signed using a broken signature " + "algorithm that can be forged.\n")); + } + + if (format == GNUTLS_OCSP_PRINT_FULL) { + gnutls_datum_t sig; + gnutls_x509_crt_t *certs; + size_t ncerts, i; + gnutls_datum_t out; + + /* Signature. */ + ret = gnutls_ocsp_resp_get_signature(resp, &sig); + if (ret < 0) + addf(str, "error: get_signature: %s\n", + gnutls_strerror(ret)); + else { + adds(str, _("\tSignature:\n")); + _gnutls_buffer_hexdump(str, sig.data, sig.size, + "\t\t"); + + gnutls_free(sig.data); + } + + /* certs */ + ret = gnutls_ocsp_resp_get_certs(resp, &certs, &ncerts); + if (ret < 0) + addf(str, "error: get_certs: %s\n", + gnutls_strerror(ret)); + else { + if (ncerts > 0) + addf(str, "\tAdditional certificates:\n"); + + for (i = 0; i < ncerts; i++) { + size_t s = 0; + + ret = + gnutls_x509_crt_print(certs[i], + GNUTLS_CRT_PRINT_FULL, + &out); + if (ret < 0) + addf(str, "error: crt_print: %s\n", + gnutls_strerror(ret)); + else { + addf(str, "%.*s", out.size, + out.data); + gnutls_free(out.data); + } + + ret = + gnutls_x509_crt_export(certs[i], + GNUTLS_X509_FMT_PEM, + NULL, &s); + if (ret != GNUTLS_E_SHORT_MEMORY_BUFFER) + addf(str, + "error: crt_export: %s\n", + gnutls_strerror(ret)); + else { + out.data = gnutls_malloc(s); + if (out.data == NULL) + addf(str, + "error: malloc: %s\n", + gnutls_strerror + (GNUTLS_E_MEMORY_ERROR)); + else { + ret = + gnutls_x509_crt_export + (certs[i], + GNUTLS_X509_FMT_PEM, + out.data, &s); + if (ret < 0) + addf(str, + "error: crt_export: %s\n", + gnutls_strerror + (ret)); + else { + out.size = s; + addf(str, "%.*s", + out.size, + out.data); + } + gnutls_free(out.data); + } + } + + gnutls_x509_crt_deinit(certs[i]); + } + gnutls_free(certs); + } + } +} + +/** + * gnutls_ocsp_resp_print: + * @resp: The data to be printed + * @format: Indicate the format to use + * @out: Newly allocated datum with (0) terminated string. + * + * This function will pretty print a OCSP response, suitable for + * display to a human. + * + * If the format is %GNUTLS_OCSP_PRINT_FULL then all fields of the + * response will be output, on multiple lines. + * + * The output @out->data needs to be deallocate using gnutls_free(). + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int +gnutls_ocsp_resp_print(gnutls_ocsp_resp_const_t resp, + gnutls_ocsp_print_formats_t format, + gnutls_datum_t * out) +{ + gnutls_buffer_st str; + int rc; + + _gnutls_buffer_init(&str); + + _gnutls_buffer_append_str(&str, _("OCSP Response Information:\n")); + + print_resp(&str, resp, format); + + rc = _gnutls_buffer_to_datum(&str, out, 1); + if (rc != GNUTLS_E_SUCCESS) { + gnutls_assert(); + return rc; + } + + return GNUTLS_E_SUCCESS; +} diff --git a/lib/x509/output.c b/lib/x509/output.c new file mode 100644 index 0000000..2ec288c --- /dev/null +++ b/lib/x509/output.c @@ -0,0 +1,3083 @@ +/* + * Copyright (C) 2007-2016 Free Software Foundation, Inc. + * Copyright (C) 2015-2017 Red Hat, Inc. + * + * Author: Simon Josefsson, Nikos Mavrogiannopoulos + * + * This file is part of GnuTLS. + * + * The GnuTLS is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/> + * + */ + +/* Functions for printing X.509 Certificate structures + */ + +#include "gnutls_int.h" +#include <common.h> +#include <x509.h> +#include <x509_int.h> +#include <num.h> +#include "errors.h" +#include "hello_ext.h" +#include "ip.h" + +#define addf _gnutls_buffer_append_printf +#define adds _gnutls_buffer_append_str + +#define NON_NULL(x) (((x)!=NULL)?((char*)(x)):"") +#define ERROR_STR (char*) "(error)" + +static void print_idn_name(gnutls_buffer_st *str, const char *prefix, const char *type, gnutls_datum_t *name) +{ + unsigned printable = 1; + unsigned is_printed = 0; + gnutls_datum_t out = {NULL, 0}; + int ret; + + if (!_gnutls_str_is_print((char*)name->data, name->size)) + printable = 0; + + is_printed = 0; + if (!printable) { + addf(str, _("%s%s: %.*s (contains illegal chars)\n"), prefix, type, name->size, NON_NULL(name->data)); + is_printed = 1; + } else if (name->data != NULL) { + if (strstr((char*)name->data, "xn--") != NULL) { + ret = gnutls_idna_reverse_map((char*)name->data, name->size, &out, 0); + if (ret >= 0) { + addf(str, _("%s%s: %.*s (%s)\n"), prefix, type, name->size, NON_NULL(name->data), out.data); + is_printed = 1; + gnutls_free(out.data); + } + } + } + + if (is_printed == 0) { + addf(str, _("%s%s: %.*s\n"), prefix, type, name->size, NON_NULL(name->data)); + } +} + +static void print_idn_email(gnutls_buffer_st *str, const char *prefix, const char *type, gnutls_datum_t *name) +{ + unsigned printable = 1; + unsigned is_printed = 0; + gnutls_datum_t out = {NULL, 0}; + int ret; + + if (!_gnutls_str_is_print((char*)name->data, name->size)) + printable = 0; + + is_printed = 0; + if (!printable) { + addf(str, _("%s%s: %.*s (contains illegal chars)\n"), prefix, type, name->size, NON_NULL(name->data)); + is_printed = 1; + } else if (name->data != NULL) { + if (strstr((char*)name->data, "xn--") != NULL) { + ret = _gnutls_idna_email_reverse_map((char*)name->data, name->size, &out); + if (ret >= 0) { + addf(str, _("%s%s: %.*s (%s)\n"), prefix, type, name->size, NON_NULL(name->data), out.data); + is_printed = 1; + gnutls_free(out.data); + } + } + } + + if (is_printed == 0) { + addf(str, _("%s%s: %.*s\n"), prefix, type, name->size, NON_NULL(name->data)); + } +} + +static void +print_name(gnutls_buffer_st *str, const char *prefix, unsigned type, gnutls_datum_t *name, unsigned ip_is_cidr) +{ + char *sname = (char*)name->data; + char str_ip[64]; + const char *p; + + if ((type == GNUTLS_SAN_DNSNAME || type == GNUTLS_SAN_OTHERNAME_XMPP + || type == GNUTLS_SAN_OTHERNAME_KRB5PRINCIPAL + || type == GNUTLS_SAN_OTHERNAME_MSUSERPRINCIPAL + || type == GNUTLS_SAN_RFC822NAME + || type == GNUTLS_SAN_URI) && sname != NULL && strlen(sname) != name->size) { + adds(str, + _("warning: SAN contains an embedded NUL, " + "replacing with '!'\n")); + while (strlen(sname) < name->size) + name->data[strlen(sname)] = '!'; + } + + switch (type) { + case GNUTLS_SAN_DNSNAME: + print_idn_name(str, prefix, "DNSname", name); + break; + + case GNUTLS_SAN_RFC822NAME: + print_idn_email(str, prefix, "RFC822Name", name); + break; + + case GNUTLS_SAN_URI: + addf(str, _("%sURI: %.*s\n"), prefix, name->size, NON_NULL(name->data)); + break; + + case GNUTLS_SAN_IPADDRESS: + if (!ip_is_cidr) + p = _gnutls_ip_to_string(name->data, name->size, str_ip, sizeof(str_ip)); + else + p = _gnutls_cidr_to_string(name->data, name->size, str_ip, sizeof(str_ip)); + if (p == NULL) + p = ERROR_STR; + addf(str, "%sIPAddress: %s\n", prefix, p); + break; + + case GNUTLS_SAN_DN: + addf(str, _("%sdirectoryName: %.*s\n"), prefix, name->size, NON_NULL(name->data)); + break; + + case GNUTLS_SAN_REGISTERED_ID: + addf(str, _("%sRegistered ID: %.*s\n"), prefix, name->size, NON_NULL(name->data)); + break; + + case GNUTLS_SAN_OTHERNAME_XMPP: + addf(str, _("%sXMPP Address: %.*s\n"), prefix, name->size, NON_NULL(name->data)); + break; + + case GNUTLS_SAN_OTHERNAME_KRB5PRINCIPAL: + addf(str, _("%sKRB5Principal: %.*s\n"), prefix, name->size, NON_NULL(name->data)); + break; + + case GNUTLS_SAN_OTHERNAME_MSUSERPRINCIPAL: + addf(str, _("%sUser Principal Name: %.*s\n"), prefix, name->size, NON_NULL(name->data)); + break; + + default: + addf(str, _("%sUnknown name: "), prefix); + _gnutls_buffer_hexprint(str, name->data, name->size); + adds(str, "\n"); + break; + } +} + +static char *get_pk_name(gnutls_x509_crt_t cert, unsigned *bits) +{ + char oid[MAX_OID_SIZE]; + size_t oid_size; + oid_size = sizeof(oid); + int ret; + + ret = gnutls_x509_crt_get_pk_algorithm(cert, bits); + if (ret > 0) { + const char *name = gnutls_pk_algorithm_get_name(ret); + + if (name != NULL) + return gnutls_strdup(name); + } + + ret = gnutls_x509_crt_get_pk_oid(cert, oid, &oid_size); + if (ret < 0) + return NULL; + + return gnutls_strdup(oid); +} + +static char *crq_get_pk_name(gnutls_x509_crq_t crq) +{ + char oid[MAX_OID_SIZE]; + size_t oid_size; + oid_size = sizeof(oid); + int ret; + + ret = gnutls_x509_crq_get_pk_algorithm(crq, NULL); + if (ret > 0) { + const char *name = gnutls_pk_algorithm_get_name(ret); + + if (name != NULL) + return gnutls_strdup(name); + } + + ret = gnutls_x509_crq_get_pk_oid(crq, oid, &oid_size); + if (ret < 0) + return NULL; + + return gnutls_strdup(oid); +} + +static char *get_sign_name(gnutls_x509_crt_t cert, int *algo) +{ + char oid[MAX_OID_SIZE]; + size_t oid_size; + oid_size = sizeof(oid); + int ret; + + *algo = 0; + + ret = gnutls_x509_crt_get_signature_algorithm(cert); + if (ret > 0) { + const char *name = gnutls_sign_get_name(ret); + + *algo = ret; + + if (name != NULL) + return gnutls_strdup(name); + } + + ret = gnutls_x509_crt_get_signature_oid(cert, oid, &oid_size); + if (ret < 0) + return NULL; + + return gnutls_strdup(oid); +} + +static char *crq_get_sign_name(gnutls_x509_crq_t crq) +{ + char oid[MAX_OID_SIZE]; + size_t oid_size; + oid_size = sizeof(oid); + int ret; + + ret = gnutls_x509_crq_get_signature_algorithm(crq); + if (ret > 0) { + const char *name = gnutls_sign_get_name(ret); + + if (name != NULL) + return gnutls_strdup(name); + } + + ret = gnutls_x509_crq_get_signature_oid(crq, oid, &oid_size); + if (ret < 0) + return NULL; + + return gnutls_strdup(oid); +} + +static char *crl_get_sign_name(gnutls_x509_crl_t crl, int *algo) +{ + char oid[MAX_OID_SIZE]; + size_t oid_size; + oid_size = sizeof(oid); + int ret; + + *algo = 0; + + ret = gnutls_x509_crl_get_signature_algorithm(crl); + if (ret > 0) { + const char *name = gnutls_sign_get_name(ret); + + *algo = ret; + + if (name != NULL) + return gnutls_strdup(name); + } + + ret = gnutls_x509_crl_get_signature_oid(crl, oid, &oid_size); + if (ret < 0) + return NULL; + + return gnutls_strdup(oid); +} + + +static void print_proxy(gnutls_buffer_st * str, gnutls_datum_t *der) +{ + int pathlen; + char *policyLanguage; + char *policy; + size_t npolicy; + int err; + + err = gnutls_x509_ext_import_proxy(der, &pathlen, &policyLanguage, + &policy, &npolicy); + if (err < 0) { + addf(str, "error: get_proxy: %s\n", gnutls_strerror(err)); + return; + } + + if (pathlen >= 0) + addf(str, _("\t\t\tPath Length Constraint: %d\n"), + pathlen); + addf(str, _("\t\t\tPolicy Language: %s"), policyLanguage); + if (strcmp(policyLanguage, "1.3.6.1.5.5.7.21.1") == 0) + adds(str, " (id-ppl-inheritALL)\n"); + else if (strcmp(policyLanguage, "1.3.6.1.5.5.7.21.2") == 0) + adds(str, " (id-ppl-independent)\n"); + else + adds(str, "\n"); + if (npolicy) { + adds(str, _("\t\t\tPolicy:\n\t\t\t\tASCII: ")); + _gnutls_buffer_asciiprint(str, policy, npolicy); + adds(str, _("\n\t\t\t\tHexdump: ")); + _gnutls_buffer_hexprint(str, policy, npolicy); + adds(str, "\n"); + } + gnutls_free(policy); + gnutls_free(policyLanguage); +} + + +static void print_nc(gnutls_buffer_st * str, const char* prefix, gnutls_datum_t *der) +{ + gnutls_x509_name_constraints_t nc; + int ret; + unsigned idx = 0; + gnutls_datum_t name; + unsigned type; + char new_prefix[16]; + + ret = gnutls_x509_name_constraints_init(&nc); + if (ret < 0) + return; + + ret = gnutls_x509_ext_import_name_constraints(der, nc, 0); + if (ret < 0) + goto cleanup; + + snprintf(new_prefix, sizeof(new_prefix), "%s\t\t\t\t", prefix); + + do { + ret = gnutls_x509_name_constraints_get_permitted(nc, idx++, &type, &name); + + if (ret >= 0) { + if (idx == 1) + addf(str, _("%s\t\t\tPermitted:\n"), prefix); + + print_name(str, new_prefix, type, &name, 1); + } + } while (ret == 0); + + idx = 0; + do { + ret = gnutls_x509_name_constraints_get_excluded(nc, idx++, &type, &name); + + if (ret >= 0) { + if (idx == 1) + addf(str, _("%s\t\t\tExcluded:\n"), prefix); + + print_name(str, new_prefix, type, &name, 1); + } + } while (ret == 0); + +cleanup: + gnutls_x509_name_constraints_deinit(nc); +} + +static void print_aia(gnutls_buffer_st * str, const gnutls_datum_t *der) +{ + int err; + int seq; + gnutls_datum_t san = { NULL, 0 }, oid = {NULL, 0}; + gnutls_x509_aia_t aia; + unsigned int san_type; + + err = gnutls_x509_aia_init(&aia); + if (err < 0) + return; + + err = gnutls_x509_ext_import_aia(der, aia, 0); + if (err < 0) { + addf(str, "error: get_aia: %s\n", + gnutls_strerror(err)); + goto cleanup; + } + + for (seq=0;;seq++) { + err = gnutls_x509_aia_get(aia, seq, &oid, &san_type, &san); + if (err == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) + goto cleanup; + if (err < 0) { + addf(str, "error: aia_get: %s\n", + gnutls_strerror(err)); + goto cleanup; + } + + if (strcmp((char*)oid.data, GNUTLS_OID_AD_OCSP) == 0) + addf(str, _("\t\t\tAccess Method: %s (%s)\n"), GNUTLS_OID_AD_OCSP, "id-ad-ocsp"); + else if (strcmp((char*)oid.data, GNUTLS_OID_AD_CAISSUERS) == 0) + addf(str, _("\t\t\tAccess Method: %s (%s)\n"), GNUTLS_OID_AD_CAISSUERS, "id-ad-caIssuers"); + else { + addf(str, _("\t\t\tAccess Method: %s (%s)\n"), (char*)oid.data, "UNKNOWN"); + } + + adds(str, "\t\t\tAccess Location "); + print_name(str, "", san_type, &san, 0); + } + +cleanup: + gnutls_x509_aia_deinit(aia); +} + +static void print_ski(gnutls_buffer_st * str, gnutls_datum_t *der) +{ + gnutls_datum_t id = {NULL, 0}; + int err; + + err = gnutls_x509_ext_import_subject_key_id(der, &id); + if (err < 0) { + addf(str, "error: get_subject_key_id: %s\n", + gnutls_strerror(err)); + return; + } + + adds(str, "\t\t\t"); + _gnutls_buffer_hexprint(str, id.data, id.size); + adds(str, "\n"); + + gnutls_free(id.data); +} + +static void print_time(gnutls_buffer_st *str, time_t timestamp) +{ + char s[42]; + size_t max = sizeof(s); + struct tm t; + + if (gmtime_r(×tamp, &t) == NULL) { + addf(str, "error: gmtime_r (%lu)\n", timestamp); + return; + } + + if (strftime(s, max, "%a, %b %d %H:%M:%S UTC %Y", &t) == 0) + addf(str, "error: strftime (%lu)\n", timestamp); + else + addf(str, "%s\n", s); +} + +static void print_scts(gnutls_buffer_st * str, const gnutls_datum_t *der, + const char *prefix) +{ + int retval; + unsigned int version; + time_t timestamp; + gnutls_datum_t logid = { NULL, 0 }, sig = { NULL, 0 }; + gnutls_sign_algorithm_t sigalg; + gnutls_x509_ct_scts_t scts; + + retval = gnutls_x509_ext_ct_scts_init(&scts); + if (retval < 0) { + addf(str, "error: gnutls_x509_ext_ct_scts_init(): %s\n", + gnutls_strerror(retval)); + return; + } + + retval = gnutls_x509_ext_ct_import_scts(der, scts, 0); + if (retval < 0) { + addf(str, "error: gnutls_x509_ext_ct_import_scts(): %s\n", + gnutls_strerror(retval)); + goto cleanup; + } + + for (int i = 0;; i++) { + retval = gnutls_x509_ct_sct_get_version(scts, i, &version); + if (retval == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) + break; + + addf(str, _("%s\t\t\tSigned Certificate Timestamp %d:\n"), + prefix, (i+1)); + + if (version != 1) { + addf(str, _("%s\t\t\t\tVersion: %d (unknown SCT version)\n"), + prefix, version); + continue; + } + + retval = gnutls_x509_ct_sct_get(scts, i, + ×tamp, + &logid, + &sigalg, &sig); + if (retval < 0) { + addf(str, "error: could not get SCT info: %s\n", + gnutls_strerror(retval)); + break; + } + + addf(str, _("%s\t\t\t\tVersion: %d\n"), + prefix, version); + addf(str, _("%s\t\t\t\tLog ID: "), prefix); + _gnutls_buffer_hexprint(str, logid.data, logid.size); + addf(str, "\n"); + addf(str, _("%s\t\t\t\tTime: "), prefix); + print_time(str, timestamp); + addf(str, _("%s\t\t\t\tExtensions: none\n"), /* there are no extensions defined for v1 */ + prefix); + addf(str, _("%s\t\t\t\tSignature algorithm: %s\n"), + prefix, gnutls_sign_get_name(sigalg)); + addf(str, _("%s\t\t\t\tSignature: "), prefix); + _gnutls_buffer_hexprint(str, sig.data, sig.size); + addf(str, "\n"); + + _gnutls_free_datum(&sig); + _gnutls_free_datum(&logid); + sig.data = NULL; + logid.data = NULL; + } + +cleanup: + _gnutls_free_datum(&sig); + _gnutls_free_datum(&logid); + gnutls_x509_ext_ct_scts_deinit(scts); +} + + +#define TYPE_CRT 2 +#define TYPE_CRQ 3 + +typedef union { + gnutls_x509_crt_t crt; + gnutls_x509_crq_t crq; +} cert_type_t; + +static void +print_aki_gn_serial(gnutls_buffer_st * str, gnutls_x509_aki_t aki) +{ + gnutls_datum_t san, other_oid, serial; + unsigned int alt_type; + int err; + + err = + gnutls_x509_aki_get_cert_issuer(aki, + 0, &alt_type, &san, &other_oid, &serial); + if (err == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { + return; + } else if (err < 0) { + addf(str, "error: gnutls_x509_aki_get_cert_issuer: %s\n", + gnutls_strerror(err)); + return; + } + + print_name(str, "\t\t\t", alt_type, &san, 0); + + adds(str, "\t\t\tserial: "); + _gnutls_buffer_hexprint(str, serial.data, serial.size); + adds(str, "\n"); +} + +static void print_aki(gnutls_buffer_st * str, gnutls_datum_t *der) +{ + int err; + gnutls_x509_aki_t aki; + gnutls_datum_t id; + + err = gnutls_x509_aki_init(&aki); + if (err < 0) { + addf(str, "error: gnutls_x509_aki_init: %s\n", + gnutls_strerror(err)); + return; + } + + err = gnutls_x509_ext_import_authority_key_id(der, aki, 0); + if (err < 0) { + addf(str, "error: gnutls_x509_ext_import_authority_key_id: %s\n", + gnutls_strerror(err)); + goto cleanup; + } + + /* Check if an alternative name is there */ + print_aki_gn_serial(str, aki); + + err = gnutls_x509_aki_get_id(aki, &id); + if (err == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { + goto cleanup; + } else if (err < 0) { + addf(str, "error: gnutls_x509_aki_get_id: %s\n", + gnutls_strerror(err)); + goto cleanup; + } + + adds(str, "\t\t\t"); + _gnutls_buffer_hexprint(str, id.data, id.size); + adds(str, "\n"); + + cleanup: + gnutls_x509_aki_deinit(aki); +} + +static void +print_key_usage2(gnutls_buffer_st * str, const char *prefix, unsigned int key_usage) +{ + if (key_usage & GNUTLS_KEY_DIGITAL_SIGNATURE) + addf(str, _("%sDigital signature.\n"), prefix); + if (key_usage & GNUTLS_KEY_NON_REPUDIATION) + addf(str, _("%sNon repudiation.\n"), prefix); + if (key_usage & GNUTLS_KEY_KEY_ENCIPHERMENT) + addf(str, _("%sKey encipherment.\n"), prefix); + if (key_usage & GNUTLS_KEY_DATA_ENCIPHERMENT) + addf(str, _("%sData encipherment.\n"), prefix); + if (key_usage & GNUTLS_KEY_KEY_AGREEMENT) + addf(str, _("%sKey agreement.\n"), prefix); + if (key_usage & GNUTLS_KEY_KEY_CERT_SIGN) + addf(str, _("%sCertificate signing.\n"), prefix); + if (key_usage & GNUTLS_KEY_CRL_SIGN) + addf(str, _("%sCRL signing.\n"), prefix); + if (key_usage & GNUTLS_KEY_ENCIPHER_ONLY) + addf(str, _("%sKey encipher only.\n"), prefix); + if (key_usage & GNUTLS_KEY_DECIPHER_ONLY) + addf(str, _("%sKey decipher only.\n"), prefix); +} + +static void +print_key_usage(gnutls_buffer_st * str, const char *prefix, gnutls_datum_t *der) +{ + unsigned int key_usage; + int err; + + err = gnutls_x509_ext_import_key_usage(der, &key_usage); + if (err < 0) { + addf(str, "error: get_key_usage: %s\n", + gnutls_strerror(err)); + return; + } + + print_key_usage2(str, prefix, key_usage); +} + +static void +print_private_key_usage_period(gnutls_buffer_st * str, const char *prefix, gnutls_datum_t *der) +{ + time_t activation, expiration; + int err; + char s[42]; + struct tm t; + size_t max; + + err = gnutls_x509_ext_import_private_key_usage_period(der, &activation, &expiration); + if (err < 0) { + addf(str, "error: get_private_key_usage_period: %s\n", + gnutls_strerror(err)); + return; + } + + max = sizeof(s); + + if (gmtime_r(&activation, &t) == NULL) + addf(str, "error: gmtime_r (%ld)\n", + (unsigned long) activation); + else if (strftime(s, max, "%a %b %d %H:%M:%S UTC %Y", &t) == 0) + addf(str, "error: strftime (%ld)\n", + (unsigned long) activation); + else + addf(str, _("\t\t\tNot Before: %s\n"), s); + + if (gmtime_r(&expiration, &t) == NULL) + addf(str, "error: gmtime_r (%ld)\n", + (unsigned long) expiration); + else if (strftime(s, max, "%a %b %d %H:%M:%S UTC %Y", &t) == 0) + addf(str, "error: strftime (%ld)\n", + (unsigned long) expiration); + else + addf(str, _("\t\t\tNot After: %s\n"), s); + +} + +static void print_crldist(gnutls_buffer_st * str, gnutls_datum_t *der) +{ + int err; + int indx; + gnutls_x509_crl_dist_points_t dp; + unsigned int flags, type; + gnutls_datum_t dist; + + err = gnutls_x509_crl_dist_points_init(&dp); + if (err < 0) { + addf(str, "error: gnutls_x509_crl_dist_points_init: %s\n", + gnutls_strerror(err)); + return; + } + + err = gnutls_x509_ext_import_crl_dist_points(der, dp, 0); + if (err < 0) { + addf(str, "error: gnutls_x509_ext_import_crl_dist_points: %s\n", + gnutls_strerror(err)); + goto cleanup; + } + + for (indx = 0;; indx++) { + err = + gnutls_x509_crl_dist_points_get(dp, indx, &type, &dist, &flags); + if (err == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) + goto cleanup; + else if (err < 0) { + addf(str, "error: get_crl_dist_points: %s\n", + gnutls_strerror(err)); + return; + } + + print_name(str, "\t\t\t", type, &dist, 0); + } + cleanup: + gnutls_x509_crl_dist_points_deinit(dp); +} + +static void +print_key_purpose(gnutls_buffer_st * str, const char *prefix, gnutls_datum_t *der) +{ + int indx; + gnutls_datum_t oid; + char *p; + int err; + gnutls_x509_key_purposes_t purposes; + + err = gnutls_x509_key_purpose_init(&purposes); + if (err < 0) { + addf(str, "error: gnutls_x509_key_purpose_init: %s\n", + gnutls_strerror(err)); + return; + } + + err = gnutls_x509_ext_import_key_purposes(der, purposes, 0); + if (err < 0) { + addf(str, "error: gnutls_x509_ext_import_key_purposes: %s\n", + gnutls_strerror(err)); + goto cleanup; + } + + for (indx = 0;; indx++) { + err = gnutls_x509_key_purpose_get(purposes, indx, &oid); + if (err == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) + goto cleanup; + else if (err < 0) { + addf(str, "error: gnutls_x509_key_purpose_get: %s\n", + gnutls_strerror(err)); + goto cleanup; + } + + p = (void*)oid.data; + if (strcmp(p, GNUTLS_KP_TLS_WWW_SERVER) == 0) + addf(str, _("%s\t\t\tTLS WWW Server.\n"), prefix); + else if (strcmp(p, GNUTLS_KP_TLS_WWW_CLIENT) == 0) + addf(str, _("%s\t\t\tTLS WWW Client.\n"), prefix); + else if (strcmp(p, GNUTLS_KP_CODE_SIGNING) == 0) + addf(str, _("%s\t\t\tCode signing.\n"), prefix); + else if (strcmp(p, GNUTLS_KP_EMAIL_PROTECTION) == 0) + addf(str, _("%s\t\t\tEmail protection.\n"), + prefix); + else if (strcmp(p, GNUTLS_KP_TIME_STAMPING) == 0) + addf(str, _("%s\t\t\tTime stamping.\n"), prefix); + else if (strcmp(p, GNUTLS_KP_OCSP_SIGNING) == 0) + addf(str, _("%s\t\t\tOCSP signing.\n"), prefix); + else if (strcmp(p, GNUTLS_KP_IPSEC_IKE) == 0) + addf(str, _("%s\t\t\tIpsec IKE.\n"), prefix); + else if (strcmp(p, GNUTLS_KP_MS_SMART_CARD_LOGON) == 0) + addf(str, _("%s\t\t\tSmart Card Logon.\n"), prefix); + else if (strcmp(p, GNUTLS_KP_ANY) == 0) + addf(str, _("%s\t\t\tAny purpose.\n"), prefix); + else + addf(str, "%s\t\t\t%s\n", prefix, p); + } + cleanup: + gnutls_x509_key_purpose_deinit(purposes); +} + +static void +print_basic(gnutls_buffer_st * str, const char *prefix, gnutls_datum_t *der) +{ + int pathlen; + unsigned ca; + int err; + + err = gnutls_x509_ext_import_basic_constraints(der, &ca, &pathlen); + if (err < 0) { + addf(str, "error: get_basic_constraints: %s\n", + gnutls_strerror(err)); + return; + } + + if (ca == 0) + addf(str, _("%s\t\t\tCertificate Authority (CA): FALSE\n"), + prefix); + else + addf(str, _("%s\t\t\tCertificate Authority (CA): TRUE\n"), + prefix); + + if (pathlen >= 0) + addf(str, _("%s\t\t\tPath Length Constraint: %d\n"), + prefix, pathlen); +} + + +static void +print_altname(gnutls_buffer_st * str, const char *prefix, gnutls_datum_t *der) +{ + unsigned int altname_idx; + gnutls_subject_alt_names_t names; + unsigned int type; + gnutls_datum_t san; + gnutls_datum_t othername; + char pfx[16]; + int err; + + err = gnutls_subject_alt_names_init(&names); + if (err < 0) { + addf(str, "error: gnutls_subject_alt_names_init: %s\n", + gnutls_strerror(err)); + return; + } + + err = gnutls_x509_ext_import_subject_alt_names(der, names, 0); + if (err < 0) { + addf(str, "error: gnutls_x509_ext_import_subject_alt_names: %s\n", + gnutls_strerror(err)); + goto cleanup; + } + + for (altname_idx = 0;; altname_idx++) { + err = gnutls_subject_alt_names_get(names, altname_idx, + &type, &san, &othername); + if (err == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) + break; + else if (err < 0) { + addf(str, + "error: gnutls_subject_alt_names_get: %s\n", + gnutls_strerror(err)); + break; + } + + + if (type == GNUTLS_SAN_OTHERNAME) { + unsigned vtype; + gnutls_datum_t virt; + + err = gnutls_x509_othername_to_virtual((char*)othername.data, &san, &vtype, &virt); + if (err >= 0) { + snprintf(pfx, sizeof(pfx), "%s\t\t\t", prefix); + print_name(str, pfx, vtype, &virt, 0); + gnutls_free(virt.data); + continue; + } + + addf(str, + _("%s\t\t\totherName OID: %.*s\n"), + prefix, (int)othername.size, (char*)othername.data); + addf(str, _("%s\t\t\totherName DER: "), + prefix); + _gnutls_buffer_hexprint(str, san.data, san.size); + addf(str, _("\n%s\t\t\totherName ASCII: "), + prefix); + _gnutls_buffer_asciiprint(str, (char*)san.data, san.size); + addf(str, "\n"); + } else { + + snprintf(pfx, sizeof(pfx), "%s\t\t\t", prefix); + print_name(str, pfx, type, &san, 0); + } + } + + cleanup: + gnutls_subject_alt_names_deinit(names); +} + +static void +guiddump(gnutls_buffer_st * str, const char *data, size_t len, + const char *spc) +{ + size_t j; + + if (spc) + adds(str, spc); + addf(str, "{"); + addf(str, "%.2X", (unsigned char) data[3]); + addf(str, "%.2X", (unsigned char) data[2]); + addf(str, "%.2X", (unsigned char) data[1]); + addf(str, "%.2X", (unsigned char) data[0]); + addf(str, "-"); + addf(str, "%.2X", (unsigned char) data[5]); + addf(str, "%.2X", (unsigned char) data[4]); + addf(str, "-"); + addf(str, "%.2X", (unsigned char) data[7]); + addf(str, "%.2X", (unsigned char) data[6]); + addf(str, "-"); + addf(str, "%.2X", (unsigned char) data[8]); + addf(str, "%.2X", (unsigned char) data[9]); + addf(str, "-"); + for (j = 10; j < 16; j++) { + addf(str, "%.2X", (unsigned char) data[j]); + } + addf(str, "}\n"); +} + +static void +print_unique_ids(gnutls_buffer_st * str, const gnutls_x509_crt_t cert) +{ + int result; + char buf[256]; /* if its longer, we won't bother to print it */ + size_t buf_size = 256; + + result = + gnutls_x509_crt_get_issuer_unique_id(cert, buf, &buf_size); + if (result >= 0) { + addf(str, ("\tIssuer Unique ID:\n")); + _gnutls_buffer_hexdump(str, buf, buf_size, "\t\t\t"); + if (buf_size == 16) { /* this could be a GUID */ + guiddump(str, buf, buf_size, "\t\t\t"); + } + } + + buf_size = 256; + result = + gnutls_x509_crt_get_subject_unique_id(cert, buf, &buf_size); + if (result >= 0) { + addf(str, ("\tSubject Unique ID:\n")); + _gnutls_buffer_hexdump(str, buf, buf_size, "\t\t\t"); + if (buf_size == 16) { /* this could be a GUID */ + guiddump(str, buf, buf_size, "\t\t\t"); + } + } +} + +static void print_tlsfeatures(gnutls_buffer_st * str, const char *prefix, const gnutls_datum_t *der) +{ + int err; + int seq; + gnutls_x509_tlsfeatures_t features; + const char *name; + unsigned int feature; + + err = gnutls_x509_tlsfeatures_init(&features); + if (err < 0) + return; + + err = gnutls_x509_ext_import_tlsfeatures(der, features, 0); + if (err < 0) { + addf(str, "error: get_tlsfeatures: %s\n", + gnutls_strerror(err)); + goto cleanup; + } + + for (seq=0;;seq++) { + err = gnutls_x509_tlsfeatures_get(features, seq, &feature); + if (err == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) + goto cleanup; + if (err < 0) { + addf(str, "error: get_tlsfeatures: %s\n", + gnutls_strerror(err)); + goto cleanup; + } + + name = gnutls_ext_get_name(feature); + if (name == NULL) + addf(str, "%s\t\t\t%u\n", prefix, feature); + else + addf(str, "%s\t\t\t%s(%u)\n", prefix, name, feature); + } + +cleanup: + gnutls_x509_tlsfeatures_deinit(features); +} + +static void print_subject_sign_tool(gnutls_buffer_st * str, const char *prefix, const gnutls_datum_t *der) +{ + int ret; + gnutls_datum_t tmp = {NULL, 0}; + + ret = _gnutls_x509_decode_string(ASN1_ETYPE_UTF8_STRING, der->data, der->size, &tmp, 0); + if (ret < 0) { + addf(str, _("%s\t\t\tASCII: "), prefix); + _gnutls_buffer_asciiprint(str, (char*)der->data, der->size); + + addf(str, "\n"); + addf(str, _("%s\t\t\tHexdump: "), prefix); + _gnutls_buffer_hexprint(str, (char*)der->data, der->size); + adds(str, "\n"); + + return; + } + + addf(str, _("%s\t\t\t%.*s\n"), prefix, tmp.size, NON_NULL(tmp.data)); + _gnutls_free_datum(&tmp); +} + +static void print_issuer_sign_tool(gnutls_buffer_st * str, const char *prefix, const gnutls_datum_t *der) +{ + int ret; + asn1_node tmpasn = NULL; + char asn1_err[ASN1_MAX_ERROR_DESCRIPTION_SIZE] = ""; + gnutls_datum_t tmp; + + if (asn1_create_element(_gnutls_get_gnutls_asn(), "GNUTLS.IssuerSignTool", + &tmpasn) != ASN1_SUCCESS) { + gnutls_assert(); + goto hexdump; + } + + if (_asn1_strict_der_decode(&tmpasn, der->data, der->size, asn1_err) != ASN1_SUCCESS) { + gnutls_assert(); + _gnutls_debug_log("_asn1_strict_der_decode: %s\n", asn1_err); + goto hexdump; + } + + ret = _gnutls_x509_read_value(tmpasn, "signTool", &tmp); + if (ret < 0) { + gnutls_assert(); + goto hexdump; + } + addf(str, _("%s\t\t\tSignTool: %.*s\n"), prefix, tmp.size, NON_NULL(tmp.data)); + _gnutls_free_datum(&tmp); + + ret = _gnutls_x509_read_value(tmpasn, "cATool", &tmp); + if (ret < 0) { + gnutls_assert(); + goto hexdump; + } + addf(str, _("%s\t\t\tCATool: %.*s\n"), prefix, tmp.size, NON_NULL(tmp.data)); + _gnutls_free_datum(&tmp); + + ret = _gnutls_x509_read_value(tmpasn, "signToolCert", &tmp); + if (ret < 0) { + gnutls_assert(); + goto hexdump; + } + addf(str, _("%s\t\t\tSignToolCert: %.*s\n"), prefix, tmp.size, NON_NULL(tmp.data)); + _gnutls_free_datum(&tmp); + + ret = _gnutls_x509_read_value(tmpasn, "cAToolCert", &tmp); + if (ret < 0) { + gnutls_assert(); + goto hexdump; + } + addf(str, _("%s\t\t\tCAToolCert: %.*s\n"), prefix, tmp.size, NON_NULL(tmp.data)); + _gnutls_free_datum(&tmp); + + asn1_delete_structure(&tmpasn); + + return; + +hexdump: + asn1_delete_structure(&tmpasn); + + addf(str, _("%s\t\t\tASCII: "), prefix); + _gnutls_buffer_asciiprint(str, (char*)der->data, der->size); + + addf(str, "\n"); + addf(str, _("%s\t\t\tHexdump: "), prefix); + _gnutls_buffer_hexprint(str, (char*)der->data, der->size); + adds(str, "\n"); +} + +#define ENTRY(oid, name) {oid, sizeof(oid)-1, name, sizeof(name)-1, NULL, 0} + +static const struct oid_to_string cp_oid2str[] = { + ENTRY("2.5.29.32.0", "anyPolicy"), + + ENTRY("2.23.140.1.2.1", "CA/B Domain Validated"), + ENTRY("2.23.140.1.2.2", "CA/B Organization Validated"), + ENTRY("2.23.140.1.2.3", "CA/B Individual Validated"), + ENTRY("2.23.140.1.1", "CA/B Extended Validation"), + + /* draft-deremin-rfc4491-bis */ + ENTRY("1.2.643.100.113.1", "Russian security class KC1"), + ENTRY("1.2.643.100.113.2", "Russian security class KC2"), + ENTRY("1.2.643.100.113.3", "Russian security class KC3"), + ENTRY("1.2.643.100.113.4", "Russian security class KB1"), + ENTRY("1.2.643.100.113.5", "Russian security class KB2"), + ENTRY("1.2.643.100.113.6", "Russian security class KA1"), + + {NULL, 0, NULL, 0}, +}; + +struct ext_indexes_st { + int san; + int ian; + int proxy; + int basic; + int keyusage; + int keypurpose; + int ski; + int aki, nc; + int crldist, pkey_usage_period; + int tlsfeatures; +}; + +static void print_extension(gnutls_buffer_st * str, const char *prefix, + struct ext_indexes_st *idx, const char *oid, + unsigned critical, gnutls_datum_t *der) +{ + int err; + unsigned j; + char pfx[16]; + + if (strcmp(oid, "2.5.29.19") == 0) { + if (idx->basic) { + addf(str, + "warning: more than one basic constraint\n"); + } + + addf(str, _("%s\t\tBasic Constraints (%s):\n"), + prefix, + critical ? _("critical") : _("not critical")); + + print_basic(str, prefix, der); + idx->basic++; + + } else if (strcmp(oid, "2.5.29.14") == 0) { + if (idx->ski) { + addf(str, + "warning: more than one SKI extension\n"); + } + + addf(str, + _("%s\t\tSubject Key Identifier (%s):\n"), + prefix, + critical ? _("critical") : _("not critical")); + + print_ski(str, der); + + idx->ski++; + } else if (strcmp(oid, "2.5.29.32") == 0) { + struct gnutls_x509_policy_st policy; + gnutls_x509_policies_t policies; + const char *name; + const struct oid_to_string *entry; + int x; + + err = gnutls_x509_policies_init(&policies); + if (err < 0) { + addf(str, + "error: certificate policies: %s\n", + gnutls_strerror(err)); + return; + } + + err = gnutls_x509_ext_import_policies(der, policies, 0); + if (err < 0) { + addf(str, + "error: certificate policies import: %s\n", + gnutls_strerror(err)); + gnutls_x509_policies_deinit(policies); + return; + } + + for (x = 0;; x++) { + err = gnutls_x509_policies_get(policies, x, &policy); + if (err == + GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) + break; + + if (err < 0) { + addf(str, + "error: certificate policy: %s\n", + gnutls_strerror(err)); + break; + } + + if (x == 0) + addf(str, + "%s\t\tCertificate Policies (%s):\n", + prefix, + critical ? _("critical") : + _("not critical")); + + entry = _gnutls_oid_get_entry(cp_oid2str, policy.oid); + if (entry != NULL && entry->name_desc != NULL) + addf(str, "%s\t\t\t%s (%s)\n", prefix, policy.oid, entry->name_desc); + else + addf(str, "%s\t\t\t%s\n", prefix, policy.oid); + for (j = 0; j < policy.qualifiers; j++) { + if (policy.qualifier[j].type == + GNUTLS_X509_QUALIFIER_URI) + name = "URI"; + else if (policy.qualifier[j]. + type == + GNUTLS_X509_QUALIFIER_NOTICE) + name = "Note"; + else + name = "Unknown qualifier"; + addf(str, "%s\t\t\t\t%s: %s\n", + prefix, name, + policy.qualifier[j].data); + } + } + gnutls_x509_policies_deinit(policies); + } else if (strcmp(oid, "2.5.29.54") == 0) { + unsigned int skipcerts; + + err = gnutls_x509_ext_import_inhibit_anypolicy(der, &skipcerts); + if (err < 0) { + addf(str, + "error: certificate inhibit any policy import: %s\n", + gnutls_strerror(err)); + return; + } + + addf(str, + "%s\t\tInhibit anyPolicy skip certs: %u (%s)\n", + prefix, skipcerts, + critical ? _("critical") : + _("not critical")); + + } else if (strcmp(oid, "2.5.29.35") == 0) { + + if (idx->aki) { + addf(str, + "warning: more than one AKI extension\n"); + } + + addf(str, + _("%s\t\tAuthority Key Identifier (%s):\n"), + prefix, + critical ? _("critical") : _("not critical")); + + print_aki(str, der); + + idx->aki++; + } else if (strcmp(oid, "2.5.29.15") == 0) { + if (idx->keyusage) { + addf(str, + "warning: more than one key usage extension\n"); + } + + addf(str, _("%s\t\tKey Usage (%s):\n"), prefix, + critical ? _("critical") : _("not critical")); + + snprintf(pfx, sizeof(pfx), "%s\t\t\t", prefix); + print_key_usage(str, pfx, der); + + idx->keyusage++; + } else if (strcmp(oid, "2.5.29.16") == 0) { + if (idx->pkey_usage_period) { + addf(str, + "warning: more than one private key usage period extension\n"); + } + + addf(str, + _("%s\t\tPrivate Key Usage Period (%s):\n"), + prefix, + critical ? _("critical") : _("not critical")); + + print_private_key_usage_period(str, prefix, der); + + idx->pkey_usage_period++; + } else if (strcmp(oid, "2.5.29.37") == 0) { + if (idx->keypurpose) { + addf(str, + "warning: more than one key purpose extension\n"); + } + + addf(str, _("%s\t\tKey Purpose (%s):\n"), prefix, + critical ? _("critical") : _("not critical")); + + print_key_purpose(str, prefix, der); + idx->keypurpose++; + } else if (strcmp(oid, "2.5.29.17") == 0) { + if (idx->san) { + addf(str, + "warning: more than one SKI extension\n"); + } + + addf(str, + _("%s\t\tSubject Alternative Name (%s):\n"), + prefix, + critical ? _("critical") : _("not critical")); + print_altname(str, prefix, der); + idx->san++; + } else if (strcmp(oid, "2.5.29.18") == 0) { + if (idx->ian) { + addf(str, + "warning: more than one Issuer AltName extension\n"); + } + + addf(str, + _("%s\t\tIssuer Alternative Name (%s):\n"), + prefix, + critical ? _("critical") : _("not critical")); + + print_altname(str, prefix, der); + + idx->ian++; + } else if (strcmp(oid, "2.5.29.31") == 0) { + if (idx->crldist) { + addf(str, + "warning: more than one CRL distribution point\n"); + } + + addf(str, + _("%s\t\tCRL Distribution points (%s):\n"), + prefix, + critical ? _("critical") : _("not critical")); + + print_crldist(str, der); + idx->crldist++; + } else if (strcmp(oid, "1.3.6.1.5.5.7.1.14") == 0) { + if (idx->proxy) { + addf(str, + "warning: more than one proxy extension\n"); + } + + addf(str, + _ + ("%s\t\tProxy Certificate Information (%s):\n"), + prefix, + critical ? _("critical") : _("not critical")); + + print_proxy(str, der); + + idx->proxy++; + } else if (strcmp(oid, "1.3.6.1.5.5.7.1.1") == 0) { + addf(str, _("%s\t\tAuthority Information " + "Access (%s):\n"), prefix, + critical ? _("critical") : _("not critical")); + + print_aia(str, der); + } else if (strcmp(oid, GNUTLS_X509EXT_OID_CT_SCT_V1) == 0) { + addf(str, _("%s\t\tCT Precertificate SCTs (%s):\n"), + prefix, critical ? _("critical") : _("not critical")); + + print_scts(str, der, prefix); + } else if (strcmp(oid, "2.5.29.30") == 0) { + if (idx->nc) { + addf(str, + "warning: more than one name constraints extension\n"); + } + idx->nc++; + + addf(str, _("%s\t\tName Constraints (%s):\n"), prefix, + critical ? _("critical") : _("not critical")); + + print_nc(str, prefix, der); + } else if (strcmp(oid, GNUTLS_X509EXT_OID_TLSFEATURES) == 0) { + if (idx->tlsfeatures) { + addf(str, + "warning: more than one tlsfeatures extension\n"); + } + + addf(str, _("%s\t\tTLS Features (%s):\n"), + prefix, + critical ? _("critical") : _("not critical")); + + print_tlsfeatures(str, prefix, der); + + idx->tlsfeatures++; + } else if (strcmp(oid, "1.2.643.100.111") == 0) { + addf(str, _("%s\t\tSubject Signing Tool(%s):\n"), + prefix, + critical ? _("critical") : _("not critical")); + + print_subject_sign_tool(str, prefix, der); + } else if (strcmp(oid, "1.2.643.100.112") == 0) { + addf(str, _("%s\t\tIssuer Signing Tool(%s):\n"), + prefix, + critical ? _("critical") : _("not critical")); + + print_issuer_sign_tool(str, prefix, der); + } else if (strcmp(oid, "2.5.4.3") == 0) { + int ret; + gnutls_datum_t tmp = {NULL, 0}; + + addf(str, _("%s\t\tCommon Name (%s):\n"), + prefix, + critical ? _("critical") : _("not critical")); + + ret = _gnutls_x509_decode_string(ASN1_ETYPE_PRINTABLE_STRING, der->data, der->size, &tmp, 0); + if (ret < 0) { + addf(str, "error: x509_decode_string: %s\n", + gnutls_strerror(ret)); + } else { + addf(str, "%s\t\t\t%s\n", prefix, tmp.data); + gnutls_free(tmp.data); + } + } else { + addf(str, _("%s\t\tUnknown extension %s (%s):\n"), + prefix, oid, + critical ? _("critical") : _("not critical")); + + addf(str, _("%s\t\t\tASCII: "), prefix); + _gnutls_buffer_asciiprint(str, (char*)der->data, der->size); + + addf(str, "\n"); + addf(str, _("%s\t\t\tHexdump: "), prefix); + _gnutls_buffer_hexprint(str, (char*)der->data, der->size); + adds(str, "\n"); + } +} + +static void +print_extensions(gnutls_buffer_st * str, const char *prefix, int type, + cert_type_t cert) +{ + unsigned i; + int err; + gnutls_datum_t der = {NULL, 0}; + struct ext_indexes_st idx; + + memset(&idx, 0, sizeof(idx)); + + for (i = 0;; i++) { + char oid[MAX_OID_SIZE] = ""; + size_t sizeof_oid = sizeof(oid); + unsigned int critical; + + if (type == TYPE_CRT) + err = + gnutls_x509_crt_get_extension_info(cert.crt, i, + oid, + &sizeof_oid, + &critical); + + else if (type == TYPE_CRQ) + err = + gnutls_x509_crq_get_extension_info(cert.crq, i, + oid, + &sizeof_oid, + &critical); + else { + gnutls_assert(); + return; + } + + if (err == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) + break; + if (err < 0) { + addf(str, "error: get_extension_info: %s\n", + gnutls_strerror(err)); + break; + } + + if (i == 0) + addf(str, _("%s\tExtensions:\n"), prefix); + + if (type == TYPE_CRT) + err = gnutls_x509_crt_get_extension_data2(cert.crt, i, &der); + else + err = gnutls_x509_crq_get_extension_data2(cert.crq, i, &der); + + if (err < 0) { + der.data = NULL; + der.size = 0; + } + + print_extension(str, prefix, &idx, oid, critical, &der); + gnutls_free(der.data); + } +} + +static void reverse_datum(gnutls_datum_t *d) +{ + unsigned int i; + unsigned char c; + + for (i = 0; i < d->size / 2; i++) { + c = d->data[i]; + d->data[i] = d->data[d->size - i - 1]; + d->data[d->size - i - 1] = c; + } +} + +static void +print_pubkey(gnutls_buffer_st * str, const char *key_name, + gnutls_pubkey_t pubkey, gnutls_x509_spki_st *spki, + gnutls_certificate_print_formats_t format) +{ + int err; + const char *name; + unsigned bits; + unsigned pk; + + err = gnutls_pubkey_get_pk_algorithm(pubkey, &bits); + if (err < 0) { + addf(str, "error: get_pk_algorithm: %s\n", + gnutls_strerror(err)); + return; + } + + pk = err; + + name = gnutls_pk_algorithm_get_name(pk); + if (name == NULL) + name = _("unknown"); + + addf(str, _("\t%sPublic Key Algorithm: %s\n"), key_name, name); + + addf(str, _("\tAlgorithm Security Level: %s (%d bits)\n"), + gnutls_sec_param_get_name(gnutls_pk_bits_to_sec_param + (err, bits)), bits); + + if (spki && pk == GNUTLS_PK_RSA_PSS && spki->pk == pk) { + addf(str, _("\t\tParameters:\n")); + addf(str, "\t\t\tHash Algorithm: %s\n", + gnutls_digest_get_name(spki->rsa_pss_dig)); + addf(str, "\t\t\tSalt Length: %d\n", spki->salt_size); + } + + switch (pk) { + case GNUTLS_PK_RSA: + case GNUTLS_PK_RSA_PSS: + { + gnutls_datum_t m, e; + + err = gnutls_pubkey_get_pk_rsa_raw(pubkey, &m, &e); + if (err < 0) + addf(str, "error: get_pk_rsa_raw: %s\n", + gnutls_strerror(err)); + else { + if (format == + GNUTLS_CRT_PRINT_FULL_NUMBERS) { + addf(str, + _("\t\tModulus (bits %d): "), + bits); + _gnutls_buffer_hexprint(str, + m.data, + m.size); + adds(str, "\n"); + addf(str, + _("\t\tExponent (bits %d): "), + e.size * 8); + _gnutls_buffer_hexprint(str, + e.data, + e.size); + adds(str, "\n"); + } else { + addf(str, + _("\t\tModulus (bits %d):\n"), + bits); + _gnutls_buffer_hexdump(str, m.data, + m.size, + "\t\t\t"); + addf(str, + _ + ("\t\tExponent (bits %d):\n"), + e.size * 8); + _gnutls_buffer_hexdump(str, e.data, + e.size, + "\t\t\t"); + } + + gnutls_free(m.data); + gnutls_free(e.data); + } + + } + break; + + case GNUTLS_PK_EDDSA_ED25519: + case GNUTLS_PK_EDDSA_ED448: + case GNUTLS_PK_ECDH_X25519: + case GNUTLS_PK_ECDH_X448: + case GNUTLS_PK_ECDSA: + { + gnutls_datum_t x, y; + gnutls_ecc_curve_t curve; + + err = + gnutls_pubkey_get_pk_ecc_raw(pubkey, &curve, + &x, &y); + if (err < 0) { + addf(str, "error: get_pk_ecc_raw: %s\n", + gnutls_strerror(err)); + } else { + addf(str, _("\t\tCurve:\t%s\n"), + gnutls_ecc_curve_get_name(curve)); + if (format == + GNUTLS_CRT_PRINT_FULL_NUMBERS) { + adds(str, _("\t\tX: ")); + _gnutls_buffer_hexprint(str, + x.data, + x.size); + adds(str, "\n"); + if (y.size > 0) { + adds(str, _("\t\tY: ")); + _gnutls_buffer_hexprint(str, + y.data, + y.size); + adds(str, "\n"); + } + } else { + adds(str, _("\t\tX:\n")); + _gnutls_buffer_hexdump(str, x.data, + x.size, + "\t\t\t"); + if (y.size > 0) { + adds(str, _("\t\tY:\n")); + _gnutls_buffer_hexdump(str, y.data, + y.size, + "\t\t\t"); + } + } + + gnutls_free(x.data); + gnutls_free(y.data); + + } + } + break; + case GNUTLS_PK_DSA: + { + gnutls_datum_t p, q, g, y; + + err = + gnutls_pubkey_get_pk_dsa_raw(pubkey, &p, &q, + &g, &y); + if (err < 0) + addf(str, "error: get_pk_dsa_raw: %s\n", + gnutls_strerror(err)); + else { + if (format == + GNUTLS_CRT_PRINT_FULL_NUMBERS) { + addf(str, + _ + ("\t\tPublic key (bits %d): "), + bits); + _gnutls_buffer_hexprint(str, + y.data, + y.size); + adds(str, "\n"); + adds(str, _("\t\tP: ")); + _gnutls_buffer_hexprint(str, + p.data, + p.size); + adds(str, "\n"); + adds(str, _("\t\tQ: ")); + _gnutls_buffer_hexprint(str, + q.data, + q.size); + adds(str, "\n"); + adds(str, _("\t\tG: ")); + _gnutls_buffer_hexprint(str, + g.data, + g.size); + adds(str, "\n"); + } else { + addf(str, + _ + ("\t\tPublic key (bits %d):\n"), + bits); + _gnutls_buffer_hexdump(str, y.data, + y.size, + "\t\t\t"); + adds(str, _("\t\tP:\n")); + _gnutls_buffer_hexdump(str, p.data, + p.size, + "\t\t\t"); + adds(str, _("\t\tQ:\n")); + _gnutls_buffer_hexdump(str, q.data, + q.size, + "\t\t\t"); + adds(str, _("\t\tG:\n")); + _gnutls_buffer_hexdump(str, g.data, + g.size, + "\t\t\t"); + } + + gnutls_free(p.data); + gnutls_free(q.data); + gnutls_free(g.data); + gnutls_free(y.data); + + } + } + break; + + case GNUTLS_PK_GOST_01: + case GNUTLS_PK_GOST_12_256: + case GNUTLS_PK_GOST_12_512: + { + gnutls_datum_t x, y; + gnutls_ecc_curve_t curve; + gnutls_digest_algorithm_t digest; + gnutls_gost_paramset_t param; + + err = + gnutls_pubkey_export_gost_raw2(pubkey, &curve, + &digest, + ¶m, + &x, &y, 0); + if (err < 0) + addf(str, "error: get_pk_gost_raw: %s\n", + gnutls_strerror(err)); + else { + addf(str, _("\t\tCurve:\t%s\n"), + gnutls_ecc_curve_get_name(curve)); + addf(str, _("\t\tDigest:\t%s\n"), + gnutls_digest_get_name(digest)); + addf(str, _("\t\tParamSet: %s\n"), + gnutls_gost_paramset_get_name(param)); + reverse_datum(&x); + reverse_datum(&y); + if (format == + GNUTLS_CRT_PRINT_FULL_NUMBERS) { + adds(str, _("\t\tX: ")); + _gnutls_buffer_hexprint(str, + x.data, + x.size); + adds(str, "\n"); + adds(str, _("\t\tY: ")); + _gnutls_buffer_hexprint(str, + y.data, + y.size); + adds(str, "\n"); + } else { + adds(str, _("\t\tX:\n")); + _gnutls_buffer_hexdump(str, x.data, + x.size, + "\t\t\t"); + adds(str, _("\t\tY:\n")); + _gnutls_buffer_hexdump(str, y.data, + y.size, + "\t\t\t"); + } + + gnutls_free(x.data); + gnutls_free(y.data); + + } + } + break; + + default: + break; + } +} + +static int +print_crt_sig_params(gnutls_buffer_st * str, gnutls_x509_crt_t crt, + gnutls_certificate_print_formats_t format) +{ + int ret; + gnutls_pk_algorithm_t pk; + gnutls_x509_spki_st params; + gnutls_sign_algorithm_t sign; + + sign = gnutls_x509_crt_get_signature_algorithm(crt); + pk = gnutls_sign_get_pk_algorithm(sign); + if (pk == GNUTLS_PK_RSA_PSS) { + ret = _gnutls_x509_read_sign_params(crt->cert, + "signatureAlgorithm", + ¶ms); + if (ret < 0) { + addf(str, "error: read_pss_params: %s\n", + gnutls_strerror(ret)); + } else + addf(str, "\t\tSalt Length: %d\n", params.salt_size); + } + + return 0; +} + +static void print_pk_name(gnutls_buffer_st * str, gnutls_x509_crt_t crt) +{ + const char *p; + char *name = get_pk_name(crt, NULL); + if (name == NULL) + p = _("unknown"); + else + p = name; + + addf(str, "\tSubject Public Key Algorithm: %s\n", p); + gnutls_free(name); +} + +static int +print_crt_pubkey(gnutls_buffer_st * str, gnutls_x509_crt_t crt, + gnutls_certificate_print_formats_t format) +{ + gnutls_pubkey_t pubkey = NULL; + gnutls_x509_spki_st params; + int ret, pk; + + ret = _gnutls_x509_crt_read_spki_params(crt, ¶ms); + if (ret < 0) + return ret; + + pk = gnutls_x509_crt_get_pk_algorithm(crt, NULL); + if (pk < 0) { + gnutls_assert(); + pk = GNUTLS_PK_UNKNOWN; + } + + if (pk == GNUTLS_PK_UNKNOWN) { + print_pk_name(str, crt); /* print basic info only */ + return 0; + } + + ret = gnutls_pubkey_init(&pubkey); + if (ret < 0) + return ret; + + ret = gnutls_pubkey_import_x509(pubkey, crt, 0); + if (ret < 0) { + if (ret != GNUTLS_E_UNIMPLEMENTED_FEATURE) + addf(str, "error importing public key: %s\n", gnutls_strerror(ret)); + print_pk_name(str, crt); /* print basic info only */ + ret = 0; + goto cleanup; + } + + print_pubkey(str, _("Subject "), pubkey, ¶ms, format); + ret = 0; + + cleanup: + gnutls_pubkey_deinit(pubkey); + + return ret; +} + +static void +print_cert(gnutls_buffer_st * str, gnutls_x509_crt_t cert, + gnutls_certificate_print_formats_t format) +{ + /* Version. */ + { + int version = gnutls_x509_crt_get_version(cert); + if (version < 0) + addf(str, "error: get_version: %s\n", + gnutls_strerror(version)); + else + addf(str, _("\tVersion: %d\n"), version); + } + + /* Serial. */ + { + char serial[128]; + size_t serial_size = sizeof(serial); + int err; + + err = + gnutls_x509_crt_get_serial(cert, serial, &serial_size); + if (err < 0) + addf(str, "error: get_serial: %s\n", + gnutls_strerror(err)); + else { + adds(str, _("\tSerial Number (hex): ")); + _gnutls_buffer_hexprint(str, serial, serial_size); + adds(str, "\n"); + } + } + + /* Issuer. */ + if (format != GNUTLS_CRT_PRINT_UNSIGNED_FULL) { + gnutls_datum_t dn; + int err; + + err = gnutls_x509_crt_get_issuer_dn3(cert, &dn, 0); + if (err == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { + addf(str, _("\tIssuer:\n")); + } else if (err < 0) { + addf(str, "error: get_issuer_dn: %s\n", + gnutls_strerror(err)); + } else { + addf(str, _("\tIssuer: %s\n"), dn.data); + gnutls_free(dn.data); + } + } + + /* Validity. */ + { + time_t tim; + + adds(str, _("\tValidity:\n")); + + tim = gnutls_x509_crt_get_activation_time(cert); + if (tim != -1) { + char s[42]; + size_t max = sizeof(s); + struct tm t; + + if (gmtime_r(&tim, &t) == NULL) + addf(str, "error: gmtime_r (%ld)\n", + (unsigned long) tim); + else if (strftime + (s, max, "%a %b %d %H:%M:%S UTC %Y", + &t) == 0) + addf(str, "error: strftime (%ld)\n", + (unsigned long) tim); + else + addf(str, _("\t\tNot Before: %s\n"), s); + } else { + addf(str, _("\t\tNot Before: %s\n"), _("unknown")); + } + + tim = gnutls_x509_crt_get_expiration_time(cert); + if (tim != -1) { + char s[42]; + size_t max = sizeof(s); + struct tm t; + + if (gmtime_r(&tim, &t) == NULL) + addf(str, "error: gmtime_r (%ld)\n", + (unsigned long) tim); + else if (strftime + (s, max, "%a %b %d %H:%M:%S UTC %Y", + &t) == 0) + addf(str, "error: strftime (%ld)\n", + (unsigned long) tim); + else + addf(str, _("\t\tNot After: %s\n"), s); + } else { + addf(str, _("\t\tNot After: %s\n"), _("unknown")); + } + } + + /* Subject. */ + { + gnutls_datum_t dn; + int err; + + err = gnutls_x509_crt_get_dn3(cert, &dn, 0); + if (err == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { + addf(str, _("\tSubject:\n")); + } else if (err < 0) { + addf(str, "error: get_dn: %s\n", + gnutls_strerror(err)); + } else { + addf(str, _("\tSubject: %s\n"), dn.data); + gnutls_free(dn.data); + } + } + + /* SubjectPublicKeyInfo. */ + print_crt_pubkey(str, cert, format); + + print_unique_ids(str, cert); + + /* Extensions. */ + if (gnutls_x509_crt_get_version(cert) >= 3) { + cert_type_t ccert; + + ccert.crt = cert; + print_extensions(str, "", TYPE_CRT, ccert); + } + + /* Signature. */ + if (format != GNUTLS_CRT_PRINT_UNSIGNED_FULL) { + int err; + size_t size = 0; + char *buffer = NULL; + char *name; + const char *p; + + name = get_sign_name(cert, &err); + if (name == NULL) + p = _("unknown"); + else + p = name; + + addf(str, _("\tSignature Algorithm: %s\n"), p); + gnutls_free(name); + + print_crt_sig_params(str, cert, format); + + if (err != GNUTLS_SIGN_UNKNOWN && gnutls_sign_is_secure2(err, GNUTLS_SIGN_FLAG_SECURE_FOR_CERTS) == 0) { + adds(str, + _("warning: signed using a broken signature " + "algorithm that can be forged.\n")); + } + + err = gnutls_x509_crt_get_signature(cert, buffer, &size); + if (err != GNUTLS_E_SHORT_MEMORY_BUFFER) { + addf(str, "error: get_signature: %s\n", + gnutls_strerror(err)); + return; + } + + buffer = gnutls_malloc(size); + if (!buffer) { + addf(str, "error: malloc: %s\n", + gnutls_strerror(GNUTLS_E_MEMORY_ERROR)); + return; + } + + err = gnutls_x509_crt_get_signature(cert, buffer, &size); + if (err < 0) { + gnutls_free(buffer); + addf(str, "error: get_signature2: %s\n", + gnutls_strerror(err)); + return; + } + + adds(str, _("\tSignature:\n")); + _gnutls_buffer_hexdump(str, buffer, size, "\t\t"); + + gnutls_free(buffer); + } +} + +static void +print_fingerprint(gnutls_buffer_st * str, gnutls_x509_crt_t cert) +{ + int err; + char buffer[MAX_HASH_SIZE]; + size_t size = sizeof(buffer); + + adds(str, _("\tFingerprint:\n")); + + err = gnutls_x509_crt_get_fingerprint(cert, GNUTLS_DIG_SHA1, buffer, &size); + if (err < 0) { + addf(str, "error: get_fingerprint: %s\n", + gnutls_strerror(err)); + return; + } + + adds(str, _("\t\tsha1:")); + _gnutls_buffer_hexprint(str, buffer, size); + adds(str, "\n"); + + size = sizeof(buffer); + err = gnutls_x509_crt_get_fingerprint(cert, GNUTLS_DIG_SHA256, buffer, &size); + if (err < 0) { + addf(str, "error: get_fingerprint: %s\n", + gnutls_strerror(err)); + return; + } + adds(str, _("\t\tsha256:")); + _gnutls_buffer_hexprint(str, buffer, size); + adds(str, "\n"); +} + +typedef int get_id_func(void *obj, unsigned, unsigned char*, size_t*); + +static void print_obj_id(gnutls_buffer_st *str, const char *prefix, void *obj, get_id_func *get_id) +{ + unsigned char sha1_buffer[MAX_HASH_SIZE]; + unsigned char sha2_buffer[MAX_HASH_SIZE]; + int err; + size_t sha1_size, sha2_size; + + sha1_size = sizeof(sha1_buffer); + err = get_id(obj, GNUTLS_KEYID_USE_SHA1, sha1_buffer, &sha1_size); + if (err == GNUTLS_E_UNIMPLEMENTED_FEATURE) /* unsupported algo */ + return; + + if (err < 0) { + addf(str, "error: get_key_id(sha1): %s\n", gnutls_strerror(err)); + return; + } + + sha2_size = sizeof(sha2_buffer); + err = get_id(obj, GNUTLS_KEYID_USE_SHA256, sha2_buffer, &sha2_size); + if (err == GNUTLS_E_UNIMPLEMENTED_FEATURE) /* unsupported algo */ + return; + + if (err < 0) { + addf(str, "error: get_key_id(sha256): %s\n", gnutls_strerror(err)); + return; + } + + addf(str, _("%sPublic Key ID:\n%s\tsha1:"), prefix, prefix); + _gnutls_buffer_hexprint(str, sha1_buffer, sha1_size); + addf(str, "\n%s\tsha256:", prefix); + _gnutls_buffer_hexprint(str, sha2_buffer, sha2_size); + adds(str, "\n"); + + addf(str, _("%sPublic Key PIN:\n%s\tpin-sha256:"), prefix, prefix); + _gnutls_buffer_base64print(str, sha2_buffer, sha2_size); + adds(str, "\n"); + + return; +} + +static void print_keyid(gnutls_buffer_st * str, gnutls_x509_crt_t cert) +{ + int err; + const char *name; + unsigned int bits; + unsigned char sha1_buffer[MAX_HASH_SIZE]; + size_t sha1_size; + + err = gnutls_x509_crt_get_pk_algorithm(cert, &bits); + if (err < 0) + return; + + print_obj_id(str, "\t", cert, (get_id_func*)gnutls_x509_crt_get_key_id); + + if (IS_EC(err)) { + gnutls_ecc_curve_t curve; + + err = gnutls_x509_crt_get_pk_ecc_raw(cert, &curve, NULL, NULL); + if (err < 0) + return; + + name = gnutls_ecc_curve_get_name(curve); + bits = 0; + } else if (IS_GOSTEC(err)) { + gnutls_ecc_curve_t curve; + + err = gnutls_x509_crt_get_pk_gost_raw(cert, &curve, NULL, NULL, NULL, NULL); + if (err < 0) + return; + + name = gnutls_ecc_curve_get_name(curve); + bits = 0; + } else { + name = gnutls_pk_get_name(err); + } + + if (name == NULL) + return; + + sha1_size = sizeof(sha1_buffer); + err = gnutls_x509_crt_get_key_id(cert, GNUTLS_KEYID_USE_SHA1, sha1_buffer, &sha1_size); + if (err == GNUTLS_E_UNIMPLEMENTED_FEATURE) /* unsupported algo */ + return; +} + +static void +print_other(gnutls_buffer_st * str, gnutls_x509_crt_t cert, + gnutls_certificate_print_formats_t format) +{ + if (format != GNUTLS_CRT_PRINT_UNSIGNED_FULL) { + print_fingerprint(str, cert); + } + print_keyid(str, cert); +} + +static void print_oneline(gnutls_buffer_st * str, gnutls_x509_crt_t cert) +{ + int err; + + /* Subject. */ + { + gnutls_datum_t dn; + + err = gnutls_x509_crt_get_dn3(cert, &dn, 0); + if (err == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { + addf(str, _("no subject,")); + } else if (err < 0) { + addf(str, "unknown subject (%s), ", + gnutls_strerror(err)); + } else { + addf(str, "subject `%s', ", dn.data); + gnutls_free(dn.data); + } + } + + /* Issuer. */ + { + gnutls_datum_t dn; + + err = gnutls_x509_crt_get_issuer_dn3(cert, &dn, 0); + if (err == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { + addf(str, _("no issuer,")); + } else if (err < 0) { + addf(str, "unknown issuer (%s), ", + gnutls_strerror(err)); + } else { + addf(str, "issuer `%s', ", dn.data); + gnutls_free(dn.data); + } + } + + { + char serial[128]; + size_t serial_size = sizeof(serial); + + err = + gnutls_x509_crt_get_serial(cert, serial, &serial_size); + if (err >= 0) { + adds(str, "serial 0x"); + _gnutls_buffer_hexprint(str, serial, serial_size); + adds(str, ", "); + } + } + + /* Key algorithm and size. */ + { + unsigned int bits; + const char *p; + char *name = get_pk_name(cert, &bits); + if (name == NULL) + p = _("unknown"); + else + p = name; + addf(str, "%s key %d bits, ", p, bits); + gnutls_free(name); + } + + /* Signature Algorithm. */ + { + char *name = get_sign_name(cert, &err); + const char *p; + + if (name == NULL) + p = _("unknown"); + else + p = name; + + if (err != GNUTLS_SIGN_UNKNOWN && gnutls_sign_is_secure2(err, GNUTLS_SIGN_FLAG_SECURE_FOR_CERTS) == 0) + addf(str, _("signed using %s (broken!), "), p); + else + addf(str, _("signed using %s, "), p); + gnutls_free(name); + } + + /* Validity. */ + { + time_t tim; + + tim = gnutls_x509_crt_get_activation_time(cert); + { + char s[42]; + size_t max = sizeof(s); + struct tm t; + + if (gmtime_r(&tim, &t) == NULL) + addf(str, "unknown activation (%ld), ", + (unsigned long) tim); + else if (strftime + (s, max, "%Y-%m-%d %H:%M:%S UTC", + &t) == 0) + addf(str, "failed activation (%ld), ", + (unsigned long) tim); + else + addf(str, "activated `%s', ", s); + } + + tim = gnutls_x509_crt_get_expiration_time(cert); + { + char s[42]; + size_t max = sizeof(s); + struct tm t; + + if (gmtime_r(&tim, &t) == NULL) + addf(str, "unknown expiry (%ld), ", + (unsigned long) tim); + else if (strftime + (s, max, "%Y-%m-%d %H:%M:%S UTC", + &t) == 0) + addf(str, "failed expiry (%ld), ", + (unsigned long) tim); + else + addf(str, "expires `%s', ", s); + } + } + + { + int pathlen; + char *policyLanguage; + + err = gnutls_x509_crt_get_proxy(cert, NULL, + &pathlen, &policyLanguage, + NULL, NULL); + if (err == 0) { + addf(str, "proxy certificate (policy="); + if (strcmp(policyLanguage, "1.3.6.1.5.5.7.21.1") == + 0) + addf(str, "id-ppl-inheritALL"); + else if (strcmp + (policyLanguage, + "1.3.6.1.5.5.7.21.2") == 0) + addf(str, "id-ppl-independent"); + else + addf(str, "%s", policyLanguage); + if (pathlen >= 0) + addf(str, ", pathlen=%d), ", pathlen); + else + addf(str, "), "); + gnutls_free(policyLanguage); + } + } + + { + unsigned char buffer[MAX_HASH_SIZE]; + size_t size = sizeof(buffer); + + err = gnutls_x509_crt_get_key_id(cert, GNUTLS_KEYID_USE_SHA256, + buffer, &size); + if (err >= 0) { + addf(str, "pin-sha256=\""); + _gnutls_buffer_base64print(str, buffer, size); + adds(str, "\""); + } + } + +} + +/** + * gnutls_x509_crt_print: + * @cert: The data to be printed + * @format: Indicate the format to use + * @out: Newly allocated datum with null terminated string. + * + * This function will pretty print a X.509 certificate, suitable for + * display to a human. + * + * If the format is %GNUTLS_CRT_PRINT_FULL then all fields of the + * certificate will be output, on multiple lines. The + * %GNUTLS_CRT_PRINT_ONELINE format will generate one line with some + * selected fields, which is useful for logging purposes. + * + * The output @out needs to be deallocated using gnutls_free(). + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int +gnutls_x509_crt_print(gnutls_x509_crt_t cert, + gnutls_certificate_print_formats_t format, + gnutls_datum_t * out) +{ + gnutls_buffer_st str; + int ret; + + if (format == GNUTLS_CRT_PRINT_COMPACT) { + _gnutls_buffer_init(&str); + + print_oneline(&str, cert); + + ret = _gnutls_buffer_append_data(&str, "\n", 1); + if (ret < 0) + return gnutls_assert_val(ret); + + print_keyid(&str, cert); + + return _gnutls_buffer_to_datum(&str, out, 1); + } else if (format == GNUTLS_CRT_PRINT_ONELINE) { + _gnutls_buffer_init(&str); + + print_oneline(&str, cert); + + return _gnutls_buffer_to_datum(&str, out, 1); + } else { + _gnutls_buffer_init(&str); + + _gnutls_buffer_append_str(&str, + _ + ("X.509 Certificate Information:\n")); + + print_cert(&str, cert, format); + + _gnutls_buffer_append_str(&str, _("Other Information:\n")); + + print_other(&str, cert, format); + + return _gnutls_buffer_to_datum(&str, out, 1); + } +} + +static void +print_crl(gnutls_buffer_st * str, gnutls_x509_crl_t crl, int notsigned) +{ + /* Version. */ + { + int version = gnutls_x509_crl_get_version(crl); + if (version < 0) + addf(str, "error: get_version: %s\n", + gnutls_strerror(version)); + else + addf(str, _("\tVersion: %d\n"), version); + } + + /* Issuer. */ + if (!notsigned) { + gnutls_datum_t dn; + int err; + + err = gnutls_x509_crl_get_issuer_dn3(crl, &dn, 0); + if (err == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { + addf(str, _("\tIssuer:\n")); + } else if (err < 0) { + addf(str, "error: get_issuer_dn: %s\n", + gnutls_strerror(err)); + } else { + addf(str, _("\tIssuer: %s\n"), dn.data); + gnutls_free(dn.data); + } + } + + /* Validity. */ + { + time_t tim; + + adds(str, _("\tUpdate dates:\n")); + + tim = gnutls_x509_crl_get_this_update(crl); + { + char s[42]; + size_t max = sizeof(s); + struct tm t; + + if (gmtime_r(&tim, &t) == NULL) + addf(str, "error: gmtime_r (%ld)\n", + (unsigned long) tim); + else if (strftime + (s, max, "%a %b %d %H:%M:%S UTC %Y", + &t) == 0) + addf(str, "error: strftime (%ld)\n", + (unsigned long) tim); + else + addf(str, _("\t\tIssued: %s\n"), s); + } + + tim = gnutls_x509_crl_get_next_update(crl); + { + char s[42]; + size_t max = sizeof(s); + struct tm t; + + if (tim == -1) + addf(str, "\t\tNo next update time.\n"); + else if (gmtime_r(&tim, &t) == NULL) + addf(str, "error: gmtime_r (%ld)\n", + (unsigned long) tim); + else if (strftime + (s, max, "%a %b %d %H:%M:%S UTC %Y", + &t) == 0) + addf(str, "error: strftime (%ld)\n", + (unsigned long) tim); + else + addf(str, _("\t\tNext at: %s\n"), s); + } + } + + /* Extensions. */ + if (gnutls_x509_crl_get_version(crl) >= 2) { + size_t i; + int err = 0; + int aki_idx = 0; + int crl_nr = 0; + + for (i = 0;; i++) { + char oid[MAX_OID_SIZE] = ""; + size_t sizeof_oid = sizeof(oid); + unsigned int critical; + + err = gnutls_x509_crl_get_extension_info(crl, i, + oid, + &sizeof_oid, + &critical); + if (err == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) + break; + if (err < 0) { + addf(str, + "error: get_extension_info: %s\n", + gnutls_strerror(err)); + break; + } + + if (i == 0) + adds(str, _("\tExtensions:\n")); + + if (strcmp(oid, "2.5.29.20") == 0) { + char nr[128]; + size_t nr_size = sizeof(nr); + + if (crl_nr) { + addf(str, + "warning: more than one CRL number\n"); + } + + err = + gnutls_x509_crl_get_number(crl, nr, + &nr_size, + &critical); + + addf(str, _("\t\tCRL Number (%s): "), + critical ? _("critical") : + _("not critical")); + + if (err < 0) + addf(str, + "error: get_number: %s\n", + gnutls_strerror(err)); + else { + _gnutls_buffer_hexprint(str, nr, + nr_size); + addf(str, "\n"); + } + + crl_nr++; + } else if (strcmp(oid, "2.5.29.35") == 0) { + gnutls_datum_t der; + + if (aki_idx) { + addf(str, + "warning: more than one AKI extension\n"); + } + + addf(str, + _ + ("\t\tAuthority Key Identifier (%s):\n"), + critical ? _("critical") : + _("not critical")); + + err = gnutls_x509_crl_get_extension_data2(crl, i, &der); + if (err < 0) { + addf(str, + "error: get_extension_data2: %s\n", + gnutls_strerror(err)); + continue; + } + print_aki(str, &der); + gnutls_free(der.data); + + aki_idx++; + } else { + gnutls_datum_t der; + + addf(str, + _("\t\tUnknown extension %s (%s):\n"), + oid, + critical ? _("critical") : + _("not critical")); + + err = + gnutls_x509_crl_get_extension_data2(crl, + i, + &der); + if (err < 0) { + addf(str, + "error: get_extension_data2: %s\n", + gnutls_strerror(err)); + continue; + } + + adds(str, _("\t\t\tASCII: ")); + _gnutls_buffer_asciiprint(str, (char*)der.data, der.size); + adds(str, "\n"); + + adds(str, _("\t\t\tHexdump: ")); + _gnutls_buffer_hexprint(str, der.data, der.size); + adds(str, "\n"); + + gnutls_free(der.data); + } + } + } + + + /* Revoked certificates. */ + { + int num = gnutls_x509_crl_get_crt_count(crl); + gnutls_x509_crl_iter_t iter = NULL; + int j; + + if (num) + addf(str, _("\tRevoked certificates (%d):\n"), + num); + else + adds(str, _("\tNo revoked certificates.\n")); + + for (j = 0; j < num; j++) { + unsigned char serial[128]; + size_t serial_size = sizeof(serial); + int err; + time_t tim; + + err = + gnutls_x509_crl_iter_crt_serial(crl, &iter, serial, + &serial_size, + &tim); + if (err < 0) { + addf(str, "error: iter_crt_serial: %s\n", + gnutls_strerror(err)); + break; + } else { + char s[42]; + size_t max = sizeof(s); + struct tm t; + + adds(str, _("\t\tSerial Number (hex): ")); + _gnutls_buffer_hexprint(str, serial, + serial_size); + adds(str, "\n"); + + if (gmtime_r(&tim, &t) == NULL) + addf(str, + "error: gmtime_r (%ld)\n", + (unsigned long) tim); + else if (strftime + (s, max, + "%a %b %d %H:%M:%S UTC %Y", + &t) == 0) + addf(str, + "error: strftime (%ld)\n", + (unsigned long) tim); + else + addf(str, + _("\t\tRevoked at: %s\n"), s); + } + } + gnutls_x509_crl_iter_deinit(iter); + } + + /* Signature. */ + if (!notsigned) { + int err; + size_t size = 0; + char *buffer = NULL; + char *name; + const char *p; + + name = crl_get_sign_name(crl, &err); + if (name == NULL) + p = _("unknown"); + else + p = name; + + addf(str, _("\tSignature Algorithm: %s\n"), p); + gnutls_free(name); + + if (err != GNUTLS_SIGN_UNKNOWN && gnutls_sign_is_secure2(err, GNUTLS_SIGN_FLAG_SECURE_FOR_CERTS) == 0) { + adds(str, + _("warning: signed using a broken signature " + "algorithm that can be forged.\n")); + } + + err = gnutls_x509_crl_get_signature(crl, buffer, &size); + if (err != GNUTLS_E_SHORT_MEMORY_BUFFER) { + addf(str, "error: get_signature: %s\n", + gnutls_strerror(err)); + return; + } + + buffer = gnutls_malloc(size); + if (!buffer) { + addf(str, "error: malloc: %s\n", + gnutls_strerror(GNUTLS_E_MEMORY_ERROR)); + return; + } + + err = gnutls_x509_crl_get_signature(crl, buffer, &size); + if (err < 0) { + gnutls_free(buffer); + addf(str, "error: get_signature2: %s\n", + gnutls_strerror(err)); + return; + } + + adds(str, _("\tSignature:\n")); + _gnutls_buffer_hexdump(str, buffer, size, "\t\t"); + + gnutls_free(buffer); + } +} + +/** + * gnutls_x509_crl_print: + * @crl: The data to be printed + * @format: Indicate the format to use + * @out: Newly allocated datum with null terminated string. + * + * This function will pretty print a X.509 certificate revocation + * list, suitable for display to a human. + * + * The output @out needs to be deallocated using gnutls_free(). + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int +gnutls_x509_crl_print(gnutls_x509_crl_t crl, + gnutls_certificate_print_formats_t format, + gnutls_datum_t * out) +{ + gnutls_buffer_st str; + + _gnutls_buffer_init(&str); + + _gnutls_buffer_append_str + (&str, _("X.509 Certificate Revocation List Information:\n")); + + print_crl(&str, crl, format == GNUTLS_CRT_PRINT_UNSIGNED_FULL); + + return _gnutls_buffer_to_datum(&str, out, 1); +} + +static int +print_crq_sig_params(gnutls_buffer_st * str, gnutls_x509_crq_t crt, + gnutls_certificate_print_formats_t format) +{ + int ret; + gnutls_pk_algorithm_t pk; + gnutls_x509_spki_st params; + gnutls_sign_algorithm_t sign; + + sign = gnutls_x509_crq_get_signature_algorithm(crt); + pk = gnutls_sign_get_pk_algorithm(sign); + if (pk == GNUTLS_PK_RSA_PSS) { + ret = _gnutls_x509_read_sign_params(crt->crq, + "signatureAlgorithm", + ¶ms); + if (ret < 0) { + addf(str, "error: read_pss_params: %s\n", + gnutls_strerror(ret)); + } else + addf(str, "\t\tSalt Length: %d\n", params.salt_size); + } + + return 0; +} + +static int +print_crq_pubkey(gnutls_buffer_st * str, gnutls_x509_crq_t crq, + gnutls_certificate_print_formats_t format) +{ + gnutls_pubkey_t pubkey; + gnutls_x509_spki_st params; + int ret; + + ret = _gnutls_x509_crq_read_spki_params(crq, ¶ms); + if (ret < 0) + return ret; + + ret = gnutls_pubkey_init(&pubkey); + if (ret < 0) + return ret; + + ret = gnutls_pubkey_import_x509_crq(pubkey, crq, 0); + if (ret < 0) + goto cleanup; + + print_pubkey(str, _("Subject "), pubkey, ¶ms, format); + ret = 0; + + cleanup: + gnutls_pubkey_deinit(pubkey); + + if (ret < 0) { /* print only name */ + const char *p; + char *name = crq_get_pk_name(crq); + if (name == NULL) + p = _("unknown"); + else + p = name; + + addf(str, "\tSubject Public Key Algorithm: %s\n", p); + gnutls_free(name); + ret = 0; + } + + return ret; +} + +static void +print_crq(gnutls_buffer_st * str, gnutls_x509_crq_t cert, + gnutls_certificate_print_formats_t format) +{ + /* Version. */ + { + int version = gnutls_x509_crq_get_version(cert); + if (version < 0) + addf(str, "error: get_version: %s\n", + gnutls_strerror(version)); + else + addf(str, _("\tVersion: %d\n"), version); + } + + /* Subject */ + { + gnutls_datum_t dn; + int err; + + err = gnutls_x509_crq_get_dn3(cert, &dn, 0); + if (err == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { + addf(str, _("\tSubject:\n")); + } else if (err < 0) { + addf(str, "error: get_dn: %s\n", + gnutls_strerror(err)); + } else { + addf(str, _("\tSubject: %s\n"), dn.data); + gnutls_free(dn.data); + } + } + + { + char *name; + const char *p; + + print_crq_pubkey(str, cert, format); + + name = crq_get_sign_name(cert); + if (name == NULL) + p = _("unknown"); + else + p = name; + + addf(str, _("\tSignature Algorithm: %s\n"), p); + + gnutls_free(name); + + print_crq_sig_params(str, cert, format); + } + + /* parse attributes */ + { + size_t i; + int err = 0; + int extensions = 0; + int challenge = 0; + + for (i = 0;; i++) { + char oid[MAX_OID_SIZE] = ""; + size_t sizeof_oid = sizeof(oid); + + err = + gnutls_x509_crq_get_attribute_info(cert, i, + oid, + &sizeof_oid); + if (err == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) + break; + if (err < 0) { + addf(str, + "error: get_extension_info: %s\n", + gnutls_strerror(err)); + break; + } + + if (i == 0) + adds(str, _("\tAttributes:\n")); + + if (strcmp(oid, "1.2.840.113549.1.9.14") == 0) { + cert_type_t ccert; + + if (extensions) { + addf(str, + "warning: more than one extensionsRequest\n"); + } + + ccert.crq = cert; + print_extensions(str, "\t", TYPE_CRQ, + ccert); + + extensions++; + } else if (strcmp(oid, "1.2.840.113549.1.9.7") == + 0) { + char *pass; + size_t size; + + if (challenge) { + adds(str, + "warning: more than one Challenge password attribute\n"); + } + + err = + gnutls_x509_crq_get_challenge_password + (cert, NULL, &size); + if (err < 0 + && err != + GNUTLS_E_SHORT_MEMORY_BUFFER) { + addf(str, + "error: get_challenge_password: %s\n", + gnutls_strerror(err)); + continue; + } + + size++; + + pass = gnutls_malloc(size); + if (!pass) { + addf(str, "error: malloc: %s\n", + gnutls_strerror + (GNUTLS_E_MEMORY_ERROR)); + continue; + } + + err = + gnutls_x509_crq_get_challenge_password + (cert, pass, &size); + if (err < 0) + addf(str, + "error: get_challenge_password: %s\n", + gnutls_strerror(err)); + else + addf(str, + _ + ("\t\tChallenge password: %s\n"), + pass); + + gnutls_free(pass); + + challenge++; + } else { + char *buffer; + size_t extlen = 0; + + addf(str, _("\t\tUnknown attribute %s:\n"), + oid); + + err = + gnutls_x509_crq_get_attribute_data + (cert, i, NULL, &extlen); + if (err < 0) { + addf(str, + "error: get_attribute_data: %s\n", + gnutls_strerror(err)); + continue; + } + + buffer = gnutls_malloc(extlen); + if (!buffer) { + addf(str, "error: malloc: %s\n", + gnutls_strerror + (GNUTLS_E_MEMORY_ERROR)); + continue; + } + + err = + gnutls_x509_crq_get_attribute_data + (cert, i, buffer, &extlen); + if (err < 0) { + gnutls_free(buffer); + addf(str, + "error: get_attribute_data2: %s\n", + gnutls_strerror(err)); + continue; + } + + adds(str, _("\t\t\tASCII: ")); + _gnutls_buffer_asciiprint(str, buffer, + extlen); + adds(str, "\n"); + + adds(str, _("\t\t\tHexdump: ")); + _gnutls_buffer_hexprint(str, buffer, + extlen); + adds(str, "\n"); + + gnutls_free(buffer); + } + } + } +} + +static void print_crq_other(gnutls_buffer_st * str, gnutls_x509_crq_t crq) +{ + int ret; + + /* on unknown public key algorithms don't print the key ID */ + ret = gnutls_x509_crq_get_pk_algorithm(crq, NULL); + if (ret < 0) + return; + + print_obj_id(str, "\t", crq, (get_id_func*)gnutls_x509_crq_get_key_id); +} + +/** + * gnutls_x509_crq_print: + * @crq: The data to be printed + * @format: Indicate the format to use + * @out: Newly allocated datum with null terminated string. + * + * This function will pretty print a certificate request, suitable for + * display to a human. + * + * The output @out needs to be deallocated using gnutls_free(). + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 2.8.0 + **/ +int +gnutls_x509_crq_print(gnutls_x509_crq_t crq, + gnutls_certificate_print_formats_t format, + gnutls_datum_t * out) +{ + gnutls_buffer_st str; + + _gnutls_buffer_init(&str); + + _gnutls_buffer_append_str + (&str, _("PKCS #10 Certificate Request Information:\n")); + + print_crq(&str, crq, format); + + _gnutls_buffer_append_str(&str, _("Other Information:\n")); + + print_crq_other(&str, crq); + + return _gnutls_buffer_to_datum(&str, out, 1); +} + +static void +print_pubkey_other(gnutls_buffer_st * str, gnutls_pubkey_t pubkey, + gnutls_certificate_print_formats_t format) +{ + int ret; + unsigned int usage; + + ret = gnutls_pubkey_get_key_usage(pubkey, &usage); + if (ret < 0) { + addf(str, "error: get_key_usage: %s\n", + gnutls_strerror(ret)); + return; + } + + adds(str, "\n"); + if (pubkey->key_usage) { + adds(str, _("Public Key Usage:\n")); + print_key_usage2(str, "\t", pubkey->key_usage); + } + + /* on unknown public key algorithms don't print the key ID */ + ret = gnutls_pubkey_get_pk_algorithm(pubkey, NULL); + if (ret < 0) + return; + + print_obj_id(str, "", pubkey, (get_id_func*)gnutls_pubkey_get_key_id); +} + +/** + * gnutls_pubkey_print: + * @pubkey: The data to be printed + * @format: Indicate the format to use + * @out: Newly allocated datum with null terminated string. + * + * This function will pretty print public key information, suitable for + * display to a human. + * + * Only %GNUTLS_CRT_PRINT_FULL and %GNUTLS_CRT_PRINT_FULL_NUMBERS + * are implemented. + * + * The output @out needs to be deallocated using gnutls_free(). + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 3.1.5 + **/ +int +gnutls_pubkey_print(gnutls_pubkey_t pubkey, + gnutls_certificate_print_formats_t format, + gnutls_datum_t * out) +{ + gnutls_buffer_st str; + + _gnutls_buffer_init(&str); + + _gnutls_buffer_append_str(&str, _("Public Key Information:\n")); + + print_pubkey(&str, "", pubkey, NULL, format); + print_pubkey_other(&str, pubkey, format); + + return _gnutls_buffer_to_datum(&str, out, 1); +} + +/** + * gnutls_x509_ext_print: + * @exts: The data to be printed + * @exts_size: the number of available structures + * @format: Indicate the format to use + * @out: Newly allocated datum with null terminated string. + * + * This function will pretty print X.509 certificate extensions, + * suitable for display to a human. + * + * The output @out needs to be deallocated using gnutls_free(). + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int +gnutls_x509_ext_print(gnutls_x509_ext_st *exts, unsigned int exts_size, + gnutls_certificate_print_formats_t format, + gnutls_datum_t * out) +{ + gnutls_buffer_st str; + struct ext_indexes_st idx; + unsigned i; + + memset(&idx, 0, sizeof(idx)); + _gnutls_buffer_init(&str); + + for (i=0;i<exts_size;i++) + print_extension(&str, "", &idx, (char*)exts[i].oid, exts[i].critical, &exts[i].data); + + return _gnutls_buffer_to_datum(&str, out, 1); +} diff --git a/lib/x509/pkcs12.c b/lib/x509/pkcs12.c new file mode 100644 index 0000000..11b9da3 --- /dev/null +++ b/lib/x509/pkcs12.c @@ -0,0 +1,2063 @@ +/* + * Copyright (C) 2003-2012 Free Software Foundation, Inc. + * Copyright (C) 2012 Nikos Mavrogiannopoulos + * Copyright (C) 2017 Red Hat, Inc. + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GnuTLS. + * + * The GnuTLS is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/> + * + */ + +/* Functions that relate on PKCS12 packet parsing. + */ + +#include "gnutls_int.h" +#include <libtasn1.h> + +#include <datum.h> +#include <global.h> +#include "errors.h" +#include <num.h> +#include <common.h> +#include <x509_b64.h> +#include "x509_int.h" +#include "pkcs7_int.h" +#include <random.h> +#include "intprops.h" + + +/* Decodes the PKCS #12 auth_safe, and returns the allocated raw data, + * which holds them. Returns an asn1_node of authenticatedSafe. + */ +static int +_decode_pkcs12_auth_safe(asn1_node pkcs12, asn1_node * authen_safe, + gnutls_datum_t * raw) +{ + char oid[MAX_OID_SIZE]; + asn1_node c2 = NULL; + gnutls_datum_t auth_safe = { NULL, 0 }; + int len, result; + char error_str[ASN1_MAX_ERROR_DESCRIPTION_SIZE]; + + len = sizeof(oid) - 1; + result = + asn1_read_value(pkcs12, "authSafe.contentType", oid, &len); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + if (strcmp(oid, DATA_OID) != 0) { + gnutls_assert(); + _gnutls_debug_log("Unknown PKCS12 Content OID '%s'\n", + oid); + return GNUTLS_E_UNKNOWN_PKCS_CONTENT_TYPE; + } + + /* Step 1. Read the content data + */ + + result = + _gnutls_x509_read_string(pkcs12, "authSafe.content", + &auth_safe, ASN1_ETYPE_OCTET_STRING, 1); + if (result < 0) { + gnutls_assert(); + goto cleanup; + } + + /* Step 2. Extract the authenticatedSafe. + */ + + if ((result = asn1_create_element + (_gnutls_get_pkix(), "PKIX1.pkcs-12-AuthenticatedSafe", + &c2)) != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + result = + asn1_der_decoding(&c2, auth_safe.data, auth_safe.size, + error_str); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + _gnutls_debug_log("DER error: %s\n", error_str); + result = _gnutls_asn2err(result); + goto cleanup; + } + + if (raw == NULL) { + _gnutls_free_datum(&auth_safe); + } else { + raw->data = auth_safe.data; + raw->size = auth_safe.size; + } + + if (authen_safe) + *authen_safe = c2; + else + asn1_delete_structure(&c2); + + return 0; + + cleanup: + if (c2) + asn1_delete_structure(&c2); + _gnutls_free_datum(&auth_safe); + return result; +} + +static int pkcs12_reinit(gnutls_pkcs12_t pkcs12) +{ +int result; + + if (pkcs12->pkcs12) + asn1_delete_structure(&pkcs12->pkcs12); + + result = asn1_create_element(_gnutls_get_pkix(), + "PKIX1.pkcs-12-PFX", + &pkcs12->pkcs12); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + return 0; +} + +/** + * gnutls_pkcs12_init: + * @pkcs12: A pointer to the type to be initialized + * + * This function will initialize a PKCS12 type. PKCS12 structures + * usually contain lists of X.509 Certificates and X.509 Certificate + * revocation lists. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int gnutls_pkcs12_init(gnutls_pkcs12_t * pkcs12) +{ + *pkcs12 = gnutls_calloc(1, sizeof(gnutls_pkcs12_int)); + + if (*pkcs12) { + int result = pkcs12_reinit(*pkcs12); + if (result < 0) { + gnutls_assert(); + gnutls_free(*pkcs12); + return result; + } + return 0; /* success */ + } + return GNUTLS_E_MEMORY_ERROR; +} + +/** + * gnutls_pkcs12_deinit: + * @pkcs12: The type to be initialized + * + * This function will deinitialize a PKCS12 type. + **/ +void gnutls_pkcs12_deinit(gnutls_pkcs12_t pkcs12) +{ + if (!pkcs12) + return; + + if (pkcs12->pkcs12) + asn1_delete_structure(&pkcs12->pkcs12); + + gnutls_free(pkcs12); +} + +/** + * gnutls_pkcs12_import: + * @pkcs12: The data to store the parsed PKCS12. + * @data: The DER or PEM encoded PKCS12. + * @format: One of DER or PEM + * @flags: an ORed sequence of gnutls_privkey_pkcs8_flags + * + * This function will convert the given DER or PEM encoded PKCS12 + * to the native gnutls_pkcs12_t format. The output will be stored in 'pkcs12'. + * + * If the PKCS12 is PEM encoded it should have a header of "PKCS12". + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int +gnutls_pkcs12_import(gnutls_pkcs12_t pkcs12, + const gnutls_datum_t * data, + gnutls_x509_crt_fmt_t format, unsigned int flags) +{ + int result = 0, need_free = 0; + gnutls_datum_t _data; + char error_str[ASN1_MAX_ERROR_DESCRIPTION_SIZE]; + + _data.data = data->data; + _data.size = data->size; + + if (pkcs12 == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + /* If the PKCS12 is in PEM format then decode it + */ + if (format == GNUTLS_X509_FMT_PEM) { + result = + _gnutls_fbase64_decode(PEM_PKCS12, data->data, + data->size, &_data); + + if (result < 0) { + gnutls_assert(); + return result; + } + + need_free = 1; + } + + if (pkcs12->expanded) { + result = pkcs12_reinit(pkcs12); + if (result < 0) { + gnutls_assert(); + goto cleanup; + } + } + pkcs12->expanded = 1; + + result = + asn1_der_decoding(&pkcs12->pkcs12, _data.data, _data.size, + error_str); + if (result != ASN1_SUCCESS) { + result = _gnutls_asn2err(result); + _gnutls_debug_log("DER error: %s\n", error_str); + gnutls_assert(); + goto cleanup; + } + + if (need_free) + _gnutls_free_datum(&_data); + + return 0; + + cleanup: + if (need_free) + _gnutls_free_datum(&_data); + return result; +} + + +/** + * gnutls_pkcs12_export: + * @pkcs12: A pkcs12 type + * @format: the format of output params. One of PEM or DER. + * @output_data: will contain a structure PEM or DER encoded + * @output_data_size: holds the size of output_data (and will be + * replaced by the actual size of parameters) + * + * This function will export the pkcs12 structure to DER or PEM format. + * + * If the buffer provided is not long enough to hold the output, then + * *output_data_size will be updated and GNUTLS_E_SHORT_MEMORY_BUFFER + * will be returned. + * + * If the structure is PEM encoded, it will have a header + * of "BEGIN PKCS12". + * + * Returns: In case of failure a negative error code will be + * returned, and 0 on success. + **/ +int +gnutls_pkcs12_export(gnutls_pkcs12_t pkcs12, + gnutls_x509_crt_fmt_t format, void *output_data, + size_t * output_data_size) +{ + int ret; + + if (pkcs12 == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + ret = _gnutls_x509_export_int(pkcs12->pkcs12, format, PEM_PKCS12, + output_data, output_data_size); + + if (ret < 0) { + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR); + } else { + /* PKCS#12 export is always non-approved, because the MAC + * calculation involves non-approved KDF (PKCS#12 KDF) and + * without MAC the protection is insufficient. + */ + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_NOT_APPROVED); + } + return ret; +} + +/** + * gnutls_pkcs12_export2: + * @pkcs12: A pkcs12 type + * @format: the format of output params. One of PEM or DER. + * @out: will contain a structure PEM or DER encoded + * + * This function will export the pkcs12 structure to DER or PEM format. + * + * The output buffer is allocated using gnutls_malloc(). + * + * If the structure is PEM encoded, it will have a header + * of "BEGIN PKCS12". + * + * Returns: In case of failure a negative error code will be + * returned, and 0 on success. + * + * Since: 3.1.3 + **/ +int +gnutls_pkcs12_export2(gnutls_pkcs12_t pkcs12, + gnutls_x509_crt_fmt_t format, gnutls_datum_t * out) +{ + int ret; + + if (pkcs12 == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + ret = _gnutls_x509_export_int2(pkcs12->pkcs12, format, PEM_PKCS12, + out); + if (ret < 0) { + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR); + } else { + /* PKCS#12 export is always non-approved, because the MAC + * calculation involves non-approved KDF (PKCS#12 KDF) and + * without MAC the protection is insufficient. + */ + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_NOT_APPROVED); + } + return ret; +} + +static int oid2bag(const char *oid) +{ + if (strcmp(oid, BAG_PKCS8_KEY) == 0) + return GNUTLS_BAG_PKCS8_KEY; + if (strcmp(oid, BAG_PKCS8_ENCRYPTED_KEY) == 0) + return GNUTLS_BAG_PKCS8_ENCRYPTED_KEY; + if (strcmp(oid, BAG_CERTIFICATE) == 0) + return GNUTLS_BAG_CERTIFICATE; + if (strcmp(oid, BAG_CRL) == 0) + return GNUTLS_BAG_CRL; + if (strcmp(oid, BAG_SECRET) == 0) + return GNUTLS_BAG_SECRET; + + return GNUTLS_BAG_UNKNOWN; +} + +static const char *bag_to_oid(int bag) +{ + switch (bag) { + case GNUTLS_BAG_PKCS8_KEY: + return BAG_PKCS8_KEY; + case GNUTLS_BAG_PKCS8_ENCRYPTED_KEY: + return BAG_PKCS8_ENCRYPTED_KEY; + case GNUTLS_BAG_CERTIFICATE: + return BAG_CERTIFICATE; + case GNUTLS_BAG_CRL: + return BAG_CRL; + case GNUTLS_BAG_SECRET: + return BAG_SECRET; + } + return NULL; +} + +/* Decodes the SafeContents, and puts the output in + * the given bag. + */ +int +_pkcs12_decode_safe_contents(const gnutls_datum_t * content, + gnutls_pkcs12_bag_t bag) +{ + char oid[MAX_OID_SIZE], root[MAX_NAME_SIZE]; + asn1_node c2 = NULL; + int len, result; + int bag_type; + gnutls_datum_t attr_val; + gnutls_datum_t t; + int count = 0, attributes, j; + unsigned i; + + /* Step 1. Extract the SEQUENCE. + */ + + if ((result = asn1_create_element + (_gnutls_get_pkix(), "PKIX1.pkcs-12-SafeContents", + &c2)) != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + result = + asn1_der_decoding(&c2, content->data, content->size, NULL); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + /* Count the number of bags + */ + result = asn1_number_of_elements(c2, "", &count); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + bag->bag_elements = MIN(MAX_BAG_ELEMENTS, count); + + for (i = 0; i < bag->bag_elements; i++) { + + snprintf(root, sizeof(root), "?%u.bagId", i + 1); + + len = sizeof(oid); + result = asn1_read_value(c2, root, oid, &len); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + /* Read the Bag type + */ + bag_type = oid2bag(oid); + + if (bag_type < 0) { + gnutls_assert(); + goto cleanup; + } + + /* Read the Bag Value + */ + + snprintf(root, sizeof(root), "?%u.bagValue", i + 1); + + result = + _gnutls_x509_read_value(c2, root, + &bag->element[i].data); + if (result < 0) { + gnutls_assert(); + goto cleanup; + } + + if (bag_type == GNUTLS_BAG_CERTIFICATE + || bag_type == GNUTLS_BAG_CRL + || bag_type == GNUTLS_BAG_SECRET) { + gnutls_datum_t tmp = bag->element[i].data; + bag->element[i].data.data = NULL; + bag->element[i].data.size = 0; + + result = + _pkcs12_decode_crt_bag(bag_type, &tmp, + &bag->element[i].data); + _gnutls_free_datum(&tmp); + if (result < 0) { + gnutls_assert(); + goto cleanup; + } + } + + /* read the bag attributes + */ + snprintf(root, sizeof(root), "?%u.bagAttributes", i + 1); + + result = asn1_number_of_elements(c2, root, &attributes); + if (result != ASN1_SUCCESS + && result != ASN1_ELEMENT_NOT_FOUND) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + if (attributes < 0) + attributes = 1; + + if (result != ASN1_ELEMENT_NOT_FOUND) + for (j = 0; j < attributes; j++) { + + snprintf(root, sizeof(root), + "?%u.bagAttributes.?%d", i + 1, + j + 1); + + result = + _gnutls_x509_decode_and_read_attribute + (c2, root, oid, sizeof(oid), &attr_val, + 1, 0); + + if (result < 0) { + gnutls_assert(); + continue; /* continue in case we find some known attributes */ + } + + if (strcmp(oid, KEY_ID_OID) == 0) { + result = + _gnutls_x509_decode_string + (ASN1_ETYPE_OCTET_STRING, + attr_val.data, attr_val.size, + &t, 1); + + _gnutls_free_datum(&attr_val); + if (result < 0) { + gnutls_assert(); + _gnutls_debug_log + ("Error decoding PKCS12 Bag Attribute OID '%s'\n", + oid); + continue; + } + + _gnutls_free_datum(&bag->element[i].local_key_id); + bag->element[i].local_key_id.data = t.data; + bag->element[i].local_key_id.size = t.size; + } else if (strcmp(oid, FRIENDLY_NAME_OID) == 0 && bag->element[i].friendly_name == NULL) { + result = + _gnutls_x509_decode_string + (ASN1_ETYPE_BMP_STRING, + attr_val.data, attr_val.size, + &t, 1); + + _gnutls_free_datum(&attr_val); + if (result < 0) { + gnutls_assert(); + _gnutls_debug_log + ("Error decoding PKCS12 Bag Attribute OID '%s'\n", + oid); + continue; + } + + gnutls_free(bag->element[i].friendly_name); + bag->element[i].friendly_name = (char *) t.data; + } else { + _gnutls_free_datum(&attr_val); + _gnutls_debug_log + ("Unknown PKCS12 Bag Attribute OID '%s'\n", + oid); + } + } + + + bag->element[i].type = bag_type; + + } + + result = 0; + + cleanup: + if (c2) + asn1_delete_structure(&c2); + return result; + +} + + +static int +_parse_safe_contents(asn1_node sc, const char *sc_name, + gnutls_pkcs12_bag_t bag) +{ + gnutls_datum_t content = { NULL, 0 }; + int result; + + /* Step 1. Extract the content. + */ + + result = + _gnutls_x509_read_string(sc, sc_name, &content, + ASN1_ETYPE_OCTET_STRING, 1); + if (result < 0) { + gnutls_assert(); + goto cleanup; + } + + result = _pkcs12_decode_safe_contents(&content, bag); + if (result < 0) { + gnutls_assert(); + goto cleanup; + } + + _gnutls_free_datum(&content); + + return 0; + + cleanup: + _gnutls_free_datum(&content); + return result; +} + + +/** + * gnutls_pkcs12_get_bag: + * @pkcs12: A pkcs12 type + * @indx: contains the index of the bag to extract + * @bag: An initialized bag, where the contents of the bag will be copied + * + * This function will return a Bag from the PKCS12 structure. + * + * After the last Bag has been read + * %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE will be returned. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int +gnutls_pkcs12_get_bag(gnutls_pkcs12_t pkcs12, + int indx, gnutls_pkcs12_bag_t bag) +{ + asn1_node c2 = NULL; + int result, len; + char root2[MAX_NAME_SIZE]; + char oid[MAX_OID_SIZE]; + + if (pkcs12 == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + /* Step 1. decode the data. + */ + result = _decode_pkcs12_auth_safe(pkcs12->pkcs12, &c2, NULL); + if (result < 0) { + gnutls_assert(); + return result; + } + + /* Step 2. Parse the AuthenticatedSafe + */ + + snprintf(root2, sizeof(root2), "?%d.contentType", indx + 1); + + len = sizeof(oid) - 1; + result = asn1_read_value(c2, root2, oid, &len); + + if (result == ASN1_ELEMENT_NOT_FOUND) { + result = GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + goto cleanup; + } + + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + /* Not encrypted Bag + */ + + snprintf(root2, sizeof(root2), "?%d.content", indx + 1); + + if (strcmp(oid, DATA_OID) == 0) { + result = _parse_safe_contents(c2, root2, bag); + goto cleanup; + } + + /* ENC_DATA_OID needs decryption */ + + result = _gnutls_x509_read_value(c2, root2, &bag->element[0].data); + if (result < 0) { + gnutls_assert(); + goto cleanup; + } + + bag->element[0].type = GNUTLS_BAG_ENCRYPTED; + bag->bag_elements = 1; + + result = 0; + + cleanup: + if (c2) + asn1_delete_structure(&c2); + return result; +} + +/* Creates an empty PFX structure for the PKCS12 structure. + */ +static int create_empty_pfx(asn1_node pkcs12) +{ + uint8_t three = 3; + int result; + asn1_node c2 = NULL; + + /* Use version 3 + */ + result = asn1_write_value(pkcs12, "version", &three, 1); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + /* Write the content type of the data + */ + result = + asn1_write_value(pkcs12, "authSafe.contentType", DATA_OID, 1); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + /* Check if the authenticatedSafe content is empty, and encode a + * null one in that case. + */ + + if ((result = asn1_create_element + (_gnutls_get_pkix(), "PKIX1.pkcs-12-AuthenticatedSafe", + &c2)) != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + result = + _gnutls_x509_der_encode_and_copy(c2, "", pkcs12, + "authSafe.content", 1); + if (result < 0) { + gnutls_assert(); + goto cleanup; + } + asn1_delete_structure(&c2); + + return 0; + + cleanup: + asn1_delete_structure(&c2); + return result; + +} + +/** + * gnutls_pkcs12_set_bag: + * @pkcs12: should contain a gnutls_pkcs12_t type + * @bag: An initialized bag + * + * This function will insert a Bag into the PKCS12 structure. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int gnutls_pkcs12_set_bag(gnutls_pkcs12_t pkcs12, gnutls_pkcs12_bag_t bag) +{ + asn1_node c2 = NULL; + asn1_node safe_cont = NULL; + int result; + int enc = 0, dum = 1; + char null; + + if (pkcs12 == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + /* Step 1. Check if the pkcs12 structure is empty. In that + * case generate an empty PFX. + */ + result = + asn1_read_value(pkcs12->pkcs12, "authSafe.content", &null, + &dum); + if (result == ASN1_VALUE_NOT_FOUND) { + result = create_empty_pfx(pkcs12->pkcs12); + if (result < 0) { + gnutls_assert(); + return result; + } + } + + /* Step 2. decode the authenticatedSafe. + */ + result = _decode_pkcs12_auth_safe(pkcs12->pkcs12, &c2, NULL); + if (result < 0) { + gnutls_assert(); + return result; + } + + /* Step 3. Encode the bag elements into a SafeContents + * structure. + */ + result = _pkcs12_encode_safe_contents(bag, &safe_cont, &enc); + if (result < 0) { + gnutls_assert(); + return result; + } + + /* Step 4. Insert the encoded SafeContents into the AuthenticatedSafe + * structure. + */ + result = asn1_write_value(c2, "", "NEW", 1); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + if (enc) + result = + asn1_write_value(c2, "?LAST.contentType", ENC_DATA_OID, + 1); + else + result = + asn1_write_value(c2, "?LAST.contentType", DATA_OID, 1); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + if (enc) { + /* Encrypted packets are written directly. + */ + result = + asn1_write_value(c2, "?LAST.content", + bag->element[0].data.data, + bag->element[0].data.size); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + } else { + result = + _gnutls_x509_der_encode_and_copy(safe_cont, "", c2, + "?LAST.content", 1); + if (result < 0) { + gnutls_assert(); + goto cleanup; + } + } + + asn1_delete_structure(&safe_cont); + + + /* Step 5. Re-encode and copy the AuthenticatedSafe into the pkcs12 + * structure. + */ + result = + _gnutls_x509_der_encode_and_copy(c2, "", pkcs12->pkcs12, + "authSafe.content", 1); + if (result < 0) { + gnutls_assert(); + goto cleanup; + } + + asn1_delete_structure(&c2); + + return 0; + + cleanup: + asn1_delete_structure(&c2); + asn1_delete_structure(&safe_cont); + return result; +} + +#if ENABLE_GOST +/* + * Russian differs from PKCS#12 here. It described proprietary way + * to obtain MAC key instead of using standard mechanism. + * + * See https://wwwold.tc26.ru/standard/rs/%D0%A0%2050.1.112-2016.pdf + * section 5. + */ +static int +_gnutls_pkcs12_gost_string_to_key(gnutls_mac_algorithm_t algo, + const uint8_t * salt, + unsigned int salt_size, unsigned int iter, + const char *pass, unsigned int req_keylen, + uint8_t * keybuf) +{ + uint8_t temp[96]; + size_t temp_len = sizeof(temp); + gnutls_datum_t key; + gnutls_datum_t _salt; + int ret; + + if (iter == 0) + return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + + key.data = (void *)pass; + key.size = pass ? strlen(pass) : 0; + + _salt.data = (void *)salt; + _salt.size = salt_size; + + ret = gnutls_pbkdf2(algo, &key, &_salt, iter, temp, temp_len); + if (ret < 0) + return gnutls_assert_val(ret); + + memcpy(keybuf, temp + temp_len - req_keylen, req_keylen); + + return 0; +} +#endif + +/** + * gnutls_pkcs12_generate_mac2: + * @pkcs12: A pkcs12 type + * @mac: the MAC algorithm to use + * @pass: The password for the MAC + * + * This function will generate a MAC for the PKCS12 structure. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int gnutls_pkcs12_generate_mac2(gnutls_pkcs12_t pkcs12, gnutls_mac_algorithm_t mac, const char *pass) +{ + uint8_t salt[8], key[MAX_HASH_SIZE]; + int result; + const int iter = PKCS12_ITER_COUNT; + mac_hd_st td1; + gnutls_datum_t tmp = { NULL, 0 }; + unsigned mac_size, key_len; + uint8_t mac_out[MAX_HASH_SIZE]; + const mac_entry_st *me = mac_to_entry(mac); + + if (pkcs12 == NULL || me == NULL) + return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + + if (me->oid == NULL) + return gnutls_assert_val(GNUTLS_E_UNIMPLEMENTED_FEATURE); + + mac_size = _gnutls_mac_get_algo_len(me); + key_len = mac_size; + + /* Generate the salt. + */ + result = gnutls_rnd(GNUTLS_RND_NONCE, salt, sizeof(salt)); + if (result < 0) { + gnutls_assert(); + return result; + } + + /* Write the salt into the structure. + */ + result = + asn1_write_value(pkcs12->pkcs12, "macData.macSalt", salt, + sizeof(salt)); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + /* write the iterations + */ + + if (iter > 1) { + result = + _gnutls_x509_write_uint32(pkcs12->pkcs12, + "macData.iterations", iter); + if (result < 0) { + gnutls_assert(); + goto cleanup; + } + } + + /* Generate the key. + */ +#if ENABLE_GOST + if (me->id == GNUTLS_MAC_GOSTR_94 || + me->id == GNUTLS_MAC_STREEBOG_256 || + me->id == GNUTLS_MAC_STREEBOG_512) { + key_len = 32; + result = _gnutls_pkcs12_gost_string_to_key(me->id, + salt, + sizeof(salt), + iter, + pass, + key_len, + key); + } else +#endif + result = _gnutls_pkcs12_string_to_key(me, 3 /*MAC*/, + salt, sizeof(salt), + iter, pass, + mac_size, key); + if (result < 0) { + gnutls_assert(); + goto cleanup; + } + + /* Get the data to be MACed + */ + result = _decode_pkcs12_auth_safe(pkcs12->pkcs12, NULL, &tmp); + if (result < 0) { + gnutls_assert(); + goto cleanup; + } + + /* MAC the data + */ + result = _gnutls_mac_init(&td1, me, + key, key_len); + if (result < 0) { + gnutls_assert(); + goto cleanup; + } + + _gnutls_mac(&td1, tmp.data, tmp.size); + _gnutls_free_datum(&tmp); + + _gnutls_mac_deinit(&td1, mac_out); + + + result = + asn1_write_value(pkcs12->pkcs12, "macData.mac.digest", mac_out, + mac_size); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + result = + asn1_write_value(pkcs12->pkcs12, + "macData.mac.digestAlgorithm.parameters", + NULL, 0); + if (result != ASN1_SUCCESS && result != ASN1_ELEMENT_NOT_FOUND) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + result = + asn1_write_value(pkcs12->pkcs12, + "macData.mac.digestAlgorithm.algorithm", + me->oid, 1); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + /* _gnutls_pkcs12_string_to_key is not a FIPS approved operation */ + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_NOT_APPROVED); + return 0; + + cleanup: + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR); + _gnutls_free_datum(&tmp); + return result; +} + +/** + * gnutls_pkcs12_generate_mac: + * @pkcs12: A pkcs12 type + * @pass: The password for the MAC + * + * This function will generate a MAC for the PKCS12 structure. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int gnutls_pkcs12_generate_mac(gnutls_pkcs12_t pkcs12, const char *pass) +{ + return gnutls_pkcs12_generate_mac2(pkcs12, GNUTLS_MAC_SHA256, pass); +} + +/** + * gnutls_pkcs12_verify_mac: + * @pkcs12: should contain a gnutls_pkcs12_t type + * @pass: The password for the MAC + * + * This function will verify the MAC for the PKCS12 structure. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int gnutls_pkcs12_verify_mac(gnutls_pkcs12_t pkcs12, const char *pass) +{ + uint8_t key[MAX_HASH_SIZE]; + char oid[MAX_OID_SIZE]; + int result; + unsigned int iter; + int len; + mac_hd_st td1; + gnutls_datum_t tmp = { NULL, 0 }, salt = { + NULL, 0}; + uint8_t mac_output[MAX_HASH_SIZE]; + uint8_t mac_output_orig[MAX_HASH_SIZE]; + gnutls_mac_algorithm_t algo; + unsigned mac_len, key_len; + const mac_entry_st *entry; +#if ENABLE_GOST + int gost_retry = 0; +#endif + + if (pkcs12 == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + /* read the iterations + */ + result = + _gnutls_x509_read_uint(pkcs12->pkcs12, "macData.iterations", + &iter); + if (result < 0) { + iter = 1; /* the default */ + } + + len = sizeof(oid); + result = + asn1_read_value(pkcs12->pkcs12, "macData.mac.digestAlgorithm.algorithm", + oid, &len); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + algo = DIG_TO_MAC(gnutls_oid_to_digest(oid)); + if (algo == GNUTLS_MAC_UNKNOWN) { + unknown_mac: + gnutls_assert(); + return GNUTLS_E_UNKNOWN_HASH_ALGORITHM; + } + + entry = mac_to_entry(algo); + if (entry == NULL) + goto unknown_mac; + + mac_len = _gnutls_mac_get_algo_len(entry); + key_len = mac_len; + + /* Read the salt from the structure. + */ + result = + _gnutls_x509_read_null_value(pkcs12->pkcs12, "macData.macSalt", + &salt); + if (result < 0) { + gnutls_assert(); + goto cleanup; + } + + /* Generate the key. + */ + result = _gnutls_pkcs12_string_to_key(entry, 3 /*MAC*/, + salt.data, salt.size, + iter, pass, + key_len, key); + if (result < 0) { + gnutls_assert(); + goto cleanup; + } + + /* Get the data to be MACed + */ + result = _decode_pkcs12_auth_safe(pkcs12->pkcs12, NULL, &tmp); + if (result < 0) { + gnutls_assert(); + goto cleanup; + } + +#if ENABLE_GOST + /* GOST PKCS#12 files use either PKCS#12 scheme or proprietary + * HMAC-based scheme to generate MAC key. */ +pkcs12_try_gost: +#endif + + /* MAC the data + */ + result = _gnutls_mac_init(&td1, entry, key, key_len); + if (result < 0) { + gnutls_assert(); + goto cleanup; + } + + _gnutls_mac(&td1, tmp.data, tmp.size); + + _gnutls_mac_deinit(&td1, mac_output); + + len = sizeof(mac_output_orig); + result = + asn1_read_value(pkcs12->pkcs12, "macData.mac.digest", + mac_output_orig, &len); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + if ((unsigned)len != mac_len || + memcmp(mac_output_orig, mac_output, len) != 0) { + +#if ENABLE_GOST + /* It is possible that GOST files use proprietary + * key generation scheme */ + if (!gost_retry && + (algo == GNUTLS_MAC_GOSTR_94 || + algo == GNUTLS_MAC_STREEBOG_256 || + algo == GNUTLS_MAC_STREEBOG_512)) { + gost_retry = 1; + key_len = 32; + result = _gnutls_pkcs12_gost_string_to_key(algo, + salt.data, + salt.size, + iter, + pass, + key_len, + key); + if (result < 0) { + gnutls_assert(); + goto cleanup; + } + + goto pkcs12_try_gost; + } +#endif + + gnutls_assert(); + result = GNUTLS_E_MAC_VERIFY_FAILED; + goto cleanup; + } + + /* _gnutls_pkcs12_string_to_key is not a FIPS approved operation */ + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_NOT_APPROVED); + result = 0; + cleanup: + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR); + _gnutls_free_datum(&tmp); + _gnutls_free_datum(&salt); + return result; +} + + +static int +write_attributes(gnutls_pkcs12_bag_t bag, int elem, + asn1_node c2, const char *where) +{ + int result; + char root[128]; + + /* If the bag attributes are empty, then write + * nothing to the attribute field. + */ + if (bag->element[elem].friendly_name == NULL && + bag->element[elem].local_key_id.data == NULL) { + /* no attributes + */ + result = asn1_write_value(c2, where, NULL, 0); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + return 0; + } + + if (bag->element[elem].local_key_id.data != NULL) { + + /* Add a new Attribute + */ + result = asn1_write_value(c2, where, "NEW", 1); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + _gnutls_str_cpy(root, sizeof(root), where); + _gnutls_str_cat(root, sizeof(root), ".?LAST"); + + result = + _gnutls_x509_encode_and_write_attribute(KEY_ID_OID, c2, + root, + bag->element + [elem]. + local_key_id.data, + bag->element + [elem]. + local_key_id.size, + 1); + if (result < 0) { + gnutls_assert(); + return result; + } + } + + if (bag->element[elem].friendly_name != NULL) { + uint8_t *name; + int size, i; + const char *p; + + /* Add a new Attribute + */ + result = asn1_write_value(c2, where, "NEW", 1); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + /* convert name to BMPString + */ + size = strlen(bag->element[elem].friendly_name) * 2; + name = gnutls_malloc(size); + + if (name == NULL) { + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; + } + + p = bag->element[elem].friendly_name; + for (i = 0; i < size; i += 2) { + name[i] = 0; + name[i + 1] = *p; + p++; + } + + _gnutls_str_cpy(root, sizeof(root), where); + _gnutls_str_cat(root, sizeof(root), ".?LAST"); + + result = + _gnutls_x509_encode_and_write_attribute + (FRIENDLY_NAME_OID, c2, root, name, size, 1); + + gnutls_free(name); + + if (result < 0) { + gnutls_assert(); + return result; + } + } + + return 0; +} + + +/* Encodes the bag into a SafeContents structure, and puts the output in + * the given datum. Enc is set to non-zero if the data are encrypted; + */ +int +_pkcs12_encode_safe_contents(gnutls_pkcs12_bag_t bag, asn1_node * contents, + int *enc) +{ + asn1_node c2 = NULL; + int result; + unsigned i; + const char *oid; + + if (bag->element[0].type == GNUTLS_BAG_ENCRYPTED && enc) { + *enc = 1; + return 0; /* ENCRYPTED BAG, do nothing. */ + } else if (enc) + *enc = 0; + + /* Step 1. Create the SEQUENCE. + */ + + if ((result = asn1_create_element + (_gnutls_get_pkix(), "PKIX1.pkcs-12-SafeContents", + &c2)) != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + for (i = 0; i < bag->bag_elements; i++) { + + oid = bag_to_oid(bag->element[i].type); + if (oid == NULL) { + gnutls_assert(); + continue; + } + + result = asn1_write_value(c2, "", "NEW", 1); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + /* Copy the bag type. + */ + result = asn1_write_value(c2, "?LAST.bagId", oid, 1); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + /* Set empty attributes + */ + result = + write_attributes(bag, i, c2, "?LAST.bagAttributes"); + if (result < 0) { + gnutls_assert(); + goto cleanup; + } + + + /* Copy the Bag Value + */ + + if (bag->element[i].type == GNUTLS_BAG_CERTIFICATE || + bag->element[i].type == GNUTLS_BAG_SECRET || + bag->element[i].type == GNUTLS_BAG_CRL) { + gnutls_datum_t tmp; + + /* in that case encode it to a CertBag or + * a CrlBag. + */ + + result = + _pkcs12_encode_crt_bag(bag->element[i].type, + &bag->element[i].data, + &tmp); + + if (result < 0) { + gnutls_assert(); + goto cleanup; + } + + result = + _gnutls_x509_write_value(c2, "?LAST.bagValue", + &tmp); + + _gnutls_free_datum(&tmp); + + } else { + + result = + _gnutls_x509_write_value(c2, "?LAST.bagValue", + &bag->element[i]. + data); + } + + if (result < 0) { + gnutls_assert(); + goto cleanup; + } + + } + + /* Encode the data and copy them into the datum + */ + *contents = c2; + + return 0; + + cleanup: + if (c2) + asn1_delete_structure(&c2); + return result; + +} + +/* Checks if the extra_certs contain certificates that may form a chain + * with the first certificate in chain (it is expected that chain_len==1) + * and appends those in the chain. + */ +static int make_chain(gnutls_x509_crt_t ** chain, unsigned int *chain_len, + gnutls_x509_crt_t ** extra_certs, + unsigned int *extra_certs_len, unsigned int flags) +{ + unsigned int i; + + if (*chain_len != 1) + return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + + i = 0; + while (i < *extra_certs_len) { + /* if it is an issuer but not a self-signed one */ + if (gnutls_x509_crt_check_issuer + ((*chain)[*chain_len - 1], (*extra_certs)[i]) != 0) { + if (!(flags & GNUTLS_PKCS12_SP_INCLUDE_SELF_SIGNED) + && + gnutls_x509_crt_check_issuer((*extra_certs)[i], + (*extra_certs)[i]) + != 0) + goto skip; + + if (unlikely(INT_ADD_OVERFLOW(*chain_len, 1))) { + return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + } + + *chain = _gnutls_reallocarray_fast(*chain, + ++(*chain_len), + sizeof((*chain)[0])); + if (*chain == NULL) { + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; + } + (*chain)[*chain_len - 1] = (*extra_certs)[i]; + + (*extra_certs)[i] = + (*extra_certs)[*extra_certs_len - 1]; + (*extra_certs_len)--; + + i = 0; + continue; + } + + skip: + i++; + } + return 0; +} + +/** + * gnutls_pkcs12_simple_parse: + * @p12: A pkcs12 type + * @password: optional password used to decrypt the structure, bags and keys. + * @key: a structure to store the parsed private key. + * @chain: the corresponding to key certificate chain (may be %NULL) + * @chain_len: will be updated with the number of additional (may be %NULL) + * @extra_certs: optional pointer to receive an array of additional + * certificates found in the PKCS12 structure (may be %NULL). + * @extra_certs_len: will be updated with the number of additional + * certs (may be %NULL). + * @crl: an optional structure to store the parsed CRL (may be %NULL). + * @flags: should be zero or one of GNUTLS_PKCS12_SP_* + * + * This function parses a PKCS12 structure in @pkcs12 and extracts the + * private key, the corresponding certificate chain, any additional + * certificates and a CRL. The structures in @key, @chain @crl, and @extra_certs + * must not be initialized. + * + * The @extra_certs and @extra_certs_len parameters are optional + * and both may be set to %NULL. If either is non-%NULL, then both must + * be set. The value for @extra_certs is allocated + * using gnutls_malloc(). + * + * Encrypted PKCS12 bags and PKCS8 private keys are supported, but + * only with password based security and the same password for all + * operations. + * + * Note that a PKCS12 structure may contain many keys and/or certificates, + * and there is no way to identify which key/certificate pair you want. + * For this reason this function is useful for PKCS12 files that contain + * only one key/certificate pair and/or one CRL. + * + * If the provided structure has encrypted fields but no password + * is provided then this function returns %GNUTLS_E_DECRYPTION_FAILED. + * + * Note that normally the chain constructed does not include self signed + * certificates, to comply with TLS' requirements. If, however, the flag + * %GNUTLS_PKCS12_SP_INCLUDE_SELF_SIGNED is specified then + * self signed certificates will be included in the chain. + * + * Prior to using this function the PKCS #12 structure integrity must + * be verified using gnutls_pkcs12_verify_mac(). + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 3.1.0 + **/ +int +gnutls_pkcs12_simple_parse(gnutls_pkcs12_t p12, + const char *password, + gnutls_x509_privkey_t * key, + gnutls_x509_crt_t ** chain, + unsigned int *chain_len, + gnutls_x509_crt_t ** extra_certs, + unsigned int *extra_certs_len, + gnutls_x509_crl_t * crl, unsigned int flags) +{ + gnutls_pkcs12_bag_t bag = NULL; + gnutls_x509_crt_t *_extra_certs = NULL; + unsigned int _extra_certs_len = 0; + gnutls_x509_crt_t *_chain = NULL; + unsigned int _chain_len = 0; + int idx = 0; + int ret; + size_t cert_id_size = 0; + size_t key_id_size = 0; + uint8_t cert_id[20]; + uint8_t key_id[20]; + int privkey_ok = 0; + unsigned int i; + int elements_in_bag; + + *key = NULL; + + if (crl) + *crl = NULL; + + /* find the first private key */ + for (;;) { + + ret = gnutls_pkcs12_bag_init(&bag); + if (ret < 0) { + bag = NULL; + gnutls_assert(); + goto done; + } + + ret = gnutls_pkcs12_get_bag(p12, idx, bag); + if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { + gnutls_pkcs12_bag_deinit(bag); + bag = NULL; + break; + } + if (ret < 0) { + gnutls_assert(); + goto done; + } + + ret = gnutls_pkcs12_bag_get_type(bag, 0); + if (ret < 0) { + gnutls_assert(); + goto done; + } + + if (ret == GNUTLS_BAG_ENCRYPTED) { + if (password == NULL) { + ret = + gnutls_assert_val + (GNUTLS_E_DECRYPTION_FAILED); + goto done; + } + + ret = gnutls_pkcs12_bag_decrypt(bag, password); + if (ret < 0) { + gnutls_assert(); + goto done; + } + } + + elements_in_bag = gnutls_pkcs12_bag_get_count(bag); + if (elements_in_bag < 0) { + gnutls_assert(); + goto done; + } + + for (i = 0; i < (unsigned)elements_in_bag; i++) { + int type; + gnutls_datum_t data; + + type = gnutls_pkcs12_bag_get_type(bag, i); + if (type < 0) { + gnutls_assert(); + goto done; + } + + ret = gnutls_pkcs12_bag_get_data(bag, i, &data); + if (ret < 0) { + gnutls_assert(); + goto done; + } + + switch (type) { + case GNUTLS_BAG_PKCS8_ENCRYPTED_KEY: + if (password == NULL) { + ret = + gnutls_assert_val + (GNUTLS_E_DECRYPTION_FAILED); + goto done; + } + + FALLTHROUGH; + case GNUTLS_BAG_PKCS8_KEY: + if (*key != NULL) { /* too simple to continue */ + gnutls_assert(); + break; + } + + ret = gnutls_x509_privkey_init(key); + if (ret < 0) { + gnutls_assert(); + goto done; + } + + ret = gnutls_x509_privkey_import_pkcs8 + (*key, &data, GNUTLS_X509_FMT_DER, + password, + type == + GNUTLS_BAG_PKCS8_KEY ? + GNUTLS_PKCS_PLAIN : 0); + if (ret < 0) { + gnutls_assert(); + goto done; + } + + key_id_size = sizeof(key_id); + ret = + gnutls_x509_privkey_get_key_id(*key, 0, + key_id, + &key_id_size); + if (ret < 0) { + gnutls_assert(); + goto done; + } + + privkey_ok = 1; /* break */ + break; + default: + break; + } + } + + idx++; + gnutls_pkcs12_bag_deinit(bag); + bag = NULL; + + if (privkey_ok != 0) /* private key was found */ + break; + } + + if (privkey_ok == 0) { /* no private key */ + gnutls_assert(); + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + } + + /* now find the corresponding certificate + */ + idx = 0; + bag = NULL; + for (;;) { + ret = gnutls_pkcs12_bag_init(&bag); + if (ret < 0) { + bag = NULL; + gnutls_assert(); + goto done; + } + + ret = gnutls_pkcs12_get_bag(p12, idx, bag); + if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { + gnutls_pkcs12_bag_deinit(bag); + bag = NULL; + break; + } + if (ret < 0) { + gnutls_assert(); + goto done; + } + + ret = gnutls_pkcs12_bag_get_type(bag, 0); + if (ret < 0) { + gnutls_assert(); + goto done; + } + + if (ret == GNUTLS_BAG_ENCRYPTED) { + ret = gnutls_pkcs12_bag_decrypt(bag, password); + if (ret < 0) { + gnutls_assert(); + goto done; + } + } + + elements_in_bag = gnutls_pkcs12_bag_get_count(bag); + if (elements_in_bag < 0) { + gnutls_assert(); + goto done; + } + + for (i = 0; i < (unsigned)elements_in_bag; i++) { + int type; + gnutls_datum_t data; + gnutls_x509_crt_t this_cert; + + type = gnutls_pkcs12_bag_get_type(bag, i); + if (type < 0) { + gnutls_assert(); + goto done; + } + + ret = gnutls_pkcs12_bag_get_data(bag, i, &data); + if (ret < 0) { + gnutls_assert(); + goto done; + } + + switch (type) { + case GNUTLS_BAG_CERTIFICATE: + ret = gnutls_x509_crt_init(&this_cert); + if (ret < 0) { + gnutls_assert(); + goto done; + } + + ret = + gnutls_x509_crt_import(this_cert, + &data, + GNUTLS_X509_FMT_DER); + if (ret < 0) { + gnutls_assert(); + gnutls_x509_crt_deinit(this_cert); + this_cert = NULL; + goto done; + } + + /* check if the key id match */ + cert_id_size = sizeof(cert_id); + ret = + gnutls_x509_crt_get_key_id(this_cert, + 0, cert_id, + &cert_id_size); + if (ret < 0) { + gnutls_assert(); + gnutls_x509_crt_deinit(this_cert); + this_cert = NULL; + goto done; + } + + if (memcmp(cert_id, key_id, cert_id_size) != 0) { /* they don't match - skip the certificate */ + if (unlikely(INT_ADD_OVERFLOW(_extra_certs_len, 1))) { + ret = gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + goto done; + } + + _extra_certs = + _gnutls_reallocarray_fast(_extra_certs, + ++_extra_certs_len, + sizeof(_extra_certs[0])); + if (!_extra_certs) { + gnutls_assert(); + ret = + GNUTLS_E_MEMORY_ERROR; + goto done; + } + _extra_certs + [_extra_certs_len - + 1] = this_cert; + this_cert = NULL; + } else { + if (chain && _chain_len == 0) { + _chain = + gnutls_malloc(sizeof + (_chain + [0]) * + (++_chain_len)); + if (!_chain) { + gnutls_assert(); + ret = + GNUTLS_E_MEMORY_ERROR; + goto done; + } + _chain[_chain_len - 1] = + this_cert; + this_cert = NULL; + } else { + gnutls_x509_crt_deinit + (this_cert); + this_cert = NULL; + } + } + break; + + case GNUTLS_BAG_CRL: + if (crl == NULL || *crl != NULL) { + gnutls_assert(); + break; + } + + ret = gnutls_x509_crl_init(crl); + if (ret < 0) { + gnutls_assert(); + goto done; + } + + ret = + gnutls_x509_crl_import(*crl, &data, + GNUTLS_X509_FMT_DER); + if (ret < 0) { + gnutls_assert(); + gnutls_x509_crl_deinit(*crl); + *crl = NULL; + goto done; + } + break; + + case GNUTLS_BAG_ENCRYPTED: + /* XXX Bother to recurse one level down? Unlikely to + use the same password anyway. */ + case GNUTLS_BAG_EMPTY: + default: + break; + } + } + + idx++; + gnutls_pkcs12_bag_deinit(bag); + bag = NULL; + } + + if (chain != NULL) { + if (_chain_len != 1) { + ret = GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + goto done; + } + + ret = + make_chain(&_chain, &_chain_len, &_extra_certs, + &_extra_certs_len, flags); + if (ret < 0) { + gnutls_assert(); + goto done; + } + } + + ret = 0; + + done: + if (bag) + gnutls_pkcs12_bag_deinit(bag); + + if (ret < 0) { + if (*key) { + gnutls_x509_privkey_deinit(*key); + *key = NULL; + } + if (crl != NULL && *crl != NULL) { + gnutls_x509_crl_deinit(*crl); + *crl = NULL; + } + if (_extra_certs_len && _extra_certs != NULL) { + for (i = 0; i < _extra_certs_len; i++) + gnutls_x509_crt_deinit(_extra_certs[i]); + gnutls_free(_extra_certs); + } + if (_chain_len && _chain != NULL) { + for (i = 0; i < _chain_len; i++) + gnutls_x509_crt_deinit(_chain[i]); + gnutls_free(_chain); + } + + return ret; + } + + if (extra_certs && _extra_certs_len > 0) { + *extra_certs = _extra_certs; + *extra_certs_len = _extra_certs_len; + } else { + if (extra_certs) { + *extra_certs = NULL; + *extra_certs_len = 0; + } + for (i = 0; i < _extra_certs_len; i++) + gnutls_x509_crt_deinit(_extra_certs[i]); + gnutls_free(_extra_certs); + } + + if (chain != NULL) { + *chain = _chain; + *chain_len = _chain_len; + } + + return ret; +} + + +/** + * gnutls_pkcs12_mac_info: + * @pkcs12: A pkcs12 type + * @mac: the MAC algorithm used as %gnutls_mac_algorithm_t + * @salt: the salt used for string to key (if non-NULL then @salt_size initially holds its size) + * @salt_size: string to key salt size + * @iter_count: string to key iteration count + * @oid: if non-NULL it will contain an allocated null-terminated variable with the OID + * + * This function will provide information on the MAC algorithm used + * in a PKCS #12 structure. If the structure algorithms + * are unknown the code %GNUTLS_E_UNKNOWN_HASH_ALGORITHM will be returned, + * and only @oid, will be set. That is, @oid will be set on structures + * with a MAC whether supported or not. It must be deinitialized using gnutls_free(). + * The other variables are only set on supported structures. + * + * Returns: %GNUTLS_E_INVALID_REQUEST if the provided structure doesn't contain a MAC, + * %GNUTLS_E_UNKNOWN_HASH_ALGORITHM if the structure's MAC isn't supported, or + * another negative error code in case of a failure. Zero on success. + **/ +int +gnutls_pkcs12_mac_info(gnutls_pkcs12_t pkcs12, unsigned int *mac, + void *salt, unsigned int *salt_size, unsigned int *iter_count, char **oid) +{ + int ret; + gnutls_datum_t tmp = { NULL, 0 }, dsalt = { + NULL, 0}; + gnutls_mac_algorithm_t algo; + + if (oid) + *oid = NULL; + + if (pkcs12 == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + ret = + _gnutls_x509_read_value(pkcs12->pkcs12, "macData.mac.digestAlgorithm.algorithm", + &tmp); + if (ret < 0) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + if (oid) { + *oid = (char*)tmp.data; + } + + algo = DIG_TO_MAC(gnutls_oid_to_digest((char*)tmp.data)); + if (algo == GNUTLS_MAC_UNKNOWN || mac_to_entry(algo) == NULL) { + gnutls_assert(); + return GNUTLS_E_UNKNOWN_HASH_ALGORITHM; + } + + if (oid) { + tmp.data = NULL; + } + + if (mac) { + *mac = algo; + } + + if (iter_count) { + ret = + _gnutls_x509_read_uint(pkcs12->pkcs12, "macData.iterations", + iter_count); + if (ret < 0) { + *iter_count = 1; /* the default */ + } + } + + if (salt) { + /* Read the salt from the structure. + */ + ret = + _gnutls_x509_read_null_value(pkcs12->pkcs12, "macData.macSalt", + &dsalt); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + if (*salt_size >= (unsigned)dsalt.size) { + *salt_size = dsalt.size; + if (dsalt.size > 0) + memcpy(salt, dsalt.data, dsalt.size); + } else { + *salt_size = dsalt.size; + ret = gnutls_assert_val(GNUTLS_E_SHORT_MEMORY_BUFFER); + goto cleanup; + } + } + + ret = 0; + cleanup: + _gnutls_free_datum(&tmp); + _gnutls_free_datum(&dsalt); + return ret; + +} + diff --git a/lib/x509/pkcs12_bag.c b/lib/x509/pkcs12_bag.c new file mode 100644 index 0000000..51a506e --- /dev/null +++ b/lib/x509/pkcs12_bag.c @@ -0,0 +1,906 @@ +/* + * Copyright (C) 2003-2014 Free Software Foundation, Inc. + * Copyright (C) 2014 Red Hat + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GnuTLS. + * + * The GnuTLS is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/> + * + */ + +/* Functions that relate on PKCS12 Bag packet parsing. + */ + +#include "gnutls_int.h" + +#include <datum.h> +#include <global.h> +#include "errors.h" +#include <common.h> +#include "x509_int.h" +#include "pkcs7_int.h" + +/** + * gnutls_pkcs12_bag_init: + * @bag: A pointer to the type to be initialized + * + * This function will initialize a PKCS12 bag structure. PKCS12 Bags + * usually contain private keys, lists of X.509 Certificates and X.509 + * Certificate revocation lists. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int gnutls_pkcs12_bag_init(gnutls_pkcs12_bag_t * bag) +{ + *bag = gnutls_calloc(1, sizeof(gnutls_pkcs12_bag_int)); + + if (*bag) { + return 0; /* success */ + } + return GNUTLS_E_MEMORY_ERROR; +} + +static inline void _pkcs12_bag_free_data(gnutls_pkcs12_bag_t bag) +{ + unsigned i; + + for (i = 0; i < bag->bag_elements; i++) { + _gnutls_free_datum(&bag->element[i].data); + _gnutls_free_datum(&bag->element[i].local_key_id); + gnutls_free(bag->element[i].friendly_name); + bag->element[i].type = 0; + } + +} + + +/** + * gnutls_pkcs12_bag_deinit: + * @bag: A pointer to the type to be initialized + * + * This function will deinitialize a PKCS12 Bag structure. + **/ +void gnutls_pkcs12_bag_deinit(gnutls_pkcs12_bag_t bag) +{ + if (!bag) + return; + + _pkcs12_bag_free_data(bag); + + gnutls_free(bag); +} + +/** + * gnutls_pkcs12_bag_get_type: + * @bag: The bag + * @indx: The element of the bag to get the type + * + * This function will return the bag's type. + * + * Returns: On error a negative error value or one of the #gnutls_pkcs12_bag_type_t enumerations. + **/ +int +gnutls_pkcs12_bag_get_type(gnutls_pkcs12_bag_t bag, unsigned indx) +{ + if (bag == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + if (indx >= bag->bag_elements) + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + return bag->element[indx].type; +} + +/** + * gnutls_pkcs12_bag_get_count: + * @bag: The bag + * + * This function will return the number of the elements within the bag. + * + * Returns: Number of elements in bag, or an negative error code on + * error. + **/ +int gnutls_pkcs12_bag_get_count(gnutls_pkcs12_bag_t bag) +{ + if (bag == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + return bag->bag_elements; +} + +/** + * gnutls_pkcs12_bag_get_data: + * @bag: The bag + * @indx: The element of the bag to get the data from + * @data: where the bag's data will be. Should be treated as constant. + * + * This function will return the bag's data. The data is a constant + * that is stored into the bag. Should not be accessed after the bag + * is deleted. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int +gnutls_pkcs12_bag_get_data(gnutls_pkcs12_bag_t bag, unsigned indx, + gnutls_datum_t * data) +{ + if (bag == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + if (indx >= bag->bag_elements) + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + + data->data = bag->element[indx].data.data; + data->size = bag->element[indx].data.size; + + return 0; +} + +#define X509_CERT_OID "1.2.840.113549.1.9.22.1" +#define X509_CRL_OID "1.2.840.113549.1.9.23.1" +#define RANDOM_NONCE_OID "1.2.840.113549.1.9.25.3" + +int +_pkcs12_decode_crt_bag(gnutls_pkcs12_bag_type_t type, + const gnutls_datum_t * in, gnutls_datum_t * out) +{ + int ret; + asn1_node c2 = NULL; + + switch (type) { + case GNUTLS_BAG_CERTIFICATE: + if ((ret = asn1_create_element(_gnutls_get_pkix(), + "PKIX1.pkcs-12-CertBag", + &c2)) != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(ret); + goto cleanup; + } + + ret = asn1_der_decoding(&c2, in->data, in->size, NULL); + if (ret != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(ret); + goto cleanup; + } + + ret = + _gnutls_x509_read_string(c2, "certValue", out, + ASN1_ETYPE_OCTET_STRING, 1); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + break; + + case GNUTLS_BAG_CRL: + if ((ret = asn1_create_element(_gnutls_get_pkix(), + "PKIX1.pkcs-12-CRLBag", + &c2)) != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(ret); + goto cleanup; + } + + ret = asn1_der_decoding(&c2, in->data, in->size, NULL); + if (ret != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(ret); + goto cleanup; + } + + ret = + _gnutls_x509_read_string(c2, "crlValue", out, + ASN1_ETYPE_OCTET_STRING, 1); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + break; + + case GNUTLS_BAG_SECRET: + if ((ret = asn1_create_element(_gnutls_get_pkix(), + "PKIX1.pkcs-12-SecretBag", + &c2)) != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(ret); + goto cleanup; + } + + ret = asn1_der_decoding(&c2, in->data, in->size, NULL); + if (ret != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(ret); + goto cleanup; + } + + ret = + _gnutls_x509_read_string(c2, "secretValue", out, + ASN1_ETYPE_OCTET_STRING, 1); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + break; + + default: + gnutls_assert(); + asn1_delete_structure(&c2); + return GNUTLS_E_UNIMPLEMENTED_FEATURE; + } + + asn1_delete_structure(&c2); + + return 0; + + + cleanup: + + asn1_delete_structure(&c2); + return ret; +} + + +int +_pkcs12_encode_crt_bag(gnutls_pkcs12_bag_type_t type, + const gnutls_datum_t * raw, gnutls_datum_t * out) +{ + int ret; + asn1_node c2 = NULL; + + switch (type) { + case GNUTLS_BAG_CERTIFICATE: + if ((ret = asn1_create_element(_gnutls_get_pkix(), + "PKIX1.pkcs-12-CertBag", + &c2)) != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(ret); + goto cleanup; + } + + ret = asn1_write_value(c2, "certId", X509_CERT_OID, 1); + if (ret != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(ret); + goto cleanup; + } + + ret = + _gnutls_x509_write_string(c2, "certValue", raw, + ASN1_ETYPE_OCTET_STRING); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + break; + + case GNUTLS_BAG_CRL: + if ((ret = asn1_create_element(_gnutls_get_pkix(), + "PKIX1.pkcs-12-CRLBag", + &c2)) != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(ret); + goto cleanup; + } + + ret = asn1_write_value(c2, "crlId", X509_CRL_OID, 1); + if (ret != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(ret); + goto cleanup; + } + + ret = + _gnutls_x509_write_string(c2, "crlValue", raw, + ASN1_ETYPE_OCTET_STRING); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + break; + + case GNUTLS_BAG_SECRET: + if ((ret = asn1_create_element(_gnutls_get_pkix(), + "PKIX1.pkcs-12-SecretBag", + &c2)) != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(ret); + goto cleanup; + } + + ret = + asn1_write_value(c2, "secretTypeId", RANDOM_NONCE_OID, + 1); + if (ret != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(ret); + goto cleanup; + } + + ret = + _gnutls_x509_write_string(c2, "secretValue", raw, + ASN1_ETYPE_OCTET_STRING); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + break; + + default: + gnutls_assert(); + asn1_delete_structure(&c2); + return GNUTLS_E_UNIMPLEMENTED_FEATURE; + } + + ret = _gnutls_x509_der_encode(c2, "", out, 0); + + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + asn1_delete_structure(&c2); + + return 0; + + + cleanup: + + asn1_delete_structure(&c2); + return ret; +} + + +/** + * gnutls_pkcs12_bag_set_data: + * @bag: The bag + * @type: The data's type + * @data: the data to be copied. + * + * This function will insert the given data of the given type into + * the bag. + * + * Returns: the index of the added bag on success, or a negative + * value on error. + **/ +int +gnutls_pkcs12_bag_set_data(gnutls_pkcs12_bag_t bag, + gnutls_pkcs12_bag_type_t type, + const gnutls_datum_t * data) +{ + int ret; + if (bag == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + if (bag->bag_elements == MAX_BAG_ELEMENTS - 1) { + gnutls_assert(); + /* bag is full */ + return GNUTLS_E_MEMORY_ERROR; + } + + if (bag->bag_elements == 1) { + /* A bag with a key or an encrypted bag, must have + * only one element. + */ + + if (bag->element[0].type == GNUTLS_BAG_PKCS8_KEY || + bag->element[0].type == GNUTLS_BAG_PKCS8_ENCRYPTED_KEY + || bag->element[0].type == GNUTLS_BAG_ENCRYPTED) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + } + + ret = + _gnutls_set_datum(&bag->element[bag->bag_elements].data, + data->data, data->size); + + if (ret < 0) { + gnutls_assert(); + return ret; + } + + bag->element[bag->bag_elements].type = type; + + bag->bag_elements++; + + return bag->bag_elements - 1; +} + +/** + * gnutls_pkcs12_bag_set_crt: + * @bag: The bag + * @crt: the certificate to be copied. + * + * This function will insert the given certificate into the + * bag. This is just a wrapper over gnutls_pkcs12_bag_set_data(). + * + * Returns: the index of the added bag on success, or a negative + * value on failure. + **/ +int +gnutls_pkcs12_bag_set_crt(gnutls_pkcs12_bag_t bag, gnutls_x509_crt_t crt) +{ + int ret; + gnutls_datum_t data; + + if (bag == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + ret = _gnutls_x509_der_encode(crt->cert, "", &data, 0); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + ret = + gnutls_pkcs12_bag_set_data(bag, GNUTLS_BAG_CERTIFICATE, &data); + + _gnutls_free_datum(&data); + + return ret; +} + +/** + * gnutls_pkcs12_bag_set_crl: + * @bag: The bag + * @crl: the CRL to be copied. + * + * This function will insert the given CRL into the + * bag. This is just a wrapper over gnutls_pkcs12_bag_set_data(). + * + * Returns: the index of the added bag on success, or a negative error code + * on failure. + **/ +int +gnutls_pkcs12_bag_set_crl(gnutls_pkcs12_bag_t bag, gnutls_x509_crl_t crl) +{ + int ret; + gnutls_datum_t data; + + + if (bag == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + ret = _gnutls_x509_der_encode(crl->crl, "", &data, 0); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + ret = gnutls_pkcs12_bag_set_data(bag, GNUTLS_BAG_CRL, &data); + + _gnutls_free_datum(&data); + + return ret; +} + +/** + * gnutls_pkcs12_bag_set_key_id: + * @bag: The bag + * @indx: The bag's element to add the id + * @id: the ID + * + * This function will add the given key ID, to the specified, by the + * index, bag element. The key ID will be encoded as a 'Local key + * identifier' bag attribute, which is usually used to distinguish + * the local private key and the certificate pair. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. or a negative error code on error. + **/ +int +gnutls_pkcs12_bag_set_key_id(gnutls_pkcs12_bag_t bag, unsigned indx, + const gnutls_datum_t * id) +{ + int ret; + + + if (bag == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + if (indx > bag->bag_elements - 1) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + ret = _gnutls_set_datum(&bag->element[indx].local_key_id, + id->data, id->size); + + if (ret < 0) { + gnutls_assert(); + return ret; + } + + return 0; +} + +/** + * gnutls_pkcs12_bag_get_key_id: + * @bag: The bag + * @indx: The bag's element to add the id + * @id: where the ID will be copied (to be treated as const) + * + * This function will return the key ID, of the specified bag element. + * The key ID is usually used to distinguish the local private key and + * the certificate pair. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. or a negative error code on error. + **/ +int +gnutls_pkcs12_bag_get_key_id(gnutls_pkcs12_bag_t bag, unsigned indx, + gnutls_datum_t * id) +{ + if (bag == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + if (indx > bag->bag_elements - 1) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + id->data = bag->element[indx].local_key_id.data; + id->size = bag->element[indx].local_key_id.size; + + return 0; +} + +/** + * gnutls_pkcs12_bag_get_friendly_name: + * @bag: The bag + * @indx: The bag's element to add the id + * @name: will hold a pointer to the name (to be treated as const) + * + * This function will return the friendly name, of the specified bag + * element. The key ID is usually used to distinguish the local + * private key and the certificate pair. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. or a negative error code on error. + **/ +int +gnutls_pkcs12_bag_get_friendly_name(gnutls_pkcs12_bag_t bag, unsigned indx, + char **name) +{ + if (bag == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + if (indx > bag->bag_elements - 1) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + *name = bag->element[indx].friendly_name; + + return 0; +} + + +/** + * gnutls_pkcs12_bag_set_friendly_name: + * @bag: The bag + * @indx: The bag's element to add the id + * @name: the name + * + * This function will add the given key friendly name, to the + * specified, by the index, bag element. The name will be encoded as + * a 'Friendly name' bag attribute, which is usually used to set a + * user name to the local private key and the certificate pair. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. or a negative error code on error. + **/ +int +gnutls_pkcs12_bag_set_friendly_name(gnutls_pkcs12_bag_t bag, unsigned indx, + const char *name) +{ + if (bag == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + if (indx > bag->bag_elements - 1) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + bag->element[indx].friendly_name = gnutls_strdup(name); + + if (name == NULL) { + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; + } + + return 0; +} + + +/** + * gnutls_pkcs12_bag_decrypt: + * @bag: The bag + * @pass: The password used for encryption, must be ASCII. + * + * This function will decrypt the given encrypted bag and return 0 on + * success. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, + * otherwise a negative error code is returned. + **/ +int gnutls_pkcs12_bag_decrypt(gnutls_pkcs12_bag_t bag, const char *pass) +{ + int ret; + gnutls_datum_t dec; + + if (bag == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + if (bag->element[0].type != GNUTLS_BAG_ENCRYPTED) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + ret = + _gnutls_pkcs7_decrypt_data(&bag->element[0].data, pass, &dec); + + if (ret < 0) { + gnutls_assert(); + return ret; + } + + /* decryption succeeded. Now decode the SafeContents + * stuff, and parse it. + */ + + _gnutls_free_datum(&bag->element[0].data); + + ret = _pkcs12_decode_safe_contents(&dec, bag); + + _gnutls_free_datum(&dec); + + if (ret < 0) { + gnutls_assert(); + return ret; + } + + return 0; +} + +/** + * gnutls_pkcs12_bag_encrypt: + * @bag: The bag + * @pass: The password used for encryption, must be ASCII + * @flags: should be one of #gnutls_pkcs_encrypt_flags_t elements bitwise or'd + * + * This function will encrypt the given bag. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, + * otherwise a negative error code is returned. + **/ +int +gnutls_pkcs12_bag_encrypt(gnutls_pkcs12_bag_t bag, const char *pass, + unsigned int flags) +{ + int ret; + asn1_node safe_cont = NULL; + gnutls_datum_t der = { NULL, 0 }; + gnutls_datum_t enc = { NULL, 0 }; + schema_id id; + + if (bag == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + if (bag->element[0].type == GNUTLS_BAG_ENCRYPTED) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + /* Encode the whole bag to a safe contents + * structure. + */ + ret = _pkcs12_encode_safe_contents(bag, &safe_cont, NULL); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + /* DER encode the SafeContents. + */ + ret = _gnutls_x509_der_encode(safe_cont, "", &der, 0); + + asn1_delete_structure(&safe_cont); + + if (ret < 0) { + gnutls_assert(); + return ret; + } + + if (flags & GNUTLS_PKCS_PLAIN) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + id = _gnutls_pkcs_flags_to_schema(flags); + + /* Now encrypt them. + */ + ret = _gnutls_pkcs7_encrypt_data(id, &der, pass, &enc); + + _gnutls_free_datum(&der); + + if (ret < 0) { + gnutls_assert(); + return ret; + } + + /* encryption succeeded. + */ + + _pkcs12_bag_free_data(bag); + + bag->element[0].type = GNUTLS_BAG_ENCRYPTED; + bag->element[0].data = enc; + + bag->bag_elements = 1; + + + return 0; +} + +/** + * gnutls_pkcs12_bag_enc_info: + * @bag: The bag + * @schema: indicate the schema as one of %gnutls_pkcs_encrypt_flags_t + * @cipher: the cipher used as %gnutls_cipher_algorithm_t + * @salt: PBKDF2 salt (if non-NULL then @salt_size initially holds its size) + * @salt_size: PBKDF2 salt size + * @iter_count: PBKDF2 iteration count + * @oid: if non-NULL it will contain an allocated null-terminated variable with the OID + * + * This function will provide information on the encryption algorithms used + * in an encrypted bag. If the structure algorithms + * are unknown the code %GNUTLS_E_UNKNOWN_CIPHER_TYPE will be returned, + * and only @oid, will be set. That is, @oid will be set on encrypted bags + * whether supported or not. It must be deinitialized using gnutls_free(). + * The other variables are only set on supported structures. + * + * Returns: %GNUTLS_E_INVALID_REQUEST if the provided bag isn't encrypted, + * %GNUTLS_E_UNKNOWN_CIPHER_TYPE if the structure's encryption isn't supported, or + * another negative error code in case of a failure. Zero on success. + **/ +int +gnutls_pkcs12_bag_enc_info(gnutls_pkcs12_bag_t bag, unsigned int *schema, unsigned int *cipher, + void *salt, unsigned int *salt_size, unsigned int *iter_count, char **oid) +{ + int ret; + struct pbkdf2_params kdf; + const struct pkcs_cipher_schema_st *p; + + if (bag == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + if (bag->element[0].type != GNUTLS_BAG_ENCRYPTED) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + ret = + _gnutls_pkcs7_data_enc_info(&bag->element[0].data, &p, &kdf, oid); + + if (ret < 0) { + gnutls_assert(); + return ret; + } + + if (schema) + *schema = p->flag; + + if (cipher) + *cipher = p->cipher; + + if (iter_count) + *iter_count = kdf.iter_count; + + if (salt) { + if (*salt_size >= (unsigned)kdf.salt_size) { + memcpy(salt, kdf.salt, kdf.salt_size); + } else { + *salt_size = kdf.salt_size; + return gnutls_assert_val(GNUTLS_E_SHORT_MEMORY_BUFFER); + } + } + + if (salt_size) + *salt_size = kdf.salt_size; + + + return 0; +} + +/** + * gnutls_pkcs12_bag_set_privkey: + * @bag: The bag + * @privkey: the private key to be copied. + * @password: the password to protect the key with (may be %NULL) + * @flags: should be one of #gnutls_pkcs_encrypt_flags_t elements bitwise or'd + * + * This function will insert the given private key into the + * bag. This is just a wrapper over gnutls_pkcs12_bag_set_data(). + * + * Returns: the index of the added bag on success, or a negative + * value on failure. + **/ +int +gnutls_pkcs12_bag_set_privkey(gnutls_pkcs12_bag_t bag, gnutls_x509_privkey_t privkey, + const char *password, unsigned flags) +{ + int ret; + gnutls_datum_t data = {NULL, 0}; + + if (bag == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + ret = gnutls_x509_privkey_export2_pkcs8(privkey, GNUTLS_X509_FMT_DER, + password, flags, &data); + if (ret < 0) + return gnutls_assert_val(ret); + + if (password == NULL) { + ret = gnutls_pkcs12_bag_set_data(bag, GNUTLS_BAG_PKCS8_KEY, &data); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + } else { + ret = gnutls_pkcs12_bag_set_data(bag, GNUTLS_BAG_PKCS8_ENCRYPTED_KEY, &data); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + } + + cleanup: + _gnutls_free_datum(&data); + + return ret; +} diff --git a/lib/x509/pkcs12_encr.c b/lib/x509/pkcs12_encr.c new file mode 100644 index 0000000..55c46e4 --- /dev/null +++ b/lib/x509/pkcs12_encr.c @@ -0,0 +1,227 @@ +/* minip12.c - A mini pkcs-12 implementation (modified for gnutls) + * + * Copyright (C) 2002-2012 Free Software Foundation, Inc. + * Copyright (C) 2017 Red Hat, Inc. + * + * This file is part of GnuTLS. + * + * The GnuTLS is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/> + * + */ + +#include "gnutls_int.h" + +#include <mpi.h> +#include "errors.h" +#include <x509_int.h> +#include <algorithms.h> + +#define MAX_PASS_LEN 8192 +#define MAX_V_SIZE 128 +/* ID should be: + * 3 for MAC + * 2 for IV + * 1 for encryption key + * + * Note that this function produces different key for the + * NULL password, and for the password with zero length. + */ +int +_gnutls_pkcs12_string_to_key(const mac_entry_st * me, + unsigned int id, const uint8_t * salt, + unsigned int salt_size, unsigned int iter, + const char *pw, unsigned int req_keylen, + uint8_t * keybuf) +{ + int rc; + unsigned int i, j; + digest_hd_st md; + bigint_t num_b1 = NULL, num_ij = NULL; + bigint_t v_mpi = NULL; + unsigned int pwlen; + uint8_t hash[MAX_HASH_SIZE], buf_b[MAX_V_SIZE], buf_i[MAX_PASS_LEN + MAX_V_SIZE], *p; + uint8_t d[MAX_V_SIZE]; + size_t cur_keylen; + size_t n, m, plen, i_size; + size_t slen; + gnutls_datum_t ucs2 = {NULL, 0}; + unsigned mac_len; + uint8_t v_val[MAX_V_SIZE+1]; + unsigned v_size = 0; + + switch (me->id) { + case GNUTLS_DIG_GOSTR_94: + v_size = 32; + break; + case GNUTLS_DIG_SHA1: + case GNUTLS_DIG_SHA224: + case GNUTLS_DIG_SHA256: + case GNUTLS_DIG_STREEBOG_256: + case GNUTLS_DIG_STREEBOG_512: + v_size = 64; + break; + case GNUTLS_DIG_SHA384: + case GNUTLS_DIG_SHA512: + v_size = 128; + break; + default: + break; + } + + if (v_size == 0 || v_size > MAX_V_SIZE) + return gnutls_assert_val(GNUTLS_E_UNIMPLEMENTED_FEATURE); + + memset(v_val, 0, sizeof(v_val)); + v_val[0] = 0x01; /* make it be 2^64 or 2^128 */ + + cur_keylen = 0; + + if (pw) { + pwlen = strlen(pw); + + if (pwlen == 0) { + ucs2.data = gnutls_calloc(1, 2); + if (ucs2.data == NULL) + return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + ucs2.size = 2; + } else { + rc = _gnutls_utf8_to_ucs2(pw, pwlen, &ucs2, 1); + if (rc < 0) + return gnutls_assert_val(rc); + + /* include terminating zero */ + ucs2.size+=2; + } + pwlen = ucs2.size; + pw = (char*)ucs2.data; + } else { + pwlen = 0; + } + + if (pwlen > MAX_PASS_LEN) { + rc = gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + goto cleanup; + } + + rc = _gnutls_mpi_init_scan(&v_mpi, v_val, v_size+1); + if (rc < 0) { + gnutls_assert(); + goto cleanup; + } + + /* Store salt and password in BUF_I */ + slen = ((salt_size+v_size-1)/v_size) * v_size; + plen = ((pwlen+v_size-1)/v_size) * v_size; + i_size = slen + plen; + + if (i_size > sizeof(buf_i)) { + rc = gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + goto cleanup; + } + + p = buf_i; + for (i = 0; i < slen; i++) + *p++ = salt[i % salt_size]; + + if (pw) { + for (i = j = 0; i < plen; i += 2) { + *p++ = pw[j]; + *p++ = pw[j+1]; + j+=2; + if (j >= pwlen) + j = 0; + } + } else { + memset(p, 0, plen); + } + + mac_len = _gnutls_mac_get_algo_len(me); + assert(mac_len != 0); + + for (;;) { + rc = _gnutls_hash_init(&md, me); + if (rc < 0) { + gnutls_assert(); + goto cleanup; + } + memset(d, id & 0xff, v_size); + _gnutls_hash(&md, d, v_size); + _gnutls_hash(&md, buf_i, i_size); + _gnutls_hash_deinit(&md, hash); + for (i = 1; i < iter; i++) { + rc = _gnutls_hash_fast((gnutls_digest_algorithm_t)me->id, + hash, mac_len, hash); + if (rc < 0) { + gnutls_assert(); + goto cleanup; + } + } + for (i = 0; i < mac_len && cur_keylen < req_keylen; i++) + keybuf[cur_keylen++] = hash[i]; + if (cur_keylen == req_keylen) { + rc = 0; /* ready */ + goto cleanup; + } + + /* need more bytes. */ + for (i = 0; i < v_size; i++) + buf_b[i] = hash[i % mac_len]; + n = v_size; + rc = _gnutls_mpi_init_scan(&num_b1, buf_b, n); + if (rc < 0) { + gnutls_assert(); + goto cleanup; + } + + rc = _gnutls_mpi_add_ui(num_b1, num_b1, 1); + if (rc < 0) { + gnutls_assert(); + goto cleanup; + } + + for (i = 0; i < i_size; i += v_size) { + n = v_size; + rc = _gnutls_mpi_init_scan(&num_ij, buf_i + i, n); + if (rc < 0) { + gnutls_assert(); + goto cleanup; + } + + rc = _gnutls_mpi_addm(num_ij, num_ij, num_b1, v_mpi); + if (rc < 0) { + gnutls_assert(); + goto cleanup; + } + + n = v_size; + m = (_gnutls_mpi_get_nbits(num_ij) + 7) / 8; + + memset(buf_i + i, 0, n - m); + rc = _gnutls_mpi_print(num_ij, buf_i + i + n - m, + &n); + if (rc < 0) { + gnutls_assert(); + goto cleanup; + } + _gnutls_mpi_release(&num_ij); + } + } + cleanup: + _gnutls_mpi_release(&num_ij); + _gnutls_mpi_release(&num_b1); + _gnutls_mpi_release(&v_mpi); + gnutls_free(ucs2.data); + + return rc; +} diff --git a/lib/x509/pkcs7-attrs.c b/lib/x509/pkcs7-attrs.c new file mode 100644 index 0000000..25f9472 --- /dev/null +++ b/lib/x509/pkcs7-attrs.c @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2015 Red Hat, Inc. + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GnuTLS. + * + * The GnuTLS is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/> + * + */ + +/* Functions that relate on PKCS7 attribute setting. + */ + +#include "gnutls_int.h" + +#include <datum.h> +#include <global.h> +#include "errors.h" +#include <common.h> +#include <x509_b64.h> +#include <gnutls/abstract.h> +#include <gnutls/pkcs7.h> + +/** + * gnutls_pkcs7_add_attr: + * @list: A list of existing attributes or pointer to %NULL for the first one + * @oid: the OID of the attribute to be set + * @data: the raw (DER-encoded) data of the attribute to be set + * @flags: zero or %GNUTLS_PKCS7_ATTR_ENCODE_OCTET_STRING + * + * This function will set a PKCS #7 attribute in the provided list. + * If this function fails, the previous list would be deallocated. + * + * Note that any attributes set with this function must either be + * DER or BER encoded, unless a special flag is present. + * + * Returns: On success, the new list head, otherwise %NULL. + * + * Since: 3.4.2 + **/ +int +gnutls_pkcs7_add_attr(gnutls_pkcs7_attrs_t * list, const char *oid, + gnutls_datum_t * data, unsigned flags) +{ + int ret; + gnutls_pkcs7_attrs_st *r; + + r = gnutls_calloc(1, sizeof(gnutls_pkcs7_attrs_st)); + if (r == NULL) + goto fail; + + if (flags & GNUTLS_PKCS7_ATTR_ENCODE_OCTET_STRING) { + ret = _gnutls_x509_encode_string(ASN1_ETYPE_OCTET_STRING, + data->data, data->size, + &r->data); + } else { + ret = _gnutls_set_datum(&r->data, data->data, data->size); + } + if (ret < 0) + goto fail; + + r->oid = gnutls_strdup(oid); + if (r->oid == NULL) + goto fail; + + r->next = *list; + *list = r; + + return 0; + fail: + if (r) { + gnutls_free(r->data.data); + gnutls_free(r); + } + gnutls_pkcs7_attrs_deinit(*list); + return GNUTLS_E_MEMORY_ERROR; + +} + +/** + * gnutls_pkcs7_get_attr: + * @list: A list of existing attributes or %NULL for the first one + * @idx: the index of the attribute to get + * @oid: the OID of the attribute (read-only) + * @data: the raw data of the attribute + * @flags: zero or %GNUTLS_PKCS7_ATTR_ENCODE_OCTET_STRING + * + * This function will get a PKCS #7 attribute from the provided list. + * The OID is a constant string, but data will be allocated and must be + * deinitialized by the caller. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE is returned + * if there are no data in the current index. + * + * Since: 3.4.2 + **/ +int +gnutls_pkcs7_get_attr(gnutls_pkcs7_attrs_t list, unsigned idx, char **oid, + gnutls_datum_t * data, unsigned flags) +{ + unsigned i; + gnutls_pkcs7_attrs_st *p = list; + int ret; + + for (i = 0; i < idx; i++) { + p = p->next; + if (p == NULL) + break; + } + + if (p == NULL) + return gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE); + + *oid = p->oid; + + if (flags & GNUTLS_PKCS7_ATTR_ENCODE_OCTET_STRING) { + ret = _gnutls_x509_decode_string(ASN1_ETYPE_OCTET_STRING, + p->data.data, p->data.size, + data, 1); + } else { + ret = _gnutls_set_datum(data, p->data.data, p->data.size); + } + if (ret < 0) + return gnutls_assert_val(ret); + + return 0; +} + +/** + * gnutls_pkcs7_attrs_deinit: + * @list: A list of existing attributes + * + * This function will clear a PKCS #7 attribute list. + * + * Since: 3.4.2 + **/ +void gnutls_pkcs7_attrs_deinit(gnutls_pkcs7_attrs_t list) +{ + gnutls_pkcs7_attrs_st *r = list, *next; + + while (r) { + next = r->next; + + gnutls_free(r->data.data); + gnutls_free(r->oid); + gnutls_free(r); + r = next; + } +} diff --git a/lib/x509/pkcs7-crypt.c b/lib/x509/pkcs7-crypt.c new file mode 100644 index 0000000..59eddcd --- /dev/null +++ b/lib/x509/pkcs7-crypt.c @@ -0,0 +1,1793 @@ +/* + * Copyright (C) 2003-2016 Free Software Foundation, Inc. + * Copyright (C) 2014-2016 Red Hat + * Copyright (C) 2014-2016 Nikos Mavrogiannopoulos + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GnuTLS. + * + * The GnuTLS is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/> + * + */ + +#include "gnutls_int.h" + +#include <datum.h> +#include <global.h> +#include "errors.h" +#include <common.h> +#include <x509.h> +#include <x509_b64.h> +#include "x509_int.h" +#include "pkcs7_int.h" +#include <algorithms.h> +#include <num.h> +#include <random.h> +#include <pk.h> + +#define PBES1_DES_MD5_OID "1.2.840.113549.1.5.3" + +#define PBES2_OID "1.2.840.113549.1.5.13" +#define PBKDF2_OID "1.2.840.113549.1.5.12" +#define DES_EDE3_CBC_OID "1.2.840.113549.3.7" +#define AES_128_CBC_OID "2.16.840.1.101.3.4.1.2" +#define AES_192_CBC_OID "2.16.840.1.101.3.4.1.22" +#define AES_256_CBC_OID "2.16.840.1.101.3.4.1.42" +#define DES_CBC_OID "1.3.14.3.2.7" + +/* oid_pbeWithSHAAnd3_KeyTripleDES_CBC */ +#define PKCS12_PBE_3DES_SHA1_OID "1.2.840.113549.1.12.1.3" +#define PKCS12_PBE_ARCFOUR_SHA1_OID "1.2.840.113549.1.12.1.1" +#define PKCS12_PBE_RC2_40_SHA1_OID "1.2.840.113549.1.12.1.6" + +static const struct pkcs_cipher_schema_st avail_pkcs_cipher_schemas[] = { + { + .schema = PBES1_DES_MD5, + .name = "PBES1-DES-CBC-MD5", + .flag = GNUTLS_PKCS_PBES1_DES_MD5, + .cipher = GNUTLS_CIPHER_DES_CBC, + .pbes2 = 0, + .cipher_oid = PBES1_DES_MD5_OID, + .write_oid = PBES1_DES_MD5_OID, + .desc = NULL, + .iv_name = NULL, + .decrypt_only = 1}, + { + .schema = PBES2_3DES, + .name = "PBES2-3DES-CBC", + .flag = GNUTLS_PKCS_PBES2_3DES, + .cipher = GNUTLS_CIPHER_3DES_CBC, + .pbes2 = 1, + .cipher_oid = DES_EDE3_CBC_OID, + .write_oid = PBES2_OID, + .desc = "PKIX1.pkcs-5-des-EDE3-CBC-params", + .iv_name = "", + .decrypt_only = 0}, + { + .schema = PBES2_DES, + .name = "PBES2-DES-CBC", + .flag = GNUTLS_PKCS_PBES2_DES, + .cipher = GNUTLS_CIPHER_DES_CBC, + .pbes2 = 1, + .cipher_oid = DES_CBC_OID, + .write_oid = PBES2_OID, + .desc = "PKIX1.pkcs-5-des-CBC-params", + .iv_name = "", + .decrypt_only = 0}, + { + .schema = PBES2_AES_128, + .name = "PBES2-AES128-CBC", + .flag = GNUTLS_PKCS_PBES2_AES_128, + .cipher = GNUTLS_CIPHER_AES_128_CBC, + .pbes2 = 1, + .cipher_oid = AES_128_CBC_OID, + .write_oid = PBES2_OID, + .desc = "PKIX1.pkcs-5-aes128-CBC-params", + .iv_name = "", + .decrypt_only = 0}, + { + .schema = PBES2_AES_192, + .name = "PBES2-AES192-CBC", + .flag = GNUTLS_PKCS_PBES2_AES_192, + .cipher = GNUTLS_CIPHER_AES_192_CBC, + .pbes2 = 1, + .cipher_oid = AES_192_CBC_OID, + .write_oid = PBES2_OID, + .desc = "PKIX1.pkcs-5-aes192-CBC-params", + .iv_name = "", + .decrypt_only = 0}, + { + .schema = PBES2_AES_256, + .name = "PBES2-AES256-CBC", + .flag = GNUTLS_PKCS_PBES2_AES_256, + .cipher = GNUTLS_CIPHER_AES_256_CBC, + .pbes2 = 1, + .cipher_oid = AES_256_CBC_OID, + .write_oid = PBES2_OID, + .desc = "PKIX1.pkcs-5-aes256-CBC-params", + .iv_name = "", + .decrypt_only = 0}, + { + .schema = PBES2_GOST28147_89_TC26Z, + .name = "PBES2-GOST28147-89-TC26Z", + .flag = GNUTLS_PKCS_PBES2_GOST_TC26Z, + .cipher = GNUTLS_CIPHER_GOST28147_TC26Z_CFB, + .pbes2 = 1, + .cipher_oid = GOST28147_89_TC26Z_OID, + .write_oid = PBES2_OID, + .desc = "PKIX1.Gost28147-89-Parameters", + .iv_name = "iv", + .decrypt_only = 0}, + { + .schema = PBES2_GOST28147_89_CPA, + .name = "PBES2-GOST28147-89-CPA", + .flag = GNUTLS_PKCS_PBES2_GOST_CPA, + .cipher = GNUTLS_CIPHER_GOST28147_CPA_CFB, + .pbes2 = 1, + .cipher_oid = GOST28147_89_CPA_OID, + .write_oid = PBES2_OID, + .desc = "PKIX1.Gost28147-89-Parameters", + .iv_name = "iv", + .decrypt_only = 0}, + { + .schema = PBES2_GOST28147_89_CPB, + .name = "PBES2-GOST28147-89-CPB", + .flag = GNUTLS_PKCS_PBES2_GOST_CPB, + .cipher = GNUTLS_CIPHER_GOST28147_CPB_CFB, + .pbes2 = 1, + .cipher_oid = GOST28147_89_CPB_OID, + .write_oid = PBES2_OID, + .desc = "PKIX1.Gost28147-89-Parameters", + .iv_name = "iv", + .decrypt_only = 0}, + { + .schema = PBES2_GOST28147_89_CPC, + .name = "PBES2-GOST28147-89-CPC", + .flag = GNUTLS_PKCS_PBES2_GOST_CPC, + .cipher = GNUTLS_CIPHER_GOST28147_CPC_CFB, + .pbes2 = 1, + .cipher_oid = GOST28147_89_CPC_OID, + .write_oid = PBES2_OID, + .desc = "PKIX1.Gost28147-89-Parameters", + .iv_name = "iv", + .decrypt_only = 0}, + { + .schema = PBES2_GOST28147_89_CPD, + .name = "PBES2-GOST28147-89-CPD", + .flag = GNUTLS_PKCS_PBES2_GOST_CPD, + .cipher = GNUTLS_CIPHER_GOST28147_CPD_CFB, + .pbes2 = 1, + .cipher_oid = GOST28147_89_CPD_OID, + .write_oid = PBES2_OID, + .desc = "PKIX1.Gost28147-89-Parameters", + .iv_name = "iv", + .decrypt_only = 0}, + { + .schema = PKCS12_ARCFOUR_SHA1, + .name = "PKCS12-ARCFOUR-SHA1", + .flag = GNUTLS_PKCS_PKCS12_ARCFOUR, + .cipher = GNUTLS_CIPHER_ARCFOUR, + .pbes2 = 0, + .cipher_oid = PKCS12_PBE_ARCFOUR_SHA1_OID, + .write_oid = PKCS12_PBE_ARCFOUR_SHA1_OID, + .desc = NULL, + .iv_name = NULL, + .decrypt_only = 0}, + { + .schema = PKCS12_RC2_40_SHA1, + .name = "PKCS12-RC2-40-SHA1", + .flag = GNUTLS_PKCS_PKCS12_RC2_40, + .cipher = GNUTLS_CIPHER_RC2_40_CBC, + .pbes2 = 0, + .cipher_oid = PKCS12_PBE_RC2_40_SHA1_OID, + .write_oid = PKCS12_PBE_RC2_40_SHA1_OID, + .desc = NULL, + .iv_name = NULL, + .decrypt_only = 0}, + { + .schema = PKCS12_3DES_SHA1, + .name = "PKCS12-3DES-SHA1", + .flag = GNUTLS_PKCS_PKCS12_3DES, + .cipher = GNUTLS_CIPHER_3DES_CBC, + .pbes2 = 0, + .cipher_oid = PKCS12_PBE_3DES_SHA1_OID, + .write_oid = PKCS12_PBE_3DES_SHA1_OID, + .desc = NULL, + .iv_name = NULL, + .decrypt_only = 0}, + {0, 0, 0, 0, 0} +}; + +#define PBES2_SCHEMA_LOOP(b) { \ + const struct pkcs_cipher_schema_st * _p; \ + for (_p=avail_pkcs_cipher_schemas;_p->schema != 0;_p++) { b; } \ + } + +#define PBES2_SCHEMA_FIND_FROM_FLAGS(fl, what) \ + PBES2_SCHEMA_LOOP( if (_p->flag == GNUTLS_PKCS_CIPHER_MASK(fl)) { what; } ) + +int _gnutls_pkcs_flags_to_schema(unsigned int flags) +{ + PBES2_SCHEMA_FIND_FROM_FLAGS(flags, return _p->schema; + ); + + gnutls_assert(); + _gnutls_debug_log + ("Selecting default encryption PBES2_AES_256 (flags: %u).\n", + flags); + return PBES2_AES_256; +} + +/** + * gnutls_pkcs_schema_get_name: + * @schema: Holds the PKCS #12 or PBES2 schema (%gnutls_pkcs_encrypt_flags_t) + * + * This function will return a human readable description of the + * PKCS12 or PBES2 schema. + * + * Returns: a constraint string or %NULL on error. + * + * Since: 3.4.0 + */ +const char *gnutls_pkcs_schema_get_name(unsigned int schema) +{ + PBES2_SCHEMA_FIND_FROM_FLAGS(schema, return _p->name; + ); + return NULL; +} + +/** + * gnutls_pkcs_schema_get_oid: + * @schema: Holds the PKCS #12 or PBES2 schema (%gnutls_pkcs_encrypt_flags_t) + * + * This function will return the object identifier of the + * PKCS12 or PBES2 schema. + * + * Returns: a constraint string or %NULL on error. + * + * Since: 3.4.0 + */ +const char *gnutls_pkcs_schema_get_oid(unsigned int schema) +{ + PBES2_SCHEMA_FIND_FROM_FLAGS(schema, return _p->cipher_oid; + ); + return NULL; +} + +static const struct pkcs_cipher_schema_st *algo_to_pbes2_cipher_schema(unsigned + cipher) +{ + PBES2_SCHEMA_LOOP(if (_p->cipher == cipher && _p->pbes2 != 0) { + return _p;} + ) ; + + gnutls_assert(); + return NULL; +} + +/* Converts a PKCS#7 encryption schema OID to an internal + * schema_id or returns a negative value */ +int _gnutls_check_pkcs_cipher_schema(const char *oid) +{ + if (strcmp(oid, PBES2_OID) == 0) + return PBES2_GENERIC; /* PBES2 ciphers are under an umbrella OID */ + + PBES2_SCHEMA_LOOP(if (_p->pbes2 == 0 && strcmp(oid, _p->write_oid) == 0) { + return _p->schema;} + ) ; + _gnutls_debug_log + ("PKCS #12 encryption schema OID '%s' is unsupported.\n", oid); + + return GNUTLS_E_UNKNOWN_CIPHER_TYPE; +} + +const struct pkcs_cipher_schema_st *_gnutls_pkcs_schema_get(schema_id schema) +{ + PBES2_SCHEMA_LOOP(if (schema == _p->schema) return _p;) ; + + gnutls_assert(); + return NULL; +} + +/* Converts an OID to a gnutls cipher type. + */ +static int +pbes2_cipher_oid_to_algo(const char *oid, gnutls_cipher_algorithm_t * algo) +{ + + *algo = 0; + PBES2_SCHEMA_LOOP(if + (_p->pbes2 != 0 && strcmp(_p->cipher_oid, oid) == 0) { + *algo = _p->cipher; return 0;} + ) ; + + _gnutls_debug_log("PKCS #8 encryption OID '%s' is unsupported.\n", oid); + return GNUTLS_E_UNKNOWN_CIPHER_TYPE; +} + +/* Decrypts a PKCS #7 encryptedData. The output is allocated + * and stored in dec. + */ +int +_gnutls_pkcs7_decrypt_data(const gnutls_datum_t * data, + const char *password, gnutls_datum_t * dec) +{ + int result, len; + char enc_oid[MAX_OID_SIZE]; + gnutls_datum_t tmp; + asn1_node pasn = NULL, pkcs7_asn = NULL; + int params_start, params_end, params_len; + struct pbkdf2_params kdf_params; + struct pbe_enc_params enc_params; + schema_id schema; + + if ((result = + asn1_create_element(_gnutls_get_pkix(), + "PKIX1.pkcs-7-EncryptedData", + &pkcs7_asn)) != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto error; + } + + result = asn1_der_decoding(&pkcs7_asn, data->data, data->size, NULL); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto error; + } + + /* Check the encryption schema OID + */ + len = sizeof(enc_oid); + result = + asn1_read_value(pkcs7_asn, + "encryptedContentInfo.contentEncryptionAlgorithm.algorithm", + enc_oid, &len); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto error; + } + + if ((result = _gnutls_check_pkcs_cipher_schema(enc_oid)) < 0) { + gnutls_assert(); + goto error; + } + schema = result; + + /* Get the DER encoding of the parameters. + */ + result = + asn1_der_decoding_startEnd(pkcs7_asn, data->data, data->size, + "encryptedContentInfo.contentEncryptionAlgorithm.parameters", + ¶ms_start, ¶ms_end); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto error; + } + params_len = params_end - params_start + 1; + + result = + _gnutls_read_pkcs_schema_params(&schema, password, + &data->data[params_start], + params_len, &kdf_params, + &enc_params); + if (result < 0) { + gnutls_assert(); + goto error; + } + + /* Parameters have been decoded. Now + * decrypt the EncryptedData. + */ + + result = + _gnutls_pkcs_raw_decrypt_data(schema, pkcs7_asn, + "encryptedContentInfo.encryptedContent", + password, &kdf_params, &enc_params, + &tmp); + if (result < 0) { + gnutls_assert(); + goto error; + } + + asn1_delete_structure2(&pkcs7_asn, ASN1_DELETE_FLAG_ZEROIZE); + + *dec = tmp; + + return 0; + + error: + asn1_delete_structure(&pasn); + asn1_delete_structure2(&pkcs7_asn, ASN1_DELETE_FLAG_ZEROIZE); + return result; +} + +int +_gnutls_pkcs7_data_enc_info(const gnutls_datum_t * data, + const struct pkcs_cipher_schema_st **p, + struct pbkdf2_params *kdf_params, char **oid) +{ + int result, len; + char enc_oid[MAX_OID_SIZE]; + asn1_node pasn = NULL, pkcs7_asn = NULL; + int params_start, params_end, params_len; + struct pbe_enc_params enc_params; + schema_id schema; + + if ((result = + asn1_create_element(_gnutls_get_pkix(), + "PKIX1.pkcs-7-EncryptedData", + &pkcs7_asn)) != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto error; + } + + result = asn1_der_decoding(&pkcs7_asn, data->data, data->size, NULL); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto error; + } + + /* Check the encryption schema OID + */ + len = sizeof(enc_oid); + result = + asn1_read_value(pkcs7_asn, + "encryptedContentInfo.contentEncryptionAlgorithm.algorithm", + enc_oid, &len); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto error; + } + + if (oid) { + *oid = gnutls_strdup(enc_oid); + } + + if ((result = _gnutls_check_pkcs_cipher_schema(enc_oid)) < 0) { + gnutls_assert(); + goto error; + } + schema = result; + + /* Get the DER encoding of the parameters. + */ + result = + asn1_der_decoding_startEnd(pkcs7_asn, data->data, data->size, + "encryptedContentInfo.contentEncryptionAlgorithm.parameters", + ¶ms_start, ¶ms_end); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto error; + } + params_len = params_end - params_start + 1; + + result = + _gnutls_read_pkcs_schema_params(&schema, NULL, + &data->data[params_start], + params_len, kdf_params, + &enc_params); + if (result < 0) { + gnutls_assert(); + goto error; + } + + *p = _gnutls_pkcs_schema_get(schema); + if (*p == NULL) { + gnutls_assert(); + result = GNUTLS_E_UNKNOWN_CIPHER_TYPE; + goto error; + } + + asn1_delete_structure2(&pkcs7_asn, ASN1_DELETE_FLAG_ZEROIZE); + + return 0; + + error: + asn1_delete_structure(&pasn); + asn1_delete_structure2(&pkcs7_asn, ASN1_DELETE_FLAG_ZEROIZE); + return result; +} + +/* Encrypts to a PKCS #7 encryptedData. The output is allocated + * and stored in enc. + */ +int +_gnutls_pkcs7_encrypt_data(schema_id schema, + const gnutls_datum_t * data, + const char *password, gnutls_datum_t * enc) +{ + int result; + gnutls_datum_t key = { NULL, 0 }; + gnutls_datum_t tmp = { NULL, 0 }; + asn1_node pkcs7_asn = NULL; + struct pbkdf2_params kdf_params; + struct pbe_enc_params enc_params; + const struct pkcs_cipher_schema_st *s; + + s = _gnutls_pkcs_schema_get(schema); + if (s == NULL || s->decrypt_only) { + return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + } + + if ((result = + asn1_create_element(_gnutls_get_pkix(), + "PKIX1.pkcs-7-EncryptedData", + &pkcs7_asn)) != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto error; + } + + result = + asn1_write_value(pkcs7_asn, + "encryptedContentInfo.contentEncryptionAlgorithm.algorithm", + s->write_oid, 1); + + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto error; + } + + /* Generate a symmetric key. + */ + + result = + _gnutls_pkcs_generate_key(schema, password, &kdf_params, + &enc_params, &key); + if (result < 0) { + gnutls_assert(); + goto error; + } + + result = _gnutls_pkcs_write_schema_params(schema, pkcs7_asn, + "encryptedContentInfo.contentEncryptionAlgorithm.parameters", + &kdf_params, &enc_params); + if (result < 0) { + gnutls_assert(); + goto error; + } + + /* Parameters have been encoded. Now + * encrypt the Data. + */ + result = _gnutls_pkcs_raw_encrypt_data(data, &enc_params, &key, &tmp); + if (result < 0) { + gnutls_assert(); + goto error; + } + + /* write the encrypted data. + */ + result = + asn1_write_value(pkcs7_asn, + "encryptedContentInfo.encryptedContent", + tmp.data, tmp.size); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto error; + } + + _gnutls_free_datum(&tmp); + _gnutls_free_key_datum(&key); + + /* Now write the rest of the pkcs-7 stuff. + */ + + result = _gnutls_x509_write_uint32(pkcs7_asn, "version", 0); + if (result < 0) { + gnutls_assert(); + goto error; + } + + result = + asn1_write_value(pkcs7_asn, "encryptedContentInfo.contentType", + DATA_OID, 1); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto error; + } + + result = asn1_write_value(pkcs7_asn, "unprotectedAttrs", NULL, 0); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto error; + } + + /* Now encode and copy the DER stuff. + */ + result = _gnutls_x509_der_encode(pkcs7_asn, "", enc, 0); + + asn1_delete_structure2(&pkcs7_asn, ASN1_DELETE_FLAG_ZEROIZE); + + if (result < 0) { + gnutls_assert(); + goto error; + } + + error: + _gnutls_free_key_datum(&key); + _gnutls_free_datum(&tmp); + asn1_delete_structure2(&pkcs7_asn, ASN1_DELETE_FLAG_ZEROIZE); + return result; +} + +/* Reads the PBKDF2 parameters. + */ +static int +read_pbkdf2_params(asn1_node pasn, + const gnutls_datum_t * der, struct pbkdf2_params *params) +{ + int params_start, params_end; + int params_len, len, result; + asn1_node pbkdf2_asn = NULL; + char oid[MAX_OID_SIZE]; + + memset(params, 0, sizeof(*params)); + + params->mac = GNUTLS_MAC_SHA1; + + /* Check the key derivation algorithm + */ + len = sizeof(oid); + result = + asn1_read_value(pasn, "keyDerivationFunc.algorithm", oid, &len); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + _gnutls_hard_log("keyDerivationFunc.algorithm: %s\n", oid); + + if (strcmp(oid, PBKDF2_OID) != 0) { + gnutls_assert(); + _gnutls_debug_log + ("PKCS #8 key derivation OID '%s' is unsupported.\n", oid); + return _gnutls_asn2err(result); + } + + result = + asn1_der_decoding_startEnd(pasn, der->data, der->size, + "keyDerivationFunc.parameters", + ¶ms_start, ¶ms_end); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + params_len = params_end - params_start + 1; + + /* Now check the key derivation and the encryption + * functions. + */ + if ((result = + asn1_create_element(_gnutls_get_pkix(), + "PKIX1.pkcs-5-PBKDF2-params", + &pbkdf2_asn)) != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + result = + _asn1_strict_der_decode(&pbkdf2_asn, &der->data[params_start], + params_len, NULL); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto error; + } + + /* read the salt */ + params->salt_size = sizeof(params->salt); + result = + asn1_read_value(pbkdf2_asn, "salt.specified", params->salt, + ¶ms->salt_size); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto error; + } + _gnutls_hard_log("salt.specified.size: %d\n", params->salt_size); + + if (params->salt_size < 0) { + result = gnutls_assert_val(GNUTLS_E_ILLEGAL_PARAMETER); + goto error; + } + + /* read the iteration count + */ + result = + _gnutls_x509_read_uint(pbkdf2_asn, "iterationCount", + ¶ms->iter_count); + if (result < 0) { + gnutls_assert(); + goto error; + } + + if (params->iter_count >= MAX_ITER_COUNT || params->iter_count == 0) { + result = gnutls_assert_val(GNUTLS_E_ILLEGAL_PARAMETER); + goto error; + } + + _gnutls_hard_log("iterationCount: %d\n", params->iter_count); + + /* read the keylength, if it is set. + */ + result = + _gnutls_x509_read_uint(pbkdf2_asn, "keyLength", ¶ms->key_size); + if (result < 0) { + params->key_size = 0; + } + + if (params->key_size > MAX_CIPHER_KEY_SIZE) { + result = gnutls_assert_val(GNUTLS_E_ILLEGAL_PARAMETER); + goto error; + } + + _gnutls_hard_log("keyLength: %d\n", params->key_size); + + len = sizeof(oid); + result = asn1_read_value(pbkdf2_asn, "prf.algorithm", oid, &len); + if (result != ASN1_SUCCESS) { + /* use the default MAC */ + result = 0; + goto error; + } + + params->mac = gnutls_oid_to_mac(oid); + if (params->mac == GNUTLS_MAC_UNKNOWN) { + gnutls_assert(); + _gnutls_debug_log("Unsupported hash algorithm: %s\n", oid); + result = GNUTLS_E_UNKNOWN_HASH_ALGORITHM; + goto error; + } + + result = 0; + + error: + asn1_delete_structure(&pbkdf2_asn); + return result; + +} + +/* Reads the PBE parameters from PKCS-12 schemas (*&#%*&#% RSA). + */ +static int read_pkcs12_kdf_params(asn1_node pasn, struct pbkdf2_params *params) +{ + int result; + + memset(params, 0, sizeof(*params)); + + /* read the salt */ + params->salt_size = sizeof(params->salt); + result = + asn1_read_value(pasn, "salt", params->salt, ¶ms->salt_size); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + if (params->salt_size < 0) + return gnutls_assert_val(GNUTLS_E_ILLEGAL_PARAMETER); + + _gnutls_hard_log("salt.size: %d\n", params->salt_size); + + /* read the iteration count + */ + result = + _gnutls_x509_read_uint(pasn, "iterations", ¶ms->iter_count); + if (result < 0) + return gnutls_assert_val(result); + + if (params->iter_count >= MAX_ITER_COUNT || params->iter_count == 0) + return gnutls_assert_val(GNUTLS_E_ILLEGAL_PARAMETER); + + _gnutls_hard_log("iterationCount: %d\n", params->iter_count); + + params->key_size = 0; + + return 0; +} + +/* Writes the PBE parameters for PKCS-12 schemas. + */ +static int +write_pkcs12_kdf_params(asn1_node pasn, const struct pbkdf2_params *kdf_params) +{ + int result; + + /* write the salt + */ + result = + asn1_write_value(pasn, "salt", + kdf_params->salt, kdf_params->salt_size); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto error; + } + _gnutls_hard_log("salt.size: %d\n", kdf_params->salt_size); + + /* write the iteration count + */ + result = + _gnutls_x509_write_uint32(pasn, "iterations", + kdf_params->iter_count); + if (result < 0) { + gnutls_assert(); + goto error; + } + _gnutls_hard_log("iterationCount: %d\n", kdf_params->iter_count); + + return 0; + + error: + return result; + +} + +static int +read_pbes2_gost_oid(uint8_t *der, size_t len, char *oid, int oid_size) +{ + int result; + asn1_node pbe_asn = NULL; + + if ((result = + asn1_create_element(_gnutls_get_pkix(), + "PKIX1.Gost28147-89-Parameters", &pbe_asn)) != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + result = + _asn1_strict_der_decode(&pbe_asn, der, len, NULL); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto error; + } + + result = asn1_read_value(pbe_asn, "encryptionParamSet", + oid, &oid_size); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto error; + } + + result = 0; + + error: + asn1_delete_structure(&pbe_asn); + return result; +} + +static int +read_pbes2_enc_params(asn1_node pasn, + const gnutls_datum_t * der, struct pbe_enc_params *params) +{ + int params_start, params_end; + int params_len, len, result; + asn1_node pbe_asn = NULL; + const struct pkcs_cipher_schema_st *p; + + memset(params, 0, sizeof(*params)); + + /* Check the encryption algorithm + */ + len = sizeof(params->pbes2_oid); + result = asn1_read_value(pasn, "encryptionScheme.algorithm", params->pbes2_oid, &len); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + _gnutls_hard_log("encryptionScheme.algorithm: %s\n", params->pbes2_oid); + + result = + asn1_der_decoding_startEnd(pasn, der->data, der->size, + "encryptionScheme.parameters", + ¶ms_start, ¶ms_end); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + params_len = params_end - params_start + 1; + + /* For GOST we have to read params to determine actual cipher */ + if (!strcmp(params->pbes2_oid, GOST28147_89_OID)) { + len = sizeof(params->pbes2_oid); + result = read_pbes2_gost_oid(&der->data[params_start], + params_len, params->pbes2_oid, len); + if (result < 0) { + gnutls_assert(); + return result; + } + } + + if ((result = pbes2_cipher_oid_to_algo(params->pbes2_oid, ¶ms->cipher)) < 0) { + gnutls_assert(); + return result; + } + + /* Now check the encryption parameters. + */ + p = algo_to_pbes2_cipher_schema(params->cipher); + if (p == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + if ((result = + asn1_create_element(_gnutls_get_pkix(), + p->desc, &pbe_asn)) != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + result = + _asn1_strict_der_decode(&pbe_asn, &der->data[params_start], + params_len, NULL); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto error; + } + + /* read the IV */ + params->iv_size = sizeof(params->iv); + result = asn1_read_value(pbe_asn, + p->iv_name, + params->iv, ¶ms->iv_size); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto error; + } + _gnutls_hard_log("IV.size: %d\n", params->iv_size); + + result = 0; + + error: + asn1_delete_structure(&pbe_asn); + return result; +} + +/* Read the parameters cipher, IV, salt etc using the given + * schema ID. Initially the schema ID should have PBES2_GENERIC, for + * PBES2 schemas, and will be updated by this function for details. + */ +int +_gnutls_read_pkcs_schema_params(schema_id * schema, const char *password, + const uint8_t * data, int data_size, + struct pbkdf2_params *kdf_params, + struct pbe_enc_params *enc_params) +{ + asn1_node pasn = NULL; + int result; + gnutls_datum_t tmp; + const struct pkcs_cipher_schema_st *p; + + if (*schema == PBES2_GENERIC) { + /* Now check the key derivation and the encryption + * functions. + */ + if ((result = + asn1_create_element(_gnutls_get_pkix(), + "PKIX1.pkcs-5-PBES2-params", + &pasn)) != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto error; + } + + /* Decode the parameters. + */ + result = _asn1_strict_der_decode(&pasn, data, data_size, NULL); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto error; + } + + tmp.data = (uint8_t *) data; + tmp.size = data_size; + + result = read_pbkdf2_params(pasn, &tmp, kdf_params); + if (result < 0) { + gnutls_assert(); + goto error; + } + + result = read_pbes2_enc_params(pasn, &tmp, enc_params); + if (result < 0) { + gnutls_assert(); + goto error; + } + + asn1_delete_structure2(&pasn, ASN1_DELETE_FLAG_ZEROIZE); + + p = algo_to_pbes2_cipher_schema(enc_params->cipher); + if (p == NULL) { + result = GNUTLS_E_INVALID_REQUEST; + gnutls_assert(); + goto error; + } + + *schema = p->schema; + return 0; + } else if (*schema == PBES1_DES_MD5) { + return _gnutls_read_pbkdf1_params(data, data_size, kdf_params, + enc_params); + } else { /* PKCS #12 schema */ + memset(enc_params, 0, sizeof(*enc_params)); + + p = _gnutls_pkcs_schema_get(*schema); + if (p == NULL) { + gnutls_assert(); + result = GNUTLS_E_UNKNOWN_CIPHER_TYPE; + goto error; + } + enc_params->cipher = p->cipher; + enc_params->iv_size = gnutls_cipher_get_iv_size(p->cipher); + + if ((result = + asn1_create_element(_gnutls_get_pkix(), + "PKIX1.pkcs-12-PbeParams", + &pasn)) != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto error; + } + + /* Decode the parameters. + */ + result = _asn1_strict_der_decode(&pasn, data, data_size, NULL); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto error; + } + + result = read_pkcs12_kdf_params(pasn, kdf_params); + if (result < 0) { + gnutls_assert(); + goto error; + } + + if (enc_params->iv_size) { + result = + _gnutls_pkcs12_string_to_key(mac_to_entry + (GNUTLS_MAC_SHA1), + 2 /*IV*/, + kdf_params->salt, + kdf_params->salt_size, + kdf_params->iter_count, + password, + enc_params->iv_size, + enc_params->iv); + if (result < 0) { + gnutls_assert(); + goto error; + } + + } + + asn1_delete_structure(&pasn); + + return 0; + } /* switch */ + + error: + asn1_delete_structure(&pasn); + return result; +} + +static int +_gnutls_pbes2_string_to_key(unsigned int pass_len, const char *password, + const struct pbkdf2_params *kdf_params, + int key_size, uint8_t *key) +{ + gnutls_datum_t _key; + gnutls_datum_t salt; + + _key.data = (void *)password; + _key.size = pass_len; + salt.data = (void *)kdf_params->salt; + salt.size = kdf_params->salt_size; + + return gnutls_pbkdf2(kdf_params->mac, &_key, &salt, + kdf_params->iter_count, key, key_size); +} + +int +_gnutls_pkcs_raw_decrypt_data(schema_id schema, asn1_node pkcs8_asn, + const char *root, const char *_password, + const struct pbkdf2_params *kdf_params, + const struct pbe_enc_params *enc_params, + gnutls_datum_t * decrypted_data) +{ + gnutls_datum_t enc = { NULL, 0 }; + uint8_t *key = NULL; + gnutls_datum_t dkey, d_iv; + gnutls_cipher_hd_t ch = NULL; + int key_size, ret; + unsigned int pass_len = 0; + const struct pkcs_cipher_schema_st *p; + unsigned block_size; + const cipher_entry_st *ce; + char *password; + + if (_password) { + gnutls_datum_t pout; + ret = _gnutls_utf8_password_normalize(_password, strlen(_password), &pout, 1); + if (ret < 0) + return gnutls_assert_val(ret); + + password = (char*)pout.data; + pass_len = pout.size; + } else { + password = NULL; + pass_len = 0; + } + + ret = _gnutls_x509_read_value(pkcs8_asn, root, &enc); + if (ret < 0) { + gnutls_assert(); + enc.data = NULL; + goto cleanup; + } + + if (schema == PBES1_DES_MD5) { + ret = _gnutls_decrypt_pbes1_des_md5_data(password, pass_len, + kdf_params, + enc_params, &enc, + decrypted_data); + if (ret < 0) + goto error; + goto cleanup; + } + + if (kdf_params->key_size == 0) { + key_size = gnutls_cipher_get_key_size(enc_params->cipher); + } else + key_size = kdf_params->key_size; + + key = gnutls_malloc(key_size); + if (key == NULL) { + gnutls_assert(); + ret = GNUTLS_E_MEMORY_ERROR; + goto error; + } + + /* generate the key + */ + p = _gnutls_pkcs_schema_get(schema); + if (p != NULL && p->pbes2 != 0) { /* PBES2 */ + ret = _gnutls_pbes2_string_to_key(pass_len, password, + kdf_params, + key_size, key); + if (ret < 0) { + gnutls_assert(); + goto error; + } + } else if (p != NULL) { /* PKCS 12 schema */ + ret = + _gnutls_pkcs12_string_to_key(mac_to_entry(GNUTLS_MAC_SHA1), + 1 /*KEY*/, + kdf_params->salt, + kdf_params->salt_size, + kdf_params->iter_count, + password, key_size, key); + + if (ret < 0) { + gnutls_assert(); + goto error; + } + } else { + gnutls_assert(); + ret = GNUTLS_E_UNKNOWN_CIPHER_TYPE; + goto error; + } + + ce = cipher_to_entry(enc_params->cipher); + block_size = _gnutls_cipher_get_block_size(ce); + + if (ce->type == CIPHER_BLOCK) { + if (enc.size % block_size != 0 || (unsigned)enc_params->iv_size != block_size) { + gnutls_assert(); + ret = GNUTLS_E_DECRYPTION_FAILED; + goto error; + } + } else { + unsigned iv_size = _gnutls_cipher_get_iv_size(ce); + if (iv_size > (unsigned)enc_params->iv_size) { + gnutls_assert(); + ret = GNUTLS_E_DECRYPTION_FAILED; + goto error; + } + } + + /* do the decryption. + */ + dkey.data = key; + dkey.size = key_size; + + d_iv.data = (uint8_t *) enc_params->iv; + d_iv.size = enc_params->iv_size; + + ret = gnutls_cipher_init(&ch, ce->id, &dkey, &d_iv); + + zeroize_temp_key(key, key_size); + gnutls_free(key); + + if (ret < 0) { + gnutls_assert(); + goto error; + } + + ret = gnutls_cipher_decrypt(ch, enc.data, enc.size); + if (ret < 0) { + gnutls_assert(); + ret = GNUTLS_E_DECRYPTION_FAILED; + goto error; + } + + decrypted_data->data = enc.data; + + if (ce->type == CIPHER_BLOCK && block_size != 1) { + unsigned pslen = (uint8_t)enc.data[enc.size - 1]; + unsigned i; + + if (pslen > block_size || pslen >= enc.size || pslen == 0) { + gnutls_assert(); + ret = GNUTLS_E_DECRYPTION_FAILED; + goto error; + } + + /* verify padding according to rfc2898 */ + decrypted_data->size = enc.size - pslen; + for (i=0;i<pslen;i++) { + if (enc.data[enc.size-1-i] != pslen) { + gnutls_assert(); + ret = GNUTLS_E_DECRYPTION_FAILED; + goto error; + } + } + } else { + decrypted_data->size = enc.size; + } + + gnutls_cipher_deinit(ch); + + ret = 0; + + cleanup: + if (password) { + zeroize_temp_key(password, pass_len); + gnutls_free(password); + } + + return ret; + + error: + if (password) { + zeroize_temp_key(password, pass_len); + gnutls_free(password); + } + if (enc.data) { + zeroize_temp_key(enc.data, enc.size); + gnutls_free(enc.data); + } + if (key) { + zeroize_temp_key(key, key_size); + gnutls_free(key); + } + if (ch) { + gnutls_cipher_deinit(ch); + } + return ret; +} + +/* Writes the PBKDF2 parameters. + */ +static int +write_pbkdf2_params(asn1_node pasn, const struct pbkdf2_params *kdf_params) +{ + int result; + asn1_node pbkdf2_asn = NULL; + uint8_t tmp[MAX_OID_SIZE]; + const mac_entry_st *me; + + /* Write the key derivation algorithm + */ + result = + asn1_write_value(pasn, "keyDerivationFunc.algorithm", + PBKDF2_OID, 1); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + /* Now write the key derivation and the encryption + * functions. + */ + if ((result = + asn1_create_element(_gnutls_get_pkix(), + "PKIX1.pkcs-5-PBKDF2-params", + &pbkdf2_asn)) != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + result = asn1_write_value(pbkdf2_asn, "salt", "specified", 1); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto error; + } + + /* write the salt + */ + result = + asn1_write_value(pbkdf2_asn, "salt.specified", + kdf_params->salt, kdf_params->salt_size); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto error; + } + _gnutls_hard_log("salt.specified.size: %d\n", kdf_params->salt_size); + + /* write the iteration count + */ + _gnutls_write_uint32(kdf_params->iter_count, tmp); + + result = asn1_write_value(pbkdf2_asn, "iterationCount", tmp, 4); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto error; + } + _gnutls_hard_log("iterationCount: %d\n", kdf_params->iter_count); + + /* write the keylength, if it is set. + */ + result = asn1_write_value(pbkdf2_asn, "keyLength", NULL, 0); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto error; + } + + me = _gnutls_mac_to_entry(kdf_params->mac); + if (!me || !me->mac_oid) { + gnutls_assert(); + result = GNUTLS_E_INTERNAL_ERROR; + goto error; + } + + result = asn1_write_value(pbkdf2_asn, "prf.algorithm", + me->mac_oid, strlen(me->mac_oid)); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto error; + } + + result = asn1_write_value(pbkdf2_asn, "prf.parameters", NULL, 0); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto error; + } + + /* now encode them an put the DER output + * in the keyDerivationFunc.parameters + */ + result = _gnutls_x509_der_encode_and_copy(pbkdf2_asn, "", + pasn, + "keyDerivationFunc.parameters", + 0); + if (result < 0) { + gnutls_assert(); + goto error; + } + + result = 0; + + error: + asn1_delete_structure(&pbkdf2_asn); + return result; + +} + +static int +write_pbes2_enc_params(asn1_node pasn, const struct pbe_enc_params *params) +{ + int result; + asn1_node pbe_asn = NULL; + const struct pkcs_cipher_schema_st *p; + const char *cipher_oid; + + /* Write the encryption algorithm + */ + p = algo_to_pbes2_cipher_schema(params->cipher); + if (p == NULL || p->pbes2 == 0) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + /* Now check the encryption parameters. + */ + if ((result = + asn1_create_element(_gnutls_get_pkix(), + p->desc, &pbe_asn)) != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + if (p->schema == PBES2_GOST28147_89_TC26Z || + p->schema == PBES2_GOST28147_89_CPA || + p->schema == PBES2_GOST28147_89_CPB || + p->schema == PBES2_GOST28147_89_CPC || + p->schema == PBES2_GOST28147_89_CPD) { + cipher_oid = GOST28147_89_OID; + result = asn1_write_value(pbe_asn, "encryptionParamSet", + p->cipher_oid, 1); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto error; + } + } else { + cipher_oid = p->cipher_oid; + } + + result = + asn1_write_value(pasn, "encryptionScheme.algorithm", + cipher_oid, + 1); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + goto error; + } + _gnutls_hard_log("encryptionScheme.algorithm: %s\n", cipher_oid); + + /* read the salt */ + result = asn1_write_value(pbe_asn, p->iv_name, + params->iv, params->iv_size); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto error; + } + _gnutls_hard_log("IV.size: %d\n", params->iv_size); + + /* now encode them an put the DER output + * in the encryptionScheme.parameters + */ + result = _gnutls_x509_der_encode_and_copy(pbe_asn, "", + pasn, + "encryptionScheme.parameters", + 0); + if (result < 0) { + gnutls_assert(); + goto error; + } + + result = 0; + + error: + asn1_delete_structure(&pbe_asn); + return result; + +} + +/* Generates a key and also stores the key parameters. + */ +int +_gnutls_pkcs_generate_key(schema_id schema, + const char *_password, + struct pbkdf2_params *kdf_params, + struct pbe_enc_params *enc_params, + gnutls_datum_t * key) +{ + unsigned char rnd[2]; + unsigned int pass_len = 0; + int ret; + const struct pkcs_cipher_schema_st *p; + char *password = NULL; + + if (_password) { + gnutls_datum_t pout; + ret = _gnutls_utf8_password_normalize(_password, strlen(_password), &pout, 0); + if (ret < 0) + return gnutls_assert_val(ret); + + password = (char*)pout.data; + pass_len = pout.size; + } else { + password = NULL; + pass_len = 0; + } + + ret = gnutls_rnd(GNUTLS_RND_RANDOM, rnd, 2); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + /* generate salt */ + kdf_params->salt_size = + MIN(sizeof(kdf_params->salt), (unsigned)(12 + (rnd[1] % 10))); + + p = _gnutls_pkcs_schema_get(schema); + if (p != NULL && p->pbes2 != 0) { /* PBES2 */ + enc_params->cipher = p->cipher; + } else if (p != NULL) { + /* non PBES2 algorithms */ + enc_params->cipher = p->cipher; + kdf_params->salt_size = 8; + } else { + gnutls_assert(); + ret = GNUTLS_E_INVALID_REQUEST; + goto cleanup; + } + + ret = gnutls_rnd(GNUTLS_RND_RANDOM, kdf_params->salt, + kdf_params->salt_size); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + kdf_params->iter_count = PKCS12_ITER_COUNT; + key->size = kdf_params->key_size = + gnutls_cipher_get_key_size(enc_params->cipher); + + enc_params->iv_size = gnutls_cipher_get_iv_size(enc_params->cipher); + key->data = gnutls_malloc(key->size); + if (key->data == NULL) { + gnutls_assert(); + ret = GNUTLS_E_MEMORY_ERROR; + goto cleanup; + } + + /* now generate the key. + */ + + if (p->pbes2 != 0) { + if (p->schema == PBES2_GOST28147_89_TC26Z) + kdf_params->mac = GNUTLS_MAC_STREEBOG_512; + else if (p->schema == PBES2_GOST28147_89_CPA || + p->schema == PBES2_GOST28147_89_CPB || + p->schema == PBES2_GOST28147_89_CPC || + p->schema == PBES2_GOST28147_89_CPD) + kdf_params->mac = GNUTLS_MAC_GOSTR_94; + else + kdf_params->mac = GNUTLS_MAC_SHA256; + ret = _gnutls_pbes2_string_to_key(pass_len, password, + kdf_params, + kdf_params->key_size, + key->data); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + if (enc_params->iv_size) { + ret = gnutls_rnd(GNUTLS_RND_NONCE, + enc_params->iv, enc_params->iv_size); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + } + } else { /* PKCS 12 schema */ + ret = + _gnutls_pkcs12_string_to_key(mac_to_entry(GNUTLS_MAC_SHA1), + 1 /*KEY*/, + kdf_params->salt, + kdf_params->salt_size, + kdf_params->iter_count, + password, + kdf_params->key_size, + key->data); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + /* Now generate the IV + */ + if (enc_params->iv_size) { + ret = + _gnutls_pkcs12_string_to_key(mac_to_entry + (GNUTLS_MAC_SHA1), + 2 /*IV*/, + kdf_params->salt, + kdf_params->salt_size, + kdf_params->iter_count, + password, + enc_params->iv_size, + enc_params->iv); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + } + } + + ret = 0; + + cleanup: + gnutls_free(password); + return ret; +} + +/* Encodes the parameters to be written in the encryptionAlgorithm.parameters + * part. + */ +int +_gnutls_pkcs_write_schema_params(schema_id schema, asn1_node pkcs8_asn, + const char *where, + const struct pbkdf2_params *kdf_params, + const struct pbe_enc_params *enc_params) +{ + int result; + asn1_node pasn = NULL; + const struct pkcs_cipher_schema_st *p; + + p = _gnutls_pkcs_schema_get(schema); + + if (p != NULL && p->pbes2 != 0) { /* PBES2 */ + if ((result = + asn1_create_element(_gnutls_get_pkix(), + "PKIX1.pkcs-5-PBES2-params", + &pasn)) != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + result = write_pbkdf2_params(pasn, kdf_params); + if (result < 0) { + gnutls_assert(); + goto error; + } + + result = write_pbes2_enc_params(pasn, enc_params); + if (result < 0) { + gnutls_assert(); + goto error; + } + + result = _gnutls_x509_der_encode_and_copy(pasn, "", + pkcs8_asn, where, 0); + if (result < 0) { + gnutls_assert(); + goto error; + } + + asn1_delete_structure(&pasn); + + } else if (p != NULL) { /* PKCS #12 */ + + if ((result = + asn1_create_element(_gnutls_get_pkix(), + "PKIX1.pkcs-12-PbeParams", + &pasn)) != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto error; + } + + result = write_pkcs12_kdf_params(pasn, kdf_params); + if (result < 0) { + gnutls_assert(); + goto error; + } + + result = _gnutls_x509_der_encode_and_copy(pasn, "", + pkcs8_asn, where, 0); + if (result < 0) { + gnutls_assert(); + goto error; + } + + asn1_delete_structure(&pasn); + } + + return 0; + + error: + asn1_delete_structure(&pasn); + return result; + +} + +int +_gnutls_pkcs_raw_encrypt_data(const gnutls_datum_t * plain, + const struct pbe_enc_params *enc_params, + const gnutls_datum_t * key, gnutls_datum_t * encrypted) +{ + int result; + int data_size; + uint8_t *data = NULL; + gnutls_datum_t d_iv; + gnutls_cipher_hd_t ch = NULL; + uint8_t pad, pad_size; + const cipher_entry_st *ce; + + ce = cipher_to_entry(enc_params->cipher); + pad_size = _gnutls_cipher_get_block_size(ce); + + if (pad_size == 1 || ce->type == CIPHER_STREAM) /* stream */ + pad_size = 0; + + data = gnutls_malloc(plain->size + pad_size); + if (data == NULL) { + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; + } + + memcpy(data, plain->data, plain->size); + + if (pad_size > 0) { + pad = pad_size - (plain->size % pad_size); + if (pad == 0) + pad = pad_size; + memset(&data[plain->size], pad, pad); + } else + pad = 0; + + data_size = plain->size + pad; + + d_iv.data = (uint8_t *) enc_params->iv; + d_iv.size = enc_params->iv_size; + result = gnutls_cipher_init(&ch, enc_params->cipher, key, &d_iv); + if (result < 0) { + gnutls_assert(); + goto error; + } + + result = gnutls_cipher_encrypt(ch, data, data_size); + if (result < 0) { + gnutls_assert(); + goto error; + } + + encrypted->data = data; + encrypted->size = data_size; + + gnutls_cipher_deinit(ch); + + return 0; + + error: + gnutls_free(data); + if (ch) { + gnutls_cipher_deinit(ch); + } + return result; +} diff --git a/lib/x509/pkcs7-output.c b/lib/x509/pkcs7-output.c new file mode 100644 index 0000000..3d686df --- /dev/null +++ b/lib/x509/pkcs7-output.c @@ -0,0 +1,333 @@ +/* + * Copyright (C) 2015 Red Hat, Inc. + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GnuTLS. + * + * The GnuTLS is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/> + * + */ + +#include "gnutls_int.h" +#include <common.h> +#include <x509.h> +#include <x509_int.h> +#include <num.h> +#include "errors.h" +#include <extras/randomart.h> +#include <pkcs7_int.h> + +#define addf _gnutls_buffer_append_printf +#define adds _gnutls_buffer_append_str + +static void print_dn(gnutls_buffer_st * str, const char *prefix, + const gnutls_datum_t * raw) +{ + gnutls_x509_dn_t dn = NULL; + gnutls_datum_t output = { NULL, 0 }; + int ret; + + ret = gnutls_x509_dn_init(&dn); + if (ret < 0) { + addf(str, "%s: [error]\n", prefix); + return; + } + + ret = gnutls_x509_dn_import(dn, raw); + if (ret < 0) { + addf(str, "%s: [error]\n", prefix); + goto cleanup; + } + + ret = gnutls_x509_dn_get_str2(dn, &output, 0); + if (ret < 0) { + addf(str, "%s: [error]\n", prefix); + goto cleanup; + } + + addf(str, "%s: %s\n", prefix, output.data); + + cleanup: + gnutls_x509_dn_deinit(dn); + gnutls_free(output.data); +} + +/* Do not encode ASN1 and type for now */ +#define ENTRY(oid, name, type) {oid, sizeof(oid)-1, name, sizeof(name)-1, NULL, type} +#define ENTRY2(oid, name) {oid, sizeof(oid)-1, name, sizeof(name)-1, NULL, ASN1_ETYPE_INVALID} + +static const struct oid_to_string pkcs7_attrs[] = { + ENTRY ("1.2.840.113549.1.9.3", "contentType", ASN1_ETYPE_OBJECT_ID), + ENTRY ("1.2.840.113549.1.9.4", "messageDigest", ASN1_ETYPE_OCTET_STRING), + ENTRY ("1.2.840.113549.1.9.5", "signingTime", ASN1_ETYPE_INVALID), + ENTRY2("1.2.840.113549.1.9.6", "countersignature"), + ENTRY2("1.2.840.113549.1.9.15", "smimeCapabilities"), + + ENTRY2("1.2.840.113549.1.9.16.2.1", "aa-receiptRequest"), + ENTRY2("1.2.840.113549.1.9.16.2.2", "aa-securityLabel"), + ENTRY2("1.2.840.113549.1.9.16.2.3", "aa-mlExpandHistory"), + ENTRY2("1.2.840.113549.1.9.16.2.4", "aa-contentHint"), + ENTRY2("1.2.840.113549.1.9.16.2.9", "aa-equivalentLabels"), + ENTRY2("1.2.840.113549.1.9.16.2.10", "aa-contentReference"), + ENTRY2("1.2.840.113549.1.9.16.2.11", "aa-encrypKeyPref"), + ENTRY2("1.2.840.113549.1.9.16.2.12", "aa-signingCertificate"), + ENTRY2("1.2.840.113549.1.9.16.2.19", "aa-ets-otherSigCert"), + ENTRY2("1.2.840.113549.1.9.16.2.47", "aa-signingCertificateV2"), + + {NULL, 0, NULL, 0, NULL, 0} +}; + +static void print_raw(gnutls_buffer_st * str, const char *prefix, + const gnutls_datum_t * raw) +{ + gnutls_datum_t result; + int ret; + + if (raw->data == NULL || raw->size == 0) + return; + + ret = gnutls_hex_encode2(raw, &result); + if (ret < 0) { + addf(str, "%s: [error]\n", prefix); + return; + } + + addf(str, "%s: %s\n", prefix, result.data); + gnutls_free(result.data); +} + +static void print_pkcs7_info(gnutls_pkcs7_signature_info_st * info, + gnutls_buffer_st * str, + gnutls_certificate_print_formats_t format) +{ + unsigned i; + char *oid; + gnutls_datum_t data; + char prefix[128]; + char s[42]; + size_t max; + int ret; + const struct oid_to_string * entry; + + if (info->issuer_dn.size > 0) + print_dn(str, "\tSigner's issuer DN", &info->issuer_dn); + print_raw(str, "\tSigner's serial", &info->signer_serial); + print_raw(str, "\tSigner's issuer key ID", &info->issuer_keyid); + if (info->signing_time != -1) { + struct tm t; + if (gmtime_r(&info->signing_time, &t) == NULL) { + addf(str, "error: gmtime_r (%ld)\n", + (unsigned long)info->signing_time); + } else { + max = sizeof(s); + if (strftime(s, max, "%a %b %d %H:%M:%S UTC %Y", &t) == + 0) { + addf(str, "error: strftime (%ld)\n", + (unsigned long)info->signing_time); + } else { + addf(str, "\tSigning time: %s\n", s); + } + } + } + + addf(str, "\tSignature Algorithm: %s\n", + gnutls_sign_get_name(info->algo)); + + if (format == GNUTLS_CRT_PRINT_FULL) { + if (info->signed_attrs) { + for (i = 0;; i++) { + ret = + gnutls_pkcs7_get_attr(info->signed_attrs, i, + &oid, &data, 0); + if (ret < 0) + break; + if (i == 0) + addf(str, "\tSigned Attributes:\n"); + + entry = _gnutls_oid_get_entry(pkcs7_attrs, oid); + snprintf(prefix, sizeof(prefix), "\t\t%s", + (entry && entry->name_desc) ? entry->name_desc : oid); + print_raw(str, prefix, &data); + gnutls_free(data.data); + } + } + if (info->unsigned_attrs) { + for (i = 0;; i++) { + ret = + gnutls_pkcs7_get_attr(info->unsigned_attrs, + i, &oid, &data, 0); + if (ret < 0) + break; + if (i == 0) + addf(str, "\tUnsigned Attributes:\n"); + + entry = _gnutls_oid_get_entry(pkcs7_attrs, oid); + snprintf(prefix, sizeof(prefix), "\t\t%s", + (entry && entry->name_desc) ? entry->name_desc : oid); + print_raw(str, prefix, &data); + gnutls_free(data.data); + } + } + } + adds(str, "\n"); +} + +/** + * gnutls_pkcs7_print_signature_info: + * @info: The PKCS7 signature info struct to be printed + * @format: Indicate the format to use + * @out: Newly allocated datum with null terminated string. + * + * This function will pretty print a PKCS #7 signature info structure, suitable + * for display to a human. + * + * Currently the supported formats are %GNUTLS_CRT_PRINT_FULL and + * %GNUTLS_CRT_PRINT_COMPACT. + * + * The output @out needs to be deallocated using gnutls_free(). + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 3.6.14 + **/ +int gnutls_pkcs7_print_signature_info(gnutls_pkcs7_signature_info_st * info, + gnutls_certificate_print_formats_t format, + gnutls_datum_t * out) +{ + gnutls_buffer_st str; + + _gnutls_buffer_init(&str); + print_pkcs7_info(info, &str, format); + + return _gnutls_buffer_to_datum(&str, out, 1); +} + +/** + * gnutls_pkcs7_crt_print: + * @pkcs7: The PKCS7 struct to be printed + * @format: Indicate the format to use + * @out: Newly allocated datum with null terminated string. + * + * This function will pretty print a signed PKCS #7 structure, suitable for + * display to a human. + * + * Currently the supported formats are %GNUTLS_CRT_PRINT_FULL and + * %GNUTLS_CRT_PRINT_COMPACT. + * + * The output @out needs to be deallocated using gnutls_free(). + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int gnutls_pkcs7_print(gnutls_pkcs7_t pkcs7, + gnutls_certificate_print_formats_t format, + gnutls_datum_t * out) +{ + int count, ret, i; + gnutls_pkcs7_signature_info_st info; + gnutls_buffer_st str; + const char *oid; + + _gnutls_buffer_init(&str); + + /* For backwards compatibility with structures using the default OID, + * we don't print the eContent Type explicitly */ + oid = gnutls_pkcs7_get_embedded_data_oid(pkcs7); + if (oid) { + if (strcmp(oid, DATA_OID) != 0 + && strcmp(oid, DIGESTED_DATA_OID) != 0) { + addf(&str, "eContent Type: %s\n", oid); + } + } + + for (i = 0;; i++) { + if (i == 0) + addf(&str, "Signers:\n"); + + ret = gnutls_pkcs7_get_signature_info(pkcs7, i, &info); + if (ret < 0) + break; + + print_pkcs7_info(&info, &str, format); + gnutls_pkcs7_signature_info_deinit(&info); + } + + if (format == GNUTLS_CRT_PRINT_FULL) { + gnutls_datum_t data, b64; + + count = gnutls_pkcs7_get_crt_count(pkcs7); + + if (count > 0) { + addf(&str, "Number of certificates: %u\n\n", + count); + + for (i = 0; i < count; i++) { + ret = + gnutls_pkcs7_get_crt_raw2(pkcs7, i, &data); + if (ret < 0) { + addf(&str, + "Error: cannot print certificate %d\n", + i); + continue; + } + + ret = + gnutls_pem_base64_encode_alloc + ("CERTIFICATE", &data, &b64); + if (ret < 0) { + gnutls_free(data.data); + continue; + } + + adds(&str, (char*)b64.data); + adds(&str, "\n"); + gnutls_free(b64.data); + gnutls_free(data.data); + } + } + + count = gnutls_pkcs7_get_crl_count(pkcs7); + if (count > 0) { + addf(&str, "Number of CRLs: %u\n\n", count); + + for (i = 0; i < count; i++) { + ret = + gnutls_pkcs7_get_crl_raw2(pkcs7, i, &data); + if (ret < 0) { + addf(&str, + "Error: cannot print certificate %d\n", + i); + continue; + } + + ret = + gnutls_pem_base64_encode_alloc("X509 CRL", + &data, &b64); + if (ret < 0) { + gnutls_free(data.data); + continue; + } + + adds(&str, (char*)b64.data); + adds(&str, "\n"); + gnutls_free(b64.data); + gnutls_free(data.data); + } + } + } + + return _gnutls_buffer_to_datum(&str, out, 1); +} diff --git a/lib/x509/pkcs7.c b/lib/x509/pkcs7.c new file mode 100644 index 0000000..ff8cab0 --- /dev/null +++ b/lib/x509/pkcs7.c @@ -0,0 +1,2564 @@ +/* + * Copyright (C) 2003-2015 Free Software Foundation, Inc. + * Copyright (C) 2015 Red Hat, Inc. + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GnuTLS. + * + * The GnuTLS is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/> + * + */ + +/* Functions that relate on PKCS7 certificate lists parsing. + */ + +#include "gnutls_int.h" +#include <libtasn1.h> + +#include <datum.h> +#include <global.h> +#include "errors.h" +#include <common.h> +#include <x509_b64.h> +#include <pkcs7_int.h> +#include <gnutls/abstract.h> +#include <gnutls/pkcs7.h> + +#define ATTR_MESSAGE_DIGEST "1.2.840.113549.1.9.4" +#define ATTR_SIGNING_TIME "1.2.840.113549.1.9.5" +#define ATTR_CONTENT_TYPE "1.2.840.113549.1.9.3" + +static const uint8_t one = 1; + +/* Decodes the PKCS #7 signed data, and returns an asn1_node, + * which holds them. If raw is non null then the raw decoded + * data are copied (they are locally allocated) there. + */ +static int _decode_pkcs7_signed_data(gnutls_pkcs7_t pkcs7) +{ + asn1_node c2; + int len, result; + gnutls_datum_t tmp = {NULL, 0}; + + len = MAX_OID_SIZE - 1; + result = asn1_read_value(pkcs7->pkcs7, "contentType", pkcs7->encap_data_oid, &len); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + if (strcmp(pkcs7->encap_data_oid, SIGNED_DATA_OID) != 0) { + gnutls_assert(); + _gnutls_debug_log("Unknown PKCS7 Content OID '%s'\n", pkcs7->encap_data_oid); + return GNUTLS_E_UNKNOWN_PKCS_CONTENT_TYPE; + } + + if ((result = asn1_create_element + (_gnutls_get_pkix(), "PKIX1.pkcs-7-SignedData", + &c2)) != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + /* the Signed-data has been created, so + * decode them. + */ + result = _gnutls_x509_read_value(pkcs7->pkcs7, "content", &tmp); + if (result < 0) { + gnutls_assert(); + goto cleanup; + } + + /* Step 1. In case of a signed structure extract certificate set. + */ + + result = asn1_der_decoding(&c2, tmp.data, tmp.size, NULL); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + /* read the encapsulated content */ + len = MAX_OID_SIZE - 1; + result = + asn1_read_value(c2, "encapContentInfo.eContentType", pkcs7->encap_data_oid, &len); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + if (strcmp(pkcs7->encap_data_oid, DATA_OID) != 0 + && strcmp(pkcs7->encap_data_oid, DIGESTED_DATA_OID) != 0) { + _gnutls_debug_log + ("Unknown PKCS#7 Encapsulated Content OID '%s'; treating as raw data\n", + pkcs7->encap_data_oid); + + } + + /* Try reading as octet string according to rfc5652. If that fails, attempt + * a raw read according to rfc2315 */ + result = _gnutls_x509_read_string(c2, "encapContentInfo.eContent", &pkcs7->der_signed_data, ASN1_ETYPE_OCTET_STRING, 1); + if (result < 0) { + result = _gnutls_x509_read_value(c2, "encapContentInfo.eContent", &pkcs7->der_signed_data); + if (result < 0) { + pkcs7->der_signed_data.data = NULL; + pkcs7->der_signed_data.size = 0; + } else { + int tag_len, len_len; + unsigned char cls; + unsigned long tag; + + /* we skip the embedded element's tag and length - uncharted territorry - used by MICROSOFT_CERT_TRUST_LIST */ + result = asn1_get_tag_der(pkcs7->der_signed_data.data, pkcs7->der_signed_data.size, &cls, &tag_len, &tag); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + result = asn1_get_length_ber(pkcs7->der_signed_data.data+tag_len, pkcs7->der_signed_data.size-tag_len, &len_len); + if (result < 0) { + gnutls_assert(); + result = GNUTLS_E_ASN1_DER_ERROR; + goto cleanup; + } + + tag_len += len_len; + memmove(pkcs7->der_signed_data.data, &pkcs7->der_signed_data.data[tag_len], pkcs7->der_signed_data.size-tag_len); + pkcs7->der_signed_data.size-=tag_len; + } + } + + if (pkcs7->signed_data) + asn1_delete_structure(&pkcs7->signed_data); + pkcs7->signed_data = c2; + gnutls_free(tmp.data); + + return 0; + + cleanup: + gnutls_free(tmp.data); + if (c2) + asn1_delete_structure(&c2); + return result; +} + +static int pkcs7_reinit(gnutls_pkcs7_t pkcs7) +{ + int result; + + asn1_delete_structure(&pkcs7->pkcs7); + + result = asn1_create_element(_gnutls_get_pkix(), + "PKIX1.pkcs-7-ContentInfo", &pkcs7->pkcs7); + if (result != ASN1_SUCCESS) { + result = _gnutls_asn2err(result); + gnutls_assert(); + return result; + } + + return 0; +} + +/** + * gnutls_pkcs7_init: + * @pkcs7: A pointer to the type to be initialized + * + * This function will initialize a PKCS7 structure. PKCS7 structures + * usually contain lists of X.509 Certificates and X.509 Certificate + * revocation lists. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int gnutls_pkcs7_init(gnutls_pkcs7_t * pkcs7) +{ + *pkcs7 = gnutls_calloc(1, sizeof(gnutls_pkcs7_int)); + + if (*pkcs7) { + int result = pkcs7_reinit(*pkcs7); + if (result < 0) { + gnutls_assert(); + gnutls_free(*pkcs7); + return result; + } + return 0; /* success */ + } + return GNUTLS_E_MEMORY_ERROR; +} + +/** + * gnutls_pkcs7_deinit: + * @pkcs7: the type to be deinitialized + * + * This function will deinitialize a PKCS7 type. + **/ +void gnutls_pkcs7_deinit(gnutls_pkcs7_t pkcs7) +{ + if (!pkcs7) + return; + + if (pkcs7->pkcs7) + asn1_delete_structure(&pkcs7->pkcs7); + + if (pkcs7->signed_data) + asn1_delete_structure(&pkcs7->signed_data); + + _gnutls_free_datum(&pkcs7->der_signed_data); + + gnutls_free(pkcs7); +} + +/** + * gnutls_pkcs7_import: + * @pkcs7: The data to store the parsed PKCS7. + * @data: The DER or PEM encoded PKCS7. + * @format: One of DER or PEM + * + * This function will convert the given DER or PEM encoded PKCS7 to + * the native #gnutls_pkcs7_t format. The output will be stored in + * @pkcs7. Any signed data that may be present inside the @pkcs7 + * structure, like certificates set by gnutls_pkcs7_set_crt(), will + * be freed and overwritten by this function. + * + * If the PKCS7 is PEM encoded it should have a header of "PKCS7". + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int +gnutls_pkcs7_import(gnutls_pkcs7_t pkcs7, const gnutls_datum_t * data, + gnutls_x509_crt_fmt_t format) +{ + int result = 0, need_free = 0; + gnutls_datum_t _data; + + if (pkcs7 == NULL) + return GNUTLS_E_INVALID_REQUEST; + + _data.data = data->data; + _data.size = data->size; + + /* If the PKCS7 is in PEM format then decode it + */ + if (format == GNUTLS_X509_FMT_PEM) { + result = + _gnutls_fbase64_decode(PEM_PKCS7, data->data, + data->size, &_data); + + if (result < 0) { + gnutls_assert(); + return result; + } + + need_free = 1; + } + + if (pkcs7->expanded) { + result = pkcs7_reinit(pkcs7); + if (result < 0) { + gnutls_assert(); + goto cleanup; + } + } + pkcs7->expanded = 1; + + result = asn1_der_decoding(&pkcs7->pkcs7, _data.data, _data.size, NULL); + if (result != ASN1_SUCCESS) { + result = _gnutls_asn2err(result); + gnutls_assert(); + goto cleanup; + } + + /* Decode the signed data. + */ + result = _decode_pkcs7_signed_data(pkcs7); + if (result < 0) { + gnutls_assert(); + goto cleanup; + } + + result = 0; + + cleanup: + if (need_free) + _gnutls_free_datum(&_data); + return result; +} + +/** + * gnutls_pkcs7_get_crt_raw2: + * @pkcs7: should contain a gnutls_pkcs7_t type + * @indx: contains the index of the certificate to extract + * @cert: will hold the contents of the certificate; must be deallocated with gnutls_free() + * + * This function will return a certificate of the PKCS7 or RFC2630 + * certificate set. + * + * After the last certificate has been read + * %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE will be returned. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. If the provided buffer is not long enough, + * then @certificate_size is updated and + * %GNUTLS_E_SHORT_MEMORY_BUFFER is returned. + * + * Since: 3.4.2 + **/ +int +gnutls_pkcs7_get_crt_raw2(gnutls_pkcs7_t pkcs7, + unsigned indx, gnutls_datum_t * cert) +{ + int result, len; + char root2[MAX_NAME_SIZE]; + char oid[MAX_OID_SIZE]; + gnutls_datum_t tmp = { NULL, 0 }; + + if (pkcs7 == NULL) + return GNUTLS_E_INVALID_REQUEST; + + /* Step 2. Parse the CertificateSet + */ + snprintf(root2, sizeof(root2), "certificates.?%u", indx + 1); + + len = sizeof(oid) - 1; + + result = asn1_read_value(pkcs7->signed_data, root2, oid, &len); + + if (result == ASN1_VALUE_NOT_FOUND) { + result = GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + goto cleanup; + } + + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + /* if 'Certificate' is the choice found: + */ + if (strcmp(oid, "certificate") == 0) { + int start, end; + + result = _gnutls_x509_read_value(pkcs7->pkcs7, "content", &tmp); + if (result < 0) { + gnutls_assert(); + goto cleanup; + } + + result = + asn1_der_decoding_startEnd(pkcs7->signed_data, tmp.data, + tmp.size, root2, &start, &end); + + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + end = end - start + 1; + + result = _gnutls_set_datum(cert, &tmp.data[start], end); + } else { + result = GNUTLS_E_UNSUPPORTED_CERTIFICATE_TYPE; + } + + cleanup: + _gnutls_free_datum(&tmp); + return result; +} + +/** + * gnutls_pkcs7_get_crt_raw: + * @pkcs7: should contain a gnutls_pkcs7_t type + * @indx: contains the index of the certificate to extract + * @certificate: the contents of the certificate will be copied + * there (may be null) + * @certificate_size: should hold the size of the certificate + * + * This function will return a certificate of the PKCS7 or RFC2630 + * certificate set. + * + * After the last certificate has been read + * %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE will be returned. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. If the provided buffer is not long enough, + * then @certificate_size is updated and + * %GNUTLS_E_SHORT_MEMORY_BUFFER is returned. + **/ +int +gnutls_pkcs7_get_crt_raw(gnutls_pkcs7_t pkcs7, + unsigned indx, void *certificate, + size_t * certificate_size) +{ + int ret; + gnutls_datum_t tmp = { NULL, 0 }; + + ret = gnutls_pkcs7_get_crt_raw2(pkcs7, indx, &tmp); + if (ret < 0) + return gnutls_assert_val(ret); + + if ((unsigned)tmp.size > *certificate_size) { + *certificate_size = tmp.size; + ret = GNUTLS_E_SHORT_MEMORY_BUFFER; + goto cleanup; + } + + *certificate_size = tmp.size; + if (certificate) + memcpy(certificate, tmp.data, tmp.size); + + cleanup: + _gnutls_free_datum(&tmp); + return ret; +} + +/** + * gnutls_pkcs7_get_crt_count: + * @pkcs7: should contain a #gnutls_pkcs7_t type + * + * This function will return the number of certificates in the PKCS7 + * or RFC2630 certificate set. + * + * Returns: On success, a positive number is returned, otherwise a + * negative error value. + **/ +int gnutls_pkcs7_get_crt_count(gnutls_pkcs7_t pkcs7) +{ + int result, count; + + if (pkcs7 == NULL) + return GNUTLS_E_INVALID_REQUEST; + + /* Step 2. Count the CertificateSet */ + + result = + asn1_number_of_elements(pkcs7->signed_data, "certificates", &count); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return 0; /* no certificates */ + } + + return count; +} + +/** + * gnutls_pkcs7_signature_info_deinit: + * @info: should point to a #gnutls_pkcs7_signature_info_st structure + * + * This function will deinitialize any allocated value in the + * provided #gnutls_pkcs7_signature_info_st. + * + * Since: 3.4.2 + **/ +void gnutls_pkcs7_signature_info_deinit(gnutls_pkcs7_signature_info_st * info) +{ + gnutls_free(info->sig.data); + gnutls_free(info->issuer_dn.data); + gnutls_free(info->signer_serial.data); + gnutls_free(info->issuer_keyid.data); + gnutls_pkcs7_attrs_deinit(info->signed_attrs); + gnutls_pkcs7_attrs_deinit(info->unsigned_attrs); + memset(info, 0, sizeof(*info)); +} + +static time_t parse_time(gnutls_pkcs7_t pkcs7, const char *root) +{ + char tval[128]; + asn1_node c2 = NULL; + time_t ret; + int result, len; + + result = asn1_create_element(_gnutls_get_pkix(), "PKIX1.Time", &c2); + if (result != ASN1_SUCCESS) { + ret = -1; + gnutls_assert(); + goto cleanup; + } + + len = sizeof(tval); + result = asn1_read_value(pkcs7->signed_data, root, tval, &len); + if (result != ASN1_SUCCESS) { + ret = -1; + gnutls_assert(); + goto cleanup; + } + + result = _asn1_strict_der_decode(&c2, tval, len, NULL); + if (result != ASN1_SUCCESS) { + ret = -1; + gnutls_assert(); + goto cleanup; + } + + ret = _gnutls_x509_get_time(c2, "", 0); + + cleanup: + asn1_delete_structure(&c2); + return ret; +} + +/** + * gnutls_pkcs7_get_signature_count: + * @pkcs7: should contain a #gnutls_pkcs7_t type + * + * This function will return the number of signatures in the PKCS7 + * structure. + * + * Returns: On success, a positive number is returned, otherwise a + * negative error value. + * + * Since: 3.4.3 + **/ +int gnutls_pkcs7_get_signature_count(gnutls_pkcs7_t pkcs7) +{ + int ret, count; + + if (pkcs7 == NULL) + return GNUTLS_E_INVALID_REQUEST; + + ret = + asn1_number_of_elements(pkcs7->signed_data, "signerInfos", &count); + if (ret != ASN1_SUCCESS) { + gnutls_assert(); + return 0; + } + + return count; +} + +/** + * gnutls_pkcs7_get_signature_info: + * @pkcs7: should contain a #gnutls_pkcs7_t type + * @idx: the index of the signature info to check + * @info: will contain the output signature + * + * This function will return information about the signature identified + * by idx in the provided PKCS #7 structure. The information should be + * deinitialized using gnutls_pkcs7_signature_info_deinit(). + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 3.4.2 + **/ +int gnutls_pkcs7_get_signature_info(gnutls_pkcs7_t pkcs7, unsigned idx, + gnutls_pkcs7_signature_info_st * info) +{ + int ret, count, len; + char root[256]; + char oid[MAX_OID_SIZE]; + gnutls_pk_algorithm_t pk; + gnutls_sign_algorithm_t sig; + gnutls_datum_t tmp = { NULL, 0 }; + unsigned i; + + if (pkcs7 == NULL) + return GNUTLS_E_INVALID_REQUEST; + + memset(info, 0, sizeof(*info)); + info->signing_time = -1; + + ret = + asn1_number_of_elements(pkcs7->signed_data, "signerInfos", &count); + if (ret != ASN1_SUCCESS || idx + 1 > (unsigned)count) { + gnutls_assert(); + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + } + snprintf(root, sizeof(root), + "signerInfos.?%u.signatureAlgorithm.algorithm", idx + 1); + + len = sizeof(oid) - 1; + ret = asn1_read_value(pkcs7->signed_data, root, oid, &len); + if (ret != ASN1_SUCCESS) { + gnutls_assert(); + goto unsupp_algo; + } + + sig = gnutls_oid_to_sign(oid); + if (sig == GNUTLS_SIGN_UNKNOWN) { + /* great PKCS #7 allows to only specify a public key algo */ + pk = gnutls_oid_to_pk(oid); + if (pk == GNUTLS_PK_UNKNOWN) { + gnutls_assert(); + goto unsupp_algo; + } + + /* use the digests algorithm */ + snprintf(root, sizeof(root), + "signerInfos.?%u.digestAlgorithm.algorithm", idx + 1); + + len = sizeof(oid) - 1; + ret = asn1_read_value(pkcs7->signed_data, root, oid, &len); + if (ret != ASN1_SUCCESS) { + gnutls_assert(); + goto unsupp_algo; + } + + ret = gnutls_oid_to_digest(oid); + if (ret == GNUTLS_DIG_UNKNOWN) { + gnutls_assert(); + goto unsupp_algo; + } + + sig = gnutls_pk_to_sign(pk, ret); + if (sig == GNUTLS_SIGN_UNKNOWN) { + gnutls_assert(); + goto unsupp_algo; + } + } + + info->algo = sig; + + snprintf(root, sizeof(root), "signerInfos.?%u.signature", idx + 1); + /* read the signature */ + ret = _gnutls_x509_read_value(pkcs7->signed_data, root, &info->sig); + if (ret < 0) { + gnutls_assert(); + goto fail; + } + + /* read the issuer info */ + snprintf(root, sizeof(root), + "signerInfos.?%u.sid.issuerAndSerialNumber.issuer.rdnSequence", + idx + 1); + /* read the signature */ + ret = + _gnutls_x509_get_raw_field(pkcs7->signed_data, root, + &info->issuer_dn); + if (ret >= 0) { + snprintf(root, sizeof(root), + "signerInfos.?%u.sid.issuerAndSerialNumber.serialNumber", + idx + 1); + /* read the signature */ + ret = + _gnutls_x509_read_value(pkcs7->signed_data, root, + &info->signer_serial); + if (ret < 0) { + gnutls_assert(); + goto fail; + } + } else { /* keyid */ + snprintf(root, sizeof(root), + "signerInfos.?%u.sid.subjectKeyIdentifier", idx + 1); + /* read the signature */ + ret = + _gnutls_x509_read_value(pkcs7->signed_data, root, + &info->issuer_keyid); + if (ret < 0) { + gnutls_assert(); + } + } + + if (info->issuer_keyid.data == NULL && info->issuer_dn.data == NULL) { + ret = gnutls_assert_val(GNUTLS_E_PARSING_ERROR); + goto fail; + } + + /* read the signing time */ + for (i = 0;; i++) { + snprintf(root, sizeof(root), + "signerInfos.?%u.signedAttrs.?%u.type", idx + 1, + i + 1); + len = sizeof(oid) - 1; + ret = asn1_read_value(pkcs7->signed_data, root, oid, &len); + if (ret != ASN1_SUCCESS) { + break; + } + + snprintf(root, sizeof(root), + "signerInfos.?%u.signedAttrs.?%u.values.?1", idx + 1, + i + 1); + ret = _gnutls_x509_read_value(pkcs7->signed_data, root, &tmp); + if (ret == GNUTLS_E_ASN1_ELEMENT_NOT_FOUND) { + tmp.data = NULL; + tmp.size = 0; + } else if (ret < 0) { + gnutls_assert(); + goto fail; + } + + ret = gnutls_pkcs7_add_attr(&info->signed_attrs, oid, &tmp, 0); + gnutls_free(tmp.data); + + if (ret < 0) { + gnutls_assert(); + goto fail; + } + + if (strcmp(oid, ATTR_SIGNING_TIME) == 0) { + info->signing_time = parse_time(pkcs7, root); + } + } + + /* read the unsigned attrs */ + for (i = 0;; i++) { + snprintf(root, sizeof(root), + "signerInfos.?%u.unsignedAttrs.?%u.type", idx + 1, + i + 1); + len = sizeof(oid) - 1; + ret = asn1_read_value(pkcs7->signed_data, root, oid, &len); + if (ret != ASN1_SUCCESS) { + break; + } + + snprintf(root, sizeof(root), + "signerInfos.?%u.unsignedAttrs.?%u.values.?1", idx + 1, + i + 1); + ret = _gnutls_x509_read_value(pkcs7->signed_data, root, &tmp); + if (ret == GNUTLS_E_ASN1_ELEMENT_NOT_FOUND) { + tmp.data = NULL; + tmp.size = 0; + } else if (ret < 0) { + gnutls_assert(); + goto fail; + } + + ret = + gnutls_pkcs7_add_attr(&info->unsigned_attrs, oid, &tmp, 0); + gnutls_free(tmp.data); + + if (ret < 0) { + gnutls_assert(); + goto fail; + } + } + + return 0; + fail: + gnutls_free(tmp.data); + gnutls_pkcs7_signature_info_deinit(info); + return ret; + unsupp_algo: + return GNUTLS_E_UNKNOWN_ALGORITHM; +} + +/* Verifies that the hash attribute ATTR_MESSAGE_DIGEST is present + * and matches our calculated hash */ +static int verify_hash_attr(gnutls_pkcs7_t pkcs7, const char *root, + gnutls_sign_algorithm_t algo, + const gnutls_datum_t *data) +{ + unsigned hash; + gnutls_datum_t tmp = { NULL, 0 }; + gnutls_datum_t tmp2 = { NULL, 0 }; + uint8_t hash_output[MAX_HASH_SIZE]; + unsigned hash_size, i; + char oid[MAX_OID_SIZE]; + char name[256]; + unsigned msg_digest_ok = 0; + unsigned num_cont_types = 0; + int ret; + + hash = gnutls_sign_get_hash_algorithm(algo); + + /* hash the data */ + if (hash == GNUTLS_DIG_UNKNOWN) + return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); + + hash_size = gnutls_hash_get_len(hash); + + if (data == NULL || data->data == NULL) { + data = &pkcs7->der_signed_data; + } + + if (data->size == 0) { + return gnutls_assert_val(GNUTLS_E_NO_EMBEDDED_DATA); + } + + ret = gnutls_hash_fast(hash, data->data, data->size, hash_output); + if (ret < 0) + return gnutls_assert_val(ret); + + /* now verify that hash matches */ + for (i = 0;; i++) { + snprintf(name, sizeof(name), "%s.signedAttrs.?%u", root, i + 1); + + ret = _gnutls_x509_decode_and_read_attribute(pkcs7->signed_data, + name, oid, + sizeof(oid), &tmp, + 1, 0); + if (ret < 0) { + if (ret == GNUTLS_E_ASN1_ELEMENT_NOT_FOUND) + break; + return gnutls_assert_val(ret); + } + + if (strcmp(oid, ATTR_MESSAGE_DIGEST) == 0) { + ret = + _gnutls_x509_decode_string(ASN1_ETYPE_OCTET_STRING, + tmp.data, tmp.size, + &tmp2, 0); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + if (tmp2.size == hash_size + && memcmp(hash_output, tmp2.data, tmp2.size) == 0) { + msg_digest_ok = 1; + } else { + gnutls_assert(); + } + } else if (strcmp(oid, ATTR_CONTENT_TYPE) == 0) { + if (num_cont_types > 0) { + gnutls_assert(); + ret = GNUTLS_E_PARSING_ERROR; + goto cleanup; + } + + num_cont_types++; + + /* check if it matches */ + ret = + _gnutls_x509_get_raw_field(pkcs7->signed_data, + "encapContentInfo.eContentType", + &tmp2); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + if (tmp2.size != tmp.size + || memcmp(tmp.data, tmp2.data, tmp2.size) != 0) { + gnutls_assert(); + ret = GNUTLS_E_PARSING_ERROR; + goto cleanup; + } + } + + gnutls_free(tmp.data); + gnutls_free(tmp2.data); + } + + if (msg_digest_ok) + ret = 0; + else + ret = gnutls_assert_val(GNUTLS_E_PK_SIG_VERIFY_FAILED); + + cleanup: + gnutls_free(tmp.data); + gnutls_free(tmp2.data); + return ret; +} + +/* Returns the data to be used for signature verification. PKCS #7 + * decided that this should not be an easy task. + */ +static int figure_pkcs7_sigdata(gnutls_pkcs7_t pkcs7, const char *root, + const gnutls_datum_t * data, + gnutls_sign_algorithm_t algo, + gnutls_datum_t * sigdata) +{ + int ret; + char name[256]; + + snprintf(name, sizeof(name), "%s.signedAttrs", root); + /* read the signature */ + ret = _gnutls_x509_get_raw_field(pkcs7->signed_data, name, sigdata); + if (ret == 0) { + /* verify that hash matches */ + ret = verify_hash_attr(pkcs7, root, algo, data); + if (ret < 0) + return gnutls_assert_val(ret); + + if (sigdata->size > 0) + sigdata->data[0] = 0x31; + + return 0; + } + + /* We have no signedAttrs. Use the provided data, or the encapsulated */ + if (data == NULL || data->data == NULL) { + return _gnutls_set_datum(sigdata, pkcs7->der_signed_data.data, pkcs7->der_signed_data.size); + } + + return _gnutls_set_datum(sigdata, data->data, data->size); +} + +/** + * gnutls_pkcs7_get_embedded_data: + * @pkcs7: should contain a gnutls_pkcs7_t type + * @flags: must be zero or %GNUTLS_PKCS7_EDATA_GET_RAW + * @data: will hold the embedded data in the provided structure + * + * This function will return the data embedded in the signature of + * the PKCS7 structure. If no data are available then + * %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE will be returned. + * + * The returned data must be de-allocated using gnutls_free(). + * + * Note, that this function returns the exact same data that are + * authenticated. If the %GNUTLS_PKCS7_EDATA_GET_RAW flag is provided, + * the returned data will be including the wrapping tag/value as + * they are encoded in the structure. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 3.4.8 + **/ +int +gnutls_pkcs7_get_embedded_data(gnutls_pkcs7_t pkcs7, unsigned flags, + gnutls_datum_t *data) +{ + if (pkcs7 == NULL) + return GNUTLS_E_INVALID_REQUEST; + + if (pkcs7->der_signed_data.size == 0) + return gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE); + + if (flags & GNUTLS_PKCS7_EDATA_GET_RAW) { + if (pkcs7->signed_data == NULL) + return gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE); + + return _gnutls_x509_read_value(pkcs7->signed_data, "encapContentInfo.eContent", data); + } else { + return _gnutls_set_datum(data, pkcs7->der_signed_data.data, pkcs7->der_signed_data.size); + } +} + +/** + * gnutls_pkcs7_get_embedded_data_oid: + * @pkcs7: should contain a gnutls_pkcs7_t type + * + * This function will return the OID of the data embedded in the signature of + * the PKCS7 structure. If no data are available then %NULL will be + * returned. The returned value will be valid during the lifetime + * of the @pkcs7 structure. + * + * Returns: On success, a pointer to an OID string, %NULL on error. + * + * Since: 3.5.5 + **/ +const char * +gnutls_pkcs7_get_embedded_data_oid(gnutls_pkcs7_t pkcs7) +{ + if (pkcs7 == NULL || pkcs7->encap_data_oid[0] == 0) + return NULL; + + return pkcs7->encap_data_oid; +} + +/** + * gnutls_pkcs7_verify_direct: + * @pkcs7: should contain a #gnutls_pkcs7_t type + * @signer: the certificate believed to have signed the structure + * @idx: the index of the signature info to check + * @data: The data to be verified or %NULL + * @flags: Zero or an OR list of #gnutls_certificate_verify_flags + * + * This function will verify the provided data against the signature + * present in the SignedData of the PKCS #7 structure. If the data + * provided are NULL then the data in the encapsulatedContent field + * will be used instead. + * + * Note that, unlike gnutls_pkcs7_verify() this function does not + * verify the key purpose of the signer. It is expected for the caller + * to verify the intended purpose of the %signer -e.g., via gnutls_x509_crt_get_key_purpose_oid(), + * or gnutls_x509_crt_check_key_purpose(). + * + * Note also, that since GnuTLS 3.5.6 this function introduces checks in the + * end certificate (@signer), including time checks and key usage checks. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. A verification error results to a + * %GNUTLS_E_PK_SIG_VERIFY_FAILED and the lack of encapsulated data + * to verify to a %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE. + * + * Since: 3.4.2 + **/ +int gnutls_pkcs7_verify_direct(gnutls_pkcs7_t pkcs7, + gnutls_x509_crt_t signer, + unsigned idx, + const gnutls_datum_t *data, unsigned flags) +{ + int count, ret; + gnutls_datum_t tmpdata = { NULL, 0 }; + gnutls_pkcs7_signature_info_st info; + gnutls_datum_t sigdata = { NULL, 0 }; + char root[128]; + + memset(&info, 0, sizeof(info)); + + if (pkcs7 == NULL) + return GNUTLS_E_INVALID_REQUEST; + + ret = + asn1_number_of_elements(pkcs7->signed_data, "signerInfos", &count); + if (ret != ASN1_SUCCESS || idx + 1 > (unsigned)count) { + gnutls_assert(); + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + } + + ret = gnutls_pkcs7_get_signature_info(pkcs7, idx, &info); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + snprintf(root, sizeof(root), "signerInfos.?%u", idx + 1); + ret = figure_pkcs7_sigdata(pkcs7, root, data, info.algo, &sigdata); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = + gnutls_x509_crt_verify_data2(signer, info.algo, flags, &sigdata, + &info.sig); + if (ret < 0) { + gnutls_assert(); + } + + cleanup: + gnutls_free(tmpdata.data); + gnutls_free(sigdata.data); + gnutls_pkcs7_signature_info_deinit(&info); + + return ret; +} + +/* Finds the issuer of the given certificate (@cert) in the + * included in PKCS#7 list of certificates */ +static gnutls_x509_crt_t find_verified_issuer_of(gnutls_pkcs7_t pkcs7, + gnutls_x509_crt_t cert, + const char *purpose, + unsigned vflags) +{ + gnutls_x509_crt_t issuer = NULL; + int ret, count; + gnutls_datum_t tmp = { NULL, 0 }; + unsigned i, vtmp; + + count = gnutls_pkcs7_get_crt_count(pkcs7); + if (count < 0) { + gnutls_assert(); + return NULL; + } + + for (i = 0; i < (unsigned)count; i++) { + /* Try to find the signer in the appended list. */ + ret = gnutls_pkcs7_get_crt_raw2(pkcs7, i, &tmp); + if (ret < 0) { + gnutls_assert(); + goto fail; + } + + ret = gnutls_x509_crt_init(&issuer); + if (ret < 0) { + gnutls_assert(); + goto fail; + } + + ret = gnutls_x509_crt_import(issuer, &tmp, GNUTLS_X509_FMT_DER); + if (ret < 0) { + gnutls_assert(); + goto fail; + } + + if (!gnutls_x509_crt_check_issuer(cert, issuer)) { + gnutls_assert(); + goto skip; + } + + ret = gnutls_x509_crt_verify(cert, &issuer, 1, vflags|GNUTLS_VERIFY_DO_NOT_ALLOW_SAME, &vtmp); + if (ret < 0 || vtmp != 0 || + (purpose != NULL && !_gnutls_check_key_purpose(issuer, purpose, 0))) { + gnutls_assert(); /* maybe next one is trusted */ + _gnutls_cert_log("failed verification with", issuer); + skip: + gnutls_x509_crt_deinit(issuer); + issuer = NULL; + gnutls_free(tmp.data); + continue; + } + + _gnutls_cert_log("issued by", issuer); + + /* we found a signer we trust. let's return it */ + break; + } + + if (issuer == NULL) { + gnutls_assert(); + return NULL; + } + goto cleanup; + + fail: + if (issuer) { + gnutls_x509_crt_deinit(issuer); + issuer = NULL; + } + + cleanup: + gnutls_free(tmp.data); + + return issuer; +} + +/* Finds a certificate that is issued by @issuer -if given-, and matches + * either the serial number or the key ID (both in @info) . + */ +static gnutls_x509_crt_t find_child_of_with_serial(gnutls_pkcs7_t pkcs7, + gnutls_x509_crt_t issuer, + const char *purpose, + gnutls_pkcs7_signature_info_st *info) +{ + gnutls_x509_crt_t crt = NULL; + int ret, count; + uint8_t tmp[128]; + size_t tmp_size; + gnutls_datum_t tmpdata = { NULL, 0 }; + unsigned i; + + count = gnutls_pkcs7_get_crt_count(pkcs7); + if (count < 0) { + gnutls_assert(); + return NULL; + } + + for (i = 0; i < (unsigned)count; i++) { + /* Try to find the crt in the appended list. */ + ret = gnutls_pkcs7_get_crt_raw2(pkcs7, i, &tmpdata); + if (ret < 0) { + gnutls_assert(); + goto fail; + } + + ret = gnutls_x509_crt_init(&crt); + if (ret < 0) { + gnutls_assert(); + goto fail; + } + + ret = gnutls_x509_crt_import(crt, &tmpdata, GNUTLS_X509_FMT_DER); + if (ret < 0) { + gnutls_assert(); + goto fail; + } + + if (issuer != NULL) { + if (!gnutls_x509_crt_check_issuer(crt, issuer)) { + gnutls_assert(); + goto skip; + } + } + + if (purpose) { + ret = + _gnutls_check_key_purpose(crt, purpose, 0); + if (ret == 0) { + _gnutls_cert_log("key purpose unacceptable", crt); + goto skip; + } + } + + if (info->signer_serial.size > 0) { + tmp_size = sizeof(tmp); + ret = gnutls_x509_crt_get_serial(crt, tmp, &tmp_size); + if (ret < 0) { + gnutls_assert(); + goto skip; + } + + if (tmp_size != info->signer_serial.size + || memcmp(info->signer_serial.data, tmp, + tmp_size) != 0) { + _gnutls_cert_log("doesn't match serial", crt); + gnutls_assert(); + goto skip; + } + } else if (info->issuer_keyid.size > 0) { + tmp_size = sizeof(tmp); + ret = gnutls_x509_crt_get_subject_key_id(crt, tmp, &tmp_size, NULL); + if (ret < 0) { + gnutls_assert(); + goto skip; + } + + if (tmp_size != info->issuer_keyid.size + || memcmp(info->issuer_keyid.data, tmp, + tmp_size) != 0) { + _gnutls_cert_log("doesn't match key ID", crt); + gnutls_assert(); + skip: + gnutls_x509_crt_deinit(crt); + crt = NULL; + gnutls_free(tmpdata.data); + continue; + } + } else { + gnutls_assert(); + crt = NULL; + goto fail; + } + + _gnutls_cert_log("signer is", crt); + + /* we found the child with the given serial or key ID */ + break; + } + + if (crt == NULL) { + gnutls_assert(); + return NULL; + } + + goto cleanup; + fail: + if (crt) { + gnutls_x509_crt_deinit(crt); + crt = NULL; + } + + cleanup: + gnutls_free(tmpdata.data); + + return crt; +} + +static +gnutls_x509_crt_t find_signer(gnutls_pkcs7_t pkcs7, gnutls_x509_trust_list_t tl, + gnutls_typed_vdata_st * vdata, + unsigned vdata_size, + unsigned vflags, + gnutls_pkcs7_signature_info_st * info) +{ + gnutls_x509_crt_t issuer = NULL; + gnutls_x509_crt_t signer = NULL; + int ret; + gnutls_datum_t tmp = { NULL, 0 }; + unsigned i, vtmp; + const char *purpose = NULL; + + if (info->issuer_keyid.data) { + ret = + gnutls_x509_trust_list_get_issuer_by_subject_key_id(tl, + NULL, + &info-> + issuer_keyid, + &signer, + 0); + if (ret < 0) { + gnutls_assert(); + signer = NULL; + } + } + + /* get key purpose */ + for (i = 0; i < vdata_size; i++) { + if (vdata[i].type == GNUTLS_DT_KEY_PURPOSE_OID) { + purpose = (char *)vdata[i].data; + break; + } + } + + /* this will give us the issuer of the signer (wtf) */ + if (info->issuer_dn.data && signer == NULL) { + ret = + gnutls_x509_trust_list_get_issuer_by_dn(tl, + &info->issuer_dn, + &issuer, 0); + if (ret < 0) { + gnutls_assert(); + signer = NULL; + } + + if (issuer) { + /* try to find the actual signer in the list of + * certificates */ + signer = find_child_of_with_serial(pkcs7, issuer, purpose, info); + if (signer == NULL) { + gnutls_assert(); + goto fail; + } + + gnutls_x509_crt_deinit(issuer); + issuer = NULL; + } + } + + if (signer == NULL) { + /* get the signer from the pkcs7 list; the one that matches serial + * or key ID */ + signer = find_child_of_with_serial(pkcs7, NULL, purpose, info); + if (signer == NULL) { + gnutls_assert(); + goto fail; + } + + /* if the signer cannot be verified from our trust list, make a chain of certificates + * starting from the identified signer, to a root we know. */ + ret = gnutls_x509_trust_list_verify_crt2(tl, &signer, 1, vdata, vdata_size, vflags, &vtmp, NULL); + if (ret < 0 || vtmp != 0) { + gnutls_x509_crt_t prev = NULL; + + issuer = signer; + /* construct a chain */ + do { + if (prev && prev != signer) { + gnutls_x509_crt_deinit(prev); + } + prev = issuer; + + issuer = find_verified_issuer_of(pkcs7, issuer, purpose, vflags); + + if (issuer != NULL && gnutls_x509_crt_check_issuer(issuer, issuer)) { + if (prev && prev != signer) + gnutls_x509_crt_deinit(prev); + prev = issuer; + break; + } + } while(issuer != NULL); + + issuer = prev; /* the last we have seen */ + + if (issuer == NULL) { + gnutls_assert(); + goto fail; + } + + ret = gnutls_x509_trust_list_verify_crt2(tl, &issuer, 1, vdata, vdata_size, vflags, &vtmp, NULL); + if (ret < 0 || vtmp != 0) { + /* could not construct a valid chain */ + _gnutls_reason_log("signer's chain failed trust list verification", vtmp); + gnutls_assert(); + goto fail; + } + } + } else { + /* verify that the signer we got is trusted */ + ret = gnutls_x509_trust_list_verify_crt2(tl, &signer, 1, vdata, vdata_size, vflags, &vtmp, NULL); + if (ret < 0 || vtmp != 0) { + /* could not construct a valid chain */ + _gnutls_reason_log("signer failed trust list verification", vtmp); + gnutls_assert(); + goto fail; + } + } + + if (signer == NULL) { + gnutls_assert(); + goto fail; + } + + goto cleanup; + + fail: + if (signer != NULL) { + if (issuer == signer) + issuer = NULL; + gnutls_x509_crt_deinit(signer); + signer = NULL; + } + + cleanup: + if (issuer != NULL) { + gnutls_x509_crt_deinit(issuer); + issuer = NULL; + } + gnutls_free(tmp.data); + + return signer; +} + +/** + * gnutls_pkcs7_verify: + * @pkcs7: should contain a #gnutls_pkcs7_t type + * @tl: A list of trusted certificates + * @vdata: an array of typed data + * @vdata_size: the number of data elements + * @idx: the index of the signature info to check + * @data: The data to be verified or %NULL + * @flags: Zero or an OR list of #gnutls_certificate_verify_flags + * + * This function will verify the provided data against the signature + * present in the SignedData of the PKCS #7 structure. If the data + * provided are NULL then the data in the encapsulatedContent field + * will be used instead. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. A verification error results to a + * %GNUTLS_E_PK_SIG_VERIFY_FAILED and the lack of encapsulated data + * to verify to a %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE. + * + * Since: 3.4.2 + **/ +int gnutls_pkcs7_verify(gnutls_pkcs7_t pkcs7, + gnutls_x509_trust_list_t tl, + gnutls_typed_vdata_st *vdata, + unsigned int vdata_size, + unsigned idx, + const gnutls_datum_t *data, unsigned flags) +{ + int count, ret; + gnutls_datum_t tmpdata = { NULL, 0 }; + gnutls_pkcs7_signature_info_st info; + gnutls_x509_crt_t signer; + gnutls_datum_t sigdata = { NULL, 0 }; + char root[128]; + + memset(&info, 0, sizeof(info)); + + if (pkcs7 == NULL) + return GNUTLS_E_INVALID_REQUEST; + + ret = + asn1_number_of_elements(pkcs7->signed_data, "signerInfos", &count); + if (ret != ASN1_SUCCESS || idx + 1 > (unsigned)count) { + gnutls_assert(); + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + } + + /* read data */ + ret = gnutls_pkcs7_get_signature_info(pkcs7, idx, &info); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + snprintf(root, sizeof(root), "signerInfos.?%u", idx + 1); + ret = figure_pkcs7_sigdata(pkcs7, root, data, info.algo, &sigdata); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + signer = find_signer(pkcs7, tl, vdata, vdata_size, flags, &info); + if (signer) { + ret = + gnutls_x509_crt_verify_data3(signer, info.algo, vdata, vdata_size, + &sigdata, &info.sig, flags); + if (ret < 0) { + _gnutls_cert_log("failed struct verification with", signer); + gnutls_assert(); + } + gnutls_x509_crt_deinit(signer); + } else { + gnutls_assert(); + ret = GNUTLS_E_PK_SIG_VERIFY_FAILED; + } + + cleanup: + gnutls_free(tmpdata.data); + gnutls_free(sigdata.data); + gnutls_pkcs7_signature_info_deinit(&info); + + return ret; +} + +static void disable_opt_fields(gnutls_pkcs7_t pkcs7) +{ + int result; + int count; + + /* disable the optional fields */ + result = asn1_number_of_elements(pkcs7->signed_data, "crls", &count); + if (result != ASN1_SUCCESS || count == 0) { + (void)asn1_write_value(pkcs7->signed_data, "crls", NULL, 0); + } + + result = + asn1_number_of_elements(pkcs7->signed_data, "certificates", &count); + if (result != ASN1_SUCCESS || count == 0) { + (void)asn1_write_value(pkcs7->signed_data, "certificates", NULL, 0); + } + + return; +} + +static int reencode(gnutls_pkcs7_t pkcs7) +{ + int result; + + if (pkcs7->signed_data != NULL) { + disable_opt_fields(pkcs7); + + /* Replace the old content with the new + */ + result = + _gnutls_x509_der_encode_and_copy(pkcs7->signed_data, "", + pkcs7->pkcs7, "content", + 0); + if (result < 0) { + return gnutls_assert_val(result); + } + + /* Write the content type of the signed data + */ + result = + asn1_write_value(pkcs7->pkcs7, "contentType", + SIGNED_DATA_OID, 1); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + } + return 0; +} + +/** + * gnutls_pkcs7_export: + * @pkcs7: The pkcs7 type + * @format: the format of output params. One of PEM or DER. + * @output_data: will contain a structure PEM or DER encoded + * @output_data_size: holds the size of output_data (and will be + * replaced by the actual size of parameters) + * + * This function will export the pkcs7 structure to DER or PEM format. + * + * If the buffer provided is not long enough to hold the output, then + * *@output_data_size is updated and %GNUTLS_E_SHORT_MEMORY_BUFFER + * will be returned. + * + * If the structure is PEM encoded, it will have a header + * of "BEGIN PKCS7". + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int +gnutls_pkcs7_export(gnutls_pkcs7_t pkcs7, + gnutls_x509_crt_fmt_t format, void *output_data, + size_t * output_data_size) +{ + int ret; + if (pkcs7 == NULL) + return GNUTLS_E_INVALID_REQUEST; + + if ((ret = reencode(pkcs7)) < 0) + return gnutls_assert_val(ret); + + return _gnutls_x509_export_int(pkcs7->pkcs7, format, PEM_PKCS7, + output_data, output_data_size); +} + +/** + * gnutls_pkcs7_export2: + * @pkcs7: The pkcs7 type + * @format: the format of output params. One of PEM or DER. + * @out: will contain a structure PEM or DER encoded + * + * This function will export the pkcs7 structure to DER or PEM format. + * + * The output buffer is allocated using gnutls_malloc(). + * + * If the structure is PEM encoded, it will have a header + * of "BEGIN PKCS7". + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 3.1.3 + **/ +int +gnutls_pkcs7_export2(gnutls_pkcs7_t pkcs7, + gnutls_x509_crt_fmt_t format, gnutls_datum_t * out) +{ + int ret; + if (pkcs7 == NULL) + return GNUTLS_E_INVALID_REQUEST; + + if ((ret = reencode(pkcs7)) < 0) + return gnutls_assert_val(ret); + + return _gnutls_x509_export_int2(pkcs7->pkcs7, format, PEM_PKCS7, out); +} + +/* Creates an empty signed data structure in the pkcs7 + * structure and returns a handle to the signed data. + */ +static int create_empty_signed_data(asn1_node pkcs7, asn1_node * sdata) +{ + int result; + + *sdata = NULL; + + if ((result = asn1_create_element + (_gnutls_get_pkix(), "PKIX1.pkcs-7-SignedData", + sdata)) != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + /* Use version 1 + */ + result = asn1_write_value(*sdata, "version", &one, 1); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + /* Use no digest algorithms + */ + + /* id-data */ + result = + asn1_write_value(*sdata, "encapContentInfo.eContentType", + DIGESTED_DATA_OID, 1); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + result = asn1_write_value(*sdata, "encapContentInfo.eContent", NULL, 0); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + /* Add no certificates. + */ + + /* Add no crls. + */ + + /* Add no signerInfos. + */ + + return 0; + + cleanup: + asn1_delete_structure(sdata); + return result; + +} + +/** + * gnutls_pkcs7_set_crt_raw: + * @pkcs7: The pkcs7 type + * @crt: the DER encoded certificate to be added + * + * This function will add a certificate to the PKCS7 or RFC2630 + * certificate set. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int gnutls_pkcs7_set_crt_raw(gnutls_pkcs7_t pkcs7, const gnutls_datum_t * crt) +{ + int result; + + if (pkcs7 == NULL) + return GNUTLS_E_INVALID_REQUEST; + + /* If the signed data are uninitialized + * then create them. + */ + if (pkcs7->signed_data == NULL) { + /* The pkcs7 structure is new, so create the + * signedData. + */ + result = + create_empty_signed_data(pkcs7->pkcs7, &pkcs7->signed_data); + if (result < 0) { + gnutls_assert(); + return result; + } + } + + /* Step 2. Append the new certificate. + */ + + result = asn1_write_value(pkcs7->signed_data, "certificates", "NEW", 1); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + result = + asn1_write_value(pkcs7->signed_data, "certificates.?LAST", + "certificate", 1); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + result = + asn1_write_value(pkcs7->signed_data, + "certificates.?LAST.certificate", crt->data, + crt->size); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + result = 0; + + cleanup: + return result; +} + +/** + * gnutls_pkcs7_set_crt: + * @pkcs7: The pkcs7 type + * @crt: the certificate to be copied. + * + * This function will add a parsed certificate to the PKCS7 or + * RFC2630 certificate set. This is a wrapper function over + * gnutls_pkcs7_set_crt_raw() . + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int gnutls_pkcs7_set_crt(gnutls_pkcs7_t pkcs7, gnutls_x509_crt_t crt) +{ + int ret; + gnutls_datum_t data; + + if (pkcs7 == NULL) + return GNUTLS_E_INVALID_REQUEST; + + ret = _gnutls_x509_der_encode(crt->cert, "", &data, 0); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + ret = gnutls_pkcs7_set_crt_raw(pkcs7, &data); + + _gnutls_free_datum(&data); + + if (ret < 0) { + gnutls_assert(); + return ret; + } + + return 0; +} + +/** + * gnutls_pkcs7_delete_crt: + * @pkcs7: The pkcs7 type + * @indx: the index of the certificate to delete + * + * This function will delete a certificate from a PKCS7 or RFC2630 + * certificate set. Index starts from 0. Returns 0 on success. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int gnutls_pkcs7_delete_crt(gnutls_pkcs7_t pkcs7, int indx) +{ + int result; + char root2[MAX_NAME_SIZE]; + + if (pkcs7 == NULL) + return GNUTLS_E_INVALID_REQUEST; + + /* Step 2. Delete the certificate. + */ + + snprintf(root2, sizeof(root2), "certificates.?%d", indx + 1); + + result = asn1_write_value(pkcs7->signed_data, root2, NULL, 0); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + return 0; + + cleanup: + return result; +} + +/* Read and write CRLs + */ + +/** + * gnutls_pkcs7_get_crl_raw2: + * @pkcs7: The pkcs7 type + * @indx: contains the index of the crl to extract + * @crl: will contain the contents of the CRL in an allocated buffer + * + * This function will return a DER encoded CRL of the PKCS7 or RFC2630 crl set. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. After the last crl has been read + * %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE will be returned. + * + * Since: 3.4.2 + **/ +int +gnutls_pkcs7_get_crl_raw2(gnutls_pkcs7_t pkcs7, + unsigned indx, gnutls_datum_t * crl) +{ + int result; + char root2[MAX_NAME_SIZE]; + gnutls_datum_t tmp = { NULL, 0 }; + int start, end; + + if (pkcs7 == NULL || crl == NULL) + return GNUTLS_E_INVALID_REQUEST; + + result = _gnutls_x509_read_value(pkcs7->pkcs7, "content", &tmp); + if (result < 0) { + gnutls_assert(); + goto cleanup; + } + + /* Step 2. Parse the CertificateSet + */ + + snprintf(root2, sizeof(root2), "crls.?%u", indx + 1); + + /* Get the raw CRL + */ + result = + asn1_der_decoding_startEnd(pkcs7->signed_data, tmp.data, tmp.size, + root2, &start, &end); + + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + end = end - start + 1; + + result = _gnutls_set_datum(crl, &tmp.data[start], end); + + cleanup: + _gnutls_free_datum(&tmp); + return result; +} + +/** + * gnutls_pkcs7_get_crl_raw: + * @pkcs7: The pkcs7 type + * @indx: contains the index of the crl to extract + * @crl: the contents of the crl will be copied there (may be null) + * @crl_size: should hold the size of the crl + * + * This function will return a crl of the PKCS7 or RFC2630 crl set. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. If the provided buffer is not long enough, + * then @crl_size is updated and %GNUTLS_E_SHORT_MEMORY_BUFFER is + * returned. After the last crl has been read + * %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE will be returned. + **/ +int +gnutls_pkcs7_get_crl_raw(gnutls_pkcs7_t pkcs7, + unsigned indx, void *crl, size_t * crl_size) +{ + int ret; + gnutls_datum_t tmp = { NULL, 0 }; + + ret = gnutls_pkcs7_get_crl_raw2(pkcs7, indx, &tmp); + if (ret < 0) + return gnutls_assert_val(ret); + + if ((unsigned)tmp.size > *crl_size) { + *crl_size = tmp.size; + ret = GNUTLS_E_SHORT_MEMORY_BUFFER; + goto cleanup; + } + + assert(tmp.data != NULL); + + *crl_size = tmp.size; + if (crl) + memcpy(crl, tmp.data, tmp.size); + + cleanup: + _gnutls_free_datum(&tmp); + return ret; +} + +/** + * gnutls_pkcs7_get_crl_count: + * @pkcs7: The pkcs7 type + * + * This function will return the number of certificates in the PKCS7 + * or RFC2630 crl set. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int gnutls_pkcs7_get_crl_count(gnutls_pkcs7_t pkcs7) +{ + int result, count; + + if (pkcs7 == NULL) + return GNUTLS_E_INVALID_REQUEST; + + /* Step 2. Count the CertificateSet */ + + result = asn1_number_of_elements(pkcs7->signed_data, "crls", &count); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return 0; /* no crls */ + } + + return count; + +} + +/** + * gnutls_pkcs7_set_crl_raw: + * @pkcs7: The pkcs7 type + * @crl: the DER encoded crl to be added + * + * This function will add a crl to the PKCS7 or RFC2630 crl set. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int gnutls_pkcs7_set_crl_raw(gnutls_pkcs7_t pkcs7, const gnutls_datum_t * crl) +{ + int result; + + if (pkcs7 == NULL) + return GNUTLS_E_INVALID_REQUEST; + + /* If the signed data are uninitialized + * then create them. + */ + if (pkcs7->signed_data == NULL) { + /* The pkcs7 structure is new, so create the + * signedData. + */ + result = + create_empty_signed_data(pkcs7->pkcs7, &pkcs7->signed_data); + if (result < 0) { + gnutls_assert(); + return result; + } + } + + /* Step 2. Append the new crl. + */ + + result = asn1_write_value(pkcs7->signed_data, "crls", "NEW", 1); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + result = + asn1_write_value(pkcs7->signed_data, "crls.?LAST", crl->data, + crl->size); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + result = 0; + + cleanup: + return result; +} + +/** + * gnutls_pkcs7_set_crl: + * @pkcs7: The pkcs7 type + * @crl: the DER encoded crl to be added + * + * This function will add a parsed CRL to the PKCS7 or RFC2630 crl + * set. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int gnutls_pkcs7_set_crl(gnutls_pkcs7_t pkcs7, gnutls_x509_crl_t crl) +{ + int ret; + gnutls_datum_t data; + + if (pkcs7 == NULL) + return GNUTLS_E_INVALID_REQUEST; + + ret = _gnutls_x509_der_encode(crl->crl, "", &data, 0); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + ret = gnutls_pkcs7_set_crl_raw(pkcs7, &data); + + _gnutls_free_datum(&data); + + if (ret < 0) { + gnutls_assert(); + return ret; + } + + return 0; +} + +/** + * gnutls_pkcs7_delete_crl: + * @pkcs7: The pkcs7 type + * @indx: the index of the crl to delete + * + * This function will delete a crl from a PKCS7 or RFC2630 crl set. + * Index starts from 0. Returns 0 on success. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int gnutls_pkcs7_delete_crl(gnutls_pkcs7_t pkcs7, int indx) +{ + int result; + char root2[MAX_NAME_SIZE]; + + if (pkcs7 == NULL) + return GNUTLS_E_INVALID_REQUEST; + + /* Delete the crl. + */ + + snprintf(root2, sizeof(root2), "crls.?%d", indx + 1); + + result = asn1_write_value(pkcs7->signed_data, root2, NULL, 0); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + return 0; + + cleanup: + return result; +} + +static int write_signer_id(asn1_node c2, const char *root, + gnutls_x509_crt_t signer, unsigned flags) +{ + int result; + size_t serial_size; + uint8_t serial[128]; + char name[256]; + + if (flags & GNUTLS_PKCS7_WRITE_SPKI) { + const uint8_t ver = 3; + + snprintf(name, sizeof(name), "%s.version", root); + result = asn1_write_value(c2, name, &ver, 1); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + snprintf(name, sizeof(name), "%s.sid", root); + result = asn1_write_value(c2, name, "subjectKeyIdentifier", 1); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + serial_size = sizeof(serial); + result = + gnutls_x509_crt_get_subject_key_id(signer, serial, + &serial_size, NULL); + if (result < 0) + return gnutls_assert_val(result); + + snprintf(name, sizeof(name), "%s.subjectKeyIdentifier", root); + result = asn1_write_value(c2, name, serial, serial_size); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + } else { + serial_size = sizeof(serial); + result = + gnutls_x509_crt_get_serial(signer, serial, &serial_size); + if (result < 0) + return gnutls_assert_val(result); + + snprintf(name, sizeof(name), "%s.sid", root); + result = asn1_write_value(c2, name, "issuerAndSerialNumber", 1); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + snprintf(name, sizeof(name), + "%s.sid.issuerAndSerialNumber.serialNumber", root); + result = asn1_write_value(c2, name, serial, serial_size); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + snprintf(name, sizeof(name), + "%s.sid.issuerAndSerialNumber.issuer", root); + result = + asn1_copy_node(c2, name, signer->cert, + "tbsCertificate.issuer"); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + } + + return 0; +} + +static int add_attrs(asn1_node c2, const char *root, gnutls_pkcs7_attrs_t attrs, + unsigned already_set) +{ + char name[256]; + gnutls_pkcs7_attrs_st *p = attrs; + int result; + + if (attrs == NULL) { + /* if there are no other attributes delete that field */ + if (already_set == 0) + (void)asn1_write_value(c2, root, NULL, 0); + } else { + while (p != NULL) { + result = asn1_write_value(c2, root, "NEW", 1); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + snprintf(name, sizeof(name), "%s.?LAST.type", root); + result = asn1_write_value(c2, name, p->oid, 1); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + snprintf(name, sizeof(name), "%s.?LAST.values", root); + result = asn1_write_value(c2, name, "NEW", 1); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + snprintf(name, sizeof(name), "%s.?LAST.values.?1", + root); + result = + asn1_write_value(c2, name, p->data.data, + p->data.size); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + p = p->next; + } + } + + return 0; +} + +static int write_attributes(asn1_node c2, const char *root, + const gnutls_datum_t * data, + const mac_entry_st * me, + gnutls_pkcs7_attrs_t other_attrs, unsigned flags) +{ + char name[256]; + int result, ret; + uint8_t digest[MAX_HASH_SIZE]; + gnutls_datum_t tmp = { NULL, 0 }; + unsigned digest_size; + unsigned already_set = 0; + + if (flags & GNUTLS_PKCS7_INCLUDE_TIME) { + if (data == NULL || data->data == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + /* Add time */ + result = asn1_write_value(c2, root, "NEW", 1); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(result); + return ret; + } + + snprintf(name, sizeof(name), "%s.?LAST.type", root); + result = asn1_write_value(c2, name, ATTR_SIGNING_TIME, 1); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(result); + return ret; + } + + snprintf(name, sizeof(name), "%s.?LAST.values", root); + result = asn1_write_value(c2, name, "NEW", 1); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(result); + return ret; + } + + snprintf(name, sizeof(name), "%s.?LAST.values.?1", root); + ret = _gnutls_x509_set_raw_time(c2, name, gnutls_time(0)); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + already_set = 1; + } + + ret = add_attrs(c2, root, other_attrs, already_set); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + if (already_set != 0 || other_attrs != NULL) { + /* Add content type */ + result = asn1_write_value(c2, root, "NEW", 1); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(result); + return ret; + } + + snprintf(name, sizeof(name), "%s.?LAST.type", root); + result = asn1_write_value(c2, name, ATTR_CONTENT_TYPE, 1); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(result); + return ret; + } + + snprintf(name, sizeof(name), "%s.?LAST.values", root); + result = asn1_write_value(c2, name, "NEW", 1); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(result); + return ret; + } + + ret = + _gnutls_x509_get_raw_field(c2, + "encapContentInfo.eContentType", + &tmp); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + snprintf(name, sizeof(name), "%s.?LAST.values.?1", root); + result = asn1_write_value(c2, name, tmp.data, tmp.size); + gnutls_free(tmp.data); + + if (result != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(result); + return ret; + } + + /* If we add any attribute we should add them all */ + /* Add hash */ + digest_size = _gnutls_hash_get_algo_len(me); + ret = gnutls_hash_fast(MAC_TO_DIG(me->id), data->data, data->size, digest); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + result = asn1_write_value(c2, root, "NEW", 1); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(result); + return ret; + } + + snprintf(name, sizeof(name), "%s.?LAST", root); + ret = + _gnutls_x509_encode_and_write_attribute(ATTR_MESSAGE_DIGEST, + c2, name, digest, + digest_size, 1); + if (ret < 0) { + gnutls_assert(); + return ret; + } + } + + return 0; +} + +/** + * gnutls_pkcs7_sign: + * @pkcs7: should contain a #gnutls_pkcs7_t type + * @signer: the certificate to sign the structure + * @signer_key: the key to sign the structure + * @data: The data to be signed or %NULL if the data are already embedded + * @signed_attrs: Any additional attributes to be included in the signed ones (or %NULL) + * @unsigned_attrs: Any additional attributes to be included in the unsigned ones (or %NULL) + * @dig: The digest algorithm to use for signing + * @flags: Should be zero or one of %GNUTLS_PKCS7 flags + * + * This function will add a signature in the provided PKCS #7 structure + * for the provided data. Multiple signatures can be made with different + * signers. + * + * The available flags are: + * %GNUTLS_PKCS7_EMBED_DATA, %GNUTLS_PKCS7_INCLUDE_TIME, %GNUTLS_PKCS7_INCLUDE_CERT, + * and %GNUTLS_PKCS7_WRITE_SPKI. They are explained in the #gnutls_pkcs7_sign_flags + * definition. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 3.4.2 + **/ +int gnutls_pkcs7_sign(gnutls_pkcs7_t pkcs7, + gnutls_x509_crt_t signer, + gnutls_privkey_t signer_key, + const gnutls_datum_t *data, + gnutls_pkcs7_attrs_t signed_attrs, + gnutls_pkcs7_attrs_t unsigned_attrs, + gnutls_digest_algorithm_t dig, unsigned flags) +{ + int ret, result; + gnutls_datum_t sigdata = { NULL, 0 }; + gnutls_datum_t signature = { NULL, 0 }; + const mac_entry_st *me = hash_to_entry(dig); + unsigned pk, sigalgo; + gnutls_x509_spki_st key_params, params; + const gnutls_sign_entry_st *se; + + if (pkcs7 == NULL || me == NULL) + return GNUTLS_E_INVALID_REQUEST; + + if (pkcs7->signed_data == NULL) { + result = + asn1_create_element(_gnutls_get_pkix(), + "PKIX1.pkcs-7-SignedData", + &pkcs7->signed_data); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(result); + goto cleanup; + } + + if (!(flags & GNUTLS_PKCS7_EMBED_DATA)) { + (void)asn1_write_value(pkcs7->signed_data, + "encapContentInfo.eContent", NULL, 0); + } + } + + result = asn1_write_value(pkcs7->signed_data, "version", &one, 1); + if (result != ASN1_SUCCESS) { + ret = _gnutls_asn2err(result); + goto cleanup; + } + + result = + asn1_write_value(pkcs7->signed_data, + "encapContentInfo.eContentType", DATA_OID, + 0); + if (result != ASN1_SUCCESS) { + ret = _gnutls_asn2err(result); + goto cleanup; + } + + if ((flags & GNUTLS_PKCS7_EMBED_DATA) && data->data) { /* embed data */ + ret = + _gnutls_x509_write_string(pkcs7->signed_data, + "encapContentInfo.eContent", data, + ASN1_ETYPE_OCTET_STRING); + if (ret < 0) { + goto cleanup; + } + } + + if (flags & GNUTLS_PKCS7_INCLUDE_CERT) { + ret = gnutls_pkcs7_set_crt(pkcs7, signer); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + } + + /* append digest info algorithm */ + result = + asn1_write_value(pkcs7->signed_data, "digestAlgorithms", "NEW", 1); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(result); + goto cleanup; + } + + result = + asn1_write_value(pkcs7->signed_data, + "digestAlgorithms.?LAST.algorithm", + _gnutls_x509_digest_to_oid(me), 1); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(result); + goto cleanup; + } + + (void)asn1_write_value(pkcs7->signed_data, + "digestAlgorithms.?LAST.parameters", NULL, 0); + + /* append signer's info */ + result = asn1_write_value(pkcs7->signed_data, "signerInfos", "NEW", 1); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(result); + goto cleanup; + } + + result = + asn1_write_value(pkcs7->signed_data, "signerInfos.?LAST.version", + &one, 1); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(result); + goto cleanup; + } + + result = + asn1_write_value(pkcs7->signed_data, + "signerInfos.?LAST.digestAlgorithm.algorithm", + _gnutls_x509_digest_to_oid(me), 1); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(result); + goto cleanup; + } + + (void)asn1_write_value(pkcs7->signed_data, + "signerInfos.?LAST.digestAlgorithm.parameters", NULL, + 0); + + ret = + write_signer_id(pkcs7->signed_data, "signerInfos.?LAST", signer, + flags); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = + add_attrs(pkcs7->signed_data, "signerInfos.?LAST.unsignedAttrs", + unsigned_attrs, 0); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = + write_attributes(pkcs7->signed_data, + "signerInfos.?LAST.signedAttrs", data, me, + signed_attrs, flags); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + disable_opt_fields(pkcs7); + + /* write the signature algorithm */ + pk = gnutls_x509_crt_get_pk_algorithm(signer, NULL); + + ret = _gnutls_privkey_get_spki_params(signer_key, &key_params); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = _gnutls_x509_crt_get_spki_params(signer, &key_params, ¶ms); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = _gnutls_privkey_update_spki_params(signer_key, pk, dig, 0, + ¶ms); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + se = _gnutls_pk_to_sign_entry(params.pk, dig); + if (se == NULL) { + ret = gnutls_assert_val(GNUTLS_E_UNSUPPORTED_SIGNATURE_ALGORITHM); + goto cleanup; + } + + /* RFC5652 is silent on what the values would be and initially I assumed that + * typical signature algorithms should be set. However RFC2315 (PKCS#7) mentions + * that a generic RSA OID should be used. We switch to this "unexpected" value + * because some implementations cannot cope with the "expected" signature values. + */ + params.legacy = 1; + ret = + _gnutls_x509_write_sign_params(pkcs7->signed_data, + "signerInfos.?LAST.signatureAlgorithm", + se, ¶ms); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + sigalgo = se->id; + + /* sign the data */ + ret = + figure_pkcs7_sigdata(pkcs7, "signerInfos.?LAST", data, sigalgo, + &sigdata); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + FIX_SIGN_PARAMS(params, flags, dig); + + ret = privkey_sign_and_hash_data(signer_key, se, + &sigdata, &signature, ¶ms); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + result = + asn1_write_value(pkcs7->signed_data, "signerInfos.?LAST.signature", + signature.data, signature.size); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(result); + goto cleanup; + } + + ret = 0; + + cleanup: + gnutls_free(sigdata.data); + gnutls_free(signature.data); + return ret; +} diff --git a/lib/x509/pkcs7_int.h b/lib/x509/pkcs7_int.h new file mode 100644 index 0000000..3d57124 --- /dev/null +++ b/lib/x509/pkcs7_int.h @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2003-2016 Free Software Foundation, Inc. + * Copyright (C) 2016 Red Hat, Inc. + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GnuTLS. + * + * The GnuTLS is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/> + * + */ + +#ifndef GNUTLS_LIB_X509_PKCS7_INT_H +#define GNUTLS_LIB_X509_PKCS7_INT_H + +#include <gnutls/x509.h> + +/* PKCS #7 + */ +#define DATA_OID "1.2.840.113549.1.7.1" +#define ENC_DATA_OID "1.2.840.113549.1.7.6" + +#define SIGNED_DATA_OID "1.2.840.113549.1.7.2" +#define DIGESTED_DATA_OID "1.2.840.113549.1.7.5" + + +typedef enum schema_id { + PBES2_GENERIC=1, /* when the algorithm is unknown, temporal use when reading only */ + PBES2_DES, /* the stuff in PKCS #5 */ + PBES2_3DES, + PBES2_AES_128, + PBES2_AES_192, + PBES2_AES_256, + PBES2_GOST28147_89_TC26Z, + PBES2_GOST28147_89_CPA, + PBES2_GOST28147_89_CPB, + PBES2_GOST28147_89_CPC, + PBES2_GOST28147_89_CPD, + PKCS12_3DES_SHA1, /* the stuff in PKCS #12 */ + PKCS12_ARCFOUR_SHA1, + PKCS12_RC2_40_SHA1, + PBES1_DES_MD5 /* openssl before 1.1.0 uses that by default */ +} schema_id; + +struct pkcs_cipher_schema_st { + unsigned int schema; + const char *name; + unsigned int flag; + unsigned int cipher; + unsigned pbes2; + const char *cipher_oid; + const char *write_oid; + const char *desc; + const char *iv_name; + unsigned decrypt_only; +}; + +const struct pkcs_cipher_schema_st *_gnutls_pkcs_schema_get(schema_id schema); + +struct pbe_enc_params { + gnutls_cipher_algorithm_t cipher; + uint8_t iv[MAX_CIPHER_BLOCK_SIZE]; + int iv_size; + char pbes2_oid[MAX_OID_SIZE]; /* when reading params, the OID is stored for info purposes */ +}; + +int +_gnutls_decrypt_pbes1_des_md5_data(const char *password, + unsigned password_len, + const struct pbkdf2_params *kdf_params, + const struct pbe_enc_params *enc_params, + const gnutls_datum_t *encrypted_data, + gnutls_datum_t *decrypted_data); + +int _gnutls_check_pkcs_cipher_schema(const char *oid); + +int +_gnutls_pkcs_raw_decrypt_data(schema_id schema, asn1_node pkcs8_asn, + const char *root, const char *password, + const struct pbkdf2_params *kdf_params, + const struct pbe_enc_params *enc_params, + gnutls_datum_t *decrypted_data); + +int +_gnutls_pkcs_raw_encrypt_data(const gnutls_datum_t * plain, + const struct pbe_enc_params *enc_params, + const gnutls_datum_t * key, gnutls_datum_t * encrypted); + +int _gnutls_pkcs7_decrypt_data(const gnutls_datum_t * data, + const char *password, gnutls_datum_t * dec); + +int _gnutls_read_pbkdf1_params(const uint8_t * data, int data_size, + struct pbkdf2_params *kdf_params, + struct pbe_enc_params *enc_params); + +int +_gnutls_read_pkcs_schema_params(schema_id * schema, const char *password, + const uint8_t * data, int data_size, + struct pbkdf2_params *kdf_params, + struct pbe_enc_params *enc_params); + +int +_gnutls_pkcs_write_schema_params(schema_id schema, asn1_node pkcs8_asn, + const char *where, + const struct pbkdf2_params *kdf_params, + const struct pbe_enc_params *enc_params); + +int +_gnutls_pkcs_generate_key(schema_id schema, + const char *password, + struct pbkdf2_params *kdf_params, + struct pbe_enc_params *enc_params, gnutls_datum_t * key); + +int _gnutls_pkcs_flags_to_schema(unsigned int flags); +int _gnutls_pkcs7_encrypt_data(schema_id schema, + const gnutls_datum_t * data, + const char *password, gnutls_datum_t * enc); + +int +_gnutls_pkcs7_data_enc_info(const gnutls_datum_t * data, const struct pkcs_cipher_schema_st **p, + struct pbkdf2_params *kdf_params, char **oid); + +#endif /* GNUTLS_LIB_X509_PKCS7_INT_H */ diff --git a/lib/x509/privkey.c b/lib/x509/privkey.c new file mode 100644 index 0000000..792a413 --- /dev/null +++ b/lib/x509/privkey.c @@ -0,0 +1,2388 @@ +/* + * Copyright (C) 2003-2016 Free Software Foundation, Inc. + * Copyright (C) 2012-2016 Nikos Mavrogiannopoulos + * Copyright (C) 2015-2017 Red Hat, Inc. + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GnuTLS. + * + * The GnuTLS is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/> + * + */ + +#include "gnutls_int.h" +#include <datum.h> +#include <global.h> +#include "errors.h" +#include <tls-sig.h> +#include <common.h> +#include <x509.h> +#include <x509_b64.h> +#include <x509_int.h> +#include <pk.h> +#include <mpi.h> +#include <ecc.h> +#include <pin.h> + +/** + * gnutls_x509_privkey_init: + * @key: A pointer to the type to be initialized + * + * This function will initialize a private key type. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int gnutls_x509_privkey_init(gnutls_x509_privkey_t * key) +{ + FAIL_IF_LIB_ERROR; + + *key = gnutls_calloc(1, sizeof(gnutls_x509_privkey_int)); + + if (*key) { + (*key)->key = NULL; + return 0; /* success */ + } + + return GNUTLS_E_MEMORY_ERROR; +} + +void _gnutls_x509_privkey_reinit(gnutls_x509_privkey_t key) +{ + gnutls_pk_params_clear(&key->params); + gnutls_pk_params_release(&key->params); + /* avoid re-use of fields which may have had some sensible value */ + zeroize_key(&key->params, sizeof(key->params)); + + if (key->key) + asn1_delete_structure2(&key->key, ASN1_DELETE_FLAG_ZEROIZE); + key->key = NULL; +} + +/** + * gnutls_x509_privkey_deinit: + * @key: The key to be deinitialized + * + * This function will deinitialize a private key structure. + **/ +void gnutls_x509_privkey_deinit(gnutls_x509_privkey_t key) +{ + if (!key) + return; + + _gnutls_x509_privkey_reinit(key); + gnutls_free(key); +} + +/** + * gnutls_x509_privkey_cpy: + * @dst: The destination key, which should be initialized. + * @src: The source key + * + * This function will copy a private key from source to destination + * key. Destination has to be initialized. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int +gnutls_x509_privkey_cpy(gnutls_x509_privkey_t dst, + gnutls_x509_privkey_t src) +{ + int ret; + + if (!src || !dst) + return GNUTLS_E_INVALID_REQUEST; + + ret = _gnutls_pk_params_copy(&dst->params, &src->params); + if (ret < 0) { + return gnutls_assert_val(ret); + } + + ret = + _gnutls_asn1_encode_privkey(&dst->key, + &dst->params); + if (ret < 0) { + gnutls_assert(); + gnutls_pk_params_release(&dst->params); + return ret; + } + + return 0; +} + +/* Converts an RSA PKCS#1 key to + * an internal structure (gnutls_private_key) + */ +asn1_node +_gnutls_privkey_decode_pkcs1_rsa_key(const gnutls_datum_t * raw_key, + gnutls_x509_privkey_t pkey) +{ + int result; + asn1_node pkey_asn; + + gnutls_pk_params_init(&pkey->params); + + if (asn1_create_element(_gnutls_get_gnutls_asn(), + "GNUTLS.RSAPrivateKey", + &pkey_asn) != ASN1_SUCCESS) { + gnutls_assert(); + return NULL; + } + + result = + _asn1_strict_der_decode(&pkey_asn, raw_key->data, raw_key->size, + NULL); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + goto error; + } + + if (_gnutls_x509_read_int(pkey_asn, "modulus", + &pkey->params.params[0]) < 0) + { + gnutls_assert(); + goto error; + } + pkey->params.params_nr++; + + if (_gnutls_x509_read_int(pkey_asn, "publicExponent", + &pkey->params.params[1]) < 0) { + gnutls_assert(); + goto error; + } + pkey->params.params_nr++; + + if (_gnutls_x509_read_key_int(pkey_asn, "privateExponent", + &pkey->params.params[2]) < 0) { + gnutls_assert(); + goto error; + } + pkey->params.params_nr++; + + if (_gnutls_x509_read_key_int(pkey_asn, "prime1", + &pkey->params.params[3]) < 0) { + gnutls_assert(); + goto error; + } + pkey->params.params_nr++; + + if (_gnutls_x509_read_key_int(pkey_asn, "prime2", + &pkey->params.params[4]) < 0) { + gnutls_assert(); + goto error; + } + pkey->params.params_nr++; + + if (_gnutls_x509_read_key_int(pkey_asn, "coefficient", + &pkey->params.params[5]) < 0) { + gnutls_assert(); + goto error; + } + pkey->params.params_nr++; + + if (_gnutls_x509_read_key_int(pkey_asn, "exponent1", + &pkey->params.params[6]) < 0) { + gnutls_assert(); + goto error; + } + pkey->params.params_nr++; + + if (_gnutls_x509_read_key_int(pkey_asn, "exponent2", + &pkey->params.params[7]) < 0) { + gnutls_assert(); + goto error; + } + pkey->params.params_nr++; + + pkey->params.params_nr = RSA_PRIVATE_PARAMS; + pkey->params.algo = GNUTLS_PK_RSA; + + return pkey_asn; + + error: + asn1_delete_structure2(&pkey_asn, ASN1_DELETE_FLAG_ZEROIZE); + gnutls_pk_params_clear(&pkey->params); + gnutls_pk_params_release(&pkey->params); + return NULL; +} + +/* Converts an ECC key to + * an internal structure (gnutls_private_key) + */ +int +_gnutls_privkey_decode_ecc_key(asn1_node* pkey_asn, const gnutls_datum_t * raw_key, + gnutls_x509_privkey_t pkey, gnutls_ecc_curve_t curve) +{ + int ret; + unsigned int version; + char oid[MAX_OID_SIZE]; + int oid_size; + gnutls_datum_t out; + + if (curve_is_eddsa(curve)) { + return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + } + + gnutls_pk_params_init(&pkey->params); + + if ((ret = + asn1_create_element(_gnutls_get_gnutls_asn(), + "GNUTLS.ECPrivateKey", + pkey_asn)) != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(ret); + } + + ret = + _asn1_strict_der_decode(pkey_asn, raw_key->data, raw_key->size, + NULL); + if (ret != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(ret); + goto error; + } + + ret = _gnutls_x509_read_uint(*pkey_asn, "Version", &version); + if (ret < 0) { + gnutls_assert(); + goto error; + } + + if (version != 1) { + _gnutls_debug_log + ("ECC private key version %u is not supported\n", + version); + gnutls_assert(); + ret = GNUTLS_E_ECC_UNSUPPORTED_CURVE; + goto error; + } + + /* read the curve */ + if (curve == GNUTLS_ECC_CURVE_INVALID) { + oid_size = sizeof(oid); + ret = + asn1_read_value(*pkey_asn, "parameters.namedCurve", oid, + &oid_size); + if (ret != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(ret); + goto error; + } + + pkey->params.curve = gnutls_oid_to_ecc_curve(oid); + + if (pkey->params.curve == GNUTLS_ECC_CURVE_INVALID) { + _gnutls_debug_log("Curve %s is not supported\n", oid); + gnutls_assert(); + ret = GNUTLS_E_ECC_UNSUPPORTED_CURVE; + goto error; + } + } else { + pkey->params.curve = curve; + } + + + /* read the public key */ + ret = _gnutls_x509_read_value(*pkey_asn, "publicKey", &out); + if (ret < 0) { + gnutls_assert(); + goto error; + } + + ret = + _gnutls_ecc_ansi_x962_import(out.data, out.size, + &pkey->params.params[ECC_X], + &pkey->params.params[ECC_Y]); + + _gnutls_free_datum(&out); + if (ret < 0) { + gnutls_assert(); + goto error; + } + pkey->params.params_nr += 2; + + /* read the private key */ + ret = + _gnutls_x509_read_key_int(*pkey_asn, "privateKey", + &pkey->params.params[ECC_K]); + if (ret < 0) { + gnutls_assert(); + goto error; + } + pkey->params.params_nr++; + pkey->params.algo = GNUTLS_PK_EC; + + return 0; + + error: + asn1_delete_structure2(pkey_asn, ASN1_DELETE_FLAG_ZEROIZE); + gnutls_pk_params_clear(&pkey->params); + gnutls_pk_params_release(&pkey->params); + return ret; + +} + + +static asn1_node +decode_dsa_key(const gnutls_datum_t * raw_key, gnutls_x509_privkey_t pkey) +{ + int result; + asn1_node dsa_asn; + gnutls_datum_t seed = {NULL,0}; + char oid[MAX_OID_SIZE]; + int oid_size; + + if (asn1_create_element(_gnutls_get_gnutls_asn(), + "GNUTLS.DSAPrivateKey", + &dsa_asn) != ASN1_SUCCESS) { + gnutls_assert(); + return NULL; + } + + gnutls_pk_params_init(&pkey->params); + + + result = + _asn1_strict_der_decode(&dsa_asn, raw_key->data, raw_key->size, + NULL); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + goto error; + } + + if (_gnutls_x509_read_int(dsa_asn, "p", + &pkey->params.params[0]) < 0) { + gnutls_assert(); + goto error; + } + pkey->params.params_nr++; + + if (_gnutls_x509_read_int(dsa_asn, "q", + &pkey->params.params[1]) < 0) { + gnutls_assert(); + goto error; + } + pkey->params.params_nr++; + + if (_gnutls_x509_read_int(dsa_asn, "g", + &pkey->params.params[2]) < 0) { + gnutls_assert(); + goto error; + } + pkey->params.params_nr++; + + if (_gnutls_x509_read_int(dsa_asn, "Y", + &pkey->params.params[3]) < 0) { + gnutls_assert(); + goto error; + } + pkey->params.params_nr++; + + if (_gnutls_x509_read_key_int(dsa_asn, "priv", + &pkey->params.params[4]) < 0) + { + gnutls_assert(); + goto error; + } + pkey->params.params_nr++; + pkey->params.algo = GNUTLS_PK_DSA; + + oid_size = sizeof(oid); + result = asn1_read_value(dsa_asn, "seed.algorithm", oid, &oid_size); + if (result == ASN1_SUCCESS) { + pkey->params.palgo = gnutls_oid_to_digest(oid); + + result = _gnutls_x509_read_value(dsa_asn, "seed.seed", &seed); + if (result == ASN1_SUCCESS) { + if (seed.size <= sizeof(pkey->params.seed)) { + memcpy(pkey->params.seed, seed.data, seed.size); + pkey->params.seed_size = seed.size; + } + gnutls_free(seed.data); + } + } + + return dsa_asn; + + error: + asn1_delete_structure2(&dsa_asn, ASN1_DELETE_FLAG_ZEROIZE); + gnutls_pk_params_clear(&pkey->params); + gnutls_pk_params_release(&pkey->params); + return NULL; + +} + + +#define PEM_KEY_DSA "DSA PRIVATE KEY" +#define PEM_KEY_RSA "RSA PRIVATE KEY" +#define PEM_KEY_ECC "EC PRIVATE KEY" +#define PEM_KEY_PKCS8 "PRIVATE KEY" + +#define MAX_PEM_HEADER_SIZE 25 + +#define IF_CHECK_FOR(pemstr, _algo, cptr, bptr, size, key) \ + if (left > sizeof(pemstr) && memcmp(cptr, pemstr, sizeof(pemstr)-1) == 0) { \ + result = _gnutls_fbase64_decode(pemstr, bptr, size, &_data); \ + if (result >= 0) \ + key->params.algo = _algo; \ + } + +/** + * gnutls_x509_privkey_import: + * @key: The data to store the parsed key + * @data: The DER or PEM encoded certificate. + * @format: One of DER or PEM + * + * This function will convert the given DER or PEM encoded key to the + * native #gnutls_x509_privkey_t format. The output will be stored in + * @key . + * + * If the key is PEM encoded it should have a header that contains "PRIVATE + * KEY". Note that this function falls back to PKCS #8 decoding without + * password, if the default format fails to import. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int +gnutls_x509_privkey_import(gnutls_x509_privkey_t key, + const gnutls_datum_t * data, + gnutls_x509_crt_fmt_t format) +{ + int result = 0, need_free = 0; + gnutls_datum_t _data; + + if (key == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + _data.data = data->data; + _data.size = data->size; + + key->params.algo = GNUTLS_PK_UNKNOWN; + + /* If the Certificate is in PEM format then decode it + */ + if (format == GNUTLS_X509_FMT_PEM) { + unsigned left; + char *ptr; + uint8_t *begin_ptr; + + ptr = memmem(data->data, data->size, "PRIVATE KEY-----", sizeof("PRIVATE KEY-----")-1); + + result = GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + + if (ptr != NULL) { + left = data->size - ((ptrdiff_t)ptr - (ptrdiff_t)data->data); + + if (data->size - left > MAX_PEM_HEADER_SIZE) { + ptr -= MAX_PEM_HEADER_SIZE; + left += MAX_PEM_HEADER_SIZE; + } else { + ptr = (char*)data->data; + left = data->size; + } + + ptr = memmem(ptr, left, "-----BEGIN ", sizeof("-----BEGIN ")-1); + if (ptr != NULL) { + begin_ptr = (uint8_t*)ptr; + left = data->size - ((ptrdiff_t)begin_ptr - (ptrdiff_t)data->data); + + ptr += sizeof("-----BEGIN ")-1; + + IF_CHECK_FOR(PEM_KEY_RSA, GNUTLS_PK_RSA, ptr, begin_ptr, left, key) + else IF_CHECK_FOR(PEM_KEY_ECC, GNUTLS_PK_EC, ptr, begin_ptr, left, key) + else IF_CHECK_FOR(PEM_KEY_DSA, GNUTLS_PK_DSA, ptr, begin_ptr, left, key) + + if (key->params.algo == GNUTLS_PK_UNKNOWN && left >= sizeof(PEM_KEY_PKCS8)) { + if (memcmp(ptr, PEM_KEY_PKCS8, sizeof(PEM_KEY_PKCS8)-1) == 0) { + result = + _gnutls_fbase64_decode(PEM_KEY_PKCS8, + begin_ptr, left, &_data); + if (result >= 0) { + /* signal for PKCS #8 keys */ + key->params.algo = -1; + } + } + } + } + + } + + if (result < 0) { + gnutls_assert(); + return result; + } + + need_free = 1; + } + + if (key->expanded) { + _gnutls_x509_privkey_reinit(key); + } + key->expanded = 1; + + if (key->params.algo == (gnutls_pk_algorithm_t)-1) { + result = + gnutls_x509_privkey_import_pkcs8(key, data, format, + NULL, + GNUTLS_PKCS_PLAIN); + if (result < 0) { + gnutls_assert(); + key->key = NULL; + goto cleanup; + } else { + /* some keys under PKCS#8 don't set key->key */ + goto finish; + } + } else if (key->params.algo == GNUTLS_PK_RSA) { + key->key = + _gnutls_privkey_decode_pkcs1_rsa_key(&_data, key); + if (key->key == NULL) + gnutls_assert(); + } else if (key->params.algo == GNUTLS_PK_DSA) { + key->key = decode_dsa_key(&_data, key); + if (key->key == NULL) + gnutls_assert(); + } else if (key->params.algo == GNUTLS_PK_EC) { + result = _gnutls_privkey_decode_ecc_key(&key->key, &_data, key, 0); + if (result < 0) { + gnutls_assert(); + key->key = NULL; + } + } else { + /* Try decoding each of the keys, and accept the one that + * succeeds. + */ + key->params.algo = GNUTLS_PK_RSA; + key->key = + _gnutls_privkey_decode_pkcs1_rsa_key(&_data, key); + + if (key->key == NULL) { + key->params.algo = GNUTLS_PK_DSA; + key->key = decode_dsa_key(&_data, key); + if (key->key == NULL) { + key->params.algo = GNUTLS_PK_EC; + result = + _gnutls_privkey_decode_ecc_key(&key->key, &_data, key, 0); + if (result < 0) { + result = + gnutls_x509_privkey_import_pkcs8(key, data, format, + NULL, + GNUTLS_PKCS_PLAIN); + if (result >= 0) { + /* there are keys (ed25519) which leave key->key NULL */ + goto finish; + } + + /* result < 0 */ + gnutls_assert(); + key->key = NULL; + + if (result == GNUTLS_E_PK_INVALID_PRIVKEY) + goto cleanup; + } + } + } + } + + if (key->key == NULL) { + gnutls_assert(); + result = GNUTLS_E_ASN1_DER_ERROR; + goto cleanup; + } + + finish: + result = + _gnutls_pk_fixup(key->params.algo, GNUTLS_IMPORT, &key->params); + if (result < 0) { + gnutls_assert(); + } + + cleanup: + if (need_free) { + zeroize_temp_key(_data.data, _data.size); + _gnutls_free_datum(&_data); + } + + /* The key has now been decoded. + */ + + return result; +} + +static int import_pkcs12_privkey(gnutls_x509_privkey_t key, + const gnutls_datum_t * data, + gnutls_x509_crt_fmt_t format, + const char *password, unsigned int flags) +{ + int ret; + gnutls_pkcs12_t p12; + gnutls_x509_privkey_t newkey; + + ret = gnutls_pkcs12_init(&p12); + if (ret < 0) + return gnutls_assert_val(ret); + + ret = gnutls_pkcs12_import(p12, data, format, flags); + if (ret < 0) { + gnutls_assert(); + goto fail; + } + + ret = + gnutls_pkcs12_simple_parse(p12, password, &newkey, NULL, NULL, + NULL, NULL, NULL, 0); + if (ret < 0) { + gnutls_assert(); + goto fail; + } + + ret = gnutls_x509_privkey_cpy(key, newkey); + gnutls_x509_privkey_deinit(newkey); + if (ret < 0) { + gnutls_assert(); + goto fail; + } + + ret = 0; + fail: + + gnutls_pkcs12_deinit(p12); + + return ret; +} + +/** + * gnutls_x509_privkey_import2: + * @key: The data to store the parsed key + * @data: The DER or PEM encoded key. + * @format: One of DER or PEM + * @password: A password (optional) + * @flags: an ORed sequence of gnutls_pkcs_encrypt_flags_t + * + * This function will import the given DER or PEM encoded key, to + * the native #gnutls_x509_privkey_t format, irrespective of the + * input format. The input format is auto-detected. + * + * The supported formats are basic unencrypted key, PKCS8, PKCS12, + * and the openssl format. + * + * If the provided key is encrypted but no password was given, then + * %GNUTLS_E_DECRYPTION_FAILED is returned. Since GnuTLS 3.4.0 this + * function will utilize the PIN callbacks if any. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int +gnutls_x509_privkey_import2(gnutls_x509_privkey_t key, + const gnutls_datum_t * data, + gnutls_x509_crt_fmt_t format, + const char *password, unsigned int flags) +{ + int ret = 0; + int saved_ret = GNUTLS_E_PARSING_ERROR; + char pin[GNUTLS_PKCS11_MAX_PIN_LEN]; + unsigned head_enc = 1; + + if (format == GNUTLS_X509_FMT_PEM) { + size_t left; + char *ptr; + + ptr = memmem(data->data, data->size, "PRIVATE KEY-----", sizeof("PRIVATE KEY-----")-1); + + if (ptr != NULL) { + left = data->size - ((ptrdiff_t)ptr - (ptrdiff_t)data->data); + + if (data->size - left > 15) { + ptr -= 15; + left += 15; + } else { + ptr = (char*)data->data; + left = data->size; + } + + ptr = memmem(ptr, left, "-----BEGIN ", sizeof("-----BEGIN ")-1); + if (ptr != NULL) { + ptr += sizeof("-----BEGIN ")-1; + left = data->size - ((ptrdiff_t)ptr - (ptrdiff_t)data->data); + } + + if (ptr != NULL && left > sizeof(PEM_KEY_RSA)) { + if (memcmp(ptr, PEM_KEY_RSA, sizeof(PEM_KEY_RSA)-1) == 0 || + memcmp(ptr, PEM_KEY_ECC, sizeof(PEM_KEY_ECC)-1) == 0 || + memcmp(ptr, PEM_KEY_DSA, sizeof(PEM_KEY_DSA)-1) == 0) { + head_enc = 0; + } + } + } + } + + if (head_enc == 0 || (password == NULL && !(flags & GNUTLS_PKCS_NULL_PASSWORD))) { + ret = gnutls_x509_privkey_import(key, data, format); + if (ret >= 0) + return ret; + + gnutls_assert(); + saved_ret = ret; + /* fall through to PKCS #8 decoding */ + } + + if ((password != NULL || (flags & GNUTLS_PKCS_NULL_PASSWORD)) + || ret < 0) { + + ret = + gnutls_x509_privkey_import_pkcs8(key, data, format, + password, flags); + + if (ret == GNUTLS_E_DECRYPTION_FAILED && + password == NULL && (!(flags & GNUTLS_PKCS_PLAIN))) { + /* use the callback if any */ + ret = _gnutls_retrieve_pin(&key->pin, "key:", "", 0, pin, sizeof(pin)); + if (ret == 0) { + password = pin; + } + + ret = + gnutls_x509_privkey_import_pkcs8(key, data, format, + password, flags); + } + + if (saved_ret == GNUTLS_E_PARSING_ERROR) + saved_ret = ret; + + if (ret < 0) { + if (ret == GNUTLS_E_DECRYPTION_FAILED) + goto cleanup; + ret = + import_pkcs12_privkey(key, data, format, + password, flags); + if (ret < 0 && format == GNUTLS_X509_FMT_PEM) { + if (ret == GNUTLS_E_DECRYPTION_FAILED) + goto cleanup; + + ret = + gnutls_x509_privkey_import_openssl(key, + data, + password); + + if (ret == GNUTLS_E_DECRYPTION_FAILED && password == NULL && + (key->pin.cb || _gnutls_pin_func)) { + /* use the callback if any */ + memset(pin, 0, GNUTLS_PKCS11_MAX_PIN_LEN); + ret = _gnutls_retrieve_pin(&key->pin, "key:", "", 0, pin, sizeof(pin)); + if (ret == 0) { + ret = gnutls_x509_privkey_import_openssl(key, data, pin); + } + } + + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + } else { + gnutls_assert(); + goto cleanup; + } + } + } + + ret = 0; + + cleanup: + if (ret == GNUTLS_E_PARSING_ERROR) + ret = saved_ret; + + return ret; +} + + +/** + * gnutls_x509_privkey_import_rsa_raw: + * @key: The data to store the parsed key + * @m: holds the modulus + * @e: holds the public exponent + * @d: holds the private exponent + * @p: holds the first prime (p) + * @q: holds the second prime (q) + * @u: holds the coefficient + * + * This function will convert the given RSA raw parameters to the + * native #gnutls_x509_privkey_t format. The output will be stored in + * @key. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int +gnutls_x509_privkey_import_rsa_raw(gnutls_x509_privkey_t key, + const gnutls_datum_t * m, + const gnutls_datum_t * e, + const gnutls_datum_t * d, + const gnutls_datum_t * p, + const gnutls_datum_t * q, + const gnutls_datum_t * u) +{ + return gnutls_x509_privkey_import_rsa_raw2(key, m, e, d, p, q, u, + NULL, NULL); +} + +/** + * gnutls_x509_privkey_import_rsa_raw2: + * @key: The data to store the parsed key + * @m: holds the modulus + * @e: holds the public exponent + * @d: holds the private exponent + * @p: holds the first prime (p) + * @q: holds the second prime (q) + * @u: holds the coefficient (optional) + * @e1: holds e1 = d mod (p-1) (optional) + * @e2: holds e2 = d mod (q-1) (optional) + * + * This function will convert the given RSA raw parameters to the + * native #gnutls_x509_privkey_t format. The output will be stored in + * @key. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int +gnutls_x509_privkey_import_rsa_raw2(gnutls_x509_privkey_t key, + const gnutls_datum_t * m, + const gnutls_datum_t * e, + const gnutls_datum_t * d, + const gnutls_datum_t * p, + const gnutls_datum_t * q, + const gnutls_datum_t * u, + const gnutls_datum_t * e1, + const gnutls_datum_t * e2) +{ + int ret; + size_t siz = 0; + + if (key == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + gnutls_pk_params_init(&key->params); + + siz = m->size; + if (_gnutls_mpi_init_scan_nz(&key->params.params[RSA_MODULUS], m->data, siz)) { + gnutls_assert(); + ret = GNUTLS_E_MPI_SCAN_FAILED; + goto cleanup; + } + key->params.params_nr++; + + siz = e->size; + if (_gnutls_mpi_init_scan_nz(&key->params.params[RSA_PUB], e->data, siz)) { + gnutls_assert(); + ret = GNUTLS_E_MPI_SCAN_FAILED; + goto cleanup; + } + key->params.params_nr++; + + if (d) { + siz = d->size; + if (_gnutls_mpi_init_scan_nz(&key->params.params[RSA_PRIV], d->data, siz)) { + gnutls_assert(); + ret = GNUTLS_E_MPI_SCAN_FAILED; + goto cleanup; + } + key->params.params_nr++; + } + + siz = p->size; + if (_gnutls_mpi_init_scan_nz(&key->params.params[RSA_PRIME1], p->data, siz)) { + gnutls_assert(); + ret = GNUTLS_E_MPI_SCAN_FAILED; + goto cleanup; + } + key->params.params_nr++; + + siz = q->size; + if (_gnutls_mpi_init_scan_nz(&key->params.params[RSA_PRIME2], q->data, siz)) { + gnutls_assert(); + ret = GNUTLS_E_MPI_SCAN_FAILED; + goto cleanup; + } + key->params.params_nr++; + + if (u) { + siz = u->size; + if (_gnutls_mpi_init_scan_nz(&key->params.params[RSA_COEF], u->data, siz)) { + gnutls_assert(); + ret = GNUTLS_E_MPI_SCAN_FAILED; + goto cleanup; + } + key->params.params_nr++; + } + + if (e1 && e2) { + siz = e1->size; + if (_gnutls_mpi_init_scan_nz + (&key->params.params[RSA_E1], e1->data, siz)) { + gnutls_assert(); + ret = GNUTLS_E_MPI_SCAN_FAILED; + goto cleanup; + } + key->params.params_nr++; + + siz = e2->size; + if (_gnutls_mpi_init_scan_nz + (&key->params.params[RSA_E2], e2->data, siz)) { + gnutls_assert(); + ret = GNUTLS_E_MPI_SCAN_FAILED; + goto cleanup; + } + key->params.params_nr++; + } + + key->params.algo = GNUTLS_PK_RSA; + + ret = _gnutls_pk_fixup(GNUTLS_PK_RSA, GNUTLS_IMPORT, &key->params); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + key->params.params_nr = RSA_PRIVATE_PARAMS; + key->params.algo = GNUTLS_PK_RSA; + + ret = + _gnutls_asn1_encode_privkey(&key->key, + &key->params); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + return 0; + + cleanup: + gnutls_pk_params_clear(&key->params); + gnutls_pk_params_release(&key->params); + return ret; + +} + +/** + * gnutls_x509_privkey_import_dsa_raw: + * @key: The data to store the parsed key + * @p: holds the p + * @q: holds the q + * @g: holds the g + * @y: holds the y (optional) + * @x: holds the x + * + * This function will convert the given DSA raw parameters to the + * native #gnutls_x509_privkey_t format. The output will be stored + * in @key. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int +gnutls_x509_privkey_import_dsa_raw(gnutls_x509_privkey_t key, + const gnutls_datum_t * p, + const gnutls_datum_t * q, + const gnutls_datum_t * g, + const gnutls_datum_t * y, + const gnutls_datum_t * x) +{ + int ret; + size_t siz = 0; + + if (key == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + gnutls_pk_params_init(&key->params); + + siz = p->size; + if (_gnutls_mpi_init_scan_nz(&key->params.params[0], p->data, siz)) { + gnutls_assert(); + ret = GNUTLS_E_MPI_SCAN_FAILED; + goto cleanup; + } + + siz = q->size; + if (_gnutls_mpi_init_scan_nz(&key->params.params[1], q->data, siz)) { + gnutls_assert(); + ret = GNUTLS_E_MPI_SCAN_FAILED; + goto cleanup; + } + + siz = g->size; + if (_gnutls_mpi_init_scan_nz(&key->params.params[2], g->data, siz)) { + gnutls_assert(); + ret = GNUTLS_E_MPI_SCAN_FAILED; + goto cleanup; + } + + if (y) { + siz = y->size; + if (_gnutls_mpi_init_scan_nz(&key->params.params[3], y->data, siz)) { + gnutls_assert(); + ret = GNUTLS_E_MPI_SCAN_FAILED; + goto cleanup; + } + } + + siz = x->size; + if (_gnutls_mpi_init_scan_nz(&key->params.params[4], x->data, siz)) { + gnutls_assert(); + ret = GNUTLS_E_MPI_SCAN_FAILED; + goto cleanup; + } + + ret = + _gnutls_pk_fixup(GNUTLS_PK_DSA, GNUTLS_IMPORT, &key->params); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + key->params.algo = GNUTLS_PK_DSA; + key->params.params_nr = DSA_PRIVATE_PARAMS; + + ret = + _gnutls_asn1_encode_privkey(&key->key, + &key->params); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + return 0; + + cleanup: + gnutls_pk_params_clear(&key->params); + gnutls_pk_params_release(&key->params); + return ret; + +} + +/** + * gnutls_x509_privkey_import_ecc_raw: + * @key: The data to store the parsed key + * @curve: holds the curve + * @x: holds the x-coordinate + * @y: holds the y-coordinate + * @k: holds the k + * + * This function will convert the given elliptic curve parameters to the + * native #gnutls_x509_privkey_t format. The output will be stored + * in @key. For EdDSA keys, the @x and @k values must be in the + * native to curve format. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 3.0 + **/ +int +gnutls_x509_privkey_import_ecc_raw(gnutls_x509_privkey_t key, + gnutls_ecc_curve_t curve, + const gnutls_datum_t * x, + const gnutls_datum_t * y, + const gnutls_datum_t * k) +{ + int ret; + + if (key == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + gnutls_pk_params_init(&key->params); + + key->params.curve = curve; + + if (curve_is_eddsa(curve)) { + unsigned size; + switch (curve) { + case GNUTLS_ECC_CURVE_ED25519: + key->params.algo = GNUTLS_PK_EDDSA_ED25519; + break; + case GNUTLS_ECC_CURVE_ED448: + key->params.algo = GNUTLS_PK_EDDSA_ED448; + break; + default: + ret = gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); + goto cleanup; + } + + size = gnutls_ecc_curve_get_size(curve); + if (x->size != size || k->size != size) { + ret = gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + goto cleanup; + } + + ret = _gnutls_set_datum(&key->params.raw_pub, x->data, x->size); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = _gnutls_set_datum(&key->params.raw_priv, k->data, k->size); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + return 0; + } + + if (_gnutls_mpi_init_scan_nz + (&key->params.params[ECC_X], x->data, x->size)) { + gnutls_assert(); + ret = GNUTLS_E_MPI_SCAN_FAILED; + goto cleanup; + } + key->params.params_nr++; + + if (_gnutls_mpi_init_scan_nz + (&key->params.params[ECC_Y], y->data, y->size)) { + gnutls_assert(); + ret = GNUTLS_E_MPI_SCAN_FAILED; + goto cleanup; + } + key->params.params_nr++; + + if (_gnutls_mpi_init_scan_nz + (&key->params.params[ECC_K], k->data, k->size)) { + gnutls_assert(); + ret = GNUTLS_E_MPI_SCAN_FAILED; + goto cleanup; + } + key->params.params_nr++; + + key->params.algo = GNUTLS_PK_EC; + + ret = + _gnutls_pk_fixup(GNUTLS_PK_EC, GNUTLS_IMPORT, &key->params); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = + _gnutls_asn1_encode_privkey(&key->key, + &key->params); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + return 0; + + cleanup: + gnutls_pk_params_clear(&key->params); + gnutls_pk_params_release(&key->params); + return ret; + +} + +/** + * gnutls_x509_privkey_import_gost_raw: + * @key: The data to store the parsed key + * @curve: holds the curve + * @digest: will hold the digest + * @paramset: will hold the GOST parameter set ID + * @x: holds the x-coordinate + * @y: holds the y-coordinate + * @k: holds the k (private key) + * + * This function will convert the given GOST private key's parameters to the + * native #gnutls_x509_privkey_t format. The output will be stored + * in @key. @digest should be one of GNUTLS_DIG_GOSR_94, + * GNUTLS_DIG_STREEBOG_256 or GNUTLS_DIG_STREEBOG_512. If @paramset is set to + * GNUTLS_GOST_PARAMSET_UNKNOWN default one will be selected depending on + * @digest. + * + * Note: parameters should be stored with least significant byte first. On + * version 3.6.3 big-endian format was used incorrectly. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 3.6.3 + **/ +int +gnutls_x509_privkey_import_gost_raw(gnutls_x509_privkey_t key, + gnutls_ecc_curve_t curve, + gnutls_digest_algorithm_t digest, + gnutls_gost_paramset_t paramset, + const gnutls_datum_t * x, + const gnutls_datum_t * y, + const gnutls_datum_t * k) +{ + int ret; + + if (key == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + key->params.curve = curve; + key->params.algo = _gnutls_digest_gost(digest); + + if (paramset == GNUTLS_GOST_PARAMSET_UNKNOWN) + paramset = _gnutls_gost_paramset_default(key->params.algo); + + key->params.gost_params = paramset; + + if (_gnutls_mpi_init_scan_le + (&key->params.params[GOST_X], x->data, x->size)) { + gnutls_assert(); + ret = GNUTLS_E_MPI_SCAN_FAILED; + goto cleanup; + } + key->params.params_nr++; + + if (_gnutls_mpi_init_scan_le + (&key->params.params[GOST_Y], y->data, y->size)) { + gnutls_assert(); + ret = GNUTLS_E_MPI_SCAN_FAILED; + goto cleanup; + } + key->params.params_nr++; + + if (_gnutls_mpi_init_scan_le + (&key->params.params[GOST_K], k->data, k->size)) { + gnutls_assert(); + ret = GNUTLS_E_MPI_SCAN_FAILED; + goto cleanup; + } + key->params.params_nr++; + + ret = + _gnutls_pk_fixup(key->params.algo, GNUTLS_IMPORT, &key->params); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + return 0; + + cleanup: + gnutls_pk_params_clear(&key->params); + gnutls_pk_params_release(&key->params); + return ret; + +} + + +/** + * gnutls_x509_privkey_get_pk_algorithm: + * @key: should contain a #gnutls_x509_privkey_t type + * + * This function will return the public key algorithm of a private + * key. + * + * Returns: a member of the #gnutls_pk_algorithm_t enumeration on + * success, or a negative error code on error. + **/ +int gnutls_x509_privkey_get_pk_algorithm(gnutls_x509_privkey_t key) +{ + if (key == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + return key->params.algo; +} + +/** + * gnutls_x509_privkey_get_pk_algorithm2: + * @key: should contain a #gnutls_x509_privkey_t type + * @bits: The number of bits in the public key algorithm + * + * This function will return the public key algorithm of a private + * key. + * + * Returns: a member of the #gnutls_pk_algorithm_t enumeration on + * success, or a negative error code on error. + **/ +int +gnutls_x509_privkey_get_pk_algorithm2(gnutls_x509_privkey_t key, + unsigned int *bits) +{ + int ret; + + if (key == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + if (bits) { + ret = pubkey_to_bits(&key->params); + if (ret < 0) + ret = 0; + *bits = ret; + } + + return key->params.algo; +} + +void +_gnutls_x509_privkey_get_spki_params(gnutls_x509_privkey_t key, + gnutls_x509_spki_st *params) +{ + memcpy(params, &key->params.spki, sizeof(gnutls_x509_spki_st)); +} + +/** + * gnutls_x509_privkey_get_spki: + * @key: should contain a #gnutls_x509_privkey_t type + * @spki: a SubjectPublicKeyInfo structure of type #gnutls_x509_spki_t + * @flags: must be zero + * + * This function will return the public key information of a private + * key. The provided @spki must be initialized. + * + * Returns: Zero on success, or a negative error code on error. + **/ +int +gnutls_x509_privkey_get_spki(gnutls_x509_privkey_t key, gnutls_x509_spki_t spki, unsigned int flags) +{ + if (key == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + if (key->params.spki.pk == GNUTLS_PK_UNKNOWN) + return gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE); + + _gnutls_x509_privkey_get_spki_params(key, spki); + + return 0; +} + +/** + * gnutls_x509_privkey_set_spki: + * @key: should contain a #gnutls_x509_privkey_t type + * @spki: a SubjectPublicKeyInfo structure of type #gnutls_x509_spki_t + * @flags: must be zero + * + * This function will return the public key information of a private + * key. The provided @spki must be initialized. + * + * Returns: Zero on success, or a negative error code on error. + **/ +int +gnutls_x509_privkey_set_spki(gnutls_x509_privkey_t key, const gnutls_x509_spki_t spki, unsigned int flags) +{ + gnutls_pk_params_st tparams; + int ret; + + if (key == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + if (!_gnutls_pk_are_compat(key->params.algo, spki->pk)) + return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + + memcpy(&tparams, &key->params, sizeof(gnutls_pk_params_st)); + memcpy(&tparams.spki, spki, sizeof (gnutls_x509_spki_st)); + ret = _gnutls_x509_check_pubkey_params(&tparams); + if (ret < 0) + return gnutls_assert_val(ret); + + memcpy(&key->params.spki, spki, sizeof (gnutls_x509_spki_st)); + + key->params.algo = spki->pk; + + return 0; +} + +static const char *set_msg(gnutls_x509_privkey_t key) +{ + if (GNUTLS_PK_IS_RSA(key->params.algo)) { + return PEM_KEY_RSA; + } else if (key->params.algo == GNUTLS_PK_DSA) { + return PEM_KEY_DSA; + } else if (key->params.algo == GNUTLS_PK_EC) + return PEM_KEY_ECC; + else + return "UNKNOWN"; +} + +/** + * gnutls_x509_privkey_export: + * @key: Holds the key + * @format: the format of output params. One of PEM or DER. + * @output_data: will contain a private key PEM or DER encoded + * @output_data_size: holds the size of output_data (and will be + * replaced by the actual size of parameters) + * + * This function will export the private key to a PKCS#1 structure for + * RSA or RSA-PSS keys, and integer sequence for DSA keys. Other keys types + * will be exported in PKCS#8 form. + * + * If the structure is PEM encoded, it will have a header + * of "BEGIN RSA PRIVATE KEY". + * + * It is recommended to use gnutls_x509_privkey_export_pkcs8() instead + * of this function, when a consistent output format is required. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int +gnutls_x509_privkey_export(gnutls_x509_privkey_t key, + gnutls_x509_crt_fmt_t format, void *output_data, + size_t * output_data_size) +{ + gnutls_datum_t out; + int ret; + + ret = gnutls_x509_privkey_export2(key, format, &out); + if (ret < 0) + return gnutls_assert_val(ret); + + if (format == GNUTLS_X509_FMT_PEM) + ret = _gnutls_copy_string(&out, output_data, output_data_size); + else + ret = _gnutls_copy_data(&out, output_data, output_data_size); + gnutls_free(out.data); + + return ret; +} + +/** + * gnutls_x509_privkey_export2: + * @key: Holds the key + * @format: the format of output params. One of PEM or DER. + * @out: will contain a private key PEM or DER encoded + * + * This function will export the private key to a PKCS#1 structure for + * RSA or RSA-PSS keys, and integer sequence for DSA keys. Other keys types + * will be exported in PKCS#8 form. + * + * The output buffer is allocated using gnutls_malloc(). + * + * It is recommended to use gnutls_x509_privkey_export2_pkcs8() instead + * of this function, when a consistent output format is required. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since 3.1.3 + **/ +int +gnutls_x509_privkey_export2(gnutls_x509_privkey_t key, + gnutls_x509_crt_fmt_t format, + gnutls_datum_t * out) +{ + const char *msg; + int ret; + + if (key == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + if (key->key == NULL) { /* can only export in PKCS#8 form */ + return gnutls_x509_privkey_export2_pkcs8(key, format, NULL, 0, out); + } + + msg = set_msg(key); + + if (key->flags & GNUTLS_PRIVKEY_FLAG_EXPORT_COMPAT) { + ret = gnutls_x509_privkey_fix(key); + if (ret < 0) + return gnutls_assert_val(ret); + } + + return _gnutls_x509_export_int2(key->key, format, msg, out); +} + +/** + * gnutls_x509_privkey_sec_param: + * @key: a key + * + * This function will return the security parameter appropriate with + * this private key. + * + * Returns: On success, a valid security parameter is returned otherwise + * %GNUTLS_SEC_PARAM_UNKNOWN is returned. + * + * Since: 2.12.0 + **/ +gnutls_sec_param_t gnutls_x509_privkey_sec_param(gnutls_x509_privkey_t key) +{ + int bits; + + bits = pubkey_to_bits(&key->params); + if (bits <= 0) + return GNUTLS_SEC_PARAM_UNKNOWN; + + return gnutls_pk_bits_to_sec_param(key->params.algo, bits); +} + +/** + * gnutls_x509_privkey_export_ecc_raw: + * @key: a key + * @curve: will hold the curve + * @x: will hold the x-coordinate + * @y: will hold the y-coordinate + * @k: will hold the private key + * + * This function will export the ECC private key's parameters found + * in the given structure. The new parameters will be allocated using + * gnutls_malloc() and will be stored in the appropriate datum. + * + * In EdDSA curves the @y parameter will be %NULL and the other parameters + * will be in the native format for the curve. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 3.0 + **/ +int gnutls_x509_privkey_export_ecc_raw(gnutls_x509_privkey_t key, + gnutls_ecc_curve_t *curve, + gnutls_datum_t *x, + gnutls_datum_t *y, + gnutls_datum_t *k) +{ + if (key == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + return _gnutls_params_get_ecc_raw(&key->params, curve, x, y, k, 0); +} + +/** + * gnutls_x509_privkey_export_gost_raw: + * @key: a key + * @curve: will hold the curve + * @digest: will hold the digest + * @paramset: will hold the GOST parameter set ID + * @x: will hold the x-coordinate + * @y: will hold the y-coordinate + * @k: will hold the private key + * + * This function will export the GOST private key's parameters found + * in the given structure. The new parameters will be allocated using + * gnutls_malloc() and will be stored in the appropriate datum. + * + * Note: parameters will be stored with least significant byte first. On + * version 3.6.3 this was incorrectly returned in big-endian format. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 3.6.3 + **/ +int gnutls_x509_privkey_export_gost_raw(gnutls_x509_privkey_t key, + gnutls_ecc_curve_t * curve, + gnutls_digest_algorithm_t * digest, + gnutls_gost_paramset_t * paramset, + gnutls_datum_t * x, + gnutls_datum_t * y, + gnutls_datum_t * k) +{ + if (key == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + return _gnutls_params_get_gost_raw(&key->params, curve, digest, paramset, + x, y, k, 0); +} + +/** + * gnutls_x509_privkey_export_rsa_raw: + * @key: a key + * @m: will hold the modulus + * @e: will hold the public exponent + * @d: will hold the private exponent + * @p: will hold the first prime (p) + * @q: will hold the second prime (q) + * @u: will hold the coefficient + * + * This function will export the RSA private key's parameters found + * in the given structure. The new parameters will be allocated using + * gnutls_malloc() and will be stored in the appropriate datum. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int +gnutls_x509_privkey_export_rsa_raw(gnutls_x509_privkey_t key, + gnutls_datum_t * m, gnutls_datum_t * e, + gnutls_datum_t * d, gnutls_datum_t * p, + gnutls_datum_t * q, gnutls_datum_t * u) +{ + return _gnutls_params_get_rsa_raw(&key->params, m, e, d, p, q, u, NULL, NULL, 0); +} + +/** + * gnutls_x509_privkey_export_rsa_raw2: + * @key: a key + * @m: will hold the modulus + * @e: will hold the public exponent + * @d: will hold the private exponent + * @p: will hold the first prime (p) + * @q: will hold the second prime (q) + * @u: will hold the coefficient + * @e1: will hold e1 = d mod (p-1) + * @e2: will hold e2 = d mod (q-1) + * + * This function will export the RSA private key's parameters found + * in the given structure. The new parameters will be allocated using + * gnutls_malloc() and will be stored in the appropriate datum. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 2.12.0 + **/ +int +gnutls_x509_privkey_export_rsa_raw2(gnutls_x509_privkey_t key, + gnutls_datum_t * m, gnutls_datum_t * e, + gnutls_datum_t * d, gnutls_datum_t * p, + gnutls_datum_t * q, gnutls_datum_t * u, + gnutls_datum_t * e1, + gnutls_datum_t * e2) +{ + return _gnutls_params_get_rsa_raw(&key->params, m, e, d, p, q, u, e1, e2, 0); +} + +/** + * gnutls_x509_privkey_export_dsa_raw: + * @key: a key + * @p: will hold the p + * @q: will hold the q + * @g: will hold the g + * @y: will hold the y + * @x: will hold the x + * + * This function will export the DSA private key's parameters found + * in the given structure. The new parameters will be allocated using + * gnutls_malloc() and will be stored in the appropriate datum. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int +gnutls_x509_privkey_export_dsa_raw(gnutls_x509_privkey_t key, + gnutls_datum_t * p, gnutls_datum_t * q, + gnutls_datum_t * g, gnutls_datum_t * y, + gnutls_datum_t * x) +{ + return _gnutls_params_get_dsa_raw(&key->params, p, q, g, y, x, 0); +} + +/** + * gnutls_x509_privkey_generate: + * @key: an initialized key + * @algo: is one of the algorithms in #gnutls_pk_algorithm_t. + * @bits: the size of the parameters to generate + * @flags: Must be zero or flags from #gnutls_privkey_flags_t. + * + * This function will generate a random private key. Note that this + * function must be called on an initialized private key. + * + * The flag %GNUTLS_PRIVKEY_FLAG_PROVABLE + * instructs the key generation process to use algorithms like Shawe-Taylor + * (from FIPS PUB186-4) which generate provable parameters out of a seed + * for RSA and DSA keys. See gnutls_x509_privkey_generate2() for more + * information. + * + * Note that when generating an elliptic curve key, the curve + * can be substituted in the place of the bits parameter using the + * GNUTLS_CURVE_TO_BITS() macro. The input to the macro is any curve from + * %gnutls_ecc_curve_t. + * + * For DSA keys, if the subgroup size needs to be specified check + * the GNUTLS_SUBGROUP_TO_BITS() macro. + * + * It is recommended to do not set the number of @bits directly, use gnutls_sec_param_to_pk_bits() instead . + * + * See also gnutls_privkey_generate(), gnutls_x509_privkey_generate2(). + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int +gnutls_x509_privkey_generate(gnutls_x509_privkey_t key, + gnutls_pk_algorithm_t algo, unsigned int bits, + unsigned int flags) +{ + return gnutls_x509_privkey_generate2(key, algo, bits, flags, NULL, 0); +} + +/** + * gnutls_x509_privkey_generate2: + * @key: a key + * @algo: is one of the algorithms in #gnutls_pk_algorithm_t. + * @bits: the size of the modulus + * @flags: Must be zero or flags from #gnutls_privkey_flags_t. + * @data: Allow specifying %gnutls_keygen_data_st types such as the seed to be used. + * @data_size: The number of @data available. + * + * This function will generate a random private key. Note that this + * function must be called on an initialized private key. + * + * The flag %GNUTLS_PRIVKEY_FLAG_PROVABLE + * instructs the key generation process to use algorithms like Shawe-Taylor + * (from FIPS PUB186-4) which generate provable parameters out of a seed + * for RSA and DSA keys. On DSA keys the PQG parameters are generated using the + * seed, while on RSA the two primes. To specify an explicit seed + * (by default a random seed is used), use the @data with a %GNUTLS_KEYGEN_SEED + * type. + * + * Note that when generating an elliptic curve key, the curve + * can be substituted in the place of the bits parameter using the + * GNUTLS_CURVE_TO_BITS() macro. + * + * To export the generated keys in memory or in files it is recommended to use the + * PKCS#8 form as it can handle all key types, and can store additional parameters + * such as the seed, in case of provable RSA or DSA keys. + * Generated keys can be exported in memory using gnutls_privkey_export_x509(), + * and then with gnutls_x509_privkey_export2_pkcs8(). + * + * If key generation is part of your application, avoid setting the number + * of bits directly, and instead use gnutls_sec_param_to_pk_bits(). + * That way the generated keys will adapt to the security levels + * of the underlying GnuTLS library. + * + * See also gnutls_privkey_generate2(). + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int +gnutls_x509_privkey_generate2(gnutls_x509_privkey_t key, + gnutls_pk_algorithm_t algo, unsigned int bits, + unsigned int flags, const gnutls_keygen_data_st *data, unsigned data_size) +{ + int ret; + unsigned i; + gnutls_x509_spki_t tpki = NULL; + + if (key == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + gnutls_pk_params_init(&key->params); + + for (i=0;i<data_size;i++) { + if (data[i].type == GNUTLS_KEYGEN_SEED && data[i].size < sizeof(key->params.seed)) { + key->params.seed_size = data[i].size; + memcpy(key->params.seed, data[i].data, data[i].size); + } else if (data[i].type == GNUTLS_KEYGEN_DIGEST) { + key->params.palgo = data[i].size; + } else if (data[i].type == GNUTLS_KEYGEN_SPKI) { + tpki = (void*)data[i].data; + } + } + + if (IS_EC(algo)) { + if (GNUTLS_BITS_ARE_CURVE(bits)) + bits = GNUTLS_BITS_TO_CURVE(bits); + else + bits = _gnutls_ecc_bits_to_curve(algo, bits); + + if (gnutls_ecc_curve_get_pk(bits) != algo) { + _gnutls_debug_log("curve is incompatible with public key algorithm\n"); + return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + } + } + + if (IS_GOSTEC(algo)) { + int size; + + if (GNUTLS_BITS_ARE_CURVE(bits)) + bits = GNUTLS_BITS_TO_CURVE(bits); + else + bits = _gnutls_ecc_bits_to_curve(algo, bits); + + size = gnutls_ecc_curve_get_size(bits); + + if ((algo == GNUTLS_PK_GOST_01 && size != 32) || + (algo == GNUTLS_PK_GOST_12_256 && size != 32) || + (algo == GNUTLS_PK_GOST_12_512 && size != 64)) { + _gnutls_debug_log("curve is incompatible with public key algorithm\n"); + return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + } + + key->params.gost_params = _gnutls_gost_paramset_default(algo); + } + + if (flags & GNUTLS_PRIVKEY_FLAG_PROVABLE) { + key->params.pkflags |= GNUTLS_PK_FLAG_PROVABLE; + } + + key->params.algo = algo; + + ret = _gnutls_pk_generate_params(algo, bits, &key->params); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + if (algo == GNUTLS_PK_RSA_PSS && (flags & GNUTLS_PRIVKEY_FLAG_CA) && + !key->params.spki.pk) { + const mac_entry_st *me; + key->params.spki.pk = GNUTLS_PK_RSA_PSS; + + key->params.spki.rsa_pss_dig = _gnutls_pk_bits_to_sha_hash(bits); + + me = hash_to_entry(key->params.spki.rsa_pss_dig); + if (unlikely(me == NULL)) { + gnutls_assert(); + ret = GNUTLS_E_INVALID_REQUEST; + goto cleanup; + } + + ret = _gnutls_find_rsa_pss_salt_size(bits, me, 0); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + key->params.spki.salt_size = ret; + } + + ret = _gnutls_pk_generate_keys(algo, bits, &key->params, 0); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = _gnutls_pk_verify_priv_params(algo, &key->params); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + if (tpki) { + ret = gnutls_x509_privkey_set_spki(key, tpki, 0); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + } + + ret = _gnutls_asn1_encode_privkey(&key->key, &key->params); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + return 0; + + cleanup: + key->params.algo = GNUTLS_PK_UNKNOWN; + gnutls_pk_params_clear(&key->params); + gnutls_pk_params_release(&key->params); + + return ret; +} + +/** + * gnutls_x509_privkey_get_seed: + * @key: should contain a #gnutls_x509_privkey_t type + * @digest: if non-NULL it will contain the digest algorithm used for key generation (if applicable) + * @seed: where seed will be copied to + * @seed_size: originally holds the size of @seed, will be updated with actual size + * + * This function will return the seed that was used to generate the + * given private key. That function will succeed only if the key was generated + * as a provable key. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 3.5.0 + **/ +int gnutls_x509_privkey_get_seed(gnutls_x509_privkey_t key, gnutls_digest_algorithm_t *digest, void *seed, size_t *seed_size) +{ + if (key->params.seed_size == 0) + return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + + if (seed_size == NULL || seed == NULL) { + if (key->params.seed_size) + return gnutls_assert_val(GNUTLS_E_SHORT_MEMORY_BUFFER); + else + return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + } + + if (*seed_size < key->params.seed_size) { + *seed_size = key->params.seed_size; + return gnutls_assert_val(GNUTLS_E_SHORT_MEMORY_BUFFER); + } + + if (digest) + *digest = key->params.palgo; + + memcpy(seed, key->params.seed, key->params.seed_size); + *seed_size = key->params.seed_size; + return 0; +} + +static +int cmp_rsa_key(gnutls_x509_privkey_t key1, gnutls_x509_privkey_t key2) +{ + gnutls_datum_t m1 = {NULL, 0}, e1 = {NULL, 0}, d1 = {NULL, 0}, p1 = {NULL, 0}, q1 = {NULL, 0}; + gnutls_datum_t m2 = {NULL, 0}, e2 = {NULL, 0}, d2 = {NULL, 0}, p2 = {NULL, 0}, q2 = {NULL, 0}; + int ret; + + ret = gnutls_x509_privkey_export_rsa_raw(key1, &m1, &e1, &d1, &p1, &q1, NULL); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + ret = gnutls_x509_privkey_export_rsa_raw(key2, &m2, &e2, &d2, &p2, &q2, NULL); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + if (m1.size != m2.size || memcmp(m1.data, m2.data, m1.size) != 0) { + gnutls_assert(); + ret = GNUTLS_E_PRIVKEY_VERIFICATION_ERROR; + goto cleanup; + } + + if (d1.size != d2.size || memcmp(d1.data, d2.data, d1.size) != 0) { + gnutls_assert(); + ret = GNUTLS_E_PRIVKEY_VERIFICATION_ERROR; + goto cleanup; + } + + if (e1.size != e2.size || memcmp(e1.data, e2.data, e1.size) != 0) { + gnutls_assert(); + ret = GNUTLS_E_PRIVKEY_VERIFICATION_ERROR; + goto cleanup; + } + + if (p1.size != p2.size || memcmp(p1.data, p2.data, p1.size) != 0) { + gnutls_assert(); + ret = GNUTLS_E_PRIVKEY_VERIFICATION_ERROR; + goto cleanup; + } + + if (q1.size != q2.size || memcmp(q1.data, q2.data, q1.size) != 0) { + gnutls_assert(); + ret = GNUTLS_E_PRIVKEY_VERIFICATION_ERROR; + goto cleanup; + } + + ret = 0; + cleanup: + gnutls_free(m1.data); + gnutls_free(e1.data); + gnutls_free(d1.data); + gnutls_free(p1.data); + gnutls_free(q1.data); + gnutls_free(m2.data); + gnutls_free(e2.data); + gnutls_free(d2.data); + gnutls_free(p2.data); + gnutls_free(q2.data); + return ret; +} + +static +int cmp_dsa_key(gnutls_x509_privkey_t key1, gnutls_x509_privkey_t key2) +{ + gnutls_datum_t p1 = {NULL, 0}, q1 = {NULL, 0}, g1 = {NULL, 0}; + gnutls_datum_t p2 = {NULL, 0}, q2 = {NULL, 0}, g2 = {NULL, 0}; + int ret; + + ret = gnutls_x509_privkey_export_dsa_raw(key1, &p1, &q1, &g1, NULL, NULL); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + ret = gnutls_x509_privkey_export_dsa_raw(key2, &p2, &q2, &g2, NULL, NULL); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + if (g1.size != g2.size || memcmp(g1.data, g2.data, g1.size) != 0) { + gnutls_assert(); + ret = GNUTLS_E_PRIVKEY_VERIFICATION_ERROR; + goto cleanup; + } + + if (p1.size != p2.size || memcmp(p1.data, p2.data, p1.size) != 0) { + gnutls_assert(); + ret = GNUTLS_E_PRIVKEY_VERIFICATION_ERROR; + goto cleanup; + } + + if (q1.size != q2.size || memcmp(q1.data, q2.data, q1.size) != 0) { + gnutls_assert(); + ret = GNUTLS_E_PRIVKEY_VERIFICATION_ERROR; + goto cleanup; + } + + ret = 0; + cleanup: + gnutls_free(g1.data); + gnutls_free(p1.data); + gnutls_free(q1.data); + gnutls_free(g2.data); + gnutls_free(p2.data); + gnutls_free(q2.data); + return ret; +} + +/** + * gnutls_x509_privkey_verify_seed: + * @key: should contain a #gnutls_x509_privkey_t type + * @digest: it contains the digest algorithm used for key generation (if applicable) + * @seed: the seed of the key to be checked with + * @seed_size: holds the size of @seed + * + * This function will verify that the given private key was generated from + * the provided seed. If @seed is %NULL then the seed stored in the @key's structure + * will be used for verification. + * + * Returns: In case of a verification failure %GNUTLS_E_PRIVKEY_VERIFICATION_ERROR + * is returned, and zero or positive code on success. + * + * Since: 3.5.0 + **/ +int gnutls_x509_privkey_verify_seed(gnutls_x509_privkey_t key, gnutls_digest_algorithm_t digest, const void *seed, size_t seed_size) +{ + int ret; + gnutls_x509_privkey_t okey; + unsigned bits; + gnutls_keygen_data_st data; + + if (key == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + if (key->params.algo != GNUTLS_PK_RSA && key->params.algo != GNUTLS_PK_DSA) + return gnutls_assert_val(GNUTLS_E_UNIMPLEMENTED_FEATURE); + + ret = gnutls_x509_privkey_get_pk_algorithm2(key, &bits); + if (ret < 0) + return gnutls_assert_val(ret); + + ret = gnutls_x509_privkey_init(&okey); + if (ret < 0) + return gnutls_assert_val(ret); + + if (seed == NULL) { + seed = key->params.seed; + seed_size = key->params.seed_size; + } + + if (seed == NULL || seed_size == 0) + return gnutls_assert_val(GNUTLS_E_PK_NO_VALIDATION_PARAMS); + + data.type = GNUTLS_KEYGEN_SEED; + data.data = (void*)seed; + data.size = seed_size; + + ret = gnutls_x509_privkey_generate2(okey, key->params.algo, bits, + GNUTLS_PRIVKEY_FLAG_PROVABLE, &data, 1); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + if (key->params.algo == GNUTLS_PK_RSA) + ret = cmp_rsa_key(key, okey); + else + ret = cmp_dsa_key(key, okey); + + cleanup: + gnutls_x509_privkey_deinit(okey); + + return ret; +} + +/** + * gnutls_x509_privkey_verify_params: + * @key: a key + * + * This function will verify the private key parameters. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int gnutls_x509_privkey_verify_params(gnutls_x509_privkey_t key) +{ + int ret; + + ret = _gnutls_pk_verify_priv_params(key->params.algo, &key->params); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + return 0; +} + +/** + * gnutls_x509_privkey_get_key_id: + * @key: a key + * @flags: should be one of the flags from %gnutls_keyid_flags_t + * @output_data: will contain the key ID + * @output_data_size: holds the size of output_data (and will be + * replaced by the actual size of parameters) + * + * This function will return a unique ID that depends on the public key + * parameters. This ID can be used in checking whether a certificate + * corresponds to the given key. + * + * If the buffer provided is not long enough to hold the output, then + * *@output_data_size is updated and %GNUTLS_E_SHORT_MEMORY_BUFFER will + * be returned. The output will normally be a SHA-1 hash output, + * which is 20 bytes. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int +gnutls_x509_privkey_get_key_id(gnutls_x509_privkey_t key, + unsigned int flags, + unsigned char *output_data, + size_t * output_data_size) +{ + int ret; + + if (key == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + ret = + _gnutls_get_key_id(&key->params, + output_data, output_data_size, flags); + if (ret < 0) { + gnutls_assert(); + } + + return ret; +} + + +/** + * gnutls_x509_privkey_sign_hash: + * @key: a key + * @hash: holds the data to be signed + * @signature: will contain newly allocated signature + * + * This function will sign the given hash using the private key. Do not + * use this function directly unless you know what it is. Typical signing + * requires the data to be hashed and stored in special formats + * (e.g. BER Digest-Info for RSA). + * + * This API is provided only for backwards compatibility, and thus + * restricted to RSA, DSA and ECDSA key types. For other key types please + * use gnutls_privkey_sign_hash() and gnutls_privkey_sign_data(). + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Deprecated in: 2.12.0 + */ +int +gnutls_x509_privkey_sign_hash(gnutls_x509_privkey_t key, + const gnutls_datum_t * hash, + gnutls_datum_t * signature) +{ + int result; + + if (key == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + if (key->params.algo != GNUTLS_PK_RSA && key->params.algo != GNUTLS_PK_ECDSA && + key->params.algo != GNUTLS_PK_DSA) { + /* too primitive API - use only with legacy types */ + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + result = + _gnutls_pk_sign(key->params.algo, signature, hash, + &key->params, &key->params.spki); + + if (result < 0) { + gnutls_assert(); + return result; + } + + return 0; +} + +/** + * gnutls_x509_privkey_sign_data: + * @key: a key + * @digest: should be a digest algorithm + * @flags: should be 0 for now + * @data: holds the data to be signed + * @signature: will contain the signature + * @signature_size: holds the size of signature (and will be replaced + * by the new size) + * + * This function will sign the given data using a signature algorithm + * supported by the private key. Signature algorithms are always used + * together with a hash functions. Different hash functions may be + * used for the RSA algorithm, but only SHA-1 for the DSA keys. + * + * If the buffer provided is not long enough to hold the output, then + * *@signature_size is updated and %GNUTLS_E_SHORT_MEMORY_BUFFER will + * be returned. + * + * Use gnutls_x509_crt_get_preferred_hash_algorithm() to determine + * the hash algorithm. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + */ +int +gnutls_x509_privkey_sign_data(gnutls_x509_privkey_t key, + gnutls_digest_algorithm_t digest, + unsigned int flags, + const gnutls_datum_t * data, + void *signature, size_t * signature_size) +{ + gnutls_privkey_t privkey; + gnutls_datum_t sig = {NULL, 0}; + int ret; + + ret = gnutls_privkey_init(&privkey); + if (ret < 0) + return gnutls_assert_val(ret); + + ret = gnutls_privkey_import_x509(privkey, key, 0); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = gnutls_privkey_sign_data(privkey, digest, flags, data, &sig); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + if (*signature_size < sig.size) { + *signature_size = sig.size; + ret = GNUTLS_E_SHORT_MEMORY_BUFFER; + goto cleanup; + } + + *signature_size = sig.size; + memcpy(signature, sig.data, sig.size); + +cleanup: + _gnutls_free_datum(&sig); + gnutls_privkey_deinit(privkey); + return ret; +} + +/** + * gnutls_x509_privkey_fix: + * @key: a key + * + * This function will recalculate the secondary parameters in a key. + * In RSA keys, this can be the coefficient and exponent1,2. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int gnutls_x509_privkey_fix(gnutls_x509_privkey_t key) +{ + int ret; + + if (key == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + if (key->key) { + asn1_delete_structure2(&key->key, ASN1_DELETE_FLAG_ZEROIZE); + + ret = + _gnutls_asn1_encode_privkey(&key->key, + &key->params); + if (ret < 0) { + gnutls_assert(); + return ret; + } + } + + return 0; +} + +/** + * gnutls_x509_privkey_set_pin_function: + * @privkey: The certificate structure + * @fn: the callback + * @userdata: data associated with the callback + * + * This function will set a callback function to be used when + * it is required to access a protected object. This function overrides + * the global function set using gnutls_pkcs11_set_pin_function(). + * + * Note that this callback is used when decrypting a key. + * + * Since: 3.4.0 + * + **/ +void gnutls_x509_privkey_set_pin_function(gnutls_x509_privkey_t privkey, + gnutls_pin_callback_t fn, + void *userdata) +{ + privkey->pin.cb = fn; + privkey->pin.data = userdata; +} + +/** + * gnutls_x509_privkey_set_flags: + * @key: A key of type #gnutls_x509_privkey_t + * @flags: flags from the %gnutls_privkey_flags + * + * This function will set flags for the specified private key, after + * it is generated. Currently this is useful for the %GNUTLS_PRIVKEY_FLAG_EXPORT_COMPAT + * to allow exporting a "provable" private key in backwards compatible way. + * + * Since: 3.5.0 + * + **/ +void gnutls_x509_privkey_set_flags(gnutls_x509_privkey_t key, + unsigned int flags) +{ + key->flags |= flags; +} + diff --git a/lib/x509/privkey_openssl.c b/lib/x509/privkey_openssl.c new file mode 100644 index 0000000..9fc70e0 --- /dev/null +++ b/lib/x509/privkey_openssl.c @@ -0,0 +1,347 @@ +/* + * Copyright (C) 2012 Free Software Foundation, Inc. + * + * Author: David Woodhouse + * + * This file is part of GnuTLS. + * + * The GnuTLS is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/> + * + */ + +#include "gnutls_int.h" + +#include <datum.h> +#include <global.h> +#include "errors.h" +#include <common.h> +#include <x509.h> +#include <x509_b64.h> +#include "x509_int.h" +#include <algorithms.h> +#include <num.h> +#include <random.h> + +static int +openssl_hash_password(const char *_password, gnutls_datum_t * key, + gnutls_datum_t * salt) +{ + unsigned char md5[16]; + digest_hd_st hd; + unsigned int count = 0; + int ret; + char *password = NULL; + + if (_password != NULL) { + gnutls_datum_t pout; + ret = _gnutls_utf8_password_normalize(_password, strlen(_password), &pout, 1); + if (ret < 0) + return gnutls_assert_val(ret); + + password = (char*)pout.data; + } + + while (count < key->size) { + ret = _gnutls_hash_init(&hd, mac_to_entry(GNUTLS_MAC_MD5)); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + if (count) { + ret = _gnutls_hash(&hd, md5, sizeof(md5)); + if (ret < 0) { + hash_err: + _gnutls_hash_deinit(&hd, NULL); + gnutls_assert(); + goto cleanup; + } + } + + if (password) { + ret = _gnutls_hash(&hd, password, strlen(password)); + if (ret < 0) { + gnutls_assert(); + goto hash_err; + } + } + ret = _gnutls_hash(&hd, salt->data, 8); + if (ret < 0) { + gnutls_assert(); + goto hash_err; + } + + _gnutls_hash_deinit(&hd, md5); + + if (key->size - count <= sizeof(md5)) { + memcpy(&key->data[count], md5, key->size - count); + break; + } + + memcpy(&key->data[count], md5, sizeof(md5)); + count += sizeof(md5); + } + ret = 0; + + cleanup: + gnutls_free(password); + return ret; +} + +struct pem_cipher { + const char *name; + gnutls_cipher_algorithm_t cipher; +}; + +static const struct pem_cipher pem_ciphers[] = { + {"DES-CBC", GNUTLS_CIPHER_DES_CBC}, + {"DES-EDE3-CBC", GNUTLS_CIPHER_3DES_CBC}, + {"AES-128-CBC", GNUTLS_CIPHER_AES_128_CBC}, + {"AES-192-CBC", GNUTLS_CIPHER_AES_192_CBC}, + {"AES-256-CBC", GNUTLS_CIPHER_AES_256_CBC}, + {"CAMELLIA-128-CBC", GNUTLS_CIPHER_CAMELLIA_128_CBC}, + {"CAMELLIA-192-CBC", GNUTLS_CIPHER_CAMELLIA_192_CBC}, + {"CAMELLIA-256-CBC", GNUTLS_CIPHER_CAMELLIA_256_CBC}, +}; + +/** + * gnutls_x509_privkey_import_openssl: + * @key: The data to store the parsed key + * @data: The DER or PEM encoded key. + * @password: the password to decrypt the key (if it is encrypted). + * + * This function will convert the given PEM encrypted to + * the native gnutls_x509_privkey_t format. The + * output will be stored in @key. + * + * The @password should be in ASCII. If the password is not provided + * or wrong then %GNUTLS_E_DECRYPTION_FAILED will be returned. + * + * If the Certificate is PEM encoded it should have a header of + * "PRIVATE KEY" and the "DEK-Info" header. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int +gnutls_x509_privkey_import_openssl(gnutls_x509_privkey_t key, + const gnutls_datum_t * data, + const char *password) +{ + gnutls_cipher_hd_t handle; + gnutls_cipher_algorithm_t cipher = GNUTLS_CIPHER_UNKNOWN; + gnutls_datum_t b64_data; + gnutls_datum_t salt, enc_key; + unsigned char *key_data; + size_t key_data_size; + const char *pem_header = (void *) data->data; + const char *pem_header_start = (void *) data->data; + ssize_t pem_header_size; + int ret; + unsigned int i, iv_size, l; + + pem_header_size = data->size; + + pem_header = + memmem(pem_header, pem_header_size, "PRIVATE KEY---", 14); + if (pem_header == NULL) { + gnutls_assert(); + return GNUTLS_E_PARSING_ERROR; + } + + pem_header_size -= (ptrdiff_t) (pem_header - pem_header_start); + + pem_header = memmem(pem_header, pem_header_size, "DEK-Info: ", 10); + if (pem_header == NULL) { + gnutls_assert(); + return GNUTLS_E_PARSING_ERROR; + } + + pem_header_size = + data->size - (ptrdiff_t) (pem_header - pem_header_start) - 10; + pem_header += 10; + + for (i = 0; i < sizeof(pem_ciphers) / sizeof(pem_ciphers[0]); i++) { + l = strlen(pem_ciphers[i].name); + if (!strncmp(pem_header, pem_ciphers[i].name, l) && + pem_header[l] == ',') { + pem_header += l + 1; + cipher = pem_ciphers[i].cipher; + break; + } + } + + if (cipher == GNUTLS_CIPHER_UNKNOWN) { + _gnutls_debug_log + ("Unsupported PEM encryption type: %.10s\n", + pem_header); + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + iv_size = gnutls_cipher_get_iv_size(cipher); + salt.size = iv_size; + salt.data = gnutls_malloc(salt.size); + if (!salt.data) + return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + + for (i = 0; i < salt.size * 2; i++) { + unsigned char x; + const char *c = &pem_header[i]; + + if (*c >= '0' && *c <= '9') + x = (*c) - '0'; + else if (*c >= 'A' && *c <= 'F') + x = (*c) - 'A' + 10; + else { + gnutls_assert(); + /* Invalid salt in encrypted PEM file */ + ret = GNUTLS_E_INVALID_REQUEST; + goto out_salt; + } + if (i & 1) + salt.data[i / 2] |= x; + else + salt.data[i / 2] = x << 4; + } + + pem_header += salt.size * 2; + if (*pem_header != '\r' && *pem_header != '\n') { + gnutls_assert(); + ret = GNUTLS_E_INVALID_REQUEST; + goto out_salt; + } + while (*pem_header == '\n' || *pem_header == '\r') + pem_header++; + + ret = + _gnutls_base64_decode((const void *) pem_header, + pem_header_size, &b64_data); + if (ret < 0) { + gnutls_assert(); + goto out_salt; + } + + if (b64_data.size < 16) { + /* Just to be sure our parsing is OK */ + gnutls_assert(); + ret = GNUTLS_E_PARSING_ERROR; + goto out_b64; + } + + enc_key.size = gnutls_cipher_get_key_size(cipher); + enc_key.data = gnutls_malloc(enc_key.size); + if (!enc_key.data) { + ret = gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + goto out_b64; + } + + key_data_size = b64_data.size; + key_data = gnutls_malloc(key_data_size); + if (!key_data) { + ret = gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + goto out_enc_key; + } + + while (1) { + memcpy(key_data, b64_data.data, key_data_size); + + ret = openssl_hash_password(password, &enc_key, &salt); + if (ret < 0) { + gnutls_assert(); + goto out; + } + + ret = gnutls_cipher_init(&handle, cipher, &enc_key, &salt); + if (ret < 0) { + gnutls_assert(); + gnutls_cipher_deinit(handle); + goto out; + } + + ret = + gnutls_cipher_decrypt(handle, key_data, key_data_size); + gnutls_cipher_deinit(handle); + + if (ret < 0) { + gnutls_assert(); + goto out; + } + + /* We have to strip any padding to accept it. + So a bit more ASN.1 parsing for us. */ + if (key_data[0] == 0x30) { + gnutls_datum_t key_datum; + unsigned int blocksize = + gnutls_cipher_get_block_size(cipher); + unsigned int keylen = key_data[1]; + unsigned int ofs = 2; + + if (keylen & 0x80) { + int lenlen = keylen & 0x7f; + keylen = 0; + + if (lenlen > 3) { + gnutls_assert(); + goto fail; + } + + while (lenlen) { + keylen <<= 8; + keylen |= key_data[ofs++]; + lenlen--; + } + } + keylen += ofs; + + /* If there appears to be more or less padding than required, fail */ + if (key_data_size - keylen > blocksize || key_data_size < keylen+1) { + gnutls_assert(); + goto fail; + } + + /* If the padding bytes aren't all equal to the amount of padding, fail */ + ofs = keylen; + while (ofs < key_data_size) { + if (key_data[ofs] != + key_data_size - keylen) { + gnutls_assert(); + goto fail; + } + ofs++; + } + + key_datum.data = key_data; + key_datum.size = keylen; + ret = + gnutls_x509_privkey_import(key, &key_datum, + GNUTLS_X509_FMT_DER); + if (ret == 0) + goto out; + } + fail: + ret = GNUTLS_E_DECRYPTION_FAILED; + goto out; + } + out: + zeroize_key(key_data, key_data_size); + gnutls_free(key_data); + out_enc_key: + _gnutls_free_key_datum(&enc_key); + out_b64: + gnutls_free(b64_data.data); + out_salt: + gnutls_free(salt.data); + return ret; +} diff --git a/lib/x509/privkey_pkcs8.c b/lib/x509/privkey_pkcs8.c new file mode 100644 index 0000000..c22ece9 --- /dev/null +++ b/lib/x509/privkey_pkcs8.c @@ -0,0 +1,1675 @@ +/* + * Copyright (C) 2003-2016 Free Software Foundation, Inc. + * Copyright (C) 2014-2017 Red Hat + * Copyright (C) 2014-2016 Nikos Mavrogiannopoulos + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GnuTLS. + * + * The GnuTLS is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/> + * + */ + +#include "gnutls_int.h" + +#include <datum.h> +#include <global.h> +#include "errors.h" +#include <common.h> +#include <x509.h> +#include <x509_b64.h> +#include "x509_int.h" +#include "pkcs7_int.h" +#include <algorithms.h> +#include <num.h> +#include <random.h> +#include <pk.h> +#include "attributes.h" +#include "prov-seed.h" + +static int _decode_pkcs8_ecc_key(asn1_node pkcs8_asn, + gnutls_x509_privkey_t pkey); +static +int pkcs8_key_info(const gnutls_datum_t * raw_key, + const struct pkcs_cipher_schema_st **p, + struct pbkdf2_params *kdf_params, + char **oid); + +static int decode_private_key_info(const gnutls_datum_t * der, + gnutls_x509_privkey_t pkey); + +#define PEM_PKCS8 "ENCRYPTED PRIVATE KEY" +#define PEM_UNENCRYPTED_PKCS8 "PRIVATE KEY" + +/* Returns a negative error code if the encryption schema in + * the OID is not supported. The schema ID is returned. + */ +/* Encodes a private key to the raw format PKCS #8 needs. + * For RSA it is a PKCS #1 DER private key and for DSA it is + * an ASN.1 INTEGER of the x value. + */ +inline static int +_encode_privkey(gnutls_x509_privkey_t pkey, gnutls_datum_t * raw) +{ + int ret; + asn1_node spk = NULL; + + switch (pkey->params.algo) { + case GNUTLS_PK_EDDSA_ED25519: + case GNUTLS_PK_EDDSA_ED448: + case GNUTLS_PK_ECDH_X25519: + case GNUTLS_PK_ECDH_X448: + /* we encode as octet string (which is going to be stored inside + * another octet string). No comments. */ + ret = _gnutls_x509_encode_string(ASN1_ETYPE_OCTET_STRING, + pkey->params.raw_priv.data, pkey->params.raw_priv.size, + raw); + if (ret < 0) + gnutls_assert(); + return ret; + + case GNUTLS_PK_GOST_01: + case GNUTLS_PK_GOST_12_256: + case GNUTLS_PK_GOST_12_512: + if ((ret = asn1_create_element + (_gnutls_get_gnutls_asn(), "GNUTLS.GOSTPrivateKey", &spk)) + != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(ret); + goto error; + } + + ret = _gnutls_x509_write_key_int_le(spk, "", pkey->params.params[GOST_K]); + if (ret < 0) { + gnutls_assert(); + goto error; + } + + ret = _gnutls_x509_der_encode(spk, "", raw, 0); + if (ret < 0) { + gnutls_assert(); + goto error; + } + + asn1_delete_structure2(&spk, ASN1_DELETE_FLAG_ZEROIZE); + break; + + case GNUTLS_PK_RSA: + case GNUTLS_PK_RSA_PSS: + case GNUTLS_PK_ECDSA: + ret = + _gnutls_x509_export_int2(pkey->key, GNUTLS_X509_FMT_DER, + "", raw); + if (ret < 0) { + gnutls_assert(); + goto error; + } + + break; + case GNUTLS_PK_DSA: + /* DSAPublicKey == INTEGER */ + if ((ret = asn1_create_element + (_gnutls_get_gnutls_asn(), "GNUTLS.DSAPublicKey", + &spk)) + != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(ret); + } + + ret = + _gnutls_x509_write_int(spk, "", pkey->params.params[4], + 1); + if (ret < 0) { + gnutls_assert(); + goto error; + } + ret = _gnutls_x509_der_encode(spk, "", raw, 0); + if (ret < 0) { + gnutls_assert(); + goto error; + } + + asn1_delete_structure2(&spk, ASN1_DELETE_FLAG_ZEROIZE); + break; + + default: + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + return 0; + + error: + asn1_delete_structure2(&spk, ASN1_DELETE_FLAG_ZEROIZE); + asn1_delete_structure(&spk); + return ret; + +} + +/* + * Encodes a PKCS #1 private key to a PKCS #8 private key + * info. The output will be allocated and stored into der. Also + * the asn1_node of private key info will be returned. + */ +static int +encode_to_private_key_info(gnutls_x509_privkey_t pkey, + gnutls_datum_t * der, asn1_node * pkey_info) +{ + int result, len; + uint8_t null = 0; + const char *oid; + gnutls_datum_t algo_params = { NULL, 0 }; + gnutls_datum_t algo_privkey = { NULL, 0 }; + + oid = gnutls_pk_get_oid(pkey->params.algo); + if (oid == NULL) { + gnutls_assert(); + return GNUTLS_E_UNIMPLEMENTED_FEATURE; + } + + result = + _gnutls_x509_write_pubkey_params(&pkey->params, &algo_params); + if (result < 0) { + gnutls_assert(); + return result; + } + + if ((result = + asn1_create_element(_gnutls_get_pkix(), + "PKIX1.pkcs-8-PrivateKeyInfo", + pkey_info)) != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto error; + } + + /* Write the version. + */ + result = asn1_write_value(*pkey_info, "version", &null, 1); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto error; + } + + /* write the privateKeyAlgorithm + * fields. (OID+NULL data) + */ + result = + asn1_write_value(*pkey_info, "privateKeyAlgorithm.algorithm", + oid, 1); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto error; + } + + result = + asn1_write_value(*pkey_info, "privateKeyAlgorithm.parameters", + algo_params.data, algo_params.size); + _gnutls_free_key_datum(&algo_params); + + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto error; + } + + + /* Write the raw private key + */ + result = _encode_privkey(pkey, &algo_privkey); + if (result < 0) { + gnutls_assert(); + goto error; + } + + result = + asn1_write_value(*pkey_info, "privateKey", algo_privkey.data, + algo_privkey.size); + _gnutls_free_key_datum(&algo_privkey); + + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto error; + } + + if ((pkey->params.pkflags & GNUTLS_PK_FLAG_PROVABLE) && pkey->params.seed_size > 0) { + gnutls_datum_t seed_info; + /* rfc8479 attribute encoding */ + + result = _x509_encode_provable_seed(pkey, &seed_info); + if (result < 0) { + gnutls_assert(); + goto error; + } + + result = _x509_set_attribute(*pkey_info, "attributes", OID_ATTR_PROV_SEED, &seed_info); + gnutls_free(seed_info.data); + if (result < 0) { + gnutls_assert(); + goto error; + } + } else { + /* Append an empty Attributes field. + */ + result = asn1_write_value(*pkey_info, "attributes", NULL, 0); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto error; + } + } + + /* DER Encode the generated private key info. + */ + len = 0; + result = asn1_der_coding(*pkey_info, "", NULL, &len, NULL); + if (result != ASN1_MEM_ERROR) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto error; + } + + /* allocate data for the der + */ + der->size = len; + der->data = gnutls_malloc(len); + if (der->data == NULL) { + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; + } + + result = asn1_der_coding(*pkey_info, "", der->data, &len, NULL); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto error; + } + + return 0; + + error: + asn1_delete_structure2(pkey_info, ASN1_DELETE_FLAG_ZEROIZE); + _gnutls_free_datum(&algo_params); + _gnutls_free_key_datum(&algo_privkey); + return result; + +} + +/* Converts a PKCS #8 private key info to + * a PKCS #8 EncryptedPrivateKeyInfo. + */ +static int +encode_to_pkcs8_key(schema_id schema, const gnutls_datum_t * der_key, + const char *password, asn1_node * out) +{ + int result; + gnutls_datum_t key = { NULL, 0 }; + gnutls_datum_t tmp = { NULL, 0 }; + asn1_node pkcs8_asn = NULL; + struct pbkdf2_params kdf_params; + struct pbe_enc_params enc_params; + const struct pkcs_cipher_schema_st *s; + + s = _gnutls_pkcs_schema_get(schema); + if (s == NULL || s->decrypt_only) { + return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + } + + if ((result = + asn1_create_element(_gnutls_get_pkix(), + "PKIX1.pkcs-8-EncryptedPrivateKeyInfo", + &pkcs8_asn)) != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + /* Write the encryption schema OID + */ + result = + asn1_write_value(pkcs8_asn, "encryptionAlgorithm.algorithm", + s->write_oid, 1); + + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto error; + } + + /* Generate a symmetric key. + */ + + result = + _gnutls_pkcs_generate_key(schema, password, &kdf_params, &enc_params, &key); + if (result < 0) { + gnutls_assert(); + goto error; + } + + result = + _gnutls_pkcs_write_schema_params(schema, pkcs8_asn, + "encryptionAlgorithm.parameters", + &kdf_params, &enc_params); + if (result < 0) { + gnutls_assert(); + goto error; + } + + /* Parameters have been encoded. Now + * encrypt the Data. + */ + result = _gnutls_pkcs_raw_encrypt_data(der_key, &enc_params, &key, &tmp); + if (result < 0) { + gnutls_assert(); + goto error; + } + + /* write the encrypted data. + */ + result = + asn1_write_value(pkcs8_asn, "encryptedData", tmp.data, + tmp.size); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto error; + } + + _gnutls_free_datum(&tmp); + _gnutls_free_key_datum(&key); + + *out = pkcs8_asn; + + return 0; + + error: + _gnutls_free_key_datum(&key); + _gnutls_free_datum(&tmp); + asn1_delete_structure2(&pkcs8_asn, ASN1_DELETE_FLAG_ZEROIZE); + return result; +} + + +/** + * gnutls_x509_privkey_export_pkcs8: + * @key: Holds the key + * @format: the format of output params. One of PEM or DER. + * @password: the password that will be used to encrypt the key. + * @flags: an ORed sequence of gnutls_pkcs_encrypt_flags_t + * @output_data: will contain a private key PEM or DER encoded + * @output_data_size: holds the size of output_data (and will be + * replaced by the actual size of parameters) + * + * This function will export the private key to a PKCS8 structure. + * Both RSA and DSA keys can be exported. For DSA keys we use + * PKCS #11 definitions. If the flags do not specify the encryption + * cipher, then the default 3DES (PBES2) will be used. + * + * The @password can be either ASCII or UTF-8 in the default PBES2 + * encryption schemas, or ASCII for the PKCS12 schemas. + * + * If the buffer provided is not long enough to hold the output, then + * *output_data_size is updated and GNUTLS_E_SHORT_MEMORY_BUFFER will + * be returned. + * + * If the structure is PEM encoded, it will have a header + * of "BEGIN ENCRYPTED PRIVATE KEY" or "BEGIN PRIVATE KEY" if + * encryption is not used. + * + * Returns: In case of failure a negative error code will be + * returned, and 0 on success. + **/ +int +gnutls_x509_privkey_export_pkcs8(gnutls_x509_privkey_t key, + gnutls_x509_crt_fmt_t format, + const char *password, + unsigned int flags, + void *output_data, + size_t * output_data_size) +{ + asn1_node pkcs8_asn = NULL, pkey_info; + int ret; + gnutls_datum_t tmp = {NULL, 0}; + schema_id schema; + + if (key == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + /* Get the private key info + * tmp holds the DER encoding. + */ + ret = encode_to_private_key_info(key, &tmp, &pkey_info); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + schema = _gnutls_pkcs_flags_to_schema(flags); + + if (((flags & GNUTLS_PKCS_PLAIN) || password == NULL) + && !(flags & GNUTLS_PKCS_NULL_PASSWORD)) { + _gnutls_free_datum(&tmp); + + ret = + _gnutls_x509_export_int(pkey_info, format, + PEM_UNENCRYPTED_PKCS8, + output_data, output_data_size); + + asn1_delete_structure2(&pkey_info, ASN1_DELETE_FLAG_ZEROIZE); + } else { + asn1_delete_structure2(&pkey_info, ASN1_DELETE_FLAG_ZEROIZE); /* we don't need it */ + + ret = + encode_to_pkcs8_key(schema, &tmp, password, + &pkcs8_asn); + _gnutls_free_key_datum(&tmp); + + if (ret < 0) { + gnutls_assert(); + return ret; + } + + ret = + _gnutls_x509_export_int(pkcs8_asn, format, PEM_PKCS8, + output_data, output_data_size); + + asn1_delete_structure2(&pkcs8_asn, ASN1_DELETE_FLAG_ZEROIZE); + } + + return ret; +} + +/** + * gnutls_pkcs8_info: + * @data: Holds the PKCS #8 data + * @format: the format of the PKCS #8 data + * @schema: indicate the schema as one of %gnutls_pkcs_encrypt_flags_t + * @cipher: the cipher used as %gnutls_cipher_algorithm_t + * @salt: PBKDF2 salt (if non-NULL then @salt_size initially holds its size) + * @salt_size: PBKDF2 salt size + * @iter_count: PBKDF2 iteration count + * @oid: if non-NULL it will contain an allocated null-terminated variable with the OID + * + * This function will provide information on the algorithms used + * in a particular PKCS #8 structure. If the structure algorithms + * are unknown the code %GNUTLS_E_UNKNOWN_CIPHER_TYPE will be returned, + * and only @oid, will be set. That is, @oid will be set on encrypted PKCS #8 + * structures whether supported or not. It must be deinitialized using gnutls_free(). + * The other variables are only set on supported structures. + * + * Returns: %GNUTLS_E_INVALID_REQUEST if the provided structure isn't an encrypted key, + * %GNUTLS_E_UNKNOWN_CIPHER_TYPE if the structure's encryption isn't supported, or + * another negative error code in case of a failure. Zero on success. + * + * Since: 3.4.0 + **/ +int +gnutls_pkcs8_info(const gnutls_datum_t * data, gnutls_x509_crt_fmt_t format, + unsigned int *schema, unsigned int *cipher, + void *salt, unsigned int *salt_size, + unsigned int *iter_count, + char **oid) +{ + int ret = 0, need_free = 0; + gnutls_datum_t _data; + const struct pkcs_cipher_schema_st *p = NULL; + struct pbkdf2_params kdf; + + memset(&kdf, 0, sizeof(kdf)); + + if (oid) + *oid = NULL; + + _data.data = data->data; + _data.size = data->size; + + /* If the Certificate is in PEM format then decode it + */ + if (format == GNUTLS_X509_FMT_PEM) { + /* Try the first header + */ + ret = + _gnutls_fbase64_decode(PEM_UNENCRYPTED_PKCS8, + data->data, data->size, &_data); + + if (ret < 0) { /* Try the encrypted header + */ + ret = + _gnutls_fbase64_decode(PEM_PKCS8, data->data, + data->size, &_data); + + if (ret < 0) { + gnutls_assert(); + return ret; + } + } + + need_free = 1; + } + + ret = pkcs8_key_info(&_data, &p, &kdf, oid); + if (ret == GNUTLS_E_DECRYPTION_FAILED) + ret = GNUTLS_E_INVALID_REQUEST; + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + assert(p != NULL); + + if (need_free) + _gnutls_free_datum(&_data); + + if (schema) + *schema = p->flag; + + if (cipher) + *cipher = p->cipher; + + if (iter_count) + *iter_count = kdf.iter_count; + + if (salt) { + if (*salt_size >= (unsigned)kdf.salt_size) { + memcpy(salt, kdf.salt, kdf.salt_size); + } else { + *salt_size = kdf.salt_size; + ret = gnutls_assert_val(GNUTLS_E_SHORT_MEMORY_BUFFER); + goto cleanup; + } + } + + if (salt_size) + *salt_size = kdf.salt_size; + + return 0; + + cleanup: + if (ret != GNUTLS_E_UNKNOWN_CIPHER_TYPE && oid) { + gnutls_free(*oid); + } + if (need_free) + _gnutls_free_datum(&_data); + return ret; +} + +/** + * gnutls_x509_privkey_export2_pkcs8: + * @key: Holds the key + * @format: the format of output params. One of PEM or DER. + * @password: the password that will be used to encrypt the key. + * @flags: an ORed sequence of gnutls_pkcs_encrypt_flags_t + * @out: will contain a private key PEM or DER encoded + * + * This function will export the private key to a PKCS8 structure. + * Both RSA and DSA keys can be exported. For DSA keys we use + * PKCS #11 definitions. If the flags do not specify the encryption + * cipher, then the default 3DES (PBES2) will be used. + * + * The @password can be either ASCII or UTF-8 in the default PBES2 + * encryption schemas, or ASCII for the PKCS12 schemas. + * + * The output buffer is allocated using gnutls_malloc(). + * + * If the structure is PEM encoded, it will have a header + * of "BEGIN ENCRYPTED PRIVATE KEY" or "BEGIN PRIVATE KEY" if + * encryption is not used. + * + * Returns: In case of failure a negative error code will be + * returned, and 0 on success. + * + * Since 3.1.3 + **/ +int +gnutls_x509_privkey_export2_pkcs8(gnutls_x509_privkey_t key, + gnutls_x509_crt_fmt_t format, + const char *password, + unsigned int flags, gnutls_datum_t * out) +{ + asn1_node pkcs8_asn = NULL, pkey_info; + int ret; + gnutls_datum_t tmp = {NULL, 0}; + schema_id schema; + + if (key == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + /* Get the private key info + * tmp holds the DER encoding. + */ + ret = encode_to_private_key_info(key, &tmp, &pkey_info); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + schema = _gnutls_pkcs_flags_to_schema(flags); + + if (((flags & GNUTLS_PKCS_PLAIN) || password == NULL) + && !(flags & GNUTLS_PKCS_NULL_PASSWORD)) { + _gnutls_free_key_datum(&tmp); + + ret = + _gnutls_x509_export_int2(pkey_info, format, + PEM_UNENCRYPTED_PKCS8, out); + + asn1_delete_structure2(&pkey_info, ASN1_DELETE_FLAG_ZEROIZE); + } else { + asn1_delete_structure2(&pkey_info, ASN1_DELETE_FLAG_ZEROIZE); /* we don't need it */ + + ret = + encode_to_pkcs8_key(schema, &tmp, password, + &pkcs8_asn); + _gnutls_free_key_datum(&tmp); + + if (ret < 0) { + gnutls_assert(); + return ret; + } + + ret = + _gnutls_x509_export_int2(pkcs8_asn, format, PEM_PKCS8, + out); + + asn1_delete_structure2(&pkcs8_asn, ASN1_DELETE_FLAG_ZEROIZE); + } + + return ret; +} + + + + /* We've gotten this far. In the real world it's almost certain + * that we're dealing with a good file, but wrong password. + * Sadly like 90% of random data is somehow valid DER for the + * a first small number of bytes, so no easy way to guarantee. */ +#define CHECK_ERR_FOR_ENCRYPTED(result) \ + if (result == GNUTLS_E_ASN1_ELEMENT_NOT_FOUND || \ + result == GNUTLS_E_ASN1_IDENTIFIER_NOT_FOUND || \ + result == GNUTLS_E_ASN1_DER_ERROR || \ + result == GNUTLS_E_ASN1_VALUE_NOT_FOUND || \ + result == GNUTLS_E_ASN1_GENERIC_ERROR || \ + result == GNUTLS_E_ASN1_VALUE_NOT_VALID || \ + result == GNUTLS_E_ASN1_TAG_ERROR || \ + result == GNUTLS_E_ASN1_TAG_IMPLICIT || \ + result == GNUTLS_E_ASN1_TYPE_ANY_ERROR || \ + result == GNUTLS_E_ASN1_SYNTAX_ERROR || \ + result == GNUTLS_E_ASN1_DER_OVERFLOW) { \ + result = GNUTLS_E_DECRYPTION_FAILED; \ + } + +static int pkcs8_key_decrypt(const gnutls_datum_t * raw_key, + asn1_node pkcs8_asn, const char *password, + gnutls_x509_privkey_t pkey) +{ + int result, len; + char enc_oid[MAX_OID_SIZE]; + gnutls_datum_t tmp = {NULL, 0}; + int params_start, params_end, params_len; + struct pbkdf2_params kdf_params; + struct pbe_enc_params enc_params; + schema_id schema; + + /* Check the encryption schema OID + */ + len = sizeof(enc_oid); + result = + asn1_read_value(pkcs8_asn, "encryptionAlgorithm.algorithm", + enc_oid, &len); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + goto error; + } + + if ((result = _gnutls_check_pkcs_cipher_schema(enc_oid)) < 0) { + gnutls_assert(); + goto error; + } + + schema = result; + + /* Get the DER encoding of the parameters. + */ + result = + asn1_der_decoding_startEnd(pkcs8_asn, raw_key->data, + raw_key->size, + "encryptionAlgorithm.parameters", + ¶ms_start, ¶ms_end); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto error; + } + params_len = params_end - params_start + 1; + + result = + _gnutls_read_pkcs_schema_params(&schema, password, + &raw_key->data[params_start], + params_len, &kdf_params, &enc_params); + + if (result < 0) { + gnutls_assert(); + goto error; + } + + /* Parameters have been decoded. Now + * decrypt the EncryptedData. + */ + result = + _gnutls_pkcs_raw_decrypt_data(schema, pkcs8_asn, "encryptedData", password, + &kdf_params, &enc_params, &tmp); + if (result < 0) { + gnutls_assert(); + result = GNUTLS_E_DECRYPTION_FAILED; + goto error; + } + + result = decode_private_key_info(&tmp, pkey); + _gnutls_free_key_datum(&tmp); + + CHECK_ERR_FOR_ENCRYPTED(result); + if (result < 0) { + gnutls_assert(); + goto error; + } + + return 0; + + error: + return result; +} + +static int check_for_decrypted(const gnutls_datum_t *der) +{ + int result; + asn1_node pkcs8_asn = NULL; + + if ((result = + asn1_create_element(_gnutls_get_pkix(), + "PKIX1.pkcs-8-PrivateKeyInfo", + &pkcs8_asn)) != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + result = _asn1_strict_der_decode(&pkcs8_asn, der->data, der->size, NULL); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto error; + } + + result = 0; + error: + asn1_delete_structure2(&pkcs8_asn, ASN1_DELETE_FLAG_ZEROIZE); + return result; + +} + +static +int pkcs8_key_info(const gnutls_datum_t * raw_key, + const struct pkcs_cipher_schema_st **p, + struct pbkdf2_params *kdf_params, + char **oid) +{ + int result, len; + char enc_oid[MAX_OID_SIZE*2]; + int params_start, params_end, params_len; + struct pbe_enc_params enc_params; + schema_id schema; + asn1_node pkcs8_asn = NULL; + + memset(&enc_params, 0, sizeof(enc_params)); + + result = check_for_decrypted(raw_key); + if (result == 0) + return GNUTLS_E_INVALID_REQUEST; + + if ((result = + asn1_create_element(_gnutls_get_pkix(), + "PKIX1.pkcs-8-EncryptedPrivateKeyInfo", + &pkcs8_asn)) != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto error; + } + + result = + _asn1_strict_der_decode(&pkcs8_asn, raw_key->data, raw_key->size, + NULL); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto error; + } + + /* Check the encryption schema OID + */ + len = sizeof(enc_oid); + result = + asn1_read_value(pkcs8_asn, "encryptionAlgorithm.algorithm", + enc_oid, &len); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + goto error; + } + + if (oid) { + *oid = gnutls_strdup(enc_oid); + } + + if ((result = _gnutls_check_pkcs_cipher_schema(enc_oid)) < 0) { + gnutls_assert(); + goto error; + } + + schema = result; + + /* Get the DER encoding of the parameters. + */ + result = + asn1_der_decoding_startEnd(pkcs8_asn, raw_key->data, + raw_key->size, + "encryptionAlgorithm.parameters", + ¶ms_start, ¶ms_end); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto error; + } + params_len = params_end - params_start + 1; + + result = + _gnutls_read_pkcs_schema_params(&schema, NULL, + &raw_key->data[params_start], + params_len, kdf_params, &enc_params); + + if (result < 0) { + gnutls_assert(); + if (oid && enc_params.pbes2_oid[0] != 0) { + snprintf(enc_oid, sizeof(enc_oid), "%s/%s", *oid, enc_params.pbes2_oid); + gnutls_free(*oid); + *oid = gnutls_strdup(enc_oid); + } + goto error; + } + + *p = _gnutls_pkcs_schema_get(schema); + if (*p == NULL) { + gnutls_assert(); + result = GNUTLS_E_UNKNOWN_CIPHER_TYPE; + goto error; + } + + result = 0; + + error: + asn1_delete_structure2(&pkcs8_asn, ASN1_DELETE_FLAG_ZEROIZE); + return result; +} + +/* Converts a PKCS #8 key to + * an internal structure (gnutls_private_key) + * (normally a PKCS #1 encoded RSA key) + */ +static int +pkcs8_key_decode(const gnutls_datum_t * raw_key, + const char *password, gnutls_x509_privkey_t pkey, + unsigned int decrypt) +{ + int result; + asn1_node pkcs8_asn = NULL; + + if ((result = + asn1_create_element(_gnutls_get_pkix(), + "PKIX1.pkcs-8-EncryptedPrivateKeyInfo", + &pkcs8_asn)) != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto error; + } + + result = + _asn1_strict_der_decode(&pkcs8_asn, raw_key->data, raw_key->size, + NULL); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto error; + } + + if (decrypt) + result = + pkcs8_key_decrypt(raw_key, pkcs8_asn, password, pkey); + else + result = 0; + + error: + asn1_delete_structure2(&pkcs8_asn, ASN1_DELETE_FLAG_ZEROIZE); + return result; + +} + +/* Decodes an RSA privateKey from a PKCS8 structure. + */ +static int +_decode_pkcs8_rsa_key(asn1_node pkcs8_asn, gnutls_x509_privkey_t pkey) +{ + int ret; + gnutls_datum_t tmp = {NULL, 0}; + + ret = _gnutls_x509_read_value(pkcs8_asn, "privateKey", &tmp); + if (ret < 0) { + gnutls_assert(); + goto error; + } + + pkey->key = _gnutls_privkey_decode_pkcs1_rsa_key(&tmp, pkey); + _gnutls_free_key_datum(&tmp); + + if (pkey->key == NULL) { + ret = GNUTLS_E_PK_INVALID_PRIVKEY; + gnutls_assert(); + goto error; + } + + ret = 0; + + error: + return ret; +} + +/* Decodes an RSA-PSS privateKey from a PKCS8 structure. + */ +static int +_decode_pkcs8_rsa_pss_key(asn1_node pkcs8_asn, gnutls_x509_privkey_t pkey) +{ + int ret; + gnutls_datum_t tmp = {NULL, 0}; + gnutls_x509_spki_st params; + + memset(¶ms, 0, sizeof(params)); + + ret = _gnutls_x509_read_value(pkcs8_asn, + "privateKeyAlgorithm.parameters", &tmp); + if (ret < 0) { + if (ret == GNUTLS_E_ASN1_VALUE_NOT_FOUND || ret == GNUTLS_E_ASN1_ELEMENT_NOT_FOUND) + goto skip_params; + + gnutls_assert(); + goto error; + } + + ret = _gnutls_x509_read_rsa_pss_params(tmp.data, tmp.size, ¶ms); + _gnutls_free_key_datum(&tmp); + + if (ret < 0) { + gnutls_assert(); + goto error; + } + + skip_params: + ret = _decode_pkcs8_rsa_key(pkcs8_asn, pkey); + if (ret < 0) { + gnutls_assert(); + goto error; + } + + pkey->params.algo = GNUTLS_PK_RSA_PSS; + memcpy(&pkey->params.spki, ¶ms, sizeof(gnutls_x509_spki_st)); + + ret = 0; + + error: + return ret; +} + +/* Decodes an ECC privateKey from a PKCS8 structure. + */ +static int +_decode_pkcs8_ecc_key(asn1_node pkcs8_asn, gnutls_x509_privkey_t pkey) +{ + int ret; + gnutls_datum_t tmp = {NULL, 0}; + unsigned char oid[MAX_OID_SIZE]; + unsigned curve = GNUTLS_ECC_CURVE_INVALID; + int len, result; + + /* openssl PKCS #8 files with ECC keys place the curve in + * privateKeyAlgorithm.parameters instead of the ECPrivateKey.parameters. + */ + len = sizeof(oid); + result = + asn1_read_value(pkcs8_asn, "privateKeyAlgorithm.parameters", + oid, &len); + if (result == ASN1_SUCCESS) { + ret = _gnutls_x509_read_ecc_params(oid, len, &curve); + if (ret < 0) { + _gnutls_debug_log("PKCS#8: unknown curve OID %s\n", oid); + curve = GNUTLS_ECC_CURVE_INVALID; + } + } + + ret = _gnutls_x509_read_value(pkcs8_asn, "privateKey", &tmp); + if (ret < 0) { + gnutls_assert(); + goto error; + } + + ret = _gnutls_privkey_decode_ecc_key(&pkey->key, &tmp, pkey, curve); + _gnutls_free_key_datum(&tmp); + + if (ret < 0) { + gnutls_assert(); + goto error; + } + + ret = 0; + + error: + return ret; +} + +static int +_decode_pkcs8_eddsa_key(asn1_node pkcs8_asn, gnutls_x509_privkey_t pkey, const char *oid) +{ + int ret; + gnutls_datum_t tmp; + gnutls_ecc_curve_t curve = GNUTLS_ECC_CURVE_INVALID; + const gnutls_ecc_curve_entry_st *ce; + + gnutls_pk_params_init(&pkey->params); + + curve = gnutls_oid_to_ecc_curve(oid); + if (curve == GNUTLS_ECC_CURVE_INVALID) { + _gnutls_debug_log("PKCS#8: unknown curve OID %s\n", oid); + return gnutls_assert_val(GNUTLS_E_ECC_UNSUPPORTED_CURVE); + } + + ce = _gnutls_ecc_curve_get_params(curve); + if (_curve_is_eddsa(ce)) { + ret = _gnutls_x509_read_string(pkcs8_asn, "privateKey", &tmp, ASN1_ETYPE_OCTET_STRING, 1); + if (ret < 0) { + gnutls_assert(); + return gnutls_assert_val(ret); + } + + if (tmp.size != ce->size) { + gnutls_free(tmp.data); + return gnutls_assert_val(GNUTLS_E_ILLEGAL_PARAMETER); + } + gnutls_free(pkey->params.raw_priv.data); + switch (curve) { + case GNUTLS_ECC_CURVE_ED25519: + pkey->params.algo = GNUTLS_PK_EDDSA_ED25519; + break; + case GNUTLS_ECC_CURVE_ED448: + pkey->params.algo = GNUTLS_PK_EDDSA_ED448; + break; + default: + return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); + } + pkey->params.raw_priv.data = tmp.data; + pkey->params.raw_priv.size = tmp.size; + pkey->params.curve = curve; + + tmp.data = NULL; + return 0; + } else { + return gnutls_assert_val(GNUTLS_E_ECC_UNSUPPORTED_CURVE); + } +} + +static int +_decode_pkcs8_modern_ecdh_key(asn1_node pkcs8_asn, gnutls_x509_privkey_t pkey, const char *oid) +{ + int ret; + gnutls_datum_t tmp; + gnutls_ecc_curve_t curve = GNUTLS_ECC_CURVE_INVALID; + const gnutls_ecc_curve_entry_st *ce; + + gnutls_pk_params_init(&pkey->params); + + curve = gnutls_oid_to_ecc_curve(oid); + if (curve == GNUTLS_ECC_CURVE_INVALID) { + _gnutls_debug_log("PKCS#8: unknown curve OID %s\n", oid); + return gnutls_assert_val(GNUTLS_E_ECC_UNSUPPORTED_CURVE); + } + + ce = _gnutls_ecc_curve_get_params(curve); + if (_curve_is_modern_ecdh(ce)) { + ret = _gnutls_x509_read_string(pkcs8_asn, "privateKey", &tmp, ASN1_ETYPE_OCTET_STRING, 1); + if (ret < 0) { + gnutls_assert(); + return gnutls_assert_val(ret); + } + + if (tmp.size != ce->size) { + gnutls_free(tmp.data); + return gnutls_assert_val(GNUTLS_E_ILLEGAL_PARAMETER); + } + gnutls_free(pkey->params.raw_priv.data); + switch (curve) { + case GNUTLS_ECC_CURVE_X25519: + pkey->params.algo = GNUTLS_PK_ECDH_X25519; + break; + case GNUTLS_ECC_CURVE_X448: + pkey->params.algo = GNUTLS_PK_ECDH_X448; + break; + default: + return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); + } + pkey->params.raw_priv.data = tmp.data; + pkey->params.raw_priv.size = tmp.size; + pkey->params.curve = curve; + + tmp.data = NULL; + return 0; + } else { + return gnutls_assert_val(GNUTLS_E_ECC_UNSUPPORTED_CURVE); + } +} + +/* Converts a GOST key to + * an internal structure (gnutls_private_key) + */ +static int +_privkey_decode_gost_key(const gnutls_datum_t * raw_key, + gnutls_x509_privkey_t pkey) +{ + int ret; + int ecc_size = gnutls_ecc_curve_get_size(pkey->params.curve); + + /* Just to be sure here */ + if (ecc_size <= 0) { + gnutls_assert(); + ret = GNUTLS_E_ECC_UNSUPPORTED_CURVE; + goto error; + } + + /* Private key form described in R 50.1.112-2016. + * Private key can come up as masked value concatenated with several masks. + * each part is of ecc_size bytes. Key will be unmasked in pk_fixup */ + if (raw_key->size % ecc_size == 0) { + ret = _gnutls_mpi_init_scan_le(&pkey->params.params[GOST_K], + raw_key->data, raw_key->size); + if (ret < 0) { + gnutls_assert(); + goto error; + } + } else if (raw_key->data[0] == ASN1_TAG_INTEGER) { + asn1_node pkey_asn; + + /* Very old format: INTEGER packed in OCTET STRING */ + if ((ret = asn1_create_element(_gnutls_get_gnutls_asn(), + "GNUTLS.GOSTPrivateKeyOld", + &pkey_asn)) != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(ret); + goto error; + } + + ret = _asn1_strict_der_decode(&pkey_asn, + raw_key->data, raw_key->size, + NULL); + if (ret != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(ret); + asn1_delete_structure2(&pkey_asn, ASN1_DELETE_FLAG_ZEROIZE); + goto error; + } + + ret = _gnutls_x509_read_key_int(pkey_asn, "", + &pkey->params.params[GOST_K]); + if (ret < 0) { + gnutls_assert(); + asn1_delete_structure2(&pkey_asn, ASN1_DELETE_FLAG_ZEROIZE); + goto error; + } + asn1_delete_structure2(&pkey_asn, ASN1_DELETE_FLAG_ZEROIZE); + } else if (raw_key->data[0] == ASN1_TAG_OCTET_STRING) { + asn1_node pkey_asn; + + /* format: OCTET STRING packed in OCTET STRING */ + if ((ret = asn1_create_element(_gnutls_get_gnutls_asn(), + "GNUTLS.GOSTPrivateKey", + &pkey_asn)) != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(ret); + goto error; + } + + ret = _asn1_strict_der_decode(&pkey_asn, + raw_key->data, raw_key->size, + NULL); + if (ret != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(ret); + asn1_delete_structure2(&pkey_asn, ASN1_DELETE_FLAG_ZEROIZE); + goto error; + } + + ret = _gnutls_x509_read_key_int_le(pkey_asn, "", + &pkey->params.params[GOST_K]); + if (ret < 0) { + gnutls_assert(); + asn1_delete_structure2(&pkey_asn, ASN1_DELETE_FLAG_ZEROIZE); + goto error; + } + asn1_delete_structure2(&pkey_asn, ASN1_DELETE_FLAG_ZEROIZE); + } else { + gnutls_assert(); + ret = GNUTLS_E_PARSING_ERROR; + goto error; + } + + pkey->params.params_nr++; + + return 0; + + error: + return ret; + +} + +/* Decodes a GOST privateKey from a PKCS8 structure. + */ +static int +_decode_pkcs8_gost_key(asn1_node pkcs8_asn, gnutls_x509_privkey_t pkey, + gnutls_pk_algorithm_t algo) +{ + int ret; + gnutls_datum_t tmp; + unsigned char oid[3 * MAX_OID_SIZE]; /* GOST parameters can have 3 OIDs at most */ + int len, result; + + gnutls_pk_params_init(&pkey->params); + + len = sizeof(oid); + result = asn1_read_value(pkcs8_asn, "privateKeyAlgorithm.parameters", + oid, &len); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + ret = GNUTLS_E_PARSING_ERROR; + goto error; + } else { + ret = _gnutls_x509_read_gost_params(oid, len, &pkey->params, algo); + if (ret < 0) { + gnutls_assert(); + goto error; + } + } + + /* Will be fixed later by pk_fixup */ + ret = _gnutls_mpi_init(&pkey->params.params[GOST_X]); + if (ret < 0) { + gnutls_assert(); + goto error; + } + pkey->params.params_nr++; + + ret = _gnutls_mpi_init(&pkey->params.params[GOST_Y]); + if (ret < 0) { + gnutls_assert(); + goto error; + } + pkey->params.params_nr++; + + _gnutls_mpi_set_ui(pkey->params.params[GOST_X], 0); + _gnutls_mpi_set_ui(pkey->params.params[GOST_Y], 0); + + ret = _gnutls_x509_read_value(pkcs8_asn, "privateKey", &tmp); + if (ret < 0) { + gnutls_assert(); + goto error; + } + + ret = _privkey_decode_gost_key(&tmp, pkey); + _gnutls_free_key_datum(&tmp); + + if (ret < 0) { + gnutls_assert(); + goto error; + } + + pkey->params.algo = algo; + + return 0; + +error: + gnutls_pk_params_clear(&pkey->params); + gnutls_pk_params_release(&pkey->params); + + return ret; +} + +/* Decodes an DSA privateKey and params from a PKCS8 structure. + */ +static int +_decode_pkcs8_dsa_key(asn1_node pkcs8_asn, gnutls_x509_privkey_t pkey) +{ + int ret; + gnutls_datum_t tmp = {NULL, 0}; + + gnutls_pk_params_init(&pkey->params); + + ret = _gnutls_x509_read_value(pkcs8_asn, "privateKey", &tmp); + if (ret < 0) { + gnutls_assert(); + goto error; + } + + ret = + _gnutls_x509_read_der_int(tmp.data, tmp.size, + &pkey->params.params[4]); + _gnutls_free_key_datum(&tmp); + + if (ret < 0) { + gnutls_assert(); + goto error; + } + + ret = + _gnutls_x509_read_value(pkcs8_asn, + "privateKeyAlgorithm.parameters", + &tmp); + if (ret < 0) { + gnutls_assert(); + goto error; + } + + ret = + _gnutls_x509_read_pubkey_params(GNUTLS_PK_DSA, tmp.data, + tmp.size, &pkey->params); + _gnutls_free_datum(&tmp); + if (ret < 0) { + gnutls_assert(); + goto error; + } + + if (_gnutls_mpi_cmp_ui(pkey->params.params[0], 0) == 0) { + gnutls_assert(); + ret = GNUTLS_E_ILLEGAL_PARAMETER; + goto error; + } + + /* the public key can be generated as g^x mod p */ + ret = _gnutls_mpi_init(&pkey->params.params[3]); + if (ret < 0) { + gnutls_assert(); + goto error; + } + + ret = _gnutls_mpi_powm(pkey->params.params[3], pkey->params.params[2], + pkey->params.params[4], pkey->params.params[0]); + if (ret < 0) { + gnutls_assert(); + goto error; + } + + pkey->params.algo = GNUTLS_PK_DSA; + pkey->params.params_nr = DSA_PRIVATE_PARAMS; + + ret = + _gnutls_asn1_encode_privkey(&pkey->key, + &pkey->params); + if (ret < 0) { + gnutls_assert(); + goto error; + } + + return 0; + + error: + if (pkey->params.params_nr != DSA_PRIVATE_PARAMS) + _gnutls_mpi_release(&pkey->params.params[4]); + return ret; +} + + +static int +decode_private_key_info(const gnutls_datum_t * der, + gnutls_x509_privkey_t pkey) +{ + int result, len; + char oid[MAX_OID_SIZE]; + asn1_node pkcs8_asn = NULL; + gnutls_datum_t sder; + int ret; + + if ((result = + asn1_create_element(_gnutls_get_pkix(), + "PKIX1.pkcs-8-PrivateKeyInfo", + &pkcs8_asn)) != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto error; + } + + result = _asn1_strict_der_decode(&pkcs8_asn, der->data, der->size, NULL); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto error; + } + + /* Check the private key algorithm OID + */ + len = sizeof(oid); + result = + asn1_read_value(pkcs8_asn, "privateKeyAlgorithm.algorithm", + oid, &len); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto error; + } + + pkey->params.algo = gnutls_oid_to_pk(oid); + if (pkey->params.algo == GNUTLS_PK_UNKNOWN) { + gnutls_assert(); + _gnutls_debug_log + ("PKCS #8 private key OID '%s' is unsupported.\n", + oid); + result = GNUTLS_E_UNKNOWN_PK_ALGORITHM; + goto error; + } + + /* Get the DER encoding of the actual private key. + */ + + switch(pkey->params.algo) { + case GNUTLS_PK_RSA: + result = _decode_pkcs8_rsa_key(pkcs8_asn, pkey); + break; + case GNUTLS_PK_RSA_PSS: + result = _decode_pkcs8_rsa_pss_key(pkcs8_asn, pkey); + break; + case GNUTLS_PK_DSA: + result = _decode_pkcs8_dsa_key(pkcs8_asn, pkey); + break; + case GNUTLS_PK_ECDSA: + result = _decode_pkcs8_ecc_key(pkcs8_asn, pkey); + break; + case GNUTLS_PK_EDDSA_ED25519: + case GNUTLS_PK_EDDSA_ED448: + result = _decode_pkcs8_eddsa_key(pkcs8_asn, pkey, oid); + break; + case GNUTLS_PK_ECDH_X25519: + case GNUTLS_PK_ECDH_X448: + result = _decode_pkcs8_modern_ecdh_key(pkcs8_asn, pkey, oid); + break; + case GNUTLS_PK_GOST_01: + case GNUTLS_PK_GOST_12_256: + case GNUTLS_PK_GOST_12_512: + result = _decode_pkcs8_gost_key(pkcs8_asn, + pkey, pkey->params.algo); + break; + default: + result = gnutls_assert_val(GNUTLS_E_UNIMPLEMENTED_FEATURE); + goto error; + } + + if (result < 0) { + gnutls_assert(); + goto error; + } + + /* check for provable parameters attribute */ + ret = _x509_parse_attribute(pkcs8_asn, "attributes", OID_ATTR_PROV_SEED, 0, 1, &sder); + if (ret >= 0) { /* ignore it when not being present */ + ret = _x509_decode_provable_seed(pkey, &sder); + gnutls_free(sder.data); + if (ret < 0) { + gnutls_assert(); + } + } + + result = 0; + +error: + asn1_delete_structure2(&pkcs8_asn, ASN1_DELETE_FLAG_ZEROIZE); + return result; + +} + +/** + * gnutls_x509_privkey_import_pkcs8: + * @key: The data to store the parsed key + * @data: The DER or PEM encoded key. + * @format: One of DER or PEM + * @password: the password to decrypt the key (if it is encrypted). + * @flags: 0 if encrypted or GNUTLS_PKCS_PLAIN if not encrypted. + * + * This function will convert the given DER or PEM encoded PKCS8 2.0 + * encrypted key to the native gnutls_x509_privkey_t format. The + * output will be stored in @key. Both RSA and DSA keys can be + * imported, and flags can only be used to indicate an unencrypted + * key. + * + * The @password can be either ASCII or UTF-8 in the default PBES2 + * encryption schemas, or ASCII for the PKCS12 schemas. + * + * If the Certificate is PEM encoded it should have a header of + * "ENCRYPTED PRIVATE KEY", or "PRIVATE KEY". You only need to + * specify the flags if the key is DER encoded, since in that case + * the encryption status cannot be auto-detected. + * + * If the %GNUTLS_PKCS_PLAIN flag is specified and the supplied data + * are encrypted then %GNUTLS_E_DECRYPTION_FAILED is returned. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int +gnutls_x509_privkey_import_pkcs8(gnutls_x509_privkey_t key, + const gnutls_datum_t * data, + gnutls_x509_crt_fmt_t format, + const char *password, unsigned int flags) +{ + int result = 0, need_free = 0; + gnutls_datum_t _data; + + if (key == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + _data.data = data->data; + _data.size = data->size; + + key->params.algo = GNUTLS_PK_UNKNOWN; + + /* If the Certificate is in PEM format then decode it + */ + if (format == GNUTLS_X509_FMT_PEM) { + /* Try the first header + */ + result = + _gnutls_fbase64_decode(PEM_UNENCRYPTED_PKCS8, + data->data, data->size, &_data); + + if (result < 0) { /* Try the encrypted header + */ + result = + _gnutls_fbase64_decode(PEM_PKCS8, data->data, + data->size, &_data); + + if (result < 0) { + gnutls_assert(); + return result; + } + } else if (flags == 0) + flags |= GNUTLS_PKCS_PLAIN; + + need_free = 1; + } + + if (key->expanded) { + _gnutls_x509_privkey_reinit(key); + } + key->expanded = 1; + + /* Here we don't check for password == NULL to maintain a backwards + * compatibility behavior, with old versions that were encrypting using + * a NULL password. + */ + if (flags & GNUTLS_PKCS_PLAIN) { + result = decode_private_key_info(&_data, key); + if (result < 0) { /* check if it is encrypted */ + if (pkcs8_key_decode(&_data, "", key, 0) == 0) + result = GNUTLS_E_DECRYPTION_FAILED; + } + } else { /* encrypted. */ + result = pkcs8_key_decode(&_data, password, key, 1); + } + + if (result < 0) { + gnutls_assert(); + goto cleanup; + } + + /* This part is necessary to get the public key on certain algorithms. + * In the import above we only get the private key. */ + result = + _gnutls_pk_fixup(key->params.algo, GNUTLS_IMPORT, &key->params); + if (result < 0) { + gnutls_assert(); + goto cleanup; + } + + if (need_free) + _gnutls_free_datum(&_data); + + /* The key has now been decoded. + */ + return 0; + + cleanup: + asn1_delete_structure2(&key->key, ASN1_DELETE_FLAG_ZEROIZE); + key->params.algo = GNUTLS_PK_UNKNOWN; + if (need_free) { + zeroize_temp_key(_data.data, _data.size); + _gnutls_free_datum(&_data); + } + return result; +} + diff --git a/lib/x509/privkey_pkcs8_pbes1.c b/lib/x509/privkey_pkcs8_pbes1.c new file mode 100644 index 0000000..983530e --- /dev/null +++ b/lib/x509/privkey_pkcs8_pbes1.c @@ -0,0 +1,189 @@ +/* + * Copyright (C) 2016 Red Hat + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GnuTLS. + * + * The GnuTLS is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/> + * + */ + +#include "gnutls_int.h" + +#include <datum.h> +#include <global.h> +#include "errors.h" +#include <common.h> +#include <x509.h> +#include <x509_b64.h> +#include "x509_int.h" +#include "pkcs7_int.h" +#include <algorithms.h> +#include <nettle/md5.h> + +/* This file includes support for PKCS#8 PBES1 with DES and MD5. + * We only support decryption for compatibility with other software. + */ + +int _gnutls_read_pbkdf1_params(const uint8_t * data, int data_size, + struct pbkdf2_params *kdf_params, + struct pbe_enc_params *enc_params) +{ + asn1_node pasn = NULL; + int len; + int ret, result; + + memset(kdf_params, 0, sizeof(*kdf_params)); + memset(enc_params, 0, sizeof(*enc_params)); + + if ((result = + asn1_create_element(_gnutls_get_pkix(), + "PKIX1.pkcs-5-PBE-params", + &pasn)) != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + /* Decode the parameters. + */ + result = + _asn1_strict_der_decode(&pasn, data, data_size, NULL); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(result); + goto error; + } + + ret = + _gnutls_x509_read_uint(pasn, "iterationCount", + &kdf_params->iter_count); + if (ret < 0) { + gnutls_assert(); + goto error; + } + + if (kdf_params->iter_count >= MAX_ITER_COUNT || kdf_params->iter_count == 0) { + ret = gnutls_assert_val(GNUTLS_E_ILLEGAL_PARAMETER); + goto error; + } + + len = sizeof(kdf_params->salt); + result = + asn1_read_value(pasn, "salt", + kdf_params->salt, &len); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(result); + goto error; + } + + if (len != 8) { + gnutls_assert(); + ret = GNUTLS_E_ILLEGAL_PARAMETER; + goto error; + } + + enc_params->cipher = GNUTLS_CIPHER_DES_CBC; + + ret = 0; + error: + asn1_delete_structure2(&pasn, ASN1_DELETE_FLAG_ZEROIZE); + return ret; + +} + +static void pbkdf1_md5(const char *password, unsigned password_len, + const uint8_t salt[8], unsigned iter_count, unsigned key_size, uint8_t *key) +{ + struct md5_ctx ctx; + uint8_t tmp[16]; + unsigned i; + + if (key_size > sizeof(tmp)) + abort(); + + for (i=0;i<iter_count;i++) { + md5_init(&ctx); + if (i==0) { + md5_update(&ctx, password_len, (uint8_t*)password); + md5_update(&ctx, 8, salt); + md5_digest(&ctx, 16, tmp); + } else { + md5_update(&ctx, 16, tmp); + md5_digest(&ctx, 16, tmp); + } + } + + memcpy(key, tmp, key_size); + return; +} + +int +_gnutls_decrypt_pbes1_des_md5_data(const char *password, + unsigned password_len, + const struct pbkdf2_params *kdf_params, + const struct pbe_enc_params *enc_params, + const gnutls_datum_t *encrypted_data, + gnutls_datum_t *decrypted_data) +{ + int result; + gnutls_datum_t dkey, d_iv; + gnutls_cipher_hd_t ch; + uint8_t key[16]; + const unsigned block_size = 8; + + if (enc_params->cipher != GNUTLS_CIPHER_DES_CBC) + return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); + + if (encrypted_data->size % block_size != 0) + return gnutls_assert_val(GNUTLS_E_ILLEGAL_PARAMETER); + + /* generate the key + */ + pbkdf1_md5(password, password_len, kdf_params->salt, kdf_params->iter_count, sizeof(key), key); + + dkey.data = key; + dkey.size = 8; + d_iv.data = &key[8]; + d_iv.size = 8; + result = gnutls_cipher_init(&ch, GNUTLS_CIPHER_DES_CBC, &dkey, &d_iv); + if (result < 0) { + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR); + return gnutls_assert_val(result); + } + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_NOT_APPROVED); + + result = gnutls_cipher_decrypt(ch, encrypted_data->data, encrypted_data->size); + if (result < 0) { + gnutls_assert(); + goto error; + } + + if ((int)encrypted_data->size - encrypted_data->data[encrypted_data->size - 1] < 0) { + gnutls_assert(); + result = GNUTLS_E_ILLEGAL_PARAMETER; + goto error; + } + + decrypted_data->data = encrypted_data->data; + decrypted_data->size = encrypted_data->size - encrypted_data->data[encrypted_data->size - 1]; + + result = 0; + error: + gnutls_cipher_deinit(ch); + + return result; +} + diff --git a/lib/x509/prov-seed.c b/lib/x509/prov-seed.c new file mode 100644 index 0000000..d0119b4 --- /dev/null +++ b/lib/x509/prov-seed.c @@ -0,0 +1,141 @@ +/* + * Copyright (C) 2017 Red Hat, Inc. + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GnuTLS. + * + * The GnuTLS is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/> + * + */ + +#include "gnutls_int.h" +#include <datum.h> +#include <global.h> +#include "errors.h" +#include <common.h> +#include <x509.h> +#include <x509_int.h> +#include <mpi.h> +#include "prov-seed.h" + +/* This function encodes a seed value and a hash algorithm OID to the format + * described in RFC8479. The output is the DER encoded form. + */ +int _x509_encode_provable_seed(gnutls_x509_privkey_t pkey, gnutls_datum_t *der) +{ + + asn1_node c2; + int ret, result; + const char *oid; + + oid = gnutls_digest_get_oid(pkey->params.palgo); + if (oid == NULL) + return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + + if ((result = + asn1_create_element(_gnutls_get_gnutls_asn(), + "GNUTLS.ProvableSeed", + &c2)) != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + result = asn1_write_value(c2, "seed", pkey->params.seed, pkey->params.seed_size); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(result); + goto cleanup; + } + + result = asn1_write_value(c2, "algorithm", oid, 1); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(result); + goto cleanup; + } + + ret = _gnutls_x509_der_encode(c2, "", der, 0); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = 0; + + cleanup: + asn1_delete_structure2(&c2, ASN1_DELETE_FLAG_ZEROIZE); + return ret; +} + +/* This function decodes a DER encoded form of seed and a hash algorithm, as in + * RFC8479. + */ +int _x509_decode_provable_seed(gnutls_x509_privkey_t pkey, const gnutls_datum_t *der) +{ + + asn1_node c2; + int ret, result; + char oid[MAX_OID_SIZE]; + int oid_size; + gnutls_datum_t seed = {NULL, 0}; + + if ((result = + asn1_create_element(_gnutls_get_gnutls_asn(), + "GNUTLS.ProvableSeed", + &c2)) != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + result = _asn1_strict_der_decode(&c2, der->data, der->size, NULL); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(result); + goto cleanup; + } + + ret = _gnutls_x509_read_value(c2, "seed", &seed); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + if (seed.size <= sizeof(pkey->params.seed)) { + memcpy(pkey->params.seed, seed.data, seed.size); + pkey->params.seed_size = seed.size; + } else { + ret = 0; /* ignore struct */ + _gnutls_debug_log("%s: ignoring ProvableSeed due to very long params\n", __func__); + goto cleanup; + } + + oid_size = sizeof(oid); + result = asn1_read_value(c2, "algorithm", oid, &oid_size); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(result); + goto cleanup; + } + + pkey->params.palgo = gnutls_oid_to_digest(oid); + pkey->params.pkflags |= GNUTLS_PK_FLAG_PROVABLE; + + ret = 0; + + cleanup: + gnutls_free(seed.data); + asn1_delete_structure2(&c2, ASN1_DELETE_FLAG_ZEROIZE); + return ret; +} diff --git a/lib/x509/prov-seed.h b/lib/x509/prov-seed.h new file mode 100644 index 0000000..a7753cc --- /dev/null +++ b/lib/x509/prov-seed.h @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2017 Red Hat, Inc. + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GnuTLS. + * + * The GnuTLS is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/> + * + */ + +#ifndef GNUTLS_LIB_X509_PROV_SEED_H +#define GNUTLS_LIB_X509_PROV_SEED_H + +int _x509_encode_provable_seed(gnutls_x509_privkey_t pkey, gnutls_datum_t *der); +int _x509_decode_provable_seed(gnutls_x509_privkey_t pkey, const gnutls_datum_t *der); + +#endif /* GNUTLS_LIB_X509_PROV_SEED_H */ diff --git a/lib/x509/sign.c b/lib/x509/sign.c new file mode 100644 index 0000000..303e9f2 --- /dev/null +++ b/lib/x509/sign.c @@ -0,0 +1,218 @@ +/* + * Copyright (C) 2003-2012 Free Software Foundation, Inc. + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GnuTLS. + * + * The GnuTLS is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/> + * + */ + +/* All functions which relate to X.509 certificate signing stuff are + * included here + */ + +#include "gnutls_int.h" + +#include "errors.h" +#include <libtasn1.h> +#include <global.h> +#include <num.h> /* MAX */ +#include <tls-sig.h> +#include <str.h> +#include <datum.h> +#include <x509_int.h> +#include <common.h> +#include <gnutls/abstract.h> +#include <pk.h> + +/* This is the same as the _gnutls_x509_sign, but this one will decode + * the asn1_node given, and sign the DER data. Actually used to get the DER + * of the TBS and sign it on the fly. + */ +int +_gnutls_x509_get_tbs(asn1_node cert, const char *tbs_name, + gnutls_datum_t * tbs) +{ + return _gnutls_x509_der_encode(cert, tbs_name, tbs, 0); +} + +int +_gnutls_x509_crt_get_spki_params(gnutls_x509_crt_t crt, + const gnutls_x509_spki_st *key_params, + gnutls_x509_spki_st *params) +{ + int result; + gnutls_x509_spki_st crt_params; + + result = _gnutls_x509_crt_read_spki_params(crt, &crt_params); + if (result < 0) { + gnutls_assert(); + return result; + } + + if (crt_params.pk == GNUTLS_PK_RSA_PSS) { + if (key_params->pk == GNUTLS_PK_RSA_PSS) { + if (crt_params.rsa_pss_dig != key_params->rsa_pss_dig) { + gnutls_assert(); + return GNUTLS_E_CERTIFICATE_ERROR; + } + + if (crt_params.salt_size < key_params->salt_size) { + gnutls_assert(); + return GNUTLS_E_CERTIFICATE_ERROR; + } + } else if (key_params->pk != GNUTLS_PK_RSA && key_params->pk != GNUTLS_PK_UNKNOWN) { + gnutls_assert(); + return GNUTLS_E_CERTIFICATE_ERROR; + } + memcpy(params, &crt_params, sizeof(gnutls_x509_spki_st)); + } else { + memcpy(params, key_params, sizeof(gnutls_x509_spki_st)); + } + + return 0; +} + +/*- + * _gnutls_x509_pkix_sign - This function will sign a CRL or a certificate with a key + * @src: should contain an asn1_node + * @issuer: is the certificate of the certificate issuer + * @issuer_key: holds the issuer's private key + * + * This function will sign a CRL or a certificate with the issuer's private key, and + * will copy the issuer's information into the CRL or certificate. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + -*/ +int +_gnutls_x509_pkix_sign(asn1_node src, const char *src_name, + gnutls_digest_algorithm_t dig, + unsigned int flags, + gnutls_x509_crt_t issuer, + gnutls_privkey_t issuer_key) +{ + int result; + gnutls_datum_t signature; + gnutls_datum_t tbs; + char name[128]; + gnutls_pk_algorithm_t pk; + gnutls_x509_spki_st key_params, params; + const gnutls_sign_entry_st *se; + + pk = gnutls_x509_crt_get_pk_algorithm(issuer, NULL); + if (pk == GNUTLS_PK_UNKNOWN) + pk = gnutls_privkey_get_pk_algorithm(issuer_key, NULL); + + result = _gnutls_privkey_get_spki_params(issuer_key, &key_params); + if (result < 0) { + gnutls_assert(); + return result; + } + + result = _gnutls_x509_crt_get_spki_params(issuer, &key_params, ¶ms); + if (result < 0) { + gnutls_assert(); + return result; + } + + result = _gnutls_privkey_update_spki_params(issuer_key, pk, dig, flags, + ¶ms); + if (result < 0) { + gnutls_assert(); + return result; + } + + /* Step 1. Copy the issuer's name into the certificate. + */ + _gnutls_str_cpy(name, sizeof(name), src_name); + _gnutls_str_cat(name, sizeof(name), ".issuer"); + + result = + asn1_copy_node(src, name, issuer->cert, + "tbsCertificate.subject"); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + /* Step 1.5. Write the signature stuff in the tbsCertificate. + */ + _gnutls_str_cpy(name, sizeof(name), src_name); + _gnutls_str_cat(name, sizeof(name), ".signature"); + + se = _gnutls_pk_to_sign_entry(params.pk, dig); + if (se == NULL) + return gnutls_assert_val(GNUTLS_E_UNSUPPORTED_SIGNATURE_ALGORITHM); + + _gnutls_debug_log("signing structure using %s\n", se->name); + + result = _gnutls_x509_write_sign_params(src, name, se, ¶ms); + if (result < 0) { + gnutls_assert(); + return result; + } + + /* Step 2. Sign the certificate. + */ + result = _gnutls_x509_get_tbs(src, src_name, &tbs); + + if (result < 0) { + gnutls_assert(); + return result; + } + + FIX_SIGN_PARAMS(params, flags, dig); + + if (_gnutls_pk_is_not_prehashed(params.pk)) { + result = privkey_sign_raw_data(issuer_key, se, &tbs, &signature, ¶ms); + } else { + result = privkey_sign_and_hash_data(issuer_key, se, + &tbs, &signature, ¶ms); + } + gnutls_free(tbs.data); + + if (result < 0) { + gnutls_assert(); + return result; + } + + /* write the signature (bits) + */ + result = + asn1_write_value(src, "signature", signature.data, + signature.size * 8); + + _gnutls_free_datum(&signature); + + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + /* Step 3. Move up and write the AlgorithmIdentifier, which is also + * the same. + */ + + result = _gnutls_x509_write_sign_params(src, "signatureAlgorithm", + se, ¶ms); + if (result < 0) { + gnutls_assert(); + return result; + } + + return 0; +} diff --git a/lib/x509/spki.c b/lib/x509/spki.c new file mode 100644 index 0000000..c87ff1b --- /dev/null +++ b/lib/x509/spki.c @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2017 Red Hat, Inc. + * + * Authors: Daiki Ueno + * + * This file is part of GnuTLS. + * + * The GnuTLS is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/> + * + */ + +#include "gnutls_int.h" +#include "errors.h" +#include <common.h> +#include <x509.h> +#include <x509_int.h> + +/** + * gnutls_x509_spki_init: + * @spki: A pointer to the type to be initialized + * + * This function will initialize a SubjectPublicKeyInfo structure used + * in PKIX. The structure is used to set additional parameters + * in the public key information field of a certificate. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 3.6.0 + * + **/ +int +gnutls_x509_spki_init(gnutls_x509_spki_t *spki) +{ + gnutls_x509_spki_t tmp; + + FAIL_IF_LIB_ERROR; + + tmp = + gnutls_calloc(1, sizeof(gnutls_x509_spki_st)); + + if (!tmp) + return GNUTLS_E_MEMORY_ERROR; + + *spki = tmp; + + return 0; /* success */ +} + +/** + * gnutls_x509_spki_deinit: + * @spki: the SubjectPublicKeyInfo structure + * + * This function will deinitialize a SubjectPublicKeyInfo structure. + * + * Since: 3.6.0 + * + **/ +void +gnutls_x509_spki_deinit(gnutls_x509_spki_t spki) +{ + gnutls_free(spki); +} + +/** + * gnutls_x509_spki_set_rsa_pss_params: + * @spki: the SubjectPublicKeyInfo structure + * @dig: a digest algorithm of type #gnutls_digest_algorithm_t + * @salt_size: the size of salt string + * + * This function will set the public key parameters for + * an RSA-PSS algorithm, in the SubjectPublicKeyInfo structure. + * + * Since: 3.6.0 + * + **/ +void +gnutls_x509_spki_set_rsa_pss_params(gnutls_x509_spki_t spki, + gnutls_digest_algorithm_t dig, + unsigned int salt_size) +{ + spki->pk = GNUTLS_PK_RSA_PSS; + spki->rsa_pss_dig = dig; + spki->salt_size = salt_size; +} + +/** + * gnutls_x509_spki_get_rsa_pss_params: + * @spki: the SubjectPublicKeyInfo structure + * @dig: if non-NULL, it will hold the digest algorithm + * @salt_size: if non-NULL, it will hold the salt size + * + * This function will get the public key algorithm parameters + * of RSA-PSS type. + * + * Returns: zero if the parameters are present or a negative + * value on error. + * + * Since: 3.6.0 + * + **/ +int +gnutls_x509_spki_get_rsa_pss_params(gnutls_x509_spki_t spki, + gnutls_digest_algorithm_t *dig, + unsigned int *salt_size) +{ + if (spki->pk == 0) + return gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE); + + if (spki->pk != GNUTLS_PK_RSA_PSS) + return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + + if (dig) + *dig = spki->rsa_pss_dig; + if (salt_size) + *salt_size = spki->salt_size; + + return 0; +} diff --git a/lib/x509/supported_exts.gperf b/lib/x509/supported_exts.gperf new file mode 100644 index 0000000..aa5a6c1 --- /dev/null +++ b/lib/x509/supported_exts.gperf @@ -0,0 +1,40 @@ +%{ +%} +%language=ANSI-C +%readonly-tables +%define lookup-function-name is_ext_oid_supported +%define hash-function-name x509_ext_hash + +struct supported_exts_st { const char *name; }; +%% +#GNUTLS_X509EXT_OID_SUBJECT_KEY_ID +2.5.29.14 +#GNUTLS_X509EXT_OID_KEY_USAGE +2.5.29.15 +#GNUTLS_X509EXT_OID_PRIVATE_KEY_USAGE_PERIOD - not supported +#GNUTLS_X509EXT_OID_SAN +2.5.29.17 +#GNUTLS_X509EXT_OID_IAN +2.5.29.18 +#GNUTLS_X509EXT_OID_BASIC_CONSTRAINTS +2.5.29.19 +#GNUTLS_X509EXT_OID_NAME_CONSTRAINTS +2.5.29.30 +#GNUTLS_X509EXT_OID_CRL_DIST_POINTS +2.5.29.31 +#GNUTLS_X509EXT_OID_AUTHORITY_KEY_ID +2.5.29.35 +#GNUTLS_X509EXT_OID_EXTENDED_KEY_USAGE +2.5.29.37 +#GNUTLS_X509EXT_OID_AUTHORITY_INFO_ACCESS +1.3.6.1.5.5.7.1.1 +#GNUTLS_X509EXT_OID_PROXY_CRT_INFO +1.3.6.1.5.5.7.1.14 +#GNUTLS_X509EXT_OID_TLSFEATURES +1.3.6.1.5.5.7.1.24 +# We do not support verification with specific policies, +# as such all the policies and restrictions are acceptable. +#GNUTLS_X509EXT_OID_CRT_POLICY +2.5.29.32 +#GNUTLS_X509EXT_OID_INHIBIT_ANYPOLICY +2.5.29.54 diff --git a/lib/x509/supported_exts.h b/lib/x509/supported_exts.h new file mode 100644 index 0000000..c7bb97b --- /dev/null +++ b/lib/x509/supported_exts.h @@ -0,0 +1,161 @@ +/* ANSI-C code produced by gperf version 3.1 */ +/* Command-line: gperf --global-table -t supported_exts.gperf */ +/* Computed positions: -k'8-9,17' */ + +#if !((' ' == 32) && ('!' == 33) && ('"' == 34) && ('#' == 35) \ + && ('%' == 37) && ('&' == 38) && ('\'' == 39) && ('(' == 40) \ + && (')' == 41) && ('*' == 42) && ('+' == 43) && (',' == 44) \ + && ('-' == 45) && ('.' == 46) && ('/' == 47) && ('0' == 48) \ + && ('1' == 49) && ('2' == 50) && ('3' == 51) && ('4' == 52) \ + && ('5' == 53) && ('6' == 54) && ('7' == 55) && ('8' == 56) \ + && ('9' == 57) && (':' == 58) && (';' == 59) && ('<' == 60) \ + && ('=' == 61) && ('>' == 62) && ('?' == 63) && ('A' == 65) \ + && ('B' == 66) && ('C' == 67) && ('D' == 68) && ('E' == 69) \ + && ('F' == 70) && ('G' == 71) && ('H' == 72) && ('I' == 73) \ + && ('J' == 74) && ('K' == 75) && ('L' == 76) && ('M' == 77) \ + && ('N' == 78) && ('O' == 79) && ('P' == 80) && ('Q' == 81) \ + && ('R' == 82) && ('S' == 83) && ('T' == 84) && ('U' == 85) \ + && ('V' == 86) && ('W' == 87) && ('X' == 88) && ('Y' == 89) \ + && ('Z' == 90) && ('[' == 91) && ('\\' == 92) && (']' == 93) \ + && ('^' == 94) && ('_' == 95) && ('a' == 97) && ('b' == 98) \ + && ('c' == 99) && ('d' == 100) && ('e' == 101) && ('f' == 102) \ + && ('g' == 103) && ('h' == 104) && ('i' == 105) && ('j' == 106) \ + && ('k' == 107) && ('l' == 108) && ('m' == 109) && ('n' == 110) \ + && ('o' == 111) && ('p' == 112) && ('q' == 113) && ('r' == 114) \ + && ('s' == 115) && ('t' == 116) && ('u' == 117) && ('v' == 118) \ + && ('w' == 119) && ('x' == 120) && ('y' == 121) && ('z' == 122) \ + && ('{' == 123) && ('|' == 124) && ('}' == 125) && ('~' == 126)) +/* The character set is not based on ISO-646. */ +#error "gperf generated tables don't work with this execution character set. Please report a bug to <bug-gperf@gnu.org>." +#endif + +#line 1 "supported_exts.gperf" + +#line 8 "supported_exts.gperf" +struct supported_exts_st { const char *name; }; + +#define TOTAL_KEYWORDS 14 +#define MIN_WORD_LENGTH 9 +#define MAX_WORD_LENGTH 18 +#define MIN_HASH_VALUE 13 +#define MAX_HASH_VALUE 34 +/* maximum key range = 22, duplicates = 0 */ + +#ifdef __GNUC__ +__inline +#else +#ifdef __cplusplus +inline +#endif +#endif +static unsigned int +x509_ext_hash (register const char *str, register size_t len) +{ + static const unsigned char asso_values[] = + { + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 0, 35, 3, 10, + 4, 5, 4, 0, 35, 15, 13, 14, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35 + }; + register unsigned int hval = len; + + switch (hval) + { + default: + hval += asso_values[(unsigned char)str[16]]; + /*FALLTHROUGH*/ + case 16: + case 15: + case 14: + case 13: + case 12: + case 11: + case 10: + case 9: + hval += asso_values[(unsigned char)str[8]]; + /*FALLTHROUGH*/ + case 8: + hval += asso_values[(unsigned char)str[7]]; + break; + } + return hval; +} + +static const struct supported_exts_st wordlist[] = + { + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, +#line 40 "supported_exts.gperf" + {"2.5.29.54"}, +#line 26 "supported_exts.gperf" + {"2.5.29.35"}, + {""}, {""}, +#line 22 "supported_exts.gperf" + {"2.5.29.30"}, +#line 38 "supported_exts.gperf" + {"2.5.29.32"}, +#line 13 "supported_exts.gperf" + {"2.5.29.15"}, + {""}, {""}, +#line 34 "supported_exts.gperf" + {"1.3.6.1.5.5.7.1.24"}, +#line 11 "supported_exts.gperf" + {"2.5.29.14"}, +#line 24 "supported_exts.gperf" + {"2.5.29.31"}, + {""}, {""}, +#line 30 "supported_exts.gperf" + {"1.3.6.1.5.5.7.1.1"}, +#line 32 "supported_exts.gperf" + {"1.3.6.1.5.5.7.1.14"}, +#line 28 "supported_exts.gperf" + {"2.5.29.37"}, + {""}, {""}, +#line 18 "supported_exts.gperf" + {"2.5.29.18"}, +#line 20 "supported_exts.gperf" + {"2.5.29.19"}, +#line 16 "supported_exts.gperf" + {"2.5.29.17"} + }; + +static const struct supported_exts_st * +is_ext_oid_supported (register const char *str, register size_t len) +{ + if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH) + { + register unsigned int key = x509_ext_hash (str, len); + + if (key <= MAX_HASH_VALUE) + { + register const char *s = wordlist[key].name; + + if (*str == *s && !strcmp (str + 1, s + 1)) + return &wordlist[key]; + } + } + return 0; +} diff --git a/lib/x509/time.c b/lib/x509/time.c new file mode 100644 index 0000000..2726600 --- /dev/null +++ b/lib/x509/time.c @@ -0,0 +1,468 @@ +/* + * Copyright (C) 2003-2016 Free Software Foundation, Inc. + * Copyright (C) 2016 Red Hat, Inc. + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GnuTLS. + * + * The GnuTLS is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/> + * + */ + +#include "gnutls_int.h" +#include <libtasn1.h> +#include <datum.h> +#include <global.h> +#include "errors.h" +#include <str.h> +#include <x509.h> +#include <num.h> +#include <x509_b64.h> +#include "x509_int.h" +#include "extras/hex.h" +#include <common.h> +#include <c-ctype.h> + +time_t _gnutls_utcTime2gtime(const char *ttime); + +/* TIME functions + * Conversions between generalized or UTC time to time_t + * + */ + +/* This is an emulation of the struct tm. + * Since we do not use libc's functions, we don't need to + * depend on the libc structure. + */ +typedef struct fake_tm { + int tm_mon; + int tm_year; /* FULL year - ie 1971 */ + int tm_mday; + int tm_hour; + int tm_min; + int tm_sec; +} fake_tm; + +/* The mktime_utc function is due to Russ Allbery (rra@stanford.edu), + * who placed it under public domain: + */ + +/* The number of days in each month. + */ +static const int MONTHDAYS[] = { + 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 +}; + + /* Whether a given year is a leap year. */ +#define ISLEAP(year) \ + (((year) % 4) == 0 && (((year) % 100) != 0 || ((year) % 400) == 0)) + +/* + ** Given a struct tm representing a calendar time in UTC, convert it to + ** seconds since epoch. Returns (time_t) -1 if the time is not + ** convertible. Note that this function does not canonicalize the provided + ** struct tm, nor does it allow out of range values or years before 1970. + */ +static time_t mktime_utc(const struct fake_tm *tm) +{ + time_t result = 0; + int i; + +/* We do allow some ill-formed dates, but we don't do anything special + * with them and our callers really shouldn't pass them to us. Do + * explicitly disallow the ones that would cause invalid array accesses + * or other algorithm problems. + */ + if (tm->tm_mon < 0 || tm->tm_mon > 11 || tm->tm_year < 1970) + return (time_t) - 1; + + /* Check for "obvious" mistakes in dates */ + if (tm->tm_sec > 60 || tm->tm_min > 59 || tm->tm_mday > 31 || tm->tm_mday < 1 || tm->tm_hour > 23) + return (time_t) - 1; + +/* Convert to a time_t. + */ + for (i = 1970; i < tm->tm_year; i++) + result += 365 + ISLEAP(i); + for (i = 0; i < tm->tm_mon; i++) + result += MONTHDAYS[i]; + if (tm->tm_mon > 1 && ISLEAP(tm->tm_year)) + result++; + result = 24 * (result + tm->tm_mday - 1) + tm->tm_hour; + result = 60 * result + tm->tm_min; + result = 60 * result + tm->tm_sec; + return result; +} + + +/* this one will parse dates of the form: + * month|day|hour|minute|sec* (2 chars each) + * and year is given. Returns a time_t date. + */ +static time_t time2gtime(const char *ttime, int year) +{ + char xx[4]; + struct fake_tm etime; + + if (strlen(ttime) < 8) { + gnutls_assert(); + return (time_t) - 1; + } + + etime.tm_year = year; + + /* In order to work with 32 bit + * time_t. + */ + if (sizeof(time_t) <= 4 && etime.tm_year >= 2038) + return (time_t) 2145914603; /* 2037-12-31 23:23:23 */ + + if (etime.tm_year < 1970) + return (time_t) 0; + + xx[2] = 0; + +/* get the month + */ + memcpy(xx, ttime, 2); /* month */ + etime.tm_mon = atoi(xx) - 1; + ttime += 2; + +/* get the day + */ + memcpy(xx, ttime, 2); /* day */ + etime.tm_mday = atoi(xx); + ttime += 2; + +/* get the hour + */ + memcpy(xx, ttime, 2); /* hour */ + etime.tm_hour = atoi(xx); + ttime += 2; + +/* get the minutes + */ + memcpy(xx, ttime, 2); /* minutes */ + etime.tm_min = atoi(xx); + ttime += 2; + + if (strlen(ttime) >= 2) { + memcpy(xx, ttime, 2); + etime.tm_sec = atoi(xx); + } else + etime.tm_sec = 0; + + return mktime_utc(&etime); +} + + +/* returns a time_t value that contains the given time. + * The given time is expressed as: + * YEAR(2)|MONTH(2)|DAY(2)|HOUR(2)|MIN(2)|SEC(2)* + * + * (seconds are optional) + */ +time_t _gnutls_utcTime2gtime(const char *ttime) +{ + char xx[3]; + int year, i; + int len = strlen(ttime); + + if (len < 10) { + gnutls_assert(); + return (time_t) - 1; + } + +#ifdef STRICT_DER_TIME + /* Make sure everything else is digits. */ + for (i = 0; i < len - 1; i++) { + if (c_isdigit(ttime[i])) + continue; + return gnutls_assert_val((time_t)-1); + } +#endif + xx[2] = 0; + +/* get the year + */ + memcpy(xx, ttime, 2); /* year */ + year = atoi(xx); + ttime += 2; + + if (year > 49) + year += 1900; + else + year += 2000; + + return time2gtime(ttime, year); +} + +/* returns a time_t value that contains the given time. + * The given time is expressed as: + * YEAR(4)|MONTH(2)|DAY(2)|HOUR(2)|MIN(2)|SEC(2)* + */ +time_t _gnutls_x509_generalTime2gtime(const char *ttime) +{ + char xx[5]; + int year; + + if (strlen(ttime) < 12) { + gnutls_assert(); + return (time_t) - 1; + } + + if (strchr(ttime, 'Z') == 0) { + gnutls_assert(); + /* required to be in GMT */ + return (time_t) - 1; + } + + if (strchr(ttime, '.') != 0) { + gnutls_assert(); + /* no fractional seconds allowed */ + return (time_t) - 1; + } + xx[4] = 0; + +/* get the year + */ + memcpy(xx, ttime, 4); /* year */ + year = atoi(xx); + ttime += 4; + + return time2gtime(ttime, year); +} + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wformat-y2k" +/* tag will contain ASN1_TAG_UTCTime or ASN1_TAG_GENERALIZEDTime */ +static int +gtime_to_suitable_time(time_t gtime, char *str_time, size_t str_time_size, unsigned *tag) +{ + size_t ret; + struct tm _tm; + + if (gtime == (time_t)-1 +#if SIZEOF_LONG == 8 + || gtime >= 253402210800 +#endif + ) { + if (tag) + *tag = ASN1_TAG_GENERALIZEDTime; + snprintf(str_time, str_time_size, "99991231235959Z"); + return 0; + } + + if (!gmtime_r(>ime, &_tm)) { + gnutls_assert(); + return GNUTLS_E_INTERNAL_ERROR; + } + + if (_tm.tm_year >= 150) { + if (tag) + *tag = ASN1_TAG_GENERALIZEDTime; + ret = strftime(str_time, str_time_size, "%Y%m%d%H%M%SZ", &_tm); + } else { + if (tag) + *tag = ASN1_TAG_UTCTime; + ret = strftime(str_time, str_time_size, "%y%m%d%H%M%SZ", &_tm); + } + + if (!ret) { + gnutls_assert(); + return GNUTLS_E_SHORT_MEMORY_BUFFER; + } + + return 0; +} +#pragma GCC diagnostic pop + +static int +gtime_to_generalTime(time_t gtime, char *str_time, size_t str_time_size) +{ + size_t ret; + struct tm _tm; + + if (gtime == (time_t)-1 +#if SIZEOF_LONG == 8 + || gtime >= 253402210800 +#endif + ) { + snprintf(str_time, str_time_size, "99991231235959Z"); + return 0; + } + + if (!gmtime_r(>ime, &_tm)) { + gnutls_assert(); + return GNUTLS_E_INTERNAL_ERROR; + } + + ret = strftime(str_time, str_time_size, "%Y%m%d%H%M%SZ", &_tm); + if (!ret) { + gnutls_assert(); + return GNUTLS_E_SHORT_MEMORY_BUFFER; + } + + return 0; +} + + +/* Extracts the time in time_t from the asn1_node given. When should + * be something like "tbsCertList.thisUpdate". + */ +#define MAX_TIME 64 +time_t _gnutls_x509_get_time(asn1_node c2, const char *where, int force_general) +{ + char ttime[MAX_TIME]; + char name[128]; + time_t c_time = (time_t) - 1; + int len, result; + + len = sizeof(ttime) - 1; + result = asn1_read_value(c2, where, ttime, &len); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return (time_t) (-1); + } + + if (force_general != 0) { + c_time = _gnutls_x509_generalTime2gtime(ttime); + } else { + _gnutls_str_cpy(name, sizeof(name), where); + + /* choice */ + if (strcmp(ttime, "generalTime") == 0) { + if (name[0] == 0) + _gnutls_str_cpy(name, sizeof(name), + "generalTime"); + else + _gnutls_str_cat(name, sizeof(name), + ".generalTime"); + len = sizeof(ttime) - 1; + result = asn1_read_value(c2, name, ttime, &len); + if (result == ASN1_SUCCESS) + c_time = + _gnutls_x509_generalTime2gtime(ttime); + } else { /* UTCTIME */ + if (name[0] == 0) + _gnutls_str_cpy(name, sizeof(name), "utcTime"); + else + _gnutls_str_cat(name, sizeof(name), ".utcTime"); + len = sizeof(ttime) - 1; + result = asn1_read_value(c2, name, ttime, &len); + if (result == ASN1_SUCCESS) + c_time = _gnutls_utcTime2gtime(ttime); + } + + /* We cannot handle dates after 2031 in 32 bit machines. + * a time_t of 64bits has to be used. + */ + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return (time_t) (-1); + } + } + + return c_time; +} + +/* Sets the time in time_t in the asn1_node given. Where should + * be something like "tbsCertList.thisUpdate". + */ +int +_gnutls_x509_set_time(asn1_node c2, const char *where, time_t tim, + int force_general) +{ + char str_time[MAX_TIME]; + char name[128]; + int result, len; + unsigned tag; + + if (force_general != 0) { + result = + gtime_to_generalTime(tim, str_time, sizeof(str_time)); + if (result < 0) + return gnutls_assert_val(result); + len = strlen(str_time); + result = asn1_write_value(c2, where, str_time, len); + if (result != ASN1_SUCCESS) + return gnutls_assert_val(_gnutls_asn2err(result)); + + return 0; + } + + result = gtime_to_suitable_time(tim, str_time, sizeof(str_time), &tag); + if (result < 0) { + gnutls_assert(); + return result; + } + + _gnutls_str_cpy(name, sizeof(name), where); + if (tag == ASN1_TAG_UTCTime) { + if ((result = asn1_write_value(c2, where, "utcTime", 1)) < 0) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + _gnutls_str_cat(name, sizeof(name), ".utcTime"); + } else { + if ((result = asn1_write_value(c2, where, "generalTime", 1)) < 0) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + _gnutls_str_cat(name, sizeof(name), ".generalTime"); + } + + len = strlen(str_time); + result = asn1_write_value(c2, name, str_time, len); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + return 0; +} + +/* This will set a DER encoded Time element. To be used in fields + * which are of the ANY. + */ +int +_gnutls_x509_set_raw_time(asn1_node c2, const char *where, time_t tim) +{ + char str_time[MAX_TIME]; + uint8_t buf[128]; + int result, len, der_len; + unsigned tag; + + result = + gtime_to_suitable_time(tim, str_time, sizeof(str_time), &tag); + if (result < 0) + return gnutls_assert_val(result); + len = strlen(str_time); + + buf[0] = tag; + asn1_length_der(len, buf+1, &der_len); + + if ((unsigned)len > sizeof(buf)-der_len-1) { + return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); + } + + memcpy(buf+1+der_len, str_time, len); + + result = asn1_write_value(c2, where, buf, len+1+der_len); + if (result != ASN1_SUCCESS) + return gnutls_assert_val(_gnutls_asn2err(result)); + return 0; +} + diff --git a/lib/x509/tls_features.c b/lib/x509/tls_features.c new file mode 100644 index 0000000..bbb9896 --- /dev/null +++ b/lib/x509/tls_features.c @@ -0,0 +1,265 @@ +/* + * Copyright (C) 2003-2016 Free Software Foundation, Inc. + * + * Authors: Nikos Mavrogiannopoulos, Simon Josefsson, Howard Chu + * + * This file is part of GnuTLS. + * + * The GnuTLS is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/> + * + */ + +#include "gnutls_int.h" +#include <datum.h> +#include <global.h> +#include "errors.h" +#include <common.h> +#include <gnutls/x509-ext.h> +#include <x509.h> +#include <x509_b64.h> +#include <x509_int.h> +#include <libtasn1.h> +#include <pk.h> +#include <pkcs11_int.h> +#include "urls.h" + +/** + * gnutls_x509_tlsfeatures_init: + * @f: The TLS features + * + * This function will initialize a X.509 TLS features extension structure + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, + * otherwise a negative error value. + * + * Since: 3.5.1 + **/ +int gnutls_x509_tlsfeatures_init(gnutls_x509_tlsfeatures_t *f) +{ + *f = gnutls_calloc(1, sizeof(struct gnutls_x509_tlsfeatures_st)); + if (*f == NULL) + return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + + return 0; +} + +/** + * gnutls_x509_tlsfeatures_deinit: + * @f: The TLS features + * + * This function will deinitialize a X.509 TLS features extension structure + * + * Since: 3.5.1 + **/ +void gnutls_x509_tlsfeatures_deinit(gnutls_x509_tlsfeatures_t f) +{ + gnutls_free(f); +} + +/** + * gnutls_x509_tlsfeatures_get: + * @f: The TLS features + * @idx: The index of the feature to get + * @feature: If the function succeeds, the feature will be stored in this variable + * + * This function will get a feature from the X.509 TLS features + * extension structure. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, + * otherwise a negative error value. + * + * Since: 3.5.1 + **/ +int gnutls_x509_tlsfeatures_get(gnutls_x509_tlsfeatures_t f, unsigned idx, unsigned int *feature) +{ + if (f == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + if (idx >= f->size) { + return gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE); + } + + *feature = f->feature[idx]; + return 0; +} + +/** + * gnutls_x509_crt_get_tlsfeatures: + * @crt: A X.509 certificate + * @features: If the function succeeds, the + * features will be stored in this variable. + * @flags: zero or %GNUTLS_EXT_FLAG_APPEND + * @critical: the extension status + * + * This function will get the X.509 TLS features + * extension structure from the certificate. The + * returned structure needs to be freed using + * gnutls_x509_tlsfeatures_deinit(). + * + * When the @flags is set to %GNUTLS_EXT_FLAG_APPEND, + * then if the @features structure is empty this function will behave + * identically as if the flag was not set. Otherwise if there are elements + * in the @features structure then they will be merged with. + * + * Note that @features must be initialized prior to calling this function. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, + * otherwise a negative error value. + * + * Since: 3.5.1 + **/ +int gnutls_x509_crt_get_tlsfeatures(gnutls_x509_crt_t crt, + gnutls_x509_tlsfeatures_t features, + unsigned int flags, + unsigned int *critical) +{ + int ret; + gnutls_datum_t der; + + if (crt == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + if ((ret = + _gnutls_x509_crt_get_extension(crt, GNUTLS_X509EXT_OID_TLSFEATURES, 0, + &der, critical)) < 0) + { + return ret; + } + + if (der.size == 0 || der.data == NULL) { + gnutls_assert(); + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + } + + ret = gnutls_x509_ext_import_tlsfeatures(&der, features, flags); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = 0; + cleanup: + gnutls_free(der.data); + return ret; +} + +/** + * gnutls_x509_crt_set_tlsfeatures: + * @crt: A X.509 certificate + * @features: If the function succeeds, the + * features will be added to the certificate. + * + * This function will set the certificates + * X.509 TLS extension from the given structure. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, + * otherwise a negative error value. + * + * Since: 3.5.1 + **/ +int gnutls_x509_crt_set_tlsfeatures(gnutls_x509_crt_t crt, + gnutls_x509_tlsfeatures_t features) +{ + int ret; + gnutls_datum_t der; + + if (crt == NULL || features == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + ret = gnutls_x509_ext_export_tlsfeatures(features, &der); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + ret = _gnutls_x509_crt_set_extension(crt, GNUTLS_X509EXT_OID_TLSFEATURES, &der, 0); + + _gnutls_free_datum(&der); + + if (ret < 0) { + gnutls_assert(); + } + + return ret; +} + +/** + * gnutls_x509_tls_features_check_crt: + * @feat: a set of TLSFeatures + * @cert: the certificate to be checked + * + * This function will check the provided certificate against the TLSFeatures + * set in @feat using the RFC7633 p.4.2.2 rules. It will check whether the certificate + * contains the features in @feat or a superset. + * + * Returns: non-zero if the provided certificate complies, and zero otherwise. + * + * Since: 3.5.1 + **/ +unsigned gnutls_x509_tlsfeatures_check_crt(gnutls_x509_tlsfeatures_t feat, + gnutls_x509_crt_t cert) +{ + int ret; + gnutls_x509_tlsfeatures_t cfeat; + unsigned i, j, uret, found; + + if (feat->size == 0) + return 1; /* shortcut; no constraints to check */ + + ret = gnutls_x509_tlsfeatures_init(&cfeat); + if (ret < 0) + return gnutls_assert_val(0); + + ret = gnutls_x509_crt_get_tlsfeatures(cert, cfeat, 0, NULL); + if (ret < 0) { + gnutls_assert(); + uret = 0; + goto cleanup; + } + + /* if cert's features cannot be a superset */ + if (feat->size > cfeat->size) { + _gnutls_debug_log("certificate has %u, while issuer has %u tlsfeatures\n", cfeat->size, feat->size); + gnutls_assert(); + uret = 0; + goto cleanup; + } + + for (i=0;i<feat->size;i++) { + found = 0; + for (j=0;j<cfeat->size;j++) { + if (feat->feature[i] == cfeat->feature[j]) { + found = 1; + break; + } + } + + if (found == 0) { + _gnutls_debug_log("feature %d was not found in cert\n", (int)feat->feature[i]); + uret = 0; + goto cleanup; + } + } + + uret = 1; + cleanup: + gnutls_x509_tlsfeatures_deinit(cfeat); + return uret; +} diff --git a/lib/x509/verify-high.c b/lib/x509/verify-high.c new file mode 100644 index 0000000..5698d4f --- /dev/null +++ b/lib/x509/verify-high.c @@ -0,0 +1,1834 @@ +/* + * Copyright (C) 2011-2016 Free Software Foundation, Inc. + * Copyright (C) 2015-2016 Red Hat, Inc. + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GnuTLS. + * + * The GnuTLS is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/> + * + */ + +#include "gnutls_int.h" +#include "errors.h" +#include <libtasn1.h> +#include <global.h> +#include <num.h> /* MAX */ +#include <tls-sig.h> +#include <str.h> +#include <datum.h> +#include <hash-pjw-bare.h> +#include "x509_int.h" +#include <common.h> +#include <gnutls/x509-ext.h> +#include "verify-high.h" +#include "intprops.h" + +struct named_cert_st { + gnutls_x509_crt_t cert; + uint8_t name[MAX_SERVER_NAME_SIZE]; + unsigned int name_size; +}; + +struct node_st { + /* The trusted certificates */ + gnutls_x509_crt_t *trusted_cas; + unsigned int trusted_ca_size; + + struct named_cert_st *named_certs; + unsigned int named_cert_size; + + /* The trusted CRLs */ + gnutls_x509_crl_t *crls; + unsigned int crl_size; +}; + +struct gnutls_x509_trust_list_iter { + unsigned int node_index; + unsigned int ca_index; + +#ifdef ENABLE_PKCS11 + gnutls_pkcs11_obj_t* pkcs11_list; + unsigned int pkcs11_index; + unsigned int pkcs11_size; +#endif +}; + +#define DEFAULT_SIZE 127 + +struct cert_set_node_st { + gnutls_x509_crt_t *certs; + unsigned int size; +}; + +struct cert_set_st { + struct cert_set_node_st *node; + unsigned int size; +}; + +static int +cert_set_init(struct cert_set_st *set, unsigned int size) +{ + memset(set, 0, sizeof(*set)); + + set->size = size; + set->node = gnutls_calloc(size, sizeof(*set->node)); + if (!set->node) { + return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + } + + return 0; +} + +static void +cert_set_deinit(struct cert_set_st *set) +{ + size_t i; + + for (i = 0; i < set->size; i++) { + gnutls_free(set->node[i].certs); + } + + gnutls_free(set->node); +} + +static bool +cert_set_contains(struct cert_set_st *set, const gnutls_x509_crt_t cert) +{ + size_t hash, i; + + hash = hash_pjw_bare(cert->raw_dn.data, cert->raw_dn.size); + hash %= set->size; + + for (i = 0; i < set->node[hash].size; i++) { + if (unlikely(gnutls_x509_crt_equals(set->node[hash].certs[i], cert))) { + return true; + } + } + + return false; +} + +static int +cert_set_add(struct cert_set_st *set, const gnutls_x509_crt_t cert) +{ + size_t hash; + + hash = hash_pjw_bare(cert->raw_dn.data, cert->raw_dn.size); + hash %= set->size; + + if (unlikely(INT_ADD_OVERFLOW(set->node[hash].size, 1))) { + return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + } + + set->node[hash].certs = + _gnutls_reallocarray_fast(set->node[hash].certs, + set->node[hash].size + 1, + sizeof(*set->node[hash].certs)); + if (!set->node[hash].certs) { + return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + } + set->node[hash].certs[set->node[hash].size] = cert; + set->node[hash].size++; + + return 0; +} + +/** + * gnutls_x509_trust_list_init: + * @list: A pointer to the type to be initialized + * @size: The size of the internal hash table. Use (0) for default size. + * + * This function will initialize an X.509 trust list structure. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 3.0.0 + **/ +int +gnutls_x509_trust_list_init(gnutls_x509_trust_list_t * list, + unsigned int size) +{ + gnutls_x509_trust_list_t tmp; + + FAIL_IF_LIB_ERROR; + + tmp = + gnutls_calloc(1, sizeof(struct gnutls_x509_trust_list_st)); + + if (!tmp) + return GNUTLS_E_MEMORY_ERROR; + + if (size == 0) + size = DEFAULT_SIZE; + tmp->size = size; + + tmp->node = gnutls_calloc(1, tmp->size * sizeof(tmp->node[0])); + if (tmp->node == NULL) { + gnutls_assert(); + gnutls_free(tmp); + return GNUTLS_E_MEMORY_ERROR; + } + + *list = tmp; + + return 0; /* success */ +} + +/** + * gnutls_x509_trust_list_deinit: + * @list: The list to be deinitialized + * @all: if non-zero it will deinitialize all the certificates and CRLs contained in the structure. + * + * This function will deinitialize a trust list. Note that the + * @all flag should be typically non-zero unless you have specified + * your certificates using gnutls_x509_trust_list_add_cas() and you + * want to prevent them from being deinitialized by this function. + * + * Since: 3.0.0 + **/ +void +gnutls_x509_trust_list_deinit(gnutls_x509_trust_list_t list, + unsigned int all) +{ + unsigned int i, j; + + if (!list) + return; + + for (j = 0; j < list->blacklisted_size; j++) { + gnutls_x509_crt_deinit(list->blacklisted[j]); + } + gnutls_free(list->blacklisted); + + for (j = 0; j < list->keep_certs_size; j++) { + gnutls_x509_crt_deinit(list->keep_certs[j]); + } + gnutls_free(list->keep_certs); + + for (i = 0; i < list->size; i++) { + if (all) { + for (j = 0; j < list->node[i].trusted_ca_size; j++) { + gnutls_x509_crt_deinit(list->node[i]. + trusted_cas[j]); + } + } + gnutls_free(list->node[i].trusted_cas); + + + if (all) { + for (j = 0; j < list->node[i].crl_size; j++) { + gnutls_x509_crl_deinit(list->node[i]. + crls[j]); + } + } + gnutls_free(list->node[i].crls); + + if (all) { + for (j = 0; j < list->node[i].named_cert_size; j++) { + gnutls_x509_crt_deinit(list->node[i]. + named_certs[j]. + cert); + } + } + gnutls_free(list->node[i].named_certs); + } + + gnutls_free(list->x509_rdn_sequence.data); + gnutls_free(list->node); + gnutls_free(list->pkcs11_token); + gnutls_free(list); +} + +static int +add_new_ca_to_rdn_seq(gnutls_x509_trust_list_t list, + gnutls_x509_crt_t ca) +{ + gnutls_datum_t tmp; + size_t newsize; + unsigned char *newdata, *p; + + /* Add DN of the last added CAs to the RDN sequence + * This will be sent to clients when a certificate + * request message is sent. + */ + tmp.data = ca->raw_dn.data; + tmp.size = ca->raw_dn.size; + + newsize = list->x509_rdn_sequence.size + 2 + tmp.size; + if (newsize < list->x509_rdn_sequence.size) { + gnutls_assert(); + return GNUTLS_E_SHORT_MEMORY_BUFFER; + } + + newdata = + gnutls_realloc_fast(list->x509_rdn_sequence.data, + newsize); + if (newdata == NULL) { + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; + } + + p = newdata + list->x509_rdn_sequence.size; + _gnutls_write_uint16(tmp.size, p); + if (tmp.data != NULL) + memcpy(p + 2, tmp.data, tmp.size); + + list->x509_rdn_sequence.size = newsize; + list->x509_rdn_sequence.data = newdata; + + return 0; +} + +#ifdef ENABLE_PKCS11 +/* Keeps the provided certificate in a structure that will be + * deallocated on deinit. This is to handle get_issuer() with + * pkcs11 trust modules when the GNUTLS_TL_GET_COPY flag isn't + * given. It is not thread safe. */ +static int +trust_list_add_compat(gnutls_x509_trust_list_t list, + gnutls_x509_crt_t cert) +{ + if (unlikely(INT_ADD_OVERFLOW(list->keep_certs_size, 1))) { + return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + } + + list->keep_certs = + _gnutls_reallocarray_fast(list->keep_certs, + list->keep_certs_size + 1, + sizeof(list->keep_certs[0])); + if (list->keep_certs == NULL) { + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; + } + + list->keep_certs[list->keep_certs_size] = cert; + list->keep_certs_size++; + + return 0; +} +#endif + +/** + * gnutls_x509_trust_list_add_cas: + * @list: The list + * @clist: A list of CAs + * @clist_size: The length of the CA list + * @flags: flags from %gnutls_trust_list_flags_t + * + * This function will add the given certificate authorities + * to the trusted list. The CAs in @clist must not be deinitialized + * during the lifetime of @list. + * + * If the flag %GNUTLS_TL_NO_DUPLICATES is specified, then + * this function will ensure that no duplicates will be + * present in the final trust list. + * + * If the flag %GNUTLS_TL_NO_DUPLICATE_KEY is specified, then + * this function will ensure that no certificates with the + * same key are present in the final trust list. + * + * If either %GNUTLS_TL_NO_DUPLICATE_KEY or %GNUTLS_TL_NO_DUPLICATES + * are given, gnutls_x509_trust_list_deinit() must be called with parameter + * @all being 1. + * + * Returns: The number of added elements is returned; that includes + * duplicate entries. + * + * Since: 3.0.0 + **/ +int +gnutls_x509_trust_list_add_cas(gnutls_x509_trust_list_t list, + const gnutls_x509_crt_t * clist, + unsigned clist_size, unsigned int flags) +{ + unsigned i, j; + size_t hash; + int ret; + unsigned exists; + + for (i = 0; i < clist_size; i++) { + exists = 0; + hash = + hash_pjw_bare(clist[i]->raw_dn.data, + clist[i]->raw_dn.size); + hash %= list->size; + + /* avoid duplicates */ + if (flags & GNUTLS_TL_NO_DUPLICATES || flags & GNUTLS_TL_NO_DUPLICATE_KEY) { + for (j=0;j<list->node[hash].trusted_ca_size;j++) { + if (flags & GNUTLS_TL_NO_DUPLICATES) + ret = gnutls_x509_crt_equals(list->node[hash].trusted_cas[j], clist[i]); + else + ret = _gnutls_check_if_same_key(list->node[hash].trusted_cas[j], clist[i], 1); + if (ret != 0) { + exists = 1; + break; + } + } + + if (exists != 0) { + gnutls_x509_crt_deinit(list->node[hash].trusted_cas[j]); + list->node[hash].trusted_cas[j] = clist[i]; + continue; + } + } + + if (unlikely(INT_ADD_OVERFLOW(list->node[hash].trusted_ca_size, 1))) { + gnutls_assert(); + return i; + } + + list->node[hash].trusted_cas = + _gnutls_reallocarray_fast(list->node[hash].trusted_cas, + list->node[hash].trusted_ca_size + 1, + sizeof(list->node[hash].trusted_cas[0])); + if (list->node[hash].trusted_cas == NULL) { + gnutls_assert(); + return i; + } + + if (gnutls_x509_crt_get_version(clist[i]) >= 3 && + gnutls_x509_crt_get_ca_status(clist[i], NULL) <= 0) { + gnutls_datum_t dn; + gnutls_assert(); + if (gnutls_x509_crt_get_dn2(clist[i], &dn) >= 0) { + _gnutls_audit_log(NULL, + "There was a non-CA certificate in the trusted list: %s.\n", + dn.data); + gnutls_free(dn.data); + } + } + + list->node[hash].trusted_cas[list->node[hash]. + trusted_ca_size] = clist[i]; + list->node[hash].trusted_ca_size++; + + if (flags & GNUTLS_TL_USE_IN_TLS) { + ret = add_new_ca_to_rdn_seq(list, clist[i]); + if (ret < 0) { + gnutls_assert(); + return i+1; + } + } + } + + return i; +} + +static int +advance_iter(gnutls_x509_trust_list_t list, + gnutls_x509_trust_list_iter_t iter) +{ + if (iter->node_index < list->size) { + ++iter->ca_index; + + /* skip entries */ + while (iter->node_index < list->size && + iter->ca_index >= list->node[iter->node_index].trusted_ca_size) { + ++iter->node_index; + iter->ca_index = 0; + } + + if (iter->node_index < list->size) + return 0; + } + +#ifdef ENABLE_PKCS11 + if (list->pkcs11_token != NULL) { + if (iter->pkcs11_list == NULL) { + int ret = gnutls_pkcs11_obj_list_import_url2(&iter->pkcs11_list, &iter->pkcs11_size, + list->pkcs11_token, (GNUTLS_PKCS11_OBJ_FLAG_PRESENT_IN_TRUSTED_MODULE|GNUTLS_PKCS11_OBJ_FLAG_CRT|GNUTLS_PKCS11_OBJ_FLAG_MARK_CA|GNUTLS_PKCS11_OBJ_FLAG_MARK_TRUSTED), 0); + if (ret < 0) + return gnutls_assert_val(ret); + + if (iter->pkcs11_size > 0) + return 0; + } else if (iter->pkcs11_index < iter->pkcs11_size) { + ++iter->pkcs11_index; + if (iter->pkcs11_index < iter->pkcs11_size) + return 0; + } + } +#endif + + return gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE); +} + +/** + * gnutls_x509_trust_list_iter_get_ca: + * @list: The list + * @iter: A pointer to an iterator (initially the iterator should be %NULL) + * @crt: where the certificate will be copied + * + * This function obtains a certificate in the trust list and advances the + * iterator to the next certificate. The certificate returned in @crt must be + * deallocated with gnutls_x509_crt_deinit(). + * + * When past the last element is accessed %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE + * is returned and the iterator is reset. + * + * The iterator is deinitialized and reset to %NULL automatically by this + * function after iterating through all elements until + * %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE is returned. If the iteration is + * aborted early, it must be manually deinitialized using + * gnutls_x509_trust_list_iter_deinit(). + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 3.4.0 + **/ +int +gnutls_x509_trust_list_iter_get_ca(gnutls_x509_trust_list_t list, + gnutls_x509_trust_list_iter_t *iter, + gnutls_x509_crt_t *crt) +{ + int ret; + + /* initialize iterator */ + if (*iter == NULL) { + *iter = gnutls_malloc(sizeof (struct gnutls_x509_trust_list_iter)); + if (*iter == NULL) + return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + + (*iter)->node_index = 0; + (*iter)->ca_index = 0; + +#ifdef ENABLE_PKCS11 + (*iter)->pkcs11_list = NULL; + (*iter)->pkcs11_size = 0; + (*iter)->pkcs11_index = 0; +#endif + + /* Advance iterator to the first valid entry */ + if (list->node[0].trusted_ca_size == 0) { + ret = advance_iter(list, *iter); + if (ret != 0) { + gnutls_x509_trust_list_iter_deinit(*iter); + *iter = NULL; + + *crt = NULL; + return gnutls_assert_val(ret); + } + } + } + + /* obtain the certificate at the current iterator position */ + if ((*iter)->node_index < list->size) { + ret = gnutls_x509_crt_init(crt); + if (ret < 0) + return gnutls_assert_val(ret); + + ret = _gnutls_x509_crt_cpy(*crt, list->node[(*iter)->node_index].trusted_cas[(*iter)->ca_index]); + if (ret < 0) { + gnutls_x509_crt_deinit(*crt); + return gnutls_assert_val(ret); + } + } +#ifdef ENABLE_PKCS11 + else if ( (*iter)->pkcs11_index < (*iter)->pkcs11_size) { + ret = gnutls_x509_crt_init(crt); + if (ret < 0) + return gnutls_assert_val(ret); + + ret = gnutls_x509_crt_import_pkcs11(*crt, (*iter)->pkcs11_list[(*iter)->pkcs11_index]); + if (ret < 0) { + gnutls_x509_crt_deinit(*crt); + return gnutls_assert_val(ret); + } + } +#endif + + else { + /* iterator is at end */ + gnutls_x509_trust_list_iter_deinit(*iter); + *iter = NULL; + + *crt = NULL; + return gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE); + } + + /* Move iterator to the next position. + * GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE is returned if the iterator + * has been moved to the end position. That is okay, we return the + * certificate that we read and when this function is called again we + * report GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE to our caller. */ + ret = advance_iter(list, *iter); + if (ret < 0 && ret != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { + gnutls_x509_crt_deinit(*crt); + *crt = NULL; + + return gnutls_assert_val(ret); + } + + return 0; +} + +/** + * gnutls_x509_trust_list_iter_deinit: + * @iter: The iterator structure to be deinitialized + * + * This function will deinitialize an iterator structure. + * + * Since: 3.4.0 + **/ +void gnutls_x509_trust_list_iter_deinit(gnutls_x509_trust_list_iter_t iter) +{ + if (!iter) + return; + +#ifdef ENABLE_PKCS11 + if (iter->pkcs11_size > 0) { + unsigned i; + for (i = 0; i < iter->pkcs11_size; ++i) + gnutls_pkcs11_obj_deinit(iter->pkcs11_list[i]); + gnutls_free(iter->pkcs11_list); + } +#endif + + gnutls_free(iter); +} + +static gnutls_x509_crt_t crt_cpy(gnutls_x509_crt_t src) +{ +gnutls_x509_crt_t dst; +int ret; + + ret = gnutls_x509_crt_init(&dst); + if (ret < 0) { + gnutls_assert(); + return NULL; + } + + ret = _gnutls_x509_crt_cpy(dst, src); + if (ret < 0) { + gnutls_x509_crt_deinit(dst); + gnutls_assert(); + return NULL; + } + + return dst; +} + +/** + * gnutls_x509_trust_list_remove_cas: + * @list: The list + * @clist: A list of CAs + * @clist_size: The length of the CA list + * + * This function will remove the given certificate authorities + * from the trusted list. + * + * Note that this function can accept certificates and authorities + * not yet known. In that case they will be kept in a separate + * black list that will be used during certificate verification. + * Unlike gnutls_x509_trust_list_add_cas() there is no deinitialization + * restriction for certificate list provided in this function. + * + * Returns: The number of removed elements is returned. + * + * Since: 3.1.10 + **/ +int +gnutls_x509_trust_list_remove_cas(gnutls_x509_trust_list_t list, + const gnutls_x509_crt_t * clist, + unsigned clist_size) +{ + int r = 0; + unsigned j, i; + size_t hash; + + for (i = 0; i < clist_size; i++) { + hash = + hash_pjw_bare(clist[i]->raw_dn.data, + clist[i]->raw_dn.size); + hash %= list->size; + + for (j = 0; j < list->node[hash].trusted_ca_size; j++) { + if (gnutls_x509_crt_equals + (clist[i], + list->node[hash].trusted_cas[j]) != 0) { + + gnutls_x509_crt_deinit(list->node[hash]. + trusted_cas[j]); + list->node[hash].trusted_cas[j] = + list->node[hash].trusted_cas[list-> + node + [hash]. + trusted_ca_size + - 1]; + list->node[hash].trusted_ca_size--; + r++; + break; + } + } + + if (unlikely(INT_ADD_OVERFLOW(list->blacklisted_size, 1))) { + return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + } + + /* Add the CA (or plain) certificate to the black list as well. + * This will prevent a subordinate CA from being valid, and + * ensure that a server certificate will also get rejected. + */ + list->blacklisted = + _gnutls_reallocarray_fast(list->blacklisted, + list->blacklisted_size + 1, + sizeof(list->blacklisted[0])); + if (list->blacklisted == NULL) + return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + + list->blacklisted[list->blacklisted_size] = crt_cpy(clist[i]); + if (list->blacklisted[list->blacklisted_size] != NULL) + list->blacklisted_size++; + } + + return r; +} + +/** + * gnutls_x509_trust_list_add_named_crt: + * @list: The list + * @cert: A certificate + * @name: An identifier for the certificate + * @name_size: The size of the identifier + * @flags: should be 0. + * + * This function will add the given certificate to the trusted + * list and associate it with a name. The certificate will not be + * be used for verification with gnutls_x509_trust_list_verify_crt() + * but with gnutls_x509_trust_list_verify_named_crt() or + * gnutls_x509_trust_list_verify_crt2() - the latter only since + * GnuTLS 3.4.0 and if a hostname is provided. + * + * In principle this function can be used to set individual "server" + * certificates that are trusted by the user for that specific server + * but for no other purposes. + * + * The certificate @cert must not be deinitialized during the lifetime + * of the @list. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 3.0.0 + **/ +int +gnutls_x509_trust_list_add_named_crt(gnutls_x509_trust_list_t list, + gnutls_x509_crt_t cert, + const void *name, size_t name_size, + unsigned int flags) +{ + size_t hash; + + if (name_size >= MAX_SERVER_NAME_SIZE) + return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + + hash = + hash_pjw_bare(cert->raw_issuer_dn.data, + cert->raw_issuer_dn.size); + hash %= list->size; + + if (unlikely(INT_ADD_OVERFLOW(list->node[hash].named_cert_size, 1))) { + return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + } + + list->node[hash].named_certs = + _gnutls_reallocarray_fast(list->node[hash].named_certs, + list->node[hash].named_cert_size + 1, + sizeof(list->node[hash].named_certs[0])); + if (list->node[hash].named_certs == NULL) + return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + + list->node[hash].named_certs[list->node[hash].named_cert_size]. + cert = cert; + memcpy(list->node[hash]. + named_certs[list->node[hash].named_cert_size].name, name, + name_size); + list->node[hash].named_certs[list->node[hash]. + named_cert_size].name_size = + name_size; + + list->node[hash].named_cert_size++; + + return 0; +} + +/** + * gnutls_x509_trust_list_add_crls: + * @list: The list + * @crl_list: A list of CRLs + * @crl_size: The length of the CRL list + * @flags: flags from %gnutls_trust_list_flags_t + * @verification_flags: gnutls_certificate_verify_flags if flags specifies GNUTLS_TL_VERIFY_CRL + * + * This function will add the given certificate revocation lists + * to the trusted list. The CRLs in @crl_list must not be deinitialized + * during the lifetime of @list. + * + * This function must be called after gnutls_x509_trust_list_add_cas() + * to allow verifying the CRLs for validity. If the flag %GNUTLS_TL_NO_DUPLICATES + * is given, then the final CRL list will not contain duplicate entries. + * + * If the flag %GNUTLS_TL_NO_DUPLICATES is given, gnutls_x509_trust_list_deinit() must be + * called with parameter @all being 1. + * + * If flag %GNUTLS_TL_VERIFY_CRL is given the CRLs will be verified before being added, + * and if verification fails, they will be skipped. + * + * Returns: The number of added elements is returned; that includes + * duplicate entries. + * + * Since: 3.0 + **/ +int +gnutls_x509_trust_list_add_crls(gnutls_x509_trust_list_t list, + const gnutls_x509_crl_t * crl_list, + unsigned crl_size, unsigned int flags, + unsigned int verification_flags) +{ + int ret; + unsigned x, i, j = 0; + unsigned int vret = 0; + size_t hash; + gnutls_x509_crl_t *tmp; + + /* Probably we can optimize things such as removing duplicates + * etc. + */ + if (crl_size == 0 || crl_list == NULL) + return 0; + + for (i = 0; i < crl_size; i++) { + hash = + hash_pjw_bare(crl_list[i]->raw_issuer_dn.data, + crl_list[i]->raw_issuer_dn.size); + hash %= list->size; + + if (flags & GNUTLS_TL_VERIFY_CRL) { + + ret = + gnutls_x509_crl_verify(crl_list[i], + list->node[hash]. + trusted_cas, + list->node[hash]. + trusted_ca_size, + verification_flags, + &vret); + if (ret < 0 || vret != 0) { + _gnutls_debug_log("CRL verification failed, not adding it\n"); + if (flags & GNUTLS_TL_NO_DUPLICATES) + gnutls_x509_crl_deinit(crl_list[i]); + if (flags & GNUTLS_TL_FAIL_ON_INVALID_CRL) + return gnutls_assert_val(GNUTLS_E_CRL_VERIFICATION_ERROR); + continue; + } + } + + /* If the CRL added overrides a previous one, then overwrite + * the old one */ + if (flags & GNUTLS_TL_NO_DUPLICATES) { + for (x=0;x<list->node[hash].crl_size;x++) { + if (crl_list[i]->raw_issuer_dn.size == list->node[hash].crls[x]->raw_issuer_dn.size && + memcmp(crl_list[i]->raw_issuer_dn.data, list->node[hash].crls[x]->raw_issuer_dn.data, crl_list[i]->raw_issuer_dn.size) == 0) { + if (gnutls_x509_crl_get_this_update(crl_list[i]) >= + gnutls_x509_crl_get_this_update(list->node[hash].crls[x])) { + + gnutls_x509_crl_deinit(list->node[hash].crls[x]); + list->node[hash].crls[x] = crl_list[i]; + goto next; + } else { + /* The new is older, discard it */ + gnutls_x509_crl_deinit(crl_list[i]); + goto next; + } + } + } + } + + if (unlikely(INT_ADD_OVERFLOW(list->node[hash].crl_size, 1))) { + gnutls_assert(); + goto error; + } + + tmp = _gnutls_reallocarray(list->node[hash].crls, + list->node[hash].crl_size + 1, + sizeof(list->node[hash].crls[0])); + if (tmp == NULL) { + gnutls_assert(); + goto error; + } + list->node[hash].crls = tmp; + + + list->node[hash].crls[list->node[hash].crl_size] = + crl_list[i]; + list->node[hash].crl_size++; + + next: + j++; + } + + return j; + + error: + ret = i; + if (flags & GNUTLS_TL_NO_DUPLICATES) + while (i < crl_size) + gnutls_x509_crl_deinit(crl_list[i++]); + return ret; +} + +/* Takes a certificate list and shortens it if there are + * intermedia certificates already trusted by us. + * + * Returns the new size of the list or a negative number on error. + */ +static int shorten_clist(gnutls_x509_trust_list_t list, + gnutls_x509_crt_t * certificate_list, + unsigned int clist_size) +{ + unsigned int j, i; + size_t hash; + + if (clist_size > 1) { + /* Check if the last certificate in the path is self signed. + * In that case ignore it (a certificate is trusted only if it + * leads to a trusted party by us, not the server's). + * + * This prevents from verifying self signed certificates against + * themselves. This (although not bad) caused verification + * failures on some root self signed certificates that use the + * MD2 algorithm. + */ + if (gnutls_x509_crt_check_issuer + (certificate_list[clist_size - 1], + certificate_list[clist_size - 1]) != 0) { + clist_size--; + } + } + + /* We want to shorten the chain by removing the cert that matches + * one of the certs we trust and all the certs after that i.e. if + * cert chain is A signed-by B signed-by C signed-by D (signed-by + * self-signed E but already removed above), and we trust B, remove + * B, C and D. */ + for (i = 1; i < clist_size; i++) { + hash = + hash_pjw_bare(certificate_list[i]->raw_issuer_dn.data, + certificate_list[i]->raw_issuer_dn.size); + hash %= list->size; + + for (j = 0; j < list->node[hash].trusted_ca_size; j++) { + if (gnutls_x509_crt_equals + (certificate_list[i], + list->node[hash].trusted_cas[j]) != 0) { + /* cut the list at the point of first the trusted certificate */ + clist_size = i + 1; + break; + } + } + /* clist_size may have been changed which gets out of loop */ + } + + return clist_size; +} + +/* Takes a subject certificate, retrieves a chain from its issuers in + * @certificate_list, using the issuer callback set for @list. + * + * Returns the new size of the list or a negative number on error. + */ +static int +retrieve_issuers(gnutls_x509_trust_list_t list, + gnutls_x509_crt_t subject, + gnutls_x509_crt_t *certificate_list, + unsigned int clist_size_max) +{ + gnutls_x509_crt_t *issuers; + unsigned int issuers_size; + unsigned int i; + int ret; + + if (!list->issuer_callback) { + return 0; + } + + _gnutls_cert_log("calling issuer callback on", subject); + + ret = list->issuer_callback(list, subject, &issuers, &issuers_size); + if (ret < 0) { + return gnutls_assert_val(ret); + } + + /* Ignore empty list */ + if (!issuers_size) { + ret = 0; + goto cleanup; + } + + if (issuers_size > clist_size_max) { + _gnutls_debug_log("too many issuers returned; skipping\n"); + ret = 0; + goto cleanup; + } + + for (i = 0; i < issuers_size; i++) { + if (!gnutls_x509_crt_check_issuer(subject, issuers[i])) { + _gnutls_cert_log("unrelated certificate; skipping", + issuers[i]); + break; + } + subject = issuers[i]; + } + + ret = i; + + memcpy(certificate_list, issuers, ret * sizeof(gnutls_x509_crt_t)); + + cleanup: + for (i = ret; i < issuers_size; i++) { + gnutls_x509_crt_deinit(issuers[i]); + } + gnutls_free(issuers); + + return ret; +} + +int _gnutls_trust_list_get_issuer(gnutls_x509_trust_list_t list, + gnutls_x509_crt_t cert, + gnutls_x509_crt_t * issuer, + unsigned int flags) +{ + int ret; + unsigned int i; + size_t hash; + + hash = + hash_pjw_bare(cert->raw_issuer_dn.data, + cert->raw_issuer_dn.size); + hash %= list->size; + + for (i = 0; i < list->node[hash].trusted_ca_size; i++) { + ret = + gnutls_x509_crt_check_issuer(cert, + list->node[hash]. + trusted_cas[i]); + if (ret != 0) { + if (flags & GNUTLS_TL_GET_COPY) { + *issuer = crt_cpy(list->node[hash].trusted_cas[i]); + } else { + *issuer = list->node[hash].trusted_cas[i]; + } + return 0; + } + } + + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; +} + +static +int trust_list_get_issuer_by_dn(gnutls_x509_trust_list_t list, + const gnutls_datum_t *dn, + const gnutls_datum_t *spki, + gnutls_x509_crt_t * issuer, + unsigned int flags) +{ + int ret; + unsigned int i, j; + size_t hash; + uint8_t tmp[256]; + size_t tmp_size; + + if (dn) { + hash = + hash_pjw_bare(dn->data, + dn->size); + hash %= list->size; + + for (i = 0; i < list->node[hash].trusted_ca_size; i++) { + ret = _gnutls_x509_compare_raw_dn(dn, &list->node[hash].trusted_cas[i]->raw_dn); + if (ret != 0) { + if (spki && spki->size > 0) { + tmp_size = sizeof(tmp); + + ret = gnutls_x509_crt_get_subject_key_id(list->node[hash].trusted_cas[i], tmp, &tmp_size, NULL); + if (ret < 0) + continue; + if (spki->size != tmp_size || memcmp(spki->data, tmp, spki->size) != 0) + continue; + } + *issuer = crt_cpy(list->node[hash].trusted_cas[i]); + return 0; + } + } + } else if (spki) { + /* search everything! */ + for (i = 0; i < list->size; i++) { + for (j = 0; j < list->node[i].trusted_ca_size; j++) { + tmp_size = sizeof(tmp); + + ret = gnutls_x509_crt_get_subject_key_id(list->node[i].trusted_cas[j], tmp, &tmp_size, NULL); + if (ret < 0) + continue; + + if (spki->size != tmp_size || memcmp(spki->data, tmp, spki->size) != 0) + continue; + + *issuer = crt_cpy(list->node[i].trusted_cas[j]); + return 0; + } + } + } + + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; +} + +/** + * gnutls_x509_trust_list_get_issuer: + * @list: The list + * @cert: is the certificate to find issuer for + * @issuer: Will hold the issuer if any. Should be treated as constant + * unless %GNUTLS_TL_GET_COPY is set in @flags. + * @flags: flags from %gnutls_trust_list_flags_t (%GNUTLS_TL_GET_COPY is applicable) + * + * This function will find the issuer of the given certificate. + * If the flag %GNUTLS_TL_GET_COPY is specified a copy of the issuer + * will be returned which must be freed using gnutls_x509_crt_deinit(). + * In that case the provided @issuer must not be initialized. + * + * Note that the flag %GNUTLS_TL_GET_COPY is required for this function + * to work with PKCS#11 trust lists in a thread-safe way. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 3.0 + **/ +int gnutls_x509_trust_list_get_issuer(gnutls_x509_trust_list_t list, + gnutls_x509_crt_t cert, + gnutls_x509_crt_t * issuer, + unsigned int flags) +{ + int ret; + + ret = _gnutls_trust_list_get_issuer(list, cert, issuer, flags); + if (ret == 0) { + return 0; + } + +#ifdef ENABLE_PKCS11 + if (ret < 0 && list->pkcs11_token) { + gnutls_x509_crt_t crt; + gnutls_datum_t der = {NULL, 0}; + /* use the token for verification */ + ret = gnutls_pkcs11_get_raw_issuer(list->pkcs11_token, cert, &der, + GNUTLS_X509_FMT_DER, GNUTLS_PKCS11_OBJ_FLAG_PRESENT_IN_TRUSTED_MODULE); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + ret = gnutls_x509_crt_init(&crt); + if (ret < 0) { + gnutls_free(der.data); + return gnutls_assert_val(ret); + } + + ret = gnutls_x509_crt_import(crt, &der, GNUTLS_X509_FMT_DER); + gnutls_free(der.data); + if (ret < 0) { + gnutls_x509_crt_deinit(crt); + return gnutls_assert_val(ret); + } + + if (flags & GNUTLS_TL_GET_COPY) { + *issuer = crt; + return 0; + } else { + /* we add this CA to the keep_cert list in order to make it + * persistent. It will be deallocated when the trust list is. + */ + ret = trust_list_add_compat(list, crt); + if (ret < 0) { + gnutls_x509_crt_deinit(crt); + return gnutls_assert_val(ret); + } + *issuer = crt; + return ret; + } + } +#endif + return ret; +} + +/** + * gnutls_x509_trust_list_get_issuer_by_dn: + * @list: The list + * @dn: is the issuer's DN + * @issuer: Will hold the issuer if any. Should be deallocated after use. + * @flags: Use zero + * + * This function will find the issuer with the given name, and + * return a copy of the issuer, which must be freed using gnutls_x509_crt_deinit(). + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 3.4.0 + **/ +int gnutls_x509_trust_list_get_issuer_by_dn(gnutls_x509_trust_list_t list, + const gnutls_datum_t *dn, + gnutls_x509_crt_t *issuer, + unsigned int flags) +{ + int ret; + + ret = trust_list_get_issuer_by_dn(list, dn, NULL, issuer, flags); + if (ret == 0) { + return 0; + } + +#ifdef ENABLE_PKCS11 + if (ret < 0 && list->pkcs11_token) { + gnutls_x509_crt_t crt; + gnutls_datum_t der = {NULL, 0}; + /* use the token for verification */ + ret = gnutls_pkcs11_get_raw_issuer_by_dn(list->pkcs11_token, dn, &der, + GNUTLS_X509_FMT_DER, GNUTLS_PKCS11_OBJ_FLAG_PRESENT_IN_TRUSTED_MODULE); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + ret = gnutls_x509_crt_init(&crt); + if (ret < 0) { + gnutls_free(der.data); + return gnutls_assert_val(ret); + } + + ret = gnutls_x509_crt_import(crt, &der, GNUTLS_X509_FMT_DER); + gnutls_free(der.data); + if (ret < 0) { + gnutls_x509_crt_deinit(crt); + return gnutls_assert_val(ret); + } + + *issuer = crt; + return 0; + } +#endif + return ret; +} + +/** + * gnutls_x509_trust_list_get_issuer_by_subject_key_id: + * @list: The list + * @dn: is the issuer's DN (may be %NULL) + * @spki: is the subject key ID + * @issuer: Will hold the issuer if any. Should be deallocated after use. + * @flags: Use zero + * + * This function will find the issuer with the given name and subject key ID, and + * return a copy of the issuer, which must be freed using gnutls_x509_crt_deinit(). + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 3.4.2 + **/ +int gnutls_x509_trust_list_get_issuer_by_subject_key_id(gnutls_x509_trust_list_t list, + const gnutls_datum_t *dn, + const gnutls_datum_t *spki, + gnutls_x509_crt_t *issuer, + unsigned int flags) +{ + int ret; + + ret = trust_list_get_issuer_by_dn(list, dn, spki, issuer, flags); + if (ret == 0) { + return 0; + } + +#ifdef ENABLE_PKCS11 + if (ret < 0 && list->pkcs11_token) { + gnutls_x509_crt_t crt; + gnutls_datum_t der = {NULL, 0}; + /* use the token for verification */ + ret = gnutls_pkcs11_get_raw_issuer_by_subject_key_id(list->pkcs11_token, dn, spki, &der, + GNUTLS_X509_FMT_DER, GNUTLS_PKCS11_OBJ_FLAG_PRESENT_IN_TRUSTED_MODULE); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + ret = gnutls_x509_crt_init(&crt); + if (ret < 0) { + gnutls_free(der.data); + return gnutls_assert_val(ret); + } + + ret = gnutls_x509_crt_import(crt, &der, GNUTLS_X509_FMT_DER); + gnutls_free(der.data); + if (ret < 0) { + gnutls_x509_crt_deinit(crt); + return gnutls_assert_val(ret); + } + + *issuer = crt; + return 0; + } +#endif + return ret; +} + +static +int check_if_in_blacklist(gnutls_x509_crt_t * cert_list, unsigned int cert_list_size, + gnutls_x509_crt_t * blacklist, unsigned int blacklist_size) +{ +unsigned i, j; + + if (blacklist_size == 0) + return 0; + + for (i=0;i<cert_list_size;i++) { + for (j=0;j<blacklist_size;j++) { + if (gnutls_x509_crt_equals(cert_list[i], blacklist[j]) != 0) { + return 1; + } + } + } + + return 0; +} + +/** + * gnutls_x509_trust_list_verify_crt: + * @list: The list + * @cert_list: is the certificate list to be verified + * @cert_list_size: is the certificate list size + * @flags: Flags that may be used to change the verification algorithm. Use OR of the gnutls_certificate_verify_flags enumerations. + * @voutput: will hold the certificate verification output. + * @func: If non-null will be called on each chain element verification with the output. + * + * This function will try to verify the given certificate and return + * its status. The @voutput parameter will hold an OR'ed sequence of + * %gnutls_certificate_status_t flags. + * + * The details of the verification are the same as in gnutls_x509_trust_list_verify_crt2(). + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 3.0 + **/ +int +gnutls_x509_trust_list_verify_crt(gnutls_x509_trust_list_t list, + gnutls_x509_crt_t * cert_list, + unsigned int cert_list_size, + unsigned int flags, + unsigned int *voutput, + gnutls_verify_output_function func) +{ + return gnutls_x509_trust_list_verify_crt2(list, cert_list, cert_list_size, + NULL, 0, flags, voutput, func); +} + +#define LAST_DN cert_list[cert_list_size-1]->raw_dn +#define LAST_IDN cert_list[cert_list_size-1]->raw_issuer_dn +/* This macro is introduced to detect a verification output which + * indicates an unknown signer, a signer which uses an insecure + * algorithm (e.g., sha1), a signer has expired, or something that + * indicates a superseded signer */ +#define SIGNER_OLD_OR_UNKNOWN(output) ((output & GNUTLS_CERT_SIGNER_NOT_FOUND) || \ + (output & GNUTLS_CERT_EXPIRED) || \ + (output & GNUTLS_CERT_INSECURE_ALGORITHM)) +#define SIGNER_WAS_KNOWN(output) (!(output & GNUTLS_CERT_SIGNER_NOT_FOUND)) + +/** + * gnutls_x509_trust_list_verify_crt2: + * @list: The list + * @cert_list: is the certificate list to be verified + * @cert_list_size: is the certificate list size + * @data: an array of typed data + * @elements: the number of data elements + * @flags: Flags that may be used to change the verification algorithm. Use OR of the gnutls_certificate_verify_flags enumerations. + * @voutput: will hold the certificate verification output. + * @func: If non-null will be called on each chain element verification with the output. + * + * This function will attempt to verify the given certificate chain and return + * its status. The @voutput parameter will hold an OR'ed sequence of + * %gnutls_certificate_status_t flags. + * + * When a certificate chain of @cert_list_size with more than one certificates is + * provided, the verification status will apply to the first certificate in the chain + * that failed verification. The verification process starts from the end of the chain + * (from CA to end certificate). The first certificate in the chain must be the end-certificate + * while the rest of the members may be sorted or not. + * + * Additionally a certificate verification profile can be specified + * from the ones in %gnutls_certificate_verification_profiles_t by + * ORing the result of GNUTLS_PROFILE_TO_VFLAGS() to the verification + * flags. + * + * Additional verification parameters are possible via the @data types; the + * acceptable types are %GNUTLS_DT_DNS_HOSTNAME, %GNUTLS_DT_IP_ADDRESS and %GNUTLS_DT_KEY_PURPOSE_OID. + * The former accepts as data a null-terminated hostname, and the latter a null-terminated + * object identifier (e.g., %GNUTLS_KP_TLS_WWW_SERVER). + * If a DNS hostname is provided then this function will compare + * the hostname in the end certificate against the given. If names do not match the + * %GNUTLS_CERT_UNEXPECTED_OWNER status flag will be set. In addition it + * will consider certificates provided with gnutls_x509_trust_list_add_named_crt(). + * + * If a key purpose OID is provided and the end-certificate contains the extended key + * usage PKIX extension, it will be required to match the provided OID + * or be marked for any purpose, otherwise verification will fail with + * %GNUTLS_CERT_PURPOSE_MISMATCH status. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. Note that verification failure will not result to an + * error code, only @voutput will be updated. + * + * Since: 3.3.8 + **/ +int +gnutls_x509_trust_list_verify_crt2(gnutls_x509_trust_list_t list, + gnutls_x509_crt_t * cert_list, + unsigned int cert_list_size, + gnutls_typed_vdata_st *data, + unsigned int elements, + unsigned int flags, + unsigned int *voutput, + gnutls_verify_output_function func) +{ + int ret = 0; + unsigned int i; + size_t hash; + gnutls_x509_crt_t sorted[DEFAULT_MAX_VERIFY_DEPTH]; + gnutls_x509_crt_t retrieved[DEFAULT_MAX_VERIFY_DEPTH]; + unsigned int retrieved_size = 0; + const char *hostname = NULL, *purpose = NULL, *email = NULL; + unsigned hostname_size = 0; + unsigned have_set_name = 0; + unsigned saved_output; + gnutls_datum_t ip = {NULL, 0}; + struct cert_set_st cert_set = { NULL, 0 }; + + if (cert_list == NULL || cert_list_size < 1) + return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + + for (i=0;i<elements;i++) { + if (data[i].type == GNUTLS_DT_DNS_HOSTNAME) { + hostname = (void*)data[i].data; + if (data[i].size > 0) { + hostname_size = data[i].size; + } + + if (have_set_name != 0) return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + have_set_name = 1; + } else if (data[i].type == GNUTLS_DT_IP_ADDRESS) { + if (data[i].size > 0) { + ip.data = data[i].data; + ip.size = data[i].size; + } + + if (have_set_name != 0) return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + have_set_name = 1; + } else if (data[i].type == GNUTLS_DT_RFC822NAME) { + email = (void*)data[i].data; + + if (have_set_name != 0) return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + have_set_name = 1; + } else if (data[i].type == GNUTLS_DT_KEY_PURPOSE_OID) { + purpose = (void*)data[i].data; + } + } + + if (hostname) { /* shortcut using the named certs - if any */ + unsigned vtmp = 0; + if (hostname_size == 0) + hostname_size = strlen(hostname); + + ret = gnutls_x509_trust_list_verify_named_crt(list, + cert_list[0], hostname, hostname_size, + flags, &vtmp, func); + if (ret == 0 && vtmp == 0) { + *voutput = vtmp; + return 0; + } + } + + memcpy(sorted, cert_list, cert_list_size * sizeof(gnutls_x509_crt_t)); + cert_list = sorted; + + ret = cert_set_init(&cert_set, DEFAULT_MAX_VERIFY_DEPTH); + if (ret < 0) { + return ret; + } + + for (i = 0; i < cert_list_size && + cert_list_size <= DEFAULT_MAX_VERIFY_DEPTH; ) { + unsigned int sorted_size = 1; + unsigned int j; + gnutls_x509_crt_t issuer; + + if (!(flags & GNUTLS_VERIFY_DO_NOT_ALLOW_UNSORTED_CHAIN)) { + sorted_size = _gnutls_sort_clist(&cert_list[i], + cert_list_size - i); + } + + /* Remove duplicates. Start with index 1, as the first element + * may be re-checked after issuer retrieval. */ + for (j = 1; j < sorted_size; j++) { + if (cert_set_contains(&cert_set, cert_list[i + j])) { + if (i + j < cert_list_size - 1) { + memmove(&cert_list[i + j], + &cert_list[i + j + 1], + sizeof(cert_list[i])); + } + cert_list_size--; + break; + } + } + /* Found a duplicate, try again with the same index. */ + if (j < sorted_size) { + continue; + } + + /* Record the certificates seen. */ + for (j = 0; j < sorted_size; j++, i++) { + ret = cert_set_add(&cert_set, cert_list[i]); + if (ret < 0) { + goto cleanup; + } + } + + /* If the issuer of the certificate is known, no need + * for further processing. */ + if (gnutls_x509_trust_list_get_issuer(list, + cert_list[i - 1], + &issuer, + GNUTLS_TL_GET_COPY) == 0) { + gnutls_x509_crt_deinit(issuer); + cert_list_size = i; + break; + } + + /* If there is no gap between this and the next certificate, + * proceed with the next certificate. */ + if (i < cert_list_size && + gnutls_x509_crt_check_issuer(cert_list[i - 1], + cert_list[i])) { + continue; + } + + ret = retrieve_issuers(list, + cert_list[i - 1], + &retrieved[retrieved_size], + DEFAULT_MAX_VERIFY_DEPTH - + MAX(retrieved_size, + cert_list_size)); + if (ret < 0) { + break; + } else if (ret > 0) { + assert((unsigned int)ret <= + DEFAULT_MAX_VERIFY_DEPTH - cert_list_size); + memmove(&cert_list[i + ret], + &cert_list[i], + (cert_list_size - i) * + sizeof(gnutls_x509_crt_t)); + memcpy(&cert_list[i], + &retrieved[retrieved_size], + ret * sizeof(gnutls_x509_crt_t)); + retrieved_size += ret; + cert_list_size += ret; + + /* Start again from the end of the previous segment. */ + i--; + } + } + + cert_list_size = shorten_clist(list, cert_list, cert_list_size); + if (cert_list_size <= 0) + return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); + + hash = + hash_pjw_bare(cert_list[cert_list_size - 1]->raw_issuer_dn. + data, + cert_list[cert_list_size - + 1]->raw_issuer_dn.size); + hash %= list->size; + + ret = check_if_in_blacklist(cert_list, cert_list_size, + list->blacklisted, list->blacklisted_size); + if (ret != 0) { + *voutput = 0; + *voutput |= GNUTLS_CERT_REVOKED; + *voutput |= GNUTLS_CERT_INVALID; + ret = 0; + goto cleanup; + } + + *voutput = + _gnutls_verify_crt_status(list, cert_list, cert_list_size, + list->node[hash].trusted_cas, + list->node[hash].trusted_ca_size, + flags, purpose, func); + saved_output = *voutput; + + if (SIGNER_OLD_OR_UNKNOWN(*voutput) && + (LAST_DN.size != LAST_IDN.size || + memcmp(LAST_DN.data, LAST_IDN.data, LAST_IDN.size) != 0)) { + + /* if we couldn't find the issuer, try to see if the last + * certificate is in the trusted list and try to verify against + * (if it is not self signed) */ + hash = + hash_pjw_bare(cert_list[cert_list_size - 1]->raw_dn. + data, cert_list[cert_list_size - 1]->raw_dn.size); + hash %= list->size; + + _gnutls_debug_log("issuer in verification was not found or insecure; trying against trust list\n"); + + *voutput = + _gnutls_verify_crt_status(list, cert_list, cert_list_size, + list->node[hash].trusted_cas, + list->node[hash].trusted_ca_size, + flags, purpose, func); + if (*voutput != 0) { + if (SIGNER_WAS_KNOWN(saved_output)) + *voutput = saved_output; + gnutls_assert(); + } + } + + saved_output = *voutput; + +#ifdef ENABLE_PKCS11 + if (SIGNER_OLD_OR_UNKNOWN(*voutput) && list->pkcs11_token) { + /* use the token for verification */ + + *voutput = _gnutls_pkcs11_verify_crt_status(list, list->pkcs11_token, + cert_list, cert_list_size, + purpose, + flags, func); + if (*voutput != 0) { + if (SIGNER_WAS_KNOWN(saved_output)) + *voutput = saved_output; + gnutls_assert(); + } + } +#endif + + /* End-certificate, key purpose and hostname checks. */ + if (purpose) { + ret = _gnutls_check_key_purpose(cert_list[0], purpose, 0); + if (ret != 1) { + gnutls_assert(); + *voutput |= GNUTLS_CERT_PURPOSE_MISMATCH|GNUTLS_CERT_INVALID; + } + } + + if (hostname) { + ret = + gnutls_x509_crt_check_hostname2(cert_list[0], hostname, flags); + if (ret == 0) { + gnutls_assert(); + *voutput |= GNUTLS_CERT_UNEXPECTED_OWNER|GNUTLS_CERT_INVALID; + } + } + + if (ip.data) { + ret = + gnutls_x509_crt_check_ip(cert_list[0], ip.data, ip.size, flags); + if (ret == 0) { + gnutls_assert(); + *voutput |= GNUTLS_CERT_UNEXPECTED_OWNER|GNUTLS_CERT_INVALID; + } + } + + if (email) { + ret = + gnutls_x509_crt_check_email(cert_list[0], email, 0); + if (ret == 0) { + gnutls_assert(); + *voutput |= GNUTLS_CERT_UNEXPECTED_OWNER|GNUTLS_CERT_INVALID; + } + } + + /* CRL checks follow */ + + if (*voutput != 0 || (flags & GNUTLS_VERIFY_DISABLE_CRL_CHECKS)) { + ret = 0; + goto cleanup; + } + + /* Check revocation of individual certificates. + * start with the last one that we already have its hash + */ + ret = + _gnutls_x509_crt_check_revocation(cert_list + [cert_list_size - 1], + list->node[hash].crls, + list->node[hash].crl_size, + func); + if (ret == 1) { /* revoked */ + *voutput |= GNUTLS_CERT_REVOKED; + *voutput |= GNUTLS_CERT_INVALID; + ret = 0; + goto cleanup; + } + + for (i = 0; i < cert_list_size - 1; i++) { + hash = + hash_pjw_bare(cert_list[i]->raw_issuer_dn.data, + cert_list[i]->raw_issuer_dn.size); + hash %= list->size; + + ret = _gnutls_x509_crt_check_revocation(cert_list[i], + list->node[hash]. + crls, + list->node[hash]. + crl_size, func); + if (ret < 0) { + gnutls_assert(); + } else if (ret == 1) { /* revoked */ + *voutput |= GNUTLS_CERT_REVOKED; + *voutput |= GNUTLS_CERT_INVALID; + ret = 0; + goto cleanup; + } + } + + cleanup: + for (i = 0; i < retrieved_size; i++) { + gnutls_x509_crt_deinit(retrieved[i]); + } + cert_set_deinit(&cert_set); + return ret; +} + +/** + * gnutls_x509_trust_list_verify_named_crt: + * @list: The list + * @cert: is the certificate to be verified + * @name: is the certificate's name + * @name_size: is the certificate's name size + * @flags: Flags that may be used to change the verification algorithm. Use OR of the gnutls_certificate_verify_flags enumerations. + * @voutput: will hold the certificate verification output. + * @func: If non-null will be called on each chain element verification with the output. + * + * This function will try to find a certificate that is associated with the provided + * name --see gnutls_x509_trust_list_add_named_crt(). If a match is found the + * certificate is considered valid. In addition to that this function will also + * check CRLs. The @voutput parameter will hold an OR'ed sequence of + * %gnutls_certificate_status_t flags. + * + * Additionally a certificate verification profile can be specified + * from the ones in %gnutls_certificate_verification_profiles_t by + * ORing the result of GNUTLS_PROFILE_TO_VFLAGS() to the verification + * flags. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 3.0.0 + **/ +int +gnutls_x509_trust_list_verify_named_crt(gnutls_x509_trust_list_t list, + gnutls_x509_crt_t cert, + const void *name, + size_t name_size, + unsigned int flags, + unsigned int *voutput, + gnutls_verify_output_function func) +{ + int ret; + unsigned int i; + size_t hash; + + + hash = + hash_pjw_bare(cert->raw_issuer_dn.data, + cert->raw_issuer_dn.size); + hash %= list->size; + + ret = check_if_in_blacklist(&cert, 1, + list->blacklisted, list->blacklisted_size); + if (ret != 0) { + *voutput = 0; + *voutput |= GNUTLS_CERT_REVOKED; + *voutput |= GNUTLS_CERT_INVALID; + return 0; + } + + *voutput = GNUTLS_CERT_INVALID | GNUTLS_CERT_SIGNER_NOT_FOUND; + + for (i = 0; i < list->node[hash].named_cert_size; i++) { + if (gnutls_x509_crt_equals(cert, list->node[hash].named_certs[i].cert) != 0) { /* check if name matches */ + if (list->node[hash].named_certs[i].name_size == + name_size + && memcmp(list->node[hash].named_certs[i].name, + name, name_size) == 0) { + *voutput = 0; + break; + } + } + } + + if (*voutput != 0 || (flags & GNUTLS_VERIFY_DISABLE_CRL_CHECKS)) + return 0; + + /* Check revocation of individual certificates. + * start with the last one that we already have its hash + */ + ret = _gnutls_x509_crt_check_revocation(cert, + list->node[hash].crls, + list->node[hash].crl_size, + func); + if (ret == 1) { /* revoked */ + *voutput |= GNUTLS_CERT_REVOKED; + *voutput |= GNUTLS_CERT_INVALID; + return 0; + } + + return 0; +} + +/* return 1 if @cert is in @list, 0 if not */ +int +_gnutls_trustlist_inlist(gnutls_x509_trust_list_t list, + gnutls_x509_crt_t cert) +{ + int ret; + unsigned int i; + size_t hash; + + hash = hash_pjw_bare(cert->raw_dn.data, cert->raw_dn.size); + hash %= list->size; + + for (i = 0; i < list->node[hash].trusted_ca_size; i++) { + ret = + gnutls_x509_crt_equals(cert, + list->node[hash]. + trusted_cas[i]); + if (ret != 0) + return 1; + } + + return 0; +} diff --git a/lib/x509/verify-high.h b/lib/x509/verify-high.h new file mode 100644 index 0000000..4cbb29a --- /dev/null +++ b/lib/x509/verify-high.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2011-2012 Free Software Foundation, Inc. + * + * Author: Simon Josefsson + * + * This file is part of GnuTLS. + * + * The GnuTLS is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/> + * + */ + +#ifndef GNUTLS_LIB_X509_VERIFY_HIGH_H +#define GNUTLS_LIB_X509_VERIFY_HIGH_H + +struct gnutls_x509_trust_list_st { + unsigned int size; + struct node_st *node; + + /* holds a sequence of the RDNs of the CAs above. + * This is used when using the trust list in TLS. + */ + gnutls_datum_t x509_rdn_sequence; + + gnutls_x509_crt_t *blacklisted; + unsigned int blacklisted_size; + + /* certificates that will be deallocated when this struct + * will be deinitialized */ + gnutls_x509_crt_t *keep_certs; + unsigned int keep_certs_size; + + char* pkcs11_token; + + /* set this callback if the issuer in the certificate + * chain is missing. */ + gnutls_x509_trust_list_getissuer_function *issuer_callback; + /* set user pointer. */ + void *usr_ptr; +}; + +int _gnutls_trustlist_inlist(gnutls_x509_trust_list_t list, + gnutls_x509_crt_t cert); + +#endif /* GNUTLS_LIB_X509_VERIFY_HIGH_H */ diff --git a/lib/x509/verify-high2.c b/lib/x509/verify-high2.c new file mode 100644 index 0000000..16d757c --- /dev/null +++ b/lib/x509/verify-high2.c @@ -0,0 +1,568 @@ +/* + * Copyright (C) 2012-2014 Free Software Foundation, Inc. + * Copyright (C) 2014 Nikos Mavrogiannopoulos + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GnuTLS. + * + * The GnuTLS is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/> + * + */ + +#include "gnutls_int.h" +#include "errors.h" +#include <libtasn1.h> +#include <global.h> +#include <num.h> +#include <tls-sig.h> +#include <str.h> +#include <c-strcase.h> +#include <datum.h> +#include "x509_int.h" +#include <common.h> +#include "verify-high.h" +#include "read-file.h" +#include <pkcs11_int.h> +#include "urls.h" + +#include <dirent.h> + +#if !defined(_DIRENT_HAVE_D_TYPE) && !defined(__native_client__) +# ifdef DT_UNKNOWN +# define _DIRENT_HAVE_D_TYPE +# endif +#endif + +#ifdef _WIN32 +# include <tchar.h> +#endif + +/* Convenience functions for verify-high functionality + */ + +/** + * gnutls_x509_trust_list_add_trust_mem: + * @list: The list + * @cas: A buffer containing a list of CAs (optional) + * @crls: A buffer containing a list of CRLs (optional) + * @type: The format of the certificates + * @tl_flags: flags from %gnutls_trust_list_flags_t + * @tl_vflags: gnutls_certificate_verify_flags if flags specifies GNUTLS_TL_VERIFY_CRL + * + * This function will add the given certificate authorities + * to the trusted list. + * + * If this function is used gnutls_x509_trust_list_deinit() must be called + * with parameter @all being 1. + * + * Returns: The number of added elements is returned. + * + * Since: 3.1 + **/ +int +gnutls_x509_trust_list_add_trust_mem(gnutls_x509_trust_list_t list, + const gnutls_datum_t * cas, + const gnutls_datum_t * crls, + gnutls_x509_crt_fmt_t type, + unsigned int tl_flags, + unsigned int tl_vflags) +{ + int ret; + gnutls_x509_crt_t *x509_ca_list = NULL; + gnutls_x509_crl_t *x509_crl_list = NULL; + unsigned int x509_ncas, x509_ncrls; + unsigned int r = 0; + + /* When adding CAs or CRLs, we use the GNUTLS_TL_NO_DUPLICATES flag to ensure + * that unaccounted certificates/CRLs are deinitialized. */ + + if (cas != NULL && cas->data != NULL) { + ret = + gnutls_x509_crt_list_import2(&x509_ca_list, &x509_ncas, + cas, type, 0); + if (ret < 0) + return gnutls_assert_val(ret); + + ret = + gnutls_x509_trust_list_add_cas(list, x509_ca_list, + x509_ncas, tl_flags|GNUTLS_TL_NO_DUPLICATES); + gnutls_free(x509_ca_list); + + if (ret < 0) + return gnutls_assert_val(ret); + else + r += ret; + } + + if (crls != NULL && crls->data != NULL) { + ret = + gnutls_x509_crl_list_import2(&x509_crl_list, + &x509_ncrls, crls, type, + 0); + if (ret < 0) + return gnutls_assert_val(ret); + + ret = + gnutls_x509_trust_list_add_crls(list, x509_crl_list, + x509_ncrls, tl_flags|GNUTLS_TL_NO_DUPLICATES, + tl_vflags); + gnutls_free(x509_crl_list); + + if (ret < 0) + return gnutls_assert_val(ret); + else + r += ret; + } + + return r; +} + +/** + * gnutls_x509_trust_list_remove_trust_mem: + * @list: The list + * @cas: A buffer containing a list of CAs (optional) + * @type: The format of the certificates + * + * This function will remove the provided certificate authorities + * from the trusted list, and add them into a black list when needed. + * + * See also gnutls_x509_trust_list_remove_cas(). + * + * Returns: The number of removed elements is returned. + * + * Since: 3.1.10 + **/ +int +gnutls_x509_trust_list_remove_trust_mem(gnutls_x509_trust_list_t list, + const gnutls_datum_t * cas, + gnutls_x509_crt_fmt_t type) +{ + int ret; + gnutls_x509_crt_t *x509_ca_list = NULL; + unsigned int x509_ncas; + unsigned int r = 0, i; + + if (cas != NULL && cas->data != NULL) { + ret = + gnutls_x509_crt_list_import2(&x509_ca_list, &x509_ncas, + cas, type, 0); + if (ret < 0) + return gnutls_assert_val(ret); + + ret = + gnutls_x509_trust_list_remove_cas(list, x509_ca_list, + x509_ncas); + + for (i = 0; i < x509_ncas; i++) + gnutls_x509_crt_deinit(x509_ca_list[i]); + gnutls_free(x509_ca_list); + + if (ret < 0) + return gnutls_assert_val(ret); + else + r += ret; + } + + return r; +} + +#ifdef ENABLE_PKCS11 +static +int remove_pkcs11_url(gnutls_x509_trust_list_t list, const char *ca_file) +{ + if (strcmp(ca_file, list->pkcs11_token) == 0) { + gnutls_free(list->pkcs11_token); + } + return 0; +} + +/* This function does add a PKCS #11 object URL into trust list. The + * CA certificates are imported directly, rather than using it as a + * trusted PKCS#11 token. + */ +static +int add_trust_list_pkcs11_object_url(gnutls_x509_trust_list_t list, const char *url, unsigned flags) +{ + gnutls_x509_crt_t *xcrt_list = NULL; + gnutls_pkcs11_obj_t *pcrt_list = NULL; + unsigned int pcrt_list_size = 0, i; + int ret; + + /* here we don't use the flag GNUTLS_PKCS11_OBJ_FLAG_PRESENT_IN_TRUSTED_MODULE, + * as we want to explicitly load from any module available in the system. + */ + ret = + gnutls_pkcs11_obj_list_import_url2(&pcrt_list, &pcrt_list_size, + url, + GNUTLS_PKCS11_OBJ_FLAG_CRT|GNUTLS_PKCS11_OBJ_FLAG_MARK_TRUSTED, + 0); + if (ret < 0) + return gnutls_assert_val(ret); + + if (pcrt_list_size == 0) { + ret = 0; + goto cleanup; + } + + xcrt_list = _gnutls_reallocarray(NULL, pcrt_list_size, + sizeof(gnutls_x509_crt_t)); + if (xcrt_list == NULL) { + ret = GNUTLS_E_MEMORY_ERROR; + goto cleanup; + } + + ret = + gnutls_x509_crt_list_import_pkcs11(xcrt_list, pcrt_list_size, + pcrt_list, 0); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = + gnutls_x509_trust_list_add_cas(list, xcrt_list, pcrt_list_size, + flags); + + cleanup: + for (i = 0; i < pcrt_list_size; i++) + gnutls_pkcs11_obj_deinit(pcrt_list[i]); + gnutls_free(pcrt_list); + gnutls_free(xcrt_list); + + return ret; +} + +static +int remove_pkcs11_object_url(gnutls_x509_trust_list_t list, const char *url) +{ + gnutls_x509_crt_t *xcrt_list = NULL; + gnutls_pkcs11_obj_t *pcrt_list = NULL; + unsigned int pcrt_list_size = 0, i; + int ret; + + ret = + gnutls_pkcs11_obj_list_import_url2(&pcrt_list, &pcrt_list_size, + url, + GNUTLS_PKCS11_OBJ_FLAG_CRT|GNUTLS_PKCS11_OBJ_FLAG_MARK_TRUSTED, + 0); + if (ret < 0) + return gnutls_assert_val(ret); + + if (pcrt_list_size == 0) { + ret = 0; + goto cleanup; + } + + xcrt_list = _gnutls_reallocarray(NULL, pcrt_list_size, + sizeof(gnutls_x509_crt_t)); + if (xcrt_list == NULL) { + ret = GNUTLS_E_MEMORY_ERROR; + goto cleanup; + } + + ret = + gnutls_x509_crt_list_import_pkcs11(xcrt_list, pcrt_list_size, + pcrt_list, 0); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = + gnutls_x509_trust_list_remove_cas(list, xcrt_list, pcrt_list_size); + + cleanup: + for (i = 0; i < pcrt_list_size; i++) { + gnutls_pkcs11_obj_deinit(pcrt_list[i]); + if (xcrt_list) + gnutls_x509_crt_deinit(xcrt_list[i]); + } + gnutls_free(pcrt_list); + gnutls_free(xcrt_list); + + return ret; +} +#endif + + +/** + * gnutls_x509_trust_list_add_trust_file: + * @list: The list + * @ca_file: A file containing a list of CAs (optional) + * @crl_file: A file containing a list of CRLs (optional) + * @type: The format of the certificates + * @tl_flags: flags from %gnutls_trust_list_flags_t + * @tl_vflags: gnutls_certificate_verify_flags if flags specifies GNUTLS_TL_VERIFY_CRL + * + * This function will add the given certificate authorities + * to the trusted list. PKCS #11 URLs are also accepted, instead + * of files, by this function. A PKCS #11 URL implies a trust + * database (a specially marked module in p11-kit); the URL "pkcs11:" + * implies all trust databases in the system. Only a single URL specifying + * trust databases can be set; they cannot be stacked with multiple calls. + * + * Returns: The number of added elements is returned. + * + * Since: 3.1 + **/ +int +gnutls_x509_trust_list_add_trust_file(gnutls_x509_trust_list_t list, + const char *ca_file, + const char *crl_file, + gnutls_x509_crt_fmt_t type, + unsigned int tl_flags, + unsigned int tl_vflags) +{ + gnutls_datum_t cas = { NULL, 0 }; + gnutls_datum_t crls = { NULL, 0 }; + size_t size; + int ret; + + if (ca_file != NULL) { +#ifdef ENABLE_PKCS11 + if (c_strncasecmp(ca_file, PKCS11_URL, PKCS11_URL_SIZE) == 0) { + unsigned pcrt_list_size = 0; + + /* in case of a token URL import it as a PKCS #11 token, + * otherwise import the individual certificates. + */ + if (is_pkcs11_url_object(ca_file) != 0) { + return add_trust_list_pkcs11_object_url(list, ca_file, tl_flags); + } else { /* trusted token */ + if (list->pkcs11_token != NULL) + return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + list->pkcs11_token = gnutls_strdup(ca_file); + + /* enumerate the certificates */ + ret = gnutls_pkcs11_obj_list_import_url(NULL, &pcrt_list_size, + ca_file, + (GNUTLS_PKCS11_OBJ_FLAG_PRESENT_IN_TRUSTED_MODULE|GNUTLS_PKCS11_OBJ_FLAG_CRT|GNUTLS_PKCS11_OBJ_FLAG_MARK_CA|GNUTLS_PKCS11_OBJ_FLAG_MARK_TRUSTED), + 0); + if (ret < 0 && ret != GNUTLS_E_SHORT_MEMORY_BUFFER) + return gnutls_assert_val(ret); + + return pcrt_list_size; + } + } else +#endif + { + cas.data = (void *) read_file(ca_file, RF_BINARY, &size); + if (cas.data == NULL) { + gnutls_assert(); + return GNUTLS_E_FILE_ERROR; + } + cas.size = size; + } + } + + if (crl_file) { + crls.data = (void *) read_file(crl_file, RF_BINARY, &size); + if (crls.data == NULL) { + gnutls_assert(); + return GNUTLS_E_FILE_ERROR; + } + crls.size = size; + } + + ret = + gnutls_x509_trust_list_add_trust_mem(list, &cas, &crls, type, + tl_flags, tl_vflags); + free(crls.data); + free(cas.data); + + return ret; +} + +static +int load_dir_certs(const char *dirname, + gnutls_x509_trust_list_t list, + unsigned int tl_flags, unsigned int tl_vflags, + unsigned type, unsigned crl) +{ + int ret; + int r = 0; + char path[GNUTLS_PATH_MAX]; + +#if !defined(_WIN32) || !defined(_UNICODE) + DIR *dirp; + struct dirent *d; + + dirp = opendir(dirname); + if (dirp != NULL) { + while ((d = readdir(dirp)) != NULL) { +#ifdef _DIRENT_HAVE_D_TYPE + if (d->d_type == DT_REG || d->d_type == DT_LNK || d->d_type == DT_UNKNOWN) +#endif + { + snprintf(path, sizeof(path), "%s/%s", + dirname, d->d_name); + + if (crl != 0) { + ret = + gnutls_x509_trust_list_add_trust_file + (list, NULL, path, type, tl_flags, + tl_vflags); + } else { + ret = + gnutls_x509_trust_list_add_trust_file + (list, path, NULL, type, tl_flags, + tl_vflags); + } + if (ret >= 0) + r += ret; + } + } + closedir(dirp); + } +#else /* _WIN32 */ + + _TDIR *dirp; + struct _tdirent *d; + gnutls_datum_t utf16 = {NULL, 0}; + +#ifdef WORDS_BIGENDIAN + r = _gnutls_utf8_to_ucs2(dirname, strlen(dirname), &utf16, 1); +#else + r = _gnutls_utf8_to_ucs2(dirname, strlen(dirname), &utf16, 0); +#endif + if (r < 0) + return gnutls_assert_val(r); + dirp = _topendir((_TCHAR*)utf16.data); + gnutls_free(utf16.data); + if (dirp != NULL) { + while ((d = _treaddir(dirp)) != NULL) { +#ifdef _DIRENT_HAVE_D_TYPE + if (d->d_type == DT_REG || d->d_type == DT_LNK || d->d_type == DT_UNKNOWN) +#endif + { + snprintf(path, sizeof(path), "%s/%ls", + dirname, d->d_name); + + if (crl != 0) { + ret = + gnutls_x509_trust_list_add_trust_file + (list, NULL, path, type, tl_flags, + tl_vflags); + } else { + ret = + gnutls_x509_trust_list_add_trust_file + (list, path, NULL, type, tl_flags, + tl_vflags); + } + if (ret >= 0) + r += ret; + } + } + _tclosedir(dirp); + } +#endif /* _WIN32 */ + return r; +} + +/** + * gnutls_x509_trust_list_add_trust_dir: + * @list: The list + * @ca_dir: A directory containing the CAs (optional) + * @crl_dir: A directory containing a list of CRLs (optional) + * @type: The format of the certificates + * @tl_flags: flags from %gnutls_trust_list_flags_t + * @tl_vflags: gnutls_certificate_verify_flags if flags specifies GNUTLS_TL_VERIFY_CRL + * + * This function will add the given certificate authorities + * to the trusted list. Only directories are accepted by + * this function. + * + * Returns: The number of added elements is returned. + * + * Since: 3.3.6 + **/ +int +gnutls_x509_trust_list_add_trust_dir(gnutls_x509_trust_list_t list, + const char *ca_dir, + const char *crl_dir, + gnutls_x509_crt_fmt_t type, + unsigned int tl_flags, + unsigned int tl_vflags) +{ + int ret = 0; + + if (ca_dir != NULL) { + int r = 0; + r = load_dir_certs(ca_dir, list, tl_flags, tl_vflags, type, 0); + + if (r >= 0) + ret += r; + } + + if (crl_dir) { + int r = 0; + r = load_dir_certs(crl_dir, list, tl_flags, tl_vflags, type, 1); + + if (r >= 0) + ret += r; + } + + return ret; +} + +/** + * gnutls_x509_trust_list_remove_trust_file: + * @list: The list + * @ca_file: A file containing a list of CAs + * @type: The format of the certificates + * + * This function will remove the given certificate authorities + * from the trusted list, and add them into a black list when needed. + * PKCS 11 URLs are also accepted, instead + * of files, by this function. + * + * See also gnutls_x509_trust_list_remove_cas(). + * + * Returns: The number of added elements is returned. + * + * Since: 3.1.10 + **/ +int +gnutls_x509_trust_list_remove_trust_file(gnutls_x509_trust_list_t list, + const char *ca_file, + gnutls_x509_crt_fmt_t type) +{ + gnutls_datum_t cas = { NULL, 0 }; + size_t size; + int ret; + +#ifdef ENABLE_PKCS11 + if (c_strncasecmp(ca_file, PKCS11_URL, PKCS11_URL_SIZE) == 0) { + if (is_pkcs11_url_object(ca_file) != 0) { + return remove_pkcs11_object_url(list, ca_file); + } else { /* token */ + return remove_pkcs11_url(list, ca_file); + } + } else +#endif + { + cas.data = (void *) read_file(ca_file, RF_BINARY, &size); + if (cas.data == NULL) { + gnutls_assert(); + return GNUTLS_E_FILE_ERROR; + } + cas.size = size; + } + + ret = gnutls_x509_trust_list_remove_trust_mem(list, &cas, type); + free(cas.data); + + return ret; +} diff --git a/lib/x509/verify.c b/lib/x509/verify.c new file mode 100644 index 0000000..c7e35f7 --- /dev/null +++ b/lib/x509/verify.c @@ -0,0 +1,1752 @@ +/* + * Copyright (C) 2003-2014 Free Software Foundation, Inc. + * Copyright (C) 2013 Nikos Mavrogiannopoulos + * Copyright (C) 2014 Red Hat + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GnuTLS. + * + * The GnuTLS is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/> + * + */ + +/* All functions which relate to X.509 certificate verification stuff are + * included here + */ + +#include "gnutls_int.h" +#include "errors.h" +#include <libtasn1.h> +#include <global.h> +#include <num.h> /* MAX */ +#include <tls-sig.h> +#include <str.h> +#include <datum.h> +#include <pkcs11_int.h> +#include <x509_int.h> +#include <common.h> +#include <pk.h> +#include <x509/verify-high.h> +#include "supported_exts.h" +#include "profiles.h" + +/* Checks if two certs have the same name and the same key. Return 1 on match. + * If @is_ca is zero then this function is identical to gnutls_x509_crt_equals() + */ +unsigned +_gnutls_check_if_same_key(gnutls_x509_crt_t cert1, + gnutls_x509_crt_t cert2, + unsigned is_ca) +{ + int ret; + unsigned result; + + if (is_ca == 0) + return gnutls_x509_crt_equals(cert1, cert2); + + ret = _gnutls_is_same_dn(cert1, cert2); + if (ret == 0) + return 0; + + if (cert1->raw_spki.size > 0 && (cert1->raw_spki.size == cert2->raw_spki.size) && + (memcmp(cert1->raw_spki.data, cert2->raw_spki.data, cert1->raw_spki.size) == 0)) + result = 1; + else + result = 0; + + return result; +} + +unsigned +_gnutls_check_if_same_key2(gnutls_x509_crt_t cert1, + gnutls_datum_t * cert2bin) +{ + int ret; + gnutls_x509_crt_t cert2; + + ret = gnutls_x509_crt_init(&cert2); + if (ret < 0) + return gnutls_assert_val(0); + + ret = gnutls_x509_crt_import(cert2, cert2bin, GNUTLS_X509_FMT_DER); + if (ret < 0) { + gnutls_x509_crt_deinit(cert2); + return gnutls_assert_val(0); + } + + ret = _gnutls_check_if_same_key(cert1, cert2, 1); + + gnutls_x509_crt_deinit(cert2); + return ret; +} + +/* checks whether there are present unknown/unsupported critical extensions. + * + * Returns true if they are present. + */ +static unsigned check_for_unknown_exts(gnutls_x509_crt_t cert) +{ + unsigned i; + char oid[MAX_OID_SIZE]; + size_t oid_size; + unsigned critical; + int ret; + + for (i=0;;i++) { + oid_size = sizeof(oid); + oid[0] = 0; + critical = 0; + + ret = gnutls_x509_crt_get_extension_info(cert, i, oid, &oid_size, &critical); + if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { + return 0; + } else if (ret < 0) { + gnutls_assert(); + /* could not decode? */ + _gnutls_debug_log("Could not decode extension %d\n", i); + return 1; + } + + if (critical == 0) + continue; + + if (is_ext_oid_supported(oid, oid_size) == NULL) { + gnutls_assert(); + _gnutls_debug_log("Unsupported critical extension: %s\n", oid); + return 1; + } + } + + return 0; +} + +/* Checks if the issuer of a certificate is a + * Certificate Authority, or if the certificate is the same + * as the issuer (and therefore it doesn't need to be a CA). + * + * Returns true or false, if the issuer is a CA, + * or not. + */ +static unsigned +check_if_ca(gnutls_x509_crt_t cert, gnutls_x509_crt_t issuer, + unsigned int *max_path, unsigned int flags) +{ + gnutls_datum_t cert_signed_data = { NULL, 0 }; + gnutls_datum_t issuer_signed_data = { NULL, 0 }; + gnutls_datum_t cert_signature = { NULL, 0 }; + gnutls_datum_t issuer_signature = { NULL, 0 }; + int pathlen = -1, ret; + unsigned result; + unsigned int ca_status = 0; + + /* Check if the issuer is the same with the + * certificate. This is added in order for trusted + * certificates to be able to verify themselves. + */ + + ret = + _gnutls_x509_get_signed_data(issuer->cert, &issuer->der, "tbsCertificate", + &issuer_signed_data); + if (ret < 0) { + gnutls_assert(); + goto fail; + } + + ret = + _gnutls_x509_get_signed_data(cert->cert, &cert->der, "tbsCertificate", + &cert_signed_data); + if (ret < 0) { + gnutls_assert(); + goto fail; + } + + ret = + _gnutls_x509_get_signature(issuer->cert, "signature", + &issuer_signature); + if (ret < 0) { + gnutls_assert(); + goto fail; + } + + ret = + _gnutls_x509_get_signature(cert->cert, "signature", + &cert_signature); + if (ret < 0) { + gnutls_assert(); + goto fail; + } + + /* If the subject certificate is the same as the issuer + * return true. + */ + if (!(flags & GNUTLS_VERIFY_DO_NOT_ALLOW_SAME)) + if (cert_signed_data.size == issuer_signed_data.size) { + if ((memcmp + (cert_signed_data.data, + issuer_signed_data.data, + cert_signed_data.size) == 0) + && (cert_signature.size == + issuer_signature.size) + && + (memcmp + (cert_signature.data, issuer_signature.data, + cert_signature.size) == 0)) { + result = 1; + goto cleanup; + } + } + + ret = + gnutls_x509_crt_get_basic_constraints(issuer, NULL, &ca_status, + &pathlen); + if (ret < 0) { + ca_status = 0; + pathlen = -1; + } + + if (ca_status != 0 && pathlen != -1) { + if ((unsigned) pathlen < *max_path) + *max_path = pathlen; + } + + if (ca_status != 0) { + result = 1; + goto cleanup; + } + /* Handle V1 CAs that do not have a basicConstraint, but accept + these certs only if the appropriate flags are set. */ + else if ((ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) && + ((flags & GNUTLS_VERIFY_ALLOW_ANY_X509_V1_CA_CRT) || + (!(flags & GNUTLS_VERIFY_DO_NOT_ALLOW_X509_V1_CA_CRT) && + (gnutls_x509_crt_check_issuer(issuer, issuer) != 0)))) { + gnutls_assert(); + result = 1; + goto cleanup; + } else { + gnutls_assert(); + } + + fail: + result = 0; + + cleanup: + _gnutls_free_datum(&cert_signed_data); + _gnutls_free_datum(&issuer_signed_data); + _gnutls_free_datum(&cert_signature); + _gnutls_free_datum(&issuer_signature); + return result; +} + + +/* This function checks if cert's issuer is issuer. + * This does a straight (DER) compare of the issuer/subject DN fields in + * the given certificates, as well as check the authority key ID. + * + * Returns 1 if they match and (0) if they don't match. + */ +static unsigned is_issuer(gnutls_x509_crt_t cert, gnutls_x509_crt_t issuer) +{ + uint8_t id1[MAX_KEY_ID_SIZE]; + uint8_t id2[MAX_KEY_ID_SIZE]; + size_t id1_size; + size_t id2_size; + int ret; + unsigned result; + + if (_gnutls_x509_compare_raw_dn + (&cert->raw_issuer_dn, &issuer->raw_dn) != 0) + result = 1; + else + result = 0; + + if (result != 0) { + /* check if the authority key identifier matches the subject key identifier + * of the issuer */ + id1_size = sizeof(id1); + + ret = + gnutls_x509_crt_get_authority_key_id(cert, id1, + &id1_size, NULL); + if (ret < 0) { + /* If there is no authority key identifier in the + * certificate, assume they match */ + result = 1; + goto cleanup; + } + + id2_size = sizeof(id2); + ret = + gnutls_x509_crt_get_subject_key_id(issuer, id2, + &id2_size, NULL); + if (ret < 0) { + /* If there is no subject key identifier in the + * issuer certificate, assume they match */ + result = 1; + gnutls_assert(); + goto cleanup; + } + + if (id1_size == id2_size + && memcmp(id1, id2, id1_size) == 0) + result = 1; + else + result = 0; + } + + cleanup: + return result; +} + +/* Check if the given certificate is the issuer of the CRL. + * Returns 1 on success and 0 otherwise. + */ +static unsigned is_crl_issuer(gnutls_x509_crl_t crl, gnutls_x509_crt_t issuer) +{ + if (_gnutls_x509_compare_raw_dn + (&crl->raw_issuer_dn, &issuer->raw_dn) != 0) + return 1; + else + return 0; +} + +/* Checks if the DN of two certificates is the same. + * Returns 1 if they match and (0) if they don't match. Otherwise + * a negative error code is returned to indicate error. + */ +unsigned _gnutls_is_same_dn(gnutls_x509_crt_t cert1, gnutls_x509_crt_t cert2) +{ + if (_gnutls_x509_compare_raw_dn(&cert1->raw_dn, &cert2->raw_dn) != + 0) + return 1; + else + return 0; +} + +/* Finds an issuer of the certificate. If multiple issuers + * are present, returns one that is activated and not expired. + */ +static inline gnutls_x509_crt_t +find_issuer(gnutls_x509_crt_t cert, + const gnutls_x509_crt_t * trusted_cas, int tcas_size) +{ + int i; + gnutls_x509_crt_t issuer = NULL; + + /* this is serial search. + */ + for (i = 0; i < tcas_size; i++) { + if (is_issuer(cert, trusted_cas[i]) != 0) { + if (issuer == NULL) { + issuer = trusted_cas[i]; + } else { + time_t now = gnutls_time(0); + + if (now < + gnutls_x509_crt_get_expiration_time + (trusted_cas[i]) + && now >= + gnutls_x509_crt_get_activation_time + (trusted_cas[i])) { + issuer = trusted_cas[i]; + } + } + } + } + + return issuer; +} + +static unsigned int check_time_status(gnutls_x509_crt_t crt, time_t now) +{ + int status = 0; + time_t t; + + t = gnutls_x509_crt_get_activation_time(crt); + if (t == (time_t) - 1 || now < t) { + status |= GNUTLS_CERT_NOT_ACTIVATED; + status |= GNUTLS_CERT_INVALID; + return status; + } + + t = gnutls_x509_crt_get_expiration_time(crt); + if (t == (time_t) - 1 || now > t) { + status |= GNUTLS_CERT_EXPIRED; + status |= GNUTLS_CERT_INVALID; + return status; + } + + return 0; +} + +unsigned _gnutls_is_broken_sig_allowed(const gnutls_sign_entry_st *se, unsigned int flags) +{ + gnutls_digest_algorithm_t hash; + + /* we have a catch all */ + if ((flags & GNUTLS_VERIFY_ALLOW_BROKEN) == GNUTLS_VERIFY_ALLOW_BROKEN) + return 1; + + /* the first two are for backwards compatibility */ + if ((se->id == GNUTLS_SIGN_RSA_MD2) + && (flags & GNUTLS_VERIFY_ALLOW_SIGN_RSA_MD2)) + return 1; + if ((se->id == GNUTLS_SIGN_RSA_MD5) + && (flags & GNUTLS_VERIFY_ALLOW_SIGN_RSA_MD5)) + return 1; + + hash = se->hash; + if (hash == GNUTLS_DIG_SHA1 && (flags & GNUTLS_VERIFY_ALLOW_SIGN_WITH_SHA1)) + return 1; + + return 0; +} + +#define CASE_SEC_PARAM(profile, level) \ + case profile: \ + sym_bits = gnutls_sec_param_to_symmetric_bits(level); \ + se = _gnutls_sign_to_entry(sigalg); \ + if (unlikely(se == NULL)) { \ + _gnutls_cert_log("cert", crt); \ + _gnutls_debug_log(#level": certificate's signature algorithm is unknown\n"); \ + return gnutls_assert_val(0); \ + } \ + if (unlikely(se->hash == GNUTLS_DIG_UNKNOWN)) { \ + _gnutls_cert_log("cert", crt); \ + _gnutls_debug_log(#level": certificate's signature hash is unknown\n"); \ + return gnutls_assert_val(0); \ + } \ + if (!trusted && \ + _gnutls_sign_get_hash_strength(sigalg) < sym_bits) { \ + _gnutls_cert_log("cert", crt); \ + _gnutls_debug_log(#level": certificate's signature hash strength is unacceptable (is %u bits, needed %u)\n", _gnutls_sign_get_hash_strength(sigalg), sym_bits); \ + return gnutls_assert_val(0); \ + } \ + sp = gnutls_pk_bits_to_sec_param(pkalg, bits); \ + if (sp < level) { \ + _gnutls_cert_log("cert", crt); \ + _gnutls_debug_log(#level": certificate's security level is unacceptable\n"); \ + return gnutls_assert_val(0); \ + } \ + if (issuer) { \ + sp = gnutls_pk_bits_to_sec_param(issuer_pkalg, issuer_bits); \ + if (sp < level) { \ + _gnutls_cert_log("issuer", issuer); \ + _gnutls_debug_log(#level": certificate's issuer security level is unacceptable\n"); \ + return gnutls_assert_val(0); \ + } \ + } \ + break; + +/* Checks whether the provided certificates are acceptable + * according to verification profile specified. + * + * @crt: a certificate + * @issuer: the certificates issuer (allowed to be NULL) + * @sigalg: the signature algorithm used + * @trusted: whether @crt is treated as trusted (e.g., present in the system + * trust list); if it is true, the check on signature algorithm will + * be skipped + * @flags: the specified verification flags + */ +static unsigned is_level_acceptable( + gnutls_x509_crt_t crt, gnutls_x509_crt_t issuer, + gnutls_sign_algorithm_t sigalg, bool trusted, + unsigned flags) +{ + gnutls_certificate_verification_profiles_t profile = GNUTLS_VFLAGS_TO_PROFILE(flags); + int issuer_pkalg = 0, pkalg, ret; + unsigned bits = 0, issuer_bits = 0, sym_bits = 0; + gnutls_pk_params_st params; + gnutls_sec_param_t sp; + const gnutls_sign_entry_st *se; + gnutls_certificate_verification_profiles_t min_profile; + + min_profile = _gnutls_get_system_wide_verification_profile(); + + if (min_profile) { + if (profile < min_profile) { + gnutls_assert(); + profile = min_profile; + } + } + + if (profile == GNUTLS_PROFILE_UNKNOWN) { + return 1; + } + + pkalg = gnutls_x509_crt_get_pk_algorithm(crt, &bits); + if (pkalg < 0) + return gnutls_assert_val(0); + + if (issuer) { + issuer_pkalg = gnutls_x509_crt_get_pk_algorithm(issuer, &issuer_bits); + if (issuer_pkalg < 0) + return gnutls_assert_val(0); + } + + switch (profile) { + CASE_SEC_PARAM(GNUTLS_PROFILE_VERY_WEAK, GNUTLS_SEC_PARAM_VERY_WEAK); + CASE_SEC_PARAM(GNUTLS_PROFILE_LOW, GNUTLS_SEC_PARAM_LOW); + CASE_SEC_PARAM(GNUTLS_PROFILE_LEGACY, GNUTLS_SEC_PARAM_LEGACY); + CASE_SEC_PARAM(GNUTLS_PROFILE_MEDIUM, GNUTLS_SEC_PARAM_MEDIUM); + CASE_SEC_PARAM(GNUTLS_PROFILE_HIGH, GNUTLS_SEC_PARAM_HIGH); + CASE_SEC_PARAM(GNUTLS_PROFILE_ULTRA, GNUTLS_SEC_PARAM_ULTRA); + CASE_SEC_PARAM(GNUTLS_PROFILE_FUTURE, GNUTLS_SEC_PARAM_FUTURE); + case GNUTLS_PROFILE_SUITEB128: + case GNUTLS_PROFILE_SUITEB192: { + unsigned curve, issuer_curve; + + /* check suiteB params validity: rfc5759 */ + + if (gnutls_x509_crt_get_version(crt) != 3) { + _gnutls_debug_log("SUITEB: certificate uses an unacceptable version number\n"); + return gnutls_assert_val(0); + } + + if (sigalg != GNUTLS_SIGN_ECDSA_SHA256 && sigalg != GNUTLS_SIGN_ECDSA_SHA384) { + _gnutls_debug_log("SUITEB: certificate is not signed using ECDSA-SHA256 or ECDSA-SHA384\n"); + return gnutls_assert_val(0); + } + + if (pkalg != GNUTLS_PK_EC) { + _gnutls_debug_log("SUITEB: certificate does not contain ECC parameters\n"); + return gnutls_assert_val(0); + } + + if (issuer_pkalg != GNUTLS_PK_EC) { + _gnutls_debug_log("SUITEB: certificate's issuer does not have ECC parameters\n"); + return gnutls_assert_val(0); + } + + ret = _gnutls_x509_crt_get_mpis(crt, ¶ms); + if (ret < 0) { + _gnutls_debug_log("SUITEB: cannot read certificate params\n"); + return gnutls_assert_val(0); + } + + curve = params.curve; + gnutls_pk_params_release(¶ms); + + if (curve != GNUTLS_ECC_CURVE_SECP256R1 && + curve != GNUTLS_ECC_CURVE_SECP384R1) { + _gnutls_debug_log("SUITEB: certificate's ECC params do not contain SECP256R1 or SECP384R1\n"); + return gnutls_assert_val(0); + } + + if (profile == GNUTLS_PROFILE_SUITEB192) { + if (curve != GNUTLS_ECC_CURVE_SECP384R1) { + _gnutls_debug_log("SUITEB192: certificate does not use SECP384R1\n"); + return gnutls_assert_val(0); + } + } + + if (issuer != NULL) { + if (gnutls_x509_crt_get_version(issuer) != 3) { + _gnutls_debug_log("SUITEB: certificate's issuer uses an unacceptable version number\n"); + return gnutls_assert_val(0); + } + + ret = _gnutls_x509_crt_get_mpis(issuer, ¶ms); + if (ret < 0) { + _gnutls_debug_log("SUITEB: cannot read certificate params\n"); + return gnutls_assert_val(0); + } + + issuer_curve = params.curve; + gnutls_pk_params_release(¶ms); + + if (issuer_curve != GNUTLS_ECC_CURVE_SECP256R1 && + issuer_curve != GNUTLS_ECC_CURVE_SECP384R1) { + _gnutls_debug_log("SUITEB: certificate's issuer ECC params do not contain SECP256R1 or SECP384R1\n"); + return gnutls_assert_val(0); + } + + if (issuer_curve < curve) { + _gnutls_debug_log("SUITEB: certificate's issuer ECC params are weaker than the certificate's\n"); + return gnutls_assert_val(0); + } + + if (sigalg == GNUTLS_SIGN_ECDSA_SHA256 && + issuer_curve == GNUTLS_ECC_CURVE_SECP384R1) { + _gnutls_debug_log("SUITEB: certificate is signed with ECDSA-SHA256 when using SECP384R1\n"); + return gnutls_assert_val(0); + } + } + + break; + case GNUTLS_PROFILE_UNKNOWN: /* already checked; avoid compiler warnings */ + _gnutls_debug_log("An unknown profile (%d) was encountered\n", (int)profile); + } + } + + return 1; +} + +typedef struct verify_state_st { + time_t now; + unsigned int max_path; + gnutls_x509_name_constraints_t nc; + gnutls_x509_tlsfeatures_t tls_feat; + gnutls_verify_output_function *func; +} verify_state_st; + +#define MARK_INVALID(x) { gnutls_assert(); \ + out |= (x|GNUTLS_CERT_INVALID); \ + result = 0; } + +static int _gnutls_x509_verify_data(gnutls_sign_algorithm_t sign, + const gnutls_datum_t * data, + const gnutls_datum_t * signature, + gnutls_x509_crt_t cert, + gnutls_x509_crt_t issuer, + unsigned vflags); + +/* + * Verifies the given certificate against a certificate list of + * trusted CAs. + * + * Returns only 0 or 1. If 1 it means that the certificate + * was successfully verified. + * + * 'flags': an OR of the gnutls_certificate_verify_flags enumeration. + * + * Output will hold some extra information about the verification + * procedure. + */ +static unsigned verify_crt(gnutls_x509_trust_list_t tlist, + gnutls_x509_crt_t cert, + const gnutls_x509_crt_t * trusted_cas, + int tcas_size, unsigned int flags, + unsigned int *output, + verify_state_st *vparams, + unsigned end_cert) +{ + gnutls_datum_t cert_signed_data = { NULL, 0 }; + gnutls_datum_t cert_signature = { NULL, 0 }; + gnutls_x509_crt_t issuer = NULL; + int issuer_version; + unsigned result = 1; + unsigned int out = 0, usage; + int sigalg, ret; + const gnutls_sign_entry_st *se; + + if (output) + *output = 0; + + if (vparams->max_path == 0) { + MARK_INVALID(GNUTLS_CERT_SIGNER_CONSTRAINTS_FAILURE); + /* bail immediately, to avoid inconistency */ + goto cleanup; + } + vparams->max_path--; + + if (tcas_size >= 1) + issuer = find_issuer(cert, trusted_cas, tcas_size); + + ret = + _gnutls_x509_get_signed_data(cert->cert, &cert->der, "tbsCertificate", + &cert_signed_data); + if (ret < 0) { + MARK_INVALID(0); + cert_signed_data.data = NULL; + } + + ret = + _gnutls_x509_get_signature(cert->cert, "signature", + &cert_signature); + if (ret < 0) { + MARK_INVALID(0); + cert_signature.data = NULL; + } + + ret = + _gnutls_x509_get_signature_algorithm(cert->cert, + "signatureAlgorithm"); + if (ret < 0) { + MARK_INVALID(0); + } + sigalg = ret; + + se = _gnutls_sign_to_entry(sigalg); + + /* issuer is not in trusted certificate + * authorities. + */ + if (issuer == NULL) { + MARK_INVALID(GNUTLS_CERT_SIGNER_NOT_FOUND); + } else { + if (vparams->nc != NULL) { + /* append the issuer's constraints */ + ret = gnutls_x509_crt_get_name_constraints(issuer, vparams->nc, + GNUTLS_NAME_CONSTRAINTS_FLAG_APPEND, NULL); + if (ret < 0 && ret != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { + MARK_INVALID(GNUTLS_CERT_SIGNER_CONSTRAINTS_FAILURE); + goto nc_done; + } + + /* only check name constraints in server certificates, not CAs */ + if (end_cert != 0) { + ret = gnutls_x509_name_constraints_check_crt(vparams->nc, GNUTLS_SAN_DNSNAME, cert); + if (ret == 0) { + MARK_INVALID(GNUTLS_CERT_SIGNER_CONSTRAINTS_FAILURE); + goto nc_done; + } + + ret = gnutls_x509_name_constraints_check_crt(vparams->nc, GNUTLS_SAN_RFC822NAME, cert); + if (ret == 0) { + MARK_INVALID(GNUTLS_CERT_SIGNER_CONSTRAINTS_FAILURE); + goto nc_done; + } + + ret = gnutls_x509_name_constraints_check_crt(vparams->nc, GNUTLS_SAN_DN, cert); + if (ret == 0) { + MARK_INVALID(GNUTLS_CERT_SIGNER_CONSTRAINTS_FAILURE); + goto nc_done; + } + + ret = gnutls_x509_name_constraints_check_crt(vparams->nc, GNUTLS_SAN_URI, cert); + if (ret == 0) { + MARK_INVALID(GNUTLS_CERT_SIGNER_CONSTRAINTS_FAILURE); + goto nc_done; + } + + ret = gnutls_x509_name_constraints_check_crt(vparams->nc, GNUTLS_SAN_IPADDRESS, cert); + if (ret == 0) { + MARK_INVALID(GNUTLS_CERT_SIGNER_CONSTRAINTS_FAILURE); + goto nc_done; + } + } + } + + nc_done: + if (vparams->tls_feat != NULL) { + /* append the issuer's constraints */ + ret = gnutls_x509_crt_get_tlsfeatures(issuer, vparams->tls_feat, GNUTLS_EXT_FLAG_APPEND, NULL); + if (ret < 0 && ret != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { + MARK_INVALID(GNUTLS_CERT_SIGNER_CONSTRAINTS_FAILURE); + goto feat_done; + } + + ret = gnutls_x509_tlsfeatures_check_crt(vparams->tls_feat, cert); + if (ret == 0) { + MARK_INVALID(GNUTLS_CERT_SIGNER_CONSTRAINTS_FAILURE); + goto feat_done; + } + } + + feat_done: + issuer_version = gnutls_x509_crt_get_version(issuer); + + if (issuer_version < 0) { + MARK_INVALID(0); + } else if (!(flags & GNUTLS_VERIFY_DISABLE_CA_SIGN) && + ((flags & GNUTLS_VERIFY_DO_NOT_ALLOW_X509_V1_CA_CRT) + || issuer_version != 1)) { + if (check_if_ca(cert, issuer, &vparams->max_path, flags) != 1) { + MARK_INVALID(GNUTLS_CERT_SIGNER_NOT_CA); + } + + ret = + gnutls_x509_crt_get_key_usage(issuer, &usage, NULL); + if (ret != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { + if (ret < 0) { + MARK_INVALID(0); + } else if (!(usage & GNUTLS_KEY_KEY_CERT_SIGN)) { + MARK_INVALID(GNUTLS_CERT_SIGNER_CONSTRAINTS_FAILURE); + } + } + } + + if (sigalg < 0) { + MARK_INVALID(0); + } else if (cert_signed_data.data != NULL && + cert_signature.data != NULL) { + ret = + _gnutls_x509_verify_data(sigalg, + &cert_signed_data, + &cert_signature, + cert, + issuer, flags); + + if (ret == GNUTLS_E_PK_SIG_VERIFY_FAILED) { + MARK_INVALID(GNUTLS_CERT_SIGNATURE_FAILURE); + } else if (ret == GNUTLS_E_CONSTRAINT_ERROR) { + MARK_INVALID(GNUTLS_CERT_SIGNER_CONSTRAINTS_FAILURE); + } else if (ret < 0) { + MARK_INVALID(0); + } + } + } + + /* we always check the issuer for unsupported critical extensions */ + if (issuer && check_for_unknown_exts(issuer) != 0) { + if (!(flags & GNUTLS_VERIFY_IGNORE_UNKNOWN_CRIT_EXTENSIONS)) { + MARK_INVALID(GNUTLS_CERT_UNKNOWN_CRIT_EXTENSIONS); + } + } + + /* we only check the end-certificate for critical extensions; that + * way do not perform this check twice on the certificates when + * verifying a large list */ + if (end_cert && check_for_unknown_exts(cert) != 0) { + if (!(flags & GNUTLS_VERIFY_IGNORE_UNKNOWN_CRIT_EXTENSIONS)) { + MARK_INVALID(GNUTLS_CERT_UNKNOWN_CRIT_EXTENSIONS); + } + } + + if (sigalg >= 0 && se) { + if (is_level_acceptable(cert, issuer, sigalg, false, flags) == 0) { + MARK_INVALID(GNUTLS_CERT_INSECURE_ALGORITHM); + } + + /* If the certificate is not self signed check if the algorithms + * used are secure. If the certificate is self signed it doesn't + * really matter. + */ + if (_gnutls_sign_is_secure2(se, GNUTLS_SIGN_FLAG_SECURE_FOR_CERTS) == 0 && + _gnutls_is_broken_sig_allowed(se, flags) == 0 && + is_issuer(cert, cert) == 0) { + MARK_INVALID(GNUTLS_CERT_INSECURE_ALGORITHM); + } + } + + /* Check activation/expiration times + */ + if (!(flags & GNUTLS_VERIFY_DISABLE_TIME_CHECKS)) { + /* check the time of the issuer first */ + if (issuer != NULL && + !(flags & GNUTLS_VERIFY_DISABLE_TRUSTED_TIME_CHECKS)) { + out |= check_time_status(issuer, vparams->now); + if (out != 0) { + gnutls_assert(); + result = 0; + } + } + + out |= check_time_status(cert, vparams->now); + if (out != 0) { + gnutls_assert(); + result = 0; + } + } + + cleanup: + if (output) + *output |= out; + + if (vparams->func) { + if (result == 0) { + out |= GNUTLS_CERT_INVALID; + } + vparams->func(cert, issuer, NULL, out); + } + _gnutls_free_datum(&cert_signed_data); + _gnutls_free_datum(&cert_signature); + + return result; +} + +/** + * gnutls_x509_crt_check_issuer: + * @cert: is the certificate to be checked + * @issuer: is the certificate of a possible issuer + * + * This function will check if the given certificate was issued by the + * given issuer. It checks the DN fields and the authority + * key identifier and subject key identifier fields match. + * + * If the same certificate is provided at the @cert and @issuer fields, + * it will check whether the certificate is self-signed. + * + * Returns: It will return true (1) if the given certificate is issued + * by the given issuer, and false (0) if not. + **/ +unsigned +gnutls_x509_crt_check_issuer(gnutls_x509_crt_t cert, + gnutls_x509_crt_t issuer) +{ + return is_issuer(cert, issuer); +} + +static +unsigned check_ca_sanity(const gnutls_x509_crt_t issuer, + time_t now, unsigned int flags) +{ + unsigned int status = 0; + unsigned sigalg; + int ret; + + /* explicit time check for trusted CA that we remove from + * list. GNUTLS_VERIFY_DISABLE_TRUSTED_TIME_CHECKS + */ + if (!(flags & GNUTLS_VERIFY_DISABLE_TRUSTED_TIME_CHECKS) && + !(flags & GNUTLS_VERIFY_DISABLE_TIME_CHECKS)) { + status |= check_time_status(issuer, now); + } + + ret = + _gnutls_x509_get_signature_algorithm(issuer->cert, "signatureAlgorithm"); + sigalg = ret; + + /* we explicitly allow CAs which we do not support their self-algorithms + * to pass. */ + if (ret >= 0 && !is_level_acceptable(issuer, NULL, sigalg, true, flags)) { + status |= GNUTLS_CERT_INSECURE_ALGORITHM|GNUTLS_CERT_INVALID; + } + + return status; + +} + +/* Verify X.509 certificate chain. + * + * Note that the return value is an OR of GNUTLS_CERT_* elements. + * + * This function verifies a X.509 certificate list. The certificate + * list should lead to a trusted certificate in order to be trusted. + */ +unsigned int +_gnutls_verify_crt_status(gnutls_x509_trust_list_t tlist, + const gnutls_x509_crt_t * certificate_list, + int clist_size, + const gnutls_x509_crt_t * trusted_cas, + int tcas_size, + unsigned int flags, + const char *purpose, + gnutls_verify_output_function func) +{ + int i = 0, ret; + unsigned int status = 0, output; + time_t now = gnutls_time(0); + verify_state_st vparams; + + if (clist_size > 1) { + /* Check if the last certificate in the path is self signed. + * In that case ignore it (a certificate is trusted only if it + * leads to a trusted party by us, not the server's). + * + * This prevents from verifying self signed certificates against + * themselves. This (although not bad) caused verification + * failures on some root self signed certificates that use the + * MD2 algorithm. + */ + if (gnutls_x509_crt_check_issuer + (certificate_list[clist_size - 1], + certificate_list[clist_size - 1]) != 0) { + clist_size--; + } + } + + /* We want to shorten the chain by removing the cert that matches + * one of the certs we trust and all the certs after that i.e. if + * cert chain is A signed-by B signed-by C signed-by D (signed-by + * self-signed E but already removed above), and we trust B, remove + * B, C and D. */ + if (!(flags & GNUTLS_VERIFY_DO_NOT_ALLOW_SAME)) + i = 0; /* also replace the first one */ + else + i = 1; /* do not replace the first one */ + + for (; i < clist_size; i++) { + int j; + + for (j = 0; j < tcas_size; j++) { + /* we check for a certificate that may not be identical with the one + * sent by the client, but will have the same name and key. That is + * because it can happen that a CA certificate is upgraded from intermediate + * CA to self-signed CA at some point. */ + if (_gnutls_check_if_same_key + (certificate_list[i], trusted_cas[j], i) != 0) { + + status |= check_ca_sanity(trusted_cas[j], now, flags); + + if (func) + func(certificate_list[i], + trusted_cas[j], NULL, status); + + if (status != 0) { + return gnutls_assert_val(status); + } + + clist_size = i; + break; + } + } + /* clist_size may have been changed which gets out of loop */ + } + + if (clist_size == 0) { + /* The certificate is already present in the trusted certificate list. + * Nothing to verify. */ + return status; + } + + memset(&vparams, 0, sizeof(vparams)); + vparams.now = now; + vparams.max_path = MAX_VERIFY_DEPTH; + vparams.func = func; + + ret = gnutls_x509_name_constraints_init(&vparams.nc); + if (ret < 0) { + gnutls_assert(); + status |= GNUTLS_CERT_INVALID; + return status; + } + + ret = gnutls_x509_tlsfeatures_init(&vparams.tls_feat); + if (ret < 0) { + gnutls_assert(); + status |= GNUTLS_CERT_INVALID; + goto cleanup; + } + + /* Verify the last certificate in the certificate path + * against the trusted CA certificate list. + * + * If no CAs are present returns CERT_INVALID. Thus works + * in self signed etc certificates. + */ + output = 0; + + ret = verify_crt(tlist, + certificate_list[clist_size - 1], + trusted_cas, tcas_size, flags, + &output, + &vparams, + clist_size==1?1:0); + if (ret != 1) { + /* if the last certificate in the certificate + * list is invalid, then the certificate is not + * trusted. + */ + gnutls_assert(); + status |= output; + status |= GNUTLS_CERT_INVALID; + goto cleanup; + } + + /* Verify the certificate path (chain) + */ + for (i = clist_size - 1; i > 0; i--) { + output = 0; + + if (purpose != NULL) { + ret = _gnutls_check_key_purpose(certificate_list[i], purpose, 1); + if (ret != 1) { + gnutls_assert(); + status |= GNUTLS_CERT_INVALID; + status |= GNUTLS_CERT_PURPOSE_MISMATCH; + + if (func) + func(certificate_list[i-1], + certificate_list[i], NULL, status); + goto cleanup; + } + } + + /* note that here we disable this V1 CA flag. So that no version 1 + * certificates can exist in a supplied chain. + */ + if (!(flags & GNUTLS_VERIFY_ALLOW_ANY_X509_V1_CA_CRT)) { + flags |= GNUTLS_VERIFY_DO_NOT_ALLOW_X509_V1_CA_CRT; + } + + if (!verify_crt(tlist, + certificate_list[i - 1], + &certificate_list[i], 1, + flags, &output, + &vparams, + i==1?1:0)) { + gnutls_assert(); + status |= output; + status |= GNUTLS_CERT_INVALID; + goto cleanup; + } + } + +cleanup: + gnutls_x509_name_constraints_deinit(vparams.nc); + gnutls_x509_tlsfeatures_deinit(vparams.tls_feat); + return status; +} + + +#define PURPOSE_NSSGC "2.16.840.1.113730.4.1" +#define PURPOSE_VSGC "2.16.840.1.113733.1.8.1" + +/* Returns true if the provided purpose is in accordance with the certificate. + */ +unsigned _gnutls_check_key_purpose(gnutls_x509_crt_t cert, const char *purpose, unsigned no_any) +{ + char oid[MAX_OID_SIZE]; + size_t oid_size; + int ret; + unsigned critical = 0; + unsigned check_obsolete_oids = 0; + unsigned i; + + /* The check_obsolete_oids hack is because of certain very old CA certificates + * around which instead of having the GNUTLS_KP_TLS_WWW_SERVER have some old + * OIDs for that purpose. Assume these OIDs equal GNUTLS_KP_TLS_WWW_SERVER in + * CA certs */ + if (strcmp(purpose, GNUTLS_KP_TLS_WWW_SERVER) == 0) { + unsigned ca_status; + ret = + gnutls_x509_crt_get_basic_constraints(cert, NULL, &ca_status, + NULL); + if (ret < 0) + ca_status = 0; + + if (ca_status) + check_obsolete_oids = 1; + } + + for (i=0;;i++) { + oid_size = sizeof(oid); + ret = gnutls_x509_crt_get_key_purpose_oid(cert, i, oid, &oid_size, &critical); + if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { + if (i==0) { + /* no key purpose in certificate, assume ANY */ + return 1; + } else { + gnutls_assert(); + break; + } + } else if (ret < 0) { + gnutls_assert(); + break; + } + + if (check_obsolete_oids) { + if (strcmp(oid, PURPOSE_NSSGC) == 0) { + return 1; + } else if (strcmp(oid, PURPOSE_VSGC) == 0) { + return 1; + } + } + + if (strcmp(oid, purpose) == 0 || (no_any == 0 && strcmp(oid, GNUTLS_KP_ANY) == 0)) { + return 1; + } + _gnutls_debug_log("looking for key purpose '%s', but have '%s'\n", purpose, oid); + } + return 0; +} + +#ifdef ENABLE_PKCS11 +/* Verify X.509 certificate chain using a PKCS #11 token. + * + * Note that the return value is an OR of GNUTLS_CERT_* elements. + * + * Unlike the non-PKCS#11 version, this function accepts a key purpose + * (from GNUTLS_KP_...). That is because in the p11-kit trust modules + * anchors are mixed and get assigned a purpose. + * + * This function verifies a X.509 certificate list. The certificate + * list should lead to a trusted certificate in order to be trusted. + */ +unsigned int +_gnutls_pkcs11_verify_crt_status(gnutls_x509_trust_list_t tlist, + const char* url, + const gnutls_x509_crt_t * certificate_list, + unsigned clist_size, + const char *purpose, + unsigned int flags, + gnutls_verify_output_function func) +{ + int ret; + unsigned int status = 0, i; + gnutls_x509_crt_t issuer = NULL; + gnutls_datum_t raw_issuer = {NULL, 0}; + time_t now = gnutls_time(0); + + if (clist_size > 1) { + /* Check if the last certificate in the path is self signed. + * In that case ignore it (a certificate is trusted only if it + * leads to a trusted party by us, not the server's). + * + * This prevents from verifying self signed certificates against + * themselves. This (although not bad) caused verification + * failures on some root self signed certificates that use the + * MD2 algorithm. + */ + if (gnutls_x509_crt_check_issuer + (certificate_list[clist_size - 1], + certificate_list[clist_size - 1]) != 0) { + clist_size--; + } + } + + /* We want to shorten the chain by removing the cert that matches + * one of the certs we trust and all the certs after that i.e. if + * cert chain is A signed-by B signed-by C signed-by D (signed-by + * self-signed E but already removed above), and we trust B, remove + * B, C and D. */ + if (!(flags & GNUTLS_VERIFY_DO_NOT_ALLOW_SAME)) + i = 0; /* also replace the first one */ + else + i = 1; /* do not replace the first one */ + + for (; i < clist_size; i++) { + unsigned vflags; + gnutls_x509_crt_t trusted_cert; + + if (i == 0) /* in the end certificate do full comparison */ + vflags = GNUTLS_PKCS11_OBJ_FLAG_PRESENT_IN_TRUSTED_MODULE| + GNUTLS_PKCS11_OBJ_FLAG_COMPARE|GNUTLS_PKCS11_OBJ_FLAG_RETRIEVE_TRUSTED; + else + vflags = GNUTLS_PKCS11_OBJ_FLAG_PRESENT_IN_TRUSTED_MODULE| + GNUTLS_PKCS11_OBJ_FLAG_COMPARE_KEY|GNUTLS_PKCS11_OBJ_FLAG_RETRIEVE_TRUSTED; + + if (_gnutls_pkcs11_crt_is_known (url, certificate_list[i], vflags, &trusted_cert) != 0) { + + status |= check_ca_sanity(trusted_cert, now, flags); + + if (func) + func(trusted_cert, + certificate_list[i], NULL, status); + + gnutls_x509_crt_deinit(trusted_cert); + + if (status != 0) { + return gnutls_assert_val(status); + } + + clist_size = i; + break; + } + /* clist_size may have been changed which gets out of loop */ + } + + if (clist_size == 0) { + /* The certificate is already present in the trusted certificate list. + * Nothing to verify. */ + return status; + } + + /* check for blacklists */ + for (i = 0; i < clist_size; i++) { + if (gnutls_pkcs11_crt_is_known (url, certificate_list[i], + GNUTLS_PKCS11_OBJ_FLAG_PRESENT_IN_TRUSTED_MODULE| + GNUTLS_PKCS11_OBJ_FLAG_RETRIEVE_DISTRUSTED) != 0) { + status |= GNUTLS_CERT_INVALID; + status |= GNUTLS_CERT_REVOKED; + if (func) + func(certificate_list[i], certificate_list[i], NULL, status); + goto cleanup; + } + } + + /* check against issuer */ + ret = gnutls_pkcs11_get_raw_issuer(url, certificate_list[clist_size - 1], + &raw_issuer, GNUTLS_X509_FMT_DER, + GNUTLS_PKCS11_OBJ_FLAG_OVERWRITE_TRUSTMOD_EXT|GNUTLS_PKCS11_OBJ_FLAG_PRESENT_IN_TRUSTED_MODULE); + if (ret < 0) { + gnutls_assert(); + if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE && clist_size > 2) { + + /* check if the last certificate in the chain is present + * in our trusted list, and if yes, verify against it. */ + ret = gnutls_pkcs11_crt_is_known(url, certificate_list[clist_size - 1], + GNUTLS_PKCS11_OBJ_FLAG_RETRIEVE_TRUSTED|GNUTLS_PKCS11_OBJ_FLAG_COMPARE); + if (ret != 0) { + return _gnutls_verify_crt_status(tlist, + certificate_list, clist_size, + &certificate_list[clist_size - 1], + 1, flags, purpose, func); + } + } + + status |= GNUTLS_CERT_INVALID; + status |= GNUTLS_CERT_SIGNER_NOT_FOUND; + /* verify the certificate list against 0 trusted CAs in order + * to get, any additional flags from the certificate list (e.g., + * insecure algorithms or expired */ + status |= _gnutls_verify_crt_status(tlist, certificate_list, clist_size, + NULL, 0, flags, purpose, func); + goto cleanup; + } + + ret = gnutls_x509_crt_init(&issuer); + if (ret < 0) { + gnutls_assert(); + status |= GNUTLS_CERT_INVALID; + status |= GNUTLS_CERT_SIGNER_NOT_FOUND; + goto cleanup; + } + + ret = gnutls_x509_crt_import(issuer, &raw_issuer, GNUTLS_X509_FMT_DER); + if (ret < 0) { + gnutls_assert(); + status |= GNUTLS_CERT_INVALID; + status |= GNUTLS_CERT_SIGNER_NOT_FOUND; + goto cleanup; + } + + /* check if the raw issuer is blacklisted (it can happen if + * the issuer is both in the trusted list and the blacklisted) + */ + if (gnutls_pkcs11_crt_is_known (url, issuer, + GNUTLS_PKCS11_OBJ_FLAG_PRESENT_IN_TRUSTED_MODULE| + GNUTLS_PKCS11_OBJ_FLAG_RETRIEVE_DISTRUSTED) != 0) { + status |= GNUTLS_CERT_INVALID; + status |= GNUTLS_CERT_SIGNER_NOT_FOUND; /* if the signer is revoked - it is as if it doesn't exist */ + goto cleanup; + } + + /* security modules that provide trust, bundle all certificates (of all purposes) + * together. In software that doesn't specify any purpose assume the default to + * be www-server. */ + ret = _gnutls_check_key_purpose(issuer, purpose==NULL?GNUTLS_KP_TLS_WWW_SERVER:purpose, 0); + if (ret != 1) { + gnutls_assert(); + status |= GNUTLS_CERT_INVALID; + status |= GNUTLS_CERT_SIGNER_NOT_FOUND; + goto cleanup; + } + + status = _gnutls_verify_crt_status(tlist, certificate_list, clist_size, + &issuer, 1, flags, purpose, func); + +cleanup: + gnutls_free(raw_issuer.data); + if (issuer != NULL) + gnutls_x509_crt_deinit(issuer); + + return status; +} +#endif + +static int +_gnutls_x509_validate_sign_params(gnutls_pk_algorithm_t pk_algorithm, + asn1_node cert, + const char *name, + gnutls_x509_spki_st *sig_params) +{ + /* The signature parameter validation is only needed for RSA-PSS */ + if (pk_algorithm == GNUTLS_PK_RSA_PSS) { + int result; + gnutls_x509_spki_st params; + + result = _gnutls_x509_read_sign_params(cert, name, ¶ms); + if (result < 0) { + /* If parameters field is absent, no parameter + * validation is needed */ + if (result != GNUTLS_E_ASN1_ELEMENT_NOT_FOUND && + result != GNUTLS_E_ASN1_VALUE_NOT_FOUND) { + gnutls_assert(); + return result; + } + } else { + /* Check if the underlying hash algorithms are same. */ + if (sig_params->rsa_pss_dig != params.rsa_pss_dig) { + gnutls_assert(); + return GNUTLS_E_CONSTRAINT_ERROR; + } + + /* The salt length used to generate the + * signature must be equal to or larger than + * the one in the key parameter. */ + if (sig_params->salt_size < params.salt_size) { + gnutls_assert(); + return GNUTLS_E_CONSTRAINT_ERROR; + } + } + } + return 0; +} + +/* verifies if the certificate is properly signed. + * returns GNUTLS_E_PK_VERIFY_SIG_FAILED on failure and 1 on success. + * + * 'data' is the signed data + * 'signature' is the signature! + */ +static int +_gnutls_x509_verify_data(gnutls_sign_algorithm_t sign, + const gnutls_datum_t * data, + const gnutls_datum_t * signature, + gnutls_x509_crt_t cert, + gnutls_x509_crt_t issuer, + unsigned vflags) +{ + gnutls_pk_params_st params; + gnutls_pk_algorithm_t issuer_pk; + int ret; + gnutls_x509_spki_st sign_params; + const gnutls_sign_entry_st *se; + + /* Read the MPI parameters from the issuer's certificate. + */ + ret = _gnutls_x509_crt_get_mpis(issuer, ¶ms); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + issuer_pk = gnutls_x509_crt_get_pk_algorithm(issuer, NULL); + + se = _gnutls_sign_to_entry(sign); + if (se == NULL) + return gnutls_assert_val(GNUTLS_E_UNSUPPORTED_SIGNATURE_ALGORITHM); + + if (cert != NULL) { + ret = _gnutls_x509_read_sign_params(cert->cert, + "signatureAlgorithm", + &sign_params); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = _gnutls_x509_validate_sign_params(issuer_pk, + issuer->cert, + "tbsCertificate." + "subjectPublicKeyInfo." + "algorithm", + &sign_params); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + } else { + memcpy(&sign_params, ¶ms.spki, + sizeof(gnutls_x509_spki_st)); + + sign_params.pk = se->pk; + if (sign_params.pk == GNUTLS_PK_RSA_PSS) + sign_params.rsa_pss_dig = se->hash; + } + + ret = pubkey_verify_data(se, hash_to_entry(se->hash), data, signature, ¶ms, + &sign_params, vflags); + if (ret < 0) { + gnutls_assert(); + } + + cleanup: + /* release all allocated MPIs + */ + gnutls_pk_params_release(¶ms); + + return ret; +} + +/** + * gnutls_x509_crt_list_verify: + * @cert_list: is the certificate list to be verified + * @cert_list_length: holds the number of certificate in cert_list + * @CA_list: is the CA list which will be used in verification + * @CA_list_length: holds the number of CA certificate in CA_list + * @CRL_list: holds a list of CRLs. + * @CRL_list_length: the length of CRL list. + * @flags: Flags that may be used to change the verification algorithm. Use OR of the gnutls_certificate_verify_flags enumerations. + * @verify: will hold the certificate verification output. + * + * + * This function will try to verify the given certificate list and + * return its status. The details of the verification are the same + * as in gnutls_x509_trust_list_verify_crt2(). + * + * You must check the peer's name in order to check if the verified + * certificate belongs to the actual peer. + * + * The certificate verification output will be put in @verify and will + * be one or more of the gnutls_certificate_status_t enumerated + * elements bitwise or'd. For a more detailed verification status use + * gnutls_x509_crt_verify() per list element. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int +gnutls_x509_crt_list_verify(const gnutls_x509_crt_t * cert_list, + unsigned cert_list_length, + const gnutls_x509_crt_t * CA_list, + unsigned CA_list_length, + const gnutls_x509_crl_t * CRL_list, + unsigned CRL_list_length, unsigned int flags, + unsigned int *verify) +{ + unsigned i; + int ret; + gnutls_x509_trust_list_t tlist; + + if (cert_list == NULL || cert_list_length == 0) + return GNUTLS_E_NO_CERTIFICATE_FOUND; + + gnutls_x509_trust_list_init(&tlist, 0); + + /* Verify certificate + */ + *verify = _gnutls_verify_crt_status(tlist, cert_list, cert_list_length, + CA_list, CA_list_length, + flags, NULL, NULL); + + /* Check for revoked certificates in the chain. + */ + for (i = 0; i < cert_list_length; i++) { + ret = gnutls_x509_crt_check_revocation(cert_list[i], + CRL_list, + CRL_list_length); + if (ret == 1) { /* revoked */ + *verify |= GNUTLS_CERT_REVOKED; + *verify |= GNUTLS_CERT_INVALID; + } + } + + gnutls_x509_trust_list_deinit(tlist, 0); + return 0; +} + +/** + * gnutls_x509_crt_verify: + * @cert: is the certificate to be verified + * @CA_list: is one certificate that is considered to be trusted one + * @CA_list_length: holds the number of CA certificate in CA_list + * @flags: Flags that may be used to change the verification algorithm. Use OR of the gnutls_certificate_verify_flags enumerations. + * @verify: will hold the certificate verification output. + * + * This function will try to verify the given certificate and return + * its status. Note that a verification error does not imply a negative + * return status. In that case the @verify status is set. + * + * The details of the verification are the same + * as in gnutls_x509_trust_list_verify_crt2(). + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int +gnutls_x509_crt_verify(gnutls_x509_crt_t cert, + const gnutls_x509_crt_t * CA_list, + unsigned CA_list_length, unsigned int flags, + unsigned int *verify) +{ + gnutls_x509_trust_list_t tlist; + + gnutls_x509_trust_list_init(&tlist, 0); + + /* Verify certificate + */ + *verify = _gnutls_verify_crt_status(tlist, &cert, 1, + CA_list, CA_list_length, + flags, NULL, NULL); + + gnutls_x509_trust_list_deinit(tlist, 0); + return 0; +} + +/** + * gnutls_x509_crl_check_issuer: + * @crl: is the CRL to be checked + * @issuer: is the certificate of a possible issuer + * + * This function will check if the given CRL was issued by the given + * issuer certificate. + * + * Returns: true (1) if the given CRL was issued by the given issuer, + * and false (0) if not. + **/ +unsigned +gnutls_x509_crl_check_issuer(gnutls_x509_crl_t crl, + gnutls_x509_crt_t issuer) +{ + return is_crl_issuer(crl, issuer); +} + +static inline gnutls_x509_crt_t +find_crl_issuer(gnutls_x509_crl_t crl, + const gnutls_x509_crt_t * trusted_cas, int tcas_size) +{ + int i; + + /* this is serial search. + */ + + for (i = 0; i < tcas_size; i++) { + if (is_crl_issuer(crl, trusted_cas[i]) != 0) + return trusted_cas[i]; + } + + gnutls_assert(); + return NULL; +} + +/** + * gnutls_x509_crl_verify: + * @crl: is the crl to be verified + * @trusted_cas: is a certificate list that is considered to be trusted one + * @tcas_size: holds the number of CA certificates in CA_list + * @flags: Flags that may be used to change the verification algorithm. Use OR of the gnutls_certificate_verify_flags enumerations. + * @verify: will hold the crl verification output. + * + * This function will try to verify the given crl and return its verification status. + * See gnutls_x509_crt_list_verify() for a detailed description of + * return values. Note that since GnuTLS 3.1.4 this function includes + * the time checks. + * + * Note that value in @verify is set only when the return value of this + * function is success (i.e, failure to trust a CRL a certificate does not imply + * a negative return value). + * + * Before GnuTLS 3.5.7 this function would return zero or a positive + * number on success. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0), otherwise a + * negative error value. + **/ +int +gnutls_x509_crl_verify(gnutls_x509_crl_t crl, + const gnutls_x509_crt_t * trusted_cas, + unsigned tcas_size, unsigned int flags, + unsigned int *verify) +{ +/* CRL is ignored for now */ + gnutls_datum_t crl_signed_data = { NULL, 0 }; + gnutls_datum_t crl_signature = { NULL, 0 }; + gnutls_x509_crt_t issuer = NULL; + int result, sigalg; + time_t now = gnutls_time(0); + time_t nextu; + unsigned int usage; + + if (verify) + *verify = 0; + + if (tcas_size >= 1) + issuer = find_crl_issuer(crl, trusted_cas, tcas_size); + + result = + _gnutls_x509_get_signed_data(crl->crl, &crl->der, "tbsCertList", + &crl_signed_data); + if (result < 0) { + gnutls_assert(); + if (verify) + *verify |= GNUTLS_CERT_INVALID; + goto cleanup; + } + + result = + _gnutls_x509_get_signature(crl->crl, "signature", + &crl_signature); + if (result < 0) { + gnutls_assert(); + if (verify) + *verify |= GNUTLS_CERT_INVALID; + goto cleanup; + } + + sigalg = + _gnutls_x509_get_signature_algorithm(crl->crl, + "signatureAlgorithm"); + if (sigalg < 0) { + gnutls_assert(); + if (verify) + *verify |= GNUTLS_CERT_INVALID; + goto cleanup; + } + + /* issuer is not in trusted certificate + * authorities. + */ + if (issuer == NULL) { + gnutls_assert(); + if (verify) + *verify |= + GNUTLS_CERT_SIGNER_NOT_FOUND | + GNUTLS_CERT_INVALID; + } else { + if (!(flags & GNUTLS_VERIFY_DISABLE_CA_SIGN)) { + if (gnutls_x509_crt_get_ca_status(issuer, NULL) != 1) { + gnutls_assert(); + if (verify) + *verify |= + GNUTLS_CERT_SIGNER_NOT_CA | + GNUTLS_CERT_INVALID; + } + + result = + gnutls_x509_crt_get_key_usage(issuer, &usage, NULL); + if (result != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { + if (result < 0) { + gnutls_assert(); + if (verify) + *verify |= GNUTLS_CERT_INVALID; + } else if (!(usage & GNUTLS_KEY_CRL_SIGN)) { + gnutls_assert(); + if (verify) + *verify |= + GNUTLS_CERT_SIGNER_CONSTRAINTS_FAILURE + | GNUTLS_CERT_INVALID; + } + } + } + + result = + _gnutls_x509_verify_data(sigalg, + &crl_signed_data, &crl_signature, + NULL, + issuer, flags); + if (result == GNUTLS_E_PK_SIG_VERIFY_FAILED) { + gnutls_assert(); + /* error. ignore it */ + if (verify) + *verify |= GNUTLS_CERT_SIGNATURE_FAILURE; + result = 0; + } else if (result == GNUTLS_E_CONSTRAINT_ERROR) { + if (verify) + *verify |= GNUTLS_CERT_SIGNER_CONSTRAINTS_FAILURE; + result = 0; + } else if (result < 0) { + gnutls_assert(); + if (verify) + *verify |= GNUTLS_CERT_INVALID; + goto cleanup; + } else { + result = 0; /* everything ok */ + } + } + + { + sigalg = gnutls_x509_crl_get_signature_algorithm(crl); + + if (((sigalg == GNUTLS_SIGN_RSA_MD2) && + !(flags & GNUTLS_VERIFY_ALLOW_SIGN_RSA_MD2)) || + ((sigalg == GNUTLS_SIGN_RSA_MD5) && + !(flags & GNUTLS_VERIFY_ALLOW_SIGN_RSA_MD5))) { + if (verify) + *verify |= GNUTLS_CERT_INSECURE_ALGORITHM; + result = 0; + } + } + + if (gnutls_x509_crl_get_this_update(crl) > now && verify) + *verify |= GNUTLS_CERT_REVOCATION_DATA_ISSUED_IN_FUTURE; + + nextu = gnutls_x509_crl_get_next_update(crl); + if (nextu != -1 && nextu < now && verify) + *verify |= GNUTLS_CERT_REVOCATION_DATA_SUPERSEDED; + + + cleanup: + if (verify && *verify != 0) + *verify |= GNUTLS_CERT_INVALID; + + _gnutls_free_datum(&crl_signed_data); + _gnutls_free_datum(&crl_signature); + + return result; +} diff --git a/lib/x509/virt-san.c b/lib/x509/virt-san.c new file mode 100644 index 0000000..e62bd4a --- /dev/null +++ b/lib/x509/virt-san.c @@ -0,0 +1,191 @@ +/* + * Copyright (C) 2015-2016 Nikos Mavrogiannopoulos + * Copyright (C) 2015-2016 Red Hat, Inc. + * + * This file is part of GnuTLS. + * + * The GnuTLS is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/> + * + */ + +/* This file contains functions to handle the virtual subject alternative names, + * based on othernames, such as GNUTLS_SAN_OTHERNAME_XMPP. + */ + +#include "gnutls_int.h" +#include "x509_int.h" +#include "x509_ext_int.h" +#include "common.h" +#include "krb5.h" +#include "virt-san.h" + +static +int san_othername_to_virtual(const char *oid, size_t size) +{ + if (oid) { + if ((unsigned) size == (sizeof(XMPP_OID)-1) + && memcmp(oid, XMPP_OID, sizeof(XMPP_OID)-1) == 0) + return GNUTLS_SAN_OTHERNAME_XMPP; + else if ((unsigned) size == (sizeof(KRB5_PRINCIPAL_OID)-1) + && memcmp(oid, KRB5_PRINCIPAL_OID, sizeof(KRB5_PRINCIPAL_OID)-1) == 0) + return GNUTLS_SAN_OTHERNAME_KRB5PRINCIPAL; + else if ((unsigned) size == (sizeof(MSUSER_PRINCIPAL_NAME_OID)-1) + && memcmp(oid, MSUSER_PRINCIPAL_NAME_OID, sizeof(MSUSER_PRINCIPAL_NAME_OID)-1) == 0) + return GNUTLS_SAN_OTHERNAME_MSUSERPRINCIPAL; + } + + return GNUTLS_SAN_OTHERNAME; +} + +static +const char * virtual_to_othername_oid(unsigned type) +{ + switch(type) { + case GNUTLS_SAN_OTHERNAME_XMPP: + return XMPP_OID; + case GNUTLS_SAN_OTHERNAME_KRB5PRINCIPAL: + return KRB5_PRINCIPAL_OID; + case GNUTLS_SAN_OTHERNAME_MSUSERPRINCIPAL: + return MSUSER_PRINCIPAL_NAME_OID; + default: + return NULL; + } +} + +int _gnutls_alt_name_assign_virt_type(struct name_st *name, unsigned type, gnutls_datum_t *san, const char *othername_oid, unsigned raw) +{ + gnutls_datum_t encoded = {NULL, 0}; + gnutls_datum_t xmpp = {NULL,0}; + int ret; + + if (type < 1000) { + name->type = type; + ret = _gnutls_alt_name_process(&name->san, type, san, raw); + if (ret < 0) + return gnutls_assert_val(ret); + gnutls_free(san->data); + + if (othername_oid) { + name->othername_oid.data = (uint8_t *) othername_oid; + name->othername_oid.size = strlen(othername_oid); + } else { + name->othername_oid.data = NULL; + name->othername_oid.size = 0; + } + } else { /* virtual types */ + const char *oid = virtual_to_othername_oid(type); + + if (oid == NULL) + return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + + switch(type) { + case GNUTLS_SAN_OTHERNAME_XMPP: + + ret = gnutls_idna_map((char*)san->data, san->size, &xmpp, 0); + if (ret < 0) + return gnutls_assert_val(ret); + + ret = _gnutls_x509_encode_string(ASN1_ETYPE_UTF8_STRING, + xmpp.data, xmpp.size, &encoded); + + gnutls_free(xmpp.data); + if (ret < 0) + return gnutls_assert_val(ret); + + name->type = GNUTLS_SAN_OTHERNAME; + name->san.data = encoded.data; + name->san.size = encoded.size; + name->othername_oid.data = (void*)gnutls_strdup(oid); + name->othername_oid.size = strlen(oid); + break; + + case GNUTLS_SAN_OTHERNAME_KRB5PRINCIPAL: + ret = _gnutls_krb5_principal_to_der((char*)san->data, &name->san); + if (ret < 0) + return gnutls_assert_val(ret); + + name->othername_oid.data = (void*)gnutls_strdup(oid); + name->othername_oid.size = strlen(oid); + name->type = GNUTLS_SAN_OTHERNAME; + break; + + default: + return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + } + + gnutls_free(san->data); + } + + return 0; +} + +/** + * gnutls_x509_othername_to_virtual: + * @oid: The othername object identifier + * @othername: The othername data + * @virt_type: GNUTLS_SAN_OTHERNAME_XXX + * @virt: allocated printable data + * + * This function will parse and convert the othername data to a virtual + * type supported by gnutls. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value. + * + * Since: 3.3.8 + **/ +int gnutls_x509_othername_to_virtual(const char *oid, + const gnutls_datum_t *othername, + unsigned int *virt_type, + gnutls_datum_t *virt) +{ + int ret; + unsigned type; + + type = san_othername_to_virtual(oid, strlen(oid)); + if (type == GNUTLS_SAN_OTHERNAME) + return gnutls_assert_val(GNUTLS_E_X509_UNKNOWN_SAN); + + if (virt_type) + *virt_type = type; + + switch(type) { + case GNUTLS_SAN_OTHERNAME_XMPP: + ret = _gnutls_x509_decode_string + (ASN1_ETYPE_UTF8_STRING, othername->data, + othername->size, virt, 0); + if (ret < 0) { + gnutls_assert(); + return ret; + } + return 0; + case GNUTLS_SAN_OTHERNAME_KRB5PRINCIPAL: + ret = _gnutls_krb5_der_to_principal(othername, virt); + if (ret < 0) { + gnutls_assert(); + return ret; + } + return 0; + case GNUTLS_SAN_OTHERNAME_MSUSERPRINCIPAL: + ret = _gnutls_x509_decode_string + (ASN1_ETYPE_UTF8_STRING, othername->data, + othername->size, virt, 0); + if (ret < 0) { + gnutls_assert(); + return ret; + } + return 0; + default: + return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); + } +} diff --git a/lib/x509/virt-san.h b/lib/x509/virt-san.h new file mode 100644 index 0000000..44b6fe6 --- /dev/null +++ b/lib/x509/virt-san.h @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2015 Nikos Mavrogiannopoulos + * Copyright (C) 2015 Red Hat, Inc. + * + * This file is part of GnuTLS. + * + * The GnuTLS is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/> + * + */ + +#ifndef GNUTLS_LIB_X509_VIRT_SAN_H +#define GNUTLS_LIB_X509_VIRT_SAN_H + +#include "x509_ext_int.h" + +int _gnutls_alt_name_assign_virt_type(struct name_st *name, unsigned type, gnutls_datum_t *san, const char *othername_oid, unsigned raw); + +#endif /* GNUTLS_LIB_X509_VIRT_SAN_H */ diff --git a/lib/x509/x509.c b/lib/x509/x509.c new file mode 100644 index 0000000..50dcc8e --- /dev/null +++ b/lib/x509/x509.c @@ -0,0 +1,4689 @@ +/* + * Copyright (C) 2003-2018 Free Software Foundation, Inc. + * Copyright (C) 2018 Red Hat, Inc. + * + * Authors: Nikos Mavrogiannopoulos, Simon Josefsson, Howard Chu + * + * This file is part of GnuTLS. + * + * The GnuTLS is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/> + * + */ + +/* Functions on X.509 Certificate parsing + */ + +#include "gnutls_int.h" +#include <datum.h> +#include <global.h> +#include "errors.h" +#include <common.h> +#include <gnutls/x509-ext.h> +#include <x509.h> +#include <x509_b64.h> +#include <x509_int.h> +#include <libtasn1.h> +#include <pk.h> +#include <pkcs11_int.h> +#include "urls.h" +#include "system-keys.h" +#include "hash.h" +#include "hash-pjw-bare.h" + +static int crt_reinit(gnutls_x509_crt_t crt) +{ + int result; + + _gnutls_free_datum(&crt->der); + crt->raw_dn.size = 0; + crt->raw_issuer_dn.size = 0; + crt->raw_spki.size = 0; + + asn1_delete_structure(&crt->cert); + + result = asn1_create_element(_gnutls_get_pkix(), + "PKIX1.Certificate", + &crt->cert); + if (result != ASN1_SUCCESS) { + result = _gnutls_asn2err(result); + gnutls_assert(); + return result; + } + + gnutls_subject_alt_names_deinit(crt->san); + result = gnutls_subject_alt_names_init(&crt->san); + if (result < 0) { + gnutls_assert(); + return result; + } + + gnutls_subject_alt_names_deinit(crt->ian); + result = gnutls_subject_alt_names_init(&crt->ian); + if (result < 0) { + gnutls_assert(); + return result; + } + + return 0; +} + +/** + * gnutls_x509_crt_equals - This function compares two gnutls_x509_crt_t certificates + * @cert1: The first certificate + * @cert2: The second certificate + * + * This function will compare two X.509 certificate structures. + * + * Returns: On equality non-zero is returned, otherwise zero. + * + * Since: 3.5.0 + **/ +unsigned gnutls_x509_crt_equals(gnutls_x509_crt_t cert1, + gnutls_x509_crt_t cert2) +{ + int ret; + bool result; + + if (cert1->modified == 0 && cert2->modified == 0 && + cert1->raw_dn.size > 0 && cert2->raw_dn.size > 0) { + ret = _gnutls_is_same_dn(cert1, cert2); + if (ret == 0) + return 0; + } + + if (cert1->der.size == 0 || cert2->der.size == 0 || + cert1->modified != 0 || cert2->modified != 0) { + gnutls_datum_t tmp1, tmp2; + + /* on uninitialized or modified certificates, we have to re-encode */ + ret = + gnutls_x509_crt_export2(cert1, GNUTLS_X509_FMT_DER, &tmp1); + if (ret < 0) + return gnutls_assert_val(0); + + ret = + gnutls_x509_crt_export2(cert2, GNUTLS_X509_FMT_DER, &tmp2); + if (ret < 0) { + gnutls_free(tmp1.data); + return gnutls_assert_val(0); + } + + if ((tmp1.size == tmp2.size) && + (memcmp(tmp1.data, tmp2.data, tmp1.size) == 0)) + result = 1; + else + result = 0; + + gnutls_free(tmp1.data); + gnutls_free(tmp2.data); + } else { + if ((cert1->der.size == cert2->der.size) && + (memcmp(cert1->der.data, cert2->der.data, cert1->der.size) == 0)) + result = 1; + else + result = 0; + } + + return result; +} + +/** + * gnutls_x509_crt_equals2 - This function compares a gnutls_x509_crt_t cert with DER data + * @cert1: The first certificate + * @der: A DER encoded certificate + * + * This function will compare an X.509 certificate structures, with DER + * encoded certificate data. + * + * Returns: On equality non-zero is returned, otherwise zero. + * + * Since: 3.5.0 + **/ +unsigned +gnutls_x509_crt_equals2(gnutls_x509_crt_t cert1, + const gnutls_datum_t * der) +{ + bool result; + + if (cert1 == NULL || der == NULL) + return 0; + + if (cert1->der.size == 0 || cert1->modified) { + gnutls_datum_t tmp1; + int ret; + + /* on uninitialized or modified certificates, we have to re-encode */ + ret = + gnutls_x509_crt_export2(cert1, GNUTLS_X509_FMT_DER, &tmp1); + if (ret < 0) + return gnutls_assert_val(0); + + if ((tmp1.size == der->size) && + (memcmp(tmp1.data, der->data, tmp1.size) == 0)) + result = 1; + else + result = 0; + + gnutls_free(tmp1.data); + } else { + if ((cert1->der.size == der->size) && + (memcmp(cert1->der.data, der->data, cert1->der.size) == 0)) + result = 1; + else + result = 0; + } + + return result; +} + +/** + * gnutls_x509_crt_init: + * @cert: A pointer to the type to be initialized + * + * This function will initialize an X.509 certificate structure. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int gnutls_x509_crt_init(gnutls_x509_crt_t * cert) +{ + gnutls_x509_crt_t tmp; + int result; + + FAIL_IF_LIB_ERROR; + + tmp = + gnutls_calloc(1, sizeof(gnutls_x509_crt_int)); + + if (!tmp) + return GNUTLS_E_MEMORY_ERROR; + + result = asn1_create_element(_gnutls_get_pkix(), + "PKIX1.Certificate", &tmp->cert); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + gnutls_free(tmp); + return _gnutls_asn2err(result); + } + + result = gnutls_subject_alt_names_init(&tmp->san); + if (result < 0) { + gnutls_assert(); + asn1_delete_structure(&tmp->cert); + gnutls_free(tmp); + return result; + } + + result = gnutls_subject_alt_names_init(&tmp->ian); + if (result < 0) { + gnutls_assert(); + asn1_delete_structure(&tmp->cert); + gnutls_subject_alt_names_deinit(tmp->san); + gnutls_free(tmp); + return result; + } + + /* If you add anything here, be sure to check if it has to be added + to gnutls_x509_crt_import as well. */ + + *cert = tmp; + + return 0; /* success */ +} + +/*- + * _gnutls_x509_crt_cpy - This function copies a gnutls_x509_crt_t type + * @dest: The data where to copy + * @src: The data to be copied + * @flags: zero or CRT_CPY_FAST + * + * This function will copy an X.509 certificate structure. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + -*/ +int _gnutls_x509_crt_cpy(gnutls_x509_crt_t dest, gnutls_x509_crt_t src) +{ + int ret; + gnutls_datum_t tmp; + unsigned dealloc = 0; + + if (src->der.size == 0 || src->modified) { + ret = + gnutls_x509_crt_export2(src, GNUTLS_X509_FMT_DER, &tmp); + if (ret < 0) + return gnutls_assert_val(ret); + dealloc = 1; + } else { + tmp.data = src->der.data; + tmp.size = src->der.size; + } + + ret = gnutls_x509_crt_import(dest, &tmp, GNUTLS_X509_FMT_DER); + + if (dealloc) { + gnutls_free(tmp.data); + } + + if (ret < 0) + return gnutls_assert_val(ret); + + return 0; +} + +/** + * gnutls_x509_crt_deinit: + * @cert: The data to be deinitialized + * + * This function will deinitialize a certificate structure. + **/ +void gnutls_x509_crt_deinit(gnutls_x509_crt_t cert) +{ + if (!cert) + return; + + if (cert->cert) + asn1_delete_structure(&cert->cert); + gnutls_free(cert->der.data); + gnutls_subject_alt_names_deinit(cert->san); + gnutls_subject_alt_names_deinit(cert->ian); + gnutls_free(cert); +} + +static int compare_sig_algorithm(gnutls_x509_crt_t cert) +{ + int ret, len1, len2, result; + char oid1[MAX_OID_SIZE]; + char oid2[MAX_OID_SIZE]; + gnutls_datum_t sp1 = {NULL, 0}; + gnutls_datum_t sp2 = {NULL, 0}; + unsigned empty1 = 0, empty2 = 0; + + len1 = sizeof(oid1); + result = asn1_read_value(cert->cert, "signatureAlgorithm.algorithm", oid1, &len1); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + len2 = sizeof(oid2); + result = asn1_read_value(cert->cert, "tbsCertificate.signature.algorithm", oid2, &len2); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + if (len1 != len2 || memcmp(oid1, oid2, len1) != 0) { + _gnutls_debug_log("signatureAlgorithm.algorithm differs from tbsCertificate.signature.algorithm: %s, %s\n", + oid1, oid2); + gnutls_assert(); + return GNUTLS_E_CERTIFICATE_ERROR; + } + + /* compare the parameters */ + ret = _gnutls_x509_read_value(cert->cert, "signatureAlgorithm.parameters", &sp1); + if (ret == GNUTLS_E_ASN1_ELEMENT_NOT_FOUND) { + empty1 = 1; + } else if (ret < 0) { + gnutls_assert(); + return ret; + } + + ret = _gnutls_x509_read_value(cert->cert, "tbsCertificate.signature.parameters", &sp2); + if (ret == GNUTLS_E_ASN1_ELEMENT_NOT_FOUND) { + empty2 = 1; + } else if (ret < 0) { + gnutls_assert(); + return ret; + } + + /* handle equally empty parameters with missing parameters */ + if (sp1.size == 2 && memcmp(sp1.data, "\x05\x00", 2) == 0) { + empty1 = 1; + _gnutls_free_datum(&sp1); + } + + if (sp2.size == 2 && memcmp(sp2.data, "\x05\x00", 2) == 0) { + empty2 = 1; + _gnutls_free_datum(&sp2); + } + + if (empty1 != empty2 || + sp1.size != sp2.size || + (sp1.size > 0 && memcmp(sp1.data, sp2.data, sp1.size) != 0)) { + gnutls_assert(); + ret = GNUTLS_E_CERTIFICATE_ERROR; + goto cleanup; + } + + ret = 0; + cleanup: + _gnutls_free_datum(&sp1); + _gnutls_free_datum(&sp2); + return ret; +} + +static int cache_alt_names(gnutls_x509_crt_t cert) +{ + gnutls_datum_t tmpder = {NULL, 0}; + int ret; + + /* pre-parse subject alt name */ + ret = _gnutls_x509_crt_get_extension(cert, "2.5.29.17", 0, &tmpder, NULL); + if (ret < 0 && ret != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { + gnutls_free(tmpder.data); + return gnutls_assert_val(ret); + } + + if (ret >= 0) { + ret = gnutls_x509_ext_import_subject_alt_names(&tmpder, cert->san, 0); + gnutls_free(tmpder.data); + if (ret < 0) + return gnutls_assert_val(ret); + } + + ret = _gnutls_x509_crt_get_extension(cert, "2.5.29.18", 0, &tmpder, NULL); + if (ret < 0 && ret != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) + return gnutls_assert_val(ret); + + if (ret >= 0) { + ret = gnutls_x509_ext_import_subject_alt_names(&tmpder, cert->ian, 0); + gnutls_free(tmpder.data); + if (ret < 0) + return gnutls_assert_val(ret); + } + + return 0; +} + +static bool hcomparator(const void *v1, const void *v2) +{ + return (strcmp(v1, v2)==0); +} + +static size_t hhasher(const void *entry, size_t n) +{ + const char *e = entry; + if (e == NULL || e[0] == 0) + return 0; + + return hash_pjw_bare(e, strlen(e)) % n; +} + +#ifdef STRICT_X509 + +/* Check whether certificates serial number is RFC5280 compliant */ +static bool has_valid_serial(gnutls_x509_crt_t cert) +{ + int err, is_zero; + unsigned i; + unsigned char serial[128]; + size_t serial_size = sizeof(serial); + + err = gnutls_x509_crt_get_serial(cert, serial, &serial_size); + if (err < 0) { + _gnutls_debug_log("error: could not read serial number\n"); + return false; + } + + if (serial_size > 20) { + _gnutls_debug_log("error: serial number value is longer than 20 octets\n"); + return false; + } + + if (serial[0] & 0x80) { + _gnutls_debug_log("error: serial number is negative\n"); + return false; + } + + is_zero = 1; + for (i = 0; i < serial_size; ++i) { + if (serial[i]) { + is_zero = 0; + break; + } + } + + if (is_zero) { + _gnutls_debug_log("error: serial number is zero\n"); + return false; + } + + return true; +} + +/* Check if extension can be successfully parsed */ +static bool is_valid_extension(const char *oid, gnutls_datum_t *der) +{ + int err = 0, i; + unsigned u; + size_t sz; + time_t t1, t2; + char *s1 = NULL, *s2 = NULL; + gnutls_datum_t datum = {NULL, 0}; + + if (!strcmp(oid, GNUTLS_X509EXT_OID_BASIC_CONSTRAINTS)) { + err = gnutls_x509_ext_import_basic_constraints(der, &u, &i); + } else if (!strcmp(oid, GNUTLS_X509EXT_OID_SUBJECT_KEY_ID)) { + err = gnutls_x509_ext_import_subject_key_id(der, &datum); + } else if (!strcmp(oid, GNUTLS_X509EXT_OID_CRT_POLICY)) { + gnutls_x509_policies_t policies; + if (gnutls_x509_policies_init(&policies) < 0) + return false; + err = gnutls_x509_ext_import_policies(der, policies, 0); + gnutls_x509_policies_deinit(policies); + } else if (!strcmp(oid, GNUTLS_X509_OID_POLICY_ANY)) { + err = gnutls_x509_ext_import_inhibit_anypolicy(der, &u); + } else if (!strcmp(oid, GNUTLS_X509EXT_OID_AUTHORITY_KEY_ID)) { + gnutls_x509_aki_t aki; + if (gnutls_x509_aki_init(&aki) < 0) + return false; + err = gnutls_x509_ext_import_authority_key_id(der, aki, 0); + gnutls_x509_aki_deinit(aki); + } else if (!strcmp(oid, GNUTLS_X509EXT_OID_KEY_USAGE)) { + err = gnutls_x509_ext_import_key_usage(der, &u); + } else if (!strcmp(oid, GNUTLS_X509EXT_OID_PRIVATE_KEY_USAGE_PERIOD)) { + err = gnutls_x509_ext_import_private_key_usage_period(der, &t1, &t2); + } else if (!strcmp(oid, GNUTLS_X509EXT_OID_EXTENDED_KEY_USAGE)) { + gnutls_x509_key_purposes_t purposes; + if (gnutls_x509_key_purpose_init(&purposes) < 0) + return false; + err = gnutls_x509_ext_import_key_purposes(der, purposes, 0); + gnutls_x509_key_purpose_deinit(purposes); + } else if (!strcmp(oid, GNUTLS_X509EXT_OID_SAN) || + !strcmp(oid, GNUTLS_X509EXT_OID_IAN)) { + gnutls_subject_alt_names_t names; + if (gnutls_subject_alt_names_init(&names) < 0) + return false; + err = gnutls_x509_ext_import_subject_alt_names(der, names, 0); + gnutls_subject_alt_names_deinit(names); + } else if (!strcmp(oid, GNUTLS_X509EXT_OID_CRL_DIST_POINTS)) { + gnutls_x509_crl_dist_points_t dp; + if (gnutls_x509_crl_dist_points_init(&dp) < 0) + return false; + err = gnutls_x509_ext_import_crl_dist_points(der, dp, 0); + gnutls_x509_crl_dist_points_deinit(dp); + } else if (!strcmp(oid, GNUTLS_X509EXT_OID_PROXY_CRT_INFO)) { + err = gnutls_x509_ext_import_proxy(der, &i, &s1, &s2, &sz); + } else if (!strcmp(oid, GNUTLS_X509EXT_OID_AUTHORITY_INFO_ACCESS)) { + gnutls_x509_aia_t aia; + if (gnutls_x509_aia_init(&aia) < 0) + return false; + err = gnutls_x509_ext_import_aia(der, aia, 0); + gnutls_x509_aia_deinit(aia); + } else if (!strcmp(oid, GNUTLS_X509EXT_OID_CT_SCT_V1)) { + gnutls_x509_ct_scts_t scts; + if (gnutls_x509_ext_ct_scts_init(&scts) < 0) + return false; + err = gnutls_x509_ext_ct_import_scts(der, scts, 0); + gnutls_x509_ext_ct_scts_deinit(scts); + } else if (!strcmp(oid, GNUTLS_X509EXT_OID_NAME_CONSTRAINTS)) { + gnutls_x509_name_constraints_t nc; + if (gnutls_x509_name_constraints_init(&nc) < 0) + return false; + err = gnutls_x509_ext_import_name_constraints(der, nc, 0); + gnutls_x509_name_constraints_deinit(nc); + } else if (!strcmp(oid, GNUTLS_X509EXT_OID_TLSFEATURES)) { + gnutls_x509_tlsfeatures_t features; + if (gnutls_x509_tlsfeatures_init(&features) < 0) + return false; + err = gnutls_x509_ext_import_tlsfeatures(der, features, 0); + gnutls_x509_tlsfeatures_deinit(features); + } else { + return true; + } + + gnutls_free(s1); + gnutls_free(s2); + _gnutls_free_datum(&datum); + + return err == 0; +} + +#endif /* STRICT_X509 */ + +int _gnutls_check_cert_sanity(gnutls_x509_crt_t cert) +{ + int ret = 0, version; + gnutls_datum_t exts; + Hash_table *htable = NULL; + + if (cert->flags & GNUTLS_X509_CRT_FLAG_IGNORE_SANITY) + return 0; + + /* enforce the rule that only version 3 certificates carry extensions */ + ret = gnutls_x509_crt_get_version(cert); + if (ret < 0) { + return gnutls_assert_val(ret); + } + + version = ret; + +#ifdef STRICT_X509 + /* enforce upper bound on certificate version (RFC5280 compliant) */ + if (version > 3) { + _gnutls_debug_log("error: invalid certificate version %d\n", version); + return gnutls_assert_val(GNUTLS_E_X509_CERTIFICATE_ERROR); + } +#endif + + if (version < 3) { + if (!cert->modified) { + ret = _gnutls_x509_get_raw_field2(cert->cert, &cert->der, + "tbsCertificate.extensions", &exts); + if (ret >= 0 && exts.size > 0) { + _gnutls_debug_log("error: extensions present in certificate with version %d\n", version); + return gnutls_assert_val(GNUTLS_E_X509_CERTIFICATE_ERROR); + } + } else { + if (cert->use_extensions) { + _gnutls_debug_log("error: extensions set in certificate with version %d\n", version); + return gnutls_assert_val(GNUTLS_E_X509_CERTIFICATE_ERROR); + } + } + } else { + /* Version is 3; ensure no duplicate extensions are present. */ + unsigned i, critical; + char oid[MAX_OID_SIZE]; + size_t oid_size; + char *o; + + htable = hash_initialize(16, NULL, hhasher, hcomparator, gnutls_free); + if (htable == NULL) + return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + + for (i=0;;i++) { + oid_size = sizeof(oid); + ret = gnutls_x509_crt_get_extension_info(cert, i, oid, &oid_size, &critical); + if (ret < 0) { + if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) + break; + gnutls_assert(); + goto cleanup; + } + o = gnutls_strdup(oid); + if (o == NULL) { + ret = gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + goto cleanup; + } + + ret = hash_insert_if_absent(htable, o, NULL); + if (ret == -1) { + gnutls_free(o); + ret = gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + goto cleanup; + } else if (ret == 0) { + /* duplicate */ + gnutls_free(o); + _gnutls_debug_log("error: duplicate extension (%s) detected\n", oid); + ret = gnutls_assert_val(GNUTLS_E_X509_DUPLICATE_EXTENSION); + goto cleanup; + } + +#ifdef STRICT_X509 + gnutls_datum_t der = { NULL, 0 }; + ret = gnutls_x509_crt_get_extension_data2(cert, i, &der); + if (ret < 0) + continue; + if (critical && !is_valid_extension(oid, &der)) { + _gnutls_free_datum(&der); + _gnutls_debug_log("error: could not parse extension (%s)\n"); + return gnutls_assert_val(GNUTLS_E_X509_CERTIFICATE_ERROR); + } + _gnutls_free_datum(&der); +#endif + } + + hash_free(htable); + htable = NULL; + } + + if (version < 2) { + char id[128]; + size_t id_size; + + id_size = sizeof(id); + ret = gnutls_x509_crt_get_subject_unique_id(cert, id, &id_size); + if (ret >= 0 || ret == GNUTLS_E_SHORT_MEMORY_BUFFER) { + _gnutls_debug_log("error: subjectUniqueID present in certificate with version %d\n", version); + ret = gnutls_assert_val(GNUTLS_E_X509_CERTIFICATE_ERROR); + goto cleanup; + } + + id_size = sizeof(id); + ret = gnutls_x509_crt_get_issuer_unique_id(cert, id, &id_size); + if (ret >= 0 || ret == GNUTLS_E_SHORT_MEMORY_BUFFER) { + _gnutls_debug_log("error: subjectUniqueID present in certificate with version %d\n", version); + ret = gnutls_assert_val(GNUTLS_E_X509_CERTIFICATE_ERROR); + goto cleanup; + } + } + +#ifdef STRICT_X509 + if (!has_valid_serial(cert)) { + ret = gnutls_assert_val(GNUTLS_E_X509_CERTIFICATE_ERROR); + goto cleanup; + } +#endif + + if (gnutls_x509_crt_get_expiration_time(cert) == -1 || + gnutls_x509_crt_get_activation_time(cert) == -1) { + _gnutls_debug_log("error: invalid expiration or activation time in certificate\n"); + ret = gnutls_assert_val(GNUTLS_E_CERTIFICATE_TIME_ERROR); + goto cleanup; + } + + ret = 0; + + cleanup: + if (htable) + hash_free(htable); + return ret; +} + +/** + * gnutls_x509_crt_import: + * @cert: The data to store the parsed certificate. + * @data: The DER or PEM encoded certificate. + * @format: One of DER or PEM + * + * This function will convert the given DER or PEM encoded Certificate + * to the native gnutls_x509_crt_t format. The output will be stored + * in @cert. + * + * If the Certificate is PEM encoded it should have a header of "X509 + * CERTIFICATE", or "CERTIFICATE". + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int +gnutls_x509_crt_import(gnutls_x509_crt_t cert, + const gnutls_datum_t * data, + gnutls_x509_crt_fmt_t format) +{ + int result; + + if (cert == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + if (cert->expanded) { + /* Any earlier _asn1_strict_der_decode will modify the ASN.1 + structure, so we need to replace it with a fresh + structure. */ + result = crt_reinit(cert); + if (result < 0) { + gnutls_assert(); + goto cleanup; + } + } + + /* If the Certificate is in PEM format then decode it + */ + if (format == GNUTLS_X509_FMT_PEM) { + /* Try the first header */ + result = + _gnutls_fbase64_decode(PEM_X509_CERT2, data->data, + data->size, &cert->der); + + if (result < 0) { + /* try for the second header */ + result = + _gnutls_fbase64_decode(PEM_X509_CERT, + data->data, data->size, + &cert->der); + + if (result < 0) { + gnutls_assert(); + return result; + } + } + } else { + result = _gnutls_set_datum(&cert->der, data->data, data->size); + if (result < 0) { + gnutls_assert(); + return result; + } + } + + cert->expanded = 1; + cert->modified = 0; + + result = + _asn1_strict_der_decode(&cert->cert, cert->der.data, cert->der.size, NULL); + if (result != ASN1_SUCCESS) { + result = _gnutls_asn2err(result); + gnutls_assert(); + goto cleanup; + } + + result = compare_sig_algorithm(cert); + if (result < 0) { + gnutls_assert(); + goto cleanup; + } + + /* The following do not allocate but rather point to DER data */ + result = _gnutls_x509_get_raw_field2(cert->cert, &cert->der, + "tbsCertificate.issuer.rdnSequence", + &cert->raw_issuer_dn); + if (result < 0) { + gnutls_assert(); + goto cleanup; + } + + result = _gnutls_x509_get_raw_field2(cert->cert, &cert->der, + "tbsCertificate.subject.rdnSequence", + &cert->raw_dn); + if (result < 0) { + gnutls_assert(); + goto cleanup; + } + + result = _gnutls_x509_get_raw_field2(cert->cert, &cert->der, + "tbsCertificate.subjectPublicKeyInfo", + &cert->raw_spki); + if (result < 0) { + gnutls_assert(); + goto cleanup; + } + + result = cache_alt_names(cert); + if (result < 0) { + gnutls_assert(); + goto cleanup; + } + + result = _gnutls_check_cert_sanity(cert); + if (result < 0) { + gnutls_assert(); + goto cleanup; + } + + /* Since we do not want to disable any extension + */ + cert->use_extensions = 1; + + return 0; + + cleanup: + _gnutls_free_datum(&cert->der); + return result; +} + + +/** + * gnutls_x509_crt_get_issuer_dn: + * @cert: should contain a #gnutls_x509_crt_t type + * @buf: a pointer to a structure to hold the name (may be null) + * @buf_size: initially holds the size of @buf + * + * This function will copy the name of the Certificate issuer in the + * provided buffer. The name will be in the form + * "C=xxxx,O=yyyy,CN=zzzz" as described in RFC4514. The output string + * will be ASCII or UTF-8 encoded, depending on the certificate data. + * + * If @buf is null then only the size will be filled. + * + * This function does not output a fully RFC4514 compliant string, if + * that is required see gnutls_x509_crt_get_issuer_dn3(). + * + * Returns: %GNUTLS_E_SHORT_MEMORY_BUFFER if the provided buffer is not + * long enough, and in that case the @buf_size will be updated + * with the required size. %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE if + * the DN does not exist, or another error value on error. On success 0 is returned. + **/ +int +gnutls_x509_crt_get_issuer_dn(gnutls_x509_crt_t cert, char *buf, + size_t * buf_size) +{ + if (cert == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + return _gnutls_x509_parse_dn(cert->cert, + "tbsCertificate.issuer.rdnSequence", + buf, buf_size, GNUTLS_X509_DN_FLAG_COMPAT); +} + +/** + * gnutls_x509_crt_get_issuer_dn2: + * @cert: should contain a #gnutls_x509_crt_t type + * @dn: a pointer to a structure to hold the name; must be freed using gnutls_free() + * + * This function will allocate buffer and copy the name of issuer of the Certificate. + * The name will be in the form "C=xxxx,O=yyyy,CN=zzzz" as + * described in RFC4514. The output string will be ASCII or UTF-8 + * encoded, depending on the certificate data. + * + * This function does not output a fully RFC4514 compliant string, if + * that is required see gnutls_x509_crt_get_issuer_dn3(). + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 3.1.10 + **/ +int +gnutls_x509_crt_get_issuer_dn2(gnutls_x509_crt_t cert, gnutls_datum_t * dn) +{ + if (cert == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + return _gnutls_x509_get_dn(cert->cert, + "tbsCertificate.issuer.rdnSequence", + dn, GNUTLS_X509_DN_FLAG_COMPAT); +} + +/** + * gnutls_x509_crt_get_issuer_dn3: + * @cert: should contain a #gnutls_x509_crt_t type + * @dn: a pointer to a structure to hold the name; must be freed using gnutls_free() + * @flags: zero or %GNUTLS_X509_DN_FLAG_COMPAT + * + * This function will allocate buffer and copy the name of issuer of the Certificate. + * The name will be in the form "C=xxxx,O=yyyy,CN=zzzz" as + * described in RFC4514. The output string will be ASCII or UTF-8 + * encoded, depending on the certificate data. + * + * When the flag %GNUTLS_X509_DN_FLAG_COMPAT is specified, the output + * format will match the format output by previous to 3.5.6 versions of GnuTLS + * which was not not fully RFC4514-compliant. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 3.5.7 + **/ +int +gnutls_x509_crt_get_issuer_dn3(gnutls_x509_crt_t cert, gnutls_datum_t *dn, unsigned flags) +{ + if (cert == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + return _gnutls_x509_get_dn(cert->cert, + "tbsCertificate.issuer.rdnSequence", + dn, flags); +} + +/** + * gnutls_x509_crt_get_issuer_dn_by_oid: + * @cert: should contain a #gnutls_x509_crt_t type + * @oid: holds an Object Identified in null terminated string + * @indx: In case multiple same OIDs exist in the RDN, this specifies which to send. Use (0) to get the first one. + * @raw_flag: If non-zero returns the raw DER data of the DN part. + * @buf: a pointer to a structure to hold the name (may be null) + * @buf_size: initially holds the size of @buf + * + * This function will extract the part of the name of the Certificate + * issuer specified by the given OID. The output, if the raw flag is not + * used, will be encoded as described in RFC4514. Thus a string that is + * ASCII or UTF-8 encoded, depending on the certificate data. + * + * Some helper macros with popular OIDs can be found in gnutls/x509.h + * If raw flag is (0), this function will only return known OIDs as + * text. Other OIDs will be DER encoded, as described in RFC4514 -- + * in hex format with a '#' prefix. You can check about known OIDs + * using gnutls_x509_dn_oid_known(). + * + * If @buf is null then only the size will be filled. If the @raw_flag + * is not specified the output is always null terminated, although the + * @buf_size will not include the null character. + * + * Returns: %GNUTLS_E_SHORT_MEMORY_BUFFER if the provided buffer is not + * long enough, and in that case the @buf_size will be updated with + * the required size. %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE if there + * are no data in the current index. On success 0 is returned. + **/ +int +gnutls_x509_crt_get_issuer_dn_by_oid(gnutls_x509_crt_t cert, + const char *oid, unsigned indx, + unsigned int raw_flag, void *buf, + size_t * buf_size) +{ + gnutls_datum_t td; + int ret; + + if (cert == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + ret = _gnutls_x509_parse_dn_oid(cert->cert, + "tbsCertificate.issuer.rdnSequence", + oid, indx, raw_flag, &td); + if (ret < 0) + return gnutls_assert_val(ret); + + return _gnutls_strdatum_to_buf(&td, buf, buf_size); +} + +/** + * gnutls_x509_crt_get_issuer_dn_oid: + * @cert: should contain a #gnutls_x509_crt_t type + * @indx: This specifies which OID to return. Use (0) to get the first one. + * @oid: a pointer to a buffer to hold the OID (may be null) + * @oid_size: initially holds the size of @oid + * + * This function will extract the OIDs of the name of the Certificate + * issuer specified by the given index. + * + * If @oid is null then only the size will be filled. The @oid + * returned will be null terminated, although @oid_size will not + * account for the trailing null. + * + * Returns: %GNUTLS_E_SHORT_MEMORY_BUFFER if the provided buffer is not + * long enough, and in that case the @buf_size will be updated with + * the required size. %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE if there + * are no data in the current index. On success 0 is returned. + **/ +int +gnutls_x509_crt_get_issuer_dn_oid(gnutls_x509_crt_t cert, + unsigned indx, void *oid, size_t * oid_size) +{ + if (cert == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + return _gnutls_x509_get_dn_oid(cert->cert, + "tbsCertificate.issuer.rdnSequence", + indx, oid, oid_size); +} + +/** + * gnutls_x509_crt_get_dn: + * @cert: should contain a #gnutls_x509_crt_t type + * @buf: a pointer to a structure to hold the name (may be null) + * @buf_size: initially holds the size of @buf + * + * This function will copy the name of the Certificate in the provided + * buffer. The name will be in the form "C=xxxx,O=yyyy,CN=zzzz" as + * described in RFC4514. The output string will be ASCII or UTF-8 + * encoded, depending on the certificate data. + * + * The @buf returned will be null terminated and the @buf_size will account + * for the trailing null. If @buf is null then only the size will be filled. + * + * This function does not output a fully RFC4514 compliant string, if + * that is required see gnutls_x509_crt_get_dn3(). + * + * Returns: %GNUTLS_E_SHORT_MEMORY_BUFFER if the provided buffer is not + * long enough, and in that case the @buf_size will be updated + * with the required size. %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE if + * the DN does not exist, or another error value on error. On success 0 is returned. + **/ +int +gnutls_x509_crt_get_dn(gnutls_x509_crt_t cert, char *buf, + size_t * buf_size) +{ + if (cert == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + return _gnutls_x509_parse_dn(cert->cert, + "tbsCertificate.subject.rdnSequence", + buf, buf_size, GNUTLS_X509_DN_FLAG_COMPAT); +} + +/** + * gnutls_x509_crt_get_dn2: + * @cert: should contain a #gnutls_x509_crt_t type + * @dn: a pointer to a structure to hold the name; must be freed using gnutls_free() + * + * This function will allocate buffer and copy the name of the Certificate. + * The name will be in the form "C=xxxx,O=yyyy,CN=zzzz" as + * described in RFC4514. The output string will be ASCII or UTF-8 + * encoded, depending on the certificate data. + * + * This function does not output a fully RFC4514 compliant string, if + * that is required see gnutls_x509_crt_get_dn3(). + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 3.1.10 + **/ +int gnutls_x509_crt_get_dn2(gnutls_x509_crt_t cert, gnutls_datum_t * dn) +{ + if (cert == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + return _gnutls_x509_get_dn(cert->cert, + "tbsCertificate.subject.rdnSequence", + dn, GNUTLS_X509_DN_FLAG_COMPAT); +} + +/** + * gnutls_x509_crt_get_dn3: + * @cert: should contain a #gnutls_x509_crt_t type + * @dn: a pointer to a structure to hold the name; must be freed using gnutls_free() + * @flags: zero or %GNUTLS_X509_DN_FLAG_COMPAT + * + * This function will allocate buffer and copy the name of the Certificate. + * The name will be in the form "C=xxxx,O=yyyy,CN=zzzz" as + * described in RFC4514. The output string will be ASCII or UTF-8 + * encoded, depending on the certificate data. + * + * When the flag %GNUTLS_X509_DN_FLAG_COMPAT is specified, the output + * format will match the format output by previous to 3.5.6 versions of GnuTLS + * which was not not fully RFC4514-compliant. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 3.5.7 + **/ +int gnutls_x509_crt_get_dn3(gnutls_x509_crt_t cert, gnutls_datum_t *dn, unsigned flags) +{ + if (cert == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + return _gnutls_x509_get_dn(cert->cert, + "tbsCertificate.subject.rdnSequence", + dn, flags); +} + +/** + * gnutls_x509_crt_get_dn_by_oid: + * @cert: should contain a #gnutls_x509_crt_t type + * @oid: holds an Object Identified in null terminated string + * @indx: In case multiple same OIDs exist in the RDN, this specifies which to send. Use (0) to get the first one. + * @raw_flag: If non-zero returns the raw DER data of the DN part. + * @buf: a pointer where the DN part will be copied (may be null). + * @buf_size: initially holds the size of @buf + * + * This function will extract the part of the name of the Certificate + * subject specified by the given OID. The output, if the raw flag is + * not used, will be encoded as described in RFC4514. Thus a string + * that is ASCII or UTF-8 encoded, depending on the certificate data. + * + * Some helper macros with popular OIDs can be found in gnutls/x509.h + * If raw flag is (0), this function will only return known OIDs as + * text. Other OIDs will be DER encoded, as described in RFC4514 -- + * in hex format with a '#' prefix. You can check about known OIDs + * using gnutls_x509_dn_oid_known(). + * + * If @buf is null then only the size will be filled. If the @raw_flag + * is not specified the output is always null terminated, although the + * @buf_size will not include the null character. + * + * Returns: %GNUTLS_E_SHORT_MEMORY_BUFFER if the provided buffer is not + * long enough, and in that case the @buf_size will be updated with + * the required size. %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE if there + * are no data in the current index. On success 0 is returned. + **/ +int +gnutls_x509_crt_get_dn_by_oid(gnutls_x509_crt_t cert, const char *oid, + unsigned indx, unsigned int raw_flag, + void *buf, size_t * buf_size) +{ + gnutls_datum_t td; + int ret; + + if (cert == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + ret = _gnutls_x509_parse_dn_oid(cert->cert, + "tbsCertificate.subject.rdnSequence", + oid, indx, raw_flag, &td); + if (ret < 0) + return gnutls_assert_val(ret); + + return _gnutls_strdatum_to_buf(&td, buf, buf_size); +} + +/** + * gnutls_x509_crt_get_dn_oid: + * @cert: should contain a #gnutls_x509_crt_t type + * @indx: This specifies which OID to return. Use (0) to get the first one. + * @oid: a pointer to a buffer to hold the OID (may be null) + * @oid_size: initially holds the size of @oid + * + * This function will extract the OIDs of the name of the Certificate + * subject specified by the given index. + * + * If @oid is null then only the size will be filled. The @oid + * returned will be null terminated, although @oid_size will not + * account for the trailing null. + * + * Returns: %GNUTLS_E_SHORT_MEMORY_BUFFER if the provided buffer is not + * long enough, and in that case the @buf_size will be updated with + * the required size. %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE if there + * are no data in the current index. On success 0 is returned. + **/ +int +gnutls_x509_crt_get_dn_oid(gnutls_x509_crt_t cert, + unsigned indx, void *oid, size_t * oid_size) +{ + if (cert == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + return _gnutls_x509_get_dn_oid(cert->cert, + "tbsCertificate.subject.rdnSequence", + indx, oid, oid_size); +} + +/** + * gnutls_x509_crt_get_signature_algorithm: + * @cert: should contain a #gnutls_x509_crt_t type + * + * This function will return a value of the #gnutls_sign_algorithm_t + * enumeration that is the signature algorithm that has been used to + * sign this certificate. + * + * Since 3.6.0 this function never returns a negative error code. + * Error cases and unknown/unsupported signature algorithms are + * mapped to %GNUTLS_SIGN_UNKNOWN. + * + * Returns: a #gnutls_sign_algorithm_t value + **/ +int gnutls_x509_crt_get_signature_algorithm(gnutls_x509_crt_t cert) +{ + return map_errs_to_zero(_gnutls_x509_get_signature_algorithm(cert->cert, + "signatureAlgorithm")); +} + +/** + * gnutls_x509_crt_get_signature_oid: + * @cert: should contain a #gnutls_x509_crt_t type + * @oid: a pointer to a buffer to hold the OID (may be null) + * @oid_size: initially holds the size of @oid + * + * This function will return the OID of the signature algorithm + * that has been used to sign this certificate. This is function + * is useful in the case gnutls_x509_crt_get_signature_algorithm() + * returned %GNUTLS_SIGN_UNKNOWN. + * + * Returns: zero or a negative error code on error. + * + * Since: 3.5.0 + **/ +int gnutls_x509_crt_get_signature_oid(gnutls_x509_crt_t cert, char *oid, size_t *oid_size) +{ + char str[MAX_OID_SIZE]; + int len, result, ret; + gnutls_datum_t out; + + len = sizeof(str); + result = asn1_read_value(cert->cert, "signatureAlgorithm.algorithm", str, &len); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + out.data = (void*)str; + out.size = len; + + ret = _gnutls_copy_string(&out, (void*)oid, oid_size); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + return 0; +} + +/** + * gnutls_x509_crt_get_pk_oid: + * @cert: should contain a #gnutls_x509_crt_t type + * @oid: a pointer to a buffer to hold the OID (may be null) + * @oid_size: initially holds the size of @oid + * + * This function will return the OID of the public key algorithm + * on that certificate. This is function + * is useful in the case gnutls_x509_crt_get_pk_algorithm() + * returned %GNUTLS_PK_UNKNOWN. + * + * Returns: zero or a negative error code on error. + * + * Since: 3.5.0 + **/ +int gnutls_x509_crt_get_pk_oid(gnutls_x509_crt_t cert, char *oid, size_t *oid_size) +{ + char str[MAX_OID_SIZE]; + int len, result, ret; + gnutls_datum_t out; + + len = sizeof(str); + result = asn1_read_value(cert->cert, "tbsCertificate.subjectPublicKeyInfo.algorithm.algorithm", str, &len); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + out.data = (void*)str; + out.size = len; + + ret = _gnutls_copy_string(&out, (void*)oid, oid_size); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + return 0; +} + +/** + * gnutls_x509_crt_get_signature: + * @cert: should contain a #gnutls_x509_crt_t type + * @sig: a pointer where the signature part will be copied (may be null). + * @sig_size: initially holds the size of @sig + * + * This function will extract the signature field of a certificate. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int +gnutls_x509_crt_get_signature(gnutls_x509_crt_t cert, + char *sig, size_t * sig_size) +{ + gnutls_datum_t dsig = {NULL, 0}; + int ret; + + if (cert == NULL) + return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + + ret = _gnutls_x509_get_signature(cert->cert, "signature", &dsig); + if (ret < 0) + return gnutls_assert_val(ret); + + ret = _gnutls_copy_data(&dsig, (uint8_t*)sig, sig_size); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = 0; + cleanup: + gnutls_free(dsig.data); + return ret; +} + +/** + * gnutls_x509_crt_get_version: + * @cert: should contain a #gnutls_x509_crt_t type + * + * This function will return the version of the specified Certificate. + * + * Returns: version of certificate, or a negative error code on error. + **/ +int gnutls_x509_crt_get_version(gnutls_x509_crt_t cert) +{ + if (cert == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + return _gnutls_x509_get_version(cert->cert, "tbsCertificate.version"); +} + +/** + * gnutls_x509_crt_get_activation_time: + * @cert: should contain a #gnutls_x509_crt_t type + * + * This function will return the time this Certificate was or will be + * activated. + * + * Returns: activation time, or (time_t)-1 on error. + **/ +time_t gnutls_x509_crt_get_activation_time(gnutls_x509_crt_t cert) +{ + if (cert == NULL) { + gnutls_assert(); + return (time_t) - 1; + } + + return _gnutls_x509_get_time(cert->cert, + "tbsCertificate.validity.notBefore", + 0); +} + +/** + * gnutls_x509_crt_get_expiration_time: + * @cert: should contain a #gnutls_x509_crt_t type + * + * This function will return the time this certificate was or will be + * expired. + * + * Returns: expiration time, or (time_t)-1 on error. + **/ +time_t gnutls_x509_crt_get_expiration_time(gnutls_x509_crt_t cert) +{ + if (cert == NULL) { + gnutls_assert(); + return (time_t) - 1; + } + + return _gnutls_x509_get_time(cert->cert, + "tbsCertificate.validity.notAfter", + 0); +} + +/** + * gnutls_x509_crt_get_private_key_usage_period: + * @cert: should contain a #gnutls_x509_crt_t type + * @activation: The activation time + * @expiration: The expiration time + * @critical: the extension status + * + * This function will return the expiration and activation + * times of the private key of the certificate. It relies on + * the PKIX extension 2.5.29.16 being present. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE + * if the extension is not present, otherwise a negative error value. + **/ +int +gnutls_x509_crt_get_private_key_usage_period(gnutls_x509_crt_t cert, + time_t * activation, + time_t * expiration, + unsigned int *critical) +{ + int ret; + gnutls_datum_t der = { NULL, 0 }; + + if (cert == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + ret = + _gnutls_x509_crt_get_extension(cert, "2.5.29.16", 0, &der, + critical); + if (ret < 0) + return gnutls_assert_val(ret); + + if (der.size == 0 || der.data == NULL) + return + gnutls_assert_val + (GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE); + + ret = gnutls_x509_ext_import_private_key_usage_period(&der, activation, expiration); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = 0; + + cleanup: + _gnutls_free_datum(&der); + + return ret; +} + + +/** + * gnutls_x509_crt_get_serial: + * @cert: should contain a #gnutls_x509_crt_t type + * @result: The place where the serial number will be copied + * @result_size: Holds the size of the result field. + * + * This function will return the X.509 certificate's serial number. + * This is obtained by the X509 Certificate serialNumber field. Serial + * is not always a 32 or 64bit number. Some CAs use large serial + * numbers, thus it may be wise to handle it as something uint8_t. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int +gnutls_x509_crt_get_serial(gnutls_x509_crt_t cert, void *result, + size_t * result_size) +{ + int ret, len; + + if (cert == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + len = *result_size; + ret = + asn1_read_value(cert->cert, "tbsCertificate.serialNumber", + result, &len); + *result_size = len; + + if (ret != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(ret); + } + + return 0; +} + +/** + * gnutls_x509_crt_get_subject_key_id: + * @cert: should contain a #gnutls_x509_crt_t type + * @ret: The place where the identifier will be copied + * @ret_size: Holds the size of the result field. + * @critical: will be non-zero if the extension is marked as critical (may be null) + * + * This function will return the X.509v3 certificate's subject key + * identifier. This is obtained by the X.509 Subject Key identifier + * extension field (2.5.29.14). + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE + * if the extension is not present, otherwise a negative error value. + **/ +int +gnutls_x509_crt_get_subject_key_id(gnutls_x509_crt_t cert, void *ret, + size_t * ret_size, + unsigned int *critical) +{ + int result; + gnutls_datum_t id = {NULL,0}; + gnutls_datum_t der = {NULL, 0}; + + if (cert == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + if (ret == NULL) + *ret_size = 0; + + if ((result = + _gnutls_x509_crt_get_extension(cert, "2.5.29.14", 0, &der, + critical)) < 0) { + return result; + } + + result = gnutls_x509_ext_import_subject_key_id(&der, &id); + if (result < 0) { + gnutls_assert(); + goto cleanup; + } + + result = _gnutls_copy_data(&id, ret, ret_size); + if (result < 0) { + gnutls_assert(); + goto cleanup; + } + + result = 0; + + cleanup: + gnutls_free(der.data); + gnutls_free(id.data); + return result; +} + +inline static int is_type_printable(int type) +{ + if (type == GNUTLS_SAN_DNSNAME || type == GNUTLS_SAN_RFC822NAME || + type == GNUTLS_SAN_URI || type == GNUTLS_SAN_OTHERNAME_XMPP || + type == GNUTLS_SAN_OTHERNAME || type == GNUTLS_SAN_REGISTERED_ID) + return 1; + else + return 0; +} + +/** + * gnutls_x509_crt_get_authority_key_gn_serial: + * @cert: should contain a #gnutls_x509_crt_t type + * @seq: specifies the sequence number of the alt name (0 for the first one, 1 for the second etc.) + * @alt: is the place where the alternative name will be copied to + * @alt_size: holds the size of alt. + * @alt_type: holds the type of the alternative name (one of gnutls_x509_subject_alt_name_t). + * @serial: buffer to store the serial number (may be null) + * @serial_size: Holds the size of the serial field (may be null) + * @critical: will be non-zero if the extension is marked as critical (may be null) + * + * This function will return the X.509 authority key + * identifier when stored as a general name (authorityCertIssuer) + * and serial number. + * + * Because more than one general names might be stored + * @seq can be used as a counter to request them all until + * %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE is returned. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE + * if the extension is not present, otherwise a negative error value. + * + * Since: 3.0 + **/ +int +gnutls_x509_crt_get_authority_key_gn_serial(gnutls_x509_crt_t cert, + unsigned int seq, void *alt, + size_t * alt_size, + unsigned int *alt_type, + void *serial, + size_t * serial_size, + unsigned int *critical) +{ + int ret; + gnutls_datum_t der, san, iserial; + gnutls_x509_aki_t aki = NULL; + unsigned san_type; + + if (cert == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + if ((ret = + _gnutls_x509_crt_get_extension(cert, "2.5.29.35", 0, &der, + critical)) < 0) { + return gnutls_assert_val(ret); + } + + if (der.size == 0 || der.data == NULL) { + gnutls_assert(); + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + } + + ret = gnutls_x509_aki_init(&aki); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = gnutls_x509_ext_import_authority_key_id(&der, aki, 0); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = gnutls_x509_aki_get_cert_issuer(aki, seq, &san_type, &san, NULL, &iserial); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + if (is_type_printable(san_type)) + ret = _gnutls_copy_string(&san, alt, alt_size); + else + ret = _gnutls_copy_data(&san, alt, alt_size); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + if (alt_type) + *alt_type = san_type; + + ret = _gnutls_copy_data(&iserial, serial, serial_size); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = 0; + cleanup: + if (aki != NULL) + gnutls_x509_aki_deinit(aki); + gnutls_free(der.data); + return ret; +} + +/** + * gnutls_x509_crt_get_authority_key_id: + * @cert: should contain a #gnutls_x509_crt_t type + * @id: The place where the identifier will be copied + * @id_size: Holds the size of the id field. + * @critical: will be non-zero if the extension is marked as critical (may be null) + * + * This function will return the X.509v3 certificate authority's key + * identifier. This is obtained by the X.509 Authority Key + * identifier extension field (2.5.29.35). Note that this function + * only returns the keyIdentifier field of the extension and + * %GNUTLS_E_X509_UNSUPPORTED_EXTENSION, if the extension contains + * the name and serial number of the certificate. In that case + * gnutls_x509_crt_get_authority_key_gn_serial() may be used. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE + * if the extension is not present, otherwise a negative error value. + **/ +int +gnutls_x509_crt_get_authority_key_id(gnutls_x509_crt_t cert, void *id, + size_t * id_size, + unsigned int *critical) +{ + int ret; + gnutls_datum_t der, l_id; + gnutls_x509_aki_t aki = NULL; + + if (cert == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + if ((ret = + _gnutls_x509_crt_get_extension(cert, "2.5.29.35", 0, &der, + critical)) < 0) { + return gnutls_assert_val(ret); + } + + if (der.size == 0 || der.data == NULL) { + gnutls_assert(); + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + } + + ret = gnutls_x509_aki_init(&aki); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = gnutls_x509_ext_import_authority_key_id(&der, aki, 0); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = gnutls_x509_aki_get_id(aki, &l_id); + + if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { + gnutls_datum_t serial; + ret = gnutls_x509_aki_get_cert_issuer(aki, 0, NULL, NULL, NULL, &serial); + if (ret >= 0) { + ret = gnutls_assert_val(GNUTLS_E_X509_UNSUPPORTED_EXTENSION); + } else { + ret = gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE); + } + } + + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = _gnutls_copy_data(&l_id, id, id_size); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = 0; + cleanup: + if (aki != NULL) + gnutls_x509_aki_deinit(aki); + gnutls_free(der.data); + return ret; +} + +/** + * gnutls_x509_crt_get_pk_algorithm: + * @cert: should contain a #gnutls_x509_crt_t type + * @bits: if bits is non null it will hold the size of the parameters' in bits + * + * This function will return the public key algorithm of an X.509 + * certificate. + * + * If bits is non null, it should have enough size to hold the parameters + * size in bits. For RSA the bits returned is the modulus. + * For DSA the bits returned are of the public + * exponent. + * + * Unknown/unsupported algorithms are mapped to %GNUTLS_PK_UNKNOWN. + * + * Returns: a member of the #gnutls_pk_algorithm_t enumeration on + * success, or a negative error code on error. + **/ +int +gnutls_x509_crt_get_pk_algorithm(gnutls_x509_crt_t cert, + unsigned int *bits) +{ + int result; + + if (cert == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + if (bits) + *bits = 0; + + result = + _gnutls_x509_get_pk_algorithm(cert->cert, + "tbsCertificate.subjectPublicKeyInfo", + NULL, + bits); + + if (result < 0) { + gnutls_assert(); + return result; + } + + return result; +} + +/** + * gnutls_x509_crt_get_spki: + * @cert: a certificate of type #gnutls_x509_crt_t + * @spki: a SubjectPublicKeyInfo structure of type #gnutls_x509_spki_t + * @flags: must be zero + * + * This function will return the public key information of an X.509 + * certificate. The provided @spki must be initialized. + * + * Since: 3.6.0 + **/ +int +gnutls_x509_crt_get_spki(gnutls_x509_crt_t cert, gnutls_x509_spki_t spki, unsigned int flags) +{ + int result; + gnutls_x509_spki_st params; + + if (cert == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + + spki->pk = gnutls_x509_crt_get_pk_algorithm(cert, NULL); + + memset(¶ms, 0, sizeof(params)); + + result = _gnutls_x509_crt_read_spki_params(cert, ¶ms); + if (result < 0) { + gnutls_assert(); + return result; + } + + if (params.pk == GNUTLS_PK_UNKNOWN) + return gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE); + + spki->rsa_pss_dig = params.rsa_pss_dig; + spki->salt_size = params.salt_size; + + return 0; +} + +/* returns the type and the name on success. + * Type is also returned as a parameter in case of an error. + * + * @seq: in case of GeneralNames it will return the corresponding name. + * in case of GeneralName, it must be -1 + * @dname: the name returned + * @ret_type: The type of the name + * @othername_oid: if the name is otherName return the OID + * + */ +int +_gnutls_parse_general_name2(asn1_node src, const char *src_name, + int seq, gnutls_datum_t *dname, + unsigned int *ret_type, int othername_oid) +{ + int len, ret; + char nptr[MAX_NAME_SIZE]; + int result; + gnutls_datum_t tmp = {NULL, 0}; + char choice_type[128]; + gnutls_x509_subject_alt_name_t type; + + if (seq != -1) { + seq++; /* 0->1, 1->2 etc */ + + if (src_name[0] != 0) + snprintf(nptr, sizeof(nptr), "%s.?%d", src_name, seq); + else + snprintf(nptr, sizeof(nptr), "?%d", seq); + } else { + snprintf(nptr, sizeof(nptr), "%s", src_name); + } + + len = sizeof(choice_type); + result = asn1_read_value(src, nptr, choice_type, &len); + if (result == ASN1_VALUE_NOT_FOUND + || result == ASN1_ELEMENT_NOT_FOUND) { + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + } + + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + type = _gnutls_x509_san_find_type(choice_type); + if (type == (gnutls_x509_subject_alt_name_t) - 1) { + gnutls_assert(); + return GNUTLS_E_X509_UNKNOWN_SAN; + } + + if (ret_type) + *ret_type = type; + + if (type == GNUTLS_SAN_OTHERNAME) { + if (othername_oid) + _gnutls_str_cat(nptr, sizeof(nptr), + ".otherName.type-id"); + else + _gnutls_str_cat(nptr, sizeof(nptr), + ".otherName.value"); + + ret = _gnutls_x509_read_value(src, nptr, &tmp); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + if (othername_oid) { + dname->size = tmp.size; + dname->data = tmp.data; + } else { + char oid[MAX_OID_SIZE]; + + if (src_name[0] != 0 && seq != -1) + snprintf(nptr, sizeof(nptr), + "%s.?%d.otherName.type-id", + src_name, seq); + else if (src_name[0] != 0) + snprintf(nptr, sizeof(nptr), + "%s.otherName.type-id", + src_name); + else + snprintf(nptr, sizeof(nptr), + "?%d.otherName.type-id", seq); + + len = sizeof(oid); + + result = asn1_read_value(src, nptr, oid, &len); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(result); + goto cleanup; + } + if (len > 0) len--; + + dname->size = tmp.size; + dname->data = tmp.data; + } + } else if (type == GNUTLS_SAN_DN) { + _gnutls_str_cat(nptr, sizeof(nptr), ".directoryName"); + ret = _gnutls_x509_get_dn(src, nptr, dname, 0); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + } else if (othername_oid) { + gnutls_assert(); + ret = GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + goto cleanup; + } else { + _gnutls_str_cat(nptr, sizeof(nptr), "."); + _gnutls_str_cat(nptr, sizeof(nptr), choice_type); + + ret = _gnutls_x509_read_null_value(src, nptr, &tmp); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + /* _gnutls_x509_read_value() null terminates */ + dname->size = tmp.size; + dname->data = tmp.data; + } + + return type; + + cleanup: + gnutls_free(tmp.data); + return ret; +} + +/* returns the type and the name on success. + * Type is also returned as a parameter in case of an error. + */ +int +_gnutls_parse_general_name(asn1_node src, const char *src_name, + int seq, void *name, size_t * name_size, + unsigned int *ret_type, int othername_oid) +{ + int ret; + gnutls_datum_t res = {NULL,0}; + unsigned type; + + ret = _gnutls_parse_general_name2(src, src_name, seq, &res, ret_type, othername_oid); + if (ret < 0) + return gnutls_assert_val(ret); + + type = ret; + + if (is_type_printable(type)) { + ret = _gnutls_copy_string(&res, name, name_size); + } else { + ret = _gnutls_copy_data(&res, name, name_size); + } + + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = type; +cleanup: + gnutls_free(res.data); + return ret; +} + +static int +get_alt_name(gnutls_subject_alt_names_t san, + unsigned int seq, uint8_t *alt, + size_t * alt_size, unsigned int *alt_type, + unsigned int *critical, int othername_oid) +{ + int ret; + gnutls_datum_t ooid = {NULL, 0}; + gnutls_datum_t oname; + gnutls_datum_t virt = {NULL, 0}; + unsigned int type; + + if (san == NULL) { + gnutls_assert(); + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + } + + if (alt == NULL) + *alt_size = 0; + + ret = gnutls_subject_alt_names_get(san, seq, &type, &oname, &ooid); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + if (type == GNUTLS_SAN_OTHERNAME && ooid.data) { + unsigned vtype; + ret = gnutls_x509_othername_to_virtual((char*)ooid.data, &oname, &vtype, &virt); + if (ret >= 0) { + type = vtype; + oname.data = virt.data; + oname.size = virt.size; + } + } + + if (alt_type) + *alt_type = type; + + if (othername_oid) { + ret = _gnutls_copy_string(&ooid, alt, alt_size); + } else { + if (is_type_printable(type)) { + ret = _gnutls_copy_string(&oname, alt, alt_size); + } else { + ret = _gnutls_copy_data(&oname, alt, alt_size); + } + } + + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = type; +cleanup: + gnutls_free(virt.data); + + return ret; +} + +/** + * gnutls_x509_crt_get_subject_alt_name: + * @cert: should contain a #gnutls_x509_crt_t type + * @seq: specifies the sequence number of the alt name (0 for the first one, 1 for the second etc.) + * @san: is the place where the alternative name will be copied to + * @san_size: holds the size of san. + * @critical: will be non-zero if the extension is marked as critical (may be null) + * + * This function retrieves the Alternative Name (2.5.29.17), contained + * in the given certificate in the X509v3 Certificate Extensions. + * + * When the SAN type is otherName, it will extract the data in the + * otherName's value field, and %GNUTLS_SAN_OTHERNAME is returned. + * You may use gnutls_x509_crt_get_subject_alt_othername_oid() to get + * the corresponding OID and the "virtual" SAN types (e.g., + * %GNUTLS_SAN_OTHERNAME_XMPP). + * + * If an otherName OID is known, the data will be decoded. Otherwise + * the returned data will be DER encoded, and you will have to decode + * it yourself. Currently, only the RFC 3920 id-on-xmppAddr SAN is + * recognized. + * + * Returns: the alternative subject name type on success, one of the + * enumerated #gnutls_x509_subject_alt_name_t. It will return + * %GNUTLS_E_SHORT_MEMORY_BUFFER if @san_size is not large enough to + * hold the value. In that case @san_size will be updated with the + * required size. If the certificate does not have an Alternative + * name with the specified sequence number then + * %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE is returned. + **/ +int +gnutls_x509_crt_get_subject_alt_name(gnutls_x509_crt_t cert, + unsigned int seq, void *san, + size_t * san_size, + unsigned int *critical) +{ + return get_alt_name(cert->san, seq, san, san_size, NULL, + critical, 0); +} + +/** + * gnutls_x509_crt_get_issuer_alt_name: + * @cert: should contain a #gnutls_x509_crt_t type + * @seq: specifies the sequence number of the alt name (0 for the first one, 1 for the second etc.) + * @ian: is the place where the alternative name will be copied to + * @ian_size: holds the size of ian. + * @critical: will be non-zero if the extension is marked as critical (may be null) + * + * This function retrieves the Issuer Alternative Name (2.5.29.18), + * contained in the given certificate in the X509v3 Certificate + * Extensions. + * + * When the SAN type is otherName, it will extract the data in the + * otherName's value field, and %GNUTLS_SAN_OTHERNAME is returned. + * You may use gnutls_x509_crt_get_subject_alt_othername_oid() to get + * the corresponding OID and the "virtual" SAN types (e.g., + * %GNUTLS_SAN_OTHERNAME_XMPP). + * + * If an otherName OID is known, the data will be decoded. Otherwise + * the returned data will be DER encoded, and you will have to decode + * it yourself. Currently, only the RFC 3920 id-on-xmppAddr Issuer + * AltName is recognized. + * + * Returns: the alternative issuer name type on success, one of the + * enumerated #gnutls_x509_subject_alt_name_t. It will return + * %GNUTLS_E_SHORT_MEMORY_BUFFER if @ian_size is not large enough + * to hold the value. In that case @ian_size will be updated with + * the required size. If the certificate does not have an + * Alternative name with the specified sequence number then + * %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE is returned. + * + * Since: 2.10.0 + **/ +int +gnutls_x509_crt_get_issuer_alt_name(gnutls_x509_crt_t cert, + unsigned int seq, void *ian, + size_t * ian_size, + unsigned int *critical) +{ + return get_alt_name(cert->ian, seq, ian, ian_size, NULL, + critical, 0); +} + +/** + * gnutls_x509_crt_get_subject_alt_name2: + * @cert: should contain a #gnutls_x509_crt_t type + * @seq: specifies the sequence number of the alt name (0 for the first one, 1 for the second etc.) + * @san: is the place where the alternative name will be copied to + * @san_size: holds the size of ret. + * @san_type: holds the type of the alternative name (one of gnutls_x509_subject_alt_name_t). + * @critical: will be non-zero if the extension is marked as critical (may be null) + * + * This function will return the alternative names, contained in the + * given certificate. It is the same as + * gnutls_x509_crt_get_subject_alt_name() except for the fact that it + * will return the type of the alternative name in @san_type even if + * the function fails for some reason (i.e. the buffer provided is + * not enough). + * + * Returns: the alternative subject name type on success, one of the + * enumerated #gnutls_x509_subject_alt_name_t. It will return + * %GNUTLS_E_SHORT_MEMORY_BUFFER if @san_size is not large enough + * to hold the value. In that case @san_size will be updated with + * the required size. If the certificate does not have an + * Alternative name with the specified sequence number then + * %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE is returned. + **/ +int +gnutls_x509_crt_get_subject_alt_name2(gnutls_x509_crt_t cert, + unsigned int seq, void *san, + size_t * san_size, + unsigned int *san_type, + unsigned int *critical) +{ + return get_alt_name(cert->san, seq, san, san_size, + san_type, critical, 0); +} + +/** + * gnutls_x509_crt_get_issuer_alt_name2: + * @cert: should contain a #gnutls_x509_crt_t type + * @seq: specifies the sequence number of the alt name (0 for the first one, 1 for the second etc.) + * @ian: is the place where the alternative name will be copied to + * @ian_size: holds the size of ret. + * @ian_type: holds the type of the alternative name (one of gnutls_x509_subject_alt_name_t). + * @critical: will be non-zero if the extension is marked as critical (may be null) + * + * This function will return the alternative names, contained in the + * given certificate. It is the same as + * gnutls_x509_crt_get_issuer_alt_name() except for the fact that it + * will return the type of the alternative name in @ian_type even if + * the function fails for some reason (i.e. the buffer provided is + * not enough). + * + * Returns: the alternative issuer name type on success, one of the + * enumerated #gnutls_x509_subject_alt_name_t. It will return + * %GNUTLS_E_SHORT_MEMORY_BUFFER if @ian_size is not large enough + * to hold the value. In that case @ian_size will be updated with + * the required size. If the certificate does not have an + * Alternative name with the specified sequence number then + * %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE is returned. + * + * Since: 2.10.0 + * + **/ +int +gnutls_x509_crt_get_issuer_alt_name2(gnutls_x509_crt_t cert, + unsigned int seq, void *ian, + size_t * ian_size, + unsigned int *ian_type, + unsigned int *critical) +{ + return get_alt_name(cert->ian, seq, ian, ian_size, + ian_type, critical, 0); +} + +/** + * gnutls_x509_crt_get_subject_alt_othername_oid: + * @cert: should contain a #gnutls_x509_crt_t type + * @seq: specifies the sequence number of the alt name (0 for the first one, 1 for the second etc.) + * @oid: is the place where the otherName OID will be copied to + * @oid_size: holds the size of ret. + * + * This function will extract the type OID of an otherName Subject + * Alternative Name, contained in the given certificate, and return + * the type as an enumerated element. + * + * This function is only useful if + * gnutls_x509_crt_get_subject_alt_name() returned + * %GNUTLS_SAN_OTHERNAME. + * + * If @oid is null then only the size will be filled. The @oid + * returned will be null terminated, although @oid_size will not + * account for the trailing null. + * + * Returns: the alternative subject name type on success, one of the + * enumerated gnutls_x509_subject_alt_name_t. For supported OIDs, it + * will return one of the virtual (GNUTLS_SAN_OTHERNAME_*) types, + * e.g. %GNUTLS_SAN_OTHERNAME_XMPP, and %GNUTLS_SAN_OTHERNAME for + * unknown OIDs. It will return %GNUTLS_E_SHORT_MEMORY_BUFFER if + * @ian_size is not large enough to hold the value. In that case + * @ian_size will be updated with the required size. If the + * certificate does not have an Alternative name with the specified + * sequence number and with the otherName type then + * %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE is returned. + **/ +int +gnutls_x509_crt_get_subject_alt_othername_oid(gnutls_x509_crt_t cert, + unsigned int seq, + void *oid, size_t * oid_size) +{ + return get_alt_name(cert->san, seq, oid, oid_size, NULL, + NULL, 1); +} + +/** + * gnutls_x509_crt_get_issuer_alt_othername_oid: + * @cert: should contain a #gnutls_x509_crt_t type + * @seq: specifies the sequence number of the alt name (0 for the first one, 1 for the second etc.) + * @ret: is the place where the otherName OID will be copied to + * @ret_size: holds the size of ret. + * + * This function will extract the type OID of an otherName Subject + * Alternative Name, contained in the given certificate, and return + * the type as an enumerated element. + * + * If @oid is null then only the size will be filled. The @oid + * returned will be null terminated, although @oid_size will not + * account for the trailing null. + * + * This function is only useful if + * gnutls_x509_crt_get_issuer_alt_name() returned + * %GNUTLS_SAN_OTHERNAME. + * + * Returns: the alternative issuer name type on success, one of the + * enumerated gnutls_x509_subject_alt_name_t. For supported OIDs, it + * will return one of the virtual (GNUTLS_SAN_OTHERNAME_*) types, + * e.g. %GNUTLS_SAN_OTHERNAME_XMPP, and %GNUTLS_SAN_OTHERNAME for + * unknown OIDs. It will return %GNUTLS_E_SHORT_MEMORY_BUFFER if + * @ret_size is not large enough to hold the value. In that case + * @ret_size will be updated with the required size. If the + * certificate does not have an Alternative name with the specified + * sequence number and with the otherName type then + * %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE is returned. + * + * Since: 2.10.0 + **/ +int +gnutls_x509_crt_get_issuer_alt_othername_oid(gnutls_x509_crt_t cert, + unsigned int seq, + void *ret, size_t * ret_size) +{ + return get_alt_name(cert->ian, seq, ret, ret_size, NULL, + NULL, 1); +} + +/** + * gnutls_x509_crt_get_basic_constraints: + * @cert: should contain a #gnutls_x509_crt_t type + * @critical: will be non-zero if the extension is marked as critical + * @ca: pointer to output integer indicating CA status, may be NULL, + * value is 1 if the certificate CA flag is set, 0 otherwise. + * @pathlen: pointer to output integer indicating path length (may be + * NULL), non-negative error codes indicate a present pathLenConstraint + * field and the actual value, -1 indicate that the field is absent. + * + * This function will read the certificate's basic constraints, and + * return the certificates CA status. It reads the basicConstraints + * X.509 extension (2.5.29.19). + * + * Returns: If the certificate is a CA a positive value will be + * returned, or (0) if the certificate does not have CA flag set. A + * negative error code may be returned in case of errors. If the + * certificate does not contain the basicConstraints extension + * GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE will be returned. + **/ +int +gnutls_x509_crt_get_basic_constraints(gnutls_x509_crt_t cert, + unsigned int *critical, + unsigned int *ca, int *pathlen) +{ + int result; + gnutls_datum_t basicConstraints; + unsigned int tmp_ca; + + if (cert == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + if ((result = + _gnutls_x509_crt_get_extension(cert, "2.5.29.19", 0, + &basicConstraints, + critical)) < 0) { + return result; + } + + if (basicConstraints.size == 0 || basicConstraints.data == NULL) { + gnutls_assert(); + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + } + + result = gnutls_x509_ext_import_basic_constraints(&basicConstraints, &tmp_ca, pathlen); + if (ca) + *ca = tmp_ca; + + _gnutls_free_datum(&basicConstraints); + + if (result < 0) { + gnutls_assert(); + return result; + } + + return tmp_ca; +} + +/** + * gnutls_x509_crt_get_ca_status: + * @cert: should contain a #gnutls_x509_crt_t type + * @critical: will be non-zero if the extension is marked as critical + * + * This function will return certificates CA status, by reading the + * basicConstraints X.509 extension (2.5.29.19). If the certificate is + * a CA a positive value will be returned, or (0) if the certificate + * does not have CA flag set. + * + * Use gnutls_x509_crt_get_basic_constraints() if you want to read the + * pathLenConstraint field too. + * + * Returns: If the certificate is a CA a positive value will be + * returned, or (0) if the certificate does not have CA flag set. A + * negative error code may be returned in case of errors. If the + * certificate does not contain the basicConstraints extension + * GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE will be returned. + **/ +int +gnutls_x509_crt_get_ca_status(gnutls_x509_crt_t cert, + unsigned int *critical) +{ + int pathlen; + unsigned int ca; + return gnutls_x509_crt_get_basic_constraints(cert, critical, &ca, + &pathlen); +} + +/** + * gnutls_x509_crt_get_key_usage: + * @cert: should contain a #gnutls_x509_crt_t type + * @key_usage: where the key usage bits will be stored + * @critical: will be non-zero if the extension is marked as critical + * + * This function will return certificate's key usage, by reading the + * keyUsage X.509 extension (2.5.29.15). The key usage value will ORed + * values of the: %GNUTLS_KEY_DIGITAL_SIGNATURE, + * %GNUTLS_KEY_NON_REPUDIATION, %GNUTLS_KEY_KEY_ENCIPHERMENT, + * %GNUTLS_KEY_DATA_ENCIPHERMENT, %GNUTLS_KEY_KEY_AGREEMENT, + * %GNUTLS_KEY_KEY_CERT_SIGN, %GNUTLS_KEY_CRL_SIGN, + * %GNUTLS_KEY_ENCIPHER_ONLY, %GNUTLS_KEY_DECIPHER_ONLY. + * + * Returns: zero on success, or a negative error code in case of + * parsing error. If the certificate does not contain the keyUsage + * extension %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE will be + * returned. + **/ +int +gnutls_x509_crt_get_key_usage(gnutls_x509_crt_t cert, + unsigned int *key_usage, + unsigned int *critical) +{ + int result; + gnutls_datum_t keyUsage; + + if (cert == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + if ((result = + _gnutls_x509_crt_get_extension(cert, "2.5.29.15", 0, + &keyUsage, critical)) < 0) { + return result; + } + + if (keyUsage.size == 0 || keyUsage.data == NULL) { + gnutls_assert(); + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + } + + result = gnutls_x509_ext_import_key_usage(&keyUsage, key_usage); + _gnutls_free_datum(&keyUsage); + + if (result < 0) { + gnutls_assert(); + return result; + } + + return 0; +} + +/** + * gnutls_x509_crt_get_inhibit_anypolicy: + * @cert: should contain a #gnutls_x509_crt_t type + * @skipcerts: will hold the number of certificates after which anypolicy is no longer acceptable. + * @critical: will be non-zero if the extension is marked as critical + * + * This function will return certificate's value of the SkipCerts, i.e., + * the Inhibit anyPolicy X.509 extension (2.5.29.54). + * + * The returned value is the number of additional certificates that + * may appear in the path before the anyPolicy is no longer acceptable. + + * Returns: zero on success, or a negative error code in case of + * parsing error. If the certificate does not contain the Inhibit anyPolicy + * extension %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE will be + * returned. + * + * Since: 3.6.0 + **/ +int +gnutls_x509_crt_get_inhibit_anypolicy(gnutls_x509_crt_t cert, + unsigned int *skipcerts, + unsigned int *critical) +{ + int ret; + gnutls_datum_t ext; + + if (cert == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + if ((ret = + _gnutls_x509_crt_get_extension(cert, "2.5.29.54", 0, + &ext, critical)) < 0) { + return ret; + } + + if (ext.size == 0 || ext.data == NULL) { + gnutls_assert(); + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + } + + ret = gnutls_x509_ext_import_key_usage(&ext, skipcerts); + _gnutls_free_datum(&ext); + + if (ret < 0) { + gnutls_assert(); + return ret; + } + + return 0; +} + +/** + * gnutls_x509_crt_get_proxy: + * @cert: should contain a #gnutls_x509_crt_t type + * @critical: will be non-zero if the extension is marked as critical + * @pathlen: pointer to output integer indicating path length (may be + * NULL), non-negative error codes indicate a present pCPathLenConstraint + * field and the actual value, -1 indicate that the field is absent. + * @policyLanguage: output variable with OID of policy language + * @policy: output variable with policy data + * @sizeof_policy: output variable size of policy data + * + * This function will get information from a proxy certificate. It + * reads the ProxyCertInfo X.509 extension (1.3.6.1.5.5.7.1.14). + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, + * otherwise a negative error code is returned. + **/ +int +gnutls_x509_crt_get_proxy(gnutls_x509_crt_t cert, + unsigned int *critical, + int *pathlen, + char **policyLanguage, + char **policy, size_t * sizeof_policy) +{ + int result; + gnutls_datum_t proxyCertInfo; + + if (cert == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + if ((result = + _gnutls_x509_crt_get_extension(cert, "1.3.6.1.5.5.7.1.14", 0, + &proxyCertInfo, critical)) < 0) + { + return result; + } + + if (proxyCertInfo.size == 0 || proxyCertInfo.data == NULL) { + gnutls_assert(); + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + } + + result = gnutls_x509_ext_import_proxy(&proxyCertInfo, pathlen, + policyLanguage, + policy, + sizeof_policy); + _gnutls_free_datum(&proxyCertInfo); + if (result < 0) { + gnutls_assert(); + return result; + } + + return 0; +} + + +/** + * gnutls_x509_policy_release: + * @policy: a certificate policy + * + * This function will deinitialize all memory associated with the provided + * @policy. The policy is allocated using gnutls_x509_crt_get_policy(). + * + * Since: 3.1.5 + **/ +void gnutls_x509_policy_release(struct gnutls_x509_policy_st *policy) +{ + unsigned i; + + gnutls_free(policy->oid); + for (i = 0; i < policy->qualifiers; i++) + gnutls_free(policy->qualifier[i].data); +} + + +/** + * gnutls_x509_crt_get_policy: + * @crt: should contain a #gnutls_x509_crt_t type + * @indx: This specifies which policy to return. Use (0) to get the first one. + * @policy: A pointer to a policy structure. + * @critical: will be non-zero if the extension is marked as critical + * + * This function will extract the certificate policy (extension 2.5.29.32) + * specified by the given index. + * + * The policy returned by this function must be deinitialized by using + * gnutls_x509_policy_release(). + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE + * if the extension is not present, otherwise a negative error value. + * + * Since: 3.1.5 + **/ +int +gnutls_x509_crt_get_policy(gnutls_x509_crt_t crt, unsigned indx, + struct gnutls_x509_policy_st *policy, + unsigned int *critical) +{ + gnutls_datum_t tmpd = { NULL, 0 }; + int ret; + gnutls_x509_policies_t policies = NULL; + + if (crt == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + memset(policy, 0, sizeof(*policy)); + + ret = gnutls_x509_policies_init(&policies); + if (ret < 0) + return gnutls_assert_val(ret); + + if ((ret = + _gnutls_x509_crt_get_extension(crt, "2.5.29.32", 0, &tmpd, + critical)) < 0) { + goto cleanup; + } + + if (tmpd.size == 0 || tmpd.data == NULL) { + gnutls_assert(); + ret = GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + goto cleanup; + } + + ret = gnutls_x509_ext_import_policies(&tmpd, policies, 0); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = gnutls_x509_policies_get(policies, indx, policy); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + _gnutls_x509_policies_erase(policies, indx); + + ret = 0; + + cleanup: + if (policies != NULL) + gnutls_x509_policies_deinit(policies); + _gnutls_free_datum(&tmpd); + + return ret; +} + + +/** + * gnutls_x509_crt_get_extension_by_oid: + * @cert: should contain a #gnutls_x509_crt_t type + * @oid: holds an Object Identified in null terminated string + * @indx: In case multiple same OIDs exist in the extensions, this specifies which to send. Use (0) to get the first one. + * @buf: a pointer to a structure to hold the name (may be null) + * @buf_size: initially holds the size of @buf + * @critical: will be non-zero if the extension is marked as critical + * + * This function will return the extension specified by the OID in the + * certificate. The extensions will be returned as binary data DER + * encoded, in the provided buffer. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, + * otherwise a negative error code is returned. If the certificate does not + * contain the specified extension + * GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE will be returned. + **/ +int +gnutls_x509_crt_get_extension_by_oid(gnutls_x509_crt_t cert, + const char *oid, unsigned indx, + void *buf, size_t * buf_size, + unsigned int *critical) +{ + int result; + gnutls_datum_t output; + + if (cert == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + if ((result = + _gnutls_x509_crt_get_extension(cert, oid, indx, &output, + critical)) < 0) { + gnutls_assert(); + return result; + } + + if (output.size == 0 || output.data == NULL) { + gnutls_assert(); + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + } + + if (output.size > (unsigned int) *buf_size) { + *buf_size = output.size; + _gnutls_free_datum(&output); + return GNUTLS_E_SHORT_MEMORY_BUFFER; + } + + *buf_size = output.size; + + if (buf) + memcpy(buf, output.data, output.size); + + _gnutls_free_datum(&output); + + return 0; +} + +/** + * gnutls_x509_crt_get_extension_by_oid2: + * @cert: should contain a #gnutls_x509_crt_t type + * @oid: holds an Object Identified in null terminated string + * @indx: In case multiple same OIDs exist in the extensions, this specifies which to send. Use (0) to get the first one. + * @output: will hold the allocated extension data + * @critical: will be non-zero if the extension is marked as critical + * + * This function will return the extension specified by the OID in the + * certificate. The extensions will be returned as binary data DER + * encoded, in the provided buffer. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, + * otherwise a negative error code is returned. If the certificate does not + * contain the specified extension + * GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE will be returned. + * + * Since: 3.3.8 + **/ +int +gnutls_x509_crt_get_extension_by_oid2(gnutls_x509_crt_t cert, + const char *oid, unsigned indx, + gnutls_datum_t *output, + unsigned int *critical) +{ + int ret; + + if (cert == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + if ((ret = + _gnutls_x509_crt_get_extension(cert, oid, indx, output, + critical)) < 0) { + gnutls_assert(); + return ret; + } + + if (output->size == 0 || output->data == NULL) { + gnutls_assert(); + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + } + + return 0; +} + +/** + * gnutls_x509_crt_get_extension_oid: + * @cert: should contain a #gnutls_x509_crt_t type + * @indx: Specifies which extension OID to send. Use (0) to get the first one. + * @oid: a pointer to a structure to hold the OID (may be null) + * @oid_size: initially holds the size of @oid + * + * This function will return the requested extension OID in the certificate. + * The extension OID will be stored as a string in the provided buffer. + * + * The @oid returned will be null terminated, although @oid_size will not + * account for the trailing null. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, + * otherwise a negative error code is returned. If you have reached the + * last extension available %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE + * will be returned. + **/ +int +gnutls_x509_crt_get_extension_oid(gnutls_x509_crt_t cert, unsigned indx, + void *oid, size_t * oid_size) +{ + int result; + + if (cert == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + result = + _gnutls_x509_crt_get_extension_oid(cert, indx, oid, oid_size); + if (result < 0) { + return result; + } + + return 0; + +} + +/** + * gnutls_x509_crt_get_extension_info: + * @cert: should contain a #gnutls_x509_crt_t type + * @indx: Specifies which extension OID to send. Use (0) to get the first one. + * @oid: a pointer to a structure to hold the OID + * @oid_size: initially holds the maximum size of @oid, on return + * holds actual size of @oid. + * @critical: output variable with critical flag, may be NULL. + * + * This function will return the requested extension OID in the + * certificate, and the critical flag for it. The extension OID will + * be stored as a string in the provided buffer. Use + * gnutls_x509_crt_get_extension() to extract the data. + * + * If the buffer provided is not long enough to hold the output, then + * @oid_size is updated and %GNUTLS_E_SHORT_MEMORY_BUFFER will be + * returned. The @oid returned will be null terminated, although + * @oid_size will not account for the trailing null (the latter is not + * true for GnuTLS prior to 3.6.0). + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, + * otherwise a negative error code is returned. If you have reached the + * last extension available %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE + * will be returned. + **/ +int +gnutls_x509_crt_get_extension_info(gnutls_x509_crt_t cert, unsigned indx, + void *oid, size_t * oid_size, + unsigned int *critical) +{ + int result; + char str_critical[10]; + char name[MAX_NAME_SIZE]; + int len; + + if (!cert) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + snprintf(name, sizeof(name), + "tbsCertificate.extensions.?%u.extnID", indx + 1); + + len = *oid_size; + result = asn1_read_value(cert->cert, name, oid, &len); + *oid_size = len; + + if (result == ASN1_ELEMENT_NOT_FOUND) + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + else if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + /* remove any trailing null */ + if (oid && len > 0 && ((uint8_t*)oid)[len-1] == 0) + (*oid_size)--; + + if (critical) { + snprintf(name, sizeof(name), + "tbsCertificate.extensions.?%u.critical", indx + 1); + len = sizeof(str_critical); + result = asn1_read_value(cert->cert, name, str_critical, &len); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + if (str_critical[0] == 'T') + *critical = 1; + else + *critical = 0; + } + + return 0; + +} + +/** + * gnutls_x509_crt_get_extension_data: + * @cert: should contain a #gnutls_x509_crt_t type + * @indx: Specifies which extension OID to send. Use (0) to get the first one. + * @data: a pointer to a structure to hold the data (may be null) + * @sizeof_data: initially holds the size of @data + * + * This function will return the requested extension data in the + * certificate. The extension data will be stored in the + * provided buffer. + * + * Use gnutls_x509_crt_get_extension_info() to extract the OID and + * critical flag. Use gnutls_x509_crt_get_extension_by_oid() instead, + * if you want to get data indexed by the extension OID rather than + * sequence. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, + * otherwise a negative error code is returned. If you have reached the + * last extension available %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE + * will be returned. + **/ +int +gnutls_x509_crt_get_extension_data(gnutls_x509_crt_t cert, unsigned indx, + void *data, size_t * sizeof_data) +{ + int result, len; + char name[MAX_NAME_SIZE]; + + if (!cert) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + snprintf(name, sizeof(name), + "tbsCertificate.extensions.?%u.extnValue", indx + 1); + + len = *sizeof_data; + result = asn1_read_value(cert->cert, name, data, &len); + *sizeof_data = len; + + if (result == ASN1_ELEMENT_NOT_FOUND) { + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + } else if (result == ASN1_MEM_ERROR && data == NULL) { + /* normally we should return GNUTLS_E_SHORT_MEMORY_BUFFER, + * but we haven't done that for long time, so use + * backwards compatible behavior */ + return 0; + } else if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + return 0; +} + +/** + * gnutls_x509_crt_get_raw_issuer_dn: + * @cert: should contain a #gnutls_x509_crt_t type + * @dn: will hold the starting point of the DN + * + * This function will return a pointer to the DER encoded DN structure + * and the length. This points to allocated data that must be free'd using gnutls_free(). + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value.or a negative error code on error. + * + **/ +int +gnutls_x509_crt_get_raw_issuer_dn(gnutls_x509_crt_t cert, + gnutls_datum_t * dn) +{ + if (cert->raw_issuer_dn.size > 0 && cert->modified == 0) { + return _gnutls_set_datum(dn, cert->raw_issuer_dn.data, + cert->raw_issuer_dn.size); + } else { + return _gnutls_x509_get_raw_field(cert->cert, "tbsCertificate.issuer.rdnSequence", dn); + } +} + +/** + * gnutls_x509_crt_get_raw_dn: + * @cert: should contain a #gnutls_x509_crt_t type + * @dn: will hold the starting point of the DN + * + * This function will return a pointer to the DER encoded DN structure and + * the length. This points to allocated data that must be free'd using gnutls_free(). + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. or a negative error code on error. + * + **/ +int gnutls_x509_crt_get_raw_dn(gnutls_x509_crt_t cert, gnutls_datum_t * dn) +{ + if (cert->raw_dn.size > 0 && cert->modified == 0) { + return _gnutls_set_datum(dn, cert->raw_dn.data, cert->raw_dn.size); + } else { + return _gnutls_x509_get_raw_field(cert->cert, "tbsCertificate.subject.rdnSequence", dn); + } +} + +static int +get_dn(gnutls_x509_crt_t cert, const char *whom, gnutls_x509_dn_t * dn, unsigned subject) +{ + gnutls_x509_dn_st *store; + + if (subject) + store = &cert->dn; + else + store = &cert->idn; + + store->asn = asn1_find_node(cert->cert, whom); + if (!store->asn) + return GNUTLS_E_ASN1_ELEMENT_NOT_FOUND; + + *dn = store; + + return 0; +} + +/** + * gnutls_x509_crt_get_subject: + * @cert: should contain a #gnutls_x509_crt_t type + * @dn: output variable with pointer to uint8_t DN. + * + * Return the Certificate's Subject DN as a %gnutls_x509_dn_t data type, + * that can be decoded using gnutls_x509_dn_get_rdn_ava(). + * + * Note that @dn should be treated as constant. Because it points + * into the @cert object, you should not use @dn after @cert is + * deallocated. + * + * Returns: Returns 0 on success, or an error code. + **/ +int +gnutls_x509_crt_get_subject(gnutls_x509_crt_t cert, gnutls_x509_dn_t * dn) +{ + return get_dn(cert, "tbsCertificate.subject.rdnSequence", dn, 1); +} + +/** + * gnutls_x509_crt_get_issuer: + * @cert: should contain a #gnutls_x509_crt_t type + * @dn: output variable with pointer to uint8_t DN + * + * Return the Certificate's Issuer DN as a %gnutls_x509_dn_t data type, + * that can be decoded using gnutls_x509_dn_get_rdn_ava(). + * + * Note that @dn should be treated as constant. Because it points + * into the @cert object, you should not use @dn after @cert is + * deallocated. + * + * Returns: Returns 0 on success, or an error code. + **/ +int +gnutls_x509_crt_get_issuer(gnutls_x509_crt_t cert, gnutls_x509_dn_t * dn) +{ + return get_dn(cert, "tbsCertificate.issuer.rdnSequence", dn, 0); +} + +/** + * gnutls_x509_crt_get_fingerprint: + * @cert: should contain a #gnutls_x509_crt_t type + * @algo: is a digest algorithm + * @buf: a pointer to a structure to hold the fingerprint (may be null) + * @buf_size: initially holds the size of @buf + * + * This function will calculate and copy the certificate's fingerprint + * in the provided buffer. The fingerprint is a hash of the DER-encoded + * data of the certificate. + * + * If the buffer is null then only the size will be filled. + * + * Returns: %GNUTLS_E_SHORT_MEMORY_BUFFER if the provided buffer is + * not long enough, and in that case the *buf_size will be updated + * with the required size. On success 0 is returned. + **/ +int +gnutls_x509_crt_get_fingerprint(gnutls_x509_crt_t cert, + gnutls_digest_algorithm_t algo, + void *buf, size_t * buf_size) +{ + uint8_t *cert_buf; + int cert_buf_size; + int result; + gnutls_datum_t tmp; + + if (buf_size == 0 || cert == NULL) { + return GNUTLS_E_INVALID_REQUEST; + } + + cert_buf_size = 0; + result = asn1_der_coding(cert->cert, "", NULL, &cert_buf_size, NULL); + if (result != ASN1_MEM_ERROR) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + cert_buf = gnutls_malloc(cert_buf_size); + if (cert_buf == NULL) { + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; + } + + result = + asn1_der_coding(cert->cert, "", cert_buf, &cert_buf_size, + NULL); + + if (result != ASN1_SUCCESS) { + gnutls_assert(); + gnutls_free(cert_buf); + return _gnutls_asn2err(result); + } + + tmp.data = cert_buf; + tmp.size = cert_buf_size; + + result = gnutls_fingerprint(algo, &tmp, buf, buf_size); + gnutls_free(cert_buf); + + return result; +} + +/** + * gnutls_x509_crt_export: + * @cert: Holds the certificate + * @format: the format of output params. One of PEM or DER. + * @output_data: will contain a certificate PEM or DER encoded + * @output_data_size: holds the size of output_data (and will be + * replaced by the actual size of parameters) + * + * This function will export the certificate to DER or PEM format. + * + * If the buffer provided is not long enough to hold the output, then + * *output_data_size is updated and GNUTLS_E_SHORT_MEMORY_BUFFER will + * be returned. + * + * If the structure is PEM encoded, it will have a header + * of "BEGIN CERTIFICATE". + * + * Returns: In case of failure a negative error code will be + * returned, and 0 on success. + **/ +int +gnutls_x509_crt_export(gnutls_x509_crt_t cert, + gnutls_x509_crt_fmt_t format, void *output_data, + size_t * output_data_size) +{ + gnutls_datum_t out; + int ret; + + ret = gnutls_x509_crt_export2(cert, format, &out); + if (ret < 0) + return gnutls_assert_val(ret); + + if (format == GNUTLS_X509_FMT_PEM) + ret = _gnutls_copy_string(&out, (uint8_t*)output_data, output_data_size); + else + ret = _gnutls_copy_data(&out, (uint8_t*)output_data, output_data_size); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = 0; + cleanup: + gnutls_free(out.data); + return ret; +} + +/** + * gnutls_x509_crt_export2: + * @cert: Holds the certificate + * @format: the format of output params. One of PEM or DER. + * @out: will contain a certificate PEM or DER encoded + * + * This function will export the certificate to DER or PEM format. + * The output buffer is allocated using gnutls_malloc(). + * + * If the structure is PEM encoded, it will have a header + * of "BEGIN CERTIFICATE". + * + * Returns: In case of failure a negative error code will be + * returned, and 0 on success. + * + * Since: 3.1.3 + **/ +int +gnutls_x509_crt_export2(gnutls_x509_crt_t cert, + gnutls_x509_crt_fmt_t format, gnutls_datum_t * out) +{ + if (cert == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + if (!cert->modified && cert->der.size) { + if (format == GNUTLS_X509_FMT_DER) + return _gnutls_set_datum(out, cert->der.data, cert->der.size); + else { + int ret = _gnutls_fbase64_encode(PEM_X509_CERT2, + cert->der.data, + cert->der.size, + out); + if (ret < 0) + return ret; + return 0; + } + } + + return _gnutls_x509_export_int2(cert->cert, format, PEM_X509_CERT2, + out); +} + +int +_gnutls_get_key_id(gnutls_pk_params_st * params, + unsigned char *output_data, size_t * output_data_size, + unsigned flags) +{ + int ret = 0; + gnutls_datum_t der = { NULL, 0 }; + gnutls_digest_algorithm_t hash = GNUTLS_DIG_SHA1; + unsigned int digest_len; + + if ((flags & GNUTLS_KEYID_USE_SHA512) || (flags & GNUTLS_KEYID_USE_BEST_KNOWN)) + hash = GNUTLS_DIG_SHA512; + else if (flags & GNUTLS_KEYID_USE_SHA256) + hash = GNUTLS_DIG_SHA256; + + digest_len = + _gnutls_hash_get_algo_len(hash_to_entry(hash)); + + if (output_data == NULL || *output_data_size < digest_len) { + gnutls_assert(); + *output_data_size = digest_len; + return GNUTLS_E_SHORT_MEMORY_BUFFER; + } + + ret = _gnutls_x509_encode_PKI_params(&der, params); + if (ret < 0) + return gnutls_assert_val(ret); + + ret = _gnutls_hash_fast(hash, der.data, der.size, output_data); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + *output_data_size = digest_len; + + ret = 0; + + cleanup: + + _gnutls_free_datum(&der); + return ret; +} + +/** + * gnutls_x509_crt_get_key_id: + * @crt: Holds the certificate + * @flags: should be one of the flags from %gnutls_keyid_flags_t + * @output_data: will contain the key ID + * @output_data_size: holds the size of output_data (and will be + * replaced by the actual size of parameters) + * + * This function will return a unique ID that depends on the public + * key parameters. This ID can be used in checking whether a + * certificate corresponds to the given private key. + * + * If the buffer provided is not long enough to hold the output, then + * *output_data_size is updated and GNUTLS_E_SHORT_MEMORY_BUFFER will + * be returned. The output will normally be a SHA-1 hash output, + * which is 20 bytes. + * + * Returns: In case of failure a negative error code will be + * returned, and 0 on success. + **/ +int +gnutls_x509_crt_get_key_id(gnutls_x509_crt_t crt, unsigned int flags, + unsigned char *output_data, + size_t * output_data_size) +{ + int ret = 0; + gnutls_pk_params_st params; + + if (crt == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + /* initializes params */ + ret = _gnutls_x509_crt_get_mpis(crt, ¶ms); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + ret = + _gnutls_get_key_id(¶ms, output_data, output_data_size, flags); + + gnutls_pk_params_release(¶ms); + + return ret; +} + +static int +crl_issuer_matches(gnutls_x509_crl_t crl, gnutls_x509_crt_t cert) +{ + if (_gnutls_x509_compare_raw_dn + (&crl->raw_issuer_dn, &cert->raw_issuer_dn) != 0) + return 1; + else + return 0; +} + +/* This is exactly as gnutls_x509_crt_check_revocation() except that + * it calls func. + */ +int +_gnutls_x509_crt_check_revocation(gnutls_x509_crt_t cert, + const gnutls_x509_crl_t * crl_list, + int crl_list_length, + gnutls_verify_output_function func) +{ + uint8_t serial[128]; + uint8_t cert_serial[128]; + size_t serial_size, cert_serial_size; + int ret, j; + gnutls_x509_crl_iter_t iter = NULL; + + if (cert == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + for (j = 0; j < crl_list_length; j++) { /* do for all the crls */ + + /* Step 1. check if issuer's DN match + */ + ret = crl_issuer_matches(crl_list[j], cert); + if (ret == 0) { + /* issuers do not match so don't even + * bother checking. + */ + gnutls_assert(); + continue; + } + + /* Step 2. Read the certificate's serial number + */ + cert_serial_size = sizeof(cert_serial); + ret = + gnutls_x509_crt_get_serial(cert, cert_serial, + &cert_serial_size); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + /* Step 3. cycle through the CRL serials and compare with + * certificate serial we have. + */ + + iter = NULL; + do { + serial_size = sizeof(serial); + ret = + gnutls_x509_crl_iter_crt_serial(crl_list[j], + &iter, + serial, + &serial_size, + NULL); + if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { + break; + } else if (ret < 0) { + gnutls_assert(); + goto fail; + } + + if (serial_size == cert_serial_size) { + if (memcmp + (serial, cert_serial, + serial_size) == 0) { + /* serials match */ + if (func) + func(cert, NULL, + crl_list[j], + GNUTLS_CERT_REVOKED | + GNUTLS_CERT_INVALID); + ret = 1; /* revoked! */ + goto fail; + } + } + } while(1); + + gnutls_x509_crl_iter_deinit(iter); + iter = NULL; + + if (func) + func(cert, NULL, crl_list[j], 0); + + } + return 0; /* not revoked. */ + + fail: + gnutls_x509_crl_iter_deinit(iter); + return ret; +} + + +/** + * gnutls_x509_crt_check_revocation: + * @cert: should contain a #gnutls_x509_crt_t type + * @crl_list: should contain a list of gnutls_x509_crl_t types + * @crl_list_length: the length of the crl_list + * + * This function will check if the given certificate is + * revoked. It is assumed that the CRLs have been verified before. + * + * Returns: 0 if the certificate is NOT revoked, and 1 if it is. A + * negative error code is returned on error. + **/ +int +gnutls_x509_crt_check_revocation(gnutls_x509_crt_t cert, + const gnutls_x509_crl_t * crl_list, + unsigned crl_list_length) +{ + return _gnutls_x509_crt_check_revocation(cert, crl_list, + crl_list_length, NULL); +} + +/** + * gnutls_x509_crt_check_key_purpose: + * @cert: should contain a #gnutls_x509_crt_t type + * @purpose: a key purpose OID (e.g., %GNUTLS_KP_CODE_SIGNING) + * @flags: zero or %GNUTLS_KP_FLAG_DISALLOW_ANY + * + * This function will check whether the given certificate matches + * the provided key purpose. If @flags contains %GNUTLS_KP_FLAG_ALLOW_ANY then + * it a certificate marked for any purpose will not match. + * + * Returns: zero if the key purpose doesn't match, and non-zero otherwise. + * + * Since: 3.5.6 + **/ +unsigned +gnutls_x509_crt_check_key_purpose(gnutls_x509_crt_t cert, + const char *purpose, + unsigned flags) +{ + return _gnutls_check_key_purpose(cert, purpose, (flags&GNUTLS_KP_FLAG_DISALLOW_ANY)?1:0); +} + +/** + * gnutls_x509_crt_get_preferred_hash_algorithm: + * @crt: Holds the certificate + * @hash: The result of the call with the hash algorithm used for signature + * @mand: If non-zero it means that the algorithm MUST use this hash. May be %NULL. + * + * This function will read the certificate and return the appropriate digest + * algorithm to use for signing with this certificate. Some certificates (i.e. + * DSA might not be able to sign without the preferred algorithm). + * + * Deprecated: Please use gnutls_pubkey_get_preferred_hash_algorithm(). + * + * Returns: the 0 if the hash algorithm is found. A negative error code is + * returned on error. + * + * Since: 2.12.0 + **/ +int +gnutls_x509_crt_get_preferred_hash_algorithm(gnutls_x509_crt_t crt, + gnutls_digest_algorithm_t * + hash, unsigned int *mand) +{ + int ret; + gnutls_pubkey_t pubkey; + + if (crt == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + ret = gnutls_pubkey_init(&pubkey); + if (ret < 0) + return gnutls_assert_val(ret); + + ret = gnutls_pubkey_import_x509(pubkey, crt, 0); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = gnutls_pubkey_get_preferred_hash_algorithm(pubkey, hash, mand); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + cleanup: + gnutls_pubkey_deinit(pubkey); + return ret; +} + +/** + * gnutls_x509_crt_get_crl_dist_points: + * @cert: should contain a #gnutls_x509_crt_t type + * @seq: specifies the sequence number of the distribution point (0 for the first one, 1 for the second etc.) + * @san: is the place where the distribution point will be copied to + * @san_size: holds the size of ret. + * @reason_flags: Revocation reasons. An ORed sequence of flags from %gnutls_x509_crl_reason_flags_t. + * @critical: will be non-zero if the extension is marked as critical (may be null) + * + * This function retrieves the CRL distribution points (2.5.29.31), + * contained in the given certificate in the X509v3 Certificate + * Extensions. + * + * Returns: %GNUTLS_E_SHORT_MEMORY_BUFFER and updates @ret_size if + * @ret_size is not enough to hold the distribution point, or the + * type of the distribution point if everything was ok. The type is + * one of the enumerated %gnutls_x509_subject_alt_name_t. If the + * certificate does not have an Alternative name with the specified + * sequence number then %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE is + * returned. + **/ +int +gnutls_x509_crt_get_crl_dist_points(gnutls_x509_crt_t cert, + unsigned int seq, void *san, + size_t * san_size, + unsigned int *reason_flags, + unsigned int *critical) +{ + int ret; + gnutls_datum_t dist_points = { NULL, 0 }; + unsigned type; + gnutls_x509_crl_dist_points_t cdp = NULL; + gnutls_datum_t t_san; + + if (cert == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + ret = gnutls_x509_crl_dist_points_init(&cdp); + if (ret < 0) + return gnutls_assert_val(ret); + + if (reason_flags) + *reason_flags = 0; + + ret = + _gnutls_x509_crt_get_extension(cert, "2.5.29.31", 0, + &dist_points, critical); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + if (dist_points.size == 0 || dist_points.data == NULL) { + gnutls_assert(); + ret = GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + goto cleanup; + } + + ret = gnutls_x509_ext_import_crl_dist_points(&dist_points, cdp, 0); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = gnutls_x509_crl_dist_points_get(cdp, seq, &type, &t_san, reason_flags); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = _gnutls_copy_string(&t_san, san, san_size); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = type; + + cleanup: + _gnutls_free_datum(&dist_points); + if (cdp != NULL) + gnutls_x509_crl_dist_points_deinit(cdp); + + return ret; +} + +/** + * gnutls_x509_crt_get_key_purpose_oid: + * @cert: should contain a #gnutls_x509_crt_t type + * @indx: This specifies which OID to return. Use (0) to get the first one. + * @oid: a pointer to a buffer to hold the OID (may be null) + * @oid_size: initially holds the size of @oid + * @critical: output flag to indicate criticality of extension + * + * This function will extract the key purpose OIDs of the Certificate + * specified by the given index. These are stored in the Extended Key + * Usage extension (2.5.29.37) See the GNUTLS_KP_* definitions for + * human readable names. + * + * If @oid is null then only the size will be filled. The @oid + * returned will be null terminated, although @oid_size will not + * account for the trailing null. + * + * Returns: %GNUTLS_E_SHORT_MEMORY_BUFFER if the provided buffer is + * not long enough, and in that case the *oid_size will be updated + * with the required size. On success 0 is returned. + **/ +int +gnutls_x509_crt_get_key_purpose_oid(gnutls_x509_crt_t cert, + unsigned indx, void *oid, size_t * oid_size, + unsigned int *critical) +{ + int ret; + gnutls_datum_t ext; + gnutls_x509_key_purposes_t p = NULL; + gnutls_datum_t out; + + if (cert == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + if (oid) + memset(oid, 0, *oid_size); + else + *oid_size = 0; + + if ((ret = + _gnutls_x509_crt_get_extension(cert, "2.5.29.37", 0, &ext, + critical)) < 0) { + return ret; + } + + if (ext.size == 0 || ext.data == NULL) { + gnutls_assert(); + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + } + + ret = gnutls_x509_key_purpose_init(&p); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = gnutls_x509_ext_import_key_purposes(&ext, p, 0); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = gnutls_x509_key_purpose_get(p, indx, &out); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = _gnutls_copy_string(&out, oid, oid_size); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = 0; + + cleanup: + gnutls_free(ext.data); + if (p!=NULL) + gnutls_x509_key_purpose_deinit(p); + return ret; +} + +/** + * gnutls_x509_crt_get_pk_rsa_raw: + * @crt: Holds the certificate + * @m: will hold the modulus + * @e: will hold the public exponent + * + * This function will export the RSA public key's parameters found in + * the given structure. The new parameters will be allocated using + * gnutls_malloc() and will be stored in the appropriate datum. + * + * Returns: %GNUTLS_E_SUCCESS on success, otherwise a negative error code. + **/ +int +gnutls_x509_crt_get_pk_rsa_raw(gnutls_x509_crt_t crt, + gnutls_datum_t * m, gnutls_datum_t * e) +{ + int ret; + gnutls_pubkey_t pubkey; + + if (crt == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + ret = gnutls_pubkey_init(&pubkey); + if (ret < 0) + return gnutls_assert_val(ret); + + ret = gnutls_pubkey_import_x509(pubkey, crt, 0); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = gnutls_pubkey_export_rsa_raw(pubkey, m, e); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + cleanup: + gnutls_pubkey_deinit(pubkey); + return ret; +} + +/** + * gnutls_x509_crt_get_pk_ecc_raw: + * @crt: Holds the certificate + * @curve: will hold the curve + * @x: will hold the x-coordinate + * @y: will hold the y-coordinate + * + * This function will export the ECC public key's parameters found in + * the given certificate. The new parameters will be allocated using + * gnutls_malloc() and will be stored in the appropriate datum. + * + * In EdDSA curves the @y parameter will be %NULL and the other parameters + * will be in the native format for the curve. + * + * Returns: %GNUTLS_E_SUCCESS on success, otherwise a negative error code. + * + * Since: 3.4.1 + **/ +int +gnutls_x509_crt_get_pk_ecc_raw(gnutls_x509_crt_t crt, + gnutls_ecc_curve_t *curve, + gnutls_datum_t *x, gnutls_datum_t *y) +{ + int ret; + gnutls_pubkey_t pubkey; + + if (crt == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + ret = gnutls_pubkey_init(&pubkey); + if (ret < 0) + return gnutls_assert_val(ret); + + ret = gnutls_pubkey_import_x509(pubkey, crt, 0); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = gnutls_pubkey_export_ecc_raw(pubkey, curve, x, y); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + cleanup: + gnutls_pubkey_deinit(pubkey); + return ret; +} + +/** + * gnutls_x509_crt_get_pk_gost_raw: + * @crt: Holds the certificate + * @curve: will hold the curve + * @digest: will hold the digest + * @paramset: will hold the GOST parameter set ID + * @x: will hold the x-coordinate + * @y: will hold the y-coordinate + * + * This function will export the GOST public key's parameters found in + * the given certificate. The new parameters will be allocated using + * gnutls_malloc() and will be stored in the appropriate datum. + * + * Returns: %GNUTLS_E_SUCCESS on success, otherwise a negative error code. + * + * Since: 3.6.3 + **/ +int +gnutls_x509_crt_get_pk_gost_raw(gnutls_x509_crt_t crt, + gnutls_ecc_curve_t *curve, + gnutls_digest_algorithm_t *digest, + gnutls_gost_paramset_t *paramset, + gnutls_datum_t *x, gnutls_datum_t *y) +{ + int ret; + gnutls_pubkey_t pubkey; + + if (crt == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + ret = gnutls_pubkey_init(&pubkey); + if (ret < 0) + return gnutls_assert_val(ret); + + ret = gnutls_pubkey_import_x509(pubkey, crt, 0); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = gnutls_pubkey_export_gost_raw2(pubkey, curve, digest, + paramset, x, y, 0); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + cleanup: + gnutls_pubkey_deinit(pubkey); + return ret; +} + +/** + * gnutls_x509_crt_get_pk_dsa_raw: + * @crt: Holds the certificate + * @p: will hold the p + * @q: will hold the q + * @g: will hold the g + * @y: will hold the y + * + * This function will export the DSA public key's parameters found in + * the given certificate. The new parameters will be allocated using + * gnutls_malloc() and will be stored in the appropriate datum. + * + * Returns: %GNUTLS_E_SUCCESS on success, otherwise a negative error code. + **/ +int +gnutls_x509_crt_get_pk_dsa_raw(gnutls_x509_crt_t crt, + gnutls_datum_t * p, gnutls_datum_t * q, + gnutls_datum_t * g, gnutls_datum_t * y) +{ + int ret; + gnutls_pubkey_t pubkey; + + if (crt == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + ret = gnutls_pubkey_init(&pubkey); + if (ret < 0) + return gnutls_assert_val(ret); + + ret = gnutls_pubkey_import_x509(pubkey, crt, 0); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = gnutls_pubkey_export_dsa_raw(pubkey, p, q, g, y); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + cleanup: + gnutls_pubkey_deinit(pubkey); + return ret; +} + +/** + * gnutls_x509_crt_list_import2: + * @certs: Will hold the parsed certificate list. + * @size: It will contain the size of the list. + * @data: The PEM encoded certificate. + * @format: One of DER or PEM. + * @flags: must be (0) or an OR'd sequence of gnutls_certificate_import_flags. + * + * This function will convert the given PEM encoded certificate list + * to the native gnutls_x509_crt_t format. The output will be stored + * in @certs which will be allocated and initialized. + * + * If the Certificate is PEM encoded it should have a header of "X509 + * CERTIFICATE", or "CERTIFICATE". + * + * To deinitialize @certs, you need to deinitialize each crt structure + * independently, and use gnutls_free() at @certs. + * + * Returns: %GNUTLS_E_SUCCESS on success, otherwise a negative error code. + * + * Since: 3.0 + **/ +int +gnutls_x509_crt_list_import2(gnutls_x509_crt_t ** certs, + unsigned int *size, + const gnutls_datum_t * data, + gnutls_x509_crt_fmt_t format, + unsigned int flags) +{ + unsigned int init = 1024; + int ret; + + *certs = _gnutls_reallocarray(NULL, init, sizeof(gnutls_x509_crt_t)); + if (*certs == NULL) { + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; + } + + ret = + gnutls_x509_crt_list_import(*certs, &init, data, format, + flags | GNUTLS_X509_CRT_LIST_IMPORT_FAIL_IF_EXCEED); + if (ret == GNUTLS_E_SHORT_MEMORY_BUFFER) { + *certs = _gnutls_reallocarray_fast(*certs, init, + sizeof(gnutls_x509_crt_t)); + if (*certs == NULL) { + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; + } + + ret = + gnutls_x509_crt_list_import(*certs, &init, data, + format, flags); + } + + if (ret < 0) { + gnutls_free(*certs); + return ret; + } + + *size = init; + return 0; +} + +/** + * gnutls_x509_crt_list_import: + * @certs: Indicates where the parsed list will be copied to. Must not be initialized. + * @cert_max: Initially must hold the maximum number of certs. It will be updated with the number of certs available. + * @data: The PEM encoded certificate. + * @format: One of DER or PEM. + * @flags: must be (0) or an OR'd sequence of gnutls_certificate_import_flags. + * + * This function will convert the given PEM encoded certificate list + * to the native gnutls_x509_crt_t format. The output will be stored + * in @certs. They will be automatically initialized. + * + * The flag %GNUTLS_X509_CRT_LIST_IMPORT_FAIL_IF_EXCEED will cause + * import to fail if the certificates in the provided buffer are more + * than the available structures. The %GNUTLS_X509_CRT_LIST_FAIL_IF_UNSORTED + * flag will cause the function to fail if the provided list is not + * sorted from subject to issuer. + * + * If the Certificate is PEM encoded it should have a header of "X509 + * CERTIFICATE", or "CERTIFICATE". + * + * Returns: the number of certificates read or a negative error value. + **/ +int +gnutls_x509_crt_list_import(gnutls_x509_crt_t * certs, + unsigned int *cert_max, + const gnutls_datum_t * data, + gnutls_x509_crt_fmt_t format, + unsigned int flags) +{ + int size; + const char *ptr; + gnutls_datum_t tmp; + int ret, nocopy = 0; + unsigned int count = 0, j, copied = 0; + + if (format == GNUTLS_X509_FMT_DER) { + if (*cert_max < 1) { + *cert_max = 1; + return GNUTLS_E_SHORT_MEMORY_BUFFER; + } + + count = 1; /* import only the first one */ + + ret = gnutls_x509_crt_init(&certs[0]); + if (ret < 0) { + gnutls_assert(); + goto error; + } + + ret = gnutls_x509_crt_import(certs[0], data, format); + if (ret < 0) { + gnutls_assert(); + goto error; + } + + *cert_max = 1; + return 1; + } + + /* move to the certificate + */ + ptr = memmem(data->data, data->size, + PEM_CERT_SEP, sizeof(PEM_CERT_SEP) - 1); + if (ptr == NULL) + ptr = memmem(data->data, data->size, + PEM_CERT_SEP2, sizeof(PEM_CERT_SEP2) - 1); + + if (ptr == NULL) + return gnutls_assert_val(GNUTLS_E_NO_CERTIFICATE_FOUND); + + count = 0; + + do { + if (count >= *cert_max) { + if (! + (flags & + GNUTLS_X509_CRT_LIST_IMPORT_FAIL_IF_EXCEED)) + break; + else + nocopy = 1; + } + + if (!nocopy) { + ret = gnutls_x509_crt_init(&certs[count]); + if (ret < 0) { + gnutls_assert(); + goto error; + } + + tmp.data = (void *) ptr; + tmp.size = + data->size - (ptr - (char *) data->data); + + ret = + gnutls_x509_crt_import(certs[count], &tmp, + GNUTLS_X509_FMT_PEM); + if (ret < 0) { + count++; + gnutls_assert(); + goto error; + } + + copied++; + } + + /* now we move ptr after the pem header + */ + ptr++; + /* find the next certificate (if any) + */ + size = data->size - (ptr - (char *) data->data); + + if (size > 0) { + char *ptr2; + + ptr2 = + memmem(ptr, size, PEM_CERT_SEP, + sizeof(PEM_CERT_SEP) - 1); + if (ptr2 == NULL) + ptr2 = memmem(ptr, size, PEM_CERT_SEP2, + sizeof(PEM_CERT_SEP2) - 1); + + ptr = ptr2; + } else + ptr = NULL; + + count++; + } + while (ptr != NULL); + + *cert_max = count; + + if (nocopy == 0) { + if (flags & GNUTLS_X509_CRT_LIST_SORT && *cert_max > 1) { + if (*cert_max > DEFAULT_MAX_VERIFY_DEPTH) { + ret = GNUTLS_E_UNIMPLEMENTED_FEATURE; + goto error; + } + count = _gnutls_sort_clist(certs, *cert_max); + if (count < *cert_max) { + for (j = count; j < *cert_max; j++) { + gnutls_x509_crt_deinit(certs[j]); + } + } + *cert_max = count; + } + + if (flags & GNUTLS_X509_CRT_LIST_FAIL_IF_UNSORTED) { + ret = _gnutls_check_if_sorted(certs, *cert_max); + if (ret < 0) { + gnutls_assert(); + goto error; + } + } + + return count; + } else { + count = copied; + ret = GNUTLS_E_SHORT_MEMORY_BUFFER; + } + + error: + for (j = 0; j < count; j++) + gnutls_x509_crt_deinit(certs[j]); + return ret; +} + +/** + * gnutls_x509_crt_get_subject_unique_id: + * @crt: Holds the certificate + * @buf: user allocated memory buffer, will hold the unique id + * @buf_size: size of user allocated memory buffer (on input), will hold + * actual size of the unique ID on return. + * + * This function will extract the subjectUniqueID value (if present) for + * the given certificate. + * + * If the user allocated memory buffer is not large enough to hold the + * full subjectUniqueID, then a GNUTLS_E_SHORT_MEMORY_BUFFER error will be + * returned, and buf_size will be set to the actual length. + * + * This function had a bug prior to 3.4.8 that prevented the setting + * of %NULL @buf to discover the @buf_size. To use this function safely + * with the older versions the @buf must be a valid buffer that can hold + * at least a single byte if @buf_size is zero. + * + * Returns: %GNUTLS_E_SUCCESS on success, otherwise a negative error code. + **/ +int +gnutls_x509_crt_get_subject_unique_id(gnutls_x509_crt_t crt, char *buf, + size_t * buf_size) +{ + int result; + gnutls_datum_t datum = { NULL, 0 }; + + result = + _gnutls_x509_read_value(crt->cert, + "tbsCertificate.subjectUniqueID", + &datum); + if (result < 0) + return gnutls_assert_val(result); + + if (datum.size > *buf_size) { /* then we're not going to fit */ + *buf_size = datum.size; + result = GNUTLS_E_SHORT_MEMORY_BUFFER; + } else { + *buf_size = datum.size; + memcpy(buf, datum.data, datum.size); + } + + _gnutls_free_datum(&datum); + + return result; +} + +/** + * gnutls_x509_crt_get_issuer_unique_id: + * @crt: Holds the certificate + * @buf: user allocated memory buffer, will hold the unique id + * @buf_size: size of user allocated memory buffer (on input), will hold + * actual size of the unique ID on return. + * + * This function will extract the issuerUniqueID value (if present) for + * the given certificate. + * + * If the user allocated memory buffer is not large enough to hold the + * full subjectUniqueID, then a GNUTLS_E_SHORT_MEMORY_BUFFER error will be + * returned, and buf_size will be set to the actual length. + * + * This function had a bug prior to 3.4.8 that prevented the setting + * of %NULL @buf to discover the @buf_size. To use this function safely + * with the older versions the @buf must be a valid buffer that can hold + * at least a single byte if @buf_size is zero. + * + * Returns: %GNUTLS_E_SUCCESS on success, otherwise a negative error code. + * + * Since: 2.12.0 + **/ +int +gnutls_x509_crt_get_issuer_unique_id(gnutls_x509_crt_t crt, char *buf, + size_t * buf_size) +{ + int result; + gnutls_datum_t datum = { NULL, 0 }; + + result = + _gnutls_x509_read_value(crt->cert, + "tbsCertificate.issuerUniqueID", + &datum); + if (result < 0) + return gnutls_assert_val(result); + + if (datum.size > *buf_size) { /* then we're not going to fit */ + *buf_size = datum.size; + result = GNUTLS_E_SHORT_MEMORY_BUFFER; + } else { + *buf_size = datum.size; + memcpy(buf, datum.data, datum.size); + } + + _gnutls_free_datum(&datum); + + return result; +} + +static int +legacy_parse_aia(asn1_node src, + unsigned int seq, int what, gnutls_datum_t * data) +{ + int len; + char nptr[MAX_NAME_SIZE]; + int result; + gnutls_datum_t d; + const char *oid = NULL; + + seq++; /* 0->1, 1->2 etc */ + switch (what) { + case GNUTLS_IA_ACCESSMETHOD_OID: + snprintf(nptr, sizeof(nptr), "?%u.accessMethod", seq); + break; + + case GNUTLS_IA_ACCESSLOCATION_GENERALNAME_TYPE: + snprintf(nptr, sizeof(nptr), "?%u.accessLocation", seq); + break; + + case GNUTLS_IA_CAISSUERS_URI: + oid = GNUTLS_OID_AD_CAISSUERS; + FALLTHROUGH; + + case GNUTLS_IA_OCSP_URI: + if (oid == NULL) + oid = GNUTLS_OID_AD_OCSP; + { + char tmpoid[MAX_OID_SIZE]; + snprintf(nptr, sizeof(nptr), "?%u.accessMethod", + seq); + len = sizeof(tmpoid); + result = asn1_read_value(src, nptr, tmpoid, &len); + + if (result == ASN1_VALUE_NOT_FOUND + || result == ASN1_ELEMENT_NOT_FOUND) + return + gnutls_assert_val + (GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE); + + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + if ((unsigned) len != strlen(oid) + 1 + || memcmp(tmpoid, oid, len) != 0) + return + gnutls_assert_val + (GNUTLS_E_UNKNOWN_ALGORITHM); + } + FALLTHROUGH; + + case GNUTLS_IA_URI: + snprintf(nptr, sizeof(nptr), + "?%u.accessLocation.uniformResourceIdentifier", + seq); + break; + + default: + return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + } + + len = 0; + result = asn1_read_value(src, nptr, NULL, &len); + if (result == ASN1_VALUE_NOT_FOUND + || result == ASN1_ELEMENT_NOT_FOUND) + return + gnutls_assert_val + (GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE); + + if (result != ASN1_MEM_ERROR) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + d.size = len; + + d.data = gnutls_malloc(d.size); + if (d.data == NULL) + return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + + result = asn1_read_value(src, nptr, d.data, &len); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + gnutls_free(d.data); + return _gnutls_asn2err(result); + } + + if (data) { + data->data = d.data; + data->size = d.size; + } else + gnutls_free(d.data); + + return 0; +} + +/** + * gnutls_x509_crt_get_authority_info_access: + * @crt: Holds the certificate + * @seq: specifies the sequence number of the access descriptor (0 for the first one, 1 for the second etc.) + * @what: what data to get, a #gnutls_info_access_what_t type. + * @data: output data to be freed with gnutls_free(). + * @critical: pointer to output integer that is set to non-zero if the extension is marked as critical (may be %NULL) + * + * Note that a simpler API to access the authority info data is provided + * by gnutls_x509_aia_get() and gnutls_x509_ext_import_aia(). + * + * This function extracts the Authority Information Access (AIA) + * extension, see RFC 5280 section 4.2.2.1 for more information. The + * AIA extension holds a sequence of AccessDescription (AD) data. + * + * The @seq input parameter is used to indicate which member of the + * sequence the caller is interested in. The first member is 0, the + * second member 1 and so on. When the @seq value is out of bounds, + * %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE is returned. + * + * The type of data returned in @data is specified via @what which + * should be #gnutls_info_access_what_t values. + * + * If @what is %GNUTLS_IA_ACCESSMETHOD_OID then @data will hold the + * accessMethod OID (e.g., "1.3.6.1.5.5.7.48.1"). + * + * If @what is %GNUTLS_IA_ACCESSLOCATION_GENERALNAME_TYPE, @data will + * hold the accessLocation GeneralName type (e.g., + * "uniformResourceIdentifier"). + * + * If @what is %GNUTLS_IA_URI, @data will hold the accessLocation URI + * data. Requesting this @what value leads to an error if the + * accessLocation is not of the "uniformResourceIdentifier" type. + * + * If @what is %GNUTLS_IA_OCSP_URI, @data will hold the OCSP URI. + * Requesting this @what value leads to an error if the accessMethod + * is not 1.3.6.1.5.5.7.48.1 aka OCSP, or if accessLocation is not of + * the "uniformResourceIdentifier" type. In that case %GNUTLS_E_UNKNOWN_ALGORITHM + * will be returned, and @seq should be increased and this function + * called again. + * + * If @what is %GNUTLS_IA_CAISSUERS_URI, @data will hold the caIssuers + * URI. Requesting this @what value leads to an error if the + * accessMethod is not 1.3.6.1.5.5.7.48.2 aka caIssuers, or if + * accessLocation is not of the "uniformResourceIdentifier" type. + * In that case handle as in %GNUTLS_IA_OCSP_URI. + * + * More @what values may be allocated in the future as needed. + * + * If @data is NULL, the function does the same without storing the + * output data, that is, it will set @critical and do error checking + * as usual. + * + * The value of the critical flag is returned in *@critical. Supply a + * NULL @critical if you want the function to make sure the extension + * is non-critical, as required by RFC 5280. + * + * Returns: %GNUTLS_E_SUCCESS on success, %GNUTLS_E_INVALID_REQUEST on + * invalid @crt, %GNUTLS_E_CONSTRAINT_ERROR if the extension is + * incorrectly marked as critical (use a non-NULL @critical to + * override), %GNUTLS_E_UNKNOWN_ALGORITHM if the requested OID does + * not match (e.g., when using %GNUTLS_IA_OCSP_URI), otherwise a + * negative error code. + * + * Since: 3.0 + **/ +int +gnutls_x509_crt_get_authority_info_access(gnutls_x509_crt_t crt, + unsigned int seq, + int what, + gnutls_datum_t * data, + unsigned int *critical) +{ + int ret; + gnutls_datum_t aia; + asn1_node c2 = NULL; + + if (crt == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + if ((ret = + _gnutls_x509_crt_get_extension(crt, GNUTLS_OID_AIA, 0, &aia, + critical)) < 0) + return ret; + + if (aia.size == 0 || aia.data == NULL) { + gnutls_assert(); + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + } + + if (critical && *critical) + return GNUTLS_E_CONSTRAINT_ERROR; + + ret = asn1_create_element(_gnutls_get_pkix(), + "PKIX1.AuthorityInfoAccessSyntax", &c2); + if (ret != ASN1_SUCCESS) { + gnutls_assert(); + _gnutls_free_datum(&aia); + return _gnutls_asn2err(ret); + } + + ret = _asn1_strict_der_decode(&c2, aia.data, aia.size, NULL); + /* asn1_print_structure (stdout, c2, "", ASN1_PRINT_ALL); */ + _gnutls_free_datum(&aia); + if (ret != ASN1_SUCCESS) { + gnutls_assert(); + asn1_delete_structure(&c2); + return _gnutls_asn2err(ret); + } + + ret = legacy_parse_aia(c2, seq, what, data); + + asn1_delete_structure(&c2); + if (ret < 0) + gnutls_assert(); + + return ret; +} + +/** + * gnutls_x509_crt_set_pin_function: + * @crt: The certificate structure + * @fn: the callback + * @userdata: data associated with the callback + * + * This function will set a callback function to be used when + * it is required to access a protected object. This function overrides + * the global function set using gnutls_pkcs11_set_pin_function(). + * + * Note that this callback is currently used only during the import + * of a PKCS #11 certificate with gnutls_x509_crt_import_url(). + * + * Since: 3.1.0 + * + **/ +void gnutls_x509_crt_set_pin_function(gnutls_x509_crt_t crt, + gnutls_pin_callback_t fn, + void *userdata) +{ + if (crt) { + crt->pin.cb = fn; + crt->pin.data = userdata; + } +} + +/** + * gnutls_x509_crt_import_url: + * @crt: A certificate of type #gnutls_x509_crt_t + * @url: A PKCS 11 url + * @flags: One of GNUTLS_PKCS11_OBJ_* flags for PKCS#11 URLs or zero otherwise + * + * This function will import a certificate present in a PKCS#11 token + * or any type of back-end that supports URLs. + * + * In previous versions of gnutls this function was named + * gnutls_x509_crt_import_pkcs11_url, and the old name is + * an alias to this one. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 3.4.0 + **/ +int +gnutls_x509_crt_import_url(gnutls_x509_crt_t crt, + const char *url, unsigned int flags) +{ + int ret; + unsigned i; + + for (i=0;i<_gnutls_custom_urls_size;i++) { + if (strncmp(url, _gnutls_custom_urls[i].name, _gnutls_custom_urls[i].name_size) == 0) { + if (_gnutls_custom_urls[i].import_crt) { + ret = _gnutls_custom_urls[i].import_crt(crt, url, flags); + goto cleanup; + } + break; + } + } + + if (strncmp(url, SYSTEM_URL, SYSTEM_URL_SIZE) == 0) { + ret = _gnutls_x509_crt_import_system_url(crt, url); +#ifdef ENABLE_PKCS11 + } else if (strncmp(url, PKCS11_URL, PKCS11_URL_SIZE) == 0) { + ret = _gnutls_x509_crt_import_pkcs11_url(crt, url, flags); +#endif + } else { + ret = gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + } + + cleanup: + return ret; +} + +/** + * gnutls_x509_crt_list_import_url: + * @certs: Will hold the allocated certificate list. + * @size: It will contain the size of the list. + * @url: A PKCS 11 url + * @pin_fn: a PIN callback if not globally set + * @pin_fn_userdata: parameter for the PIN callback + * @flags: One of GNUTLS_PKCS11_OBJ_* flags for PKCS#11 URLs or zero otherwise + * + * This function will import a certificate chain present in a PKCS#11 token + * or any type of back-end that supports URLs. The certificates + * must be deinitialized afterwards using gnutls_x509_crt_deinit() + * and the returned pointer must be freed using gnutls_free(). + * + * The URI provided must be the first certificate in the chain; subsequent + * certificates will be retrieved using gnutls_pkcs11_get_raw_issuer() or + * equivalent functionality for the supported URI. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 3.6.3 + **/ +int +gnutls_x509_crt_list_import_url(gnutls_x509_crt_t **certs, + unsigned int *size, + const char *url, + gnutls_pin_callback_t pin_fn, + void *pin_fn_userdata, + unsigned int flags) +{ + int ret; + unsigned i; + gnutls_x509_crt_t crts[DEFAULT_MAX_VERIFY_DEPTH]; + gnutls_datum_t issuer = {NULL, 0}; + unsigned total = 0; + + memset(crts, 0, sizeof(crts)); + + ret = gnutls_x509_crt_init(&crts[0]); + if (ret < 0) + return gnutls_assert_val(ret); + + gnutls_x509_crt_set_pin_function(crts[0], pin_fn, pin_fn_userdata); + + total = 1; + + ret = gnutls_x509_crt_import_url(crts[0], url, flags); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + for (i=1;i<DEFAULT_MAX_VERIFY_DEPTH;i++) { + ret = _gnutls_get_raw_issuer(url, crts[i-1], &issuer, flags|GNUTLS_PKCS11_OBJ_FLAG_RETRIEVE_ANY); + if (ret < 0) { + issuer.data = NULL; + break; + } + + if (gnutls_x509_crt_equals2(crts[i-1], &issuer)) { + gnutls_free(issuer.data); + break; + } + + ret = gnutls_x509_crt_init(&crts[i]); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + total++; + + gnutls_x509_crt_set_pin_function(crts[i], pin_fn, pin_fn_userdata); + + ret = gnutls_x509_crt_import(crts[i], &issuer, GNUTLS_X509_FMT_DER); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + gnutls_free(issuer.data); + } + + *certs = _gnutls_reallocarray(NULL, total, sizeof(gnutls_x509_crt_t)); + if (*certs == NULL) { + ret = GNUTLS_E_MEMORY_ERROR; + goto cleanup; + } + + memcpy(*certs, crts, total*sizeof(gnutls_x509_crt_t)); + *size = total; + + return 0; + cleanup: + gnutls_free(issuer.data); + for (i=0;i<total;i++) + gnutls_x509_crt_deinit(crts[i]); + + return ret; +} + +/*- + * gnutls_x509_crt_verify_data3: + * @crt: Holds the certificate to verify with + * @algo: The signature algorithm used + * @flags: Zero or an OR list of #gnutls_certificate_verify_flags + * @data: holds the signed data + * @signature: contains the signature + * + * This function will verify the given signed data, using the + * parameters from the certificate. + * + * Returns: In case of a verification failure %GNUTLS_E_PK_SIG_VERIFY_FAILED + * is returned, %GNUTLS_E_EXPIRED or %GNUTLS_E_NOT_YET_ACTIVATED on expired + * or not yet activated certificate and zero or positive code on success. + * + * Since: 3.5.6 + -*/ +int +gnutls_x509_crt_verify_data3(gnutls_x509_crt_t crt, + gnutls_sign_algorithm_t algo, + gnutls_typed_vdata_st *vdata, + unsigned int vdata_size, + const gnutls_datum_t *data, + const gnutls_datum_t *signature, + unsigned int flags) +{ + int ret; + gnutls_pubkey_t pubkey; + + if (crt == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + + ret = gnutls_pubkey_init(&pubkey); + if (ret < 0) + return gnutls_assert_val(ret); + + ret = gnutls_pubkey_import_x509(pubkey, crt, 0); + if (ret < 0) + return gnutls_assert_val(ret); + + ret = gnutls_pubkey_verify_data2(pubkey, algo, flags, data, signature); + gnutls_pubkey_deinit(pubkey); + + if (ret >= 0) { + time_t now = gnutls_time(0); + int res; + unsigned usage, i; + + if (!(flags & GNUTLS_VERIFY_DISABLE_TRUSTED_TIME_CHECKS) || + !(flags & GNUTLS_VERIFY_DISABLE_TIME_CHECKS)) { + if (now > gnutls_x509_crt_get_expiration_time(crt)) { + return gnutls_assert_val(GNUTLS_E_EXPIRED); + } + + if (now < gnutls_x509_crt_get_activation_time(crt)) { + return gnutls_assert_val(GNUTLS_E_NOT_YET_ACTIVATED); + } + } + + res = gnutls_x509_crt_get_key_usage(crt, &usage, NULL); + if (res >= 0) { + if (!(usage & GNUTLS_KEY_DIGITAL_SIGNATURE)) { + return gnutls_assert_val(GNUTLS_CERT_SIGNER_CONSTRAINTS_FAILURE); + } + } + + for (i=0;i<vdata_size;i++) { + if (vdata[i].type == GNUTLS_DT_KEY_PURPOSE_OID) { + res = _gnutls_check_key_purpose(crt, (char *)vdata[i].data, 0); + if (res == 0) + return gnutls_assert_val(GNUTLS_CERT_SIGNER_CONSTRAINTS_FAILURE); + break; + } + } + } + + return ret; +} + +/** + * gnutls_x509_crt_verify_data2: + * @crt: Holds the certificate to verify with + * @algo: The signature algorithm used + * @flags: Zero or an OR list of #gnutls_certificate_verify_flags + * @data: holds the signed data + * @signature: contains the signature + * + * This function will verify the given signed data, using the + * parameters from the certificate. + * + * Returns: In case of a verification failure %GNUTLS_E_PK_SIG_VERIFY_FAILED + * is returned, %GNUTLS_E_EXPIRED or %GNUTLS_E_NOT_YET_ACTIVATED on expired + * or not yet activated certificate and zero or positive code on success. + * + * Note that since GnuTLS 3.5.6 this function introduces checks in the + * end certificate (@crt), including time checks and key usage checks. + * + * Since: 3.4.0 + **/ +int +gnutls_x509_crt_verify_data2(gnutls_x509_crt_t crt, + gnutls_sign_algorithm_t algo, + unsigned int flags, + const gnutls_datum_t *data, + const gnutls_datum_t *signature) +{ + return gnutls_x509_crt_verify_data3(crt, algo, NULL, 0, + data, signature, flags); +} + +/** + * gnutls_x509_crt_set_flags: + * @cert: A type #gnutls_x509_crt_t + * @flags: flags from the %gnutls_x509_crt_flags + * + * This function will set flags for the specified certificate. + * Currently this is useful for the %GNUTLS_X509_CRT_FLAG_IGNORE_SANITY + * which allows importing certificates even if they have known issues. + * + * Since: 3.6.0 + * + **/ +void gnutls_x509_crt_set_flags(gnutls_x509_crt_t cert, + unsigned int flags) +{ + cert->flags = flags; +} + diff --git a/lib/x509/x509_dn.c b/lib/x509/x509_dn.c new file mode 100644 index 0000000..1dde410 --- /dev/null +++ b/lib/x509/x509_dn.c @@ -0,0 +1,702 @@ +/* + * Copyright (C) 2013-2016 Nikos Mavrogiannopoulos + * Copyright (C) 2016 Red Hat, Inc. + * + * This file is part of GnuTLS. + * + * The GnuTLS is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/> + * + */ + +/* This file contains functions to handle X.509 certificate generation. + */ + +#include "gnutls_int.h" + +#include <datum.h> +#include <global.h> +#include "errors.h" +#include <common.h> +#include <x509.h> +#include <x509_b64.h> +#include <c-ctype.h> + +typedef int (*set_dn_func) (void *, const char *oid, unsigned int raw_flag, + const void *name, unsigned int name_size); + +static +int dn_attr_crt_set(set_dn_func f, void *crt, const gnutls_datum_t * name, + const gnutls_datum_t * val, unsigned is_raw) +{ + char _oid[MAX_OID_SIZE]; + gnutls_datum_t tmp; + const char *oid; + int ret; + unsigned i,j; + + if (name->size == 0 || val->size == 0) + return gnutls_assert_val(GNUTLS_E_PARSING_ERROR); + + if (c_isdigit(name->data[0]) != 0) { + if (name->size >= sizeof(_oid)) + return gnutls_assert_val(GNUTLS_E_PARSING_ERROR); + + memcpy(_oid, name->data, name->size); + _oid[name->size] = 0; + + oid = _oid; + + if (gnutls_x509_dn_oid_known(oid) == 0 && !is_raw) { + _gnutls_debug_log("Unknown OID: '%s'\n", oid); + return gnutls_assert_val(GNUTLS_E_PARSING_ERROR); + } + } else { + oid = + _gnutls_ldap_string_to_oid((char *) name->data, + name->size); + } + + if (oid == NULL) { + _gnutls_debug_log("Unknown DN attribute: '%.*s'\n", + (int) name->size, name->data); + return gnutls_assert_val(GNUTLS_E_PARSING_ERROR); + } + + if (is_raw) { + gnutls_datum_t hex = {val->data+1, val->size-1}; + + ret = gnutls_hex_decode2(&hex, &tmp); + if (ret < 0) + return gnutls_assert_val(GNUTLS_E_PARSING_ERROR); + } else { + tmp.size = val->size; + tmp.data = gnutls_malloc(tmp.size+1); + if (tmp.data == NULL) { + return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + } + + /* unescape */ + for (j=i=0;i<tmp.size;i++) { + if (1+j!=val->size && val->data[j] == '\\') { + if (val->data[j+1] == ',' || val->data[j+1] == '#' || + val->data[j+1] == ' ' || val->data[j+1] == '+' || + val->data[j+1] == '"' || val->data[j+1] == '<' || + val->data[j+1] == '>' || val->data[j+1] == ';' || + val->data[j+1] == '\\' || val->data[j+1] == '=') { + tmp.data[i] = val->data[j+1]; + j+=2; + tmp.size--; + } else { + ret = gnutls_assert_val(GNUTLS_E_PARSING_ERROR); + goto fail; + } + } else { + tmp.data[i] = val->data[j++]; + } + } + tmp.data[tmp.size] = 0; + } + + ret = f(crt, oid, is_raw, tmp.data, tmp.size); + if (ret < 0) { + gnutls_assert(); + goto fail; + } + + ret = 0; + fail: + gnutls_free(tmp.data); + return ret; +} + +static int read_attr_and_val(const char **ptr, + gnutls_datum_t *name, gnutls_datum_t *val, + unsigned *is_raw) +{ + const unsigned char *p = (void *) *ptr; + + *is_raw = 0; + + /* skip any space */ + while (c_isspace(*p)) + p++; + + /* Read the name */ + name->data = (void *) p; + while (*p != '=' && *p != 0 && !c_isspace(*p)) + p++; + + name->size = p - name->data; + + /* skip any space */ + while (c_isspace(*p)) + p++; + + if (*p != '=') + return gnutls_assert_val(GNUTLS_E_PARSING_ERROR); + p++; + + while (c_isspace(*p)) + p++; + + if (*p == '#') { + *is_raw = 1; + } + + /* Read value */ + val->data = (void *) p; + while (*p != 0 && (*p != ',' || (*p == ',' && *(p - 1) == '\\')) + && *p != '\n') { + p++; + } + val->size = p - (val->data); + *ptr = (void*)p; + + p = val->data; + /* check for unescaped '+' - we do not support them */ + while (*p != 0) { + if (*p == '+' && (*(p - 1) != '\\')) + return gnutls_assert_val(GNUTLS_E_PARSING_ERROR); + p++; + } + + /* remove spaces from the end */ + while(val->size > 0 && c_isspace(val->data[val->size-1])) { + if (val->size > 2 && val->data[val->size-2] == '\\') + break; + val->size--; + } + + if (val->size == 0 || name->size == 0) + return gnutls_assert_val(GNUTLS_E_PARSING_ERROR); + + return 0; +} + +typedef struct elem_list_st { + gnutls_datum_t name; + gnutls_datum_t val; + const char *pos; + unsigned is_raw; + struct elem_list_st *next; +} elem_list_st; + +static int add_new_elem(elem_list_st **head, const gnutls_datum_t *name, const gnutls_datum_t *val, const char *pos, unsigned is_raw) +{ + elem_list_st *elem = gnutls_malloc(sizeof(*elem)); + if (elem == NULL) + return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + + memcpy(&elem->name, name, sizeof(*name)); + memcpy(&elem->val, val, sizeof(*val)); + elem->pos = pos; + elem->is_raw = is_raw; + elem->next = *head; + *head = elem; + + return 0; +} + +static int +crt_set_dn(set_dn_func f, void *crt, const char *dn, const char **err) +{ + const char *p = dn; + int ret; + gnutls_datum_t name, val; + unsigned is_raw; + elem_list_st *list = NULL, *plist, *next; + + if (crt == NULL || dn == NULL) + return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + + /* We parse the string and set all elements to a linked list in + * reverse order. That way we can encode in reverse order, + * the way RFC4514 requires. */ + + /* For each element */ + while (*p != 0 && *p != '\n') { + if (err) + *err = p; + + is_raw = 0; + ret = read_attr_and_val(&p, &name, &val, &is_raw); + if (ret < 0) { + gnutls_assert(); + goto fail; + } + + /* skip spaces and look for comma */ + while (c_isspace(*p)) + p++; + + ret = add_new_elem(&list, &name, &val, p, is_raw); + if (ret < 0) { + gnutls_assert(); + goto fail; + } + + if (*p != ',' && *p != 0 && *p != '\n') { + ret = gnutls_assert_val(GNUTLS_E_PARSING_ERROR); + goto fail; + } + if (*p == ',') + p++; + } + + plist = list; + while(plist) { + if (err) + *err = plist->pos; + ret = dn_attr_crt_set(f, crt, &plist->name, &plist->val, plist->is_raw); + if (ret < 0) + goto fail; + + plist = plist->next; + } + + ret = 0; +fail: + plist = list; + while(plist) { + next = plist->next; + gnutls_free(plist); + plist = next; + } + return ret; +} + + +/** + * gnutls_x509_crt_set_dn: + * @crt: a certificate of type #gnutls_x509_crt_t + * @dn: a comma separated DN string (RFC4514) + * @err: indicates the error position (if any) + * + * This function will set the DN on the provided certificate. + * The input string should be plain ASCII or UTF-8 encoded. On + * DN parsing error %GNUTLS_E_PARSING_ERROR is returned. + * + * Note that DNs are not expected to hold DNS information, and thus + * no automatic IDNA conversions are attempted when using this function. + * If that is required (e.g., store a domain in CN), process the corresponding + * input with gnutls_idna_map(). + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int +gnutls_x509_crt_set_dn(gnutls_x509_crt_t crt, const char *dn, + const char **err) +{ + return crt_set_dn((set_dn_func) gnutls_x509_crt_set_dn_by_oid, crt, + dn, err); +} + +/** + * gnutls_x509_crt_set_issuer_dn: + * @crt: a certificate of type #gnutls_x509_crt_t + * @dn: a comma separated DN string (RFC4514) + * @err: indicates the error position (if any) + * + * This function will set the DN on the provided certificate. + * The input string should be plain ASCII or UTF-8 encoded. On + * DN parsing error %GNUTLS_E_PARSING_ERROR is returned. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int +gnutls_x509_crt_set_issuer_dn(gnutls_x509_crt_t crt, const char *dn, + const char **err) +{ + return crt_set_dn((set_dn_func) + gnutls_x509_crt_set_issuer_dn_by_oid, crt, dn, + err); +} + +/** + * gnutls_x509_crq_set_dn: + * @crq: a certificate of type #gnutls_x509_crq_t + * @dn: a comma separated DN string (RFC4514) + * @err: indicates the error position (if any) + * + * This function will set the DN on the provided certificate. + * The input string should be plain ASCII or UTF-8 encoded. On + * DN parsing error %GNUTLS_E_PARSING_ERROR is returned. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int +gnutls_x509_crq_set_dn(gnutls_x509_crq_t crq, const char *dn, + const char **err) +{ + return crt_set_dn((set_dn_func) gnutls_x509_crq_set_dn_by_oid, crq, + dn, err); +} + +static +int set_dn_by_oid(gnutls_x509_dn_t dn, const char *oid, unsigned int raw_flag, const void *name, unsigned name_size) +{ + return _gnutls_x509_set_dn_oid(dn->asn, "", oid, raw_flag, name, name_size); +} + +/** + * gnutls_x509_dn_set_str: + * @dn: a pointer to DN + * @str: a comma separated DN string (RFC4514) + * @err: indicates the error position (if any) + * + * This function will set the DN on the provided DN structure. + * The input string should be plain ASCII or UTF-8 encoded. On + * DN parsing error %GNUTLS_E_PARSING_ERROR is returned. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 3.5.3 + **/ +int +gnutls_x509_dn_set_str(gnutls_x509_dn_t dn, const char *str, const char **err) +{ + if (dn == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + return crt_set_dn((set_dn_func) set_dn_by_oid, dn, + str, err); +} + +/** + * gnutls_x509_dn_init: + * @dn: the object to be initialized + * + * This function initializes a #gnutls_x509_dn_t type. + * + * The object returned must be deallocated using + * gnutls_x509_dn_deinit(). + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 2.4.0 + **/ +int gnutls_x509_dn_init(gnutls_x509_dn_t * dn) +{ + int result; + + *dn = gnutls_calloc(1, sizeof(gnutls_x509_dn_st)); + + if ((result = + asn1_create_element(_gnutls_get_pkix(), + "PKIX1.Name", &(*dn)->asn)) != ASN1_SUCCESS) { + gnutls_assert(); + gnutls_free(*dn); + return _gnutls_asn2err(result); + } + + return 0; +} + +/** + * gnutls_x509_dn_import: + * @dn: the structure that will hold the imported DN + * @data: should contain a DER encoded RDN sequence + * + * This function parses an RDN sequence and stores the result to a + * #gnutls_x509_dn_t type. The data must have been initialized + * with gnutls_x509_dn_init(). You may use gnutls_x509_dn_get_rdn_ava() to + * decode the DN. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 2.4.0 + **/ +int gnutls_x509_dn_import(gnutls_x509_dn_t dn, const gnutls_datum_t * data) +{ + int result; + char err[ASN1_MAX_ERROR_DESCRIPTION_SIZE]; + + if (data->data == NULL || data->size == 0) + return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + + result = _asn1_strict_der_decode(&dn->asn, + data->data, data->size, err); + if (result != ASN1_SUCCESS) { + /* couldn't decode DER */ + _gnutls_debug_log("ASN.1 Decoding error: %s\n", err); + gnutls_assert(); + return _gnutls_asn2err(result); + } + + return 0; +} + +/** + * gnutls_x509_dn_deinit: + * @dn: a DN uint8_t object pointer. + * + * This function deallocates the DN object as returned by + * gnutls_x509_dn_import(). + * + * Since: 2.4.0 + **/ +void gnutls_x509_dn_deinit(gnutls_x509_dn_t dn) +{ + asn1_delete_structure(&dn->asn); + gnutls_free(dn); +} + +/** + * gnutls_x509_dn_export: + * @dn: Holds the uint8_t DN object + * @format: the format of output params. One of PEM or DER. + * @output_data: will contain a DN PEM or DER encoded + * @output_data_size: holds the size of output_data (and will be + * replaced by the actual size of parameters) + * + * This function will export the DN to DER or PEM format. + * + * If the buffer provided is not long enough to hold the output, then + * *@output_data_size is updated and %GNUTLS_E_SHORT_MEMORY_BUFFER + * will be returned. + * + * If the structure is PEM encoded, it will have a header + * of "BEGIN NAME". + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int +gnutls_x509_dn_export(gnutls_x509_dn_t dn, + gnutls_x509_crt_fmt_t format, void *output_data, + size_t * output_data_size) +{ + if (dn == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + return _gnutls_x509_export_int_named(dn->asn, "rdnSequence", + format, "NAME", + output_data, + output_data_size); +} + +/** + * gnutls_x509_dn_export2: + * @dn: Holds the uint8_t DN object + * @format: the format of output params. One of PEM or DER. + * @out: will contain a DN PEM or DER encoded + * + * This function will export the DN to DER or PEM format. + * + * The output buffer is allocated using gnutls_malloc(). + * + * If the structure is PEM encoded, it will have a header + * of "BEGIN NAME". + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 3.1.3 + **/ +int +gnutls_x509_dn_export2(gnutls_x509_dn_t dn, + gnutls_x509_crt_fmt_t format, gnutls_datum_t * out) +{ + if (dn == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + return _gnutls_x509_export_int_named2(dn->asn, "rdnSequence", + format, "NAME", out); +} + +/** + * gnutls_x509_dn_get_rdn_ava: + * @dn: a pointer to DN + * @irdn: index of RDN + * @iava: index of AVA. + * @ava: Pointer to structure which will hold output information. + * + * Get pointers to data within the DN. The format of the @ava structure + * is shown below. + * + * struct gnutls_x509_ava_st { + * gnutls_datum_t oid; + * gnutls_datum_t value; + * unsigned long value_tag; + * }; + * + * The X.509 distinguished name is a sequence of sequences of strings + * and this is what the @irdn and @iava indexes model. + * + * Note that @ava will contain pointers into the @dn structure which + * in turns points to the original certificate. Thus you should not + * modify any data or deallocate any of those. + * + * This is a low-level function that requires the caller to do the + * value conversions when necessary (e.g. from UCS-2). + * + * Returns: Returns 0 on success, or an error code. + **/ +int +gnutls_x509_dn_get_rdn_ava(gnutls_x509_dn_t dn, + int irdn, int iava, gnutls_x509_ava_st * ava) +{ + asn1_node rdn, elem; + asn1_data_node_st vnode; + long len; + int lenlen, remlen, ret; + char rbuf[MAX_NAME_SIZE]; + unsigned char cls; + const unsigned char *ptr; + + iava++; + irdn++; /* 0->1, 1->2 etc */ + + snprintf(rbuf, sizeof(rbuf), "rdnSequence.?%d.?%d", irdn, iava); + rdn = asn1_find_node(dn->asn, rbuf); + if (!rdn) { + gnutls_assert(); + return GNUTLS_E_ASN1_ELEMENT_NOT_FOUND; + } + + snprintf(rbuf, sizeof(rbuf), "?%d.type", iava); + elem = asn1_find_node(rdn, rbuf); + if (!elem) { + gnutls_assert(); + return GNUTLS_E_ASN1_ELEMENT_NOT_FOUND; + } + + ret = asn1_read_node_value(elem, &vnode); + if (ret != ASN1_SUCCESS) { + gnutls_assert(); + return GNUTLS_E_ASN1_ELEMENT_NOT_FOUND; + } + + ava->oid.data = (void *) vnode.value; + ava->oid.size = vnode.value_len; + + snprintf(rbuf, sizeof(rbuf), "?%d.value", iava); + elem = asn1_find_node(rdn, rbuf); + if (!elem) { + gnutls_assert(); + return GNUTLS_E_ASN1_ELEMENT_NOT_FOUND; + } + + ret = asn1_read_node_value(elem, &vnode); + if (ret != ASN1_SUCCESS) { + gnutls_assert(); + return GNUTLS_E_ASN1_ELEMENT_NOT_FOUND; + } + /* The value still has the previous tag's length bytes, plus the + * current value's tag and length bytes. Decode them. + */ + + ptr = vnode.value; + remlen = vnode.value_len; + len = asn1_get_length_der(ptr, remlen, &lenlen); + if (len < 0) { + gnutls_assert(); + return GNUTLS_E_ASN1_DER_ERROR; + } + + ptr += lenlen; + remlen -= lenlen; + ret = + asn1_get_tag_der(ptr, remlen, &cls, &lenlen, &ava->value_tag); + if (ret) { + gnutls_assert(); + return _gnutls_asn2err(ret); + } + + ptr += lenlen; + remlen -= lenlen; + + { + signed long tmp; + + tmp = asn1_get_length_der(ptr, remlen, &lenlen); + if (tmp < 0) { + gnutls_assert(); + return GNUTLS_E_ASN1_DER_ERROR; + } + ava->value.size = tmp; + } + ava->value.data = (void *) (ptr + lenlen); + + return 0; +} + +/** + * gnutls_x509_dn_get_str: + * @dn: a pointer to DN + * @str: a datum that will hold the name + * + * This function will allocate buffer and copy the name in the provided DN. + * The name will be in the form "C=xxxx,O=yyyy,CN=zzzz" as + * described in RFC4514. The output string will be ASCII or UTF-8 + * encoded, depending on the certificate data. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 3.4.2 + **/ +int +gnutls_x509_dn_get_str(gnutls_x509_dn_t dn, gnutls_datum_t *str) +{ + if (dn == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + return _gnutls_x509_get_dn(dn->asn, "rdnSequence", str, GNUTLS_X509_DN_FLAG_COMPAT); +} + +/** + * gnutls_x509_dn_get_str: + * @dn: a pointer to DN + * @str: a datum that will hold the name + * @flags: zero or %GNUTLS_X509_DN_FLAG_COMPAT + * + * This function will allocate buffer and copy the name in the provided DN. + * The name will be in the form "C=xxxx,O=yyyy,CN=zzzz" as + * described in RFC4514. The output string will be ASCII or UTF-8 + * encoded, depending on the certificate data. + * + * When the flag %GNUTLS_X509_DN_FLAG_COMPAT is specified, the output + * format will match the format output by previous to 3.5.6 versions of GnuTLS + * which was not not fully RFC4514-compliant. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 3.5.7 + **/ +int +gnutls_x509_dn_get_str2(gnutls_x509_dn_t dn, gnutls_datum_t *str, unsigned flags) +{ + if (dn == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + return _gnutls_x509_get_dn(dn->asn, "rdnSequence", str, flags); +} diff --git a/lib/x509/x509_ext.c b/lib/x509/x509_ext.c new file mode 100644 index 0000000..0b4c6a0 --- /dev/null +++ b/lib/x509/x509_ext.c @@ -0,0 +1,4050 @@ +/* + * Copyright (C) 2014-2016 Free Software Foundation, Inc. + * Copyright (C) 2016 Red Hat, Inc. + * + * This file is part of GnuTLS. + * + * The GnuTLS is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/> + * + */ + +/* This file contains functions to handle X.509 certificate extensions (the x509-ext API) + */ + +#include "gnutls_int.h" +#include <datum.h> +#include "errors.h" +#include <common.h> +#include <x509.h> +#include <x509_b64.h> +#include "x509_ext_int.h" +#include "virt-san.h" +#include <gnutls/x509-ext.h> +#include "intprops.h" + +#define MAX_ENTRIES 64 +struct gnutls_subject_alt_names_st { + struct name_st *names; + unsigned int size; +}; + +/** + * gnutls_subject_alt_names_init: + * @sans: The alternative names + * + * This function will initialize an alternative names structure. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value. + * + * Since: 3.3.0 + **/ +int gnutls_subject_alt_names_init(gnutls_subject_alt_names_t * sans) +{ + *sans = gnutls_calloc(1, sizeof(struct gnutls_subject_alt_names_st)); + if (*sans == NULL) { + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; + } + + return 0; +} + +static void subject_alt_names_deinit(gnutls_subject_alt_names_t sans) +{ + unsigned int i; + + for (i = 0; i < sans->size; i++) { + gnutls_free(sans->names[i].san.data); + gnutls_free(sans->names[i].othername_oid.data); + } + gnutls_free(sans->names); +} + +/** + * gnutls_subject_alt_names_deinit: + * @sans: The alternative names + * + * This function will deinitialize an alternative names structure. + * + * Since: 3.3.0 + **/ +void gnutls_subject_alt_names_deinit(gnutls_subject_alt_names_t sans) +{ + subject_alt_names_deinit(sans); + gnutls_free(sans); +} + +/** + * gnutls_subject_alt_names_get: + * @sans: The alternative names + * @seq: The index of the name to get + * @san_type: Will hold the type of the name (of %gnutls_subject_alt_names_t) + * @san: The alternative name data (should be treated as constant) + * @othername_oid: The object identifier if @san_type is %GNUTLS_SAN_OTHERNAME (should be treated as constant) + * + * This function will return a specific alternative name as stored in + * the @sans type. The returned values should be treated as constant + * and valid for the lifetime of @sans. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE + * if the index is out of bounds, otherwise a negative error value. + * + * Since: 3.3.0 + **/ +int gnutls_subject_alt_names_get(gnutls_subject_alt_names_t sans, + unsigned int seq, unsigned int *san_type, + gnutls_datum_t * san, + gnutls_datum_t * othername_oid) +{ + if (seq >= sans->size) + return gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE); + + if (san) { + memcpy(san, &sans->names[seq].san, sizeof(gnutls_datum_t)); + } + + if (san_type) + *san_type = sans->names[seq].type; + + if (othername_oid != NULL && sans->names[seq].type == GNUTLS_SAN_OTHERNAME) { + othername_oid->data = sans->names[seq].othername_oid.data; + othername_oid->size = sans->names[seq].othername_oid.size; + } + + return 0; +} + +/* This is the same as gnutls_subject_alt_names_set() but will not + * copy the strings. It expects all the provided input to be already + * allocated by gnutls. */ +static +int subject_alt_names_set(struct name_st **names, + unsigned int *size, + unsigned int san_type, + gnutls_datum_t * san, char *othername_oid, + unsigned raw) +{ + void *tmp; + int ret; + + if (unlikely(INT_ADD_OVERFLOW(*size, 1))) { + return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + } + + tmp = _gnutls_reallocarray(*names, *size + 1, sizeof((*names)[0])); + if (tmp == NULL) { + return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + } + *names = tmp; + + ret = _gnutls_alt_name_assign_virt_type(&(*names)[*size], san_type, san, othername_oid, raw); + if (ret < 0) + return gnutls_assert_val(ret); + + (*size)++; + return 0; +} + +/** + * gnutls_subject_alt_names_set: + * @sans: The alternative names + * @san_type: The type of the name (of %gnutls_subject_alt_names_t) + * @san: The alternative name data + * @othername_oid: The object identifier if @san_type is %GNUTLS_SAN_OTHERNAME + * + * This function will store the specified alternative name in + * the @sans. + * + * Since version 3.5.7 the %GNUTLS_SAN_RFC822NAME, %GNUTLS_SAN_DNSNAME, and + * %GNUTLS_SAN_OTHERNAME_XMPP are converted to ACE format when necessary. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0), otherwise a negative error value. + * + * Since: 3.3.0 + **/ +int gnutls_subject_alt_names_set(gnutls_subject_alt_names_t sans, + unsigned int san_type, + const gnutls_datum_t * san, + const char *othername_oid) +{ + int ret; + gnutls_datum_t copy; + char *ooc; + + ret = _gnutls_set_strdatum(©, san->data, san->size); + if (ret < 0) + return gnutls_assert_val(ret); + + if (othername_oid != NULL) + ooc = gnutls_strdup(othername_oid); + else + ooc = NULL; + ret = subject_alt_names_set(&sans->names, &sans->size, + san_type, ©, ooc, 0); + if (ret < 0) { + gnutls_free(copy.data); + return gnutls_assert_val(ret); + } + + return 0; +} + +/** + * gnutls_x509_ext_import_subject_alt_names: + * @ext: The DER-encoded extension data + * @sans: The alternative names + * @flags: should be zero + * + * This function will export the alternative names in the provided DER-encoded + * SubjectAltName PKIX extension, to a %gnutls_subject_alt_names_t type. @sans + * must be initialized. + * + * This function will succeed even if there no subject alternative names + * in the structure. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value. + * + * Since: 3.3.0 + **/ +int gnutls_x509_ext_import_subject_alt_names(const gnutls_datum_t * ext, + gnutls_subject_alt_names_t sans, + unsigned int flags) +{ + asn1_node c2 = NULL; + int result, ret; + unsigned int i; + gnutls_datum_t san, othername_oid; + unsigned type; + + result = + asn1_create_element(_gnutls_get_pkix(), "PKIX1.GeneralNames", &c2); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + result = _asn1_strict_der_decode(&c2, ext->data, ext->size, NULL); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(result); + goto cleanup; + } + + for (i=0;;i++) { + san.data = NULL; + san.size = 0; + othername_oid.data = NULL; + + ret = _gnutls_parse_general_name2(c2, "", i, &san, &type, 0); + if (ret < 0) + break; + + if (type == GNUTLS_SAN_OTHERNAME) { + ret = + _gnutls_parse_general_name2(c2, "", i, + &othername_oid, + NULL, 1); + if (ret < 0) + break; + + } else if (san.size == 0 || san.data == NULL) { + ret = gnutls_assert_val(GNUTLS_E_X509_UNKNOWN_SAN); + break; + } + + ret = subject_alt_names_set(&sans->names, &sans->size, + type, &san, + (char *)othername_oid.data, 1); + if (ret < 0) + break; + } + + sans->size = i; + if (ret < 0 && ret != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { + gnutls_free(san.data); + gnutls_free(othername_oid.data); + gnutls_assert(); + goto cleanup; + } + + ret = 0; + cleanup: + asn1_delete_structure(&c2); + return ret; +} + +/** + * gnutls_x509_ext_export_subject_alt_names: + * @sans: The alternative names + * @ext: The DER-encoded extension data; must be freed using gnutls_free(). + * + * This function will convert the provided alternative names structure to a + * DER-encoded SubjectAltName PKIX extension. The output data in @ext will be allocated using + * gnutls_malloc(). + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value. + * + * Since: 3.3.0 + **/ +int gnutls_x509_ext_export_subject_alt_names(gnutls_subject_alt_names_t sans, + gnutls_datum_t * ext) +{ + asn1_node c2 = NULL; + int result, ret; + unsigned i; + + result = + asn1_create_element(_gnutls_get_pkix(), "PKIX1.GeneralNames", &c2); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + for (i = 0; i < sans->size; i++) { + if (sans->names[i].type == GNUTLS_SAN_OTHERNAME) { + ret = _gnutls_write_new_othername(c2, "", (char*)sans->names[i].othername_oid.data, + sans->names[i].san.data, sans->names[i].san.size); + } else { + ret = + _gnutls_write_new_general_name(c2, "", sans->names[i].type, + sans->names[i].san.data, + sans->names[i].san.size); + } + + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + } + + ret = _gnutls_x509_der_encode(c2, "", ext, 0); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = 0; + + cleanup: + asn1_delete_structure(&c2); + return ret; +} + +/** + * gnutls_x509_ext_import_name_constraints: + * @ext: a DER encoded extension + * @nc: The nameconstraints + * @flags: zero or %GNUTLS_NAME_CONSTRAINTS_FLAG_APPEND + * + * This function will return an intermediate type containing + * the name constraints of the provided NameConstraints extension. That + * can be used in combination with gnutls_x509_name_constraints_check() + * to verify whether a server's name is in accordance with the constraints. + * + * When the @flags is set to %GNUTLS_NAME_CONSTRAINTS_FLAG_APPEND, then if + * the @nc type is empty this function will behave identically as if the flag was not set. + * Otherwise if there are elements in the @nc structure then the + * constraints will be merged with the existing constraints following + * RFC5280 p6.1.4 (excluded constraints will be appended, permitted + * will be intersected). + * + * Note that @nc must be initialized prior to calling this function. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE + * if the extension is not present, otherwise a negative error value. + * + * Since: 3.3.0 + **/ +int gnutls_x509_ext_import_name_constraints(const gnutls_datum_t * ext, + gnutls_x509_name_constraints_t nc, + unsigned int flags) +{ + int result, ret; + asn1_node c2 = NULL; + gnutls_x509_name_constraints_t nc2 = NULL; + + result = asn1_create_element + (_gnutls_get_pkix(), "PKIX1.NameConstraints", &c2); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + result = _asn1_strict_der_decode(&c2, ext->data, ext->size, NULL); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(result); + goto cleanup; + } + + if (flags & GNUTLS_NAME_CONSTRAINTS_FLAG_APPEND && + (nc->permitted != NULL || nc->excluded != NULL)) { + ret = gnutls_x509_name_constraints_init (&nc2); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = + _gnutls_extract_name_constraints(c2, "permittedSubtrees", + &nc2->permitted); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = + _gnutls_extract_name_constraints(c2, "excludedSubtrees", + &nc2->excluded); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = _gnutls_x509_name_constraints_merge(nc, nc2); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + } else { + _gnutls_name_constraints_node_free(nc->permitted); + _gnutls_name_constraints_node_free(nc->excluded); + + ret = + _gnutls_extract_name_constraints(c2, "permittedSubtrees", + &nc->permitted); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = + _gnutls_extract_name_constraints(c2, "excludedSubtrees", + &nc->excluded); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + } + + ret = 0; + + cleanup: + asn1_delete_structure(&c2); + if (nc2) + gnutls_x509_name_constraints_deinit (nc2); + + return ret; +} + +/** + * gnutls_x509_ext_export_name_constraints: + * @nc: The nameconstraints + * @ext: The DER-encoded extension data; must be freed using gnutls_free(). + * + * This function will convert the provided name constraints type to a + * DER-encoded PKIX NameConstraints (2.5.29.30) extension. The output data in + * @ext will be allocated using gnutls_malloc(). + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value. + * + * Since: 3.3.0 + **/ +int gnutls_x509_ext_export_name_constraints(gnutls_x509_name_constraints_t nc, + gnutls_datum_t * ext) +{ + int ret, result; + uint8_t null = 0; + asn1_node c2 = NULL; + struct name_constraints_node_st *tmp; + + if (nc->permitted == NULL && nc->excluded == NULL) + return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + + result = asn1_create_element + (_gnutls_get_pkix(), "PKIX1.NameConstraints", &c2); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + if (nc->permitted == NULL) { + (void)asn1_write_value(c2, "permittedSubtrees", NULL, 0); + } else { + tmp = nc->permitted; + do { + result = + asn1_write_value(c2, "permittedSubtrees", "NEW", 1); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(result); + goto cleanup; + } + + result = + asn1_write_value(c2, + "permittedSubtrees.?LAST.maximum", + NULL, 0); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(result); + goto cleanup; + } + + result = + asn1_write_value(c2, + "permittedSubtrees.?LAST.minimum", + &null, 1); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(result); + goto cleanup; + } + + ret = + _gnutls_write_general_name(c2, + "permittedSubtrees.?LAST.base", + tmp->type, + tmp->name.data, + tmp->name.size); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + tmp = tmp->next; + } while (tmp != NULL); + } + + if (nc->excluded == NULL) { + (void)asn1_write_value(c2, "excludedSubtrees", NULL, 0); + } else { + tmp = nc->excluded; + do { + result = + asn1_write_value(c2, "excludedSubtrees", "NEW", 1); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(result); + goto cleanup; + } + + result = + asn1_write_value(c2, + "excludedSubtrees.?LAST.maximum", + NULL, 0); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(result); + goto cleanup; + } + + result = + asn1_write_value(c2, + "excludedSubtrees.?LAST.minimum", + &null, 1); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(result); + goto cleanup; + } + + ret = + _gnutls_write_general_name(c2, + "excludedSubtrees.?LAST.base", + tmp->type, + tmp->name.data, + tmp->name.size); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + tmp = tmp->next; + } while (tmp != NULL); + + } + + ret = _gnutls_x509_der_encode(c2, "", ext, 0); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = 0; + + cleanup: + asn1_delete_structure(&c2); + return ret; +} + +/** + * gnutls_x509_ext_import_subject_key_id: + * @ext: a DER encoded extension + * @id: will contain the subject key ID + * + * This function will return the subject key ID stored in the provided + * SubjectKeyIdentifier extension. The ID will be allocated using + * gnutls_malloc(). + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE + * if the extension is not present, otherwise a negative error value. + * + * Since: 3.3.0 + **/ +int gnutls_x509_ext_import_subject_key_id(const gnutls_datum_t * ext, + gnutls_datum_t * id) +{ + int result, ret; + asn1_node c2 = NULL; + + if (ext->size == 0 || ext->data == NULL) { + gnutls_assert(); + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + } + + result = asn1_create_element + (_gnutls_get_pkix(), "PKIX1.SubjectKeyIdentifier", &c2); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + result = _asn1_strict_der_decode(&c2, ext->data, ext->size, NULL); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(result); + goto cleanup; + } + + ret = _gnutls_x509_read_value(c2, "", id); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = 0; + cleanup: + asn1_delete_structure(&c2); + + return ret; + +} + +/** + * gnutls_x509_ext_export_subject_key_id: + * @id: The key identifier + * @ext: The DER-encoded extension data; must be freed using gnutls_free(). + * + * This function will convert the provided key identifier to a + * DER-encoded PKIX SubjectKeyIdentifier extension. + * The output data in @ext will be allocated using + * gnutls_malloc(). + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value. + * + * Since: 3.3.0 + **/ +int gnutls_x509_ext_export_subject_key_id(const gnutls_datum_t * id, + gnutls_datum_t * ext) +{ + asn1_node c2 = NULL; + int ret, result; + + result = + asn1_create_element(_gnutls_get_pkix(), + "PKIX1.SubjectKeyIdentifier", &c2); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + result = asn1_write_value(c2, "", id->data, id->size); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(result); + goto cleanup; + } + + ret = _gnutls_x509_der_encode(c2, "", ext, 0); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = 0; + cleanup: + asn1_delete_structure(&c2); + return ret; +} + +struct gnutls_x509_aki_st { + gnutls_datum_t id; + struct gnutls_subject_alt_names_st cert_issuer; + gnutls_datum_t serial; +}; + +/** + * gnutls_x509_aki_init: + * @aki: The authority key ID type + * + * This function will initialize an authority key ID. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value. + * + * Since: 3.3.0 + **/ +int gnutls_x509_aki_init(gnutls_x509_aki_t * aki) +{ + *aki = gnutls_calloc(1, sizeof(struct gnutls_x509_aki_st)); + if (*aki == NULL) + return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + + return 0; +} + +/** + * gnutls_x509_aki_deinit: + * @aki: The authority key identifier type + * + * This function will deinitialize an authority key identifier. + * + * Since: 3.3.0 + **/ +void gnutls_x509_aki_deinit(gnutls_x509_aki_t aki) +{ + gnutls_free(aki->serial.data); + gnutls_free(aki->id.data); + subject_alt_names_deinit(&aki->cert_issuer); + gnutls_free(aki); +} + +/** + * gnutls_x509_aki_get_id: + * @aki: The authority key ID + * @id: Will hold the identifier + * + * This function will return the key identifier as stored in + * the @aki type. The identifier should be treated as constant. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE + * if the index is out of bounds, otherwise a negative error value. + * + * Since: 3.3.0 + **/ +int gnutls_x509_aki_get_id(gnutls_x509_aki_t aki, gnutls_datum_t * id) +{ + if (aki->id.size == 0) + return gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE); + + memcpy(id, &aki->id, sizeof(gnutls_datum_t)); + return 0; +} + +/** + * gnutls_x509_aki_set_id: + * @aki: The authority key ID + * @id: the key identifier + * + * This function will set the keyIdentifier to be stored in the @aki + * type. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value. + * + * Since: 3.3.0 + **/ +int gnutls_x509_aki_set_id(gnutls_x509_aki_t aki, const gnutls_datum_t * id) +{ + return _gnutls_set_datum(&aki->id, id->data, id->size); +} + +/** + * gnutls_x509_aki_set_cert_issuer: + * @aki: The authority key ID + * @san_type: the type of the name (of %gnutls_subject_alt_names_t), may be null + * @san: The alternative name data + * @othername_oid: The object identifier if @san_type is %GNUTLS_SAN_OTHERNAME + * @serial: The authorityCertSerialNumber number (may be null) + * + * This function will set the authorityCertIssuer name and the authorityCertSerialNumber + * to be stored in the @aki type. When storing multiple names, the serial + * should be set on the first call, and subsequent calls should use a %NULL serial. + * + * Since version 3.5.7 the %GNUTLS_SAN_RFC822NAME, %GNUTLS_SAN_DNSNAME, and + * %GNUTLS_SAN_OTHERNAME_XMPP are converted to ACE format when necessary. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value. + * + * Since: 3.3.0 + **/ +int gnutls_x509_aki_set_cert_issuer(gnutls_x509_aki_t aki, + unsigned int san_type, + const gnutls_datum_t *san, + const char *othername_oid, + const gnutls_datum_t *serial) +{ + int ret; + gnutls_datum_t t_san, t_othername_oid = { NULL, 0 }; + + ret = _gnutls_set_datum(&aki->serial, serial->data, serial->size); + if (ret < 0) + return gnutls_assert_val(ret); + + aki->cert_issuer.names[aki->cert_issuer.size].type = san_type; + + ret = _gnutls_set_strdatum(&t_san, san->data, san->size); + if (ret < 0) + return gnutls_assert_val(ret); + + if (othername_oid) { + t_othername_oid.data = (uint8_t *) gnutls_strdup(othername_oid); + if (t_othername_oid.data == NULL) { + gnutls_free(t_san.data); + return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + } + t_othername_oid.size = strlen(othername_oid); + } + + ret = + subject_alt_names_set(&aki->cert_issuer.names, + &aki->cert_issuer.size, san_type, &t_san, + (char *)t_othername_oid.data, 0); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + return 0; +} + +/** + * gnutls_x509_aki_get_cert_issuer: + * @aki: The authority key ID + * @seq: The index of the name to get + * @san_type: Will hold the type of the name (of %gnutls_subject_alt_names_t) + * @san: The alternative name data + * @othername_oid: The object identifier if @san_type is %GNUTLS_SAN_OTHERNAME + * @serial: The authorityCertSerialNumber number + * + * This function will return a specific authorityCertIssuer name as stored in + * the @aki type, as well as the authorityCertSerialNumber. All the returned + * values should be treated as constant, and may be set to %NULL when are not required. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE + * if the index is out of bounds, otherwise a negative error value. + * + * Since: 3.3.0 + **/ +int gnutls_x509_aki_get_cert_issuer(gnutls_x509_aki_t aki, unsigned int seq, + unsigned int *san_type, + gnutls_datum_t *san, + gnutls_datum_t *othername_oid, + gnutls_datum_t *serial) +{ + if (seq >= aki->cert_issuer.size) + return gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE); + + if (aki->serial.size == 0) + return gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE); + + if (serial) + memcpy(serial, &aki->serial, sizeof(gnutls_datum_t)); + + if (san) { + memcpy(san, &aki->cert_issuer.names[seq].san, + sizeof(gnutls_datum_t)); + } + + if (othername_oid != NULL + && aki->cert_issuer.names[seq].type == GNUTLS_SAN_OTHERNAME) { + othername_oid->data = + aki->cert_issuer.names[seq].othername_oid.data; + othername_oid->size = + aki->cert_issuer.names[seq].othername_oid.size; + } + + if (san_type) + *san_type = aki->cert_issuer.names[seq].type; + + return 0; + +} + +/** + * gnutls_x509_ext_import_authority_key_id: + * @ext: a DER encoded extension + * @aki: An initialized authority key identifier type + * @flags: should be zero + * + * This function will return the subject key ID stored in the provided + * AuthorityKeyIdentifier extension. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE + * if the extension is not present, otherwise a negative error value. + * + * Since: 3.3.0 + **/ +int gnutls_x509_ext_import_authority_key_id(const gnutls_datum_t * ext, + gnutls_x509_aki_t aki, + unsigned int flags) +{ + int ret; + unsigned i; + asn1_node c2 = NULL; + gnutls_datum_t san, othername_oid; + unsigned type; + + ret = asn1_create_element + (_gnutls_get_pkix(), "PKIX1.AuthorityKeyIdentifier", &c2); + if (ret != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(ret); + } + + ret = _asn1_strict_der_decode(&c2, ext->data, ext->size, NULL); + if (ret != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(ret); + goto cleanup; + } + + /* Read authorityCertIssuer */ + for (i=0;;i++) { + san.data = NULL; + san.size = 0; + othername_oid.data = NULL; + + ret = _gnutls_parse_general_name2(c2, "authorityCertIssuer", i, + &san, &type, 0); + if (ret < 0) + break; + + if (type == GNUTLS_SAN_OTHERNAME) { + ret = + _gnutls_parse_general_name2(c2, + "authorityCertIssuer", + i, + &othername_oid, + NULL, 1); + if (ret < 0) + break; + } + + ret = subject_alt_names_set(&aki->cert_issuer.names, + &aki->cert_issuer.size, + type, &san, + (char *)othername_oid.data, 1); + if (ret < 0) + break; + } + + assert(ret < 0); + aki->cert_issuer.size = i; + if (ret != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE + && ret != GNUTLS_E_ASN1_ELEMENT_NOT_FOUND) { + gnutls_assert(); + gnutls_free(san.data); + gnutls_free(othername_oid.data); + goto cleanup; + } + + /* Read the serial number */ + ret = + _gnutls_x509_read_value(c2, "authorityCertSerialNumber", + &aki->serial); + if (ret < 0 && ret != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE + && ret != GNUTLS_E_ASN1_ELEMENT_NOT_FOUND) { + gnutls_assert(); + goto cleanup; + } + + /* Read the key identifier */ + ret = _gnutls_x509_read_value(c2, "keyIdentifier", &aki->id); + if (ret < 0 && ret != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE + && ret != GNUTLS_E_ASN1_ELEMENT_NOT_FOUND) { + gnutls_assert(); + goto cleanup; + } + + ret = 0; + + cleanup: + asn1_delete_structure(&c2); + + return ret; +} + +/** + * gnutls_x509_ext_export_authority_key_id: + * @aki: An initialized authority key identifier + * @ext: The DER-encoded extension data; must be freed using gnutls_free(). + * + * This function will convert the provided key identifier to a + * DER-encoded PKIX AuthorityKeyIdentifier extension. + * The output data in @ext will be allocated using + * gnutls_malloc(). + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value. + * + * Since: 3.3.0 + **/ +int gnutls_x509_ext_export_authority_key_id(gnutls_x509_aki_t aki, + gnutls_datum_t * ext) +{ + asn1_node c2 = NULL; + unsigned i; + int result, ret; + + result = + asn1_create_element(_gnutls_get_pkix(), + "PKIX1.AuthorityKeyIdentifier", &c2); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + if (aki->id.data != NULL) { + result = + asn1_write_value(c2, "keyIdentifier", aki->id.data, + aki->id.size); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(result); + goto cleanup; + } + } else { + (void)asn1_write_value(c2, "keyIdentifier", NULL, 0); + } + + if (aki->serial.data != NULL) { + result = + asn1_write_value(c2, "authorityCertSerialNumber", + aki->serial.data, aki->serial.size); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(result); + goto cleanup; + } + } else { + (void)asn1_write_value(c2, "authorityCertSerialNumber", NULL, 0); + } + + if (aki->cert_issuer.size == 0) { + (void)asn1_write_value(c2, "authorityCertIssuer", NULL, 0); + } else { + for (i = 0; i < aki->cert_issuer.size; i++) { + ret = + _gnutls_write_new_general_name(c2, + "authorityCertIssuer", + aki->cert_issuer. + names[i].type, + aki-> + cert_issuer.names[i]. + san.data, + aki->cert_issuer. + names[i].san.size); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + } + } + + ret = _gnutls_x509_der_encode(c2, "", ext, 0); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = 0; + cleanup: + asn1_delete_structure(&c2); + return ret; + +} + +/** + * gnutls_x509_ext_import_key_usage: + * @ext: the DER encoded extension data + * @key_usage: where the key usage bits will be stored + * + * This function will return certificate's key usage, by reading the DER + * data of the keyUsage X.509 extension (2.5.29.15). The key usage value will ORed + * values of the: %GNUTLS_KEY_DIGITAL_SIGNATURE, + * %GNUTLS_KEY_NON_REPUDIATION, %GNUTLS_KEY_KEY_ENCIPHERMENT, + * %GNUTLS_KEY_DATA_ENCIPHERMENT, %GNUTLS_KEY_KEY_AGREEMENT, + * %GNUTLS_KEY_KEY_CERT_SIGN, %GNUTLS_KEY_CRL_SIGN, + * %GNUTLS_KEY_ENCIPHER_ONLY, %GNUTLS_KEY_DECIPHER_ONLY. + * + * Returns: the certificate key usage, or a negative error code in case of + * parsing error. If the certificate does not contain the keyUsage + * extension %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE will be + * returned. + * + * Since: 3.3.0 + **/ +int gnutls_x509_ext_import_key_usage(const gnutls_datum_t * ext, + unsigned int *key_usage) +{ + asn1_node c2 = NULL; + int len, result; + uint8_t str[2]; + + str[0] = str[1] = 0; + *key_usage = 0; + + if ((result = asn1_create_element + (_gnutls_get_pkix(), "PKIX1.KeyUsage", &c2)) != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + result = _asn1_strict_der_decode(&c2, ext->data, ext->size, NULL); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + asn1_delete_structure(&c2); + return _gnutls_asn2err(result); + } + + len = sizeof(str); + result = asn1_read_value(c2, "", str, &len); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + asn1_delete_structure(&c2); + return _gnutls_asn2err(result); + } + + *key_usage = str[0] | (str[1] << 8); + + asn1_delete_structure(&c2); + + return 0; +} + +static int _last_key_usage_set_bit(int usage) +{ +/* the byte ordering is a bit strange here, see how GNUTLS_KEY_* is laid out, and how + * asn1_write_value() writes out BIT STRING objects. + */ + if (usage & GNUTLS_KEY_DECIPHER_ONLY) + return 9; + else if (usage & GNUTLS_KEY_ENCIPHER_ONLY) + return 8; + else if (usage & GNUTLS_KEY_CRL_SIGN) + return 7; + else if (usage & GNUTLS_KEY_KEY_CERT_SIGN) + return 6; + else if (usage & GNUTLS_KEY_KEY_AGREEMENT) + return 5; + else if (usage & GNUTLS_KEY_DATA_ENCIPHERMENT) + return 4; + else if (usage & GNUTLS_KEY_KEY_ENCIPHERMENT) + return 3; + else if (usage & GNUTLS_KEY_NON_REPUDIATION) + return 2; + else if (usage & GNUTLS_KEY_DIGITAL_SIGNATURE) + return 1; + else + return 0; +} + +/** + * gnutls_x509_ext_export_key_usage: + * @usage: an ORed sequence of the GNUTLS_KEY_* elements. + * @ext: The DER-encoded extension data; must be freed using gnutls_free(). + * + * This function will convert the keyUsage bit string to a DER + * encoded PKIX extension. The @ext data will be allocated using + * gnutls_malloc(). + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 3.3.0 + **/ +int gnutls_x509_ext_export_key_usage(unsigned int usage, gnutls_datum_t * ext) +{ + asn1_node c2 = NULL; + int result; + uint8_t str[2]; + + result = asn1_create_element(_gnutls_get_pkix(), "PKIX1.KeyUsage", &c2); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + str[0] = usage & 0xff; + str[1] = usage >> 8; + + /* Since KeyUsage is a BIT STRING, the input to asn1_write_value + * is the number of bits to be written/read. */ + result = asn1_write_value(c2, "", str, _last_key_usage_set_bit(usage)); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + asn1_delete_structure(&c2); + return _gnutls_asn2err(result); + } + + result = _gnutls_x509_der_encode(c2, "", ext, 0); + + asn1_delete_structure(&c2); + + if (result < 0) { + gnutls_assert(); + return result; + } + + return 0; +} + +/** + * gnutls_x509_ext_import_inhibit_anypolicy: + * @ext: the DER encoded extension data + * @skipcerts: will hold the number of certificates after which anypolicy is no longer acceptable. + * + * This function will return certificate's value of SkipCerts, + * by reading the DER data of the Inhibit anyPolicy X.509 extension (2.5.29.54). + * + * The @skipcerts value is the number of additional certificates that + * may appear in the path before the anyPolicy (%GNUTLS_X509_OID_POLICY_ANY) + * is no longer acceptable. + * + * Returns: zero, or a negative error code in case of + * parsing error. If the certificate does not contain the Inhibit anyPolicy + * extension %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE will be + * returned. + * + * Since: 3.6.0 + **/ +int gnutls_x509_ext_import_inhibit_anypolicy(const gnutls_datum_t * ext, + unsigned int *skipcerts) +{ + int ret; + + ret = _gnutls_x509_read_der_uint(ext->data, ext->size, skipcerts); + if (ret < 0) { + gnutls_assert(); + } + + return ret; +} + +/** + * gnutls_x509_ext_export_inhibit_anypolicy: + * @skipcerts: number of certificates after which anypolicy is no longer acceptable. + * @ext: The DER-encoded extension data; must be freed using gnutls_free(). + * + * This function will convert the @skipcerts value to a DER + * encoded Inhibit AnyPolicy PKIX extension. The @ext data will be allocated using + * gnutls_malloc(). + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 3.6.0 + **/ +int gnutls_x509_ext_export_inhibit_anypolicy(unsigned int skipcerts, gnutls_datum_t * ext) +{ + asn1_node c2 = NULL; + int result, ret; + + result = asn1_create_element(_gnutls_get_gnutls_asn(), "GNUTLS.DSAPublicKey", &c2); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + ret = _gnutls_x509_write_uint32(c2, "", skipcerts); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = _gnutls_x509_der_encode(c2, "", ext, 0); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = 0; + + cleanup: + asn1_delete_structure(&c2); + + return ret; +} + +/** + * gnutls_x509_ext_import_private_key_usage_period: + * @ext: the DER encoded extension data + * @activation: Will hold the activation time + * @expiration: Will hold the expiration time + * + * This function will return the expiration and activation + * times of the private key as written in the + * PKIX extension 2.5.29.16. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 3.3.0 + **/ +int gnutls_x509_ext_import_private_key_usage_period(const gnutls_datum_t * ext, + time_t * activation, + time_t * expiration) +{ + int result, ret; + asn1_node c2 = NULL; + + result = asn1_create_element + (_gnutls_get_pkix(), "PKIX1.PrivateKeyUsagePeriod", &c2); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(result); + goto cleanup; + } + + result = _asn1_strict_der_decode(&c2, ext->data, ext->size, NULL); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(result); + goto cleanup; + } + + if (activation) + *activation = _gnutls_x509_get_time(c2, "notBefore", 1); + + if (expiration) + *expiration = _gnutls_x509_get_time(c2, "notAfter", 1); + + ret = 0; + + cleanup: + asn1_delete_structure(&c2); + + return ret; +} + +/** + * gnutls_x509_ext_export_private_key_usage_period: + * @activation: The activation time + * @expiration: The expiration time + * @ext: The DER-encoded extension data; must be freed using gnutls_free(). + * + * This function will convert the periods provided to a private key + * usage DER encoded extension (2.5.29.16). + ( + * The @ext data will be allocated using + * gnutls_malloc(). + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 3.3.0 + **/ +int gnutls_x509_ext_export_private_key_usage_period(time_t activation, + time_t expiration, + gnutls_datum_t * ext) +{ + int result; + asn1_node c2 = NULL; + + result = + asn1_create_element(_gnutls_get_pkix(), + "PKIX1.PrivateKeyUsagePeriod", &c2); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + result = _gnutls_x509_set_time(c2, "notBefore", activation, 1); + if (result < 0) { + gnutls_assert(); + goto cleanup; + } + + result = _gnutls_x509_set_time(c2, "notAfter", expiration, 1); + if (result < 0) { + gnutls_assert(); + goto cleanup; + } + + result = _gnutls_x509_der_encode(c2, "", ext, 0); + if (result < 0) { + gnutls_assert(); + goto cleanup; + } + + cleanup: + asn1_delete_structure(&c2); + + return result; + +} + +/** + * gnutls_x509_ext_import_basic_constraints: + * @ext: the DER encoded extension data + * @ca: will be non zero if the CA status is true + * @pathlen: the path length constraint; will be set to -1 for no limit + * + * This function will return the CA status and path length constraint + * as written in the PKIX extension 2.5.29.19. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 3.3.0 + **/ +int gnutls_x509_ext_import_basic_constraints(const gnutls_datum_t * ext, + unsigned int *ca, int *pathlen) +{ + asn1_node c2 = NULL; + char str[128]=""; + int len, result; + + if ((result = asn1_create_element + (_gnutls_get_pkix(), "PKIX1.BasicConstraints", + &c2)) != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + result = _asn1_strict_der_decode(&c2, ext->data, ext->size, NULL); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + if (pathlen) { + result = _gnutls_x509_read_uint(c2, "pathLenConstraint", + (unsigned int *) + pathlen); + if (result == GNUTLS_E_ASN1_ELEMENT_NOT_FOUND) + *pathlen = -1; + else if (result != GNUTLS_E_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + } + + /* the default value of cA is false. + */ + len = sizeof(str) - 1; + result = asn1_read_value(c2, "cA", str, &len); + if (result == ASN1_SUCCESS && strcmp(str, "TRUE") == 0) + *ca = 1; + else + *ca = 0; + + result = 0; + cleanup: + asn1_delete_structure(&c2); + + return result; + +} + +/** + * gnutls_x509_ext_export_basic_constraints: + * @ca: non-zero for a CA + * @pathlen: The path length constraint (set to -1 for no constraint) + * @ext: The DER-encoded extension data; must be freed using gnutls_free(). + * + * This function will convert the parameters provided to a basic constraints + * DER encoded extension (2.5.29.19). + ( + * The @ext data will be allocated using + * gnutls_malloc(). + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 3.3.0 + **/ +int gnutls_x509_ext_export_basic_constraints(unsigned int ca, int pathlen, + gnutls_datum_t * ext) +{ + asn1_node c2 = NULL; + const char *str; + int result; + + if (ca == 0) + str = "FALSE"; + else + str = "TRUE"; + + result = + asn1_create_element(_gnutls_get_pkix(), + "PKIX1.BasicConstraints", &c2); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + result = asn1_write_value(c2, "cA", str, 1); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + if (pathlen < 0) { + result = asn1_write_value(c2, "pathLenConstraint", NULL, 0); + if (result < 0) + result = _gnutls_asn2err(result); + } else + result = + _gnutls_x509_write_uint32(c2, "pathLenConstraint", pathlen); + if (result < 0) { + gnutls_assert(); + goto cleanup; + } + + result = _gnutls_x509_der_encode(c2, "", ext, 0); + if (result < 0) { + gnutls_assert(); + goto cleanup; + } + + result = 0; + + cleanup: + asn1_delete_structure(&c2); + return result; + +} + +/** + * gnutls_x509_ext_import_proxy: + * @ext: the DER encoded extension data + * @pathlen: pointer to output integer indicating path length (may be + * NULL), non-negative error codes indicate a present pCPathLenConstraint + * field and the actual value, -1 indicate that the field is absent. + * @policyLanguage: output variable with OID of policy language + * @policy: output variable with policy data + * @sizeof_policy: output variable with size of policy data + * + * This function will return the information from a proxy certificate + * extension. It reads the ProxyCertInfo X.509 extension (1.3.6.1.5.5.7.1.14). + * The @policyLanguage and @policy values must be deinitialized using gnutls_free() after use. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 3.3.0 + **/ +int gnutls_x509_ext_import_proxy(const gnutls_datum_t *ext, int *pathlen, + char **policyLanguage, char **policy, + size_t *sizeof_policy) +{ + asn1_node c2 = NULL; + int result; + gnutls_datum_t value1 = { NULL, 0 }; + gnutls_datum_t value2 = { NULL, 0 }; + + if ((result = asn1_create_element + (_gnutls_get_pkix(), "PKIX1.ProxyCertInfo", + &c2)) != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + result = _asn1_strict_der_decode(&c2, ext->data, ext->size, NULL); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + if (pathlen) { + result = _gnutls_x509_read_uint(c2, "pCPathLenConstraint", + (unsigned int *) + pathlen); + if (result == GNUTLS_E_ASN1_ELEMENT_NOT_FOUND) + *pathlen = -1; + else if (result != GNUTLS_E_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + } + + result = _gnutls_x509_read_value(c2, "proxyPolicy.policyLanguage", + &value1); + if (result < 0) { + gnutls_assert(); + goto cleanup; + } + + result = _gnutls_x509_read_value(c2, "proxyPolicy.policy", &value2); + if (result == GNUTLS_E_ASN1_ELEMENT_NOT_FOUND) { + if (policy) + *policy = NULL; + if (sizeof_policy) + *sizeof_policy = 0; + } else if (result < 0) { + gnutls_assert(); + goto cleanup; + } else { + if (policy) { + *policy = (char *)value2.data; + value2.data = NULL; + } + if (sizeof_policy) + *sizeof_policy = value2.size; + } + + if (policyLanguage) { + *policyLanguage = (char *)value1.data; + value1.data = NULL; + } + + result = 0; + cleanup: + gnutls_free(value1.data); + gnutls_free(value2.data); + asn1_delete_structure(&c2); + + return result; +} + +/** + * gnutls_x509_ext_export_proxy: + * @pathLenConstraint: A negative value will remove the path length constraint, + * while non-negative values will be set as the length of the pathLenConstraints field. + * @policyLanguage: OID describing the language of @policy. + * @policy: uint8_t byte array with policy language, can be %NULL + * @sizeof_policy: size of @policy. + * @ext: The DER-encoded extension data; must be freed using gnutls_free(). + * + * This function will convert the parameters provided to a proxyCertInfo extension. + * + * The @ext data will be allocated using gnutls_malloc(). + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 3.3.0 + **/ +int gnutls_x509_ext_export_proxy(int pathLenConstraint, const char *policyLanguage, + const char *policy, size_t sizeof_policy, + gnutls_datum_t * ext) +{ + asn1_node c2 = NULL; + int result; + + result = asn1_create_element(_gnutls_get_pkix(), + "PKIX1.ProxyCertInfo", &c2); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + if (pathLenConstraint < 0) { + result = asn1_write_value(c2, "pCPathLenConstraint", NULL, 0); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + } else { + result = + _gnutls_x509_write_uint32(c2, "pCPathLenConstraint", + pathLenConstraint); + + if (result < 0) { + gnutls_assert(); + goto cleanup; + } + } + + result = asn1_write_value(c2, "proxyPolicy.policyLanguage", + policyLanguage, 1); + if (result < 0) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + result = asn1_write_value(c2, "proxyPolicy.policy", + policy, sizeof_policy); + if (result < 0) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + result = _gnutls_x509_der_encode(c2, "", ext, 0); + if (result < 0) { + gnutls_assert(); + goto cleanup; + } + + result = 0; + cleanup: + asn1_delete_structure(&c2); + return result; + +} + +static int decode_user_notice(const void *data, size_t size, + gnutls_datum_t * txt) +{ + asn1_node c2 = NULL; + int ret, len; + char choice_type[64]; + char name[128]; + gnutls_datum_t td = {NULL,0}, utd; + + ret = asn1_create_element(_gnutls_get_pkix(), "PKIX1.UserNotice", &c2); + if (ret != ASN1_SUCCESS) { + gnutls_assert(); + ret = GNUTLS_E_PARSING_ERROR; + goto cleanup; + } + + ret = _asn1_strict_der_decode(&c2, data, size, NULL); + if (ret != ASN1_SUCCESS) { + gnutls_assert(); + ret = GNUTLS_E_PARSING_ERROR; + goto cleanup; + } + + len = sizeof(choice_type); + ret = asn1_read_value(c2, "explicitText", choice_type, &len); + if (ret != ASN1_SUCCESS) { + gnutls_assert(); + ret = GNUTLS_E_PARSING_ERROR; + goto cleanup; + } + + if (strcmp(choice_type, "utf8String") != 0 + && strcmp(choice_type, "ia5String") != 0 + && strcmp(choice_type, "bmpString") != 0 + && strcmp(choice_type, "visibleString") != 0) { + gnutls_assert(); + ret = GNUTLS_E_PARSING_ERROR; + goto cleanup; + } + + snprintf(name, sizeof(name), "explicitText.%s", choice_type); + + ret = _gnutls_x509_read_value(c2, name, &td); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + if (strcmp(choice_type, "bmpString") == 0) { /* convert to UTF-8 */ + ret = _gnutls_ucs2_to_utf8(td.data, td.size, &utd, 1); + _gnutls_free_datum(&td); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + td.data = utd.data; + td.size = utd.size; + } else { + /* _gnutls_x509_read_value allows that */ + td.data[td.size] = 0; + } + + txt->data = (void *)td.data; + txt->size = td.size; + ret = 0; + + cleanup: + asn1_delete_structure(&c2); + return ret; + +} + +struct gnutls_x509_policies_st { + struct gnutls_x509_policy_st policy[MAX_ENTRIES]; + unsigned int size; +}; + +/** + * gnutls_x509_policies_init: + * @policies: The authority key ID + * + * This function will initialize an authority key ID type. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value. + * + * Since: 3.3.0 + **/ +int gnutls_x509_policies_init(gnutls_x509_policies_t * policies) +{ + *policies = gnutls_calloc(1, sizeof(struct gnutls_x509_policies_st)); + if (*policies == NULL) + return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + + return 0; +} + +/** + * gnutls_x509_policies_deinit: + * @policies: The authority key identifier + * + * This function will deinitialize an authority key identifier type. + * + * Since: 3.3.0 + **/ +void gnutls_x509_policies_deinit(gnutls_x509_policies_t policies) +{ + unsigned i; + + for (i = 0; i < policies->size; i++) { + gnutls_x509_policy_release(&policies->policy[i]); + } + gnutls_free(policies); +} + +/** + * gnutls_x509_policies_get: + * @policies: The policies + * @seq: The index of the name to get + * @policy: Will hold the policy + * + * This function will return a specific policy as stored in + * the @policies type. The returned values should be treated as constant + * and valid for the lifetime of @policies. + * + * The any policy OID is available as the %GNUTLS_X509_OID_POLICY_ANY macro. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE + * if the index is out of bounds, otherwise a negative error value. + * + * Since: 3.3.0 + **/ +int gnutls_x509_policies_get(gnutls_x509_policies_t policies, + unsigned int seq, + struct gnutls_x509_policy_st *policy) +{ + if (seq >= policies->size) + return gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE); + + if (policy) { + memcpy(policy, &policies->policy[seq], + sizeof(struct gnutls_x509_policy_st)); + } + + return 0; +} + +void _gnutls_x509_policies_erase(gnutls_x509_policies_t policies, + unsigned int seq) +{ + if (seq >= policies->size) + return; + + memset(&policies->policy[seq], 0, sizeof(struct gnutls_x509_policy_st)); +} + +/** + * gnutls_x509_policies_set: + * @policies: An initialized policies + * @seq: The index of the name to get + * @policy: Contains the policy to set + * + * This function will store the specified policy in + * the provided @policies. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0), otherwise a negative error value. + * + * Since: 3.3.0 + **/ +int gnutls_x509_policies_set(gnutls_x509_policies_t policies, + const struct gnutls_x509_policy_st *policy) +{ + unsigned i; + + if (policies->size + 1 > MAX_ENTRIES) + return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + + policies->policy[policies->size].oid = gnutls_strdup(policy->oid); + if (policies->policy[policies->size].oid == NULL) + return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + + for (i = 0; i < policy->qualifiers; i++) { + policies->policy[policies->size].qualifier[i].type = + policy->qualifier[i].type; + policies->policy[policies->size].qualifier[i].size = + policy->qualifier[i].size; + policies->policy[policies->size].qualifier[i].data = + gnutls_malloc(policy->qualifier[i].size + 1); + if (policies->policy[policies->size].qualifier[i].data == NULL) + return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + memcpy(policies->policy[policies->size].qualifier[i].data, + policy->qualifier[i].data, policy->qualifier[i].size); + policies->policy[policies->size].qualifier[i].data[policy-> + qualifier[i]. + size] = 0; + } + + policies->policy[policies->size].qualifiers = policy->qualifiers; + policies->size++; + + return 0; +} + +/** + * gnutls_x509_ext_import_policies: + * @ext: the DER encoded extension data + * @policies: A pointer to an initialized policies. + * @flags: should be zero + * + * This function will extract the certificate policy extension (2.5.29.32) + * and store it the provided policies. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value. + * + * Since: 3.3.0 + **/ +int gnutls_x509_ext_import_policies(const gnutls_datum_t * ext, + gnutls_x509_policies_t policies, + unsigned int flags) +{ + asn1_node c2 = NULL; + char tmpstr[128]; + char tmpoid[MAX_OID_SIZE]; + gnutls_datum_t tmpd = { NULL, 0 }; + int ret, len; + unsigned i, j, current = 0; + + ret = asn1_create_element + (_gnutls_get_pkix(), "PKIX1.certificatePolicies", &c2); + if (ret != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(ret); + goto cleanup; + } + + ret = _asn1_strict_der_decode(&c2, ext->data, ext->size, NULL); + if (ret != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(ret); + goto cleanup; + } + + for (j = 0;; j++) { + if (j >= MAX_ENTRIES) + break; + + memset(&policies->policy[j], 0, + sizeof(struct gnutls_x509_policy_st)); + + /* create a string like "?1" + */ + snprintf(tmpstr, sizeof(tmpstr), "?%u.policyIdentifier", j + 1); + current = j+1; + + ret = _gnutls_x509_read_value(c2, tmpstr, &tmpd); + if (ret == GNUTLS_E_ASN1_ELEMENT_NOT_FOUND) + break; + + if (ret < 0) { + gnutls_assert(); + goto full_cleanup; + } + + policies->policy[j].oid = (void *)tmpd.data; + tmpd.data = NULL; + + for (i = 0; i < GNUTLS_MAX_QUALIFIERS; i++) { + gnutls_datum_t td; + + snprintf(tmpstr, sizeof(tmpstr), + "?%u.policyQualifiers.?%u.policyQualifierId", + j + 1, i + 1); + + len = sizeof(tmpoid); + ret = asn1_read_value(c2, tmpstr, tmpoid, &len); + + if (ret == ASN1_ELEMENT_NOT_FOUND) + break; /* finished */ + + if (ret != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(ret); + goto full_cleanup; + } + + if (strcmp(tmpoid, "1.3.6.1.5.5.7.2.1") == 0) { + snprintf(tmpstr, sizeof(tmpstr), + "?%u.policyQualifiers.?%u.qualifier", + j + 1, i + 1); + + ret = + _gnutls_x509_read_string(c2, tmpstr, &td, + ASN1_ETYPE_IA5_STRING, 0); + if (ret < 0) { + gnutls_assert(); + goto full_cleanup; + } + + policies->policy[j].qualifier[i].data = + (void *)td.data; + policies->policy[j].qualifier[i].size = td.size; + td.data = NULL; + policies->policy[j].qualifier[i].type = + GNUTLS_X509_QUALIFIER_URI; + } else if (strcmp(tmpoid, "1.3.6.1.5.5.7.2.2") == 0) { + gnutls_datum_t txt = {NULL, 0}; + + snprintf(tmpstr, sizeof(tmpstr), + "?%u.policyQualifiers.?%u.qualifier", + j + 1, i + 1); + + ret = _gnutls_x509_read_value(c2, tmpstr, &td); + if (ret < 0) { + gnutls_assert(); + goto full_cleanup; + } + + ret = + decode_user_notice(td.data, td.size, &txt); + gnutls_free(td.data); + + if (ret < 0) { + gnutls_assert(); + goto full_cleanup; + } + + policies->policy[j].qualifier[i].data = + (void *)txt.data; + policies->policy[j].qualifier[i].size = + txt.size; + policies->policy[j].qualifier[i].type = + GNUTLS_X509_QUALIFIER_NOTICE; + } else + policies->policy[j].qualifier[i].type = + GNUTLS_X509_QUALIFIER_UNKNOWN; + + policies->policy[j].qualifiers++; + } + + } + + policies->size = j; + + ret = 0; + goto cleanup; + + full_cleanup: + for (j = 0; j < current; j++) + gnutls_x509_policy_release(&policies->policy[j]); + + cleanup: + _gnutls_free_datum(&tmpd); + asn1_delete_structure(&c2); + return ret; + +} + +static int encode_user_notice(const gnutls_datum_t * txt, + gnutls_datum_t * der_data) +{ + int result; + asn1_node c2 = NULL; + + if ((result = + asn1_create_element(_gnutls_get_pkix(), + "PKIX1.UserNotice", &c2)) != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto error; + } + + /* delete noticeRef */ + result = asn1_write_value(c2, "noticeRef", NULL, 0); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto error; + } + + result = asn1_write_value(c2, "explicitText", "utf8String", 1); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto error; + } + + result = + asn1_write_value(c2, "explicitText.utf8String", txt->data, + txt->size); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto error; + } + + result = _gnutls_x509_der_encode(c2, "", der_data, 0); + if (result < 0) { + gnutls_assert(); + goto error; + } + + result = 0; + + error: + asn1_delete_structure(&c2); + return result; + +} + +/** + * gnutls_x509_ext_export_policies: + * @policies: A pointer to an initialized policies. + * @ext: The DER-encoded extension data; must be freed using gnutls_free(). + * + * This function will convert the provided policies, to a certificate policy + * DER encoded extension (2.5.29.32). + * + * The @ext data will be allocated using gnutls_malloc(). + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value. + * + * Since: 3.3.0 + **/ +int gnutls_x509_ext_export_policies(gnutls_x509_policies_t policies, + gnutls_datum_t * ext) +{ + int result; + unsigned i, j; + gnutls_datum_t der_data = {NULL, 0}, tmpd; + asn1_node c2 = NULL; + const char *oid; + + result = + asn1_create_element(_gnutls_get_pkix(), + "PKIX1.certificatePolicies", &c2); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + for (j = 0; j < policies->size; j++) { + /* 1. write a new policy */ + result = asn1_write_value(c2, "", "NEW", 1); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + /* 2. Add the OID. + */ + result = + asn1_write_value(c2, "?LAST.policyIdentifier", + policies->policy[j].oid, 1); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + if (policies->policy[j].qualifiers == 0) { + /* remove the optional policyQualifiers if none are present. */ + result = asn1_write_value(c2, "?LAST.policyQualifiers", NULL, 0); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + } + + for (i = 0; + i < MIN(policies->policy[j].qualifiers, + GNUTLS_MAX_QUALIFIERS); i++) { + result = + asn1_write_value(c2, "?LAST.policyQualifiers", + "NEW", 1); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + if (policies->policy[j].qualifier[i].type == + GNUTLS_X509_QUALIFIER_URI) + oid = "1.3.6.1.5.5.7.2.1"; + else if (policies->policy[j].qualifier[i].type == + GNUTLS_X509_QUALIFIER_NOTICE) + oid = "1.3.6.1.5.5.7.2.2"; + else { + result = + gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + goto cleanup; + } + + result = + asn1_write_value(c2, + "?LAST.policyQualifiers.?LAST.policyQualifierId", + oid, 1); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + if (policies->policy[j].qualifier[i].type == + GNUTLS_X509_QUALIFIER_URI) { + tmpd.data = + (void *)policies->policy[j].qualifier[i]. + data; + tmpd.size = + policies->policy[j].qualifier[i].size; + result = + _gnutls_x509_write_string(c2, + "?LAST.policyQualifiers.?LAST.qualifier", + &tmpd, + ASN1_ETYPE_IA5_STRING); + if (result < 0) { + gnutls_assert(); + goto cleanup; + } + } else if (policies->policy[j].qualifier[i].type == + GNUTLS_X509_QUALIFIER_NOTICE) { + tmpd.data = + (void *)policies->policy[j].qualifier[i]. + data; + tmpd.size = + policies->policy[j].qualifier[i].size; + + if (tmpd.size > 200) { + gnutls_assert(); + result = GNUTLS_E_INVALID_REQUEST; + goto cleanup; + } + + result = encode_user_notice(&tmpd, &der_data); + if (result < 0) { + gnutls_assert(); + goto cleanup; + } + + result = + _gnutls_x509_write_value(c2, + "?LAST.policyQualifiers.?LAST.qualifier", + &der_data); + _gnutls_free_datum(&der_data); + if (result < 0) { + gnutls_assert(); + goto cleanup; + } + } + } + } + + result = _gnutls_x509_der_encode(c2, "", ext, 0); + if (result < 0) { + gnutls_assert(); + goto cleanup; + } + + cleanup: + asn1_delete_structure(&c2); + + return result; +} + +struct crl_dist_point_st { + unsigned int type; + gnutls_datum_t san; + unsigned int reasons; +}; + +struct gnutls_x509_crl_dist_points_st { + struct crl_dist_point_st *points; + unsigned int size; +}; + +/** + * gnutls_x509_crl_dist_points_init: + * @cdp: The CRL distribution points + * + * This function will initialize a CRL distribution points type. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value. + * + * Since: 3.3.0 + **/ +int gnutls_x509_crl_dist_points_init(gnutls_x509_crl_dist_points_t * cdp) +{ + *cdp = gnutls_calloc(1, sizeof(struct gnutls_x509_crl_dist_points_st)); + if (*cdp == NULL) + return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + + return 0; +} + +/** + * gnutls_x509_crl_dist_points_deinit: + * @cdp: The CRL distribution points + * + * This function will deinitialize a CRL distribution points type. + * + * Since: 3.3.0 + **/ +void gnutls_x509_crl_dist_points_deinit(gnutls_x509_crl_dist_points_t cdp) +{ + unsigned i; + + for (i = 0; i < cdp->size; i++) { + gnutls_free(cdp->points[i].san.data); + } + gnutls_free(cdp->points); + gnutls_free(cdp); +} + +/** + * gnutls_x509_crl_dist_points_get: + * @cdp: The CRL distribution points + * @seq: specifies the sequence number of the distribution point (0 for the first one, 1 for the second etc.) + * @type: The name type of the corresponding name (gnutls_x509_subject_alt_name_t) + * @san: The distribution point names (to be treated as constant) + * @reasons: Revocation reasons. An ORed sequence of flags from %gnutls_x509_crl_reason_flags_t. + * + * This function retrieves the individual CRL distribution points (2.5.29.31), + * contained in provided type. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE + * if the index is out of bounds, otherwise a negative error value. + **/ + +int gnutls_x509_crl_dist_points_get(gnutls_x509_crl_dist_points_t cdp, + unsigned int seq, unsigned int *type, + gnutls_datum_t * san, unsigned int *reasons) +{ + if (seq >= cdp->size) + return gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE); + + if (reasons) + *reasons = cdp->points[seq].reasons; + + if (type) + *type = cdp->points[seq].type; + + if (san) { + san->data = cdp->points[seq].san.data; + san->size = cdp->points[seq].san.size; + } + + return 0; +} + +static +int crl_dist_points_set(gnutls_x509_crl_dist_points_t cdp, + gnutls_x509_subject_alt_name_t type, + const gnutls_datum_t * san, unsigned int reasons) +{ + void *tmp; + + if (unlikely(INT_ADD_OVERFLOW(cdp->size, 1))) { + return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + } + + /* new dist point */ + tmp = _gnutls_reallocarray(cdp->points, cdp->size + 1, + sizeof(cdp->points[0])); + if (tmp == NULL) { + return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + } + cdp->points = tmp; + + cdp->points[cdp->size].type = type; + cdp->points[cdp->size].san.data = san->data; + cdp->points[cdp->size].san.size = san->size; + cdp->points[cdp->size].reasons = reasons; + + cdp->size++; + return 0; + +} + +/** + * gnutls_x509_crl_dist_points_set: + * @cdp: The CRL distribution points + * @type: The type of the name (of %gnutls_subject_alt_names_t) + * @san: The point name data + * @reasons: Revocation reasons. An ORed sequence of flags from %gnutls_x509_crl_reason_flags_t. + * + * This function will store the specified CRL distribution point value + * the @cdp type. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0), otherwise a negative error value. + * + * Since: 3.3.0 + **/ +int gnutls_x509_crl_dist_points_set(gnutls_x509_crl_dist_points_t cdp, + gnutls_x509_subject_alt_name_t type, + const gnutls_datum_t * san, + unsigned int reasons) +{ + int ret; + gnutls_datum_t t_san; + + ret = _gnutls_set_datum(&t_san, san->data, san->size); + if (ret < 0) + return gnutls_assert_val(ret); + + ret = crl_dist_points_set(cdp, type, &t_san, reasons); + if (ret < 0) { + gnutls_free(t_san.data); + return gnutls_assert_val(ret); + } + + return 0; +} + +/** + * gnutls_x509_ext_import_crl_dist_points: + * @ext: the DER encoded extension data + * @cdp: A pointer to an initialized CRL distribution points. + * @flags: should be zero + * + * This function will extract the CRL distribution points extension (2.5.29.31) + * and store it into the provided type. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value. + * + * Since: 3.3.0 + **/ +int gnutls_x509_ext_import_crl_dist_points(const gnutls_datum_t * ext, + gnutls_x509_crl_dist_points_t cdp, + unsigned int flags) +{ + int result; + asn1_node c2 = NULL; + char name[MAX_NAME_SIZE]; + int len, ret; + uint8_t reasons[2]; + unsigned i, type, rflags, j; + gnutls_datum_t san = {NULL, 0}; + + result = asn1_create_element + (_gnutls_get_pkix(), "PKIX1.CRLDistributionPoints", &c2); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + result = _asn1_strict_der_decode(&c2, ext->data, ext->size, NULL); + + if (result != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(result); + goto cleanup; + } + + /* Return the different names from the first CRLDistr. point. + * The whole thing is a mess. + */ + + i = 0; + do { + snprintf(name, sizeof(name), "?%u.reasons", (unsigned)i + 1); + + len = sizeof(reasons); + result = asn1_read_value(c2, name, reasons, &len); + + if (result != ASN1_VALUE_NOT_FOUND && + result != ASN1_ELEMENT_NOT_FOUND && + result != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(result); + break; + } + + if (result == ASN1_VALUE_NOT_FOUND + || result == ASN1_ELEMENT_NOT_FOUND) + rflags = 0; + else + rflags = reasons[0] | (reasons[1] << 8); + + snprintf(name, sizeof(name), + "?%u.distributionPoint.fullName", (unsigned)i + 1); + + for (j=0;;j++) { + san.data = NULL; + san.size = 0; + + ret = + _gnutls_parse_general_name2(c2, name, j, &san, + &type, 0); + if (j > 0 + && ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { + ret = 0; + break; + } + if (ret < 0) + break; + + ret = crl_dist_points_set(cdp, type, &san, rflags); + if (ret < 0) + break; + san.data = NULL; /* it is now in cdp */ + } + + i++; + } while (ret >= 0); + + if (ret < 0 && ret != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { + gnutls_assert(); + gnutls_free(san.data); + goto cleanup; + } + + ret = 0; + cleanup: + asn1_delete_structure(&c2); + return ret; +} + +/** + * gnutls_x509_ext_export_crl_dist_points: + * @cdp: A pointer to an initialized CRL distribution points. + * @ext: The DER-encoded extension data; must be freed using gnutls_free(). + * + * This function will convert the provided policies, to a certificate policy + * DER encoded extension (2.5.29.31). + * + * The @ext data will be allocated using gnutls_malloc(). + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value. + * + * Since: 3.3.0 + **/ +int gnutls_x509_ext_export_crl_dist_points(gnutls_x509_crl_dist_points_t cdp, + gnutls_datum_t * ext) +{ + asn1_node c2 = NULL; + int result; + uint8_t reasons[2]; + unsigned i; + + result = + asn1_create_element(_gnutls_get_pkix(), + "PKIX1.CRLDistributionPoints", &c2); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + for (i = 0; i < cdp->size; i++) { + + if (i == 0 + || cdp->points[i].reasons != cdp->points[i - 1].reasons) { + result = asn1_write_value(c2, "", "NEW", 1); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + if (cdp->points[i].reasons) { + reasons[0] = cdp->points[i].reasons & 0xff; + reasons[1] = cdp->points[i].reasons >> 8; + + result = + asn1_write_value(c2, "?LAST.reasons", + reasons, 2); + } else { + result = + asn1_write_value(c2, "?LAST.reasons", NULL, + 0); + } + + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + result = + asn1_write_value(c2, "?LAST.cRLIssuer", NULL, 0); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + /* When used as type CHOICE. + */ + result = + asn1_write_value(c2, "?LAST.distributionPoint", + "fullName", 1); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + } + + result = + _gnutls_write_new_general_name(c2, + "?LAST.distributionPoint.fullName", + cdp->points[i].type, + cdp->points[i].san.data, + cdp->points[i].san.size); + if (result < 0) { + gnutls_assert(); + goto cleanup; + } + } + + result = _gnutls_x509_der_encode(c2, "", ext, 0); + if (result < 0) { + gnutls_assert(); + goto cleanup; + } + + result = 0; + + cleanup: + asn1_delete_structure(&c2); + + return result; + +} + +struct gnutls_x509_aia_st { + struct { + gnutls_datum_t oid; + unsigned int san_type; + gnutls_datum_t san; + } *aia; + unsigned int size; +}; + +/** + * gnutls_x509_aia_init: + * @aia: The authority info access + * + * This function will initialize an authority info access type. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value. + * + * Since: 3.3.0 + **/ +int gnutls_x509_aia_init(gnutls_x509_aia_t * aia) +{ + *aia = gnutls_calloc(1, sizeof(struct gnutls_x509_aia_st)); + if (*aia == NULL) + return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + + return 0; +} + +/** + * gnutls_x509_aia_deinit: + * @aia: The authority info access + * + * This function will deinitialize an authority info access type. + * + * Since: 3.3.0 + **/ +void gnutls_x509_aia_deinit(gnutls_x509_aia_t aia) +{ + unsigned i; + + for (i = 0; i < aia->size; i++) { + gnutls_free(aia->aia[i].san.data); + gnutls_free(aia->aia[i].oid.data); + } + gnutls_free(aia->aia); + gnutls_free(aia); +} + +/** + * gnutls_x509_aia_get: + * @aia: The authority info access + * @seq: specifies the sequence number of the access descriptor (0 for the first one, 1 for the second etc.) + * @oid: the type of available data; to be treated as constant. + * @san_type: Will hold the type of the name of %gnutls_subject_alt_names_t (may be null). + * @san: the access location name; to be treated as constant (may be null). + * + * This function reads from the Authority Information Access type. + * + * The @seq input parameter is used to indicate which member of the + * sequence the caller is interested in. The first member is 0, the + * second member 1 and so on. When the @seq value is out of bounds, + * %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE is returned. + * + * Typically @oid is %GNUTLS_OID_AD_CAISSUERS or %GNUTLS_OID_AD_OCSP. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value. + * + * Since: 3.3.0 + **/ +int gnutls_x509_aia_get(gnutls_x509_aia_t aia, unsigned int seq, + gnutls_datum_t *oid, + unsigned *san_type, + gnutls_datum_t *san) +{ + if (seq >= aia->size) + return gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE); + + if (san_type) + *san_type = aia->aia[seq].san_type; + if (san) { + san->data = aia->aia[seq].san.data; + san->size = aia->aia[seq].san.size; + } + + if (oid) { + oid->data = aia->aia[seq].oid.data; + oid->size = aia->aia[seq].oid.size; + } + + return 0; +} + +int _gnutls_alt_name_process(gnutls_datum_t *out, unsigned type, const gnutls_datum_t *san, unsigned raw) +{ + int ret; + if (type == GNUTLS_SAN_DNSNAME && !raw) { + ret = gnutls_idna_map((char*)san->data, san->size, out, 0); + if (ret < 0) { + return gnutls_assert_val(ret); + } + } else if (type == GNUTLS_SAN_RFC822NAME && !raw) { + ret = _gnutls_idna_email_map((char*)san->data, san->size, out); + if (ret < 0) { + return gnutls_assert_val(ret); + } + } else if (type == GNUTLS_SAN_URI && !raw) { + if (!_gnutls_str_is_print((char*)san->data, san->size)) { + _gnutls_debug_log("non-ASCII URIs are not supported\n"); + return gnutls_assert_val(GNUTLS_E_UNIMPLEMENTED_FEATURE); + } else { + ret = _gnutls_set_strdatum(out, san->data, san->size); + if (ret < 0) + return gnutls_assert_val(ret); + } + } else { + ret = _gnutls_set_strdatum(out, san->data, san->size); + if (ret < 0) + return gnutls_assert_val(ret); + } + + return 0; +} + +/** + * gnutls_x509_aia_set: + * @aia: The authority info access + * @oid: the type of data. + * @san_type: The type of the name (of %gnutls_subject_alt_names_t) + * @san: The alternative name data + * @othername_oid: The object identifier if @san_type is %GNUTLS_SAN_OTHERNAME + * + * This function will store the specified alternative name in + * the @aia type. + * + * Typically the value for @oid should be %GNUTLS_OID_AD_OCSP, or + * %GNUTLS_OID_AD_CAISSUERS. + * + * Since version 3.5.7 the %GNUTLS_SAN_RFC822NAME, and %GNUTLS_SAN_DNSNAME, + * are converted to ACE format when necessary. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0), otherwise a negative error value. + * + * Since: 3.3.0 + **/ +int gnutls_x509_aia_set(gnutls_x509_aia_t aia, + const char *oid, + unsigned san_type, + const gnutls_datum_t * san) +{ + int ret; + void *tmp; + unsigned indx; + + if (unlikely(INT_ADD_OVERFLOW(aia->size, 1))) { + return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + } + + tmp = _gnutls_reallocarray(aia->aia, aia->size + 1, sizeof(aia->aia[0])); + if (tmp == NULL) { + return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + } + aia->aia = tmp; + indx = aia->size; + + aia->aia[indx].san_type = san_type; + if (oid) { + aia->aia[indx].oid.data = (void*)gnutls_strdup(oid); + aia->aia[indx].oid.size = strlen(oid); + } else { + aia->aia[indx].oid.data = NULL; + aia->aia[indx].oid.size = 0; + } + + ret = _gnutls_alt_name_process(&aia->aia[indx].san, san_type, san, 0); + if (ret < 0) + return gnutls_assert_val(ret); + + aia->size++; + + return 0; +} + + +static int parse_aia(asn1_node c2, gnutls_x509_aia_t aia) +{ + int len; + char nptr[MAX_NAME_SIZE]; + int ret, result; + char tmpoid[MAX_OID_SIZE]; + void * tmp; + unsigned i, indx; + + for (i = 1;; i++) { + snprintf(nptr, sizeof(nptr), "?%u.accessMethod", i); + + len = sizeof(tmpoid); + result = asn1_read_value(c2, nptr, tmpoid, &len); + if (result == ASN1_VALUE_NOT_FOUND + || result == ASN1_ELEMENT_NOT_FOUND) { + ret = GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + break; + } + + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + indx = aia->size; + if (unlikely(INT_ADD_OVERFLOW(aia->size, 1))) { + return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + } + tmp = _gnutls_reallocarray(aia->aia, aia->size + 1, + sizeof(aia->aia[0])); + if (tmp == NULL) { + return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + } + aia->aia = tmp; + + snprintf(nptr, sizeof(nptr), "?%u.accessLocation", i); + + + ret = _gnutls_parse_general_name2(c2, nptr, -1, &aia->aia[indx].san, + &aia->aia[indx].san_type, 0); + if (ret < 0) + break; + + /* we do the strdup after parsing to avoid a memory leak */ + aia->aia[indx].oid.data = (void*)gnutls_strdup(tmpoid); + aia->aia[indx].oid.size = strlen(tmpoid); + + aia->size++; + + if (aia->aia[indx].oid.data == NULL) { + gnutls_assert(); + return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + } + } + + assert(ret < 0); + if (ret != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { + return ret; + } + + return 0; +} + +/** + * gnutls_x509_ext_import_aia: + * @ext: The DER-encoded extension data + * @aia: The authority info access + * @flags: should be zero + * + * This function extracts the Authority Information Access (AIA) + * extension from the provided DER-encoded data; see RFC 5280 section 4.2.2.1 + * for more information on the extension. The + * AIA extension holds a sequence of AccessDescription (AD) data. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value. + * + * Since: 3.3.0 + **/ +int gnutls_x509_ext_import_aia(const gnutls_datum_t * ext, + gnutls_x509_aia_t aia, + unsigned int flags) +{ + int ret; + asn1_node c2 = NULL; + + if (ext->size == 0 || ext->data == NULL) { + gnutls_assert(); + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + } + + ret = asn1_create_element(_gnutls_get_pkix(), + "PKIX1.AuthorityInfoAccessSyntax", &c2); + if (ret != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(ret); + } + + ret = _asn1_strict_der_decode(&c2, ext->data, ext->size, NULL); + if (ret != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(ret); + goto cleanup; + } + + ret = parse_aia(c2, aia); + if (ret < 0) { + gnutls_assert(); + } + + cleanup: + asn1_delete_structure(&c2); + + return ret; + +} + +/** + * gnutls_x509_ext_export_aia: + * @aia: The authority info access + * @ext: The DER-encoded extension data; must be freed using gnutls_free(). + * + * This function will DER encode the Authority Information Access (AIA) + * extension; see RFC 5280 section 4.2.2.1 for more information on the + * extension. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 3.3.0 + **/ +int gnutls_x509_ext_export_aia(gnutls_x509_aia_t aia, + gnutls_datum_t * ext) +{ + int ret, result; + asn1_node c2 = NULL; + unsigned int i; + + ret = asn1_create_element(_gnutls_get_pkix(), + "PKIX1.AuthorityInfoAccessSyntax", &c2); + if (ret != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(ret); + } + + /* 1. create a new element. + */ + for (i=0;i<aia->size;i++) { + result = asn1_write_value(c2, "", "NEW", 1); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(result); + goto cleanup; + } + + /* 2. Add the OID. + */ + result = asn1_write_value(c2, "?LAST.accessMethod", aia->aia[i].oid.data, 1); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(result); + goto cleanup; + } + + ret = + _gnutls_write_general_name(c2, + "?LAST.accessLocation", + aia->aia[i].san_type, + aia->aia[i].san.data, + aia->aia[i].san.size); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + } + + ret = _gnutls_x509_der_encode(c2, "", ext, 0); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + cleanup: + asn1_delete_structure(&c2); + + return ret; +} + + +struct gnutls_x509_key_purposes_st { + gnutls_datum_t oid[MAX_ENTRIES]; + unsigned int size; +}; + +/** + * gnutls_subject_alt_names_init: + * @p: The key purposes + * + * This function will initialize an alternative names type. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value. + * + * Since: 3.3.0 + **/ +int gnutls_x509_key_purpose_init(gnutls_x509_key_purposes_t * p) +{ + *p = gnutls_calloc(1, sizeof(struct gnutls_x509_key_purposes_st)); + if (*p == NULL) { + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; + } + + return 0; +} + +static void key_purposes_deinit(gnutls_x509_key_purposes_t p) +{ + unsigned int i; + + for (i = 0; i < p->size; i++) { + gnutls_free(p->oid[i].data); + } +} + +/** + * gnutls_x509_key_purpose_deinit: + * @p: The key purposes + * + * This function will deinitialize a key purposes type. + * + * Since: 3.3.0 + **/ +void gnutls_x509_key_purpose_deinit(gnutls_x509_key_purposes_t p) +{ + key_purposes_deinit(p); + gnutls_free(p); +} + +/** + * gnutls_x509_key_purpose_set: + * @p: The key purposes + * @oid: The object identifier of the key purpose + * + * This function will store the specified key purpose in the + * purposes. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0), otherwise a negative error value. + * + * Since: 3.3.0 + **/ +int gnutls_x509_key_purpose_set(gnutls_x509_key_purposes_t p, const char *oid) +{ + if (p->size + 1 > MAX_ENTRIES) + return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + + p->oid[p->size].data = (void*)gnutls_strdup(oid); + if (p->oid[p->size].data == NULL) + return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + + p->oid[p->size].size = strlen(oid); + p->size++; + + return 0; +} + +/** + * gnutls_x509_key_purpose_get: + * @p: The key purposes + * @idx: The index of the key purpose to retrieve + * @oid: Will hold the object identifier of the key purpose (to be treated as constant) + * + * This function will retrieve the specified by the index key purpose in the + * purposes type. The object identifier will be a null terminated string. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE + * if the index is out of bounds, otherwise a negative error value. + * + * Since: 3.3.0 + **/ +int gnutls_x509_key_purpose_get(gnutls_x509_key_purposes_t p, unsigned idx, gnutls_datum_t *oid) +{ + if (idx >= p->size) + return gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE); + + oid->data = p->oid[idx].data; + oid->size = p->oid[idx].size; + + return 0; +} + +/** + * gnutls_x509_ext_import_key_purposes: + * @ext: The DER-encoded extension data + * @p: The key purposes + * @flags: should be zero + * + * This function will extract the key purposes in the provided DER-encoded + * ExtKeyUsageSyntax PKIX extension, to a %gnutls_x509_key_purposes_t type. + * The data must be initialized. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value. + * + * Since: 3.3.0 + **/ +int gnutls_x509_ext_import_key_purposes(const gnutls_datum_t * ext, + gnutls_x509_key_purposes_t p, + unsigned int flags) +{ + char tmpstr[MAX_NAME_SIZE]; + int result, ret; + asn1_node c2 = NULL; + gnutls_datum_t oid = {NULL, 0}; + unsigned i; + + result = asn1_create_element + (_gnutls_get_pkix(), "PKIX1.ExtKeyUsageSyntax", &c2); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + result = _asn1_strict_der_decode(&c2, ext->data, ext->size, NULL); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(result); + goto cleanup; + } + + key_purposes_deinit(p); + i = 0; + p->size = 0; + + for (;i<MAX_ENTRIES;i++) { + /* create a string like "?1" + */ + snprintf(tmpstr, sizeof(tmpstr), "?%u", i+1); + + ret = _gnutls_x509_read_value(c2, tmpstr, &oid); + if (ret == GNUTLS_E_ASN1_ELEMENT_NOT_FOUND) { + break; + } + + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + p->oid[i].data = oid.data; + p->oid[i].size = oid.size; + + oid.data = NULL; + oid.size = 0; + p->size++; + } + + ret = 0; + cleanup: + gnutls_free(oid.data); + asn1_delete_structure(&c2); + + return ret; + +} + +/** + * gnutls_x509_ext_export_key_purposes: + * @p: The key purposes + * @ext: The DER-encoded extension data; must be freed using gnutls_free(). + * + * This function will convert the key purposes type to a + * DER-encoded PKIX ExtKeyUsageSyntax (2.5.29.37) extension. The output data in + * @ext will be allocated using gnutls_malloc(). + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value. + * + * Since: 3.3.0 + **/ +int gnutls_x509_ext_export_key_purposes(gnutls_x509_key_purposes_t p, + gnutls_datum_t * ext) +{ + int result, ret; + asn1_node c2 = NULL; + unsigned i; + + result = asn1_create_element + (_gnutls_get_pkix(), "PKIX1.ExtKeyUsageSyntax", &c2); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + /* generate the extension. + */ + for (i=0;i<p->size;i++) { + /* 1. create a new element. + */ + result = asn1_write_value(c2, "", "NEW", 1); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(result); + goto cleanup; + } + + /* 2. Add the OID. + */ + result = asn1_write_value(c2, "?LAST", p->oid[i].data, 1); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(result); + goto cleanup; + } + } + + ret = _gnutls_x509_der_encode(c2, "", ext, 0); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = 0; + + cleanup: + asn1_delete_structure(&c2); + return ret; +} + +/** + * gnutls_ext_deinit: + * @ext: The extensions structure + * + * This function will deinitialize an extensions structure. + * + * Since: 3.3.8 + **/ +void gnutls_x509_ext_deinit(gnutls_x509_ext_st *ext) +{ + gnutls_free(ext->oid); + gnutls_free(ext->data.data); +} + +int _gnutls_x509_decode_ext(const gnutls_datum_t *der, gnutls_x509_ext_st *out) +{ + asn1_node c2 = NULL; + char str_critical[10]; + char oid[MAX_OID_SIZE]; + int result, len, ret; + + memset(out, 0, sizeof(*out)); + + /* decode der */ + result = asn1_create_element(_gnutls_get_pkix(), "PKIX1.Extension", &c2); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + result = _asn1_strict_der_decode(&c2, der->data, der->size, NULL); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(result); + goto cleanup; + } + + len = sizeof(oid)-1; + result = asn1_read_value(c2, "extnID", oid, &len); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(result); + goto cleanup; + } + + len = sizeof(str_critical)-1; + result = asn1_read_value(c2, "critical", str_critical, &len); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(result); + goto cleanup; + } + + if (str_critical[0] == 'T') + out->critical = 1; + else + out->critical = 0; + + ret = _gnutls_x509_read_value(c2, "extnValue", &out->data); + if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE || ret == GNUTLS_E_ASN1_ELEMENT_NOT_FOUND) { + out->data.data = NULL; + out->data.size = 0; + } else if (ret < 0) { + gnutls_assert(); + goto fail; + } + + out->oid = gnutls_strdup(oid); + if (out->oid == NULL) { + ret = GNUTLS_E_MEMORY_ERROR; + goto fail; + } + + ret = 0; + goto cleanup; + fail: + memset(out, 0, sizeof(*out)); + cleanup: + asn1_delete_structure(&c2); + return ret; + +} + +/* flags can be zero or GNUTLS_EXT_FLAG_APPEND + */ +static int parse_tlsfeatures(asn1_node c2, gnutls_x509_tlsfeatures_t f, unsigned flags) +{ + char nptr[MAX_NAME_SIZE]; + int result; + unsigned i, indx, j; + unsigned int feature; + + if (!(flags & GNUTLS_EXT_FLAG_APPEND)) + f->size = 0; + + for (i = 1;; i++) { + unsigned skip = 0; + snprintf(nptr, sizeof(nptr), "?%u", i); + + result = _gnutls_x509_read_uint(c2, nptr, &feature); + + if (result == GNUTLS_E_ASN1_ELEMENT_NOT_FOUND || result == GNUTLS_E_ASN1_VALUE_NOT_FOUND) { + break; + } + else if (result != GNUTLS_E_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + if (feature > UINT16_MAX) { + gnutls_assert(); + return GNUTLS_E_CERTIFICATE_ERROR; + } + + /* skip duplicates */ + for (j=0;j<f->size;j++) { + if (f->feature[j] == feature) { + skip = 1; + break; + } + } + + if (!skip) { + if (f->size >= sizeof(f->feature)/sizeof(f->feature[0])) { + gnutls_assert(); + return GNUTLS_E_INTERNAL_ERROR; + } + + indx = f->size; + f->feature[indx] = feature; + f->size++; + } + } + + return 0; +} + +/** + * gnutls_x509_ext_import_tlsfeatures: + * @ext: The DER-encoded extension data + * @f: The features structure + * @flags: zero or %GNUTLS_EXT_FLAG_APPEND + * + * This function will export the features in the provided DER-encoded + * TLS Features PKIX extension, to a %gnutls_x509_tlsfeatures_t type. @f + * must be initialized. + * + * When the @flags is set to %GNUTLS_EXT_FLAG_APPEND, + * then if the @features structure is empty this function will behave + * identically as if the flag was not set. Otherwise if there are elements + * in the @features structure then they will be merged with. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value. + * + * Since: 3.5.1 + **/ +int gnutls_x509_ext_import_tlsfeatures(const gnutls_datum_t * ext, + gnutls_x509_tlsfeatures_t f, + unsigned int flags) +{ + int ret; + asn1_node c2 = NULL; + + if (ext->size == 0 || ext->data == NULL) { + gnutls_assert(); + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + } + + ret = asn1_create_element(_gnutls_get_pkix(), + "PKIX1.TlsFeatures", &c2); + if (ret != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(ret); + } + + ret = _asn1_strict_der_decode(&c2, ext->data, ext->size, NULL); + if (ret != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(ret); + goto cleanup; + } + + ret = parse_tlsfeatures(c2, f, flags); + if (ret < 0) { + gnutls_assert(); + } + + cleanup: + asn1_delete_structure(&c2); + + return ret; +} + +/** + * gnutls_x509_ext_export_tlsfeatures: + * @f: The features structure + * @ext: The DER-encoded extension data; must be freed using gnutls_free(). + * + * This function will convert the provided TLS features structure structure to a + * DER-encoded TLS features PKIX extension. The output data in @ext will be allocated using + * gnutls_malloc(). + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value. + * + * Since: 3.5.1 + **/ +int gnutls_x509_ext_export_tlsfeatures(gnutls_x509_tlsfeatures_t f, + gnutls_datum_t * ext) +{ + if (f == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + asn1_node c2 = NULL; + int ret; + unsigned i; + + ret = asn1_create_element(_gnutls_get_pkix(), "PKIX1.TlsFeatures", &c2); + if (ret != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(ret); + } + + for (i = 0; i < f->size; ++i) { + + ret = asn1_write_value(c2, "", "NEW", 1); + if (ret != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(ret); + goto cleanup; + } + + ret = _gnutls_x509_write_uint32(c2, "?LAST", f->feature[i]); + if (ret != GNUTLS_E_SUCCESS) { + gnutls_assert(); + goto cleanup; + } + } + + ret = _gnutls_x509_der_encode(c2, "", ext, 0); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = 0; + + cleanup: + asn1_delete_structure(&c2); + return ret; +} + +/** + * gnutls_x509_tlsfeatures_add: + * @f: The TLS features + * @feature: The feature to add + * + * This function will append a feature to the X.509 TLS features + * extension structure. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, + * otherwise a negative error value. + * + * Since: 3.5.1 + **/ +int gnutls_x509_tlsfeatures_add(gnutls_x509_tlsfeatures_t f, unsigned int feature) +{ + if (f == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + if (feature > UINT16_MAX) + return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + + if (f->size >= sizeof(f->feature)/sizeof(f->feature[0])) + return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); + + f->feature[f->size++] = feature; + + return 0; +} + +#define SCT_V1_LOGID_SIZE 32 +struct ct_sct_st { + int version; + uint8_t logid[SCT_V1_LOGID_SIZE]; + uint64_t timestamp; + gnutls_sign_algorithm_t sigalg; + gnutls_datum_t signature; +}; + +struct gnutls_x509_ct_scts_st { + struct ct_sct_st *scts; + size_t size; +}; + +static void _gnutls_free_scts(struct gnutls_x509_ct_scts_st *scts) +{ + for (size_t i = 0; i < scts->size; i++) + _gnutls_free_datum(&scts->scts[i].signature); + gnutls_free(scts->scts); + scts->size = 0; +} + +/** + * gnutls_x509_ext_ct_scts_init: + * @scts: The SCT list + * + * This function will initialize a Certificate Transparency SCT list. + * + * Returns: %GNUTLS_E_SUCCESS (0) on success, otherwise a negative error value. + **/ +int gnutls_x509_ext_ct_scts_init(gnutls_x509_ct_scts_t * scts) +{ + *scts = gnutls_calloc(1, sizeof(struct gnutls_x509_ct_scts_st)); + if (*scts == NULL) + return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + return 0; +} + +/** + * gnutls_x509_ext_ct_scts_deinit: + * @scts: The SCT list + * + * This function will deinitialize a Certificate Transparency SCT list. + **/ +void gnutls_x509_ext_ct_scts_deinit(gnutls_x509_ct_scts_t scts) +{ + _gnutls_free_scts(scts); + gnutls_free(scts); +} + +struct sct_sign_algorithm_st { + uint8_t codepoint[2]; + gnutls_sign_algorithm_t sign_algo; +}; + +static const struct sct_sign_algorithm_st algos[] = { + { + .codepoint = { 0x01, 0x01 }, + .sign_algo = GNUTLS_SIGN_RSA_MD5 + }, + { + .codepoint = { 0x02, 0x01 }, + .sign_algo = GNUTLS_SIGN_RSA_SHA1 + }, + { + .codepoint = { 0x03, 0x01 }, + .sign_algo = GNUTLS_SIGN_RSA_SHA224 + }, + { + .codepoint = { 0x04, 0x01 }, + .sign_algo = GNUTLS_SIGN_RSA_SHA256 + }, + { + .codepoint = { 0x05, 0x01 }, + .sign_algo = GNUTLS_SIGN_RSA_SHA384 + }, + { + .codepoint = { 0x06, 0x01 }, + .sign_algo = GNUTLS_SIGN_RSA_SHA512, + }, + { + .codepoint = { 0x02, 0x02 }, + .sign_algo = GNUTLS_SIGN_DSA_SHA1 + }, + { + .codepoint = { 0x03, 0x02 }, + .sign_algo = GNUTLS_SIGN_DSA_SHA224 + }, + { + .codepoint = { 0x04, 0x02 }, + .sign_algo = GNUTLS_SIGN_DSA_SHA256 + }, + { + .codepoint = { 0x05, 0x02 }, + .sign_algo = GNUTLS_SIGN_DSA_SHA384 + }, + { + .codepoint = { 0x06, 0x02 }, + .sign_algo = GNUTLS_SIGN_DSA_SHA512, + }, + { + .codepoint = { 0x02, 0x03 }, + .sign_algo = GNUTLS_SIGN_ECDSA_SHA1 + }, + { + .codepoint = { 0x03, 0x03 }, + .sign_algo = GNUTLS_SIGN_ECDSA_SHA224 + }, + { + .codepoint = { 0x04, 0x03 }, + .sign_algo = GNUTLS_SIGN_ECDSA_SHA256 + }, + { + .codepoint = { 0x05, 0x03 }, + .sign_algo = GNUTLS_SIGN_ECDSA_SHA384 + }, + { + .codepoint = { 0x06, 0x03 }, + .sign_algo = GNUTLS_SIGN_ECDSA_SHA512, + } +}; + +static gnutls_sign_algorithm_t get_sigalg(uint8_t hash_algo, uint8_t sig_algo) +{ + const struct sct_sign_algorithm_st *algo; + size_t i, num_algos = sizeof(algos) / sizeof(algos[0]); + + if (hash_algo == 0 || sig_algo == 0) + return GNUTLS_SIGN_UNKNOWN; + + for (i = 0; i < num_algos; i++) { + algo = &algos[i]; + if (algo->codepoint[0] == hash_algo && algo->codepoint[1] == sig_algo) + break; + } + + if (i == num_algos) + return GNUTLS_SIGN_UNKNOWN; + + return algo->sign_algo; +} + +static int write_sigalg(gnutls_sign_algorithm_t sigalg, uint8_t out[]) +{ + const struct sct_sign_algorithm_st *algo; + size_t i, num_algos = sizeof(algos) / sizeof(algos[0]); + + for (i = 0; i < num_algos; i++) { + algo = &algos[i]; + if (algo->sign_algo == sigalg) + break; + } + + if (i == num_algos) + return GNUTLS_E_UNSUPPORTED_SIGNATURE_ALGORITHM; + + out[0] = algo->codepoint[0]; + out[1] = algo->codepoint[1]; + return 0; +} + +static int _gnutls_parse_ct_sct(uint8_t *ptr, uint16_t length, + struct ct_sct_st *sct) +{ + uint16_t sig_length; + uint8_t hash_algo, sig_algo; + + sct->signature.size = 0; + sct->signature.data = NULL; + + DECR_LENGTH_RET(length, 1, GNUTLS_E_PREMATURE_TERMINATION); + sct->version = (int) *ptr; + ptr++; + + /* LogID + * In version 1, it has a fixed length of 32 bytes. + */ + DECR_LENGTH_RET(length, SCT_V1_LOGID_SIZE, GNUTLS_E_PREMATURE_TERMINATION); + memcpy(sct->logid, ptr, SCT_V1_LOGID_SIZE); + ptr += SCT_V1_LOGID_SIZE; + + /* Timestamp */ + DECR_LENGTH_RET(length, sizeof(uint64_t), GNUTLS_E_PREMATURE_TERMINATION); + sct->timestamp = (uint64_t) _gnutls_read_uint64(ptr); + ptr += sizeof(uint64_t); + + /* + * There are no extensions defined in SCT v1. + * Check that there are actually no extensions - the following two bytes should be zero. + */ + DECR_LENGTH_RET(length, 2, GNUTLS_E_PREMATURE_TERMINATION); + if (*ptr != 0 || *(ptr+1) != 0) + return gnutls_assert_val(GNUTLS_E_UNEXPECTED_EXTENSIONS_LENGTH); + ptr += 2; + + /* + * Hash and signature algorithms, modeled after + * SignatureAndHashAlgorithm structure, as defined in + * RFC 5246, section 7.4.1.4.1. + * We take both values separately (hash and signature), + * and return them as a gnutls_sign_algorithm_t enum value. + */ + DECR_LENGTH_RET(length, 2, GNUTLS_E_PREMATURE_TERMINATION); + hash_algo = *ptr++; + sig_algo = *ptr++; + + sct->sigalg = get_sigalg(hash_algo, sig_algo); + if (sct->sigalg == GNUTLS_SIGN_UNKNOWN) + return gnutls_assert_val(GNUTLS_E_UNSUPPORTED_SIGNATURE_ALGORITHM); + + /* Signature, length and content */ + DECR_LENGTH_RET(length, sizeof(uint16_t), GNUTLS_E_PREMATURE_TERMINATION); + sig_length = _gnutls_read_uint16(ptr); + ptr += sizeof(uint16_t); + if (sig_length == 0) + return gnutls_assert_val(GNUTLS_E_PREMATURE_TERMINATION); + + /* Remaining length should be sig_length at this point. + * If not, that means there is more data than what the length field said it was, + * and hence we must treat this as an error. */ + if (length != sig_length) + return gnutls_assert_val(GNUTLS_E_ASN1_DER_OVERFLOW); + + if (_gnutls_set_datum(&sct->signature, ptr, sig_length) < 0) + return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + + return 0; +} + +static int _gnutls_ct_sct_add(struct ct_sct_st *sct, + struct ct_sct_st **scts, size_t *size) +{ + struct ct_sct_st *new_scts; + + new_scts = _gnutls_reallocarray(*scts, *size + 1, sizeof(struct ct_sct_st)); + if (new_scts == NULL) + return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + + memcpy(&new_scts[*size], sct, sizeof(struct ct_sct_st)); + (*size)++; + *scts = new_scts; + + return 0; +} + +static int _gnutls_export_ct_v1_sct(gnutls_buffer_st *buf, + const struct ct_sct_st *sct) +{ + int ret; + uint8_t tstamp_out[8], sigalg[2]; + /* There are no extensions defined for v1 */ + const uint8_t extensions[2] = { 0x00, 0x00 }; + size_t length_offset; + + /* Length field; filled later */ + length_offset = buf->length; + if ((ret = _gnutls_buffer_append_prefix(buf, 16, 0)) < 0) + return gnutls_assert_val(ret); + + /* Version */ + if ((ret = _gnutls_buffer_append_data(buf, + &sct->version, sizeof(uint8_t))) < 0) + return gnutls_assert_val(ret); + + /* Log ID - has a fixed 32-byte size in version 1 */ + if ((ret = _gnutls_buffer_append_data(buf, + sct->logid, SCT_V1_LOGID_SIZE)) < 0) + return gnutls_assert_val(ret); + + /* Timestamp */ + _gnutls_write_uint64(sct->timestamp, tstamp_out); + if ((ret = _gnutls_buffer_append_data(buf, + tstamp_out, sizeof(tstamp_out))) < 0) + return gnutls_assert_val(ret); + + /* Extensions */ + if ((ret = _gnutls_buffer_append_data(buf, + extensions, sizeof(extensions))) < 0) + return gnutls_assert_val(ret); + + /* Hash and signature algorithms */ + if ((ret = write_sigalg(sct->sigalg, sigalg)) < 0) + return gnutls_assert_val(ret); + + if ((ret = _gnutls_buffer_append_data(buf, + sigalg, sizeof(sigalg))) < 0) + return gnutls_assert_val(ret); + + /* Signature */ + if ((ret = _gnutls_buffer_append_data_prefix(buf, 16, + sct->signature.data, sct->signature.size)) < 0) + return gnutls_assert_val(ret); + + /* Fill the length */ + _gnutls_write_uint16(buf->length - length_offset - 2, + buf->data + length_offset); + + return 0; +} + +/** + * gnutls_x509_ext_ct_import_scts: + * @ext: a DER-encoded extension + * @scts: The SCT list + * @flags: should be zero + * + * This function will read a SignedCertificateTimestampList structure + * from the DER data of the X.509 Certificate Transparency SCT extension + * (OID 1.3.6.1.4.1.11129.2.4.2). + * + * The list of SCTs (Signed Certificate Timestamps) is placed on @scts, + * which must be previously initialized with gnutls_x509_ext_ct_scts_init(). + * + * Returns: %GNUTLS_E_SUCCESS (0) on success or a negative error value. + **/ +int gnutls_x509_ext_ct_import_scts(const gnutls_datum_t *ext, gnutls_x509_ct_scts_t scts, + unsigned int flags) +{ + int retval; + uint8_t *ptr; + uint16_t length, sct_length; + struct ct_sct_st sct; + gnutls_datum_t scts_content; + + if (flags != 0) + return gnutls_assert_val(GNUTLS_E_UNIMPLEMENTED_FEATURE); + + retval = + _gnutls_x509_decode_string(ASN1_ETYPE_OCTET_STRING, + ext->data, ext->size, &scts_content, + 0); + if (retval < 0) + return gnutls_assert_val(retval); + + if (scts_content.size < 2) { + gnutls_free(scts_content.data); + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + } + + length = _gnutls_read_uint16(scts_content.data); + if (length < 4) { + gnutls_free(scts_content.data); + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + } + + ptr = &scts_content.data[2]; + while (length > 0) { + if (length < 2) + break; + + sct_length = _gnutls_read_uint16(ptr); + if (sct_length == 0 || sct_length > length) + break; + + ptr += sizeof(uint16_t); + length -= sizeof(uint16_t); + + /* + * _gnutls_parse_ct_sct() will try to read exactly sct_length bytes, + * returning an error if it can't + */ + if (_gnutls_parse_ct_sct(ptr, sct_length, &sct) < 0) + break; + if (_gnutls_ct_sct_add(&sct, &scts->scts, &scts->size) < 0) + break; + + ptr += sct_length; + length -= sct_length; + } + + _gnutls_free_datum(&scts_content); + + if (length > 0) { + gnutls_assert(); + _gnutls_free_scts(scts); + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + } + + return GNUTLS_E_SUCCESS; +} + +/** + * gnutls_x509_ext_ct_export_scts: + * @scts: An initialized SCT list + * @ext: The DER-encoded extension data; must be freed with gnutls_free() + * + * This function will convert the provided list of SCTs to a DER-encoded + * SignedCertificateTimestampList extension (1.3.6.1.4.1.11129.2.4.2). + * The output data in @ext will be allocated using gnutls_malloc(). + * + * Returns: %GNUTLS_E_SUCCESS (0) on success or a negative error value. + **/ +int gnutls_x509_ext_ct_export_scts(const gnutls_x509_ct_scts_t scts, gnutls_datum_t *ext) +{ + int ret; + gnutls_buffer_st buf; + + _gnutls_buffer_init(&buf); + + /* Start with the length of the whole string; the actual + * length is filled later */ + _gnutls_buffer_append_prefix(&buf, 16, 0); + + for (size_t i = 0; i < scts->size; i++) { + if ((ret = _gnutls_export_ct_v1_sct(&buf, + &scts->scts[i])) < 0) { + gnutls_assert(); + goto cleanup; + } + } + + /* Fill the length */ + _gnutls_write_uint16(buf.length - 2, buf.data); + + /* DER-encode the whole thing as an opaque OCTET STRING, as the spec mandates */ + ret = _gnutls_x509_encode_string( + ASN1_ETYPE_OCTET_STRING, + buf.data, buf.length, + ext); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = GNUTLS_E_SUCCESS; + +cleanup: + _gnutls_buffer_clear(&buf); + return ret; +} + +/** + * gnutls_x509_ct_sct_get_version: + * @scts: A list of SCTs + * @idx: The index of the target SCT in the list + * @version_out: The version of the target SCT. + * + * This function obtains the version of the SCT at the given position + * in the SCT list. + * + * The version of that SCT will be placed on @version_out. + * + * Return : %GNUTLS_E_SUCCESS (0) is returned on success, + * %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE if @idx exceeds the number of SCTs in the list + * and %GNUTLS_E_INVALID_REQUEST if the SCT's version is different than 1, as that's currently + * the only defined version. + **/ +int gnutls_x509_ct_sct_get_version(gnutls_x509_ct_scts_t scts, unsigned idx, + unsigned int *version_out) +{ + int version; + + if (idx >= scts->size) + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + + /* + * Currently, only version 1 SCTs are defined (RFC 6962). + * A version 1 SCT has actually the value 0 in the 'version' field. + */ + version = scts->scts[idx].version; + if (version != 0 || version_out == NULL) + return GNUTLS_E_INVALID_REQUEST; + + *version_out = 1; + return GNUTLS_E_SUCCESS; +} + +/** + * gnutls_x509_ct_sct_get: + * @scts: A list of SCTs + * @idx: The index of the target SCT in the list + * @timestamp: The timestamp of the SCT + * @logid: The LogID field of the SCT; must be freed with gnutls_free() + * @sigalg: The signature algorithm + * @signature: The signature of the SCT; must be freed with gnutls_free() + * + * This function will return a specific SCT (Signed Certificate Timestamp) + * stored in the SCT list @scts. + * + * The datums holding the SCT's LogId and signature will be allocated + * using gnutls_malloc(). + * + * Returns: %GNUTLS_E_SUCCESS (0) will be returned on success, + * %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE if @idx exceeds the number of SCTs in the list + * or a negative error value. + **/ +int gnutls_x509_ct_sct_get(const gnutls_x509_ct_scts_t scts, unsigned idx, + time_t *timestamp, + gnutls_datum_t *logid, + gnutls_sign_algorithm_t *sigalg, + gnutls_datum_t *signature) +{ + int retval = 0; + struct ct_sct_st *sct; + + if (idx >= scts->size) + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + + sct = &scts->scts[idx]; + if (sct->version != 0) + return GNUTLS_E_INVALID_REQUEST; + + if (signature) { + retval = _gnutls_set_datum(signature, + sct->signature.data, + sct->signature.size); + if (retval < 0) + return retval; + } + + if (logid) { + retval = _gnutls_set_datum(logid, + sct->logid, + SCT_V1_LOGID_SIZE); + if (retval < 0) { + _gnutls_free_datum(signature); + return retval; + } + } + + if (timestamp) + *timestamp = sct->timestamp / 1000; + + if (sigalg) + *sigalg = sct->sigalg; + + return GNUTLS_E_SUCCESS; +} diff --git a/lib/x509/x509_ext_int.h b/lib/x509/x509_ext_int.h new file mode 100644 index 0000000..2e3f162 --- /dev/null +++ b/lib/x509/x509_ext_int.h @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2014-2016 Free Software Foundation + * Copyright (C) 2014-2016 Red Hat, Inc. + * + * This file is part of GnuTLS. + * + * The GnuTLS is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/> + * + */ + +#ifndef GNUTLS_LIB_X509_X509_EXT_INT_H +#define GNUTLS_LIB_X509_X509_EXT_INT_H + +#include "gnutls_int.h" +struct name_st { + unsigned int type; + gnutls_datum_t san; + gnutls_datum_t othername_oid; +}; + +int _gnutls_alt_name_process(gnutls_datum_t *out, unsigned type, const gnutls_datum_t *san, unsigned raw); + +#endif /* GNUTLS_LIB_X509_X509_EXT_INT_H */ diff --git a/lib/x509/x509_int.h b/lib/x509/x509_int.h new file mode 100644 index 0000000..acbc185 --- /dev/null +++ b/lib/x509/x509_int.h @@ -0,0 +1,558 @@ +/* + * Copyright (C) 2003-2012 Free Software Foundation, Inc. + * Copyright (C) 2017 Red Hat, Inc. + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GnuTLS. + * + * The GnuTLS is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/> + * + */ + +#ifndef GNUTLS_LIB_X509_X509_INT_H +#define GNUTLS_LIB_X509_X509_INT_H + +#include <gnutls/x509.h> +#include <gnutls/x509-ext.h> +#include <gnutls/abstract.h> + +#include <libtasn1.h> + +#define MAX_CRQ_EXTENSIONS_SIZE 8*1024 +#define MAX_OID_SIZE 128 +#define MAX_KEY_ID_SIZE 128 +#define MAX_SALT_SIZE 256 +#define MAX_NAME_SIZE (3*ASN1_MAX_NAME_SIZE) + +#define HASH_OID_SHA1 "1.3.14.3.2.26" +#define HASH_OID_MD5 "1.2.840.113549.2.5" +#define HASH_OID_MD2 "1.2.840.113549.2.2" +#define HASH_OID_RMD160 "1.3.36.3.2.1" +#define HASH_OID_SHA224 "2.16.840.1.101.3.4.2.4" +#define HASH_OID_SHA256 "2.16.840.1.101.3.4.2.1" +#define HASH_OID_SHA384 "2.16.840.1.101.3.4.2.2" +#define HASH_OID_SHA512 "2.16.840.1.101.3.4.2.3" +#define HASH_OID_SHA3_224 "2.16.840.1.101.3.4.2.7" +#define HASH_OID_SHA3_256 "2.16.840.1.101.3.4.2.8" +#define HASH_OID_SHA3_384 "2.16.840.1.101.3.4.2.9" +#define HASH_OID_SHA3_512 "2.16.840.1.101.3.4.2.10" +#define HASH_OID_SHAKE_128 "2.16.840.1.101.3.4.2.11" +#define HASH_OID_SHAKE_256 "2.16.840.1.101.3.4.2.12" +#define HASH_OID_GOST_R_3411_94 "1.2.643.2.2.9" +#define HASH_OID_STREEBOG_256 "1.2.643.7.1.1.2.2" +#define HASH_OID_STREEBOG_512 "1.2.643.7.1.1.2.3" + +#define HASH_OID_GOST_R_3411_94_CRYPTOPRO_PARAMS "1.2.643.2.2.30.1" + +/* from rfc8479 */ +#define OID_ATTR_PROV_SEED "1.3.6.1.4.1.2312.18.8.1" + +struct gnutls_x509_crl_iter { + /* This is used to optimize reads by gnutls_x509_crl_iter_crt_serial() */ + asn1_node rcache; + unsigned rcache_idx; +}; + +typedef struct gnutls_x509_crl_int { + asn1_node crl; + + unsigned expanded; + /* This is used to optimize reads by gnutls_x509_crl_get_crt_serial2() */ + asn1_node rcache; + unsigned rcache_idx; + int use_extensions; + + gnutls_datum_t der; + gnutls_datum_t raw_issuer_dn; +} gnutls_x509_crl_int; + +typedef struct gnutls_x509_dn_st { + asn1_node asn; +} gnutls_x509_dn_st; + +typedef struct gnutls_x509_crt_int { + asn1_node cert; + int use_extensions; + unsigned expanded; /* a certificate has been expanded */ + unsigned modified; /* the cached values below may no longer be valid */ + unsigned flags; + + struct pin_info_st pin; + + /* These two cached values allow fast calls to + * get_raw_*_dn(). */ + gnutls_datum_t raw_dn; + gnutls_datum_t raw_issuer_dn; + gnutls_datum_t raw_spki; + + gnutls_datum_t der; + + /* this cached value allows fast access to alt names */ + gnutls_subject_alt_names_t san; + gnutls_subject_alt_names_t ian; + + /* backwards compatibility for gnutls_x509_crt_get_subject() + * and gnutls_x509_crt_get_issuer() */ + gnutls_x509_dn_st dn; + gnutls_x509_dn_st idn; +} gnutls_x509_crt_int; + +#define MODIFIED(crt) crt->modified=1 + +typedef struct gnutls_x509_crq_int { + asn1_node crq; +} gnutls_x509_crq_int; + +typedef struct gnutls_pkcs7_attrs_st { + char *oid; + gnutls_datum_t data; + struct gnutls_pkcs7_attrs_st *next; +} gnutls_pkcs7_attrs_st; + +typedef struct gnutls_pkcs7_int { + asn1_node pkcs7; + + char encap_data_oid[MAX_OID_SIZE]; + + gnutls_datum_t der_signed_data; + asn1_node signed_data; + unsigned expanded; +} gnutls_pkcs7_int; + +struct pbkdf2_params { + uint8_t salt[MAX_SALT_SIZE]; + int salt_size; + unsigned iter_count; + unsigned key_size; + gnutls_mac_algorithm_t mac; +}; + +typedef struct gnutls_x509_privkey_int { + /* the size of params depends on the public + * key algorithm + */ + gnutls_pk_params_st params; + + unsigned expanded; + unsigned flags; + + asn1_node key; + struct pin_info_st pin; +} gnutls_x509_privkey_int; + +int _gnutls_x509_crt_cpy(gnutls_x509_crt_t dest, gnutls_x509_crt_t src); + +int _gnutls_x509_compare_raw_dn(const gnutls_datum_t * dn1, + const gnutls_datum_t * dn2); + +int _gnutls_x509_crl_cpy(gnutls_x509_crl_t dest, gnutls_x509_crl_t src); +int _gnutls_x509_crl_get_raw_issuer_dn(gnutls_x509_crl_t crl, + gnutls_datum_t * dn); + +/* sign.c */ +int _gnutls_x509_get_tbs(asn1_node cert, const char *tbs_name, + gnutls_datum_t * tbs); +int _gnutls_x509_pkix_sign(asn1_node src, const char *src_name, + gnutls_digest_algorithm_t, + unsigned int flags, + gnutls_x509_crt_t issuer, + gnutls_privkey_t issuer_key); +int _gnutls_x509_crt_get_spki_params(gnutls_x509_crt_t issuer, + const gnutls_x509_spki_st *key_params, + gnutls_x509_spki_st *params); + +#define map_errs_to_zero(x) ((x)<0?0:(x)) + +/* dn.c */ +#define OID_X520_COUNTRY_NAME "2.5.4.6" +#define OID_X520_ORGANIZATION_NAME "2.5.4.10" +#define OID_X520_ORGANIZATIONAL_UNIT_NAME "2.5.4.11" +#define OID_X520_COMMON_NAME "2.5.4.3" +#define OID_X520_LOCALITY_NAME "2.5.4.7" +#define OID_X520_STATE_OR_PROVINCE_NAME "2.5.4.8" +#define OID_LDAP_DC "0.9.2342.19200300.100.1.25" +#define OID_LDAP_UID "0.9.2342.19200300.100.1.1" +#define OID_PKCS9_EMAIL "1.2.840.113549.1.9.1" + +int _gnutls_x509_parse_dn(asn1_node asn1_struct, + const char *asn1_rdn_name, char *buf, + size_t * sizeof_buf, + unsigned flags); + +int +_gnutls_x509_get_dn(asn1_node asn1_struct, + const char *asn1_rdn_name, gnutls_datum_t * dn, + unsigned flags); + +int +_gnutls_x509_parse_dn_oid(asn1_node asn1_struct, + const char *asn1_rdn_name, + const char *given_oid, int indx, + unsigned int raw_flag, gnutls_datum_t * out); + +int _gnutls_x509_set_dn_oid(asn1_node asn1_struct, + const char *asn1_rdn_name, const char *oid, + int raw_flag, const char *name, + int sizeof_name); + +int _gnutls_x509_get_dn_oid(asn1_node asn1_struct, + const char *asn1_rdn_name, + int indx, void *_oid, size_t * sizeof_oid); + +int _gnutls_encode_othername_data(unsigned flags, const void *data, unsigned data_size, gnutls_datum_t *output); + +int _gnutls_parse_general_name(asn1_node src, const char *src_name, + int seq, void *name, size_t * name_size, + unsigned int *ret_type, int othername_oid); + +int +_gnutls_parse_general_name2(asn1_node src, const char *src_name, + int seq, gnutls_datum_t *dname, + unsigned int *ret_type, int othername_oid); + +int +_gnutls_write_new_general_name(asn1_node ext, const char *ext_name, + gnutls_x509_subject_alt_name_t type, + const void *data, unsigned int data_size); + +int +_gnutls_write_new_othername(asn1_node ext, const char *ext_name, + const char *oid, + const void *data, unsigned int data_size); + +/* dsa.c */ + + +/* verify.c */ +int gnutls_x509_crt_is_issuer(gnutls_x509_crt_t cert, + gnutls_x509_crt_t issuer); + +int +_gnutls_x509_verify_algorithm(gnutls_digest_algorithm_t * hash, + const gnutls_datum_t * signature, + gnutls_pk_algorithm_t pk, + gnutls_pk_params_st * issuer_params); + +/* privkey.h */ +void _gnutls_x509_privkey_reinit(gnutls_x509_privkey_t key); + +asn1_node _gnutls_privkey_decode_pkcs1_rsa_key(const gnutls_datum_t * + raw_key, + gnutls_x509_privkey_t pkey); +int _gnutls_privkey_decode_ecc_key(asn1_node* pkey_asn, const gnutls_datum_t * + raw_key, + gnutls_x509_privkey_t pkey, + gnutls_ecc_curve_t curve); + +int _gnutls_privkey_decode_eddsa_key(asn1_node* pkey_asn, + const gnutls_datum_t *raw_key, + gnutls_x509_privkey_t pkey, + gnutls_ecc_curve_t curve); + +int +_gnutls_x509_read_ecc_params(uint8_t * der, int dersize, + unsigned int *curve); + +int +_gnutls_x509_read_gost_params(uint8_t * der, int dersize, + gnutls_pk_params_st * params, + gnutls_pk_algorithm_t algo); + +int _gnutls_asn1_encode_privkey(asn1_node * c2, + gnutls_pk_params_st * params); + +void _gnutls_x509_privkey_get_spki_params(gnutls_x509_privkey_t key, + gnutls_x509_spki_st * params); + +int _gnutls_x509_read_rsa_pss_params(uint8_t * der, int dersize, + gnutls_x509_spki_st * params); +int _gnutls_x509_write_rsa_pss_params(const gnutls_x509_spki_st * params, + gnutls_datum_t * der); + +/* extensions.c */ +int _gnutls_x509_crl_get_extension_oid(gnutls_x509_crl_t crl, + int indx, void *oid, + size_t * sizeof_oid); + +int _gnutls_x509_crl_set_extension(gnutls_x509_crl_t crl, + const char *ext_id, + const gnutls_datum_t * ext_data, + unsigned int critical); + +int +_gnutls_x509_crl_get_extension(gnutls_x509_crl_t crl, + const char *extension_id, int indx, + gnutls_datum_t * data, + unsigned int *critical); + +int +_gnutls_x509_crt_get_extension(gnutls_x509_crt_t cert, + const char *extension_id, int indx, + gnutls_datum_t * data, unsigned int *critical); + +int _gnutls_x509_crt_get_extension_oid(gnutls_x509_crt_t cert, + int indx, void *ret, + size_t * ret_size); +int _gnutls_x509_crt_set_extension(gnutls_x509_crt_t cert, + const char *extension_id, + const gnutls_datum_t * ext_data, + unsigned int critical); + +int +_gnutls_x509_ext_extract_number(uint8_t * number, + size_t * nr_size, + uint8_t * extnValue, int extnValueLen); +int +_gnutls_x509_ext_gen_number(const uint8_t * number, size_t nr_size, + gnutls_datum_t * der_ext); + + +int +_gnutls_write_general_name(asn1_node ext, const char *ext_name, + gnutls_x509_subject_alt_name_t type, + const void *data, unsigned int data_size); + +int _gnutls_x509_ext_gen_subject_alt_name(gnutls_x509_subject_alt_name_t + type, + const char *othername_oid, + const void *data, + unsigned int data_size, + const gnutls_datum_t * prev_der_ext, + gnutls_datum_t * der_ext); +int _gnutls_x509_ext_gen_auth_key_id(const void *id, size_t id_size, + gnutls_datum_t * der_data); + +/* mpi.c */ +int _gnutls_x509_crq_get_mpis(gnutls_x509_crq_t cert, + gnutls_pk_params_st *); + +int _gnutls_x509_crt_get_mpis(gnutls_x509_crt_t cert, + gnutls_pk_params_st * params); + +int _gnutls_x509_read_pubkey_params(gnutls_pk_algorithm_t, uint8_t * der, + int dersize, + gnutls_pk_params_st * params); +int _gnutls_x509_check_pubkey_params(gnutls_pk_params_st * params); + +int _gnutls_x509_read_pubkey(gnutls_pk_algorithm_t, uint8_t * der, + int dersize, gnutls_pk_params_st * params); + +int _gnutls_x509_read_pubkey_signature_params(gnutls_pk_algorithm_t algo, + uint8_t * der, int dersize, + gnutls_pk_params_st * params); + +int _gnutls_x509_write_ecc_params(const gnutls_ecc_curve_t curve, + gnutls_datum_t * der); +int _gnutls_x509_write_ecc_pubkey(const gnutls_pk_params_st * params, + gnutls_datum_t * der); + +int _gnutls_x509_write_eddsa_pubkey(const gnutls_pk_params_st * params, + gnutls_datum_t * der); + +int +_gnutls_x509_write_pubkey_params(const gnutls_pk_params_st * params, + gnutls_datum_t * der); +int _gnutls_x509_write_pubkey(const gnutls_pk_params_st * params, + gnutls_datum_t * der); + +int _gnutls_x509_read_uint(asn1_node node, const char *value, + unsigned int *ret); + +int _gnutls_x509_read_der_int(uint8_t * der, int dersize, bigint_t * out); +int _gnutls_x509_read_der_uint(uint8_t * der, int dersize, unsigned int *out); + +int _gnutls_x509_read_int(asn1_node node, const char *value, + bigint_t * ret_mpi); +int _gnutls_x509_write_int(asn1_node node, const char *value, bigint_t mpi, + int lz); + +int _gnutls_x509_write_uint32(asn1_node node, const char *value, + uint32_t num); + + +int _gnutls_x509_read_key_int(asn1_node node, const char *value, + bigint_t * ret_mpi); +int _gnutls_x509_write_key_int(asn1_node node, const char *value, bigint_t mpi, + int lz); + +int _gnutls_x509_read_key_int_le(asn1_node node, const char *value, + bigint_t * ret_mpi); +int _gnutls_x509_write_key_int_le(asn1_node node, const char *value, + bigint_t mpi); + +int _gnutls_x509_read_pkalgo_params(asn1_node src, const char *src_name, + gnutls_x509_spki_st *params, unsigned is_sig); +int _gnutls_x509_write_sign_params(asn1_node dst, const char *dst_name, + const gnutls_sign_entry_st *se, gnutls_x509_spki_st *params); + +#define _gnutls_x509_read_sign_params(src,name,params) _gnutls_x509_read_pkalgo_params(src,name,params,1) +#define _gnutls_x509_read_spki_params(src,name,params) _gnutls_x509_read_pkalgo_params(src,name,params,0) +int _gnutls_x509_write_spki_params(asn1_node dst, const char *dst_name, + gnutls_x509_spki_st *params); + +inline static int +_gnutls_x509_crt_read_spki_params(gnutls_x509_crt_t crt, + gnutls_x509_spki_st *params) +{ + return _gnutls_x509_read_spki_params(crt->cert, + "tbsCertificate." + "subjectPublicKeyInfo." + "algorithm", + params); +} + +inline static int +_gnutls_x509_crq_read_spki_params(gnutls_x509_crq_t crt, + gnutls_x509_spki_st *params) +{ + return _gnutls_x509_read_spki_params(crt->crq, + "certificationRequestInfo." + "subjectPKInfo." + "algorithm", + params); +} + + +/* pkcs12.h */ +#include <gnutls/pkcs12.h> + +typedef struct gnutls_pkcs12_int { + asn1_node pkcs12; + unsigned expanded; +} gnutls_pkcs12_int; + +#define MAX_BAG_ELEMENTS 32 + +struct bag_element { + gnutls_datum_t data; + gnutls_pkcs12_bag_type_t type; + gnutls_datum_t local_key_id; + char *friendly_name; +}; + +typedef struct gnutls_pkcs12_bag_int { + struct bag_element element[MAX_BAG_ELEMENTS]; + unsigned bag_elements; +} gnutls_pkcs12_bag_int; + +#define BAG_PKCS8_KEY "1.2.840.113549.1.12.10.1.1" +#define BAG_PKCS8_ENCRYPTED_KEY "1.2.840.113549.1.12.10.1.2" +#define BAG_CERTIFICATE "1.2.840.113549.1.12.10.1.3" +#define BAG_CRL "1.2.840.113549.1.12.10.1.4" +#define BAG_SECRET "1.2.840.113549.1.12.10.1.5" + +/* Bag attributes + */ +#define FRIENDLY_NAME_OID "1.2.840.113549.1.9.20" +#define KEY_ID_OID "1.2.840.113549.1.9.21" + +int +_gnutls_pkcs12_string_to_key(const mac_entry_st * me, + unsigned int id, const uint8_t * salt, + unsigned int salt_size, unsigned int iter, + const char *pw, unsigned int req_keylen, + uint8_t * keybuf); + + +int _pkcs12_decode_safe_contents(const gnutls_datum_t * content, + gnutls_pkcs12_bag_t bag); + +int +_pkcs12_encode_safe_contents(gnutls_pkcs12_bag_t bag, asn1_node * content, + int *enc); + +int _pkcs12_decode_crt_bag(gnutls_pkcs12_bag_type_t type, + const gnutls_datum_t * in, + gnutls_datum_t * out); +int _pkcs12_encode_crt_bag(gnutls_pkcs12_bag_type_t type, + const gnutls_datum_t * raw, + gnutls_datum_t * out); + +/* crq */ +int _gnutls_x509_crq_set_extension(gnutls_x509_crq_t crq, + const char *ext_id, + const gnutls_datum_t * ext_data, + unsigned int critical); + +int +gnutls_x509_crt_verify_data3(gnutls_x509_crt_t crt, + gnutls_sign_algorithm_t algo, + gnutls_typed_vdata_st *vdata, + unsigned int vdata_size, + const gnutls_datum_t *data, + const gnutls_datum_t *signature, + unsigned int flags); + +int _gnutls_trust_list_get_issuer(gnutls_x509_trust_list_t list, + gnutls_x509_crt_t cert, + gnutls_x509_crt_t * issuer, + unsigned int flags); + +unsigned int +_gnutls_verify_crt_status(gnutls_x509_trust_list_t tlist, + const gnutls_x509_crt_t * certificate_list, + int clist_size, + const gnutls_x509_crt_t * trusted_cas, + int tcas_size, + unsigned int flags, + const char *purpose, + gnutls_verify_output_function func); + +#ifdef ENABLE_PKCS11 +unsigned int +_gnutls_pkcs11_verify_crt_status(gnutls_x509_trust_list_t tlist, + const char* url, + const gnutls_x509_crt_t * certificate_list, + unsigned clist_size, + const char *purpose, + unsigned int flags, + gnutls_verify_output_function func); +#endif + +int _gnutls_check_cert_sanity(gnutls_x509_crt_t cert); + +int +_gnutls_x509_crt_check_revocation(gnutls_x509_crt_t cert, + const gnutls_x509_crl_t * crl_list, + int crl_list_length, + gnutls_verify_output_function func); + +typedef struct gnutls_name_constraints_st { + struct name_constraints_node_st * permitted; + struct name_constraints_node_st * excluded; +} gnutls_name_constraints_st; + +typedef struct name_constraints_node_st { + unsigned type; + gnutls_datum_t name; + struct name_constraints_node_st *next; +} name_constraints_node_st; + +int _gnutls_extract_name_constraints(asn1_node c2, const char *vstr, + name_constraints_node_st ** _nc); +void _gnutls_name_constraints_node_free (name_constraints_node_st *node); +int _gnutls_x509_name_constraints_merge(gnutls_x509_name_constraints_t nc, + gnutls_x509_name_constraints_t nc2); + +void _gnutls_x509_policies_erase(gnutls_x509_policies_t policies, unsigned int seq); + +struct gnutls_x509_tlsfeatures_st { + uint16_t feature[MAX_EXT_TYPES]; + unsigned int size; +}; + +unsigned _gnutls_is_broken_sig_allowed(const gnutls_sign_entry_st *se, unsigned int flags); + +#endif /* GNUTLS_LIB_X509_X509_INT_H */ diff --git a/lib/x509/x509_write.c b/lib/x509/x509_write.c new file mode 100644 index 0000000..e9a7087 --- /dev/null +++ b/lib/x509/x509_write.c @@ -0,0 +1,2125 @@ +/* + * Copyright (C) 2003-2016 Free Software Foundation, Inc. + * Copyright (C) 2016-2017 Red Hat, Inc. + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GnuTLS. + * + * The GnuTLS is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/> + * + */ + +/* This file contains functions to handle X.509 certificate generation. + */ + +#include "gnutls_int.h" + +#include <datum.h> +#include <global.h> +#include "errors.h" +#include <common.h> +#include <x509.h> +#include <gnutls/x509-ext.h> +#include <x509_b64.h> +#include "x509_int.h" +#include <libtasn1.h> +#include <pk.h> + +static void disable_optional_stuff(gnutls_x509_crt_t cert); + +/** + * gnutls_x509_crt_set_dn_by_oid: + * @crt: a certificate of type #gnutls_x509_crt_t + * @oid: holds an Object Identifier in a null terminated string + * @raw_flag: must be 0, or 1 if the data are DER encoded + * @name: a pointer to the name + * @sizeof_name: holds the size of @name + * + * This function will set the part of the name of the Certificate + * subject, specified by the given OID. The input string should be + * ASCII or UTF-8 encoded. + * + * Some helper macros with popular OIDs can be found in gnutls/x509.h + * With this function you can only set the known OIDs. You can test + * for known OIDs using gnutls_x509_dn_oid_known(). For OIDs that are + * not known (by gnutls) you should properly DER encode your data, + * and call this function with @raw_flag set. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int +gnutls_x509_crt_set_dn_by_oid(gnutls_x509_crt_t crt, const char *oid, + unsigned int raw_flag, const void *name, + unsigned int sizeof_name) +{ + if (sizeof_name == 0 || name == NULL || crt == NULL) { + return GNUTLS_E_INVALID_REQUEST; + } + + MODIFIED(crt); + + return _gnutls_x509_set_dn_oid(crt->cert, "tbsCertificate.subject", + oid, raw_flag, name, sizeof_name); +} + +/** + * gnutls_x509_crt_set_issuer_dn_by_oid: + * @crt: a certificate of type #gnutls_x509_crt_t + * @oid: holds an Object Identifier in a null terminated string + * @raw_flag: must be 0, or 1 if the data are DER encoded + * @name: a pointer to the name + * @sizeof_name: holds the size of @name + * + * This function will set the part of the name of the Certificate + * issuer, specified by the given OID. The input string should be + * ASCII or UTF-8 encoded. + * + * Some helper macros with popular OIDs can be found in gnutls/x509.h + * With this function you can only set the known OIDs. You can test + * for known OIDs using gnutls_x509_dn_oid_known(). For OIDs that are + * not known (by gnutls) you should properly DER encode your data, + * and call this function with @raw_flag set. + * + * Normally you do not need to call this function, since the signing + * operation will copy the signer's name as the issuer of the + * certificate. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int +gnutls_x509_crt_set_issuer_dn_by_oid(gnutls_x509_crt_t crt, + const char *oid, + unsigned int raw_flag, + const void *name, + unsigned int sizeof_name) +{ + if (sizeof_name == 0 || name == NULL || crt == NULL) { + return GNUTLS_E_INVALID_REQUEST; + } + + MODIFIED(crt); + + return _gnutls_x509_set_dn_oid(crt->cert, "tbsCertificate.issuer", + oid, raw_flag, name, sizeof_name); +} + +/** + * gnutls_x509_crt_set_proxy_dn: + * @crt: a gnutls_x509_crt_t type with the new proxy cert + * @eecrt: the end entity certificate that will be issuing the proxy + * @raw_flag: must be 0, or 1 if the CN is DER encoded + * @name: a pointer to the CN name, may be NULL (but MUST then be added later) + * @sizeof_name: holds the size of @name + * + * This function will set the subject in @crt to the end entity's + * @eecrt subject name, and add a single Common Name component @name + * of size @sizeof_name. This corresponds to the required proxy + * certificate naming style. Note that if @name is %NULL, you MUST + * set it later by using gnutls_x509_crt_set_dn_by_oid() or similar. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int +gnutls_x509_crt_set_proxy_dn(gnutls_x509_crt_t crt, + gnutls_x509_crt_t eecrt, + unsigned int raw_flag, const void *name, + unsigned int sizeof_name) +{ + int result; + + if (crt == NULL || eecrt == NULL) { + return GNUTLS_E_INVALID_REQUEST; + } + + MODIFIED(crt); + + result = asn1_copy_node(crt->cert, "tbsCertificate.subject", + eecrt->cert, "tbsCertificate.subject"); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + if (name && sizeof_name) { + return _gnutls_x509_set_dn_oid(crt->cert, + "tbsCertificate.subject", + GNUTLS_OID_X520_COMMON_NAME, + raw_flag, name, + sizeof_name); + } + + return 0; +} + +/** + * gnutls_x509_crt_set_version: + * @crt: a certificate of type #gnutls_x509_crt_t + * @version: holds the version number. For X.509v1 certificates must be 1. + * + * This function will set the version of the certificate. This must + * be one for X.509 version 1, and so on. Plain certificates without + * extensions must have version set to one. + * + * To create well-formed certificates, you must specify version 3 if + * you use any certificate extensions. Extensions are created by + * functions such as gnutls_x509_crt_set_subject_alt_name() + * or gnutls_x509_crt_set_key_usage(). + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int +gnutls_x509_crt_set_version(gnutls_x509_crt_t crt, unsigned int version) +{ + int result; + unsigned char null = version; + + if (crt == NULL || version == 0 || version >= 0x80) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + MODIFIED(crt); + + if (null > 0) + null--; + + result = + asn1_write_value(crt->cert, "tbsCertificate.version", &null, + 1); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + return 0; +} + +/** + * gnutls_x509_crt_set_key: + * @crt: a certificate of type #gnutls_x509_crt_t + * @key: holds a private key + * + * This function will set the public parameters from the given + * private key to the certificate. + * + * To export the public key (i.e., the SubjectPublicKeyInfo part), check + * gnutls_pubkey_import_x509(). + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + **/ +int +gnutls_x509_crt_set_key(gnutls_x509_crt_t crt, gnutls_x509_privkey_t key) +{ + int result; + + if (crt == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + MODIFIED(crt); + + result = _gnutls_x509_encode_and_copy_PKI_params(crt->cert, + "tbsCertificate.subjectPublicKeyInfo", + &key->params); + + if (result < 0) { + gnutls_assert(); + return result; + } + + return 0; +} + +/** + * gnutls_x509_crt_set_crq: + * @crt: a certificate of type #gnutls_x509_crt_t + * @crq: holds a certificate request + * + * This function will set the name and public parameters as well as + * the extensions from the given certificate request to the certificate. + * Only RSA keys are currently supported. + * + * Note that this function will only set the @crq if it is self + * signed and the signature is correct. See gnutls_x509_crq_sign2(). + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int gnutls_x509_crt_set_crq(gnutls_x509_crt_t crt, gnutls_x509_crq_t crq) +{ + int result; + + if (crt == NULL || crq == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + MODIFIED(crt); + + result = gnutls_x509_crq_verify(crq, 0); + if (result < 0) + return gnutls_assert_val(result); + + result = asn1_copy_node(crt->cert, "tbsCertificate.subject", + crq->crq, + "certificationRequestInfo.subject"); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + result = + asn1_copy_node(crt->cert, + "tbsCertificate.subjectPublicKeyInfo", crq->crq, + "certificationRequestInfo.subjectPKInfo"); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + return 0; +} + +/** + * gnutls_x509_crt_set_crq_extensions: + * @crt: a certificate of type #gnutls_x509_crt_t + * @crq: holds a certificate request + * + * This function will set the extensions from the given request to the + * certificate. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 2.8.0 + **/ +int +gnutls_x509_crt_set_crq_extensions(gnutls_x509_crt_t crt, + gnutls_x509_crq_t crq) +{ + return gnutls_x509_crt_set_crq_extension_by_oid(crt, crq, NULL, 0); +} + +/** + * gnutls_x509_crt_set_crq_extension_by_oid: + * @crt: a certificate of type #gnutls_x509_crt_t + * @crq: holds a certificate request + * @oid: the object identifier of the OID to copy + * @flags: should be zero + * + * This function will set the extension specify by @oid from the given request to the + * certificate. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 3.5.1 + **/ +int +gnutls_x509_crt_set_crq_extension_by_oid(gnutls_x509_crt_t crt, + gnutls_x509_crq_t crq, const char *oid, + unsigned flags) +{ + size_t i; + + if (crt == NULL || crq == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + MODIFIED(crt); + + for (i = 0;; i++) { + int result; + char local_oid[MAX_OID_SIZE]; + size_t local_oid_size; + uint8_t *extensions; + size_t extensions_size; + unsigned int critical; + gnutls_datum_t ext; + + local_oid_size = sizeof(local_oid); + result = gnutls_x509_crq_get_extension_info(crq, i, local_oid, + &local_oid_size, + &critical); + if (result < 0) { + if (result == + GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) + break; + + gnutls_assert(); + return result; + } + + if (oid && strcmp(local_oid, oid) != 0) + continue; + + extensions_size = 0; + result = gnutls_x509_crq_get_extension_data(crq, i, NULL, + &extensions_size); + if (result < 0) { + gnutls_assert(); + return result; + } + + extensions = gnutls_malloc(extensions_size); + if (extensions == NULL) { + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; + } + + result = + gnutls_x509_crq_get_extension_data(crq, i, extensions, + &extensions_size); + if (result < 0) { + gnutls_assert(); + gnutls_free(extensions); + return result; + } + + ext.data = extensions; + ext.size = extensions_size; + + result = + _gnutls_x509_crt_set_extension(crt, local_oid, &ext, + critical); + gnutls_free(extensions); + if (result < 0) { + gnutls_assert(); + return result; + } + } + + return 0; +} + +/** + * gnutls_x509_crt_set_extension_by_oid: + * @crt: a certificate of type #gnutls_x509_crt_t + * @oid: holds an Object Identifier in null terminated string + * @buf: a pointer to a DER encoded data + * @sizeof_buf: holds the size of @buf + * @critical: should be non-zero if the extension is to be marked as critical + * + * This function will set an the extension, by the specified OID, in + * the certificate. The extension data should be binary data DER + * encoded. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int +gnutls_x509_crt_set_extension_by_oid(gnutls_x509_crt_t crt, + const char *oid, const void *buf, + size_t sizeof_buf, + unsigned int critical) +{ + int result; + gnutls_datum_t der_data; + + der_data.data = (void *) buf; + der_data.size = sizeof_buf; + + if (crt == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + result = + _gnutls_x509_crt_set_extension(crt, oid, &der_data, critical); + if (result < 0) { + gnutls_assert(); + return result; + } + + return 0; + +} + +/** + * gnutls_x509_crt_set_basic_constraints: + * @crt: a certificate of type #gnutls_x509_crt_t + * @ca: true(1) or false(0). Depending on the Certificate authority status. + * @pathLenConstraint: non-negative error codes indicate maximum length of path, + * and negative error codes indicate that the pathLenConstraints field should + * not be present. + * + * This function will set the basicConstraints certificate extension. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int +gnutls_x509_crt_set_basic_constraints(gnutls_x509_crt_t crt, + unsigned int ca, + int pathLenConstraint) +{ + int result; + gnutls_datum_t der_data; + + if (crt == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + /* generate the extension. + */ + result = gnutls_x509_ext_export_basic_constraints(ca, pathLenConstraint, &der_data); + if (result < 0) { + gnutls_assert(); + return result; + } + + result = + _gnutls_x509_crt_set_extension(crt, "2.5.29.19", &der_data, 1); + + _gnutls_free_datum(&der_data); + + if (result < 0) { + gnutls_assert(); + return result; + } + + return 0; +} + +/** + * gnutls_x509_crt_set_ca_status: + * @crt: a certificate of type #gnutls_x509_crt_t + * @ca: true(1) or false(0). Depending on the Certificate authority status. + * + * This function will set the basicConstraints certificate extension. + * Use gnutls_x509_crt_set_basic_constraints() if you want to control + * the pathLenConstraint field too. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int gnutls_x509_crt_set_ca_status(gnutls_x509_crt_t crt, unsigned int ca) +{ + return gnutls_x509_crt_set_basic_constraints(crt, ca, -1); +} + +/** + * gnutls_x509_crt_set_key_usage: + * @crt: a certificate of type #gnutls_x509_crt_t + * @usage: an ORed sequence of the GNUTLS_KEY_* elements. + * + * This function will set the keyUsage certificate extension. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int +gnutls_x509_crt_set_key_usage(gnutls_x509_crt_t crt, unsigned int usage) +{ + int result; + gnutls_datum_t der_data; + + if (crt == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + /* generate the extension. + */ + result = + gnutls_x509_ext_export_key_usage(usage, &der_data); + if (result < 0) { + gnutls_assert(); + return result; + } + + result = + _gnutls_x509_crt_set_extension(crt, "2.5.29.15", &der_data, 1); + + _gnutls_free_datum(&der_data); + + if (result < 0) { + gnutls_assert(); + return result; + } + + return 0; +} + +/** + * gnutls_x509_crt_set_inhibit_anypolicy: + * @crt: a certificate of type #gnutls_x509_crt_t + * @skipcerts: number of certificates after which anypolicy is no longer acceptable. + * + * This function will set the Inhibit anyPolicy certificate extension. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int +gnutls_x509_crt_set_inhibit_anypolicy(gnutls_x509_crt_t crt, unsigned int skipcerts) +{ + int ret; + gnutls_datum_t der_data; + + if (crt == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + /* generate the extension. + */ + ret = + gnutls_x509_ext_export_inhibit_anypolicy(skipcerts, &der_data); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + ret = + _gnutls_x509_crt_set_extension(crt, "2.5.29.54", &der_data, 1); + _gnutls_free_datum(&der_data); + + if (ret < 0) { + gnutls_assert(); + return ret; + } + + return 0; +} + +/** + * gnutls_x509_crt_set_subject_alternative_name: + * @crt: a certificate of type #gnutls_x509_crt_t + * @type: is one of the gnutls_x509_subject_alt_name_t enumerations + * @data_string: The data to be set, a (0) terminated string + * + * This function will set the subject alternative name certificate + * extension. This function assumes that data can be expressed as a null + * terminated string. + * + * The name of the function is unfortunate since it is inconsistent with + * gnutls_x509_crt_get_subject_alt_name(). + * + * See gnutls_x509_crt_set_subject_alt_name() for more information. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int +gnutls_x509_crt_set_subject_alternative_name(gnutls_x509_crt_t crt, + gnutls_x509_subject_alt_name_t + type, const char *data_string) +{ + if (crt == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + /* only handle text extensions */ + if (type != GNUTLS_SAN_DNSNAME && type != GNUTLS_SAN_RFC822NAME && + type != GNUTLS_SAN_URI) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + return gnutls_x509_crt_set_subject_alt_name(crt, type, data_string, + strlen(data_string), + GNUTLS_FSAN_SET); +} + +/** + * gnutls_x509_crt_set_subject_alt_name: + * @crt: a certificate of type #gnutls_x509_crt_t + * @type: is one of the gnutls_x509_subject_alt_name_t enumerations + * @data: The data to be set + * @data_size: The size of data to be set + * @flags: GNUTLS_FSAN_SET to clear previous data or GNUTLS_FSAN_APPEND to append. + * + * This function will set the subject alternative name certificate + * extension. It can set the following types: %GNUTLS_SAN_DNSNAME as a text string, + * %GNUTLS_SAN_RFC822NAME as a text string, %GNUTLS_SAN_URI as a text string, + * %GNUTLS_SAN_IPADDRESS as a binary IP address (4 or 16 bytes), + * %GNUTLS_SAN_OTHERNAME_XMPP as a UTF8 string (since 3.5.0). + * + * Since version 3.5.7 the %GNUTLS_SAN_RFC822NAME, %GNUTLS_SAN_DNSNAME, and + * %GNUTLS_SAN_OTHERNAME_XMPP are converted to ACE format when necessary. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 2.6.0 + **/ +int +gnutls_x509_crt_set_subject_alt_name(gnutls_x509_crt_t crt, + gnutls_x509_subject_alt_name_t type, + const void *data, + unsigned int data_size, + unsigned int flags) +{ + int result; + gnutls_datum_t der_data = { NULL, 0 }; + gnutls_datum_t prev_der_data = { NULL, 0 }; + unsigned int critical = 0; + + if (crt == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + /* Check if the extension already exists. + */ + + if (flags & GNUTLS_FSAN_APPEND) { + result = + _gnutls_x509_crt_get_extension(crt, "2.5.29.17", 0, + &prev_der_data, + &critical); + if (result < 0 + && result != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { + gnutls_assert(); + return result; + } + } + + /* generate the extension. + */ + result = + _gnutls_x509_ext_gen_subject_alt_name(type, NULL, data, data_size, + &prev_der_data, + &der_data); + + if (result < 0) { + gnutls_assert(); + goto finish; + } + + result = + _gnutls_x509_crt_set_extension(crt, "2.5.29.17", &der_data, + critical); + + _gnutls_free_datum(&der_data); + + if (result < 0) { + gnutls_assert(); + return result; + } + + result = 0; + + finish: + _gnutls_free_datum(&prev_der_data); + return result; +} + +/** + * gnutls_x509_crt_set_issuer_alt_name: + * @crt: a certificate of type #gnutls_x509_crt_t + * @type: is one of the gnutls_x509_subject_alt_name_t enumerations + * @data: The data to be set + * @data_size: The size of data to be set + * @flags: GNUTLS_FSAN_SET to clear previous data or GNUTLS_FSAN_APPEND to append. + * + * This function will set the issuer alternative name certificate + * extension. It can set the same types as gnutls_x509_crt_set_subject_alt_name(). + * + * Since version 3.5.7 the %GNUTLS_SAN_RFC822NAME, %GNUTLS_SAN_DNSNAME, and + * %GNUTLS_SAN_OTHERNAME_XMPP are converted to ACE format when necessary. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 3.3.0 + **/ +int +gnutls_x509_crt_set_issuer_alt_name(gnutls_x509_crt_t crt, + gnutls_x509_subject_alt_name_t type, + const void *data, + unsigned int data_size, + unsigned int flags) +{ + int result; + gnutls_datum_t der_data = { NULL, 0 }; + gnutls_datum_t prev_der_data = { NULL, 0 }; + unsigned int critical = 0; + + if (crt == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + /* Check if the extension already exists. + */ + + if (flags & GNUTLS_FSAN_APPEND) { + result = + _gnutls_x509_crt_get_extension(crt, "2.5.29.18", 0, + &prev_der_data, + &critical); + if (result < 0 + && result != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { + gnutls_assert(); + return result; + } + } + + /* generate the extension. + */ + result = + _gnutls_x509_ext_gen_subject_alt_name(type, NULL, data, data_size, + &prev_der_data, + &der_data); + + if (result < 0) { + gnutls_assert(); + goto finish; + } + + result = + _gnutls_x509_crt_set_extension(crt, "2.5.29.18", &der_data, + critical); + + _gnutls_free_datum(&der_data); + + if (result < 0) { + gnutls_assert(); + return result; + } + + result = 0; + + finish: + _gnutls_free_datum(&prev_der_data); + return result; +} + +int _gnutls_encode_othername_data(unsigned flags, const void *data, unsigned data_size, gnutls_datum_t *output) +{ + int ret; + if (flags & GNUTLS_FSAN_ENCODE_OCTET_STRING) { + ret = _gnutls_x509_encode_string(ASN1_ETYPE_OCTET_STRING, + data, data_size, output); + } else if (flags & GNUTLS_FSAN_ENCODE_UTF8_STRING) { + ret = _gnutls_x509_encode_string(ASN1_ETYPE_UTF8_STRING, + data, data_size, output); + } else { + ret = _gnutls_set_datum(output, data, data_size); + } + return ret; +} + +/** + * gnutls_x509_crt_set_subject_alt_othername: + * @crt: a certificate of type #gnutls_x509_crt_t + * @oid: The other name OID + * @data: The data to be set + * @data_size: The size of data to be set + * @flags: GNUTLS_FSAN_SET to clear previous data or GNUTLS_FSAN_APPEND to append. + * + * This function will set an "othername" to the subject alternative name certificate + * extension. + * + * The values set are set as binary values and are expected to have the proper DER encoding. + * For convenience the flags %GNUTLS_FSAN_ENCODE_OCTET_STRING and %GNUTLS_FSAN_ENCODE_UTF8_STRING + * can be used to encode the provided data. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 3.5.0 + **/ +int +gnutls_x509_crt_set_subject_alt_othername(gnutls_x509_crt_t crt, + const char *oid, + const void *data, + unsigned int data_size, + unsigned int flags) +{ + int result; + gnutls_datum_t der_data = { NULL, 0 }; + gnutls_datum_t prev_der_data = { NULL, 0 }; + gnutls_datum_t encoded_data = { NULL, 0 }; + unsigned int critical = 0; + + if (crt == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + /* Check if the extension already exists. + */ + + if (flags & GNUTLS_FSAN_APPEND) { + result = + _gnutls_x509_crt_get_extension(crt, "2.5.29.17", 0, + &prev_der_data, + &critical); + if (result < 0 + && result != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { + gnutls_assert(); + return result; + } + } + + result = _gnutls_encode_othername_data(flags, data, data_size, &encoded_data); + if (result < 0) { + gnutls_assert(); + goto finish; + } + + /* generate the extension. + */ + result = + _gnutls_x509_ext_gen_subject_alt_name(GNUTLS_SAN_OTHERNAME, oid, + encoded_data.data, encoded_data.size, + &prev_der_data, &der_data); + + if (result < 0) { + gnutls_assert(); + goto finish; + } + + result = + _gnutls_x509_crt_set_extension(crt, "2.5.29.17", &der_data, + critical); + + + if (result < 0) { + gnutls_assert(); + goto finish; + } + + result = 0; + + finish: + _gnutls_free_datum(&der_data); + _gnutls_free_datum(&prev_der_data); + _gnutls_free_datum(&encoded_data); + return result; +} + +/** + * gnutls_x509_crt_set_issuer_alt_othername: + * @crt: a certificate of type #gnutls_x509_crt_t + * @oid: The other name OID + * @data: The data to be set + * @data_size: The size of data to be set + * @flags: GNUTLS_FSAN_SET to clear previous data or GNUTLS_FSAN_APPEND to append. + * + * This function will set an "othername" to the issuer alternative name certificate + * extension. + * + * The values set are set as binary values and are expected to have the proper DER encoding. + * For convenience the flags %GNUTLS_FSAN_ENCODE_OCTET_STRING and %GNUTLS_FSAN_ENCODE_UTF8_STRING + * can be used to encode the provided data. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 3.5.0 + **/ +int +gnutls_x509_crt_set_issuer_alt_othername(gnutls_x509_crt_t crt, + const char *oid, + const void *data, + unsigned int data_size, + unsigned int flags) +{ + int result; + gnutls_datum_t der_data = { NULL, 0 }; + gnutls_datum_t prev_der_data = { NULL, 0 }; + gnutls_datum_t encoded_data = {NULL, 0}; + unsigned int critical = 0; + + if (crt == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + /* Check if the extension already exists. + */ + + if (flags & GNUTLS_FSAN_APPEND) { + result = + _gnutls_x509_crt_get_extension(crt, "2.5.29.18", 0, + &prev_der_data, + &critical); + if (result < 0 + && result != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { + gnutls_assert(); + return result; + } + } + + result = _gnutls_encode_othername_data(flags, data, data_size, &encoded_data); + if (result < 0) { + gnutls_assert(); + goto finish; + } + + /* generate the extension. + */ + result = + _gnutls_x509_ext_gen_subject_alt_name(GNUTLS_SAN_OTHERNAME, oid, + encoded_data.data, encoded_data.size, + &prev_der_data, &der_data); + if (result < 0) { + gnutls_assert(); + goto finish; + } + + result = + _gnutls_x509_crt_set_extension(crt, "2.5.29.18", &der_data, + critical); + + if (result < 0) { + gnutls_assert(); + goto finish; + } + + result = 0; + + finish: + _gnutls_free_datum(&der_data); + _gnutls_free_datum(&prev_der_data); + _gnutls_free_datum(&encoded_data); + return result; +} + +/** + * gnutls_x509_crt_set_proxy: + * @crt: a certificate of type #gnutls_x509_crt_t + * @pathLenConstraint: non-negative error codes indicate maximum length of path, + * and negative error codes indicate that the pathLenConstraints field should + * not be present. + * @policyLanguage: OID describing the language of @policy. + * @policy: uint8_t byte array with policy language, can be %NULL + * @sizeof_policy: size of @policy. + * + * This function will set the proxyCertInfo extension. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int +gnutls_x509_crt_set_proxy(gnutls_x509_crt_t crt, + int pathLenConstraint, + const char *policyLanguage, + const char *policy, size_t sizeof_policy) +{ + int result; + gnutls_datum_t der_data; + + if (crt == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + /* generate the extension. + */ + result = gnutls_x509_ext_export_proxy(pathLenConstraint, + policyLanguage, + policy, sizeof_policy, + &der_data); + if (result < 0) { + gnutls_assert(); + return result; + } + + result = _gnutls_x509_crt_set_extension(crt, "1.3.6.1.5.5.7.1.14", + &der_data, 1); + + _gnutls_free_datum(&der_data); + + if (result < 0) { + gnutls_assert(); + return result; + } + + return 0; +} + +/** + * gnutls_x509_crt_set_private_key_usage_period: + * @crt: a certificate of type #gnutls_x509_crt_t + * @activation: The activation time + * @expiration: The expiration time + * + * This function will set the private key usage period extension (2.5.29.16). + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int +gnutls_x509_crt_set_private_key_usage_period(gnutls_x509_crt_t crt, + time_t activation, + time_t expiration) +{ + int result; + gnutls_datum_t der_data; + + if (crt == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + result = gnutls_x509_ext_export_private_key_usage_period(activation, + expiration, &der_data); + if (result < 0) { + gnutls_assert(); + goto cleanup; + } + + result = _gnutls_x509_crt_set_extension(crt, "2.5.29.16", + &der_data, 0); + + _gnutls_free_datum(&der_data); + + cleanup: + return result; +} + +/** + * gnutls_x509_crt_sign2: + * @crt: a certificate of type #gnutls_x509_crt_t + * @issuer: is the certificate of the certificate issuer + * @issuer_key: holds the issuer's private key + * @dig: The message digest to use, %GNUTLS_DIG_SHA256 is a safe choice + * @flags: must be 0 + * + * This function will sign the certificate with the issuer's private key, and + * will copy the issuer's information into the certificate. + * + * This must be the last step in a certificate generation since all + * the previously set parameters are now signed. + * + * A known limitation of this function is, that a newly-signed certificate will not + * be fully functional (e.g., for signature verification), until it + * is exported an re-imported. + * + * After GnuTLS 3.6.1 the value of @dig may be %GNUTLS_DIG_UNKNOWN, + * and in that case, a suitable but reasonable for the key algorithm will be selected. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int +gnutls_x509_crt_sign2(gnutls_x509_crt_t crt, gnutls_x509_crt_t issuer, + gnutls_x509_privkey_t issuer_key, + gnutls_digest_algorithm_t dig, unsigned int flags) +{ + int result; + gnutls_privkey_t privkey; + + if (crt == NULL || issuer == NULL || issuer_key == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + MODIFIED(crt); + + result = gnutls_privkey_init(&privkey); + if (result < 0) { + gnutls_assert(); + return result; + } + + result = gnutls_privkey_import_x509(privkey, issuer_key, 0); + if (result < 0) { + gnutls_assert(); + goto fail; + } + + result = + gnutls_x509_crt_privkey_sign(crt, issuer, privkey, dig, flags); + if (result < 0) { + gnutls_assert(); + goto fail; + } + + result = 0; + + fail: + gnutls_privkey_deinit(privkey); + + return result; +} + +/** + * gnutls_x509_crt_sign: + * @crt: a certificate of type #gnutls_x509_crt_t + * @issuer: is the certificate of the certificate issuer + * @issuer_key: holds the issuer's private key + * + * This function is the same a gnutls_x509_crt_sign2() with no flags, + * and an appropriate hash algorithm. The hash algorithm used may + * vary between versions of GnuTLS, and it is tied to the security + * level of the issuer's public key. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int +gnutls_x509_crt_sign(gnutls_x509_crt_t crt, gnutls_x509_crt_t issuer, + gnutls_x509_privkey_t issuer_key) +{ + return gnutls_x509_crt_sign2(crt, issuer, issuer_key, + 0, 0); +} + +/** + * gnutls_x509_crt_set_activation_time: + * @cert: a certificate of type #gnutls_x509_crt_t + * @act_time: The actual time + * + * This function will set the time this certificate was or will be + * activated. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int +gnutls_x509_crt_set_activation_time(gnutls_x509_crt_t cert, + time_t act_time) +{ + if (cert == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + MODIFIED(cert); + + return _gnutls_x509_set_time(cert->cert, + "tbsCertificate.validity.notBefore", + act_time, 0); +} + +/** + * gnutls_x509_crt_set_expiration_time: + * @cert: a certificate of type #gnutls_x509_crt_t + * @exp_time: The actual time + * + * This function will set the time this Certificate will expire. + * Setting an expiration time to (time_t)-1 will set + * to the no well-defined expiration date value. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int +gnutls_x509_crt_set_expiration_time(gnutls_x509_crt_t cert, + time_t exp_time) +{ + if (cert == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + MODIFIED(cert); + + return _gnutls_x509_set_time(cert->cert, + "tbsCertificate.validity.notAfter", + exp_time, 0); +} + +/** + * gnutls_x509_crt_set_serial: + * @cert: a certificate of type #gnutls_x509_crt_t + * @serial: The serial number + * @serial_size: Holds the size of the serial field. + * + * This function will set the X.509 certificate's serial number. + * While the serial number is an integer, it is often handled + * as an opaque field by several CAs. For this reason this function + * accepts any kind of data as a serial number. To be consistent + * with the X.509/PKIX specifications the provided @serial should be + * a big-endian positive number (i.e. its leftmost bit should be zero). + * + * The size of the serial is restricted to 20 bytes maximum by RFC5280. + * This function allows writing more than 20 bytes but the generated + * certificates in that case may be rejected by other implementations. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int +gnutls_x509_crt_set_serial(gnutls_x509_crt_t cert, const void *serial, + size_t serial_size) +{ + int ret; + unsigned all_zero, i; + const unsigned char *pserial = serial; + + if (cert == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + /* check for non-zero serial */ + all_zero = 1; + for (i=0;i<serial_size;i++) { + if (pserial[i] != 0) { + all_zero = 0; + break; + } + } + + if (all_zero) { + _gnutls_debug_log("error: certificate serial is zero\n"); + return GNUTLS_E_INVALID_REQUEST; + } + + MODIFIED(cert); + + ret = + asn1_write_value(cert->cert, "tbsCertificate.serialNumber", + serial, serial_size); + if (ret != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(ret); + } + + return 0; + +} + +/** + * gnutls_x509_crt_set_issuer_unique_id: + * @cert: a certificate of type #gnutls_x509_crt_t + * @id: The unique ID + * @id_size: Holds the size of the unique ID. + * + * This function will set the X.509 certificate's issuer unique ID field. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 3.4.7 + **/ +int +gnutls_x509_crt_set_issuer_unique_id(gnutls_x509_crt_t cert, const void *id, + size_t id_size) +{ + int ret; + + if (cert == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + MODIFIED(cert); + + ret = + asn1_write_value(cert->cert, "tbsCertificate.issuerUniqueID", + id, id_size*8); + if (ret != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(ret); + } + + return 0; +} + +/** + * gnutls_x509_crt_set_subject_unique_id: + * @cert: a certificate of type #gnutls_x509_crt_t + * @id: The unique ID + * @id_size: Holds the size of the unique ID. + * + * This function will set the X.509 certificate's subject unique ID field. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 3.4.7 + **/ +int +gnutls_x509_crt_set_subject_unique_id(gnutls_x509_crt_t cert, const void *id, + size_t id_size) +{ + int ret; + + if (cert == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + MODIFIED(cert); + + ret = + asn1_write_value(cert->cert, "tbsCertificate.subjectUniqueID", + id, id_size*8); + if (ret != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(ret); + } + + return 0; +} + +/* If OPTIONAL fields have not been initialized then + * disable them. + */ +static void disable_optional_stuff(gnutls_x509_crt_t cert) +{ + asn1_data_node_st n; + asn1_node node; + unsigned remove_subject_unique_id = 1; + unsigned remove_issuer_unique_id = 1; + + node = asn1_find_node(cert->cert, "tbsCertificate.issuerUniqueID"); + if (node) { + if (asn1_read_node_value(node, &n) == ASN1_SUCCESS && n.value_len != 0) + remove_issuer_unique_id = 0; + } + + node = asn1_find_node(cert->cert, "tbsCertificate.subjectUniqueID"); + if (node) { + if (asn1_read_node_value(node, &n) == ASN1_SUCCESS && n.value_len != 0) + remove_subject_unique_id = 0; + } + + if (remove_issuer_unique_id) + (void)asn1_write_value(cert->cert, "tbsCertificate.issuerUniqueID", NULL, + 0); + + if (remove_subject_unique_id) + (void)asn1_write_value(cert->cert, "tbsCertificate.subjectUniqueID", + NULL, 0); + + if (cert->use_extensions == 0) { + _gnutls_debug_log("Disabling X.509 extensions.\n"); + (void)asn1_write_value(cert->cert, "tbsCertificate.extensions", + NULL, 0); + } + + return; +} + +/** + * gnutls_x509_crt_set_crl_dist_points: + * @crt: a certificate of type #gnutls_x509_crt_t + * @type: is one of the gnutls_x509_subject_alt_name_t enumerations + * @data_string: The data to be set + * @reason_flags: revocation reasons + * + * This function will set the CRL distribution points certificate extension. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int +gnutls_x509_crt_set_crl_dist_points(gnutls_x509_crt_t crt, + gnutls_x509_subject_alt_name_t type, + const void *data_string, + unsigned int reason_flags) +{ + return gnutls_x509_crt_set_crl_dist_points2(crt, type, data_string, + strlen(data_string), + reason_flags); +} + +/** + * gnutls_x509_crt_set_crl_dist_points2: + * @crt: a certificate of type #gnutls_x509_crt_t + * @type: is one of the gnutls_x509_subject_alt_name_t enumerations + * @data: The data to be set + * @data_size: The data size + * @reason_flags: revocation reasons + * + * This function will set the CRL distribution points certificate extension. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 2.6.0 + **/ +int +gnutls_x509_crt_set_crl_dist_points2(gnutls_x509_crt_t crt, + gnutls_x509_subject_alt_name_t type, + const void *data, + unsigned int data_size, + unsigned int reason_flags) +{ + int ret; + gnutls_datum_t der_data = { NULL, 0 }; + gnutls_datum_t old_der = { NULL, 0 }; + unsigned int critical; + gnutls_x509_crl_dist_points_t cdp = NULL; + gnutls_datum_t san; + + if (crt == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + ret = gnutls_x509_crl_dist_points_init(&cdp); + if (ret < 0) + return gnutls_assert_val(ret); + + /* Check if the extension already exists. + */ + ret = + _gnutls_x509_crt_get_extension(crt, "2.5.29.31", 0, &old_der, + &critical); + + if (ret >= 0 && old_der.data != NULL) { + ret = gnutls_x509_ext_import_crl_dist_points(&old_der, cdp, 0); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + } + + san.data = (void*)data; + san.size = data_size; + ret = gnutls_x509_crl_dist_points_set(cdp, type, &san, reason_flags); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + /* generate the extension. + */ + ret = + gnutls_x509_ext_export_crl_dist_points(cdp, &der_data); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = + _gnutls_x509_crt_set_extension(crt, "2.5.29.31", &der_data, 0); + + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = 0; + cleanup: + _gnutls_free_datum(&der_data); + _gnutls_free_datum(&old_der); + if (cdp != NULL) + gnutls_x509_crl_dist_points_deinit(cdp); + + return ret; + +} + +/** + * gnutls_x509_crt_cpy_crl_dist_points: + * @dst: a certificate of type #gnutls_x509_crt_t + * @src: the certificate where the dist points will be copied from + * + * This function will copy the CRL distribution points certificate + * extension, from the source to the destination certificate. + * This may be useful to copy from a CA certificate to issued ones. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int +gnutls_x509_crt_cpy_crl_dist_points(gnutls_x509_crt_t dst, + gnutls_x509_crt_t src) +{ + int result; + gnutls_datum_t der_data; + unsigned int critical; + + if (dst == NULL || src == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + /* Check if the extension already exists. + */ + result = + _gnutls_x509_crt_get_extension(src, "2.5.29.31", 0, &der_data, + &critical); + if (result < 0) { + gnutls_assert(); + return result; + } + + result = + _gnutls_x509_crt_set_extension(dst, "2.5.29.31", &der_data, + critical); + _gnutls_free_datum(&der_data); + + if (result < 0) { + gnutls_assert(); + return result; + } + + return 0; +} + +/** + * gnutls_x509_crt_set_subject_key_id: + * @cert: a certificate of type #gnutls_x509_crt_t + * @id: The key ID + * @id_size: Holds the size of the subject key ID field. + * + * This function will set the X.509 certificate's subject key ID + * extension. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int +gnutls_x509_crt_set_subject_key_id(gnutls_x509_crt_t cert, + const void *id, size_t id_size) +{ + int result; + gnutls_datum_t old_id, der_data; + gnutls_datum_t d_id; + unsigned int critical; + + if (cert == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + /* Check if the extension already exists. + */ + result = + _gnutls_x509_crt_get_extension(cert, "2.5.29.14", 0, &old_id, + &critical); + + if (result >= 0) + _gnutls_free_datum(&old_id); + if (result != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + /* generate the extension. + */ + d_id.data = (void*)id; + d_id.size = id_size; + + result = gnutls_x509_ext_export_subject_key_id(&d_id, &der_data); + if (result < 0) { + gnutls_assert(); + return result; + } + + result = + _gnutls_x509_crt_set_extension(cert, "2.5.29.14", &der_data, + 0); + + _gnutls_free_datum(&der_data); + + if (result < 0) { + gnutls_assert(); + return result; + } + + return 0; +} + +/** + * gnutls_x509_crt_set_authority_key_id: + * @cert: a certificate of type #gnutls_x509_crt_t + * @id: The key ID + * @id_size: Holds the size of the key ID field. + * + * This function will set the X.509 certificate's authority key ID extension. + * Only the keyIdentifier field can be set with this function. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int +gnutls_x509_crt_set_authority_key_id(gnutls_x509_crt_t cert, + const void *id, size_t id_size) +{ + int result; + gnutls_datum_t old_id, der_data; + unsigned int critical; + + if (cert == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + /* Check if the extension already exists. + */ + result = + _gnutls_x509_crt_get_extension(cert, "2.5.29.35", 0, &old_id, + &critical); + + if (result >= 0) + _gnutls_free_datum(&old_id); + if (result != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + /* generate the extension. + */ + result = _gnutls_x509_ext_gen_auth_key_id(id, id_size, &der_data); + if (result < 0) { + gnutls_assert(); + return result; + } + + result = + _gnutls_x509_crt_set_extension(cert, "2.5.29.35", &der_data, + 0); + + _gnutls_free_datum(&der_data); + + if (result < 0) { + gnutls_assert(); + return result; + } + + return 0; +} + +/** + * gnutls_x509_crt_set_key_purpose_oid: + * @cert: a certificate of type #gnutls_x509_crt_t + * @oid: a pointer to a null terminated string that holds the OID + * @critical: Whether this extension will be critical or not + * + * This function will set the key purpose OIDs of the Certificate. + * These are stored in the Extended Key Usage extension (2.5.29.37) + * See the GNUTLS_KP_* definitions for human readable names. + * + * Subsequent calls to this function will append OIDs to the OID list. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, + * otherwise a negative error code is returned. + **/ +int +gnutls_x509_crt_set_key_purpose_oid(gnutls_x509_crt_t cert, + const void *oid, unsigned int critical) +{ + int ret; + gnutls_datum_t old_id = {NULL,0}; + gnutls_datum_t der = {NULL,0}; + gnutls_x509_key_purposes_t p = NULL; + + if (cert == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + ret = gnutls_x509_key_purpose_init(&p); + if (ret < 0) + return gnutls_assert_val(ret); + + /* Check if the extension already exists. + */ + ret = + _gnutls_x509_crt_get_extension(cert, "2.5.29.37", 0, &old_id, + NULL); + + if (ret >= 0) { + ret = gnutls_x509_ext_import_key_purposes(&old_id, p, 0); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + } + + ret = gnutls_x509_key_purpose_set(p, oid); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = gnutls_x509_ext_export_key_purposes(p, &der); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = _gnutls_x509_crt_set_extension(cert, "2.5.29.37", + &der, critical); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = 0; + cleanup: + _gnutls_free_datum(&der); + _gnutls_free_datum(&old_id); + if (p != NULL) + gnutls_x509_key_purpose_deinit(p); + + return ret; + +} + +/** + * gnutls_x509_crt_privkey_sign: + * @crt: a certificate of type #gnutls_x509_crt_t + * @issuer: is the certificate of the certificate issuer + * @issuer_key: holds the issuer's private key + * @dig: The message digest to use, %GNUTLS_DIG_SHA256 is a safe choice + * @flags: must be 0 + * + * This function will sign the certificate with the issuer's private key, and + * will copy the issuer's information into the certificate. + * + * This must be the last step in a certificate generation since all + * the previously set parameters are now signed. + * + * A known limitation of this function is, that a newly-signed certificate will not + * be fully functional (e.g., for signature verification), until it + * is exported an re-imported. + * + * After GnuTLS 3.6.1 the value of @dig may be %GNUTLS_DIG_UNKNOWN, + * and in that case, a suitable but reasonable for the key algorithm will be selected. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int +gnutls_x509_crt_privkey_sign(gnutls_x509_crt_t crt, + gnutls_x509_crt_t issuer, + gnutls_privkey_t issuer_key, + gnutls_digest_algorithm_t dig, + unsigned int flags) +{ + int result; + + if (crt == NULL || issuer == NULL || issuer_key == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + if (dig == 0) { + result = gnutls_x509_crt_get_preferred_hash_algorithm(issuer, &dig, NULL); + if (result < 0) + return gnutls_assert_val(result); + } + + MODIFIED(crt); + + /* disable all the unneeded OPTIONAL fields. + */ + disable_optional_stuff(crt); + + result = _gnutls_check_cert_sanity(crt); + if (result < 0) { + gnutls_assert(); + return result; + } + + result = _gnutls_x509_pkix_sign(crt->cert, "tbsCertificate", + dig, flags, issuer, issuer_key); + if (result < 0) { + gnutls_assert(); + return result; + } + + return 0; +} + +/** + * gnutls_x509_crt_set_authority_info_access: + * @crt: Holds the certificate + * @what: what data to get, a #gnutls_info_access_what_t type. + * @data: output data to be freed with gnutls_free(). + * + * This function sets the Authority Information Access (AIA) + * extension, see RFC 5280 section 4.2.2.1 for more information. + * + * The type of data stored in @data is specified via @what which + * should be #gnutls_info_access_what_t values. + * + * If @what is %GNUTLS_IA_OCSP_URI, @data will hold the OCSP URI. + * If @what is %GNUTLS_IA_CAISSUERS_URI, @data will hold the caIssuers + * URI. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 3.0 + **/ +int +gnutls_x509_crt_set_authority_info_access(gnutls_x509_crt_t crt, + int what, gnutls_datum_t * data) +{ + int ret; + gnutls_datum_t der = { NULL, 0 }; + gnutls_datum_t new_der = { NULL, 0 }; + gnutls_x509_aia_t aia_ctx = NULL; + const char *oid; + unsigned int c; + + if (crt == NULL) + return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + + ret = gnutls_x509_aia_init(&aia_ctx); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + ret = _gnutls_x509_crt_get_extension(crt, GNUTLS_OID_AIA, 0, &der, + &c); + if (ret >= 0) { /* decode it */ + ret = gnutls_x509_ext_import_aia(&der, aia_ctx, 0); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + } + + if (what == GNUTLS_IA_OCSP_URI) + oid = GNUTLS_OID_AD_OCSP; + else if (what == GNUTLS_IA_CAISSUERS_URI) + oid = GNUTLS_OID_AD_CAISSUERS; + else + return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + ret = gnutls_x509_aia_set(aia_ctx, oid, GNUTLS_SAN_URI, data); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = gnutls_x509_ext_export_aia(aia_ctx, &new_der); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = _gnutls_x509_crt_set_extension(crt, GNUTLS_OID_AIA, + &new_der, 0); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + cleanup: + if (aia_ctx != NULL) + gnutls_x509_aia_deinit(aia_ctx); + _gnutls_free_datum(&new_der); + _gnutls_free_datum(&der); + + return ret; +} + + +/** + * gnutls_x509_crt_set_policy: + * @crt: should contain a #gnutls_x509_crt_t type + * @policy: A pointer to a policy + * @critical: use non-zero if the extension is marked as critical + * + * This function will set the certificate policy extension (2.5.29.32). + * Multiple calls to this function append a new policy. + * + * Note the maximum text size for the qualifier %GNUTLS_X509_QUALIFIER_NOTICE + * is 200 characters. This function will fail with %GNUTLS_E_INVALID_REQUEST + * if this is exceeded. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 3.1.5 + **/ +int +gnutls_x509_crt_set_policy(gnutls_x509_crt_t crt, + const struct gnutls_x509_policy_st *policy, + unsigned int critical) +{ + int ret; + gnutls_datum_t der_data = {NULL, 0}, prev_der_data = { NULL, 0 }; + gnutls_x509_policies_t policies = NULL; + + if (crt == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + ret = gnutls_x509_policies_init(&policies); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + ret = _gnutls_x509_crt_get_extension(crt, "2.5.29.32", 0, + &prev_der_data, NULL); + if (ret < 0 && ret != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { + gnutls_assert(); + goto cleanup; + } + + + if (ret != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { + ret = gnutls_x509_ext_import_policies(&prev_der_data, + policies, 0); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + } + + ret = gnutls_x509_policies_set(policies, policy); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = gnutls_x509_ext_export_policies(policies, &der_data); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = _gnutls_x509_crt_set_extension(crt, "2.5.29.32", + &der_data, 0); + + cleanup: + if (policies != NULL) + gnutls_x509_policies_deinit(policies); + _gnutls_free_datum(&prev_der_data); + _gnutls_free_datum(&der_data); + + return ret; +} + +/** + * gnutls_x509_crt_set_spki: + * @crt: a certificate of type #gnutls_x509_crt_t + * @spki: a SubjectPublicKeyInfo structure of type #gnutls_x509_spki_t + * @flags: must be zero + * + * This function will set the certificate's subject public key + * information explicitly. This is intended to be used in the cases + * where a single public key (e.g., RSA) can be used for multiple + * signature algorithms (RSA PKCS1-1.5, and RSA-PSS). + * + * To export the public key (i.e., the SubjectPublicKeyInfo part), check + * gnutls_pubkey_import_x509(). + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 3.6.0 + **/ +int +gnutls_x509_crt_set_spki(gnutls_x509_crt_t crt, + const gnutls_x509_spki_t spki, + unsigned int flags) +{ + int ret; + gnutls_pk_algorithm_t crt_pk; + gnutls_x509_spki_st tpki; + gnutls_pk_params_st params; + unsigned bits; + + if (crt == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + ret = _gnutls_x509_crt_get_mpis(crt, ¶ms); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + bits = pubkey_to_bits(¶ms); + crt_pk = params.algo; + + if (!_gnutls_pk_are_compat(crt_pk, spki->pk)) { + ret = gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + goto cleanup; + } + + if (spki->pk != GNUTLS_PK_RSA_PSS) { + if (crt_pk == spki->pk) { + ret = 0; + goto cleanup; + } + + gnutls_assert(); + ret = GNUTLS_E_INVALID_REQUEST; + goto cleanup; + } + + memset(&tpki, 0, sizeof(gnutls_x509_spki_st)); + + if (crt_pk == GNUTLS_PK_RSA) { + const mac_entry_st *me; + + me = hash_to_entry(spki->rsa_pss_dig); + if (unlikely(me == NULL)) { + gnutls_assert(); + ret = GNUTLS_E_INVALID_REQUEST; + goto cleanup; + } + + tpki.pk = spki->pk; + tpki.rsa_pss_dig = spki->rsa_pss_dig; + + /* If salt size is zero, find the optimal salt size. */ + if (spki->salt_size == 0) { + ret = _gnutls_find_rsa_pss_salt_size(bits, me, + spki->salt_size); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + tpki.salt_size = ret; + } else + tpki.salt_size = spki->salt_size; + } else if (crt_pk == GNUTLS_PK_RSA_PSS) { + ret = _gnutls_x509_crt_read_spki_params(crt, &tpki); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + tpki.salt_size = spki->salt_size; + tpki.rsa_pss_dig = spki->rsa_pss_dig; + } + + memcpy(¶ms.spki, &tpki, sizeof(tpki)); + ret = _gnutls_x509_check_pubkey_params(¶ms); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + MODIFIED(crt); + + ret = _gnutls_x509_write_spki_params(crt->cert, + "tbsCertificate." + "subjectPublicKeyInfo.algorithm", + &tpki); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = 0; + cleanup: + gnutls_pk_params_release(¶ms); + return ret; +} diff --git a/lib/x509_b64.c b/lib/x509_b64.c new file mode 100644 index 0000000..668760a --- /dev/null +++ b/lib/x509_b64.c @@ -0,0 +1,536 @@ +/* + * Copyright (C) 2000-2012 Free Software Foundation, Inc. + * Copyright (C) 2017 Red Hat, Inc. + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GnuTLS. + * + * The GnuTLS is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/> + * + */ + +/* Functions that relate to base64 encoding and decoding. + */ + +#include "gnutls_int.h" +#include "errors.h" +#include <datum.h> +#include <x509_b64.h> +#include <nettle/base64.h> + +#define INCR(what, size, max_len) \ + do { \ + what+=size; \ + if (what > max_len) { \ + gnutls_assert(); \ + gnutls_free( result->data); result->data = NULL; \ + return GNUTLS_E_INTERNAL_ERROR; \ + } \ + } while(0) + +/* encodes data and puts the result into result (locally allocated) + * The result_size (including the null terminator) is the return value. + */ +int +_gnutls_fbase64_encode(const char *msg, const uint8_t * data, + size_t data_size, gnutls_datum_t * result) +{ + int tmp; + unsigned int i; + uint8_t tmpres[66]; + uint8_t *ptr; + char top[80]; + char bottom[80]; + size_t size, max, bytes; + int pos, top_len = 0, bottom_len = 0; + unsigned raw_encoding = 0; + + if (msg == NULL || msg[0] == 0) + raw_encoding = 1; + + if (!raw_encoding) { + if (strlen(msg) > 50) { + gnutls_assert(); + return GNUTLS_E_BASE64_ENCODING_ERROR; + } + + _gnutls_str_cpy(top, sizeof(top), "-----BEGIN "); + _gnutls_str_cat(top, sizeof(top), msg); + _gnutls_str_cat(top, sizeof(top), "-----\n"); + + _gnutls_str_cpy(bottom, sizeof(bottom), "-----END "); + _gnutls_str_cat(bottom, sizeof(bottom), msg); + _gnutls_str_cat(bottom, sizeof(bottom), "-----\n"); + + top_len = strlen(top); + bottom_len = strlen(bottom); + } + + max = B64FSIZE(top_len + bottom_len, data_size); + + result->data = gnutls_malloc(max + 1); + if (result->data == NULL) { + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; + } + + bytes = 0; + INCR(bytes, top_len, max); + pos = top_len; + + memcpy(result->data, top, top_len); + + for (i = 0; i < data_size; i += 48) { + if (data_size - i < 48) + tmp = data_size - i; + else + tmp = 48; + + size = BASE64_ENCODE_RAW_LENGTH(tmp); + if (sizeof(tmpres) < size) + return gnutls_assert_val(GNUTLS_E_BASE64_ENCODING_ERROR); + + base64_encode_raw((void*)tmpres, tmp, &data[i]); + + INCR(bytes, size + 1, max); + ptr = &result->data[pos]; + + memcpy(ptr, tmpres, size); + ptr += size; + pos += size; + if (!raw_encoding) { + *ptr++ = '\n'; + pos++; + } else { + bytes--; + } + } + + INCR(bytes, bottom_len, max); + + memcpy(&result->data[bytes - bottom_len], bottom, bottom_len); + result->data[bytes] = 0; + result->size = bytes; + + return max + 1; +} + +/** + * gnutls_pem_base64_encode: + * @msg: is a message to be put in the header (may be %NULL) + * @data: contain the raw data + * @result: the place where base64 data will be copied + * @result_size: holds the size of the result + * + * This function will convert the given data to printable data, using + * the base64 encoding. This is the encoding used in PEM messages. + * + * The output string will be null terminated, although the output size will + * not include the terminating null. + * + * Returns: On success %GNUTLS_E_SUCCESS (0) is returned, + * %GNUTLS_E_SHORT_MEMORY_BUFFER is returned if the buffer given is + * not long enough, or 0 on success. + **/ +int +gnutls_pem_base64_encode(const char *msg, const gnutls_datum_t * data, + char *result, size_t * result_size) +{ + gnutls_datum_t res; + int ret; + + ret = _gnutls_fbase64_encode(msg, data->data, data->size, &res); + if (ret < 0) + return ret; + + if (result == NULL || *result_size < (unsigned) res.size) { + gnutls_free(res.data); + *result_size = res.size + 1; + return GNUTLS_E_SHORT_MEMORY_BUFFER; + } else { + memcpy(result, res.data, res.size); + gnutls_free(res.data); + *result_size = res.size; + } + + return 0; +} + +/** + * gnutls_pem_base64_encode2: + * @header: is a message to be put in the encoded header (may be %NULL) + * @data: contains the raw data + * @result: will hold the newly allocated encoded data + * + * This function will convert the given data to printable data, using + * the base64 encoding. This is the encoding used in PEM messages. + * This function will allocate the required memory to hold the encoded + * data. + * + * You should use gnutls_free() to free the returned data. + * + * Note, that prior to GnuTLS 3.4.0 this function was available + * under the name gnutls_pem_base64_encode_alloc(). There is + * compatibility macro pointing to this function. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise + * an error code is returned. + * + * Since: 3.4.0 + **/ +int +gnutls_pem_base64_encode2(const char *header, + const gnutls_datum_t * data, + gnutls_datum_t * result) +{ + int ret; + + if (result == NULL) + return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + + ret = _gnutls_fbase64_encode(header, data->data, data->size, result); + if (ret < 0) + return gnutls_assert_val(ret); + + return 0; +} + +/* copies data to result but removes newlines and <CR> + * returns the size of the data copied. + * + * It will fail with GNUTLS_E_BASE64_DECODING_ERROR if the + * end-result is the empty string. + */ +inline static int +cpydata(const uint8_t * data, int data_size, gnutls_datum_t * result) +{ + int i, j; + + result->data = gnutls_malloc(data_size + 1); + if (result->data == NULL) + return GNUTLS_E_MEMORY_ERROR; + + for (j = i = 0; i < data_size; i++) { + if (data[i] == '\n' || data[i] == '\r' || data[i] == ' ' + || data[i] == '\t') + continue; + else if (data[i] == '-') + break; + result->data[j] = data[i]; + j++; + } + + result->size = j; + result->data[j] = 0; + + if (j==0) { + gnutls_free(result->data); + return gnutls_assert_val(GNUTLS_E_BASE64_DECODING_ERROR); + } + + return j; +} + +/* decodes data and puts the result into result (locally allocated). + * Note that encodings of zero-length strings are being rejected + * with GNUTLS_E_BASE64_DECODING_ERROR. + * + * The result_size is the return value. + */ +int +_gnutls_base64_decode(const uint8_t * data, size_t data_size, + gnutls_datum_t * result) +{ + int ret; + size_t size; + gnutls_datum_t pdata; + struct base64_decode_ctx ctx; + + if (data_size == 0) { + result->data = (unsigned char*)gnutls_strdup(""); + if (result->data == NULL) + return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + result->size = 0; + return 0; + } + + ret = cpydata(data, data_size, &pdata); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + base64_decode_init(&ctx); + + size = BASE64_DECODE_LENGTH(pdata.size); + if (size == 0) { + ret = gnutls_assert_val(GNUTLS_E_BASE64_DECODING_ERROR); + goto cleanup; + } + + result->data = gnutls_malloc(size); + if (result->data == NULL) { + ret = gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + goto cleanup; + } + + ret = base64_decode_update(&ctx, &size, result->data, + pdata.size, (void*)pdata.data); + if (ret == 0 || size == 0) { + gnutls_assert(); + ret = GNUTLS_E_BASE64_DECODING_ERROR; + goto fail; + } + + ret = base64_decode_final(&ctx); + if (ret != 1) { + ret = gnutls_assert_val(GNUTLS_E_BASE64_DECODING_ERROR); + goto fail; + } + + result->size = size; + + ret = size; + goto cleanup; + + fail: + gnutls_free(result->data); + + cleanup: + gnutls_free(pdata.data); + return ret; +} + + +/* Searches the given string for ONE PEM encoded certificate, and + * stores it in the result. + * + * The result_size (always non-zero) is the return value, + * or a negative error code. + */ +#define ENDSTR "-----" +int +_gnutls_fbase64_decode(const char *header, const uint8_t * data, + size_t data_size, gnutls_datum_t * result) +{ + int ret; + static const char top[] = "-----BEGIN "; + static const char bottom[] = "-----END "; + uint8_t *rdata, *kdata; + int rdata_size; + char pem_header[128]; + + _gnutls_str_cpy(pem_header, sizeof(pem_header), top); + if (header != NULL) + _gnutls_str_cat(pem_header, sizeof(pem_header), header); + + rdata = memmem(data, data_size, pem_header, strlen(pem_header)); + if (rdata == NULL) { + gnutls_assert(); + _gnutls_hard_log("Could not find '%s'\n", pem_header); + return GNUTLS_E_BASE64_UNEXPECTED_HEADER_ERROR; + } + + data_size -= MEMSUB(rdata, data); + + if (data_size < 4 + strlen(bottom)) { + gnutls_assert(); + return GNUTLS_E_BASE64_DECODING_ERROR; + } + + kdata = + memmem(rdata + 1, data_size - 1, ENDSTR, sizeof(ENDSTR) - 1); + /* allow CR as well. + */ + if (kdata == NULL) { + gnutls_assert(); + _gnutls_hard_log("Could not find '%s'\n", ENDSTR); + return GNUTLS_E_BASE64_DECODING_ERROR; + } + data_size -= strlen(ENDSTR); + data_size -= MEMSUB(kdata, rdata); + + rdata = kdata + strlen(ENDSTR); + + /* position is now after the ---BEGIN--- headers */ + + kdata = memmem(rdata, data_size, bottom, strlen(bottom)); + if (kdata == NULL) { + gnutls_assert(); + return GNUTLS_E_BASE64_DECODING_ERROR; + } + + /* position of kdata is before the ----END--- footer + */ + rdata_size = MEMSUB(kdata, rdata); + + if (rdata_size < 4) { + gnutls_assert(); + return GNUTLS_E_BASE64_DECODING_ERROR; + } + + if ((ret = _gnutls_base64_decode(rdata, rdata_size, result)) < 0) { + gnutls_assert(); + return GNUTLS_E_BASE64_DECODING_ERROR; + } + + return ret; +} + +/** + * gnutls_pem_base64_decode: + * @header: A null terminated string with the PEM header (eg. CERTIFICATE) + * @b64_data: contain the encoded data + * @result: the place where decoded data will be copied + * @result_size: holds the size of the result + * + * This function will decode the given encoded data. If the header + * given is non %NULL this function will search for "-----BEGIN header" + * and decode only this part. Otherwise it will decode the first PEM + * packet found. + * + * Returns: On success %GNUTLS_E_SUCCESS (0) is returned, + * %GNUTLS_E_SHORT_MEMORY_BUFFER is returned if the buffer given is + * not long enough, or 0 on success. + **/ +int +gnutls_pem_base64_decode(const char *header, + const gnutls_datum_t * b64_data, + unsigned char *result, size_t * result_size) +{ + gnutls_datum_t res; + int ret; + + ret = + _gnutls_fbase64_decode(header, b64_data->data, b64_data->size, + &res); + if (ret < 0) + return gnutls_assert_val(ret); + + if (result == NULL || *result_size < (unsigned) res.size) { + gnutls_free(res.data); + *result_size = res.size; + return GNUTLS_E_SHORT_MEMORY_BUFFER; + } else { + memcpy(result, res.data, res.size); + gnutls_free(res.data); + *result_size = res.size; + } + + return 0; +} + +/** + * gnutls_pem_base64_decode2: + * @header: The PEM header (eg. CERTIFICATE) + * @b64_data: contains the encoded data + * @result: the location of decoded data + * + * This function will decode the given encoded data. The decoded data + * will be allocated, and stored into result. If the header given is + * non null this function will search for "-----BEGIN header" and + * decode only this part. Otherwise it will decode the first PEM + * packet found. + * + * You should use gnutls_free() to free the returned data. + * + * Note, that prior to GnuTLS 3.4.0 this function was available + * under the name gnutls_pem_base64_decode_alloc(). There is + * compatibility macro pointing to this function. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise + * an error code is returned. + * + * Since: 3.4.0 + **/ +int +gnutls_pem_base64_decode2(const char *header, + const gnutls_datum_t * b64_data, + gnutls_datum_t * result) +{ + int ret; + + if (result == NULL) + return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + + ret = + _gnutls_fbase64_decode(header, b64_data->data, b64_data->size, + result); + if (ret < 0) + return gnutls_assert_val(ret); + + return 0; +} + +/** + * gnutls_base64_decode2: + * @base64: contains the encoded data + * @result: the location of decoded data + * + * This function will decode the given base64 encoded data. The decoded data + * will be allocated, and stored into result. + * + * You should use gnutls_free() to free the returned data. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise + * an error code is returned. + * + * Since: 3.6.0 + **/ +int +gnutls_base64_decode2(const gnutls_datum_t *base64, + gnutls_datum_t *result) +{ + int ret; + + ret = _gnutls_base64_decode(base64->data, base64->size, result); + if (ret < 0) { + return gnutls_assert_val(ret); + } + + return 0; +} + +/** + * gnutls_base64_encode2: + * @data: contains the raw data + * @result: will hold the newly allocated encoded data + * + * This function will convert the given data to printable data, using + * the base64 encoding. This function will allocate the required + * memory to hold the encoded data. + * + * You should use gnutls_free() to free the returned data. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise + * an error code is returned. + * + * Since: 3.6.0 + **/ +int +gnutls_base64_encode2(const gnutls_datum_t *data, + gnutls_datum_t *result) +{ + int ret; + + if (result == NULL) + return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + + ret = _gnutls_fbase64_encode(NULL, data->data, data->size, result); + if (ret < 0) + return gnutls_assert_val(ret); + + return 0; +} diff --git a/lib/x509_b64.h b/lib/x509_b64.h new file mode 100644 index 0000000..271850a --- /dev/null +++ b/lib/x509_b64.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2000-2012 Free Software Foundation, Inc. + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GnuTLS. + * + * The GnuTLS is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/> + * + */ + +#ifndef GNUTLS_LIB_X509_B64_H +#define GNUTLS_LIB_X509_B64_H + +int _gnutls_fbase64_encode(const char *msg, const uint8_t * data, + size_t data_size, gnutls_datum_t * result); +int _gnutls_fbase64_decode(const char *header, const uint8_t * data, + size_t data_size, gnutls_datum_t * result); + +int +_gnutls_base64_decode(const uint8_t * data, size_t data_size, + gnutls_datum_t * result); + +#define B64SIZE( data_size) ((data_size%3==0)?((data_size*4)/3):(4+((data_size/3)*4))) + +/* The size for B64 encoding + newlines plus header + */ + +#define B64FSIZE( hsize, dsize) \ + (B64SIZE(dsize) + (hsize) + /*newlines*/ \ + B64SIZE(dsize)/64 + (((B64SIZE(dsize) % 64) > 0) ? 1 : 0)) + +#endif /* GNUTLS_LIB_X509_B64_H */ |