diff --git a/src/main/resources/drivers/Fortinet_FortiMail.js b/src/main/resources/drivers/Fortinet_FortiMail.js new file mode 100644 index 00000000..04329915 --- /dev/null +++ b/src/main/resources/drivers/Fortinet_FortiMail.js @@ -0,0 +1,273 @@ +/** + * Copyright 2013-2024 Netshot + * + * This file is part of Netshot. + * + * Netshot 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 3 of the License, or + * (at your option) any later version. + * + * Netshot 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 Netshot. If not, see . + */ + +/** + * 'Info' object = Meta data of the driver. + */ +var Info = { + name: "FortinetFortiMail", /* Unique identifier of the driver within Netshot. */ + description: "Fortinet FortiMail", /* Description to be used in the UI. */ + author: "Najihel", + version: "1.1" /* Version will appear in the Admin tab. */ +}; + +/** + * 'Config' object = Data fields to be included in each configuration revision. + */ +var Config = { + "osVersion": { /* This stores the detected FortiMail OS version on each snapshot. */ + type: "Text", + title: "FortiMail OS version", + comparable: true, + searchable: true, + checkable: true, + dump: { + pre: "## FortiMail OS version:", + preLine: "## " + } + }, + "configuration": { /* This stores the full textual configuration of the device on each snapshot. */ + type: "LongText", + title: "Configuration", + comparable: true, + searchable: true, + checkable: true, + dump: { + pre: "## Configuration (taken on %when%):", + post: "## End of configuration" + } + } +}; + +/** + * 'Device' object = Data fields to add to devices of this type. + */ +var Device = { + "haPeer": { /* This stores the name of an optional HA peer. */ + type: "Text", + title: "HA peer name", + searchable: true, + checkable: true + } +}; + +/** + * 'CLI' object = Definition of the finite state machine to recognize and handle the CLI prompt changes. + */ +var CLI = { + telnet: { /* Entry point for Telnet access. */ + macros: { /* List of available macros in the CLI mode. */ + basic: { /* 'basic' macro (will be called in the snapshot procedure. */ + options: [ "login", "password", "basic" ], /* Possible next modes, Netshot will test the associated prompt regexp's. */ + target: "basic" /* Netshot will target this mode. */ + } + } + }, + ssh: { /* Entry point for SSH access. */ + macros: { + basic: { + options: [ "basic" ], + target: "basic" + } + } + }, + login: { /* 'login' prompt: send the username and expect the password prompt. */ + prompt: / login: $/, + macros: { + auto: { + cmd: "$$NetshotUsername$$", + options: [ "password" ] + } + } + }, + password: { /* 'password' prompt: send the password, and expect the basic prompt. */ + prompt: /^Password: $/, + macros: { + auto: { + cmd: "$$NetshotPassword$$", + options: [ "loginAgain", "basic" ] + } + } + }, + loginAgain: { /* 'login' prompt again: this means that authentication failed. */ + prompt: / login: $/, + fail: "Authentication failed - Telnet authentication failure." + }, + basic: { /* The basic FortiOS prompt. */ + prompt: /^[A-Za-z0-9_\-\~]+?\s(\([A-Za-z0-9_\-\~]+?\)\s)?[#$]\s?$/, + error: /^(Unknown action|Command fail)/m, + pager: { /* 'pager': define how to handle the pager for long outputs. */ + match: /^--More-- /, + response: " " + }, + macros: { + } + + } +}; + +/** + * The 'snapshot' function entry point = Will be called by Netshot when initiating a snapshot of this type of device. + * @param cli = object used to interact with the current device via CLI. + * @param device = used to store data at the device level. + * @param config = used to store data at the configuration revision level. + */ +function snapshot(cli, device, config) { + // Targets the 'basic' CLI mode. + cli.macro("basic"); + + // 'status' will be used to read the version, hostname, etc. + var status = cli.command("get system status"); + + // Read version and family from the 'status' output. + var version = status.match(/Version:[\s]*(FortiMail([a-zA-Z0-9\-]*)) v(.*)/); + var family = "FortiMail"; + device.set("family", family); + version = (version ? version[3] : "Unknown"); + device.set("softwareVersion", version); + config.set("osVersion", version); + + device.set("networkClass", "SERVER"); + + // The main configuration + var configuration = null; + + // Store the interface config block + var showSystemInterface = cli.command("show system interface"); + // Store the SNMP config block + var showSystemSnmp = cli.command("show system snmp sysinfo"); + // Store the HA status + var getHa = cli.command("get system ha"); + + var configuration; + // The configuration is retrieved by a simple 'show' at the root level. Add grep to avoid paging. + configuration = cli.command("show", { timeout: 120000 }); + + // Read the device hostname from the 'status' output. + var hostname = status.match(/Hostname[\s]*:[\s]*(.*)$/m); + if (hostname) { + hostname = hostname[1]; + device.set("name", hostname); + } + // Read the serial number from the 'status' output. + var serial = status.match(/Serial-Number[\s]*:[\s]*(.*)$/m); + if (serial) { + var module = { + slot: "Chassis", + partNumber: family, + serialNumber: serial[1] + }; + device.add("module", module); + device.set("serialNumber", serial[1]); + } + else { + device.set("serialNumber", ""); + } + + // Read the contact and location fields from the configuration directly. + device.set("contact", ""); + device.set("location", ""); + var sysInfos = cli.findSections(showSystemSnmp, /config system snmp sysinfo/); + for (var s in sysInfos) { + var contact = sysInfos[s].config.match(/set contact (.*)/); + if (contact) { + device.set("contact", contact[1]); + } + var location = sysInfos[s].config.match(/set location (.*)/); + if (location) { + device.set("location", location[1]); + } + } + var peerPattern = /^([\s]*config-peer-ip[\s]*: )(.*)$/gm; + var match; + while (match = peerPattern.exec(getHa)) { + device.set("haPeer", match[2]); + break; + } + + // Read the list of interfaces. + var interfaces = cli.findSections(showSystemInterface, /^ *edit (.*)/m); + for (var i in interfaces) { + var networkInterface = { + name: interfaces[i].match[1], + ip: [] + }; + var ipAddress = interfaces[i].config.match(/set ip (\d+\.\d+\.\d+\.\d+)\/(\d{2})/); + if (ipAddress) { + var ip = { + ip: ipAddress[1], + mask: parseInt(ipAddress[2]), + usage: "PRIMARY" + }; + networkInterface.ip.push(ip); + } + + if (interfaces[i].config.match(/set status down/)) { + networkInterface.disabled = true; + } + var descMatch = interfaces[i].config.match(/set description "(.+)"/); + if (descMatch) { + networkInterface.description = descMatch[1]; + } + device.add("networkInterface", networkInterface); + } + + var removeChangingParts = function(text) { + var cleaned = text; + cleaned = cleaned.replace(/^#conf_file_ver=.*/mg, ""); + cleaned = cleaned.replace(/^ *set [a-z\-]+ ENC .*$/mg, ""); + cleaned = cleaned.replace(/^ *set private-key "(.|[\r\n])*?"$/mg, ""); + return cleaned; + } + + // If only the passwords are changing (they are hashed with a new salt at each 'show') then + // just keep the previous configuration. + // That means we could miss a password change in the history of configurations, but no choice... + var previousConfiguration = device.get("configuration"); + if (typeof previousConfiguration === "string" && + removeChangingParts(previousConfiguration) === removeChangingParts(configuration)) { + config.set("configuration", previousConfiguration); + } + else { + config.set("configuration", configuration); + } + +}; + +// No known log message upon configuration change + +/** + * The 'analyzeTrap' function entry point = Will be called when receiving a trap for a device of this type. + * @param trap = the SNMP data embedded in the trap. + * @returns true if the trap indicates a configuration changes (this means to initiate a new snapshot). + */ +function analyzeTrap(trap) { + return trap["1.3.6.1.6.3.1.1.4.1.0"] == "1.3.6.1.2.1.47.2.0.1"; +} + +/** + * The 'snmpAutoDiscover' function entry point = Will be called with the sysObjectID and sysDesc + * of a device when auto discovering the type of a device. + * @param sysObjectID = The SNMP sysObjectID of a device being discovered. + * @param sysDesc = The SNMP sysDesc of a device being discovered. + * @returns true if the scanned device is supported by this driver. + */ +function snmpAutoDiscover(sysObjectID, sysDesc) { + return sysObjectID.startsWith("1.3.6.1.4.1.12356.105"); +} diff --git a/src/main/resources/drivers/Fortinet_FortiManager.js b/src/main/resources/drivers/Fortinet_FortiManager.js new file mode 100644 index 00000000..9390a62f --- /dev/null +++ b/src/main/resources/drivers/Fortinet_FortiManager.js @@ -0,0 +1,289 @@ +/** + * Copyright 2013-2024 Netshot + * + * This file is part of Netshot. + * + * Netshot 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 3 of the License, or + * (at your option) any later version. + * + * Netshot 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 Netshot. If not, see . + */ + +/** + * 'Info' object = Meta data of the driver. + */ +var Info = { + name: "FortinetFortiManager", /* Unique identifier of the driver within Netshot. */ + description: "Fortinet FortiManager and FortiAnalyzer", /* Description to be used in the UI. */ + author: "Najihel", + version: "1.2" /* Version will appear in the Admin tab. */ +}; + +/** + * 'Config' object = Data fields to be included in each configuration revision. + */ +var Config = { + "osVersion": { /* This stores the detected FortiManager OS version on each snapshot. */ + type: "Text", + title: "FortiManager OS version", + comparable: true, + searchable: true, + checkable: true, + dump: { + pre: "## FortiManager OS version:", + preLine: "## " + } + }, + "configuration": { /* This stores the full textual configuration of the device on each snapshot. */ + type: "LongText", + title: "Configuration", + comparable: true, + searchable: true, + checkable: true, + dump: { + pre: "## Configuration (taken on %when%):", + post: "## End of configuration" + } + } +}; + +/** + * 'Device' object = Data fields to add to devices of this type. + */ +var Device = { + "haPeer": { /* This stores the name of an optional HA peer. */ + type: "Text", + title: "HA peer name", + searchable: true, + checkable: true + } +}; + +/** + * 'CLI' object = Definition of the finite state machine to recognize and handle the CLI prompt changes. + */ +var CLI = { + telnet: { /* Entry point for Telnet access. */ + macros: { /* List of available macros in the CLI mode. */ + basic: { /* 'basic' macro (will be called in the snapshot procedure. */ + options: [ "login", "password", "basic" ], /* Possible next modes, Netshot will test the associated prompt regexp's. */ + target: "basic" /* Netshot will target this mode. */ + } + } + }, + ssh: { /* Entry point for SSH access. */ + macros: { + basic: { + options: [ "basic" ], + target: "basic" + } + } + }, + login: { /* 'login' prompt: send the username and expect the password prompt. */ + prompt: / login: $/, + macros: { + auto: { + cmd: "$$NetshotUsername$$", + options: [ "password" ] + } + } + }, + password: { /* 'password' prompt: send the password, and expect the basic prompt. */ + prompt: /^Password: $/, + macros: { + auto: { + cmd: "$$NetshotPassword$$", + options: [ "loginAgain", "basic" ] + } + } + }, + loginAgain: { /* 'login' prompt again: this means that authentication failed. */ + prompt: / login: $/, + fail: "Authentication failed - Telnet authentication failure." + }, + basic: { /* The basic FortiOS prompt. */ + prompt: /^[A-Za-z0-9_\-\~]+?\s(\([A-Za-z0-9_\-\~]+?\)\s)?[#$]\s?$/, + error: /^(Unknown action|Command fail)/m, + pager: { /* 'pager': define how to handle the pager for long outputs. */ + match: /^--More-- /, + response: " " + }, + macros: { + } + + } +}; + +/** + * The 'snapshot' function entry point = Will be called by Netshot when initiating a snapshot of this type of device. + * @param cli = object used to interact with the current device via CLI. + * @param device = used to store data at the device level. + * @param config = used to store data at the configuration revision level. + */ +function snapshot(cli, device, config) { + // Targets the 'basic' CLI mode. + cli.macro("basic"); + + // 'status' will be used to read the version, hostname, etc. + var status = cli.command("get system status"); + + // Read version and family from the 'status' output. + var version = status.match(/^Version[\s]*: v([0-9]+.*)/m); + var platform = status.match(/^Platform Full Name[\s]*: ((.*?)(-.*)?)$/m); + var family = (platform ? platform[2] : "FortiManager"); + platform = (platform ? platform[1] : "FortiManager"); + device.set("family", family); + version = (version ? version[1] : "Unknown"); + device.set("softwareVersion", version); + config.set("osVersion", version); + + device.set("networkClass", "SERVER"); + + // The main configuration + var configuration = null; + + // Store the interface config block + var showSystemInterface = cli.command("show system interface"); + // Store the SNMP config block + var showSystemSnmp = cli.command("show system snmp sysinfo"); + + // Store the HA status + var getHa = ""; + var haCommands = ["get system ha-status", "diagnose ha status"]; + while (true) { + try { + getHa = haCommands.shift(); + break; + } + catch (e) { + // ignore + } + } + + var configuration; + // The configuration is retrieved by a simple 'show' at the root level. Add grep to avoid paging. + configuration = cli.command("show", { timeout: 120000 }); + + // Read the device hostname from the 'status' output. + var hostname = status.match(/^Hostname[\s]*:[\s]*(.*)$/m); + if (hostname) { + hostname = hostname[1]; + device.set("name", hostname); + } + // Read the serial number from the 'status' output. + var serial = status.match(/^Serial Number[\s]*:[\s]*(.*)$/m); + if (serial) { + var module = { + slot: "Chassis", + partNumber: platform, + serialNumber: serial[1] + }; + device.add("module", module); + device.set("serialNumber", serial[1]); + } + else { + device.set("serialNumber", ""); + } + + // Read the contact and location fields from the configuration directly. + device.set("contact", ""); + device.set("location", ""); + var sysInfos = cli.findSections(showSystemSnmp, /config system snmp sysinfo/); + for (var s in sysInfos) { + var contact = sysInfos[s].config.match(/set contact_info "(.*)"/); + if (contact) { + device.set("contact", contact[1]); + } + var location = sysInfos[s].config.match(/set location "(.*)"/); + if (location) { + device.set("location", location[1]); + } + } + var peerPattern = /^([\s]*hostname:[\s]*)(.*)$/gm; + var match; + while (match = peerPattern.exec(getHa)) { + if (match[2] != hostname) { + device.set("haPeer", match[2]); + break; + } + } + + // Read the list of interfaces. + var vdomArp = {}; + var interfaces = cli.findSections(showSystemInterface, /^ *edit "(.*)"/m); + for (var i in interfaces) { + var networkInterface = { + name: interfaces[i].match[1], + ip: [] + }; + var ipAddress = interfaces[i].config.match(/set ip (\d+\.\d+\.\d+\.\d+) (\d+\.\d+\.\d+\.\d+)/); + if (ipAddress) { + var ip = { + ip: ipAddress[1], + mask: ipAddress[2], + usage: "PRIMARY" + }; + networkInterface.ip.push(ip); + } + + if (interfaces[i].config.match(/set status down/)) { + networkInterface.disabled = true; + } + var descMatch = interfaces[i].config.match(/set description "(.+)"/); + if (descMatch) { + networkInterface.description = descMatch[1]; + } + device.add("networkInterface", networkInterface); + } + + var removeChangingParts = function(text) { + var cleaned = text; + cleaned = cleaned.replace(/^#conf_file_ver=.*/mg, ""); + cleaned = cleaned.replace(/^ *set (passphrase|password|passwd|secondary-secret|crptpasswd) ENC .*$/mg, ""); + cleaned = cleaned.replace(/^ *set private-key "(.|[\r\n])*?"$/mg, ""); + return cleaned; + } + + // If only the passwords are changing (they are hashed with a new salt at each 'show') then + // just keep the previous configuration. + // That means we could miss a password change in the history of configurations, but no choice... + var previousConfiguration = device.get("configuration"); + if (typeof previousConfiguration === "string" && + removeChangingParts(previousConfiguration) === removeChangingParts(configuration)) { + config.set("configuration", previousConfiguration); + } + else { + config.set("configuration", configuration); + } + +}; + +// No known log message upon configuration change + +/** + * The 'analyzeTrap' function entry point = Will be called when receiving a trap for a device of this type. + * @param trap = the SNMP data embedded in the trap. + * @returns true if the trap indicates a configuration changes (this means to initiate a new snapshot). + */ +function analyzeTrap(trap) { + return trap["1.3.6.1.6.3.1.1.4.1.0"] == "1.3.6.1.2.1.47.2.0.1"; +} + +/** + * The 'snmpAutoDiscover' function entry point = Will be called with the sysObjectID and sysDesc + * of a device when auto discovering the type of a device. + * @param sysObjectID = The SNMP sysObjectID of a device being discovered. + * @param sysDesc = The SNMP sysDesc of a device being discovered. + * @returns true if the scanned device is supported by this driver. + */ +function snmpAutoDiscover(sysObjectID, sysDesc) { + return sysObjectID.startsWith("1.3.6.1.4.1.12356.103."); +}