Here's a quick prototype implementation of a typical backend. 
The wrapper isn't shown - that's the code common to all plug-ins 
that's located in the static part of the library, e.g., breaking a
cert chain into multiple certs, or reconstructing that cert chain.
The dynamic part is much more database-centric.


/*
 *      reference plug-in implementation for Berkeley DB.
 *      prototype only - do not use!
 *
 *      N.B., the corresponding loader & library wrapper are not yet
 *      published.
 *
 *      Major omission: search() needs to support search attributes -
 *      vector of OID and attribute value.
 */
#include <errno.h>
#include <time.h>
#include <string.h>

#include <openssl/asn1.h>
#include <openssl/bio.h>
#include <openssl/x509.h>

#include <db1/db.h>

/* ------------------------------------------------------------
 * Structures defined by the loader / wrapper.
 * Names likely to change, etc.
 * ------------------------------------------------------------ */

/*
 *  This is the data that *every* plug-in is expected to support
 */
struct rec {
        time_t creationDate;
        X509 *cert;
        X509_SIG *key;
};

/* hook structure - in reality 'DB *' will be 'void *' */
struct hook {
        char *name;
        int version[2];

        int (*load)(void);
        int (*unload)(void);

        DB * (*open)(const char *, int, int);
        int (*close)(DB *);

        /* lookup-by-alias routines */
        int (*lookup)(DB *, const void *, size_t, struct rec *);
        
        /* update-by-alias routines */
        int (*store)(DB *, const void *, size_t, const struct rec *);
        int (*remove)(DB *, const void *, size_t);
        
        /* search routines and kin */
        int (*search)(DB *, int (*)(), void * /*, search attributes */);

        int (*getCount)(DB *);
        ssize_t (*getAliasByCertificate)(DB *, void *, size_t, const X509 *);
};

/* ------------------------------------------------------------ */

/* this function doesn't exist yet... */
extern X509_SIG * X509_SIG_dup(const X509_SIG *);

static int decode (DBT *dbt, struct rec *);
static int encode (DBT *dbt, const struct rec *);
static void release(struct rec *);
static int lookup(DB *db, const void *alias, size_t len, struct rec *);
static int store(DB *db, const void *alias, size_t len, const struct rec *);
static int scan(DB *db, int (*cb)(), void *ptr);
static int deleteEntry(DB *db, const void *alias, size_t len);

static DB *dopen(const char *filename, int flags, int mode);
static int dclose(DB *);

static ssize_t getCount(DB *);
static ssize_t getAliasByCertificate (DB *, void *, size_t, const X509 *);
static int search(DB *db, int (*cb)(), void *data /*, search attributes */);


/* ------------------------------------------------------------
 * Utility functions
 * ------------------------------------------------------------ */

/*
 *      Decode a single entry from database format.
 */
int
decode (DBT *dbt, struct rec *rec)
{
        BIO *bio = NULL;
        unsigned char *p;
        size_t len;

        if (dbt == NULL || rec == NULL) {
                errno = EINVAL;
                return -1;
        }

        if (dbt->size <= 10) {
                errno = EINVAL;
                return -1;
        }

        p = dbt->data;

        /* check version number */
        if (!(p[0] == 1 && p[1] == 0)) {
                errno = EINVAL;
                return -1;
        }

        rec->creationDate = (p[2] << 24) | (p[3] << 16) | (p[4] << 8) | p[5];

        len = (p[6] << 24) | (p[7] << 16) | (p[8] << 8) | p[9];

        if (dbt->size != 10 + len) {
                errno = EINVAL;
                return -1;
        }
        
        bio = BIO_new_mem_buf(p + 10, len);
        // FIXME: does this work when cert or key is missing?
        ASN1_d2i_bio(X509_new, d2i_X509, bio, &rec->cert);
        ASN1_d2i_bio(X509_SIG_new, d2i_X509_SIG, bio, &rec->key);
        BIO_free(bio);

        return 0;
}

/*
 *      Encode a single entry into database format.
 */
int
encode (DBT *dbt, const struct rec *rec)
{
        BIO *bio = NULL;
        size_t len;
        unsigned char *d;

        if (rec->cert == NULL && rec->key == NULL) {
                errno = EINVAL;
                return -1;
        }

        if ((bio = BIO_new(BIO_s_mem())) == NULL) {
                errno = ENOMEM;
                return -1;
        }

        if (rec->cert != NULL) {
                ASN1_i2d_bio(i2d_X509, bio, rec->cert);
        }
        if (rec->key != NULL) {
                ASN1_i2d_bio(i2d_X509_SIG, bio, rec->key);
        }
        len = BIO_pending(bio);

        if ((d = OPENSSL_malloc(10 + len)) == NULL) {
                errno = ENOMEM;
                BIO_free(bio);
                return -1;
        }

        /* copy version number */
        d[0] = 1;
        d[1] = 0;

        /* copy timestamp */
        d[2] = 0xFF & (rec->creationDate >> 24);
        d[3] = 0xFF & (rec->creationDate >> 16);
        d[4] = 0xFF & (rec->creationDate >> 8);
        d[5] = 0xFF & (rec->creationDate);

        /* copy length of rest of data */
        d[6] = 0xFF & (len >> 24);
        d[7] = 0xFF & (len >> 16);
        d[8] = 0xFF & (len >> 8);
        d[9] = 0xFF & (len);

        if (BIO_read(bio, d+10, len) != len) {
                // FIXME: record error here....
                BIO_free(bio);
                OPENSSL_free(d);
                return -1;
        }

        BIO_free(bio);
        dbt->data = d;
        dbt->size = 10 + len;

        return 0;
}

/*
 *      Release fields allocated by decode()
 */
void
release (struct rec *rec)
{
        if (rec->cert != NULL) {
                X509_free((X509 *) rec->cert);
                rec->cert = NULL;
        }
        if (rec->key != NULL) {
                X509_SIG_free((X509_SIG *) rec->key);
                rec->key = NULL;
        }
}

/*
 *      Open or create a database
 */
DB *
dopen (const char *filename, int flags, int mode)
{
        return dbopen(filename, flags, mode, DB_HASH, 0);
}

/*
 *      Close the current database.
 */
int
dclose (DB *db)
{
        if (db == NULL) {
                return EINVAL;
                return -1;
        }

        return db->close(db);
}

/*
 *      Look up a single entry by alias.
 */
int
lookup (DB *db, const void *alias, size_t len, struct rec *rec)
{
        DBT key, value = { NULL, 0 };
        int r;

        if (db == NULL || alias == NULL || len == 0 || rec == NULL) {
                errno = EINVAL;
                return -1;
        }

        key.data = (void *) alias;
        key.size = len;
        if ((r = db->get(db, &key, &value, 0)) == -1) {
                return -1;
        }

        if (r == 1) {
                errno = ENOENT;
                return -1;
        }

        r = decode(&value, rec);
        free(value.data);

        return r;
}

/*
 *      Store a single entry by alias
 */
int
store (DB *db, const void *alias, size_t len, const struct rec *rec)
{
        DBT key, value;
        int r;

        if (db == NULL || alias == NULL || len == 0 || rec == NULL) {
                errno = EINVAL;
                return -1;
        }

        if (rec->cert == NULL && rec->key == NULL) {
                return deleteEntry(db, alias, len);
        }

        key.data = (void *) alias;
        key.size = len;
        if ((r = encode(&value, rec)) == -1) {
                return -1;
        }

        if ((r = db->put(db, &key, &value, 0)) == -1) {
                OPENSSL_free(value.data);
                return -1;
        }

        OPENSSL_free(value.data);

        return r;
}

/*
 *      Step through all entries.  If callback routine returns non-zero
 *      value, processing will be terminated.
 */
int
scan (DB *db, int (*cb)(), void *ptr)
{
        DBT key = { NULL, 0 }, value = { NULL, 0 };
        int flags;
        struct rec rec = { 0, NULL, NULL };
        int r;

        if (db == NULL || cb == NULL) {
                errno = EINVAL;
                return -1;
        }

        for (flags = R_FIRST; ; flags = R_NEXT) {
                if ((r = db->seq(db, &key, &value, flags)) == -1) {
                        break;
                }
                if (r == 1) {
                        break;
                }

                r = cb(&key, &value, ptr);
                if (r != 0) {
                        break;
                }
        }
        release(&rec);
        free(key.data);
        free(value.data);

        return r;
}

/*
 * Remove an entry.
 */
int
deleteEntry (DB *db, const void *alias, size_t len)
{
        DBT key;

        if (db == NULL || alias == NULL || len == 0) {
                errno = EINVAL;
                return -1;
        }

        key.data = (void *) alias;
        key.size = len;
        return db->del(db, &key, 0);
}

/* ============================================================
 * No Berkeley DB-specific code should follow this point.
 * ============================================================ */

/* ------------------------------------------------------------
 * Search functions
 * ------------------------------------------------------------ */
static int count_cb(DBT *key, DBT *value, void *ptr);
static int certLookup_cb(DBT *key, DBT *value, void *ptr);
static int search_cb(DBT *key, DBT *value, void *ptr);

/*
 * Ancillary function used to count the number of entries.
 */
int
count_cb (DBT *key, DBT *value, void *ptr)
{
        ssize_t *s = (ssize_t *) ptr;
        (*s)++;
        return 0;
}

/*
 * Count the total number of entries in the database.
 */
ssize_t
getCount (DB *db)
{
        ssize_t cnt = 0;
        int r;

        if (db == NULL) {
                errno = EINVAL;
                return -1;
        }

        if ((r = scan(db, count_cb, &cnt)) == -1) {
                return -1;
        }

        return cnt;
}

/*
 * Ancillary structure used to do reverse lookup from cert to alias.
 */
struct certLookup_cbs {
        const X509 *cert;
        void *alias;
        size_t size;
};

/*
 * Ancillary function used do reverse lookup from cert to alias.
 */
int
certLookup_cb (DBT *key, DBT *value, void *ptr)
{
        struct rec rec;
        struct certLookup_cbs *cbs = (struct certLookup_cbs *) ptr;

        if (decode(value, &rec) == -1) {
                return -1;
        }

        if (X509_cmp(rec.cert, cbs->cert) == 0) {
                memset(cbs->alias, 0, cbs->size);
                strncpy(cbs->alias, key->data, 
                        (key->size < cbs->size) ? key->size : cbs->size);
                release(&rec);
                return key->size;
        }
        release(&rec);

        return 0;
}

/*
 * Look up the alias for a specified certificate.
 * Returns length of alias string - may be larger than 'sz,' only up
 * to 'sz' bytes will be written into buffer.
 */
ssize_t getAliasByCertificate (DB *db, void *alias, size_t sz, const X509 *x)
{
        struct certLookup_cbs cbs;
        int r;

        cbs.cert = x;
        cbs.alias = alias;
        cbs.size = sz;

        if ((r = scan (db, certLookup_cb, &cbs)) == -1) {
                return -1;
        }

        if (r == 0) {
                errno = ENOENT;
                return -1;
        }

        return r;
}

/*
 * Ancillary structure used with searches
 */
struct search_cbs {
        int (*cb)();
        void *data;
//  FIXME: list of OID & value here
};

/*
 * Ancillary function used with searches
 */
static int
search_cb (DBT *key, DBT *value, void *ptr)
{
        struct search_cbs *cbs = (struct search_cbs *) ptr;
        struct rec rec;
        int r;

        if (decode(value, &rec) == -1) {
                return -1;
        }

        // FIXME: actually test for matching attributes here...

        r = cbs->cb(key->data, key->size, rec.cert, rec.key, ptr);

        release (&rec);

        return r;
}

/*
 * Search for entries matching pattern.
 *
 * The callback function should have a function signature like
 *
 *  int cb (const void *alias, size_t len,
 *          const X509 *x, const X509_SIG *sig, void *data)
 *
 * FIXME: need to add search attributes.
 */
int
search (DB *db, int (*cb)(), void *data /*, search attributes */)
{
        struct search_cbs cbs;
        int r;

        cbs.cb = cb;
        cbs.data = data;

        if ((r = scan (db, search_cb, &cbs)) == -1) {
                return -1;
        }

        return r;
}


/* ------------------------------------------------------------
 * Hook for plugin
 * ------------------------------------------------------------ */

struct hook keystorehook = {

        "Berkeley DB 1.x (reference implementation)",
        { 1, 0 },  /* API version */

        NULL,   /* load */
        NULL,   /* unload */
        dopen,
        dclose,

        /* lookup-by-alias routines */
        lookup,

        /* update-by-alias routines */
        store,
        deleteEntry,

        /* search routines and kin */
        search,
        getCount,
        getAliasByCertificate
};
______________________________________________________________________
OpenSSL Project                                 http://www.openssl.org
Development Mailing List                       [EMAIL PROTECTED]
Automated List Manager                           [EMAIL PROTECTED]

Reply via email to