summaryrefslogtreecommitdiffstats
path: root/dirmngr
diff options
context:
space:
mode:
Diffstat (limited to 'dirmngr')
-rw-r--r--dirmngr/ChangeLog-20112
-rw-r--r--dirmngr/Makefile.am36
-rw-r--r--dirmngr/Makefile.in119
-rw-r--r--dirmngr/crlcache.c75
-rw-r--r--dirmngr/dirmngr-client-w32info.rc52
-rw-r--r--dirmngr/dirmngr-client.w32-manifest.in25
-rw-r--r--dirmngr/dirmngr-w32info.rc2
-rw-r--r--dirmngr/dirmngr.c48
-rw-r--r--dirmngr/dirmngr.h18
-rw-r--r--dirmngr/dirmngr.w32-manifest.in9
-rw-r--r--dirmngr/dirmngr_ldap-w32info.rc52
-rw-r--r--dirmngr/dirmngr_ldap.c2
-rw-r--r--dirmngr/dirmngr_ldap.w32-manifest.in25
-rw-r--r--dirmngr/dns.c2
-rw-r--r--dirmngr/http-common.h2
-rw-r--r--dirmngr/http.c1671
-rw-r--r--dirmngr/http.h4
-rw-r--r--dirmngr/ks-action.c151
-rw-r--r--dirmngr/ks-action.h7
-rw-r--r--dirmngr/ks-engine-hkp.c19
-rw-r--r--dirmngr/ks-engine-http.c14
-rw-r--r--dirmngr/ks-engine-ldap.c864
-rw-r--r--dirmngr/ks-engine.h10
-rw-r--r--dirmngr/ldap-misc.c88
-rw-r--r--dirmngr/ldap-misc.h2
-rw-r--r--dirmngr/ocsp.c153
-rw-r--r--dirmngr/server.c281
-rw-r--r--dirmngr/t-http.c9
-rw-r--r--dirmngr/validate.c71
-rw-r--r--dirmngr/validate.h3
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,