RPM Package Manager, CVS Repository
http://rpm5.org/cvs/
____________________________________________________________________________
Server: rpm5.org Name: Ralf S. Engelschall
Root: /v/rpm/cvs Email: rse@rpm5.org
Module: rpm lua Date: 02-Jan-2008 17:57:37
Branch: HEAD Handle: 2008010216573601
Added files:
rpm/scripts integrity.cfg integrity.fp integrity.lua
integrity.pgp integrity.sh integrity.txt
Modified files:
lua/local llocal.lua
rpm VENDOR devtool.conf rpmqv.c
Log:
On Jeff's request, still contribute my (currently fully OpenPKG-scoped)
RPM Integrity Checking Framework to rpm5.org to make it available to a
wider audience and to include it already in the RPM 5 code base.
This commit provides both the framework for RPM itself (changes to
files rpmqv.c and lua/local/llocal.lua) and example usage (new files
scripts/integrity.*).
In short, the RPM Integrity Checking Framework is based around the
following four technological parts:
1. Declarative Integrity Configuration Specification (.cfg)
2. Programmatic Integrity Validation Processor (.lua)
3. Cryptographic Integrity Authority Public-Key (.pgp)
4. Cryptographic Integrity Authority Fingerprint (.fp)
More detailed documentation can be found in scripts/integrity.txt.
Summary:
Revision Changes Path
1.10 +116 -0 lua/local/llocal.lua
2.26 +5 -0 rpm/VENDOR
2.163 +5 -0 rpm/devtool.conf
1.137 +196 -0 rpm/rpmqv.c
1.1 +25 -0 rpm/scripts/integrity.cfg
1.1 +1 -0 rpm/scripts/integrity.fp
1.1 +65 -0 rpm/scripts/integrity.lua
1.1 +17 -0 rpm/scripts/integrity.pgp
1.1 +59 -0 rpm/scripts/integrity.sh
1.1 +150 -0 rpm/scripts/integrity.txt
____________________________________________________________________________
patch -p0 <<'@@ .'
Index: lua/local/llocal.lua
============================================================================
$ cvs diff -u -r1.9 -r1.10 llocal.lua
--- lua/local/llocal.lua 2 Jan 2008 15:54:32 -0000 1.9
+++ lua/local/llocal.lua 2 Jan 2008 16:57:37 -0000 1.10
@@ -213,3 +213,119 @@
return data
end
+-- ----------------------------------------------------------------------- --
+-- INTEGRITY PROCESSOR GLUE CODE
+-- ----------------------------------------------------------------------- --
+
+-- provide additional "integrity" namespace
+integrity = {}
+
+-- extract plaintext from a PGP clearsigned text
+function integrity.plaintext(text)
+ local s, _, m = util.rmatch(
+ text,
+ "(?s)" ..
+ "^\\s*" ..
+ "-----BEGIN PGP SIGNED MESSAGE-----" ..
+ ".*?" ..
+ "\\r?\\n" ..
+ "\\r?\\n" ..
+ "(.+?\\r?\\n)" ..
+ "-----BEGIN PGP SIGNATURE-----" ..
+ "\\r?\\n" ..
+ ".*" ..
+ "$"
+ )
+ if s == nil then
+ return nil
+ else
+ return m[1]
+ end
+end
+
+-- integrity processor
+function integrity.processor(progname, progmode, spec_fn, spec_cfg, proc_fn, proc_lua, proc_minver)
+ -- optionally read integrity configuration specification
+ if spec_cfg == nil then
+ local fp, error = io.open(spec_fn, "r")
+ if fp == nil then
+ return "ERROR: unable to read Integrity Configuration Specification file: " .. error
+ end
+ spec_cfg = fp:read("*all")
+ io.close(fp)
+ end
+
+ -- optionally read integrity validation processor
+ if proc_lua == nil then
+ local fp, error = io.open(proc_fn, "r")
+ if fp == nil then
+ return "ERROR: unable to read Integrity Validation Processor file: " .. error
+ end
+ proc_lua = fp:read("*all")
+ io.close(fp)
+ end
+
+ -- extract plaintext from "clearsigned" integrity configuration specification
+ spec_cfg = integrity.plaintext(spec_cfg)
+ if spec_cfg == nil then
+ return "ERROR: failed to extract Integrity Configuration Specification plaintext from clearsigned text"
+ end
+
+ -- extract plaintext from "clearsigned" integrity validation processor
+ proc_lua = integrity.plaintext(proc_lua)
+ if proc_lua == nil then
+ return "ERROR: failed to extract Integrity Validation Processor plaintext from clearsigned text"
+ end
+
+ -- parse integrity configuration specification
+ local cfg = {}
+ util.rsubst(spec_cfg,
+ "(?s)" ..
+ "([A-Za-z0-9][A-Za-z0-9-]*):[ \\t]*" ..
+ "([^\\r\\n]*" ..
+ "(?:\\r?\\n[ \\t]+[^\\r\\n]+)*" ..
+ ")\\r?\\n",
+ function (m)
+ local key = m[1]
+ local value = util.rsubst(m[2], "^[ \\t]*(?:\\r?\\n)?(.*?)[ \\t]*$", "$1")
+ cfg[key] = value
+ end
+ )
+
+ -- create validation context object
+ local ctx = {}
+ ctx.rpm = {}
+ ctx.rpm.name = progname;
+ ctx.rpm.mode = progmode;
+
+ -- load integrity validation processor
+ integrity.version = nil
+ integrity.validate = nil
+ local f, e = loadstring(proc_lua)
+ if f == nil then
+ return "ERROR: failed to load Integrity Validation Processor (syntax problem):\n" ..
+ "rpm: Lua: " .. e
+ end
+ f();
+ if type(integrity.validate) ~= "function" then
+ return "ERROR: failed to load Integrity Validation Processor (semantic problem)"
+ end
+
+ -- ensure that integrity validation processor is at least of the minimum required version
+ if type(integrity.version) ~= "string" then
+ return "ERROR: invalid Integrity Validation Processor version (syntax problem)"
+ end
+ if rpm.vercmp(integrity.version, proc_minver) < 0 then
+ return "ERROR: too old Integrity Validation Processor version (semantic problem)"
+ end
+
+ -- call integrity validation processor
+ local result = integrity.validate(ctx, cfg)
+
+ -- return validation result
+ if type(result) ~= "string" then
+ result = "ERROR: invalid result from Integrity Validation Processor"
+ end
+ return result
+end
+
@@ .
patch -p0 <<'@@ .'
Index: rpm/VENDOR
============================================================================
$ cvs diff -u -r2.25 -r2.26 VENDOR
--- rpm/VENDOR 2 Jan 2008 11:29:51 -0000 2.25
+++ rpm/VENDOR 2 Jan 2008 16:57:36 -0000 2.26
@@ -370,6 +370,11 @@
plus "rpm -bb" operations.
________________________________________________________________________
+ Change: integrity-checking
+ Purpose: RPM integrity checking framework
+ Reason: [see scripts/integrity.txt for detailed documentation]
+ ________________________________________________________________________
+
o Name: RPM4DARWIN
Vendor: RPM for Darwin (Mac OS X) <http://rpm4darwin.sourceforge.net/>
Representative: Anders F. Bjorklund <afb@users.sourceforge.net> <afb@rpm5.org>
@@ .
patch -p0 <<'@@ .'
Index: rpm/devtool.conf
============================================================================
$ cvs diff -u -r2.162 -r2.163 devtool.conf
--- rpm/devtool.conf 31 Dec 2007 20:22:38 -0000 2.162
+++ rpm/devtool.conf 2 Jan 2008 16:57:36 -0000 2.163
@@ -857,6 +857,11 @@
LIBS=""
if [ ".$RPM_VENDOR_OPENPKG" != . ]; then
CPPFLAGS="$CPPFLAGS -DRPM_VENDOR_OPENPKG"
+ RPM_INTEGRITY_FP=`cat scripts/integrity.fp`
+ RPM_INTEGRITY_MV=`grep 'integrity\.version' scripts/integrity.lua |\
+ sed -e 's;^.*"\([0-9][0-9.]*\)".*$;\1;'`
+ CPPFLAGS="$CPPFLAGS -DRPM_INTEGRITY_FP=\\\"$RPM_INTEGRITY_FP\\\""
+ CPPFLAGS="$CPPFLAGS -DRPM_INTEGRITY_MV=\\\"$RPM_INTEGRITY_MV\\\""
fi
LDFLAGS="$LDFLAGS -L$base3rd/bin/$platform/openssl-${v_openssl}" # shameless workaround for Neon/XAR
LDFLAGS="$LDFLAGS -L$base3rd/bin/$platform/libxml2-${v_libxml2}/.libs" # shameless workaround for XAR
@@ .
patch -p0 <<'@@ .'
Index: rpm/rpmqv.c
============================================================================
$ cvs diff -u -r1.136 -r1.137 rpmqv.c
--- rpm/rpmqv.c 28 Dec 2007 11:46:58 -0000 1.136
+++ rpm/rpmqv.c 2 Jan 2008 16:57:36 -0000 1.137
@@ -13,10 +13,21 @@
#define IAM_RPMK
#endif
+#if defined(RPM_VENDOR_OPENPKG) /* integrity-checking */
+#include "rpmio_internal.h"
+#endif
+
#include <rpmio.h>
#include <rpmcli.h>
#include <rpmbuild.h>
+#if defined(RPM_VENDOR_OPENPKG) /* integrity-checking */
+#include "rpmns.h"
+#define _RPMLUA_INTERNAL
+#include "rpmlua.h"
+#include "rpmluaext.h"
+#endif
+
#include "rpmdb.h"
#include "rpmps.h"
#include "rpmts.h"
@@ -168,6 +179,187 @@
poptPrintUsage(con, fp, flags);
}
+#if defined(RPM_VENDOR_OPENPKG) /* integrity-checking */
+
+#if !defined(RPM_INTEGRITY_FP)
+#error required RPM_INTEGRITY_FP (fingerprint of public key of integrity authority) not defined!
+#endif
+
+enum {
+ INTEGRITY_OK = 0,
+ INTEGRITY_WARNING = 1,
+ INTEGRITY_ERROR = 2
+};
+
+static void integrity_check_message(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ fprintf(stderr, "rpm: ATTENTION: INTEGRITY CHECKING DETECTED AN ENVIRONMENT ANOMALY!\nrpm: ");
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ return;
+}
+
+static void integrity_check(const char *progname, enum modes progmode_num)
+{
+ rpmts ts = NULL;
+ rpmlua lua = NULL;
+ char *spec_fn = NULL;
+ char *proc_fn = NULL;
+ char *pkey_fn = NULL;
+ char *spec = NULL;
+ char *proc = NULL;
+ const char *result = NULL;
+ const char *error = NULL;
+ int xx;
+ const char *progmode;
+ int rc = INTEGRITY_ERROR;
+
+ /* determine paths of integrity checking related files */
+ spec_fn = rpmExpand("%{?_integrity_spec_cfg}%{!?_integrity_spec_cfg:scripts/integrity.cfg}", NULL);
+ if (spec_fn == NULL || spec_fn[0] == '\0') {
+ integrity_check_message("ERROR: Integrity Configuration Specification file not configured.\n"
+ "rpm: HINT: macro %%{_integrity_spec_cfg} not configured correctly.\n");
+ goto failure;
+ }
+ proc_fn = rpmExpand("%{?_integrity_proc_lua}%{!?_integrity_proc_lua:scripts/integrity.lua}", NULL);
+ if (proc_fn == NULL || proc_fn[0] == '\0') {
+ integrity_check_message("ERROR: Integrity Validation Processor file not configured.\n"
+ "rpm: HINT: macro %%{_integrity_proc_lua} not configured correctly.\n");
+ goto failure;
+ }
+ pkey_fn = rpmExpand("%{?_integrity_pkey_pgp}%{!?_integrity_pkey_pgp:scripts/integrity.pgp}", NULL);
+ if (pkey_fn == NULL || pkey_fn[0] == '\0') {
+ integrity_check_message("ERROR: Integrity Autority Public-Key file not configured.\n"
+ "rpm: HINT: macro %%{_integrity_pkey_pgp} not configured correctly.\n");
+ goto failure;
+ }
+
+ /* create RPM transaction environment and open RPM database */
+ ts = rpmtsCreate();
+ (void)rpmtsOpenDB(ts, O_RDONLY);
+
+ /* check signature on integrity configuration specification file */
+ if (rpmnsProbeSignature(ts, spec_fn, NULL, pkey_fn, RPM_INTEGRITY_FP, 0) != RPMRC_OK) {
+ integrity_check_message("ERROR: Integrity Configuration Specification file contains invalid signature.\n"
+ "rpm: HINT: Check file \"%s\".\n", spec_fn);
+ goto failure;
+ }
+
+ /* check signature on integrity validation processor file */
+ if (rpmnsProbeSignature(ts, proc_fn, NULL, pkey_fn, RPM_INTEGRITY_FP, 0) != RPMRC_OK) {
+ integrity_check_message("ERROR: Integrity Validation Processor file contains invalid signature.\n"
+ "rpm: HINT: Check file \"%s\".\n", proc_fn);
+ goto failure;
+ }
+
+ /* load integrity configuration specification file */
+ spec = NULL;
+ xx = rpmioSlurp(spec_fn, (uint8_t **)&spec, NULL);
+ if (!(xx == 0 && spec != NULL)) {
+ integrity_check_message("ERROR: Unable to load Integrity Configuration Specification file.\n"
+ "rpm: HINT: Check file \"%s\".\n", spec_fn);
+ goto failure;
+ }
+
+ /* load integrity validation processor file */
+ proc = NULL;
+ xx = rpmioSlurp(proc_fn, (uint8_t **)&proc, NULL);
+ if (!(xx == 0 && proc != NULL)) {
+ integrity_check_message("ERROR: Unable to load Integrity Validation Processor file.\n"
+ "rpm: HINT: Check file \"%s\".\n", proc_fn);
+ goto failure;
+ }
+
+ /* provision program name and mode */
+ if (progname == NULL || progname[0] == '\0')
+ progname = "rpm";
+ switch (progmode_num) {
+ case MODE_QUERY: progmode = "query"; break;
+ case MODE_VERIFY: progmode = "verify"; break;
+ case MODE_CHECKSIG: progmode = "checksig"; break;
+ case MODE_RESIGN: progmode = "resign"; break;
+ case MODE_INSTALL: progmode = "install"; break;
+ case MODE_ERASE: progmode = "erase"; break;
+ case MODE_BUILD: progmode = "build"; break;
+ case MODE_REBUILD: progmode = "rebuild"; break;
+ case MODE_RECOMPILE: progmode = "recompile"; break;
+ case MODE_TARBUILD: progmode = "tarbuild"; break;
+ case MODE_INITDB: progmode = "initdb"; break;
+ case MODE_REBUILDDB: progmode = "rebuilddb"; break;
+ case MODE_VERIFYDB: progmode = "verifydb"; break;
+ case MODE_UNKNOWN: progmode = "unknown"; break;
+ default: progmode = "unknown"; break;
+ }
+
+ /* execute Integrity Validation Processor via Lua glue code */
+ lua = rpmluaNew();
+ rpmluaSetPrintBuffer(lua, 1);
+ rpmluaextActivate(lua);
+ lua_getfield(lua->L, LUA_GLOBALSINDEX, "integrity");
+ lua_getfield(lua->L, -1, "processor");
+ lua_remove(lua->L, -2);
+ lua_pushstring(lua->L, progname);
+ lua_pushstring(lua->L, progmode);
+ lua_pushstring(lua->L, spec_fn);
+ lua_pushstring(lua->L, spec);
+ lua_pushstring(lua->L, proc_fn);
+ lua_pushstring(lua->L, proc);
+#ifdef RPM_INTEGRITY_MV
+ lua_pushstring(lua->L, RPM_INTEGRITY_MV);
+#else
+ lua_pushstring(lua->L, "0");
+#endif
+ if (lua_pcall(lua->L, 7, 1, 0) != 0) {
+ error = lua_isstring(lua->L, -1) ? lua_tostring(lua->L, -1) : "unknown error";
+ lua_pop(lua->L, 1);
+ integrity_check_message("ERROR: Failed to execute Integrity Validation Processor.\n"
+ "rpm: ERROR: Lua: %s.\n"
+ "rpm: HINT: Check file \"%s\".\n", error, proc_fn);
+ goto failure;
+ }
+
+ /* check Integrity Validation Processor results */
+ if (!lua_isstring(lua->L, -1)) {
+ integrity_check_message("ERROR: Failed to fetch Integrity Validation Processor results.\n"
+ "rpm: HINT: Check file \"%s\".\n", proc_fn);
+ goto failure;
+ }
+ result = lua_tostring(lua->L, -1);
+ if (strcmp(result, "OK") == 0)
+ rc = INTEGRITY_OK;
+ else if (strncmp(result, "WARNING:", 8) == 0) {
+ rc = INTEGRITY_WARNING;
+ integrity_check_message("%s\n", result);
+ }
+ else {
+ rc = INTEGRITY_ERROR;
+ integrity_check_message("%s\n", result);
+ }
+
+ /* cleanup processing */
+ failure:
+ if (lua != NULL)
+ rpmluaFree(lua);
+ if (ts != NULL)
+ ts = rpmtsFree(ts);
+ if (spec != NULL)
+ spec = _free(spec);
+ if (proc != NULL)
+ proc = _free(proc);
+
+ /* final result handling */
+ if (rc != INTEGRITY_OK) {
+ sleep(4);
+ if (rc == INTEGRITY_ERROR)
+ exit(42);
+ }
+ return;
+}
+#endif
+
/*@-bounds@*/ /* LCL: segfault */
/*@-mods@*/ /* FIX: shrug */
#if !defined(__GLIBC__) && !defined(__LCLINT__)
@@ -472,6 +664,10 @@
}
}
+#if defined(RPM_VENDOR_OPENPKG) /* integrity-checking */
+ integrity_check(__progname, bigMode);
+#endif
+
#if defined(IAM_RPMBT) || defined(IAM_RPMK)
if (0
#if defined(IAM_RPMBT)
@@ .
patch -p0 <<'@@ .'
Index: rpm/scripts/integrity.cfg
============================================================================
$ cvs diff -u -r0 -r1.1 integrity.cfg
--- /dev/null 2008-01-02 17:55:00 +0100
+++ integrity.cfg 2008-01-02 17:57:37 +0100
@@ -0,0 +1,25 @@
+-----BEGIN PGP SIGNED MESSAGE-----
+Hash: SHA1
+
+Id: TRIVIAL
+Name: Trivial Integrity Configuration Specification
+Version: 0.0.0
+Issued: 2008-01-01
+Issuer: rpm-devel@rpm5.org
+
+Description:
+ This is a trivial Integrity Configuration Specification which does
+ nothing but to show the basic syntax of the the key/value pairs and
+ contain a single environment constraint based on package names.
+
+Package:
+ install:^(rpm|gpg-pubkey)$
+ (build|rebuild|recompile|tarbuild):!^(foo|bar|quux)$
+
+-----BEGIN PGP SIGNATURE-----
+Version: GnuPG v2.0.8 (OpenPKG-CURRENT)
+
+iEYEARECAAYFAkd7wjQACgkQ0mcr4lELuksBDQCg8LuAQXmvxy9HgI5B1FmAuw23
+HkcAnj8m04N2AAGEwijWtXaUchq/bJOd
+=WKYF
+-----END PGP SIGNATURE-----
@@ .
patch -p0 <<'@@ .'
Index: rpm/scripts/integrity.fp
============================================================================
$ cvs diff -u -r0 -r1.1 integrity.fp
--- /dev/null 2008-01-02 17:55:00 +0100
+++ integrity.fp 2008-01-02 17:57:37 +0100
@@ -0,0 +1 @@
+A53B9DDD6C1446636CCC9E3BD2672BE2510BBA4B
@@ .
patch -p0 <<'@@ .'
Index: rpm/scripts/integrity.lua
============================================================================
$ cvs diff -u -r0 -r1.1 integrity.lua
--- /dev/null 2008-01-02 17:55:00 +0100
+++ integrity.lua 2008-01-02 17:57:37 +0100
@@ -0,0 +1,65 @@
+-----BEGIN PGP SIGNED MESSAGE-----
+Hash: SHA1
+
+integrity.version = "0.9.0"
+
+function integrity.validate(ctx, cfg)
+ -- display configuration specification information
+ if rpm.verbose() then
+ io.stderr:write("rpm: integrity.validate: ctx.rpm.name = \"" .. ctx.rpm.name .. "\"\n")
+ io.stderr:write("rpm: integrity.validate: ctx.rpm.mode = \"" .. ctx.rpm.mode .. "\"\n")
+ io.stderr:write("rpm: integrity.validate: cfg.Id = \"" .. cfg.Id .. "\"\n")
+ io.stderr:write("rpm: integrity.validate: cfg.Name = \"" .. cfg.Name .. "\"\n")
+ io.stderr:write("rpm: integrity.validate: cfg.Version = \"" .. cfg.Version .. "\"\n")
+ io.stderr:write("rpm: integrity.validate: cfg.Issued = \"" .. cfg.Issued .. "\"\n")
+ io.stderr:write("rpm: integrity.validate: cfg.Issuer = \"" .. cfg.Issuer .. "\"\n")
+ io.stderr:write("rpm: integrity.validate: cfg.Description = \"" .. cfg.Description .. "\"\n")
+ io.stderr:write("rpm: integrity.validate: cfg.Package = \"" .. cfg.Package .. "\"\n")
+ end
+
+ -- process "Package" constraints
+ if cfg.Package ~= nil then
+ -- query RPMDB for names of all installed packages
+ local packages = rpm.query("%{NAME}", true, "*")
+ -- iterate over all constraints
+ for _, constraint in ipairs(util.rsplit(util.rsubst(cfg.Package, "(?s)^\\s*(.+?)\\s*$", "$1"), "(?s)\\s+")) do
+ -- parse constraint
+ local s, _, m = util.rmatch(constraint, "(?s)^(!?)([^:]+):(!?)(.+)$")
+ if s == nil then
+ return "ERROR: Invalid syntax in \"Package\" constraint: \"" .. constraint .. "\"."
+ end
+ local mode_negate = m[1] ~= ""
+ local mode_regex = m[2]
+ local package_negate = m[3] ~= ""
+ local package_regex = m[4]
+ -- apply the mode filter
+ local mode_matches, _, _ = util.rmatch(ctx.rpm.mode, mode_regex);
+ if (not mode_negate and mode_matches ~= nil)
+ or ( mode_negate and mode_matches == nil) then
+ -- apply the package filter to names of all installed packages
+ for _, package in ipairs(packages) do
+ local package_matches, _, _ = util.rmatch(package, package_regex)
+ if not ( (not package_negate and package_matches ~= nil)
+ or ( package_negate and package_matches == nil)) then
+ -- indicate integrity validation error
+ return
+ "ERROR: Installed package \"" .. package .. "\" " ..
+ "not covered by \"Package\" constraint \"" .. package_regex .. "\" " ..
+ "under RPM mode \"" .. ctx.rpm.mode .. "\""
+ end
+ end
+ end
+ end
+ end
+
+ -- indicate integrity validation success
+ return "OK"
+end
+
+-----BEGIN PGP SIGNATURE-----
+Version: GnuPG v2.0.8 (OpenPKG-CURRENT)
+
+iEYEARECAAYFAkd7wjQACgkQ0mcr4lELukvhbQCfZjAV4Ut2fUr+TjQaxorJWgzi
+mDgAnjwALDwR5rFVf07HJXGkg1Q+mxQE
+=bwnz
+-----END PGP SIGNATURE-----
@@ .
patch -p0 <<'@@ .'
Index: rpm/scripts/integrity.pgp
============================================================================
$ cvs diff -u -r0 -r1.1 integrity.pgp
--- /dev/null 2008-01-02 17:55:00 +0100
+++ integrity.pgp 2008-01-02 17:57:37 +0100
@@ -0,0 +1,17 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v2.0.8 (OpenPKG-CURRENT)
+
+mQGiBEd7wjQRBACcZkFtmSEDdnI/sTqQ7r0d5/kAKIT7HkJQ3FaD04C3RBfL7xfW
+ULTmszDO1UWU3ApaVPtFH6PedZ9OPh3saP7Yl6UZ50p63SIP945fVvtkw8gr8uQ8
+7GSc2CjlJ+WisGn4y/Lxv9x/vo9mReyAVN9o0qer6nHOxHZ027lgWB5mnwCg/AOe
+8V6Xk/iC7qfsNS0vsV234ysD/0G/P3e+jKKVPAokILt7HETK/TGToMC0v18pa0OJ
+9XGF4kf0PDoSdbemPHVStSM2KBBMCp5I1Hh5FTcbEvt++U1m1wG+G9gy1yu0uxKw
+AsQMH98ljVc0jybjjC2nKJu+Jz2DhX/QqdL1RYStUjZKFvGFoucFu4Nh1TKZ20Gr
+3fUeA/0ezN89QlvUzxFWMjTTYlqlUdqnzS1Ao1kDhFaVRz6/5pjwfgbesZv4oAe/
+BVL6TP1stP74DSyifQb+266dnMXENStoRLgBlq1dkdnjYgB8RX0jijxjHKm0Ii46
+Mm+8dyyHqIbQ3yOPHS0fCYGCHX0xeHbiriyaNtx+2wY586V2c7Q1SW50ZWdyaXR5
+LUF1dGhvcml0eSA8aW50ZWdyaXR5LWF1dGhvcml0eUBleGFtcGxlLmNvbT6IYAQT
+EQIAIAUCR3vCNAIbAwYLCQgHAwIEFQIIAwQWAgMBAh4BAheAAAoJENJnK+JRC7pL
+TWcAoN3mM5tlb7LV9gWFMEbywaNSh8jvAKDEzrxZYvEQIDIpF3K2dueeZ+SkRQ==
+=qLx+
+-----END PGP PUBLIC KEY BLOCK-----
@@ .
patch -p0 <<'@@ .'
Index: rpm/scripts/integrity.sh
============================================================================
$ cvs diff -u -r0 -r1.1 integrity.sh
--- /dev/null 2008-01-02 17:55:00 +0100
+++ integrity.sh 2008-01-02 17:57:37 +0100
@@ -0,0 +1,59 @@
+#!/bin/sh -x
+
+# preparation
+GNUPGHOME="`pwd`/.integrity"
+export GNUPGHOME
+if [ ! -d $GNUPGHOME ]; then
+ mkdir $GNUPGHOME
+ chmod 700 $GNUPGHOME
+fi
+
+# set name of signer
+integrity_signer_name="Integrity-Authority"
+integrity_signer_mail="integrity-authority@example.com"
+
+# generate signing key pair
+gpg --quiet --gen-key --batch <<EOT
+Key-Type: DSA
+Key-Length: 1024
+Key-Usage: sign
+Name-Real: $integrity_signer_name
+Name-Email: $integrity_signer_mail
+%commit
+EOT
+
+# (re-)clearsign the integrity specification and processor
+clearsign () {
+ perl -e '
+ my $txt; { local $/; $txt = <STDIN>; }
+ $txt =~ s/
+ ^
+ \s*
+ -----BEGIN\s+PGP\s+SIGNED\s+MESSAGE-----
+ .*?
+ \r?\n
+ \r?\n
+ (.+?\r?\n)
+ -----BEGIN\s+PGP\s+SIGNATURE-----\r?\n
+ .*
+ $
+ /$1/xs;
+ print $txt;
+ ' <$1 >$1.tmp && \
+ gpg --quiet --batch --clearsign --output - $1.tmp >$1
+ rm -f $1.tmp
+}
+clearsign integrity.cfg
+clearsign integrity.lua
+
+# export signing public key
+gpg --quiet --export --armor "$integrity_signer_name" >integrity.pgp
+
+# determine signing key fingerprint
+gpg --quiet --fingerprint "Integrity-Authority" 2>&1 |\
+ grep "Key fingerprint" | sed -e 's;.*Key fingerprint = ;;' -e 's;[^A-Z0-9];;g' \
+ >integrity.fp
+
+# cleanup
+rm -rf $GNUPGHOME
+
@@ .
patch -p0 <<'@@ .'
Index: rpm/scripts/integrity.txt
============================================================================
$ cvs diff -u -r0 -r1.1 integrity.txt
--- /dev/null 2008-01-02 17:55:00 +0100
+++ integrity.txt 2008-01-02 17:57:37 +0100
@@ -0,0 +1,150 @@
+
+ RPM Integrity Checking Framework
+ ================================
+
+ Author: Ralf S. Engelschall <rse@engelschall.com>
+ Created: 2007-12-30
+ Modified: 2008-01-02
+
+ Background
+ ----------
+
+ If a software installation is RPM based, RPM plays the role of the
+ central management tool. Hence installation integrity checking should
+ be wired directly into RPM.
+
+ The valid "integrity" of a software installation can be defined
+ arbitrarily. It can mean that all installation files are untouched
+ (still match a particular checksum or message digest), it can mean
+ that a valid run-time license is installed (which was signed by the
+ trusted license issuer), it can mean that the underlying system is
+ operating in a particular mode or in a particular network environment,
+ etc.
+
+ Approach
+ --------
+
+ As it is not possible for a generic tool like RPM to cover all
+ possibilities, RPM provides just the "framework" for hooking arbitrary
+ integrity checks into its operation. For flexibility the integrity
+ checking is split into the following four technological parts:
+
+ 1. Declarative Integrity Configuration Specification (.cfg)
+ 2. Programmatic Integrity Validation Processor (.lua)
+ 3. Cryptographic Integrity Authority Public-Key (.pgp)
+ 4. Cryptographic Integrity Authority Fingerprint (.fp)
+
+ In detail these are:
+
+ 1. Declarative Integrity Configuration Specification (.cfg)
+ --------------------------------------------------------
+
+ This is a static and declarative specification of the integrity
+ configuration. It is a text file consisting of key/value pairs
+ matching the following regular expression:
+
+ ([A-Za-z0-9][A-Za-z0-9-]*):[ \t]*([^\r\n]*(?:\r?\n
+ [ \t]+[^\r\n]+)*)\r?\n
+
+ Examples are key/value pairs are "Foo: Bar\n" or "Foo: Bar\n Baz\n
+ Quux\n". The integrity configuration usually is individually issued
+ for a particular software installation or a class of similar
+ software installations. The supported and required key/value pairs
+ depend entirely on the particular Integrity Validation Processor.
+
+ The file has to be PGP "clear-signed" by the Integrity Authority.
+
+ 2. Programmatic Integrity Validation Processor (.lua)
+ --------------------------------------------------
+
+ This is a dynamic and programmatic integrity validation processor.
+ It is a Lua script file consisting of at least a definition of
+ the Lua string variable "integrity.version" and the Lua function
+ "integrity.validate(ctx, spec)". The minimum valid Lua script is:
+
+ integrity.version = "0"
+ function integrity.validate(ctx, spec)
+ return "OK"
+ end
+
+ The integrity validation processor usually is generic and reusable
+ across a larger set of integrity configuration specifications. It
+ is also implemented outside the RPM scope as its implementation
+ can be arbitrary and RPM independent. It is dynamically loaded and
+ executed under RPM run-time.
+
+ The file has to be PGP "clear-signed" by the Integrity Authority.
+
+ 3. Cryptographic Integrity Authority Public-Key (.pgp)
+ ---------------------------------------------------
+
+ This is the OpenPGP public key of the integrity authority, a
+ party which is trusted by RPM to issue Integrity Configuraton
+ Specifications and Integrity Validation Processors. RPM uses this
+ file to cryptographically check the integrity of the Integrity
+ Configuraton Specification and Integrity Validation Processor. For
+ flexibility reasons it is also kept outside of RPM.
+
+ 4. Cryptographic Integrity Authority Fingerprint (.fp)
+ ---------------------------------------------------
+
+ This is the fingerprint (usually MD5 or SHA-1 message digest) of
+ the OpenPGP public key of the Integrity Authority Public-Key. It is
+ hard-coded into RPM during the build-time of RPM. It is the root of
+ the integrity trust chain.
+
+ Implementation
+ --------------
+
+ The current implementation for RPM 5 is as following:
+
+ - RPM is built with the fixed/hard-coded Integrity Authority
+ Fingerprint in the C preprocessor variable RPM_INTEGRITY_FP.
+ Additionally, it is built with the fixed/hard-coded minimum version
+ of the Integrity Validation Processor in the C preprocessor
+ variable RPM_INTEGRITY_MV.
+
+ - The paths to the external files are configured via RPM macros:
+ %{_integrity_spec_cfg}: Declarative Integrity Configuration Specification
+ %{_integrity_proc_lua}: Programmatic Integrity Validation Processor
+ %{_integrity_pkey_pgp}: Cryptographic Integrity Authority Public-Key
+
+ - RPM during run-time always runs the integrity checking via:
+
+ 1. ensuring that %{_integrity_spec_cfg} is valid by checking
+ that it was signed by the %{_integrity_pkey_pgp}
+ and that %{_integrity_pkey_pgp} has the fingerprint
+ RPM_INTEGRITY_FP.
+
+ 2. ensuring that %{_integrity_proc_lua} is valid by checking
+ that it was signed by the %{_integrity_pkey_pgp}
+ and that %{_integrity_pkey_pgp} has the fingerprint
+ RPM_INTEGRITY_FP.
+
+ 3. extracting the declarative key/value pairs from
+ the file %{_integrity_spec_cfg} as a Lua table "spec"
+
+ 4. extracting the Lua function integrity.validate(ctx, spec)
+ from the file %{_integrity_proc_lua}
+
+ 5. assembling at least the RPM program name and run-time mode
+ into the Lua table "ctx" as "ctx.rpm.name" and "ctx.rpm.mode".
+
+ 6. executing the Lua function integrity.validate(ctx, spec)
+
+ - The purpose of the integrity.validate(ctx, spec) function
+ is to validate the current run-time environment against the
+ integrity configuration specification. It can return:
+
+ - "OK" to indicate that RPM should silently continue
+ with its regular processing.
+
+ - "WARNING[: ...]" to indicate a detected non-fatal anomaly in the
+ environment. RPM will print the warning, wait a few seconds and
+ then still contunue with its regular processing.
+
+ - "ERROR[: ...]" to indicate a detected fatal anomaly in the
+ environment. RPM will print the error, wait a few seconds and
+ then stop the regular processing immediately and exit with the
+ special process return code 42.
+
@@ .
Received on Wed Jan 2 17:57:37 2008