├── .github └── workflows │ └── release.yml ├── .gitignore ├── .travis.yml ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── PATENTS ├── README.md ├── examples ├── foobar_config.ext ├── foobar_logger.ext ├── foobar_table.ext ├── many_plugins.ext ├── run.py └── spawn.py ├── osquery.thrift ├── osquery ├── TPipe.py ├── __init__.py ├── config_plugin.py ├── extension_client.py ├── extension_manager.py ├── extensions │ ├── Extension.py │ ├── ExtensionManager.py │ ├── README │ ├── __init__.py │ ├── constants.py │ └── ttypes.py ├── logger_plugin.py ├── management.py ├── plugin.py ├── singleton.py └── table_plugin.py ├── requirements.txt ├── setup.cfg ├── setup.py ├── tests ├── __init__.py ├── test_config_plugin.py ├── test_logger_plugin.py ├── test_plugin.py ├── test_singleton.py └── test_table_plugin.py └── win-requirements.txt /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Upload Python Package to PyPI when a Release is Created 2 | 3 | on: 4 | workflow_dispatch: 5 | release: 6 | types: [created] 7 | 8 | jobs: 9 | build_and_test: 10 | runs-on: ${{ matrix.os }} 11 | strategy: 12 | fail-fast: false 13 | matrix: 14 | os: 15 | - ubuntu-24.04 16 | - macos-latest 17 | - windows-latest 18 | steps: 19 | - uses: actions/checkout@v4 20 | - name: Set up Python 21 | uses: actions/setup-python@v4 22 | with: 23 | python-version: "3.x" 24 | - name: Install dependencies 25 | run: | 26 | python -m pip install --upgrade pip 27 | pip install setuptools wheel build 28 | - name: build 29 | run: | 30 | python setup.py build 31 | - name: package 32 | run: | 33 | python -m build 34 | publish: 35 | needs: build_and_test 36 | runs-on: ubuntu-latest 37 | environment: 38 | name: pypi 39 | url: https://pypi.org/p/osquery 40 | permissions: 41 | id-token: write 42 | steps: 43 | - uses: actions/checkout@v4 44 | - name: Set up Python 45 | uses: actions/setup-python@v4 46 | with: 47 | python-version: "3.x" 48 | - name: Install dependencies 49 | run: | 50 | python -m pip install --upgrade pip 51 | pip install setuptools wheel build 52 | - name: build 53 | run: | 54 | python setup.py build 55 | - name: package 56 | run: | 57 | python -m build 58 | - name: Publish package distributions to PyPI 59 | uses: pypa/gh-action-pypi-publish@release/v1 60 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | *.pyc 3 | osquery.egg-info 4 | dist 5 | .eggs 6 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | sudo: false 3 | cache: 4 | directories: 5 | - $HOME/.cache/pip 6 | python: 7 | - 2.7 8 | - 3.6 9 | - 3.7 10 | - pypy 11 | - pypy3 12 | install: 13 | - pip install -r requirements.txt 14 | - pip install twine readme_renderer 15 | script: 16 | - python setup.py build 17 | - python setup.py test 18 | - python setup.py sdist 19 | - twine check dist/* 20 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Code of Conduct 2 | Facebook has adopted a Code of Conduct that we expect project participants to adhere to. Please [read the full text](https://code.fb.com/codeofconduct) so that you can understand what actions will and will not be tolerated. -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## Contributor License Agreement ("CLA") 2 | 3 | In order to accept your pull request, we need you to submit a CLA. You only need 4 | to do this once to work on any of Facebook's open source projects. 5 | 6 | If you submit a pull request to this project and you have never submitted a 7 | Facebook CLA, a GitHub Bot will comment on the pull request asking you to submit 8 | a CLA. Feel free to wait until then to submit it (it only takes a few seconds) 9 | or complete your CLA proactively, here: 10 | 11 | ## Tracking Features and Getting Help 12 | 13 | If you'd like to implement new features, check out this projects 14 | [GitHub Issues](https://github.com/osquery/osquery-python/issues). Community 15 | contributions are welcomed and encouraged. 16 | 17 | If you want to suggest a feature, you should create a GitHub issue outlining 18 | your feature request. 19 | 20 | If you need help, have questions, or want to participate in a forum-style 21 | conversation with the core developers and users of this projects, create an 22 | issue. 23 | 24 | Generally, use GitHub Issues for everything. 25 | 26 | ## Code Formatting 27 | 28 | ### Lint 29 | 30 | #### Running the linter 31 | 32 | With the exception of the thrift autogenerated code, all code must be pylint 33 | compliant. After installing `pylint` and/or running `pip install -r requirements.txt` 34 | from the root of this repository, run the following to lint the code: 35 | 36 | ``` 37 | python setup.py lint 38 | ``` 39 | 40 | Once you run that, a bunch of output will come out. The very last thing that 41 | lint should say is: 42 | 43 | ``` 44 | Global evaluation 45 | ----------------- 46 | Your code has been rated at 10.00/10 (previous run: 10.00/10, +0.00) 47 | ``` 48 | 49 | If the code rating has dropped below `10.00/10`, please fix the code so that it 50 | is compliant. 51 | 52 | #### Adding ignores 53 | 54 | Under the hood, `python setup.py lint` just runs `pylint` with a few options 55 | on the relevant parts of the codebase. Sometime, `pylint` is unreasonable. For 56 | that reason, you may ignore specific messages on a file-by-file basis by 57 | adding text like `# pylint: disable=too-few-public-methods` to the top of your 58 | file, right below the license docstring, before any imports. 59 | 60 | If you'd like to edit the arguments that `pylint` itself is ran with, edit the 61 | `PylintCommand` class' `_pylint_arguments` property. 62 | 63 | Ignoring lint warnings is generally not something you should default to doing. 64 | With great power comes great responsibility. 65 | 66 | ### Style 67 | 68 | Generally, be consistent. We don't have a good auto-formatter configured, so 69 | ensure that the style of you code matches existing style. If you feel strongly 70 | that a given style in use throughout the codebase is egregious, feel free to 71 | submit a pull request with your update and we can discuss it's merits. 72 | 73 | We adhere to [PEP 8](https://www.python.org/dev/peps/pep-0008/) for style, as 74 | well as [PEP 257](https://www.python.org/dev/peps/pep-0257/) for docstring 75 | conventions. If you notice that the codebase is not compliant with those 76 | specifications, please file an issue and/or submit a pull request with a patch. 77 | 78 | ### Testing 79 | 80 | To run the tests, run the following from the root of the repository: 81 | 82 | ``` 83 | # install dependencies 84 | pip install -r requirements.txt 85 | 86 | # build the module 87 | python setup.py build 88 | 89 | # test the module 90 | python setup.py test 91 | ``` 92 | 93 | ## Packaging 94 | 95 | We build the osquery package as a [wheel](https://pypi.python.org/pypi/wheel). 96 | To build the wheel, run the following from the root of this repository: 97 | 98 | 99 | ``` 100 | # install dependencies 101 | pip install -r requirements.txt 102 | 103 | # build package 104 | python setup.py bdist_wheel 105 | 106 | # upload the package 107 | twine upload dist/* 108 | ``` 109 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD License 2 | 3 | For osquery-python software 4 | 5 | Copyright (c) 2014, Facebook, Inc. All rights reserved. 6 | 7 | Redistribution and use in source and binary forms, with or without modification, 8 | are permitted provided that the following conditions are met: 9 | 10 | * Redistributions of source code must retain the above copyright notice, this 11 | list of conditions and the following disclaimer. 12 | 13 | * Redistributions in binary form must reproduce the above copyright notice, 14 | this list of conditions and the following disclaimer in the documentation 15 | and/or other materials provided with the distribution. 16 | 17 | * Neither the name Facebook nor the names of its contributors may be used to 18 | endorse or promote products derived from this software without specific 19 | prior written permission. 20 | 21 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 22 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 23 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 24 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 25 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 26 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 27 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 28 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 30 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | -------------------------------------------------------------------------------- /PATENTS: -------------------------------------------------------------------------------- 1 | Additional Grant of Patent Rights Version 2 2 | 3 | "Software" means the osquery-python software distributed by Facebook, Inc. 4 | 5 | Facebook, Inc. ("Facebook") hereby grants to each recipient of the Software 6 | ("you") a perpetual, worldwide, royalty-free, non-exclusive, irrevocable 7 | (subject to the termination provision below) license under any Necessary 8 | Claims, to make, have made, use, sell, offer to sell, import, and otherwise 9 | transfer the Software. For avoidance of doubt, no license is granted under 10 | Facebook’s rights in any patent claims that are infringed by (i) modifications 11 | to the Software made by you or any third party or (ii) the Software in 12 | combination with any software or other technology. 13 | 14 | The license granted hereunder will terminate, automatically and without notice, 15 | if you (or any of your subsidiaries, corporate affiliates or agents) initiate 16 | directly or indirectly, or take a direct financial interest in, any Patent 17 | Assertion: (i) against Facebook or any of its subsidiaries or corporate 18 | affiliates, (ii) against any party if such Patent Assertion arises in whole or 19 | in part from any software, technology, product or service of Facebook or any of 20 | its subsidiaries or corporate affiliates, or (iii) against any party relating 21 | to the Software. Notwithstanding the foregoing, if Facebook or any of its 22 | subsidiaries or corporate affiliates files a lawsuit alleging patent 23 | infringement against you in the first instance, and you respond by filing a 24 | patent infringement counterclaim in that lawsuit against that party that is 25 | unrelated to the Software, the license granted hereunder will not terminate 26 | under section (i) of this paragraph due to such counterclaim. 27 | 28 | A "Necessary Claim" is a claim of a patent owned by Facebook that is 29 | necessarily infringed by the Software standing alone. 30 | 31 | A "Patent Assertion" is any lawsuit or other action alleging direct, indirect, 32 | or contributory infringement or inducement to infringe any patent, including a 33 | cross-claim or counterclaim. 34 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## osquery-python 2 | 3 | ![osquery-python-logo](https://i.imgur.com/9Vy2GFx.png) 4 | 5 | [osquery](https://github.com/facebook/osquery) exposes an operating system as a high-performance relational database. This allows you to write SQL-based queries to explore operating system data. With osquery, SQL tables represent abstract concepts such as running processes, loaded kernel modules, open network connections, browser plugins, hardware events or file hashes. 6 | 7 | If you're interested in learning more about osquery, visit the [GitHub project](https://github.com/facebook/osquery), the [website](https://osquery.io), and the [users guide](https://osquery.readthedocs.org/). 8 | 9 | ### What is osquery-python? 10 | 11 | [![Build Status](https://travis-ci.org/osquery/osquery-python.svg?branch=master)](https://travis-ci.org/osquery/osquery-python) 12 | 13 | In osquery, SQL tables, configuration retrieval, log handling, etc are implemented via a simple, robust plugin and extensions API. This project contains the official Python bindings for creating osquery extensions in Python. Consider the following example: 14 | 15 | ```python 16 | #!/usr/bin/env python 17 | 18 | import osquery 19 | 20 | @osquery.register_plugin 21 | class MyTablePlugin(osquery.TablePlugin): 22 | def name(self): 23 | return "foobar" 24 | 25 | def columns(self): 26 | return [ 27 | osquery.TableColumn(name="foo", type=osquery.STRING), 28 | osquery.TableColumn(name="baz", type=osquery.STRING), 29 | ] 30 | 31 | def generate(self, context): 32 | query_data = [] 33 | 34 | for _ in range(2): 35 | row = {} 36 | row["foo"] = "bar" 37 | row["baz"] = "baz" 38 | query_data.append(row) 39 | 40 | return query_data 41 | 42 | if __name__ == "__main__": 43 | osquery.start_extension(name="my_awesome_extension", version="1.0.0") 44 | ``` 45 | 46 | To test this code start an osquery shell: 47 | 48 | ``` 49 | osqueryi --nodisable_extensions 50 | osquery> select value from osquery_flags where name = 'extensions_socket'; 51 | +-----------------------------------+ 52 | | value | 53 | +-----------------------------------+ 54 | | /Users/USERNAME/.osquery/shell.em | 55 | +-----------------------------------+ 56 | ``` 57 | 58 | Then start the Python extension: 59 | 60 | ``` 61 | python ./my_table_plugin.py --socket /Users/USERNAME/.osquery/shell.em 62 | ``` 63 | 64 | Alternatively, you can also autoload your extension when starting an osquery shell: 65 | 66 | ``` 67 | osqueryi --extension path_to_my_table_plugin.py 68 | ``` 69 | 70 | This will register a table called "foobar". As you can see, the table will return two rows: 71 | 72 | ``` 73 | osquery> select * from foobar; 74 | +-----+-----+ 75 | | foo | baz | 76 | +-----+-----+ 77 | | bar | baz | 78 | | bar | baz | 79 | +-----+-----+ 80 | osquery> 81 | ``` 82 | 83 | This is obviously a contrived example, but it's easy to imagine the possibilities. 84 | 85 | Using the instructions found on the [wiki](https://osquery.readthedocs.org/en/latest/development/osquery-sdk/), you can easily deploy your extension with an existing osquery deployment. 86 | 87 | Extensions are the core way that you can extend and customize osquery. At Facebook, we use extensions extensively to implement many plugins that take advantage of internal APIs and tools. 88 | 89 | ### Execute queries in Python 90 | 91 | The same Thrift bindings can be used to create a Python client for the `osqueryd` or `osqueryi`'s extension socket. There are helper classes provided that spawn an ephemeral osquery process for consecutive or long running client instances. 92 | 93 | ```python 94 | import osquery 95 | 96 | if __name__ == "__main__": 97 | # Spawn an osquery process using an ephemeral extension socket. 98 | instance = osquery.SpawnInstance() 99 | instance.open() # This may raise an exception 100 | 101 | # Issues queries and call osquery Thrift APIs. 102 | instance.client.query("select timestamp from time") 103 | ``` 104 | 105 | ### Connect to an existing socket 106 | 107 | In the example above the `SpawnInstance()` method is used to fork and configure an osquery instance. We can use similar APIs to connect to the Thrift socket of an existing osquery instance. Remember, normal UNIX permissions apply to the Thrift socket. 108 | 109 | Imagine if you started `osqueryd`: 110 | ```sh 111 | $ osqueryd --ephemeral --disable_logging --disable_database \ 112 | --extensions_socket /home/you/.osquery/osqueryd.sock & 113 | ``` 114 | 115 | Then use the Python bindings: 116 | ```python 117 | import osquery 118 | 119 | if __name__ == "__main__": 120 | # You must know the Thrift socket path 121 | # For an installed and running system osqueryd, this is: 122 | # Linux and macOS: /var/osquery/osquery.em 123 | # FreeBSD: /var/run/osquery.em 124 | # Windows: \\.\pipe\osquery.em 125 | instance = osquery.ExtensionClient('/home/you/.osquery/osqueryd.sock') 126 | instance.open() # This may raise an exception 127 | 128 | # Issue queries and call osquery Thrift APIs. 129 | client = instance.extension_client() 130 | client.query('select timestamp from time') 131 | ``` 132 | 133 | ### Install 134 | 135 | To install from PyPi, run the following: 136 | 137 | ``` 138 | pip install osquery 139 | ``` 140 | 141 | Alternatively, to install from this repo, run the following: 142 | 143 | ``` 144 | python setup.py build 145 | python setup.py install 146 | ``` 147 | 148 | 149 | ### Development 150 | 151 | See [CONTRIBUTING.md](https://github.com/osquery/osquery-python/blob/master/CONTRIBUTING.md) and the [osquery wiki](https://osquery.readthedocs.org) for development information. 152 | 153 | ### How To Release 154 | 155 | 1. Pick a version number 156 | 2. Update `osquery/__init__.py` to match 157 | 3. Use the GitHub release 158 | 4. Make sure the GitHub Action ran 159 | 160 | ### Vulnerabilities 161 | 162 | Facebook has a [bug bounty](https://www.facebook.com/whitehat/) program that includes osquery. If you find a security vulnerability in osquery, please submit it via the process outlined on that page and do not file a public issue. For more information on finding vulnerabilities in osquery, see a recent blog post about [bug-hunting osquery](https://www.facebook.com/notes/facebook-bug-bounty/bug-hunting-osquery/954850014529225). 163 | -------------------------------------------------------------------------------- /examples/foobar_config.ext: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """This source code is licensed under the BSD-style license found in the 3 | LICENSE file in the root directory of this source tree. An additional grant 4 | of patent rights can be found in the PATENTS file in the same directory. 5 | """ 6 | 7 | import osquery 8 | import json 9 | 10 | @osquery.register_plugin 11 | class FoobarConfigPlugin(osquery.ConfigPlugin): 12 | """Example config plugin""" 13 | def name(self): 14 | return "foobar" 15 | 16 | def content(self): 17 | return [ 18 | { 19 | "source_one": json.dumps({ 20 | "schedule": { 21 | "time_1": { 22 | "query": "select * from time", 23 | "interval": 1, 24 | }, 25 | }, 26 | }), 27 | "source_two": json.dumps({ 28 | "schedule": { 29 | "time_2": { 30 | "query": "select * from time", 31 | "interval": 2, 32 | }, 33 | }, 34 | }), 35 | } 36 | ] 37 | 38 | if __name__ == "__main__": 39 | osquery.start_extension( 40 | name="foobar_config", 41 | version="1.0.0",) 42 | -------------------------------------------------------------------------------- /examples/foobar_logger.ext: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """This source code is licensed under the BSD-style license found in the 3 | LICENSE file in the root directory of this source tree. An additional grant 4 | of patent rights can be found in the PATENTS file in the same directory. 5 | """ 6 | 7 | import osquery 8 | 9 | @osquery.register_plugin 10 | class FoobarLoggerPlugin(osquery.LoggerPlugin): 11 | """Example logger plugin""" 12 | def name(self): 13 | return "foobar_logger" 14 | 15 | def log_string(self, value): 16 | with open("/tmp/osqueryd.results.log", "a") as file_handle: 17 | file_handle.write(value) 18 | return osquery.extensions.ttypes.ExtensionStatus(code=0, message="OK") 19 | 20 | if __name__ == "__main__": 21 | osquery.start_extension( 22 | name="foobar_logger", 23 | version="1.0.0",) 24 | -------------------------------------------------------------------------------- /examples/foobar_table.ext: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """This source code is licensed under the BSD-style license found in the 3 | LICENSE file in the root directory of this source tree. An additional grant 4 | of patent rights can be found in the PATENTS file in the same directory. 5 | """ 6 | 7 | import osquery 8 | 9 | @osquery.register_plugin 10 | class FoobarTablePlugin(osquery.TablePlugin): 11 | """Example table plugin""" 12 | def name(self): 13 | return "foobar" 14 | 15 | def columns(self): 16 | return [ 17 | osquery.TableColumn(name="foo", type=osquery.STRING), 18 | osquery.TableColumn(name="baz", type=osquery.STRING), 19 | ] 20 | 21 | def generate(self, context): 22 | query_data = [] 23 | 24 | for _ in range(2): 25 | row = {} 26 | row["foo"] = "bar" 27 | row["baz"] = "baz" 28 | query_data.append(row) 29 | 30 | return query_data 31 | 32 | if __name__ == "__main__": 33 | osquery.start_extension( 34 | name="foobar_table", 35 | version="1.0.0",) 36 | -------------------------------------------------------------------------------- /examples/many_plugins.ext: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """This source code is licensed under the BSD-style license found in the 3 | LICENSE file in the root directory of this source tree. An additional grant 4 | of patent rights can be found in the PATENTS file in the same directory. 5 | """"" 6 | 7 | import osquery 8 | import json 9 | 10 | @osquery.register_plugin 11 | class TestConfigPlugin(osquery.ConfigPlugin): 12 | """Example config plugin""" 13 | def name(self): 14 | return "test_config" 15 | 16 | def content(self): 17 | return [ 18 | { 19 | "source_one": json.dumps({ 20 | "schedule": { 21 | "time_1": { 22 | "query": "select * from time", 23 | "interval": 1, 24 | }, 25 | }, 26 | }), 27 | "source_two": json.dumps({ 28 | "schedule": { 29 | "time_2": { 30 | "query": "select * from foobar", 31 | "interval": 2, 32 | }, 33 | }, 34 | }), 35 | } 36 | ] 37 | 38 | @osquery.register_plugin 39 | class TestLoggerPlugin(osquery.LoggerPlugin): 40 | """Example logger plugin""" 41 | def name(self): 42 | return "test_logger" 43 | 44 | def log_string(self, value): 45 | with open("/tmp/osqueryd.results.log", "a") as file_handle: 46 | file_handle.write(value) 47 | return osquery.extensions.ttypes.ExtensionStatus(code=0, message="OK") 48 | 49 | @osquery.register_plugin 50 | class FoobarTablePlugin(osquery.TablePlugin): 51 | """Example table plugin""" 52 | def name(self): 53 | return "foobar" 54 | 55 | def columns(self): 56 | return [ 57 | osquery.TableColumn(name="foo", type=osquery.STRING), 58 | osquery.TableColumn(name="baz", type=osquery.STRING), 59 | ] 60 | 61 | def generate(self, context): 62 | query_data = [] 63 | 64 | for _ in range(2): 65 | row = {} 66 | row["foo"] = "bar" 67 | row["baz"] = "baz" 68 | query_data.append(row) 69 | 70 | return query_data 71 | 72 | if __name__ == "__main__": 73 | osquery.start_extension( 74 | name="many_plugins", 75 | version="1.0.0",) 76 | -------------------------------------------------------------------------------- /examples/run.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """ 3 | simple script which runs a query from the command-line 4 | """ 5 | 6 | import sys 7 | import osquery 8 | 9 | if __name__ == "__main__": 10 | if len(sys.argv) != 2: 11 | print("Usage: %s \"query\"" % sys.argv[0]) 12 | sys.exit(1) 13 | CLIENT = osquery.ExtensionClient() 14 | CLIENT.open() 15 | RESULTS = CLIENT.extension_client().query(sys.argv[1]) 16 | if RESULTS.status.code != 0: 17 | print("Error running the query: %s" % RESULTS.status.message) 18 | sys.exit(1) 19 | 20 | for row in RESULTS.response: 21 | print("=" * 80) 22 | for key, val in row.iteritems(): 23 | print("%s => %s" % (key, val)) 24 | if len(RESULTS.response) > 0: 25 | print("=" * 80) 26 | -------------------------------------------------------------------------------- /examples/spawn.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """ 3 | simple script which runs a query from the command-line by spawning osqueryd 4 | """ 5 | 6 | import sys 7 | import osquery 8 | 9 | if __name__ == "__main__": 10 | if len(sys.argv) != 2: 11 | print("Usage: %s \"query\"" % sys.argv[0]) 12 | sys.exit(1) 13 | INSTANCE = osquery.SpawnInstance() 14 | INSTANCE.open() 15 | RESULTS = INSTANCE.client.query(sys.argv[1]) 16 | if RESULTS.status.code != 0: 17 | print("Error running the query: %s" % RESULTS.status.message) 18 | sys.exit(1) 19 | 20 | for row in RESULTS.response: 21 | print("=" * 80) 22 | for key, val in row.iteritems(): 23 | print("%s => %s" % (key, val)) 24 | if len(RESULTS.response) > 0: 25 | print("=" * 80) 26 | -------------------------------------------------------------------------------- /osquery.thrift: -------------------------------------------------------------------------------- 1 | namespace cpp osquery.extensions 2 | namespace py osquery.extensions 3 | 4 | /// Registry operations use a registry name, plugin name, request/response. 5 | typedef map ExtensionPluginRequest 6 | typedef list> ExtensionPluginResponse 7 | 8 | /// Extensions should request osquery options to set active registries and 9 | /// bootstrap any config/logger plugins. 10 | struct InternalOptionInfo { 11 | 1:string value, 12 | 2:string default_value, 13 | 3:string type, 14 | } 15 | 16 | /// Each option (CLI flag) has a unique name. 17 | typedef map InternalOptionList 18 | 19 | /// When communicating extension metadata, use a thrift-internal structure. 20 | struct InternalExtensionInfo { 21 | 1:string name, 22 | 2:string version, 23 | 3:string sdk_version, 24 | 4:string min_sdk_version, 25 | } 26 | 27 | /// Unique ID for each extension. 28 | typedef i64 ExtensionRouteUUID 29 | /// A map from each plugin name to its optional route information. 30 | typedef map ExtensionRouteTable 31 | /// A map from each registry name. 32 | typedef map ExtensionRegistry 33 | /// A map from each extension's unique ID to its map of registries. 34 | typedef map InternalExtensionList 35 | 36 | enum ExtensionCode { 37 | EXT_SUCCESS = 0, 38 | EXT_FAILED = 1, 39 | EXT_FATAL = 2, 40 | } 41 | 42 | /// Most communication uses the Status return type. 43 | struct ExtensionStatus { 44 | 1:i32 code, 45 | 2:string message, 46 | /// Add a thrift Status parameter identifying the request/response. 47 | 3:ExtensionRouteUUID uuid, 48 | } 49 | 50 | struct ExtensionResponse { 51 | 1:ExtensionStatus status, 52 | 2:ExtensionPluginResponse response, 53 | } 54 | 55 | exception ExtensionException { 56 | 1:i32 code, 57 | 2:string message, 58 | 3:ExtensionRouteUUID uuid, 59 | } 60 | 61 | service Extension { 62 | /// Ping to/from an extension and extension manager for metadata. 63 | ExtensionStatus ping(), 64 | /// Call an extension (or core) registry plugin. 65 | ExtensionResponse call( 66 | /// The registry name (e.g., config, logger, table, etc). 67 | 1:string registry, 68 | /// The registry item name (plugin name). 69 | 2:string item, 70 | /// The thrift-equivilent of an osquery::PluginRequest. 71 | 3:ExtensionPluginRequest request), 72 | /// Request that an extension shutdown (does not apply to managers). 73 | void shutdown(), 74 | } 75 | 76 | /// The extension manager is run by the osquery core process. 77 | service ExtensionManager extends Extension { 78 | /// Return the list of active registered extensions. 79 | InternalExtensionList extensions(), 80 | /// Return the list of bootstrap or configuration options. 81 | InternalOptionList options(), 82 | /// The API endpoint used by an extension to register its plugins. 83 | ExtensionStatus registerExtension( 84 | 1:InternalExtensionInfo info, 85 | 2:ExtensionRegistry registry), 86 | ExtensionStatus deregisterExtension( 87 | 1:ExtensionRouteUUID uuid, 88 | ), 89 | /// Allow an extension to query using an SQL string. 90 | ExtensionResponse query( 91 | 1:string sql, 92 | ), 93 | /// Allow an extension to introspect into SQL used in a parsed query. 94 | ExtensionResponse getQueryColumns( 95 | 1:string sql, 96 | ), 97 | } 98 | -------------------------------------------------------------------------------- /osquery/TPipe.py: -------------------------------------------------------------------------------- 1 | """This source code is licensed under the BSD-style license found in the 2 | LICENSE file in the root directory of this source tree. An additional grant 3 | of patent rights can be found in the PATENTS file in the same directory. 4 | """ 5 | 6 | from __future__ import absolute_import 7 | from __future__ import division 8 | from __future__ import print_function 9 | from __future__ import unicode_literals 10 | 11 | import win32event 12 | import win32pipe 13 | import win32file 14 | import win32api 15 | import winerror 16 | import pywintypes 17 | 18 | from thrift.transport.TTransport import TTransportBase 19 | from thrift.transport.TTransport import TTransportException 20 | from thrift.transport.TTransport import TServerTransportBase 21 | 22 | 23 | class TPipeBase(TTransportBase): 24 | """Base class for Windows TPipe transport""" 25 | 26 | def __init__(self): 27 | self._handle = None 28 | 29 | def close(self): 30 | """ 31 | Generic close method, as both server and client rely on closing pipes 32 | in the same way 33 | """ 34 | if self._handle is not None: 35 | win32pipe.DisconnectNamedPipe(self._handle) 36 | self._handle = None 37 | 38 | 39 | class TPipe(TPipeBase): 40 | """Client for communicating over Named Pipes""" 41 | 42 | def __init__(self, pipe_name, timeout=5, max_attempts=5): 43 | """ 44 | Initialize a TPipe client 45 | 46 | @param pipe_name(string) The path of the pipe to connect to. 47 | @param timeout(int) The time to wait for a named pipe connection. 48 | @param max_attempts(int) Maximum number of connection attempts. 49 | """ 50 | self._handle = None 51 | self._pipe_name = pipe_name 52 | self._timeout = timeout 53 | self._max_conn_attempts = max_attempts 54 | 55 | def set_handle(self, h): 56 | self._handle = h 57 | 58 | def is_open(self): 59 | return self._handle is not None 60 | 61 | def open(self): 62 | if self.is_open(): 63 | raise TTransportException(TTransportException.ALREADY_OPEN) 64 | 65 | h = None 66 | conns = 0 67 | while conns < self._max_conn_attempts: 68 | try: 69 | h = win32file.CreateFile( 70 | self._pipe_name, 71 | win32file.GENERIC_READ | win32file.GENERIC_WRITE, 0, None, 72 | win32file.OPEN_EXISTING, win32file.FILE_FLAG_OVERLAPPED, 73 | None) 74 | except pywintypes.error as e: 75 | if e.winerror != winerror.ERROR_PIPE_BUSY: 76 | raise TTransportException( 77 | TTransportException.NOT_OPEN, 78 | 'Failed to open connection to pipe: {}'.format(e)) 79 | 80 | # Successfully connected, break out. 81 | if h is not None and h.handle != winerror.ERROR_INVALID_HANDLE: 82 | self._handle = h 83 | return 84 | 85 | # Wait for the connection to the pipe 86 | try: 87 | win32pipe.WaitNamedPipe(self._pipe_name, self._timeout) 88 | except Exception as e: 89 | if e.args[0] not in (winerror.ERROR_SEM_TIMEOUT, 90 | winerror.ERROR_PIPE_BUSY): 91 | raise TTransportException( 92 | type=TTransportException.UNKNOWN, 93 | message='Client failed to connect to server with {}'. 94 | format(e.args[0])) 95 | conns += 1 96 | 97 | raise TTransportException( 98 | type=TTransportException.UNKNOWN, 99 | message='Client exceeded max connection attempts') 100 | 101 | def read(self, sz): 102 | if not self.is_open(): 103 | raise TTransportException( 104 | type=TTransportException.NOT_OPEN, 105 | message='Called read on non-open pipe') 106 | buff = None 107 | err = None 108 | try: 109 | (err, buff) = win32file.ReadFile(self._handle, sz, None) 110 | except Exception as e: 111 | raise TTransportException( 112 | type=TTransportException.UNKNOWN, message='TPipe read failed') 113 | 114 | if err: 115 | raise TTransportException( 116 | type=TTransportException.UNKNOWN, 117 | message='TPipe read failed with GLE={}'.format(err)) 118 | if len(buff) == 0: 119 | raise TTransportException( 120 | type=TTransportException.END_OF_FILE, 121 | message='TPipe read 0 bytes') 122 | return buff 123 | 124 | def write(self, buff): 125 | if not self.is_open(): 126 | raise TTransportException( 127 | type=TTransportException.NOT_OPEN, 128 | message='Called read on non-open pipe') 129 | 130 | bytesWritten = None 131 | try: 132 | (_, bytesWritten) = win32file.WriteFile(self._handle, buff, None) 133 | except Exception as e: 134 | raise TTransportException( 135 | type=TTransportException.UNKNOWN, 136 | message='Failed to write to named pipe: ' + str(e)) 137 | 138 | if bytesWritten != len(buff): 139 | raise TTransportException( 140 | type=TTransportException.UNKNOWN, 141 | message='Failed to write complete buffer to named pipe') 142 | 143 | def flush(self): 144 | if self.is_open(): 145 | win32file.FlushFileBuffers(self._handle) 146 | 147 | 148 | class TPipeServer(TPipeBase, TServerTransportBase): 149 | """Named pipe implementation of TServerTransport""" 150 | 151 | def __init__(self, 152 | pipe_name, 153 | buff_size=4096, 154 | max_conns=255, 155 | max_conn_attempts=5): 156 | """ 157 | Initialize a TPipe client 158 | 159 | @param pipe_name(string) The path of the pipe to connect to. 160 | @param buff_size(int) The size of read/write buffers to use. 161 | @param max_conns(int) Maximum number of simultaneous connections. 162 | @param max_conn_attempts(int) Maximum number of connection attempts 163 | """ 164 | self._pipe_name = pipe_name 165 | self._buff_size = buff_size 166 | self._max_conns = max_conns 167 | self._max_conn_attempts = max_conn_attempts 168 | self._handle = None 169 | 170 | # Overlapped event for Windows Async IO 171 | self._overlapped = pywintypes.OVERLAPPED() 172 | self._overlapped.hEvent = win32event.CreateEvent(None, 0, 0, None) 173 | 174 | # Create a new named pipe if one doesn't already exist 175 | def listen(self): 176 | if self._handle is None: 177 | self.create_named_pipe() 178 | 179 | def accept(self): 180 | if self._handle is not None: 181 | self.create_named_pipe() 182 | 183 | self.initiate_named_connect() 184 | result = TPipe(self._pipe_name) 185 | result.set_handle(self._handle) 186 | 187 | return result 188 | 189 | def create_named_pipe(self): 190 | openMode = win32pipe.PIPE_ACCESS_DUPLEX | win32file.FILE_FLAG_OVERLAPPED 191 | pipeMode = win32pipe.PIPE_TYPE_BYTE | win32pipe.PIPE_READMODE_BYTE 192 | 193 | saAttr = pywintypes.SECURITY_ATTRIBUTES() 194 | saAttr.SetSecurityDescriptorDacl(1, None, 0) 195 | 196 | self._handle = win32pipe.CreateNamedPipe( 197 | self._pipe_name, openMode, pipeMode, 198 | win32pipe.PIPE_UNLIMITED_INSTANCES, self._buff_size, 199 | self._buff_size, win32pipe.NMPWAIT_WAIT_FOREVER, saAttr) 200 | 201 | err = win32api.GetLastError() 202 | if self._handle.handle == winerror.ERROR_INVALID_HANDLE: 203 | raise TTransportException( 204 | type=TTransportException.NOT_OPEN, 205 | message='Tcreate_named_pipe failed: {}'.format(err)) 206 | return True 207 | 208 | def initiate_named_connect(self): 209 | conns = 0 210 | while conns < self._max_conn_attempts: 211 | try: 212 | ret = win32pipe.ConnectNamedPipe(self._handle, 213 | self._overlapped) 214 | except Exception as e: 215 | raise TTransportException( 216 | type=TTransportException.NOT_OPEN, 217 | message='TConnectNamedPipe failed: {}'.format(str(e))) 218 | 219 | # Successfully connected, break out. 220 | if ret == winerror.ERROR_PIPE_CONNECTED: 221 | win32event.SetEvent(self._overlapped.hEvent) 222 | break 223 | 224 | def close(self): 225 | """ 226 | The server must ensure to disconnect so that subsequent reconnect 227 | attempts are successful 228 | """ 229 | if self._handle is not None: 230 | super(TPipe, self).close() 231 | win32file.CloseHandle(self._handle) 232 | self._handle = None 233 | -------------------------------------------------------------------------------- /osquery/__init__.py: -------------------------------------------------------------------------------- 1 | """This source code is licensed under the BSD-style license found in the 2 | LICENSE file in the root directory of this source tree. An additional grant 3 | of patent rights can be found in the PATENTS file in the same directory. 4 | """ 5 | 6 | __title__ = "osquery" 7 | __version__ = "3.1.1" 8 | __author__ = "osquery authors" 9 | __license__ = "BSD" 10 | __copyright__ = "Copyright 2015-present, The osquery authors" 11 | __url__ = "https://osquery.io" 12 | 13 | __all__ = [ 14 | "BasePlugin", 15 | "ConfigPlugin", 16 | "DEFAULT_SOCKET_PATH", 17 | "deregister_extension", 18 | "ExtensionClient", 19 | "ExtensionManager", 20 | "INTEGER", 21 | "LoggerPlugin", 22 | "parse_cli_params", 23 | "register_plugin", 24 | "start_extension", 25 | "Singleton", 26 | "SpawnInstance", 27 | "STRING", 28 | "TableColumn", 29 | "TablePlugin", 30 | "WINDOWS_PLATFORM", 31 | ] 32 | 33 | from osquery.config_plugin import ConfigPlugin 34 | from osquery.extension_client import DEFAULT_SOCKET_PATH, ExtensionClient, \ 35 | WINDOWS_PLATFORM 36 | from osquery.extension_manager import ExtensionManager 37 | from osquery.logger_plugin import LoggerPlugin 38 | from osquery.management import SpawnInstance, \ 39 | deregister_extension, parse_cli_params, register_plugin, start_extension 40 | from osquery.plugin import BasePlugin 41 | from osquery.singleton import Singleton 42 | from osquery.table_plugin import INTEGER, STRING, TableColumn, TablePlugin 43 | -------------------------------------------------------------------------------- /osquery/config_plugin.py: -------------------------------------------------------------------------------- 1 | """This source code is licensed under the BSD-style license found in the 2 | LICENSE file in the root directory of this source tree. An additional grant 3 | of patent rights can be found in the PATENTS file in the same directory. 4 | """ 5 | 6 | # pylint: disable=no-self-use 7 | 8 | from __future__ import absolute_import 9 | from __future__ import division 10 | from __future__ import print_function 11 | from __future__ import unicode_literals 12 | 13 | from abc import ABCMeta, abstractmethod 14 | from future.utils import with_metaclass 15 | 16 | from osquery.extensions.ttypes import ExtensionResponse, ExtensionStatus 17 | from osquery.plugin import BasePlugin 18 | 19 | class ConfigPlugin(with_metaclass(ABCMeta, BasePlugin)): 20 | """All config plugins should inherit from ConfigPlugin""" 21 | 22 | def call(self, context): 23 | """Internal routing for this plugin type. 24 | 25 | Do not override this method. 26 | """ 27 | if "action" in context: 28 | if context["action"] == "genConfig": 29 | return ExtensionResponse(status=ExtensionStatus(code=0, 30 | message="OK",), 31 | response=self.content(),) 32 | 33 | message = "Not a valid config plugin action" 34 | return ExtensionResponse(status=ExtensionStatus(code=1, 35 | message=message,), 36 | response=[],) 37 | 38 | def registry_name(self): 39 | """The name of the registry type for config plugins. 40 | 41 | Do not override this method. 42 | """ 43 | return "config" 44 | 45 | @abstractmethod 46 | def content(self): 47 | """The implementation of your config plugin. 48 | 49 | This should return a dictionary of the following format: 50 | 51 | [ 52 | { 53 | "source_name_1": serialized_json_config_string_1, 54 | "source_name_2": serialized_json_config_string_2, 55 | } 56 | ] 57 | 58 | Consider the following full example of the content method: 59 | 60 | @osquery.register_plugin 61 | class TestConfigPlugin(osquery.ConfigPlugin): 62 | def name(self): 63 | return "test_config" 64 | 65 | def content(self): 66 | return [ 67 | { 68 | "source_one": json.dumps({ 69 | "schedule": { 70 | "time_1": { 71 | "query": "select * from time", 72 | "interval": 1, 73 | }, 74 | }, 75 | }), 76 | "source_two": json.dumps({ 77 | "schedule": { 78 | "time_2": { 79 | "query": "select * from time", 80 | "interval": 2, 81 | }, 82 | }, 83 | }), 84 | } 85 | ] 86 | 87 | This must be implemented by your plugin. 88 | """ 89 | raise NotImplementedError 90 | -------------------------------------------------------------------------------- /osquery/extension_client.py: -------------------------------------------------------------------------------- 1 | """This source code is licensed under the BSD-style license found in the 2 | LICENSE file in the root directory of this source tree. An additional grant 3 | of patent rights can be found in the PATENTS file in the same directory. 4 | """ 5 | 6 | from __future__ import absolute_import 7 | from __future__ import division 8 | from __future__ import print_function 9 | from __future__ import unicode_literals 10 | 11 | import time 12 | import sys 13 | 14 | from thrift.protocol import TBinaryProtocol 15 | from thrift.transport import TSocket 16 | from thrift.transport import TTransport 17 | 18 | from osquery.extensions.ExtensionManager import Client 19 | 20 | WINDOWS_PLATFORM = "win32" 21 | 22 | """The default path for osqueryd sockets""" 23 | if sys.platform == WINDOWS_PLATFORM: 24 | # We bootleg our own version of Windows pipe coms 25 | from osquery.TPipe import TPipe 26 | DEFAULT_SOCKET_PATH = r'\\.\pipe\osquery.em' 27 | else: 28 | DEFAULT_SOCKET_PATH = "/var/osquery/osquery.em" 29 | 30 | class ExtensionClient(object): 31 | """A client for connecting to an existing extension manager socket""" 32 | 33 | _protocol = None 34 | _transport = None 35 | 36 | def __init__(self, path=DEFAULT_SOCKET_PATH, uuid=None): 37 | """ 38 | Keyword arguments: 39 | path -- the path of the extension socket to connect to 40 | uuid -- the additional UUID to use when constructing the socket path 41 | """ 42 | self.path = path 43 | sock = None 44 | if sys.platform == WINDOWS_PLATFORM: 45 | sock = TPipe(pipe_name=self.path) 46 | else: 47 | self.path += ".{}".format(uuid) if uuid else "" 48 | sock = TSocket.TSocket(unix_socket=self.path) 49 | self._transport = TTransport.TBufferedTransport(sock) 50 | self._protocol = TBinaryProtocol.TBinaryProtocol(self._transport) 51 | 52 | def close(self): 53 | """Close the extension client connection""" 54 | if self._transport: 55 | self._transport.close() 56 | 57 | def open(self, timeout=1, interval=0.2): 58 | """Attempt to open the UNIX domain socket""" 59 | delay = 0 60 | while delay < timeout: 61 | try: 62 | self._transport.open() 63 | return True 64 | except TTransport.TTransportException: 65 | pass 66 | delay += interval 67 | time.sleep(interval) 68 | return False 69 | 70 | def extension_manager_client(self): 71 | """Return an extension manager (osquery core) client.""" 72 | return Client(self._protocol) 73 | 74 | def extension_client(self): 75 | """Return an extension (osquery extension) client.""" 76 | return Client(self._protocol) 77 | -------------------------------------------------------------------------------- /osquery/extension_manager.py: -------------------------------------------------------------------------------- 1 | """This source code is licensed under the BSD-style license found in the 2 | LICENSE file in the root directory of this source tree. An additional grant 3 | of patent rights can be found in the PATENTS file in the same directory. 4 | """ 5 | 6 | from __future__ import absolute_import 7 | from __future__ import division 8 | from __future__ import print_function 9 | from __future__ import unicode_literals 10 | 11 | import os 12 | import threading 13 | import time 14 | 15 | from osquery.extensions.Extension import Iface 16 | from osquery.extensions.ttypes import ExtensionResponse, ExtensionStatus 17 | from osquery.singleton import Singleton 18 | 19 | def shutdown_request(code, timeout=1): 20 | """A delayed shutdown request.""" 21 | time.sleep(timeout) 22 | os._exit(code) 23 | 24 | class ExtensionManager(Singleton, Iface): 25 | """The thrift server for handling extension requests 26 | 27 | An extension's manager is responsible for maintaining the state of 28 | registered plugins, broadcasting the registry of those plugins to the 29 | core's extension manager and fielding requests that come in on the 30 | extension's socket. 31 | """ 32 | _plugins = {} 33 | _registry = {} 34 | 35 | uuid = None 36 | 37 | def add_plugin(self, plugin): 38 | """Register a plugin with the extension manager. In order for the 39 | extension manager to broadcast a plugin, it must be added using this 40 | interface. 41 | 42 | Keyword arguments: 43 | plugin -- the plugin class to register 44 | """ 45 | 46 | # First, we create an instance of the plugin. All plugins are 47 | # singletons, so this instance will be long-lived. 48 | obj = plugin() 49 | 50 | 51 | # When the extension manager broadcasts it's registry to core's 52 | # extension manager, the data structure should follow a specific 53 | # format. Whenever we add a plugin, we need to update the internal 54 | # _registry instance variable, which will be sent to core's extension 55 | # manager once the extension has been started 56 | if obj.registry_name() not in self._registry: 57 | self._registry[obj.registry_name()] = {} 58 | if obj.name() not in self._registry[obj.registry_name()]: 59 | self._registry[obj.registry_name()][obj.name()] = obj.routes() 60 | 61 | # The extension manager needs a way to route calls to the appropriate 62 | # implementation class. We maintain references to the plugin's 63 | # singleton instantiation in the _plugins instance variable. The 64 | # _plugins member has the same general structure as _registry, but 65 | # instead of pointing to the plugin's routes, it points to the plugin 66 | # implementation object 67 | if obj.registry_name() not in self._plugins: 68 | self._plugins[obj.registry_name()] = {} 69 | if obj.name() not in self._plugins[obj.registry_name()]: 70 | self._plugins[obj.registry_name()][obj.name()] = obj 71 | 72 | def shutdown(self): 73 | """The osquery extension manager requested a shutdown""" 74 | rt = threading.Thread(target=shutdown_request, args=(0,)) 75 | rt.daemon = True 76 | rt.start() 77 | return ExtensionStatus(code=0, message="OK") 78 | 79 | def registry(self): 80 | """Accessor for the internal _registry member variable""" 81 | return self._registry 82 | 83 | def ping(self): 84 | """Lightweight health verification 85 | 86 | The core osquery extension manager will periodically "ping" each 87 | extension that has connected to it to ensure that the extension is 88 | still active and can field requests, if necessary. 89 | """ 90 | return ExtensionStatus(code=0, message="OK") 91 | 92 | def call(self, registry, item, request): 93 | """The entry-point for plugin requests 94 | 95 | When a plugin is accessed from another process, osquery core's 96 | extension manager will send a thrift request to the implementing 97 | extension manager's call method. 98 | 99 | Arguments: 100 | registry -- a string representing what registry is being accessed. 101 | for config plugins this is "config", for table plugins this is 102 | "table", etc. 103 | item -- the registry item that is being requested. this is the "name" 104 | of your plugin. for example, this would be the exact name of the 105 | SQL table, if the plugin was a table plugin. 106 | """ 107 | 108 | # this API only support plugins of the following types: 109 | # - table 110 | # - config 111 | # - logger 112 | if registry not in ["table", "config", "logger"]: 113 | message = "A registry of an unknown type was called: %s" % registry 114 | return ExtensionResponse( 115 | status=ExtensionStatus(code=1, message=message,), 116 | response=[],) 117 | 118 | try: 119 | return self._plugins[registry][item].call(request) 120 | except KeyError: 121 | message = "Extension registry does not contain requested plugin" 122 | return ExtensionResponse( 123 | status=ExtensionStatus(code=1, message=message,), 124 | response=[],) 125 | -------------------------------------------------------------------------------- /osquery/extensions/Extension.py: -------------------------------------------------------------------------------- 1 | # 2 | # Autogenerated by Thrift Compiler (0.10.0) 3 | # 4 | # DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING 5 | # 6 | # options string: py 7 | # 8 | 9 | from thrift.Thrift import TType, TMessageType, TFrozenDict, TException, TApplicationException 10 | from thrift.protocol.TProtocol import TProtocolException 11 | import sys 12 | import logging 13 | from .ttypes import * 14 | from thrift.Thrift import TProcessor 15 | from thrift.transport import TTransport 16 | 17 | 18 | class Iface(object): 19 | def ping(self): 20 | pass 21 | 22 | def call(self, registry, item, request): 23 | """ 24 | Parameters: 25 | - registry 26 | - item 27 | - request 28 | """ 29 | pass 30 | 31 | def shutdown(self): 32 | pass 33 | 34 | 35 | class Client(Iface): 36 | def __init__(self, iprot, oprot=None): 37 | self._iprot = self._oprot = iprot 38 | if oprot is not None: 39 | self._oprot = oprot 40 | self._seqid = 0 41 | 42 | def ping(self): 43 | self.send_ping() 44 | return self.recv_ping() 45 | 46 | def send_ping(self): 47 | self._oprot.writeMessageBegin('ping', TMessageType.CALL, self._seqid) 48 | args = ping_args() 49 | args.write(self._oprot) 50 | self._oprot.writeMessageEnd() 51 | self._oprot.trans.flush() 52 | 53 | def recv_ping(self): 54 | iprot = self._iprot 55 | (fname, mtype, rseqid) = iprot.readMessageBegin() 56 | if mtype == TMessageType.EXCEPTION: 57 | x = TApplicationException() 58 | x.read(iprot) 59 | iprot.readMessageEnd() 60 | raise x 61 | result = ping_result() 62 | result.read(iprot) 63 | iprot.readMessageEnd() 64 | if result.success is not None: 65 | return result.success 66 | raise TApplicationException(TApplicationException.MISSING_RESULT, "ping failed: unknown result") 67 | 68 | def call(self, registry, item, request): 69 | """ 70 | Parameters: 71 | - registry 72 | - item 73 | - request 74 | """ 75 | self.send_call(registry, item, request) 76 | return self.recv_call() 77 | 78 | def send_call(self, registry, item, request): 79 | self._oprot.writeMessageBegin('call', TMessageType.CALL, self._seqid) 80 | args = call_args() 81 | args.registry = registry 82 | args.item = item 83 | args.request = request 84 | args.write(self._oprot) 85 | self._oprot.writeMessageEnd() 86 | self._oprot.trans.flush() 87 | 88 | def recv_call(self): 89 | iprot = self._iprot 90 | (fname, mtype, rseqid) = iprot.readMessageBegin() 91 | if mtype == TMessageType.EXCEPTION: 92 | x = TApplicationException() 93 | x.read(iprot) 94 | iprot.readMessageEnd() 95 | raise x 96 | result = call_result() 97 | result.read(iprot) 98 | iprot.readMessageEnd() 99 | if result.success is not None: 100 | return result.success 101 | raise TApplicationException(TApplicationException.MISSING_RESULT, "call failed: unknown result") 102 | 103 | def shutdown(self): 104 | self.send_shutdown() 105 | self.recv_shutdown() 106 | 107 | def send_shutdown(self): 108 | self._oprot.writeMessageBegin('shutdown', TMessageType.CALL, self._seqid) 109 | args = shutdown_args() 110 | args.write(self._oprot) 111 | self._oprot.writeMessageEnd() 112 | self._oprot.trans.flush() 113 | 114 | def recv_shutdown(self): 115 | iprot = self._iprot 116 | (fname, mtype, rseqid) = iprot.readMessageBegin() 117 | if mtype == TMessageType.EXCEPTION: 118 | x = TApplicationException() 119 | x.read(iprot) 120 | iprot.readMessageEnd() 121 | raise x 122 | result = shutdown_result() 123 | result.read(iprot) 124 | iprot.readMessageEnd() 125 | return 126 | 127 | 128 | class Processor(Iface, TProcessor): 129 | def __init__(self, handler): 130 | self._handler = handler 131 | self._processMap = {} 132 | self._processMap["ping"] = Processor.process_ping 133 | self._processMap["call"] = Processor.process_call 134 | self._processMap["shutdown"] = Processor.process_shutdown 135 | 136 | def process(self, iprot, oprot): 137 | (name, type, seqid) = iprot.readMessageBegin() 138 | if name not in self._processMap: 139 | iprot.skip(TType.STRUCT) 140 | iprot.readMessageEnd() 141 | x = TApplicationException(TApplicationException.UNKNOWN_METHOD, 'Unknown function %s' % (name)) 142 | oprot.writeMessageBegin(name, TMessageType.EXCEPTION, seqid) 143 | x.write(oprot) 144 | oprot.writeMessageEnd() 145 | oprot.trans.flush() 146 | return 147 | else: 148 | self._processMap[name](self, seqid, iprot, oprot) 149 | return True 150 | 151 | def process_ping(self, seqid, iprot, oprot): 152 | args = ping_args() 153 | args.read(iprot) 154 | iprot.readMessageEnd() 155 | result = ping_result() 156 | try: 157 | result.success = self._handler.ping() 158 | msg_type = TMessageType.REPLY 159 | except (TTransport.TTransportException, KeyboardInterrupt, SystemExit): 160 | raise 161 | except Exception as ex: 162 | msg_type = TMessageType.EXCEPTION 163 | logging.exception(ex) 164 | result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') 165 | oprot.writeMessageBegin("ping", msg_type, seqid) 166 | result.write(oprot) 167 | oprot.writeMessageEnd() 168 | oprot.trans.flush() 169 | 170 | def process_call(self, seqid, iprot, oprot): 171 | args = call_args() 172 | args.read(iprot) 173 | iprot.readMessageEnd() 174 | result = call_result() 175 | try: 176 | result.success = self._handler.call(args.registry, args.item, args.request) 177 | msg_type = TMessageType.REPLY 178 | except (TTransport.TTransportException, KeyboardInterrupt, SystemExit): 179 | raise 180 | except Exception as ex: 181 | msg_type = TMessageType.EXCEPTION 182 | logging.exception(ex) 183 | result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') 184 | oprot.writeMessageBegin("call", msg_type, seqid) 185 | result.write(oprot) 186 | oprot.writeMessageEnd() 187 | oprot.trans.flush() 188 | 189 | def process_shutdown(self, seqid, iprot, oprot): 190 | args = shutdown_args() 191 | args.read(iprot) 192 | iprot.readMessageEnd() 193 | result = shutdown_result() 194 | try: 195 | self._handler.shutdown() 196 | msg_type = TMessageType.REPLY 197 | except (TTransport.TTransportException, KeyboardInterrupt, SystemExit): 198 | raise 199 | except Exception as ex: 200 | msg_type = TMessageType.EXCEPTION 201 | logging.exception(ex) 202 | result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') 203 | oprot.writeMessageBegin("shutdown", msg_type, seqid) 204 | result.write(oprot) 205 | oprot.writeMessageEnd() 206 | oprot.trans.flush() 207 | 208 | # HELPER FUNCTIONS AND STRUCTURES 209 | 210 | 211 | class ping_args(object): 212 | 213 | thrift_spec = ( 214 | ) 215 | 216 | def read(self, iprot): 217 | if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: 218 | iprot._fast_decode(self, iprot, (self.__class__, self.thrift_spec)) 219 | return 220 | iprot.readStructBegin() 221 | while True: 222 | (fname, ftype, fid) = iprot.readFieldBegin() 223 | if ftype == TType.STOP: 224 | break 225 | else: 226 | iprot.skip(ftype) 227 | iprot.readFieldEnd() 228 | iprot.readStructEnd() 229 | 230 | def write(self, oprot): 231 | if oprot._fast_encode is not None and self.thrift_spec is not None: 232 | oprot.trans.write(oprot._fast_encode(self, (self.__class__, self.thrift_spec))) 233 | return 234 | oprot.writeStructBegin('ping_args') 235 | oprot.writeFieldStop() 236 | oprot.writeStructEnd() 237 | 238 | def validate(self): 239 | return 240 | 241 | def __repr__(self): 242 | L = ['%s=%r' % (key, value) 243 | for key, value in self.__dict__.items()] 244 | return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) 245 | 246 | def __eq__(self, other): 247 | return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ 248 | 249 | def __ne__(self, other): 250 | return not (self == other) 251 | 252 | 253 | class ping_result(object): 254 | """ 255 | Attributes: 256 | - success 257 | """ 258 | 259 | thrift_spec = ( 260 | (0, TType.STRUCT, 'success', (ExtensionStatus, ExtensionStatus.thrift_spec), None, ), # 0 261 | ) 262 | 263 | def __init__(self, success=None,): 264 | self.success = success 265 | 266 | def read(self, iprot): 267 | if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: 268 | iprot._fast_decode(self, iprot, (self.__class__, self.thrift_spec)) 269 | return 270 | iprot.readStructBegin() 271 | while True: 272 | (fname, ftype, fid) = iprot.readFieldBegin() 273 | if ftype == TType.STOP: 274 | break 275 | if fid == 0: 276 | if ftype == TType.STRUCT: 277 | self.success = ExtensionStatus() 278 | self.success.read(iprot) 279 | else: 280 | iprot.skip(ftype) 281 | else: 282 | iprot.skip(ftype) 283 | iprot.readFieldEnd() 284 | iprot.readStructEnd() 285 | 286 | def write(self, oprot): 287 | if oprot._fast_encode is not None and self.thrift_spec is not None: 288 | oprot.trans.write(oprot._fast_encode(self, (self.__class__, self.thrift_spec))) 289 | return 290 | oprot.writeStructBegin('ping_result') 291 | if self.success is not None: 292 | oprot.writeFieldBegin('success', TType.STRUCT, 0) 293 | self.success.write(oprot) 294 | oprot.writeFieldEnd() 295 | oprot.writeFieldStop() 296 | oprot.writeStructEnd() 297 | 298 | def validate(self): 299 | return 300 | 301 | def __repr__(self): 302 | L = ['%s=%r' % (key, value) 303 | for key, value in self.__dict__.items()] 304 | return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) 305 | 306 | def __eq__(self, other): 307 | return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ 308 | 309 | def __ne__(self, other): 310 | return not (self == other) 311 | 312 | 313 | class call_args(object): 314 | """ 315 | Attributes: 316 | - registry 317 | - item 318 | - request 319 | """ 320 | 321 | thrift_spec = ( 322 | None, # 0 323 | (1, TType.STRING, 'registry', 'UTF8', None, ), # 1 324 | (2, TType.STRING, 'item', 'UTF8', None, ), # 2 325 | (3, TType.MAP, 'request', (TType.STRING, 'UTF8', TType.STRING, 'UTF8', False), None, ), # 3 326 | ) 327 | 328 | def __init__(self, registry=None, item=None, request=None,): 329 | self.registry = registry 330 | self.item = item 331 | self.request = request 332 | 333 | def read(self, iprot): 334 | if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: 335 | iprot._fast_decode(self, iprot, (self.__class__, self.thrift_spec)) 336 | return 337 | iprot.readStructBegin() 338 | while True: 339 | (fname, ftype, fid) = iprot.readFieldBegin() 340 | if ftype == TType.STOP: 341 | break 342 | if fid == 1: 343 | if ftype == TType.STRING: 344 | self.registry = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() 345 | else: 346 | iprot.skip(ftype) 347 | elif fid == 2: 348 | if ftype == TType.STRING: 349 | self.item = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() 350 | else: 351 | iprot.skip(ftype) 352 | elif fid == 3: 353 | if ftype == TType.MAP: 354 | self.request = {} 355 | (_ktype17, _vtype18, _size16) = iprot.readMapBegin() 356 | for _i20 in range(_size16): 357 | _key21 = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() 358 | _val22 = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() 359 | self.request[_key21] = _val22 360 | iprot.readMapEnd() 361 | else: 362 | iprot.skip(ftype) 363 | else: 364 | iprot.skip(ftype) 365 | iprot.readFieldEnd() 366 | iprot.readStructEnd() 367 | 368 | def write(self, oprot): 369 | if oprot._fast_encode is not None and self.thrift_spec is not None: 370 | oprot.trans.write(oprot._fast_encode(self, (self.__class__, self.thrift_spec))) 371 | return 372 | oprot.writeStructBegin('call_args') 373 | if self.registry is not None: 374 | oprot.writeFieldBegin('registry', TType.STRING, 1) 375 | oprot.writeString(self.registry.encode('utf-8') if sys.version_info[0] == 2 else self.registry) 376 | oprot.writeFieldEnd() 377 | if self.item is not None: 378 | oprot.writeFieldBegin('item', TType.STRING, 2) 379 | oprot.writeString(self.item.encode('utf-8') if sys.version_info[0] == 2 else self.item) 380 | oprot.writeFieldEnd() 381 | if self.request is not None: 382 | oprot.writeFieldBegin('request', TType.MAP, 3) 383 | oprot.writeMapBegin(TType.STRING, TType.STRING, len(self.request)) 384 | for kiter23, viter24 in self.request.items(): 385 | oprot.writeString(kiter23.encode('utf-8') if sys.version_info[0] == 2 else kiter23) 386 | oprot.writeString(viter24.encode('utf-8') if sys.version_info[0] == 2 else viter24) 387 | oprot.writeMapEnd() 388 | oprot.writeFieldEnd() 389 | oprot.writeFieldStop() 390 | oprot.writeStructEnd() 391 | 392 | def validate(self): 393 | return 394 | 395 | def __repr__(self): 396 | L = ['%s=%r' % (key, value) 397 | for key, value in self.__dict__.items()] 398 | return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) 399 | 400 | def __eq__(self, other): 401 | return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ 402 | 403 | def __ne__(self, other): 404 | return not (self == other) 405 | 406 | 407 | class call_result(object): 408 | """ 409 | Attributes: 410 | - success 411 | """ 412 | 413 | thrift_spec = ( 414 | (0, TType.STRUCT, 'success', (ExtensionResponse, ExtensionResponse.thrift_spec), None, ), # 0 415 | ) 416 | 417 | def __init__(self, success=None,): 418 | self.success = success 419 | 420 | def read(self, iprot): 421 | if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: 422 | iprot._fast_decode(self, iprot, (self.__class__, self.thrift_spec)) 423 | return 424 | iprot.readStructBegin() 425 | while True: 426 | (fname, ftype, fid) = iprot.readFieldBegin() 427 | if ftype == TType.STOP: 428 | break 429 | if fid == 0: 430 | if ftype == TType.STRUCT: 431 | self.success = ExtensionResponse() 432 | self.success.read(iprot) 433 | else: 434 | iprot.skip(ftype) 435 | else: 436 | iprot.skip(ftype) 437 | iprot.readFieldEnd() 438 | iprot.readStructEnd() 439 | 440 | def write(self, oprot): 441 | if oprot._fast_encode is not None and self.thrift_spec is not None: 442 | oprot.trans.write(oprot._fast_encode(self, (self.__class__, self.thrift_spec))) 443 | return 444 | oprot.writeStructBegin('call_result') 445 | if self.success is not None: 446 | oprot.writeFieldBegin('success', TType.STRUCT, 0) 447 | self.success.write(oprot) 448 | oprot.writeFieldEnd() 449 | oprot.writeFieldStop() 450 | oprot.writeStructEnd() 451 | 452 | def validate(self): 453 | return 454 | 455 | def __repr__(self): 456 | L = ['%s=%r' % (key, value) 457 | for key, value in self.__dict__.items()] 458 | return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) 459 | 460 | def __eq__(self, other): 461 | return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ 462 | 463 | def __ne__(self, other): 464 | return not (self == other) 465 | 466 | 467 | class shutdown_args(object): 468 | 469 | thrift_spec = ( 470 | ) 471 | 472 | def read(self, iprot): 473 | if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: 474 | iprot._fast_decode(self, iprot, (self.__class__, self.thrift_spec)) 475 | return 476 | iprot.readStructBegin() 477 | while True: 478 | (fname, ftype, fid) = iprot.readFieldBegin() 479 | if ftype == TType.STOP: 480 | break 481 | else: 482 | iprot.skip(ftype) 483 | iprot.readFieldEnd() 484 | iprot.readStructEnd() 485 | 486 | def write(self, oprot): 487 | if oprot._fast_encode is not None and self.thrift_spec is not None: 488 | oprot.trans.write(oprot._fast_encode(self, (self.__class__, self.thrift_spec))) 489 | return 490 | oprot.writeStructBegin('shutdown_args') 491 | oprot.writeFieldStop() 492 | oprot.writeStructEnd() 493 | 494 | def validate(self): 495 | return 496 | 497 | def __repr__(self): 498 | L = ['%s=%r' % (key, value) 499 | for key, value in self.__dict__.items()] 500 | return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) 501 | 502 | def __eq__(self, other): 503 | return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ 504 | 505 | def __ne__(self, other): 506 | return not (self == other) 507 | 508 | 509 | class shutdown_result(object): 510 | 511 | thrift_spec = ( 512 | ) 513 | 514 | def read(self, iprot): 515 | if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: 516 | iprot._fast_decode(self, iprot, (self.__class__, self.thrift_spec)) 517 | return 518 | iprot.readStructBegin() 519 | while True: 520 | (fname, ftype, fid) = iprot.readFieldBegin() 521 | if ftype == TType.STOP: 522 | break 523 | else: 524 | iprot.skip(ftype) 525 | iprot.readFieldEnd() 526 | iprot.readStructEnd() 527 | 528 | def write(self, oprot): 529 | if oprot._fast_encode is not None and self.thrift_spec is not None: 530 | oprot.trans.write(oprot._fast_encode(self, (self.__class__, self.thrift_spec))) 531 | return 532 | oprot.writeStructBegin('shutdown_result') 533 | oprot.writeFieldStop() 534 | oprot.writeStructEnd() 535 | 536 | def validate(self): 537 | return 538 | 539 | def __repr__(self): 540 | L = ['%s=%r' % (key, value) 541 | for key, value in self.__dict__.items()] 542 | return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) 543 | 544 | def __eq__(self, other): 545 | return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ 546 | 547 | def __ne__(self, other): 548 | return not (self == other) 549 | -------------------------------------------------------------------------------- /osquery/extensions/ExtensionManager.py: -------------------------------------------------------------------------------- 1 | # 2 | # Autogenerated by Thrift Compiler (0.10.0) 3 | # 4 | # DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING 5 | # 6 | # options string: py 7 | # 8 | 9 | from thrift.Thrift import TType, TMessageType, TFrozenDict, TException, TApplicationException 10 | from thrift.protocol.TProtocol import TProtocolException 11 | import sys 12 | import osquery.extensions.Extension 13 | import logging 14 | from .ttypes import * 15 | from thrift.Thrift import TProcessor 16 | from thrift.transport import TTransport 17 | 18 | 19 | class Iface(osquery.extensions.Extension.Iface): 20 | def extensions(self): 21 | pass 22 | 23 | def options(self): 24 | pass 25 | 26 | def registerExtension(self, info, registry): 27 | """ 28 | Parameters: 29 | - info 30 | - registry 31 | """ 32 | pass 33 | 34 | def deregisterExtension(self, uuid): 35 | """ 36 | Parameters: 37 | - uuid 38 | """ 39 | pass 40 | 41 | def query(self, sql): 42 | """ 43 | Parameters: 44 | - sql 45 | """ 46 | pass 47 | 48 | def getQueryColumns(self, sql): 49 | """ 50 | Parameters: 51 | - sql 52 | """ 53 | pass 54 | 55 | 56 | class Client(osquery.extensions.Extension.Client, Iface): 57 | def __init__(self, iprot, oprot=None): 58 | osquery.extensions.Extension.Client.__init__(self, iprot, oprot) 59 | 60 | def extensions(self): 61 | self.send_extensions() 62 | return self.recv_extensions() 63 | 64 | def send_extensions(self): 65 | self._oprot.writeMessageBegin('extensions', TMessageType.CALL, self._seqid) 66 | args = extensions_args() 67 | args.write(self._oprot) 68 | self._oprot.writeMessageEnd() 69 | self._oprot.trans.flush() 70 | 71 | def recv_extensions(self): 72 | iprot = self._iprot 73 | (fname, mtype, rseqid) = iprot.readMessageBegin() 74 | if mtype == TMessageType.EXCEPTION: 75 | x = TApplicationException() 76 | x.read(iprot) 77 | iprot.readMessageEnd() 78 | raise x 79 | result = extensions_result() 80 | result.read(iprot) 81 | iprot.readMessageEnd() 82 | if result.success is not None: 83 | return result.success 84 | raise TApplicationException(TApplicationException.MISSING_RESULT, "extensions failed: unknown result") 85 | 86 | def options(self): 87 | self.send_options() 88 | return self.recv_options() 89 | 90 | def send_options(self): 91 | self._oprot.writeMessageBegin('options', TMessageType.CALL, self._seqid) 92 | args = options_args() 93 | args.write(self._oprot) 94 | self._oprot.writeMessageEnd() 95 | self._oprot.trans.flush() 96 | 97 | def recv_options(self): 98 | iprot = self._iprot 99 | (fname, mtype, rseqid) = iprot.readMessageBegin() 100 | if mtype == TMessageType.EXCEPTION: 101 | x = TApplicationException() 102 | x.read(iprot) 103 | iprot.readMessageEnd() 104 | raise x 105 | result = options_result() 106 | result.read(iprot) 107 | iprot.readMessageEnd() 108 | if result.success is not None: 109 | return result.success 110 | raise TApplicationException(TApplicationException.MISSING_RESULT, "options failed: unknown result") 111 | 112 | def registerExtension(self, info, registry): 113 | """ 114 | Parameters: 115 | - info 116 | - registry 117 | """ 118 | self.send_registerExtension(info, registry) 119 | return self.recv_registerExtension() 120 | 121 | def send_registerExtension(self, info, registry): 122 | self._oprot.writeMessageBegin('registerExtension', TMessageType.CALL, self._seqid) 123 | args = registerExtension_args() 124 | args.info = info 125 | args.registry = registry 126 | args.write(self._oprot) 127 | self._oprot.writeMessageEnd() 128 | self._oprot.trans.flush() 129 | 130 | def recv_registerExtension(self): 131 | iprot = self._iprot 132 | (fname, mtype, rseqid) = iprot.readMessageBegin() 133 | if mtype == TMessageType.EXCEPTION: 134 | x = TApplicationException() 135 | x.read(iprot) 136 | iprot.readMessageEnd() 137 | raise x 138 | result = registerExtension_result() 139 | result.read(iprot) 140 | iprot.readMessageEnd() 141 | if result.success is not None: 142 | return result.success 143 | raise TApplicationException(TApplicationException.MISSING_RESULT, "registerExtension failed: unknown result") 144 | 145 | def deregisterExtension(self, uuid): 146 | """ 147 | Parameters: 148 | - uuid 149 | """ 150 | self.send_deregisterExtension(uuid) 151 | return self.recv_deregisterExtension() 152 | 153 | def send_deregisterExtension(self, uuid): 154 | self._oprot.writeMessageBegin('deregisterExtension', TMessageType.CALL, self._seqid) 155 | args = deregisterExtension_args() 156 | args.uuid = uuid 157 | args.write(self._oprot) 158 | self._oprot.writeMessageEnd() 159 | self._oprot.trans.flush() 160 | 161 | def recv_deregisterExtension(self): 162 | iprot = self._iprot 163 | (fname, mtype, rseqid) = iprot.readMessageBegin() 164 | if mtype == TMessageType.EXCEPTION: 165 | x = TApplicationException() 166 | x.read(iprot) 167 | iprot.readMessageEnd() 168 | raise x 169 | result = deregisterExtension_result() 170 | result.read(iprot) 171 | iprot.readMessageEnd() 172 | if result.success is not None: 173 | return result.success 174 | raise TApplicationException(TApplicationException.MISSING_RESULT, "deregisterExtension failed: unknown result") 175 | 176 | def query(self, sql): 177 | """ 178 | Parameters: 179 | - sql 180 | """ 181 | self.send_query(sql) 182 | return self.recv_query() 183 | 184 | def send_query(self, sql): 185 | self._oprot.writeMessageBegin('query', TMessageType.CALL, self._seqid) 186 | args = query_args() 187 | args.sql = sql 188 | args.write(self._oprot) 189 | self._oprot.writeMessageEnd() 190 | self._oprot.trans.flush() 191 | 192 | def recv_query(self): 193 | iprot = self._iprot 194 | (fname, mtype, rseqid) = iprot.readMessageBegin() 195 | if mtype == TMessageType.EXCEPTION: 196 | x = TApplicationException() 197 | x.read(iprot) 198 | iprot.readMessageEnd() 199 | raise x 200 | result = query_result() 201 | result.read(iprot) 202 | iprot.readMessageEnd() 203 | if result.success is not None: 204 | return result.success 205 | raise TApplicationException(TApplicationException.MISSING_RESULT, "query failed: unknown result") 206 | 207 | def getQueryColumns(self, sql): 208 | """ 209 | Parameters: 210 | - sql 211 | """ 212 | self.send_getQueryColumns(sql) 213 | return self.recv_getQueryColumns() 214 | 215 | def send_getQueryColumns(self, sql): 216 | self._oprot.writeMessageBegin('getQueryColumns', TMessageType.CALL, self._seqid) 217 | args = getQueryColumns_args() 218 | args.sql = sql 219 | args.write(self._oprot) 220 | self._oprot.writeMessageEnd() 221 | self._oprot.trans.flush() 222 | 223 | def recv_getQueryColumns(self): 224 | iprot = self._iprot 225 | (fname, mtype, rseqid) = iprot.readMessageBegin() 226 | if mtype == TMessageType.EXCEPTION: 227 | x = TApplicationException() 228 | x.read(iprot) 229 | iprot.readMessageEnd() 230 | raise x 231 | result = getQueryColumns_result() 232 | result.read(iprot) 233 | iprot.readMessageEnd() 234 | if result.success is not None: 235 | return result.success 236 | raise TApplicationException(TApplicationException.MISSING_RESULT, "getQueryColumns failed: unknown result") 237 | 238 | 239 | class Processor(osquery.extensions.Extension.Processor, Iface, TProcessor): 240 | def __init__(self, handler): 241 | osquery.extensions.Extension.Processor.__init__(self, handler) 242 | self._processMap["extensions"] = Processor.process_extensions 243 | self._processMap["options"] = Processor.process_options 244 | self._processMap["registerExtension"] = Processor.process_registerExtension 245 | self._processMap["deregisterExtension"] = Processor.process_deregisterExtension 246 | self._processMap["query"] = Processor.process_query 247 | self._processMap["getQueryColumns"] = Processor.process_getQueryColumns 248 | 249 | def process(self, iprot, oprot): 250 | (name, type, seqid) = iprot.readMessageBegin() 251 | if name not in self._processMap: 252 | iprot.skip(TType.STRUCT) 253 | iprot.readMessageEnd() 254 | x = TApplicationException(TApplicationException.UNKNOWN_METHOD, 'Unknown function %s' % (name)) 255 | oprot.writeMessageBegin(name, TMessageType.EXCEPTION, seqid) 256 | x.write(oprot) 257 | oprot.writeMessageEnd() 258 | oprot.trans.flush() 259 | return 260 | else: 261 | self._processMap[name](self, seqid, iprot, oprot) 262 | return True 263 | 264 | def process_extensions(self, seqid, iprot, oprot): 265 | args = extensions_args() 266 | args.read(iprot) 267 | iprot.readMessageEnd() 268 | result = extensions_result() 269 | try: 270 | result.success = self._handler.extensions() 271 | msg_type = TMessageType.REPLY 272 | except (TTransport.TTransportException, KeyboardInterrupt, SystemExit): 273 | raise 274 | except Exception as ex: 275 | msg_type = TMessageType.EXCEPTION 276 | logging.exception(ex) 277 | result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') 278 | oprot.writeMessageBegin("extensions", msg_type, seqid) 279 | result.write(oprot) 280 | oprot.writeMessageEnd() 281 | oprot.trans.flush() 282 | 283 | def process_options(self, seqid, iprot, oprot): 284 | args = options_args() 285 | args.read(iprot) 286 | iprot.readMessageEnd() 287 | result = options_result() 288 | try: 289 | result.success = self._handler.options() 290 | msg_type = TMessageType.REPLY 291 | except (TTransport.TTransportException, KeyboardInterrupt, SystemExit): 292 | raise 293 | except Exception as ex: 294 | msg_type = TMessageType.EXCEPTION 295 | logging.exception(ex) 296 | result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') 297 | oprot.writeMessageBegin("options", msg_type, seqid) 298 | result.write(oprot) 299 | oprot.writeMessageEnd() 300 | oprot.trans.flush() 301 | 302 | def process_registerExtension(self, seqid, iprot, oprot): 303 | args = registerExtension_args() 304 | args.read(iprot) 305 | iprot.readMessageEnd() 306 | result = registerExtension_result() 307 | try: 308 | result.success = self._handler.registerExtension(args.info, args.registry) 309 | msg_type = TMessageType.REPLY 310 | except (TTransport.TTransportException, KeyboardInterrupt, SystemExit): 311 | raise 312 | except Exception as ex: 313 | msg_type = TMessageType.EXCEPTION 314 | logging.exception(ex) 315 | result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') 316 | oprot.writeMessageBegin("registerExtension", msg_type, seqid) 317 | result.write(oprot) 318 | oprot.writeMessageEnd() 319 | oprot.trans.flush() 320 | 321 | def process_deregisterExtension(self, seqid, iprot, oprot): 322 | args = deregisterExtension_args() 323 | args.read(iprot) 324 | iprot.readMessageEnd() 325 | result = deregisterExtension_result() 326 | try: 327 | result.success = self._handler.deregisterExtension(args.uuid) 328 | msg_type = TMessageType.REPLY 329 | except (TTransport.TTransportException, KeyboardInterrupt, SystemExit): 330 | raise 331 | except Exception as ex: 332 | msg_type = TMessageType.EXCEPTION 333 | logging.exception(ex) 334 | result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') 335 | oprot.writeMessageBegin("deregisterExtension", msg_type, seqid) 336 | result.write(oprot) 337 | oprot.writeMessageEnd() 338 | oprot.trans.flush() 339 | 340 | def process_query(self, seqid, iprot, oprot): 341 | args = query_args() 342 | args.read(iprot) 343 | iprot.readMessageEnd() 344 | result = query_result() 345 | try: 346 | result.success = self._handler.query(args.sql) 347 | msg_type = TMessageType.REPLY 348 | except (TTransport.TTransportException, KeyboardInterrupt, SystemExit): 349 | raise 350 | except Exception as ex: 351 | msg_type = TMessageType.EXCEPTION 352 | logging.exception(ex) 353 | result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') 354 | oprot.writeMessageBegin("query", msg_type, seqid) 355 | result.write(oprot) 356 | oprot.writeMessageEnd() 357 | oprot.trans.flush() 358 | 359 | def process_getQueryColumns(self, seqid, iprot, oprot): 360 | args = getQueryColumns_args() 361 | args.read(iprot) 362 | iprot.readMessageEnd() 363 | result = getQueryColumns_result() 364 | try: 365 | result.success = self._handler.getQueryColumns(args.sql) 366 | msg_type = TMessageType.REPLY 367 | except (TTransport.TTransportException, KeyboardInterrupt, SystemExit): 368 | raise 369 | except Exception as ex: 370 | msg_type = TMessageType.EXCEPTION 371 | logging.exception(ex) 372 | result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') 373 | oprot.writeMessageBegin("getQueryColumns", msg_type, seqid) 374 | result.write(oprot) 375 | oprot.writeMessageEnd() 376 | oprot.trans.flush() 377 | 378 | # HELPER FUNCTIONS AND STRUCTURES 379 | 380 | 381 | class extensions_args(object): 382 | 383 | thrift_spec = ( 384 | ) 385 | 386 | def read(self, iprot): 387 | if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: 388 | iprot._fast_decode(self, iprot, (self.__class__, self.thrift_spec)) 389 | return 390 | iprot.readStructBegin() 391 | while True: 392 | (fname, ftype, fid) = iprot.readFieldBegin() 393 | if ftype == TType.STOP: 394 | break 395 | else: 396 | iprot.skip(ftype) 397 | iprot.readFieldEnd() 398 | iprot.readStructEnd() 399 | 400 | def write(self, oprot): 401 | if oprot._fast_encode is not None and self.thrift_spec is not None: 402 | oprot.trans.write(oprot._fast_encode(self, (self.__class__, self.thrift_spec))) 403 | return 404 | oprot.writeStructBegin('extensions_args') 405 | oprot.writeFieldStop() 406 | oprot.writeStructEnd() 407 | 408 | def validate(self): 409 | return 410 | 411 | def __repr__(self): 412 | L = ['%s=%r' % (key, value) 413 | for key, value in self.__dict__.items()] 414 | return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) 415 | 416 | def __eq__(self, other): 417 | return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ 418 | 419 | def __ne__(self, other): 420 | return not (self == other) 421 | 422 | 423 | class extensions_result(object): 424 | """ 425 | Attributes: 426 | - success 427 | """ 428 | 429 | thrift_spec = ( 430 | (0, TType.MAP, 'success', (TType.I64, None, TType.STRUCT, (InternalExtensionInfo, InternalExtensionInfo.thrift_spec), False), None, ), # 0 431 | ) 432 | 433 | def __init__(self, success=None,): 434 | self.success = success 435 | 436 | def read(self, iprot): 437 | if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: 438 | iprot._fast_decode(self, iprot, (self.__class__, self.thrift_spec)) 439 | return 440 | iprot.readStructBegin() 441 | while True: 442 | (fname, ftype, fid) = iprot.readFieldBegin() 443 | if ftype == TType.STOP: 444 | break 445 | if fid == 0: 446 | if ftype == TType.MAP: 447 | self.success = {} 448 | (_ktype26, _vtype27, _size25) = iprot.readMapBegin() 449 | for _i29 in range(_size25): 450 | _key30 = iprot.readI64() 451 | _val31 = InternalExtensionInfo() 452 | _val31.read(iprot) 453 | self.success[_key30] = _val31 454 | iprot.readMapEnd() 455 | else: 456 | iprot.skip(ftype) 457 | else: 458 | iprot.skip(ftype) 459 | iprot.readFieldEnd() 460 | iprot.readStructEnd() 461 | 462 | def write(self, oprot): 463 | if oprot._fast_encode is not None and self.thrift_spec is not None: 464 | oprot.trans.write(oprot._fast_encode(self, (self.__class__, self.thrift_spec))) 465 | return 466 | oprot.writeStructBegin('extensions_result') 467 | if self.success is not None: 468 | oprot.writeFieldBegin('success', TType.MAP, 0) 469 | oprot.writeMapBegin(TType.I64, TType.STRUCT, len(self.success)) 470 | for kiter32, viter33 in self.success.items(): 471 | oprot.writeI64(kiter32) 472 | viter33.write(oprot) 473 | oprot.writeMapEnd() 474 | oprot.writeFieldEnd() 475 | oprot.writeFieldStop() 476 | oprot.writeStructEnd() 477 | 478 | def validate(self): 479 | return 480 | 481 | def __repr__(self): 482 | L = ['%s=%r' % (key, value) 483 | for key, value in self.__dict__.items()] 484 | return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) 485 | 486 | def __eq__(self, other): 487 | return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ 488 | 489 | def __ne__(self, other): 490 | return not (self == other) 491 | 492 | 493 | class options_args(object): 494 | 495 | thrift_spec = ( 496 | ) 497 | 498 | def read(self, iprot): 499 | if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: 500 | iprot._fast_decode(self, iprot, (self.__class__, self.thrift_spec)) 501 | return 502 | iprot.readStructBegin() 503 | while True: 504 | (fname, ftype, fid) = iprot.readFieldBegin() 505 | if ftype == TType.STOP: 506 | break 507 | else: 508 | iprot.skip(ftype) 509 | iprot.readFieldEnd() 510 | iprot.readStructEnd() 511 | 512 | def write(self, oprot): 513 | if oprot._fast_encode is not None and self.thrift_spec is not None: 514 | oprot.trans.write(oprot._fast_encode(self, (self.__class__, self.thrift_spec))) 515 | return 516 | oprot.writeStructBegin('options_args') 517 | oprot.writeFieldStop() 518 | oprot.writeStructEnd() 519 | 520 | def validate(self): 521 | return 522 | 523 | def __repr__(self): 524 | L = ['%s=%r' % (key, value) 525 | for key, value in self.__dict__.items()] 526 | return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) 527 | 528 | def __eq__(self, other): 529 | return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ 530 | 531 | def __ne__(self, other): 532 | return not (self == other) 533 | 534 | 535 | class options_result(object): 536 | """ 537 | Attributes: 538 | - success 539 | """ 540 | 541 | thrift_spec = ( 542 | (0, TType.MAP, 'success', (TType.STRING, 'UTF8', TType.STRUCT, (InternalOptionInfo, InternalOptionInfo.thrift_spec), False), None, ), # 0 543 | ) 544 | 545 | def __init__(self, success=None,): 546 | self.success = success 547 | 548 | def read(self, iprot): 549 | if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: 550 | iprot._fast_decode(self, iprot, (self.__class__, self.thrift_spec)) 551 | return 552 | iprot.readStructBegin() 553 | while True: 554 | (fname, ftype, fid) = iprot.readFieldBegin() 555 | if ftype == TType.STOP: 556 | break 557 | if fid == 0: 558 | if ftype == TType.MAP: 559 | self.success = {} 560 | (_ktype35, _vtype36, _size34) = iprot.readMapBegin() 561 | for _i38 in range(_size34): 562 | _key39 = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() 563 | _val40 = InternalOptionInfo() 564 | _val40.read(iprot) 565 | self.success[_key39] = _val40 566 | iprot.readMapEnd() 567 | else: 568 | iprot.skip(ftype) 569 | else: 570 | iprot.skip(ftype) 571 | iprot.readFieldEnd() 572 | iprot.readStructEnd() 573 | 574 | def write(self, oprot): 575 | if oprot._fast_encode is not None and self.thrift_spec is not None: 576 | oprot.trans.write(oprot._fast_encode(self, (self.__class__, self.thrift_spec))) 577 | return 578 | oprot.writeStructBegin('options_result') 579 | if self.success is not None: 580 | oprot.writeFieldBegin('success', TType.MAP, 0) 581 | oprot.writeMapBegin(TType.STRING, TType.STRUCT, len(self.success)) 582 | for kiter41, viter42 in self.success.items(): 583 | oprot.writeString(kiter41.encode('utf-8') if sys.version_info[0] == 2 else kiter41) 584 | viter42.write(oprot) 585 | oprot.writeMapEnd() 586 | oprot.writeFieldEnd() 587 | oprot.writeFieldStop() 588 | oprot.writeStructEnd() 589 | 590 | def validate(self): 591 | return 592 | 593 | def __repr__(self): 594 | L = ['%s=%r' % (key, value) 595 | for key, value in self.__dict__.items()] 596 | return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) 597 | 598 | def __eq__(self, other): 599 | return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ 600 | 601 | def __ne__(self, other): 602 | return not (self == other) 603 | 604 | 605 | class registerExtension_args(object): 606 | """ 607 | Attributes: 608 | - info 609 | - registry 610 | """ 611 | 612 | thrift_spec = ( 613 | None, # 0 614 | (1, TType.STRUCT, 'info', (InternalExtensionInfo, InternalExtensionInfo.thrift_spec), None, ), # 1 615 | (2, TType.MAP, 'registry', (TType.STRING, 'UTF8', TType.MAP, (TType.STRING, 'UTF8', TType.LIST, (TType.MAP, (TType.STRING, 'UTF8', TType.STRING, 'UTF8', False), False), False), False), None, ), # 2 616 | ) 617 | 618 | def __init__(self, info=None, registry=None,): 619 | self.info = info 620 | self.registry = registry 621 | 622 | def read(self, iprot): 623 | if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: 624 | iprot._fast_decode(self, iprot, (self.__class__, self.thrift_spec)) 625 | return 626 | iprot.readStructBegin() 627 | while True: 628 | (fname, ftype, fid) = iprot.readFieldBegin() 629 | if ftype == TType.STOP: 630 | break 631 | if fid == 1: 632 | if ftype == TType.STRUCT: 633 | self.info = InternalExtensionInfo() 634 | self.info.read(iprot) 635 | else: 636 | iprot.skip(ftype) 637 | elif fid == 2: 638 | if ftype == TType.MAP: 639 | self.registry = {} 640 | (_ktype44, _vtype45, _size43) = iprot.readMapBegin() 641 | for _i47 in range(_size43): 642 | _key48 = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() 643 | _val49 = {} 644 | (_ktype51, _vtype52, _size50) = iprot.readMapBegin() 645 | for _i54 in range(_size50): 646 | _key55 = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() 647 | _val56 = [] 648 | (_etype60, _size57) = iprot.readListBegin() 649 | for _i61 in range(_size57): 650 | _elem62 = {} 651 | (_ktype64, _vtype65, _size63) = iprot.readMapBegin() 652 | for _i67 in range(_size63): 653 | _key68 = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() 654 | _val69 = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() 655 | _elem62[_key68] = _val69 656 | iprot.readMapEnd() 657 | _val56.append(_elem62) 658 | iprot.readListEnd() 659 | _val49[_key55] = _val56 660 | iprot.readMapEnd() 661 | self.registry[_key48] = _val49 662 | iprot.readMapEnd() 663 | else: 664 | iprot.skip(ftype) 665 | else: 666 | iprot.skip(ftype) 667 | iprot.readFieldEnd() 668 | iprot.readStructEnd() 669 | 670 | def write(self, oprot): 671 | if oprot._fast_encode is not None and self.thrift_spec is not None: 672 | oprot.trans.write(oprot._fast_encode(self, (self.__class__, self.thrift_spec))) 673 | return 674 | oprot.writeStructBegin('registerExtension_args') 675 | if self.info is not None: 676 | oprot.writeFieldBegin('info', TType.STRUCT, 1) 677 | self.info.write(oprot) 678 | oprot.writeFieldEnd() 679 | if self.registry is not None: 680 | oprot.writeFieldBegin('registry', TType.MAP, 2) 681 | oprot.writeMapBegin(TType.STRING, TType.MAP, len(self.registry)) 682 | for kiter70, viter71 in self.registry.items(): 683 | oprot.writeString(kiter70.encode('utf-8') if sys.version_info[0] == 2 else kiter70) 684 | oprot.writeMapBegin(TType.STRING, TType.LIST, len(viter71)) 685 | for kiter72, viter73 in viter71.items(): 686 | oprot.writeString(kiter72.encode('utf-8') if sys.version_info[0] == 2 else kiter72) 687 | oprot.writeListBegin(TType.MAP, len(viter73)) 688 | for iter74 in viter73: 689 | oprot.writeMapBegin(TType.STRING, TType.STRING, len(iter74)) 690 | for kiter75, viter76 in iter74.items(): 691 | oprot.writeString(kiter75.encode('utf-8') if sys.version_info[0] == 2 else kiter75) 692 | oprot.writeString(viter76.encode('utf-8') if sys.version_info[0] == 2 else viter76) 693 | oprot.writeMapEnd() 694 | oprot.writeListEnd() 695 | oprot.writeMapEnd() 696 | oprot.writeMapEnd() 697 | oprot.writeFieldEnd() 698 | oprot.writeFieldStop() 699 | oprot.writeStructEnd() 700 | 701 | def validate(self): 702 | return 703 | 704 | def __repr__(self): 705 | L = ['%s=%r' % (key, value) 706 | for key, value in self.__dict__.items()] 707 | return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) 708 | 709 | def __eq__(self, other): 710 | return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ 711 | 712 | def __ne__(self, other): 713 | return not (self == other) 714 | 715 | 716 | class registerExtension_result(object): 717 | """ 718 | Attributes: 719 | - success 720 | """ 721 | 722 | thrift_spec = ( 723 | (0, TType.STRUCT, 'success', (ExtensionStatus, ExtensionStatus.thrift_spec), None, ), # 0 724 | ) 725 | 726 | def __init__(self, success=None,): 727 | self.success = success 728 | 729 | def read(self, iprot): 730 | if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: 731 | iprot._fast_decode(self, iprot, (self.__class__, self.thrift_spec)) 732 | return 733 | iprot.readStructBegin() 734 | while True: 735 | (fname, ftype, fid) = iprot.readFieldBegin() 736 | if ftype == TType.STOP: 737 | break 738 | if fid == 0: 739 | if ftype == TType.STRUCT: 740 | self.success = ExtensionStatus() 741 | self.success.read(iprot) 742 | else: 743 | iprot.skip(ftype) 744 | else: 745 | iprot.skip(ftype) 746 | iprot.readFieldEnd() 747 | iprot.readStructEnd() 748 | 749 | def write(self, oprot): 750 | if oprot._fast_encode is not None and self.thrift_spec is not None: 751 | oprot.trans.write(oprot._fast_encode(self, (self.__class__, self.thrift_spec))) 752 | return 753 | oprot.writeStructBegin('registerExtension_result') 754 | if self.success is not None: 755 | oprot.writeFieldBegin('success', TType.STRUCT, 0) 756 | self.success.write(oprot) 757 | oprot.writeFieldEnd() 758 | oprot.writeFieldStop() 759 | oprot.writeStructEnd() 760 | 761 | def validate(self): 762 | return 763 | 764 | def __repr__(self): 765 | L = ['%s=%r' % (key, value) 766 | for key, value in self.__dict__.items()] 767 | return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) 768 | 769 | def __eq__(self, other): 770 | return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ 771 | 772 | def __ne__(self, other): 773 | return not (self == other) 774 | 775 | 776 | class deregisterExtension_args(object): 777 | """ 778 | Attributes: 779 | - uuid 780 | """ 781 | 782 | thrift_spec = ( 783 | None, # 0 784 | (1, TType.I64, 'uuid', None, None, ), # 1 785 | ) 786 | 787 | def __init__(self, uuid=None,): 788 | self.uuid = uuid 789 | 790 | def read(self, iprot): 791 | if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: 792 | iprot._fast_decode(self, iprot, (self.__class__, self.thrift_spec)) 793 | return 794 | iprot.readStructBegin() 795 | while True: 796 | (fname, ftype, fid) = iprot.readFieldBegin() 797 | if ftype == TType.STOP: 798 | break 799 | if fid == 1: 800 | if ftype == TType.I64: 801 | self.uuid = iprot.readI64() 802 | else: 803 | iprot.skip(ftype) 804 | else: 805 | iprot.skip(ftype) 806 | iprot.readFieldEnd() 807 | iprot.readStructEnd() 808 | 809 | def write(self, oprot): 810 | if oprot._fast_encode is not None and self.thrift_spec is not None: 811 | oprot.trans.write(oprot._fast_encode(self, (self.__class__, self.thrift_spec))) 812 | return 813 | oprot.writeStructBegin('deregisterExtension_args') 814 | if self.uuid is not None: 815 | oprot.writeFieldBegin('uuid', TType.I64, 1) 816 | oprot.writeI64(self.uuid) 817 | oprot.writeFieldEnd() 818 | oprot.writeFieldStop() 819 | oprot.writeStructEnd() 820 | 821 | def validate(self): 822 | return 823 | 824 | def __repr__(self): 825 | L = ['%s=%r' % (key, value) 826 | for key, value in self.__dict__.items()] 827 | return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) 828 | 829 | def __eq__(self, other): 830 | return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ 831 | 832 | def __ne__(self, other): 833 | return not (self == other) 834 | 835 | 836 | class deregisterExtension_result(object): 837 | """ 838 | Attributes: 839 | - success 840 | """ 841 | 842 | thrift_spec = ( 843 | (0, TType.STRUCT, 'success', (ExtensionStatus, ExtensionStatus.thrift_spec), None, ), # 0 844 | ) 845 | 846 | def __init__(self, success=None,): 847 | self.success = success 848 | 849 | def read(self, iprot): 850 | if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: 851 | iprot._fast_decode(self, iprot, (self.__class__, self.thrift_spec)) 852 | return 853 | iprot.readStructBegin() 854 | while True: 855 | (fname, ftype, fid) = iprot.readFieldBegin() 856 | if ftype == TType.STOP: 857 | break 858 | if fid == 0: 859 | if ftype == TType.STRUCT: 860 | self.success = ExtensionStatus() 861 | self.success.read(iprot) 862 | else: 863 | iprot.skip(ftype) 864 | else: 865 | iprot.skip(ftype) 866 | iprot.readFieldEnd() 867 | iprot.readStructEnd() 868 | 869 | def write(self, oprot): 870 | if oprot._fast_encode is not None and self.thrift_spec is not None: 871 | oprot.trans.write(oprot._fast_encode(self, (self.__class__, self.thrift_spec))) 872 | return 873 | oprot.writeStructBegin('deregisterExtension_result') 874 | if self.success is not None: 875 | oprot.writeFieldBegin('success', TType.STRUCT, 0) 876 | self.success.write(oprot) 877 | oprot.writeFieldEnd() 878 | oprot.writeFieldStop() 879 | oprot.writeStructEnd() 880 | 881 | def validate(self): 882 | return 883 | 884 | def __repr__(self): 885 | L = ['%s=%r' % (key, value) 886 | for key, value in self.__dict__.items()] 887 | return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) 888 | 889 | def __eq__(self, other): 890 | return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ 891 | 892 | def __ne__(self, other): 893 | return not (self == other) 894 | 895 | 896 | class query_args(object): 897 | """ 898 | Attributes: 899 | - sql 900 | """ 901 | 902 | thrift_spec = ( 903 | None, # 0 904 | (1, TType.STRING, 'sql', 'UTF8', None, ), # 1 905 | ) 906 | 907 | def __init__(self, sql=None,): 908 | self.sql = sql 909 | 910 | def read(self, iprot): 911 | if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: 912 | iprot._fast_decode(self, iprot, (self.__class__, self.thrift_spec)) 913 | return 914 | iprot.readStructBegin() 915 | while True: 916 | (fname, ftype, fid) = iprot.readFieldBegin() 917 | if ftype == TType.STOP: 918 | break 919 | if fid == 1: 920 | if ftype == TType.STRING: 921 | self.sql = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() 922 | else: 923 | iprot.skip(ftype) 924 | else: 925 | iprot.skip(ftype) 926 | iprot.readFieldEnd() 927 | iprot.readStructEnd() 928 | 929 | def write(self, oprot): 930 | if oprot._fast_encode is not None and self.thrift_spec is not None: 931 | oprot.trans.write(oprot._fast_encode(self, (self.__class__, self.thrift_spec))) 932 | return 933 | oprot.writeStructBegin('query_args') 934 | if self.sql is not None: 935 | oprot.writeFieldBegin('sql', TType.STRING, 1) 936 | oprot.writeString(self.sql.encode('utf-8') if sys.version_info[0] == 2 else self.sql) 937 | oprot.writeFieldEnd() 938 | oprot.writeFieldStop() 939 | oprot.writeStructEnd() 940 | 941 | def validate(self): 942 | return 943 | 944 | def __repr__(self): 945 | L = ['%s=%r' % (key, value) 946 | for key, value in self.__dict__.items()] 947 | return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) 948 | 949 | def __eq__(self, other): 950 | return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ 951 | 952 | def __ne__(self, other): 953 | return not (self == other) 954 | 955 | 956 | class query_result(object): 957 | """ 958 | Attributes: 959 | - success 960 | """ 961 | 962 | thrift_spec = ( 963 | (0, TType.STRUCT, 'success', (ExtensionResponse, ExtensionResponse.thrift_spec), None, ), # 0 964 | ) 965 | 966 | def __init__(self, success=None,): 967 | self.success = success 968 | 969 | def read(self, iprot): 970 | if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: 971 | iprot._fast_decode(self, iprot, (self.__class__, self.thrift_spec)) 972 | return 973 | iprot.readStructBegin() 974 | while True: 975 | (fname, ftype, fid) = iprot.readFieldBegin() 976 | if ftype == TType.STOP: 977 | break 978 | if fid == 0: 979 | if ftype == TType.STRUCT: 980 | self.success = ExtensionResponse() 981 | self.success.read(iprot) 982 | else: 983 | iprot.skip(ftype) 984 | else: 985 | iprot.skip(ftype) 986 | iprot.readFieldEnd() 987 | iprot.readStructEnd() 988 | 989 | def write(self, oprot): 990 | if oprot._fast_encode is not None and self.thrift_spec is not None: 991 | oprot.trans.write(oprot._fast_encode(self, (self.__class__, self.thrift_spec))) 992 | return 993 | oprot.writeStructBegin('query_result') 994 | if self.success is not None: 995 | oprot.writeFieldBegin('success', TType.STRUCT, 0) 996 | self.success.write(oprot) 997 | oprot.writeFieldEnd() 998 | oprot.writeFieldStop() 999 | oprot.writeStructEnd() 1000 | 1001 | def validate(self): 1002 | return 1003 | 1004 | def __repr__(self): 1005 | L = ['%s=%r' % (key, value) 1006 | for key, value in self.__dict__.items()] 1007 | return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) 1008 | 1009 | def __eq__(self, other): 1010 | return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ 1011 | 1012 | def __ne__(self, other): 1013 | return not (self == other) 1014 | 1015 | 1016 | class getQueryColumns_args(object): 1017 | """ 1018 | Attributes: 1019 | - sql 1020 | """ 1021 | 1022 | thrift_spec = ( 1023 | None, # 0 1024 | (1, TType.STRING, 'sql', 'UTF8', None, ), # 1 1025 | ) 1026 | 1027 | def __init__(self, sql=None,): 1028 | self.sql = sql 1029 | 1030 | def read(self, iprot): 1031 | if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: 1032 | iprot._fast_decode(self, iprot, (self.__class__, self.thrift_spec)) 1033 | return 1034 | iprot.readStructBegin() 1035 | while True: 1036 | (fname, ftype, fid) = iprot.readFieldBegin() 1037 | if ftype == TType.STOP: 1038 | break 1039 | if fid == 1: 1040 | if ftype == TType.STRING: 1041 | self.sql = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() 1042 | else: 1043 | iprot.skip(ftype) 1044 | else: 1045 | iprot.skip(ftype) 1046 | iprot.readFieldEnd() 1047 | iprot.readStructEnd() 1048 | 1049 | def write(self, oprot): 1050 | if oprot._fast_encode is not None and self.thrift_spec is not None: 1051 | oprot.trans.write(oprot._fast_encode(self, (self.__class__, self.thrift_spec))) 1052 | return 1053 | oprot.writeStructBegin('getQueryColumns_args') 1054 | if self.sql is not None: 1055 | oprot.writeFieldBegin('sql', TType.STRING, 1) 1056 | oprot.writeString(self.sql.encode('utf-8') if sys.version_info[0] == 2 else self.sql) 1057 | oprot.writeFieldEnd() 1058 | oprot.writeFieldStop() 1059 | oprot.writeStructEnd() 1060 | 1061 | def validate(self): 1062 | return 1063 | 1064 | def __repr__(self): 1065 | L = ['%s=%r' % (key, value) 1066 | for key, value in self.__dict__.items()] 1067 | return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) 1068 | 1069 | def __eq__(self, other): 1070 | return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ 1071 | 1072 | def __ne__(self, other): 1073 | return not (self == other) 1074 | 1075 | 1076 | class getQueryColumns_result(object): 1077 | """ 1078 | Attributes: 1079 | - success 1080 | """ 1081 | 1082 | thrift_spec = ( 1083 | (0, TType.STRUCT, 'success', (ExtensionResponse, ExtensionResponse.thrift_spec), None, ), # 0 1084 | ) 1085 | 1086 | def __init__(self, success=None,): 1087 | self.success = success 1088 | 1089 | def read(self, iprot): 1090 | if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: 1091 | iprot._fast_decode(self, iprot, (self.__class__, self.thrift_spec)) 1092 | return 1093 | iprot.readStructBegin() 1094 | while True: 1095 | (fname, ftype, fid) = iprot.readFieldBegin() 1096 | if ftype == TType.STOP: 1097 | break 1098 | if fid == 0: 1099 | if ftype == TType.STRUCT: 1100 | self.success = ExtensionResponse() 1101 | self.success.read(iprot) 1102 | else: 1103 | iprot.skip(ftype) 1104 | else: 1105 | iprot.skip(ftype) 1106 | iprot.readFieldEnd() 1107 | iprot.readStructEnd() 1108 | 1109 | def write(self, oprot): 1110 | if oprot._fast_encode is not None and self.thrift_spec is not None: 1111 | oprot.trans.write(oprot._fast_encode(self, (self.__class__, self.thrift_spec))) 1112 | return 1113 | oprot.writeStructBegin('getQueryColumns_result') 1114 | if self.success is not None: 1115 | oprot.writeFieldBegin('success', TType.STRUCT, 0) 1116 | self.success.write(oprot) 1117 | oprot.writeFieldEnd() 1118 | oprot.writeFieldStop() 1119 | oprot.writeStructEnd() 1120 | 1121 | def validate(self): 1122 | return 1123 | 1124 | def __repr__(self): 1125 | L = ['%s=%r' % (key, value) 1126 | for key, value in self.__dict__.items()] 1127 | return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) 1128 | 1129 | def __eq__(self, other): 1130 | return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ 1131 | 1132 | def __ne__(self, other): 1133 | return not (self == other) 1134 | -------------------------------------------------------------------------------- /osquery/extensions/README: -------------------------------------------------------------------------------- 1 | All of the code in this directory is autogenerated by the thrift compiler. 2 | Do not modify this code. To regenerate this code, run the following from 3 | the root of this repository: 4 | 5 | python setup.py generate 6 | 7 | Quite intuitively, you must have the thrift compiler installed to generate the 8 | code. On OS X, you can get the thrift compiler from brew: 9 | 10 | brew install thrift 11 | 12 | Most Linux distributions also have native packages for thrift. 13 | -------------------------------------------------------------------------------- /osquery/extensions/__init__.py: -------------------------------------------------------------------------------- 1 | __all__ = ['ttypes', 'constants', 'Extension', 'ExtensionManager'] 2 | -------------------------------------------------------------------------------- /osquery/extensions/constants.py: -------------------------------------------------------------------------------- 1 | # 2 | # Autogenerated by Thrift Compiler (0.10.0) 3 | # 4 | # DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING 5 | # 6 | # options string: py 7 | # 8 | 9 | from thrift.Thrift import TType, TMessageType, TFrozenDict, TException, TApplicationException 10 | from thrift.protocol.TProtocol import TProtocolException 11 | import sys 12 | from .ttypes import * 13 | -------------------------------------------------------------------------------- /osquery/extensions/ttypes.py: -------------------------------------------------------------------------------- 1 | # 2 | # Autogenerated by Thrift Compiler (0.10.0) 3 | # 4 | # DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING 5 | # 6 | # options string: py 7 | # 8 | 9 | from thrift.Thrift import TType, TMessageType, TFrozenDict, TException, TApplicationException 10 | from thrift.protocol.TProtocol import TProtocolException 11 | import sys 12 | 13 | from thrift.transport import TTransport 14 | 15 | 16 | class ExtensionCode(object): 17 | EXT_SUCCESS = 0 18 | EXT_FAILED = 1 19 | EXT_FATAL = 2 20 | 21 | _VALUES_TO_NAMES = { 22 | 0: "EXT_SUCCESS", 23 | 1: "EXT_FAILED", 24 | 2: "EXT_FATAL", 25 | } 26 | 27 | _NAMES_TO_VALUES = { 28 | "EXT_SUCCESS": 0, 29 | "EXT_FAILED": 1, 30 | "EXT_FATAL": 2, 31 | } 32 | 33 | 34 | class InternalOptionInfo(object): 35 | """ 36 | Attributes: 37 | - value 38 | - default_value 39 | - type 40 | """ 41 | 42 | thrift_spec = ( 43 | None, # 0 44 | (1, TType.STRING, 'value', 'UTF8', None, ), # 1 45 | (2, TType.STRING, 'default_value', 'UTF8', None, ), # 2 46 | (3, TType.STRING, 'type', 'UTF8', None, ), # 3 47 | ) 48 | 49 | def __init__(self, value=None, default_value=None, type=None,): 50 | self.value = value 51 | self.default_value = default_value 52 | self.type = type 53 | 54 | def read(self, iprot): 55 | if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: 56 | iprot._fast_decode(self, iprot, (self.__class__, self.thrift_spec)) 57 | return 58 | iprot.readStructBegin() 59 | while True: 60 | (fname, ftype, fid) = iprot.readFieldBegin() 61 | if ftype == TType.STOP: 62 | break 63 | if fid == 1: 64 | if ftype == TType.STRING: 65 | self.value = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() 66 | else: 67 | iprot.skip(ftype) 68 | elif fid == 2: 69 | if ftype == TType.STRING: 70 | self.default_value = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() 71 | else: 72 | iprot.skip(ftype) 73 | elif fid == 3: 74 | if ftype == TType.STRING: 75 | self.type = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() 76 | else: 77 | iprot.skip(ftype) 78 | else: 79 | iprot.skip(ftype) 80 | iprot.readFieldEnd() 81 | iprot.readStructEnd() 82 | 83 | def write(self, oprot): 84 | if oprot._fast_encode is not None and self.thrift_spec is not None: 85 | oprot.trans.write(oprot._fast_encode(self, (self.__class__, self.thrift_spec))) 86 | return 87 | oprot.writeStructBegin('InternalOptionInfo') 88 | if self.value is not None: 89 | oprot.writeFieldBegin('value', TType.STRING, 1) 90 | oprot.writeString(self.value.encode('utf-8') if sys.version_info[0] == 2 else self.value) 91 | oprot.writeFieldEnd() 92 | if self.default_value is not None: 93 | oprot.writeFieldBegin('default_value', TType.STRING, 2) 94 | oprot.writeString(self.default_value.encode('utf-8') if sys.version_info[0] == 2 else self.default_value) 95 | oprot.writeFieldEnd() 96 | if self.type is not None: 97 | oprot.writeFieldBegin('type', TType.STRING, 3) 98 | oprot.writeString(self.type.encode('utf-8') if sys.version_info[0] == 2 else self.type) 99 | oprot.writeFieldEnd() 100 | oprot.writeFieldStop() 101 | oprot.writeStructEnd() 102 | 103 | def validate(self): 104 | return 105 | 106 | def __repr__(self): 107 | L = ['%s=%r' % (key, value) 108 | for key, value in self.__dict__.items()] 109 | return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) 110 | 111 | def __eq__(self, other): 112 | return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ 113 | 114 | def __ne__(self, other): 115 | return not (self == other) 116 | 117 | 118 | class InternalExtensionInfo(object): 119 | """ 120 | Attributes: 121 | - name 122 | - version 123 | - sdk_version 124 | - min_sdk_version 125 | """ 126 | 127 | thrift_spec = ( 128 | None, # 0 129 | (1, TType.STRING, 'name', 'UTF8', None, ), # 1 130 | (2, TType.STRING, 'version', 'UTF8', None, ), # 2 131 | (3, TType.STRING, 'sdk_version', 'UTF8', None, ), # 3 132 | (4, TType.STRING, 'min_sdk_version', 'UTF8', None, ), # 4 133 | ) 134 | 135 | def __init__(self, name=None, version=None, sdk_version=None, min_sdk_version=None,): 136 | self.name = name 137 | self.version = version 138 | self.sdk_version = sdk_version 139 | self.min_sdk_version = min_sdk_version 140 | 141 | def read(self, iprot): 142 | if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: 143 | iprot._fast_decode(self, iprot, (self.__class__, self.thrift_spec)) 144 | return 145 | iprot.readStructBegin() 146 | while True: 147 | (fname, ftype, fid) = iprot.readFieldBegin() 148 | if ftype == TType.STOP: 149 | break 150 | if fid == 1: 151 | if ftype == TType.STRING: 152 | self.name = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() 153 | else: 154 | iprot.skip(ftype) 155 | elif fid == 2: 156 | if ftype == TType.STRING: 157 | self.version = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() 158 | else: 159 | iprot.skip(ftype) 160 | elif fid == 3: 161 | if ftype == TType.STRING: 162 | self.sdk_version = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() 163 | else: 164 | iprot.skip(ftype) 165 | elif fid == 4: 166 | if ftype == TType.STRING: 167 | self.min_sdk_version = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() 168 | else: 169 | iprot.skip(ftype) 170 | else: 171 | iprot.skip(ftype) 172 | iprot.readFieldEnd() 173 | iprot.readStructEnd() 174 | 175 | def write(self, oprot): 176 | if oprot._fast_encode is not None and self.thrift_spec is not None: 177 | oprot.trans.write(oprot._fast_encode(self, (self.__class__, self.thrift_spec))) 178 | return 179 | oprot.writeStructBegin('InternalExtensionInfo') 180 | if self.name is not None: 181 | oprot.writeFieldBegin('name', TType.STRING, 1) 182 | oprot.writeString(self.name.encode('utf-8') if sys.version_info[0] == 2 else self.name) 183 | oprot.writeFieldEnd() 184 | if self.version is not None: 185 | oprot.writeFieldBegin('version', TType.STRING, 2) 186 | oprot.writeString(self.version.encode('utf-8') if sys.version_info[0] == 2 else self.version) 187 | oprot.writeFieldEnd() 188 | if self.sdk_version is not None: 189 | oprot.writeFieldBegin('sdk_version', TType.STRING, 3) 190 | oprot.writeString(self.sdk_version.encode('utf-8') if sys.version_info[0] == 2 else self.sdk_version) 191 | oprot.writeFieldEnd() 192 | if self.min_sdk_version is not None: 193 | oprot.writeFieldBegin('min_sdk_version', TType.STRING, 4) 194 | oprot.writeString(self.min_sdk_version.encode('utf-8') if sys.version_info[0] == 2 else self.min_sdk_version) 195 | oprot.writeFieldEnd() 196 | oprot.writeFieldStop() 197 | oprot.writeStructEnd() 198 | 199 | def validate(self): 200 | return 201 | 202 | def __repr__(self): 203 | L = ['%s=%r' % (key, value) 204 | for key, value in self.__dict__.items()] 205 | return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) 206 | 207 | def __eq__(self, other): 208 | return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ 209 | 210 | def __ne__(self, other): 211 | return not (self == other) 212 | 213 | 214 | class ExtensionStatus(object): 215 | """ 216 | Attributes: 217 | - code 218 | - message 219 | - uuid 220 | """ 221 | 222 | thrift_spec = ( 223 | None, # 0 224 | (1, TType.I32, 'code', None, None, ), # 1 225 | (2, TType.STRING, 'message', 'UTF8', None, ), # 2 226 | (3, TType.I64, 'uuid', None, None, ), # 3 227 | ) 228 | 229 | def __init__(self, code=None, message=None, uuid=None,): 230 | self.code = code 231 | self.message = message 232 | self.uuid = uuid 233 | 234 | def read(self, iprot): 235 | if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: 236 | iprot._fast_decode(self, iprot, (self.__class__, self.thrift_spec)) 237 | return 238 | iprot.readStructBegin() 239 | while True: 240 | (fname, ftype, fid) = iprot.readFieldBegin() 241 | if ftype == TType.STOP: 242 | break 243 | if fid == 1: 244 | if ftype == TType.I32: 245 | self.code = iprot.readI32() 246 | else: 247 | iprot.skip(ftype) 248 | elif fid == 2: 249 | if ftype == TType.STRING: 250 | self.message = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() 251 | else: 252 | iprot.skip(ftype) 253 | elif fid == 3: 254 | if ftype == TType.I64: 255 | self.uuid = iprot.readI64() 256 | else: 257 | iprot.skip(ftype) 258 | else: 259 | iprot.skip(ftype) 260 | iprot.readFieldEnd() 261 | iprot.readStructEnd() 262 | 263 | def write(self, oprot): 264 | if oprot._fast_encode is not None and self.thrift_spec is not None: 265 | oprot.trans.write(oprot._fast_encode(self, (self.__class__, self.thrift_spec))) 266 | return 267 | oprot.writeStructBegin('ExtensionStatus') 268 | if self.code is not None: 269 | oprot.writeFieldBegin('code', TType.I32, 1) 270 | oprot.writeI32(self.code) 271 | oprot.writeFieldEnd() 272 | if self.message is not None: 273 | oprot.writeFieldBegin('message', TType.STRING, 2) 274 | oprot.writeString(self.message.encode('utf-8') if sys.version_info[0] == 2 else self.message) 275 | oprot.writeFieldEnd() 276 | if self.uuid is not None: 277 | oprot.writeFieldBegin('uuid', TType.I64, 3) 278 | oprot.writeI64(self.uuid) 279 | oprot.writeFieldEnd() 280 | oprot.writeFieldStop() 281 | oprot.writeStructEnd() 282 | 283 | def validate(self): 284 | return 285 | 286 | def __repr__(self): 287 | L = ['%s=%r' % (key, value) 288 | for key, value in self.__dict__.items()] 289 | return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) 290 | 291 | def __eq__(self, other): 292 | return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ 293 | 294 | def __ne__(self, other): 295 | return not (self == other) 296 | 297 | 298 | class ExtensionResponse(object): 299 | """ 300 | Attributes: 301 | - status 302 | - response 303 | """ 304 | 305 | thrift_spec = ( 306 | None, # 0 307 | (1, TType.STRUCT, 'status', (ExtensionStatus, ExtensionStatus.thrift_spec), None, ), # 1 308 | (2, TType.LIST, 'response', (TType.MAP, (TType.STRING, 'UTF8', TType.STRING, 'UTF8', False), False), None, ), # 2 309 | ) 310 | 311 | def __init__(self, status=None, response=None,): 312 | self.status = status 313 | self.response = response 314 | 315 | def read(self, iprot): 316 | if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: 317 | iprot._fast_decode(self, iprot, (self.__class__, self.thrift_spec)) 318 | return 319 | iprot.readStructBegin() 320 | while True: 321 | (fname, ftype, fid) = iprot.readFieldBegin() 322 | if ftype == TType.STOP: 323 | break 324 | if fid == 1: 325 | if ftype == TType.STRUCT: 326 | self.status = ExtensionStatus() 327 | self.status.read(iprot) 328 | else: 329 | iprot.skip(ftype) 330 | elif fid == 2: 331 | if ftype == TType.LIST: 332 | self.response = [] 333 | (_etype3, _size0) = iprot.readListBegin() 334 | for _i4 in range(_size0): 335 | _elem5 = {} 336 | (_ktype7, _vtype8, _size6) = iprot.readMapBegin() 337 | for _i10 in range(_size6): 338 | _key11 = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() 339 | _val12 = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() 340 | _elem5[_key11] = _val12 341 | iprot.readMapEnd() 342 | self.response.append(_elem5) 343 | iprot.readListEnd() 344 | else: 345 | iprot.skip(ftype) 346 | else: 347 | iprot.skip(ftype) 348 | iprot.readFieldEnd() 349 | iprot.readStructEnd() 350 | 351 | def write(self, oprot): 352 | if oprot._fast_encode is not None and self.thrift_spec is not None: 353 | oprot.trans.write(oprot._fast_encode(self, (self.__class__, self.thrift_spec))) 354 | return 355 | oprot.writeStructBegin('ExtensionResponse') 356 | if self.status is not None: 357 | oprot.writeFieldBegin('status', TType.STRUCT, 1) 358 | self.status.write(oprot) 359 | oprot.writeFieldEnd() 360 | if self.response is not None: 361 | oprot.writeFieldBegin('response', TType.LIST, 2) 362 | oprot.writeListBegin(TType.MAP, len(self.response)) 363 | for iter13 in self.response: 364 | oprot.writeMapBegin(TType.STRING, TType.STRING, len(iter13)) 365 | for kiter14, viter15 in iter13.items(): 366 | oprot.writeString(kiter14.encode('utf-8') if sys.version_info[0] == 2 else kiter14) 367 | oprot.writeString(viter15.encode('utf-8') if sys.version_info[0] == 2 else viter15) 368 | oprot.writeMapEnd() 369 | oprot.writeListEnd() 370 | oprot.writeFieldEnd() 371 | oprot.writeFieldStop() 372 | oprot.writeStructEnd() 373 | 374 | def validate(self): 375 | return 376 | 377 | def __repr__(self): 378 | L = ['%s=%r' % (key, value) 379 | for key, value in self.__dict__.items()] 380 | return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) 381 | 382 | def __eq__(self, other): 383 | return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ 384 | 385 | def __ne__(self, other): 386 | return not (self == other) 387 | 388 | 389 | class ExtensionException(TException): 390 | """ 391 | Attributes: 392 | - code 393 | - message 394 | - uuid 395 | """ 396 | 397 | thrift_spec = ( 398 | None, # 0 399 | (1, TType.I32, 'code', None, None, ), # 1 400 | (2, TType.STRING, 'message', 'UTF8', None, ), # 2 401 | (3, TType.I64, 'uuid', None, None, ), # 3 402 | ) 403 | 404 | def __init__(self, code=None, message=None, uuid=None,): 405 | self.code = code 406 | self.message = message 407 | self.uuid = uuid 408 | 409 | def read(self, iprot): 410 | if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: 411 | iprot._fast_decode(self, iprot, (self.__class__, self.thrift_spec)) 412 | return 413 | iprot.readStructBegin() 414 | while True: 415 | (fname, ftype, fid) = iprot.readFieldBegin() 416 | if ftype == TType.STOP: 417 | break 418 | if fid == 1: 419 | if ftype == TType.I32: 420 | self.code = iprot.readI32() 421 | else: 422 | iprot.skip(ftype) 423 | elif fid == 2: 424 | if ftype == TType.STRING: 425 | self.message = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() 426 | else: 427 | iprot.skip(ftype) 428 | elif fid == 3: 429 | if ftype == TType.I64: 430 | self.uuid = iprot.readI64() 431 | else: 432 | iprot.skip(ftype) 433 | else: 434 | iprot.skip(ftype) 435 | iprot.readFieldEnd() 436 | iprot.readStructEnd() 437 | 438 | def write(self, oprot): 439 | if oprot._fast_encode is not None and self.thrift_spec is not None: 440 | oprot.trans.write(oprot._fast_encode(self, (self.__class__, self.thrift_spec))) 441 | return 442 | oprot.writeStructBegin('ExtensionException') 443 | if self.code is not None: 444 | oprot.writeFieldBegin('code', TType.I32, 1) 445 | oprot.writeI32(self.code) 446 | oprot.writeFieldEnd() 447 | if self.message is not None: 448 | oprot.writeFieldBegin('message', TType.STRING, 2) 449 | oprot.writeString(self.message.encode('utf-8') if sys.version_info[0] == 2 else self.message) 450 | oprot.writeFieldEnd() 451 | if self.uuid is not None: 452 | oprot.writeFieldBegin('uuid', TType.I64, 3) 453 | oprot.writeI64(self.uuid) 454 | oprot.writeFieldEnd() 455 | oprot.writeFieldStop() 456 | oprot.writeStructEnd() 457 | 458 | def validate(self): 459 | return 460 | 461 | def __str__(self): 462 | return repr(self) 463 | 464 | def __repr__(self): 465 | L = ['%s=%r' % (key, value) 466 | for key, value in self.__dict__.items()] 467 | return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) 468 | 469 | def __eq__(self, other): 470 | return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ 471 | 472 | def __ne__(self, other): 473 | return not (self == other) 474 | -------------------------------------------------------------------------------- /osquery/logger_plugin.py: -------------------------------------------------------------------------------- 1 | """This source code is licensed under the BSD-style license found in the 2 | LICENSE file in the root directory of this source tree. An additional grant 3 | of patent rights can be found in the PATENTS file in the same directory. 4 | """ 5 | 6 | # pylint: disable=no-self-use 7 | # pylint: disable=unused-argument 8 | 9 | from __future__ import absolute_import 10 | from __future__ import division 11 | from __future__ import print_function 12 | from __future__ import unicode_literals 13 | 14 | from abc import ABCMeta, abstractmethod 15 | from future.utils import with_metaclass 16 | 17 | from osquery.extensions.ttypes import ExtensionResponse, ExtensionStatus 18 | from osquery.plugin import BasePlugin 19 | 20 | 21 | class LoggerPlugin(with_metaclass(ABCMeta, BasePlugin)): 22 | """All logger plugins should inherit from LoggerPlugin""" 23 | 24 | _use_glog_message = "Use Glog for status logging" 25 | _invalid_action_message = "Not a valid logger plugin action" 26 | 27 | def call(self, context): 28 | """Internal routing for this plugin type. 29 | 30 | Do not override this method. 31 | """ 32 | if "string" in context: 33 | return ExtensionResponse(status=self.log_string(context["string"]), 34 | response=[],) 35 | elif "snapshot" in context: 36 | return ExtensionResponse( 37 | status=self.log_snapshot(context["snapshot"]), 38 | response=[],) 39 | elif "health" in context: 40 | return ExtensionResponse(status=self.log_health(context["health"]), 41 | response=[],) 42 | elif "init" in context: 43 | return ExtensionResponse( 44 | status=ExtensionStatus(code=1, 45 | message=self._use_glog_message,), 46 | response=[],) 47 | elif "status" in context: 48 | return ExtensionResponse( 49 | status=ExtensionStatus(code=1, 50 | message=self._use_glog_message,), 51 | response=[],) 52 | else: 53 | return ExtensionResponse( 54 | status=ExtensionStatus(code=1, 55 | message=self._invalid_action_message,), 56 | response=[],) 57 | 58 | def registry_name(self): 59 | """The name of the registry type for logger plugins. 60 | 61 | Do not override this method.""" 62 | return "logger" 63 | 64 | @abstractmethod 65 | def log_string(self, value): 66 | """The implementation of your logger plugin. 67 | 68 | This must be implemented by your plugin. 69 | 70 | This must return an ExtensionStatus 71 | 72 | Arguments: 73 | value -- the string to log 74 | """ 75 | raise NotImplementedError 76 | 77 | def log_health(self, value): 78 | """If you'd like the log health statistics about osquery's performance, 79 | override this method in your logger plugin. 80 | 81 | By default, this action is a noop. 82 | 83 | This must return an ExtensionStatus 84 | """ 85 | return ExtensionStatus(code=0, message="OK",) 86 | 87 | def log_snapshot(self, value): 88 | """If you'd like to log snapshot queries in a special way, override 89 | this method. 90 | 91 | By default, this action is just hands off the string to log_string. 92 | 93 | This must return an ExtensionStatus 94 | """ 95 | return self.log_string(value) 96 | -------------------------------------------------------------------------------- /osquery/management.py: -------------------------------------------------------------------------------- 1 | """This source code is licensed under the BSD-style license found in the 2 | LICENSE file in the root directory of this source tree. An additional grant 3 | of patent rights can be found in the PATENTS file in the same directory. 4 | """ 5 | 6 | from __future__ import absolute_import 7 | from __future__ import division 8 | from __future__ import print_function 9 | from __future__ import unicode_literals 10 | 11 | import argparse 12 | import logging 13 | import os 14 | import random 15 | import socket 16 | import subprocess 17 | import sys 18 | import tempfile 19 | import threading 20 | import time 21 | 22 | # logging support for Python 2.6 23 | try: 24 | from logging import NullHandler 25 | except ImportError: 26 | class NullHandler(logging.Handler): 27 | def emit(self, record): 28 | pass 29 | logging.NullHandler = NullHandler 30 | 31 | from thrift.protocol import TBinaryProtocol 32 | from thrift.server import TServer 33 | from thrift.transport import TSocket 34 | from thrift.transport import TTransport 35 | 36 | from osquery.extensions.ttypes import ExtensionException, InternalExtensionInfo 37 | from osquery.extensions.Extension import Processor 38 | from osquery.extension_client import ExtensionClient, DEFAULT_SOCKET_PATH, WINDOWS_PLATFORM 39 | from osquery.extension_manager import ExtensionManager 40 | 41 | if sys.platform == WINDOWS_PLATFORM: 42 | # We bootleg our own version of Windows pipe coms 43 | from osquery.TPipe import TPipe 44 | from osquery.TPipe import TPipeServer 45 | PTH = os.path.join(os.environ["PROGRAMDATA"], "osquery", "osqueryd", "osqueryd.exe") 46 | if os.path.exists(PTH): 47 | WINDOWS_BINARY_PATH = PTH 48 | PTH = os.path.join(os.environ["PROGRAMW6432"], "osquery", "osqueryd", "osqueryd.exe") 49 | if os.path.exists(PTH): 50 | WINDOWS_BINARY_PATH = PTH 51 | 52 | DARWIN_BINARY_PATH = "/usr/local/bin/osqueryd" 53 | LINUX_BINARY_PATH = "/usr/bin/osqueryd" 54 | 55 | class SpawnInstance(object): 56 | """Spawn a standalone osquery instance""" 57 | """The osquery process instance.""" 58 | instance = None 59 | """The extension client connection attached to the instance.""" 60 | connection = None 61 | _socket = None 62 | 63 | def __init__(self, path=None): 64 | """ 65 | Keyword arguments: 66 | path -- the path to and osqueryd binary to spawn 67 | """ 68 | if path is None: 69 | # Darwin is special and must have binaries installed in /usr/local. 70 | if sys.platform == "darwin": 71 | self.path = DARWIN_BINARY_PATH 72 | elif sys.platform == WINDOWS_PLATFORM: 73 | self.path = WINDOWS_BINARY_PATH 74 | else: 75 | self.path = LINUX_BINARY_PATH 76 | else: 77 | self.path = path 78 | 79 | # Disable logging for the thrift module (can be loud). 80 | logging.getLogger('thrift').addHandler(logging.NullHandler()) 81 | if sys.platform == WINDOWS_PLATFORM: 82 | # Windows fails to spawn if the pidfile already exists 83 | self._pidfile = (None, tempfile.gettempdir() + '\\pyosqpid-' + 84 | str(random.randint(10000, 20000))) 85 | pipeName = r'\\.\pipe\pyosqsock-' + str( 86 | random.randint(10000, 20000)) 87 | self._socket = (None, pipeName) 88 | else: 89 | self._socket = tempfile.mkstemp(prefix="pyosqsock") 90 | 91 | def __del__(self): 92 | if self.connection is not None: 93 | self.connection.close() 94 | self.connection = None 95 | if self.instance is not None: 96 | self.instance.kill() 97 | self.instance.wait() 98 | self.instance = None 99 | 100 | # On macOS and Linux mkstemp opens a descriptor. 101 | if self._socket is not None and self._socket[0] is not None: 102 | os.close(self._socket[0]) 103 | 104 | # Remove the dangling temporary file from mkstemp if it still exists 105 | if os.path.exists(self._socket[1]): 106 | try: 107 | os.unlink(self._socket[1]) 108 | except OSError: 109 | logging.warning("Failed to remove socket descriptor: %s", self._socket[1]) 110 | 111 | self._socket = None 112 | 113 | def open(self, timeout=2, interval=0.01): 114 | """ 115 | Start the instance process and open an extension client 116 | 117 | Keyword arguments: 118 | timeout -- maximum number of seconds to wait for client 119 | interval -- seconds between client open attempts 120 | """ 121 | proc = [ 122 | self.path, 123 | "--extensions_socket", 124 | self._socket[1], 125 | "--disable_database", 126 | "--disable_watchdog", 127 | "--disable_logging", 128 | "--ephemeral", 129 | "--config_path", 130 | "/dev/null", 131 | ] 132 | self.instance = subprocess.Popen( 133 | proc, 134 | stdin=subprocess.PIPE, 135 | stdout=subprocess.PIPE, 136 | stderr=subprocess.PIPE) 137 | self.connection = ExtensionClient(path=self._socket[1]) 138 | if not self.is_running(): 139 | raise Exception("Cannot start process from path: %s" % (self.path)) 140 | 141 | # Attempt to open the extension client. 142 | delay = 0 143 | while delay < timeout: 144 | try: 145 | self.connection.open() 146 | return 147 | except Exception: 148 | time.sleep(interval) 149 | delay += interval 150 | self.instance.kill() 151 | self.instance = None 152 | raise Exception("Cannot open connection: %s" % (self._socket[1])) 153 | 154 | def is_running(self): 155 | """Check if the instance has spawned.""" 156 | if self.instance is None: 157 | return False 158 | return self.instance.poll() is None 159 | 160 | @property 161 | def client(self): 162 | """The extension client.""" 163 | return self.connection.extension_manager_client() 164 | 165 | 166 | def parse_cli_params(): 167 | """Parse CLI parameters passed to the extension executable""" 168 | parser = argparse.ArgumentParser(description=("osquery python extension")) 169 | parser.add_argument( 170 | "--socket", 171 | type=str, 172 | default=DEFAULT_SOCKET_PATH, 173 | help="Path to the extensions UNIX domain socket") 174 | parser.add_argument( 175 | "--timeout", 176 | type=int, 177 | default=1, 178 | help="Seconds to wait for autoloaded extensions") 179 | parser.add_argument( 180 | "--interval", 181 | type=int, 182 | default=1, 183 | help="Seconds delay between connectivity checks") 184 | parser.add_argument( 185 | "--verbose", 186 | action="store_true", 187 | help="Enable verbose informational messages") 188 | return parser.parse_args() 189 | 190 | 191 | def start_watcher(client, interval): 192 | """Ping the osquery extension manager to detect dirty shutdowns.""" 193 | try: 194 | while True: 195 | status = client.extension_manager_client().ping() 196 | if status.code != 0: 197 | logging.error("Ping received nonzero status: %d", status) 198 | break 199 | time.sleep(interval) 200 | 201 | except socket.error as e: 202 | logging.error("Ping received socket.error: %s", e) 203 | 204 | except TTransport.TTransportException as e: 205 | logging.error("Ping received thrift.transport.TTransport.TTransportException: %s", e) 206 | 207 | except Exception as e: 208 | logging.error("Ping received unknown exception: %s", e) 209 | 210 | finally: 211 | os._exit(1) 212 | 213 | 214 | def start_extension(name="", 215 | version="0.0.0", 216 | sdk_version="3.0.7", 217 | min_sdk_version="1.8.0"): 218 | """Start your extension by communicating with osquery core and starting 219 | a thrift server. 220 | 221 | Keyword arguments: 222 | name -- the name of your extension 223 | version -- the version of your extension 224 | sdk_version -- the version of the osquery SDK used to build this extension 225 | min_sdk_version -- the minimum version of the osquery SDK that you can use 226 | """ 227 | args = parse_cli_params() 228 | 229 | # Disable logging for the thrift module (can be loud). 230 | logging.getLogger('thrift').addHandler(logging.NullHandler()) 231 | 232 | client = ExtensionClient(path=args.socket) 233 | 234 | if not client.open(args.timeout): 235 | if args.verbose: 236 | message = "Could not open socket %s" % args.socket 237 | raise ExtensionException( 238 | code=1, 239 | message=message, 240 | ) 241 | return 242 | ext_manager = ExtensionManager() 243 | 244 | # try connecting to the desired osquery core extension manager socket 245 | try: 246 | status = client.extension_manager_client().registerExtension( 247 | info=InternalExtensionInfo( 248 | name=name, 249 | version=version, 250 | sdk_version=sdk_version, 251 | min_sdk_version=min_sdk_version, 252 | ), 253 | registry=ext_manager.registry(), 254 | ) 255 | except socket.error: 256 | message = "Could not connect to %s" % args.socket 257 | raise ExtensionException( 258 | code=1, 259 | message=message, 260 | ) 261 | 262 | if status.code != 0: 263 | raise ExtensionException( 264 | code=1, 265 | message=status.message, 266 | ) 267 | 268 | # Start a watchdog thread to monitor the osquery process. 269 | rt = threading.Thread(target=start_watcher, args=(client, args.interval)) 270 | rt.daemon = True 271 | rt.start() 272 | 273 | # start a thrift server listening at the path dictated by the uuid returned 274 | # by the osquery core extension manager 275 | ext_manager.uuid = status.uuid 276 | processor = Processor(ext_manager) 277 | 278 | transport = None 279 | if sys.platform == 'win32': 280 | transport = TPipeServer(pipe_name="{}.{}".format(args.socket, status.uuid)) 281 | else: 282 | transport = TSocket.TServerSocket( 283 | unix_socket=args.socket + "." + str(status.uuid)) 284 | 285 | tfactory = TTransport.TBufferedTransportFactory() 286 | pfactory = TBinaryProtocol.TBinaryProtocolFactory() 287 | server = TServer.TSimpleServer(processor, transport, tfactory, pfactory) 288 | server.serve() 289 | 290 | 291 | def deregister_extension(): 292 | """Deregister the entire extension from the core extension manager""" 293 | args = parse_cli_params() 294 | client = ExtensionClient(path=args.socket) 295 | client.open() 296 | ext_manager = ExtensionManager() 297 | 298 | if ext_manager.uuid is None: 299 | raise ExtensionException( 300 | code=1, 301 | message="Extension Manager does not have a valid UUID", 302 | ) 303 | 304 | try: 305 | status = client.extension_manager_client().deregisterExtension( 306 | ext_manager.uuid) 307 | except socket.error: 308 | message = "Could not connect to %s" % args.socket 309 | raise ExtensionException( 310 | code=1, 311 | message=message, 312 | ) 313 | 314 | if status.code != 0: 315 | raise ExtensionException( 316 | code=1, 317 | message=status.message, 318 | ) 319 | 320 | 321 | def register_plugin(plugin): 322 | """Decorator wrapper used for registering a plugin class 323 | 324 | To register your plugin, add this decorator to your plugin's implementation 325 | class: 326 | 327 | @osquery.register_plugin 328 | class MyTablePlugin(osquery.TablePlugin): 329 | """ 330 | ext_manager = ExtensionManager() 331 | ext_manager.add_plugin(plugin) 332 | -------------------------------------------------------------------------------- /osquery/plugin.py: -------------------------------------------------------------------------------- 1 | """This source code is licensed under the BSD-style license found in the 2 | LICENSE file in the root directory of this source tree. An additional grant 3 | of patent rights can be found in the PATENTS file in the same directory. 4 | """ 5 | 6 | # pylint: disable=no-self-use 7 | 8 | from __future__ import absolute_import 9 | from __future__ import division 10 | from __future__ import print_function 11 | from __future__ import unicode_literals 12 | 13 | from abc import ABCMeta, abstractmethod 14 | from future.utils import with_metaclass 15 | 16 | from osquery.singleton import Singleton 17 | 18 | class BasePlugin(with_metaclass(ABCMeta, Singleton)): 19 | """All osquery plugins should inherit from BasePlugin""" 20 | 21 | @abstractmethod 22 | def call(self, context): 23 | """Call is the method that is responsible for routing a thrift request 24 | to the appropriate class method. 25 | 26 | This must be implemented by the plugin type (ie: LoggerPlugin), but 27 | explicitly not an end-user plugin type (ie: MyAwesomeLoggerPlugin) 28 | 29 | call should return an ExtensionResponse, as defined in osquery.thrift 30 | """ 31 | raise NotImplementedError 32 | 33 | @abstractmethod 34 | def name(self): 35 | """The name of your plugin. 36 | 37 | This must be implemented by your plugin. 38 | """ 39 | raise NotImplementedError 40 | 41 | def routes(self): 42 | """The routes that should be broadcasted by your plugin""" 43 | return [] 44 | -------------------------------------------------------------------------------- /osquery/singleton.py: -------------------------------------------------------------------------------- 1 | """This source code is licensed under the BSD-style license found in the 2 | LICENSE file in the root directory of this source tree. An additional grant 3 | of patent rights can be found in the PATENTS file in the same directory. 4 | """ 5 | 6 | # pylint: disable=too-few-public-methods 7 | 8 | from __future__ import absolute_import 9 | from __future__ import division 10 | from __future__ import print_function 11 | from __future__ import unicode_literals 12 | 13 | class Singleton(object): 14 | """A simple singleton base class""" 15 | _instance = None 16 | 17 | def __new__(cls, *args, **kwargs): 18 | """Override __new__ to implement custom instantiation""" 19 | if not cls._instance: 20 | cls._instance = super(Singleton, cls).__new__(cls, *args, **kwargs) 21 | return cls._instance 22 | -------------------------------------------------------------------------------- /osquery/table_plugin.py: -------------------------------------------------------------------------------- 1 | """This source code is licensed under the BSD-style license found in the 2 | LICENSE file in the root directory of this source tree. An additional grant 3 | of patent rights can be found in the PATENTS file in the same directory. 4 | """ 5 | 6 | # pylint: disable=no-self-use 7 | 8 | from __future__ import absolute_import 9 | from __future__ import division 10 | from __future__ import print_function 11 | from __future__ import unicode_literals 12 | 13 | from abc import ABCMeta, abstractmethod 14 | from builtins import str 15 | from collections import namedtuple 16 | from future.utils import with_metaclass 17 | import json 18 | import logging 19 | 20 | from osquery.extensions.ttypes import ExtensionResponse, ExtensionStatus 21 | from osquery.plugin import BasePlugin 22 | 23 | class TablePlugin(with_metaclass(ABCMeta, BasePlugin)): 24 | """All table plugins should inherit from TablePlugin""" 25 | 26 | _no_action_message = "Table plugins must include a request action" 27 | 28 | def call(self, context): 29 | """Internal routing for this plugin type. 30 | 31 | Do not override this method. 32 | """ 33 | if "action" not in context: 34 | return ExtensionResponse( 35 | status=ExtensionStatus(code=1, 36 | message=self._no_action_message,), 37 | response=[],) 38 | 39 | if context["action"] == "generate": 40 | ctx = {} 41 | if "context" in context: 42 | ctx = json.loads(context["context"]) 43 | rows = self.generate(ctx) 44 | for i, row in enumerate(rows): 45 | for key, value in row.items(): 46 | if not isinstance(value, str): 47 | try: 48 | rows[i][key] = str(value) 49 | except ValueError as e: 50 | rows[i][key] = '' 51 | logging.error("Cannot convert key %s: %s" % ( 52 | i, key, str(e))) 53 | return ExtensionResponse( 54 | status=ExtensionStatus(code=0, message="OK",), 55 | response=rows) 56 | elif context["action"] == "columns": 57 | return ExtensionResponse( 58 | status=ExtensionStatus(code=0, message="OK",), 59 | response=self.routes(),) 60 | return ExtensionResponse(code=1, message="Unknown action",) 61 | 62 | def registry_name(self): 63 | """The name of the registry type for table plugins. 64 | 65 | Do not override this method.""" 66 | return "table" 67 | 68 | def routes(self): 69 | """The routes that will be broadcasted for table plugins 70 | 71 | Do not override this method. 72 | """ 73 | routes = [] 74 | for column in self.columns(): 75 | route = { 76 | "id": "column", 77 | "name": column.name, 78 | "type": column.type, 79 | "op": "0", 80 | } 81 | routes.append(route) 82 | return routes 83 | 84 | @abstractmethod 85 | def columns(self): 86 | """The columns of your table plugin. 87 | 88 | This method should return an array of osquery.TableColumn instances. 89 | 90 | Consider the following example: 91 | 92 | class MyTablePlugin(osquery.TablePlugin): 93 | def columns(self): 94 | return [ 95 | osquery.TableColumn(name="foo", type=osquery.STRING), 96 | osquery.TableColumn(name="baz", type=osquery.STRING), 97 | ] 98 | 99 | This must be implemented by your plugin. 100 | """ 101 | raise NotImplementedError 102 | 103 | @abstractmethod 104 | def generate(self, context): 105 | """The implementation of your table plugin. 106 | 107 | This method should return a list of dictionaries, such that each 108 | dictionary has a key corresponding to each of your table's columns. 109 | 110 | Consider the following example: 111 | 112 | class MyTablePlugin(osquery.TablePlugin): 113 | def generate(self, context): 114 | query_data = [] 115 | 116 | for i in range(5): 117 | row = {} 118 | row["foo"] = "bar" 119 | row["baz"] = "boo" 120 | query_data.append(row) 121 | 122 | return query_data 123 | 124 | This must be implemented by your plugin. 125 | """ 126 | raise NotImplementedError 127 | 128 | STRING = "TEXT" 129 | """The text SQL column type""" 130 | 131 | INTEGER = "INTEGER" 132 | """The integer SQL column type""" 133 | 134 | TableColumn = namedtuple("TableColumn", ["name", "type"]) 135 | """An object which allows you to define the name and type of a SQL column""" 136 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | thrift>=0.10.0 2 | pylint>=1.4.3 3 | twine>=1.5.0 4 | wheel>=0.24.0 5 | setuptools>=15.0 6 | future 7 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | description_file = README.rst 3 | 4 | [bdist_wheel] 5 | universal=1 6 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """This source code is licensed under the BSD-style license found in the 3 | LICENSE file in the root directory of this source tree. An additional grant 4 | of patent rights can be found in the PATENTS file in the same directory. 5 | """ 6 | 7 | from os import system 8 | import re 9 | from setuptools import setup, Command 10 | 11 | class GenerateThriftCommand(Command): 12 | """Generate thrift code""" 13 | 14 | description = "Generate thrift code" 15 | user_options = [] 16 | 17 | def initialize_options(self): 18 | """Set default values for options.""" 19 | pass 20 | 21 | def finalize_options(self): 22 | """Post-process options.""" 23 | pass 24 | 25 | def run(self): 26 | """Run the command""" 27 | system("thrift -gen py -out . osquery.thrift") 28 | system("rm osquery/extensions/*-remote") 29 | system("rm __init__.py") 30 | 31 | class LintCommand(Command): 32 | """Run pylint on implementation and test code""" 33 | 34 | description = "Run pylint on implementation and test code" 35 | user_options = [] 36 | 37 | _pylint_options = [ 38 | "--max-line-length 80", 39 | "--ignore-imports yes" 40 | ] 41 | 42 | _lint_paths = [ 43 | "osquery/*.py", 44 | "tests/*.py", 45 | ] 46 | 47 | def initialize_options(self): 48 | """Set default values for options.""" 49 | pass 50 | 51 | def finalize_options(self): 52 | """Post-process options.""" 53 | pass 54 | 55 | def run(self): 56 | """Run the command""" 57 | system("pylint %s %s" % ( 58 | " ".join(self._pylint_options), 59 | " ".join(self._lint_paths), 60 | )) 61 | 62 | with open("README.md", "r") as f: 63 | README = f.read() 64 | 65 | with open("osquery/__init__.py", "r") as f: 66 | __INIT__ = f.read() 67 | 68 | TITLE = re.search(r'^__title__\s*=\s*[\'"]([^\'"]*)[\'"]', 69 | __INIT__, re.MULTILINE).group(1) 70 | VERSION = re.search(r'^__version__\s*=\s*[\'"]([^\'"]*)[\'"]', 71 | __INIT__, re.MULTILINE).group(1) 72 | AUTHOR = re.search(r'^__author__\s*=\s*[\'"]([^\'"]*)[\'"]', 73 | __INIT__, re.MULTILINE).group(1) 74 | LICENSE = re.search(r'^__license__\s*=\s*[\'"]([^\'"]*)[\'"]', 75 | __INIT__, re.MULTILINE).group(1) 76 | 77 | setup(name=TITLE, 78 | version=VERSION, 79 | description="Osquery Python API", 80 | long_description=README, 81 | long_description_content_type="text/markdown", 82 | author=AUTHOR, 83 | author_email="osquery@fb.com", 84 | url="https://osquery.io", 85 | license=LICENSE, 86 | packages=[ 87 | "osquery", 88 | "osquery.extensions", 89 | ], 90 | install_requires=[ 91 | "thrift>=0.10", 92 | "future", 93 | ], 94 | extras_require={ 95 | ':sys_platform == "win32"': ['pywin32'], 96 | }, 97 | test_suite="tests", 98 | cmdclass={ 99 | "generate": GenerateThriftCommand, 100 | "lint": LintCommand, 101 | }, 102 | classifiers=[ 103 | # https://pypi.python.org/pypi?%3Aaction=list_classifiers 104 | "Development Status :: 5 - Production/Stable", 105 | 106 | "Intended Audience :: System Administrators", 107 | "Topic :: Security", 108 | 109 | "License :: OSI Approved :: BSD License", 110 | 111 | "Programming Language :: Python :: 2", 112 | "Programming Language :: Python :: 2.7", 113 | "Programming Language :: Python :: 3", 114 | "Programming Language :: Python :: 3.3", 115 | "Programming Language :: Python :: 3.4", 116 | "Programming Language :: Python :: 3.6", 117 | ], 118 | keywords="security databases operating systems",) 119 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/osquery/osquery-python/214016774444b371d1756743c0e51be4ef62f816/tests/__init__.py -------------------------------------------------------------------------------- /tests/test_config_plugin.py: -------------------------------------------------------------------------------- 1 | """This source code is licensed under the BSD-style license found in the 2 | LICENSE file in the root directory of this source tree. An additional grant 3 | of patent rights can be found in the PATENTS file in the same directory. 4 | """ 5 | 6 | from __future__ import absolute_import 7 | from __future__ import division 8 | from __future__ import print_function 9 | from __future__ import unicode_literals 10 | 11 | import json 12 | import os 13 | import sys 14 | import unittest 15 | 16 | sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), 17 | "../build/lib/"))) 18 | import osquery 19 | 20 | class MockConfigPlugin(osquery.ConfigPlugin): 21 | """Mock config plugin for testing the config API""" 22 | 23 | def name(self): 24 | return "foobar" 25 | 26 | def content(self): 27 | return [ 28 | { 29 | "source_1": json.dumps({ 30 | "schedule": { 31 | "foo": { 32 | "query": "select * from foobar", 33 | "interval": 5, 34 | }, 35 | }, 36 | }), 37 | }, 38 | ] 39 | 40 | class TestConfigPlugin(unittest.TestCase): 41 | """Tests for osquery.ConfigPlugin""" 42 | 43 | def test_simple_call(self): 44 | """Tests for the call method of osquery.TablePlugin""" 45 | ext_manager = osquery.ExtensionManager() 46 | ext_manager.add_plugin(MockConfigPlugin) 47 | response = ext_manager.call("config", 48 | "foobar", 49 | {"action":"genConfig"}) 50 | self.assertEqual(0, response.status.code) 51 | self.assertTrue(len(response.response) > 0) 52 | self.assertTrue("source_1" in response.response[0]) 53 | 54 | if __name__ == '__main__': 55 | unittest.main() 56 | -------------------------------------------------------------------------------- /tests/test_logger_plugin.py: -------------------------------------------------------------------------------- 1 | """This source code is licensed under the BSD-style license found in the 2 | LICENSE file in the root directory of this source tree. An additional grant 3 | of patent rights can be found in the PATENTS file in the same directory. 4 | """ 5 | 6 | from __future__ import absolute_import 7 | from __future__ import division 8 | from __future__ import print_function 9 | from __future__ import unicode_literals 10 | 11 | import os 12 | import sys 13 | import unittest 14 | 15 | sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), 16 | "../build/lib/"))) 17 | 18 | import osquery 19 | 20 | class MockLoggerPlugin(osquery.LoggerPlugin): 21 | """Mock logger plugin for testing the logger API""" 22 | 23 | logs = [] 24 | 25 | def name(self): 26 | return "foobar" 27 | 28 | def log_string(self, value): 29 | self.logs.append(value) 30 | 31 | class TestLoggerPlugin(unittest.TestCase): 32 | """Tests for osquery.LoggerPlugin""" 33 | 34 | def test_simple_call(self): 35 | """Tests for the call method of osquery.TablePlugin""" 36 | ext_manager = osquery.ExtensionManager() 37 | ext_manager.add_plugin(MockLoggerPlugin) 38 | test_log_string = "test_log_string" 39 | ext_manager.call("logger", 40 | "foobar", 41 | {"string": test_log_string}) 42 | mlp = MockLoggerPlugin() 43 | self.assertTrue(test_log_string in mlp.logs) 44 | 45 | if __name__ == '__main__': 46 | unittest.main() 47 | -------------------------------------------------------------------------------- /tests/test_plugin.py: -------------------------------------------------------------------------------- 1 | """This source code is licensed under the BSD-style license found in the 2 | LICENSE file in the root directory of this source tree. An additional grant 3 | of patent rights can be found in the PATENTS file in the same directory. 4 | """ 5 | 6 | from __future__ import absolute_import 7 | from __future__ import division 8 | from __future__ import print_function 9 | from __future__ import unicode_literals 10 | 11 | import os 12 | import sys 13 | import unittest 14 | 15 | sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), 16 | "../build/lib/"))) 17 | 18 | import osquery 19 | 20 | class TestBasePlugin(unittest.TestCase): 21 | """Tests for osquery.Singleton""" 22 | 23 | class SimplePlugin(osquery.BasePlugin): 24 | """A simple plugin for testing the osquery.BasePlugin class""" 25 | 26 | def call(self, context): 27 | return None 28 | 29 | def name(self): 30 | return "pass" 31 | 32 | def test_plugin_inheritance(self): 33 | """Test that an object derived from BasePlugin works properly""" 34 | simple_plugin = self.SimplePlugin() 35 | self.assertEqual(simple_plugin.routes(), []) 36 | 37 | if __name__ == '__main__': 38 | unittest.main() 39 | -------------------------------------------------------------------------------- /tests/test_singleton.py: -------------------------------------------------------------------------------- 1 | """This source code is licensed under the BSD-style license found in the 2 | LICENSE file in the root directory of this source tree. An additional grant 3 | of patent rights can be found in the PATENTS file in the same directory. 4 | """ 5 | 6 | # pylint: disable=too-few-public-methods 7 | 8 | from __future__ import absolute_import 9 | from __future__ import division 10 | from __future__ import print_function 11 | from __future__ import unicode_literals 12 | 13 | import os 14 | import sys 15 | import unittest 16 | 17 | sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), 18 | "../build/lib/"))) 19 | 20 | import osquery 21 | 22 | class TestSingleton(unittest.TestCase): 23 | """Tests for osquery.Singleton""" 24 | 25 | class MockSingleton(osquery.Singleton): 26 | """Mock singleton class for testing""" 27 | pass 28 | 29 | def test_singleton_creation(self): 30 | """Test that two singletons are the same object""" 31 | singleton_a = self.MockSingleton() 32 | singleton_b = self.MockSingleton() 33 | self.assertEqual(id(singleton_a), id(singleton_b)) 34 | 35 | if __name__ == '__main__': 36 | unittest.main() 37 | -------------------------------------------------------------------------------- /tests/test_table_plugin.py: -------------------------------------------------------------------------------- 1 | """This source code is licensed under the BSD-style license found in the 2 | LICENSE file in the root directory of this source tree. An additional grant 3 | of patent rights can be found in the PATENTS file in the same directory. 4 | """ 5 | 6 | from __future__ import absolute_import 7 | from __future__ import division 8 | from __future__ import print_function 9 | from __future__ import unicode_literals 10 | 11 | import os 12 | import sys 13 | import unittest 14 | 15 | sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), 16 | "../build/lib/"))) 17 | 18 | import osquery 19 | 20 | class MockTablePlugin(osquery.TablePlugin): 21 | """Mock table plugin for testing the table API""" 22 | def name(self): 23 | return "foobar" 24 | 25 | def columns(self): 26 | return [ 27 | osquery.TableColumn(name="foo", type=osquery.STRING), 28 | osquery.TableColumn(name="baz", type=osquery.STRING), 29 | osquery.TableColumn(name="int", type=osquery.INTEGER), 30 | ] 31 | 32 | def generate(self, context): 33 | query_data = [] 34 | 35 | for _ in range(2): 36 | row = {} 37 | row["foo"] = "bar" 38 | row["baz"] = "baz" 39 | row["int"] = 42 40 | query_data.append(row) 41 | 42 | return query_data 43 | 44 | class TestTablePlugin(unittest.TestCase): 45 | """Tests for osquery.TablePlugin""" 46 | 47 | def test_plugin_was_registered(self): 48 | """Tests to ensure that a plugin was registered""" 49 | osquery.ExtensionManager().add_plugin(MockTablePlugin) 50 | registry = osquery.ExtensionManager().registry() 51 | self.assertTrue("table" in registry) 52 | self.assertTrue("foobar" in registry["table"]) 53 | 54 | def test_routes_are_correct(self): 55 | """Tests to ensure that a plugins routes are correct""" 56 | expected = [ 57 | { 58 | "id": "column", 59 | "op": "0", 60 | "type": "TEXT", 61 | "name": "foo", 62 | }, 63 | { 64 | "id": "column", 65 | "op": "0", 66 | "type": "TEXT", 67 | "name": "baz", 68 | }, 69 | { 70 | "id": "column", 71 | "op": "0", 72 | "type": "INTEGER", 73 | "name": "int", 74 | }, 75 | ] 76 | osquery.ExtensionManager().add_plugin(MockTablePlugin) 77 | mtp = MockTablePlugin() 78 | self.assertEqual(expected, mtp.routes()) 79 | 80 | def test_simple_call(self): 81 | """Tests for the call method of osquery.TablePlugin""" 82 | ext_manager = osquery.ExtensionManager() 83 | results = ext_manager.call("table", "foobar", {"action":"generate"}) 84 | expected = [ 85 | { 86 | "foo": "bar", 87 | "baz": "baz", 88 | "int": "42", 89 | }, 90 | { 91 | "foo": "bar", 92 | "baz": "baz", 93 | "int": "42", 94 | }, 95 | ] 96 | self.assertEqual(results.response, expected) 97 | 98 | if __name__ == '__main__': 99 | unittest.main() 100 | -------------------------------------------------------------------------------- /win-requirements.txt: -------------------------------------------------------------------------------- 1 | pypiwin32 2 | --------------------------------------------------------------------------------