def parse_pr_data(msg): lmsg = b4.LoreMessage(msg) if lmsg.body is None: logger.critical('Could not find a plain part in the message body') return None logger.info('Looking at: %s', lmsg.full_subject) for since_re in PULL_BODY_SINCE_ID_RE: matches = since_re.search(lmsg.body) if matches: lmsg.pr_base_commit = matches.groups()[0] break for reporef_re in PULL_BODY_REMOTE_REF_RE: matches = reporef_re.search(lmsg.body) if matches: chunks = matches.groups() lmsg.pr_repo = chunks[0] if len(chunks) > 1: lmsg.pr_ref = chunks[1] else: lmsg.pr_ref = 'refs/heads/master' break for cid_re in PULL_BODY_WITH_COMMIT_ID_RE: matches = cid_re.search(lmsg.body) if matches: lmsg.pr_tip_commit = matches.groups()[0] break if lmsg.pr_repo and lmsg.pr_ref: lmsg.pr_remote_tip_commit = git_get_commit_id_from_repo_ref(lmsg.pr_repo, lmsg.pr_ref) return lmsg
def attest_patches(cmdargs: argparse.Namespace) -> None: for pf in cmdargs.patchfile: with open(pf, 'rb') as fh: msg = email.message_from_bytes(fh.read()) lmsg = b4.LoreMessage(msg) lmsg.load_hashes() if not lmsg.attestation: logger.debug('Nothing to attest in %s, skipped') continue logger.info('Attesting: %s', pf) in_header_attest(lmsg, replace=True) with open(pf, 'wb') as fh: fh.write(lmsg.msg.as_bytes())
def attest_and_send(cmdargs: argparse.Namespace): # Grab the message from stdin as bytes if sys.stdin.isatty(): logger.critical('Pass the message to attest as stdin') sys.exit(1) inbytes = sys.stdin.buffer.read() msg = email.message_from_bytes(inbytes) lmsg = b4.LoreMessage(msg) lmsg.load_hashes() if not lmsg.attestation: logger.debug('Nothing to attest in %s, sending as-is') outbytes = inbytes else: in_header_attest(lmsg) outbytes = lmsg.msg.as_bytes() if cmdargs.nosend: logger.info('--- MESSAGE FOLLOWS ---') sys.stdout.buffer.write(outbytes) return if cmdargs.identity: cfgname = f'sendemail\\.{cmdargs.identity}\\..*' else: cfgname = 'sendemail\\..*' scfg = b4.get_config_from_git(cfgname) sserver = scfg.get('smtpserver') if not sserver: logger.critical('MISSING: smtpserver option in %s', cfgname) sys.exit(1) if sserver[0] == '/': args = [sserver, '-i'] + cmdargs.recipients extraopts = scfg.get('smtpserveroption') if extraopts: args += extraopts.split() ecode, out, err = b4._run_command(args, outbytes) # noqa sys.stdout.buffer.write(out) sys.stderr.buffer.write(err) sys.exit(ecode) sport = int(scfg.get('smtpserverport', '0')) sdomain = scfg.get('smtpdomain') suser = scfg.get('smtpuser') spass = scfg.get('smtppass') senc = scfg.get('smtpencryption', 'tls') sfrom = scfg.get('from') if not sfrom: sfrom = lmsg.fromemail logger.info('Connecting to %s', sserver) if senc == 'ssl': sconn = smtplib.SMTP_SSL(host=sserver, port=sport, local_hostname=sdomain) else: sconn = smtplib.SMTP(host=sserver, port=sport, local_hostname=sdomain) if senc == 'tls': sconn.starttls() if suser: logger.info('Logging in as user %s', suser) sconn.login(suser, spass) logger.info('Sending %s', lmsg.full_subject) sconn.sendmail(sfrom, cmdargs.recipients, outbytes)
def create_attestation(cmdargs): attlines = list() subject = 'Patch attestation' for patchfile in cmdargs.patchfile: with open(patchfile, 'r', encoding='utf-8') as fh: content = fh.read() if content.find('From') != 0: logger.info('SKIP | %s', os.path.basename(patchfile)) continue msg = email.message_from_string(content) lmsg = b4.LoreMessage(msg) lmsg.load_hashes() att = lmsg.attestation if att is None: logger.info('SKIP | %s', os.path.basename(patchfile)) # See if it's a cover letter if lmsg.counters_inferred or lmsg.counter > 0: # No continue newprefs = list() for prefix in lmsg.lsubject.prefixes: if prefix.lower() == 'patch': newprefs.append('PSIGN') elif prefix == '%s/%s' % (lmsg.counter, lmsg.expected): newprefs.append('X/%s' % lmsg.expected) else: newprefs.append(prefix) subject = '[%s] %s' % (' '.join(newprefs), lmsg.subject) continue logger.info('HASH | %s', os.path.basename(patchfile)) attlines.append('%s:' % att.attid) attlines.append(' i: %s' % att.i) attlines.append(' m: %s' % att.m) attlines.append(' p: %s' % att.p) payload = '\n'.join(attlines) usercfg = b4.get_user_config() gpgargs = list() if 'signingkey' in usercfg: gpgargs += ['-u', usercfg['signingkey']] gpgargs += ['--clearsign', '--comment', 'att-fmt-ver: %s' % b4.ATTESTATION_FORMAT_VER, '--comment', 'att-hash: sha256', ] ecode, signed = b4.gpg_run_command(gpgargs, stdin=payload.encode('utf-8')) if ecode > 0: config = b4.get_main_config() logger.critical('ERROR: Unable to sign using %s', config['gpgbin']) sys.exit(1) att_msg = email.message.EmailMessage() att_msg.set_payload(signed.encode('utf-8')) sender = cmdargs.sender if '>' not in sender: sender = '<%s>' % sender att_msg['From'] = sender att_msg['To'] = '<*****@*****.**>' att_msg['Message-Id'] = email.utils.make_msgid(domain='kernel.org') att_msg['Subject'] = subject logger.info('---') if not cmdargs.nosubmit: # Try to deliver it via mail.kernel.org try: mailserver = smtplib.SMTP('mail.kernel.org', 587) # identify ourselves to smtp gmail client mailserver.ehlo() # secure our email with tls encryption mailserver.starttls() # re-identify ourselves as an encrypted connection mailserver.ehlo() logger.info('Delivering via mail.kernel.org') mailserver.sendmail('*****@*****.**', '*****@*****.**', att_msg.as_string()) mailserver.quit() sys.exit(0) except Exception as ex: logger.info('Could not deliver: %s', ex) # Future iterations will also be able to submit this to a RESTful URL # at git.kernel.org, in order not to depend on avaialbility of SMTP gateways with open(cmdargs.output, 'wb') as fh: fh.write(att_msg.as_bytes()) logger.info('Wrote %s', cmdargs.output) logger.info('You can send it using:') logger.info(' sendmail -oi [email protected] < %s', cmdargs.output) logger.info(' mutt -H %s', cmdargs.output)
def verify_attestation(cmdargs): config = b4.get_main_config() if cmdargs.tofu: config['attestation-trust-model'] = 'tofu' exact_from_match = True if cmdargs.ignorefrom: exact_from_match = False mbx = mailbox.mbox(cmdargs.mbox[0]) if cmdargs.attfile: b4.LoreAttestationDocument.load_from_file(cmdargs.attfile) eligible = list() for msg in mbx: lmsg = b4.LoreMessage(msg) if lmsg.has_diff: eligible.append(lmsg) continue # See if body has "att-fmt-ver if re.search(r'^Comment: att-fmt-ver:', lmsg.body, re.I | re.M): logger.debug('Found attestation message') b4.LoreAttestationDocument.load_from_string(lmsg.msgid, lmsg.body) logger.debug('SKIP | %s', msg['Subject']) if not len(eligible): logger.error('No patches found in %s', cmdargs.mbox[0]) sys.exit(1) logger.info('---') attrailers = set() ecode = 1 if config['attestation-checkmarks'] == 'fancy': attpass = b4.PASS_FANCY attfail = b4.FAIL_FANCY else: attpass = b4.PASS_SIMPLE attfail = b4.FAIL_SIMPLE for lmsg in eligible: attdoc = lmsg.get_attestation(lore_lookup=True, exact_from_match=exact_from_match) if not attdoc: logger.critical('%s %s', attfail, lmsg.full_subject) if not cmdargs.nofast: logger.critical('Aborting due to failure.') ecode = 1 break else: ecode = 128 continue if ecode != 128: ecode = 0 logger.critical('%s %s', attpass, lmsg.full_subject) attrailers.add(attdoc.attestor.get_trailer(lmsg.fromemail)) logger.critical('---') if ecode > 0: logger.critical('Attestation verification failed.') errors = set() for attdoc in b4.ATTESTATIONS: errors.update(attdoc.errors) if len(errors): logger.critical('---') logger.critical('The validation process reported the following errors:') for error in errors: logger.critical(' %s %s', attfail, error) else: logger.critical('All patches passed attestation:') for attrailer in attrailers: logger.critical(' %s %s', attpass, attrailer) sys.exit(ecode)