From 22e101e14f1fe705d5c345b4a91a93cb18dab7af Mon Sep 17 00:00:00 2001 From: Rob van Son Date: Tue, 19 Jun 2012 16:48:18 +0200 Subject: [PATCH] Infrastructure for signing tickets --- CGIscriptor.pl | 109 +++++++++++++++++++++++++-------------- CGIservletSETUP.pl | 2 + Private/.Passwords/test | 4 +- Private/.Passwords/testchallenge | 4 +- Private/.Passwords/testip | 4 +- 5 files changed, 77 insertions(+), 46 deletions(-) diff --git a/CGIscriptor.pl b/CGIscriptor.pl index 20a71cd..22b4cf2 100755 --- a/CGIscriptor.pl +++ b/CGIscriptor.pl @@ -3223,6 +3223,8 @@ sub create_newuser # ($loginfile, $sessionfile, $authorizationfile, $password, $ $newaccount->{'Session'} = ['SESSION']; if($newsession eq 'IPADDRESS'){$newaccount->{'Session'} = ['IPADDRESS'];}; if($newsession eq 'CHALLENGE'){$newaccount->{'Session'} = ['CHALLENGE'];}; + my $timesec = time(); + $newaccount->{'Time'} = [$timesec]; # Encrypt all passwords with the CGIMasterKey EncryptTicketWithMasterPassword($newaccount, $serversalt) || @@ -3242,22 +3244,30 @@ sub create_newuser # ($loginfile, $sessionfile, $authorizationfile, $password, $ if($currentRoot) { $currentRoot .= '/' unless $currentRoot =~ m!/$!; - $NewAllowedPaths .= 'AllowedPaths: ^'.${currentRoot}.'[\w\-]+\.html?'."\n"; + my $newpath = ${currentRoot}.'[\w\-]+\.html?'; + $NewAllowedPaths .= 'AllowedPaths: ^'.${currentRoot}.'[\w\-]+\.html?'."\n"; + $newaccount->{'AllowedPaths'} = [$newpath]; } else { # Tricky PATH_INFO, deny all $NewAllowedPaths .= "DeniedPaths: ^/\n"; + $newaccount->{'DeniedPaths'} = ["DeniedPaths: ^/\n"]; }; + # Construct home directory path my $currentHome = lc($newaccount->{'Username'}->[0]); $currentHome =~ s/[^\w]/_/isg; - $NewAllowedPaths .= "AllowedPaths: ^${currentRoot}$currentHome/\n"; + my $newpath = "^${currentRoot}$currentHome/"; + $NewAllowedPaths .= "AllowedPaths: $newpath\n"; + push(@{$newaccount->{'AllowedPaths'}}, $newpath); chomp($NewAllowedPaths); + # Sign the new ticket + my $Signature = SignTicketWithMasterkey($newaccount, $newaccount->{'Salt'}->[0]); + # Write my $datetime = gmtime(); - my $timesec = time(); my $passwordline = "".($newaccount->{'Password'}->[0]).""; my $newaccounttext = << "ENDOFNEWACCOUNTTEXT"; Type: $newaccount->{'Type'}->[0] @@ -3267,7 +3277,8 @@ Salt: $newaccount->{'Salt'}->[0] $NewAllowedPaths Session: $newaccount->{'Session'}->[0] Date: $datetime UTC -Time: $timesec +Time: $newaccount->{'Time'}->[0] +Signature: $newaccount->{'Signature'}->[0] ENDOFNEWACCOUNTTEXT return $newaccounttext; }; @@ -3426,19 +3437,7 @@ sub create_login_file #($PasswordDir, $SessionDir, $IPaddress) $loginticket->{Expires} = ['+600s']; $loginticket->{Date} = ["$datetime UTC"]; $loginticket->{Time} = [$timesec]; - my $Signature = SignTicketWithMasterKey($loginticket, $SERVERSALT); - $loginticket->{Signature} = [$Signature]; - open(LOGINTICKET, ">$SessionDir/$LOGINTICKET") || die "$SessionDir/$LOGINTICKET: $!\n"; - foreach my $label (Type, IPaddress, Salt, Session, Randomsalt, Expires, Date, Time, Signature) - { - foreach my $value (@{$loginticket->{$label}}) - { - print LOGINTICKET "$label: $value\n"; - }; - }; - close(LOGINTICKET); - my $loginticket = read_ticket('$SessionDir/$LOGINTICKET'); - + write_ticket("$SessionDir/$LOGINTICKET", $loginticket, $SERVERSALT); # Set global variables # $SERVERSALT @@ -3500,7 +3499,7 @@ sub create_session_file #($sessionfile, $loginfile, $authorizationfile, $path) - # Convert Authorization content to Session content my $sessionContent = {}; my $SessionType = $authorization->{'Session'}->[0] ? $authorization->{'Session'}->[0] : "SESSION"; - $sessionContent->{Type} = [$authorization->{'Session'}->[0]]; + $sessionContent->{Type} = [$SessionType]; $sessionContent->{Username} = [$authorization->{'Username'}->[0]]; $sessionContent->{Session} = [$sessionid]; $sessionContent->{Time} = [time]; @@ -3519,26 +3518,15 @@ sub create_session_file #($sessionfile, $loginfile, $authorizationfile, $path) - push(@{$sessionContent->{Expires}}, $validtime); }; }; + $sessionContent->{Key} = [$sessionkey] if $sessionkey; + $sessionContent->{Secretkey} = [$secretkey] if $secretkey; + $sessionContent->{Date} = [gmtime()." UTC\n"]; # Sign Session Ticket - my $Signature = SignTicketWithMasterKey($sessionContent, $authorization->{'Salt'}->[0]); - $sessionContent->{Signature} = [$Signature]; + my $Signature = SignTicketWithMasterkey($sessionContent, $authorization->{'Salt'}->[0]); # Write Session Ticket - open(SESSION, ">$sessionfile") || die "$sessionfile: $!\n"; - foreach my $list ('Type', 'IPaddress', 'AllowedPaths', 'DeniedPaths', 'Expires', 'Capabilities', 'Salt', 'Randomsalt', 'Username', 'Time', 'Session', 'Signature') - { - foreach my $entry (@{$sessionContent->{$list}}) - { - print SESSION "$list: $entry\n"; - }; - }; - - # Special lines - print SESSION "Key: $sessionkey\n" if $sessionkey; - print SESSION "Secretkey: $secretkey\n" if $secretkey; - print SESSION "Date: ", gmtime()." UTC\n"; - close(SESSION); + write_ticket($sessionfile, $sessionContent, $authorization->{'Salt'}->[0]); # Login file should now be removed return unlink($loginfile); @@ -3594,7 +3582,7 @@ sub check_ticket_validity # ($type, $ticketfile, $address, $path) return 0 if $Expired; # Check signature - my $Signature = SignTicketWithMasterKey($ticket, $ticket->{'Salt'}->[0]); + my $Signature = TicketSignature($ticket, $ticket->{'Salt'}->[0]); if($Signature ne $ticket->{'Signature'}->[0]) { @@ -3642,6 +3630,30 @@ sub remove_expired_tickets # ($path) -> number of tickets removed return $removed_tickets; }; +sub write_ticket # ($ticketfile, $ticket, $salt) -> &%ticket +{ + my $ticketfile = shift || ""; + my $ticket = shift || ""; + my $salt = shift || ""; + + # Sign the new ticket + my $signature = SignTicketWithMasterkey($ticket, ""); + open(TICKET, ">$ticketfile") || die "$ticketfile: $!\n"; + foreach my $label ('Type', 'Username', 'IPaddress', 'AllowedPaths', 'DeniedPaths', + 'Expires', 'Capabilities', 'Salt', 'Session', 'Randomsalt', + 'Date', 'Time', 'Signature', 'Key', 'Secretkey') + { + next unless exists($ticket->{$label}) && $ticket->{$label}->[0]; + foreach my $value (@{$ticket->{$label}}) + { + print TICKET "$label: $value\n"; + }; + }; + close(TICKET); + + return $ticketfile; +}; + sub read_ticket # ($ticketfile) -> &%ticket { my $ticketfile = shift || ""; @@ -3693,7 +3705,7 @@ sub read_ticket # ($ticketfile) -> &%ticket }; }; }; - if($ENV{'CGIMasterKey'} && exists($ticket->{Password}) && $ticket->{Password}->[0]) + if($ENV{'CGIMasterKey'} && exists($ticket->{'Password'}) && $ticket->{'Password'}->[0]) { my $serversalt = ""; # If the ServerSalt is not stored in the ticket, the SALT file has to be found @@ -3743,14 +3755,31 @@ sub read_ticket # ($ticketfile) -> &%ticket return $ticket; }; -# Add a signature to each ticket -sub SignTicketWithMasterKey # ($ticket, $serversalt) -> $Signature +# Add a signature from $masterkey to a ticket in the label $signlabel +sub SignTicketWithMasterkey # ($ticket, $serversalt [, $masterkey, $signlabel]) -> $Signature +{ + my $ticket = shift || return 0; + my $serversalt = shift || ""; + my $masterkey = shift || $ENV{'CGIMasterKey'}; + my $signlabel = shift || 'Signature'; + return "" unless $masterkey; + + my $Signature = TicketSignature($ticket, $serversalt, $masterkey); + + $ticket->{$signlabel} = [$Signature]; + + return $Signature; +}; + +# Determine ticket signature +sub TicketSignature # ($ticket, $serversalt [, $masterkey]) -> $Signature { my $ticket = shift || return 0; my $serversalt = shift || ""; + my $masterkey = shift || $ENV{'CGIMasterKey'}; my $Signature = ""; - if($ENV{'CGIMasterKey'}) + if($masterkey) { # If the ServerSalt is not stored in the ticket, the SALT file has to be found if(exists($ticket->{Salt}) && $ticket->{Salt}->[0]) @@ -3760,7 +3789,7 @@ sub SignTicketWithMasterKey # ($ticket, $serversalt) -> $Signature # Sign if($serversalt) { - my $hash1 = hash_string(${serversalt}.$ENV{'CGIMasterKey'}); + my $hash1 = hash_string(${serversalt}.$masterkey); my $SignText = "Type: ".$ticket->{'Type'}->[0]."\n"; $SignText .= "Username: ".(sort(@{$ticket->{'Username'}}))."\n"; $SignText .= "IPaddress: ".(sort(@{$ticket->{'IPaddress'}}))."\n"; diff --git a/CGIservletSETUP.pl b/CGIservletSETUP.pl index c8bd015..5b48dcc 100755 --- a/CGIservletSETUP.pl +++ b/CGIservletSETUP.pl @@ -30,6 +30,8 @@ ############################################################ # User set Envrionment variables +# In real use, read the key from a keyfile, e.g., +# open(KEYFILE, ";chomp($UserEnv{'CGIMasterKey'});close(KEYFILE); $UserEnv{'CGIMasterKey'} = "Sherlock investigates oleander curry in Bath"; $port = 8080; # The port number diff --git a/Private/.Passwords/test b/Private/.Passwords/test index 7812ffe..cbb94f7 100644 --- a/Private/.Passwords/test +++ b/Private/.Passwords/test @@ -1,7 +1,7 @@ Type: PASSWORD Username: test -Password: 5acf4e6d4e69381eb69e5637498087a3b308e66a250608ef8c99f30ca83abf51 -Salt: 5fe65748c3847cbc902b9eda0a8cd188633e6b8c0646d1fd5e0e63958469cd69 +Password: 5c6d9e5d66d963cbc35d8518398afa9715e538859a094a543c9a5f106bdabaf0 +Salt: e93cf858a1d5626bf095ea5c25df990dfa969ff5a5dc908b22c9a5229b525f65 AllowedPaths: ^/Private/CreateUser\.html AllowedPaths: ^/Private/index\.html$ AllowedPaths: ^/Private/[^/]+\.html$ diff --git a/Private/.Passwords/testchallenge b/Private/.Passwords/testchallenge index 5560e82..c294b59 100644 --- a/Private/.Passwords/testchallenge +++ b/Private/.Passwords/testchallenge @@ -1,7 +1,7 @@ Type: PASSWORD Username: testchallenge -Password: 8e314558578a876b97f5859564e23386b78c6dfd4a773d97aa5afcebf49b91cc -Salt: 5fe65748c3847cbc902b9eda0a8cd188633e6b8c0646d1fd5e0e63958469cd69 +Password: c7e320a0d9b174037788bfd672ba8556e78c57bdc83b07f57011b1668c53ba05 +Salt: e93cf858a1d5626bf095ea5c25df990dfa969ff5a5dc908b22c9a5229b525f65 AllowedPaths: ^/Private/index\.html$ AllowedPaths: ^/Private/[^/]+\.html$ AllowedPaths: ^/Private/?$ diff --git a/Private/.Passwords/testip b/Private/.Passwords/testip index b0de958..27a4601 100644 --- a/Private/.Passwords/testip +++ b/Private/.Passwords/testip @@ -1,7 +1,7 @@ Type: PASSWORD Username: testip -Password: 3997cd75692e85e19980bb9081a6c9c43eea49f403bbb52ebaa938156e7a7774 -Salt: 5fe65748c3847cbc902b9eda0a8cd188633e6b8c0646d1fd5e0e63958469cd69 +Password: 45e8e66eda2e41d75f9cd963c4abc15bb560674a929528e4e52a2abe1435c9dc +Salt: e93cf858a1d5626bf095ea5c25df990dfa969ff5a5dc908b22c9a5229b525f65 AllowedPaths: ^/Private/index.html$ AllowedPaths: ^/Private/[^/]+.html$ AllowedPaths: ^/Private/?$ -- 2.11.4.GIT