#include "mod_auth_singleid.h" #include #include #include /* Yes, this looks backwards. */ typedef char sid_nonce_t[40]; struct sid_storage { /* The association with our server */ char server[256]; char handle[256]; unsigned char secret[256]; char type[64]; size_t n_secret; time_t expires; /* Book keeping for the records */ sid_nonce_t *records; size_t first; size_t total; size_t count; int wrapped; }; int sid_storage_store_assoc (sid_storage_t *store, const sid_assoc_t *assoc) { assert (store); assert (assoc); /* Check that we have enough space to store this information */ if ((assoc->server && strlen (assoc->server) > sizeof (store->server)) || (assoc->handle && strlen (assoc->handle) > sizeof (store->handle)) || (assoc->n_secret && assoc->n_secret > sizeof (store->secret)) || (assoc->type && strlen (assoc->type) > sizeof (store->type))) return 0; strcpy (store->server, assoc->server ? assoc->server : ""); strcpy (store->handle, assoc->handle ? assoc->handle : ""); memcpy (store->secret, assoc->secret, assoc->n_secret); strcpy (store->type, assoc->type ? assoc->type : ""); store->expires = assoc->expires; return 1; } int sid_storage_find_assoc (sid_storage_t *store, const char *server, const char *handle, sid_assoc_t *assoc) { assert (store); assert (assoc); if (server && (strlen (server) > sizeof (store->server) || strcmp (server, store->server) != 0)) return 0; if (handle && (strlen (handle) > sizeof (store->handle) || strcmp (handle, store->handle) != 0)) return 0; assoc->server = store->server; assoc->handle = store->handle; assoc->type = store->type; assoc->secret = store->secret; assoc->n_secret = store->n_secret; assoc->expires = store->expires; return 1; } void sid_storage_invalidate_assoc (sid_storage_t *store, const char *server, const char *handle) { sid_assoc_t dummy; assert (store); if (sid_storage_find_assoc (store, server, handle, &dummy)) { store->server[0] = 0; store->handle[0] = 0; store->type[0] = 0; memset (store->secret, 0, sizeof (store->secret)); store->n_secret = 0; store->expires = 0; store->secret[0] = 0; } } #define nonce_compare(a, b) \ memcmp (a, b, sizeof (sid_nonce_t)) static void nonce_put (sid_nonce_t rec, const char *nonce) { size_t len = strlen (nonce); char *dst = (char*)rec; /* If it's short enough, then just store. Fast */ if (len < sizeof (rec)) { memcpy (dst, nonce, len); memset (dst + len, 0, sizeof (rec) - len); /* Too long, need to compress into the record */ } else { apr_sha1_ctx_t ctx; assert (sizeof (sid_nonce_t) == APR_SHA1_DIGESTSIZE + 20); assert (len > 20); /* The date prefix we just copy in */ memcpy (dst, nonce, 20); /* Hash the rest into the buffer */ apr_sha1_init (&ctx); apr_sha1_update (&ctx, nonce + 20, len - 20); apr_sha1_final ((unsigned char*)dst + 20, &ctx); } } static void insert_nonce_record (sid_storage_t *store, sid_nonce_t rec, size_t at) { sid_nonce_t *records = store->records; assert (store->total > 2); assert (at < store->total); assert (at != store->first); /* Insertion right after latest, either ancient, more likely top */ if (at == store->first + 1 % store->total) { /* We can just copy in at this point */ /* Our ring has empty space in it, so always push forwards, but only until first */ } else if (!store->wrapped) { memmove (records + at + 1, records + at, sizeof (rec) * (store->first - at)); store->first += 1; /* Move data backwards to make space */ } else if (store->first < at) { memmove (records + store->first + 2, records + store->first + 1, sizeof (rec) * (at - store->first)); /* Move data forwards to make space simply */ } else { memmove (records + at + 1, records + at, sizeof (rec) * (store->first - at - 1)); } memcpy (records[at], rec, sizeof (rec)); ++store->count; /* Track whether we have a full ring or not. */ if (!store->wrapped && store->count > store->total) store->wrapped = 1; } int sid_storage_check_nonce (sid_storage_t *store, const char *server, const char *nonce) { sid_nonce_t *records; sid_nonce_t rec; size_t at, lower, upper, mid; int res; assert (store); assert (nonce); assert (store->records); assert (store->first < store->total); nonce_put (rec, nonce); records = store->records; /* Best case scenario, new nonce is higher than our latest one */ res = nonce_compare (rec, records[store->first]); /* Was the last nonce */ if (res == 0) { return 1; /* Newer than anything, push on top */ } else if (res < 0) { at = (store->first + 1) % store->total; insert_nonce_record (store, rec, at); store->first = at; return 0; } /* Do a binary search for the item */ for (lower = 0, upper = store->total; lower < upper; ) { mid = lower + ((upper - lower) / 2); // Note: not (low + high) / 2 !! at = at + store->first % store->total; res = nonce_compare (rec, records[at]); if (res == 0) return 1; /* Have the nonce */ else if (res > 0) lower = mid + 1; else upper = mid; } insert_nonce_record (store, rec, at); return 0; /* Didn't find nonce */ }