fewer #includes
[hcoop/debian/libapache-mod-waklog.git] / mod_waklog.c
1 #include "httpd.h"
2 #include "http_config.h"
3 #include "http_conf_globals.h"
4 #include "http_log.h"
5 #include "http_protocol.h"
6 #include "http_request.h"
7 #include "http_core.h"
8 #include "ap_config.h"
9 #include <krb5.h>
10
11 #if defined(sun)
12 #include <sys/ioccom.h>
13 #endif /* sun */
14 #include <stropts.h>
15 #if 0
16 #include <kerberosIV/krb.h>
17 #include <kerberosIV/des.h>
18 #endif /* 0 */
19 #include <afs/venus.h>
20 #include <afs/auth.h>
21 #include <rx/rxkad.h>
22
23 #if 0
24 #include <asm/bitops.h>
25 #include <sys/shm.h>
26 #endif /* 0 */
27
28 #define KEYTAB "/home/drh/keytab.umweb.drhtest"
29 #define KEYTAB_PRINCIPAL "umweb/drhtest"
30
31 #define TKT_LIFE 10*60*60
32 #define SLEEP_TIME 5*60 /* should be TKT_LIFE */
33
34 #define AFS_CELL "umich.edu" /* NB: lower case */
35
36 #define K5PATH "FILE:/tmp/waklog.creds.k5"
37 #define K4PATH "/tmp/waklog.creds.k4"
38
39 module waklog_module;
40
41 struct ClearToken {
42 long AuthHandle;
43 char HandShakeKey[ 8 ];
44 long ViceId;
45 long BeginTimestamp;
46 long EndTimestamp;
47 };
48
49 typedef struct {
50 int configured;
51 int protect;
52 char *keytab;
53 char *keytab_principal;
54 char *afs_cell;
55 } waklog_host_config;
56
57 typedef struct {
58 struct ktc_token token;
59 } waklog_child_config;
60 waklog_child_config child;
61
62
63 static void *
64 waklog_create_dir_config( pool *p, char *path )
65 {
66 waklog_host_config *cfg;
67
68 cfg = (waklog_host_config *)ap_pcalloc( p, sizeof( waklog_host_config ));
69 cfg->configured = 0;
70 cfg->protect = 0;
71 cfg->keytab = KEYTAB;
72 cfg->keytab_principal = KEYTAB_PRINCIPAL;
73 cfg->afs_cell = AFS_CELL;
74
75 return( cfg );
76 }
77
78
79 static void *
80 waklog_create_server_config( pool *p, server_rec *s )
81 {
82 waklog_host_config *cfg;
83
84 cfg = (waklog_host_config *)ap_pcalloc( p, sizeof( waklog_host_config ));
85 cfg->configured = 0;
86 cfg->protect = 0;
87 cfg->keytab = KEYTAB;
88 cfg->keytab_principal = KEYTAB_PRINCIPAL;
89 cfg->afs_cell = AFS_CELL;
90
91 return( cfg );
92 }
93
94
95 static const char *
96 set_waklog_protect( cmd_parms *params, void *mconfig, int flag )
97 {
98 waklog_host_config *cfg;
99
100 if ( params->path == NULL ) {
101 cfg = (waklog_host_config *) ap_get_module_config(
102 params->server->module_config, &waklog_module );
103 } else {
104 cfg = (waklog_host_config *)mconfig;
105 }
106
107 cfg->protect = flag;
108 cfg->configured = 1;
109 return( NULL );
110 }
111
112
113 static const char *
114 set_waklog_use_keytab( cmd_parms *params, void *mconfig, char *file )
115 {
116 waklog_host_config *cfg;
117
118 if ( params->path == NULL ) {
119 cfg = (waklog_host_config *) ap_get_module_config(
120 params->server->module_config, &waklog_module );
121 } else {
122 cfg = (waklog_host_config *)mconfig;
123 }
124
125 ap_log_error( APLOG_MARK, APLOG_INFO|APLOG_NOERRNO, params->server,
126 "mod_waklog: using keytab: %s", file );
127
128 cfg->keytab = file;
129 cfg->configured = 1;
130 return( NULL );
131 }
132
133
134 static const char *
135 set_waklog_use_keytab_principal( cmd_parms *params, void *mconfig, char *file )
136 {
137 waklog_host_config *cfg;
138
139 if ( params->path == NULL ) {
140 cfg = (waklog_host_config *) ap_get_module_config(
141 params->server->module_config, &waklog_module );
142 } else {
143 cfg = (waklog_host_config *)mconfig;
144 }
145
146 ap_log_error( APLOG_MARK, APLOG_INFO|APLOG_NOERRNO, params->server,
147 "mod_waklog: using keytab_principal: %s", file );
148
149 cfg->keytab_principal = file;
150 cfg->configured = 1;
151 return( NULL );
152 }
153
154
155 static const char *
156 set_waklog_use_afs_cell( cmd_parms *params, void *mconfig, char *file )
157 {
158 waklog_host_config *cfg;
159
160 if ( params->path == NULL ) {
161 cfg = (waklog_host_config *) ap_get_module_config(
162 params->server->module_config, &waklog_module );
163 } else {
164 cfg = (waklog_host_config *)mconfig;
165 }
166
167 ap_log_error( APLOG_MARK, APLOG_INFO|APLOG_NOERRNO, params->server,
168 "mod_waklog: using afs_cell: %s", file );
169
170 cfg->afs_cell = file;
171 cfg->configured = 1;
172 return( NULL );
173 }
174
175
176 static void
177 waklog_child_init( server_rec *s, pool *p )
178 {
179
180 memset( &child.token, 0, sizeof( struct ktc_token ) );
181
182 setpag();
183
184 return;
185 }
186
187
188 command_rec waklog_cmds[ ] =
189 {
190 { "WaklogProtected", set_waklog_protect,
191 NULL, RSRC_CONF | ACCESS_CONF, FLAG,
192 "enable waklog on a location or directory basis" },
193
194 { "WaklogUseKeytabPath", set_waklog_use_keytab,
195 NULL, RSRC_CONF, TAKE1,
196 "Use the supplied keytab rather than the default" },
197
198 { "WaklogUseKeytabPrincipal", set_waklog_use_keytab_principal,
199 NULL, RSRC_CONF, TAKE1,
200 "Use the supplied keytab principal rather than the default" },
201
202 { "WaklogUseAFSCell", set_waklog_use_afs_cell,
203 NULL, RSRC_CONF, TAKE1,
204 "Use the supplied AFS cell rather than the default" },
205
206 { NULL }
207 };
208
209
210 static void
211 token_cleanup( void *data )
212 {
213 request_rec *r = (request_rec *)data;
214
215 if ( child.token.ticketLen ) {
216 memset( &child.token, 0, sizeof( struct ktc_token ) );
217
218 ktc_ForgetAllTokens();
219
220 ap_log_error( APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r->server,
221 "mod_waklog: ktc_ForgetAllTokens succeeded: pid: %d", getpid() );
222 }
223 return;
224 }
225
226
227 static int
228 waklog_kinit( server_rec *s )
229 {
230 krb5_error_code kerror;
231 krb5_context kcontext = NULL;
232 krb5_principal kprinc = NULL;
233 krb5_get_init_creds_opt kopts;
234 krb5_creds v5creds;
235 krb5_ccache kccache = NULL;
236 krb5_keytab keytab = NULL;
237 char ktbuf[ MAX_KEYTAB_NAME_LEN + 1 ];
238 waklog_host_config *cfg;
239
240 ap_log_error( APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, s,
241 "mod_waklog: waklog_kinit called" );
242
243 cfg = (waklog_host_config *) ap_get_module_config( s->module_config,
244 &waklog_module );
245
246 if (( kerror = krb5_init_context( &kcontext ))) {
247 ap_log_error( APLOG_MARK, APLOG_ERR, s,
248 (char *)error_message( kerror ));
249
250 goto cleanup;
251 }
252
253 /* use the path */
254 if (( kerror = krb5_cc_resolve( kcontext, K5PATH, &kccache )) != 0 ) {
255 ap_log_error( APLOG_MARK, APLOG_ERR, s,
256 (char *)error_message( kerror ));
257
258 goto cleanup;
259 }
260
261 if (( kerror = krb5_parse_name( kcontext, cfg->keytab_principal, &kprinc ))) {
262 ap_log_error( APLOG_MARK, APLOG_ERR, s,
263 (char *)error_message( kerror ));
264
265 goto cleanup;
266 }
267
268 krb5_get_init_creds_opt_init( &kopts );
269 krb5_get_init_creds_opt_set_tkt_life( &kopts, TKT_LIFE );
270 krb5_get_init_creds_opt_set_renew_life( &kopts, 0 );
271 krb5_get_init_creds_opt_set_forwardable( &kopts, 1 );
272 krb5_get_init_creds_opt_set_proxiable( &kopts, 0 );
273
274 /* keytab from config */
275 strncpy( ktbuf, cfg->keytab, sizeof( ktbuf ) - 1 );
276
277 ap_log_error( APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, s,
278 "mod_waklog: waklog_kinit using: %s", ktbuf );
279
280 if (( kerror = krb5_kt_resolve( kcontext, ktbuf, &keytab )) != 0 ) {
281 ap_log_error( APLOG_MARK, APLOG_ERR, s,
282 (char *)error_message( kerror ));
283
284 goto cleanup;
285 }
286
287 /* get the krbtgt */
288 if (( kerror = krb5_get_init_creds_keytab( kcontext, &v5creds,
289 kprinc, keytab, 0, NULL, &kopts ))) {
290
291 ap_log_error( APLOG_MARK, APLOG_ERR, s,
292 (char *)error_message( kerror ));
293
294 goto cleanup;
295 }
296
297 if (( kerror = krb5_verify_init_creds( kcontext, &v5creds,
298 kprinc, keytab, NULL, NULL )) != 0 ) {
299
300 ap_log_error( APLOG_MARK, APLOG_ERR, s,
301 (char *)error_message( kerror ));
302
303 goto cleanup;
304 }
305
306 if (( kerror = krb5_cc_initialize( kcontext, kccache, kprinc )) != 0 ) {
307 ap_log_error( APLOG_MARK, APLOG_ERR, s,
308 (char *)error_message( kerror ));
309
310 goto cleanup;
311 }
312
313 kerror = krb5_cc_store_cred( kcontext, kccache, &v5creds );
314 krb5_free_cred_contents( kcontext, &v5creds );
315 if ( kerror != 0 ) {
316 ap_log_error( APLOG_MARK, APLOG_ERR, s,
317 (char *)error_message( kerror ));
318
319 goto cleanup;
320 }
321
322 ap_log_error( APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, s,
323 "mod_waklog: waklog_kinit success" );
324
325 cleanup:
326 if ( keytab )
327 (void)krb5_kt_close( kcontext, keytab );
328 if ( kprinc )
329 krb5_free_principal( kcontext, kprinc );
330 if ( kccache )
331 krb5_cc_close( kcontext, kccache );
332 if ( kcontext )
333 krb5_free_context( kcontext );
334
335 ap_log_error( APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, s,
336 "mod_waklog: waklog_kinit: exiting" );
337
338 return( 0 );
339 }
340
341
342 static void
343 waklog_aklog( request_rec *r )
344 {
345 int rc;
346 char buf[ 2048 ];
347 const char *k4path = NULL;
348 const char *k5path = NULL;
349 krb5_error_code kerror;
350 krb5_context kcontext = NULL;
351 krb5_creds increds;
352 krb5_creds *v5credsp = NULL;
353 krb5_ccache kccache = NULL;
354 struct ktc_principal server = { "afs", "", "" };
355 struct ktc_principal client;
356 struct ktc_token token;
357 waklog_host_config *cfg;
358 int buflen;
359
360 k5path = ap_table_get( r->subprocess_env, "KRB5CCNAME" );
361 k4path = ap_table_get( r->subprocess_env, "KRBTKFILE" );
362
363 ap_log_error( APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r->server,
364 "mod_waklog: waklog_aklog called: k5path: %s, k4path: %s", k5path, k4path );
365
366 if ( !k5path || !k4path ) {
367 ap_log_error( APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r->server,
368 "mod_waklog: waklog_aklog giving up" );
369 goto cleanup;
370 }
371
372 /*
373 ** Get/build creds from file/tgs, then see if we need to SetToken
374 */
375
376 if (( kerror = krb5_init_context( &kcontext ))) {
377 /* Authentication Required ( kerberos error ) */
378 ap_log_error( APLOG_MARK, APLOG_ERR, r->server,
379 (char *)error_message( kerror ));
380
381 goto cleanup;
382 }
383
384 memset( (char *)&increds, 0, sizeof(increds));
385
386 cfg = (waklog_host_config *) ap_get_module_config(
387 r->server->module_config, &waklog_module );
388
389 /* afs/<cell> or afs */
390 strncpy( buf, "afs", sizeof( buf ) - 1 );
391 if ( strcmp( cfg->afs_cell, AFS_CELL ) ) {
392 strncat( buf, "/" , sizeof( buf ) - strlen( buf ) - 1 );
393 strncat( buf, cfg->afs_cell, sizeof( buf ) - strlen( buf ) - 1 );
394 }
395
396 /* set server part */
397 if (( kerror = krb5_parse_name( kcontext, buf, &increds.server ))) {
398 ap_log_error( APLOG_MARK, APLOG_ERR, r->server,
399 (char *)error_message( kerror ));
400
401 goto cleanup;
402 }
403
404 if (( kerror = krb5_cc_resolve( kcontext, k5path, &kccache )) != 0 ) {
405 ap_log_error( APLOG_MARK, APLOG_ERR, r->server,
406 (char *)error_message( kerror ));
407
408 goto cleanup;
409 }
410
411 /* set client part */
412 krb5_cc_get_principal( kcontext, kccache, &increds.client );
413
414 increds.times.endtime = 0;
415 /* Ask for DES since that is what V4 understands */
416 increds.keyblock.enctype = ENCTYPE_DES_CBC_CRC;
417
418 /* get the V5 credentials */
419 if (( kerror = krb5_get_credentials( kcontext, 0, kccache,
420 &increds, &v5credsp ) ) ) {
421 ap_log_error( APLOG_MARK, APLOG_ERR, r->server,
422 "mod_waklog: krb5_get_credentials: %s", error_message( kerror ));
423 goto cleanup;
424 }
425
426 /* don't overflor */
427 if ( v5credsp->ticket.length >= 344 ) { /* from krb524d.c */
428 ap_log_error( APLOG_MARK, APLOG_ERR, r->server,
429 "mod_waklog: ticket size (%d) to big to fake", v5credsp->ticket.length );
430 goto cleanup;
431 }
432
433 /* assemble the token */
434 memset( &token, 0, sizeof( struct ktc_token ) );
435
436 token.startTime = v5credsp->times.starttime ? v5credsp->times.starttime : v5credsp->times.authtime;
437 token.endTime = v5credsp->times.endtime;
438 memmove( &token.sessionKey, v5credsp->keyblock.contents, v5credsp->keyblock.length );
439 token.kvno = RXKAD_TKT_TYPE_KERBEROS_V5;
440 token.ticketLen = v5credsp->ticket.length;
441 memmove( token.ticket, v5credsp->ticket.data, token.ticketLen );
442
443 /* make sure we have to do this */
444 if ( child.token.kvno != token.kvno ||
445 child.token.ticketLen != token.ticketLen ||
446 (memcmp( &child.token.sessionKey, &token.sessionKey,
447 sizeof( token.sessionKey ) )) ||
448 (memcmp( child.token.ticket, token.ticket, token.ticketLen )) ) {
449
450 ap_log_error( APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r->server,
451 "mod_waklog: client: %s", buf );
452
453 /* build the name */
454 memmove( buf, v5credsp->client->data[0].data, v5credsp->client->data[0].length );
455 buf[ v5credsp->client->data[0].length ] = '\0';
456 if ( v5credsp->client->length > 1 ) {
457 strncat( buf, ".", sizeof( buf ) - strlen( buf ) - 1 );
458 buflen = strlen( buf );
459 memmove( buf + buflen, v5credsp->client->data[1].data, v5credsp->client->data[1].length );
460 buf[ buflen + v5credsp->client->data[1].length ] = '\0';
461 }
462
463 /* assemble the client */
464 strncpy( client.name, buf, sizeof( client.name ) - 1 );
465 strncpy( client.instance, "", sizeof( client.instance) - 1 );
466 memmove( buf, v5credsp->client->realm.data, v5credsp->client->realm.length );
467 buf[ v5credsp->client->realm.length ] = '\0';
468 strncpy( client.cell, buf, sizeof( client.cell ) - 1 );
469
470 /* assemble the server's cell */
471 strncpy( server.cell, cfg->afs_cell , sizeof( server.cell ) - 1 );
472
473 ap_log_error( APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r->server,
474 "mod_waklog: server: name=%s, instance=%s, cell=%s",
475 server.name, server.instance, server.cell );
476
477 ap_log_error( APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r->server,
478 "mod_waklog: client: name=%s, instance=%s, cell=%s",
479 client.name, client.instance, client.cell );
480
481 /* use the path */
482 krb_set_tkt_string( (char *)k4path );
483
484 /* rumor: we have to do this for AIX 4.1.4 with AFS 3.4+ */
485 write( 2, "", 0 );
486
487 if ( ( rc = ktc_SetToken( &server, &token, &client, 0 ) ) ) {
488 ap_log_error( APLOG_MARK, APLOG_ERR, r->server,
489 "mod_waklog: settoken returned %d", rc );
490 goto cleanup;
491 }
492
493 /* save this */
494 memmove( &child.token, &token, sizeof( struct ktc_token ) );
495
496 /* we'll need to unlog when this connection is done. */
497 ap_register_cleanup( r->pool, (void *)r, token_cleanup, ap_null_cleanup );
498 }
499
500 cleanup:
501 if ( v5credsp )
502 krb5_free_cred_contents( kcontext, v5credsp );
503 if ( increds.client )
504 krb5_free_principal( kcontext, increds.client );
505 if ( increds.server )
506 krb5_free_principal( kcontext, increds.server );
507 if ( kccache )
508 krb5_cc_close( kcontext, kccache );
509 if ( kcontext )
510 krb5_free_context( kcontext );
511
512 ap_log_error( APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r->server,
513 "mod_waklog: finished with waklog_aklog" );
514
515 return;
516
517 }
518
519 static int
520 waklog_child_routine( void *s, child_info *pinfo )
521 {
522 if ( !getuid() ) {
523 ap_log_error( APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, s,
524 "mod_waklog: waklog_child_routine called as root" );
525
526 /* this was causing the credential file to get owned by root */
527 setgid(ap_group_id);
528 setuid(ap_user_id);
529 }
530
531 while( 1 ) {
532 waklog_kinit( s );
533 sleep( SLEEP_TIME );
534 }
535
536 }
537
538
539 static void
540 waklog_init( server_rec *s, pool *p )
541 {
542 extern char *version;
543 int pid;
544
545 ap_log_error( APLOG_MARK, APLOG_INFO|APLOG_NOERRNO, s,
546 "mod_waklog: version %s initialized.", version );
547
548 pid = ap_bspawn_child( p, waklog_child_routine, s, kill_always,
549 NULL, NULL, NULL );
550
551 ap_log_error( APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, s,
552 "mod_waklog: ap_bspawn_child: %d.", pid );
553 }
554
555
556 static int
557 waklog_phase0( request_rec *r )
558 {
559 waklog_host_config *cfg;
560
561 ap_log_error( APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r->server,
562 "mod_waklog: phase0 called" );
563
564 /* directory config? */
565 cfg = (waklog_host_config *)ap_get_module_config(
566 r->per_dir_config, &waklog_module);
567
568 /* server config? */
569 if ( !cfg->configured ) {
570 cfg = (waklog_host_config *)ap_get_module_config(
571 r->server->module_config, &waklog_module);
572 }
573
574 if ( !cfg->protect ) {
575 ap_log_error( APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r->server,
576 "mod_waklog: phase0 declining" );
577 return( DECLINED );
578 }
579
580 /* do this only if we are still unauthenticated */
581 if ( !child.token.ticketLen ) {
582
583 /* set our environment variables */
584 ap_table_set( r->subprocess_env, "KRB5CCNAME", K5PATH );
585 ap_table_set( r->subprocess_env, "KRBTKFILE", K4PATH );
586
587 /* stuff the credentials into the kernel */
588 waklog_aklog( r );
589 }
590
591 ap_log_error( APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r->server,
592 "mod_waklog: phase0 returning" );
593 return DECLINED;
594 }
595
596
597 static int
598 waklog_phase7( request_rec *r )
599 {
600 waklog_host_config *cfg;
601
602 ap_log_error( APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r->server,
603 "mod_waklog: phase7 called" );
604
605 /* directory config? */
606 cfg = (waklog_host_config *)ap_get_module_config(
607 r->per_dir_config, &waklog_module);
608
609 /* server config? */
610 if ( !cfg->configured ) {
611 cfg = (waklog_host_config *)ap_get_module_config(
612 r->server->module_config, &waklog_module);
613 }
614
615 if ( !cfg->protect ) {
616 return( DECLINED );
617 }
618
619 /* stuff the credentials into the kernel */
620 waklog_aklog( r );
621
622 ap_log_error( APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r->server,
623 "mod_waklog: phase7 returning" );
624
625 return DECLINED;
626 }
627
628 static void
629 waklog_new_connection( conn_rec *c ) {
630 ap_log_error( APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, c->server,
631 "mod_waklog: new_connection called: pid: %d", getpid() );
632 return;
633 }
634
635 module MODULE_VAR_EXPORT waklog_module = {
636 STANDARD_MODULE_STUFF,
637 waklog_init, /* module initializer */
638 waklog_create_dir_config, /* create per-dir config structures */
639 NULL, /* merge per-dir config structures */
640 waklog_create_server_config, /* create per-server config structures */
641 NULL, /* merge per-server config structures */
642 waklog_cmds, /* table of config file commands */
643 NULL, /* [#8] MIME-typed-dispatched handlers */
644 NULL, /* [#1] URI to filename translation */
645 NULL, /* [#4] validate user id from request */
646 NULL, /* [#5] check if the user is ok _here_ */
647 NULL, /* [#3] check access by host address */
648 NULL, /* [#6] determine MIME type */
649 waklog_phase7, /* [#7] pre-run fixups */
650 NULL, /* [#9] log a transaction */
651 NULL, /* [#2] header parser */
652 waklog_child_init, /* child_init */
653 NULL, /* child_exit */
654 waklog_phase0 /* [#0] post read-request */
655 #ifdef EAPI
656 ,NULL, /* EAPI: add_module */
657 NULL, /* EAPI: remove_module */
658 NULL, /* EAPI: rewrite_command */
659 waklog_new_connection /* EAPI: new_connection */
660 #endif
661 };