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]