Skip to content

Commit 11bf961

Browse files
committed
Implement openpgp.cert.d based keystore
This does implement the layout on the file system and the write lock of the openpgp.cert.d proposal according to https://www.ietf.org/archive/id/draft-nwjw-openpgp-cert-d-00.html but not the Trust root, Petname mapping or Trusted introducers. Resolves: rpm-software-management#3341
1 parent 5ee40a5 commit 11bf961

File tree

4 files changed

+187
-0
lines changed

4 files changed

+187
-0
lines changed

lib/keystore.cc

+89
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#include <string>
44

55
#include <fcntl.h>
6+
#include <unistd.h>
67

78
#include <rpm/header.h>
89
#include <rpm/rpmbase64.h>
@@ -148,6 +149,94 @@ rpmRC keystore_fs::import_key(rpmtxn txn, rpmPubkey key, int replace, rpmFlags f
148149
return rc;
149150
}
150151

152+
/*****************************************************************************/
153+
154+
static rpmRC acquire_write_lock(rpmtxn txn)
155+
{
156+
char * keyringpath = rpmGenPath(rpmtxnRootDir(txn), "%{_keyringpath}/", NULL);
157+
char * lockpath = rpmGenPath(rpmtxnRootDir(txn), "%{_keyringpath}/", "writelock");
158+
FILE* fp = NULL;
159+
rpmRC rc = RPMRC_FAIL;
160+
161+
if (rpmMkdirs(NULL, keyringpath)) {
162+
rpmlog(RPMLOG_ERR, _("failed to create keyring directory %s: %s\n"),
163+
keyringpath, strerror(errno));
164+
goto exit;
165+
}
166+
167+
fp = std::fopen(lockpath, "wx");
168+
169+
if (!fp) {
170+
rpmlog(RPMLOG_ERR, _("Can't acquire writelock for keyring at %s\n"), keyringpath);
171+
} else {
172+
rc = RPMRC_OK;
173+
std::fclose(fp);
174+
}
175+
176+
exit:
177+
free(keyringpath);
178+
free(lockpath);
179+
return rc;
180+
}
181+
182+
static void free_write_lock(rpmtxn txn)
183+
{
184+
char * path = rpmGenPath(rpmtxnRootDir(txn), "%{_keyringpath}/", "writelock");
185+
unlink(path);
186+
free(path);
187+
}
188+
189+
rpmRC keystore_openpgp_cert_d::load_keys(rpmtxn txn, rpmKeyring keyring)
190+
{
191+
return load_keys_from_glob(txn, keyring, "%{_keyringpath}/*/*");
192+
}
193+
194+
rpmRC keystore_openpgp_cert_d::delete_key(rpmtxn txn, rpmPubkey key)
195+
{
196+
rpmRC rc = RPMRC_NOTFOUND;
197+
198+
if (acquire_write_lock(txn) != RPMRC_OK)
199+
return RPMRC_FAIL;
200+
201+
string fp = rpmPubkeyFingerprintAsHex(key);
202+
string dir = fp.substr(0, 2);
203+
string filename = fp.substr(2);
204+
char * filepath = rpmGetPath(rpmtxnRootDir(txn), "%{_keyringpath}/", dir.c_str(), "/", filename.c_str(), NULL);
205+
char * dirpath = rpmGetPath(rpmtxnRootDir(txn), "%{_keyringpath}/", dir.c_str(), NULL);
206+
207+
if (!access(filepath, F_OK))
208+
rc = unlink(filepath) ? RPMRC_FAIL : RPMRC_OK;
209+
/* delete directory if empty */
210+
rmdir(dirpath);
211+
212+
free(filepath);
213+
free(dirpath);
214+
free_write_lock(txn);
215+
return rc;
216+
}
217+
218+
rpmRC keystore_openpgp_cert_d::import_key(rpmtxn txn, rpmPubkey key, int replace, rpmFlags flags)
219+
{
220+
rpmRC rc = RPMRC_NOTFOUND;
221+
222+
if ((rc = acquire_write_lock(txn)) == RPMRC_OK) {
223+
string fp = rpmPubkeyFingerprintAsHex(key);
224+
string dir = fp.substr(0, 2);
225+
string filename = fp.substr(2);
226+
char *dirpath = rpmGetPath(rpmtxnRootDir(txn), "%{_keyringpath}/", dir.c_str(), NULL);
227+
string dirstr = dirpath;
228+
229+
rc = write_key_to_disk(key, dirstr, filename, replace, flags);
230+
231+
free_write_lock(txn);
232+
free(dirpath);
233+
}
234+
235+
return rc;
236+
}
237+
238+
/*****************************************************************************/
239+
151240
rpmRC keystore_rpmdb::load_keys(rpmtxn txn, rpmKeyring keyring)
152241
{
153242
Header h;

lib/keystore.hh

+7
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,13 @@ private:
3737
rpmRC delete_key(rpmtxn txn, const std::string & keyid, unsigned int newinstance = 0);
3838
};
3939

40+
class keystore_openpgp_cert_d : public keystore {
41+
public:
42+
virtual rpmRC load_keys(rpmtxn txn, rpmKeyring keyring);
43+
virtual rpmRC import_key(rpmtxn txn, rpmPubkey key, int replace = 1, rpmFlags flags = 0);
44+
virtual rpmRC delete_key(rpmtxn txn, rpmPubkey key);
45+
};
46+
4047
}; /* namespace */
4148

4249
#endif /* _KEYSTORE_H */

lib/rpmts.cc

+2
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,8 @@ static keystore *getKeystore(rpmts ts)
275275
ts->keystore = new keystore_fs();
276276
} else if (rstreq(krtype, "rpmdb")) {
277277
ts->keystore = new keystore_rpmdb();
278+
} else if (rstreq(krtype, "openpgp")) {
279+
ts->keystore = new keystore_openpgp_cert_d();
278280
} else {
279281
/* Fall back to using rpmdb if unknown, for now at least */
280282
rpmlog(RPMLOG_WARNING,

tests/rpmsigdig.at

+89
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,95 @@ runroot rpmkeys -Kv /data/RPMS/hello-2.0-1.x86_64.rpm /data/RPMS/hello-1.0-1.i38
264264
RPMTEST_CLEANUP
265265

266266

267+
AT_SETUP([rpmkeys key update (openpgp)])
268+
AT_KEYWORDS([rpmkeys signature])
269+
RPMDB_INIT
270+
# root's .rpmmacros used to keep this build prefix independent
271+
echo "%_keyring openpgp" >> "${RPMTEST}"/root/.rpmmacros
272+
RPMTEST_CHECK([
273+
runroot rpmkeys --import /data/keys/rpm.org-rsa-2048-test.pub
274+
runroot rpmkeys -Kv /data/RPMS/hello-2.0-1.x86_64-signed-with-new-subkey.rpm
275+
],
276+
[1],
277+
[/data/RPMS/hello-2.0-1.x86_64-signed-with-new-subkey.rpm:
278+
Header V4 EdDSA/SHA512 signature, key ID 6323c42711450b6c: NOKEY
279+
Header SHA256 digest: OK
280+
Payload SHA256 digest: OK
281+
],
282+
[])
283+
284+
RPMTEST_CHECK([
285+
runroot_other touch /usr/lib/sysimage/rpm/pubkeys/writelock
286+
runroot rpmkeys --import /data/keys/rpm.org-rsa-2048-add-subkey.asc
287+
runroot_other rm /usr/lib/sysimage/rpm/pubkeys/writelock
288+
],
289+
[0],
290+
[],
291+
[error: Can't acquire writelock for keyring at /usr/lib/sysimage/rpm/pubkeys
292+
error: /data/keys/rpm.org-rsa-2048-add-subkey.asc: key 1 import failed.
293+
])
294+
295+
296+
RPMTEST_CHECK([
297+
runroot rpmkeys --list | wc -l
298+
runroot rpmkeys --import /data/keys/rpm.org-rsa-2048-add-subkey.asc
299+
runroot rpmkeys --list | wc -l
300+
],
301+
[0],
302+
[1
303+
1
304+
],
305+
[])
306+
307+
RPMTEST_CHECK([
308+
runroot rpmkeys -Kv /data/RPMS/hello-2.0-1.x86_64-signed-with-new-subkey.rpm
309+
],
310+
[0],
311+
[/data/RPMS/hello-2.0-1.x86_64-signed-with-new-subkey.rpm:
312+
Header V4 EdDSA/SHA512 signature, key fingerprint: 771b18d3d7baa28734333c424344591e1964c5fc: OK
313+
Header SHA256 digest: OK
314+
Payload SHA256 digest: OK
315+
],
316+
[])
317+
318+
RPMTEST_CHECK([
319+
runroot rpmkeys --delete abcd gimmekey 1111aaaa2222bbbb
320+
],
321+
[1],
322+
[],
323+
[error: invalid key id: abcd
324+
error: invalid key id: gimmekey
325+
error: key not found: 1111aaaa2222bbbb
326+
])
327+
328+
RPMTEST_CHECK([
329+
runroot rpmkeys --delete 1964c5fc
330+
],
331+
[0],
332+
[],
333+
[])
334+
335+
RPMTEST_CHECK([
336+
runroot rpmkeys --list | wc -l
337+
],
338+
[0],
339+
[0
340+
],
341+
[])
342+
RPMTEST_CLEANUP
343+
# ------------------------------
344+
# Test rpmkeys write errors
345+
AT_SETUP([[rpmkeys -K no space left on stdout]])
346+
AT_KEYWORDS([rpmkeys digest])
347+
RPMTEST_CHECK([
348+
RPMDB_INIT[
349+
350+
runroot rpmkeys -Kv /data/RPMS/hello-2.0-1.x86_64.rpm /data/RPMS/hello-1.0-1.i386.rpm >/dev/full
351+
]],255,,[[Error writing to log: No space left on device
352+
]])
353+
RPMTEST_CLEANUP
354+
355+
267356
AT_SETUP([rpmkeys -Kv <reconstructed> 1])
268357
AT_KEYWORDS([rpmkeys digest])
269358
RPMDB_INIT

0 commit comments

Comments
 (0)