def get_enrolled_machine_token(self, request): authorization_header = request.META.get("HTTP_AUTHORIZATION") if not authorization_header: raise APIAuthError("Missing or empty Authorization header") if "MunkiEnrolledMachine" not in authorization_header: raise APIAuthError("Wrong authorization token") return authorization_header.replace("MunkiEnrolledMachine", "").strip()
def check_data_secret(self, data): msn = data.get('machine_serial_number') if not msn: raise APIAuthError( f"No reported machine serial number. Request SN {self.machine_serial_number}." ) if msn != self.machine_serial_number: # the serial number reported by the zentral postflight is not the one in the enrollment secret. auth_err = "Zentral postflight reported SN {} different from enrollment SN {}".format( msn, self.machine_serial_number) post_machine_conflict_event(self.request, "zentral.contrib.munki", msn, self.machine_serial_number, {}) raise APIAuthError(auth_err)
def verify_signed_secret(self, enroll_secret): api_secret_data = verify_secret(enroll_secret, SOURCE_MODULE) self.machine_serial_number = api_secret_data.get( 'machine_serial_number', None) if not self.machine_serial_number: raise APIAuthError("No serial number") self.business_unit = api_secret_data.get("business_unit", None)
def get_instance_with_secret(self, secret): try: data = signing.loads(secret, key=API_SECRET) except signing.BadSignature: raise APIAuthError("Bad secret signature") else: return self.instances[data["url"]]
def verify_enrolled_machine_id(self): """Find the corresponding enrolled machine""" try: self.enrolled_machine = (EnrolledMachine.objects .select_related("enrollment__secret__meta_business_unit") .get(machine_id=self.machine_id)) except EnrolledMachine.DoesNotExist: raise APIAuthError("Could not authorize the request") else: self.machine_serial_number = self.enrolled_machine.serial_number self.business_unit = self.enrolled_machine.enrollment.secret.get_api_enrollment_business_unit()
def verify_enrolled_machine_token(self, token): try: enrolled_machine = (EnrolledMachine.objects.select_related( "enrollment__secret__meta_business_unit").get(token=token)) except EnrolledMachine.DoesNotExist: raise APIAuthError("Enrolled machine does not exist") else: self.enrollment = enrolled_machine.enrollment self.machine_serial_number = enrolled_machine.serial_number self.business_unit = self.enrollment.secret.get_api_enrollment_business_unit( )
def get_serial_number(self, data): try: serial_number = data["host_details"]["system_info"]["hardware_serial"].strip() except (KeyError, AttributeError): serial_number = None if serial_number is None: # special configuration for linux machines. see install script. serial_number = data.get("host_identifier", None) if not serial_number: raise APIAuthError("No serial number") return serial_number
def check_data_secret(self, data): reported_serial_number = data['serial_num'] if reported_serial_number != self.machine_serial_number: # the SN reported by osquery is not the one configured in the enrollment secret auth_err = "santa reported SN {} different from enrollment SN {}".format(reported_serial_number, self.machine_serial_number) machine_info = {k: v for k, v in data.items() if k in ("hostname", "os_build", "os_version", "serial_num", "primary_user") and v} post_machine_conflict_event(self.request, "zentral.contrib.santa", reported_serial_number, self.machine_serial_number, machine_info) raise APIAuthError(auth_err)
def check_request_secret(self, request, *args, **kwargs): enrolled_machine_token = self.get_enrolled_machine_token(request) if enrolled_machine_token: # new way self.request_secret = enrolled_machine_token self.verify_enrolled_machine_token(enrolled_machine_token) else: # old way self.request_secret = self.get_request_secret(request) if self.request_secret: self.verify_request_secret(self.request_secret) else: raise APIAuthError("Could not authenticate the request")
def verify_enrollment_secret(self, enroll_secret, serial_number, uuid): try: es_request = verify_enrollment_secret("osquery_enrollment", enroll_secret, self.user_agent, self.ip, serial_number, uuid) except EnrollmentSecretVerificationFailed: raise APIAuthError("Unknown enrolled machine") else: self.enrollment = es_request.enrollment_secret.osquery_enrollment self.machine_serial_number = serial_number self.business_unit = self.enrollment.secret.get_api_enrollment_business_unit( )
def get_machine_snapshot(self): if not self.machine_snapshot: auth_err = None try: self.machine_snapshot = MachineSnapshot.objects.current().get(source__module=SOURCE_MODULE, reference=self.node_key) except MachineSnapshot.DoesNotExist: auth_err = "Wrong node_key" except MachineSnapshot.MultipleObjectsReturned: auth_err = "Multiple current osquery machine snapshots for node key '{}'".format(self.node_key) if auth_err: logger.error("APIAuthError %s", auth_err) raise APIAuthError(auth_err) return self.machine_snapshot
def check_data_secret(self, data): auth_err = None try: self.ms = MachineSnapshot.objects.current().get(source__module='zentral.contrib.osquery', reference=data['node_key']) except KeyError: auth_err = "Missing node_key" except MachineSnapshot.DoesNotExist: auth_err = "Wrong node_key" if auth_err: logger.error("APIAuthError %s", auth_err, extra=data) raise APIAuthError(auth_err) # TODO: Better verification ? self.machine_serial_number = self.ms.serial_number self.business_unit = self.ms.business_unit
def check_data_secret(self, data): # no node_key, use the session_id # TODO: better? auth_err = None try: self.session_id = data["session_id"] self.carve_session = CarveSession.objects.get(session_id=self.session_id) self.machine_serial_number = self.carve_session.machine_serial_number except KeyError: auth_err = "Missing session id" except CarveSession.DoesNotExist: auth_err = "Unknown session id" if auth_err: logger.error("APIAuthError %s", auth_err, extra=data) raise APIAuthError(auth_err)
def check_data_secret(self, data): super().check_data_secret(data) self.data_data = data.pop("data") for r in self.data_data: decorations = r.pop("decorations", None) if decorations: hardware_serial = decorations.get("hardware_serial") if hardware_serial and hardware_serial != self.machine_serial_number: # the SN reported by osquery is not the one configured in the enrollment secret auth_err = "osquery reported SN {} different from enrollment SN {}".format( hardware_serial, self.machine_serial_number) post_machine_conflict_event(self.request, "zentral.contrib.osquery", hardware_serial, self.machine_serial_number, decorations) raise APIAuthError(auth_err)
def verify_enrolled_machine_token(self, token): cache_key = f"munki.{token}" try: self.enrollment, self.machine_serial_number, self.business_unit = cache.get( cache_key) except TypeError: try: enrolled_machine = (EnrolledMachine.objects.select_related( "enrollment__secret__meta_business_unit").get(token=token)) except EnrolledMachine.DoesNotExist: raise APIAuthError("Enrolled machine does not exist") else: self.enrollment = enrolled_machine.enrollment self.machine_serial_number = enrolled_machine.serial_number self.business_unit = self.enrollment.secret.get_api_enrollment_business_unit( ) cache.set(cache_key, (self.enrollment, self.machine_serial_number, self.business_unit), timeout=600)
def check_data_secret(self, data): # get the node_key try: self.node_key = data["node_key"] except KeyError: raise APIAuthError("Missing node_key in osquery request") enrolled_machine = self.get_enrolled_machine() if enrolled_machine: # new way self.enrollment = enrolled_machine.enrollment self.machine_serial_number = enrolled_machine.serial_number self.business_unit = self.enrollment.secret.get_api_enrollment_business_unit() if not enrolled_machine: # old way, look for a MachineSnapshot with the node_key as reference # TODO: deprecate and remove machine_snapshot = self.get_machine_snapshot() self.machine_serial_number = machine_snapshot.serial_number self.business_unit = machine_snapshot.business_unit
def check_data_secret(self, data): # no node id => check carve session id auth_err = None try: self.session_id = data["session_id"] self.carve_session = CarveSession.objects.get( session_id=self.session_id) self.machine_serial_number = self.carve_session.machine_serial_number self.ms = MachineSnapshot.objects.current().get( source__module='zentral.contrib.osquery', serial_number=self.machine_serial_number) except KeyError: auth_err = "Missing session id" except CarveSession.DoesNotExist: auth_err = "Unknown session id" except MachineSnapshot.DoesNotExist: auth_err = "Unknown machine serial number" if auth_err: logger.error("APIAuthError %s", auth_err, extra=data) raise APIAuthError(auth_err) self.business_unit = self.ms.business_unit
def check_data_secret(self, data): super().check_data_secret(data) self.data_data = data.pop("data") for r in self.data_data: decorations = r.pop("decorations", None) if decorations: platform = platform_with_os_name(decorations.get("os_name")) if platform == MACOS: hardware_serial = decorations.get("hardware_serial") if hardware_serial and hardware_serial != self.machine_serial_number: # The SN reported by osquery is not the one configured in the enrollment secret. # For other platforms than MACOS, it could happen. For example, we take the GCE instance ID as # serial number in the enrollment secret for linux, if possible. # Osquery builds one from the SMBIOS/DMI. auth_err = "osquery reported SN {} different from enrollment SN {}".format( hardware_serial, self.machine_serial_number) post_machine_conflict_event(self.request, SOURCE_MODULE, hardware_serial, self.machine_serial_number, decorations) raise APIAuthError(auth_err)