/* * get a value from the shared memory cache */ static apr_byte_t oidc_cache_shm_get(request_rec *r, const char *section, const char *key, const char **value) { oidc_debug(r, "enter, section=\"%s\", key=\"%s\"", section, key); oidc_cfg *cfg = ap_get_module_config(r->server->module_config, &auth_openidc_module); oidc_cache_cfg_shm_t *context = (oidc_cache_cfg_shm_t *) cfg->cache_cfg; int i; const char *section_key = oidc_cache_shm_get_key(r->pool, section, key); *value = NULL; /* grab the global lock */ if (oidc_cache_mutex_lock(r, context->mutex) == FALSE) return FALSE; /* get the pointer to the start of the shared memory block */ oidc_cache_shm_entry_t *t = apr_shm_baseaddr_get(context->shm); /* loop over the block, looking for the key */ for (i = 0; i < cfg->cache_shm_size_max; i++, OIDC_CACHE_SHM_ADD_OFFSET(t, cfg->cache_shm_entry_size_max)) { const char *tablekey = t->section_key; if ( (tablekey != NULL) && (apr_strnatcmp(tablekey, section_key) == 0) ) { /* found a match, check if it has expired */ if (t->expires > apr_time_now()) { /* update access timestamp */ t->access = apr_time_now(); *value = t->value; } else { /* clear the expired entry */ t->section_key[0] = '\0'; t->access = 0; } /* we safely can break now since we would not have found an expired match twice */ break; } } /* release the global lock */ oidc_cache_mutex_unlock(r, context->mutex); return (*value == NULL) ? FALSE : TRUE; }
/* * store a name/value pair in Redis */ static apr_byte_t oidc_cache_redis_set(request_rec *r, const char *section, const char *key, const char *value, apr_time_t expiry) { oidc_debug(r, "enter, section=\"%s\", key=\"%s\"", section, key); oidc_cfg *cfg = ap_get_module_config(r->server->module_config, &auth_openidc_module); oidc_cache_cfg_redis_t *context = (oidc_cache_cfg_redis_t *) cfg->cache_cfg; redisReply *reply = NULL; /* grab the global lock */ if (oidc_cache_mutex_lock(r, context->mutex) == FALSE) return FALSE; /* see if we should be clearing this entry */ if (value == NULL) { /* delete it */ reply = oidc_cache_redis_command(r, context, "DEL %s", oidc_cache_redis_get_key(r->pool, section, key)); if (reply == NULL) { oidc_cache_mutex_unlock(r, context->mutex); return FALSE; } freeReplyObject(reply); } else { /* calculate the timeout from now */ apr_uint32_t timeout = apr_time_sec(expiry - apr_time_now()); /* store it */ reply = oidc_cache_redis_command(r, context, "SETEX %s %d %s", oidc_cache_redis_get_key(r->pool, section, key), timeout, value); if (reply == NULL) { oidc_cache_mutex_unlock(r, context->mutex); return FALSE; } freeReplyObject(reply); } /* release the global lock */ oidc_cache_mutex_unlock(r, context->mutex); return TRUE; }
/* * get a name/value pair from Redis */ static apr_byte_t oidc_cache_redis_get(request_rec *r, const char *section, const char *key, const char **value) { oidc_debug(r, "enter, section=\"%s\", key=\"%s\"", section, key); oidc_cfg *cfg = ap_get_module_config(r->server->module_config, &auth_openidc_module); oidc_cache_cfg_redis_t *context = (oidc_cache_cfg_redis_t *) cfg->cache_cfg; redisReply *reply = NULL; /* grab the global lock */ if (oidc_cache_mutex_lock(r, context->mutex) == FALSE) return FALSE; /* get */ reply = oidc_cache_redis_command(r, context, "GET %s", oidc_cache_redis_get_key(r->pool, section, key)); if (reply == NULL) { oidc_cache_mutex_unlock(r, context->mutex); return FALSE; } /* check that we got a string back */ if (reply->type != REDIS_REPLY_STRING) { freeReplyObject(reply); /* this is a normal cache miss, so we'll return OK */ oidc_cache_mutex_unlock(r, context->mutex); return TRUE; } /* do a sanity check on the returned value */ if (reply->len != strlen(reply->str)) { oidc_error(r, "redisCommand reply->len != strlen(reply->str): '%s'", reply->str); freeReplyObject(reply); oidc_cache_mutex_unlock(r, context->mutex); return FALSE; } /* copy it in to the request memory pool */ *value = apr_pstrdup(r->pool, reply->str); freeReplyObject(reply); /* release the global lock */ oidc_cache_mutex_unlock(r, context->mutex); return TRUE; }
/* * store a value in the shared memory cache */ static apr_byte_t oidc_cache_shm_set(request_rec *r, const char *section, const char *key, const char *value, apr_time_t expiry) { oidc_debug(r, "enter, section=\"%s\", key=\"%s\", value size=%llu", section, key, value ? (unsigned long long )strlen(value) : 0); oidc_cfg *cfg = ap_get_module_config(r->server->module_config, &auth_openidc_module); oidc_cache_cfg_shm_t *context = (oidc_cache_cfg_shm_t *) cfg->cache_cfg; oidc_cache_shm_entry_t *match, *free, *lru; oidc_cache_shm_entry_t *t; apr_time_t current_time; int i; apr_time_t age; const char *section_key = oidc_cache_shm_get_key(r->pool, section, key); /* check that the passed in key is valid */ if (strlen(section_key) > OIDC_CACHE_SHM_KEY_MAX) { oidc_error(r, "could not store value since key size is too large (%s)", section_key); return FALSE; } /* check that the passed in value is valid */ if ((value != NULL) && (strlen(value) > (cfg->cache_shm_entry_size_max - sizeof(oidc_cache_shm_entry_t)))) { oidc_error(r, "could not store value since value size is too large (%llu > %lu); consider increasing OIDCCacheShmEntrySizeMax", (unsigned long long)strlen(value), (unsigned long)(cfg->cache_shm_entry_size_max - sizeof(oidc_cache_shm_entry_t))); return FALSE; } /* grab the global lock */ if (oidc_cache_mutex_lock(r, context->mutex) == FALSE) return FALSE; /* get a pointer to the shared memory block */ t = apr_shm_baseaddr_get(context->shm); /* get the current time */ current_time = apr_time_now(); /* loop over the block, looking for the key */ match = NULL; free = NULL; lru = t; for (i = 0; i < cfg->cache_shm_size_max; i++, OIDC_CACHE_SHM_ADD_OFFSET(t, cfg->cache_shm_entry_size_max)) { /* see if this slot is free */ if (t->section_key[0] == '\0') { if (free == NULL) free = t; continue; } /* see if a value already exists for this key */ if (apr_strnatcmp(t->section_key, section_key) == 0) { match = t; break; } /* see if this slot has expired */ if (t->expires <= current_time) { if (free == NULL) free = t; continue; } /* see if this slot was less recently used than the current pointer */ if (t->access < lru->access) { lru = t; } } /* if we have no free slots, issue a warning about the LRU entry */ if (match == NULL && free == NULL) { age = (current_time - lru->access) / 1000000; if (age < 3600) { oidc_warn(r, "dropping LRU entry with age = %" APR_TIME_T_FMT "s, which is less than one hour; consider increasing the shared memory caching space (which is %d now) with the (global) OIDCCacheShmMax setting.", age, cfg->cache_shm_size_max); } } /* pick the best slot: choose one with a matching key over a free slot, over a least-recently-used one */ t = match ? match : (free ? free : lru); /* see if we need to clear or set the value */ if (value != NULL) { /* fill out the entry with the provided data */ strcpy(t->section_key, section_key); strcpy(t->value, value); t->expires = expiry; t->access = current_time; } else { t->section_key[0] = '\0'; t->access = 0; } /* release the global lock */ oidc_cache_mutex_unlock(r, context->mutex); return TRUE; }