From 38ba9ffacb1080a45f7222b8519f78b24dec1717 Mon Sep 17 00:00:00 2001 From: Rob van Son Date: Thu, 21 Jun 2012 13:46:31 +0200 Subject: [PATCH] Debugging signing --- CGIscriptor.pl | 210 ++++++++++++++++----------------------- Private/.Passwords/admin | 15 +++ Private/.Passwords/test | 2 +- Private/.Passwords/testchallenge | 2 +- Private/.Passwords/testip | 2 +- 5 files changed, 106 insertions(+), 125 deletions(-) diff --git a/CGIscriptor.pl b/CGIscriptor.pl index b8cc516..4e38b27 100755 --- a/CGIscriptor.pl +++ b/CGIscriptor.pl @@ -2951,7 +2951,7 @@ sub Log_In_Access # () -> 0 = Access Allowed, Login page if access is not allowe goto Login unless (-s "$PasswordsPath/$userfile"); my $ticket_valid = check_ticket_validity("PASSWORD", "$PasswordsPath/$userfile", $REMOTE_ADDR, $PATH_INFO); goto Login unless $ticket_valid; - $ticket_valid = check_ticket_validity("LOGIN", "$SessionPath/$LOGINTICKET", $REMOTE_ADDR, "."); + $ticket_valid = check_ticket_validity("LOGIN", "$SessionPath/$LOGINTICKET", $REMOTE_ADDR, ".", 1); goto Login unless $ticket_valid; my ($sessiontype, $currentticket) = ("", ""); @@ -2990,7 +2990,7 @@ sub Log_In_Access # () -> 0 = Access Allowed, Login page if access is not allowe goto Login unless (-s "$PasswordsPath/$userfile"); my $ticket_valid = check_ticket_validity("PASSWORD", "$PasswordsPath/$userfile", $REMOTE_ADDR, $PATH_INFO); goto Login unless $ticket_valid; - $ticket_valid = check_ticket_validity("LOGIN", "$SessionPath/$LOGINTICKET", $REMOTE_ADDR, "."); + $ticket_valid = check_ticket_validity("LOGIN", "$SessionPath/$LOGINTICKET", $REMOTE_ADDR, ".", 1); goto Login unless $ticket_valid; my ($sessiontype, $currentticket) = ("", ""); @@ -3175,7 +3175,6 @@ sub change_password # ($loginfile, $sessionfile, $authorizationfile, $password, my $storedpassword = $authorization->{'Password'}->[0]; my $Hashedpassword = hash_string("$Randomsalt$storedpassword"); - chomp($Hashedpassword); return "" unless $password eq $Hashedpassword; my $secretkey = hash_string("$Randomsalt$LoginID$storedpassword"); @@ -3185,20 +3184,7 @@ sub change_password # ($loginfile, $sessionfile, $authorizationfile, $password, # Authorization succeeded, change password $authorization->{'Password'}->[0] = $decryptedPassword; # Apply masterkey - EncryptTicketWithMasterKey($authorization, $authorization->{'Salt'}->[0]) || return ""; - - - open(USERFILE, "<$authorizationfile") || die "<$authorizationfile: $!\n"; - my @USERlines = ; - close(USERFILE); - # Change - open(USERFILE, ">$authorizationfile") || die ">$authorizationfile: $!\n"; - foreach my $line (@USERlines) - { - $line =~ s/^Password: ($storedpassword)$/Password: $decryptedPassword/ig; - print USERFILE $line; - }; - close(USERFILE); + write_ticket($authorizationfile, $authorization, $authorization->{'Salt'}->[0]); return $newpassword; }; @@ -3254,15 +3240,6 @@ sub create_newuser # ($loginfile, $sessionfile, $authorizationfile, $password, $ if($newsession eq 'CHALLENGE'){$newaccount->{'Session'} = ['CHALLENGE'];}; my $timesec = time(); $newaccount->{'Time'} = [$timesec]; - - # Encrypt all passwords with the CGIMasterKey - EncryptTicketWithMasterKey($newaccount, $serversalt) || - die "Encryption failed: EncryptTicketWithMasterKey ($newaccount, $serversalt)\n"; - - # Re-encrypt the new password for transmission - my $plainpasswordline = $newaccount->{'Password'}->[0]; - my $reencryptedpassword = XOR_hex_strings($secretkey, $newaccount->{'Password'}->[0]); - my $encryptedpasswordline = "$reencryptedpassword"; # AllowedPaths my $NewAllowedPaths = ""; @@ -3273,7 +3250,7 @@ sub create_newuser # ($loginfile, $sessionfile, $authorizationfile, $password, $ if($currentRoot) { $currentRoot .= '/' unless $currentRoot =~ m!/$!; - my $newpath = ${currentRoot}.'[\w\-]+\.html?'; + my $newpath = "^".${currentRoot}.'[\w\-]+\.html?'; $NewAllowedPaths .= 'AllowedPaths: ^'.${currentRoot}.'[\w\-]+\.html?'."\n"; $newaccount->{'AllowedPaths'} = [$newpath]; } @@ -3298,17 +3275,16 @@ sub create_newuser # ($loginfile, $sessionfile, $authorizationfile, $password, $ # Write my $datetime = gmtime(); my $passwordline = "".($newaccount->{'Password'}->[0]).""; - my $newaccounttext = << "ENDOFNEWACCOUNTTEXT"; -Type: $newaccount->{'Type'}->[0] -Username: $newaccount->{'Username'}->[0] -Password: $encryptedpasswordline -Salt: $newaccount->{'Salt'}->[0] -$NewAllowedPaths -Session: $newaccount->{'Session'}->[0] -Date: $datetime UTC -Time: $newaccount->{'Time'}->[0] -Signature: $newaccount->{'Signature'}->[0] -ENDOFNEWACCOUNTTEXT + my $newaccounttext = write_ticket("", $newaccount, $serversalt); + + # Re-encrypt the new password for transmission + if($newaccounttext =~ /^(Password\:\s+)(\S+)\s*$/) + { + my $passwordvalue = $1; + my $reencryptedpassword = XOR_hex_strings($secretkey, $passwordvalue); + my $encryptedpasswordline = "$reencryptedpassword"; + $newaccounttext =~ s/^(Password\:\s+)(\S+)\s*$/\1$encryptedpasswordline/gim; + }; return $newaccounttext; }; @@ -3524,10 +3500,7 @@ sub create_session_file #($sessionfile, $loginfile, $authorizationfile, $path) - }; $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->{Date} = [gmtime()." UTC"]; # Write Session Ticket write_ticket($sessionfile, $sessionContent, $authorization->{'Salt'}->[0]); @@ -3536,18 +3509,20 @@ sub create_session_file #($sessionfile, $loginfile, $authorizationfile, $path) - return unlink($loginfile); }; -sub check_ticket_validity # ($type, $ticketfile, $address, $path) +sub check_ticket_validity # ($type, $ticketfile, $address, $path [, $unsigned]) { my $type = shift || "SESSION"; my $ticketfile = shift || ""; my $address = shift || ""; my $path = shift || ""; + my $unsigned = shift || 0; # Is there a session ticket of this name? return 0 unless -s "$ticketfile"; # There is a session ticket, is it linked to this IP address? my $ticket = read_ticket($ticketfile); + return unless $ticket; # Is this the right type of ticket return unless $ticket && $ticket->{"Type"}->[0] eq $type; @@ -3576,23 +3551,9 @@ sub check_ticket_validity # ($type, $ticketfile, $address, $path) }; return 0 unless !@{$ticket->{"AllowedPaths"}} || $Pathmatches; - # Is the ticket expired? - my $Expired = 0; - if($ticket->{"Expires"} && @{$ticket->{"Expires"}}) - { - my $CurrentTime = time(); - ++$Expired if($CurrentTime > $ticket->{"Expires"}->[0]); - }; - return 0 if $Expired; - - # Check signature + # Check signature if not told to use an unsigned ticket (dangerous) my $Signature = TicketSignature($ticket, $ticket->{'Salt'}->[0]); - - if($Signature ne $ticket->{'Signature'}->[0]) - { - print STDERR "$ticket->{'Type'}->[0]: $ticket->{'Username'}->[0]\n"; - print STDERR "$Signature ne $ticket->{'Signature'}->[0]\n"; - }; + return 0 if (! $unsigned) && $Signature ne $ticket->{'Signature'}->[0]; # Make login values available (will also protect against resetting by query) $ENV{"LOGINUSERNAME"} = lc($ticket->{'Username'}->[0]); @@ -3625,7 +3586,7 @@ sub remove_expired_tickets # ($path) -> number of tickets removed foreach my $ticketfile (@ticketlist) { my $ticket = read_ticket($ticketfile); - unless($ticket && @{$ticket->{'Expires'}} && $ticket->{'Expires'}->[0] > time) + unless($ticket) { unlink $ticketfile; ++$removed_tickets; @@ -3657,35 +3618,46 @@ sub write_ticket # ($ticketfile, $ticket, $salt [, $masterkey]) -> &%ticket # Encrypt password EncryptTicketWithMasterKey($ticket, $salt, $masterkey); + SignTicketWithMasterkey ($ticket, $salt, $masterkey); # Sign the new ticket + my $signature = SignTicketWithMasterkey($ticket, $salt, $masterkey); + + # Create ordered list with labels my @orderlist = ('Type', 'Username', 'Password', 'IPaddress', 'AllowedPaths', 'DeniedPaths', 'Expires', 'Capabilities', 'Salt', 'Session', 'Randomsalt', 'Date', 'Time', 'Signature', 'Key', 'Secretkey'); my @labellist = keys(%{$ticket}); - my $signature = SignTicketWithMasterkey($ticket, $salt, $masterkey); - open(TICKET, ">$ticketfile") || die "$ticketfile: $!\n"; foreach my $label (@orderlist) { @labellist = grep(!/\b$label\b/, @labellist); }; + + # Create ticket in text + my $TicketText = ""; foreach my $label (@orderlist, @labellist) { next unless exists($ticket->{$label}) && $ticket->{$label}->[0]; foreach my $value (@{$ticket->{$label}}) { - print TICKET "$label: $value\n"; + $TicketText .= "$label: $value\n"; }; }; - close(TICKET); + if($ticketfile) + { + open(TICKET, ">$ticketfile") || die "$ticketfile: $!\n"; + print TICKET $TicketText; + close(TICKET); + }; - return $ticketfile; + return $TicketText; }; -sub read_ticket # ($ticketfile [, $masterkey]) -> &%ticket +sub read_ticket # ($ticketfile [, $salt, $masterkey]) -> &%ticket { my $ticketfile = shift || ""; - my $masterkey = shift || ""; + my $serversalt = shift || ""; + my $masterkey = shift || $ENV{'CGIMasterKey'}; my $ticket = {}; if($ticketfile && -s $ticketfile) @@ -3703,58 +3675,17 @@ sub read_ticket # ($ticketfile [, $masterkey]) -> &%ticket { my $Label = $1; my $Value = $2; - # Recalculate expire date from relative time - if($Label =~ /^Expires$/ig && $Value =~ /^\+/) - { - if($Value =~ /^\+(\d+)\s*d(ays)?\s*$/) - { - $ExpireTime = 24*3600*$1; - } - elsif($Value =~ /^\+(\d+)\s*m(inutes)?\s*$/) - { - $ExpireTime = 60*$1; - } - elsif($Value =~ /^\+(\d+)\s*h(ours)?\s*$/) - { - $ExpireTime = 3600*$1; - } - elsif($Value =~ /^\+(\d+)\s*s(econds)?\s*$/) - { - $ExpireTime = $1; - } - elsif($Value =~ /^\+(\d+)\s*$/) - { - $ExpireTime = $1; - }; - - my $ActualExpireTime = $ExpireTime; - $Value = $ActualExpireTime; - }; $ticket->{$Label} = () unless exists($ticket->{$Label}); push(@{$ticket->{$Label}}, $Value); }; }; }; - if($ENV{'CGIMasterKey'} && exists($ticket->{'Password'}) && $ticket->{'Password'}->[0]) + if($masterkey && exists($ticket->{'Password'}) && $ticket->{'Password'}->[0]) { - my $serversalt = ""; - # If the ServerSalt is not stored in the ticket, the SALT file has to be found + # Use the ServerSalt stored in the ticket, if present if(exists($ticket->{Salt}) && $ticket->{Salt}->[0]) { $serversalt = $ticket->{Salt}->[0]; - } - else - { - my $saltfile = $ticketfile; - $saltfile =~ s![^/]+$!!isg; - $saltfile .= "SALT"; - if(-s "$saltfile") - { - open(SERVERSALT, "<$saltfile") || die "<$saltfile: $!\n"; - $serversalt = ; - chomp($serversalt); - close(SERVERSALT); - }; }; # Decrypt all passwords DecryptTicketWithMasterKey($ticket, $serversalt, $masterkey) || @@ -3775,9 +3706,33 @@ sub read_ticket # ($ticketfile [, $masterkey]) -> &%ticket = stat($ticketfile); $StartTime = $ctime; }; - foreach my $absoluteTime (@{$ticket->{Expires}}) + foreach my $Value (@{$ticket->{'Expires'}}) { - $absoluteTime += $StartTime; + # Recalculate expire date from relative time + if($Value =~ /^\+/) + { + if($Value =~ /^\+(\d+)\s*d(ays)?\s*$/) + { + $ExpireTime = 24*3600*$1; + } + elsif($Value =~ /^\+(\d+)\s*m(inutes)?\s*$/) + { + $ExpireTime = 60*$1; + } + elsif($Value =~ /^\+(\d+)\s*h(ours)?\s*$/) + { + $ExpireTime = 3600*$1; + } + elsif($Value =~ /^\+(\d+)\s*s(econds)?\s*$/) + { + $ExpireTime = $1; + } + elsif($Value =~ /^\+(\d+)\s*$/) + { + $ExpireTime = $1; + }; + }; + my $absoluteTime = $Value =~ /^\+/ ? $StartTime + $ExpireTime : $Value; return 0 unless $absoluteTime > time; }; @{$ticket->{Expires}} = sort(@{$ticket->{Expires}}); @@ -3803,6 +3758,9 @@ sub read_ticket # ($ticketfile [, $masterkey]) -> &%ticket # sub setup_ticket_file # (@ARGV) { + # Stop when run on-line + return if $ENV{'PATH_INFO'} || $ENV{'QUERY_STRING'}; + my %Settings = (); foreach my $input (@_) { @@ -3823,7 +3781,7 @@ sub setup_ticket_file # (@ARGV) $value =~ s/(^\"([^\"]*)\"$)/\1/g; $Settings{$name} = $value; } - elsif($input !~ m![^\w\.\~\/\:\-]! && $input !~ /^[\-\.]/ && -s $input) + elsif($input !~ m![^\w\.\~\/\:\-]!i && $input !~ /^[\-\.]/i && -s $input) { # We MUST have a salt $Settings{'salt'} = $ticket->{'Salt'}->[0] unless $Settings{'salt'}; @@ -3832,7 +3790,7 @@ sub setup_ticket_file # (@ARGV) $Settings{'newmasterkey'} = $Settings{'masterkey'} unless exists($Settings{'newmasterkey'}); # Get the ticket - my $ticket = read_ticket($input, $Settings{'masterkey'}); + my $ticket = read_ticket($input, $Settings{'salt'}, $Settings{'masterkey'}); # Set a new password from plaintext $ticket->{'Salt'}->[0] = $Settings{'salt'} if $Settings{'salt'} && $Settings{'password'}; @@ -3883,14 +3841,22 @@ sub TicketSignature # ($ticket, $serversalt [, $masterkey]) -> $Signature { 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"; - $SignText .= "AllowedPaths: ".(sort(@{$ticket->{'AllowedPaths'}}))."\n"; - $SignText .= "DeniedPaths: ".(sort(@{$ticket->{'DeniedPaths'}}))."\n"; - $SignText .= "Session: ".(sort(@{$ticket->{'Session'}}))."\n"; - $SignText .= "Time: ".(sort(@{$ticket->{'Time'}}))."\n"; - $SignText .= "Expires: ".(sort(@{$ticket->{'Expires'}})),"\n"; - $SignText .= "Capabilities: ".(sort(@{$ticket->{'Capabilities'}}))."\n"; + my @tmp = sort(@{$ticket->{'Username'}}); + $SignText .= "Username: @tmp\n"; + @tmp = sort(@{$ticket->{'IPaddress'}}); + $SignText .= "IPaddress: @tmp\n"; + @tmp = sort(@{$ticket->{'AllowedPaths'}}); + $SignText .= "AllowedPaths: @tmp\n"; + @tmp = sort(@{$ticket->{'DeniedPaths'}}); + $SignText .= "DeniedPaths: @tmp\n"; + @tmp = sort(@{$ticket->{'Session'}}); + $SignText .= "Session: @tmp\n"; + @tmp = sort(@{$ticket->{'Time'}}); + $SignText .= "Time: @tmp\n"; + @tmp = sort(@{$ticket->{'Expires'}}); + $SignText .= "Expires: @tmp\n"; + @tmp = sort(@{$ticket->{'Capabilities'}}); + $SignText .= "Capabilities: @tmp\n"; $Signature = hash_string(${'hash1'}.$SignText); }; }; diff --git a/Private/.Passwords/admin b/Private/.Passwords/admin index e69de29..a45bad8 100644 --- a/Private/.Passwords/admin +++ b/Private/.Passwords/admin @@ -0,0 +1,15 @@ +Type: INACTIVE PASSWORD +Username: admin +Password: 7f895820a1493b045691eaa56c82e825140c3e45c07d53bf8ca5f772f996bfec +IPaddress: 127.0.0.1 +AllowedPaths: ^/Private/ +AllowedPaths: ^/Private/?$ +Capabilities: CreateUser +Salt: e93cf858a1d5626bf095ea5c25df990dfa969ff5a5dc908b22c9a5229b525f65 +Session: CHALLENGE +Signature: 8736e7dfb2260c698c51b5e8a00260a44f62ba8611bac561920d35f6e27bee38 +Comment: 1 Replace 'INACTIVE PASSWORD' by 'PASSWORD' +Comment: 2 Run CGIscriptor.pl --managelogin masterkey='' Private/.Password/admin +Comment: 3 to get a new signature +Comment: 4 Password is 'There is no password like more password', please change it immediately! +MaxLifetime: +15m diff --git a/Private/.Passwords/test b/Private/.Passwords/test index 576ef42..b13f646 100644 --- a/Private/.Passwords/test +++ b/Private/.Passwords/test @@ -8,5 +8,5 @@ AllowedPaths: ^/Private/[^/]+\.html$ AllowedPaths: ^/Private/?$ Salt: e93cf858a1d5626bf095ea5c25df990dfa969ff5a5dc908b22c9a5229b525f65 Session: SESSION -Signature: 4880ebe45fc41d2a8af3bd4596b32ec7df76060491e808f40b3dc4a96f177243 +Signature: 407a88734a934d697eeb0ea949bb195dbc9f09e1c14d5c545588f7f939172f61 MaxLifetime: +12h diff --git a/Private/.Passwords/testchallenge b/Private/.Passwords/testchallenge index 0bd1461..b85b1c6 100644 --- a/Private/.Passwords/testchallenge +++ b/Private/.Passwords/testchallenge @@ -7,5 +7,5 @@ AllowedPaths: ^/Private/[^/]+\.html$ AllowedPaths: ^/Private/?$ Salt: e93cf858a1d5626bf095ea5c25df990dfa969ff5a5dc908b22c9a5229b525f65 Session: CHALLENGE -Signature: 4880ebe45fc41d2a8af3bd4596b32ec7df76060491e808f40b3dc4a96f177243 +Signature: b871b57138519bc37d8079ac589cd277dc4229cebdc0d14aafa04addcd827d75 MaxLifetime: +45m diff --git a/Private/.Passwords/testip b/Private/.Passwords/testip index 4a2d8d8..6247aec 100644 --- a/Private/.Passwords/testip +++ b/Private/.Passwords/testip @@ -8,5 +8,5 @@ AllowedPaths: ^/Private/?$ DeniedPaths: ^/Private/CreateUser.html.*$ Salt: e93cf858a1d5626bf095ea5c25df990dfa969ff5a5dc908b22c9a5229b525f65 Session: IPADDRESS -Signature: 4880ebe45fc41d2a8af3bd4596b32ec7df76060491e808f40b3dc4a96f177243 +Signature: 427693f5983f04f254d619e01cca07d7a85c128f404b1b0c757d552fe280c2f6 MaxLifetime: +6h -- 2.11.4.GIT