"Fossies" - the Fresh Open Source Software Archive 
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