aboutsummaryrefslogtreecommitdiff
path: root/gpigeon-template.cgi
diff options
context:
space:
mode:
authorMiquel Lionel <lionel@les-miquelots.net>2021-04-11 18:20:41 +0100
committerMiquel Lionel <lionelmiquel@sfr.fr>2021-04-11 18:20:41 +0100
commit8cbc83817dbafd2ac26d9834009e6cfa3d58b3d7 (patch)
treea35776798fd2a5edc33c989a0f4b0b38c927eb93 /gpigeon-template.cgi
parent734fe365eac84cc94d5b814df617f24f7d597f91 (diff)
downloadgpigeon-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-xgpigeon-template.cgi264
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">&nbsp;$text_strings{passwd_label}:</label>
+ <input id="pwfield" type="password" name="password"><br>
<input type="submit" value="$text_strings{login}">
</form>
-
- <p><a
+ <p>&nbsp;<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>};
}