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

fin at linux.vnet.ibm.com fin at linux.vnet.ibm.com
Fri Aug 29 17:33:30 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 RPMTAG_FILESIGNATURES, an 
RPM macro %_file_signing_key, new options --fskpath, --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.

Package files can also be signed during install with the new option --signfiles.

rpm -i [--signfiles] PACKAGE_FILE ...

v2: Added --signfiles option to rpminstall. File signing key can be configured
on the command line with --fskpath. Added missing file (plugins/ima.c). Fixed 
typo in rpmDigestAlgo.

Signed-off-by: Fionnuala Gunter <fin at linux.vnet.ibm.com>
---
 configure.ac         |   8 ++
 doc/rpm.8            |  28 +++--
 doc/rpmsign.8        |  22 +++-
 lib/Makefile.am      |   3 +-
 lib/fsm.c            |  70 ++++++++++-
 lib/poptI.c          |   7 ++
 lib/rpmcli.h         |   2 +
 lib/rpminstall.c     |  10 +-
 lib/rpmplugin.h      |   3 +-
 lib/rpmplugins.c     |   5 +-
 lib/rpmplugins.h     |   4 +-
 lib/rpmsignfiles.c   | 130 +++++++++++++++++++++
 lib/rpmsignfiles.h   |  45 ++++++++
 lib/rpmtag.h         |   1 +
 lib/rpmts.c          |  15 +++
 lib/rpmts.h          |  15 +++
 lib/rpmts_internal.h |   2 +
 lib/rpmtypes.h       |   1 +
 macros.in            |   1 +
 plugins/Makefile.am  |   4 +
 plugins/ima.c        |  83 ++++++++++++++
 rpmpopt.in           |   1 +
 rpmsign.c            |  14 ++-
 sign/rpmgensig.c     | 319 +++++++++++++++++++++++++++++++++++++++++++++++----
 sign/rpmsign.h       |   7 +-
 25 files changed, 752 insertions(+), 48 deletions(-)
 create mode 100644 lib/rpmsignfiles.c
 create mode 100644 lib/rpmsignfiles.h
 create mode 100644 plugins/ima.c

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/rpm.8 b/doc/rpm.8
index e583009..4079f71 100644
--- a/doc/rpm.8
+++ b/doc/rpm.8
@@ -84,15 +84,14 @@ rpm \- RPM Package Manager
 
 
  [\fB--allfiles\fR] [\fB--badreloc\fR] [\fB--excludepath \fIOLDPATH\fB\fR]
- [\fB--excludedocs\fR] [\fB--force\fR] [\fB-h,--hash\fR]
- [\fB--ignoresize\fR] [\fB--ignorearch\fR] [\fB--ignoreos\fR]
- [\fB--includedocs\fR] [\fB--justdb\fR] [\fB--nocollections\fR]
- [\fB--nodeps\fR] [\fB--nodigest\fR] [\fB--nosignature\fR]
- [\fB--noorder\fR] [\fB--noscripts\fR] [\fB--notriggers\fR] 
- [\fB--oldpackage\fR] [\fB--percent\fR] [\fB--prefix \fINEWPATH\fB\fR]
- [\fB--relocate \fIOLDPATH\fB=\fINEWPATH\fB\fR]
- [\fB--replacefiles\fR] [\fB--replacepkgs\fR]
- [\fB--test\fR]
+ [\fB--excludedocs\fR] [\fB--force\fR] [\fB--fskpath \fIKEY\fB\fR]
+ [\fB-h,--hash\fR] [\fB--ignoresize\fR] [\fB--ignorearch\fR]
+ [\fB--ignoreos\fR] [\fB--includedocs\fR] [\fB--justdb\fR]
+ [\fB--nocollections\fR] [\fB--nodeps\fR] [\fB--nodigest\fR]
+ [\fB--nosignature\fR] [\fB--noorder\fR] [\fB--noscripts\fR]
+ [\fB--notriggers\fR] [\fB--oldpackage\fR] [\fB--percent\fR]
+ [\fB--prefix \fINEWPATH\fB\fR] [\fB--relocate \fIOLDPATH\fB=\fINEWPATH\fB\fR]
+ [\fB--replacefiles\fR] [\fB--replacepkgs\fR] [\fB--signfiles] [\fB--test\fR]
 
 .SH "DESCRIPTION"
 .PP
@@ -232,6 +231,9 @@ Don't install files whose name begins with
 Don't install any files which are marked as documentation
 (which includes man pages and texinfo documents).
 .TP
+\fB--fskpath \fIKEY\fB\fR
+Used with \fB--signfiles\fR, use file signing key \fIKEY\fR.
+.TP
 \fB--force\fR
 Same as using
 \fB--replacepkgs\fR,
@@ -362,6 +364,13 @@ already installed, packages.
 Install the packages even if some of them are already installed
 on this system.
 .TP
+\fB--signfiles\fR
+Sign package files. The macro \fB%_binary_filedigest_algorithm\fR must be set
+before building the package, and the macro must be set to a supported algorithm:
+2, 8, 9, or 10, which represent SHA1, SHA256, SHA384, and SHA512, respectively.
+The file signing key (RSA private key) can be configured on the command line
+with \fB--fskpath\fR or the macro \fB%_file_signing_key\fR.
+.TP
 \fB--test\fR
 Do not install the package, simply check for and report
 potential conflicts.
@@ -875,4 +884,5 @@ what's available.
 Marc Ewing <marc at redhat.com>
 Jeff Johnson <jbj at redhat.com>
 Erik Troan <ewt at redhat.com>
+Fionnuala Gunter <fin at linux.vnet.ibm.com>
 .fi
diff --git a/doc/rpmsign.8 b/doc/rpmsign.8
index 53f2d70..b19f172 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--fskpath \fIKEY\fB\fR] [\fB--signfiles\fR]
+
 .SH DESCRIPTION
 .PP
 Both of the \fB--addsign\fR and \fB--resign\fR
@@ -20,6 +26,19 @@ there is no difference in behavior currently.
 .PP
 Delete all signatures from each package \fIPACKAGE_FILE\fR given.
 
+.SS "SIGN OPTIONS"
+.PP
+.TP
+\fB--fskpath \fIKEY\fB\fR
+Used with \fB--signfiles\fR, use file signing key \fIKEY\fR.
+.TP
+\fB--signfiles\fR
+Sign package files. The macro \fB%_binary_filedigest_algorithm\fR must be set
+before building the package, and the macro must be set to a supported algorithm:
+2, 8, 9, or 10, which represent SHA1, SHA256, SHA384, and SHA512, respectively.
+The file signing key (RSA private key) can be configured on the command line
+with \fB--fskpath\fR or the macro \fB%_file_signing_key\fR.
+
 .SS "USING GPG TO SIGN PACKAGES"
 .PP
 In order to sign packages using GPG, \fBrpm\fR
@@ -78,4 +97,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/Makefile.am b/lib/Makefile.am
index a65eb80..f80a47a 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -38,7 +38,8 @@ librpm_la_SOURCES = \
 	verify.c rpmlock.c rpmlock.h misc.h relocation.c \
 	rpmscript.h rpmscript.c legacy.c \
 	rpmchroot.c rpmchroot.h \
-	rpmplugins.c rpmplugins.h rpmplugin.h rpmug.c rpmug.h
+	rpmplugins.c rpmplugins.h rpmplugin.h rpmug.c rpmug.h \
+	rpmsignfiles.c rpmsignfiles.h
 
 librpm_la_LDFLAGS = -version-info $(rpm_version_info)
 
diff --git a/lib/fsm.c b/lib/fsm.c
index 7ae4747..dce09b9 100644
--- a/lib/fsm.c
+++ b/lib/fsm.c
@@ -20,6 +20,8 @@
 #include "lib/rpmte_internal.h"	/* XXX rpmfs */
 #include "lib/rpmplugins.h"	/* rpm plugins hooks */
 #include "lib/rpmug.h"
+#include "lib/rpmlib.h"
+#include "lib/rpmsignfiles.h"	/* getDigestAlgo, getDigestLength, signFile */
 
 #include "debug.h"
 
@@ -449,7 +451,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,
@@ -815,19 +818,57 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files,
     struct stat sb;
     int saveerrno = errno;
     int rc = 0;
-    int nodigest = (rpmtsFlags(ts) & RPMTRANS_FLAG_NOFILEDIGEST) ? 1 : 0;
+    int nodigest = 0;
     int firsthardlink = -1;
     int skip;
     rpmFileAction action;
     char *tid = NULL;
     const char *suffix;
     char *fpath = NULL;
+    Header h = rpmteHeader(te);
+    struct rpmtd_s digests, sigs;
+    int signFiles = rpmtsSignFiles(ts);
+    const char *key;
+    const char *algo;
+    const char *digest;
+    const char *sig;
+    int diglen = 0;
 
     if (fi == NULL) {
 	rc = RPMERR_BAD_MAGIC;
 	goto exit;
     }
 
+    if (h == NULL) {
+	rc = RPMRC_FAIL;
+	goto exit;
+    }
+
+    if (signFiles) {
+	algo = getDigestAlgo(h);
+	if (!algo) {
+	    rc = RPMRC_FAIL;
+	    goto exit;
+	}
+
+	diglen = getDigestLength(h);
+	if (diglen < 0) {
+	    rc = RPMRC_FAIL;
+	    goto exit;
+	}
+
+	key = rpmExpand("%{_file_signing_key}", NULL);
+	if (rstreq(key, "")) {
+	    rc = RPMRC_FAIL;
+	    fprintf(stderr, _("You must set \"$$_file_signing_key\" in your macro file or on the command line with --fskpath\n"));
+	    rpmlog(RPMLOG_ERR, _("no file signing key provided\n"));
+	}
+
+	headerGet(h, RPMTAG_FILEDIGESTS, &digests, HEADERGET_MINMEM);
+    } else {
+	headerGet(h, RPMTAG_FILESIGNATURES, &sigs, HEADERGET_MINMEM);
+    }
+
     /* transaction id used for temporary path suffix while installing */
     rasprintf(&tid, ";%08x", (unsigned)rpmtsGetTid(ts));
 
@@ -952,9 +993,27 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files,
 	if (rc)
 	    *failedFile = xstrdup(fpath);
 
+	/* sign executable files */
+	if (sb.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH) && signFiles) {
+	    digest = rpmtdNextString(&digests);
+	    sig = signFile(algo, digest, diglen, key);
+	    if (!sig) {
+		rpmlog(RPMLOG_ERR, _("signFile failed\n"));
+		goto exit;
+	    }
+	}
+	/* or get file signatures from header */
+	else if (sb.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH)) {
+	    sig = rpmtdNextString(&sigs);
+	} else {
+	    sig = NULL;
+	    rpmtdNextString(&sigs);
+	    rpmtdNextString(&digests);
+	}
+
 	/* 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 +1023,9 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files,
 exit:
 
     /* No need to bother with close errors on read */
+    rpmtdFreeData(&digests);
+    rpmtdFreeData(&sigs);
+    headerFree(h);
     rpmfiArchiveClose(fi);
     rpmfiFree(fi);
     Fclose(payload);
@@ -1034,7 +1096,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/poptI.c b/lib/poptI.c
index e21cde1..699c8cd 100644
--- a/lib/poptI.c
+++ b/lib/poptI.c
@@ -16,8 +16,10 @@ struct rpmInstallArguments_s rpmIArgs = {
     0,			/* numRelocations */
     0,			/* noDeps */
     0,			/* incldocs */
+    0,			/* signFiles */
     NULL,		/* relocations */
     NULL,		/* prefix */
+    NULL,		/* fileSigningKey */
 };
 
 #define	POPT_RELOCATE		-1021
@@ -146,6 +148,9 @@ struct poptOption rpmInstallPoptTable[] = {
 	(INSTALL_UPGRADE|INSTALL_FRESHEN|INSTALL_INSTALL),
 	N_("upgrade package(s) if already installed"),
 	N_("<packagefile>+") },
+ { "fskpath", '\0', POPT_ARG_STRING, &rpmIArgs.fileSigningKey, 0,
+	N_("use file signing key <key>"),
+	N_("<key>") },
  { "hash", 'h', POPT_BIT_SET, &rpmIArgs.installInterfaceFlags, INSTALL_HASH,
 	N_("print hash marks as package installs (good with -v)"), NULL},
  { "ignorearch", '\0', POPT_BIT_SET,
@@ -243,6 +248,8 @@ struct poptOption rpmInstallPoptTable[] = {
  { "replacepkgs", '\0', POPT_BIT_SET,
 	&rpmIArgs.probFilter, RPMPROB_FILTER_REPLACEPKG,
 	N_("reinstall if the package is already present"), NULL},
+ { "signfiles", '\0', POPT_ARG_NONE, &rpmIArgs.signFiles, 0,
+	N_("sign package files"), NULL},
  { "test", '\0', POPT_BIT_SET, &rpmIArgs.transFlags, RPMTRANS_FLAG_TEST,
 	N_("don't install, but tell if it would work or not"), NULL},
  { "upgrade", 'U', POPT_BIT_SET,
diff --git a/lib/rpmcli.h b/lib/rpmcli.h
index 48e8250..ff89171 100644
--- a/lib/rpmcli.h
+++ b/lib/rpmcli.h
@@ -339,8 +339,10 @@ struct rpmInstallArguments_s {
     int numRelocations;
     int noDeps;
     int incldocs;
+    int signFiles;
     rpmRelocation * relocations;
     char * prefix;
+    char * fileSigningKey;
 };
 
 /** \ingroup rpmcli
diff --git a/lib/rpminstall.c b/lib/rpminstall.c
index 94840ea..764d8bf 100644
--- a/lib/rpminstall.c
+++ b/lib/rpminstall.c
@@ -11,6 +11,7 @@
 #include <rpm/rpmds.h>
 #include <rpm/rpmts.h>
 #include <rpm/rpmlog.h>
+#include <rpm/rpmmacro.h>
 #include <rpm/rpmfileutil.h>
 
 #include "lib/rpmgi.h"
@@ -404,7 +405,14 @@ int rpmInstall(rpmts ts, struct rpmInstallArguments_s * ia, ARGV_t fileArgv)
 
     relocations = ia->relocations;
 
-    setNotifyFlag(ia, ts); 
+    setNotifyFlag(ia, ts);
+
+    rpmtsSetSignFiles(ts, ia->signFiles);
+
+    if (ia->fileSigningKey) {
+	addMacro(NULL, "_file_signing_key", NULL, ia->fileSigningKey,
+		 RMIL_GLOBAL);
+    }
 
     if ((eiu->relocations = relocations) != NULL) {
 	while (eiu->relocations->oldPath)
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/rpmsignfiles.c b/lib/rpmsignfiles.c
new file mode 100644
index 0000000..9c33103
--- /dev/null
+++ b/lib/rpmsignfiles.c
@@ -0,0 +1,130 @@
+/**
+ * Copyright (C) 2014 IBM Corporation
+ *
+ * Author: Fionnuala Gunter <fin at linux.vnet.ibm.com>
+ */
+
+#include "system.h"
+#include "imaevm.h"
+
+#include <rpm/rpmlog.h>		/* rpmlog */
+#include <rpm/rpmstring.h>	/* rnibble */
+#include <rpm/rpmpgp.h>		/* rpmDigestLength */
+#include "lib/header.h"		/* HEADERGET_MINMEM */
+#include "lib/rpmtypes.h"	/* rpmRC */
+
+#include "lib/rpmsignfiles.h"
+
+static 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;
+    }
+}
+
+char *getDigestAlgo(Header h)
+{
+    struct rpmtd_s digalgo;
+    uint32_t *dalgo;
+    char *algo;
+
+    headerGet(h, RPMTAG_FILEDIGESTALGO, &digalgo, HEADERGET_MINMEM);
+    dalgo = rpmtdGetUint32(&digalgo);
+    if (!dalgo) {
+        rpmlog(RPMLOG_ERR, _("rpmtdGetUint32 failed\n"));
+        return NULL;
+    }
+    algo = rpmDigestAlgo(*dalgo);
+    return algo;
+}
+
+int getDigestLength(Header h)
+{
+    struct rpmtd_s digalgo;
+    uint32_t *dalgo;
+    int diglen;
+
+    headerGet(h, RPMTAG_FILEDIGESTALGO, &digalgo, HEADERGET_MINMEM);
+    dalgo = rpmtdGetUint32(&digalgo);
+    if (!dalgo) {
+        rpmlog(RPMLOG_ERR, _("rpmtdGetUint32 failed\n"));
+        return -1;
+    }
+
+    diglen = rpmDigestLength(*dalgo);
+    return diglen;
+}
+
+char *signFile(const char *algo, const char *fdigest, int diglen, const char *key)
+{
+   char *fsignature;
+   unsigned char digest[BUFSIZ];
+   unsigned char signature[BUFSIZ];
+   int siglen;
+
+#ifndef IMAEVM
+    rpmlog(RPMLOG_ERR, _("missing libimaevm\n"));
+    return NULL;
+#endif
+
+   /* convert file digest hex to binary */
+   memset(digest, 0, BUFSIZ);
+   for (int i = 0; i < diglen; ++i, fdigest += 2)
+        digest[i] = (rnibble(fdigest[0]) << 4) | rnibble(fdigest[1]);
+
+   /* prepare file 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"));
+        return NULL;
+   }
+
+   /* convert file signature binary to hex */
+   fsignature = pgpHexStr(signature, siglen+1);
+   return fsignature;
+}
+
+rpmRC signFiles(Header h, const char *key)
+{
+    struct rpmtd_s digests;
+    const char *algo;
+    const char *digest;
+    char *signature;
+    int diglen;
+
+    algo = getDigestAlgo(h);
+    if (!algo) {
+        rpmlog(RPMLOG_ERR, _("getDigestAlgo failed\n"));
+        return RPMRC_FAIL;
+    }
+
+    diglen = getDigestLength(h);
+    if (diglen < 0) {
+        rpmlog(RPMLOG_ERR, _("getDigestLength failed\n"));
+        return RPMRC_FAIL;
+    }
+
+    headerGet(h, RPMTAG_FILEDIGESTS, &digests, HEADERGET_MINMEM);
+    while ((digest = rpmtdNextString(&digests))) {
+        signature = signFile(algo, digest, diglen, key);
+        if (!signature) {
+            rpmlog(RPMLOG_ERR, _("signFile failed\n"));
+            return RPMRC_FAIL;
+        }
+        if (!headerPutString(h, RPMTAG_FILESIGNATURES, signature)) {
+            rpmlog(RPMLOG_ERR, _("headerPutString failed\n"));
+            return RPMRC_FAIL;
+        }
+    }
+    return RPMRC_OK;
+}
diff --git a/lib/rpmsignfiles.h b/lib/rpmsignfiles.h
new file mode 100644
index 0000000..05459f0
--- /dev/null
+++ b/lib/rpmsignfiles.h
@@ -0,0 +1,45 @@
+#ifndef H_RPMSIGNFILES
+#define H_RPMSIGNFILES
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Get file digest algorithm from header
+ * @param header	package header
+ * @return		file digest algorithm
+ */
+char *getDigestAlgo(Header h);
+
+/**
+ * Get file digest length from header
+ * @param header	package header
+ * @return		file digest length
+ */
+int getDigestLength(Header h);
+
+/**
+ * Sign a file digest with libimaevm
+ * @param algo		file digest algorithm
+ * @param fdigest	file digest hex
+ * @param diglen	digest length
+ * @param key		file signing key
+ * @return		file signature hex
+ */
+char *signFile(const char *algo, const char *fdigest, int diglen,
+	       const char *key);
+
+/**
+ * Sign file digests in header and store the signatures in header
+ * @param h		package header
+ * @param key		signing key
+ * @return		RPMRC_OK on success
+ */
+rpmRC signFiles(Header h, const char *key);
+
+#ifdef _cplusplus
+}
+#endif
+
+#endif /* H_RPMSIGNFILES */
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/lib/rpmts.c b/lib/rpmts.c
index a3b4ed2..6d9eb30 100644
--- a/lib/rpmts.c
+++ b/lib/rpmts.c
@@ -897,6 +897,21 @@ int rpmtsSetNotifyCallback(rpmts ts,
     return 0;
 }
 
+int rpmtsSignFiles(rpmts ts)
+{
+    return ts ? ts->signFiles : NULL;
+}
+
+int rpmtsSetSignFiles(rpmts ts, int signFiles)
+{
+    if (ts == NULL) {
+	return -1;
+    }
+
+    ts->signFiles = signFiles;
+    return 0;
+}
+
 tsMembers rpmtsMembers(rpmts ts)
 {
     return (ts != NULL) ? ts->members : NULL;
diff --git a/lib/rpmts.h b/lib/rpmts.h
index 5231c80..5f45972 100644
--- a/lib/rpmts.h
+++ b/lib/rpmts.h
@@ -393,6 +393,21 @@ const char * rpmtsRootDir(rpmts ts);
  */
 int rpmtsSetRootDir(rpmts ts, const char * rootDir);
 
+/**
+ * Get transaction sign files flag
+ * @param ts		transaction set
+ * @return		non-zero if package files need to be signed
+ */
+int rpmtsSignFiles(rpmts ts);
+
+/**
+ * Set transaction sign files flag
+ * @param ts		transaction set
+ * @param signFiles	new sign files flag
+ * @return		0 on success, -1 on error
+ */
+int rpmtsSetSignFiles(rpmts ts, int signFiles);
+
 /** \ingroup rpmts
  * Get transaction script file handle, i.e. stdout/stderr on scriptlet execution
  * @param ts		transaction set
diff --git a/lib/rpmts_internal.h b/lib/rpmts_internal.h
index 0caa7cb..a196932 100644
--- a/lib/rpmts_internal.h
+++ b/lib/rpmts_internal.h
@@ -68,6 +68,8 @@ struct rpmts_s {
     rpmPlugins plugins;		/*!< Transaction plugins */
 
     int nrefs;			/*!< Reference count. */
+
+    int signFiles;		/*!< Sign package files. */
 };
 
 #ifdef __cplusplus
diff --git a/lib/rpmtypes.h b/lib/rpmtypes.h
index 1948d18..fa0bab9 100644
--- a/lib/rpmtypes.h
+++ b/lib/rpmtypes.h
@@ -91,6 +91,7 @@ typedef struct rpmSpec_s * rpmSpec;
 
 typedef struct rpmRelocation_s rpmRelocation;
 
+typedef struct rpmFileSignature_s rpmFileSignature;
 
 /** \ingroup rpmtypes 
  * RPM IO file descriptor type
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/plugins/ima.c b/plugins/ima.c
new file mode 100644
index 0000000..6b739db
--- /dev/null
+++ b/plugins/ima.c
@@ -0,0 +1,83 @@
+/**
+ * Copyright (C) 2014 IBM Corporation
+ *
+ * Author: Fionnuala Gunter <fin at linux.vnet.ibm.com>
+ */
+#include <syslog.h>
+
+#include <sys/types.h>
+#include <sys/xattr.h>
+#include <rpm/rpmts.h>
+#include <rpm/rpmlog.h>
+#include <lib/rpmplugin.h>
+#include <errno.h>
+#include <ctype.h>
+
+#include "debug.h"
+
+#define XATTR_NAME_IMA "security.ima"
+
+static int hex_to_bin(char ch)
+{
+	if ((ch >= '0') && (ch <= '9'))
+		return ch - '0';
+	ch = tolower(ch);
+	if ((ch >= 'a') && (ch <= 'f'))
+		return ch - 'a' + 10;
+	return -1;
+}
+
+static int hex2bin(char *dst, const char *src, size_t count)
+{
+	int hi, lo;
+
+	while (count--) {
+		if (*src == ' ')
+			src++;
+
+		hi = hex_to_bin(*src++);
+		lo = hex_to_bin(*src++);
+
+		if ((hi < 0) || (lo < 0))
+			return -1;
+
+		*dst++ = (hi << 4) | lo;
+        }
+	return 0;
+}
+
+static rpmRC ima_init(rpmPlugin plugin, rpmts ts)
+{
+	return RPMRC_OK;
+}
+
+static void ima_cleanup(rpmPlugin plugin)
+{
+}
+
+static rpmRC ima_fsm_file_post(rpmPlugin plugin, rpmfi fi, const char *path,
+                               mode_t file_mode, rpmFsmOp op, const char *sig,
+                               int res)
+{
+	rpmRC rc = RPMRC_OK;
+	if (sig == NULL) {
+		return rc;
+	}
+
+	int siglen = strlen(sig) + 1;
+	char bin[siglen/2];
+	hex2bin(bin, sig, siglen);
+	rc = lsetxattr(path, XATTR_NAME_IMA, bin, siglen/2, 0);
+
+	if (rpmIsDebug())
+		rpmlog(RPMLOG_DEBUG, "lsetxattr: (%s) %s\n",
+			path, (rc < 0 ? strerror(errno) : ""));
+
+	return rc;
+}
+
+struct rpmPluginHooks_s ima_hooks = {
+	.init = ima_init,
+	.cleanup = ima_cleanup,
+	.fsm_file_post = ima_fsm_file_post,
+};
diff --git a/rpmpopt.in b/rpmpopt.in
index 036ab4e..df5e2ec 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..a2692b9 100644
--- a/rpmsign.c
+++ b/rpmsign.c
@@ -20,6 +20,9 @@ enum modes {
 
 static int mode = 0;
 
+static int signfiles = 0;
+static char * fileSigningKey = NULL;
+
 static struct poptOption signOptsTable[] = {
     { "addsign", '\0', (POPT_ARG_VAL|POPT_ARGFLAG_OR), &mode, MODE_ADDSIGN,
 	N_("sign package(s)"), NULL },
@@ -27,6 +30,11 @@ 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},
+    { "fskpath", '\0', POPT_ARG_STRING, &fileSigningKey, 0,
+	N_("use file signing key <key>"),
+	N_("<key>") },
     POPT_TABLEEND
 };
 
@@ -119,6 +127,10 @@ static int doSign(poptContext optCon)
 	goto exit;
     }
 
+    if (fileSigningKey && signfiles) {
+	addMacro(NULL, "_file_signing_key", NULL, fileSigningKey, RMIL_GLOBAL);
+    }
+
     /* XXX FIXME: eliminate obsolete getpass() usage */
     passPhrase = getpass(_("Enter pass phrase: "));
     passPhrase = (passPhrase != NULL) ? rstrdup(passPhrase) : NULL;
@@ -127,7 +139,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..f07c87e 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,23 @@
 #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 "lib/rpmsignfiles.h"		/* signFiles */
+#include "rpmio/rpmio_internal.h"	/* fdInitDigest, fdFiniDigest */
 
 #include "debug.h"
 
@@ -88,6 +100,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 +137,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 +472,277 @@ 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 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);
+    }
+}
+
+/**
+ * Include file 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;
+    char *trpm = NULL;
+    const char *key;
+    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_signing_key}", NULL);
+    if (rstreq(key, "")) {
+	rc = RPMRC_FAIL;
+	fprintf(stderr, _("You must set \"$$_file_signing_key\" in your macro file or on the command line with --fskpath\n"));
+	rpmlog(RPMLOG_ERR, _("no file signing key provided\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 +793,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 +833,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 +936,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 +953,7 @@ int rpmPkgSign(const char *path,
 	}
     }
 
-    rc = rpmSign(path, 0, passPhrase);
+    rc = rpmSign(path, 0, passPhrase, signfiles);
 
     if (args) {
 	if (args->hashalgo) {
@@ -700,5 +969,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..0ec37c1 100644
--- a/sign/rpmsign.h
+++ b/sign/rpmsign.h
@@ -11,6 +11,8 @@ extern "C" {
 struct rpmSignArgs {
     char *keyid;
     pgpHashAlgo hashalgo;
+    int signFiles;
+    const char *fileSigningKey;
     /* ... what else? */
 };
 
@@ -19,10 +21,11 @@ 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);
+int rpmPkgSign(const char *path, 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