|
|
2ff057 |
/** \ingroup header
|
|
|
2ff057 |
* \file lib/package.c
|
|
|
2ff057 |
*/
|
|
|
2ff057 |
|
|
|
2ff057 |
#include "system.h"
|
|
|
2ff057 |
|
|
|
2ff057 |
#include <netinet/in.h>
|
|
|
2ff057 |
#include <pthread.h>
|
|
|
2ff057 |
|
|
|
2ff057 |
#include <rpm/rpmlib.h> /* XXX RPMSIGTAG, other sig stuff */
|
|
|
2ff057 |
#include <rpm/rpmts.h>
|
|
|
2ff057 |
#include <rpm/rpmlog.h>
|
|
|
2ff057 |
#include <rpm/rpmstring.h>
|
|
|
2ff057 |
#include <rpm/rpmkeyring.h>
|
|
|
2ff057 |
|
|
|
2ff057 |
#include "lib/rpmlead.h"
|
|
|
2ff057 |
#include "rpmio/rpmio_internal.h" /* fd digest bits */
|
|
|
2ff057 |
#include "lib/header_internal.h" /* XXX headerCheck */
|
|
|
2ff057 |
#include "lib/rpmvs.h"
|
|
|
2ff057 |
|
|
|
2ff057 |
#include "debug.h"
|
|
|
2ff057 |
|
|
|
2ff057 |
/** \ingroup header
|
|
|
2ff057 |
* Translate and merge legacy signature tags into header.
|
|
|
2ff057 |
* @param h header (dest)
|
|
|
2ff057 |
* @param sigh signature header (src)
|
|
|
2ff057 |
*/
|
|
|
2ff057 |
static
|
|
|
2ff057 |
void headerMergeLegacySigs(Header h, Header sigh)
|
|
|
2ff057 |
{
|
|
|
2ff057 |
HeaderIterator hi;
|
|
|
2ff057 |
struct rpmtd_s td;
|
|
|
2ff057 |
|
|
|
2ff057 |
hi = headerInitIterator(sigh);
|
|
|
2ff057 |
for (; headerNext(hi, &td); rpmtdFreeData(&td))
|
|
|
2ff057 |
{
|
|
|
2ff057 |
switch (td.tag) {
|
|
|
2ff057 |
/* XXX Translate legacy signature tag values. */
|
|
|
2ff057 |
case RPMSIGTAG_SIZE:
|
|
|
2ff057 |
td.tag = RPMTAG_SIGSIZE;
|
|
|
2ff057 |
break;
|
|
|
2ff057 |
case RPMSIGTAG_PGP:
|
|
|
2ff057 |
td.tag = RPMTAG_SIGPGP;
|
|
|
2ff057 |
break;
|
|
|
2ff057 |
case RPMSIGTAG_MD5:
|
|
|
2ff057 |
td.tag = RPMTAG_SIGMD5;
|
|
|
2ff057 |
break;
|
|
|
2ff057 |
case RPMSIGTAG_GPG:
|
|
|
2ff057 |
td.tag = RPMTAG_SIGGPG;
|
|
|
2ff057 |
break;
|
|
|
2ff057 |
case RPMSIGTAG_PGP5:
|
|
|
2ff057 |
td.tag = RPMTAG_SIGPGP5;
|
|
|
2ff057 |
break;
|
|
|
2ff057 |
case RPMSIGTAG_PAYLOADSIZE:
|
|
|
2ff057 |
td.tag = RPMTAG_ARCHIVESIZE;
|
|
|
2ff057 |
break;
|
|
|
2ff057 |
case RPMSIGTAG_SHA1:
|
|
|
2ff057 |
case RPMSIGTAG_SHA256:
|
|
|
2ff057 |
case RPMSIGTAG_DSA:
|
|
|
2ff057 |
case RPMSIGTAG_RSA:
|
|
|
2ff057 |
default:
|
|
|
2ff057 |
if (!(td.tag >= HEADER_SIGBASE && td.tag < HEADER_TAGBASE))
|
|
|
2ff057 |
continue;
|
|
|
2ff057 |
break;
|
|
|
2ff057 |
}
|
|
|
2ff057 |
if (!headerIsEntry(h, td.tag)) {
|
|
|
2ff057 |
switch (td.type) {
|
|
|
2ff057 |
case RPM_NULL_TYPE:
|
|
|
2ff057 |
continue;
|
|
|
2ff057 |
break;
|
|
|
2ff057 |
case RPM_CHAR_TYPE:
|
|
|
2ff057 |
case RPM_INT8_TYPE:
|
|
|
2ff057 |
case RPM_INT16_TYPE:
|
|
|
2ff057 |
case RPM_INT32_TYPE:
|
|
|
2ff057 |
case RPM_INT64_TYPE:
|
|
|
2ff057 |
if (td.count != 1)
|
|
|
2ff057 |
continue;
|
|
|
2ff057 |
break;
|
|
|
2ff057 |
case RPM_STRING_TYPE:
|
|
|
2ff057 |
case RPM_BIN_TYPE:
|
|
|
2ff057 |
if (td.count >= 16*1024)
|
|
|
2ff057 |
continue;
|
|
|
2ff057 |
break;
|
|
|
2ff057 |
case RPM_STRING_ARRAY_TYPE:
|
|
|
2ff057 |
case RPM_I18NSTRING_TYPE:
|
|
|
2ff057 |
continue;
|
|
|
2ff057 |
break;
|
|
|
2ff057 |
}
|
|
|
2ff057 |
(void) headerPut(h, &td, HEADERPUT_DEFAULT);
|
|
|
2ff057 |
}
|
|
|
2ff057 |
}
|
|
|
2ff057 |
headerFreeIterator(hi);
|
|
|
2ff057 |
}
|
|
|
2ff057 |
|
|
|
2ff057 |
/**
|
|
|
2ff057 |
* Remember current key id.
|
|
|
2ff057 |
* XXX: This s*** needs to die. Hook it into keyring or sumthin...
|
|
|
2ff057 |
* @param keyid signature keyid
|
|
|
2ff057 |
* @return 0 if new keyid, otherwise 1
|
|
|
2ff057 |
*/
|
|
|
2ff057 |
static int stashKeyid(unsigned int keyid)
|
|
|
2ff057 |
{
|
|
|
2ff057 |
static pthread_mutex_t keyid_lock = PTHREAD_MUTEX_INITIALIZER;
|
|
|
2ff057 |
static const unsigned int nkeyids_max = 256;
|
|
|
2ff057 |
static unsigned int nkeyids = 0;
|
|
|
2ff057 |
static unsigned int nextkeyid = 0;
|
|
|
2ff057 |
static unsigned int * keyids;
|
|
|
2ff057 |
|
|
|
2ff057 |
int i;
|
|
|
2ff057 |
int seen = 0;
|
|
|
2ff057 |
|
|
|
2ff057 |
if (keyid == 0)
|
|
|
2ff057 |
return 0;
|
|
|
2ff057 |
|
|
|
2ff057 |
/* Just pretend we didn't see the keyid if we fail to lock */
|
|
|
2ff057 |
if (pthread_mutex_lock(&keyid_lock))
|
|
|
2ff057 |
return 0;
|
|
|
2ff057 |
|
|
|
2ff057 |
if (keyids != NULL)
|
|
|
2ff057 |
for (i = 0; i < nkeyids; i++) {
|
|
|
2ff057 |
if (keyid == keyids[i]) {
|
|
|
2ff057 |
seen = 1;
|
|
|
2ff057 |
goto exit;
|
|
|
2ff057 |
}
|
|
|
2ff057 |
}
|
|
|
2ff057 |
|
|
|
2ff057 |
if (nkeyids < nkeyids_max) {
|
|
|
2ff057 |
nkeyids++;
|
|
|
2ff057 |
keyids = xrealloc(keyids, nkeyids * sizeof(*keyids));
|
|
|
2ff057 |
}
|
|
|
2ff057 |
if (keyids) /* XXX can't happen */
|
|
|
2ff057 |
keyids[nextkeyid] = keyid;
|
|
|
2ff057 |
nextkeyid++;
|
|
|
2ff057 |
nextkeyid %= nkeyids_max;
|
|
|
2ff057 |
|
|
|
2ff057 |
exit:
|
|
|
2ff057 |
pthread_mutex_unlock(&keyid_lock);
|
|
|
2ff057 |
return seen;
|
|
|
2ff057 |
}
|
|
|
2ff057 |
|
|
|
2ff057 |
static int handleHdrVS(struct rpmsinfo_s *sinfo, void *cbdata)
|
|
|
2ff057 |
{
|
|
|
2ff057 |
char **buf = cbdata;
|
|
|
2ff057 |
if (buf) {
|
|
|
2ff057 |
char *vsmsg = rpmsinfoMsg(sinfo);
|
|
|
2ff057 |
*buf = rstrscat(buf, "\n", vsmsg, NULL);
|
|
|
2ff057 |
free(vsmsg);
|
|
|
2ff057 |
}
|
|
|
2ff057 |
return 1;
|
|
|
2ff057 |
}
|
|
|
2ff057 |
|
|
|
2ff057 |
static void updateHdrDigests(rpmDigestBundle bundle, struct hdrblob_s *blob)
|
|
|
2ff057 |
{
|
|
|
2ff057 |
int32_t ildl[2] = { htonl(blob->ril), htonl(blob->rdl) };
|
|
|
2ff057 |
|
|
|
2ff057 |
rpmDigestBundleUpdate(bundle, rpm_header_magic, sizeof(rpm_header_magic));
|
|
|
2ff057 |
rpmDigestBundleUpdate(bundle, ildl, sizeof(ildl));
|
|
|
2ff057 |
rpmDigestBundleUpdate(bundle, blob->pe, (blob->ril * sizeof(*blob->pe)));
|
|
|
2ff057 |
rpmDigestBundleUpdate(bundle, blob->dataStart, blob->rdl);
|
|
|
2ff057 |
}
|
|
|
2ff057 |
|
|
|
2ff057 |
rpmRC headerCheck(rpmts ts, const void * uh, size_t uc, char ** msg)
|
|
|
2ff057 |
{
|
|
|
2ff057 |
rpmRC rc = RPMRC_FAIL;
|
|
|
2ff057 |
rpmVSFlags vsflags = rpmtsVSFlags(ts) | RPMVSF_NEEDPAYLOAD;
|
|
|
2ff057 |
rpmKeyring keyring = rpmtsGetKeyring(ts, 1);
|
|
|
2ff057 |
struct hdrblob_s blob;
|
|
|
2ff057 |
|
|
|
2ff057 |
if (hdrblobInit(uh, uc, 0, 0, &blob, msg) == RPMRC_OK) {
|
|
|
2ff057 |
struct rpmvs_s *vs = rpmvsCreate(0, vsflags, keyring);
|
|
|
2ff057 |
rpmDigestBundle bundle = rpmDigestBundleNew();
|
|
|
2ff057 |
|
|
|
2ff057 |
rpmswEnter(rpmtsOp(ts, RPMTS_OP_DIGEST), 0);
|
|
|
2ff057 |
|
|
|
2ff057 |
rpmvsInit(vs, &blob, bundle);
|
|
|
2ff057 |
rpmvsInitRange(vs, RPMSIG_HEADER);
|
|
|
2ff057 |
updateHdrDigests(bundle, &blob;;
|
|
|
2ff057 |
rpmvsFiniRange(vs, RPMSIG_HEADER);
|
|
|
2ff057 |
|
|
|
2ff057 |
rc = rpmvsVerify(vs, RPMSIG_VERIFIABLE_TYPE, handleHdrVS, msg);
|
|
|
2ff057 |
|
|
|
2ff057 |
rpmswExit(rpmtsOp(ts, RPMTS_OP_DIGEST), uc);
|
|
|
2ff057 |
|
|
|
2ff057 |
if (rc == RPMRC_OK && msg != NULL && *msg == NULL)
|
|
|
2ff057 |
rasprintf(msg, "Header sanity check: OK");
|
|
|
2ff057 |
|
|
|
2ff057 |
rpmDigestBundleFree(bundle);
|
|
|
2ff057 |
rpmvsFree(vs);
|
|
|
2ff057 |
}
|
|
|
2ff057 |
|
|
|
2ff057 |
rpmKeyringFree(keyring);
|
|
|
2ff057 |
|
|
|
2ff057 |
return rc;
|
|
|
2ff057 |
}
|
|
|
2ff057 |
|
|
|
2ff057 |
rpmRC rpmReadHeader(rpmts ts, FD_t fd, Header *hdrp, char ** msg)
|
|
|
2ff057 |
{
|
|
|
2ff057 |
char *buf = NULL;
|
|
|
2ff057 |
struct hdrblob_s blob;
|
|
|
2ff057 |
Header h = NULL;
|
|
|
2ff057 |
rpmRC rc = RPMRC_FAIL; /* assume failure */
|
|
|
2ff057 |
|
|
|
2ff057 |
if (hdrp)
|
|
|
2ff057 |
*hdrp = NULL;
|
|
|
2ff057 |
if (msg)
|
|
|
2ff057 |
*msg = NULL;
|
|
|
2ff057 |
|
|
|
2ff057 |
if (hdrblobRead(fd, 1, 1, RPMTAG_HEADERIMMUTABLE, &blob, &buf) != RPMRC_OK)
|
|
|
2ff057 |
goto exit;
|
|
|
2ff057 |
|
|
|
2ff057 |
/* OK, blob looks sane, load the header. */
|
|
|
2ff057 |
rc = hdrblobImport(&blob, 0, &h, &buf;;
|
|
|
2ff057 |
|
|
|
2ff057 |
exit:
|
|
|
2ff057 |
if (hdrp && h && rc == RPMRC_OK)
|
|
|
2ff057 |
*hdrp = headerLink(h);
|
|
|
2ff057 |
headerFree(h);
|
|
|
2ff057 |
|
|
|
2ff057 |
if (msg != NULL && *msg == NULL && buf != NULL) {
|
|
|
2ff057 |
*msg = buf;
|
|
|
2ff057 |
} else {
|
|
|
2ff057 |
free(buf);
|
|
|
2ff057 |
}
|
|
|
2ff057 |
|
|
|
2ff057 |
return rc;
|
|
|
2ff057 |
}
|
|
|
2ff057 |
|
|
|
2ff057 |
static
|
|
|
2ff057 |
void applyRetrofits(Header h)
|
|
|
2ff057 |
{
|
|
|
2ff057 |
/*
|
|
|
2ff057 |
* Make sure that either RPMTAG_SOURCERPM or RPMTAG_SOURCEPACKAGE
|
|
|
2ff057 |
* is set. Use a simple heuristic to find the type if both are unset.
|
|
|
2ff057 |
*/
|
|
|
2ff057 |
if (!headerIsEntry(h, RPMTAG_SOURCERPM) && !headerIsEntry(h, RPMTAG_SOURCEPACKAGE)) {
|
|
|
2ff057 |
/* the heuristic needs the compressed file list */
|
|
|
2ff057 |
if (headerIsEntry(h, RPMTAG_OLDFILENAMES))
|
|
|
2ff057 |
headerConvert(h, HEADERCONV_COMPRESSFILELIST);
|
|
|
2ff057 |
if (headerIsSourceHeuristic(h)) {
|
|
|
2ff057 |
/* Retrofit RPMTAG_SOURCEPACKAGE to srpms for compatibility */
|
|
|
2ff057 |
uint32_t one = 1;
|
|
|
2ff057 |
headerPutUint32(h, RPMTAG_SOURCEPACKAGE, &one, 1);
|
|
|
2ff057 |
} else {
|
|
|
2ff057 |
/*
|
|
|
2ff057 |
* Make sure binary rpms have RPMTAG_SOURCERPM set as that's
|
|
|
2ff057 |
* what we use for differentiating binary vs source elsewhere.
|
|
|
2ff057 |
*/
|
|
|
2ff057 |
headerPutString(h, RPMTAG_SOURCERPM, "(none)");
|
|
|
2ff057 |
}
|
|
|
2ff057 |
}
|
|
|
2ff057 |
|
|
|
2ff057 |
/*
|
|
|
2ff057 |
* Convert legacy headers on the fly. Not having immutable region
|
|
|
2ff057 |
* equals a truly ancient package, do full retrofit. OTOH newer
|
|
|
2ff057 |
* packages might have been built with --nodirtokens, test and handle
|
|
|
2ff057 |
* the non-compressed filelist case separately.
|
|
|
2ff057 |
*/
|
|
|
2ff057 |
if (!headerIsEntry(h, RPMTAG_HEADERIMMUTABLE))
|
|
|
2ff057 |
headerConvert(h, HEADERCONV_RETROFIT_V3);
|
|
|
2ff057 |
else if (headerIsEntry(h, RPMTAG_OLDFILENAMES))
|
|
|
2ff057 |
headerConvert(h, HEADERCONV_COMPRESSFILELIST);
|
|
|
2ff057 |
}
|
|
|
2ff057 |
|
|
|
2ff057 |
struct pkgdata_s {
|
|
|
2ff057 |
const char *fn;
|
|
|
2ff057 |
rpmRC rc;
|
|
|
2ff057 |
};
|
|
|
2ff057 |
|
|
|
2ff057 |
static int handlePkgVS(struct rpmsinfo_s *sinfo, void *cbdata)
|
|
|
2ff057 |
{
|
|
|
2ff057 |
struct pkgdata_s *pkgdata = cbdata;
|
|
|
2ff057 |
int lvl = RPMLOG_DEBUG;
|
|
|
2ff057 |
char *vsmsg = rpmsinfoMsg(sinfo);
|
|
|
2ff057 |
switch (sinfo->rc) {
|
|
|
2ff057 |
case RPMRC_OK: /* Signature is OK. */
|
|
|
2ff057 |
break;
|
|
|
2ff057 |
case RPMRC_NOTTRUSTED: /* Signature is OK, but key is not trusted. */
|
|
|
2ff057 |
case RPMRC_NOKEY: /* Public key is unavailable. */
|
|
|
2ff057 |
/* XXX Print NOKEY/NOTTRUSTED warning only once. */
|
|
|
2ff057 |
if (stashKeyid(sinfo->keyid) == 0)
|
|
|
2ff057 |
lvl = RPMLOG_WARNING;
|
|
|
2ff057 |
break;
|
|
|
2ff057 |
case RPMRC_NOTFOUND: /* Signature/digest not present. */
|
|
|
2ff057 |
lvl = RPMLOG_WARNING;
|
|
|
2ff057 |
break;
|
|
|
2ff057 |
default:
|
|
|
2ff057 |
case RPMRC_FAIL: /* Signature does not verify. */
|
|
|
2ff057 |
lvl = RPMLOG_ERR;
|
|
|
2ff057 |
break;
|
|
|
2ff057 |
}
|
|
|
2ff057 |
|
|
|
2ff057 |
rpmlog(lvl, "%s: %s\n", pkgdata->fn, vsmsg);
|
|
|
2ff057 |
|
|
|
2ff057 |
/* Remember actual return code, but don't override a previous failure */
|
|
|
2ff057 |
if (sinfo->rc && pkgdata->rc != RPMRC_FAIL)
|
|
|
2ff057 |
pkgdata->rc = sinfo->rc;
|
|
|
2ff057 |
|
|
|
2ff057 |
/* Preserve traditional behavior for now: only failure prevents read */
|
|
|
2ff057 |
if (sinfo->rc != RPMRC_FAIL)
|
|
|
2ff057 |
sinfo->rc = RPMRC_OK;
|
|
|
2ff057 |
|
|
|
2ff057 |
free(vsmsg);
|
|
|
2ff057 |
return 1;
|
|
|
2ff057 |
}
|
|
|
2ff057 |
|
|
|
2ff057 |
rpmRC rpmReadPackageFile(rpmts ts, FD_t fd, const char * fn, Header * hdrp)
|
|
|
2ff057 |
{
|
|
|
2ff057 |
char *msg = NULL;
|
|
|
2ff057 |
Header h = NULL;
|
|
|
2ff057 |
Header sigh = NULL;
|
|
|
2ff057 |
hdrblob blob = NULL;
|
|
|
2ff057 |
hdrblob sigblob = NULL;
|
|
|
2ff057 |
rpmVSFlags vsflags = rpmtsVSFlags(ts) | RPMVSF_NEEDPAYLOAD;
|
|
|
2ff057 |
rpmKeyring keyring = rpmtsGetKeyring(ts, 1);
|
|
|
2ff057 |
struct rpmvs_s *vs = rpmvsCreate(0, vsflags, keyring);
|
|
|
2ff057 |
struct pkgdata_s pkgdata = {
|
|
|
2ff057 |
.fn = fn ? fn : Fdescr(fd),
|
|
|
2ff057 |
.rc = RPMRC_OK,
|
|
|
2ff057 |
};
|
|
|
2ff057 |
|
|
|
2ff057 |
/* XXX: lots of 3rd party software relies on the behavior */
|
|
|
2ff057 |
if (hdrp)
|
|
|
2ff057 |
*hdrp = NULL;
|
|
|
2ff057 |
|
|
|
2ff057 |
rpmRC rc = rpmpkgRead(vs, fd, &sigblob, &blob, &msg;;
|
|
|
2ff057 |
if (rc)
|
|
|
2ff057 |
goto exit;
|
|
|
2ff057 |
|
|
|
2ff057 |
/* Actually all verify discovered signatures and digests */
|
|
|
2ff057 |
rc = RPMRC_FAIL;
|
|
|
2ff057 |
if (!rpmvsVerify(vs, RPMSIG_VERIFIABLE_TYPE, handlePkgVS, &pkgdata)) {
|
|
|
2ff057 |
/* Finally import the headers and do whatever required retrofits etc */
|
|
|
2ff057 |
if (hdrp) {
|
|
|
2ff057 |
if (hdrblobImport(sigblob, 0, &sigh, &msg))
|
|
|
2ff057 |
goto exit;
|
|
|
2ff057 |
if (hdrblobImport(blob, 0, &h, &msg))
|
|
|
2ff057 |
goto exit;
|
|
|
2ff057 |
|
|
|
2ff057 |
/* Append (and remap) signature tags to the metadata. */
|
|
|
2ff057 |
headerMergeLegacySigs(h, sigh);
|
|
|
2ff057 |
applyRetrofits(h);
|
|
|
2ff057 |
|
|
|
2ff057 |
/* Bump reference count for return. */
|
|
|
2ff057 |
*hdrp = headerLink(h);
|
|
|
2ff057 |
}
|
|
|
2ff057 |
rc = RPMRC_OK;
|
|
|
2ff057 |
}
|
|
|
2ff057 |
|
|
|
2ff057 |
/* If there was a "substatus" (NOKEY in practise), return that instead */
|
|
|
2ff057 |
if (rc == RPMRC_OK && pkgdata.rc)
|
|
|
2ff057 |
rc = pkgdata.rc;
|
|
|
2ff057 |
|
|
|
2ff057 |
exit:
|
|
|
2ff057 |
if (rc && msg)
|
|
|
2ff057 |
rpmlog(RPMLOG_ERR, "%s: %s\n", Fdescr(fd), msg);
|
|
|
2ff057 |
hdrblobFree(sigblob);
|
|
|
2ff057 |
hdrblobFree(blob);
|
|
|
2ff057 |
headerFree(sigh);
|
|
|
2ff057 |
headerFree(h);
|
|
|
2ff057 |
rpmKeyringFree(keyring);
|
|
|
2ff057 |
rpmvsFree(vs);
|
|
|
2ff057 |
free(msg);
|
|
|
2ff057 |
|
|
|
2ff057 |
return rc;
|
|
|
2ff057 |
}
|
|
|
2ff057 |
|
|
|
2ff057 |
|
|
|
2ff057 |
|