csomh / source-git / rpm

Forked from source-git/rpm 4 years ago
Clone
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