summaryrefslogtreecommitdiffstats
path: root/meta/lib
diff options
context:
space:
mode:
authorMarkus Lehtonen <markus.lehtonen@linux.intel.com>2016-01-25 14:21:34 +0200
committerRichard Purdie <richard.purdie@linuxfoundation.org>2016-01-26 22:31:58 +0000
commitbb971577ab308caf7177d4bda290d1fe5ab842db (patch)
tree49c1811106a9b58717dcfd3c6fe4e4810341c1b3 /meta/lib
parentaadb879e5b302e405e05443f56611c17868d10b6 (diff)
downloadpoky-bb971577ab308caf7177d4bda290d1fe5ab842db.tar.gz
meta/lib: new module for handling GPG signing
Add a new Python module (oe.gpg_sign) for handling GPG signing operations, i.e. currently package and package feed signing. The purpose is to be able to more easily support various signing backends and to be able to centralise signing functionality into one place (e.g. package signing and sstate signing). Currently, only local signing with gpg is implemented. [YOCTO #8755] (From OE-Core rev: 9b3dc1bd4b8336423a3f8f7db0ab5fa6fa0e7257) Signed-off-by: Markus Lehtonen <markus.lehtonen@linux.intel.com> Signed-off-by: Ross Burton <ross.burton@intel.com> Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
Diffstat (limited to 'meta/lib')
-rw-r--r--meta/lib/oe/gpg_sign.py76
-rw-r--r--meta/lib/oe/package_manager.py31
2 files changed, 87 insertions, 20 deletions
diff --git a/meta/lib/oe/gpg_sign.py b/meta/lib/oe/gpg_sign.py
new file mode 100644
index 0000000000..55abad8ffc
--- /dev/null
+++ b/meta/lib/oe/gpg_sign.py
@@ -0,0 +1,76 @@
1"""Helper module for GPG signing"""
2import os
3
4import bb
5import oe.utils
6
7class LocalSigner(object):
8 """Class for handling local (on the build host) signing"""
9 def __init__(self, d, keyid, passphrase_file):
10 self.keyid = keyid
11 self.passphrase_file = passphrase_file
12 self.gpg_bin = d.getVar('GPG_BIN', True) or \
13 bb.utils.which(os.getenv('PATH'), 'gpg')
14 self.gpg_path = d.getVar('GPG_PATH', True)
15 self.rpm_bin = bb.utils.which(os.getenv('PATH'), "rpm")
16
17 def export_pubkey(self, output_file):
18 """Export GPG public key to a file"""
19 cmd = '%s --batch --yes --export --armor -o %s ' % \
20 (self.gpg_bin, output_file)
21 if self.gpg_path:
22 cmd += "--homedir %s " % self.gpg_path
23 cmd += self.keyid
24 status, output = oe.utils.getstatusoutput(cmd)
25 if status:
26 raise bb.build.FuncFailed('Failed to export gpg public key (%s): %s' %
27 (self.keyid, output))
28
29 def sign_rpms(self, files):
30 """Sign RPM files"""
31 import pexpect
32
33 cmd = self.rpm_bin + " --addsign --define '_gpg_name %s' " % self.keyid
34 if self.gpg_bin:
35 cmd += "--define '%%__gpg %s' " % self.gpg_bin
36 if self.gpg_path:
37 cmd += "--define '_gpg_path %s' " % self.gpg_path
38 cmd += ' '.join(files)
39
40 # Need to use pexpect for feeding the passphrase
41 proc = pexpect.spawn(cmd)
42 try:
43 proc.expect_exact('Enter pass phrase:', timeout=15)
44 with open(self.passphrase_file) as fobj:
45 proc.sendline(fobj.readline().rstrip('\n'))
46 proc.expect(pexpect.EOF, timeout=900)
47 proc.close()
48 except pexpect.TIMEOUT as err:
49 bb.error('rpmsign timeout: %s' % err)
50 proc.terminate()
51 if os.WEXITSTATUS(proc.status) or not os.WIFEXITED(proc.status):
52 bb.error('rpmsign failed: %s' % proc.before.strip())
53 raise bb.build.FuncFailed("Failed to sign RPM packages")
54
55 def detach_sign(self, input_file):
56 """Create a detached signature of a file"""
57 cmd = "%s --detach-sign --armor --batch --no-tty --yes " \
58 "--passphrase-file '%s' -u '%s' " % \
59 (self.gpg_bin, self.passphrase_file, self.keyid)
60 if self.gpg_path:
61 gpg_cmd += "--homedir %s " % self.gpg_path
62 cmd += input_file
63 status, output = oe.utils.getstatusoutput(cmd)
64 if status:
65 raise bb.build.FuncFailed("Failed to create signature for '%s': %s" %
66 (input_file, output))
67
68
69def get_signer(d, backend, keyid, passphrase_file):
70 """Get signer object for the specified backend"""
71 # Use local signing by default
72 if backend == 'local':
73 return LocalSigner(d, keyid, passphrase_file)
74 else:
75 bb.fatal("Unsupported signing backend '%s'" % backend)
76
diff --git a/meta/lib/oe/package_manager.py b/meta/lib/oe/package_manager.py
index 5b87f45127..3f9e4e3b60 100644
--- a/meta/lib/oe/package_manager.py
+++ b/meta/lib/oe/package_manager.py
@@ -9,6 +9,7 @@ import bb
9import tempfile 9import tempfile
10import oe.utils 10import oe.utils
11import string 11import string
12from oe.gpg_sign import get_signer
12 13
13# this can be used by all PM backends to create the index files in parallel 14# this can be used by all PM backends to create the index files in parallel
14def create_index(arg): 15def create_index(arg):
@@ -109,16 +110,14 @@ class RpmIndexer(Indexer):
109 110
110 rpm_createrepo = bb.utils.which(os.getenv('PATH'), "createrepo") 111 rpm_createrepo = bb.utils.which(os.getenv('PATH'), "createrepo")
111 if self.d.getVar('PACKAGE_FEED_SIGN', True) == '1': 112 if self.d.getVar('PACKAGE_FEED_SIGN', True) == '1':
112 pkgfeed_gpg_name = self.d.getVar('PACKAGE_FEED_GPG_NAME', True) 113 signer = get_signer(self.d,
113 pkgfeed_gpg_pass = self.d.getVar('PACKAGE_FEED_GPG_PASSPHRASE_FILE', True) 114 self.d.getVar('PACKAGE_FEED_GPG_BACKEND', True),
115 self.d.getVar('PACKAGE_FEED_GPG_NAME', True),
116 self.d.getVar('PACKAGE_FEED_GPG_PASSPHRASE_FILE', True))
114 else: 117 else:
115 pkgfeed_gpg_name = None 118 signer = None
116 pkgfeed_gpg_pass = None
117 gpg_bin = self.d.getVar('GPG_BIN', True) or \
118 bb.utils.which(os.getenv('PATH'), "gpg")
119
120 index_cmds = [] 119 index_cmds = []
121 repo_sign_cmds = [] 120 repomd_files = []
122 rpm_dirs_found = False 121 rpm_dirs_found = False
123 for arch in archs: 122 for arch in archs:
124 dbpath = os.path.join(self.d.getVar('WORKDIR', True), 'rpmdb', arch) 123 dbpath = os.path.join(self.d.getVar('WORKDIR', True), 'rpmdb', arch)
@@ -130,15 +129,7 @@ class RpmIndexer(Indexer):
130 129
131 index_cmds.append("%s --dbpath %s --update -q %s" % \ 130 index_cmds.append("%s --dbpath %s --update -q %s" % \
132 (rpm_createrepo, dbpath, arch_dir)) 131 (rpm_createrepo, dbpath, arch_dir))
133 if pkgfeed_gpg_name: 132 repomd_files.append(os.path.join(arch_dir, 'repodata', 'repomd.xml'))
134 repomd_file = os.path.join(arch_dir, 'repodata', 'repomd.xml')
135 gpg_cmd = "%s --detach-sign --armor --batch --no-tty --yes " \
136 "--passphrase-file '%s' -u '%s' " % \
137 (gpg_bin, pkgfeed_gpg_pass, pkgfeed_gpg_name)
138 if self.d.getVar('GPG_PATH', True):
139 gpg_cmd += "--homedir %s " % self.d.getVar('GPG_PATH', True)
140 gpg_cmd += repomd_file
141 repo_sign_cmds.append(gpg_cmd)
142 133
143 rpm_dirs_found = True 134 rpm_dirs_found = True
144 135
@@ -151,9 +142,9 @@ class RpmIndexer(Indexer):
151 if result: 142 if result:
152 bb.fatal('%s' % ('\n'.join(result))) 143 bb.fatal('%s' % ('\n'.join(result)))
153 # Sign repomd 144 # Sign repomd
154 result = oe.utils.multiprocess_exec(repo_sign_cmds, create_index) 145 if signer:
155 if result: 146 for repomd in repomd_files:
156 bb.fatal('%s' % ('\n'.join(result))) 147 signer.detach_sign(repomd)
157 # Copy pubkey(s) to repo 148 # Copy pubkey(s) to repo
158 distro_version = self.d.getVar('DISTRO_VERSION', True) or "oe.0" 149 distro_version = self.d.getVar('DISTRO_VERSION', True) or "oe.0"
159 if self.d.getVar('RPM_SIGN_PACKAGES', True) == '1': 150 if self.d.getVar('RPM_SIGN_PACKAGES', True) == '1':