forked from Disroot/gpg-lacre
Initial commit for gpg-mailgate-web addition.
This commit is contained in:
parent
4942419db7
commit
98b4e341cf
15 changed files with 933 additions and 8 deletions
|
@ -1,5 +1,9 @@
|
|||
import os
|
||||
import os.path
|
||||
import subprocess
|
||||
import shutil
|
||||
import random
|
||||
import string
|
||||
|
||||
def public_keys( keyhome ):
|
||||
cmd = '/usr/bin/gpg --homedir %s --list-keys --with-colons' % keyhome
|
||||
|
@ -15,12 +19,57 @@ def public_keys( keyhome ):
|
|||
keys.append(key)
|
||||
return keys
|
||||
|
||||
# confirms a key has a given email address
|
||||
def confirm_key( content, email ):
|
||||
tmpkeyhome = ''
|
||||
|
||||
while True:
|
||||
tmpkeyhome = '/tmp/' + ''.join(random.choice(string.ascii_uppercase + string.digits) for x in range(12))
|
||||
if not os.path.exists(tmpkeyhome):
|
||||
break
|
||||
|
||||
os.mkdir(tmpkeyhome)
|
||||
p = subprocess.Popen( ['/usr/bin/gpg', '--homedir', tmpkeyhome, '--import', '--batch'], stdin=subprocess.PIPE, stdout=subprocess.PIPE,stderr=subprocess.PIPE )
|
||||
result = p.communicate(input=content)[1]
|
||||
confirmed = False
|
||||
|
||||
for line in result.split("\n"):
|
||||
if 'imported' in line and '<' in line and '>' in line:
|
||||
if line.split('<')[1].split('>')[0].lower() == email.lower():
|
||||
confirmed = True
|
||||
break
|
||||
else:
|
||||
break # confirmation failed
|
||||
|
||||
# cleanup
|
||||
shutil.rmtree(tmpkeyhome)
|
||||
|
||||
return confirmed
|
||||
|
||||
# adds a key and ensures it has the given email address
|
||||
def add_key( keyhome, content ):
|
||||
p = subprocess.Popen( ['/usr/bin/gpg', '--homedir', keyhome, '--import', '--batch'], stdin=subprocess.PIPE, stdout=subprocess.PIPE,stderr=subprocess.PIPE )
|
||||
p.communicate(input=content)
|
||||
p.wait()
|
||||
|
||||
def delete_key( keyhome, email ):
|
||||
from email.utils import parseaddr
|
||||
result = parseaddr(email)
|
||||
|
||||
if result[1]:
|
||||
# delete all keys matching this email address
|
||||
p = subprocess.Popen( ['/usr/bin/gpg', '--homedir', keyhome, '--delete-key', '--batch', '--yes', result[1]], stdin=None, stdout=subprocess.PIPE, stderr=subprocess.PIPE )
|
||||
p.wait()
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
class GPGEncryptor:
|
||||
def __init__(self, keyhome, recipients = None, charset = None):
|
||||
self._keyhome = keyhome
|
||||
self._message = ''
|
||||
self._recipients = list()
|
||||
self._charset = charset
|
||||
self._charset = charset
|
||||
if recipients != None:
|
||||
self._recipients.extend(recipients)
|
||||
|
||||
|
@ -34,15 +83,15 @@ class GPGEncryptor:
|
|||
|
||||
def _command(self):
|
||||
cmd = ["/usr/bin/gpg", "--trust-model", "always", "--homedir", self._keyhome, "--batch", "--yes", "--pgp7", "--no-secmem-warning", "-a", "-e"]
|
||||
|
||||
|
||||
# add recipients
|
||||
for recipient in self._recipients:
|
||||
cmd.append("-r")
|
||||
cmd.append(recipient)
|
||||
|
||||
|
||||
# add on the charset, if set
|
||||
if self._charset:
|
||||
cmd.append("--comment")
|
||||
cmd.append('Charset: ' + self._charset)
|
||||
|
||||
|
||||
return cmd
|
||||
|
|
30
gpg-mailgate-web/README
Normal file
30
gpg-mailgate-web/README
Normal file
|
@ -0,0 +1,30 @@
|
|||
gpg-mailgate-web
|
||||
----------------
|
||||
|
||||
gpg-mailgate-web is a web interface designed to allow any web user
|
||||
to upload their PGP public key and then have all mail sent from
|
||||
your mail server be encrypted. (Note: this is not meant for email
|
||||
authentication, only encryption.)
|
||||
|
||||
After submitting their key to a web form, the user will be required
|
||||
to confirm their email address. A cron script will register the
|
||||
public key with gpg-mailgate (keyhome_only must be set to no
|
||||
currently, which is the default) after email confirmation. From
|
||||
then on, email to the specified address will be encrypted with
|
||||
the public key.
|
||||
|
||||
Installation instructions:
|
||||
|
||||
1) Install gpg-mailgate.
|
||||
2) Create a MySQL database for gpg-mailgate.
|
||||
a) Schema file is located in schema.sql
|
||||
b) Database name and account goes in /etc/gpg-mailgate.conf (and set enabled = yes)
|
||||
3) Copy the contents of public_html to your web directory.
|
||||
4) Move config.sample.php to config.php and edit the configuration file.
|
||||
5) Copy cron.py to /usr/local/bin/gpgmw-cron.py and set up a cron job
|
||||
a) Create /etc/cron.d/gpgmw with the contents:
|
||||
*/3 * * * * gpgmap /usr/bin/python /usr/local/bin/gpgmw-cron.py > /dev/null
|
||||
(replace gpgmap with the owner of the gpg-mailgate GPG home directory)
|
||||
6) Ensure that cron is working and test your new gpg-mailgate-web installation!
|
||||
|
||||
Any issues should be reported to https://github.com/uakfdotb/gpg-mailgate
|
57
gpg-mailgate-web/cron.py
Normal file
57
gpg-mailgate-web/cron.py
Normal file
|
@ -0,0 +1,57 @@
|
|||
#!/usr/bin/python
|
||||
|
||||
from ConfigParser import RawConfigParser
|
||||
import GnuPG
|
||||
import MySQLdb
|
||||
|
||||
def appendLog(msg):
|
||||
if cfg.has_key('logging') and cfg['logging'].has_key('file'):
|
||||
log = open(cfg['logging']['file'], 'a')
|
||||
log.write(msg + "\n")
|
||||
log.close()
|
||||
|
||||
# Read configuration from /etc/gpg-mailgate.conf
|
||||
_cfg = RawConfigParser()
|
||||
_cfg.read('/etc/gpg-mailgate.conf')
|
||||
cfg = dict()
|
||||
for sect in _cfg.sections():
|
||||
cfg[sect] = dict()
|
||||
for (name, value) in _cfg.items(sect):
|
||||
cfg[sect][name] = value
|
||||
|
||||
if cfg.has_key('database') and cfg['database'].has_key('enabled') and cfg['database']['enabled'] == 'yes' and cfg['database'].has_key('name') and cfg['database'].has_key('host') and cfg['database'].has_key('username') and cfg['database'].has_key('password'):
|
||||
connection = MySQLdb.connect(host = cfg['database']['host'], user = cfg['database']['username'], passwd = cfg['database']['password'], db = cfg['database']['name'], port = 3306)
|
||||
cursor = connection.cursor()
|
||||
|
||||
# import keys
|
||||
cursor.execute("SELECT publickey, id, email FROM gpgmw_keys WHERE status = 0 AND confirm = '' LIMIT 100")
|
||||
result_set = cursor.fetchall()
|
||||
|
||||
for row in result_set:
|
||||
# delete any other public keys associated with this confirmed email address
|
||||
cursor.execute("DELETE FROM gpgmw_keys WHERE email = %s AND id != %s", (row[2], row[1],))
|
||||
GnuPG.delete_key(cfg['gpg']['keyhome'], row[2])
|
||||
appendLog('Deleted key for <' + row[2] + '> via import request')
|
||||
|
||||
if row[0].strip(): # we have this so that user can submit blank key to remove any encryption
|
||||
if GnuPG.confirm_key(row[0], row[2]):
|
||||
GnuPG.add_key(cfg['gpg']['keyhome'], row[0]) # import the key to gpg
|
||||
cursor.execute("UPDATE gpgmw_keys SET status = 1 WHERE id = %s", (row[1],)) # mark key as accepted
|
||||
appendLog('Imported key from <' + row[2] + '>')
|
||||
else:
|
||||
cursor.execute("DELETE FROM gpgmw_keys WHERE id = %s", (row[1],)) # delete key
|
||||
appendLog('Import confirmation failed for <' + row[2] + '>')
|
||||
|
||||
connection.commit()
|
||||
|
||||
# delete keys
|
||||
cursor.execute("SELECT email, id FROM gpgmw_keys WHERE status = 2 LIMIT 100")
|
||||
result_set = cursor.fetchall()
|
||||
|
||||
for row in result_set:
|
||||
GnuPG.delete_key(cfg['gpg']['keyhome'], row[0])
|
||||
cursor.execute("DELETE FROM gpgmw_keys WHERE id = %s", (row[1],))
|
||||
appendLog('Deleted key for <' + row[0] + '>')
|
||||
connection.commit()
|
||||
else:
|
||||
print "Warning: doing nothing since database settings are not configured!"
|
93
gpg-mailgate-web/public_html/config.sample.php
Normal file
93
gpg-mailgate-web/public_html/config.sample.php
Normal file
|
@ -0,0 +1,93 @@
|
|||
<?php
|
||||
/*
|
||||
|
||||
gpg-mailgate
|
||||
|
||||
This file is part of the gpg-mailgate source code.
|
||||
|
||||
gpg-mailgate is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
gpg-mailgate source code 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 Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with gpg-mailgate source code. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
$config = array();
|
||||
|
||||
//
|
||||
// GENERAL SITE SETTINGS
|
||||
//
|
||||
|
||||
//web team contact
|
||||
// this email address will be displayed if there is a database error
|
||||
$config['email_web'] = 'admin@example.com';
|
||||
|
||||
//address to send emails from
|
||||
$config['email_from'] = 'gpg-mailgate-web@example.com';
|
||||
|
||||
//this will be used as the subject when a user requests to add a PGP key
|
||||
$config['email_subject_requestpgp'] = 'Confirm your email address';
|
||||
|
||||
//site URL, without trailing slash
|
||||
$config['site_url'] = 'http://example.com/gpgmw';
|
||||
|
||||
//title of the website (displayed on home page)
|
||||
$config['site_title'] = 'PGP key management';
|
||||
|
||||
//
|
||||
// MAIL SETTINGS
|
||||
//
|
||||
|
||||
//whether to send mail through SMTP (instead of PHP mail function)
|
||||
$config['mail_smtp'] = false;
|
||||
|
||||
//SMTP settings, if mail_smtp is enabled
|
||||
$config['mail_smtp_host'] = 'localhost';
|
||||
$config['mail_smtp_port'] = 25;
|
||||
$config['mail_smtp_username'] = 'gpgmw';
|
||||
$config['mail_smtp_password'] = '';
|
||||
|
||||
//
|
||||
// DATABASE SETTINGS
|
||||
//
|
||||
|
||||
//database name (MySQL only); or see include/dbconnect.php
|
||||
$config['db_name'] = 'gpgmw';
|
||||
|
||||
//database host
|
||||
$config['db_host'] = 'localhost';
|
||||
|
||||
//database username
|
||||
$config['db_username'] = 'gpgmw';
|
||||
|
||||
//database password
|
||||
$config['db_password'] = '';
|
||||
|
||||
//
|
||||
// LOCK SETTINGS
|
||||
//
|
||||
|
||||
//the time in seconds a user must wait before trying again; otherwise they get locked out (count not increased)
|
||||
$config['lock_time_initial'] = array('requestpgp' => 10);
|
||||
|
||||
//the number of tries a user has (that passes the lock_time_initial test) before being locked by overload (extended duration)
|
||||
$config['lock_count_overload'] = array('requestpgp' => 3);
|
||||
|
||||
//the time that overloads last
|
||||
$config['lock_time_overload'] = array('requestpgp' => 900);
|
||||
|
||||
//time after which locks no longer apply, assuming the lock isn't active
|
||||
$config['lock_time_reset'] = 300;
|
||||
|
||||
//max time to store locks in the database; this way we can clear old locks with one function
|
||||
$config['lock_time_max'] = 3600;
|
||||
|
||||
?>
|
40
gpg-mailgate-web/public_html/confirm.php
Normal file
40
gpg-mailgate-web/public_html/confirm.php
Normal file
|
@ -0,0 +1,40 @@
|
|||
<?php
|
||||
/*
|
||||
|
||||
gpg-mailgate
|
||||
|
||||
This file is part of the gpg-mailgate source code.
|
||||
|
||||
gpg-mailgate is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
gpg-mailgate source code 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 Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with gpg-mailgate source code. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
require_once("config.php");
|
||||
require_once("include/common.php");
|
||||
require_once("include/dbconnect.php");
|
||||
require_once("include/pgp.php");
|
||||
|
||||
if(isset($_REQUEST['email']) && isset($_REQUEST['confirm'])) {
|
||||
$result = confirmPGP($_REQUEST['email'], $_REQUEST['confirm']);
|
||||
|
||||
if($result === true) {
|
||||
get_page("home", array('message' => 'Your email address has been confirmed successfully. Within a few minutes, emails from our mail server to you should be encrypted with your PGP public key.'));
|
||||
} else {
|
||||
get_page("home", array('message' => 'Error: failed to confirm any email address. You may have already confirmed the address, or you may have the wrong confirmation key.'));
|
||||
}
|
||||
} else {
|
||||
get_page("home");
|
||||
}
|
||||
|
||||
?>
|
257
gpg-mailgate-web/public_html/include/common.php
Normal file
257
gpg-mailgate-web/public_html/include/common.php
Normal file
|
@ -0,0 +1,257 @@
|
|||
<?php
|
||||
/*
|
||||
|
||||
gpg-mailgate
|
||||
|
||||
This file is part of the gpg-mailgate source code.
|
||||
|
||||
gpg-mailgate is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
gpg-mailgate source code 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 Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with gpg-mailgate source code. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
function string_begins_with($string, $search)
|
||||
{
|
||||
return (strncmp($string, $search, strlen($search)) == 0);
|
||||
}
|
||||
|
||||
function boolToString($bool) {
|
||||
return $bool ? 'true' : 'false';
|
||||
}
|
||||
|
||||
//returns an absolute path to the include directory
|
||||
function includePath() {
|
||||
$self = __FILE__;
|
||||
$lastSlash = strrpos($self, "/");
|
||||
return substr($self, 0, $lastSlash + 1);
|
||||
}
|
||||
|
||||
//returns a relative path to the gpg-mailgate-web web root directory, without trailing slash
|
||||
function basePath() {
|
||||
$commonPath = __FILE__;
|
||||
$requestPath = $_SERVER['SCRIPT_FILENAME'];
|
||||
|
||||
//count the number of slashes
|
||||
// number of .. needed for include level is numslashes(request) - numslashes(common)
|
||||
// then add one more to get to base
|
||||
$commonSlashes = substr_count($commonPath, '/');
|
||||
$requestSlashes = substr_count($requestPath, '/');
|
||||
$numParent = $requestSlashes - $commonSlashes + 1;
|
||||
|
||||
$basePath = ".";
|
||||
for($i = 0; $i < $numParent; $i++) {
|
||||
$basePath .= "/..";
|
||||
}
|
||||
|
||||
return $basePath;
|
||||
}
|
||||
|
||||
function uid($length) {
|
||||
$characters = "0123456789abcdefghijklmnopqrstuvwxyz";
|
||||
$string = "";
|
||||
|
||||
for ($p = 0; $p < $length; $p++) {
|
||||
$string .= $characters[secure_random() % strlen($characters)];
|
||||
}
|
||||
|
||||
return $string;
|
||||
}
|
||||
|
||||
function get_page($page, $args = array()) {
|
||||
//let pages use some variables
|
||||
extract($args);
|
||||
$config = $GLOBALS['config'];
|
||||
|
||||
$basePath = basePath();
|
||||
|
||||
$themePath = $basePath . "/theme";
|
||||
$themePageInclude = "$themePath/$page.php";
|
||||
|
||||
if(file_exists("$themePath/header.php")) {
|
||||
include("$themePath/header.php");
|
||||
}
|
||||
|
||||
if(file_exists($themePageInclude)) {
|
||||
include($themePageInclude);
|
||||
}
|
||||
|
||||
if(file_exists("$themePath/footer.php")) {
|
||||
include("$themePath/footer.php");
|
||||
}
|
||||
}
|
||||
|
||||
function isAscii($str) {
|
||||
return 0 == preg_match('/[^\x00-\x7F]/', $str);
|
||||
}
|
||||
|
||||
//returns random number from 0 to 2^24
|
||||
function secure_random() {
|
||||
return hexdec(bin2hex(secure_random_bytes(3)));
|
||||
}
|
||||
|
||||
function gpgmw_mail($subject, $body, $to) { //returns true=ok, false=notok
|
||||
$config = $GLOBALS['config'];
|
||||
$from = filter_var($config['email_from'], FILTER_SANITIZE_EMAIL);
|
||||
$to = filter_var($to, FILTER_SANITIZE_EMAIL);
|
||||
|
||||
if($to === false || $from === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(isset($config['mail_smtp']) && $config['mail_smtp']) {
|
||||
require_once "Mail.php";
|
||||
|
||||
$host = $config['mail_smtp_host'];
|
||||
$port = $config['mail_smtp_port'];
|
||||
$username = $config['mail_smtp_username'];
|
||||
$password = $config['mail_smtp_password'];
|
||||
$headers = array ('From' => $from,
|
||||
'To' => $to,
|
||||
'Subject' => $subject,
|
||||
'Content-Type' => 'text/plain');
|
||||
$smtp = Mail::factory('smtp',
|
||||
array ('host' => $host,
|
||||
'port' => $port,
|
||||
'auth' => true,
|
||||
'username' => $username,
|
||||
'password' => $password));
|
||||
|
||||
$mail = $smtp->send($to, $headers, $body);
|
||||
|
||||
if (PEAR::isError($mail)) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
$headers = "From: $from\r\n";
|
||||
$headers .= "Content-type: text/plain\r\n";
|
||||
return mail($to, $subject, $body, $headers);
|
||||
}
|
||||
}
|
||||
|
||||
//secure_random_bytes from https://github.com/GeorgeArgyros/Secure-random-bytes-in-PHP
|
||||
/*
|
||||
* The function is providing, at least at the systems tested :),
|
||||
* $len bytes of entropy under any PHP installation or operating system.
|
||||
* The execution time should be at most 10-20 ms in any system.
|
||||
*/
|
||||
function secure_random_bytes($len = 10) {
|
||||
|
||||
/*
|
||||
* Our primary choice for a cryptographic strong randomness function is
|
||||
* openssl_random_pseudo_bytes.
|
||||
*/
|
||||
$SSLstr = '4'; // http://xkcd.com/221/
|
||||
if (function_exists('openssl_random_pseudo_bytes') &&
|
||||
(version_compare(PHP_VERSION, '5.3.4') >= 0 ||
|
||||
substr(PHP_OS, 0, 3) !== 'WIN'))
|
||||
{
|
||||
$SSLstr = openssl_random_pseudo_bytes($len, $strong);
|
||||
if ($strong)
|
||||
return $SSLstr;
|
||||
}
|
||||
|
||||
/*
|
||||
* If mcrypt extension is available then we use it to gather entropy from
|
||||
* the operating system's PRNG. This is better than reading /dev/urandom
|
||||
* directly since it avoids reading larger blocks of data than needed.
|
||||
* Older versions of mcrypt_create_iv may be broken or take too much time
|
||||
* to finish so we only use this function with PHP 5.3 and above.
|
||||
*/
|
||||
if (function_exists('mcrypt_create_iv') &&
|
||||
(version_compare(PHP_VERSION, '5.3.0') >= 0 ||
|
||||
substr(PHP_OS, 0, 3) !== 'WIN'))
|
||||
{
|
||||
$str = mcrypt_create_iv($len, MCRYPT_DEV_URANDOM);
|
||||
if ($str !== false)
|
||||
return $str;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* No build-in crypto randomness function found. We collect any entropy
|
||||
* available in the PHP core PRNGs along with some filesystem info and memory
|
||||
* stats. To make this data cryptographically strong we add data either from
|
||||
* /dev/urandom or if its unavailable, we gather entropy by measuring the
|
||||
* time needed to compute a number of SHA-1 hashes.
|
||||
*/
|
||||
$str = '';
|
||||
$bits_per_round = 2; // bits of entropy collected in each clock drift round
|
||||
$msec_per_round = 400; // expected running time of each round in microseconds
|
||||
$hash_len = 20; // SHA-1 Hash length
|
||||
$total = $len; // total bytes of entropy to collect
|
||||
|
||||
$handle = @fopen('/dev/urandom', 'rb');
|
||||
if ($handle && function_exists('stream_set_read_buffer'))
|
||||
@stream_set_read_buffer($handle, 0);
|
||||
|
||||
do
|
||||
{
|
||||
$bytes = ($total > $hash_len)? $hash_len : $total;
|
||||
$total -= $bytes;
|
||||
|
||||
//collect any entropy available from the PHP system and filesystem
|
||||
$entropy = rand() . uniqid(mt_rand(), true) . $SSLstr;
|
||||
$entropy .= implode('', @fstat(@fopen( __FILE__, 'r')));
|
||||
$entropy .= memory_get_usage();
|
||||
if ($handle)
|
||||
{
|
||||
$entropy .= @fread($handle, $bytes);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Measure the time that the operations will take on average
|
||||
for ($i = 0; $i < 3; $i ++)
|
||||
{
|
||||
$c1 = microtime(true);
|
||||
$var = sha1(mt_rand());
|
||||
for ($j = 0; $j < 50; $j++)
|
||||
{
|
||||
$var = sha1($var);
|
||||
}
|
||||
$c2 = microtime(true);
|
||||
$entropy .= $c1 . $c2;
|
||||
}
|
||||
|
||||
// Based on the above measurement determine the total rounds
|
||||
// in order to bound the total running time.
|
||||
$rounds = (int)($msec_per_round*50 / (int)(($c2-$c1)*1000000));
|
||||
|
||||
// Take the additional measurements. On average we can expect
|
||||
// at least $bits_per_round bits of entropy from each measurement.
|
||||
$iter = $bytes*(int)(ceil(8 / $bits_per_round));
|
||||
for ($i = 0; $i < $iter; $i ++)
|
||||
{
|
||||
$c1 = microtime();
|
||||
$var = sha1(mt_rand());
|
||||
for ($j = 0; $j < $rounds; $j++)
|
||||
{
|
||||
$var = sha1($var);
|
||||
}
|
||||
$c2 = microtime();
|
||||
$entropy .= $c1 . $c2;
|
||||
}
|
||||
|
||||
}
|
||||
// We assume sha1 is a deterministic extractor for the $entropy variable.
|
||||
$str .= sha1($entropy, true);
|
||||
} while ($len > strlen($str));
|
||||
|
||||
if ($handle)
|
||||
@fclose($handle);
|
||||
|
||||
return substr($str, 0, $len);
|
||||
}
|
||||
|
||||
?>
|
76
gpg-mailgate-web/public_html/include/dbconnect.php
Normal file
76
gpg-mailgate-web/public_html/include/dbconnect.php
Normal file
|
@ -0,0 +1,76 @@
|
|||
<?php
|
||||
/*
|
||||
|
||||
gpg-mailgate
|
||||
|
||||
This file is part of the gpg-mailgate source code.
|
||||
|
||||
gpg-mailgate is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
gpg-mailgate source code 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 Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with gpg-mailgate source code. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
function dieDatabaseError($ex = NULL) {
|
||||
global $config;
|
||||
|
||||
if($ex == NULL) {
|
||||
$pre = "Encountered database error.";
|
||||
} else {
|
||||
$pre = "Encountered database error: " . $ex->getMessage() . ".";
|
||||
}
|
||||
|
||||
die($pre . " If this is unexpected, consider <a href=\"mailto:{$config['email_web']}\">reporting it to our web team</a>. Otherwise, <a href=\"/\">click here to return to the home page.</a>");
|
||||
}
|
||||
|
||||
try {
|
||||
$database = new PDO('mysql:host=' . $config['db_host'] . ';dbname=' . $config['db_name'], $config['db_username'], $config['db_password'], array(PDO::ATTR_EMULATE_PREPARES => false, PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION));
|
||||
} catch(PDOException $ex) {
|
||||
dieDatabaseError($ex);
|
||||
}
|
||||
|
||||
function databaseQuery($command, $array = array(), $assoc = false) {
|
||||
global $database;
|
||||
|
||||
if(!is_array($array)) {
|
||||
dieDatabaseError();
|
||||
}
|
||||
|
||||
try {
|
||||
$query = $database->prepare($command);
|
||||
|
||||
if(!$query) {
|
||||
print_r($database->errorInfo());
|
||||
dieDatabaseError();
|
||||
}
|
||||
|
||||
//set fetch mode depending on parameter
|
||||
if($assoc) {
|
||||
$query->setFetchMode(PDO::FETCH_ASSOC);
|
||||
} else {
|
||||
$query->setFetchMode(PDO::FETCH_NUM);
|
||||
}
|
||||
|
||||
$success = $query->execute($array);
|
||||
|
||||
if(!$success) {
|
||||
print_r($query->errorInfo());
|
||||
dieDatabaseError();
|
||||
}
|
||||
|
||||
return $query;
|
||||
} catch(PDOException $ex) {
|
||||
dieDatabaseError($ex);
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
124
gpg-mailgate-web/public_html/include/lock.php
Normal file
124
gpg-mailgate-web/public_html/include/lock.php
Normal file
|
@ -0,0 +1,124 @@
|
|||
<?php
|
||||
/*
|
||||
|
||||
gpg-mailgate
|
||||
|
||||
This file is part of the gpg-mailgate source code.
|
||||
|
||||
gpg-mailgate is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
gpg-mailgate source code 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 Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with gpg-mailgate source code. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
//lock.php is basic spam-submit prevention
|
||||
//lock_time_initial, lock_time_overload, lock_count_overload, lock_time_reset, and lock_time_max should be defined in $config
|
||||
|
||||
//returns boolean: true=proceed, false=lock up; the difference between this and lockAction is that this can be used for repeated tasks, like admin
|
||||
// then, only if action was unsuccessful would lockAction be called
|
||||
function checkLock($action) {
|
||||
global $config;
|
||||
$lock_time_initial = $config['lock_time_initial'];
|
||||
$lock_time_overload = $config['lock_time_overload'];
|
||||
$lock_count_overload = $config['lock_count_overload'];
|
||||
$lock_time_reset = $config['lock_time_reset'];
|
||||
$lock_time_max = $config['lock_time_max'];
|
||||
|
||||
if(!isset($lock_time_initial[$action])) {
|
||||
return true; //well we can't do anything...
|
||||
}
|
||||
|
||||
$ip = $_SERVER['REMOTE_ADDR'];
|
||||
|
||||
$result = databaseQuery("SELECT id, time, num FROM gpgmw_locks WHERE ip = ? AND action = ?", array($ip, $action), true);
|
||||
if($row = $result->fetch()) {
|
||||
$id = $row['id'];
|
||||
$time = $row['time'];
|
||||
$count = $row['num']; //>=0 count means it's a regular initial lock; -1 count means overload lock
|
||||
|
||||
if($count >= 0) {
|
||||
if(time() <= $time + $lock_time_initial[$action]) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if(time() <= $time + $lock_time_overload[$action]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//returns boolean: true=proceed, false=lock up
|
||||
function lockAction($action) {
|
||||
global $config;
|
||||
$lock_time_initial = $config['lock_time_initial'];
|
||||
$lock_time_overload = $config['lock_time_overload'];
|
||||
$lock_count_overload = $config['lock_count_overload'];
|
||||
$lock_time_reset = $config['lock_time_reset'];
|
||||
$lock_time_max = $config['lock_time_max'];
|
||||
|
||||
if(!isset($lock_time_initial[$action])) {
|
||||
return true; //well we can't do anything...
|
||||
}
|
||||
|
||||
$ip = $_SERVER['REMOTE_ADDR'];
|
||||
$replace_id = -1;
|
||||
|
||||
//first find records with ip/action
|
||||
$result = databaseQuery("SELECT id, time, num FROM gpgmw_locks WHERE ip = ? AND action = ?", array($ip, $action), true);
|
||||
if($row = $result->fetch()) {
|
||||
$id = $row['id'];
|
||||
$time = $row['time'];
|
||||
$count = $row['num']; //>=0 count means it's a regular initial lock; -1 count means overload lock
|
||||
|
||||
if($count >= 0) {
|
||||
if(time() <= $time + $lock_time_initial[$action]) {
|
||||
return false;
|
||||
} else if(time() > $time + $lock_time_reset) {
|
||||
//this entry is old, but use it to replace
|
||||
$replace_id = $id;
|
||||
} else {
|
||||
//increase the count; maybe initiate an OVERLOAD
|
||||
$count = $count + 1;
|
||||
if($count >= $lock_count_overload[$action]) {
|
||||
databaseQuery("UPDATE gpgmw_locks SET num = '-1', time = ? WHERE ip = ?", array(time(), $ip));
|
||||
return false;
|
||||
} else {
|
||||
databaseQuery("UPDATE gpgmw_locks SET num = ?, time = ? WHERE ip = ?", array($count, time(), $ip));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if(time() <= $time + $lock_time_overload[$action]) {
|
||||
return false;
|
||||
} else {
|
||||
//their overload is over, so this entry is old
|
||||
$replace_id = $id;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
databaseQuery("INSERT INTO gpgmw_locks (ip, time, action, num) VALUES (?, ?, ?, '1')", array($ip, time(), $action));
|
||||
}
|
||||
|
||||
if($replace_id != -1) {
|
||||
databaseQuery("UPDATE gpgmw_locks SET num = '1', time = ? WHERE id = ?", array(time(), $replace_id));
|
||||
}
|
||||
|
||||
//some housekeeping
|
||||
$delete_time = time() - $lock_time_max;
|
||||
databaseQuery("DELETE FROM gpgmw_locks WHERE time <= ?", array($delete_time));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
?>
|
88
gpg-mailgate-web/public_html/include/pgp.php
Normal file
88
gpg-mailgate-web/public_html/include/pgp.php
Normal file
|
@ -0,0 +1,88 @@
|
|||
<?php
|
||||
/*
|
||||
|
||||
gpg-mailgate
|
||||
|
||||
This file is part of the gpg-mailgate source code.
|
||||
|
||||
gpg-mailgate is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
gpg-mailgate source code 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 Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with gpg-mailgate source code. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
//returns true on success or error message on failure
|
||||
function requestPGP($email, $key) {
|
||||
require_once(includePath() . "/lock.php");
|
||||
global $config;
|
||||
|
||||
if(!checkLock('requestpgp')) {
|
||||
return "please wait a bit before trying again";
|
||||
}
|
||||
|
||||
if(!filter_var($email, FILTER_VALIDATE_EMAIL)) {
|
||||
return "invalid email address";
|
||||
}
|
||||
|
||||
if(strlen($email) > 256 || strlen($key) > 1024 * 32) {
|
||||
return "email address or key too long";
|
||||
}
|
||||
|
||||
if(!isAscii($key)) {
|
||||
return "only keys encoded with ASCII armor are accepted (gpg --armor)";
|
||||
}
|
||||
|
||||
//housekeeping
|
||||
databaseQuery("DELETE FROM gpgmw_keys WHERE time < DATE_SUB(NOW(), INTERVAL 48 HOUR) AND confirm != '' AND status = 0");
|
||||
|
||||
//if we already have an unaccepted key for this user, only replace if one day has elapsed since the last request
|
||||
// this may prevent spam
|
||||
$result = databaseQuery("SELECT HOUR(TIMEDIFF(time, NOW())), id FROM gpgmw_keys WHERE email = ? AND status = 0", array($email));
|
||||
|
||||
if($row = $result->fetch()) {
|
||||
if($row[0] < 24) {
|
||||
return "there is already a key in the queue for this email address; please wait twenty-four hours between submitting keys, or confirm the previous key and then resubmit";
|
||||
} else {
|
||||
databaseQuery('DELETE FROM gpgmw_keys WHERE id = ?', array($row[1]));
|
||||
}
|
||||
}
|
||||
|
||||
//well, it looks good, let's submit it
|
||||
lockAction('requestpgp');
|
||||
$confirm = uid(32);
|
||||
$result = gpgmw_mail($config['email_subject_requestpgp'], "Please confirm your email address to complete the submission process. You can do so by clicking the link below\n\n{$config['site_url']}/confirm.php?email=" . urlencode($email) . "&confirm=$confirm\n\nThanks,\ngpg-mailgate-web", $email);
|
||||
|
||||
if(!$result) {
|
||||
return "failed to send email";
|
||||
}
|
||||
|
||||
databaseQuery("INSERT INTO gpgmw_keys (email, publickey, confirm) VALUES (?, ?, ?)", array($email, $key, $confirm));
|
||||
return true;
|
||||
}
|
||||
|
||||
//returns false on failure or true on success
|
||||
function confirmPGP($email, $confirm) {
|
||||
if(!lockAction('confirmpgp')) {
|
||||
return "try again later";
|
||||
}
|
||||
|
||||
$result = databaseQuery("SELECT id FROM gpgmw_keys WHERE confirm = ? AND email = ?", array($confirm, $email));
|
||||
|
||||
if($row = $result->fetch()) {
|
||||
databaseQuery("UPDATE gpgmw_keys SET confirm = '' WHERE id = ?", array($row[0]));
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
?>
|
40
gpg-mailgate-web/public_html/index.php
Normal file
40
gpg-mailgate-web/public_html/index.php
Normal file
|
@ -0,0 +1,40 @@
|
|||
<?php
|
||||
/*
|
||||
|
||||
gpg-mailgate
|
||||
|
||||
This file is part of the gpg-mailgate source code.
|
||||
|
||||
gpg-mailgate is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
gpg-mailgate source code 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 Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with gpg-mailgate source code. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
require_once("config.php");
|
||||
require_once("include/common.php");
|
||||
require_once("include/dbconnect.php");
|
||||
require_once("include/pgp.php");
|
||||
|
||||
if(isset($_POST['email']) && isset($_POST['key'])) {
|
||||
$result = requestPGP($_POST['email'], $_POST['key']);
|
||||
|
||||
if($result === true) {
|
||||
get_page("home", array('message' => 'Key submission successful. Please check your email to confirm your email address.'));
|
||||
} else {
|
||||
get_page("home", array('message' => 'Error: ' . $result . '.'));
|
||||
}
|
||||
} else {
|
||||
get_page("home");
|
||||
}
|
||||
|
||||
?>
|
52
gpg-mailgate-web/public_html/theme/home.php
Normal file
52
gpg-mailgate-web/public_html/theme/home.php
Normal file
|
@ -0,0 +1,52 @@
|
|||
<?php
|
||||
/*
|
||||
|
||||
gpg-mailgate
|
||||
|
||||
This file is part of the gpg-mailgate source code.
|
||||
|
||||
gpg-mailgate is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
gpg-mailgate source code 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 Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with gpg-mailgate source code. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
?>
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<title>gpg-mailgate-web</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1><?= $config['site_title'] ?></h1>
|
||||
|
||||
<? if(!empty($message)) { ?>
|
||||
<p><b><i><?= htmlspecialchars($message) ?></i></b></p>
|
||||
<? } ?>
|
||||
|
||||
<p>Use the form below to submit an ASCII-armored PGP public key. After submission, you will receive an email asking you to confirm your email address. Note that this is not a keyserver.</p>
|
||||
|
||||
<form method="POST">
|
||||
<table>
|
||||
<tr>
|
||||
<td>Your email address (must match key)</td>
|
||||
<td><input type="text" name="email" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>ASCII-armored PGP public key</td>
|
||||
<td><textarea name="key" rows="10" cols="80"></textarea></td>
|
||||
</tr>
|
||||
</table>
|
||||
<input type="submit" value="Submit key" />
|
||||
</form>
|
||||
|
||||
</body>
|
||||
</html>
|
0
gpg-mailgate-web/public_html/theme/index.html
Normal file
0
gpg-mailgate-web/public_html/theme/index.html
Normal file
10
gpg-mailgate-web/schema.sql
Normal file
10
gpg-mailgate-web/schema.sql
Normal file
|
@ -0,0 +1,10 @@
|
|||
-- confirm is empty once an email address has been confirmed, and otherwise is the confirmation key
|
||||
-- status
|
||||
-- initializes to 0
|
||||
-- is set to 1 after a public key with (confirm='', status=0) has been imported
|
||||
-- is set to 2 if a key should be deleted (will be deleted based on email address)
|
||||
-- publickey is the ASCII-armored PGP public key; can be cleared to save space if status > 0
|
||||
CREATE TABLE gpgmw_keys (id INT NOT NULL PRIMARY KEY AUTO_INCREMENT, email VARCHAR(256), publickey TEXT, confirm VARCHAR(32), status INT NOT NULL DEFAULT 0, time TIMESTAMP DEFAULT CURRENT_TIMESTAMP);
|
||||
|
||||
-- see include/lock.php for documentation
|
||||
CREATE TABLE gpgmw_locks (id INT NOT NULL PRIMARY KEY AUTO_INCREMENT, ip VARCHAR(16), time INT, action VARCHAR(16), num INT);
|
|
@ -24,6 +24,15 @@ file = /tmp/gpg-mailgate.log
|
|||
host = 127.0.0.1
|
||||
port = 10028
|
||||
|
||||
[database]
|
||||
# uncomment the settings below if you want
|
||||
# to read keys from a gpg-mailgate-web database
|
||||
#enabled = yes
|
||||
#name = gpgmw
|
||||
#host = localhost
|
||||
#username = gpgmw
|
||||
#password =
|
||||
|
||||
[keymap]
|
||||
# You can find these by running the following command:
|
||||
# gpg --list-keys --keyid-format long user@example.com
|
||||
|
|
|
@ -45,15 +45,15 @@ def encrypt_payload( payload, gpg_to_cmdline ):
|
|||
gpg = GnuPG.GPGEncryptor( cfg['gpg']['keyhome'], gpg_to_cmdline, payload.get_content_charset() )
|
||||
gpg.update( raw_payload )
|
||||
payload.set_payload( gpg.encrypt() )
|
||||
|
||||
|
||||
isAttachment = payload.get_param( 'attachment', None, 'Content-Disposition' ) is not None
|
||||
|
||||
|
||||
if isAttachment:
|
||||
filename = payload.get_filename()
|
||||
|
||||
|
||||
if filename:
|
||||
pgpFilename = filename + ".pgp"
|
||||
|
||||
|
||||
if payload.get('Content-Disposition') is not None:
|
||||
payload.set_param( 'filename', pgpFilename, 'Content-Disposition' )
|
||||
if payload.get('Content-Type') is not None:
|
||||
|
|
Loading…
Reference in a new issue