def _sigul_detached_sign_data(contents, config, key): # check is valid if not config: raise PluginError('No config file set') if not key: raise PluginError('No signing key set') # write firmware to temp file src = tempfile.NamedTemporaryFile(mode='wb', prefix='sigul_', suffix=".bin", dir=None, delete=True) src.write(contents) src.flush() # get asc file from temp file dst = tempfile.NamedTemporaryFile(mode='wb', prefix='sigul_', suffix=".asc", dir=None, delete=True) # sign argv = [ 'sigul', '--batch', '--config-file', config, 'sign-data', '--output', dst.name, key, src.name ] ps = subprocess.Popen(argv, stdout=subprocess.PIPE, stderr=subprocess.PIPE) if ps.wait() != 0: raise PluginError('Failed to sign: %s' % ps.stderr.read()) # read back the temp file return open(dst.name, 'r').read()
def file_modified(self, fn): # is the file in the whitelist fns = self.get_setting('cdn_purge_files', required=True) basename = os.path.basename(fn) if not _basename_matches_globs(basename, fns.split(',')): print('%s not in %s' % (basename, fns)) return # purge url = self.get_setting('cdn_purge_uri', required=True) + basename headers = {} accesskey = self.get_setting('cdn_purge_accesskey') if accesskey: headers['AccessKey'] = accesskey r = requests.request(self.get_setting('cdn_purge_method', required=True), url, headers=headers) if r.text: try: response = json.loads(r.text) if response['status'] != 'ok': raise PluginError('Failed to purge metadata on CDN: ' + r.text) except ValueError as e: # BunnyCDN doesn't sent a JSON blob raise PluginError('Failed to purge metadata on CDN: %s: %s' % (r.text, str(e)))
def run_test_on_md(self, test, md): # sanity check if not md.blob: return # build the remote name remote_path = '/' + md.fw.vendor.group_id + '/' + str( md.component_id) + '/' + md.filename_contents # upload the file try: headers = {} headers['X-Apikey'] = self.get_setting('virustotal_api_key', required=True) headers['User-Agent'] = self.get_setting('virustotal_user_agent', required=True) files = {'file': ('filepath', md.blob, 'application/octet-stream')} args = {'path': remote_path} r = requests.post(self.get_setting('virustotal_uri', required=True), files=files, data=args, headers=headers) if r.status_code != 200: test.add_fail('Failed to upload', r.text) return except IOError as e: raise PluginError(e)
def _sign_blob(self, contents): # write firmware to temp file src = tempfile.NamedTemporaryFile(mode='wb', prefix='pkcs7_', suffix=".bin", dir=None, delete=True) src.write(contents) src.flush() # get p7b file from temp file dst = tempfile.NamedTemporaryFile(mode='wb', prefix='pkcs7_', suffix=".p7b", dir=None, delete=True) # sign argv = [ 'certtool', '--p7-detached-sign', '--p7-time', '--load-privkey', self.get_setting('sign_pkcs7_privkey', required=True), '--load-certificate', self.get_setting('sign_pkcs7_certificate', required=True), '--infile', src.name, '--outfile', dst.name ] ps = subprocess.Popen(argv, stdout=subprocess.PIPE, stderr=subprocess.PIPE) if ps.wait() != 0: raise PluginError('Failed to sign: %s' % ps.stderr.read()) # read back the temp file with open(dst.name, 'rb') as f: return f.read()
def _get_shards_for_blob(self, blob): # write blob to temp file cwd = tempfile.TemporaryDirectory(prefix='lvfs') src = tempfile.NamedTemporaryFile(mode='wb', prefix='lvfs_', suffix=".bin", dir=cwd.name, delete=False) src.write(blob) src.flush() # run chipsec cmd = self.get_setting('chipsec_binary', required=True) argv = [cmd, '--no_driver', 'uefi', 'decode', src.name] ps = subprocess.Popen(argv, stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=cwd.name) if ps.wait() != 0: raise PluginError('Failed to decode file: %s' % ps.stderr.read()) # look for shards outdir = src.name + '.dir' files = glob.glob(outdir + '/FV/**/*.efi', recursive=True) files.extend(glob.glob(outdir + '/FV/**/*.pe32', recursive=True)) return self._convert_files_to_shards(files)
def _get_shards_for_blob(self, blob): # write blob to temp file cwd = tempfile.TemporaryDirectory(prefix='lvfs') src = tempfile.NamedTemporaryFile(mode='wb', prefix='lvfs_', suffix=".bin", dir=cwd.name, delete=False) src.write(blob) src.flush() # run chipsec cmd = self.get_setting('chipsec_binary', required=True) try: # pylint: disable=unexpected-keyword-arg subprocess.check_output( [cmd, '--no_driver', 'uefi', 'decode', src.name], stderr=subprocess.PIPE, cwd=cwd.name) except subprocess.CalledProcessError as e: raise PluginError('Failed to decode file: {}'.format(e.output)) # look for shards outdir = src.name + '.dir' files = glob.glob(outdir + '/FV/**/*.efi', recursive=True) files.extend(glob.glob(outdir + '/FV/**/*.pe32', recursive=True)) return self._convert_files_to_shards(files)
def _get_shards_for_blob(self, blob): # write blob to temp file cwd = tempfile.TemporaryDirectory(prefix='lvfs') src = tempfile.NamedTemporaryFile(mode='wb', prefix='lvfs_', suffix=".bin", dir=cwd.name, delete=False) src.write(blob) src.flush() # run UEFIExtract cmd = self.get_setting('uefi_extract_binary', required=True) try: # pylint: disable=unexpected-keyword-arg subprocess.check_output([cmd, src.name], stderr=subprocess.PIPE, cwd=cwd.name) except subprocess.CalledProcessError as e: raise PluginError('Failed to decode file') from e # look for shards files = glob.glob(src.name + '.dump' + '/**/info.txt', recursive=True) return self._convert_files_to_shards(files)
def verify(self, data): """ Verify that the data was signed by something we trust """ gpg = gnupg.GPG(gnupghome=self._homedir, gpgbinary='gpg2') gpg.encoding = 'utf-8' ver = gpg.verify(data) if not ver.valid: raise PluginError('Firmware was signed with an unknown private key') return True
def __init__(self, key_uid=None, homedir='/tmp'): """ Set defaults """ # check exists if not os.path.exists(homedir): try: os.mkdir(homedir) except OSError as e: raise PluginError(e) # find correct key ID for the UID self._keyid = None gpg = gnupg.GPG(gnupghome=homedir, gpgbinary='gpg2') gpg.encoding = 'utf-8' for privkey in gpg.list_keys(True): for uid in privkey['uids']: if uid.find(key_uid) != -1: self._keyid = privkey['keyid'] if not self._keyid: raise PluginError('No imported private key for %s' % key_uid) self._homedir = homedir
def oauth_get_data(self): # get response success try: oauth_response = remote_app.authorized_response() except OAuthException as e: raise PluginError(str(e)) if oauth_response is None: raise PluginError('Access Denied' + str(request)) # check response for correct GUID if str(session['auth_azure_state']) != str(request.args['state']): raise PluginError('State has been messed with, end authentication') # save the access token -- treat as a password session['auth_azure_token'] = (oauth_response['access_token'], '') # get the profile ID resp = remote_app.get('me') if not resp: raise PluginError('No suitable profile response') if not resp.data: raise PluginError('No suitable profile data') return resp.data
def archive_finalize(self, cabarchive, metadata): # does the readme file already exist? filename = self.get_setting('info_readme_filename', required=True) if filename in cabarchive: return # read in the file and do substititons try: with open(self.get_setting('info_readme_template', required=True), 'rb') as f: template = f.read().decode('utf-8') except IOError as e: raise PluginError(e) for key in metadata: template = template.replace(key, metadata[key]) # add it to the archive cabarchive[filename] = CabFile(template.encode('utf-8'))