diff options
author | Miquel Lionel <lionel@les-miquelots.net> | 2021-04-11 18:20:41 +0100 |
---|---|---|
committer | Miquel Lionel <lionelmiquel@sfr.fr> | 2021-04-11 18:20:41 +0100 |
commit | 8cbc83817dbafd2ac26d9834009e6cfa3d58b3d7 (patch) | |
tree | a35776798fd2a5edc33c989a0f4b0b38c927eb93 /gpigeon-template.cgi | |
parent | 734fe365eac84cc94d5b814df617f24f7d597f91 (diff) | |
download | gpigeon-8cbc83817dbafd2ac26d9834009e6cfa3d58b3d7.tar.gz gpigeon-8cbc83817dbafd2ac26d9834009e6cfa3d58b3d7.zip |
Cookie based login added
- the cookie last 1 year and is set upon login
- deleted when pressing "Disconnect" button
- updated the config.mk and Makefile for COOKIES_DIR variable
- camel-cased the subroutines. I like it that way, it stands out more
compared to variables
- reinserted some vars back into link-tmpl.cgi, it wasn't making sense
to have them in gpigeon-template.cgi
- no more 'Unknown' link asker in the links table: if the string is a valid
email address, we link to the file in the table.
Diffstat (limited to 'gpigeon-template.cgi')
-rwxr-xr-x | gpigeon-template.cgi | 264 |
1 files changed, 177 insertions, 87 deletions
diff --git a/gpigeon-template.cgi b/gpigeon-template.cgi index 277ae0a..5470dfd 100755 --- a/gpigeon-template.cgi +++ b/gpigeon-template.cgi @@ -6,23 +6,79 @@ use Crypt::Argon2 qw(argon2id_verify); use Email::Valid; use String::Random; use CGI qw(param); -#use CGI::Session; +use CGI::Cookie; use CGI::Carp qw(fatalsToBrowser); -sub notif_if_defined{ - my $notif = shift; - if (defined $notif){ - return $notif; +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 '<!-- undef -->'; + 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 untaint_cgi_filename { +sub EscapeArobase { + my $escapedmailaddress = shift; + $escapedmailaddress =~ s/@/\\@/; + return $escapedmailaddress; +} + +sub UntaintCGIFilename { my $filename = shift; if ($filename =~ /^([-\@\w.\/]+)$/) { - #data untainted $filename = $1; } else { @@ -32,16 +88,27 @@ sub untaint_cgi_filename { return $filename; } +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)}; $ENV{'PATH'} = '/usr/bin'; -my $cgi_query_get = CGI->new; -my @created_links = (); -my ($linkgen_notif, $mailisok_notif, $deletion_notif) = undef; -my $LINK_TEMPLATE_PATH='/usr/share/webapps/gpigeon/link-template.pl'; # this is the file where the SMTP and mail address values goes -my $HOSTNAME = $ENV{'SERVER_NAME'}; -my $msg_form_char_limit = 3000; -my $PASSWD_HASH = q{password_hash_goes_here}; #argon2id hash format -my $PASSWD = $cgi_query_get->param('password'); +my $hostname = $ENV{'SERVER_NAME'}; + +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', @@ -51,20 +118,20 @@ my %text_strings = ( create_link_btn => 'Generate link', delete_link_btn_text => 'Delete', delete_links_btn_text => 'Delete all links', - disconnect_btn_text => 'Disconnect', + logout_btn_text => 'Logout', here => 'here', - link_web_title => 'One time GPG messaging form', login => 'Login', + 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_generated_ok => 'Generated a link for', + link_ok_for => 'Generated a link for', link_del_failed => 'Deletion failed and here is why : ', - notif_login_failure => 'Cannot login. Check if your username and password match.' mailto_body => 'Your link is ', mailto_subject => 'Link to your one time GPG messaging form', - msg_too_long => 'Cannot send message : message length must be under ' .$msg_form_char_limit . ' characters.', - msg_empty => 'Cannot send message : message is empty. You can type up to ' . $msg_form_char_limit . ' characters.', + notif_login_failure => 'Cannot login. Check if your username and password match.' + passwd_label => 'Password :', refresh_btn_text => 'Refresh', type_msg_below => 'Type your message below', theader_link => 'Link', @@ -73,17 +140,62 @@ my %text_strings = ( web_title => 'GPIGEON.CGI: generate one time GPG messaging links !', web_greet_msg => 'Hi and welcome.', ); +my $cgi_query_get = CGI->new; +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 (argon2id_verify($PASSWD_HASH,$PASSWD)){ - my $hidden_pwfield = qq{<input type="hidden" name="password" value="$PASSWD">}; + if ($idval =~ /^([\w]+)$/){ + $idval = $1; + } + + 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 $linkfile_fn = "./l/$pending_deletion"; - if (unlink untaint_cgi_filename($linkfile_fn)){ - $deletion_notif=qq{<span style="color:green">$text_strings{link_del_ok}</span>}; + if (unlink UntaintCGIFilename($linkfile_fn)){ + $deletion_notif = qq{<span style="color:green">$text_strings{link_del_ok}</span>}; } else { - $deletion_notif=qq{<span style="color:red">$text_strings{link_del_failed} $linkfile_fn : $!</span>}; + $deletion_notif = qq{<span style="color:red">$text_strings{link_del_failed} $linkfile_fn : $!</span>}; } } @@ -91,56 +203,47 @@ if (argon2id_verify($PASSWD_HASH,$PASSWD)){ opendir my $link_dir_handle, './l' or die "Can't open ./l: $!"; while (readdir $link_dir_handle) { if ($_ ne '.' and $_ ne '..'){ - my $linkfile_fn = "./l/$_"; - unlink untaint_cgi_filename($linkfile_fn) or die "$!"; - $deletion_notif=qq{<span style="color:green">$text_strings{link_del_ok}</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 $link_asker = scalar $cgi_query_get->param('mail'); + $link_asker = scalar $cgi_query_get->param('mail'); 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 = escape_arobase($link_asker); + my $escaped_link_asker = EscapeArobase($link_asker); my $str_rand_obj = String::Random->new; - my $random_fn = $str_rand_obj->randregex('\w{64}'); - my $GENERATED_FORM_FILENAME = "$random_fn.cgi"; - my $HREF_LINK = "https://$HOSTNAME/cgi-bin/l/$GENERATED_FORM_FILENAME"; - my $LINK_PATH = "./l/$GENERATED_FORM_FILENAME"; + 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"; - 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: $!"; + 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_filename}/{$GENERATED_FORM_FILENAME}/g; - s/{msg_too_long}/$text_strings{msg_too_long}/g; - s/{msg_empty}/$text_strings{msg_empty}/g; - s/{msg_form_char_limit}/$msg_form_char_limit/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 $_; } close $in or die; - chmod(0755,$LINK_PATH) or die; + chmod(0755,$link_path) or die; close $out or die; - - $linkgen_notif = qq{<span style="color:green">$text_strings{link_generated_ok} $link_asker: </span><br><a href="$HREF_LINK">$HREF_LINK</a>}; + $linkgen_notif = qq{<span style="color:green">$text_strings{link_ok_for} $link_asker: </span><br><a href="$href">$href</a>}; } else{ $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: $!"; while (readdir $link_dir_handle) { if ($_ ne '.' and $_ ne '..'){ my $linkfile_fn = $_; - my $link_asker = undef; if (open my $linkfile_handle , '<', "./l/$linkfile_fn"){ for (1..2){ $link_asker = readline $linkfile_handle; @@ -149,23 +252,20 @@ if (argon2id_verify($PASSWD_HASH,$PASSWD)){ } close $linkfile_handle; - if (not defined $link_asker){ - $link_asker = $text_strings{unknown}; + 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, - qq{<tr> - <td><a href="/cgi-bin/l/$linkfile_fn">ici</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"> - <input type="hidden" name="supprlien" value="$linkfile_fn"> - <input type="hidden" name="password" value="$PASSWD"> - <input type="submit" value="$text_strings{delete_link_btn_text}"> - </form> - </td> - </tr>}; - } else { close $linkfile_handle; @@ -187,32 +287,29 @@ if (argon2id_verify($PASSWD_HASH,$PASSWD)){ </head> <body> <p>$text_strings{web_greet_msg}</p> - <form method="POST"> - <input type="hidden" name="password" value="0"> - <input type="submit" value="$text_strings{disconnect_btn_text}"> - </form> - <form method="POST"> - $hidden_pwfield - <input type="submit" value="$text_strings{refresh_btn_text}"> + <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"> $hidden_pwfield - Mail de la personne:<br> + $text_strings{link_asker_field_label}<br> <input tabindex="1" type="text" name="mail"> <input tabindex="2" type="submit" value="$text_strings{create_link_btn}"> </form>}, - notif_if_defined($mailisok_notif), + NotifIfDefined($mailisok_notif), '<br>', - notif_if_defined($linkgen_notif), + 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>}, - notif_if_defined($deletion_notif), + NotifIfDefined($deletion_notif), qq{<table> <tr> <th>$text_strings{theader_link}</th> @@ -224,8 +321,8 @@ if (argon2id_verify($PASSWD_HASH,$PASSWD)){ </body> </html>}; } -else { - print 'Content-type: text/html',"\n\n", +else{ + print "Content-Type: text/html\n\n", qq{<!DOCTYPE html> <html> <head> @@ -236,21 +333,14 @@ else { </head> <body> <form action="/cgi-bin/gpigeon.cgi" method="POST"> - <h1 style="text-align:center">GPIGEON</h1> - Mot de passe : <input type="password" name="password"><br> + <h1>GPIGEON</h1> + <label for="pwfield"> $text_strings{passwd_label}:</label> + <input id="pwfield" type="password" name="password"><br> <input type="submit" value="$text_strings{login}"> </form> - - <p><a + <p> <a href="http://git.les-miquelots.net/gpigeon" title="gpigeon download link" alt="gpigeon download link">Source code here.</a> It is similar to <a href="https://hawkpost.co/">hawkpost.co</a>.</p> - - <a href="https://xkcd.com/538"><img id="crypto_secu" - src="security.png" title="XKCD fait redescendre les nerds du - chiffrement sur terre (xkcd.com/538)" alt="BD de XKCD faisant redescendre les - nerds du chiffrement sur terre"></a> - </body> - </html>}; } |