diff --git a/osquery.thrift b/osquery.thrift index 8103c5d..f432dfa 100644 --- a/osquery.thrift +++ b/osquery.thrift @@ -69,6 +69,8 @@ service Extension { 2:string item, /// The thrift-equivilent of an osquery::PluginRequest. 3:ExtensionPluginRequest request), + /// Request that an extension shutdown (does not apply to managers). + void shutdown(), } /// The extension manager is run by the osquery core process. diff --git a/osquery/extension_manager.py b/osquery/extension_manager.py index 26f4a49..7b56581 100644 --- a/osquery/extension_manager.py +++ b/osquery/extension_manager.py @@ -8,6 +8,8 @@ from __future__ import print_function from __future__ import unicode_literals +import sys + from osquery.extensions.Extension import Iface from osquery.extensions.ttypes import ExtensionResponse, ExtensionStatus from osquery.singleton import Singleton @@ -60,6 +62,10 @@ def add_plugin(self, plugin): if obj.name() not in self._plugins[obj.registry_name()]: self._plugins[obj.registry_name()][obj.name()] = obj + def shutdown(self): + """The osquery extension manager requested a shutdown""" + sys.exit(0) + def registry(self): """Accessor for the internal _registry member variable""" return self._registry diff --git a/osquery/extensions/Extension.py b/osquery/extensions/Extension.py index 4d1518d..1ae35da 100644 --- a/osquery/extensions/Extension.py +++ b/osquery/extensions/Extension.py @@ -1,5 +1,5 @@ # -# Autogenerated by Thrift Compiler (0.9.2) +# Autogenerated by Thrift Compiler (0.9.3) # # DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING # @@ -7,7 +7,8 @@ # from thrift.Thrift import TType, TMessageType, TException, TApplicationException -from osquery.extensions.ttypes import * +import logging +from ttypes import * from thrift.Thrift import TProcessor from thrift.transport import TTransport from thrift.protocol import TBinaryProtocol, TProtocol @@ -30,6 +31,9 @@ def call(self, registry, item, request): """ pass + def shutdown(self): + pass + class Client(Iface): def __init__(self, iprot, oprot=None): @@ -62,7 +66,7 @@ def recv_ping(self): iprot.readMessageEnd() if result.success is not None: return result.success - raise TApplicationException(TApplicationException.MISSING_RESULT, "ping failed: unknown result"); + raise TApplicationException(TApplicationException.MISSING_RESULT, "ping failed: unknown result") def call(self, registry, item, request): """ @@ -97,7 +101,31 @@ def recv_call(self): iprot.readMessageEnd() if result.success is not None: return result.success - raise TApplicationException(TApplicationException.MISSING_RESULT, "call failed: unknown result"); + raise TApplicationException(TApplicationException.MISSING_RESULT, "call failed: unknown result") + + def shutdown(self): + self.send_shutdown() + self.recv_shutdown() + + def send_shutdown(self): + self._oprot.writeMessageBegin('shutdown', TMessageType.CALL, self._seqid) + args = shutdown_args() + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_shutdown(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = shutdown_result() + result.read(iprot) + iprot.readMessageEnd() + return class Processor(Iface, TProcessor): @@ -106,6 +134,7 @@ def __init__(self, handler): self._processMap = {} self._processMap["ping"] = Processor.process_ping self._processMap["call"] = Processor.process_call + self._processMap["shutdown"] = Processor.process_shutdown def process(self, iprot, oprot): (name, type, seqid) = iprot.readMessageBegin() @@ -127,8 +156,16 @@ def process_ping(self, seqid, iprot, oprot): args.read(iprot) iprot.readMessageEnd() result = ping_result() - result.success = self._handler.ping() - oprot.writeMessageBegin("ping", TMessageType.REPLY, seqid) + try: + result.success = self._handler.ping() + msg_type = TMessageType.REPLY + except (TTransport.TTransportException, KeyboardInterrupt, SystemExit): + raise + except Exception as ex: + msg_type = TMessageType.EXCEPTION + logging.exception(ex) + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("ping", msg_type, seqid) result.write(oprot) oprot.writeMessageEnd() oprot.trans.flush() @@ -138,8 +175,35 @@ def process_call(self, seqid, iprot, oprot): args.read(iprot) iprot.readMessageEnd() result = call_result() - result.success = self._handler.call(args.registry, args.item, args.request) - oprot.writeMessageBegin("call", TMessageType.REPLY, seqid) + try: + result.success = self._handler.call(args.registry, args.item, args.request) + msg_type = TMessageType.REPLY + except (TTransport.TTransportException, KeyboardInterrupt, SystemExit): + raise + except Exception as ex: + msg_type = TMessageType.EXCEPTION + logging.exception(ex) + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("call", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_shutdown(self, seqid, iprot, oprot): + args = shutdown_args() + args.read(iprot) + iprot.readMessageEnd() + result = shutdown_result() + try: + self._handler.shutdown() + msg_type = TMessageType.REPLY + except (TTransport.TTransportException, KeyboardInterrupt, SystemExit): + raise + except Exception as ex: + msg_type = TMessageType.EXCEPTION + logging.exception(ex) + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("shutdown", msg_type, seqid) result.write(oprot) oprot.writeMessageEnd() oprot.trans.flush() @@ -289,12 +353,12 @@ def read(self, iprot): break if fid == 1: if ftype == TType.STRING: - self.registry = iprot.readString(); + self.registry = iprot.readString() else: iprot.skip(ftype) elif fid == 2: if ftype == TType.STRING: - self.item = iprot.readString(); + self.item = iprot.readString() else: iprot.skip(ftype) elif fid == 3: @@ -302,8 +366,8 @@ def read(self, iprot): self.request = {} (_ktype17, _vtype18, _size16 ) = iprot.readMapBegin() for _i20 in xrange(_size16): - _key21 = iprot.readString(); - _val22 = iprot.readString(); + _key21 = iprot.readString() + _val22 = iprot.readString() self.request[_key21] = _val22 iprot.readMapEnd() else: @@ -423,3 +487,95 @@ def __eq__(self, other): def __ne__(self, other): return not (self == other) + +class shutdown_args: + + thrift_spec = ( + ) + + def read(self, iprot): + if iprot.__class__ == TBinaryProtocol.TBinaryProtocolAccelerated and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None and fastbinary is not None: + fastbinary.decode_binary(self, iprot.trans, (self.__class__, self.thrift_spec)) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot.__class__ == TBinaryProtocol.TBinaryProtocolAccelerated and self.thrift_spec is not None and fastbinary is not None: + oprot.trans.write(fastbinary.encode_binary(self, (self.__class__, self.thrift_spec))) + return + oprot.writeStructBegin('shutdown_args') + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + + def __hash__(self): + value = 17 + return value + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.iteritems()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) + +class shutdown_result: + + thrift_spec = ( + ) + + def read(self, iprot): + if iprot.__class__ == TBinaryProtocol.TBinaryProtocolAccelerated and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None and fastbinary is not None: + fastbinary.decode_binary(self, iprot.trans, (self.__class__, self.thrift_spec)) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot.__class__ == TBinaryProtocol.TBinaryProtocolAccelerated and self.thrift_spec is not None and fastbinary is not None: + oprot.trans.write(fastbinary.encode_binary(self, (self.__class__, self.thrift_spec))) + return + oprot.writeStructBegin('shutdown_result') + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + + def __hash__(self): + value = 17 + return value + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.iteritems()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) diff --git a/osquery/extensions/ExtensionManager.py b/osquery/extensions/ExtensionManager.py index 8e61d5d..607bf67 100644 --- a/osquery/extensions/ExtensionManager.py +++ b/osquery/extensions/ExtensionManager.py @@ -1,5 +1,5 @@ # -# Autogenerated by Thrift Compiler (0.9.2) +# Autogenerated by Thrift Compiler (0.9.3) # # DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING # @@ -8,7 +8,8 @@ from thrift.Thrift import TType, TMessageType, TException, TApplicationException import osquery.extensions.Extension -from osquery.extensions.ttypes import * +import logging +from ttypes import * from thrift.Thrift import TProcessor from thrift.transport import TTransport from thrift.protocol import TBinaryProtocol, TProtocol @@ -83,7 +84,7 @@ def recv_extensions(self): iprot.readMessageEnd() if result.success is not None: return result.success - raise TApplicationException(TApplicationException.MISSING_RESULT, "extensions failed: unknown result"); + raise TApplicationException(TApplicationException.MISSING_RESULT, "extensions failed: unknown result") def options(self): self.send_options() @@ -109,7 +110,7 @@ def recv_options(self): iprot.readMessageEnd() if result.success is not None: return result.success - raise TApplicationException(TApplicationException.MISSING_RESULT, "options failed: unknown result"); + raise TApplicationException(TApplicationException.MISSING_RESULT, "options failed: unknown result") def registerExtension(self, info, registry): """ @@ -142,7 +143,7 @@ def recv_registerExtension(self): iprot.readMessageEnd() if result.success is not None: return result.success - raise TApplicationException(TApplicationException.MISSING_RESULT, "registerExtension failed: unknown result"); + raise TApplicationException(TApplicationException.MISSING_RESULT, "registerExtension failed: unknown result") def deregisterExtension(self, uuid): """ @@ -173,7 +174,7 @@ def recv_deregisterExtension(self): iprot.readMessageEnd() if result.success is not None: return result.success - raise TApplicationException(TApplicationException.MISSING_RESULT, "deregisterExtension failed: unknown result"); + raise TApplicationException(TApplicationException.MISSING_RESULT, "deregisterExtension failed: unknown result") def query(self, sql): """ @@ -204,7 +205,7 @@ def recv_query(self): iprot.readMessageEnd() if result.success is not None: return result.success - raise TApplicationException(TApplicationException.MISSING_RESULT, "query failed: unknown result"); + raise TApplicationException(TApplicationException.MISSING_RESULT, "query failed: unknown result") def getQueryColumns(self, sql): """ @@ -235,7 +236,7 @@ def recv_getQueryColumns(self): iprot.readMessageEnd() if result.success is not None: return result.success - raise TApplicationException(TApplicationException.MISSING_RESULT, "getQueryColumns failed: unknown result"); + raise TApplicationException(TApplicationException.MISSING_RESULT, "getQueryColumns failed: unknown result") class Processor(osquery.extensions.Extension.Processor, Iface, TProcessor): @@ -268,8 +269,16 @@ def process_extensions(self, seqid, iprot, oprot): args.read(iprot) iprot.readMessageEnd() result = extensions_result() - result.success = self._handler.extensions() - oprot.writeMessageBegin("extensions", TMessageType.REPLY, seqid) + try: + result.success = self._handler.extensions() + msg_type = TMessageType.REPLY + except (TTransport.TTransportException, KeyboardInterrupt, SystemExit): + raise + except Exception as ex: + msg_type = TMessageType.EXCEPTION + logging.exception(ex) + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("extensions", msg_type, seqid) result.write(oprot) oprot.writeMessageEnd() oprot.trans.flush() @@ -279,8 +288,16 @@ def process_options(self, seqid, iprot, oprot): args.read(iprot) iprot.readMessageEnd() result = options_result() - result.success = self._handler.options() - oprot.writeMessageBegin("options", TMessageType.REPLY, seqid) + try: + result.success = self._handler.options() + msg_type = TMessageType.REPLY + except (TTransport.TTransportException, KeyboardInterrupt, SystemExit): + raise + except Exception as ex: + msg_type = TMessageType.EXCEPTION + logging.exception(ex) + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("options", msg_type, seqid) result.write(oprot) oprot.writeMessageEnd() oprot.trans.flush() @@ -290,8 +307,16 @@ def process_registerExtension(self, seqid, iprot, oprot): args.read(iprot) iprot.readMessageEnd() result = registerExtension_result() - result.success = self._handler.registerExtension(args.info, args.registry) - oprot.writeMessageBegin("registerExtension", TMessageType.REPLY, seqid) + try: + result.success = self._handler.registerExtension(args.info, args.registry) + msg_type = TMessageType.REPLY + except (TTransport.TTransportException, KeyboardInterrupt, SystemExit): + raise + except Exception as ex: + msg_type = TMessageType.EXCEPTION + logging.exception(ex) + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("registerExtension", msg_type, seqid) result.write(oprot) oprot.writeMessageEnd() oprot.trans.flush() @@ -301,8 +326,16 @@ def process_deregisterExtension(self, seqid, iprot, oprot): args.read(iprot) iprot.readMessageEnd() result = deregisterExtension_result() - result.success = self._handler.deregisterExtension(args.uuid) - oprot.writeMessageBegin("deregisterExtension", TMessageType.REPLY, seqid) + try: + result.success = self._handler.deregisterExtension(args.uuid) + msg_type = TMessageType.REPLY + except (TTransport.TTransportException, KeyboardInterrupt, SystemExit): + raise + except Exception as ex: + msg_type = TMessageType.EXCEPTION + logging.exception(ex) + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("deregisterExtension", msg_type, seqid) result.write(oprot) oprot.writeMessageEnd() oprot.trans.flush() @@ -312,8 +345,16 @@ def process_query(self, seqid, iprot, oprot): args.read(iprot) iprot.readMessageEnd() result = query_result() - result.success = self._handler.query(args.sql) - oprot.writeMessageBegin("query", TMessageType.REPLY, seqid) + try: + result.success = self._handler.query(args.sql) + msg_type = TMessageType.REPLY + except (TTransport.TTransportException, KeyboardInterrupt, SystemExit): + raise + except Exception as ex: + msg_type = TMessageType.EXCEPTION + logging.exception(ex) + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("query", msg_type, seqid) result.write(oprot) oprot.writeMessageEnd() oprot.trans.flush() @@ -323,8 +364,16 @@ def process_getQueryColumns(self, seqid, iprot, oprot): args.read(iprot) iprot.readMessageEnd() result = getQueryColumns_result() - result.success = self._handler.getQueryColumns(args.sql) - oprot.writeMessageBegin("getQueryColumns", TMessageType.REPLY, seqid) + try: + result.success = self._handler.getQueryColumns(args.sql) + msg_type = TMessageType.REPLY + except (TTransport.TTransportException, KeyboardInterrupt, SystemExit): + raise + except Exception as ex: + msg_type = TMessageType.EXCEPTION + logging.exception(ex) + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("getQueryColumns", msg_type, seqid) result.write(oprot) oprot.writeMessageEnd() oprot.trans.flush() @@ -405,7 +454,7 @@ def read(self, iprot): self.success = {} (_ktype26, _vtype27, _size25 ) = iprot.readMapBegin() for _i29 in xrange(_size25): - _key30 = iprot.readI64(); + _key30 = iprot.readI64() _val31 = InternalExtensionInfo() _val31.read(iprot) self.success[_key30] = _val31 @@ -526,7 +575,7 @@ def read(self, iprot): self.success = {} (_ktype35, _vtype36, _size34 ) = iprot.readMapBegin() for _i38 in xrange(_size34): - _key39 = iprot.readString(); + _key39 = iprot.readString() _val40 = InternalOptionInfo() _val40.read(iprot) self.success[_key39] = _val40 @@ -611,19 +660,19 @@ def read(self, iprot): self.registry = {} (_ktype44, _vtype45, _size43 ) = iprot.readMapBegin() for _i47 in xrange(_size43): - _key48 = iprot.readString(); + _key48 = iprot.readString() _val49 = {} (_ktype51, _vtype52, _size50 ) = iprot.readMapBegin() for _i54 in xrange(_size50): - _key55 = iprot.readString(); + _key55 = iprot.readString() _val56 = [] (_etype60, _size57) = iprot.readListBegin() for _i61 in xrange(_size57): _elem62 = {} (_ktype64, _vtype65, _size63 ) = iprot.readMapBegin() for _i67 in xrange(_size63): - _key68 = iprot.readString(); - _val69 = iprot.readString(); + _key68 = iprot.readString() + _val69 = iprot.readString() _elem62[_key68] = _val69 iprot.readMapEnd() _val56.append(_elem62) @@ -781,7 +830,7 @@ def read(self, iprot): break if fid == 1: if ftype == TType.I64: - self.uuid = iprot.readI64(); + self.uuid = iprot.readI64() else: iprot.skip(ftype) else: @@ -911,7 +960,7 @@ def read(self, iprot): break if fid == 1: if ftype == TType.STRING: - self.sql = iprot.readString(); + self.sql = iprot.readString() else: iprot.skip(ftype) else: @@ -1041,7 +1090,7 @@ def read(self, iprot): break if fid == 1: if ftype == TType.STRING: - self.sql = iprot.readString(); + self.sql = iprot.readString() else: iprot.skip(ftype) else: diff --git a/osquery/extensions/constants.py b/osquery/extensions/constants.py index 99717a9..4a6492b 100644 --- a/osquery/extensions/constants.py +++ b/osquery/extensions/constants.py @@ -1,5 +1,5 @@ # -# Autogenerated by Thrift Compiler (0.9.2) +# Autogenerated by Thrift Compiler (0.9.3) # # DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING # diff --git a/osquery/extensions/ttypes.py b/osquery/extensions/ttypes.py index 390b9a1..63b26b5 100644 --- a/osquery/extensions/ttypes.py +++ b/osquery/extensions/ttypes.py @@ -1,5 +1,5 @@ # -# Autogenerated by Thrift Compiler (0.9.2) +# Autogenerated by Thrift Compiler (0.9.3) # # DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING # @@ -65,17 +65,17 @@ def read(self, iprot): break if fid == 1: if ftype == TType.STRING: - self.value = iprot.readString(); + self.value = iprot.readString() else: iprot.skip(ftype) elif fid == 2: if ftype == TType.STRING: - self.default_value = iprot.readString(); + self.default_value = iprot.readString() else: iprot.skip(ftype) elif fid == 3: if ftype == TType.STRING: - self.type = iprot.readString(); + self.type = iprot.readString() else: iprot.skip(ftype) else: @@ -159,22 +159,22 @@ def read(self, iprot): break if fid == 1: if ftype == TType.STRING: - self.name = iprot.readString(); + self.name = iprot.readString() else: iprot.skip(ftype) elif fid == 2: if ftype == TType.STRING: - self.version = iprot.readString(); + self.version = iprot.readString() else: iprot.skip(ftype) elif fid == 3: if ftype == TType.STRING: - self.sdk_version = iprot.readString(); + self.sdk_version = iprot.readString() else: iprot.skip(ftype) elif fid == 4: if ftype == TType.STRING: - self.min_sdk_version = iprot.readString(); + self.min_sdk_version = iprot.readString() else: iprot.skip(ftype) else: @@ -260,17 +260,17 @@ def read(self, iprot): break if fid == 1: if ftype == TType.I32: - self.code = iprot.readI32(); + self.code = iprot.readI32() else: iprot.skip(ftype) elif fid == 2: if ftype == TType.STRING: - self.message = iprot.readString(); + self.message = iprot.readString() else: iprot.skip(ftype) elif fid == 3: if ftype == TType.I64: - self.uuid = iprot.readI64(); + self.uuid = iprot.readI64() else: iprot.skip(ftype) else: @@ -360,8 +360,8 @@ def read(self, iprot): _elem5 = {} (_ktype7, _vtype8, _size6 ) = iprot.readMapBegin() for _i10 in xrange(_size6): - _key11 = iprot.readString(); - _val12 = iprot.readString(); + _key11 = iprot.readString() + _val12 = iprot.readString() _elem5[_key11] = _val12 iprot.readMapEnd() self.response.append(_elem5) @@ -448,17 +448,17 @@ def read(self, iprot): break if fid == 1: if ftype == TType.I32: - self.code = iprot.readI32(); + self.code = iprot.readI32() else: iprot.skip(ftype) elif fid == 2: if ftype == TType.STRING: - self.message = iprot.readString(); + self.message = iprot.readString() else: iprot.skip(ftype) elif fid == 3: if ftype == TType.I64: - self.uuid = iprot.readI64(); + self.uuid = iprot.readI64() else: iprot.skip(ftype) else: diff --git a/osquery/management.py b/osquery/management.py index 7cf1eb9..d37401a 100644 --- a/osquery/management.py +++ b/osquery/management.py @@ -144,8 +144,8 @@ def parse_cli_params(): help="Enable verbose informational messages") return parser.parse_args() -def start_extension(name="", version="0.0.0", sdk_version="1.7.5", - min_sdk_version="1.7.5"): +def start_extension(name="", version="0.0.0", sdk_version="1.8.0", + min_sdk_version="1.8.0"): """Start your extension by communicating with osquery core and starting a thrift server. diff --git a/tests/test_table_plugin.py b/tests/test_table_plugin.py index 8716d3b..3ddd4ec 100644 --- a/tests/test_table_plugin.py +++ b/tests/test_table_plugin.py @@ -53,10 +53,14 @@ def test_routes_are_correct(self): """Tests to ensure that a plugins routes are correct""" expected = [ { + "id": "column", + "op": "0", "type": "TEXT", "name": "foo", }, { + "id": "column", + "op": "0", "type": "TEXT", "name": "baz", },