gnutls-commit
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[SCM] GNU gnutls branch, master, updated. gnutls-3_0_12-98-gdc42971


From: Nikos Mavrogiannopoulos
Subject: [SCM] GNU gnutls branch, master, updated. gnutls-3_0_12-98-gdc42971
Date: Sat, 28 Jan 2012 12:27:39 +0000

This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "GNU gnutls".

http://git.savannah.gnu.org/cgit/gnutls.git/commit/?id=dc42971afc5051136ebc8d4b21cb49a2055d4a7b

The branch, master has been updated
       via  dc42971afc5051136ebc8d4b21cb49a2055d4a7b (commit)
       via  eb3ba487cd5881107f8c63dd3ae4356ccb847dff (commit)
       via  63e14992cdc710f3322f205485efb3aad4c6465f (commit)
       via  96a1ad7b7d57655f10433b05589a1d558197253b (commit)
       via  2216b3561d86cccb94af363590fd7a329fd013f5 (commit)
       via  6b837df532f54b47bac8593d6fef4d6c73c91d80 (commit)
      from  015780d11567c3421c2f92ab5246c2237d210422 (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit dc42971afc5051136ebc8d4b21cb49a2055d4a7b
Author: Nikos Mavrogiannopoulos <address@hidden>
Date:   Sat Jan 28 12:47:49 2012 +0100

    Added gnutls_verify_stored_pubkey() and gnutls_store_pubkey().
    This enables using ssh-like authentication for TLS sessions.

commit eb3ba487cd5881107f8c63dd3ae4356ccb847dff
Author: Nikos Mavrogiannopoulos <address@hidden>
Date:   Sat Jan 28 12:40:18 2012 +0100

    allow the usage of --load-trust with --ask

commit 63e14992cdc710f3322f205485efb3aad4c6465f
Author: Nikos Mavrogiannopoulos <address@hidden>
Date:   Fri Jan 27 22:37:03 2012 +0100

    Added strtok_r.

commit 96a1ad7b7d57655f10433b05589a1d558197253b
Author: Nikos Mavrogiannopoulos <address@hidden>
Date:   Fri Jan 27 22:08:40 2012 +0100

    added base64 module

commit 2216b3561d86cccb94af363590fd7a329fd013f5
Author: Nikos Mavrogiannopoulos <address@hidden>
Date:   Fri Jan 27 22:01:50 2012 +0100

    added fixme

commit 6b837df532f54b47bac8593d6fef4d6c73c91d80
Author: Nikos Mavrogiannopoulos <address@hidden>
Date:   Fri Jan 27 20:47:34 2012 +0100

    updated description

-----------------------------------------------------------------------

Summary of changes:
 NEWS                                 |    8 +
 configure.ac                         |    3 +-
 doc/cha-cert-auth.texi               |   14 +
 doc/cha-gtls-examples.texi           |   13 +
 doc/examples/Makefile.am             |    2 +-
 doc/examples/ex-cert-select-pkcs11.c |    2 +-
 doc/examples/ex-cert-select.c        |    2 +-
 doc/examples/ex-client-dtls.c        |    2 +-
 doc/examples/ex-client-resume.c      |    2 +-
 doc/examples/ex-client-srp.c         |    2 +-
 doc/examples/ex-client-x509.c        |    2 +-
 doc/examples/ex-serv-dtls.c          |    2 +-
 doc/examples/ex-serv-psk.c           |    2 +-
 doc/examples/ex-serv-srp.c           |    2 +-
 doc/examples/ex-serv-x509.c          |    2 +-
 doc/examples/ex-verify-ssh.c         |  138 ++++++++
 doc/examples/examples.h              |    3 +
 gl/Makefile.am                       |   17 +-
 gl/base64.c                          |  575 ++++++++++++++++++++++++++++++++++
 gl/base64.h                          |   61 ++++
 gl/m4/{sockets.m4 => base64.m4}      |   17 +-
 gl/m4/gnulib-cache.m4                |    4 +-
 gl/m4/gnulib-comp.m4                 |   16 +
 gl/m4/stdalign.m4                    |   15 +-
 gl/m4/strtok_r.m4                    |   74 +++++
 gl/strtok_r.c                        |   76 +++++
 gl/tests/Makefile.am                 |    8 +
 gl/tests/malloca.c                   |    6 +-
 gl/tests/test-base64.c               |  238 ++++++++++++++
 lib/Makefile.am                      |    2 +-
 lib/includes/gnutls/gnutls.h.in      |   24 ++-
 lib/libgnutls.map                    |    2 +
 lib/openpgp/output.c                 |    7 +
 lib/system.c                         |   76 +++++-
 lib/system.h                         |    2 +
 lib/verify-ssh.c                     |  489 +++++++++++++++++++++++++++++
 lib/x509/output.c                    |   16 +
 lib/x509_b64.c                       |    3 +
 src/cli-args.def.in                  |    8 +-
 src/cli.c                            |   88 +++++-
 src/common.c                         |  330 ++++++++++++++------
 src/common.h                         |    4 +-
 src/ocsptool-args.def.in             |    6 +-
 src/ocsptool.c                       |    4 +-
 src/tests.c                          |    2 +-
 45 files changed, 2226 insertions(+), 145 deletions(-)
 create mode 100644 doc/examples/ex-verify-ssh.c
 create mode 100644 gl/base64.c
 create mode 100644 gl/base64.h
 copy gl/m4/{sockets.m4 => base64.m4} (50%)
 create mode 100644 gl/m4/strtok_r.m4
 create mode 100644 gl/strtok_r.c
 create mode 100644 gl/tests/test-base64.c
 create mode 100644 lib/verify-ssh.c

diff --git a/NEWS b/NEWS
index c6aed0e..ccce513 100644
--- a/NEWS
+++ b/NEWS
@@ -4,12 +4,18 @@ See the end for copying conditions.
 
 * Version 3.0.13 (unreleased)
 
+** gnutls-cli: If no --x509cafile is provided a default is
+assumed (/etc/ssl/certs/ca-certificates.crt).
+
 ** ocsptool: Added --ask parameter, to verify a certificate's
 status from an ocsp server.
 
 ** command line apps: Use gnu autogen (libopts) to parse command
 line arguments and template files.
 
+** libgnutls: Added new functions to easily allow the usage of
+an SSH-style authentication.
+
 ** libgnutls: SUITEB128 and SUITEB192 priority strings account
 for the RFC6460 requirements.
 
@@ -28,6 +34,8 @@ of PKCS #11 modules. This is required on the child process 
after
 a fork.
 
 ** API and ABI modifications:
+gnutls_verify_stored_pubkey: Added
+gnutls_store_pubkey: Added
 gnutls_x509_crt_get_authority_key_gn_serial: Added
 gnutls_x509_crl_get_authority_key_gn_serial: Added
 gnutls_pkcs11_reinit: Added
diff --git a/configure.ac b/configure.ac
index 7531ae5..42a84d4 100644
--- a/configure.ac
+++ b/configure.ac
@@ -117,9 +117,8 @@ AC_C_BIGENDIAN
 
 
 dnl No fork on MinGW, disable some self-tests until we fix them.
-AC_CHECK_FUNCS(fork,,)
+AC_CHECK_FUNCS([fork getrusage getpwuid_r],,)
 AM_CONDITIONAL(HAVE_FORK, test "$ac_cv_func_fork" != "no")
-AC_CHECK_FUNCS(getrusage,,)
 AC_LIB_HAVE_LINKFLAGS(pthread,, [#include <pthread.h>], [pthread_mutex_lock 
(0);])
 
 dnl Check for p11-kit
diff --git a/doc/cha-cert-auth.texi b/doc/cha-cert-auth.texi
index 5253f5b..7fb41a6 100644
--- a/doc/cha-cert-auth.texi
+++ b/doc/cha-cert-auth.texi
@@ -87,6 +87,7 @@ acceptable.  The framework is illustrated on @ref{fig:x509}.
 * X.509 distinguished names::
 * Verifying X.509 certificate paths::
 * Verifying a certificate in the context of TLS session::
+* Verifying a certificate using SSH-style authentication::
 @end menu
 
 @node X.509 certificate structure
@@ -276,7 +277,20 @@ about the peer's identity. It is required to verify if the
 certificate's owner is the one you expect. For more information
 consult @xcite{RFC2818} and section @ref{ex:verify} for an example.
 
address@hidden Verifying a certificate using SSH-style authentication
address@hidden Verifying a certificate using SSH-style authentication
address@hidden verifying certificate paths
address@hidden SSH-style authentication
address@hidden gnutls_certificate_verify_flags
+
+It is possible to use an SSH-style authentication method in GnuTLS.
+That means that having seen and associated a public key with a host
+is enough to trust it on the subsequent connections.
+A hybrid system with X.509 and SSH authentication is 
+shown in @ref{Simple client example with SSH-style certificate verification}.
 
address@hidden
address@hidden
 
 @node OpenPGP certificates
 @section @acronym{OpenPGP} certificates
diff --git a/doc/cha-gtls-examples.texi b/doc/cha-gtls-examples.texi
index 8fbf6b8..9d253a0 100644
--- a/doc/cha-gtls-examples.texi
+++ b/doc/cha-gtls-examples.texi
@@ -25,6 +25,7 @@ implemented by another example.
 @menu
 * Simple client example with anonymous authentication::
 * Simple client example with X.509 certificate support::
+* Simple client example with SSH-style certificate verification::
 * Simple Datagram TLS client example::
 * Obtaining session information::
 * Using a callback to select the certificate to use::
@@ -47,6 +48,8 @@ is vulnerable to man-in-the-middle (active or redirection) 
attacks.
 However, the data are integrity protected and encrypted from
 passive eavesdroppers.
 
+Note that the server must support anonymous authentication as well.
+
 @verbatiminclude examples/ex-client-anon.c
 
 @node Simple client example with X.509 certificate support
@@ -62,6 +65,16 @@ resumption.
 
 @verbatiminclude examples/ex-client-x509.c
 
address@hidden Simple client example with SSH-style certificate verification
address@hidden Simple client example with SSH-style certificate verification
+
+This is an alternative verification function that will use the
+X.509 certificate authorities for verification, but also assume an
+SSH-like authentication system. That is the user is prompted on unknown 
+public keys and known public keys are considered trusted.
+
address@hidden examples/ex-verify-ssh.c
+
 @node Simple Datagram TLS client example
 @subsection Simple datagram @acronym{TLS} client example
 
diff --git a/doc/examples/Makefile.am b/doc/examples/Makefile.am
index ba6d047..ed1592c 100644
--- a/doc/examples/Makefile.am
+++ b/doc/examples/Makefile.am
@@ -84,4 +84,4 @@ endif
 
 libexamples_la_SOURCES = examples.h ex-alert.c ex-pkcs12.c \
        ex-session-info.c ex-x509-info.c ex-verify.c    \
-       tcp.c udp.c ex-pkcs11-list.c verify.c
+       tcp.c udp.c ex-pkcs11-list.c verify.c ex-verify-ssh.c
diff --git a/doc/examples/ex-cert-select-pkcs11.c 
b/doc/examples/ex-cert-select-pkcs11.c
index bd7851c..e8cb21e 100644
--- a/doc/examples/ex-cert-select-pkcs11.c
+++ b/doc/examples/ex-cert-select-pkcs11.c
@@ -26,7 +26,7 @@
 #define MSG "GET / HTTP/1.0\r\n\r\n"
 #define MIN(x,y) (((x)<(y))?(x):(y))
 
-#define CAFILE "ca.pem"
+#define CAFILE "/etc/ssl/certs/ca-certificates.crt"
 
 /* The URLs of the objects can be obtained
  * using p11tool --list-all --login
diff --git a/doc/examples/ex-cert-select.c b/doc/examples/ex-cert-select.c
index 43f666f..d45e8e5 100644
--- a/doc/examples/ex-cert-select.c
+++ b/doc/examples/ex-cert-select.c
@@ -26,7 +26,7 @@
 
 #define CERT_FILE "cert.pem"
 #define KEY_FILE "key.pem"
-#define CAFILE "ca.pem"
+#define CAFILE "/etc/ssl/certs/ca-certificates.crt"
 
 extern int tcp_connect (void);
 extern void tcp_close (int sd);
diff --git a/doc/examples/ex-client-dtls.c b/doc/examples/ex-client-dtls.c
index 222762a..3c9d1ea 100644
--- a/doc/examples/ex-client-dtls.c
+++ b/doc/examples/ex-client-dtls.c
@@ -18,7 +18,7 @@
  */
 
 #define MAX_BUF 1024
-#define CAFILE "ca.pem"
+#define CAFILE "/etc/ssl/certs/ca-certificates.crt"
 #define MSG "GET / HTTP/1.0\r\n\r\n"
 
 extern int udp_connect (void);
diff --git a/doc/examples/ex-client-resume.c b/doc/examples/ex-client-resume.c
index 5aeae58..1a041d1 100644
--- a/doc/examples/ex-client-resume.c
+++ b/doc/examples/ex-client-resume.c
@@ -16,7 +16,7 @@ extern int tcp_connect (void);
 extern void tcp_close (int sd);
 
 #define MAX_BUF 1024
-#define CAFILE "ca.pem"
+#define CAFILE "/etc/ssl/certs/ca-certificates.crt"
 #define MSG "GET / HTTP/1.0\r\n\r\n"
 
 int
diff --git a/doc/examples/ex-client-srp.c b/doc/examples/ex-client-srp.c
index 5a753ab..89d5165 100644
--- a/doc/examples/ex-client-srp.c
+++ b/doc/examples/ex-client-srp.c
@@ -18,7 +18,7 @@ extern void tcp_close (int sd);
 #define MAX_BUF 1024
 #define USERNAME "user"
 #define PASSWORD "pass"
-#define CAFILE "ca.pem"
+#define CAFILE "/etc/ssl/certs/ca-certificates.crt"
 #define MSG "GET / HTTP/1.0\r\n\r\n"
 
 int
diff --git a/doc/examples/ex-client-x509.c b/doc/examples/ex-client-x509.c
index 0ea151d..c5ed190 100644
--- a/doc/examples/ex-client-x509.c
+++ b/doc/examples/ex-client-x509.c
@@ -16,7 +16,7 @@
  */
 
 #define MAX_BUF 1024
-#define CAFILE "ca.pem"
+#define CAFILE "/etc/ssl/certs/ca-certificates.crt"
 #define MSG "GET / HTTP/1.0\r\n\r\n"
 
 extern int tcp_connect (void);
diff --git a/doc/examples/ex-serv-dtls.c b/doc/examples/ex-serv-dtls.c
index 355f7b9..a32a984 100644
--- a/doc/examples/ex-serv-dtls.c
+++ b/doc/examples/ex-serv-dtls.c
@@ -20,7 +20,7 @@
 
 #define KEYFILE "key.pem"
 #define CERTFILE "cert.pem"
-#define CAFILE "ca.pem"
+#define CAFILE "/etc/ssl/certs/ca-certificates.crt"
 #define CRLFILE "crl.pem"
 
 /* This is a sample DTLS echo server, using X.509 authentication.
diff --git a/doc/examples/ex-serv-psk.c b/doc/examples/ex-serv-psk.c
index 70732f6..1f2af6c 100644
--- a/doc/examples/ex-serv-psk.c
+++ b/doc/examples/ex-serv-psk.c
@@ -17,7 +17,7 @@
 
 #define KEYFILE "key.pem"
 #define CERTFILE "cert.pem"
-#define CAFILE "ca.pem"
+#define CAFILE "/etc/ssl/certs/ca-certificates.crt"
 #define CRLFILE "crl.pem"
 
 /* This is a sample TLS echo server, supporting X.509 and PSK
diff --git a/doc/examples/ex-serv-srp.c b/doc/examples/ex-serv-srp.c
index 70ae166..5873b39 100644
--- a/doc/examples/ex-serv-srp.c
+++ b/doc/examples/ex-serv-srp.c
@@ -20,7 +20,7 @@
 
 #define KEYFILE "key.pem"
 #define CERTFILE "cert.pem"
-#define CAFILE "ca.pem"
+#define CAFILE "/etc/ssl/certs/ca-certificates.crt"
 
 /* This is a sample TLS-SRP echo server.
  */
diff --git a/doc/examples/ex-serv-x509.c b/doc/examples/ex-serv-x509.c
index 44c671a..c06f13e 100644
--- a/doc/examples/ex-serv-x509.c
+++ b/doc/examples/ex-serv-x509.c
@@ -17,7 +17,7 @@
 
 #define KEYFILE "key.pem"
 #define CERTFILE "cert.pem"
-#define CAFILE "ca.pem"
+#define CAFILE "/etc/ssl/certs/ca-certificates.crt"
 #define CRLFILE "crl.pem"
 
 /* This is a sample TLS 1.0 echo server, using X.509 authentication.
diff --git a/doc/examples/ex-verify-ssh.c b/doc/examples/ex-verify-ssh.c
new file mode 100644
index 0000000..c9fab66
--- /dev/null
+++ b/doc/examples/ex-verify-ssh.c
@@ -0,0 +1,138 @@
+/* This example code is placed in the public domain. */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <gnutls/gnutls.h>
+#include <gnutls/x509.h>
+#include "examples.h"
+
+/* This function will verify the peer's certificate, check
+ * if the hostname matches. In addition it will perform an
+ * SSH-style authentication, where ultimately trusted keys
+ * are only the keys that have been seen before.
+ */
+int
+_ssh_verify_certificate_callback (gnutls_session_t session)
+{
+  unsigned int status;
+  const gnutls_datum_t *cert_list;
+  unsigned int cert_list_size;
+  int ret;
+  gnutls_x509_crt_t cert;
+  const char *hostname;
+
+  /* read hostname */
+  hostname = gnutls_session_get_ptr (session);
+
+  /* This verification function uses the trusted CAs in the credentials
+   * structure. So you must have installed one or more CA certificates.
+   */
+  ret = gnutls_certificate_verify_peers2 (session, &status);
+  if (ret < 0)
+    {
+      printf ("Error\n");
+      return GNUTLS_E_CERTIFICATE_ERROR;
+    }
+
+  if (status & GNUTLS_CERT_INVALID)
+    printf ("The certificate is not trusted.\n");
+
+  if (status & GNUTLS_CERT_SIGNER_NOT_FOUND)
+    printf ("The certificate hasn't got a known issuer.\n");
+
+  if (status & GNUTLS_CERT_REVOKED)
+    printf ("The certificate has been revoked.\n");
+
+  if (status & GNUTLS_CERT_EXPIRED)
+    printf ("The certificate has expired\n");
+
+  if (status & GNUTLS_CERT_NOT_ACTIVATED)
+    printf ("The certificate is not yet activated\n");
+
+  /* Up to here the process is the same for X.509 certificates and
+   * OpenPGP keys. From now on X.509 certificates are assumed. This can
+   * be easily extended to work with openpgp keys as well.
+   */
+  if (gnutls_certificate_type_get (session) != GNUTLS_CRT_X509)
+    return GNUTLS_E_CERTIFICATE_ERROR;
+
+  if (gnutls_x509_crt_init (&cert) < 0)
+    {
+      printf ("error in initialization\n");
+      return GNUTLS_E_CERTIFICATE_ERROR;
+    }
+
+  cert_list = gnutls_certificate_get_peers (session, &cert_list_size);
+  if (cert_list == NULL)
+    {
+      printf ("No certificate was found!\n");
+      return GNUTLS_E_CERTIFICATE_ERROR;
+    }
+
+  /* This is not a real world example, since we only check the first 
+   * certificate in the given chain.
+   */
+  if (gnutls_x509_crt_import (cert, &cert_list[0], GNUTLS_X509_FMT_DER) < 0)
+    {
+      printf ("error parsing certificate\n");
+      return GNUTLS_E_CERTIFICATE_ERROR;
+    }
+
+  if (!gnutls_x509_crt_check_hostname (cert, hostname))
+    {
+      printf ("The certificate's owner does not match hostname '%s'\n",
+              hostname);
+      status |= GNUTLS_CERT_INVALID;
+    }
+
+  gnutls_x509_crt_deinit (cert);
+  
+  ret = gnutls_verify_stored_pubkey(NULL, NULL, hostname, "443", 
+                                    GNUTLS_CRT_X509, &cert_list[0], 0);
+  if (ret == GNUTLS_E_NO_CERTIFICATE_FOUND)
+    {
+      fprintf(stderr, "Host %s is not known.", hostname);
+      if (status == 0)
+        fprintf(stderr, "Its certificate is valid for %s.\n", hostname);
+      
+      /* the certificate must be printed and user must be asked on
+       * whether it is trustworthy. --see gnutls_x509_crt_print() */
+      
+      /* if not trusted */
+      return GNUTLS_E_CERTIFICATE_ERROR;
+    }
+  else if (ret == GNUTLS_E_CERTIFICATE_KEY_MISMATCH)
+    {
+      fprintf(stderr, "Warning: host %s is known but has another key 
associated.", hostname);
+      fprintf(stderr, "It might be that the server has multiple keys, or you 
are under attack\n");
+      if (status == 0)
+        fprintf(stderr, "Its certificate is valid for %s.\n", hostname);
+      
+      /* the certificate must be printed and user must be asked on
+       * whether it is trustworthy. --see gnutls_x509_crt_print() */
+      
+      /* if not trusted */
+      return GNUTLS_E_CERTIFICATE_ERROR;
+    }
+  else if (ret < 0)
+    {
+      fprintf(stderr, "gnutls_verify_stored_pubkey: %s\n", 
gnutls_strerror(ret));
+      return ret;
+    }
+  
+  /* user trusts the key -> store it */
+  ret = gnutls_store_pubkey(NULL, NULL, hostname, "443", GNUTLS_CRT_X509, 
&cert_list[0], 0);
+  if (ret < 0)
+    {
+      fprintf(stderr, "gnutls_store_pubkey: %s\n", gnutls_strerror(ret));
+    }
+
+  /* notify gnutls to continue handshake normally */
+  return 0;
+}
+
diff --git a/doc/examples/examples.h b/doc/examples/examples.h
index e96cb26..0c2dbb3 100644
--- a/doc/examples/examples.h
+++ b/doc/examples/examples.h
@@ -12,6 +12,9 @@ int print_info (gnutls_session_t session);
 
 void print_x509_certificate_info (gnutls_session_t session);
 
+int
+_ssh_verify_certificate_callback (gnutls_session_t session);
+
 void
 verify_certificate_chain (const char *hostname,
                           const gnutls_datum_t * cert_chain,
diff --git a/gl/Makefile.am b/gl/Makefile.am
index 5607417..1da9139 100644
--- a/gl/Makefile.am
+++ b/gl/Makefile.am
@@ -21,7 +21,7 @@
 # the same distribution terms as the rest of that program.
 #
 # Generated by gnulib-tool.
-# Reproduce by: gnulib-tool --import --dir=. --local-dir=gl/override 
--lib=libgnu --source-base=gl --m4-base=gl/m4 --doc-base=doc 
--tests-base=gl/tests --aux-dir=build-aux --with-tests --avoid=alignof-tests 
--avoid=lock-tests --avoid=lseek-tests --no-conditional-dependencies --libtool 
--macro-prefix=gl --no-vc-files accept alloca alphasort argp bind byteswap 
c-ctype close connect error extensions func gendocs getaddrinfo getpass 
getsubopt gettext gettime havelib inet_ntop inet_pton lib-msvc-compat 
lib-symbol-versions listen maintainer-makefile manywarnings memmem-simple 
minmax netdb netinet_in pmccabe2html progname read-file recv recvfrom scandir 
select send sendto setsockopt shutdown snprintf socket sockets socklen stdint 
strcase strverscmp sys_socket sys_stat time_r timespec u64 unistd 
valgrind-tests vasprintf version-etc version-etc-fsf vfprintf-posix 
vprintf-posix vsnprintf warnings
+# Reproduce by: gnulib-tool --import --dir=. --local-dir=gl/override 
--lib=libgnu --source-base=gl --m4-base=gl/m4 --doc-base=doc 
--tests-base=gl/tests --aux-dir=build-aux --with-tests --avoid=alignof-tests 
--avoid=lock-tests --avoid=lseek-tests --no-conditional-dependencies --libtool 
--macro-prefix=gl --no-vc-files accept alloca alphasort argp base64 bind 
byteswap c-ctype close connect error extensions func gendocs getaddrinfo 
getpass getsubopt gettext gettime havelib inet_ntop inet_pton lib-msvc-compat 
lib-symbol-versions listen maintainer-makefile manywarnings memmem-simple 
minmax netdb netinet_in pmccabe2html progname read-file recv recvfrom scandir 
select send sendto setsockopt shutdown snprintf socket sockets socklen stdint 
strcase strtok_r strverscmp sys_socket sys_stat time_r timespec u64 unistd 
valgrind-tests vasprintf version-etc version-etc-fsf vfprintf-posix 
vprintf-posix vsnprintf warnings
 
 AUTOMAKE_OPTIONS = 1.5 gnits
 
@@ -158,6 +158,12 @@ EXTRA_DIST += arpa_inet.in.h
 
 ## end   gnulib module arpa_inet
 
+## begin gnulib module base64
+
+libgnu_la_SOURCES += base64.h base64.c
+
+## end   gnulib module base64
+
 ## begin gnulib module bind
 
 
@@ -1880,6 +1886,15 @@ EXTRA_libgnu_la_SOURCES += strnlen.c
 
 ## end   gnulib module strnlen
 
+## begin gnulib module strtok_r
+
+
+EXTRA_DIST += strtok_r.c
+
+EXTRA_libgnu_la_SOURCES += strtok_r.c
+
+## end   gnulib module strtok_r
+
 ## begin gnulib module strverscmp
 
 
diff --git a/gl/base64.c b/gl/base64.c
new file mode 100644
index 0000000..acf49c8
--- /dev/null
+++ b/gl/base64.c
@@ -0,0 +1,575 @@
+/* base64.c -- Encode binary data using printable characters.
+   Copyright (C) 1999-2001, 2004-2006, 2009-2012 Free Software Foundation, Inc.
+
+   This program 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, or (at your option)
+   any later version.
+
+   This program 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, write to the Free Software Foundation,
+   Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
+
+/* Written by Simon Josefsson.  Partially adapted from GNU MailUtils
+ * (mailbox/filter_trans.c, as of 2004-11-28).  Improved by review
+ * from Paul Eggert, Bruno Haible, and Stepan Kasal.
+ *
+ * See also RFC 4648 <http://www.ietf.org/rfc/rfc4648.txt>.
+ *
+ * Be careful with error checking.  Here is how you would typically
+ * use these functions:
+ *
+ * bool ok = base64_decode_alloc (in, inlen, &out, &outlen);
+ * if (!ok)
+ *   FAIL: input was not valid base64
+ * if (out == NULL)
+ *   FAIL: memory allocation error
+ * OK: data in OUT/OUTLEN
+ *
+ * size_t outlen = base64_encode_alloc (in, inlen, &out);
+ * if (out == NULL && outlen == 0 && inlen != 0)
+ *   FAIL: input too long
+ * if (out == NULL)
+ *   FAIL: memory allocation error
+ * OK: data in OUT/OUTLEN.
+ *
+ */
+
+#include <config.h>
+
+/* Get prototype. */
+#include "base64.h"
+
+/* Get malloc. */
+#include <stdlib.h>
+
+/* Get UCHAR_MAX. */
+#include <limits.h>
+
+#include <string.h>
+
+/* C89 compliant way to cast 'char' to 'unsigned char'. */
+static inline unsigned char
+to_uchar (char ch)
+{
+  return ch;
+}
+
+/* Base64 encode IN array of size INLEN into OUT array of size OUTLEN.
+   If OUTLEN is less than BASE64_LENGTH(INLEN), write as many bytes as
+   possible.  If OUTLEN is larger than BASE64_LENGTH(INLEN), also zero
+   terminate the output buffer. */
+void
+base64_encode (const char *restrict in, size_t inlen,
+               char *restrict out, size_t outlen)
+{
+  static const char b64str[64] =
+    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+  while (inlen && outlen)
+    {
+      *out++ = b64str[(to_uchar (in[0]) >> 2) & 0x3f];
+      if (!--outlen)
+        break;
+      *out++ = b64str[((to_uchar (in[0]) << 4)
+                       + (--inlen ? to_uchar (in[1]) >> 4 : 0))
+                      & 0x3f];
+      if (!--outlen)
+        break;
+      *out++ =
+        (inlen
+         ? b64str[((to_uchar (in[1]) << 2)
+                   + (--inlen ? to_uchar (in[2]) >> 6 : 0))
+                  & 0x3f]
+         : '=');
+      if (!--outlen)
+        break;
+      *out++ = inlen ? b64str[to_uchar (in[2]) & 0x3f] : '=';
+      if (!--outlen)
+        break;
+      if (inlen)
+        inlen--;
+      if (inlen)
+        in += 3;
+    }
+
+  if (outlen)
+    *out = '\0';
+}
+
+/* Allocate a buffer and store zero terminated base64 encoded data
+   from array IN of size INLEN, returning BASE64_LENGTH(INLEN), i.e.,
+   the length of the encoded data, excluding the terminating zero.  On
+   return, the OUT variable will hold a pointer to newly allocated
+   memory that must be deallocated by the caller.  If output string
+   length would overflow, 0 is returned and OUT is set to NULL.  If
+   memory allocation failed, OUT is set to NULL, and the return value
+   indicates length of the requested memory block, i.e.,
+   BASE64_LENGTH(inlen) + 1. */
+size_t
+base64_encode_alloc (const char *in, size_t inlen, char **out)
+{
+  size_t outlen = 1 + BASE64_LENGTH (inlen);
+
+  /* Check for overflow in outlen computation.
+   *
+   * If there is no overflow, outlen >= inlen.
+   *
+   * If the operation (inlen + 2) overflows then it yields at most +1, so
+   * outlen is 0.
+   *
+   * If the multiplication overflows, we lose at least half of the
+   * correct value, so the result is < ((inlen + 2) / 3) * 2, which is
+   * less than (inlen + 2) * 0.66667, which is less than inlen as soon as
+   * (inlen > 4).
+   */
+  if (inlen > outlen)
+    {
+      *out = NULL;
+      return 0;
+    }
+
+  *out = malloc (outlen);
+  if (!*out)
+    return outlen;
+
+  base64_encode (in, inlen, *out, outlen);
+
+  return outlen - 1;
+}
+
+/* With this approach this file works independent of the charset used
+   (think EBCDIC).  However, it does assume that the characters in the
+   Base64 alphabet (A-Za-z0-9+/) are encoded in 0..255.  POSIX
+   1003.1-2001 require that char and unsigned char are 8-bit
+   quantities, though, taking care of that problem.  But this may be a
+   potential problem on non-POSIX C99 platforms.
+
+   IBM C V6 for AIX mishandles "#define B64(x) ...'x'...", so use "_"
+   as the formal parameter rather than "x".  */
+#define B64(_)                                  \
+  ((_) == 'A' ? 0                               \
+   : (_) == 'B' ? 1                             \
+   : (_) == 'C' ? 2                             \
+   : (_) == 'D' ? 3                             \
+   : (_) == 'E' ? 4                             \
+   : (_) == 'F' ? 5                             \
+   : (_) == 'G' ? 6                             \
+   : (_) == 'H' ? 7                             \
+   : (_) == 'I' ? 8                             \
+   : (_) == 'J' ? 9                             \
+   : (_) == 'K' ? 10                            \
+   : (_) == 'L' ? 11                            \
+   : (_) == 'M' ? 12                            \
+   : (_) == 'N' ? 13                            \
+   : (_) == 'O' ? 14                            \
+   : (_) == 'P' ? 15                            \
+   : (_) == 'Q' ? 16                            \
+   : (_) == 'R' ? 17                            \
+   : (_) == 'S' ? 18                            \
+   : (_) == 'T' ? 19                            \
+   : (_) == 'U' ? 20                            \
+   : (_) == 'V' ? 21                            \
+   : (_) == 'W' ? 22                            \
+   : (_) == 'X' ? 23                            \
+   : (_) == 'Y' ? 24                            \
+   : (_) == 'Z' ? 25                            \
+   : (_) == 'a' ? 26                            \
+   : (_) == 'b' ? 27                            \
+   : (_) == 'c' ? 28                            \
+   : (_) == 'd' ? 29                            \
+   : (_) == 'e' ? 30                            \
+   : (_) == 'f' ? 31                            \
+   : (_) == 'g' ? 32                            \
+   : (_) == 'h' ? 33                            \
+   : (_) == 'i' ? 34                            \
+   : (_) == 'j' ? 35                            \
+   : (_) == 'k' ? 36                            \
+   : (_) == 'l' ? 37                            \
+   : (_) == 'm' ? 38                            \
+   : (_) == 'n' ? 39                            \
+   : (_) == 'o' ? 40                            \
+   : (_) == 'p' ? 41                            \
+   : (_) == 'q' ? 42                            \
+   : (_) == 'r' ? 43                            \
+   : (_) == 's' ? 44                            \
+   : (_) == 't' ? 45                            \
+   : (_) == 'u' ? 46                            \
+   : (_) == 'v' ? 47                            \
+   : (_) == 'w' ? 48                            \
+   : (_) == 'x' ? 49                            \
+   : (_) == 'y' ? 50                            \
+   : (_) == 'z' ? 51                            \
+   : (_) == '0' ? 52                            \
+   : (_) == '1' ? 53                            \
+   : (_) == '2' ? 54                            \
+   : (_) == '3' ? 55                            \
+   : (_) == '4' ? 56                            \
+   : (_) == '5' ? 57                            \
+   : (_) == '6' ? 58                            \
+   : (_) == '7' ? 59                            \
+   : (_) == '8' ? 60                            \
+   : (_) == '9' ? 61                            \
+   : (_) == '+' ? 62                            \
+   : (_) == '/' ? 63                            \
+   : -1)
+
+static const signed char b64[0x100] = {
+  B64 (0), B64 (1), B64 (2), B64 (3),
+  B64 (4), B64 (5), B64 (6), B64 (7),
+  B64 (8), B64 (9), B64 (10), B64 (11),
+  B64 (12), B64 (13), B64 (14), B64 (15),
+  B64 (16), B64 (17), B64 (18), B64 (19),
+  B64 (20), B64 (21), B64 (22), B64 (23),
+  B64 (24), B64 (25), B64 (26), B64 (27),
+  B64 (28), B64 (29), B64 (30), B64 (31),
+  B64 (32), B64 (33), B64 (34), B64 (35),
+  B64 (36), B64 (37), B64 (38), B64 (39),
+  B64 (40), B64 (41), B64 (42), B64 (43),
+  B64 (44), B64 (45), B64 (46), B64 (47),
+  B64 (48), B64 (49), B64 (50), B64 (51),
+  B64 (52), B64 (53), B64 (54), B64 (55),
+  B64 (56), B64 (57), B64 (58), B64 (59),
+  B64 (60), B64 (61), B64 (62), B64 (63),
+  B64 (64), B64 (65), B64 (66), B64 (67),
+  B64 (68), B64 (69), B64 (70), B64 (71),
+  B64 (72), B64 (73), B64 (74), B64 (75),
+  B64 (76), B64 (77), B64 (78), B64 (79),
+  B64 (80), B64 (81), B64 (82), B64 (83),
+  B64 (84), B64 (85), B64 (86), B64 (87),
+  B64 (88), B64 (89), B64 (90), B64 (91),
+  B64 (92), B64 (93), B64 (94), B64 (95),
+  B64 (96), B64 (97), B64 (98), B64 (99),
+  B64 (100), B64 (101), B64 (102), B64 (103),
+  B64 (104), B64 (105), B64 (106), B64 (107),
+  B64 (108), B64 (109), B64 (110), B64 (111),
+  B64 (112), B64 (113), B64 (114), B64 (115),
+  B64 (116), B64 (117), B64 (118), B64 (119),
+  B64 (120), B64 (121), B64 (122), B64 (123),
+  B64 (124), B64 (125), B64 (126), B64 (127),
+  B64 (128), B64 (129), B64 (130), B64 (131),
+  B64 (132), B64 (133), B64 (134), B64 (135),
+  B64 (136), B64 (137), B64 (138), B64 (139),
+  B64 (140), B64 (141), B64 (142), B64 (143),
+  B64 (144), B64 (145), B64 (146), B64 (147),
+  B64 (148), B64 (149), B64 (150), B64 (151),
+  B64 (152), B64 (153), B64 (154), B64 (155),
+  B64 (156), B64 (157), B64 (158), B64 (159),
+  B64 (160), B64 (161), B64 (162), B64 (163),
+  B64 (164), B64 (165), B64 (166), B64 (167),
+  B64 (168), B64 (169), B64 (170), B64 (171),
+  B64 (172), B64 (173), B64 (174), B64 (175),
+  B64 (176), B64 (177), B64 (178), B64 (179),
+  B64 (180), B64 (181), B64 (182), B64 (183),
+  B64 (184), B64 (185), B64 (186), B64 (187),
+  B64 (188), B64 (189), B64 (190), B64 (191),
+  B64 (192), B64 (193), B64 (194), B64 (195),
+  B64 (196), B64 (197), B64 (198), B64 (199),
+  B64 (200), B64 (201), B64 (202), B64 (203),
+  B64 (204), B64 (205), B64 (206), B64 (207),
+  B64 (208), B64 (209), B64 (210), B64 (211),
+  B64 (212), B64 (213), B64 (214), B64 (215),
+  B64 (216), B64 (217), B64 (218), B64 (219),
+  B64 (220), B64 (221), B64 (222), B64 (223),
+  B64 (224), B64 (225), B64 (226), B64 (227),
+  B64 (228), B64 (229), B64 (230), B64 (231),
+  B64 (232), B64 (233), B64 (234), B64 (235),
+  B64 (236), B64 (237), B64 (238), B64 (239),
+  B64 (240), B64 (241), B64 (242), B64 (243),
+  B64 (244), B64 (245), B64 (246), B64 (247),
+  B64 (248), B64 (249), B64 (250), B64 (251),
+  B64 (252), B64 (253), B64 (254), B64 (255)
+};
+
+#if UCHAR_MAX == 255
+# define uchar_in_range(c) true
+#else
+# define uchar_in_range(c) ((c) <= 255)
+#endif
+
+/* Return true if CH is a character from the Base64 alphabet, and
+   false otherwise.  Note that '=' is padding and not considered to be
+   part of the alphabet.  */
+bool
+isbase64 (char ch)
+{
+  return uchar_in_range (to_uchar (ch)) && 0 <= b64[to_uchar (ch)];
+}
+
+/* Initialize decode-context buffer, CTX.  */
+void
+base64_decode_ctx_init (struct base64_decode_context *ctx)
+{
+  ctx->i = 0;
+}
+
+/* If CTX->i is 0 or 4, there are four or more bytes in [*IN..IN_END), and
+   none of those four is a newline, then return *IN.  Otherwise, copy up to
+   4 - CTX->i non-newline bytes from that range into CTX->buf, starting at
+   index CTX->i and setting CTX->i to reflect the number of bytes copied,
+   and return CTX->buf.  In either case, advance *IN to point to the byte
+   after the last one processed, and set *N_NON_NEWLINE to the number of
+   verified non-newline bytes accessible through the returned pointer.  */
+static inline char *
+get_4 (struct base64_decode_context *ctx,
+       char const *restrict *in, char const *restrict in_end,
+       size_t *n_non_newline)
+{
+  if (ctx->i == 4)
+    ctx->i = 0;
+
+  if (ctx->i == 0)
+    {
+      char const *t = *in;
+      if (4 <= in_end - *in && memchr (t, '\n', 4) == NULL)
+        {
+          /* This is the common case: no newline.  */
+          *in += 4;
+          *n_non_newline = 4;
+          return (char *) t;
+        }
+    }
+
+  {
+    /* Copy non-newline bytes into BUF.  */
+    char const *p = *in;
+    while (p < in_end)
+      {
+        char c = *p++;
+        if (c != '\n')
+          {
+            ctx->buf[ctx->i++] = c;
+            if (ctx->i == 4)
+              break;
+          }
+      }
+
+    *in = p;
+    *n_non_newline = ctx->i;
+    return ctx->buf;
+  }
+}
+
+#define return_false                            \
+  do                                            \
+    {                                           \
+      *outp = out;                              \
+      return false;                             \
+    }                                           \
+  while (false)
+
+/* Decode up to four bytes of base64-encoded data, IN, of length INLEN
+   into the output buffer, *OUT, of size *OUTLEN bytes.  Return true if
+   decoding is successful, false otherwise.  If *OUTLEN is too small,
+   as many bytes as possible are written to *OUT.  On return, advance
+   *OUT to point to the byte after the last one written, and decrement
+   *OUTLEN to reflect the number of bytes remaining in *OUT.  */
+static inline bool
+decode_4 (char const *restrict in, size_t inlen,
+          char *restrict *outp, size_t *outleft)
+{
+  char *out = *outp;
+  if (inlen < 2)
+    return false;
+
+  if (!isbase64 (in[0]) || !isbase64 (in[1]))
+    return false;
+
+  if (*outleft)
+    {
+      *out++ = ((b64[to_uchar (in[0])] << 2)
+                | (b64[to_uchar (in[1])] >> 4));
+      --*outleft;
+    }
+
+  if (inlen == 2)
+    return_false;
+
+  if (in[2] == '=')
+    {
+      if (inlen != 4)
+        return_false;
+
+      if (in[3] != '=')
+        return_false;
+    }
+  else
+    {
+      if (!isbase64 (in[2]))
+        return_false;
+
+      if (*outleft)
+        {
+          *out++ = (((b64[to_uchar (in[1])] << 4) & 0xf0)
+                    | (b64[to_uchar (in[2])] >> 2));
+          --*outleft;
+        }
+
+      if (inlen == 3)
+        return_false;
+
+      if (in[3] == '=')
+        {
+          if (inlen != 4)
+            return_false;
+        }
+      else
+        {
+          if (!isbase64 (in[3]))
+            return_false;
+
+          if (*outleft)
+            {
+              *out++ = (((b64[to_uchar (in[2])] << 6) & 0xc0)
+                        | b64[to_uchar (in[3])]);
+              --*outleft;
+            }
+        }
+    }
+
+  *outp = out;
+  return true;
+}
+
+/* Decode base64-encoded input array IN of length INLEN to output array
+   OUT that can hold *OUTLEN bytes.  The input data may be interspersed
+   with newlines.  Return true if decoding was successful, i.e. if the
+   input was valid base64 data, false otherwise.  If *OUTLEN is too
+   small, as many bytes as possible will be written to OUT.  On return,
+   *OUTLEN holds the length of decoded bytes in OUT.  Note that as soon
+   as any non-alphabet, non-newline character is encountered, decoding
+   is stopped and false is returned.  If INLEN is zero, then process
+   only whatever data is stored in CTX.
+
+   Initially, CTX must have been initialized via base64_decode_ctx_init.
+   Subsequent calls to this function must reuse whatever state is recorded
+   in that buffer.  It is necessary for when a quadruple of base64 input
+   bytes spans two input buffers.
+
+   If CTX is NULL then newlines are treated as garbage and the input
+   buffer is processed as a unit.  */
+
+bool
+base64_decode_ctx (struct base64_decode_context *ctx,
+                   const char *restrict in, size_t inlen,
+                   char *restrict out, size_t *outlen)
+{
+  size_t outleft = *outlen;
+  bool ignore_newlines = ctx != NULL;
+  bool flush_ctx = false;
+  unsigned int ctx_i = 0;
+
+  if (ignore_newlines)
+    {
+      ctx_i = ctx->i;
+      flush_ctx = inlen == 0;
+    }
+
+
+  while (true)
+    {
+      size_t outleft_save = outleft;
+      if (ctx_i == 0 && !flush_ctx)
+        {
+          while (true)
+            {
+              /* Save a copy of outleft, in case we need to re-parse this
+                 block of four bytes.  */
+              outleft_save = outleft;
+              if (!decode_4 (in, inlen, &out, &outleft))
+                break;
+
+              in += 4;
+              inlen -= 4;
+            }
+        }
+
+      if (inlen == 0 && !flush_ctx)
+        break;
+
+      /* Handle the common case of 72-byte wrapped lines.
+         This also handles any other multiple-of-4-byte wrapping.  */
+      if (inlen && *in == '\n' && ignore_newlines)
+        {
+          ++in;
+          --inlen;
+          continue;
+        }
+
+      /* Restore OUT and OUTLEFT.  */
+      out -= outleft_save - outleft;
+      outleft = outleft_save;
+
+      {
+        char const *in_end = in + inlen;
+        char const *non_nl;
+
+        if (ignore_newlines)
+          non_nl = get_4 (ctx, &in, in_end, &inlen);
+        else
+          non_nl = in;  /* Might have nl in this case. */
+
+        /* If the input is empty or consists solely of newlines (0 
non-newlines),
+           then we're done.  Likewise if there are fewer than 4 bytes when not
+           flushing context and not treating newlines as garbage.  */
+        if (inlen == 0 || (inlen < 4 && !flush_ctx && ignore_newlines))
+          {
+            inlen = 0;
+            break;
+          }
+        if (!decode_4 (non_nl, inlen, &out, &outleft))
+          break;
+
+        inlen = in_end - in;
+      }
+    }
+
+  *outlen -= outleft;
+
+  return inlen == 0;
+}
+
+/* Allocate an output buffer in *OUT, and decode the base64 encoded
+   data stored in IN of size INLEN to the *OUT buffer.  On return, the
+   size of the decoded data is stored in *OUTLEN.  OUTLEN may be NULL,
+   if the caller is not interested in the decoded length.  *OUT may be
+   NULL to indicate an out of memory error, in which case *OUTLEN
+   contains the size of the memory block needed.  The function returns
+   true on successful decoding and memory allocation errors.  (Use the
+   *OUT and *OUTLEN parameters to differentiate between successful
+   decoding and memory error.)  The function returns false if the
+   input was invalid, in which case *OUT is NULL and *OUTLEN is
+   undefined. */
+bool
+base64_decode_alloc_ctx (struct base64_decode_context *ctx,
+                         const char *in, size_t inlen, char **out,
+                         size_t *outlen)
+{
+  /* This may allocate a few bytes too many, depending on input,
+     but it's not worth the extra CPU time to compute the exact size.
+     The exact size is 3 * (inlen + (ctx ? ctx->i : 0)) / 4, minus 1 if the
+     input ends with "=" and minus another 1 if the input ends with "==".
+     Dividing before multiplying avoids the possibility of overflow.  */
+  size_t needlen = 3 * (inlen / 4) + 3;
+
+  *out = malloc (needlen);
+  if (!*out)
+    return true;
+
+  if (!base64_decode_ctx (ctx, in, inlen, *out, &needlen))
+    {
+      free (*out);
+      *out = NULL;
+      return false;
+    }
+
+  if (outlen)
+    *outlen = needlen;
+
+  return true;
+}
diff --git a/gl/base64.h b/gl/base64.h
new file mode 100644
index 0000000..537a3b8
--- /dev/null
+++ b/gl/base64.h
@@ -0,0 +1,61 @@
+/* base64.h -- Encode binary data using printable characters.
+   Copyright (C) 2004-2006, 2009-2012 Free Software Foundation, Inc.
+   Written by Simon Josefsson.
+
+   This program 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, or (at your option)
+   any later version.
+
+   This program 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, write to the Free Software Foundation,
+   Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
+
+#ifndef BASE64_H
+# define BASE64_H
+
+/* Get size_t. */
+# include <stddef.h>
+
+/* Get bool. */
+# include <stdbool.h>
+
+/* This uses that the expression (n+(k-1))/k means the smallest
+   integer >= n/k, i.e., the ceiling of n/k.  */
+# define BASE64_LENGTH(inlen) ((((inlen) + 2) / 3) * 4)
+
+struct base64_decode_context
+{
+  unsigned int i;
+  char buf[4];
+};
+
+extern bool isbase64 (char ch) _GL_ATTRIBUTE_CONST;
+
+extern void base64_encode (const char *restrict in, size_t inlen,
+                           char *restrict out, size_t outlen);
+
+extern size_t base64_encode_alloc (const char *in, size_t inlen, char **out);
+
+extern void base64_decode_ctx_init (struct base64_decode_context *ctx);
+
+extern bool base64_decode_ctx (struct base64_decode_context *ctx,
+                               const char *restrict in, size_t inlen,
+                               char *restrict out, size_t *outlen);
+
+extern bool base64_decode_alloc_ctx (struct base64_decode_context *ctx,
+                                     const char *in, size_t inlen,
+                                     char **out, size_t *outlen);
+
+#define base64_decode(in, inlen, out, outlen) \
+        base64_decode_ctx (NULL, in, inlen, out, outlen)
+
+#define base64_decode_alloc(in, inlen, out, outlen) \
+        base64_decode_alloc_ctx (NULL, in, inlen, out, outlen)
+
+#endif /* BASE64_H */
diff --git a/gl/m4/sockets.m4 b/gl/m4/base64.m4
similarity index 50%
copy from gl/m4/sockets.m4
copy to gl/m4/base64.m4
index e3738d9..fa0c192 100644
--- a/gl/m4/sockets.m4
+++ b/gl/m4/base64.m4
@@ -1,17 +1,16 @@
-# sockets.m4 serial 7
-dnl Copyright (C) 2008-2012 Free Software Foundation, Inc.
+# base64.m4 serial 3
+dnl Copyright (C) 2004, 2006, 2009-2012 Free Software Foundation, Inc.
 dnl This file is free software; the Free Software Foundation
 dnl gives unlimited permission to copy and/or distribute it,
 dnl with or without modifications, as long as this notice is preserved.
 
-AC_DEFUN([gl_SOCKETS],
+AC_DEFUN([gl_FUNC_BASE64],
 [
-  AC_REQUIRE([AC_C_INLINE])
-  AC_REQUIRE([gl_SOCKETLIB])
-  gl_PREREQ_SOCKETS
+  gl_PREREQ_BASE64
 ])
 
-# Prerequisites of lib/sockets.c.
-AC_DEFUN([gl_PREREQ_SOCKETS], [
-  :
+# Prerequisites of lib/base64.c.
+AC_DEFUN([gl_PREREQ_BASE64], [
+  AC_REQUIRE([AC_C_INLINE])
+  AC_REQUIRE([AC_C_RESTRICT])
 ])
diff --git a/gl/m4/gnulib-cache.m4 b/gl/m4/gnulib-cache.m4
index 90b612a..4c81916 100644
--- a/gl/m4/gnulib-cache.m4
+++ b/gl/m4/gnulib-cache.m4
@@ -27,7 +27,7 @@
 
 
 # Specification in the form of a command-line invocation:
-#   gnulib-tool --import --dir=. --local-dir=gl/override --lib=libgnu 
--source-base=gl --m4-base=gl/m4 --doc-base=doc --tests-base=gl/tests 
--aux-dir=build-aux --with-tests --avoid=alignof-tests --avoid=lock-tests 
--avoid=lseek-tests --no-conditional-dependencies --libtool --macro-prefix=gl 
--no-vc-files accept alloca alphasort argp bind byteswap c-ctype close connect 
error extensions func gendocs getaddrinfo getpass getsubopt gettext gettime 
havelib inet_ntop inet_pton lib-msvc-compat lib-symbol-versions listen 
maintainer-makefile manywarnings memmem-simple minmax netdb netinet_in 
pmccabe2html progname read-file recv recvfrom scandir select send sendto 
setsockopt shutdown snprintf socket sockets socklen stdint strcase strverscmp 
sys_socket sys_stat time_r timespec u64 unistd valgrind-tests vasprintf 
version-etc version-etc-fsf vfprintf-posix vprintf-posix vsnprintf warnings
+#   gnulib-tool --import --dir=. --local-dir=gl/override --lib=libgnu 
--source-base=gl --m4-base=gl/m4 --doc-base=doc --tests-base=gl/tests 
--aux-dir=build-aux --with-tests --avoid=alignof-tests --avoid=lock-tests 
--avoid=lseek-tests --no-conditional-dependencies --libtool --macro-prefix=gl 
--no-vc-files accept alloca alphasort argp base64 bind byteswap c-ctype close 
connect error extensions func gendocs getaddrinfo getpass getsubopt gettext 
gettime havelib inet_ntop inet_pton lib-msvc-compat lib-symbol-versions listen 
maintainer-makefile manywarnings memmem-simple minmax netdb netinet_in 
pmccabe2html progname read-file recv recvfrom scandir select send sendto 
setsockopt shutdown snprintf socket sockets socklen stdint strcase strtok_r 
strverscmp sys_socket sys_stat time_r timespec u64 unistd valgrind-tests 
vasprintf version-etc version-etc-fsf vfprintf-posix vprintf-posix vsnprintf 
warnings
 
 # Specification in the form of a few gnulib-tool.m4 macro invocations:
 gl_LOCAL_DIR([gl/override])
@@ -36,6 +36,7 @@ gl_MODULES([
   alloca
   alphasort
   argp
+  base64
   bind
   byteswap
   c-ctype
@@ -79,6 +80,7 @@ gl_MODULES([
   socklen
   stdint
   strcase
+  strtok_r
   strverscmp
   sys_socket
   sys_stat
diff --git a/gl/m4/gnulib-comp.m4 b/gl/m4/gnulib-comp.m4
index a0edea2..599aae6 100644
--- a/gl/m4/gnulib-comp.m4
+++ b/gl/m4/gnulib-comp.m4
@@ -48,6 +48,8 @@ AC_DEFUN([gl_EARLY],
   # Code from module argp-tests:
   # Code from module arpa_inet:
   # Code from module arpa_inet-tests:
+  # Code from module base64:
+  # Code from module base64-tests:
   # Code from module binary-io:
   # Code from module binary-io-tests:
   # Code from module bind:
@@ -287,6 +289,7 @@ AC_DEFUN([gl_EARLY],
   # Code from module strndup:
   # Code from module strnlen:
   # Code from module strnlen-tests:
+  # Code from module strtok_r:
   # Code from module strverscmp:
   # Code from module strverscmp-tests:
   # Code from module symlink:
@@ -382,6 +385,7 @@ m4_ifdef([AM_XGETTEXT_OPTION],
    AM_][XGETTEXT_OPTION([--flag=argp_failure:4:c-format])])
 gl_HEADER_ARPA_INET
 AC_PROG_MKDIR_P
+gl_FUNC_BASE64
 AC_REQUIRE([gl_HEADER_SYS_SOCKET])
 if test "$ac_cv_header_winsock2_h" = yes; then
   AC_LIBOBJ([bind])
@@ -777,6 +781,12 @@ if test $HAVE_DECL_STRNLEN = 0 || test $REPLACE_STRNLEN = 
1; then
   gl_PREREQ_STRNLEN
 fi
 gl_STRING_MODULE_INDICATOR([strnlen])
+gl_FUNC_STRTOK_R
+if test $HAVE_STRTOK_R = 0 || test $REPLACE_STRTOK_R = 1; then
+  AC_LIBOBJ([strtok_r])
+  gl_PREREQ_STRTOK_R
+fi
+gl_STRING_MODULE_INDICATOR([strtok_r])
 gl_FUNC_STRVERSCMP
 if test $HAVE_STRVERSCMP = 0; then
   AC_LIBOBJ([strverscmp])
@@ -1125,6 +1135,8 @@ AC_DEFUN([gl_FILE_LIST], [
   lib/arpa_inet.in.h
   lib/asnprintf.c
   lib/asprintf.c
+  lib/base64.c
+  lib/base64.h
   lib/basename-lgpl.c
   lib/bind.c
   lib/byteswap.in.h
@@ -1255,6 +1267,7 @@ AC_DEFUN([gl_FILE_LIST], [
   lib/strncasecmp.c
   lib/strndup.c
   lib/strnlen.c
+  lib/strtok_r.c
   lib/strverscmp.c
   lib/sys_select.in.h
   lib/sys_socket.in.h
@@ -1286,6 +1299,7 @@ AC_DEFUN([gl_FILE_LIST], [
   m4/alphasort.m4
   m4/argp.m4
   m4/arpa_inet_h.m4
+  m4/base64.m4
   m4/byteswap.m4
   m4/clock_time.m4
   m4/close.m4
@@ -1426,6 +1440,7 @@ AC_DEFUN([gl_FILE_LIST], [
   m4/strings_h.m4
   m4/strndup.m4
   m4/strnlen.m4
+  m4/strtok_r.m4
   m4/strverscmp.m4
   m4/symlink.m4
   m4/sys_ioctl_h.m4
@@ -1468,6 +1483,7 @@ AC_DEFUN([gl_FILE_LIST], [
   tests/test-argp-2.sh
   tests/test-argp.c
   tests/test-arpa_inet.c
+  tests/test-base64.c
   tests/test-binary-io.c
   tests/test-binary-io.sh
   tests/test-bind.c
diff --git a/gl/m4/stdalign.m4 b/gl/m4/stdalign.m4
index 9752ba5..d78a589 100644
--- a/gl/m4/stdalign.m4
+++ b/gl/m4/stdalign.m4
@@ -14,13 +14,26 @@ AC_DEFUN([gl_STDALIGN_H],
     [AC_COMPILE_IFELSE(
        [AC_LANG_PROGRAM(
           [[#include <stdalign.h>
-            int align_int = alignof (int) + _Alignof (double);
+            #include <stddef.h>
+
+            /* Test that alignof yields a result consistent with offsetof.
+               This catches GCC bug 52023
+               <http://gcc.gnu.org/bugzilla/show_bug.cgi?id=52023>.  */
+            #ifdef __cplusplus
+               template <class t> struct alignof_helper { char a; t b; };
+            # define ao(type) offsetof (alignof_helper<type>, b)
+            #else
+            # define ao(type) offsetof (struct { char a; type b; }, b)
+            #endif
+            char test1[_Alignof (double) == ao (double) ? 1 : -1];
+            char test2[alignof (long int) == ao (long int) ? 1 : -1];
 
             /* Test _Alignas only on platforms where gnulib can help.  */
             #if \
                 (__GNUC__ || __IBMC__ || __IBMCPP__ \
                  || 0x5110 <= __SUNPRO_C || 1300 <= _MSC_VER)
               int alignas (8) alignas_int = 1;
+              char test3[8 <= _Alignof (alignas_int) ? 1 : -1];
             #endif
           ]])],
        [gl_cv_header_working_stdalign_h=yes],
diff --git a/gl/m4/strtok_r.m4 b/gl/m4/strtok_r.m4
new file mode 100644
index 0000000..60e33a6
--- /dev/null
+++ b/gl/m4/strtok_r.m4
@@ -0,0 +1,74 @@
+# strtok_r.m4 serial 13
+dnl Copyright (C) 2002-2004, 2006-2007, 2009-2012 Free Software Foundation,
+dnl Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+AC_DEFUN([gl_FUNC_STRTOK_R],
+[
+  dnl The strtok_r() declaration in lib/string.in.h uses 'restrict'.
+  AC_REQUIRE([AC_C_RESTRICT])
+
+  AC_REQUIRE([gl_HEADER_STRING_H_DEFAULTS])
+  AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles
+  AC_CHECK_FUNCS([strtok_r])
+  if test $ac_cv_func_strtok_r = yes; then
+    HAVE_STRTOK_R=1
+    dnl glibc 2.7 has a bug in strtok_r that causes a segmentation fault
+    dnl when the second argument to strtok_r is a constant string that has
+    dnl exactly one byte and compiling with optimization.  This bug is, for
+    dnl example, present in the glibc 2.7-18 package in Debian "lenny".
+    dnl See <http://sources.redhat.com/bugzilla/show_bug.cgi?id=5614>.
+    AC_CACHE_CHECK([whether strtok_r works], [gl_cv_func_strtok_r_works],
+      [AC_RUN_IFELSE(
+         [AC_LANG_PROGRAM([[
+              #ifndef __OPTIMIZE__
+              # define __OPTIMIZE__ 1
+              #endif
+              #undef __OPTIMIZE_SIZE__
+              #undef __NO_INLINE__
+              #include <stdlib.h>
+              #include <string.h>
+            ]],
+            [[static const char dummy[] = "\177\01a";
+              char delimiters[] = "xxxxxxxx";
+              char *save_ptr = (char *) dummy;
+              strtok_r (delimiters, "x", &save_ptr);
+              strtok_r (NULL, "x", &save_ptr);
+              return 0;
+            ]])
+         ],
+         [gl_cv_func_strtok_r_works=yes],
+         [gl_cv_func_strtok_r_works=no],
+         [
+changequote(,)dnl
+          case "$host_os" in
+                    # Guess no on glibc systems.
+            *-gnu*) gl_cv_func_strtok_r_works="guessing no";;
+            *)      gl_cv_func_strtok_r_works="guessing yes";;
+          esac
+changequote([,])dnl
+         ])
+      ])
+    case "$gl_cv_func_strtok_r_works" in
+      *no)
+        dnl We could set REPLACE_STRTOK_R=1 here, but it's only the macro
+        dnl version in <bits/string2.h> which is wrong. The code compiled
+        dnl into libc is fine.
+        UNDEFINE_STRTOK_R=1
+        ;;
+    esac
+  else
+    HAVE_STRTOK_R=0
+  fi
+  AC_CHECK_DECLS_ONCE([strtok_r])
+  if test $ac_cv_have_decl_strtok_r = no; then
+    HAVE_DECL_STRTOK_R=0
+  fi
+])
+
+# Prerequisites of lib/strtok_r.c.
+AC_DEFUN([gl_PREREQ_STRTOK_R], [
+  :
+])
diff --git a/gl/strtok_r.c b/gl/strtok_r.c
new file mode 100644
index 0000000..e1499bc
--- /dev/null
+++ b/gl/strtok_r.c
@@ -0,0 +1,76 @@
+/* Reentrant string tokenizer.  Generic version.
+   Copyright (C) 1991, 1996-1999, 2001, 2004, 2007, 2009-2012 Free Software
+   Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   This program 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.
+
+   This program 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/>.  */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <string.h>
+
+#ifdef _LIBC
+# undef strtok_r
+# undef __strtok_r
+#else
+# define __strtok_r strtok_r
+# define __rawmemchr strchr
+#endif
+
+/* Parse S into tokens separated by characters in DELIM.
+   If S is NULL, the saved pointer in SAVE_PTR is used as
+   the next starting point.  For example:
+        char s[] = "-abc-=-def";
+        char *sp;
+        x = strtok_r(s, "-", &sp);      // x = "abc", sp = "=-def"
+        x = strtok_r(NULL, "-=", &sp);  // x = "def", sp = NULL
+        x = strtok_r(NULL, "=", &sp);   // x = NULL
+                // s = "abc\0-def\0"
+*/
+char *
+__strtok_r (char *s, const char *delim, char **save_ptr)
+{
+  char *token;
+
+  if (s == NULL)
+    s = *save_ptr;
+
+  /* Scan leading delimiters.  */
+  s += strspn (s, delim);
+  if (*s == '\0')
+    {
+      *save_ptr = s;
+      return NULL;
+    }
+
+  /* Find the end of the token.  */
+  token = s;
+  s = strpbrk (token, delim);
+  if (s == NULL)
+    /* This token finishes the string.  */
+    *save_ptr = __rawmemchr (token, '\0');
+  else
+    {
+      /* Terminate the token and make *SAVE_PTR point past it.  */
+      *s = '\0';
+      *save_ptr = s + 1;
+    }
+  return token;
+}
+#ifdef weak_alias
+libc_hidden_def (__strtok_r)
+weak_alias (__strtok_r, strtok_r)
+#endif
diff --git a/gl/tests/Makefile.am b/gl/tests/Makefile.am
index fa7f5b4..c3c8f3c 100644
--- a/gl/tests/Makefile.am
+++ b/gl/tests/Makefile.am
@@ -93,6 +93,14 @@ EXTRA_DIST += test-arpa_inet.c
 
 ## end   gnulib module arpa_inet-tests
 
+## begin gnulib module base64-tests
+
+TESTS += test-base64
+check_PROGRAMS += test-base64
+EXTRA_DIST += test-base64.c macros.h
+
+## end   gnulib module base64-tests
+
 ## begin gnulib module binary-io
 
 libtests_a_SOURCES += binary-io.h
diff --git a/gl/tests/malloca.c b/gl/tests/malloca.c
index 3309088..009ca0c 100644
--- a/gl/tests/malloca.c
+++ b/gl/tests/malloca.c
@@ -22,6 +22,8 @@
 /* Specification.  */
 #include "malloca.h"
 
+#include <stdint.h>
+
 #include "verify.h"
 
 /* The speed critical point in this file is freea() applied to an alloca()
@@ -85,7 +87,7 @@ mmalloca (size_t n)
           ((int *) p)[-1] = MAGIC_NUMBER;
 
           /* Enter p into the hash table.  */
-          slot = (unsigned long) p % HASH_TABLE_SIZE;
+          slot = (uintptr_t) p % HASH_TABLE_SIZE;
           ((struct header *) (p - HEADER_SIZE))->next = mmalloca_results[slot];
           mmalloca_results[slot] = p;
 
@@ -118,7 +120,7 @@ freea (void *p)
         {
           /* Looks like a mmalloca() result.  To see whether it really is one,
              perform a lookup in the hash table.  */
-          size_t slot = (unsigned long) p % HASH_TABLE_SIZE;
+          size_t slot = (uintptr_t) p % HASH_TABLE_SIZE;
           void **chain = &mmalloca_results[slot];
           for (; *chain != NULL;)
             {
diff --git a/gl/tests/test-base64.c b/gl/tests/test-base64.c
new file mode 100644
index 0000000..9a533c5
--- /dev/null
+++ b/gl/tests/test-base64.c
@@ -0,0 +1,238 @@
+/* Self tests for base64.
+   Copyright (C) 2004, 2008-2012 Free Software Foundation, Inc.
+   Written by Simon Josefsson.
+
+   This program 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.
+
+   This program 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 "base64.h"
+
+#include <stddef.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+
+#include "macros.h"
+
+int
+main (void)
+{
+  const char *in = "abcdefghijklmnop";
+  const char *b64in = "YWJjZGVmZw==";
+  char out[255];
+  size_t len;
+  bool ok;
+  char *p;
+
+  memset (out, 0x42, sizeof (out));
+  base64_encode (in, 0, out, 0);
+  ASSERT (out[0] == '\x42');
+
+  memset (out, 0x42, sizeof (out));
+  base64_encode (in, 1, out, 1);
+  ASSERT (memcmp (out, "YQ==", 1) == 0);
+
+  memset (out, 0x42, sizeof (out));
+  base64_encode (in, 1, out, 2);
+  ASSERT (memcmp (out, "YQ==", 2) == 0);
+
+  memset (out, 0x42, sizeof (out));
+  base64_encode (in, 1, out, 3);
+  ASSERT (memcmp (out, "YQ==", 3) == 0);
+
+  memset (out, 0x42, sizeof (out));
+  base64_encode (in, 1, out, 4);
+  ASSERT (memcmp (out, "YQ==", 4) == 0);
+
+  memset (out, 0x42, sizeof (out));
+  base64_encode (in, 1, out, 8);
+  ASSERT (memcmp (out, "YQ==", 4) == 0);
+
+  memset (out, 0x42, sizeof (out));
+  base64_encode (in, 2, out, 4);
+  ASSERT (memcmp (out, "YWI=", 4) == 0);
+
+  memset (out, 0x42, sizeof (out));
+  base64_encode (in, 3, out, 4);
+  ASSERT (memcmp (out, "YWJj", 4) == 0);
+
+  memset (out, 0x42, sizeof (out));
+  base64_encode (in, 4, out, 5);
+  ASSERT (memcmp (out, "YWJjZA==", 5) == 0);
+
+  memset (out, 0x42, sizeof (out));
+  base64_encode (in, 4, out, 100);
+  ASSERT (memcmp (out, "YWJjZA==", 6) == 0);
+
+  /* Decode. */
+
+  memset (out, 0x42, sizeof (out));
+  len = 0;
+  ok = base64_decode (b64in, 4, out, &len);
+  ASSERT (ok);
+  ASSERT (len == 0);
+
+  memset (out, 0x42, sizeof (out));
+  len = 1;
+  ok = base64_decode (b64in, 4, out, &len);
+  ASSERT (ok);
+  ASSERT (len == 1);
+  ASSERT (memcmp (out, "abcdefg", 1) == 0);
+
+  memset (out, 0x42, sizeof (out));
+  len = 2;
+  ok = base64_decode (b64in, 4, out, &len);
+  ASSERT (ok);
+  ASSERT (len == 2);
+  ASSERT (memcmp (out, "abcdefg", 2) == 0);
+
+  memset (out, 0x42, sizeof (out));
+  len = 3;
+  ok = base64_decode (b64in, 4, out, &len);
+  ASSERT (ok);
+  ASSERT (len == 3);
+  ASSERT (memcmp (out, "abcdefg", 3) == 0);
+
+  memset (out, 0x42, sizeof (out));
+  len = 4;
+  ok = base64_decode (b64in, 4, out, &len);
+  ASSERT (ok);
+  ASSERT (len == 3);
+  ASSERT (memcmp (out, "abcdefg", 3) == 0);
+
+  memset (out, 0x42, sizeof (out));
+  len = 100;
+  ok = base64_decode (b64in, strlen (b64in), out, &len);
+  ASSERT (ok);
+  ASSERT (len == 7);
+  ASSERT (memcmp (out, "abcdefg", 7) == 0);
+
+  /* Allocating encode */
+
+  len = base64_encode_alloc (in, strlen (in), &p);
+  ASSERT (len == 24);
+  ASSERT (strcmp (p, "YWJjZGVmZ2hpamtsbW5vcA==") == 0);
+  free (p);
+
+  len = base64_encode_alloc (in, SIZE_MAX - 5, &p);
+  ASSERT (len == 0);
+
+  /* Decode context function */
+  {
+    struct base64_decode_context ctx;
+
+    base64_decode_ctx_init (&ctx);
+
+    len = sizeof (out);
+    ok = base64_decode_ctx (&ctx, b64in, strlen (b64in), out, &len);
+    ASSERT (ok);
+    ASSERT (len == 7);
+    ASSERT (memcmp (out, "abcdefg", len) == 0);
+  }
+
+  /* Allocating decode context function */
+
+  ok = base64_decode_alloc_ctx (NULL, b64in, strlen (b64in), &p, &len);
+  ASSERT (ok);
+  ASSERT (len == 7);
+  ASSERT (memcmp (out, "abcdefg", len) == 0);
+  free (p);
+
+  {
+    struct base64_decode_context ctx;
+    const char *newlineb64 = "YWJjZG\nVmZ2hp\namtsbW5vcA==";
+
+    base64_decode_ctx_init (&ctx);
+
+    ok = base64_decode_alloc_ctx (&ctx, newlineb64, strlen (newlineb64), &p, 
&len);
+    ASSERT (ok);
+    ASSERT (len == strlen (in));
+    ASSERT (memcmp (p, in, len) == 0);
+    free (p);
+  }
+
+  {
+    struct base64_decode_context ctx;
+    base64_decode_ctx_init (&ctx);
+
+    ok = base64_decode_alloc_ctx (&ctx, "YW\nJjZGVmZ2hp", 13, &p, &len);
+    ASSERT (ok);
+    ASSERT (len == 9);
+    ASSERT (memcmp (p, "abcdefghi", len) == 0);
+    free (p);
+
+    base64_decode_ctx_init (&ctx);
+
+    ok = base64_decode_alloc_ctx (&ctx, "YW\n", 3, &p, &len);
+    ASSERT (ok);
+    ASSERT (len == 0);
+    free (p);
+
+    ok = base64_decode_alloc_ctx (&ctx, "JjZGVmZ2", 8, &p, &len);
+    ASSERT (ok);
+    ASSERT (len == 6);
+    ASSERT (memcmp (p, "abcdef", len) == 0);
+    free (p);
+
+    ok = base64_decode_alloc_ctx (&ctx, "hp", 2, &p, &len);
+    ASSERT (ok);
+    ASSERT (len == 3);
+    ASSERT (memcmp (p, "ghi", len) == 0);
+    free (p);
+
+    ok = base64_decode_alloc_ctx (&ctx, "", 0, &p, &len);
+    ASSERT (ok);
+    free (p);
+  }
+
+  {
+    struct base64_decode_context ctx;
+    const char *newlineb64 = "\n\n\n\n\n";
+
+    base64_decode_ctx_init (&ctx);
+
+    ok = base64_decode_alloc_ctx (&ctx, newlineb64, strlen (newlineb64), &p, 
&len);
+    ASSERT (ok);
+    ASSERT (len == 0);
+    free (p);
+  }
+
+  ok = base64_decode_alloc_ctx (NULL, " ! ", 3, &p, &len);
+  ASSERT (!ok);
+
+  ok = base64_decode_alloc_ctx (NULL, "abc\ndef", 7, &p, &len);
+  ASSERT (!ok);
+
+  ok = base64_decode_alloc_ctx (NULL, "aa", 2, &p, &len);
+  ASSERT (!ok);
+
+  ok = base64_decode_alloc_ctx (NULL, "aa=", 3, &p, &len);
+  ASSERT (!ok);
+
+  ok = base64_decode_alloc_ctx (NULL, "aax", 3, &p, &len);
+  ASSERT (!ok);
+
+  ok = base64_decode_alloc_ctx (NULL, "aa=X", 4, &p, &len);
+  ASSERT (!ok);
+
+  ok = base64_decode_alloc_ctx (NULL, "aa=X", 4, &p, &len);
+  ASSERT (!ok);
+
+  ok = base64_decode_alloc_ctx (NULL, "aax=X", 5, &p, &len);
+  ASSERT (!ok);
+
+  return 0;
+}
diff --git a/lib/Makefile.am b/lib/Makefile.am
index d692a58..820f090 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -74,7 +74,7 @@ COBJECTS = gnutls_record.c gnutls_compress.c debug.c 
gnutls_cipher.c  \
        gnutls_rsa_export.c gnutls_helper.c gnutls_supplemental.c       \
        random.c crypto-api.c gnutls_privkey.c gnutls_pcert.c           \
        gnutls_pubkey.c locks.c hash.c gnutls_dtls.c system_override.c  \
-       crypto-backend.c
+       crypto-backend.c verify-ssh.c
 
 if ENABLE_PKCS11
 COBJECTS += pkcs11.c pkcs11_privkey.c pkcs11_write.c pkcs11_secret.c
diff --git a/lib/includes/gnutls/gnutls.h.in b/lib/includes/gnutls/gnutls.h.in
index 45008ae..415e282 100644
--- a/lib/includes/gnutls/gnutls.h.in
+++ b/lib/includes/gnutls/gnutls.h.in
@@ -526,6 +526,7 @@ extern "C"
  * @GNUTLS_CRT_UNKNOWN: Unknown certificate type.
  * @GNUTLS_CRT_X509: X.509 Certificate.
  * @GNUTLS_CRT_OPENPGP: OpenPGP certificate.
+ * @GNUTLS_CRT_RAW: Raw public key (SubjectPublicKey)
  *
  * Enumeration of different certificate types.
  */
@@ -533,7 +534,8 @@ extern "C"
   {
     GNUTLS_CRT_UNKNOWN = 0,
     GNUTLS_CRT_X509 = 1,
-    GNUTLS_CRT_OPENPGP = 2
+    GNUTLS_CRT_OPENPGP = 2,
+    GNUTLS_CRT_RAW = 3
   } gnutls_certificate_type_t;
 
 /**
@@ -552,6 +554,7 @@ extern "C"
 /**
  * gnutls_certificate_print_formats_t:
  * @GNUTLS_CRT_PRINT_FULL: Full information about certificate.
+ * @GNUTLS_CRT_PRINT_COMPACT: Information about certificate name in one line, 
plus identification of the public key.
  * @GNUTLS_CRT_PRINT_ONELINE: Information about certificate in one line.
  * @GNUTLS_CRT_PRINT_UNSIGNED_FULL: All info for an unsigned certificate.
  *
@@ -561,7 +564,8 @@ extern "C"
   {
     GNUTLS_CRT_PRINT_FULL = 0,
     GNUTLS_CRT_PRINT_ONELINE = 1,
-    GNUTLS_CRT_PRINT_UNSIGNED_FULL = 2
+    GNUTLS_CRT_PRINT_UNSIGNED_FULL = 2,
+    GNUTLS_CRT_PRINT_COMPACT = 3
   } gnutls_certificate_print_formats_t;
 
 #define GNUTLS_PK_ECC GNUTLS_PK_EC
@@ -1652,6 +1656,22 @@ gnutls_ecc_curve_t gnutls_ecc_curve_get(gnutls_session_t 
session);
   int gnutls_hex2bin (const char *hex_data, size_t hex_size,
                       void *bin_data, size_t * bin_size);
 
+  /* ssh style functions */
+  int gnutls_verify_stored_pubkey(const char* file, 
+                            const char* application,
+                            const char* host,
+                            const char* service,
+                            gnutls_certificate_type_t cert_type,
+                            const gnutls_datum_t * cert, unsigned int flags);
+
+  int gnutls_store_pubkey(const char* file, 
+                    const char* application,
+                    const char* host,
+                    const char* service,
+                    gnutls_certificate_type_t cert_type,
+                    const gnutls_datum_t * cert, unsigned int flags);
+
+
   /* Gnutls error codes. The mapping to a TLS alert is also shown in
    * comments.
    */
diff --git a/lib/libgnutls.map b/lib/libgnutls.map
index e169a06..40765f8 100644
--- a/lib/libgnutls.map
+++ b/lib/libgnutls.map
@@ -771,6 +771,8 @@ GNUTLS_3_0_0 {
        gnutls_priority_protocol_list;
        gnutls_priority_compression_list;
        gnutls_priority_ecc_curve_list;
+       gnutls_verify_stored_pubkey;
+       gnutls_store_pubkey;
 } GNUTLS_2_12;
 
 GNUTLS_PRIVATE {
diff --git a/lib/openpgp/output.c b/lib/openpgp/output.c
index fdd176a..88f522d 100644
--- a/lib/openpgp/output.c
+++ b/lib/openpgp/output.c
@@ -506,6 +506,13 @@ gnutls_openpgp_crt_print (gnutls_openpgp_crt_t cert,
 
   if (format == GNUTLS_CRT_PRINT_ONELINE)
     print_oneline (&str, cert);
+  else if (format == GNUTLS_CRT_PRINT_COMPACT)
+    {
+      print_oneline (&str, cert);
+
+      _gnutls_buffer_append_data (&str, "\n", 1);
+      print_key_fingerprint (&str, cert);
+    }
   else
     {
       _gnutls_buffer_append_str (&str,
diff --git a/lib/system.c b/lib/system.c
index eddb105..476b313 100644
--- a/lib/system.c
+++ b/lib/system.c
@@ -25,14 +25,20 @@
 #include <gnutls_errors.h>
 
 #include <errno.h>
+#include <sys/stat.h>
+#include <sys/types.h>
 
 #ifdef _WIN32
-#include <windows.h>
+# include <windows.h>
 
 #else
-#ifdef HAVE_PTHREAD_LOCKS
-#include <pthread.h>
-#endif
+# ifdef HAVE_PTHREAD_LOCKS
+#  include <pthread.h>
+# endif
+
+# if defined(HAVE_GETPWUID_R)
+#  include <pwd.h>
+# endif
 #endif
 
 /* We need to disable gnulib's replacement wrappers to get native
@@ -278,3 +284,65 @@ mutex_init_func gnutls_mutex_init = 
gnutls_system_mutex_init;
 mutex_deinit_func gnutls_mutex_deinit = gnutls_system_mutex_deinit;
 mutex_lock_func gnutls_mutex_lock = gnutls_system_mutex_lock;
 mutex_unlock_func gnutls_mutex_unlock = gnutls_system_mutex_unlock;
+
+#define CONFIG_PATH ".gnutls"
+
+/* Returns a path to store user-specific configuration
+ * data.
+ */
+int _gnutls_find_config_path(char* path, size_t max_size)
+{
+char tmp_home_dir[1024];
+const char *home_dir = getenv ("HOME");
+
+#ifdef _WIN32
+  if (home_dir == NULL || home_dir[0] == '\0')
+    {
+      const char *home_drive = getenv ("HOMEDRIVE");
+      const char *home_path = getenv ("HOMEPATH");
+
+      if (home_drive != NULL && home_path != NULL)
+        {
+          snprintf(tmp_home_dir, sizeof(tmp_home_dir), "%s%s", home_drive, 
home_path);
+        }
+      else
+        {
+          tmp_home_dir[0] = 0;
+        }
+      
+      home_dir = tmp_home_dir;
+    }
+#elsif defined(HAVE_GETPWUID_R)
+  if (home_dir == NULL || home_dir[0] == '\0')
+    {
+      struct passwd *pwd;
+      struct passwd _pwd;
+      char buf[1024];
+
+      getpwuid_r(getuid(), &_pwd, buf, sizeof(buf), &pwd);
+      if (pwd != NULL)
+        {
+          snprintf(tmp_home_dir, sizeof(tmp_home_dir), "%s", pwd->pw_dir);
+        }
+      else
+        {
+          tmp_home_dir[0] = 0;
+        }
+
+      home_dir = tmp_home_dir;
+    }
+#else
+  if (home_dir == NULL || home_dir[0] == '\0')
+    {
+      tmp_home_dir[0] = 0;
+      home_dir = tmp_home_dir;
+    }
+#endif
+
+  if (home_dir == NULL || home_dir[0] == 0)
+    path[0] = 0;
+  else
+    snprintf(path, max_size, "%s/"CONFIG_PATH, home_dir);
+      
+  return 0;
+}
diff --git a/lib/system.h b/lib/system.h
index df4ee5a..0d2f5a5 100644
--- a/lib/system.h
+++ b/lib/system.h
@@ -71,4 +71,6 @@ struct timespec ts;
 #endif
 }
 
+int _gnutls_find_config_path(char* path, size_t max_size);
+
 #endif /* SYSTEM_H */
diff --git a/lib/verify-ssh.c b/lib/verify-ssh.c
new file mode 100644
index 0000000..8d6562f
--- /dev/null
+++ b/lib/verify-ssh.c
@@ -0,0 +1,489 @@
+/*
+ * Copyright (C) 2012 Free Software Foundation, Inc.
+ *
+ * Author: Nikos Mavrogiannopoulos
+ *
+ * This file is part of GnuTLS.
+ *
+ * The GnuTLS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#include <gnutls_int.h>
+#include <gnutls_errors.h>
+#include <libtasn1.h>
+#include <gnutls_global.h>
+#include <gnutls_num.h>         /* MAX */
+#include <gnutls_sig.h>
+#include <gnutls_str.h>
+#include <gnutls_datum.h>
+#include <hash.h>
+#include "x509_int.h"
+#include <common.h>
+#include <base64.h>
+#include <gnutls/abstract.h>
+#include <system.h>
+
+static int raw_pubkey_to_base64(gnutls_datum_t* pubkey);
+static int x509_crt_to_raw_pubkey(const gnutls_datum_t * cert, gnutls_datum_t 
*rpubkey);
+static int pgp_crt_to_raw_pubkey(const gnutls_datum_t * cert, gnutls_datum_t 
*rpubkey);
+static int find_stored_pubkey(const char* file, const char* application,
+                              const char* host, const char* service, 
+                              const gnutls_datum_t* skey);
+static int find_config_file(char* file, size_t max_size);
+#define MAX_FILENAME 512
+
+/**
+ * gnutls_verify_stored_pubkey:
+ * @file: A file specifying the stored keys (use NULL for the default)
+ * @application: non-NULL with an application name if this key is 
application-specific
+ * @host: The peer's name
+ * @service: non-NULL if this key is specific to a service (e.g. http)
+ * @cert_type: The type of the certificate
+ * @cert: The raw (der) data of the certificate
+ * @flags: should be 0.
+ *
+ * This function will try to verify the provided certificate using
+ * a list of stored public keys.  The @service field if non-NULL should
+ * be a port number.
+ *
+ * Returns: If no associated public key is found
+ * then %GNUTLS_E_NO_CERTIFICATE_FOUND will be returned. If a key
+ * is found but does not match %GNUTLS_E_CERTIFICATE_KEY_MISMATCH
+ * is returned. On success, %GNUTLS_E_SUCCESS (0) is returned, 
+ * or a negative error value on other errors.
+ *
+ * Since: 3.0.0
+ **/
+int
+gnutls_verify_stored_pubkey(const char* file, 
+                            const char* application,
+                            const char* host,
+                            const char* service,
+                            gnutls_certificate_type_t cert_type,
+                            const gnutls_datum_t * cert, unsigned int flags)
+{
+gnutls_datum_t pubkey = { NULL, 0 };
+int ret;
+char local_file[MAX_FILENAME];
+
+  if (cert_type != GNUTLS_CRT_X509 && cert_type != GNUTLS_CRT_OPENPGP)
+    return gnutls_assert_val(GNUTLS_E_UNSUPPORTED_CERTIFICATE_TYPE);
+
+  if (file == NULL)
+    {
+      ret = find_config_file(local_file, sizeof(local_file));
+      if (ret < 0)
+        return gnutls_assert_val(ret);
+      file = local_file;
+    }
+
+  if (cert_type == GNUTLS_CRT_X509)
+    ret = x509_crt_to_raw_pubkey(cert, &pubkey);
+  else
+    ret = pgp_crt_to_raw_pubkey(cert, &pubkey);
+
+  if (ret < 0)
+    {
+      gnutls_assert();
+      goto cleanup;
+    }
+  
+  ret = raw_pubkey_to_base64(&pubkey);
+  if (ret < 0)
+    {
+      gnutls_assert();
+      goto cleanup;
+    }
+
+  ret = find_stored_pubkey(file, application, host, service, &pubkey);
+  if (ret < 0)
+    return gnutls_assert_val(GNUTLS_E_NO_CERTIFICATE_FOUND);
+  
+
+cleanup:
+  gnutls_free(pubkey.data);
+  return ret;
+}
+
+static int parse_line(char* line, const char* application,
+                      size_t application_len,
+                      const char* host, size_t host_len,
+                      const char* service, size_t service_len,
+                      const gnutls_datum_t *skey)
+{
+char* p, *kp;
+char* savep = NULL;
+size_t kp_len;
+
+  /* read version */
+  p = strtok_r(line, "|", &savep);
+  if (p == NULL)
+    return gnutls_assert_val(GNUTLS_E_PARSING_ERROR);
+
+  if (strncmp(p, "g0", 2) != 0)
+    return gnutls_assert_val(GNUTLS_E_PARSING_ERROR);
+
+  /* read application */
+  p = strtok_r(NULL, "|", &savep);
+  if (p == NULL)
+    return gnutls_assert_val(GNUTLS_E_PARSING_ERROR);
+    
+  if (p[0] != '*' && strcmp(p, application)!=0)
+    return gnutls_assert_val(GNUTLS_E_PARSING_ERROR);
+
+  /* read host */
+  p = strtok_r(NULL, "|", &savep);
+  if (p == NULL)
+    return gnutls_assert_val(GNUTLS_E_PARSING_ERROR);
+    
+  if (p[0] != '*' && strcmp(p, host) != 0)
+    return gnutls_assert_val(GNUTLS_E_PARSING_ERROR);
+
+  /* read service */
+  p = strtok_r(NULL, "|", &savep);
+  if (p == NULL)
+    return gnutls_assert_val(GNUTLS_E_PARSING_ERROR);
+    
+  if (p[0] != '*' && strcmp(p, service) != 0)
+    return gnutls_assert_val(GNUTLS_E_PARSING_ERROR);
+
+  /* read service */
+  kp = strtok_r(NULL, "|", &savep);
+  if (kp == NULL)
+    return gnutls_assert_val(GNUTLS_E_PARSING_ERROR);
+  
+  p = strpbrk(kp, "\n \r\t|");
+  if (p != NULL) *p = 0;
+
+  kp_len = strlen(kp);
+  if (kp_len != skey->size)
+    return gnutls_assert_val(GNUTLS_E_CERTIFICATE_KEY_MISMATCH);
+    
+  if (memcmp(kp, skey->data, skey->size) != 0)
+    return gnutls_assert_val(GNUTLS_E_CERTIFICATE_KEY_MISMATCH);
+  
+  /* key found and matches */
+  return 0;
+}
+
+/* Returns the base64 key if found 
+ */
+static int find_stored_pubkey(const char* file, const char* application,
+                             const char* host, const char* service, 
+                             const gnutls_datum_t* skey)
+{
+FILE* fd;
+char* line = NULL;
+size_t line_size = 0;
+int ret, l2, mismatch = 0;
+size_t application_len = 0, host_len = 0, service_len = 0;
+
+  if (host != NULL) host_len = strlen(host);
+  if (service != NULL) service_len = strlen(service);
+  if (application != NULL) application_len = strlen(application);
+
+  fd = fopen(file, "rb");
+  if (fd == NULL)
+    return gnutls_assert_val(GNUTLS_E_FILE_ERROR);
+  
+  do 
+    {
+      l2 = getline(&line, &line_size, fd);
+      if (l2 > 0)
+        {
+          ret = parse_line(line, application, application_len,
+                          host, host_len, service, service_len, skey);
+          if (ret == 0) /* found */
+            {
+              goto cleanup;
+            }
+          else if (ret == GNUTLS_E_CERTIFICATE_KEY_MISMATCH)
+            mismatch = 1;
+        }
+    }
+  while(l2 >= 0);
+
+  if (mismatch)
+    ret = GNUTLS_E_CERTIFICATE_KEY_MISMATCH;
+  else
+    ret = GNUTLS_E_NO_CERTIFICATE_FOUND;
+  
+cleanup:
+  free(line);
+  fclose(fd);
+  
+  return ret;
+}
+
+static int raw_pubkey_to_base64(gnutls_datum_t* pubkey)
+{
+  int ret;
+  char* out;
+  
+  ret = base64_encode_alloc((void*)pubkey->data, pubkey->size, &out);
+  if (ret == 0)
+    return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+  
+  gnutls_free(pubkey->data);
+  pubkey->data = (void*)out;
+  pubkey->size = ret;
+  
+  return 0;
+}
+
+static int x509_crt_to_raw_pubkey(const gnutls_datum_t * cert, gnutls_datum_t 
*rpubkey)
+{
+gnutls_x509_crt_t crt = NULL;
+gnutls_pubkey_t pubkey = NULL;
+size_t size;
+int ret;
+
+  ret = gnutls_x509_crt_init(&crt);
+  if (ret < 0)
+    return gnutls_assert_val(ret);
+
+  ret = gnutls_pubkey_init(&pubkey);
+  if (ret < 0)
+    {
+      gnutls_assert();
+      goto cleanup;
+    }
+  
+  ret = gnutls_x509_crt_import(crt, cert, GNUTLS_X509_FMT_DER);
+  if (ret < 0)
+    {
+      gnutls_assert();
+      goto cleanup;
+    }
+
+  ret = gnutls_pubkey_import_x509 (pubkey, crt, 0);
+  if (ret < 0)
+    {
+      gnutls_assert();
+      goto cleanup;
+    }
+  
+  size = 0;
+  ret = gnutls_pubkey_export(pubkey, GNUTLS_X509_FMT_DER, NULL, &size);
+  if (ret < 0 && ret != GNUTLS_E_SHORT_MEMORY_BUFFER)
+    {
+      gnutls_assert();
+      goto cleanup;
+    }
+
+  rpubkey->data = gnutls_malloc(size);
+  if (rpubkey->data == NULL)
+  if (ret < 0 && ret != GNUTLS_E_SHORT_MEMORY_BUFFER)
+    {
+      ret = GNUTLS_E_MEMORY_ERROR;
+      gnutls_assert();
+      goto cleanup;
+    }
+  
+  ret = gnutls_pubkey_export(pubkey, GNUTLS_X509_FMT_DER, rpubkey->data, 
&size);
+  if (ret < 0)
+    {
+      gnutls_free(rpubkey->data);
+      gnutls_assert();
+      goto cleanup;
+    }
+
+  rpubkey->size = size;
+  ret = 0;
+
+cleanup:
+  gnutls_x509_crt_deinit(crt);
+  gnutls_pubkey_deinit(pubkey);
+
+  return ret;
+}
+
+static int pgp_crt_to_raw_pubkey(const gnutls_datum_t * cert, gnutls_datum_t 
*rpubkey)
+{
+gnutls_openpgp_crt_t crt = NULL;
+gnutls_pubkey_t pubkey = NULL;
+size_t size;
+int ret;
+
+  ret = gnutls_openpgp_crt_init(&crt);
+  if (ret < 0)
+    return gnutls_assert_val(ret);
+
+  ret = gnutls_pubkey_init(&pubkey);
+  if (ret < 0)
+    {
+      gnutls_assert();
+      goto cleanup;
+    }
+  
+  ret = gnutls_openpgp_crt_import(crt, cert, GNUTLS_OPENPGP_FMT_RAW);
+  if (ret < 0)
+    {
+      gnutls_assert();
+      goto cleanup;
+    }
+
+  ret = gnutls_pubkey_import_openpgp (pubkey, crt, 0);
+  if (ret < 0)
+    {
+      gnutls_assert();
+      goto cleanup;
+    }
+  
+  size = 0;
+  ret = gnutls_pubkey_export(pubkey, GNUTLS_X509_FMT_DER, NULL, &size);
+  if (ret < 0 && ret != GNUTLS_E_SHORT_MEMORY_BUFFER)
+    {
+      gnutls_assert();
+      goto cleanup;
+    }
+
+  rpubkey->data = gnutls_malloc(size);
+  if (rpubkey->data == NULL)
+  if (ret < 0 && ret != GNUTLS_E_SHORT_MEMORY_BUFFER)
+    {
+      ret = GNUTLS_E_MEMORY_ERROR;
+      gnutls_assert();
+      goto cleanup;
+    }
+  
+  ret = gnutls_pubkey_export(pubkey, GNUTLS_X509_FMT_DER, rpubkey->data, 
&size);
+  if (ret < 0)
+    {
+      gnutls_free(rpubkey->data);
+      gnutls_assert();
+      goto cleanup;
+    }
+
+  rpubkey->size = size;
+  ret = 0;
+
+cleanup:
+  gnutls_openpgp_crt_deinit(crt);
+  gnutls_pubkey_deinit(pubkey);
+
+  return ret;
+}
+
+/**
+ * gnutls_store_pubkey:
+ * @file: A file specifying the stored keys (use NULL for the default)
+ * @application: non-NULL with an application name if this key is 
application-specific
+ * @host: The peer's name
+ * @service: non-NULL if this key is specific to a service (e.g. http)
+ * @cert_type: The type of the certificate
+ * @cert: The data of the certificate
+ * @flags: should be 0.
+ *
+ * This function will store to verify the provided certificate to 
+ * the list of stored public keys. 
+ *
+ * Note that this function is not thread safe.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ *   negative error value.
+ *
+ * Since: 3.0.0
+ **/
+int
+gnutls_store_pubkey(const char* file, 
+                    const char* application,
+                    const char* host,
+                    const char* service,
+                    gnutls_certificate_type_t cert_type,
+                    const gnutls_datum_t * cert, unsigned int flags)
+{
+FILE* fd = NULL;
+gnutls_datum_t pubkey = { NULL, 0 };
+int ret;
+char local_file[MAX_FILENAME];
+
+  if (cert_type != GNUTLS_CRT_X509 && cert_type != GNUTLS_CRT_OPENPGP)
+    return gnutls_assert_val(GNUTLS_E_UNSUPPORTED_CERTIFICATE_TYPE);
+  
+  if (file == NULL)
+    {
+      ret = _gnutls_find_config_path(local_file, sizeof(local_file));
+      if (ret < 0)
+        return gnutls_assert_val(ret);
+      
+      _gnutls_debug_log("Configuration path: %s\n", local_file);
+      mkdir(local_file, 0700);
+      
+      ret = find_config_file(local_file, sizeof(local_file));
+      if (ret < 0)
+        return gnutls_assert_val(ret);
+      file = local_file;
+    }
+    
+  if (cert_type == GNUTLS_CRT_X509)
+    ret = x509_crt_to_raw_pubkey(cert, &pubkey);
+  else
+    ret = pgp_crt_to_raw_pubkey(cert, &pubkey);
+  if (ret < 0)
+    {
+      gnutls_assert();
+      goto cleanup;
+    }
+  
+  ret = raw_pubkey_to_base64(&pubkey);
+  if (ret < 0)
+    {
+      gnutls_assert();
+      goto cleanup;
+    }
+
+  _gnutls_debug_log("Configuration file: %s\n", file);
+
+  fd = fopen(file, "ab+");
+  if (fd == NULL)
+    {
+      ret = gnutls_assert_val(GNUTLS_E_FILE_ERROR);
+      goto cleanup;
+    }
+
+  if (application == NULL) application = "*";
+  if (service == NULL) service = "*";
+  if (host == NULL) host = "*";
+
+  fprintf(fd, "|g0|%s|%s|%s|%.*s\n", application, host, service, pubkey.size, 
pubkey.data);
+
+  ret = 0;
+
+cleanup:
+  gnutls_free(pubkey.data);
+  if (fd != NULL) fclose(fd);
+  
+  return ret;
+}
+
+#define CONFIG_FILE "known_hosts"
+
+static int find_config_file(char* file, size_t max_size)
+{
+char path[MAX_FILENAME];
+int ret;
+
+  ret = _gnutls_find_config_path(path, sizeof(path));
+  if (ret < 0)
+    return gnutls_assert_val(ret);
+
+  if (path[0] == 0)
+    snprintf(file, max_size, "%s", CONFIG_FILE);
+  else
+    snprintf(file, max_size, "%s/%s", path, CONFIG_FILE);
+      
+  return 0;
+}
diff --git a/lib/x509/output.c b/lib/x509/output.c
index 564004f..827845f 100644
--- a/lib/x509/output.c
+++ b/lib/x509/output.c
@@ -1700,6 +1700,22 @@ gnutls_x509_crt_print (gnutls_x509_crt_t cert,
       
       return ret;
     }
+  else if (format == GNUTLS_CRT_PRINT_COMPACT)
+    {
+      _gnutls_buffer_init (&str);
+
+      print_oneline (&str, cert);
+
+      _gnutls_buffer_append_data (&str, "\n", 1);
+      print_keyid (&str, cert);
+
+      _gnutls_buffer_append_data (&str, "\0", 1);
+
+      ret = _gnutls_buffer_to_datum( &str, out);
+      if (out->size > 0) out->size--;
+      
+      return ret;
+    }
   else if (format == GNUTLS_CRT_PRINT_ONELINE)
     {
       _gnutls_buffer_init (&str);
diff --git a/lib/x509_b64.c b/lib/x509_b64.c
index 53dc1cc..00379c1 100644
--- a/lib/x509_b64.c
+++ b/lib/x509_b64.c
@@ -28,6 +28,9 @@
 #include <gnutls_datum.h>
 #include <x509_b64.h>
 
+/* FIXME: rewrite this to use GNULIB's base64 implementation.
+ */
+
 static const uint8_t b64table[] =
   "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
 
diff --git a/src/cli-args.def.in b/src/cli-args.def.in
index a691c86..92c3bd6 100644
--- a/src/cli-args.def.in
+++ b/src/cli-args.def.in
@@ -37,6 +37,12 @@ flag = {
 };
 
 flag = {
+    name      = ssh;
+    descrip   = "Enable SSH-style authentication";
+    doc       = "This option will, in addition to certificate authentication, 
perform authentication based on stored public keys.";
+};
+
+flag = {
     name      = resume;
     value     = r;
     descrip   = "Connect, establish a session. Connect again and resume this 
session";
@@ -229,7 +235,7 @@ flag = {
     name      = port;
     value     = p;
     arg-type  = string;
-    descrip   = "The port to connect to";
+    descrip   = "The port or service to connect to";
     doc      = "";
 };
 
diff --git a/src/cli.c b/src/cli.c
index 27885c5..4226e77 100644
--- a/src/cli.c
+++ b/src/cli.c
@@ -70,6 +70,8 @@ int crlf;
 unsigned int verbose = 0;
 extern int print_cert;
 
+#define DEFAULT_CA_FILE "/etc/ssl/certs/ca-certificates.crt"
+
 const char *srp_passwd = NULL;
 const char *srp_username = NULL;
 const char *pgp_keyfile = NULL;
@@ -423,23 +425,93 @@ load_keys (void)
 
 }
 
+#define IS_NEWLINE(x) ((x[0] == '\n') || (x[0] == '\r'))
+static int
+read_yesno (const char *input_str)
+{
+  char input[128];
+
+  fputs (input_str, stderr);
+  if (fgets (input, sizeof (input), stdin) == NULL)
+    return 0;
+
+  if (IS_NEWLINE(input))
+    return 0;
+
+  if (input[0] == 'y' || input[0] == 'Y')
+    return 1;
+
+  return 0;
+}
+
 static int
 cert_verify_callback (gnutls_session_t session)
 {
   int rc;
-  unsigned int status;
+  unsigned int status = 0;
+  int ssh = HAVE_OPT(SSH);
 
   if (!x509_cafile && !pgp_keyring)
     return 0;
 
-  rc = gnutls_certificate_verify_peers2 (session, &status);
-  if (rc != 0 || status != 0)
+  rc = cert_verify(session, hostname);
+  if (rc == 0)
     {
       printf ("*** Verifying server certificate failed...\n");
-      if (!insecure)
+      if (!insecure && !ssh)
         return -1;
     }
 
+  if (ssh) /* try ssh auth */
+    {
+      unsigned int list_size;
+      const gnutls_datum_t * cert;
+      
+      cert = gnutls_certificate_get_peers(session, &list_size);
+      if (cert == NULL)
+        {
+          fprintf(stderr, "Cannot obtain peer's certificate!\n");
+          return -1;
+        }
+      
+      rc = gnutls_verify_stored_pubkey(NULL, NULL, hostname, service, 
GNUTLS_CRT_X509,
+                                       cert, 0);
+      if (rc == GNUTLS_E_NO_CERTIFICATE_FOUND)
+        {
+          print_cert_info(session, GNUTLS_CRT_PRINT_COMPACT);
+          fprintf(stderr, "Host %s has never been contacted before and is not 
in the trusted list.\n", hostname);
+          if (status == 0)
+            fprintf(stderr, "Its certificate is valid for %s.\n", hostname);
+
+          rc = read_yesno("Are you sure you want to trust it? (y/N): ");
+          if (rc == 0)
+            return -1;
+        }
+      else if (rc == GNUTLS_E_CERTIFICATE_KEY_MISMATCH)
+        {
+          print_cert_info(session, GNUTLS_CRT_PRINT_COMPACT);
+          fprintf(stderr, "Warning: host %s is known and it is associated with 
a different key.\n", hostname);
+          fprintf(stderr, "It might be that the server has multiple keys, or 
an attacker replaced the key to eavesdrop this connection .\n");
+          if (status == 0)
+            fprintf(stderr, "Its certificate is valid for %s.\n", hostname);
+
+          rc = read_yesno("Do you trust the received key? (y/N): ");
+          if (rc == 0)
+            return -1;
+        }
+      else if (rc < 0)
+        {
+          fprintf(stderr, "gnutls_verify_stored_pubkey: %s\n", 
gnutls_strerror(rc));
+          return -1;
+        }
+      
+      rc = gnutls_store_pubkey(NULL, NULL, hostname, service, GNUTLS_CRT_X509, 
cert, 0);
+      if (rc < 0)
+        {
+          fprintf(stderr, "Could not store key: %s\n", gnutls_strerror(rc));
+        }
+    }
+
   return 0;
 }
 
@@ -1018,6 +1090,7 @@ const char* rest = NULL;
   resume = HAVE_OPT(RESUME);
   rehandshake = HAVE_OPT(REHANDSHAKE);
   insecure = HAVE_OPT(INSECURE);
+
   udp = HAVE_OPT(UDP);
   mtu = OPT_VALUE_MTU;
   
@@ -1046,6 +1119,11 @@ const char* rest = NULL;
   
   if (HAVE_OPT(X509CAFILE))
     x509_cafile = OPT_ARG(X509CAFILE);
+  else
+    {
+      if (access(DEFAULT_CA_FILE, R_OK) == 0)
+        x509_cafile = DEFAULT_CA_FILE;
+    }
   
   if (HAVE_OPT(X509CRLFILE))
     x509_crlfile = OPT_ARG(X509CRLFILE);
@@ -1151,8 +1229,6 @@ do_handshake (socket_st * socket)
     {
       /* print some information */
       print_info (socket->session, socket->hostname, HAVE_OPT(INSECURE));
-
-
       socket->secure = 1;
     }
   else
diff --git a/src/common.c b/src/common.c
index 40aa5b0..38ea2cf 100644
--- a/src/common.c
+++ b/src/common.c
@@ -68,14 +68,53 @@ raw_to_string (const unsigned char *raw, size_t raw_size)
 }
 
 static void
-print_x509_info (gnutls_session_t session, const char *hostname,
-                 int insecure)
+print_x509_info_compact (gnutls_session_t session, int flag)
+{
+    gnutls_x509_crt_t crt;
+    const gnutls_datum_t *cert_list;
+    unsigned int cert_list_size = 0;
+    int ret;
+    gnutls_datum_t cinfo;
+
+    cert_list = gnutls_certificate_get_peers (session, &cert_list_size);
+    if (cert_list_size == 0)
+      {
+          fprintf (stderr, "No certificates found!\n");
+          return;
+      }
+
+    gnutls_x509_crt_init (&crt);
+    ret =
+          gnutls_x509_crt_import (crt, &cert_list[0],
+                                  GNUTLS_X509_FMT_DER);
+    if (ret < 0)
+      {
+        fprintf (stderr, "Decoding error: %s\n",
+                 gnutls_strerror (ret));
+        return;
+      }
+
+    ret =
+      gnutls_x509_crt_print (crt, flag, &cinfo);
+    if (ret == 0)
+      {
+        printf ("- X.509 cert: %s\n", cinfo.data);
+        gnutls_free (cinfo.data);
+      }
+
+    gnutls_x509_crt_deinit (crt);
+}
+
+static void
+print_x509_info (gnutls_session_t session, int flag)
 {
     gnutls_x509_crt_t crt;
     const gnutls_datum_t *cert_list;
     unsigned int cert_list_size = 0, j;
-    int hostname_ok = 0;
     int ret;
+    
+    if (flag == GNUTLS_CRT_PRINT_COMPACT)
+      return print_x509_info_compact(session, flag);
 
     cert_list = gnutls_certificate_get_peers (session, &cert_list_size);
     if (cert_list_size == 0)
@@ -84,6 +123,7 @@ print_x509_info (gnutls_session_t session, const char 
*hostname,
           return;
       }
 
+    printf (" - Certificate type: X.509\n");
     printf (" - Got a certificate list of %d certificates.\n",
             cert_list_size);
 
@@ -104,14 +144,8 @@ print_x509_info (gnutls_session_t session, const char 
*hostname,
 
           printf (" - Certificate[%d] info:\n  - ", j);
 
-          if (verbose)
-              ret =
-                  gnutls_x509_crt_print (crt, GNUTLS_CRT_PRINT_FULL,
-                                         &cinfo);
-          else
-              ret =
-                  gnutls_x509_crt_print (crt, GNUTLS_CRT_PRINT_ONELINE,
-                                         &cinfo);
+          ret =
+            gnutls_x509_crt_print (crt, flag, &cinfo);
           if (ret == 0)
             {
                 printf ("%s\n", cinfo.data);
@@ -153,46 +187,118 @@ print_x509_info (gnutls_session_t session, const char 
*hostname,
                 gnutls_free (p);
             }
 
-          if (j == 0 && hostname != NULL)
-            {
-                /* Check the hostname of the first certificate if it matches
-                 * the name of the host we connected to.
-                 */
-                if (gnutls_x509_crt_check_hostname (crt, hostname) == 0)
-                    hostname_ok = 1;
-                else
-                    hostname_ok = 2;
-            }
-
           gnutls_x509_crt_deinit (crt);
       }
+}
 
-    if (hostname_ok == 1)
-      {
-          printf
-              ("- The hostname in the certificate does NOT match '%s'\n",
-               hostname);
-          if (!insecure)
-              exit (1);
-      }
-    else if (hostname_ok == 2)
-      {
-          printf ("- The hostname in the certificate matches '%s'.\n",
-                  hostname);
-      }
+/* returns true or false, depending on whether the hostname
+ * matches to certificate */
+static int
+verify_x509_hostname (gnutls_session_t session, const char *hostname)
+{
+  gnutls_x509_crt_t crt;
+  const gnutls_datum_t *cert_list;
+  unsigned int cert_list_size = 0;
+  int ret;
+
+  cert_list = gnutls_certificate_get_peers (session, &cert_list_size);
+  if (cert_list_size == 0)
+    {
+      fprintf (stderr, "No certificates found!\n");
+      return 0;
+    }
+
+  gnutls_x509_crt_init (&crt);
+  ret =
+      gnutls_x509_crt_import (crt, &cert_list[0],
+                              GNUTLS_X509_FMT_DER);
+  if (ret < 0)
+    {
+      fprintf (stderr, "Decoding error: %s\n",
+               gnutls_strerror (ret));
+      return 0;
+    }
+
+  /* Check the hostname of the first certificate if it matches
+   * the name of the host we connected to.
+   */
+  if (gnutls_x509_crt_check_hostname (crt, hostname) == 0)
+    {
+      printf
+             ("- The hostname in the certificate does NOT match '%s'\n",
+              hostname);
+      ret = 0;
+    }
+  else
+    {
+      printf ("- The hostname in the certificate matches '%s'.\n",
+              hostname);
+      ret = 1;
+    }
+
+  gnutls_x509_crt_deinit (crt);
+
+  return ret;
 }
 
 #ifdef ENABLE_OPENPGP
+/* returns true or false, depending on whether the hostname
+ * matches to certificate */
+static int
+verify_openpgp_hostname (gnutls_session_t session, const char *hostname)
+{
+  gnutls_openpgp_crt_t crt;
+  const gnutls_datum_t *cert_list;
+  unsigned int cert_list_size = 0;
+  int ret;
+
+  cert_list = gnutls_certificate_get_peers (session, &cert_list_size);
+  if (cert_list_size == 0)
+    {
+      fprintf (stderr, "No certificates found!\n");
+      return 0;
+    }
+
+  gnutls_openpgp_crt_init (&crt);
+  ret =
+      gnutls_openpgp_crt_import (crt, &cert_list[0],
+                              GNUTLS_OPENPGP_FMT_RAW);
+  if (ret < 0)
+    {
+      fprintf (stderr, "Decoding error: %s\n",
+               gnutls_strerror (ret));
+      return 0;
+    }
+
+  /* Check the hostname of the first certificate if it matches
+   * the name of the host we connected to.
+   */
+  if (gnutls_openpgp_crt_check_hostname (crt, hostname) == 0)
+    {
+      printf
+             ("- The hostname in the certificate does NOT match '%s'\n",
+              hostname);
+      ret = 0;
+    }
+  else
+    {
+      printf ("- The hostname in the certificate matches '%s'.\n",
+              hostname);
+      ret = 1;
+    }
+
+  gnutls_openpgp_crt_deinit (crt);
+
+  return ret;
+}
 
 static void
-print_openpgp_info (gnutls_session_t session, const char *hostname,
-                    int insecure)
+print_openpgp_info_compact (gnutls_session_t session, int flag)
 {
 
     gnutls_openpgp_crt_t crt;
     const gnutls_datum_t *cert_list;
     unsigned int cert_list_size = 0;
-    int hostname_ok = 0;
     int ret;
 
     cert_list = gnutls_certificate_get_peers (session, &cert_list_size);
@@ -211,14 +317,50 @@ print_openpgp_info (gnutls_session_t session, const char 
*hostname,
                 return;
             }
 
-          if (verbose)
-              ret =
-                  gnutls_openpgp_crt_print (crt, GNUTLS_CRT_PRINT_FULL,
-                                            &cinfo);
-          else
-              ret =
-                  gnutls_openpgp_crt_print (crt, GNUTLS_CRT_PRINT_ONELINE,
-                                            &cinfo);
+          ret =
+              gnutls_openpgp_crt_print (crt, flag, &cinfo);
+          if (ret == 0)
+            {
+                printf ("- OpenPGP cert: %s\n", cinfo.data);
+                gnutls_free (cinfo.data);
+            }
+
+          gnutls_openpgp_crt_deinit (crt);
+      }
+}
+
+static void
+print_openpgp_info (gnutls_session_t session, int flag)
+{
+
+    gnutls_openpgp_crt_t crt;
+    const gnutls_datum_t *cert_list;
+    unsigned int cert_list_size = 0;
+    int ret;
+
+    if (flag == GNUTLS_CRT_PRINT_COMPACT)
+      print_openpgp_info_compact(session, flag);
+    
+    printf (" - Certificate type: OpenPGP\n");
+
+    cert_list = gnutls_certificate_get_peers (session, &cert_list_size);
+
+    if (cert_list_size > 0)
+      {
+          gnutls_datum_t cinfo;
+
+          gnutls_openpgp_crt_init (&crt);
+          ret = gnutls_openpgp_crt_import (crt, &cert_list[0],
+                                           GNUTLS_OPENPGP_FMT_RAW);
+          if (ret < 0)
+            {
+                fprintf (stderr, "Decoding error: %s\n",
+                         gnutls_strerror (ret));
+                return;
+            }
+
+          ret =
+              gnutls_openpgp_crt_print (crt, flag, &cinfo);
           if (ret == 0)
             {
                 printf (" - %s\n", cinfo.data);
@@ -261,59 +403,38 @@ print_openpgp_info (gnutls_session_t session, const char 
*hostname,
                 gnutls_free (p);
             }
 
-          if (hostname != NULL)
-            {
-                /* Check the hostname of the first certificate if it matches
-                 * the name of the host we connected to.
-                 */
-                if (gnutls_openpgp_crt_check_hostname (crt, hostname) == 0)
-                    hostname_ok = 1;
-                else
-                    hostname_ok = 2;
-            }
-
           gnutls_openpgp_crt_deinit (crt);
       }
-
-    if (hostname_ok == 1)
-      {
-          printf
-              ("- The hostname in the certificate does NOT match '%s'\n",
-               hostname);
-          if (!insecure)
-              exit (1);
-      }
-    else if (hostname_ok == 2)
-      {
-          printf ("- The hostname in the certificate matches '%s'.\n",
-                  hostname);
-      }
 }
 
 #endif
 
-static void
-print_cert_vrfy (gnutls_session_t session)
+/* returns false (0) if not verified, or true (1) otherwise */
+int
+cert_verify (gnutls_session_t session, const char* hostname)
 {
     int rc;
-    unsigned int status;
+    unsigned int status = 0;
+    int type;
 
     rc = gnutls_certificate_verify_peers2 (session, &status);
+    if (rc == GNUTLS_E_NO_CERTIFICATE_FOUND)
+      {
+          printf ("- Peer did not send any certificate.\n");
+          return 0;
+      }
+
     if (rc < 0)
       {
           printf ("- Could not verify certificate (err: %s)\n",
                   gnutls_strerror (rc));
-          return;
+          return 0;
       }
 
-    if (rc == GNUTLS_E_NO_CERTIFICATE_FOUND)
+    type = gnutls_certificate_type_get (session);
+    if (type == GNUTLS_CRT_X509)
       {
-          printf ("- Peer did not send any certificate.\n");
-          return;
-      }
 
-    if (gnutls_certificate_type_get (session) == GNUTLS_CRT_X509)
-      {
           if (status & GNUTLS_CERT_REVOKED)
               printf ("- Peer's certificate chain revoked\n");
           if (status & GNUTLS_CERT_SIGNER_NOT_FOUND)
@@ -333,8 +454,11 @@ print_cert_vrfy (gnutls_session_t session)
               printf ("- Peer's certificate is NOT trusted\n");
           else
               printf ("- Peer's certificate is trusted\n");
+
+          rc = verify_x509_hostname (session, hostname);
+          if (rc == 0) status |= GNUTLS_CERT_INVALID;
       }
-    else
+    else if (type == GNUTLS_CRT_OPENPGP)
       {
           if (status & GNUTLS_CERT_INVALID)
               printf ("- Peer's key is invalid\n");
@@ -342,7 +466,20 @@ print_cert_vrfy (gnutls_session_t session)
               printf ("- Peer's key is valid\n");
           if (status & GNUTLS_CERT_SIGNER_NOT_FOUND)
               printf ("- Could not find a signer of the peer's key\n");
+
+          rc = verify_openpgp_hostname (session, hostname);
+          if (rc == 0) status |= GNUTLS_CERT_INVALID;
+      }
+    else
+      {
+        fprintf(stderr, "Unknown certificate type\n");
+        status |= GNUTLS_CERT_INVALID;
       }
+
+    if (status)
+      return 0;
+
+    return 1;
 }
 
 static void
@@ -450,6 +587,7 @@ print_info (gnutls_session_t session, const char *hostname, 
int insecure)
     gnutls_credentials_type_t cred;
     gnutls_kx_algorithm_t kx;
     unsigned char session_id[33];
+    int ret;
     size_t session_id_size = sizeof (session_id);
 
     /* print session ID */
@@ -517,9 +655,14 @@ print_info (gnutls_session_t session, const char 
*hostname, int insecure)
                 }
           }
 
-          print_cert_info (session, hostname, insecure);
+          print_cert_info (session, 
verbose?GNUTLS_CRT_PRINT_FULL:GNUTLS_CRT_PRINT_COMPACT);
 
-          print_cert_vrfy (session);
+          ret = cert_verify (session, hostname);
+          if (insecure == 0 && ret == 0)
+            {
+              fprintf(stderr, "Exiting because verification failed (use 
--insecure to force connection)\n");
+              exit(1);
+            }
 
           if (kx == GNUTLS_KX_DHE_RSA || kx == GNUTLS_KX_DHE_DSS)
               print_dh_info (session, "Ephemeral ");
@@ -578,32 +721,25 @@ print_info (gnutls_session_t session, const char 
*hostname, int insecure)
 }
 
 void
-print_cert_info (gnutls_session_t session, const char *hostname,
-                 int insecure)
+print_cert_info (gnutls_session_t session, int flag)
 {
 
     if (gnutls_certificate_client_get_request_status (session) != 0)
         printf ("- Server has requested a certificate.\n");
 
-    printf ("- Certificate type: ");
     switch (gnutls_certificate_type_get (session))
       {
-      case GNUTLS_CRT_UNKNOWN:
-          printf ("Unknown\n");
-
-          if (!insecure)
-              exit (1);
-          break;
       case GNUTLS_CRT_X509:
-          printf ("X.509\n");
-          print_x509_info (session, hostname, insecure);
+          print_x509_info (session, flag);
           break;
 #ifdef ENABLE_OPENPGP
       case GNUTLS_CRT_OPENPGP:
-          printf ("OpenPGP\n");
-          print_openpgp_info (session, hostname, insecure);
+          print_openpgp_info (session, flag);
           break;
 #endif
+      default:
+          printf ("Unknown type\n");
+          break;
       }
 }
 
diff --git a/src/common.h b/src/common.h
index dd6d569..aeeb395 100644
--- a/src/common.h
+++ b/src/common.h
@@ -50,9 +50,9 @@
 extern const char str_unknown[];
 
 int print_info (gnutls_session_t state, const char *hostname, int insecure);
-void print_cert_info (gnutls_session_t state, const char *hostname,
-                      int insecure);
+void print_cert_info (gnutls_session_t, int flag);
 void print_list (const char* priorities, int verbose);
+int cert_verify (gnutls_session_t session, const char* hostname);
 
 const char *raw_to_string (const unsigned char *raw, size_t raw_size);
 void pkcs11_common (void);
diff --git a/src/ocsptool-args.def.in b/src/ocsptool-args.def.in
index 7f4fb3a..44fdac6 100644
--- a/src/ocsptool-args.def.in
+++ b/src/ocsptool-args.def.in
@@ -38,12 +38,12 @@ flag = {
 flag = {
     name      = ask;
     arg-type  = string;
-    arg-name  = "url";
+    arg-name  = "server name|url";
     arg-optional;
-    descrip   = "Ask server about the loaded certificate";
+    descrip   = "Ask an OCSP/HTTP server on a certificate validity";
     flags-must = load-cert;
     flags-must = load-issuer;
-    doc = "Connects to the specified HTTP URL and queries an OCSP request.";
+    doc = "Connects to the specified HTTP OCSP server and queries on the 
validity of the loaded certificate.";
 };
 
 flag = {
diff --git a/src/ocsptool.c b/src/ocsptool.c
index 9810f0d..0e48e44 100644
--- a/src/ocsptool.c
+++ b/src/ocsptool.c
@@ -365,6 +365,7 @@ _verify_response (gnutls_datum_t *data)
        error (EXIT_FAILURE, 0, "error parsing CAs: %s",
               gnutls_strerror (ret));
 
+#if 0
       if (HAVE_OPT(VERBOSE))
        {
          unsigned int i;
@@ -382,6 +383,7 @@ _verify_response (gnutls_datum_t *data)
              gnutls_free (out.data);
            }
        }
+#endif
 
       ret = gnutls_x509_trust_list_add_cas (list, x509_ca_list, x509_ncas, 0);
       if (ret < 0)
@@ -598,7 +600,7 @@ socket_st hd;
   
   _response_info (&resp_data);
 
-  if (HAVE_OPT(LOAD_SIGNER))
+  if (HAVE_OPT(LOAD_SIGNER) || HAVE_OPT(LOAD_TRUST))
     {
       fprintf(outfile, "\n");
       v = _verify_response(&resp_data);
diff --git a/src/tests.c b/src/tests.c
index ede92c3..cbed468 100644
--- a/src/tests.c
+++ b/src/tests.c
@@ -1056,7 +1056,7 @@ test_certificate (gnutls_session_t session)
     return ret;
 
   printf ("\n");
-  print_cert_info (session, hostname, 1);
+  print_cert_info (session, GNUTLS_CRT_PRINT_FULL);
 
   return TEST_SUCCEED;
 }


hooks/post-receive
-- 
GNU gnutls



reply via email to

[Prev in Thread] Current Thread [Next in Thread]