The yara_load API allows to load a set of Yara rules contained within a
file on the host.
Rules can be in binary format, as when compiled with yarac command, or
in source code format. In the latter case, the rules will be first
compiled and then loaded.
Subsequent calls of the yara_load API will result in the discard of the
previously loaded rules.
Signed-off-by: Matteo Cafasso <noxdafox(a)gmail.com>
---
daemon/Makefile.am | 1 +
daemon/cleanups.c | 28 +++++++
daemon/cleanups.h | 9 ++
daemon/yara.c | 227 +++++++++++++++++++++++++++++++++++++++++++++++++++
generator/actions.ml | 18 ++++
src/MAX_PROC_NR | 2 +-
6 files changed, 284 insertions(+), 1 deletion(-)
create mode 100644 daemon/yara.c
diff --git a/daemon/Makefile.am b/daemon/Makefile.am
index 3a25f43..c385edc 100644
--- a/daemon/Makefile.am
+++ b/daemon/Makefile.am
@@ -200,6 +200,7 @@ guestfsd_SOURCES = \
wc.c \
xattr.c \
xfs.c \
+ yara.c \
zero.c \
zerofree.c
diff --git a/daemon/cleanups.c b/daemon/cleanups.c
index 092e493..a02e521 100644
--- a/daemon/cleanups.c
+++ b/daemon/cleanups.c
@@ -24,6 +24,12 @@
#include <augeas.h>
+#ifdef HAVE_YARA
+
+#include <yara.h>
+
+#endif /* !HAVE_YARA */
+
#include "cleanups.h"
/* Use by the CLEANUP_* macros. Do not call these directly. */
@@ -62,6 +68,15 @@ cleanup_close (void *ptr)
}
void
+cleanup_fclose (void *ptr)
+{
+ FILE *f = * (FILE **) ptr;
+
+ if (f)
+ fclose (f);
+}
+
+void
cleanup_aug_close (void *ptr)
{
augeas *aug = * (augeas **) ptr;
@@ -78,3 +93,16 @@ cleanup_free_stringsbuf (void *ptr)
{
free_stringsbuf ((struct stringsbuf *) ptr);
}
+
+#ifdef HAVE_YARA
+
+void
+cleanup_destroy_yara_compiler (void *ptr)
+{
+ YR_COMPILER *compiler = * (YR_COMPILER **) ptr;
+
+ if (compiler != NULL)
+ yr_compiler_destroy (compiler);
+}
+
+#endif /* !HAVE_YARA */
diff --git a/daemon/cleanups.h b/daemon/cleanups.h
index 6746e27..c61f646 100644
--- a/daemon/cleanups.h
+++ b/daemon/cleanups.h
@@ -26,8 +26,12 @@ extern void cleanup_free (void *ptr);
extern void cleanup_free_string_list (void *ptr);
extern void cleanup_unlink_free (void *ptr);
extern void cleanup_close (void *ptr);
+extern void cleanup_fclose (void *ptr);
extern void cleanup_aug_close (void *ptr);
extern void cleanup_free_stringsbuf (void *ptr);
+#ifdef HAVE_YARA
+extern void cleanup_destroy_yara_compiler (void *ptr);
+#endif /* !HAVE_YARA */
#ifdef HAVE_ATTRIBUTE_CLEANUP
#define CLEANUP_FREE __attribute__((cleanup(cleanup_free)))
@@ -35,8 +39,13 @@ extern void cleanup_free_stringsbuf (void *ptr);
__attribute__((cleanup(cleanup_free_string_list)))
#define CLEANUP_UNLINK_FREE __attribute__((cleanup(cleanup_unlink_free)))
#define CLEANUP_CLOSE __attribute__((cleanup(cleanup_close)))
+#define CLEANUP_FCLOSE __attribute__((cleanup(cleanup_fclose)))
#define CLEANUP_AUG_CLOSE __attribute__((cleanup(cleanup_aug_close)))
#define CLEANUP_FREE_STRINGSBUF __attribute__((cleanup(cleanup_free_stringsbuf)))
+#ifdef HAVE_YARA
+#define CLEANUP_DESTROY_YARA_COMPILER \
+ __attribute__((cleanup(cleanup_destroy_yara_compiler)))
+#endif /* !HAVE_YARA */
#else
#define CLEANUP_FREE
#define CLEANUP_FREE_STRING_LIST
diff --git a/daemon/yara.c b/daemon/yara.c
new file mode 100644
index 0000000..be53322
--- /dev/null
+++ b/daemon/yara.c
@@ -0,0 +1,227 @@
+/* libguestfs - the guestfsd daemon
+ * Copyright (C) 2016 Red Hat Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <inttypes.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdbool.h>
+#include <rpc/xdr.h>
+#include <rpc/types.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include "daemon.h"
+#include "actions.h"
+#include "optgroups.h"
+#include "guestfs_protocol.h"
+
+#ifdef HAVE_YARA
+
+#include <yara.h>
+
+struct write_callback_data {
+ int fd;
+ uint64_t written;
+};
+
+/* Yara compiled rules. */
+static YR_RULES *rules = NULL;
+static bool initialized = false;
+
+static int upload_rules_file (char *);
+static int compile_rules_file (const char *);
+static int write_callback (void *, const void *, size_t);
+static void compile_error_callback (int, const char *, int, const char *, void *);
+
+/* Has one FileIn parameter. */
+int
+do_yara_load (void)
+{
+ int ret = 0;
+ char tmpfile[] = "/tmp/yaraXXXXXX";
+
+ ret = upload_rules_file (tmpfile);
+ if (ret < 0)
+ return -1;
+
+ /* Initialize yara only once. */
+ if (!initialized) {
+ ret = yr_initialize ();
+ if (ret != ERROR_SUCCESS) {
+ reply_with_error ("failed initializing yara");
+ return -1;
+ }
+
+ initialized = true;
+ }
+
+ /* Destroy previously loaded rules. */
+ if (rules != NULL) {
+ yr_rules_destroy (rules);
+ rules = NULL;
+ }
+
+ /* Try to load the rules as compiled.
+ * If their are in source code format, compile them first.
+ */
+ ret = yr_rules_load (tmpfile, &rules);
+ if (ret == ERROR_INVALID_FILE)
+ ret = compile_rules_file (tmpfile);
+
+ unlink (tmpfile);
+
+ return (ret == ERROR_SUCCESS) ? 0 : -1;
+}
+
+/* Upload rules file on a temporary file.
+ * Return 0 on success, -1 on error.
+ */
+static int
+upload_rules_file (char *rules_path)
+{
+ int ret = 0;
+ CLEANUP_CLOSE int fd = 0;
+ struct write_callback_data data = { .written = 0 };
+
+ data.fd = mkstemp (rules_path);
+ if (data.fd == -1) {
+ reply_with_perror ("mkstemp");
+ return -1;
+ }
+
+ ret = receive_file (write_callback, &data);
+ if (ret == -1) {
+ /* Write error. */
+ cancel_receive ();
+ reply_with_error ("write error: %s", rules_path);
+ return -1;
+ }
+ if (ret == -2) {
+ /* Cancellation from library */
+ reply_with_error ("file upload cancelled");
+ return -1;
+ }
+
+ return 0;
+}
+
+/* Compile source code rules and load them.
+ * Return ERROR_SUCCESS on success, Yara error code type on error.
+ */
+static int
+compile_rules_file (const char *rules_path)
+{
+ int ret = 0;
+ CLEANUP_FCLOSE FILE *rule_file = NULL;
+ CLEANUP_DESTROY_YARA_COMPILER YR_COMPILER *compiler = NULL;
+
+ ret = yr_compiler_create (&compiler);
+ if (ret != ERROR_SUCCESS) {
+ reply_with_error ("yr_compiler_create");
+ return ret;
+ }
+
+ yr_compiler_set_callback (compiler, compile_error_callback, NULL);
+
+ rule_file = fopen (rules_path, "r");
+ if (rule_file == NULL) {
+ reply_with_error ("unable to open rules file");
+ return ret;
+ }
+
+ ret = yr_compiler_add_file (compiler, rule_file, NULL, rules_path);
+ if (ret > 0)
+ return ret;
+
+ ret = yr_compiler_get_rules (compiler, &rules);
+ if (ret != ERROR_SUCCESS)
+ reply_with_error ("yr_compiler_get_rules");
+
+ return ret;
+}
+
+/* File upload callback, called by receive_file.
+ * Return 0 on success, -1 on error.
+ */
+static int
+write_callback (void *data_vp, const void *buf, size_t len)
+{
+ int ret;
+ struct write_callback_data *data = data_vp;
+
+ ret = xwrite (data->fd, buf, len);
+ if (ret == -1)
+ return -1;
+
+ data->written += len;
+
+ if (progress_hint > 0)
+ notify_progress (data->written, progress_hint);
+
+ return 0;
+}
+
+/* Yara compilation error callback.
+ * Reports back the compilation error message.
+ * Prints compilation warnings if verbose.
+ */
+static void
+compile_error_callback(int level, const char *name, int line,
+ const char *message, void *data)
+{
+ if (level == YARA_ERROR_LEVEL_ERROR)
+ reply_with_error ("(%d): Yara error: %s", line, message);
+ else
+ fprintf (stderr, "(%d): Yara warning: %s\n", line, message);
+}
+
+/* Clean up yara handle on daemon exit. */
+void yara_finalize (void) __attribute__((destructor));
+void
+yara_finalize (void)
+{
+ int ret = 0;
+
+ if (rules != NULL) {
+ yr_rules_destroy (rules);
+ rules = NULL;
+ }
+
+ ret = yr_finalize ();
+ if (ret != ERROR_SUCCESS)
+ perror ("yr_finalize");
+
+ initialized = false;
+}
+
+int
+optgroup_libyara_available (void)
+{
+ return 1;
+}
+
+#else /* !HAVE_YARA */
+
+OPTGROUP_YARA_NOT_AVAILABLE
+
+#endif /* !HAVE_YARA */
diff --git a/generator/actions.ml b/generator/actions.ml
index 91a1819..f69564a 100644
--- a/generator/actions.ml
+++ b/generator/actions.ml
@@ -13253,6 +13253,24 @@ is removed." };
shortdesc = "search the entries associated to the given inode";
longdesc = "Internal function for find_inode." };
+ { defaults with
+ name = "yara_load"; added = (1, 35, 15);
+ style = RErr, [FileIn "filename";], [];
+ proc_nr = Some 471;
+ progress = true; cancellable = true;
+ optional = Some "libyara";
+ shortdesc = "load yara rules within libguestfs";
+ longdesc = "\
+Load a set of Yara rules from F<filename> within libguestfs appliance.
+
+Rules can be in binary format, as when compiled with yarac command, or
+in source code format. In the latter case, the rules will be first
+compiled and then loaded.
+
+Rules in source code format cannot include external files. In such cases,
+it is recommended to compile them first.
+
+Previously loaded rules will be destroyed." };
]
(* Non-API meta-commands available only in guestfish.
diff --git a/src/MAX_PROC_NR b/src/MAX_PROC_NR
index 5f476b6..c305aa5 100644
--- a/src/MAX_PROC_NR
+++ b/src/MAX_PROC_NR
@@ -1 +1 @@
-470
+471
--
2.10.2