diff options
author | Miquel Lionel <lionel@les-miquelots.net> | 2021-06-24 13:53:58 +0100 |
---|---|---|
committer | Miquel Lionel <lionelmiquel@sfr.fr> | 2021-07-02 01:21:59 +0100 |
commit | de854ac0a8fe72a1b0d0137c351b39de25e0adfe (patch) | |
tree | ae1386ade7ebb65e2b269347b09c8ced87fb614f | |
parent | 420155e750c4b95dd4327d7adb4310a64eafb364 (diff) | |
download | gpigeon-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-- | Makefile | 92 | ||||
-rw-r--r-- | README.md | 39 | ||||
-rw-r--r-- | config.def.mk | 26 | ||||
-rw-r--r-- | domains.csv | 323 | ||||
-rwxr-xr-x | gpigeon-template.cgi | 424 | ||||
-rwxr-xr-x | gpigeonctl | 101 | ||||
-rwxr-xr-x | gpigeonctl.def.pl | 283 | ||||
-rw-r--r-- | index.html | 44 | ||||
-rw-r--r-- | link-tmpl-template.cgi | 28 | ||||
-rw-r--r-- | nginx-example.conf | 6 | ||||
-rw-r--r-- | styles.css | 163 |
11 files changed, 844 insertions, 685 deletions
@@ -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 @@ -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} 🔗</th> + <th>$text_strings{theader_for} 📧</th> + <th>$text_strings{theader_deletion} 🗑</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(); +} @@ -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 } @@ -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 } } |