"Fossies" - the Fresh Open Source Software Archive

Member "mod_auth_dce-3.4/auth_dce/mod_auth_dce_cache.c" (10 Aug 2006, 15034 Bytes) of package /linux/www/apache_httpd_modules/old/mod_auth_dce-3.4.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file.

    1 /*
    2  * DCE Authentication Module for Apache HTTP Server
    3  *
    4  * Paul B. Henson <henson@acm.org>
    5  *
    6  * Copyright (c) 1996-2003 Paul B. Henson -- see COPYRIGHT file for details
    7  *
    8  */
    9 
   10 #ifndef NO_CACHING
   11 
   12 /* Native Solaris pthreads */
   13 #include <pthread.h>
   14 
   15 #include <sys/mman.h>
   16 #include "httpd.h"
   17 #include "http_config.h"
   18 #include "http_core.h"
   19 #include "http_log.h"
   20 #include "http_main.h"
   21 #include "http_protocol.h"
   22 #include "util_script.h"
   23 #include "ap_md5.h"
   24 #include "mod_auth_dce.h"
   25 
   26 #define SLOTS_PER_BUCKET 4
   27 
   28 typedef struct cache_entry {
   29   unsigned char key[16];
   30   unsigned long pag;
   31   unsigned int refcount;
   32   time_t last_use;
   33   time_t expiration;
   34 } cache_entry_rec;
   35 
   36 typedef struct hash_table_entry {
   37   pthread_mutex_t mutex;
   38   cache_entry_rec entries[SLOTS_PER_BUCKET];
   39 } hash_table_entry_rec;
   40 
   41 static hash_table_entry_rec *hash_table;
   42 static pthread_t cache_thread;
   43 
   44 extern server_config_rec auth_dce_server_config;
   45 
   46 static void pthread_delay_np(struct timespec *sleep_interval)
   47 {
   48   pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
   49   pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
   50   struct timespec wakeup_time = *sleep_interval;
   51   
   52   pthread_mutex_lock(&mutex);
   53   wakeup_time.tv_sec += time(NULL);
   54   pthread_cond_timedwait(&cond, &mutex, &wakeup_time);
   55 }
   56 
   57 
   58 static void cache_cleanup(void *arg)
   59 {
   60   int bucket_index;
   61   int slot_index;
   62   server_rec *s = (server_rec *)arg;
   63 
   64   DEBUG_S("auth_dce.cache_cleanup: cancelling cache cleanup thread");
   65   
   66   pthread_cancel(cache_thread);
   67 
   68   DEBUG_S("auth_dce.cache_cleanup: cleaning cache data structure");
   69   
   70   for (bucket_index = 0; bucket_index < auth_dce_server_config.cache_buckets; bucket_index++)
   71     {
   72       pthread_mutex_destroy(&hash_table[bucket_index].mutex);
   73       for (slot_index = 0; slot_index < SLOTS_PER_BUCKET; slot_index++)
   74     {
   75       if (hash_table[bucket_index].entries[slot_index].pag != 0)
   76         auth_dce_purge_context(s, hash_table[bucket_index].entries[slot_index].pag);
   77     }
   78     }
   79 
   80   DEBUG_S("auth_dce.cache_cleanup: releasing shared memory");
   81   
   82   munmap((void *)hash_table, sizeof(hash_table_entry_rec) * auth_dce_server_config.cache_buckets);
   83 }
   84 
   85 
   86 static void cache_maintain(void *arg)
   87 {
   88   int bucket_index;
   89   int slot_index;
   90   time_t now;
   91   struct timespec sweep_interval;
   92   struct timespec sleep_interval;
   93   int tries;
   94   unsigned int pag_queue[SLOTS_PER_BUCKET];
   95   int pag_index = 0;
   96   server_rec *s = (server_rec *)arg;
   97 
   98   sweep_interval.tv_sec = auth_dce_server_config.cache_sweep_interval;
   99   sweep_interval.tv_nsec = 0;
  100 
  101   sleep_interval.tv_sec = 0;
  102   sleep_interval.tv_nsec = 100000000; /* 1/10 second */
  103 
  104   DEBUG_S("auth_dce.cache_maintain: entering cache maintenance loop");
  105   
  106   while (1)
  107     {
  108 #ifdef CACHE_STATS_INTERVAL
  109       int contexts_reviewed = 0;
  110       int contexts_deleted = 0;
  111       int full_buckets = 0;
  112 #endif
  113 
  114       pthread_delay_np(&sweep_interval);
  115 
  116       DEBUG_S("auth_dce.cache_maintain: performing cache sweep");
  117 
  118       now = time(NULL);
  119       
  120       for (bucket_index = 0; bucket_index < auth_dce_server_config.cache_buckets; bucket_index++)
  121     {
  122 #ifdef CACHE_STATS_INTERVAL
  123       int full_slots = 0;
  124 #endif
  125       tries = 0;
  126       
  127       while (1)
  128         {
  129           if (!pthread_mutex_trylock(&hash_table[bucket_index].mutex))
  130         {
  131           for (slot_index = 0; slot_index < SLOTS_PER_BUCKET; slot_index++)
  132             {
  133               if (hash_table[bucket_index].entries[slot_index].pag != 0)
  134             {
  135 #ifdef CACHE_STATS_INTERVAL
  136               contexts_reviewed++;
  137               full_slots++;
  138 #endif
  139               if (((hash_table[bucket_index].entries[slot_index].expiration < now) ||
  140                    (hash_table[bucket_index].entries[slot_index].last_use + auth_dce_server_config.cache_max_idle < now)) &&
  141                   ((hash_table[bucket_index].entries[slot_index].refcount == 0) ||
  142                    (hash_table[bucket_index].entries[slot_index].expiration + auth_dce_server_config.cache_graceperiod < now)))
  143                 {
  144 #ifdef CACHE_STATS_INTERVAL
  145                   contexts_deleted++;
  146 #endif
  147                   pag_queue[pag_index++] = hash_table[bucket_index].entries[slot_index].pag;
  148                   hash_table[bucket_index].entries[slot_index].pag = 0;
  149                 }
  150             }
  151             }
  152           
  153           pthread_mutex_unlock(&hash_table[bucket_index].mutex);
  154 #ifdef CACHE_STATS_INTERVAL
  155           if (full_slots == SLOTS_PER_BUCKET) full_buckets++;
  156 #endif
  157           
  158           while (pag_index > 0)
  159             auth_dce_purge_context(s, pag_queue[--pag_index]);
  160           
  161           break;
  162         }
  163           else
  164         {
  165           if (++tries == 5)
  166             {
  167               ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, s,
  168                    "auth_dce.cache_maintain: %d failures locking mutex for bucket %d, skipping", tries, bucket_index);
  169               break;
  170             }
  171 
  172           DEBUG_S("auth_dce.cache_maintain: failed to lock mutex for bucket %d", bucket_index);
  173           pthread_delay_np(&sleep_interval);
  174         }
  175         }
  176     }
  177 #ifdef CACHE_STATS_INTERVAL
  178       ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, s,
  179            "auth_dce.cache_maintain: reviewed %d contexts, deleted %d, %d buckets full", contexts_reviewed, contexts_deleted, full_buckets);
  180 #endif
  181     }
  182 
  183 }
  184 
  185 
  186 void auth_dce_initialize_cache(server_rec *s, pool *p) {
  187 
  188   pthread_mutexattr_t mutex_attr;
  189   int fd;
  190   int bucket_index;
  191 
  192   DEBUG_S("auth_dce.initialize_cache: initializing shared memory for cache");
  193   
  194   if ((fd = open("/dev/zero", O_RDWR)) == -1)
  195     {
  196       ap_log_error(APLOG_MARK, APLOG_EMERG, s,
  197            "auth_dce.initialize_cache: failed to open /dev/zero");
  198       exit(1);
  199     }
  200   
  201   hash_table = (hash_table_entry_rec *) mmap((caddr_t) 0, sizeof(hash_table_entry_rec) * auth_dce_server_config.cache_buckets,
  202                       PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
  203   
  204   if (hash_table == (void *) (caddr_t) - 1)
  205     {
  206       ap_log_error(APLOG_MARK, APLOG_EMERG, s,
  207            "auth_dce.initialize_cache: mmap failed");
  208       exit(1);
  209     }
  210   
  211   close(fd);
  212 
  213   memset(hash_table, 0, sizeof(hash_table_entry_rec) * auth_dce_server_config.cache_buckets);
  214   
  215   if ((errno = pthread_mutexattr_init(&mutex_attr)))
  216     {
  217       ap_log_error(APLOG_MARK, APLOG_EMERG, s,
  218            "auth_dce.initialize_cache: pthread_mutexattr_init failed");
  219       exit(1);
  220     }
  221   
  222   if ((errno = pthread_mutexattr_setpshared(&mutex_attr, PTHREAD_PROCESS_SHARED)))
  223     {
  224       ap_log_error(APLOG_MARK, APLOG_EMERG, s,
  225            "auth_dce.initialize_cache: pthread_mutexattr_setpshared failed");
  226       exit(1);
  227     }
  228 
  229   for (bucket_index = 0; bucket_index < auth_dce_server_config.cache_buckets; bucket_index++)
  230     if ((errno = pthread_mutex_init(&hash_table[bucket_index].mutex, &mutex_attr)))
  231       {
  232     ap_log_error(APLOG_MARK, APLOG_EMERG, s,
  233              "auth_dce.initialize_cache: pthread_mutex_init failed");
  234     exit(1);
  235       }
  236   
  237   if (pthread_create(&cache_thread, NULL, (void *)cache_maintain, (void *)s))
  238     {
  239       ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_EMERG, s,
  240            "auth_dce.initialize_cache: cache maintenance pthread create failed");
  241       exit(1);
  242     }
  243 
  244   DEBUG_S("auth_dce.initialize_cache: registering cache cleanup");
  245   ap_register_cleanup(p, NULL, cache_cleanup, ap_null_cleanup);
  246 }
  247 
  248 
  249 void auth_dce_find_cached_context(request_rec *r, request_config_rec *request_config, char *username, char *password) {
  250 
  251   AP_MD5_CTX md5_context;
  252   time_t now = time(NULL);
  253   struct timespec sleep_interval;
  254   int tries = 0;
  255   int slot_index;
  256 
  257 #ifdef CACHE_STATS_INTERVAL
  258   static unsigned int cache_accesses = 0;
  259   static unsigned int cache_hits = 0;
  260   static unsigned int total_cache_accesses = 0;
  261   static unsigned int total_cache_hits = 0;
  262 #endif
  263   
  264 #ifdef CACHE_TEST_LEVEL
  265   int username_len = strlen(username);
  266   int index;
  267   char *testname = ap_palloc(r->pool, username_len + CACHE_TEST_LEVEL + 1);
  268 
  269   strcpy(testname, username);
  270 
  271   for (index = username_len; index < username_len + CACHE_TEST_LEVEL; index++)
  272     testname[index] = 'A' + (lrand48() % 10);
  273 
  274   testname[index] = '\0';
  275 
  276 #define username testname
  277 #endif
  278 
  279 #ifdef CACHE_STATS_INTERVAL
  280   if (cache_accesses == CACHE_STATS_INTERVAL) {
  281     total_cache_accesses += cache_accesses;
  282     total_cache_hits += cache_hits;
  283 
  284     ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, r,
  285           "auth_dce.find_cached_context: %d hits / %d accesses (%0.2f%%), %d hits / %d accesses total (%0.2f%%)",
  286           cache_hits, cache_accesses, (float)cache_hits/(float)cache_accesses,
  287           total_cache_hits, total_cache_accesses, (float)total_cache_hits/(float)total_cache_accesses);
  288 
  289     cache_hits = cache_accesses = 0;
  290   }
  291   cache_accesses++;
  292 #endif
  293   
  294   DEBUG_R("auth_dce.find_cached_context: looking for username %s", username);
  295   
  296   ap_MD5Init(&md5_context);
  297   ap_MD5Update(&md5_context, (const unsigned char *)username, strlen(username));
  298   ap_MD5Update(&md5_context, (const unsigned char *)password, strlen(password));
  299   ap_MD5Final(request_config->hash_key, &md5_context);
  300 
  301   memcpy(&request_config->hash_index, request_config->hash_key, sizeof(request_config->hash_index));
  302   request_config->hash_index %= auth_dce_server_config.cache_buckets;
  303 
  304   DEBUG_R("auth_dce.find_cached_context: hash index set to %d", request_config->hash_index);
  305   
  306   sleep_interval.tv_sec = 0;
  307   sleep_interval.tv_nsec = 100000000; /* 1/10 second */
  308 
  309   while (1)
  310     {
  311       if (!pthread_mutex_trylock(&hash_table[request_config->hash_index].mutex))
  312     {
  313       for (slot_index = 0; slot_index < SLOTS_PER_BUCKET; slot_index++)
  314         {
  315           if (memcmp(hash_table[request_config->hash_index].entries[slot_index].key, request_config->hash_key, 16) == 0)
  316         {
  317           DEBUG_R("auth_dce.find_cached_context: found candidate context in slot %d", slot_index);
  318           
  319           if (hash_table[request_config->hash_index].entries[slot_index].expiration > now)
  320             {
  321               request_config->pag = hash_table[request_config->hash_index].entries[slot_index].pag;
  322               hash_table[request_config->hash_index].entries[slot_index].refcount++;
  323               hash_table[request_config->hash_index].entries[slot_index].last_use = now;
  324 
  325               pthread_mutex_unlock(&hash_table[request_config->hash_index].mutex);
  326               
  327               DEBUG_R("auth_dce.find_cached_context: candidate context acceptable, using pag %08x", request_config->pag);
  328 
  329 #ifdef CACHE_STATS_INTERVAL
  330               cache_hits++;
  331 #endif
  332               
  333               return;
  334             }
  335         }
  336         }
  337       
  338       pthread_mutex_unlock(&hash_table[request_config->hash_index].mutex);
  339 
  340       DEBUG_R("auth_dce.find_cached_context: no acceptable contexts found for username %s", username);
  341       
  342       return;
  343     }
  344       
  345       if (++tries == 5)
  346     {
  347       ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
  348             "auth_dce.find_cached_context: %d failures locking bucket %d, giving up", tries, request_config->hash_index);
  349       
  350       break;
  351     }
  352 
  353       DEBUG_R("auth_dce.find_cached_context: failed to lock mutex for bucket %d", request_config->hash_index);
  354       
  355       pthread_delay_np(&sleep_interval);
  356     }
  357 #ifdef CACHE_TEST_LEVEL
  358 #undef username
  359 #endif
  360 }
  361 
  362 void auth_dce_add_cached_context(request_rec *r, request_config_rec *request_config) {
  363   time_t now = time(NULL);
  364   int slot_index;
  365   struct timespec sleep_interval;
  366   int tries = 0;
  367 
  368   sleep_interval.tv_sec = 0;
  369   sleep_interval.tv_nsec = 100000000; /* 1/10 second */
  370     
  371   DEBUG_R("auth_dce.add_cached_context: attempting to add context for pag %08x", request_config->pag);
  372   
  373   while (1)
  374     {
  375       if (!pthread_mutex_trylock(&hash_table[request_config->hash_index].mutex))
  376     {
  377       DEBUG_R("auth_dce.add_cached_context: looking for empty slot in bucket %d", request_config->hash_index);
  378       
  379       for (slot_index = 0; slot_index < SLOTS_PER_BUCKET; slot_index++)
  380         {
  381           if (hash_table[request_config->hash_index].entries[slot_index].pag == 0)
  382         {         
  383           memcpy(hash_table[request_config->hash_index].entries[slot_index].key, request_config->hash_key, 16);
  384           hash_table[request_config->hash_index].entries[slot_index].pag = request_config->pag;
  385           hash_table[request_config->hash_index].entries[slot_index].refcount = 1;
  386           hash_table[request_config->hash_index].entries[slot_index].last_use = now;
  387           hash_table[request_config->hash_index].entries[slot_index].expiration = now + auth_dce_server_config.cache_lifetime;
  388 
  389           pthread_mutex_unlock(&hash_table[request_config->hash_index].mutex);
  390 
  391           DEBUG_R("auth_dce.add_cached_context: successfully stored context in slot %d", slot_index);
  392           
  393           return;
  394         }
  395         }
  396 
  397       pthread_mutex_unlock(&hash_table[request_config->hash_index].mutex);
  398 
  399       ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, r,
  400             "auth_dce.add_cached_context: no empty slots found in bucket %d, marking context for purging", request_config->hash_index);
  401       
  402       memset(request_config->hash_key, 0, 16);
  403       return;
  404     }
  405 
  406       if (++tries == 5)
  407     {
  408       ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
  409             "auth_dce.add_cached_context: %d failures locking bucket %d, giving up", tries, request_config->hash_index);
  410       
  411       break;
  412     }
  413 
  414       DEBUG_R("auth_dce.add_cached_context: failed to lock mutex for bucket %d", request_config->hash_index);
  415 
  416       pthread_delay_np(&sleep_interval);
  417     }
  418 
  419   DEBUG_R("auth_dce.add_cached_context: failed to add context, marking for purging");
  420   
  421   memset(request_config->hash_key, 0, 16);
  422 }
  423 
  424 void auth_dce_release_cached_context(request_rec *r, request_config_rec *request_config) {
  425   int slot_index;
  426   struct timespec sleep_interval;
  427   int tries = 0;
  428 
  429   sleep_interval.tv_sec = 0;
  430   sleep_interval.tv_nsec = 100000000; /* 1/10 second */
  431 
  432   DEBUG_R("auth_dce.release_cached_context: trying to release pag %08x", request_config->pag);
  433 
  434   while (1)
  435     {
  436       if (!pthread_mutex_trylock(&hash_table[request_config->hash_index].mutex))
  437     {
  438       DEBUG_R("auth_dce.release_cached_context: looking for pag %08x in bucket %d", request_config->pag, request_config->hash_index);
  439       
  440       for (slot_index = 0; slot_index < SLOTS_PER_BUCKET; slot_index++)
  441         {
  442           if ((memcmp(hash_table[request_config->hash_index].entries[slot_index].key, request_config->hash_key, 16) == 0) &&
  443           (hash_table[request_config->hash_index].entries[slot_index].pag == request_config->pag))      
  444         {
  445           hash_table[request_config->hash_index].entries[slot_index].refcount--;
  446 
  447           pthread_mutex_unlock(&hash_table[request_config->hash_index].mutex);
  448 
  449           DEBUG_R("auth_dce.release_cached_context: successfully released context for %08x", request_config->pag);
  450           
  451           return;
  452         }
  453         }
  454 
  455       pthread_mutex_unlock(&hash_table[request_config->hash_index].mutex);
  456 
  457       ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, r,
  458             "auth_dce.release_cached_context: context for pag %08x not found in bucket %d", request_config->pag, request_config->hash_index);
  459       
  460       return;
  461     }
  462 
  463       if (++tries == 5)
  464     {
  465       ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
  466             "auth_dce.release_cached_context: %d failures locking bucket %d, giving up", tries, request_config->hash_index);
  467       
  468       break;
  469     }
  470       
  471       DEBUG_R("auth_dce.release_cached_context: failed to lock mutex for bucket %d", request_config->hash_index);
  472           
  473       pthread_delay_np(&sleep_interval);
  474     }
  475 
  476   ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, r,
  477         "auth_dce.release_cached_context: possible bad reference count for pag %08x in bucket %d", request_config->pag, request_config->hash_index);
  478 }
  479 
  480 #else
  481 void auth_dce_dummy() {}
  482 #endif