[Rpm-maint] [PATCH] Add RPMTAG_IDENTITY calculation as tag extension

Panu Matilainen pmatilai at redhat.com
Wed Apr 4 08:01:39 UTC 2018


On 04/03/2018 10:31 PM, Vladimir D. Seleznev wrote:
> RPMTAG_IDENTITY is calculating as digest of part of package header that
> does not contain irrelevant to package build tag entries.
> 
> Mathematically RPMTAG_IDENTITY value is a result of function of two
> variable: a package header and an rpm utility, thus this value can
> differ for same package and different version of rpm.
> 
> Despite tag entries are filtering by blacklist, rpm also filters tags
> that it doesn't know while it calculates identity value. So technically,
> it's a graylist.
> ---
>   lib/rpmtag.h  |  3 +-
>   lib/tagexts.c | 93 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>   2 files changed, 95 insertions(+), 1 deletion(-)
> 
> diff --git a/lib/rpmtag.h b/lib/rpmtag.h
> index 973a6b6..36dac85 100644
> --- a/lib/rpmtag.h
> +++ b/lib/rpmtag.h
> @@ -371,11 +371,12 @@ typedef enum rpmTag_e {
>       RPMTAG_PAYLOADDIGEST	= 5092, /* s[] */
>       RPMTAG_PAYLOADDIGESTALGO	= 5093, /* i */
>       RPMTAG_AUTOINSTALLED	= 5094, /* i reservation (unimplemented) */
> -    RPMTAG_IDENTITY		= 5095, /* s reservation (unimplemented) */
> +    RPMTAG_IDENTITY		= 5095, /* s extension */
>   
>       RPMTAG_FIRSTFREE_TAG	/*!< internal */
>   } rpmTag;
>   
> +#define	RPMTAG_IDENTITY_MAX_TAG		RPMTAG_FIRSTFREE_TAG
>   #define	RPMTAG_EXTERNAL_TAG		1000000
>   
>   /** \ingroup rpmtag
> diff --git a/lib/tagexts.c b/lib/tagexts.c
> index f72ff60..ca8ac35 100644
> --- a/lib/tagexts.c
> +++ b/lib/tagexts.c
> @@ -952,6 +952,98 @@ static int filenlinksTag(Header h, rpmtd td, headerGetFlags hgflags)
>       return (fc > 0);
>   }
>   
> +static int identityFilter(rpmTag tag)
> +{
> +    int ret = 0;
> +
> +    switch(tag) {
> +    case RPMTAG_ARCH:
> +    case RPMTAG_ARCHIVESIZE:
> +    case RPMTAG_AUTOINSTALLED:
> +    case RPMTAG_BUILDHOST:
> +    case RPMTAG_BUILDTIME:
> +    case RPMTAG_CHANGELOG:
> +    case RPMTAG_COOKIE:
> +    case RPMTAG_DBINSTANCE:
> +    case RPMTAG_DISTRIBUTION:
> +    case RPMTAG_DISTTAG:
> +    case RPMTAG_DISTURL:
> +    case RPMTAG_FILESTATES:
> +    case RPMTAG_HEADERIMMUTABLE:
> +    case RPMTAG_IDENTITY:
> +    case RPMTAG_INSTALLCOLOR:
> +    case RPMTAG_INSTALLTID:
> +    case RPMTAG_INSTALLTIME:

Many of these are only ever present in installed headers, and of such 
tags at least RPMTAG_ORIGDIRINDEXES, RPMTAG_ORIGBASENAMES and
RPMTAG_ORIGDIRNAMES should also be filtered out.

But see below...

> +    case RPMTAG_INSTFILENAMES:

RPMTAG_INSTFILENAMES is an extension and will never be part of a header, 
ditto for RPMTAG_IDENTITY in this implementation.

> +    case RPMTAG_LONGARCHIVESIZE:
> +    case RPMTAG_LONGSIGSIZE:
> +    case RPMTAG_LONGSIZE:
> +    case RPMTAG_PAYLOADDIGEST:

RPMTAG_PAYLOADDIGESTALGO too.

> +    case RPMTAG_RSAHEADER:
> +    case RPMTAG_SHA1HEADER:
> +    case RPMTAG_SIGLEMD5_1:
> +    case RPMTAG_SIGLEMD5_2:
> +    case RPMTAG_SIGMD5:
> +    case RPMTAG_SIGPGP:
> +    case RPMTAG_SIGSIZE:
> +    case RPMTAG_SIZE:

At least RPMTAG_SHA256HEADER, RPMSIGTAG_FILESIGNATURES and 
RPMSIGTAG_FILESIGNATURELENGTH should also be on this list.

Basically you'd want everything below HEADER_TAGBASE filtered out, with 
the possible exception of HEADER_I18NTABLE. And also you'd want to 
filter out everything that was added to the header as a part of 
installation. There's a better way to all those goals than list 
individual tags:

Grab the immutable region of the header, and do the iteration on that 
instead of the header you got as an argument to identityTag(). Something 
like (untested):

static int identityTag(Header h, rpmtd td, headerGetFlags hgflags)
{
     rpmtd_s itd;
     Header ih;

     /* rpm v3 packages dont have an immutable region */
     if (!headerGet(h, RPMTAG_HEADERIMMUTABLE, &itd, HEADERGET_DEFAULT))
         goto err;

     ih = headerImport(itd.data, itd.count, HEADERIMPORT_FAST);

     /* ... and then iterate + copy over that */
     HeaderIterator hi = headerInitIterator(h);
     while (headerNext(hi, &ttd) && rc) {
     ...
}

> +       ret = 1;
> +       break;
> +    default:
> +       break;
> +    }
> +
> +    if (tag >= RPMTAG_IDENTITY_MAX_TAG)
> +	ret = 1;
> +
> +    return ret;
> +}
> +
> +static int identityTag(Header h, rpmtd td, headerGetFlags hgflags)
> +{
> +    int rc = 1;
> +    unsigned int bsize;
> +    void * hb;
> +    DIGEST_CTX ctx;
> +    char * identity = NULL;
> +
> +    struct rpmtd_s ttd;
> +    Header ih = headerNew();
> +    HeaderIterator hi = headerInitIterator(h);
> +
> +    while (headerNext(hi, &ttd) && rc) {
> +	if (rpmtdCount(&ttd) > 0) {
> +	    if (!identityFilter(ttd.tag))
> +		rc = headerPut(ih, &ttd, HEADERPUT_DEFAULT);
> +	}
> +	rpmtdFreeData(&ttd);
> +    }
> +    headerFreeIterator(hi);
> +
> +    if (!rc)
> +	return 0;
> +
> +    hb = headerExport(ih, &bsize);
> +
> +    if (hb == NULL)
> +	return 0;

These early returns would leak the "ih" header. In general, we prefer 
functions to have one point of return where all function-level 
allocations are freed and early failure points jump to the exit point 
with goto. That way there's less chance for these kinds of leaks.

> +
> +    ctx = rpmDigestInit(PGPHASHALGO_SHA256, RPMDIGEST_NONE);

rpmDigestInit() can fail, you should check for errors.

> +    rpmDigestUpdate(ctx, hb, bsize);
> +    rpmDigestFinal(ctx, (void **) &identity, NULL, 1);
> +    headerFree(ih);
> +
> +    td->data = xstrdup(identity);
> +    td->type = RPM_STRING_TYPE;
> +    td->count = 1;
> +    td->flags = RPMTD_ALLOCED;
> +
> +    if (identity != NULL)
> +	identity = _free(identity);

Don't bother checking for NULL when freeing, free() will do that anyway.

Overall I do like this a whole lot better than the earlier version.

	- Panu -



More information about the Rpm-maint mailing list