Skip to content

Commit 88c0d9a

Browse files
committed
Implement openpgp.cert.d based keystore
Refactor code from the fs backend into shared helper functions This does implement the write lock for the openpgp.cert.d according to https://www.ietf.org/archive/id/draft-nwjw-openpgp-cert-d-00.htm but not the implement the Trust root, Petname mapping or Trusted introducers. Resolves: rpm-software-management#3341
1 parent 117e6b5 commit 88c0d9a

File tree

4 files changed

+240
-36
lines changed

4 files changed

+240
-36
lines changed

lib/keystore.cc

+142-36
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>
@@ -24,11 +25,11 @@ using namespace rpm;
2425

2526
static int makePubkeyHeader(rpmts ts, rpmPubkey key, Header * hdrp);
2627

27-
rpmRC keystore_fs::load_keys(rpmtxn txn, rpmKeyring keyring)
28+
static rpmRC load_keys_from_glob(rpmtxn txn, rpmKeyring keyring, string glob)
2829
{
2930
ARGV_t files = NULL;
3031
/* XXX TODO: deal with chroot path issues */
31-
char *pkpath = rpmGetPath(rpmtxnRootDir(txn), "%{_keyringpath}/*.key", NULL);
32+
char *pkpath = rpmGetPath(rpmtxnRootDir(txn), glob.c_str(), NULL);
3233

3334
rpmlog(RPMLOG_DEBUG, "loading keyring from pubkeys in %s\n", pkpath);
3435
if (rpmGlob(pkpath, NULL, &files)) {
@@ -55,6 +56,53 @@ rpmRC keystore_fs::load_keys(rpmtxn txn, rpmKeyring keyring)
5556
return RPMRC_OK;
5657
}
5758

59+
static rpmRC write_key_to_disk(rpmPubkey key, string & dir, string & filename, int replace, rpmFlags flags)
60+
{
61+
rpmRC rc = RPMRC_FAIL;
62+
char *keyval = rpmPubkeyArmorWrap(key);
63+
string tmppath = "";
64+
string path = dir + "/" + filename;
65+
66+
if (replace) {
67+
tmppath = dir + "/" + filename + ".new";
68+
unlink(tmppath.c_str());
69+
}
70+
71+
if (rpmMkdirs(NULL, dir.c_str())) {
72+
rpmlog(RPMLOG_ERR, _("failed to create keyring directory %s: %s\n"),
73+
path.c_str(), strerror(errno));
74+
goto exit;
75+
}
76+
77+
if (FD_t fd = Fopen(replace ? tmppath.c_str() : path.c_str(), "wx")) {
78+
size_t keylen = strlen(keyval);
79+
if (Fwrite(keyval, 1, keylen, fd) == keylen)
80+
rc = RPMRC_OK;
81+
Fclose(fd);
82+
}
83+
if (!rc && replace && rename(tmppath.c_str(), path.c_str()) != 0)
84+
rc = RPMRC_FAIL;
85+
86+
if (rc) {
87+
rpmlog(RPMLOG_ERR, _("failed to import key: %s: %s\n"),
88+
replace ? tmppath.c_str() : path.c_str(), strerror(errno));
89+
if (replace)
90+
unlink(tmppath.c_str());
91+
}
92+
93+
exit:
94+
free(keyval);
95+
return rc;
96+
}
97+
98+
99+
/*****************************************************************************/
100+
101+
rpmRC keystore_fs::load_keys(rpmtxn txn, rpmKeyring keyring)
102+
{
103+
return load_keys_from_glob(txn, keyring, "%{_keyringpath}/*.key");
104+
}
105+
58106
rpmRC keystore_fs::delete_key(rpmtxn txn, const string & keyid, const string & newname)
59107
{
60108
rpmRC rc = RPMRC_NOTFOUND;
@@ -82,55 +130,113 @@ rpmRC keystore_fs::delete_key(rpmtxn txn, rpmPubkey key)
82130
rpmRC keystore_fs::import_key(rpmtxn txn, rpmPubkey key, int replace, rpmFlags flags)
83131
{
84132
rpmRC rc = RPMRC_FAIL;
85-
const char *fp = rpmPubkeyFingerprintAsHex(key);
86-
char *keyfmt = rstrscat(NULL, "gpg-pubkey-", fp, ".key", NULL);
87-
char *keyval = rpmPubkeyArmorWrap(key);
88-
char *path = rpmGenPath(rpmtxnRootDir(txn), "%{_keyringpath}/", keyfmt);
89-
char *tmppath = NULL;
133+
string fp = rpmPubkeyFingerprintAsHex(key);
134+
string keyfmt = "gpg-pubkey-" + fp + ".key";
135+
char *dir = rpmGenPath(rpmtxnRootDir(txn), "%{_keyringpath}/", NULL);
136+
string dirstr = dir;
90137

91-
if (replace) {
92-
rasprintf(&tmppath, "%s.new", path);
93-
unlink(tmppath);
138+
rc = write_key_to_disk(key, dirstr, keyfmt, replace, flags);
139+
140+
if (!rc && replace) {
141+
/* find and delete the old pubkey entry */
142+
if (delete_key(txn, fp, keyfmt) == RPMRC_NOTFOUND) {
143+
/* make sure an old, short keyid version gets removed */
144+
delete_key(txn, fp.substr(32), keyfmt);
145+
}
94146
}
95147

96-
if (rpmMkdirs(rpmtxnRootDir(txn), "%{_keyringpath}")) {
148+
free(dir);
149+
return rc;
150+
}
151+
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)) {
97162
rpmlog(RPMLOG_ERR, _("failed to create keyring directory %s: %s\n"),
98-
path, strerror(errno));
163+
keyringpath, strerror(errno));
99164
goto exit;
100165
}
101166

102-
if (FD_t fd = Fopen(tmppath ? tmppath : path, "wx")) {
103-
size_t keylen = strlen(keyval);
104-
if (Fwrite(keyval, 1, keylen, fd) == keylen)
105-
rc = RPMRC_OK;
106-
Fclose(fd);
107-
}
108-
if (!rc && tmppath && rename(tmppath, path) != 0)
109-
rc = RPMRC_FAIL;
167+
fp = std::fopen(lockpath, "wx");
110168

111-
if (rc) {
112-
rpmlog(RPMLOG_ERR, _("failed to import key: %s: %s\n"),
113-
tmppath ? tmppath : path, strerror(errno));
114-
if (tmppath)
115-
unlink(tmppath);
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);
116174
}
117175

118-
if (!rc && replace) {
119-
/* find and delete the old pubkey entry */
120-
if (delete_key(txn, fp, keyfmt) == RPMRC_NOTFOUND) {
121-
/* make sure an old, short keyid version gets removed */
122-
delete_key(txn, fp+32, keyfmt);
123-
}
124-
}
176+
exit:
177+
free(keyringpath);
178+
free(lockpath);
179+
return rc;
180+
}
125181

126-
exit:
182+
static void free_write_lock(rpmtxn txn)
183+
{
184+
char * path = rpmGenPath(rpmtxnRootDir(txn), "%{_keyringpath}/", "writelock");
185+
unlink(path);
127186
free(path);
128-
free(keyval);
129-
free(keyfmt);
130-
free(tmppath);
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+
/* unlink directory if empty */
210+
unlink(dirpath);
211+
212+
free(filepath);
213+
free(dirpath);
214+
free_write_lock(txn);
131215
return rc;
132216
}
133217

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+
134240
rpmRC keystore_rpmdb::load_keys(rpmtxn txn, rpmKeyring keyring)
135241
{
136242
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)