diff options
Diffstat (limited to 'dirmngr')
30 files changed, 3027 insertions, 789 deletions
diff --git a/dirmngr/ChangeLog-2011 b/dirmngr/ChangeLog-2011 index 243f2b5..30e026f 100644 --- a/dirmngr/ChangeLog-2011 +++ b/dirmngr/ChangeLog-2011 @@ -1373,7 +1373,7 @@ truncated search. * ldap.c (add_server_to_servers): Reactivated. (url_fetch_ldap): Call it here and try all configured servers in - case of a a failed lookup. + case of a failed lookup. (fetch_next_cert_ldap): Detect the truncation error flag. * misc.c (host_and_port_from_url, remove_percent_escapes): New. diff --git a/dirmngr/Makefile.am b/dirmngr/Makefile.am index 8da0d91..b1329bd 100644 --- a/dirmngr/Makefile.am +++ b/dirmngr/Makefile.am @@ -22,7 +22,9 @@ ## Process this file with automake to produce Makefile.in EXTRA_DIST = OAUTHORS ONEWS ChangeLog-2011 tls-ca.pem \ - dirmngr-w32info.rc dirmngr.w32-manifest.in + dirmngr-w32info.rc dirmngr.w32-manifest.in \ + dirmngr_ldap-w32info.rc dirmngr_ldap.w32-manifest.in \ + dirmngr-client-w32info.rc dirmngr-client.w32-manifest.in dist_pkgdata_DATA = sks-keyservers.netCA.pem @@ -46,6 +48,16 @@ AM_CPPFLAGS = include $(top_srcdir)/am/cmacros.am +if HAVE_W32_SYSTEM +dirmngr_rc_objs = dirmngr-w32info.o +dirmngr_ldap_rc_objs = dirmngr_ldap-w32info.o +dirmngr_client_rc_objs = dirmngr-client-w32info.o + +dirmngr-w32info.o : dirmngr.w32-manifest ../common/w32info-rc.h +dirmngr_ldap-w32info.o : dirmngr_ldap.w32-manifest ../common/w32info-rc.h +dirmngr-client-w32info.o : dirmngr-client.w32-manifest ../common/w32info-rc.h +endif + AM_CFLAGS = $(USE_C99_CFLAGS) \ $(LIBGCRYPT_CFLAGS) $(KSBA_CFLAGS) $(LIBASSUAN_CFLAGS) \ $(GPG_ERROR_CFLAGS) $(NPTH_CFLAGS) $(NTBTLS_CFLAGS) \ @@ -54,6 +66,7 @@ AM_CFLAGS = $(USE_C99_CFLAGS) \ if HAVE_W32_SYSTEM ldap_url = ldap-url.h ldap-url.c +NETLIBS += -lwinhttp -lsecurity else ldap_url = endif @@ -86,23 +99,17 @@ else ldaplibs = endif -if HAVE_W32_SYSTEM -dirmngr_robjs = $(resource_objs) dirmngr-w32info.o -dirmngr-w32info.o : dirmngr.w32-manifest -else -dirmngr_robjs = -endif - dirmngr_LDADD = $(libcommonpth) \ $(DNSLIBS) $(LIBASSUAN_LIBS) \ $(KSBA_LIBS) $(NPTH_LIBS) $(NTBTLS_LIBS) $(LIBGNUTLS_LIBS) \ $(LIBGCRYPT_LIBS) $(GPG_ERROR_LIBS) $(LIBINTL) $(LIBICONV) \ - $(NETLIBS) $(dirmngr_robj) + $(NETLIBS) $(dirmngr_rc_objs) if USE_LDAP dirmngr_LDADD += $(ldaplibs) $(LBER_LIBS) endif dirmngr_LDFLAGS = $(extra_bin_ldflags) +dirmngr_DEPENDENCIES = $(dirmngr_rc_objs) if USE_LDAP dirmngr_ldap_SOURCES = dirmngr_ldap.c ldap-misc.c ldap-misc.h $(ldap_url) @@ -110,15 +117,18 @@ dirmngr_ldap_CFLAGS = $(GPG_ERROR_CFLAGS) $(LIBGCRYPT_CFLAGS) dirmngr_ldap_LDFLAGS = dirmngr_ldap_LDADD = $(libcommon) \ $(GPG_ERROR_LIBS) $(LIBGCRYPT_LIBS) $(LDAPLIBS) \ - $(LBER_LIBS) $(LIBINTL) $(LIBICONV) $(NETLIBS) + $(LBER_LIBS) $(LIBINTL) $(LIBICONV) $(NETLIBS) \ + $(dirmngr_ldap_rc_objs) +dirmngr_ldap_DEPENDENCIES = $(dirmngr_ldap_rc_objs) endif dirmngr_client_SOURCES = dirmngr-client.c dirmngr_client_LDADD = $(libcommon) \ $(LIBASSUAN_LIBS) $(GPG_ERROR_LIBS) \ - $(LIBGCRYPT_LIBS) $(NETLIBS) $(LIBINTL) $(LIBICONV) -dirmngr_client_LDFLAGS = $(extra_bin_ldflags) - + $(LIBGCRYPT_LIBS) $(NETLIBS) $(LIBINTL) $(LIBICONV) \ + $(dirmngr_client_rc_objs) +dirmngr_client_LDFLAGS = +dirmngr_client_DEPENDENCIES = $(dirmngr_client_rc_objs) t_common_src = t-support.h t-support.c if USE_LIBDNS diff --git a/dirmngr/Makefile.in b/dirmngr/Makefile.in index 9762403..10ae9f1 100644 --- a/dirmngr/Makefile.in +++ b/dirmngr/Makefile.in @@ -150,38 +150,39 @@ noinst_PROGRAMS = $(am__EXEEXT_2) $(am__EXEEXT_3) $(am__EXEEXT_4) @GNUPG_DIRMNGR_PGM_TRUE@am__append_5 = -DGNUPG_DEFAULT_DIRMNGR="\"@GNUPG_DIRMNGR_PGM@\"" @GNUPG_PROTECT_TOOL_PGM_TRUE@am__append_6 = -DGNUPG_DEFAULT_PROTECT_TOOL="\"@GNUPG_PROTECT_TOOL_PGM@\"" @GNUPG_DIRMNGR_LDAP_PGM_TRUE@am__append_7 = -DGNUPG_DEFAULT_DIRMNGR_LDAP="\"@GNUPG_DIRMNGR_LDAP_PGM@\"" -@USE_LIBDNS_TRUE@am__append_8 = dns.c dns.h -@USE_LDAP_TRUE@am__append_9 = ldapserver.h ldapserver.c ldap.c w32-ldap-help.h \ +@HAVE_W32_SYSTEM_TRUE@am__append_8 = -lwinhttp -lsecurity +@USE_LIBDNS_TRUE@am__append_9 = dns.c dns.h +@USE_LDAP_TRUE@am__append_10 = ldapserver.h ldapserver.c ldap.c w32-ldap-help.h \ @USE_LDAP_TRUE@ ldap-wrapper.h ldap-parse-uri.c ldap-parse-uri.h \ @USE_LDAP_TRUE@ ldap-misc.c ldap-misc.h \ @USE_LDAP_TRUE@ ks-engine-ldap.c $(ldap_url) ldap-wrapper.c -@USE_LDAP_TRUE@am__append_10 = $(ldaplibs) $(LBER_LIBS) -@USE_LIBDNS_TRUE@am__append_11 = dns.c dns.h -@USE_LDAP_TRUE@am__append_12 = t-ldap-parse-uri t-ldap-misc +@USE_LDAP_TRUE@am__append_11 = $(ldaplibs) $(LBER_LIBS) +@USE_LIBDNS_TRUE@am__append_12 = dns.c dns.h +@USE_LDAP_TRUE@am__append_13 = t-ldap-parse-uri t-ldap-misc subdir = dirmngr ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/autobuild.m4 \ $(top_srcdir)/m4/codeset.m4 $(top_srcdir)/m4/gettext.m4 \ $(top_srcdir)/m4/gpg-error.m4 $(top_srcdir)/m4/iconv.m4 \ - $(top_srcdir)/m4/isc-posix.m4 $(top_srcdir)/m4/ksba.m4 \ - $(top_srcdir)/m4/lcmessage.m4 $(top_srcdir)/m4/ldap.m4 \ - $(top_srcdir)/m4/lib-ld.m4 $(top_srcdir)/m4/lib-link.m4 \ - $(top_srcdir)/m4/lib-prefix.m4 $(top_srcdir)/m4/libassuan.m4 \ - $(top_srcdir)/m4/libgcrypt.m4 $(top_srcdir)/m4/nls.m4 \ - $(top_srcdir)/m4/npth.m4 $(top_srcdir)/m4/ntbtls.m4 \ - $(top_srcdir)/m4/pkg.m4 $(top_srcdir)/m4/po.m4 \ - $(top_srcdir)/m4/progtest.m4 $(top_srcdir)/m4/readline.m4 \ - $(top_srcdir)/m4/socklen.m4 $(top_srcdir)/m4/sys_socket_h.m4 \ - $(top_srcdir)/m4/tar-ustar.m4 $(top_srcdir)/acinclude.m4 \ - $(top_srcdir)/configure.ac + $(top_srcdir)/m4/ksba.m4 $(top_srcdir)/m4/lcmessage.m4 \ + $(top_srcdir)/m4/ldap.m4 $(top_srcdir)/m4/lib-ld.m4 \ + $(top_srcdir)/m4/lib-link.m4 $(top_srcdir)/m4/lib-prefix.m4 \ + $(top_srcdir)/m4/libassuan.m4 $(top_srcdir)/m4/libgcrypt.m4 \ + $(top_srcdir)/m4/nls.m4 $(top_srcdir)/m4/npth.m4 \ + $(top_srcdir)/m4/ntbtls.m4 $(top_srcdir)/m4/pkg.m4 \ + $(top_srcdir)/m4/po.m4 $(top_srcdir)/m4/progtest.m4 \ + $(top_srcdir)/m4/readline.m4 $(top_srcdir)/m4/socklen.m4 \ + $(top_srcdir)/m4/sys_socket_h.m4 $(top_srcdir)/m4/tar-ustar.m4 \ + $(top_srcdir)/acinclude.m4 $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(dist_pkgdata_DATA) \ $(noinst_HEADERS) $(am__DIST_COMMON) mkinstalldirs = $(SHELL) $(top_srcdir)/build-aux/mkinstalldirs CONFIG_HEADER = $(top_builddir)/config.h -CONFIG_CLEAN_FILES = dirmngr.w32-manifest +CONFIG_CLEAN_FILES = dirmngr.w32-manifest dirmngr_ldap.w32-manifest \ + dirmngr-client.w32-manifest CONFIG_CLEAN_VPATH_FILES = am__installdirs = "$(DESTDIR)$(bindir)" "$(DESTDIR)$(libexecdir)" \ "$(DESTDIR)$(pkgdatadir)" @@ -219,24 +220,14 @@ am_dirmngr_OBJECTS = dirmngr.$(OBJEXT) server.$(OBJEXT) \ ks-engine-kdns.$(OBJEXT) $(am__objects_1) $(am__objects_3) dirmngr_OBJECTS = $(am_dirmngr_OBJECTS) am__DEPENDENCIES_1 = -@USE_LDAP_TRUE@am__DEPENDENCIES_2 = $(am__DEPENDENCIES_1) -@USE_LDAP_TRUE@am__DEPENDENCIES_3 = $(am__DEPENDENCIES_2) \ +am__DEPENDENCIES_2 = $(am__DEPENDENCIES_1) +@USE_LDAP_TRUE@am__DEPENDENCIES_3 = $(am__DEPENDENCIES_1) +@USE_LDAP_TRUE@am__DEPENDENCIES_4 = $(am__DEPENDENCIES_3) \ @USE_LDAP_TRUE@ $(am__DEPENDENCIES_1) -dirmngr_DEPENDENCIES = $(libcommonpth) $(am__DEPENDENCIES_1) \ - $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ - $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ - $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ - $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ - $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ - $(am__DEPENDENCIES_3) dirmngr_LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(dirmngr_LDFLAGS) \ $(LDFLAGS) -o $@ am_dirmngr_client_OBJECTS = dirmngr-client.$(OBJEXT) dirmngr_client_OBJECTS = $(am_dirmngr_client_OBJECTS) -dirmngr_client_DEPENDENCIES = $(libcommon) $(am__DEPENDENCIES_1) \ - $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ - $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ - $(am__DEPENDENCIES_1) dirmngr_client_LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(dirmngr_client_LDFLAGS) $(LDFLAGS) -o $@ am__dirmngr_ldap_SOURCES_DIST = dirmngr_ldap.c ldap-misc.c ldap-misc.h \ @@ -247,11 +238,6 @@ am__dirmngr_ldap_SOURCES_DIST = dirmngr_ldap.c ldap-misc.c ldap-misc.h \ @USE_LDAP_TRUE@ dirmngr_ldap-ldap-misc.$(OBJEXT) \ @USE_LDAP_TRUE@ $(am__objects_4) dirmngr_ldap_OBJECTS = $(am_dirmngr_ldap_OBJECTS) -@USE_LDAP_TRUE@dirmngr_ldap_DEPENDENCIES = $(libcommon) \ -@USE_LDAP_TRUE@ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ -@USE_LDAP_TRUE@ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ -@USE_LDAP_TRUE@ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ -@USE_LDAP_TRUE@ $(am__DEPENDENCIES_1) dirmngr_ldap_LINK = $(CCLD) $(dirmngr_ldap_CFLAGS) $(CFLAGS) \ $(dirmngr_ldap_LDFLAGS) $(LDFLAGS) -o $@ am__t_dns_stuff_SOURCES_DIST = t-support.h t-support.c dns.c dns.h \ @@ -262,12 +248,12 @@ am_t_dns_stuff_OBJECTS = $(am__objects_6) \ t_dns_stuff-t-dns-stuff.$(OBJEXT) \ t_dns_stuff-dns-stuff.$(OBJEXT) t_dns_stuff_OBJECTS = $(am_t_dns_stuff_OBJECTS) -am__DEPENDENCIES_4 = $(libcommon) $(am__DEPENDENCIES_1) \ - $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ +am__DEPENDENCIES_5 = $(libcommon) $(am__DEPENDENCIES_1) \ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_2) $(am__DEPENDENCIES_1) \ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) -t_dns_stuff_DEPENDENCIES = $(am__DEPENDENCIES_4) $(am__DEPENDENCIES_1) +t_dns_stuff_DEPENDENCIES = $(am__DEPENDENCIES_5) $(am__DEPENDENCIES_1) t_dns_stuff_LINK = $(CCLD) $(t_dns_stuff_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ am__t_http_SOURCES_DIST = t-support.h t-support.c dns.c dns.h t-http.c \ @@ -278,7 +264,7 @@ am_t_http_OBJECTS = $(am__objects_8) t_http-t-http.$(OBJEXT) \ t_http-http.$(OBJEXT) t_http-dns-stuff.$(OBJEXT) \ t_http-http-common.$(OBJEXT) t_http_OBJECTS = $(am_t_http_OBJECTS) -t_http_DEPENDENCIES = $(am__DEPENDENCIES_4) $(am__DEPENDENCIES_1) \ +t_http_DEPENDENCIES = $(am__DEPENDENCIES_5) $(am__DEPENDENCIES_1) \ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ $(am__DEPENDENCIES_1) t_http_LINK = $(CCLD) $(t_http_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \ @@ -292,7 +278,7 @@ am_t_http_basic_OBJECTS = $(am__objects_10) \ t_http_basic-http.$(OBJEXT) t_http_basic-dns-stuff.$(OBJEXT) \ t_http_basic-http-common.$(OBJEXT) t_http_basic_OBJECTS = $(am_t_http_basic_OBJECTS) -t_http_basic_DEPENDENCIES = $(am__DEPENDENCIES_4) \ +t_http_basic_DEPENDENCIES = $(am__DEPENDENCIES_5) \ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) @@ -308,7 +294,7 @@ t_ldap_misc_DEPENDENCIES = $(libcommon) $(am__DEPENDENCIES_1) \ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ - $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) + $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_2) t_ldap_misc_LINK = $(CCLD) $(t_ldap_misc_CFLAGS) $(CFLAGS) \ $(t_ldap_misc_LDFLAGS) $(LDFLAGS) -o $@ am__t_ldap_parse_uri_SOURCES_DIST = t-ldap-parse-uri.c \ @@ -329,8 +315,8 @@ am_t_ldap_parse_uri_OBJECTS = \ t_ldap_parse_uri-ldap-misc.$(OBJEXT) $(am__objects_12) \ $(am__objects_14) t_ldap_parse_uri_OBJECTS = $(am_t_ldap_parse_uri_OBJECTS) -t_ldap_parse_uri_DEPENDENCIES = $(am__DEPENDENCIES_2) \ - $(am__DEPENDENCIES_4) $(am__DEPENDENCIES_1) \ +t_ldap_parse_uri_DEPENDENCIES = $(am__DEPENDENCIES_3) \ + $(am__DEPENDENCIES_5) $(am__DEPENDENCIES_1) \ $(am__DEPENDENCIES_1) t_ldap_parse_uri_LINK = $(CCLD) $(t_ldap_parse_uri_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ @@ -494,8 +480,10 @@ am__tty_colors = { \ fi; \ } am__DIST_COMMON = $(srcdir)/Makefile.in \ - $(srcdir)/dirmngr.w32-manifest.in $(top_srcdir)/am/cmacros.am \ - $(top_srcdir)/build-aux/depcomp \ + $(srcdir)/dirmngr-client.w32-manifest.in \ + $(srcdir)/dirmngr.w32-manifest.in \ + $(srcdir)/dirmngr_ldap.w32-manifest.in \ + $(top_srcdir)/am/cmacros.am $(top_srcdir)/build-aux/depcomp \ $(top_srcdir)/build-aux/mkinstalldirs DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ @@ -588,7 +576,7 @@ MKDIR_P = @MKDIR_P@ MSGFMT = @MSGFMT@ MSGFMT_015 = @MSGFMT_015@ MSGMERGE = @MSGMERGE@ -NETLIBS = @NETLIBS@ +NETLIBS = @NETLIBS@ $(am__append_8) NPTH_CFLAGS = @NPTH_CFLAGS@ NPTH_CONFIG = @NPTH_CONFIG@ NPTH_LIBS = @NPTH_LIBS@ @@ -684,7 +672,9 @@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ EXTRA_DIST = OAUTHORS ONEWS ChangeLog-2011 tls-ca.pem \ - dirmngr-w32info.rc dirmngr.w32-manifest.in + dirmngr-w32info.rc dirmngr.w32-manifest.in \ + dirmngr_ldap-w32info.rc dirmngr_ldap.w32-manifest.in \ + dirmngr-client-w32info.rc dirmngr-client.w32-manifest.in dist_pkgdata_DATA = sks-keyservers.netCA.pem @@ -710,6 +700,9 @@ libcommon = ../common/libcommon.a libcommonpth = ../common/libcommonpth.a libcommontls = ../common/libcommontls.a libcommontlsnpth = ../common/libcommontlsnpth.a +@HAVE_W32_SYSTEM_TRUE@dirmngr_rc_objs = dirmngr-w32info.o +@HAVE_W32_SYSTEM_TRUE@dirmngr_ldap_rc_objs = dirmngr_ldap-w32info.o +@HAVE_W32_SYSTEM_TRUE@dirmngr_client_rc_objs = dirmngr-client-w32info.o AM_CFLAGS = $(USE_C99_CFLAGS) \ $(LIBGCRYPT_CFLAGS) $(KSBA_CFLAGS) $(LIBASSUAN_CFLAGS) \ $(GPG_ERROR_CFLAGS) $(NPTH_CFLAGS) $(NTBTLS_CFLAGS) \ @@ -724,37 +717,40 @@ dirmngr_SOURCES = dirmngr.c dirmngr.h server.c crlcache.c crlfetch.c \ ocsp.h validate.c validate.h dns-stuff.c dns-stuff.h http.c \ http.h http-common.c http-common.h http-ntbtls.c ks-action.c \ ks-action.h ks-engine.h ks-engine-hkp.c ks-engine-http.c \ - ks-engine-finger.c ks-engine-kdns.c $(am__append_8) \ - $(am__append_9) + ks-engine-finger.c ks-engine-kdns.c $(am__append_9) \ + $(am__append_10) @USE_LDAP_FALSE@ldaplibs = @USE_LDAP_TRUE@ldaplibs = $(LDAPLIBS) -@HAVE_W32_SYSTEM_FALSE@dirmngr_robjs = -@HAVE_W32_SYSTEM_TRUE@dirmngr_robjs = $(resource_objs) dirmngr-w32info.o dirmngr_LDADD = $(libcommonpth) $(DNSLIBS) $(LIBASSUAN_LIBS) \ $(KSBA_LIBS) $(NPTH_LIBS) $(NTBTLS_LIBS) $(LIBGNUTLS_LIBS) \ $(LIBGCRYPT_LIBS) $(GPG_ERROR_LIBS) $(LIBINTL) $(LIBICONV) \ - $(NETLIBS) $(dirmngr_robj) $(am__append_10) + $(NETLIBS) $(dirmngr_rc_objs) $(am__append_11) dirmngr_LDFLAGS = $(extra_bin_ldflags) +dirmngr_DEPENDENCIES = $(dirmngr_rc_objs) @USE_LDAP_TRUE@dirmngr_ldap_SOURCES = dirmngr_ldap.c ldap-misc.c ldap-misc.h $(ldap_url) @USE_LDAP_TRUE@dirmngr_ldap_CFLAGS = $(GPG_ERROR_CFLAGS) $(LIBGCRYPT_CFLAGS) @USE_LDAP_TRUE@dirmngr_ldap_LDFLAGS = @USE_LDAP_TRUE@dirmngr_ldap_LDADD = $(libcommon) \ @USE_LDAP_TRUE@ $(GPG_ERROR_LIBS) $(LIBGCRYPT_LIBS) $(LDAPLIBS) \ -@USE_LDAP_TRUE@ $(LBER_LIBS) $(LIBINTL) $(LIBICONV) $(NETLIBS) +@USE_LDAP_TRUE@ $(LBER_LIBS) $(LIBINTL) $(LIBICONV) $(NETLIBS) \ +@USE_LDAP_TRUE@ $(dirmngr_ldap_rc_objs) +@USE_LDAP_TRUE@dirmngr_ldap_DEPENDENCIES = $(dirmngr_ldap_rc_objs) dirmngr_client_SOURCES = dirmngr-client.c dirmngr_client_LDADD = $(libcommon) \ $(LIBASSUAN_LIBS) $(GPG_ERROR_LIBS) \ - $(LIBGCRYPT_LIBS) $(NETLIBS) $(LIBINTL) $(LIBICONV) + $(LIBGCRYPT_LIBS) $(NETLIBS) $(LIBINTL) $(LIBICONV) \ + $(dirmngr_client_rc_objs) -dirmngr_client_LDFLAGS = $(extra_bin_ldflags) -t_common_src = t-support.h t-support.c $(am__append_11) +dirmngr_client_LDFLAGS = +dirmngr_client_DEPENDENCIES = $(dirmngr_client_rc_objs) +t_common_src = t-support.h t-support.c $(am__append_12) t_common_ldadd = $(libcommon) $(LIBASSUAN_LIBS) \ $(NTBTLS_LIBS) $(LIBGCRYPT_LIBS) $(GPG_ERROR_LIBS) \ $(LIBGNUTLS_LIBS) \ $(NETLIBS) $(DNSLIBS) $(LIBINTL) $(LIBICONV) -module_tests = t-http-basic $(am__append_12) +module_tests = t-http-basic $(am__append_13) @MAINTAINER_MODE_FALSE@module_net_tests = # Test which need a network connections are only used in maintainer mode. @@ -848,6 +844,10 @@ $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) $(am__aclocal_m4_deps): dirmngr.w32-manifest: $(top_builddir)/config.status $(srcdir)/dirmngr.w32-manifest.in cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +dirmngr_ldap.w32-manifest: $(top_builddir)/config.status $(srcdir)/dirmngr_ldap.w32-manifest.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +dirmngr-client.w32-manifest: $(top_builddir)/config.status $(srcdir)/dirmngr-client.w32-manifest.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ install-binPROGRAMS: $(bin_PROGRAMS) @$(NORMAL_INSTALL) @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ @@ -1942,7 +1942,10 @@ uninstall-am: uninstall-binPROGRAMS uninstall-dist_pkgdataDATA \ @HAVE_W32_SYSTEM_TRUE@.rc.o: @HAVE_W32_SYSTEM_TRUE@ $(WINDRES) $(DEFAULT_INCLUDES) $(INCLUDES) "$<" "$@" -@HAVE_W32_SYSTEM_TRUE@dirmngr-w32info.o : dirmngr.w32-manifest + +@HAVE_W32_SYSTEM_TRUE@dirmngr-w32info.o : dirmngr.w32-manifest ../common/w32info-rc.h +@HAVE_W32_SYSTEM_TRUE@dirmngr_ldap-w32info.o : dirmngr_ldap.w32-manifest ../common/w32info-rc.h +@HAVE_W32_SYSTEM_TRUE@dirmngr-client-w32info.o : dirmngr-client.w32-manifest ../common/w32info-rc.h $(PROGRAMS) : $(libcommon) $(libcommonpth) diff --git a/dirmngr/crlcache.c b/dirmngr/crlcache.c index 2380369..cc5300d 100644 --- a/dirmngr/crlcache.c +++ b/dirmngr/crlcache.c @@ -1625,8 +1625,21 @@ start_sig_check (ksba_crl_t crl, gcry_md_hd_t *md, int *algo, int *use_pss) } else *algo = gcry_md_map_name (algoid); + if (!*algo && algoid) + { + if (!strcmp (algoid, "1.2.840.10045.4.3.1")) + *algo = GCRY_MD_SHA224; /* ecdsa-with-sha224 */ + else if (!strcmp (algoid, "1.2.840.10045.4.3.2")) + *algo = GCRY_MD_SHA256; /* ecdsa-with-sha256 */ + else if (!strcmp (algoid, "1.2.840.10045.4.3.3")) + *algo = GCRY_MD_SHA384; /* ecdsa-with-sha384 */ + else if (!strcmp (algoid, "1.2.840.10045.4.3.4")) + *algo = GCRY_MD_SHA512; /* ecdsa-with-sha512 */ + } if (!*algo) { + log_debug ("XXXXX %s: %s <%s>\n", + __func__, gpg_strerror (err), gpg_strsource (err)); log_error (_("unknown hash algorithm '%s'\n"), algoid? algoid:"?"); return gpg_error (GPG_ERR_DIGEST_ALGO); } @@ -1660,6 +1673,7 @@ finish_sig_check (ksba_crl_t crl, gcry_md_hd_t md, int algo, size_t n; gcry_sexp_t s_sig = NULL, s_hash = NULL, s_pkey = NULL; unsigned int saltlen = 0; /* (used only with use_pss) */ + int pkalgo; /* This also stops debugging on the MD. */ gcry_md_final (md); @@ -1784,6 +1798,54 @@ finish_sig_check (ksba_crl_t crl, gcry_md_hd_t md, int algo, gcry_md_read (md, algo), saltlen); } + else if ((pkalgo = pk_algo_from_sexp (s_pkey)) == GCRY_PK_ECC) + { + unsigned int qbits0, qbits; + + qbits0 = gcry_pk_get_nbits (s_pkey); + qbits = qbits0 == 521? 512 : qbits0; + + if ((qbits%8)) + { + log_error ("ECDSA requires the hash length to be a" + " multiple of 8 bits\n"); + err = gpg_error (GPG_ERR_INTERNAL); + goto leave; + } + + /* Don't allow any Q smaller than 160 bits. */ + if (qbits < 160) + { + log_error (_("%s key uses an unsafe (%u bit) hash\n"), + gcry_pk_algo_name (pkalgo), qbits0); + err = gpg_error (GPG_ERR_INTERNAL); + goto leave; + } + + /* Check if we're too short. */ + n = gcry_md_get_algo_dlen (algo); + if (n < qbits/8) + { + log_error (_("a %u bit hash is not valid for a %u bit %s key\n"), + (unsigned int)n*8, + qbits0, + gcry_pk_algo_name (pkalgo)); + if (n < 20) + { + err = gpg_error (GPG_ERR_INTERNAL); + goto leave; + } + } + + /* Truncate. */ + if (n > qbits/8) + n = qbits/8; + + err = gcry_sexp_build (&s_hash, NULL, "(data(flags raw)(value %b))", + (int)n, + gcry_md_read (md, algo)); + + } else { err = gcry_sexp_build (&s_hash, NULL, @@ -1801,7 +1863,7 @@ finish_sig_check (ksba_crl_t crl, gcry_md_hd_t md, int algo, /* Pass this on to the signature verification. */ err = gcry_pk_verify (s_sig, s_hash, s_pkey); if (DBG_X509) - log_debug ("gcry_pk_verify: %s\n", gpg_strerror (err)); + log_debug ("%s: gcry_pk_verify: %s\n", __func__, gpg_strerror (err)); leave: xfree (sigval); @@ -2024,6 +2086,7 @@ crl_parse_insert (ctrl_t ctrl, ksba_crl_t crl, err = validate_cert_chain (ctrl, crlissuer_cert, NULL, (VALIDATE_FLAG_TRUST_CONFIG + | VALIDATE_FLAG_TRUST_SYSTEM | VALIDATE_FLAG_CRL | VALIDATE_FLAG_RECURSIVE), r_trust_anchor); @@ -2294,11 +2357,21 @@ crl_cache_insert (ctrl_t ctrl, const char *url, ksba_reader_t reader) for (idx=0; !(err=ksba_crl_get_extension (crl, idx, &oid, &critical, NULL, NULL)); idx++) { + strlist_t sl; + if (!critical || !strcmp (oid, oidstr_authorityKeyIdentifier) || !strcmp (oid, oidstr_crlNumber) ) continue; + + for (sl=opt.ignored_crl_extensions; + sl && strcmp (sl->d, oid); sl = sl->next) + ; + if (sl) + continue; /* Is in ignored list. */ + log_error (_("unknown critical CRL extension %s\n"), oid); + log_info ("(CRL='%s')\n", url); if (!err2) err2 = gpg_error (GPG_ERR_INV_CRL); invalidate_crl |= 2; diff --git a/dirmngr/dirmngr-client-w32info.rc b/dirmngr/dirmngr-client-w32info.rc new file mode 100644 index 0000000..020447b --- /dev/null +++ b/dirmngr/dirmngr-client-w32info.rc @@ -0,0 +1,52 @@ +/* dirmngr-client-w32info.rc -*- c -*- + * Copyright (C) 2023 g10 Code GmbH + * + * This file is free software; as a special exception the author gives + * unlimited permission to copy and/or distribute it, with or without + * modifications, as long as this notice is preserved. + * + * This 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. + */ + +#include "afxres.h" +#include "../common/w32info-rc.h" + +1 ICON "../common/gnupg.ico" + +1 VERSIONINFO + FILEVERSION W32INFO_VI_FILEVERSION + PRODUCTVERSION W32INFO_VI_PRODUCTVERSION + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x01L /* VS_FF_DEBUG (0x1)*/ +#else + FILEFLAGS 0x00L +#endif + FILEOS 0x40004L /* VOS_NT (0x40000) | VOS__WINDOWS32 (0x4) */ + FILETYPE 0x1L /* VFT_APP (0x1) */ + FILESUBTYPE 0x0L /* VFT2_UNKNOWN */ + BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" /* US English (0409), Unicode (04b0) */ + BEGIN + VALUE "FileDescription", L"GnuPG\x2019s dirmngr client\0" + VALUE "InternalName", "dirmngr-client\0" + VALUE "OriginalFilename", "dirmngr-client.exe\0" + VALUE "ProductName", W32INFO_PRODUCTNAME + VALUE "ProductVersion", W32INFO_PRODUCTVERSION + VALUE "CompanyName", W32INFO_COMPANYNAME + VALUE "FileVersion", W32INFO_FILEVERSION + VALUE "LegalCopyright", W32INFO_LEGALCOPYRIGHT + VALUE "Comments", W32INFO_COMMENTS + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 0x4b0 + END + END + +1 RT_MANIFEST "dirmngr-client.w32-manifest" diff --git a/dirmngr/dirmngr-client.w32-manifest.in b/dirmngr/dirmngr-client.w32-manifest.in new file mode 100644 index 0000000..1d46d19 --- /dev/null +++ b/dirmngr/dirmngr-client.w32-manifest.in @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8" standalone="yes" ?> +<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"> +<description>GNU Privacy Guard (Dirmngr Client)</description> +<assemblyIdentity + type="win32" + name="GnuPG.dirmngr.client" + version="@BUILD_VERSION@" + /> +<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1"> + <application> + <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/><!-- 10 --> + <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/><!-- 8.1 --> + <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/><!-- 8 --> + <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/><!-- 7 --> + <supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/><!-- Vista --> + </application> +</compatibility> +<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2"> + <security> + <requestedPrivileges> + <requestedExecutionLevel level="asInvoker"/> + </requestedPrivileges> + </security> +</trustInfo> +</assembly> diff --git a/dirmngr/dirmngr-w32info.rc b/dirmngr/dirmngr-w32info.rc index c8101b3..dd8d258 100644 --- a/dirmngr/dirmngr-w32info.rc +++ b/dirmngr/dirmngr-w32info.rc @@ -32,7 +32,7 @@ BEGIN BLOCK "040904b0" /* US English (0409), Unicode (04b0) */ BEGIN - VALUE "FileDescription", L"GnuPG\x2019s network daemon\0" + VALUE "FileDescription", L"GnuPG\x2019s network access daemon\0" VALUE "InternalName", "dirmngr\0" VALUE "OriginalFilename", "dirmngr.exe\0" VALUE "ProductName", W32INFO_PRODUCTNAME diff --git a/dirmngr/dirmngr.c b/dirmngr/dirmngr.c index e287194..06ef22b 100644 --- a/dirmngr/dirmngr.c +++ b/dirmngr/dirmngr.c @@ -146,6 +146,7 @@ enum cmd_and_opt_values { oHTTPWrapperProgram, oIgnoreCert, oIgnoreCertExtension, + oIgnoreCRLExtension, oUseTor, oNoUseTor, oKeyServer, @@ -157,6 +158,7 @@ enum cmd_and_opt_values { oConnectTimeout, oConnectQuickTimeout, oListenBacklog, + oCompatibilityFlags, aTest }; @@ -221,6 +223,7 @@ static ARGPARSE_OPTS opts[] = { ARGPARSE_s_n (oDisableCheckOwnSocket, "disable-check-own-socket", "@"), ARGPARSE_s_s (oIgnoreCert,"ignore-cert", "@"), ARGPARSE_s_s (oIgnoreCertExtension,"ignore-cert-extension", "@"), + ARGPARSE_s_s (oIgnoreCRLExtension,"ignore-crl-extension", "@"), ARGPARSE_header ("Network", N_("Network related options")), @@ -297,6 +300,7 @@ static ARGPARSE_OPTS opts[] = { ARGPARSE_s_s (oSocketName, "socket-name", "@"), /* Only for debugging. */ ARGPARSE_s_n (oDebugCacheExpiredCerts, "debug-cache-expired-certs", "@"), + ARGPARSE_s_s (oCompatibilityFlags, "compatibility-flags", "@"), ARGPARSE_header (NULL, ""), /* Stop the header group. */ @@ -328,6 +332,13 @@ static struct debug_flags_s debug_flags [] = { 77, NULL } /* 77 := Do not exit on "help" or "?". */ }; +/* The list of compatibility flags. */ +static struct compatibility_flags_s compatibility_flags [] = + { + { 0, NULL } + }; + + #define DEFAULT_MAX_REPLIES 10 #define DEFAULT_LDAP_TIMEOUT 15 /* seconds */ @@ -695,6 +706,7 @@ parse_rereadable_options (ARGPARSE_ARGS *pargs, int reread) opt.ignored_certs = tmp; } FREE_STRLIST (opt.ignored_cert_extensions); + FREE_STRLIST (opt.ignored_crl_extensions); http_register_tls_ca (NULL); FREE_STRLIST (hkp_cacert_filenames); FREE_STRLIST (opt.keyserver); @@ -709,6 +721,7 @@ parse_rereadable_options (ARGPARSE_ARGS *pargs, int reread) opt.ldaptimeout = DEFAULT_LDAP_TIMEOUT; ldapserver_list_needs_reset = 1; opt.debug_cache_expired_certs = 0; + opt.compat_flags = 0; return 1; } @@ -805,6 +818,10 @@ parse_rereadable_options (ARGPARSE_ARGS *pargs, int reread) add_to_strlist (&opt.ignored_cert_extensions, pargs->r.ret_str); break; + case oIgnoreCRLExtension: + add_to_strlist (&opt.ignored_crl_extensions, pargs->r.ret_str); + break; + case oUseTor: tor_mode = TOR_MODE_FORCE; break; @@ -871,6 +888,15 @@ parse_rereadable_options (ARGPARSE_ARGS *pargs, int reread) opt.debug_cache_expired_certs = 0; break; + case oCompatibilityFlags: + if (parse_compatibility_flags (pargs->r.ret_str, &opt.compat_flags, + compatibility_flags)) + { + pargs->r_opt = ARGPARSE_INVALID_ARG; + pargs->err = ARGPARSE_PRINT_WARNING; + } + break; + default: return 0; /* Not handled. */ } @@ -887,7 +913,7 @@ parse_rereadable_options (ARGPARSE_ARGS *pargs, int reread) /* This fucntion is called after option parsing to adjust some values * and call option setup functions. */ static void -post_option_parsing (void) +post_option_parsing (enum cmd_and_opt_values cmd) { /* It would be too surpirsing if the quick timeout is larger than * the standard value. */ @@ -895,7 +921,18 @@ post_option_parsing (void) opt.connect_quick_timeout = opt.connect_timeout; set_debug (); - set_tor_mode (); + /* For certain commands we do not want to set/test for Tor mode + * because that is somewhat expensive. */ + switch (cmd) + { + case aGPGConfList: + case aGPGConfTest: + case aGPGConfVersions: + break; + default: + set_tor_mode (); + break; + } } @@ -1208,7 +1245,7 @@ main (int argc, char **argv) log_printf ("\n"); } - post_option_parsing (); + post_option_parsing (cmd); /* Get LDAP server list from file unless --ldapserver has been used. */ #if USE_LDAP @@ -1669,6 +1706,8 @@ dirmngr_deinit_default_ctrl (ctrl_t ctrl) xfree (ctrl->http_proxy); ctrl->http_proxy = NULL; + nvc_release (ctrl->rootdse); + ctrl->rootdse = NULL; } @@ -1944,7 +1983,7 @@ reread_configuration (void) } gnupg_argparse (NULL, &pargs, NULL); /* Release internal state. */ xfree (twopart); - post_option_parsing (); + post_option_parsing (0); } @@ -1960,6 +1999,7 @@ dirmngr_sighup_action (void) crl_cache_deinit (); cert_cache_init (hkp_cacert_filenames); crl_cache_init (); + http_reinitialize (); reload_dns_stuff (0); ks_hkp_reload (); } diff --git a/dirmngr/dirmngr.h b/dirmngr/dirmngr.h index fed4599..07f0df8 100644 --- a/dirmngr/dirmngr.h +++ b/dirmngr/dirmngr.h @@ -36,6 +36,7 @@ #include "../common/sysutils.h" /* (gnupg_fd_t) */ #include "../common/asshelp.h" /* (assuan_context_t) */ #include "../common/i18n.h" +#include "../common/name-value.h" #include "dirmngr-status.h" #include "http.h" /* (parsed_uri_t) */ @@ -131,6 +132,11 @@ struct OID per string. */ strlist_t ignored_cert_extensions; + /* A list of CRL extension OIDs which are ignored so that one can + * claim that a critical extension has been handled. One OID per + * string. */ + strlist_t ignored_crl_extensions; + /* Allow expired certificates in the cache. */ int debug_cache_expired_certs; @@ -153,6 +159,9 @@ struct current after nextUpdate. */ strlist_t keyserver; /* List of default keyservers. */ + + /* Compatibility flags (COMPAT_FLAG_xxxx). */ + unsigned int compat_flags; } opt; @@ -179,6 +188,10 @@ struct #define DBG_LOOKUP (opt.debug & DBG_LOOKUP_VALUE) #define DBG_EXTPROG (opt.debug & DBG_EXTPROG_VALUE) +/* Compatibility flags */ +/* None so far. */ + + /* A simple list of certificate references. FIXME: Better use certlist_t also for references (Store NULL at .cert) */ struct cert_ref_s @@ -218,9 +231,12 @@ struct server_control_s int audit_events; /* Send audit events to client. */ char *http_proxy; /* The used http_proxy or NULL. */ + nvc_t rootdse; /* Container wit the rootDSE properties. */ + unsigned int timeout; /* Timeout for connect calls in ms. */ unsigned int http_no_crl:1; /* Do not check CRLs for https. */ + unsigned int rootdse_tried:1;/* Already tried to get the rootDSE. */ }; @@ -239,6 +255,8 @@ void ks_hkp_reload (void); /*-- server.c --*/ +void release_uri_item_list (uri_item_t list); + ldap_server_t get_ldapservers_from_ctrl (ctrl_t ctrl); ksba_cert_t get_cert_local (ctrl_t ctrl, const char *issuer); ksba_cert_t get_issuing_cert_local (ctrl_t ctrl, const char *issuer); diff --git a/dirmngr/dirmngr.w32-manifest.in b/dirmngr/dirmngr.w32-manifest.in index 719ca97..31d0965 100644 --- a/dirmngr/dirmngr.w32-manifest.in +++ b/dirmngr/dirmngr.w32-manifest.in @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="UTF-8" standalone="yes" ?> <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"> -<description>GNU Privacy Guard (Network daemon)</description> +<description>GNU Privacy Guard (Network Access Daemon)</description> <assemblyIdentity type="win32" name="GnuPG.dirmngr" @@ -15,4 +15,11 @@ <supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/><!-- Vista --> </application> </compatibility> +<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2"> + <security> + <requestedPrivileges> + <requestedExecutionLevel level="asInvoker"/> + </requestedPrivileges> + </security> +</trustInfo> </assembly> diff --git a/dirmngr/dirmngr_ldap-w32info.rc b/dirmngr/dirmngr_ldap-w32info.rc new file mode 100644 index 0000000..779d858 --- /dev/null +++ b/dirmngr/dirmngr_ldap-w32info.rc @@ -0,0 +1,52 @@ +/* dirmngr_ldap-w32info.rc -*- c -*- + * Copyright (C) 2023 g10 Code GmbH + * + * This file is free software; as a special exception the author gives + * unlimited permission to copy and/or distribute it, with or without + * modifications, as long as this notice is preserved. + * + * This 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. + */ + +#include "afxres.h" +#include "../common/w32info-rc.h" + +1 ICON "../common/gnupg.ico" + +1 VERSIONINFO + FILEVERSION W32INFO_VI_FILEVERSION + PRODUCTVERSION W32INFO_VI_PRODUCTVERSION + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x01L /* VS_FF_DEBUG (0x1)*/ +#else + FILEFLAGS 0x00L +#endif + FILEOS 0x40004L /* VOS_NT (0x40000) | VOS__WINDOWS32 (0x4) */ + FILETYPE 0x1L /* VFT_APP (0x1) */ + FILESUBTYPE 0x0L /* VFT2_UNKNOWN */ + BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" /* US English (0409), Unicode (04b0) */ + BEGIN + VALUE "FileDescription", L"GnuPG\x2019s LDAP helper\0" + VALUE "InternalName", "dirmngr_ldap\0" + VALUE "OriginalFilename", "dirmngr_ldap.exe\0" + VALUE "ProductName", W32INFO_PRODUCTNAME + VALUE "ProductVersion", W32INFO_PRODUCTVERSION + VALUE "CompanyName", W32INFO_COMPANYNAME + VALUE "FileVersion", W32INFO_FILEVERSION + VALUE "LegalCopyright", W32INFO_LEGALCOPYRIGHT + VALUE "Comments", W32INFO_COMMENTS + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 0x4b0 + END + END + +1 RT_MANIFEST "dirmngr_ldap.w32-manifest" diff --git a/dirmngr/dirmngr_ldap.c b/dirmngr/dirmngr_ldap.c index 10bf1ac..790663f 100644 --- a/dirmngr/dirmngr_ldap.c +++ b/dirmngr/dirmngr_ldap.c @@ -824,7 +824,7 @@ fetch_ldap (LDAP *ld, char *base, int scope, char *filter) /* Main processing. Take the filter and run the LDAP query. The * result is printed to stdout, errors are logged to the log stream. * To allow searching with a different base it is possible to extend - * the filer. For example: + * the filter. For example: * * ^CN=foo, OU=My Users&(objectClasses=*) * diff --git a/dirmngr/dirmngr_ldap.w32-manifest.in b/dirmngr/dirmngr_ldap.w32-manifest.in new file mode 100644 index 0000000..67db084 --- /dev/null +++ b/dirmngr/dirmngr_ldap.w32-manifest.in @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8" standalone="yes" ?> +<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"> +<description>GNU Privacy Guard (LDAP Helper)</description> +<assemblyIdentity + type="win32" + name="GnuPG.dirmngr.ldap" + version="@BUILD_VERSION@" + /> +<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1"> + <application> + <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/><!-- 10 --> + <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/><!-- 8.1 --> + <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/><!-- 8 --> + <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/><!-- 7 --> + <supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/><!-- Vista --> + </application> +</compatibility> +<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2"> + <security> + <requestedPrivileges> + <requestedExecutionLevel level="asInvoker"/> + </requestedPrivileges> + </security> +</trustInfo> +</assembly> diff --git a/dirmngr/dns.c b/dirmngr/dns.c index 25d839a..64deb5d 100644 --- a/dirmngr/dns.c +++ b/dirmngr/dns.c @@ -56,7 +56,7 @@ #include <ws2tcpip.h> typedef SOCKET socket_fd_t; #define STDCALL __stdcall -#ifdef TIME_WITH_SYS_TIME +#ifdef HAVE_SYS_TIME_H #include <sys/time.h> /* gettimeofday(2) */ #endif #else diff --git a/dirmngr/http-common.h b/dirmngr/http-common.h index 5e6657b..ddb340d 100644 --- a/dirmngr/http-common.h +++ b/dirmngr/http-common.h @@ -22,4 +22,6 @@ const char *get_default_keyserver (int name_only); +void http_reinitialize (void); + #endif /* HTTP_COMMON_H */ diff --git a/dirmngr/http.c b/dirmngr/http.c index 946234e..7fe3b50 100644 --- a/dirmngr/http.c +++ b/dirmngr/http.c @@ -2,7 +2,7 @@ * Copyright (C) 1999, 2001-2004, 2006, 2009, 2010, * 2011 Free Software Foundation, Inc. * Copyright (C) 1999, 2001-2004, 2006, 2009, 2010, 2011, 2014 Werner Koch - * Copyright (C) 2015-2017, 2021 g10 Code GmbH + * Copyright (C) 2015-2017, 2021, 2023 g10 Code GmbH * * This file is part of GnuPG. * @@ -65,6 +65,9 @@ # include <winsock2.h> # endif # include <windows.h> +# include <winhttp.h> +# define SECURITY_WIN32 1 +# include <security.h> #else /*!HAVE_W32_SYSTEM*/ # include <sys/types.h> # include <sys/socket.h> @@ -207,10 +210,29 @@ struct cookie_s /* True if TLS is to be used. */ int use_tls; + /* Optional malloced buffer holding pending bytes for the read + * function. LEN gives the used length, SIZE the allocated length. + * Used by the up_to_empty_line machinery. */ + struct { + size_t size; + size_t len; + char *data; + } pending; + /* The remaining content length and a flag telling whether to use the content length. */ uint64_t content_length; unsigned int content_length_valid:1; + + /* If the next flag is set the read function will limit the returned + * buffer to an empty line. That is the the pattern "\n\r\n" is + * detected and any further bytes are not returned to the caller. + * The flag is then reset. For technical reason we might have + * already read more which will be then saved for the next call in + * the PENDING buffer. */ + unsigned int up_to_empty_line:1; + unsigned int last_was_lf:1; /* Helper to detect empty line. */ + unsigned int last_was_lfcr:1; /* Helper to detect empty line. */ }; typedef struct cookie_s *cookie_t; @@ -227,6 +249,33 @@ static es_cookie_io_functions_t simple_cookie_functions = }; #endif +enum auth_negotiate_states + { + AUTH_NGT_NONE = 0, + AUTH_NGT_RCVD = 1, + AUTH_NGT_SENT = 2 + }; + +/* An object to store information about a proxy. */ +struct proxy_info_s +{ + parsed_uri_t uri; /* The parsed proxy URL. */ + int is_http_proxy; /* This is an http proxy. */ + +#ifdef HAVE_W32_SYSTEM + CredHandle cred_handle; /* Credential handle. */ + wchar_t *spn; /* Service principal name. */ + CtxtHandle ctxt_handle; /* Security context. */ + unsigned long token_size; /* Max. length of a token. */ + unsigned int cred_handle_valid:1; + unsigned int ctxt_handle_valid:1; +#endif /*HAVE_W32_SYSTEM*/ + + unsigned char *outtoken; /* The output token allocated with token_size. */ + unsigned long outtoklen; /* The current length of the token. */ +}; +typedef struct proxy_info_s *proxy_info_t; + #if SIZEOF_UNSIGNED_LONG == 8 # define HTTP_SESSION_MAGIC 0x0068545470534553 /* "hTTpSES" */ @@ -293,6 +342,7 @@ struct http_context_s my_socket_t sock; unsigned int in_data:1; unsigned int is_http_0_9:1; + unsigned int keep_alive:1; /* Keep the connection alive. */ estream_t fp_read; estream_t fp_write; void *write_cookie; @@ -313,13 +363,13 @@ struct http_context_s static int opt_verbose; static int opt_debug; -/* The global callback for the verification function. */ +/* The global callback for the verification function for GNUTLS. */ static gpg_error_t (*tls_callback) (http_t, http_session_t, int); -/* The list of files with trusted CA certificates. */ +/* The list of files with trusted CA certificates for GNUTLS. */ static strlist_t tls_ca_certlist; -/* The list of files with extra trusted CA certificates. */ +/* The list of files with extra trusted CA certificates for GNUTLS. */ static strlist_t cfg_ca_certlist; /* The global callback for net activity. */ @@ -564,7 +614,7 @@ http_set_verbose (int verbose, int debug) /* Register a non-standard global TLS callback function. If no verification is desired a callback needs to be registered which - always returns NULL. */ + always returns NULL. Only used for GNUTLS. */ void http_register_tls_callback (gpg_error_t (*cb)(http_t, http_session_t, int)) { @@ -575,7 +625,7 @@ http_register_tls_callback (gpg_error_t (*cb)(http_t, http_session_t, int)) /* Register a CA certificate for future use. The certificate is expected to be in FNAME. PEM format is assume if FNAME has a suffix of ".pem". If FNAME is NULL the list of CA files is - removed. */ + removed. Only used for GNUTLS. */ void http_register_tls_ca (const char *fname) { @@ -604,7 +654,8 @@ http_register_tls_ca (const char *fname) * expected to be in FNAME. PEM format is assume if FNAME has a * suffix of ".pem". If FNAME is NULL the list of CA files is * removed. This is a variant of http_register_tls_ca which puts the - * certificate into a separate list enabled using HTTP_FLAG_TRUST_CFG. */ + * certificate into a separate list enabled using HTTP_FLAG_TRUST_CFG. + * Only used for GNUTLS. */ void http_register_cfg_ca (const char *fname) { @@ -708,6 +759,64 @@ http_session_release (http_session_t sess) } +/* Create a write stream and store it in the fp_write member. Also + * store the tls flag and the session. */ +static gpg_error_t +make_fp_write (http_t hd, int use_tls, http_session_t session) +{ + cookie_t cookie; + + cookie = xtrycalloc (1, sizeof *cookie); + if (!cookie) + return gpg_error_from_syserror (); + cookie->sock = my_socket_ref (hd->sock); + cookie->use_tls = use_tls; + if (session) + cookie->session = http_session_ref (session); + hd->fp_write = es_fopencookie (cookie, "w", cookie_functions); + if (!hd->fp_write) + { + gpg_error_t err = gpg_error_from_syserror (); + my_socket_unref (cookie->sock, NULL, NULL); + if (session) + http_session_unref (cookie->session); + xfree (cookie); + return err; + } + hd->write_cookie = cookie; /* Cookie now owned by FP_WRITE. */ + return 0; +} + + +/* Create a read stream and store it in the fp_read member. Also + * store the tls flag and the session. */ +static gpg_error_t +make_fp_read (http_t hd, int use_tls, http_session_t session) +{ + cookie_t cookie; + + cookie = xtrycalloc (1, sizeof *cookie); + if (!cookie) + return gpg_error_from_syserror (); + cookie->sock = my_socket_ref (hd->sock); + cookie->use_tls = use_tls; + if (session) + cookie->session = http_session_ref (session); + hd->fp_read = es_fopencookie (cookie, "r", cookie_functions); + if (!hd->fp_read) + { + gpg_error_t err = gpg_error_from_syserror (); + my_socket_unref (cookie->sock, NULL, NULL); + if (session) + http_session_unref (cookie->session); + xfree (cookie); + return err; + } + hd->read_cookie = cookie; /* Cookie now owned by FP_READ. */ + return 0; +} + + /* Create a new session object which is currently used to enable TLS * support. It may eventually allow reusing existing connections. * Valid values for FLAGS are: @@ -757,6 +866,8 @@ http_session_new (http_session_t *r_session, int add_system_cas = !!(flags & HTTP_FLAG_TRUST_SYS); int is_hkps_pool; + (void)intended_hostname; + rc = gnutls_certificate_allocate_credentials (&sess->certcred); if (rc < 0) { @@ -821,7 +932,6 @@ http_session_new (http_session_t *r_session, /* Add system certificates to the session. */ if (add_system_cas) { -#if GNUTLS_VERSION_NUMBER >= 0x030014 static int shown; rc = gnutls_certificate_set_x509_system_trust (sess->certcred); @@ -832,7 +942,6 @@ http_session_new (http_session_t *r_session, shown = 1; log_info ("number of system provided CAs: %d\n", rc); } -#endif /* gnutls >= 3.0.20 */ } /* Add other configured certificates to the session. */ @@ -999,7 +1108,6 @@ http_raw_connect (http_t *r_hd, const char *server, unsigned short port, { gpg_error_t err = 0; http_t hd; - cookie_t cookie; *r_hd = NULL; @@ -1046,39 +1154,13 @@ http_raw_connect (http_t *r_hd, const char *server, unsigned short port, } /* Setup estreams for reading and writing. */ - cookie = xtrycalloc (1, sizeof *cookie); - if (!cookie) - { - err = gpg_err_make (default_errsource, gpg_err_code_from_syserror ()); - goto leave; - } - cookie->sock = my_socket_ref (hd->sock); - hd->fp_write = es_fopencookie (cookie, "w", cookie_functions); - if (!hd->fp_write) - { - err = gpg_err_make (default_errsource, gpg_err_code_from_syserror ()); - my_socket_unref (cookie->sock, NULL, NULL); - xfree (cookie); - goto leave; - } - hd->write_cookie = cookie; /* Cookie now owned by FP_WRITE. */ + err = make_fp_write (hd, 0, NULL); + if (err) + goto leave; - cookie = xtrycalloc (1, sizeof *cookie); - if (!cookie) - { - err = gpg_err_make (default_errsource, gpg_err_code_from_syserror ()); - goto leave; - } - cookie->sock = my_socket_ref (hd->sock); - hd->fp_read = es_fopencookie (cookie, "r", cookie_functions); - if (!hd->fp_read) - { - err = gpg_err_make (default_errsource, gpg_err_code_from_syserror ()); - my_socket_unref (cookie->sock, NULL, NULL); - xfree (cookie); - goto leave; - } - hd->read_cookie = cookie; /* Cookie now owned by FP_READ. */ + err = make_fp_read (hd, 0, NULL); + if (err) + goto leave; /* Register close notification to interlock the use of es_fclose in http_close and in user code. */ @@ -1110,7 +1192,7 @@ http_start_data (http_t hd) if (!hd->in_data) { if (opt_debug || (hd->flags & HTTP_FLAG_LOG_RESP)) - log_debug_with_string ("\r\n", "http.c:request-header:"); + log_debug ("http.c:request-header:start_data:\n"); es_fputs ("\r\n", hd->fp_write); es_fflush (hd->fp_write); hd->in_data = 1; @@ -1126,6 +1208,7 @@ http_wait_response (http_t hd) gpg_error_t err; cookie_t cookie; int use_tls; + int newfpread; /* Make sure that we are in the data. */ http_start_data (hd); @@ -1137,41 +1220,36 @@ http_wait_response (http_t hd) return gpg_err_make (default_errsource, GPG_ERR_INTERNAL); use_tls = cookie->use_tls; - es_fclose (hd->fp_write); - hd->fp_write = NULL; - /* The close has released the cookie and thus we better set it to NULL. */ - hd->write_cookie = NULL; + if (!hd->keep_alive) + { + es_fclose (hd->fp_write); + hd->fp_write = NULL; + /* The close has released the cookie and thus we better set it + * to NULL. */ + hd->write_cookie = NULL; + } /* Shutdown one end of the socket is desired. As per HTTP/1.0 this is not required but some very old servers (e.g. the original pksd keyserver didn't worked without it. */ - if ((hd->flags & HTTP_FLAG_SHUTDOWN)) + if (!hd->keep_alive && (hd->flags & HTTP_FLAG_SHUTDOWN)) shutdown (FD2INT (hd->sock->fd), 1); hd->in_data = 0; /* Create a new cookie and a stream for reading. */ - cookie = xtrycalloc (1, sizeof *cookie); - if (!cookie) - return gpg_err_make (default_errsource, gpg_err_code_from_syserror ()); - cookie->sock = my_socket_ref (hd->sock); - cookie->session = http_session_ref (hd->session); - cookie->use_tls = use_tls; - - hd->read_cookie = cookie; - hd->fp_read = es_fopencookie (cookie, "r", cookie_functions); - if (!hd->fp_read) + newfpread = 0; + if (!hd->keep_alive || !hd->fp_read) { - err = gpg_err_make (default_errsource, gpg_err_code_from_syserror ()); - my_socket_unref (cookie->sock, NULL, NULL); - http_session_unref (cookie->session); - xfree (cookie); - hd->read_cookie = NULL; - return err; + err = make_fp_read (hd, use_tls, hd->session); + if (err) + return err; + newfpread = 1; + ((cookie_t)(hd->read_cookie))->up_to_empty_line = 1; } err = parse_response (hd); - if (!err) + if (!err && newfpread) err = es_onclose (hd->fp_read, 1, fp_onclose_notification, hd); return err; @@ -1744,37 +1822,267 @@ is_hostname_port (const char *string) } -/* - * Send a HTTP request to the server - * Returns 0 if the request was successful - */ +/* Free the PROXY object. */ +static void +release_proxy_info (proxy_info_t proxy) +{ + if (!proxy) + return; + http_release_parsed_uri (proxy->uri); + xfree (proxy->outtoken); +#ifdef HAVE_W32_SYSTEM + if (proxy->ctxt_handle_valid) + DeleteSecurityContext (&proxy->ctxt_handle); + if (proxy->cred_handle_valid) + FreeCredentialsHandle (&proxy->cred_handle); +#endif + xfree (proxy); +} + + +/* Return an http session object. If clear is set, the object is + * destroyed. On error nULL is returned. */ +#ifdef HAVE_W32_SYSTEM +static HINTERNET +w32_get_internet_session (int clear) +{ + static HINTERNET session; + + if (clear) + { + if (session) + { + WinHttpCloseHandle (session); + session = NULL; + } + return NULL; + } + + if (!session) + { + session = WinHttpOpen (L"GnuPG dirmngr", + WINHTTP_ACCESS_TYPE_NO_PROXY, + WINHTTP_NO_PROXY_NAME, + WINHTTP_NO_PROXY_BYPASS, + 0); + if (!session) + { + log_error ("WinHttpOpen failed: %s\n", w32_strerror (-1)); + return NULL; + } + } + + return session; +} +#endif /*HAVE_W32_SYSTEM*/ + + +/* Return a proxy using a Windows API. */ +#ifdef HAVE_W32_SYSTEM +static char * +w32_get_proxy (const char *url) +{ + WINHTTP_AUTOPROXY_OPTIONS options = {0}; + WINHTTP_PROXY_INFO info; + char *result = NULL; + char *p; + wchar_t *wurl; + int defaultcfg = 0; + + wurl = utf8_to_wchar (url); + if (!wurl) + { + log_error ("utf8_to_wchar failed: %s\n", + gpg_strerror (gpg_error_from_syserror ())); + return NULL; + } + + options.dwFlags = (WINHTTP_AUTOPROXY_ALLOW_AUTOCONFIG + | WINHTTP_AUTOPROXY_ALLOW_CM + | WINHTTP_AUTOPROXY_ALLOW_STATIC + | WINHTTP_AUTOPROXY_AUTO_DETECT + | WINHTTP_AUTOPROXY_SORT_RESULTS); + options.dwAutoDetectFlags = (WINHTTP_AUTO_DETECT_TYPE_DHCP + | WINHTTP_AUTO_DETECT_TYPE_DNS_A); + options.fAutoLogonIfChallenged = TRUE; + + if (opt_debug) + log_debug ("calling WinHttpGetProxyForUrl (%s)\n", url); + if (!WinHttpGetProxyForUrl (w32_get_internet_session (0), + wurl, &options, &info)) + { + int ec = (int)GetLastError (); + if (ec == ERROR_WINHTTP_AUTODETECTION_FAILED) + { + if (opt_debug) + log_debug ("calling WinHttpGetDefaultProxyConfiguration\n"); + if (!WinHttpGetDefaultProxyConfiguration (&info)) + { + if (opt_verbose) + log_info ("WinHttpGetDefaultProxyConfiguration failed: " + "%s (%d)\n", w32_strerror (ec), ec); + xfree (wurl); + return NULL; + } + defaultcfg = 1; + } + else + { + if (opt_verbose) + log_info ("WinHttpGetProxyForUrl failed: %s (%d)\n", + w32_strerror (ec), ec); + xfree (wurl); + return NULL; + } + } + xfree (wurl); + + if (info.dwAccessType == WINHTTP_ACCESS_TYPE_NAMED_PROXY) + { + result = wchar_to_utf8 (info.lpszProxy); + if (!result) + log_error ("wchar_to_utf8 failed: %s\n", + gpg_strerror (gpg_error_from_syserror ())); + else + { + if (opt_debug) + log_debug ("proxies to use: '%s'\n", result); + /* The returned proxies are delimited by whitespace or + * semicolons. We return only the first proxy. */ + for (p=result; *p; p++) + if (spacep (p) || *p == ';') + { + *p = 0; + break; + } + } + } + else if (info.dwAccessType == WINHTTP_ACCESS_TYPE_NO_PROXY) + { + /* No proxy shall be used. */ + } + else + log_error ("%s returned unexpected code %lu\n", + defaultcfg? "WinHttpGetDefaultProxyConfiguration" + :"WinHttpGetProxyForUrl", info.dwAccessType); + + if (info.lpszProxy) + GlobalFree (info.lpszProxy); + if (info.lpszProxyBypass) + GlobalFree (info.lpszProxyBypass); + return result; +} +#endif /*HAVE_W32_SYSTEM*/ + + +/* Return the proxy to be used for the URL or host specified in HD. + * If OVERRIDE_PROXY is not NULL and not empty, this proxy will be + * used instead of any configured or dynamically determined proxy. If + * the function runs into an error an error code is returned and NULL + * is stored at R_PROXY. If the fucntion was successful and a proxy + * is to be used, information on the procy is stored at R_PROXY; if no + * proxy shall be used R_PROXY is set to NULL. Caller should always + * use release_proxy_info on the value stored at R_PROXY. */ static gpg_error_t -send_request (http_t hd, const char *httphost, const char *auth, - const char *proxy, const char *srvtag, unsigned int timeout, - strlist_t headers) +get_proxy_for_url (http_t hd, const char *override_proxy, proxy_info_t *r_proxy) { - gpg_error_t err; - const char *server; - char *request, *p; - unsigned short port; - const char *http_proxy = NULL; - char *proxy_authstr = NULL; - char *authstr = NULL; - assuan_fd_t sock; -#ifdef USE_TLS - int have_http_proxy = 0; + gpg_error_t err = 0; + const char *proxystr, *s; + proxy_info_t proxy; +#ifdef HAVE_W32_SYSTEM + char *proxystrbuf = NULL; +#endif + + *r_proxy = NULL; + + if (override_proxy && *override_proxy) + proxystr = override_proxy; + else if (!(hd->flags & HTTP_FLAG_TRY_PROXY)) + return 0; /* --honor-http-proxy not active */ + else if ((s = getenv (HTTP_PROXY_ENV)) && *s) + proxystr = s; +#ifdef HAVE_W32_SYSTEM + else if (hd->uri && hd->uri->original + && (proxystrbuf = w32_get_proxy (hd->uri->original))) + proxystr = proxystrbuf; +#endif + else + return 0; /* No proxy known. */ + + proxy = xtrycalloc (1, sizeof *proxy); + if (!proxy) + { + err = gpg_error_from_syserror (); + log_error ("error allocating memory for proxy\n"); + goto leave; + } + + err = parse_uri (&proxy->uri, proxystr, 0, 0); + if (gpg_err_code (err) == GPG_ERR_INV_URI + && is_hostname_port (proxystr)) + { + /* Retry assuming a "hostname:port" string. */ + char *tmpname = strconcat ("http://", proxystr, NULL); + if (!tmpname) + err = gpg_error_from_syserror (); + else if (!parse_uri (&proxy->uri, tmpname, 0, 0)) + err = 0; + xfree (tmpname); + } + + if (!err) + { + /* Get rid of the escapes in the authstring. */ + if (proxy->uri->auth) + remove_escapes (proxy->uri->auth); + + if (!strcmp (proxy->uri->scheme, "http")) + proxy->is_http_proxy = 1; + else if (!strcmp (proxy->uri->scheme, "socks4") + || !strcmp (proxy->uri->scheme, "socks5h")) + err = gpg_error (GPG_ERR_NOT_IMPLEMENTED); + else + err = gpg_error (GPG_ERR_INV_URI); + + if (err) + { + log_error ("invalid HTTP proxy (%s): %s\n", + proxystr, gpg_strerror (err)); + err = gpg_err_make (default_errsource, GPG_ERR_CONFIGURATION); + } + else if (opt_verbose) + log_info ("using '%s' to proxy '%s'\n", + proxystr, hd->uri? hd->uri->original : NULL); + } + + leave: +#ifdef HAVE_W32_SYSTEM + xfree (proxystrbuf); #endif + if (err) + xfree (proxy); + else + *r_proxy = proxy; + return err; +} + + +/* Some checks done by send_request. */ +static gpg_error_t +send_request_basic_checks (http_t hd) +{ + int mode; if (hd->uri->use_tls && !hd->session) { log_error ("TLS requested but no session object provided\n"); - return gpg_err_make (default_errsource, GPG_ERR_INTERNAL); + return gpg_error (GPG_ERR_INTERNAL); } #ifdef USE_TLS if (hd->uri->use_tls && !hd->session->tls_session) { log_error ("TLS requested but no TLS context available\n"); - return gpg_err_make (default_errsource, GPG_ERR_INTERNAL); + return gpg_error (GPG_ERR_INTERNAL); } if (opt_debug) log_debug ("Using TLS library: %s %s\n", @@ -1788,38 +2096,36 @@ send_request (http_t hd, const char *httphost, const char *auth, ); #endif /*USE_TLS*/ - if ((hd->flags & HTTP_FLAG_FORCE_TOR)) + if ((hd->flags & HTTP_FLAG_FORCE_TOR) + && (assuan_sock_get_flag (ASSUAN_INVALID_FD, "tor-mode", &mode) || !mode)) { - int mode; - - if (assuan_sock_get_flag (ASSUAN_INVALID_FD, "tor-mode", &mode) || !mode) - { - log_error ("Tor support is not available\n"); - return gpg_err_make (default_errsource, GPG_ERR_NOT_IMPLEMENTED); - } - /* Non-blocking connects do not work with our Tor proxy because - * we can't continue the Socks protocol after the EINPROGRESS. - * Disable the timeout to use a blocking connect. */ - timeout = 0; + log_error ("Tor support is not available\n"); + return gpg_error (GPG_ERR_NOT_IMPLEMENTED); } - server = *hd->uri->host ? hd->uri->host : "localhost"; - port = hd->uri->port ? hd->uri->port : 80; + return 0; +} + + +/* Helper for send_request to set the servername. */ +static gpg_error_t +send_request_set_sni (http_t hd, const char *name) +{ + gpg_error_t err = 0; +# if HTTP_USE_GNUTLS + int rc; +# endif /* Try to use SNI. */ #ifdef USE_TLS if (hd->uri->use_tls) { -# if HTTP_USE_GNUTLS - int rc; -# endif - xfree (hd->session->servername); - hd->session->servername = xtrystrdup (httphost? httphost : server); + hd->session->servername = xtrystrdup (name); if (!hd->session->servername) { - err = gpg_err_make (default_errsource, gpg_err_code_from_syserror ()); - return err; + err = gpg_error_from_syserror (); + goto leave; } # if HTTP_USE_NTBTLS @@ -1828,7 +2134,7 @@ send_request (http_t hd, const char *httphost, const char *auth, if (err) { log_info ("ntbtls_set_hostname failed: %s\n", gpg_strerror (err)); - return err; + goto leave; } # elif HTTP_USE_GNUTLS rc = gnutls_server_name_set (hd->session->tls_session, @@ -1841,177 +2147,21 @@ send_request (http_t hd, const char *httphost, const char *auth, } #endif /*USE_TLS*/ - if ( (proxy && *proxy) - || ( (hd->flags & HTTP_FLAG_TRY_PROXY) - && (http_proxy = getenv (HTTP_PROXY_ENV)) - && *http_proxy )) - { - parsed_uri_t uri; - - if (proxy) - http_proxy = proxy; - - err = parse_uri (&uri, http_proxy, 0, 0); - if (gpg_err_code (err) == GPG_ERR_INV_URI - && is_hostname_port (http_proxy)) - { - /* Retry assuming a "hostname:port" string. */ - char *tmpname = strconcat ("http://", http_proxy, NULL); - if (tmpname && !parse_uri (&uri, tmpname, 0, 0)) - err = 0; - xfree (tmpname); - } - - if (err) - ; -#ifdef USE_TLS - else if (!strcmp (uri->scheme, "http")) - have_http_proxy = 1; -#endif - else if (!strcmp (uri->scheme, "socks4") - || !strcmp (uri->scheme, "socks5h")) - err = gpg_err_make (default_errsource, GPG_ERR_NOT_IMPLEMENTED); - else - err = gpg_err_make (default_errsource, GPG_ERR_INV_URI); - - if (err) - { - log_error ("invalid HTTP proxy (%s): %s\n", - http_proxy, gpg_strerror (err)); - return gpg_err_make (default_errsource, GPG_ERR_CONFIGURATION); - } - - if (uri->auth) - { - remove_escapes (uri->auth); - proxy_authstr = make_header_line ("Proxy-Authorization: Basic ", - "\r\n", - uri->auth, strlen(uri->auth)); - if (!proxy_authstr) - { - err = gpg_err_make (default_errsource, - gpg_err_code_from_syserror ()); - http_release_parsed_uri (uri); - return err; - } - } - - err = connect_server (*uri->host ? uri->host : "localhost", - uri->port ? uri->port : 80, - hd->flags, NULL, timeout, &sock); - http_release_parsed_uri (uri); - } - else - { - err = connect_server (server, port, hd->flags, srvtag, timeout, &sock); - } - - if (err) - { - xfree (proxy_authstr); - return err; - } - hd->sock = my_socket_new (sock); - if (!hd->sock) - { - xfree (proxy_authstr); - return gpg_err_make (default_errsource, gpg_err_code_from_syserror ()); - } - -#if USE_TLS - if (have_http_proxy && hd->uri->use_tls) - { - int saved_flags; - cookie_t cookie; - - /* Try to use the CONNECT method to proxy our TLS stream. */ - request = es_bsprintf - ("CONNECT %s:%hu HTTP/1.0\r\nHost: %s:%hu\r\n%s", - httphost ? httphost : server, - port, - httphost ? httphost : server, - port, - proxy_authstr ? proxy_authstr : ""); - xfree (proxy_authstr); - proxy_authstr = NULL; - - if (! request) - return gpg_err_make (default_errsource, gpg_err_code_from_syserror ()); - - if (opt_debug || (hd->flags & HTTP_FLAG_LOG_RESP)) - log_debug_with_string (request, "http.c:request:"); - - cookie = xtrycalloc (1, sizeof *cookie); - if (! cookie) - { - err = gpg_err_make (default_errsource, gpg_err_code_from_syserror ()); - xfree (request); - return err; - } - cookie->sock = my_socket_ref (hd->sock); - hd->write_cookie = cookie; - - hd->fp_write = es_fopencookie (cookie, "w", cookie_functions); - if (! hd->fp_write) - { - err = gpg_err_make (default_errsource, gpg_err_code_from_syserror ()); - my_socket_unref (cookie->sock, NULL, NULL); - xfree (cookie); - xfree (request); - hd->write_cookie = NULL; - return err; - } - else if (es_fputs (request, hd->fp_write) || es_fflush (hd->fp_write)) - err = gpg_err_make (default_errsource, gpg_err_code_from_syserror ()); - - xfree (request); - request = NULL; - - /* Make sure http_wait_response doesn't close the stream. */ - saved_flags = hd->flags; - hd->flags &= ~HTTP_FLAG_SHUTDOWN; - - /* Get the response. */ - err = http_wait_response (hd); - - /* Restore flags, destroy stream. */ - hd->flags = saved_flags; - es_fclose (hd->fp_read); - hd->fp_read = NULL; - hd->read_cookie = NULL; - - /* Reset state. */ - hd->in_data = 0; - - if (err) - return err; - - if (hd->status_code != 200) - { - request = es_bsprintf - ("CONNECT %s:%hu", - httphost ? httphost : server, - port); - - log_error (_("error accessing '%s': http status %u\n"), - request ? request : "out of core", - http_get_status_code (hd)); - - xfree (request); - return gpg_error (GPG_ERR_NO_DATA); - } + leave: + return err; +} - /* We are done with the proxy, the code below will establish a - * TLS session and talk directly to the target server. */ - http_proxy = NULL; - } -#endif /* USE_TLS */ +/* Run the NTBTLS handshake if needed. */ #if HTTP_USE_NTBTLS +static gpg_error_t +run_ntbtls_handshake (http_t hd) +{ + gpg_error_t err; + estream_t in, out; + if (hd->uri->use_tls) { - estream_t in, out; - my_socket_ref (hd->sock); /* Until we support send/recv in estream under Windows we need @@ -2025,8 +2175,7 @@ send_request (http_t hd, const char *httphost, const char *auth, if (!in) { err = gpg_error_from_syserror (); - xfree (proxy_authstr); - return err; + goto leave; } #ifdef HAVE_W32_SYSTEM @@ -2039,8 +2188,7 @@ send_request (http_t hd, const char *httphost, const char *auth, { err = gpg_error_from_syserror (); es_fclose (in); - xfree (proxy_authstr); - return err; + goto leave; } err = ntbtls_set_transport (hd->session->tls_session, in, out); @@ -2048,11 +2196,11 @@ send_request (http_t hd, const char *httphost, const char *auth, { log_info ("TLS set_transport failed: %s <%s>\n", gpg_strerror (err), gpg_strsource (err)); - xfree (proxy_authstr); - return err; + es_fclose (in); + es_fclose (out); + goto leave; } -#ifdef HTTP_USE_NTBTLS if (hd->session->verify_cb) { err = ntbtls_set_verify_cb (hd->session->tls_session, @@ -2061,74 +2209,62 @@ send_request (http_t hd, const char *httphost, const char *auth, { log_error ("ntbtls_set_verify_cb failed: %s\n", gpg_strerror (err)); - xfree (proxy_authstr); - return err; + goto leave; } } -#endif /*HTTP_USE_NTBTLS*/ while ((err = ntbtls_handshake (hd->session->tls_session))) { -#if NTBTLS_VERSION_NUMBER >= 0x000200 unsigned int tlevel, ttype; - const char *s = ntbtls_get_last_alert (hd->session->tls_session, - &tlevel, &ttype); + const char *s; + + s = ntbtls_get_last_alert (hd->session->tls_session, &tlevel, &ttype); if (s) log_info ("TLS alert: %s (%u.%u)\n", s, tlevel, ttype); -#endif switch (err) { default: log_info ("TLS handshake failed: %s <%s>\n", gpg_strerror (err), gpg_strsource (err)); - xfree (proxy_authstr); - return err; + goto leave; } } hd->session->verify.done = 0; - /* Try the available verify callbacks until one returns success - * or a real error. Note that NTBTLS does the verification - * during the handshake via */ -#ifdef HTTP_USE_NTBTLS - err = 0; /* Fixme check that the CB has been called. */ -#else - err = gpg_error (GPG_ERR_NOT_IMPLEMENTED); -#endif + /* Note that in contrast to GNUTLS NTBTLS uses a registered + * callback to run the verification as part of the handshake. */ + err = 0; + /* FIXME: We could check that the CB has been called and if not + * error out with this warning: + * if (err) + * { + * log_info ("TLS connection authentication failed: %s <%s>\n", + * gpg_strerror (err), gpg_strsource (err)); + * goto leave; + * } + */ + } + else + err = 0; - if (hd->session->verify_cb - && gpg_err_source (err) == GPG_ERR_SOURCE_DIRMNGR - && gpg_err_code (err) == GPG_ERR_NOT_IMPLEMENTED) - err = hd->session->verify_cb (hd->session->verify_cb_value, - hd, hd->session, - (hd->flags | hd->session->flags), - hd->session->tls_session); - - if (tls_callback - && gpg_err_source (err) == GPG_ERR_SOURCE_DIRMNGR - && gpg_err_code (err) == GPG_ERR_NOT_IMPLEMENTED) - err = tls_callback (hd, hd->session, 0); + leave: + return err; +} +#endif /*HTTP_USE_NTBTLS*/ - if (gpg_err_source (err) == GPG_ERR_SOURCE_DIRMNGR - && gpg_err_code (err) == GPG_ERR_NOT_IMPLEMENTED) - err = http_verify_server_credentials (hd->session); - if (err) - { - log_info ("TLS connection authentication failed: %s <%s>\n", - gpg_strerror (err), gpg_strsource (err)); - xfree (proxy_authstr); - return err; - } +/* Run the GNUTLS handshake if needed. */ +#if HTTP_USE_GNUTLS +static gpg_error_t +run_gnutls_handshake (http_t hd, const char *server) +{ + gpg_error_t err; + int rc; - } -#elif HTTP_USE_GNUTLS if (hd->uri->use_tls) { - int rc; - my_socket_ref (hd->sock); gnutls_transport_set_ptr (hd->session->tls_session, hd->sock); gnutls_transport_set_pull_function (hd->session->tls_session, @@ -2164,8 +2300,8 @@ send_request (http_t hd, const char *httphost, const char *auth, } else log_info ("TLS handshake failed: %s\n", gnutls_strerror (rc)); - xfree (proxy_authstr); - return gpg_err_make (default_errsource, GPG_ERR_NETWORK); + err = gpg_error (GPG_ERR_NETWORK); + goto leave; } hd->session->verify.done = 0; @@ -2177,12 +2313,576 @@ send_request (http_t hd, const char *httphost, const char *auth, { log_info ("TLS connection authentication failed: %s\n", gpg_strerror (err)); - xfree (proxy_authstr); - return err; + goto leave; } } + else + err =0; + + leave: + return err; +} #endif /*HTTP_USE_GNUTLS*/ + +/* It INPUTSTRING is NULL get the intial token. If INPUTSTRING is not + * NULL, decode the string and use this as input from teh server. On + * success the final output token is stored at PROXY->OUTTOKEN and + * OUTTOKLEN. IF the authentication succeeded OUTTOKLEN is zero. */ +static gpg_error_t +proxy_get_token (proxy_info_t proxy, const char *inputstring) +{ +#ifdef HAVE_W32_SYSTEM + gpg_error_t err; + int rc; + SecBuffer chlg_buf; /* challenge buffer */ + SecBufferDesc chlg_desc; /* challenge descriptor */ + SecBuffer resp_buf; /* response buffer */ + SecBufferDesc resp_desc; /* response descriptor */ + unsigned long attrs; + TimeStamp expiry; /* (value not used) */ + void *intoken = NULL; + size_t intoklen; + + if (inputstring) + { + /* The input is expected in the token parameter but the paremter + * name is often forgotten. Thus we simply detect the parameter + * name and skip it, assuming no other parameters are given. */ + if (!strncmp (inputstring, "token=", 6)) + inputstring += 6; + + err = b64decode (inputstring, NULL, &intoken, &intoklen); + /* Just to be safe that we don't overflow an ulong we check the + * actual size against an arbitrary limit. */ + if (!err && intoklen > 65535) + err = gpg_error (GPG_ERR_ERANGE); + if (err || !intoklen) + { + log_error ("error decoding received auth token: %s\n", + err? gpg_strerror (err):"empty challenge token received"); + if (!err) + err = gpg_error (GPG_ERR_BAD_AUTH); + goto leave; + } + } + + if (!proxy->spn) + { + char *buffer = strconcat ("HTTP/", (*proxy->uri->host + ?proxy->uri->host:"localhost"), NULL); + if (!buffer) + { + err = gpg_error_from_syserror (); + goto leave; + } + if (opt_debug) + log_debug ("http.c:proxy_connect: using '%s' as SPN\n", buffer); + proxy->spn = utf8_to_wchar (buffer); + xfree (buffer); + if (!proxy->spn) + { + err = gpg_error_from_syserror (); + goto leave; + } + } + + if (!proxy->token_size || !proxy->outtoken) /* Not yet initialized. */ + { + PSecPkgInfoW pinfo; + + rc = QuerySecurityPackageInfoW (NEGOSSP_NAME_W, &pinfo); + if (rc) + { + log_error ("QSPI(Negotiate) failed: %s (%d)\n", + w32_strerror (rc), rc); + err = gpg_error (GPG_ERR_BAD_AUTH); + goto leave; + } + proxy->token_size = pinfo->cbMaxToken; + FreeContextBuffer (pinfo); + + proxy->outtoken = xtrymalloc (proxy->token_size); + if (!proxy->outtoken) + { + err = gpg_error_from_syserror (); + goto leave; + } + } + + if (!proxy->cred_handle_valid) + { + rc = AcquireCredentialsHandleW (NULL, NEGOSSP_NAME_W, + SECPKG_CRED_OUTBOUND, NULL, + NULL, /* Current user */ + NULL, /* reserved */ + NULL, /* reserved */ + &proxy->cred_handle, + NULL /* expiry */); + if (rc) + { + log_error ("ACH(Negotiate) failed: %s (%d)\n", w32_strerror (rc), rc); + err = gpg_error (GPG_ERR_NO_AUTH); + goto leave; + } + proxy->cred_handle_valid = 1; + } + + /* Now generate our challenge-response message. */ + if (intoken) + { + chlg_buf.BufferType = SECBUFFER_TOKEN; + chlg_buf.pvBuffer = intoken; + chlg_buf.cbBuffer = intoklen; + chlg_desc.ulVersion = SECBUFFER_VERSION; + chlg_desc.cBuffers = 1; + chlg_desc.pBuffers = &chlg_buf; + } + + resp_buf.BufferType = SECBUFFER_TOKEN; + resp_buf.pvBuffer = proxy->outtoken; + resp_buf.cbBuffer = proxy->token_size; + resp_desc.ulVersion = SECBUFFER_VERSION; + resp_desc.cBuffers = 1; + resp_desc.pBuffers = &resp_buf; + rc = InitializeSecurityContextW (&proxy->cred_handle, + (intoken && proxy->ctxt_handle_valid) + ? &proxy->ctxt_handle : NULL, + proxy->spn, /* service principal name */ + ISC_REQ_CONFIDENTIALITY, + 0, /* reserved */ + SECURITY_NATIVE_DREP, + intoken? &chlg_desc : NULL, + 0, /* reserved */ + &proxy->ctxt_handle, /* new context */ + &resp_desc, /* the output. */ + &attrs, /* attribs of the context. */ + &expiry); + switch (rc) + { + case SEC_E_OK: /* All done and no more ISC expected. */ + break; + + case SEC_I_COMPLETE_AND_CONTINUE: /* Need to call CompleteAuthToken. */ + case SEC_I_COMPLETE_NEEDED: + rc = CompleteAuthToken (&proxy->ctxt_handle, &resp_desc); + log_error ("CompleteAuthToken failed: %s (%d)\n", w32_strerror (rc), rc); + err = gpg_error (GPG_ERR_NO_AUTH); + goto leave; + break; + + case SEC_I_CONTINUE_NEEDED: /* Send the new token to the client. */ + break; + + default: + log_error ("ISC(Negotiate) failed: %s (%d)\n", w32_strerror (rc), rc); + err = gpg_error (GPG_ERR_NO_AUTH); + goto leave; + } + + proxy->outtoklen = resp_buf.cbBuffer; + proxy->ctxt_handle_valid = 1; + err = 0; + + leave: + xfree (intoken); + return err; + +#else /*!HAVE_W32_SYSTEM*/ + + (void)proxy; + (void)inputstring; + return gpg_error (GPG_ERR_NOT_IMPLEMENTED); + +#endif /*!HAVE_W32_SYSTEM*/ +} + + +/* Use the CONNECT method to proxy our TLS stream. */ +static gpg_error_t +run_proxy_connect (http_t hd, proxy_info_t proxy, + const char *httphost, const char *server, + unsigned short port) +{ + gpg_error_t err; + int saved_flags = hd->flags; + char *authhdr = NULL; + char *request = NULL; + char *tmpstr = NULL; + const char *s, *parms; + unsigned int idx; + int auth_basic = 0; + enum auth_negotiate_states authstate = 0; + unsigned int authpasses = 0; + + /* Authentication methods implemented here: + * RFC-2617 - HTTP Authentication: Basic and Digest Access Authentication + * RFC-4559 - SPNEGO-based Kerberos and NTLM HTTP Authentication + */ + auth_basic = !!proxy->uri->auth; + hd->keep_alive = !auth_basic; /* We may need to send more requests. */ + + /* For basic authentication we need to send just one request. */ + if (auth_basic + && !(authhdr = make_header_line ("Proxy-Authorization: Basic ", + "\r\n", + proxy->uri->auth, + strlen (proxy->uri->auth)))) + { + err = gpg_error_from_syserror (); + goto leave; + } + + again: + xfree (request); + request = es_bsprintf ("CONNECT %s:%hu HTTP/1.%c\r\nHost: %s:%hu\r\n%s%s", + httphost ? httphost : server, + port, + auth_basic? '0' : '1', + httphost ? httphost : server, + port, + authhdr ? authhdr : "", + hd->keep_alive? "Connection: keep-alive\r\n" : ""); + if (!request) + { + err = gpg_error_from_syserror (); + goto leave; + } + + if (opt_debug || (hd->flags & HTTP_FLAG_LOG_RESP)) + log_debug_with_string (request, "http.c:proxy:request:"); + + if (!hd->fp_write) + { + err = make_fp_write (hd, 0, NULL); + if (err) + goto leave; + } + + if (es_fputs (request, hd->fp_write) || es_fflush (hd->fp_write)) + { + err = gpg_error_from_syserror (); + goto leave; + } + + /* Make sure http_wait_response doesn't close the stream. */ + saved_flags = hd->flags; + hd->flags &= ~HTTP_FLAG_SHUTDOWN; + + /* Get the response and set hd->fp_read */ + err = http_wait_response (hd); + if (err) + goto leave; + + /* Reset state. */ + es_clearerr (hd->fp_read); + ((cookie_t)(hd->read_cookie))->up_to_empty_line = 1; + hd->in_data = 0; + + if (hd->status_code >= 200 && hd->status_code < 300 ) + err = 0; /* Success. */ + else if (hd->status_code == 407) + { + if (opt_debug) + log_debug ("http.c:proxy_connect: 407 seen\n"); + parms = NULL; + for (idx=0; (s = http_get_header (hd, "Proxy-Authenticate", idx)); idx++) + { + if (opt_debug) + log_debug ("http.c:proxy_connect: method=%s\n", s); + if (!parms) + parms = has_leading_keyword (s, "Negotiate"); + } + if (!parms) + authstate = AUTH_NGT_NONE; + else if (authstate == AUTH_NGT_NONE) + authstate = AUTH_NGT_RCVD; + + switch (authstate) + { + case AUTH_NGT_NONE: + if (opt_debug) + log_debug ("http.c:proxy_connect: no supported auth method\n"); + err = gpg_error (GPG_ERR_NO_AUTH); + break; + + case AUTH_NGT_RCVD: + if (opt_debug) + log_debug ("http.c:proxy_connect: using negotiate - init\n"); + err = proxy_get_token (proxy, NULL); + if (err) + goto leave; + if (proxy->outtoklen) /* Authentication needs to continue. */ + { + xfree (authhdr); + authhdr = make_header_line ("Proxy-Authorization: Negotiate ", + "\r\n", + proxy->outtoken, proxy->outtoklen); + if (!authhdr) + { + err = gpg_error_from_syserror (); + goto leave; + } + authstate = AUTH_NGT_SENT; + authpasses++; + goto again; + } + break; + + case AUTH_NGT_SENT: + if (opt_debug) + log_debug ("http.c:proxy_connect: using negotiate - next\n"); + if (!*parms) + { + log_debug ("proxy authentication failed" + " due to server not accepting our challenge\n"); + err = gpg_error (GPG_ERR_BAD_AUTH); + goto leave; + } + if (authpasses > 5) + { + log_error ("proxy authentication failed" + " due to too many passes\n"); + err = gpg_error (GPG_ERR_BAD_AUTH); + goto leave; + + } + err = proxy_get_token (proxy, parms); + if (err) + goto leave; + if (proxy->outtoklen) /* Authentication needs to continue. */ + { + xfree (authhdr); + authhdr = make_header_line ("Proxy-Authorization: Negotiate ", + "\r\n", + proxy->outtoken, proxy->outtoklen); + if (!authhdr) + { + err = gpg_error_from_syserror (); + goto leave; + } + authpasses++; + goto again; + } + break; + + default: + BUG(); + } + } + else + err = gpg_error (GPG_ERR_NO_DATA); + + if (err) + { + xfree (tmpstr); + tmpstr = es_bsprintf ("%s:%hu", httphost ? httphost : server, port); + log_error (_("error accessing '%s': http status %u\n"), + tmpstr ? tmpstr : "out of core", + http_get_status_code (hd)); + goto leave; + } + + leave: + if (hd->keep_alive) + { + es_fclose (hd->fp_write); + hd->fp_write = NULL; + /* The close has released the cookie and thus we better set it + * to NULL. */ + hd->write_cookie = NULL; + } + /* Restore flags, destroy stream, reset state. */ + hd->flags = saved_flags; + es_fclose (hd->fp_read); + hd->fp_read = NULL; + hd->read_cookie = NULL; + hd->keep_alive = 0; + hd->in_data = 0; + + xfree (request); + xfree (authhdr); + xfree (tmpstr); + return err; +} + + +/* Make a request string using a standard proxy. On success the + * request is stored at R_REQUEST (and will never be NULL). */ +static gpg_error_t +mk_proxy_request (http_t hd, proxy_info_t proxy, + const char *httphost, const char *server, + unsigned short port, const char *relpath, + const char *authstr, + char **r_request) +{ + gpg_error_t err = 0; + char *authhdr = NULL; + char *request = NULL; + + *r_request = NULL; + + if (proxy->uri->auth + && !(authhdr = make_header_line ("Proxy-Authorization: Basic ", + "\r\n", + proxy->uri->auth, + strlen (proxy->uri->auth)))) + { + err = gpg_error_from_syserror (); + goto leave; + } + + request = es_bsprintf ("%s %s://%s:%hu%s%s HTTP/1.0\r\n%s%s", + hd->req_type == HTTP_REQ_GET ? "GET" : + hd->req_type == HTTP_REQ_HEAD ? "HEAD" : + hd->req_type == HTTP_REQ_POST ? "POST" : "OOPS", + hd->uri->use_tls? "https" : "http", + httphost? httphost : server, + port, *relpath == '/' ? "" : "/", relpath, + authstr ? authstr : "", + authhdr ? authhdr : ""); + if (!request) + { + err = gpg_error_from_syserror (); + goto leave; + } + *r_request = request; + request = NULL; + + leave: + xfree (request); + xfree (authhdr); + return err; +} + + +/* Make a request string using. On success the request is stored at + * R_REQUEST (and will never be NULL). */ +static gpg_error_t +mk_std_request (http_t hd, + const char *httphost, const char *server, + unsigned short port, const char *relpath, + const char *authstr, + char **r_request) +{ + gpg_error_t err = 0; + char portstr[35]; + char *request = NULL; + + *r_request = NULL; + + if (port == (hd->uri->use_tls? 443 : 80)) + *portstr = 0; + else + snprintf (portstr, sizeof portstr, ":%u", port); + + request = es_bsprintf ("%s %s%s HTTP/1.0\r\nHost: %s%s\r\n%s", + hd->req_type == HTTP_REQ_GET ? "GET" : + hd->req_type == HTTP_REQ_HEAD ? "HEAD" : + hd->req_type == HTTP_REQ_POST ? "POST" : "OOPS", + *relpath == '/' ? "" : "/", relpath, + httphost? httphost : server, + portstr, + authstr? authstr:""); + if (!request) + { + err = gpg_error_from_syserror (); + goto leave; + } + *r_request = request; + request = NULL; + + leave: + xfree (request); + return err; +} + + +/* + * Send a HTTP request to the server + * Returns 0 if the request was successful + */ +static gpg_error_t +send_request (http_t hd, const char *httphost, const char *auth, + const char *override_proxy, + const char *srvtag, unsigned int timeout, + strlist_t headers) +{ + gpg_error_t err; + const char *server; + char *request = NULL; + char *relpath = NULL; + unsigned short port; + int use_http_proxy = 0; + char *proxy_authstr = NULL; + char *authstr = NULL; + assuan_fd_t sock; + proxy_info_t proxy = NULL; + + err = send_request_basic_checks (hd); + if (err) + goto leave; + + if ((hd->flags & HTTP_FLAG_FORCE_TOR)) + { + /* Non-blocking connects do not work with our Tor proxy because + * we can't continue the Socks protocol after the EINPROGRESS. + * Disable the timeout to use a blocking connect. */ + timeout = 0; + } + + server = *hd->uri->host ? hd->uri->host : "localhost"; + port = hd->uri->port ? hd->uri->port : 80; + + if ((err = send_request_set_sni (hd, httphost? httphost : server))) + goto leave; + + if ((err = get_proxy_for_url (hd, override_proxy, &proxy))) + goto leave; + + if (proxy && proxy->is_http_proxy) + { + use_http_proxy = 1; /* We want to use a proxy for the conenction. */ + err = connect_server (*proxy->uri->host ? proxy->uri->host : "localhost", + proxy->uri->port ? proxy->uri->port : 80, + hd->flags, NULL, timeout, &sock); + } + else + { + err = connect_server (server, port, hd->flags, srvtag, timeout, &sock); + } + if (err) + goto leave; + + hd->sock = my_socket_new (sock); + if (!hd->sock) + { + err = gpg_error_from_syserror (); + goto leave; + } + + if (use_http_proxy && hd->uri->use_tls) + { + err = run_proxy_connect (hd, proxy, httphost, server, port); + if (err) + goto leave; + + /* We are done with the proxy, the code below will establish a + * TLS session and talk directly to the target server. Thus we + * clear the flag to indicate this. */ + use_http_proxy = 0; + } + +#if HTTP_USE_NTBTLS + err = run_ntbtls_handshake (hd); +#elif HTTP_USE_GNUTLS + err = run_gnutls_handshake (hd, server); +#else + err = 0; +#endif + if (err) + goto leave; + if (auth || hd->uri->auth) { char *myauth; @@ -2192,8 +2892,8 @@ send_request (http_t hd, const char *httphost, const char *auth, myauth = xtrystrdup (auth); if (!myauth) { - xfree (proxy_authstr); - return gpg_err_make (default_errsource, gpg_err_code_from_syserror ()); + err = gpg_error_from_syserror (); + goto leave; } remove_escapes (myauth); } @@ -2205,115 +2905,65 @@ send_request (http_t hd, const char *httphost, const char *auth, authstr = make_header_line ("Authorization: Basic ", "\r\n", myauth, strlen (myauth)); - if (auth) + if (auth) /* (Was allocated.) */ xfree (myauth); if (!authstr) { - xfree (proxy_authstr); - return gpg_err_make (default_errsource, - gpg_err_code_from_syserror ()); + err = gpg_error_from_syserror (); + goto leave; } } - p = build_rel_path (hd->uri); - if (!p) - return gpg_err_make (default_errsource, gpg_err_code_from_syserror ()); - - if (http_proxy && *http_proxy) + relpath = build_rel_path (hd->uri); + if (!relpath) { - request = es_bsprintf - ("%s %s://%s:%hu%s%s HTTP/1.0\r\n%s%s", - hd->req_type == HTTP_REQ_GET ? "GET" : - hd->req_type == HTTP_REQ_HEAD ? "HEAD" : - hd->req_type == HTTP_REQ_POST ? "POST" : "OOPS", - hd->uri->use_tls? "https" : "http", - httphost? httphost : server, - port, *p == '/' ? "" : "/", p, - authstr ? authstr : "", - proxy_authstr ? proxy_authstr : ""); + err = gpg_error_from_syserror (); + goto leave; } - else - { - char portstr[35]; - if (port == (hd->uri->use_tls? 443 : 80)) - *portstr = 0; - else - snprintf (portstr, sizeof portstr, ":%u", port); - - request = es_bsprintf - ("%s %s%s HTTP/1.0\r\nHost: %s%s\r\n%s", - hd->req_type == HTTP_REQ_GET ? "GET" : - hd->req_type == HTTP_REQ_HEAD ? "HEAD" : - hd->req_type == HTTP_REQ_POST ? "POST" : "OOPS", - *p == '/' ? "" : "/", p, - httphost? httphost : server, - portstr, - authstr? authstr:""); - } - xfree (p); - if (!request) - { - err = gpg_err_make (default_errsource, gpg_err_code_from_syserror ()); - xfree (authstr); - xfree (proxy_authstr); - return err; - } + if (use_http_proxy) + err = mk_proxy_request (hd, proxy, httphost, server, port, + relpath, authstr, &request); + else + err = mk_std_request (hd, httphost, server, port, + relpath, authstr, &request); + if (err) + goto leave; if (opt_debug || (hd->flags & HTTP_FLAG_LOG_RESP)) log_debug_with_string (request, "http.c:request:"); /* First setup estream so that we can write even the first line using estream. This is also required for the sake of gnutls. */ - { - cookie_t cookie; - - cookie = xtrycalloc (1, sizeof *cookie); - if (!cookie) - { - err = gpg_err_make (default_errsource, gpg_err_code_from_syserror ()); - goto leave; - } - cookie->sock = my_socket_ref (hd->sock); - hd->write_cookie = cookie; - cookie->use_tls = hd->uri->use_tls; - cookie->session = http_session_ref (hd->session); + err = make_fp_write (hd, hd->uri->use_tls, hd->session); + if (err) + goto leave; - hd->fp_write = es_fopencookie (cookie, "w", cookie_functions); - if (!hd->fp_write) - { - err = gpg_err_make (default_errsource, gpg_err_code_from_syserror ()); - my_socket_unref (cookie->sock, NULL, NULL); - xfree (cookie); - hd->write_cookie = NULL; - } - else if (es_fputs (request, hd->fp_write) || es_fflush (hd->fp_write)) - err = gpg_err_make (default_errsource, gpg_err_code_from_syserror ()); - else - err = 0; + if (es_fputs (request, hd->fp_write) || es_fflush (hd->fp_write)) + { + err = gpg_error_from_syserror (); + goto leave; + } - if (!err) + for (;headers; headers=headers->next) { - for (;headers; headers=headers->next) + if (opt_debug || (hd->flags & HTTP_FLAG_LOG_RESP)) + log_debug_with_string (headers->d, "http.c:request-header:"); + if ((es_fputs (headers->d, hd->fp_write) || es_fflush (hd->fp_write)) + || (es_fputs("\r\n",hd->fp_write) || es_fflush(hd->fp_write))) { - if (opt_debug || (hd->flags & HTTP_FLAG_LOG_RESP)) - log_debug_with_string (headers->d, "http.c:request-header:"); - if ((es_fputs (headers->d, hd->fp_write) || es_fflush (hd->fp_write)) - || (es_fputs("\r\n",hd->fp_write) || es_fflush(hd->fp_write))) - { - err = gpg_err_make (default_errsource, - gpg_err_code_from_syserror ()); - break; - } + err = gpg_error_from_syserror (); + goto leave; } } - } leave: es_free (request); xfree (authstr); xfree (proxy_authstr); + xfree (relpath); + release_proxy_info (proxy); return err; } @@ -2439,19 +3089,26 @@ store_header (http_t hd, char *line) p++; value = p; - for (h=hd->headers; h; h = h->next) - if ( !strcmp (h->name, line) ) - break; - if (h) + /* Check whether we have already seen a line with that name. In + * that case we assume it is a comma separated list and merge + * them. Of course there are a few exceptions. */ + if (!strcmp (line, "Proxy-Authenticate") + || !strcmp (line, "Www-Authenticate")) + ; /* Better to have them separate. */ + else { - /* We have already seen a line with that name. Thus we assume - * it is a comma separated list and merge them. */ - p = strconcat (h->value, ",", value, NULL); - if (!p) - return gpg_err_code_from_syserror (); - xfree (h->value); - h->value = p; - return 0; + for (h=hd->headers; h; h = h->next) + if ( !strcmp (h->name, line) ) + break; + if (h) + { + p = strconcat (h->value, ",", value, NULL); + if (!p) + return gpg_err_code_from_syserror (); + xfree (h->value); + h->value = p; + return 0; + } } /* Append a new header. */ @@ -2474,18 +3131,27 @@ store_header (http_t hd, char *line) /* Return the header NAME from the last response. The returned value - is valid as along as HD has not been closed and no other request - has been send. If the header was not found, NULL is returned. NAME - must be canonicalized, that is the first letter of each dash - delimited part must be uppercase and all other letters lowercase. */ + * is valid as along as HD has not been closed and no other request + * has been send. If the header was not found, NULL is returned. NAME + * must be canonicalized, that is the first letter of each dash + * delimited part must be uppercase and all other letters lowercase. + * SKIP gives the number of entries of the requested NAME to skip + * before returning; this can be used to enumerate headers with the + * same name (see store_header). +*/ const char * -http_get_header (http_t hd, const char *name) +http_get_header (http_t hd, const char *name, unsigned int skip) { header_t h; for (h=hd->headers; h; h = h->next) - if ( !strcmp (h->name, name) ) - return h->value; + if (!strcmp (h->name, name)) + { + if (skip) + skip--; + else + return h->value; + } return NULL; } @@ -2608,7 +3274,7 @@ parse_response (http_t hd) cookie->content_length_valid = 0; if (!(hd->flags & HTTP_FLAG_IGNORE_CL)) { - s = http_get_header (hd, "Content-Length"); + s = http_get_header (hd, "Content-Length", 0); if (s) { cookie->content_length_valid = 1; @@ -3063,8 +3729,14 @@ connect_server (const char *server, unsigned short port, if (!connected) { if (!hostfound) - log_error ("can't connect to '%s': %s\n", - server, "host not found"); + { + log_error ("can't connect to '%s': %s\n", + server, "host not found"); + /* If the resolver told us "no name" translate this in this + * case to "unknown host". */ + if (gpg_err_code (last_err) == GPG_ERR_NO_NAME) + last_err = 0; + } else if (!anyhostaddr) log_error ("can't connect to '%s': %s\n", server, "no IP address for host"); @@ -3185,31 +3857,48 @@ cookie_read (void *cookie, void *buffer, size_t size) { cookie_t c = cookie; int nread; + size_t offset = 0; if (c->content_length_valid) { if (!c->content_length) - return 0; /* EOF */ + { + c->content_length_valid = 0; + return 0; /* EOF */ + } if (c->content_length < size) size = c->content_length; } + if (c->pending.len) + { + offset = c->pending.len > size? size : c->pending.len; + memcpy (buffer, c->pending.data, offset); + c->pending.len -= offset; + } + + if (offset >= size) + nread = offset; + else #if HTTP_USE_NTBTLS if (c->use_tls && c->session && c->session->tls_session) { estream_t in, out; ntbtls_get_stream (c->session->tls_session, &in, &out); - nread = es_fread (buffer, 1, size, in); + nread = es_fread ((char*)buffer+offset, 1, size-offset, in); if (opt_debug) - log_debug ("TLS network read: %d/%zu\n", nread, size); + log_debug ("TLS network read: %d/%zu\n", nread, size-offset); + if (nread >= 0) + nread += offset; } else #elif HTTP_USE_GNUTLS if (c->use_tls && c->session && c->session->tls_session) { again: - nread = gnutls_record_recv (c->session->tls_session, buffer, size); + nread = gnutls_record_recv (c->session->tls_session, + (char*)buffer+offset, size-offset); if (nread < 0) { if (nread == GNUTLS_E_INTERRUPTED) @@ -3236,11 +3925,86 @@ cookie_read (void *cookie, void *buffer, size_t size) gpg_err_set_errno (EIO); return -1; } + if (nread >= 0) + nread += offset; } else #endif /*HTTP_USE_GNUTLS*/ { - nread = read_server (c->sock->fd, buffer, size); + nread = read_server (c->sock->fd, (char*)buffer+offset, size-offset); + if (opt_debug) + log_debug ("network read: %d/%zu\n", nread, size); + if (nread >= 0) + nread += offset; + } + + if (nread > 0 && c->up_to_empty_line) + { + gpg_error_t err; + const char *s; + size_t n; + int extra; + int lfcr_pending = 0; + char *bufp = buffer; + + if (c->last_was_lf && nread > 1 && bufp[0] == '\r' && bufp[1] == '\n') + { + s = buffer; + extra = 2; + } + else if (c->last_was_lf && bufp[0] == '\r') + { + lfcr_pending = 1; + s = buffer; /* Only to avoid the call to gnupg_memstr. */ + } + else if (c->last_was_lfcr && bufp[0] == '\n') + { + s = buffer; + extra = 1; + } + else + s = NULL; + + c->last_was_lfcr = c->last_was_lf = 0; + + if (!s) + { + s = gnupg_memstr (buffer, nread, "\n\r\n"); + extra = 3; + } + + if (lfcr_pending) + c->last_was_lfcr = 1; + else if (s) + { + /* Save away the rest and return up to the LF. */ + log_assert (!c->pending.len); + n = (s+extra) - bufp; + log_assert (n <= nread); + c->pending.len = nread - n; + if (!c->pending.data || c->pending.len >= c->pending.size) + { + xfree (c->pending.data); + c->pending.size = c->pending.len + 256; /* Some extra space. */ + c->pending.data = xtrymalloc (c->pending.size); + if (!c->pending.data) + { + err = gpg_error_from_syserror (); + log_error ("error allocating network read buffer: %s\n", + gpg_strerror (err)); + return -1; + } + memcpy (c->pending.data, bufp + n, c->pending.len); + } + else + memcpy (c->pending.data, bufp + n, c->pending.len); + nread = n; /* Return everything up to the empty line. */ + c->up_to_empty_line = 0; + } + else if (bufp[nread-1] == '\n') + c->last_was_lf = 1; + else if (nread > 1 && bufp[nread-2] == '\n' && bufp[nread-1] == '\r') + c->last_was_lfcr = 1; } if (c->content_length_valid && nread > 0) @@ -3401,6 +4165,7 @@ cookie_close (void *cookie) if (c->session) http_session_unref (c->session); + xfree (c->pending.data); xfree (c); return 0; } @@ -3409,7 +4174,7 @@ cookie_close (void *cookie) /* Verify the credentials of the server. Returns 0 on success and - store the result in the session object. */ + store the result in the session object. Only used by GNUTLS. */ gpg_error_t http_verify_server_credentials (http_session_t sess) { @@ -3443,19 +4208,15 @@ http_verify_server_credentials (http_session_t sess) } else if (status) { - log_error ("%s: status=0x%04x\n", errprefix, status); -#if GNUTLS_VERSION_NUMBER >= 0x030104 - { - gnutls_datum_t statusdat; + gnutls_datum_t statusdat; - if (!gnutls_certificate_verification_status_print - (status, GNUTLS_CRT_X509, &statusdat, 0)) - { - log_info ("%s: %s\n", errprefix, statusdat.data); - gnutls_free (statusdat.data); - } - } -#endif /*gnutls >= 3.1.4*/ + log_error ("%s: status=0x%04x\n", errprefix, status); + if (!gnutls_certificate_verification_status_print + (status, GNUTLS_CRT_X509, &statusdat, 0)) + { + log_info ("%s: %s\n", errprefix, statusdat.data); + gnutls_free (statusdat.data); + } sess->verify.status = status; if (!err) @@ -3796,3 +4557,13 @@ http_status2string (unsigned int status) return ""; } + + +/* Fucntion called on SIGHUP to flush internal variables. */ +void +http_reinitialize (void) +{ +#ifdef HAVE_W32_SYSTEM + w32_get_internet_session (1); /* Clear our session. */ +#endif /*HAVE_W32_SYSTEM*/ +} diff --git a/dirmngr/http.h b/dirmngr/http.h index 2b11c58..fc6a823 100644 --- a/dirmngr/http.h +++ b/dirmngr/http.h @@ -130,9 +130,11 @@ typedef gpg_error_t (*http_verify_cb_t) (void *opaque, void http_set_verbose (int verbose, int debug); +/* The next three functions are only used with GNUTLS. */ void http_register_tls_callback (gpg_error_t (*cb)(http_t,http_session_t,int)); void http_register_tls_ca (const char *fname); void http_register_cfg_ca (const char *fname); + void http_register_netactivity_cb (void (*cb)(void)); @@ -191,7 +193,7 @@ estream_t http_get_read_ptr (http_t hd); estream_t http_get_write_ptr (http_t hd); unsigned int http_get_status_code (http_t hd); const char *http_get_tls_info (http_t hd, const char *what); -const char *http_get_header (http_t hd, const char *name); +const char *http_get_header (http_t hd, const char *name, unsigned int skip); const char **http_get_header_names (http_t hd); gpg_error_t http_verify_server_credentials (http_session_t sess); diff --git a/dirmngr/ks-action.c b/dirmngr/ks-action.c index edf4ca5..ad200ec 100644 --- a/dirmngr/ks-action.c +++ b/dirmngr/ks-action.c @@ -34,6 +34,100 @@ # include "ldap-parse-uri.h" #endif + +/* Parse an URI and store it in a new parsed URI item object which is + * returned at R_PARSEDURI (with its next set to NULL). On error an + * error code is returned an NULL stored at R_PARSEDITEM. */ +gpg_error_t +ks_action_parse_uri (const char *uri, uri_item_t *r_parseduri) +{ + gpg_error_t err; + uri_item_t item; + char *tmpstr = NULL; +#if USE_LDAP + const char *s; +#endif + + *r_parseduri = NULL; + + if (!uri) + return gpg_error (GPG_ERR_INV_URI); + + item = xtrymalloc (sizeof *item + strlen (uri)); + if (!item) + return gpg_error_from_syserror (); + + item->next = NULL; + item->parsed_uri = NULL; + strcpy (item->uri, uri); + +#if USE_LDAP + if (!strncmp (uri, "ldap:", 5) && !(uri[5] == '/' && uri[6] == '/')) + { + /* Special ldap scheme given. This differs from a valid ldap + * scheme in that no double slash follows. We use + * http_parse_uri to put it as opaque value into parsed_uri. */ + tmpstr = strconcat ("opaque:", uri+5, NULL); + if (!tmpstr) + err = gpg_error_from_syserror (); + else + err = http_parse_uri (&item->parsed_uri, tmpstr, 0); + } + else if ((s=strchr (uri, ':')) && !(s[1] == '/' && s[2] == '/')) + { + /* No valid scheme given. We use http_parse_uri to put the + * string as opaque value into parsed_uri. */ + tmpstr = strconcat ("opaque:", uri, NULL); + if (!tmpstr) + err = gpg_error_from_syserror (); + else + err = http_parse_uri (&item->parsed_uri, tmpstr, 0); + } + else if (ldap_uri_p (uri)) + { + int fixup = 0; + /* Fixme: We should get rid of that parser and replace it with + * our generic (http) URI parser. */ + + /* If no port has been specified and the scheme ist ldaps we use + * our idea of the default port because the standard LDAP URL + * parser would use 636 here. This is because we redefined + * ldaps to mean starttls. */ +#ifdef HAVE_W32_SYSTEM + if (!strcmp (uri, "ldap:///")) + fixup = 1; + else +#endif + if (!http_parse_uri (&item->parsed_uri,uri,HTTP_PARSE_NO_SCHEME_CHECK)) + { + if (!item->parsed_uri->port + && !strcmp (item->parsed_uri->scheme, "ldaps")) + fixup = 2; + http_release_parsed_uri (item->parsed_uri); + item->parsed_uri = NULL; + } + + err = ldap_parse_uri (&item->parsed_uri, uri); + if (!err && fixup == 1) + item->parsed_uri->ad_current = 1; + else if (!err && fixup == 2) + item->parsed_uri->port = 389; + } + else +#endif /* USE_LDAP */ + { + err = http_parse_uri (&item->parsed_uri, uri, HTTP_PARSE_NO_SCHEME_CHECK); + } + + xfree (tmpstr); + if (err) + xfree (item); + else + *r_parseduri = item; + return err; +} + + /* Called by the engine's help functions to print the actual help. */ gpg_error_t ks_print_help (ctrl_t ctrl, const char *text) @@ -241,7 +335,8 @@ ks_action_search (ctrl_t ctrl, uri_item_t keyservers, keyservers and write the result to the provided output stream. */ gpg_error_t ks_action_get (ctrl_t ctrl, uri_item_t keyservers, - strlist_t patterns, unsigned int ks_get_flags, estream_t outfp) + strlist_t patterns, unsigned int ks_get_flags, + gnupg_isotime_t newer, estream_t outfp) { gpg_error_t err = 0; gpg_error_t first_err = 0; @@ -268,7 +363,7 @@ ks_action_get (ctrl_t ctrl, uri_item_t keyservers, || strcmp (uri->parsed_uri->scheme, "https") == 0); int is_ldap = 0; - if ((ks_get_flags & KS_GET_FLAG_ONLY_LDAP)) + if ((ks_get_flags & (KS_GET_FLAG_ONLY_LDAP|KS_GET_FLAG_ONLY_AD))) is_hkp_s = is_http_s = 0; #if USE_LDAP @@ -286,7 +381,7 @@ ks_action_get (ctrl_t ctrl, uri_item_t keyservers, #if USE_LDAP if (is_ldap) err = ks_ldap_get (ctrl, uri->parsed_uri, sl->d, ks_get_flags, - &infp); + newer, &infp); else #endif if (is_hkp_s) @@ -446,3 +541,53 @@ ks_action_put (ctrl_t ctrl, uri_item_t keyservers, err = first_err; return err; } + + + +/* Query the default LDAP server or the one given by URL using + * the filter expression FILTER. Write the result to OUTFP. */ +gpg_error_t +ks_action_query (ctrl_t ctrl, const char *url, unsigned int ks_get_flags, + const char *filter, char **attrs, + gnupg_isotime_t newer, estream_t outfp) +{ +#if USE_LDAP + gpg_error_t err; + estream_t infp = NULL; + uri_item_t puri; /* The broken down URI (only one item used). */ + + if (!url && (ks_get_flags & KS_GET_FLAG_ROOTDSE)) + url = "ldap://"; + + err = ks_action_parse_uri (url, &puri); + if (err) + return err; + + if ((ks_get_flags & KS_GET_FLAG_ROOTDSE)) + { + /* Reset authentication for a serverless connection. */ + puri->parsed_uri->ad_current = 0; + puri->parsed_uri->auth = NULL; + } + + if (!strcmp (puri->parsed_uri->scheme, "ldap") + || !strcmp (puri->parsed_uri->scheme, "ldaps") + || !strcmp (puri->parsed_uri->scheme, "ldapi") + || puri->parsed_uri->opaque) + { + err = ks_ldap_query (ctrl, puri->parsed_uri, ks_get_flags, filter, + attrs, newer, &infp); + if (!err) + err = copy_stream (infp, outfp); + } + else + err = gpg_error (GPG_ERR_CONFIGURATION); /* No LDAP server known. */ + + es_fclose (infp); + release_uri_item_list (puri); + return err; + +#else /* !USE_LDAP */ + return gpg_error (GPG_ERR_NOT_IMPLEMENTED); +#endif +} diff --git a/dirmngr/ks-action.h b/dirmngr/ks-action.h index e780fc7..223aae2 100644 --- a/dirmngr/ks-action.h +++ b/dirmngr/ks-action.h @@ -21,17 +21,22 @@ #ifndef DIRMNGR_KS_ACTION_H #define DIRMNGR_KS_ACTION_H 1 +gpg_error_t ks_action_parse_uri (const char *uri, uri_item_t *r_parseduri); gpg_error_t ks_action_help (ctrl_t ctrl, const char *url); gpg_error_t ks_action_resolve (ctrl_t ctrl, uri_item_t keyservers); gpg_error_t ks_action_search (ctrl_t ctrl, uri_item_t keyservers, strlist_t patterns, estream_t outfp); gpg_error_t ks_action_get (ctrl_t ctrl, uri_item_t keyservers, strlist_t patterns, unsigned int ks_get_flags, - estream_t outfp); + gnupg_isotime_t newer, estream_t outfp); gpg_error_t ks_action_fetch (ctrl_t ctrl, const char *url, estream_t outfp); gpg_error_t ks_action_put (ctrl_t ctrl, uri_item_t keyservers, void *data, size_t datalen, void *info, size_t infolen); +gpg_error_t ks_action_query (ctrl_t ctrl, const char *ldapserver, + unsigned int ks_get_flags, + const char *filter, char **attr, + gnupg_isotime_t newer, estream_t outfp); #endif /*DIRMNGR_KS_ACTION_H*/ diff --git a/dirmngr/ks-engine-hkp.c b/dirmngr/ks-engine-hkp.c index ef7a717..1767629 100644 --- a/dirmngr/ks-engine-hkp.c +++ b/dirmngr/ks-engine-hkp.c @@ -1281,7 +1281,7 @@ send_request (ctrl_t ctrl, const char *request, const char *hostportstr, { xfree (request_buffer); err = http_prepare_redirect (&redirinfo, http_get_status_code (http), - http_get_header (http, "Location"), + http_get_header (http, "Location", 0), &request_buffer); if (err) goto leave; @@ -1294,18 +1294,17 @@ send_request (ctrl_t ctrl, const char *request, const char *hostportstr, } goto once_more; - case 501: - err = gpg_error (GPG_ERR_NOT_IMPLEMENTED); - goto leave; - - case 413: /* Payload too large */ - err = gpg_error (GPG_ERR_TOO_LARGE); - goto leave; - default: log_error (_("error accessing '%s': http status %u\n"), request, http_get_status_code (http)); - err = gpg_error (GPG_ERR_NO_DATA); + switch (http_get_status_code (http)) + { + case 401: err = gpg_error (GPG_ERR_NO_AUTH); break; + case 407: err = gpg_error (GPG_ERR_BAD_AUTH); break; + case 413: err = gpg_error (GPG_ERR_TOO_LARGE); break; + case 501: err = gpg_error (GPG_ERR_NOT_IMPLEMENTED); break; + default: err = gpg_error (GPG_ERR_NO_DATA); break; + } goto leave; } diff --git a/dirmngr/ks-engine-http.c b/dirmngr/ks-engine-http.c index c96625d..bb7a033 100644 --- a/dirmngr/ks-engine-http.c +++ b/dirmngr/ks-engine-http.c @@ -179,7 +179,7 @@ ks_http_fetch (ctrl_t ctrl, const char *url, unsigned int flags, { xfree (request_buffer); err = http_prepare_redirect (&redirinfo, http_get_status_code (http), - http_get_header (http, "Location"), + http_get_header (http, "Location", 0), &request_buffer); if (err) goto leave; @@ -192,14 +192,16 @@ ks_http_fetch (ctrl_t ctrl, const char *url, unsigned int flags, } goto once_more; - case 413: /* Payload too large */ - err = gpg_error (GPG_ERR_TOO_LARGE); - goto leave; - default: log_error (_("error accessing '%s': http status %u\n"), url, http_get_status_code (http)); - err = gpg_error (GPG_ERR_NO_DATA); + switch (http_get_status_code (http)) + { + case 401: err = gpg_error (GPG_ERR_NO_AUTH); break; + case 407: err = gpg_error (GPG_ERR_BAD_AUTH); break; + case 413: err = gpg_error (GPG_ERR_TOO_LARGE); break; + default: err = gpg_error (GPG_ERR_NO_DATA); break; + } goto leave; } diff --git a/dirmngr/ks-engine-ldap.c b/dirmngr/ks-engine-ldap.c index 22f974c..b416ac0 100644 --- a/dirmngr/ks-engine-ldap.c +++ b/dirmngr/ks-engine-ldap.c @@ -1,7 +1,7 @@ /* ks-engine-ldap.c - talk to a LDAP keyserver * Copyright (C) 2001, 2002, 2004, 2005, 2006 * 2007 Free Software Foundation, Inc. - * Copyright (C) 2015, 2020 g10 Code GmbH + * Copyright (C) 2015, 2020, 2023 g10 Code GmbH * * This file is part of GnuPG. * @@ -29,12 +29,20 @@ #endif #include <stdlib.h> #include <npth.h> +#ifdef HAVE_W32_SYSTEM +# ifndef WINVER +# define WINVER 0x0500 /* Same as in common/sysutils.c */ +# endif +# include <winsock2.h> +# include <sddl.h> +#endif #include "dirmngr.h" #include "misc.h" #include "../common/userids.h" #include "../common/mbox-util.h" +#include "ks-action.h" #include "ks-engine.h" #include "ldap-misc.h" #include "ldap-parse-uri.h" @@ -46,6 +54,7 @@ #define SERVERINFO_PGPKEYV2 2 /* Needs "pgpKeyV2" instead of "pgpKey"*/ #define SERVERINFO_SCHEMAV2 4 /* Version 2 of the Schema. */ #define SERVERINFO_NTDS 8 /* Server is an Active Directory. */ +#define SERVERINFO_GENERIC 16 /* Connected in genric mode. */ /* The page size requested from the server. */ @@ -64,6 +73,7 @@ struct ks_engine_ldap_local_s LDAPMessage *message; LDAPMessage *msg_iter; /* Iterator for message. */ unsigned int serverinfo; + int scope; char *basedn; char *keyspec; char *filter; @@ -73,6 +83,9 @@ struct ks_engine_ldap_local_s int more_pages; /* More pages announced by server. */ }; +/*-- prototypes --*/ +static char *map_rid_to_dn (ctrl_t ctrl, const char *rid); +static char *basedn_from_rootdse (ctrl_t ctrl, parsed_uri_t uri); @@ -150,6 +163,114 @@ my_ldap_value_free (char **vals) } +/* Print a description of supported variables. */ +void +ks_ldap_help_variables (ctrl_t ctrl) +{ + const char data[] = + "Supported variables in LDAP filter expressions:\n" + "\n" + "domain - The defaultNamingContext.\n" + "domain_admins - Group of domain admins.\n" + "domain_users - Group with all user accounts.\n" + "domain_guests - Group with the builtin gues account.\n" + "domain_computers - Group with all clients and servers.\n" + "cert_publishers - Group with all cert issuing computers.\n" + "protected_users - Group of users with extra protection.\n" + "key_admins - Group for delegated access to msdsKeyCredentialLink.\n" + "enterprise_key_admins - Similar to key_admins.\n" + "domain_domain_controllers - Group with all domain controllers.\n" + "sid_domain - SubAuthority numbers.\n"; + + ks_print_help (ctrl, data); +} + + +/* Helper function for substitute_vars. */ +static const char * +getval_for_filter (void *cookie, const char *name) +{ + ctrl_t ctrl = cookie; + const char *result = NULL; + + if (!strcmp (name, "sid_domain")) + { +#ifdef HAVE_W32_SYSTEM + PSID mysid; + static char *sidstr; + char *s, *s0; + int i; + + if (!sidstr) + { + mysid = w32_get_user_sid (); + if (!mysid) + { + gpg_err_set_errno (ENOENT); + goto leave; + } + + if (!ConvertSidToStringSid (mysid, &sidstr)) + { + gpg_err_set_errno (EINVAL); + goto leave; + } + /* Example for SIDSTR: + * S-1-5-21-3636969917-2569447256-918939550-1127 */ + for (s0=NULL,s=sidstr,i=0; (s=strchr (s, '-')); i++) + { + s++; + if (i == 3) + s0 = s; + else if (i==6) + { + s[-1] = 0; + break; + } + } + if (!s0) + { + log_error ("oops: invalid SID received from OS"); + gpg_err_set_errno (EINVAL); + LocalFree (sidstr); + goto leave; + } + sidstr = s0; /* (We never release SIDSTR thus no memmove.) */ + } + result = sidstr; +#else + gpg_err_set_errno (ENOSYS); + goto leave; +#endif + } + else if (!strcmp (name, "domain")) + result = basedn_from_rootdse (ctrl, NULL); + else if (!strcmp (name, "domain_admins")) + result = map_rid_to_dn (ctrl, "512"); + else if (!strcmp (name, "domain_users")) + result = map_rid_to_dn (ctrl, "513"); + else if (!strcmp (name, "domain_guests")) + result = map_rid_to_dn (ctrl, "514"); + else if (!strcmp (name, "domain_computers")) + result = map_rid_to_dn (ctrl, "515"); + else if (!strcmp (name, "domain_domain_controllers")) + result = map_rid_to_dn (ctrl, "516"); + else if (!strcmp (name, "cert_publishers")) + result = map_rid_to_dn (ctrl, "517"); + else if (!strcmp (name, "protected_users")) + result = map_rid_to_dn (ctrl, "525"); + else if (!strcmp (name, "key_admins")) + result = map_rid_to_dn (ctrl, "526"); + else if (!strcmp (name, "enterprise_key_admins")) + result = map_rid_to_dn (ctrl, "527"); + else + result = ""; /* Unknown variables are empty. */ + + leave: + return result; +} + + /* Print a help output for the schemata supported by this module. */ gpg_error_t @@ -196,7 +317,12 @@ ks_ldap_help (ctrl_t ctrl, parsed_uri_t uri) static struct ks_engine_ldap_local_s * ks_ldap_new_state (void) { - return xtrycalloc (1, sizeof(struct ks_engine_ldap_local_s)); + struct ks_engine_ldap_local_s *state; + + state = xtrycalloc (1, sizeof(struct ks_engine_ldap_local_s)); + if (state) + state->scope = LDAP_SCOPE_SUBTREE; + return state; } @@ -221,6 +347,7 @@ ks_ldap_clear_state (struct ks_engine_ldap_local_s *state) } state->serverinfo = 0; xfree (state->basedn); + state->scope = LDAP_SCOPE_SUBTREE; state->basedn = NULL; xfree (state->keyspec); state->keyspec = NULL; @@ -244,6 +371,45 @@ ks_ldap_free_state (struct ks_engine_ldap_local_s *state) } +/* Helper for ks_ldap_get and ks_ldap_query. On return first_mode and + * next_mode are set accordingly. */ +static gpg_error_t +ks_ldap_prepare_my_state (ctrl_t ctrl, unsigned int ks_get_flags, + int *first_mode, int *next_mode) +{ + *first_mode = *next_mode = 0; + + if ((ks_get_flags & KS_GET_FLAG_FIRST)) + { + if (ctrl->ks_get_state) + ks_ldap_clear_state (ctrl->ks_get_state); + else if (!(ctrl->ks_get_state = ks_ldap_new_state ())) + return gpg_error_from_syserror (); + *first_mode = 1; + } + + if ((ks_get_flags & KS_GET_FLAG_NEXT)) + { + if (!ctrl->ks_get_state || !ctrl->ks_get_state->ldap_conn + || !ctrl->ks_get_state->message) + { + log_error ("ks-ldap: --next requested but no state\n"); + return gpg_error (GPG_ERR_INV_STATE); + } + *next_mode = 1; + } + + /* Do not keep an old state around if not needed. */ + if (!(*first_mode || *next_mode)) + { + ks_ldap_free_state (ctrl->ks_get_state); + ctrl->ks_get_state = NULL; + } + + return 0; +} + + /* Convert a keyspec to a filter. Return an error if the keyspec is bad or is not supported. The filter is escaped and returned in @@ -443,7 +609,9 @@ interrogate_ldap_dn (LDAP *ldap_conn, const char *basedn_search, * * URI describes the server to connect to and various options * including whether to use TLS and the username and password (see - * ldap_parse_uri for a description of the various fields). + * ldap_parse_uri for a description of the various fields). Be + * default a PGP keyserver is assumed; if GENERIC is true a generic + * ldap conenction is instead established. * * Returns: The ldap connection handle in *LDAP_CONNP, R_BASEDN is set * to the base DN for the PGP key space, several flags will be stored @@ -456,7 +624,7 @@ interrogate_ldap_dn (LDAP *ldap_conn, const char *basedn_search, * If it is NULL, then the server does not appear to be an OpenPGP * keyserver. */ static gpg_error_t -my_ldap_connect (parsed_uri_t uri, LDAP **ldap_connp, +my_ldap_connect (parsed_uri_t uri, unsigned int generic, LDAP **ldap_connp, char **r_basedn, char **r_host, int *r_use_tls, unsigned int *r_serverinfo) { @@ -525,15 +693,15 @@ my_ldap_connect (parsed_uri_t uri, LDAP **ldap_connp, } if (opt.verbose) - log_info ("ldap connect to '%s:%d:%s:%s:%s:%s%s%s'\n", + log_info ("ldap connect to '%s:%d:%s:%s:%s:%s%s%s'%s\n", host, port, basedn_arg ? basedn_arg : "", bindname ? bindname : "", password ? "*****" : "", use_tls == 1? "starttls" : use_tls == 2? "ldaptls" : "plain", use_ntds ? ",ntds":"", - use_areconly? ",areconly":""); - + use_areconly? ",areconly":"", + generic? " (generic)":""); /* If the uri specifies a secure connection and we don't support TLS, then fail; don't silently revert to an insecure @@ -541,7 +709,7 @@ my_ldap_connect (parsed_uri_t uri, LDAP **ldap_connp, if (use_tls) { #ifndef HAVE_LDAP_START_TLS_S - log_error ("ldap: can't connect to the server: no TLS support."); + log_error ("ks-ldap: can't connect to the server: no TLS support."); err = GPG_ERR_LDAP_NOT_SUPPORTED; goto out; #endif @@ -613,6 +781,8 @@ my_ldap_connect (parsed_uri_t uri, LDAP **ldap_connp, { int ver = opt.ldaptimeout; + /* fixme: also use LDAP_OPT_SEND_TIMEOUT? */ + lerr = ldap_set_option (ldap_conn, LDAP_OPT_TIMELIMIT, &ver); if (lerr != LDAP_SUCCESS) { @@ -710,7 +880,21 @@ my_ldap_connect (parsed_uri_t uri, LDAP **ldap_connp, /* By default we don't bind as there is usually no need to. */ } - if (basedn_arg && *basedn_arg) + if (generic) + { + /* Generic use of this function for arbitrary LDAP servers. */ + *r_serverinfo |= SERVERINFO_GENERIC; + if (basedn_arg && *basedn_arg) + { + basedn = xtrystrdup (basedn_arg); + if (!basedn) + { + err = gpg_error_from_syserror (); + goto out; + } + } + } + else if (basedn_arg && *basedn_arg) { /* User specified base DN. In this case we know the server is a * real LDAP server. */ @@ -830,11 +1014,15 @@ my_ldap_connect (parsed_uri_t uri, LDAP **ldap_connp, if (!err && opt.debug) { log_debug ("ldap_conn: %p\n", ldap_conn); - log_debug ("server_type: %s\n", ((*r_serverinfo & SERVERINFO_REALLDAP) - ? "LDAP" : "PGP.com keyserver") ); + log_debug ("server_type: %s\n", + ((*r_serverinfo & SERVERINFO_GENERIC) + ? "Generic" : + (*r_serverinfo & SERVERINFO_REALLDAP) + ? "LDAP" : "PGP.com keyserver") ); log_debug ("basedn: %s\n", basedn); - log_debug ("pgpkeyattr: %s\n", - (*r_serverinfo & SERVERINFO_PGPKEYV2)? "pgpKeyV2":"pgpKey"); + if (!(*r_serverinfo & SERVERINFO_GENERIC)) + log_debug ("pgpkeyattr: %s\n", + (*r_serverinfo & SERVERINFO_PGPKEYV2)? "pgpKeyV2":"pgpKey"); } ldapserver_list_free (server); @@ -945,10 +1133,32 @@ extract_keys (estream_t output, } my_ldap_value_free (vals); + vals = ldap_get_values (ldap_conn, message, "modifyTimestamp"); + if (vals && vals[0]) + { + gnupg_isotime_t atime; + if (!rfc4517toisotime (atime, vals[0])) + es_fprintf (output, "chg:%s:\n", atime); + } + my_ldap_value_free (vals); + es_fprintf (output, "INFO %s END\n", certid); } +/* For now we do not support LDAP over Tor. */ +static gpg_error_t +no_ldap_due_to_tor (ctrl_t ctrl) +{ + gpg_error_t err = gpg_error (GPG_ERR_NOT_SUPPORTED); + const char *msg = _("LDAP access not possible due to Tor mode"); + + log_error ("%s", msg); + dirmngr_status_printf (ctrl, "NOTE", "no_ldap_due_to_tor %u %s", err, msg); + return err; +} + + /* Helper for ks_ldap_get. Returns 0 if a key was fetched and printed * to FP. The error code GPG_ERR_NO_DATA is returned if no key was * printed. Note that FP is updated by this function. */ @@ -1026,11 +1236,132 @@ return_one_keyblock (LDAP *ldap_conn, LDAPMessage *msg, unsigned int serverinfo, } -/* Helper for ks_ldap_get. Note that KEYSPEC is only used for - * diagnostics. */ +/* Helper for ks_ldap_query. Returns 0 if an attr was fetched and + * printed to FP. The error code GPG_ERR_NO_DATA is returned if no + * data was printed. Note that FP is updated by this function. */ +static gpg_error_t +return_all_attributes (LDAP *ld, LDAPMessage *msg, estream_t *fp) +{ + gpg_error_t err = 0; + BerElement *berctx = NULL; + char *attr = NULL; + const char *attrprefix; + struct berval **values = NULL; + int idx; + int any = 0; + const char *s; + const char *val; + size_t len; + char *mydn; + + mydn = ldap_get_dn (ld, msg); + if (!*fp) + { + *fp = es_fopenmem(0, "rw"); + if (!*fp) + { + err = gpg_error_from_syserror (); + goto leave; + } + } + + /* Always print the DN - note that by using only unbkown attributes + * it is pissible to list just the DNs with out addiional + * linefeeds. */ + es_fprintf (*fp, "Dn: %s\n", mydn? mydn : "[oops DN missing]"); + + for (npth_unprotect (), attr = ldap_first_attribute (ld, msg, &berctx), + npth_protect (); + attr; + npth_unprotect (), attr = ldap_next_attribute (ld, msg, berctx), + npth_protect ()) + { + npth_unprotect (); + values = ldap_get_values_len (ld, msg, attr); + npth_protect (); + + if (!values) + { + if (opt.verbose) + log_info ("attribute '%s' not found\n", attr); + ldap_memfree (attr); + attr = NULL; + continue; + } + + any = 1; + + if (opt.verbose > 1) + { + log_info ("found attribute '%s'\n", attr); + for (idx=0; values[idx]; idx++) + log_info (" length[%d]=%d\n", + idx, (int)values[0]->bv_len); + } + + if (!ascii_strcasecmp (attr, "Dn")) + attrprefix = "X-"; + else if (*attr == '#') + attrprefix = "X-hash-"; + else if (*attr == ' ') + attrprefix = "X-blank-"; + else + attrprefix = ""; + /* FIXME: We should remap all invalid chars in ATTR. */ + + for (idx=0; values[idx]; idx++) + { + es_fprintf (*fp, "%s%s: ", attrprefix, attr); + val = values[idx]->bv_val; + len = values[idx]->bv_len; + while (len && (s = memchr (val, '\n', len))) + { + s++; /* We als want to print the LF. */ + if (es_fwrite (val, s - val, 1, *fp) != 1) + goto fwrite_failed; + len -= (s-val); + val = s; + if (len && es_fwrite (" ", 1, 1, *fp) != 1) + goto fwrite_failed; + } + if (len && es_fwrite (val, len, 1, *fp) != 1) + goto fwrite_failed; + if (es_fwrite ("\n", 1, 1, *fp) != 1) /* Final LF. */ + goto fwrite_failed; + } + + ldap_value_free_len (values); + values = NULL; + ldap_memfree (attr); + attr = NULL; + } + + /* One final linefeed to prettify the output. */ + if (any && es_fwrite ("\n", 1, 1, *fp) != 1) + goto fwrite_failed; + + + leave: + if (values) + ldap_value_free_len (values); + ldap_memfree (attr); + if (mydn) + ldap_memfree (mydn); + ber_free (berctx, 0); + return err; + + fwrite_failed: + err = gpg_error_from_syserror (); + log_error ("error writing to stdout: %s\n", gpg_strerror (err)); + goto leave; +} + + +/* Helper for ks_ldap_get and ks_ldap_query. Note that KEYSPEC is + * only used for diagnostics. */ static gpg_error_t search_and_parse (ctrl_t ctrl, const char *keyspec, - LDAP *ldap_conn, char *basedn, char *filter, + LDAP *ldap_conn, char *basedn, int scope, char *filter, char **attrs, LDAPMessage **r_message) { gpg_error_t err = 0; @@ -1063,7 +1394,7 @@ search_and_parse (ctrl_t ctrl, const char *keyspec, } npth_unprotect (); - l_err = ldap_search_ext_s (ldap_conn, basedn, LDAP_SCOPE_SUBTREE, + l_err = ldap_search_ext_s (ldap_conn, basedn, scope, filter, attrs, 0, srvctrls[0]? srvctrls : NULL, NULL, NULL, 0, r_message); @@ -1128,7 +1459,7 @@ search_and_parse (ctrl_t ctrl, const char *keyspec, if (count < 1) { if (!ctrl->ks_get_state || ctrl->ks_get_state->pageno == 1) - log_info ("ks-ldap: key %s not found on keyserver\n", keyspec); + log_info ("ks-ldap: '%s' not found on LDAP server\n", keyspec); if (count == -1) err = ldap_to_gpg_err (ldap_conn); @@ -1148,20 +1479,149 @@ search_and_parse (ctrl_t ctrl, const char *keyspec, } +/* Fetch all entries from the RootDSE and return them as a name value + * object. */ +static nvc_t +fetch_rootdse (ctrl_t ctrl, parsed_uri_t uri) +{ + gpg_error_t err; + estream_t infp = NULL; + uri_item_t puri; /* The broken down URI (only one item used). */ + nvc_t nvc = NULL; + + /* FIXME: We need the unparsed URI here - use uri_item_t instead + * of fix the parser to fill in original */ + err = ks_action_parse_uri (uri && uri->original? uri->original : "ldap://", + &puri); + if (err) + return NULL; + + /* Reset authentication for a serverless. */ + puri->parsed_uri->ad_current = 0; + puri->parsed_uri->auth = NULL; + + if (!strcmp (puri->parsed_uri->scheme, "ldap") + || !strcmp (puri->parsed_uri->scheme, "ldaps") + || !strcmp (puri->parsed_uri->scheme, "ldapi") + || puri->parsed_uri->opaque) + { + err = ks_ldap_query (ctrl, puri->parsed_uri, KS_GET_FLAG_ROOTDSE, + "^&base&(objectclass=*)", NULL, NULL, &infp); + if (err) + log_error ("ldap: reading the rootDES failed: %s\n", + gpg_strerror (err)); + else if ((err = nvc_parse (&nvc, NULL, infp))) + log_error ("parsing the rootDES failed: %s\n", gpg_strerror (err)); + } + + es_fclose (infp); + release_uri_item_list (puri); + if (err) + { + nvc_release (nvc); + nvc = NULL; + } + return nvc; +} + + +/* Return the DN for the given RID. This is used with the Active + * Directory. */ +static char * +map_rid_to_dn (ctrl_t ctrl, const char *rid) +{ + gpg_error_t err; + char *result = NULL; + estream_t infp = NULL; + uri_item_t puri; /* The broken down URI. */ + nvc_t nvc = NULL; + char *filter = NULL; + const char *s; + char *attr[2] = {"dn", NULL}; + + err = ks_action_parse_uri ("ldap:///", &puri); + if (err) + return NULL; + + filter = strconcat ("(objectSid=S-1-5-21-$sid_domain-", rid, ")", NULL); + if (!filter) + goto leave; + + err = ks_ldap_query (ctrl, puri->parsed_uri, KS_GET_FLAG_SUBST, + filter, attr, NULL, &infp); + if (err) + { + log_error ("ldap: AD query '%s' failed: %s\n", filter,gpg_strerror (err)); + goto leave; + } + if ((err = nvc_parse (&nvc, NULL, infp))) + { + log_error ("ldap: parsing the result failed: %s\n",gpg_strerror (err)); + goto leave; + } + if (!(s = nvc_get_string (nvc, "Dn:"))) + { + err = gpg_error (GPG_ERR_NOT_FOUND); + log_error ("ldap: mapping rid '%s'failed: %s\n", rid, gpg_strerror (err)); + goto leave; + } + result = xtrystrdup (s); + if (!result) + { + err = gpg_error_from_syserror (); + log_error ("ldap: strdup failed: %s\n", gpg_strerror (err)); + goto leave; + } + + leave: + es_fclose (infp); + release_uri_item_list (puri); + xfree (filter); + nvc_release (nvc); + return result; +} + + +/* Return the baseDN for URI which might have already been cached for + * this session. */ +static char * +basedn_from_rootdse (ctrl_t ctrl, parsed_uri_t uri) +{ + const char *s; + + if (!ctrl->rootdse && !ctrl->rootdse_tried) + { + ctrl->rootdse = fetch_rootdse (ctrl, uri); + ctrl->rootdse_tried = 1; + if (ctrl->rootdse) + { + log_debug ("Dump of all rootDSE attributes:\n"); + nvc_write (ctrl->rootdse, log_get_stream ()); + log_debug ("End of dump\n"); + } + } + s = nvc_get_string (ctrl->rootdse, "defaultNamingContext:"); + return s? xtrystrdup (s): NULL; +} + + + + /* Get the key described key the KEYSPEC string from the keyserver * identified by URI. On success R_FP has an open stream to read the * data. KS_GET_FLAGS conveys flags from the client. */ gpg_error_t ks_ldap_get (ctrl_t ctrl, parsed_uri_t uri, const char *keyspec, - unsigned int ks_get_flags, estream_t *r_fp) + unsigned int ks_get_flags, gnupg_isotime_t newer, estream_t *r_fp) { - gpg_error_t err = 0; + gpg_error_t err; unsigned int serverinfo; char *host = NULL; int use_tls; char *filter = NULL; LDAP *ldap_conn = NULL; char *basedn = NULL; + int scope = LDAP_SCOPE_SUBTREE; estream_t fp = NULL; LDAPMessage *message = NULL; LDAPMessage *msg; @@ -1177,48 +1637,18 @@ ks_ldap_get (ctrl_t ctrl, parsed_uri_t uri, const char *keyspec, { "dummy", /* (to be be replaced.) */ "pgpcertid", "pgpuserid", "pgpkeyid", "pgprevoked", "pgpdisabled", - "pgpkeycreatetime", "modifytimestamp", "pgpkeysize", "pgpkeytype", + "pgpkeycreatetime", "modifyTimestamp", "pgpkeysize", "pgpkeytype", "gpgfingerprint", NULL }; - (void) ctrl; if (dirmngr_use_tor ()) - { - /* For now we do not support LDAP over Tor. */ - log_error (_("LDAP access not possible due to Tor mode\n")); - return gpg_error (GPG_ERR_NOT_SUPPORTED); - } - - /* Make sure we got a state. */ - if ((ks_get_flags & KS_GET_FLAG_FIRST)) - { - if (ctrl->ks_get_state) - ks_ldap_clear_state (ctrl->ks_get_state); - else if (!(ctrl->ks_get_state = ks_ldap_new_state ())) - return gpg_error_from_syserror (); - first_mode = 1; - } - - if ((ks_get_flags & KS_GET_FLAG_NEXT)) - { - if (!ctrl->ks_get_state || !ctrl->ks_get_state->ldap_conn - || !ctrl->ks_get_state->message) - { - log_error ("ks_ldap: --next requested but no state\n"); - return gpg_error (GPG_ERR_INV_STATE); - } - next_mode = 1; - } - - /* Do not keep an old state around if not needed. */ - if (!(first_mode || next_mode)) - { - ks_ldap_free_state (ctrl->ks_get_state); - ctrl->ks_get_state = NULL; - } + return no_ldap_due_to_tor (ctrl); + err = ks_ldap_prepare_my_state (ctrl, ks_get_flags, &first_mode, &next_mode); + if (err) + return err; if (next_mode) { @@ -1236,6 +1666,7 @@ ks_ldap_get (ctrl_t ctrl, parsed_uri_t uri, const char *keyspec, err = search_and_parse (ctrl, ctrl->ks_get_state->keyspec, ctrl->ks_get_state->ldap_conn, ctrl->ks_get_state->basedn, + ctrl->ks_get_state->scope, ctrl->ks_get_state->filter, attrs, &ctrl->ks_get_state->message); @@ -1284,7 +1715,7 @@ ks_ldap_get (ctrl_t ctrl, parsed_uri_t uri, const char *keyspec, else /* Not in --next mode. */ { /* Make sure we are talking to an OpenPGP LDAP server. */ - err = my_ldap_connect (uri, &ldap_conn, + err = my_ldap_connect (uri, 0, &ldap_conn, &basedn, &host, &use_tls, &serverinfo); if (err || !basedn) { @@ -1305,14 +1736,36 @@ ks_ldap_get (ctrl_t ctrl, parsed_uri_t uri, const char *keyspec, if (err) goto leave; + if (*newer) + { + char *tstr, *fstr; + + tstr = isotime2rfc4517 (newer); + if (!tstr) + { + err = gpg_error_from_syserror (); + goto leave; + } + fstr = strconcat ("(&", filter, + "(modifyTimestamp>=", tstr, "))", NULL); + xfree (tstr); + if (!fstr) + { + err = gpg_error_from_syserror (); + goto leave; + } + xfree (filter); + filter = fstr; + } + if (opt.debug) log_debug ("ks-ldap: using filter: %s\n", filter); /* Replace "dummy". */ attrs[0] = (serverinfo & SERVERINFO_PGPKEYV2)? "pgpKeyV2" : "pgpKey"; - err = search_and_parse (ctrl, keyspec, ldap_conn, basedn, filter, attrs, - &message); + err = search_and_parse (ctrl, keyspec, ldap_conn, basedn, scope, + filter, attrs, &message); if (err) goto leave; @@ -1363,6 +1816,7 @@ ks_ldap_get (ctrl_t ctrl, parsed_uri_t uri, const char *keyspec, ctrl->ks_get_state->message = message; message = NULL; ctrl->ks_get_state->serverinfo = serverinfo; + ctrl->ks_get_state->scope = scope; ctrl->ks_get_state->basedn = basedn; basedn = NULL; ctrl->ks_get_state->keyspec = keyspec? xtrystrdup (keyspec) : NULL; @@ -1418,14 +1872,10 @@ ks_ldap_search (ctrl_t ctrl, parsed_uri_t uri, const char *pattern, (void) ctrl; if (dirmngr_use_tor ()) - { - /* For now we do not support LDAP over Tor. */ - log_error (_("LDAP access not possible due to Tor mode\n")); - return gpg_error (GPG_ERR_NOT_SUPPORTED); - } + return no_ldap_due_to_tor (ctrl); /* Make sure we are talking to an OpenPGP LDAP server. */ - err = my_ldap_connect (uri, &ldap_conn, &basedn, NULL, NULL, &serverinfo); + err = my_ldap_connect (uri, 0, &ldap_conn, &basedn, NULL, NULL, &serverinfo); if (err || !basedn) { if (!err) @@ -1461,7 +1911,7 @@ ks_ldap_search (ctrl_t ctrl, parsed_uri_t uri, const char *pattern, char *attrs[] = { "pgpcertid", "pgpuserid", "pgprevoked", "pgpdisabled", - "pgpkeycreatetime", "pgpkeyexpiretime", "modifytimestamp", + "pgpkeycreatetime", "pgpkeyexpiretime", "modifyTimestamp", "pgpkeysize", "pgpkeytype", "gpgfingerprint", NULL }; @@ -1615,19 +2065,17 @@ ks_ldap_search (ctrl_t ctrl, parsed_uri_t uri, const char *pattern, } my_ldap_value_free (vals); -#if 0 - /* This is not yet specified in the keyserver - protocol, but may be someday. */ es_fputc (':', fp); - vals = ldap_get_values (ldap_conn, each, "modifytimestamp"); - if(vals && vals[0] strlen (vals[0]) == 15) + vals = ldap_get_values (ldap_conn, each, "modifyTimestamp"); + if(vals && vals[0]) { - es_fprintf (fp, "%u", - (unsigned int) ldap2epochtime (vals[0])); + gnupg_isotime_t atime; + if (rfc4517toisotime (atime, vals[0])) + *atime = 0; + es_fprintf (fp, "%s", atime); } my_ldap_value_free (vals); -#endif es_fprintf (fp, "\n"); @@ -2310,13 +2758,9 @@ ks_ldap_put (ctrl_t ctrl, parsed_uri_t uri, (void) ctrl; if (dirmngr_use_tor ()) - { - /* For now we do not support LDAP over Tor. */ - log_error (_("LDAP access not possible due to Tor mode\n")); - return gpg_error (GPG_ERR_NOT_SUPPORTED); - } + return no_ldap_due_to_tor (ctrl); - err = my_ldap_connect (uri, &ldap_conn, &basedn, NULL, NULL, &serverinfo); + err = my_ldap_connect (uri, 0, &ldap_conn, &basedn, NULL, NULL, &serverinfo); if (err || !basedn) { if (!err) @@ -2542,3 +2986,265 @@ ks_ldap_put (ctrl_t ctrl, parsed_uri_t uri, return err; } + + + +/* Get the data described by FILTER_ARG from URI. On success R_FP has + * an open stream to read the data. KS_GET_FLAGS conveys flags from + * the client. ATTRS is a NULL terminated list of attributes to + * return or NULL for all. */ +gpg_error_t +ks_ldap_query (ctrl_t ctrl, parsed_uri_t uri, unsigned int ks_get_flags, + const char *filter_arg, char **attrs, + gnupg_isotime_t newer, estream_t *r_fp) +{ + gpg_error_t err; + unsigned int serverinfo; + char *host = NULL; + int use_tls; + LDAP *ldap_conn = NULL; + char *basedn = NULL; + estream_t fp = NULL; + char *filter_arg_buffer = NULL; + char *filter = NULL; + int scope = LDAP_SCOPE_SUBTREE; + LDAPMessage *message = NULL; + LDAPMessage *msg; + int anydata = 0; + int first_mode = 0; + int next_mode = 0; + int get_first; + + if (dirmngr_use_tor ()) + return no_ldap_due_to_tor (ctrl); + + if ((!filter_arg || !*filter_arg) && (ks_get_flags & KS_GET_FLAG_ROOTDSE)) + filter_arg = "^&base&(objectclass=*)"; + + if ((ks_get_flags & KS_GET_FLAG_SUBST) + && filter_arg && strchr (filter_arg, '$')) + { + filter_arg_buffer = substitute_vars (filter_arg, getval_for_filter, ctrl); + if (!filter_arg_buffer) + { + err = gpg_error_from_syserror (); + log_error ("substituting filter variables failed: %s\n", + gpg_strerror (err)); + goto leave; + } + filter_arg = filter_arg_buffer; + } + + err = ks_ldap_prepare_my_state (ctrl, ks_get_flags, &first_mode, &next_mode); + if (err) + goto leave; + + if (!next_mode) /* (In --next mode the filter is ignored.) */ + { + if (!filter_arg || !*filter_arg) + { + err = gpg_error (GPG_ERR_LDAP_FILTER); + goto leave; + } + err = ldap_parse_extfilter (filter_arg, 0, &basedn, &scope, &filter); + if (err) + goto leave; + if (newer && *newer) + { + char *tstr, *fstr; + + tstr = isotime2rfc4517 (newer); + if (!tstr) + { + err = gpg_error_from_syserror (); + goto leave; + } + if (filter && *filter) + fstr = strconcat ("(&", filter, + "(modifyTimestamp>=", tstr, "))", NULL); + else + fstr = strconcat ("(modifyTimestamp>=", tstr, ")", NULL); + xfree (tstr); + if (!fstr) + { + err = gpg_error_from_syserror (); + goto leave; + } + xfree (filter); + filter = fstr; + } + } + + + if (next_mode) + { + next_again: + if (!ctrl->ks_get_state->msg_iter && ctrl->ks_get_state->more_pages) + { + /* Get the next page of results. */ + if (ctrl->ks_get_state->message) + { + ldap_msgfree (ctrl->ks_get_state->message); + ctrl->ks_get_state->message = NULL; + } + err = search_and_parse (ctrl, ctrl->ks_get_state->keyspec, + ctrl->ks_get_state->ldap_conn, + ctrl->ks_get_state->basedn, + ctrl->ks_get_state->scope, + ctrl->ks_get_state->filter, + attrs, + &ctrl->ks_get_state->message); + if (err) + goto leave; + ctrl->ks_get_state->msg_iter = ctrl->ks_get_state->message; + get_first = 1; + } + else + get_first = 0; + + while (ctrl->ks_get_state->msg_iter) + { + npth_unprotect (); + ctrl->ks_get_state->msg_iter + = get_first? ldap_first_entry (ctrl->ks_get_state->ldap_conn, + ctrl->ks_get_state->msg_iter) + /* */ : ldap_next_entry (ctrl->ks_get_state->ldap_conn, + ctrl->ks_get_state->msg_iter); + npth_protect (); + get_first = 0; + if (ctrl->ks_get_state->msg_iter) + { + err = return_all_attributes (ctrl->ks_get_state->ldap_conn, + ctrl->ks_get_state->msg_iter, + &fp); + if (!err) + break; /* Found. */ + else if (gpg_err_code (err) == GPG_ERR_NO_DATA) + err = 0; /* Skip empty attributes. */ + else + goto leave; + } + } + + if (!ctrl->ks_get_state->msg_iter || !fp) + { + ctrl->ks_get_state->msg_iter = NULL; + if (ctrl->ks_get_state->more_pages) + goto next_again; + err = gpg_error (GPG_ERR_NO_DATA); + } + + } + else /* Not in --next mode. */ + { + /* Connect to the LDAP server in generic mode. */ + char *tmpbasedn; + + err = my_ldap_connect (uri, 1 /*generic*/, &ldap_conn, + &tmpbasedn, &host, &use_tls, &serverinfo); + if (err) + goto leave; + if (basedn) + xfree (tmpbasedn); /* Extended syntax overrides. */ + else if (tmpbasedn) + basedn = tmpbasedn; + else if (!(ks_get_flags & KS_GET_FLAG_ROOTDSE)) + { + /* No BaseDN known - get one. */ + basedn = basedn_from_rootdse (ctrl, uri); + } + + if (opt.debug) + { + log_debug ("ks-ldap: using basedn: %s\n", basedn); + log_debug ("ks-ldap: using filter: %s\n", filter); + } + + err = search_and_parse (ctrl, filter, ldap_conn, basedn, scope, filter, + attrs, &message); + if (err) + goto leave; + + + for (npth_unprotect (), + msg = ldap_first_entry (ldap_conn, message), + npth_protect (); + msg; + npth_unprotect (), + msg = ldap_next_entry (ldap_conn, msg), + npth_protect ()) + { + err = return_all_attributes (ldap_conn, msg, &fp); + if (!err) + { + anydata = 1; + if (first_mode) + break; + } + else if (gpg_err_code (err) == GPG_ERR_NO_DATA) + err = 0; /* Skip empty/duplicate attributes. */ + else + goto leave; + } + + if (ctrl->ks_get_state) /* Save the iterator. */ + ctrl->ks_get_state->msg_iter = msg; + + if (!fp) /* Nothing was found. */ + err = gpg_error (GPG_ERR_NO_DATA); + + if (!err && anydata) + err = dirmngr_status_printf (ctrl, "SOURCE", "%s://%s", + use_tls? "ldaps" : "ldap", + host? host:""); + } + + + leave: + /* Store our state if needed. */ + if (!err && (ks_get_flags & KS_GET_FLAG_FIRST)) + { + log_assert (!ctrl->ks_get_state->ldap_conn); + ctrl->ks_get_state->ldap_conn = ldap_conn; + ldap_conn = NULL; + log_assert (!ctrl->ks_get_state->message); + ctrl->ks_get_state->message = message; + message = NULL; + ctrl->ks_get_state->serverinfo = serverinfo; + ctrl->ks_get_state->scope = scope; + ctrl->ks_get_state->basedn = basedn; + basedn = NULL; + ctrl->ks_get_state->keyspec = filter? xtrystrdup (filter) : NULL; + ctrl->ks_get_state->filter = filter; + filter = NULL; + } + if ((ks_get_flags & KS_GET_FLAG_NEXT)) + { + /* Keep the state in --next mode even with errors. */ + ldap_conn = NULL; + message = NULL; + } + + if (message) + ldap_msgfree (message); + + if (err) + es_fclose (fp); + else + { + if (fp) + es_fseek (fp, 0, SEEK_SET); + *r_fp = fp; + } + + xfree (basedn); + xfree (host); + + if (ldap_conn) + ldap_unbind (ldap_conn); + + xfree (filter); + xfree (filter_arg_buffer); + + return err; +} diff --git a/dirmngr/ks-engine.h b/dirmngr/ks-engine.h index be4e27e..6de77cc 100644 --- a/dirmngr/ks-engine.h +++ b/dirmngr/ks-engine.h @@ -27,6 +27,9 @@ #define KS_GET_FLAG_ONLY_LDAP 1 #define KS_GET_FLAG_FIRST 2 #define KS_GET_FLAG_NEXT 4 +#define KS_GET_FLAG_ONLY_AD 8 /* Do this only if we have an AD. */ +#define KS_GET_FLAG_ROOTDSE 16 /* Get the rootDSE. */ +#define KS_GET_FLAG_SUBST 32 /* Substiture variables. */ /*-- ks-action.c --*/ @@ -68,16 +71,21 @@ gpg_error_t ks_kdns_help (ctrl_t ctrl, parsed_uri_t uri); gpg_error_t ks_kdns_fetch (ctrl_t ctrl, parsed_uri_t uri, estream_t *r_fp); /*-- ks-engine-ldap.c --*/ +void ks_ldap_help_variables (ctrl_t ctrl); gpg_error_t ks_ldap_help (ctrl_t ctrl, parsed_uri_t uri); void ks_ldap_free_state (struct ks_engine_ldap_local_s *state); gpg_error_t ks_ldap_search (ctrl_t ctrl, parsed_uri_t uri, const char *pattern, estream_t *r_fp); gpg_error_t ks_ldap_get (ctrl_t ctrl, parsed_uri_t uri, const char *keyspec, unsigned int ks_get_flags, - estream_t *r_fp); + gnupg_isotime_t newer, estream_t *r_fp); gpg_error_t ks_ldap_put (ctrl_t ctrl, parsed_uri_t uri, void *data, size_t datalen, void *info, size_t infolen); +gpg_error_t ks_ldap_query (ctrl_t ctrl, parsed_uri_t uri, + unsigned int ks_get_flags, + const char *filter, char **attrs, + gnupg_isotime_t newer, estream_t *r_fp); #endif /*DIRMNGR_KS_ENGINE_H*/ diff --git a/dirmngr/ldap-misc.c b/dirmngr/ldap-misc.c index 42f9c7a..6e9e763 100644 --- a/dirmngr/ldap-misc.c +++ b/dirmngr/ldap-misc.c @@ -330,3 +330,91 @@ ldap_parse_extfilter (const char *string, int silent, } return err; } + + + +/* Scan an ISO timestamp and return a Generalized Time according to + * RFC-4517. The only supported format is "yyyymmddThhmmss[Z]" + * delimited by white space, nul, a colon or a comma. Returns a + * malloced string or NULL for an invalid string or on memory + * error. */ +char * +isotime2rfc4517 (const char *string) +{ + int year, month, day, hour, minu, sec; + + if (!isotime_p (string)) + { + errno = 0; + return NULL; + } + + year = atoi_4 (string); + month = atoi_2 (string + 4); + day = atoi_2 (string + 6); + hour = atoi_2 (string + 9); + minu = atoi_2 (string + 11); + sec = atoi_2 (string + 13); + + /* Basic checks (1600 due to the LDAP time format base) */ + if (year < 1600 || month < 1 || month > 12 || day < 1 || day > 31 + || hour > 23 || minu > 59 || sec > 61 ) + { + errno = 0; + return NULL; + } + + return gpgrt_bsprintf ("%04d%02d%02d%02d%02d%02d.0Z", + year, month, day, hour, minu, sec); +} + + +/* Parse an LDAP Generalized Time string and update the provided + * isotime buffer. On error return and error code. */ +gpg_error_t +rfc4517toisotime (gnupg_isotime_t timebuf, const char *string) +{ + int i; + int year, month, day, hour, minu, sec; + const char *s; + + /* Sample value: "20230823141623Z"; */ + for (i=0, s=string; i < 10; i++, s++) /* Need yyyymmddhh */ + if (!digitp (s)) + return gpg_error (GPG_ERR_INV_TIME); + year = atoi_4 (string); + month = atoi_2 (string + 4); + day = atoi_2 (string + 6); + hour = atoi_2 (string + 8); + minu = 0; + sec = 0; + if (digitp (s) && digitp (s+1)) + { + minu = atoi_2 (s); + s += 2; + if (digitp (s) && digitp (s+1)) + { + sec = atoi_2 (s); + s += 2; + } + } + if (*s == '.' || *s == ',') + { + s++; + if (!digitp (s)) /* At least one digit of the fraction required. */ + return gpg_error (GPG_ERR_INV_TIME); + s++; + while (digitp (s)) + s++; + } + if (*s == 'Z' && (!s[1] || spacep (s+1))) + ; /* stop here. */ + else if (*s == '-' || *s == '+') + return gpg_error (GPG_ERR_NOT_IMPLEMENTED); /* FIXME */ + else + return gpg_error (GPG_ERR_INV_TIME); + + snprintf (timebuf, sizeof (gnupg_isotime_t), "%04d%02d%02dT%02d%02d%02d", + year, month, day, hour, minu, sec); + return 0; +} diff --git a/dirmngr/ldap-misc.h b/dirmngr/ldap-misc.h index d555caf..03efe5f 100644 --- a/dirmngr/ldap-misc.h +++ b/dirmngr/ldap-misc.h @@ -38,6 +38,8 @@ gpg_err_code_t ldap_err_to_gpg_err (int code); gpg_err_code_t ldap_to_gpg_err (LDAP *ld); gpg_error_t ldap_parse_extfilter (const char *string, int silent, char **r_base, int *r_scope, char **r_filter); +char *isotime2rfc4517 (const char *string); +gpg_error_t rfc4517toisotime (gnupg_isotime_t timebuf, const char *string); #endif /*DIRMNGR_LDAP_MISC_H*/ diff --git a/dirmngr/ocsp.c b/dirmngr/ocsp.c index 3483ab9..5462449 100644 --- a/dirmngr/ocsp.c +++ b/dirmngr/ocsp.c @@ -224,7 +224,7 @@ do_ocsp_request (ctrl_t ctrl, ksba_ocsp_t ocsp, case 301: case 302: { - const char *s = http_get_header (http, "Location"); + const char *s = http_get_header (http, "Location", 0); log_info (_("URL '%s' redirected to '%s' (%u)\n"), url, s?s:"[none]", http_get_status_code (http)); @@ -406,32 +406,115 @@ validate_responder_cert (ctrl_t ctrl, ksba_cert_t cert, } -/* Helper for check_signature. */ -static int +/* Helper for check_signature. MD is the finalized hash context. */ +static gpg_error_t check_signature_core (ctrl_t ctrl, ksba_cert_t cert, gcry_sexp_t s_sig, - gcry_sexp_t s_hash, fingerprint_list_t signer_fpr_list) + gcry_md_hd_t md, fingerprint_list_t signer_fpr_list) { gpg_error_t err; - ksba_sexp_t pubkey; gcry_sexp_t s_pkey = NULL; + gcry_sexp_t s_hash = NULL; + const char *s; + int mdalgo, mdlen; + + /* Get the public key as a gcrypt s-expression. */ + { + ksba_sexp_t pk = ksba_cert_get_public_key (cert); + if (!pk) + err = gpg_error (GPG_ERR_INV_OBJ); + else + { + err = canon_sexp_to_gcry (pk, &s_pkey); + xfree (pk); + } + if (err) + goto leave; + } + + mdalgo = gcry_md_get_algo (md); + mdlen = gcry_md_get_algo_dlen (mdalgo); + + if (pk_algo_from_sexp (s_pkey) == GCRY_PK_ECC) + { + unsigned int qbits0, qbits; + + qbits0 = gcry_pk_get_nbits (s_pkey); + qbits = qbits0 == 521? 512 : qbits0; + + if ((qbits%8)) + { + log_error ("ECDSA requires the hash length to be a" + " multiple of 8 bits\n"); + err = gpg_error (GPG_ERR_INTERNAL); + goto leave; + } + + /* Don't allow any Q smaller than 160 bits. */ + if (qbits < 160) + { + log_error (_("%s key uses an unsafe (%u bit) hash\n"), + "ECDSA", qbits0); + err = gpg_error (GPG_ERR_INTERNAL); + goto leave; + } + + /* Check if we're too short. */ + if (mdlen < qbits/8) + { + log_error (_("a %u bit hash is not valid for a %u bit %s key\n"), + (unsigned int)mdlen*8, + qbits0, + "ECDSA"); + if (mdlen < 20) + { + err = gpg_error (GPG_ERR_INTERNAL); + goto leave; + } + } + + /* Truncate. */ + if (mdlen > qbits/8) + mdlen = qbits/8; + + err = gcry_sexp_build (&s_hash, NULL, "(data(flags raw)(value %b))", + (int)mdlen, gcry_md_read (md, mdalgo)); + } + else if (mdalgo && (s = gcry_md_algo_name (mdalgo)) && strlen (s) < 16) + { + /* Assume RSA */ + char hashalgostr[16+1]; + int i; - pubkey = ksba_cert_get_public_key (cert); - if (!pubkey) - err = gpg_error (GPG_ERR_INV_OBJ); + for (i=0; s[i]; i++) + hashalgostr[i] = ascii_tolower (s[i]); + hashalgostr[i] = 0; + err = gcry_sexp_build (&s_hash, NULL, "(data(flags pkcs1)(hash %s %b))", + hashalgostr, + (int)mdlen, + gcry_md_read (md, mdalgo)); + } else - err = canon_sexp_to_gcry (pubkey, &s_pkey); - xfree (pubkey); - if (!err) - err = gcry_pk_verify (s_sig, s_hash, s_pkey); - if (!err) - err = validate_responder_cert (ctrl, cert, signer_fpr_list); - if (!err) + err = gpg_error (GPG_ERR_DIGEST_ALGO); + if (err) + { + log_error (_("creating S-expression failed: %s\n"), gcry_strerror (err)); + goto leave; + } + + if (DBG_CRYPTO) { - gcry_sexp_release (s_pkey); - return 0; /* Successfully verified the signature. */ + gcry_log_debugsxp ("sig ", s_sig); + gcry_log_debugsxp ("hash", s_hash); } - /* We simply ignore all errors. */ + err = gcry_pk_verify (s_sig, s_hash, s_pkey); + if (err) + goto leave; + + err = validate_responder_cert (ctrl, cert, signer_fpr_list); + + leave: + gcry_sexp_release (s_hash); gcry_sexp_release (s_pkey); return err; } @@ -449,35 +532,11 @@ check_signature (ctrl_t ctrl, fingerprint_list_t signer_fpr_list) { gpg_error_t err; - int algo, cert_idx; - gcry_sexp_t s_hash; + int cert_idx; ksba_cert_t cert; - const char *s; /* Create a suitable S-expression with the hash value of our response. */ gcry_md_final (md); - algo = gcry_md_get_algo (md); - s = gcry_md_algo_name (algo); - if (algo && s && strlen (s) < 16) - { - char hashalgostr[16+1]; - int i; - - for (i=0; s[i]; i++) - hashalgostr[i] = ascii_tolower (s[i]); - hashalgostr[i] = 0; - err = gcry_sexp_build (&s_hash, NULL, "(data(flags pkcs1)(hash %s %b))", - hashalgostr, - (int)gcry_md_get_algo_dlen (algo), - gcry_md_read (md, algo)); - } - else - err = gpg_error (GPG_ERR_DIGEST_ALGO); - if (err) - { - log_error (_("creating S-expression failed: %s\n"), gcry_strerror (err)); - return err; - } /* Get rid of old OCSP specific certificate references. */ release_ctrl_ocsp_certs (ctrl); @@ -492,13 +551,12 @@ check_signature (ctrl_t ctrl, cert = get_cert_local (ctrl, signer_fpr_list->hexfpr); if (cert) { - err = check_signature_core (ctrl, cert, s_sig, s_hash, + err = check_signature_core (ctrl, cert, s_sig, md, signer_fpr_list); ksba_cert_release (cert); cert = NULL; if (!err) { - gcry_sexp_release (s_hash); return 0; /* Successfully verified the signature. */ } } @@ -558,14 +616,12 @@ check_signature (ctrl_t ctrl, if (cert) { - err = check_signature_core (ctrl, cert, s_sig, s_hash, - signer_fpr_list); + err = check_signature_core (ctrl, cert, s_sig, md, signer_fpr_list); ksba_cert_release (cert); if (!err) { ksba_free (name); ksba_free (keyid); - gcry_sexp_release (s_hash); return 0; /* Successfully verified the signature. */ } log_error ("responder certificate "); @@ -583,7 +639,6 @@ check_signature (ctrl_t ctrl, ksba_free (keyid); } - gcry_sexp_release (s_hash); log_error (_("no suitable certificate found to verify the OCSP response\n")); return gpg_error (GPG_ERR_NO_PUBKEY); } diff --git a/dirmngr/server.c b/dirmngr/server.c index 651f67c..bf4e891 100644 --- a/dirmngr/server.c +++ b/dirmngr/server.c @@ -32,6 +32,13 @@ #include <sys/stat.h> #include <unistd.h> #include <errno.h> +#ifdef HAVE_W32_SYSTEM +# ifndef WINVER +# define WINVER 0x0500 /* Same as in common/sysutils.c */ +# endif +# include <winsock2.h> +# include <sddl.h> +#endif #include "dirmngr.h" #include <assuan.h> @@ -146,7 +153,7 @@ get_ldapservers_from_ctrl (ctrl_t ctrl) } /* Release an uri_item_t list. */ -static void +void release_uri_item_list (uri_item_t list) { while (list) @@ -2124,13 +2131,6 @@ cmd_validate (assuan_context_t ctx, char *line) static gpg_error_t make_keyserver_item (const char *uri, uri_item_t *r_item) { - gpg_error_t err; - uri_item_t item; - const char *s; - char *tmpstr = NULL; - - *r_item = NULL; - /* We used to have DNS CNAME redirection from the URLs below to * sks-keyserver. pools. The idea was to allow for a quick way to * switch to a different set of pools. The problem with that @@ -2162,78 +2162,7 @@ make_keyserver_item (const char *uri, uri_item_t *r_item) else if (!strcmp (uri, "http://http-keys.gnupg.net")) uri = "hkp://pgp.surf.nl:80"; - item = xtrymalloc (sizeof *item + strlen (uri)); - if (!item) - return gpg_error_from_syserror (); - - item->next = NULL; - item->parsed_uri = NULL; - strcpy (item->uri, uri); - -#if USE_LDAP - if (!strncmp (uri, "ldap:", 5) && !(uri[5] == '/' && uri[6] == '/')) - { - /* Special ldap scheme given. This differs from a valid ldap - * scheme in that no double slash follows.. Use http_parse_uri - * to put it as opaque value into parsed_uri. */ - tmpstr = strconcat ("opaque:", uri+5, NULL); - if (!tmpstr) - err = gpg_error_from_syserror (); - else - err = http_parse_uri (&item->parsed_uri, tmpstr, 0); - } - else if ((s=strchr (uri, ':')) && !(s[1] == '/' && s[2] == '/')) - { - /* No valid scheme given. Use http_parse_uri to put the string - * as opaque value into parsed_uri. */ - tmpstr = strconcat ("opaque:", uri, NULL); - if (!tmpstr) - err = gpg_error_from_syserror (); - else - err = http_parse_uri (&item->parsed_uri, tmpstr, 0); - } - else if (ldap_uri_p (uri)) - { - int fixup = 0; - /* Fixme: We should get rid of that parser and repalce it with - * our generic (http) URI parser. */ - - /* If no port has been specified and the scheme ist ldaps we use - * our idea of the default port because the standard LDAP URL - * parser would use 636 here. This is because we redefined - * ldaps to mean starttls. */ -#ifdef HAVE_W32_SYSTEM - if (!strcmp (uri, "ldap:///")) - fixup = 1; - else -#endif - if (!http_parse_uri (&item->parsed_uri,uri,HTTP_PARSE_NO_SCHEME_CHECK)) - { - if (!item->parsed_uri->port - && !strcmp (item->parsed_uri->scheme, "ldaps")) - fixup = 2; - http_release_parsed_uri (item->parsed_uri); - item->parsed_uri = NULL; - } - - err = ldap_parse_uri (&item->parsed_uri, uri); - if (!err && fixup == 1) - item->parsed_uri->ad_current = 1; - else if (!err && fixup == 2) - item->parsed_uri->port = 389; - } - else -#endif /* USE_LDAP */ - { - err = http_parse_uri (&item->parsed_uri, uri, HTTP_PARSE_NO_SCHEME_CHECK); - } - - xfree (tmpstr); - if (err) - xfree (item); - else - *r_item = item; - return err; + return ks_action_parse_uri (uri, r_item); } @@ -2250,6 +2179,7 @@ ensure_keyserver (ctrl_t ctrl) uri_item_t plain_items = NULL; uri_item_t ui; strlist_t sl; + int none_seen = 1; if (ctrl->server_local->keyservers) return 0; /* Already set for this session. */ @@ -2262,6 +2192,15 @@ ensure_keyserver (ctrl_t ctrl) for (sl = opt.keyserver; sl; sl = sl->next) { + /* Frontends like Kleopatra may prefix option values without a + * scheme with "hkps://". Thus we need to check that too. + * Nobody will be mad enough to call a machine "none". */ + if (!strcmp (sl->d, "none") || !strcmp (sl->d, "hkp://none") + || !strcmp (sl->d, "hkps://none")) + { + none_seen = 1; + continue; + } err = make_keyserver_item (sl->d, &item); if (err) goto leave; @@ -2277,6 +2216,12 @@ ensure_keyserver (ctrl_t ctrl) } } + if (none_seen && !plain_items && !onion_items) + { + err = gpg_error (GPG_ERR_NO_KEYSERVER); + goto leave; + } + /* Decide which to use. Note that the session has no keyservers yet set. */ if (onion_items && !onion_items->next && plain_items && !plain_items->next) @@ -2347,8 +2292,7 @@ cmd_keyserver (assuan_context_t ctx, char *line) gpg_error_t err = 0; int clear_flag, add_flag, help_flag, host_flag, resolve_flag; int dead_flag, alive_flag; - uri_item_t item = NULL; /* gcc 4.4.5 is not able to detect that it - is always initialized. */ + uri_item_t item = NULL; clear_flag = has_option (line, "--clear"); help_flag = has_option (line, "--help"); @@ -2414,13 +2358,17 @@ cmd_keyserver (assuan_context_t ctx, char *line) if (add_flag) { - err = make_keyserver_item (line, &item); + if (!strcmp (line, "none") || !strcmp (line, "hkp://none") + || !strcmp (line, "hkps://none")) + err = 0; + else + err = make_keyserver_item (line, &item); if (err) goto leave; } if (clear_flag) release_ctrl_keyservers (ctrl); - if (add_flag) + if (add_flag && item) { item->next = ctrl->server_local->keyservers; ctrl->server_local->keyservers = item; @@ -2516,22 +2464,28 @@ cmd_ks_search (assuan_context_t ctx, char *line) static const char hlp_ks_get[] = - "KS_GET [--quick] [--ldap] [--first|--next] {<pattern>}\n" + "KS_GET [--quick] [--newer=TIME] [--ldap] [--first|--next] {<pattern>}\n" "\n" "Get the keys matching PATTERN from the configured OpenPGP keyservers\n" "(see command KEYSERVER). Each pattern should be a keyid, a fingerprint,\n" "or an exact name indicated by the '=' prefix. Option --quick uses a\n" "shorter timeout; --ldap will use only ldap servers. With --first only\n" - "the first item is returned; --next is used to return the next item"; + "the first item is returned; --next is used to return the next item\n" + "Option --newer works only with certain LDAP servers."; static gpg_error_t cmd_ks_get (assuan_context_t ctx, char *line) { ctrl_t ctrl = assuan_get_pointer (ctx); gpg_error_t err; - strlist_t list, sl; + strlist_t list = NULL; + strlist_t sl; + const char *s; char *p; estream_t outfp; unsigned int flags = 0; + gnupg_isotime_t opt_newer; + + *opt_newer = 0; if (has_option (line, "--quick")) ctrl->timeout = opt.connect_quick_timeout; @@ -2541,13 +2495,18 @@ cmd_ks_get (assuan_context_t ctx, char *line) flags |= KS_GET_FLAG_FIRST; if (has_option (line, "--next")) flags |= KS_GET_FLAG_NEXT; + if ((s = option_value (line, "--newer")) + && !string2isotime (opt_newer, s)) + { + err = set_error (GPG_ERR_SYNTAX, "invalid time format"); + goto leave; + } line = skip_options (line); /* Break the line into a strlist. Each pattern is by definition percent-plus escaped. However we only support keyids and fingerprints and thus the client has no need to apply the escaping. */ - list = NULL; for (p=line; *p; line = p) { while (*p && *p != ' ') @@ -2624,7 +2583,7 @@ cmd_ks_get (assuan_context_t ctx, char *line) ctrl->server_local->inhibit_data_logging_now = 0; ctrl->server_local->inhibit_data_logging_count = 0; err = ks_action_get (ctrl, ctrl->server_local->keyservers, - list, flags, outfp); + list, flags, opt_newer, outfp); es_fclose (outfp); ctrl->server_local->inhibit_data_logging = 0; } @@ -2744,6 +2703,116 @@ cmd_ks_put (assuan_context_t ctx, char *line) +static const char hlp_ad_query[] = + "AD_QUERY [--first|--next] [--] <filter> \n" + "\n" + "Query properties from a Windows Active Directory.\n" + "Options:\n" + "\n" + " --rootdse - Query the root using serverless binding,\n" + " --subst - Substitute variables in the filter\n" + " --attr=<attribs> - Comma delimited list of attributes\n" + " to return.\n" + " --help - List supported variables\n" + "\n" + "Extended filter syntax is allowed:\n" + " ^[<base>][&<scope>]&[<filter>]\n" + "Usual escaping rules apply. An ampersand in <base> must\n" + "doubled. <scope> may be \"base\", \"one\", or \"sub\"." + ; +static gpg_error_t +cmd_ad_query (assuan_context_t ctx, char *line) +{ + ctrl_t ctrl = assuan_get_pointer (ctx); + gpg_error_t err; + unsigned int flags = 0; + const char *filter; + estream_t outfp = NULL; + char *p; + char **opt_attr = NULL; + const char *s; + gnupg_isotime_t opt_newer; + int opt_help = 0; + + *opt_newer = 0; + + /* No options for now. */ + if (has_option (line, "--first")) + flags |= KS_GET_FLAG_FIRST; + if (has_option (line, "--next")) + flags |= KS_GET_FLAG_NEXT; + if (has_option (line, "--rootdse")) + flags |= KS_GET_FLAG_ROOTDSE; + if (has_option (line, "--subst")) + flags |= KS_GET_FLAG_SUBST; + if (has_option (line, "--help")) + opt_help = 1; + if ((s = option_value (line, "--newer")) + && !string2isotime (opt_newer, s)) + { + err = set_error (GPG_ERR_SYNTAX, "invalid time format"); + goto leave; + } + err = get_option_value (line, "--attr", &p); + if (err) + goto leave; + if (p) + { + opt_attr = strtokenize (p, ","); + if (!opt_attr) + { + err = gpg_error_from_syserror (); + xfree (p); + goto leave; + } + xfree (p); + } + line = skip_options (line); + filter = line; + + if (opt_help) + { +#if USE_LDAP + ks_ldap_help_variables (ctrl); +#endif + err = 0; + goto leave; + } + + if ((flags & KS_GET_FLAG_NEXT)) + { + if (*filter || (flags & ~KS_GET_FLAG_NEXT)) + { + err = PARM_ERROR ("No filter or other options allowed with --next"); + goto leave; + } + } + + /* Setup an output stream and perform the get. */ + outfp = es_fopencookie (ctx, "w", data_line_cookie_functions); + if (!outfp) + { + err = set_error (GPG_ERR_ASS_GENERAL, "error setting up a data stream"); + goto leave; + } + + ctrl->server_local->inhibit_data_logging = 1; + ctrl->server_local->inhibit_data_logging_now = 0; + ctrl->server_local->inhibit_data_logging_count = 0; + + err = ks_action_query (ctrl, + (flags & KS_GET_FLAG_ROOTDSE)? NULL : "ldap:///", + flags, filter, opt_attr, opt_newer, outfp); + + leave: + es_fclose (outfp); + xfree (opt_attr); + ctrl->server_local->inhibit_data_logging = 0; + return leave_cmd (ctx, err); +} + + + static const char hlp_loadswdb[] = "LOADSWDB [--force]\n" "\n" @@ -2854,14 +2923,39 @@ cmd_getinfo (assuan_context_t ctx, char *line) { const char *s = getenv (line); if (!s) - err = set_error (GPG_ERR_NOT_FOUND, "No such envvar"); - else - err = assuan_send_data (ctx, s, strlen (s)); + { + err = set_error (GPG_ERR_NOT_FOUND, "No such envvar"); + goto leave; + } + err = assuan_send_data (ctx, s, strlen (s)); + } + } +#ifdef HAVE_W32_SYSTEM + else if (!strcmp (line, "sid")) + { + PSID mysid; + char *sidstr; + + mysid = w32_get_user_sid (); + if (!mysid) + { + err = set_error (GPG_ERR_NOT_FOUND, "Error getting my SID"); + goto leave; + } + + if (!ConvertSidToStringSid (mysid, &sidstr)) + { + err = set_error (GPG_ERR_BUG, "Error converting SID to a string"); + goto leave; } + err = assuan_send_data (ctx, sidstr, strlen (sidstr)); + LocalFree (sidstr); } +#endif /*HAVE_W32_SYSTEM*/ else err = set_error (GPG_ERR_ASS_PARAMETER, "unknown value for WHAT"); + leave: return leave_cmd (ctx, err); } @@ -2941,6 +3035,7 @@ register_commands (assuan_context_t ctx) { "KS_GET", cmd_ks_get, hlp_ks_get }, { "KS_FETCH", cmd_ks_fetch, hlp_ks_fetch }, { "KS_PUT", cmd_ks_put, hlp_ks_put }, + { "AD_QUERY", cmd_ad_query, hlp_ad_query }, { "GETINFO", cmd_getinfo, hlp_getinfo }, { "LOADSWDB", cmd_loadswdb, hlp_loadswdb }, { "KILLDIRMNGR",cmd_killdirmngr,hlp_killdirmngr }, @@ -3135,8 +3230,10 @@ start_command_handler (assuan_fd_t fd, unsigned int session_id) ctrl->refcount); else { +#if USE_LDAP ks_ldap_free_state (ctrl->ks_get_state); ctrl->ks_get_state = NULL; +#endif release_ctrl_ocsp_certs (ctrl); xfree (ctrl->server_local); dirmngr_deinit_default_ctrl (ctrl); diff --git a/dirmngr/t-http.c b/dirmngr/t-http.c index 75874df..475f730 100644 --- a/dirmngr/t-http.c +++ b/dirmngr/t-http.c @@ -288,6 +288,11 @@ main (int argc, char **argv) my_http_flags |= HTTP_FLAG_FORCE_TOR; argc--; argv++; } + else if (!strcmp (*argv, "--try-proxy")) + { + my_http_flags |= HTTP_FLAG_TRY_PROXY; + argc--; argv++; + } else if (!strcmp (*argv, "--no-out")) { no_out = 1; @@ -457,7 +462,7 @@ main (int argc, char **argv) log_fatal ("http_get_header_names failed: %s\n", gpg_strerror (gpg_error_from_syserror ())); for (i = 0; names[i]; i++) - printf ("HDR: %s: %s\n", names[i], http_get_header (hd, names[i])); + printf ("HDR: %s: %s\n", names[i], http_get_header (hd, names[i], 0)); xfree (names); } fflush (stdout); @@ -483,7 +488,7 @@ main (int argc, char **argv) case 301: case 302: case 307: - log_info ("Redirected to: %s\n", http_get_header (hd, "Location")); + log_info ("Redirected to: %s\n", http_get_header (hd, "Location", 0)); break; } http_close (hd, 0); diff --git a/dirmngr/validate.c b/dirmngr/validate.c index 901c165..b3a1c14 100644 --- a/dirmngr/validate.c +++ b/dirmngr/validate.c @@ -231,7 +231,8 @@ allowed_ca (ksba_cert_t cert, int *chainlen) /* The German SigG Root CA's certificate does not flag itself as a CA; thus we relax this requirement if we trust a root CA. I think this is reasonable. Note, that - gpgsm implements a far stricter scheme here. */ + gpgsm implements a far stricter scheme here but also + features a "relax" flag in the trustlist.txt. */ if (chainlen) *chainlen = 3; /* That is what the SigG implements. */ if (opt.verbose) @@ -858,7 +859,7 @@ validate_cert_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t r_exptime, /* Return the public key algorithm id from the S-expression PKEY. FIXME: libgcrypt should provide such a function. Note that this implementation uses the names as used by libksba. */ -static int +int pk_algo_from_sexp (gcry_sexp_t pkey) { gcry_sexp_t l1, l2; @@ -879,6 +880,8 @@ pk_algo_from_sexp (gcry_sexp_t pkey) algo = GCRY_PK_RSA; else if (n==3 && !memcmp (name, "dsa", 3)) algo = GCRY_PK_DSA; + else if (n==3 && !memcmp (name, "ecc", 3)) + algo = GCRY_PK_ECC; else if (n==13 && !memcmp (name, "ambiguous-rsa", 13)) algo = GCRY_PK_RSA; else @@ -952,15 +955,24 @@ check_cert_sig (ksba_cert_t issuer_cert, ksba_cert_t cert) int digestlen; unsigned char *digest; int use_pss = 0; - unsigned int saltlen; + unsigned int saltlen; /* (use is controlled by use_pss) */ /* Hash the target certificate using the algorithm from that certificate. */ algoid = ksba_cert_get_digest_algo (cert); algo = gcry_md_map_name (algoid); if (!algo && algoid && !strcmp (algoid, "1.2.840.113549.1.1.10")) use_pss = 1; + else if (!algo && algoid && !strcmp (algoid, "1.2.840.10045.4.3.1")) + algo = GCRY_MD_SHA224; /* ecdsa-with-sha224 */ + else if (!algo && algoid && !strcmp (algoid, "1.2.840.10045.4.3.2")) + algo = GCRY_MD_SHA256; /* ecdsa-with-sha256 */ + else if (!algo && algoid && !strcmp (algoid, "1.2.840.10045.4.3.3")) + algo = GCRY_MD_SHA384; /* ecdsa-with-sha384 */ + else if (!algo && algoid && !strcmp (algoid, "1.2.840.10045.4.3.4")) + algo = GCRY_MD_SHA512; /* ecdsa-with-sha512 */ else if (!algo) { + log_debug ("XXXXX %s\n", __func__); log_error (_("unknown hash algorithm '%s'\n"), algoid? algoid:"?"); return gpg_error (GPG_ERR_GENERAL); } @@ -1106,19 +1118,48 @@ check_cert_sig (ksba_cert_t issuer_cert, ksba_cert_t cert) digest, saltlen); } - else if (pk_algo_from_sexp (s_pkey) == GCRY_PK_DSA) + else if (pk_algo_from_sexp (s_pkey) == GCRY_PK_ECC) { - /* NB.: We support only SHA-1 here because we had problems back - * then to get test data for DSA-2. Meanwhile DSA has been - * replaced by ECDSA which we do not yet support. */ - if (digestlen != 20) + unsigned int qbits0, qbits; + + qbits0 = gcry_pk_get_nbits (s_pkey); + qbits = qbits0 == 521? 512 : qbits0; + + if ((qbits%8)) + { + log_error ("ECDSA requires the hash length to be a" + " multiple of 8 bits\n"); + err = gpg_error (GPG_ERR_INTERNAL); + goto leave; + } + + /* Don't allow any Q smaller than 160 bits. */ + if (qbits < 160) + { + log_error (_("%s key uses an unsafe (%u bit) hash\n"), + "ECDSA", qbits0); + err = gpg_error (GPG_ERR_INTERNAL); + goto leave; + } + + /* Check if we're too short. */ + if (digestlen < qbits/8) { - log_error ("DSA requires the use of a 160 bit hash algorithm\n"); - gcry_md_close (md); - gcry_sexp_release (s_sig); - gcry_sexp_release (s_pkey); - return gpg_error (GPG_ERR_INTERNAL); + log_error (_("a %u bit hash is not valid for a %u bit %s key\n"), + (unsigned int)digestlen*8, + qbits0, + "ECDSA"); + if (digestlen < 20) + { + err = gpg_error (GPG_ERR_INTERNAL); + goto leave; + } } + + /* Truncate. */ + if (digestlen > qbits/8) + digestlen = qbits/8; + err = gcry_sexp_build (&s_hash, NULL, "(data(flags raw)(value %b))", (int)digestlen, digest); } @@ -1131,7 +1172,9 @@ check_cert_sig (ksba_cert_t issuer_cert, ksba_cert_t cert) if (!err) err = gcry_pk_verify (s_sig, s_hash, s_pkey); if (DBG_X509) - log_debug ("gcry_pk_verify: %s\n", gpg_strerror (err)); + log_debug ("%s: gcry_pk_verify: %s\n", __func__, gpg_strerror (err)); + + leave: gcry_md_close (md); gcry_sexp_release (s_sig); gcry_sexp_release (s_hash); diff --git a/dirmngr/validate.h b/dirmngr/validate.h index c7082e3..5b23cb4 100644 --- a/dirmngr/validate.h +++ b/dirmngr/validate.h @@ -48,6 +48,9 @@ #define VALIDATE_FLAG_NOCRLCHECK 1024 +/* Helper to get the public key algo from a public key. */ +int pk_algo_from_sexp (gcry_sexp_t pkey); + /* Validate the certificate CHAIN up to the trust anchor. Optionally return the closest expiration time in R_EXPTIME. */ gpg_error_t validate_cert_chain (ctrl_t ctrl, |