def testExecutePythonEnvironment(self): """Test the basic ExecutePython action.""" python_code = """ import StringIO import uu def decode(encoded): # Use the import (uu) inside a function. This will fail if the environment # for exec is not set up properly. i = StringIO.StringIO(s) o = StringIO.StringIO() uu.decode(i, o) return o.getvalue() s = "626567696e20363636202d0a2c3226354c3b265c4035565d523b2630410a200a656e640a" s = s.decode("hex") magic_return_str = decode(s) """ signed_blob = rdf_crypto.SignedBlob() signed_blob.Sign(python_code, self.signing_key) request = rdf_client.ExecutePythonRequest(python_code=signed_blob) result = self.RunAction(standard.ExecutePython, request)[0] self.assertTrue(result.time_used > 0) self.assertEqual(result.return_val, "Hello World!")
def NewFromContent(cls, content, urn, chunk_size=1024, token=None, private_key=None, public_key=None, prompt=True): """Alternate constructor for GRRSignedBlob. Creates a GRRSignedBlob from a content string by chunking it and signing each chunk. Args: content: The data to stored in the GRRSignedBlob. urn: The AFF4 URN to create. chunk_size: Data will be chunked into this size (each chunk is individually signed. token: The ACL Token. private_key: An rdf_crypto.PEMPrivateKey() instance. public_key: An rdf_crypto.PEMPublicKey() instance. prompt: If True we may present a prompt to unlock the key. Returns: the URN of the new object written. """ with aff4.FACTORY.Create(urn, cls, mode="w", token=token) as fd: for start_of_chunk in xrange(0, len(content), chunk_size): chunk = content[start_of_chunk:start_of_chunk + chunk_size] blob_rdf = rdf_crypto.SignedBlob() blob_rdf.Sign(chunk, private_key, public_key, prompt=prompt) fd.Add(blob_rdf) return urn
def testExecuteModifiedPython(self): """Test that rejects invalid ExecutePython action.""" utils.TEST_VAL = "original" python_code = "utils.TEST_VAL = 'modified'" signed_blob = rdf_crypto.SignedBlob() signed_blob.Sign(python_code, self.signing_key) # Modify the data so the signature does not match. signed_blob.data = "utils.TEST_VAL = 'notmodified'" request = rdf_client.ExecutePythonRequest(python_code=signed_blob) # Should raise since the code has been modified. self.assertRaises(rdf_crypto.VerificationError, self.RunAction, standard.ExecutePython, request) # Lets also adjust the hash. signed_blob.digest = hashlib.sha256(signed_blob.data).digest() request = rdf_client.ExecutePythonRequest(python_code=signed_blob) self.assertRaises(rdf_crypto.VerificationError, self.RunAction, standard.ExecutePython, request) # Make sure the code never ran. self.assertEqual(utils.TEST_VAL, "original")
def SignComponent(component_filename, overwrite=False, token=None): """Sign and upload the component to the data store.""" print "Signing and uploading component %s" % component_filename serialized_component = open(component_filename, "rb").read() component = rdf_client.ClientComponent.FromSerializedString( serialized_component) print "Opened component %s." % component.summary.name client_context = ["Platform:%s" % component.build_system.system.title(), "Arch:%s" % component.build_system.arch] sig_key = config_lib.CONFIG.Get("PrivateKeys.executable_signing_private_key", context=client_context) ver_key = config_lib.CONFIG.Get("Client.executable_signing_public_key", context=client_context) # For each platform specific component, we have a component summary object # which contains high level information in common to all components of this # specific version. component_urn = config_lib.CONFIG.Get("Config.aff4_root").Add( "components").Add("%s_%s" % (component.summary.name, component.summary.version)) component_fd = aff4.FACTORY.Create( component_urn, collects.ComponentObject, mode="rw", token=token) component_summary = component_fd.Get(component_fd.Schema.COMPONENT) if overwrite or component_summary is None: print "Storing component summary at %s" % component_urn component_summary = component.summary component_summary.seed = "%x%x" % (time.time(), utils.PRNG.GetULong()) component_summary.url = ( config_lib.CONFIG.Get("Client.component_url_stem", context=client_context) + component_summary.seed) component_fd.Set(component_fd.Schema.COMPONENT, component_summary) component_fd.Close() else: print "Using seed from stored component summary at %s" % component_urn component.summary.url = component_summary.url component.summary.seed = component_summary.seed # Sign the component, encrypt it and store it at the static aff4 location. signed_component = rdf_crypto.SignedBlob() signed_component.Sign(component.SerializeToString(), sig_key, ver_key) aff4_urn = config_lib.CONFIG.Get( "Client.component_aff4_stem", context=client_context).Add( component.summary.seed).Add(component.build_system.signature()) print "Storing signed component at %s" % aff4_urn with aff4.FACTORY.Create(aff4_urn, aff4.AFF4MemoryStream, token=token) as fd: fd.Write( component_summary.cipher.Encrypt(signed_component.SerializeToString())) return component
def testExecuteBrokenPython(self): """Test broken code raises back to the original flow.""" python_code = "raise ValueError" signed_blob = rdf_crypto.SignedBlob() signed_blob.Sign(python_code, self.signing_key) request = rdf_client.ExecutePythonRequest(python_code=signed_blob) self.assertRaises(ValueError, self.RunAction, "ExecutePython", request)
def testReturnVals(self): """Test return values.""" python_code = "magic_return_str = 'return string'" signed_blob = rdf_crypto.SignedBlob() signed_blob.Sign(python_code, self.signing_key) request = rdf_client.ExecutePythonRequest(python_code=signed_blob) result = self.RunAction(standard.ExecutePython, request)[0] self.assertEqual(result.return_val, "return string")
def testWrongKey(self): """Test return values.""" python_code = "print 'test'" # Generate a test valid RSA key that isn't the real one. signing_key = rdf_crypto.RSAPrivateKey.GenerateKey(2048, 65537) signed_blob = rdf_crypto.SignedBlob() signed_blob.Sign(python_code, signing_key) request = rdf_client.ExecutePythonRequest(python_code=signed_blob) self.assertRaises(rdf_crypto.VerificationError, self.RunAction, standard.ExecutePython, request)
def testExecutePython(self): """Test the basic ExecutePython action.""" utils.TEST_VAL = "original" python_code = "utils.TEST_VAL = 'modified'" signed_blob = rdf_crypto.SignedBlob() signed_blob.Sign(python_code, self.signing_key) request = rdf_client.ExecutePythonRequest(python_code=signed_blob) result = self.RunAction(standard.ExecutePython, request)[0] self.assertTrue(result.time_used > 0) self.assertEqual(result.return_val, "") self.assertEqual(utils.TEST_VAL, "modified")
def testExecuteBinary(self): """Test the basic ExecuteBinaryCommand action.""" signed_blob = rdf_crypto.SignedBlob() signed_blob.Sign(open("/bin/ls", "rb").read(), self.signing_key) request = rdf_client.ExecuteBinaryRequest(executable=signed_blob, args=[__file__], write_path="ablob") result = self.RunAction(standard.ExecuteBinaryCommand, request)[0] self.assertTrue(result.time_used > 0) self.assertTrue(__file__ in result.stdout)
def testExecuteBinary(self): """Test the basic ExecuteBinaryCommand action.""" signed_blob = rdf_crypto.SignedBlob() signed_blob.Sign(open("/bin/ls", "rb").read(), self.signing_key) writefile = utils.Join(self.temp_dir, "binexecute", "ablob") os.makedirs(os.path.dirname(writefile)) request = rdf_client.ExecuteBinaryRequest( executable=signed_blob, args=[__file__], write_path=writefile) result = self.RunAction(standard.ExecuteBinaryCommand, request)[0] self.assertTrue(result.time_used > 0) self.assertTrue(__file__ in result.stdout)
def testArgs(self): """Test passing arguments.""" utils.TEST_VAL = "original" python_code = """ magic_return_str = py_args['test'] utils.TEST_VAL = py_args[43] """ signed_blob = rdf_crypto.SignedBlob() signed_blob.Sign(python_code, self.signing_key) pdict = rdf_protodict.Dict({"test": "dict_arg", 43: "dict_arg2"}) request = rdf_client.ExecutePythonRequest(python_code=signed_blob, py_args=pdict) result = self.RunAction(standard.ExecutePython, request)[0] self.assertEqual(result.return_val, "dict_arg") self.assertEqual(utils.TEST_VAL, "dict_arg2")
def testStdoutHooking(self): python_code = """ def f(n): print "F called:", n print "Calling f." f(1) print "Done." """ signed_blob = rdf_crypto.SignedBlob() signed_blob.Sign(python_code, self.signing_key) request = rdf_client.ExecutePythonRequest(python_code=signed_blob) result = self.RunAction(standard.ExecutePython, request)[0] self.assertTrue(result.time_used > 0) self.assertEqual(result.return_val, "Calling f.\nF called: 1\nDone.\n")
def UploadSignedConfigBlob(content, aff4_path, client_context=None, limit=None, token=None): """Upload a signed blob into the datastore. Args: content: File content to upload. aff4_path: aff4 path to upload to. client_context: The configuration contexts to use. limit: The maximum size of the chunk to use. token: A security token. Raises: IOError: On failure to write. """ if limit is None: limit = config_lib.CONFIG["Datastore.maximum_blob_size"] # Get the values of these parameters which apply to the client running on the # target platform. if client_context is None: # Default to the windows client. client_context = ["Platform:Windows", "Client Context"] config_lib.CONFIG.Validate( parameters="PrivateKeys.executable_signing_private_key") sig_key = config_lib.CONFIG.Get( "PrivateKeys.executable_signing_private_key", context=client_context) ver_key = config_lib.CONFIG.Get("Client.executable_signing_public_key", context=client_context) with aff4.FACTORY.Create(aff4_path, "GRRSignedBlob", mode="w", token=token) as fd: for start_of_chunk in xrange(0, len(content), limit): chunk = content[start_of_chunk:start_of_chunk + limit] blob_rdf = rdf_crypto.SignedBlob() blob_rdf.Sign(chunk, sig_key, ver_key, prompt=True) fd.Add(blob_rdf) logging.info("Uploaded to %s", fd.urn)
def testProgress(self): python_code = """ def f(): # This should also work inside a function. Progress() f() Progress() print "Done." """ signed_blob = rdf_crypto.SignedBlob() signed_blob.Sign(python_code, self.signing_key) request = rdf_client.ExecutePythonRequest(python_code=signed_blob) result = self.RunAction(standard.ExecutePython, request)[0] self.assertTrue(result.time_used > 0) self.assertEqual(result.return_val, "Done.\n")
def NewFromContent(cls, content, urn, chunk_size=1024, token=None, private_key=None, public_key=None): """Alternate constructor for GRRSignedBlob. Creates a GRRSignedBlob from a content string by chunking it and signing each chunk. Args: content: The data to stored in the GRRSignedBlob. urn: The AFF4 URN to create. chunk_size: Data will be chunked into this size (each chunk is individually signed. token: The ACL Token. private_key: An rdf_crypto.RSAPrivateKey() instance. public_key: An rdf_crypto.RSAPublicKey() instance. Returns: the URN of the new object written. """ aff4.FACTORY.Delete(urn, token=token) with data_store.DB.GetMutationPool() as pool: with aff4.FACTORY.Create(urn, cls, mode="w", mutation_pool=pool, token=token) as fd: for start_of_chunk in xrange(0, len(content), chunk_size): chunk = content[start_of_chunk:start_of_chunk + chunk_size] blob_rdf = rdf_crypto.SignedBlob() blob_rdf.Sign(chunk, private_key, public_key) fd.Add(blob_rdf, mutation_pool=pool) return urn
def Run(self, request): """Load the component requested. The component defines a set of python imports which should be imported into the running program. The purpose of this client action is to ensure that the imports are available and of the correct version. We ensure this by: 1) Attempt to import the relevant modules. 2) If that fails checks for the presence of a component installed at the require path. Attempt to import the modules again. 3) If no component is installed, we fetch and install the component from the server. We then attempt to use it. If all imports succeed we return a success status, otherwise we raise an exception. Args: request: The LoadComponent request. Raises: RuntimeError: If the component is invalid. """ summary = request.summary # Just try to load the required modules. try: self.LoadComponent(summary) # If we succeed we just report this component is done. self.SendReply(request) return except ImportError: pass # Try to add an existing component path. component_path = utils.JoinPath( config_lib.CONFIG.Get("Client.component_path"), summary.name, summary.version) # Add the component path to the site packages: site = Site() site.AddSiteDir(component_path) LOADED_COMPONENTS[summary.name] = summary.version try: self.LoadComponent(summary) logging.info("Component %s already present.", summary.name) self.SendReply(request) return except ImportError: pass # Could not import component - will have to fetch it. logging.info("Unable to import component %s.", summary.name) # Derive the name of the component that we need depending on the current # architecture. The client build system should have burned its environment # into the client config file. This is the best choice because it will # choose the same component that was built together with the client # itself (on the same build environment). build_environment = config_lib.CONFIG.Get("Client.build_environment") if not build_environment: # Failing this we try to get something similar to the running system. build_environment = rdf_client.Uname.FromCurrentSystem().signature( ) url = "%s/%s" % (summary.url, build_environment) logging.info("Fetching component from %s", url) crypted_data = self.grr_worker.http_manager.OpenServerEndpoint( url).data # Decrypt and check signature. The cipher is created when the component is # uploaded and contains the key to decrypt it. signed_blob = rdf_crypto.SignedBlob( summary.cipher.Decrypt(crypted_data)) # Ensure the blob is signed with the correct key. signed_blob.Verify( config_lib.CONFIG["Client.executable_signing_public_key"]) component = rdf_client.ClientComponent(signed_blob.data) # Make sure its the component we actually want. if (component.summary.name != summary.name or component.summary.version != summary.version): raise RuntimeError( "Downloaded component is not the correct version") # Make intermediate directories. try: os.makedirs(component_path) except (OSError, IOError): pass # Unzip the component into the path. logging.info("Installing component to %s", component_path) component_zip = zipfile.ZipFile(StringIO.StringIO(component.raw_data)) component_zip.extractall(component_path) # Add the component to the site packages: site.AddSiteDir(component_path) LOADED_COMPONENTS[component.summary.name] = component.summary.version # If this does not work now, we just fail. self.LoadComponent(summary) # If we succeed we just report this component is done. self.SendReply(request)
def UploadSignedDriverBlob(content, aff4_path=None, client_context=None, install_request=None, token=None): """Upload a signed blob into the datastore. Args: content: Content of the driver file to upload. aff4_path: aff4 path to upload to. If not specified, we use the config to figure out where it goes. client_context: The configuration contexts to use. install_request: A DriverInstallRequest rdfvalue describing the installation parameters for this driver. If None these are read from the config. token: A security token. Returns: String containing path the file was written to. Raises: IOError: On failure to write. """ sig_key = config_lib.CONFIG.Get("PrivateKeys.driver_signing_private_key", context=client_context) ver_key = config_lib.CONFIG.Get("Client.driver_signing_public_key", context=client_context) if aff4_path is None: aff4_paths = config_lib.CONFIG.Get("MemoryDriver.aff4_paths", context=client_context) if not aff4_paths: raise IOError("Could not determine driver location.") if len(aff4_paths) > 1: logging.info("Possible driver locations: %s", aff4_paths) raise IOError("Ambiguous driver location, please specify.") aff4_path = aff4_paths[0] blob_rdf = rdf_crypto.SignedBlob() blob_rdf.Sign(content, sig_key, ver_key, prompt=True) with aff4.FACTORY.Create(aff4_path, "GRRMemoryDriver", mode="w", token=token) as fd: fd.Add(blob_rdf) if install_request is None: # Create install_request from the configuration. install_request = rdf_client.DriverInstallTemplate( device_path=config_lib.CONFIG.Get("MemoryDriver.device_path", context=client_context), driver_display_name=config_lib.CONFIG.Get( "MemoryDriver.driver_display_name", context=client_context), driver_name=config_lib.CONFIG.Get( "MemoryDriver.driver_service_name", context=client_context)) fd.Set(fd.Schema.INSTALLATION(install_request)) logging.info("Uploaded to %s", fd.urn) return fd.urn
def testM2CryptoCompatibility(self): serialized_blob = open( os.path.join(self.base_path, "m2crypto/signed_blob")).read() blob = rdf_crypto.SignedBlob(serialized_blob) self.assertTrue(blob.Verify(self.public_key))