1# Copyright (C) 2017 Open Information Security Foundation 2# Copyright (c) 2015-2017 Jason Ish 3# 4# You can copy, redistribute or modify this Program under the terms of 5# the GNU General Public License version 2 as published by the Free 6# Software Foundation. 7# 8# This program is distributed in the hope that it will be useful, 9# but WITHOUT ANY WARRANTY; without even the implied warranty of 10# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11# GNU General Public License for more details. 12# 13# You should have received a copy of the GNU General Public License 14# version 2 along with this program; if not, write to the Free Software 15# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 16# 02110-1301, USA. 17 18import os.path 19import logging 20 21import yaml 22 23import suricata.update.engine 24from suricata.update.exceptions import ApplicationError 25 26try: 27 from suricata.config import defaults 28 has_defaults = True 29except: 30 has_defaults = False 31 32logger = logging.getLogger() 33 34DEFAULT_DATA_DIRECTORY = "/var/lib/suricata" 35 36# Cache directory - relative to the data directory. 37CACHE_DIRECTORY = os.path.join("update", "cache") 38 39# Source directory - relative to the data directory. 40SOURCE_DIRECTORY = os.path.join("update", "sources") 41 42# Configuration keys. 43DATA_DIRECTORY_KEY = "data-directory" 44CACHE_DIRECTORY_KEY = "cache-directory" 45IGNORE_KEY = "ignore" 46DISABLE_CONF_KEY = "disable-conf" 47ENABLE_CONF_KEY = "enable-conf" 48MODIFY_CONF_KEY = "modify-conf" 49DROP_CONF_KEY = "drop-conf" 50LOCAL_CONF_KEY = "local" 51OUTPUT_KEY = "output" 52DIST_RULE_DIRECTORY_KEY = "dist-rule-directory" 53 54if has_defaults: 55 DEFAULT_UPDATE_YAML_PATH = os.path.join(defaults.sysconfdir, "update.yaml") 56else: 57 DEFAULT_UPDATE_YAML_PATH = "/etc/suricata/update.yaml" 58 59DEFAULT_SURICATA_YAML_PATH = [ 60 "/etc/suricata/suricata.yaml", 61 "/usr/local/etc/suricata/suricata.yaml", 62 "/etc/suricata/suricata-debian.yaml" 63] 64 65if has_defaults: 66 DEFAULT_DIST_RULE_PATH = [ 67 defaults.datarulesdir, 68 "/etc/suricata/rules", 69 ] 70else: 71 DEFAULT_DIST_RULE_PATH = [ 72 "/etc/suricata/rules", 73 ] 74 75DEFAULT_CONFIG = { 76 "disable-conf": "/etc/suricata/disable.conf", 77 "enable-conf": "/etc/suricata/enable.conf", 78 "drop-conf": "/etc/suricata/drop.conf", 79 "modify-conf": "/etc/suricata/modify.conf", 80 "sources": [], 81 LOCAL_CONF_KEY: [], 82 83 # The default file patterns to ignore. 84 "ignore": [ 85 "*deleted.rules", 86 ], 87} 88 89_args = None 90_config = {} 91 92# The filename the config was read from, if any. 93filename = None 94 95def has(key): 96 """Return true if a configuration key exists.""" 97 return key in _config 98 99def set(key, value): 100 """Set a configuration value.""" 101 _config[key] = value 102 103def get(key): 104 """Get a configuration value.""" 105 if key in _config: 106 return _config[key] 107 return None 108 109def set_state_dir(directory): 110 _config[DATA_DIRECTORY_KEY] = directory 111 112def get_state_dir(): 113 """Get the data directory. This is more of the Suricata state 114 directory than a specific Suricata-Update directory, and is used 115 as the root directory for Suricata-Update data. 116 """ 117 if os.getenv("DATA_DIRECTORY"): 118 return os.getenv("DATA_DIRECTORY") 119 if DATA_DIRECTORY_KEY in _config: 120 return _config[DATA_DIRECTORY_KEY] 121 return DEFAULT_DATA_DIRECTORY 122 123def set_cache_dir(directory): 124 """Set an alternate cache directory.""" 125 _config[CACHE_DIRECTORY_KEY] = directory 126 127def get_cache_dir(): 128 """Get the cache directory.""" 129 if CACHE_DIRECTORY_KEY in _config: 130 return _config[CACHE_DIRECTORY_KEY] 131 return os.path.join(get_state_dir(), CACHE_DIRECTORY) 132 133def get_output_dir(): 134 """Get the rule output directory.""" 135 if OUTPUT_KEY in _config: 136 return _config[OUTPUT_KEY] 137 return os.path.join(get_state_dir(), "rules") 138 139def args(): 140 """Return sthe parsed argument object.""" 141 return _args 142 143def get_arg(key): 144 key = key.replace("-", "_") 145 if hasattr(_args, key): 146 val = getattr(_args, key) 147 if val not in [[], None]: 148 return val 149 return None 150 151def init(args): 152 global _args 153 global filename 154 155 _args = args 156 _config.update(DEFAULT_CONFIG) 157 158 if args.config: 159 logger.info("Loading %s", args.config) 160 with open(args.config, "rb") as fileobj: 161 config = yaml.safe_load(fileobj) 162 if config: 163 _config.update(config) 164 filename = args.config 165 elif os.path.exists(DEFAULT_UPDATE_YAML_PATH): 166 logger.info("Loading %s", DEFAULT_UPDATE_YAML_PATH) 167 with open(DEFAULT_UPDATE_YAML_PATH, "rb") as fileobj: 168 config = yaml.safe_load(fileobj) 169 if config: 170 _config.update(config) 171 filename = DEFAULT_UPDATE_YAML_PATH 172 173 # Apply command line arguments to the config. 174 for arg in vars(args): 175 if arg == "local": 176 for local in args.local: 177 logger.debug("Adding local ruleset to config: %s", local) 178 _config[LOCAL_CONF_KEY].append(local) 179 elif arg == "data_dir" and args.data_dir: 180 logger.debug("Setting data directory to %s", args.data_dir) 181 _config[DATA_DIRECTORY_KEY] = args.data_dir 182 elif getattr(args, arg) is not None: 183 key = arg.replace("_", "-") 184 val = getattr(args, arg) 185 logger.debug("Setting configuration value %s -> %s", key, val) 186 _config[key] = val 187 188 # Find and set the path to suricata if not provided. 189 if "suricata" in _config: 190 if not os.path.exists(_config["suricata"]): 191 raise ApplicationError( 192 "Configured path to suricata does not exist: %s" % ( 193 _config["suricata"])) 194 else: 195 suricata_path = suricata.update.engine.get_path() 196 if not suricata_path: 197 logger.warning("No suricata application binary found on path.") 198 else: 199 _config["suricata"] = suricata_path 200 201 if "suricata" in _config: 202 build_info = suricata.update.engine.get_build_info(_config["suricata"]) 203 204 # Set the first suricata.yaml to check for to the one in the 205 # --sysconfdir provided by build-info. 206 if not "suricata_conf" in _config and "sysconfdir" in build_info: 207 DEFAULT_SURICATA_YAML_PATH.insert( 208 0, os.path.join( 209 build_info["sysconfdir"], "suricata/suricata.yaml")) 210 211 # Amend the path to look for Suricata provided rules based on 212 # the build info. As we are inserting at the front, put the 213 # highest priority path last. 214 if "sysconfdir" in build_info: 215 DEFAULT_DIST_RULE_PATH.insert( 216 0, os.path.join(build_info["sysconfdir"], "suricata/rules")) 217 if "datarootdir" in build_info: 218 DEFAULT_DIST_RULE_PATH.insert( 219 0, os.path.join(build_info["datarootdir"], "suricata/rules")) 220 221 # Set the data-directory prefix to that of the --localstatedir 222 # found in the build-info. 223 if not DATA_DIRECTORY_KEY in _config and "localstatedir" in build_info: 224 data_directory = os.path.join( 225 build_info["localstatedir"], "lib/suricata") 226 logger.info("Using data-directory %s.", data_directory) 227 _config[DATA_DIRECTORY_KEY] = data_directory 228 229 # If suricata-conf not provided on the command line or in the 230 # configuration file, look for it. 231 if not "suricata-conf" in _config: 232 for conf in DEFAULT_SURICATA_YAML_PATH: 233 if os.path.exists(conf): 234 logger.info("Using Suricata configuration %s" % (conf)) 235 _config["suricata-conf"] = conf 236 break 237 238 if not DIST_RULE_DIRECTORY_KEY in _config: 239 for path in DEFAULT_DIST_RULE_PATH: 240 if os.path.exists(path): 241 logger.info("Using %s for Suricata provided rules.", path) 242 _config[DIST_RULE_DIRECTORY_KEY] = path 243 break 244