def examine(self, suspect): if not DKIMPY_AVAILABLE: suspect.debug("dkimpy not available, can not check") suspect.set_tag('DKIMVerify.skipreason', 'dkimpy library not available') return DUNNO hdr_from_domain = extract_from_domain(suspect) if not hdr_from_domain: self.logger.debug( '%s DKIM Verification skipped, no header from address') suspect.set_tag("DKIMVerify.skipreason", 'no header from address') return DUNNO self.skiplist.filename = self.config.get(self.section, 'skiplist') skiplist = self.skiplist.get_list() if hdr_from_domain in skiplist: self.logger.debug( '%s DKIM Verification skipped, sender domain skiplisted') suspect.set_tag("DKIMVerify.skipreason", 'sender domain skiplisted') return DUNNO source = suspect.get_original_source() if "dkim-signature" not in suspect.get_message_rep(): suspect.set_tag('DKIMVerify.skipreason', 'not dkim signed') suspect.write_sa_temp_header('X-DKIMVerify', 'unsigned') suspect.debug("No dkim signature header found") return DUNNO # use the local logger of the plugin but prepend the fuglu id d = DKIM(source, logger=PrependLoggerMsg(self.logger, prepend=suspect.id, maxlevel=logging.INFO)) try: try: valid = d.verify() except DKIMException as de: self.logger.warning("%s: DKIM validation failed: %s" % (suspect.id, str(de))) valid = False suspect.set_tag("DKIMVerify.sigvalid", valid) suspect.write_sa_temp_header('X-DKIMVerify', 'valid' if valid else 'invalid') except NameError as ne: self.logger.warning( "%s: DKIM validation failed due to missing dependency: %s" % (suspect.id, str(ne))) suspect.set_tag('DKIMVerify.skipreason', 'plugin error') except Exception as e: self.logger.warning("%s: DKIM validation failed: %s" % (suspect.id, str(e))) suspect.set_tag('DKIMVerify.skipreason', 'plugin error') return DUNNO
def check_dkim(msg, dnsfunc=None): d = DKIM(msg) try: if(dnsfunc): res = d.verify(dnsfunc=dnsfunc) and 'pass' or 'fail' else: res = d.verify() and 'pass' or 'fail' except DKIMException as e: res = 'fail' header_i = d.signature_fields.get(b'i', b'').decode('ascii') header_d = d.signature_fields.get(b'd', b'').decode('ascii') return DKIMAuthenticationResult(result=res, header_d=header_d, header_i=header_i)
def sign_message(msg, selector, domain, privkey, sig_headers, sig='DKIM', srv_id=None, identity=None, length=None, canonicalize=(b'relaxed', b'relaxed'), timestamp=None, logger=None, standardize=False): """Sign an RFC822 message and return the ARC or DKIM header(s) @param msg: an RFC822 formatted message (with either \\n or \\r\\n line endings) @param selector: the DKIM selector value for the signature @param domain: the DKIM domain value for the signature @param privkey: a PKCS#1 private key in base64-encoded text form @param sig_headers: a list of strings indicating which headers are to be signed @param sig: "DKIM" or "ARC" @param srv_id: an authserv_id to identify AR headers to sign @param identity: (DKIM) the DKIM identity value for the signature (default "@"+domain) @param length: (DKIM) true if the l= tag should be included to indicate body length (default False) @param canonicalize: (DKIM) the canonicalization algorithms to use (default (Relaxed, Relaxed)) @param timestamp: (for testing) a manual timestamp to use for signature generation @param logger: An optional logger @param standardize: A testing flag for arc to output a standardized header format @return: The DKIM-Message-Signature, or ARC set headers @raises: DKIMException if mis-configured """ if sig=="DKIM": return DKIM(msg, logger=logger).sign(selector, domain, privkey, include_headers=sig_headers, identity=identity, length=length, canonicalize=canonicalize, timestamp=timestamp) else: return ARC(msg, logger=logger).sign(selector, domain, privkey, srv_id, include_headers=sig_headers, timestamp=timestamp, standardize=standardize)
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 examine(self, suspect): if not DKIMPY_AVAILABLE: suspect.debug("dkimpy not available, can not check") suspect.set_tag( 'DKIMVerify.skipreason', 'dkimpy library not available') return DUNNO source = suspect.get_original_source() if "dkim-signature: " not in suspect.get_headers().lower(): suspect.set_tag('DKIMVerify.skipreason', 'not dkim signed') suspect.debug("No dkim signature header found") return DUNNO d = DKIM(source, logger=suspect.get_tag('debugfile')) try: valid = d.verify(source) except DKIMException, de: self.logger.warning("%s: DKIM validation failed: %s" % (str(de))) valid = False
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 check_dkim(msg, dnsfunc=None): try: d = DKIM(msg) if (dnsfunc): res = d.verify(dnsfunc=dnsfunc) and 'pass' or 'fail' else: res = d.verify() and 'pass' or 'fail' # TODO: this could probably just be one line of: # `except Exception as e:` except DKIMException as e: res = 'fail' except DNSException as e: res = 'fail' except Exception as e: res = 'fail' header_i = d.signature_fields.get(b'i', b'').decode('ascii') header_d = d.signature_fields.get(b'd', b'').decode('ascii') return DKIMAuthenticationResult(result=res, header_d=header_d, header_i=header_i)
def examine(self, suspect): if not DKIMPY_AVAILABLE: suspect.debug("dkimpy not available, can not check") suspect.set_tag( 'DKIMVerify.skipreason', 'dkimpy library not available') return DUNNO source = suspect.get_original_source() if "dkim-signature: " not in suspect.get_headers().lower(): suspect.set_tag('DKIMVerify.skipreason', 'not dkim signed') suspect.debug("No dkim signature header found") return DUNNO d = DKIM(source, logger=suspect.get_tag('debugfile')) try: valid = d.verify() except DKIMException as de: self.logger.warning("%s: DKIM validation failed: %s" % (suspect.id, str(de))) valid = False suspect.set_tag("DKIMVerify.sigvalid", valid) return DUNNO
#!/usr/bin/env python3 # Command-line tool to use hardcoded public-key instead of DNS query # from https://gist.githubusercontent.com/stevecheckoway/51e63d4c269bd2be4a50a3b39645a77c/raw/4ed815490f4c619a5ba47dcd01d50aa32ee2cf55/verify.py # written by Stephen Checkoway @stevecheckoway # do 'pip install dkimpy' first to get 'dkim' dependency from dkim import DKIM import sys def get_txt(*args, **kwargs): return b'k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1Kd87/UeJjenpabgbFwh+eBCsSTrqmwIYYvywlbhbqoo2DymndFkbjOVIPIldNs/m40KF+yzMn1skyoxcTUGCQs8g3FgD2Ap3ZB5DekAo5wMmk4wimDO+U8QzI3SD0" "7y2+07wlNWwIt8svnxgdxGkVbbhzY8i+RQ9DpSVpPbF7ykQxtKXkv/ahW3KjViiAH+ghvvIhkx4xYSIc9oSwVmAl5OctMEeWUwg8Istjqz8BZeTWbf41fbNhte7Y+YqZOwq1Sd0DbvYAD9NOZK9vlfuac0598HY+vtSBczUiKERHv1yRbcaQtZFh5wtiRrN04BLUTD21MycBX5jYchHjPY/wIDAQAB' failed = False for path in sys.argv[1:]: with open(path, 'rb') as f: verifier = DKIM(message=f.read()) if verifier.verify(0, dnsfunc=get_txt): print("{}: DKIM signature verified".format(path)) else: print("{}: DKIM signature verification failed".format(path)) failed = True if failed: sys.exit(1)