Merge sourceforge mod_waklog
[hcoop/debian/libapache-mod-waklog.git] / mod_waklog.c
1 #define _LARGEFILE64_SOURCE
2 #define _GNU_SOURCE
3
4 #include "httpd.h"
5 #include "http_config.h"
6 #include "http_log.h"
7 #include "http_protocol.h"
8 #include "http_request.h"
9 #include "http_core.h"
10
11 #ifdef sun
12 #include <synch.h>
13 #include <stropts.h>
14 #include <sys/ioccom.h>
15 #elif linux
16 #define use_pthreads
17 #include <features.h>
18 #include <sys/types.h>
19 #include <sys/mman.h>
20 #include <pthread.h>
21 #else
22 #error "make sure you include the right stuff here"
23 #endif
24
25 #ifndef MAXNAMELEN
26 #define MAXNAMELEN 1024
27 #endif
28
29 #ifdef STANDARD20_MODULE_STUFF
30 #define APACHE2
31 #endif
32
33 /********************* APACHE1 ******************************************************************************/
34 #ifndef APACHE2
35 #include "ap_config.h"
36 #include <http_conf_globals.h>
37 #define MK_POOL pool
38 #define MK_TABLE_GET ap_table_get
39 #define MK_TABLE_SET ap_table_set
40 #define command(name, func, var, type, usage) \
41 { name, func, \
42 NULL , \
43 RSRC_CONF | ACCESS_CONF , type, usage }
44 module waklog_module;
45
46 /********************* APACHE2 ******************************************************************************/
47 #else
48 #include "http_connection.h"
49 #include <apr_strings.h>
50 #include <apr_base64.h>
51 #include <apr_time.h>
52 #define ap_pcalloc apr_pcalloc
53 #define ap_pdupstr apr_pdupstr
54 #define ap_pstrdup apr_pstrdup
55 #define MK_POOL apr_pool_t
56 #define MK_TABLE_GET apr_table_get
57 #define MK_TABLE_SET apr_table_set
58 #define command(name, func, var, type, usage) \
59 AP_INIT_ ## type (name, (void*) func, \
60 NULL, \
61 RSRC_CONF | ACCESS_CONF, usage)
62 module AP_MODULE_DECLARE_DATA waklog_module;
63 typedef struct { int dummy; } child_info;
64 const char *userdata_key = "waklog_init";
65
66 /* Apache 2.4 */
67 #ifdef APLOG_USE_MODULE
68 APLOG_USE_MODULE(waklog);
69 #endif
70
71 #endif /* APACHE2 */
72 /**************************************************************************************************/
73
74 #include <krb5.h>
75 #include <kopenafs.h>
76
77 #include <afs/param.h>
78
79 #include <afs/venus.h>
80 #include <afs/auth.h>
81 #include <afs/dirpath.h>
82 #include <afs/ptuser.h>
83 #include <afs/com_err.h>
84 #include <rx/rxkad.h>
85
86 #define TKT_LIFE ( 12 * 60 * 60 )
87 #define SLEEP_TIME ( TKT_LIFE - 5*60 )
88
89 #define WAKLOG_UNSET -1
90
91 #ifdef WAKLOG_DEBUG
92 #undef APLOG_DEBUG
93 #define APLOG_DEBUG APLOG_ERR
94 #endif
95
96 /* this is used to turn off pag generation for the backround worker child during startup */
97 int pag_for_children = 1;
98
99 typedef struct
100 {
101 int forked;
102 int configured;
103 int protect;
104 int usertokens;
105 int cell_in_principal;
106 int disable_token_cache;
107 char *keytab;
108 char *principal;
109 char *default_principal;
110 char *default_keytab;
111 char *afs_cell;
112 char *afs_cell_realm;
113 char *path;
114 MK_POOL *p;
115 }
116 waklog_config;
117
118 typedef struct
119 {
120 struct ktc_token token;
121 char clientprincipal[MAXNAMELEN];
122 krb5_context kcontext;
123 krb5_ccache ccache;
124 struct ktc_principal server;
125 struct ktc_principal client;
126 int pr_init;
127 } waklog_child_config;
128
129 waklog_child_config child;
130
131 struct tokencache_ent {
132 char clientprincipal[MAXNAMELEN];
133 struct ktc_token token;
134 struct ktc_principal client;
135 struct ktc_principal server;
136 time_t lastused;
137 int persist;
138 };
139
140 #define SHARED_TABLE_SIZE 512
141
142 struct sharedspace_s {
143 int renewcount;
144 struct tokencache_ent sharedtokens[SHARED_TABLE_SIZE];
145 };
146
147 struct sharedspace_s *sharedspace = NULL;
148
149 struct renew_ent {
150 char *keytab;
151 char *principal;
152 int lastrenewed;
153 };
154
155 #ifdef use_pthreads
156 pthread_rwlock_t *sharedlock = NULL;
157 #else
158 rwlock_t *sharedlock = NULL;
159 #endif
160
161 struct renew_ent renewtable[SHARED_TABLE_SIZE];
162
163 int renewcount = 0;
164
165
166
167 #define getModConfig(P, X) P = (waklog_config *) ap_get_module_config( (X)->module_config, &waklog_module );
168
169
170 #ifdef APLOG_USE_MODULE
171 static void
172 log_error (const char *file, int line, int module_index, int level, int status,
173 const server_rec * s, const char *fmt, ...)
174 #else
175 static void
176 log_error (const char *file, int line, int level, int status,
177 const server_rec * s, const char *fmt, ...)
178 #endif
179 {
180 char errstr[4096];
181 va_list ap;
182
183 va_start (ap, fmt);
184 vsnprintf (errstr, 1024, fmt, ap);
185 va_end (ap);
186
187 #ifdef APACHE2
188 #ifdef APLOG_USE_MODULE
189 /* Apache 2.4 */
190 ap_log_error (file, line, module_index, level | APLOG_NOERRNO, status, s, "%s", errstr);
191 #else
192 ap_log_error (file, line, level | APLOG_NOERRNO, status, s, "(%d) %s", getpid(), errstr);
193 #endif
194 #else
195 ap_log_error (file, line, level | APLOG_NOERRNO, s, "(%d) %s", getpid(), errstr);
196 #endif
197
198 }
199
200 waklog_config *retrieve_config(request_rec *r) {
201
202 request_rec *my;
203 waklog_config *cfg;
204
205 if ( r && r->main ) {
206 my = r->main;
207 } else if (r) {
208 my = r;
209 } else {
210 return NULL;
211 }
212
213 if ( my && ( cfg = (waklog_config *) ap_get_module_config(my->per_dir_config, &waklog_module ) ) ) {
214 return cfg;
215 } else {
216 getModConfig (cfg, r->server);
217 }
218
219 return cfg;
220
221 }
222
223 /* set_auth -- sets the tokens of the current process to this user.
224 if "self" is set, it divines the user from the current requests' environment.
225 otherwise, it's gettng it from principal/keytab */
226
227 int
228 set_auth ( server_rec *s, request_rec *r, int self, char *principal, char *keytab, int storeonly ) {
229
230 int i;
231 int usecached = 0;
232 krb5_error_code kerror = 0;
233 krb5_principal kprinc = NULL;
234 krb5_get_init_creds_opt kopts;
235 krb5_creds v5creds;
236 krb5_creds increds;
237 krb5_ccache clientccache;
238 struct ktc_principal server = { "afs", "", "" };
239 struct ktc_principal client;
240 struct ktc_token token;
241 krb5_creds *v5credsp = NULL;
242 krb5_keytab krb5kt = NULL;
243 char buf[MAXNAMELEN];
244 waklog_config *cfg;
245 int rc = 0;
246 int buflen = 0;
247 time_t oldest_time = 0;
248 int oldest = 0;
249 int stored = -1;
250 time_t mytime;
251 int indentical;
252 int cell_in_principal;
253 int attempt;
254 int use_client_credentials = 0;
255
256 char k5user[MAXNAMELEN] = "";
257 char *k5secret = NULL;
258
259 char *k5path = NULL;
260
261 memset((char *) &increds, 0, sizeof(increds));
262 /* init some stuff if it ain't already */
263 /* XXX - In what situation will these not be initialized? */
264
265 if ( ! child.kcontext ) {
266 if ((kerror = krb5_init_context(&child.kcontext))) {
267 log_error(APLOG_MARK, APLOG_ERR, 0, s, "mod_waklog: can't initialize Kerberos context err=%d",
268 kerror);
269 return(-1);
270 }
271 }
272
273 if ( !child.ccache) {
274 if ((kerror = krb5_cc_resolve(child.kcontext, "MEMORY:tmpcache", &child.ccache))) {
275 log_error(APLOG_MARK, APLOG_ERR, 0, s, "mod_waklog: can't initialize credentials cache %s err=%d",
276 k5path, kerror );
277 return(-1);
278 }
279 }
280
281 log_error(APLOG_MARK, APLOG_DEBUG, 0, s, "mod_waklog: set_auth: %d, %s, %s, %d, KRB5CC=%s user=%s",
282 self, principal ? principal : "NULL",
283 keytab ? keytab : "NULL",
284 storeonly,
285 k5path ? k5path : "NULL",
286 #ifdef APACHE2
287 (r && r->user) ? r->user : "NULL"
288 #else
289 (r && r->connection && r->connection->user) ? r->connection->user : "NULL"
290 #endif
291 );
292
293 /* pull the server config record that we care about... */
294
295 if ( r ) {
296 cfg = retrieve_config(r);
297 } else {
298 log_error (APLOG_MARK, APLOG_DEBUG, 0, s,
299 "mod_waklog: set_auth using no config" );
300 getModConfig (cfg, s);
301 }
302
303 if ( ! cfg ) {
304 log_error(APLOG_MARK, APLOG_DEBUG, 0, s, "mod_waklog: cfg is %d", cfg );
305 }
306
307
308 if ( self ) {
309 /* pull out our principal name and stuff from the environment -- webauth better have sent
310 through. */
311
312 #ifdef APACHE2
313 if ( ! ( r && r->connection && r->user )) {
314 log_error(APLOG_MARK, APLOG_ERR, 0, s, "mod_waklog: self authentication selected, but no data available");
315 log_error(APLOG_MARK, APLOG_ERR, 0, s, "mod_waklog: r->user=%s", (r->user==NULL ? "null" : r->user));
316 return -1;
317 }
318
319 strncpy(k5user, r->user, sizeof(k5user));
320 #else
321 if ( ! (r && r->connection && r->connection->user)) {
322 log_error(APLOG_MARK, APLOG_ERR, 0, s, "mod_waklog: self authentication selected, but no username available");
323 return -1;
324 }
325
326 strncpy(k5user, r->connection->user, sizeof(k5user));
327 #endif
328 /* if they've supplied a credentials cache */
329 k5path = (char *) MK_TABLE_GET( r->subprocess_env, "KRB5CCNAME" );
330
331 /* the other thing we need is someone's password */
332 k5secret = (char *) MK_TABLE_GET( r->notes, "ATTR_PASSWORD" );
333
334 /* we'll pick this up later after we've checked the cache and current state */
335
336 } else
337 if ( principal ) {
338 strncpy(k5user, principal, sizeof(k5user));
339 } else
340 #ifdef APACHE2
341 if (r && r->user) {
342 strncpy(k5user, r->user, sizeof(k5user));
343 }
344 #else
345 if (r && r->connection && r->connection->user) {
346 strncpy(k5user, r->connection->user, sizeof(k5user));
347 }
348 #endif
349
350 log_error(APLOG_MARK, APLOG_DEBUG, 0, s, "mod_waklog: set_auth: k5user=%s", k5user);
351 mytime = time(0);
352
353 /* see if we should just go ahead and ignore this call, since we already should be set to these
354 credentials */
355
356 if ( ! storeonly ) {
357
358 #ifdef use_pthreads
359 pthread_rwlock_rdlock( sharedlock );
360 #else
361 rw_rdlock( sharedlock );
362 #endif
363
364 for ( i = 0; i < SHARED_TABLE_SIZE; ++i ) {
365
366 /* if it's a token for the principal we're looking for, and it hasn't expired yet */
367
368 if ( ( !strcmp( k5user,
369 sharedspace->sharedtokens[i].clientprincipal ) ) &&
370 ( sharedspace->sharedtokens[i].token.endTime > mytime ) ) {
371
372 if ( ! memcmp(&child.token, &sharedspace->sharedtokens[i].token, sizeof(child.token) ) ) {
373 indentical = 1;
374 } else {
375 indentical = 0;
376 }
377
378 /* copy the token out of the cache and into the child object */
379
380 strcpy(child.clientprincipal, sharedspace->sharedtokens[i].clientprincipal );
381 memcpy(&child.token, &sharedspace->sharedtokens[i].token, sizeof(child.token));
382 memcpy(&child.server, &sharedspace->sharedtokens[i].server, sizeof(child.server));
383 memcpy(&child.client, &sharedspace->sharedtokens[i].client, sizeof(child.client));
384
385 /* set our last used time thing */
386 sharedspace->sharedtokens[i].lastused = mytime;
387
388 usecached = 1;
389
390 break;
391
392 }
393
394 }
395
396 /* release the lock on the token cache */
397 #ifdef use_pthreads
398 pthread_rwlock_unlock( sharedlock );
399 #else
400 rw_unlock( sharedlock );
401 #endif
402
403 if ( usecached ) {
404 /* release the lock on the token cache */
405 log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
406 "mod_waklog: set_auth using shared token %d for %s", i, k5user );
407
408 }
409
410 /* if this is something that was in the cache, and it's the same as the token we already have stored,
411 and we weren't calling this just to renew it... */
412
413 if ( usecached && indentical ) {
414 log_error (APLOG_MARK, APLOG_DEBUG, 0, s, "mod_waklog: token is identical for %s", k5user );
415 return 0;
416 }
417
418 }
419
420 /* if 'usecached' isn't set, we've got to get our tokens from somewhere... */
421 if ( ! usecached ) {
422
423 /* clear out the creds structure */
424 memset((void *) &v5creds, 0, sizeof(v5creds));
425
426 /* create a principal out of our k5user string */
427
428 if ( ( kerror = krb5_parse_name (child.kcontext, k5user, &kprinc ) ) ) {
429 log_error(APLOG_MARK, APLOG_ERR, 0, s, "mod_waklog: krb5_parse_name %s", (char *) afs_error_message(kerror) );
430 goto cleanup;
431 }
432
433 /* create the credentials options */
434
435 krb5_get_init_creds_opt_init ( &kopts );
436 krb5_get_init_creds_opt_set_tkt_life ( &kopts, TKT_LIFE );
437 krb5_get_init_creds_opt_set_renew_life ( &kopts, 0 );
438 krb5_get_init_creds_opt_set_forwardable ( &kopts, 0 );
439 krb5_get_init_creds_opt_set_proxiable ( &kopts, 0 );
440
441 if ( keytab || k5secret ) {
442
443 if (keytab) {
444 /* if we've been passed a keytab, we're going to be getting our credentials from it */
445
446 log_error( APLOG_MARK, APLOG_DEBUG, 0, s, "mod_waklog: using keytab %s", keytab);
447
448 if ( ( kerror = krb5_kt_resolve(child.kcontext, keytab, &krb5kt ) ) ) {
449 log_error( APLOG_MARK, APLOG_ERR, 0, s,
450 "mod_waklog: krb5_kt_resolve %s", afs_error_message(kerror) );
451 goto cleanup;
452 }
453
454 if ((kerror = krb5_get_init_creds_keytab (child.kcontext, &v5creds,
455 kprinc, krb5kt, 0, NULL, &kopts ) ) ) {
456 log_error(APLOG_MARK, APLOG_ERR, 0, s, "mod_waklog: krb5_get_init_creds_keytab %s",
457 afs_error_message(kerror) );
458 goto cleanup;
459 }
460 } else if (k5secret) {
461
462 /* If the WebSSO is lame enough to provide a secret, then try and use that to get a token */
463
464 if ((kerror = krb5_get_init_creds_password ( child.kcontext, &v5creds,
465 kprinc, k5secret, NULL, NULL, 0, NULL, &kopts ) ) ) {
466 log_error(APLOG_MARK, APLOG_ERR, 0, s, "mod_waklog: krb5_get_init_creds_password %s",
467 afs_error_message(kerror) );
468 /* nuke the password so it doesn't end up in core files */
469 memset(k5secret, 0, strlen(k5secret));
470 goto cleanup;
471 }
472
473 memset(k5secret, 0, strlen(k5secret));
474 }
475
476 /* initialize the credentials cache and store the stuff we just got */
477 if ( ( kerror = krb5_cc_initialize (child.kcontext, child.ccache, kprinc) ) ) {
478 log_error(APLOG_MARK, APLOG_ERR, 0, s, "mod_waklog: init credentials cache %s",
479 afs_error_message(kerror));
480 goto cleanup;
481 }
482
483 if ( ( kerror = krb5_cc_store_cred(child.kcontext, child.ccache, &v5creds) ) ) {
484 log_error(APLOG_MARK, APLOG_ERR, 0, s, "mod_waklog: cannot store credentials %s",
485 afs_error_message(kerror));
486 goto cleanup;
487 }
488
489 krb5_free_cred_contents(child.kcontext, &v5creds);
490
491 if ( kerror ) {
492 log_error(APLOG_MARK, APLOG_ERR, 0, s, "mod_waklog: store cred %s", afs_error_message(kerror));
493 goto cleanup;
494 }
495
496 log_error(APLOG_MARK, APLOG_DEBUG, 0, s, "mod_waklog: kinit ok for %s", k5user );
497
498 } else if (k5path) {
499 /* If we've got a path to a credentials cache, then try and use that. We can't just
500 * replace child.creds, because we want to ensure that only this request gets access to
501 * that cache */
502
503 if ( ( kerror = krb5_cc_resolve(child.kcontext, k5path, &clientccache ) ) ) {
504 log_error(APLOG_MARK, APLOG_ERR, 0, s,
505 "mod_waklog: can't open provided credentials cache %s err=%d",
506 k5path, kerror );
507 goto cleanup;
508 }
509
510 use_client_credentials = 1;
511 }
512
513 /* now, to the 'aklog' portion of our program. */
514
515 /** we make two attempts here, one for afs@REALM and one for afs/cell@REALM */
516 for(attempt = 0; attempt <= 1; attempt++) {
517 strncpy( buf, "afs", sizeof(buf) - 1 );
518 cell_in_principal = (cfg->cell_in_principal + attempt) % 2;
519
520 log_error(APLOG_MARK, APLOG_DEBUG, 0, s, "mod_waklog: cell_in_principal=%d", cell_in_principal );
521 if (cell_in_principal) {
522 strncat(buf, "/", sizeof(buf) - strlen(buf) - 1);
523 strncat(buf, cfg->afs_cell, sizeof(buf) - strlen(buf) - 1);
524 }
525 if (cfg->afs_cell_realm != NULL) {
526 strncat(buf, "@", sizeof(buf) - strlen(buf) - 1);
527 strncat(buf, cfg->afs_cell_realm, sizeof(buf) - strlen(buf) - 1);
528 }
529
530 log_error(APLOG_MARK, APLOG_DEBUG, 0, s, "mod_waklog: using AFS principal: %s", buf);
531
532 if ((kerror = krb5_parse_name (child.kcontext, buf, &increds.server))) {
533 log_error(APLOG_MARK, APLOG_ERR, 0, s, "mod_waklog: krb5_parse name %s", afs_error_message(kerror));
534 goto cleanup;
535 }
536
537 if (!use_client_credentials) {
538 clientccache = child.ccache;
539 }
540
541 if ((kerror = krb5_cc_get_principal(child.kcontext, clientccache, &increds.client))) {
542 log_error(APLOG_MARK, APLOG_ERR, 0, s, "mod_waklog: krb5_cc_get_princ %s %p", afs_error_message(kerror), clientccache);
543 goto cleanup;
544 }
545
546 log_error(APLOG_MARK, APLOG_DEBUG, 0, s, "mod_waklog: retrieved data from ccache for %s", k5user);
547
548 increds.times.endtime = 0;
549
550 if ( ( kerror = krb5_get_credentials (child.kcontext, 0, clientccache, &increds, &v5credsp ) ) ) {
551 /* only complain once we've tried both afs@REALM and afs/cell@REALM */
552 if (attempt>=1) {
553 log_error(APLOG_MARK, APLOG_ERR, 0, s, "mod_waklog: krb5_get_credentials: %s",
554 afs_error_message(kerror));
555 goto cleanup;
556 } else {
557 continue;
558 }
559 }
560 cfg->cell_in_principal = cell_in_principal;
561 break;
562 }
563
564 log_error(APLOG_MARK, APLOG_DEBUG, 0, s, "mod_waklog: get_credentials passed for %s", k5user);
565
566 if ( v5credsp->ticket.length >= MAXKTCTICKETLEN ) {
567 log_error(APLOG_MARK, APLOG_ERR, 0, s, "mod_waklog: ticket size (%d) too big to fake",
568 v5credsp->ticket.length);
569 goto cleanup;
570 }
571
572 memset(&token, 0, sizeof(struct ktc_token));
573
574 token.startTime = v5credsp->times.starttime ? v5credsp->times.starttime : v5credsp->times.authtime;
575 token.endTime = v5credsp->times.endtime;
576
577 if (tkt_DeriveDesKey(v5credsp->keyblock.enctype, v5credsp->keyblock.contents,
578 v5credsp->keyblock.length, &token.sessionKey) != 0) {
579 log_error(APLOG_MARK, APLOG_ERR, 0, s, "mod_waklog: tkt_DeriveDesKey failure (enctype: %d)",
580 v5credsp->keyblock.enctype);
581 goto cleanup;
582 }
583 token.kvno = RXKAD_TKT_TYPE_KERBEROS_V5;
584 token.ticketLen = v5credsp->ticket.length;
585 memmove( token.ticket, v5credsp->ticket.data, token.ticketLen);
586
587 /* build the name */
588
589 memmove( buf, v5credsp->client->data[0].data, min(v5credsp->client->data[0].length,
590 MAXKTCNAMELEN -1 ));
591 buf[v5credsp->client->data[0].length] = '\0';
592 if ( v5credsp->client->length > 1 ) {
593 strncat(buf, ".", sizeof(buf) - strlen(buf) - 1);
594 buflen = strlen(buf);
595 memmove(buf + buflen, v5credsp->client->data[1].data,
596 min(v5credsp->client->data[1].length,
597 MAXKTCNAMELEN - strlen(buf) - 1));
598 buf[buflen + v5credsp->client->data[1].length] = '\0';
599 }
600
601 /* assemble the client */
602 strncpy(client.name, buf, sizeof(client.name) - 1 );
603 strncpy(client.instance, "", sizeof(client.instance) - 1 );
604 memmove(buf, v5credsp->client->realm.data, min(v5credsp->client->realm.length,
605 MAXKTCNAMELEN - 1));
606 buf[v5credsp->client->realm.length] = '\0';
607 strncpy(client.cell, buf, sizeof(client.cell));
608
609 /* assemble the server's cell */
610 strncpy(server.cell, cfg->afs_cell, sizeof(server.cell) - 1);
611
612 log_error(APLOG_MARK, APLOG_DEBUG, 0, s, "mod_waklog: preparing to init PTS connection for %s", server.cell);
613
614 /* fill out the AFS ID in the client name */
615 /* we've done a pr_Initialize in the child_init -- once, per process. If you try to do it more
616 * strange things seem to happen. */
617
618 {
619 afs_int32 viceId = 0;
620
621 log_error(APLOG_MARK, APLOG_DEBUG, 0, s, "mod_waklog: making PTS call to look up %s", client.name);
622
623 if ( ( rc = pr_SNameToId( client.name, &viceId ) ) == 0 ) {
624 snprintf( client.name, sizeof(client.name), "AFS ID %d", viceId );
625 } else {
626 log_error(APLOG_MARK, APLOG_DEBUG, 0, s, "mod_waklog: PTS call returned error %d ", rc);
627 }
628
629 log_error(APLOG_MARK, APLOG_DEBUG, 0, s, "mod_waklog: PTS call returned %s ", client.name);
630
631 }
632
633 log_error(APLOG_MARK, APLOG_DEBUG, 0, s, "mod_waklog: server: name %s, instance %s, cell %s",
634 server.name, server.instance, server.cell );
635
636 log_error(APLOG_MARK, APLOG_DEBUG, 0, s, "mod_waklog: client: name %s, instance %s, cell %s",
637 client.name, client.instance, client.cell );
638
639 /* copy the resulting stuff into the child structure */
640
641 strncpy(child.clientprincipal, k5user, sizeof(child.clientprincipal));
642 memcpy(&child.token, &token, sizeof(child.token));
643 memcpy(&child.server, &server, sizeof(child.server));
644 memcpy(&child.client, &client, sizeof(child.client));
645
646 /* stuff the resulting token-related stuff into our shared token cache */
647 /* note, that anything that was set from a keytab is "persistant", and is immune
648 * from LRU-aging. This is because nothing but the process that's running as root
649 * can update these, and it's running every hour or so and renewing these tokens.
650 * and we don't want them aged out.
651 */
652
653 mytime = oldest_time = time(0);
654
655 log_error(APLOG_MARK, APLOG_DEBUG, 0, s, "mod_waklog: waiting for shared space for %s ", k5user);
656
657 #ifdef use_pthreads
658 pthread_rwlock_wrlock(sharedlock);
659 #else
660 rw_wrlock(sharedlock);
661 #endif
662
663 for( i = ( SHARED_TABLE_SIZE - 1 ); i >= 0; i-- ) {
664 if ( ( sharedspace->sharedtokens[i].lastused <= oldest_time) &&
665 ( sharedspace->sharedtokens[i].persist == 0 ) ) {
666 oldest = i;
667 oldest_time = sharedspace->sharedtokens[i].lastused;
668 }
669 if ( ! strcmp ( sharedspace->sharedtokens[i].clientprincipal,
670 child.clientprincipal ) ) {
671 memcpy(&sharedspace->sharedtokens[i].token, &child.token, sizeof(child.token) );
672 memcpy(&sharedspace->sharedtokens[i].client, &child.client, sizeof(child.client) );
673 memcpy(&sharedspace->sharedtokens[i].server, &child.server, sizeof(child.server) );
674 sharedspace->sharedtokens[i].lastused = mytime;
675 sharedspace->sharedtokens[i].persist = keytab ? 1 : 0;
676 stored = i;
677 break;
678 }
679 }
680
681 if ( stored == -1 ) {
682 memcpy(&sharedspace->sharedtokens[oldest].token, &child.token, sizeof(child.token) );
683 memcpy(&sharedspace->sharedtokens[oldest].client, &child.client, sizeof(child.client) );
684 memcpy(&sharedspace->sharedtokens[oldest].server, &child.server, sizeof(child.server) );
685 strcpy(sharedspace->sharedtokens[oldest].clientprincipal, child.clientprincipal );
686 sharedspace->sharedtokens[oldest].lastused = mytime;
687 sharedspace->sharedtokens[oldest].persist = keytab ? 1 : 0;
688 stored = oldest;
689 }
690
691 #ifdef use_pthreads
692 pthread_rwlock_unlock(sharedlock);
693 #else
694 rw_unlock(sharedlock);
695 #endif
696
697 log_error( APLOG_MARK, APLOG_ERR, 0, s, "mod_waklog: token stored in slot %d for %s", stored,
698 child.clientprincipal );
699
700 } else if ( ! usecached ) {
701 log_error( APLOG_MARK, APLOG_ERR, 0, s, "mod_waklog: set_auth divergent case");
702 }
703
704 if ( storeonly ) {
705 goto cleanup;
706 }
707
708 usecachedtoken:
709
710 /* don't ask. Something about AIX. We're leaving it here.*/
711 /* write(2, "", 0); */
712
713 /* we try twice, because sometimes the first one fails. Dunno why, but it always works the second time */
714
715 if ((rc = ktc_SetToken(&child.server, &child.token, &child.client, 0))) {
716 log_error(APLOG_MARK, APLOG_DEBUG, 0, s, "mod_waklog: settoken returned %s for %s -- trying again",
717 afs_error_message(rc), k5user);
718 if ((rc = ktc_SetToken(&child.server, &child.token, &child.client, 0))) {
719 log_error(APLOG_MARK, APLOG_ERR, 0, s, "mod_waklog: settoken2 returned %s for %s",
720 afs_error_message(rc), k5user);
721 goto cleanup;
722 }
723 }
724
725 cleanup:
726 if (use_client_credentials)
727 krb5_cc_close(child.kcontext, clientccache);
728 if ( v5credsp )
729 krb5_free_cred_contents(child.kcontext, v5credsp);
730 if ( increds.client )
731 krb5_free_principal (child.kcontext, increds.client);
732 if ( increds.server )
733 krb5_free_principal (child.kcontext, increds.server);
734 if ( krb5kt )
735 (void) krb5_kt_close(child.kcontext, krb5kt);
736 if ( kprinc )
737 krb5_free_principal (child.kcontext, kprinc);
738
739 if ( rc ) {
740 log_error(APLOG_MARK, APLOG_ERR, 0, s, "mod_waklog: set_auth ending with %d", rc );
741 } else if ( kerror ) {
742 log_error(APLOG_MARK, APLOG_ERR, 0, s, "mod_waklog: set_auth ending with krb5 error %d, %s", kerror, afs_error_message(kerror));
743 } else {
744 log_error(APLOG_MARK, APLOG_DEBUG, 0, s, "mod_waklog: set_auth ending ok");
745 }
746
747 return kerror ? (int) kerror : (int) rc;
748
749 }
750
751
752 int get_cfg_usertokens(waklog_config *cfg)
753 {
754 if (cfg->usertokens==WAKLOG_UNSET)
755 return 0; /* default */
756 return cfg->usertokens;
757 }
758
759 int get_cfg_protect(waklog_config *cfg)
760 {
761 if (cfg->protect==WAKLOG_UNSET)
762 return 0; /* default */
763 return cfg->protect;
764 }
765
766 int get_cfg_disable_token_cache(waklog_config *cfg)
767 {
768 if (cfg->disable_token_cache==WAKLOG_UNSET)
769 return 0; /* default */
770 return cfg->disable_token_cache;
771 }
772
773
774 static void *
775 waklog_create_server_config (MK_POOL * p, server_rec * s)
776 {
777 waklog_config *cfg;
778
779 cfg = (waklog_config *) ap_pcalloc (p, sizeof (waklog_config));
780 cfg->p = p;
781 memset(cfg, 0, sizeof(waklog_config));
782 cfg->path = "(server)";
783 cfg->protect = WAKLOG_UNSET;
784 cfg->usertokens = WAKLOG_UNSET;
785 cfg->disable_token_cache = WAKLOG_UNSET;
786 cfg->keytab = NULL;
787 cfg->principal = NULL;
788 cfg->default_principal = NULL;
789 cfg->default_keytab = NULL;
790 cfg->afs_cell = NULL;
791 cfg->afs_cell_realm = NULL;
792 cfg->forked = 0;
793 cfg->configured = 0;
794
795 log_error (APLOG_MARK, APLOG_DEBUG, 0, s,
796 "mod_waklog: server config created.");
797
798 return (cfg);
799 }
800
801 /* initialize with host-config information */
802
803 static void *
804 waklog_create_dir_config (MK_POOL * p, char *dir)
805 {
806 waklog_config *cfg;
807
808 cfg = (waklog_config *) ap_pcalloc (p, sizeof (waklog_config));
809 memset(cfg, 0, sizeof(waklog_config));
810 cfg->p = p;
811 cfg->path = ap_pstrdup(p, dir );
812 cfg->protect = WAKLOG_UNSET;
813 cfg->usertokens = WAKLOG_UNSET;
814 cfg->disable_token_cache = WAKLOG_UNSET;
815 cfg->keytab = NULL;
816 cfg->principal = NULL;
817 cfg->default_principal = NULL;
818 cfg->default_keytab = NULL;
819 cfg->afs_cell = NULL;
820 cfg->afs_cell_realm = NULL;
821 cfg->forked = 0;
822 cfg->configured = 0;
823
824 return (cfg);
825 }
826
827 static void *waklog_merge_dir_config(MK_POOL *p, void *parent_conf, void *newloc_conf) {
828
829 waklog_config *merged = ( waklog_config * ) ap_pcalloc(p, sizeof(waklog_config ) );
830 waklog_config *parent = ( waklog_config * ) parent_conf;
831 waklog_config *child = ( waklog_config * ) newloc_conf;
832
833 merged->protect = child->protect != WAKLOG_UNSET ? child->protect : parent->protect;
834
835 merged->path = child->path != NULL ? child->path : parent->path;
836
837 merged->usertokens = child->usertokens != WAKLOG_UNSET ? child->usertokens : parent->usertokens;
838
839 merged->disable_token_cache = child->disable_token_cache != WAKLOG_UNSET ? child->disable_token_cache : parent->disable_token_cache;
840
841 merged->principal = child->principal != NULL ? child->principal : parent->principal;
842
843 merged->keytab = child->keytab != NULL ? child->keytab : parent->keytab;
844
845 merged->default_keytab = child->default_keytab != NULL ? child->default_keytab : parent->default_keytab;
846
847 merged->default_principal = child->default_principal != NULL ? child->default_principal : parent->default_principal;
848
849 merged->afs_cell = child->afs_cell != NULL ? child->afs_cell : parent->afs_cell;
850
851 merged->afs_cell_realm = child->afs_cell_realm != NULL ? child->afs_cell_realm : parent->afs_cell_realm;
852
853 return (void *) merged;
854
855 }
856
857 static void *waklog_merge_server_config(MK_POOL *p, void *parent_conf, void *newloc_conf) {
858
859 waklog_config *merged = ( waklog_config * ) ap_pcalloc(p, sizeof(waklog_config ) );
860 waklog_config *pconf = ( waklog_config * ) parent_conf;
861 waklog_config *nconf = ( waklog_config * ) newloc_conf;
862
863 merged->protect = nconf->protect == WAKLOG_UNSET ? pconf->protect : nconf->protect;
864
865 merged->usertokens = nconf->usertokens == WAKLOG_UNSET ? pconf->usertokens : nconf->usertokens;
866
867 merged->disable_token_cache = nconf->disable_token_cache == WAKLOG_UNSET ? pconf->disable_token_cache : nconf->disable_token_cache;
868
869 merged->keytab = nconf->keytab == NULL ? ap_pstrdup(p, pconf->keytab) :
870 ( nconf->keytab == NULL ? NULL : ap_pstrdup(p, nconf->keytab) );
871
872 merged->principal = nconf->principal == NULL ? ap_pstrdup(p, pconf->principal) :
873 ( nconf->principal == NULL ? NULL : ap_pstrdup(p, nconf->principal) );
874
875 merged->afs_cell = nconf->afs_cell == NULL ? ap_pstrdup(p, pconf->afs_cell) :
876 ( nconf->afs_cell == NULL ? NULL : ap_pstrdup(p, nconf->afs_cell) );
877
878 merged->afs_cell_realm = nconf->afs_cell_realm == NULL ? ap_pstrdup(p, pconf->afs_cell_realm) :
879 ( nconf->afs_cell_realm == NULL ? NULL : ap_pstrdup(p, nconf->afs_cell_realm) );
880
881 merged->default_keytab = nconf->default_keytab == NULL ? ap_pstrdup(p, pconf->default_keytab) :
882 ( nconf->default_keytab == NULL ? NULL : ap_pstrdup(p, nconf->default_keytab) );
883
884 merged->default_principal = nconf->default_principal == NULL ? ap_pstrdup(p, pconf->default_principal) :
885 ( nconf->default_principal == NULL ? NULL : ap_pstrdup(p, nconf->default_principal) );
886
887
888 return (void *) merged;
889
890 }
891
892 static const char *
893 set_waklog_enabled (cmd_parms * params, void *mconfig, int flag)
894 {
895 waklog_config *cfg = mconfig ? ( waklog_config * ) mconfig :
896 ( waklog_config * ) ap_get_module_config(params->server->module_config, &waklog_module );
897
898 cfg->protect = flag;
899 cfg->configured = 1;
900 log_error (APLOG_MARK, APLOG_DEBUG, 0, params->server,
901 "mod_waklog: waklog_enabled set on %s", cfg->path ? cfg->path : "NULL");
902 return (NULL);
903 }
904
905
906 /* this adds a principal/keytab pair to get their tokens renewed by the
907 child process every few centons. */
908
909 void add_to_renewtable(MK_POOL *p, char *keytab, char *principal) {
910
911 int i;
912
913 if ( renewcount >= SHARED_TABLE_SIZE ) {
914 log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL, "mod_waklog: big problem. Increase the SHARED_TABLE_SIZE or \
915 decrease your tokens.");
916 return;
917 }
918
919 /* check to see if it's already there */
920
921 for ( i = 0; i < renewcount; i++ ) {
922 if ( ! strcmp(renewtable[i].principal, principal ) ) {
923 return;
924 }
925 }
926
927 renewtable[renewcount].keytab = ap_pstrdup(p, keytab);
928 renewtable[renewcount].principal = ap_pstrdup(p, principal);
929 renewtable[renewcount].lastrenewed = 0;
930 ++renewcount;
931
932 }
933
934 static const char *
935 set_waklog_location_principal (cmd_parms *params, void *mconfig, char *principal, char *keytab)
936 {
937 waklog_config *cfg = mconfig ? ( waklog_config * ) mconfig :
938 ( waklog_config * ) ap_get_module_config(params->server->module_config, &waklog_module );
939
940 log_error (APLOG_MARK, APLOG_DEBUG, 0, params->server,
941 "mod_waklog: configuring principal: %s, keytab: %s", principal, keytab);
942
943 cfg->principal = ap_pstrdup(params->pool, principal);
944 cfg->keytab = ap_pstrdup (params->pool, keytab);
945
946 add_to_renewtable(params->pool, keytab, principal);
947
948 cfg->configured = 1;
949
950 return (NULL);
951 }
952
953 static const char *
954 set_waklog_afs_cell (cmd_parms * params, void *mconfig, char *file)
955 {
956 waklog_config *waklog_mconfig = ( waklog_config * ) mconfig;
957 waklog_config *waklog_srvconfig =
958 ( waklog_config * ) ap_get_module_config(params->server->module_config, &waklog_module );
959
960 log_error (APLOG_MARK, APLOG_INFO, 0, params->server,
961 "mod_waklog: will use afs_cell: %s", file);
962
963 // Prefer afs/cell@REALM over afs@REALM, just like the OpenAFS tools
964 waklog_srvconfig->cell_in_principal = 1;
965
966 waklog_srvconfig->afs_cell = ap_pstrdup (params->pool, file);
967 waklog_srvconfig->configured = 1;
968
969 if (waklog_mconfig != NULL) {
970 waklog_mconfig->cell_in_principal = waklog_srvconfig->cell_in_principal;
971 waklog_mconfig->afs_cell = ap_pstrdup (params->pool, file);
972 waklog_mconfig->configured = 1;
973 }
974 return (NULL);
975 }
976
977 static const char *
978 set_waklog_afs_cell_realm (cmd_parms * params, void *mconfig, char *file)
979 {
980 waklog_config *waklog_mconfig = ( waklog_config * ) mconfig;
981 waklog_config *waklog_srvconfig =
982 ( waklog_config * ) ap_get_module_config(params->server->module_config, &waklog_module );
983
984 log_error (APLOG_MARK, APLOG_INFO, 0, params->server,
985 "mod_waklog: will use afs_cell_realm: %s", file);
986
987 waklog_srvconfig->afs_cell_realm = ap_pstrdup (params->pool, file);
988
989 if (waklog_mconfig != NULL) {
990 waklog_mconfig->afs_cell_realm = ap_pstrdup (params->pool, file);
991 }
992 return (NULL);
993 }
994
995 static const char *
996 set_waklog_default_principal (cmd_parms * params, void *mconfig, char *principal, char *keytab)
997 {
998 waklog_config *cfg = mconfig ? ( waklog_config * ) mconfig :
999 ( waklog_config * ) ap_get_module_config(params->server->module_config, &waklog_module );
1000
1001 waklog_config *srvcfg = ( waklog_config * ) ap_get_module_config(params->server->module_config, &waklog_module );
1002
1003 log_error (APLOG_MARK, APLOG_DEBUG, 0, params->server,
1004 "mod_waklog: set default princ/keytab: %s, %s for %s", principal, keytab, cfg->path ? cfg->path : "NULL");
1005
1006 cfg->default_principal = ap_pstrdup (params->pool, principal);
1007 cfg->default_keytab = ap_pstrdup(params->pool, keytab );
1008
1009 /* this also gets set at the server level */
1010 if ( mconfig && ( ! cfg->path ) ) {
1011 srvcfg->default_principal = ap_pstrdup (params->pool, principal);
1012 srvcfg->default_keytab = ap_pstrdup(params->pool, keytab );
1013 } else {
1014 log_error(APLOG_MARK, APLOG_ERR, 0, params->server, "only able to set default principal on a global level!");
1015 return "Unable to set DefaultPrincipal outside of top level config!";
1016 }
1017
1018 add_to_renewtable( params->pool, keytab, principal );
1019
1020 cfg->configured = 1;
1021
1022 return (NULL);
1023 }
1024
1025 static const char *
1026 set_waklog_use_usertokens (cmd_parms * params, void *mconfig, int flag)
1027 {
1028 waklog_config *cfg = mconfig ? ( waklog_config * ) mconfig :
1029 ( waklog_config * ) ap_get_module_config(params->server->module_config, &waklog_module );
1030
1031 cfg->usertokens = flag;
1032
1033 cfg->configured = 1;
1034
1035 log_error (APLOG_MARK, APLOG_DEBUG, 0, params->server,
1036 "mod_waklog: waklog_use_user_tokens set");
1037 return (NULL);
1038 }
1039
1040
1041 static const char *
1042 set_waklog_disable_token_cache (cmd_parms * params, void *mconfig, int flag)
1043 {
1044 waklog_config *cfg = mconfig ? ( waklog_config * ) mconfig :
1045 ( waklog_config * ) ap_get_module_config(params->server->module_config, &waklog_module );
1046
1047 cfg->disable_token_cache = flag;
1048
1049 cfg->configured = 1;
1050
1051 log_error (APLOG_MARK, APLOG_DEBUG, 0, params->server,
1052 "mod_waklog: waklog_disable_token_cache set");
1053 return (NULL);
1054 }
1055
1056
1057 #ifndef APACHE2
1058 static void waklog_child_exit( server_rec *s, MK_POOL *p ) {
1059 #else
1060 apr_status_t waklog_child_exit( void *sr ) {
1061
1062 server_rec *s = (server_rec *) sr;
1063 #endif
1064
1065 if ( child.ccache ) {
1066 krb5_cc_close(child.kcontext, child.ccache);
1067 }
1068
1069 if ( child.kcontext ) {
1070 krb5_free_context(child.kcontext);
1071 }
1072
1073 /* forget our tokens */
1074
1075 ktc_ForgetAllTokens ();
1076
1077 log_error (APLOG_MARK, APLOG_DEBUG, 0, s,
1078 "mod_waklog: waklog_child_exit complete");
1079
1080 #ifdef APACHE2
1081 return APR_SUCCESS;
1082 #endif
1083
1084 }
1085
1086 static void
1087 #ifdef APACHE2
1088 waklog_child_init (MK_POOL * p, server_rec * s)
1089 #else
1090 waklog_child_init (server_rec * s, MK_POOL * p)
1091 #endif
1092 {
1093
1094 krb5_error_code code;
1095 waklog_config *cfg;
1096
1097 char *cell;
1098
1099 log_error (APLOG_MARK, APLOG_DEBUG, 0, s, "mod_waklog: child_init called for pid %d", getpid());
1100
1101 if ( !sharedspace ) {
1102 log_error( APLOG_MARK, APLOG_ERR, 0, s, "mod_waklog: child_init called without shared space? %d", getpid());
1103 return;
1104 }
1105
1106 log_error (APLOG_MARK, APLOG_DEBUG, 0, s, "mod_waklog: child_init called for pid %d", getpid());
1107
1108 memset (&child, 0, sizeof(child));
1109
1110 if ( ( code = krb5_init_context(&child.kcontext) ) ) {
1111 log_error(APLOG_MARK, APLOG_ERR, 0, s, "mod_waklog: can't init kerberos context %d", code );
1112 }
1113
1114 if ( ( code = krb5_cc_resolve(child.kcontext, "MEMORY:tmpcache", &child.ccache) ) ) {
1115 log_error(APLOG_MARK, APLOG_ERR, 0, s, "mod_waklog: can't initialize in-memory credentials cache %d", code );
1116 }
1117
1118 if ( pag_for_children ) {
1119 k_setpag ();
1120 }
1121
1122 getModConfig (cfg, s);
1123
1124 if ( cfg->default_principal != NULL ) {
1125 log_error(APLOG_MARK, APLOG_DEBUG, 0, s, "mod_waklog: child_init setting default user %s, %s", cfg->default_principal, cfg->default_keytab);
1126 set_auth( s, NULL, 0, cfg->default_principal, cfg->default_keytab, 0);
1127 }
1128
1129 cell = strdup(cfg->afs_cell);
1130 pr_Initialize( 0, AFSDIR_CLIENT_ETC_DIR, cell );
1131
1132 #ifdef APACHE2
1133 apr_pool_cleanup_register(p, s, waklog_child_exit, apr_pool_cleanup_null);
1134 #endif
1135
1136 log_error (APLOG_MARK, APLOG_DEBUG, 0, s,
1137 "mod_waklog: child_init returned");
1138
1139 return;
1140 }
1141
1142 command_rec waklog_cmds[] = {
1143
1144 command ("WaklogAFSCell", set_waklog_afs_cell, 0, TAKE1,
1145 "Use the supplied AFS cell (required)"),
1146
1147 command ("WaklogAFSCellRealm", set_waklog_afs_cell_realm, 0, TAKE1,
1148 "Assume that the AFS cell belongs to the specified Kerberos realm (optional)"),
1149
1150 command ("WaklogEnabled", set_waklog_enabled, 0, FLAG,
1151 "enable waklog on a server, location, or directory basis"),
1152
1153 command ("WaklogDefaultPrincipal", set_waklog_default_principal, 0, TAKE2,
1154 "Set the default principal that the server runs as"),
1155
1156 command ("WaklogLocationPrincipal", set_waklog_location_principal, 0, TAKE2,
1157 "Set the principal on a <Location>-specific basis"),
1158
1159 command ("WaklogDisableTokenCache", set_waklog_disable_token_cache, 0, FLAG,
1160 "Ignore the token cache (location-specific); useful for scripts that need kerberos tickets."),
1161
1162 command ("WaklogUseUserTokens", set_waklog_use_usertokens, 0, FLAG,
1163 "Use the requesting user tokens (from webauth)"),
1164
1165 {NULL}
1166 };
1167
1168
1169 /* not currently using this */
1170
1171 static int
1172 token_cleanup (void *data)
1173 {
1174 request_rec *r = (request_rec *) data;
1175
1176 if (child.token.ticketLen)
1177 {
1178 memset (&child.token, 0, sizeof (struct ktc_token));
1179
1180 ktc_ForgetAllTokens ();
1181
1182 log_error (APLOG_MARK, APLOG_DEBUG, 0, r->server,
1183 "mod_waklog: ktc_ForgetAllTokens succeeded: pid: %d",
1184 getpid ());
1185 }
1186 return 0;
1187 }
1188
1189 /* This function doesn't return anything but is passed to ap_bspawn_child on
1190 * Apache 1 which expects it to return a pid as an int. For want of better
1191 * understanding, err on the side of not changing Apache 1 code while fixing
1192 * the compile warning on Apache 2. */
1193 #ifdef APACHE2
1194 static void
1195 #else
1196 static int
1197 #endif
1198 waklog_child_routine (void *data, child_info * pinfo)
1199 {
1200 int i;
1201 server_rec *s = (server_rec *) data;
1202 krb5_error_code code;
1203 char *cell;
1204 time_t sleep_time = ( TKT_LIFE / 2 ) ;
1205 time_t when;
1206 time_t left;
1207 waklog_config *cfg;
1208
1209 getModConfig( cfg, s );
1210
1211 log_error(APLOG_MARK, APLOG_DEBUG, 0, s, "mod_waklog: waklog_child_routine started, running as %d", getuid());
1212
1213 memset (&child, 0, sizeof(child));
1214
1215 if ( ( code = krb5_init_context(&child.kcontext) ) ) {
1216 log_error(APLOG_MARK, APLOG_ERR, 0, s, "mod_waklog: can't init kerberos context %d", code );
1217 }
1218
1219 if ( ( code = krb5_cc_resolve(child.kcontext, "MEMORY:tmpcache", &child.ccache) ) ) {
1220 log_error(APLOG_MARK, APLOG_ERR, 0, s, "mod_waklog: can't initialize in-memory credentials cache %d", code );
1221 }
1222
1223 log_error(APLOG_MARK, APLOG_DEBUG, 0, s, "mod_waklog: about to pr_Initialize");
1224
1225 /* need to do this so we can make PTS calls */
1226 cell = strdup(cfg->afs_cell); /* stupid */
1227 pr_Initialize( 0, AFSDIR_CLIENT_ETC_DIR, cell );
1228
1229 log_error(APLOG_MARK, APLOG_DEBUG, 0, s, "mod_waklog: still here");
1230
1231 while(1) {
1232
1233 for ( i = 0; i < renewcount; ++i ) {
1234 renewtable[i].lastrenewed = time(0);
1235 log_error(APLOG_MARK, APLOG_ERR, 0, s, "mod_waklog: (pid %d) renewing %s / %s", getpid(), renewtable[i].principal,
1236 renewtable[i].keytab);
1237
1238 set_auth( s, NULL, 0, renewtable[i].principal, renewtable[i].keytab, 1 );
1239
1240 /* if this is our default token, we want to "stash" it in our current PAG so the parent maintains readability of
1241 things that it needs to read */
1242
1243 if ( cfg && cfg->default_principal && ( ! strcmp(cfg->default_principal, renewtable[i].principal ) ) ) {
1244 log_error(APLOG_MARK, APLOG_ERR, 0, s, "mod_waklog: renewing/setting default tokens" );
1245 set_auth( s, NULL, 0, renewtable[i].principal, renewtable[i].keytab, 0 );
1246 }
1247
1248 }
1249
1250 sharedspace->renewcount++;
1251
1252 left = sleep_time;
1253
1254 while( left > 5 ) {
1255 when = time(0);
1256
1257 sleep(left);
1258
1259 left -= ( time(0) - when );
1260 }
1261
1262 }
1263
1264 pr_End();
1265
1266 }
1267
1268 #ifdef APACHE2
1269 static int
1270 waklog_init_handler (apr_pool_t * p, apr_pool_t * plog,
1271 apr_pool_t * ptemp, server_rec * s)
1272 {
1273 int rv;
1274 extern char *version;
1275 apr_proc_t *proc;
1276 waklog_config *cfg;
1277 void *data;
1278 int fd = -1;
1279 int use_existing = 1;
1280 int oldrenewcount;
1281 char cache_file[MAXNAMELEN];
1282 #ifdef use_pthreads
1283 pthread_rwlockattr_t rwlock_attr;
1284 #endif
1285
1286
1287 getModConfig (cfg, s);
1288
1289 /* initialize_module() will be called twice, and if it's a DSO
1290 * then all static data from the first call will be lost. Only
1291 * set up our static data on the second call.
1292 * see http://issues.apache.org/bugzilla/show_bug.cgi?id=37519 */
1293 apr_pool_userdata_get (&data, userdata_key, s->process->pool);
1294
1295
1296 if (cfg->afs_cell==NULL) {
1297 log_error (APLOG_MARK, APLOG_ERR, 0, s,
1298 "mod_waklog: afs_cell==NULL; please provide the WaklogAFSCell directive");
1299 /** clobber apache */
1300 exit(-1);
1301 }
1302
1303 if (!data)
1304 {
1305 apr_pool_userdata_set ((const void *) 1, userdata_key,
1306 apr_pool_cleanup_null, s->process->pool);
1307 }
1308 else
1309 {
1310 log_error (APLOG_MARK, APLOG_INFO, 0, s,
1311 "mod_waklog: version %s initialized for cell %s", version, cfg->afs_cell);
1312
1313 if ( sharedspace ) {
1314 log_error(APLOG_MARK, APLOG_ERR, 0, s, "mod_waklog: shared memory already allocated." );
1315 } else {
1316
1317 snprintf( cache_file, MAXNAMELEN, "/tmp/waklog_cache.%d", getpid() );
1318
1319 if ( ( fd = open( cache_file, O_RDWR, 0600 ) ) == -1 ) {
1320
1321 if ( errno == ENOENT ) {
1322
1323 log_error(APLOG_MARK, APLOG_ERR, 0, s, "mod_waklog: creating shared token cache file %s", cache_file );
1324 use_existing = 0;
1325 if ( ( fd = open( cache_file, O_RDWR|O_CREAT|O_TRUNC, 0600 ) ) == -1 ) {
1326 log_error(APLOG_MARK, APLOG_ERR, 0, s, "mod_waklog: cannot create shared token cache file %s (%d)", cache_file, errno );
1327 exit(errno);
1328 }
1329 } else {
1330 log_error(APLOG_MARK, APLOG_ERR, 0, s, "mod_waklog: cannot open existing shared token cache file %s (%d)", cache_file, errno );
1331 }
1332 }
1333
1334 if ( use_existing == 0 ) {
1335 struct sharedspace_s bob;
1336 log_error(APLOG_MARK, APLOG_ERR, 0, s, "mod_waklog: sizing our cache file %d to %d", fd, sizeof(struct sharedspace_s) );
1337 memset( &bob, 0, sizeof(struct sharedspace_s));
1338 if ( write(fd, &bob, sizeof(struct sharedspace_s)) != sizeof(struct sharedspace_s) ) {
1339 log_error(APLOG_MARK, APLOG_ERR, 0, s, "mod_waklog: failed to write to our cache file %s (%d)", cache_file, errno );
1340 exit(errno);
1341 }
1342 log_error(APLOG_MARK, APLOG_ERR, 0, s, "mod_waklog: done sizing our cache file to %d", sizeof(struct sharedspace_s) );
1343 }
1344
1345 /* mmap the region */
1346
1347 if ( ( sharedspace = (struct sharedspace_s *) mmap ( NULL, sizeof(struct sharedspace_s), PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0 ) ) != MAP_FAILED ) {
1348 int err = 0;
1349 log_error( APLOG_MARK, APLOG_DEBUG, 0, s, "mod_waklog: shared mmap region ok %d", sharedspace );
1350 err = unlink(cache_file);
1351 if (err) {
1352 log_error( APLOG_MARK, APLOG_ERR, 0, s, "mod_waklog: unable to delete %s due to %d", cache_file, errno);
1353 } else {
1354 log_error( APLOG_MARK, APLOG_DEBUG, 0, s, "mod_waklog: shared cache unlinked (will be deleted when Apache quits)");
1355 }
1356 } else {
1357 log_error( APLOG_MARK, APLOG_DEBUG, 0, s, "mod_waklog: mmap failed %d", errno );
1358 exit(errno);
1359 }
1360 }
1361
1362 #ifdef use_pthreads
1363 #define locktype pthread_rwlock_t
1364 #else
1365 #define locktype rwlock_t
1366 #endif
1367
1368 if ( ( sharedlock = ( locktype * ) mmap ( NULL, sizeof(locktype), PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANON, -1, 0 ) ) != NULL ) {
1369 #ifndef use_pthreads
1370 rwlock_init(sharedlock, USYNC_PROCESS, NULL );
1371 #else
1372 pthread_rwlockattr_init(&rwlock_attr);
1373 pthread_rwlockattr_setpshared(&rwlock_attr, PTHREAD_PROCESS_SHARED);
1374 pthread_rwlock_init(sharedlock, &rwlock_attr );
1375 #endif
1376 } else {
1377 log_error( APLOG_MARK, APLOG_DEBUG, 0, s, "mod_waklog: rwlock mmap failed %d", errno );
1378 }
1379
1380 #undef locktype
1381
1382 /* set our default tokens */
1383
1384 oldrenewcount = sharedspace->renewcount;
1385
1386 pag_for_children = 0;
1387
1388 proc = (apr_proc_t *) ap_pcalloc (s->process->pool, sizeof (apr_proc_t));
1389
1390 rv = apr_proc_fork (proc, s->process->pool);
1391
1392 if (rv == APR_INCHILD)
1393 {
1394 waklog_child_routine (s, NULL);
1395 }
1396 else
1397 {
1398 apr_pool_note_subprocess (s->process->pool, proc, APR_KILL_ALWAYS);
1399 }
1400 /* parent and child */
1401 cfg->forked = proc->pid;
1402 pag_for_children = 1;
1403
1404 if ( use_existing == 0 ) {
1405 /* wait here until our child process has gone and done it's renewing thing. */
1406 while( sharedspace->renewcount == oldrenewcount ) {
1407 log_error( APLOG_MARK, APLOG_ERR, 0, s, "mod_waklog: waiting for tokens..." );
1408 apr_sleep(150000);
1409 }
1410 }
1411
1412 if ( cfg->default_principal ) {
1413 set_auth( s, NULL, 0, cfg->default_principal, cfg->default_keytab, 0);
1414 }
1415 }
1416 return 0;
1417 }
1418 #else
1419 static void
1420 waklog_init (server_rec * s, MK_POOL * p)
1421 {
1422 extern char *version;
1423 int pid;
1424 waklog_config *cfg;
1425 int fd = -1;
1426 int use_existing = 1;
1427 int oldrenewcount;
1428 char cache_file[MAXNAMELEN];
1429 #ifdef use_pthreads
1430 pthread_rwlockattr_t rwlock_attr;
1431 #endif
1432
1433 log_error (APLOG_MARK, APLOG_DEBUG, 0, s,
1434 "mod_waklog: version %s initialized.", version);
1435
1436 if ( sharedspace ) {
1437 log_error(APLOG_MARK, APLOG_ERR, 0, s, "mod_waklog: shared memory already allocated." );
1438 } else {
1439
1440 snprintf( cache_file, MAXNAMELEN, "/tmp/waklog_cache.%d", getpid() );
1441
1442 if ( ( fd = open( cache_file, O_RDWR, 0600 ) ) == -1 ) {
1443
1444 if ( errno == ENOENT ) {
1445
1446 log_error(APLOG_MARK, APLOG_ERR, 0, s, "mod_waklog: creating shared token cache file %s", cache_file );
1447 use_existing = 0;
1448 if ( ( fd = open( cache_file, O_RDWR|O_CREAT|O_TRUNC, 0600 ) ) == -1 ) {
1449 log_error(APLOG_MARK, APLOG_ERR, 0, s, "mod_waklog: cannot create shared token cache file %s (%d)", cache_file, errno );
1450 exit(errno);
1451 }
1452 } else {
1453 log_error(APLOG_MARK, APLOG_ERR, 0, s, "mod_waklog: cannot open existing shared token cache file %s (%d)", cache_file, errno );
1454 }
1455
1456 }
1457
1458 if ( use_existing == 0 ) {
1459 struct sharedspace_s bob;
1460 log_error(APLOG_MARK, APLOG_ERR, 0, s, "mod_waklog: sizing our cache file %d to %d", fd, sizeof(struct sharedspace_s) );
1461 memset( &bob, 0, sizeof(struct sharedspace_s));
1462 write(fd, &bob, sizeof(struct sharedspace_s));
1463 log_error(APLOG_MARK, APLOG_ERR, 0, s, "mod_waklog: done sizing our cache file to %d", sizeof(struct sharedspace_s) );
1464 }
1465
1466 /* mmap the region */
1467
1468 if ( ( sharedspace = (struct sharedspace_s *) mmap ( NULL, sizeof(struct sharedspace_s), PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0 ) ) != (void *) -1 ) {
1469 log_error( APLOG_MARK, APLOG_DEBUG, 0, s, "mod_waklog: shared mmap region ok %d", sharedspace );
1470 close(fd);
1471 } else {
1472 log_error( APLOG_MARK, APLOG_DEBUG, 0, s, "mod_waklog: mmap failed %d", errno );
1473 exit(errno);
1474 }
1475 }
1476
1477 #ifdef use_pthreads
1478 #define locktype pthread_rwlock_t
1479 #else
1480 #define locktype rwlock_t
1481 #endif
1482
1483 /* mmap our shared space for our lock */
1484 if ( ( sharedlock = ( locktype * ) mmap ( NULL, sizeof(locktype), PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANON, -1, 0 ) ) ) {
1485 #ifndef use_pthreads
1486 rwlock_init(sharedlock, USYNC_PROCESS, NULL );
1487 #else
1488 pthread_rwlockattr_init(&rwlock_attr);
1489 pthread_rwlockattr_setpshared(&rwlock_attr, PTHREAD_PROCESS_SHARED);
1490 pthread_rwlock_init(sharedlock, &rwlock_attr );
1491 #endif
1492 } else {
1493 log_error( APLOG_MARK, APLOG_DEBUG, 0, s, "mod_waklog: rwlock mmap failed %d", errno );
1494 }
1495
1496 #undef locktype
1497
1498 /* set our default tokens */
1499
1500 getModConfig (cfg, s);
1501
1502 oldrenewcount = sharedspace->renewcount;
1503
1504 pag_for_children = 0;
1505
1506 pid = ap_bspawn_child (p, waklog_child_routine, s, kill_always,
1507 NULL, NULL, NULL);
1508
1509 pag_for_children = 1;
1510
1511 log_error (APLOG_MARK, APLOG_DEBUG, 0, s,
1512 "mod_waklog: ap_bspawn_child: %d.", pid);
1513
1514 if ( use_existing == 0 ) {
1515 /* wait here until our child process has gone and done it's renewing thing. */
1516 while( sharedspace->renewcount == oldrenewcount ) {
1517 log_error( APLOG_MARK, APLOG_ERR, 0, s, "mod_waklog: waiting for tokens..." );
1518 sleep(2);
1519 }
1520 }
1521
1522 if ( cfg->default_principal ) {
1523 set_auth( s, NULL, 0, cfg->default_principal, cfg->default_keytab, 0);
1524 }
1525
1526 }
1527 #endif
1528
1529 static int
1530 waklog_phase0 (request_rec * r)
1531 {
1532 waklog_config *cfg;
1533
1534 log_error (APLOG_MARK, APLOG_DEBUG, 0, r->server,
1535 "mod_waklog: phase0 called");
1536
1537 cfg = retrieve_config(r);
1538
1539 if ( get_cfg_protect(cfg) && cfg->principal ) {
1540 log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, "mod_waklog: phase0 using user %s", cfg->principal);
1541 set_auth(r->server, r, 0, cfg->principal, cfg->keytab, 0);
1542 } else if ( cfg->default_principal ) {
1543 log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, "mod_waklog: phase0 using default user %s", cfg->default_principal);
1544 set_auth(r->server, r, 0, cfg->default_principal, cfg->default_keytab, 0);
1545 } else {
1546
1547 if (child.token.ticketLen) {
1548 memset( &child.token, 0, sizeof (struct ktc_token) );
1549 ktc_ForgetAllTokens();
1550 }
1551
1552 log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, "mod_waklog: phase0 not doing nothin.");
1553 }
1554
1555 return DECLINED;
1556 }
1557
1558 static int
1559 waklog_phase1 (request_rec * r)
1560 {
1561 waklog_config *cfg;
1562
1563 log_error (APLOG_MARK, APLOG_DEBUG, 0, r->server,
1564 "mod_waklog: phase1 called");
1565
1566 cfg = retrieve_config(r);
1567
1568 if ( get_cfg_protect(cfg) && cfg->principal ) {
1569 log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, "mod_waklog: phase1 using user %s", cfg->principal);
1570 set_auth(r->server, r, 0, cfg->principal, cfg->keytab, 0);
1571 } else if ( cfg->default_principal ) {
1572 log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, "mod_waklog: phase1 using default user %s", cfg->default_principal);
1573 set_auth(r->server, r, 0, cfg->default_principal, cfg->default_keytab, 0);
1574 } else {
1575 log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, "mod_waklog: phase1 not doing nothin.");
1576 }
1577
1578 return DECLINED;
1579 }
1580
1581 static int
1582 waklog_phase3 (request_rec * r)
1583 {
1584 waklog_config *cfg;
1585
1586 log_error (APLOG_MARK, APLOG_DEBUG, 0, r->server,
1587 "mod_waklog: phase 3 called");
1588
1589 cfg = retrieve_config(r);
1590
1591 if ( get_cfg_protect(cfg) && cfg->principal ) {
1592 log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, "mod_waklog: phase3 using user %s", cfg->principal);
1593 set_auth(r->server, r, 0, cfg->principal, cfg->keytab, 0);
1594 } else if ( cfg->default_principal ) {
1595 log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, "mod_waklog: phase3 using default user %s", cfg->default_principal);
1596 set_auth(r->server, r, 0, cfg->default_principal, cfg->default_keytab, 0);
1597 } else {
1598 log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, "mod_waklog: phase3 not doing nothin.");
1599 }
1600
1601 return DECLINED;
1602 }
1603
1604 static int
1605 waklog_phase6 (request_rec * r)
1606 {
1607 waklog_config *cfg;
1608
1609 log_error (APLOG_MARK, APLOG_DEBUG, 0, r->server,
1610 "mod_waklog: phase6 called");
1611
1612 cfg = retrieve_config(r);
1613
1614 if ( get_cfg_protect(cfg) && cfg->principal ) {
1615 log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, "mod_waklog: phase6 using user %s", cfg->principal);
1616 set_auth(r->server, r, 0, cfg->principal, cfg->keytab, 0);
1617 } else if ( cfg->default_principal ) {
1618 log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, "mod_waklog: phase6 using default user %s", cfg->default_principal);
1619 set_auth(r->server, r, 0, cfg->default_principal, cfg->default_keytab, 0);
1620 } else {
1621 log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, "mod_waklog: phase6 not doing nothin.");
1622 }
1623
1624 return DECLINED;
1625 }
1626
1627 static int
1628 waklog_phase7 (request_rec * r)
1629 {
1630 waklog_config *cfg;
1631 int rc = 0;
1632
1633 log_error (APLOG_MARK, APLOG_DEBUG, 0, r->server,
1634 "mod_waklog: phase7 called");
1635
1636 cfg = retrieve_config (r);
1637
1638 if ( get_cfg_protect(cfg) && get_cfg_usertokens(cfg) ) {
1639 log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, "mod_waklog: phase7 using usertokens");
1640 rc = set_auth( r->server, r, 1, NULL, NULL, 0);
1641 } else if ( get_cfg_protect(cfg) && cfg->principal ) {
1642 log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, "mod_waklog: phase7 using user %s", cfg->principal);
1643 rc = set_auth( r->server, r, 0, cfg->principal, cfg->keytab, 0);
1644 } else if ( cfg->default_principal ) {
1645 log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, "mod_waklog: phase7 using default user %s", cfg->default_principal);
1646 rc = set_auth( r->server, r, 0, cfg->default_principal, cfg->default_keytab, 0);
1647 } else {
1648 log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, "mod_waklog: no tokens");
1649 if (child.token.ticketLen) {
1650 memset(&child.token, 0, sizeof(struct ktc_token));
1651 ktc_ForgetAllTokens();
1652 }
1653 }
1654
1655 if ( rc ) {
1656 return 400;
1657 }
1658
1659 return DECLINED;
1660 }
1661
1662 static int
1663 waklog_phase9 (request_rec * r)
1664 {
1665 waklog_config *cfg;
1666
1667 log_error (APLOG_MARK, APLOG_DEBUG, 0, r->server,
1668 "mod_waklog: phase9 called");
1669
1670 getModConfig (cfg, r->server);
1671
1672 if ( cfg->default_principal ) {
1673 log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, "mod_waklog: phase9 using default user %s", cfg->default_principal);
1674 set_auth( r->server, r, 0, cfg->default_principal, cfg->default_keytab, 0);
1675 }
1676
1677 return DECLINED;
1678 }
1679
1680
1681 static
1682 #ifdef APACHE2
1683 int
1684 #else
1685 void
1686 #endif
1687 waklog_new_connection (conn_rec * c
1688 #ifdef APACHE2
1689 , void *dummy
1690 #endif
1691 )
1692 {
1693
1694 waklog_config *cfg;
1695
1696 log_error (APLOG_MARK, APLOG_DEBUG, 0, c->base_server,
1697 "mod_waklog: new_connection called: pid: %d", getpid ());
1698
1699 getModConfig(cfg, c->base_server);
1700
1701 if ( cfg->default_principal ) {
1702 log_error(APLOG_MARK, APLOG_DEBUG, 0, c->base_server, "mod_waklog: new conn setting default user %s",
1703 cfg->default_principal);
1704 set_auth( c->base_server, NULL, 0, cfg->default_principal, cfg->default_keytab, 0);
1705 }
1706
1707
1708 return
1709 #ifdef APACHE2
1710 0
1711 #endif
1712 ;
1713 }
1714
1715
1716 /*
1717 ** Here's a quick explaination for phase0 and phase2:
1718 ** Apache does a stat() on the path between phase0 and
1719 ** phase2, and must by ACLed rl to succeed. So, at
1720 ** phase0 we acquire credentials for umweb:servers from
1721 ** a keytab, and at phase2 we must ensure we remove them.
1722 **
1723 ** Failure to "unlog" would be a security risk.
1724 */
1725 static int
1726 waklog_phase2 (request_rec * r)
1727 {
1728
1729 log_error (APLOG_MARK, APLOG_DEBUG, 0, r->server,
1730 "mod_waklog: phase2 called");
1731
1732 if (child.token.ticketLen)
1733 {
1734 memset (&child.token, 0, sizeof (struct ktc_token));
1735
1736 ktc_ForgetAllTokens ();
1737
1738 log_error (APLOG_MARK, APLOG_DEBUG, 0, r->server,
1739 "mod_waklog: ktc_ForgetAllTokens succeeded: pid: %d",
1740 getpid ());
1741 }
1742
1743 log_error (APLOG_MARK, APLOG_DEBUG, 0, r->server,
1744 "mod_waklog: phase2 returning");
1745
1746 return DECLINED;
1747 }
1748
1749 #ifndef APACHE2
1750 module MODULE_VAR_EXPORT waklog_module = {
1751 STANDARD_MODULE_STUFF,
1752 waklog_init, /* module initializer */
1753 waklog_create_dir_config, /* create per-dir config structures */
1754 waklog_merge_dir_config, /* merge per-dir config structures */
1755 waklog_create_server_config, /* create per-server config structures */
1756 waklog_merge_dir_config, /* merge per-server config structures */
1757 waklog_cmds, /* table of config file commands */
1758 NULL, /* [#8] MIME-typed-dispatched handlers */
1759 waklog_phase1, /* [#1] URI to filename translation */
1760 NULL, /* [#4] validate user id from request */
1761 NULL, /* [#5] check if the user is ok _here_ */
1762 waklog_phase3, /* [#3] check access by host address */
1763 waklog_phase6, /* [#6] determine MIME type */
1764 waklog_phase7, /* [#7] pre-run fixups */
1765 waklog_phase9, /* [#9] log a transaction */
1766 waklog_phase2, /* [#2] header parser */
1767 waklog_child_init, /* child_init */
1768 waklog_child_exit, /* child_exit */
1769 waklog_phase0 /* [#0] post read-request */
1770 #ifdef EAPI
1771 , NULL, /* EAPI: add_module */
1772 NULL, /* EAPI: remove_module */
1773 NULL, /* EAPI: rewrite_command */
1774 waklog_new_connection /* EAPI: new_connection */
1775 #endif
1776 };
1777 #else
1778 static void
1779 waklog_register_hooks (apr_pool_t * p)
1780 {
1781 ap_hook_translate_name (waklog_phase1, NULL, NULL, APR_HOOK_FIRST);
1782 ap_hook_header_parser (waklog_phase2, NULL, NULL, APR_HOOK_FIRST);
1783 ap_hook_access_checker (waklog_phase3, NULL, NULL, APR_HOOK_FIRST);
1784 ap_hook_type_checker (waklog_phase6, NULL, NULL, APR_HOOK_FIRST);
1785 ap_hook_fixups (waklog_phase7, NULL, NULL, APR_HOOK_FIRST);
1786 ap_hook_log_transaction (waklog_phase9, NULL, NULL, APR_HOOK_FIRST);
1787 ap_hook_child_init (waklog_child_init, NULL, NULL, APR_HOOK_FIRST);
1788 ap_hook_post_read_request (waklog_phase0, NULL, NULL, APR_HOOK_FIRST);
1789 ap_hook_pre_connection (waklog_new_connection, NULL, NULL, APR_HOOK_FIRST);
1790 ap_hook_post_config (waklog_init_handler, NULL, NULL, APR_HOOK_MIDDLE);
1791 }
1792
1793
1794 module AP_MODULE_DECLARE_DATA waklog_module = {
1795 STANDARD20_MODULE_STUFF,
1796 waklog_create_dir_config, /* create per-dir conf structures */
1797 waklog_merge_dir_config, /* merge per-dir conf structures */
1798 waklog_create_server_config, /* create per-server conf structures */
1799 waklog_merge_dir_config, /* merge per-server conf structures */
1800 waklog_cmds, /* table of configuration directives */
1801 waklog_register_hooks /* register hooks */
1802 };
1803 #endif