Page Menu
Home
GnuPG
Search
Configure Global Search
Log In
Files
F34329199
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Size
212 KB
Subscribers
None
View Options
diff --git a/common/Makefile.am b/common/Makefile.am
index 03bc5eb06..87d68208e 100644
--- a/common/Makefile.am
+++ b/common/Makefile.am
@@ -1,233 +1,234 @@
# Makefile for common gnupg modules
# Copyright (C) 2001, 2003, 2007, 2010 Free Software Foundation, Inc.
#
# This file is part of GnuPG.
#
# GnuPG is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# GnuPG is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, see <http://www.gnu.org/licenses/>.
## Process this file with automake to produce Makefile.in
EXTRA_DIST = mkstrtable.awk exaudit.awk exstatus.awk ChangeLog-2011 \
audit-events.h status-codes.h README.jnlib ChangeLog.jnlib \
ChangeLog-2011.include w32info-rc.h.in gnupg.ico tls-ca.pem
noinst_LIBRARIES = libcommon.a libcommonpth.a libgpgrl.a \
libcommontls.a libcommontlsnpth.a
if !HAVE_W32CE_SYSTEM
noinst_LIBRARIES += libsimple-pwquery.a
endif
noinst_PROGRAMS = $(jnlib_tests) $(module_tests) $(module_maint_tests)
TESTS = $(jnlib_tests) $(module_tests)
BUILT_SOURCES = audit-events.h status-codes.h
MAINTAINERCLEANFILES = audit-events.h status-codes.h
AM_CPPFLAGS = -I$(top_srcdir)/gl -I$(top_srcdir)/intl
AM_CFLAGS = $(LIBGCRYPT_CFLAGS) $(LIBASSUAN_CFLAGS) $(KSBA_CFLAGS)
include $(top_srcdir)/am/cmacros.am
jnlib_sources = \
libjnlib-config.h \
types.h host2net.h dynload.h w32help.h \
mapstrings.c stringhelp.c stringhelp.h \
strlist.c strlist.h \
utf8conv.c utf8conv.h \
argparse.c argparse.h \
logging.c logging.h \
dotlock.c dotlock.h \
mischelp.c mischelp.h
if HAVE_W32_SYSTEM
jnlib_sources += w32-reg.c w32-afunix.c w32-afunix.h
endif
common_sources = \
common-defs.h \
util.h i18n.c i18n.h \
status.c status.h\
shareddefs.h \
openpgpdefs.h \
gc-opt-flags.h \
keyserver.h \
sexp-parse.h \
tlv.c tlv.h \
init.c init.h \
sexputil.c \
sysutils.c sysutils.h \
homedir.c \
gettime.c gettime.h \
yesno.c \
b64enc.c b64dec.c zb32.c \
convert.c \
percent.c \
miscellaneous.c \
xasprintf.c \
xreadline.c \
membuf.c membuf.h \
iobuf.c iobuf.h \
ttyio.c ttyio.h \
asshelp.c asshelp2.c asshelp.h \
exechelp.h \
signal.c \
audit.c audit.h \
srv.h \
dns-cert.c dns-cert.h \
pka.c pka.h \
localename.c \
session-env.c session-env.h \
userids.c userids.h \
openpgp-oid.c \
ssh-utils.c ssh-utils.h \
agent-opt.c \
helpfile.c
# Sources possible requiring a TLS library are put into a separate
# conveince library.
tls_sources = \
http.c http.h
# To make the code easier to read we have split home some code into
# separate source files.
if HAVE_W32_SYSTEM
if HAVE_W32CE_SYSTEM
common_sources += exechelp-w32ce.c
else
common_sources += exechelp-w32.c
endif
else
common_sources += exechelp-posix.c
endif
# Sources only useful without NPTH.
without_npth_sources = \
get-passphrase.c get-passphrase.h
libcommon_a_SOURCES = $(jnlib_sources) $(common_sources) $(without_npth_sources)
if USE_DNS_SRV
libcommon_a_SOURCES += srv.c
endif
libcommon_a_CFLAGS = $(AM_CFLAGS) $(LIBASSUAN_CFLAGS) -DWITHOUT_NPTH=1
libcommonpth_a_SOURCES = $(jnlib_sources) $(common_sources)
if USE_DNS_SRV
libcommonpth_a_SOURCES += srv.c
endif
libcommonpth_a_CFLAGS = $(AM_CFLAGS) $(LIBASSUAN_CFLAGS) $(NPTH_CFLAGS)
libcommontls_a_SOURCES = $(tls_sources)
libcommontls_a_CFLAGS = $(AM_CFLAGS) $(LIBGNUTLS_CFLAGS) -DWITHOUT_NPTH=1
libcommontlsnpth_a_SOURCES = $(tls_sources)
libcommontlsnpth_a_CFLAGS = $(AM_CFLAGS) $(LIBGNUTLS_CFLAGS) $(NPTH_CFLAGS)
if !HAVE_W32CE_SYSTEM
libsimple_pwquery_a_SOURCES = \
simple-pwquery.c simple-pwquery.h asshelp.c asshelp.h
libsimple_pwquery_a_CFLAGS = $(AM_CFLAGS) $(LIBASSUAN_CFLAGS)
endif
libgpgrl_a_SOURCES = \
gpgrlhelp.c
if MAINTAINER_MODE
# Note: Due to the dependency on Makefile, the file will always be
# rebuilt, so we allow this only in maintainer mode.
# Create the audit-events.h include file from audit.h
# Note: We create the target file in the source directory because it
# is a distributed built source. If we would not do that we may end
# up with two files and then it is not clear which version of the
# files will be picked up.
audit-events.h: Makefile.am mkstrtable.awk exaudit.awk audit.h
$(AWK) -f $(srcdir)/exaudit.awk $(srcdir)/audit.h \
| $(AWK) -f $(srcdir)/mkstrtable.awk -v textidx=3 -v nogettext=1 \
-v namespace=eventstr_ > $(srcdir)/audit-events.h
# Create the status-codes.h include file from status.h
status-codes.h: Makefile.am mkstrtable.awk exstatus.awk status.h
$(AWK) -f $(srcdir)/exstatus.awk $(srcdir)/status.h \
| $(AWK) -f $(srcdir)/mkstrtable.awk -v textidx=3 -v nogettext=1 \
-v namespace=statusstr_ > $(srcdir)/status-codes.h
endif
#
# Module tests
#
t_jnlib_src = t-support.c t-support.h
jnlib_tests = t-stringhelp t-timestuff
if HAVE_W32_SYSTEM
jnlib_tests += t-w32-reg
endif
module_tests = t-convert t-percent t-gettime t-sysutils t-sexputil \
t-session-env t-openpgp-oid t-ssh-utils t-dns-cert \
t-mapstrings t-zb32
if !HAVE_W32CE_SYSTEM
module_tests += t-exechelp
endif
if MAINTAINER_MODE
module_maint_tests = t-helpfile t-b64 t-http
else
module_maint_tests =
endif
t_common_cflags = $(KSBA_CFLAGS) $(LIBGCRYPT_CFLAGS) \
$(LIBASSUAN_CFLAGS) $(GPG_ERROR_CFLAGS)
t_common_ldadd = libcommon.a ../gl/libgnu.a \
$(LIBGCRYPT_LIBS) $(LIBASSUAN_LIBS) $(GPG_ERROR_LIBS) \
$(LIBINTL) $(LIBICONV)
# jnlib tests
t_stringhelp_SOURCES = t-stringhelp.c $(t_jnlib_src)
t_stringhelp_LDADD = $(t_common_ldadd)
t_timestuff_SOURCES = t-timestuff.c $(t_jnlib_src)
t_timestuff_LDADD = $(t_common_ldadd)
if HAVE_W32_SYSTEM
t_w32_reg_SOURCES = t-w32-reg.c $(t_jnlib_src)
t_w32_reg_LDADD = $(t_common_ldadd)
endif
# common tests
t_convert_LDADD = $(t_common_ldadd)
t_percent_LDADD = $(t_common_ldadd)
t_gettime_LDADD = $(t_common_ldadd)
t_sysutils_LDADD = $(t_common_ldadd)
t_helpfile_LDADD = $(t_common_ldadd)
t_sexputil_LDADD = $(t_common_ldadd)
t_b64_LDADD = $(t_common_ldadd)
t_exechelp_LDADD = $(t_common_ldadd)
t_session_env_LDADD = $(t_common_ldadd)
t_openpgp_oid_LDADD = $(t_common_ldadd)
t_ssh_utils_LDADD = $(t_common_ldadd)
t_dns_cert_LDADD = $(t_common_ldadd) $(DNSLIBS)
t_mapstrings_LDADD = $(t_common_ldadd)
t_zb32_LDADD = $(t_common_ldadd)
# http tests
t_http_SOURCES = t-http.c
-t_http_CFLAGS = $(t_common_cflags) $(LIBGNUTLS_CFLAGS)
-t_http_LDADD = libcommontls.a $(t_common_ldadd) $(LIBGNUTLS_LIBS) $(DNSLIBS)
+t_http_CFLAGS = $(t_common_cflags) $(NTBTLS_CFLAGS) $(LIBGNUTLS_CFLAGS)
+t_http_LDADD = libcommontls.a $(t_common_ldadd) \
+ $(NTBTLS_LIBS) $(LIBGNUTLS_LIBS) $(DNSLIBS)
# All programs should depend on the created libs.
$(PROGRAMS) : libcommon.a libcommonpth.a libcommontls.a libcommontlsnpth.a
diff --git a/common/http.c b/common/http.c
index 7e3bb5717..413efd840 100644
--- a/common/http.c
+++ b/common/http.c
@@ -1,2596 +1,2662 @@
/* http.c - HTTP protocol handler
* Copyright (C) 1999, 2001, 2002, 2003, 2004, 2006, 2009, 2010,
* 2011 Free Software Foundation, Inc.
* Copyright (C) 2014 Werner Koch
*
* This file is part of GnuPG.
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of either
*
* - the GNU Lesser General Public License as published by the Free
* Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* or
*
* - the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* or both in parallel, as here.
*
* This file is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
/* Simple HTTP client implementation. We try to keep the code as
self-contained as possible. There are some contraints however:
- estream is required. We now require estream because it provides a
very useful and portable asprintf implementation and the fopencookie
function.
- stpcpy is required
- fixme: list other requirements.
- - With HTTP_USE_GNUTLS or HTTP_USE_POLARSSL support for https is
+ - With HTTP_USE_NTBTLS or HTTP_USE_GNUTLS support for https is
provided (this also requires estream).
- With HTTP_NO_WSASTARTUP the socket initialization is not done
under Windows. This is useful if the socket layer has already
been initialized elsewhere. This also avoids the installation of
an exit handler to cleanup the socket layer.
*/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <unistd.h>
#ifdef HAVE_W32_SYSTEM
# ifdef HAVE_WINSOCK2_H
# include <winsock2.h>
# endif
# include <windows.h>
#else /*!HAVE_W32_SYSTEM*/
# include <sys/types.h>
# include <sys/socket.h>
# include <sys/time.h>
# include <time.h>
# include <netinet/in.h>
# include <arpa/inet.h>
# include <netdb.h>
#endif /*!HAVE_W32_SYSTEM*/
#ifdef WITHOUT_NPTH /* Give the Makefile a chance to build without Pth. */
# undef USE_NPTH
#endif
#ifdef USE_NPTH
# include <npth.h>
#endif
-#if defined (HTTP_USE_GNUTLS) && defined (HTTP_USE_POLARSSL)
-# error Both, HTTP_USE_GNUTLS and HTTP_USE_POLARSSL, are defined.
+#if defined (HTTP_USE_GNUTLS) && defined (HTTP_USE_NTBTLS)
+# error Both, HTTP_USE_GNUTLS and HTTP_USE_NTBTLS, are defined.
#endif
-#ifdef HTTP_USE_GNUTLS
+#ifdef HTTP_USE_NTBTLS
+# include <ntbtls.h>
+#elif HTTP_USE_GNUTLS
# include <gnutls/gnutls.h>
# include <gnutls/x509.h>
#endif /*HTTP_USE_GNUTLS*/
-#ifdef HTTP_USE_POLARSSL
-# error Support for PolarSSL has not yet been added
-#endif
#include "util.h"
#include "i18n.h"
#include "http.h"
#ifdef USE_DNS_SRV
# include "srv.h"
#else /*!USE_DNS_SRV*/
/* If we are not compiling with SRV record support we provide stub
data structures. */
# ifndef MAXDNAME
# define MAXDNAME 1025
# endif
struct srventry
{
unsigned short priority;
unsigned short weight;
unsigned short port;
int run_count;
char target[MAXDNAME];
};
#endif/*!USE_DNS_SRV*/
#ifdef USE_NPTH
# define my_select(a,b,c,d,e) npth_select ((a), (b), (c), (d), (e))
# define my_connect(a,b,c) npth_connect ((a), (b), (c))
# define my_accept(a,b,c) npth_accept ((a), (b), (c))
#else
# define my_select(a,b,c,d,e) select ((a), (b), (c), (d), (e))
# define my_connect(a,b,c) connect ((a), (b), (c))
# define my_accept(a,b,c) accept ((a), (b), (c))
#endif
#ifdef HAVE_W32_SYSTEM
#define sock_close(a) closesocket(a)
#else
#define sock_close(a) close(a)
#endif
#ifndef EAGAIN
#define EAGAIN EWOULDBLOCK
#endif
#ifndef INADDR_NONE /* Slowaris is missing that. */
#define INADDR_NONE ((unsigned long)(-1))
#endif /*INADDR_NONE*/
#define HTTP_PROXY_ENV "http_proxy"
#define MAX_LINELEN 20000 /* Max. length of a HTTP header line. */
#define VALID_URI_CHARS "abcdefghijklmnopqrstuvwxyz" \
"ABCDEFGHIJKLMNOPQRSTUVWXYZ" \
"01234567890@" \
"!\"#$%&'()*+,-./:;<=>?[\\]^_{|}~"
/* A long counter type. */
#ifdef HAVE_STRTOULL
typedef unsigned long long longcounter_t;
# define counter_strtoul(a) strtoull ((a), NULL, 10)
#else
typedef unsigned long longcounter_t;
# define counter_strtoul(a) strtoul ((a), NULL, 10)
#endif
-#ifndef HTTP_USE_GNUTLS
-typedef void * gnutls_session_t;
+#if HTTP_USE_NTBTLS
+typedef ntbtls_t tls_session_t;
+# define USE_TLS 1
+#elif HTTP_USE_GNUTLS
+typedef gnutls_session_t tls_session_t;
+# define USE_TLS 1
+#else
+typedef void *tls_session_t;
+# undef USE_TLS
#endif
static gpg_err_code_t do_parse_uri (parsed_uri_t uri, int only_local_part,
int no_scheme_check, int force_tls);
static gpg_error_t parse_uri (parsed_uri_t *ret_uri, const char *uri,
int no_scheme_check, int force_tls);
static int remove_escapes (char *string);
static int insert_escapes (char *buffer, const char *string,
const char *special);
static uri_tuple_t parse_tuple (char *string);
static gpg_error_t send_request (http_t hd, const char *httphost,
const char *auth,const char *proxy,
const char *srvtag,strlist_t headers);
static char *build_rel_path (parsed_uri_t uri);
static gpg_error_t parse_response (http_t hd);
static int connect_server (const char *server, unsigned short port,
unsigned int flags, const char *srvtag,
int *r_host_not_found);
static gpg_error_t write_server (int sock, const char *data, size_t length);
static ssize_t cookie_read (void *cookie, void *buffer, size_t size);
static ssize_t cookie_write (void *cookie, const void *buffer, size_t size);
static int cookie_close (void *cookie);
/* A socket object used to a allow ref counting of sockets. */
struct my_socket_s
{
int fd; /* The actual socket - shall never be -1. */
int refcount; /* Number of references to this socket. */
};
typedef struct my_socket_s *my_socket_t;
/* Cookie function structure and cookie object. */
static es_cookie_io_functions_t cookie_functions =
{
cookie_read,
cookie_write,
NULL,
cookie_close
};
struct cookie_s
{
/* Socket object or NULL if already closed. */
my_socket_t sock;
/* The session object or NULL if not used. */
http_session_t session;
/* True if TLS is to be used. */
int use_tls;
/* The remaining content length and a flag telling whether to use
the content length. */
longcounter_t content_length;
unsigned int content_length_valid:1;
};
typedef struct cookie_s *cookie_t;
/* The session object. */
struct http_session_s
{
int refcount; /* Number of references to this object. */
#ifdef HTTP_USE_GNUTLS
gnutls_certificate_credentials_t certcred;
- gnutls_session_t tls_session;
+#endif /*HTTP_USE_GNUTLS*/
+#ifdef USE_TLS
+ tls_session_t tls_session;
struct {
int done; /* Verifciation has been done. */
- int rc; /* GnuTLS verification return code. */
+ int rc; /* TLS verification return code. */
unsigned int status; /* Verification status. */
} verify;
char *servername; /* Malloced server name. */
-#endif /*HTTP_USE_GNUTLS*/
+#endif /*USE_TLS*/
/* A callback function to log details of TLS certifciates. */
void (*cert_log_cb) (http_session_t, gpg_error_t, const char *,
const void **, size_t *);
};
/* An object to save header lines. */
struct header_s
{
struct header_s *next;
char *value; /* The value of the header (malloced). */
char name[1]; /* The name of the header (canonicalized). */
};
typedef struct header_s *header_t;
/* Our handle context. */
struct http_context_s
{
unsigned int status_code;
my_socket_t sock;
unsigned int in_data:1;
unsigned int is_http_0_9:1;
estream_t fp_read;
estream_t fp_write;
void *write_cookie;
void *read_cookie;
http_session_t session;
parsed_uri_t uri;
http_req_t req_type;
char *buffer; /* Line buffer. */
size_t buffer_size;
unsigned int flags;
header_t headers; /* Received headers. */
};
/* The global callback for the verification fucntion. */
static gpg_error_t (*tls_callback) (http_t, http_session_t, int);
/* The list of files with trusted CA certificates. */
static strlist_t tls_ca_certlist;
#if defined(HAVE_W32_SYSTEM) && !defined(HTTP_NO_WSASTARTUP)
#if GNUPG_MAJOR_VERSION == 1
#define REQ_WINSOCK_MAJOR 1
#define REQ_WINSOCK_MINOR 1
#else
#define REQ_WINSOCK_MAJOR 2
#define REQ_WINSOCK_MINOR 2
#endif
static void
deinit_sockets (void)
{
WSACleanup();
}
static void
init_sockets (void)
{
static int initialized;
static WSADATA wsdata;
if (initialized)
return;
if ( WSAStartup( MAKEWORD (REQ_WINSOCK_MINOR, REQ_WINSOCK_MAJOR), &wsdata ) )
{
log_error ("error initializing socket library: ec=%d\n",
(int)WSAGetLastError () );
return;
}
if ( LOBYTE(wsdata.wVersion) != REQ_WINSOCK_MAJOR
|| HIBYTE(wsdata.wVersion) != REQ_WINSOCK_MINOR )
{
log_error ("socket library version is %x.%x - but %d.%d needed\n",
LOBYTE(wsdata.wVersion), HIBYTE(wsdata.wVersion),
REQ_WINSOCK_MAJOR, REQ_WINSOCK_MINOR);
WSACleanup();
return;
}
atexit ( deinit_sockets );
initialized = 1;
}
#endif /*HAVE_W32_SYSTEM && !HTTP_NO_WSASTARTUP*/
/* Create a new socket object. Returns NULL and closes FD if not
enough memory is available. */
static my_socket_t
_my_socket_new (int lnr, int fd)
{
my_socket_t so;
so = xtrymalloc (sizeof *so);
if (!so)
{
int save_errno = errno;
sock_close (fd);
gpg_err_set_errno (save_errno);
return NULL;
}
so->fd = fd;
so->refcount = 1;
/* log_debug ("http.c:socket_new(%d): object %p for fd %d created\n", */
/* lnr, so, so->fd); */
(void)lnr;
return so;
}
#define my_socket_new(a) _my_socket_new (__LINE__, (a))
/* Bump up the reference counter for the socket object SO. */
static my_socket_t
_my_socket_ref (int lnr, my_socket_t so)
{
so->refcount++;
/* log_debug ("http.c:socket_ref(%d) object %p for fd %d refcount now %d\n", */
/* lnr, so, so->fd, so->refcount); */
(void)lnr;
return so;
}
#define my_socket_ref(a) _my_socket_ref (__LINE__,(a))
/* Bump down the reference counter for the socket object SO. If SO
has no more references, close the socket and release the
object. */
static void
_my_socket_unref (int lnr, my_socket_t so,
void (*preclose)(void*), void *preclosearg)
{
if (so)
{
so->refcount--;
/* log_debug ("http.c:socket_unref(%d): object %p for fd %d ref now %d\n", */
/* lnr, so, so->fd, so->refcount); */
(void)lnr;
if (!so->refcount)
{
if (preclose)
preclose (preclosearg);
sock_close (so->fd);
xfree (so);
}
}
}
#define my_socket_unref(a,b,c) _my_socket_unref (__LINE__,(a),(b),(c))
#if defined (USE_NPTH) && defined(HTTP_USE_GNUTLS)
static ssize_t
my_npth_read (gnutls_transport_ptr_t ptr, void *buffer, size_t size)
{
my_socket_t sock = ptr;
return npth_read (sock->fd, buffer, size);
}
static ssize_t
my_npth_write (gnutls_transport_ptr_t ptr, const void *buffer, size_t size)
{
my_socket_t sock = ptr;
return npth_write (sock->fd, buffer, size);
}
#endif /*USE_NPTH && HTTP_USE_GNUTLS*/
/* This notification function is called by estream whenever stream is
closed. Its purpose is to mark the closing in the handle so
that a http_close won't accidentally close the estream. The function
http_close removes this notification so that it won't be called if
http_close was used before an es_fclose. */
static void
fp_onclose_notification (estream_t stream, void *opaque)
{
http_t hd = opaque;
if (hd->fp_read && hd->fp_read == stream)
hd->fp_read = NULL;
else if (hd->fp_write && hd->fp_write == stream)
hd->fp_write = NULL;
}
/*
* Helper function to create an HTTP header with hex encoded data. A
* new buffer is returned. This buffer is the concatenation of the
* string PREFIX, the hex-encoded DATA of length LEN and the string
* SUFFIX. On error NULL is returned and ERRNO set.
*/
static char *
make_header_line (const char *prefix, const char *suffix,
const void *data, size_t len )
{
static unsigned char bintoasc[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789+/";
const unsigned char *s = data;
char *buffer, *p;
buffer = xtrymalloc (strlen (prefix) + (len+2)/3*4 + strlen (suffix) + 1);
if (!buffer)
return NULL;
p = stpcpy (buffer, prefix);
for ( ; len >= 3 ; len -= 3, s += 3 )
{
*p++ = bintoasc[(s[0] >> 2) & 077];
*p++ = bintoasc[(((s[0] <<4)&060)|((s[1] >> 4)&017))&077];
*p++ = bintoasc[(((s[1]<<2)&074)|((s[2]>>6)&03))&077];
*p++ = bintoasc[s[2]&077];
*p = 0;
}
if ( len == 2 )
{
*p++ = bintoasc[(s[0] >> 2) & 077];
*p++ = bintoasc[(((s[0] <<4)&060)|((s[1] >> 4)&017))&077];
*p++ = bintoasc[((s[1]<<2)&074)];
*p++ = '=';
}
else if ( len == 1 )
{
*p++ = bintoasc[(s[0] >> 2) & 077];
*p++ = bintoasc[(s[0] <<4)&060];
*p++ = '=';
*p++ = '=';
}
*p = 0;
strcpy (p, suffix);
return buffer;
}
/* Register a non-standard global TLS callback function. If no
verification is desired a callback needs to be registered which
always returns NULL. */
void
http_register_tls_callback (gpg_error_t (*cb)(http_t, http_session_t, int))
{
tls_callback = cb;
}
/* 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. */
void
http_register_tls_ca (const char *fname)
{
strlist_t sl;
if (!fname)
{
free_strlist (tls_ca_certlist);
tls_ca_certlist = NULL;
}
else
{
sl = add_to_strlist (&tls_ca_certlist, fname);
if (*sl->d && !strcmp (sl->d + strlen (sl->d) - 4, ".pem"))
sl->flags = 1;
}
}
/* Release a session. Take care not to release it while it is being
used by a http context object. */
static void
session_unref (int lnr, http_session_t sess)
{
if (!sess)
return;
sess->refcount--;
/* log_debug ("http.c:session_unref(%d): sess %p ref now %d\n", */
/* lnr, sess, sess->refcount); */
(void)lnr;
if (sess->refcount)
return;
-#ifdef HTTP_USE_GNUTLS
+#ifdef USE_TLS
+# ifdef HTTP_USE_GNUTLS
if (sess->tls_session)
{
my_socket_t sock = gnutls_transport_get_ptr (sess->tls_session);
my_socket_unref (sock, NULL, NULL);
gnutls_deinit (sess->tls_session);
}
if (sess->certcred)
gnutls_certificate_free_credentials (sess->certcred);
+# endif /*HTTP_USE_GNUTLS*/
xfree (sess->servername);
-#endif /*HTTP_USE_GNUTLS*/
+#endif /*USE_TLS*/
xfree (sess);
}
#define http_session_unref(a) session_unref (__LINE__, (a))
void
http_session_release (http_session_t sess)
{
http_session_unref (sess);
}
/* Create a new session object which is currently used to enable TLS
support. It may eventually allow reusing existing connections. */
gpg_error_t
http_session_new (http_session_t *r_session, const char *tls_priority)
{
gpg_error_t err;
http_session_t sess;
*r_session = NULL;
sess = xtrycalloc (1, sizeof *sess);
if (!sess)
return gpg_error_from_syserror ();
sess->refcount = 1;
-#ifdef HTTP_USE_GNUTLS
+#if HTTP_USE_NTBTLS
+ {
+ (void)tls_priority;
+
+ err = ntbtls_new (&sess->tls_session, NTBTLS_CLIENT);
+ if (err)
+ {
+ log_error ("ntbtls_new failed: %s\n", gpg_strerror (err));
+ goto leave;
+ }
+ }
+#elif HTTP_USE_GNUTLS
{
const char *errpos;
int rc;
strlist_t sl;
rc = gnutls_certificate_allocate_credentials (&sess->certcred);
if (rc < 0)
{
log_error ("gnutls_certificate_allocate_credentials failed: %s\n",
gnutls_strerror (rc));
err = gpg_error (GPG_ERR_GENERAL);
goto leave;
}
for (sl = tls_ca_certlist; sl; sl = sl->next)
{
rc = gnutls_certificate_set_x509_trust_file
(sess->certcred, sl->d,
(sl->flags & 1)? GNUTLS_X509_FMT_PEM : GNUTLS_X509_FMT_DER);
if (rc < 0)
log_info ("setting CA from file '%s' failed: %s\n",
sl->d, gnutls_strerror (rc));
}
rc = gnutls_init (&sess->tls_session, GNUTLS_CLIENT);
if (rc < 0)
{
log_error ("gnutls_init failed: %s\n", gnutls_strerror (rc));
err = gpg_error (GPG_ERR_GENERAL);
goto leave;
}
/* A new session has the transport ptr set to (void*(-1), we need
it to be NULL. */
gnutls_transport_set_ptr (sess->tls_session, NULL);
rc = gnutls_priority_set_direct (sess->tls_session,
tls_priority? tls_priority : "NORMAL",
&errpos);
if (rc < 0)
{
log_error ("gnutls_priority_set_direct failed at '%s': %s\n",
errpos, gnutls_strerror (rc));
err = gpg_error (GPG_ERR_GENERAL);
goto leave;
}
rc = gnutls_credentials_set (sess->tls_session,
GNUTLS_CRD_CERTIFICATE, sess->certcred);
if (rc < 0)
{
log_error ("gnutls_credentials_set failed: %s\n", gnutls_strerror (rc));
err = gpg_error (GPG_ERR_GENERAL);
goto leave;
}
}
-
#else /*!HTTP_USE_GNUTLS*/
- (void)tls_priority;
+ {
+ (void)tls_priority;
+ }
#endif /*!HTTP_USE_GNUTLS*/
/* log_debug ("http.c:session_new: sess %p created\n", sess); */
err = 0;
-#ifdef HTTP_USE_GNUTLS
+#if USE_TLS
leave:
-#endif /*HTTP_USE_GNUTLS*/
+#endif /*USE_TLS*/
if (err)
http_session_unref (sess);
else
*r_session = sess;
return err;
}
/* Increment the reference count for session SESS. Passing NULL for
SESS is allowed. */
http_session_t
http_session_ref (http_session_t sess)
{
if (sess)
{
sess->refcount++;
/* log_debug ("http.c:session_ref: sess %p ref now %d\n", sess, */
/* sess->refcount); */
}
return sess;
}
void
http_session_set_log_cb (http_session_t sess,
void (*cb)(http_session_t, gpg_error_t,
const char *hostname,
const void **certs, size_t *certlens))
{
sess->cert_log_cb = cb;
}
/* Start a HTTP retrieval and on success store at R_HD a context
pointer for completing the request and to wait for the response.
If HTTPHOST is not NULL it is used hor the Host header instead of a
Host header derived from the URL. */
gpg_error_t
http_open (http_t *r_hd, http_req_t reqtype, const char *url,
const char *httphost,
const char *auth, unsigned int flags, const char *proxy,
http_session_t session, const char *srvtag, strlist_t headers)
{
gpg_error_t err;
http_t hd;
*r_hd = NULL;
if (!(reqtype == HTTP_REQ_GET || reqtype == HTTP_REQ_POST))
return gpg_err_make (default_errsource, GPG_ERR_INV_ARG);
/* Create the handle. */
hd = xtrycalloc (1, sizeof *hd);
if (!hd)
return gpg_error_from_syserror ();
hd->req_type = reqtype;
hd->flags = flags;
hd->session = http_session_ref (session);
err = parse_uri (&hd->uri, url, 0, !!(flags & HTTP_FLAG_FORCE_TLS));
if (!err)
err = send_request (hd, httphost, auth, proxy, srvtag, headers);
if (err)
{
my_socket_unref (hd->sock, NULL, NULL);
if (hd->fp_read)
es_fclose (hd->fp_read);
if (hd->fp_write)
es_fclose (hd->fp_write);
http_session_unref (hd->session);
xfree (hd);
}
else
*r_hd = hd;
return err;
}
/* This function is useful to connect to a generic TCP service using
this http abstraction layer. This has the advantage of providing
service tags and an estream interface. */
gpg_error_t
http_raw_connect (http_t *r_hd, const char *server, unsigned short port,
unsigned int flags, const char *srvtag)
{
gpg_error_t err = 0;
int sock;
http_t hd;
cookie_t cookie;
int hnf;
*r_hd = NULL;
/* Create the handle. */
hd = xtrycalloc (1, sizeof *hd);
if (!hd)
return gpg_error_from_syserror ();
hd->req_type = HTTP_REQ_OPAQUE;
hd->flags = flags;
/* Connect. */
sock = connect_server (server, port, hd->flags, srvtag, &hnf);
if (sock == -1)
{
err = gpg_err_make (default_errsource,
(hnf? GPG_ERR_UNKNOWN_HOST
: gpg_err_code_from_syserror ()));
xfree (hd);
return err;
}
hd->sock = my_socket_new (sock);
if (!hd->sock)
{
err = gpg_err_make (default_errsource, gpg_err_code_from_syserror ());
xfree (hd);
return err;
}
/* 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. */
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. */
/* Register close notification to interlock the use of es_fclose in
http_close and in user code. */
err = es_onclose (hd->fp_write, 1, fp_onclose_notification, hd);
if (!err)
err = es_onclose (hd->fp_read, 1, fp_onclose_notification, hd);
leave:
if (err)
{
if (hd->fp_read)
es_fclose (hd->fp_read);
if (hd->fp_write)
es_fclose (hd->fp_write);
my_socket_unref (hd->sock, NULL, NULL);
xfree (hd);
}
else
*r_hd = hd;
return err;
}
void
http_start_data (http_t hd)
{
if (!hd->in_data)
{
es_fputs ("\r\n", hd->fp_write);
es_fflush (hd->fp_write);
hd->in_data = 1;
}
else
es_fflush (hd->fp_write);
}
gpg_error_t
http_wait_response (http_t hd)
{
gpg_error_t err;
cookie_t cookie;
/* Make sure that we are in the data. */
http_start_data (hd);
/* Close the write stream. Note that the reference counted socket
object keeps the actual system socket open. */
cookie = hd->write_cookie;
if (!cookie)
return gpg_err_make (default_errsource, GPG_ERR_INTERNAL);
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
key server didn't worked without it. */
if ((hd->flags & HTTP_FLAG_SHUTDOWN))
shutdown (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 = hd->uri->use_tls;
hd->read_cookie = cookie;
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);
http_session_unref (cookie->session);
xfree (cookie);
hd->read_cookie = NULL;
return err;
}
err = parse_response (hd);
if (!err)
err = es_onclose (hd->fp_read, 1, fp_onclose_notification, hd);
return err;
}
/* Convenience function to send a request and wait for the response.
Closes the handle on error. If PROXY is not NULL, this value will
be used as an HTTP proxy and any enabled $http_proxy gets
ignored. */
gpg_error_t
http_open_document (http_t *r_hd, const char *document,
const char *auth, unsigned int flags, const char *proxy,
http_session_t session,
const char *srvtag, strlist_t headers)
{
gpg_error_t err;
err = http_open (r_hd, HTTP_REQ_GET, document, NULL, auth, flags,
proxy, session, srvtag, headers);
if (err)
return err;
err = http_wait_response (*r_hd);
if (err)
http_close (*r_hd, 0);
return err;
}
void
http_close (http_t hd, int keep_read_stream)
{
if (!hd)
return;
/* First remove the close notifications for the streams. */
if (hd->fp_read)
es_onclose (hd->fp_read, 0, fp_onclose_notification, hd);
if (hd->fp_write)
es_onclose (hd->fp_write, 0, fp_onclose_notification, hd);
/* Now we can close the streams. */
my_socket_unref (hd->sock, NULL, NULL);
if (hd->fp_read && !keep_read_stream)
es_fclose (hd->fp_read);
if (hd->fp_write)
es_fclose (hd->fp_write);
http_session_unref (hd->session);
http_release_parsed_uri (hd->uri);
while (hd->headers)
{
header_t tmp = hd->headers->next;
xfree (hd->headers->value);
xfree (hd->headers);
hd->headers = tmp;
}
xfree (hd->buffer);
xfree (hd);
}
estream_t
http_get_read_ptr (http_t hd)
{
return hd?hd->fp_read:NULL;
}
estream_t
http_get_write_ptr (http_t hd)
{
return hd?hd->fp_write:NULL;
}
unsigned int
http_get_status_code (http_t hd)
{
return hd?hd->status_code:0;
}
/* Return information pertaining to TLS. If TLS is not in use for HD,
NULL is returned. WHAT is used ask for specific information:
(NULL) := Only check whether TLS is is use. Returns an
unspecified string if TLS is in use. That string may
even be the empty string.
*/
const char *
http_get_tls_info (http_t hd, const char *what)
{
(void)what;
if (!hd)
return NULL;
return hd->uri->use_tls? "":NULL;
}
static gpg_error_t
parse_uri (parsed_uri_t *ret_uri, const char *uri,
int no_scheme_check, int force_tls)
{
gpg_err_code_t ec;
*ret_uri = xtrycalloc (1, sizeof **ret_uri + strlen (uri));
if (!*ret_uri)
return gpg_err_make (default_errsource, gpg_err_code_from_syserror ());
strcpy ((*ret_uri)->buffer, uri);
ec = do_parse_uri (*ret_uri, 0, no_scheme_check, force_tls);
if (ec)
{
xfree (*ret_uri);
*ret_uri = NULL;
}
return gpg_err_make (default_errsource, ec);
}
/*
* Parse an URI and put the result into the newly allocated RET_URI.
* On success the caller must use release_parsed_uri() to releases the
* resources. If NO_SCHEME_CHECK is set, the function tries to parse
* the URL in the same way it would do for an HTTP style URI.
*/
gpg_error_t
http_parse_uri (parsed_uri_t *ret_uri, const char *uri,
int no_scheme_check)
{
return parse_uri (ret_uri, uri, no_scheme_check, 0);
}
void
http_release_parsed_uri (parsed_uri_t uri)
{
if (uri)
{
uri_tuple_t r, r2;
for (r = uri->query; r; r = r2)
{
r2 = r->next;
xfree (r);
}
xfree (uri);
}
}
static gpg_err_code_t
do_parse_uri (parsed_uri_t uri, int only_local_part,
int no_scheme_check, int force_tls)
{
uri_tuple_t *tail;
char *p, *p2, *p3, *pp;
int n;
p = uri->buffer;
n = strlen (uri->buffer);
/* Initialize all fields to an empty string or an empty list. */
uri->scheme = uri->host = uri->path = p + n;
uri->port = 0;
uri->params = uri->query = NULL;
uri->use_tls = 0;
uri->is_http = 0;
uri->opaque = 0;
uri->v6lit = 0;
/* A quick validity check. */
if (strspn (p, VALID_URI_CHARS) != n)
return GPG_ERR_BAD_URI; /* Invalid characters found. */
if (!only_local_part)
{
/* Find the scheme. */
if (!(p2 = strchr (p, ':')) || p2 == p)
return GPG_ERR_BAD_URI; /* No scheme. */
*p2++ = 0;
for (pp=p; *pp; pp++)
*pp = tolower (*(unsigned char*)pp);
uri->scheme = p;
if (!strcmp (uri->scheme, "http") && !force_tls)
{
uri->port = 80;
uri->is_http = 1;
}
else if (!strcmp (uri->scheme, "hkp") && !force_tls)
{
uri->port = 11371;
uri->is_http = 1;
}
-#ifdef HTTP_USE_GNUTLS
+#ifdef USE_TLS
else if (!strcmp (uri->scheme, "https") || !strcmp (uri->scheme,"hkps")
|| (force_tls && (!strcmp (uri->scheme, "http")
|| !strcmp (uri->scheme,"hkp"))))
{
uri->port = 443;
uri->is_http = 1;
uri->use_tls = 1;
}
-#endif
+#endif /*USE_TLS*/
else if (!no_scheme_check)
return GPG_ERR_INV_URI; /* Unsupported scheme */
p = p2;
if (*p == '/' && p[1] == '/' ) /* There seems to be a hostname. */
{
p += 2;
if ((p2 = strchr (p, '/')))
*p2++ = 0;
/* Check for username/password encoding */
if ((p3 = strchr (p, '@')))
{
uri->auth = p;
*p3++ = '\0';
p = p3;
}
for (pp=p; *pp; pp++)
*pp = tolower (*(unsigned char*)pp);
/* Handle an IPv6 literal */
if( *p == '[' && (p3=strchr( p, ']' )) )
{
*p3++ = '\0';
/* worst case, uri->host should have length 0, points to \0 */
uri->host = p + 1;
uri->v6lit = 1;
p = p3;
}
else
uri->host = p;
if ((p3 = strchr (p, ':')))
{
*p3++ = '\0';
uri->port = atoi (p3);
}
if ((n = remove_escapes (uri->host)) < 0)
return GPG_ERR_BAD_URI;
if (n != strlen (uri->host))
return GPG_ERR_BAD_URI; /* Hostname incudes a Nul. */
p = p2 ? p2 : NULL;
}
else if (uri->is_http)
return GPG_ERR_INV_URI; /* No Leading double slash for HTTP. */
else
{
uri->opaque = 1;
uri->path = p;
return 0;
}
} /* End global URI part. */
/* Parse the pathname part */
if (!p || !*p)
return 0; /* We don't have a path. Okay. */
/* TODO: Here we have to check params. */
/* Do we have a query part? */
if ((p2 = strchr (p, '?')))
*p2++ = 0;
uri->path = p;
if ((n = remove_escapes (p)) < 0)
return GPG_ERR_BAD_URI;
if (n != strlen (p))
return GPG_ERR_BAD_URI; /* Path includes a Nul. */
p = p2 ? p2 : NULL;
if (!p || !*p)
return 0; /* We don't have a query string. Okay. */
/* Now parse the query string. */
tail = &uri->query;
for (;;)
{
uri_tuple_t elem;
if ((p2 = strchr (p, '&')))
*p2++ = 0;
if (!(elem = parse_tuple (p)))
return GPG_ERR_BAD_URI;
*tail = elem;
tail = &elem->next;
if (!p2)
break; /* Ready. */
p = p2;
}
return 0;
}
/*
* Remove all %xx escapes; this is done in-place. Returns: New length
* of the string.
*/
static int
remove_escapes (char *string)
{
int n = 0;
unsigned char *p, *s;
for (p = s = (unsigned char*)string; *s; s++)
{
if (*s == '%')
{
if (s[1] && s[2] && isxdigit (s[1]) && isxdigit (s[2]))
{
s++;
*p = *s >= '0' && *s <= '9' ? *s - '0' :
*s >= 'A' && *s <= 'F' ? *s - 'A' + 10 : *s - 'a' + 10;
*p <<= 4;
s++;
*p |= *s >= '0' && *s <= '9' ? *s - '0' :
*s >= 'A' && *s <= 'F' ? *s - 'A' + 10 : *s - 'a' + 10;
p++;
n++;
}
else
{
*p++ = *s++;
if (*s)
*p++ = *s++;
if (*s)
*p++ = *s++;
if (*s)
*p = 0;
return -1; /* Bad URI. */
}
}
else
{
*p++ = *s;
n++;
}
}
*p = 0; /* Make sure to keep a string terminator. */
return n;
}
/* If SPECIAL is NULL this function escapes in forms mode. */
static size_t
escape_data (char *buffer, const void *data, size_t datalen,
const char *special)
{
int forms = !special;
const unsigned char *s;
size_t n = 0;
if (forms)
special = "%;?&=";
for (s = data; datalen; s++, datalen--)
{
if (forms && *s == ' ')
{
if (buffer)
*buffer++ = '+';
n++;
}
else if (forms && *s == '\n')
{
if (buffer)
memcpy (buffer, "%0D%0A", 6);
n += 6;
}
else if (forms && *s == '\r' && datalen > 1 && s[1] == '\n')
{
if (buffer)
memcpy (buffer, "%0D%0A", 6);
n += 6;
s++;
datalen--;
}
else if (strchr (VALID_URI_CHARS, *s) && !strchr (special, *s))
{
if (buffer)
*(unsigned char*)buffer++ = *s;
n++;
}
else
{
if (buffer)
{
snprintf (buffer, 4, "%%%02X", *s);
buffer += 3;
}
n += 3;
}
}
return n;
}
static int
insert_escapes (char *buffer, const char *string,
const char *special)
{
return escape_data (buffer, string, strlen (string), special);
}
/* Allocate a new string from STRING using standard HTTP escaping as
well as escaping of characters given in SPECIALS. A common pattern
for SPECIALS is "%;?&=". However it depends on the needs, for
example "+" and "/: often needs to be escaped too. Returns NULL on
failure and sets ERRNO. If SPECIAL is NULL a dedicated forms
encoding mode is used. */
char *
http_escape_string (const char *string, const char *specials)
{
int n;
char *buf;
n = insert_escapes (NULL, string, specials);
buf = xtrymalloc (n+1);
if (buf)
{
insert_escapes (buf, string, specials);
buf[n] = 0;
}
return buf;
}
/* Allocate a new string from {DATA,DATALEN} using standard HTTP
escaping as well as escaping of characters given in SPECIALS. A
common pattern for SPECIALS is "%;?&=". However it depends on the
needs, for example "+" and "/: often needs to be escaped too.
Returns NULL on failure and sets ERRNO. If SPECIAL is NULL a
dedicated forms encoding mode is used. */
char *
http_escape_data (const void *data, size_t datalen, const char *specials)
{
int n;
char *buf;
n = escape_data (NULL, data, datalen, specials);
buf = xtrymalloc (n+1);
if (buf)
{
escape_data (buf, data, datalen, specials);
buf[n] = 0;
}
return buf;
}
static uri_tuple_t
parse_tuple (char *string)
{
char *p = string;
char *p2;
int n;
uri_tuple_t tuple;
if ((p2 = strchr (p, '=')))
*p2++ = 0;
if ((n = remove_escapes (p)) < 0)
return NULL; /* Bad URI. */
if (n != strlen (p))
return NULL; /* Name with a Nul in it. */
tuple = xtrycalloc (1, sizeof *tuple);
if (!tuple)
return NULL; /* Out of core. */
tuple->name = p;
if (!p2) /* We have only the name, so we assume an empty value string. */
{
tuple->value = p + strlen (p);
tuple->valuelen = 0;
tuple->no_value = 1; /* Explicitly mark that we have seen no '='. */
}
else /* Name and value. */
{
if ((n = remove_escapes (p2)) < 0)
{
xfree (tuple);
return NULL; /* Bad URI. */
}
tuple->value = p2;
tuple->valuelen = n;
}
return tuple;
}
/*
* 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 *proxy, const char *srvtag, strlist_t headers)
{
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;
int sock;
int hnf;
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);
}
-#ifdef HTTP_USE_GNUTLS
+#ifdef USE_TLS
if (hd->uri->use_tls && !hd->session->tls_session)
{
log_error ("TLS requested but no GNUTLS context available\n");
return gpg_err_make (default_errsource, GPG_ERR_INTERNAL);
}
-#endif /*HTTP_USE_GNUTLS*/
+#endif /*USE_TLS*/
server = *hd->uri->host ? hd->uri->host : "localhost";
port = hd->uri->port ? hd->uri->port : 80;
/* Try to use SNI. */
-#ifdef HTTP_USE_GNUTLS
+#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);
if (!hd->session->servername)
{
err = gpg_err_make (default_errsource, gpg_err_code_from_syserror ());
return err;
}
+# if HTTP_USE_NTBTLS
+ err = ntbtls_set_hostname (hd->session->tls_session, server);
+ if (err)
+ {
+ log_info ("ntbtls_set_hostname failed: %s\n", gpg_strerror (err));
+ return err;
+ }
+# elif HTTP_USE_GNUTLS
rc = gnutls_server_name_set (hd->session->tls_session,
GNUTLS_NAME_DNS,
server, strlen (server));
if (rc < 0)
log_info ("gnutls_server_name_set failed: %s\n", gnutls_strerror (rc));
+# endif /*HTTP_USE_GNUTLS*/
}
-#endif /*HTTP_USE_GNUTLS*/
+#endif /*USE_TLS*/
if ( (proxy && *proxy)
|| ( (hd->flags & HTTP_FLAG_TRY_PROXY)
&& (http_proxy = getenv (HTTP_PROXY_ENV))
&& *http_proxy ))
{
parsed_uri_t uri;
int save_errno;
if (proxy)
http_proxy = proxy;
err = parse_uri (&uri, http_proxy, 0,
!!(hd->flags & HTTP_FLAG_FORCE_TLS));
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;
}
}
sock = connect_server (*uri->host ? uri->host : "localhost",
uri->port ? uri->port : 80,
hd->flags, srvtag, &hnf);
save_errno = errno;
http_release_parsed_uri (uri);
if (sock == -1)
gpg_err_set_errno (save_errno);
}
else
{
sock = connect_server (server, port, hd->flags, srvtag, &hnf);
}
if (sock == -1)
{
xfree (proxy_authstr);
return gpg_err_make (default_errsource,
(hnf? GPG_ERR_UNKNOWN_HOST
: gpg_err_code_from_syserror ()));
}
hd->sock = my_socket_new (sock);
if (!hd->sock)
{
xfree (proxy_authstr);
return gpg_err_make (default_errsource, gpg_err_code_from_syserror ());
}
-#ifdef HTTP_USE_GNUTLS
+#if HTTP_USE_NTBTLS
+ if (hd->uri->use_tls)
+ {
+ my_socket_ref (hd->sock);
+
+ while ((err = ntbtls_handshake (hd->session->tls_session)))
+ {
+ switch (err)
+ {
+ default:
+ log_info ("TLS handshake failed: %s <%s>\n",
+ gpg_strerror (err), gpg_strsource (err));
+ xfree (proxy_authstr);
+ return err;
+ }
+ }
+
+ hd->session->verify.done = 0;
+ if (tls_callback)
+ err = tls_callback (hd, hd->session, 0);
+ else
+ 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;
+ }
+ }
+#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);
#ifdef USE_NPTH
gnutls_transport_set_pull_function (hd->session->tls_session,
my_npth_read);
gnutls_transport_set_push_function (hd->session->tls_session,
my_npth_write);
#endif
do
{
rc = gnutls_handshake (hd->session->tls_session);
}
while (rc == GNUTLS_E_INTERRUPTED || rc == GNUTLS_E_AGAIN);
if (rc < 0)
{
log_info ("TLS handshake failed: %s\n", gnutls_strerror (rc));
xfree (proxy_authstr);
return gpg_err_make (default_errsource, GPG_ERR_NETWORK);
}
hd->session->verify.done = 0;
if (tls_callback)
err = tls_callback (hd, hd->session, 0);
else
err = http_verify_server_credentials (hd->session);
if (err)
{
log_info ("TLS connection authentication failed: %s\n",
gpg_strerror (err));
xfree (proxy_authstr);
return err;
}
}
#endif /*HTTP_USE_GNUTLS*/
if (auth || hd->uri->auth)
{
char *myauth;
if (auth)
{
myauth = xtrystrdup (auth);
if (!myauth)
{
xfree (proxy_authstr);
return gpg_err_make (default_errsource, gpg_err_code_from_syserror ());
}
remove_escapes (myauth);
}
else
{
remove_escapes (hd->uri->auth);
myauth = hd->uri->auth;
}
authstr = make_header_line ("Authorization: Basic ", "\r\n",
myauth, strlen (myauth));
if (auth)
xfree (myauth);
if (!authstr)
{
xfree (proxy_authstr);
return gpg_err_make (default_errsource,
gpg_err_code_from_syserror ());
}
}
p = build_rel_path (hd->uri);
if (!p)
return gpg_err_make (default_errsource, gpg_err_code_from_syserror ());
if (http_proxy && *http_proxy)
{
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 : "");
}
else
{
char portstr[35];
if (port == 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;
}
/* log_debug ("request:\n%s\nEND request\n", 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);
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 (!err)
{
for (;headers; headers=headers->next)
{
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;
}
}
}
}
leave:
es_free (request);
xfree (authstr);
xfree (proxy_authstr);
return err;
}
/*
* Build the relative path from the parsed URI. Minimal
* implementation. May return NULL in case of memory failure; errno
* is then set accordingly.
*/
static char *
build_rel_path (parsed_uri_t uri)
{
uri_tuple_t r;
char *rel_path, *p;
int n;
/* Count the needed space. */
n = insert_escapes (NULL, uri->path, "%;?&");
/* TODO: build params. */
for (r = uri->query; r; r = r->next)
{
n++; /* '?'/'&' */
n += insert_escapes (NULL, r->name, "%;?&=");
if (!r->no_value)
{
n++; /* '=' */
n += insert_escapes (NULL, r->value, "%;?&=");
}
}
n++;
/* Now allocate and copy. */
p = rel_path = xtrymalloc (n);
if (!p)
return NULL;
n = insert_escapes (p, uri->path, "%;?&");
p += n;
/* TODO: add params. */
for (r = uri->query; r; r = r->next)
{
*p++ = r == uri->query ? '?' : '&';
n = insert_escapes (p, r->name, "%;?&=");
p += n;
if (!r->no_value)
{
*p++ = '=';
/* TODO: Use valuelen. */
n = insert_escapes (p, r->value, "%;?&=");
p += n;
}
}
*p = 0;
return rel_path;
}
/* Transform a header name into a standard capitalized format; e.g.
"Content-Type". Conversion stops at the colon. As usual we don't
use the localized versions of ctype.h. */
static void
capitalize_header_name (char *name)
{
int first = 1;
for (; *name && *name != ':'; name++)
{
if (*name == '-')
first = 1;
else if (first)
{
if (*name >= 'a' && *name <= 'z')
*name = *name - 'a' + 'A';
first = 0;
}
else if (*name >= 'A' && *name <= 'Z')
*name = *name - 'A' + 'a';
}
}
/* Store an HTTP header line in LINE away. Line continuation is
supported as well as merging of headers with the same name. This
function may modify LINE. */
static gpg_err_code_t
store_header (http_t hd, char *line)
{
size_t n;
char *p, *value;
header_t h;
n = strlen (line);
if (n && line[n-1] == '\n')
{
line[--n] = 0;
if (n && line[n-1] == '\r')
line[--n] = 0;
}
if (!n) /* we are never called to hit this. */
return GPG_ERR_BUG;
if (*line == ' ' || *line == '\t')
{
/* Continuation. This won't happen too often as it is not
recommended. We use a straightforward implementaion. */
if (!hd->headers)
return GPG_ERR_PROTOCOL_VIOLATION;
n += strlen (hd->headers->value);
p = xtrymalloc (n+1);
if (!p)
return gpg_err_code_from_syserror ();
strcpy (stpcpy (p, hd->headers->value), line);
xfree (hd->headers->value);
hd->headers->value = p;
return 0;
}
capitalize_header_name (line);
p = strchr (line, ':');
if (!p)
return GPG_ERR_PROTOCOL_VIOLATION;
*p++ = 0;
while (*p == ' ' || *p == '\t')
p++;
value = p;
for (h=hd->headers; h; h = h->next)
if ( !strcmp (h->name, line) )
break;
if (h)
{
/* We have already seen a line with that name. Thus we assume
it is a comma separated list and merge them. */
p = xtrymalloc (strlen (h->value) + 1 + strlen (value)+ 1);
if (!p)
return gpg_err_code_from_syserror ();
strcpy (stpcpy (stpcpy (p, h->value), ","), value);
xfree (h->value);
h->value = p;
return 0;
}
/* Append a new header. */
h = xtrymalloc (sizeof *h + strlen (line));
if (!h)
return gpg_err_code_from_syserror ();
strcpy (h->name, line);
h->value = xtrymalloc (strlen (value)+1);
if (!h->value)
{
xfree (h);
return gpg_err_code_from_syserror ();
}
strcpy (h->value, value);
h->next = hd->headers;
hd->headers = h;
return 0;
}
/* 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. */
const char *
http_get_header (http_t hd, const char *name)
{
header_t h;
for (h=hd->headers; h; h = h->next)
if ( !strcmp (h->name, name) )
return h->value;
return NULL;
}
/* Return a newly allocated and NULL terminated array with pointers to
header names. The array must be released with xfree() and its
content is only values as long as no other request has been
send. */
const char **
http_get_header_names (http_t hd)
{
const char **array;
size_t n;
header_t h;
for (n=0, h = hd->headers; h; h = h->next)
n++;
array = xtrycalloc (n+1, sizeof *array);
if (array)
{
for (n=0, h = hd->headers; h; h = h->next)
array[n++] = h->name;
}
return array;
}
/*
* Parse the response from a server.
* Returns: Errorcode and sets some files in the handle
*/
static gpg_err_code_t
parse_response (http_t hd)
{
char *line, *p, *p2;
size_t maxlen, len;
cookie_t cookie = hd->read_cookie;
const char *s;
/* Delete old header lines. */
while (hd->headers)
{
header_t tmp = hd->headers->next;
xfree (hd->headers->value);
xfree (hd->headers);
hd->headers = tmp;
}
/* Wait for the status line. */
do
{
maxlen = MAX_LINELEN;
len = es_read_line (hd->fp_read, &hd->buffer, &hd->buffer_size, &maxlen);
line = hd->buffer;
if (!line)
return gpg_err_code_from_syserror (); /* Out of core. */
if (!maxlen)
return GPG_ERR_TRUNCATED; /* Line has been truncated. */
if (!len)
return GPG_ERR_EOF;
if ((hd->flags & HTTP_FLAG_LOG_RESP))
log_info ("RESP: '%.*s'\n",
(int)strlen(line)-(*line&&line[1]?2:0),line);
}
while (!*line);
if ((p = strchr (line, '/')))
*p++ = 0;
if (!p || strcmp (line, "HTTP"))
return 0; /* Assume http 0.9. */
if ((p2 = strpbrk (p, " \t")))
{
*p2++ = 0;
p2 += strspn (p2, " \t");
}
if (!p2)
return 0; /* Also assume http 0.9. */
p = p2;
/* TODO: Add HTTP version number check. */
if ((p2 = strpbrk (p, " \t")))
*p2++ = 0;
if (!isdigit ((unsigned int)p[0]) || !isdigit ((unsigned int)p[1])
|| !isdigit ((unsigned int)p[2]) || p[3])
{
/* Malformed HTTP status code - assume http 0.9. */
hd->is_http_0_9 = 1;
hd->status_code = 200;
return 0;
}
hd->status_code = atoi (p);
/* Skip all the header lines and wait for the empty line. */
do
{
maxlen = MAX_LINELEN;
len = es_read_line (hd->fp_read, &hd->buffer, &hd->buffer_size, &maxlen);
line = hd->buffer;
if (!line)
return gpg_err_code_from_syserror (); /* Out of core. */
/* Note, that we can silently ignore truncated lines. */
if (!len)
return GPG_ERR_EOF;
/* Trim line endings of empty lines. */
if ((*line == '\r' && line[1] == '\n') || *line == '\n')
*line = 0;
if ((hd->flags & HTTP_FLAG_LOG_RESP))
log_info ("RESP: '%.*s'\n",
(int)strlen(line)-(*line&&line[1]?2:0),line);
if (*line)
{
gpg_err_code_t ec = store_header (hd, line);
if (ec)
return ec;
}
}
while (len && *line);
cookie->content_length_valid = 0;
if (!(hd->flags & HTTP_FLAG_IGNORE_CL))
{
s = http_get_header (hd, "Content-Length");
if (s)
{
cookie->content_length_valid = 1;
cookie->content_length = counter_strtoul (s);
}
}
return 0;
}
#if 0
static int
start_server ()
{
struct sockaddr_in mya;
struct sockaddr_in peer;
int fd, client;
fd_set rfds;
int addrlen;
int i;
if ((fd = socket (AF_INET, SOCK_STREAM, 0)) == -1)
{
log_error ("socket() failed: %s\n", strerror (errno));
return -1;
}
i = 1;
if (setsockopt (fd, SOL_SOCKET, SO_REUSEADDR, (byte *) & i, sizeof (i)))
log_info ("setsockopt(SO_REUSEADDR) failed: %s\n", strerror (errno));
mya.sin_family = AF_INET;
memset (&mya.sin_addr, 0, sizeof (mya.sin_addr));
mya.sin_port = htons (11371);
if (bind (fd, (struct sockaddr *) &mya, sizeof (mya)))
{
log_error ("bind to port 11371 failed: %s\n", strerror (errno));
sock_close (fd);
return -1;
}
if (listen (fd, 5))
{
log_error ("listen failed: %s\n", strerror (errno));
sock_close (fd);
return -1;
}
for (;;)
{
FD_ZERO (&rfds);
FD_SET (fd, &rfds);
if (my_select (fd + 1, &rfds, NULL, NULL, NULL) <= 0)
continue; /* ignore any errors */
if (!FD_ISSET (fd, &rfds))
continue;
addrlen = sizeof peer;
client = my_accept (fd, (struct sockaddr *) &peer, &addrlen);
if (client == -1)
continue; /* oops */
log_info ("connect from %s\n", inet_ntoa (peer.sin_addr));
fflush (stdout);
fflush (stderr);
if (!fork ())
{
int c;
FILE *fp;
fp = fdopen (client, "r");
while ((c = getc (fp)) != EOF)
putchar (c);
fclose (fp);
exit (0);
}
sock_close (client);
}
return 0;
}
#endif
/* Actually connect to a server. Returns the file descriptor or -1 on
error. ERRNO is set on error. */
static int
connect_server (const char *server, unsigned short port,
unsigned int flags, const char *srvtag, int *r_host_not_found)
{
int sock = -1;
int srvcount = 0;
int hostfound = 0;
int srv, connected;
int last_errno = 0;
struct srventry *serverlist = NULL;
#ifdef HAVE_W32_SYSTEM
unsigned long inaddr;
#endif
*r_host_not_found = 0;
#ifdef HAVE_W32_SYSTEM
#ifndef HTTP_NO_WSASTARTUP
init_sockets ();
#endif
/* Win32 gethostbyname doesn't handle IP addresses internally, so we
try inet_addr first on that platform only. */
inaddr = inet_addr(server);
if ( inaddr != INADDR_NONE )
{
struct sockaddr_in addr;
memset(&addr,0,sizeof(addr));
sock = socket(AF_INET,SOCK_STREAM,0);
if ( sock==INVALID_SOCKET )
{
log_error("error creating socket: ec=%d\n",(int)WSAGetLastError());
return -1;
}
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
memcpy (&addr.sin_addr,&inaddr,sizeof(inaddr));
if (!my_connect (sock,(struct sockaddr *)&addr,sizeof(addr)) )
return sock;
sock_close(sock);
return -1;
}
#endif /*HAVE_W32_SYSTEM*/
#ifdef USE_DNS_SRV
/* Do the SRV thing */
if (srvtag)
{
/* We're using SRV, so append the tags. */
if (1+strlen (srvtag) + 6 + strlen (server) + 1 <= MAXDNAME)
{
char srvname[MAXDNAME];
stpcpy (stpcpy (stpcpy (stpcpy (srvname,"_"), srvtag),
"._tcp."), server);
srvcount = getsrv (srvname, &serverlist);
}
}
#else
(void)flags;
(void)srvtag;
#endif /*USE_DNS_SRV*/
if (!serverlist)
{
/* Either we're not using SRV, or the SRV lookup failed. Make
up a fake SRV record. */
serverlist = xtrycalloc (1, sizeof *serverlist);
if (!serverlist)
return -1; /* Out of core. */
serverlist->port = port;
strncpy (serverlist->target, server, MAXDNAME);
serverlist->target[MAXDNAME-1] = '\0';
srvcount = 1;
}
#ifdef HAVE_GETADDRINFO
connected = 0;
for (srv=0; srv < srvcount && !connected; srv++)
{
struct addrinfo hints, *res, *ai;
char portstr[35];
snprintf (portstr, sizeof portstr, "%hu", port);
memset (&hints, 0, sizeof (hints));
hints.ai_socktype = SOCK_STREAM;
if (getaddrinfo (serverlist[srv].target, portstr, &hints, &res))
continue; /* Not found - try next one. */
hostfound = 1;
for (ai = res; ai && !connected; ai = ai->ai_next)
{
if (ai->ai_family == AF_INET && (flags & HTTP_FLAG_IGNORE_IPv4))
continue;
if (ai->ai_family == AF_INET6 && (flags & HTTP_FLAG_IGNORE_IPv6))
continue;
if (sock != -1)
sock_close (sock);
sock = socket (ai->ai_family, ai->ai_socktype, ai->ai_protocol);
if (sock == -1)
{
int save_errno = errno;
log_error ("error creating socket: %s\n", strerror (errno));
freeaddrinfo (res);
xfree (serverlist);
errno = save_errno;
return -1;
}
if (my_connect (sock, ai->ai_addr, ai->ai_addrlen))
last_errno = errno;
else
connected = 1;
}
freeaddrinfo (res);
}
#else /* !HAVE_GETADDRINFO */
connected = 0;
for (srv=0; srv < srvcount && !connected; srv++)
{
int i;
struct hostent *host = NULL;
struct sockaddr_in addr;
/* Note: This code is not thread-safe. */
memset (&addr, 0, sizeof (addr));
host = gethostbyname (serverlist[srv].target);
if (!host)
continue;
hostfound = 1;
if (sock != -1)
sock_close (sock);
sock = socket (host->h_addrtype, SOCK_STREAM, 0);
if (sock == -1)
{
log_error ("error creating socket: %s\n", strerror (errno));
xfree (serverlist);
return -1;
}
addr.sin_family = host->h_addrtype;
if (addr.sin_family != AF_INET)
{
log_error ("unknown address family for '%s'\n",
serverlist[srv].target);
xfree (serverlist);
return -1;
}
addr.sin_port = htons (serverlist[srv].port);
if (host->h_length != 4)
{
log_error ("illegal address length for '%s'\n",
serverlist[srv].target);
xfree (serverlist);
return -1;
}
/* Try all A records until one responds. */
for (i = 0; host->h_addr_list[i] && !connected; i++)
{
memcpy (&addr.sin_addr, host->h_addr_list[i], host->h_length);
if (my_connect (sock, (struct sockaddr *) &addr, sizeof (addr)))
last_errno = errno;
else
{
connected = 1;
break;
}
}
}
#endif /* !HAVE_GETADDRINFO */
xfree (serverlist);
if (!connected)
{
#ifdef HAVE_W32_SYSTEM
log_error ("can't connect to '%s': %s%sec=%d\n",
server,
hostfound? "":"host not found",
hostfound? "":" - ", (int)WSAGetLastError());
#else
log_error ("can't connect to '%s': %s\n",
server,
hostfound? strerror (last_errno):"host not found");
#endif
if (!hostfound)
*r_host_not_found = 1;
if (sock != -1)
sock_close (sock);
gpg_err_set_errno (last_errno);
return -1;
}
return sock;
}
static gpg_error_t
write_server (int sock, const char *data, size_t length)
{
int nleft;
int nwritten;
nleft = length;
while (nleft > 0)
{
#if defined(HAVE_W32_SYSTEM) && !defined(USE_NPTH)
nwritten = send (sock, data, nleft, 0);
if ( nwritten == SOCKET_ERROR )
{
log_info ("network write failed: ec=%d\n", (int)WSAGetLastError ());
return gpg_error (GPG_ERR_NETWORK);
}
#else /*!HAVE_W32_SYSTEM || USE_NPTH*/
# ifdef USE_NPTH
nwritten = npth_write (sock, data, nleft);
# else
nwritten = write (sock, data, nleft);
# endif
if (nwritten == -1)
{
if (errno == EINTR)
continue;
if (errno == EAGAIN)
{
struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = 50000;
my_select (0, NULL, NULL, NULL, &tv);
continue;
}
log_info ("network write failed: %s\n", strerror (errno));
return gpg_error_from_syserror ();
}
#endif /*!HAVE_W32_SYSTEM || USE_NPTH*/
nleft -= nwritten;
data += nwritten;
}
return 0;
}
/* Read handler for estream. */
static ssize_t
cookie_read (void *cookie, void *buffer, size_t size)
{
cookie_t c = cookie;
int nread;
if (c->content_length_valid)
{
if (!c->content_length)
return 0; /* EOF */
if (c->content_length < size)
size = c->content_length;
}
#ifdef HTTP_USE_GNUTLS
if (c->use_tls && c->session && c->session->tls_session)
{
again:
nread = gnutls_record_recv (c->session->tls_session, buffer, size);
if (nread < 0)
{
if (nread == GNUTLS_E_INTERRUPTED)
goto again;
if (nread == GNUTLS_E_AGAIN)
{
struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = 50000;
my_select (0, NULL, NULL, NULL, &tv);
goto again;
}
if (nread == GNUTLS_E_REHANDSHAKE)
goto again; /* A client is allowed to just ignore this request. */
log_info ("TLS network read failed: %s\n", gnutls_strerror (nread));
gpg_err_set_errno (EIO);
return -1;
}
}
else
#endif /*HTTP_USE_GNUTLS*/
{
do
{
#ifdef USE_NPTH
nread = npth_read (c->sock->fd, buffer, size);
#elif defined(HAVE_W32_SYSTEM)
/* Under Windows we need to use recv for a socket. */
nread = recv (c->sock->fd, buffer, size, 0);
#else
nread = read (c->sock->fd, buffer, size);
#endif
}
while (nread == -1 && errno == EINTR);
}
if (c->content_length_valid && nread > 0)
{
if (nread < c->content_length)
c->content_length -= nread;
else
c->content_length = 0;
}
return nread;
}
/* Write handler for estream. */
static ssize_t
cookie_write (void *cookie, const void *buffer_arg, size_t size)
{
const char *buffer = buffer_arg;
cookie_t c = cookie;
int nwritten = 0;
#ifdef HTTP_USE_GNUTLS
if (c->use_tls && c->session && c->session->tls_session)
{
int nleft = size;
while (nleft > 0)
{
nwritten = gnutls_record_send (c->session->tls_session,
buffer, nleft);
if (nwritten <= 0)
{
if (nwritten == GNUTLS_E_INTERRUPTED)
continue;
if (nwritten == GNUTLS_E_AGAIN)
{
struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = 50000;
my_select (0, NULL, NULL, NULL, &tv);
continue;
}
log_info ("TLS network write failed: %s\n",
gnutls_strerror (nwritten));
gpg_err_set_errno (EIO);
return -1;
}
nleft -= nwritten;
buffer += nwritten;
}
}
else
#endif /*HTTP_USE_GNUTLS*/
{
if ( write_server (c->sock->fd, buffer, size) )
{
gpg_err_set_errno (EIO);
nwritten = -1;
}
else
nwritten = size;
}
return nwritten;
}
#ifdef HTTP_USE_GNUTLS
/* Wrapper for gnutls_bye used by my_socket_unref. */
static void
send_gnutls_bye (void *opaque)
{
- gnutls_session_t tls_session = opaque;
+ tls_session_t tls_session = opaque;
int ret;
again:
do
ret = gnutls_bye (tls_session, GNUTLS_SHUT_RDWR);
while (ret == GNUTLS_E_INTERRUPTED);
if (ret == GNUTLS_E_AGAIN)
{
struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = 50000;
my_select (0, NULL, NULL, NULL, &tv);
goto again;
}
}
#endif /*HTTP_USE_GNUTLS*/
/* Close handler for estream. */
static int
cookie_close (void *cookie)
{
cookie_t c = cookie;
if (!c)
return 0;
#ifdef HTTP_USE_GNUTLS
if (c->use_tls && c->session && c->session->tls_session)
my_socket_unref (c->sock, send_gnutls_bye, c->session->tls_session);
else
#endif /*HTTP_USE_GNUTLS*/
if (c->sock)
my_socket_unref (c->sock, NULL, NULL);
if (c->session)
http_session_unref (c->session);
xfree (c);
return 0;
}
/* Verify the credentials of the server. Returns 0 on success and
store the result in the session object. */
gpg_error_t
http_verify_server_credentials (http_session_t sess)
{
-#ifdef HTTP_USE_GNUTLS
+#if HTTP_USE_NTBTLS
+ (void)sess;
+ return 0; /* FIXME!! */
+#elif HTTP_USE_GNUTLS
static const char const errprefix[] = "TLS verification of peer failed";
int rc;
unsigned int status;
const char *hostname;
const gnutls_datum_t *certlist;
unsigned int certlistlen;
gnutls_x509_crt_t cert;
gpg_error_t err = 0;
sess->verify.done = 1;
sess->verify.status = 0;
sess->verify.rc = GNUTLS_E_CERTIFICATE_ERROR;
if (gnutls_certificate_type_get (sess->tls_session) != GNUTLS_CRT_X509)
{
log_error ("%s: %s\n", errprefix, "not an X.509 certificate");
sess->verify.rc = GNUTLS_E_UNSUPPORTED_CERTIFICATE_TYPE;
return gpg_error (GPG_ERR_GENERAL);
}
rc = gnutls_certificate_verify_peers2 (sess->tls_session, &status);
if (rc)
{
log_error ("%s: %s\n", errprefix, gnutls_strerror (rc));
if (!err)
err = gpg_error (GPG_ERR_GENERAL);
}
else if (status)
{
log_error ("%s: status=0x%04x\n", errprefix, status);
#if GNUTLS_VERSION_NUMBER >= 0x030104
{
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*/
sess->verify.status = status;
if (!err)
err = gpg_error (GPG_ERR_GENERAL);
}
hostname = sess->servername;
if (!hostname || !strchr (hostname, '.'))
{
log_error ("%s: %s\n", errprefix, "hostname missing");
if (!err)
err = gpg_error (GPG_ERR_GENERAL);
}
certlist = gnutls_certificate_get_peers (sess->tls_session, &certlistlen);
if (!certlistlen)
{
log_error ("%s: %s\n", errprefix, "server did not send a certificate");
if (!err)
err = gpg_error (GPG_ERR_GENERAL);
/* Need to stop here. */
if (err)
return err;
}
rc = gnutls_x509_crt_init (&cert);
if (rc < 0)
{
if (!err)
err = gpg_error (GPG_ERR_GENERAL);
if (err)
return err;
}
rc = gnutls_x509_crt_import (cert, &certlist[0], GNUTLS_X509_FMT_DER);
if (rc < 0)
{
log_error ("%s: %s: %s\n", errprefix, "error importing certificate",
gnutls_strerror (rc));
if (!err)
err = gpg_error (GPG_ERR_GENERAL);
}
if (!gnutls_x509_crt_check_hostname (cert, hostname))
{
log_error ("%s: %s\n", errprefix, "hostname does not match");
if (!err)
err = gpg_error (GPG_ERR_GENERAL);
}
gnutls_x509_crt_deinit (cert);
if (!err)
sess->verify.rc = 0;
if (sess->cert_log_cb)
{
const void *bufarr[10];
size_t buflenarr[10];
size_t n;
for (n = 0; n < certlistlen && n < DIM (bufarr)-1; n++)
{
bufarr[n] = certlist[n].data;
buflenarr[n] = certlist[n].size;
}
bufarr[n] = NULL;
buflenarr[n] = 0;
sess->cert_log_cb (sess, err, hostname, bufarr, buflenarr);
}
return err;
#else /*!HTTP_USE_GNUTLS*/
(void)sess;
return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
#endif
}
diff --git a/common/t-http.c b/common/t-http.c
index 9872f9a74..e031ef943 100644
--- a/common/t-http.c
+++ b/common/t-http.c
@@ -1,370 +1,381 @@
/* t-http.c
* Copyright (C) 1999, 2001, 2002, 2003, 2004, 2006, 2009, 2010,
* 2011 Free Software Foundation, Inc.
* Copyright (C) 2014 Werner Koch
*
* This file is part of GnuPG.
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of either
*
* - the GNU Lesser General Public License as published by the Free
* Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* or
*
* - the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* or both in parallel, as here.
*
* This file is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#include <config.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include "util.h"
#include "logging.h"
#include "http.h"
-#ifdef HTTP_USE_GNUTLS
+#if HTTP_USE_NTBTLS
+# include <ntbtls.h>
+#elif HTTP_USE_GNUTLS
# include <gnutls/gnutls.h> /* For init, logging, and deinit. */
#endif /*HTTP_USE_GNUTLS*/
#define PGM "t-http"
static int verbose;
static int debug;
static int no_verify;
/* static void */
/* read_dh_params (const char *fname) */
/* { */
/* gpg_error_t err; */
/* int rc; */
/* FILE *fp; */
/* struct stat st; */
/* char *buf; */
/* size_t buflen; */
/* gnutls_datum_t datum; */
/* fp = fopen (fname, "rb"); */
/* if (!fp) */
/* { */
/* err = gpg_error_from_syserror (); */
/* log_fatal ("can't open '%s': %s\n", fname, gpg_strerror (err)); */
/* } */
/* if (fstat (fileno(fp), &st)) */
/* { */
/* err = gpg_error_from_syserror (); */
/* log_fatal ("can't stat '%s': %s\n", fname, gpg_strerror (err)); */
/* } */
/* buflen = st.st_size; */
/* buf = xmalloc (buflen+1); */
/* if (fread (buf, buflen, 1, fp) != 1) */
/* { */
/* err = gpg_error_from_syserror (); */
/* log_fatal ("error reading '%s': %s\n", fname, gpg_strerror (err)); */
/* } */
/* fclose (fp); */
/* datum.size = buflen; */
/* datum.data = buf; */
/* rc = gnutls_dh_params_import_pkcs3 (dh_params, &datum, GNUTLS_X509_FMT_PEM); */
/* if (rc < 0) */
/* log_fatal ("gnutls_dh_param_import failed: %s\n", gnutls_strerror (rc)); */
/* xfree (buf); */
/* } */
+#if HTTP_USE_GNUTLS
static gpg_error_t
verify_callback (http_t hd, http_session_t session, int reserved)
{
(void)hd;
(void)reserved;
return no_verify? 0 : http_verify_server_credentials (session);
}
+#endif
-
+#if HTTP_USE_GNUTLS
static void
my_gnutls_log (int level, const char *text)
{
fprintf (stderr, "gnutls:L%d: %s", level, text);
}
-
+#endif
/* Prepend FNAME with the srcdir environment variable's value and
return an allocated filename. */
static char *
prepend_srcdir (const char *fname)
{
static const char *srcdir;
char *result;
if (!srcdir && !(srcdir = getenv ("srcdir")))
srcdir = ".";
result = xmalloc (strlen (srcdir) + 1 + strlen (fname) + 1);
strcpy (result, srcdir);
strcat (result, "/");
strcat (result, fname);
return result;
}
int
main (int argc, char **argv)
{
int last_argc = -1;
gpg_error_t err;
int rc;
parsed_uri_t uri;
uri_tuple_t r;
http_t hd;
int c;
unsigned int my_http_flags = 0;
int no_out = 0;
int tls_dbg = 0;
const char *cafile = NULL;
http_session_t session = NULL;
gpgrt_init ();
log_set_prefix (PGM, 1 | 4);
if (argc)
{ argc--; argv++; }
while (argc && last_argc != argc )
{
last_argc = argc;
if (!strcmp (*argv, "--"))
{
argc--; argv++;
break;
}
else if (!strcmp (*argv, "--help"))
{
fputs ("usage: " PGM " URL\n"
"Options:\n"
" --verbose print timings etc.\n"
" --debug flyswatter\n"
" --gnutls-debug N use GNUTLS debug level N\n"
" --cacert FNAME expect CA certificate in file FNAME\n"
" --no-verify do not verify the certificate\n"
" --force-tls use HTTP_FLAG_FORCE_TLS\n"
" --no-out do not print the content\n",
stdout);
exit (0);
}
else if (!strcmp (*argv, "--verbose"))
{
verbose++;
argc--; argv++;
}
else if (!strcmp (*argv, "--debug"))
{
verbose += 2;
debug++;
argc--; argv++;
}
else if (!strcmp (*argv, "--gnutls-debug"))
{
argc--; argv++;
if (argc)
{
tls_dbg = atoi (*argv);
argc--; argv++;
}
}
else if (!strcmp (*argv, "--cacert"))
{
argc--; argv++;
if (argc)
{
cafile = *argv;
argc--; argv++;
}
}
else if (!strcmp (*argv, "--no-verify"))
{
no_verify = 1;
argc--; argv++;
}
else if (!strcmp (*argv, "--force-tls"))
{
my_http_flags |= HTTP_FLAG_FORCE_TLS;
argc--; argv++;
}
else if (!strcmp (*argv, "--no-out"))
{
no_out = 1;
argc--; argv++;
}
else if (!strncmp (*argv, "--", 2))
{
fprintf (stderr, PGM ": unknown option '%s'\n", *argv);
exit (1);
}
}
if (argc != 1)
{
fprintf (stderr, PGM ": no or too many URLS given\n");
exit (1);
}
if (!cafile)
cafile = prepend_srcdir ("tls-ca.pem");
-#ifdef HTTP_USE_GNUTLS
+#if HTTP_USE_NTBTLS
+
+ (void)err;
+
+ ntbtls_set_debug (tls_dbg, NULL, NULL);
+
+#elif HTTP_USE_GNUTLS
+
rc = gnutls_global_init ();
if (rc)
log_error ("gnutls_global_init failed: %s\n", gnutls_strerror (rc));
http_register_tls_callback (verify_callback);
http_register_tls_ca (cafile);
err = http_session_new (&session, NULL);
if (err)
log_error ("http_session_new failed: %s\n", gpg_strerror (err));
/* rc = gnutls_dh_params_init(&dh_params); */
/* if (rc) */
/* log_error ("gnutls_dh_params_init failed: %s\n", gnutls_strerror (rc)); */
/* read_dh_params ("dh_param.pem"); */
/* rc = gnutls_certificate_set_x509_trust_file */
/* (certcred, "ca.pem", GNUTLS_X509_FMT_PEM); */
/* if (rc) */
/* log_error ("gnutls_certificate_set_x509_trust_file failed: %s\n", */
/* gnutls_strerror (rc)); */
/* gnutls_certificate_set_dh_params (certcred, dh_params); */
gnutls_global_set_log_function (my_gnutls_log);
if (tls_dbg)
gnutls_global_set_log_level (tls_dbg);
#endif /*HTTP_USE_GNUTLS*/
rc = http_parse_uri (&uri, *argv, 1);
if (rc)
{
log_error ("'%s': %s\n", *argv, gpg_strerror (rc));
return 1;
}
printf ("Scheme: %s\n", uri->scheme);
if (uri->opaque)
printf ("Value : %s\n", uri->path);
else
{
printf ("Auth : %s\n", uri->auth? uri->auth:"[none]");
printf ("Host : %s\n", uri->host);
printf ("Port : %u\n", uri->port);
printf ("Path : %s\n", uri->path);
for (r = uri->params; r; r = r->next)
{
printf ("Params: %s", r->name);
if (!r->no_value)
{
printf ("=%s", r->value);
if (strlen (r->value) != r->valuelen)
printf (" [real length=%d]", (int) r->valuelen);
}
putchar ('\n');
}
for (r = uri->query; r; r = r->next)
{
printf ("Query : %s", r->name);
if (!r->no_value)
{
printf ("=%s", r->value);
if (strlen (r->value) != r->valuelen)
printf (" [real length=%d]", (int) r->valuelen);
}
putchar ('\n');
}
printf ("TLS : %s\n",
uri->use_tls? "yes":
(my_http_flags&HTTP_FLAG_FORCE_TLS)? "forced" : "no");
}
fflush (stdout);
http_release_parsed_uri (uri);
uri = NULL;
rc = http_open_document (&hd, *argv, NULL, my_http_flags,
NULL, session, NULL, NULL);
if (rc)
{
log_error ("can't get '%s': %s\n", *argv, gpg_strerror (rc));
return 1;
}
log_info ("open_http_document succeeded; status=%u\n",
http_get_status_code (hd));
{
const char **names;
int i;
names = http_get_header_names (hd);
if (!names)
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]));
xfree (names);
}
fflush (stdout);
switch (http_get_status_code (hd))
{
case 200:
case 400:
case 401:
case 403:
case 404:
{
unsigned long count = 0;
while ((c = es_getc (http_get_read_ptr (hd))) != EOF)
{
count++;
if (!no_out)
putchar (c);
}
log_info ("Received bytes: %lu\n", count);
}
break;
case 301:
case 302:
case 307:
log_info ("Redirected to: %s\n", http_get_header (hd, "Location"));
break;
}
http_close (hd, 0);
http_session_release (session);
#ifdef HTTP_USE_GNUTLS
gnutls_global_deinit ();
#endif /*HTTP_USE_GNUTLS*/
return 0;
}
diff --git a/configure.ac b/configure.ac
index daca838af..46a0aade5 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,1808 +1,1826 @@
# configure.ac - for GnuPG 2.1
# Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007,
# 2008, 2009, 2010, 2011, 2012 Free Software Foundation, Inc.
# Copyright (C) 2013, 2014 Werner Koch
#
# This file is part of GnuPG.
#
# GnuPG is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# GnuPG is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, see <http://www.gnu.org/licenses/>.
# Process this file with autoconf to produce a configure script.
AC_PREREQ(2.61)
min_automake_version="1.10"
# To build a release you need to create a tag with the version number
# (git tag -s gnupg-2.n.m) and run "./autogen.sh --force". Please
# bump the version number immediately *after* the release and do
# another commit and push so that the git magic is able to work.
m4_define([mym4_package],[gnupg])
m4_define([mym4_major], [2])
m4_define([mym4_minor], [1])
m4_define([mym4_micro], [0])
# To start a new development series, i.e a new major or minor number
# you need to mark an arbitrary commit before the first beta release
# with an annotated tag. For example the 2.1 branch starts off with
# the tag "gnupg-2.1-base". This is used as the base for counting
# beta numbers before the first release of a series.
# Below is m4 magic to extract and compute the git revision number,
# the decimalized short revision number, a beta version string and a
# flag indicating a development version (mym4_isbeta). Note that the
# m4 processing is done by autoconf and not during the configure run.
m4_define([mym4_verslist], m4_split(m4_esyscmd([./autogen.sh --find-version] \
mym4_package mym4_major mym4_minor mym4_micro),[:]))
m4_define([mym4_isbeta], m4_argn(2, mym4_verslist))
m4_define([mym4_version], m4_argn(4, mym4_verslist))
m4_define([mym4_revision], m4_argn(7, mym4_verslist))
m4_define([mym4_revision_dec], m4_argn(8, mym4_verslist))
m4_esyscmd([echo ]mym4_version[>VERSION])
AC_INIT([mym4_package],[mym4_version], [http://bugs.gnupg.org])
NEED_GPG_ERROR_VERSION=1.15
NEED_LIBGCRYPT_API=1
NEED_LIBGCRYPT_VERSION=1.6.0
NEED_LIBASSUAN_API=2
NEED_LIBASSUAN_VERSION=2.1.0
NEED_KSBA_API=1
NEED_KSBA_VERSION=1.2.0
+NEED_NTBTLS_API=1
+NEED_NTBTLS_VERSION=0.1.0
+
NEED_NPTH_API=1
NEED_NPTH_VERSION=0.91
+
NEED_GNUTLS_VERSION=3.0
development_version=mym4_isbeta
PACKAGE=$PACKAGE_NAME
PACKAGE_GT=${PACKAGE_NAME}2
VERSION=$PACKAGE_VERSION
AC_CONFIG_AUX_DIR([build-aux])
AC_CONFIG_SRCDIR([sm/gpgsm.c])
AC_CONFIG_HEADER([config.h])
# Note: For automake 1.13 add the option
# serial-tests
AM_INIT_AUTOMAKE([dist-bzip2 no-dist-gzip])
AC_CANONICAL_HOST
AB_INIT
AC_GNU_SOURCE
# Some status variables.
have_gpg_error=no
have_libgcrypt=no
have_libassuan=no
have_ksba=no
+have_ntbtls=no
have_npth=no
have_libusb=no
have_adns=no
gnupg_have_ldap="n/a"
use_zip=yes
use_bzip2=yes
use_exec=yes
use_trust_models=yes
card_support=yes
use_ccid_driver=yes
use_standard_socket=yes
dirmngr_auto_start=yes
+use_tls_library=no
GNUPG_BUILD_PROGRAM(gpg, yes)
GNUPG_BUILD_PROGRAM(gpgsm, yes)
GNUPG_BUILD_PROGRAM(agent, yes)
GNUPG_BUILD_PROGRAM(scdaemon, yes)
GNUPG_BUILD_PROGRAM(g13, yes)
GNUPG_BUILD_PROGRAM(dirmngr, yes)
GNUPG_BUILD_PROGRAM(tools, yes)
GNUPG_BUILD_PROGRAM(doc, yes)
GNUPG_BUILD_PROGRAM(symcryptrun, no)
GNUPG_BUILD_PROGRAM(gpgtar, yes)
AC_SUBST(PACKAGE)
AC_SUBST(PACKAGE_GT)
AC_SUBST(VERSION)
AC_DEFINE_UNQUOTED(PACKAGE, "$PACKAGE", [Name of this package])
AC_DEFINE_UNQUOTED(PACKAGE_GT, "$PACKAGE_GT",
[Name of this package for gettext])
AC_DEFINE_UNQUOTED(VERSION, "$VERSION", [Version of this package])
AC_DEFINE_UNQUOTED(PACKAGE_BUGREPORT, "$PACKAGE_BUGREPORT",
[Bug report address])
AC_DEFINE_UNQUOTED(NEED_LIBGCRYPT_VERSION, "$NEED_LIBGCRYPT_VERSION",
[Required version of Libgcrypt])
AC_DEFINE_UNQUOTED(NEED_KSBA_VERSION, "$NEED_KSBA_VERSION",
[Required version of Libksba])
+AC_DEFINE_UNQUOTED(NEED_NTBTLS_VERSION, "$NEED_NTBTLS_VERSION",
+ [Required version of NTBTLS])
# The default is to use the modules from this package and the few
# other packages in a standard place; i.e where this package gets
# installed. With these options it is possible to override these
# ${prefix} depended values with fixed paths, which can't be replaced
# at make time. See also am/cmacros.am and the defaults in AH_BOTTOM.
AC_ARG_WITH(agent-pgm,
[ --with-agent-pgm=PATH Use PATH as the default for the agent)],
GNUPG_AGENT_PGM="$withval", GNUPG_AGENT_PGM="" )
AC_SUBST(GNUPG_AGENT_PGM)
AM_CONDITIONAL(GNUPG_AGENT_PGM, test -n "$GNUPG_AGENT_PGM")
show_gnupg_agent_pgm="(default)"
test -n "$GNUPG_AGENT_PGM" && show_gnupg_agent_pgm="$GNUPG_AGENT_PGM"
AC_ARG_WITH(pinentry-pgm,
[ --with-pinentry-pgm=PATH Use PATH as the default for the pinentry)],
GNUPG_PINENTRY_PGM="$withval", GNUPG_PINENTRY_PGM="" )
AC_SUBST(GNUPG_PINENTRY_PGM)
AM_CONDITIONAL(GNUPG_PINENTRY_PGM, test -n "$GNUPG_PINENTRY_PGM")
show_gnupg_pinentry_pgm="(default)"
test -n "$GNUPG_PINENTRY_PGM" && show_gnupg_pinentry_pgm="$GNUPG_PINENTRY_PGM"
AC_ARG_WITH(scdaemon-pgm,
[ --with-scdaemon-pgm=PATH Use PATH as the default for the scdaemon)],
GNUPG_SCDAEMON_PGM="$withval", GNUPG_SCDAEMON_PGM="" )
AC_SUBST(GNUPG_SCDAEMON_PGM)
AM_CONDITIONAL(GNUPG_SCDAEMON_PGM, test -n "$GNUPG_SCDAEMON_PGM")
show_gnupg_scdaemon_pgm="(default)"
test -n "$GNUPG_SCDAEMON_PGM" && show_gnupg_scdaemon_pgm="$GNUPG_SCDAEMON_PGM"
AC_ARG_WITH(dirmngr-pgm,
[ --with-dirmngr-pgm=PATH Use PATH as the default for the dirmngr)],
GNUPG_DIRMNGR_PGM="$withval", GNUPG_DIRMNGR_PGM="" )
AC_SUBST(GNUPG_DIRMNGR_PGM)
AM_CONDITIONAL(GNUPG_DIRMNGR_PGM, test -n "$GNUPG_DIRMNGR_PGM")
show_gnupg_dirmngr_pgm="(default)"
test -n "$GNUPG_DIRMNGR_PGM" && show_gnupg_dirmngr_pgm="$GNUPG_DIRMNGR_PGM"
AC_ARG_WITH(protect-tool-pgm,
[ --with-protect-tool-pgm=PATH Use PATH as the default for the protect-tool)],
GNUPG_PROTECT_TOOL_PGM="$withval", GNUPG_PROTECT_TOOL_PGM="" )
AC_SUBST(GNUPG_PROTECT_TOOL_PGM)
AM_CONDITIONAL(GNUPG_PROTECT_TOOL_PGM, test -n "$GNUPG_PROTECT_TOOL_PGM")
show_gnupg_protect_tool_pgm="(default)"
test -n "$GNUPG_PROTECT_TOOL_PGM" \
&& show_gnupg_protect_tool_pgm="$GNUPG_PROTECT_TOOL_PGM"
AC_ARG_WITH(dirmngr-ldap-pgm,
[ --with-dirmngr-ldap-pgm=PATH Use PATH as the default for the dirmnge ldap wrapper)],
GNUPG_DIRMNGR_LDAP_PGM="$withval", GNUPG_DIRMNGR_LDAP_PGM="" )
AC_SUBST(GNUPG_DIRMNGR_LDAP_PGM)
AM_CONDITIONAL(GNUPG_DIRMNGR_LDAP_PGM, test -n "$GNUPG_DIRMNGR_LDAP_PGM")
show_gnupg_dirmngr_ldap_pgm="(default)"
test -n "$GNUPG_DIRMNGR_LDAP_PGM" \
&& show_gnupg_dirmngr_ldap_pgm="$GNUPG_DIRMNGR_LDAP_PGM"
#
# On some platforms gpg2 is usually installed as gpg without using a
# symlink. For correct operation of gpgconf it needs to know the
# installed name of gpg. This option sets "gpg2"'s installed name to
# just "gpg". Note that it might be required to rename gpg2 to gpg
# manually after the build process.
#
AC_ARG_ENABLE(gpg2-is-gpg,
AC_HELP_STRING([--enable-gpg2-is-gpg],[Set installed name of gpg2 to gpg]),
gpg2_is_gpg=$enableval)
if test "$gpg2_is_gpg" = "yes"; then
name_of_installed_gpg=gpg
else
name_of_installed_gpg=gpg2
fi
AC_DEFINE_UNQUOTED(NAME_OF_INSTALLED_GPG, "$name_of_installed_gpg",
[The name of the installed GPG tool])
# SELinux support includes tracking of sensitive files to avoid
# leaking their contents through processing these files by gpg itself
AC_MSG_CHECKING([whether SELinux support is requested])
AC_ARG_ENABLE(selinux-support,
AC_HELP_STRING([--enable-selinux-support],
[enable SELinux support]),
selinux_support=$enableval, selinux_support=no)
AC_MSG_RESULT($selinux_support)
AC_MSG_CHECKING([whether to enable trust models])
AC_ARG_ENABLE(trust-models,
AC_HELP_STRING([--disable-trust-models],
[disable all trust models except "always"]),
use_trust_models=$enableval)
AC_MSG_RESULT($use_trust_models)
if test "$use_trust_models" = no ; then
AC_DEFINE(NO_TRUST_MODELS, 1,
[Define to include only trust-model always])
fi
#
# Options to disable algorithm
#
GNUPG_GPG_DISABLE_ALGO([rsa],[RSA public key])
# Elgamal is a MUST algorithm
# DSA is a MUST algorithm
GNUPG_GPG_DISABLE_ALGO([ecdh],[ECDH public key])
GNUPG_GPG_DISABLE_ALGO([ecdsa],[ECDSA public key])
GNUPG_GPG_DISABLE_ALGO([eddsa],[EdDSA public key])
GNUPG_GPG_DISABLE_ALGO([idea],[IDEA cipher])
# 3DES is a MUST algorithm
GNUPG_GPG_DISABLE_ALGO([cast5],[CAST5 cipher])
GNUPG_GPG_DISABLE_ALGO([blowfish],[BLOWFISH cipher])
GNUPG_GPG_DISABLE_ALGO([aes128],[AES128 cipher])
GNUPG_GPG_DISABLE_ALGO([aes192],[AES192 cipher])
GNUPG_GPG_DISABLE_ALGO([aes256],[AES256 cipher])
GNUPG_GPG_DISABLE_ALGO([twofish],[TWOFISH cipher])
GNUPG_GPG_DISABLE_ALGO([camellia128],[CAMELLIA128 cipher])
GNUPG_GPG_DISABLE_ALGO([camellia192],[CAMELLIA192 cipher])
GNUPG_GPG_DISABLE_ALGO([camellia256],[CAMELLIA256 cipher])
GNUPG_GPG_DISABLE_ALGO([md5],[MD5 hash])
# SHA1 is a MUSt algorithm
GNUPG_GPG_DISABLE_ALGO([rmd160],[RIPE-MD160 hash])
GNUPG_GPG_DISABLE_ALGO([sha224],[SHA-224 hash])
# SHA256 is a MUST algorithm for GnuPG.
GNUPG_GPG_DISABLE_ALGO([sha384],[SHA-384 hash])
GNUPG_GPG_DISABLE_ALGO([sha512],[SHA-512 hash])
# Allow disabling of zip support.
# This is in general not a good idea because according to rfc4880 OpenPGP
# implementations SHOULD support ZLIB.
AC_MSG_CHECKING([whether to enable the ZIP and ZLIB compression algorithm])
AC_ARG_ENABLE(zip,
AC_HELP_STRING([--disable-zip],
[disable the ZIP and ZLIB compression algorithm]),
use_zip=$enableval)
AC_MSG_RESULT($use_zip)
# Allow disabling of bzib2 support.
# It is defined only after we confirm the library is available later
AC_MSG_CHECKING([whether to enable the BZIP2 compression algorithm])
AC_ARG_ENABLE(bzip2,
AC_HELP_STRING([--disable-bzip2],[disable the BZIP2 compression algorithm]),
use_bzip2=$enableval)
AC_MSG_RESULT($use_bzip2)
# Configure option to allow or disallow execution of external
# programs, like a photo viewer.
AC_MSG_CHECKING([whether to enable external program execution])
AC_ARG_ENABLE(exec,
AC_HELP_STRING([--disable-exec],[disable all external program execution]),
use_exec=$enableval)
AC_MSG_RESULT($use_exec)
if test "$use_exec" = no ; then
AC_DEFINE(NO_EXEC,1,[Define to disable all external program execution])
fi
if test "$use_exec" = yes ; then
AC_MSG_CHECKING([whether to enable photo ID viewing])
AC_ARG_ENABLE(photo-viewers,
[ --disable-photo-viewers disable photo ID viewers],
[if test "$enableval" = no ; then
AC_DEFINE(DISABLE_PHOTO_VIEWER,1,[define to disable photo viewing])
fi],enableval=yes)
gnupg_cv_enable_photo_viewers=$enableval
AC_MSG_RESULT($enableval)
if test "$gnupg_cv_enable_photo_viewers" = yes ; then
AC_MSG_CHECKING([whether to use a fixed photo ID viewer])
AC_ARG_WITH(photo-viewer,
[ --with-photo-viewer=FIXED_VIEWER set a fixed photo ID viewer],
[if test "$withval" = yes ; then
withval=no
elif test "$withval" != no ; then
AC_DEFINE_UNQUOTED(FIXED_PHOTO_VIEWER,"$withval",
[if set, restrict photo-viewer to this])
fi],withval=no)
AC_MSG_RESULT($withval)
fi
fi
#
# Check for the key/uid cache size. This can't be zero, but can be
# pretty small on embedded systems. This is used for the gpg part.
#
AC_MSG_CHECKING([for the size of the key and uid cache])
AC_ARG_ENABLE(key-cache,
AC_HELP_STRING([--enable-key-cache=SIZE],
[Set key cache to SIZE (default 4096)]),,enableval=4096)
if test "$enableval" = "no"; then
enableval=5
elif test "$enableval" = "yes" || test "$enableval" = ""; then
enableval=4096
fi
changequote(,)dnl
key_cache_size=`echo "$enableval" | sed 's/[A-Za-z]//g'`
changequote([,])dnl
if test "$enableval" != "$key_cache_size" || test "$key_cache_size" -lt 5; then
AC_MSG_ERROR([invalid key-cache size])
fi
AC_MSG_RESULT($key_cache_size)
AC_DEFINE_UNQUOTED(PK_UID_CACHE_SIZE,$key_cache_size,
[Size of the key and UID caches])
#
# Check whether we want to use Linux capabilities
#
AC_MSG_CHECKING([whether use of capabilities is requested])
AC_ARG_WITH(capabilities,
[ --with-capabilities use linux capabilities [default=no]],
[use_capabilities="$withval"],[use_capabilities=no])
AC_MSG_RESULT($use_capabilities)
#
# Check whether to disable the card support
AC_MSG_CHECKING([whether smartcard support is requested])
AC_ARG_ENABLE(card-support,
AC_HELP_STRING([--disable-card-support],
[disable smartcard support]),
card_support=$enableval)
AC_MSG_RESULT($card_support)
if test "$card_support" = yes ; then
AC_DEFINE(ENABLE_CARD_SUPPORT,1,[Define to include smartcard support])
else
build_scdaemon=no
fi
#
# Allow disabling of internal CCID support.
# It is defined only after we confirm the library is available later
#
AC_MSG_CHECKING([whether to enable the internal CCID driver])
AC_ARG_ENABLE(ccid-driver,
AC_HELP_STRING([--disable-ccid-driver],
[disable the internal CCID driver]),
use_ccid_driver=$enableval)
AC_MSG_RESULT($use_ccid_driver)
AC_MSG_CHECKING([whether to auto start dirmngr])
AC_ARG_ENABLE(dirmngr-auto-start,
AC_HELP_STRING([--disable-dirmngr-auto-start],
[disable auto starting of the dirmngr]),
dirmngr_auto_start=$enableval)
AC_MSG_RESULT($dirmngr_auto_start)
if test "$dirmngr_auto_start" = yes ; then
AC_DEFINE(USE_DIRMNGR_AUTO_START,1,
[Define to enable auto starting of the dirmngr])
fi
#
# To avoid double inclusion of config.h which might happen at some
# places, we add the usual double inclusion protection at the top of
# config.h.
#
AH_TOP([
#ifndef GNUPG_CONFIG_H_INCLUDED
#define GNUPG_CONFIG_H_INCLUDED
])
#
# Stuff which goes at the bottom of config.h.
#
AH_BOTTOM([
/* This is the major version number of GnuPG so that
source included files can test for this. Note, that
we use 2 here even for GnuPG 1.9.x. */
#define GNUPG_MAJOR_VERSION 2
/* Now to separate file name parts.
Please note that the string version must not contain more
than one character because the code assumes strlen()==1 */
#ifdef HAVE_DOSISH_SYSTEM
#define DIRSEP_C '\\'
#define DIRSEP_S "\\"
#define EXTSEP_C '.'
#define EXTSEP_S "."
#define PATHSEP_C ';'
#define PATHSEP_S ";"
#define EXEEXT_S ".exe"
#else
#define DIRSEP_C '/'
#define DIRSEP_S "/"
#define EXTSEP_C '.'
#define EXTSEP_S "."
#define PATHSEP_C ':'
#define PATHSEP_S ":"
#define EXEEXT_S ""
#endif
/* This is the same as VERSION, but should be overridden if the
platform cannot handle things like dots '.' in filenames. Set
SAFE_VERSION_DOT and SAFE_VERSION_DASH to whatever SAFE_VERSION
uses for dots and dashes. */
#define SAFE_VERSION VERSION
#define SAFE_VERSION_DOT '.'
#define SAFE_VERSION_DASH '-'
/* Some global constants. */
#ifdef HAVE_DOSISH_SYSTEM
# ifdef HAVE_DRIVE_LETTERS
# define GNUPG_DEFAULT_HOMEDIR "c:/gnupg"
# else
# define GNUPG_DEFAULT_HOMEDIR "/gnupg"
# endif
#elif defined(__VMS)
#define GNUPG_DEFAULT_HOMEDIR "/SYS$LOGIN/gnupg"
#else
#define GNUPG_DEFAULT_HOMEDIR "~/.gnupg"
#endif
#define GNUPG_PRIVATE_KEYS_DIR "private-keys-v1.d"
#define GNUPG_OPENPGP_REVOC_DIR "openpgp-revocs.d"
/* For some systems (DOS currently), we hardcode the path here. For
POSIX systems the values are constructed by the Makefiles, so that
the values may be overridden by the make invocations; this is to
comply with the GNU coding standards. Note that these values are
only defaults. */
#ifdef HAVE_DOSISH_SYSTEM
# ifdef HAVE_DRIVE_LETTERS
# define GNUPG_BINDIR "c:\\gnupg"
# define GNUPG_LIBEXECDIR "c:\\gnupg"
# define GNUPG_LIBDIR "c:\\gnupg"
# define GNUPG_DATADIR "c:\\gnupg"
# define GNUPG_SYSCONFDIR "c:\\gnupg"
# else
# define GNUPG_BINDIR "\\gnupg"
# define GNUPG_LIBEXECDIR "\\gnupg"
# define GNUPG_LIBDIR "\\gnupg"
# define GNUPG_DATADIR "\\gnupg"
# define GNUPG_SYSCONFDIR "\\gnupg"
# endif
#endif
/* Derive some other constants. */
#if !(defined(HAVE_FORK) && defined(HAVE_PIPE) && defined(HAVE_WAITPID))
#define EXEC_TEMPFILE_ONLY
#endif
/* We didn't define endianness above, so get it from OS macros. This
is intended for making fat binary builds on OS X. */
#if !defined(BIG_ENDIAN_HOST) && !defined(LITTLE_ENDIAN_HOST)
#if defined(__BIG_ENDIAN__)
#define BIG_ENDIAN_HOST 1
#elif defined(__LITTLE_ENDIAN__)
#define LITTLE_ENDIAN_HOST 1
#else
#error "No endianness found"
#endif
#endif
/* Hack used for W32: ldap.m4 also tests for the ASCII version of
ldap_start_tls_s because that is the actual symbol used in the
library. winldap.h redefines it to our commonly used value,
thus we define our usual macro here. */
#ifdef HAVE_LDAP_START_TLS_SA
# ifndef HAVE_LDAP_START_TLS_S
# define HAVE_LDAP_START_TLS_S 1
# endif
#endif
/* Provide the es_ macro for estream. */
#define GPGRT_ENABLE_ES_MACROS 1
/* Tell libgcrypt not to use its own libgpg-error implementation. */
#define USE_LIBGPG_ERROR 1
/* Tell Libgcrypt not to include deprecated definitions. */
#define GCRYPT_NO_DEPRECATED 1
/* We use jnlib, so tell other modules about it. */
#define HAVE_JNLIB_LOGGING 1
/* Our HTTP code is used in estream mode. */
#define HTTP_USE_ESTREAM 1
/* Under W32 we do an explicit socket initialization, thus we need to
avoid the on-demand initialization which would also install an atexit
handler. */
#define HTTP_NO_WSASTARTUP
/* Under Windows we use the gettext code from libgpg-error. */
#define GPG_ERR_ENABLE_GETTEXT_MACROS
/* Under WindowsCE we use the strerror replacement from libgpg-error. */
#define GPG_ERR_ENABLE_ERRNO_MACROS
#endif /*GNUPG_CONFIG_H_INCLUDED*/
])
AM_MAINTAINER_MODE
AC_ARG_VAR(SYSROOT,[locate config scripts also below that directory])
# Checks for programs.
AC_MSG_NOTICE([checking for programs])
AC_PROG_MAKE_SET
AM_SANITY_CHECK
missing_dir=`cd $ac_aux_dir && pwd`
AM_MISSING_PROG(ACLOCAL, aclocal, $missing_dir)
AM_MISSING_PROG(AUTOCONF, autoconf, $missing_dir)
AM_MISSING_PROG(AUTOMAKE, automake, $missing_dir)
AM_MISSING_PROG(AUTOHEADER, autoheader, $missing_dir)
AM_MISSING_PROG(MAKEINFO, makeinfo, $missing_dir)
AM_SILENT_RULES
AC_PROG_AWK
AC_PROG_CC
AC_PROG_CPP
AM_PROG_CC_C_O
if test "x$ac_cv_prog_cc_c89" = "xno" ; then
AC_MSG_ERROR([[No C-89 compiler found]])
fi
AC_PROG_INSTALL
AC_PROG_LN_S
AC_PROG_RANLIB
AC_CHECK_TOOL(AR, ar, :)
AC_PATH_PROG(PERL,"perl")
AC_CHECK_TOOL(WINDRES, windres, :)
AC_ISC_POSIX
gl_EARLY
AC_SYS_LARGEFILE
GNUPG_CHECK_USTAR
# We need to compile and run a program on the build machine. A
# comment in libgpg-error says that the AC_PROG_CC_FOR_BUILD macro in
# the AC archive is broken for autoconf 2.57. Given that there is no
# newer version of that macro, we assume that it is also broken for
# autoconf 2.61 and thus we use a simple but usually sufficient
# approach.
AC_MSG_CHECKING(for cc for build)
if test "$cross_compiling" = "yes"; then
CC_FOR_BUILD="${CC_FOR_BUILD-cc}"
else
CC_FOR_BUILD="${CC_FOR_BUILD-$CC}"
fi
AC_MSG_RESULT($CC_FOR_BUILD)
AC_ARG_VAR(CC_FOR_BUILD,[build system C compiler])
try_gettext=yes
require_iconv=yes
have_dosish_system=no
have_w32_system=no
have_w32ce_system=no
have_android_system=no
run_tests=yes
use_simple_gettext=no
use_ldapwrapper=yes
mmap_needed=yes
case "${host}" in
*-mingw32*)
# special stuff for Windoze NT
ac_cv_have_dev_random=no
AC_DEFINE(USE_ONLY_8DOT3,1,
[Set this to limit filenames to the 8.3 format])
AC_DEFINE(USE_SIMPLE_GETTEXT,1,
[Because the Unix gettext has too much overhead on
MingW32 systems and these systems lack Posix functions,
we use a simplified version of gettext])
have_dosish_system=yes
have_w32_system=yes
run_tests=no
use_ldapwrapper=no # Fixme: Do this only for CE.
case "${host}" in
*-mingw32ce*)
have_w32ce_system=yes
;;
*)
AC_DEFINE(HAVE_DRIVE_LETTERS,1,
[Defined if the OS supports drive letters.])
;;
esac
try_gettext="no"
use_simple_gettext=yes
mmap_needed=no
;;
i?86-emx-os2 | i?86-*-os2*emx )
# OS/2 with the EMX environment
ac_cv_have_dev_random=no
AC_DEFINE(HAVE_DRIVE_LETTERS)
have_dosish_system=yes
try_gettext="no"
;;
i?86-*-msdosdjgpp*)
# DOS with the DJGPP environment
ac_cv_have_dev_random=no
AC_DEFINE(HAVE_DRIVE_LETTERS)
have_dosish_system=yes
try_gettext="no"
;;
*-*-hpux*)
if test -z "$GCC" ; then
CFLAGS="$CFLAGS -Ae -D_HPUX_SOURCE"
fi
;;
*-dec-osf4*)
if test -z "$GCC" ; then
# Suppress all warnings
# to get rid of the unsigned/signed char mismatch warnings.
CFLAGS="$CFLAGS -w"
fi
;;
*-dec-osf5*)
if test -z "$GCC" ; then
# Use the newer compiler `-msg_disable ptrmismatch1' to
# get rid of the unsigned/signed char mismatch warnings.
# Using this may hide other pointer mismatch warnings, but
# it at least lets other warning classes through
CFLAGS="$CFLAGS -msg_disable ptrmismatch1"
fi
;;
m68k-atari-mint)
;;
*-linux-androideabi)
have_android_system=yes
# Android is fully utf-8 and we do not want to use iconv to
# keeps things simple
require_iconv=no
run_tests=no
;;
*)
;;
esac
if test "$have_dosish_system" = yes; then
AC_DEFINE(HAVE_DOSISH_SYSTEM,1,
[Defined if we run on some of the PCDOS like systems
(DOS, Windoze. OS/2) with special properties like
no file modes, case insensitive file names and preferred
use of backslashes as directory name separators.])
fi
AM_CONDITIONAL(HAVE_DOSISH_SYSTEM, test "$have_dosish_system" = yes)
AM_CONDITIONAL(USE_SIMPLE_GETTEXT, test x"$use_simple_gettext" = xyes)
if test "$have_w32_system" = yes; then
AC_DEFINE(HAVE_W32_SYSTEM,1, [Defined if we run on a W32 API based system])
if test "$have_w32ce_system" = yes; then
AC_DEFINE(HAVE_W32CE_SYSTEM,1,[Defined if we run on WindowsCE])
fi
fi
AM_CONDITIONAL(HAVE_W32_SYSTEM, test "$have_w32_system" = yes)
AM_CONDITIONAL(HAVE_W32CE_SYSTEM, test "$have_w32ce_system" = yes)
if test "$have_android_system" = yes; then
AC_DEFINE(HAVE_ANDROID_SYSTEM,1, [Defined if we build for an Android system])
fi
AM_CONDITIONAL(HAVE_ANDROID_SYSTEM, test "$have_android_system" = yes)
if test "$run_tests" = yes; then
AC_DEFINE(RUN_TESTS,1, [Defined if we should run the tests])
fi
AM_CONDITIONAL(RUN_TESTS, test "$run_tests" = yes)
if test "$use_ldapwrapper" = yes; then
AC_DEFINE(USE_LDAPWRAPPER,1, [Build dirmngr with LDAP wrapper process])
fi
AM_CONDITIONAL(USE_LDAPWRAPPER, test "$use_ldapwrapper" = yes)
#
# Allows enabling the use of a standard socket by default This is
# gpg-agent's option --[no-]use-standard-socket. For Windows we force
# the use of this.
#
AC_MSG_CHECKING([whether to use a standard socket by default])
AC_ARG_ENABLE(standard-socket,
AC_HELP_STRING([--disable-standard-socket],
[don't use a standard socket by default]),
use_standard_socket=$enableval)
tmp=""
if test "$use_standard_socket" != yes; then
if test "$have_w32_system" = yes; then
use_standard_socket=yes
tmp=" (forced)"
fi
fi
AC_MSG_RESULT($use_standard_socket$tmp)
if test "$use_standard_socket" = yes; then
AC_DEFINE(USE_STANDARD_SOCKET,1,
[Use the standard socket for the agent by default])
fi
# (These need to go after AC_PROG_CC so that $EXEEXT is defined)
AC_DEFINE_UNQUOTED(EXEEXT,"$EXEEXT",[The executable file extension, if any])
#
# Checks for libraries.
#
AC_MSG_NOTICE([checking for libraries])
#
# libgpg-error is a library with error codes shared between GnuPG
# related projects.
#
AM_PATH_GPG_ERROR("$NEED_GPG_ERROR_VERSION",
have_gpg_error=yes,have_gpg_error=no)
#
# Libgcrypt is our generic crypto library
#
AM_PATH_LIBGCRYPT("$NEED_LIBGCRYPT_API:$NEED_LIBGCRYPT_VERSION",
have_libgcrypt=yes,have_libgcrypt=no)
#
# libassuan is used for IPC
#
AM_PATH_LIBASSUAN("$NEED_LIBASSUAN_API:$NEED_LIBASSUAN_VERSION",
have_libassuan=yes,have_libassuan=no)
if test "$have_libassuan" = "yes"; then
AC_DEFINE_UNQUOTED(GNUPG_LIBASSUAN_VERSION, "$libassuan_version",
[version of the libassuan library])
fi
#
# libksba is our X.509 support library
#
AM_PATH_KSBA("$NEED_KSBA_API:$NEED_KSBA_VERSION",have_ksba=yes,have_ksba=no)
#
# libusb allows us to use the integrated CCID smartcard reader driver.
#
# FiXME: Use GNUPG_CHECK_LIBUSB and modify to use separate AC_SUBSTs.
if test "$use_ccid_driver" = yes ; then
AC_CHECK_LIB(usb, usb_bulk_write,
[ LIBUSB_LIBS="$LIBUSB_LIBS -lusb"
AC_DEFINE(HAVE_LIBUSB,1,
[defined if libusb is available])
have_libusb=yes
])
AC_CHECK_FUNCS(usb_create_match)
fi
AC_SUBST(LIBUSB_LIBS)
#
# Check wether it is necessary to link against libdl.
# (For example to load libpcsclite)
#
gnupg_dlopen_save_libs="$LIBS"
LIBS=""
AC_SEARCH_LIBS(dlopen, c dl,,,)
DL_LIBS=$LIBS
AC_SUBST(DL_LIBS)
LIBS="$gnupg_dlopen_save_libs"
# Checks for g13
AC_PATH_PROG(ENCFS, encfs, /usr/bin/encfs)
AC_DEFINE_UNQUOTED(ENCFS,
"${ENCFS}", [defines the filename of the encfs program])
AC_PATH_PROG(FUSERMOUNT, fusermount, /usr/bin/fusermount)
AC_DEFINE_UNQUOTED(FUSERMOUNT,
"${FUSERMOUNT}", [defines the filename of the fusermount program])
# Checks for dirmngr
#
# Checks for symcryptrun:
#
# libutil has openpty() and login_tty().
AC_CHECK_LIB(util, openpty,
[ LIBUTIL_LIBS="$LIBUTIL_LIBS -lutil"
AC_DEFINE(HAVE_LIBUTIL,1,
[defined if libutil is available])
])
AC_SUBST(LIBUTIL_LIBS)
# shred is used to clean temporary plain text files.
AC_PATH_PROG(SHRED, shred, /usr/bin/shred)
AC_DEFINE_UNQUOTED(SHRED,
"${SHRED}", [defines the filename of the shred program])
#
# Check whether the nPth library is available
#
AM_PATH_NPTH("$NEED_NPTH_API:$NEED_NPTH_VERSION",have_npth=yes,have_npth=no)
if test "$have_npth" = "yes"; then
AC_DEFINE(HAVE_NPTH, 1,
[Defined if the New Portable Thread Library is available])
AC_DEFINE(USE_NPTH, 1,
[Defined if support for nPth is requested and nPth is available])
else
AC_MSG_WARN([[
***
*** To support concurrent access for example in gpg-agent and the SCdaemon
*** we need the support of the New Portable Threads Library.
***]])
fi
+
#
-# Check whether GNUTLS is available
+# NTBTLS is our TLS library. If it is not available fallback to
+# GNUTLS.
#
-PKG_CHECK_MODULES([LIBGNUTLS], [gnutls >= $NEED_GNUTLS_VERSION],
- [have_gnutls=yes],
- [have_gnutls=no])
-if test "$have_gnutls" = "yes"; then
- AC_SUBST([LIBGNUTLS_CFLAGS])
- AC_SUBST([LIBGNUTLS_LIBS])
- AC_DEFINE(HTTP_USE_GNUTLS, 1, [Enable GNUTLS support in http.c])
+AM_PATH_NTBTLS("$NEED_NTBTLS_API:$NEED_NTBTLS_VERSION",
+ [have_ntbtls=yes],[have_ntbtls=no])
+
+if test "$have_ntbtls" = yes ; then
+ use_tls_library=ntbtls
+ AC_DEFINE(HTTP_USE_NTBTLS, 1, [Enable NTBTLS support in http.c])
else
- tmp=$(echo "$LIBGNUTLS_PKG_ERRORS" | tr '\n' '\v' | sed 's/\v/\n*** /g')
- AC_MSG_WARN([[
+ PKG_CHECK_MODULES([LIBGNUTLS], [gnutls >= $NEED_GNUTLS_VERSION],
+ [have_gnutls=yes],
+ [have_gnutls=no])
+ if test "$have_gnutls" = "yes"; then
+ AC_SUBST([LIBGNUTLS_CFLAGS])
+ AC_SUBST([LIBGNUTLS_LIBS])
+ use_tls_library=gnutls
+ AC_DEFINE(HTTP_USE_GNUTLS, 1, [Enable GNUTLS support in http.c])
+ else
+ tmp=$(echo "$LIBGNUTLS_PKG_ERRORS" | tr '\n' '\v' | sed 's/\v/\n*** /g')
+ AC_MSG_WARN([[
***
-*** Building without GNUTLS - no TLS access to keyservers.
+*** Building without NTBTLS and GNUTLS - no TLS access to keyservers.
***
*** $tmp]])
+ fi
fi
-
AC_MSG_NOTICE([checking for networking options])
#
# Must check for network library requirements before doing link tests
# for ldap, for example. If ldap libs are static (or dynamic and without
# ELF runtime link paths), then link will fail and LDAP support won't
# be detected.
#
AC_CHECK_FUNC(gethostbyname, , AC_CHECK_LIB(nsl, gethostbyname,
[NETLIBS="-lnsl $NETLIBS"]))
AC_CHECK_FUNC(setsockopt, , AC_CHECK_LIB(socket, setsockopt,
[NETLIBS="-lsocket $NETLIBS"]))
#
# Check for ADNS.
#
_cppflags="${CPPFLAGS}"
_ldflags="${LDFLAGS}"
AC_ARG_WITH(adns,
AC_HELP_STRING([--with-adns=DIR],
[look for the adns library in DIR]),
[if test -d "$withval"; then
CPPFLAGS="${CPPFLAGS} -I$withval/include"
LDFLAGS="${LDFLAGS} -L$withval/lib"
fi])
if test "$with_adns" != "no"; then
AC_CHECK_HEADERS(adns.h,
AC_CHECK_LIB(adns, adns_init,
[have_adns=yes],
[CPPFLAGS=${_cppflags} LDFLAGS=${_ldflags}]),
[CPPFLAGS=${_cppflags} LDFLAGS=${_ldflags}])
fi
if test "$have_adns" = "yes"; then
ADNSLIBS="-ladns"
fi
AC_SUBST(ADNSLIBS)
# Newer adns versions feature a free function to be used under W32.
AC_CHECK_FUNCS(adns_free)
#
# Now try for the resolver functions so we can use DNS for SRV, PA and CERT.
#
AC_ARG_ENABLE(dns-srv,
AC_HELP_STRING([--disable-dns-srv],
[disable the use of DNS SRV in HKP and HTTP]),
use_dns_srv=$enableval,use_dns_srv=yes)
AC_ARG_ENABLE(dns-pka,
AC_HELP_STRING([--disable-dns-pka],
[disable the use of PKA records in DNS]),
use_dns_pka=$enableval,use_dns_pka=yes)
AC_ARG_ENABLE(dns-cert,
AC_HELP_STRING([--disable-dns-cert],
[disable the use of CERT records in DNS]),
use_dns_cert=$enableval,use_dns_cert=yes)
if test x"$use_dns_pka" = xyes || test x"$use_dns_srv" = xyes \
|| test x"$use_dns_cert" = xyes; then
_dns_save_libs=$LIBS
LIBS=""
# the double underscore thing is a glibc-ism?
AC_SEARCH_LIBS(res_query,resolv bind,,
AC_SEARCH_LIBS(__res_query,resolv bind,,have_resolver=no))
AC_SEARCH_LIBS(dn_expand,resolv bind,,
AC_SEARCH_LIBS(__dn_expand,resolv bind,,have_resolver=no))
AC_SEARCH_LIBS(dn_skipname,resolv bind,,
AC_SEARCH_LIBS(__dn_skipname,resolv bind,,have_resolver=no))
if test x"$have_resolver" != xno ; then
# Make sure that the BIND 4 resolver interface is workable before
# enabling any code that calls it. At some point I'll rewrite the
# code to use the BIND 8 resolver API.
# We might also want to use adns instead. Problem with ADNS is that
# it does not support v6.
AC_MSG_CHECKING([whether the resolver is usable])
AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/nameser.h>
#include <resolv.h>]],
[[unsigned char answer[PACKETSZ];
res_query("foo.bar",C_IN,T_A,answer,PACKETSZ);
dn_skipname(0,0);
dn_expand(0,0,0,0,0);
]])],have_resolver=yes,have_resolver=no)
AC_MSG_RESULT($have_resolver)
# This is Apple-specific and somewhat bizarre as they changed the
# define in bind 8 for some reason.
if test x"$have_resolver" != xyes ; then
AC_MSG_CHECKING(
[whether I can make the resolver usable with BIND_8_COMPAT])
AC_LINK_IFELSE([AC_LANG_PROGRAM([[#define BIND_8_COMPAT
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/nameser.h>
#include <resolv.h>]],
[[unsigned char answer[PACKETSZ];
res_query("foo.bar",C_IN,T_A,answer,PACKETSZ);
dn_skipname(0,0); dn_expand(0,0,0,0,0);
]])],[have_resolver=yes ; need_compat=yes])
AC_MSG_RESULT($have_resolver)
fi
fi
if test x"$have_resolver" = xyes ; then
DNSLIBS=$LIBS
if test x"$use_dns_srv" = xyes ; then
AC_DEFINE(USE_DNS_SRV,1,[define to use DNS SRV])
fi
if test x"$use_dns_pka" = xyes ; then
AC_DEFINE(USE_DNS_PKA,1,[define to use our experimental DNS PKA])
fi
if test x"$use_dns_cert" = xyes ; then
AC_DEFINE(USE_DNS_CERT,1,[define to use DNS CERT])
fi
if test x"$need_compat" = xyes ; then
AC_DEFINE(BIND_8_COMPAT,1,[an Apple OSXism])
fi
else
# If we have no resolver library but ADNS (e.g. under W32) enable the
# code parts which can be used with ADNS.
if test x"$have_adns" = xyes ; then
DNSLIBS="$ADNSLIBS"
AC_DEFINE(USE_ADNS,1,[Use ADNS as resolver library.])
if test x"$use_dns_srv" = xyes ; then
AC_DEFINE(USE_DNS_SRV,1)
fi
if test x"$use_dns_pka" = xyes ; then
AC_DEFINE(USE_DNS_PKA,1)
fi
if test x"$use_dns_cert" = xyes ; then
AC_DEFINE(USE_DNS_CERT,1,[define to use DNS CERT])
fi
else
use_dns_srv=no
use_dns_pka=no
use_dns_cert=no
fi
fi
LIBS=$_dns_save_libs
fi
AC_SUBST(DNSLIBS)
AM_CONDITIONAL(USE_DNS_SRV, test x"$use_dns_srv" = xyes)
#
# Check for LDAP
#
# Note that running the check changes the variable
# gnupg_have_ldap from "n/a" to "no" or "yes".
if test "$build_dirmngr" = "yes" ; then
GNUPG_CHECK_LDAP($NETLIBS)
AC_CHECK_LIB(lber, ber_free,
[ LBER_LIBS="$LBER_LIBS -llber"
AC_DEFINE(HAVE_LBER,1,
[defined if liblber is available])
have_lber=yes
])
fi
AC_SUBST(LBER_LIBS)
#
# Check for sendmail
#
# This isn't necessarily sendmail itself, but anything that gives a
# sendmail-ish interface to the outside world. That includes Exim,
# Postfix, etc. Basically, anything that can handle "sendmail -t".
AC_ARG_WITH(mailprog,
AC_HELP_STRING([--with-mailprog=NAME],
[use "NAME -t" for mail transport]),
,with_mailprog=yes)
if test x"$with_mailprog" = xyes ; then
AC_PATH_PROG(SENDMAIL,sendmail,,$PATH:/usr/sbin:/usr/libexec:/usr/lib)
elif test x"$with_mailprog" != xno ; then
AC_MSG_CHECKING([for a mail transport program])
AC_SUBST(SENDMAIL,$with_mailprog)
AC_MSG_RESULT($with_mailprog)
fi
#
# Construct a printable name of the OS
#
case "${host}" in
*-mingw32ce*)
PRINTABLE_OS_NAME="W32CE"
;;
*-mingw32*)
PRINTABLE_OS_NAME="MingW32"
;;
*-*-cygwin*)
PRINTABLE_OS_NAME="Cygwin"
;;
i?86-emx-os2 | i?86-*-os2*emx )
PRINTABLE_OS_NAME="OS/2"
;;
i?86-*-msdosdjgpp*)
PRINTABLE_OS_NAME="MSDOS/DJGPP"
try_dynload=no
;;
*-linux*)
PRINTABLE_OS_NAME="GNU/Linux"
;;
*)
PRINTABLE_OS_NAME=`uname -s || echo "Unknown"`
;;
esac
AC_DEFINE_UNQUOTED(PRINTABLE_OS_NAME, "$PRINTABLE_OS_NAME",
[A human readable text with the name of the OS])
#
# Checking for iconv
#
if test "$require_iconv" = yes; then
AM_ICONV
else
LIBICONV=
LTLIBICONV=
AC_SUBST(LIBICONV)
AC_SUBST(LTLIBICONV)
fi
#
# Check for gettext
#
# This is "GNU gnupg" - The project-id script from gettext
# needs this string
#
AC_MSG_NOTICE([checking for gettext])
AM_PO_SUBDIRS
AM_GNU_GETTEXT_VERSION([0.17])
if test "$try_gettext" = yes; then
AM_GNU_GETTEXT([external],[need-ngettext])
# gettext requires some extra checks. These really should be part of
# the basic AM_GNU_GETTEXT macro. TODO: move other gettext-specific
# function checks to here.
AC_CHECK_FUNCS(strchr)
else
USE_NLS=no
USE_INCLUDED_LIBINTL=no
BUILD_INCLUDED_LIBINTL=no
POSUB=po
AC_SUBST(USE_NLS)
AC_SUBST(USE_INCLUDED_LIBINTL)
AC_SUBST(BUILD_INCLUDED_LIBINTL)
AC_SUBST(POSUB)
fi
# We use HAVE_LANGINFO_CODESET in a couple of places.
AM_LANGINFO_CODESET
# Checks required for our use of locales
gt_LC_MESSAGES
#
# SELinux support
#
if test "$selinux_support" = yes ; then
AC_DEFINE(ENABLE_SELINUX_HACKS,1,[Define to enable SELinux support])
fi
#
# Checks for header files.
#
AC_MSG_NOTICE([checking for header files])
AC_HEADER_STDC
AC_CHECK_HEADERS([string.h unistd.h langinfo.h termio.h locale.h getopt.h \
pty.h utmp.h pwd.h inttypes.h signal.h])
AC_HEADER_TIME
#
# Checks for typedefs, structures, and compiler characteristics.
#
AC_MSG_NOTICE([checking for system characteristics])
AC_C_CONST
AC_C_INLINE
AC_C_VOLATILE
AC_TYPE_SIZE_T
AC_TYPE_MODE_T
AC_TYPE_SIGNAL
AC_DECL_SYS_SIGLIST
gl_HEADER_SYS_SOCKET
gl_TYPE_SOCKLEN_T
AC_SEARCH_LIBS([inet_addr], [nsl])
AC_ARG_ENABLE(endian-check,
AC_HELP_STRING([--disable-endian-check],
[disable the endian check and trust the OS provided macros]),
endiancheck=$enableval,endiancheck=yes)
if test x"$endiancheck" = xyes ; then
GNUPG_CHECK_ENDIAN
fi
# fixme: we should get rid of the byte type
GNUPG_CHECK_TYPEDEF(byte, HAVE_BYTE_TYPEDEF)
GNUPG_CHECK_TYPEDEF(ushort, HAVE_USHORT_TYPEDEF)
GNUPG_CHECK_TYPEDEF(ulong, HAVE_ULONG_TYPEDEF)
GNUPG_CHECK_TYPEDEF(u16, HAVE_U16_TYPEDEF)
GNUPG_CHECK_TYPEDEF(u32, HAVE_U32_TYPEDEF)
AC_CHECK_SIZEOF(unsigned short)
AC_CHECK_SIZEOF(unsigned int)
AC_CHECK_SIZEOF(unsigned long)
AC_CHECK_SIZEOF(unsigned long long)
AC_HEADER_TIME
AC_CHECK_SIZEOF(time_t,,[[
#include <stdio.h>
#if TIME_WITH_SYS_TIME
# include <sys/time.h>
# include <time.h>
#else
# if HAVE_SYS_TIME_H
# include <sys/time.h>
# else
# include <time.h>
# endif
#endif
]])
GNUPG_TIME_T_UNSIGNED
if test "$ac_cv_sizeof_unsigned_short" = "0" \
|| test "$ac_cv_sizeof_unsigned_int" = "0" \
|| test "$ac_cv_sizeof_unsigned_long" = "0"; then
AC_MSG_WARN([Hmmm, something is wrong with the sizes - using defaults]);
fi
#
# Checks for library functions.
#
AC_MSG_NOTICE([checking for library functions])
AC_CHECK_DECLS(getpagesize)
AC_FUNC_FSEEKO
AC_FUNC_VPRINTF
AC_FUNC_FORK
AC_CHECK_FUNCS([strerror strlwr tcgetattr mmap canonicalize_file_name])
AC_CHECK_FUNCS([strcasecmp strncasecmp ctermid times gmtime_r strtoull])
AC_CHECK_FUNCS([unsetenv fcntl ftruncate inet_ntop canonicalize_file_name])
AC_CHECK_FUNCS([gettimeofday getrusage getrlimit setrlimit clock_gettime])
AC_CHECK_FUNCS([atexit raise getpagesize strftime nl_langinfo setlocale])
AC_CHECK_FUNCS([waitpid wait4 sigaction sigprocmask pipe getaddrinfo])
AC_CHECK_FUNCS([ttyname rand ftello fsync stat lstat])
if test "$have_android_system" = yes; then
# On Android ttyname is a stub but prints an error message.
AC_DEFINE(HAVE_BROKEN_TTYNAME,1,
[Defined if ttyname does not work properly])
fi
AC_CHECK_TYPES([struct sigaction, sigset_t],,,[#include <signal.h>])
# Dirmngr requires mmap on Unix systems.
if test $ac_cv_func_mmap != yes -a $mmap_needed = yes; then
AC_MSG_ERROR([[Sorry, the current implemenation requires mmap.]])
fi
#
# These are needed by the jnlib parts in common.
# Note: We already checked pwd.h.
AC_CHECK_HEADERS([signal.h])
AC_CHECK_FUNCS([memicmp stpcpy strsep strlwr strtoul memmove stricmp strtol \
memrchr isascii timegm getrusage setrlimit stat setlocale \
flockfile funlockfile fopencookie funopen getpwnam getpwuid \
getenv inet_pton])
# end jnlib checks.
#
# gnulib checks
#
gl_SOURCE_BASE([gl])
gl_M4_BASE([gl/m4])
gl_MODULES([setenv mkdtemp xsize strpbrk])
gl_INIT
#
# W32 specific test
#
GNUPG_FUNC_MKDIR_TAKES_ONE_ARG
#
# Sanity check regex. Tests adapted from mutt.
#
AC_MSG_CHECKING([whether regular expression support is requested])
AC_ARG_ENABLE(regex,
AC_HELP_STRING([--disable-regex],
[do not handle regular expressions in trust signatures]),
use_regex=$enableval, use_regex=yes)
AC_MSG_RESULT($use_regex)
if test "$use_regex" = yes ; then
_cppflags="${CPPFLAGS}"
_ldflags="${LDFLAGS}"
AC_ARG_WITH(regex,
AC_HELP_STRING([--with-regex=DIR],[look for regex in DIR]),
[
if test -d "$withval" ; then
CPPFLAGS="${CPPFLAGS} -I$withval/include"
LDFLAGS="${LDFLAGS} -L$withval/lib"
fi
],withval="")
# Does the system have regex functions at all?
AC_SEARCH_LIBS([regcomp], [regex])
AC_CHECK_FUNC(regcomp, gnupg_cv_have_regex=yes, gnupg_cv_have_regex=no)
if test $gnupg_cv_have_regex = no; then
use_regex=no
else
if test x"$cross_compiling" = xyes; then
AC_MSG_WARN([cross compiling; assuming regexp libray is not broken])
else
AC_CACHE_CHECK([whether your system's regexp library is broken],
[gnupg_cv_regex_broken],
AC_TRY_RUN([
#include <unistd.h>
#include <regex.h>
main() { regex_t blah ; regmatch_t p; p.rm_eo = p.rm_eo; return regcomp(&blah, "foo.*bar", REG_NOSUB) || regexec (&blah, "foobar", 0, NULL, 0); }],
gnupg_cv_regex_broken=no, gnupg_cv_regex_broken=yes, gnupg_cv_regex_broken=yes))
if test $gnupg_cv_regex_broken = yes; then
AC_MSG_WARN([your regex is broken - disabling regex use])
use_regex=no
fi
fi
fi
CPPFLAGS="${_cppflags}"
LDFLAGS="${_ldflags}"
fi
if test "$use_regex" != yes ; then
AC_DEFINE(DISABLE_REGEX,1, [Define to disable regular expression support])
fi
AM_CONDITIONAL(DISABLE_REGEX, test x"$use_regex" != xyes)
#
# Do we have zlib? Must do it here because Solaris failed
# when compiling a conftest (due to the "-lz" from LIBS).
# Note that we combine zlib and bzlib2 in ZLIBS.
#
if test "$use_zip" = yes ; then
_cppflags="${CPPFLAGS}"
_ldflags="${LDFLAGS}"
AC_ARG_WITH(zlib,
[ --with-zlib=DIR use libz in DIR],[
if test -d "$withval"; then
CPPFLAGS="${CPPFLAGS} -I$withval/include"
LDFLAGS="${LDFLAGS} -L$withval/lib"
fi
])
AC_CHECK_HEADER(zlib.h,
AC_CHECK_LIB(z, deflateInit2_,
[
ZLIBS="-lz"
AC_DEFINE(HAVE_ZIP,1, [Defined if ZIP and ZLIB are supported])
],
CPPFLAGS=${_cppflags} LDFLAGS=${_ldflags}),
CPPFLAGS=${_cppflags} LDFLAGS=${_ldflags})
fi
#
# Check whether we can support bzip2
#
if test "$use_bzip2" = yes ; then
_cppflags="${CPPFLAGS}"
_ldflags="${LDFLAGS}"
AC_ARG_WITH(bzip2,
AC_HELP_STRING([--with-bzip2=DIR],[look for bzip2 in DIR]),
[
if test -d "$withval" ; then
CPPFLAGS="${CPPFLAGS} -I$withval/include"
LDFLAGS="${LDFLAGS} -L$withval/lib"
fi
],withval="")
# Checking alongside stdio.h as an early version of bzip2 (1.0)
# required stdio.h to be included before bzlib.h, and Solaris 9 is
# woefully out of date.
if test "$withval" != no ; then
AC_CHECK_HEADER(bzlib.h,
AC_CHECK_LIB(bz2,BZ2_bzCompressInit,
[
have_bz2=yes
ZLIBS="$ZLIBS -lbz2"
AC_DEFINE(HAVE_BZIP2,1,
[Defined if the bz2 compression library is available])
],
CPPFLAGS=${_cppflags} LDFLAGS=${_ldflags}),
CPPFLAGS=${_cppflags} LDFLAGS=${_ldflags},[#include <stdio.h>])
fi
fi
AM_CONDITIONAL(ENABLE_BZIP2_SUPPORT,test x"$have_bz2" = "xyes")
AC_SUBST(ZLIBS)
# Check for readline support
GNUPG_CHECK_READLINE
if test "$development_version" = yes; then
AC_DEFINE(IS_DEVELOPMENT_VERSION,1,
[Defined if this is not a regular release])
fi
AM_CONDITIONAL(CROSS_COMPILING, test x$cross_compiling = xyes)
GNUPG_CHECK_GNUMAKE
# Add some extra libs here so that previous tests don't fail for
# mysterious reasons - the final link step should bail out.
# W32SOCKLIBS is also defined so that if can be used for tools not
# requiring any network stuff but linking to code in libcommon which
# tracks in winsock stuff (e.g. init_common_subsystems).
if test "$have_w32_system" = yes; then
if test "$have_w32ce_system" = yes; then
W32SOCKLIBS="-lws2"
else
W32SOCKLIBS="-lws2_32"
fi
NETLIBS="${NETLIBS} ${W32SOCKLIBS}"
fi
AC_SUBST(NETLIBS)
AC_SUBST(W32SOCKLIBS)
#
# Setup gcc specific options
#
AC_MSG_NOTICE([checking for cc features])
if test "$GCC" = yes; then
# Check whether gcc does not emit a diagnositc for unknow -Wno-*
# options. This is the case for gcc >= 4.6
AC_MSG_CHECKING([if gcc ignores unknown -Wno-* options])
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
#if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 6 )
#kickerror
#endif]],[])],[_gcc_silent_wno=yes],[_gcc_silent_wno=no])
AC_MSG_RESULT($_gcc_silent_wno)
# Note that it is okay to use CFLAGS here because these are just
# warning options and the user should have a chance of overriding
# them.
if test "$USE_MAINTAINER_MODE" = "yes"; then
CFLAGS="$CFLAGS -O3 -Wall -Wcast-align -Wshadow -Wstrict-prototypes"
CFLAGS="$CFLAGS -Wformat -Wno-format-y2k -Wformat-security"
if test x"$_gcc_silent_wno" = xyes ; then
_gcc_wopt=yes
else
AC_MSG_CHECKING([if gcc supports -Wno-missing-field-initializers])
_gcc_cflags_save=$CFLAGS
CFLAGS="-Wno-missing-field-initializers"
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([],[])],
[_gcc_wopt=yes],[_gcc_wopt=no])
AC_MSG_RESULT($_gcc_wopt)
CFLAGS=$_gcc_cflags_save;
fi
if test x"$_gcc_wopt" = xyes ; then
CFLAGS="$CFLAGS -W -Wno-sign-compare -Wno-missing-field-initializers"
fi
AC_MSG_CHECKING([if gcc supports -Wdeclaration-after-statement])
_gcc_cflags_save=$CFLAGS
CFLAGS="-Wdeclaration-after-statement"
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([],[])],_gcc_wopt=yes,_gcc_wopt=no)
AC_MSG_RESULT($_gcc_wopt)
CFLAGS=$_gcc_cflags_save;
if test x"$_gcc_wopt" = xyes ; then
CFLAGS="$CFLAGS -Wdeclaration-after-statement"
fi
else
CFLAGS="$CFLAGS -Wall"
fi
if test x"$_gcc_silent_wno" = xyes ; then
_gcc_psign=yes
else
AC_MSG_CHECKING([if gcc supports -Wno-pointer-sign])
_gcc_cflags_save=$CFLAGS
CFLAGS="-Wno-pointer-sign"
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([],[])],
[_gcc_psign=yes],[_gcc_psign=no])
AC_MSG_RESULT($_gcc_psign)
CFLAGS=$_gcc_cflags_save;
fi
if test x"$_gcc_psign" = xyes ; then
CFLAGS="$CFLAGS -Wno-pointer-sign"
fi
AC_MSG_CHECKING([if gcc supports -Wpointer-arith])
_gcc_cflags_save=$CFLAGS
CFLAGS="-Wpointer-arith"
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([],[])],_gcc_psign=yes,_gcc_psign=no)
AC_MSG_RESULT($_gcc_psign)
CFLAGS=$_gcc_cflags_save;
if test x"$_gcc_psign" = xyes ; then
CFLAGS="$CFLAGS -Wpointer-arith"
fi
fi
#
# This is handy for debugging so the compiler doesn't rearrange
# things and eliminate variables.
#
AC_ARG_ENABLE(optimization,
AC_HELP_STRING([--disable-optimization],
[disable compiler optimization]),
[if test $enableval = no ; then
CFLAGS=`echo $CFLAGS | sed s/-O[[1-9]]\ /-O0\ /g`
fi])
#
# Decide what to build
#
build_scdaemon_extra=""
if test "$build_scdaemon" = "yes"; then
if test $have_libusb = no; then
build_scdaemon_extra="without internal CCID driver"
fi
if test -n "$build_scdaemon_extra"; then
build_scdaemon_extra="(${build_scdaemon_extra})"
fi
fi
#
# Set variables for use by automake makefiles.
#
AM_CONDITIONAL(BUILD_GPG, test "$build_gpg" = "yes")
AM_CONDITIONAL(BUILD_GPGSM, test "$build_gpgsm" = "yes")
AM_CONDITIONAL(BUILD_AGENT, test "$build_agent" = "yes")
AM_CONDITIONAL(BUILD_SCDAEMON, test "$build_scdaemon" = "yes")
AM_CONDITIONAL(BUILD_G13, test "$build_g13" = "yes")
AM_CONDITIONAL(BUILD_DIRMNGR, test "$build_dirmngr" = "yes")
AM_CONDITIONAL(BUILD_TOOLS, test "$build_tools" = "yes")
AM_CONDITIONAL(BUILD_DOC, test "$build_doc" = "yes")
AM_CONDITIONAL(BUILD_SYMCRYPTRUN, test "$build_symcryptrun" = "yes")
AM_CONDITIONAL(BUILD_GPGTAR, test "$build_gpgtar" = "yes")
AM_CONDITIONAL(ENABLE_CARD_SUPPORT, test "$card_support" = yes)
AM_CONDITIONAL(NO_TRUST_MODELS, test "$use_trust_models" = no)
AM_CONDITIONAL(RUN_GPG_TESTS,
test x$cross_compiling = xno -a "$build_gpg" = yes )
#
# Set some defines for use gpgconf.
#
if test "$build_gpg" = yes ; then
AC_DEFINE(BUILD_WITH_GPG,1,[Defined if GPG is to be build])
fi
if test "$build_gpgsm" = yes ; then
AC_DEFINE(BUILD_WITH_GPGSM,1,[Defined if GPGSM is to be build])
fi
if test "$build_agent" = yes ; then
AC_DEFINE(BUILD_WITH_AGENT,1,[Defined if GPG-AGENT is to be build])
fi
if test "$build_scdaemon" = yes ; then
AC_DEFINE(BUILD_WITH_SCDAEMON,1,[Defined if SCDAEMON is to be build])
fi
if test "$build_dirmngr" = yes ; then
AC_DEFINE(BUILD_WITH_DIRMNGR,1,[Defined if SCDAEMON is to be build])
fi
if test "$build_g13" = yes ; then
AC_DEFINE(BUILD_WITH_G13,1,[Defined if G13 is to be build])
fi
#
# Define Name strings
#
AC_DEFINE_UNQUOTED(GNUPG_NAME, "GnuPG", [The name of the project])
AC_DEFINE_UNQUOTED(GPG_NAME, "gpg", [The name of the OpenPGP tool])
AC_DEFINE_UNQUOTED(GPG_DISP_NAME, "GnuPG", [The displayed name of gpg])
AC_DEFINE_UNQUOTED(GPGSM_NAME, "gpgsm", [The name of the S/MIME tool])
AC_DEFINE_UNQUOTED(GPGSM_DISP_NAME, "GPGSM", [The displayed name of gpgsm])
AC_DEFINE_UNQUOTED(GPG_AGENT_NAME, "gpg-agent", [The name of the agent])
AC_DEFINE_UNQUOTED(GPG_AGENT_DISP_NAME, "GPG Agent",
[The displayed name of gpg-agent])
AC_DEFINE_UNQUOTED(SCDAEMON_NAME, "scdaemon", [The name of the scdaemon])
AC_DEFINE_UNQUOTED(SCDAEMON_DISP_NAME, "SCDaemon",
[The displayed name of scdaemon])
AC_DEFINE_UNQUOTED(DIRMNGR_NAME, "dirmngr", [The name of the dirmngr])
AC_DEFINE_UNQUOTED(DIRMNGR_DISP_NAME, "DirMngr",
[The displayed name of dirmngr])
AC_DEFINE_UNQUOTED(G13_NAME, "g13", [The name of the g13 tool])
AC_DEFINE_UNQUOTED(G13_DISP_NAME, "G13", [The displayed name of g13])
AC_DEFINE_UNQUOTED(GPGCONF_NAME, "gpgconf", [The name of the gpgconf tool])
AC_DEFINE_UNQUOTED(GPGCONF_DISP_NAME, "GPGConf",
[The displayed name of gpgconf])
AC_DEFINE_UNQUOTED(GPGTAR_NAME, "gpgtar", [The name of the gpgtar tool])
AC_DEFINE_UNQUOTED(GPG_AGENT_INFO_NAME, "GPG_AGENT_INFO",
[The name of the agent info envvar])
AC_DEFINE_UNQUOTED(GPG_AGENT_SOCK_NAME, "S.gpg-agent",
[The name of the agent socket])
AC_DEFINE_UNQUOTED(GPG_AGENT_SSH_SOCK_NAME, "S.gpg-agent.ssh",
[The name of the agent socket for ssh])
AC_DEFINE_UNQUOTED(DIRMNGR_INFO_NAME, "DIRMNGR_INFO",
[The name of the dirmngr info envvar])
AC_DEFINE_UNQUOTED(SCDAEMON_SOCK_NAME, "S.scdaemon",
[The name of the SCdaemon socket])
AC_DEFINE_UNQUOTED(DIRMNGR_SOCK_NAME, "S.dirmngr",
[The name of the dirmngr socket])
AC_DEFINE_UNQUOTED(GPGEXT_GPG, "gpg", [The standard binary file suffix])
if test "$have_w32_system" = yes; then
AC_DEFINE_UNQUOTED(GNUPG_REGISTRY_DIR, "\\\\Software\\\\GNU\\\\GnuPG",
[The directory part of the W32 registry keys])
fi
#
# Provide information about the build.
#
BUILD_REVISION="mym4_revision"
AC_SUBST(BUILD_REVISION)
AC_DEFINE_UNQUOTED(BUILD_REVISION, "$BUILD_REVISION",
[GIT commit id revision used to build this package])
changequote(,)dnl
BUILD_FILEVERSION=`echo "$VERSION" | sed 's/\([0-9.]*\).*/\1./;s/\./,/g'`
changequote([,])dnl
BUILD_FILEVERSION="${BUILD_FILEVERSION}mym4_revision_dec"
AC_SUBST(BUILD_FILEVERSION)
BUILD_TIMESTAMP=`date -u +%Y-%m-%dT%H:%M+0000 2>/dev/null || date`
AC_SUBST(BUILD_TIMESTAMP)
AC_DEFINE_UNQUOTED(BUILD_TIMESTAMP, "$BUILD_TIMESTAMP",
[The time this package was configured for a build])
BUILD_HOSTNAME="$ac_hostname"
AC_SUBST(BUILD_HOSTNAME)
#
# Print errors here so that they are visible all
# together and the user can acquire them all together.
#
die=no
if test "$have_gpg_error" = "no"; then
die=yes
AC_MSG_NOTICE([[
***
*** You need libgpg-error to build this program.
** This library is for example available at
*** ftp://ftp.gnupg.org/gcrypt/libgpg-error
*** (at least version $NEED_GPG_ERROR_VERSION is required.)
***]])
fi
if test "$have_libgcrypt" = "no"; then
die=yes
AC_MSG_NOTICE([[
***
*** You need libgcrypt to build this program.
** This library is for example available at
*** ftp://ftp.gnupg.org/gcrypt/libgcrypt/
*** (at least version $NEED_LIBGCRYPT_VERSION (API $NEED_LIBGCRYPT_API) is required.)
***]])
fi
if test "$have_libassuan" = "no"; then
die=yes
AC_MSG_NOTICE([[
***
*** You need libassuan to build this program.
*** This library is for example available at
*** ftp://ftp.gnupg.org/gcrypt/libassuan/
*** (at least version $NEED_LIBASSUAN_VERSION (API $NEED_LIBASSUAN_API) is required).
***]])
fi
if test "$have_ksba" = "no"; then
die=yes
AC_MSG_NOTICE([[
***
*** You need libksba to build this program.
*** This library is for example available at
*** ftp://ftp.gnupg.org/gcrypt/libksba/
*** (at least version $NEED_KSBA_VERSION using API $NEED_KSBA_API is required).
***]])
fi
if test "$gnupg_have_ldap" = "no"; then
die=yes
AC_MSG_NOTICE([[
***
*** The Dirmngr part requires an LDAP library
*** Check out
*** http://www.openldap.org
*** for a suitable implementation.
***]])
if test "$have_w32ce_system" = yes; then
AC_MSG_NOTICE([[
*** Note that CeGCC might be broken, a package fixing this is:
*** http://files.kolab.org/local/windows-ce/
*** source/wldap32_0.1-mingw32ce.orig.tar.gz
*** binary/wldap32-ce-arm-dev_0.1-1_all.deb
***]])
fi
fi
if test "$have_npth" = "no"; then
die=yes
AC_MSG_NOTICE([[
***
*** It is now required to build with support for the
*** New Portable Threads Library (nPth). Please install this
*** library first. The library is for example available at
*** ftp://ftp.gnupg.org/gcrypt/npth/
*** (at least version $NEED_NPTH_VERSION (API $NEED_NPTH_API) is required).
***]])
fi
if test "$require_iconv" = yes; then
if test "$am_func_iconv" != yes; then
die=yes
AC_MSG_NOTICE([[
***
*** The system does not provide a working iconv function. Please
*** install a suitable library; for example GNU Libiconv which is
*** available at:
*** http://ftp.gnu.org/gnu/libiconv/
***]])
fi
fi
if test "$die" = "yes"; then
AC_MSG_ERROR([[
***
*** Required libraries not found. Please consult the above messages
*** and install them before running configure again.
***]])
fi
AC_CONFIG_FILES([ m4/Makefile
Makefile
po/Makefile.in
gl/Makefile
common/Makefile
common/w32info-rc.h
kbx/Makefile
g10/Makefile
sm/Makefile
agent/Makefile
scd/Makefile
g13/Makefile
dirmngr/Makefile
tools/gpg-zip
tools/Makefile
doc/Makefile
tests/Makefile
tests/openpgp/Makefile
tests/pkits/Makefile
])
AC_OUTPUT
echo "
GnuPG v${VERSION} has been configured as follows:
Revision: mym4_revision (mym4_revision_dec)
Platform: $PRINTABLE_OS_NAME ($host)
OpenPGP: $build_gpg
S/MIME: $build_gpgsm
Agent: $build_agent
Smartcard: $build_scdaemon $build_scdaemon_extra
G13: $build_g13
Dirmngr: $build_dirmngr
Gpgtar: $build_gpgtar
Protect tool: $show_gnupg_protect_tool_pgm
LDAP wrapper: $show_gnupg_dirmngr_ldap_pgm
Default agent: $show_gnupg_agent_pgm
Default pinentry: $show_gnupg_pinentry_pgm
Default scdaemon: $show_gnupg_scdaemon_pgm
Default dirmngr: $show_gnupg_dirmngr_pgm
Use standard socket: $use_standard_socket
Dirmngr auto start: $dirmngr_auto_start
Readline support: $gnupg_cv_have_readline
DNS SRV support: $use_dns_srv
- TLS support: $have_gnutls
+ TLS support: $use_tls_library
"
if test x"$use_regex" != xyes ; then
echo "
Warning: No regular expression support available.
OpenPGP trust signatures won't work.
gpg-check-pattern will not be build.
"
fi
if test "x${gpg_config_script_warn}" != x; then
cat <<G10EOF
Warning: Mismatches between the target platform and the
to be used libraries have been detected for:
${gpg_config_script_warn}
Please check above for more warning messages.
G10EOF
fi
diff --git a/dirmngr/Makefile.am b/dirmngr/Makefile.am
index 7e2449fc3..d0226a3e8 100644
--- a/dirmngr/Makefile.am
+++ b/dirmngr/Makefile.am
@@ -1,92 +1,92 @@
# Makefile.am - dirmngr
# Copyright (C) 2002 Klarälvdalens Datakonsult AB
# Copyright (C) 2004, 2007, 2010 g10 Code GmbH
#
# This file is part of GnuPG.
#
# GnuPG is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# GnuPG is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, see <http://www.gnu.org/licenses/>.
## Process this file with automake to produce Makefile.in
EXTRA_DIST = OAUTHORS ONEWS ChangeLog.1 ChangeLog-2011
bin_PROGRAMS = dirmngr dirmngr-client
if USE_LDAPWRAPPER
libexec_PROGRAMS = dirmngr_ldap
endif
AM_CPPFLAGS = -I$(top_srcdir)/gl -I$(top_srcdir)/intl -I$(top_srcdir)/common
include $(top_srcdir)/am/cmacros.am
AM_CFLAGS = $(LIBGCRYPT_CFLAGS) $(KSBA_CFLAGS) \
$(LIBASSUAN_CFLAGS) $(GPG_ERROR_CFLAGS) $(NPTH_CFLAGS)
BUILT_SOURCES = no-libgcrypt.c
CLEANFILES = no-libgcrypt.c
if HAVE_W32_SYSTEM
ldap_url = ldap-url.h ldap-url.c
else
ldap_url =
endif
noinst_HEADERS = dirmngr.h crlcache.h crlfetch.h misc.h
dirmngr_SOURCES = dirmngr.c dirmngr.h server.c crlcache.c crlfetch.c \
ldapserver.h ldapserver.c certcache.c certcache.h \
cdb.h cdblib.c ldap.c misc.c dirmngr-err.h w32-ldap-help.h \
ocsp.c ocsp.h validate.c validate.h ldap-wrapper.h $(ldap_url) \
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
if USE_LDAPWRAPPER
dirmngr_SOURCES += ldap-wrapper.c
else
dirmngr_SOURCES += ldap-wrapper-ce.c dirmngr_ldap.c
endif
dirmngr_LDADD = $(libcommontlsnpth) $(libcommonpth) \
../gl/libgnu.a $(DNSLIBS) $(LIBASSUAN_LIBS) \
$(LIBGCRYPT_LIBS) $(KSBA_LIBS) $(NPTH_LIBS) \
- $(LIBGNUTLS_LIBS) $(LIBINTL) $(LIBICONV)
+ $(NTBTLS_LIBS) $(LIBGNUTLS_LIBS) $(LIBINTL) $(LIBICONV)
if !USE_LDAPWRAPPER
dirmngr_LDADD += $(LDAPLIBS)
endif
dirmngr_LDFLAGS = $(extra_bin_ldflags)
if USE_LDAPWRAPPER
dirmngr_ldap_SOURCES = dirmngr_ldap.c $(ldap_url)
dirmngr_ldap_CFLAGS = $(GPG_ERROR_CFLAGS) $(LIBGCRYPT_CFLAGS)
dirmngr_ldap_LDFLAGS =
dirmngr_ldap_LDADD = $(libcommon) no-libgcrypt.o ../gl/libgnu.a $(DNSLIBS) \
$(GPG_ERROR_LIBS) $(LDAPLIBS) $(LBER_LIBS) $(LIBINTL) \
$(LIBICONV)
endif
dirmngr_client_SOURCES = dirmngr-client.c
dirmngr_client_LDADD = $(libcommon) no-libgcrypt.o \
../gl/libgnu.a $(LIBASSUAN_LIBS) \
$(GPG_ERROR_LIBS) $(NETLIBS) $(LIBINTL) $(LIBICONV)
dirmngr_client_LDFLAGS = $(extra_bin_ldflags)
no-libgcrypt.c : $(top_srcdir)/tools/no-libgcrypt.c
cat $(top_srcdir)/tools/no-libgcrypt.c > no-libgcrypt.c
$(PROGRAMS) : $(libcommon) $(libcommonpth) $(libcommontls) $(libcommontlsnpth)
diff --git a/dirmngr/dirmngr.c b/dirmngr/dirmngr.c
index 48fa80be7..8110df235 100644
--- a/dirmngr/dirmngr.c
+++ b/dirmngr/dirmngr.c
@@ -1,1986 +1,2001 @@
/* dirmngr.c - LDAP access
* Copyright (C) 2002 Klarälvdalens Datakonsult AB
* Copyright (C) 2003, 2004, 2006, 2007, 2008, 2010, 2011 g10 Code GmbH
* Copyright (C) 2014 Werner Koch
*
* This file is part of GnuPG.
*
* GnuPG is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GnuPG is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <stdarg.h>
#include <string.h>
#include <errno.h>
#include <assert.h>
#include <time.h>
#include <fcntl.h>
#ifndef HAVE_W32_SYSTEM
#include <sys/socket.h>
#include <sys/un.h>
#endif
#include <sys/stat.h>
#include <unistd.h>
#ifdef HAVE_SIGNAL_H
# include <signal.h>
#endif
#include <npth.h>
-#ifdef HTTP_USE_GNUTLS
+
+#include "dirmngr-err.h"
+
+#if HTTP_USE_NTBTLS
+# include <ntbtls.h>
+#elif HTTP_USE_GNUTLS
# include <gnutls/gnutls.h>
#endif /*HTTP_USE_GNUTLS*/
#define JNLIB_NEED_LOG_LOGV
#define JNLIB_NEED_AFLOCAL
#include "dirmngr.h"
#include <assuan.h>
#include "certcache.h"
#include "crlcache.h"
#include "crlfetch.h"
#include "misc.h"
#include "ldapserver.h"
#include "asshelp.h"
#include "ldap-wrapper.h"
#include "../common/init.h"
#include "gc-opt-flags.h"
/* The plain Windows version uses the windows service system. For
example to start the service you may use "sc start dirmngr".
WindowsCE does not support this; the service system over there is
based on a single process with all services being DLLs - we can't
support this easily. */
#if defined(HAVE_W32_SYSTEM) && !defined(HAVE_W32CE_SYSTEM)
# define USE_W32_SERVICE 1
#endif
enum cmd_and_opt_values {
aNull = 0,
oCsh = 'c',
oQuiet = 'q',
oSh = 's',
oVerbose = 'v',
oNoVerbose = 500,
aServer,
aDaemon,
aService,
aListCRLs,
aLoadCRL,
aFetchCRL,
aShutdown,
aFlush,
aGPGConfList,
aGPGConfTest,
oOptions,
oDebug,
oDebugAll,
oDebugWait,
oDebugLevel,
oGnutlsDebug,
oNoGreeting,
oNoOptions,
oHomedir,
oNoDetach,
oLogFile,
oBatch,
oDisableHTTP,
oDisableLDAP,
oIgnoreLDAPDP,
oIgnoreHTTPDP,
oIgnoreOCSPSvcUrl,
oHonorHTTPProxy,
oHTTPProxy,
oLDAPProxy,
oOnlyLDAPProxy,
oLDAPFile,
oLDAPTimeout,
oLDAPAddServers,
oOCSPResponder,
oOCSPSigner,
oOCSPMaxClockSkew,
oOCSPMaxPeriod,
oOCSPCurrentPeriod,
oMaxReplies,
oHkpCaCert,
oFakedSystemTime,
oForce,
oAllowOCSP,
oSocketName,
oLDAPWrapperProgram,
oHTTPWrapperProgram,
oIgnoreCertExtension,
aTest
};
static ARGPARSE_OPTS opts[] = {
ARGPARSE_group (300, N_("@Commands:\n ")),
ARGPARSE_c (aServer, "server", N_("run in server mode (foreground)") ),
ARGPARSE_c (aDaemon, "daemon", N_("run in daemon mode (background)") ),
#ifdef USE_W32_SERVICE
ARGPARSE_c (aService, "service", N_("run as windows service (background)")),
#endif
ARGPARSE_c (aListCRLs, "list-crls", N_("list the contents of the CRL cache")),
ARGPARSE_c (aLoadCRL, "load-crl", N_("|FILE|load CRL from FILE into cache")),
ARGPARSE_c (aFetchCRL, "fetch-crl", N_("|URL|fetch a CRL from URL")),
ARGPARSE_c (aShutdown, "shutdown", N_("shutdown the dirmngr")),
ARGPARSE_c (aFlush, "flush", N_("flush the cache")),
ARGPARSE_c (aGPGConfList, "gpgconf-list", "@"),
ARGPARSE_c (aGPGConfTest, "gpgconf-test", "@"),
ARGPARSE_group (301, N_("@\nOptions:\n ")),
ARGPARSE_s_n (oVerbose, "verbose", N_("verbose")),
ARGPARSE_s_n (oQuiet, "quiet", N_("be somewhat more quiet")),
ARGPARSE_s_n (oSh, "sh", N_("sh-style command output")),
ARGPARSE_s_n (oCsh, "csh", N_("csh-style command output")),
ARGPARSE_s_s (oOptions, "options", N_("|FILE|read options from FILE")),
ARGPARSE_s_s (oDebugLevel, "debug-level",
N_("|LEVEL|set the debugging level to LEVEL")),
ARGPARSE_s_n (oNoDetach, "no-detach", N_("do not detach from the console")),
ARGPARSE_s_s (oLogFile, "log-file",
N_("|FILE|write server mode logs to FILE")),
ARGPARSE_s_n (oBatch, "batch", N_("run without asking a user")),
ARGPARSE_s_n (oForce, "force", N_("force loading of outdated CRLs")),
ARGPARSE_s_n (oAllowOCSP, "allow-ocsp", N_("allow sending OCSP requests")),
ARGPARSE_s_n (oDisableHTTP, "disable-http", N_("inhibit the use of HTTP")),
ARGPARSE_s_n (oDisableLDAP, "disable-ldap", N_("inhibit the use of LDAP")),
ARGPARSE_s_n (oIgnoreHTTPDP,"ignore-http-dp",
N_("ignore HTTP CRL distribution points")),
ARGPARSE_s_n (oIgnoreLDAPDP,"ignore-ldap-dp",
N_("ignore LDAP CRL distribution points")),
ARGPARSE_s_n (oIgnoreOCSPSvcUrl, "ignore-ocsp-service-url",
N_("ignore certificate contained OCSP service URLs")),
ARGPARSE_s_s (oHTTPProxy, "http-proxy",
N_("|URL|redirect all HTTP requests to URL")),
ARGPARSE_s_s (oLDAPProxy, "ldap-proxy",
N_("|HOST|use HOST for LDAP queries")),
ARGPARSE_s_n (oOnlyLDAPProxy, "only-ldap-proxy",
N_("do not use fallback hosts with --ldap-proxy")),
ARGPARSE_s_s (oLDAPFile, "ldapserverlist-file",
N_("|FILE|read LDAP server list from FILE")),
ARGPARSE_s_n (oLDAPAddServers, "add-servers",
N_("add new servers discovered in CRL distribution"
" points to serverlist")),
ARGPARSE_s_i (oLDAPTimeout, "ldaptimeout",
N_("|N|set LDAP timeout to N seconds")),
ARGPARSE_s_s (oOCSPResponder, "ocsp-responder",
N_("|URL|use OCSP responder at URL")),
ARGPARSE_s_s (oOCSPSigner, "ocsp-signer",
N_("|FPR|OCSP response signed by FPR")),
ARGPARSE_s_i (oOCSPMaxClockSkew, "ocsp-max-clock-skew", "@"),
ARGPARSE_s_i (oOCSPMaxPeriod, "ocsp-max-period", "@"),
ARGPARSE_s_i (oOCSPCurrentPeriod, "ocsp-current-period", "@"),
ARGPARSE_s_i (oMaxReplies, "max-replies",
N_("|N|do not return more than N items in one query")),
ARGPARSE_s_s (oHkpCaCert, "hkp-cacert",
N_("|FILE|use the CA certificates in FILE for HKP over TLS")),
ARGPARSE_s_s (oSocketName, "socket-name", "@"), /* Only for debugging. */
ARGPARSE_s_u (oFakedSystemTime, "faked-system-time", "@"), /*(epoch time)*/
ARGPARSE_p_u (oDebug, "debug", "@"),
ARGPARSE_s_n (oDebugAll, "debug-all", "@"),
ARGPARSE_s_i (oGnutlsDebug, "gnutls-debug", "@"),
+ ARGPARSE_s_i (oGnutlsDebug, "tls-debug", "@"),
ARGPARSE_s_i (oDebugWait, "debug-wait", "@"),
ARGPARSE_s_n (oNoGreeting, "no-greeting", "@"),
ARGPARSE_s_s (oHomedir, "homedir", "@"),
ARGPARSE_s_s (oLDAPWrapperProgram, "ldap-wrapper-program", "@"),
ARGPARSE_s_s (oHTTPWrapperProgram, "http-wrapper-program", "@"),
ARGPARSE_s_n (oHonorHTTPProxy, "honor-http-proxy", "@"),
ARGPARSE_s_s (oIgnoreCertExtension,"ignore-cert-extension", "@"),
ARGPARSE_group (302,N_("@\n(See the \"info\" manual for a complete listing "
"of all commands and options)\n")),
ARGPARSE_end ()
};
#define DEFAULT_MAX_REPLIES 10
#define DEFAULT_LDAP_TIMEOUT 100 /* arbitrary large timeout */
/* For the cleanup handler we need to keep track of the socket's name. */
static const char *socket_name;
/* We need to keep track of the server's nonces (these are dummies for
POSIX systems). */
static assuan_sock_nonce_t socket_nonce;
/* Only if this flag has been set we will remove the socket file. */
static int cleanup_socket;
/* Keep track of the current log file so that we can avoid updating
the log file after a SIGHUP if it didn't changed. Malloced. */
static char *current_logfile;
/* Helper to implement --debug-level. */
static const char *debug_level;
-/* Helper to set the GNUTLS log level. */
+/* Helper to set the NTBTLS or GNUTLS log level. */
static int opt_gnutls_debug = -1;
/* Flag indicating that a shutdown has been requested. */
static volatile int shutdown_pending;
/* Counter for the active connections. */
static int active_connections;
/* The timer tick used for housekeeping stuff. For Windows we use a
longer period as the SetWaitableTimer seems to signal earlier than
the 2 seconds. All values are in seconds. */
#if defined(HAVE_W32CE_SYSTEM)
# define TIMERTICK_INTERVAL (60)
#elif defined(HAVE_W32_SYSTEM)
# define TIMERTICK_INTERVAL (4)
#else
# define TIMERTICK_INTERVAL (2)
#endif
#define HOUSEKEEPING_INTERVAL (600)
/* This union is used to avoid compiler warnings in case a pointer is
64 bit and an int 32 bit. We store an integer in a pointer and get
it back later (npth_getspecific et al.). */
union int_and_ptr_u
{
int aint;
assuan_fd_t afd;
void *aptr;
};
/* The key used to store the current file descriptor in the thread
local storage. We use this in conjunction with the
log_set_pid_suffix_cb feature.. */
#ifndef HAVE_W32_SYSTEM
static int my_tlskey_current_fd;
#endif
/* Prototypes. */
static void cleanup (void);
static ldap_server_t parse_ldapserver_file (const char* filename);
static fingerprint_list_t parse_ocsp_signer (const char *string);
static void handle_connections (assuan_fd_t listen_fd);
/* NPth wrapper function definitions. */
ASSUAN_SYSTEM_NPTH_IMPL;
static const char *
my_strusage( int level )
{
const char *p;
switch ( level )
{
case 11: p = "@DIRMNGR@ (@GNUPG@)";
break;
case 13: p = VERSION; break;
case 17: p = PRINTABLE_OS_NAME; break;
/* TRANSLATORS: @EMAIL@ will get replaced by the actual bug
reporting address. This is so that we can change the
reporting address without breaking the translations. */
case 19: p = _("Please report bugs to <@EMAIL@>.\n"); break;
case 49: p = PACKAGE_BUGREPORT; break;
case 1:
case 40: p = _("Usage: @DIRMNGR@ [options] (-h for help)");
break;
case 41: p = _("Syntax: @DIRMNGR@ [options] [command [args]]\n"
"LDAP and OCSP access for @GNUPG@\n");
break;
default: p = NULL;
}
return p;
}
/* Callback from libksba to hash a provided buffer. Our current
implementation does only allow SHA-1 for hashing. This may be
extended by mapping the name, testing for algorithm availibility
and adjust the length checks accordingly. */
static gpg_error_t
my_ksba_hash_buffer (void *arg, const char *oid,
const void *buffer, size_t length, size_t resultsize,
unsigned char *result, size_t *resultlen)
{
(void)arg;
if (oid && strcmp (oid, "1.3.14.3.2.26"))
return gpg_error (GPG_ERR_NOT_SUPPORTED);
if (resultsize < 20)
return gpg_error (GPG_ERR_BUFFER_TOO_SHORT);
gcry_md_hash_buffer (2, result, buffer, length);
*resultlen = 20;
return 0;
}
/* GNUTLS log function callback. */
static void
my_gnutls_log (int level, const char *text)
{
int n;
n = strlen (text);
while (n && text[n-1] == '\n')
n--;
log_debug ("gnutls:L%d: %.*s\n", level, n, text);
}
/* Setup the debugging. With a LEVEL of NULL only the active debug
flags are propagated to the subsystems. With LEVEL set, a specific
set of debug flags is set; thus overriding all flags already
set. */
static void
set_debug (void)
{
int numok = (debug_level && digitp (debug_level));
int numlvl = numok? atoi (debug_level) : 0;
if (!debug_level)
;
else if (!strcmp (debug_level, "none") || (numok && numlvl < 1))
opt.debug = 0;
else if (!strcmp (debug_level, "basic") || (numok && numlvl <= 2))
opt.debug = DBG_ASSUAN_VALUE;
else if (!strcmp (debug_level, "advanced") || (numok && numlvl <= 5))
opt.debug = (DBG_ASSUAN_VALUE|DBG_X509_VALUE|DBG_LOOKUP_VALUE);
else if (!strcmp (debug_level, "expert") || (numok && numlvl <= 8))
opt.debug = (DBG_ASSUAN_VALUE|DBG_X509_VALUE|DBG_LOOKUP_VALUE
|DBG_CACHE_VALUE|DBG_CRYPTO_VALUE);
else if (!strcmp (debug_level, "guru") || numok)
{
opt.debug = ~0;
/* Unless the "guru" string has been used we don't want to allow
hashing debugging. The rationale is that people tend to
select the highest debug value and would then clutter their
disk with debug files which may reveal confidential data. */
if (numok)
opt.debug &= ~(DBG_HASHING_VALUE);
}
else
{
log_error (_("invalid debug-level '%s' given\n"), debug_level);
log_info (_("valid debug levels are: %s\n"),
"none, basic, advanced, expert, guru");
opt.debug = 0; /* Reset debugging, so that prior debug
statements won't have an undesired effect. */
}
if (opt.debug && !opt.verbose)
{
opt.verbose = 1;
gcry_control (GCRYCTL_SET_VERBOSITY, (int)opt.verbose);
}
if (opt.debug && opt.quiet)
opt.quiet = 0;
if (opt.debug & DBG_CRYPTO_VALUE )
gcry_control (GCRYCTL_SET_DEBUG_FLAGS, 1);
-#ifdef HTTP_USE_GNUTLS
+#if HTTP_USE_NTBTLS
+ if (opt_gnutls_debug >= 0)
+ {
+ ntbtls_set_debug (opt_gnutls_debug, NULL, NULL);
+ }
+#elif HTTP_USE_GNUTLS
if (opt_gnutls_debug >= 0)
{
gnutls_global_set_log_function (my_gnutls_log);
gnutls_global_set_log_level (opt_gnutls_debug);
}
#endif /*HTTP_USE_GNUTLS*/
}
static void
wrong_args (const char *text)
{
es_fprintf (es_stderr, _("usage: %s [options] "), DIRMNGR_NAME);
es_fputs (text, es_stderr);
es_putc ('\n', es_stderr);
dirmngr_exit (2);
}
/* Helper to stop the reaper thread for the ldap wrapper. */
static void
shutdown_reaper (void)
{
ldap_wrapper_wait_connections ();
}
/* Handle options which are allowed to be reset after program start.
Return true if the current option in PARGS could be handled and
false if not. As a special feature, passing a value of NULL for
PARGS, resets the options to the default. REREAD should be set
true if it is not the initial option parsing. */
static int
parse_rereadable_options (ARGPARSE_ARGS *pargs, int reread)
{
if (!pargs)
{ /* Reset mode. */
opt.quiet = 0;
opt.verbose = 0;
opt.debug = 0;
opt.ldap_wrapper_program = NULL;
opt.disable_http = 0;
opt.disable_ldap = 0;
opt.honor_http_proxy = 0;
opt.http_proxy = NULL;
opt.ldap_proxy = NULL;
opt.only_ldap_proxy = 0;
opt.ignore_http_dp = 0;
opt.ignore_ldap_dp = 0;
opt.ignore_ocsp_service_url = 0;
opt.allow_ocsp = 0;
opt.ocsp_responder = NULL;
opt.ocsp_max_clock_skew = 10 * 60; /* 10 minutes. */
opt.ocsp_max_period = 90 * 86400; /* 90 days. */
opt.ocsp_current_period = 3 * 60 * 60; /* 3 hours. */
opt.max_replies = DEFAULT_MAX_REPLIES;
while (opt.ocsp_signer)
{
fingerprint_list_t tmp = opt.ocsp_signer->next;
xfree (opt.ocsp_signer);
opt.ocsp_signer = tmp;
}
FREE_STRLIST (opt.ignored_cert_extensions);
http_register_tls_ca (NULL);
return 1;
}
switch (pargs->r_opt)
{
case oQuiet: opt.quiet = 1; break;
case oVerbose: opt.verbose++; break;
case oDebug: opt.debug |= pargs->r.ret_ulong; break;
case oDebugAll: opt.debug = ~0; break;
case oDebugLevel: debug_level = pargs->r.ret_str; break;
case oGnutlsDebug: opt_gnutls_debug = pargs->r.ret_int; break;
case oLogFile:
if (!reread)
return 0; /* Not handled. */
if (!current_logfile || !pargs->r.ret_str
|| strcmp (current_logfile, pargs->r.ret_str))
{
log_set_file (pargs->r.ret_str);
xfree (current_logfile);
current_logfile = xtrystrdup (pargs->r.ret_str);
}
break;
case oLDAPWrapperProgram:
opt.ldap_wrapper_program = pargs->r.ret_str;
break;
case oHTTPWrapperProgram:
opt.http_wrapper_program = pargs->r.ret_str;
break;
case oDisableHTTP: opt.disable_http = 1; break;
case oDisableLDAP: opt.disable_ldap = 1; break;
case oHonorHTTPProxy: opt.honor_http_proxy = 1; break;
case oHTTPProxy: opt.http_proxy = pargs->r.ret_str; break;
case oLDAPProxy: opt.ldap_proxy = pargs->r.ret_str; break;
case oOnlyLDAPProxy: opt.only_ldap_proxy = 1; break;
case oIgnoreHTTPDP: opt.ignore_http_dp = 1; break;
case oIgnoreLDAPDP: opt.ignore_ldap_dp = 1; break;
case oIgnoreOCSPSvcUrl: opt.ignore_ocsp_service_url = 1; break;
case oAllowOCSP: opt.allow_ocsp = 1; break;
case oOCSPResponder: opt.ocsp_responder = pargs->r.ret_str; break;
case oOCSPSigner:
opt.ocsp_signer = parse_ocsp_signer (pargs->r.ret_str);
break;
case oOCSPMaxClockSkew: opt.ocsp_max_clock_skew = pargs->r.ret_int; break;
case oOCSPMaxPeriod: opt.ocsp_max_period = pargs->r.ret_int; break;
case oOCSPCurrentPeriod: opt.ocsp_current_period = pargs->r.ret_int; break;
case oMaxReplies: opt.max_replies = pargs->r.ret_int; break;
case oHkpCaCert:
http_register_tls_ca (pargs->r.ret_str);
break;
case oIgnoreCertExtension:
add_to_strlist (&opt.ignored_cert_extensions, pargs->r.ret_str);
break;
default:
return 0; /* Not handled. */
}
return 1; /* Handled. */
}
#ifdef USE_W32_SERVICE
/* The global status of our service. */
SERVICE_STATUS_HANDLE service_handle;
SERVICE_STATUS service_status;
DWORD WINAPI
w32_service_control (DWORD control, DWORD event_type, LPVOID event_data,
LPVOID context)
{
(void)event_type;
(void)event_data;
(void)context;
/* event_type and event_data are not used here. */
switch (control)
{
case SERVICE_CONTROL_SHUTDOWN:
/* For shutdown we will try to force termination. */
service_status.dwCurrentState = SERVICE_STOP_PENDING;
SetServiceStatus (service_handle, &service_status);
shutdown_pending = 3;
break;
case SERVICE_CONTROL_STOP:
service_status.dwCurrentState = SERVICE_STOP_PENDING;
SetServiceStatus (service_handle, &service_status);
shutdown_pending = 1;
break;
default:
break;
}
return 0;
}
#endif /*USE_W32_SERVICE*/
#ifndef HAVE_W32_SYSTEM
static int
pid_suffix_callback (unsigned long *r_suffix)
{
union int_and_ptr_u value;
value.aptr = npth_getspecific (my_tlskey_current_fd);
*r_suffix = value.aint;
return (*r_suffix != -1); /* Use decimal representation. */
}
#endif /*!HAVE_W32_SYSTEM*/
#ifdef USE_W32_SERVICE
# define main real_main
#endif
int
main (int argc, char **argv)
{
#ifdef USE_W32_SERVICE
# undef main
#endif
enum cmd_and_opt_values cmd = 0;
ARGPARSE_ARGS pargs;
int orig_argc;
char **orig_argv;
FILE *configfp = NULL;
char *configname = NULL;
const char *shell;
unsigned configlineno;
int parse_debug = 0;
int default_config =1;
int greeting = 0;
int nogreeting = 0;
int nodetach = 0;
int csh_style = 0;
char *logfile = NULL;
char *ldapfile = NULL;
int debug_wait = 0;
int rc;
int homedir_seen = 0;
struct assuan_malloc_hooks malloc_hooks;
#ifdef USE_W32_SERVICE
/* The option will be set by main() below if we should run as a
system daemon. */
if (opt.system_service)
{
service_handle
= RegisterServiceCtrlHandlerEx ("DirMngr",
&w32_service_control, NULL /*FIXME*/);
if (service_handle == 0)
log_error ("failed to register service control handler: ec=%d",
(int) GetLastError ());
service_status.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
service_status.dwCurrentState = SERVICE_START_PENDING;
service_status.dwControlsAccepted
= SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
service_status.dwWin32ExitCode = NO_ERROR;
service_status.dwServiceSpecificExitCode = NO_ERROR;
service_status.dwCheckPoint = 0;
service_status.dwWaitHint = 10000; /* 10 seconds timeout. */
SetServiceStatus (service_handle, &service_status);
}
#endif /*USE_W32_SERVICE*/
set_strusage (my_strusage);
log_set_prefix (DIRMNGR_NAME, 1|4);
/* Make sure that our subsystems are ready. */
i18n_init ();
init_common_subsystems (&argc, &argv);
npth_init ();
gcry_control (GCRYCTL_DISABLE_SECMEM, 0);
/* Check that the libraries are suitable. Do it here because
the option parsing may need services of the libraries. */
if (!gcry_check_version (NEED_LIBGCRYPT_VERSION) )
log_fatal (_("%s is too old (need %s, have %s)\n"), "libgcrypt",
NEED_LIBGCRYPT_VERSION, gcry_check_version (NULL) );
if (!ksba_check_version (NEED_KSBA_VERSION) )
log_fatal( _("%s is too old (need %s, have %s)\n"), "libksba",
NEED_KSBA_VERSION, ksba_check_version (NULL) );
ksba_set_malloc_hooks (gcry_malloc, gcry_realloc, gcry_free );
ksba_set_hash_buffer_function (my_ksba_hash_buffer, NULL);
- /* Init GNUTLS. */
-#ifdef HTTP_USE_GNUTLS
+ /* Init TLS library. */
+#if HTTP_USE_NTBTLS
+ if (!ntbtls_check_version (NEED_NTBTLS_VERSION) )
+ log_fatal( _("%s is too old (need %s, have %s)\n"), "ntbtls",
+ NEED_NTBTLS_VERSION, ntbtls_check_version (NULL) );
+#elif HTTP_USE_GNUTLS
rc = gnutls_global_init ();
if (rc)
log_fatal ("gnutls_global_init failed: %s\n", gnutls_strerror (rc));
#endif /*HTTP_USE_GNUTLS*/
/* Init Assuan. */
malloc_hooks.malloc = gcry_malloc;
malloc_hooks.realloc = gcry_realloc;
malloc_hooks.free = gcry_free;
assuan_set_malloc_hooks (&malloc_hooks);
assuan_set_assuan_log_prefix (log_get_prefix (NULL));
assuan_set_gpg_err_source (GPG_ERR_SOURCE_DEFAULT);
assuan_set_system_hooks (ASSUAN_SYSTEM_NPTH);
assuan_sock_init ();
setup_libassuan_logging (&opt.debug);
setup_libgcrypt_logging ();
/* Setup defaults. */
shell = getenv ("SHELL");
if (shell && strlen (shell) >= 3 && !strcmp (shell+strlen (shell)-3, "csh") )
csh_style = 1;
opt.homedir = default_homedir ();
/* Now with NPth running we can set the logging callback. Our
windows implementation does not yet feature the NPth TLS
functions. */
#ifndef HAVE_W32_SYSTEM
if (npth_key_create (&my_tlskey_current_fd, NULL) == 0)
if (npth_setspecific (my_tlskey_current_fd, NULL) == 0)
log_set_pid_suffix_cb (pid_suffix_callback);
#endif /*!HAVE_W32_SYSTEM*/
/* Reset rereadable options to default values. */
parse_rereadable_options (NULL, 0);
/* LDAP defaults. */
opt.add_new_ldapservers = 0;
opt.ldaptimeout = DEFAULT_LDAP_TIMEOUT;
/* Other defaults. */
/* Check whether we have a config file given on the commandline */
orig_argc = argc;
orig_argv = argv;
pargs.argc = &argc;
pargs.argv = &argv;
pargs.flags= 1|(1<<6); /* do not remove the args, ignore version */
while (arg_parse( &pargs, opts))
{
if (pargs.r_opt == oDebug || pargs.r_opt == oDebugAll)
parse_debug++;
else if (pargs.r_opt == oOptions)
{ /* Yes there is one, so we do not try the default one, but
read the option file when it is encountered at the
commandline */
default_config = 0;
}
else if (pargs.r_opt == oNoOptions)
default_config = 0; /* --no-options */
else if (pargs.r_opt == oHomedir)
{
opt.homedir = pargs.r.ret_str;
homedir_seen = 1;
}
else if (pargs.r_opt == aDaemon)
opt.system_daemon = 1;
else if (pargs.r_opt == aService)
{
/* Redundant. The main function takes care of it. */
opt.system_service = 1;
opt.system_daemon = 1;
}
#ifdef HAVE_W32_SYSTEM
else if (pargs.r_opt == aGPGConfList || pargs.r_opt == aGPGConfTest)
/* We set this so we switch to the system configuration
directory below. This is a crutch to solve the problem
that the user configuration is never used on Windows. Also
see below at aGPGConfList. */
opt.system_daemon = 1;
#endif
}
/* If --daemon has been given on the command line but not --homedir,
we switch to /etc/gnupg as default home directory. Note, that
this also overrides the GNUPGHOME environment variable. */
if (opt.system_daemon && !homedir_seen)
{
#ifdef HAVE_W32CE_SYSTEM
opt.homedir = DIRSEP_S "gnupg";
#else
opt.homedir = gnupg_sysconfdir ();
#endif
opt.homedir_data = gnupg_datadir ();
opt.homedir_cache = gnupg_cachedir ();
socket_name = dirmngr_sys_socket_name ();
}
else if (dirmngr_user_socket_name ())
socket_name = dirmngr_user_socket_name ();
else
socket_name = dirmngr_sys_socket_name ();
if (default_config)
configname = make_filename (opt.homedir, DIRMNGR_NAME".conf", NULL );
argc = orig_argc;
argv = orig_argv;
pargs.argc = &argc;
pargs.argv = &argv;
pargs.flags= 1; /* do not remove the args */
next_pass:
if (configname)
{
configlineno = 0;
configfp = fopen (configname, "r");
if (!configfp)
{
if (default_config)
{
if( parse_debug )
log_info (_("NOTE: no default option file '%s'\n"),
configname );
}
else
{
log_error (_("option file '%s': %s\n"),
configname, strerror(errno) );
exit(2);
}
xfree (configname);
configname = NULL;
}
if (parse_debug && configname )
log_info (_("reading options from '%s'\n"), configname );
default_config = 0;
}
while (optfile_parse( configfp, configname, &configlineno, &pargs, opts) )
{
if (parse_rereadable_options (&pargs, 0))
continue; /* Already handled */
switch (pargs.r_opt)
{
case aServer:
case aDaemon:
case aService:
case aShutdown:
case aFlush:
case aListCRLs:
case aLoadCRL:
case aFetchCRL:
case aGPGConfList:
case aGPGConfTest:
cmd = pargs.r_opt;
break;
case oQuiet: opt.quiet = 1; break;
case oVerbose: opt.verbose++; break;
case oBatch: opt.batch=1; break;
case oDebug: opt.debug |= pargs.r.ret_ulong; break;
case oDebugAll: opt.debug = ~0; break;
case oDebugLevel: debug_level = pargs.r.ret_str; break;
case oDebugWait: debug_wait = pargs.r.ret_int; break;
case oOptions:
/* Config files may not be nested (silently ignore them) */
if (!configfp)
{
xfree(configname);
configname = xstrdup(pargs.r.ret_str);
goto next_pass;
}
break;
case oNoGreeting: nogreeting = 1; break;
case oNoVerbose: opt.verbose = 0; break;
case oNoOptions: break; /* no-options */
case oHomedir: /* Ignore this option here. */; break;
case oNoDetach: nodetach = 1; break;
case oLogFile: logfile = pargs.r.ret_str; break;
case oCsh: csh_style = 1; break;
case oSh: csh_style = 0; break;
case oLDAPFile: ldapfile = pargs.r.ret_str; break;
case oLDAPAddServers: opt.add_new_ldapservers = 1; break;
case oLDAPTimeout:
opt.ldaptimeout = pargs.r.ret_int;
break;
case oFakedSystemTime:
gnupg_set_time ((time_t)pargs.r.ret_ulong, 0);
break;
case oForce: opt.force = 1; break;
case oSocketName: socket_name = pargs.r.ret_str; break;
default : pargs.err = configfp? 1:2; break;
}
}
if (configfp)
{
fclose (configfp);
configfp = NULL;
/* Keep a copy of the name so that it can be read on SIGHUP. */
opt.config_filename = configname;
configname = NULL;
goto next_pass;
}
xfree (configname);
configname = NULL;
if (log_get_errorcount(0))
exit(2);
if (nogreeting )
greeting = 0;
if (!opt.homedir_data)
opt.homedir_data = opt.homedir;
if (!opt.homedir_cache)
opt.homedir_cache = opt.homedir;
if (greeting)
{
es_fprintf (es_stderr, "%s %s; %s\n",
strusage(11), strusage(13), strusage(14) );
es_fprintf (es_stderr, "%s\n", strusage(15) );
}
#ifdef IS_DEVELOPMENT_VERSION
log_info ("NOTE: this is a development version!\n");
#endif
/* Print a warning if an argument looks like an option. */
if (!opt.quiet && !(pargs.flags & ARGPARSE_FLAG_STOP_SEEN))
{
int i;
for (i=0; i < argc; i++)
if (argv[i][0] == '-' && argv[i][1] == '-')
log_info (_("NOTE: '%s' is not considered an option\n"), argv[i]);
}
if (!access ("/etc/"DIRMNGR_NAME, F_OK) && !strncmp (opt.homedir, "/etc/", 5))
log_info
("NOTE: DirMngr is now a proper part of %s. The configuration and"
" other directory names changed. Please check that no other version"
" of dirmngr is still installed. To disable this warning, remove the"
" directory '/etc/dirmngr'.\n", GNUPG_NAME);
if (gnupg_faked_time_p ())
{
gnupg_isotime_t tbuf;
log_info (_("WARNING: running with faked system time: "));
gnupg_get_isotime (tbuf);
dump_isotime (tbuf);
log_printf ("\n");
}
set_debug ();
/* Get LDAP server list from file. */
if (!ldapfile)
{
ldapfile = make_filename (opt.homedir,
opt.system_daemon?
"ldapservers.conf":"dirmngr_ldapservers.conf",
NULL);
opt.ldapservers = parse_ldapserver_file (ldapfile);
xfree (ldapfile);
}
else
opt.ldapservers = parse_ldapserver_file (ldapfile);
#ifndef HAVE_W32_SYSTEM
/* We need to ignore the PIPE signal because the we might log to a
socket and that code handles EPIPE properly. The ldap wrapper
also requires us to ignore this silly signal. Assuan would set
this signal to ignore anyway.*/
signal (SIGPIPE, SIG_IGN);
#endif
/* Ready. Now to our duties. */
if (!cmd && opt.system_service)
cmd = aDaemon;
else if (!cmd)
cmd = aServer;
rc = 0;
if (cmd == aServer)
{
/* Note that this server mode is mainly useful for debugging. */
if (argc)
wrong_args ("--server");
if (logfile)
{
log_set_file (logfile);
log_set_prefix (NULL, 2|4);
}
if (debug_wait)
{
log_debug ("waiting for debugger - my pid is %u .....\n",
(unsigned int)getpid());
gnupg_sleep (debug_wait);
log_debug ("... okay\n");
}
ldap_wrapper_launch_thread ();
cert_cache_init ();
crl_cache_init ();
start_command_handler (ASSUAN_INVALID_FD);
shutdown_reaper ();
}
else if (cmd == aDaemon)
{
assuan_fd_t fd;
pid_t pid;
int len;
struct sockaddr_un serv_addr;
if (argc)
wrong_args ("--daemon");
/* Now start with logging to a file if this is desired. */
if (logfile)
{
log_set_file (logfile);
log_set_prefix (NULL, (JNLIB_LOG_WITH_PREFIX
|JNLIB_LOG_WITH_TIME
|JNLIB_LOG_WITH_PID));
current_logfile = xstrdup (logfile);
}
#ifndef HAVE_W32_SYSTEM
if (strchr (socket_name, ':'))
{
log_error (_("colons are not allowed in the socket name\n"));
dirmngr_exit (1);
}
#endif
if (strlen (socket_name)+1 >= sizeof serv_addr.sun_path )
{
log_error (_("name of socket too long\n"));
dirmngr_exit (1);
}
fd = assuan_sock_new (AF_UNIX, SOCK_STREAM, 0);
if (fd == ASSUAN_INVALID_FD)
{
log_error (_("can't create socket: %s\n"), strerror (errno));
cleanup ();
dirmngr_exit (1);
}
memset (&serv_addr, 0, sizeof serv_addr);
serv_addr.sun_family = AF_UNIX;
strcpy (serv_addr.sun_path, socket_name);
len = SUN_LEN (&serv_addr);
rc = assuan_sock_bind (fd, (struct sockaddr*) &serv_addr, len);
if (rc == -1
&& (errno == EADDRINUSE
#ifdef HAVE_W32_SYSTEM
|| errno == EEXIST
#endif
))
{
/* Fixme: We should test whether a dirmngr is already running. */
gnupg_remove (socket_name);
rc = assuan_sock_bind (fd, (struct sockaddr*) &serv_addr, len);
}
if (rc != -1
&& (rc = assuan_sock_get_nonce ((struct sockaddr*) &serv_addr, len, &socket_nonce)))
log_error (_("error getting nonce for the socket\n"));
if (rc == -1)
{
log_error (_("error binding socket to '%s': %s\n"),
serv_addr.sun_path, gpg_strerror (gpg_error_from_errno (errno)));
assuan_sock_close (fd);
dirmngr_exit (1);
}
cleanup_socket = 1;
if (listen (FD2INT (fd), 5) == -1)
{
log_error (_("listen() failed: %s\n"), strerror (errno));
assuan_sock_close (fd);
dirmngr_exit (1);
}
if (opt.verbose)
log_info (_("listening on socket '%s'\n"), socket_name );
es_fflush (NULL);
/* Note: We keep the dirmngr_info output only for the sake of
existing scripts which might use this to detect a successful
start of the dirmngr. */
#ifdef HAVE_W32_SYSTEM
(void)csh_style;
(void)nodetach;
pid = getpid ();
es_printf ("set %s=%s;%lu;1\n",
DIRMNGR_INFO_NAME, socket_name, (ulong) pid);
#else
pid = fork();
if (pid == (pid_t)-1)
{
log_fatal (_("error forking process: %s\n"), strerror (errno));
dirmngr_exit (1);
}
if (pid)
{ /* We are the parent */
char *infostr;
/* Don't let cleanup() remove the socket - the child is
responsible for doing that. */
cleanup_socket = 0;
close (fd);
/* Create the info string: <name>:<pid>:<protocol_version> */
if (asprintf (&infostr, "%s=%s:%lu:1",
DIRMNGR_INFO_NAME, socket_name, (ulong)pid ) < 0)
{
log_error (_("out of core\n"));
kill (pid, SIGTERM);
dirmngr_exit (1);
}
/* Print the environment string, so that the caller can use
shell's eval to set it. But see above. */
if (csh_style)
{
*strchr (infostr, '=') = ' ';
es_printf ( "setenv %s;\n", infostr);
}
else
{
es_printf ( "%s; export %s;\n", infostr, DIRMNGR_INFO_NAME);
}
free (infostr);
exit (0);
/*NEVER REACHED*/
} /* end parent */
/*
This is the child
*/
/* Detach from tty and put process into a new session */
if (!nodetach )
{
int i;
unsigned int oldflags;
/* Close stdin, stdout and stderr unless it is the log stream */
for (i=0; i <= 2; i++)
{
if (!log_test_fd (i) && i != fd )
close (i);
}
if (setsid() == -1)
{
log_error ("setsid() failed: %s\n", strerror(errno) );
dirmngr_exit (1);
}
log_get_prefix (&oldflags);
log_set_prefix (NULL, oldflags | JNLIB_LOG_RUN_DETACHED);
opt.running_detached = 1;
if (chdir("/"))
{
log_error ("chdir to / failed: %s\n", strerror (errno));
dirmngr_exit (1);
}
}
#endif
ldap_wrapper_launch_thread ();
cert_cache_init ();
crl_cache_init ();
#ifdef USE_W32_SERVICE
if (opt.system_service)
{
service_status.dwCurrentState = SERVICE_RUNNING;
SetServiceStatus (service_handle, &service_status);
}
#endif
handle_connections (fd);
assuan_sock_close (fd);
shutdown_reaper ();
#ifdef USE_W32_SERVICE
if (opt.system_service)
{
service_status.dwCurrentState = SERVICE_STOPPED;
SetServiceStatus (service_handle, &service_status);
}
#endif
}
else if (cmd == aListCRLs)
{
/* Just list the CRL cache and exit. */
if (argc)
wrong_args ("--list-crls");
ldap_wrapper_launch_thread ();
crl_cache_init ();
crl_cache_list (es_stdout);
}
else if (cmd == aLoadCRL)
{
struct server_control_s ctrlbuf;
memset (&ctrlbuf, 0, sizeof ctrlbuf);
dirmngr_init_default_ctrl (&ctrlbuf);
ldap_wrapper_launch_thread ();
cert_cache_init ();
crl_cache_init ();
if (!argc)
rc = crl_cache_load (&ctrlbuf, NULL);
else
{
for (; !rc && argc; argc--, argv++)
rc = crl_cache_load (&ctrlbuf, *argv);
}
}
else if (cmd == aFetchCRL)
{
ksba_reader_t reader;
struct server_control_s ctrlbuf;
if (argc != 1)
wrong_args ("--fetch-crl URL");
memset (&ctrlbuf, 0, sizeof ctrlbuf);
dirmngr_init_default_ctrl (&ctrlbuf);
ldap_wrapper_launch_thread ();
cert_cache_init ();
crl_cache_init ();
rc = crl_fetch (&ctrlbuf, argv[0], &reader);
if (rc)
log_error (_("fetching CRL from '%s' failed: %s\n"),
argv[0], gpg_strerror (rc));
else
{
rc = crl_cache_insert (&ctrlbuf, argv[0], reader);
if (rc)
log_error (_("processing CRL from '%s' failed: %s\n"),
argv[0], gpg_strerror (rc));
crl_close_reader (reader);
}
}
else if (cmd == aFlush)
{
/* Delete cache and exit. */
if (argc)
wrong_args ("--flush");
rc = crl_cache_flush();
}
else if (cmd == aGPGConfTest)
dirmngr_exit (0);
else if (cmd == aGPGConfList)
{
unsigned long flags = 0;
char *filename;
char *filename_esc;
#ifdef HAVE_W32_SYSTEM
/* On Windows systems, dirmngr always runs as system daemon, and
the per-user configuration is never used. So we short-cut
everything to use the global system configuration of dirmngr
above, and here we set the no change flag to make these
read-only. */
flags |= GC_OPT_FLAG_NO_CHANGE;
#endif
/* First the configuration file. This is not an option, but it
is vital information for GPG Conf. */
if (!opt.config_filename)
opt.config_filename = make_filename (opt.homedir,
"dirmngr.conf", NULL );
filename = percent_escape (opt.config_filename, NULL);
es_printf ("gpgconf-dirmngr.conf:%lu:\"%s\n",
GC_OPT_FLAG_DEFAULT, filename);
xfree (filename);
es_printf ("verbose:%lu:\n", flags | GC_OPT_FLAG_NONE);
es_printf ("quiet:%lu:\n", flags | GC_OPT_FLAG_NONE);
es_printf ("debug-level:%lu:\"none\n", flags | GC_OPT_FLAG_DEFAULT);
es_printf ("log-file:%lu:\n", flags | GC_OPT_FLAG_NONE);
es_printf ("force:%lu:\n", flags | GC_OPT_FLAG_NONE);
/* --csh and --sh are mutually exclusive, something we can not
express in GPG Conf. --options is only usable from the
command line, really. --debug-all interacts with --debug,
and having both of them is thus problematic. --no-detach is
also only usable on the command line. --batch is unused. */
filename = make_filename (opt.homedir,
opt.system_daemon?
"ldapservers.conf":"dirmngr_ldapservers.conf",
NULL);
filename_esc = percent_escape (filename, NULL);
es_printf ("ldapserverlist-file:%lu:\"%s\n", flags | GC_OPT_FLAG_DEFAULT,
filename_esc);
xfree (filename_esc);
xfree (filename);
es_printf ("ldaptimeout:%lu:%u\n",
flags | GC_OPT_FLAG_DEFAULT, DEFAULT_LDAP_TIMEOUT);
es_printf ("max-replies:%lu:%u\n",
flags | GC_OPT_FLAG_DEFAULT, DEFAULT_MAX_REPLIES);
es_printf ("allow-ocsp:%lu:\n", flags | GC_OPT_FLAG_NONE);
es_printf ("ocsp-responder:%lu:\n", flags | GC_OPT_FLAG_NONE);
es_printf ("ocsp-signer:%lu:\n", flags | GC_OPT_FLAG_NONE);
es_printf ("faked-system-time:%lu:\n", flags | GC_OPT_FLAG_NONE);
es_printf ("no-greeting:%lu:\n", flags | GC_OPT_FLAG_NONE);
es_printf ("disable-http:%lu:\n", flags | GC_OPT_FLAG_NONE);
es_printf ("disable-ldap:%lu:\n", flags | GC_OPT_FLAG_NONE);
es_printf ("honor-http-proxy:%lu\n", flags | GC_OPT_FLAG_NONE);
es_printf ("http-proxy:%lu:\n", flags | GC_OPT_FLAG_NONE);
es_printf ("ldap-proxy:%lu:\n", flags | GC_OPT_FLAG_NONE);
es_printf ("only-ldap-proxy:%lu:\n", flags | GC_OPT_FLAG_NONE);
es_printf ("ignore-ldap-dp:%lu:\n", flags | GC_OPT_FLAG_NONE);
es_printf ("ignore-http-dp:%lu:\n", flags | GC_OPT_FLAG_NONE);
es_printf ("ignore-ocsp-service-url:%lu:\n", flags | GC_OPT_FLAG_NONE);
/* Note: The next one is to fix a typo in gpgconf - should be
removed eventually. */
es_printf ("ignore-ocsp-servic-url:%lu:\n", flags | GC_OPT_FLAG_NONE);
}
cleanup ();
return !!rc;
}
#ifdef USE_W32_SERVICE
static void WINAPI
call_real_main (DWORD argc, LPSTR *argv)
{
real_main (argc, argv);
}
int
main (int argc, char *argv[])
{
int i;
/* Find out if we run in daemon mode or on the command line. */
for (i = 1; i < argc; i++)
if (!strcmp (argv[i], "--service"))
{
opt.system_service = 1;
opt.system_daemon = 1;
break;
}
if (!opt.system_service)
return real_main (argc, argv);
else
{
SERVICE_TABLE_ENTRY DispatchTable [] =
{
{ "DirMngr", &call_real_main },
{ NULL, NULL }
};
if (!StartServiceCtrlDispatcher (DispatchTable))
return 1;
return 0;
}
}
#endif /*USE_W32_SERVICE*/
static void
cleanup (void)
{
crl_cache_deinit ();
cert_cache_deinit (1);
ldapserver_list_free (opt.ldapservers);
opt.ldapservers = NULL;
if (cleanup_socket)
{
cleanup_socket = 0;
if (socket_name && *socket_name)
gnupg_remove (socket_name);
}
}
void
dirmngr_exit (int rc)
{
cleanup ();
exit (rc);
}
void
dirmngr_init_default_ctrl (ctrl_t ctrl)
{
(void)ctrl;
/* Nothing for now. */
}
/* Create a list of LDAP servers from the file FILENAME. Returns the
list or NULL in case of errors.
The format fo such a file is line oriented where empty lines and
lines starting with a hash mark are ignored. All other lines are
assumed to be colon seprated with these fields:
1. field: Hostname
2. field: Portnumber
3. field: Username
4. field: Password
5. field: Base DN
*/
static ldap_server_t
parse_ldapserver_file (const char* filename)
{
char buffer[1024];
char *p;
ldap_server_t server, serverstart, *serverend;
int c;
unsigned int lineno = 0;
estream_t fp;
fp = es_fopen (filename, "r");
if (!fp)
{
log_error (_("error opening '%s': %s\n"), filename, strerror (errno));
return NULL;
}
serverstart = NULL;
serverend = &serverstart;
while (es_fgets (buffer, sizeof buffer, fp))
{
lineno++;
if (!*buffer || buffer[strlen(buffer)-1] != '\n')
{
if (*buffer && es_feof (fp))
; /* Last line not terminated - continue. */
else
{
log_error (_("%s:%u: line too long - skipped\n"),
filename, lineno);
while ( (c=es_fgetc (fp)) != EOF && c != '\n')
; /* Skip until end of line. */
continue;
}
}
/* Skip empty and comment lines.*/
for (p=buffer; spacep (p); p++)
;
if (!*p || *p == '\n' || *p == '#')
continue;
/* Parse the colon separated fields. */
server = ldapserver_parse_one (buffer, filename, lineno);
if (server)
{
*serverend = server;
serverend = &server->next;
}
}
if (es_ferror (fp))
log_error (_("error reading '%s': %s\n"), filename, strerror (errno));
es_fclose (fp);
return serverstart;
}
static fingerprint_list_t
parse_ocsp_signer (const char *string)
{
gpg_error_t err;
char *fname;
estream_t fp;
char line[256];
char *p;
fingerprint_list_t list, *list_tail, item;
unsigned int lnr = 0;
int c, i, j;
int errflag = 0;
/* Check whether this is not a filename and treat it as a direct
fingerprint specification. */
if (!strpbrk (string, "/.~\\"))
{
item = xcalloc (1, sizeof *item);
for (i=j=0; (string[i] == ':' || hexdigitp (string+i)) && j < 40; i++)
if ( string[i] != ':' )
item->hexfpr[j++] = string[i] >= 'a'? (string[i] & 0xdf): string[i];
item->hexfpr[j] = 0;
if (j != 40 || !(spacep (string+i) || !string[i]))
{
log_error (_("%s:%u: invalid fingerprint detected\n"),
"--ocsp-signer", 0);
xfree (item);
return NULL;
}
return item;
}
/* Well, it is a filename. */
if (*string == '/' || (*string == '~' && string[1] == '/'))
fname = make_filename (string, NULL);
else
{
if (string[0] == '.' && string[1] == '/' )
string += 2;
fname = make_filename (opt.homedir, string, NULL);
}
fp = es_fopen (fname, "r");
if (!fp)
{
err = gpg_error_from_syserror ();
log_error (_("can't open '%s': %s\n"), fname, gpg_strerror (err));
xfree (fname);
return NULL;
}
list = NULL;
list_tail = &list;
for (;;)
{
if (!es_fgets (line, DIM(line)-1, fp) )
{
if (!es_feof (fp))
{
err = gpg_error_from_syserror ();
log_error (_("%s:%u: read error: %s\n"),
fname, lnr, gpg_strerror (err));
errflag = 1;
}
es_fclose (fp);
if (errflag)
{
while (list)
{
fingerprint_list_t tmp = list->next;
xfree (list);
list = tmp;
}
}
xfree (fname);
return list; /* Ready. */
}
lnr++;
if (!*line || line[strlen(line)-1] != '\n')
{
/* Eat until end of line. */
while ( (c=es_getc (fp)) != EOF && c != '\n')
;
err = gpg_error (*line? GPG_ERR_LINE_TOO_LONG
/* */: GPG_ERR_INCOMPLETE_LINE);
log_error (_("%s:%u: read error: %s\n"),
fname, lnr, gpg_strerror (err));
errflag = 1;
continue;
}
/* Allow for empty lines and spaces */
for (p=line; spacep (p); p++)
;
if (!*p || *p == '\n' || *p == '#')
continue;
item = xcalloc (1, sizeof *item);
*list_tail = item;
list_tail = &item->next;
for (i=j=0; (p[i] == ':' || hexdigitp (p+i)) && j < 40; i++)
if ( p[i] != ':' )
item->hexfpr[j++] = p[i] >= 'a'? (p[i] & 0xdf): p[i];
item->hexfpr[j] = 0;
if (j != 40 || !(spacep (p+i) || p[i] == '\n'))
{
log_error (_("%s:%u: invalid fingerprint detected\n"), fname, lnr);
errflag = 1;
}
i++;
while (spacep (p+i))
i++;
if (p[i] && p[i] != '\n')
log_info (_("%s:%u: garbage at end of line ignored\n"), fname, lnr);
}
/*NOTREACHED*/
}
/*
Stuff used in daemon mode.
*/
/* Reread parts of the configuration. Note, that this function is
obviously not thread-safe and should only be called from the NPTH
signal handler.
Fixme: Due to the way the argument parsing works, we create a
memory leak here for all string type arguments. There is currently
no clean way to tell whether the memory for the argument has been
allocated or points into the process' original arguments. Unless
we have a mechanism to tell this, we need to live on with this. */
static void
reread_configuration (void)
{
ARGPARSE_ARGS pargs;
FILE *fp;
unsigned int configlineno = 0;
int dummy;
if (!opt.config_filename)
return; /* No config file. */
fp = fopen (opt.config_filename, "r");
if (!fp)
{
log_error (_("option file '%s': %s\n"),
opt.config_filename, strerror(errno) );
return;
}
parse_rereadable_options (NULL, 1); /* Start from the default values. */
memset (&pargs, 0, sizeof pargs);
dummy = 0;
pargs.argc = &dummy;
pargs.flags = 1; /* do not remove the args */
while (optfile_parse (fp, opt.config_filename, &configlineno, &pargs, opts) )
{
if (pargs.r_opt < -1)
pargs.err = 1; /* Print a warning. */
else /* Try to parse this option - ignore unchangeable ones. */
parse_rereadable_options (&pargs, 1);
}
fclose (fp);
set_debug ();
}
/* A global function which allows us to trigger the reload stuff from
other places. */
void
dirmngr_sighup_action (void)
{
log_info (_("SIGHUP received - "
"re-reading configuration and flushing caches\n"));
reread_configuration ();
cert_cache_deinit (0);
crl_cache_deinit ();
cert_cache_init ();
crl_cache_init ();
}
/* The signal handler. */
#ifndef HAVE_W32_SYSTEM
static void
handle_signal (int signo)
{
switch (signo)
{
case SIGHUP:
dirmngr_sighup_action ();
break;
case SIGUSR1:
cert_cache_print_stats ();
break;
case SIGUSR2:
log_info (_("SIGUSR2 received - no action defined\n"));
break;
case SIGTERM:
if (!shutdown_pending)
log_info (_("SIGTERM received - shutting down ...\n"));
else
log_info (_("SIGTERM received - still %d active connections\n"),
active_connections);
shutdown_pending++;
if (shutdown_pending > 2)
{
log_info (_("shutdown forced\n"));
log_info ("%s %s stopped\n", strusage(11), strusage(13) );
cleanup ();
dirmngr_exit (0);
}
break;
case SIGINT:
log_info (_("SIGINT received - immediate shutdown\n"));
log_info( "%s %s stopped\n", strusage(11), strusage(13));
cleanup ();
dirmngr_exit (0);
break;
default:
log_info (_("signal %d received - no action defined\n"), signo);
}
}
#endif /*!HAVE_W32_SYSTEM*/
/* Thread to do the housekeeping. */
static void *
housekeeping_thread (void *arg)
{
static int sentinel;
time_t curtime;
(void)arg;
curtime = gnupg_get_time ();
if (sentinel)
{
log_info ("housekeeping is already going on\n");
return NULL;
}
sentinel++;
if (opt.verbose)
log_info ("starting housekeeping\n");
ks_hkp_housekeeping (curtime);
if (opt.verbose)
log_info ("ready with housekeeping\n");
sentinel--;
return NULL;
}
#if JNLIB_GCC_HAVE_PUSH_PRAGMA
# pragma GCC push_options
# pragma GCC optimize ("no-strict-overflow")
#endif
static int
time_for_housekeeping_p (time_t curtime)
{
static time_t last_housekeeping;
if (!last_housekeeping)
last_housekeeping = curtime;
if (last_housekeeping + HOUSEKEEPING_INTERVAL <= curtime
|| last_housekeeping > curtime /*(be prepared for y2038)*/)
{
last_housekeeping = curtime;
return 1;
}
return 0;
}
#if JNLIB_GCC_HAVE_PUSH_PRAGMA
# pragma GCC pop_options
#endif
/* This is the worker for the ticker. It is called every few seconds
and may only do fast operations. */
static void
handle_tick (void)
{
/* Under Windows we don't use signals and need a way for the loop to
check for the shutdown flag. */
#ifdef HAVE_W32_SYSTEM
if (shutdown_pending)
log_info (_("SIGTERM received - shutting down ...\n"));
if (shutdown_pending > 2)
{
log_info (_("shutdown forced\n"));
log_info ("%s %s stopped\n", strusage(11), strusage(13) );
cleanup ();
dirmngr_exit (0);
}
#endif /*HAVE_W32_SYSTEM*/
if (time_for_housekeeping_p (gnupg_get_time ()))
{
npth_t thread;
npth_attr_t tattr;
int err;
err = npth_attr_init (&tattr);
if (err)
log_error ("error preparing housekeeping thread: %s\n", strerror (err));
else
{
npth_attr_setdetachstate (&tattr, NPTH_CREATE_DETACHED);
err = npth_create (&thread, &tattr, housekeeping_thread, NULL);
if (err)
log_error ("error spawning housekeeping thread: %s\n",
strerror (err));
npth_attr_destroy (&tattr);
}
}
}
/* Check the nonce on a new connection. This is a NOP unless we we
are using our Unix domain socket emulation under Windows. */
static int
check_nonce (assuan_fd_t fd, assuan_sock_nonce_t *nonce)
{
if (assuan_sock_check_nonce (fd, nonce))
{
log_info (_("error reading nonce on fd %d: %s\n"),
FD2INT (fd), strerror (errno));
assuan_sock_close (fd);
return -1;
}
else
return 0;
}
/* Helper to call a connection's main fucntion. */
static void *
start_connection_thread (void *arg)
{
union int_and_ptr_u argval;
gnupg_fd_t fd;
argval.aptr = arg;
fd = argval.afd;
if (check_nonce (fd, &socket_nonce))
{
log_error ("handler nonce check FAILED\n");
return NULL;
}
#ifndef HAVE_W32_SYSTEM
npth_setspecific (my_tlskey_current_fd, argval.aptr);
#endif
active_connections++;
if (opt.verbose)
log_info (_("handler for fd %d started\n"), FD2INT (fd));
start_command_handler (fd);
if (opt.verbose)
log_info (_("handler for fd %d terminated\n"), FD2INT (fd));
active_connections--;
#ifndef HAVE_W32_SYSTEM
argval.afd = ASSUAN_INVALID_FD;
npth_setspecific (my_tlskey_current_fd, argval.aptr);
#endif
return NULL;
}
/* Main loop in daemon mode. */
static void
handle_connections (assuan_fd_t listen_fd)
{
npth_attr_t tattr;
#ifndef HAVE_W32_SYSTEM
int signo;
#endif
struct sockaddr_un paddr;
socklen_t plen = sizeof( paddr );
gnupg_fd_t fd;
int nfd, ret;
fd_set fdset, read_fdset;
struct timespec abstime;
struct timespec curtime;
struct timespec timeout;
int saved_errno;
npth_attr_init (&tattr);
npth_attr_setdetachstate (&tattr, NPTH_CREATE_DETACHED);
#ifndef HAVE_W32_SYSTEM /* FIXME */
npth_sigev_init ();
npth_sigev_add (SIGHUP);
npth_sigev_add (SIGUSR1);
npth_sigev_add (SIGUSR2);
npth_sigev_add (SIGINT);
npth_sigev_add (SIGTERM);
npth_sigev_fini ();
#endif
/* Setup the fdset. It has only one member. This is because we use
pth_select instead of pth_accept to properly sync timeouts with
to full second. */
FD_ZERO (&fdset);
FD_SET (FD2INT (listen_fd), &fdset);
nfd = FD2INT (listen_fd);
npth_clock_gettime (&abstime);
abstime.tv_sec += TIMERTICK_INTERVAL;
/* Main loop. */
for (;;)
{
/* Shutdown test. */
if (shutdown_pending)
{
if (!active_connections)
break; /* ready */
/* Do not accept new connections but keep on running the
loop to cope with the timer events. */
FD_ZERO (&fdset);
}
/* Take a copy of the fdset. */
read_fdset = fdset;
npth_clock_gettime (&curtime);
if (!(npth_timercmp (&curtime, &abstime, <)))
{
/* Timeout. */
handle_tick ();
npth_clock_gettime (&abstime);
abstime.tv_sec += TIMERTICK_INTERVAL;
}
npth_timersub (&abstime, &curtime, &timeout);
#ifndef HAVE_W32_SYSTEM
ret = npth_pselect (nfd+1, &read_fdset, NULL, NULL, &timeout, npth_sigev_sigmask());
saved_errno = errno;
while (npth_sigev_get_pending(&signo))
handle_signal (signo);
#else
ret = npth_eselect (nfd+1, &read_fdset, NULL, NULL, &timeout, NULL, NULL);
saved_errno = errno;
#endif
if (ret == -1 && saved_errno != EINTR)
{
log_error (_("npth_pselect failed: %s - waiting 1s\n"),
strerror (saved_errno));
npth_sleep (1);
continue;
}
if (ret <= 0)
/* Interrupt or timeout. Will be handled when calculating the
next timeout. */
continue;
if (!shutdown_pending && FD_ISSET (FD2INT (listen_fd), &read_fdset))
{
plen = sizeof paddr;
fd = INT2FD (npth_accept (FD2INT(listen_fd),
(struct sockaddr *)&paddr, &plen));
if (fd == GNUPG_INVALID_FD)
{
log_error ("accept failed: %s\n", strerror (errno));
}
else
{
char threadname[50];
union int_and_ptr_u argval;
npth_t thread;
argval.afd = fd;
snprintf (threadname, sizeof threadname-1,
"conn fd=%d", FD2INT(fd));
threadname[sizeof threadname -1] = 0;
ret = npth_create (&thread, &tattr, start_connection_thread, argval.aptr);
if (ret)
{
log_error ("error spawning connection handler: %s\n",
strerror (ret) );
assuan_sock_close (fd);
}
npth_setname_np (thread, threadname);
}
fd = GNUPG_INVALID_FD;
}
}
npth_attr_destroy (&tattr);
cleanup ();
log_info ("%s %s stopped\n", strusage(11), strusage(13));
}
diff --git a/m4/Makefile.am b/m4/Makefile.am
index 05a2be366..f1b8df9bd 100644
--- a/m4/Makefile.am
+++ b/m4/Makefile.am
@@ -1,13 +1,13 @@
EXTRA_DIST = intl.m4 intldir.m4 glibc2.m4 lock.m4 visibility.m4 intmax.m4 longdouble.m4 printf-posix.m4 signed.m4 size_max.m4 wchar_t.m4 wint_t.m4 xsize.m4 codeset.m4 gettext.m4 glibc21.m4 iconv.m4 intdiv0.m4 inttypes.m4 inttypes_h.m4 inttypes-pri.m4 isc-posix.m4 lcmessage.m4 lib-ld.m4 lib-link.m4 lib-prefix.m4 progtest.m4 stdint_h.m4 uintmax_t.m4
EXTRA_DIST += ldap.m4 libcurl.m4 libusb.m4 tar-ustar.m4 readline.m4
EXTRA_DIST += gnupg-pth.m4
-EXTRA_DIST += gpg-error.m4 libgcrypt.m4 libassuan.m4 ksba.m4
+EXTRA_DIST += gpg-error.m4 libgcrypt.m4 libassuan.m4 ksba.m4 ntbtls.m4
EXTRA_DIST += autobuild.m4
EXTRA_DIST += sys_socket_h.m4 socklen.m4
EXTRA_DIST += ChangeLog-2011
diff --git a/m4/ntbtls.m4 b/m4/ntbtls.m4
new file mode 100644
index 000000000..85c8ee9a0
--- /dev/null
+++ b/m4/ntbtls.m4
@@ -0,0 +1,137 @@
+dnl Autoconf macros for NTBTLS
+dnl Copyright (C) 2002, 2004, 2011 Free Software Foundation, Inc.
+dnl
+dnl This file is free software; as a special exception the author gives
+dnl unlimited permission to copy and/or distribute it, with or without
+dnl modifications, as long as this notice is preserved.
+dnl
+dnl This file is distributed in the hope that it will be useful, but
+dnl WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
+dnl implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+
+dnl AM_PATH_NTBTLS([MINIMUM-VERSION,
+dnl [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND ]]])
+dnl
+dnl Test for NTBTLS and define NTBTLS_CFLAGS and NTBTLS_LIBS.
+dnl MINIMUN-VERSION is a string with the version number optionalliy prefixed
+dnl with the API version to also check the API compatibility. Example:
+dnl a MINIMUN-VERSION of 1:1.2.5 won't pass the test unless the installed
+dnl version of libgcrypt is at least 1.2.5 *and* the API number is 1. Using
+dnl this features allows to prevent build against newer versions of libgcrypt
+dnl with a changed API.
+dnl
+AC_DEFUN([AM_PATH_NTBTLS],
+[ AC_REQUIRE([AC_CANONICAL_HOST])
+ AC_ARG_WITH(ntbtls-prefix,
+ AC_HELP_STRING([--with-ntbtls-prefix=PFX],
+ [prefix where NTBTLS is installed (optional)]),
+ ntbtls_config_prefix="$withval", ntbtls_config_prefix="")
+ if test x"${NTBTLS_CONFIG}" = x ; then
+ if test x"${ntbtls_config_prefix}" != x ; then
+ NTBTLS_CONFIG="${ntbtls_config_prefix}/bin/ntbtls-config"
+ else
+ case "${SYSROOT}" in
+ /*)
+ if test -x "${SYSROOT}/bin/ntbtls-config" ; then
+ NTBTLS_CONFIG="${SYSROOT}/bin/ntbtls-config"
+ fi
+ ;;
+ '')
+ ;;
+ *)
+ AC_MSG_WARN([Ignoring \$SYSROOT as it is not an absolute path.])
+ ;;
+ esac
+ fi
+ fi
+
+ AC_PATH_PROG(NTBTLS_CONFIG, ntbtls-config, no)
+ tmp=ifelse([$1], ,1:1.0.0,$1)
+ if echo "$tmp" | grep ':' >/dev/null 2>/dev/null ; then
+ req_ntbtls_api=`echo "$tmp" | sed 's/\(.*\):\(.*\)/\1/'`
+ min_ntbtls_version=`echo "$tmp" | sed 's/\(.*\):\(.*\)/\2/'`
+ else
+ req_ntbtls_api=0
+ min_ntbtls_version="$tmp"
+ fi
+
+ AC_MSG_CHECKING(for NTBTLS - version >= $min_ntbtls_version)
+ ok=no
+ if test "$NTBTLS_CONFIG" != "no" ; then
+ req_major=`echo $min_ntbtls_version | \
+ sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\)/\1/'`
+ req_minor=`echo $min_ntbtls_version | \
+ sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\)/\2/'`
+ req_micro=`echo $min_ntbtls_version | \
+ sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\)/\3/'`
+ ntbtls_config_version=`$NTBTLS_CONFIG --version`
+ major=`echo $ntbtls_config_version | \
+ sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\).*/\1/'`
+ minor=`echo $ntbtls_config_version | \
+ sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\).*/\2/'`
+ micro=`echo $ntbtls_config_version | \
+ sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\).*/\3/'`
+ if test "$major" -gt "$req_major"; then
+ ok=yes
+ else
+ if test "$major" -eq "$req_major"; then
+ if test "$minor" -gt "$req_minor"; then
+ ok=yes
+ else
+ if test "$minor" -eq "$req_minor"; then
+ if test "$micro" -ge "$req_micro"; then
+ ok=yes
+ fi
+ fi
+ fi
+ fi
+ fi
+ fi
+ if test $ok = yes; then
+ AC_MSG_RESULT([yes ($ntbtls_config_version)])
+ else
+ AC_MSG_RESULT(no)
+ fi
+ if test $ok = yes; then
+ # If we have a recent ntbtls, we should also check that the
+ # API is compatible
+ if test "$req_ntbtls_api" -gt 0 ; then
+ tmp=`$NTBTLS_CONFIG --api-version 2>/dev/null || echo 0`
+ if test "$tmp" -gt 0 ; then
+ AC_MSG_CHECKING([NTBTLS API version])
+ if test "$req_ntbtls_api" -eq "$tmp" ; then
+ AC_MSG_RESULT([okay])
+ else
+ ok=no
+ AC_MSG_RESULT([does not match. want=$req_ntbtls_api got=$tmp])
+ fi
+ fi
+ fi
+ fi
+ if test $ok = yes; then
+ NTBTLS_CFLAGS=`$NTBTLS_CONFIG --cflags`
+ NTBTLS_LIBS=`$NTBTLS_CONFIG --libs`
+ ifelse([$2], , :, [$2])
+ ntbtls_config_host=`$NTBTLS_CONFIG --host 2>/dev/null || echo none`
+ if test x"$ntbtls_config_host" != xnone ; then
+ if test x"$ntbtls_config_host" != x"$host" ; then
+ AC_MSG_WARN([[
+***
+*** The config script $NTBTLS_CONFIG was
+*** built for $ntbtls_config_host and thus may not match the
+*** used host $host.
+*** You may want to use the configure option --with-ntbtls-prefix
+*** to specify a matching config script or use \$SYSROOT.
+***]])
+ gpg_config_script_warn="$gpg_config_script_warn ntbtls"
+ fi
+ fi
+ else
+ NTBTLS_CFLAGS=""
+ NTBTLS_LIBS=""
+ ifelse([$3], , :, [$3])
+ fi
+ AC_SUBST(NTBTLS_CFLAGS)
+ AC_SUBST(NTBTLS_LIBS)
+])
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Tue, Dec 30, 5:59 PM (3 m, 41 s)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
f5/60/8aea22ff262fb517ed00ac4451a2
Attached To
rG GnuPG
Event Timeline
Log In to Comment