audit-rpm.c 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
  1. /*
  2. ** rpm.c -- OpenPKG RPM Auditing Wrapper
  3. ** Copyright (c) 2004 The OpenPKG Project <http://www.openpkg.org/>
  4. ** Copyright (c) 2004 Ralf S. Engelschall <rse@engelschall.com>
  5. ** Copyright (c) 2004 Cable & Wireless <http://www.cw.com/>
  6. **
  7. ** Permission to use, copy, modify, and distribute this software for
  8. ** any purpose with or without fee is hereby granted, provided that
  9. ** the above copyright notice and this permission notice appear in all
  10. ** copies.
  11. **
  12. ** THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
  13. ** WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
  14. ** MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
  15. ** IN NO EVENT SHALL THE AUTHORS AND COPYRIGHT HOLDERS AND THEIR
  16. ** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  17. ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  18. ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
  19. ** USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  20. ** ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  21. ** OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
  22. ** OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  23. ** SUCH DAMAGE.
  24. */
  25. /* This is a small OpenPKG RPM command wrapper which provides minimal
  26. auditing/logging possibilities to an OpenPKG instance by writing
  27. a <prefix>/RPM/DB/Audit logfile containing the RPM commands which
  28. actually led to a RPM database change. */
  29. #include <stdio.h>
  30. #include <stdlib.h>
  31. #include <stdarg.h>
  32. #include <string.h>
  33. #include <time.h>
  34. #include <sys/types.h>
  35. #include <sys/stat.h>
  36. #include <pwd.h>
  37. #include <unistd.h>
  38. #include <fcntl.h>
  39. #include <sys/wait.h>
  40. #include <sys/time.h>
  41. #include <sys/resource.h>
  42. /* utility function for fatal program termination under error condition */
  43. void die(const char *fmt, ...)
  44. {
  45. va_list ap;
  46. va_start(ap, fmt);
  47. fprintf(stderr, "openpkg-audit: rpm: ERROR: ");
  48. vfprintf(stderr, fmt, ap);
  49. fprintf(stderr, "\n");
  50. va_end(ap);
  51. exit(1);
  52. }
  53. /* the auditing logfile */
  54. #ifndef LOGFILE
  55. #define LOGFILE "/var/openpkg-audit/openpkg-audit.log"
  56. #endif
  57. /* the RPM database directory and owner/permission */
  58. #define RPMDB_DIR "RPM/DB"
  59. /* list of RPM database files to check */
  60. static char *RPMDB_files[] = {
  61. "Basenames",
  62. "Conflictname",
  63. "Depends",
  64. "Dirnames",
  65. "Filemd5s",
  66. "Group",
  67. "Installtid",
  68. "Name",
  69. "Packages",
  70. "Providename",
  71. "Provideversion",
  72. "Pubkeys",
  73. "Requirename",
  74. "Requireversion",
  75. "Sha1header",
  76. "Sigmd5",
  77. "Triggername"
  78. };
  79. /* utility function for making a concatenated string out of multiple arguments */
  80. static char *mkstr(const char *pad, const char *s1, ...)
  81. {
  82. va_list ap;
  83. va_list apbak;
  84. int n;
  85. const char *cp;
  86. char *str;
  87. va_start(ap, s1);
  88. va_copy(apbak, ap);
  89. n = 0;
  90. for (cp = s1; cp != NULL; cp = (const char *)va_arg(ap, const char *))
  91. n += strlen(cp);
  92. n++;
  93. va_copy(ap, apbak);
  94. if ((str = (char *)malloc(n)) == NULL)
  95. die("failed to allocate %d bytes of memory", n);
  96. str[0] = '\0';
  97. for (cp = s1; cp != NULL; cp = (const char *)va_arg(ap, const char *)) {
  98. if (pad != NULL && str[0] != '\0')
  99. strcat(str, pad);
  100. strcat(str, cp);
  101. }
  102. va_end(ap);
  103. return str;
  104. }
  105. /* utility function for making a concatenated string out of an array */
  106. static char *mkstra(const char *pad, char **sa)
  107. {
  108. int n, i;
  109. char *str;
  110. n = 0;
  111. for (i = 0; sa[i] != NULL; i++)
  112. n += strlen(sa[i]);
  113. n++;
  114. if ((str = (char *)malloc(n)) == NULL)
  115. die("failed to allocate %d bytes of memory", n);
  116. str[0] = '\0';
  117. for (i = 0; sa[i] != NULL; i++) {
  118. if (pad != NULL && str[0] != '\0')
  119. strcat(str, pad);
  120. strcat(str, sa[i]);
  121. }
  122. return str;
  123. }
  124. /* utility function for determining the maximum mtime of the RPM database */
  125. static time_t rpmdb_mtime(const char *cpPrefix)
  126. {
  127. time_t mtime = 0;
  128. int i;
  129. char *cp;
  130. struct stat sb;
  131. for (i = 0; i < sizeof(RPMDB_files)/sizeof(RPMDB_files[0]); i++) {
  132. cp = mkstr(NULL, cpPrefix, "/", RPMDB_DIR, "/", RPMDB_files[i], NULL);
  133. if (stat(cp, &sb) == 0) {
  134. if (mtime < sb.st_mtime)
  135. mtime = sb.st_mtime;
  136. }
  137. free(cp);
  138. }
  139. return mtime;
  140. }
  141. /* main procedure */
  142. int main(int argc, char *argv[])
  143. {
  144. char *cpPrefix;
  145. char *cpToolsCmdProg;
  146. char *cpToolsCmdName;
  147. int i, j;
  148. char **argv2;
  149. FILE *fp;
  150. char *cpLogfile;
  151. struct tm *tm;
  152. time_t t;
  153. struct passwd *pw;
  154. char *cpCmd;
  155. time_t mtime_before;
  156. time_t mtime_after;
  157. char *cp;
  158. pid_t pid;
  159. int status;
  160. int rv;
  161. int fd;
  162. struct stat sb;
  163. /* determine OpenPKG run-time information
  164. (provided by <prefix>/bin/openpkg execution wrapper) */
  165. if ((cpPrefix = getenv("OPENPKG_PREFIX")) == NULL)
  166. die("$OPENPKG_PREFIX not set");
  167. if ((cpToolsCmdProg = getenv("OPENPKG_TOOLS_CMDPROG")) == NULL)
  168. die("$OPENPKG_TOOLS_CMDPROG not set");
  169. if ((cpToolsCmdName = getenv("OPENPKG_TOOLS_CMDNAME")) == NULL)
  170. die("$OPENPKG_TOOLS_CMDNAME not set");
  171. /* determine argument vector for real OpenPKG RPM command
  172. argv: <prefix>/lib/openpkg-audit/rpm <arg1> <arg2> ...
  173. argv2: <prefix>/bin/openpkg rpm <arg1> <arg2> ... */
  174. if ((argv2 = (char **)malloc((2+argc+1) * sizeof(char *))) == NULL)
  175. die("cannot malloc");
  176. j = 0;
  177. argv2[j++] = cpToolsCmdProg;
  178. argv2[j++] = cpToolsCmdName;
  179. i = 1;
  180. while (i < argc)
  181. argv2[j++] = argv[i++];
  182. argv2[j++] = NULL;
  183. /* determine whether access to the RPM database is possible
  184. and it not, short-circuit processing */
  185. cp = mkstr(NULL, cpPrefix, "/", RPMDB_DIR, "/", RPMDB_files[0], NULL);
  186. if (access(cp, R_OK|W_OK) != 0) {
  187. /* pass-through execution to real OpenPKG RPM command */
  188. execvp(argv2[0], argv2);
  189. /* NEVER REACHED */
  190. abort();
  191. }
  192. free(cp);
  193. /* determine maximum modification time (before operation) */
  194. mtime_before = rpmdb_mtime(cpPrefix);
  195. /* execute real OpenPKG RPM command */
  196. if ((pid = fork()) == 0) {
  197. /* pass-through execution to real OpenPKG RPM command */
  198. execvp(argv2[0], argv2);
  199. /* NEVER REACHED */
  200. abort();
  201. }
  202. /* wait for child to terminate */
  203. if (waitpid(pid, &status, 0) < 0)
  204. die("failed to wait for child process with PID %d\n", pid);
  205. /* determine return code */
  206. rv = (WIFEXITED(status) ? WEXITSTATUS(status) : -1);
  207. /* determine maximum modification time (after operation) */
  208. mtime_after = rpmdb_mtime(cpPrefix);
  209. /* if an operation was performed which has not changed the RPM
  210. database we short-circuit processing and exit without logging */
  211. if (mtime_after == mtime_before)
  212. exit(rv);
  213. /* determine time */
  214. t = time(NULL);
  215. if ((tm = localtime(&t)) == NULL)
  216. die("cannot determine local time");
  217. /* determine user */
  218. if ((pw = getpwuid(getuid())) == NULL)
  219. die("cannot determine user information");
  220. /* determine command */
  221. cpCmd = mkstra(" ", &argv2[1]);
  222. /* optionally give up any root privileges to make sure
  223. the file is written with the OpenPKG RPM database user/group */
  224. cp = mkstr(NULL, cpPrefix, "/", RPMDB_DIR, "/", RPMDB_files[0], NULL);
  225. if (stat(cp, &sb) < 0)
  226. die("unable to stat the RPM database file %s", cp);
  227. free(cp);
  228. setgid(sb.st_gid); setegid(sb.st_gid);
  229. setuid(sb.st_uid); seteuid(sb.st_uid);
  230. /* write entry to logfile */
  231. cpLogfile = mkstr(NULL, cpPrefix, "/", LOGFILE, NULL);
  232. if ((fd = open(cpLogfile, O_CREAT|O_RDWR|O_APPEND, sb.st_mode)) == -1)
  233. die("cannot open logfile \"%s\"", cpLogfile);
  234. free(cpLogfile);
  235. if ((fp = fdopen(fd, "a")) == NULL)
  236. die("cannot open filedescriptor");
  237. fprintf(fp, "%04d-%02d-%02d %02d:%02d:%02d user=%s, command=\"%s\", return=%d\n",
  238. tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
  239. tm->tm_hour, tm->tm_min, tm->tm_sec,
  240. pw->pw_name,
  241. cpCmd, rv);
  242. fclose(fp);
  243. close(fd);
  244. /* cleanup */
  245. free(argv2);
  246. free(cpCmd);
  247. exit(0);
  248. }