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
));
277 /* convert K5 => K4 */
278 ap_log_error( APLOG_MARK
, APLOG_NOERRNO
|APLOG_ERR
, s
,
279 "mod_waklog: before krb524_convert_creds" );
281 if (( kerror
= krb524_convert_creds_kdc( kcontext
,
282 &v5creds
, &v4creds
)) != 0 ) {
284 ap_log_error( APLOG_MARK
, APLOG_ERR
, s
,
285 (char *)error_message( kerror
));
291 krb_set_tkt_string( (char *)K4PATH
);
293 /* initialize ticket cache */
294 ap_log_error( APLOG_MARK
, APLOG_NOERRNO
|APLOG_ERR
, s
,
295 "mod_waklog: before in_tkt" );
297 if (( kerror
= in_tkt( v4creds
.pname
, v4creds
.pinst
)) != KSUCCESS
) {
298 ap_log_error( APLOG_MARK
, APLOG_ERR
, s
,
299 (char *)error_message( kerror
));
304 /* stash, ticket, session key, etc for future use */
305 ap_log_error( APLOG_MARK
, APLOG_NOERRNO
|APLOG_ERR
, s
,
306 "mod_waklog: before krb_save_credentials" );
308 if (( kerror
= krb_save_credentials( v4creds
.service
,
309 v4creds
.instance
, v4creds
.realm
, v4creds
.session
,
310 v4creds
.lifetime
, v4creds
.kvno
, &(v4creds
.ticket_st
),
311 v4creds
.issue_date
)) != 0 ) {
313 ap_log_error( APLOG_MARK
, APLOG_ERR
, s
,
314 (char *)error_message( kerror
));
320 ap_log_error( APLOG_MARK
, APLOG_NOERRNO
|APLOG_ERR
, s
,
321 "mod_waklog: waklog_ktinit success" );
323 cleanup6
: if ( v5creds
.client
== kprinc
) {
326 krb5_free_cred_contents( kcontext
, &v5creds
);
327 cleanup5
: (void)krb5_kt_close( kcontext
, keytab
);
328 cleanup4
: krb5_free_principal( kcontext
, kprinc
);
329 cleanup3
: krb5_cc_close( kcontext
, kccache
);
330 cleanup2
: krb5_free_context( kcontext
);
333 ap_log_error( APLOG_MARK
, APLOG_NOERRNO
|APLOG_ERR
, s
,
334 "mod_waklog: waklog_ktinit: exiting" );
341 waklog_aklog( request_rec
*r
)
345 const char *k4path
= NULL
;
346 const char *k5path
= NULL
;
347 krb5_error_code kerror
;
348 krb5_context kcontext
;
350 krb5_creds
*v5credsp
= NULL
;
353 struct ktc_principal server
= { "afs", "", "umich.edu" };
354 struct ktc_principal client
;
355 struct ktc_token token
;
357 k5path
= ap_table_get( r
->subprocess_env
, "KRB5CCNAME" );
358 k4path
= ap_table_get( r
->subprocess_env
, "KRBTKFILE" );
360 ap_log_error( APLOG_MARK
, APLOG_NOERRNO
|APLOG_ERR
, r
->server
,
361 "mod_waklog: waklog_aklog called: k5path: %s, k4path: %s", k5path
, k4path
);
363 if ( !k5path
|| !k4path
) {
364 ap_log_error( APLOG_MARK
, APLOG_NOERRNO
|APLOG_ERR
, r
->server
,
365 "mod_waklog: waklog_aklog giving up" );
370 ** Get/build creds from file/tgs, then see if we need to SetToken
373 if (( kerror
= krb5_init_context( &kcontext
))) {
374 /* Authentication Required ( kerberos error ) */
375 ap_log_error( APLOG_MARK
, APLOG_ERR
, r
->server
,
376 (char *)error_message( kerror
));
381 memset( (char *)&increds
, 0, sizeof(increds
));
383 /* set server part */
384 if (( kerror
= krb5_parse_name( kcontext
, AFS
, &increds
.server
))) {
385 ap_log_error( APLOG_MARK
, APLOG_ERR
, r
->server
,
386 (char *)error_message( kerror
));
391 if (( kerror
= krb5_cc_resolve( kcontext
, k5path
, &kccache
)) != 0 ) {
392 ap_log_error( APLOG_MARK
, APLOG_ERR
, r
->server
,
393 (char *)error_message( kerror
));
398 /* set client part */
399 krb5_cc_get_principal( kcontext
, kccache
, &increds
.client
);
401 increds
.times
.endtime
= 0;
402 /* Ask for DES since that is what V4 understands */
403 increds
.keyblock
.enctype
= ENCTYPE_DES_CBC_CRC
;
405 /* get the V5 credentials */
406 if (( kerror
= krb5_get_credentials( kcontext
, 0, kccache
,
407 &increds
, &v5credsp
) ) ) {
408 ap_log_error( APLOG_MARK
, APLOG_NOERRNO
|APLOG_ERR
, r
->server
,
409 "mod_waklog: krb5_get_credentials: %s", krb_err_txt
[ kerror
] );
413 /* get the V4 credentials */
414 if (( kerror
= krb524_convert_creds_kdc( kcontext
, v5credsp
, &v4creds
) ) ) {
415 ap_log_error( APLOG_MARK
, APLOG_NOERRNO
|APLOG_ERR
, r
->server
,
416 "mod_waklog: krb524_convert_creds_kdc: %s", krb_err_txt
[ kerror
] );
420 /* assemble the token */
421 token
.kvno
= v4creds
.kvno
;
422 token
.startTime
= v4creds
.issue_date
;
423 token
.endTime
= v5credsp
->times
.endtime
;
424 memmove( &token
.sessionKey
, v4creds
.session
, 8 );
425 token
.ticketLen
= v4creds
.ticket_st
.length
;
426 memmove( token
.ticket
, v4creds
.ticket_st
.dat
, token
.ticketLen
);
428 /* make sure we have to do this */
429 if ( child
->token
.kvno
!= token
.kvno
||
430 child
->token
.ticketLen
!= token
.ticketLen
||
431 memcmp( &child
->token
.sessionKey
, &token
.sessionKey
,
432 sizeof( token
.sessionKey
) ) ||
433 memcmp( child
->token
.ticket
, token
.ticket
, token
.ticketLen
) ) {
435 ap_log_error( APLOG_MARK
, APLOG_NOERRNO
|APLOG_ERR
, r
->server
,
436 "mod_waklog: %s.%s@%s", v4creds
.service
, v4creds
.instance
,
438 ap_log_error( APLOG_MARK
, APLOG_NOERRNO
|APLOG_ERR
, r
->server
,
439 "mod_waklog: %d %d %d", v4creds
.lifetime
, v4creds
.kvno
,
440 v4creds
.issue_date
);
441 ap_log_error( APLOG_MARK
, APLOG_NOERRNO
|APLOG_ERR
, r
->server
,
442 "mod_waklog: %s %s", v4creds
.pname
, v4creds
.pinst
);
443 ap_log_error( APLOG_MARK
, APLOG_NOERRNO
|APLOG_ERR
, r
->server
,
444 "mod_waklog: %d", v4creds
.ticket_st
.length
);
447 strcpy( buf
, v4creds
.pname
);
448 if ( v4creds
.pinst
[ 0 ] ) {
450 strcat( buf
, v4creds
.pinst
);
453 /* assemble the client */
454 strncpy( client
.name
, buf
, MAXKTCNAMELEN
- 1 );
455 strcpy( client
.instance
, "" );
456 strncpy( client
.cell
, v4creds
.realm
, MAXKTCNAMELEN
- 1 );
458 ap_log_error( APLOG_MARK
, APLOG_NOERRNO
|APLOG_ERR
, r
->server
,
459 "mod_waklog: server: name=%s, instance=%s, cell=%s",
460 server
.name
, server
.instance
, server
.cell
);
462 ap_log_error( APLOG_MARK
, APLOG_NOERRNO
|APLOG_ERR
, r
->server
,
463 "mod_waklog: client: name=%s, instance=%s, cell=%s",
464 client
.name
, client
.instance
, client
.cell
);
467 krb_set_tkt_string( (char *)k4path
);
469 /* rumor: we have to do this for AIX 4.1.4 with AFS 3.4+ */
472 if ( ( rc
= ktc_SetToken( &server
, &token
, &client
, 0 ) ) ) {
473 ap_log_error( APLOG_MARK
, APLOG_NOERRNO
|APLOG_ERR
, r
->server
,
474 "mod_waklog: settoken returned %d", rc
);
478 memmove( &child
->token
, &token
, sizeof( struct ktc_token
) );
480 /* we'll need to unlog when this connection is done. */
481 ap_register_cleanup( r
->pool
, (void *)r
, pioctl_cleanup
, ap_null_cleanup
);
484 krb5_free_cred_contents( kcontext
, v5credsp
);
485 krb5_free_principal( kcontext
, increds
.client
);
486 krb5_cc_close( kcontext
, kccache
);
487 krb5_free_context( kcontext
);
489 ap_log_error( APLOG_MARK
, APLOG_NOERRNO
|APLOG_ERR
, r
->server
,
490 "mod_waklog: finished with waklog_aklog" );
495 waklog_child_routine( void *s
, child_info
*pinfo
)
497 ap_log_error( APLOG_MARK
, APLOG_INFO
|APLOG_NOERRNO
, s
,
498 "mod_waklog: waklog_child_routine called" );
501 ap_log_error( APLOG_MARK
, APLOG_INFO
|APLOG_NOERRNO
, s
,
502 "mod_waklog: waklog_child_routine called as root" );
504 /* this was causing the credential file to get owned by root */
511 sleep( 60 /* 10*60*60 - 5*60 */ );
518 waklog_init( server_rec
*s
, pool
*p
)
520 extern char *version
;
523 ap_log_error( APLOG_MARK
, APLOG_INFO
|APLOG_NOERRNO
, s
,
524 "mod_waklog: version %s initialized.", version
);
526 pid
= ap_bspawn_child( p
, waklog_child_routine
, s
, kill_always
,
529 ap_log_error( APLOG_MARK
, APLOG_INFO
|APLOG_NOERRNO
, s
,
530 "mod_waklog: ap_bspawn_child: %d.", pid
);
535 waklog_phase0( request_rec
*r
)
537 waklog_host_config
*cfg
;
539 ap_log_error( APLOG_MARK
, APLOG_NOERRNO
|APLOG_ERR
, r
->server
,
540 "mod_waklog: phase0 called" );
542 /* directory config? */
543 cfg
= (waklog_host_config
*)ap_get_module_config(
544 r
->per_dir_config
, &waklog_module
);
547 if ( !cfg
->configured
) {
548 cfg
= (waklog_host_config
*)ap_get_module_config(
549 r
->server
->module_config
, &waklog_module
);
552 if ( !cfg
->protect
) {
553 ap_log_error( APLOG_MARK
, APLOG_NOERRNO
|APLOG_ERR
, r
->server
,
554 "mod_waklog: phase0 declining" );
558 /* do this only if we are still unauthenticated */
559 if ( !child
->token
.ticketLen
) {
561 /* set our environment variables */
562 ap_table_set( r
->subprocess_env
, "KRB5CCNAME", K5PATH
);
563 ap_table_set( r
->subprocess_env
, "KRBTKFILE", K4PATH
);
565 /* stuff the credentials into the kernel */
569 ap_log_error( APLOG_MARK
, APLOG_NOERRNO
|APLOG_ERR
, r
->server
,
570 "mod_waklog: phase0 returning" );
576 waklog_phase7( request_rec
*r
)
578 waklog_host_config
*cfg
;
580 ap_log_error( APLOG_MARK
, APLOG_NOERRNO
|APLOG_ERR
, r
->server
,
581 "mod_waklog: phase7 called" );
583 /* directory config? */
584 cfg
= (waklog_host_config
*)ap_get_module_config(
585 r
->per_dir_config
, &waklog_module
);
588 if ( !cfg
->configured
) {
589 cfg
= (waklog_host_config
*)ap_get_module_config(
590 r
->server
->module_config
, &waklog_module
);
593 if ( !cfg
->protect
) {
597 /* stuff the credentials into the kernel */
600 ap_log_error( APLOG_MARK
, APLOG_NOERRNO
|APLOG_ERR
, r
->server
,
601 "mod_waklog: phase7 returning" );
607 waklog_new_connection( conn_rec
*c
) {
608 ap_log_error( APLOG_MARK
, APLOG_NOERRNO
|APLOG_ERR
, c
->server
,
609 "mod_waklog: new_connection called: conn_rec: 0x%08x pid: %d", c
, getpid() );
613 module MODULE_VAR_EXPORT waklog_module
= {
614 STANDARD_MODULE_STUFF
,
615 waklog_init
, /* module initializer */
616 waklog_create_dir_config
, /* create per-dir config structures */
617 NULL
, /* merge per-dir config structures */
618 waklog_create_server_config
, /* create per-server config structures */
619 NULL
, /* merge per-server config structures */
620 waklog_cmds
, /* table of config file commands */
621 NULL
, /* [#8] MIME-typed-dispatched handlers */
622 NULL
, /* [#1] URI to filename translation */
623 NULL
, /* [#4] validate user id from request */
624 NULL
, /* [#5] check if the user is ok _here_ */
625 NULL
, /* [#3] check access by host address */
626 NULL
, /* [#6] determine MIME type */
627 waklog_phase7
, /* [#7] pre-run fixups */
628 NULL
, /* [#9] log a transaction */
629 NULL
, /* [#2] header parser */
630 waklog_child_init
, /* child_init */
631 NULL
, /* child_exit */
632 waklog_phase0
/* [#0] post read-request */
634 ,NULL
, /* EAPI: add_module */
635 NULL
, /* EAPI: remove_module */
636 NULL
, /* EAPI: rewrite_command */
637 waklog_new_connection
/* EAPI: new_connection */