diff options
Diffstat (limited to 'gpigeon-template.cgi')
-rwxr-xr-x | gpigeon-template.cgi | 479 |
1 files changed, 242 insertions, 237 deletions
diff --git a/gpigeon-template.cgi b/gpigeon-template.cgi index 7f82857..be249e9 100755 --- a/gpigeon-template.cgi +++ b/gpigeon-template.cgi @@ -1,26 +1,84 @@ -#! /usr/bin/perl -wT +#! /usr/bin/perl -T use warnings; use strict; -use Digest::SHA qw(sha256_hex); +use Crypt::Argon2 qw(argon2id_verify); use Email::Valid; use String::Random; use CGI qw(param); -#use CGI::Carp qw(fatalsToBrowser); +use CGI::Cookie; +use CGI::Carp qw(fatalsToBrowser); -sub escape_arobase { - my $mailaddress = shift; - my $arobase = '@'; - my $espaced_arob = q{\@}; - my $escapedmailaddress = $mailaddress; - $escapedmailaddress =~ s/$arobase/$espaced_arob/; +sub ValidCookie { + my $client_login_cookie = shift; + my $dir = shift; + if (not defined $client_login_cookie){ + return; + } + my $cookie_line = undef; + my $filename = $client_login_cookie->value; + if ($filename =~ /^([\w]+)$/){ + $filename = $1; + } + else{ + return; + } + my $login_cookiefile = "$dir/$filename.txt"; + if (-e $login_cookiefile){ + open my $in, '<', $login_cookiefile or die "can't read file: $!"; + for(1){ + my $cookie_line = readline $in; + } + close $in; + if (not defined $cookie_line){ + return; + } + } + else{ + return; + } + + if ($client_login_cookie == $cookie_line){ + return 1; + } + return; + +} + +sub LoginCookieGen { + my $id_cookie = shift; + my $dir = shift; + my $str_rand_obj = String::Random->new; + my $val = $str_rand_obj->randregex('\w{64}'); + my $cookiefile = "$dir/$val.txt"; + my $new_login_cookie = CGI::Cookie->new( + -name => 'id', + -value => $val, + -expires => '+1y', + '-max-age' => '+1y', + -domain => ".$ENV{'SERVER_NAME'}", + -path => '/', + -secure => 1, + -httponly => 1, + -samesite => 'Strict', + ) or die "Can't create cookie: $!"; + if (not defined $id_cookie){ + open my $out, '>', $cookiefile or die "Can't write to $cookiefile: $!"; + print $out "$new_login_cookie"; + close $out; + print "Set-Cookie: $new_login_cookie\n"; + } +} + +sub EscapeArobase { + my $escapedmailaddress = shift; + $escapedmailaddress =~ s/@/\\@/; return $escapedmailaddress; } -sub untaint_cgi_filename { +sub UntaintCGIFilename { my $filename = shift; if ($filename =~ /^([-\@\w.\/]+)$/) { - #data untainted $filename = $1; } else { @@ -30,292 +88,239 @@ sub untaint_cgi_filename { return $filename; } -sub notif_if_defined{ +sub NotifIfDefined{ my $notif = shift; if (defined $notif){ return $notif; } + else{ + return '<!--undef notif-->'; + } } +my ($linkgen_notif, $link_asker, $mailisok_notif, $deletion_notif, + $checkedornot, $hidden_pwfield, $id_cookie, + $delete_id_cookie, $idval, $refresh_form) = undef; +my @created_links = (); delete @ENV{qw(IFS PATH CDPATH BASH_ENV)}; -# execute 'printf "yourpassword" | sha256sum' on a terminal -# and copy the long string $ENV{'PATH'} = '/usr/bin'; -my $HAS_MAILSERVER = 0; -my $SRV_NAME = $ENV{'SERVER_NAME'}; -my $HTML_CONTENT_TYPE_HEADER = 'Content-type: text/html'; -my $HTML_CHARSET = 'UTF-8'; -my $HTML_CSS = '/gpigeon.css'; -my $mymailaddr = q{your_mail_address_goes_here}; -my $mymailaddr_pw = q{your_mail_address_password_goes_here}; -my $mymail_smtp = q{smtp_domain_goes_here}; -my $mymail_smtport = q{smtp_port_goes_here}; -my $mymail_gpgid = q{gpgid_goes_here}; #0xlong keyid form -my $PASSWD_HASH = q{password_hash_goes_here}; -my $mymailaddr_escaped = escape_arobase($mymailaddr); -my $msg_form_char_limit = 3000; -my @text_strings = ('Successful removal !', - 'Address', - 'is valid!', - 'is not valid !', - 'Unknown', # displays on main page table when supposed sender isn't identified - 'Message length must be under$msg_form_char_limit chars.', - 'One time GPG messaging form', # title for generated links - 'Type your message below, ', - 'Send to me', - 'Generated a link for', #displays if link gen is successful - 'Link to your one time GPG messaging form', # mail subject when clicking a mailto: link in table - 'Your link is ', # message body when clicking a mailto: link in table - 'Delete', # text on button for deleting links - 'Damn! I cannot open ', # message when file opening fails - 'GPIGEON.CGI: generate one time GPG messaging links !', # main page title! - 'Hi and welcome.', # a greeting at the top of the main page. - 'Disconnect', # disconnect button text on main page - 'Refresh', # refresh button text - 'Generate link', #link generation button text - "Generated links by you, <b>$mymailaddr</b>:", # label above links table - 'Delete all links', # delete all links button text - 'Link', # first table header, 'Link' - 'For', # second table header, 'For' - 'Deletion', # third table header, 'Delete' - 'Deletion failed and here is why : ', - 'Cannot send message : message length must be under ' .$msg_form_char_limit . ' characters.', - 'Cannot send message : message is empty. You can type up to ' . $msg_form_char_limit . ' characters.' +my $hostname = $ENV{'SERVER_NAME'}; +my $USERID = $cgi_query_get->param('username'); +my $PASSWD = $cgi_query_get->param('password'); + +my $argon2id_hash = q{argon2id_hash_goes_here} +my $cookies_dir = q{cookies_dir_goes_here}; +my $link_template_path = q{link_template_path_goes_here}; + +my %text_strings = ( + addr => 'Address', + addr_ok => 'is valid!', + addr_nok => 'is not valid !', + addr_unknown => 'Unknown', + create_link_btn => 'Generate link', + delete_link_btn_text => 'Delete', + delete_links_btn_text => 'Delete all links', + logout_btn_text => 'Logout', + here => 'here', + link_asker_field_label => q{Asker's mail :}, + link_web_title => 'One time GPG messaging form', + link_del_ok => 'Successful removal !', + link_legend_textarea =>'Type your message below :', + link_send_btn => 'Send', + link_ok_for => 'Generated a link for', + link_del_failed => 'Deletion failed and here is why : ', + mailto_body => 'Your link is ', + mailto_subject => 'Link to your one time GPG messaging form', + notif_login_failure => 'Cannot login. Check if your username and password match.' + refresh_btn_text => 'Refresh', + type_msg_below => 'Type your message below', + theader_link => 'Link', + theader_for => 'For', + theader_deletion => 'Deletion', + web_title => 'GPIGEON.CGI: generate one time GPG messaging links !', + web_greet_msg => 'Hi and welcome.', ); my $cgi_query_get = CGI->new; -my $PASSWD = $cgi_query_get->param('password'); -my $psswd_formfield = '<input type="hidden" name="password" value="' . $cgi_query_get->param('password') . '">'; -my ($notif_de_creation, $notif_mail_valide, $notif_suppression) = undef; -my @created_links = (); +my $pw = $cgi_query_get->param('password'); +my $logout = $cgi_query_get->param('logout'); +my %cur_cookies = CGI::Cookie->fetch; +$id_cookie = $cur_cookies{'id'}; +if (not defined $id_cookie){ + $hidden_pwfield = qq{<input type="hidden" name="password" value="$pw">}; + $refresh_form = qq{<form method="POST"> + $hidden_pwfield + <input type="submit" value="$text_strings{refresh_btn_text}"> + </form>}; +} +else{ + $refresh_form = qq{<form method="GET"> + <input type="submit" value="$text_strings{refresh_btn_text}"> + </form>}; + $idval = $id_cookie->value; + + if ($idval =~ /^([\w]+)$/){ + $idval = $1; + } -if ( sha256_hex($PASSWD) eq $PASSWD_HASH and $ENV{'REQUEST_METHOD'} eq 'POST'){ + if ($logout){ + $delete_id_cookie = CGI::Cookie->new( + -name => 'id', + -value => $idval, + -expires => '-1d', + '-max-age' => '-1d', + -domain => ".$hostname", + -path => '/', + -secure => 1, + -httponly => 1, + -samesite => 'Strict', + ); + my $f = "$cookies_dir/$idval.txt"; + if (-e "$f"){ + unlink "$f" or die "Can't delete file :$!"; + } + print "Set-Cookie: $delete_id_cookie\n"; + } +} +print "Cache-Control: no-store, must-revalidate\n"; +if (ValidCookie($id_cookie, $cookies_dir) or argon2id_verify($argon2id_hash,$pw)){ + + LoginCookieGen($id_cookie, $cookies_dir); + if (defined $cgi_query_get->param('supprlien')){ my $pending_deletion = $cgi_query_get->param('supprlien'); - my $gpg_form_fn = "./l/$pending_deletion"; - if (unlink untaint_cgi_filename($gpg_form_fn)){ - $notif_suppression='<span style="color:green">'.$text_strings[0].'</span>'; + my $linkfile_fn = "./l/$pending_deletion"; + if (unlink UntaintCGIFilename($linkfile_fn)){ + $deletion_notif = qq{<span style="color:green">$text_strings{link_del_ok}</span>}; } else { - $notif_suppression='<span style="color:red">'. $text_strings[24] . $gpg_form_fn.':'. $! .'</span>'; + $deletion_notif = qq{<span style="color:red">$text_strings{link_del_failed} $linkfile_fn : $!</span>}; } } if (defined $cgi_query_get->param('supprtout')){ opendir my $link_dir_handle, './l' or die "Can't open ./l: $!"; - while (readdir $link_dir_handle) { if ($_ ne '.' and $_ ne '..'){ - my $gpg_form_fn = "./l/$_"; - unlink untaint_cgi_filename($gpg_form_fn) or die "$!"; - $notif_suppression='<span style="color:green">'. $text_strings[0] .'</span>'; + unlink UntaintCGIFilename("./l/$_") or die "$!"; + $deletion_notif = qq{<span style="color:green">$text_strings{link_del_ok}</span>}; } } closedir $link_dir_handle; } if (defined $cgi_query_get->param('mail')){ - my $non_gpguser = scalar $cgi_query_get->param('mail'); + $link_asker = scalar $cgi_query_get->param('mail'); - if ( Email::Valid->address($non_gpguser) ){ - $notif_mail_valide = '<span style="color:green">'. $text_strings[1] . ' '. $non_gpguser.' '. $text_strings[2] . '</span>'; - my $escaped_non_gpguser = escape_arobase($non_gpguser); - my $random_mailform_fn_str = String::Random->new; - my @mailform_fn_str_buffer = (); + if ( Email::Valid->address($link_asker) ){ + $mailisok_notif = qq{<span style="color:green">$text_strings{addr} $link_asker $text_strings{addr_ok}</span>}; + my $escaped_link_asker = EscapeArobase($link_asker); + my $str_rand_obj = String::Random->new; + my $generated_form_filename = $str_rand_obj->randregex('\w{64}') . '.cgi'; + my $href = "https://$hostname/cgi-bin/l/$generated_form_filename"; + my $link_path = "./l/$generated_form_filename"; - for (1..5){ - push @mailform_fn_str_buffer, - $random_mailform_fn_str->randregex('\w{1,15}[0-9]{1,15}'); + open my $in, '<', $link_template_path or die "Can't read link template file: $!"; + open my $out, '>', $link_path or die "Can't write to link file: $!"; + while( <$in> ) { + s/{link_user}/{$link_asker}/g; + s/{link_web_title}/$text_strings{link_web_title}/g; + s/{link_send_btn}/$text_strings{link_send_btn}/g; + s/{type_msg_below}/$text_strings{type_msg_below}/g; + print $out $_; } - - my $mailform_fn_str_buffer_nospace = join('',@mailform_fn_str_buffer); - my $GENERATED_FORM_FILENAME = "$mailform_fn_str_buffer_nospace.cgi"; - my $MAILFORM_LINK = "http://$SRV_NAME/cgi-bin/l/$GENERATED_FORM_FILENAME"; - my $MAILFORM_RELPATH = "./l/$GENERATED_FORM_FILENAME"; - if (open my $gpg_form_fh, ">", $MAILFORM_RELPATH){ - print $gpg_form_fh '#! /usr/bin/perl -wT',"\n\n", - ' my $non_gpguser = q{'. $non_gpguser .'};', "\n", - 'delete @ENV{qw(IFS PATH CDPATH BASH_ENV)};', "\n", - '$ENV{\'PATH\'}="/usr/bin";', "\n", - 'use warnings;', "\n", - 'use strict;',"\n", - 'use GPG;',"\n", - '#use CGI::Carp qw(fatalsToBrowser);', "\n", - 'use CGI qw(param);', "\n", - 'my $cgi_query_get = CGI->new;', "\n", - 'my ($msg_form, $enc_msg, $error_processing_msg,$msg_form_char_limit) = undef;', "\n", - '$msg_form_char_limit = '. $msg_form_char_limit . ' ;', "\n", - '$msg_form = $cgi_query_get->param(\'msg\');', "\n", - 'my $length_msg_form = length $msg_form;', "\n", - - 'if (defined $length_msg_form and $length_msg_form > $msg_form_char_limit){', "\n", - ' $error_processing_msg = q{<span style="color:red"><b>'. $text_strings[25] .'.</b></span>};', "\n", - '} elsif (defined $length_msg_form and $length_msg_form eq 0 ){', "\n", - ' $error_processing_msg = q{<span style="color:red"><b>'. $text_strings[26] . '.</b></span>};', "\n", - '} else {', "\n", - ' if (defined $length_msg_form and $ENV{\'REQUEST_METHOD\'} eq \'POST\'){',"\n", - ' $msg_form =~ tr/\r//d;', "\n", - ' my $gpg = new GPG(gnupg_path => "/usr/bin", homedir => "/usr/share/www-data/.gnupg/");', "\n", - ' $enc_msg = $gpg->encrypt("De la part de " . $non_gpguser . ":\n". $msg_form, \''. $mymail_gpgid .'\') or die $gpg->error();', "\n"; - if ($HAS_MAILSERVER){ - undef $mymailaddr_escaped; - print $gpg_form_fh "\n", - ' use Mail::Sendmail;', "\n", - ' my %mail = ( To => \''.$mymailaddr.'\', ', "\n", - ' From => \''.$mymailaddr.'\', ', "\n", - ' Subject => \'Gpigeon\', ', "\n", - ' Message => "$enc_msg\n" ', "\n", - ' );', "\n", - ' sendmail(%mail) or die $Mail::Sendmail::error;', "\n"; - } - else { - print $gpg_form_fh "\n", - ' use Net::SMTP;',"\n", - ' use Net::SMTPS;',"\n", - ' my $smtp = Net::SMTPS->new(\''. $mymail_smtp .'\', Port => \''. $mymail_smtport .'\', doSSL => \'ssl\', Debug_SSL => 0);', "\n", - ' $smtp->auth(\''. $mymailaddr .'\', \''. $mymailaddr_pw .'\') or die;', "\n", - ' $smtp->mail(\''. $mymailaddr .'\') or die "Net::SMTP module has broke: $!.";', "\n", - ' if ($smtp->to(\''. $mymailaddr .'\')){', "\n", - ' $smtp->data();', "\n", - ' $smtp->datasend("To: '. $mymailaddr_escaped .'\n");', "\n", - ' $smtp->datasend("\n");', "\n", - ' $smtp->datasend("$enc_msg\n");', "\n", - ' $smtp->dataend();', "\n", - ' }', "\n", - ' else {', "\n", - ' die $smtp->message();', "\n", - ' }', "\n"; - } - print $gpg_form_fh "\n", - ' unlink "../' . $MAILFORM_RELPATH . '";', "\n", - ' print "Location: /merci/index.html\n\n";', "\n", - ' }', "\n", - '}', "\n", - 'print "Content-type: text/html", "\n\n";', "\n", - 'print q{<!DOCTYPE html>', "\n", - '<html>', "\n", - ' <head>', "\n", - ' <link rel="icon" sizes="48x48" type="image/ico" href="/favicon.ico">', "\n", - ' <link rel="stylesheet" type="text/css" href="'. $HTML_CSS .'">', - ' <meta http-equiv="content-type" content="text/html;charset='. $HTML_CHARSET .'">',"\n",'<meta charset="'. $HTML_CHARSET .'">',"\n", - ' <title>Formulaire d\'envoi de message GPG</title>',"\n", - ' </head>', "\n", - ' <body>', "\n", - ' <p>'. $text_strings[7] . '<b>' . $non_gpguser .'</b> :</p>', "\n", - ' <form method="POST">', "\n", - ' <textarea wrap="off" cols="50" rows="30" name="msg"></textarea><br>', - '};', "\n", - 'if (defined $error_processing_msg){printf $error_processing_msg;}', "\n", - 'printf qq{ <br> - <input type="submit" value="'. $text_strings[8] .'">', "\n", - ' </form>', "\n", - ' </body>', "\n", - '</html> };'; - close $gpg_form_fh; - chmod(0755,$MAILFORM_RELPATH); - $notif_de_creation='<span style="color:green">'. $text_strings[9] . $non_gpguser .'</span><br><a href="'. $MAILFORM_LINK .'">'. $MAILFORM_LINK .'</a>'; - } - else{ - close $gpg_form_fh and die "Can't open $MAILFORM_RELPATH: $!"; - } + close $in or die; + chmod(0755,$link_path) or die; + close $out or die; + $linkgen_notif = qq{<span style="color:green">$text_strings{link_ok_for} $link_asker: </span><br><a href="$href">$href</a>}; } else{ - $notif_mail_valide = "<span style='color:red'>$text_strings[1] $non_gpguser $text_strings[3].</span>"; + $mailisok_notif = qq{<span style="color:red">$text_strings{addr} $link_asker $text_strings{addr_nok}.</span>}; } } - - opendir my $link_dir_handle, './l' or die "Can't open ./l: $!"; + opendir my $link_dir_handle, './l' or die "Can't open ./l: $!"; while (readdir $link_dir_handle) { if ($_ ne '.' and $_ ne '..'){ - my $gpg_form_fn = $_; - my $non_gpguser = undef; - if (open my $gpg_form_handle , '<', "./l/$gpg_form_fn"){ - - for (1..3){ - $non_gpguser = readline $gpg_form_handle; - $non_gpguser =~ s/q\{(.*?)\}//i; - $non_gpguser = $1; + my $linkfile_fn = $_; + if (open my $linkfile_handle , '<', "./l/$linkfile_fn"){ + for (1..2){ + $link_asker = readline $linkfile_handle; + $link_asker =~ s/q\{(.*?)\}//i; + $link_asker = $1; } - close $gpg_form_handle; + close $linkfile_handle; - if (not defined $non_gpguser){ - $non_gpguser = $text_strings[4]; + if (Email::Valid->address($link_asker){ + push @created_links, + qq{<tr> + <td><a href="/cgi-bin/l/$linkfile_fn">$text_strings{here}</a></td> + <td><a href="mailto:$link_asker?subject=$text_strings{mailto_subject}&body=$text_strings{mailto_body} http://$hostname/cgi-bin/l/$linkfile_fn">$link_asker</a></td> + <td> + <form method="POST"> + $hidden_pwfield + <input type="hidden" name="supprlien" value="$linkfile_fn"> + <input type="submit" value="$text_strings{delete_link_btn_text}"> + </form> + </td> + </tr>}; } - - #create links table html - push @created_links, - '<tr> - <td><a href="/cgi-bin/l/'. $gpg_form_fn .'">ici</a></td> - <td><a href="mailto:'. $non_gpguser .'?subject='. $text_strings[10] .'&body='. $text_strings[11] .'http://$SRV_NAME/cgi-bin/l/'. $gpg_form_fn .'">'.$non_gpguser.'</a></td> - <td> - <form method="POST"> - <input type="hidden" name="supprlien" value="'. $gpg_form_fn .'"> - <input type="hidden" name="password" value="'. $cgi_query_get->param('password') .'"> - <input type="submit" value="'. $text_strings[12] .'"> - </form> - </td> - </tr>'; - } else { - close $gpg_form_handle; - die 'Content-type: text/plain', "\n\n", "$text_strings[13] $gpg_form_fn: $!"; + close $linkfile_handle; + die 'Content-type: text/plain', "\n\n", "Error: Can't open $linkfile_fn: $!"; } } } closedir $link_dir_handle; - print $HTML_CONTENT_TYPE_HEADER,"\n\n", - '<!DOCTYPE html> + print 'Content-type: text/html',"\n\n", + qq{<!DOCTYPE html> <html> <head> <link rel="icon" sizes="48x48" type="image/ico" href="/favicon.ico"> - <link rel="stylesheet" type="text/css" href="'. $HTML_CSS .'"> - <meta http-equiv="content-type" content="text/html;charset='. $HTML_CHARSET .'">',"\n",'<meta charset="'. $HTML_CHARSET .'"> - <title>'. $text_strings[14] .'</title> + <link rel="stylesheet" type="text/css" href="/styles.css"> + <meta http-equiv="content-type" content="text/html;charset=UTF-8"> + <meta charset="UTF-8"> + <title>$text_strings{web_title}</title> </head> <body> - <p>'. $text_strings[15] .'</p> - <form method="POST"> - <input type="hidden" name="password" value="0"> - <input type="submit" value="'. $text_strings[16] .'"> - </form> - <form method="POST">', - $psswd_formfield, - '<input type="submit" value="'. $text_strings[17] .'"> + <p>$text_strings{web_greet_msg}</p> + <form method="GET"> + <input type="hidden" name="logout" value="1"> + <input type="submit" value="$text_strings{logout_btn_text}"> </form> + $refresh_form <hr> <br> - <form method="POST">', - $psswd_formfield, - 'Mail de la personne:<br> + <form method="POST"> + $hidden_pwfield + $text_strings{link_asker_field_label}<br> <input tabindex="1" type="text" name="mail"> - <input tabindex="2" type="submit" value="'. $text_strings[18] .'"> - </form>'; - print notif_if_defined($notif_mail_valide); - print '<br>'; - print notif_if_defined($notif_de_creation); - print '<hr> - <p>'. $text_strings[19] .'</p>', - '<form method="POST">', - $psswd_formfield, - '<input type="hidden" name="supprtout"> - <input type="submit" value="'. $text_strings[20] .'"> - </form>', - notif_if_defined($notif_suppression), - '<table> + <input tabindex="2" type="submit" value="$text_strings{create_link_btn}"> + </form>}, + NotifIfDefined($mailisok_notif), + '<br>', + NotifIfDefined($linkgen_notif), + qq{<hr> + <form method="POST"> + $hidden_pwfield + <input type="hidden" name="supprtout"> + <input type="submit" value="$text_strings{delete_links_btn_text}"> + </form>}, + NotifIfDefined($deletion_notif), + qq{<table> <tr> - <th>'. $text_strings[21] .'</th>', - '<th>'. $text_strings[22] .'</th>', - '<th>'. $text_strings[23] .'</th>', - '</tr>', - "@created_links", - '</table> + <th>$text_strings{theader_link}</th> + <th>$text_strings{theader_for}</th> + <th>$text_strings{theader_deletion}</th> + </tr> + @created_links + </table> </body> - </html>'; + </html>}; } -else { - print 'Location: /index.html', "\n\n"; +else{ + print "Location: /\n\n"; } |