Esempio n. 1
0
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
Esempio n. 2
0
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())
Esempio n. 3
0
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)
Esempio n. 4
0
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)
Esempio n. 5
0
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)