diff options
Diffstat (limited to 'lib/ext')
55 files changed, 12989 insertions, 0 deletions
diff --git a/lib/ext/Makefile.am b/lib/ext/Makefile.am new file mode 100644 index 0000000..81efdbc --- /dev/null +++ b/lib/ext/Makefile.am @@ -0,0 +1,63 @@ +## Process this file with automake to produce Makefile.in +# Copyright (C) 2002-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 3 of +# the License, or (at your option) any later version. +# +# The GnuTLS 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$(builddir)/../../gl \ + -I$(srcdir)/.. + +if ENABLE_MINITASN1 +AM_CPPFLAGS += -I$(srcdir)/../minitasn1 +endif + +noinst_LTLIBRARIES = libgnutls_ext.la + +libgnutls_ext_la_SOURCES = max_record.c \ + server_name.c signature.c safe_renegotiation.c \ + max_record.h server_name.h srp.h \ + session_ticket.h signature.h safe_renegotiation.h \ + session_ticket.c srp.c heartbeat.c heartbeat.h \ + status_request.h status_request.c dumbfw.c dumbfw.h \ + ext_master_secret.c ext_master_secret.h etm.h etm.c \ + supported_versions.c supported_versions.h \ + post_handshake.c post_handshake.h key_share.c key_share.h \ + cookie.c cookie.h \ + psk_ke_modes.c psk_ke_modes.h pre_shared_key.c pre_shared_key.h \ + supported_groups.c supported_groups.h \ + ec_point_formats.c ec_point_formats.h \ + early_data.c early_data.h \ + record_size_limit.c record_size_limit.h \ + client_cert_type.c client_cert_type.h \ + server_cert_type.c server_cert_type.h \ + cert_types.h \ + compress_certificate.c compress_certificate.h + +if ENABLE_ALPN +libgnutls_ext_la_SOURCES += alpn.c alpn.h +endif + +if ENABLE_DTLS_SRTP +libgnutls_ext_la_SOURCES += srtp.c srtp.h +endif diff --git a/lib/ext/Makefile.in b/lib/ext/Makefile.in new file mode 100644 index 0000000..47c4abc --- /dev/null +++ b/lib/ext/Makefile.in @@ -0,0 +1,2517 @@ +# 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) 2002-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 3 of +# the License, or (at your option) any later version. +# +# The GnuTLS 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_ALPN_TRUE@am__append_2 = alpn.c alpn.h +@ENABLE_DTLS_SRTP_TRUE@am__append_3 = srtp.c srtp.h +subdir = lib/ext +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_ext_la_LIBADD = +am__libgnutls_ext_la_SOURCES_DIST = max_record.c server_name.c \ + signature.c safe_renegotiation.c max_record.h server_name.h \ + srp.h session_ticket.h signature.h safe_renegotiation.h \ + session_ticket.c srp.c heartbeat.c heartbeat.h \ + status_request.h status_request.c dumbfw.c dumbfw.h \ + ext_master_secret.c ext_master_secret.h etm.h etm.c \ + supported_versions.c supported_versions.h post_handshake.c \ + post_handshake.h key_share.c key_share.h cookie.c cookie.h \ + psk_ke_modes.c psk_ke_modes.h pre_shared_key.c \ + pre_shared_key.h supported_groups.c supported_groups.h \ + ec_point_formats.c ec_point_formats.h early_data.c \ + early_data.h record_size_limit.c record_size_limit.h \ + client_cert_type.c client_cert_type.h server_cert_type.c \ + server_cert_type.h cert_types.h compress_certificate.c \ + compress_certificate.h alpn.c alpn.h srtp.c srtp.h +@ENABLE_ALPN_TRUE@am__objects_1 = alpn.lo +@ENABLE_DTLS_SRTP_TRUE@am__objects_2 = srtp.lo +am_libgnutls_ext_la_OBJECTS = max_record.lo server_name.lo \ + signature.lo safe_renegotiation.lo session_ticket.lo srp.lo \ + heartbeat.lo status_request.lo dumbfw.lo ext_master_secret.lo \ + etm.lo supported_versions.lo post_handshake.lo key_share.lo \ + cookie.lo psk_ke_modes.lo pre_shared_key.lo \ + supported_groups.lo ec_point_formats.lo early_data.lo \ + record_size_limit.lo client_cert_type.lo server_cert_type.lo \ + compress_certificate.lo $(am__objects_1) $(am__objects_2) +libgnutls_ext_la_OBJECTS = $(am_libgnutls_ext_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)/alpn.Plo \ + ./$(DEPDIR)/client_cert_type.Plo \ + ./$(DEPDIR)/compress_certificate.Plo ./$(DEPDIR)/cookie.Plo \ + ./$(DEPDIR)/dumbfw.Plo ./$(DEPDIR)/early_data.Plo \ + ./$(DEPDIR)/ec_point_formats.Plo ./$(DEPDIR)/etm.Plo \ + ./$(DEPDIR)/ext_master_secret.Plo ./$(DEPDIR)/heartbeat.Plo \ + ./$(DEPDIR)/key_share.Plo ./$(DEPDIR)/max_record.Plo \ + ./$(DEPDIR)/post_handshake.Plo ./$(DEPDIR)/pre_shared_key.Plo \ + ./$(DEPDIR)/psk_ke_modes.Plo ./$(DEPDIR)/record_size_limit.Plo \ + ./$(DEPDIR)/safe_renegotiation.Plo \ + ./$(DEPDIR)/server_cert_type.Plo ./$(DEPDIR)/server_name.Plo \ + ./$(DEPDIR)/session_ticket.Plo ./$(DEPDIR)/signature.Plo \ + ./$(DEPDIR)/srp.Plo ./$(DEPDIR)/srtp.Plo \ + ./$(DEPDIR)/status_request.Plo \ + ./$(DEPDIR)/supported_groups.Plo \ + ./$(DEPDIR)/supported_versions.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_ext_la_SOURCES) +DIST_SOURCES = $(am__libgnutls_ext_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$(builddir)/../../gl -I$(srcdir)/.. $(am__append_1) +noinst_LTLIBRARIES = libgnutls_ext.la +libgnutls_ext_la_SOURCES = max_record.c server_name.c signature.c \ + safe_renegotiation.c max_record.h server_name.h srp.h \ + session_ticket.h signature.h safe_renegotiation.h \ + session_ticket.c srp.c heartbeat.c heartbeat.h \ + status_request.h status_request.c dumbfw.c dumbfw.h \ + ext_master_secret.c ext_master_secret.h etm.h etm.c \ + supported_versions.c supported_versions.h post_handshake.c \ + post_handshake.h key_share.c key_share.h cookie.c cookie.h \ + psk_ke_modes.c psk_ke_modes.h pre_shared_key.c \ + pre_shared_key.h supported_groups.c supported_groups.h \ + ec_point_formats.c ec_point_formats.h early_data.c \ + early_data.h record_size_limit.c record_size_limit.h \ + client_cert_type.c client_cert_type.h server_cert_type.c \ + server_cert_type.h cert_types.h compress_certificate.c \ + compress_certificate.h $(am__append_2) $(am__append_3) +all: 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/ext/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign lib/ext/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_ext.la: $(libgnutls_ext_la_OBJECTS) $(libgnutls_ext_la_DEPENDENCIES) $(EXTRA_libgnutls_ext_la_DEPENDENCIES) + $(AM_V_CCLD)$(LINK) $(libgnutls_ext_la_OBJECTS) $(libgnutls_ext_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/alpn.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/client_cert_type.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/compress_certificate.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cookie.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dumbfw.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/early_data.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ec_point_formats.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/etm.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext_master_secret.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/heartbeat.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/key_share.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/max_record.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/post_handshake.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pre_shared_key.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/psk_ke_modes.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/record_size_limit.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/safe_renegotiation.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/server_cert_type.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/server_name.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/session_ticket.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/signature.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/srp.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/srtp.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/status_request.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/supported_groups.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/supported_versions.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: check-am +all-am: Makefile $(LTLIBRARIES) +installdirs: +install: install-am +install-exec: 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." +clean: clean-am + +clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \ + mostlyclean-am + +distclean: distclean-am + -rm -f ./$(DEPDIR)/alpn.Plo + -rm -f ./$(DEPDIR)/client_cert_type.Plo + -rm -f ./$(DEPDIR)/compress_certificate.Plo + -rm -f ./$(DEPDIR)/cookie.Plo + -rm -f ./$(DEPDIR)/dumbfw.Plo + -rm -f ./$(DEPDIR)/early_data.Plo + -rm -f ./$(DEPDIR)/ec_point_formats.Plo + -rm -f ./$(DEPDIR)/etm.Plo + -rm -f ./$(DEPDIR)/ext_master_secret.Plo + -rm -f ./$(DEPDIR)/heartbeat.Plo + -rm -f ./$(DEPDIR)/key_share.Plo + -rm -f ./$(DEPDIR)/max_record.Plo + -rm -f ./$(DEPDIR)/post_handshake.Plo + -rm -f ./$(DEPDIR)/pre_shared_key.Plo + -rm -f ./$(DEPDIR)/psk_ke_modes.Plo + -rm -f ./$(DEPDIR)/record_size_limit.Plo + -rm -f ./$(DEPDIR)/safe_renegotiation.Plo + -rm -f ./$(DEPDIR)/server_cert_type.Plo + -rm -f ./$(DEPDIR)/server_name.Plo + -rm -f ./$(DEPDIR)/session_ticket.Plo + -rm -f ./$(DEPDIR)/signature.Plo + -rm -f ./$(DEPDIR)/srp.Plo + -rm -f ./$(DEPDIR)/srtp.Plo + -rm -f ./$(DEPDIR)/status_request.Plo + -rm -f ./$(DEPDIR)/supported_groups.Plo + -rm -f ./$(DEPDIR)/supported_versions.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)/alpn.Plo + -rm -f ./$(DEPDIR)/client_cert_type.Plo + -rm -f ./$(DEPDIR)/compress_certificate.Plo + -rm -f ./$(DEPDIR)/cookie.Plo + -rm -f ./$(DEPDIR)/dumbfw.Plo + -rm -f ./$(DEPDIR)/early_data.Plo + -rm -f ./$(DEPDIR)/ec_point_formats.Plo + -rm -f ./$(DEPDIR)/etm.Plo + -rm -f ./$(DEPDIR)/ext_master_secret.Plo + -rm -f ./$(DEPDIR)/heartbeat.Plo + -rm -f ./$(DEPDIR)/key_share.Plo + -rm -f ./$(DEPDIR)/max_record.Plo + -rm -f ./$(DEPDIR)/post_handshake.Plo + -rm -f ./$(DEPDIR)/pre_shared_key.Plo + -rm -f ./$(DEPDIR)/psk_ke_modes.Plo + -rm -f ./$(DEPDIR)/record_size_limit.Plo + -rm -f ./$(DEPDIR)/safe_renegotiation.Plo + -rm -f ./$(DEPDIR)/server_cert_type.Plo + -rm -f ./$(DEPDIR)/server_name.Plo + -rm -f ./$(DEPDIR)/session_ticket.Plo + -rm -f ./$(DEPDIR)/signature.Plo + -rm -f ./$(DEPDIR)/srp.Plo + -rm -f ./$(DEPDIR)/srtp.Plo + -rm -f ./$(DEPDIR)/status_request.Plo + -rm -f ./$(DEPDIR)/supported_groups.Plo + -rm -f ./$(DEPDIR)/supported_versions.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: install-am 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 + + +# 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/ext/alpn.c b/lib/ext/alpn.c new file mode 100644 index 0000000..7cc7997 --- /dev/null +++ b/lib/ext/alpn.c @@ -0,0 +1,327 @@ +/* + * Copyright (C) 2013 Nikos Mavrogiannopoulos + * 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 "auth.h" +#include "errors.h" +#include "num.h" +#include <ext/alpn.h> + +static int _gnutls_alpn_recv_params(gnutls_session_t session, + const uint8_t * data, + size_t data_size); +static int _gnutls_alpn_send_params(gnutls_session_t session, + gnutls_buffer_st * extdata); + +static void _gnutls_alpn_deinit_data(gnutls_ext_priv_data_t priv); + + +const hello_ext_entry_st ext_mod_alpn = { + .name = "ALPN", + .tls_id = 16, + .gid = GNUTLS_EXTENSION_ALPN, + /* this extension must be parsed even on resumption */ + .client_parse_point = GNUTLS_EXT_MANDATORY, + .server_parse_point = GNUTLS_EXT_MANDATORY, + .validity = GNUTLS_EXT_FLAG_TLS | GNUTLS_EXT_FLAG_DTLS | + GNUTLS_EXT_FLAG_CLIENT_HELLO | GNUTLS_EXT_FLAG_EE | + GNUTLS_EXT_FLAG_TLS12_SERVER_HELLO, + .recv_func = _gnutls_alpn_recv_params, + .send_func = _gnutls_alpn_send_params, + .deinit_func = _gnutls_alpn_deinit_data, + .cannot_be_overriden = 1 +}; + +static int +_gnutls_alpn_recv_params(gnutls_session_t session, + const uint8_t * data, size_t data_size) +{ + unsigned int i; + int ret; + const uint8_t *p = data; + unsigned len1, len; + alpn_ext_st *priv; + gnutls_ext_priv_data_t epriv; + int selected_protocol_index; + + ret = + _gnutls_hello_ext_get_priv(session, GNUTLS_EXTENSION_ALPN, + &epriv); + if (ret < 0) + return 0; + + priv = epriv; + + DECR_LENGTH_RET(data_size, 2, GNUTLS_E_UNEXPECTED_PACKET_LENGTH); + len = _gnutls_read_uint16(p); + p += 2; + + if (len == 0 || len > (size_t)data_size) + return + gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH); + + if (session->security_parameters.entity == GNUTLS_SERVER) { + selected_protocol_index = MAX_ALPN_PROTOCOLS+1; + + while (data_size > 0) { + DECR_LENGTH_RET(data_size, 1, GNUTLS_E_UNEXPECTED_PACKET_LENGTH); + len1 = *p; + p += 1; + DECR_LENGTH_RET(data_size, len1, GNUTLS_E_UNEXPECTED_PACKET_LENGTH); + + if (len1 == 0) + return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH); + + for (i = 0; i < priv->size; i++) { + if (priv->protocol_size[i] == len1 + && memcmp(p, priv->protocols[i], + len1) == 0) { + + if (priv->flags & GNUTLS_ALPN_SERVER_PRECEDENCE) { + if (selected_protocol_index > (int)i) { + selected_protocol_index = i; + priv->selected_protocol = + priv->protocols[i]; + priv->selected_protocol_size = + priv->protocol_size[i]; + break; + } + } else { + priv->selected_protocol = + priv->protocols[i]; + priv->selected_protocol_size = + priv->protocol_size[i]; + return 0; + } + } + } + p += len1; + } + } else { + DECR_LENGTH_RET(data_size, 1, GNUTLS_E_UNEXPECTED_PACKET_LENGTH); + len1 = *p; + p += 1; + DECR_LENGTH_RET(data_size, len1, GNUTLS_E_UNEXPECTED_PACKET_LENGTH); + + for (i = 0; i < priv->size; i++) { + if (priv->protocol_size[i] == len1 + && memcmp(p, priv->protocols[i], len1) == 0) { + priv->selected_protocol = + priv->protocols[i]; + priv->selected_protocol_size = + priv->protocol_size[i]; + break; + } + } + /*p += len1;*/ + } + + if (priv->selected_protocol == NULL + && (priv->flags & GNUTLS_ALPN_MAND)) + return gnutls_assert_val(GNUTLS_E_NO_APPLICATION_PROTOCOL); + + return 0; +} + +static int +_gnutls_alpn_send_params(gnutls_session_t session, + gnutls_buffer_st * extdata) +{ + unsigned i; + int total_size = 0, ret; + alpn_ext_st *priv; + gnutls_ext_priv_data_t epriv; + + ret = + _gnutls_hello_ext_get_priv(session, GNUTLS_EXTENSION_ALPN, + &epriv); + if (ret < 0) + return 0; + + priv = epriv; + + if (priv->size == 0) + return 0; + + if (session->security_parameters.entity == GNUTLS_SERVER) { + if (priv->selected_protocol_size == 0) + return 0; + + ret = + _gnutls_buffer_append_prefix(extdata, 16, + priv-> + selected_protocol_size + + 1); + if (ret < 0) + return gnutls_assert_val(ret); + + total_size += 2; + + ret = + _gnutls_buffer_append_data_prefix(extdata, 8, + priv-> + selected_protocol, + priv-> + selected_protocol_size); + if (ret < 0) + return gnutls_assert_val(ret); + + total_size += 1 + priv->selected_protocol_size; + } else { + int t = 0; + for (i = 0; i < priv->size; i++) + t += priv->protocol_size[i] + 1; + + ret = _gnutls_buffer_append_prefix(extdata, 16, t); + if (ret < 0) + return gnutls_assert_val(ret); + + total_size += 2; + + for (i = 0; i < priv->size; i++) { + ret = + _gnutls_buffer_append_data_prefix(extdata, 8, + priv-> + protocols[i], + priv-> + protocol_size + [i]); + if (ret < 0) + return gnutls_assert_val(ret); + + total_size += 1 + priv->protocol_size[i]; + } + } + + return total_size; +} + +/** + * gnutls_alpn_get_selected_protocol: + * @session: is a #gnutls_session_t type. + * @protocol: will hold the protocol name + * + * This function allows you to get the negotiated protocol name. The + * returned protocol should be treated as opaque, constant value and + * only valid during the session life. + * + * The selected protocol is the first supported by the list sent + * by the client. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, + * otherwise a negative error code is returned. + * + * Since 3.2.0 + **/ +int +gnutls_alpn_get_selected_protocol(gnutls_session_t session, + gnutls_datum_t * protocol) +{ + alpn_ext_st *priv; + int ret; + gnutls_ext_priv_data_t epriv; + + ret = + _gnutls_hello_ext_get_priv(session, GNUTLS_EXTENSION_ALPN, + &epriv); + if (ret < 0) { + gnutls_assert(); + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + } + + priv = epriv; + + if (priv->selected_protocol_size == 0) + return + gnutls_assert_val + (GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE); + + protocol->data = priv->selected_protocol; + protocol->size = priv->selected_protocol_size; + + return 0; +} + +/** + * gnutls_alpn_set_protocols: + * @session: is a #gnutls_session_t type. + * @protocols: is the protocol names to add. + * @protocols_size: the number of protocols to add. + * @flags: zero or a sequence of %gnutls_alpn_flags_t + * + * This function is to be used by both clients and servers, to declare + * the supported ALPN protocols, which are used during negotiation with peer. + * + * See %gnutls_alpn_flags_t description for the documentation of available + * flags. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, + * otherwise a negative error code is returned. + * + * Since 3.2.0 + **/ +int +gnutls_alpn_set_protocols(gnutls_session_t session, + const gnutls_datum_t * protocols, + unsigned protocols_size, unsigned int flags) +{ + int ret; + alpn_ext_st *priv; + gnutls_ext_priv_data_t epriv; + unsigned i; + + ret = + _gnutls_hello_ext_get_priv(session, GNUTLS_EXTENSION_ALPN, + &epriv); + if (ret < 0) { + priv = gnutls_calloc(1, sizeof(*priv)); + if (priv == NULL) { + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; + } + epriv = priv; + _gnutls_hello_ext_set_priv(session, + GNUTLS_EXTENSION_ALPN, epriv); + } else + priv = epriv; + + if (protocols_size > MAX_ALPN_PROTOCOLS) + return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + + for (i = 0; i < protocols_size; i++) { + if (protocols[i].size >= MAX_ALPN_PROTOCOL_NAME) + return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + + memcpy(priv->protocols[i], protocols[i].data, + protocols[i].size); + priv->protocol_size[i] = protocols[i].size; + priv->size++; + } + priv->flags = flags; + + return 0; +} + + +static void _gnutls_alpn_deinit_data(gnutls_ext_priv_data_t priv) +{ + gnutls_free(priv); +} diff --git a/lib/ext/alpn.h b/lib/ext/alpn.h new file mode 100644 index 0000000..0b32a32 --- /dev/null +++ b/lib/ext/alpn.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2013 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_EXT_ALPN_H +#define GNUTLS_LIB_EXT_ALPN_H + +#include <hello_ext.h> + +#define MAX_ALPN_PROTOCOLS 8 +#define MAX_ALPN_PROTOCOL_NAME 32 + +typedef struct { + uint8_t protocols[MAX_ALPN_PROTOCOLS][MAX_ALPN_PROTOCOL_NAME]; + unsigned protocol_size[MAX_ALPN_PROTOCOLS]; + unsigned size; + uint8_t *selected_protocol; + unsigned selected_protocol_size; + unsigned flags; +} alpn_ext_st; + +extern const hello_ext_entry_st ext_mod_alpn; + +#endif /* GNUTLS_LIB_EXT_ALPN_H */ diff --git a/lib/ext/cert_types.h b/lib/ext/cert_types.h new file mode 100644 index 0000000..98dfdef --- /dev/null +++ b/lib/ext/cert_types.h @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2018 ARPA2 project + * + * Author: Tom Vrancken (dev@tomvrancken.nl) + * + * 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 provides common functionality for certificate type + * handling during TLS hello extensions. + * + */ + +#ifndef GNUTLS_LIB_EXT_CERT_TYPES_H +#define GNUTLS_LIB_EXT_CERT_TYPES_H + +/* Maps IANA TLS Certificate Types identifiers to internal + * certificate type representation. + */ +static inline gnutls_certificate_type_t IANA2cert_type(int num) +{ + switch (num) { + case 0: + return GNUTLS_CRT_X509; + case 2: + return GNUTLS_CRT_RAWPK; + default: + return GNUTLS_CRT_UNKNOWN; + } +} + +/* Maps internal certificate type representation to + * IANA TLS Certificate Types identifiers. + */ +static inline int cert_type2IANA(gnutls_certificate_type_t cert_type) +{ + switch (cert_type) { + case GNUTLS_CRT_X509: + return 0; + case GNUTLS_CRT_RAWPK: + return 2; + default: + return GNUTLS_E_UNSUPPORTED_CERTIFICATE_TYPE; + } +} + +/* Checks whether the given cert type is enabled in the application + */ +static inline bool is_cert_type_enabled(gnutls_session_t session, gnutls_certificate_type_t cert_type) +{ + switch(cert_type) { + case GNUTLS_CRT_X509: + // Default cert type, always enabled + return true; + case GNUTLS_CRT_RAWPK: + return session->internals.flags & GNUTLS_ENABLE_RAWPK; + default: + // When not explicitly supported here disable it + return false; + } +} + +/* Checks whether alternative cert types (i.e. other than X.509) + * are enabled in the application + */ +static inline bool are_alternative_cert_types_allowed(gnutls_session_t session) +{ + // OR-ed list of defined cert type init flags + #define CERT_TYPES_FLAGS GNUTLS_ENABLE_RAWPK + + return session->internals.flags & CERT_TYPES_FLAGS; + + #undef CERT_TYPES_FLAGS +} + +#endif /* GNUTLS_LIB_EXT_CERT_TYPES_H */ diff --git a/lib/ext/client_cert_type.c b/lib/ext/client_cert_type.c new file mode 100644 index 0000000..261b56c --- /dev/null +++ b/lib/ext/client_cert_type.c @@ -0,0 +1,393 @@ +/* + * Copyright (C) 2016 - 2018 ARPA2 project + * + * Author: Tom Vrancken (dev@tomvrancken.nl) + * + * 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 is part of the client_certificate_type extension as + * defined in RFC7250 (https://tools.ietf.org/html/rfc7250). + * + * The client_certificate_type extension in the client hello indicates + * the certificate types the client is able to provide to the server, + * when requested using a certificate_request message. + */ + +#include "gnutls_int.h" +#include <gnutls/gnutls.h> +#include "ext/cert_types.h" +#include "ext/client_cert_type.h" +#include "hello_ext.h" +#include "hello_ext_lib.h" +#include "errors.h" +#include "state.h" +#include "datum.h" + + +static int _gnutls_client_cert_type_recv_params(gnutls_session_t session, + const uint8_t* data, + size_t data_size); +static int _gnutls_client_cert_type_send_params(gnutls_session_t session, + gnutls_buffer_st* data); + + +const hello_ext_entry_st ext_mod_client_cert_type = { + .name = "Client Certificate Type", + .tls_id = 19, + .gid = GNUTLS_EXTENSION_CLIENT_CERT_TYPE, + .client_parse_point = GNUTLS_EXT_TLS, + .server_parse_point = GNUTLS_EXT_TLS, + .validity = GNUTLS_EXT_FLAG_TLS | + GNUTLS_EXT_FLAG_DTLS | + GNUTLS_EXT_FLAG_CLIENT_HELLO | + GNUTLS_EXT_FLAG_TLS12_SERVER_HELLO | + GNUTLS_EXT_FLAG_EE, + .recv_func = _gnutls_client_cert_type_recv_params, + .send_func = _gnutls_client_cert_type_send_params, + .pack_func = _gnutls_hello_ext_default_pack, + .unpack_func = _gnutls_hello_ext_default_unpack, + .deinit_func = _gnutls_hello_ext_default_deinit, + .cannot_be_overriden = 1 +}; + + +static int _gnutls_client_cert_type_recv_params(gnutls_session_t session, + const uint8_t* data, + size_t data_size) +{ + int ret; + gnutls_certificate_type_t cert_type; + size_t i; + bool found = false; + const uint8_t* pdata = data; + + /* Only activate this extension if we have cert credentials set + * and alternative cert types are allowed */ + if (!are_alternative_cert_types_allowed(session) || + (_gnutls_get_cred(session, GNUTLS_CRD_CERTIFICATE) == NULL)) + return 0; + + if (!IS_SERVER(session)) { // client mode + gnutls_datum_t sent_cert_types; // Holds the previously sent cert types + + /* Compare packet length with expected packet length. For the + * client this is a single byte. */ + if (data_size != 1) { + return + gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH); + } + + /* The server picked one of the offered cert types if he supports + * at least one of them and decided to do a client certificate + * request. If both parties play by the rules then we may only + * receive a cert type that we offered, i.e. one that we support. + * Because the world isn't as beautiful as it may seem, we're going + * to check it nevertheless. */ + cert_type = IANA2cert_type(pdata[0]); + + _gnutls_handshake_log("EXT[%p]: Received a %s client certificate type confirmation from the server.\n", + session, gnutls_certificate_type_get_name(cert_type)); + + // Check validity of cert type + if (cert_type == GNUTLS_CRT_UNKNOWN) { + return gnutls_assert_val(GNUTLS_E_UNSUPPORTED_CERTIFICATE_TYPE); + } + + /* Get the cert types that we sent to the server (they were stored + * in IANA representation. + */ + ret = _gnutls_hello_ext_get_datum(session, + GNUTLS_EXTENSION_CLIENT_CERT_TYPE, + &sent_cert_types); + if (ret < 0) { + /* This should not happen and indicate a memory corruption! + * Assertion are always on in production code so execution + * will halt here. */ + assert(false); + } + + // Check whether what we got back is actually offered by us + for (i = 0; i < sent_cert_types.size; i++) { + if (IANA2cert_type(sent_cert_types.data[i]) == cert_type) + found = true; + } + + if (found) { + // Everything OK, now set the client certificate type + _gnutls_session_client_cert_type_set(session, cert_type); + ret = GNUTLS_E_SUCCESS; + } else { + // No valid cert type found + ret = GNUTLS_E_UNSUPPORTED_CERTIFICATE_TYPE; + } + + return ret; + + } else { // server mode + gnutls_datum_t cert_types; // Holds the received cert types + + // Compare packet length with expected packet length. + DECR_LEN(data_size, 1); + if (data[0] != data_size) { + return + gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH); + } + pdata += 1; + + // Assign the contents of our data buffer to a gnutls_datum_t + cert_types.data = (uint8_t*)pdata; // Need casting to get rid of 'discards const qualifier' warning + cert_types.size = data_size; + + // Store the client certificate types in our session + _gnutls_hello_ext_set_datum(session, + GNUTLS_EXTENSION_CLIENT_CERT_TYPE, + &cert_types); + + /* We receive a list of supported certificate types that the client + * is able to provide when requested via a client certificate + * request. This list is sorted by order of preference. We now check + * in this order of preference whether we support any of these + * certificate types. + */ + for (i = 0; i < cert_types.size; i++) { + // Convert to internal representation + cert_type = IANA2cert_type(cert_types.data[i]); + + // If we have an invalid cert id then continue to the next + if (cert_type == GNUTLS_CRT_UNKNOWN) + continue; + + _gnutls_handshake_log("EXT[%p]: Checking compatibility of a %s client certificate type that was received from the client.\n", + session, gnutls_certificate_type_get_name(cert_type)); + + // Check for support of this cert type + if (_gnutls_session_is_cert_type_supported(session, cert_type, false, GNUTLS_CTYPE_CLIENT) == 0) { + found = true; + break; + } + } + + // We found a matching ctype, we pick this one + if (found) { + _gnutls_session_client_cert_type_set(session, cert_type); + ret = GNUTLS_E_SUCCESS; + } else { + /* If no supported certificate type can be found we terminate + * with a fatal alert of type "unsupported_certificate" + * (according to specification rfc7250). + */ + _gnutls_handshake_log + ("EXT[%p]: No supported client certificate type was found. " + "Aborting connection.\n", session); + ret = GNUTLS_E_UNSUPPORTED_CERTIFICATE_TYPE; + } + + return ret; + } +} + +static int _gnutls_client_cert_type_send_params(gnutls_session_t session, + gnutls_buffer_st* data) +{ + int ret; + uint8_t cert_type_IANA; // Holds an IANA cert type ID + gnutls_certificate_type_t cert_type; + + /* Only activate this extension if we have cert credentials set + * and alternative cert types are allowed */ + if (!are_alternative_cert_types_allowed(session) || + (_gnutls_get_cred(session, GNUTLS_CRD_CERTIFICATE) == NULL)) + return 0; + + if (!IS_SERVER(session)) { // Client mode + uint8_t cert_types[GNUTLS_CRT_MAX]; // The list with supported (IANA) cert types. Inv: 0 <= cert type Id < 256 + size_t i, num_cert_types = 0; + priority_st* cert_priorities; + gnutls_datum_t tmp_cert_types; // For type conversion + + // For brevity + cert_priorities = + &session->internals.priorities->client_ctype; + + /* Retrieve client certificate type priorities if any. If no + * priorities are set then the default client certificate type + * initialization values apply. This default is currently set to + * x.509 in which case we don't enable this extension. + */ + if (cert_priorities->num_priorities > 0) { // Priorities are explicitly set + /* If the certificate priority is explicitly set to only + * X.509 (default) then, according to spec we don't send + * this extension. We check this here to avoid further work in + * this routine. We also check it below after pruning supported + * types. + */ + if (cert_priorities->num_priorities == 1 && + cert_priorities->priorities[0] == DEFAULT_CERT_TYPE) { + _gnutls_handshake_log + ("EXT[%p]: Client certificate type was set to default cert type (%s). " + "We therefore do not send this extension.\n", + session, + gnutls_certificate_type_get_name(DEFAULT_CERT_TYPE)); + + // Explicitly set but default ctype, so don't send anything + return 0; + } + + /* We are only allowed to send certificate types that we support, + * i.e. have credentials for. Therefore we check this here and + * prune our original list. + */ + for (i = 0; i < cert_priorities->num_priorities; i++) { + cert_type = cert_priorities->priorities[i]; + + if (_gnutls_session_is_cert_type_supported(session, cert_type, + true, GNUTLS_CTYPE_CLIENT) == 0) { + /* Check whether we are allowed to store another cert type + * in our buffer. In other words, prevent a possible buffer + * overflow. This situation can occur when a user sets + * duplicate cert types in the priority strings. */ + if (num_cert_types >= GNUTLS_CRT_MAX) + return gnutls_assert_val(GNUTLS_E_SHORT_MEMORY_BUFFER); + + // Convert to IANA representation + ret = cert_type2IANA(cert_type); + + if (ret < 0) + return gnutls_assert_val(ret); + + cert_type_IANA = ret; // For readability + + // Add this cert type to our list with supported types + cert_types[num_cert_types] = cert_type_IANA; + num_cert_types++; + + _gnutls_handshake_log + ("EXT[%p]: Client certificate type %s (%d) was queued.\n", + session, + gnutls_certificate_type_get_name(cert_type), + cert_type_IANA); + } + } + + /* Check whether there are any supported certificate types left + * after the previous pruning step. If not, we do not send this + * extension. Also, if the only supported type is the default type + * we do not send this extension (according to RFC7250). + */ + if (num_cert_types == 0) { + _gnutls_handshake_log + ("EXT[%p]: Client certificate types were set but none of them is supported. " + "You might want to check your credentials or your priorities. " + "We do not send this extension.\n", + session); + + return 0; + } else if (num_cert_types == 1 && + IANA2cert_type(cert_types[0]) == DEFAULT_CERT_TYPE) { + _gnutls_handshake_log + ("EXT[%p]: The only supported client certificate type is (%s) which is the default. " + "We therefore do not send this extension.\n", + session, + gnutls_certificate_type_get_name(DEFAULT_CERT_TYPE)); + + return 0; + } + + /* We have data to send and store a copy internally. We convert + * our list with supported cert types to a datum_t in order to + * be able to make the ..._set_datum call. + */ + tmp_cert_types.data = cert_types; + tmp_cert_types.size = num_cert_types; + + _gnutls_hello_ext_set_datum(session, + GNUTLS_EXTENSION_CLIENT_CERT_TYPE, + &tmp_cert_types); + + /* Serialize the certificate types into a sequence of octets + * uint8: length of sequence of cert types (1 octet) + * uint8: cert types (0 <= #octets <= 255) + */ + ret = _gnutls_buffer_append_data_prefix(data, 8, + cert_types, + num_cert_types); + + // Check for errors + if (ret < 0) { + return gnutls_assert_val(ret); + } else { + // Number of bytes we are sending + return num_cert_types + 1; + } + } + } else { // Server mode + const version_entry_st* vers = get_version(session); + /* TLS 1.2: + * Check whether we are going to send a certificate request, + * otherwise omit the response. This is conform spec. + * (RFC7250, 4.2 case 3.). + * + * TLS 1.3: + * TLS 1.3 supports post-handshake authentication for the client. + * It means that a server can ask for a client certificate anytime + * after the handshake. In order for this to work we must always + * complete the certificate type negotiation and therefore respond + * with a cert type message. + */ + if (session->internals.send_cert_req != 0 || + vers->tls13_sem) { + /* Retrieve negotiated client certificate type and send it to + * the client. + * The scenario where we want to send a certificate request but + * do not have a matching certificate does not occur because we + * already terminate the connection at reception of this extension + * when we cannot find a matching client certificate. This is conform + * spec (RFC7250, 4.2 case 2.). + */ + cert_type = get_certificate_type(session, GNUTLS_CTYPE_CLIENT); + ret = cert_type2IANA(cert_type); + + if (ret < 0) + return gnutls_assert_val(ret); + + cert_type_IANA = ret; // For readability + + _gnutls_handshake_log("EXT[%p]: Confirming to use a %s client certificate type.\n", + session, gnutls_certificate_type_get_name(cert_type)); + + ret = gnutls_buffer_append_data(data, &cert_type_IANA, 1); + + if (ret < 0) + return gnutls_assert_val(ret); + + return 1; // sent one byte + } + } + + // In all other cases don't enable this extension + return 0; +} + + +/** Extension interface **/ + +/* The interface is defined in state.c: + * Public: + * - gnutls_certificate_type_get2 + * + * Private: + * - _gnutls_session_client_cert_type_set + */ diff --git a/lib/ext/client_cert_type.h b/lib/ext/client_cert_type.h new file mode 100644 index 0000000..7e89ca8 --- /dev/null +++ b/lib/ext/client_cert_type.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2016 - 2018 ARPA2 project + * + * Author: Tom Vrancken (dev@tomvrancken.nl) + * + * 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 is part of the client_certificate_type extension as + * defined in RFC7250 (https://tools.ietf.org/html/rfc7250). + * + * The client_certificate_type extension in the client hello indicates + * the certificate types the client is able to provide to the server, + * when requested using a certificate_request message. + */ + +#ifndef GNUTLS_LIB_EXT_CLIENT_CERT_TYPE_H +#define GNUTLS_LIB_EXT_CLIENT_CERT_TYPE_H + +#include <hello_ext.h> + +extern const hello_ext_entry_st ext_mod_client_cert_type; + +#endif /* GNUTLS_LIB_EXT_CLIENT_CERT_TYPE_H */ diff --git a/lib/ext/compress_certificate.c b/lib/ext/compress_certificate.c new file mode 100644 index 0000000..8f8d75c --- /dev/null +++ b/lib/ext/compress_certificate.c @@ -0,0 +1,252 @@ +/* + * Copyright (C) 2022 Red Hat, Inc. + * + * Author: Zoltan Fridrich + * + * 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 "errors.h" +#include "gnutls_int.h" +#include "hello_ext_lib.h" +#include "num.h" +#include <ext/compress_certificate.h> + +/* Check whether certificate compression method is valid, ie. supported by gnutls */ +static inline int +is_valid_method(gnutls_compression_method_t method) +{ + switch (method) { +#ifdef HAVE_LIBZ + case GNUTLS_COMP_ZLIB: + return 1; +#endif +#ifdef HAVE_LIBBROTLI + case GNUTLS_COMP_BROTLI: + return 1; +#endif +#ifdef HAVE_LIBZSTD + case GNUTLS_COMP_ZSTD: + return 1; +#endif + default: + return 0; + } +} + +/* Converts compression algorithm number established in RFC8879 to internal compression method type */ +gnutls_compression_method_t +_gnutls_compress_certificate_num2method(uint16_t num) +{ + switch (num) { + case 1: + return GNUTLS_COMP_ZLIB; + case 2: + return GNUTLS_COMP_BROTLI; + case 3: + return GNUTLS_COMP_ZSTD; + default: + return GNUTLS_COMP_UNKNOWN; + } +} + +/* Converts compression method type to compression algorithm number established in RFC8879 */ +int +_gnutls_compress_certificate_method2num(gnutls_compression_method_t method) +{ + switch (method) { + case GNUTLS_COMP_ZLIB: + return 1; + case GNUTLS_COMP_BROTLI: + return 2; + case GNUTLS_COMP_ZSTD: + return 3; + default: + return GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER; + } +} + +/** + * gnutls_compress_certificate_get_selected_method: + * @session: is a #gnutls_session_t type. + * + * This function returns the certificate compression method that has been + * selected to compress the certificate before sending it to the peer. + * The selection is done based on the local list of supported compression + * methods and the peer's requested compression methods. + * + * Returns: selected certificate compression method. + * + * Since 3.7.4 + **/ +gnutls_compression_method_t +gnutls_compress_certificate_get_selected_method(gnutls_session_t session) +{ + return session->internals.compress_certificate_method; +} + +/** + * gnutls_compress_certificate_set_methods: + * @session: is a #gnutls_session_t type. + * @methods: is a list of supported compression methods. + * @methods_len: number of compression methods in @methods + * + * This function sets the supported compression methods for certificate compression + * for the given session. The list of supported compression methods will be used + * for a) requesting the compression of peer's certificate and b) selecting the + * method to compress the local certificate before sending it to the peer. + * The order of compression methods inside the list does matter as the method + * that appears earlier in the list will be preffered before the later ones. + * Note that even if you set the list of supported compression methods, the + * compression might not be used if the peer does not support any of your chosen + * compression methods. + * + * The list of supported compression methods must meet the following criteria: + * Argument @methods must be an array of valid compression methods of type + * #gnutls_compression_method_t. Argument @methods_len must contain the number of + * compression methods stored in the @methods array and must be within range <1, 127>. + * The length constraints are defined by %MIN_COMPRESS_CERTIFICATE_METHODS + * and %MAX_COMPRESS_CERTIFICATE_METHODS macros located in the header file + * compress_certificate.h. + * + * If either @methods or @methods_len is equal to 0, current list of supported + * compression methods will be unset. + * + * Returns: %GNUTLS_E_SUCCESS on success, otherwise a negative error code. + * + * Since 3.7.4 + **/ +int +gnutls_compress_certificate_set_methods(gnutls_session_t session, + const gnutls_compression_method_t * methods, + size_t methods_len) +{ + unsigned i; + compress_certificate_ext_st *priv; + + if (methods == NULL || methods_len == 0) { + _gnutls_hello_ext_unset_priv(session, GNUTLS_EXTENSION_COMPRESS_CERTIFICATE); + return 0; + } + + if (methods_len > MAX_COMPRESS_CERTIFICATE_METHODS) + return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + + for (i = 0; i < methods_len; ++i) + if (!is_valid_method(methods[i])) + return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + + priv = gnutls_malloc(sizeof(*priv)); + if (priv == NULL) + return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + + priv->methods_len = methods_len; + memcpy(priv->methods, methods, methods_len * sizeof(*methods)); + _gnutls_hello_ext_set_priv(session, GNUTLS_EXTENSION_COMPRESS_CERTIFICATE, priv); + + return 0; +} + +int +_gnutls_compress_certificate_recv_params(gnutls_session_t session, + const uint8_t * data, + size_t data_size) +{ + int ret; + unsigned i, j; + uint16_t num; + uint8_t bytes_len; + size_t methods_len; + gnutls_compression_method_t methods[MAX_COMPRESS_CERTIFICATE_METHODS]; + gnutls_compression_method_t method = GNUTLS_COMP_UNKNOWN; + compress_certificate_ext_st *priv; + gnutls_ext_priv_data_t epriv; + + ret = _gnutls_hello_ext_get_priv(session, GNUTLS_EXTENSION_COMPRESS_CERTIFICATE, &epriv); + if (ret < 0) + return 0; + priv = epriv; + + DECR_LEN(data_size, 1); + bytes_len = *data; + + if (bytes_len < 2 || bytes_len > 254 || bytes_len % 2 == 1) + return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER); + + DECR_LEN(data_size, bytes_len); + methods_len = bytes_len / 2; + + for (i = 0; i < methods_len; ++i) { + num = _gnutls_read_uint16(data + i + i + 1); + methods[i] = _gnutls_compress_certificate_num2method(num); + if (methods[i] == GNUTLS_COMP_UNKNOWN) + return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER); + } + + for (i = 0; i < methods_len; ++i) + for (j = 0; j < priv->methods_len; ++j) + if (methods[i] == priv->methods[j]) { + method = methods[i]; + goto endloop; + } +endloop: + session->internals.compress_certificate_method = method; + + return 0; +} + +int +_gnutls_compress_certificate_send_params(gnutls_session_t session, + gnutls_buffer_st * data) +{ + int ret, num; + unsigned i; + uint8_t bytes_len; + uint8_t bytes[2 * MAX_COMPRESS_CERTIFICATE_METHODS]; + compress_certificate_ext_st *priv; + gnutls_ext_priv_data_t epriv; + + ret = _gnutls_hello_ext_get_priv(session, GNUTLS_EXTENSION_COMPRESS_CERTIFICATE, &epriv); + if (ret < 0) + return 0; + priv = epriv; + + bytes_len = 2 * priv->methods_len; + for (i = 0; i < priv->methods_len; ++i) { + num = _gnutls_compress_certificate_method2num(priv->methods[i]); + _gnutls_write_uint16(num, bytes + i + i); + } + + ret = _gnutls_buffer_append_data_prefix(data, 8, bytes, bytes_len); + if (ret < 0) + return gnutls_assert_val(ret); + + return bytes_len + 1; +} + +const hello_ext_entry_st ext_mod_compress_certificate = { + .name = "Compress Certificate", + .tls_id = 27, + .gid = GNUTLS_EXTENSION_COMPRESS_CERTIFICATE, + .client_parse_point = GNUTLS_EXT_TLS, + .server_parse_point = GNUTLS_EXT_TLS, + .validity = GNUTLS_EXT_FLAG_TLS | GNUTLS_EXT_FLAG_DTLS | + GNUTLS_EXT_FLAG_CLIENT_HELLO, + .recv_func = _gnutls_compress_certificate_recv_params, + .send_func = _gnutls_compress_certificate_send_params, + .deinit_func = _gnutls_hello_ext_default_deinit +}; diff --git a/lib/ext/compress_certificate.h b/lib/ext/compress_certificate.h new file mode 100644 index 0000000..b708374 --- /dev/null +++ b/lib/ext/compress_certificate.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2022 Red Hat, Inc. + * + * Author: Zoltan Fridrich + * + * 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_EXT_COMPRESS_CERTIFICATE_H +#define GNUTLS_LIB_EXT_COMPRESS_CERTIFICATE_H + +#include <hello_ext.h> + +#define MIN_COMPRESS_CERTIFICATE_METHODS 1 +#define MAX_COMPRESS_CERTIFICATE_METHODS 127 + +typedef struct { + gnutls_compression_method_t methods[MAX_COMPRESS_CERTIFICATE_METHODS]; + size_t methods_len; +} compress_certificate_ext_st; + +extern const hello_ext_entry_st ext_mod_compress_certificate; + +gnutls_compression_method_t _gnutls_compress_certificate_num2method(uint16_t num); +int _gnutls_compress_certificate_method2num(gnutls_compression_method_t method); + +int +_gnutls_compress_certificate_recv_params(gnutls_session_t session, + const uint8_t * data, + size_t data_size); +int +_gnutls_compress_certificate_send_params(gnutls_session_t session, + gnutls_buffer_st * data); + +#endif /* GNUTLS_LIB_EXT_COMPRESS_CERTIFICATE_H */ diff --git a/lib/ext/cookie.c b/lib/ext/cookie.c new file mode 100644 index 0000000..b4608f3 --- /dev/null +++ b/lib/ext/cookie.c @@ -0,0 +1,117 @@ +/* + * 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/> + * + */ + +/* This file contains the code for the Max Record Size TLS extension. + */ + +#include "gnutls_int.h" +#include "errors.h" +#include "num.h" +#include "hello_ext_lib.h" +#include <ext/cookie.h> + +static int cookie_recv_params(gnutls_session_t session, + const uint8_t * data, + size_t data_size); +static int cookie_send_params(gnutls_session_t session, + gnutls_buffer_st * extdata); + +const hello_ext_entry_st ext_mod_cookie = { + .name = "Cookie", + .tls_id = 44, + .gid = GNUTLS_EXTENSION_COOKIE, + .validity = GNUTLS_EXT_FLAG_TLS | GNUTLS_EXT_FLAG_CLIENT_HELLO | + GNUTLS_EXT_FLAG_HRR | GNUTLS_EXT_FLAG_IGNORE_CLIENT_REQUEST, + .client_parse_point = GNUTLS_EXT_MANDATORY, /* force parsing prior to EXT_TLS extensions */ + .server_parse_point = GNUTLS_EXT_MANDATORY, /* force parsing prior to EXT_TLS extensions */ + .recv_func = cookie_recv_params, + .send_func = cookie_send_params, + .pack_func = NULL, + .unpack_func = NULL, + .deinit_func = _gnutls_hello_ext_default_deinit, + .cannot_be_overriden = 0 +}; + +/* Only client sends this extension. */ +static int +cookie_recv_params(gnutls_session_t session, + const uint8_t * data, size_t data_size) +{ + size_t csize; + int ret; + gnutls_datum_t tmp; + + if (session->security_parameters.entity == GNUTLS_SERVER) { + /* we don't support it */ + return 0; + } else { /* client */ + if (_gnutls_ext_get_msg(session) == GNUTLS_EXT_FLAG_HRR) { + DECR_LEN(data_size, 2); + + csize = _gnutls_read_uint16(data); + data += 2; + + DECR_LEN(data_size, csize); + + if (data_size != 0) + return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH); + + tmp.data = (void*)data; + tmp.size = csize; + + ret = _gnutls_hello_ext_set_datum(session, GNUTLS_EXTENSION_COOKIE, &tmp); + if (ret < 0) + return gnutls_assert_val(ret); + + return 0; + } + + return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_EXTENSION); + } + + return 0; +} + +/* returns data_size or a negative number on failure + */ +static int +cookie_send_params(gnutls_session_t session, + gnutls_buffer_st * extdata) +{ + gnutls_datum_t tmp; + int ret; + + /* this function sends the client extension data (dnsname) */ + if (session->security_parameters.entity == GNUTLS_CLIENT) { + ret = _gnutls_hello_ext_get_datum(session, GNUTLS_EXTENSION_COOKIE, &tmp); + if (ret < 0) + return 0; + + ret = _gnutls_buffer_append_data_prefix(extdata, 16, tmp.data, tmp.size); + if (ret < 0) + return gnutls_assert_val(ret); + + return 0; + } + + return 0; +} diff --git a/lib/ext/cookie.h b/lib/ext/cookie.h new file mode 100644 index 0000000..13ea7c7 --- /dev/null +++ b/lib/ext/cookie.h @@ -0,0 +1,30 @@ +/* + * 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_EXT_COOKIE_H +#define GNUTLS_LIB_EXT_COOKIE_H + +#include <hello_ext.h> + +extern const hello_ext_entry_st ext_mod_cookie; + +#endif /* GNUTLS_LIB_EXT_COOKIE_H */ diff --git a/lib/ext/dumbfw.c b/lib/ext/dumbfw.c new file mode 100644 index 0000000..dfd2ee0 --- /dev/null +++ b/lib/ext/dumbfw.c @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2013-2018 Nikos Mavrogiannopoulos + * Copyright (C) 2018 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 "auth.h" +#include "errors.h" +#include "num.h" +#include <ext/dumbfw.h> + +/* This extension adds additional padding data in the TLS client hello. + * There is an issue with some firewalls [0] rejecting TLS client hello + * data that are between 256 and 511 bytes, and this extension will + * make sure that client hello isn't in this range. + * + * [0]. https://www.ietf.org/mail-archive/web/tls/current/msg10423.html + */ + +static int _gnutls_dumbfw_send_params(gnutls_session_t session, + gnutls_buffer_st * extdata); + +const hello_ext_entry_st ext_mod_dumbfw = { + .name = "ClientHello Padding", + .tls_id = 21, + .gid = GNUTLS_EXTENSION_DUMBFW, + .client_parse_point = GNUTLS_EXT_APPLICATION, + .server_parse_point = GNUTLS_EXT_APPLICATION, + .validity = GNUTLS_EXT_FLAG_TLS | GNUTLS_EXT_FLAG_CLIENT_HELLO, + .recv_func = NULL, + .send_func = _gnutls_dumbfw_send_params, + .pack_func = NULL, + .unpack_func = NULL, + .deinit_func = NULL, + .cannot_be_overriden = 0 +}; + +static int +_gnutls_dumbfw_send_params(gnutls_session_t session, + gnutls_buffer_st * extdata) +{ + int total_size = 0, ret; + uint8_t pad[257]; + unsigned pad_size; + ssize_t len = extdata->length - sizeof(mbuffer_st); + + if (session->security_parameters.entity == GNUTLS_SERVER || + session->internals.dumbfw == 0 || + IS_DTLS(session) != 0 || + (len < 256 || len >= 512)) { + return 0; + } else { + /* 256 <= extdata->length < 512 */ + pad_size = 512 - len; + memset(pad, 0, pad_size); + + ret = + gnutls_buffer_append_data(extdata, pad, + pad_size); + if (ret < 0) + return gnutls_assert_val(ret); + + total_size += pad_size; + } + + return total_size; +} + diff --git a/lib/ext/dumbfw.h b/lib/ext/dumbfw.h new file mode 100644 index 0000000..87bb072 --- /dev/null +++ b/lib/ext/dumbfw.h @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2013 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_EXT_DUMBFW_H +#define GNUTLS_LIB_EXT_DUMBFW_H + +#include <hello_ext.h> + +extern const hello_ext_entry_st ext_mod_dumbfw; + +#endif /* GNUTLS_LIB_EXT_DUMBFW_H */ diff --git a/lib/ext/early_data.c b/lib/ext/early_data.c new file mode 100644 index 0000000..28c3182 --- /dev/null +++ b/lib/ext/early_data.c @@ -0,0 +1,147 @@ +/* + * Copyright (C) 2018 Red Hat, Inc. + * + * Author: 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/> + * + */ + +/* This file contains the code for the Early Data TLS 1.3 extension. + */ + +#include "gnutls_int.h" +#include "errors.h" +#include "num.h" +#include "hello_ext_lib.h" +#include <ext/early_data.h> + +static int early_data_recv_params(gnutls_session_t session, + const uint8_t * data, + size_t data_size); +static int early_data_send_params(gnutls_session_t session, + gnutls_buffer_st * extdata); + +const hello_ext_entry_st ext_mod_early_data = { + .name = "Early Data", + .tls_id = 42, + .gid = GNUTLS_EXTENSION_EARLY_DATA, + .validity = GNUTLS_EXT_FLAG_TLS | GNUTLS_EXT_FLAG_CLIENT_HELLO | GNUTLS_EXT_FLAG_EE, + .client_parse_point = GNUTLS_EXT_MANDATORY, /* force parsing prior to EXT_TLS extensions */ + .server_parse_point = GNUTLS_EXT_MANDATORY, /* force parsing prior to EXT_TLS extensions */ + .recv_func = early_data_recv_params, + .send_func = early_data_send_params, + .pack_func = NULL, + .unpack_func = NULL, + .deinit_func = _gnutls_hello_ext_default_deinit, + .cannot_be_overriden = 0 +}; + +static int +early_data_recv_params(gnutls_session_t session, + const uint8_t * data, size_t _data_size) +{ + const version_entry_st *vers = get_version(session); + + if (!vers || !vers->tls13_sem) + return gnutls_assert_val(0); + + if (session->security_parameters.entity == GNUTLS_SERVER) { + /* Whether to accept early data is decided during processing the + * pre_shared_key extension in the later phase. + */ + session->internals.hsk_flags |= HSK_EARLY_DATA_IN_FLIGHT; + } else { + if (_gnutls_ext_get_msg(session) == GNUTLS_EXT_FLAG_EE) + session->internals.hsk_flags |= HSK_EARLY_DATA_ACCEPTED; + } + + return 0; +} + +/* returns data_size or a negative number on failure + */ +static int +early_data_send_params(gnutls_session_t session, + gnutls_buffer_st * extdata) +{ + if (session->security_parameters.entity == GNUTLS_SERVER) { + if (session->internals.hsk_flags & HSK_EARLY_DATA_ACCEPTED) + return GNUTLS_E_INT_RET_0; + } else { + /* early data is enabled and resuming a TLS 1.3 session */ + if (session->internals.flags & GNUTLS_ENABLE_EARLY_DATA && + !(session->internals.resumption_requested == 0 && + session->internals.premaster_set == 0) && + session->internals.resumed_security_parameters.pversion && + session->internals.resumed_security_parameters.pversion->tls13_sem) { + session->internals.hsk_flags |= HSK_EARLY_DATA_IN_FLIGHT; + return GNUTLS_E_INT_RET_0; + } + } + + return 0; +} + +/** + * gnutls_record_get_max_early_data_size: + * @session: is a #gnutls_session_t type. + * + * This function returns the maximum early data size in this connection. + * This property can only be set to servers. The client may be + * provided with the maximum allowed size through the "early_data" + * extension of the NewSessionTicket handshake message. + * + * Returns: The maximum early data size in this connection. + * + * Since: 3.6.5 + **/ +size_t +gnutls_record_get_max_early_data_size(gnutls_session_t session) +{ + return session->security_parameters.max_early_data_size; +} + +/** + * gnutls_record_set_max_early_data_size: + * @session: is a #gnutls_session_t type. + * @size: is the new size + * + * This function sets the maximum early data size in this connection. + * This property can only be set to servers. The client may be + * provided with the maximum allowed size through the "early_data" + * extension of the NewSessionTicket handshake message. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, + * otherwise a negative error code is returned. + * + * Since: 3.6.4 + **/ +int +gnutls_record_set_max_early_data_size(gnutls_session_t session, + size_t size) +{ + if (session->security_parameters.entity == GNUTLS_CLIENT) + return GNUTLS_E_INVALID_REQUEST; + + /* Reject zero as well, as it is useless. */ + if (size == 0 || size > UINT32_MAX) + return GNUTLS_E_INVALID_REQUEST; + + session->security_parameters.max_early_data_size = (uint32_t) size; + + return 0; +} diff --git a/lib/ext/early_data.h b/lib/ext/early_data.h new file mode 100644 index 0000000..d7178e8 --- /dev/null +++ b/lib/ext/early_data.h @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2018 Red Hat, Inc. + * + * Author: 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/> + * + */ + +#ifndef GNUTLS_LIB_EXT_EARLY_DATA_H +#define GNUTLS_LIB_EXT_EARLY_DATA_H + +#include <hello_ext.h> + +extern const hello_ext_entry_st ext_mod_early_data; + +#endif /* GNUTLS_LIB_EXT_EARLY_DATA_H */ diff --git a/lib/ext/ec_point_formats.c b/lib/ext/ec_point_formats.c new file mode 100644 index 0000000..d426580 --- /dev/null +++ b/lib/ext/ec_point_formats.c @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2011-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/> + * + */ + +/* This file contains the code for the Elliptic Curve Point Formats extension. + */ + +#include "ext/ec_point_formats.h" +#include "str.h" +#include "state.h" +#include <gnutls/gnutls.h> + + +static int _gnutls_supported_ec_point_formats_recv_params(gnutls_session_t session, + const uint8_t * data, + size_t data_size); +static int _gnutls_supported_ec_point_formats_send_params(gnutls_session_t session, + gnutls_buffer_st * extdata); + + +const hello_ext_entry_st ext_mod_supported_ec_point_formats = { + .name = "Supported EC Point Formats", + .tls_id = 11, + .gid = GNUTLS_EXTENSION_SUPPORTED_EC_POINT_FORMATS, + .client_parse_point = GNUTLS_EXT_TLS, + .server_parse_point = GNUTLS_EXT_TLS, + .validity = GNUTLS_EXT_FLAG_TLS | GNUTLS_EXT_FLAG_DTLS | + GNUTLS_EXT_FLAG_CLIENT_HELLO | GNUTLS_EXT_FLAG_TLS12_SERVER_HELLO, + .recv_func = _gnutls_supported_ec_point_formats_recv_params, + .send_func = _gnutls_supported_ec_point_formats_send_params, + .pack_func = NULL, + .unpack_func = NULL, + .deinit_func = NULL +}; + + +/* Receive point formats + */ +static int +_gnutls_supported_ec_point_formats_recv_params(gnutls_session_t session, + const uint8_t * data, + size_t data_size) +{ + size_t len, i; + int uncompressed = 0; + + if (session->security_parameters.entity == GNUTLS_CLIENT) { + if (data_size < 1) + return + gnutls_assert_val + (GNUTLS_E_RECEIVED_ILLEGAL_EXTENSION); + + len = data[0]; + if (len < 1) + return + gnutls_assert_val + (GNUTLS_E_RECEIVED_ILLEGAL_EXTENSION); + + DECR_LEN(data_size, len + 1); + + for (i = 1; i <= len; i++) + if (data[i] == 0) { /* uncompressed */ + uncompressed = 1; + break; + } + + if (uncompressed == 0) + return + gnutls_assert_val + (GNUTLS_E_UNKNOWN_PK_ALGORITHM); + } else { + /* only sanity check here. We only support uncompressed points + * and a client must support it thus nothing to check. + */ + if (data_size < 1) + return + gnutls_assert_val + (GNUTLS_E_RECEIVED_ILLEGAL_EXTENSION); + } + + return 0; +} + +/* returns data_size or a negative number on failure + */ +static int +_gnutls_supported_ec_point_formats_send_params(gnutls_session_t session, + gnutls_buffer_st * extdata) +{ + const uint8_t p[2] = { 0x01, 0x00 }; /* only support uncompressed point format */ + int ret; + + if (session->security_parameters.entity == GNUTLS_SERVER + && !_gnutls_session_is_ecc(session)) + return 0; + + if (session->internals.priorities->groups.size > 0) { + ret = _gnutls_buffer_append_data(extdata, p, 2); + if (ret < 0) + return gnutls_assert_val(ret); + + return 2; + } + return 0; +} diff --git a/lib/ext/ec_point_formats.h b/lib/ext/ec_point_formats.h new file mode 100644 index 0000000..cc65c04 --- /dev/null +++ b/lib/ext/ec_point_formats.h @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2011-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_EXT_EC_POINT_FORMATS_H +#define GNUTLS_LIB_EXT_EC_POINT_FORMATS_H + +#include <hello_ext.h> + +extern const hello_ext_entry_st ext_mod_supported_ec_point_formats; + +#endif /* GNUTLS_LIB_EXT_EC_POINT_FORMATS_H */ diff --git a/lib/ext/etm.c b/lib/ext/etm.c new file mode 100644 index 0000000..273a31a --- /dev/null +++ b/lib/ext/etm.c @@ -0,0 +1,151 @@ +/* + * Copyright (C) 2014 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 the code for the Max Record Size TLS extension. + */ + +#include "gnutls_int.h" +#include "errors.h" +#include "num.h" +#include <hello_ext.h> +#include <ext/etm.h> + +static int _gnutls_ext_etm_recv_params(gnutls_session_t session, + const uint8_t * data, + size_t data_size); +static int _gnutls_ext_etm_send_params(gnutls_session_t session, + gnutls_buffer_st * extdata); + +const hello_ext_entry_st ext_mod_etm = { + .name = "Encrypt-then-MAC", + .tls_id = 22, + .gid = GNUTLS_EXTENSION_ETM, + .client_parse_point = GNUTLS_EXT_MANDATORY, + .server_parse_point = GNUTLS_EXT_MANDATORY, + .validity = GNUTLS_EXT_FLAG_TLS | GNUTLS_EXT_FLAG_DTLS | GNUTLS_EXT_FLAG_CLIENT_HELLO | + GNUTLS_EXT_FLAG_TLS12_SERVER_HELLO, + .recv_func = _gnutls_ext_etm_recv_params, + .send_func = _gnutls_ext_etm_send_params, + .pack_func = NULL, + .unpack_func = NULL, + .deinit_func = NULL, + .cannot_be_overriden = 1 +}; + +/* + * In case of a server: if an EXT_MASTER_SECRET extension type is received then it + * sets a flag into the session security parameters. + * + */ +static int +_gnutls_ext_etm_recv_params(gnutls_session_t session, + const uint8_t * data, size_t _data_size) +{ + ssize_t data_size = _data_size; + + if (data_size != 0) { + return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER); + } + + if (session->security_parameters.entity == GNUTLS_SERVER) { + gnutls_ext_priv_data_t epriv; + + if (session->internals.no_etm != 0) + return 0; + + epriv = (void*)(intptr_t)1; + _gnutls_hello_ext_set_priv(session, + GNUTLS_EXTENSION_ETM, + epriv); + + /* don't decide now, decide on send */ + return 0; + } else { /* client */ + const gnutls_cipher_suite_entry_st *e = + session->security_parameters.cs; + if (e != NULL) { + const cipher_entry_st *c; + c = cipher_to_entry(e->block_algorithm); + if (c == NULL || (c->type == CIPHER_AEAD || c->type == CIPHER_STREAM)) + return 0; + + session->security_parameters.etm = 1; + } + } + + return 0; +} + +/* returns data_size or a negative number on failure + */ +static int +_gnutls_ext_etm_send_params(gnutls_session_t session, + gnutls_buffer_st * extdata) +{ + if (session->internals.no_etm != 0) + return 0; + + /* this function sends the client extension data */ + if (session->security_parameters.entity == GNUTLS_CLIENT) { + if (session->internals.priorities->have_cbc != 0) + return GNUTLS_E_INT_RET_0; + else + return 0; + } else { /* server side */ + const gnutls_cipher_suite_entry_st *e; + const cipher_entry_st *c; + int ret; + gnutls_ext_priv_data_t epriv; + + e = session->security_parameters.cs; + if (e != NULL) { + c = cipher_to_entry(e->block_algorithm); + if (c == NULL || (c->type == CIPHER_AEAD || c->type == CIPHER_STREAM)) + return 0; + + ret = _gnutls_hello_ext_get_priv(session, + GNUTLS_EXTENSION_ETM, + &epriv); + if (ret < 0 || ((intptr_t)epriv) == 0) + return 0; + + session->security_parameters.etm = 1; + return GNUTLS_E_INT_RET_0; + } + } + + return 0; +} + +/** + * gnutls_session_etm_status: + * @session: is a #gnutls_session_t type. + * + * Get the status of the encrypt-then-mac extension negotiation. + * This is in accordance to rfc7366 + * + * Returns: Non-zero if the negotiation was successful or zero otherwise. + **/ +unsigned gnutls_session_etm_status(gnutls_session_t session) +{ + return session->security_parameters.etm; +} diff --git a/lib/ext/etm.h b/lib/ext/etm.h new file mode 100644 index 0000000..76f9dd5 --- /dev/null +++ b/lib/ext/etm.h @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2014 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_EXT_ETM_H +#define GNUTLS_LIB_EXT_ETM_H + +#include <hello_ext.h> + +extern const hello_ext_entry_st ext_mod_etm; + +#endif /* GNUTLS_LIB_EXT_ETM_H */ diff --git a/lib/ext/ext_master_secret.c b/lib/ext/ext_master_secret.c new file mode 100644 index 0000000..bc704e6 --- /dev/null +++ b/lib/ext/ext_master_secret.c @@ -0,0 +1,155 @@ +/* + * Copyright (C) 2014-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 the code for the RFC7627 (ext master secret) TLS extension. + */ + +#include "gnutls_int.h" +#include "errors.h" +#include "num.h" +#include <hello_ext.h> +#include <ext/ext_master_secret.h> + +static int _gnutls_ext_master_secret_recv_params(gnutls_session_t session, + const uint8_t * data, + size_t data_size); +static int _gnutls_ext_master_secret_send_params(gnutls_session_t session, + gnutls_buffer_st * extdata); + +const hello_ext_entry_st ext_mod_ext_master_secret = { + .name = "Extended Master Secret", + .tls_id = 23, + .gid = GNUTLS_EXTENSION_EXT_MASTER_SECRET, + .client_parse_point = GNUTLS_EXT_MANDATORY, + .server_parse_point = GNUTLS_EXT_MANDATORY, + .validity = GNUTLS_EXT_FLAG_TLS|GNUTLS_EXT_FLAG_DTLS | GNUTLS_EXT_FLAG_CLIENT_HELLO | + GNUTLS_EXT_FLAG_TLS12_SERVER_HELLO, + .recv_func = _gnutls_ext_master_secret_recv_params, + .send_func = _gnutls_ext_master_secret_send_params, + .pack_func = NULL, + .unpack_func = NULL, + .deinit_func = NULL, + .cannot_be_overriden = 1 +}; + +#ifdef ENABLE_SSL3 +static inline unsigned have_only_ssl3_enabled(gnutls_session_t session) +{ + if (session->internals.priorities->protocol.num_priorities == 1 && + session->internals.priorities->protocol.priorities[0] == GNUTLS_SSL3) + return 1; + return 0; +} +#endif + +/* + * In case of a server: if an EXT_MASTER_SECRET extension type is received then it + * sets a flag into the session security parameters. + * + */ +static int +_gnutls_ext_master_secret_recv_params(gnutls_session_t session, + const uint8_t * data, size_t _data_size) +{ + ssize_t data_size = _data_size; + + if ((session->internals.flags & GNUTLS_NO_EXTENSIONS) || + session->internals.priorities->no_extensions || + session->internals.no_ext_master_secret != 0) { + return 0; + } + + if (data_size != 0) { + return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH); + } + +#ifdef ENABLE_SSL3 + if (session->security_parameters.entity == GNUTLS_CLIENT) { + const version_entry_st *ver = get_version(session); + + if (unlikely(ver == NULL)) + return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); + + if (ver->id != GNUTLS_SSL3) + session->security_parameters.ext_master_secret = 1; + /* do not enable ext master secret if SSL 3.0 is the only protocol supported by server */ + } else if (!have_only_ssl3_enabled(session)) +#endif + session->security_parameters.ext_master_secret = 1; + + return 0; +} + +/* returns data_size or a negative number on failure + */ +static int +_gnutls_ext_master_secret_send_params(gnutls_session_t session, + gnutls_buffer_st * extdata) +{ + if ((session->internals.flags & GNUTLS_NO_EXTENSIONS) || + session->internals.priorities->no_extensions != 0 || + session->internals.no_ext_master_secret != 0) { + session->security_parameters.ext_master_secret = 0; + return 0; + } + + /* this function sends the client extension data */ +#ifdef ENABLE_SSL3 + if (session->security_parameters.entity == GNUTLS_CLIENT) { + if (have_only_ssl3_enabled(session)) + return 0; /* this extension isn't available for SSL 3.0 */ + + return GNUTLS_E_INT_RET_0; + } else { /* server side */ + const version_entry_st *ver = get_version(session); + if (unlikely(ver == NULL)) + return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); + + if (ver->id != GNUTLS_SSL3 && session->security_parameters.ext_master_secret != 0) + return GNUTLS_E_INT_RET_0; + } + + + return 0; +#else + if (session->security_parameters.entity == GNUTLS_CLIENT || + session->security_parameters.ext_master_secret != 0) + return GNUTLS_E_INT_RET_0; + return 0; +#endif +} + +/** + * gnutls_session_ext_master_secret_status: + * @session: is a #gnutls_session_t type. + * + * Get the status of the extended master secret extension negotiation. + * This is in accordance to RFC7627. That information is also + * available to the more generic gnutls_session_get_flags(). + * + * Returns: Non-zero if the negotiation was successful or zero otherwise. + **/ +unsigned gnutls_session_ext_master_secret_status(gnutls_session_t session) +{ + return session->security_parameters.ext_master_secret; +} + diff --git a/lib/ext/ext_master_secret.h b/lib/ext/ext_master_secret.h new file mode 100644 index 0000000..419335b --- /dev/null +++ b/lib/ext/ext_master_secret.h @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2014 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_EXT_EXT_MASTER_SECRET_H +#define GNUTLS_LIB_EXT_EXT_MASTER_SECRET_H + +#include <hello_ext.h> + +extern const hello_ext_entry_st ext_mod_ext_master_secret; + +#endif /* GNUTLS_LIB_EXT_EXT_MASTER_SECRET_H */ diff --git a/lib/ext/heartbeat.c b/lib/ext/heartbeat.c new file mode 100644 index 0000000..5d9e9f4 --- /dev/null +++ b/lib/ext/heartbeat.c @@ -0,0 +1,574 @@ +/* + * Copyright (C) 2012,2013 Free Software Foundation, Inc. + * Copyright (C) 2013 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/> + * + */ + +/* This file implements the TLS heartbeat extension. + */ + +#include "errors.h" +#include "gnutls_int.h" +#include <dtls.h> +#include <record.h> +#include <ext/heartbeat.h> +#include <hello_ext.h> +#include <random.h> + +#ifdef ENABLE_HEARTBEAT +/** + * gnutls_heartbeat_enable: + * @session: is a #gnutls_session_t type. + * @type: one of the GNUTLS_HB_* flags + * + * If this function is called with the %GNUTLS_HB_PEER_ALLOWED_TO_SEND + * @type, GnuTLS will allow heartbeat messages to be received. Moreover it also + * request the peer to accept heartbeat messages. This function + * must be called prior to TLS handshake. + * + * If the @type used is %GNUTLS_HB_LOCAL_ALLOWED_TO_SEND, then the peer + * will be asked to accept heartbeat messages but not send ones. + * + * The function gnutls_heartbeat_allowed() can be used to test Whether + * locally generated heartbeat messages can be accepted by the peer. + * + * Since: 3.1.2 + **/ +void gnutls_heartbeat_enable(gnutls_session_t session, unsigned int type) +{ + gnutls_ext_priv_data_t epriv; + + epriv = (void*)(intptr_t)type; + _gnutls_hello_ext_set_priv(session, GNUTLS_EXTENSION_HEARTBEAT, + epriv); +} + +/** + * gnutls_heartbeat_allowed: + * @session: is a #gnutls_session_t type. + * @type: one of %GNUTLS_HB_LOCAL_ALLOWED_TO_SEND and %GNUTLS_HB_PEER_ALLOWED_TO_SEND + * + * This function will check whether heartbeats are allowed + * to be sent or received in this session. + * + * Returns: Non zero if heartbeats are allowed. + * + * Since: 3.1.2 + **/ +unsigned gnutls_heartbeat_allowed(gnutls_session_t session, unsigned int type) +{ + gnutls_ext_priv_data_t epriv; + + if (session->internals.handshake_in_progress != 0) + return 0; /* not allowed */ + + if (_gnutls_hello_ext_get_priv + (session, GNUTLS_EXTENSION_HEARTBEAT, &epriv) < 0) + return 0; /* Not enabled */ + + if (type == GNUTLS_HB_LOCAL_ALLOWED_TO_SEND) { + if (((intptr_t)epriv) & LOCAL_ALLOWED_TO_SEND) + return 1; + } else if (((intptr_t)epriv) & GNUTLS_HB_PEER_ALLOWED_TO_SEND) + return 1; + + return 0; +} + +#define DEFAULT_PADDING_SIZE 16 + +/* + * Sends heartbeat data. + */ +static int +heartbeat_send_data(gnutls_session_t session, const void *data, + size_t data_size, uint8_t type) +{ + int ret, pos; + uint8_t *response; + + response = gnutls_malloc(1 + 2 + data_size + DEFAULT_PADDING_SIZE); + if (response == NULL) + return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + + pos = 0; + response[pos++] = type; + + _gnutls_write_uint16(data_size, &response[pos]); + pos += 2; + + memcpy(&response[pos], data, data_size); + pos += data_size; + + ret = + gnutls_rnd(GNUTLS_RND_NONCE, &response[pos], + DEFAULT_PADDING_SIZE); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + pos += DEFAULT_PADDING_SIZE; + + ret = + _gnutls_send_int(session, GNUTLS_HEARTBEAT, -1, + EPOCH_WRITE_CURRENT, response, pos, + MBUFFER_FLUSH); + + cleanup: + gnutls_free(response); + return ret; +} + +/** + * gnutls_heartbeat_ping: + * @session: is a #gnutls_session_t type. + * @data_size: is the length of the ping payload. + * @max_tries: if flags is %GNUTLS_HEARTBEAT_WAIT then this sets the number of retransmissions. Use zero for indefinite (until timeout). + * @flags: if %GNUTLS_HEARTBEAT_WAIT then wait for pong or timeout instead of returning immediately. + * + * This function sends a ping to the peer. If the @flags is set + * to %GNUTLS_HEARTBEAT_WAIT then it waits for a reply from the peer. + * + * Note that it is highly recommended to use this function with the + * flag %GNUTLS_HEARTBEAT_WAIT, or you need to handle retransmissions + * and timeouts manually. + * + * The total TLS data transmitted as part of the ping message are given by + * the following formula: MAX(16, @data_size)+gnutls_record_overhead_size()+3. + * + * Returns: %GNUTLS_E_SUCCESS on success, otherwise a negative error code. + * + * Since: 3.1.2 + **/ +int +gnutls_heartbeat_ping(gnutls_session_t session, size_t data_size, + unsigned int max_tries, unsigned int flags) +{ + int ret; + unsigned int retries = 1, diff; + struct timespec now; + + if (data_size > MAX_HEARTBEAT_LENGTH) + return + gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH); + + if (gnutls_heartbeat_allowed + (session, GNUTLS_HB_LOCAL_ALLOWED_TO_SEND) == 0) + return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + + /* resume previous call if interrupted */ + if (session->internals.record_send_buffer.byte_length > 0 && + session->internals.record_send_buffer.head != NULL && + session->internals.record_send_buffer.head->type == + GNUTLS_HEARTBEAT) + return _gnutls_io_write_flush(session); + + switch (session->internals.hb_state) { + case SHB_SEND1: + if (data_size > DEFAULT_PADDING_SIZE) + data_size -= DEFAULT_PADDING_SIZE; + else + data_size = 0; + + _gnutls_buffer_reset(&session->internals.hb_local_data); + + ret = + _gnutls_buffer_resize(&session->internals. + hb_local_data, data_size); + if (ret < 0) + return gnutls_assert_val(ret); + + ret = + gnutls_rnd(GNUTLS_RND_NONCE, + session->internals.hb_local_data.data, + data_size); + if (ret < 0) + return gnutls_assert_val(ret); + + gnutls_gettime(&session->internals.hb_ping_start); + session->internals.hb_local_data.length = data_size; + session->internals.hb_state = SHB_SEND2; + + FALLTHROUGH; + case SHB_SEND2: + session->internals.hb_actual_retrans_timeout_ms = + session->internals.hb_retrans_timeout_ms; + retry: + ret = + heartbeat_send_data(session, + session->internals.hb_local_data. + data, + session->internals.hb_local_data. + length, HEARTBEAT_REQUEST); + if (ret < 0) + return gnutls_assert_val(ret); + + gnutls_gettime(&session->internals.hb_ping_sent); + + if (!(flags & GNUTLS_HEARTBEAT_WAIT)) { + session->internals.hb_state = SHB_SEND1; + break; + } + + session->internals.hb_state = SHB_RECV; + FALLTHROUGH; + + case SHB_RECV: + ret = + _gnutls_recv_int(session, GNUTLS_HEARTBEAT, + NULL, 0, NULL, + session->internals. + hb_actual_retrans_timeout_ms); + if (ret == GNUTLS_E_HEARTBEAT_PONG_RECEIVED) { + session->internals.hb_state = SHB_SEND1; + break; + } else if (ret == GNUTLS_E_TIMEDOUT) { + retries++; + if (max_tries > 0 && retries > max_tries) { + session->internals.hb_state = SHB_SEND1; + return gnutls_assert_val(ret); + } + + gnutls_gettime(&now); + diff = + timespec_sub_ms(&now, + &session->internals. + hb_ping_start); + if (diff > session->internals.hb_total_timeout_ms) { + session->internals.hb_state = SHB_SEND1; + return + gnutls_assert_val(GNUTLS_E_TIMEDOUT); + } + + session->internals.hb_actual_retrans_timeout_ms *= + 2; + session->internals.hb_actual_retrans_timeout_ms %= + MAX_DTLS_TIMEOUT; + + session->internals.hb_state = SHB_SEND2; + goto retry; + } else if (ret < 0) { + session->internals.hb_state = SHB_SEND1; + return gnutls_assert_val(ret); + } + } + + return 0; +} + +/** + * gnutls_heartbeat_pong: + * @session: is a #gnutls_session_t type. + * @flags: should be zero + * + * This function replies to a ping by sending a pong to the peer. + * + * Returns: %GNUTLS_E_SUCCESS on success, otherwise a negative error code. + * + * Since: 3.1.2 + **/ +int gnutls_heartbeat_pong(gnutls_session_t session, unsigned int flags) +{ + int ret; + + if (session->internals.record_send_buffer.byte_length > 0 && + session->internals.record_send_buffer.head != NULL && + session->internals.record_send_buffer.head->type == + GNUTLS_HEARTBEAT) + return _gnutls_io_write_flush(session); + + if (session->internals.hb_remote_data.length == 0) + return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + + ret = + heartbeat_send_data(session, + session->internals.hb_remote_data.data, + session->internals.hb_remote_data.length, + HEARTBEAT_RESPONSE); + + _gnutls_buffer_reset(&session->internals.hb_remote_data); + + if (ret < 0) + return gnutls_assert_val(ret); + + return 0; +} + +/* + * Processes a heartbeat message. + */ +int _gnutls_heartbeat_handle(gnutls_session_t session, mbuffer_st * bufel) +{ + int ret; + unsigned type; + unsigned pos; + uint8_t *msg = _mbuffer_get_udata_ptr(bufel); + size_t hb_len, len = _mbuffer_get_udata_size(bufel); + + if (gnutls_heartbeat_allowed + (session, GNUTLS_HB_PEER_ALLOWED_TO_SEND) == 0) + return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET); + + if (len < 3 + DEFAULT_PADDING_SIZE) + return + gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH); + + pos = 0; + type = msg[pos++]; + + hb_len = _gnutls_read_uint16(&msg[pos]); + if (hb_len > len - 3 - DEFAULT_PADDING_SIZE) + return + gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH); + + pos += 2; + + switch (type) { + case HEARTBEAT_REQUEST: + _gnutls_buffer_reset(&session->internals.hb_remote_data); + + ret = + _gnutls_buffer_resize(&session->internals. + hb_remote_data, hb_len); + if (ret < 0) + return gnutls_assert_val(ret); + + if (hb_len > 0) + memcpy(session->internals.hb_remote_data.data, + &msg[pos], hb_len); + session->internals.hb_remote_data.length = hb_len; + + return gnutls_assert_val(GNUTLS_E_HEARTBEAT_PING_RECEIVED); + + case HEARTBEAT_RESPONSE: + + if (hb_len != session->internals.hb_local_data.length) + return + gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET); + + if (hb_len > 0 && + memcmp(&msg[pos], + session->internals.hb_local_data.data, + hb_len) != 0) { + if (IS_DTLS(session)) + return gnutls_assert_val(GNUTLS_E_AGAIN); /* ignore it */ + else + return + gnutls_assert_val + (GNUTLS_E_UNEXPECTED_PACKET); + } + + _gnutls_buffer_reset(&session->internals.hb_local_data); + + return gnutls_assert_val(GNUTLS_E_HEARTBEAT_PONG_RECEIVED); + default: + _gnutls_record_log + ("REC[%p]: HB: received unknown type %u\n", session, + type); + return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET); + } +} + +/** + * gnutls_heartbeat_get_timeout: + * @session: is a #gnutls_session_t type. + * + * This function will return the milliseconds remaining + * for a retransmission of the previously sent ping + * message. This function is useful when ping is used in + * non-blocking mode, to estimate when to call gnutls_heartbeat_ping() + * if no packets have been received. + * + * Returns: the remaining time in milliseconds. + * + * Since: 3.1.2 + **/ +unsigned int gnutls_heartbeat_get_timeout(gnutls_session_t session) +{ + struct timespec now; + unsigned int diff; + + gnutls_gettime(&now); + diff = timespec_sub_ms(&now, &session->internals.hb_ping_sent); + if (diff >= session->internals.hb_actual_retrans_timeout_ms) + return 0; + else + return session->internals.hb_actual_retrans_timeout_ms - + diff; +} + +/** + * gnutls_heartbeat_set_timeouts: + * @session: is a #gnutls_session_t type. + * @retrans_timeout: The time at which a retransmission will occur in milliseconds + * @total_timeout: The time at which the connection will be aborted, in milliseconds. + * + * This function will override the timeouts for the DTLS heartbeat + * protocol. The retransmission timeout is the time after which a + * message from the peer is not received, the previous request will + * be retransmitted. The total timeout is the time after which the + * handshake will be aborted with %GNUTLS_E_TIMEDOUT. + * + * Since: 3.1.2 + **/ +void gnutls_heartbeat_set_timeouts(gnutls_session_t session, + unsigned int retrans_timeout, + unsigned int total_timeout) +{ + session->internals.hb_retrans_timeout_ms = retrans_timeout; + session->internals.hb_total_timeout_ms = total_timeout; +} + + +static int +_gnutls_heartbeat_recv_params(gnutls_session_t session, + const uint8_t * data, size_t _data_size) +{ + unsigned policy; + gnutls_ext_priv_data_t epriv; + + if (_gnutls_hello_ext_get_priv + (session, GNUTLS_EXTENSION_HEARTBEAT, &epriv) < 0) { + if (session->security_parameters.entity == GNUTLS_CLIENT) + return + gnutls_assert_val + (GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER); + return 0; /* Not enabled */ + } + + if (_data_size == 0) + return GNUTLS_E_UNEXPECTED_PACKET_LENGTH; + + policy = (intptr_t)epriv; + + if (data[0] == 1) + policy |= LOCAL_ALLOWED_TO_SEND; + else if (data[0] == 2) + policy |= LOCAL_NOT_ALLOWED_TO_SEND; + else + return + gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER); + + epriv = (void*)(intptr_t)policy; + _gnutls_hello_ext_set_priv(session, GNUTLS_EXTENSION_HEARTBEAT, + epriv); + + return 0; +} + +static int +_gnutls_heartbeat_send_params(gnutls_session_t session, + gnutls_buffer_st * extdata) +{ + gnutls_ext_priv_data_t epriv; + uint8_t p; + + if (_gnutls_hello_ext_get_priv + (session, GNUTLS_EXTENSION_HEARTBEAT, &epriv) < 0) + return 0; /* nothing to send - not enabled */ + + if (((intptr_t)epriv) & GNUTLS_HB_PEER_ALLOWED_TO_SEND) + p = 1; + else /*if (epriv.num & GNUTLS_HB_PEER_NOT_ALLOWED_TO_SEND) */ + p = 2; + + if (_gnutls_buffer_append_data(extdata, &p, 1) < 0) + return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + + return 1; +} + +static int +_gnutls_heartbeat_pack(gnutls_ext_priv_data_t epriv, gnutls_buffer_st * ps) +{ + int ret; + + BUFFER_APPEND_NUM(ps, (intptr_t)epriv); + + return 0; + +} + +static int +_gnutls_heartbeat_unpack(gnutls_buffer_st * ps, + gnutls_ext_priv_data_t * _priv) +{ + gnutls_ext_priv_data_t epriv; + int ret; + + BUFFER_POP_CAST_NUM(ps, epriv); + + *_priv = epriv; + + ret = 0; + error: + return ret; +} + +const hello_ext_entry_st ext_mod_heartbeat = { + .name = "Heartbeat", + .tls_id = 15, + .gid = GNUTLS_EXTENSION_HEARTBEAT, + .client_parse_point = GNUTLS_EXT_TLS, + .server_parse_point = GNUTLS_EXT_TLS, + .validity = GNUTLS_EXT_FLAG_TLS | GNUTLS_EXT_FLAG_DTLS | GNUTLS_EXT_FLAG_CLIENT_HELLO | + GNUTLS_EXT_FLAG_EE | GNUTLS_EXT_FLAG_TLS12_SERVER_HELLO, + .recv_func = _gnutls_heartbeat_recv_params, + .send_func = _gnutls_heartbeat_send_params, + .pack_func = _gnutls_heartbeat_pack, + .unpack_func = _gnutls_heartbeat_unpack, + .deinit_func = NULL, + .cannot_be_overriden = 1 +}; + +#else +void gnutls_heartbeat_enable(gnutls_session_t session, unsigned int type) +{ +} + +unsigned gnutls_heartbeat_allowed(gnutls_session_t session, unsigned int type) +{ + return 0; +} + +int +gnutls_heartbeat_ping(gnutls_session_t session, size_t data_size, + unsigned int max_tries, unsigned int flags) +{ + return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); +} + +int gnutls_heartbeat_pong(gnutls_session_t session, unsigned int flags) +{ + return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); +} + +unsigned int gnutls_heartbeat_get_timeout(gnutls_session_t session) +{ + return 0; +} + +void gnutls_heartbeat_set_timeouts(gnutls_session_t session, + unsigned int retrans_timeout, + unsigned int total_timeout) +{ + return; +} +#endif diff --git a/lib/ext/heartbeat.h b/lib/ext/heartbeat.h new file mode 100644 index 0000000..1a21004 --- /dev/null +++ b/lib/ext/heartbeat.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2012 Free Software Foundation, Inc. + * Copyright (C) 2013 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/> + * + */ + +#ifndef GNUTLS_LIB_EXT_HEARTBEAT_H +#define GNUTLS_LIB_EXT_HEARTBEAT_H + +#include <hello_ext.h> + +#define HEARTBEAT_REQUEST 1 +#define HEARTBEAT_RESPONSE 2 + +#define MAX_HEARTBEAT_LENGTH DEFAULT_MAX_RECORD_SIZE + +#define LOCAL_ALLOWED_TO_SEND (1<<2) +#define LOCAL_NOT_ALLOWED_TO_SEND (1<<3) + +#define HEARTBEAT_DEFAULT_POLICY PEER_NOT_ALLOWED_TO_SEND + +extern const hello_ext_entry_st ext_mod_heartbeat; + +int _gnutls_heartbeat_handle(gnutls_session_t session, mbuffer_st * bufel); +int _gnutls_heartbeat_enabled(gnutls_session_t session, int local); + +#endif /* GNUTLS_LIB_EXT_HEARTBEAT_H */ diff --git a/lib/ext/key_share.c b/lib/ext/key_share.c new file mode 100644 index 0000000..a4db3af --- /dev/null +++ b/lib/ext/key_share.c @@ -0,0 +1,794 @@ +/* + * 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/> + * + */ + +/* This file contains the code the Key Share TLS 1.3 extension. + */ + +#include "gnutls_int.h" +#include "errors.h" +#include "num.h" +#include "ext/supported_groups.h" +#include <state.h> +#include <num.h> +#include <algorithms.h> +#include "auth/psk.h" +#include "auth/cert.h" +#include "handshake.h" +#include "../ecc.h" +#include "../algorithms.h" +#include "pk.h" + +static int key_share_recv_params(gnutls_session_t session, + const uint8_t * data, + size_t data_size); +static int key_share_send_params(gnutls_session_t session, + gnutls_buffer_st * extdata); + +const hello_ext_entry_st ext_mod_key_share = { + .name = "Key Share", + .tls_id = 51, + .gid = GNUTLS_EXTENSION_KEY_SHARE, + .client_parse_point = _GNUTLS_EXT_TLS_POST_CS, + .server_parse_point = _GNUTLS_EXT_TLS_POST_CS, + .validity = GNUTLS_EXT_FLAG_TLS | GNUTLS_EXT_FLAG_CLIENT_HELLO | GNUTLS_EXT_FLAG_TLS13_SERVER_HELLO | + GNUTLS_EXT_FLAG_HRR, + .recv_func = key_share_recv_params, + .send_func = key_share_send_params, + .pack_func = NULL, + .unpack_func = NULL, + .deinit_func = NULL, + .cannot_be_overriden = 1 +}; + +/* + * Generates key exchange parameters, and stores them in + * session->key.kshare_*_params. + * + * struct { + * NamedGroup group; + * opaque key_exchange<1..2^16-1>; + * } KeyShareEntry; + * + */ +static int client_gen_key_share(gnutls_session_t session, const gnutls_group_entry_st *group, gnutls_buffer_st *extdata) +{ + gnutls_datum_t tmp = {NULL, 0}; + int ret; + + if (group->pk != GNUTLS_PK_EC && group->pk != GNUTLS_PK_ECDH_X25519 && + group->pk != GNUTLS_PK_ECDH_X448 && + group->pk != GNUTLS_PK_DH) { + _gnutls_debug_log("Cannot send key share for group %s!\n", group->name); + return GNUTLS_E_INT_RET_0; + } + + _gnutls_handshake_log("EXT[%p]: sending key share for %s\n", session, group->name); + + ret = + _gnutls_buffer_append_prefix(extdata, 16, group->tls_id); + if (ret < 0) + return gnutls_assert_val(ret); + + if (group->pk == GNUTLS_PK_EC) { + gnutls_pk_params_release(&session->key.kshare.ecdh_params); + gnutls_pk_params_init(&session->key.kshare.ecdh_params); + + ret = _gnutls_pk_generate_keys(group->pk, group->curve, + &session->key.kshare.ecdh_params, 1); + if (ret < 0) + return gnutls_assert_val(ret); + + ret = _gnutls_ecc_ansi_x962_export(group->curve, + session->key.kshare.ecdh_params.params[ECC_X], + session->key.kshare.ecdh_params.params[ECC_Y], + &tmp); + if (ret < 0) + return gnutls_assert_val(ret); + + ret = + _gnutls_buffer_append_data_prefix(extdata, 16, tmp.data, tmp.size); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + session->key.kshare.ecdh_params.algo = group->pk; + session->key.kshare.ecdh_params.curve = group->curve; + + ret = 0; + + } else if (group->pk == GNUTLS_PK_ECDH_X25519 || + group->pk == GNUTLS_PK_ECDH_X448) { + gnutls_pk_params_release(&session->key.kshare.ecdhx_params); + gnutls_pk_params_init(&session->key.kshare.ecdhx_params); + + ret = _gnutls_pk_generate_keys(group->pk, group->curve, + &session->key.kshare.ecdhx_params, 1); + if (ret < 0) + return gnutls_assert_val(ret); + + ret = + _gnutls_buffer_append_data_prefix(extdata, 16, + session->key.kshare.ecdhx_params.raw_pub.data, + session->key.kshare.ecdhx_params.raw_pub.size); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + session->key.kshare.ecdhx_params.algo = group->pk; + session->key.kshare.ecdhx_params.curve = group->curve; + + ret = 0; + + } else if (group->pk == GNUTLS_PK_DH) { + /* we need to initialize the group parameters first */ + gnutls_pk_params_release(&session->key.kshare.dh_params); + gnutls_pk_params_init(&session->key.kshare.dh_params); + + ret = _gnutls_mpi_init_scan_nz(&session->key.kshare.dh_params.params[DH_G], + group->generator->data, group->generator->size); + if (ret < 0) + return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER); + + ret = _gnutls_mpi_init_scan_nz(&session->key.kshare.dh_params.params[DH_P], + group->prime->data, group->prime->size); + if (ret < 0) + return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER); + + ret = _gnutls_mpi_init_scan_nz(&session->key.kshare.dh_params.params[DH_Q], + group->q->data, group->q->size); + if (ret < 0) + return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER); + + session->key.kshare.dh_params.algo = group->pk; + session->key.kshare.dh_params.dh_group = group->id; /* no curve in FFDH, we write the group */ + session->key.kshare.dh_params.qbits = *group->q_bits; + session->key.kshare.dh_params.params_nr = 3; + + ret = _gnutls_pk_generate_keys(group->pk, 0, &session->key.kshare.dh_params, 1); + if (ret < 0) + return gnutls_assert_val(ret); + + ret = + _gnutls_buffer_append_prefix(extdata, 16, group->prime->size); + if (ret < 0) + return gnutls_assert_val(ret); + + ret = _gnutls_buffer_append_fixed_mpi(extdata, session->key.kshare.dh_params.params[DH_Y], + group->prime->size); + if (ret < 0) + return gnutls_assert_val(ret); + + ret = 0; + } + + cleanup: + gnutls_free(tmp.data); + return ret; +} + +/* + * Sends server key exchange parameters + * + */ +static int server_gen_key_share(gnutls_session_t session, const gnutls_group_entry_st *group, gnutls_buffer_st *extdata) +{ + gnutls_datum_t tmp = {NULL, 0}; + int ret; + + if (group->pk != GNUTLS_PK_EC && group->pk != GNUTLS_PK_ECDH_X25519 && + group->pk != GNUTLS_PK_ECDH_X448 && + group->pk != GNUTLS_PK_DH) { + _gnutls_debug_log("Cannot send key share for group %s!\n", group->name); + return GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER; + } + + _gnutls_handshake_log("EXT[%p]: sending key share for %s\n", session, group->name); + + ret = + _gnutls_buffer_append_prefix(extdata, 16, group->tls_id); + if (ret < 0) + return gnutls_assert_val(ret); + + if (group->pk == GNUTLS_PK_EC) { + ret = _gnutls_ecc_ansi_x962_export(group->curve, + session->key.kshare.ecdh_params.params[ECC_X], + session->key.kshare.ecdh_params.params[ECC_Y], + &tmp); + if (ret < 0) + return gnutls_assert_val(ret); + + ret = + _gnutls_buffer_append_data_prefix(extdata, 16, tmp.data, tmp.size); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = 0; + + } else if (group->pk == GNUTLS_PK_ECDH_X25519 || + group->pk == GNUTLS_PK_ECDH_X448) { + ret = + _gnutls_buffer_append_data_prefix(extdata, 16, + session->key.kshare.ecdhx_params.raw_pub.data, + session->key.kshare.ecdhx_params.raw_pub.size); + if (ret < 0) + return gnutls_assert_val(ret); + + ret = 0; + + } else if (group->pk == GNUTLS_PK_DH) { + ret = + _gnutls_buffer_append_prefix(extdata, 16, group->prime->size); + if (ret < 0) + return gnutls_assert_val(ret); + + ret = _gnutls_buffer_append_fixed_mpi(extdata, session->key.kshare.dh_params.params[DH_Y], + group->prime->size); + if (ret < 0) + return gnutls_assert_val(ret); + + ret = 0; + } + + cleanup: + gnutls_free(tmp.data); + return ret; +} + +/* Generates shared key and stores it in session->key.key + */ +static int +server_use_key_share(gnutls_session_t session, const gnutls_group_entry_st *group, + const uint8_t * data, size_t data_size) +{ + const gnutls_ecc_curve_entry_st *curve; + int ret; + + if (group->pk == GNUTLS_PK_EC) { + gnutls_pk_params_st pub; + + gnutls_pk_params_release(&session->key.kshare.ecdh_params); + gnutls_pk_params_init(&session->key.kshare.ecdh_params); + + curve = _gnutls_ecc_curve_get_params(group->curve); + + gnutls_pk_params_init(&pub); + + if (curve->size*2+1 != data_size) + return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER); + + /* generate our key */ + ret = _gnutls_pk_generate_keys(curve->pk, curve->id, &session->key.kshare.ecdh_params, 1); + if (ret < 0) + return gnutls_assert_val(ret); + + /* read the public key */ + ret = _gnutls_ecc_ansi_x962_import(data, data_size, + &pub.params[ECC_X], + &pub.params[ECC_Y]); + if (ret < 0) + return gnutls_assert_val(ret); + + pub.algo = group->pk; + pub.curve = curve->id; + pub.params_nr = 2; + + /* generate shared */ + ret = _gnutls_pk_derive_tls13(curve->pk, &session->key.key, &session->key.kshare.ecdh_params, &pub); + gnutls_pk_params_release(&pub); + if (ret < 0) { + return gnutls_assert_val(ret); + } + + ret = 0; + + } else if (group->pk == GNUTLS_PK_ECDH_X25519 || + group->pk == GNUTLS_PK_ECDH_X448) { + gnutls_pk_params_st pub; + + gnutls_pk_params_release(&session->key.kshare.ecdhx_params); + gnutls_pk_params_init(&session->key.kshare.ecdhx_params); + + curve = _gnutls_ecc_curve_get_params(group->curve); + + if (curve->size != data_size) + return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER); + + /* generate our key */ + ret = _gnutls_pk_generate_keys(curve->pk, curve->id, &session->key.kshare.ecdhx_params, 1); + if (ret < 0) + return gnutls_assert_val(ret); + + /* read the public key and generate shared */ + gnutls_pk_params_init(&pub); + + pub.algo = group->pk; + pub.curve = curve->id; + + pub.raw_pub.data = (void*)data; + pub.raw_pub.size = data_size; + + /* We don't mask the MSB in the final byte as required + * by RFC7748. This will be done internally by nettle 3.3 or later. + */ + ret = _gnutls_pk_derive_tls13(curve->pk, &session->key.key, &session->key.kshare.ecdhx_params, &pub); + if (ret < 0) { + return gnutls_assert_val(ret); + } + + ret = 0; + + } else if (group->pk == GNUTLS_PK_DH) { + gnutls_pk_params_st pub; + + /* we need to initialize the group parameters first */ + gnutls_pk_params_release(&session->key.kshare.dh_params); + gnutls_pk_params_init(&session->key.kshare.dh_params); + + if (data_size != group->prime->size) + return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER); + + /* set group params */ + ret = _gnutls_mpi_init_scan_nz(&session->key.kshare.dh_params.params[DH_G], + group->generator->data, group->generator->size); + if (ret < 0) + return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER); + + ret = _gnutls_mpi_init_scan_nz(&session->key.kshare.dh_params.params[DH_P], + group->prime->data, group->prime->size); + if (ret < 0) + return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER); + + ret = _gnutls_mpi_init_scan_nz(&session->key.kshare.dh_params.params[DH_Q], + group->q->data, group->q->size); + if (ret < 0) + return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER); + + session->key.kshare.dh_params.algo = GNUTLS_PK_DH; + session->key.kshare.dh_params.qbits = *group->q_bits; + session->key.kshare.dh_params.params_nr = 3; + + /* generate our keys */ + ret = _gnutls_pk_generate_keys(group->pk, 0, &session->key.kshare.dh_params, 1); + if (ret < 0) + return gnutls_assert_val(ret); + + /* read the public key and generate shared */ + gnutls_pk_params_init(&pub); + + ret = _gnutls_mpi_init_scan_nz(&pub.params[DH_Y], + data, data_size); + if (ret < 0) + return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER); + + pub.algo = group->pk; + + /* generate shared key */ + ret = _gnutls_pk_derive_tls13(GNUTLS_PK_DH, &session->key.key, &session->key.kshare.dh_params, &pub); + _gnutls_mpi_release(&pub.params[DH_Y]); + if (ret < 0) + return gnutls_assert_val(ret); + + ret = 0; + } else { + return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER); + } + + _gnutls_debug_log("EXT[%p]: server generated %s shared key\n", session, group->name); + + return ret; +} + +/* Generates shared key and stores it in session->key.key + */ +static int +client_use_key_share(gnutls_session_t session, const gnutls_group_entry_st *group, + const uint8_t * data, size_t data_size) +{ + const gnutls_ecc_curve_entry_st *curve; + int ret; + + if (group->pk == GNUTLS_PK_EC) { + gnutls_pk_params_st pub; + + curve = _gnutls_ecc_curve_get_params(group->curve); + + gnutls_pk_params_init(&pub); + + if (session->key.kshare.ecdh_params.algo != group->pk || session->key.kshare.ecdh_params.curve != curve->id) + return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER); + + if (curve->size*2+1 != data_size) + return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER); + + /* read the server's public key */ + ret = _gnutls_ecc_ansi_x962_import(data, data_size, + &pub.params[ECC_X], + &pub.params[ECC_Y]); + if (ret < 0) + return gnutls_assert_val(ret); + + pub.algo = group->pk; + pub.curve = curve->id; + pub.params_nr = 2; + + /* generate shared key */ + ret = _gnutls_pk_derive_tls13(curve->pk, &session->key.key, &session->key.kshare.ecdh_params, &pub); + gnutls_pk_params_release(&pub); + if (ret < 0) { + return gnutls_assert_val(ret); + } + + ret = 0; + + } else if (group->pk == GNUTLS_PK_ECDH_X25519 || + group->pk == GNUTLS_PK_ECDH_X448) { + gnutls_pk_params_st pub; + + curve = _gnutls_ecc_curve_get_params(group->curve); + + if (session->key.kshare.ecdhx_params.algo != group->pk || session->key.kshare.ecdhx_params.curve != curve->id) + return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER); + + if (curve->size != data_size) + return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER); + + /* read the public key and generate shared */ + gnutls_pk_params_init(&pub); + + pub.algo = group->pk; + pub.curve = curve->id; + + pub.raw_pub.data = (void*)data; + pub.raw_pub.size = data_size; + + /* We don't mask the MSB in the final byte as required + * by RFC7748. This will be done internally by nettle 3.3 or later. + */ + ret = _gnutls_pk_derive_tls13(curve->pk, &session->key.key, &session->key.kshare.ecdhx_params, &pub); + if (ret < 0) { + return gnutls_assert_val(ret); + } + + ret = 0; + + } else if (group->pk == GNUTLS_PK_DH) { + gnutls_pk_params_st pub; + + if (session->key.kshare.dh_params.algo != group->pk || session->key.kshare.dh_params.dh_group != group->id) + return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER); + + if (data_size != group->prime->size) + return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER); + + /* read the public key and generate shared */ + gnutls_pk_params_init(&pub); + + ret = _gnutls_mpi_init_scan_nz(&pub.params[DH_Y], + data, data_size); + if (ret < 0) + return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER); + + pub.algo = group->pk; + + /* generate shared key */ + ret = _gnutls_pk_derive_tls13(GNUTLS_PK_DH, &session->key.key, &session->key.kshare.dh_params, &pub); + _gnutls_mpi_release(&pub.params[DH_Y]); + if (ret < 0) + return gnutls_assert_val(ret); + + ret = 0; + } else { + return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER); + } + + _gnutls_debug_log("EXT[%p]: client generated %s shared key\n", session, group->name); + + return ret; +} + +static int +key_share_recv_params(gnutls_session_t session, + const uint8_t * data, size_t data_size) +{ + int ret; + size_t size; + unsigned gid; + const version_entry_st *ver; + const gnutls_group_entry_st *group; + unsigned used_share = 0; + + if (session->security_parameters.entity == GNUTLS_SERVER) { + ver = get_version(session); + if (ver == NULL || ver->key_shares == 0) + return gnutls_assert_val(0); + + DECR_LEN(data_size, 2); + size = _gnutls_read_uint16(data); + data += 2; + + if (data_size != size) + return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH); + + /* if we do PSK without DH ignore that share */ + if ((session->internals.hsk_flags & HSK_PSK_SELECTED) && + (session->internals.hsk_flags & HSK_PSK_KE_MODE_PSK)) { + reset_cand_groups(session); + return 0; + } + + while(data_size > 0) { + DECR_LEN(data_size, 2); + gid = _gnutls_read_uint16(data); + data += 2; + + DECR_LEN(data_size, 2); + size = _gnutls_read_uint16(data); + data += 2; + + DECR_LEN(data_size, size); + + /* at this point we have already negotiated a group; + * find the group's share. */ + group = _gnutls_tls_id_to_group(gid); + + if (group != NULL) + _gnutls_handshake_log("EXT[%p]: Received key share for %s\n", session, group->name); + + if (group != NULL && group == session->internals.cand_group) { + _gnutls_session_group_set(session, group); + + ret = server_use_key_share(session, group, data, size); + if (ret < 0) + return gnutls_assert_val(ret); + + used_share = 1; + break; + + } + + data += size; + continue; + } + + /* we utilize GNUTLS_E_NO_COMMON_KEY_SHARE for: + * 1. signal for hello-retry-request in the handshake + * layer during first client hello parsing (server side - here). + * This does not result to error code being + * propagated to app layer. + * 2. Propagate to application error code that no + * common key share was found after an HRR was + * received (client side) + * 3. Propagate to application error code that no + * common key share was found after an HRR was + * sent (server side). + * In cases (2,3) the error is translated to illegal + * parameter alert. + */ + if (used_share == 0) { + return gnutls_assert_val(GNUTLS_E_NO_COMMON_KEY_SHARE); + } + + session->internals.hsk_flags |= HSK_KEY_SHARE_RECEIVED; + } else { /* Client */ + ver = get_version(session); + if (unlikely(ver == NULL || ver->key_shares == 0)) + return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER); + + if (_gnutls_ext_get_msg(session) == GNUTLS_EXT_FLAG_HRR) { + if (unlikely(!(session->internals.hsk_flags & HSK_HRR_RECEIVED))) + return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER); + + DECR_LEN(data_size, 2); + gid = _gnutls_read_uint16(data); + + group = _gnutls_tls_id_to_group(gid); + if (group == NULL) + return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER); + + _gnutls_handshake_log("EXT[%p]: HRR key share with %s\n", session, group->name); + + /* check if we support it */ + ret = _gnutls_session_supports_group(session, group->id); + if (ret < 0) { + _gnutls_handshake_log("EXT[%p]: received share for %s which is disabled\n", session, group->name); + return gnutls_assert_val(ret); + } + + _gnutls_session_group_set(session, group); + + return 0; + } + /* else */ + + DECR_LEN(data_size, 2); + gid = _gnutls_read_uint16(data); + data += 2; + + DECR_LEN(data_size, 2); + size = _gnutls_read_uint16(data); + data+=2; + + if (data_size != size) + return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH); + + group = _gnutls_tls_id_to_group(gid); + if (group == NULL) + return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER); + + /* check if we support it */ + ret = _gnutls_session_supports_group(session, group->id); + if (ret < 0) { + _gnutls_handshake_log("EXT[%p]: received share for %s which is disabled\n", session, group->name); + return gnutls_assert_val(ret); + } + + _gnutls_session_group_set(session, group); + session->internals.hsk_flags |= HSK_KEY_SHARE_RECEIVED; + + ret = client_use_key_share(session, group, data, size); + if (ret < 0) + return gnutls_assert_val(ret); + } + + return 0; +} + +static inline bool +pk_type_is_ecdhx(gnutls_pk_algorithm_t pk) +{ + return pk == GNUTLS_PK_ECDH_X25519 || pk == GNUTLS_PK_ECDH_X448; +} + +static inline bool +pk_type_equal(gnutls_pk_algorithm_t a, gnutls_pk_algorithm_t b) +{ + return a == b || (pk_type_is_ecdhx(a) && pk_type_is_ecdhx(b)); +} + +/* returns data_size or a negative number on failure + */ +static int +key_share_send_params(gnutls_session_t session, + gnutls_buffer_st * extdata) +{ + unsigned i; + int ret; + unsigned int generated = 0; + const gnutls_group_entry_st *group; + const version_entry_st *ver; + + /* this extension is only being sent on client side */ + if (session->security_parameters.entity == GNUTLS_CLIENT) { + unsigned int length_pos; + + ver = _gnutls_version_max(session); + if (unlikely(ver == NULL || ver->key_shares == 0)) + return 0; + + if (!have_creds_for_tls13(session)) + return 0; + + length_pos = extdata->length; + + ret = + _gnutls_buffer_append_prefix(extdata, 16, 0); + if (ret < 0) + return gnutls_assert_val(ret); + + if (session->internals.hsk_flags & HSK_HRR_RECEIVED) { /* we know the group */ + group = get_group(session); + if (unlikely(group == NULL)) + return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER); + + ret = client_gen_key_share(session, group, extdata); + if (ret == GNUTLS_E_INT_RET_0) + return gnutls_assert_val(GNUTLS_E_NO_COMMON_KEY_SHARE); + if (ret < 0) + return gnutls_assert_val(ret); + } else { + gnutls_pk_algorithm_t selected_groups[3]; + unsigned max_groups = 2; /* GNUTLS_KEY_SHARE_TOP2 */ + + if (session->internals.flags & GNUTLS_KEY_SHARE_TOP) + max_groups = 1; + else if (session->internals.flags & GNUTLS_KEY_SHARE_TOP3) + max_groups = 3; + + assert(max_groups <= sizeof(selected_groups)/sizeof(selected_groups[0])); + + /* generate key shares for out top-(max_groups) groups + * if they are of different PK type. */ + for (i = 0; i < session->internals.priorities->groups.size; i++) { + unsigned int j; + + group = session->internals.priorities->groups.entry[i]; + + for (j = 0; j < generated; j++) { + if (pk_type_equal(group->pk, selected_groups[j])) { + break; + } + } + if (j < generated) { + continue; + } + + selected_groups[generated] = group->pk; + + ret = client_gen_key_share(session, group, extdata); + if (ret == GNUTLS_E_INT_RET_0) + continue; /* no key share for this algorithm */ + if (ret < 0) + return gnutls_assert_val(ret); + + generated++; + + if (generated >= max_groups) + break; + } + } + + /* copy actual length */ + _gnutls_write_uint16(extdata->length - length_pos - 2, + &extdata->data[length_pos]); + + } else { /* server */ + ver = get_version(session); + if (unlikely(ver == NULL || ver->key_shares == 0)) + return gnutls_assert_val(0); + + if (_gnutls_ext_get_msg(session) == GNUTLS_EXT_FLAG_HRR) { + group = session->internals.cand_group; + + if (group == NULL) + return gnutls_assert_val(GNUTLS_E_NO_COMMON_KEY_SHARE); + + _gnutls_session_group_set(session, group); + + _gnutls_handshake_log("EXT[%p]: requesting retry with group %s\n", session, group->name); + ret = + _gnutls_buffer_append_prefix(extdata, 16, group->tls_id); + if (ret < 0) + return gnutls_assert_val(ret); + } else { + /* if we are negotiating PSK without DH, do not send a key share */ + if ((session->internals.hsk_flags & HSK_PSK_SELECTED) && + (session->internals.hsk_flags & HSK_PSK_KE_MODE_PSK)) + return gnutls_assert_val(0); + + group = get_group(session); + if (unlikely(group == NULL)) + return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER); + + ret = server_gen_key_share(session, group, extdata); + if (ret < 0) + return gnutls_assert_val(ret); + } + + session->internals.hsk_flags |= HSK_KEY_SHARE_SENT; + } + + return 0; +} diff --git a/lib/ext/key_share.h b/lib/ext/key_share.h new file mode 100644 index 0000000..ed9aa16 --- /dev/null +++ b/lib/ext/key_share.h @@ -0,0 +1,30 @@ +/* + * 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_EXT_KEY_SHARE_H +#define GNUTLS_LIB_EXT_KEY_SHARE_H + +#include <hello_ext.h> + +extern const hello_ext_entry_st ext_mod_key_share; + +#endif /* GNUTLS_LIB_EXT_KEY_SHARE_H */ diff --git a/lib/ext/max_record.c b/lib/ext/max_record.c new file mode 100644 index 0000000..87302cb --- /dev/null +++ b/lib/ext/max_record.c @@ -0,0 +1,317 @@ +/* + * Copyright (C) 2001-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/> + * + */ + +/* This file contains the code for the Max Record Size TLS extension. + */ + +#include "gnutls_int.h" +#include "errors.h" +#include "num.h" +#include <hello_ext.h> +#include <ext/max_record.h> + +static int _gnutls_max_record_recv_params(gnutls_session_t session, + const uint8_t * data, + size_t data_size); +static int _gnutls_max_record_send_params(gnutls_session_t session, + gnutls_buffer_st * extdata); + +/* Maps record size to numbers according to the + * extensions draft. + */ +static int _gnutls_mre_num2record(int num); +static int _gnutls_mre_record2num(uint16_t record_size); + + +const hello_ext_entry_st ext_mod_max_record_size = { + .name = "Maximum Record Size", + .tls_id = 1, + .gid = GNUTLS_EXTENSION_MAX_RECORD_SIZE, + .client_parse_point = GNUTLS_EXT_TLS, + .server_parse_point = GNUTLS_EXT_TLS, + .validity = GNUTLS_EXT_FLAG_TLS | GNUTLS_EXT_FLAG_DTLS | GNUTLS_EXT_FLAG_CLIENT_HELLO | + GNUTLS_EXT_FLAG_EE | GNUTLS_EXT_FLAG_TLS12_SERVER_HELLO, + .recv_func = _gnutls_max_record_recv_params, + .send_func = _gnutls_max_record_send_params +}; + +/* + * In case of a server: if a MAX_RECORD_SIZE extension type is received then it stores + * into the session the new value. The server may use gnutls_get_max_record_size(), + * in order to access it. + * + * In case of a client: If a different max record size (than the default) has + * been specified then it sends the extension. + * + */ + +static int +_gnutls_max_record_recv_params(gnutls_session_t session, + const uint8_t * data, size_t data_size) +{ + ssize_t new_size; + + if (session->internals.hsk_flags & HSK_RECORD_SIZE_LIMIT_NEGOTIATED) + return 0; + + if (session->security_parameters.entity == GNUTLS_SERVER) { + if (data_size > 0) { + DECR_LEN(data_size, 1); + + new_size = _gnutls_mre_num2record(data[0]); + + if (new_size < 0) { + gnutls_assert(); + return new_size; + } + + session->security_parameters.max_record_send_size = + new_size; + session->security_parameters.max_record_recv_size = + new_size; + } + } else { /* CLIENT SIDE - we must check if the sent record size is the right one + */ + if (data_size > 0) { + if (data_size != 1) { + gnutls_assert(); + return GNUTLS_E_UNEXPECTED_PACKET_LENGTH; + } + + new_size = _gnutls_mre_num2record(data[0]); + + if (new_size < 0) { + gnutls_assert(); + return new_size; + } + + if (new_size != session->security_parameters. + max_user_record_send_size) { + gnutls_assert(); + return GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER; + } else { + session->security_parameters. + max_record_send_size = new_size; + session->security_parameters. + max_record_recv_size = new_size; + } + + } + + + } + + return 0; +} + +/* returns data_size or a negative number on failure + */ +static int +_gnutls_max_record_send_params(gnutls_session_t session, + gnutls_buffer_st * extdata) +{ + uint8_t p; + int ret; + + /* this function sends the client extension data (dnsname) */ + if (session->security_parameters.entity == GNUTLS_CLIENT) { + /* if the user limits for sending and receiving are + * different, that means the programmer had chosen to + * use record_size_limit instead */ + if (session->security_parameters.max_user_record_send_size != + session->security_parameters.max_user_record_recv_size) + return 0; + + if (session->security_parameters.max_user_record_send_size != + DEFAULT_MAX_RECORD_SIZE) { + ret = _gnutls_mre_record2num + (session->security_parameters. + max_user_record_send_size); + + /* it's not an error, as long as we send the + * record_size_limit extension with that value */ + if (ret < 0) + return 0; + + p = (uint8_t) ret; + ret = _gnutls_buffer_append_data(extdata, &p, 1); + if (ret < 0) + return gnutls_assert_val(ret); + + return 1; + } + + } else { /* server side */ + + if (session->internals.hsk_flags & HSK_RECORD_SIZE_LIMIT_SENT) + return 0; + + if (session->security_parameters.max_record_recv_size != + DEFAULT_MAX_RECORD_SIZE) { + ret = _gnutls_mre_record2num + (session->security_parameters. + max_record_recv_size); + if (ret < 0) + return gnutls_assert_val(ret); + + p = (uint8_t) ret; + ret = _gnutls_buffer_append_data(extdata, &p, 1); + if (ret < 0) + return gnutls_assert_val(ret); + + return 1; + } + } + + return 0; +} + + +/* Maps numbers to record sizes according to the + * extensions draft. + */ +static int _gnutls_mre_num2record(int num) +{ + switch (num) { + case 1: + return 512; + case 2: + return 1024; + case 3: + return 2048; + case 4: + return 4096; + default: + return GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER; + } +} + +/* Maps record size to numbers according to the + * extensions draft. + */ +static int _gnutls_mre_record2num(uint16_t record_size) +{ + switch (record_size) { + case 512: + return 1; + case 1024: + return 2; + case 2048: + return 3; + case 4096: + return 4; + default: + return GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER; + } + +} + +/** + * gnutls_record_get_max_size: + * @session: is a #gnutls_session_t type. + * + * Get the record size. The maximum record size is negotiated by the + * client after the first handshake message. + * + * Returns: The maximum record packet size in this connection. + **/ +size_t gnutls_record_get_max_size(gnutls_session_t session) +{ + /* Recv will hold the negotiated max record size + * always. + */ + return session->security_parameters.max_record_recv_size; +} + + +/** + * gnutls_record_set_max_size: + * @session: is a #gnutls_session_t type. + * @size: is the new size + * + * This function sets the maximum amount of plaintext sent and + * received in a record in this connection. + * + * Prior to 3.6.4, this function was implemented using a TLS extension + * called 'max fragment length', which limits the acceptable values to + * 512(=2^9), 1024(=2^10), 2048(=2^11) and 4096(=2^12). + * + * Since 3.6.4, the limit is also negotiated through a new TLS + * extension called 'record size limit', which doesn't have the + * limitation, as long as the value ranges between 512 and 16384. + * Note that while the 'record size limit' extension is preferred, not + * all TLS implementations use or even understand the extension. + * + * Deprecated: if the client can assume that the 'record size limit' + * extension is supported by the server, we recommend using + * gnutls_record_set_max_recv_size() instead. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, + * otherwise a negative error code is returned. + **/ +ssize_t gnutls_record_set_max_size(gnutls_session_t session, size_t size) +{ + if (size < MIN_RECORD_SIZE || size > DEFAULT_MAX_RECORD_SIZE) + return GNUTLS_E_INVALID_REQUEST; + + if (session->internals.handshake_in_progress) + return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + + session->security_parameters.max_user_record_send_size = size; + session->security_parameters.max_user_record_recv_size = size; + + return 0; +} + +/** + * gnutls_record_set_max_recv_size: + * @session: is a #gnutls_session_t type. + * @size: is the new size + * + * This function sets the maximum amount of plaintext received in a + * record in this connection. + * + * The limit is also negotiated through a TLS extension called 'record + * size limit'. Note that while the 'record size limit' extension is + * preferred, not all TLS implementations use or even understand the + * extension. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, + * otherwise a negative error code is returned. + * + * Since: 3.6.8 + **/ +ssize_t gnutls_record_set_max_recv_size(gnutls_session_t session, size_t size) +{ + if (size < + (session->internals.allow_small_records ? + MIN_RECORD_SIZE_SMALL : MIN_RECORD_SIZE) || + size > DEFAULT_MAX_RECORD_SIZE) + return GNUTLS_E_INVALID_REQUEST; + + if (session->internals.handshake_in_progress) + return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + + session->security_parameters.max_user_record_recv_size = size; + + return 0; +} diff --git a/lib/ext/max_record.h b/lib/ext/max_record.h new file mode 100644 index 0000000..758c8e1 --- /dev/null +++ b/lib/ext/max_record.h @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2001-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_EXT_MAX_RECORD_H +#define GNUTLS_LIB_EXT_MAX_RECORD_H + +#include <hello_ext.h> + +extern const hello_ext_entry_st ext_mod_max_record_size; + +#endif /* GNUTLS_LIB_EXT_MAX_RECORD_H */ diff --git a/lib/ext/post_handshake.c b/lib/ext/post_handshake.c new file mode 100644 index 0000000..27fe1e7 --- /dev/null +++ b/lib/ext/post_handshake.c @@ -0,0 +1,101 @@ +/* + * 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/> + * + */ + +/* This file contains the code for the Post-Handshake TLS 1.3 extension. + */ + +#include "gnutls_int.h" +#include "errors.h" +#include "num.h" +#include <hello_ext.h> +#include <ext/post_handshake.h> +#include "auth/cert.h" + +static int _gnutls_post_handshake_recv_params(gnutls_session_t session, + const uint8_t * data, + size_t data_size); +static int _gnutls_post_handshake_send_params(gnutls_session_t session, + gnutls_buffer_st * extdata); + +const hello_ext_entry_st ext_mod_post_handshake = { + .name = "Post Handshake Auth", + .tls_id = 49, + .gid = GNUTLS_EXTENSION_POST_HANDSHAKE, + .client_parse_point = GNUTLS_EXT_TLS, + .server_parse_point = GNUTLS_EXT_TLS, + .validity = GNUTLS_EXT_FLAG_TLS | GNUTLS_EXT_FLAG_CLIENT_HELLO, + .recv_func = _gnutls_post_handshake_recv_params, + .send_func = _gnutls_post_handshake_send_params, + .pack_func = NULL, + .unpack_func = NULL, + .deinit_func = NULL, + .cannot_be_overriden = 1 +}; + +static int +_gnutls_post_handshake_recv_params(gnutls_session_t session, + const uint8_t * data, size_t _data_size) +{ + const version_entry_st *vers; + + if (session->security_parameters.entity == GNUTLS_SERVER) { + vers = get_version(session); + if (unlikely(vers == NULL)) + return 0; + + if ((session->internals.flags & GNUTLS_POST_HANDSHAKE_AUTH) && + vers->post_handshake_auth) + session->security_parameters.post_handshake_auth = 1; + } + + return 0; +} + +/* returns data_size or a negative number on failure + */ +static int +_gnutls_post_handshake_send_params(gnutls_session_t session, + gnutls_buffer_st * extdata) +{ + gnutls_certificate_credentials_t cred; + const version_entry_st *max; + + if (session->security_parameters.entity != GNUTLS_CLIENT || + !(session->internals.flags & GNUTLS_POST_HANDSHAKE_AUTH)) { + /* not sent on server side */ + return 0; + } + + cred = (gnutls_certificate_credentials_t) + _gnutls_get_cred(session, GNUTLS_CRD_CERTIFICATE); + if (cred == NULL) /* no certificate authentication */ + return gnutls_assert_val(0); + + max = _gnutls_version_max(session); + if (unlikely(max == NULL)) + return gnutls_assert_val(0); + + if (max->post_handshake_auth) + return GNUTLS_E_INT_RET_0; + else + return 0; +} diff --git a/lib/ext/post_handshake.h b/lib/ext/post_handshake.h new file mode 100644 index 0000000..7a1cc7a --- /dev/null +++ b/lib/ext/post_handshake.h @@ -0,0 +1,30 @@ +/* + * 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_EXT_POST_HANDSHAKE_H +#define GNUTLS_LIB_EXT_POST_HANDSHAKE_H + +#include <hello_ext.h> + +extern const hello_ext_entry_st ext_mod_post_handshake; + +#endif /* GNUTLS_LIB_EXT_POST_HANDSHAKE_H */ diff --git a/lib/ext/pre_shared_key.c b/lib/ext/pre_shared_key.c new file mode 100644 index 0000000..8dff2b4 --- /dev/null +++ b/lib/ext/pre_shared_key.c @@ -0,0 +1,911 @@ +/* + * Copyright (C) 2017-2018 Free Software Foundation, Inc. + * Copyright (C) 2018 Red Hat, Inc. + * + * Author: Ander Juaristi, 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 "auth/psk.h" +#include "handshake.h" +#include "kx.h" +#include "secrets.h" +#include "tls13/anti_replay.h" +#include "tls13/psk_ext_parser.h" +#include "tls13/finished.h" +#include "tls13/session_ticket.h" +#include "auth/psk_passwd.h" +#include <ext/session_ticket.h> +#include <ext/pre_shared_key.h> +#include <assert.h> + +static int +compute_psk_from_ticket(const tls13_ticket_st *ticket, gnutls_datum_t *key) +{ + int ret; + + if (unlikely(ticket->prf == NULL || ticket->prf->output_size == 0)) + return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); + + key->data = gnutls_malloc(ticket->prf->output_size); + if (!key->data) { + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; + } + key->size = ticket->prf->output_size; + + ret = _tls13_expand_secret2(ticket->prf, + RESUMPTION_LABEL, sizeof(RESUMPTION_LABEL)-1, + ticket->nonce, ticket->nonce_size, + ticket->resumption_master_secret, + key->size, + key->data); + if (ret < 0) + gnutls_assert(); + + return ret; +} + +static int +compute_binder_key(const mac_entry_st *prf, + const uint8_t *key, size_t keylen, + bool resuming, + void *out) +{ + int ret; + const char ext_label[] = EXT_BINDER_LABEL; + const size_t ext_label_len = sizeof(ext_label) - 1; + const char res_label[] = RES_BINDER_LABEL; + const size_t res_label_len = sizeof(res_label) - 1; + const char *label = resuming ? res_label : ext_label; + size_t label_len = resuming ? res_label_len : ext_label_len; + uint8_t tmp_key[MAX_HASH_SIZE]; + + /* Compute HKDF-Extract(0, psk) */ + ret = _tls13_init_secret2(prf, key, keylen, tmp_key); + if (ret < 0) + return ret; + + /* Compute Derive-Secret(secret, label, transcript_hash) */ + ret = _tls13_derive_secret2(prf, label, label_len, + NULL, 0, tmp_key, out); + if (ret < 0) + return ret; + + return 0; +} + +static int +compute_psk_binder(gnutls_session_t session, + const mac_entry_st *prf, unsigned binders_length, + int exts_length, int ext_offset, + const gnutls_datum_t *psk, const gnutls_datum_t *client_hello, + bool resuming, void *out) +{ + int ret; + unsigned client_hello_pos, extensions_len_pos; + gnutls_buffer_st handshake_buf; + uint8_t binder_key[MAX_HASH_SIZE]; + + _gnutls_buffer_init(&handshake_buf); + + if (session->security_parameters.entity == GNUTLS_CLIENT) { + if (session->internals.hsk_flags & HSK_HRR_RECEIVED) { + ret = gnutls_buffer_append_data(&handshake_buf, + (const void *) session->internals.handshake_hash_buffer.data, + session->internals.handshake_hash_buffer.length); + if (ret < 0) { + gnutls_assert(); + goto error; + } + } + + client_hello_pos = handshake_buf.length; + ret = gnutls_buffer_append_data(&handshake_buf, client_hello->data, + client_hello->size); + if (ret < 0) { + gnutls_assert(); + goto error; + } + + /* This is a ClientHello message */ + handshake_buf.data[client_hello_pos] = GNUTLS_HANDSHAKE_CLIENT_HELLO; + + /* At this point we have not yet added the binders to the ClientHello, + * but we have to overwrite the size field, pretending as if binders + * of the correct length were present. + */ + _gnutls_write_uint24(handshake_buf.length - client_hello_pos + binders_length - 2, &handshake_buf.data[client_hello_pos + 1]); + _gnutls_write_uint16(handshake_buf.length - client_hello_pos + binders_length - ext_offset, + &handshake_buf.data[client_hello_pos + ext_offset]); + extensions_len_pos = handshake_buf.length - client_hello_pos - exts_length - 2; + _gnutls_write_uint16(exts_length + binders_length + 2, + &handshake_buf.data[client_hello_pos + extensions_len_pos]); + } else { + if (session->internals.hsk_flags & HSK_HRR_SENT) { + if (unlikely(session->internals.handshake_hash_buffer.length <= client_hello->size)) { + ret = gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER); + goto error; + } + + ret = gnutls_buffer_append_data(&handshake_buf, + session->internals.handshake_hash_buffer.data, + session->internals.handshake_hash_buffer.length - client_hello->size); + if (ret < 0) { + gnutls_assert(); + goto error; + } + } + + if (unlikely(client_hello->size <= binders_length)) { + ret = gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER); + goto error; + } + + ret = gnutls_buffer_append_data(&handshake_buf, + (const void *) client_hello->data, + client_hello->size - binders_length); + if (ret < 0) { + gnutls_assert(); + goto error; + } + } + + ret = compute_binder_key(prf, + psk->data, psk->size, resuming, + binder_key); + if (ret < 0) { + gnutls_assert(); + goto error; + } + + ret = _gnutls13_compute_finished(prf, binder_key, + &handshake_buf, + out); + if (ret < 0) { + gnutls_assert(); + goto error; + } + + ret = 0; +error: + _gnutls_buffer_clear(&handshake_buf); + return ret; +} + +static int +generate_early_secrets(gnutls_session_t session, + const mac_entry_st *prf) +{ + int ret; + + ret = _tls13_derive_secret2(prf, EARLY_TRAFFIC_LABEL, sizeof(EARLY_TRAFFIC_LABEL)-1, + session->internals.handshake_hash_buffer.data, + session->internals.handshake_hash_buffer_client_hello_len, + session->key.proto.tls13.temp_secret, + session->key.proto.tls13.e_ckey); + if (ret < 0) + return gnutls_assert_val(ret); + + ret = _gnutls_call_keylog_func(session, "CLIENT_EARLY_TRAFFIC_SECRET", + session->key.proto.tls13.e_ckey, + prf->output_size); + if (ret < 0) + return gnutls_assert_val(ret); + + ret = _tls13_derive_secret2(prf, EARLY_EXPORTER_MASTER_LABEL, sizeof(EARLY_EXPORTER_MASTER_LABEL)-1, + session->internals.handshake_hash_buffer.data, + session->internals.handshake_hash_buffer_client_hello_len, + session->key.proto.tls13.temp_secret, + session->key.proto.tls13.ap_expkey); + if (ret < 0) + return gnutls_assert_val(ret); + + ret = _gnutls_call_keylog_func(session, "EARLY_EXPORTER_SECRET", + session->key.proto.tls13.ap_expkey, + prf->output_size); + if (ret < 0) + return gnutls_assert_val(ret); + + return 0; +} + +/* Calculate TLS 1.3 Early Secret and the derived secrets from the + * selected PSK. */ +int +_gnutls_generate_early_secrets_for_psk(gnutls_session_t session) +{ + const uint8_t *psk; + size_t psk_size; + const mac_entry_st *prf; + int ret; + + psk = session->key.binders[0].psk.data; + psk_size = session->key.binders[0].psk.size; + prf = session->key.binders[0].prf; + + if (unlikely(psk_size == 0)) + return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + + ret = _tls13_init_secret2(prf, psk, psk_size, + session->key.proto.tls13.temp_secret); + if (ret < 0) + return gnutls_assert_val(ret); + + session->key.proto.tls13.temp_secret_size = prf->output_size; + + ret = generate_early_secrets(session, session->key.binders[0].prf); + if (ret < 0) + return gnutls_assert_val(ret); + + return 0; +} + +static int +client_send_params(gnutls_session_t session, + gnutls_buffer_t extdata, + const gnutls_psk_client_credentials_t cred) +{ + int ret, ext_offset = 0; + uint8_t binder_value[MAX_HASH_SIZE]; + size_t spos; + gnutls_datum_t username = {NULL, 0}; + gnutls_datum_t user_key = {NULL, 0}, rkey = {NULL, 0}; + unsigned client_hello_len; + unsigned next_idx; + const mac_entry_st *prf_res = NULL; + const mac_entry_st *prf_psk = NULL; + struct timespec cur_time; + uint32_t ticket_age, ob_ticket_age; + int free_username = 0; + psk_auth_info_t info = NULL; + unsigned psk_id_len = 0; + unsigned binders_len, binders_pos; + tls13_ticket_st *ticket = &session->internals.tls13_ticket; + + if (((session->internals.flags & GNUTLS_NO_TICKETS) || + session->internals.tls13_ticket.ticket.data == NULL) && + (!cred || !_gnutls_have_psk_credentials(cred, session))) { + + return 0; + } + + binders_len = 0; + + /* placeholder to be filled later */ + spos = extdata->length; + ret = _gnutls_buffer_append_prefix(extdata, 16, 0); + if (ret < 0) + return gnutls_assert_val(ret); + + /* First, let's see if we have a session ticket to send */ + if (!(session->internals.flags & GNUTLS_NO_TICKETS) && + ticket->ticket.data != NULL) { + + /* We found a session ticket */ + if (unlikely(ticket->prf == NULL)) { + tls13_ticket_deinit(ticket); + ret = gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); + goto cleanup; + } + + prf_res = ticket->prf; + + gnutls_gettime(&cur_time); + if (unlikely(_gnutls_timespec_cmp(&cur_time, + &ticket->arrival_time) < 0)) { + gnutls_assert(); + tls13_ticket_deinit(ticket); + goto ignore_ticket; + } + + /* Check whether the ticket is stale */ + ticket_age = timespec_sub_ms(&cur_time, &ticket->arrival_time); + if (ticket_age / 1000 > ticket->lifetime) { + tls13_ticket_deinit(ticket); + goto ignore_ticket; + } + + ret = compute_psk_from_ticket(ticket, &rkey); + if (ret < 0) { + tls13_ticket_deinit(ticket); + goto ignore_ticket; + } + + /* Calculate obfuscated ticket age, in milliseconds, mod 2^32 */ + ob_ticket_age = ticket_age + ticket->age_add; + + if ((ret = _gnutls_buffer_append_data_prefix(extdata, 16, + ticket->ticket.data, + ticket->ticket.size)) < 0) { + gnutls_assert(); + goto cleanup; + } + + /* Now append the obfuscated ticket age */ + if ((ret = _gnutls_buffer_append_prefix(extdata, 32, ob_ticket_age)) < 0) { + gnutls_assert(); + goto cleanup; + } + + psk_id_len += 6 + ticket->ticket.size; + binders_len += 1 + _gnutls_mac_get_algo_len(prf_res); + } + + ignore_ticket: + if (cred && _gnutls_have_psk_credentials(cred, session)) { + gnutls_datum_t tkey; + + if (cred->binder_algo == NULL) { + gnutls_assert(); + ret = gnutls_assert_val(GNUTLS_E_INSUFFICIENT_CREDENTIALS); + goto cleanup; + } + + prf_psk = cred->binder_algo; + + ret = _gnutls_find_psk_key(session, cred, &username, &tkey, &free_username); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + if (username.size == 0 || username.size > UINT16_MAX) { + ret = gnutls_assert_val(GNUTLS_E_INVALID_PASSWORD); + goto cleanup; + } + + if (!free_username) { + /* we need to copy the key */ + ret = _gnutls_set_datum(&user_key, tkey.data, tkey.size); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + } else { + user_key.data = tkey.data; + user_key.size = tkey.size; + } + + ret = _gnutls_auth_info_init(session, GNUTLS_CRD_PSK, sizeof(psk_auth_info_st), 1); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + info = _gnutls_get_auth_info(session, GNUTLS_CRD_PSK); + assert(info != NULL); + + ret = _gnutls_copy_psk_username(info, username); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + + if ((ret = _gnutls_buffer_append_data_prefix(extdata, 16, + username.data, + username.size)) < 0) { + gnutls_assert(); + goto cleanup; + } + + /* Now append the obfuscated ticket age */ + if ((ret = _gnutls_buffer_append_prefix(extdata, 32, 0)) < 0) { + gnutls_assert(); + goto cleanup; + } + + psk_id_len += 6 + username.size; + binders_len += 1 + _gnutls_mac_get_algo_len(prf_psk); + } + + /* if no tickets or identities to be sent */ + if (psk_id_len == 0) { + /* reset extensions buffer */ + extdata->length = spos; + return 0; + } + + _gnutls_write_uint16(psk_id_len, &extdata->data[spos]); + + binders_pos = extdata->length-spos; + ext_offset = _gnutls_ext_get_extensions_offset(session); + + /* Compute the binders. extdata->data points to the start + * of this client hello. */ + assert(extdata->length >= sizeof(mbuffer_st)); + assert(ext_offset >= (ssize_t)sizeof(mbuffer_st)); + ext_offset -= sizeof(mbuffer_st); + client_hello_len = extdata->length-sizeof(mbuffer_st); + + next_idx = 0; + + ret = _gnutls_buffer_append_prefix(extdata, 16, binders_len); + if (ret < 0) { + gnutls_assert_val(ret); + goto cleanup; + } + + if (prf_res && rkey.size > 0) { + gnutls_datum_t client_hello; + + client_hello.data = extdata->data+sizeof(mbuffer_st); + client_hello.size = client_hello_len; + + ret = compute_psk_binder(session, prf_res, + binders_len, binders_pos, + ext_offset, &rkey, &client_hello, 1, + binder_value); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + /* Associate the selected pre-shared key with the session */ + gnutls_free(session->key.binders[next_idx].psk.data); + session->key.binders[next_idx].psk.data = rkey.data; + session->key.binders[next_idx].psk.size = rkey.size; + rkey.data = NULL; + + session->key.binders[next_idx].prf = prf_res; + session->key.binders[next_idx].resumption = 1; + session->key.binders[next_idx].idx = next_idx; + + _gnutls_handshake_log("EXT[%p]: sent PSK resumption identity (%d)\n", session, next_idx); + + next_idx++; + + /* Add the binder */ + ret = _gnutls_buffer_append_data_prefix(extdata, 8, binder_value, prf_res->output_size); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + session->internals.hsk_flags |= HSK_TLS13_TICKET_SENT; + } + + if (prf_psk && user_key.size > 0 && info) { + gnutls_datum_t client_hello; + + client_hello.data = extdata->data+sizeof(mbuffer_st); + client_hello.size = client_hello_len; + + ret = compute_psk_binder(session, prf_psk, + binders_len, binders_pos, + ext_offset, &user_key, &client_hello, 0, + binder_value); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + /* Associate the selected pre-shared key with the session */ + gnutls_free(session->key.binders[next_idx].psk.data); + session->key.binders[next_idx].psk.data = user_key.data; + session->key.binders[next_idx].psk.size = user_key.size; + user_key.data = NULL; + + session->key.binders[next_idx].prf = prf_psk; + session->key.binders[next_idx].resumption = 0; + session->key.binders[next_idx].idx = next_idx; + + _gnutls_handshake_log("EXT[%p]: sent PSK identity '%s' (%d)\n", session, info->username, next_idx); + + next_idx++; + + /* Add the binder */ + ret = _gnutls_buffer_append_data_prefix(extdata, 8, binder_value, prf_psk->output_size); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + } + + ret = 0; + +cleanup: + if (free_username) + _gnutls_free_datum(&username); + + _gnutls_free_temp_key_datum(&user_key); + _gnutls_free_temp_key_datum(&rkey); + + return ret; +} + +static int +server_send_params(gnutls_session_t session, gnutls_buffer_t extdata) +{ + int ret; + + if (!(session->internals.hsk_flags & HSK_PSK_SELECTED)) + return 0; + + ret = _gnutls_buffer_append_prefix(extdata, 16, + session->key.binders[0].idx); + if (ret < 0) + return gnutls_assert_val(ret); + + return 2; +} + +static int server_recv_params(gnutls_session_t session, + const unsigned char *data, size_t len, + const gnutls_psk_server_credentials_t pskcred) +{ + int ret; + const mac_entry_st *prf; + gnutls_datum_t full_client_hello; + uint8_t binder_value[MAX_HASH_SIZE]; + uint16_t psk_index, i; + gnutls_datum_t binder_recvd = { NULL, 0 }; + gnutls_datum_t key = {NULL, 0}; + psk_ext_parser_st psk_parser; + psk_ext_iter_st psk_iter; + struct psk_st psk; + psk_auth_info_t info; + tls13_ticket_st ticket_data; + /* These values should be set properly when session ticket is accepted. */ + uint32_t ticket_age = UINT32_MAX; + struct timespec ticket_creation_time = { 0, 0 }; + bool resuming; + bool refuse_early_data = false; + + ret = _gnutls13_psk_ext_parser_init(&psk_parser, data, len); + if (ret < 0) { + /* No PSKs advertised by client */ + if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) + return 0; + return gnutls_assert_val(ret); + } + + _gnutls13_psk_ext_iter_init(&psk_iter, &psk_parser); + for (psk_index = 0; ; psk_index++) { + ret = _gnutls13_psk_ext_iter_next_identity(&psk_iter, &psk); + if (ret < 0) { + /* We couldn't find any usable PSK */ + if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) + return 0; + return gnutls_assert_val(ret); + } + + /* This will unpack the session ticket if it is well + * formed and has the expected name */ + if (!(session->internals.flags & GNUTLS_NO_TICKETS) && + _gnutls13_unpack_session_ticket(session, &psk.identity, &ticket_data) == 0) { + prf = ticket_data.prf; + + session->internals.resumption_requested = 1; + + /* Check whether ticket is stale or not */ + ticket_age = psk.ob_ticket_age - ticket_data.age_add; + if (ticket_age / 1000 > ticket_data.lifetime) { + gnutls_assert(); + tls13_ticket_deinit(&ticket_data); + continue; + } + + ret = compute_psk_from_ticket(&ticket_data, &key); + if (ret < 0) { + gnutls_assert(); + tls13_ticket_deinit(&ticket_data); + continue; + } + + memcpy(&ticket_creation_time, + &ticket_data.creation_time, + sizeof(struct timespec)); + + tls13_ticket_deinit(&ticket_data); + + resuming = 1; + break; + } else if (pskcred && + psk.ob_ticket_age == 0 && + psk.identity.size > 0 && psk.identity.size <= MAX_USERNAME_SIZE) { + prf = pskcred->binder_algo; + + /* this fails only on configuration errors; as such we always + * return its error code in that case */ + ret = _gnutls_psk_pwd_find_entry(session, (char *) psk.identity.data, psk.identity.size, &key); + if (ret < 0) + return gnutls_assert_val(ret); + + resuming = 0; + break; + } + } + + _gnutls13_psk_ext_iter_init(&psk_iter, &psk_parser); + for (i = 0; i <= psk_index; i++) { + ret = _gnutls13_psk_ext_iter_next_binder(&psk_iter, &binder_recvd); + if (ret < 0) { + gnutls_assert(); + /* We couldn't extract binder */ + if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) + ret = GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER; + goto fail; + } + } + + /* Get full ClientHello */ + if (!_gnutls_ext_get_full_client_hello(session, &full_client_hello)) { + ret = GNUTLS_E_INTERNAL_ERROR; + gnutls_assert(); + goto fail; + } + + /* Compute the binder value for this PSK */ + ret = compute_psk_binder(session, prf, psk_parser.binders_len+2, 0, 0, + &key, &full_client_hello, resuming, + binder_value); + if (ret < 0) { + gnutls_assert(); + goto fail; + } + + if (_gnutls_mac_get_algo_len(prf) != binder_recvd.size || + gnutls_memcmp(binder_value, binder_recvd.data, binder_recvd.size)) { + gnutls_assert(); + ret = GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER; + goto fail; + } + + if (session->internals.hsk_flags & HSK_PSK_KE_MODE_DHE_PSK) + _gnutls_handshake_log("EXT[%p]: selected DHE-PSK mode\n", session); + else { + reset_cand_groups(session); + _gnutls_handshake_log("EXT[%p]: selected PSK mode\n", session); + } + + /* save the username in psk_auth_info to make it available + * using gnutls_psk_server_get_username() */ + if (!resuming) { + assert(psk.identity.size <= MAX_USERNAME_SIZE); + + ret = _gnutls_auth_info_init(session, GNUTLS_CRD_PSK, sizeof(psk_auth_info_st), 1); + if (ret < 0) { + gnutls_assert(); + goto fail; + } + + info = _gnutls_get_auth_info(session, GNUTLS_CRD_PSK); + assert(info != NULL); + + ret = _gnutls_copy_psk_username(info, psk.identity); + if (ret < 0) { + gnutls_assert(); + goto fail; + } + + _gnutls_handshake_log("EXT[%p]: selected PSK identity: %s (%d)\n", session, info->username, psk_index); + + /* We currently only support early data in resuming connection, + * due to lack of API function to associate encryption + * parameters with external PSK. + */ + refuse_early_data = true; + } else { + if (session->internals.hsk_flags & HSK_EARLY_DATA_IN_FLIGHT) { + if (session->internals.anti_replay) { + ret = _gnutls_anti_replay_check(session->internals.anti_replay, + ticket_age, + &ticket_creation_time, + &binder_recvd); + if (ret < 0) { + refuse_early_data = true; + _gnutls_handshake_log("EXT[%p]: replay detected; rejecting early data\n", + session); + } + } else { + refuse_early_data = true; + _gnutls_handshake_log("EXT[%p]: anti-replay is not enabled; rejecting early data\n", + session); + } + } + + session->internals.resumed = true; + _gnutls_handshake_log("EXT[%p]: selected resumption PSK identity (%d)\n", session, psk_index); + } + + session->internals.hsk_flags |= HSK_PSK_SELECTED; + + if ((session->internals.flags & GNUTLS_ENABLE_EARLY_DATA) && + (session->internals.hsk_flags & HSK_EARLY_DATA_IN_FLIGHT) && + !refuse_early_data && + !(session->internals.hsk_flags & HSK_HRR_SENT)) { + session->internals.hsk_flags |= HSK_EARLY_DATA_ACCEPTED; + _gnutls_handshake_log("EXT[%p]: early data accepted\n", + session); + } + + /* Reference the selected pre-shared key */ + session->key.binders[0].psk.data = key.data; + session->key.binders[0].psk.size = key.size; + + session->key.binders[0].idx = psk_index; + session->key.binders[0].prf = prf; + session->key.binders[0].resumption = resuming; + + ret = _gnutls_generate_early_secrets_for_psk(session); + if (ret < 0) { + gnutls_assert(); + goto fail; + } + + return 0; + + fail: + gnutls_free(key.data); + return ret; +} + +/* + * Return values for this function: + * - 0 : Not applicable. + * - >0 : Ok. Return size of extension data. + * - GNUTLS_E_INT_RET_0 : Size of extension data is zero. + * - <0 : There's been an error. + * + * In the client, generates the PskIdentity and PskBinderEntry messages. + * + * PskIdentity identities<7..2^16-1>; + * PskBinderEntry binders<33..2^16-1>; + * + * struct { + * opaque identity<1..2^16-1>; + * uint32 obfuscated_ticket_age; + * } PskIdentity; + * + * opaque PskBinderEntry<32..255>; + * + * The server sends the selected identity, which is a zero-based index + * of the PSKs offered by the client: + * + * struct { + * uint16 selected_identity; + * } PreSharedKeyExtension; + */ +static int _gnutls_psk_send_params(gnutls_session_t session, + gnutls_buffer_t extdata) +{ + gnutls_psk_client_credentials_t cred = NULL; + const version_entry_st *vers; + + if (session->security_parameters.entity == GNUTLS_CLIENT) { + vers = _gnutls_version_max(session); + + if (!vers || !vers->tls13_sem) + return 0; + + if (session->internals.hsk_flags & HSK_PSK_KE_MODES_SENT) { + cred = (gnutls_psk_client_credentials_t) + _gnutls_get_cred(session, GNUTLS_CRD_PSK); + } + + if ((session->internals.flags & GNUTLS_NO_TICKETS) && !session->internals.priorities->have_psk) + return 0; + + return client_send_params(session, extdata, cred); + } else { + vers = get_version(session); + + if (!vers || !vers->tls13_sem) + return 0; + + if ((session->internals.flags & GNUTLS_NO_TICKETS) && !session->internals.priorities->have_psk) + return 0; + + if (session->internals.hsk_flags & HSK_PSK_KE_MODES_RECEIVED) + return server_send_params(session, extdata); + else + return 0; + } +} + +static void swap_binders(gnutls_session_t session) +{ + struct binder_data_st tmp; + + memcpy(&tmp, &session->key.binders[0], sizeof(struct binder_data_st)); + memcpy(&session->key.binders[0], &session->key.binders[1], sizeof(struct binder_data_st)); + memcpy(&session->key.binders[1], &tmp, sizeof(struct binder_data_st)); +} + +/* + * Return values for this function: + * - 0 : Not applicable. + * - >0 : Ok. Return size of extension data. + * - <0 : There's been an error. + */ +static int _gnutls_psk_recv_params(gnutls_session_t session, + const unsigned char *data, size_t len) +{ + unsigned i; + gnutls_psk_server_credentials_t pskcred; + const version_entry_st *vers = get_version(session); + int ret; + + if (!vers || !vers->tls13_sem) + return 0; + + if (session->security_parameters.entity == GNUTLS_CLIENT) { + if (session->internals.hsk_flags & HSK_PSK_KE_MODES_SENT) { + uint16_t selected_identity = _gnutls_read_uint16(data); + + for (i=0;i<sizeof(session->key.binders)/sizeof(session->key.binders[0]);i++) { + if (session->key.binders[i].prf != NULL && session->key.binders[i].idx == selected_identity) { + if (session->key.binders[i].resumption) { + session->internals.resumed = true; + _gnutls_handshake_log("EXT[%p]: selected PSK-resumption mode\n", session); + } else { + _gnutls_handshake_log("EXT[%p]: selected PSK mode\n", session); + } + + /* different PSK is selected, than the one we calculated early secrets */ + if (i != 0) { + /* ensure that selected binder is set on (our) index zero */ + swap_binders(session); + + ret = _gnutls_generate_early_secrets_for_psk(session); + if (ret < 0) + return gnutls_assert_val(ret); + } + session->internals.hsk_flags |= HSK_PSK_SELECTED; + } + } + + return 0; + } else { + return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_EXTENSION); + } + } else { + if (session->internals.hsk_flags & HSK_PSK_KE_MODES_RECEIVED) { + if (session->internals.hsk_flags & HSK_PSK_KE_MODE_INVALID) { + /* We received a "psk_ke_modes" extension, but with a value we don't support */ + return 0; + } + + pskcred = (gnutls_psk_server_credentials_t) + _gnutls_get_cred(session, GNUTLS_CRD_PSK); + + /* If there are no PSK credentials, this extension is not applicable, + * so we return zero. */ + if (pskcred == NULL && (session->internals.flags & GNUTLS_NO_TICKETS)) + return 0; + + return server_recv_params(session, data, len, pskcred); + } else { + return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_EXTENSION); + } + } +} + +const hello_ext_entry_st ext_mod_pre_shared_key = { + .name = "Pre Shared Key", + .tls_id = PRE_SHARED_KEY_TLS_ID, + .gid = GNUTLS_EXTENSION_PRE_SHARED_KEY, + .client_parse_point = GNUTLS_EXT_TLS, + .server_parse_point = GNUTLS_EXT_TLS, + .validity = GNUTLS_EXT_FLAG_TLS | GNUTLS_EXT_FLAG_CLIENT_HELLO | GNUTLS_EXT_FLAG_TLS13_SERVER_HELLO, + .send_func = _gnutls_psk_send_params, + .recv_func = _gnutls_psk_recv_params +}; diff --git a/lib/ext/pre_shared_key.h b/lib/ext/pre_shared_key.h new file mode 100644 index 0000000..f1ea62c --- /dev/null +++ b/lib/ext/pre_shared_key.h @@ -0,0 +1,23 @@ +#ifndef GNUTLS_LIB_EXT_PRE_SHARED_KEY_H +#define GNUTLS_LIB_EXT_PRE_SHARED_KEY_H + +#include "auth/psk.h" +#include <hello_ext.h> +#include "tls13/session_ticket.h" + +#define PRE_SHARED_KEY_TLS_ID 41 + +extern const hello_ext_entry_st ext_mod_pre_shared_key; + +inline static +unsigned _gnutls_have_psk_credentials(const gnutls_psk_client_credentials_t cred, gnutls_session_t session) +{ + if ((cred->get_function || cred->username.data) && session->internals.priorities->have_psk) + return 1; + else + return 0; +} + +int _gnutls_generate_early_secrets_for_psk(gnutls_session_t session); + +#endif /* GNUTLS_LIB_EXT_PRE_SHARED_KEY_H */ diff --git a/lib/ext/psk_ke_modes.c b/lib/ext/psk_ke_modes.c new file mode 100644 index 0000000..cc28536 --- /dev/null +++ b/lib/ext/psk_ke_modes.c @@ -0,0 +1,205 @@ +/* + * Copyright (C) 2017 Free Software Foundation, Inc. + * + * Author: Ander Juaristi + * + * 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 "ext/psk_ke_modes.h" +#include "ext/pre_shared_key.h" +#include <assert.h> + +#define PSK_KE 0 +#define PSK_DHE_KE 1 + +static int +psk_ke_modes_send_params(gnutls_session_t session, + gnutls_buffer_t extdata) +{ + int ret; + const version_entry_st *vers; + uint8_t data[2]; + unsigned pos, i; + unsigned have_dhpsk = 0; + unsigned have_psk = 0; + + /* Server doesn't send psk_key_exchange_modes */ + if (session->security_parameters.entity == GNUTLS_SERVER) + return 0; + + /* If session ticket is disabled and no PSK key exchange is + * enabled, don't send the extension */ + if ((session->internals.flags & GNUTLS_NO_TICKETS) && + !session->internals.priorities->have_psk) + return 0; + + vers = _gnutls_version_max(session); + if (!vers || !vers->tls13_sem) + return 0; + + /* We send the list prioritized according to our preferences as a convention + * (used throughout the protocol), even if the protocol doesn't mandate that + * for this particular message. That way we can keep the TLS 1.2 semantics/ + * prioritization when negotiating PSK or DHE-PSK. Receiving servers would + * very likely respect our prioritization if they parse the message serially. */ + pos = 0; + for (i=0;i<session->internals.priorities->_kx.num_priorities;i++) { + if (session->internals.priorities->_kx.priorities[i] == GNUTLS_KX_PSK && !have_psk) { + assert(pos <= 1); + data[pos++] = PSK_KE; + session->internals.hsk_flags |= HSK_PSK_KE_MODE_PSK; + have_psk = 1; + } else if ((session->internals.priorities->_kx.priorities[i] == GNUTLS_KX_DHE_PSK || + session->internals.priorities->_kx.priorities[i] == GNUTLS_KX_ECDHE_PSK) && !have_dhpsk) { + assert(pos <= 1); + data[pos++] = PSK_DHE_KE; + session->internals.hsk_flags |= HSK_PSK_KE_MODE_DHE_PSK; + have_dhpsk = 1; + } + + if (have_psk && have_dhpsk) + break; + } + + /* For session resumption we need to send at least one */ + if (pos == 0) { + if (session->internals.flags & GNUTLS_NO_TICKETS) + return 0; + + data[pos++] = PSK_DHE_KE; + data[pos++] = PSK_KE; + session->internals.hsk_flags |= HSK_PSK_KE_MODE_DHE_PSK; + session->internals.hsk_flags |= HSK_PSK_KE_MODE_PSK; + } + + ret = _gnutls_buffer_append_data_prefix(extdata, 8, data, pos); + if (ret < 0) + return gnutls_assert_val(ret); + + session->internals.hsk_flags |= HSK_PSK_KE_MODES_SENT; + + return 0; +} + +#define MAX_POS INT_MAX + +/* + * Since we only support ECDHE-authenticated PSKs, the server + * just verifies that a "psk_key_exchange_modes" extension was received, + * and that it contains the value one. + */ +static int +psk_ke_modes_recv_params(gnutls_session_t session, + const unsigned char *data, size_t len) +{ + uint8_t ke_modes_len; + const version_entry_st *vers = get_version(session); + gnutls_psk_server_credentials_t cred; + int dhpsk_pos = MAX_POS; + int psk_pos = MAX_POS; + int cli_psk_pos = MAX_POS; + int cli_dhpsk_pos = MAX_POS; + unsigned i; + + /* Client doesn't receive psk_key_exchange_modes */ + if (session->security_parameters.entity == GNUTLS_CLIENT) + return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_EXTENSION); + + /* we set hsk_flags to HSK_PSK_KE_MODE_INVALID on failure to ensure that + * when we parse the pre-shared key extension we detect PSK_KE_MODES as + * received. */ + if (!vers || !vers->tls13_sem) { + session->internals.hsk_flags |= HSK_PSK_KE_MODE_INVALID; + return gnutls_assert_val(0); + } + + cred = (gnutls_psk_server_credentials_t)_gnutls_get_cred(session, GNUTLS_CRD_PSK); + if (cred == NULL && (session->internals.flags & GNUTLS_NO_TICKETS)) { + session->internals.hsk_flags |= HSK_PSK_KE_MODE_INVALID; + return gnutls_assert_val(0); + } + + DECR_LEN(len, 1); + ke_modes_len = *(data++); + + for (i=0;i<session->internals.priorities->_kx.num_priorities;i++) { + if (session->internals.priorities->_kx.priorities[i] == GNUTLS_KX_PSK && psk_pos == MAX_POS) { + psk_pos = i; + } else if ((session->internals.priorities->_kx.priorities[i] == GNUTLS_KX_DHE_PSK || + session->internals.priorities->_kx.priorities[i] == GNUTLS_KX_ECDHE_PSK) && + dhpsk_pos == MAX_POS) { + dhpsk_pos = i; + } + + if (dhpsk_pos != MAX_POS && psk_pos != MAX_POS) + break; + } + + if (psk_pos == MAX_POS && dhpsk_pos == MAX_POS) { + if (!(session->internals.flags & GNUTLS_NO_TICKETS)) + dhpsk_pos = 0; + else if (session->internals.priorities->groups.size == 0) + return gnutls_assert_val(0); + } + + for (i=0;i<ke_modes_len;i++) { + DECR_LEN(len, 1); + if (data[i] == PSK_DHE_KE) + cli_dhpsk_pos = i; + else if (data[i] == PSK_KE) + cli_psk_pos = i; + + _gnutls_handshake_log("EXT[%p]: PSK KE mode %.2x received\n", + session, (unsigned)data[i]); + if (cli_psk_pos != MAX_POS && cli_dhpsk_pos != MAX_POS) + break; + } + + if (session->internals.priorities->server_precedence) { + if (dhpsk_pos != MAX_POS && cli_dhpsk_pos != MAX_POS && (dhpsk_pos < psk_pos || cli_psk_pos == MAX_POS)) + session->internals.hsk_flags |= HSK_PSK_KE_MODE_DHE_PSK; + else if (psk_pos != MAX_POS && cli_psk_pos != MAX_POS && (psk_pos < dhpsk_pos || cli_dhpsk_pos == MAX_POS)) + session->internals.hsk_flags |= HSK_PSK_KE_MODE_PSK; + } else { + if (dhpsk_pos != MAX_POS && cli_dhpsk_pos != MAX_POS && (cli_dhpsk_pos < cli_psk_pos || psk_pos == MAX_POS)) + session->internals.hsk_flags |= HSK_PSK_KE_MODE_DHE_PSK; + else if (psk_pos != MAX_POS && cli_psk_pos != MAX_POS && (cli_psk_pos < cli_dhpsk_pos || dhpsk_pos == MAX_POS)) + session->internals.hsk_flags |= HSK_PSK_KE_MODE_PSK; + } + + if ((session->internals.hsk_flags & HSK_PSK_KE_MODE_PSK) || + (session->internals.hsk_flags & HSK_PSK_KE_MODE_DHE_PSK)) { + + return 0; + } else { + session->internals.hsk_flags |= HSK_PSK_KE_MODE_INVALID; + return gnutls_assert_val(0); + } +} + +const hello_ext_entry_st ext_mod_psk_ke_modes = { + .name = "PSK Key Exchange Modes", + .tls_id = 45, + .gid = GNUTLS_EXTENSION_PSK_KE_MODES, + .client_parse_point = GNUTLS_EXT_TLS, + .server_parse_point = GNUTLS_EXT_TLS, + .validity = GNUTLS_EXT_FLAG_TLS | GNUTLS_EXT_FLAG_CLIENT_HELLO | GNUTLS_EXT_FLAG_TLS13_SERVER_HELLO, + .send_func = psk_ke_modes_send_params, + .recv_func = psk_ke_modes_recv_params +}; diff --git a/lib/ext/psk_ke_modes.h b/lib/ext/psk_ke_modes.h new file mode 100644 index 0000000..56876a9 --- /dev/null +++ b/lib/ext/psk_ke_modes.h @@ -0,0 +1,8 @@ +#ifndef GNUTLS_LIB_EXT_PSK_KE_MODES_H +#define GNUTLS_LIB_EXT_PSK_KE_MODES_H + +#include <hello_ext.h> + +extern const hello_ext_entry_st ext_mod_psk_ke_modes; + +#endif /* GNUTLS_LIB_EXT_PSK_KE_MODES_H */ diff --git a/lib/ext/record_size_limit.c b/lib/ext/record_size_limit.c new file mode 100644 index 0000000..9398b18 --- /dev/null +++ b/lib/ext/record_size_limit.c @@ -0,0 +1,157 @@ +/* + * Copyright (C) 2018 Red Hat, Inc. + * + * Author: 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/> + * + */ + +/* This file contains the code for the Record Size Limit TLS extension. + */ + +#include "gnutls_int.h" +#include "errors.h" +#include "num.h" +#include <hello_ext.h> +#include <ext/record_size_limit.h> + +static int _gnutls_record_size_limit_recv_params(gnutls_session_t session, + const uint8_t * data, + size_t data_size); +static int _gnutls_record_size_limit_send_params(gnutls_session_t session, + gnutls_buffer_st * extdata); + +const hello_ext_entry_st ext_mod_record_size_limit = { + .name = "Record Size Limit", + .tls_id = 28, + .gid = GNUTLS_EXTENSION_RECORD_SIZE_LIMIT, + .client_parse_point = GNUTLS_EXT_MANDATORY, + .server_parse_point = GNUTLS_EXT_MANDATORY, + .validity = GNUTLS_EXT_FLAG_TLS | GNUTLS_EXT_FLAG_DTLS | GNUTLS_EXT_FLAG_CLIENT_HELLO | + GNUTLS_EXT_FLAG_EE | GNUTLS_EXT_FLAG_TLS12_SERVER_HELLO, + .recv_func = _gnutls_record_size_limit_recv_params, + .send_func = _gnutls_record_size_limit_send_params +}; + +static int +_gnutls_record_size_limit_recv_params(gnutls_session_t session, + const uint8_t * data, size_t data_size) +{ + ssize_t new_size; + const version_entry_st *vers; + + DECR_LEN(data_size, 2); + if (data_size != 0) + return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH); + new_size = _gnutls_read_uint16(data); + + /* protocol error */ + if (new_size < 64) + return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER); + + session->internals.hsk_flags |= HSK_RECORD_SIZE_LIMIT_RECEIVED; + + /* we do not want to accept sizes outside of our supported range */ + if (new_size < + (session->internals.allow_small_records ? + MIN_RECORD_SIZE_SMALL : MIN_RECORD_SIZE)) { + /* for server, reject it by omitting the extension in the reply */ + if (session->security_parameters.entity == GNUTLS_SERVER) { + _gnutls_handshake_log("EXT[%p]: client requested too small record_size_limit %u; ignoring\n", + session, (unsigned)new_size); + return gnutls_assert_val(0); + } else { + _gnutls_handshake_log("EXT[%p]: server requested too small record_size_limit %u; closing the connection\n", + session, (unsigned)new_size); + return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER); + } + } + + session->internals.hsk_flags |= HSK_RECORD_SIZE_LIMIT_NEGOTIATED; + + /* client uses the reception of this extension as an + * indication of the request was accepted by the server */ + if (session->security_parameters.entity == GNUTLS_CLIENT) + session->security_parameters.max_record_recv_size = + session->security_parameters.max_user_record_recv_size; + + _gnutls_handshake_log("EXT[%p]: record_size_limit %u negotiated\n", + session, (unsigned)new_size); + + /* subtract 1 octet for content type */ + vers = get_version(session); + if (unlikely(vers == NULL)) + return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); + + session->security_parameters.max_record_send_size = + MIN(new_size - vers->tls13_sem, + session->security_parameters.max_user_record_send_size); + + return 0; +} + +/* returns data_size or a negative number on failure + */ +static int +_gnutls_record_size_limit_send_params(gnutls_session_t session, + gnutls_buffer_st * extdata) +{ + int ret; + uint16_t send_size; + + assert(session->security_parameters.max_user_record_recv_size >= 64 && + session->security_parameters.max_user_record_recv_size <= + DEFAULT_MAX_RECORD_SIZE); + + send_size = session->security_parameters.max_user_record_recv_size; + + if (session->security_parameters.entity == GNUTLS_SERVER) { + const version_entry_st *vers; + + /* if we had received the extension and rejected, don't send it */ + if (session->internals.hsk_flags & HSK_RECORD_SIZE_LIMIT_RECEIVED && + !(session->internals.hsk_flags & HSK_RECORD_SIZE_LIMIT_NEGOTIATED)) + return gnutls_assert_val(0); + + /* add 1 octet for content type */ + vers = get_version(session); + if (unlikely(vers == NULL)) + return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); + + session->security_parameters.max_record_recv_size = + send_size; + + send_size += vers->tls13_sem; + } else { + const version_entry_st *vers; + + /* add 1 octet for content type */ + vers = _gnutls_version_max(session); + if (unlikely(vers == NULL)) + return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); + + send_size += vers->tls13_sem; + } + + ret = _gnutls_buffer_append_prefix(extdata, 16, send_size); + if (ret < 0) + return gnutls_assert_val(ret); + + session->internals.hsk_flags |= HSK_RECORD_SIZE_LIMIT_SENT; + + return 2; +} diff --git a/lib/ext/record_size_limit.h b/lib/ext/record_size_limit.h new file mode 100644 index 0000000..da7cade --- /dev/null +++ b/lib/ext/record_size_limit.h @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2018 Red Hat, Inc. + * + * Author: 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/> + * + */ + +#ifndef GNUTLS_LIB_EXT_RECORD_SIZE_LIMIT_H +#define GNUTLS_LIB_EXT_RECORD_SIZE_LIMIT_H + +#include <hello_ext.h> + +extern const hello_ext_entry_st ext_mod_record_size_limit; + +#endif /* GNUTLS_LIB_EXT_RECORD_SIZE_LIMIT_H */ diff --git a/lib/ext/safe_renegotiation.c b/lib/ext/safe_renegotiation.c new file mode 100644 index 0000000..f76895d --- /dev/null +++ b/lib/ext/safe_renegotiation.c @@ -0,0 +1,449 @@ +/* + * Copyright (C) 2009-2012 Free Software Foundation, Inc. + * + * Author: Steve Dispensa (<dispensa@phonefactor.com>) + * + * 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 <ext/safe_renegotiation.h> +#include "errors.h" + + +static int _gnutls_sr_recv_params(gnutls_session_t state, + const uint8_t * data, size_t data_size); +static int _gnutls_sr_send_params(gnutls_session_t state, + gnutls_buffer_st *); +static void _gnutls_sr_deinit_data(gnutls_ext_priv_data_t priv); + +const hello_ext_entry_st ext_mod_sr = { + .name = "Safe Renegotiation", + .tls_id = 65281, + .gid = GNUTLS_EXTENSION_SAFE_RENEGOTIATION, + .validity = GNUTLS_EXT_FLAG_TLS | GNUTLS_EXT_FLAG_DTLS | GNUTLS_EXT_FLAG_CLIENT_HELLO | + GNUTLS_EXT_FLAG_TLS12_SERVER_HELLO, + .client_parse_point = GNUTLS_EXT_MANDATORY, + .server_parse_point = GNUTLS_EXT_MANDATORY, + .recv_func = _gnutls_sr_recv_params, + .send_func = _gnutls_sr_send_params, + .pack_func = NULL, + .unpack_func = NULL, + .deinit_func = _gnutls_sr_deinit_data, + .cannot_be_overriden = 1 +}; + +int +_gnutls_ext_sr_finished(gnutls_session_t session, void *vdata, + size_t vdata_size, int dir) +{ + int ret; + sr_ext_st *priv; + gnutls_ext_priv_data_t epriv; + + if (session->internals.priorities->sr == SR_DISABLED || + session->internals.priorities->no_extensions) { + return 0; + } + + ret = _gnutls_hello_ext_get_priv(session, + GNUTLS_EXTENSION_SAFE_RENEGOTIATION, + &epriv); + if (ret < 0) { + gnutls_assert(); + /* if a client didn't advertise safe renegotiation, we treat + * it as disabled. */ + if (session->security_parameters.entity == GNUTLS_SERVER) + return 0; + return ret; + } + priv = epriv; + + /* Save data for safe renegotiation. + */ + if (vdata_size > MAX_VERIFY_DATA_SIZE) { + gnutls_assert(); + return GNUTLS_E_INTERNAL_ERROR; + } + + if ((session->security_parameters.entity == GNUTLS_CLIENT + && dir == 0) + || (session->security_parameters.entity == GNUTLS_SERVER + && dir == 1)) { + priv->client_verify_data_len = vdata_size; + memcpy(priv->client_verify_data, vdata, vdata_size); + } else { + priv->server_verify_data_len = vdata_size; + memcpy(priv->server_verify_data, vdata, vdata_size); + } + + return 0; +} + +int _gnutls_ext_sr_verify(gnutls_session_t session) +{ + int ret; + sr_ext_st *priv = NULL; + gnutls_ext_priv_data_t epriv; + + if (session->internals.priorities->sr == SR_DISABLED) { + gnutls_assert(); + return 0; + } + + ret = _gnutls_hello_ext_get_priv(session, + GNUTLS_EXTENSION_SAFE_RENEGOTIATION, + &epriv); + if (ret >= 0) + priv = epriv; + + /* Safe renegotiation */ + + if (priv && priv->safe_renegotiation_received) { + if ((priv->ri_extension_data_len < + priv->client_verify_data_len) + || + (memcmp + (priv->ri_extension_data, priv->client_verify_data, + priv->client_verify_data_len))) { + gnutls_assert(); + _gnutls_handshake_log + ("HSK[%p]: Safe renegotiation failed [1]\n", + session); + return GNUTLS_E_SAFE_RENEGOTIATION_FAILED; + } + + if (session->security_parameters.entity == GNUTLS_CLIENT) { + if ((priv->ri_extension_data_len != + priv->client_verify_data_len + + priv->server_verify_data_len) + || memcmp(priv->ri_extension_data + + priv->client_verify_data_len, + priv->server_verify_data, + priv->server_verify_data_len) != 0) { + gnutls_assert(); + _gnutls_handshake_log + ("HSK[%p]: Safe renegotiation failed [2]\n", + session); + return GNUTLS_E_SAFE_RENEGOTIATION_FAILED; + } + } else { /* Make sure there are 0 extra bytes */ + + if (priv->ri_extension_data_len != + priv->client_verify_data_len) { + gnutls_assert(); + _gnutls_handshake_log + ("HSK[%p]: Safe renegotiation failed [3]\n", + session); + return GNUTLS_E_SAFE_RENEGOTIATION_FAILED; + } + } + + _gnutls_handshake_log + ("HSK[%p]: Safe renegotiation succeeded\n", session); + } else { /* safe renegotiation not received... */ + + if (priv && priv->connection_using_safe_renegotiation) { + gnutls_assert(); + _gnutls_handshake_log + ("HSK[%p]: Peer previously asked for safe renegotiation\n", + session); + return GNUTLS_E_SAFE_RENEGOTIATION_FAILED; + } + + /* Clients can't tell if it's an initial negotiation */ + if (session->internals.initial_negotiation_completed) { + if (session->internals.priorities->sr < SR_PARTIAL) { + _gnutls_handshake_log + ("HSK[%p]: Allowing unsafe (re)negotiation\n", + session); + } else { + gnutls_assert(); + _gnutls_handshake_log + ("HSK[%p]: Denying unsafe (re)negotiation\n", + session); + return + GNUTLS_E_UNSAFE_RENEGOTIATION_DENIED; + } + } else { + if (session->internals.priorities->sr < SR_SAFE) { + _gnutls_handshake_log + ("HSK[%p]: Allowing unsafe initial negotiation\n", + session); + } else { + gnutls_assert(); + _gnutls_handshake_log + ("HSK[%p]: Denying unsafe initial negotiation\n", + session); + return GNUTLS_E_SAFE_RENEGOTIATION_FAILED; + } + } + } + + return 0; +} + +/* if a server received the special ciphersuite. + */ +int _gnutls_ext_sr_recv_cs(gnutls_session_t session) +{ + int ret, set = 0; + sr_ext_st *priv; + gnutls_ext_priv_data_t epriv; + + ret = _gnutls_hello_ext_get_priv(session, + GNUTLS_EXTENSION_SAFE_RENEGOTIATION, + &epriv); + if (ret < 0) { + set = 1; + } + + if (set != 0) { + priv = gnutls_calloc(1, sizeof(*priv)); + if (priv == NULL) { + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; + } + epriv = priv; + } else + priv = epriv; + + priv->safe_renegotiation_received = 1; + priv->connection_using_safe_renegotiation = 1; + _gnutls_hello_ext_save_sr(session); + + if (set != 0) + _gnutls_hello_ext_set_priv(session, + GNUTLS_EXTENSION_SAFE_RENEGOTIATION, + epriv); + + return 0; +} + +int _gnutls_ext_sr_send_cs(gnutls_session_t session) +{ + int ret, set = 0; + sr_ext_st *priv; + gnutls_ext_priv_data_t epriv; + + ret = _gnutls_hello_ext_get_priv(session, + GNUTLS_EXTENSION_SAFE_RENEGOTIATION, + &epriv); + if (ret < 0) { + set = 1; + } + + if (set != 0) { + priv = gnutls_calloc(1, sizeof(*priv)); + if (priv == NULL) { + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; + } + epriv = priv; + + _gnutls_hello_ext_set_priv(session, + GNUTLS_EXTENSION_SAFE_RENEGOTIATION, + epriv); + } + + return 0; +} + +static int +_gnutls_sr_recv_params(gnutls_session_t session, + const uint8_t * data, size_t data_size) +{ + unsigned int len; + sr_ext_st *priv; + gnutls_ext_priv_data_t epriv; + int set = 0, ret; + + if (data_size == 0) + return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH); + + len = data[0]; + DECR_LEN(data_size, + len + 1 /* count the first byte and payload */ ); + + if (session->internals.priorities->sr == SR_DISABLED) { + gnutls_assert(); + return 0; + } + + ret = _gnutls_hello_ext_get_priv(session, + GNUTLS_EXTENSION_SAFE_RENEGOTIATION, + &epriv); + if (ret < 0 + && session->security_parameters.entity == GNUTLS_SERVER) { + set = 1; + } else if (ret < 0) { + gnutls_assert(); + return ret; + } + + if (set != 0) { + priv = gnutls_calloc(1, sizeof(*priv)); + if (priv == NULL) { + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; + } + epriv = priv; + + _gnutls_hello_ext_set_priv(session, + GNUTLS_EXTENSION_SAFE_RENEGOTIATION, + epriv); + } else { + priv = epriv; + } + + /* It is not legal to receive this extension on a renegotiation and + * not receive it on the initial negotiation. + */ + if (session->internals.initial_negotiation_completed != 0 && + priv->connection_using_safe_renegotiation == 0) { + gnutls_assert(); + return GNUTLS_E_SAFE_RENEGOTIATION_FAILED; + } + + if (len > sizeof(priv->ri_extension_data)) { + gnutls_assert(); + return GNUTLS_E_SAFE_RENEGOTIATION_FAILED; + } + + if (len > 0) + memcpy(priv->ri_extension_data, &data[1], len); + priv->ri_extension_data_len = len; + + /* "safe renegotiation received" means on *this* handshake; "connection using + * safe renegotiation" means that the initial hello received on the connection + * indicated safe renegotiation. + */ + priv->safe_renegotiation_received = 1; + priv->connection_using_safe_renegotiation = 1; + + return 0; +} + +static int +_gnutls_sr_send_params(gnutls_session_t session, + gnutls_buffer_st * extdata) +{ + /* The format of this extension is a one-byte length of verify data followed + * by the verify data itself. Note that the length byte does not include + * itself; IOW, empty verify data is represented as a length of 0. That means + * the minimum extension is one byte: 0x00. + */ + sr_ext_st *priv; + int ret, set = 0, len; + gnutls_ext_priv_data_t epriv; + size_t init_length = extdata->length; + + if (session->internals.priorities->sr == SR_DISABLED) { + gnutls_assert(); + return 0; + } + + ret = _gnutls_hello_ext_get_priv(session, + GNUTLS_EXTENSION_SAFE_RENEGOTIATION, + &epriv); + if (ret < 0) { + set = 1; + } + + if (set != 0) { + priv = gnutls_calloc(1, sizeof(*priv)); + if (priv == NULL) { + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; + } + epriv = priv; + + _gnutls_hello_ext_set_priv(session, + GNUTLS_EXTENSION_SAFE_RENEGOTIATION, + epriv); + } else + priv = epriv; + + /* Always offer the extension if we're a client */ + if (priv->connection_using_safe_renegotiation || + session->security_parameters.entity == GNUTLS_CLIENT) { + len = priv->client_verify_data_len; + if (session->security_parameters.entity == GNUTLS_SERVER) + len += priv->server_verify_data_len; + + ret = _gnutls_buffer_append_prefix(extdata, 8, len); + if (ret < 0) + return gnutls_assert_val(ret); + + ret = + _gnutls_buffer_append_data(extdata, + priv->client_verify_data, + priv-> + client_verify_data_len); + if (ret < 0) + return gnutls_assert_val(ret); + + if (session->security_parameters.entity == GNUTLS_SERVER) { + ret = + _gnutls_buffer_append_data(extdata, + priv-> + server_verify_data, + priv-> + server_verify_data_len); + if (ret < 0) + return gnutls_assert_val(ret); + } + } else + return 0; + + return extdata->length - init_length; +} + +static void _gnutls_sr_deinit_data(gnutls_ext_priv_data_t priv) +{ + gnutls_free(priv); +} + +/** + * gnutls_safe_renegotiation_status: + * @session: is a #gnutls_session_t type. + * + * Can be used to check whether safe renegotiation is being used + * in the current session. + * + * Returns: 0 when safe renegotiation is not used and non (0) when + * safe renegotiation is used. + * + * Since: 2.10.0 + **/ +unsigned gnutls_safe_renegotiation_status(gnutls_session_t session) +{ + int ret; + sr_ext_st *priv; + gnutls_ext_priv_data_t epriv; + + ret = _gnutls_hello_ext_get_priv(session, + GNUTLS_EXTENSION_SAFE_RENEGOTIATION, + &epriv); + if (ret < 0) { + gnutls_assert(); + return 0; + } + priv = epriv; + + return priv->connection_using_safe_renegotiation; +} diff --git a/lib/ext/safe_renegotiation.h b/lib/ext/safe_renegotiation.h new file mode 100644 index 0000000..bc2024a --- /dev/null +++ b/lib/ext/safe_renegotiation.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2009-2012 Free Software Foundation, Inc. + * + * Author: Steve Dispensa (<dispensa@phonefactor.com>) + * + * 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_EXT_SAFE_RENEGOTIATION_H +#define GNUTLS_LIB_EXT_SAFE_RENEGOTIATION_H + +#include <hello_ext.h> + +typedef struct { + uint8_t client_verify_data[MAX_VERIFY_DATA_SIZE]; + size_t client_verify_data_len; + uint8_t server_verify_data[MAX_VERIFY_DATA_SIZE]; + size_t server_verify_data_len; + uint8_t ri_extension_data[MAX_VERIFY_DATA_SIZE * 2]; /* max signal is 72 bytes in s->c sslv3 */ + size_t ri_extension_data_len; + + unsigned int safe_renegotiation_received:1; + unsigned int initial_negotiation_completed:1; + unsigned int connection_using_safe_renegotiation:1; +} sr_ext_st; + +extern const hello_ext_entry_st ext_mod_sr; + +int _gnutls_ext_sr_finished(gnutls_session_t session, void *vdata, + size_t vdata_size, int dir); +int _gnutls_ext_sr_recv_cs(gnutls_session_t session); +int _gnutls_ext_sr_verify(gnutls_session_t session); +int _gnutls_ext_sr_send_cs(gnutls_session_t); + +#endif /* GNUTLS_LIB_EXT_SAFE_RENEGOTIATION_H */ diff --git a/lib/ext/server_cert_type.c b/lib/ext/server_cert_type.c new file mode 100644 index 0000000..6db2a1f --- /dev/null +++ b/lib/ext/server_cert_type.c @@ -0,0 +1,370 @@ +/* + * Copyright (C) 2016 - 2018 ARPA2 project + * + * Author: Tom Vrancken (dev@tomvrancken.nl) + * + * 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 is part of the server_certificate_type extension as + * defined in RFC7250 (https://tools.ietf.org/html/rfc7250). + * + * The server_certificate_type extension in the client hello indicates + * the types of certificates the client is able to process when provided + * by the server in a subsequent certificate payload. + */ + +#include <gnutls_int.h> +#include <gnutls/gnutls.h> +#include "ext/cert_types.h" +#include "ext/server_cert_type.h" +#include "hello_ext.h" +#include "hello_ext_lib.h" +#include "errors.h" +#include "state.h" +#include "datum.h" + + +static int _gnutls_server_cert_type_recv_params(gnutls_session_t session, + const uint8_t* data, + size_t data_size); +static int _gnutls_server_cert_type_send_params(gnutls_session_t session, + gnutls_buffer_st* data); + + +const hello_ext_entry_st ext_mod_server_cert_type = { + .name = "Server Certificate Type", + .tls_id = 20, + .gid = GNUTLS_EXTENSION_SERVER_CERT_TYPE, + .client_parse_point = GNUTLS_EXT_TLS, + .server_parse_point = GNUTLS_EXT_TLS, + .validity = GNUTLS_EXT_FLAG_TLS | + GNUTLS_EXT_FLAG_DTLS | + GNUTLS_EXT_FLAG_CLIENT_HELLO | + GNUTLS_EXT_FLAG_TLS12_SERVER_HELLO | + GNUTLS_EXT_FLAG_EE, + .recv_func = _gnutls_server_cert_type_recv_params, + .send_func = _gnutls_server_cert_type_send_params, + .pack_func = _gnutls_hello_ext_default_pack, + .unpack_func = _gnutls_hello_ext_default_unpack, + .deinit_func = _gnutls_hello_ext_default_deinit, + .cannot_be_overriden = 1 +}; + + +static int _gnutls_server_cert_type_recv_params(gnutls_session_t session, + const uint8_t* data, + size_t data_size) +{ + int ret; + gnutls_certificate_type_t cert_type; + size_t i; + bool found = false; + const uint8_t* pdata = data; + + /* Only activate this extension if we have cert credentials set + * and alternative cert types are allowed */ + if (!are_alternative_cert_types_allowed(session) || + (_gnutls_get_cred(session, GNUTLS_CRD_CERTIFICATE) == NULL)) + return 0; + + if (!IS_SERVER(session)) { // client mode + gnutls_datum_t sent_cert_types; // Holds the previously sent cert types + + /* Compare packet length with expected packet length. For the + * client this is a single byte. */ + if (data_size != 1) { + return + gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH); + } + + /* The server picked one of the offered cert types if he supports + * at least one of them. If both parties play by the rules then we + * may only receive a cert type that we offered, i.e. one that we + * support. Because the world isn't as beautiful as it may seem, + * we're going to check it nevertheless. */ + cert_type = IANA2cert_type(pdata[0]); + + _gnutls_handshake_log("EXT[%p]: Received a %s server certificate type confirmation from the server.\n", + session, gnutls_certificate_type_get_name(cert_type)); + + // Check validity of cert type + if (cert_type == GNUTLS_CRT_UNKNOWN) { + return gnutls_assert_val(GNUTLS_E_UNSUPPORTED_CERTIFICATE_TYPE); + } + + /* Get the cert types that we sent to the server (they were stored + * in IANA representation. + */ + ret = _gnutls_hello_ext_get_datum(session, + GNUTLS_EXTENSION_SERVER_CERT_TYPE, + &sent_cert_types); + if (ret < 0) { + /* This should not happen and indicate a memory corruption! + * Assertion are always on in production code so execution + * will halt here. */ + assert(false); + } + + // Check whether what we got back is actually offered by us + for (i = 0; i < sent_cert_types.size; i++) { + if (IANA2cert_type(sent_cert_types.data[i]) == cert_type) + found = true; + } + + if (found) { + // Everything OK, now set the server certificate type + _gnutls_session_server_cert_type_set(session, cert_type); + ret = GNUTLS_E_SUCCESS; + } else { + // No valid cert type found + ret = GNUTLS_E_UNSUPPORTED_CERTIFICATE_TYPE; + } + + return ret; + + } else { // server mode + gnutls_datum_t cert_types; // Holds the received cert types + + // Compare packet length with expected packet length. + DECR_LEN(data_size, 1); + if (data[0] != data_size) { + return + gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH); + } + pdata += 1; + + // Assign the contents of our data buffer to a gnutls_datum_t + cert_types.data = (uint8_t*)pdata; // Need casting to get rid of 'discards const qualifier' warning + cert_types.size = data_size; + + // Store the server certificate types in our session + _gnutls_hello_ext_set_datum(session, + GNUTLS_EXTENSION_SERVER_CERT_TYPE, + &cert_types); + + /* We receive a list of supported certificate types that the client + * is able to process when offered by the server via a subsequent + * Certificate message. This list is sorted by order of preference. + * We now check in this order of preference whether we support any + * of these certificate types. + */ + for (i = 0; i < cert_types.size; i++) { + // Convert to internal representation + cert_type = IANA2cert_type(cert_types.data[i]); + + // If we have an invalid cert id then continue to the next + if (cert_type == GNUTLS_CRT_UNKNOWN) + continue; + + _gnutls_handshake_log("EXT[%p]: Checking compatibility of a %s server certificate type that was received from the client.\n", + session, gnutls_certificate_type_get_name(cert_type)); + + // Check for support of this cert type + if (_gnutls_session_is_cert_type_supported(session, cert_type, true, GNUTLS_CTYPE_SERVER) == 0) { + found = true; + break; + } + } + + // We found a matching ctype, we pick this one + if (found) { + _gnutls_session_server_cert_type_set(session, cert_type); + ret = GNUTLS_E_SUCCESS; + } else { + /* If no supported certificate type can be found we terminate + * with a fatal alert of type "unsupported_certificate" + * (according to specification rfc7250). + */ + ret = GNUTLS_E_UNSUPPORTED_CERTIFICATE_TYPE; + } + + return ret; + } +} + +static int _gnutls_server_cert_type_send_params(gnutls_session_t session, + gnutls_buffer_st* data) +{ + int ret; + uint8_t cert_type_IANA; // Holds an IANA cert type ID + gnutls_certificate_type_t cert_type; + + /* Only activate this extension if we have cert credentials set + * and alternative cert types are allowed */ + if (!are_alternative_cert_types_allowed(session) || + (_gnutls_get_cred(session, GNUTLS_CRD_CERTIFICATE) == NULL)) + return 0; + + if (!IS_SERVER(session)) { // Client mode + uint8_t cert_types[GNUTLS_CRT_MAX]; // The list with supported (IANA) cert types. Inv: 0 <= cert type Id < 256 + size_t i, num_cert_types = 0; + priority_st* cert_priorities; + gnutls_datum_t tmp_cert_types; // For type conversion + + // For brevity + cert_priorities = &session->internals.priorities->server_ctype; + + /* Retrieve server certificate type priorities if any. If no + * priorities are set then the default server certificate type + * initialization values apply. This default is currently set to + * X.509 in which case we don't enable this extension. + */ + if (cert_priorities->num_priorities > 0) { // Priorities are explicitly set + /* If the certificate priority is explicitly set to only + * X.509 (default) then, according to spec we don't send + * this extension. We check this here to avoid further work in + * this routine. We also check it below after pruning supported + * types. + */ + if (cert_priorities->num_priorities == 1 && + cert_priorities->priorities[0] == DEFAULT_CERT_TYPE) { + _gnutls_handshake_log + ("EXT[%p]: Server certificate type was set to default cert type (%s). " + "We therefore do not send this extension.\n", + session, + gnutls_certificate_type_get_name(DEFAULT_CERT_TYPE)); + + // Explicitly set but default ctype, so don't send anything + return 0; + } + + /* We are only allowed to send certificate types that we support. + * Therefore we check this here and prune our original list. + * This check might seem redundant now because we don't check for + * credentials (they are not needed for a client) and only check the + * priorities over which we already iterate. In the future, + * additional checks might be necessary and they can be easily + * added in the ..type_supported() routine without modifying the + * structure of the code here. + */ + for (i = 0; i < cert_priorities->num_priorities; i++) { + + cert_type = cert_priorities->priorities[i]; + + if (_gnutls_session_is_cert_type_supported(session, cert_type, + false, GNUTLS_CTYPE_SERVER) == 0) { + /* Check whether we are allowed to store another cert type + * in our buffer. In other words, prevent a possible buffer + * overflow. This situation can occur when a user sets + * duplicate cert types in the priority strings. */ + if (num_cert_types >= GNUTLS_CRT_MAX) + return gnutls_assert_val(GNUTLS_E_SHORT_MEMORY_BUFFER); + + // Convert to IANA representation + ret = cert_type2IANA(cert_type); + + if (ret < 0) + return gnutls_assert_val(ret); + + cert_type_IANA = ret; // For readability + + // Add this cert type to our list with supported types + cert_types[num_cert_types] = cert_type_IANA; + num_cert_types++; + + _gnutls_handshake_log + ("EXT[%p]: Server certificate type %s (%d) was queued.\n", + session, + gnutls_certificate_type_get_name(cert_type), + cert_type_IANA); + } + } + + /* Check whether there are any supported certificate types left + * after the previous pruning step. If not, we do not send this + * extension. Also, if the only supported type is the default type + * we do not send this extension (according to RFC7250). + */ + if (num_cert_types == 0) { // For now, this should not occur since we only check priorities while pruning. + _gnutls_handshake_log + ("EXT[%p]: Server certificate types were set but none of them is supported. " + "We do not send this extension.\n", + session); + + return 0; + } else if (num_cert_types == 1 && + IANA2cert_type(cert_types[0]) == DEFAULT_CERT_TYPE) { + _gnutls_handshake_log + ("EXT[%p]: The only supported server certificate type is (%s) which is the default. " + "We therefore do not send this extension.\n", + session, + gnutls_certificate_type_get_name(DEFAULT_CERT_TYPE)); + + return 0; + } + + /* We have data to send and store a copy internally. We convert + * our list with supported cert types to a datum_t in order to + * be able to make the ..._set_datum call. + */ + tmp_cert_types.data = cert_types; + tmp_cert_types.size = num_cert_types; + + _gnutls_hello_ext_set_datum(session, + GNUTLS_EXTENSION_SERVER_CERT_TYPE, + &tmp_cert_types); + + /* Serialize the certificate types into a sequence of octets + * uint8: length of sequence of cert types (1 octet) + * uint8: cert types (0 <= #octets <= 255) + */ + ret = _gnutls_buffer_append_data_prefix(data, 8, + cert_types, + num_cert_types); + + // Check for errors and cleanup in case of error + if (ret < 0) { + return gnutls_assert_val(ret); + } else { + // Number of bytes we are sending + return num_cert_types + 1; + } + } + } else { // Server mode + // Retrieve negotiated server certificate type and send it + cert_type = get_certificate_type(session, GNUTLS_CTYPE_SERVER); + ret = cert_type2IANA(cert_type); + + if (ret < 0) + return gnutls_assert_val(ret); + + cert_type_IANA = ret; // For readability + + _gnutls_handshake_log("EXT[%p]: Confirming to use a %s server certificate type.\n", + session, gnutls_certificate_type_get_name(cert_type)); + + ret = gnutls_buffer_append_data(data, &cert_type_IANA, 1); + + if (ret < 0) + return gnutls_assert_val(ret); + + return 1; // sent one byte + } + + // In all other cases don't enable this extension + return 0; +} + + +/** Extension interface **/ + +/* The interface is defined in state.c: + * Public: + * - gnutls_certificate_type_get2 + * + * Private: + * - _gnutls_session_server_cert_type_set + */ diff --git a/lib/ext/server_cert_type.h b/lib/ext/server_cert_type.h new file mode 100644 index 0000000..3c15b0b --- /dev/null +++ b/lib/ext/server_cert_type.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2016 - 2018 ARPA2 project + * + * Author: Tom Vrancken (dev@tomvrancken.nl) + * + * 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 is part of the server_certificate_type extension as + * defined in RFC7250 (https://tools.ietf.org/html/rfc7250). + * + * The server_certificate_type extension in the client hello indicates + * the certificate types the client is able to process from the server + * in order to authenticate the server. + */ + +#ifndef GNUTLS_LIB_EXT_SERVER_CERT_TYPE_H +#define GNUTLS_LIB_EXT_SERVER_CERT_TYPE_H + +#include <hello_ext.h> + +extern const hello_ext_entry_st ext_mod_server_cert_type; + +#endif /* GNUTLS_LIB_EXT_SERVER_CERT_TYPE_H */ diff --git a/lib/ext/server_name.c b/lib/ext/server_name.c new file mode 100644 index 0000000..d52c8d0 --- /dev/null +++ b/lib/ext/server_name.c @@ -0,0 +1,385 @@ +/* + * Copyright (C) 2002-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/> + * + */ + +#include "gnutls_int.h" +#include "auth.h" +#include "errors.h" +#include "num.h" +#include "str.h" +#include <ext/server_name.h> +#include "hello_ext_lib.h" + +static int _gnutls_server_name_recv_params(gnutls_session_t session, + const uint8_t * data, + size_t data_size); +static int _gnutls_server_name_send_params(gnutls_session_t session, + gnutls_buffer_st * extdata); + +int +_gnutls_server_name_set_raw(gnutls_session_t session, + gnutls_server_name_type_t type, + const void *name, size_t name_length); + +const hello_ext_entry_st ext_mod_server_name = { + .name = "Server Name Indication", + .tls_id = 0, + .gid = GNUTLS_EXTENSION_SERVER_NAME, + .validity = GNUTLS_EXT_FLAG_TLS | GNUTLS_EXT_FLAG_DTLS | GNUTLS_EXT_FLAG_CLIENT_HELLO | + GNUTLS_EXT_FLAG_EE | GNUTLS_EXT_FLAG_TLS12_SERVER_HELLO, + .client_parse_point = GNUTLS_EXT_MANDATORY, + .server_parse_point = GNUTLS_EXT_MANDATORY, + .recv_func = _gnutls_server_name_recv_params, + .send_func = _gnutls_server_name_send_params, + .pack_func = _gnutls_hello_ext_default_pack, + .unpack_func = _gnutls_hello_ext_default_unpack, + .deinit_func = _gnutls_hello_ext_default_deinit, + .cannot_be_overriden = 1 +}; + +/* + * In case of a server: if a NAME_DNS extension type is received then + * it stores into the session the value of NAME_DNS. The server may + * use gnutls_ext_get_server_name(), in order to access it. + * + * In case of a client: If a proper NAME_DNS extension type is found + * in the session then it sends the extension to the peer. + * + */ +static int +_gnutls_server_name_recv_params(gnutls_session_t session, + const uint8_t * data, size_t data_size) +{ + const unsigned char *p; + uint16_t len, type; + gnutls_datum_t name; + + if (session->security_parameters.entity == GNUTLS_SERVER) { + DECR_LENGTH_RET(data_size, 2, GNUTLS_E_UNEXPECTED_PACKET_LENGTH); + len = _gnutls_read_uint16(data); + if (len == 0) + return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH); + + if (len != data_size) { + gnutls_assert(); + return GNUTLS_E_UNEXPECTED_PACKET_LENGTH; + } + + p = data + 2; + + while (data_size > 0) { + DECR_LEN(data_size, 1); + type = *p; + p++; + + DECR_LEN(data_size, 2); + len = _gnutls_read_uint16(p); + p += 2; + + if (len == 0) { + _gnutls_handshake_log + ("HSK[%p]: Received server name size of zero\n", + session); + return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH); + } + + DECR_LEN(data_size, len); + + if (type == 0) { /* NAME_DNS */ + if (!_gnutls_dnsname_is_valid((char*)p, len)) { + _gnutls_handshake_log + ("HSK[%p]: Server name is not acceptable: '%.*s'\n", + session, (int) len, p); + return gnutls_assert_val(GNUTLS_E_RECEIVED_DISALLOWED_NAME); + } + + name.data = (void*)p; + name.size = len; + + _gnutls_hello_ext_unset_priv(session, GNUTLS_EXTENSION_SERVER_NAME); + return _gnutls_hello_ext_set_datum(session, + GNUTLS_EXTENSION_SERVER_NAME, + &name); + } + p += len; + + } + + + } + + return 0; +} + +/* returns data_size or a negative number on failure + */ +static int +_gnutls_server_name_send_params(gnutls_session_t session, + gnutls_buffer_st * extdata) +{ + int total_size = 0, ret; + gnutls_datum_t name; + + ret = + _gnutls_hello_ext_get_datum(session, GNUTLS_EXTENSION_SERVER_NAME, + &name); + if (ret < 0) + return 0; + + /* this function sends the client extension data (dnsname) + */ + if (session->security_parameters.entity == GNUTLS_CLIENT) { + if (name.size == 0) + return 0; + + /* uint8_t + uint16_t + size + */ + total_size = 2 + 1 + 2 + name.size; + + /* UINT16: write total size of all names + */ + ret = + _gnutls_buffer_append_prefix(extdata, 16, + total_size - 2); + if (ret < 0) + return gnutls_assert_val(ret); + + /* UINT8: type of this extension + * UINT16: size of the first name + * LEN: the actual server name. + */ + ret = + _gnutls_buffer_append_prefix(extdata, 8, 0); + if (ret < 0) + return gnutls_assert_val(ret); + + _gnutls_debug_log("HSK[%p]: sent server name: '%.*s'\n", session, name.size, name.data); + + ret = + _gnutls_buffer_append_data_prefix + (extdata, 16, + name.data, name.size); + if (ret < 0) + return gnutls_assert_val(ret); + } else { + return 0; + } + + return total_size; +} + +/** + * gnutls_server_name_get: + * @session: is a #gnutls_session_t type. + * @data: will hold the data + * @data_length: will hold the data length. Must hold the maximum size of data. + * @type: will hold the server name indicator type + * @indx: is the index of the server_name + * + * This function will allow you to get the name indication (if any), a + * client has sent. The name indication may be any of the enumeration + * gnutls_server_name_type_t. + * + * If @type is GNUTLS_NAME_DNS, then this function is to be used by + * servers that support virtual hosting, and the data will be a null + * terminated IDNA ACE string (prior to GnuTLS 3.4.0 it was a UTF-8 string). + * + * If @data has not enough size to hold the server name + * GNUTLS_E_SHORT_MEMORY_BUFFER is returned, and @data_length will + * hold the required size. + * + * @indx is used to retrieve more than one server names (if sent by + * the client). The first server name has an index of 0, the second 1 + * and so on. If no name with the given index exists + * GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE is returned. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, on UTF-8 + * decoding error %GNUTLS_E_IDNA_ERROR is returned, otherwise a negative + * error code is returned. + **/ +int +gnutls_server_name_get(gnutls_session_t session, void *data, + size_t * data_length, + unsigned int *type, unsigned int indx) +{ + char *_data = data; + gnutls_datum_t name; + int ret; + + if (session->security_parameters.entity == GNUTLS_CLIENT) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + if (indx != 0) + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + + ret = + _gnutls_hello_ext_get_datum(session, GNUTLS_EXTENSION_SERVER_NAME, &name); + if (ret < 0) { + gnutls_assert(); + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + } + + if (name.size == 0) { + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + } + + *type = GNUTLS_NAME_DNS; + + if (*data_length > name.size) { /* greater since we need one extra byte for the null */ + *data_length = name.size; + memcpy(data, name.data, *data_length); + + /* null terminate */ + _data[(*data_length)] = 0; + + } else { + *data_length = name.size + 1; + ret = GNUTLS_E_SHORT_MEMORY_BUFFER; + goto cleanup; + } + + ret = 0; + cleanup: + return ret; +} + +/* This does not do any conversion not perform any check */ +int +_gnutls_server_name_set_raw(gnutls_session_t session, + gnutls_server_name_type_t type, + const void *name, size_t name_length) +{ + int ret; + gnutls_datum_t dname; + + if (name_length >= MAX_SERVER_NAME_SIZE) { + return GNUTLS_E_INVALID_REQUEST; + } + + _gnutls_hello_ext_unset_priv(session, GNUTLS_EXTENSION_SERVER_NAME); + + dname.data = (void*)name; + dname.size = name_length; + + ret = _gnutls_hello_ext_set_datum(session, GNUTLS_EXTENSION_SERVER_NAME, &dname); + if (ret < 0) + return gnutls_assert_val(ret); + + return 0; +} + +/** + * gnutls_server_name_set: + * @session: is a #gnutls_session_t type. + * @type: specifies the indicator type + * @name: is a string that contains the server name. + * @name_length: holds the length of name excluding the terminating null byte + * + * This function is to be used by clients that want to inform (via a + * TLS extension mechanism) the server of the name they connected to. + * This should be used by clients that connect to servers that do + * virtual hosting. + * + * The value of @name depends on the @type type. In case of + * %GNUTLS_NAME_DNS, a UTF-8 null-terminated domain name string, + * without the trailing dot, is expected. + * + * IPv4 or IPv6 addresses are not permitted to be set by this function. + * If the function is called with a name of @name_length zero it will clear + * all server names set. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, + * otherwise a negative error code is returned. + **/ +int +gnutls_server_name_set(gnutls_session_t session, + gnutls_server_name_type_t type, + const void *name, size_t name_length) +{ + int ret; + gnutls_datum_t idn_name = {NULL,0}; + + if (session->security_parameters.entity == GNUTLS_SERVER) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + if (name_length == 0) { /* unset extension */ + _gnutls_hello_ext_unset_priv(session, GNUTLS_EXTENSION_SERVER_NAME); + return 0; + } + + ret = gnutls_idna_map(name, name_length, &idn_name, 0); + if (ret < 0) { + _gnutls_debug_log("unable to convert name %s to IDNA2008 format\n", (char*)name); + return ret; + } + + name = idn_name.data; + name_length = idn_name.size; + + ret = _gnutls_server_name_set_raw(session, type, name, name_length); + gnutls_free(idn_name.data); + + return ret; +} + +unsigned _gnutls_server_name_matches_resumed(gnutls_session_t session) +{ + gnutls_datum_t name1, name2; + int ret; + + ret = + _gnutls_hello_ext_get_datum(session, + GNUTLS_EXTENSION_SERVER_NAME, + &name1); + if (ret < 0) { /* no server name in this session */ + name1.data = NULL; + name1.size = 0; + } + + ret = + _gnutls_hello_ext_get_resumed_datum(session, + GNUTLS_EXTENSION_SERVER_NAME, + &name2); + if (ret < 0) { /* no server name in this session */ + name2.data = NULL; + name2.size = 0; + } + + if (name1.data == NULL || name2.data == NULL) { + if (name1.data == name2.data) + return 1; + else + return 0; + } + + if (name1.size != name2.size) + return 0; + + if (memcmp(name1.data, name2.data, name1.size) != 0) + return 0; + + return 1; +} diff --git a/lib/ext/server_name.h b/lib/ext/server_name.h new file mode 100644 index 0000000..b5cdd0e --- /dev/null +++ b/lib/ext/server_name.h @@ -0,0 +1,33 @@ +/* + * 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/> + * + */ + +#ifndef GNUTLS_LIB_EXT_SERVER_NAME_H +#define GNUTLS_LIB_EXT_SERVER_NAME_H + +#include <hello_ext.h> + +extern const hello_ext_entry_st ext_mod_server_name; + +unsigned _gnutls_server_name_matches_resumed(gnutls_session_t); + +#endif /* GNUTLS_LIB_EXT_SERVER_NAME_H */ diff --git a/lib/ext/session_ticket.c b/lib/ext/session_ticket.c new file mode 100644 index 0000000..87d9069 --- /dev/null +++ b/lib/ext/session_ticket.c @@ -0,0 +1,843 @@ +/* + * Copyright (C) 2009-2018 Free Software Foundation, Inc. + * + * Author: Daiki Ueno, Ander Juaristi + * + * 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 implements the TLS session ticket extension. + * + * Note that the extension is only used in TLS 1.2. For TLS 1.3, session + * tickets are sent as part of pre_shared_key extension (see pre_shared_key.c). + */ + +#include "gnutls_int.h" +#include "errors.h" +#include <fips.h> +#include <datum.h> +#include <algorithms.h> +#include <handshake.h> +#include <num.h> +#include <constate.h> +#include <session_pack.h> +#include <random.h> +#include <ext/session_ticket.h> +#include <mbuffers.h> +#include <hello_ext.h> +#include <constate.h> +#include <dtls.h> +#include "stek.h" +#include "db.h" + +static int session_ticket_recv_params(gnutls_session_t session, + const uint8_t * data, + size_t data_size); +static int session_ticket_send_params(gnutls_session_t session, + gnutls_buffer_st * extdata); +static int session_ticket_unpack(gnutls_buffer_st * ps, + gnutls_ext_priv_data_t * _priv); +static int session_ticket_pack(gnutls_ext_priv_data_t _priv, + gnutls_buffer_st * ps); +static void session_ticket_deinit_data(gnutls_ext_priv_data_t priv); + +const hello_ext_entry_st ext_mod_session_ticket = { + .name = "Session Ticket", + .tls_id = 35, + .gid = GNUTLS_EXTENSION_SESSION_TICKET, + .validity = GNUTLS_EXT_FLAG_TLS | GNUTLS_EXT_FLAG_DTLS | GNUTLS_EXT_FLAG_CLIENT_HELLO | + GNUTLS_EXT_FLAG_TLS12_SERVER_HELLO, + /* This extension must be parsed on session resumption as well; see + * https://gitlab.com/gnutls/gnutls/issues/841 */ + .client_parse_point = GNUTLS_EXT_MANDATORY, + /* on server side we want this parsed after normal handshake resumption + * actions are complete */ + .server_parse_point = GNUTLS_EXT_TLS, + .recv_func = session_ticket_recv_params, + .send_func = session_ticket_send_params, + .pack_func = session_ticket_pack, + .unpack_func = session_ticket_unpack, + .deinit_func = session_ticket_deinit_data, + .cannot_be_overriden = 1 +}; + +typedef struct { + uint8_t *session_ticket; + int session_ticket_len; +} session_ticket_ext_st; + +static void +deinit_ticket(struct ticket_st *ticket) +{ + free(ticket->encrypted_state); +} + +static int +unpack_ticket(const gnutls_datum_t *ticket_data, struct ticket_st *ticket) +{ + const uint8_t * data = ticket_data->data; + size_t data_size = ticket_data->size; + const uint8_t *encrypted_state; + + /* Format: + * Key name + * IV + * data length + * encrypted data + * MAC + */ + DECR_LEN(data_size, TICKET_KEY_NAME_SIZE); + memcpy(ticket->key_name, data, TICKET_KEY_NAME_SIZE); + data += TICKET_KEY_NAME_SIZE; + + DECR_LEN(data_size, TICKET_IV_SIZE); + memcpy(ticket->IV, data, TICKET_IV_SIZE); + data += TICKET_IV_SIZE; + + DECR_LEN(data_size, 2); + ticket->encrypted_state_len = _gnutls_read_uint16(data); + data += 2; + + encrypted_state = data; + + DECR_LEN(data_size, ticket->encrypted_state_len); + data += ticket->encrypted_state_len; + + DECR_LEN(data_size, TICKET_MAC_SIZE); + memcpy(ticket->mac, data, TICKET_MAC_SIZE); + + ticket->encrypted_state = + gnutls_malloc(ticket->encrypted_state_len); + if (!ticket->encrypted_state) { + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; + } + memcpy(ticket->encrypted_state, encrypted_state, + ticket->encrypted_state_len); + + return 0; +} + +static void +pack_ticket(const struct ticket_st *ticket, gnutls_datum_t *ticket_data) +{ + uint8_t *p; + + p = ticket_data->data; + + memcpy(p, ticket->key_name, TICKET_KEY_NAME_SIZE); + p += TICKET_KEY_NAME_SIZE; + + memcpy(p, ticket->IV, TICKET_IV_SIZE); + p += TICKET_IV_SIZE; + + _gnutls_write_uint16(ticket->encrypted_state_len, p); + p += 2; + + /* We use memmove instead of memcpy here because + * ticket->encrypted_state is allocated from + * ticket_data->data, and thus both memory areas may overlap. + */ + memmove(p, ticket->encrypted_state, ticket->encrypted_state_len); + p += ticket->encrypted_state_len; + + memcpy(p, ticket->mac, TICKET_MAC_SIZE); +} + +static +int digest_ticket(const gnutls_datum_t * key, struct ticket_st *ticket, + uint8_t * digest) +{ + mac_hd_st digest_hd; + uint16_t length16; + int ret; + + ret = _gnutls_mac_init(&digest_hd, mac_to_entry(TICKET_MAC_ALGO), + key->data, key->size); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + _gnutls_mac(&digest_hd, ticket->key_name, TICKET_KEY_NAME_SIZE); + _gnutls_mac(&digest_hd, ticket->IV, TICKET_IV_SIZE); + length16 = _gnutls_conv_uint16(ticket->encrypted_state_len); + _gnutls_mac(&digest_hd, &length16, 2); + _gnutls_mac(&digest_hd, ticket->encrypted_state, + ticket->encrypted_state_len); + _gnutls_mac_deinit(&digest_hd, digest); + + return 0; +} + +int +_gnutls_decrypt_session_ticket(gnutls_session_t session, + const gnutls_datum_t *ticket_data, + gnutls_datum_t *state) +{ + cipher_hd_st cipher_hd; + gnutls_datum_t IV; + gnutls_datum_t stek_key_name, stek_cipher_key, stek_mac_key; + uint8_t cmac[TICKET_MAC_SIZE]; + struct ticket_st ticket; + int ret; + + /* Retrieve ticket decryption keys */ + if (_gnutls_get_session_ticket_decryption_key(session, + ticket_data, + &stek_key_name, + &stek_mac_key, + &stek_cipher_key) < 0) + return gnutls_assert_val(GNUTLS_E_DECRYPTION_FAILED); + + ret = unpack_ticket(ticket_data, &ticket); + if (ret < 0) + return ret; + + /* If the key name of the ticket does not match the one that is currently active, + issue a new ticket. */ + if (memcmp + (ticket.key_name, stek_key_name.data, + stek_key_name.size)) { + ret = GNUTLS_E_DECRYPTION_FAILED; + goto cleanup; + } + + /* Check the integrity of ticket */ + ret = digest_ticket(&stek_mac_key, &ticket, cmac); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + if (memcmp(ticket.mac, cmac, TICKET_MAC_SIZE)) { + ret = gnutls_assert_val(GNUTLS_E_DECRYPTION_FAILED); + goto cleanup; + } + + if (ticket.encrypted_state_len % TICKET_BLOCK_SIZE != 0) { + ret = gnutls_assert_val(GNUTLS_E_DECRYPTION_FAILED); + goto cleanup; + } + + /* Decrypt encrypted_state */ + IV.data = ticket.IV; + IV.size = TICKET_IV_SIZE; + ret = + _gnutls_cipher_init(&cipher_hd, + cipher_to_entry(TICKET_CIPHER), + &stek_cipher_key, &IV, 0); + if (ret < 0) { + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR); + gnutls_assert(); + goto cleanup; + } + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_APPROVED); + + ret = _gnutls_cipher_decrypt(&cipher_hd, ticket.encrypted_state, + ticket.encrypted_state_len); + if (ret < 0) { + gnutls_assert(); + goto cleanup2; + } + + state->data = ticket.encrypted_state; + state->size = ticket.encrypted_state_len; + + ticket.encrypted_state = NULL; + + ret = 0; + +cleanup2: + _gnutls_cipher_deinit(&cipher_hd); + +cleanup: + deinit_ticket(&ticket); + + return ret; + +} + +int +_gnutls_encrypt_session_ticket(gnutls_session_t session, + const gnutls_datum_t *state, + gnutls_datum_t *ticket_data) +{ + cipher_hd_st cipher_hd; + gnutls_datum_t IV; + gnutls_datum_t encrypted_state; + gnutls_datum_t result = { NULL, 0 }; + uint8_t iv[TICKET_IV_SIZE]; + gnutls_datum_t stek_cipher_key, stek_mac_key, stek_key_name; + struct ticket_st ticket; + int ret; + + encrypted_state.size = ((state->size + TICKET_BLOCK_SIZE - 1) / TICKET_BLOCK_SIZE) * TICKET_BLOCK_SIZE; + result.size = TICKET_KEY_NAME_SIZE + TICKET_IV_SIZE + 2 + + encrypted_state.size + TICKET_MAC_SIZE; + result.data = gnutls_calloc(1, result.size); + if (!result.data) { + return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + } + encrypted_state.data = result.data + TICKET_KEY_NAME_SIZE + TICKET_IV_SIZE + 2; + memcpy(encrypted_state.data, state->data, state->size); + + /* Retrieve ticket encryption keys */ + if (_gnutls_get_session_ticket_encryption_key(session, + &stek_key_name, + &stek_mac_key, + &stek_cipher_key) < 0) { + ret = GNUTLS_E_ENCRYPTION_FAILED; + goto cleanup; + } + + /* Encrypt state */ + IV.data = iv; + IV.size = TICKET_IV_SIZE; + + ret = gnutls_rnd(GNUTLS_RND_NONCE, iv, TICKET_IV_SIZE); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = + _gnutls_cipher_init(&cipher_hd, + cipher_to_entry(TICKET_CIPHER), + &stek_cipher_key, &IV, 1); + if (ret < 0) { + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR); + gnutls_assert(); + goto cleanup; + } + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_APPROVED); + + ret = _gnutls_cipher_encrypt(&cipher_hd, encrypted_state.data, + encrypted_state.size); + if (ret < 0) { + gnutls_assert(); + goto cleanup2; + } + + + /* Fill the ticket structure to compute MAC. */ + memcpy(ticket.key_name, stek_key_name.data, stek_key_name.size); + memcpy(ticket.IV, IV.data, IV.size); + ticket.encrypted_state_len = encrypted_state.size; + ticket.encrypted_state = encrypted_state.data; + + ret = digest_ticket(&stek_mac_key, &ticket, ticket.mac); + if (ret < 0) { + gnutls_assert(); + goto cleanup2; + } + + pack_ticket(&ticket, &result); + ticket_data->data = result.data; + ticket_data->size = result.size; + result.data = NULL; + +cleanup2: + _gnutls_cipher_deinit(&cipher_hd); + +cleanup: + _gnutls_free_datum(&result); + + return ret; +} + +static int +unpack_session(gnutls_session_t session, const gnutls_datum_t *state) +{ + int ret; + + if (unlikely(!state)) + return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); + + ret = _gnutls_session_unpack(session, state); + if (ret < 0) + return gnutls_assert_val(ret); + + ret = _gnutls_check_resumed_params(session); + if (ret < 0) + return gnutls_assert_val(ret); + + session->internals.resumed = true; + return 0; +} + +static int +session_ticket_recv_params(gnutls_session_t session, + const uint8_t * data, size_t data_size) +{ + gnutls_datum_t ticket_data; + gnutls_datum_t state; + int ret; + + if (session->internals.flags & (GNUTLS_NO_TICKETS | GNUTLS_NO_TICKETS_TLS12)) + return 0; + + if (session->security_parameters.entity == GNUTLS_SERVER) { + /* The client requested a new session ticket. */ + if (data_size == 0) { + session->internals.session_ticket_renew = 1; + return 0; + } + + ticket_data.data = (void *)data; + ticket_data.size = data_size; + if ((ret = _gnutls_decrypt_session_ticket(session, &ticket_data, &state)) == 0) { + ret = unpack_session(session, &state); + + _gnutls_free_datum(&state); + } + + if (ret < 0) { + session->internals.session_ticket_renew = 1; + return 0; + } + } else { /* Client */ + + if (data_size == 0) { + session->internals.session_ticket_renew = 1; + return 0; + } + } + + return 0; +} + +/* returns a positive number if we send the extension data, (0) if we + do not want to send it, and a negative number on failure. + */ +static int +session_ticket_send_params(gnutls_session_t session, + gnutls_buffer_st * extdata) +{ + session_ticket_ext_st *priv = NULL; + gnutls_ext_priv_data_t epriv; + int ret; + + if (session->internals.flags & (GNUTLS_NO_TICKETS | GNUTLS_NO_TICKETS_TLS12)) + return 0; + + if (session->security_parameters.entity == GNUTLS_SERVER) { + if (session->internals.session_ticket_renew) { + return GNUTLS_E_INT_RET_0; + } + } else { + ret = + _gnutls_hello_ext_get_resumed_priv(session, + GNUTLS_EXTENSION_SESSION_TICKET, + &epriv); + if (ret >= 0) + priv = epriv; + + /* no previous data. Just advertise it */ + if (ret < 0) + return GNUTLS_E_INT_RET_0; + + /* previous data had session tickets disabled. Don't advertise. Ignore. */ + if (session->internals.flags & GNUTLS_NO_TICKETS) + return 0; + + if (priv->session_ticket_len > 0) { + ret = + _gnutls_buffer_append_data(extdata, + priv-> + session_ticket, + priv-> + session_ticket_len); + if (ret < 0) + return gnutls_assert_val(ret); + + return priv->session_ticket_len; + } + } + return 0; +} + + +static void session_ticket_deinit_data(gnutls_ext_priv_data_t epriv) +{ + session_ticket_ext_st *priv = epriv; + + gnutls_free(priv->session_ticket); + gnutls_free(priv); +} + +static int +session_ticket_pack(gnutls_ext_priv_data_t epriv, gnutls_buffer_st * ps) +{ + session_ticket_ext_st *priv = epriv; + int ret; + + BUFFER_APPEND_PFX4(ps, priv->session_ticket, + priv->session_ticket_len); + + return 0; +} + +static int +session_ticket_unpack(gnutls_buffer_st * ps, gnutls_ext_priv_data_t * _priv) +{ + session_ticket_ext_st *priv = NULL; + int ret; + gnutls_ext_priv_data_t epriv; + gnutls_datum_t ticket; + + priv = gnutls_calloc(1, sizeof(*priv)); + if (priv == NULL) { + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; + } + + BUFFER_POP_DATUM(ps, &ticket); + priv->session_ticket = ticket.data; + priv->session_ticket_len = ticket.size; + + epriv = priv; + *_priv = epriv; + + return 0; + + error: + gnutls_free(priv); + return ret; +} + + + +/** + * gnutls_session_ticket_key_generate: + * @key: is a pointer to a #gnutls_datum_t which will contain a newly + * created key. + * + * Generate a random key to encrypt security parameters within + * SessionTicket. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, or an + * error code. + * + * Since: 2.10.0 + **/ +int gnutls_session_ticket_key_generate(gnutls_datum_t * key) +{ + if (_gnutls_fips_mode_enabled()) { + int ret; + /* in FIPS140-2 mode gnutls_key_generate imposes + * some limits on allowed key size, thus it is not + * used. These limits do not affect this function as + * it does not generate a "key" but rather key material + * that includes nonces and other stuff. */ + key->data = gnutls_malloc(TICKET_MASTER_KEY_SIZE); + if (key->data == NULL) + return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + + key->size = TICKET_MASTER_KEY_SIZE; + ret = gnutls_rnd(GNUTLS_RND_RANDOM, key->data, key->size); + if (ret < 0) { + gnutls_free(key->data); + return ret; + } + return 0; + } else { + return gnutls_key_generate(key, TICKET_MASTER_KEY_SIZE); + } +} + +/** + * gnutls_session_ticket_enable_client: + * @session: is a #gnutls_session_t type. + * + * Request that the client should attempt session resumption using + * SessionTicket. This call is typically unnecessary as session + * tickets are enabled by default. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, or an + * error code. + * + * Since: 2.10.0 + **/ +int gnutls_session_ticket_enable_client(gnutls_session_t session) +{ + if (!session) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + session->internals.flags &= ~GNUTLS_NO_TICKETS; + + return 0; +} + +/** + * gnutls_session_ticket_enable_server: + * @session: is a #gnutls_session_t type. + * @key: key to encrypt session parameters. + * + * Request that the server should attempt session resumption using + * session tickets, i.e., by delegating storage to the client. + * @key must be initialized using gnutls_session_ticket_key_generate(). + * To avoid leaking that key, use gnutls_memset() prior to + * releasing it. + * + * The default ticket expiration time can be overridden using + * gnutls_db_set_cache_expiration(). + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, or an + * error code. + * + * Since: 2.10.0 + **/ +int +gnutls_session_ticket_enable_server(gnutls_session_t session, + const gnutls_datum_t * key) +{ + int ret; + + if (!session || !key || key->size != TICKET_MASTER_KEY_SIZE || !key->data) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + ret = _gnutls_initialize_session_ticket_key_rotation(session, key); + if (ret < 0) + return gnutls_assert_val(ret); + + session->internals.flags &= ~GNUTLS_NO_TICKETS; + + return 0; +} + +/* + * Return zero if session tickets haven't been enabled. + */ +int _gnutls_send_new_session_ticket(gnutls_session_t session, int again) +{ + mbuffer_st *bufel = NULL; + uint8_t *data = NULL, *p; + int data_size = 0; + int ret; + gnutls_datum_t state = { NULL, 0 }; + uint16_t epoch_saved = session->security_parameters.epoch_write; + gnutls_datum_t ticket_data; + + if (again == 0) { + if (session->internals.flags & (GNUTLS_NO_TICKETS | + GNUTLS_NO_TICKETS_TLS12)) { + return 0; + } + if (!session->key.stek_initialized) { + return 0; + } + if (!session->internals.session_ticket_renew) { + return 0; + } + + _gnutls_handshake_log + ("HSK[%p]: sending session ticket\n", session); + + /* XXX: Temporarily set write algorithms to be used. + _gnutls_write_connection_state_init() does this job, but it also + triggers encryption, while NewSessionTicket should not be + encrypted in the record layer. */ + ret = + _gnutls_epoch_set_keys(session, + session->security_parameters. + epoch_next, 0); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + /* Under TLS1.2 with session tickets, the session ID is used for different + * purposes than the TLS1.0 session ID. Ensure that there is an internally + * set value which the server will see on the original and resumed sessions */ + if (!session->internals.resumed) { + ret = _gnutls_generate_session_id(session->security_parameters. + session_id, + &session->security_parameters. + session_id_size); + if (ret < 0) { + gnutls_assert(); + return ret; + } + } + + session->security_parameters.epoch_write = + session->security_parameters.epoch_next; + + /* Pack security parameters. */ + ret = _gnutls_session_pack(session, &state); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + /* Generate an encrypted ticket */ + ret = _gnutls_encrypt_session_ticket(session, &state, &ticket_data); + session->security_parameters.epoch_write = epoch_saved; + _gnutls_free_datum(&state); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + bufel = + _gnutls_handshake_alloc(session, + 4 + 2 + ticket_data.size); + if (!bufel) { + gnutls_assert(); + _gnutls_free_datum(&ticket_data); + return GNUTLS_E_MEMORY_ERROR; + } + + data = _mbuffer_get_udata_ptr(bufel); + p = data; + + _gnutls_write_uint32(session->internals.expire_time, p); + p += 4; + + _gnutls_write_uint16(ticket_data.size, p); + p += 2; + + memcpy(p, ticket_data.data, ticket_data.size); + p += ticket_data.size; + + _gnutls_free_datum(&ticket_data); + + data_size = p - data; + + session->internals.hsk_flags |= HSK_TLS12_TICKET_SENT; + } + return _gnutls_send_handshake(session, data_size ? bufel : NULL, + GNUTLS_HANDSHAKE_NEW_SESSION_TICKET); +} + +/* + * Return zero if session tickets haven't been enabled. + */ +int _gnutls_recv_new_session_ticket(gnutls_session_t session) +{ + uint8_t *p; + int data_size; + gnutls_buffer_st buf; + uint16_t ticket_len; + int ret; + session_ticket_ext_st *priv = NULL; + gnutls_ext_priv_data_t epriv; + + if (session->internals.flags & (GNUTLS_NO_TICKETS | + GNUTLS_NO_TICKETS_TLS12)) + return 0; + if (!session->internals.session_ticket_renew) + return 0; + + /* This is the last flight and peer cannot be sure + * we have received it unless we notify him. So we + * wait for a message and retransmit if needed. */ + if (IS_DTLS(session) && !_dtls_is_async(session)) { + unsigned have; + mbuffer_st *bufel = NULL; + + have = gnutls_record_check_pending(session) + + record_check_unprocessed(session); + + if (have != 0) { + bufel = _mbuffer_head_get_first(&session->internals.record_buffer, NULL); + } + + if (have == 0 || (bufel && bufel->type != GNUTLS_HANDSHAKE)) { + ret = _dtls_wait_and_retransmit(session); + if (ret < 0) + return gnutls_assert_val(ret); + } + } + + ret = _gnutls_recv_handshake(session, + GNUTLS_HANDSHAKE_NEW_SESSION_TICKET, + 0, &buf); + if (ret < 0) + return gnutls_assert_val_fatal(ret); + + p = buf.data; + data_size = buf.length; + + DECR_LENGTH_COM(data_size, 4, ret = + GNUTLS_E_UNEXPECTED_PACKET_LENGTH; + goto error); + /* skip over lifetime hint */ + p += 4; + + DECR_LENGTH_COM(data_size, 2, ret = + GNUTLS_E_UNEXPECTED_PACKET_LENGTH; + goto error); + ticket_len = _gnutls_read_uint16(p); + p += 2; + + DECR_LENGTH_COM(data_size, ticket_len, ret = + GNUTLS_E_UNEXPECTED_PACKET_LENGTH; + goto error); + + priv = gnutls_calloc(1, sizeof(*priv)); + if (!priv) { + gnutls_assert(); + ret = GNUTLS_E_MEMORY_ERROR; + goto error; + } + if (ticket_len > 0) { + priv->session_ticket = + gnutls_realloc_fast(priv->session_ticket, ticket_len); + if (!priv->session_ticket) { + gnutls_free(priv); + gnutls_assert(); + ret = GNUTLS_E_MEMORY_ERROR; + goto error; + } + memcpy(priv->session_ticket, p, ticket_len); + } + priv->session_ticket_len = ticket_len; + epriv = priv; + + /* Discard the current session ID. (RFC5077 3.4) */ + ret = + _gnutls_generate_session_id(session->security_parameters. + session_id, + &session->security_parameters. + session_id_size); + if (ret < 0) { + gnutls_assert(); + session_ticket_deinit_data(epriv); + ret = GNUTLS_E_INTERNAL_ERROR; + goto error; + } + ret = 0; + + _gnutls_handshake_log + ("HSK[%p]: received session ticket\n", session); + session->internals.hsk_flags |= HSK_TICKET_RECEIVED; + + _gnutls_hello_ext_set_priv(session, + GNUTLS_EXTENSION_SESSION_TICKET, + epriv); + + error: + _gnutls_buffer_clear(&buf); + + return ret; +} diff --git a/lib/ext/session_ticket.h b/lib/ext/session_ticket.h new file mode 100644 index 0000000..da804ec --- /dev/null +++ b/lib/ext/session_ticket.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2009-2012 Free Software Foundation, Inc. + * + * Author: 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/> + * + */ + +#ifndef GNUTLS_LIB_EXT_SESSION_TICKET_H +#define GNUTLS_LIB_EXT_SESSION_TICKET_H + +#include <hello_ext.h> + +extern const hello_ext_entry_st ext_mod_session_ticket; + +int _gnutls_send_new_session_ticket(gnutls_session_t session, int again); +int _gnutls_recv_new_session_ticket(gnutls_session_t session); + +int _gnutls_encrypt_session_ticket(gnutls_session_t session, + const gnutls_datum_t *state, + gnutls_datum_t *ticket_data); +int _gnutls_decrypt_session_ticket(gnutls_session_t session, + const gnutls_datum_t *ticket_data, + gnutls_datum_t *state); + +#endif /* GNUTLS_LIB_EXT_SESSION_TICKET_H */ diff --git a/lib/ext/signature.c b/lib/ext/signature.c new file mode 100644 index 0000000..bb350f5 --- /dev/null +++ b/lib/ext/signature.c @@ -0,0 +1,587 @@ +/* + * Copyright (C) 2002-2016 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/> + * + */ + +/* This file contains the code for the Signature Algorithms TLS extension. + * This extension is currently gnutls specific. + */ + +#include "gnutls_int.h" +#include "errors.h" +#include "num.h" +#include <gnutls/gnutls.h> +#include <ext/signature.h> +#include <state.h> +#include <num.h> +#include <algorithms.h> +#include <abstract_int.h> + +/* + * Some (all SChannel) clients fail to send proper SigAlgs due to Micro$oft crazyness. + * Patch the extension for them. + */ +#ifdef ENABLE_GOST +#define GOST_SIG_FIXUP_SCHANNEL +#endif + +static int _gnutls_signature_algorithm_recv_params(gnutls_session_t + session, + const uint8_t * data, + size_t data_size); +static int _gnutls_signature_algorithm_send_params(gnutls_session_t + session, + gnutls_buffer_st * extdata); +static void signature_algorithms_deinit_data(gnutls_ext_priv_data_t priv); +static int signature_algorithms_pack(gnutls_ext_priv_data_t epriv, + gnutls_buffer_st * ps); +static int signature_algorithms_unpack(gnutls_buffer_st * ps, + gnutls_ext_priv_data_t * _priv); + +const hello_ext_entry_st ext_mod_sig = { + .name = "Signature Algorithms", + .tls_id = 13, + .gid = GNUTLS_EXTENSION_SIGNATURE_ALGORITHMS, + .validity = GNUTLS_EXT_FLAG_TLS | GNUTLS_EXT_FLAG_DTLS | GNUTLS_EXT_FLAG_CLIENT_HELLO, + .client_parse_point = GNUTLS_EXT_TLS, + .server_parse_point = GNUTLS_EXT_TLS, + .recv_func = _gnutls_signature_algorithm_recv_params, + .send_func = _gnutls_signature_algorithm_send_params, + .pack_func = signature_algorithms_pack, + .unpack_func = signature_algorithms_unpack, + .deinit_func = signature_algorithms_deinit_data, + .cannot_be_overriden = 1 +}; + +typedef struct { + /* TLS 1.2 signature algorithms */ + gnutls_sign_algorithm_t sign_algorithms[MAX_ALGOS]; + uint16_t sign_algorithms_size; +} sig_ext_st; + +/* generates a SignatureAndHashAlgorithm structure with length as prefix + * by using the setup priorities. + */ +int +_gnutls_sign_algorithm_write_params(gnutls_session_t session, + gnutls_buffer_st * extdata) +{ + uint8_t *p; + unsigned int len, i; + const sign_algorithm_st *aid, *prev = NULL; + uint8_t buffer[MAX_ALGOS*2]; + + p = buffer; + len = 0; + + /* This generates a list of TLS signature algorithms. It has + * limited duplicate detection, and does not add twice the same + * AID */ + + for (i=0;i<session->internals.priorities->sigalg.size;i++) { + aid = &session->internals.priorities->sigalg.entry[i]->aid; + + if (HAVE_UNKNOWN_SIGAID(aid)) + continue; + + if (prev && prev->id[0] == aid->id[0] && prev->id[1] == aid->id[1]) + continue; + + /* Ignore non-GOST sign types for CertReq */ + if (session->security_parameters.cs && + _gnutls_kx_is_vko_gost(session->security_parameters.cs->kx_algorithm) && + !_sign_is_gost(session->internals.priorities->sigalg.entry[i])) + continue; + + _gnutls_handshake_log + ("EXT[%p]: sent signature algo (%d.%d) %s\n", session, + (int)aid->id[0], (int)aid->id[1], + session->internals.priorities->sigalg.entry[i]->name); + + len += 2; + if (unlikely(len >= sizeof(buffer))) { + len -= 2; + break; + } + + *p = aid->id[0]; + p++; + *p = aid->id[1]; + p++; + prev = aid; + } + + return _gnutls_buffer_append_data_prefix(extdata, 16, buffer, len); +} + + +/* Parses the Signature Algorithm structure and stores data into + * session->security_parameters.extensions. + */ +int +_gnutls_sign_algorithm_parse_data(gnutls_session_t session, + const uint8_t * data, size_t data_size) +{ + unsigned int sig, i; + sig_ext_st *priv; + gnutls_ext_priv_data_t epriv; + const version_entry_st *ver = get_version(session); + + if (data_size == 0 || data_size % 2 != 0) + return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH); + + if (ver == NULL) { /* assume TLS 1.2 semantics */ + ver = version_to_entry(GNUTLS_TLS1_2); + if (unlikely(ver == NULL)) { + return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); + } + } + + priv = gnutls_calloc(1, sizeof(*priv)); + if (priv == NULL) { + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; + } + + for (i = 0; i < data_size; i += 2) { + uint8_t id[2]; + + id[0] = data[i]; + id[1] = data[i + 1]; + + sig = _gnutls_tls_aid_to_sign(id[0], id[1], ver); + + _gnutls_handshake_log + ("EXT[%p]: rcvd signature algo (%d.%d) %s\n", session, + (int)id[0], (int)id[1], + gnutls_sign_get_name(sig)); + + if (sig != GNUTLS_SIGN_UNKNOWN) { + if (priv->sign_algorithms_size == MAX_ALGOS) + break; + priv->sign_algorithms[priv-> + sign_algorithms_size++] = sig; + } + } + + epriv = priv; + _gnutls_hello_ext_set_priv(session, + GNUTLS_EXTENSION_SIGNATURE_ALGORITHMS, + epriv); + + return 0; +} + +/* + * In case of a server: if a SIGNATURE_ALGORITHMS extension type is + * received then it stores into the session security parameters the + * new value. + * + * In case of a client: If a signature_algorithms have been specified + * then it is an error; + */ + +static int +_gnutls_signature_algorithm_recv_params(gnutls_session_t session, + const uint8_t * data, + size_t data_size) +{ + int ret; + + if (session->security_parameters.entity == GNUTLS_CLIENT) { + /* nothing for now */ + gnutls_assert(); + /* Although TLS 1.2 mandates that we must not accept reply + * to this message, there are good reasons to just ignore it. Check + * https://www.ietf.org/mail-archive/web/tls/current/msg03880.html + */ + /* return GNUTLS_E_UNEXPECTED_PACKET; */ + } else { + /* SERVER SIDE + */ + if (data_size >= 2) { + uint16_t len; + + DECR_LEN(data_size, 2); + len = _gnutls_read_uint16(data); + DECR_LEN(data_size, len); + + if (data_size > 0) + return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH); + + ret = + _gnutls_sign_algorithm_parse_data(session, + data + 2, + len); + if (ret < 0) { + gnutls_assert(); + return ret; + } + } else { + return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH); + } + } + + return 0; +} + +/* returns data_size or a negative number on failure + */ +static int +_gnutls_signature_algorithm_send_params(gnutls_session_t session, + gnutls_buffer_st * extdata) +{ + int ret; + size_t init_length = extdata->length; + const version_entry_st *ver = get_version(session); + + if (unlikely(ver == NULL)) + return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); + + /* this function sends the client extension data */ + if (session->security_parameters.entity == GNUTLS_CLIENT + && _gnutls_version_has_selectable_sighash(ver)) { + if (session->internals.priorities->sigalg.size > 0) { + ret = + _gnutls_sign_algorithm_write_params(session, extdata); + if (ret < 0) + return gnutls_assert_val(ret); + + return extdata->length - init_length; + } + } + + /* if we are here it means we don't send the extension */ + return 0; +} + +#ifdef GOST_SIG_FIXUP_SCHANNEL +static bool +is_gost_sig_present(sig_ext_st *priv) +{ + unsigned i; + const gnutls_sign_entry_st *se; + + for (i = 0; i < priv->sign_algorithms_size; i++) { + se = _gnutls_sign_to_entry(priv->sign_algorithms[i]); + if (se != NULL && _sign_is_gost(se)) + return true; + } + + return false; +} +#endif + +/* Returns a requested by the peer signature algorithm that + * matches the given certificate's public key algorithm. + * + * When the @client_cert flag is not set, then this function will + * also check whether the signature algorithm is allowed to be + * used in that session. Otherwise GNUTLS_SIGN_UNKNOWN is + * returned. + */ +gnutls_sign_algorithm_t +_gnutls_session_get_sign_algo(gnutls_session_t session, + gnutls_pcert_st * cert, + gnutls_privkey_t privkey, + unsigned client_cert, + gnutls_kx_algorithm_t kx_algorithm) +{ + unsigned i; + int ret; + const version_entry_st *ver = get_version(session); + sig_ext_st *priv; + gnutls_ext_priv_data_t epriv; + unsigned int cert_algo; + const gnutls_sign_entry_st *se; + + if (unlikely(ver == NULL)) + return gnutls_assert_val(GNUTLS_SIGN_UNKNOWN); + + cert_algo = gnutls_pubkey_get_pk_algorithm(cert->pubkey, NULL); + + ret = + _gnutls_hello_ext_get_priv(session, + GNUTLS_EXTENSION_SIGNATURE_ALGORITHMS, + &epriv); + if (ret < 0) + priv = NULL; + else + priv = epriv; + +#ifdef GOST_SIG_FIXUP_SCHANNEL + /* + * Some (all SChannel) clients fail to send proper SigAlgs due to Micro$oft crazyness. + * If we are negotiating GOST KX (because we have received GOST + * ciphersuites) and if we have received no GOST SignatureAlgorithms, + * assume that the client could not send them and continue negotiation + * as if correct algorithm was sent. + */ + if (_gnutls_kx_is_vko_gost(kx_algorithm) && + (!priv || + !is_gost_sig_present(priv) || + !_gnutls_version_has_selectable_sighash(ver))) { + gnutls_digest_algorithm_t dig; + + _gnutls_handshake_log("EXT[%p]: GOST KX, but no GOST SigAlgs received, patching up.", session); + + if (cert_algo == GNUTLS_PK_GOST_01) + dig = GNUTLS_DIG_GOSTR_94; + else if (cert_algo == GNUTLS_PK_GOST_12_256) + dig = GNUTLS_DIG_STREEBOG_256; + else if (cert_algo == GNUTLS_PK_GOST_12_512) + dig = GNUTLS_DIG_STREEBOG_512; + else + dig = GNUTLS_DIG_SHA1; + + ret = gnutls_pk_to_sign(cert_algo, dig); + + if (!client_cert && _gnutls_session_sign_algo_enabled(session, ret) < 0) + goto fail; + return ret; + } +#endif + + if (!priv || !_gnutls_version_has_selectable_sighash(ver)) { + /* none set, allow SHA-1 only */ + ret = gnutls_pk_to_sign(cert_algo, GNUTLS_DIG_SHA1); + + if (!client_cert && _gnutls_session_sign_algo_enabled(session, ret) < 0) + goto fail; + return ret; + } + + + + for (i = 0; i < priv->sign_algorithms_size; i++) { + se = _gnutls_sign_to_entry(priv->sign_algorithms[i]); + if (se == NULL) + continue; + + _gnutls_handshake_log("checking cert compat with %s\n", se->name); + + if (_gnutls_privkey_compatible_with_sig(privkey, priv->sign_algorithms[i]) == 0) + continue; + + if (sign_supports_cert_pk_algorithm(se, cert_algo) != 0) { + if (_gnutls_pubkey_compatible_with_sig + (session, cert->pubkey, ver, se->id) < 0) + continue; + + if (_gnutls_session_sign_algo_enabled + (session, se->id) < 0) + continue; + + return se->id; + } + } + + /* When having a legacy client certificate which can only be signed + * using algorithms we don't always enable by default (e.g., DSA-SHA1), + * continue and sign with it. */ + if (client_cert) { + _gnutls_audit_log(session, "No shared signature schemes with peer for client certificate (%s). Is the certificate a legacy one?\n", + gnutls_pk_get_name(cert_algo)); + } + + fail: + return GNUTLS_SIGN_UNKNOWN; +} + +/* Check if the given signature algorithm is supported. + * This means that it is enabled by the priority functions, + * and in case of a server a matching certificate exists. + */ +int +_gnutls_session_sign_algo_enabled(gnutls_session_t session, + gnutls_sign_algorithm_t sig) +{ + unsigned i; + const version_entry_st *ver = get_version(session); + + if (unlikely(ver == NULL)) + return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); + + if (!_gnutls_version_has_selectable_sighash(ver)) { + return 0; + } + + if (ver->tls13_sem) { + /* disallow RSA, DSA, and SHA1 */ + const gnutls_sign_entry_st *se; + + se = _gnutls_sign_to_entry(sig); + if (se == NULL || (se->flags & GNUTLS_SIGN_FLAG_TLS13_OK) == 0) { + gnutls_assert(); + goto disallowed; + } + } + + for (i = 0; i < session->internals.priorities->sigalg.size; i++) { + if (session->internals.priorities->sigalg.entry[i]->id == sig) { + return 0; /* ok */ + } + } + + disallowed: + _gnutls_handshake_log("Signature algorithm %s is not enabled\n", gnutls_sign_algorithm_get_name(sig)); + return GNUTLS_E_UNSUPPORTED_SIGNATURE_ALGORITHM; +} + +static void signature_algorithms_deinit_data(gnutls_ext_priv_data_t priv) +{ + gnutls_free(priv); +} + +static int +signature_algorithms_pack(gnutls_ext_priv_data_t epriv, + gnutls_buffer_st * ps) +{ + sig_ext_st *priv = epriv; + int ret, i; + + BUFFER_APPEND_NUM(ps, priv->sign_algorithms_size); + for (i = 0; i < priv->sign_algorithms_size; i++) { + BUFFER_APPEND_NUM(ps, priv->sign_algorithms[i]); + } + return 0; +} + +static int +signature_algorithms_unpack(gnutls_buffer_st * ps, + gnutls_ext_priv_data_t * _priv) +{ + sig_ext_st *priv; + int i, ret; + gnutls_ext_priv_data_t epriv; + + priv = gnutls_calloc(1, sizeof(*priv)); + if (priv == NULL) { + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; + } + + BUFFER_POP_NUM(ps, priv->sign_algorithms_size); + for (i = 0; i < priv->sign_algorithms_size; i++) { + BUFFER_POP_NUM(ps, priv->sign_algorithms[i]); + } + + epriv = priv; + *_priv = epriv; + + return 0; + + error: + gnutls_free(priv); + return ret; +} + + + +/** + * gnutls_sign_algorithm_get_requested: + * @session: is a #gnutls_session_t type. + * @indx: is an index of the signature algorithm to return + * @algo: the returned certificate type will be stored there + * + * Returns the signature algorithm specified by index that was + * requested by the peer. If the specified index has no data available + * this function returns %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE. If + * the negotiated TLS version does not support signature algorithms + * then %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE will be returned even + * for the first index. The first index is 0. + * + * This function is useful in the certificate callback functions + * to assist in selecting the correct certificate. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise + * an error code is returned. + * + * Since: 2.10.0 + **/ +int +gnutls_sign_algorithm_get_requested(gnutls_session_t session, + size_t indx, + gnutls_sign_algorithm_t * algo) +{ + const version_entry_st *ver = get_version(session); + sig_ext_st *priv; + gnutls_ext_priv_data_t epriv; + int ret; + + if (unlikely(ver == NULL)) + return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); + + ret = + _gnutls_hello_ext_get_priv(session, + GNUTLS_EXTENSION_SIGNATURE_ALGORITHMS, + &epriv); + if (ret < 0) { + gnutls_assert(); + return ret; + } + priv = epriv; + + if (!_gnutls_version_has_selectable_sighash(ver) + || priv->sign_algorithms_size == 0) { + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + } + + if (indx < priv->sign_algorithms_size) { + *algo = priv->sign_algorithms[indx]; + return 0; + } else + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; +} + +/** + * gnutls_sign_algorithm_get: + * @session: is a #gnutls_session_t type. + * + * Returns the signature algorithm that is (or will be) used in this + * session by the server to sign data. This function should be + * used only with TLS 1.2 or later. + * + * Returns: The sign algorithm or %GNUTLS_SIGN_UNKNOWN. + * + * Since: 3.1.1 + **/ +int gnutls_sign_algorithm_get(gnutls_session_t session) +{ + return session->security_parameters.server_sign_algo; +} + +/** + * gnutls_sign_algorithm_get_client: + * @session: is a #gnutls_session_t type. + * + * Returns the signature algorithm that is (or will be) used in this + * session by the client to sign data. This function should be + * used only with TLS 1.2 or later. + * + * Returns: The sign algorithm or %GNUTLS_SIGN_UNKNOWN. + * + * Since: 3.1.11 + **/ +int gnutls_sign_algorithm_get_client(gnutls_session_t session) +{ + return session->security_parameters.client_sign_algo; +} diff --git a/lib/ext/signature.h b/lib/ext/signature.h new file mode 100644 index 0000000..ef42763 --- /dev/null +++ b/lib/ext/signature.h @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2002-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_EXT_SIGNATURE_H +#define GNUTLS_LIB_EXT_SIGNATURE_H + +/* signature algorithms extension + */ + +#include <hello_ext.h> + +extern const hello_ext_entry_st ext_mod_sig; + +gnutls_sign_algorithm_t +_gnutls_session_get_sign_algo(gnutls_session_t session, + gnutls_pcert_st * cert, + gnutls_privkey_t privkey, + unsigned client_cert, + gnutls_kx_algorithm_t kx_algorithm); +int _gnutls_sign_algorithm_parse_data(gnutls_session_t session, + const uint8_t * data, + size_t data_size); +int _gnutls_sign_algorithm_write_params(gnutls_session_t session, + gnutls_buffer_st * extdata); +int _gnutls_session_sign_algo_enabled(gnutls_session_t session, + gnutls_sign_algorithm_t sig); + +static inline void +gnutls_sign_algorithm_set_server(gnutls_session_t session, + gnutls_sign_algorithm_t sign) +{ + session->security_parameters.server_sign_algo = sign; +} + +static inline void +gnutls_sign_algorithm_set_client(gnutls_session_t session, + gnutls_sign_algorithm_t sign) +{ + session->security_parameters.client_sign_algo = sign; +} + +#endif /* GNUTLS_LIB_EXT_SIGNATURE_H */ diff --git a/lib/ext/srp.c b/lib/ext/srp.c new file mode 100644 index 0000000..f236c64 --- /dev/null +++ b/lib/ext/srp.c @@ -0,0 +1,277 @@ +/* + * Copyright (C) 2001-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/> + * + */ + +#include "gnutls_int.h" +#include <ext/srp.h> + +#ifdef ENABLE_SRP + +#include "auth.h" +#include <auth/srp_kx.h> +#include "errors.h" +#include "algorithms.h" +#include <num.h> +#include <hello_ext.h> + +static int _gnutls_srp_unpack(gnutls_buffer_st * ps, + gnutls_ext_priv_data_t * _priv); +static int _gnutls_srp_pack(gnutls_ext_priv_data_t epriv, + gnutls_buffer_st * ps); +static void _gnutls_srp_deinit_data(gnutls_ext_priv_data_t epriv); +static int _gnutls_srp_recv_params(gnutls_session_t state, + const uint8_t * data, size_t data_size); +static int _gnutls_srp_send_params(gnutls_session_t state, + gnutls_buffer_st * extdata); + +const hello_ext_entry_st ext_mod_srp = { + .name = "SRP", + .tls_id = 12, + .gid = GNUTLS_EXTENSION_SRP, + .client_parse_point = GNUTLS_EXT_TLS, + .server_parse_point = GNUTLS_EXT_TLS, + .validity = GNUTLS_EXT_FLAG_TLS | GNUTLS_EXT_FLAG_DTLS | GNUTLS_EXT_FLAG_CLIENT_HELLO, + .recv_func = _gnutls_srp_recv_params, + .send_func = _gnutls_srp_send_params, + .pack_func = _gnutls_srp_pack, + .unpack_func = _gnutls_srp_unpack, + .deinit_func = _gnutls_srp_deinit_data, + .cannot_be_overriden = 1 +}; + + +static int +_gnutls_srp_recv_params(gnutls_session_t session, const uint8_t * data, + size_t data_size) +{ + uint8_t len; + gnutls_ext_priv_data_t epriv; + srp_ext_st *priv; + + if (session->security_parameters.entity == GNUTLS_SERVER) { + if (data_size > 0) { + DECR_LEN(data_size, 1); + + len = data[0]; + DECR_LEN(data_size, len); + + priv = gnutls_calloc(1, sizeof(*priv)); + if (priv == NULL) { + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; + } + + priv->username = gnutls_malloc(len + 1); + if (priv->username) { + memcpy(priv->username, &data[1], len); + /* null terminated */ + priv->username[len] = 0; + } + + epriv = priv; + _gnutls_hello_ext_set_priv(session, + GNUTLS_EXTENSION_SRP, + epriv); + } + } + return 0; +} + +static unsigned have_srp_ciphersuites(gnutls_session_t session) +{ + unsigned j; + unsigned kx; + + for (j = 0; j < session->internals.priorities->cs.size; j++) { + kx = session->internals.priorities->cs.entry[j]->kx_algorithm; + if (kx == GNUTLS_KX_SRP || kx == GNUTLS_KX_SRP_RSA || kx == GNUTLS_KX_SRP_DSS) + return 1; + } + + return 0; +} + +/* returns data_size or a negative number on failure + * data is allocated locally + */ +static int +_gnutls_srp_send_params(gnutls_session_t session, + gnutls_buffer_st * extdata) +{ + unsigned len; + int ret; + gnutls_ext_priv_data_t epriv; + srp_ext_st *priv = NULL; + char *username = NULL, *password = NULL; + gnutls_srp_client_credentials_t cred = + (gnutls_srp_client_credentials_t) + _gnutls_get_cred(session, GNUTLS_CRD_SRP); + + if (session->security_parameters.entity != GNUTLS_CLIENT) + return 0; + + if (cred == NULL) + return 0; + + if (!have_srp_ciphersuites(session)) { + return 0; + } + + /* this function sends the client extension data (username) */ + priv = gnutls_calloc(1, sizeof(*priv)); + if (priv == NULL) + return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + + if (cred->username != NULL) { /* send username */ + len = MIN(strlen(cred->username), 255); + + ret = + _gnutls_buffer_append_data_prefix(extdata, 8, + cred-> + username, + len); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + priv->username = gnutls_strdup(cred->username); + if (priv->username == NULL) { + gnutls_assert(); + goto cleanup; + } + + priv->password = gnutls_strdup(cred->password); + if (priv->password == NULL) { + gnutls_assert(); + goto cleanup; + } + + epriv = priv; + _gnutls_hello_ext_set_priv(session, + GNUTLS_EXTENSION_SRP, + epriv); + + return len + 1; + } else if (cred->get_function != NULL) { + /* Try the callback + */ + + if (cred-> + get_function(session, &username, &password) < 0 + || username == NULL || password == NULL) { + gnutls_assert(); + return GNUTLS_E_ILLEGAL_SRP_USERNAME; + } + + len = MIN(strlen(username), 255); + + priv->username = username; + priv->password = password; + + ret = + _gnutls_buffer_append_data_prefix(extdata, 8, + username, + len); + if (ret < 0) { + ret = gnutls_assert_val(ret); + goto cleanup; + } + + epriv = priv; + _gnutls_hello_ext_set_priv(session, + GNUTLS_EXTENSION_SRP, + epriv); + + return len + 1; + } + return 0; + + cleanup: + gnutls_free(username); + gnutls_free(password); + gnutls_free(priv); + + return ret; +} + +static void _gnutls_srp_deinit_data(gnutls_ext_priv_data_t epriv) +{ + srp_ext_st *priv = epriv; + + gnutls_free(priv->username); + gnutls_free(priv->password); + gnutls_free(priv); +} + +static int +_gnutls_srp_pack(gnutls_ext_priv_data_t epriv, gnutls_buffer_st * ps) +{ + srp_ext_st *priv = epriv; + int ret; + int password_len = 0, username_len = 0; + + if (priv->username) + username_len = strlen(priv->username); + + if (priv->password) + password_len = strlen(priv->password); + + BUFFER_APPEND_PFX4(ps, priv->username, username_len); + BUFFER_APPEND_PFX4(ps, priv->password, password_len); + + return 0; +} + +static int +_gnutls_srp_unpack(gnutls_buffer_st * ps, gnutls_ext_priv_data_t * _priv) +{ + srp_ext_st *priv; + int ret; + gnutls_ext_priv_data_t epriv; + gnutls_datum_t username = { NULL, 0 }; + gnutls_datum_t password = { NULL, 0 }; + + priv = gnutls_calloc(1, sizeof(*priv)); + if (priv == NULL) { + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; + } + + BUFFER_POP_DATUM(ps, &username); + BUFFER_POP_DATUM(ps, &password); + + priv->username = (char *) username.data; + priv->password = (char *) password.data; + + epriv = priv; + *_priv = epriv; + + return 0; + + error: + _gnutls_free_datum(&username); + _gnutls_free_datum(&password); + return ret; +} + + +#endif /* ENABLE_SRP */ diff --git a/lib/ext/srp.h b/lib/ext/srp.h new file mode 100644 index 0000000..fd7576f --- /dev/null +++ b/lib/ext/srp.h @@ -0,0 +1,42 @@ +/* + * 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_EXT_SRP_H +#define GNUTLS_LIB_EXT_SRP_H + +#include <hello_ext.h> + +#define IS_SRP_KX(kx) ((kx == GNUTLS_KX_SRP || (kx == GNUTLS_KX_SRP_RSA) || \ + kx == GNUTLS_KX_SRP_DSS)?1:0) + +#ifdef ENABLE_SRP + +extern const hello_ext_entry_st ext_mod_srp; + +typedef struct { + char *username; + char *password; +} srp_ext_st; + +#endif + +#endif /* GNUTLS_LIB_EXT_SRP_H */ diff --git a/lib/ext/srtp.c b/lib/ext/srtp.c new file mode 100644 index 0000000..b2e36b3 --- /dev/null +++ b/lib/ext/srtp.c @@ -0,0 +1,672 @@ +/* + * Copyright (C) 2012 Free Software Foundation + * + * Author: Martin Storsjo + * + * 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 "auth.h" +#include "errors.h" +#include "num.h" +#include <ext/srtp.h> + +static int _gnutls_srtp_recv_params(gnutls_session_t session, + const uint8_t * data, + size_t data_size); +static int _gnutls_srtp_send_params(gnutls_session_t session, + gnutls_buffer_st * extdata); + +static int _gnutls_srtp_unpack(gnutls_buffer_st * ps, + gnutls_ext_priv_data_t * _priv); +static int _gnutls_srtp_pack(gnutls_ext_priv_data_t _priv, + gnutls_buffer_st * ps); +static void _gnutls_srtp_deinit_data(gnutls_ext_priv_data_t priv); + + +const hello_ext_entry_st ext_mod_srtp = { + .name = "SRTP", + .tls_id = 14, + .gid = GNUTLS_EXTENSION_SRTP, + .validity = GNUTLS_EXT_FLAG_TLS | GNUTLS_EXT_FLAG_DTLS | GNUTLS_EXT_FLAG_CLIENT_HELLO | + GNUTLS_EXT_FLAG_EE | GNUTLS_EXT_FLAG_TLS12_SERVER_HELLO, + .client_parse_point = GNUTLS_EXT_APPLICATION, + .server_parse_point = GNUTLS_EXT_APPLICATION, + .recv_func = _gnutls_srtp_recv_params, + .send_func = _gnutls_srtp_send_params, + .pack_func = _gnutls_srtp_pack, + .unpack_func = _gnutls_srtp_unpack, + .deinit_func = _gnutls_srtp_deinit_data, + .cannot_be_overriden = 1 +}; + +typedef struct { + const char *name; + gnutls_srtp_profile_t id; + unsigned int key_length; + unsigned int salt_length; +} srtp_profile_st; + +static const srtp_profile_st profile_names[] = { + { + "SRTP_AES128_CM_HMAC_SHA1_80", + GNUTLS_SRTP_AES128_CM_HMAC_SHA1_80, + 16, 14}, + { + "SRTP_AES128_CM_HMAC_SHA1_32", + GNUTLS_SRTP_AES128_CM_HMAC_SHA1_32, + 16, 14}, + { + "SRTP_NULL_HMAC_SHA1_80", + GNUTLS_SRTP_NULL_HMAC_SHA1_80, + 16, 14}, + { + "SRTP_NULL_SHA1_32", + GNUTLS_SRTP_NULL_HMAC_SHA1_32, + 16, 14}, + { + NULL, + 0, 0, 0} +}; + +static const srtp_profile_st *get_profile(gnutls_srtp_profile_t profile) +{ + const srtp_profile_st *p = profile_names; + while (p->name != NULL) { + if (p->id == profile) + return p; + p++; + } + return NULL; +} + +static gnutls_srtp_profile_t find_profile(const char *str, const char *end) +{ + const srtp_profile_st *prof = profile_names; + unsigned int len; + if (end != NULL) { + len = end - str; + } else { + len = strlen(str); + } + + while (prof->name != NULL) { + if (strlen(prof->name) == len + && !strncmp(str, prof->name, len)) { + return prof->id; + } + prof++; + } + return 0; +} + +/** + * gnutls_srtp_get_profile_id + * @name: The name of the profile to look up + * @profile: Will hold the profile id + * + * This function allows you to look up a profile based on a string. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, + * otherwise a negative error code is returned. + * + * Since 3.1.4 + **/ +int gnutls_srtp_get_profile_id(const char *name, + gnutls_srtp_profile_t * profile) +{ + *profile = find_profile(name, NULL); + if (*profile == 0) { + return GNUTLS_E_ILLEGAL_PARAMETER; + } + return 0; +} + +#define MAX_PROFILES_IN_SRTP_EXTENSION 256 + +/** + * gnutls_srtp_get_profile_name + * @profile: The profile to look up a string for + * + * This function allows you to get the corresponding name for a + * SRTP protection profile. + * + * Returns: On success, the name of a SRTP profile as a string, + * otherwise NULL. + * + * Since 3.1.4 + **/ +const char *gnutls_srtp_get_profile_name(gnutls_srtp_profile_t profile) +{ + const srtp_profile_st *p = get_profile(profile); + + if (p != NULL) + return p->name; + + return NULL; +} + +static int +_gnutls_srtp_recv_params(gnutls_session_t session, + const uint8_t * data, size_t data_size) +{ + unsigned int i; + int ret; + const uint8_t *p = data; + size_t len; + srtp_ext_st *priv; + gnutls_ext_priv_data_t epriv; + uint16_t profile; + + ret = + _gnutls_hello_ext_get_priv(session, GNUTLS_EXTENSION_SRTP, + &epriv); + if (ret < 0) + return 0; + + priv = epriv; + + DECR_LENGTH_RET(data_size, 2, 0); + len = _gnutls_read_uint16(p); + p += 2; + + if (len + 1 > data_size) + return + gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH); + + if (session->security_parameters.entity == GNUTLS_SERVER) { + if (len > MAX_PROFILES_IN_SRTP_EXTENSION * 2) + return 0; + } else { + if (len != 2) + return + gnutls_assert_val + (GNUTLS_E_UNEXPECTED_PACKET_LENGTH); + } + + priv->selected_profile = 0; + + while (len > 0) { + DECR_LEN(data_size, 2); + profile = _gnutls_read_uint16(p); + + for (i = 0; + i < priv->profiles_size + && priv->selected_profile == 0; i++) { + if (priv->profiles[i] == profile) { + priv->selected_profile = profile; + break; + } + } + p += 2; + len -= 2; + } + + DECR_LEN(data_size, 1); + priv->mki_size = *p; + p++; + + if (priv->mki_size > 0) { + DECR_LEN(data_size, priv->mki_size); + memcpy(priv->mki, p, priv->mki_size); + priv->mki_received = 1; + } + + return 0; +} + +static int +_gnutls_srtp_send_params(gnutls_session_t session, + gnutls_buffer_st * extdata) +{ + unsigned i; + int total_size = 0, ret; + srtp_ext_st *priv; + gnutls_ext_priv_data_t epriv; + + ret = + _gnutls_hello_ext_get_priv(session, GNUTLS_EXTENSION_SRTP, + &epriv); + if (ret < 0) + return 0; + + priv = epriv; + + if (priv->profiles_size == 0) + return 0; + + if (session->security_parameters.entity == GNUTLS_SERVER) { + /* Don't send anything if no matching profile was found */ + if (priv->selected_profile == 0) + return 0; + + ret = _gnutls_buffer_append_prefix(extdata, 16, 2); + if (ret < 0) + return gnutls_assert_val(ret); + ret = + _gnutls_buffer_append_prefix(extdata, 16, + priv->selected_profile); + if (ret < 0) + return gnutls_assert_val(ret); + total_size = 4; + } else { + ret = + _gnutls_buffer_append_prefix(extdata, 16, + 2 * priv->profiles_size); + if (ret < 0) + return gnutls_assert_val(ret); + + for (i = 0; i < priv->profiles_size; i++) { + ret = + _gnutls_buffer_append_prefix(extdata, 16, + priv-> + profiles[i]); + if (ret < 0) + return gnutls_assert_val(ret); + } + total_size = 2 + 2 * priv->profiles_size; + } + + /* use_mki */ + ret = + _gnutls_buffer_append_data_prefix(extdata, 8, priv->mki, + priv->mki_size); + if (ret < 0) + return gnutls_assert_val(ret); + total_size += 1 + priv->mki_size; + + return total_size; +} + +/** + * gnutls_srtp_get_selected_profile: + * @session: is a #gnutls_session_t type. + * @profile: will hold the profile + * + * This function allows you to get the negotiated SRTP profile. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, + * otherwise a negative error code is returned. + * + * Since 3.1.4 + **/ +int +gnutls_srtp_get_selected_profile(gnutls_session_t session, + gnutls_srtp_profile_t * profile) +{ + srtp_ext_st *priv; + int ret; + gnutls_ext_priv_data_t epriv; + + ret = + _gnutls_hello_ext_get_priv(session, GNUTLS_EXTENSION_SRTP, + &epriv); + if (ret < 0) { + gnutls_assert(); + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + } + + priv = epriv; + + if (priv->selected_profile == 0) { + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + } + + *profile = priv->selected_profile; + + return 0; +} + +/** + * gnutls_srtp_get_mki: + * @session: is a #gnutls_session_t type. + * @mki: will hold the MKI + * + * This function exports the negotiated Master Key Identifier, + * received by the peer if any. The returned value in @mki should be + * treated as constant and valid only during the session's lifetime. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, + * otherwise a negative error code is returned. + * + * Since 3.1.4 + **/ +int gnutls_srtp_get_mki(gnutls_session_t session, gnutls_datum_t * mki) +{ + srtp_ext_st *priv; + int ret; + gnutls_ext_priv_data_t epriv; + + ret = + _gnutls_hello_ext_get_priv(session, GNUTLS_EXTENSION_SRTP, + &epriv); + if (ret < 0) + return + gnutls_assert_val + (GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE); + + priv = epriv; + + if (priv->mki_received == 0) + return + gnutls_assert_val + (GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE); + + mki->data = priv->mki; + mki->size = priv->mki_size; + + return 0; +} + +/** + * gnutls_srtp_set_mki: + * @session: is a #gnutls_session_t type. + * @mki: holds the MKI + * + * This function sets the Master Key Identifier, to be + * used by this session (if any). + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, + * otherwise a negative error code is returned. + * + * Since 3.1.4 + **/ +int +gnutls_srtp_set_mki(gnutls_session_t session, const gnutls_datum_t * mki) +{ + int ret; + srtp_ext_st *priv; + gnutls_ext_priv_data_t epriv; + + ret = + _gnutls_hello_ext_get_priv(session, GNUTLS_EXTENSION_SRTP, + &epriv); + if (ret < 0) { + priv = gnutls_calloc(1, sizeof(*priv)); + if (priv == NULL) { + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; + } + epriv = priv; + _gnutls_hello_ext_set_priv(session, + GNUTLS_EXTENSION_SRTP, epriv); + } else + priv = epriv; + + if (mki->size > 0 && mki->size <= sizeof(priv->mki)) { + priv->mki_size = mki->size; + memcpy(priv->mki, mki->data, mki->size); + } else + return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + + return 0; +} + +/** + * gnutls_srtp_set_profile: + * @session: is a #gnutls_session_t type. + * @profile: is the profile id to add. + * + * This function is to be used by both clients and servers, to declare + * what SRTP profiles they support, to negotiate with the peer. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, + * otherwise a negative error code is returned. + * + * Since 3.1.4 + **/ +int +gnutls_srtp_set_profile(gnutls_session_t session, + gnutls_srtp_profile_t profile) +{ + int ret; + srtp_ext_st *priv; + gnutls_ext_priv_data_t epriv; + + ret = + _gnutls_hello_ext_get_priv(session, GNUTLS_EXTENSION_SRTP, + &epriv); + if (ret < 0) { + priv = gnutls_calloc(1, sizeof(*priv)); + if (priv == NULL) { + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; + } + epriv = priv; + _gnutls_hello_ext_set_priv(session, + GNUTLS_EXTENSION_SRTP, epriv); + } else + priv = epriv; + + if (priv->profiles_size < MAX_SRTP_PROFILES) + priv->profiles_size++; + priv->profiles[priv->profiles_size - 1] = profile; + + return 0; +} + +/** + * gnutls_srtp_set_profile_direct: + * @session: is a #gnutls_session_t type. + * @profiles: is a string that contains the supported SRTP profiles, + * separated by colons. + * @err_pos: In case of an error this will have the position in the string the error occurred, may be NULL. + * + * This function is to be used by both clients and servers, to declare + * what SRTP profiles they support, to negotiate with the peer. + * + * Returns: On syntax error %GNUTLS_E_INVALID_REQUEST is returned, + * %GNUTLS_E_SUCCESS on success, or an error code. + * + * Since 3.1.4 + **/ +int +gnutls_srtp_set_profile_direct(gnutls_session_t session, + const char *profiles, const char **err_pos) +{ + int ret; + srtp_ext_st *priv; + gnutls_ext_priv_data_t epriv; + int set = 0; + const char *col; + gnutls_srtp_profile_t id; + + ret = + _gnutls_hello_ext_get_priv(session, GNUTLS_EXTENSION_SRTP, + &epriv); + if (ret < 0) { + set = 1; + priv = gnutls_calloc(1, sizeof(*priv)); + if (priv == NULL) { + if (err_pos != NULL) + *err_pos = profiles; + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; + } + epriv = priv; + } else + priv = epriv; + + do { + col = strchr(profiles, ':'); + id = find_profile(profiles, col); + if (id == 0) { + if (set != 0) + gnutls_free(priv); + if (err_pos != NULL) + *err_pos = profiles; + return GNUTLS_E_INVALID_REQUEST; + } + + if (priv->profiles_size < MAX_SRTP_PROFILES) { + priv->profiles_size++; + } + priv->profiles[priv->profiles_size - 1] = id; + profiles = col + 1; + } while (col != NULL); + + if (set != 0) + _gnutls_hello_ext_set_priv(session, + GNUTLS_EXTENSION_SRTP, epriv); + + return 0; +} + +/** + * gnutls_srtp_get_keys: + * @session: is a #gnutls_session_t type. + * @key_material: Space to hold the generated key material + * @key_material_size: The maximum size of the key material + * @client_key: The master client write key, pointing inside the key material + * @server_key: The master server write key, pointing inside the key material + * @client_salt: The master client write salt, pointing inside the key material + * @server_salt: The master server write salt, pointing inside the key material + * + * This is a helper function to generate the keying material for SRTP. + * It requires the space of the key material to be pre-allocated (should be at least + * 2x the maximum key size and salt size). The @client_key, @client_salt, @server_key + * and @server_salt are convenience datums that point inside the key material. They may + * be %NULL. + * + * Returns: On success the size of the key material is returned, + * otherwise, %GNUTLS_E_SHORT_MEMORY_BUFFER if the buffer given is not + * sufficient, or a negative error code. + * + * Since 3.1.4 + **/ +int +gnutls_srtp_get_keys(gnutls_session_t session, + void *key_material, + unsigned int key_material_size, + gnutls_datum_t * client_key, + gnutls_datum_t * client_salt, + gnutls_datum_t * server_key, + gnutls_datum_t * server_salt) +{ + int ret; + const srtp_profile_st *p; + gnutls_srtp_profile_t profile; + unsigned int msize; + uint8_t *km = key_material; + + ret = gnutls_srtp_get_selected_profile(session, &profile); + if (ret < 0) + return gnutls_assert_val(ret); + + p = get_profile(profile); + if (p == NULL) + return gnutls_assert_val(GNUTLS_E_UNKNOWN_ALGORITHM); + + msize = 2 * (p->key_length + p->salt_length); + if (msize > key_material_size) + return gnutls_assert_val(GNUTLS_E_SHORT_MEMORY_BUFFER); + + if (msize == 0) + return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + + ret = + gnutls_prf(session, sizeof("EXTRACTOR-dtls_srtp") - 1, + "EXTRACTOR-dtls_srtp", 0, 0, NULL, msize, + key_material); + if (ret < 0) + return gnutls_assert_val(ret); + + if (client_key) { + client_key->data = km; + client_key->size = p->key_length; + } + + if (server_key) { + server_key->data = km + p->key_length; + server_key->size = p->key_length; + } + + if (client_salt) { + client_salt->data = km + 2 * p->key_length; + client_salt->size = p->salt_length; + } + + if (server_salt) { + server_salt->data = + km + 2 * p->key_length + p->salt_length; + server_salt->size = p->salt_length; + } + + return msize; +} + +static void _gnutls_srtp_deinit_data(gnutls_ext_priv_data_t priv) +{ + gnutls_free(priv); +} + +static int +_gnutls_srtp_pack(gnutls_ext_priv_data_t epriv, gnutls_buffer_st * ps) +{ + srtp_ext_st *priv = epriv; + unsigned int i; + int ret; + + BUFFER_APPEND_NUM(ps, priv->profiles_size); + for (i = 0; i < priv->profiles_size; i++) { + BUFFER_APPEND_NUM(ps, priv->profiles[i]); + } + + BUFFER_APPEND_NUM(ps, priv->mki_received); + if (priv->mki_received) { + BUFFER_APPEND_NUM(ps, priv->selected_profile); + BUFFER_APPEND_PFX4(ps, priv->mki, priv->mki_size); + } + return 0; +} + +static int +_gnutls_srtp_unpack(gnutls_buffer_st * ps, gnutls_ext_priv_data_t * _priv) +{ + srtp_ext_st *priv; + unsigned int i; + int ret; + gnutls_ext_priv_data_t epriv; + + priv = gnutls_calloc(1, sizeof(*priv)); + if (priv == NULL) { + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; + } + + BUFFER_POP_NUM(ps, priv->profiles_size); + for (i = 0; i < priv->profiles_size; i++) { + BUFFER_POP_NUM(ps, priv->profiles[i]); + } + BUFFER_POP_NUM(ps, priv->selected_profile); + + BUFFER_POP_NUM(ps, priv->mki_received); + if (priv->mki_received) { + BUFFER_POP_NUM(ps, priv->mki_size); + BUFFER_POP(ps, priv->mki, priv->mki_size); + } + + epriv = priv; + *_priv = epriv; + + return 0; + + error: + gnutls_free(priv); + return ret; +} diff --git a/lib/ext/srtp.h b/lib/ext/srtp.h new file mode 100644 index 0000000..d05454a --- /dev/null +++ b/lib/ext/srtp.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2012 Free Software Foundation + * + * Author: Martin Storsjo + * + * 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_EXT_SRTP_H +#define GNUTLS_LIB_EXT_SRTP_H +#ifndef EXT_SRTP_H +#define EXT_SRTP_H + +#include <hello_ext.h> + +#define MAX_SRTP_PROFILES 4 + +typedef struct { + gnutls_srtp_profile_t profiles[MAX_SRTP_PROFILES]; + unsigned profiles_size; + gnutls_srtp_profile_t selected_profile; + uint8_t mki[256]; + unsigned mki_size; + unsigned int mki_received; +} srtp_ext_st; + +extern const hello_ext_entry_st ext_mod_srtp; + +#endif + +#endif /* GNUTLS_LIB_EXT_SRTP_H */ diff --git a/lib/ext/status_request.c b/lib/ext/status_request.c new file mode 100644 index 0000000..1e89286 --- /dev/null +++ b/lib/ext/status_request.c @@ -0,0 +1,512 @@ +/* + * Copyright (C) 2012-2017 Free Software Foundation, Inc. + * Copyright (C) 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/> + * + */ + +/* + Status Request (OCSP) TLS extension. See RFC 6066 section 8: + https://tools.ietf.org/html/rfc6066#section-8 +*/ + +#include "gnutls_int.h" +#include "errors.h" +#include <hello_ext.h> +#include <ext/status_request.h> +#include <mbuffers.h> +#include <auth.h> +#include <auth/cert.h> +#include <handshake.h> + +#ifdef ENABLE_OCSP + +typedef struct { + /* server response */ + gnutls_datum_t sresp; + + unsigned int expect_cstatus; +} status_request_ext_st; + +/* + From RFC 6066. Client sends: + + struct { + CertificateStatusType status_type; + select (status_type) { + case ocsp: OCSPStatusRequest; + } request; + } CertificateStatusRequest; + + enum { ocsp(1), (255) } CertificateStatusType; + + struct { + ResponderID responder_id_list<0..2^16-1>; + Extensions request_extensions; + } OCSPStatusRequest; + + opaque ResponderID<1..2^16-1>; + opaque Extensions<0..2^16-1>; +*/ + + +static int +client_send(gnutls_session_t session, + gnutls_buffer_st * extdata, status_request_ext_st * priv) +{ + const uint8_t data[5] = "\x01\x00\x00\x00\x00"; + const int len = 5; + int ret; + + /* We do not support setting either ResponderID or Extensions */ + + ret = _gnutls_buffer_append_data(extdata, data, len); + if (ret < 0) + return gnutls_assert_val(ret); + + session->internals.hsk_flags |= HSK_OCSP_REQUESTED; + + return len; +} + +static int +server_recv(gnutls_session_t session, + const uint8_t * data, size_t data_size) +{ + unsigned rid_bytes = 0; + + /* minimum message is type (1) + responder_id_list (2) + + request_extension (2) = 5 */ + if (data_size < 5) + return + gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH); + + /* We ignore non-ocsp CertificateStatusType. The spec is unclear + what should be done. */ + if (data[0] != 0x01) { + gnutls_assert(); + _gnutls_handshake_log("EXT[%p]: unknown status_type %d\n", + session, data[0]); + return 0; + } + DECR_LEN(data_size, 1); + data++; + + rid_bytes = _gnutls_read_uint16(data); + + DECR_LEN(data_size, 2); + /*data += 2;*/ + + /* sanity check only, we don't use any of the data below */ + + if (data_size < rid_bytes) + return + gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER); + + _gnutls_handshake_log("EXT[%p]: OCSP status was requested\n", session); + session->internals.hsk_flags |= HSK_OCSP_REQUESTED; + + return 0; +} + + +static int +client_recv(gnutls_session_t session, + status_request_ext_st * priv, + const uint8_t * data, size_t size) +{ + if (size != 0) + return + gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH); + else { + priv->expect_cstatus = 1; + return 0; + } +} + + +/* + * Servers return a certificate response along with their certificate + * by sending a "CertificateStatus" message immediately after the + * "Certificate" message (and before any "ServerKeyExchange" or + * "CertificateRequest" messages). If a server returns a + * "CertificateStatus" message, then the server MUST have included an + * extension of type "status_request" with empty "extension_data" in + * the extended server hello. + * + * According to the description above, as a server we could simply + * return GNUTLS_E_INT_RET_0 on this function. In that case we would + * only need to use the callbacks at the time we need to send the data, + * and skip the status response packet if no such data are there. + * However, that behavior would break gnutls 3.3.x which expects the status + * response to be always send if the extension is present. + * + * Instead we ensure that this extension is parsed after the CS/certificate + * are selected (with the _GNUTLS_EXT_TLS_POST_CS type), and we discover + * (or not) the response to send early. + */ +static int +server_send(gnutls_session_t session, + gnutls_buffer_st * extdata, status_request_ext_st * priv) +{ + int ret; + gnutls_certificate_credentials_t cred; + gnutls_status_request_ocsp_func func; + void *func_ptr; + const version_entry_st *ver = get_version(session); + + cred = (gnutls_certificate_credentials_t) + _gnutls_get_cred(session, GNUTLS_CRD_CERTIFICATE); + if (cred == NULL) /* no certificate authentication */ + return 0; + + /* no need to set sresp; responses are send during certificate sending and + * no response is required from server side. */ + if (ver && ver->multi_ocsp) + return 0; + + /* Under TLS1.2 we obtain the response at this point in order to respond + * appropriately (empty extension vs no response) */ + if (session->internals.selected_ocsp_length > 0) { + if (session->internals.selected_ocsp[0].response.data) { + if (session->internals.selected_ocsp[0].exptime != 0 && + (gnutls_time(0) >= session->internals.selected_ocsp[0].exptime)) { + gnutls_assert(); + return 0; + } + + ret = _gnutls_set_datum(&priv->sresp, + session->internals.selected_ocsp[0].response.data, + session->internals.selected_ocsp[0].response.size); + if (ret < 0) + return gnutls_assert_val(ret); + return GNUTLS_E_INT_RET_0; + } else { + return 0; + } + } else if (session->internals.selected_ocsp_func) { + func = session->internals.selected_ocsp_func; + func_ptr = session->internals.selected_ocsp_func_ptr; + + if (func == NULL) + return 0; + + ret = func(session, func_ptr, &priv->sresp); + if (ret == GNUTLS_E_NO_CERTIFICATE_STATUS) + return 0; + else if (ret < 0) + return gnutls_assert_val(ret); + } else { + return 0; + } + + return GNUTLS_E_INT_RET_0; +} + +static int +_gnutls_status_request_send_params(gnutls_session_t session, + gnutls_buffer_st * extdata) +{ + gnutls_ext_priv_data_t epriv; + status_request_ext_st *priv; + int ret; + + /* Do not bother sending the OCSP status request extension + * if we are not using certificate authentication */ + if (_gnutls_get_cred(session, GNUTLS_CRD_CERTIFICATE) == NULL) + return 0; + + if (session->security_parameters.entity == GNUTLS_CLIENT) { + ret = _gnutls_hello_ext_get_priv(session, + GNUTLS_EXTENSION_STATUS_REQUEST, + &epriv); + if (ret < 0 || epriv == NULL) /* it is ok not to have it */ + return 0; + priv = epriv; + + return client_send(session, extdata, priv); + } else { + epriv = priv = gnutls_calloc(1, sizeof(*priv)); + if (priv == NULL) + return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + + _gnutls_hello_ext_set_priv(session, + GNUTLS_EXTENSION_STATUS_REQUEST, + epriv); + + return server_send(session, extdata, priv); + } +} + +static int +_gnutls_status_request_recv_params(gnutls_session_t session, + const uint8_t * data, size_t size) +{ + gnutls_ext_priv_data_t epriv; + status_request_ext_st *priv; + int ret; + + if (session->security_parameters.entity == GNUTLS_CLIENT) { + ret = _gnutls_hello_ext_get_priv(session, + GNUTLS_EXTENSION_STATUS_REQUEST, + &epriv); + if (ret < 0 || epriv == NULL) /* it is ok not to have it */ + return 0; + priv = epriv; + + return client_recv(session, priv, data, size); + } else { + return server_recv(session, data, size); + } +} + +/** + * gnutls_ocsp_status_request_enable_client: + * @session: is a #gnutls_session_t type. + * @responder_id: ignored, must be %NULL + * @responder_id_size: ignored, must be zero + * @extensions: ignored, must be %NULL + * + * This function is to be used by clients to request OCSP response + * from the server, using the "status_request" TLS extension. Only + * OCSP status type is supported. + * + * Previous versions of GnuTLS supported setting @responder_id and + * @extensions fields, but due to the difficult semantics of the + * parameter usage, and other issues, this support was removed + * since 3.6.0 and these parameters must be set to %NULL. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, + * otherwise a negative error code is returned. + * + * Since: 3.1.3 + **/ +int +gnutls_ocsp_status_request_enable_client(gnutls_session_t session, + gnutls_datum_t * responder_id, + size_t responder_id_size, + gnutls_datum_t * extensions) +{ + status_request_ext_st *priv; + gnutls_ext_priv_data_t epriv; + + if (session->security_parameters.entity == GNUTLS_SERVER) + return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + + epriv = priv = gnutls_calloc(1, sizeof(*priv)); + if (priv == NULL) + return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + + _gnutls_hello_ext_set_priv(session, + GNUTLS_EXTENSION_STATUS_REQUEST, + epriv); + + return 0; +} + + +static void _gnutls_status_request_deinit_data(gnutls_ext_priv_data_t epriv) +{ + status_request_ext_st *priv = epriv; + + if (priv == NULL) + return; + + gnutls_free(priv->sresp.data); + gnutls_free(priv); +} + +const hello_ext_entry_st ext_mod_status_request = { + .name = "OCSP Status Request", + .tls_id = STATUS_REQUEST_TLS_ID, + .gid = GNUTLS_EXTENSION_STATUS_REQUEST, + .validity = GNUTLS_EXT_FLAG_TLS | GNUTLS_EXT_FLAG_DTLS | GNUTLS_EXT_FLAG_CLIENT_HELLO | + GNUTLS_EXT_FLAG_TLS12_SERVER_HELLO, + .client_parse_point = _GNUTLS_EXT_TLS_POST_CS, + .server_parse_point = _GNUTLS_EXT_TLS_POST_CS, + .recv_func = _gnutls_status_request_recv_params, + .send_func = _gnutls_status_request_send_params, + .deinit_func = _gnutls_status_request_deinit_data, + .cannot_be_overriden = 1 +}; + +/* Functions to be called from handshake */ + +int +_gnutls_send_server_certificate_status(gnutls_session_t session, int again) +{ + mbuffer_st *bufel = NULL; + uint8_t *data; + int data_size = 0; + int ret; + gnutls_ext_priv_data_t epriv; + status_request_ext_st *priv; + + if (!(session->internals.hsk_flags & HSK_OCSP_REQUESTED)) + return 0; + + if (again == 0) { + ret = + _gnutls_hello_ext_get_priv(session, + GNUTLS_EXTENSION_STATUS_REQUEST, + &epriv); + if (ret < 0) + return 0; + + priv = epriv; + + if (!priv->sresp.size) + return 0; + + data_size = priv->sresp.size + 4; + bufel = + _gnutls_handshake_alloc(session, data_size); + if (!bufel) { + _gnutls_free_datum(&priv->sresp); + return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + } + + data = _mbuffer_get_udata_ptr(bufel); + + data[0] = 0x01; + _gnutls_write_uint24(priv->sresp.size, &data[1]); + memcpy(&data[4], priv->sresp.data, priv->sresp.size); + + _gnutls_free_datum(&priv->sresp); + } + return _gnutls_send_handshake(session, data_size ? bufel : NULL, + GNUTLS_HANDSHAKE_CERTIFICATE_STATUS); +} + +int _gnutls_parse_ocsp_response(gnutls_session_t session, const uint8_t *data, ssize_t data_size, gnutls_datum_t *resp) +{ + int ret; + ssize_t r_size; + + resp->data = 0; + resp->size = 0; + + /* minimum message is type (1) + response (3) + data */ + if (data_size < 4) + return + gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH); + + if (data[0] != 0x01) { + gnutls_assert(); + _gnutls_handshake_log("EXT[%p]: unknown status_type %d\n", + session, data[0]); + return 0; + } + + DECR_LENGTH_RET(data_size, 1, + GNUTLS_E_UNEXPECTED_PACKET_LENGTH); + data++; + + DECR_LENGTH_RET(data_size, 3, + GNUTLS_E_UNEXPECTED_PACKET_LENGTH); + + r_size = _gnutls_read_uint24(data); + data += 3; + + DECR_LENGTH_RET(data_size, r_size, + GNUTLS_E_UNEXPECTED_PACKET_LENGTH); + + if (r_size < 1) + return + gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH); + + ret = _gnutls_set_datum(resp, data, r_size); + if (ret < 0) + return gnutls_assert_val(ret); + + return 0; +} + +int _gnutls_recv_server_certificate_status(gnutls_session_t session) +{ + uint8_t *data; + ssize_t data_size; + gnutls_buffer_st buf; + int ret; + gnutls_datum_t resp; + status_request_ext_st *priv = NULL; + gnutls_ext_priv_data_t epriv; + cert_auth_info_t info = _gnutls_get_auth_info(session, GNUTLS_CRD_CERTIFICATE); + + if (info == NULL) + return 0; + + ret = + _gnutls_hello_ext_get_priv(session, + GNUTLS_EXTENSION_STATUS_REQUEST, + &epriv); + if (ret < 0) + return 0; + + priv = epriv; + + if (!priv->expect_cstatus) + return 0; + + ret = _gnutls_recv_handshake(session, + GNUTLS_HANDSHAKE_CERTIFICATE_STATUS, + 1, &buf); + if (ret < 0) + return gnutls_assert_val_fatal(ret); + + priv->expect_cstatus = 0; + + data = buf.data; + data_size = buf.length; + + if (data_size == 0) { + ret = 0; + goto error; + } + + ret = _gnutls_parse_ocsp_response(session, data, data_size, &resp); + if (ret < 0) { + gnutls_assert(); + goto error; + } + + if (resp.data && resp.size > 0) { + for (unsigned int i = 0; i < info->nocsp; i++) + gnutls_free(info->raw_ocsp_list[i].data); + gnutls_free(info->raw_ocsp_list); + + info->raw_ocsp_list = gnutls_malloc(sizeof(gnutls_datum_t)); + if (info->raw_ocsp_list == NULL) { + ret = GNUTLS_E_MEMORY_ERROR; + goto error; + } + info->raw_ocsp_list[0].data = resp.data; + info->raw_ocsp_list[0].size = resp.size; + info->nocsp = 1; + } + + ret = 0; + + error: + _gnutls_buffer_clear(&buf); + + return ret; +} + +#endif diff --git a/lib/ext/status_request.h b/lib/ext/status_request.h new file mode 100644 index 0000000..dc1d233 --- /dev/null +++ b/lib/ext/status_request.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 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_EXT_STATUS_REQUEST_H +#define GNUTLS_LIB_EXT_STATUS_REQUEST_H + +#include <hello_ext.h> + +#define STATUS_REQUEST_TLS_ID 5 + +extern const hello_ext_entry_st ext_mod_status_request; + +int +_gnutls_send_server_certificate_status(gnutls_session_t session, + int again); +int _gnutls_recv_server_certificate_status(gnutls_session_t session); + +int _gnutls_parse_ocsp_response(gnutls_session_t session, const uint8_t *data, + ssize_t data_size, + gnutls_datum_t *resp); + +#endif /* GNUTLS_LIB_EXT_STATUS_REQUEST_H */ diff --git a/lib/ext/supported_groups.c b/lib/ext/supported_groups.c new file mode 100644 index 0000000..56e514a --- /dev/null +++ b/lib/ext/supported_groups.c @@ -0,0 +1,269 @@ +/* + * Copyright (C) 2011-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/> + * + */ + +/* This file contains the code for the Supported Groups extension (rfc7919). + * This extension was previously named Supported Elliptic Curves under TLS 1.2. + */ + +#include "ext/supported_groups.h" +#include "str.h" +#include "num.h" +#include "auth/psk.h" +#include "auth/cert.h" +#include "auth/anon.h" +#include "algorithms.h" +#include <gnutls/gnutls.h> + + +static int _gnutls_supported_groups_recv_params(gnutls_session_t session, + const uint8_t * data, + size_t data_size); +static int _gnutls_supported_groups_send_params(gnutls_session_t session, + gnutls_buffer_st * extdata); + + +const hello_ext_entry_st ext_mod_supported_groups = { + .name = "Supported Groups", + .tls_id = 10, + .gid = GNUTLS_EXTENSION_SUPPORTED_GROUPS, + .client_parse_point = GNUTLS_EXT_TLS, + .server_parse_point = GNUTLS_EXT_TLS, + .validity = GNUTLS_EXT_FLAG_TLS | GNUTLS_EXT_FLAG_DTLS | GNUTLS_EXT_FLAG_CLIENT_HELLO | + GNUTLS_EXT_FLAG_EE | GNUTLS_EXT_FLAG_TLS12_SERVER_HELLO, + .recv_func = _gnutls_supported_groups_recv_params, + .send_func = _gnutls_supported_groups_send_params, + .pack_func = NULL, + .unpack_func = NULL, + .deinit_func = NULL, + .cannot_be_overriden = 1 +}; + + +static unsigned get_min_dh(gnutls_session_t session) +{ + gnutls_certificate_credentials_t cert_cred; + gnutls_psk_server_credentials_t psk_cred; + gnutls_anon_server_credentials_t anon_cred; + unsigned level = 0; + + cert_cred = (gnutls_certificate_credentials_t)_gnutls_get_cred(session, GNUTLS_CRD_CERTIFICATE); + psk_cred = (gnutls_psk_server_credentials_t)_gnutls_get_cred(session, GNUTLS_CRD_PSK); + anon_cred = (gnutls_anon_server_credentials_t)_gnutls_get_cred(session, GNUTLS_CRD_ANON); + + if (cert_cred) { + level = cert_cred->dh_sec_param; + } else if (psk_cred) { + level = psk_cred->dh_sec_param; + } else if (anon_cred) { + level = anon_cred->dh_sec_param; + } + + if (level) + return gnutls_sec_param_to_pk_bits(GNUTLS_PK_DH, level); + + return 0; +} + +/* + * In case of a server: if a SUPPORTED_GROUPS extension type is received then it stores + * into the session security parameters the new value. The server may use gnutls_session_certificate_type_get(), + * to access it. + * + * In case of a client: If supported_eccs have been specified then we send the extension. + * + */ +static int +_gnutls_supported_groups_recv_params(gnutls_session_t session, + const uint8_t * data, size_t data_size) +{ + int i; + uint16_t len; + const uint8_t *p = data; + const gnutls_group_entry_st *group = NULL; + unsigned have_ffdhe = 0; + unsigned tls_id; + unsigned min_dh; + unsigned j; + int serv_ec_idx, serv_dh_idx; /* index in server's priority listing */ + int cli_ec_pos, cli_dh_pos; /* position in listing sent by client */ + + if (session->security_parameters.entity == GNUTLS_CLIENT) { + /* A client shouldn't receive this extension in TLS1.2. It is + * possible to read that message under TLS1.3 as an encrypted + * extension. */ + return 0; + } else { /* SERVER SIDE - we must check if the sent supported ecc type is the right one + */ + if (data_size < 2) + return + gnutls_assert_val + (GNUTLS_E_RECEIVED_ILLEGAL_EXTENSION); + + DECR_LEN(data_size, 2); + len = _gnutls_read_uint16(p); + p += 2; + + if (len % 2 != 0) + return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH); + + DECR_LEN(data_size, len); + + /* we figure what is the minimum DH allowed for this session, if any */ + min_dh = get_min_dh(session); + + serv_ec_idx = serv_dh_idx = -1; + cli_ec_pos = cli_dh_pos = -1; + + /* This extension is being processed prior to a ciphersuite being selected, + * so we cannot rely on ciphersuite information. */ + for (i = 0; i < len; i += 2) { + if (have_ffdhe == 0 && p[i] == 0x01) + have_ffdhe = 1; + + tls_id = _gnutls_read_uint16(&p[i]); + group = _gnutls_tls_id_to_group(tls_id); + + _gnutls_handshake_log("EXT[%p]: Received group %s (0x%x)\n", session, group?group->name:"unknown", tls_id); + if (group == NULL) + continue; + + if (min_dh > 0 && group->prime && group->prime->size*8 < min_dh) + continue; + + /* we simulate _gnutls_session_supports_group, but we prioritize if + * %SERVER_PRECEDENCE is given */ + for (j = 0; j < session->internals.priorities->groups.size; j++) { + if (session->internals.priorities->groups.entry[j]->id == group->id) { + if (session->internals.priorities->server_precedence) { + if (group->pk == GNUTLS_PK_DH) { + if (serv_dh_idx != -1 && (int)j > serv_dh_idx) + break; + serv_dh_idx = j; + cli_dh_pos = i; + } else if (IS_EC(group->pk)) { + if (serv_ec_idx != -1 && (int)j > serv_ec_idx) + break; + serv_ec_idx = j; + cli_ec_pos = i; + } + } else { + if (group->pk == GNUTLS_PK_DH) { + if (cli_dh_pos != -1) + break; + cli_dh_pos = i; + serv_dh_idx = j; + } else if (IS_EC(group->pk)) { + if (cli_ec_pos != -1) + break; + cli_ec_pos = i; + serv_ec_idx = j; + } + } + break; + } + } + } + + /* serv_dh/ec_pos contain the index of the groups we want to use. + */ + if (serv_dh_idx != -1) { + session->internals.cand_dh_group = session->internals.priorities->groups.entry[serv_dh_idx]; + session->internals.cand_group = session->internals.cand_dh_group; + } + + if (serv_ec_idx != -1) { + session->internals.cand_ec_group = session->internals.priorities->groups.entry[serv_ec_idx]; + if (session->internals.cand_group == NULL || + (session->internals.priorities->server_precedence && serv_ec_idx < serv_dh_idx) || + (!session->internals.priorities->server_precedence && cli_ec_pos < cli_dh_pos)) { + session->internals.cand_group = session->internals.cand_ec_group; + } + } + + if (session->internals.cand_group) + _gnutls_handshake_log("EXT[%p]: Selected group %s\n", session, session->internals.cand_group->name); + + if (have_ffdhe) + session->internals.hsk_flags |= HSK_HAVE_FFDHE; + } + + return 0; +} + +/* returns data_size or a negative number on failure + */ +static int +_gnutls_supported_groups_send_params(gnutls_session_t session, + gnutls_buffer_st * extdata) +{ + unsigned len, i; + int ret; + uint16_t p; + + /* this extension is only being sent on client side */ + if (session->security_parameters.entity == GNUTLS_CLIENT) { + + len = session->internals.priorities->groups.size; + if (len > 0) { + ret = + _gnutls_buffer_append_prefix(extdata, 16, + len * 2); + if (ret < 0) + return gnutls_assert_val(ret); + + for (i = 0; i < len; i++) { + p = session->internals.priorities->groups.entry[i]->tls_id; + + _gnutls_handshake_log("EXT[%p]: Sent group %s (0x%x)\n", session, + session->internals.priorities->groups.entry[i]->name, (unsigned)p); + + ret = + _gnutls_buffer_append_prefix(extdata, + 16, p); + if (ret < 0) + return gnutls_assert_val(ret); + } + return (len + 1) * 2; + } + + } + + return 0; +} + +/* Returns 0 if the given ECC curve is allowed in the current + * session. A negative error value is returned otherwise. + */ +int +_gnutls_session_supports_group(gnutls_session_t session, + unsigned int group) +{ + unsigned i; + + for (i = 0; i < session->internals.priorities->groups.size; i++) { + if (session->internals.priorities->groups.entry[i]->id == group) + return 0; + } + + return GNUTLS_E_ECC_UNSUPPORTED_CURVE; +} diff --git a/lib/ext/supported_groups.h b/lib/ext/supported_groups.h new file mode 100644 index 0000000..769b3be --- /dev/null +++ b/lib/ext/supported_groups.h @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2011-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_EXT_SUPPORTED_GROUPS_H +#define GNUTLS_LIB_EXT_SUPPORTED_GROUPS_H + +#include <hello_ext.h> + +extern const hello_ext_entry_st ext_mod_supported_groups; + +int +_gnutls_session_supports_group(gnutls_session_t session, + unsigned int group); + +#endif /* GNUTLS_LIB_EXT_SUPPORTED_GROUPS_H */ diff --git a/lib/ext/supported_versions.c b/lib/ext/supported_versions.c new file mode 100644 index 0000000..157a0a7 --- /dev/null +++ b/lib/ext/supported_versions.c @@ -0,0 +1,239 @@ +/* + * Copyright (C) 2001-2012 Free Software Foundation, Inc. + * Copyright (C) 2017-2018 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 the code for the Max Record Size TLS extension. + */ + +#include "gnutls_int.h" +#include "errors.h" +#include "num.h" +#include <hello_ext.h> +#include <ext/supported_versions.h> +#include "handshake.h" + +static int supported_versions_recv_params(gnutls_session_t session, + const uint8_t * data, + size_t data_size); +static int supported_versions_send_params(gnutls_session_t session, + gnutls_buffer_st * extdata); + +const hello_ext_entry_st ext_mod_supported_versions = { + .name = "Supported Versions", + .tls_id = 43, + .gid = GNUTLS_EXTENSION_SUPPORTED_VERSIONS, + .validity = GNUTLS_EXT_FLAG_CLIENT_HELLO | GNUTLS_EXT_FLAG_TLS12_SERVER_HELLO | + GNUTLS_EXT_FLAG_TLS13_SERVER_HELLO | GNUTLS_EXT_FLAG_HRR|GNUTLS_EXT_FLAG_TLS, + .client_parse_point = GNUTLS_EXT_VERSION_NEG, /* force parsing prior to EXT_TLS extensions */ + .server_parse_point = GNUTLS_EXT_VERSION_NEG, + .recv_func = supported_versions_recv_params, + .send_func = supported_versions_send_params, + .pack_func = NULL, + .unpack_func = NULL, + .deinit_func = NULL, + .cannot_be_overriden = 1 +}; + +static int +supported_versions_recv_params(gnutls_session_t session, + const uint8_t * data, size_t data_size) +{ + const version_entry_st *vers; + uint8_t major, minor; + size_t bytes; + int ret; + + if (session->security_parameters.entity == GNUTLS_SERVER) { + const version_entry_st *old_vers; + const version_entry_st *cli_vers = NULL; + + vers = _gnutls_version_max(session); + old_vers = get_version(session); + + /* do not parse this extension when we haven't TLS1.3 + * enabled. That is because we cannot handle earlier protocol + * negotiation (such as SSL3.0) with this */ + if (vers && !vers->tls13_sem) + return 0; + + DECR_LEN(data_size, 1); + bytes = data[0]; + data++; + + if (bytes % 2 == 1) + return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH); + + DECR_LEN(data_size, bytes); + + if (data_size != 0) + return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH); + + while (bytes > 0) { + major = data[0]; + minor = data[1]; + data += 2; + bytes -= 2; + + _gnutls_handshake_log("EXT[%p]: Found version: %d.%d\n", + session, (int)major, (int)minor); + + if (!_gnutls_nversion_is_supported(session, major, minor)) + continue; + + /* Prefer the latest possible version + * regardless of the client's precedence. See + * https://gitlab.com/gnutls/gnutls/issues/837 + * for the rationale. + */ + if (!cli_vers || + major > cli_vers->major || + (major == cli_vers->major && + minor > cli_vers->minor)) + cli_vers = nversion_to_entry(major, minor); + } + + if (!cli_vers) + return gnutls_assert_val(GNUTLS_E_UNSUPPORTED_VERSION_PACKET); + + session->security_parameters.pversion = cli_vers; + + _gnutls_handshake_log("EXT[%p]: Negotiated version: %d.%d\n", + session, + (int)cli_vers->major, + (int)cli_vers->minor); + + if (old_vers != cli_vers) { + /* regenerate the random value to set + * downgrade sentinel if necessary + */ + ret = _gnutls_gen_server_random(session, cli_vers->id); + if (ret < 0) + return gnutls_assert_val(ret); + } + + return 0; + } else { /* client */ + + if (!have_creds_for_tls13(session)) { + /* if we don't have certificate or PSK (which work under TLS1.3) + * don't try to negotiate version using the extension. We fallback + * instead to the normal TLS negotiation which has a cap on TLS1.2. + */ + return 0; + } + + DECR_LEN(data_size, 2); + + if (data_size != 0) + return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH); + + major = data[0]; + minor = data[1]; + + vers = nversion_to_entry(major, minor); + if (!vers) + return gnutls_assert_val(GNUTLS_E_UNSUPPORTED_VERSION_PACKET); + + set_adv_version(session, major, minor); + + _gnutls_handshake_log("EXT[%p]: Negotiated version: %d.%d\n", + session, (int)major, (int)minor); + + if (!vers->tls13_sem) + return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER); + + ret = _gnutls_negotiate_version(session, major, minor, 1); + if (ret < 0) { + gnutls_assert(); + return ret; + } + } + + return 0; +} + +/* returns data_size or a negative number on failure + */ +static int +supported_versions_send_params(gnutls_session_t session, + gnutls_buffer_st * extdata) +{ + uint8_t versions[32]; + size_t versions_size; + const version_entry_st *vers; + int ret; + + /* this function sends the client extension data (dnsname) */ + if (session->security_parameters.entity == GNUTLS_CLIENT) { + vers = _gnutls_version_max(session); + + /* Do not advertise this extension if we are not doing certificate + * or PSK authentication; i.e., do not try to do TLS1.3 if we have + * credentials which do not fit it. */ + if (!have_creds_for_tls13(session)) { + /* if we don't have certificate or PSK (which work under TLS1.3) + * don't try to negotiate version using the extension. We fallback + * instead to the normal TLS negotiation which has a cap on TLS1.2. + */ + return 0; + } + + /* do not advertise this extension when we haven't TLS1.3 + * enabled. */ + if (vers && !vers->tls13_sem) + return 0; + + ret = _gnutls_write_supported_versions(session, versions, sizeof(versions)); + if (ret <= 0) /* if this function doesn't succeed do not send anything */ + return 0; + + versions_size = ret; + + ret = _gnutls_buffer_append_data_prefix(extdata, 8, versions, versions_size); + if (ret < 0) + return gnutls_assert_val(ret); + + return versions_size+2; + } else { + vers = get_version(session); + if (unlikely(vers == NULL)) + return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); + + /* don't use this extension to negotiate versions <= 1.2, + * pretend we don't support it, so that we use a single + * code path to negotiate these protocols. */ + if (!vers->tls13_sem) + return 0; + + ret = _gnutls_buffer_append_data(extdata, &vers->major, 1); + if (ret < 0) + return gnutls_assert_val(ret); + + ret = _gnutls_buffer_append_data(extdata, &vers->minor, 1); + if (ret < 0) + return gnutls_assert_val(ret); + + return 2; + } + + return 0; +} diff --git a/lib/ext/supported_versions.h b/lib/ext/supported_versions.h new file mode 100644 index 0000000..f7dfb9e --- /dev/null +++ b/lib/ext/supported_versions.h @@ -0,0 +1,30 @@ +/* + * 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_EXT_SUPPORTED_VERSIONS_H +#define GNUTLS_LIB_EXT_SUPPORTED_VERSIONS_H + +#include <hello_ext.h> + +extern const hello_ext_entry_st ext_mod_supported_versions; + +#endif /* GNUTLS_LIB_EXT_SUPPORTED_VERSIONS_H */ |