2 #include "http_config.h"
3 #include "http_conf_globals.h"
5 #include "http_protocol.h"
6 #include "http_request.h"
12 #include <sys/ioccom.h>
15 #include <kerberosIV/krb.h>
16 #include <kerberosIV/des.h>
17 #include <afs/venus.h>
21 #include <asm/bitops.h>
24 #define KEYTAB_PATH "/home/drh/keytab.umweb.drhtest"
25 #define PRINCIPAL "umweb/drhtest"
27 #define IN_TKT_SERVICE "krbtgt/UMICH.EDU"
29 #define K5PATH "FILE:/tmp/waklog.creds.k5"
30 #define K4PATH "/tmp/waklog.creds.k4"
36 char HandShakeKey
[ 8 ];
46 char *keytab_principal
;
51 struct ktc_token token
;
52 } waklog_child_config
;
53 waklog_child_config
*child
= NULL
;
57 waklog_create_dir_config( pool
*p
, char *path
)
59 waklog_host_config
*cfg
;
61 cfg
= (waklog_host_config
*)ap_pcalloc( p
, sizeof( waklog_host_config
));
65 cfg
->keytab_principal
= 0;
66 cfg
->afs_instance
= 0;
73 waklog_create_server_config( pool
*p
, server_rec
*s
)
75 waklog_host_config
*cfg
;
77 cfg
= (waklog_host_config
*)ap_pcalloc( p
, sizeof( waklog_host_config
));
81 cfg
->keytab_principal
= 0;
82 cfg
->afs_instance
= 0;
89 set_waklog_protect( cmd_parms
*params
, void *mconfig
, int flag
)
91 waklog_host_config
*cfg
;
93 if ( params
->path
== NULL
) {
94 cfg
= (waklog_host_config
*) ap_get_module_config(
95 params
->server
->module_config
, &waklog_module
);
97 cfg
= (waklog_host_config
*)mconfig
;
107 set_waklog_use_keytab( cmd_parms
*params
, void *mconfig
, char *file
)
109 waklog_host_config
*cfg
;
111 if ( params
->path
== NULL
) {
112 cfg
= (waklog_host_config
*) ap_get_module_config(
113 params
->server
->module_config
, &waklog_module
);
115 cfg
= (waklog_host_config
*)mconfig
;
118 ap_log_error( APLOG_MARK
, APLOG_INFO
|APLOG_NOERRNO
, params
->server
,
119 "mod_waklog: using keytab: %s", file
);
128 waklog_child_init( server_rec
*s
, pool
*p
)
131 if ( child
== NULL
) {
132 child
= (waklog_child_config
*) ap_palloc( p
, sizeof( waklog_child_config
) );
135 memset( &child
->token
, 0, sizeof( struct ktc_token
) );
143 command_rec waklog_cmds
[ ] =
145 { "WaklogProtected", set_waklog_protect
,
146 NULL
, RSRC_CONF
| ACCESS_CONF
, FLAG
,
147 "enable waklog on a location or directory basis" },
149 { "WaklogUseKeytab", set_waklog_use_keytab
,
150 NULL
, RSRC_CONF
, TAKE1
,
151 "Use the supplied keytab file rather than the user's TGT" },
158 pioctl_cleanup( void *data
)
160 request_rec
*r
= (request_rec
*)data
;
162 if ( child
->token
.ticketLen
) {
163 memset( &child
->token
, 0, sizeof( struct ktc_token
) );
165 ktc_ForgetAllTokens();
167 ap_log_error( APLOG_MARK
, APLOG_NOERRNO
|APLOG_ERR
, r
->server
,
168 "mod_waklog: ktc_ForgetAllTokens succeeded" );
175 waklog_ktinit( server_rec
*s
)
177 krb5_error_code kerror
;
178 krb5_context kcontext
;
179 krb5_principal kprinc
;
180 krb5_get_init_creds_opt kopts
;
184 krb5_keytab keytab
= 0;
185 char ktbuf
[ MAX_KEYTAB_NAME_LEN
+ 1 ];
187 waklog_host_config
*cfg
;
189 ap_log_error( APLOG_MARK
, APLOG_NOERRNO
|APLOG_ERR
, s
,
190 "mod_waklog: waklog_ktinit called" );
192 if (( kerror
= krb5_init_context( &kcontext
))) {
193 ap_log_error( APLOG_MARK
, APLOG_ERR
, s
,
194 (char *)error_message( kerror
));
200 if (( kerror
= krb5_cc_resolve( kcontext
, K5PATH
, &kccache
)) != 0 ) {
201 ap_log_error( APLOG_MARK
, APLOG_ERR
, s
,
202 (char *)error_message( kerror
));
207 if (( kerror
= krb5_parse_name( kcontext
, PRINCIPAL
, &kprinc
))) {
208 ap_log_error( APLOG_MARK
, APLOG_ERR
, s
,
209 (char *)error_message( kerror
));
214 krb5_get_init_creds_opt_init( &kopts
);
215 krb5_get_init_creds_opt_set_tkt_life( &kopts
, 10*60*60 );
216 krb5_get_init_creds_opt_set_renew_life( &kopts
, 0 );
217 krb5_get_init_creds_opt_set_forwardable( &kopts
, 1 );
218 krb5_get_init_creds_opt_set_proxiable( &kopts
, 0 );
220 cfg
= (waklog_host_config
*) ap_get_module_config( s
->module_config
,
223 /* which keytab should we use? */
224 strcpy( ktbuf
, cfg
->keytab
? cfg
->keytab
: KEYTAB_PATH
);
226 if ( strlen( ktbuf
) > MAX_KEYTAB_NAME_LEN
) {
227 ap_log_error( APLOG_MARK
, APLOG_ERR
, s
,
228 "server configuration error" );
233 ap_log_error( APLOG_MARK
, APLOG_NOERRNO
|APLOG_ERR
, s
,
234 "mod_waklog: waklog_ktinit using: %s", ktbuf
);
236 if (( kerror
= krb5_kt_resolve( kcontext
, ktbuf
, &keytab
)) != 0 ) {
237 ap_log_error( APLOG_MARK
, APLOG_ERR
, s
,
238 (char *)error_message( kerror
));
244 if (( kerror
= krb5_get_init_creds_keytab( kcontext
, &v5creds
,
245 kprinc
, keytab
, 0, IN_TKT_SERVICE
, &kopts
))) {
247 ap_log_error( APLOG_MARK
, APLOG_ERR
, s
,
248 (char *)error_message( kerror
));
253 if (( kerror
= krb5_verify_init_creds( kcontext
, &v5creds
,
254 kprinc
, keytab
, NULL
, NULL
)) != 0 ) {
256 ap_log_error( APLOG_MARK
, APLOG_ERR
, s
,
257 (char *)error_message( kerror
));
262 if (( kerror
= krb5_cc_initialize( kcontext
, kccache
, kprinc
)) != 0 ) {
263 ap_log_error( APLOG_MARK
, APLOG_ERR
, s
,
264 (char *)error_message( kerror
));
269 if (( kerror
= krb5_cc_store_cred( kcontext
, kccache
, &v5creds
)) != 0 ) {
270 ap_log_error( APLOG_MARK
, APLOG_ERR
, s
,
271 (char *)error_message( kerror
));
276 ap_log_error( APLOG_MARK
, APLOG_NOERRNO
|APLOG_ERR
, s
,
277 "mod_waklog: waklog_ktinit success" );
279 cleanup6
: if ( v5creds
.client
== kprinc
) {
282 krb5_free_cred_contents( kcontext
, &v5creds
);
283 cleanup5
: (void)krb5_kt_close( kcontext
, keytab
);
284 cleanup4
: krb5_free_principal( kcontext
, kprinc
);
285 cleanup3
: krb5_cc_close( kcontext
, kccache
);
286 cleanup2
: krb5_free_context( kcontext
);
289 ap_log_error( APLOG_MARK
, APLOG_NOERRNO
|APLOG_ERR
, s
,
290 "mod_waklog: waklog_ktinit: exiting" );
297 waklog_aklog( request_rec
*r
)
301 const char *k4path
= NULL
;
302 const char *k5path
= NULL
;
303 krb5_error_code kerror
;
304 krb5_context kcontext
;
306 krb5_creds
*v5credsp
= NULL
;
309 struct ktc_principal server
= { "afs", "", "umich.edu" };
310 struct ktc_principal client
;
311 struct ktc_token token
;
313 k5path
= ap_table_get( r
->subprocess_env
, "KRB5CCNAME" );
314 k4path
= ap_table_get( r
->subprocess_env
, "KRBTKFILE" );
316 ap_log_error( APLOG_MARK
, APLOG_NOERRNO
|APLOG_ERR
, r
->server
,
317 "mod_waklog: waklog_aklog called: k5path: %s, k4path: %s", k5path
, k4path
);
319 if ( !k5path
|| !k4path
) {
320 ap_log_error( APLOG_MARK
, APLOG_NOERRNO
|APLOG_ERR
, r
->server
,
321 "mod_waklog: waklog_aklog giving up" );
326 ** Get/build creds from file/tgs, then see if we need to SetToken
329 if (( kerror
= krb5_init_context( &kcontext
))) {
330 /* Authentication Required ( kerberos error ) */
331 ap_log_error( APLOG_MARK
, APLOG_ERR
, r
->server
,
332 (char *)error_message( kerror
));
337 memset( (char *)&increds
, 0, sizeof(increds
));
339 /* set server part */
340 if (( kerror
= krb5_parse_name( kcontext
, AFS
, &increds
.server
))) {
341 ap_log_error( APLOG_MARK
, APLOG_ERR
, r
->server
,
342 (char *)error_message( kerror
));
347 if (( kerror
= krb5_cc_resolve( kcontext
, k5path
, &kccache
)) != 0 ) {
348 ap_log_error( APLOG_MARK
, APLOG_ERR
, r
->server
,
349 (char *)error_message( kerror
));
354 /* set client part */
355 krb5_cc_get_principal( kcontext
, kccache
, &increds
.client
);
357 increds
.times
.endtime
= 0;
358 /* Ask for DES since that is what V4 understands */
359 increds
.keyblock
.enctype
= ENCTYPE_DES_CBC_CRC
;
361 /* get the V5 credentials */
362 if (( kerror
= krb5_get_credentials( kcontext
, 0, kccache
,
363 &increds
, &v5credsp
) ) ) {
364 ap_log_error( APLOG_MARK
, APLOG_NOERRNO
|APLOG_ERR
, r
->server
,
365 "mod_waklog: krb5_get_credentials: %s", krb_err_txt
[ kerror
] );
369 /* get the V4 credentials */
370 if (( kerror
= krb524_convert_creds_kdc( kcontext
, v5credsp
, &v4creds
) ) ) {
371 ap_log_error( APLOG_MARK
, APLOG_NOERRNO
|APLOG_ERR
, r
->server
,
372 "mod_waklog: krb524_convert_creds_kdc: %s", krb_err_txt
[ kerror
] );
376 /* assemble the token */
377 token
.kvno
= v4creds
.kvno
;
378 token
.startTime
= v4creds
.issue_date
;
379 token
.endTime
= v5credsp
->times
.endtime
;
380 memmove( &token
.sessionKey
, v4creds
.session
, 8 );
381 token
.ticketLen
= v4creds
.ticket_st
.length
;
382 memmove( token
.ticket
, v4creds
.ticket_st
.dat
, token
.ticketLen
);
384 /* make sure we have to do this */
385 if ( child
->token
.kvno
!= token
.kvno
||
386 child
->token
.ticketLen
!= token
.ticketLen
||
387 memcmp( &child
->token
.sessionKey
, &token
.sessionKey
,
388 sizeof( token
.sessionKey
) ) ||
389 memcmp( child
->token
.ticket
, token
.ticket
, token
.ticketLen
) ) {
391 ap_log_error( APLOG_MARK
, APLOG_NOERRNO
|APLOG_ERR
, r
->server
,
392 "mod_waklog: %s.%s@%s", v4creds
.service
, v4creds
.instance
,
394 ap_log_error( APLOG_MARK
, APLOG_NOERRNO
|APLOG_ERR
, r
->server
,
395 "mod_waklog: %d %d %d", v4creds
.lifetime
, v4creds
.kvno
,
396 v4creds
.issue_date
);
397 ap_log_error( APLOG_MARK
, APLOG_NOERRNO
|APLOG_ERR
, r
->server
,
398 "mod_waklog: %s %s", v4creds
.pname
, v4creds
.pinst
);
399 ap_log_error( APLOG_MARK
, APLOG_NOERRNO
|APLOG_ERR
, r
->server
,
400 "mod_waklog: %d", v4creds
.ticket_st
.length
);
403 strcpy( buf
, v4creds
.pname
);
404 if ( v4creds
.pinst
[ 0 ] ) {
406 strcat( buf
, v4creds
.pinst
);
409 /* assemble the client */
410 strncpy( client
.name
, buf
, MAXKTCNAMELEN
- 1 );
411 strcpy( client
.instance
, "" );
412 strncpy( client
.cell
, v4creds
.realm
, MAXKTCNAMELEN
- 1 );
414 ap_log_error( APLOG_MARK
, APLOG_NOERRNO
|APLOG_ERR
, r
->server
,
415 "mod_waklog: server: name=%s, instance=%s, cell=%s",
416 server
.name
, server
.instance
, server
.cell
);
418 ap_log_error( APLOG_MARK
, APLOG_NOERRNO
|APLOG_ERR
, r
->server
,
419 "mod_waklog: client: name=%s, instance=%s, cell=%s",
420 client
.name
, client
.instance
, client
.cell
);
423 krb_set_tkt_string( (char *)k4path
);
425 /* rumor: we have to do this for AIX 4.1.4 with AFS 3.4+ */
428 if ( ( rc
= ktc_SetToken( &server
, &token
, &client
, 0 ) ) ) {
429 ap_log_error( APLOG_MARK
, APLOG_NOERRNO
|APLOG_ERR
, r
->server
,
430 "mod_waklog: settoken returned %d", rc
);
434 memmove( &child
->token
, &token
, sizeof( struct ktc_token
) );
436 /* we'll need to unlog when this connection is done. */
437 ap_register_cleanup( r
->pool
, (void *)r
, pioctl_cleanup
, ap_null_cleanup
);
440 krb5_free_cred_contents( kcontext
, v5credsp
);
441 krb5_free_principal( kcontext
, increds
.client
);
442 krb5_cc_close( kcontext
, kccache
);
443 krb5_free_context( kcontext
);
445 ap_log_error( APLOG_MARK
, APLOG_NOERRNO
|APLOG_ERR
, r
->server
,
446 "mod_waklog: finished with waklog_aklog" );
451 waklog_child_routine( void *s
, child_info
*pinfo
)
454 ap_log_error( APLOG_MARK
, APLOG_ERR
|APLOG_NOERRNO
, s
,
455 "mod_waklog: waklog_child_routine called as root" );
457 /* this was causing the credential file to get owned by root */
464 sleep( 300 /* 10*60*60 - 5*60 */ );
471 waklog_init( server_rec
*s
, pool
*p
)
473 extern char *version
;
476 ap_log_error( APLOG_MARK
, APLOG_INFO
|APLOG_NOERRNO
, s
,
477 "mod_waklog: version %s initialized.", version
);
479 pid
= ap_bspawn_child( p
, waklog_child_routine
, s
, kill_always
,
482 ap_log_error( APLOG_MARK
, APLOG_ERR
|APLOG_NOERRNO
, s
,
483 "mod_waklog: ap_bspawn_child: %d.", pid
);
488 waklog_phase0( request_rec
*r
)
490 waklog_host_config
*cfg
;
492 ap_log_error( APLOG_MARK
, APLOG_NOERRNO
|APLOG_ERR
, r
->server
,
493 "mod_waklog: phase0 called" );
495 /* directory config? */
496 cfg
= (waklog_host_config
*)ap_get_module_config(
497 r
->per_dir_config
, &waklog_module
);
500 if ( !cfg
->configured
) {
501 cfg
= (waklog_host_config
*)ap_get_module_config(
502 r
->server
->module_config
, &waklog_module
);
505 if ( !cfg
->protect
) {
506 ap_log_error( APLOG_MARK
, APLOG_NOERRNO
|APLOG_ERR
, r
->server
,
507 "mod_waklog: phase0 declining" );
511 /* do this only if we are still unauthenticated */
512 if ( !child
->token
.ticketLen
) {
514 /* set our environment variables */
515 ap_table_set( r
->subprocess_env
, "KRB5CCNAME", K5PATH
);
516 ap_table_set( r
->subprocess_env
, "KRBTKFILE", K4PATH
);
518 /* stuff the credentials into the kernel */
522 ap_log_error( APLOG_MARK
, APLOG_NOERRNO
|APLOG_ERR
, r
->server
,
523 "mod_waklog: phase0 returning" );
529 waklog_phase7( request_rec
*r
)
531 waklog_host_config
*cfg
;
533 ap_log_error( APLOG_MARK
, APLOG_NOERRNO
|APLOG_ERR
, r
->server
,
534 "mod_waklog: phase7 called" );
536 /* directory config? */
537 cfg
= (waklog_host_config
*)ap_get_module_config(
538 r
->per_dir_config
, &waklog_module
);
541 if ( !cfg
->configured
) {
542 cfg
= (waklog_host_config
*)ap_get_module_config(
543 r
->server
->module_config
, &waklog_module
);
546 if ( !cfg
->protect
) {
550 /* stuff the credentials into the kernel */
553 ap_log_error( APLOG_MARK
, APLOG_NOERRNO
|APLOG_ERR
, r
->server
,
554 "mod_waklog: phase7 returning" );
560 waklog_new_connection( conn_rec
*c
) {
561 ap_log_error( APLOG_MARK
, APLOG_NOERRNO
|APLOG_ERR
, c
->server
,
562 "mod_waklog: new_connection called: conn_rec: 0x%08x pid: %d", c
, getpid() );
566 module MODULE_VAR_EXPORT waklog_module
= {
567 STANDARD_MODULE_STUFF
,
568 waklog_init
, /* module initializer */
569 waklog_create_dir_config
, /* create per-dir config structures */
570 NULL
, /* merge per-dir config structures */
571 waklog_create_server_config
, /* create per-server config structures */
572 NULL
, /* merge per-server config structures */
573 waklog_cmds
, /* table of config file commands */
574 NULL
, /* [#8] MIME-typed-dispatched handlers */
575 NULL
, /* [#1] URI to filename translation */
576 NULL
, /* [#4] validate user id from request */
577 NULL
, /* [#5] check if the user is ok _here_ */
578 NULL
, /* [#3] check access by host address */
579 NULL
, /* [#6] determine MIME type */
580 waklog_phase7
, /* [#7] pre-run fixups */
581 NULL
, /* [#9] log a transaction */
582 NULL
, /* [#2] header parser */
583 waklog_child_init
, /* child_init */
584 NULL
, /* child_exit */
585 waklog_phase0
/* [#0] post read-request */
587 ,NULL
, /* EAPI: add_module */
588 NULL
, /* EAPI: remove_module */
589 NULL
, /* EAPI: rewrite_command */
590 waklog_new_connection
/* EAPI: new_connection */