/* Copyright (c) 2020 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "llist.h" #include "buffer.h" #include "hash.h" #include "dcrypt.h" #include "oauth2.h" #include "oauth2-private.h" struct oauth2_key_cache_entry { const char *key_id; struct dcrypt_public_key *pubkey; buffer_t *hmac_key; struct oauth2_key_cache_entry *prev, *next; }; HASH_TABLE_DEFINE_TYPE(oauth2_key_cache, const char *, struct oauth2_key_cache_entry *); struct oauth2_validation_key_cache { pool_t pool; HASH_TABLE_TYPE(oauth2_key_cache) keys; struct oauth2_key_cache_entry *list_start; }; struct oauth2_validation_key_cache *oauth2_validation_key_cache_init(void) { pool_t pool = pool_alloconly_create( MEMPOOL_GROWING"oauth2 key cache", 128); struct oauth2_validation_key_cache *cache = p_new(pool, struct oauth2_validation_key_cache, 1); cache->pool = pool; hash_table_create(&cache->keys, pool, 8, str_hash, strcmp); return cache; } void oauth2_validation_key_cache_deinit( struct oauth2_validation_key_cache **_cache) { struct oauth2_validation_key_cache *cache = *_cache; *_cache = NULL; if (cache == NULL) return; /* free resources */ struct oauth2_key_cache_entry *entry = cache->list_start; while (entry != NULL) { if (entry->pubkey != NULL) dcrypt_key_unref_public(&entry->pubkey); entry = entry->next; } hash_table_destroy(&cache->keys); pool_unref(&cache->pool); } int oauth2_validation_key_cache_lookup_pubkey( struct oauth2_validation_key_cache *cache, const char *key_id, struct dcrypt_public_key **pubkey_r) { if (cache == NULL) return -1; struct oauth2_key_cache_entry *entry = hash_table_lookup(cache->keys, key_id); if (entry == NULL || entry->pubkey == NULL) return -1; *pubkey_r = entry->pubkey; return 0; } int oauth2_validation_key_cache_lookup_hmac_key( struct oauth2_validation_key_cache *cache, const char *key_id, const buffer_t **hmac_key_r) { if (cache == NULL) return -1; struct oauth2_key_cache_entry *entry = hash_table_lookup(cache->keys, key_id); if (entry == NULL || entry->hmac_key == NULL || entry->hmac_key->used == 0) return -1; *hmac_key_r = entry->hmac_key; return 0; } void oauth2_validation_key_cache_insert_pubkey( struct oauth2_validation_key_cache *cache, const char *key_id, struct dcrypt_public_key *pubkey) { if (cache == NULL) return; struct oauth2_key_cache_entry *entry = hash_table_lookup(cache->keys, key_id); if (entry != NULL) { dcrypt_key_unref_public(&entry->pubkey); entry->pubkey = pubkey; if (entry->hmac_key != NULL) buffer_set_used_size(entry->hmac_key, 0); return; } entry = p_new(cache->pool, struct oauth2_key_cache_entry, 1); entry->key_id = p_strdup(cache->pool, key_id); entry->pubkey = pubkey; DLLIST_PREPEND(&cache->list_start, entry); hash_table_insert(cache->keys, entry->key_id, entry); } void oauth2_validation_key_cache_insert_hmac_key( struct oauth2_validation_key_cache *cache, const char *key_id, const buffer_t *hmac_key) { if (cache == NULL) return; struct oauth2_key_cache_entry *entry = hash_table_lookup(cache->keys, key_id); if (entry != NULL) { dcrypt_key_unref_public(&entry->pubkey); if (entry->hmac_key == NULL) { entry->hmac_key = buffer_create_dynamic( cache->pool, hmac_key->used); } else { buffer_set_used_size(entry->hmac_key, 0); } buffer_append(entry->hmac_key, hmac_key->data, hmac_key->used); return; } entry = p_new(cache->pool, struct oauth2_key_cache_entry, 1); entry->key_id = p_strdup(cache->pool, key_id); entry->hmac_key = buffer_create_dynamic(cache->pool, hmac_key->used); buffer_append(entry->hmac_key, hmac_key->data, hmac_key->used); DLLIST_PREPEND(&cache->list_start, entry); hash_table_insert(cache->keys, entry->key_id, entry); } int oauth2_validation_key_cache_evict(struct oauth2_validation_key_cache *cache, const char *key_id) { if (cache == NULL) return -1; struct oauth2_key_cache_entry *entry = hash_table_lookup(cache->keys, key_id); if (entry == NULL) return -1; if (entry->pubkey != NULL) dcrypt_key_unref_public(&entry->pubkey); DLLIST_REMOVE(&cache->list_start, entry); hash_table_remove(cache->keys, key_id); return 0; }