def get_admin_email(): ''' Get the admin's email. >>> email = get_admin_email() >>> email is not None True >>> email.endswith(get_domain()) True ''' admin_email = None try: users = User.objects.filter(is_superuser=True) if users is not None and len(users) > 0: for user in users: email = user.email if email is not None and len(email.strip()) > 0: admin_email = email break else: username = user.username email = get_email(user.username) if email is not None and len(email.strip()) > 0: admin_email = email break except: record_exception() log_message('EXCEPTION - see syr.exception.log for details') if admin_email is None: admin_email = 'daemon@{}'.format(get_domain()) return admin_email
def prep_message_header(self, message, to_domain): ''' Prepare the header of a Message. ''' if message is None: self.log_message('no message defined in prep_message_header') elif to_domain is None: self.log_message('domain is not defined in prep_message_header') else: message_date = datetime.utcnow().replace(tzinfo=utc) from_user = get_metadata_address(domain=get_domain()) to_user = get_metadata_address(domain=to_domain) message.__setitem__(mime_constants.FROM_KEYWORD, from_user) message.__setitem__(mime_constants.TO_KEYWORD, to_user) message.__setitem__(constants.ORIGINAL_FROM, from_user) message.__setitem__(constants.ORIGINAL_TO, to_user) message.__setitem__(mime_constants.DATE_KEYWORD, message_date.__str__()) message.__setitem__(mime_constants.MESSAGE_ID_KEYWORD, utils.get_message_id()) self.log_message("message's content type: {}".format( message.get_content_type())) self.log_message("message's boundary: {}".format( message.get_boundary())) if self.DEBUGGING: self.log_message("message's key/value pair") for key in message.keys(): self.log_message('{}: {}'.format(key, message.get(key))) return message
def get_mail_status(): ''' Return whether Mail is running. >>> status = get_mail_status() >>> status == 'green' True ''' programs_running = is_mail_running() domain = get_domain() mta = mail_server_address() app_configured = (domain is not None and len(domain.strip()) > 0 and mta is not None and len(mta.strip()) > 0) if programs_running: if app_configured: status = STATUS_GREEN else: status = STATUS_YELLOW else: status = STATUS_RED if status != STATUS_GREEN: log_message('is postfix running: {}'.format(is_program_running('postfix/master'))) log_message('is rqworker running: {}'.format(is_program_running('rqworker'))) log_message('is redis running: {}'.format(is_program_running('redis'))) log_message('programs running: {}'.format(programs_running)) log_message('domain ok: {}'.format(domain is not None and len(domain.strip()) > 0)) log_message('mta ok: {}'.format(mta is not None and len(mta.strip()) > 0)) log_message('app_configured: {}'.format(app_configured)) return status
def create_encrypted_message(self, inner_message, to_domain): ''' Create an encrypted Message. ''' message = None if to_domain is None: self.log_message('domain is not defined') elif inner_message is None: self.log_message('no inner message defined') else: from_user = get_email(get_metadata_address(domain=get_domain())) to_user = get_email(get_metadata_address(domain=to_domain)) crypto_message = create_protected_message( from_user, to_user, inner_message.as_string(), utils.get_message_id()) if crypto_message.is_crypted(): # add the DKIM signature to the inner message if user opted for it crypto_message = add_dkim_sig_optionally(crypto_message) message = crypto_message.get_email_message().get_message() self.crypted_with = crypto_message.get_metadata_crypted_with() self.log_message('crypted with: {}'.format(self.crypted_with)) for part in message.walk(): self.log_message('Content type of part: {}'.format( part.get_content_type())) if self.DEBUGGING: self.log_message(part.get_payload()) else: report_bad_bundled_encrypted_message(to_domain, self.bundled_messages) return message
def add_dkim_sig_optionally(crypto_message): ''' Add DKIM signature if option selected. ''' if (options.add_dkim_sig() and options.dkim_public_key() is not None and len(options.dkim_public_key()) > 0): log_message('trying to add DKIM signature') try: global log SELECTOR = b'mail' DKIM_SIG = b'DKIM-Signature' PRIVATE_KEY_FILE = '/etc/opendkim/{}/dkim.private.key'.format( get_domain()) # in case there's a mixture of CR-LF and LF lines, convert CR-LF to LF and then all LFs to CR-LFs message = crypto_message.get_email_message().to_string().replace( constants.CRLF, constants.LF).replace(constants.LF, constants.CRLF) charset, __ = get_charset(crypto_message.get_email_message()) msg = bytes(message, charset) with open(PRIVATE_KEY_FILE, 'rb') as f: private_key = f.read() dkim = DKIM(message=msg, minkey=constants.MIN_DKIM_KEY, logger=log) # stop header injections of standard headers dkim.frozen_sign = set(DKIM.RFC5322_SINGLETON) sig = dkim.sign(SELECTOR, get_domain().encode(), private_key) if sig.startswith(DKIM_SIG): signed_message = '{}{}'.format(sig.decode(), message) crypto_message.get_email_message().set_message(signed_message) crypto_message.set_dkim_signed(True) crypto_message.set_dkim_sig_verified(True) log_message('added DKIM signature successfully') else: log_message('error trying to add DKIM signature') except ParameterError as pe: log_message(str(pe)) except: log_message('EXCEPTION - see syr.exception.log for details') record_exception() return crypto_message
def add_dkim_sig_optionally(crypto_message): ''' Add DKIM signature if option selected. ''' if (options.add_dkim_sig() and options.dkim_public_key() is not None and len(options.dkim_public_key()) > 0): log_message('trying to add DKIM signature') try: global log SELECTOR = b'mail' DKIM_SIG = b'DKIM-Signature' PRIVATE_KEY_FILE = '/etc/opendkim/{}/dkim.private.key'.format(get_domain()) # in case there's a mixture of CR-LF and LF lines, convert CR-LF to LF and then all LFs to CR-LFs message = crypto_message.get_email_message().to_string().replace( constants.CRLF, constants.LF).replace(constants.LF, constants.CRLF) charset, __ = get_charset(crypto_message.get_email_message()) msg = bytes(message, charset) with open(PRIVATE_KEY_FILE, 'rb') as f: private_key = f.read() dkim = DKIM(message=msg, minkey=constants.MIN_DKIM_KEY, logger=log) # stop header injections of standard headers dkim.frozen_sign = set(DKIM.RFC5322_SINGLETON) sig = dkim.sign(SELECTOR, get_domain().encode(), private_key) if sig.startswith(DKIM_SIG): signed_message = '{}{}'.format(sig.decode(), message) crypto_message.get_email_message().set_message(signed_message) crypto_message.set_dkim_signed(True) crypto_message.set_dkim_sig_verified(True) log_message('added DKIM signature successfully') else: log_message('error trying to add DKIM signature') except ParameterError as pe: log_message(str(pe)) except: log_message('EXCEPTION - see syr.exception.log for details') record_exception() return crypto_message
def get_domain_email(): ''' Get the email address for a domain key. >>> domain = get_domain_email() >>> domain == '*****@*****.**' True ''' return '{}@{}'.format(get_domain_user(), get_domain())
def get_message_id(): ''' Get a unique message id. ''' Chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-.' random_chars = '' for i in range(10): random_chars += choice(Chars) timestamp = time.strftime('%Y%m%d%H%M%S', time.gmtime()) message_id = '{}{}@{}'.format(random_chars, timestamp, get_domain()) return message_id
def report_metadata_key_creation_error(email): ''' Report that an error occurred creating a metadata key. >>> report_metadata_key_creation_error('*****@*****.**') ''' subject = i18n('GoodCrypto - Error while creating a private metadata key') body = '{}.\n{}'.format( subject, i18n("Metadata cannot be protected until you create a private key for {}@{}".format( DOMAIN_USER, get_domain()))) notify_user(email, subject, body)
def report_metadata_key_creation_error(email): ''' Report that an error occurred creating a metadata key. >>> report_metadata_key_creation_error('*****@*****.**') ''' subject = i18n('GoodCrypto - Error while creating a private metadata key') body = '{}.\n{}'.format( subject, i18n( "Metadata cannot be protected until you create a private key for {}@{}" .format(DOMAIN_USER, get_domain()))) notify_user(email, subject, body)
def clean(self): '''Verify there is only 1 general info record.''' cleaned_data = super(OptionsAdminForm, self).clean() # the mail_server_address should either be an ip address or a domain mail_server_address = cleaned_data.get('mail_server_address') if is_mta_ok(mail_server_address): _log.write('mail server address ok') self.cleaned_data['mail_server_address'] = mail_server_address else: del self.cleaned_data['mail_server_address'] _log.write('deleted mail server address from cleaned data') if mail_server_address is None or len( mail_server_address.strip()) <= 0: error_message = i18n( 'You need to define the mail server address (MTA).') else: error_message = i18n( 'The mail server address contains one or more bad characters or spaces.' ) _log.write(error_message) raise forms.ValidationError(error_message, code='invalid') encrypt_metadata = cleaned_data.get('encrypt_metadata') bundle_and_pad = cleaned_data.get('bundle_and_pad') if bundle_and_pad and not encrypt_metadata: del self.cleaned_data['encrypt_metadata'] _log.write('deleted encrypt_metadata from cleaned data') del self.cleaned_data['bundle_and_pad'] _log.write('deleted bundle_and_pad from cleaned data') error_message = i18n( 'You can only bundle and pad messages if you also encrypt metadata. Either add a check mark to "Encrypt metadata" or remove the check mark from "Bundle and pad".' ) _log.write(error_message) raise forms.ValidationError(error_message, code='invalid') add_dkim_sig = cleaned_data.get('add_dkim_sig') dkim_public_key = cleaned_data.get('dkim_public_key') if add_dkim_sig: if not dkim_public_key or len(dkim_public_key.strip()) <= 0: config_dkim.start(get_domain()) _log.write('starting to configure dkim') return cleaned_data
def add_private_key(email, encryption_software=None): ''' Add a private key if it doesn't exist. Creating a key takes minutes so a separate process handles it so no return code. >>> add_private_key(None) ''' try: # only add private keys for members of the domain if email_in_domain(email): if options.create_private_keys(): if encryption_software is None or len(encryption_software) <= 0: encryption_software = CryptoFactory.DEFAULT_ENCRYPTION_NAME user_key = user_keys.get(email, encryption_software) if user_key is None: contacts_crypto = contacts.get_contacts_crypto(email, encryption_name=encryption_software) if contacts_crypto is None: # a private user key will automatically be created # when the contact's crypto record is created after the contact is created contacts.add(email, encryption_software, source=AUTO_GENERATED) log_message('add {} key for {}'.format(encryption_software, email)) else: log_message('adding private {} user key for {}'.format(encryption_software, email)) sync_private_key_via_queue(contacts_crypto) elif user_key.contacts_encryption.fingerprint is None: log_message('setting private {} key fingerprint for {}'.format(encryption_software, email)) sync_fingerprint_via_queue(user_key.contacts_encryption) else: log_message('{} already has crypto software defined'.format(email)) else: log_message('creating private key disabled so no key created for {}'.format(email)) else: log_message('{} not a member of {}'.format(email, get_domain())) except Exception: record_exception() log_message('EXCEPTION - see syr.exception.log for details')
def main(argv): # set the defaults crypto_name = CryptoFactory.DEFAULT_ENCRYPTION_NAME # use the args passed on the command line if argv and len(argv) >= 1: pathname = argv[0] if len(argv) >= 2: crypto_name = argv[1] crypto = crypto_software.get(crypto_name) if crypto is None: print('{} encryption not defined in database'.format(crypto_name)) else: domain = get_domain() with open(pathname, 'rt') as f: lines = f.readlines() for line in lines: if len(line.strip()) > 0 and not line.startswith('#'): make_key(line, domain, crypto_name) else: print_usage()
def send_bundled_message(self, message, to_domain): ''' Send a Message to the domain. ''' try: if message is None: result_ok = False self.log_message('nothing to send to {}'.format(to_domain)) else: sender = get_email(get_metadata_address(domain=get_domain())) recipient = get_email(get_metadata_address(domain=to_domain)) self.log_message( 'starting to send message from {} to {}'.format( sender, recipient)) result_ok = send_message(sender, recipient, message.as_string()) self.log_message('finished sending message') except Exception as exception: result_ok = False self.log_message('error while sending message') self.log_message('EXCEPTION - see syr.exception.log for details') record_exception() return result_ok
def email_in_domain(email): ''' Determine if the email address has the supported domain. >>> # In honor of Sergeant First Class Amitai, who co-signed letter and refused to serve >>> # in operations involving the occupied Palestinian territories because >>> # of the widespread surveillance of innocent residents. >>> email_in_domain('*****@*****.**') True >>> email_in_domain('*****@*****.**') False ''' if email is None: result_ok = False else: domain = get_domain() address = get_email(email) if address is None or len(address) <= 0 or domain is None or len(domain) <= 0: result_ok = False else: result_ok = address.lower().endswith('@{}'.format(domain.lower())) return result_ok
def home(request): '''Show the home page.''' domain = get_domain() if domain is None or len(domain.strip()) <= 0: log_message('redirecting to system configuration') response = HttpResponseRedirect('/system/customize/') else: is_secure = is_secure_connection(request) params = { 'domain': domain, 'secure': is_secure, 'fingerprint_login_req': options.login_to_view_fingerprints(), 'export_login_req': options.login_to_export_keys(), 'use_keyservers': options.use_keyservers() } mta = options.mail_server_address() if mta is not None and len(mta.strip()) > 0: params['mta'] = mta template = 'mail/home.html' response = render_to_response(template, params, context_instance=RequestContext(request)) return response
def main(argv): # set the defaults crypto_name = CryptoFactory.DEFAULT_ENCRYPTION_NAME parent_dir = '/var/local/projects/goodcrypto/server/data/oce/pubkeys' # use the args passed on the command line if argv and len(argv) >= 1: parent_dir = argv[0] if len(argv) >= 2: crypto_name = argv[1] if not os.path.exists(parent_dir): os.makedirs(parent_dir) domain = get_domain() gpg_crypto = crypto_software.get(crypto_name) contacts = Contact.objects.filter(email__iendswith=domain) for contact in contacts: email = contact.email public_key = get_public_key(email, gpg_crypto) filename = os.path.join(parent_dir, email + '.asc') with open(filename, 'wt') as f: f.write(public_key)
from goodcrypto.mail.message.metadata import is_metadata_address from goodcrypto.mail.options import goodcrypto_server_url, require_key_verified from goodcrypto.mail.utils import get_admin_email, parse_domain, send_message, write_message from goodcrypto.mail.utils.dirs import get_notices_directory from goodcrypto.oce.utils import format_fingerprint from goodcrypto.utils import get_email, i18n from goodcrypto.utils.log_file import LogFile from syr.exception import record_exception from syr.message import prep_mime_message USE_SMTP = False # Notices From: address. NOTICE_FROM_NAME = 'GoodCrypto Private Server Daemon' NOTICE_FROM_EMAIL = 'mailer-daemon@{}'.format(get_domain()) NOTICE_FROM_ADDRESS = (NOTICE_FROM_NAME, NOTICE_FROM_EMAIL) NOTICE_FROM_ADDR = formataddr(NOTICE_FROM_ADDRESS) # shared details CREDENTIALS_SUBJECT = i18n('GoodCrypto - Save these credentials') VERIFY_HEADER = i18n('To verify a message was received privately:') _log = None def send_user_credentials(email, password): ''' Email the user with their credentials to access private website. >>> send_user_credentials('*****@*****.**', 'test-password')
from goodcrypto.mail.message.inspect_utils import get_hashcode from goodcrypto.mail.message.metadata import is_metadata_address from goodcrypto.mail.options import goodcrypto_server_url, require_key_verified from goodcrypto.mail.utils import get_admin_email, parse_domain, send_message, write_message from goodcrypto.mail.utils.dirs import get_notices_directory from goodcrypto.oce.utils import format_fingerprint from goodcrypto.utils import get_email, i18n from goodcrypto.utils.log_file import LogFile from syr.exception import record_exception from syr.message import prep_mime_message USE_SMTP = False # Notices From: address. NOTICE_FROM_NAME = 'GoodCrypto Private Server Daemon' NOTICE_FROM_EMAIL = 'mailer-daemon@{}'.format(get_domain()) NOTICE_FROM_ADDRESS = (NOTICE_FROM_NAME, NOTICE_FROM_EMAIL) NOTICE_FROM_ADDR = formataddr(NOTICE_FROM_ADDRESS) # shared details CREDENTIALS_SUBJECT = i18n('GoodCrypto - Save these credentials') VERIFY_HEADER = i18n('To verify a message was received privately:') _log = None def send_user_credentials(email, password): ''' Email the user with their credentials to access private website. >>> send_user_credentials('*****@*****.**', 'test-password')