Exemplo n.º 1
0
    def EnrolFleetspeakClient(self, client_id):
        """Enrols a Fleetspeak-enabled client for use with GRR."""
        client_urn = rdf_client.ClientURN(client_id)

        # If already enrolled, return.
        if aff4.FACTORY.ExistsWithType(client_urn,
                                       aff4_type=aff4_grr.VFSGRRClient,
                                       token=self.token):
            return

        logging.info("Enrolling a new Fleetspeak client: %r", client_id)

        if data_store.RelationalDBEnabled():
            data_store.REL_DB.WriteClientMetadata(client_id.Basename(),
                                                  fleetspeak_enabled=True)

        # TODO(fleetspeak-team,grr-team): If aff4 isn't reliable enough, we can
        # catch exceptions from it and forward them to Fleetspeak by failing its
        # gRPC call. Fleetspeak will then retry with a random, perhaps healthier,
        # instance of the GRR frontend.
        with aff4.FACTORY.Create(client_urn,
                                 aff4_type=aff4_grr.VFSGRRClient,
                                 mode="rw",
                                 token=self.token) as client:

            client.Set(client.Schema.FLEETSPEAK_ENABLED,
                       rdfvalue.RDFBool(True))

            index = client_index.CreateClientIndex(token=self.token)
            index.AddClient(client)
            if data_store.RelationalDBEnabled():
                index = client_index.ClientIndex()
                index.AddClient(client_urn.Basename(),
                                data_migration.ConvertVFSGRRClient(client))

        enrollment_session_id = rdfvalue.SessionID(queue=queues.ENROLLMENT,
                                                   flow_name="Enrol")

        publish_msg = rdf_flows.GrrMessage(
            payload=client_urn,
            session_id=enrollment_session_id,
            # Fleetspeak ensures authentication.
            auth_state=rdf_flows.GrrMessage.AuthorizationState.AUTHENTICATED,
            source=enrollment_session_id,
            priority=rdf_flows.GrrMessage.Priority.MEDIUM_PRIORITY)

        # Publish the client enrollment message.
        events.Events.PublishEvent("ClientEnrollment",
                                   publish_msg,
                                   token=self.token)
Exemplo n.º 2
0
    def Start(self):
        """Sign the CSR from the client."""
        with aff4.FACTORY.Create(self.client_id,
                                 aff4_grr.VFSGRRClient,
                                 mode="rw",
                                 token=self.token) as client:

            if self.args.csr.type != rdf_crypto.Certificate.Type.CSR:
                raise IOError("Must be called with CSR")

            csr = rdf_crypto.CertificateSigningRequest(self.args.csr.pem)
            # Verify the CSR. This is not strictly necessary but doesn't harm either.
            try:
                csr.Verify(csr.GetPublicKey())
            except rdf_crypto.VerificationError:
                raise flow.FlowError("CSR for client %s did not verify: %s" %
                                     (self.client_id, csr.AsPEM()))

            # Verify that the CN is of the correct form. The common name should refer
            # to a client URN.
            self.cn = rdf_client.ClientURN.FromPublicKey(csr.GetPublicKey())
            if self.cn != csr.GetCN():
                raise IOError("CSR CN %s does not match public key %s." %
                              (csr.GetCN(), self.cn))

            logging.info("Will sign CSR for: %s", self.cn)

            cert = rdf_crypto.RDFX509Cert.ClientCertFromCSR(csr)

            # This check is important to ensure that the client id reported in the
            # source of the enrollment request is the same as the one in the
            # certificate. We use the ClientURN to ensure this is also of the correct
            # form for a client name.
            if self.cn != self.client_id:
                raise flow.FlowError(
                    "Certificate name %s mismatch for client %s", self.cn,
                    self.client_id)

            # Set and write the certificate to the client record.
            now = rdfvalue.RDFDatetime.Now()
            client.Set(client.Schema.CERT, cert)
            client.Set(client.Schema.FIRST_SEEN, now)
            if data_store.RelationalDBEnabled():
                data_store.REL_DB.WriteClientMetadata(
                    self.client_id.Basename(),
                    certificate=cert,
                    first_seen=now,
                    fleetspeak_enabled=False)

            index = client_index.CreateClientIndex(token=self.token)
            index.AddClient(client)
            if data_store.RelationalDBEnabled:
                index = client_index.ClientIndex()
                index.AddClient(self.client_id.Basename(),
                                data_migration.ConvertVFSGRRClient(client))

        # Publish the client enrollment message.
        self.Publish("ClientEnrollment", self.client_id)

        self.Log("Enrolled %s successfully", self.client_id)
Exemplo n.º 3
0
  def End(self):
    """Finalize client registration."""
    # Update summary and publish to the Discovery queue.
    client = self._OpenClient()
    summary = client.GetSummary()
    self.Publish("Discovery", summary)
    self.SendReply(summary)

    # Update the client index
    client_index.CreateClientIndex(token=self.token).AddClient(client)
    if data_store.RelationalDBEnabled():
      index = client_index.ClientIndex()
      index.AddClient(self.client_id.Basename(),
                      data_migration.ConvertVFSGRRClient(client))
Exemplo n.º 4
0
    def Handle(self, args, token=None):
        audit_description = ",".join([
            token.username + u"." + utils.SmartUnicode(name)
            for name in args.labels
        ])
        audit_events = []

        try:
            index = client_index.CreateClientIndex(token=token)
            client_objs = aff4.FACTORY.MultiOpen(
                [cid.ToClientURN() for cid in args.client_ids],
                aff4_type=aff4_grr.VFSGRRClient,
                mode="rw",
                token=token)
            for client_obj in client_objs:
                if data_store.RelationalDBEnabled():
                    cid = client_obj.urn.Basename()
                    data_store.REL_DB.RemoveClientLabels(
                        cid, token.username, args.labels)
                    labels_to_remove = set(args.labels)
                    existing_labels = data_store.REL_DB.GetClientLabels(cid)
                    for label in existing_labels:
                        labels_to_remove.discard(label.name)
                    if labels_to_remove:
                        idx = client_index.ClientIndex()
                        idx.RemoveClientLabels(cid, labels_to_remove)

                index.RemoveClientLabels(client_obj)
                self.RemoveClientLabels(client_obj, args.labels)
                index.AddClient(client_obj)
                client_obj.Close()

                audit_events.append(
                    events.AuditEvent(
                        user=token.username,
                        action="CLIENT_REMOVE_LABEL",
                        flow_name="handler.ApiRemoveClientsLabelsHandler",
                        client=client_obj.urn,
                        description=audit_description))
        finally:
            events.Events.PublishMultipleEvents(
                {audit.AUDIT_EVENT: audit_events}, token=token)
Exemplo n.º 5
0
    def Handle(self, args, token=None):
        audit_description = ",".join([
            token.username + u"." + utils.SmartUnicode(name)
            for name in args.labels
        ])
        audit_events = []

        try:
            index = client_index.CreateClientIndex(token=token)
            client_objs = aff4.FACTORY.MultiOpen(
                [cid.ToClientURN() for cid in args.client_ids],
                aff4_type=aff4_grr.VFSGRRClient,
                mode="rw",
                token=token)
            for client_obj in client_objs:
                if data_store.RelationalDBEnabled():
                    cid = client_obj.urn.Basename()
                    try:
                        data_store.REL_DB.AddClientLabels(
                            cid, token.username, args.labels)
                        idx = client_index.ClientIndex()
                        idx.AddClientLabels(cid, args.labels)
                    except db.UnknownClientError:
                        # TODO(amoser): Remove after data migration.
                        pass

                client_obj.AddLabels(args.labels)
                index.AddClient(client_obj)
                client_obj.Close()

                audit_events.append(
                    events.AuditEvent(
                        user=token.username,
                        action="CLIENT_ADD_LABEL",
                        flow_name="handler.ApiAddClientsLabelsHandler",
                        client=client_obj.urn,
                        description=audit_description))
        finally:
            events.Events.PublishMultipleEvents(
                {audit.AUDIT_EVENT: audit_events}, token=token)
Exemplo n.º 6
0
    def VerifyMessageSignature(self, response_comms, packed_message_list,
                               cipher, cipher_verified, api_version,
                               remote_public_key):
        """Verifies the message list signature.

    In the server we check that the timestamp is later than the ping timestamp
    stored with the client. This ensures that client responses can not be
    replayed.

    Args:
      response_comms: The raw response_comms rdfvalue.
      packed_message_list: The PackedMessageList rdfvalue from the server.
      cipher: The cipher object that should be used to verify the message.
      cipher_verified: If True, the cipher's signature is not verified again.
      api_version: The api version we should use.
      remote_public_key: The public key of the source.
    Returns:
      An rdf_flows.GrrMessage.AuthorizationState.
    """
        if (not cipher_verified
                and not cipher.VerifyCipherSignature(remote_public_key)):
            stats.STATS.IncrementCounter("grr_unauthenticated_messages")
            return rdf_flows.GrrMessage.AuthorizationState.UNAUTHENTICATED

        try:
            client_id = cipher.cipher_metadata.source
            try:
                client = self.client_cache.Get(client_id)
            except KeyError:
                client = aff4.FACTORY.Create(
                    client_id,
                    aff4.AFF4Object.classes["VFSGRRClient"],
                    mode="rw",
                    token=self.token)
                self.client_cache.Put(client_id, client)
                stats.STATS.SetGaugeValue(
                    "grr_frontendserver_client_cache_size",
                    len(self.client_cache))

            ip = response_comms.orig_request.source_ip
            client.Set(client.Schema.CLIENT_IP(ip))

            # The very first packet we see from the client we do not have its clock
            remote_time = client.Get(
                client.Schema.CLOCK) or rdfvalue.RDFDatetime(0)
            client_time = packed_message_list.timestamp or rdfvalue.RDFDatetime(
                0)

            # This used to be a strict check here so absolutely no out of
            # order messages would be accepted ever. Turns out that some
            # proxies can send your request with some delay even if the
            # client has already timed out (and sent another request in
            # the meantime, making the first one out of order). In that
            # case we would just kill the whole flow as a
            # precaution. Given the behavior of those proxies, this seems
            # now excessive and we have changed the replay protection to
            # only trigger on messages that are more than one hour old.

            if client_time < long(remote_time - rdfvalue.Duration("1h")):
                logging.warning("Message desynchronized for %s: %s >= %s",
                                client_id, long(remote_time), int(client_time))
                # This is likely an old message
                return rdf_flows.GrrMessage.AuthorizationState.DESYNCHRONIZED

            stats.STATS.IncrementCounter("grr_authenticated_messages")

            # Update the client and server timestamps only if the client
            # time moves forward.
            if client_time > long(remote_time):
                client.Set(client.Schema.CLOCK,
                           rdfvalue.RDFDatetime(client_time))
                client.Set(client.Schema.PING, rdfvalue.RDFDatetime.Now())

                clock = client_time
                ping = rdfvalue.RDFDatetime.Now()

                for label in client.Get(client.Schema.LABELS, []):
                    stats.STATS.IncrementCounter("client_pings_by_label",
                                                 fields=[label.name])
            else:
                clock = None
                ping = None
                logging.warning("Out of order message for %s: %s >= %s",
                                client_id, long(remote_time), int(client_time))

            client.Flush()
            if data_store.RelationalDBEnabled():
                source_ip = response_comms.orig_request.source_ip
                if source_ip:
                    last_ip = rdf_client.NetworkAddress(
                        human_readable_address=response_comms.orig_request.
                        source_ip)
                else:
                    last_ip = None

                if ping or clock or last_ip:
                    data_store.REL_DB.WriteClientMetadata(
                        client_id.Basename(),
                        last_ip=last_ip,
                        last_clock=clock,
                        last_ping=ping,
                        fleetspeak_enabled=False)

        except communicator.UnknownClientCert:
            pass

        return rdf_flows.GrrMessage.AuthorizationState.AUTHENTICATED
Exemplo n.º 7
0
 def __init__(self):
     if not data_store.RelationalDBEnabled():
         raise ValueError("No relational database available.")