Add a brand new rpmsign utility for package signing
authorPanu Matilainen <pmatilai@redhat.com>
Fri, 3 Sep 2010 11:40:18 +0000 (14:40 +0300)
committerPanu Matilainen <pmatilai@redhat.com>
Fri, 3 Sep 2010 12:11:59 +0000 (15:11 +0300)
- Signing (and deleting) are different from everything else in rpm
  in that it needs very little of rpm's facilities. For example access
  to the rpmdb is not needed at all. Splitting this to a separate,
  small utility allows various possibilities, like severely limiting
  its access from SELinux POV, control of signature generation with
  cli arguments (the main rpm executable is already overcrowded with
  options). It's also the first step to allow reasonably splitting
  rpm signing to a separate package; not everybody needs to sign
  packages, yet signing support needs to drag in GPG and whatnot.
- Reimplement / refactor various librpm signature generation helpers
  into somewhat saner internal versions.

Makefile.am
po/POTFILES.in
rpmsign.c [new file with mode: 0644]

index dfee933..4dae4a3 100644 (file)
@@ -78,7 +78,7 @@ pkginclude_HEADERS += build/rpmspec.h
 rpmbindir = `echo $(bindir) | $(SED) -e s,usr/bin,bin,`
 rpmbin_PROGRAMS = rpm
 
-bin_PROGRAMS =         rpm2cpio rpmbuild
+bin_PROGRAMS =         rpm2cpio rpmbuild rpmsign
 
 rpmlibexec_PROGRAMS =
 rpmconfig_SCRIPTS =    find-provides find-requires mkinstalldirs \
@@ -98,6 +98,12 @@ rpm_LDADD =          libcliutils.la
 rpm_LDADD +=           build/librpmbuild.la lib/librpm.la rpmio/librpmio.la
 rpm_LDADD +=           @WITH_NSS_LIB@ @WITH_POPT_LIB@ @WITH_ZLIB_LIB@
 
+rpmsign_SOURCES =      rpmsign.c debug.h system.h
+rpmsign_CPPFLAGS =     $(AM_CPPFLAGS)
+rpmsign_LDADD =                libcliutils.la
+rpmsign_LDADD +=       lib/librpm.la rpmio/librpmio.la
+rpmsign_LDADD +=       @WITH_NSS_LIB@ @WITH_POPT_LIB@ @WITH_ZLIB_LIB@
+
 rpmbuild_SOURCES =     rpmbuild.c debug.h system.h
 rpmbuild_CPPFLAGS =    $(AM_CPPFLAGS)
 rpmbuild_LDADD =       libcliutils.la
@@ -191,8 +197,6 @@ install-exec-hook:
        @LN_S@ ../../bin/rpm $(DESTDIR)$(bindir)/rpmquery
        rm -f $(DESTDIR)$(bindir)/rpmverify
        @LN_S@ ../../bin/rpm $(DESTDIR)$(bindir)/rpmverify
-       rm -f $(DESTDIR)$(bindir)/rpmsign
-       @LN_S@ ../../bin/rpm $(DESTDIR)$(bindir)/rpmsign
        rm -f $(DESTDIR)$(bindir)/rpmdb
        @LN_S@ ../../bin/rpm $(DESTDIR)$(bindir)/rpmdb
 
@@ -206,7 +210,6 @@ install-data-local:
 # XXX to appease distcheck we need to remove "stuff" here...
 uninstall-local:
        @rm -f $(DESTDIR)$(bindir)/rpmquery
-       @rm -f $(DESTDIR)$(bindir)/rpmsign
        @rm -f $(DESTDIR)$(bindir)/rpmverify
        @rm -f $(DESTDIR)$(bindir)/rpmdb
        @rm -rf $(DESTDIR)$(rpmconfigdir)/platform/
index 7ea6694..c1f28b0 100644 (file)
@@ -6,6 +6,7 @@ cliutils.c
 rpm2cpio.c
 rpmqv.c
 rpmbuild.c
+rpmsign.c
 build/build.c
 build/expression.c
 build/files.c
diff --git a/rpmsign.c b/rpmsign.c
new file mode 100644 (file)
index 0000000..61e753e
--- /dev/null
+++ b/rpmsign.c
@@ -0,0 +1,187 @@
+#include "system.h"
+#include <errno.h>
+#include <sys/wait.h>
+
+#include <popt.h>
+#include <rpm/rpmcli.h>
+#include "cliutils.h"
+#include "debug.h"
+
+#if !defined(__GLIBC__) && !defined(__APPLE__)
+char ** environ = NULL;
+#endif
+
+enum modes {
+    MODE_ADDSIGN = (1 << 0),
+    MODE_RESIGN  = (1 << 1),
+    MODE_DELSIGN = (1 << 2),
+};
+
+static int mode = 0;
+
+static struct poptOption optionsTable[] = {
+    { NULL, '\0', POPT_ARG_INCLUDE_TABLE, rpmcliAllPoptTable, 0,
+       N_("Common options for all rpm modes and executables:"), NULL },
+    { "addsign", '\0', (POPT_ARG_VAL|POPT_ARGFLAG_OR), &mode, MODE_ADDSIGN,
+       N_("sign package(s)"), NULL },
+    { "resign", '\0', (POPT_ARG_VAL|POPT_ARGFLAG_OR), &mode, MODE_RESIGN,
+       N_("sign package(s) (identical to --addsign)"), NULL },
+    { "delsign", '\0', (POPT_ARG_VAL|POPT_ARGFLAG_OR), &mode, MODE_DELSIGN,
+       N_("delete package signatures"), NULL },
+
+    POPT_AUTOALIAS
+    POPT_AUTOHELP
+    POPT_TABLEEND
+};
+
+static rpmSigTag lookupSignatureType(void)
+{
+    rpmSigTag rc = 0;
+
+    char *name = rpmExpand("%{?_signature}", NULL);
+    if (!(name && *name != '\0'))
+       rc = 0;
+    else if (!rstrcasecmp(name, "none"))
+       rc = 0;
+    else if (!rstrcasecmp(name, "pgp"))
+       rc = RPMSIGTAG_PGP;
+    else if (!rstrcasecmp(name, "pgp5"))       /* XXX legacy */
+       rc = RPMSIGTAG_PGP;
+    else if (!rstrcasecmp(name, "gpg"))
+       rc = RPMSIGTAG_GPG;
+    else
+       rc = -1;        /* Invalid %_signature spec in macro file */
+
+    name = _free(name);
+    return rc;
+}
+
+static int checkPassPhrase(const char * passPhrase)
+{
+    int passPhrasePipe[2];
+    int pid, status;
+    int rc;
+    int xx;
+
+    if (passPhrase == NULL)
+       return -1;
+
+    passPhrasePipe[0] = passPhrasePipe[1] = 0;
+    xx = pipe(passPhrasePipe);
+    if (!(pid = fork())) {
+       char * cmd, * gpg_path;
+       char *const *av;
+       int fdno;
+
+       xx = close(STDIN_FILENO);
+       xx = close(STDOUT_FILENO);
+       xx = close(passPhrasePipe[1]);
+       if ((fdno = open("/dev/null", O_RDONLY)) != STDIN_FILENO) {
+           xx = dup2(fdno, STDIN_FILENO);
+           xx = close(fdno);
+       }
+       if ((fdno = open("/dev/null", O_WRONLY)) != STDOUT_FILENO) {
+           xx = dup2(fdno, STDOUT_FILENO);
+           xx = close(fdno);
+       }
+       xx = dup2(passPhrasePipe[0], 3);
+
+       unsetenv("MALLOC_CHECK_");
+       gpg_path = rpmExpand("%{?_gpg_path}", NULL);
+
+       if (!rstreq(gpg_path, ""))
+           setenv("GNUPGHOME", gpg_path, 1);
+       
+       cmd = rpmExpand("%{?__gpg_check_password_cmd}", NULL);
+       rc = poptParseArgvString(cmd, NULL, (const char ***)&av);
+       if (!rc)
+           rc = execve(av[0], av+1, environ);
+
+       fprintf(stderr, _("Could not exec %s: %s\n"), "gpg",
+                   strerror(errno));
+       _exit(EXIT_FAILURE);
+    }
+
+    xx = close(passPhrasePipe[0]);
+    xx = write(passPhrasePipe[1], passPhrase, strlen(passPhrase));
+    xx = write(passPhrasePipe[1], "\n", 1);
+    xx = close(passPhrasePipe[1]);
+
+    (void) waitpid(pid, &status, 0);
+
+    return ((WIFEXITED(status) && WEXITSTATUS(status) == 0)) ? 0 : 1;
+}
+
+/* TODO: permit overriding macro setup on the command line */
+static int doSign(ARGV_const_t args)
+{
+    int rc = EXIT_FAILURE;
+    int sigTag = lookupSignatureType();
+    char * passPhrase = NULL;
+    char * name = rpmExpand("%{?_gpg_name}", NULL);
+
+    if (rstreq(name, "")) {
+       fprintf(stderr, _("You must set \"%%_gpg_name\" in your macro file\n"));
+       goto exit;
+    }
+
+    switch (sigTag) {
+    case RPMSIGTAG_PGP:
+    case RPMSIGTAG_GPG:
+    case RPMSIGTAG_DSA:
+    case RPMSIGTAG_RSA:
+       break;
+    default:
+       fprintf(stderr, _("Invalid %%_signature spec in macro file.\n"));
+       goto exit;
+       break;
+    }
+
+    /* XXX FIXME: eliminate obsolete getpass() usage */
+    passPhrase = getpass(_("Enter pass phrase: "));
+    passPhrase = (passPhrase != NULL) ? rstrdup(passPhrase) : NULL;
+    if (checkPassPhrase(passPhrase) == 0) {
+       fprintf(stderr, _("Pass phrase is good.\n"));
+       rc = rpmcliSign(args, 0, sigTag, passPhrase);
+    } else {
+       fprintf(stderr, _("Pass phrase check failed\n"));
+    }
+
+exit:
+    free(name);
+    return rc;
+}
+
+int main(int argc, char *argv[])
+{
+    int ec = EXIT_FAILURE;
+    poptContext optCon = rpmcliInit(argc, argv, optionsTable);
+    ARGV_const_t args = NULL;
+    
+    if (argc <= 1) {
+       printUsage(optCon, stderr, 0);
+       goto exit;
+    }
+
+    args = (ARGV_const_t) poptGetArgs(optCon);
+    if (args == NULL) {
+       argerror(_("no arguments given"));
+    }
+
+    switch (mode) {
+    case MODE_ADDSIGN:
+    case MODE_RESIGN:
+       ec = doSign(args);
+       break;
+    case MODE_DELSIGN:
+       ec = rpmcliSign(args, 1, 0, NULL);
+       break;
+    default:
+       argerror(_("only one major mode may be specified"));
+       break;
+    }
+
+exit:
+    rpmcliFini(optCon);
+    return ec;
+}