Automatic generation of priority strings
authorRick van Rein <rick@openfortress.nl>
Wed, 28 Jan 2015 20:47:17 +0000 (20:47 +0000)
committerRick van Rein <rick@openfortress.nl>
Wed, 28 Jan 2015 20:47:17 +0000 (20:47 +0000)
* Generates X.509, OpenPGP and SRP permissions based on their LID_TYPE_xxx
* No support for ANON-DH yet (this is always suppressed)
* Retained old fixed strings for testing purposes

TODO
src/Makefile
src/donai.c [new file with mode: 0644]
src/donai.h [new file with mode: 0644]
src/handler.c
src/localid.c [deleted file]
src/localid.h [deleted file]

diff --git a/TODO b/TODO
index c5bd8ac..0344d1c 100644 (file)
--- a/TODO
+++ b/TODO
@@ -6,8 +6,9 @@ There are many ways in which the current TLS Pool can be improved:
 + Support for all of X.509, OpenPGP and SRP schemes (undecided on PSK)
 + Extension of GnuTLS' PKCS #11 support to OpenPGP, PSK and SRP
 - TOFU callbacks and storage of (signed?) acceptance
-- Incorporate session resumption (on both ends) (store creds in database?)
+- Incorporate session resumption (on both ends) (store creds in memcache?)
 - Key derivation API with the PRF functionality of TLS 1.2 (RFC 5705)
+- RFC 5705: repeated seeding labels? overlap proto-fixed ones? session revival?
 + Error translation from GnuTLS and BerkeleyDB to errno (with detail report)
 + Transactions for an entire thread
 - Thread cleanup with pthread_setcanceltype(), pthread_cleanup_push()
@@ -16,9 +17,12 @@ There are many ways in which the current TLS Pool can be improved:
 - Regularly refresh DH parameters ; find out how to apply refcnt and/or locks
 - Introduce (at least a basic form of) certificate validation
 + Support X.509 certificate chains
-- Derive GnuTLS priority string automatically from credentials for localid
++ Derive GnuTLS priority string automatically from credentials for localid
 + Migrate from fprintf (stderr, ...) to syslog()
 - Migrate from file-based SRP to SRP #11 when GnuTLS offers it
 - Add support for TLS-KDH when GnuTLS offers it
 + Move database environment and names into configuration parameters
 + Move DH params file to a configuration parameter
+- Explain how to generate X.509 and GnuPG certificates with PKCS #11
+- Recognise callbacks with a "same" file handle as session access requests
+- Move PID file handling to daemon.c; make -k switch after new initialisation
index bc81fa1..77972d9 100644 (file)
@@ -18,10 +18,10 @@ tags: .PHONEY
 .c.o:
        gcc -ggdb3 -pthread -I ../include -I /usr/local/include -I /usr/include/p11-kit-1 -c -o $@ $<
 
-tlspool: daemon.o config.o manage.o service.o cache.o pinentry.o handler.o localid.o remote.o error.o
-       @# gcc -ggdb3 -pthread -o $@ daemon.o config.o manage.o service.o cache.o pinentry.o handler.o localid.o remote.o error.o -L /usr/local/lib -L /usr/lib -L /usr/lib/x86_64-linux-gnu -lgnutls -ldb -lmemcached -lldap -lp11-kit -ltasn1
-       gcc -ggdb3 -pthread -o $@ daemon.o config.o manage.o service.o cache.o pinentry.o handler.o localid.o remote.o error.o -L /usr/local/lib -L /usr/lib -L /usr/lib/x86_64-linux-gnu -ldb -lgnutls -lp11-kit -ltasn1
-       @#STATLINK# gcc -ggdb3 -pthread -o $@ daemon.o config.o manage.o service.o cache.o pinentry.o handler.o localid.o remote.o error.o -L /usr/local/lib -L /usr/lib -L /usr/lib/x86_64-linux-gnu /usr/lib/libdb.a /usr/local/lib/libgnutls.a /usr/local/lib/libnettle.a /usr/local/lib/libhogweed.a /usr/lib/libgmp.a /usr/local/lib/libp11-kit.so /usr/lib/libz.a /usr/lib/libtasn1.a
+tlspool: daemon.o config.o manage.o service.o cache.o pinentry.o handler.o donai.o remote.o error.o
+       @# gcc -ggdb3 -pthread -o $@ daemon.o config.o manage.o service.o cache.o pinentry.o handler.o donai.o remote.o error.o -L /usr/local/lib -L /usr/lib -L /usr/lib/x86_64-linux-gnu -lgnutls -ldb -lmemcached -lldap -lp11-kit -ltasn1
+       gcc -ggdb3 -pthread -o $@ daemon.o config.o manage.o service.o cache.o pinentry.o handler.o donai.o remote.o error.o -L /usr/local/lib -L /usr/lib -L /usr/lib/x86_64-linux-gnu -ldb -lgnutls -lp11-kit -ltasn1
+       @#STATLINK# gcc -ggdb3 -pthread -o $@ daemon.o config.o manage.o service.o cache.o pinentry.o handler.o donai.o remote.o error.o -L /usr/local/lib -L /usr/lib -L /usr/lib/x86_64-linux-gnu /usr/lib/libdb.a /usr/local/lib/libgnutls.a /usr/local/lib/libnettle.a /usr/local/lib/libhogweed.a /usr/lib/libgmp.a /usr/local/lib/libp11-kit.so /usr/lib/libz.a /usr/lib/libtasn1.a
 
 
 #
diff --git a/src/donai.c b/src/donai.c
new file mode 100644 (file)
index 0000000..489853a
--- /dev/null
@@ -0,0 +1,369 @@
+/* tlspool/localid.c -- Map the keys of local identities to credentials */
+
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <syslog.h>
+#include <errno.h>
+
+#include <gnutls/gnutls.h>
+#include <gnutls/abstract.h>
+
+#include <tlspool/internal.h>
+
+#include "manage.h"
+#include "donai.h"
+
+
+/*
+ * Lookup local identities from a BDB database.  The identities take the
+ * form of a NAI, and are the keys for a key-values lookup.  The outcome
+ * may offer multiple values, each representing an identity.  The general
+ * structure of a value is:
+ *
+ * - 4 netbytes, a flags field for local identity management (see LID_xxx below)
+ * - NUL-terminated string with a pkcs11 URI [ draft-pechanec-pkcs11uri ]
+ * - Binary string holding the identity in binary form
+ *
+ * There may be prefixes for generic management, but these are not made
+ * available to this layer.
+ */
+
+
+
+/* Retrieve flags from the credentials structure found in dbh_localid.
+ * The function returns non-zero on success (zero indicates syntax error).
+ */
+int dbcred_flags (DBT *creddata, uint32_t *flags) {
+       int p11privlen;
+       if (creddata->size <= 4) {
+               return 0;
+       }
+       *flags = ntohl (* (uint32_t *) creddata->data);
+       return 1;
+}
+
+
+/* Interpret the credentials structure found in dbh_localid.
+ * This comes down to splitting the (data,size) structure into fields:
+ *  - a 32-bit flags field
+ *  - a char * sharing the PKCS #11 private key location, NULL on LID_NO_PKCS11
+ *  - a (data,size) structure for the public credential, also when LID_CHAINED
+ * The function returns non-zero on success (zero indicates syntax error).
+ */
+int dbcred_interpret (gnutls_datum_t *creddata, uint32_t *flags, char **p11priv, uint8_t **pubdata, int *pubdatalen) {
+       int p11privlen;
+       if (creddata->size <= 4) {
+               return 0;
+       }
+       *flags = ntohl (* (uint32_t *) creddata->data);
+       if ((*flags) & LID_NO_PKCS11) {
+               *p11priv = NULL;
+       } else {
+               *p11priv = ((char *) creddata->data) + 4;
+               p11privlen = strnlen (*p11priv, creddata->size - 4);
+               if (p11privlen == creddata->size - 4) {
+                       return 0;
+               }
+#ifdef TODO_PKCS11_ADDED
+               if (strncmp (*p11priv, "pkcs11:", 7) != 0) {
+                       return 0;
+               }
+#endif
+       }
+       *pubdata    = ((uint8_t *) creddata->data) + 4 + p11privlen + 1;
+       *pubdatalen =              creddata->size  - 4 - p11privlen - 1;
+       if (*pubdatalen < 20) {
+               // Unbelievably short certificate (arbitrary sanity limit 20)
+               return 0;
+       }
+       return 1;
+}
+
+
+/* Create an iterator for a given localid value.  Use keys from dhb_lid.
+ * The first value is delivered; continue with dbcred_iterate_next().
+ *
+ * The cursor must have been opened on dbh_localid within the desired
+ * transaction context; the caller must close it after iteration.
+ *
+ * The value returned is only non-zero if a value was setup.
+ * The DB_NOTFOUND value indicates that the key was not found.
+ */
+gtls_error dbcred_iterate_from_localid (DBC *cursor, DBT *keydata, DBT *creddata) {
+       int gtls_errno = GNUTLS_E_SUCCESS;
+       E_d2ge ("Key not found in db_localid",
+               cursor->get (cursor, keydata, creddata, DB_SET));
+       return gtls_errno;
+}
+
+
+/* Construct an iterator for a given remoteid selector.  Apply stepwise
+ * generalisation to find the most concrete match.  The first value found
+ * is delivered; continue with dbcred_iterate_next().
+ *
+ * The remotesel value in string representation is the key to discpatn,
+ * forming the initial disclosure pattern.  This key should be setup with
+ * enough space to store the pattern (which is never longer than the original
+ * remoteid) plus a terminating NUL character.
+ *
+ * Note that remotesel already has the first value activated, usually the
+ * same as the remoteid.  This is assumed to be available, so don't call
+ * this function otherwise.  In practice, this is hardly a problem; any
+ * valid remoteid will provide a valid selector whose first iteration is to
+ * repeat the remoteid.  Failure to start even this is a sign of a syntax
+ * error, which is good to be treating separately from not-found conditions.
+ *
+ * The started iteration is a nested iteration over dbh_disclose for the
+ * pattern found, and inside that an iteration over dbh_localid for the
+ * localid values that this gave.  This means that two cursors are needed,
+ * both here and in the subsequent dbcred_iterate_next() calls.
+ *
+ * The cursors crs_disclose and crs_localid must have been opened on
+ * dbh_disclose and dbh_localid within the desired transaction context;
+ * the caller must close them after iteration.
+ *
+ * The value returned is zero if a value was setup; otherwise an error code.
+ * The DB_NOTFOUND value indicates that no selector matching the remoteid
+ * was found in dbh_disclose.
+ */
+gtls_error dbcred_iterate_from_remoteid_selector (DBC *crs_disclose, DBC *crs_localid, selector_t *remotesel, DBT *discpatn, DBT *keydata, DBT *creddata) {
+       int gtls_errno = GNUTLS_E_SUCCESS;
+       int more = 1;
+       while (more) {
+               int fnd;
+               discpatn->size = donai_iterate_memput (discpatn->data, remotesel);
+               tlog (TLOG_DB, LOG_DEBUG, "Looking up remote selector %.*s", discpatn->size, (char *) discpatn->data);
+               fnd = crs_disclose->get (crs_disclose, discpatn, keydata, DB_SET);
+               if (fnd == 0) {
+                       // Got the selector pattern!
+                       // Now continue, even when no localids will work.
+                       E_d2ge ("Key not found in db_localid",
+                               crs_localid->get (
+                                       crs_localid,
+                                       keydata,
+                                       creddata,
+                                       DB_SET));
+                       return gtls_errno;
+               } else if (fnd != DB_NOTFOUND) {
+                       E_d2ge ("Failed while searching with remote ID selector", fnd);
+                       break;
+               }
+               more = selector_iterate_next (remotesel);
+       }
+       // Ended here with nothing more to find
+       E_d2ge ("No selector matches remote ID in db_disclose",
+               DB_NOTFOUND);
+       return gtls_errno;
+}
+
+
+/* Move an iterator to the next credential data value.  When done, the value
+ * returned should be DB_NOTFOUND.
+ *
+ * The outer cursor (for dbh_disclose) is optional, and is only used when
+ * the prior call was from dbcred_iterate_from_remoteid().
+ *
+ * The optional discpatn must be supplied only when dbh_disclose is provided.
+ * It holds the key value for the dbh_disclose outer cursor.
+ *
+ * The keydata will be filled with the intermediate key when dbh_disclose is
+ * provided.  It is also used to match the next record with the current one.
+ *
+ * The value returned is zero if a value was setup; otherwise an error code.
+ * The DB_NOTFOUND value indicates that no further duplicate was not found.
+ */
+db_error dbcred_iterate_next (DBC *opt_crs_disclose, DBC *crs_localid, DBT *opt_discpatn, DBT *keydata, DBT *creddata) {
+       int db_errno = 0;
+       db_errno = crs_localid->get (crs_localid, keydata, creddata, DB_NEXT_DUP);
+       if (db_errno != DB_NOTFOUND) {
+               return db_errno;
+       }
+       // Inner loop ended in DB_NOTFOUND, optionally continue in outer loop
+       if ((opt_crs_disclose != NULL) && (opt_discpatn != NULL)) {
+               while (db_errno == DB_NOTFOUND) {
+                       db_errno = opt_crs_disclose->get (opt_crs_disclose, opt_discpatn, keydata, DB_NEXT_DUP);
+                       if (db_errno == DB_NOTFOUND) {
+                               return db_errno;
+                       }
+                       db_errno = crs_localid->get (crs_localid, keydata, creddata, DB_SET);
+               }
+       }
+       return db_errno;
+}
+
+
+/* Iterate over selector values that would generalise the donai.  The
+ * selector_t shares data from the donai, so it allocates no internal
+ * storage and so it can be dropped at any time during the iteration.
+ * Meanwhile, the donai must not drop storage before iteration stops.
+ *
+ * The value returned is only non-zero if a value was setup.
+ */
+int selector_iterate_init (selector_t *iterator, donai_t *donai) {
+       //
+       // If the user name is not NULL but empty, bail out in horror
+       if ((donai->user != NULL) && (donai->userlen <= 0)) {
+               return 0;
+       }
+       //
+       // If the domain name is empty or NULL, bail out in horror
+       if ((donai->domain == NULL) || (donai->domlen == 0)) {
+               return 0;
+       }
+       //
+       // The first and most concrete pattern is the donai itself
+       memcpy (iterator, donai, sizeof (*iterator));
+       return 1;
+}
+
+int selector_iterate_next (selector_t *iterator) {
+       int skip;
+       //
+       // If the user name is not NULL but empty, bail out in horror
+       if ((iterator->user != NULL) && (iterator->userlen == 0)) {
+               return 0;
+       }
+       //
+       // If the domain name is empty or NULL, bail out in horror
+       if ((iterator->domain == NULL) || (iterator->domlen == 0)) {
+               return 0;
+       }
+       //
+       // If there is a user component and it is non-empty, make it empty
+       // If it was empty, permit it to become non-empty again, and continue
+       if (iterator->user) {
+               if (iterator->userlen > 0) {
+                       iterator->userlen = -iterator->userlen;
+                       return 1;
+               }
+               iterator->userlen = -iterator->userlen;
+       }
+       //
+       // If the domain is a single dot, we're done
+       if ((iterator->domlen == 1) && (*iterator->domain == '.')) {
+               return 0;
+       }
+       //
+       // Replace the domain (known >= 1 chars) with the next dot's domain
+       skip = 1;
+       while ((skip < iterator->domlen) && (iterator->domain [skip] != '.')) {
+               skip++;
+       }
+       if (skip == iterator->domlen) {
+               iterator->domain = ".";         // Last resort domain
+               iterator->domlen = 1;
+       } else {
+               iterator->domain += skip;
+               iterator->domlen -= skip;
+       }
+       return 1;
+}
+
+
+/* Check if a selector is a pattern that matches the given donai value.
+ * The value returned is non-zero for a match, zero for a non-match.
+ */
+int donai_matches_selector (donai_t *donai, selector_t *pattern) {
+       int extra;
+       //
+       // Bail out in horror on misconfigurations
+       if ((donai->user != NULL) && (donai->userlen <= 0)) {
+               return 0;
+       }
+       if ((donai  ->domain == NULL) || (donai  ->domlen <= 0)) {
+               return 0;
+       }
+       if ((pattern->domain == NULL) || (pattern->domlen <= 0)) {
+               return 0;
+       }
+       //
+       // User name handling first
+       if (pattern->user) {
+               //
+               // Pattern has a user?  Then request a user in the donai too
+               if (donai->user == NULL) {
+                       return 0;
+               }
+               //
+               // Non-empty user in pattern?  Then match everything
+               if (*pattern->user) {
+                       if (pattern->userlen > 0) {
+                               if (donai->userlen != pattern->userlen) {
+                                       return 0;
+                               }
+                               if (memcmp (donai->user, pattern->user, donai->userlen) != 0) {
+                                       return 0;
+                               }
+                       }
+               }
+       } else {
+               //
+               // Pattern without user, then donai may not have one either
+               if (donai->user != NULL) {
+                       return 0;
+               }
+       }
+       //
+       // Domain name handling second
+       if (*pattern->domain == '.') {
+               extra = donai->domlen - pattern->domlen;
+               if (extra < 0) {
+                       //
+                       // No good having a longer pattern than a donai.domain
+                       return 0;
+               }
+       } else {
+               extra = 0;
+       }
+       return (memcmp (donai->domain + extra, pattern->domain, pattern->domlen) == 0);
+}
+
+
+/* Fill a donai structure from a stable string. The donai will share parts
+ * of the string.  The function can also be used to construct a selector
+ * from a string; their structures are the same and the syntax is not
+ * parsed to ensure non-empty usernames and non-dot-prefixed domain names.
+ */
+donai_t donai_from_stable_string (char *stable, int stablelen) {
+       donai_t retval;
+       retval.userlen = stablelen - 1;
+       while (retval.userlen > 0) {
+               if (stable [retval.userlen] == '@') {
+                       break;
+               }
+               retval.userlen--;
+       }
+       if (stable [retval.userlen] == '@') {
+               retval.user = stable;
+               retval.domain = stable + (retval.userlen + 1);
+               retval.domlen = stablelen - 1 - retval.userlen;
+       } else {
+               retval.user = NULL;
+               retval.domain = stable;
+               retval.domlen = stablelen;
+       }
+       return retval;
+}
+
+/* Print a donai or iterated selector to the given text buffer.  The
+ * text will be precisely the same as the originally parsed text.  An
+ * iterator may deliver values that are shorter, not longer.  The value
+ * returned is the number of bytes written.  No trailing NUL character
+ * will be written.
+ */
+int donai_iterate_memput (char *selector_text, donai_t *iterator) {
+       int len = 0;
+       if (iterator->user != NULL) {
+               if (iterator->userlen > 0) {
+                       memcpy (selector_text, iterator->user, iterator->userlen);
+                       len += iterator->userlen;
+               }
+               selector_text [len++] = '@';
+       }
+       memcpy (selector_text + len, iterator->domain, iterator->domlen);
+       len += iterator->domlen;
+       return len;
+}
+
diff --git a/src/donai.h b/src/donai.h
new file mode 100644 (file)
index 0000000..190b7ae
--- /dev/null
@@ -0,0 +1,223 @@
+/* tlspool/localid.h -- Map the keys of local identities to credentials */
+
+
+/*
+ * Lookup local identities from a BDB database.  The identities take the
+ * form of a NAI, and are the keys for a key-values lookup.  The outcome
+ * may offer multiple values, each representing an identity.  The general
+ * structure of a value is:
+ *
+ * - 4 netbytes, a flags field for local identity management (see LID_xxx below)
+ * - NUL-terminated string with a pkcs11 URI [ draft-pechanec-pkcs11uri ]
+ * - Binary string holding the identity in binary form
+ *
+ * There may be prefixes for generic management, but these are not made
+ * available to this layer.
+ */
+
+
+#define LID_HDRSZ      (MGT_HDRSZ + 4)
+
+
+#define LID_TYPE_MASK  0x000000ff      /* Separate out the LID_TYPE_xxx bits */
+#define LID_TYPE_ANY   0x000000ff      /* No filter, permit anything */
+#define LID_TYPE_X509  0x00000001      /* X.509 certificate, DER-encoded */
+#define LID_TYPE_PGP   0x00000002      /* OpenPGP public key, binary form */
+#define LID_TYPE_SRP   0x00000003      /* No data, flags existence */
+#define LID_TYPE_KRB5  0x00000004      /* Ticket */
+
+#define LID_TYPE_MIN   LID_TYPE_X509
+#define LID_TYPE_MAX   LID_TYPE_KRB5
+#define LID_TYPE_OFS   LID_TYPE_MIN
+#define LID_TYPE_CNT   (1 + LID_TYPE_MAX - LID_TYPE_MIN)
+
+#define LID_ROLE_MASK  0x00000300      /* Separate out the LID_ROLE_xxx bits */
+#define LID_ROLE_CLIENT        0x00000100      /* This may be used for clients */
+#define LID_ROLE_SERVER        0x00000200      /* This may be used for servers */
+#define LID_ROLE_BOTH  0x00000300      /* This may be used for both roles */
+#define LID_ROLE_NONE  0x00000000      /* This may be used for neither role */
+
+#define LID_NO_PKCS11  0x00001000      /* No prefixed PKCS #11 URI + NUL */
+#define LID_CHAINED    0x00002000      /* Credential isa type-specific chain */
+//TODO// Encode LID_NEEDS_CHAIN support
+#define LID_NEEDS_CHAIN        0x00004000      /* Chain certs are in central storage */
+
+
+/* Impose a practial upper bound to the lenght of a DoNAI, a domain-or-NAI.
+ * This is important to avoid overzealous allocations and subsequent buffer
+ * or stack overflows.  Sigh, we live in a world where networks can carry
+ * the size between memory segments in a brief time.
+ */
+#define DONAI_MAXLEN 512
+
+
+/* A donai is a structure holding either user@domain.name or domain.name.
+ * A selector is a simple pattern that can match with a donain, by stripping
+ * local components.  For instance user@domain.name or @domain.name or @.name
+ * or @. to match with user@domain.name; and, for instance domain.name or
+ * .name or . to match with domain.name.
+ */
+
+struct userdomain {
+       char *user;     /* not NUL-terminated; user==NULL for no @ at all */
+       int userlen;    /* valid if user!=NULL; userlen<0 in selector_t for 0 */
+       char *domain;   /* not NULL, start . signifies a pattern */
+       int domlen;     /* always >0 */
+};
+
+
+typedef struct userdomain donai_t;  /* (user==NULL OR userlen>0) AND *domain!='.' */
+
+typedef struct userdomain selector_t;  /* userlen<0 should be read as userlen==0 */
+
+
+
+/* Setup a DBT data handle to point to a pre-allocated, fixed-size
+ * data buffer that will be used throughout the use of the handle.
+ * Cleanup is not necessary, but the buffer must not be cleared
+ * before the last use of the data handle.
+ */
+static inline void dbt_init_fixbuf (DBT *dbt, void *buffer, u_int32_t bufsize) {
+       bzero (dbt, sizeof (DBT));
+       dbt->data = buffer;
+       dbt->size =
+       dbt->ulen = bufsize;
+       dbt->flags |= DB_DBT_USERMEM;
+}
+
+/* Setup a DBT data handle for malloc() by the database, and free() by the
+ * calling program.
+ * Cleanup with dbt_free() or dbt_store() is needed after every lookup
+ * that succeeded.
+ */
+static inline void dbt_init_malloc (DBT *dbt) {
+       bzero (dbt, sizeof (DBT));
+       dbt->flags |= DB_DBT_MALLOC;
+}
+
+/* Free the DBT data handle that was setup with dbt_init_malloc().  This
+ * or dbt_store() must be called after every successfully returned data
+ * item.
+ */
+static inline void dbt_free (DBT *dbt) {
+       /* assert (dbt->flags & DB_DBT_MALLOC); */
+       free (dbt->data);
+       dbt->data = NULL;
+}
+
+/* Store the DBT data handle's data into external structures, moving both
+ * the data pointer and size.  The data handle must have been setup with
+ * dbt_init_malloc().  Afterwards, clear the data handle for use in
+ * another iteration.
+ */
+static inline void dbt_store (DBT *dbt, gnutls_datum_t *output) {
+       /* assert (dbt->flags & DB_DBT_MALLOC); */
+       output->data = dbt->data;
+       output->size = dbt->size;
+       dbt->data = NULL;
+}
+
+
+/* Create an iterator for a given localid value.  Use keys from dhb_lid.
+ * The first value is delivered; continue with dbcred_iterate_next().
+ *
+ * The cursor must have been opened on dbh_localid within the desired
+ * transaction context; the caller must close it after iteration.
+ *
+ * The value returned is only non-zero if a value was setup.
+ * The DB_NOTFOUND value indicates that the key was not found.
+ */
+int dbcred_iterate_from_localid (DBC *cursor, DBT *keydata, DBT *creddata);
+
+/* Construct an iterator for a given remoteid selector.  Apply stepwise
+ * generalisation to find the most concrete match.  The first value found
+ * is delivered; continue with dbcred_iterate_next().
+ *
+ * The remotesel value in string representation is the key to discpatn,
+ * forming the initial disclosure pattern.  This key should be setup with
+ * enough space to store the pattern (which is never longer than the original
+ * remoteid) plus a terminating NUL character.
+ *
+ * Note that remotesel already has the first value activated, usually the
+ * same as the remoteid.  This is assumed to be available, so don't call
+ * this function otherwise.  In practice, this is hardly a problem; any
+ * valid remoteid will provide a valid selector whose first iteration is to
+ * repeat the remoteid.  Failure to start even this is a sign of a syntax
+ * error, which is good to be treating separately from not-found conditions.
+ *
+ * The started iteration is a nested iteration over dbh_disclose for the
+ * pattern found, and inside that an iteration over dbh_localid for the
+ * localid values that this gave.  This means that two cursors are needed,
+ * both here and in the subsequent dbcred_iterate_next() calls.
+ *
+ * The cursors crs_disclose and crs_localid must have been opened on
+ * dbh_disclose and dbh_localid within the desired transaction context;
+ * the caller must close them after iteration.
+ *
+ * The value returned is zero if a value was setup; otherwise an error code.
+ * The DB_NOTFOUND value indicates that no selector matching the remoteid
+ * was found in dbh_disclose.
+ */
+int dbcred_iterate_from_remoteid_selector (DBC *crs_disclose, DBC *crs_localid, selector_t *remotesel, DBT *discpatn, DBT *keydata, DBT *creddata);
+
+/* Move an iterator to the next credential data value.  When done, the value
+ * returned should be DB_NOTFOUND.
+ *
+ * The outer cursor (for dbh_disclose) is optional, and is only used when
+ * the prior call was from dbcred_iterate_from_remoteid().
+ *
+ * The optional discpatn must be supplied only when dbh_disclose is provided.
+ * It holds the key value for the dbh_disclose outer cursor.
+ *
+ * The keydata will be filled with the intermediate key when dbh_disclose is
+ * provided.  It is also used to match the next record with the current one.
+ *
+ * The value returned is zero if a value was setup; otherwise an error code.
+ * The DB_NOTFOUND value indicates that no further duplicate was not found.
+ */
+int dbcred_iterate_next (DBC *opt_crs_disclose, DBC *crs_localid, DBT *opt_discpatn, DBT *keydata, DBT *creddata);
+
+
+
+/* Interpret the credentials structure found in dbh_localid.
+ * This comes down to splitting the (data,size) structure into fields:
+ *  - a 32-bit flags field
+ *  - a char * sharing the PKCS #11 private key location
+ *  - a (data,size) structure for the public credential
+ * The function returns non-zero on success (zero indicates syntax error).
+ */
+int dbcred_interpret (gnutls_datum_t *creddata, uint32_t *flags, char **p11priv, uint8_t **pubdata, int *pubdatalen);
+
+
+/* Iterate over selector values that would generalise the donai.  The
+ * selector_t shares data from the donai, so it allocates no internal
+ * storage and so it can be dropped at any time during the iteration.
+ * Meanwhile, the donai must not drop storage before iteration stops.
+ *
+ * The value returned is only non-zero if a value was setup.
+ */
+int selector_iterate_init (selector_t *iterator, donai_t *donai);
+int selector_iterate_next (selector_t *iterator);
+
+/* Print a donai or iterated selector to the given text buffer.  The
+ * text will be precisely the same as the originally parsed text.  An
+ * iterator may deliver values that are shorter, not longer.  The value
+ * returned is the number of bytes written.  No trailing NUL character
+ * will be written.
+ */
+int donai_iterate_memput (char *selector_text, donai_t *iterator);
+
+
+/* Check if a selector is a pattern that matches the given donai value.
+ * The value returned is non-zero for a match, zero for a non-match.
+ */
+int donai_matches_selector (donai_t *donai, selector_t *pattern);
+
+
+/* Fill a donai structure from a stable string. The donai will share parts
+ * of the string.  The function can also be used to construct a selector
+ * from a string; their structures are the same and the syntax is not
+ * parsed to ensure non-empty usernames and non-dot-prefixed domain names.
+ */
+donai_t donai_from_stable_string (char *stable, int stablelen);
+
index 1532592..1a2a676 100644 (file)
@@ -24,7 +24,7 @@
 
 
 #include "manage.h"
-#include "localid.h"
+#include "donai.h"
 
 
 #if EXPECTED_LID_TYPE_COUNT != LID_TYPE_CNT
@@ -1161,6 +1161,14 @@ void cleanup_handler_credentials (void) {
 
 
 /*
+ * Check if a given cmd has the given LID_TYPE setup.
+ * Return 1 for yes or 0 for no; this is used in priority strings.
+ */
+static inline int lidtpsup (struct command *cmd, int lidtp) {
+       return cmd->lids [lidtp - LID_TYPE_MIN].data != NULL;
+}
+
+/*
  * The starttls_thread is a main program for the setup of a TLS connection,
  * either in client mode or server mode.  Note that the distinction between
  * client and server mode is only a TLS concern, but not of interest to the
@@ -1320,20 +1328,40 @@ static void *starttls_thread (void *cmd_void) {
                fetch_local_credentials (cmd));
 
        //
-       // Setup the priority string for this session
-       // TODO: Derive the sting from available local identities
+       // Setup the priority string for this session; this avoids future
+       // credential callbacks that ask for something impossible or
+       // undesired.
+       //
        // Variation factors:
        //  - starting configuration (can it be empty?)
        //  - Configured security parameters (database? variable?)
        //  - CTYPEs, SRP, ANON-or-not --> fill in as + or - characters
+       //TODO// Support for ANON-DH where appropriate
        if (gtls_errno == GNUTLS_E_SUCCESS) {
+               char priostr [256];
+               snprintf (priostr, sizeof (priostr)-1,
+                       "NORMAL:"
+                       "%cCTYPE-X.509:"
+                       "%cCTYPE-OPENPGP:"
+                       "%cSRP:%cSRP-RSA:%cSRP-DSS:"
+                       "%cANON-ECDH:%cANON-DH",
+                       lidtpsup (cmd, LID_TYPE_X509)           ?'+':'-',
+                       lidtpsup (cmd, LID_TYPE_PGP)            ?'+':'-',
+                       lidtpsup (cmd, LID_TYPE_SRP)            ?'+':'-',
+                       lidtpsup (cmd, LID_TYPE_SRP)            ?'+':'-',
+                       lidtpsup (cmd, LID_TYPE_SRP)            ?'+':'-',
+                       0 /* TODO: ANON-DH */                   ?'+':'-',
+                       0 /* TODO: ANON-DH */                   ?'+':'-');
+               tlog (TLOG_TLS, LOG_DEBUG, "Constructed priority string %s for local ID %s",
+                       priostr, cmd->cmd.pio_data.pioc_starttls.localid);
                E_g2e ("Failed to set GnuTLS priority string",
                        gnutls_priority_set_direct (
                        session,
                        // "NORMAL:-KX-ALL:+SRP:+SRP-RSA:+SRP-DSS",
-                       "NORMAL:+CTYPE-X.509:-CTYPE-OPENPGP:+CTYPE-X.509",
+                       // "NORMAL:+CTYPE-X.509:-CTYPE-OPENPGP:+CTYPE-X.509",
                        // "NORMAL:-CTYPE-X.509:+CTYPE-OPENPGP:-CTYPE-X.509",
                        // "NORMAL:+ANON-ECDH:+ANON-DH",
+                       priostr,
                        NULL));
        }
 
diff --git a/src/localid.c b/src/localid.c
deleted file mode 100644 (file)
index c181d1d..0000000
+++ /dev/null
@@ -1,369 +0,0 @@
-/* tlspool/localid.c -- Map the keys of local identities to credentials */
-
-
-#include <stdlib.h>
-#include <string.h>
-
-#include <syslog.h>
-#include <errno.h>
-
-#include <gnutls/gnutls.h>
-#include <gnutls/abstract.h>
-
-#include <tlspool/internal.h>
-
-#include "manage.h"
-#include "localid.h"
-
-
-/*
- * Lookup local identities from a BDB database.  The identities take the
- * form of a NAI, and are the keys for a key-values lookup.  The outcome
- * may offer multiple values, each representing an identity.  The general
- * structure of a value is:
- *
- * - 4 netbytes, a flags field for local identity management (see LID_xxx below)
- * - NUL-terminated string with a pkcs11 URI [ draft-pechanec-pkcs11uri ]
- * - Binary string holding the identity in binary form
- *
- * There may be prefixes for generic management, but these are not made
- * available to this layer.
- */
-
-
-
-/* Retrieve flags from the credentials structure found in dbh_localid.
- * The function returns non-zero on success (zero indicates syntax error).
- */
-int dbcred_flags (DBT *creddata, uint32_t *flags) {
-       int p11privlen;
-       if (creddata->size <= 4) {
-               return 0;
-       }
-       *flags = ntohl (* (uint32_t *) creddata->data);
-       return 1;
-}
-
-
-/* Interpret the credentials structure found in dbh_localid.
- * This comes down to splitting the (data,size) structure into fields:
- *  - a 32-bit flags field
- *  - a char * sharing the PKCS #11 private key location, NULL on LID_NO_PKCS11
- *  - a (data,size) structure for the public credential, also when LID_CHAINED
- * The function returns non-zero on success (zero indicates syntax error).
- */
-int dbcred_interpret (gnutls_datum_t *creddata, uint32_t *flags, char **p11priv, uint8_t **pubdata, int *pubdatalen) {
-       int p11privlen;
-       if (creddata->size <= 4) {
-               return 0;
-       }
-       *flags = ntohl (* (uint32_t *) creddata->data);
-       if ((*flags) & LID_NO_PKCS11) {
-               *p11priv = NULL;
-       } else {
-               *p11priv = ((char *) creddata->data) + 4;
-               p11privlen = strnlen (*p11priv, creddata->size - 4);
-               if (p11privlen == creddata->size - 4) {
-                       return 0;
-               }
-#ifdef TODO_PKCS11_ADDED
-               if (strncmp (*p11priv, "pkcs11:", 7) != 0) {
-                       return 0;
-               }
-#endif
-       }
-       *pubdata    = ((uint8_t *) creddata->data) + 4 + p11privlen + 1;
-       *pubdatalen =              creddata->size  - 4 - p11privlen - 1;
-       if (*pubdatalen < 20) {
-               // Unbelievably short certificate (arbitrary sanity limit 20)
-               return 0;
-       }
-       return 1;
-}
-
-
-/* Create an iterator for a given localid value.  Use keys from dhb_lid.
- * The first value is delivered; continue with dbcred_iterate_next().
- *
- * The cursor must have been opened on dbh_localid within the desired
- * transaction context; the caller must close it after iteration.
- *
- * The value returned is only non-zero if a value was setup.
- * The DB_NOTFOUND value indicates that the key was not found.
- */
-gtls_error dbcred_iterate_from_localid (DBC *cursor, DBT *keydata, DBT *creddata) {
-       int gtls_errno = GNUTLS_E_SUCCESS;
-       E_d2ge ("Key not found in db_localid",
-               cursor->get (cursor, keydata, creddata, DB_SET));
-       return gtls_errno;
-}
-
-
-/* Construct an iterator for a given remoteid selector.  Apply stepwise
- * generalisation to find the most concrete match.  The first value found
- * is delivered; continue with dbcred_iterate_next().
- *
- * The remotesel value in string representation is the key to discpatn,
- * forming the initial disclosure pattern.  This key should be setup with
- * enough space to store the pattern (which is never longer than the original
- * remoteid) plus a terminating NUL character.
- *
- * Note that remotesel already has the first value activated, usually the
- * same as the remoteid.  This is assumed to be available, so don't call
- * this function otherwise.  In practice, this is hardly a problem; any
- * valid remoteid will provide a valid selector whose first iteration is to
- * repeat the remoteid.  Failure to start even this is a sign of a syntax
- * error, which is good to be treating separately from not-found conditions.
- *
- * The started iteration is a nested iteration over dbh_disclose for the
- * pattern found, and inside that an iteration over dbh_localid for the
- * localid values that this gave.  This means that two cursors are needed,
- * both here and in the subsequent dbcred_iterate_next() calls.
- *
- * The cursors crs_disclose and crs_localid must have been opened on
- * dbh_disclose and dbh_localid within the desired transaction context;
- * the caller must close them after iteration.
- *
- * The value returned is zero if a value was setup; otherwise an error code.
- * The DB_NOTFOUND value indicates that no selector matching the remoteid
- * was found in dbh_disclose.
- */
-gtls_error dbcred_iterate_from_remoteid_selector (DBC *crs_disclose, DBC *crs_localid, selector_t *remotesel, DBT *discpatn, DBT *keydata, DBT *creddata) {
-       int gtls_errno = GNUTLS_E_SUCCESS;
-       int more = 1;
-       while (more) {
-               int fnd;
-               discpatn->size = donai_iterate_memput (discpatn->data, remotesel);
-               tlog (TLOG_DB, LOG_DEBUG, "Looking up remote selector %.*s", discpatn->size, (char *) discpatn->data);
-               fnd = crs_disclose->get (crs_disclose, discpatn, keydata, DB_SET);
-               if (fnd == 0) {
-                       // Got the selector pattern!
-                       // Now continue, even when no localids will work.
-                       E_d2ge ("Key not found in db_localid",
-                               crs_localid->get (
-                                       crs_localid,
-                                       keydata,
-                                       creddata,
-                                       DB_SET));
-                       return gtls_errno;
-               } else if (fnd != DB_NOTFOUND) {
-                       E_d2ge ("Failed while searching with remote ID selector", fnd);
-                       break;
-               }
-               more = selector_iterate_next (remotesel);
-       }
-       // Ended here with nothing more to find
-       E_d2ge ("No selector matches remote ID in db_disclose",
-               DB_NOTFOUND);
-       return gtls_errno;
-}
-
-
-/* Move an iterator to the next credential data value.  When done, the value
- * returned should be DB_NOTFOUND.
- *
- * The outer cursor (for dbh_disclose) is optional, and is only used when
- * the prior call was from dbcred_iterate_from_remoteid().
- *
- * The optional discpatn must be supplied only when dbh_disclose is provided.
- * It holds the key value for the dbh_disclose outer cursor.
- *
- * The keydata will be filled with the intermediate key when dbh_disclose is
- * provided.  It is also used to match the next record with the current one.
- *
- * The value returned is zero if a value was setup; otherwise an error code.
- * The DB_NOTFOUND value indicates that no further duplicate was not found.
- */
-db_error dbcred_iterate_next (DBC *opt_crs_disclose, DBC *crs_localid, DBT *opt_discpatn, DBT *keydata, DBT *creddata) {
-       int db_errno = 0;
-       db_errno = crs_localid->get (crs_localid, keydata, creddata, DB_NEXT_DUP);
-       if (db_errno != DB_NOTFOUND) {
-               return db_errno;
-       }
-       // Inner loop ended in DB_NOTFOUND, optionally continue in outer loop
-       if ((opt_crs_disclose != NULL) && (opt_discpatn != NULL)) {
-               while (db_errno == DB_NOTFOUND) {
-                       db_errno = opt_crs_disclose->get (opt_crs_disclose, opt_discpatn, keydata, DB_NEXT_DUP);
-                       if (db_errno == DB_NOTFOUND) {
-                               return db_errno;
-                       }
-                       db_errno = crs_localid->get (crs_localid, keydata, creddata, DB_SET);
-               }
-       }
-       return db_errno;
-}
-
-
-/* Iterate over selector values that would generalise the donai.  The
- * selector_t shares data from the donai, so it allocates no internal
- * storage and so it can be dropped at any time during the iteration.
- * Meanwhile, the donai must not drop storage before iteration stops.
- *
- * The value returned is only non-zero if a value was setup.
- */
-int selector_iterate_init (selector_t *iterator, donai_t *donai) {
-       //
-       // If the user name is not NULL but empty, bail out in horror
-       if ((donai->user != NULL) && (donai->userlen <= 0)) {
-               return 0;
-       }
-       //
-       // If the domain name is empty or NULL, bail out in horror
-       if ((donai->domain == NULL) || (donai->domlen == 0)) {
-               return 0;
-       }
-       //
-       // The first and most concrete pattern is the donai itself
-       memcpy (iterator, donai, sizeof (*iterator));
-       return 1;
-}
-
-int selector_iterate_next (selector_t *iterator) {
-       int skip;
-       //
-       // If the user name is not NULL but empty, bail out in horror
-       if ((iterator->user != NULL) && (iterator->userlen == 0)) {
-               return 0;
-       }
-       //
-       // If the domain name is empty or NULL, bail out in horror
-       if ((iterator->domain == NULL) || (iterator->domlen == 0)) {
-               return 0;
-       }
-       //
-       // If there is a user component and it is non-empty, make it empty
-       // If it was empty, permit it to become non-empty again, and continue
-       if (iterator->user) {
-               if (iterator->userlen > 0) {
-                       iterator->userlen = -iterator->userlen;
-                       return 1;
-               }
-               iterator->userlen = -iterator->userlen;
-       }
-       //
-       // If the domain is a single dot, we're done
-       if ((iterator->domlen == 1) && (*iterator->domain == '.')) {
-               return 0;
-       }
-       //
-       // Replace the domain (known >= 1 chars) with the next dot's domain
-       skip = 1;
-       while ((skip < iterator->domlen) && (iterator->domain [skip] != '.')) {
-               skip++;
-       }
-       if (skip == iterator->domlen) {
-               iterator->domain = ".";         // Last resort domain
-               iterator->domlen = 1;
-       } else {
-               iterator->domain += skip;
-               iterator->domlen -= skip;
-       }
-       return 1;
-}
-
-
-/* Check if a selector is a pattern that matches the given donai value.
- * The value returned is non-zero for a match, zero for a non-match.
- */
-int donai_matches_selector (donai_t *donai, selector_t *pattern) {
-       int extra;
-       //
-       // Bail out in horror on misconfigurations
-       if ((donai->user != NULL) && (donai->userlen <= 0)) {
-               return 0;
-       }
-       if ((donai  ->domain == NULL) || (donai  ->domlen <= 0)) {
-               return 0;
-       }
-       if ((pattern->domain == NULL) || (pattern->domlen <= 0)) {
-               return 0;
-       }
-       //
-       // User name handling first
-       if (pattern->user) {
-               //
-               // Pattern has a user?  Then request a user in the donai too
-               if (donai->user == NULL) {
-                       return 0;
-               }
-               //
-               // Non-empty user in pattern?  Then match everything
-               if (*pattern->user) {
-                       if (pattern->userlen > 0) {
-                               if (donai->userlen != pattern->userlen) {
-                                       return 0;
-                               }
-                               if (memcmp (donai->user, pattern->user, donai->userlen) != 0) {
-                                       return 0;
-                               }
-                       }
-               }
-       } else {
-               //
-               // Pattern without user, then donai may not have one either
-               if (donai->user != NULL) {
-                       return 0;
-               }
-       }
-       //
-       // Domain name handling second
-       if (*pattern->domain == '.') {
-               extra = donai->domlen - pattern->domlen;
-               if (extra < 0) {
-                       //
-                       // No good having a longer pattern than a donai.domain
-                       return 0;
-               }
-       } else {
-               extra = 0;
-       }
-       return (memcmp (donai->domain + extra, pattern->domain, pattern->domlen) == 0);
-}
-
-
-/* Fill a donai structure from a stable string. The donai will share parts
- * of the string.  The function can also be used to construct a selector
- * from a string; their structures are the same and the syntax is not
- * parsed to ensure non-empty usernames and non-dot-prefixed domain names.
- */
-donai_t donai_from_stable_string (char *stable, int stablelen) {
-       donai_t retval;
-       retval.userlen = stablelen - 1;
-       while (retval.userlen > 0) {
-               if (stable [retval.userlen] == '@') {
-                       break;
-               }
-               retval.userlen--;
-       }
-       if (stable [retval.userlen] == '@') {
-               retval.user = stable;
-               retval.domain = stable + (retval.userlen + 1);
-               retval.domlen = stablelen - 1 - retval.userlen;
-       } else {
-               retval.user = NULL;
-               retval.domain = stable;
-               retval.domlen = stablelen;
-       }
-       return retval;
-}
-
-/* Print a donai or iterated selector to the given text buffer.  The
- * text will be precisely the same as the originally parsed text.  An
- * iterator may deliver values that are shorter, not longer.  The value
- * returned is the number of bytes written.  No trailing NUL character
- * will be written.
- */
-int donai_iterate_memput (char *selector_text, donai_t *iterator) {
-       int len = 0;
-       if (iterator->user != NULL) {
-               if (iterator->userlen > 0) {
-                       memcpy (selector_text, iterator->user, iterator->userlen);
-                       len += iterator->userlen;
-               }
-               selector_text [len++] = '@';
-       }
-       memcpy (selector_text + len, iterator->domain, iterator->domlen);
-       len += iterator->domlen;
-       return len;
-}
-
diff --git a/src/localid.h b/src/localid.h
deleted file mode 100644 (file)
index 190b7ae..0000000
+++ /dev/null
@@ -1,223 +0,0 @@
-/* tlspool/localid.h -- Map the keys of local identities to credentials */
-
-
-/*
- * Lookup local identities from a BDB database.  The identities take the
- * form of a NAI, and are the keys for a key-values lookup.  The outcome
- * may offer multiple values, each representing an identity.  The general
- * structure of a value is:
- *
- * - 4 netbytes, a flags field for local identity management (see LID_xxx below)
- * - NUL-terminated string with a pkcs11 URI [ draft-pechanec-pkcs11uri ]
- * - Binary string holding the identity in binary form
- *
- * There may be prefixes for generic management, but these are not made
- * available to this layer.
- */
-
-
-#define LID_HDRSZ      (MGT_HDRSZ + 4)
-
-
-#define LID_TYPE_MASK  0x000000ff      /* Separate out the LID_TYPE_xxx bits */
-#define LID_TYPE_ANY   0x000000ff      /* No filter, permit anything */
-#define LID_TYPE_X509  0x00000001      /* X.509 certificate, DER-encoded */
-#define LID_TYPE_PGP   0x00000002      /* OpenPGP public key, binary form */
-#define LID_TYPE_SRP   0x00000003      /* No data, flags existence */
-#define LID_TYPE_KRB5  0x00000004      /* Ticket */
-
-#define LID_TYPE_MIN   LID_TYPE_X509
-#define LID_TYPE_MAX   LID_TYPE_KRB5
-#define LID_TYPE_OFS   LID_TYPE_MIN
-#define LID_TYPE_CNT   (1 + LID_TYPE_MAX - LID_TYPE_MIN)
-
-#define LID_ROLE_MASK  0x00000300      /* Separate out the LID_ROLE_xxx bits */
-#define LID_ROLE_CLIENT        0x00000100      /* This may be used for clients */
-#define LID_ROLE_SERVER        0x00000200      /* This may be used for servers */
-#define LID_ROLE_BOTH  0x00000300      /* This may be used for both roles */
-#define LID_ROLE_NONE  0x00000000      /* This may be used for neither role */
-
-#define LID_NO_PKCS11  0x00001000      /* No prefixed PKCS #11 URI + NUL */
-#define LID_CHAINED    0x00002000      /* Credential isa type-specific chain */
-//TODO// Encode LID_NEEDS_CHAIN support
-#define LID_NEEDS_CHAIN        0x00004000      /* Chain certs are in central storage */
-
-
-/* Impose a practial upper bound to the lenght of a DoNAI, a domain-or-NAI.
- * This is important to avoid overzealous allocations and subsequent buffer
- * or stack overflows.  Sigh, we live in a world where networks can carry
- * the size between memory segments in a brief time.
- */
-#define DONAI_MAXLEN 512
-
-
-/* A donai is a structure holding either user@domain.name or domain.name.
- * A selector is a simple pattern that can match with a donain, by stripping
- * local components.  For instance user@domain.name or @domain.name or @.name
- * or @. to match with user@domain.name; and, for instance domain.name or
- * .name or . to match with domain.name.
- */
-
-struct userdomain {
-       char *user;     /* not NUL-terminated; user==NULL for no @ at all */
-       int userlen;    /* valid if user!=NULL; userlen<0 in selector_t for 0 */
-       char *domain;   /* not NULL, start . signifies a pattern */
-       int domlen;     /* always >0 */
-};
-
-
-typedef struct userdomain donai_t;  /* (user==NULL OR userlen>0) AND *domain!='.' */
-
-typedef struct userdomain selector_t;  /* userlen<0 should be read as userlen==0 */
-
-
-
-/* Setup a DBT data handle to point to a pre-allocated, fixed-size
- * data buffer that will be used throughout the use of the handle.
- * Cleanup is not necessary, but the buffer must not be cleared
- * before the last use of the data handle.
- */
-static inline void dbt_init_fixbuf (DBT *dbt, void *buffer, u_int32_t bufsize) {
-       bzero (dbt, sizeof (DBT));
-       dbt->data = buffer;
-       dbt->size =
-       dbt->ulen = bufsize;
-       dbt->flags |= DB_DBT_USERMEM;
-}
-
-/* Setup a DBT data handle for malloc() by the database, and free() by the
- * calling program.
- * Cleanup with dbt_free() or dbt_store() is needed after every lookup
- * that succeeded.
- */
-static inline void dbt_init_malloc (DBT *dbt) {
-       bzero (dbt, sizeof (DBT));
-       dbt->flags |= DB_DBT_MALLOC;
-}
-
-/* Free the DBT data handle that was setup with dbt_init_malloc().  This
- * or dbt_store() must be called after every successfully returned data
- * item.
- */
-static inline void dbt_free (DBT *dbt) {
-       /* assert (dbt->flags & DB_DBT_MALLOC); */
-       free (dbt->data);
-       dbt->data = NULL;
-}
-
-/* Store the DBT data handle's data into external structures, moving both
- * the data pointer and size.  The data handle must have been setup with
- * dbt_init_malloc().  Afterwards, clear the data handle for use in
- * another iteration.
- */
-static inline void dbt_store (DBT *dbt, gnutls_datum_t *output) {
-       /* assert (dbt->flags & DB_DBT_MALLOC); */
-       output->data = dbt->data;
-       output->size = dbt->size;
-       dbt->data = NULL;
-}
-
-
-/* Create an iterator for a given localid value.  Use keys from dhb_lid.
- * The first value is delivered; continue with dbcred_iterate_next().
- *
- * The cursor must have been opened on dbh_localid within the desired
- * transaction context; the caller must close it after iteration.
- *
- * The value returned is only non-zero if a value was setup.
- * The DB_NOTFOUND value indicates that the key was not found.
- */
-int dbcred_iterate_from_localid (DBC *cursor, DBT *keydata, DBT *creddata);
-
-/* Construct an iterator for a given remoteid selector.  Apply stepwise
- * generalisation to find the most concrete match.  The first value found
- * is delivered; continue with dbcred_iterate_next().
- *
- * The remotesel value in string representation is the key to discpatn,
- * forming the initial disclosure pattern.  This key should be setup with
- * enough space to store the pattern (which is never longer than the original
- * remoteid) plus a terminating NUL character.
- *
- * Note that remotesel already has the first value activated, usually the
- * same as the remoteid.  This is assumed to be available, so don't call
- * this function otherwise.  In practice, this is hardly a problem; any
- * valid remoteid will provide a valid selector whose first iteration is to
- * repeat the remoteid.  Failure to start even this is a sign of a syntax
- * error, which is good to be treating separately from not-found conditions.
- *
- * The started iteration is a nested iteration over dbh_disclose for the
- * pattern found, and inside that an iteration over dbh_localid for the
- * localid values that this gave.  This means that two cursors are needed,
- * both here and in the subsequent dbcred_iterate_next() calls.
- *
- * The cursors crs_disclose and crs_localid must have been opened on
- * dbh_disclose and dbh_localid within the desired transaction context;
- * the caller must close them after iteration.
- *
- * The value returned is zero if a value was setup; otherwise an error code.
- * The DB_NOTFOUND value indicates that no selector matching the remoteid
- * was found in dbh_disclose.
- */
-int dbcred_iterate_from_remoteid_selector (DBC *crs_disclose, DBC *crs_localid, selector_t *remotesel, DBT *discpatn, DBT *keydata, DBT *creddata);
-
-/* Move an iterator to the next credential data value.  When done, the value
- * returned should be DB_NOTFOUND.
- *
- * The outer cursor (for dbh_disclose) is optional, and is only used when
- * the prior call was from dbcred_iterate_from_remoteid().
- *
- * The optional discpatn must be supplied only when dbh_disclose is provided.
- * It holds the key value for the dbh_disclose outer cursor.
- *
- * The keydata will be filled with the intermediate key when dbh_disclose is
- * provided.  It is also used to match the next record with the current one.
- *
- * The value returned is zero if a value was setup; otherwise an error code.
- * The DB_NOTFOUND value indicates that no further duplicate was not found.
- */
-int dbcred_iterate_next (DBC *opt_crs_disclose, DBC *crs_localid, DBT *opt_discpatn, DBT *keydata, DBT *creddata);
-
-
-
-/* Interpret the credentials structure found in dbh_localid.
- * This comes down to splitting the (data,size) structure into fields:
- *  - a 32-bit flags field
- *  - a char * sharing the PKCS #11 private key location
- *  - a (data,size) structure for the public credential
- * The function returns non-zero on success (zero indicates syntax error).
- */
-int dbcred_interpret (gnutls_datum_t *creddata, uint32_t *flags, char **p11priv, uint8_t **pubdata, int *pubdatalen);
-
-
-/* Iterate over selector values that would generalise the donai.  The
- * selector_t shares data from the donai, so it allocates no internal
- * storage and so it can be dropped at any time during the iteration.
- * Meanwhile, the donai must not drop storage before iteration stops.
- *
- * The value returned is only non-zero if a value was setup.
- */
-int selector_iterate_init (selector_t *iterator, donai_t *donai);
-int selector_iterate_next (selector_t *iterator);
-
-/* Print a donai or iterated selector to the given text buffer.  The
- * text will be precisely the same as the originally parsed text.  An
- * iterator may deliver values that are shorter, not longer.  The value
- * returned is the number of bytes written.  No trailing NUL character
- * will be written.
- */
-int donai_iterate_memput (char *selector_text, donai_t *iterator);
-
-
-/* Check if a selector is a pattern that matches the given donai value.
- * The value returned is non-zero for a match, zero for a non-match.
- */
-int donai_matches_selector (donai_t *donai, selector_t *pattern);
-
-
-/* Fill a donai structure from a stable string. The donai will share parts
- * of the string.  The function can also be used to construct a selector
- * from a string; their structures are the same and the syntax is not
- * parsed to ensure non-empty usernames and non-dot-prefixed domain names.
- */
-donai_t donai_from_stable_string (char *stable, int stablelen);
-