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 krb5_timestamp endtime
; /* time krbtgt expires */
52 int getting_tgt
; /* TAS flag, protecting above */
54 waklog_mod_config
*mod
= NULL
;
59 struct ktc_token token
;
60 } waklog_child_config
;
61 waklog_child_config
*child
= NULL
;
64 waklog_create_dir_config( pool
*p
, char *path
)
66 waklog_host_config
*cfg
;
68 cfg
= (waklog_host_config
*)ap_pcalloc( p
, sizeof( waklog_host_config
));
72 cfg
->keytab_principal
= 0;
73 cfg
->afs_instance
= 0;
80 waklog_create_server_config( pool
*p
, server_rec
*s
)
82 waklog_host_config
*cfg
;
84 cfg
= (waklog_host_config
*)ap_pcalloc( p
, sizeof( waklog_host_config
));
88 cfg
->keytab_principal
= 0;
89 cfg
->afs_instance
= 0;
96 waklog_init( server_rec
*s
, pool
*p
)
99 static key_t shmkey
= IPC_PRIVATE
;
100 struct shmid_ds shmbuf
;
102 ap_log_error( APLOG_MARK
, APLOG_INFO
|APLOG_NOERRNO
, s
,
103 "mod_waklog: version %s initialized.", version
);
106 if (( shmid
= shmget( shmkey
, sizeof( waklog_mod_config
),
107 IPC_CREAT
| SHM_R
| SHM_W
)) == -1 ) {
108 ap_log_error( APLOG_MARK
, APLOG_ERR
, s
,
109 "mod_waklog: shmget failed" );
113 ap_log_error( APLOG_MARK
, APLOG_INFO
|APLOG_NOERRNO
, s
,
114 "mod_waklog: waklog_init: created shared memory segment %d", shmid
);
116 if (( mod
= (waklog_mod_config
*) shmat( shmid
, 0, 0 ) ) ==
117 (waklog_mod_config
*) -1 ) {
118 ap_log_error( APLOG_MARK
, APLOG_ERR
, s
,
119 "mod_waklog: shmat failed" );
120 /* we'll exit after removing the segment */
123 if ( shmctl( shmid
, IPC_STAT
, &shmbuf
) != 0 ) {
124 ap_log_error( APLOG_MARK
, APLOG_ERR
, s
,
125 "mod_waklog: shmctl failed to stat" );
128 shmbuf
.shm_perm
.uid
= ap_user_id
;
129 shmbuf
.shm_perm
.gid
= ap_group_id
;
131 if ( shmctl( shmid
, IPC_SET
, &shmbuf
) != 0 ) {
132 ap_log_error( APLOG_MARK
, APLOG_ERR
, s
,
133 "mod_waklog: shmctl failed to set" );
139 if ( shmctl( shmid
, IPC_RMID
, NULL
) != 0 ) {
140 ap_log_error( APLOG_MARK
, APLOG_ERR
, s
,
141 "mod_waklog: shmctl failed to remove" );
144 if ( mod
== (waklog_mod_config
*) -1 ) {
150 mod
->getting_tgt
= 0;
157 set_waklog_protect( cmd_parms
*params
, void *mconfig
, int flag
)
159 waklog_host_config
*cfg
;
161 if ( params
->path
== NULL
) {
162 cfg
= (waklog_host_config
*) ap_get_module_config(
163 params
->server
->module_config
, &waklog_module
);
165 cfg
= (waklog_host_config
*)mconfig
;
175 set_waklog_use_keytab( cmd_parms
*params
, void *mconfig
, char *file
)
177 waklog_host_config
*cfg
;
179 if ( params
->path
== NULL
) {
180 cfg
= (waklog_host_config
*) ap_get_module_config(
181 params
->server
->module_config
, &waklog_module
);
183 cfg
= (waklog_host_config
*)mconfig
;
186 ap_log_error( APLOG_MARK
, APLOG_INFO
|APLOG_NOERRNO
, params
->server
,
187 "mod_waklog: using keytab: %s", file
);
196 waklog_child_init( server_rec
*s
, pool
*p
)
199 if ( child
== NULL
) {
200 child
= (waklog_child_config
*) ap_palloc( p
, sizeof( waklog_child_config
) );
203 memset( &child
->token
, 0, sizeof( struct ktc_token
) );
207 ap_log_error( APLOG_MARK
, APLOG_INFO
|APLOG_NOERRNO
, s
,
208 "mod_waklog: waklog_child_init: child: 0x%08x", child
);
214 command_rec waklog_cmds
[ ] =
216 { "WaklogProtected", set_waklog_protect
,
217 NULL
, RSRC_CONF
| ACCESS_CONF
, FLAG
,
218 "enable waklog on a location or directory basis" },
220 { "WaklogUseKeytab", set_waklog_use_keytab
,
221 NULL
, RSRC_CONF
, TAKE1
,
222 "Use the supplied keytab file rather than the user's TGT" },
229 pioctl_cleanup( void *data
)
231 request_rec
*r
= (request_rec
*)data
;
233 if ( child
->token
.ticketLen
) {
234 memset( &child
->token
, 0, sizeof( struct ktc_token
) );
236 ktc_ForgetAllTokens();
238 ap_log_error( APLOG_MARK
, APLOG_NOERRNO
|APLOG_ERR
, r
->server
,
239 "mod_waklog: ktc_ForgetAllTokens succeeded" );
246 waklog_ktinit( request_rec
*r
, char *keytab_path
)
248 krb5_error_code kerror
;
249 krb5_context kcontext
;
250 krb5_principal kprinc
;
251 krb5_get_init_creds_opt kopts
;
255 krb5_keytab keytab
= 0;
256 char ktbuf
[ MAX_KEYTAB_NAME_LEN
+ 1 ];
259 ap_log_error( APLOG_MARK
, APLOG_NOERRNO
|APLOG_ERR
, r
->server
,
260 "mod_waklog: waklog_ktinit called" );
262 /* set our environment variables */
263 ap_table_set( r
->subprocess_env
, "KRB5CCNAME", K5PATH
);
264 ap_table_set( r
->subprocess_env
, "KRBTKFILE", K4PATH
);
266 ap_log_error( APLOG_MARK
, APLOG_NOERRNO
|APLOG_ERR
, r
->server
,
267 "mod_waklog: KRB5CCNAME: %s, KRBTKFILE: %s", K5PATH
, K4PATH
);
271 /* will we need another tgt soon? */
273 if ( !mod
->getting_tgt
&& mod
->endtime
< now
+ SOON
) {
275 ap_log_error( APLOG_MARK
, APLOG_NOERRNO
|APLOG_ERR
, r
->server
,
276 "mod_waklog: mod->endtime: %u, now: %u", mod
->endtime
, now
);
279 ** Only one process will get into the critical region below and
280 ** replace the soon-to-be-expired credential; the rest will just
281 ** use the still-good credential until the new one shows up.
283 ** The TAS flag will stop other processes from entering the
284 ** first half of the conditional above, and the critical region
285 ** below will not exit until mod->endtime has been updated to
286 ** reflect that of the new credentials (or fail).
288 ** (While it is possible for all child processes to get past the
289 ** first half of the conditional above, and then for one
290 ** process to get into the critical region below and run to the
291 ** end (clearing the TAS flag), a fair scheduler would not
292 ** do this; but even so, it really wouldn't kill us if ALL of
293 ** the child processes got credentials, anyway.
295 if ( !test_and_set_bit( 0, &mod
->getting_tgt
) ) {
297 if (( kerror
= krb5_init_context( &kcontext
))) {
298 ap_log_error( APLOG_MARK
, APLOG_ERR
, r
->server
,
299 (char *)error_message( kerror
));
305 if (( kerror
= krb5_cc_resolve( kcontext
, K5PATH
, &kccache
)) != 0 ) {
306 ap_log_error( APLOG_MARK
, APLOG_ERR
, r
->server
,
307 (char *)error_message( kerror
));
312 if (( kerror
= krb5_parse_name( kcontext
, PRINCIPAL
, &kprinc
))) {
313 ap_log_error( APLOG_MARK
, APLOG_ERR
, r
->server
,
314 (char *)error_message( kerror
));
319 krb5_get_init_creds_opt_init( &kopts
);
320 krb5_get_init_creds_opt_set_tkt_life( &kopts
, 10*60*60 );
321 krb5_get_init_creds_opt_set_renew_life( &kopts
, 0 );
322 krb5_get_init_creds_opt_set_forwardable( &kopts
, 1 );
323 krb5_get_init_creds_opt_set_proxiable( &kopts
, 0 );
325 /* which keytab should we use? */
326 strcpy( ktbuf
, keytab_path
? keytab_path
: KEYTAB_PATH
);
328 if ( strlen( ktbuf
) > MAX_KEYTAB_NAME_LEN
) {
329 ap_log_error( APLOG_MARK
, APLOG_ERR
, r
->server
,
330 "server configuration error" );
335 ap_log_error( APLOG_MARK
, APLOG_NOERRNO
|APLOG_ERR
, r
->server
,
336 "mod_waklog: waklog_ktinit using: %s", ktbuf
);
338 if (( kerror
= krb5_kt_resolve( kcontext
, ktbuf
, &keytab
)) != 0 ) {
339 ap_log_error( APLOG_MARK
, APLOG_ERR
, r
->server
,
340 (char *)error_message( kerror
));
346 if (( kerror
= krb5_get_init_creds_keytab( kcontext
, &v5creds
,
347 kprinc
, keytab
, 0, IN_TKT_SERVICE
, &kopts
))) {
349 ap_log_error( APLOG_MARK
, APLOG_ERR
, r
->server
,
350 (char *)error_message( kerror
));
355 if (( kerror
= krb5_verify_init_creds( kcontext
, &v5creds
,
356 kprinc
, keytab
, NULL
, NULL
)) != 0 ) {
358 ap_log_error( APLOG_MARK
, APLOG_ERR
, r
->server
,
359 (char *)error_message( kerror
));
364 if (( kerror
= krb5_cc_initialize( kcontext
, kccache
, kprinc
)) != 0 ) {
365 ap_log_error( APLOG_MARK
, APLOG_ERR
, r
->server
,
366 (char *)error_message( kerror
));
371 if (( kerror
= krb5_cc_store_cred( kcontext
, kccache
, &v5creds
)) != 0 ) {
372 ap_log_error( APLOG_MARK
, APLOG_ERR
, r
->server
,
373 (char *)error_message( kerror
));
378 /* convert K5 => K4 */
379 ap_log_error( APLOG_MARK
, APLOG_NOERRNO
|APLOG_ERR
, r
->server
,
380 "mod_waklog: before krb524_convert_creds" );
382 if (( kerror
= krb524_convert_creds_kdc( kcontext
,
383 &v5creds
, &v4creds
)) != 0 ) {
385 ap_log_error( APLOG_MARK
, APLOG_ERR
, r
->server
,
386 (char *)error_message( kerror
));
392 krb_set_tkt_string( (char *)K4PATH
);
394 /* initialize ticket cache */
395 ap_log_error( APLOG_MARK
, APLOG_NOERRNO
|APLOG_ERR
, r
->server
,
396 "mod_waklog: before in_tkt" );
398 if (( kerror
= in_tkt( v4creds
.pname
, v4creds
.pinst
)) != KSUCCESS
) {
399 ap_log_error( APLOG_MARK
, APLOG_ERR
, r
->server
,
400 (char *)error_message( kerror
));
405 /* stash, ticket, session key, etc for future use */
406 ap_log_error( APLOG_MARK
, APLOG_NOERRNO
|APLOG_ERR
, r
->server
,
407 "mod_waklog: before krb_save_credentials" );
409 if (( kerror
= krb_save_credentials( v4creds
.service
,
415 &(v4creds
.ticket_st
),
416 v4creds
.issue_date
)) != 0 ) {
418 ap_log_error( APLOG_MARK
, APLOG_ERR
, r
->server
,
419 (char *)error_message( kerror
));
425 mod
->endtime
= v5creds
.times
.endtime
;
426 ap_log_error( APLOG_MARK
, APLOG_NOERRNO
|APLOG_ERR
, r
->server
,
427 "mod_waklog: mod->endtime: %u, mod->getting_tgt: %d",
428 mod
->endtime
, mod
->getting_tgt
);
430 cleanup6
: krb5_free_cred_contents( kcontext
, &v5creds
);
431 cleanup5
: (void)krb5_kt_close( kcontext
, keytab
);
432 cleanup4
: krb5_free_principal( kcontext
, kprinc
);
433 cleanup3
: krb5_cc_close( kcontext
, kccache
);
434 cleanup2
: krb5_free_context( kcontext
);
436 /* exit the critical region */
437 cleanup1
: mod
->getting_tgt
= 0;
442 ap_log_error( APLOG_MARK
, APLOG_NOERRNO
|APLOG_ERR
, r
->server
,
443 "mod_waklog: waklog_ktinit: exiting" );
450 waklog_aklog( request_rec
*r
)
454 const char *k4path
= NULL
;
455 const char *k5path
= NULL
;
456 krb5_error_code kerror
;
457 krb5_context kcontext
;
459 krb5_creds
*v5credsp
= NULL
;
462 struct ktc_principal server
= { "afs", "", "umich.edu" };
463 struct ktc_principal client
;
464 struct ktc_token token
;
466 k5path
= ap_table_get( r
->subprocess_env
, "KRB5CCNAME" );
467 k4path
= ap_table_get( r
->subprocess_env
, "KRBTKFILE" );
469 ap_log_error( APLOG_MARK
, APLOG_NOERRNO
|APLOG_ERR
, r
->server
,
470 "mod_waklog: waklog_aklog called: k5path: %s, k4path: %s", k5path
, k4path
);
472 if ( !k5path
|| !k4path
) {
473 ap_log_error( APLOG_MARK
, APLOG_NOERRNO
|APLOG_ERR
, r
->server
,
474 "mod_waklog: waklog_aklog giving up" );
479 ** Get/build creds from file/tgs, then see if we need to SetToken
482 if (( kerror
= krb5_init_context( &kcontext
))) {
483 /* Authentication Required ( kerberos error ) */
484 ap_log_error( APLOG_MARK
, APLOG_ERR
, r
->server
,
485 (char *)error_message( kerror
));
490 memset( (char *)&increds
, 0, sizeof(increds
));
492 /* set server part */
493 if (( kerror
= krb5_parse_name( kcontext
, AFS
, &increds
.server
))) {
494 ap_log_error( APLOG_MARK
, APLOG_ERR
, r
->server
,
495 (char *)error_message( kerror
));
500 if (( kerror
= krb5_cc_resolve( kcontext
, k5path
, &kccache
)) != 0 ) {
501 ap_log_error( APLOG_MARK
, APLOG_ERR
, r
->server
,
502 (char *)error_message( kerror
));
507 /* set client part */
508 krb5_cc_get_principal( kcontext
, kccache
, &increds
.client
);
510 increds
.times
.endtime
= 0;
511 /* Ask for DES since that is what V4 understands */
512 increds
.keyblock
.enctype
= ENCTYPE_DES_CBC_CRC
;
514 /* get the V5 credentials */
515 if (( kerror
= krb5_get_credentials( kcontext
, 0, kccache
,
516 &increds
, &v5credsp
) ) ) {
517 ap_log_error( APLOG_MARK
, APLOG_NOERRNO
|APLOG_ERR
, r
->server
,
518 "mod_waklog: krb5_get_credentials: %s", krb_err_txt
[ kerror
] );
522 /* get the V4 credentials */
523 if (( kerror
= krb524_convert_creds_kdc( kcontext
, v5credsp
, &v4creds
) ) ) {
524 ap_log_error( APLOG_MARK
, APLOG_NOERRNO
|APLOG_ERR
, r
->server
,
525 "mod_waklog: krb524_convert_creds_kdc: %s", krb_err_txt
[ kerror
] );
529 /* assemble the token */
530 token
.kvno
= v4creds
.kvno
;
531 token
.startTime
= v4creds
.issue_date
;
532 token
.endTime
= v5credsp
->times
.endtime
;
533 memmove( &token
.sessionKey
, v4creds
.session
, 8 );
534 token
.ticketLen
= v4creds
.ticket_st
.length
;
535 memmove( token
.ticket
, v4creds
.ticket_st
.dat
, token
.ticketLen
);
537 /* make sure we have to do this */
538 if ( child
->token
.kvno
!= token
.kvno
||
539 child
->token
.ticketLen
!= token
.ticketLen
||
540 memcmp( &child
->token
.sessionKey
, &token
.sessionKey
,
541 sizeof( token
.sessionKey
) ) ||
542 memcmp( child
->token
.ticket
, token
.ticket
, token
.ticketLen
) ) {
544 ap_log_error( APLOG_MARK
, APLOG_NOERRNO
|APLOG_ERR
, r
->server
,
545 "mod_waklog: %s.%s@%s", v4creds
.service
, v4creds
.instance
,
547 ap_log_error( APLOG_MARK
, APLOG_NOERRNO
|APLOG_ERR
, r
->server
,
548 "mod_waklog: %d %d %d", v4creds
.lifetime
, v4creds
.kvno
,
549 v4creds
.issue_date
);
550 ap_log_error( APLOG_MARK
, APLOG_NOERRNO
|APLOG_ERR
, r
->server
,
551 "mod_waklog: %s %s", v4creds
.pname
, v4creds
.pinst
);
552 ap_log_error( APLOG_MARK
, APLOG_NOERRNO
|APLOG_ERR
, r
->server
,
553 "mod_waklog: %d", v4creds
.ticket_st
.length
);
556 strcpy( buf
, v4creds
.pname
);
557 if ( v4creds
.pinst
[ 0 ] ) {
559 strcat( buf
, v4creds
.pinst
);
562 /* assemble the client */
563 strncpy( client
.name
, buf
, MAXKTCNAMELEN
- 1 );
564 strcpy( client
.instance
, "" );
565 strncpy( client
.cell
, v4creds
.realm
, MAXKTCNAMELEN
- 1 );
567 ap_log_error( APLOG_MARK
, APLOG_NOERRNO
|APLOG_ERR
, r
->server
,
568 "mod_waklog: server: name=%s, instance=%s, cell=%s",
569 server
.name
, server
.instance
, server
.cell
);
571 ap_log_error( APLOG_MARK
, APLOG_NOERRNO
|APLOG_ERR
, r
->server
,
572 "mod_waklog: client: name=%s, instance=%s, cell=%s",
573 client
.name
, client
.instance
, client
.cell
);
576 krb_set_tkt_string( (char *)k4path
);
578 /* rumor: we have to do this for AIX 4.1.4 with AFS 3.4+ */
581 if ( ( rc
= ktc_SetToken( &server
, &token
, &client
, 0 ) ) ) {
582 ap_log_error( APLOG_MARK
, APLOG_NOERRNO
|APLOG_ERR
, r
->server
,
583 "mod_waklog: settoken returned %d", rc
);
587 memmove( &child
->token
, &token
, sizeof( struct ktc_token
) );
589 /* we'll need to unlog when this connection is done. */
590 ap_register_cleanup( r
->pool
, (void *)r
, pioctl_cleanup
, ap_null_cleanup
);
593 krb5_free_cred_contents( kcontext
, v5credsp
);
594 krb5_free_principal( kcontext
, increds
.client
);
595 krb5_cc_close( kcontext
, kccache
);
596 krb5_free_context( kcontext
);
598 ap_log_error( APLOG_MARK
, APLOG_NOERRNO
|APLOG_ERR
, r
->server
,
599 "mod_waklog: finished with waklog_aklog" );
605 waklog_phase0( request_rec
*r
)
607 waklog_host_config
*cfg
;
609 ap_log_error( APLOG_MARK
, APLOG_NOERRNO
|APLOG_ERR
, r
->server
,
610 "mod_waklog: phase0 called" );
612 /* directory config? */
613 cfg
= (waklog_host_config
*)ap_get_module_config(
614 r
->per_dir_config
, &waklog_module
);
617 if ( !cfg
->configured
) {
618 cfg
= (waklog_host_config
*)ap_get_module_config(
619 r
->server
->module_config
, &waklog_module
);
622 if ( !cfg
->protect
) {
623 ap_log_error( APLOG_MARK
, APLOG_NOERRNO
|APLOG_ERR
, r
->server
,
624 "mod_waklog: phase0 declining" );
628 /* do this only if we are still unauthenticated */
629 if ( !child
->token
.ticketLen
) {
631 /* authenticate using keytab file */
632 waklog_ktinit( r
, cfg
->keytab
);
634 /* stuff the credentials into the kernel */
638 ap_log_error( APLOG_MARK
, APLOG_NOERRNO
|APLOG_ERR
, r
->server
,
639 "mod_waklog: phase0 returning" );
645 waklog_phase7( request_rec
*r
)
647 waklog_host_config
*cfg
;
649 ap_log_error( APLOG_MARK
, APLOG_NOERRNO
|APLOG_ERR
, r
->server
,
650 "mod_waklog: phase7 called" );
652 /* directory config? */
653 cfg
= (waklog_host_config
*)ap_get_module_config(
654 r
->per_dir_config
, &waklog_module
);
657 if ( !cfg
->configured
) {
658 cfg
= (waklog_host_config
*)ap_get_module_config(
659 r
->server
->module_config
, &waklog_module
);
662 if ( !cfg
->protect
) {
666 /* stuff the credentials into the kernel */
669 ap_log_error( APLOG_MARK
, APLOG_NOERRNO
|APLOG_ERR
, r
->server
,
670 "mod_waklog: phase7 returning" );
676 waklog_new_connection( conn_rec
*c
) {
677 ap_log_error( APLOG_MARK
, APLOG_NOERRNO
|APLOG_ERR
, c
->server
,
678 "mod_waklog: new_connection called: conn_rec: 0x%08x pid: %d", c
, getpid() );
682 module MODULE_VAR_EXPORT waklog_module
= {
683 STANDARD_MODULE_STUFF
,
684 waklog_init
, /* module initializer */
685 waklog_create_dir_config
, /* create per-dir config structures */
686 NULL
, /* merge per-dir config structures */
687 waklog_create_server_config
, /* create per-server config structures */
688 NULL
, /* merge per-server config structures */
689 waklog_cmds
, /* table of config file commands */
690 NULL
, /* [#8] MIME-typed-dispatched handlers */
691 NULL
, /* [#1] URI to filename translation */
692 NULL
, /* [#4] validate user id from request */
693 NULL
, /* [#5] check if the user is ok _here_ */
694 NULL
, /* [#3] check access by host address */
695 NULL
, /* [#6] determine MIME type */
696 waklog_phase7
, /* [#7] pre-run fixups */
697 NULL
, /* [#9] log a transaction */
698 NULL
, /* [#2] header parser */
699 waklog_child_init
, /* child_init */
700 NULL
, /* child_exit */
701 waklog_phase0
/* [#0] post read-request */
703 ,NULL
, /* EAPI: add_module */
704 NULL
, /* EAPI: remove_module */
705 NULL
, /* EAPI: rewrite_command */
706 waklog_new_connection
/* EAPI: new_connection */