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