|
|
@@ -0,0 +1,279 @@
|
|
|
+/*
|
|
|
+** rpm.c -- OpenPKG RPM Auditing Wrapper
|
|
|
+** Copyright (c) 2004 The OpenPKG Project <http://www.openpkg.org/>
|
|
|
+** Copyright (c) 2004 Ralf S. Engelschall <rse@engelschall.com>
|
|
|
+** Copyright (c) 2004 Cable & Wireless <http://www.cw.com/>
|
|
|
+**
|
|
|
+** Permission to use, copy, modify, and distribute this software for
|
|
|
+** any purpose with or without fee is hereby granted, provided that
|
|
|
+** the above copyright notice and this permission notice appear in all
|
|
|
+** copies.
|
|
|
+**
|
|
|
+** THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
|
|
|
+** WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
|
|
+** MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
|
+** IN NO EVENT SHALL THE AUTHORS AND COPYRIGHT HOLDERS AND THEIR
|
|
|
+** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
|
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
|
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
|
|
|
+** USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
|
+** ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
|
+** OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
|
|
+** OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
|
+** SUCH DAMAGE.
|
|
|
+*/
|
|
|
+
|
|
|
+/* This is a small OpenPKG RPM command wrapper which provides minimal
|
|
|
+ auditing/logging possibilities to an OpenPKG instance by writing
|
|
|
+ a <prefix>/RPM/DB/Audit logfile containing the RPM commands which
|
|
|
+ actually led to a RPM database change. */
|
|
|
+
|
|
|
+#include <stdio.h>
|
|
|
+#include <stdlib.h>
|
|
|
+#include <stdarg.h>
|
|
|
+#include <string.h>
|
|
|
+#include <time.h>
|
|
|
+#include <sys/types.h>
|
|
|
+#include <sys/stat.h>
|
|
|
+#include <pwd.h>
|
|
|
+#include <unistd.h>
|
|
|
+#include <fcntl.h>
|
|
|
+#include <sys/wait.h>
|
|
|
+#include <sys/time.h>
|
|
|
+#include <sys/resource.h>
|
|
|
+
|
|
|
+/* utility function for fatal program termination under error condition */
|
|
|
+void die(const char *fmt, ...)
|
|
|
+{
|
|
|
+ va_list ap;
|
|
|
+
|
|
|
+ va_start(ap, fmt);
|
|
|
+ fprintf(stderr, "openpkg-audit: rpm: ERROR: ");
|
|
|
+ vfprintf(stderr, fmt, ap);
|
|
|
+ fprintf(stderr, "\n");
|
|
|
+ va_end(ap);
|
|
|
+ exit(1);
|
|
|
+}
|
|
|
+
|
|
|
+/* the auditing logfile */
|
|
|
+#ifndef LOGFILE
|
|
|
+#define LOGFILE "/var/openpkg-audit/openpkg-audit.log"
|
|
|
+#endif
|
|
|
+
|
|
|
+/* the RPM database directory and owner/permission */
|
|
|
+#define RPMDB_DIR "RPM/DB"
|
|
|
+
|
|
|
+/* list of RPM database files to check */
|
|
|
+static char *RPMDB_files[] = {
|
|
|
+ "Basenames",
|
|
|
+ "Conflictname",
|
|
|
+ "Depends",
|
|
|
+ "Dirnames",
|
|
|
+ "Filemd5s",
|
|
|
+ "Group",
|
|
|
+ "Installtid",
|
|
|
+ "Name",
|
|
|
+ "Packages",
|
|
|
+ "Providename",
|
|
|
+ "Provideversion",
|
|
|
+ "Pubkeys",
|
|
|
+ "Requirename",
|
|
|
+ "Requireversion",
|
|
|
+ "Sha1header",
|
|
|
+ "Sigmd5",
|
|
|
+ "Triggername"
|
|
|
+};
|
|
|
+
|
|
|
+/* utility function for making a concatenated string out of multiple arguments */
|
|
|
+static char *mkstr(const char *pad, const char *s1, ...)
|
|
|
+{
|
|
|
+ va_list ap;
|
|
|
+ va_list apbak;
|
|
|
+ int n;
|
|
|
+ const char *cp;
|
|
|
+ char *str;
|
|
|
+
|
|
|
+ va_start(ap, s1);
|
|
|
+ va_copy(apbak, ap);
|
|
|
+ n = 0;
|
|
|
+ for (cp = s1; cp != NULL; cp = (const char *)va_arg(ap, const char *))
|
|
|
+ n += strlen(cp);
|
|
|
+ n++;
|
|
|
+ va_copy(ap, apbak);
|
|
|
+ if ((str = (char *)malloc(n)) == NULL)
|
|
|
+ die("failed to allocate %d bytes of memory", n);
|
|
|
+ str[0] = '\0';
|
|
|
+ for (cp = s1; cp != NULL; cp = (const char *)va_arg(ap, const char *)) {
|
|
|
+ if (pad != NULL && str[0] != '\0')
|
|
|
+ strcat(str, pad);
|
|
|
+ strcat(str, cp);
|
|
|
+ }
|
|
|
+ va_end(ap);
|
|
|
+ return str;
|
|
|
+}
|
|
|
+
|
|
|
+/* utility function for making a concatenated string out of an array */
|
|
|
+static char *mkstra(const char *pad, char **sa)
|
|
|
+{
|
|
|
+ int n, i;
|
|
|
+ char *str;
|
|
|
+
|
|
|
+ n = 0;
|
|
|
+ for (i = 0; sa[i] != NULL; i++)
|
|
|
+ n += strlen(sa[i]);
|
|
|
+ n++;
|
|
|
+ if ((str = (char *)malloc(n)) == NULL)
|
|
|
+ die("failed to allocate %d bytes of memory", n);
|
|
|
+ str[0] = '\0';
|
|
|
+ for (i = 0; sa[i] != NULL; i++) {
|
|
|
+ if (pad != NULL && str[0] != '\0')
|
|
|
+ strcat(str, pad);
|
|
|
+ strcat(str, sa[i]);
|
|
|
+ }
|
|
|
+ return str;
|
|
|
+}
|
|
|
+
|
|
|
+/* utility function for determining the maximum mtime of the RPM database */
|
|
|
+static time_t rpmdb_mtime(const char *cpPrefix)
|
|
|
+{
|
|
|
+ time_t mtime = 0;
|
|
|
+ int i;
|
|
|
+ char *cp;
|
|
|
+ struct stat sb;
|
|
|
+
|
|
|
+ for (i = 0; i < sizeof(RPMDB_files)/sizeof(RPMDB_files[0]); i++) {
|
|
|
+ cp = mkstr(NULL, cpPrefix, "/", RPMDB_DIR, "/", RPMDB_files[i], NULL);
|
|
|
+ if (stat(cp, &sb) == 0) {
|
|
|
+ if (mtime < sb.st_mtime)
|
|
|
+ mtime = sb.st_mtime;
|
|
|
+ }
|
|
|
+ free(cp);
|
|
|
+ }
|
|
|
+ return mtime;
|
|
|
+}
|
|
|
+
|
|
|
+/* main procedure */
|
|
|
+int main(int argc, char *argv[])
|
|
|
+{
|
|
|
+ char *cpPrefix;
|
|
|
+ char *cpToolsCmdProg;
|
|
|
+ char *cpToolsCmdName;
|
|
|
+ int i, j;
|
|
|
+ char **argv2;
|
|
|
+ FILE *fp;
|
|
|
+ char *cpLogfile;
|
|
|
+ struct tm *tm;
|
|
|
+ time_t t;
|
|
|
+ struct passwd *pw;
|
|
|
+ char *cpCmd;
|
|
|
+ time_t mtime_before;
|
|
|
+ time_t mtime_after;
|
|
|
+ char *cp;
|
|
|
+ pid_t pid;
|
|
|
+ int status;
|
|
|
+ int rv;
|
|
|
+ int fd;
|
|
|
+ struct stat sb;
|
|
|
+
|
|
|
+ /* determine OpenPKG run-time information
|
|
|
+ (provided by <prefix>/bin/openpkg execution wrapper) */
|
|
|
+ if ((cpPrefix = getenv("OPENPKG_PREFIX")) == NULL)
|
|
|
+ die("$OPENPKG_PREFIX not set");
|
|
|
+ if ((cpToolsCmdProg = getenv("OPENPKG_TOOLS_CMDPROG")) == NULL)
|
|
|
+ die("$OPENPKG_TOOLS_CMDPROG not set");
|
|
|
+ if ((cpToolsCmdName = getenv("OPENPKG_TOOLS_CMDNAME")) == NULL)
|
|
|
+ die("$OPENPKG_TOOLS_CMDNAME not set");
|
|
|
+
|
|
|
+ /* determine argument vector for real OpenPKG RPM command
|
|
|
+ argv: <prefix>/lib/openpkg-audit/rpm <arg1> <arg2> ...
|
|
|
+ argv2: <prefix>/bin/openpkg rpm <arg1> <arg2> ... */
|
|
|
+ if ((argv2 = (char **)malloc((2+argc+1) * sizeof(char *))) == NULL)
|
|
|
+ die("cannot malloc");
|
|
|
+ j = 0;
|
|
|
+ argv2[j++] = cpToolsCmdProg;
|
|
|
+ argv2[j++] = cpToolsCmdName;
|
|
|
+ i = 1;
|
|
|
+ while (i < argc)
|
|
|
+ argv2[j++] = argv[i++];
|
|
|
+ argv2[j++] = NULL;
|
|
|
+
|
|
|
+ /* determine whether access to the RPM database is possible
|
|
|
+ and it not, short-circuit processing */
|
|
|
+ cp = mkstr(NULL, cpPrefix, "/", RPMDB_DIR, "/", RPMDB_files[0], NULL);
|
|
|
+ if (access(cp, R_OK|W_OK) != 0) {
|
|
|
+ /* pass-through execution to real OpenPKG RPM command */
|
|
|
+ execvp(argv2[0], argv2);
|
|
|
+ /* NEVER REACHED */
|
|
|
+ abort();
|
|
|
+ }
|
|
|
+ free(cp);
|
|
|
+
|
|
|
+ /* determine maximum modification time (before operation) */
|
|
|
+ mtime_before = rpmdb_mtime(cpPrefix);
|
|
|
+
|
|
|
+ /* execute real OpenPKG RPM command */
|
|
|
+ if ((pid = fork()) == 0) {
|
|
|
+ /* pass-through execution to real OpenPKG RPM command */
|
|
|
+ execvp(argv2[0], argv2);
|
|
|
+ /* NEVER REACHED */
|
|
|
+ abort();
|
|
|
+ }
|
|
|
+
|
|
|
+ /* wait for child to terminate */
|
|
|
+ if (waitpid(pid, &status, 0) < 0)
|
|
|
+ die("failed to wait for child process with PID %d\n", pid);
|
|
|
+
|
|
|
+ /* determine return code */
|
|
|
+ rv = (WIFEXITED(status) ? WEXITSTATUS(status) : -1);
|
|
|
+
|
|
|
+ /* determine maximum modification time (after operation) */
|
|
|
+ mtime_after = rpmdb_mtime(cpPrefix);
|
|
|
+
|
|
|
+ /* if an operation was performed which has not changed the RPM
|
|
|
+ database we short-circuit processing and exit without logging */
|
|
|
+ if (mtime_after == mtime_before)
|
|
|
+ exit(rv);
|
|
|
+
|
|
|
+ /* determine time */
|
|
|
+ t = time(NULL);
|
|
|
+ if ((tm = localtime(&t)) == NULL)
|
|
|
+ die("cannot determine local time");
|
|
|
+
|
|
|
+ /* determine user */
|
|
|
+ if ((pw = getpwuid(getuid())) == NULL)
|
|
|
+ die("cannot determine user information");
|
|
|
+
|
|
|
+ /* determine command */
|
|
|
+ cpCmd = mkstra(" ", &argv2[1]);
|
|
|
+
|
|
|
+ /* optionally give up any root privileges to make sure
|
|
|
+ the file is written with the OpenPKG RPM database user/group */
|
|
|
+ cp = mkstr(NULL, cpPrefix, "/", RPMDB_DIR, "/", RPMDB_files[0], NULL);
|
|
|
+ if (stat(cp, &sb) < 0)
|
|
|
+ die("unable to stat the RPM database file %s", cp);
|
|
|
+ free(cp);
|
|
|
+ setgid(sb.st_gid); setegid(sb.st_gid);
|
|
|
+ setuid(sb.st_uid); seteuid(sb.st_uid);
|
|
|
+
|
|
|
+ /* write entry to logfile */
|
|
|
+ cpLogfile = mkstr(NULL, cpPrefix, "/", LOGFILE, NULL);
|
|
|
+ if ((fd = open(cpLogfile, O_CREAT|O_RDWR|O_APPEND, sb.st_mode)) == -1)
|
|
|
+ die("cannot open logfile \"%s\"", cpLogfile);
|
|
|
+ free(cpLogfile);
|
|
|
+ if ((fp = fdopen(fd, "a")) == NULL)
|
|
|
+ die("cannot open filedescriptor");
|
|
|
+ fprintf(fp, "%04d-%02d-%02d %02d:%02d:%02d user=%s, command=\"%s\", return=%d\n",
|
|
|
+ tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
|
|
|
+ tm->tm_hour, tm->tm_min, tm->tm_sec,
|
|
|
+ pw->pw_name,
|
|
|
+ cpCmd, rv);
|
|
|
+ fclose(fp);
|
|
|
+ close(fd);
|
|
|
+
|
|
|
+ /* cleanup */
|
|
|
+ free(argv2);
|
|
|
+ free(cpCmd);
|
|
|
+
|
|
|
+ exit(0);
|
|
|
+}
|
|
|
+
|