rpm/rpmio/rpmkeyring.c

295 lines
6.8 KiB
C

#include "system.h"
#include <mutex>
#include <vector>
#include <shared_mutex>
#include <string>
#include <map>
#include <rpm/rpmstring.h>
#include <rpm/rpmpgp.h>
#include <rpm/rpmfileutil.h>
#include <rpm/rpmlog.h>
#include <rpm/rpmkeyring.h>
#include <rpm/rpmbase64.h>
#include "debug.h"
int _print_pkts = 0;
struct rpmPubkey_s {
std::vector<uint8_t> pkt;
std::string keyid;
pgpDigParams pgpkey;
int nrefs;
std::shared_mutex mutex;
};
using wrlock = std::unique_lock<std::shared_mutex>;
using rdlock = std::shared_lock<std::shared_mutex>;
struct rpmKeyring_s {
std::map<std::string,rpmPubkey> keys; /* pointers to keys */
int nrefs;
std::shared_mutex mutex;
};
static std::string key2str(const uint8_t *keyid)
{
return std::string(reinterpret_cast<const char *>(keyid), PGP_KEYID_LEN);
}
rpmKeyring rpmKeyringNew(void)
{
rpmKeyring keyring = new rpmKeyring_s {};
keyring->nrefs = 1;
return keyring;
}
rpmKeyring rpmKeyringFree(rpmKeyring keyring)
{
if (keyring == NULL)
return NULL;
wrlock lock(keyring->mutex);
if (--keyring->nrefs == 0) {
for (auto & item : keyring->keys)
rpmPubkeyFree(item.second);
delete keyring;
}
return NULL;
}
int rpmKeyringModify(rpmKeyring keyring, rpmPubkey key, rpmKeyringModifyMode mode)
{
int rc = 1; /* assume already seen key */
if (keyring == NULL || key == NULL)
return -1;
if (mode != RPMKEYRING_ADD && mode != RPMKEYRING_DELETE && mode != RPMKEYRING_REPLACE)
return -1;
/* check if we already have this key, but always wrlock for simplicity */
wrlock lock(keyring->mutex);
auto item = keyring->keys.find(key->keyid);
if (item != keyring->keys.end() && mode == RPMKEYRING_DELETE) {
rpmPubkeyFree(item->second);
keyring->keys.erase(item);
rc = 0;
} else if (item != keyring->keys.end() && mode == RPMKEYRING_REPLACE) {
rpmPubkeyFree(item->second);
item->second = rpmPubkeyLink(key);
rc = 0;
} else if (item == keyring->keys.end() && (mode == RPMKEYRING_ADD ||mode == RPMKEYRING_REPLACE) ) {
keyring->keys.insert({key->keyid, rpmPubkeyLink(key)});
rc = 0;
}
return rc;
}
int rpmKeyringAddKey(rpmKeyring keyring, rpmPubkey key)
{
return rpmKeyringModify(keyring, key, RPMKEYRING_ADD);
}
rpmPubkey rpmKeyringLookupKey(rpmKeyring keyring, rpmPubkey key)
{
if (keyring == NULL || key == NULL)
return NULL;
rdlock lock(keyring->mutex);
auto item = keyring->keys.find(key->keyid);
rpmPubkey rkey = item == keyring->keys.end() ? NULL : rpmPubkeyLink(item->second);
return rkey;
}
rpmKeyring rpmKeyringLink(rpmKeyring keyring)
{
if (keyring) {
wrlock lock(keyring->mutex);
keyring->nrefs++;
}
return keyring;
}
rpmPubkey rpmPubkeyRead(const char *filename)
{
uint8_t *pkt = NULL;
size_t pktlen;
rpmPubkey key = NULL;
if (pgpReadPkts(filename, &pkt, &pktlen) <= 0) {
goto exit;
}
key = rpmPubkeyNew(pkt, pktlen);
free(pkt);
exit:
return key;
}
rpmPubkey rpmPubkeyNew(const uint8_t *pkt, size_t pktlen)
{
rpmPubkey key = NULL;
pgpDigParams pgpkey = NULL;
pgpKeyID_t keyid;
if (pkt == NULL || pktlen == 0)
goto exit;
if (pgpPubkeyKeyID(pkt, pktlen, keyid))
goto exit;
if (pgpPrtParams(pkt, pktlen, PGPTAG_PUBLIC_KEY, &pgpkey))
goto exit;
key = new rpmPubkey_s {};
key->pkt.resize(pktlen);
key->pgpkey = pgpkey;
key->nrefs = 1;
memcpy(key->pkt.data(), pkt, pktlen);
key->keyid = key2str(keyid);
exit:
return key;
}
static rpmPubkey rpmPubkeyNewSubkey(pgpDigParams pgpkey)
{
rpmPubkey key = new rpmPubkey_s {};
/* Packets with all subkeys already stored in main key */
key->pgpkey = pgpkey;
key->keyid = key2str(pgpDigParamsSignID(pgpkey));
key->nrefs = 1;
return key;
}
rpmPubkey *rpmGetSubkeys(rpmPubkey mainkey, int *count)
{
rpmPubkey *subkeys = NULL;
pgpDigParams *pgpsubkeys = NULL;
int pgpsubkeysCount = 0;
int i;
if (mainkey && !pgpPrtParamsSubkeys(mainkey->pkt.data(), mainkey->pkt.size(),
mainkey->pgpkey, &pgpsubkeys, &pgpsubkeysCount)) {
/* Returned to C, can't use new */
subkeys = (rpmPubkey *)xmalloc(pgpsubkeysCount * sizeof(*subkeys));
for (i = 0; i < pgpsubkeysCount; i++)
subkeys[i] = rpmPubkeyNewSubkey(pgpsubkeys[i]);
free(pgpsubkeys);
}
*count = pgpsubkeysCount;
return subkeys;
}
rpmPubkey rpmPubkeyFree(rpmPubkey key)
{
if (key == NULL)
return NULL;
wrlock lock(key->mutex);
if (--key->nrefs == 0) {
pgpDigParamsFree(key->pgpkey);
delete key;
}
return NULL;
}
rpmPubkey rpmPubkeyLink(rpmPubkey key)
{
if (key) {
wrlock lock(key->mutex);
key->nrefs++;
}
return key;
}
char * rpmPubkeyBase64(rpmPubkey key)
{
char *enc = NULL;
if (key) {
rdlock lock(key->mutex);
enc = rpmBase64Encode(key->pkt.data(), key->pkt.size(), -1);
}
return enc;
}
rpmRC rpmPubkeyMerge(rpmPubkey oldkey, rpmPubkey newkey, rpmPubkey *mergedkeyp)
{
rpmPubkey mergedkey = NULL;
uint8_t *mergedpkt = NULL;
size_t mergedpktlen = 0;
rpmRC rc;
rdlock olock(oldkey->mutex);
rdlock nlock(newkey->mutex);
rc = pgpPubkeyMerge(oldkey->pkt.data(), oldkey->pkt.size(), newkey->pkt.data(), newkey->pkt.size(), &mergedpkt, &mergedpktlen, 0);
if (rc == RPMRC_OK && (mergedpktlen != oldkey->pkt.size() || memcmp(mergedpkt, oldkey->pkt.data(), mergedpktlen) != 0)) {
mergedkey = rpmPubkeyNew(mergedpkt, mergedpktlen);
if (!mergedkey)
rc = RPMRC_FAIL;
}
*mergedkeyp = mergedkey;
free(mergedpkt);
return rc;
}
pgpDigParams rpmPubkeyPgpDigParams(rpmPubkey key)
{
pgpDigParams params= NULL;
if (key) {
params = key->pgpkey;
}
return params;
}
static rpmPubkey findbySig(rpmKeyring keyring, pgpDigParams sig)
{
rpmPubkey key = NULL;
if (keyring && sig) {
rdlock lock(keyring->mutex);
auto keyid = key2str(pgpDigParamsSignID(sig));
auto item = keyring->keys.find(keyid);
if (item != keyring->keys.end()) {
key = item->second;
pgpDigParams pub = key->pgpkey;
/* Do the parameters match the signature? */
if ((pgpDigParamsAlgo(sig, PGPVAL_PUBKEYALGO)
!= pgpDigParamsAlgo(pub, PGPVAL_PUBKEYALGO)) ||
memcmp(pgpDigParamsSignID(sig), pgpDigParamsSignID(pub),
PGP_KEYID_LEN)) {
key = NULL;
}
}
}
return key;
}
rpmRC rpmKeyringVerifySig(rpmKeyring keyring, pgpDigParams sig, DIGEST_CTX ctx)
{
rpmRC rc = RPMRC_FAIL;
if (sig && ctx) {
pgpDigParams pgpkey = NULL;
rpmPubkey key = findbySig(keyring, sig);
if (key)
pgpkey = key->pgpkey;
/* We call verify even if key not found for a signature sanity check */
char *lints = NULL;
rc = pgpVerifySignature2(pgpkey, sig, ctx, &lints);
if (lints) {
rpmlog(rc ? RPMLOG_ERR : RPMLOG_WARNING, "%s\n", lints);
free(lints);
}
}
return rc;
}