aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMiquel Lionel <lionel@les-miquelots.net>2021-06-24 13:53:58 +0100
committerMiquel Lionel <lionelmiquel@sfr.fr>2021-07-02 01:21:59 +0100
commitde854ac0a8fe72a1b0d0137c351b39de25e0adfe (patch)
treeae1386ade7ebb65e2b269347b09c8ced87fb614f
parent420155e750c4b95dd4327d7adb4310a64eafb364 (diff)
downloadgpigeon-de854ac0a8fe72a1b0d0137c351b39de25e0adfe.tar.gz
gpigeon-de854ac0a8fe72a1b0d0137c351b39de25e0adfe.zip
Implemented very basic multi-user support
- the `gpigeonctl' script is used to initialize the database, add an user and delete it, clear cookies. More info in the readme about it. - tweaked gpigeon-template.cgi and link-template-tmpl.cgi to support multiusers - tweaked the Makefile - tweaked the config.def.mk and added WWWUSER and WWWDIR variables - made some ajustements in the styles.css file for responsivity. - added some headers for security in nginx-example.conf - added gpigeonctl.def which serves as a "blueprint" for gpigeonctl
-rw-r--r--Makefile92
-rw-r--r--README.md39
-rw-r--r--config.def.mk26
-rw-r--r--domains.csv323
-rwxr-xr-xgpigeon-template.cgi424
-rwxr-xr-xgpigeonctl101
-rwxr-xr-xgpigeonctl.def.pl283
-rw-r--r--index.html44
-rw-r--r--link-tmpl-template.cgi28
-rw-r--r--nginx-example.conf6
-rw-r--r--styles.css163
11 files changed, 844 insertions, 685 deletions
diff --git a/Makefile b/Makefile
index 9781a3a..1a5ed63 100644
--- a/Makefile
+++ b/Makefile
@@ -1,28 +1,37 @@
.POSIX:
-# you should comment this line @if non-GNU make
-# and execute genpass.pl manually and edit config.mk
-# with the resulting argon2id hash.
BOLD=\033[01m
RED=\033[31m
STOP=\033[0m
-#RANDOM_ARGON2 := $(shell perl genpass.pl > genpass.txt && tail -1 genpass.txt)
include config.mk
gpigeon: gpigeon-template.cgi link-tmpl-template.cgi
+ @if test -z '$(BINPREFIX)'; then \
+ printf "\n$(RED)No \u0024BINPREFIX variable defined in config.mk.\n";\
+ printf "Look into config.def.mk for the defaults and fix that in your config.mk.$(STOP)\n";\
+ exit 1;\
+ else \
+ printf "\n\u0024BINPREFIX var is set to $(BOLD)$(BINPREFIX)$(STOP)";\
+ fi
@if test -z '$(PREFIX)'; then \
printf "\n$(RED)No \u0024PREFIX variable defined in config.mk.\n";\
- printf "Look into config.def.mk for the defaults and fix that.$(STOP)\n";\
+ printf "Look into config.def.mk for the defaults and fix that in your config.mk.$(STOP)\n";\
exit 1;\
else \
printf "\n\u0024PREFIX var is set to $(BOLD)$(PREFIX)$(STOP)";\
fi
-
@if test -z '$(WWWPREFIX)'; then\
- printf "\n${RED}No web directory defined in config.mk. Check your config.def.mk for the defaults and fix that.${STOP}";\
+ printf "\n${RED}No web directory defined in config.mk. Check your config.def.mk for the defaults and fix that in your config.mk.${STOP}";\
exit 1; \
else \
- printf "\nThe WWW directory is $(BOLD)$(WWWPREFIX)$(STOP)";\
+ printf "\nThe WWW directory is $(BOLD)$(WWWDIR)$(STOP)";\
+ fi
+ @if test -z '$(DB_PATH)'; then\
+ printf "\n${RED}No database path defined in config.mk. Check your config.def.mk for the defaults and fix that in your config.mk$(STOP)";\
+ exit 1; \
+ else \
+ printf "\nThe path to the SQLite database is $(BOLD)$(DB_PATH)$(STOP)";\
+ sed -e 's|db_path_goes_here|$(DB_PATH)|g' gpigeon-template.cgi > gpigeon.cgi;\
fi
@if test -n '$(_GPG_HOMEDIR)'; then \
@@ -32,28 +41,31 @@ gpigeon: gpigeon-template.cgi link-tmpl-template.cgi
$(MAKE) clean ;\
exit 1;\
fi
+
@if test -n '$(LINK_TEMPLATE_PATH)'; then \
- printf "\nLink template is at ${BOLD}$(LINK_TEMPLATE_PATH)${STOP}"; \
- sed -e 's|link_template_path_goes_here|$(LINK_TEMPLATE_PATH)|g' gpigeon-template.cgi > gpigeon.cgi; \
+ printf "\nLink template is at ${BOLD}$(LINK_TEMPLATE_PATH)${STOP}\n"; \
+ sed -e 's|link_template_path_goes_here|$(LINK_TEMPLATE_PATH)|g' -i gpigeon.cgi; \
else \
printf "\n${RED}The path for the link template wasn't set in your config.mk. Fix that.${STOP}" ;\
exit 1;\
fi
+
@if test -n '$(MSG_FORM_CHAR_LIMIT)'; then \
- printf "Message form will have a message limit of ${BOLD}$(MSG_FORM_CHAR_LIMIT) characters${STOP}\n"; \
+ printf "Message form will have a message limit of ${BOLD}$(MSG_FORM_CHAR_LIMIT) characters${STOP}\n"; \
sed -e "s|msg_char_limit_goes_here|$(MSG_FORM_CHAR_LIMIT)|g" link-tmpl-template.cgi > link-tmpl.cgi;\
- else \
- printf "${RED}No character limits were defined in your config.mk. Fix that.${STOP}\n" ;\
- $(MAKE) clean ;\
- exit 1;\
+ else \
+ printf "${RED}No character limits were defined in your config.mk. Fix that.${STOP}\n" ;\
+ $(MAKE) clean ;\
+ exit 1;\
fi
+
@if [ '${HAS_MAILSERVER}' == '1' ]; then \
printf "Local mail server setup. ${BOLD}Mail::Sendmail module will be used to send the mails${STOP}.\n"; \
else \
printf "External mail server setup. ${BOLD}Net::SMTPS module will be used to send the mails${STOP}.\n"; \
if test -n '$(MAILSENDER)'; then \
printf "\tEncrypted mails will be sent from ${BOLD}$(MAILSENDER)${STOP}\n"; \
- sed -e 's|sender_addr_goes_here|$(MAILSENDER)|g' link-tmpl-template.cgi > link-tmpl.cgi; \
+ sed -e 's|sender_addr_goes_here|$(MAILSENDER)|g' -i link-tmpl.cgi; \
else \
printf "\t${RED}No mail sender adress configured in your config.mk. Fix this.${STOP}\n" ; \
$(MAKE) clean ; \
@@ -61,7 +73,7 @@ gpigeon: gpigeon-template.cgi link-tmpl-template.cgi
fi; \
if test -n '$(MAILSENDER_PW)'; then \
printf "\tPassword for ${BOLD}${MAILSENDER}${STOP} is %s.\n" '${MAILSENDER_PW}'; \
- sed -e 's|sender_pw_goes_here|$(MAILSENDER_PW)|g' link-tmpl-template.cgi > link-tmpl.cgi; \
+ sed -e 's|sender_pw_goes_here|$(MAILSENDER_PW)|g' -i link-tmpl.cgi; \
else\
printf "\t${RED}Password for the sender address wasn't set in your config.mk. Fix this${STOP}.\n";\
$(MAKE) clean ; \
@@ -69,7 +81,7 @@ gpigeon: gpigeon-template.cgi link-tmpl-template.cgi
fi; \
if test -n '$(SMTP_DOMAIN)'; then \
printf "\tSMTP server: ${BOLD}$(SMTP_DOMAIN)${STOP}\n"; \
- sed -e 's|smtp_domain_goes_here|$(SMTP_DOMAIN)|g' link-tmpl-template.cgi > link-tmpl.cgi; \
+ sed -e 's|smtp_domain_goes_here|$(SMTP_DOMAIN)|g' -i link-tmpl.cgi; \
else\
printf "\t${RED}No SMTP server was configured in your config.mk. Fix this.${STOP}\n";\
$(MAKE) clean ; \
@@ -77,46 +89,56 @@ gpigeon: gpigeon-template.cgi link-tmpl-template.cgi
fi; \
if test -n '$(SMTP_PORT)'; then \
printf "\tSMTP port: ${BOLD}$(SMTP_PORT)${STOP}\n"; \
- sed -e 's|smtp_port_goes_here|$(SMTP_PORT)|g' link-tmpl-template.cgi > link-tmpl.cgi; \
+ sed -e 's|smtp_port_goes_here|$(SMTP_PORT)|g' -i link-tmpl.cgi; \
else \
printf "\t${RED}No SMTP port configured in your config.mk. Fix this${STOP}.\n"; \
$(MAKE) clean ; \
exit 1; \
fi; \
fi
- @sed -e 's|has_mailserver_goes_here|$(HAS_MAILSERVER)|g' link-tmpl-template.cgi > link-tmpl.cgi;
- @sed -e 's|gpg_homedir_goes_here|$(_GPG_HOMEDIR)|g' link-tmpl-template.cgi > link-tmpl.cgi;
- @sed -e 's|gpg_homedir_goes_here|$(_GPG_HOMEDIR)|g' gpigeon-template.cgi > gpigeonctl;
+
+ @sed -e 's|has_mailserver_goes_here|$(HAS_MAILSERVER)|g' -i link-tmpl.cgi;
+ @sed -e 's|gpg_homedir_goes_here|$(_GPG_HOMEDIR)|g' -i link-tmpl.cgi;
@if test -n '$(WWWDOMAIN)' && test -n '$(WWWPREFIX)'; then\
$(MAKE) nginxconf;\
printf "Done generating $(WWWDOMAIN).conf for nginx.";\
fi
@printf "\nDone preparing files. You can now type\n\t$$ sudo make install\nin your terminal.\n"
-
+
+gpigeonctl: gpigeonctl.def.pl
+ @sed -e 's|gpgdir_goes_here|$(_GPG_HOMEDIR)|g' gpigeonctl.def.pl > gpigeonctl;
+ @sed -e 's|cookies_dir_goes_here|$(COOKIES_DIR)|g' -i gpigeonctl ;
+ @sed -e 's|db_path_goes_here|$(DB_PATH)|g' -i gpigeonctl;
+ @sed -e 's|web_user_goes_here|$(WWWUSER)|g' -i gpigeonctl;
+ @sed -e 's|web_dir_goes_here|$(WWWDIR)|g' -i gpigeonctl;
+ chmod +x gpigeonctl;
+
install:
- $(MAKE) gpigeon;
+ $(MAKE) gpigeon gpigeonctl;
+ @if test -n "$(WWWDOMAIN)"; then\
+ $(MAKE) nginxconf;\
+ printf "\nInstalling $(WWWDOMAIN).conf into $(NGINXCONFDIR)\n";\
+ install -Dm644 $(WWWDOMAIN).conf -t $(DESTDIR)$(NGINXCONFDIR);\
+ fi
mkdir -p $(DESTDIR)$(COOKIES_DIR);
mkdir -m700 -p $(DESTDIR)$(_GPG_HOMEDIR)
- gpg --homedir "$(DESTDIR)$(_GPG_HOMEDIR)" --import gpg.txt; \
- mkdir -p $(DESTDIR)$(WWWPREFIX)/cgi-bin/l
+ mkdir -p $(DESTDIR)$(WWWDIR)/cgi-bin/l
install -Dm700 gpigeon.cgi $(DESTDIR)$(GPIGEON_PATH)
install -Dm600 link-tmpl.cgi $(DESTDIR)$(LINK_TEMPLATE_PATH)
- install -Dm644 index.html favicon.ico styles.css -t $(DESTDIR)$(WWWPREFIX)/
- install -Dm755 merci/* -t $(DESTDIR)$(PREFIX)/merci/
- @if test -e '$(WWWDOMAIN).conf'; then\
- printf "\nInstalling $(WWWDOMAIN).conf into $(NGINXCONFDIR)\n";\
- install -Dm644 $(WWWDOMAIN).conf -t $(DESTDIR)$(NGINXCONFDIR);\
- fi
+ install -Dm644 index.html favicon.ico styles.css -t $(DESTDIR)$(WWWDIR)/
+ install -Dm755 gpigeonctl -t $(DESTDIR)$(BINPREFIX)
+ @printf "Done. Now execute `gpigeonctl init' to initialize the database.\n"
nginxconf: nginx-example.conf
@sed -e 's|wwwpath_goes_here|$(WWWPREFIX)|g;s|domain_goes_here|$(WWWDOMAIN)|g' nginx-example.conf > $(WWWDOMAIN).conf ;\
uninstall:
- rm -rf $(DESTDIR)$(PREFIX)
- rm -rf $(DESTDIR)$(WWWPREFIX)
+ rm -f $(DESTDIR)$(BINPREFIX)/gpigeonctl
+ rm -rf $(DESTDIR)$(PREFIX)/gpigeon
+ rm -rf $(DESTDIR)$(WWWDIR)
clean:
- rm -f genpass.txt gpg.txt link-tmpl.cgi gpigeon.cgi $(WWWDOMAIN).conf the.db
+ rm -f genpass.txt gpg.txt link-tmpl.cgi gpigeon.cgi $(WWWDOMAIN).conf the.db gpigeonctl
.PHONY: clean install uninstall
diff --git a/README.md b/README.md
index 70da491..6fd6439 100644
--- a/README.md
+++ b/README.md
@@ -6,11 +6,15 @@ not a GPG user) so they can send you encrypted mail messages via a one-time
web link.
Feels of déjàvu ? I was inspired by [https://hawkpost.co](https://hawkpost.co) but wasn't really
interested in the multi-user perspective and managing a database.
+I've recently added minimal support for multiple users, but adding them
+is done via the command line, and you'll have to import public
+GPG keys into your keyring first.
+I plan on adding invites in the future, generated by command line
+then directly from web interface.
Features
========
-- Single user: no database required.
- One-time GPG form: after sending the encrypted message, the generated form
self-destructs.
- Cookie based login. If you block cookies, it will switch back to
@@ -25,17 +29,21 @@ Dependencies
You will need perl and the following modules and my perl version is **v5.34.0**, YMMV:
-- HTML::Entities
- CGI
- CGI::Carp
- CGI::Cookies
- Crypt::Argon2
+- DBI
+- DBD::SQLite
+- Email::Valid
+- File::Path (should be already here by default in recent perl installs)
- GPG
+- HTML::Entities
- Net:SSLeay
- Net::SMTP
- Net::SMTPS
-- Email::Valid
- String::Random
+- Term::ReadKey
Having a webserver with CGI support or a separate CGI engine is needed. I'm using
nginx and fcgiwrap.
@@ -48,10 +56,9 @@ Installation
============
Don't forget to copy `config.def.mk` into `config.mk` and tune
-the variable to your liking. Then, you can run the good old:
+the variables to your liking. Then, you can run the good old:
```
-make
-make install #you'll maybe need sudo though
+make install
```
You should also look in the
@@ -93,6 +100,26 @@ server {
fastcgi_pass unix:/run/fcgiwrap.sock;
include /etc/nginx/fastcgi_params;
}
+
+ add_header Strict-Transport-Security "max-age=63072000; preload";
+ add_header Content-Security-Policy "default-src 'self'";
+ add_header X-Frame-Options DENY;
+ add_header Access-Control-Allow-Origin https://$server_name;
+ add_header Vary Origin; # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Origin#cors_and_caching
}
```
You can also tune the `WWWDOMAIN` and `NGINXCONFDIR` variable in your `config.mk` to have it generated for you when running `make`.
+
+
+Managing the service
+====================
+
+Thanks to the `gpigeonctl` script, you can :
+- Initialize the database with (`init`)
+- Add an user (`adduser`)
+- Delete an user (`deluser`)
+- Clean cookies (`cleancookies`)
+- Clean generated links (`cleanlinks`)
+
+The script is mostly interactive, so no automatic adding of user
+at the moment.
diff --git a/config.def.mk b/config.def.mk
index 4f88b8c..931671f 100644
--- a/config.def.mk
+++ b/config.def.mk
@@ -1,25 +1,29 @@
# Customize below to fit your system
-# paths
+# prefixes
+BINPREFIX = /usr/local/bin
PREFIX = /usr/share
-#COOKIES_DIR = $(PREFIX)/cookies
+WWWPREFIX = /var/www
+
+# system stuff
+WWWUSER = 'www-data'
+
+# paths
+COOKIES_DIR = $(PREFIX)/gpigeon/cookies
_GPG_HOMEDIR = $(PREFIX)/gpigeon/gnupg
+WWWDIR = $(WWWPREFIX)/gpigeon
LINK_TEMPLATE_PATH = $(PREFIX)/gpigeon/link-tmpl.cgi
-WWWPREFIX = /var/www/gpigeon
-GPIGEON_PATH = $(WWWPREFIX)/cgi-bin/gpigeon.cgi
+GPIGEON_PATH = $(WWWDIR)/cgi-bin/gpigeon.cgi
DB_PATH=$(PREFIX)/gpigeon/the.db
-# CGI tuning stuff
+# one time gpg form tuning
MSG_FORM_CHAR_LIMIT = 3000
-# argon2id hash. generated by genpass.pl if empty when running make
-#ARGON2ID_HASH =
-
# gpg and email vars
HAS_MAILSERVER = 0# choose 0 if you'll use an external mail server, 1 if local mail server installed.
-# you don't need to set the 3 last variables if you got a local mailserver.
-#MYGPG_ID_0XLONG =# the 0xlong format of your gpg key.
-#MYMAIL_ADDR =# your mail address
+
+# you don't need to set these variables
+# below if you got a local mailserver.
MAILSENDER =# the mailer address that'll send you the encrypted mails
MAILSENDER_PW =# password for the mailer address
SMTP_DOMAIN =# smtp domain pour the mailer
diff --git a/domains.csv b/domains.csv
deleted file mode 100644
index ef1d541..0000000
--- a/domains.csv
+++ /dev/null
@@ -1,323 +0,0 @@
-ADDRESS,IMAP,imap port,SMTP,smtp port
-420blaze.it,mail.cock.li,993,mail.cock.li,587
-8chan.co,mail.cock.li,993,mail.cock.li,587
-aaathats3as.com,mail.cock.li,993,mail.cock.li,587
-accountant.com,imap.mail.com,993,smtp.mail.com,587
-activist.com,imap.mail.com,993,smtp.mail.com,587
-ad.unsw.edu.au,outlook.office365.com,993,smtp.office365.com,587
-adexec.com,imap.mail.com,993,smtp.mail.com,587
-airmail.cc,mail.cock.li,993,mail.cock.li,587
-allergist.com,imap.mail.com,993,smtp.mail.com,587
-alumni.bits-pilani.ac.in,imap.gmail.com,993,smtp.gmail.com,465
-alumni.com,imap.mail.com,993,smtp.mail.com,587
-alumnidirector.com,imap.mail.com,993,smtp.mail.com,587
-alunos.utfpr.edu.br,imap.gmail.com,993,smtp.gmail.com,587
-anche.no,mail.autistici.org,993,smtp.autistici.org,465
-angelic.com,imap.mail.com,993,smtp.mail.com,587
-aol.com,imap.aol.com,993,smtp.aol.com,465
-appraiser.net,imap.mail.com,993,smtp.mail.com,587
-aquilenet.fr,imap.aquilenet.fr,993,smtp.aquilenet.fr,587
-archaeologist.com,imap.mail.com,993,smtp.mail.com,587
-arcticmail.com,imap.mail.com,993,smtp.mail.com,587
-artlover.com,imap.mail.com,993,smtp.mail.com,587
-asia.com,imap.mail.com,993,smtp.mail.com,587
-auctioneer.net,imap.mail.com,993,smtp.mail.com,587
-autistiche.org,mail.autistici.org,993,smtp.autistici.org,465
-autistici.org,mail.autistici.org,993,smtp.autistici.org,465
-autograf.pl,imap.poczta.onet.pl,993,smtp.poczta.onet.pl,465
-autoproduzioni.net,mail.autistici.org,993,smtp.autistici.org,465
-bartender.net,imap.mail.com,993,smtp.mail.com,587
-bastardi.net,mail.autistici.org,993,smtp.autistici.org,465
-bguth.de,wp300.webpack.hosteurope.de,993,wp300.webpack.hosteurope.de,587
-bigpond.com,imap.telstra.com,143,smtp.telstra.com,587
-bikerider.com,imap.mail.com,993,smtp.mail.com,587
-billycarlyle.uk,mail.muny.us,993,mail.muny.us,465
-birdlover.com,imap.mail.com,993,smtp.mail.com,587
-bjoernguthphotography.de,wp300.webpack.hosteurope.de,993,wp300.webpack.hosteurope.de,587
-bocken.org,imap.gmail.com,993,smtp.gmail.com,465
-brew-meister.com,imap.mail.com,993,smtp.mail.com,587
-bruttocarattere.org,mail.autistici.org,993,smtp.autistici.org,465
-canaglie.net,mail.autistici.org,993,smtp.autistici.org,465
-canaglie.org,mail.autistici.org,993,smtp.autistici.org,465
-carleton.ca,imap-mail.outlook.com,993,smtp-mail.outlook.com,587
-cash4u.com,imap.mail.com,993,smtp.mail.com,587
-ceng.metu.edu.tr,imap.ceng.metu.edu.tr,993,mailhost.ceng.metu.edu.tr,587
-cheerful.com,imap.mail.com,993,smtp.mail.com,587
-chef.net,imap.mail.com,993,smtp.mail.com,587
-chemist.com,imap.mail.com,993,smtp.mail.com,587
-chrissx.ga,chrissx.ga,993,chrissx.ga,25
-clarkson.edu,imap.gmail.com,993,smtp.gmail.com,587
-clasnet.sunyocc.edu,outlook.office365.com,993,smtp.office365.com,587
-clerk.com,imap.mail.com,993,smtp.mail.com,587
-clubmember.org,imap.mail.com,993,smtp.mail.com,587
-cmail.carleton.ca,imap-mail.outlook.com,993,smtp-mail.outlook.com,587
-cn.edu,imap-mail.outlook.com,993,smtp-mail.outlook.com,587
-cocaine.ninja,mail.cock.li,993,mail.cock.li,587
-cock.email,mail.cock.li,993,mail.cock.li,587
-cock.li,mail.cock.li,993,mail.cock.li,587
-cock.lu,mail.cock.li,993,mail.cock.li,587
-collector.org,imap.mail.com,993,smtp.mail.com,587
-columnist.com,imap.mail.com,993,smtp.mail.com,587
-comic.com,imap.mail.com,993,smtp.mail.com,587
-computer4u.com,imap.mail.com,993,smtp.mail.com,587
-consultant.com,imap.mail.com,993,smtp.mail.com,587
-contractor.net,imap.mail.com,993,smtp.mail.com,587
-coolsite.net,imap.mail.com,993,smtp.mail.com,587
-counsellor.com,imap.mail.com,993,smtp.mail.com,587
-cryptolab.net,mail.autistici.org,993,smtp.autistici.org,465
-cumallover.me,mail.cock.li,993,mail.cock.li,587
-cyberservices.com,imap.mail.com,993,smtp.mail.com,587
-datentopf.org,mail.datentopf.org,993,mail.datentopf.org,587
-deliveryman.com,imap.mail.com,993,smtp.mail.com,587
-dicksinhisan.us,mail.cock.li,993,mail.cock.li,587
-dicksinmyan.us,mail.cock.li,993,mail.cock.li,587
-diplomats.com,imap.mail.com,993,smtp.mail.com,587
-disroot.org,disroot.org,993,disroot.org,587
-distruzione.org,mail.autistici.org,993,smtp.autistici.org,465
-dr.com,imap.mail.com,993,smtp.mail.com,587
-duke.edu,outlook.office365.com,993,smtp.office365.com,587
-email.arizona.edu,imap.gmail.com,993,smtp.gmail.com,587
-email.com,imap.mail.com,993,smtp.mail.com,587
-eneco.com,outlook.office365.com,993,smtp.office365.com,587
-engineer.com,imap.mail.com,993,smtp.mail.com,587
-erciyes.edu.tr,posta.erciyes.edu.tr,993,smtp.erciyes.edu.tr,587
-ethancoe.com,mail.privateemail.com,993,mail.privateemail.com,465
-ethz.ch,mail.ethz.ch,993,mail.ethz.ch,587
-etu.upmc.fr,courriel.upmc.fr,993,smtps.upmc.fr,587
-europe.com,imap.mail.com,993,smtp.mail.com,587
-fastmail.com,imap.fastmail.com,993,smtp.fastmail.com,465
-fastmail.fm,imap.fastmail.com,993,smtp.fastmail.com,465
-firemail.cc,mail.cock.li,993,mail.cock.li,587
-forpsi.com,imap.forpsi.com,993,smtp.forpsi.com,465
-forthnet.gr,mail.forthnet.gr,993,smtp-auth.forthnet.gr,465
-fsmpi.rwth-aachen.de,mail.fsmpi.rwth-aachen.de,993,mail.fsmpi.rwth-aachen.de,465
-fsu-jena,exchange.uni-jena.de,993,smtp.uni-jena.de,587
-gcc.edu,imap-mail.outlook.com,993,smtp-mail.outlook.com,587
-getbackinthe.kitchen,mail.cock.li,993,mail.cock.li,587
-gmail.com,imap.gmail.com,993,smtp.gmail.com,587
-gmx.at,imap.gmx.net,993,mail.gmx.net,587
-gmx.com,imap.gmx.net,993,mail.gmx.net,587
-gmx.de,imap.gmx.net,993,mail.gmx.net,587
-gmx.eu,imap.gmx.net,993,mail.gmx.net,587
-gmx.fr,imap.gmx.com,993,mail.gmx.com,587
-gmx.info,imap.gmx.net,993,mail.gmx.net,587
-gmx.net,imap.gmx.net,993,mail.gmx.net,587
-gmx.org,imap.gmx.net,993,mail.gmx.net,587
-go2.pl,poczta.o2.pl,993,poczta.o2.pl,465
-goat.si,mail.cock.li,993,mail.cock.li,587
-googlemail.com,imap.googlemail.com,993,smtp.googlemail.com,587
-grrlz.net,mail.autistici.org,993,smtp.autistici.org,465
-hacari.com,mail.autistici.org,993,smtp.autistici.org,465
-hacari.net,mail.autistici.org,993,smtp.autistici.org,465
-hacari.org,mail.autistici.org,993,smtp.autistici.org,465
-helsinki.fi,outlook.office365.com,993,smtp.helsinki.fi,587
-hhu.de,mail.hhu.de,993,mail.hhu.de,465
-hitler.rocks,mail.cock.li,993,mail.cock.li,587
-horsefucker.org,mail.cock.li,993,mail.cock.li,587
-hostgator,gator4171.hostgator.com,993,gator4171.hostgator.com,587
-hotmail.be,outlook.office365.com,993,smtp.office365.com,587
-hotmail.ca,outlook.office365.com,993,smtp.office365.com,587
-hotmail.cl,outlook.office365.com,993,smtp.office365.com,587
-hotmail.co.id,outlook.office365.com,993,smtp.office365.com,587
-hotmail.co.il,outlook.office365.com,993,smtp.office365.com,587
-hotmail.co.in,outlook.office365.com,993,smtp.office365.com,587
-hotmail.co.jp,outlook.office365.com,993,smtp.office365.com,587
-hotmail.co.kr,outlook.office365.com,993,smtp.office365.com,587
-hotmail.co.th,outlook.office365.com,993,smtp.office365.com,587
-hotmail.co.uk,outlook.office365.com,993,smtp.office365.com,587
-hotmail.co.za,outlook.office365.com,993,smtp.office365.com,587
-hotmail.com,imap-mail.outlook.com,993,smtp-mail.outlook.com,587
-hotmail.com.ar,outlook.office365.com,993,smtp.office365.com,587
-hotmail.com.au,outlook.office365.com,993,smtp.office365.com,587
-hotmail.com.br,outlook.office365.com,993,smtp.office365.com,587
-hotmail.com.hk,outlook.office365.com,993,smtp.office365.com,587
-hotmail.com.tr,outlook.office365.com,993,smtp.office365.com,587
-hotmail.com.tw,outlook.office365.com,993,smtp.office365.com,587
-hotmail.com.vn,outlook.office365.com,993,smtp.office365.com,587
-hotmail.cz,outlook.office365.com,993,smtp.office365.com,587
-hotmail.de,outlook.office365.com,993,smtp.office365.com,587
-hotmail.dk,outlook.office365.com,993,smtp.office365.com,587
-hotmail.es,outlook.office365.com,993,smtp.office365.com,587
-hotmail.fi,outlook.office365.com,993,smtp.office365.com,587
-hotmail.fr,outlook.office365.com,993,smtp.office365.com,587
-hotmail.gr,outlook.office365.com,993,smtp.office365.com,587
-hotmail.hu,outlook.office365.com,993,smtp.office365.com,587
-hotmail.it,outlook.office365.com,993,smtp.office365.com,587
-hotmail.lt,outlook.office365.com,993,smtp.office365.com,587
-hotmail.lv,outlook.office365.com,993,smtp.office365.com,587
-hotmail.my,outlook.office365.com,993,smtp.office365.com,587
-hotmail.nl,outlook.office365.com,993,smtp.office365.com,587
-hotmail.no,outlook.office365.com,993,smtp.office365.com,587
-hotmail.ph,outlook.office365.com,993,smtp.office365.com,587
-hotmail.rs,outlook.office365.com,993,smtp.office365.com,587
-hotmail.se,outlook.office365.com,993,smtp.office365.com,587
-hotmail.sg,outlook.office365.com,993,smtp.office365.com,587
-hotmail.sk,outlook.office365.com,993,smtp.office365.com,587
-hs-mittweida.de,mail.hs-mittweida.de,993,mail.hs-mittweida.de,465
-humbug.pw,imap.migadu.com,993,smtp.migadu.com,587
-hushmail.com,imap.hushmail.com,993,smtp.hushmail.com,465
-icloud.com,imap.mail.me.com,993,smtp.mail.me.com,587
-illinois.edu, imap.gmail.com,993,smtp.gmail.com,465
-in.tum.de,mail.in.tum.de,993,mail.in.tum.de,465
-iname.com,imap.mail.com,993,smtp.mail.com,587
-inf.h-brs.de,imap.inf.h-brs.de,993,smtp.inf.h-brs.de,587
-infomaniak.com,imap.infomaniak.com,993,imap.infomaniak.com,587
-insiberia.net,mail.autistici.org,993,smtp.autistici.org,465
-insicuri.net,mail.autistici.org,993,smtp.autistici.org,465
-interactio.io,imap.gmail.com,993,smtp.gmail.com,587
-interia.eu,poczta.interia.pl,993,poczta.interia.pl,465
-interia.pl,poczta.interia.pl,993,poczta.interia.pl,465
-inventati.org,mail.autistici.org,993,smtp.autistici.org,465
-ionos.de,imap.ionos.de,993,smtp.ionos.de,587
-itu.dk,imap-mail.outlook.com,993,smtp-mail.outlook.com,587
-kean.edu,imap.gmail.com,993,smtp.gmail.com,587
-kipras.org,mail.kipras.org,993,mail.kipras.org,587
-krutt.org,mail.autistici.org,993,smtp.autistici.org,465
-kth.se,webmail.kth.se,993,smtp.kth.se,587
-lavabit.com,lavabit.com,993,lavabit.com,587
-librem.one,imap.librem.one,993,smtp.librem.one,465
-linuxmail.org,imap.mail.com,993,smtp.mail.com,587
-live.com,imap-mail.outlook.com,993,smtp-mail.outlook.com,587
-live.de,imap-mail.outlook.com,993,smtp-mail.outlook.com,587
-live.rhul.ac.uk,outlook.office365.com,993,smtp.office365.com,587
-logorroici.org,mail.autistici.org,993,smtp.autistici.org,465
-loves.dicksinhisan.us,mail.cock.li,993,mail.cock.li,587
-loves.dicksinmyan.us,mail.cock.li,993,mail.cock.li,587
-lukesmith.xyz,mail.lukesmith.xyz,993,mail.lukesmith.xyz,587
-mail.com,imap.mail.com,993,smtp.mail.com,587
-mail.de,imap.mail.de,993,smtp.mail.de,465
-mail.mcgill.ca,outlook.office365.com,993,smtp.office365.com,587
-mail.polimi.it,outlook.office365.com,993,smtp.office365.com,587
-mail.ru,imap.mail.ru,993,smtp.mail.ru,465
-mailbox.org,imap.mailbox.org,993,smtp.mailbox.org,587
-mailbox.tu-dresden.de,msx.tu-dresden.de,993,msx.tu-dresden.de,587
-mailfence.com,imap.mailfence.com,993,smtp.mailfence.com,465
-mailo.com,mail.mailo.com,993,mail.mailo.com,465
-memeware.net,mail.cock.li,993,mail.cock.li,587
-metu.edu.tr,imap.metu.edu.tr,993,smtp.metu.edu.tr,465
-ml1.net,imap.fastmail.com,993,smtp.fastmail.com,465
-mortemale.org,mail.autistici.org,993,smtp.autistici.org,465
-msn.com,imap-mail.outlook.com,993,smtp-mail.outlook.com,587
-muny.us,mail.muny.us,993,mail.muny.us,465
-myself.com,imap.mail.com,993,smtp.mail.com,587
-narod.ru,imap.yandex.com,993,smtp.yandex.com,587
-national.shitposting.agency,mail.cock.li,993,mail.cock.li,587
-ncsu.edu,imap.gmail.com,993,smtp.gmail.com,587
-netcourrier.com,mail.netcourrier.com,993,mail.netcourrier.com,465
-nigge.rs,mail.cock.li,993,mail.cock.li,587
-niser.ac.in,imap.gmail.com,993,smtp.gmail.com,587
-nuke.africa,mail.cock.li,993,mail.cock.li,587
-nyu.edu,imap.gmail.com,993,smtp.gmail.com,587
-o2.pl,poczta.o2.pl,993,poczta.o2.pl,465
-odu.edu,imap.gmail.com,993,smtp.gmail.com,587
-one.com,imap.one.com,993,send.one.com,465
-onenetbeyond.org,mail.autistici.org,993,smtp.autistici.org,465
-onet.com.pl,imap.poczta.onet.pl,993,smtp.poczta.onet.pl,465
-onet.eu,imap.poczta.onet.pl,993,smtp.poczta.onet.pl,465
-onet.pl,imap.poczta.onet.pl,993,smtp.poczta.onet.pl,587
-online.de,imap.1und1.de,993,smtp.1und1.de,465
-op.pl,imap.poczta.onet.pl,993,smtp.poczta.onet.pl,465
-opoczta.pl,imap.poczta.onet.pl,993,smtp.poczta.onet.pl,465
-orange.fr,imap.orange.fr,993,smtp.orange.fr,465
-outlook.at,imap-mail.outlook.com,993,smtp-mail.outlook.com,587
-outlook.com,imap-mail.outlook.com,993,smtp-mail.outlook.com,587
-outlook.de,imap-mail.outlook.com,993,smtp-mail.outlook.com,587
-outlook.es,outlook.office365.com,993,smtp.office365.com,587
-paranoici.org,mail.autistici.org,993,smtp.autistici.org,465
-pm.me,127.0.0.1,1143,127.0.0.1,1025
-poczta.fm,poczta.interia.pl,993,poczta.interia.pl,465
-poczta.onet.pl,imap.poczta.onet.pl,993,smtp.poczta.onet.pl,465
-polimi.it,outlook.office365.com,993,smtp.office365.com,587
-polito.it,mail.polito.it,993,mail.polito.it,465
-polito.it,mail.polito.it,993,mail.polito.it,465
-post.com,imap.mail.com,993,smtp.mail.com,587
-posteo.de,posteo.de,993,posteo.de,587
-posteo.net,posteo.de,993,posteo.de,587
-privacyrequired.com,mail.autistici.org,993,smtp.autistici.org,465
-prokonto.pl,poczta.o2.pl,993,poczta.o2.pl,465
-protonmail.ch,127.0.0.1,1143,127.0.0.1,1025
-protonmail.com,127.0.0.1,1143,127.0.0.1,1025
-rape.lol,mail.cock.li,993,mail.cock.li,587
-redchan.it,mail.cock.li,993,mail.cock.li,587
-resch.pw,mail.resch.pw,993,mail.resch.pw,587
-riseup.net,mail.riseup.net,993,mail.riseup.net,465
-rmcacs.org,imap.gmail.com,993,smtp.gmail.com,587
-runbox.com,mail.runbox.com,993,mail.runbox.com,587
-rwth-aachen.de,mail.rwth-aachen.de,993,mail.rwth-aachen.de,587
-sapo.pt,imap.sapo.pt,993,smtp.sapo.pt,587
-smail.inf.h-brs.de,imap.inf.h-brs.de,993,smtp.inf.h-brs.de,587
-southwales.ac.uk,imap.gmail.com,993,smtp.gmail.com,587
-spoko.pl,imap.poczta.onet.pl,993,smtp.poczta.onet.pl,465
-st.amu.edu.pl,outlook.office365.com,993,smtp.office365.com,587
-stevens.edu,imap.outlook.com,993,smtp.outlook.com,587
-stronzi.org,mail.autistici.org,993,smtp.autistici.org,465
-stud.tu-darmstadt.de,imap.stud.tu-darmstadt.de,993,smtp.tu-darmstadt.de,465
-stud.uis.no,outlook.office365.com,993,smtp.office365.com,587
-stud.uni-bamberg.de,outlook.office365.com,993,smtp.office365.com,587
-student.binadarma.ac.id,imap.gmail.com,993,smtp.gmail.com,587
-student.rmit.edu.au,outlook.office365.com,993,smtp.office365.com,587
-student.tuwien.ac.at,mail.student.tuwien.ac.at,993,mail.student.tuwien.ac.at,587
-student.utwente.nl,imap.gmail.com,993,smtp.gmail.com,587
-studenti.unipi.it,outlook.office365.com,993,smtp.office365.com,587
-students.rmcacs.org,imap.gmail.com,993,smtp.gmail.com,587
-students.southwales.ac.uk,imap.gmail.com,993,smtp.gmail.com,587
-studio.unibo.it,outlook.office365.com,993,smtp.office365.com,587
-studio.unibo.it,outlook.office365.com,993,smtp.office365.com,587
-studserv.uni-leipzig.de,studserv.uni-leipzig.de,993,studserv.uni-leipzig.de,25
-subvertising.org,mail.autistici.org,993,smtp.autistici.org,465
-t-online.de,secureimap.t-online.de,993,securesmtp.t-online.de,465
-techie.com,imap.mail.com,993,smtp.mail.com,587
-tecnico.ulisboa.pt,mail.tecnico.ulisboa.pt,993,mail.tecnico.ulisboa.pt,465
-teknik.io,mail.teknik.io,993,mail.teknik.io,587
-telenet.be,imap.telenet.be,993,smtp.telenet.be,587
-tfwno.gf,mail.cock.li,993,mail.cock.li,587
-tlen.pl,poczta.o2.pl,993,poczta.o2.pl,465
-tlu.edu,imap-mail.outlook.com,993,smtp-mail.outlook.com,587
-tquad.ai,imap.mail.eu-west-1.awsapps.com,993,smtp.mail.eu-west-1.awsapps.com,465
-tu-harburg.de,mail.tu-harburg.de,993,mail.tu-harburg.de,587
-tuhh.de,mail.tu-harburg.de,993,mail.tu-harburg.de,587
-tum.de,xmail.mwn.de,993,postout.lrz.de,587
-txstate.edu,outlook.office365.com,993,smtp.office365.com,587
-ua.pt,outlook.office365.com,993,mail.ua.pt,25
-uach.mx,imap.gmail.com,993,smtp.gmail.com,587
-ucdavis.edu,imap.gmail.com,993,smtp.gmail.com,587
-uni-duesseldorf.de,mail.hhu.de,993,mail.hhu.de,465
-unilodz.eu,outlook.office365.com,993,smtp.office365.com,587
-unitybox.de,mail.unity-mail.de,993,mail.unity-mail.de,587
-univ-ubs.fr,partage.univ-ubs.fr,993,partage.univ-ubs.fr,587
-uoregon.edu,imap.uoregon.edu,993,smtp.uoregon.edu,587
-uqtr.ca,outlook.office365.com,993,smtp.office365.com,587
-usa.com,imap.mail.com,993,smtp.mail.com,587
-utas.edu.au,outlook.office365.com,993,smtp.office365.com,587
-utdallas.edu,outlook.office365.com,993,smtp.office365.com,587
-uw.edu,imap.gmail.com,993,smtp.gmail.com,465
-uwcad.it,imap.gmail.com,993,smtp.gmail.com,465
-uymail.com,imap.mail.com,993,smtp.mail.com,587
-vip.onet.pl,imap.poczta.onet.pl,993,smtp.poczta.onet.pl,465
-vivaldi.net,imap.vivaldi.net,993,smtp.vivaldi.net,587
-vp.pl,imap.poczta.onet.pl,993,smtp.poczta.onet.pl,465
-vt.edu,imap.gmail.com,993,smtp.gmail.com,587
-vxempire.xyz,vxempire.xyz,993,vxempire.xyz,587
-waifu.club,mail.cock.li,993,mail.cock.li,587
-wanadoo.fr,imap.orange.fr,993,smtp.orange.fr,465
-wants.dicksinhisan.us,mail.cock.li,993,mail.cock.li,587
-wants.dicksinmyan.us,mail.cock.li,993,mail.cock.li,587
-web.de,imap.web.de,993,smtp.web.de,587
-wit.edu,outlook.office365.com,993,smtp.office365.com,587
-wp.pl,imap.wp.pl,993,smtp.wp.pl,465
-writeme.com,imap.mail.com,993,smtp.mail.com,587
-ya.ru,imap.yandex.com,993,smtp.yandex.com,587
-yahoo.com,imap.mail.yahoo.com,993,smtp.mail.yahoo.com,587
-yahoo.fr,imap.mail.yahoo.com,993,smtp.mail.yahoo.com,587
-yahoo.gr,imap.mail.yahoo.com,993,smtp.mail.yahoo.com,587
-yandex.by,imap.yandex.com,993,smtp.yandex.com,587
-yandex.com,imap.yandex.com,993,smtp.yandex.com,587
-yandex.kz,imap.yandex.com,993,smtp.yandex.com,587
-yandex.net,imap.yandex.com,993,smtp.yandex.com,587
-yandex.ru,imap.yandex.com,993,smtp.yandex.com,587
-yandex.ua,imap.yandex.com,993,smtp.yandex.com,587
-ymail.com,imap.mail.yahoo.com,993,smtp.mail.yahoo.com,465
-zaclys.net,mail.zaclys.net,993,mail.zaclys.net,465
-zoho.com,imap.zoho.com,993,smtp.zoho.com,465
-zohomail.eu,imap.zoho.eu,993,smtp.zoho.eu,465
-sfr.fr,imap.sfr.fr,993,smtp.sfr.fr,465
diff --git a/gpigeon-template.cgi b/gpigeon-template.cgi
index be249e9..646846d 100755
--- a/gpigeon-template.cgi
+++ b/gpigeon-template.cgi
@@ -1,81 +1,185 @@
#! /usr/bin/perl -T
+# gpigeon.cgi: generate links for someone to send you GPG encrypted messages via a one time form.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+# Copyright (c) 2020-2021, Miquel Lionel <lionel@les-miquelots.net>
use warnings;
use strict;
use Crypt::Argon2 qw(argon2id_verify);
use Email::Valid;
use String::Random;
+use DBI;
use CGI qw(param);
use CGI::Cookie;
use CGI::Carp qw(fatalsToBrowser);
+use File::Path qw(mkpath rmtree);
+
+my $rIP = $ENV{REMOTE_ADDR};
+my $uagent = $ENV{HTTP_USER_AGENT};
+
+sub DbGetLine {
+ my ($dbh, $query) = @_;
+ my $prep = $dbh->prepare( $query );
+ my $exec = $prep->execute() or die $DBI::errstr;
+
+ if ($exec < 0){
+ print $DBI::errstr;
+ }
+
+ while (my @rows = $prep->fetchrow_array()) {
+ my $row = $rows[0];
+ return $row;
+ }
+}
+
+sub LoginOk {
+ my ($dbh, $username, $pass, $userid, $magic_cookie, $uid_cookie, $cookiesdir) = @_;
+ my $loginsuccess = PasswdLogin($dbh, $username, $pass);
+ if (not defined $loginsuccess){
+ $loginsuccess = CookieLogin($userid, $magic_cookie, $uid_cookie, $cookiesdir);
+ }
+ return $loginsuccess;
+}
+
+sub CookieLogin {
+ my ($userid, $magic_cookie, $uid_cookie, $cookiesdir) = @_;
+ if (not $userid =~ /^([0-9]+)$/){
+ return;
+ }
-sub ValidCookie {
- my $client_login_cookie = shift;
- my $dir = shift;
- if (not defined $client_login_cookie){
+ if (not defined $magic_cookie or not defined $uid_cookie){
return;
}
- my $cookie_line = undef;
- my $filename = $client_login_cookie->value;
+
+ my ($rip_line, $ua_line, $id_line, $uid_line) = undef;
+ my $filename = $magic_cookie->value;
if ($filename =~ /^([\w]+)$/){
- $filename = $1;
+ $filename = $1;
}
else{
- return;
+ return;
}
- my $login_cookiefile = "$dir/$filename.txt";
- if (-e $login_cookiefile){
+
+ my $login_cookiefile = "$cookiesdir/$userid/$filename.txt";
+ if (-e $login_cookiefile){
open my $in, '<', $login_cookiefile or die "can't read file: $!";
- for(1){
- my $cookie_line = readline $in;
- }
+ $rip_line = readline $in;
+ $ua_line = readline $in;
+ $id_line = readline $in;
+ $uid_line = readline $in;
close $in;
- if (not defined $cookie_line){
- return;
- }
+ chomp ($rip_line, $ua_line, $id_line); # chomp the \n
}
else{
return;
}
- if ($client_login_cookie == $cookie_line){
- return 1;
+ my %id_line_cookie = CGI::Cookie->parse($id_line);
+ my %uid_line_cookie = CGI::Cookie->parse($uid_line);
+ my $id_value = $id_line_cookie{'id'}->value;
+ my $uid_value = $uid_line_cookie{'uid'}->value;
+
+ my $ip_match = $rip_line cmp $rIP;
+ my $ua_match = $ua_line cmp $uagent;
+ my $uid_match = $uid_cookie->value cmp $uid_value;
+ my $id_match = $magic_cookie->value cmp $id_value;
+
+ if ($ip_match == 0 and $ua_match == 0 and $uid_match == 0 and $id_match == 0){
+ return $userid;
}
return;
+}
+sub PasswdLogin {
+
+ my ($dbh, $username, $pass) = @_;
+ if (not defined $username or not defined $pass){
+ return;
+ }
+ if (not Email::Valid->address($username)){
+ if ($username =~ /^([-\w.]+)$/) {
+ $username = $1;
+ } else {
+ return;
+ }
+ }
+ my ($hash, $userid) = undef;
+ my $selecthash = qq{SELECT pass from pigeons where mail='$username' or name='$username';};
+ $hash = DbGetLine($dbh, $selecthash);
+ if (defined $hash and length($hash) > 1){
+ if(argon2id_verify($hash,$pass)){
+ my $selectuserid = qq{SELECT userid from pigeons where pass='$hash';};
+ $userid = DbGetLine($dbh, $selectuserid);
+ if ($userid =~ /^([0-9]+)$/){
+ $userid = $1;
+ }
+ else {
+ return;
+ }
+ return $userid; # as an userid is always > 0, we can use it as return value
+ } else {
+ return;
+ }
+ } else {
+ $dbh->disconnect;
+ return;
+ }
+ $dbh->disconnect;
+ 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){
+ my ($userid, $magic_cookie, $cookiesdir) = @_;
+ if (not defined $magic_cookie){
+ my $str_rand_obj = String::Random->new;
+ my $val = $str_rand_obj->randregex('\w{64}');
+ if (not -d "$cookiesdir/$userid"){
+ mkpath("$cookiesdir/$userid");
+ }
+ my $cookiefile = "$cookiesdir/$userid/$val.txt";
+ my $new_magic_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 $!";
+ my $new_userid_cookie = CGI::Cookie->new(
+ -name => 'uid',
+ -value => $userid,
+ -expires => '+1y',
+ '-max-age' => '+1y',
+ -domain => ".$ENV{'SERVER_NAME'}",
+ -path => '/',
+ -secure => 1,
+ -httponly => 1,
+ -samesite => 'Strict',
+ ) or die "Can't create cookie $!";
open my $out, '>', $cookiefile or die "Can't write to $cookiefile: $!";
- print $out "$new_login_cookie";
+ print $out "$rIP\n$ua\n$new_magic_cookie\n$new_userid_cookie";
close $out;
- print "Set-Cookie: $new_login_cookie\n";
+ print "Set-Cookie: $new_magic_cookie\n";
+ print "Set-Cookie: $new_userid_cookie\n";
}
}
-sub EscapeArobase {
- my $escapedmailaddress = shift;
- $escapedmailaddress =~ s/@/\\@/;
- return $escapedmailaddress;
-}
-
sub UntaintCGIFilename {
my $filename = shift;
if ($filename =~ /^([-\@\w.\/]+)$/) {
@@ -98,18 +202,13 @@ sub NotifIfDefined{
}
}
-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 $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 $db_path = q{db_path_goes_here};
+my $argon2id_hash = q{argon2id_hash_goes_here};
+my $cookiesdir = q{cookies_dir_goes_here};
my $link_template_path = q{link_template_path_goes_here};
my %text_strings = (
@@ -120,131 +219,171 @@ 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_asker_field_label => q{Asker's mail :},
+ link_asker_field_label => "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 : ',
+ link_generated_ok => "Here's the link",
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',
+ notif_login_failure => 'Cannot login. Check if your username and password match.',
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 $pw = $cgi_query_get->param('password');
-my $logout = $cgi_query_get->param('logout');
+my $username = $cgi_query_get->param('username');
+my $pass = $cgi_query_get->param('password');
+my $disconnect = $cgi_query_get->param('disconnect');
+my ($linkgen_notif, $mailisok_notif, $deletion_notif, $checkedornot,
+ $session, $hidden_loginfield, $magic_cookie,
+ $uid_cookie, $idval, $refresh_form, $userid) = undef;
+my @created_links = ();
my %cur_cookies = CGI::Cookie->fetch;
-$id_cookie = $cur_cookies{'id'};
+$uid_cookie = $cur_cookies{'uid'};
+$magic_cookie = $cur_cookies{'id'};
+my $dbh = DBI->connect("DBI:SQLite:dbname=$db_path", undef, undef, { RaiseError => 1})
+ or die $DBI::errstr;
+
+if (not defined $magic_cookie){ # cookie is not set
+ $hidden_loginfield = qq{<input type="hidden" name="username" value="$username"><input type="hidden" name="password" value="$pass">};
-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}">
+ $hidden_loginfield
+ <input id="refreshbtn" type="submit" value="$text_strings{refresh_btn_text}">
</form>};
-}
-else{
+}else{
+ $hidden_loginfield = qq{<!-- undef -->};
$refresh_form = qq{<form method="GET">
- <input type="submit" value="$text_strings{refresh_btn_text}">
+ <input id="refreshbtn" type="submit" value="$text_strings{refresh_btn_text}">
</form>};
- $idval = $id_cookie->value;
+ $idval = $magic_cookie->value;
+ if ($idval =~ /^([\w]+)$/){
+ $idval = $1;
+ }
- if ($idval =~ /^([\w]+)$/){
- $idval = $1;
- }
+ $userid = $uid_cookie->value;
+ if ($userid =~ /^([0-9]+)$/){
+ $userid = $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";
- }
}
+if ($disconnect and defined $magic_cookie){ # if we disconnect and cookie is active
+ my $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 $delete_uid_cookie = CGI::Cookie->new(
+ -name => 'uid',
+ -value => $userid,
+ -expires => '-1d',
+ '-max-age' => '-1d',
+ -domain => ".$hostname",
+ -path => '/',
+ -secure => 1,
+ -httponly => 1,
+ -samesite => 'Strict',
+ );
+ my $f = "$cookiesdir/$userid/$idval.txt";
+ if (-e "$f"){
+ unlink "$f" or die "cant delete cookie at $f :$!\n"; # delet it
+
+ }
+ print "Set-Cookie: $delete_uid_cookie\n";
+ print "Set-Cookie: $delete_id_cookie\n";
+}
+
+
+
+my $loginok = LoginOk($dbh, $username, $pass, $userid, $magic_cookie, $uid_cookie, $cookiesdir);
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($loginok){
+
+ $userid = $loginok;
+ LoginCookieGen($userid, $magic_cookie, $cookiesdir);
+ my $user_mailaddr = DbGetLine($dbh, qq{SELECT mail from pigeons where userid='$userid';});
+ my $gpgid = DbGetLine($dbh, qq{SELECT gpgfp from pigeons where userid='$userid';});
+ my $nick = DbGetLine($dbh, qq{SELECT name from pigeons where userid='$userid';});
+ if (not -d "./l/$userid"){
+ mkpath("./l/$userid");
+ }
+
if (defined $cgi_query_get->param('supprlien')){
my $pending_deletion = $cgi_query_get->param('supprlien');
- my $linkfile_fn = "./l/$pending_deletion";
+ my $linkfile_fn = "./l/$userid/$pending_deletion";
if (unlink UntaintCGIFilename($linkfile_fn)){
- $deletion_notif = qq{<span style="color:green">$text_strings{link_del_ok}</span>};
+ $deletion_notif=qq{<span id="success">$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 id="failure">$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 '..'){
- unlink UntaintCGIFilename("./l/$_") or die "$!";
- $deletion_notif = qq{<span style="color:green">$text_strings{link_del_ok}</span>};
- }
- }
- closedir $link_dir_handle;
+ rmtree("./l/$userid", {keep_root=>1, safe=>1});
+ $deletion_notif=qq{<span id="success">$text_strings{link_del_ok}</span>};
}
if (defined $cgi_query_get->param('mail')){
- $link_asker = scalar $cgi_query_get->param('mail');
+ my $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 = EscapeArobase($link_asker);
+ $mailisok_notif = qq{<span id="success">$text_strings{addr} $link_asker $text_strings{addr_ok}</span>};
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";
+ my $random_fn = $str_rand_obj->randregex('\w{64}');
+ my $GENERATED_FORM_FILENAME = "$random_fn.cgi";
+ my $HREF_LINK = "https://$hostname/cgi-bin/l/$userid/$GENERATED_FORM_FILENAME";
+ my $LINK_PATH = "./l/$userid/$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 $out, '>', $LINK_PATH or die "Can't write to link file: $!";
while( <$in> ) {
s/{link_user}/{$link_asker}/g;
+ s/{gpgid_goes_here}/{$gpgid}/g;
+ s/{link_filename}/{$GENERATED_FORM_FILENAME}/g;
+ s/{user_mailaddr_goes_here}/{$user_mailaddr}/g;
+ s/{msg_too_long}/$text_strings{msg_too_long}/g;
+ s/{msg_empty}/$text_strings{msg_empty}/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_ok_for} $link_asker: </span><br><a href="$href">$href</a>};
+
+ $linkgen_notif = qq{<span id="success">$text_strings{link_generated_ok}: <br><a target="_blank" rel="noopener noreferrer nofollow" href="$HREF_LINK">$HREF_LINK</a></span>};
}
else{
- $mailisok_notif = qq{<span style="color:red">$text_strings{addr} $link_asker $text_strings{addr_nok}.</span>};
+ $mailisok_notif = qq{<span id="failure">$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/$userid" or die "Can't open ./l: $!";
while (readdir $link_dir_handle) {
if ($_ ne '.' and $_ ne '..'){
my $linkfile_fn = $_;
- if (open my $linkfile_handle , '<', "./l/$linkfile_fn"){
+ my $link_asker = undef;
+ if (open my $linkfile_handle , '<', "./l/$userid/$linkfile_fn"){
for (1..2){
$link_asker = readline $linkfile_handle;
$link_asker =~ s/q\{(.*?)\}//i;
@@ -252,20 +391,23 @@ if (ValidCookie($id_cookie, $cookies_dir) or argon2id_verify($argon2id_hash,$pw)
}
close $linkfile_handle;
- 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>};
+ if (not defined $link_asker){
+ $link_asker = $text_strings{unknown};
}
+ #create links table html
+ push @created_links,
+ qq{<tr>
+ <td><a href="/cgi-bin/l/$userid/$linkfile_fn" target="_blank" rel="noopener noreferrer nofollow">ici</a></td>
+ <td><a href="mailto:$link_asker?subject=$text_strings{mailto_subject}&body=$text_strings{mailto_body} http://$hostname/cgi-bin/l/$userid/$linkfile_fn">$link_asker</a></td>
+ <td>
+ <form method="POST">
+ $hidden_loginfield
+ <input type="hidden" name="supprlien" value="$linkfile_fn">
+ <input id="deletelinkbtn" type="submit" value="$text_strings{delete_link_btn_text}">
+ </form>
+ </td>
+ </tr>};
+
}
else {
close $linkfile_handle;
@@ -274,7 +416,6 @@ if (ValidCookie($id_cookie, $cookies_dir) or argon2id_verify($argon2id_hash,$pw)
}
}
closedir $link_dir_handle;
-
print 'Content-type: text/html',"\n\n",
qq{<!DOCTYPE html>
<html>
@@ -286,35 +427,35 @@ if (ValidCookie($id_cookie, $cookies_dir) or argon2id_verify($argon2id_hash,$pw)
<title>$text_strings{web_title}</title>
</head>
<body>
- <p>$text_strings{web_greet_msg}</p>
+ <p>$text_strings{web_greet_msg} <b>$nick</b></p>
<form method="GET">
- <input type="hidden" name="logout" value="1">
- <input type="submit" value="$text_strings{logout_btn_text}">
+ <input type="hidden" name="disconnect" value="1">
+ <input id="logoutbtn" type="submit" value="$text_strings{disconnect_btn_text}">
</form>
- $refresh_form
+ $refresh_form
<hr>
<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{create_link_btn}">
+ $hidden_loginfield
+ Mail de la personne:<br>
+ <input id="mailfied" tabindex="1" type="text" name="mail">
+ <input id="genlinkbtn" 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
+ $hidden_loginfield
<input type="hidden" name="supprtout">
- <input type="submit" value="$text_strings{delete_links_btn_text}">
+ <input id="deleteallbtn" type="submit" value="$text_strings{delete_links_btn_text}">
</form>},
NotifIfDefined($deletion_notif),
qq{<table>
<tr>
- <th>$text_strings{theader_link}</th>
- <th>$text_strings{theader_for}</th>
- <th>$text_strings{theader_deletion}</th>
+ <th>$text_strings{theader_link} &#128279;</th>
+ <th>$text_strings{theader_for} &#128231;</th>
+ <th>$text_strings{theader_deletion} &#128465;</th>
</tr>
@created_links
</table>
@@ -322,5 +463,6 @@ if (ValidCookie($id_cookie, $cookies_dir) or argon2id_verify($argon2id_hash,$pw)
</html>};
}
else{
+ $dbh->disconnect;
print "Location: /\n\n";
}
diff --git a/gpigeonctl b/gpigeonctl
deleted file mode 100755
index 47542bb..0000000
--- a/gpigeonctl
+++ /dev/null
@@ -1,101 +0,0 @@
-#! /usr/bin/perl -T
-
-use warnings;
-use strict;
-use Email::Valid;
-use Term::ReadKey;
-use Crypt::Argon2 qw(argon2id_pass);
-use SQLite::DB;
-delete @ENV{qw(IFS PATH CDPATH BASH_ENV)};
-$ENV{'PATH'} = '/usr/bin';
-my $db_path = '/usr/share/gpigeon/the.db';
-my $escaddr = undef;
-
-sub EscapeArobase {
- my $escapedmailaddress = shift;
- $escapedmailaddress =~ s/@/\\@/;
- return $escapedmailaddress;
-}
-
-if (defined $ARGV[0] and $ARGV[0] eq 'init'){
- if ( -e $db_path){
- print "The database already exist !\n";
- print "Overwrite ? [o/n] ";
- my $ochoice = <STDIN>;
- chomp $ochoice;
- if ($ochoice eq "o"){
- unlink $db_path;
- }
- else {
- print "We won't overwrite.\n";
- exit 1;
- }
- }
- print "Your mail address: ";
- my $addr = <STDIN>;
-
- if (not Email::Valid->address($addr)){
- print "\nNot a valid email address.";
- exit 1;
- }
-
- print "\nYour nickname (optional): ";
- my $nick = <STDIN>;
- chomp $nick;
- if (length($nick) eq 0){
- $nick = $addr;
- }
- elsif (defined $nick and not $nick =~ /^([\w]+)$/){
- print "\nYour nickname must have only alphanumeric characters.\n";
- exit 1;
- }
-
- ReadMode 2;
- print "\nPassword: ";
- my $pass = <STDIN>;
- if (not length($pass) > 10){
- print "\nFor your safety, you should have a password at least 10 characters long.\n";
- ReadMode 1;
- exit 1;
- }
- ReadMode 1;
- chomp $pass;
- my $salt = `openssl rand 16`;
- my $hash = argon2id_pass($pass, $salt, 3, '32M', 1, 32);
-
- use GPG;
- #my $GHOMEDIR = 'testgpg/';
- #my $escaddr = EscapeArobase($addr);
- if ($addr =~ /^([-\@\w.]+)$/) {
- $addr = $1; # $data now untainted
- $escaddr = EscapeArobase($addr);
- } else {
- die "uh oh\n"; # log this somewhere
- }
- my $gpgid = `gpg --with-colons -k $escaddr | grep "pub:u" | cut -d':' -f5`;
- #my $gpgid = <STDIN>;
- chomp $gpgid;
- if (not $gpgid =~ /^([\w]+)$/ and not length($gpgid) eq 16){
- print "\nYour GPG 0xlong key id is not a correct one. It seems that no public key was assiocated with the provided e-mail address.\n";
- exit 1;
- }
- else{
- $gpgid = $1;
- }
- `gpg -a --export 0x$gpgid > key.asc && gpg --homedir /tmp/testgpg/ --no-default-keyring --keyring=test.kbx --import key.asc 2>&1 /dev/null`;
- my $gpg = new GPG(gnupg_path => "/usr/bin", homedir => "/tmp/testgpg");
- my $enc_msg = $gpg->encrypt("test", $gpgid)
- or die "\nOops, it seems gpg won't encrypt the test message. Here's why :\n",$gpg->error();
-
- my $db = SQLite::DB->new($db_path);
- $db->connect;
- $db->transaction_mode;
- $db->exec("create table pigeons (userid integer primary key, mail text, name text, pass text, gpgfp text, isadmin integer);");
- $db->exec("create table cookies (forid integer, value text);");
- $db->exec("create index idx_pigeonsid on pigeons(userid);");
- $db->exec("create index idx_cookiesforid on cookies(forid);");
- $db->exec("INSERT INTO pigeons VALUES( ?, '$addr', '$nick', '$hash', '0x$gpgid', 1)");
- $db->commit or die;
- $db->disconnect;
- print "\nThe database has been initialized.\n";
-}
diff --git a/gpigeonctl.def.pl b/gpigeonctl.def.pl
new file mode 100755
index 0000000..4585967
--- /dev/null
+++ b/gpigeonctl.def.pl
@@ -0,0 +1,283 @@
+#! /usr/bin/perl -T
+# gpigeonctl: user, cookies and GPG key management for gpigeon.cgi
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+# Copyright (c) 2020-2021, Miquel Lionel <lionel@les-miquelots.net>
+
+use warnings;
+use strict;
+use Email::Valid;
+use Term::ReadKey;
+use Crypt::Argon2 qw(argon2id_pass);
+use DBI;
+delete @ENV{qw(IFS PATH CDPATH BASH_ENV)};
+$ENV{'PATH'} = '/usr/bin';
+my $dbh_path = q{db_path_goes_here};
+my $cookiesdir = q{cookies_dir_goes_here};
+my $GNUPGHOME = q{gpgdir_goes_here};
+my $web_user = q{web_user_goes_here};
+my $web_dir = q{web_dir_goes_here};
+my ($escaddr, $ynchoice) = undef;
+my $opt = $ARGV[0];
+my $version = 0.1;
+
+
+sub DbGetLine {
+ my ($dbh, $query) = @_;
+ my $prep = $dbh->prepare( $query );
+ my $exec = $prep->execute() or die $DBI::errstr;
+
+ if ($exec < 0){
+ print $DBI::errstr;
+ }
+
+ while (my @rows = $prep->fetchrow_array()) {
+ my $row = $rows[0];
+ return $row;
+ }
+}
+
+
+sub RecursiveChown {
+ my ($junk, $junk2, $uid, $gid) = getpwnam($web_user);
+ if ($_ =~ qr|^([-+@\w./]+)$|){ # pattern taken from File::Find
+ chown $uid, $gid, "$1";
+ }
+}
+
+sub DeleteCookies {
+ if ($_ =~ /^([\w]+)\.txt$/){
+ unlink "$1.txt";
+ }
+}
+
+sub EscapeArobase {
+ my $esc = shift;
+ if ($esc =~ /^([-\@\w.]+)$/) {
+ $esc = $1; # $data now untainted
+ $esc =~ s/@/\\@/;
+ return $esc;
+ } else {
+ die "\n"; # log this somewhere
+ }
+}
+
+sub PrintHelp{
+ print 'Copyright (c) 2020-2021, Miquel Lionel <lionel@les-miquelots.net>',"\n\n";
+ print 'usage: gpigeonctl [init] [adduser] [deluser] [cleancookies] [cleanlinks] [version]', "\n";
+ exit 0;
+}
+
+sub SetMail {
+ print "Mail address: ";
+ my $addr = <STDIN>;
+ if (not Email::Valid->address($addr)){
+ die "\nNot a valid email address.";
+ }
+ chomp $addr;
+ return $addr;
+}
+
+sub SetNick {
+ my $addr = shift;
+ print "\nNickname (optional): ";
+ my $nick = <STDIN>;
+ chomp $nick;
+ if (length($nick) eq 0){
+ $nick = $addr;
+ return $nick;
+ }
+ elsif (defined $nick and not $nick =~ /^([\w]+)$/){
+ die "\nYour nickname must have only alphanumeric characters.\n";
+ }
+ return $nick;
+}
+
+sub SetPasswd {
+ ReadMode 2;
+ print "\nPassword: ";
+ my $pass = <STDIN>;
+ if (not length($pass) > 10){
+ ReadMode 1;
+ die "\nFor your safety, you should have a password at least 10 characters long.\n";
+ }
+ ReadMode 1;
+ chomp $pass;
+ my $salt = `openssl rand 16`;
+ my $hash = argon2id_pass($pass, $salt, 3, '32M', 1, 32);
+}
+
+sub TransferGPGPubKey {
+ my ($addr, $GNUPGHOME) = @_;
+ my $escaddr = EscapeArobase($addr);
+ my $gpgid = '0x'.`gpg --with-colons -k $escaddr | grep "pub:u" | cut -d':' -f5`;
+ chomp $gpgid;
+ if (not $gpgid =~ /^([\w]+)$/ and not length($gpgid) eq 18){
+ die "\nYour GPG 0xlong key id is not a correct one. It seems that no public key was tied to the provided e-mail address.\n";
+ }
+ else{
+ $gpgid = $1;
+ print "\nGPG ID: $gpgid\n";
+ return $gpgid;
+ }
+}
+
+# i should use a module for this lol
+if (defined $opt){
+
+ if($opt eq 'init'){
+ if ( -e $dbh_path){
+ print "The database already exist at $dbh_path.\n";
+ print "Overwrite ? [y/n] ";
+ $ynchoice = <STDIN>;
+ chomp $ynchoice;
+ if ($ynchoice eq 'o' or $ynchoice eq 'y'){
+ unlink $dbh_path;
+ print "Done.\n";
+ }
+ }
+
+ if (-d $GNUPGHOME){
+ use File::Path qw/rmtree/;
+ print "GPG home directory already exists at $GNUPGHOME. Delete it ? [y/n] ";
+ $ynchoice = <STDIN>;
+ chomp $ynchoice;
+ if ($ynchoice eq 'o' or $ynchoice eq 'y'){
+ rmtree($GNUPGHOME);
+ print "Done.\n";
+ }
+
+ }
+
+
+ my $addr = SetMail();
+ my $nick = SetNick($addr);
+ my $hash = SetPasswd();
+ my $gpgid = TransferGPGPubKey($addr,$GNUPGHOME);
+ my ($junk, $junk2, $uid, $gid) = getpwnam($web_user);
+ use File::Path qw/make_path/;
+ make_path($GNUPGHOME);
+ chmod(0700,$GNUPGHOME);
+ open my $out, '>', "$GNUPGHOME/gpg.conf" or die $!;
+ print $out "use-agent\n";
+ print $out "charset utf-8\n";
+ print $out "no-escape-from-lines\n";
+ print $out "trust-model always\n";
+ print $out "personal-digest-preferences SHA512 SHA384 SHA256 SHA224\n";
+ print $out "default-preference-list SHA512 SHA384 SHA256 SHA224 AES256 AES192 AES CAST5 BZIP2 ZLIB ZIP Uncompressed";
+ close $out;
+ use GPG;
+ `gpg -a --export $gpgid > key.asc`;
+ `gpg --homedir $GNUPGHOME --import key.asc`;
+ my $gpg = new GPG(gnupg_path => "/usr/bin", homedir => "$GNUPGHOME");
+ my $enc_msg = $gpg->encrypt("test", $gpgid)
+ or die "\nOops, it seems gpg won't encrypt the test message. Here's why :\n",$gpg->error();
+
+ my $dbh = DBI->connect("DBI:SQLite:dbname=$dbh_path", undef, undef,
+ {
+ RaiseError => 1,
+ AutoCommit => 1,
+ }) or die $DBI::errstr;
+ $dbh->do('create table pigeons (userid integer primary key, mail text NOT NULL UNIQUE, name text UNIQUE, pass text NOT NULL, gpgfp text NOT NULL UNIQUE, isadmin integer NOT NULL)') or die $DBI::errstr;
+ $dbh->do('create index idx_pigeonsid on pigeons(userid)') or die $DBI::errstr;
+ $dbh->do(qq{INSERT INTO pigeons VALUES( ?, '$addr', '$nick', '$hash', '$gpgid', 1)}) or die $DBI::errstr;
+ $dbh->disconnect;
+ unlink 'key.asc';
+ find(\&recursivechown, $cookiesdir);
+ find(\&recursivechown, $GNUPGHOME);
+ chown $uid, $gid, $dbh_path;
+ print "\nThe database has been initialized.\n";
+ exit 0;
+ }
+
+ if ($opt eq 'adduser'){
+ if (not -e $dbh_path){
+ print "It seems that the database doesn't exist. Type `gpigeonctl init' in a terminal to create it.\n";
+ exit 1;
+ }
+
+ my $addr = SetMail();
+ my $nick = SetNick($addr);
+ my $hash = SetPasswd();
+ my $gpgid = TransferGPGPubKey($addr,$GNUPGHOME);
+
+ my $dbh = DBI->connect("DBI:SQLite:dbname=$dbh_path", undef, undef,
+ { RaiseError => 1,
+ AutoCommit => 1,
+ })
+ or die $DBI::errstr;
+ $dbh->do(qq{INSERT INTO pigeons VALUES( ?, '$addr', '$nick', '$hash', '$gpgid', 1)}) or die $DBI::errstr;
+ $dbh->disconnect;
+ print "\nUser $addr added succesfully\n";
+ exit 0;
+ }
+
+ if ($opt eq 'deluser'){
+ use File::Path qw/rmtree/;
+ my $addr = SetMail();
+ my $esc = EscapeArobase($addr);
+ my $dbh = DBI->connect("DBI:SQLite:dbname=$dbh_path", undef, undef,
+ { RaiseError => 1,
+ AutoCommit => 1,
+ })
+ or die $DBI::errstr;
+ my $uid = DbGetLine($dbh, "SELECT userid FROM pigeons WHERE mail='$esc'") or die "$!";
+ $dbh->do(qq{DELETE FROM pigeons where mail='$addr'}) or die $DBI::errstr;
+ $dbh->disconnect;
+ if (defined $uid and $uid > 0){
+ rmtree("$cookiesdir/$uid", "$web_dir/l/$uid",
+ { verbose => 1,
+ safe => 1
+ });
+ # GPG module doesn't support the delete_key yet so we yolo
+ `GNUPGHOME="$GNUPGHOME" gpg --yes --batch --delete-key $esc`;
+ }
+ print "\nUser $addr deleted succesfully\n";
+ exit 0;
+ }
+
+ if ($opt eq 'cleancookies'){
+ print "This will clean the entire cookie directory at $cookiesdir.\n";
+ print "Proceed ? [y/n]";
+ $ynchoice = <STDIN>;
+ chomp $ynchoice;
+ if ($ynchoice eq 'o' or $ynchoice eq 'y'){
+ rmtree("$cookiesdir",{safe=>1,keep_root=>1});
+ print "All cookies have been cleaned. Tell your users to clear their caches and reconnect.\n";
+ }
+ exit 0;
+ }
+
+ if ($opt eq 'cleanlinks'){
+ $ynchoice = <STDIN>;
+ chomp $ynchoice;
+ if ($ynchoice eq 'o' or $ynchoice eq 'y'){
+ rmtree("$web_dir/cgi-bin/l",{safe=>1,keep_root=>1});
+ print "All generated links have been deleted.\n";
+ }
+ exit 0;
+
+ }
+
+ if ($opt eq 'version'){
+ print "$version\n";
+ exit 0;
+ }
+
+ PrintHelp();
+}
+else {
+ PrintHelp();
+}
diff --git a/index.html b/index.html
index d6b2583..eccc4d2 100644
--- a/index.html
+++ b/index.html
@@ -1,38 +1,32 @@
<!DOCTYPE html>
-<html>
+<html lang="en">
<head>
<title>GPIGEON - Login</title>
+ <meta charset="utf-8">
<link rel="icon" type="image/x-icon" href="/favicon.ico">
<link rel="stylesheet" type="text/css" href="styles.css">
- <meta charset="utf-8">
- <style>
- td{border:none;}
- </style>
</head>
<body>
- <h1 style="text-align:center">GPIGEON - Login</h1>
+ <h1>GPIGEON - Login</h1>
<form action="/cgi-bin/gpigeon.cgi" method="POST">
- <table style="border:none;margin-right:auto;margin-left:auto">
- <tbody>
- <tr>
- <td>Username :</td>
- <td><input type="text" name="username"></td>
-
- </tr>
- <tr>
- <td>Password :</td>
- <td><input type="password" name="password"></td>
-
- </tr>
- <tr>
- <td></td>
- <td><input type="submit" value="S'authentifier"></td>
- </tr>
- </tbody>
+ <table id="loginbox">
+ <tbody>
+ <tr>
+ <td>Username :</td>
+ <td><input type="text" name="username"></td>
+ </tr>
+ <tr>
+ <td>Password :</td>
+ <td><input type="password" name="password"></td>
+ </tr>
+ <tr id="authbtn">
+ <td></td>
+ <td><input type="submit" value="S'authentifier"></td>
+ </tr>
+ </tbody>
</table>
</form>
- <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>
-
+ <p><a href="http://git.les-miquelots.net/gpigeon" title="gpigeon download link">Source code here.</a> It is similar to <a href="https://hawkpost.co/">hawkpost.co</a>.</p>
</body>
</html>
diff --git a/link-tmpl-template.cgi b/link-tmpl-template.cgi
index 2984e9f..5ac8dd4 100644
--- a/link-tmpl-template.cgi
+++ b/link-tmpl-template.cgi
@@ -1,5 +1,6 @@
#! /usr/bin/perl -wT
my $linkuser = q{link_user};
+my $linkfilename = q{link_filename};
use warnings;
use strict;
use GPG;
@@ -16,7 +17,7 @@ sub EscapeArobase {
my $HAS_MAILSERVER = q{has_mailserver_goes_here};
my $msg_form_char_limit = q{msg_char_limit_goes_here};
-my $mymailaddr = q{your_addr_goes_here};
+my $mymailaddr = q{user_mailaddr_goes_here};
my $mymail_gpgid = q{gpgid_goes_here}; #0xlong keyid form
my $mailsender = q{sender_addr_goes_here};
my $mailsender_smtp = q{smtp_domain_goes_here};
@@ -29,24 +30,23 @@ my $length_msg_form = length $msg_form;
my ($enc_msg, $error_processing_msg) = undef;
if (defined $length_msg_form and $length_msg_form > $msg_form_char_limit){
- $error_processing_msg = qq{<span style="color:red"><b>Cannot send message : message length must be under $msg_form_char_limit characters.</b></span>};
+ $error_processing_msg = qq{<span id="failure"><b>Cannot send message : message length must be under $msg_form_char_limit characters.</b></span>};
}
elsif (defined $length_msg_form and $length_msg_form eq 0 ){
- $error_processing_msg = qq{<span style="color:red"><b>Cannot send message : message is empty. You can type up to $msg_form_char_limit characters.</b></span>};
+ $error_processing_msg = qq{<span id="failure"><b>Cannot send message : message is empty. You can type up to $msg_form_char_limit characters.</b></span>};
}
else {
if (defined $length_msg_form and $ENV{REQUEST_METHOD} eq 'POST'){
- $msg_form =~ tr/\r//d;
+ $msg_form =~ tr/\r//d; # if we dont do this, ^M character in plain text mail
my $gpg = new GPG(gnupg_path => "/usr/bin", homedir => $GPG_HOMEDIR);
$enc_msg = $gpg->encrypt("$linkuser:\n\n$msg_form", $mymail_gpgid) or die $gpg->error();
if ($HAS_MAILSERVER){
- undef $mymailaddr_escaped;
use Mail::Sendmail;
- my %mail = ( To => "$mymailaddr"
- From => "$mailsender"
- Subject => '.'
- Message => "$enc_msg\n"
+ my %mail = ( To => "$mymailaddr",
+ From => "$mailsender",
+ Subject => '.',
+ Message => "$enc_msg\n"
);
sendmail(%mail) or die $Mail::Sendmail::error;
}
@@ -54,7 +54,7 @@ else {
use Net::SMTP;
use Net::SMTPS;
my $smtp = Net::SMTPS->new($mailsender_smtp, Port => $mailsender_port, doSSL => 'ssl', Debug_SSL => 0);
- my $mymailaddr_escaped = EscapeArobase{$mymailaddr};
+ my $mymailaddr_escaped = EscapeArobase($mymailaddr);
my $mailsender_escaped = EscapeArobase($mailsender);
$smtp->auth($mailsender, $mailsender_pw) or die;
@@ -73,9 +73,7 @@ else {
}
}
- if ($0 =~ /([\w]+)\.cgi$/){
- unlink "$1.cgi";
- }
+ unlink $linkfilename;
print "Location: /merci/index.html\n\n";
}
}
@@ -90,7 +88,7 @@ print qq{<!DOCTYPE html>
<title>{link_web_title}</title>
</head>
<body>
- <p>{type_msg_below}:</p>
+ <p id="msgbelow">{type_msg_below}:</p>
<form method="POST">
<textarea id="msg" wrap="off" cols="50" rows="30" name="msg"></textarea><br>
};
@@ -99,7 +97,7 @@ if (defined $error_processing_msg){
}
printf q{
<br>
- <input type="submit" value="{link_send_btn}">
+ <input id="sendbtn" type="submit" value="{link_send_btn}">
</form>
</body>
</html> };
diff --git a/nginx-example.conf b/nginx-example.conf
index 97e5027..f3d9252 100644
--- a/nginx-example.conf
+++ b/nginx-example.conf
@@ -30,5 +30,11 @@ server {
fastcgi_pass unix:/run/fcgiwrap.sock;
include /etc/nginx/fastcgi_params;
}
+
+ add_header Strict-Transport-Security "max-age=63072000; preload";
+ add_header Content-Security-Policy "default-src 'self'";
+ add_header X-Frame-Options DENY;
+ add_header Access-Control-Allow-Origin https://$server_name;
+ add_header Vary Origin; # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Origin#cors_and_caching
}
diff --git a/styles.css b/styles.css
index a83cd47..7f8770d 100644
--- a/styles.css
+++ b/styles.css
@@ -1,51 +1,158 @@
html{
- background-color:skyblue
-}
+ background-color:#87CEEB
+}
-th{
- padding:10px;
- font-size:110%;
-}
+body{
+ font-family:sans-serif;
+ width:80%;
+ margin:auto;
+ font-size:12pt;
+}
-td{
- text-align:center;
+h1, #msgbelow{
+ text-align:center;
+}
+
+
+#linkstable tr th{
+ padding:10px;
+ font-size:110%;
+ margin:0;
+}
+
+#linkstable td{
+ text-align:center;
padding: 5px ;
-}
+}
-input{
+#linkstable input{
padding:10px;
margin:5px;
margin-top:10px;
}
-table{
+#linkstable table{
margin-top:10px;
border: 1px solid black;
}
-th,tr,td{
+#linkstable td{
border:1px solid black;
}
-
-body{
- font-family:sans-serif;
- width:80%;
- margin:auto;
- font-size:12pt;
-}
#msg{
- border: 1px solid black;
- resize:vertical;
- width:50%;
+ display:block;
+ margin-left:auto;
+ margin-right:auto;
+ border: 1px solid black;
+ overflow: -moz-scrollbars-none;
+ resize:vertical;
+ width:50%;
+ -ms-overflow-style:none;
+}
+
+
+#msg::-webkit-scrollbar {
+ width: 0 !important
+}
+
+#success {
+ color:green;
+}
+
+#failure {
+ color:red;
+}
+
+#loginbox {
+ border:none;
+ margin-right:auto;
+ margin-left:auto
+}
+
+#loginbox input {
+ width:100%;
+ padding: 5px;
+ line-height:25px;
+
+}
+
+#loginbox td {
+ text-align:right;
+ border:none;
+}
+
+#loginbox tr {
+ border:none;
+}
+
+#authbtn td {
+ text-align:center;
+}
+
+#authbtn input {
+ width:100px;
+}
+
+#mailfied {
+ padding: 10px
+}
+
+#refreshbtn, #deletelinkbtn, #deleteallbtn,
+#logoutbtn, #genlinkbtn, #sendbtn {
+ border-radius:0
+ border:3px outset silver;
+ padding:15px;
+ margin:5px;
+}
+
+#mailfield {
+ padding: 10px;
}
@media screen and (max-width: 740px) {
- body { font-size:0.8em; }
- #msg{width:100%;}
- th, td{ padding:3px; }
- input {
- padding:4px;
- margin: 2px;
+ body {
+ font-size:0.8em;
+ }
+
+ #linkstable th,td{
+ padding:3px;
+ }
+
+ #linkstable input{
+ padding:4px;
+ margin:2px;
+ margin-top:10px;
+ }
+
+ #loginbox input{
+ padding: 2px;
+ line-height:15px;
+ }
+
+ #mailfield{
+ padding: 3px;
+ }
+
+ #msg {
+ width:50%;
+ margin:0;
+ }
+
+ #msgbelow {
+ text-align:left;
+ }
+
+ #refreshbtn, #deletelinkbtn, #deleteallbtn,
+ #logoutbtn, #genlinkbtn, #sendbtn {
+ padding: 8px
+ }
+
+ #genlinkbtn{
+ display:block
+ }
+
+ #sendbtn {
+ display:inline
}
}