[Rpm-maint] [RFC PATCH] Include and install file signatures

fin at linux.vnet.ibm.com fin at linux.vnet.ibm.com
Thu Aug 14 22:01:59 UTC 2014


From: Fionnuala Gunter <fin at linux.vnet.ibm.com>

IMA-appraisal, upstreamed in linux-3.7, enforces local file integrity based on 
known 'good' value stored as an extended attribute 'security.ima'. Labeling the
filesystem is currently done post install using a local private key. Including 
file signatures in the package provides not only file integrity, but file 
provenance.

This patch extends the existing rpm signing tool to sign package files and 
include them in the package header. It defines a tag called 
RPMTAG_FILESIGNATURES, an RPM macro %_file_sign_key, a new option --signfiles, 
and IMA plugin.

rpm --addsign [--signfiles] PACKAGE_FILE ...

The new option to rpmsign signs all the file digests included in the package. 
When a package is signed with the new option, the file digests are signed using 
libimaevm and the key %_file_sign_key. The resulting signatures are included in
the package header as an RPMTAG_FILESIGNATURES tag. Since the header is 
modified, the SHA1 and MD5 digests of the header are recalculated and inserted 
in the signature header.

After including the file signatures with the new option, the packages are signed
normally.

When a package with signed files is installed, the file signatures are extracted
from the package header, and the IMA plugin writes the file signatures as 
security.ima extended attributes. The IMA plugin instantiates the fsm_file_post
but the parameter list was modified to include the file signature.

Signed-off-by: Fionnuala Gunter <fin at linux.vnet.ibm.com>
---
 configure.ac        |   8 ++
 doc/rpmsign.8       |  30 +++-
 lib/fsm.c           |  24 +++-
 lib/rpmplugin.h     |   3 +-
 lib/rpmplugins.c    |   5 +-
 lib/rpmplugins.h    |   4 +-
 lib/rpmtag.h        |   1 +
 macros.in           |   1 +
 plugins/Makefile.am |   4 +
 rpmpopt.in          |   1 +
 rpmsign.c           |   6 +-
 sign/rpmgensig.c    | 396 ++++++++++++++++++++++++++++++++++++++++++++++++----
 sign/rpmsign.h      |   4 +-
 13 files changed, 452 insertions(+), 35 deletions(-)

diff --git a/configure.ac b/configure.ac
index 734f3a9..d895452 100644
--- a/configure.ac
+++ b/configure.ac
@@ -657,6 +657,14 @@ AC_SUBST(WITH_SELINUX_LIB)
 AC_SUBST(WITH_SEMANAGE_LIB)
 AM_CONDITIONAL(SELINUX,[test "$with_selinux" = yes])
 
+# libimaevm
+with_iamevm=no
+AC_ARG_WITH(imaevm, [AS_HELP_STRING([--with-imaevm],[build with imaevm support])])
+if test "$with_imaevm" = yes ; then
+  AC_DEFINE(IMAEVM, 1, [Build with imaevm support?])
+  LIBS="$LIBS -limaevm"
+fi
+
 # libcap
 WITH_CAP_LIB=
 AC_ARG_WITH(cap, [AS_HELP_STRING([--with-cap],[build with capability support])],
diff --git a/doc/rpmsign.8 b/doc/rpmsign.8
index 53f2d70..8689406 100644
--- a/doc/rpmsign.8
+++ b/doc/rpmsign.8
@@ -2,11 +2,17 @@
 .SH NAME
 rpmsign \- RPM Package Signing
 .SH SYNOPSIS
+.SS "SIGNING PACKAGES:"
+.PP
 
-\fBrpm\fR \fB--addsign|--resign\fR \fB\fIPACKAGE_FILE\fB\fR\fI ...\fR
+\fBrpm\fR \fB--addsign|--resign\fR [\fBrpmsign-options\fR] \fB\fIPACKAGE_FILE\fB\fR\fI ...\fR
 
 \fBrpm\fR \fB--delsign\fR \fB\fIPACKAGE_FILE\fB\fR\fI ...\fR
 
+.SS "rpmsign-options"
+.PP
+ [\fB--signfiles\fR]
+
 .SH DESCRIPTION
 .PP
 Both of the \fB--addsign\fR and \fB--resign\fR
@@ -20,6 +26,27 @@ there is no difference in behavior currently.
 .PP
 Delete all signatures from each package \fIPACKAGE_FILE\fR given.
 
+.SS "SIGN OPTIONS"
+.PP
+.TP
+\fB--signfiles\fR
+Sign all files in each package \fIPACKAGE_FILE\fR. You will need to configure
+the macro \fB%_binary_filedigest_algorithm\fR before building the package. The
+macro \fB%_binary_filedigest_algorithm\fR must be set to a supported algorithm:
+2, 8, 9, or 10, which represent SHA1, SHA256, SHA384, and SHA512, respectively. You will need to configure the macro \fB%_file_sign_key\fR to be the location of
+the RSA private key to use.
+.PP
+For example, to use SHA-256 and the key found at /etc/keys/privkey.pem, you
+would include
+.PP
+.nf
+%_binary_filedigest_algorithm 8
+%_file_sign_key /etc/keys/privkey.pem
+.fi
+.PP
+in a macro configuration file. Use \fI/etc/rpm/macros\fR for per-system
+configuration and \fI~/.rpmmacros\fR for per-user configuration.
+
 .SS "USING GPG TO SIGN PACKAGES"
 .PP
 In order to sign packages using GPG, \fBrpm\fR
@@ -78,4 +105,5 @@ Marc Ewing <marc at redhat.com>
 Jeff Johnson <jbj at redhat.com>
 Erik Troan <ewt at redhat.com>
 Panu Matilainen <pmatilai at redhat.com>
+Fionnuala Gunter <fin at linux.vnet.ibm.com>
 .fi
diff --git a/lib/fsm.c b/lib/fsm.c
index 7ae4747..88bf835 100644
--- a/lib/fsm.c
+++ b/lib/fsm.c
@@ -20,6 +20,7 @@
 #include "lib/rpmte_internal.h"	/* XXX rpmfs */
 #include "lib/rpmplugins.h"	/* rpm plugins hooks */
 #include "lib/rpmug.h"
+#include "lib/rpmlib.h"
 
 #include "debug.h"
 
@@ -449,7 +450,8 @@ static int fsmMkdirs(rpmfiles files, rpmfs fs, rpmPlugins plugins)
 		}
 
 		/* Run fsm file post hook for all plugins */
-		rpmpluginsCallFsmFilePost(plugins, NULL, dn, mode, op, rc);
+		rpmpluginsCallFsmFilePost(plugins, NULL, dn, mode, op, NULL,
+					  rc);
 
 		if (!rc) {
 		    rpmlog(RPMLOG_DEBUG,
@@ -822,12 +824,18 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files,
     char *tid = NULL;
     const char *suffix;
     char *fpath = NULL;
+    Header h = rpmteHeader(te);
+    struct rpmtd_s sigs;
+    const char *sig;
 
     if (fi == NULL) {
 	rc = RPMERR_BAD_MAGIC;
 	goto exit;
     }
 
+    if (h != NULL)
+	headerGet(h, RPMTAG_FILESIGNATURES, &sigs, HEADERGET_MINMEM);
+
     /* transaction id used for temporary path suffix while installing */
     rasprintf(&tid, ";%08x", (unsigned)rpmtsGetTid(ts));
 
@@ -952,9 +960,17 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files,
 	if (rc)
 	    *failedFile = xstrdup(fpath);
 
+	/* if file is executable, get file signature */
+	if (sb.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH)) {
+	    sig = rpmtdNextString(&sigs);
+	} else {
+	    sig = NULL;
+	    rpmtdNextString(&sigs);
+	}
+
 	/* Run fsm file post hook for all plugins */
 	rpmpluginsCallFsmFilePost(plugins, fi, fpath,
-				  sb.st_mode, action, rc);
+				  sb.st_mode, action, sig, rc);
 	fpath = _free(fpath);
     }
 
@@ -964,6 +980,8 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files,
 exit:
 
     /* No need to bother with close errors on read */
+    rpmtdFreeData(&sigs);
+    headerFree(h);
     rpmfiArchiveClose(fi);
     rpmfiFree(fi);
     Fclose(payload);
@@ -1034,7 +1052,7 @@ int rpmPackageFilesRemove(rpmts ts, rpmte te, rpmfiles files,
 
 	/* Run fsm file post hook for all plugins */
 	rpmpluginsCallFsmFilePost(plugins, fi, fpath,
-				  sb.st_mode, action, rc);
+				  sb.st_mode, action, NULL, rc);
 
         /* XXX Failure to remove is not (yet) cause for failure. */
         if (!strict_erasures) rc = 0;
diff --git a/lib/rpmplugin.h b/lib/rpmplugin.h
index fd81aec..f755378 100644
--- a/lib/rpmplugin.h
+++ b/lib/rpmplugin.h
@@ -55,7 +55,8 @@ typedef rpmRC (*plugin_fsm_file_pre_func)(rpmPlugin plugin, rpmfi fi,
 					  rpmFsmOp op);
 typedef rpmRC (*plugin_fsm_file_post_func)(rpmPlugin plugin, rpmfi fi,
 					   const char* path, mode_t file_mode,
-					   rpmFsmOp op, int res);
+					   rpmFsmOp op, const char *sig,
+					   int res);
 typedef rpmRC (*plugin_fsm_file_prepare_func)(rpmPlugin plugin, rpmfi fi,
 					      const char* path,
 					      const char *dest,
diff --git a/lib/rpmplugins.c b/lib/rpmplugins.c
index da04c73..bf856f2 100644
--- a/lib/rpmplugins.c
+++ b/lib/rpmplugins.c
@@ -349,7 +349,8 @@ rpmRC rpmpluginsCallFsmFilePre(rpmPlugins plugins, rpmfi fi, const char *path,
 }
 
 rpmRC rpmpluginsCallFsmFilePost(rpmPlugins plugins, rpmfi fi, const char *path,
-                                mode_t file_mode, rpmFsmOp op, int res)
+                                mode_t file_mode, rpmFsmOp op, const char *sig,
+                                int res)
 {
     plugin_fsm_file_post_func hookFunc;
     int i;
@@ -358,7 +359,7 @@ rpmRC rpmpluginsCallFsmFilePost(rpmPlugins plugins, rpmfi fi, const char *path,
     for (i = 0; i < plugins->count; i++) {
 	rpmPlugin plugin = plugins->plugins[i];
 	RPMPLUGINS_SET_HOOK_FUNC(fsm_file_post);
-	if (hookFunc && hookFunc(plugin, fi, path, file_mode, op, res) == RPMRC_FAIL)
+	if (hookFunc && hookFunc(plugin, fi, path, file_mode, op, sig, res) == RPMRC_FAIL)
 	    rc = RPMRC_FAIL;
     }
 
diff --git a/lib/rpmplugins.h b/lib/rpmplugins.h
index ecfa68b..93ab7b9 100644
--- a/lib/rpmplugins.h
+++ b/lib/rpmplugins.h
@@ -144,12 +144,14 @@ rpmRC rpmpluginsCallFsmFilePre(rpmPlugins plugins, rpmfi fi, const char* path,
  * @param path		file object path
  * @param file_mode	file object mode
  * @param op		file operation + associated flags
+ * @param sig		file signature
  * @param res		fsm result code
  * @return		RPMRC_OK on success, RPMRC_FAIL otherwise
  */
 RPM_GNUC_INTERNAL
 rpmRC rpmpluginsCallFsmFilePost(rpmPlugins plugins, rpmfi fi, const char* path,
-                                mode_t file_mode, rpmFsmOp op, int res);
+                                mode_t file_mode, rpmFsmOp op, const char *sig,
+                                int res);
 
 /** \ingroup rpmplugins
  * Call the fsm file prepare plugin hook. Called after setting
diff --git a/lib/rpmtag.h b/lib/rpmtag.h
index 12a2a50..e38be9a 100644
--- a/lib/rpmtag.h
+++ b/lib/rpmtag.h
@@ -328,6 +328,7 @@ typedef enum rpmTag_e {
     RPMTAG_SUGGESTNEVRS		= 5059, /* s[] extension */
     RPMTAG_SUPPLEMENTNEVRS	= 5060, /* s[] extension */
     RPMTAG_ENHANCENEVRS		= 5061, /* s[] extension */
+    RPMTAG_FILESIGNATURES	= 5062, /* s[] */
 
     RPMTAG_FIRSTFREE_TAG	/*!< internal */
 } rpmTag;
diff --git a/macros.in b/macros.in
index 2ada48b..21731b7 100644
--- a/macros.in
+++ b/macros.in
@@ -1040,6 +1040,7 @@ done \
 %__transaction_systemd_inhibit	%{__plugindir}/systemd_inhibit.so
 %__transaction_selinux		%{__plugindir}/selinux.so
 %__transaction_syslog		%{__plugindir}/syslog.so
+%__transaction_ima		%{__plugindir}/ima.so
 
 #------------------------------------------------------------------------------
 # Macros for further automated spec %setup and patch application
diff --git a/plugins/Makefile.am b/plugins/Makefile.am
index 53b2450..18f6170 100644
--- a/plugins/Makefile.am
+++ b/plugins/Makefile.am
@@ -31,3 +31,7 @@ endif
 syslog_la_SOURCES = syslog.c
 syslog_la_LIBADD = $(top_builddir)/lib/librpm.la $(top_builddir)/rpmio/librpmio.la
 plugins_LTLIBRARIES += syslog.la
+
+ima_la_sources = ima.c
+ima_la_LIBADD = $(top_builddir)/lib/librpm.la $(top_builddir)/rpmio/librpmio.la
+plugins_LTLIBRARIES +=  ima.la
diff --git a/rpmpopt.in b/rpmpopt.in
index 036ab4e..966a4f0 100644
--- a/rpmpopt.in
+++ b/rpmpopt.in
@@ -162,6 +162,7 @@ rpm	alias --httpproxy	--define '_httpproxy !#:+'
 rpm	exec --addsign		rpmsign --addsign
 rpm	exec --delsign		rpmsign --delsign
 rpm	exec --resign		rpmsign --resign
+rpm	exec --signfiles	rpmsign --signfiles
 rpm	exec --checksig		rpmkeys --checksig
 rpm	exec -K			rpmkeys --checksig
 rpm	exec --import		rpmkeys --import
diff --git a/rpmsign.c b/rpmsign.c
index b8e5598..64effad 100644
--- a/rpmsign.c
+++ b/rpmsign.c
@@ -20,6 +20,8 @@ enum modes {
 
 static int mode = 0;
 
+static int signfiles = 0;
+
 static struct poptOption signOptsTable[] = {
     { "addsign", '\0', (POPT_ARG_VAL|POPT_ARGFLAG_OR), &mode, MODE_ADDSIGN,
 	N_("sign package(s)"), NULL },
@@ -27,6 +29,8 @@ static struct poptOption signOptsTable[] = {
 	N_("sign package(s) (identical to --addsign)"), NULL },
     { "delsign", '\0', (POPT_ARG_VAL|POPT_ARGFLAG_OR), &mode, MODE_DELSIGN,
 	N_("delete package signatures"), NULL },
+    { "signfiles", '\0', POPT_ARG_NONE, &signfiles, 0,
+	N_("sign package(s) files"), NULL},
     POPT_TABLEEND
 };
 
@@ -127,7 +131,7 @@ static int doSign(poptContext optCon)
 	fprintf(stderr, _("Pass phrase is good.\n"));
 	rc = 0;
 	while ((arg = poptGetArg(optCon)) != NULL) {
-	    rc += rpmPkgSign(arg, NULL, passPhrase);
+	    rc += rpmPkgSign(arg, NULL, passPhrase, signfiles);
 	}
     } else {
 	fprintf(stderr, _("Pass phrase check failed or gpg key expired\n"));
diff --git a/sign/rpmgensig.c b/sign/rpmgensig.c
index 0bd14e3..72a0701 100644
--- a/sign/rpmgensig.c
+++ b/sign/rpmgensig.c
@@ -1,4 +1,10 @@
-/** \ingroup rpmcli
+/**
+ * Copyright (C) 2014 IBM Corporation
+ *
+ * Author: Fionnuala Gunter <fin at linux.vnet.ibm.com>
+ *   added support for file signatures
+ *
+ * \ingroup rpmcli
  * \file lib/rpmchecksig.c
  * Verify the signature of a package.
  */
@@ -8,17 +14,22 @@
 #include <errno.h>
 #include <sys/wait.h>
 #include <popt.h>
+#include <imaevm.h>
+#include <ctype.h>
 
 #include <rpm/rpmlib.h>			/* RPMSIGTAG & related */
 #include <rpm/rpmmacro.h>
 #include <rpm/rpmpgp.h>
 #include <rpm/rpmsign.h>
-#include <rpm/rpmfileutil.h>	/* rpmMkTemp() */
+#include <rpm/rpmfileutil.h>		/* rpmMkTemp() */
 #include <rpm/rpmlog.h>
 #include <rpm/rpmstring.h>
 
 #include "lib/rpmlead.h"
 #include "lib/signature.h"
+#include "lib/header.h"
+#include "lib/cpio.h"			/* rpmcpioOpen, rpmcpioTell */
+#include "rpmio/rpmio_internal.h"	/* fdInitDigest, fdFiniDigest */
 
 #include "debug.h"
 
@@ -88,6 +99,10 @@ static int manageFile(FD_t *fdp, const char *fn, int flags)
 
 /**
  * Copy header+payload, calculating digest(s) on the fly.
+ * @param sfdp	source file
+ * @param sfnp	source path
+ * @param tfdp	destination file
+ * @param tfnp	destination path
  */
 static int copyFile(FD_t *sfdp, const char *sfnp,
 		FD_t *tfdp, const char *tfnp)
@@ -121,8 +136,6 @@ static int copyFile(FD_t *sfdp, const char *sfnp,
     rc = 0;
 
 exit:
-    if (*sfdp)	(void) closeFile(sfdp);
-    if (*tfdp)	(void) closeFile(tfdp);
     return rc;
 }
 
@@ -458,14 +471,355 @@ static int replaceSignature(Header sigh, sigTarget sigt1, sigTarget sigt2,
     return rc;
 }
 
+static rpmRC generateSignature(char *SHA1, uint8_t *MD5, rpm_loff_t size,
+                                rpm_loff_t payloadSize, FD_t fd)
+{
+    Header sig = NULL;
+    struct rpmtd_s td;
+    rpmTagVal sizetag;
+    rpmTagVal payloadtag;
+    rpm_tagtype_t typetag;
+    rpmRC rc = RPMRC_OK;
+    char *reservedSpace;
+    int spaceSize = 0;
+
+    /* Prepare signature */
+    sig = rpmNewSignature();
+
+    rpmtdReset(&td);
+    td.tag = RPMSIGTAG_SHA1;
+    td.count = 1;
+    td.type = RPM_STRING_TYPE;
+    td.data = SHA1;
+    headerPut(sig, &td, HEADERPUT_DEFAULT);
+
+    rpmtdReset(&td);
+    td.tag = RPMSIGTAG_MD5;
+    td.count = 16;
+    td.type = RPM_BIN_TYPE;
+    td.data = MD5;
+    headerPut(sig, &td, HEADERPUT_DEFAULT);
+
+    if (payloadSize < UINT32_MAX) {
+        sizetag = RPMSIGTAG_SIZE;
+        payloadtag = RPMSIGTAG_PAYLOADSIZE;
+        typetag = RPM_INT32_TYPE;
+    } else {
+        sizetag = RPMSIGTAG_LONGSIZE;
+        payloadtag = RPMSIGTAG_LONGARCHIVESIZE;
+        typetag = RPM_INT64_TYPE;
+    }
+
+    rpmtdReset(&td);
+    td.tag = payloadtag;
+    td.count = 1;
+    td.type = typetag;
+    td.data = &payloadSize;
+    headerPut(sig, &td, HEADERPUT_DEFAULT);
+
+    rpmtdReset(&td);
+    td.tag = sizetag;
+    td.count = 1;
+    td.type = typetag;
+    td.data = &size;
+    headerPut(sig, &td, HEADERPUT_DEFAULT);
+
+    spaceSize = rpmExpandNumeric("%{__gpg_reserved_space}");
+    if(spaceSize > 0) {
+        reservedSpace = xcalloc(spaceSize, sizeof(char));
+        rpmtdReset(&td);
+        td.tag = RPMSIGTAG_RESERVEDSPACE;
+        td.count = spaceSize;
+        td.type = RPM_BIN_TYPE;
+        td.data = reservedSpace;
+        headerPut(sig, &td, HEADERPUT_DEFAULT);
+        free(reservedSpace);
+    }
+
+    /* Reallocate the signature into one contiguous region. */
+    sig = headerReload(sig, RPMTAG_HEADERSIGNATURES);
+    if (sig == NULL) { /* XXX can't happen */
+        rpmlog(RPMLOG_ERR, _("Unable to reload signature header.\n"));
+        rc = RPMRC_FAIL;
+        goto exit;
+    }
+
+    /* Write the signature section into the package. */
+    if (rpmWriteSignature(fd, sig)) {
+        rc = RPMRC_FAIL;
+        goto exit;
+    }
+
+exit:
+    rpmFreeSignature(sig);
+    return rc;
+}
+
+static const char *rpmDigestAlgo(uint32_t dalgo)
+{
+    switch (dalgo) {
+	case 0:
+	case 1: return "md5";
+	case 2: return "sha1";
+	case 8: return "sha256";
+	case 9: return "sha384";
+	case 10: return "sha512";
+	default: return NULL;
+    }
+}
+
+static int signFiles(Header h, const char *key)
+{
+    struct rpmtd_s digalgo, fdigests;
+    const char *algo, *fdigest, *fsignature;
+    unsigned char digest[BUFSIZ], signature[BUFSIZ];
+    int diglen, siglen;
+    uint32_t *dalgo;
+    rpmRC rc = RPMRC_FAIL;
+
+#ifndef IMAEVM
+    fprintf(stderr, _("Cannot find libimaevm\n"));
+    rpmlog(RPMLOG_ERR, _("Cannot find libimaevm\n"));
+    goto exit;
+#endif
+
+    headerGet(h, RPMTAG_FILEDIGESTALGO, &digalgo, HEADERGET_MINMEM);
+    headerGet(h, RPMTAG_FILEDIGESTS, &fdigests, HEADERGET_MINMEM);
+
+    dalgo = rpmtdGetUint32(&digalgo);
+    if (!dalgo) {
+	fprintf(stderr, "Set \"%%_binary_filedigest_algorithm in macro file\n");
+	rpmlog(RPMLOG_ERR, _("No digest algorithm selected\n"));
+	goto exit;
+    }
+    diglen = rpmDigestLength(*dalgo);
+    if (diglen < 0) {
+	rpmlog(RPMLOG_ERR, _("rpmDigestLength failed\n"));
+    }
+    algo = rpmDigestAlgo(*dalgo);
+    if (!algo) {
+	rpmlog(RPMLOG_ERR, _("Unsupported file digest algorithm\n"));
+	goto exit;
+    }
+
+    while ((fdigest = rpmtdNextString(&fdigests))) {
+	/* convert file digest from hex to binary */
+	memset(digest, 0, BUFSIZ);
+	for (int j = 0; j < diglen; j++, fdigest += 2)
+	    digest[j] = (rnibble(fdigest[0]) << 4) | rnibble(fdigest[1]);
+
+	/* prepare signature */
+	memset(signature, 0, BUFSIZ);
+	signature[0] = '\x03';
+
+	/* calculate file signature */
+	siglen = sign_hash(algo, digest, diglen, key, signature+1);
+	if (siglen < 0) {
+	    rpmlog(RPMLOG_ERR, _("sign_hash failed\n"));
+	    goto exit;
+	}
+
+	/* convert file signature from binary to hex */
+	fsignature = pgpHexStr(signature, siglen+1);
+
+	if (!headerPutString(h, RPMTAG_FILESIGNATURES, fsignature)) {
+	    rpmlog(RPMLOG_ERR, _("headerPutString failed\n"));
+	    goto exit;
+	}
+    }
+    rc = RPMRC_OK;
+exit:
+    return rc;
+}
+
+static void unloadImmutableRegion(Header *hdrp, rpmTagVal tag, rpmtd utd)
+{
+    struct rpmtd_s copytd;
+    Header nh;
+    Header oh;
+    HeaderIterator hi;
+
+    if (headerGet(*hdrp, tag, utd, HEADERGET_DEFAULT)) {
+	nh = headerNew();
+	oh = headerCopyLoad(utd->data);
+	hi = headerInitIterator(oh);
+
+	while (headerNext(hi, &copytd)) {
+	    if (copytd.data)
+		headerPut(nh, &copytd, HEADERPUT_DEFAULT);
+	    rpmtdFreeData(&copytd);
+	}
+
+	headerFreeIterator(hi);
+	headerFree(oh);
+	rpmtdFreeData(utd);
+
+	*hdrp = headerLink(nh);
+	headerFree(nh);
+    }
+}
+
+/**
+ * Sign file digests and include signatures in header
+ * @param fd
+ * @param rpm		path to package
+ * @param sigp		pointer to signature header
+ * @param hdrp		pointer to header
+ * @param sigStart	signature offset in rpm
+ * @param headerStart	header offset in rpm
+ */
+static rpmRC includeFileSignatures(FD_t fd, const char *rpm,
+				   Header *sigp, Header *hdrp,
+				   off_t sigStart, off_t headerStart)
+{
+    FD_t ofd = NULL;
+    struct rpmtd_s td;
+    const char *key;
+    char *trpm = NULL;
+    char *SHA1 = NULL;
+    uint8_t *MD5 = NULL;
+    unsigned char buf[32*BUFSIZ];
+    size_t sha1len;
+    size_t md5len;
+    size_t headerSize;
+    off_t archiveSize;
+    rpmRC rc = RPMRC_OK;
+
+    unloadImmutableRegion(hdrp, RPMTAG_HEADERIMMUTABLE, &td);
+
+    key = rpmExpand("%{?_file_sign_key}", NULL);
+    if (rstreq(key, "")) {
+	rc = RPMRC_FAIL;
+	fprintf(stderr,
+		_("You must set \"$$_file_sign_key\" in your macro file\n"));
+	rpmlog(RPMLOG_ERR, _("macro file is missing file sign key\n"));
+	goto exit;
+    }
+
+    rc = signFiles(*hdrp, key);
+    if (rc != RPMRC_OK) {
+	rpmlog(RPMLOG_ERR, _("signFiles failed\n"));
+	goto exit;
+    }
+
+    *hdrp = headerReload(*hdrp, RPMTAG_HEADERIMMUTABLE);
+    if (*hdrp == NULL) {
+	rc = RPMRC_FAIL;
+	rpmlog(RPMLOG_ERR, _("headerReload failed\n"));
+	goto exit;
+     }
+
+    ofd = rpmMkTempFile(NULL, &trpm);
+    if (ofd == NULL || Ferror(ofd)) {
+	rc = RPMRC_FAIL;
+	rpmlog(RPMLOG_ERR, _("rpmMkTemp failed\n"));
+	goto exit;
+    }
+
+    /* Copy archive to temp file */
+    if (copyFile(&fd, rpm, &ofd, trpm)) {
+	rc = RPMRC_FAIL;
+	rpmlog(RPMLOG_ERR, _("copyFile failed\n"));
+	goto exit;
+    }
+
+    if (Fseek(fd, headerStart, SEEK_SET) < 0) {
+	rc = RPMRC_FAIL;
+	rpmlog(RPMLOG_ERR, _("Could not seek in file %s: %s\n"),
+		rpm, Fstrerror(fd));
+	goto exit;
+    }
+
+    /* Write header to rpm and recalculate SHA1 */
+    fdInitDigest(fd, PGPHASHALGO_SHA1, 0);
+    rc = headerWrite(fd, *hdrp, HEADER_MAGIC_YES);
+    if (rc != RPMRC_OK) {
+	rpmlog(RPMLOG_ERR, _("headerWrite failed\n"));
+	goto exit;
+    }
+    fdFiniDigest(fd, PGPHASHALGO_SHA1, (void **)&SHA1, &sha1len, 1);
+    headerSize = Ftell(fd) - headerStart;
+
+    /* Copy archive from temp file */
+    if (Fseek(ofd, 0, SEEK_SET) < 0) {
+	rc = RPMRC_FAIL;
+	rpmlog(RPMLOG_ERR, _("Could not seek in file %s: %s\n"),
+		rpm, Fstrerror(fd));
+	goto exit;
+    }
+    if (copyFile(&ofd, trpm, &fd, rpm)) {
+	rc = RPMRC_FAIL;
+	rpmlog(RPMLOG_ERR, _("copyFile failed\n"));
+	goto exit;
+    }
+    unlink(trpm);
+
+    /* Recalculate MD5 digest of header+archive */
+    if (Fseek(fd, headerStart, SEEK_SET) < 0) {
+	rc = RPMRC_FAIL;
+	rpmlog(RPMLOG_ERR, _("Could not seek in file %s: %s\n"),
+		rpm, Fstrerror(fd));
+	goto exit;
+    }
+    fdInitDigest(fd, PGPHASHALGO_MD5, 0);
+
+    while (Fread(buf, sizeof(buf[0]), sizeof(buf), fd) > 0)
+	;
+    if (Ferror(fd)) {
+	rc = RPMRC_FAIL;
+	rpmlog(RPMLOG_ERR, _("Fread failed in file %s: %s\n"),
+		rpm, Fstrerror(fd));
+	goto exit;
+    }
+    fdFiniDigest(fd, PGPHASHALGO_MD5, (void **)&MD5, &md5len, 0);
+
+    if (Fseek(fd, sigStart, SEEK_SET) < 0) {
+	rc = RPMRC_FAIL;
+	rpmlog(RPMLOG_ERR, _("Could not seek in file %s: %s\n"),
+		rpm, Fstrerror(fd));
+	goto exit;
+    }
+
+    /* Get payload size from signature tag */
+    archiveSize = headerGetNumber(*sigp, RPMSIGTAG_PAYLOADSIZE);
+    if (!archiveSize) {
+	archiveSize = headerGetNumber(*sigp, RPMSIGTAG_LONGARCHIVESIZE);
+    }
+
+    /* Replace old digests in signature */
+    rc = generateSignature(SHA1, MD5, headerSize, archiveSize, fd);
+    if (rc != RPMRC_OK) {
+	rpmlog(RPMLOG_ERR, _("insertDigests failed\n"));
+	goto exit;
+    }
+
+    if (Fseek(fd, sigStart, SEEK_SET) < 0) {
+	rc = RPMRC_FAIL;
+	rpmlog(RPMLOG_ERR, _("Could not seek in file %s: %s\n"),
+		rpm, Fstrerror(fd));
+	goto exit;
+    }
+
+    rc = rpmReadSignature(fd, sigp, RPMSIGTYPE_HEADERSIG, NULL);
+    if (rc != RPMRC_OK) {
+	rpmlog(RPMLOG_ERR, _("rpmReadSignature failed\n"));
+	goto exit;
+    }
+
+exit:
+    return rc;
+}
+
 /** \ingroup rpmcli
  * Create/modify elements in signature header.
  * @param rpm		path to package
  * @param deleting	adding or deleting signature?
  * @param passPhrase	passPhrase (ignored when deleting)
+ * @param signfiles	sign files if non-zero
  * @return		0 on success, -1 on error
  */
-static int rpmSign(const char *rpm, int deleting, const char *passPhrase)
+static int rpmSign(const char *rpm, int deleting, const char *passPhrase,
+		   int signfiles)
 {
     FD_t fd = NULL;
     FD_t ofd = NULL;
@@ -516,25 +870,15 @@ static int rpmSign(const char *rpm, int deleting, const char *passPhrase)
 	goto exit;
     }
 
-    /* Dump the immutable region (if present). */
-    if (headerGet(sigh, RPMTAG_HEADERSIGNATURES, &utd, HEADERGET_DEFAULT)) {
-	struct rpmtd_s copytd;
-	Header nh = headerNew();
-	Header oh = headerCopyLoad(utd.data);
-	HeaderIterator hi = headerInitIterator(oh);
-	while (headerNext(hi, &copytd)) {
-	    if (copytd.data)
-		headerPut(nh, &copytd, HEADERPUT_DEFAULT);
-	    rpmtdFreeData(&copytd);
+    if (signfiles) {
+	rc = includeFileSignatures(fd, rpm, &sigh, &h, sigStart, headerStart);
+	if (rc != RPMRC_OK) {
+	    rpmlog(RPMLOG_ERR, _("includeFileSignatures failed\n"));
+	    goto exit;
 	}
-	headerFreeIterator(hi);
-	headerFree(oh);
-	rpmtdFreeData(&utd);
-
-	headerFree(sigh);
-	sigh = headerLink(nh);
-	headerFree(nh);
     }
+
+    unloadImmutableRegion(&sigh, RPMTAG_HEADERSIGNATURES, &utd);
     origSigSize = headerSizeof(sigh, HEADER_MAGIC_YES);
 
     if (deleting) {	/* Nuke all the signature tags. */
@@ -566,6 +910,7 @@ static int rpmSign(const char *rpm, int deleting, const char *passPhrase)
 
     /* Try to make new signature smaller to have size of original signature */
     rpmtdReset(&utd);
+
     if (headerGet(sigh, RPMSIGTAG_RESERVEDSPACE, &utd, HEADERGET_MINMEM)) {
 	int diff;
 	int count;
@@ -668,7 +1013,8 @@ exit:
 }
 
 int rpmPkgSign(const char *path,
-		const struct rpmSignArgs * args, const char *passPhrase)
+		const struct rpmSignArgs * args, const char *passPhrase,
+		int signfiles)
 {
     int rc;
 
@@ -684,7 +1030,7 @@ int rpmPkgSign(const char *path,
 	}
     }
 
-    rc = rpmSign(path, 0, passPhrase);
+    rc = rpmSign(path, 0, passPhrase, signfiles);
 
     if (args) {
 	if (args->hashalgo) {
@@ -700,5 +1046,5 @@ int rpmPkgSign(const char *path,
 
 int rpmPkgDelSign(const char *path)
 {
-    return rpmSign(path, 1, NULL);
+    return rpmSign(path, 1, NULL, 0);
 }
diff --git a/sign/rpmsign.h b/sign/rpmsign.h
index 15b3e0f..bdcc14f 100644
--- a/sign/rpmsign.h
+++ b/sign/rpmsign.h
@@ -19,10 +19,12 @@ struct rpmSignArgs {
  * @param path		path to package
  * @param args		signing parameters (or NULL for defaults)
  * @param passPhrase	passphrase for the signing key
+ * @param signfiles	sign files if non-zero
  * @return		0 on success
  */
 int rpmPkgSign(const char *path,
-	       const struct rpmSignArgs * args, const char *passPhrase);
+	       const struct rpmSignArgs * args, const char *passPhrase,
+	       int signfiles);
 
 /** \ingroup rpmsign
  * Delete signature(s) from a package
-- 
1.9.3



More information about the Rpm-maint mailing list