RPM Package Manager, CVS Repository http://rpm5.org/cvs/ ____________________________________________________________________________
Server: rpm5.org Name: Ralf S. Engelschall Root: /v/rpm/cvs Email: [EMAIL PROTECTED] 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 <[EMAIL PROTECTED]> <[EMAIL PROTECTED]> @@ . 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 + /[EMAIL PROTECTED]@*/ /* LCL: segfault */ /[EMAIL PROTECTED]@*/ /* 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: [EMAIL PROTECTED] + +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="[EMAIL PROTECTED]" + +# 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 <[EMAIL PROTECTED]> + 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. + @@ . ______________________________________________________________________ RPM Package Manager http://rpm5.org CVS Sources Repository rpm-cvs@rpm5.org