├── .gitignore ├── LICENCE ├── MANIFEST.in ├── README.md ├── examples ├── addvlan.py ├── example.script └── getproperty.py ├── setup.py ├── stormshield ├── __init__.py └── sns │ ├── __init__.py │ ├── bundle.ca │ ├── cli.py │ ├── cmd.complete │ ├── configparser.py │ ├── crc.py │ └── sslclient │ ├── __init__.py │ └── __version__.py ├── tests ├── test_auth.py ├── test_cert.py ├── test_configparser.py ├── test_file.py ├── test_format_ini.py ├── test_proxy.py └── test_utf8.py └── tox.ini /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | dist 3 | build 4 | .eggs 5 | stormshield.sns.sslclient.egg-info 6 | __pycache__ 7 | *.pyc 8 | .tox 9 | -------------------------------------------------------------------------------- /LICENCE: -------------------------------------------------------------------------------- 1 | Copyright 2018 Stormshield 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include stormshield/sns/bundle.ca 2 | include stormshield/sns/cmd.complete 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # python-SNS-API 2 | 3 | A Python client for the Stormshield Network Security appliance SSL API. 4 | 5 | Note: this module requires python3.7 or later. 6 | 7 | ## API usage 8 | 9 | ```python 10 | from stormshield.sns.sslclient import SSLClient 11 | 12 | client = SSLClient( 13 | host="10.0.0.254", port=443, 14 | user='admin', password='password', 15 | sslverifyhost=False) 16 | 17 | response = client.send_command("SYSTEM PROPERTY") 18 | 19 | if response: 20 | model = response.data['Result']['Model'] 21 | version = response.data['Result']['Version'] 22 | 23 | print("Model: {}".format(model)) 24 | print("Firmware version: {}".format(version)) 25 | else: 26 | print("Command failed: {}".format(response.output)) 27 | 28 | client.disconnect() 29 | 30 | ``` 31 | 32 | * **Note:** Starting from the 5.0 firmware, a custom CA is used by default by the SSL API. To continue to connect checking the certificate authority of the appliance, the "SNS-WebServer-default-authority" CA must be retrieved from each appliance, then added to a cabundle.pem file. Alternatively, CA verification can be bypassed using sslverifypeer=False argument to SSLClient(). 33 | 34 | ### Command results 35 | 36 | Command results are available in text, xml or python structure formats: 37 | 38 | ```python 39 | >>> response = client.send_command("CONFIG NTP SERVER LIST") 40 | 41 | >>> print(response.output) 42 | 101 code=00a01000 msg="Begin" format="section_line" 43 | [Result] 44 | name=ntp1.stormshieldcs.eu keynum=none type=host 45 | name=ntp2.stormshieldcs.eu keynum=none type=host 46 | 100 code=00a00100 msg="Ok" 47 | 48 | >>> print(response.xml) 49 | 50 |
51 | 52 | >>> print(response.data) 53 | {'Result': [{'name': 'ntp1.stormshieldcs.eu', 'keynum': 'none', 'type': 'host'}, {'name': 'ntp2.stormshieldcs.eu', 'keynum': 'none', 'type': 'host'}]} 54 | 55 | ``` 56 | 57 | The keys of the `data` property are case insensitive, `response.data['Result'][0]['name']` and `response.data['ReSuLt'][0]['NaMe']` will return the same value. 58 | 59 | Results token are also available via `response.parser.get()` method which accepts a default parameter to return if the token is not present. 60 | 61 | ```python 62 | >>> print(response.output) 63 | 101 code=00a01000 msg="Begin" format="section" 64 | [Server] 65 | 1=dns1.google.com 66 | 2=dns2.google.com 67 | 100 code=00a00100 msg="Ok" 68 | 69 | >>> print(response.data['Server']['3']) 70 | Traceback (most recent call last): 71 | File "", line 1, in 72 | File "/usr/local/lib/python3.7/site-packages/requests/structures.py", line 52, in __getitem__ 73 | return self._store[key.lower()][1] 74 | KeyError: '3' 75 | 76 | >>> print(response.parser.get(section='Server', token='3', default=None)) 77 | None 78 | 79 | ``` 80 | 81 | ### File upload/download 82 | 83 | Files can be downloaded to or uploaded from the client host by adding a redirection to a file with '>' or '<' at the end of the configuration command. 84 | 85 | ```python 86 | >>> client.send_command("CONFIG BACKUP list=all > /tmp/mybackup.na") 87 | 100 code=00a00100 msg="Ok" 88 | ``` 89 | 90 | ## snscli 91 | 92 | `snscli` is a python cli for executing configuration commands and scripts on Stormshield Network Security appliances. 93 | 94 | * Output format can be chosen between section/ini or xml 95 | * File upload and download available with adding `< upload` or `> download` at the end of the command 96 | * Client can execute script files using `--script` option. 97 | * Comments are allowed with `#` 98 | 99 | `$ snscli --host ` 100 | 101 | `$ snscli --host --user admin --password admin --script config.script` 102 | 103 | Concerning the SSL validation: 104 | 105 | * For the first connection to a new appliance, ssl host name verification can be bypassed with `--no-sslverifyhost` option. 106 | * To connect to a known appliance with the default certificate use `--host --ip ` to validate the peer certificate. 107 | * If a custom CA and certificate is installed, use `--host myfirewall.tld --cabundle `. CA bundle should contain at least the root CA. 108 | * For client certificate authentication, the expected format is a PEM file with the certificate and the unencrypted key concatenated. 109 | 110 | * **Note:** Starting from the 5.0 firmware, a custom CA is used by default by the SSL API. To continue to connect checking the certificate authority of the appliance, the "SNS-WebServer-default-authority" CA must be retrieved from each appliance, then added to a cabundle.pem file. Alternatively, CA verification can be bypassed using `--no-sslverifypeer` option. 111 | 112 | ## Proxy 113 | 114 | The library and `snscli` tool support HTTP and SOCKS proxies, use `--proxy scheme://user:password@host:port` option. 115 | 116 | 117 | ## Build 118 | 119 | `$ python3 setup.py sdist bdist_wheel` 120 | 121 | 122 | ## Install 123 | 124 | ## From PyPI: 125 | 126 | `$ pip3 install stormshield.sns.sslclient` 127 | 128 | ## From source: 129 | 130 | `$ python3 setup.py install` 131 | 132 | 133 | ## Tests 134 | 135 | Warning: some tests require a remote SNS appliance. 136 | 137 | `$ PASSWORD=password APPLIANCE=10.0.0.254 tox` 138 | 139 | To run one test: 140 | 141 | `tox -- tests/test_format_ini` 142 | 143 | 144 | To run `snscli` from the source folder without install: 145 | 146 | `$ PYTHONPATH=. python3 stormshield/sns/cli.py --help` 147 | 148 | 149 | ## Links 150 | 151 | * [Stormshield corporate website](https://www.stormshield.com) 152 | * [CLI commands reference guide](https://documentation.stormshield.eu/SNS/v3/en/Content/CLI_Serverd_Commands_reference_Guide_v3/Introduction.htm) 153 | * [Python SNS API example scripts](https://github.com/stormshield/sns-scripting/tree/master/python) 154 | -------------------------------------------------------------------------------- /examples/addvlan.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | """ 4 | Script to create a VLAN interface on a SNS appliance 5 | """ 6 | 7 | import sys 8 | import getpass 9 | 10 | from stormshield.sns.sslclient import SSLClient 11 | 12 | # user input 13 | host = input("Appliance ip address: ") 14 | user = input("User:") 15 | password = getpass.getpass("Password: ") 16 | vlanname = input("VLAN name: ") 17 | vlanphy = input("Physical interface: ") 18 | vlantag = input("VLAN tag: ") 19 | vlanaddr = input("Address: ") 20 | vlanmask = input("Mask: ") 21 | 22 | #host = "10.0.0.0.254" 23 | #user = "admin" 24 | #password = "mypassword" 25 | #vlanname = "myvlan3" 26 | #vlanphy = "Ethernet0" 27 | #vlantag = 103 28 | #vlanaddr = "192.168.103.1" 29 | #vlanmask = "255.255.255.0" 30 | 31 | MAXVLAN=60 32 | 33 | # connect to the appliance 34 | client = SSLClient( 35 | host=host, port=443, 36 | user=user, password=password, 37 | sslverifyhost=False) 38 | 39 | def error(msg): 40 | global client 41 | 42 | print("ERROR: {}".format(msg)) 43 | client.disconnect() 44 | sys.exit(1) 45 | 46 | def command(cmd): 47 | global client 48 | 49 | response = client.send_command(cmd) 50 | if not response: 51 | error("command failed:\n{}".format(response.output)) 52 | 53 | return response 54 | 55 | 56 | # get vlan list & extract first available vlanX interface 57 | response = command("config network interface show filter=vlan") 58 | if len(response.data.keys()) == 0: 59 | vlanid = 0 60 | else: 61 | vlanid = -1 62 | for i in range(MAXVLAN): 63 | if "vlan{}".format(i) not in response.data: 64 | vlanid = i 65 | break 66 | if vlanid == -1: 67 | error("all available VLAN already created") 68 | 69 | 70 | response = command("CONFIG NETWORK INTERFACE CREATE state=1 protected=0 mtu=1500 physical={} name={} tag={} priority=0 keepVlanPriority=1 maxThroughput=0 ifname=vlan{} address={} mask={}".format(vlanphy, vlanname, vlantag, vlanid, vlanaddr, vlanmask)) 71 | if response.code: 72 | print("VLAN vlan{} created".format(vlanid)) 73 | else: 74 | error("VLAN vlan{} can't be created:\n{}".format(vlanid, response.output)) 75 | 76 | response = command("CONFIG NETWORK ACTIVATE") 77 | if response.code: 78 | print("Configuration activated") 79 | else: 80 | error("Can't activate network:\n{}".format(response.output)) 81 | 82 | client.disconnect() 83 | -------------------------------------------------------------------------------- /examples/example.script: -------------------------------------------------------------------------------- 1 | #system information 2 | SYSTEM PROPERTY 3 | 4 | -------------------------------------------------------------------------------- /examples/getproperty.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | """ 4 | This example show how to connect to a SNS appliance, send a command 5 | to get appliance properties and parse the result to extract the 6 | appliance model and firmware version. 7 | """ 8 | 9 | import getpass 10 | 11 | from stormshield.sns.sslclient import SSLClient 12 | 13 | # user input 14 | host = input("Appliance ip address: ") 15 | user = input("User:") 16 | password = getpass.getpass("Password: ") 17 | 18 | # connect to the appliance 19 | client = SSLClient( 20 | host=host, port=443, 21 | user=user, password=password, 22 | sslverifyhost=False) 23 | 24 | # request appliance properties 25 | response = client.send_command("SYSTEM PROPERTY") 26 | 27 | if response: 28 | #get value using parser get method 29 | model = response.parser.get(section='Result', token='Model') 30 | # get value with direct access to data 31 | version = response.data['Result']['Version'] 32 | 33 | print("") 34 | print("Model: {}".format(model)) 35 | print("Firmware version: {}".format(version)) 36 | else: 37 | print("Command failed: {}".format(response.output)) 38 | 39 | client.disconnect() 40 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | import setuptools 4 | import os 5 | 6 | version = {} 7 | with open(os.path.join('stormshield', 'sns', 'sslclient', '__version__.py'), 'r') as fh: 8 | exec(fh.read(), version) 9 | 10 | with open("README.md", "r") as fh: 11 | long_description = fh.read() 12 | 13 | setuptools.setup( 14 | name="stormshield.sns.sslclient", 15 | version=version['__version__'], 16 | author="Remi Pauchet", 17 | author_email="remi.pauchet@stormshield.eu", 18 | description="SSL API client for Stormshield Network Security appliances", 19 | long_description=long_description, 20 | long_description_content_type="text/markdown", 21 | url="https://github.com/stormshield/python-SNS-API", 22 | license='Apache License 2.0', 23 | packages=setuptools.find_packages(), 24 | entry_points={ 25 | 'console_scripts': ['snscli=stormshield.sns.cli:main'], 26 | }, 27 | install_requires=[ 28 | 'pygments', 29 | 'requests[socks]', 30 | 'requests_toolbelt', 31 | 'colorlog', 32 | 'defusedxml', 33 | 'packaging', 34 | 'pyreadline; platform_system == "Windows"' 35 | ], 36 | include_package_data=True, 37 | tests_require=["pytest"], 38 | classifiers=[ 39 | "Programming Language :: Python :: 3", 40 | "Programming Language :: Python :: 3.7", 41 | "License :: OSI Approved :: Apache Software License", 42 | "Operating System :: OS Independent", 43 | "Topic :: System :: Networking", 44 | "Environment :: Console" 45 | ], 46 | ) 47 | -------------------------------------------------------------------------------- /stormshield/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stormshield/python-SNS-API/05b719bfd8a1777f29fa25c385b357f7e56576ef/stormshield/__init__.py -------------------------------------------------------------------------------- /stormshield/sns/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stormshield/python-SNS-API/05b719bfd8a1777f29fa25c385b357f7e56576ef/stormshield/sns/__init__.py -------------------------------------------------------------------------------- /stormshield/sns/bundle.ca: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIEFDCCAvygAwIBAgIBADANBgkqhkiG9w0BAQsFADCBmjELMAkGA1UEBhMCRlIx 3 | DTALBgNVBAgTBE5vcmQxGjAYBgNVBAcTEVZpbGxlbmV1dmUgZCdBc2NxMS4wLAYD 4 | VQQKEyVORVRBU1EgLSBTZWN1cmUgSW50ZXJuZXQgQ29ubmVjdGl2aXR5MTAwLgYD 5 | VQQLEydORVRBU1EgRmlyZXdhbGwgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcN 6 | MDIwNTE0MTIxNTI1WhcNMzgwMTAxMTMxMjU1WjCBmjELMAkGA1UEBhMCRlIxDTAL 7 | BgNVBAgTBE5vcmQxGjAYBgNVBAcTEVZpbGxlbmV1dmUgZCdBc2NxMS4wLAYDVQQK 8 | EyVORVRBU1EgLSBTZWN1cmUgSW50ZXJuZXQgQ29ubmVjdGl2aXR5MTAwLgYDVQQL 9 | EydORVRBU1EgRmlyZXdhbGwgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEiMA0G 10 | CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDaKogZ3NPpoJyFk4JaY6EgyI2t53CN 11 | Xx48WxeNZ3iWuQqQFuFCc1AIZ8bqI4N8T9JoneKv80sMZXvMNOIrYrPae3S4+ecO 12 | nDRtpSj96EdhDxW4aG6Q3JF/TFjMYwmkcxepFUMYe6O+kDFQUEEmWIzNY+kIoxhH 13 | Nd+ySjwPyzkcfspcTrxFRfnZsX/6RcbOWRRJ/HnhgcFZS2SGAFag4P7ULyAaThai 14 | xkFWoT+g2E2suFo6CaMtkxekzNVO9u1ePUgp/uCjZzZ5XvjlvRZg4E7ZHw93noIB 15 | g9VHXj9yKdGUhoN0GnVBD2CPERTgzYUfoQ4jNxbRJEV1LNgbQ9AdwVW7AgMBAAGj 16 | YzBhMB0GA1UdDgQWBBTNdCUgNq+7yVN9mLhW6Q6m3yfubTAfBgNVHSMEGDAWgBTN 17 | dCUgNq+7yVN9mLhW6Q6m3yfubTAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQE 18 | AwIBBjANBgkqhkiG9w0BAQsFAAOCAQEANlzs11N2N1fWTnpD2DI1m6f9/FAkHgZ7 19 | OSberI7M8hIM6ozHrw0OcOSnjoXqrBV/7qjemUki3TpSuiDNxq0Lcfu9rwH+y0hr 20 | DIgxvp8kj9JJsvUjCZLVwY8MTAiBaHEeZ5PFx9KZjKm9iF2MyVuiHdV3BZYmBghM 21 | J7OTjMulwnr/rRfnjuhpwUhcu+bg2gLUYFPFAQv/MlyFHRBk7vOMXphq4U+YEj+m 22 | LK5MP7gayXh6ZKhWWlFzhIb5l9vhjc3P2pgBKI6tuOsDsy3cO46AmNTz4LTd2Jgi 23 | 6Su2L8FWWOI7JTEc97NcW9IE1xHBq/ymLQFlNk2xM0qDyU9PARgG8Q== 24 | -----END CERTIFICATE----- 25 | -----BEGIN CERTIFICATE----- 26 | MIIF/TCCA+WgAwIBAgIJAI5vhN6XX26VMA0GCSqGSIb3DQEBCwUAMIGBMQswCQYD 27 | VQQGEwJGUjEcMBoGA1UEBwwTSXNzeS1MZXMtTW91bGluZWF1eDEUMBIGA1UECgwL 28 | U3Rvcm1zaGllbGQxFzAVBgNVBAsMDkNsb3VkIFNlcnZpY2VzMSUwIwYDVQQDDBxT 29 | dG9ybXNoaWVsZCBQcm9kdWN0cyBSb290IENBMB4XDTE2MDUyNjExMzc1MloXDTM4 30 | MDExMTExMzc1MlowgYExCzAJBgNVBAYTAkZSMRwwGgYDVQQHDBNJc3N5LUxlcy1N 31 | b3VsaW5lYXV4MRQwEgYDVQQKDAtTdG9ybXNoaWVsZDEXMBUGA1UECwwOQ2xvdWQg 32 | U2VydmljZXMxJTAjBgNVBAMMHFN0b3Jtc2hpZWxkIFByb2R1Y3RzIFJvb3QgQ0Ew 33 | ggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC87MX9Nwu7Cv9JI0+EcIVU 34 | eWQ5byTXhRhOIKwYbDA4cu9Yv74IYYL0xOuUFDR2i+NO4kbyVL43Rw5114ezIYlb 35 | IbzZDppAZJNEkapRVMWeGhOjPkUJ+dzvIZGBw0+j0KuKRpVMNLT2ej2SMl2/wy8j 36 | 8sEWHb+4dgnVW7izHfianBt3979MQQVAilEw6KOecM9IMJBcjUOURISsemHufdfg 37 | 7U6JlRwwvq00w9GIjf7IzkQLMstVOIG3ZvROp0Ari6EOOxZcTM/19pnoiTjDoK28 38 | bWw2raOxfZ9N4P9nxUCbcyhPWGjj34Q3JOJ82yEPEfgNxOP4p8hAMQ8aWF5wADRx 39 | 6Z6fDwL1GT6MftTNC9h+m1ihZzsI2bzLEria9geQBDRO7yohAgQImutOIBoM8fXF 40 | zEuH9Kr694LzLfCWlmo33eCzSuQXg/YhRYH4h2L9dWqJOaYbfjNeZwQ3dl+nPJIe 41 | LVKp6ERWREppaKwrzLSgT4T+D35M3G8iayNYMC9VeKIKtzbvtADl/IENobgDZeUd 42 | +G3bMZABlXTp36oqPV5Nsv6X9UQExJ0afxe0iZ+FGGQd7Ck4WVdt+Tt65bU6ghbV 43 | JOW947jX9ITPBOBM8nGDrXPxl4wPtRexTJD4w5nYBVjeyHZjCW6A1DHF6RqRbOd2 44 | bA52XAuKK8ZxgKTbmuQE7wIDAQABo3YwdDAdBgNVHQ4EFgQUzHPVW9zZflquZZSI 45 | iqPZUPkx5YcwHwYDVR0jBBgwFoAUzHPVW9zZflquZZSIiqPZUPkx5YcwDgYDVR0P 46 | AQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wEQYDVR0gBAowCDAGBgRVHSAAMA0G 47 | CSqGSIb3DQEBCwUAA4ICAQAoU9mMVuKyYjZhhwigIrbT7EBGd/r2FmRlTOLCnZ0b 48 | sUyfcMfqOhMp0quUK0FBheeO7U7uYb63Fix9La6TKk+dSLRGgev1zrMDXOcup6Nb 49 | hMn62+s7r4ejqr8MTycZue4e3w4V698Odg4vMC8KHRTQhMgC9co/Tk4Q+1/qnSWX 50 | c6jBUO+SVwjM35MwigM09uqfxk4ThDDNJ5t4J2GTpLuLJQ09zLmSb02Shj2Nvb5G 51 | LSQsfGGwIZUZYG37I8ovg2RwKT7P5+2hsFHJJYSaM5s6l97aUh/1yU1s28cEf/7m 52 | RVHWGnulphhUpimCXbrT3jpRVfYitR0Mayr00VsY2q5UrWnJ0U+EXJbLEzmRP/E8 53 | v8R/2Fn9Qng80YwpNO4aZkaoiuQ1jjYVym/MesCSlDT5FE/dx5BMWlrcJtXni5YZ 54 | BOU6Kuyc4bugAw6oUpAd1tihFx9sf4FmKrBwq/v7n9zbaU2omBujN4vsoXMX/xdR 55 | cnqLrINlzFaVzCKuWwXBJKD/G7+d/R4JqUJ3XXZxa+NxmgiwDlgPIBQ6sicVgK4v 56 | Zktp7z0lAlJxpvhp4ToKbJ+OrkKG/0xK1n+16NxNK9kWPRgzegHa3Sm8wowkosap 57 | KFTN0dOrAYBfAC5eAzcssn25wHlDQLW0Lb+KjXxGwDC6mZnhiciFHfSPaPbt9W+z 58 | Ow== 59 | -----END CERTIFICATE----- 60 | -------------------------------------------------------------------------------- /stormshield/sns/cli.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | """ cli to connect to Stormshield Network Security appliances""" 4 | 5 | from __future__ import unicode_literals 6 | import sys 7 | import os 8 | import re 9 | import logging 10 | import logging.handlers 11 | import readline 12 | import getpass 13 | import atexit 14 | import defusedxml.minidom 15 | import argparse 16 | import platform 17 | from pygments import highlight 18 | from pygments.lexers import XmlLexer 19 | from pygments.formatters import TerminalFormatter 20 | from colorlog import LevelFormatter 21 | 22 | from stormshield.sns.sslclient import SSLClient, ServerError, TOTPNeededError 23 | from stormshield.sns.sslclient.__version__ import __version__ as libversion 24 | from urllib3 import __version__ as urllib3version 25 | from requests import __version__ as requestsversion 26 | 27 | # define missing exception for python2 28 | try: 29 | FileNotFoundError 30 | except NameError: 31 | FileNotFoundError = IOError 32 | 33 | OUTPUT_LEVELV_NUM = 60 # log command response 34 | COMMAND_LEVELV_NUM = 59 # log command input 35 | FORMATTER = LevelFormatter( 36 | fmt={ 37 | 'DEBUG': "%(log_color)s%(levelname)-8s%(reset)s %(message)s", 38 | 'INFO': "%(log_color)s%(levelname)-8s%(reset)s %(message)s", 39 | 'WARNING': "%(log_color)s%(levelname)-8s%(reset)s %(message)s", 40 | 'ERROR': "%(log_color)s%(levelname)-8s%(reset)s %(message)s", 41 | 'CRITICAL': "%(log_color)s%(levelname)-8s%(reset)s %(message)s", 42 | 'OUTPUT': "%(message)s", 43 | 'COMMAND': "%(message)s" 44 | }, 45 | datefmt=None, 46 | reset=True, 47 | log_colors={ 48 | 'DEBUG': 'green', 49 | 'INFO': 'cyan', 50 | 'WARNING': 'yellow', 51 | 'ERROR': 'red', 52 | 'CRITICAL': 'red,bg_white' 53 | }, 54 | secondary_log_colors={}, 55 | style='%' 56 | ) 57 | 58 | class CommandFilter(logging.Filter): 59 | def filter(self, record): 60 | if record.levelname == 'COMMAND': 61 | return False 62 | return True 63 | 64 | EMPTY_RE = re.compile(r'^\s*$') 65 | 66 | def make_completer(): 67 | """ load completer for readline """ 68 | vocabulary = [] 69 | with open(SSLClient.get_completer(), "r") as completelist: 70 | for line in completelist: 71 | vocabulary.append(line.replace('.', ' ').strip('\n')) 72 | 73 | def custom_complete(text, state): 74 | results = [x for x in vocabulary if x.startswith(text)] + [None] 75 | return results[state] 76 | return custom_complete 77 | 78 | def main(): 79 | 80 | # parse command line 81 | 82 | parser = argparse.ArgumentParser(conflict_handler="resolve") 83 | 84 | group = parser.add_argument_group("Connection parameters") 85 | group.add_argument("-h", "--host", help="Remote UTM", default=None) 86 | group.add_argument("-i", "--ip", help="Remote UTM ip", default=None) 87 | group.add_argument("-P", "--port", help="Remote port", default=443, type=int) 88 | group.add_argument("--proxy", help="Proxy URL (scheme://user:password@host:port)", default=None) 89 | group.add_argument("-t", "--timeout", help="Connection timeout in seconds", default=-1, type=int) 90 | 91 | group = parser.add_argument_group("Authentication parameters") 92 | group.add_argument("-u", "--user", help="User name", default="admin") 93 | group.add_argument("-p", "--password", help="Password", default=None) 94 | group.add_argument("-t", "--totp", help="Time-based one time password", default=None) 95 | group.add_argument("-U", "--usercert", help="User certificate file", default=None) 96 | 97 | group = parser.add_argument_group("SSL parameters") 98 | group.add_argument("-C", "--cabundle", help="CA bundle file", default=None) 99 | group.add_argument("--sslverifypeer", help="Strict SSL CA check", default=True, action="store_true") 100 | group.add_argument("-k", "--no-sslverifypeer", help="Disable strict SSL CA check", default=True, action="store_false", dest="sslverifypeer") 101 | group.add_argument("--sslverifyhost", help="Strict SSL host name check", default=True, action="store_true") 102 | group.add_argument("-K", "--no-sslverifyhost", help="Disable strict SSL host name check", default=True, action="store_false", dest="sslverifyhost") 103 | 104 | group = parser.add_argument_group("Protocol parameters") 105 | group.add_argument("-c", "--credentials", help="Privilege list", default=None) 106 | group.add_argument("-s", "--script", help="Command script", default=None) 107 | group.add_argument("-o", "--outputformat", help="Output format (ini|xml)", default="ini") 108 | 109 | parser.add_argument("--version", help="Library version", default=False, action="store_true") 110 | 111 | group = parser.add_argument_group("Logging parameters") 112 | exclusive = group.add_mutually_exclusive_group() 113 | exclusive.add_argument("-v", "--verbose", help="Increase logging output", default=False, action="store_true") 114 | exclusive.add_argument("-q", "--quiet", help="Decrease logging output", default=False, action="store_true") 115 | group.add_argument("--loglvl", help="Set explicit log level", default=None, choices=['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL']) 116 | group.add_argument("--logfile", help='Output log messages to file', default=None) 117 | 118 | args = parser.parse_args() 119 | 120 | host = args.host 121 | ip = args.ip 122 | usercert = args.usercert 123 | cabundle = args.cabundle 124 | password = args.password 125 | totp = args.totp 126 | port = args.port 127 | proxy = args.proxy 128 | timeout = args.timeout 129 | user = args.user 130 | sslverifypeer = args.sslverifypeer 131 | sslverifyhost = args.sslverifyhost 132 | credentials = args.credentials 133 | script = args.script 134 | outputformat = args.outputformat 135 | version = args.version 136 | 137 | # logging 138 | 139 | level = logging.INFO 140 | if args.loglvl is not None: 141 | level = logging.getLevelName(args.loglvl) 142 | elif args.verbose: 143 | level = logging.DEBUG 144 | elif args.quiet: 145 | level = logging.WARNING 146 | 147 | # add custom level 148 | logging.addLevelName(OUTPUT_LEVELV_NUM, "OUTPUT") 149 | logging.addLevelName(COMMAND_LEVELV_NUM, "COMMAND") 150 | 151 | def logoutput(self, message, *args, **kwargs): 152 | # Yes, logger takes its '*args' as 'args'. 153 | self._log(OUTPUT_LEVELV_NUM, message, args, **kwargs) 154 | def logcommand(self, message, *args, **kwargs): 155 | # Yes, logger takes its '*args' as 'args'. 156 | self._log(COMMAND_LEVELV_NUM, message, args, **kwargs) 157 | 158 | logging.Logger.output = logoutput 159 | logging.Logger.command = logcommand 160 | 161 | # logger 162 | logger = logging.getLogger() 163 | for handler in logger.handlers: 164 | logger.removeHandler(handler) 165 | logger.setLevel(level) 166 | 167 | # attach handlers 168 | handler = logging.StreamHandler(sys.stdout) 169 | handler.addFilter(CommandFilter()) 170 | logger.addHandler(handler) 171 | if args.logfile is not None: 172 | if platform.system() != 'Windows': 173 | handler = logging.handlers.WatchedFileHandler(args.logfile) 174 | else: 175 | handler = logging.FileHandler(args.logfile) 176 | logger.addHandler(handler) 177 | 178 | for handler in logger.handlers: 179 | if handler.__class__ == logging.StreamHandler: 180 | handler.setFormatter(FORMATTER) 181 | 182 | if version: 183 | logging.info("snscli - stormshield.sns.sslclient version {}".format(libversion)) 184 | logging.info(" urllib3 {}".format(urllib3version)) 185 | logging.info(" requests {}".format(requestsversion)) 186 | sys.exit(0) 187 | 188 | if script is not None: 189 | try: 190 | script = open(script, 'r') 191 | except Exception as exception: 192 | logging.error("Can't open script file - %s", str(exception)) 193 | sys.exit(1) 194 | 195 | if outputformat not in ['ini', 'xml']: 196 | logging.error("Unknown output format") 197 | sys.exit(1) 198 | 199 | if host is None: 200 | logging.error("No host provided") 201 | sys.exit(1) 202 | 203 | if password is None and usercert is None: 204 | password = getpass.getpass() 205 | 206 | if timeout == -1: 207 | timeout = None 208 | 209 | # first try without totp, if needed ask for totp 210 | for i in range(0, 2): 211 | try: 212 | client = SSLClient( 213 | host=host, ip=ip, port=port, user=user, password=password, totp=totp, 214 | sslverifypeer=sslverifypeer, sslverifyhost=sslverifyhost, 215 | credentials=credentials, proxy=proxy, timeout=timeout, 216 | usercert=usercert, cabundle=cabundle, autoconnect=False) 217 | except Exception as exception: 218 | logging.error(str(exception)) 219 | sys.exit(1) 220 | 221 | try: 222 | client.connect() 223 | except TOTPNeededError as exception: 224 | if i == 0 and totp is None: 225 | logging.warning("Second factor authentication is required.") 226 | totp = getpass.getpass("Totp:") 227 | continue 228 | else: 229 | logging.error(str(exception)) 230 | sys.exit(1) 231 | except Exception as exception: 232 | search = re.search(r'doesn\'t match \'(.*)\'', str(exception)) 233 | if search: 234 | logging.error(("Appliance name can't be verified, to force connection " 235 | "use \"--host %s --ip %s\" or \"--no-sslverifyhost|-K\" " 236 | "options"), search.group(1), ip if ip is not None else host) 237 | else: 238 | logging.error(str(exception)) 239 | sys.exit(1) 240 | else: 241 | break 242 | 243 | # disconnect gracefuly at exit 244 | atexit.register(client.disconnect) 245 | 246 | if script is not None: 247 | for cmd in script.readlines(): 248 | cmd = cmd.strip('\r\n') 249 | logger.output(cmd) 250 | if cmd.startswith('#'): 251 | continue 252 | if EMPTY_RE.match(cmd): 253 | continue 254 | try: 255 | response = client.send_command(cmd) 256 | except Exception as exception: 257 | logging.error(str(exception)) 258 | sys.exit(1) 259 | if outputformat == 'xml': 260 | logger.output(highlight(defusedxml.minidom.parseString(response.xml).toprettyxml(), 261 | XmlLexer(), TerminalFormatter())) 262 | else: 263 | logger.output(response.output) 264 | sys.exit(0) 265 | 266 | # Start cli 267 | 268 | # load history 269 | histfile = os.path.join(os.path.expanduser("~"), ".sslclient_history") 270 | try: 271 | readline.read_history_file(histfile) 272 | readline.set_history_length(1000) 273 | except FileNotFoundError: 274 | pass 275 | 276 | def save_history(histfile): 277 | try: 278 | readline.write_history_file(histfile) 279 | except: 280 | logging.warning("Can't write history") 281 | 282 | atexit.register(save_history, histfile) 283 | 284 | # load auto-complete 285 | readline.parse_and_bind('tab: complete') 286 | readline.set_completer_delims('') 287 | readline.set_completer(make_completer()) 288 | 289 | while True: 290 | try: 291 | cmd = input("> ") 292 | logger.command(cmd) 293 | except EOFError: 294 | break 295 | 296 | # skip comments 297 | if cmd.startswith('#'): 298 | continue 299 | 300 | try: 301 | response = client.send_command(cmd) 302 | except ServerError as exception: 303 | # do not log error on QUIT 304 | if "quit".startswith(cmd.lower()) \ 305 | and str(exception) == "Server disconnected": 306 | sys.exit(0) 307 | logging.error(str(exception)) 308 | sys.exit(1) 309 | except Exception as exception: 310 | logging.error(str(exception)) 311 | sys.exit(1) 312 | 313 | if response.ret == client.SRV_RET_DOWNLOAD: 314 | filename = input("File to save: ") 315 | try: 316 | client.download(filename) 317 | logging.info("File downloaded") 318 | except Exception as exception: 319 | logging.error(str(exception)) 320 | elif response.ret == client.SRV_RET_UPLOAD: 321 | filename = input("File to upload: ") 322 | try: 323 | client.upload(filename) 324 | logging.info("File uploaded") 325 | except Exception as exception: 326 | logging.error(str(exception)) 327 | else: 328 | if outputformat == 'xml': 329 | logger.output(highlight(defusedxml.minidom.parseString(response.xml).toprettyxml(), 330 | XmlLexer(), TerminalFormatter())) 331 | else: 332 | logger.output(response.output) 333 | 334 | # use correct input function with python2 335 | try: 336 | input = raw_input 337 | except NameError: 338 | pass 339 | 340 | if __name__ == "__main__": 341 | # execute only if run as a script 342 | main() 343 | -------------------------------------------------------------------------------- /stormshield/sns/cmd.complete: -------------------------------------------------------------------------------- 1 | auth 2 | cache 3 | cancel 4 | certificate 5 | chpwd 6 | config 7 | config.activate 8 | config.antispam 9 | config.antispam.activate 10 | config.antispam.blacklist 11 | config.antispam.blacklist.add 12 | config.antispam.blacklist.list 13 | config.antispam.blacklist.remove 14 | config.antispam.dnsbl 15 | config.antispam.dnsbl.add 16 | config.antispam.dnsbl.edit 17 | config.antispam.dnsbl.list 18 | config.antispam.dnsbl.remove 19 | config.antispam.dnsbl.set 20 | config.antispam.dnsbl.show 21 | config.antispam.set 22 | config.antispam.show 23 | config.antispam.vr 24 | config.antispam.vr.set 25 | config.antispam.vr.show 26 | config.antispam.whitelist 27 | config.antispam.whitelist.add 28 | config.antispam.whitelist.list 29 | config.antispam.whitelist.remove 30 | config.antivirus 31 | config.antivirus.activate 32 | config.antivirus.cleanup 33 | config.antivirus.erasekav 34 | config.antivirus.licence 35 | config.antivirus.list 36 | config.antivirus.objects 37 | config.antivirus.select 38 | config.antivirus.services 39 | config.antivirus.services.ftp 40 | config.antivirus.services.pop3 41 | config.antivirus.services.show 42 | config.antivirus.services.smtp 43 | config.antivirus.show 44 | config.auth 45 | config.auth.activate 46 | config.auth.advanced 47 | config.auth.agent 48 | config.auth.agentignore 49 | config.auth.agentignore.add 50 | config.auth.agentignore.remove 51 | config.auth.agentignore.show 52 | config.auth.default 53 | config.auth.guest 54 | config.auth.https 55 | config.auth.interface 56 | config.auth.interface.advanced 57 | config.auth.interface.enrolment 58 | config.auth.interface.list 59 | config.auth.interface.method 60 | config.auth.interface.password 61 | config.auth.interface.rename 62 | config.auth.interface.show 63 | config.auth.interface.state 64 | config.auth.interface.time 65 | config.auth.interface.timerange 66 | config.auth.kerberos 67 | config.auth.ldap 68 | config.auth.ldap.voucher 69 | config.auth.ldap.voucher.activate 70 | config.auth.ldap.voucher.show 71 | config.auth.ldap.voucher.shrink 72 | config.auth.ldap.voucher.state 73 | config.auth.ldap.voucher.status 74 | config.auth.map 75 | config.auth.map.add 76 | config.auth.map.clear 77 | config.auth.map.list 78 | config.auth.map.remove 79 | config.auth.multiuser 80 | config.auth.multiuser.add 81 | config.auth.multiuser.list 82 | config.auth.multiuser.remove 83 | config.auth.radius 84 | config.auth.show 85 | config.auth.spnego 86 | config.auth.sponsor 87 | config.auth.ssl 88 | config.auth.ssl.caverify 89 | config.auth.ssl.caverify.add 90 | config.auth.ssl.caverify.remove 91 | config.auth.ssl.certidentifier 92 | config.auth.ssl.ldapidentifier 93 | config.auth.ssl.ldaplookup 94 | config.auth.ssl.ldaplookup.add 95 | config.auth.ssl.ldaplookup.list 96 | config.auth.ssl.ldaplookup.method 97 | config.auth.ssl.ldaplookup.move 98 | config.auth.ssl.ldaplookup.remove 99 | config.auth.ssl.ldaplookup.show 100 | config.auth.ssl.update 101 | config.autobackup 102 | config.autobackup.activate 103 | config.autobackup.launch 104 | config.autobackup.restore 105 | config.autobackup.set 106 | config.autobackup.show 107 | config.autoupdate 108 | config.autoupdate.activate 109 | config.autoupdate.list 110 | config.autoupdate.server 111 | config.autoupdate.show 112 | config.autoupdate.state 113 | config.backup 114 | config.bird 115 | config.bird.activate 116 | config.bird.show 117 | config.bird.state 118 | config.bird.update 119 | config.communication 120 | config.communication.activate 121 | config.communication.email 122 | config.communication.email.group 123 | config.communication.email.group.activate 124 | config.communication.email.group.addrecipient 125 | config.communication.email.group.check 126 | config.communication.email.group.create 127 | config.communication.email.group.delrecipient 128 | config.communication.email.group.edit 129 | config.communication.email.group.list 130 | config.communication.email.group.remove 131 | config.communication.email.group.rename 132 | config.communication.email.template 133 | config.communication.email.template.default 134 | config.communication.email.template.download 135 | config.communication.email.template.list 136 | config.communication.email.template.upload 137 | config.communication.httpproxy 138 | config.communication.ipfix 139 | config.communication.ipfix.show 140 | config.communication.ipfix.update 141 | config.communication.show 142 | config.communication.smtp 143 | config.communication.syslog 144 | config.communication.syslog.profile 145 | config.communication.syslog.profile.show 146 | config.communication.syslog.profile.update 147 | config.communication.test 148 | config.communication.test.smtp 149 | config.console 150 | config.console.activate 151 | config.console.gethostkey 152 | config.console.getkey 153 | config.console.remoteadmin 154 | config.console.setpassphrase 155 | config.console.ssh 156 | config.crypto 157 | config.ddnsclient 158 | config.ddnsclient.activate 159 | config.ddnsclient.delete 160 | config.ddnsclient.list 161 | config.ddnsclient.new 162 | config.ddnsclient.resetevent 163 | config.ddnsclient.set 164 | config.ddnsclient.show 165 | config.ddnsclient.unset 166 | config.dhcp 167 | config.dhcp.activate 168 | config.dhcp.host 169 | config.dhcp.host.add 170 | config.dhcp.host.list 171 | config.dhcp.host.remove 172 | config.dhcp.parameters 173 | config.dhcp.parameters.add 174 | config.dhcp.parameters.list 175 | config.dhcp.parameters.remove 176 | config.dhcp.range 177 | config.dhcp.range.add 178 | config.dhcp.range.list 179 | config.dhcp.range.remove 180 | config.dhcp.relay 181 | config.dhcp.relay.advanced 182 | config.dhcp.relay.interface 183 | config.dhcp.relay.interface.add 184 | config.dhcp.relay.interface.all 185 | config.dhcp.relay.interface.list 186 | config.dhcp.relay.interface.remove 187 | config.dhcp.relay.server 188 | config.dhcp.relay.show 189 | config.dhcp.relay.state 190 | config.dhcp.servers 191 | config.dhcp.servers.add 192 | config.dhcp.servers.list 193 | config.dhcp.servers.remove 194 | config.dhcp.show 195 | config.dhcp.state 196 | config.dhcp6 197 | config.dhcp6.activate 198 | config.dhcp6.host 199 | config.dhcp6.host.add 200 | config.dhcp6.host.list 201 | config.dhcp6.host.remove 202 | config.dhcp6.parameters 203 | config.dhcp6.parameters.add 204 | config.dhcp6.parameters.list 205 | config.dhcp6.parameters.remove 206 | config.dhcp6.range 207 | config.dhcp6.range.add 208 | config.dhcp6.range.list 209 | config.dhcp6.range.remove 210 | config.dhcp6.relay 211 | config.dhcp6.relay.fwdinterface 212 | config.dhcp6.relay.fwdinterface.add 213 | config.dhcp6.relay.fwdinterface.list 214 | config.dhcp6.relay.fwdinterface.remove 215 | config.dhcp6.relay.rcvinterface 216 | config.dhcp6.relay.rcvinterface.add 217 | config.dhcp6.relay.rcvinterface.list 218 | config.dhcp6.relay.rcvinterface.remove 219 | config.dhcp6.relay.server 220 | config.dhcp6.relay.show 221 | config.dhcp6.relay.state 222 | config.dhcp6.servers 223 | config.dhcp6.servers.add 224 | config.dhcp6.servers.list 225 | config.dhcp6.servers.remove 226 | config.dhcp6.show 227 | config.dhcp6.state 228 | config.dns 229 | config.dns.activate 230 | config.dns.advanced 231 | config.dns.client 232 | config.dns.client.add 233 | config.dns.client.list 234 | config.dns.client.remove 235 | config.dns.server 236 | config.dns.server.add 237 | config.dns.server.list 238 | config.dns.server.remove 239 | config.dns.show 240 | config.dns.state 241 | config.dns.useroot 242 | config.download 243 | config.filter 244 | config.filter.activate 245 | config.filter.check 246 | config.filter.default 247 | config.filter.explicit 248 | config.filter.export 249 | config.filter.implicit 250 | config.filter.manage 251 | config.filter.rule 252 | config.filter.rule.addsep 253 | config.filter.rule.collapse 254 | config.filter.rule.copy 255 | config.filter.rule.insert 256 | config.filter.rule.move 257 | config.filter.rule.remove 258 | config.filter.rule.update 259 | config.filter.show 260 | config.fwadmin 261 | config.fwadmin.activate 262 | config.fwadmin.update 263 | config.global 264 | config.global.object 265 | config.global.object.export 266 | config.global.object.fqdn 267 | config.global.object.fqdn.check 268 | config.global.object.fqdn.delete 269 | config.global.object.fqdn.new 270 | config.global.object.fqdn.show 271 | config.global.object.geogroup 272 | config.global.object.geogroup.addto 273 | config.global.object.geogroup.check 274 | config.global.object.geogroup.delete 275 | config.global.object.geogroup.new 276 | config.global.object.geogroup.removefrom 277 | config.global.object.geogroup.show 278 | config.global.object.get 279 | config.global.object.group 280 | config.global.object.group.addto 281 | config.global.object.group.check 282 | config.global.object.group.delete 283 | config.global.object.group.new 284 | config.global.object.group.removefrom 285 | config.global.object.group.show 286 | config.global.object.host 287 | config.global.object.host.check 288 | config.global.object.host.delete 289 | config.global.object.host.new 290 | config.global.object.import 291 | config.global.object.import.activate 292 | config.global.object.import.cancel 293 | config.global.object.import.status 294 | config.global.object.import.upload 295 | config.global.object.iprepgroup 296 | config.global.object.iprepgroup.addto 297 | config.global.object.iprepgroup.check 298 | config.global.object.iprepgroup.delete 299 | config.global.object.iprepgroup.new 300 | config.global.object.iprepgroup.removefrom 301 | config.global.object.iprepgroup.show 302 | config.global.object.network 303 | config.global.object.network.check 304 | config.global.object.network.delete 305 | config.global.object.network.new 306 | config.global.object.protocol 307 | config.global.object.protocol.check 308 | config.global.object.protocol.delete 309 | config.global.object.protocol.new 310 | config.global.object.rename 311 | config.global.object.router 312 | config.global.object.router.check 313 | config.global.object.router.delete 314 | config.global.object.router.gateway 315 | config.global.object.router.gateway.add 316 | config.global.object.router.gateway.move 317 | config.global.object.router.gateway.remove 318 | config.global.object.router.gateway.update 319 | config.global.object.router.new 320 | config.global.object.router.show 321 | config.global.object.service 322 | config.global.object.service.check 323 | config.global.object.service.delete 324 | config.global.object.service.new 325 | config.global.object.servicegroup 326 | config.global.object.servicegroup.addto 327 | config.global.object.servicegroup.check 328 | config.global.object.servicegroup.delete 329 | config.global.object.servicegroup.new 330 | config.global.object.servicegroup.removefrom 331 | config.global.object.servicegroup.show 332 | config.global.object.time 333 | config.global.object.time.check 334 | config.global.object.time.delete 335 | config.global.object.time.new 336 | config.ha 337 | config.ha.activate 338 | config.ha.create 339 | config.ha.join 340 | config.ha.show 341 | config.ha.state 342 | config.ha.update 343 | config.ha.weight 344 | config.ha.weight.activate 345 | config.ha.weight.show 346 | config.ha.weight.update 347 | config.hostrep 348 | config.hostrep.activate 349 | config.hostrep.hostlist 350 | config.hostrep.hostlist.add 351 | config.hostrep.hostlist.clear 352 | config.hostrep.hostlist.remove 353 | config.hostrep.hostlist.show 354 | config.hostrep.show 355 | config.hostrep.update 356 | config.ipsec 357 | config.ipsec.activate 358 | config.ipsec.ca 359 | config.ipsec.ca.add 360 | config.ipsec.ca.list 361 | config.ipsec.ca.remove 362 | config.ipsec.check 363 | config.ipsec.ldaplookup 364 | config.ipsec.ldaplookup.list 365 | config.ipsec.ldaplookup.method 366 | config.ipsec.ldaplookup.show 367 | config.ipsec.ldaplookup.update 368 | config.ipsec.peer 369 | config.ipsec.peer.check 370 | config.ipsec.peer.list 371 | config.ipsec.peer.new 372 | config.ipsec.peer.remove 373 | config.ipsec.peer.rename 374 | config.ipsec.peer.show 375 | config.ipsec.peer.update 376 | config.ipsec.policy 377 | config.ipsec.policy.gateway 378 | config.ipsec.policy.gateway.add 379 | config.ipsec.policy.gateway.addsep 380 | config.ipsec.policy.gateway.collapse 381 | config.ipsec.policy.gateway.list 382 | config.ipsec.policy.gateway.move 383 | config.ipsec.policy.gateway.remove 384 | config.ipsec.policy.gateway.update 385 | config.ipsec.policy.mobile 386 | config.ipsec.policy.mobile.add 387 | config.ipsec.policy.mobile.addsep 388 | config.ipsec.policy.mobile.collapse 389 | config.ipsec.policy.mobile.getpeer 390 | config.ipsec.policy.mobile.list 391 | config.ipsec.policy.mobile.move 392 | config.ipsec.policy.mobile.remove 393 | config.ipsec.policy.mobile.setpeer 394 | config.ipsec.policy.mobile.update 395 | config.ipsec.profile 396 | config.ipsec.profile.phase1 397 | config.ipsec.profile.phase1.addprop 398 | config.ipsec.profile.phase1.check 399 | config.ipsec.profile.phase1.getdefault 400 | config.ipsec.profile.phase1.list 401 | config.ipsec.profile.phase1.moveprop 402 | config.ipsec.profile.phase1.new 403 | config.ipsec.profile.phase1.remove 404 | config.ipsec.profile.phase1.removeprop 405 | config.ipsec.profile.phase1.setdefault 406 | config.ipsec.profile.phase1.show 407 | config.ipsec.profile.phase1.update 408 | config.ipsec.profile.phase2 409 | config.ipsec.profile.phase2.check 410 | config.ipsec.profile.phase2.getdefault 411 | config.ipsec.profile.phase2.list 412 | config.ipsec.profile.phase2.new 413 | config.ipsec.profile.phase2.remove 414 | config.ipsec.profile.phase2.setdefault 415 | config.ipsec.profile.phase2.show 416 | config.ipsec.profile.phase2.update 417 | config.ipsec.property 418 | config.ipsec.psk 419 | config.ipsec.psk.add 420 | config.ipsec.psk.list 421 | config.ipsec.psk.remove 422 | config.ipsec.show 423 | config.ipsec.update 424 | config.key 425 | config.key.add 426 | config.key.list 427 | config.key.remove 428 | config.ldap 429 | config.ldap.activate 430 | config.ldap.check 431 | config.ldap.count 432 | config.ldap.default 433 | config.ldap.delmap 434 | config.ldap.external 435 | config.ldap.initialize 436 | config.ldap.list 437 | config.ldap.password 438 | config.ldap.public 439 | config.ldap.remove 440 | config.ldap.rename 441 | config.ldap.setmap 442 | config.ldap.show 443 | config.ldap.state 444 | config.ldap.update 445 | config.ldap.usage 446 | config.log 447 | config.log.activate 448 | config.log.alarm 449 | config.log.auth 450 | config.log.communication 451 | config.log.communication.email 452 | config.log.communication.snmp 453 | config.log.connection 454 | config.log.filter 455 | config.log.ftp 456 | config.log.monitor 457 | config.log.plugin 458 | config.log.pop3 459 | config.log.pvm 460 | config.log.sandboxing 461 | config.log.server 462 | config.log.show 463 | config.log.smtp 464 | config.log.ssl 465 | config.log.stat 466 | config.log.system 467 | config.log.vpn 468 | config.log.web 469 | config.log.xvpn 470 | config.mailfiltering 471 | config.mailfiltering.activate 472 | config.mailfiltering.copy 473 | config.mailfiltering.default 474 | config.mailfiltering.list 475 | config.mailfiltering.rule 476 | config.mailfiltering.rule.insert 477 | config.mailfiltering.rule.move 478 | config.mailfiltering.rule.remove 479 | config.mailfiltering.rule.show 480 | config.mailfiltering.rule.update 481 | config.mailfiltering.update 482 | config.modem 483 | config.modem.activate 484 | config.modem.show 485 | config.modem.update 486 | config.network 487 | config.network.activate 488 | config.network.defaultroute 489 | config.network.defaultroute.activate 490 | config.network.defaultroute.set 491 | config.network.defaultroute.show 492 | config.network.gateway 493 | config.network.gateway.activate 494 | config.network.gateway.add 495 | config.network.gateway.ipv6 496 | config.network.gateway.ipv6.add 497 | config.network.gateway.ipv6.remove 498 | config.network.gateway.ipv6.set 499 | config.network.gateway.ipv6.show 500 | config.network.gateway.ipv6.update 501 | config.network.gateway.remove 502 | config.network.gateway.set 503 | config.network.gateway.show 504 | config.network.gateway.update 505 | config.network.interface 506 | config.network.interface.activate 507 | config.network.interface.address 508 | config.network.interface.address.add 509 | config.network.interface.address.remove 510 | config.network.interface.address.update 511 | config.network.interface.aggregate 512 | config.network.interface.capabilities 513 | config.network.interface.check 514 | config.network.interface.create 515 | config.network.interface.ipsec 516 | config.network.interface.ipv6 517 | config.network.interface.ipv6.address 518 | config.network.interface.ipv6.address.add 519 | config.network.interface.ipv6.address.remove 520 | config.network.interface.ipv6.address.update 521 | config.network.interface.ipv6.routeradv 522 | config.network.interface.ipv6.routeradv.config 523 | config.network.interface.ipv6.routeradv.prefix 524 | config.network.interface.ipv6.routeradv.prefix.add 525 | config.network.interface.ipv6.routeradv.prefix.remove 526 | config.network.interface.ipv6.routeradv.prefix.update 527 | config.network.interface.limit 528 | config.network.interface.limit.show 529 | config.network.interface.msti 530 | config.network.interface.msti.add 531 | config.network.interface.msti.remove 532 | config.network.interface.remove 533 | config.network.interface.rename 534 | config.network.interface.show 535 | config.network.interface.update 536 | config.network.ipv6 537 | config.network.ipv6.state 538 | config.network.route 539 | config.network.route.activate 540 | config.network.route.add 541 | config.network.route.ipv6 542 | config.network.route.ipv6.add 543 | config.network.route.ipv6.remove 544 | config.network.route.ipv6.reverse 545 | config.network.route.ipv6.reverse.add 546 | config.network.route.ipv6.reverse.remove 547 | config.network.route.ipv6.reverse.show 548 | config.network.route.ipv6.reverse.update 549 | config.network.route.ipv6.show 550 | config.network.route.ipv6.update 551 | config.network.route.remove 552 | config.network.route.reverse 553 | config.network.route.reverse.add 554 | config.network.route.reverse.remove 555 | config.network.route.reverse.show 556 | config.network.route.reverse.update 557 | config.network.route.show 558 | config.network.route.update 559 | config.network.switch 560 | config.network.switch.activate 561 | config.network.switch.add 562 | config.network.switch.modify 563 | config.network.switch.show 564 | config.ntp 565 | config.ntp.activate 566 | config.ntp.advanced 567 | config.ntp.key 568 | config.ntp.key.add 569 | config.ntp.key.list 570 | config.ntp.key.remove 571 | config.ntp.server 572 | config.ntp.server.add 573 | config.ntp.server.list 574 | config.ntp.server.remove 575 | config.ntp.show 576 | config.ntp.state 577 | config.object 578 | config.object.activate 579 | config.object.cncategorygroup 580 | config.object.cncategorygroup.addto 581 | config.object.cncategorygroup.check 582 | config.object.cncategorygroup.delete 583 | config.object.cncategorygroup.new 584 | config.object.cncategorygroup.removefrom 585 | config.object.cncategorygroup.show 586 | config.object.export 587 | config.object.fqdn 588 | config.object.fqdn.check 589 | config.object.fqdn.delete 590 | config.object.fqdn.new 591 | config.object.fqdn.show 592 | config.object.geogroup 593 | config.object.geogroup.addto 594 | config.object.geogroup.check 595 | config.object.geogroup.delete 596 | config.object.geogroup.new 597 | config.object.geogroup.removefrom 598 | config.object.geogroup.show 599 | config.object.get 600 | config.object.group 601 | config.object.group.addto 602 | config.object.group.check 603 | config.object.group.delete 604 | config.object.group.new 605 | config.object.group.removefrom 606 | config.object.group.show 607 | config.object.host 608 | config.object.host.check 609 | config.object.host.delete 610 | config.object.host.new 611 | config.object.import 612 | config.object.import.activate 613 | config.object.import.cancel 614 | config.object.import.status 615 | config.object.import.upload 616 | config.object.internet 617 | config.object.internet.show 618 | config.object.internet.update 619 | config.object.iprepgroup 620 | config.object.iprepgroup.addto 621 | config.object.iprepgroup.check 622 | config.object.iprepgroup.delete 623 | config.object.iprepgroup.new 624 | config.object.iprepgroup.removefrom 625 | config.object.iprepgroup.show 626 | config.object.list 627 | config.object.network 628 | config.object.network.check 629 | config.object.network.delete 630 | config.object.network.new 631 | config.object.protocol 632 | config.object.protocol.check 633 | config.object.protocol.delete 634 | config.object.protocol.new 635 | config.object.qos 636 | config.object.qos.activate 637 | config.object.qos.drop 638 | config.object.qos.interface.set 639 | config.object.qos.interface.show 640 | config.object.qos.interface.remove 641 | config.object.qos.qid 642 | config.object.qos.qid.add 643 | config.object.qos.qid.check 644 | config.object.qos.qid.list 645 | config.object.qos.qid.remove 646 | config.object.qos.qid.rename 647 | config.object.qos.set 648 | config.object.qos.tbr.set 649 | config.object.qos.tbr.show 650 | config.object.qos.tbr.remove 651 | config.object.qos.show 652 | config.object.rename 653 | config.object.router 654 | config.object.router.check 655 | config.object.router.delete 656 | config.object.router.gateway 657 | config.object.router.gateway.add 658 | config.object.router.gateway.move 659 | config.object.router.gateway.remove 660 | config.object.router.gateway.update 661 | config.object.router.new 662 | config.object.router.show 663 | config.object.service 664 | config.object.service.check 665 | config.object.service.delete 666 | config.object.service.new 667 | config.object.servicegroup 668 | config.object.servicegroup.addto 669 | config.object.servicegroup.check 670 | config.object.servicegroup.delete 671 | config.object.servicegroup.new 672 | config.object.servicegroup.removefrom 673 | config.object.servicegroup.show 674 | config.object.time 675 | config.object.time.check 676 | config.object.time.delete 677 | config.object.time.new 678 | config.object.urlcategorygroup 679 | config.object.urlcategorygroup.addto 680 | config.object.urlcategorygroup.check 681 | config.object.urlcategorygroup.delete 682 | config.object.urlcategorygroup.new 683 | config.object.urlcategorygroup.removefrom 684 | config.object.urlcategorygroup.show 685 | config.object.urlgroup 686 | config.object.urlgroup.addto 687 | config.object.urlgroup.check 688 | config.object.urlgroup.classify 689 | config.object.urlgroup.delete 690 | config.object.urlgroup.new 691 | config.object.urlgroup.removefrom 692 | config.object.urlgroup.setbase 693 | config.object.urlgroup.show 694 | config.openvpn 695 | config.openvpn.activate 696 | config.openvpn.default 697 | config.openvpn.download 698 | config.openvpn.list 699 | config.openvpn.show 700 | config.openvpn.update 701 | config.passwdpolicy 702 | config.passwdpolicy.activate 703 | config.passwdpolicy.set 704 | config.passwdpolicy.show 705 | config.pptp 706 | config.pptp.activate 707 | config.pptp.advanced 708 | config.pptp.method 709 | config.pptp.pool 710 | config.pptp.show 711 | config.pptp.state 712 | config.pptp.user 713 | config.pptp.user.activate 714 | config.pptp.user.add 715 | config.pptp.user.list 716 | config.pptp.user.remove 717 | config.protocol 718 | config.protocol.activate 719 | config.protocol.common 720 | config.protocol.common.config 721 | config.protocol.common.default 722 | config.protocol.common.ips 723 | config.protocol.common.ips.config 724 | config.protocol.common.show 725 | config.protocol.dcerpc_tcp.profile.ips.uuid 726 | config.protocol.dcerpc_tcp.profile.ips.uuid.insert 727 | config.protocol.dcerpc_tcp.profile.ips.uuid.remove 728 | config.protocol.dcerpc_tcp.profile.ips.uuid.show 729 | config.protocol.dcerpc_tcp.profile.ips.uuid.update 730 | config.protocol.enip_tcp.profile.ips.cipservice 731 | config.protocol.enip_tcp.profile.ips.cipservice.insert 732 | config.protocol.enip_tcp.profile.ips.cipservice.remove 733 | config.protocol.enip_tcp.profile.ips.cipservice.show 734 | config.protocol.enip_tcp.profile.ips.cipservice.update 735 | config.protocol.enip_udp.profile.ips.cipservice 736 | config.protocol.enip_udp.profile.ips.cipservice.insert 737 | config.protocol.enip_udp.profile.ips.cipservice.remove 738 | config.protocol.enip_udp.profile.ips.cipservice.show 739 | config.protocol.enip_udp.profile.ips.cipservice.update 740 | config.protocol.ftp.common.proxy 741 | config.protocol.ftp.common.proxy.config 742 | config.protocol.ftp.profile.proxy 743 | config.protocol.ftp.profile.proxy.antivirus 744 | config.protocol.ftp.profile.proxy.cmd 745 | config.protocol.ftp.profile.proxy.config 746 | config.protocol.ftp.profile.proxy.extracmd 747 | config.protocol.ftp.profile.proxy.extracmd.add 748 | config.protocol.ftp.profile.proxy.extracmd.list 749 | config.protocol.ftp.profile.proxy.extracmd.remove 750 | config.protocol.ftp.profile.proxy.postproc 751 | config.protocol.ftp.profile.proxy.sandboxing 752 | config.protocol.ftp.profile.proxy.sandboxing.policy 753 | config.protocol.ftp.profile.proxy.sandboxing.type 754 | config.protocol.ftp.profile.proxy.sandboxing.type.show 755 | config.protocol.ftp.profile.proxy.sandboxing.type.update 756 | config.protocol.http.common.proxy 757 | config.protocol.http.common.proxy.config 758 | config.protocol.http.profile.proxy 759 | config.protocol.http.profile.proxy.antivirus 760 | config.protocol.http.profile.proxy.config 761 | config.protocol.http.profile.proxy.icapexclude 762 | config.protocol.http.profile.proxy.icapexclude.add 763 | config.protocol.http.profile.proxy.icapexclude.list 764 | config.protocol.http.profile.proxy.icapexclude.remove 765 | config.protocol.http.profile.proxy.icapreqmod 766 | config.protocol.http.profile.proxy.icaprespmod 767 | config.protocol.http.profile.proxy.mime 768 | config.protocol.http.profile.proxy.mime.insert 769 | config.protocol.http.profile.proxy.mime.move 770 | config.protocol.http.profile.proxy.mime.remove 771 | config.protocol.http.profile.proxy.mime.show 772 | config.protocol.http.profile.proxy.mime.update 773 | config.protocol.http.profile.proxy.postproc 774 | config.protocol.http.profile.proxy.sandboxing 775 | config.protocol.http.profile.proxy.sandboxing.policy 776 | config.protocol.http.profile.proxy.sandboxing.type 777 | config.protocol.http.profile.proxy.sandboxing.type.show 778 | config.protocol.http.profile.proxy.sandboxing.type.update 779 | config.protocol.http.profile.proxy.urlfiltering 780 | config.protocol.iec104.profile.ips.cause 781 | config.protocol.iec104.profile.ips.cause.config 782 | config.protocol.iec104.profile.ips.cause.list 783 | config.protocol.ip.common.inputloadbalance 784 | config.protocol.ip.common.ips.fragment 785 | config.protocol.list 786 | config.protocol.modbus.profile.ips.memoryaccess 787 | config.protocol.modbus.profile.ips.memoryaccess.config 788 | config.protocol.nb-cifs_tcp.profile.ips.uuid 789 | config.protocol.nb-cifs_tcp.profile.ips.uuid.insert 790 | config.protocol.nb-cifs_tcp.profile.ips.uuid.remove 791 | config.protocol.nb-cifs_tcp.profile.ips.uuid.show 792 | config.protocol.nb-cifs_tcp.profile.ips.uuid.update 793 | config.protocol.nb-epmap_tcp.profile.ips.uuid 794 | config.protocol.nb-epmap_tcp.profile.ips.uuid.insert 795 | config.protocol.nb-epmap_tcp.profile.ips.uuid.remove 796 | config.protocol.nb-epmap_tcp.profile.ips.uuid.show 797 | config.protocol.nb-epmap_tcp.profile.ips.uuid.update 798 | config.protocol.nb-ssn.profile.ips.uuid 799 | config.protocol.nb-ssn.profile.ips.uuid.insert 800 | config.protocol.nb-ssn.profile.ips.uuid.remove 801 | config.protocol.nb-ssn.profile.ips.uuid.show 802 | config.protocol.nb-ssn.profile.ips.uuid.update 803 | config.protocol.oracle-tns.profile.ips.hosts 804 | config.protocol.oracle-tns.profile.ips.hosts.insert 805 | config.protocol.oracle-tns.profile.ips.hosts.remove 806 | config.protocol.oracle-tns.profile.ips.hosts.show 807 | config.protocol.pop3.common.proxy 808 | config.protocol.pop3.common.proxy.config 809 | config.protocol.pop3.profile.proxy 810 | config.protocol.pop3.profile.proxy.antivirus 811 | config.protocol.pop3.profile.proxy.cmd 812 | config.protocol.pop3.profile.proxy.config 813 | config.protocol.pop3.profile.proxy.extracmd 814 | config.protocol.pop3.profile.proxy.extracmd.add 815 | config.protocol.pop3.profile.proxy.extracmd.list 816 | config.protocol.pop3.profile.proxy.extracmd.remove 817 | config.protocol.pop3.profile.proxy.postproc 818 | config.protocol.pop3.profile.proxy.sandboxing 819 | config.protocol.pop3.profile.proxy.sandboxing.maxsize 820 | config.protocol.pop3.profile.proxy.sandboxing.maxsize.set 821 | config.protocol.pop3.profile.proxy.sandboxing.maxsize.show 822 | config.protocol.pop3.profile.proxy.sandboxing.policy 823 | config.protocol.pop3.profile.proxy.sandboxing.type 824 | config.protocol.pop3.profile.proxy.sandboxing.type.show 825 | config.protocol.pop3.profile.proxy.sandboxing.type.update 826 | config.protocol.profile 827 | config.protocol.profile.alarm 828 | config.protocol.profile.alarm.default 829 | config.protocol.profile.alarm.show 830 | config.protocol.profile.alarm.update 831 | config.protocol.profile.check 832 | config.protocol.profile.copy 833 | config.protocol.profile.default 834 | config.protocol.profile.ips 835 | config.protocol.profile.ips.config 836 | config.protocol.profile.list 837 | config.protocol.profile.show 838 | config.protocol.profile.update 839 | config.protocol.show 840 | config.protocol.smtp.common.proxy 841 | config.protocol.smtp.common.proxy.config 842 | config.protocol.smtp.profile.proxy 843 | config.protocol.smtp.profile.proxy.antivirus 844 | config.protocol.smtp.profile.proxy.cmd 845 | config.protocol.smtp.profile.proxy.config 846 | config.protocol.smtp.profile.proxy.extracmd 847 | config.protocol.smtp.profile.proxy.extracmd.add 848 | config.protocol.smtp.profile.proxy.extracmd.list 849 | config.protocol.smtp.profile.proxy.extracmd.remove 850 | config.protocol.smtp.profile.proxy.postproc 851 | config.protocol.smtp.profile.proxy.sandboxing 852 | config.protocol.smtp.profile.proxy.sandboxing.maxsize 853 | config.protocol.smtp.profile.proxy.sandboxing.maxsize.set 854 | config.protocol.smtp.profile.proxy.sandboxing.maxsize.show 855 | config.protocol.smtp.profile.proxy.sandboxing.policy 856 | config.protocol.smtp.profile.proxy.sandboxing.type 857 | config.protocol.smtp.profile.proxy.sandboxing.type.show 858 | config.protocol.smtp.profile.proxy.sandboxing.type.update 859 | config.protocol.ssl.common.proxy 860 | config.protocol.ssl.common.proxy.ca 861 | config.protocol.ssl.common.proxy.ca.custom 862 | config.protocol.ssl.common.proxy.ca.custom.add 863 | config.protocol.ssl.common.proxy.ca.custom.list 864 | config.protocol.ssl.common.proxy.ca.custom.remove 865 | config.protocol.ssl.common.proxy.ca.trusted 866 | config.protocol.ssl.common.proxy.ca.trusted.disable 867 | config.protocol.ssl.common.proxy.ca.trusted.enable 868 | config.protocol.ssl.common.proxy.ca.trusted.list 869 | config.protocol.ssl.common.proxy.cert 870 | config.protocol.ssl.common.proxy.cert.trusted 871 | config.protocol.ssl.common.proxy.cert.trusted.add 872 | config.protocol.ssl.common.proxy.cert.trusted.list 873 | config.protocol.ssl.common.proxy.cert.trusted.remove 874 | config.protocol.ssl.common.proxy.config 875 | config.protocol.ssl.common.proxy.sslprotocol 876 | config.protocol.ssl.profile.proxy 877 | config.protocol.ssl.profile.proxy.config 878 | config.protocol.ssl.profile.proxy.sslfiltering 879 | config.protocol.tcpudp.common.ips.connection 880 | config.protocol.tcpudp.profile.ips.connection 881 | config.protocol.tcpudp.profile.ips.synproxy 882 | config.pvm 883 | config.pvm.activate 884 | config.pvm.data 885 | config.pvm.data.family 886 | config.pvm.data.severity 887 | config.pvm.data.vuln 888 | config.pvm.email 889 | config.pvm.hostlist 890 | config.pvm.hostlist.add 891 | config.pvm.hostlist.clear 892 | config.pvm.hostlist.remove 893 | config.pvm.hostlist.show 894 | config.pvm.profile 895 | config.pvm.profile.clear 896 | config.pvm.profile.create 897 | config.pvm.profile.line 898 | config.pvm.profile.line.add 899 | config.pvm.profile.line.remove 900 | config.pvm.profile.line.update 901 | config.pvm.profile.list 902 | config.pvm.profile.remove 903 | config.pvm.profile.show 904 | config.pvm.profile.update 905 | config.pvm.profile.vuln 906 | config.pvm.profile.vuln.add 907 | config.pvm.profile.vuln.clear 908 | config.pvm.profile.vuln.remove 909 | config.pvm.profile.vuln.show 910 | config.pvm.show 911 | config.pvm.state 912 | config.pvm.timeout 913 | config.raid 914 | config.raid.create 915 | config.raid.hotspare 916 | config.raid.rebuild 917 | config.report 918 | config.report.activate 919 | config.report.show 920 | config.report.state 921 | config.report.update 922 | config.restore 923 | config.sandboxing 924 | config.sandboxing.activate 925 | config.sandboxing.set 926 | config.sandboxing.show 927 | config.secure 928 | config.secure.add 929 | config.secure.backup 930 | config.secure.initialize 931 | config.secure.list 932 | config.secure.load 933 | config.secure.remove 934 | config.secure.restore 935 | config.secure.show 936 | config.secure.state 937 | config.secure.sync 938 | config.secure.usbconf 939 | config.securityinspection 940 | config.securityinspection.activate 941 | config.securityinspection.common 942 | config.securityinspection.common.addresslist 943 | config.securityinspection.common.addresslist.add 944 | config.securityinspection.common.addresslist.remove 945 | config.securityinspection.common.addresslist.show 946 | config.securityinspection.common.alarm 947 | config.securityinspection.common.alarm.list 948 | config.securityinspection.common.alarm.new 949 | config.securityinspection.common.alarm.new.config 950 | config.securityinspection.common.alarm.new.list 951 | config.securityinspection.common.alarm.new.remove 952 | config.securityinspection.common.init 953 | config.securityinspection.common.probe 954 | config.securityinspection.common.probe.add 955 | config.securityinspection.common.probe.modify 956 | config.securityinspection.common.probe.remove 957 | config.securityinspection.common.probe.show 958 | config.securityinspection.common.show 959 | config.securityinspection.common.stateful 960 | config.securityinspection.config 961 | config.securityinspection.config.alarm 962 | config.securityinspection.config.alarm.list 963 | config.securityinspection.config.alarm.template 964 | config.securityinspection.config.copy 965 | config.securityinspection.config.default 966 | config.securityinspection.config.list 967 | config.securityinspection.config.protocol 968 | config.securityinspection.config.show 969 | config.securityinspection.config.update 970 | config.slot 971 | config.slot.activate 972 | config.slot.copy 973 | config.slot.default 974 | config.slot.download 975 | config.slot.list 976 | config.slot.remove 977 | config.slot.state 978 | config.slot.update 979 | config.slot.upload 980 | config.smcrouting 981 | config.smcrouting.activate 982 | config.smcrouting.default 983 | config.smcrouting.route 984 | config.smcrouting.route.add 985 | config.smcrouting.route.ipv6 986 | config.smcrouting.route.ipv6.add 987 | config.smcrouting.route.ipv6.move 988 | config.smcrouting.route.ipv6.remove 989 | config.smcrouting.route.ipv6.show 990 | config.smcrouting.route.ipv6.update 991 | config.smcrouting.route.move 992 | config.smcrouting.route.remove 993 | config.smcrouting.route.show 994 | config.smcrouting.route.update 995 | config.smcrouting.show 996 | config.smcrouting.update 997 | config.snmp 998 | config.snmp.access 999 | config.snmp.access.community 1000 | config.snmp.access.userv3 1001 | config.snmp.activate 1002 | config.snmp.show 1003 | config.snmp.state 1004 | config.snmp.system 1005 | config.snmp.trap 1006 | config.snmp.trap.auth 1007 | config.snmp.trap.v1 1008 | config.snmp.trap.v1.add 1009 | config.snmp.trap.v1.modify 1010 | config.snmp.trap.v1.remove 1011 | config.snmp.trap.v1.show 1012 | config.snmp.trap.v2 1013 | config.snmp.trap.v2.add 1014 | config.snmp.trap.v2.modify 1015 | config.snmp.trap.v2.remove 1016 | config.snmp.trap.v2.show 1017 | config.snmp.trap.v3 1018 | config.snmp.trap.v3.add 1019 | config.snmp.trap.v3.modify 1020 | config.snmp.trap.v3.remove 1021 | config.snmp.trap.v3.show 1022 | config.snmp.version 1023 | config.sslfiltering 1024 | config.sslfiltering.activate 1025 | config.sslfiltering.copy 1026 | config.sslfiltering.default 1027 | config.sslfiltering.list 1028 | config.sslfiltering.rule 1029 | config.sslfiltering.rule.insert 1030 | config.sslfiltering.rule.move 1031 | config.sslfiltering.rule.remove 1032 | config.sslfiltering.rule.show 1033 | config.sslfiltering.rule.update 1034 | config.sslfiltering.update 1035 | config.status 1036 | config.status.check 1037 | config.status.remove 1038 | config.status.show 1039 | config.status.validate 1040 | config.sysevent 1041 | config.sysevent.activate 1042 | config.sysevent.default 1043 | config.sysevent.modify 1044 | config.sysevent.show 1045 | config.upload 1046 | config.urlfiltering 1047 | config.urlfiltering.activate 1048 | config.urlfiltering.blockpage 1049 | config.urlfiltering.blockpage.default 1050 | config.urlfiltering.blockpage.list 1051 | config.urlfiltering.blockpage.update 1052 | config.urlfiltering.copy 1053 | config.urlfiltering.default 1054 | config.urlfiltering.list 1055 | config.urlfiltering.rule 1056 | config.urlfiltering.rule.insert 1057 | config.urlfiltering.rule.move 1058 | config.urlfiltering.rule.remove 1059 | config.urlfiltering.rule.show 1060 | config.urlfiltering.rule.update 1061 | config.urlfiltering.update 1062 | config.webadmin 1063 | config.webadmin.access 1064 | config.webadmin.access.add 1065 | config.webadmin.access.remove 1066 | config.webadmin.access.show 1067 | config.webadmin.access.sslonly 1068 | config.webadmin.activate 1069 | config.webadmin.adminaccount 1070 | config.webadmin.bruteforce 1071 | config.webadmin.bruteforce.nbattempts 1072 | config.webadmin.bruteforce.nbbruteforceentries 1073 | config.webadmin.bruteforce.state 1074 | config.webadmin.bruteforce.time 1075 | config.webadmin.bruteforce.triestime 1076 | config.webadmin.idle 1077 | config.webadmin.port 1078 | config.webadmin.show 1079 | config.webadmin.state 1080 | config.wifi 1081 | config.wifi.activate 1082 | config.wifi.list 1083 | config.wifi.list.channels 1084 | config.wifi.list.countrycodes 1085 | config.wifi.show 1086 | config.wifi.update 1087 | config.xvpn 1088 | config.xvpn.access 1089 | config.xvpn.activate 1090 | config.xvpn.advanced 1091 | config.xvpn.profile 1092 | config.xvpn.profile.activate 1093 | config.xvpn.profile.create 1094 | config.xvpn.profile.list 1095 | config.xvpn.profile.remove 1096 | config.xvpn.profile.show 1097 | config.xvpn.profile.update 1098 | config.xvpn.server 1099 | config.xvpn.server.http 1100 | config.xvpn.server.http.add 1101 | config.xvpn.server.http.alias 1102 | config.xvpn.server.http.alias.add 1103 | config.xvpn.server.http.alias.remove 1104 | config.xvpn.server.http.remove 1105 | config.xvpn.server.http.state 1106 | config.xvpn.server.http.update 1107 | config.xvpn.server.other 1108 | config.xvpn.server.other.add 1109 | config.xvpn.server.other.remove 1110 | config.xvpn.server.other.state 1111 | config.xvpn.server.other.update 1112 | config.xvpn.show 1113 | excluded 1114 | global 1115 | globaladmin 1116 | globaladmin.getinfos 1117 | globaladmin.getstatus 1118 | ha 1119 | ha.checksync 1120 | ha.cluster 1121 | ha.cluster.activate 1122 | ha.cluster.add 1123 | ha.cluster.list 1124 | ha.cluster.remove 1125 | ha.cluster.show 1126 | ha.cluster.update 1127 | ha.halt 1128 | ha.info 1129 | ha.reboot 1130 | ha.remote 1131 | ha.remote.haclusterremove 1132 | ha.remote.hainfo 1133 | ha.setmode 1134 | ha.sync 1135 | help 1136 | id1 1137 | id2 1138 | included 1139 | issuer 1140 | list 1141 | log 1142 | log.clear 1143 | log.datetoline 1144 | log.downlimit 1145 | log.download 1146 | log.info 1147 | log.property 1148 | log.search 1149 | log.search.get 1150 | log.search.jump 1151 | log.search.new 1152 | log.search.next 1153 | log.search.previous 1154 | log.search.resume 1155 | log.search.stop 1156 | modify 1157 | monitor 1158 | monitor.addresslist 1159 | monitor.addresslist.add 1160 | monitor.addresslist.delete 1161 | monitor.addresslist.show 1162 | monitor.agg 1163 | monitor.alarm 1164 | monitor.alarm.get 1165 | monitor.antivirus 1166 | monitor.autobackup 1167 | monitor.autoupdate 1168 | monitor.bypass 1169 | monitor.connection 1170 | monitor.cryptocard 1171 | monitor.dhcp 1172 | monitor.fan 1173 | monitor.filter 1174 | monitor.filtertable 1175 | monitor.filtertable.show 1176 | monitor.flush 1177 | monitor.flush.addresslist 1178 | monitor.flush.hostrep 1179 | monitor.flush.info 1180 | monitor.flush.pvm 1181 | monitor.flush.rulematch 1182 | monitor.flush.sa 1183 | monitor.flush.stat 1184 | monitor.flush.state 1185 | monitor.flush.user 1186 | monitor.fwadmin 1187 | monitor.getsa 1188 | monitor.getspd 1189 | monitor.gprs 1190 | monitor.health 1191 | monitor.host 1192 | monitor.hostrep 1193 | monitor.interface 1194 | monitor.ipgeoloc 1195 | monitor.iprep 1196 | monitor.log 1197 | monitor.openvpn 1198 | monitor.openvpn.list 1199 | monitor.openvpn.remove 1200 | monitor.policy 1201 | monitor.power 1202 | monitor.proxycache 1203 | monitor.pvm 1204 | monitor.pvm.force 1205 | monitor.pvm.force.check 1206 | monitor.pvm.force.list 1207 | monitor.pvm.force.set 1208 | monitor.pvm.host 1209 | monitor.pvm.hostbyos 1210 | monitor.pvm.hostbyproduct 1211 | monitor.pvm.hostbypvmid 1212 | monitor.pvm.hostbyservice 1213 | monitor.pvm.hostdata 1214 | monitor.pvm.info 1215 | monitor.pvm.os 1216 | monitor.pvm.product 1217 | monitor.pvm.service 1218 | monitor.pvm.stat 1219 | monitor.pvm.vuln 1220 | monitor.qos 1221 | monitor.raid 1222 | monitor.revrt 1223 | monitor.route 1224 | monitor.sandboxing 1225 | monitor.sandboxing.misclass 1226 | monitor.services 1227 | monitor.smart 1228 | monitor.stat 1229 | monitor.system 1230 | monitor.thind 1231 | monitor.user 1232 | nop 1233 | pki 1234 | pki.ca 1235 | pki.ca.check 1236 | pki.ca.checkcrl 1237 | pki.ca.checkcrl.add 1238 | pki.ca.checkcrl.remove 1239 | pki.ca.checkcrl.show 1240 | pki.ca.checkcrl.update 1241 | pki.ca.config 1242 | pki.ca.config.crldp 1243 | pki.ca.config.crldp.add 1244 | pki.ca.config.crldp.remove 1245 | pki.ca.config.crldp.show 1246 | pki.ca.config.show 1247 | pki.ca.config.update 1248 | pki.ca.create 1249 | pki.ca.get 1250 | pki.ca.list 1251 | pki.ca.publish 1252 | pki.ca.purge 1253 | pki.ca.rename 1254 | pki.ca.revoke 1255 | pki.ca.show 1256 | pki.certificate 1257 | pki.certificate.check 1258 | pki.certificate.comment 1259 | pki.certificate.create 1260 | pki.certificate.dropkey 1261 | pki.certificate.get 1262 | pki.certificate.list 1263 | pki.certificate.publish 1264 | pki.certificate.rename 1265 | pki.certificate.revoke 1266 | pki.certificate.show 1267 | pki.config 1268 | pki.config.show 1269 | pki.config.update 1270 | pki.crl 1271 | pki.crl.create 1272 | pki.crl.get 1273 | pki.crl.publish 1274 | pki.crl.remove 1275 | pki.crl.show 1276 | pki.import 1277 | pki.request 1278 | pki.request.create 1279 | pki.request.get 1280 | pki.request.list 1281 | pki.request.remove 1282 | pki.request.show 1283 | pki.request.sign 1284 | pki.scep 1285 | pki.scep.check 1286 | pki.scep.query 1287 | pki.search 1288 | quit 1289 | report 1290 | report.get 1291 | report.get.day 1292 | report.get.last30days 1293 | report.get.last7days 1294 | report.get.lasthour 1295 | report.reset 1296 | server 1297 | subject 1298 | system 1299 | system.backup 1300 | system.bypass 1301 | system.bypass.activate 1302 | system.bypass.config 1303 | system.bypass.rearm 1304 | system.clone 1305 | system.date 1306 | system.defaultconfig 1307 | system.fwadmin 1308 | system.halt 1309 | system.ident 1310 | system.information 1311 | system.initialize 1312 | system.language 1313 | system.led 1314 | system.licence 1315 | system.licence.dump 1316 | system.licence.updater 1317 | system.licence.updater.config 1318 | system.licence.updater.diff 1319 | system.licence.updater.get 1320 | system.licence.updater.install 1321 | system.licence.updater.show 1322 | system.licence.upload 1323 | system.logdisk 1324 | system.logdisk.format 1325 | system.logdisk.list 1326 | system.logdisk.select 1327 | system.logdisk.state 1328 | system.nslookup 1329 | system.ping 1330 | system.property 1331 | system.reboot 1332 | system.register 1333 | system.right 1334 | system.right.activate 1335 | system.right.insert 1336 | system.right.list 1337 | system.right.move 1338 | system.right.remove 1339 | system.right.ticket 1340 | system.right.ticket.acquire 1341 | system.right.ticket.activate 1342 | system.right.ticket.delete 1343 | system.right.ticket.list 1344 | system.right.ticket.new 1345 | system.right.ticket.release 1346 | system.right.update 1347 | system.session 1348 | system.setboot 1349 | system.setbranch 1350 | system.status 1351 | system.timezone 1352 | system.timezone.get 1353 | system.timezone.list 1354 | system.timezone.set 1355 | system.traceroute 1356 | system.update 1357 | system.update.activate 1358 | system.update.check 1359 | system.update.load 1360 | system.update.result 1361 | system.update.status 1362 | system.update.upload 1363 | system.watchdog 1364 | user 1365 | user.access 1366 | user.access.activate 1367 | user.access.default 1368 | user.access.default.show 1369 | user.access.default.update 1370 | user.access.insert 1371 | user.access.list 1372 | user.access.move 1373 | user.access.remove 1374 | user.access.right 1375 | user.access.right.insert 1376 | user.access.right.list 1377 | user.access.right.move 1378 | user.access.right.remove 1379 | user.access.right.update 1380 | user.access.update 1381 | user.certificate 1382 | user.check 1383 | user.create 1384 | user.group 1385 | user.group.addto 1386 | user.group.adduser 1387 | user.group.check 1388 | user.group.create 1389 | user.group.deluser 1390 | user.group.description 1391 | user.group.list 1392 | user.group.new 1393 | user.group.remove 1394 | user.group.removefrom 1395 | user.group.show 1396 | user.list 1397 | user.password 1398 | user.remove 1399 | user.request 1400 | user.request.approved 1401 | user.request.format 1402 | user.request.format.set 1403 | user.request.format.show 1404 | user.request.list 1405 | user.request.remove 1406 | user.request.sendmail 1407 | user.request.show 1408 | user.request.update 1409 | user.search 1410 | user.show 1411 | user.update 1412 | user.voucher 1413 | user.voucher.account 1414 | user.voucher.account.create 1415 | user.voucher.account.delete 1416 | user.voucher.account.list 1417 | user.voucher.account.resetpassword 1418 | user.voucher.account.status 1419 | user.voucher.account.update 1420 | version 1421 | -------------------------------------------------------------------------------- /stormshield/sns/configparser.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | """ 4 | stormshield.sns.configparser 5 | 6 | This module handles SNS API responses and extract section/token/values 7 | in ini/section format. 8 | """ 9 | 10 | import sys 11 | import re 12 | from shlex import shlex 13 | from requests.structures import CaseInsensitiveDict 14 | 15 | def unquote(value): 16 | """ remove quotes if needed """ 17 | if isinstance(value, str) and len(value) > 1 and value[0] == '"' and value[-1] == '"': 18 | return value[1:-1] 19 | return value 20 | 21 | def serialize(data): 22 | if type(data) is CaseInsensitiveDict: 23 | res = {} 24 | for (k, v) in data.items(): 25 | res[k] = serialize(v) 26 | return res 27 | elif type(data) is list: 28 | res = [] 29 | for v in data: 30 | res.append(serialize(v)) 31 | return res 32 | else: 33 | return data 34 | 35 | 36 | class ConfigParser: 37 | """ A class to parse section format from SNS API responses """ 38 | 39 | SERVERD_HEAD_RE = re.compile(r'^\d{3} code=.* msg=.* format="(.*?)"') 40 | SERVERD_TAIL_RE = re.compile(r'^\d{3} code=.*? msg=.*?') 41 | SECTION_RE = re.compile(r'^\s*\[\s*(.+?)\s*\]\s*$') 42 | EMPTY_RE = re.compile(r'^\s*$') 43 | TOKEN_VALUE_RE = re.compile(r'^(.*?)=(.*)$') 44 | 45 | def __init__(self, text): 46 | """ load a section from text """ 47 | 48 | self.data = CaseInsensitiveDict() 49 | self.format = None 50 | 51 | lines = text.splitlines() 52 | 53 | # strip serverd headers if needed 54 | match = self.SERVERD_HEAD_RE.match(lines[0]) 55 | if match: 56 | del lines[0] 57 | self.format = match.group(1) 58 | if self.SERVERD_TAIL_RE.match(lines[-1]): 59 | del lines[-1] 60 | 61 | text = "\n".join(lines) 62 | 63 | if self.format == 'raw' or self.format == 'xml': 64 | # plain data, no parsing 65 | self.data = text 66 | return 67 | 68 | section = "Result" # default section 69 | for line in text.splitlines(): 70 | 71 | # comment 72 | if line.startswith('#'): 73 | continue 74 | 75 | # empty lines 76 | if self.EMPTY_RE.match(line): 77 | continue 78 | 79 | # section header 80 | match = self.SECTION_RE.match(line) 81 | if match: 82 | section = match.group(1) 83 | if self.format == 'section': 84 | self.data[section] = CaseInsensitiveDict() 85 | else: 86 | self.data[section] = [] 87 | continue 88 | 89 | if self.format == "list": 90 | self.data[section].append(line) 91 | elif self.format == "section_line": 92 | # fix encoding for python2 93 | if sys.version_info[0] < 3: 94 | line = line.encode('utf-8') 95 | # parse token=value token2=value2 96 | lexer = shlex(line, posix=True) 97 | lexer.wordchars += "=.-*:,/@'()" 98 | lexer.quotes = '"' 99 | parsed = {} 100 | try: 101 | for word in lexer: 102 | # ignore anything else than token=value 103 | if '=' in word: 104 | token, value = word.split("=", 1) 105 | parsed[token] = value 106 | except Exception as e: 107 | print("Can't parse line:\n" + line + "\n") 108 | print(e) 109 | raise(e) 110 | self.data[section].append(parsed) 111 | else: 112 | # section 113 | (token, value) = line.split("=", 1) 114 | self.data[section][token] = unquote(value) 115 | 116 | 117 | def get(self, section, token=None, line=None, default=None): 118 | """ get the value of a token or a plain line from the current section """ 119 | 120 | if section not in self.data: 121 | value = default 122 | 123 | elif token is not None: 124 | # token/value mode 125 | 126 | if token not in self.data[section]: 127 | value = default 128 | else: 129 | value = unquote(self.data[section][token]) 130 | elif line is None: 131 | # return all tokens/lines form section 132 | if self.format == "section": 133 | value = self.data[section] 134 | elif section not in self.data: 135 | value = [] 136 | else: 137 | value = self.data[section] 138 | else: 139 | if line < 1: 140 | value = default 141 | elif section not in self.data: 142 | value = default 143 | elif len(self.data[section]) < line: 144 | value = default 145 | else: 146 | value = self.data[section][line-1] 147 | 148 | return value 149 | 150 | def serialize_data(self): 151 | """ return serializable output parsed data """ 152 | 153 | return serialize(self.data) 154 | -------------------------------------------------------------------------------- /stormshield/sns/crc.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | """ 4 | stormshield.sns.crc 5 | 6 | This module implements SNS crc32 functions. 7 | """ 8 | 9 | 10 | 11 | CRC32_init = 0xffffffff 12 | 13 | CRCTAB = [ 14 | 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 15 | 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, 16 | 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 17 | 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 18 | 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 19 | 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 20 | 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 21 | 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, 22 | 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 23 | 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 24 | 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 25 | 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, 26 | 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 27 | 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, 28 | 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 29 | 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 30 | 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 31 | 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, 32 | 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 33 | 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, 34 | 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 35 | 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 36 | 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 37 | 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, 38 | 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 39 | 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 40 | 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 41 | 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 42 | 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 43 | 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, 44 | 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 45 | 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, 46 | 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 47 | 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 48 | 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 49 | 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 50 | 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 51 | 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, 52 | 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 53 | 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 54 | 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 55 | 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, 56 | 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 57 | 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, 58 | 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 59 | 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 60 | 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 61 | 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, 62 | 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 63 | 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, 64 | 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 65 | 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 66 | 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 67 | 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, 68 | 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 69 | 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 70 | 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 71 | 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 72 | 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 73 | 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, 74 | 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 75 | 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, 76 | 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 77 | 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d, 78 | ] 79 | 80 | 81 | 82 | def compute_crc32(data): 83 | """ Returns the hex string of CRC value of DATA """ 84 | 85 | # python2 convert str to bytes 86 | if type(data) == str: 87 | data = bytearray(data) 88 | 89 | n = len(data) 90 | crc = CRC32_init 91 | 92 | for i in range(0, n): 93 | crc = (crc >> 8) ^ CRCTAB[(crc ^ data[i]) & 0xff] 94 | return crc 95 | 96 | 97 | def update_crc32(data, crc): 98 | """ Incremental CRC, returns updated CRC. """ 99 | 100 | # python2 convert str to bytes 101 | if type(data) == str: 102 | data = bytearray(data) 103 | 104 | n = len(data) 105 | 106 | for i in range(0, n): 107 | crc = (crc >> 8) ^ CRCTAB[(crc ^ data[i]) & 0xff] 108 | 109 | return crc 110 | -------------------------------------------------------------------------------- /stormshield/sns/sslclient/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | stormshield.sns.sslclient 6 | ~~~~~~~~~~~~~~~~~~~~~~~~~ 7 | 8 | This module contains SSLClient class to handle SNS API calls 9 | and Response class to handle API answers. 10 | """ 11 | 12 | from __future__ import unicode_literals 13 | import os 14 | import ipaddress 15 | import base64 16 | import logging 17 | import re 18 | import platform 19 | import defusedxml.ElementTree as ElementTree 20 | from xml.etree import ElementTree as Et 21 | import ssl 22 | import requests 23 | from requests.adapters import HTTPAdapter, DEFAULT_POOLSIZE, DEFAULT_RETRIES, DEFAULT_POOLBLOCK 24 | from urllib3.poolmanager import PoolManager, proxy_from_url 25 | from requests.utils import get_auth_from_url 26 | from requests.exceptions import InvalidSchema 27 | import requests.compat 28 | from requests_toolbelt.multipart.encoder import MultipartEncoder 29 | import urllib3 30 | try: 31 | from urllib3.contrib.socks import SOCKSProxyManager 32 | except ImportError: 33 | def SOCKSProxyManager(*args, **kwargs): 34 | raise InvalidSchema("Missing dependencies for SOCKS support.") 35 | 36 | from stormshield.sns.configparser import ConfigParser 37 | import stormshield.sns.crc as snscrc 38 | from packaging import version 39 | 40 | from .__version__ import __version__ 41 | 42 | URLLIB3V2 = version.parse(urllib3.__version__) >= version.parse('2.0.0') 43 | 44 | #disable ssl warnings, we have --sslverify* for that 45 | requests.packages.urllib3.disable_warnings( 46 | requests.packages.urllib3.exceptions.InsecureRequestWarning) 47 | try: 48 | requests.packages.urllib3.disable_warnings( 49 | requests.packages.urllib3.exceptions.SubjectAltNameWarning) 50 | except AttributeError: 51 | # urllib3 v2 doesn't have the exception anymore 52 | pass 53 | #disable http warning 'Received response with both Content-Length and Transfer-Encoding set' 54 | logging.getLogger(requests.packages.urllib3.__name__).setLevel(logging.ERROR) 55 | 56 | class HostNameAdapter(HTTPAdapter): 57 | """ HTTP adapter to disable strict ssl host name verification or check hostname against common name """ 58 | 59 | def __init__(self, host=None): 60 | self.host = host 61 | super().__init__() 62 | 63 | def init_poolmanager(self, connections, maxsize, block=False, **pool_kwargs): 64 | 65 | if URLLIB3V2: 66 | context = ssl.create_default_context() 67 | context.hostname_checks_common_name = True # use CN field for factory Stormshield certificates 68 | context.check_hostname = False # check is done with assert_hostname 69 | 70 | self.poolmanager = PoolManager(num_pools=connections, 71 | maxsize=maxsize, 72 | block=block, 73 | assert_hostname=self.host, 74 | ssl_context=context, 75 | **pool_kwargs) 76 | else: 77 | self.poolmanager = PoolManager(num_pools=connections, 78 | maxsize=maxsize, 79 | block=block, 80 | assert_hostname=False, 81 | **pool_kwargs) 82 | 83 | def proxy_manager_for(self, proxy, **proxy_kwargs): 84 | if proxy in self.proxy_manager: 85 | manager = self.proxy_manager[proxy] 86 | elif proxy.lower().startswith('socks'): 87 | username, password = get_auth_from_url(proxy) 88 | 89 | if URLLIB3V2: 90 | context = ssl.create_default_context() 91 | context.hostname_checks_common_name = True 92 | context.check_hostname = False 93 | 94 | manager = self.proxy_manager[proxy] = SOCKSProxyManager( 95 | proxy, 96 | username=username, 97 | password=password, 98 | num_pools=self._pool_connections, 99 | maxsize=self._pool_maxsize, 100 | block=self._pool_block, 101 | assert_hostname=self.host, 102 | ssl_context=context, 103 | **proxy_kwargs 104 | ) 105 | else: 106 | manager = self.proxy_manager[proxy] = SOCKSProxyManager( 107 | proxy, 108 | username=username, 109 | password=password, 110 | num_pools=self._pool_connections, 111 | maxsize=self._pool_maxsize, 112 | block=self._pool_block, 113 | assert_hostname=False, 114 | **proxy_kwargs 115 | ) 116 | else: 117 | proxy_headers = self.proxy_headers(proxy) 118 | 119 | if URLLIB3V2: 120 | context = ssl.create_default_context() 121 | context.hostname_checks_common_name = True 122 | context.check_hostname = False 123 | 124 | manager = self.proxy_manager[proxy] = proxy_from_url( 125 | proxy, 126 | proxy_headers=proxy_headers, 127 | num_pools=self._pool_connections, 128 | maxsize=self._pool_maxsize, 129 | block=self._pool_block, 130 | assert_hostname=self.host, 131 | ssl_context=context, 132 | **proxy_kwargs) 133 | else: 134 | manager = self.proxy_manager[proxy] = proxy_from_url( 135 | proxy, 136 | proxy_headers=proxy_headers, 137 | num_pools=self._pool_connections, 138 | maxsize=self._pool_maxsize, 139 | block=self._pool_block, 140 | assert_hostname=False, 141 | **proxy_kwargs) 142 | 143 | return manager 144 | 145 | class DNSResolverHTTPSAdapter(HTTPAdapter): 146 | """ HTTP adapter to check peer common_name with provided host name """ 147 | 148 | def __init__(self, common_name, host, pool_connections=DEFAULT_POOLSIZE, 149 | pool_maxsize=DEFAULT_POOLSIZE, max_retries=DEFAULT_RETRIES, 150 | pool_block=DEFAULT_POOLBLOCK): 151 | self.__common_name = common_name 152 | self.__host = host 153 | 154 | self.__is_stormshield_cert = True 155 | if re.search(r"\.", self.__common_name): 156 | self.__is_stormshield_cert = False 157 | 158 | super(DNSResolverHTTPSAdapter, self).__init__(pool_connections=pool_connections, 159 | pool_maxsize=pool_maxsize, 160 | max_retries=max_retries, 161 | pool_block=pool_block) 162 | 163 | def init_poolmanager(self, connections, maxsize, block=DEFAULT_POOLBLOCK, **pool_kwargs): 164 | pool_kwargs['assert_hostname'] = self.__common_name 165 | if URLLIB3V2 and self.__is_stormshield_cert: 166 | context = ssl.create_default_context() 167 | context.hostname_checks_common_name = True # use CN field for factory Stormshield certificates 168 | context.check_hostname = False # check is done with assert_hostname 169 | pool_kwargs["ssl_context"] = context 170 | super(DNSResolverHTTPSAdapter, self).init_poolmanager(connections, 171 | maxsize, 172 | block=block, 173 | **pool_kwargs) 174 | 175 | class Response(): 176 | """ :class:`Response ` object contains the SNS API response to a request """ 177 | 178 | def __init__(self, code=None, ret=0, msg=None, output=None, xml=None): 179 | self.code = code 180 | self.ret = ret 181 | self.msg = msg 182 | self.output = output 183 | self.xml = xml 184 | 185 | self.parser = ConfigParser(output) 186 | self.data = self.parser.data 187 | self.format = self.parser.format 188 | 189 | def __repr__(self): 190 | return self.output 191 | 192 | def __bool__(self): 193 | """ Returns True if :attr:`ret` is OK or WARNING. """ 194 | return self.ret >= 100 and self.ret < 200 195 | 196 | def quote(value): 197 | """ Quote value if needed """ 198 | try: 199 | if value and (type(value) == str or type(value) == unicode) and ' ' in value: 200 | return '"' + value + '"' 201 | except: 202 | # in python3 unicode class doesn't exists 203 | pass 204 | return value 205 | 206 | def format_output(output): 207 | """ Format command output in ini/section or text format""" 208 | nws_node = ElementTree.fromstring(output) 209 | serverd_node = nws_node[0] 210 | ini = '{} code={} msg="{}"'.format(serverd_node.get('ret'), 211 | serverd_node.get('code'), 212 | serverd_node.get('msg')) 213 | if len(list(nws_node)) > 1: 214 | data_node = serverd_node[0] 215 | node_format = data_node.get('format') 216 | ini += ' format="{}"\n'.format(node_format) 217 | if node_format == 'raw': 218 | if data_node.text: 219 | ini += data_node.text 220 | elif node_format == 'section': 221 | for section_node in data_node: 222 | ini += '[{}]\n'.format(section_node.get('title')) 223 | for key_node in section_node: 224 | ini += '{}={}\n'.format(key_node.get('name'), 225 | quote(key_node.get('value'))) 226 | elif node_format == 'section_line': 227 | for section_node in data_node: 228 | ini += '[{}]\n'.format(section_node.get('title')) 229 | for line_node in section_node: 230 | tokens = [] 231 | for key_node in line_node: 232 | tokens.append('{}={}'.format(key_node.get('name'), 233 | quote(key_node.get('value')))) 234 | ini += " ".join(tokens) + "\n" 235 | elif node_format == 'list': 236 | for section_node in data_node: 237 | ini += '[{}]\n'.format(section_node.get('title')) 238 | for line_node in section_node: 239 | ini += "{}\n".format(line_node.text) 240 | elif node_format == 'xml': 241 | # display xml data node 242 | ini += Et.tostring(data_node).decode() + "\n" 243 | serverd_node = nws_node[1] 244 | ini += '{} code={} msg="{}"'.format(serverd_node.get('ret'), 245 | serverd_node.get('code'), 246 | serverd_node.get('msg')) 247 | return ini 248 | 249 | class MissingHost(ValueError): 250 | """ The remote host is missing """ 251 | 252 | class MissingAuth(ValueError): 253 | """ password or user certificate is missing """ 254 | 255 | class MissingCABundle(ValueError): 256 | """ The certificate authority bundle is missing """ 257 | 258 | class TOTPNeededError(Exception): 259 | """ Time-base one time password needed """ 260 | 261 | class AuthenticationError(Exception): 262 | """ authentication failed """ 263 | 264 | class ServerError(Exception): 265 | """ NWS server error """ 266 | 267 | class FileError(Exception): 268 | """ file access error """ 269 | 270 | class SSLClient: 271 | """SSL client to SNS configuration API """ 272 | 273 | SSL_SERVERD_OK = 100 274 | SSL_SERVERD_REQUEST_ERROR = 200 275 | SSL_SERVERD_UNKNOWN_COMMAND = 201 276 | SSL_SERVERD_ERROR_COMMAND = 202 277 | SSL_SERVERD_INVALID_SESSION = 203 278 | SSL_SERVERD_EXPIRED_SESSION = 204 279 | SSL_SERVERD_AUTH_ERROR = 205 280 | SSL_SERVERD_PENDING_TRANSFER = 206 281 | SSL_SERVERD_PENDING_UPLOAD = 207 282 | SSL_SERVERD_OVERHEAT = 500 283 | SSL_SERVERD_UNREACHABLE = 501 284 | SSL_SERVERD_DISCONNECTED = 502 285 | SSL_SERVERD_INTERNAL_ERROR = 900 286 | 287 | SSL_SERVERD_MSG = { 288 | SSL_SERVERD_REQUEST_ERROR: "Request error", 289 | SSL_SERVERD_UNKNOWN_COMMAND: "Unknown command", 290 | SSL_SERVERD_ERROR_COMMAND: "Command error", 291 | SSL_SERVERD_INVALID_SESSION: "Invalid session", 292 | SSL_SERVERD_EXPIRED_SESSION: "Expired session", 293 | SSL_SERVERD_AUTH_ERROR: "Authentication error", 294 | SSL_SERVERD_PENDING_TRANSFER: "Pending transfer", 295 | SSL_SERVERD_PENDING_UPLOAD: "Upload pending", 296 | SSL_SERVERD_OVERHEAT: "Server overheat", 297 | SSL_SERVERD_UNREACHABLE: "Server unreachable", 298 | SSL_SERVERD_DISCONNECTED: "Server disconnected", 299 | SSL_SERVERD_INTERNAL_ERROR: "Internal error" 300 | } 301 | 302 | SRV_RET_OK = 100 303 | SRV_RET_DOWNLOAD = 101 304 | SRV_RET_UPLOAD = 102 305 | SRV_RET_LASTCMD = 103 306 | SRV_RET_MUSTREBOOT = 104 307 | SRV_RET_WARNING = 110 308 | SRV_RET_MULTIWARN = 111 309 | SRV_RET_COMMAND = 200 310 | SRV_RET_MULTILINE = 201 311 | SRV_RET_AUTHFAILED = 202 312 | SRV_RET_IDLE = 203 313 | SRV_RET_AUTHLIMIT = 204 314 | SRV_RET_AUTHLEVEL = 205 315 | SRV_RET_LICENCE = 206 316 | 317 | SRV_RET_MSG = { 318 | SRV_RET_OK: 'Command successful', 319 | SRV_RET_DOWNLOAD: 'Command successful, download follow', 320 | SRV_RET_UPLOAD: 'Command successful, upload follow', 321 | SRV_RET_LASTCMD: 'Command successful, you will be disconnected', 322 | SRV_RET_MUSTREBOOT: 'Command successful, but reboot needed', 323 | SRV_RET_WARNING: 'Command successful, but warning', 324 | SRV_RET_MULTIWARN: 'Command successful, but multiple warnings', 325 | SRV_RET_COMMAND: 'Command error', 326 | SRV_RET_MULTILINE: 'Return error message on many lines', 327 | SRV_RET_AUTHFAILED: 'Authentication failed', 328 | SRV_RET_IDLE: 'Client is idle, disconnecting', 329 | SRV_RET_AUTHLIMIT: 'Maximum number of authentication user reached for that level', 330 | SRV_RET_AUTHLEVEL: 'Not enough privilege', 331 | SRV_RET_LICENCE: 'Licence restriction' 332 | } 333 | 334 | SERVERD_WAIT_DOWNLOAD = "00a01c00" 335 | SERVERD_WAIT_UPLOAD = "00a00300" 336 | AUTH_SUCCESS = "AUTH_SUCCESS" 337 | AUTH_FAILED = "AUTH_FAILED" 338 | NEED_TOTP_AUTH = "NEED_TOTP_AUTH" 339 | ERR_BRUTEFORCE = "ERR_BRUTEFORCE" 340 | 341 | fileregexp = re.compile(r'^(?P.+?)\s*[<>]\s*(?!.*\")(?P.*?)$') 342 | 343 | CHUNK_SIZE = 10240 # bytes 344 | 345 | def __init__(self, user='admin', password=None, totp=None, host=None, ip=None, port=443, cabundle=None, 346 | sslverifypeer=True, sslverifyhost=True, credentials=None, 347 | usercert=None, autoconnect=True, proxy=None, timeout=None): 348 | """:class:`SSLclient ` constructor. 349 | 350 | :param user: Optional user name. 351 | :param password: Optional password. 352 | :param totp: Optional time-based one time password. 353 | :param host: hostname to connect or certificate common name (appliance serial). 354 | :param ip: Optional ip address to connect. 355 | :param port: Optional port number. 356 | :param cabundle: Optional certificat authorities bundle file in PEM format. 357 | :param sslverifypeer: Optional boolean to verify remote certificate authority. 358 | :param sslverifyhost: Optional boolean to verify remote certificate common name. 359 | :param credentials: Optional list of requested privileges. 360 | :param usercert: Optional user certificate. 361 | :param autoconnect: Connect to the appliance at initialization 362 | :param proxy: https proxy url (socks5://user:pass@host:port http://user:password@host/) 363 | :param timeout: connection and read timeout in seconds 364 | """ 365 | 366 | self.user = user 367 | self.password = password 368 | self.totp = totp 369 | self.host = host 370 | self.ip = ip 371 | self.port = port 372 | self.cabundle = cabundle 373 | self.app = 'sslclient' 374 | self.sslverifypeer = sslverifypeer 375 | self.sslverifyhost = sslverifyhost 376 | self.credentials = credentials 377 | self.usercert = usercert 378 | self.sessionid = "" 379 | self.protocol = "" 380 | self.sessionlevel = "" 381 | self.dl_size = 0 382 | self.dl_crc = "" 383 | self.autoconnect = autoconnect 384 | self.proxy = proxy 385 | self.conn_options = {} 386 | 387 | if host is None: 388 | raise MissingHost("Host parameter must be provided") 389 | if password is None and usercert is None: 390 | raise MissingAuth("Password parameter must be provided") 391 | if password is None and totp is not None: 392 | raise MissingAuth("Password parameter must be provided when totp parameter is provided") 393 | if usercert is not None and not os.path.isfile(usercert): 394 | raise MissingAuth("User certificate not found") 395 | if cabundle is None: 396 | # use default cabundle 397 | self.cabundle = os.path.normpath(os.path.join(os.path.dirname(__file__), "..", 'bundle.ca')) 398 | if not os.path.isfile(self.cabundle): 399 | raise MissingCABundle("Certificate authority bunble not found") 400 | 401 | #test ipv6 address 402 | try: 403 | ipaddress.IPv6Address(self.host) 404 | urlhost = "[{}]".format(self.host) 405 | except ipaddress.AddressValueError: 406 | urlhost = self.host 407 | 408 | self.baseurl = 'https://' + urlhost + ':' + str(self.port) 409 | 410 | self.headers = { 411 | 'user-agent': 'stormshield.sns.sslclient/{} ({})'.format( 412 | __version__, platform.platform()) 413 | } 414 | 415 | self.session = requests.Session() 416 | if self.sslverifypeer: 417 | self.session.verify = self.cabundle 418 | else: 419 | self.session.verify = False 420 | 421 | if not self.sslverifyhost: 422 | if URLLIB3V2: 423 | self.session.mount(self.baseurl, HostNameAdapter(False)) 424 | else: 425 | self.session.mount(self.baseurl, HostNameAdapter()) 426 | 427 | if self.ip is not None: 428 | #test ipv6 address 429 | try: 430 | ipaddress.IPv6Address(self.ip) 431 | urlip = "[{}]".format(self.ip) 432 | except ipaddress.AddressValueError: 433 | urlip = self.ip 434 | self.baseurl = 'https://' + urlip + ':' + str(self.port) 435 | self.session.mount(self.baseurl.lower(), DNSResolverHTTPSAdapter(self.host, self.ip)) 436 | 437 | if self.usercert is not None: 438 | self.session.cert = self.usercert 439 | 440 | if self.proxy: 441 | self.session.proxies = { "https": self.proxy} 442 | 443 | if timeout is not None: 444 | self.conn_options = { "timeout": timeout } 445 | 446 | self.logger = logging.getLogger() 447 | 448 | if self.autoconnect: 449 | self.connect() 450 | 451 | @staticmethod 452 | def get_completer(): 453 | """ Get the path to the installed cmd.complete file """ 454 | return os.path.normpath(os.path.join(os.path.dirname(__file__), "..", 'cmd.complete')) 455 | 456 | def connect(self): 457 | """ Connect to the server """ 458 | 459 | self.logger.log(logging.INFO, 'Connecting to %s on port %d with user %s%s', 460 | self.host, self.port, self.user, " (proxy {})".format(self.proxy) if self.proxy else "") 461 | 462 | # 1. Authentication and get cookie 463 | if self.usercert is not None: 464 | # user cert authentication 465 | self.logger.log(logging.DEBUG, "Authentication with SSL certificate") 466 | request = self.session.get( 467 | self.baseurl + '/auth/admin.html?sslcert=1&app={}'.format(self.app), 468 | headers=self.headers, **self.conn_options) 469 | else: 470 | # password authentication 471 | self.logger.log(logging.DEBUG, "Authentication with user/password") 472 | data = { 'uid':base64.b64encode(self.user.encode('utf-8')), 473 | 'pswd':base64.b64encode(self.password.encode('utf-8')), 474 | 'app':self.app } 475 | 476 | if self.totp is not None: 477 | data['totp']=base64.b64encode(self.totp.encode('utf-8')) 478 | 479 | request = self.session.post( 480 | self.baseurl + '/auth/admin.html', 481 | data, 482 | headers=self.headers, 483 | **self.conn_options) 484 | 485 | self.logger.log(logging.DEBUG, request.text) 486 | 487 | try: 488 | nws_node = ElementTree.fromstring(request.content) 489 | msg = nws_node.attrib['msg'] 490 | except (ElementTree.ParseError, KeyError): 491 | raise ServerError("Can't decode authentication result") 492 | 493 | if msg == self.ERR_BRUTEFORCE: 494 | nws_node = ElementTree.fromstring(request.content) 495 | delay = nws_node.attrib['delay'] 496 | raise AuthenticationError("Brut force detected, try again after " + delay + " seconds.") 497 | if msg == self.NEED_TOTP_AUTH: 498 | raise TOTPNeededError("TOTP is needed") 499 | if msg != self.AUTH_SUCCESS: 500 | raise AuthenticationError("Authentication failed") 501 | 502 | # 2. Serverd session 503 | data = {'app': self.app, 'id': 0} 504 | if self.credentials is not None: 505 | data['reqlevel'] = self.credentials 506 | request = self.session.post( 507 | self.baseurl + '/api/auth/login', 508 | data=data, 509 | headers=self.headers, 510 | **self.conn_options) 511 | 512 | self.logger.log(logging.DEBUG, request.text) 513 | 514 | if request.status_code == requests.codes.OK: 515 | nws_node = ElementTree.fromstring(request.content) 516 | ret = int(nws_node.attrib['code']) 517 | msg = nws_node.attrib['msg'] 518 | 519 | if ret != self.SSL_SERVERD_OK: 520 | raise ServerError("ERROR: {} {}".format(ret, msg)) 521 | 522 | self.sessionid = nws_node.find('sessionid').text 523 | self.protocol = nws_node.find('protocol').text 524 | self.sessionlevel = nws_node.find('sessionlevel').text 525 | 526 | self.logger.log(logging.DEBUG, "Session ID: %s", self.sessionid) 527 | self.logger.log(logging.DEBUG, "Protocol: %s", self.protocol) 528 | self.logger.log(logging.DEBUG, "Session level: %s", self.sessionlevel) 529 | 530 | else: 531 | raise ServerError("can't get serverd session") 532 | 533 | 534 | 535 | def disconnect(self): 536 | """ Disconnect from the server """ 537 | 538 | request = self.session.get( 539 | self.baseurl + '/api/auth/logout?sessionid=' + self.sessionid, 540 | headers=self.headers, **self.conn_options) 541 | 542 | if request.status_code == requests.codes.OK: 543 | self.logger.log(logging.INFO, 'Disconnected from %s', self.host) 544 | else: 545 | self.logger.log(logging.ERROR, 'Disconnect failed') 546 | 547 | self.session.close() 548 | 549 | def nws_parse(self, code): 550 | """ Parse server response """ 551 | 552 | if code == self.SSL_SERVERD_OK: 553 | return 554 | 555 | if code == self.SSL_SERVERD_AUTH_ERROR: 556 | raise AuthenticationError(self.SSL_SERVERD_MSG[code]) 557 | elif code in self.SSL_SERVERD_MSG: 558 | raise ServerError(self.SSL_SERVERD_MSG[code]) 559 | else: 560 | raise ServerError("Unknown error") 561 | 562 | def send_command(self, command, **conn_options): 563 | """Execute a NSRPC command on the remote appliance. 564 | 565 | :param command: SNS API command. Files can be uploaded by adding '< filename' 566 | at the end of the command. Downloads are handled with '> filename'. 567 | :return: :class:`Response ` object 568 | :rtype: stormshield.sns.Response 569 | """ 570 | 571 | # overload connection options 572 | for k in self.conn_options: 573 | if k not in conn_options: 574 | conn_options[k] = self.conn_options[k] 575 | 576 | filename = None 577 | result = self.fileregexp.match(command) 578 | if result: 579 | command = result.group('cmd') 580 | filename = result.group('file') 581 | 582 | request = self.session.get( 583 | self.baseurl + '/api/command?sessionid=' + self.sessionid + 584 | '&cmd=' + requests.compat.quote(command.encode('utf-8')), # manually done since we need %20 encoding 585 | headers=self.headers, **conn_options) 586 | 587 | self.logger.log(logging.DEBUG, request.text) 588 | 589 | if request.status_code == requests.codes.OK: 590 | nws_node = ElementTree.fromstring(request.content) 591 | code = int(nws_node.attrib['code']) 592 | self.nws_parse(code) 593 | serverd = nws_node[0] 594 | 595 | if serverd is not None: 596 | serverd_code = serverd.attrib['code'] 597 | serverd_ret = int(serverd.attrib['ret']) 598 | serverd_msg = serverd.attrib['msg'] 599 | 600 | response = Response(ret=serverd_ret, 601 | code=serverd_code, 602 | msg=serverd_msg, 603 | output=format_output(request.content), 604 | xml=request.text) 605 | 606 | #multiline answer get the final code 607 | if len(list(nws_node)) > 1: 608 | response.code = nws_node[1].get('code') 609 | response.msg = nws_node[1].get('msg') 610 | response.ret = int(nws_node[1].get('ret')) 611 | 612 | if serverd_code == self.SERVERD_WAIT_UPLOAD: 613 | if filename: 614 | return self.upload(filename) 615 | return response 616 | 617 | if serverd_code == self.SERVERD_WAIT_DOWNLOAD: 618 | data = serverd.find('data') 619 | # keep size and crc for further verification 620 | if data.get('format') == 'section': 621 | #
622 | key = data.find('section').find('key') 623 | values = key.get('value').split(',') 624 | self.dl_size = int(values[2].split('=')[1]) 625 | self.dl_crc = values[1].split('=')[1] 626 | else: 627 | # 439B8525096 628 | self.dl_size = int(data.find('size').text) 629 | self.dl_crc = data.find('crc').text 630 | if filename: 631 | return self.download(filename) 632 | return response 633 | else: 634 | raise ServerError("HTTP error {}".format(request.status_code)) 635 | 636 | return response 637 | 638 | def download(self, filename): 639 | """ handle file download """ 640 | 641 | request = self.session.get( 642 | self.baseurl + '/api/download/tmp.file?sessionid=' + self.sessionid, 643 | headers=self.headers, 644 | stream=True, 645 | **self.conn_options) 646 | 647 | if request.status_code == requests.codes.OK: 648 | size = 0 649 | crc = snscrc.CRC32_init 650 | try: 651 | with open(filename, "wb") as savefile: 652 | for chunk in request.iter_content(self.CHUNK_SIZE): 653 | savefile.write(chunk) 654 | size += len(chunk) 655 | crc = snscrc.update_crc32(chunk, crc) 656 | except Exception as exception: 657 | self.logger.log(logging.ERROR, str(exception)) 658 | raise FileError("Can't save file") 659 | 660 | if size != self.dl_size: 661 | raise ServerError("Download error: {} bytes downloaded, expecting {} bytes".format( 662 | size, self.dl_size)) 663 | 664 | crc = "%X" % (crc) 665 | 666 | if crc != self.dl_crc: 667 | raise ServerError("Download error: crc {}, expecting {}".format(crc, self.dl_crc)) 668 | 669 | return Response(ret=100, code='00a00100', msg='OK', 670 | output='100 code=00a00100 msg="Ok"', 671 | xml='' + 672 | '') 673 | 674 | raise ServerError("HTTP error {}".format(request.status_code)) 675 | 676 | def upload(self, filename): 677 | """ handle file upload """ 678 | 679 | uploadh = open(filename, 'rb') 680 | 681 | data = MultipartEncoder( 682 | fields={'upload': uploadh} 683 | ) 684 | headers = self.headers 685 | headers['Content-Type'] = data.content_type 686 | 687 | request = self.session.post( 688 | self.baseurl + '/api/upload?sessionid=' + self.sessionid, 689 | headers=headers, 690 | data=data, 691 | **self.conn_options) 692 | 693 | uploadh.close() 694 | 695 | if request.status_code == requests.codes.OK: 696 | nws_node = ElementTree.fromstring(request.content) 697 | code = int(nws_node.attrib['code']) 698 | self.nws_parse(code) 699 | 700 | return Response(code=nws_node[0].get('code'), 701 | ret=int(nws_node[0].get('ret')), 702 | msg=nws_node[0].get('msg'), 703 | output=format_output(request.content), 704 | xml=request.text) 705 | 706 | raise ServerError("HTTP error {}".format(request.status_code)) 707 | -------------------------------------------------------------------------------- /stormshield/sns/sslclient/__version__.py: -------------------------------------------------------------------------------- 1 | # major.minor.patch 2 | # major: breaking API change 3 | # minor: new functionality 4 | # patch: bugfix 5 | __version__ = "1.1.0" 6 | -------------------------------------------------------------------------------- /tests/test_auth.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | import os 4 | import unittest 5 | from stormshield.sns.sslclient import SSLClient 6 | 7 | APPLIANCE = os.getenv('APPLIANCE', "") 8 | SERIAL = os.getenv('SERIAL', "") 9 | PASSWORD = os.getenv('PASSWORD', "") 10 | SSLVERIFYPEER = os.getenv('SSLVERIFYPEER', "1") == "1"; 11 | 12 | @unittest.skipIf(APPLIANCE=="", "APPLIANCE env var must be set to the ip/hostname of a running SNS appliance") 13 | @unittest.skipIf(SERIAL=="", "SERIAL env var must be set to the firewall serial number") 14 | @unittest.skipIf(PASSWORD=="", "PASSWORD env var must be set to the firewall password") 15 | class TestAuth(unittest.TestCase): 16 | """ Test authentication options """ 17 | 18 | def test_sslverifyhost(self): 19 | """ Test sslverifyhost option """ 20 | 21 | try: 22 | client = SSLClient(host=SERIAL, ip=APPLIANCE, user='admin', password=PASSWORD, sslverifyhost=True, sslverifypeer=SSLVERIFYPEER) 23 | self.assertTrue(1==1, "SSLClient connects with sslverifyhost=True") 24 | except: 25 | self.fail("SSLClient did not connect") 26 | 27 | response = client.send_command('LIST') 28 | self.assertEqual(response.ret, 100) 29 | 30 | client.disconnect() 31 | -------------------------------------------------------------------------------- /tests/test_cert.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | import os 4 | import unittest 5 | from stormshield.sns.sslclient import SSLClient 6 | 7 | APPLIANCE = os.getenv('APPLIANCE', "") 8 | FQDN = os.getenv('FQDN', "") 9 | PASSWORD = os.getenv('PASSWORD', "") 10 | CABUNDLE = os.getenv('CABUNDLE', "") 11 | CERT = os.getenv('CERT', "") 12 | 13 | @unittest.skipIf(APPLIANCE=="", "APPLIANCE env var must be set to the ip/hostname of a running SNS appliance") 14 | @unittest.skipIf(FQDN=="", "FQDN env var must be set to the firewall fqdn") 15 | @unittest.skipIf(PASSWORD=="", "PASSWORD env var must be set to the firewall password") 16 | @unittest.skipIf(CABUNDLE=="", "CABUNDLE env var must be set to the CA bundle file") 17 | @unittest.skipIf(CERT=="", "CERT env var must be set to the certificate file") 18 | class TestCert(unittest.TestCase): 19 | """ Test cabundle / certificate authentication options """ 20 | 21 | def test_sslverifypeer(self): 22 | """ Test sslverifypeer option """ 23 | 24 | # by default sslverifypeer is True 25 | try: 26 | client = SSLClient(host=APPLIANCE, user='admin', password=PASSWORD) 27 | self.fail("SSLClient should have failed (untrusted CA)") 28 | except Exception as exception: 29 | self.assertTrue(True, "SSLClient did not connect (untrusted CA)") 30 | 31 | try: 32 | client = SSLClient(host=APPLIANCE, user='admin', password=PASSWORD, sslverifypeer=False) 33 | self.assertTrue(True, "SSLClient connects with sslverifypeer=False") 34 | except Exception as exception: 35 | print(exception) 36 | self.fail("SSLClient did not connect") 37 | 38 | response = client.send_command('LIST') 39 | self.assertEqual(response.ret, 100) 40 | 41 | client.disconnect() 42 | 43 | def test_cabundle(self): 44 | """ Test cabundle option """ 45 | 46 | try: 47 | client = SSLClient(host=FQDN, ip=APPLIANCE, user='admin', password=PASSWORD, sslverifyhost=True, cabundle=CABUNDLE) 48 | self.assertTrue(1==1, "SSLClient connects with cabundle") 49 | except Exception as exception: 50 | print(exception) 51 | self.fail("SSLClient did not connect") 52 | 53 | response = client.send_command('LIST') 54 | self.assertEqual(response.ret, 100) 55 | 56 | client.disconnect() 57 | 58 | def test_cert(self): 59 | """ Test user certificate authentication """ 60 | 61 | try: 62 | client = SSLClient(host=FQDN, ip=APPLIANCE, usercert=CERT, sslverifyhost=True, cabundle=CABUNDLE) 63 | self.assertTrue(1==1, "SSLClient connects with cabundle") 64 | except Exception as exception: 65 | print(exception) 66 | self.fail("SSLClient did not connect") 67 | 68 | response = client.send_command('LIST') 69 | self.assertEqual(response.ret, 100) 70 | 71 | client.disconnect() 72 | -------------------------------------------------------------------------------- /tests/test_configparser.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | 4 | import unittest 5 | import json 6 | 7 | from stormshield.sns.configparser import ConfigParser 8 | 9 | class TestConfigParser(unittest.TestCase): 10 | 11 | def test_section(self): 12 | """ Get token from section """ 13 | 14 | input = """101 code=00a01000 msg="Begin" format="section" 15 | [Result] 16 | Type="Firewall" 17 | Model="V50-A" 18 | MachineType="amd64" 19 | Version="3.7.1" 20 | ASQVersion="8.4.0" 21 | 100 code=00a00100 msg="Ok\"""" 22 | 23 | expected = '3.7.1' 24 | 25 | config = ConfigParser(input) 26 | self.assertEqual(expected, config.get(section='Result', token='Version')) 27 | 28 | def test_default(self): 29 | """ Test default value """ 30 | 31 | input = """101 code=00a01000 msg="Begin" format="section" 32 | [Result] 33 | Type="Firewall" 34 | Model="V50-A" 35 | MachineType="amd64" 36 | Version="3.7.1" 37 | ASQVersion="8.4.0" 38 | 100 code=00a00100 msg="Ok\"""" 39 | 40 | expected = 1 41 | 42 | config = ConfigParser(input) 43 | self.assertEqual(expected, config.get(section='Result', token='DefaultConfig', default=1)) 44 | 45 | def test_line(self): 46 | """ Get line from section """ 47 | 48 | input = """101 code=00a01000 msg="Begin" format="list" 49 | [Filter] 50 | position=1; separator color="c0c0c0" comment="FQDN" collapse="0" nb_elements="1" first_ruleid="1" 51 | position=2; usage=0 match=0 ruleid=1: pass log from group="ruser"@Network_lan domain storm to rr.labo.int # Created on 2016-05-20 13:25:24,by admin (10.2.9.2) 52 | position=3; separator color="c0c0c0" comment="Office365" collapse="0" nb_elements="4" first_ruleid="2" 53 | position=4; usage=0 match=0 ruleid=2: pass log tos 8 from Network_lan to outlook.office365.com # Créée le 2016-06-23 15:17:04, par admin (10.2.9.2) 54 | position=5; usage=0 match=0 ruleid=3: pass settos 24 log from Network_lan to xsi.outlook.com # Créée le 2016-06-23 15:29:44, par admin (10.2.9.2) 55 | position=6; usage=0 match=0 ruleid=4: pass log from Network_lan to webdir.online.lync.com # Créée le 2016-06-23 15:29:47, par admin (10.2.9.2) 56 | position=7; separator color="c0c0c0" comment="DEFAULT" collapse="0" nb_elements="2" first_ruleid="14" 57 | position=8; usage=3 match=24994 ruleid=14: pass from any to any 58 | position=9; usage=0 match=0 ruleid=15: pass from any on out to Firewall_out port ssh # Allow SSH on OUT 59 | 100 code=00a00100 msg="Ok\" 60 | """ 61 | 62 | expected = 'position=3; separator color="c0c0c0" comment="Office365" collapse="0" nb_elements="4" first_ruleid="2"' 63 | 64 | config = ConfigParser(input) 65 | self.assertEqual(expected, config.get(section='Filter', line=3)) 66 | 67 | def test_get_sectionline(self): 68 | """ Get section_line section """ 69 | 70 | input = """101 code=00a01000 msg="Begin" format="section_line" 71 | [Result] 72 | name=ntp1.stormshieldcs.eu keynum=none type=host 73 | name="ntp2.stormshieldcs.eu" keynum=none type=host 74 | 100 code=00a00100 msg="Ok\"""" 75 | 76 | expected = [ 77 | {"name":"ntp1.stormshieldcs.eu", "keynum":"none", "type":"host"}, 78 | {"name":"ntp2.stormshieldcs.eu", "keynum":"none", "type":"host"} 79 | ] 80 | 81 | config = ConfigParser(input) 82 | self.assertEqual(expected, config.get(section='Result')) 83 | 84 | 85 | def test_getall(self): 86 | """ Get all tokens of a section """ 87 | 88 | input="""101 code=00a01000 msg="Begin" format="section" 89 | [Server] 90 | 1=dnscache 91 | 2=dns1.google.com 92 | 100 code=00a00100 msg="Ok\"""" 93 | 94 | expected = { "1":"dnscache", "2":"dns1.google.com"} 95 | 96 | config = ConfigParser(input) 97 | self.assertEqual(expected, config.get(section='Server')) 98 | 99 | def test_get_list(self): 100 | """ Get list section """ 101 | 102 | input = """101 code=00a01000 msg="Begin" format="list" 103 | [Result] 104 | net1 105 | net2 106 | 100 code=00a00100 msg="Ok\"""" 107 | 108 | expected = ['net1', 'net2'] 109 | 110 | config = ConfigParser(input) 111 | self.assertEqual(expected, config.get(section='Result')) 112 | 113 | def test_case_insensitive(self): 114 | """ Test key names are case insensitive """ 115 | 116 | input = """101 code=00a01000 msg="Begin" format="section" 117 | [Result] 118 | token1=value1 119 | token2=value2 120 | 100 code=00a00100 msg="Ok\"""" 121 | 122 | config = ConfigParser(input) 123 | self.assertEqual('value1', config.get(section='result', token='TOKEN1')) 124 | self.assertEqual('value1', config.data['result']['TOKEN1']) 125 | 126 | def test_serialize(self): 127 | """ Test serialize data structure """ 128 | 129 | input = """101 code=00a01000 msg="Begin" format="section" 130 | [Server] 131 | 1="dns1" 132 | 2="dns2" 133 | 100 code=00a00100 msg="Ok\"""" 134 | 135 | expected = {"Server": { "1": "dns1", "2": "dns2"}} 136 | 137 | config = ConfigParser(input) 138 | self.assertEqual(json.dumps(expected), json.dumps(config.serialize_data())) 139 | 140 | input = """101 code=00a01000 msg="Begin" format="section_line" 141 | [Result] 142 | name=ntp1.stormshieldcs.eu keynum=none type=host 143 | name="ntp2.stormshieldcs.eu" keynum=none type=host 144 | 100 code=00a00100 msg="Ok\"""" 145 | 146 | expected = {"Result": [ 147 | {"name": "ntp1.stormshieldcs.eu", "keynum": "none", "type": "host"}, 148 | {"name": "ntp2.stormshieldcs.eu", "keynum": "none", "type": "host"} 149 | ]} 150 | 151 | config = ConfigParser(input) 152 | self.assertEqual(json.dumps(expected), json.dumps(config.serialize_data())) 153 | 154 | def test_empty_value(self): 155 | """ Test empty value and value with ':' """ 156 | 157 | input = """101 code=00a01000 msg="Begin" format="section_line" 158 | [Object] 159 | type=host global=0 name=Firewall_out ip=10.60.3.235 modify=0 comment= 160 | type=host global=0 name=dnscache ip=10.2.0.1 modify=1 comment= resolve=dynamic 161 | type=host global=0 name=myobject ip=1.2.3.4 modify=1 comment= mac=00:11:22:33:44:55 162 | 100 code=00a00100 msg="Ok\"""" 163 | 164 | expected = {"Object": [ 165 | {"type":"host", "global":"0", "name":"Firewall_out", 166 | "ip":"10.60.3.235", "modify":"0", "comment":""}, 167 | {"type":"host", "global":"0", "name":"dnscache", 168 | "ip":"10.2.0.1", "modify":"1", "comment":"", "resolve":"dynamic"}, 169 | {"type":"host", "global":"0", "name":"myobject", 170 | "ip":"1.2.3.4", "modify":"1", "comment":"", "mac":"00:11:22:33:44:55"}]} 171 | 172 | config = ConfigParser(input) 173 | self.assertEqual(expected, config.data) 174 | 175 | def test_slash_in_value(self): 176 | """ Test parsing of value with slash character """ 177 | 178 | input = """101 code=00a01000 msg="Begin" format="section_line" 179 | [StaticRoutes] 180 | Remote=remote_net Address=172.21.0.0/24 Interface=out Gateway=remote_gw Protected=0 State=1 Comment= 181 | 100 code=00a00100 msg="Ok\"""" 182 | 183 | expected = {'StaticRoutes': [ 184 | {'Comment': '', 'Remote': 'remote_net', 'State': '1', 'Protected': '0', 185 | 'Address': '172.21.0.0/24', 'Interface': 'out', 'Gateway': 'remote_gw'}]} 186 | 187 | config = ConfigParser(input) 188 | self.assertEqual(expected, config.data) 189 | 190 | def test_arobase_in_value(self): 191 | """ Test parsing of value with @ character """ 192 | 193 | input = """101 code=00a01000 msg="Begin" format="section_line" 194 | [Result] 195 | ruleid=1 state=on action=pass from=*@* to=*@* comment="default rule (pass all)" 196 | 100 code=00a00100 msg="Ok"\"""" 197 | 198 | expected = {'Result': [ 199 | {'ruleid': '1', 'state': 'on', 'action': 'pass', 'from': '*@*', 200 | 'to': '*@*', 'comment': 'default rule (pass all)'}]} 201 | 202 | config = ConfigParser(input) 203 | self.assertEqual(expected, config.data) 204 | 205 | def test_quote_in_token(self): 206 | """ Test parsing of token with ' character """ 207 | 208 | input = """101 code=00a01000 msg="Begin" format="section_line" 209 | [Object] 210 | type=group global=0 name=it's_a_test nb_elements=2 modify=1 comment= used=0 211 | 100 code=00a00100 msg="Ok"\"""" 212 | 213 | expected = {"Object": [ 214 | {"type":"group", "global":"0", "name":"it's_a_test", 215 | "nb_elements":"2", "modify":"1", "comment":"", "used":"0"}]} 216 | 217 | config = ConfigParser(input) 218 | self.assertEqual(expected, config.data) 219 | 220 | if __name__ == '__main__': 221 | unittest.main() 222 | -------------------------------------------------------------------------------- /tests/test_file.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | 4 | from __future__ import unicode_literals 5 | import random 6 | import string 7 | import os 8 | import sys 9 | import tempfile 10 | import unittest 11 | import shutil 12 | from stormshield.sns.sslclient import SSLClient 13 | 14 | APPLIANCE = os.getenv('APPLIANCE', "") 15 | PASSWORD = os.getenv('PASSWORD', "") 16 | SSLVERIFYPEER = os.getenv('SSLVERIFYPEER', "1") == "1"; 17 | 18 | @unittest.skipIf(APPLIANCE=="", "APPLIANCE env var must be set to the ip/hostname of a running SNS appliance") 19 | @unittest.skipIf(PASSWORD=="", "PASSWORD env var must be set to the firewall password") 20 | class TestFormatIni(unittest.TestCase): 21 | """ Test file upload & download """ 22 | 23 | def setUp(self): 24 | self.client = SSLClient(host=APPLIANCE, user='admin', password=PASSWORD, sslverifyhost=False, sslverifypeer=SSLVERIFYPEER) 25 | 26 | self.tmpdir = tempfile.mkdtemp() 27 | self.upload = os.path.join(self.tmpdir, 'upload') 28 | self.download = os.path.join(self.tmpdir, 'download') 29 | 30 | def tearDown(self): 31 | self.client.disconnect() 32 | shutil.rmtree(self.tmpdir, ignore_errors=True) 33 | 34 | def test_upload_download(self): 35 | """ Test file upload and download """ 36 | 37 | letters = string.ascii_letters + 'éèàÎîô' 38 | 39 | #generate a random file 40 | content = ( "[Filter] \n pass from network_internals to any #ASCII" + 41 | "".join( [random.choice(letters) for i in range(100)] ) 42 | ).encode('utf-8') 43 | with open(self.upload, "wb") as fh: 44 | fh.write(content) 45 | 46 | response = self.client.send_command('CONFIG SLOT UPLOAD slot=1 name=testUpload < ' + self.upload) 47 | self.assertEqual(response.ret, 100) 48 | 49 | response = self.client.send_command('CONFIG SLOT DOWNLOAD slot=1 name=testUpload > ' + self.download) 50 | self.assertEqual(response.ret, 100) 51 | 52 | self.client.send_command('CONFIG SLOT DEFAULT type=filter slot=1') 53 | self.assertEqual(response.ret, 100) 54 | 55 | with open(self.download, "rb") as fh: 56 | downloaded = fh.read() 57 | 58 | self.assertEqual(content, downloaded) 59 | 60 | if __name__ == '__main__': 61 | unittest.main() 62 | -------------------------------------------------------------------------------- /tests/test_format_ini.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | import os 4 | import sys 5 | import unittest 6 | import re 7 | 8 | from stormshield.sns.sslclient import SSLClient 9 | 10 | APPLIANCE = os.getenv('APPLIANCE', "") 11 | PASSWORD = os.getenv('PASSWORD', "") 12 | SSLVERIFYPEER = os.getenv('SSLVERIFYPEER', "1") == "1"; 13 | 14 | @unittest.skipIf(APPLIANCE=="", "APPLIANCE env var must be set to the ip/hostname of a running SNS appliance") 15 | @unittest.skipIf(PASSWORD=="", "PASSWORD env var must be set to the firewall password") 16 | class TestFormatIni(unittest.TestCase): 17 | """ Test INI format """ 18 | 19 | def setUp(self): 20 | self.client = SSLClient(host=APPLIANCE, user='admin', password=PASSWORD, sslverifyhost=False, sslverifypeer=SSLVERIFYPEER) 21 | 22 | self.maxDiff = 5000 23 | 24 | def tearDown(self): 25 | self.client.disconnect() 26 | 27 | def test_raw(self): 28 | """ raw format """ 29 | 30 | expected_re = """101 code=00a01000 msg="Begin" format="raw" 31 | AUTH.* 32 | CHPWD.* 33 | 100 code=00a00100 msg="Ok\"""" 34 | 35 | response = self.client.send_command('HELP') 36 | 37 | self.assertTrue(re.match(expected_re, response.output, re.MULTILINE|re.DOTALL)) 38 | self.assertEqual(response.ret, 100) 39 | 40 | def test_section(self): 41 | """ section format """ 42 | 43 | expected = { 44 | "Global": { 45 | "State": "0", 46 | "RiskHalfLife": "21600", 47 | "RiskTTL": "86400" 48 | }, 49 | "Alarm": { 50 | "Minor": "2", 51 | "Major": "10" 52 | }, 53 | "Sandboxing" : { 54 | "Suspicious": "2", 55 | "Malicious": "50", 56 | "Failed": "0" 57 | }, 58 | "Antivirus": { 59 | "Infected": "100", 60 | "Unknown": "2", 61 | "Failed": "0" 62 | } 63 | } 64 | 65 | response = self.client.send_command('CONFIG HOSTREP SHOW') 66 | 67 | self.assertEqual(response.data, expected) 68 | self.assertEqual(response.ret, 100) 69 | 70 | def test_section_line(self): 71 | """ section_line format """ 72 | 73 | expected = { 74 | 'Object': [ 75 | {'type': 'host', 'global': '0', 'name': '_TestHost1', 'ip': '10.10.5.5', 'modify': '1', 'comment': ''}, 76 | {'type': 'host', 'global': '0', 'name': '_TestHost2', 'ip': '10.10.5.6', 'modify': '1', 'comment': ''}, 77 | {'type': 'host', 'global': '0', 'name': '_TestHost3', 'ip': '10.10.5.7', 'modify': '1', 'comment': ''} 78 | ] 79 | } 80 | 81 | self.client.send_command('CONFIG OBJECT HOST NEW name=_TestHost1 ip=10.10.5.5') 82 | self.client.send_command('CONFIG OBJECT HOST NEW name=_TestHost2 ip=10.10.5.6') 83 | self.client.send_command('CONFIG OBJECT HOST NEW name=_TestHost3 ip=10.10.5.7') 84 | 85 | response = self.client.send_command('CONFIG OBJECT LIST type=host search=_Test* searchfield=name start=0') 86 | 87 | self.client.send_command('CONFIG OBJECT HOST DELETE name=_TestHost1') 88 | self.client.send_command('CONFIG OBJECT HOST DELETE name=_TestHost2') 89 | self.client.send_command('CONFIG OBJECT HOST DELETE name=_TestHost3') 90 | 91 | self.assertEqual(response.data, expected) 92 | self.assertEqual(response.ret, 100) 93 | 94 | def test_list(self): 95 | """ list format """ 96 | 97 | expected = {'Result': ['network_internals', 'labo_networks']} 98 | 99 | response = self.client.send_command('CONFIG WEBADMIN ACCESS SHOW') 100 | 101 | self.assertEqual(response.data, expected) 102 | self.assertEqual(response.ret, 100) 103 | 104 | def test_xml(self): 105 | """ xml text output """ 106 | 107 | expected = """ 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | """ 116 | 117 | response = self.client.send_command('CONFIG FILTER EXPLICIT index=1 type=filter output=xml') 118 | 119 | self.assertEqual(response.xml, expected) 120 | self.assertEqual(response.ret, 100) 121 | 122 | if __name__ == '__main__': 123 | unittest.main() 124 | -------------------------------------------------------------------------------- /tests/test_proxy.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | import os 4 | import unittest 5 | from stormshield.sns.sslclient import SSLClient 6 | 7 | APPLIANCE = os.getenv('APPLIANCE', "") 8 | PASSWORD = os.getenv('PASSWORD', "") 9 | PROXY = os.getenv('PROXY', "") 10 | 11 | @unittest.skipIf(APPLIANCE=="", "APPLIANCE env var must be set to the ip/hostname of a running SNS appliance") 12 | @unittest.skipIf(PASSWORD=="", "PASSWORD env var must be set to the firewall password") 13 | @unittest.skipIf(PROXY=="", "PROXY env var must be set to proxy url") 14 | class TestProxy(unittest.TestCase): 15 | """ Test proxy option """ 16 | 17 | def test_sslverifyhost(self): 18 | """ Test proxy option """ 19 | 20 | try: 21 | client = SSLClient(host=APPLIANCE, user='admin', password=PASSWORD, sslverifypeer=False, proxy=PROXY) 22 | self.assertTrue(1==1, "SSLClient connects with proxy") 23 | except: 24 | self.fail("SSLClient did not connect") 25 | 26 | response = client.send_command('LIST') 27 | self.assertEqual(response.ret, 100) 28 | 29 | client.disconnect() 30 | -------------------------------------------------------------------------------- /tests/test_utf8.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | 4 | from __future__ import unicode_literals 5 | import os 6 | import sys 7 | import unittest 8 | 9 | from stormshield.sns.sslclient import SSLClient 10 | 11 | APPLIANCE = os.getenv('APPLIANCE', "") 12 | PASSWORD = os.getenv('PASSWORD', "") 13 | SSLVERIFYPEER = os.getenv('SSLVERIFYPEER', "1") == "1"; 14 | 15 | @unittest.skipIf(APPLIANCE=="", "APPLIANCE env var must be set to the ip/hostname of a running SNS appliance") 16 | @unittest.skipIf(PASSWORD=="", "PASSWORD env var must be set to the firewall password") 17 | class TestUtf8(unittest.TestCase): 18 | """ Test INI format """ 19 | 20 | def setUp(self): 21 | self.client = SSLClient(host=APPLIANCE, user='admin', password=PASSWORD, sslverifyhost=False, sslverifypeer=SSLVERIFYPEER) 22 | self.client.send_command('CONFIG OBJECT HOST NEW type=host name=hostutf8 ip=1.2.3.4 comment="comment with utf8 characters éè\u2713"') 23 | 24 | self.maxDiff = 5000 25 | 26 | def tearDown(self): 27 | self.client.send_command('CONFIG OBJECT HOST delete name=hostutf8') 28 | self.client.disconnect() 29 | 30 | def test_utf8(self): 31 | """ send and receive utf-8 content """ 32 | 33 | expected = """101 code=00a01000 msg="Begin" format="section_line" 34 | [Object] 35 | type=host global=0 name=hostutf8 ip=1.2.3.4 modify=1 comment="comment with utf8 characters éè\u2713" type=host 36 | 100 code=00a00100 msg="Ok\"""" 37 | 38 | response = self.client.send_command('CONFIG OBJECT LIST type=host search=hostutf8 start=0') 39 | 40 | self.assertEqual(response.output, expected) 41 | self.assertEqual(response.ret, 100) 42 | 43 | 44 | if __name__ == '__main__': 45 | unittest.main() 46 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | env_list = py3-urllib{1,2} 3 | 4 | [testenv] 5 | description = run unit tests 6 | deps = 7 | pytest 8 | urllib1: urllib3>=1.25.0,<2.0.0 9 | urllib2: urllib3>=2.0.0 10 | commands = 11 | pytest {posargs:tests} 12 | passenv = * --------------------------------------------------------------------------------