diff --git a/daemon/Makefile.am b/daemon/Makefile.am index ca01aeb4e..68f863bcd 100644 --- a/daemon/Makefile.am +++ b/daemon/Makefile.am @@ -170,6 +170,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 092e493d7..3102cf94b 100644 --- a/daemon/cleanups.c +++ b/daemon/cleanups.c @@ -61,6 +61,15 @@ cleanup_close (void *ptr) close (fd); } +void +cleanup_fclose (void *ptr) +{ + FILE *f = * (FILE **) ptr; + + if (f) + fclose (f); +} + void cleanup_aug_close (void *ptr) { diff --git a/daemon/cleanups.h b/daemon/cleanups.h index 6746e2744..a791244cb 100644 --- a/daemon/cleanups.h +++ b/daemon/cleanups.h @@ -26,6 +26,7 @@ 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); @@ -35,6 +36,7 @@ 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))) #else diff --git a/daemon/yara.c b/daemon/yara.c new file mode 100644 index 000000000..a1e3aa878 --- /dev/null +++ b/daemon/yara.c @@ -0,0 +1,215 @@ +/* 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 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "daemon.h" +#include "actions.h" +#include "optgroups.h" +#include "guestfs_protocol.h" + +#ifdef HAVE_YARA + +#include + +#ifdef HAVE_ATTRIBUTE_CLEANUP +#define CLEANUP_DESTROY_YARA_COMPILER \ + __attribute__((cleanup(cleanup_destroy_yara_compiler))) +#else +#define CLEANUP_DESTROY_YARA_COMPILER +#endif + +struct write_callback_data { + int fd; + uint64_t written; +}; + +/* Yara compiled rules. */ +static YR_RULES *rules = NULL; +static bool initialized = false; + +static int compile_rules_file (const char *); +static void compile_error_callback (int, const char *, int, const char *, void *); +static void cleanup_destroy_yara_compiler (void *ptr); + +/* Has one FileIn parameter. + * Takes optional arguments, consult optargs_bitmask. + */ +int +do_yara_load (void) +{ + int r; + CLEANUP_CLOSE int fd = -1; + char tmpfile[] = "/tmp/yaraXXXXXX"; + + fd = mkstemp (tmpfile); + if (fd == -1) { + reply_with_perror ("mkstemp"); + return -1; + } + + r = upload_to_fd (fd, tmpfile); + if (r == -1) { + unlink (tmpfile); + return -1; + } + + /* Initialize yara only once. */ + if (!initialized) { + r = yr_initialize (); + if (r != ERROR_SUCCESS) { + reply_with_error ("failed initializing yara"); + unlink (tmpfile); + 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. + */ + r = yr_rules_load (tmpfile, &rules); + if (r == ERROR_INVALID_FILE) + r = compile_rules_file (tmpfile); + + unlink (tmpfile); + + return (r == ERROR_SUCCESS) ? 0 : -1; +} + +/* 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; + 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"); + ret = ERROR_COULD_NOT_OPEN_FILE; + goto err; + } + + ret = yr_compiler_add_file (compiler, rule_file, NULL, NULL); + if (ret > 0) { + reply_with_error ("found %d errors when compiling the rules", ret); + goto err; + } + + ret = yr_compiler_get_rules (compiler, &rules); + if (ret == ERROR_INSUFICIENT_MEMORY) { + errno = ENOMEM; + reply_with_perror ("yr_compiler_get_rules"); + } + + err: +#ifndef HAVE_ATTRIBUTE_CLEANUP + yr_compiler_destroy (compiler); +#endif + + return ret; +} + +/* 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) + fprintf (stderr, "Yara error (line %d): %s\n", line, message); + else if (verbose) + fprintf (stderr, "Yara warning (line %d): %s\n", line, message); +} + +/* Clean up yara handle on daemon exit. */ +void yara_finalize (void) __attribute__((destructor)); + +void +yara_finalize (void) +{ + int r; + + if (!initialized) + return; + + if (rules != NULL) { + yr_rules_destroy (rules); + rules = NULL; + } + + r = yr_finalize (); + if (r != ERROR_SUCCESS) + perror ("yr_finalize"); + + initialized = false; +} + +static void +cleanup_destroy_yara_compiler (void *ptr) +{ + YR_COMPILER *compiler = * (YR_COMPILER **) ptr; + + if (compiler != NULL) + yr_compiler_destroy (compiler); +} + +int +optgroup_libyara_available (void) +{ + return 1; +} + +#else /* !HAVE_YARA */ + +OPTGROUP_LIBYARA_NOT_AVAILABLE + +#endif /* !HAVE_YARA */ diff --git a/docs/C_SOURCE_FILES b/docs/C_SOURCE_FILES index 553c055c7..bd0798ee9 100644 --- a/docs/C_SOURCE_FILES +++ b/docs/C_SOURCE_FILES @@ -170,6 +170,7 @@ daemon/uuids.c daemon/wc.c daemon/xattr.c daemon/xfs.c +daemon/yara.c daemon/zero.c daemon/zerofree.c df/df.c diff --git a/generator/Makefile.am b/generator/Makefile.am index 31989cd4a..81b49cab1 100644 --- a/generator/Makefile.am +++ b/generator/Makefile.am @@ -45,6 +45,8 @@ sources = \ actions_tsk.mli \ authors.ml \ authors.mli \ + actions_yara.ml \ + actions_yara.mli \ bindtests.ml \ bindtests.mli \ c.ml \ @@ -132,6 +134,7 @@ objects = \ actions_properties.cmo \ actions_properties_deprecated.cmo \ actions_tsk.cmo \ + actions_yara.cmo \ actions.cmo \ structs.cmo \ fish_commands.cmo \ diff --git a/generator/actions.ml b/generator/actions.ml index a04fdc0f9..6e11d99c3 100644 --- a/generator/actions.ml +++ b/generator/actions.ml @@ -50,7 +50,8 @@ let daemon_functions = Actions_core_deprecated.daemon_functions @ Actions_debug.daemon_functions @ Actions_hivex.daemon_functions @ - Actions_tsk.daemon_functions + Actions_tsk.daemon_functions @ + Actions_yara.daemon_functions (* Some post-processing of the basic lists of actions. *) diff --git a/generator/actions_yara.ml b/generator/actions_yara.ml new file mode 100644 index 000000000..2542d8f00 --- /dev/null +++ b/generator/actions_yara.ml @@ -0,0 +1,48 @@ +(* libguestfs + * Copyright (C) 2009-2017 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 + *) + +(* Please read generator/README first. *) + +open Types + +(* Yara APIs. *) + +let daemon_functions = [ + { defaults with + name = "yara_load"; added = (1, 37, 13); + style = RErr, [FileIn "filename"], []; + progress = true; cancellable = true; + optional = Some "libyara"; + shortdesc = "load yara rules within libguestfs"; + longdesc = "\ +Upload a set of Yara rules from local file F. + +Yara rules allow to categorize files based on textual or binary patterns +within their content. +See C to see how to scan files with the loaded rules. + +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." }; + +] diff --git a/generator/actions_yara.mli b/generator/actions_yara.mli new file mode 100644 index 000000000..23eefe068 --- /dev/null +++ b/generator/actions_yara.mli @@ -0,0 +1,21 @@ +(* libguestfs + * Copyright (C) 2009-2017 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 + *) + +(* Please read generator/README first. *) + +val daemon_functions : Types.action list diff --git a/generator/proc_nr.ml b/generator/proc_nr.ml index b3be31996..d50cc9efa 100644 --- a/generator/proc_nr.ml +++ b/generator/proc_nr.ml @@ -479,6 +479,7 @@ let proc_nr = [ 469, "aug_transform"; 470, "internal_find_inode"; 471, "mksquashfs"; +472, "yara_load"; ] (* End of list. If adding a new entry, add it at the end of the list diff --git a/lib/MAX_PROC_NR b/lib/MAX_PROC_NR index c305aa5ae..68cfb10d1 100644 --- a/lib/MAX_PROC_NR +++ b/lib/MAX_PROC_NR @@ -1 +1 @@ -471 +472 diff --git a/po/POTFILES b/po/POTFILES index dc2a36776..c8423bc64 100644 --- a/po/POTFILES +++ b/po/POTFILES @@ -152,6 +152,7 @@ daemon/uuids.c daemon/wc.c daemon/xattr.c daemon/xfs.c +daemon/yara.c daemon/zero.c daemon/zerofree.c df/df.c