From patchwork Mon Aug 26 10:37:15 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Dr. Greg" X-Patchwork-Id: 13777618 X-Patchwork-Delegate: paul@paul-moore.com Received: from blizzard.enjellic.com (wind.enjellic.com [76.10.64.91]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 5539A39ACC; Mon, 26 Aug 2024 10:49:54 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=76.10.64.91 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1724669398; cv=none; b=IAoa/yaupTOAX8ONdfCq9aa4R41hhwybx0KEmb1Rne4xZYXX/cphof+cv09uVx+UhWo08eTDpflHLfTRL6KeU2AquXDDQD846EDidvsXGZ4k805YdfPtYFbHeePUGZBTvPVRZvH83wWUTniHGFZrZYz+NJEh552BK6JWh07tNq0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1724669398; c=relaxed/simple; bh=rbU4Y17++Pv9h75lBxpFvTz5+XkNgT7DcwoBJKeq984=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=iIYf0OhMIcB5Ud78DMjVdhvkCrAmoQceKdzUakvvECQ7FPRBo7BIWr+HVhy9vFaNeUcubAPORpshNeX9WAGuKS2xq6iXI3q/N6MD7MIjSVFPq5RZv0oh1zRPwieptaZRPxhrTl87E6X+48uHSeOXID9zlIR+G3t32vK7J4WCyTE= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=enjellic.com; spf=pass smtp.mailfrom=enjellic.com; arc=none smtp.client-ip=76.10.64.91 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=enjellic.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=enjellic.com Received: from blizzard.enjellic.com (localhost [127.0.0.1]) by blizzard.enjellic.com (8.15.2/8.15.2) with ESMTP id 47QAbU7q003405; Mon, 26 Aug 2024 05:37:30 -0500 Received: (from greg@localhost) by blizzard.enjellic.com (8.15.2/8.15.2/Submit) id 47QAbUsB003404; Mon, 26 Aug 2024 05:37:30 -0500 X-Authentication-Warning: blizzard.enjellic.com: greg set sender to greg@enjellic.com using -f From: Greg Wettstein To: linux-security-module@vger.kernel.org, linux-kernel@vger.kernel.org Cc: jmorris@namei.org Subject: [PATCH v4 01/14] Update MAINTAINERS file. Date: Mon, 26 Aug 2024 05:37:15 -0500 Message-Id: <20240826103728.3378-2-greg@enjellic.com> X-Mailer: git-send-email 2.39.1 In-Reply-To: <20240826103728.3378-1-greg@enjellic.com> References: <20240826103728.3378-1-greg@enjellic.com> Precedence: bulk X-Mailing-List: linux-security-module@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 --- MAINTAINERS | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 958e935449e5..f4a5772ea255 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -22824,6 +22824,14 @@ S: Maintained F: Documentation/tee/ts-tee.rst F: drivers/tee/tstee/ +TSEM SECURITY MODULE +M: Greg Wettstein +S: Maintained +L: linux-security-module@vger.kernel.org +F: Documentation/admin-guide/LSM/tsem.rst +F: Documentation/ABI/testing/tsem +F: security/tsem/ + TTY LAYER AND SERIAL DRIVERS M: Greg Kroah-Hartman M: Jiri Slaby From patchwork Mon Aug 26 10:37:16 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Dr. Greg" X-Patchwork-Id: 13777632 X-Patchwork-Delegate: paul@paul-moore.com Received: from blizzard.enjellic.com (wind.enjellic.com [76.10.64.91]) by smtp.subspace.kernel.org (Postfix) with ESMTP id DED6014A4FB; Mon, 26 Aug 2024 10:50:57 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=76.10.64.91 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1724669464; cv=none; b=W94SM1HNuhdiHndZRKU3PHG2CNqXpBYE8G+Sj1xh7GVZIjNe1efh6iaV6EYrJaWs0kylGRdn4nCzc4KlaObONhfgvPCjmYQFaEYMDgtpq7uuqXB1YEG0HbmVCeXrAjhBLEKbP3uBLPk9XgyINUTMsvJvtoKO1z2OM/mdpIdK81M= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1724669464; c=relaxed/simple; bh=gR7+lmAasVMOSEk+yo9iDzry5B37WK6kmtsNFxvNd/M=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=kIfmZcpm1CTMozcTCGyCYLo9t47JyG5qgSYnzvHegjOz+42uy1+JkcriQQ58KhVXxDvOkr73aC341MXwQJUtJKv9pl5aQX9nWcBxq6Emzu7S6FyBeQQnRFDQPaSIGhzjpydHJdKJWhEpCbJAN23VesucODSUsCNQfb35cMrwTTI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=enjellic.com; spf=pass smtp.mailfrom=enjellic.com; arc=none smtp.client-ip=76.10.64.91 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=enjellic.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=enjellic.com Received: from blizzard.enjellic.com (localhost [127.0.0.1]) by blizzard.enjellic.com (8.15.2/8.15.2) with ESMTP id 47QAbUmw003409; Mon, 26 Aug 2024 05:37:30 -0500 Received: (from greg@localhost) by blizzard.enjellic.com (8.15.2/8.15.2/Submit) id 47QAbUke003408; Mon, 26 Aug 2024 05:37:30 -0500 X-Authentication-Warning: blizzard.enjellic.com: greg set sender to greg@enjellic.com using -f From: Greg Wettstein To: linux-security-module@vger.kernel.org, linux-kernel@vger.kernel.org Cc: jmorris@namei.org Subject: [PATCH v4 02/14] Add TSEM specific documentation. Date: Mon, 26 Aug 2024 05:37:16 -0500 Message-Id: <20240826103728.3378-3-greg@enjellic.com> X-Mailer: git-send-email 2.39.1 In-Reply-To: <20240826103728.3378-1-greg@enjellic.com> References: <20240826103728.3378-1-greg@enjellic.com> Precedence: bulk X-Mailing-List: linux-security-module@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 An entry was added to the ABI testing documentation to document the API definitions for the TSEM ontrol plane. The file documenting the kernel command-line parameters was updated to document the TSEM specific parameters that are implemented. The primary TSEM documentation file was added to the LSM administration guide and the file was linked to the index of LSM documentation. --- Documentation/ABI/testing/tsem | 2420 +++++++++++++++++ Documentation/admin-guide/LSM/index.rst | 1 + Documentation/admin-guide/LSM/tsem.rst | 1680 ++++++++++++ .../admin-guide/kernel-parameters.txt | 29 + 4 files changed, 4130 insertions(+) create mode 100644 Documentation/ABI/testing/tsem create mode 100644 Documentation/admin-guide/LSM/tsem.rst diff --git a/Documentation/ABI/testing/tsem b/Documentation/ABI/testing/tsem new file mode 100644 index 000000000000..0e0e3201edf4 --- /dev/null +++ b/Documentation/ABI/testing/tsem @@ -0,0 +1,2420 @@ +This file contains descriptions of output generated by the TSEM +control plane files and the role of the only write-only file in the +control plane. + +It is highly recommended that the following document be read in order +to fully understand the rationale and functioning of these files: + +Documentation/admin-guide/LSM/tsem.rst + +What: /sys/kernel/security/tsem +Date: March 2024 +Contact: Greg Wettstein +Description: + The /sys/kernel/security/tsem directory contains files + and directories that implement the control plane for + the Trusted Security Event Modeling (TSEM) LSM. + + The files in this directory hierarchy, with the + exception of the aggregate file, when read, reflect + the values for the security modeling namespace that + the process reading the files is operating in. + +What: /sys/kernel/security/tsem/id +Date: March 2024 +Contact: Greg Wettstein +Description: + The id file outputs the ASCII base 10 representation + of the security modeling namespace that the reading + process is operating in. + + The root security modeling namespace has a value of + zero, a non-zero value indicates a modeling namespace + subordinate to the root model. + + Each externally modeled security namespace will have a + file, with this id number, created in the + /sys/kernel/security/tsem/external_tma that is + documented below. + +What: /sys/kernel/security/tsem/aggregate +Date: March 2024 +Contact: Greg Wettstein +Description: + The aggregate file contains the ASCII base 16 + representation of the Trusted Platform Module (TPM) + based hardware aggregate measurement. The hardware + aggregate measurement is the linear extension sum, + using the digest function chosen for the root modeling + namespace, of PCR registers 0 through 8 + + The hardware aggregate measurement is a common concept + in trusted systems and is used to verify the integrity + and known good state of the hardware environment + leading up to the boot of the operating system. + + The size of the measurement is equal in length to the + ASCII Base16 representation of the cryptographic + digest function being used for the root security + modeling namespace. For example, if the default + sha256 digest function is being used, the aggregate + measurement will be 64 ASCII hexadecimal characters. + + On a platform without a TPM this value will be all + ASCII 0 values. + +What: /sys/kernel/security/tsem/control +Date: March 2024 +Contact: Greg Wettstein +Description: + The control file is the only writable file in the + filesystem and is used by trust orchestrator's to + configure and control the behavior of the TSEM + implementation. + + The following keyword and arguments are recognized: + + internal + The internal keyword causes an internally + modeled namespace to be created for the + calling process. + + external + The external keyword causes an externally + modeled namespace to be created for the + calling process. + + A modeling namespace created by either of these + directives accepts the following key=value pairs that + configure the namespace: + + + model=MODULE_NAME + The model key specifies the name of a security + model that is to be used to conduct the event + processing for a security modeling namespace. + + If the standard build practice of using the + KBUILD_MODNAME CPP define to set the name of + the security model the model name will match + the name of the module, as displayed by the + lsmod command. There is no requirement that + the security model name match the module name. + + nsref=initial|current + The nsref key specifies the namespace that is + to be referenced when determining the UID/GID + values used to characterize a COE or CELL + mapping. + + The initial keyword specifies that the initial + user namespace is to be used. The current + keyword specifies that the user namespace of + the process that is invoking a security event + handler is used. + + The default is to use the initial user + namespace. + + digest=digestname + The digest key value is used to specify the + cryptographic hash function that is to be used + for the generation of the security state + coefficients for the LSM events being modeled. + + The digestname value is the name used by the + Linux kernel cryptographic API to identify the + hash function. Examples would include; + sha256, sha3-256, sm3 etc. The source files + for the various cryptographic hash functions in + the crypto directory of the kernel source has + the names for hash functions implemented in + the kernel. + + The sha256 hash function is the default + function for the root modeling namespace. + This can be modified by the tsem_digest + command-line parameter. + + A hash function, used by the root modeling + namespace, must be built into the kernel. + + cache=NN + The cache key specifies the size of the caches + to be used for holding pointers to structures + used for the modeling or export of security + events that are called while the invoking + process is in atomic context. + + The value NN is the ASCII base 10 + representation of the number of entries in the + cache. + + By default, the root modeling namespace and an + externally modeled namespace will have 96 + entries. An internally modeled namespace will + have 16 entries. + + key=HEXID + The key argument is used to specify the + authentication key that will be used by a + trust orchestrator to authenticate trust + control requests to a process running in the + security modeling namespace. + + The HEXID value is the ASCII base16 encoded + representation of the key that is to be used. + The length of this key, in binary, must be + equal to the size of the digest produced by + the cryptographic hash function that is being + used in the security modeling namespace. + + enforce + The enforce keyword causes the modeling + namespace of the process to enter enforcing + mode. In this mode, a value of -EPERM will be + returned for a security event coefficient that + does not map into the current set of allowed + coefficients for the security model being + implemented in the namespace. + + seal + The seal keyword causes the security model + being implemented for the namespace to be + placed in a sealed state. In this state, the + current set of security coefficients is + considered to be the only set of valid + coefficients for the model. Any subsequent + events that map to a coefficient not in the + current model will be considered a violation + of the model. + + trusted pid=PID key=HEXID + The trusted keyword is used by a trust + orchestrator to specify that a process being + woken up after model evaluation of a security + event should be allowed to run in trusted + status. + + The pid argument is used to locate the process + that is sleeping on the modeling event. + + The HEXID argument is the authentication key + that was configured by a trust orchestrator + for the namespace at the time of its creation. + + untrusted pid=PID key=HEXID + The untrusted keyword is used by a trust + orchestrator to specify that a process being + woken up after model evaluation should be + placed in untrusted status. + + The pid argument is used to locate the process + that is sleeping on the modeling event. + + The HEXID argument is the authentication key + that was configured by a trust orchestrator + for the namespace at the time of its creation. + + state value=HEXID + The state keyword is used to indicate that the + security state coefficient, identified by the + ASCII base 16 encoded value specified by HEXID, + should be loaded into the current security + model as a valid security event coefficient. + + The HEXID value will be the length of the + digest value of the cryptographic hash + function specified for the security modeling + namespace. + + pseudonym value=HEXID + The pseudonym keyword is used to indicate that + the pathname, identified by the ASCII base 16 + encoded value HEXID, should be designated to + return a constant digest value for the + contents of the file named by the pseudonym. + + The HEXID value is computed with the following + function: + + HEXID = HF(PATHNAME_LENGTH || PATHNAME) + + Where HF is the cryptographic hash function + that has been designated for use by the + security modeling namespace and || implies + concatenation of the byte stream fed to the + hash function. + + base value=HEXID + The base keyword is used to indicate that the + ASCII base 16 encoded value HEXID should be + registered as the value used to generate + security state coefficients for the model + implemented in the modeling namespace. + + The binary length of the value specified by + HEXID must match the digest size of the + cryptographic hash function specified for use + in the security modeling namespace. + + A model specific base value is designed to be + used as a 'freshness' nonce, similar to an + attestation nonce, to prove that a model state + value or measurement is current and not being + replayed. + + lock + This command is only valid when loadable + module support is available in the kernel. + + When executed this command blocks any further + TSEM models from being registered. In + addition the reference count on all currently + registered modeling modules is increased so + that it is impossible to remove currently + loaded models. + +What: /sys/kernel/security/tsem/internal_tma +Date: March 2024 +Contact: Greg Wettstein +Description: + This directory will contain directories that will be + used to hold files that surface model parameters for + Trusted Modeling Agents (TMA's) for internally modeled + security namespaces. + + There is currently only one internal TMA that is + surfaced through the following directory: + + /sys/kernel/security/tsem/internal_tma/model0 + + The subsequent descriptions for the files implemented + in this directory will use ROOTDIR to represent the + above directory for space conservation purposes. + +What: ROOTDIR/measurement +Date: March 2024 +Contact: Greg Wettstein +Description: + The measurement file contains the ASCII base 16 + representation of the time dependent linear extension + value of all the security state coefficients in the + model implemented by the namespace of the calling + process. + + This value is similar in concept to measurement values + generated by TPM PCR's. In the case of TSEM, it can + be used to validate the order integrity of the + security state coefficients output in the + trajectory_coefficients file documented below. + +What: ROOTDIR/state +Date: March 2024 +Contact: Greg Wettstein +Description: + The state file contains the ASCII base 16 + representation of the functional value of a security + modeling namespace. + + The state value is a time independent representation + of the measurement of a security model. This value is + designed to be a single value that represents whether + or not a workload has deviated from a defined set of + known good behaviors. + +What: ROOTDIR/trajectory +Date: March 2024 +Contact: Greg Wettstein +Description: + The trajectory file contains a description of the + security events that have occurred in a security + modeling namespace + + Each entry in this file represents a single security + event and consists of a JSON encoded record with keys + and associated values or structures that define the + characteristics of the event. + + Examples and descriptions of the JSON structures are + provided at the end of this document. + +What: ROOTDIR/trajectory_coefficients +Date: March 2024 +Contact: Greg Wettstein +Description: + The trajectory_coefficients file will output the ASCII + base16 representation of each security state + coefficient that has been generated by the security + modeling namespace of the calling process. + + The length of each point will be the ASCII + representation of the size of the cryptographic hash + function that has been specified for the model. + +What: ROOTDIR/trajectory_counts +Date: March 2024 +Contact: Greg Wettstein +Description: + The trajectory_coefficients file will output the ASCII + base10 representation of the number of times each + security state coefficient has been generated by the + security modeling namespace of the calling process. + +What: ROOTDIR/forensics +Date: March 2024 +Contact: Greg Wettstein +Description: + The forensics file contains the descriptions of + security events that are inconsistent with the + security model that the security namespace is + implementing. Forensics events are generated after a + security model is 'sealed' and the events represent + security state coefficients that have not already been + generated by the model. + + The format of lines in this file are identical to the + output generated by the ROOTDIR/trajectory file + that is documented above. + +What: ROOTDIR/forensics_coefficients +Date: March 2024 +Contact: Greg Wettstein +Description: + The forensics_coefficients file will output the ASCII + base16 representation of each security state + coefficient that have been generated by forensics + events in the security modeling namespace of the + calling process. + + The length of each point will be the ASCII + representation of the size of the cryptographic hash + function that has been specified for the model. + +What: ROOTDIR/forensics_counts +Date: March 2024 +Contact: Greg Wettstein +Description: + The forensics_counts file will output the ASCII base10 + representation of the number of times each security + state coefficient, generated by a forensics event, has + been generated by the security modeling namespace of + the calling process. + +What: /sys/kernel/security/tsem/external_tma +Date: March 2024 +Contact: Greg Wettstein +Description: + The external_tma is a container directory that holds + files that will be used to export JSON encoded security + event descriptions for externally modeled security + modeling namespaces. + + The files created in this directory will be named by + the base 10 ASCII representation of the id value + assigned to the security modeling namespace. See the + documentation for the /sys/kernel/security/tsem/id + file in this documentation for more details on this + value. + + This file will is a read-only file that can be polled + by a userspace trust orchestrator implementation to + obtain security events that are to be modeled by a + Trusted Modeling Agent implementation associated with + the trust orchestrator. + + Each exported event is a JSON encoded record + describing an event that is to be externally + processed. The encoding for these events are included + below. + + +JSON Security Event Description Encoding: + +Methodology: +------------ + +The security event descriptions maintained by the internal TMA and +that are exported to external trust orchestrator's are encoded in +JavaScript Object Notation (JSON). This section of the document will +describe the encoding strategy that is used. + +An encoding strategy was used where each non-structure key value is +encoded as a JSON string type. In the case of numeric values it is +the responsibility of the TMA to convert the string value into an +appropriate numeric value for the generation of the mapping values +that contribute to a security state coefficient. + +The JSON string encodings for numeric values follow the conventions +used by the strtol() and/or strtoll() functions to interpret how to +convert a string encoded numeric value into an appropriate numeric +binary type when the conversion function is called with the 'special' +value of 0 for the base argument. + +The following nomenclature will be define the value member for numeric +types: + + 0MM: A base 8 ASCII value. + NN: A base 10 ASCII value. + 0xHEX A base 16 ASCII value. + +The MM, NN and HEX labels above do not imply a length of the numeric +string but are used instead to imply that numeric digits of the +specified type will follow. + +When the documentation below specifies a value name of DIGEST it is +referring to a string value containing the base 16 hexadecimal +encoding of a cryptographic digest value. The length of the value +will be equal to the ASCII hexadecimal encoding length of a digest +value for the cryptographic compression function that is in effect for +the security modeling namespace. A DIGEST value will NOT be preceded +by the 0x prefix for these values. + +A value name of ASCII implies an ASCII string. + + +Event descriptions: +------------------- + +Each TSEM supported LSM event handler (hook) encodes a description of +the characteristics of an event in the following general form: + +{"event": {}, "COE": {}, "CELL": {}} + +Where the event key is used to label a structure that characterizes +the event type. The COE key labels a structure that characterizes the +Context Of Execution that is executing the event and the CELL key +labels a structure that defines the characteristics of the security +event that an LSM handler/hook is invoked with. + +The CELL label used above is a generic label for documentation +purposes. The "CELL" label in an actual security event description +will be replaced with one of the following currently supported event +type descriptions: + + task_kill + task_setpgid + task_getpgid + task_getsid + task_setnice + task_setioprio + task_getioprio + task_prlimit + task_setrlimit + task_setscheduler + task_getscheduler + task_prctl + file_open + mmap_file + file_ioctl + file_lock + file_fcntl + file_receive + unix_stream_connect + unix_may_send + socket_create + socket_connect + socket_bind + socket_accept + socket_listen + socket_socketpair + socket_sendmsg" + socket_recvmsg + socket_getsockname + socket_getpeername + socket_setsockopt + socket_shutdown + ptrace_traceme + kernel_module_request + kernel_load_data + kernel_read_file + sb_mount + sb_umount + sb_remount + sb_pivotroot + sb_statfs + move_mount + shm_associate + shm_shmctl + shm_shmat + sem_associate + sem_semctl + sem_semop + syslog + settime + quotactl + quota_on + msg_queue_associate + msg_queue_msgctl + msg_queue_msgsnd + msg_queue_msgrcv + ipc_permission + key_alloc + key_permission + netlink_send + inode_create + inode_link + inode_unlink + inode_symlink + inode_mkdir + inode_rmdir + inode_mknod + inode_rename + inode_setattr + inode_getattr + inode_setxattr + inode_getxattr + inode_listxattr + inode_removexattr + inode_killpriv + tun_dev_create + tun_dev_attach_queue + tun_dev_attach + tun_dev_open + bpf + bpf_map + bpf_prog + ptrace_access_check + capable + capget + capset + + +Event encoding: +--------------- + +The 'event' label encodes a value structure of the following form: + + "event": { + "context": "NN", + "number": "NN", + "process": "ASCII", + "type": "ASCII", + "ttd": "NN", + "p_ttd": "NN", + "task_id": "DIGEST", + "p_task_id": "DIGEST", + "ts": "NN" + { + +Where the value type for the individual keys is as follows: + + context: A 64-bit numeric value representing the identity + number of the security modeling namespace that + generated the event. + + number: A 64-bit numeric value representing the sequence + number of the event in the security modeling + namespace that generated the event. + + process: The name of the process (current->comm) that + is invoking the security handler. + + type: One of the security event description names + noted above. + + ttd: A 64-bit numeric value incremented each time + a TASK_ID is generated by the bprm_committed_creds + handler. This value is used to separate unique + invocations of an executable corpus of code. + + p_ttd: The ttd value of the parent process of the process + generating the event. This value and the ttd + value can be used to reproduce the order of + execution of executable code on a platform or + in a security modeling namespace. + + task_id: The TASK_ID of the process executing the event. + + p_task_id: The TASK_ID of the parent process of the process + executing the event. + + ts: The timestamp for when the security event occurred. + The timestamp value is in monotonic nanoseconds + since the hardware platform was booted. + + +Context Of Execution encoding: +------------------------------ + +The 'COE' label encodes a structure of the following form: + + "COE": { + "uid": "NN", + "euid": "NN", + "suid": "NN", + "gid": "NN", + "egid": "NN", + "sgid": "NN", + "fsuid": "NN", + "fsgid": "NN", + "capeff": "0xHEX" + } + + Which describes the credentials of the process that is + executing the event. The reference for the numeric values of + the discretionary access control values is either the initial + user namespace or the user namespace that the security + modeling namespace is running in. + + The namespace reference is a selectable feature when the + security modeling namespace is created. The root security + modeling namespace always uses the initial user namespace. + + It is assumed that anyone developing security models will be + familiar with the common definitions for the uid, gid + etc. labels. + + The 'capeff' label is the effective capability set possessed + by a process when the security event is executed. + + +CELL (event description) encoding: +---------------------------------- + +The 'CELL' label encodes multiple different structures depending on +the security event that is being encoded. An understanding of the +various LSM security event hooks and their arguments is recommended +prerequisite for anyone wishing to develop security models using these +event descriptions. + +The CELL event descriptions frequently have subordinate JSON +structures embedded in them. In some cases only a single instance of +a structure is used for that event type, in which case the definition +of that structure will be included inline with the description of the +event itself. + +The following general event subordinate parameter descriptions are +used: + +The 'creds' structure is used to define the standard discretionary +access based security credentials for a process: + + creds { + "uid": "NN", + "euid": "NN", + "suid": "NN", + "gid": "NN", + "egid": "NN", + "sgid": "NN", + "fsuid": "NN", + "fsgid": "NN", + "capeff": "0xHEX", + "securebits:" "NN" + } + + As in the case for the COE encoding standard knowledge of + discretionary access controls is assumed with the capeff label + being previously described. + + securebits: A numeric value containing bit specific fields + that a file capability enabled kernel will use + to invoke special handling of capabilities for + the root user id. + + +The 'perm' structure is used to describe the IPC permissions for +shared memory primitives such as shared memory regions, message +queues, semaphores etc.: + + "perm": { + "uid": "NN", + "gid": "NN", + "cuid": "NN", + "cgid": "NN", + "mode": "0MM", + "owner": "DIGEST" + } + + uid: The user id requesting permission for access to the + memory area. + + gid: The group id requesting permission for access to + the memory area. + + cuid: The alternate user id requesting permission for + access to the memory area. + + cgid: The alternate group id requesting permission for + access to the memory area. + + mode: The permissions being requested for access. + + owner: The TASK_ID of the process requesting access + to the memory area. + + +The 'inode' structure is used to describe an inode structure used in +a security event description: + + "inode": { + "uid": "NN", + "gid": "NN", + "mode": "0MM, + "s_magic": "0xHEX", + "s_id": "ASCII", + "s_uuid": "HEX" + } + + uid: The user id that owns the inode. + + gid: The group id that owns the inode. + + mode: The discretionary access control permissions + for the inode. + + s_magic: The magic number of the filesystem that the + inode is in. + + s_id: The informational name from the superblock + of the filesystem the inode is in. + + s_uuid: The UUID of the filesystem the inode is in. + + +The 'inode' structure is used to describe an inode structure used in +a security event description: + + "inode": { + "uid": "NN", + "gid": "NN", + "mode": "0MM, + "s_magic": "0xHEX", + "s_id": "ASCII", + "s_uuid": "HEX" + } + + +The 'path' structure is used to describe a path definition for an +inode: + + "path": { + "dev": { + "major": "NN", + "minor": "NN" + }, + "instance": "NN", /* Optional. */ + "owner": "DIGEST", /* Optional. */ + "pathname": "ASCII" + } + + dev: A structure used to encapsulate the major and minor + numbers of the device that the filesystem is based + on. This structure will not be included if the + path is not on a block device. + + instance: This key/value pair is only reported for an + inode that is created in the context of the + security modeling namespace that is reporting + the path description. + + If reported, the instance value is the + instance count of an inode created under the + containing directory by the TASK_ID of the + process creating the inode. + + owner: As with the instance key, this key/value pair + is only reported for an inode created in the + context of the security modeling namespace + that is reporting the path description. + + If reported, the DIGEST value is the TASK_ID + of the process that created the inode. + + pathname: The pathname of the file. + + +The 'dentry' structure is used to describe a dentry argument: + + "dentry": { + "inode": {}, + "path": {} + } + + inode: A JSON inode structure that describes the backing + inode for the dentry. In some instances a + dentry does not have an inode defined, in which + case this key/value structure will be omitted + from the security event description. + + path: A JSON path structure describing the pathname + implemented by the dentry. + + +The 'file' structure is used to describe a file structure used in +a security event description: + + "file": { + "flags": "NN", + "inode": {}, + "path": {} + "digest": "DIGEST" + } + + flags: The f_flags member of the file structure describing + how the file was opened. + + inode: A structure describing the inode that is backing + the file. The definition of the inode structure will + be included after the descriptions of the inode based + security handlers. + + path: A structure describing the path to the file. + + digest: The cryptographic digest of the file. For files + on a pseudo-filesystem or files that are created in + the context of a security modeling namespace this + value will be the 'zero-value' digest for the + hash function being used in the security modeling + namespace. + + +The 'sb' structure is used to describe a filesystem superblock. + + "sb": { + "dev_name", "ASCII", + "path", {}, + "type", "ASCII", + "flags", "NN" + } + + dev_name: The device name that the filesystem is mounted + on. + + path: A JSON path description describing where the + the superblock is mounted. + + type: An ASCII string describing the mounted filesystem. + + flags: The mount flags for the mountpoint. + + +The 'sock' structure is used to describe a socket that is used in +a security event description: + + sock { + "family": "NN", + "type": "NN", + "protocol": "NN", + "owner": "DIGEST" + } + + family: A numeric value describing the protocol family + that the socket is to be created for, ie. AF_UNIX, + AF_INET etc. + + type: The type of communications the socket is to + be used for, ie. SOCKET_STREAM, SOCKET_DGRAM. + + protocol: An optional protocol to be used with the socket. + + +The 'addr' structure is used is an enclosing structure to hold +subordinate structures that describe the address of a socket. The +addr field will contain a subordinate structure that is specific to +the address type: + + addr { + "af_inet": {} + "af_inet6": {} + "af_unix": {} + "af_other": {} + } + +The 'af_inet' structure defines an IPV4 network address: + + "af_inet": { + "port": "NN", + "address": "NN + { + + port: The port number of the address. + + address: The 32-bit IPV4 address. + +The 'af_inet6' structure defines an IPV6 network address: + + "af_inet6": { + "port": "NN", + "flow": "NN", + "scope": "NN", + "address": "HEXID" + } + + port: The port number of the address. + + flow: The IPV6 flow specifier. + + scope: The IPV6 scope designator. + + address: The 128 bit IPV6 address in ASCII hexadecimal. + +The 'af_unix' structure a UNIX domain address: + + "af_unix": { + "address": "ASCII" + } + + address: The path to the filesystem object representing + the socket. + +The 'af_other' structure is used to define a socket address other +than one of the three above: + + "af_other": { + "address": "DIGEST" + } + + address: The DIGEST value is the cryptographic digest + value over the sa_data member of the + sockaddr structure. + + +Event Specific Encodings: +------------------------- + +The 'task_kill' event models a request to send a signal to a target +task: + + "task_kill": { + "target": "DIGEST", + "sig": "NN", + "cross_ns": "NN" + } + + target: The TASK_ID of the process that the signal is + being sent to. + + sig: The number of the signal being sent. + + cross_ns: A boolean flag variable of 0 or 1 indicating + whether or not the signal will be crossing a + security modeling namespace boundary. + + +The 'task_setpgid' event models a request to set the process group id +of a task: + + "task_setpgid": { + "task": "DIGEST", + "source": "DIGEST" + } + + task: The TASK_ID of the process whose process group + id is to be set. + + source: The TASK_ID of the process whose process group + id the target task is to be placed into. + + +The 'task_getpgid' event describes a request to obtain the process +group id of a task: + + "task_getpgid": { + "task": "DIGEST", + } + + task: The TASK_ID of the process whose process group + id is to be obtained. + + +The 'task_getsid' event describes a request to obtain the session id of +a task. + + "task_getsid": { + "task": "DIGEST", + } + + task: The TASK_ID of the process whose session id is + to be obtained. + + +The 'task_setnice' event describes a request to set the execution +priority of a task. + + "task_setnice": { + "task": "DIGEST", + "nice": "NN" + } + + task: The TASK_ID of the process whose priority is + to be set. + + nice: The numerical priority value to be set. + + +The 'task_setioprio' event describes a request to set the io priority +of a task. + + "task_setioprio": { + "task": "DIGEST", + "ioprio": "NN" + } + + task: The TASK_ID of the process whose I/O priority is + to be set. + + ioprio: The numerical priority value to be set. + + + +The 'task_getioprio' event describes a request to get the io priority +of a task. + + "task_getioprio": { + "task": "DIGEST" + } + + task: The TASK_ID of the process whose I/O priority is + to be obtained. + + +The 'task_prlimit' event describes a request to get or set resource +limits for a task + + "task_prlimit": { + "cred": {} + "tcred": {} + } + + cred: A credential structure, defined below, that + describes the credentials of the task that + is requesting the right to set resource limits + for a task. + + tcred: A credential structure that describes the + credentials of the task whose resource limits + are to queried. + + +The 'task_setrlimit' event describes a request to set a new resource +limit for a task. + + "task_setrlimit": { + "task": "DIGEST" + "new_rlim": { + "resource": "NN", + "current": "NN", + "max": "NN" + } + } + + task: The TASK_ID of the process whose resource limit + is to be set. + + new_rlim: A structure with the following key values that + describes the resource limit being set: + + resource: The numeric identifier for the + resource being set. + + current: The current numeric value of + the resource. + + max: The new numeric value for the + resource. + + +The 'task_setscheduler' event describes a request to set a new scheduler +type for a task. + + "task_setscheduler": { + "task": "DIGEST" + } + + task: The TASK_ID of the process whose scheduler is + to be set. + + +The 'task_getscheduler' event describes a request to obtain the type of +process scheduler that is set for a task. + + "task_getscheduler": { + "task": "DIGEST" + } + + task: The TASK_ID of the process whose scheduler + type is to be queried. + + +The 'task_prctl' event describes a request to perform a process control +operation on the task that is executing the event. + + "task_prctl": { + "option": "NN", + "arg2": "NN", + "arg3": "NN", + "arg4": "NN", + "arg5": "NN + } + + option: The type of process control operation that is + to be performed. + + arg2-5: A total of four numeric values that describe + the process control operation. + + +The 'file_open' event describes a request to open a file. + + "file_open": { + "file": {} + } + + file: A structure describing the file that is to be opened. + This structure is used as a parameter in multiple + events and will be documented later. + + +The 'file_ioctl' event describes a request to issue an ioctl call against +a file. + + "file_ioctl": { + "file": {}, + "cmd": "NN + } + + file: The description of the file that the ioctl call is + to be issued against. + + prot: The numeric value of the argument specifying the memory + protections that are to be implemented. + + flags: The flags argument specifying the type of mapping to be + implemented. See the mmap(2) manpage for information + about the protection and flags argument to the mmap + system call. + + +The 'file_lock' event describes a request for permission to issue a +lock operation against the specified file. + + "file_lock": { + "file": {}, + "cmd": "NN" + } + + file: The description of the file that the lock is to be + placed on. + + cmd: The numeric coding of the lock operation that is to + be performed. + + +The 'file_fcntl' event describes a request for permission to issue a +perform a file operation on the designated file. + + "file_fcntl": { + "file": {}, + "cmd": "NN" + } + + file: The description of the file that the file operation is + to be conducted on. + + cmd: The numeric coding of the type of file operation that + is to be performed. + + +The 'file_receive' event describes a request for permission to receive +an open file descriptor though an InterProcess Communications (IPC) +call. + + "file_receive": { + "file": {}, + } + + file: The description of the file whose file descriptor is to + be received. + + +The 'unix_stream_connect' event describes a request for permission to +establish a UNIX domain stream connection. This request for +permission is for AF_UNIX sockets that are not enumerated using the +traditional filename model. + + "unix_stream_connect": { + "sock": {}, + "other": "{}" + } + + sock: A JSON socket structure describing the socket that is + to be connected to a second socket. A socket description + structure is used by other socket operations and its + characteristics will be documented later. + + other: A JSON socket structure describing the socket that is + to be connected to. + + +The 'unix_may_send' event describes a request for permission to send +a datagram from an originating socket to a destination socket. This +permission check is associated with the non-filename based sockets +described above. + + "unix_may_send": { + "sock": {}, + "other": "{}" + } + + sock: A structure describing the socket that is originating + the datagram. + + other: A structure describing the socket that is to receive + the datagram. + + +The 'socket_create' event describes a request for permission to create +a networking socket. + + "socket_create": { + "family": NN, + "type": "NN", + "protocol": "NN", + "kern": "NN" + } + + family: A numeric value describing the protocol family + that the socket is to be created for, ie. AF_UNIX, + AF_INET etc. + + type: The type of communications the socket is to + be used for, ie. SOCKET_STREAM, SOCKET_DGRAM. + + protocol: An optional protocol to be used with the socket. + + kern: A boolean value used to indicate whether or + not the socket is kernel based. + + +The 'socket_connect' event describes a request for permission to +create connect a socket to a remote endpoint address. + + "socket_connect": { + "sock": {}, + "addr": {} + } + + sock: A JSON socket description of the socket making + the connection request. + + addr: A JSON socket structure describing the connection + endpoint address. + + +The 'socket_bind' event describes a request for permission to bind a +socket to a network endpoint address. + + "socket_bind": { + "sock": {}, + "addr": {} + } + + sock: A JSON socket description of the socket making + the request. + + addr: A JSON address description describing the address + that is being bound to. + + +The 'socket_accept' event describes a request for permission for a +socket to accept a connection from a remote address. + + "socket_accept": { + "sock": {}, + "addr": {} + } + + sock: A JSON socket description of the socket making + the request. + + addr: A JSON address description of the address being + accepted by the socket. + + +The 'socket_listen' event describes a request for permission for a +socket to listen for a connection. + + "socket_listen": { + "sock": {}, + "backlog": "NN" + } + + sock: A JSON socket description of the socket making + the request to listen. + + backlog: The maximum number of entries that will be + allowed to be queued waiting for acceptance. + + +The 'socket_socketpair' event describes a request for permission to +create a pair of connected sockets. + + "socket_socketpair": { + "socka": {}, + "sockb": {}" + } + + socka: A JSON socket description of the first socket in + the pair. + + sockb: A JSON socket description of the second socket in + the pair. + + +The 'socket_sendmsg' event describes a request for permission to +send a message over a socket. + + "socket_sendmsg": { + "sock": {}, + "addr": {}" + } + + sock: A JSON socket description of the socket sending + the message. + + addr: A JSON address description defining the address + to which the message is being sent. + + +The 'socket_recvmsg' event describes a request for permission to +receive a message over a socket. + + "socket_recvmsg": { + "sock": {}, + "addr": {}" + } + + sock: A JSON socket description of the socket receiving + the message. + + addr: A JSON address description the address from which + the message is being received. + + +The 'socket_getsockname' event describes a request for permission to +read the address of a socket. + + "socket_getsockname": { + "sock": {} + } + + sock: A JSON socket description of the socket whose + address is to be read. + + +The 'socket_getpeername' event describes a request for permission to +read the address of the socket's peer. + + "socket_getpeername": { + "sock": {} + } + + sock: A JSON socket description of the socket the + peer address is to be obtained from. + + +The 'socket_setsockopt' event describes a request for permission to +set an option on a socket. + + "socket_setsockopt": { + "sock": {}, + "level": "NN", + "optname": "NN" + } + + sock: A JSON socket description of the socket whose + options are to be set. + + level: The protocol level at which the option will be + set. + + optname: The numeric coding of the option that will be set. + + +The 'socket_shutdown' event describes a request for permission to shutdown +a socket. + + "socket_shutdown": { + "sock": {}, + "how": "NN" + } + + sock: A JSON socket description of the socket that is + to be shutdown. + + how: A numeric code specifying how send and receives + on the socket are to be handled by the shutdown. + + +The 'ptrace_traceme' event describes a request for permission for the +parent process to trace the process that is executing the security +handler. + + "ptrace_traceme": { + "source": "DIGEST" + } + + source: The TASK_ID of the process requesting tracing + permissions. + + +The 'kernel_module_request' event describes a request for permission +to load a kernel module. + + "kernel_module_request": { + "kmod_name": "ASCII" + } + + kmod_name: The name of the kernel module being requested. + + +The 'kernel_load_data' event describes a request for permission to +load data into the kernel + + "kernel_load_data": { + "id": "NN", + "contents": "NN" + } + + id: A numeric identifier value representing the + type of data to be loaded. + + contents: A flag variable indicating if post data + loading will be used. + + +The 'kernel_read_file' event describes a request for permission for the +kernel to load a file specified by userspace. + + "kernel_read_file": { + "file": {}, + "contents": "NN" + } + + file: A JSON description of the file to be read. + + id: A numeric identifier value representing the + type of data to be loaded. + + +The 'sb_mount' event describes a request for permission to mount a +filesystem. + + sb_mount { + "dev_name": "ASCII", + "path": {} + "type": "ASCII", + "flags": "NN" + } + + dev_name: The name of the device that is to be mounted. + + path: A JSON path description describing where the + device is to be mounted. + + type: The type of filesystem to be mounted. + + flags: The mount flags to be implemented. + + +The 'sb_umount' event describes a request to unmount a filesystem. + + "sb_umount": { + "mnt", {}, + "flags", "NN" + } + + mnt: A JSON dentry structure defining the mount point + that is to be unmounted. + + flags: A numerical value specifying the flags passed + to the unmount command. + + +The 'sb_remount' event describes a request for permission to remount a +filesystem. + + "sb_remount": { + "sb": {} + "dentry": {}, + "fstype": "ASCII", + "s_flags": "NN" + } + + sb: A JSON superblock structure describing the + mountpoint that is to be remounted. + + dentry: A JSON dentry structure describing the mountpoint + that is to be remounted. + + fstype: The name of the filesystem being remounted. + + s_flags: The superblock flags describing the mount. + + +The 'sb_pivotroot' event describes a request for permission to pivot +the root filesystem to a new mount point. + + "sb_pivotroot": { + "old_path": {}, + "new_path": {} + } + + old_path: A JSON path structure specifying the location + from which the root filesystem is to be moved. + + new_path: A JSON path structure specifying the location + to which the root filesystem is to be moved. + + +The 'sb_statfs' event describes a request for permission to access +filesystem statistics. + + "sb_statfs": { + "dentry": {}, + } + + dentry: A JSON dentry structure describing the superblock + location for the request. + + +The 'move_mount' event describes a request for permission to move a +mount from one location to another. + + "move_mount": { + "from_path": {}, + "to_path": {} + } + + from_path: A JSON path structure describing the location from + where the mount is to be moved from. + + to_path: A JSON path structure describing the location to + where the mount is to be moved to. + + +The 'shm_associate' event structure describes a request for permission +to access an existing shared memory region. + + "shm_associate": { + "perm": {}, + "shmflg": "NN" + } + + perm: A JSON IPC permissions structure describing the + permissions of the shared memory region. + + shmflg: A numeric flag describing the shared memory + operations being requested. + + +The 'shm_shmctl' event structure describes a request for permission to +execute a command on a SYSV shared memory region. + + "shm_shmctl": { + "perm": {}, + "cmd": "NN" + } + + perm: A JSON IPC permissions structure describing the + permissions of the shared memory region. + + cmd: The command whose permission is to be checked. + + +The 'shm_shmat' event structure describes a request for permission to +attach to a shared memory region. + + "shm_shmat": { + "perm": {}, + "shmflg": "NN" + } + + perm: A JSON IPC permissions structure describing the + permissions of the shared memory region. + + shmflg: The shared memory attachment command being + requested. + + +The 'sem_associate' event structure describes a request for permission +to attach to an existing shared memory semaphore. + + "sem_associate": { + "perm": {}, + "shmflg": "NN" + } + + perm: A JSON IPC permissions structure describing the + permissions of the semaphores that the + association request is being requested for. + + semflg: The semaphore operations flag. + + +The 'sem_semctl' event structure describes a request for permission +to execute a control operation on a shared memory semaphore. + + "sem_semctl": { + "perm": {}, + "cmd": "NN" + } + + perm: A JSON IPC permissions structure describing the + permissions of on the semaphore that the control + command is being requested on. + + cmd: The semaphore command that is being requested. + + +The 'sem_semop' event structure describes a request for permission to +request an operation on a semaphore set. + + "sem_semctl": { + "perm": {}, + "nsops": "NN", + "alter": "NN" + } + + perm: A JSON IPC permissions structure describing the + permissions of the semaphore set that the + operation is being requested on. + + nsops: The number of operations to be performed. + + alter: The changes that are to be made. + + +The 'syslog' event structure describes a request for permission to +access the kernel message buffers or change console logging. + + "syslog": { + "type": NN + } + + type: The action that is being requested. + + +The 'settime' event structure describes a request for permission to +access the kernel message buffers or change console logging. + + "settime": { + "ts": { + "seconds", "NN", + "nsecs", "NN" + }, + "tz": { + "minuteswest", "NN", + "dsttime", "NN" + } + } + + ts: If the call to the security_settime handler was + to set the time this structure will be present. + + seconds: The time in seconds to set. + + nsecs: The time in nano-seconds to be set. + + tz: If the call to the security_settime handler was + to set the timezone this structure will be present. + + minuteswest: The offset of the timezone. + + dsttime: A flag variable to indicate if daylight savings + time is to be set. + + +The 'quotactl' event structure describes a request for permission to +control manipulate filesystem quotas. + + "quotactl": { + "cmds": "NN", + "type": "NN", + "id": "NN", + "sb", {} + } + + cmds: The quota commands to be enforced. + + type: The type of quota to be enforced. + + id: The quota version to be enforced. + + sb: A JSON superblock structure describing the + filesystem on which the quota command is to + operate on. + + +The 'quota_on' event structure describes a request to enable quotas +for a filesystem. + + "quota_on": { + "dentry": {} + } + + dentry: A JSON dentry description describing the + mountpoint that quotas are to be enabled on. + + +The 'msg_queue_associate' event structure describes a request to +access a previously defined message queue. + + "msg_queue_associate": { + "perm": {} + "msgflg": "NN" + } + + perm: A JSON IPC permissions structure describing the + permissions of the message queue that association + is being requested for. + + msgflg: A numeric flag indicating the operation that is + being requested. + + +The 'msg_queue_msgctl' event structure describes a request to execute +a control operation on a shared memory message queue region. + + "msg_queue_msgctl": { + "perm": {} + "cmd": "NN" + } + + perm: A JSON IPC permissions structure describing the + permissions of the shared memory message queue + region that the control command is being + requested on. + + cmd: A numeric flag indicating the control operation + that is to be executed. + + +The 'msg_queue_msgsnd' event structure describes a request to send a +message over a shared memory message queue. + + "msg_queue_msgsnd": { + "perm": {} + "msqflg": "NN" + } + + perm: A JSON IPC permissions structure describing the + permissions of the shared memory message queue + region that the message is being sent on. + + msqflg: A numeric coding of the operation that is to + be performed. + + +The 'msg_queue_msgrcv' event structure describes a request to receive +a message over a shared memory message queue. + + "msg_queue_msgrcv": { + "perm": {} + "target": "DIGEST" + "type": "NN", + "mode": + } + + perm: A JSON IPC permissions structure describing the + permissions of the shared memory message queue + region that the message is being received on. + + target: The TASK_ID of the process that is to receive + the message. + + type: The type of message that is to be received. + + mode: The operations flag for the message. + + +The 'ipc_permission' event structure describes a request to check if +SYSV IPC access is to be allowed. + + "ipc_permission": { + "perm": {} + "target": "DIGEST" + "type": "NN", + "mode": "NN" + } + + ipcp: A JSON IPC permissions structure describing the + permissions on the SYSV shared memory region that + is to be checked. + + flag: The access permissions being requested. + + +The 'key_alloc' event structure describes a request for permission to +allocate and initialize a kernel key structure. + + "key_alloc": { + "creds": {}, + "flags": "NN" + } + + creds: A JSON credentials structure describing the + the access controls for the key structure + + flags: A numeric control constant describing how the + key is to be created. + + +The 'key_permission' event structure describes a request for +permission to execute a control operation on a kernel key structure. + + "key_permission": { + "key_refs": { + "possessed": "NN", + "uid": "NN", + "gid": "NN", + "flags": "NN" + } + "creds": {}, + "perm", "NN" + } + + key_refs: A JSON structure describing the key to be + accessed. + + possessed: A flag indicating whether or not the key is + possessed by a process. + + uid: The user id that owns the key. + + gid: The group id that owns the key. + + flags: Flags describing the state of the key. + + creds A credentials structure describing the task that + is requesting access to the key. + + perm: The permissions that are being requested on the + key. + + +The 'netlink_send' event structure describes the permissions that are +to be set on a netlink socket. + + "netlink_send": { + "sock": {}, + "uid": "NN", + "gid": "NN" + "portid": "NN", + "dst_group": "NN", + "nsid_set": "NN", + "nsid": "NN" + } + + sock: A JSON socket description structure describing + the socket over which the message is to be sent. + + uid: The user id that the message is to be sent to. + + gid: The group id that the message is to be sent to. + + portid: The port identifier of the + + dst_group: The destination group for the message. + + nsid_set: A flag variable indicating that the nsid value + is valid. + + nsid: The namespace identifier. + + +The 'inode_create' event structure describes a request to create an +inode. + + "inode_create": { + "dir": {}, + "dentry": {}, + "mode": "0MM" + } + + dir: A JSON inode description describing the directory + the inode is to be created under. + + dentry: A JSON dentry description describing the location + where the inode is to be created. + + mode: The permissions to be assigned to the inode. + + +The 'inode_link' event structure describes a request to link an inode +to a new location. + + "inode_link": { + "old_dentry": {}, + "dir": {}, + "new_dentry": {} + } + + old_dentry: A JSON dentry description describing the current + location of the inode. + + dir A JSON inode description describing the parent + directory under which the inode link will be + created. + + new_dentry: A JSON dentry description describing the location + of the new link. + + +The 'inode_unlink' event structure describes a request to delete an +inode. + + "inode_unlink": { + "dir": {}, + "dentry": {} + } + + dir: A JSON inode structure describing the parent + directory from which the inode is to be removed. + + dentry: A JSON dentry description describing the inode + to be removed. + + +The inode_symlink event structure describes a request to delete an inode. + + "inode_symlink": { + "dir": {}, + "dentry": {}, + "old_name": "ASCII" + } + + dir: A JSON inode structure describing the parent + directory in which the link is to be created. + + dentry: A JSON dentry description describing the inode + that is to be created as the link. + + old_name: The name of the existing filename to be linked. + + +The 'inode_mkdir' event structure describes a request to create an +directory. + + "inode_mkdir": { + "dir": {}, + "dentry": {}, + "mode": "0MM" + } + + dir: A JSON inode structure describing the parent + directory in which the directory is to be created. + + dentry: A JSON dentry description describing the directory + inode that is to be created. + + mode: The permissions to be assigned to the new directory. + + +The 'inode_rmdir' event structure describes a request to remove a +directory inode. + + "inode_rmdir": { + "dir": {}, + "dentry": {}, + } + + dir: A JSON inode structure describing the parent + directory of the directory being removed. + + dentry: A JSON dentry description describing the directory + inode that is to be removed. + + mode: The permissions to be assigned to the new directory. + + +The 'inode_mknod' event structure describes a request to remove a directory +inode. + + "inode_mknod": { + "dir": {}, + "dentry": {}, + "mode": "0MM", + "dev": { + "major": "NN", + "minor": "NN" + } + } + + dir: A JSON inode structure describing the directory + under which the device node is being created. + + dentry: A JSON dentry description describing the location + of the device node inode. + + mode: The permissions to be assigned to the device node. + + dev: A JSON structure describing the device node + characteristics: + + major: The major number of the device to be + created. + + minor: The minor number of the device to be + created. + + +The 'inode_rename' event structure describes a request to rename an +inode. + + "inode_rename": { + "old_dir": {}, + "old_dentry": {}, + "new_dir": {}, + "new_dentry": {} + } + + old_dir: A JSON inode structure describing the parent + directory of the inode to be renamed. + + old_dentry: A JSON dentry description describing the location + of the inode to be renamed. + + new_dir: A JSON inode structure describing the parent + directory of the new location of the inode. + + new_dentry: A JSON dentry description describing the new + inode location. + + +The 'inode_setattr' event structure describes a request to set the +attributes of an inode. + + "inode_setattr": { + "dentry": {}, + "attr": { + "valid": "NN", + "mode": "0MM", + "uid": "NN", + "gid": "NN", + "size": "NN + } + } + + dentry: A JSON dentry structure describing the inode + whose characteristics are to be set. + + attr: A JSON structure describing the characteristics + that will be set: + + valid: A numeric encoding of what characteristics + of the inode should be set. + + mode: If requested, the permissions that are to + be set. + + uid: If requested, the new user id to be set. + + gid: If requested, the new group id to be set. + + size: If requested, the new size of the file. + + + +The 'inode_getattr' event structure describes a request to obtain the +attributes of an inode. + + "inode_getattr": { + "path": {} + } + + path: A JSON path structure describing the path to + the inode whose attributes are to be retrieved. + + +The 'inode_setxattr' event structure describes a request to set the +extended attributes of an inode. + + "inode_setxattr": { + "dentry": {}, + "name": "ASCII", + "value": "BASE64", + "flags": "NN" + } + + dentry: A JSON dentry structure describing the inode + whose extended attributes are to be set. + + name: The name of the attribute to be set. + + value: The value of the attribute to be set encoded + as a Base64 ASCII string. + + flags: The operations flag for how to set the attribute. + + +The 'inode_getxattr' event structure describes a request to get the +extended attributes of an inode. + + "inode_getxattr": { + "dentry": {}, + "name": "ASCII", + } + + dentry: A JSON dentry structure describing the inode + whose extended attributes are to be requested. + + name: The name of the attribute whose value is to + be requested. + + +The 'inode_listxattr' event structure describes a request to list the +extended attributes attached to an inode. + + "inode_listxattr": { + "dentry": {} + } + + dentry: A JSON dentry structure describing the inode + whose extended attributes are to be listed. + + +The 'inode_removexattr' event structure describes a request to remove +an extended attributes attached to an inode. + + "inode_removexattr": { + "dentry": {}, + "name": "ASCII" + } + + dentry: A JSON dentry structure describing the inode + whose extended attribute are to be removed. + + name: The name of the extended attribute that is to + be removed. + + +The 'inode_killpriv' event structure describes a request to remove +privileges from an inode. + + "inode_killpriv": { + "dentry": {} + } + + dentry: A JSON dentry structure describing the inode + whose privileges are to be removed. + + +The 'tun_dev_create' event structure describes a request to create a +TUN network device. + + "tun_dev_create": { + } + + No parameters are processed for this request. + + +The 'tun_dev_attach_queue' event structure describes a request to +attach a TUN device queue. + + "tun_dev_attach_queue": { + } + + No parameters are processed for this request. + + +The 'tun_dev_attach' event structure describes a request to attach to +a TUN device. + + "tun_dev_attach": { + "sock": {} + } + + sock: A JSON socket description structure describing + the socket requesting access to the TUN device. + + +The 'tun_dev_open' event structure describes a request to open a +network TUN device. + + "tun_dev_open": { + } + + No parameters are processed for this request. + + +The 'bpf' event structure describes a request to use the BPS system +call. + + "bpf": { + "cmd": "NN", + "attr": { + "size": "NN" + } + } + + cmd: The BPF command that is being requested. + + attr: A JSON structure describing the attributes for + the command: + + size: The size of the BPF attribute description + that will be passed to the BPF call. + + +The 'bpf_map' event structure describes a request for permission to +access a BPF map. + + "bpf_map": { + "map": { + "map_type": "NN", + "fmode": "NN" + } + } + + map: A JSON description structure used to describe the + map that will be accessed. + + fmode: The permissions to be used for accessing the + map. + + +The 'bpf_prog' event structure describes a request for permission to +access a BPF program. + + "bpf_prog": { + "prog": { + "type": "NN", + "attach_type": "NN" + } + } + + prog: A JSON description structure used to describe the + program that access is being requested to. + + type: The type of program being described. + + attach_type: The type of attachment to be used for the + program. + + +The 'capable' event structure describes a request to check if a +process has a specific capability. + + "capable": { + "cap": "NN", + "opts": "NN" + } + + cap: The capability being requested. + + opts: The options directing how the check is to be + performed. + + +The 'capget' event structure describes a request to return the +capability sets that a process has. + + "capget": { + "target": "DIGEST", + "effective": "0xHEX", + "inheritable": "0xHEX", + "permitted": "0xHEX" + } + + target: The TASK_ID of the process whose capability masks + are to be checked. + + effective: The effective capabilities of the process. + + inheritable: The inheritable capabilities of the process. + + permitted: The permitted capabilities of the process. + + +The 'capset' event structure describes a request to set the +capabilities of a target process. + + "capset": { + "effective": "0xHEX", + "inheritable": "0xHEX", + "permitted": "0xHEX" + } + + effective: The effective capabilities to be set. + + inheritable: The inheritable capabilities to be set. + + permitted: The permitted capabilities to be set. + + +Security event export encodings: +-------------------------------- + +External trust orchestrator's read security events from the control +plane file created for an externally modeled security namespace. + +The encoding of the event is in the following form: + +{"export": {}, "event": {}} + +Where the event structure is the previously described event structure +with the following additional key: + + pid: The process identifier (PID) of the task that + generated the event. + + The primary TSEM documentation file documents why + this value can be safely used in this context. + + +The 'export' structure encodes the characteristics type of the +exported event. + + "export:" { + "type": "ASCII", + "TYPE": {} + } + +The 'export' structure is a self-describing structure where the ASCII +string that serves as the value of the 'type' key will serve as the +key value that describes the payload structure of the event, ie. the +TYPE key will be substituted with the ASCII string. + +The following payload labels are defined: + + event + async_event + aggregate + log + +The event and async_event payload labels both resolve to the +previously documented 'event' payload. The use of different labels +for the event is to provide a means for a trust orchestrator to know +the context that the process was running in when the event was +generated. + +The 'aggregate' structure defines the hardware boot measurement +described previously in this documentation. + + "aggregate": { + "value": "DIGEST" + } + + value: The hardware aggregate measurement. + + +The 'log' structure is used to describe a security event that was +requested while the task was running in untrusted status after a +security model violating event: + + "log": { + "process": "ASCII", + "event": "ASCII", + "action": "ASCII" + } + + process: The process name (current->comm) that invoked + the event. + + event: The CELL description name of the event that + was requested. + + action: A string value indicating how the event was + enforced, either DENY or LOG. diff --git a/Documentation/admin-guide/LSM/index.rst b/Documentation/admin-guide/LSM/index.rst index a6ba95fbaa9f..cebd3b02598d 100644 --- a/Documentation/admin-guide/LSM/index.rst +++ b/Documentation/admin-guide/LSM/index.rst @@ -47,3 +47,4 @@ subdirectories. tomoyo Yama SafeSetID + tsem diff --git a/Documentation/admin-guide/LSM/tsem.rst b/Documentation/admin-guide/LSM/tsem.rst new file mode 100644 index 000000000000..1c69a5cb7517 --- /dev/null +++ b/Documentation/admin-guide/LSM/tsem.rst @@ -0,0 +1,1680 @@ +==== +TSEM +==== + + "This is the story of the wine of Brule, and it shows what + men love is never money itself but their own way, and + that human beings love sympathy and pageant above all + things." + - Hilaire Belloc + The Path to Rome + +TSEM is the Trusted Security Event Modeling system. TSEM is the +kernel LSM based infrastructure that provides a platform for +implementing model based mandatory access controls. TSEM is model +agnostic and is designed to support deterministic, quasi-deterministic +and machine learning models. + +TSEM also provides a framework for implementing Host Based Intrusion +Detection (HIDS) and anomaly interdiction systems without the need to +write kernel code or implement kernel loadable modules or BPF +programs. + +The design and implementation of TSEM is inspired by the notion that +the security behavior of a platform, or a workload, like all other +physical phenomenon, can be mathematically modeled. + +Security, is at once, both a technical and economic problem. One of +the objectives of TSEM is to address inherent and structural economic +barriers to security, by introducing technology that reduces the skill +and time needed to implement a level of security, equivalent to what +can be achieved by mandatory access controls, through unit testing of +an application stack. + +A second objective is to reduce the skill, complexity and +infrastructure needed to create trusted and remotely attestable +platforms and/or workloads. + +To assist in achieving these objectives, TSEM implements the concept +of a security modeling namespace that reduces the scope and hence +complexity of a security model and allows it to be limited to the +level of a single process hierarchy or a container. + +TSEM is the Linux kernel component of a new security architecture +introduced by the Quixote Project, the notion of a Trust Orchestration +System (TOS). The Quixote Project provides a complete implementation +of the userspace tools and utilities that are used in combination +with the TSEM LSM. + +The Trusted Computed Base (TCB) for a platform or a subordinate +workload is maintained and enforced by a Trust Orchestrator (TO). +Paired with a Trust Orchestrator is a Trusted Modeling Agent (TMA) +that maps the description of a security event into a security state +coefficient. + +TSEM is implemented as a Linux Security Module (LSM) and is designed +to be self-contained with little or no dependency on kernel +infrastructure, other than the LSM hooks themselves. It can be +stacked in any order with existing LSM's. It is implemented as the +first LSM in the call sequence, since it provides infrastructure that +can be used, for example, to validate extended attributes that may be +used by subsequently invoked LSM's. + +TSEM implements mandatory access controls, without a requirement for +extended attributes, filesystem labeling or the need to protect +filesystem metadata against offline attack. A mathematically defined +security model, generated by unit testing of a workload, is the +bearer's token that carries the security guarantee for a system or +workload. + +TBDHTTRAD +========= + +A quick summary for those interested in experimenting with trust +orchestration and security modeling but are constrained by the concept +of: 'Too Busy Don't Have Time To Read Any Documentation'. + +A kernel with TSEM support in its list of enabled LSM's must be +available for use. A TSEM enabled kernel will have the tsem keyword +in the following file: + +/sys/kernel/security/lsm + +For experimentation, or integrating TSEM modeling into a Continuous +Integration/Continous Development (CI/CD) workflow, modeling can be +restricted to subordinate security modeling namespaces by booting a +kernel with the following kernel command-line option: + +tsem_mode=no_root_modeling + +This disables modeling of the root security modeling namespace and +only implements modeling for subordinate security namespaces. + +The Quixote trust orchestration utilities either need to be built or +the statically compiled sample utilities need to be installed. Source +for the userspace utilities and compiled sample programs are available +at the following location: + +https://github.com/Quixote-Project + +After installing the utilities, two shell sessions will be needed with +root privileges in each shell. + +The following directories need to be in the PATH variable of each shell: + +/opt/Quixote/sbin +/opt/Quixote/bin + +Execute the following command to start a process in an independent +security namespace with the modeling being done in the kernel: + +quixote -P -c test -o test.model + +In the second shell session, run the following command to display the +security execution trajectory of the model: + +quixote-console -p test -T + +In the shell session provided by the trust orchestrator, run the +following command: + +grep SOME_STRING /etc/passwd + +Then exit the shell. + +The orchestrator will indicate that the security model definition has +been written to the test.model file. + +Run the following command to execute a shell in an enforced security +model obtained from the previous session: + +quixote -P -c test -m test.model -e + +In the shell that is provided, run the following command: + +cat /etc/passwd + +The command will fail. + +Running the following command in the second shell session will output +forensics on the command that failed: + +quixote-console -p test -F + +Executing additional commands in the trust orchestrated shell will +cause additional entries to be added to the forensics trajectory. + +The test can be repeated using the quixote-us trust orchestrator. +This test will model the security namespace in a userspace process +rather than in the kernel based trusted modeling agent. + +Mandatory Access Controls +========================= + + "If I have seen further it is by standing on the shoulders of + Giants." + - Sir Isaac Newton + +It is assumed that astute readers will be familiar with classic +subject/object based mandatory access controls; or at least astute +enough to use a search engine to develop a modicum of secundem artem +in the discipline. + +Very simplistically, subject/object based mandatory access controls +can be thought of as being implemented with a two dimensional access +vector matrix, with some type of a description of a process (subject) +on one axis and a description of a data sync/source (object), +typically an inode, on the second axis. The descriptions are +commonly referred to as subjects and objects. + +A security policy is developed that assigns a boolean value for each +element of the matrix that specifies whether or not permission should +be granted for the subject to access the object. + +These schemes are frequently referred to as 'mandatory access +controls', since only the kernel has the ability to implement the +labeling and decision processes. In these systems, the root or +administrative user has no ability to affect kernel decision making +with respect to whether or not permission is granted or denied. + +These systems were derived from governmental and military information +classification systems and are capable of delivering security +guarantees appropriate to classified and high sensitivity assets. The +delivery of these security guarantees comes with it a reputation for +complexity and fragility. + +Development of a system wide security policy is a complex process and +administration of such systems is frequently done in an iterative +fashion. The system is monitored for permission denials with +modifications to correct these false denials folded back into the +policy. In many cases, mandatory access control systems are run in +warning rather than enforcing mode and used as an indicator for +potential security violations. + +One of the additional challenges is that the integrity of labels is +fundamental to the ability of these systems to deliver their security +guarantees. This requires that the labeling process be conducted +under security controlled conditions, with the labels subsequently +protected against offline modification by cryptographic integrity +guarantees. + +Mandatory access controls had their origin in centralized multi-user +platforms, and before the now widely accepted, strategy of using +resource compartmentalization (namespaces) to isolate applications +from each other and the system at large. A legitimate technical +argument can be made as to whether or not enforcement of a system wide +security policy is suitable for these environments. + +At the other end of the spectrum, in embedded systems, structural +economic barriers incent very little attention to security, where time +to market is the primary goal. These systems are pushed into the +field, many time for multi-year operational lifetimes, with little +prospect for upgrades or any notion of an iterative tuning process of +a security policy. + +Security Event Modeling +======================= + + "We can no longer speak of the behavior of the particle + independently of the process of observation. As a final + consequence, the natural laws formulated mathematically in + quantum theory no longer deal with the elementary particles + themselves but with our knowledge of them. Nor is it any + longer possible to ask whether or not these particles exist in + space and time objectively ... When we speak of the picture of + nature in the exact science of our age, we do not mean a + picture of nature so much as a picture of our relationships + with nature. ...Science no longer confronts nature as an + objective observer, but sees itself as an actor in this + interplay between man and nature. The scientific method of + analysing, explaining and classifying has become conscious of + its limitations, which arise out of the fact that by its + intervention science alters and refashions the object of + investigation. In other words, method and object can no longer + be separated." + - Werner Karl Heisenberg + +Security Event Modeling (SEM), is an alternative strategy to implement +the security guarantees of mandatory access and integrity controls, in +a manner that is consistent with emerging application development +strategies such as namespaces and CI/CD workflows. + +As was noted at the start of this document, the premise for SEM is +that the security behavior of a platform, or alternatively a workload, +can be modeled like any other physical phenomenon in science and +engineering. + +Inspiration for this came from the primary TSEM author/architect +having trained as a quantum chemist, conducting very early research in +the development of multi-scale modeling strategies for molecules of +size to be of interest to pharmaceutical intents. + +SEM is premised on the theory that kernel security architects have +instrumented the LSM security event hooks to be called in locations +before security sensitive operations are conducted, with appropriate +descriptive parameters, that are considered relevant to the security +posture of the kernel. With respect to modeling, the security event +hooks are conceptualized as representing the independent variables of +a basis set that yields a functional definition for the security state +of an execution trajectory. + +SEM can be framed in the context of classic subject/object mandatory +access controls, by the notion that a unique identity can be generated +for each element of an access vector matrix, rather than a boolean +value. In SEM, a security execution trajectory is defined by the set +of security state coefficients that a process hierarchy (workload) +generates. This execution trajectory produces a vector of identities, +whose sum in an appropriate form, yields a functional definition of +the security state of the system. + +Two subordinate identities are combined to yield a security event +state coefficient. These subordinate identities are referred to as +the Context Of Execution (COE) and the CELL, which are conceptually +similar to the subject and object in mandatory access control. The +COE identity is derived from the parameters that describe the security +relevant characteristics (ie. credentials) of a process, while the +CELL value is derived from the parameters used by a security event +hook to describe the characteristics of the event. + +A security policy is implemented by a modeling algorithm that +translates COE and CELL event parameters into their respective +identities. The COE and CELL are combined to yield a security state +coefficient that uniquely describes the security event in the security +model. Different security policies and criteria can be developed by +modifying how the modeling algorithm utilizes the COE and CELL +characteristics. + +Since the security policy is implemented with a modeling algorithm, a +single platform can support multiple and arbitrary security policies. +The equivalent of a resource namespace in SEM is referred to as a +security modeling namespace. + +The formation of the security state coefficients from existing kernel +parameters eliminates the need for the use of extended attributes to +hold security label definitions. In SEM, a cryptographically signed +security model definition, designed to be interpreted by a modeling +algorithm, becomes the bearer's token for the security of the modeled +workload, rather than information encoded in filesystem security +attributes. + +Trusted Security Event Modeling +=============================== + + "Do you see over yonder, friend Sancho, thirty or forty + hulking giants? I intend to do battle with them and slay + them." + - Don Quixote + +In TSEM, the modeling algorithm is implemented in an entity known as a +Trusted Modeling Agent (TMA), in a 'trusted' environment where +modeling is immune from modification or alteration by any activity on +the platform or in a workload. The notion of a TMA provides a +framework for such things as next generation security co-processors +that extend functionality beyond what is defined by the concept of a +Trusted Platform Module (TPM). + +In addition to providing an attestation of an execution trajectory, a +TMA, in contrast to a TPM, has the ability to advise an operating +system on whether or not an event being modeled is consistent with the +security model that is being enforced. In this manner, it introduces +a prospective rather than a retrospective trust model. + +TSEM is designed to support Trust Orchestration Systems (TOS). In a +TOS, the trust orchestrators are supervisory programs that run +workloads in independent security modeling namespaces , enforcing a +workload specific security model. Each trust orchestrator is paired +with a 'trusted partner TMA', known as a Sancho, that implements the +workload specific modeling algorithm. + +The root of trust for a security modeling namespace is based on where +the TMA instance is implemented. As an example, the Quixote TOS +implementation currently offers orchestrators for the following TMA +execution localities: + +- Kernel. + +- Userspace process. + +- SGX enclave. + +- Xen stub domain. + +- Micro-controller. + +This partitioning of trust results in the concept of security modeling +namespaces being referred to as internally or externally modeled. A +TMA implementation run in the kernel is referred to as an internally +modeled namespace; TMA's run outside of the kernel are referred to as +an externally modeled namespace. + +The TMA, regardless of locality, is responsible for processing the +characteristics that describe a security event, computing the identity +for the COE and CELL and then combining these two identities to create +a security state coefficient. With respect to modeling theory, the +coefficient is a task specific value representing the event in a +security model. + +TSEM is dispassionate with respect to the type of algorithm that is +implemented. The processing of the security event characteristics and +their conversion to security coefficients, is driven by the security +model/policy that will be implemented for the workload. The +architecture is designed to support security modeling algorithms that +are either deterministic or embrace approximations, stochastic +inference and machine learning algorithms in response to specific +workload, platform or device requirements. + +A security model, to be enforced by a trust orchestrator, is +implemented by providing the TMA with a set of security state +coefficients that are to be observed. A TMA processes the +characteristics of a security event and converts the characteristics +to a security state coefficient that is evaluated against the +coefficients provided to the TMA as the reference security model for a +workload. + +A security event that translates to one of the provided 'good' +coefficients, will cause the TMA to indicate to the trust orchestrator +that the process is to be allowed to run as a trusted process. A +security event that does not map to a known good coefficient, results +in the trust orchestrator designating that the process be labeled as +an untrusted process. + +Trust orchestrators and their associated TMA's, are designed to +support signed security models. This results in the elimination of +the requirement to verify or appraise extended attributes and other +measures currently required to protect labeled security systems or +filesystem metadata against offline attacks. + +The use of a cryptographic hash function to generate the security +coefficient results in the definition of very specific security +behaviors, that are sensitive to any variation in their +characteristics. Any offline modifications to files will result in a +coefficient that is inconsistent with a signed model provided to a +TMA. + +In order to support the development of TSEM based security models, a +TMA is designed to run in one of the following three modes: + +- Free modeling. + +- Sealed. + +- Enforcing. + +In a free modeling configuration, the TMA adds the security state +coefficient for the characteristics of a security event to the current +set of known good states. In addition, the description of the +security event is retained as a member of the security execution +trajectory for the model. This mode is used, in combination with unit +testing of a workload, to generate a security model for subsequent +enforcement. + +Placing a TMA in 'sealed' mode implies that any subsequent security +coefficients, that do not map into a known security state, are to be +considered 'forensic' violations to the security state of the model. + +This mode is designed to provide the ability to either fine tune a +model or provide early warning of a potential attempt to subvert the +security status of a workload. The characteristics of the violating +event are registered in the forensics trajectory of the model for use +in subsequent evaluation of the violating event and/or model +refinement. + +Placing a TMA model in 'enforcing' status implies that the model is in +a sealed state and any subsequent violations to the model will result +in the violating process being placed in untrusted status and a +permissions violation returned to the task invoking the security +event. + +Process and Platform Trust Status +================================= + +A fundamental concept in TSEM is the notion of providing a precise +definition for what it means for a platform or workload to be trusted. +A trusted platform or workload is one where there has not been an +attempt by a process to execute a security relevant event that does +not map into a known security state coefficient. + +The process trust status is a characteristic of the process that is +passed to any subordinate processes that are descendants of that +process. Once a process is tagged as untrusted, that characteristic +cannot be removed from the process. In a 'fruit from the poisoned +vine' paradigm, all subordinate processes created by an untrusted +process are untrusted as well. + +On entry into each TSEM security event handler, the trust status of a +process is checked before an attempt to model the event is made. An +attempt to execute a security event by an untrusted process will cause +the event, and its characteristics, to be logged. The return status +of the hook will be determined by the enforcement state of the model. +A permission denial is only returned if the TMA is running in +enforcing mode. + +If the platform running the TSEM LSM has a TPM, the hardware aggregate +value is computed at the time that TSEM is initialized. This hardware +aggregate value is the linear extension sum over Platform +Configuration Registers (PCR's) 0 through 7. This is the same +aggregate value that is computed by the Integrity Measurement +Architecture (IMA) and is the industry standard method of providing an +evaluation measurement of the hardware platform state. + +Internally modeled namespaces have the hardware aggregate measurement +included as the first event in the security model. Externally modeled +namespaces export the hardware aggregate value to the TMA for +inclusion as the first event of the model maintained by the external +TMA. + +The root security model extends each security state coefficient into a +PCR. The default PCR is 11 but is configurable through the kernel +compilation configuration process. The use of a separate PCR from IMA +allows hardware based TSEM measurements to coexist with IMA +measurement values. This hardware measurement value is designed to +allow attestation to the hardware state that the root model is running +in. + +TSEM is designed to support a philosophy where the root security +modeling namespace will be a minimum Trusted Computing Base +implementation that will only be running trust orchestrators and any +other infrastructure needed to support running workloads in +subordinate security namespaces. + +The subordinate security modeling namespaces are designed to decrease +model complexity in order to support a single functional value +describing the 'known good' security state of a subordinate security +workload. Subordinate security modeling namespaces are +non-hierarchical, ie. a security modeling namespace cannot itself +parent an additional security modeling namespace. + +The Linux TSEM Implementation +============================= + + "Sometimes the questions are complicated and the answers are + simple." + - Dr. Seuss + +The Linux TSEM implementation is deliberately simplistic and consists +of the following two generic components: + +- Security modeling namespace and security event export functionality. + +- Internal trusted modeling agent implementation. + +The security modeling namespace and export functionality is designed +to be generic infrastructure that allows security namespaces to be +created that are either internally or externally modeled. The TSEM +implementation does not pose any constraints on what type of modeling +can or should be implemented in these namespaces. + +On the theory that security event handlers represent all of the +security relevant action points in the kernel, any security or +integrity model can be implemented using the TSEM infrastructure. For +example, basic IMA functionality could be implemented by a TMA that +maps the digests of files accessed, or mapped executable, by the root +user as the security state coefficients. + +A primary intent of the Linux TSEM implementation is to provide a +generic method for implementing security policy in userspace rather +than the kernel. This is consistent with what has been the historic +understanding in Linux architecture, that policy decisions should be +delegated, when possible, to userspace rather than to kernel based +implementations. + +The model is extremely simplistic; a TMA interprets a security event +and its characteristics and advises whether or not the kernel should +designate the process as trusted or untrusted after event processing +is complete. + +The following sections discuss various aspects of the infrastructure +used to implement this architecture. + +Internal vs external modeling +----------------------------- + +When a TSEM security modeling namespace is created, a designation is +made as to whether the namespace is to be internally or externally +modeled. + +In an internally modeled namespace, the security event handlers pass the +event type and its characteristics to the designated internal trusted +modeling agent. The agent provides the permission value for the +security event handler to return as the result of the event and sets +the trust status of the process executing the event. + +In an externally modeled namespace, the event type and parameters are +exported to userspace for processing by a trust orchestrator with an +associated TMA. The trust orchestrator communicates the result of the +modeling back to the kernel to support the setting of the process +trust status. + +The exception to this model are for security event handlers that are +called in atomic, ie. non-sleeping context. The export of these +security event descriptions are done asynchronously in order to avoid +having the TSEM implementation attempt to sleep in atomic context +while the userspace trust orchestrator is scheduled for execution. + +It is up to the trust orchestrator and its security policy to +determine how it handles events that violate the security model being +enforced in this model. The Quixote trust orchestrators shut down the +entire workload running in the security namespace if an asynchronously +modeled event violates the security model being enforced and the model +is running in enforcing mode. + +Internally modeled domains are able to provide immediate interception +and modification of the trust status of a process that is violating +the security model. This has implications for the root security +namespace that is running on a system with a TPM, since the security +event coefficients are logged to the Platform Configuration Register +that is being used by TSEM. + +Issuing the TPM transaction would cause the process to attempt to +sleep while it waits for the TPM command to complete. In order to +address this issue, the TPM transactions are deferred to an ordered +workqueue for execution. The use of an ordered workqueue maintains +the time dependency of the security coefficients being registered. + +In order to handle modeling of security events in atomic context, the +TSEM implementation maintains caches (magazines) of structures that +are needed to implement the modeling and export of events. The size +of this cache can be configured independently for each individual +security modeling namespace that is created. The default +implementation is for a cache size of 32 for internally modeled +namespaces and 128 for externally modeled namespaces. + +By default the root security namespace uses a cache size of 128. This +value can be configured by the 'tsem_cache' kernel command-line +parameter to an alternate value. + +Trust Orchestrator/Process authentication +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The process identifier values (PID's) that are exported in the +security event descriptions are the unique global PID values, not the +value as seen through the lens of a PID namespace. + +PID values are, by default, not considered to be a stable identifier +between the kernel and userspace. In the case of TSEM external +modeling, the threat model for a namespace is whether or not an +adversarial process, running in either the root security modeling +namespace or another subordinate security modeling namespace, can kill +a process that is being orchestrated and substitute an alternate +process with an identical PID value. + +The suggested threat model would be that the orchestrator would set +the trust status of the adversarial process rather than the one that +had emitted the security event characteristics. The threat interval +is the latency time required for the processing of the security event +description by the trust orchestrator and its associated TMA. + +Exploiting this theoretical race is extremely complex and requires an +in depth understanding of the TSEM architecture. Rather than discuss +the conditions that must be met and their implications, this +discussion will first focus on the generic threat model and its +generic utility to an adversary followed by a treatment of the +mechanisms that TSEM implements in order to mitigate this threat. + +In short, a process in an adversarial security modeling namespace +would want to execute security events that are barred from its +security model with the hope of having them approved by an alternate +namespace. + +A process waiting for the external modeling of a security event +description can only be placed back into run state by two methods: +reception of a fatal signal or the TRUST_PENDING status bit being +cleared from its TSEM specific task control structure by a trust +orchestrator. + +If a process being evaluated receives a fatal signal, its trust status +will be set to untrusted and an error will be returned to the trust +orchestrator. The error would cause a trust violation to be +registered for the workload. In addition, the evaluation of the event +would be terminated, so a replacement process would not receive an +incorrect trust assessment for an event that was initiated by its +predecessor. + +The second issue that limits the utility of a PID substitution attack +is that from the point of substitution forward it would place the +replacement process in the context of the security model that the +trust orchestrator is enforcing. As a result, a substituted process +would not be allowed to exhibit any security behaviors inconsistent +with the model being enforced. + +If an attempt to exploit this race would be considered, an adversarial +process would have to force the termination of a process in the target +namespace and then fork and exit a process a sufficient number of +times in order to have a process under its control match the PID value +of the process that was waiting for an orchestration response. + +Measured modeling latency times for a trust orchestrator running the +deterministic Quixote TMA in userspace, on current generation x86_64 +hardware, averages 170 micro-seconds. In a worst case scenario from +the perspective of an adversary, there would be a need to force the +termination of the target process and then fork and execute a +sufficient number of times to force the PID collision during this time +interval. + +As a generic protection, TSEM in the tsem_task_kill() handler, blocks +the notion of 'cross-model' signals, ie. a signal originating from an +external security modeling namespace. This would require the +adversary to reliably force a process termination through a mechanism +other than signaling, for example, through the OOM killer whose signal +transmission would not be blocked by this policy control. + +When a subordinate security modeling namespace is created, the id +number of the namespace is registered in the tsem_task structure of +the trust orchestrator that is creating the namespace. The TSEM +driver will refuse to honor control plane requests affecting the trust +status of a process whose trust orchestrator security namespace id +does not match the namespace identifier of the process that it is +being asked to act on. + +As an additional protection, TSEM uses an authentication strategy that +allows a process running in a security modeling namespace to verify +that a control request is coming from the trust orchestrator that +initiated the namespace the process is running in. As part of the +setup of a security modeling namespace, a trust orchestrator is +required to provide an ASCII hexadecimally encoded authentication key +that matches the length of a digest value of cryptographic hash +function being used to generate security state coefficient in the +security modeling namespace. This authentication key must be provided +by the trust orchestrator for every subsequent control plane request. + +The process that is being transferred to a subordinate security +modeling namespace generates a second random key that is hashed with +the authentication key provided by the trust orchestrator, using the +hash function that has been defined for the security namespace. The +resultant digest value is compared to a list of authentication keys +for all currently executing namespaces. The selection of the second +random key is repeated until a globally unique key is generated. + +This randomly generated authentication key is stored in the tsem_task +structure of the process and propagated to any subsequent processes +that are created in the namespace. The hash product of this key and +the orchestration authentication key, ie. the globally unique key, is +placed in the tsem_task control structure of the orchestration +process. + +When a control plane request is received, the authentication key +provided by the trust orchestrator is used to re-generate an +authentication key based on the randomly generated namespace key held +by the process whose trust status is being updated. The generated +authentication key is compared to the key in the tsem_task structure +of the process issuing the orchestration call. The control plane will +refuse to honor a control plane request if the call specific key that +is generated does not match the key generated at the time the security +namespace was created. + +Event modeling +-------------- + +The generation of security state coefficients is a functional process +that uses a cryptographic hash function for the creation of the +individual identity mappings that contribute to the generation of the +security state coefficient. + +TSEM can use any cryptographic hash function available to the Linux +kernel for this purpose. The hash function to be used for a security +modeling namespace is specified as a parameter to the namespace +creation process. + +By default, the root security namespace uses sha256. This value can +be modified through the tsem_digest kernel command-line parameter. + +Since TSEM is active before the kernel has the ability to load +modules, the root modeling domain must be a cryptographic hash +function that is statically compiled into the kernel. By default the +TSEM configuration selects for the presence of the sha256 hash +function. + +TSEM security event modeling is based on the following functional +definition for a security event coefficient: + +Coeff = HF(HF(EVENT_ID) || PTASK_ID || TASK_ID || HF(COE) || HF(CELL)) + + Where: + Coeff = A security state coefficient that is equal + in length to the digest value of the + cryptographic hash function in use for the + security modeling namespace. + + HF = Security namespace specific hash function. + + || = Concatenation operator. + + EVENT_ID = The ASCII name of event. + + PTASK_ID = The TASK_ID of the parent process of the + process represented by TASK_ID. + + TASK_ID = The process specific identity of the + executable code that is calling the security + event handler. + + COE = Characteristics of the context of execution + of the event. + + CELL = Characteristics of the LSM event that is being + modeled. + +Workload or platform specific security state coefficient definitions +are generated by a TMA, using the COE or CELL characteristics that are +considered relevant for the model being implemented. These +coefficients are used to determine whether or not an event should lead +to the process being considered trusted or untrusted. + +The TASK_ID component of the function above is important with respect +to the generation of the security state coefficients. The notion of a +task identity serves to link the concepts of system integrity and +security access control. + +The TASK_ID is defined by the following function: + +TASK_ID = HF(HF(EVENT) || PTASK_ID || NULL_ID || HF(COE) || HF(CELL)) + + Where: + TASK_ID = The executable identity of the process + expressed as a digest value of length + equal to the cryptographic hash function + the security modeling namespace is using. + + HF = Security namespace specific hash function. + + || = Concatenation operator. + + EVENT = The string "bprm_committed_creds". + + PTASK_ID = The TASK_ID of the parent process of the + process whose TASK_ID is being generated. + + NULL_ID = A buffer of null bytes equal to the digest + size of the hash function being used for + the namespace. + + COE = Characteristics of the context of execution + calling the bprm_committed_creds LSM hook. + + CELL = The characteristics of the file provided + by the linux_binprm structure passed to + the security_bprm_committed_creds handler. + +An attentive reader will quickly conclude, correctly, that the TASK_ID +function generates an executable specific security coefficient for the +bprm_committed_creds security hook. The generative function for the +TASK_ID is the same as the standard security state coefficient; with +the exception that the task identity is replaced with a 'null id', +consisting of the number of null bytes in the digest size of the +namespace specific hash function. + +One of the CELL characteristics used in the computation of the task +identity is the digest of the executable file. Modifying an +executable, or attempting to execute a binary not considered in the +security model, will result in an alteration of the task identity that +propagates to the generation of invalid state coefficients. + +The task identity is saved in the TSEM specific task structure and is +used to compute the state coefficients for any security events that +the task subsequently executes. As noted in the previous paragraph, +incorporating the TASK_ID into the computation of security state +coefficients results in the security state coefficient values becoming +specific to the corpus of executable code that initiated a process. +This affords a very high degree of specificity with respect to the +security models that can be implemented. + +As was demonstrated in the TBDHTTRAD section, in contrast to standard +digest based controls, TSEM will discriminate the following commands +as different events/coefficients in a security model: + +cat /etc/shadow + +grep something /etc/shadow + +while read input +do + echo $input; +done < /etc/shadow + +An important, and perhaps subtle issue to note, is how these events +result in the change of process trust status. In the first two cases, +if access to the /etc/shadow file is not permitted by the operative +security model, the cat and grep process will become untrusted. + +In the third example, the shell process itself would become untrusted. +This would cause any subsequent attempts to execute a binary to be +considered untrusted events, even if access to the binary is a +permitted coefficient in the model. + +The integration of the PTASK_ID in the generation of the security +state coefficients causes the coefficients to be dependent on the +chain of execution of executable code. This concept generates +extremely specific security coefficients that yield the high +sensitivity of TSEM based security models. + +For example, consider the following chain of execution: + +init/systemd -> sshd -> bash + +init/systemd -> getty -> bash + +Even if the COE characteristics (credentials) of the two bash +processes are identical, the security coefficients generated by the +two bash shells will be different. This is secondary to the fact that +the TASK_ID of the two bash processes will be different by virtue of +the fact that the first bash process will have a PTASK_ID that +represents the TASK_ID of the ssh process, while the second process +will have a PTASK_ID that represents the TASK_ID of the getty process. + +This generative functions provides a framework for modeling that +yields very precise tracking of security relevant events. This is +significant with respect to detecting and addressing adversarial +techniques such as Living Off The Land (LOTL). + +Since the modeling operates at the level of a mandatory security +control, these permission denials would occur even if the process is +running with classic root privilege levels. This is secondary to the +notion that security and trust status are invested in the trust +orchestrator and ultimately the TMA. + +From a hardware perspective, this is important with respect to the +notion of a TMA being a model for a successor to the TPM. From a +system trust or integrity perspective, a TPM is designed to provide a +retrospective assessment of the actions that have occurred on a +platform. A verifying party uses the TPM event log and a PCR based +summary measurement, to verify what actions have occurred on the host, +in order to allow a determination of whether or not the platform +should be 'trusted'. + +In contrast, a TSEM/TMA based system enforces, on a real time basis, +that a platform or workload remains in a trusted state. Security +relevant actions cannot be conducted unless the TMA authorizes the +actions as being trusted. + +This is particularly important with respect to embedded systems. A +TPM based architecture would not prevent a system from having its +trust status altered. Maintaining the system in a trusted state would +require attestation polling of the system, and presumably, executing +actions if the platform has engaged in untrusted behavior. + +Conversely, a trust orchestrated software implementation enforces that +a system or workload remain in a security/trust state that it's +security model was unit tested to. + +Security model functional definitions +------------------------------------- + +Previously, classic trusted system implementations supported the +notion of the 'measurement' of the system. The measurement is the +value of a linear extension function of all the security relevant +actions recorded by a trust measurement system such as IMA. + +In TPM based trust architectures, this measurement is maintained in a +PCR. A measurement value is submitted to the TPM that extends the +current measurement using the following formula: + +MEASUREMENT = HF(CURRENT || NEW) + + Where: + MEASUREMENT = The new measurement value to be maintained + in the register for the system. + + HF = A cryptographic hash function supported + by the TPM device. + + || = Concatenation operator. + + CURRENT = The current measurement value. + + NEW = A new measurement value to be added to + the current measurement. + +The use of a cryptographic function produces a non-commutative sum +that can be used to verify the integrity of a series of measurements. +With respect to security modeling theory, this can be thought of as a +'time-dependent' measurement of the system. Stated more simply, the +measurement value is sensitive to the order in which the measurements +were made. + +In systems such as IMA, the measurement value reflects the sum of +digest values of what are considered to be security critical entities, +most principally, files that are accessed or memory that is mapped +executable, based on various policies. + +In TSEM based TMA's, the measurement of a security modeling namespace +is the sum of the unique security state coefficients generated by the +security model being enforced. As previously noted, on systems with a +TPM, the root security modeling namespace measurement is maintained by +default in PCR 11 or the PCR that was selected at kernel configuration +time. + +The challenge associated with classic integrity measurements is the +time dependent nature of using a non-commutative summing function. +The almost universal embrace of SMP based hardware architectures, in +addition to standard kernel task scheduling issues, makes the +measurement values non-deterministic. This requires a verifying party +to evaluate an event log, verified by a measurement value, to +determine whether or not the system is in a security appropriate or +trusted state. + +TSEM addresses this issue by implementing a strategy designed to +produce a single functional value that represents the functional +security state of a model. This allows a TMA to attest to the +trust/security status of a platform or workload by signing this +singular value and presenting it to a verifying party. + +In TSEM nomenclature, this functional value is referred to as the +'state' of the model. The attestation model is to use trust +orchestrators to generate the state value of a workload by unit +testing. This state value can be packaged with a utility or container +to represent a summary trust characteristic that can be attested by a +TMA, eliminating the need for a verifying partner to review and verify +an event log. + +TMA's implement this architecture by maintaining a single instance +vector of the set of unique security state coefficients that have been +experienced in a security modeling namespace. The state measurement +is generated by sorting the security state coefficient vector in +big-endian hash format and then generating a standard linear extension +measurement over this new vector. + +Any security event that generates an associated state coefficient that +is not in the model will resulted in a perturbed state function value. +That perturbed value would be interpreted by a verifying party as an +indication of an untrusted system. + +Since the TMA maintains the security event descriptions in time +ordered form, the option to provide a classic event log and +measurement are preserved and available. Extensive experience in the +development of TSEM modeled systems has demonstrated the superiority +of state value interpretation over classic measurement schemes. + +A TMA may choose to incorporate a 'base nonce' into a security model +that it is implementing, this base nonce is designed to serve in a +manner similar to an attestation nonce. If used, the trust +orchestrator is responsible for negotiating a random base nonce with a +verifying party at the time of initialization of a security modeling +namespace and providing it to the TMA. + +The TMA uses the base nonce to extend each security event coefficient +that is generated by the model. This causes the state and measurement +values of the model to become dependent on this base nonce, a process +that can be used to defeat a replay attack against the security model. + +Control plane +------------- + +Both primary functions of TSEM: security modeling namespace management +and the internal TMA modeling implementation, are controlled by +pseudo-files in the securityfs filesystem. The following directory +is the top level implementation directory for the TSEM control plane: + +/sys/kernel/security/tsem + +The following file in the kernel source tree documents, in detail, +the interfaces provided by the filesystem: + +Documentation/ABI/testing/tsem + +This filesystem is primarily intended for use by trust orchestrators +to create and manage security modeling namespaces. + +The files are process context sensitive. Writing to the control file, +or reading from the informational files, will act on or reference the +security modeling namespace that the accessing process is assigned to. + +The following files are provided in the root directory of the TSEM +control plane and implement global controls for the TSEM LSM: + + aggregate + id + control + +The 'aggregate' file is used by trust orchestrators for internally +modeled namespaces to obtain the hardware measurement value for +inclusion in a security model. A trust orchestrator for an externally +modeled namespace capture this value as the first event generated by a +security modeling namespace. + +The 'id' file is used to determine the security modeling namespace +that the process is running in. The namespace id value of 0 is +reserved for the root security modeling namespace, a non-zero value +indicates that the process is running in a subordinate security +modeling namespace. + +The TSEM implementation is controlled by the only writable file, which +is the 'control' file. + +The following keywords are used by trust orchestrators to place the +process writing to the file in an internally or externally modeled +security namespace: + + internal + external + +Each argument accepts key=value pairs that configure the namespace. +The following key values are currently accepted: + + model + nsref + digest + cache + key + +The 'model' keyword takes as an argument the name of a loadable module +that will be used to implement the event processing for a security +modeling namespace. If the module has not already been loaded, TSEM +will attempt to dynamically load the module. If the standard practice +is followed of using the KBUILD_MODNAME CPP define to set the name of +the security model, the argument to the model keyword will be that +name, a value that will match the name that is displayed by the lsmod +command. It should be noted that there is no requirement that the +security model name match the name of the module generated by the +build process. + +The 'nsref' keyword takes one of the following two values: + + initial + current + +The initial argument indicates that the UID/GID values for the COE and +CELL characteristics are derived from the initial user namespace. +This is the default characteristic if the nsref key is not specified. + +The current argument indicates that the UID/GID values are derived +from the user namespace that the process is running in, when the +request is made to model an event. + +The 'digest' keyword is used to specify the cryptographic hash +function that is to be used to create the functional values for the +security state coefficients for the namespace. The value to this +keyword is the name by which the hash function is defined by the +cryptographic API in the kernel. + +Examples of suitable strings are as follows: + + sha256 + sha3-256 + sm3 + +Definitions for the names of the cryptographic hashes can be found in +the source files for the various cryptographic hash functions in the +'crypto' directory of the Linux source tree. + +The 'cache' keyword is used to specify the size of the caches used to +hold pointers to data structures used for the internal modeling of +security events or the export of the security event to external trust +orchestrators. These pre-allocated structures are used to service +security event hooks that are called while the process is running in +atomic context and thus cannot sleep in order to allocate memory. + +The argument to this keyword is a numeric value specifying the number +of structures that are to be held in reserve for the namespace. + +By default the root security modeling namespace and externally modeled +namespaces have a default value of 128 entries. An internally modeled +namespace has a default value of 32 entries. The size requirements of +these caches can be highly dependent on the characteristics of the +modeled workload and may require tuning to the needs of the platform +or workload. + +The structures that are used by security events generated in atomic +context are replenished by work requests submitted to the high +priority system workqueue. The refill latency will also affect the +magazine sizes that are needed. + +The 'key' keyword is used to specify the authentication key that is to +be used to support the authentication of trust control requests from a +trust orchestrator to processes running in a security modeling +namespace. The argument to this keyword is the ASCII base16 +representation of the key that is to be used. The length of the key +must be equal to the length of the ASCII base16 representation of the +digest value of the cryptographic digest function defined for the +security modeling namespace. + +The following keywords and arguments are used by trust orchestrators +to set the trust status of a process after the processing of a +security event by an external TMA: + + trusted pid=PID key=HEXID + untrusted pid=PID key=HEXID + + PID is the process identifier that is provided to the TMA in + the security event description. HEXID is the base16 ASCII + representation of the authentication key that the security + modeling namespace was configured with when the namespace was + created. The length of the ASCII representation of HEXID must + equal the size of the base16 ASCII representation of a digest + value for the cryptographic hash function selected for the + security modeling namespace. + +By default a security modeling namespace runs in free modeling mode. +The modeling mode is changed by writing the following keywords to the +control file: + + seal + enforce + +The seal value is used to specify that any further security state +coefficients are to be considered outside the bounds of a desired +security model. The security event descriptions that generate these +coefficients will be considered forensics events for the model. + +The enforce key is used to specify that invalid security events +generate permission denials as the return value for the LSM security +event handler that generates the invalid events. + +The following keyword and argument are used to load a security model +into an internal TMA modeling implementation: + + state value=HEXID + + Where HEXID is the ASCII base 16 representation of a security + state coefficient that represents a valid security event in + the model. The length of the HEXID string must be equal to + the size of the ASCII base 16 representation of the digest + value of the cryptographic hash function defined for the + security modeling namespace. + + After writing a series of state values the trust orchestrator + writes the 'seal' keyword to the control file to complete + creation of a security model. + + Writing the 'enforce' keyword to the control file will place + the defined model in enforcing mode. + + Defining a security model to be enforced will affect the + output of the 'trajectory' file. The 'trajectory' file will + have no event descriptions for a sealed model, since the event + description list is only populated when a new state + coefficient is added to the model. + + In a sealed model the security event descriptions will be + surfaced in the 'forensics' file instead to indicate they are + violations against the security model being enforced. + + Since the state state coefficients are generated with a + cryptographic hash function, the first pre-image resistance + characteristics of the function prevents a security model + description from disclosing information, a-priori, about the + desired characteristics of the workload. + +The following keyword and argument is used to set a base nonce for the +internal TMA: + + base value=HEXID + + Where HEXID is the ASCII base 16 representation of a value + that each security state event mapping is to be extended with + before being committed as a security state coefficient value + for the model. The size of the HEXID string must equal the + size of the ASCII base 16 representation of a digest value of + the cryptographic hash function defined for the security + modeling namespace. + +The following keyword and argument is used to create a file digest +pseudonym for the internal TMA: + + pseudonym value=HEXID + + Where HEXID is the ASCII base 16 representation of a file + digest pseudonym that is to be maintained by the model. See + the ABI documentation for how the argument to this verb is + generated. + + The size of the HEXID string must equal the size of the ASCII + base 16 representation of a digest value of the cryptographic + hash function defined for the security modeling namespace. + +The following keyword is used to lock the current TSEM modeling +configuration: + + lock + +This command is only valid when loadable module support is available +in the kernel. When executed this command blocks any further TSEM +models from being registered. In addition the reference count on all +currently registgered modeling modules is increased so that it is not +possible to remove currently loaded modules. + +The following two directories are implemented in the top level TSEM +control directory in order to support interfaces to internally and +externally modeled namespaces: + + external_tma + internal_tma + +The external_tma directory holds a file, that is created when the +request to create an externally modeled namespace is made. The filename +is the ASCII base 10 representation of the id number of the security +modeling namespace. The descriptions for security events that occur +in the context of the namespace are exported in JSON format through +this file to the external trust orchestrator that is controlling the +security modeling namespace. + +The internal_tma directory is a container directory that holds +directories for the control of each internal TMA that is implemented +in the kernel. + +There is currently only a single kernel based TMA that is managed +through the following directory: + +/sys/kernel/security/tsem/internal_tma/model0 + +The following files are implemented for this model: + + measurement + state + + trajectory + trajectory_coefficients + trajectory_counts + + forensics + forensics_coefficient + forensics_counts + +The 'measurement' file outputs the classic linear extension value of +the security state coefficients that are generated in the context of +the security modeling namespace. This value is time dependent and can +be used to verify the order of the security events that occurred in +the model. + +The 'state' file outputs the time independent functional value of +security state of the security modeling namespace. This value and its +generation and motivation are discussed in the 'Security model +functional definitions' section of this document. + +The 'trajectory' file outputs the description of each security event +recorded by the model in time dependent form. The ABI documentation +file contains a complete description of the output that is generated +by this file and the 'forensics' file described below. + +The 'trajectory_coefficients' file outputs the set of security state +coefficients in the model. These coefficients match the entries of +the event descriptions that are output in the 'trajectory' file. + +The security state coefficients can be paired with the security state +descriptions with the following shell command, where DIR is the path +to the individual files: + +paste DIR/trajectory_coefficients DIR/trajectory + +The 'trajectory_counts" file outputs the number of times that each +security state coefficient, output by the 'trajectory_coefficients' +file, has been experienced in the security modeling namespace. This +value can be used to verify that a security sensitive event has +occurred or for statistical inference as to the anomaly status of an +event. + +The 'forensics' file outputs the description of security events that +have occurred when the namespace security model is running in a sealed +state. These events are useful for characterizing a security +intrusion that has occurred or for refinement of a security model. + +The 'forensics_coefficients' file outputs the security state +coefficients that are generated by the forensics events that have +been captured by the model and available through the 'forensics' file. + +The 'forensics_counts" file outputs the number of times that each +security state coefficient output by the 'forensics_coefficients' file +has been experienced in the security namespace. This value can can be +used for statistical inference as to the anomaly status of the +namespace. + +Trust orchestrators +=================== + +In security modeling, the need for a trust orchestrator is embodied in +Heisenberg's reflections on quantum mechanical modeling. A modeled +system cannot model itself without affecting the functional value of +the security model being implemented. An external entity is needed to +setup, configure and monitor the state of a modeled system, in a +manner that does affect the state of the modeled system itself. + +After creating and configuring a security modeling namespace, the +orchestrator is responsible for executing and monitoring a process +that is run in the context of the namespace. The trust orchestrator +is also responsible for providing access to the status of the security +model being implemented by the TMA associated with the orchestrator. + +Trust orchestrators for externally modeled namespaces, have an +associated external TMA that is responsible for implementing the +security model for a namespace. The TMA represents the the root of +trust for the modeled namespace. The TMA advises the trust +orchestrator as to what the trust status for a process should be set +to, based on the modeling of the security event characteristics that +are presented to it by the trust orchestrator. + +In a trust orchestration architecture, secondary to their integral +role in maintaining the trust state of the system, the trust +orchestrators are the highest value security asset running on the +system. The CAP_MAC_ADMIN capability must be held by a trust +orchestrator in order to access the TSEM control plane. + +Trust orchestrators are designed to drop the CAP_MAC_ADMIN capability +before forking the process that will be responsible for launching a +security modeled workload. This provides an architecture where the +root of trust for the system can be predicated on a small body of well +audited orchestration utilities, that can be linked to a hardware root +of trust implemented by a TPM or a hardware based TMA. + +Quixote +======= + + "He is awkward, past his prime and engaged in a task beyond his + capacities." + - Don Quixote's able mount Rocinante + +The Quixote Trust Orchestration System, released in concert with TSEM, +is an implementation of a trust orchestration environment that +implements the characteristics described in the previous section. It +provides all off the basic functionality needed to build and run +security architectures based on TSEM using either internal or external +TMA implementations. + +It is anticipated that Quixote would not be the only such system to +take advantage of TSEM. Given the burgeoning capability set of +systemd, it would be an architecturally valid concept to have systemd, +or other system init equivalents, gain the ability to launch critical +system services in security modeled environments. + +Source code, in GIT form, for all Quixote and TSEM components are +available at the Quixote project site: + +https://github.com/Quixote-Project + +The build of Quixote is somewhat formidable, given that it spans the +range from system programming though SGX programming and into embedded +micro-controller systems. In order to facilitate experimentation, +Quixote projects binaries statically compiled against MUSL libc, are +provided that have virtually no system dependencies, other than a TSEM +enabled kernel. + +Sample utilities +---------------- + +The Quixote TSEM implementation implements a separate trust +orchestration utility for each TMA environment, nee Sancho partner, +that is supported: + +quixote -> TMA run in the kernel for internally modeled namespaces. + +quixote-us -> TMA run in a userspace process. + +quixote-xen -> TMA run in a Xen based stub domain. + +quixote-sgx -> TMA run in an SGX enclave. + +quixote-export* -> Utility for exporting security event descriptions. + +quixote-mcu** -> TMA run in a micro-controller implementation. + +* = See discussion below. + +Each modeling utility runs in one of two modes: process or container + +In process mode, a shell process is run as the workload process in a +security modeling namespace. This mode is selected with the -P +command-line option. + +In container mode, the default, the OCI runc utility is run as the +workload process, with a 'bundle' argument that specifies a directory +that contains a JSON container definition for a directory hierarchy in +the bundle directory. The /var/lib/Quixote/Magazine directory +contains the bundle directories. + +The -c command-line option selects container mode, the argument to the +option specifies the bundle directory for the runc utility. + +In order to support the creation of security models, each utility +supports the -o command-line option to specify that a security model +description be output when the modeled workload terminates. The model +is written to the name of the file supplied via the command-line +option. + +If the -t command-line option is also specified, the security +execution trajectory, rather than a model consisting of security state +coefficients, is written to the output file. This trajectory +represents the description of the security events that were modeled. +This trajectory can be converted to security state coefficients with +the generate-states utility that is provided in the utilities package. + +The -m command-line option is used to specify a model that is to be +loaded into the TMA and optionally enforced. By default, a security +model output with the -o command-line option will place the TMA in a +sealed modeling state. Any security events that are non-compliant +with the model will be registered as forensics events. + +Adding the -e command-line option, with the '-m FILENAME' option, will +cause the loaded model to be enforced. Any forensic events will cause +a permission denial to be returned to the caller of a TSEM LSM hook +implementation. + +The Quixote package also includes the quixote-console utility, for +interrogating the model state of both external and internal TMA's. +The following command-line options request output of the following +characteristics of the model: + +-C -> The current execution trajectory coefficient counts. + +-E -> The log of denied events. + +-F -> The current forensics execution trajectory. + +-M -> A definition for the current security model. + +-P -> The current security state coefficients. + +-S -> The state value of the model. + +-T -> The current security execution trajectory. + +Executing the utility, without these arguments, will cause a +command-line version of the utility to be presented that takes the +following arguments: + +show trajectory + +show coefficients + +show counts + +show forensics + +show forensics_coefficients + +show forensics_counts + +show state + +show model + +quit + +It is important to note that any of the values output, represent the +current state of the model and do not reflect a cumulative model of +the workload. Capturing a complete workload model requires the use of +the -m command-line option to the trust orchestrators to capture a +model that is representative of the entire execution trajectory of the +workload after it completes. + +As an example, the following security model definition represents the +execution and termination of a shell session run on a system with a +hardware TPM: + +aggregate de2b9c37eb1ceefa4bcbc6d8412920693d3272f30eb5ba98d51d2f898d620289 +state 97b29769580b412fbf55e326a98d6a1b97c6ebf446aaf78ea38c884e954ca5b2 +state 7c435854b4fa421175ec0a5d3ca7c156480913d85c03155ea3305afa56c9717d +state 554d9f62693d522c9a43acf40780065f99cea3d67ca629ac4eaab4e22d4e63c2 +state 1b228046c4c2e7aa14db9a29fcff6f718f4f852afbfb76c8a45af7bf0485f9ce +state 24fd04b10e2b5016e0061952f3bdea959e0fa80a55ff0f4e8e13f9f72ede7498 +state da6038511db71b08c49a838d178ed055e0b7bfc42548b4c2d71eca046e9a222e +state 94b24ad4c8902f8ecb578a702408e8458e72c0774c402c3bd09ec5f390c4d0ae +state 5ffa5a2a38f42d89ae74a6d58be8b687c1baed9746d9c6a7ae3c632a2e7c082f +state a2e309d84bd4a52466c22779a622254c65ad1208583d70113751c4624baa7804 +state e93ceb0b1bf3cd58373a9e9ab4aca11a507782bbfde395ff68f8bfaf1678ed43 +state bf42388d63887368605fac9816134bc67314762c3a97b440cc48c5a30c07fdb9 +state eaa342599d682d63be4b64e159b98f21d85f0133ef5b28588e444ad12e446bf6 +state 2b9c86bc34202504c398c2f177d1dcf807b2f267c160bf8ebda863a9b427917f +state 686fc3c958f2e4f2ce3b2c6a2cb3fff44ccc4db98869bd377b14e557a5191231 +state 613c39fd2a58413b32f448c13ea4d6bc38b77966dfc5560e39e4b37d2b2f5675 +state 70e276bfd7c20262cd9c9f5b09a922f11d16d1e3a602e8005d68e9ed6afc9b5d +state 456aaedc5c1fc63f852ee97ae9561aba2a06c416154ecb9d7a1bf9d9a8c9c064 +state 97507c4c91af4a9b34b4d66118f6cc0ba1f8b55b8bb6e623dcafe27b100aea07 +state ea635c48031f81140b3561ed2291a3b1790a302e6adf5244320593b08a5af924 +state 2fd6a4d6ea1869a193926e998fbdf855916b510257d379762f48a1df63a810d4 +state 9c4cb7ef4848be1e29f9eb35fadaf5bfdc1fa3cbb22b6407cbd31b7088257026 +state 66640cbf9ae772515070f8613182b6852bf46220df0833fbe6b330a418fad95b +state 6b0d1890cbd78c627e23d7a564e77a5ee88fb20e0662ce5e66f3727ebf75fa1d +state bd28fa43b34850591fdf6fb2aa5542f33c21c20ee91b4bc2034e199b4e09edc1 +state 04425354419e53e6e73cde7d61856ff27763c2be01934e9990c1ae9f8d2a0b6e +state 2650d86382f6404367b7fdeec07f873b67b9ce26caef09d035b4dff09fce04d5 +state df2f91f5fd84ca4621092420eaf1b0a3743b328a95e3f9e0b7b1281468462aa2 +state c730c66ecfabe99480e61a7f25962582ca7bb6f2b17983048e77adde1fe7f72b +state 0fc937b71d0067fcc2c2f37c060763de250b3142e621174ffedc1b2520cdf6fd +state 7f267400a3ccf462c77ae5129799558c2c62d8bc5b388882caec813ab4cf7b7f +seal +end + +As was previously discussed, the model output is cryptographically +secure against the elucidation of the security events that resulted in +the described security states. + +The Quixote userspace implementation also contains utilities for +generating signed versions of these security models. + +Quixote Export Utility +---------------------- + +The quixote-export utility is used to implement security modeling +namespaces that are running in 'export only' mode. In this mode the +security event descriptions for a security modeling namespace are +exported asynchronously and do not wait approval. This utility and +modeling mode can be used to implement kernel native security +surveillance systems. + +The root security modeling namespace can be placed in 'export only' +mode through the following kernel command-line option: + +tsem_mode=root_export_only + +The supplied quixote-export utility operates in a manner similar to +the trust orchestrators for externally modeled namespaces. + +Workloads can be run in either 'cartridge' or 'process' modes that are +specified with the -C or -P command-line options. + +Processing of events from the root security modeling namespace is +specified with the -R command-line option. + +By default the quixote-export utility will run in 'one-shot' mode +where all of the buffered security event descriptions are read and +output, after which the utility terminates. + +Specifying the -f command-line option places the utility in 'follow' +mode where the utility will first output all of the buffered security +event descriptions and then wait for subsequent descriptions to be +generated. This mode can be terminated by issuing a CNTRL-C key +sequence to the utility. + +The -q command-line option is used to specify the queuing factor or +the number of events that will be held by the export utility before +flushing the events to the output device. This increases the +efficiency of the utility and decreases the impact the export utility +has on the security modeling namespace that it is running in, see the +discussion of the 'Heisenberg' effect in security modeling. The +default queue size is 100 entries. + +By default the security event descriptions are written to standard +out. The -o command-line option can be used to specify that the +events are to be written to a file. + +The quixote-export utility has native support for exporting security +event descriptions as MQTT encoded messages. This facilitates the use +of cloud based assets for security monitoring/surveillance. MQTT mode +is specified with the -b command-line option. The argument to the -b +option is the hostname of an MQTT broker that is to receive the +encoded security event descriptions. + +In MQTT broker mode the -t command-line option is used to specify a +'topic' that the descriptions are to published to in the target +broker. + +An important issue that should be noted with 'export only' mode is +that security event descriptions are buffered in the kernel until read +by an export orchestrator. These events are not subject to +'uniqueness' compression, as is the case with internally modeled +namespaces, this can result in large kernel descriptions, particularly +during the boot of a kernel whose root security modeling namespace is +configured for export only mode. + +In addition there are no latency delays since the security event +descriptions are asynchronously exported. This may require an +increase in the kernel event magazine sizes in order to avoid security +failures caused by the inability to allocate structures for security +events running in atomic context. + +** MCU TMA's +------------ + +One of the objectives of TSEM/Quixote is to explore architectures for +trusted systems that extend beyond what is provided by the TPM model +for security co-processors. The MCU based reference implementations +allow experimentation with hardware based TMA's. + +The Quixote TSEM utilities include TMA implementations for the +following following ARM32 based micro-controller platforms: + +STM32L496 + +STM32L562 + +NRF52840-DK + +NRF52840-DONGLE + +The STM32L496 platform, in addition to the base TMA implementation, +includes support for a CAT1-M based cellular modem. This demonstrates +the ability of an external TMA to conduct remote, out-of-band, +signaling of security violations for modeled platforms/workloads and +the downloading of security models outside the context of the platform +itself. + +The STM32L562 platform is a low power MCU designed for security +focused IOT implementations. It includes hardware hashing, hardware +asymmetric encryption and Trust Zone support. + +Of primary interest may be the NRF52840-DONGLE implementation. This +is a 'USB fob' form factor board that GOOGLE uses as the basis for +their OpenSK security key implementation. This form factor allows the +development and experimentation with easily deployable hardware based +TMA implementations. + +The NRF52840-DONGLE architecture was chosen by the NLnet sponsored +'FobNail' project, that is developing a hardware based attestation +server: + +https://fobnail.3mdeb.com/ + +The Fobnail projects discusses the notion of their architecture +expanding to provide protection for a Linux system at large. +Quixote/TSEM, running on the NRF52840-DONGLE micro-controller, is a +demonstration of such an implementation. + +=============== +Closing Remarks +=============== + + "Sometimes it is the people no one can imagine anything of who + do the things no one can imagine. + - Alan Turing + +While this document is of some length and detail, it hopefully +fulfills its obligation to provide sufficient prose for the +justification of the security model that TSEM addresses, and in +combination with trust orchestrators, implements. + +The MAINTAINERS file has contact information for feedback, patches +and/or questions regarding TSEM and its reference TOS implementation. + + The Quixote Team - Flailing at the Travails of Cybersecurity + + With all due respect to Miguel de Cervantes Saavedra. + + From the glacial moraine lake country of West-Central Minnesota. diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index 27ec49af1bf2..36460e179793 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -6856,6 +6856,35 @@ with CPUID.16h support and partial CPUID.15h support. Format: + tsem_cache= [TSEM] Define the size of the caches used to hold + pointers to structures that will be used to model + security events occurring in the root modeling + namespace that are called in atomic context. The + value is the size of the arrays of pointers to the + pre-allocated structures that will be maintained. + For example, a value of 16 means each array would + have 16 entries in it. + Format: + Default: 96 + + tsem_digest= [TSEM] Define the cryptographic hash function that + will be used to generate the security event + coefficients in the root modeling namespace. + Format: {name of the cryptographic hash function} + Default: sha256 + + tsem_lock [TSEM] Lock the TSEM model state so that no kernel + module based security models can be registered. + + tsem_mode= [TSEM] Set the mode that the Trusted Security Event + Modeling LSM is to run in. + Format: + no_root_modeling - Disable root security namespace + modeling + root_export_only - Do not model the root security + modeling namespace, only export + the security event descriptions. + tsx= [X86] Control Transactional Synchronization Extensions (TSX) feature in Intel processors that support TSX control. From patchwork Mon Aug 26 10:37:17 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Dr. Greg" X-Patchwork-Id: 13777631 X-Patchwork-Delegate: paul@paul-moore.com Received: from blizzard.enjellic.com (wind.enjellic.com [76.10.64.91]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 4649517BECA; Mon, 26 Aug 2024 10:50:51 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=76.10.64.91 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1724669456; cv=none; b=GtWhSxY/x3bGaz0Ki94YOO4lN9D8gGBoTO4xHgHMR0h2vD4yq3y14WzXwWtZiHQfFAm5q576xQUdcoO0+6/FJdKr97qsBVP468KCILks8DiYSGCqDDEI/SCsDF0jzgvXXlUBCB6e7G2I8rRQ0MXDi3kft3BL1pgQWoiOuov1rnY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1724669456; c=relaxed/simple; bh=YxU+seUoSTsRgNL/PETrAmg6E/HMP4AIUvKBZhIDr6A=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=YoGgrPk+CsaZmSjx7qY0UCI3rMHb60ov7ub+PRUVdjj+j4S7gLUtMu5mHy5KF2Mkun+d0i5odq+h/YF07CrLlPTX86fufHjE1xwvfGCqSPNBcGVndC7WsRIhAXE3MmpgUhIL6wQZ710FsmYYplwScShBHC5NvIs0F5FFo7u4XW4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=enjellic.com; spf=pass smtp.mailfrom=enjellic.com; arc=none smtp.client-ip=76.10.64.91 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=enjellic.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=enjellic.com Received: from blizzard.enjellic.com (localhost [127.0.0.1]) by blizzard.enjellic.com (8.15.2/8.15.2) with ESMTP id 47QAbUVk003413; Mon, 26 Aug 2024 05:37:30 -0500 Received: (from greg@localhost) by blizzard.enjellic.com (8.15.2/8.15.2/Submit) id 47QAbUx5003412; Mon, 26 Aug 2024 05:37:30 -0500 X-Authentication-Warning: blizzard.enjellic.com: greg set sender to greg@enjellic.com using -f From: Greg Wettstein To: linux-security-module@vger.kernel.org, linux-kernel@vger.kernel.org Cc: jmorris@namei.org Subject: [PATCH v4 03/14] TSEM global declarations. Date: Mon, 26 Aug 2024 05:37:17 -0500 Message-Id: <20240826103728.3378-4-greg@enjellic.com> X-Mailer: git-send-email 2.39.1 In-Reply-To: <20240826103728.3378-1-greg@enjellic.com> References: <20240826103728.3378-1-greg@enjellic.com> Precedence: bulk X-Mailing-List: linux-security-module@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 TSEM is designed, from a functional perspective, to be entirely contained in its own directory. TSEM uses a single global header file, tsem.h, to define the enumeration types, structure definitions and functions that are referenced across all of the compilation units that implement the LSM. --- security/tsem/tsem.h | 2336 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 2336 insertions(+) create mode 100644 security/tsem/tsem.h diff --git a/security/tsem/tsem.h b/security/tsem/tsem.h new file mode 100644 index 000000000000..24f52824089e --- /dev/null +++ b/security/tsem/tsem.h @@ -0,0 +1,2336 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +/* + * Copyright (C) 2024 Enjellic Systems Development, LLC + * Author: Dr. Greg Wettstein + * + * This is the single include file that documents all of the externally + * visible types and functions that are used by TSEM. This file is + * currently organized into four major sections in the following order; + * + * includes used by all compilation units + * CPP definitions + * enumeration types + * structure definitions + * function declarations + * inline encapsulation functions. + * + * Include files that are referenced by more than a single compilation + * should be included in this file. Includes that are needed to + * satisfy compilation requirements for only a single file should be + * included in the file needing that include. + * + * Understanding the overall implementation and architecture of TSEM + * will be facilitated by reviewing the documentation in this file. + */ + +#include +#include +#include +#include +#include +#include +#include + +/* + * The number of 'slots' in the structure magazines that are used to + * satisfy modeling of security events that are called in atomic context. + */ +#define TSEM_ROOT_MAGAZINE_SIZE 128 +#define TSEM_MAGAZINE_SIZE_INTERNAL 32 +#define TSEM_MAGAZINE_SIZE_EXTERNAL 128 + +/** + * enum tsem_event_type - Ordinal value for a security event. + * @TSEM_BPRM_COMMITTED_CREDS: Ordinal value for bprm_committed_creds. + * @TSEM_TASK_KILL: Ordinal value for task kill. + * @....: Remainder follows with a similar naming format that has + * TSEM_ prep ended to the raw LSM security hook name. + * @TSEM_EVENT_CNT: The final ordinal value is used to define the + * length of the following arrays that are indexed + * by the ordinal value of the hook: + * + * This enumeration is used to designate an ordinal value for each + * security event, ie. LSM hook/event handler, that TSEM is + * implementing modeling for. This value is used to identify the + * handler that is either having its event description being exported + * to an external trust orchestrator or modeled by the internal TMA + * implementation. + * + * The primary use of this enumeration is to conditionalize code paths + * based on the security hook being processed and to index the + * tsem_names array and the array that defines the action that is to + * be taken in response to an event that generates a permissions + * violation. + * + * NOTE: + * If additions or deletions are made to these enumerated constants + * there needs to be coordinated changes made to the tsem_names array + * in the tsem.c file. + */ +enum tsem_event_type { + TSEM_BPRM_COMMITTED_CREDS = 1, + TSEM_TASK_KILL, + TSEM_TASK_SETPGID, + TSEM_TASK_GETPGID, + TSEM_TASK_GETSID, + TSEM_TASK_SETNICE, + TSEM_TASK_SETIOPRIO, + TSEM_TASK_GETIOPRIO, + TSEM_TASK_PRLIMIT, + TSEM_TASK_SETRLIMIT, + TSEM_TASK_SETSCHEDULER, + TSEM_TASK_GETSCHEDULER, + TSEM_TASK_PRCTL, + TSEM_FILE_OPEN, + TSEM_MMAP_FILE, + TSEM_FILE_IOCTL, + TSEM_FILE_LOCK, + TSEM_FILE_FCNTL, + TSEM_FILE_RECEIVE, + TSEM_UNIX_STREAM_CONNECT, + TSEM_UNIX_MAY_SEND, + TSEM_SOCKET_CREATE, + TSEM_SOCKET_CONNECT, + TSEM_SOCKET_BIND, + TSEM_SOCKET_ACCEPT, + TSEM_SOCKET_LISTEN, + TSEM_SOCKET_SOCKETPAIR, + TSEM_SOCKET_SENDMSG, + TSEM_SOCKET_RECVMSG, + TSEM_SOCKET_GETSOCKNAME, + TSEM_SOCKET_GETPEERNAME, + TSEM_SOCKET_SETSOCKOPT, + TSEM_SOCKET_SHUTDOWN, + TSEM_PTRACE_TRACEME, + TSEM_KERNEL_MODULE_REQUEST, + TSEM_KERNEL_LOAD_DATA, + TSEM_KERNEL_READ_FILE, + TSEM_SB_MOUNT, + TSEM_SB_UMOUNT, + TSEM_SB_REMOUNT, + TSEM_SB_PIVOTROOT, + TSEM_SB_STATFS, + TSEM_MOVE_MOUNT, + TSEM_SHM_ASSOCIATE, + TSEM_SHM_SHMCTL, + TSEM_SHM_SHMAT, + TSEM_SEM_ASSOCIATE, + TSEM_SEM_SEMCTL, + TSEM_SEM_SEMOP, + TSEM_SYSLOG, + TSEM_SETTIME, + TSEM_QUOTACTL, + TSEM_QUOTA_ON, + TSEM_MSG_QUEUE_ASSOCIATE, + TSEM_MSG_QUEUE_MSGCTL, + TSEM_MSG_QUEUE_MSGSND, + TSEM_MSG_QUEUE_MSGRCV, + TSEM_IPC_PERMISSION, + TSEM_KEY_ALLOC, + TSEM_KEY_PERMISSION, + TSEM_NETLINK_SEND, + TSEM_INODE_CREATE, + TSEM_INODE_LINK, + TSEM_INODE_UNLINK, + TSEM_INODE_SYMLINK, + TSEM_INODE_MKDIR, + TSEM_INODE_RMDIR, + TSEM_INODE_MKNOD, + TSEM_INODE_RENAME, + TSEM_INODE_SETATTR, + TSEM_INODE_GETATTR, + TSEM_INODE_SETXATTR, + TSEM_INODE_GETXATTR, + TSEM_INODE_LISTXATTR, + TSEM_INODE_REMOVEXATTR, + TSEM_INODE_KILLPRIV, + TSEM_TUN_DEV_CREATE, + TSEM_TUN_DEV_ATTACH_QUEUE, + TSEM_TUN_DEV_ATTACH, + TSEM_TUN_DEV_OPEN, + TSEM_BPF, + TSEM_BPF_MAP, + TSEM_BPF_PROG, + TSEM_PTRACE_ACCESS_CHECK, + TSEM_CAPABLE, + TSEM_CAPGET, + TSEM_CAPSET, + TSEM_EVENT_CNT +}; + +/** + * enum tsem_action_type - Ordinal value for security responses. + * @TSEM_ACTION_LOG: Ordinal value to indicate that a security event + * that results in a model permissions violation + * should be logged. + * @TSEM_ACTION_EPERM: Ordinal value to indicate that a security event + * generating a model permissions violation should + * return -EPERM to the caller. + * + * This enumeration type is used to designate what type of action is + * to be taken when the processing of a security event hook results in + * a model violation. The TSEM_ACTION_LOG and TSEM_ACTION_EPERM + * translate into the classical concepts of logging or enforcing + * actions used by other mandatory access control architectures. + */ +enum tsem_action_type { + TSEM_ACTION_LOG = 0, + TSEM_ACTION_EPERM, + TSEM_ACTION_CNT +}; + +/** + * enum tsem_control_type - Ordinal values for TSEM control actions. + * @TSEM_CONTROL_INTERNAL: This ordinal value is set when the first + * word of an argument string written to the + * control file is the word 'internal'. This + * designates that the security namespace will + * be modeled by the internal TMA. + * @TSEM_CONTROL_EXTERNAL: This ordinal value is set when the first + * word of an argument string written to the + * control file is the word 'external'. This + * designates that the security namespace will + * be model by an external TMA. + * @TSEM_CONTROL_ENFORCE: This ordinal value is set when the word + * 'enforce' is written to the control file. + * This indicates that model is to be placed + * in 'enforcing' mode and security events that + * result in model violations will return EPERM. + * @TSEM_CONTROL_SEAL: This ordinal value is set when the word 'seal' + * is written to the control file. This indicates + * that the model for security domain will treat + * all security events that do not conform to the + * model as 'forensics' events. + * @TSEM_CONTROL_TRUSTED: This ordinal value is used when the first + * word of an argument string written to the + * control file is the word 'trusted'. This + * is interpreted as a directive to set the + * trust status of the task that executed the + * security event to be trusted. + * @TSEM_CONTROL_UNTRUSTED: This ordinal value is used when the first + * word of an argument string written to the + * control file is the word 'untrusted'. + * This is interpreted as a directive to set + * the trust status of the task that executed + * the security event to be untrusted. + * @TSEM_CONTROL_MAP_STATE: This ordinal value is used when the first + * word of an argument string written to the + * control file is the word 'state'. The + * argument to this directive will be an + * ASCII hexadecimally encoded string of the + * current model's digest size that will be + * treated as a security state point for + * inclusion in the security model for the + * security domain/namespace. + * @TSEM_CONTROL_MAP_PSEUDONYM: This ordinal value is used when the + * first word of an argument string + * written to the control file is the + * word 'pseudonym'. The argument to + * this directive will be an ASCII + * hexadecimally encoded string of the + * current model's digest size that will + * be treated as a pseudonym directive + * for the security domain/namespace. + * TSEM_CONTROL_MAP_BASE: This ordinal value is used when the first + * word of an argument string written to the + * control file is the word 'base'. The + * argument to this directive will be an ASCII + * hexadecimally encoded string of the current + * model's digest size that will be treated as + * the base value for the computation of the + * functional values (measurement and state) of + * the security domain/namespace. + * TSEM_CONTROL_LOCK: This ordinal value is used to indicate that a + * request is being made to lock the model configuration + * of the host from further modification. Invoking + * this command causes any additional requests to + * register security models to be denied. In addition + * the reference count of all currently loaded models + * is increased in order to prevent the models from + * being unloaded. + * + * This enumeration type is used to designate what type of control + * action is to be implemented when arguments are written to the TSEM + * control file (/sys/kernel/security/tsem/control). The ordinal + * values govern the processing of the command and the interpretation + * of the rest of the command argument string. + */ +enum tsem_control_type { + TSEM_CONTROL_INTERNAL = 0, + TSEM_CONTROL_EXTERNAL, + TSEM_CONTROL_EXPORT, + TSEM_CONTROL_ENFORCE, + TSEM_CONTROL_SEAL, + TSEM_CONTROL_TRUSTED, + TSEM_CONTROL_UNTRUSTED, + TSEM_CONTROL_MAP_STATE, + TSEM_CONTROL_MAP_PSEUDONYM, + TSEM_CONTROL_MAP_BASE, + TSEM_CONTROL_LOCK +}; + +/** + * enum tsem_ns_reference - Ordinal value for DAC namespace reference. + * @TSEM_NS_INITIAL: This ordinal value indicates that the uid/gid + * values should be interpreted against the initial + * user namespace. + * @TSEM_NS_CURRENT: This ordinal value indicates that the uid/gid + * values should be interpreted against the user + * namespace that is in effect for the process being + * modeled. + * + * This enumeration type is used to indicate what user namespace + * should be referenced when the uid/gid values are interpreted for + * the creation of either the COE or CELL identities. The enumeration + * ordinal passed to the tsem_ns_create() function, to configure the + * security domain/namespace, is set by the nsref argument to either + * the 'internal' or 'external' control commands. + */ +enum tsem_ns_reference { + TSEM_NS_INITIAL = 1, + TSEM_NS_CURRENT +}; + +/** + * enum tsem_task_trust - Ordinal value describing task trust status. + * @TSEM_TASK_TRUSTED: This ordinal value indicates that the task has + * not executed a security event that has resulted + * in a security behavior not described by the + * security model the task is being governed by. + * @TSEM_TASK_UNTRUSTED: This ordinal value indicates that the task + * has requested the execution of a security event + * that resulted in a security behavior not + * permitted by the security model the task is + * being governed by. + * @TSEM_TASK_TRUST_PENDING: This ordinal value indicates that the setting + * of the task trust status is pending a response + * from an external TMA. + * + * This enumeration type is used to specify the three different trust + * states that a task can be in. The trust status of a task is + * regulated by the trust_status member of struct tsem_task. A task + * carrying the status of TSEM_TASK_TRUSTED means that it has + * not requested the execution of any security events that are + * inconsistent with the security model that the task is running in. + * + * If a task requests execution of a security event that is + * inconsistent with the security model it is operating in, and the + * domain is running in 'sealed' mode, the task trust status is set to + * TSEM_TASK_UNTRUSTED. This value is 'sticky' in that it will be + * propagated to any child tasks that are spawned from an untrusted + * task. + * + * In the case of an externally modeled security domain/namespace, the + * task trust status cannot be determined until the modeling of the + * security event has been completed. The tsem_export_event() + * function sets the trust status TSEM_TASK_TRUST_PENDING and then + * places the task into an interruptible sleep state. + * + * Only two events will cause the task to be removed from sleep state. + * Either the task is killed or a control message is written to the + * TSEM control file that specifies the trust status of the task. See + * the description of the TSEM_CONTROL_TRUSTED and + * TSEM_CONTROL_UNTRUSTED enumeration types. + */ +enum tsem_task_trust { + TSEM_TASK_TRUSTED = 1, + TSEM_TASK_UNTRUSTED = 2, + TSEM_TASK_TRUST_PENDING = 4 +}; + +/** + * enum tsem_inode_state - Ordinal value for inode reference state. + * @TSEM_INODE_COLLECTING: This ordinal value indicates that the inode + * is being opened in order to compute the + * digest of the file. + * @TSEM_INODE_COLLECTED: This ordinal value indicates that the digest + * file for the contents of the file referenced + * by the inode has been collected and is + * available in the digest cache attached to + * the inode. + * @TSEM_INODE_CONTROL_PLANE: The associated inode represents a TSEM + * control plane file that should be + * bypassed for security tests such as + * the TSEM_FILE_OPEN event. + * + * This enumeration type is used to specify the status of the inode. + * The primary purpose of this enumeration is so that the recursive + * call to the TSEM_FILE_OPEN hook, caused by the kernel opening the + * file to compute the checksum, can be bypassed when the digest + * value of the file is being computed for inclusion in an event + * description. + * + * The state value of the inode is carried in struct tsem_inode and is + * set and interrogated by the event.c:add_file_digest() function. If + * the status of the inode is TSEM_INODE_COLLECTED and the iversion of + * the inode is the same as it was at collection time, the cached + * value for the currently active namespace digest function is + * returned. + * + * If the test for the relevancy of the cached digest value fails the + * status of the inode is set to TSEM_INODE_COLLECTING. The + * tsem_file_open() function will check the inode status when it is + * invoked by the integrity_kernel_read() function and if it is + * set to 'COLLECTING', a successful permissions check is returned so + * that the kernel can open the file and compute its digest. + * + * The TSEM_INODE_CONTROL_PLANE value is used to indicate that the + * attached inode is part of the TSEM control plane. This allows + * security events referencing this inode to bypass event processing + * in order to avoid a 'Heisenberg deadlock' situation. + */ +enum tsem_inode_state { + TSEM_INODE_COLLECTING = 1, + TSEM_INODE_COLLECTED, + TSEM_INODE_CONTROL_PLANE +}; + +/** + * struct tsem_task - TSEM task control structure. + * @tma_for_ns: The context identity number of the namespace that + * the task has control over if any. + * @instance: The instance number of the task. The global task + * instance number is incremented each time the + * bprm_committed_creds handler is invoked to compute the + * TASK_ID of a process. This instance number represents + * the total number of unique instances of a specific body + * of executable code has been requested. + * @p_instance: The instance number of the parent process to the + * process represented by an instance of this structure. + * This value allows an execution heirarchy of executable + * code to be established. + * @trust_status: The enumeration type that specifies the trust state of + * the process. + * @task_id: The TSEM task identity (TASK_ID) of the process. + * @p_task_id: The TASK_ID of the parent process to the process + * represented by an instance of this structure. + * @task_key: A security model specific digest value that is used to + * authenticate a task that is running as a trust + * orchestrator to a task that is under the control of the + * orchestrator. + * @context: A pointer to the tsem_context structure that defines the + * modeling context that the task is running under. + + * This structure is represents the TSEM security state of a task. It + * is automatically created when the task control structure is + * allocated for the creation of a new task. + * + * The trust_status member of structure determines whether or not the + * task is in a condition to be trusted. It represents whether or not + * the task has requested execution of a security event that is + * inconsistent with the security model that the task is running + * under. Reference the tsem_trust_status enumeration type for more + * information on this member. The trust status value is propagated + * to any child tasks that are spawned from a task. + * + * The value of task_id member is generated by the + * tsem_bprm_committed_creds() function that computes the task + * identity based TSEM TASK_ID generative function. This task_id + * value is used in the computation of the security state point values + * in combination with the COE and CELL mappings for this event. + * The task_id digest creates security state points that are specific + * to the executable code that was used to initiate the task. + * + * The instance member of the structure is used to temporally + * disambiguate instances of the same task_id. A single 64-bit + * counter is used to generate the instance. This counter is + * incremented and assigned to the instance member of the structure + * at the same tame the TASK_ID value is computed. + * + * The task_key member holds the authentication key that will be used + * to authenticate a process that is requesting the ability to set the + * trust status of a process. This value is generated for the task + * structure of the trust orchestrator when a security modeling + * namespace is created by the orchestrator. + * + * The context member of the structure contains a pointer to the + * tsem_context structure allocated when a security modeling namespace + * is created by the tsem_ns_create() function. This structure will + * contain all of the information needed to define how the task is to + * have its security behavior modeled. + */ +struct tsem_task { + u64 tma_for_ns; + u64 instance; + u64 p_instance; + enum tsem_task_trust trust_status; + u8 task_id[HASH_MAX_DIGESTSIZE]; + u8 p_task_id[HASH_MAX_DIGESTSIZE]; + u8 task_key[HASH_MAX_DIGESTSIZE]; + struct tsem_context *context; +}; + +/** + * struct tsem_context - TSEM modeling context description. + * @kref: Reference count for the context. + * @work: Work structure for asynchronous release of the context. + * @id: The index number of the context. + * @event_number: The current sequence number of events that have occurred + * in the security modeling namespace represented by + * the structure. + * @sealed: A status variable indicating whether or not the + * modeling context can be modified. + * @use_current_ns: Status variable indicating which user namespace + * should be used for resolution of uid/gid values. + * A true value indicates that the user namespace + * the process is running under should be used. + * @actions: An array of enum tsem_action_type variables indicating + * the type of response that should be returned in + * response to the modeling of a security event that + * is inconsistent with the model being used for the + * security context. + * @digestname: A pointer to a null-terminated buffer containing the + * name of the digest function that is to be used for + * this security context. + * @zero_digest: The digest value for a 'zero-length' digest value. + * @tfm: A pointer to the digest transformation structure that is to + * generate cryptographic checksums for the modeling context. + * @inode_mutex: The lock that protects the inode_list that tracks + * inodes created in the context of a security modeling + * namespace. + * @inode_list: The list of inodes created in a security modeling + * namespace protected by the inode_mutex member of + * this structure. + * @magazine_size: The number of struct tsem_event structures that + * are held in reserve for security event handlers that + * are called in atomic context. + * @magazine_lock: The spinlock that protects access to the event + * magazine. + * @magazine_index: The bitmap that is used to track the magazine slots + * that have been allocated. + * @ws: An array of work structures that are used to refill the event + * magazine slots. + * @magazine: An array of pointers to tsem_event structures that are + * pre-allocated for security handlers that are called in + * atomic context. + * @ops: A pointer to the tsem_context_ops that implements the + * models for the security model using in a security modeling + * namespace. + * @model: If the modeling context is implemented with a kernel based + * trusted model agent this pointer will point to the struct + * tsem_model structure that maintains the state of the + * security model. + * @external: If the modeling context is implemented with an external + * modeling agent this pointer will point to the + * tsem_external structure that implements the interface to + * the trust orchestrator that is managing the security + * modeling namespace represented by this structure. + * + * This structure is used to represent the state of a TSEM security + * modeling namespace. A pointer to this structure is stored in the + * struct tsem_task structure. + * + * This structure is allocated by the tsem_ns_create() function in + * response to a TSEM control request. This structure maintains all + * of the information that describes the security modeling namespace + * that is not specific to the type of namespace, ie. external or + * internal that is being implemented. + * + * The id member is a 64-bit counter that cannot feasibly be + * overflowed and that is incremented for each namespace that is + * created. The root modeling namespace has a value of zero so the + * TSEM code uses a pattern of testing this value for non-zero status + * as an indication of whether or not the task is running in a + * subordinate modeling namespace. + * + * Each security modeling namespace can have an independent + * cryptographic digest function that is used as the compression + * function for generating the security coefficients, and other + * entities, that are used to model security events that occur in a + * namespace. A single struct tfm is allocated for this digest + * function at the time that the tsem_context structure is created and + * is maintained in this structure for subsequent use during event + * processing. + * + * Each cryptographic digest function has a 'zero message' value that + * is the result of the initialization and closure of a hash function + * that has no other input. This zero digest value is computed at the + * time of the creation of the array. This digest value is returned + * for files with zero sizes, have pseudonyms declared for them or + * that reside on pseudo-filesystems. + + * The actions array contains a specification of how each security + * event should be handled in the event that a TMA detects a + * security event inconsistent with the model designated for the + * security modeling namespace. This array allows the specification + * of whether the events should be enforcing or logging. + * + * Each security event that is processed requires a struct tsem_event + * structure that drives either the internal modeling of an event or + * its export to an external modeling agent. Some security event + * hooks are called while a task is running in atomic context. Since + * memory cannot be allocated while a process is in atomic context, a + * magazine of these structures is maintained by this structure for + * security events that run in atomic context. The size of this + * magazine is dynamic and is configurable for each security modeling + * + * When a tsem_event structure is allocated for an atomic event a + * request for the refill of the slot that is vacated is dispatched to + * an asynchronous workqueue. The ws member of this structure points + * to an array of work structures for this refill capability, one for + * each slot in the magazine. + * + * All of this infrastructure is generic for each security modeling + * namespace. How the security modeling is done is governed by the + * model and externally defined members of this structure. These + * members point to data structures that either maintain the security + * model state for an in kernel trusted modeling agent or handle the + * export of the event to an external trust orchestrator. + * + * Each task that is created in a non-root security modeling namespace + * increments the reference count maintained in the kref member of + * this structure in the tsem_task_alloc() function. The + * tsem_task_free() function decrements this reference count. When + * the reference count expires, ie. when the last task using the + * modeling namespace exits, an asynchronous workqueue request is + * dispatched to dispose of the context. The work member of this + * structure is used to reference that workqueue. + */ +struct tsem_context { + struct kref kref; + struct work_struct work; + + u64 id; + u64 event_number; + u64 timestamp; + bool sealed; + bool use_current_ns; + + enum tsem_action_type actions[TSEM_EVENT_CNT]; + + char *digestname; + u8 zero_digest[HASH_MAX_DIGESTSIZE]; + struct crypto_shash *tfm; + + struct mutex inode_mutex; + struct list_head inode_list; + + unsigned int magazine_size; + spinlock_t magazine_lock; + unsigned long *magazine_index; + struct tsem_work *ws; + struct tsem_event **magazine; + + const struct tsem_context_ops *ops; + struct tsem_model *model; + struct tsem_external *external; +}; + +/** + * struct tsem_context_ops - Security modeling namespace operations. + * @char: A pointer to a null-terminated array containing the name + * of the security model being implemented. + * @bypass: A pointer to an array of booleans of size TSEM_EVENT_CNT + * that specify whether or not a security event handler should + * be bypassed. + * @event_init: A pointer to the function that implements initialization + * of the characteristics of the security event. + * @map: A pointer to the function that implements the mapping + * of security event characteristics into a security + * coefficient. + * + * This structure is used to define the operations that are available + * for a security modeling namespace. It provides the mechanism for + * customizing the CELL descriptions that are implemented for a security + * model. + */ +struct tsem_context_ops { + const char *name; + const bool *bypasses; + int (*init)(struct tsem_event *ep); + int (*map)(struct tsem_event *ep); +}; + +/** + * struct tsem_model - TSEM internal TMA description. + * @have_aggregate: Flag variable to indicate whether or not the + * hardware aggregate value has been injected into + * the model. + * @base: The base value that is to be used in computing the + * security state coefficients for the model. + * @measurement: The time dependent linear extension state of the + * security state coefficients that have been + * experienced in the model. + * @state: The time independent functional description of the security + * model. + * @point_lock: The spinlock that protects access to the list of + * security state coefficients in the model. + * @point_list: A pointer to the list of security state coefficients + * in the model protected by the point_lock. + * @point_end_mutex: The mutex that is used to protect the end of the + * list of security state coefficients that will + * be exported. + * @point_end: A pointer to the end of the list of security state + * coefficients that will be traversed by a call to the + * control plane. + * @trajectory_lock: The spinlock used to protect the list of security + * event descriptions in the model. + * @trajectory_list: A pointer to the list of descriptions of the + * security events that have been recorded in this + * model. + * @trajectory_end_mutex: The mutex that protects the end of the list + * of security event descriptions. + * @trajectory_end: A pointer to the end of the list of security event + * descriptions that will be traversed by a call to + * the control plane. + * @forensics_lock: The spinlock used to protect the list of security + * event descriptions that are considered invalid by + * the model being enforced. + * @forensics_list: A pointer to the list of descriptions of security + * events that are considered invalid by the security + * model being enforced. + * @forensics_end_mutex: The mutex that protects the end of the list + * of security event descriptions that are + * considered invalid by the current model. + * @forensics_end: A pointer to the end of the list of security event + * descriptions, that are considered invalid, that are + * to be traversed by a call to the control plane. + * @pseudonym_mutex: The mutex lock that protects the list of file + * digest pseudonyms for the current model. + * @pseudonum_list: A pointer to the list of file digest pseudonyms + * that have been declared for the current model. + * @magazine_size: The number of struct tsem_event_point structures that + * are held in reserve for security event hooks that + * are called in atomic context. + * @magazine_lock: The spinlock that protects access to the event + * magazine for the security context. + * @magazine_index: The bitmap that is used to track the magazine slots + * that have been allocated. + * @ws: An array of work structures that are used to refill the magazine + * slots. + * @magazine: An array of pointers to struct tsem_event_point structures that + * are pre-allocated for security hooks called in atomic + * context. + * + * If a call to the tsem_ns_create() function specifies that a kernel + * based trusted modeling agent is to be used to implement the + * security namespace model, a pointer to this structure is placed in + * the struct tsem_context structure. This structure is used to + * maintain the state of the kernel based model. + * + * There are two primary functional values that are maintained by the + * model. The measurement member of this structure represents the + * time dependent linear extension sum of the security state + * coefficients that have been assigned to security events that have + * occurred in the context of the model. This is a measurement + * that has been classically maintained by a Trusted Platform Module. + * + * This classic integrity measurement is subject to scheduling + * dependencies and may be invariant from run to run of the model. It + * is of primary use in verifying the order of security events that + * have occurred in the model. + * + * The state member of this structure represents a time independent + * linear extension sum of the security state coefficients that have + * been generated in the model. It represents a functional value + * for the security state of the model being enforced. + * + * Both of these measurements are dependent on the platform hardware + * aggregate value and the base point that has been defined for the + * define. + * + * A non-NULL representation of the hardware aggregate value is only + * available if the platform has a TPM. The have_aggregate member of + * this structure is a flag variable that indicates whether or not the + * aggregate value has been injected into the model. + * + * The base member of this structure contains a model specific + * coefficient that is used to perturb each security state coefficient + * generated in the model. This value is designed to serve as a + * 'freshness' value for a verifying party to the model. + * + * There are three primary model lists maintain by this structure: + * + * * security state points + * * security trajectory events + * * security forensics events + * + * Similar members are maintained in this structure to support each of + * these lists. + * + * All three lists are extension only and are protected by a spinlock + * that can be held in atomic context. This spinlock is only held for + * the period of time required to extend the list. + * + * Calls by the control plane to interrogate the lists require the + * traversal of the list that is ill-suited for a spinlock. As a + * result each list type has a mutex associated with it that protects + * a pointer to the end of the list, an endpoint that is determined at + * the start of a call to the control plane. + * + * The list spinlock is used at the start of the control plane call to + * capture the end of the list that is then protected by the mutex. + * In essence this is used to transition protection of the list from + * the spinlock to the mutex. + * + * The kernel based modeling agent has support for maintaining a + * constant digest value for files, that by function, do not have a + * fixed digest value, such as log files or files residing on a + * pseudo-filesystem. The pseudonym_list member of this structure + * points to the list of these designations. The pseudonym_mutex + * structure protects this list. + * + * Like the struct tsem_context structure the tsem_model structure + * maintains a magazine of structures that are used to service + * security events that are called in atomic context. The magazine + * maintained by this structure is a list of struct tsem_event_point + * structures that are used to describe the security state + * coefficients held by the model. + * + * The description of struct tsem_context details the implementation + * of the magazine which is identical to the implementation for this + * structure, with the exception of the type of structures that are + * held in reserve. + */ +struct tsem_model { + bool have_aggregate; + + u8 base[HASH_MAX_DIGESTSIZE]; + u8 measurement[HASH_MAX_DIGESTSIZE]; + u8 state[HASH_MAX_DIGESTSIZE]; + + spinlock_t point_lock; + struct list_head point_list; + struct mutex point_end_mutex; + struct list_head *point_end; + unsigned int point_count; + + spinlock_t trajectory_lock; + struct list_head trajectory_list; + struct mutex trajectory_end_mutex; + struct list_head *trajectory_end; + + spinlock_t forensics_lock; + struct list_head forensics_list; + struct mutex forensics_end_mutex; + struct list_head *forensics_end; + + struct mutex pseudonym_mutex; + struct list_head pseudonym_list; + + struct mutex mount_mutex; + struct list_head mount_list; + + unsigned int magazine_size; + spinlock_t magazine_lock; + unsigned long *magazine_index; + struct tsem_work *ws; + struct tsem_event_point **magazine; +}; + +/** + * struct tsem_external - TSEM external TMA description. + * @export_only: A flag variable used to indicate that the security + * namespace is running in export only mode that + * simply presents the events to the external trust + * orchestrator. + * @export_lock: The spinlock that protects access to the export_list + * member of this structure. + * @export_list: A pointer to the list of events waiting to be + * exported to the trust orchestrator for the security + * modeling namespace. The structure type that is + * linked by this list is the struct export_event + * structure that is private to the export.c compilation + * unit. + * @dentry: A pointer to the dentry describing the pseudo-file in the + * /sys/kernel/security/tsem/external_tma directory that is + * being used to export security event descriptions to the + * external trust orchestrator for the security modeling + * namespace. + * @have_event: A flag variable to indicate that is work queued + * on the export pseudo-file for the security modeling + * namespace. + * @wq: The work queue used to implement polling for the security + * event export file. + * @magazine_size: The number of struct export_event structures that + * are held in reserve for security event hooks that + * are called in atomic context. + * @magazine_lock: The spinlock that protects access to the event + * magazine for the security modeling domain. + * @magazine_index: The bitmap that is used to track the magazine slots + * that have been allocated. + * @ws: An array of work structures that are used to refill the magazine + * slots. + * @magazine: An array of pointers to struct export_event structures that + * are pre-allocated for security hooks called in atomic + * context. + * + * If an externally modeled security modeling namespace is created + * a structure of this type is allocated for the namespace and placed + * in the struct tsem_context structure. + * + * The primary purpose of this structure is to manage event + * descriptions that are being transmitted to the trust orchestrator + * associated with the security modeling namespace. The pseudo-file + * will be as follows: + * + * /sys/kernel/security/tsem/external_tma/N + * + * Where N is the context id number of the modeling namespace. + * + * The dentry member of this structure is used to represent the + * pseudo-file that is created when the external modeled namespace is + * created. + * + * This list of events waiting to be received by the trust + * orchestrator is maintained in the export_list member of this + * structure. Additions or removals from the list hold the spinlock + * described by the export_lock member of this structure. + * + * The wq member of this structure is used to implement a workqueue + * to support polling for events on the export control file. The + * have_event flag is set to indicate to the polling call that + * security events are available for export. + * + * When a security event description is exported the calling task is + * scheduled away to allow the trust orchestrator to process the + * event. This obviously creates issues for security events that are + * called in atomic context. + * + * Security events in atomic context are exported as an async_event + * rather than a simple event. The trust orchestrator has the option + * of killing the workload that deviated from the security model or + * signaling a violation of the model. + * + * To support the export of asynchronous events, magazine + * infrastructure, similar to the event and model structure magazines, + * is maintained by this structure for the external modeling + * namespace. + */ +struct tsem_external { + bool export_only; + + spinlock_t export_lock; + struct list_head export_list; + struct dentry *dentry; + bool have_event; + wait_queue_head_t wq; + + unsigned int magazine_size; + spinlock_t magazine_lock; + unsigned long *magazine_index; + struct tsem_work *ws; + struct export_event **magazine; +}; + +/** + * struct tsem_work - TSEM magazine refill work structure. + * @index: The index number of the slot in the structure magazine that + * is being refilled. + * @u: A union that holds pointers to the structure whose magazine is + * being refilled. + * @work: The work structure that manages the workqueue being used to + * refill the magazine entry. + * + * As has been previously documented for the struct tsem_context, + * struct tsem_model and struct tsem_external structures, there is a + * need to maintain a magazine of these structures in order to allow + * the processing of security events that are called in atomic + * context. An array of this structure type is embedded in each of + * those structures to manage the asynchronous refill of the slot in + * the magazine that was used to handle an atomic security event. + * + * The index member of this structure points to the slot in the + * magazine that this work item is referencing. + * + * The structure that the refill work is being done for is maintained + * in the respective structure pointer in the u member of this + * structure. + * + * The work member of this structure is used to reference the + * asynchronous work request that is being submitted for the refill. + */ +struct tsem_work { + unsigned int index; + union { + struct tsem_context *ctx; + struct tsem_model *model; + struct tsem_external *ext; + } u; + struct work_struct work; +}; + +/** + * struct tsem_COE - TSEM context of execution definition structure. + * @uid: The numeric user identity that the COE is running with. + * @euid: The effective user identity that the COE is running with. + * @suid: The saved user identity possessed by the COE. + * @gid: The group identity that the COE is running with. + * @egid: The effective group identity that the COE possesses. + * @sgid: The saved group identity of the COE. + * @fsuid: The filesystem user identity that the COE is running with. + * @fsgid: The filesystem group identity that the COE is running with. + * @capeff: This union is used to implement access to the effective + * capability set the COE is running with. The mask value + * is used to assign to the structure with the value member + * used to extract the 64 bit value for export and + * computation. + * @securebits: In a file capabilities implementation this value + * specifies potential handling for process running with + * a UID value of 0. + * + * A security state coefficient is computed from two primary entities: + * the COE and the CELL identities. This structure is used to carry + * and encapsulate the characteristics of the context of execution + * (COE) that will be used to generate the COE identity. + * + * The numeric values for discretionary access controls, ie. uid, gid, + * are determined by which user namespace the security modeling + * namespace is configured to reference. The reference will be either + * the initial user namespace or the user namespace that the context + * of execution is running in. This reference can be set on a per + * security model namespace basis. + */ +struct tsem_COE { + uid_t uid; + uid_t euid; + uid_t suid; + + gid_t gid; + gid_t egid; + gid_t sgid; + + uid_t fsuid; + gid_t fsgid; + + union { + kernel_cap_t mask; + u64 value; + } capeff; + + unsigned int securebits; +}; + +/** + * struct tsem_inode_cell - TSEM inode information. + * @uid: The numeric user identity assigned to the inode. + * @gid: The numeric group identity assigned to the inode. + * @mode: The discretionary access mode for the file. + * @s_magic: The magic number of the filesystem that the file resides + * in. + * @s_id: The name of the block device supporting the filesystem the + * inode is on. + * @s_uuid: The uuid of the filesystem that contains the inode. + * + * This structure defines the characteristics of an inode that is + * referenced by a security event. + */ +struct tsem_inode_cell { + uid_t uid; + gid_t gid; + umode_t mode; + u32 s_magic; + u8 s_id[32]; + u8 s_uuid[16]; +}; + +/** + * struct tsem_inode_entry - Reference to a directory inode with temp files. + * @list: List of directory inodes for a security modeling namespace + * that have had an inode created under the directory. + * @tsip: A pointer to the TSEM security description of a temporary + * file that was createdunder a directory entry. + * + * This structure is used to implement a list of directory inodes that + * have had temporary files created under them in a security modeling + * namespace. This list is used to allow the instance identifiers + * for inodes to be removed when the security modeling namespace + * terminates or when the directory in which temporary files had been + * created is removed. + */ + +struct tsem_inode_entry { + struct list_head list; + struct tsem_inode *tsip; +}; + +/** + * struct tsem_inode_instance - Instance information for a created inode. + * @list: List of inode owners. + * @creator: The id number of the security modeling namespace that is + * creating an inode. + * @instance: The instance number of an inode being created under a + * given directory. + * @owner: The TASK_ID of the process creating the inode. + * @pathname: A pointer to allocated memory holding the null-terminated + * pathname for the inode. + * + * This structure is used to convey information about the owner and + * instance number of an inode created in a security modeling namespace. + * + * This structure serves three distinct purposes. + * + * A linked list of these structures is used to convey ownership and + * instance information about a created inode from the + * tsem_inode_create() function to the tsem_inode_init_security() + * function, so that this information can be attached to the inode via + * the tsem_inode structure. + * + * Secondly, a linked list of inode ownership information is + * maintained for inodes that are created in a security modeling + * namespace and used as mountpoints. This list is maintained in the + * security model description for the namespace. Since the inode that + * is 'covering' the mountpoint is different than the inode describing + * the directory created for the mountpoint, the ownership information + * for the inode needs to carried as a characteristic of the model. + * + * The final use of this structure is to track the instance numbers of + * an inode created by a TASK_ID. This list is carried by the + * directory in which temporary files and directories are created. + * + */ +struct tsem_inode_instance { + struct list_head list; + + u64 creator; + u64 instance; + u8 owner[HASH_MAX_DIGESTSIZE]; + char *pathname; +}; + +/** + * struct tsem_path - TSEM path information. + * @created: A flag to indicate that the path was created in the + * context of the current security modeling namespace. + * @creator: The id of the security modeling namespace that created + * the path. + * @instance: The instance number of an inode that was created. + * @owner: The TASK_ID of the process that created the path. + * @dev: The device number that the filesystem is mounted on. + * @pathname: An allocated and null-terminated buffer containing the + * path from the root directory to the file. + * + * The tsem_path structure is used to carry information about the + * pathname and ownership of a filesystem object that is an argument + * to a security event handler. + */ +struct tsem_path { + bool created; + u64 creator; + u64 instance; + u8 owner[HASH_MAX_DIGESTSIZE]; + + dev_t dev; + char *pathname; +}; + +/** + * struct tsem_dentry - TSEM dentry definition. + * @have_inode: A flag variable to indicate that the dentry has an + * inode associated with it. + * @inode: The TSEM characteristics of the inode associated with a dentry. + * @path: The path definition for the dentry. + * + * This structure is used to contain the TSEM representation of a + * dentry. + */ +struct tsem_dentry { + bool have_inode; + struct tsem_inode_cell inode; + struct tsem_path path; +}; + +/** + * struct tsem_inode_args - Arguments for inode security handlers. + * @mode: The access mode requested for an inode being created. + * @dev: For the inode_mknod handler, the device specification for + * device node being created. + * @in.old_name: In the case of the tsem_inode_symlink handler, this + * member contains a pointer to the filename of the target + * of the symbolic link. + * @in.dir: For handlers processing rename or movement of an inode, + * the inode of the directory that contains the inode to be moved. + * @in.new_dir: For handlers processing the rename or movement of an + * inode, the inode of the directory that will contain + * the destination inode. + * @in.dentry: The dentry argument to inode event handlers that take + * a dentry. + * @in.new_dentry: In the case of handlers that result in a new dentry + * a pointer to that dentry. + * @out.old_name: In the case of the tsem_inode_symlink handler this + * member contains a pointer to a copy of the name of + * the target of symbolic link. This second + * representation is used to avoid warnings about the + * use of a constant character pointer in the arguments + * to the handler. + * @out.dir: The TSEM representation of the inode representing a directory + * that the security handler is acting on. + * @out.new_dir: For inode movements or renames, the TSEM representation + * of the new_dir argument. + * @out.dentry: The TSEM representation of the dentry argument to a + * security handler. + * @out.new_dentry: For inode movements or renames, the + * representation of the new location of the inode. + * + * This structure is used to carry input parameters and their + * retained and translated TSEM equivalent for LSM security handlers + * that are acting on inodes and/or dentries. + */ +struct tsem_inode_args { + umode_t mode; + dev_t dev; + + union { + struct { + const char *old_name; + struct inode *dir; + struct inode *new_dir; + struct dentry *dentry; + struct dentry *new_dentry; + } in; + + struct { + char *old_name; + struct tsem_inode_cell dir; + struct tsem_inode_cell new_dir; + struct tsem_dentry dentry; + struct tsem_dentry new_dentry; + } out; + }; +}; + +/** + * struct tsem_file_args - TSEM file argument description. + * @cmd: The command argument for security handlers that take a + * command type arguement, ie. file_ioctl, file_fcntl, file_lock + * handlers. + * @in.pseudo_file: A flag indicating that the file was on a + * pseudo-filesystem and will not have a digest value. + * @in.file: A structure to the file that will be modeled. + * @out.path: The TSEM representation of the pathname to a file. + * @out.inode: The TSEM representation of the inode that backs a file + * description + * @out.flags: The flags value from the file structure. + * @out.digest: The cryptographic checksum of the contents of the file. + * + * This structure is used to carry the input file description and + * their TSEM retention values for security event handles that are + * provided with a struct file pointer. + */ +struct tsem_file_args { + unsigned int cmd; + + union { + struct { + bool pseudo_file; + struct file *file; + } in; + + struct { + struct tsem_path path; + struct tsem_inode_cell inode; + unsigned int flags; + u8 digest[HASH_MAX_DIGESTSIZE]; + } out; + + }; +}; + +/** + * struct tsem_mmap_file_args - TSEM memory mapping arguments. + * @anonymous: A flag variable to indicate whether or not the mapping + * is file backed or anonymous. + * @file: If the handler is being called for a file backed mapping this + * structure will be populated with the TSEM description of the + * file. + * @prot: The protections that are being requested for the mapping. + * @flags: The control flags to the memory mapping call. + * + * This structure is used to encapsulate the arguments provided to the + * tsem_mmap_file security event handler. The anonymous member of + * this structure is used internally by TSEM to indicate that the + * file pointer to the call was NULL, thus indicating that the mapping + * is for anonymous memory. + */ +struct tsem_mmap_file_args { + u32 anonymous; + struct tsem_file_args file; + u32 prot; + u32 flags; +}; + +/** + * struct tsem_socket - TSEM socket information + * @family: The family name of the socket whose creation is being + * requested. + * @type: The type of the socket being created. + * @protocol: The protocol family of the socket being created. + * @kern: A flag variable to indicate whether or not the socket being + * created is kernel or userspace based. + * @owner: The TASK_ID of the task that created the socket. + * + * This structure is used to encapsulate socket information for + * security handlers that take a socket description as an argument. + */ +struct tsem_socket { + int family; + int type; + int protocol; + int kern; + u8 owner[HASH_MAX_DIGESTSIZE]; +}; + +/** + * struct tsem_socket_args - TSEM socket arguments + * @value: Possible numeric values passed to event handlers. + * @optname: The option name for the tsem_socket_setsockopt call. + * @in.socka: A pointer to the socket argument of a security handler. + * @in.sockb: A pointer to a second socket argument that may be supplied + * to the handler. + * @in.addr: In the case of handlers that accept an address + * description a pointer to that description. + * @out.socka: The TSEM representation of the first socket argument. + * @out.sockb: The TSEM representation of the second socket argument. + * @out.have_addr: A boolean flag used to indicate that either the + * ipv6 or ipv6 union members have been populated. + * @out.ipv4: The IPV4 address of an AF_INET socket. + * @out.ipv6: The IPV6 address of an AF_INET6 socket. + * @out.path: The path of an AF_UNIX socket. + * @out.mapping: The checksum of the socket address if the socket type + * is other than AF_INET, AF_INET6 or AF_UNIX. + * + * This structure is used to maintain arguments provided to LSM + * hooks that handle generic socket security events. + */ +struct tsem_socket_args { + int value; + int optname; + + union { + struct { + struct sock *socka; + struct sock *sockb; + void *addr; + } in; + + struct { + struct tsem_socket socka; + struct tsem_socket sockb; + bool have_addr; + union { + struct sockaddr_in ipv4; + struct sockaddr_in6 ipv6; + char path[UNIX_PATH_MAX + 1]; + u8 mapping[HASH_MAX_DIGESTSIZE]; + }; + } out; + }; +}; + +/** + * struct tsem_netlink_args - TSEM netlink event parameters + * @in.sock: The sock argument to the LSM event handler. + * @in.parms: The pointer to the netlink parameters from the sk_buff + * structure that was passed to the LSM hook. + * @out.sock: The TSEM representation of the sock argument. + * @out.uid: The UID, in the TSEM designated namespace of the uid in + * the netlink control block. + * @out.gid: THE GID, in the TSEM designated namespace of the gid in + * the netlink control block. + * @out.portid: The portid member of the netlink_skb_parms structure. + * @out.dst_group: The dst_group member of the netlink_skb_parms structure. + * @out.flags: The flags member of the netlink_skb_parms structure. + * @nsid_set: The nsid_set flag member of the netlink_skb_parms structure. + * @nsid: The nsid member of the netlink_skb_parms structure. + * + * This structure is used to encapsulate and retain the arguments + * provided to the tsem_netlink_send event handler. + * + */ +struct tsem_netlink_args { + union { + struct { + struct sock *sock; + struct netlink_skb_parms *parms; + } in; + + struct { + struct tsem_socket sock; + uid_t uid; + gid_t gid; + __u32 portid; + __u32 dst_group; + __u32 flags; + bool nsid_set; + int nsid; + } out; + }; +}; + +/** + * struct tsem_sb_args - TSEM parameters for superblock security events. + * flags: An integer value that was a component of the LSM argument + * for the sb_mount, sb_umount, sb_remount handlers. + * @in.sb: For the sb_remount handler the pointer to the superblock + * argument passed to the caller. + * @in.dentry: An incoming dentry argument. + * @in.dev_name: The name of the device to be used for the sb_mount + * command. + * @in.path: The path argument passed to the sb_mount commands. + * @in.type: A character pointer to the filesystem type being processed + * by the superblock security handlers. + * @in.path2: The second path argument passed to the move_mount and + * sb_pivotroot security handlers. + * @out.dentry: The TSEM representation of the dentry argument to the + * handler. + * @out.inode: The TSEM representation of the inode backing the dentry + * argument to an LSM handler. + * @out.path: The TSEM representation of the incoming path argument. + * @out.dev_name: A an allocated copy of the dev_name argument. + * @out.type: A allocated copy of the filesystem type. + * @out.path2: The TSEM representation of the second path argument if + * used. + * + * This structure is used to encapsulate and retain the arguments for + * the family of security event handlers that deal with superblocks. The + * list of these handlers is as follows: + * + * tsem_sb_mount + * tsem_sb_umount + * tsem_sb_remount + * tsem_move_mount + * tsem_sb_statfs + */ +struct tsem_sb_args { + unsigned long flags; + + union { + struct { + struct super_block *sb; + struct dentry *dentry; + const char *dev_name; + const char *type; + const struct path *path; + const struct path *path2; + } in; + + struct { + struct tsem_dentry dentry; + char *dev_name; + char *type; + struct tsem_path path; + struct tsem_path path2; + } out; + }; +}; + +/** + * struct tsem_task_kill_args - TSEM task kill arguments. + * @u.value: The signed representation of an integer argument. + * @u.resource: The unsigned representation of an integer argument. + * @cur: The current resource limit for a task_setrlimit call. + * @max: The maximum resource limit for a task_setrlimit call. + * @cross_model: A flag variable used to indicate whether or not the + * signal is originating from a security modeling + * namespace other than the namespace of the target process. + * @signal: The number of the signal being sent. + * @source: The task identifier of the process sending the signal + * @target: The task identifier of the target process. + * + * This structure is used to encapsulate and retain the arguments + * provided to the tsem_task_kill security event handler. + * + */ +struct tsem_task_kill_args { + union { + int value; + unsigned int resource; + } u; + u64 cur; + u64 max; + u32 cross_model; + u32 signal; + u8 source[HASH_MAX_DIGESTSIZE]; + u8 target[HASH_MAX_DIGESTSIZE]; +}; + +/** + * struct tsem_task_prlimit_args - TSEM task prlimit arguments. + * @flags: The flag variable passed to the LSM handler. + * @in.cred: The cred pointer passed to the handler. + * @in.tcred: The tcred pointer passed to the handler. + * @out.cred: The TSEM representation of the in.cred pointer. + * @out.tcred: The TSEM representation of the in.tcred pointer. + * + * This structure is used to hold and retain the arguments provided to + * the tsem_task_prlimit security event handler. + */ +struct tsem_task_prlimit_args { + unsigned int flags; + + union { + struct { + const struct cred *cred; + const struct cred *tcred; + + } in; + + struct { + struct tsem_COE cred; + struct tsem_COE tcred; + } out; + + }; +}; + +/** + * struct tsem_task_prctl - TSEM task prctl arguments. + * @option: The first argument to the task_prctl LSM handler + * specifying the command to be executed. + * @arg2: The first argument to the handler. + * @arg3: The second argument to the handler. + * @arg4: The third argument to the handler. + * @arg5: The fourth and final argument to the handler. + * + * This structure is used to encapsulate the arguments provided to the + * tsem_task_prctl security event handler. The argument model is to + * specify the 'option' value which is the kernel prctl call that is + * to be executed. The remaining positional arguments are without + * specific format and are designed to be interpreted by the prctl + * system call based on the command specified. + */ +struct tsem_task_prctl_args { + int option; + unsigned long arg2; + unsigned long arg3; + unsigned long arg4; + unsigned long arg5; +}; + +/** + * struct tsem_inode_attr_args - TSEM inode manipulation arguments. + * @in.path: In the case of the inode_getattr call the path to the + * inode being referenced. + * @in.dentry: In the case of the inode_setattr call the dentry that + * whose characteristics will be set. + * @in.iattr: A pointer to the iattr structure that was passed to the + * inode_setattr handler. + * @out.dentry: A TSEM dentry definition structure that will retain + * the description of either a dentry or path argument + * to a security handler. + * @out.valid: The ia_valid member from the iattr structure passed to the + * inode_setattr handler + * @out.mode: The ia_mode member from the iattr structure passed to the + * inode_setattr handler. + * @out.uid: The ia_uid member from the iattr structure passed to the + * inode_setattr handler. + * @out.gid: The ia_gid member from the iattr structure passed to the + * inode_setattr handler. + * hook. + * @out.size: The ia_size member from the iattr structure passed to the + * inode_setattr handler. + * + * This structure is used to encapsulate information on the arguments + * passed to the inode_getattr and inode_setattr LSM handler. The in + * structure is used to hold the arguments passed that were passed to + * the handlers. Argument information that is to be held for the life + * of the event description are in the out structure. + */ +struct tsem_inode_attr_args { + union { + struct { + const struct path *path; + struct dentry *dentry; + struct iattr *iattr; + } in; + + struct { + struct tsem_dentry dentry; + unsigned int valid; + umode_t mode; + uid_t uid; + gid_t gid; + loff_t size; + } out; + }; +}; + +/** + * struct tsem_inode_xattr_args - TSEM extended attribute arguments. + * @in.dentry: A pointer to the backing inode for the dentry that was + * passed to the LSM hook. The relevant values from the inode + * will be copied into the tsem_file structure. + * @in.name: A pointer to the name of the extended attribute being + * queried. + * @in.size: The size of an extended attribute that may be set. + * @in.flags: The flag value specifying how an extended attributte is + * to be set. + * @out.dentry: The TSEM representation of the path that is being + * action on. + * @out.name: The name of an attribute to be set or retrieved. + * @out.value: The binary value of the extended attribute that was + * passed to the inode_setxattr handler. For an + * internally modeled namespace this value will be freed + * after the coefficient for the event is mapped. + * @out.encoded_value: The Base64 encoding of the extended attribute + * value that is used for either the export of + * the event or the trajectory history. This + * memory will be allocated in order to support + * encoding of the attribute. + * @out.size: The size of the att + * @out.flags: The flags value that was passed to the inode_setxattr + * handler. + * + * This structure is used to encapsulate information on the arguments + * passed to the LSM hooks that manipulate extended attributes. The + * in structure is used to hold the pointers to the arguments passed + * to the LSM hook while the out structure holds the arguments in + * converted form that will be held for the lifetime of the modeling + * namespace. + */ +struct tsem_inode_xattr_args { + union { + struct { + struct dentry *dentry; + const char *name; + const void *value; + size_t size; + int flags; + } in; + + struct { + struct tsem_dentry dentry; + char *name; + char *value; + char *encoded_value; + size_t size; + int flags; + } out; + }; +}; + +/** + * struct tsem_kernel_args - TSEM event descriptions for kernel requests. + * @id: For the tsem_kernel_load_data handler the indicator of the type + * of data being requested. + * @contents: The boolean flag used to indicate whether or not the + * security_kernel_post_load_data handler should be called. + * @in.file: A pointer to the file structure passwd to the + * tsem_kernel_file_file handler. + * @in.kmod_name: A pointer to the buffer containing the name of the + * module to load. + * @out.kmod_name: The retained copy of the kernel module name. + * @out.file: The TSEM representation of the file structure that was + * passed to the tsem_kernel_read_file handler. + * + * This structure is used to encapsulate information on the arguments + * passed to the following LSM hook handlers: + * + * tsem_kernel_module_request + * tsem_kernel_load_data + * tsem_kernel_read_file + */ +struct tsem_kernel_args { + enum kernel_load_data_id id; + bool contents; + + union { + struct { + struct file *file; + char *kmod_name; + } in; + + struct { + char *kmod_name; + struct tsem_file_args file; + } out; + }; +}; + +/** + * struct tsem_time_args - TSEM event description for setting the time + * @have_ts: A flag variable to indicate if the time in seconds and + * nanoseconds is valid. + * @seconds: The number of seconds passed to the time set function. + * @nsecs: The number of nanoseconds to set the time to. + * @have_tz: A flag variable to indicate if the timezone information + * is valid. + * @minuteswest: The minutes west of GMT for the time being set. + * @dsttime: The daylight savings time offset. + * + * This structure is a simple encapsulation of the arguments passed to + * the TSEM_SETTIME handler. + */ +struct tsem_time_args { + bool have_ts; + long seconds; + long nsecs; + + bool have_tz; + int minuteswest; + int dsttime; +}; + +/** + * struct tsem_quota_args - TSEM arguments for quota security management. + * @cmds: The cmds argument from the security_quotactl handler. + * @type: The type argument from the security_quotactl handler. + * @id: The id argument from the security_quotactl handler. + * @in.dentry: In the case of the quota_on LSM handler the dentry + * argument to the handler. + * @in.sb: The superblock pointer argument from the security_quotactl handler. + * @out.dentry: The TSEM dentry representation of a dentry arguement + * to the quota handlers. + * @out.s_flags: In the case of the quotactl handler the flags from + * the superblock of the filesystem. + * @out.fstype: In the case of the quotactl handler the filesystem + * type of the mountpoint. + * + * This structure is an encapsulation of the arguments and their + * retention values for the LSM security handlers that make security + * decisions relevant to filesystem quota manipulation. + */ +struct tsem_quota_args { + int cmds; + int type; + int id; + + union { + struct { + struct dentry *dentry; + const struct super_block *sb; + } in; + + struct { + struct tsem_dentry dentry; + unsigned long s_flags; + char *fstype; + } out; + }; +}; + +/** + * struct tsem_key_args - TSEM key handler arguments. + * @flags: The flags value passed to the key_alloc handler. + * @in.cred: A pointer to the credential structures passed to the + * security handlers. + * @out.possessed: A flag variable indicating if a key is owned by a + * task. + * @out.uid: The owner id of a key. + * @out.gid: The group id of a key. + * @out.flags: The flags value retained for a key operation. + * @out.cred: The retained credentials of a process attempting to + * access a key. + * @out.perm: The retained permissions value of a key. + * + * This structure is used to hold the arguments to the LSM hooks that + * handle key security event and their retained TSEM equivalents. + */ +struct tsem_key_args { + unsigned long flags; + + union { + struct { + key_ref_t ref; + const struct cred *cred; + + } in; + + struct { + bool possessed; + uid_t uid; + gid_t gid; + unsigned long flags; + struct tsem_COE cred; + u32 perm; + } out; + + }; +}; + +/** + * struct tsem_bpf_args - TSEM bpf security handler arguments. + * @bpf.cmd: For the security_bpf LSM handler the command number passed + * the event handler. + * @bpf.size: For the security_bpf LSM handler the size argument passed + * to the event handler. + * @prog.type: For the security_bpf_prog LSM handler the type member of + * the bpf_prog structure passed to the event handler. + * @prog.attach_type: For the security_bpf LSM handler the attach_type + * member of the bpf_prog structure passed to the + * event handler. + * @map.map_type: For the security_map LSM handler the map_type member + * of the bpf_map structure passed to the event handler. + * @map.fmode: For the security_map LSM handler the fmode argument passed + * to the event handler. + + * This structure is used to hold the arguments to the various LSM + * hooks that handle BPF security management. This structure is a + * union over structures for each of the TSEM bpf handlers. + */ +struct tsem_bpf_args { + union { + struct { + int cmd; + unsigned int size; + } bpf; + + struct { + int type; + int attach_type; + } prog; + + struct { + int map_type; + fmode_t fmode; + } map; + }; +}; + +/** + * struct tsem_ipc_perm - TSEM retained members of an IPC permission + * structure. + * @uid: The uid member of the IPC permission structure translated into + * the namespace reference for the modeling namespace. + * @gid: The gid member of the IPC permission structure translated into + * the namespace reference for the modeling namespace. + * @cuid: The cuid member of the IPC permission structure translated + * into the namespace reference for the modeling namespace. + * @cgid: The cgid member of the IPC permission structure translated + * into the namespace reference for the modeling namespace. + * @mode: The mode member of the IPC permission structure. + * + * This structure is used to hold the translated values from a + * kern_ipc_perm structure that is passed to one of the LSM IPC + * shared memory security handlers. + */ +struct tsem_ipc_perm { + uid_t uid; + gid_t gid; + uid_t cuid; + gid_t cgid; + umode_t mode; +}; + +/** + * struct tsem_ipc_args - TSEM arguments for IPC security handlers. + * @perm_flag: For the tsem_ipc_permission handler the permission flag. + * @value: A signed integer value that serves as an argument type to + * a number of the handlers. + * @nsops: In the came of the tsem_sem_semop handler the nsops argument + * to the handler. + * @type: The type argument to the msg_queue_msgrcv handler. + * @in.perm: The kern_ipc_perm structure that is passed to multiple + * handlers that define the permissions for the IPC + * object whose security status is being checked. + * @in.target: For the msg_queue_msgrc handler the TASK_ID of the + * process ending the message. + * @out.perm: The TSEM translated versions of the perm pointer that + * was passed to a handler. + * @out.owner: The TASK_ID of the task that created the IPC resource. + * @out.target: The retained version of the TASK_ID describing the + * sender of a message. + * + * This structure is an encapsulation of the arguments and their + * retention values for the LSM security handlers that make security + * decisions relevant to IPC objects. + */ +struct tsem_ipc_args { + short perm_flag; + int value; + unsigned int nsops; + long type; + + union { + struct { + struct kern_ipc_perm *perm; + struct task_struct *target; + } in; + + struct { + struct tsem_ipc_perm perm; + u8 owner[HASH_MAX_DIGESTSIZE]; + u8 target[HASH_MAX_DIGESTSIZE]; + } out; + }; +}; + +/** + * struct tsem_capability_args - TSEM arguments for capability handling. + * @cap: A capability specified for an event. + * @opts: Options for handling a capabilities command. + * @effective: The effective capability that is being manipulated. + * @inheritable: The inheritable capability that is being manipulated. + * @permitted: The permitted capability that is being manipulated. + * @target: The TASK_ID of the process whose capabilities are being + * requested. + * + * This structure is an encapsulation of the arguments to be retained + * for security event descriptions that describe security events + * involving process capabilities. + */ +struct tsem_capability_args { + int cap; + unsigned int opts; + + kernel_cap_t effective; + kernel_cap_t inheritable; + kernel_cap_t permitted; + + u8 target[HASH_MAX_DIGESTSIZE]; +}; + +/** + * struct tsem_event - TSEM security event description. + * @kref: Reference count structure to track event lifetime. + * @list: The list of security events in a security modeling namespace. + * @work: The work structure that manages the workqueue being used to + * refill the event magazine structures. + * @event: The enumeration type describing the security event that the + * structure is defining. + * @context: The context number of the security modeling namespace that + * generated the event. + * @locked: A boolean flag used to indicate whether or not the + * security event is running in atomic context. + * @instance: The process instance number that is executing the + * event described by the structure. + * @p_instance: The parent process instance number of the process + * executing the event described by the structure. + * @event_number: The sequence number of the event in the security modeling + * namespace that generated the event. + * @pid: The process id number, in the global pid namespace, of the + * task that is executing the security event. + * @comm: A pointer to a null terminated buffer containing the name of + * the process that is requesting the security event. + * @digestsize: The size in bytes of the cryptographic hash function + * that is being used in the security modeling namespace + * in which the event occurred. + * @task_id: The TSEM TASK_ID of the process that generated the + * security event described by an instance of this + * structure. + * @mapping: The security state coefficient that the event described + * by this structure generates. + * @COE: The tsem_COE structure that describes the Context Of + * Execution that generated the event described by this + * structure. + * @no_params: A boolean value that is set if the security event + * has no characterizing parameters. + * @CELL: The CELL union is used to hold the data structures that + * characterize the CELL mapping of the event. + * @CELL.value: A single numeric value that may be used in + * characterizing an event. + * @CELL.netlink: A structure describing parameters that characterize + * an event inolving a netlink event. + * @CELL.inode: A structure describing characters of security events + * that address inode manipulation operations. + * @CELL.file: A structure characterizing a file used as an arguement + * for a security event. + * @CELL.mmap_file: The structure describing the characteristics of + * a security event involving a memory mapping event. + * @CELL.socket: A structure characterizing security events that + * involve a socket. + * @CELL.kernel: A structure characterizing kernel I/O or memory + * loading opeations. + * @CELL.task_kill: The structure describing the characteristics of an + * event sending a signal to a process. + * @CELL.task_prlimit: A structure describing a security event that + * sets process resource limits. + * @CELL.task_prctl: A structure describing an event involving the + * process control operations. + * @CELL.inode_attr: A structure describing events involving getting + * or setting of inode attributes. + * @CELL.inode_xattr: A structure describing events involving actions + * on inode extended attributes. + * @CELL.key: A structure describing events involving manipulation of + * a kernel key. + * @CELL.sb: A structure describing events that involve the a + * filesystem superblock. + * @CELL.quota: A structure describing events that involve the + * manipulation of filesystem quotas. + * @CELL.time: A structure describing the setting of the system time + * or timezone. + * @CELL.bpf: A structure describing involves involving manipulation + * of BPF functionality + * @CELL.ipc: A structure describing events that are manipulating + * shared memory structures or operations. + * @CELL.capability: A structure describing events that manipulate + * the capabilities of a process. + * + * This structure is the primary data structure for describing + * security events that occur in a security modeling namespace. Each + * unique security coefficient in the namespace will have one of these + * structures associated with it. + * + * This structure encapsulates the following three major sources of + * information about the event: + * + * * A description of the process initiating the event. + * * The characteristics of the COE identity of the event. + * * The characteristics of the CELL identity of the event. + * + * Since one event description has to ultimately characterize any + * security event that can occur, the strategy is to use a union that + * contains security event specific structures that describe the + * characteristics of the event. The event member of the structure + * is used to indicate the structure that should be referenced. + * + * The kref member of this structure is used to track the lifetime of + * an instance of this structure. For example, in the case of an + * externally modeled event, when the export of the event description + * is complete. In the case of an internally modeled namespace the + * structure will be released if it represents a security state + * coefficient that is already present in the model. The structures + * are also released when an internally modeled namespace terminates. + * + * The work member of this structure is used to support asynchronous + * updates to a TPM for the root modeling domain. Asynchronous + * updates are used to improve the performance of modeling and to + * handle security events that are running in atomic context and + * cannot be scheduled away while the TPM transaction completes. + * + * The tsem_event_allocate() function is called by a TSEM security + * event handler to allocate and populate an instance of this + * structure. The locked member of this structure is used to + * determine whether the structure should be allocated from the + * kmem_cache based structure pool or from the magazine of structures + * help for processes running in atomic context. + * + * After the event is mapped this structure is either passed to the + * internal trusted modeling agent or the contents of this structure + * is exported to the trust orchestrator attached to the namespace for + * modeling by an external trusted modeling agent. + */ +struct tsem_event { + struct kref kref; + struct list_head list; + struct work_struct work; + void (*event_free)(struct tsem_event *ep); + + enum tsem_event_type event; + u64 context; + bool locked; + u64 instance; + u64 p_instance; + u64 timestamp; + u64 event_number; + pid_t pid; + char comm[TASK_COMM_LEN]; + + unsigned int digestsize; + u8 task_id[HASH_MAX_DIGESTSIZE]; + u8 p_task_id[HASH_MAX_DIGESTSIZE]; + u8 mapping[HASH_MAX_DIGESTSIZE]; + + struct tsem_COE COE; + + bool no_params; + union { + int value; + struct tsem_netlink_args netlink; + struct tsem_inode_args inode; + struct tsem_file_args file; + struct tsem_mmap_file_args mmap_file; + struct tsem_socket_args socket; + struct tsem_kernel_args kernel; + struct tsem_task_kill_args task_kill; + struct tsem_task_prlimit_args task_prlimit; + struct tsem_task_prctl_args task_prctl; + struct tsem_inode_attr_args inode_attr; + struct tsem_inode_xattr_args inode_xattr; + struct tsem_key_args key; + struct tsem_sb_args sb; + struct tsem_quota_args quota; + struct tsem_time_args time; + struct tsem_bpf_args bpf; + struct tsem_ipc_args ipc; + struct tsem_capability_args capability; + + } CELL; +}; + +/** + * struct tsem_event_point - TSEM security coefficient characteristics. + * @list: The list of all the security state coefficients for a + * modeling namespace. + * @valid: A boolean value use to indicate whether or not the security + * state point is a valid coefficient in the model. + * @count: The number of times this coefficient has been expressed by + * the security modeling namespace. + * @point: The security state coefficient for a security event. + * + * This structure is used by internal trusted modeling agents to + * represent each unique security coefficient in a security model. + * Security state coefficients are unique within a model so only one + * struct tsem_event_point structure will be generated regardless of + * how many times the security event that generates the point occurs. + * The count member of this structure represents the total number of + * security events that have occurred generated this point. + * + * The valid member of this structure is used to flag whether this + * is consistent with the model for the namespace or was generated by + * a 'forensic', ie. out of model, event. + * + * Within each security namespace these structures are linked together + * in a list that describes the functional security value of the + * namespace. Entries are only added to this list and never removed. + * + * The desired state of a security model is created by using the TSEM + * control plane to inject a list of acceptable security state + * coefficients into the model. Sealing a model causes any security + * events that produce a coefficient different from those already in + * the model to be rejected as an invalid security event and logged as + * a forensic event for the model. + */ +struct tsem_event_point { + struct list_head list; + bool valid; + u64 count; + u8 point[HASH_MAX_DIGESTSIZE]; +}; + +/** + * struct tsem_inode - TSEM inode status structure. + * @create_mutex: The mutex that protects the create_list. + * @create_list: The list of structures that contain ownership and instance + * information for inodes created under a directory. + * @instance_mutex: The mutex protecting the instance_list. + * @instance_list: The list of task identities that have created inodes + * under a directory and the instance count of inodes + * for each TASK_ID. + * @digest_mutex: The mutex protecting the digest_list. + * @digest_list: A list of structures containing the digest values that + * have been calculated for the inode. + * @status: The status of the inode. See the discussion of enum + * tsem_inode_state for the information that is conveyed + * by this member. + * @created: A boolean flag to indicate that this inode was created + * in the context of a security modeling namespace. + * @creator: The context identity of the security modeling namespace that + * created the inode. + * @instance: The inode instance number for the TASK_ID that created + * the inode. + * @owner: The TASK_ID of the process that created the inode. + * + * This structure is used to contain the TSEM security state of an + * inode. + * + * Three lists are managed by this structure: + * + * An inode that is a directory will have inodes that are create under + * it added to the create_list. This list will be traversed when the + * inode is instantiated so creation information about the inode can + * be registered in the tsem_inode structure attached to that inode. + * + * The instance_list is also maintained by a directory inode and is + * used to track the instances of an inode that is created under the + * directory by specific TASK_ID's. + * + * The digest_list is used for inodes that are backing files and is + * used to track the various cryptographic digest values that have + * been computed for the file. This supports the use of multiple + * simultaneous digest values across multiple security modeling + * namespaces. + * + * The status member of this function serves two purposes: + * + * For file based inodes the status member is used to indicate + * whether or not the digest value is being computed. The + * tsem_file_open handler uses this status variable to determine + * whether or not the modeling of an event should be bypassed if the + * file is being opened by the kernel to compute the digest value of + * the file. + * + * The second role of the status member is to indicate that the inode + * is one of the TSEM control plane files. This allows modeling of + * events involving this inode to be bypassed in order to avoid a + * 'Heisenberg Deadlock' situation. + */ +struct tsem_inode { + struct mutex create_mutex; + struct list_head create_list; + + struct mutex instance_mutex; + struct list_head instance_list; + + struct mutex digest_mutex; + struct list_head digest_list; + + enum tsem_inode_state status; + bool created; + u64 creator; + u64 instance; + u8 owner[HASH_MAX_DIGESTSIZE]; +}; + +/** + * struct tsem_ipc - TSEM IPC security structure. + * @owner: The identity of the task that created the IPC resource. + * + * This structure is used to track the TASK_ID of the process that + * created the IPC memory resource so that information can be + * integrated into security coefficients that are generated for + * shared memory events. + */ +struct tsem_ipc { + u8 owner[HASH_MAX_DIGESTSIZE]; +}; + +/** + * struct tsem_inode_digest - Digest specific file checksum. + * @list: The list structure used to link multiple digest values + * for an inode. + * @name: A pointer to an allocated null-terminated character + * buffer containing the name of the hash function that + * generated the digest value represented by an instance + * of this structure. + * @version: The version number of the inode that generated the digest + * value that is currently represented. + * @value: The digest value of the file represented by the inode.. + * + * A linked list of these structures is maintained for each inode that + * is modeled by TSEM and is used to support multiple hash specific + * digest values for a file represented by the inode. The tsem_inode + * structure that represents the TSEM security status of the inode + * contains a list of these structures. + * + * The name member of structure contains the name of the hash function + * that generated the checksum value. This name is used to locate the + * correct structure by comparing its value against the hash function + * that is being used by the security modeling namespace that is + * traversing the list attempting to locate a previously computed + * digest value. + * + * The version member of the structure contains the inode version number + * that was in effect when the last digest value of this type was computed. + * This version number value is used to detect changes and to trigger an + * update of the digest value. + */ +struct tsem_inode_digest { + struct list_head list; + char *name; + u64 version; + u8 value[HASH_MAX_DIGESTSIZE]; +}; + +/* + * The following four variables are the only globally visible + * variables used in the TSEM implementation. + * + * The tsem_blob_sizes variable is used by the LSM infrastructure to + * describe the amount of space that will be needed by the TSEM + * security structures. + * + * The tsem_names array is defined in the tsem.c file and contains an + * array of pointers to the strings that define the names for each of + * the TSEM security event handlers. The enum tsem_event_type + * enumeration indexes this array. + * + * The tsem_root_actions array is also indexed by the tsem_event_type + * enumeration and is used to determine the type of response that a + * TSEM security event handler is to return to the caller, ie. either + * logging or enforcing. The contents of this array is inherited by + * copying the array into the struct tsem_context structure for + * modeling namespaces that are subordinate to the root model. + */ +extern struct lsm_blob_sizes tsem_blob_sizes; +extern const char * const tsem_names[TSEM_EVENT_CNT]; +extern enum tsem_action_type tsem_root_actions[TSEM_EVENT_CNT]; +extern const struct tsem_context_ops tsem_model0_ops; + +/* + * The following section of the file contains the definitions for the + * externally visible functions in each of the TSEM compilation units. + */ +extern struct dentry *tsem_fs_create_external(const char *name); +extern void tsem_fs_show_trajectory(struct seq_file *c, struct tsem_event *ep); +extern void tsem_fs_show_field(struct seq_file *c, const char *field); +extern void tsem_fs_show_key(struct seq_file *c, char *term, char *key, + char *fmt, ...); +extern int tsem_fs_init(void); + +extern struct tsem_model *tsem_model_allocate(size_t size); +extern void tsem_model_free(struct tsem_context *ctx); +extern int tsem_model_event(struct tsem_event *ep); +extern int tsem_model_load_point(u8 *point); +extern int tsem_model_load_pseudonym(u8 *mapping); +extern int tsem_model_has_pseudonym(struct tsem_inode *tsip, char *pathname); +extern void tsem_model_load_base(u8 *mapping); +extern int tsem_model_add_aggregate(void); +extern void tsem_model_compute_state(void); +extern void tsem_model_magazine_free(struct tsem_model *model); +extern int tsem_model_cache_init(struct tsem_model *model, size_t size); + +extern void tsem_ns_put(struct tsem_context *ctx); +extern int tsem_ns_event_key(u8 *task_key, const char *keystr, u8 *key); +extern int tsem_ns_create(const enum tsem_control_type type, + const char *digest, const enum tsem_ns_reference ns, + const char *key, const unsigned int cache_size, + const struct tsem_context_ops *ops); +extern int tsem_ns_export_root(unsigned int magazine_size); + +extern int tsem_export_show(struct seq_file *m, void *v); +extern int tsem_export_event(struct tsem_event *ep); +extern int tsem_export_action(enum tsem_event_type event, bool locked); +extern int tsem_export_aggregate(void); +extern int tsem_export_magazine_allocate(struct tsem_external *ext, + size_t size); +extern void tsem_export_magazine_free(struct tsem_external *ext); +extern int tsem_export_cache_init(void); + +extern int tsem_map_task(struct file *file, u8 *mapping); +extern int tsem_map_event(struct tsem_event *ep); + +extern struct tsem_event *tsem_event_allocate(enum tsem_event_type event, + bool locked); +extern int tsem_event_init(struct tsem_event *ep); +extern int tsem_event_generate(struct tsem_event *ep); +extern void tsem_event_put(struct tsem_event *ep); +extern void tsem_event_get(struct tsem_event *ep); +extern int tsem_event_magazine_allocate(struct tsem_context *ctx, size_t size); +extern void tsem_event_magazine_free(struct tsem_context *ctx); +extern int tsem_event_cache_init(void); + +extern u8 *tsem_trust_aggregate(void); +extern int tsem_trust_add_event(struct tsem_event *ep); + +/* + * The remaining inline function declarations follow the design + * pattern of the other LSM's and implement functions that return + * various TSEM characteristics of tasks, modeling contexts and + * inodes. + */ +static inline struct tsem_task *tsem_task(const struct task_struct *task) +{ + return task->security + tsem_blob_sizes.lbs_task; +} + +static inline bool tsem_task_trusted(struct task_struct *task) +{ + return tsem_task(task)->trust_status & TSEM_TASK_TRUSTED; +} + +static inline bool tsem_task_untrusted(struct task_struct *task) +{ + return tsem_task(task)->trust_status & ~TSEM_TASK_TRUSTED; +} + +static inline struct tsem_context *tsem_context(struct task_struct *task) +{ + return tsem_task(task)->context; +} + +static inline struct tsem_model *tsem_model(struct task_struct *task) +{ + return tsem_task(task)->context->model; +} + +static inline struct tsem_inode *tsem_inode(struct inode *inode) +{ + return inode->i_security + tsem_blob_sizes.lbs_inode; +} + +static inline struct tsem_ipc *tsem_ipc(struct kern_ipc_perm *kipc) +{ + return kipc->security + tsem_blob_sizes.lbs_ipc; +} + +static inline struct crypto_shash *tsem_digest(void) +{ + return tsem_context(current)->tfm; +} + +static inline unsigned int tsem_digestsize(void) +{ + return crypto_shash_digestsize(tsem_digest()); +} From patchwork Mon Aug 26 10:37:18 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Dr. Greg" X-Patchwork-Id: 13777629 X-Patchwork-Delegate: paul@paul-moore.com Received: from blizzard.enjellic.com (wind.enjellic.com [76.10.64.91]) by smtp.subspace.kernel.org (Postfix) with ESMTP id CCAA117A599; Mon, 26 Aug 2024 10:50:40 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=76.10.64.91 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1724669444; cv=none; b=mT00BU6Yuegqtzt1c7T/aInKpg41fnd7Lcd71kdforOv+pJjfklJw6QXMLi32zeYOxTpBDzgc/1fFHKHFFpCrLuMJhSTij9Ktg6jgZ1hholXgeUGIbmaeaOE2GVMVbX+iQr3X7pzRggvFnRlsbLW/62vMW5KcTb9WDYmZDUMSgM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1724669444; c=relaxed/simple; bh=9YUNiymeyRMuAuOZQUKTPtfNJ7SkfhZmgQxyGMsIEv8=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=T9DmRGzvbF95hkt6lgBsxLjVJHm3Jf6mgt4oeJ2c1Yqkh6r5UjT8azeZgppO4o8K3pw9Zpd2BciNTxBYekvmjBBvMS1mxy1dLpxkDha37aDdkT+/Ljhs14J+q9Np7AmJHcemnxqmgAKZNTbNSRGDwWi+ObBVGExmjq4TQ78IEso= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=enjellic.com; spf=pass smtp.mailfrom=enjellic.com; arc=none smtp.client-ip=76.10.64.91 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=enjellic.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=enjellic.com Received: from blizzard.enjellic.com (localhost [127.0.0.1]) by blizzard.enjellic.com (8.15.2/8.15.2) with ESMTP id 47QAbUYr003417; Mon, 26 Aug 2024 05:37:30 -0500 Received: (from greg@localhost) by blizzard.enjellic.com (8.15.2/8.15.2/Submit) id 47QAbUZf003416; Mon, 26 Aug 2024 05:37:30 -0500 X-Authentication-Warning: blizzard.enjellic.com: greg set sender to greg@enjellic.com using -f From: Greg Wettstein To: linux-security-module@vger.kernel.org, linux-kernel@vger.kernel.org Cc: jmorris@namei.org Subject: [PATCH v4 04/14] Add primary TSEM implementation file. Date: Mon, 26 Aug 2024 05:37:18 -0500 Message-Id: <20240826103728.3378-5-greg@enjellic.com> X-Mailer: git-send-email 2.39.1 In-Reply-To: <20240826103728.3378-1-greg@enjellic.com> References: <20240826103728.3378-1-greg@enjellic.com> Precedence: bulk X-Mailing-List: linux-security-module@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 The tsem.c file is the 'master' file in the TSEM implementation. It is responsible for initializing the LSM and providing the implementation of the security event handlers. --- security/tsem/tsem.c | 2446 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 2446 insertions(+) create mode 100644 security/tsem/tsem.c diff --git a/security/tsem/tsem.c b/security/tsem/tsem.c new file mode 100644 index 000000000000..76d65b3e62b3 --- /dev/null +++ b/security/tsem/tsem.c @@ -0,0 +1,2446 @@ +// SPDX-License-Identifier: GPL-2.0-only + +/* + * Copyright (C) 2024 Enjellic Systems Development, LLC + * Author: Dr. Greg Wettstein + * + * This file is the primary implementation file for the tsem LSM. + * + * It implements initialization and setup functions that interpret + * kernel command-line arguments and prepares TSEM for operation. + * + * In addition it contains all of the TSEM specific security event + * handlers that are responsible for handling the LSM events that TSEM + * models. + * + * Each TSEM event handler calls the tsem_allocate_event() function to + * allocate a structure that will be used to describe the event. The + * CELL union of this structure contains various structures that are + * used to hold these parameters. + * + * Since the event characterization parameters need to be retained for + * the lifetime of the tsem_event structure that is allocated. In the + * case of internally modeled namespaces this lifespan is the lifetime + * of the security modeling namespace. In the case of externally + * modeled namespaces, the lifespan is until the security event + * description is exported to an external trust orchestrator. + * + * In order to support this model, the event description structures + * are typically composed of a union over 'in' and 'out' structures. + * The 'in' structures are used to hold arguments to the event handler + * that may only be relevant for the duration of the call. These + * values are translated into members of the 'out' structure that + * retain the values until the end of the lifetime of the tsem_event + * structure. + * + * Each TSEM event handler is responsible for allocating a tsem_event + * structure and populating the appropriate CELL structure with the + * input characteristics of the event. The dispatch_event() function + * is called to handle the modeling of the event. This function + * returns the permission value that is returned as the result of the + * LSM event handler. + * + * The dispatch_event() calls the tsem_event_init() function that is + * responsible for translating the input parameters into values that + * will be retained for the lifetime of the security event + * description. The populated event description is then dispatched to + * either the tsem_model_event() or the tsem_export_event() for + * modeling by either the internal TMA or by a TMA associated with an + * external trust orchestrator. + */ + +#define LOCK true +#define NOLOCK false + +#include +#include +#include +#include +#include +#include + +#include "tsem.h" +#include "nsmgr.h" + +static const struct lsm_id tsem_lsmid = { + .name = "tsem", + .id = LSM_ID_TSEM +}; + +struct lsm_blob_sizes tsem_blob_sizes __ro_after_init = { + .lbs_task = sizeof(struct tsem_task), + .lbs_inode = sizeof(struct tsem_inode), + .lbs_ipc = sizeof(struct tsem_ipc), + .lbs_xattr_count = 1 +}; + +enum tsem_action_type tsem_root_actions[TSEM_EVENT_CNT] = { + TSEM_ACTION_EPERM /* Undefined. */ +}; + +static atomic64_t task_instance; + +static struct tsem_model root_model = { + .point_lock = __SPIN_LOCK_INITIALIZER(root_model.point_lock), + .point_list = LIST_HEAD_INIT(root_model.point_list), + .point_end_mutex = __MUTEX_INITIALIZER(root_model.point_end_mutex), + + .trajectory_lock = __SPIN_LOCK_INITIALIZER(root_model.trajectory_lock), + .trajectory_list = LIST_HEAD_INIT(root_model.trajectory_list), + .trajectory_end_mutex = __MUTEX_INITIALIZER(root_model.trajectory_end_mutex), + + .forensics_lock = __SPIN_LOCK_INITIALIZER(root_model.forensics_lock), + .forensics_list = LIST_HEAD_INIT(root_model.forensics_list), + .forensics_end_mutex = __MUTEX_INITIALIZER(root_model.forensics_end_mutex), + + .pseudonym_mutex = __MUTEX_INITIALIZER(root_model.pseudonym_mutex), + .pseudonym_list = LIST_HEAD_INIT(root_model.pseudonym_list), + + .mount_mutex = __MUTEX_INITIALIZER(root_model.mount_mutex), + .mount_list = LIST_HEAD_INIT(root_model.mount_list) +}; + +static struct tsem_context root_context; + +DEFINE_STATIC_KEY_TRUE(tsem_not_ready); + +static bool tsem_available __ro_after_init; + +static unsigned int magazine_size __ro_after_init = TSEM_ROOT_MAGAZINE_SIZE; + +static enum mode_type { + FULL_MODELING, + NO_ROOT_MODELING, + ROOT_EXPORT_ONLY +} tsem_mode __ro_after_init; + +static char *default_hash_function __ro_after_init; + +static const struct { + char *key; + enum mode_type mode; +} mode_arguments[] __ro_after_init = { + {"no_root_modeling", NO_ROOT_MODELING}, + {"root_export_only", ROOT_EXPORT_ONLY} +}; + +/* + * NOTE: + * The tsem_event_type enumerations are used to select the + * member of the tsem_names array that is to be used. If the members + * of this array change position, or there are additions or deletions + * to this array, there needs to be coordinated changes made to the + * tsem_event_type enumeration in the tsem.h file. + */ +const char * const tsem_names[TSEM_EVENT_CNT] = { + "undefined", + "bprm_committed_creds", /* TSEM_BPRM_COMMITTED_CREDS */ + "task_kill", /* TSEM_TASK_KILL */ + "task_setpgid", /* TSEM_TASK_SETPGID */ + "task_getpgid", /* TSEM_TASK_GETPGID */ + "task_getsid", /* TSEM_TASK_GETSID */ + "task_setnice", /* TSEM_TASK_SETNICE */ + "task_setioprio", /* TSEM_TASK_SETIOPRIO */ + "task_getioprio", /* TSEM_TASK_GETIOPRIO */ + "task_prlimit", /* TSEM_TASK_PRLIMIT */ + "task_setrlimit", /* TSEM_TASK_SETRLIMIT */ + "task_setscheduler", /* TSEM_TASK_SETSCHEDULER */ + "task_getscheduler", /* TSEM_TASK_GETSCHEDULER */ + "task_prctl", /* TSEM_TASK_PRCTL */ + "file_open", /* TSEM_FILE_OPEN */ + "mmap_file", /* TSEM_MMAP_FILE */ + "file_ioctl", /* TSEM_FILE_IOCTL */ + "file_lock", /* TSEM_FILE_LOCK */ + "file_fcntl", /* TSEM_FILE_FCNTL */ + "file_receive", /* TSEM_FILE_RECEIVE */ + "unix_stream_connect", /* TSEM_UNIX_STREAM_CONNECT */ + "unix_may_send", /* TSEM_UNIX_MAY_SEND */ + "socket_create", /* TSEM_SOCKET_CREATE */ + "socket_connect", /* TSEM_SOCKET_CONNECT */ + "socket_bind", /* TSEM_SOCKET_BIND */ + "socket_accept", /* TSEM_SOCKET_ACCEPT */ + "socket_listen", /* TSEM_SOCKET_LISTEN */ + "socket_socketpair", /* TSEM_SOCKET_SOCKETPAIR */ + "socket_sendmsg", /* TSEM_SOCKET_SENDMSG */ + "socket_recvmsg", /* TSEM_SOCKET_RECVMSG */ + "socket_getsockname", /* TSEM_SOCKET_GETSOCKNAME */ + "socket_getpeername", /* TSEM_SOCKET_GETPEERNAME */ + "socket_setsockopt", /* TSEM_SOCKET_SETSOCKOPT */ + "socket_shutdown", /* TSEM_SOCKET_SHUTDOWN */ + "ptrace_traceme", /* TSEM_PTRACE_TRACEME */ + "kernel_module_request", /* TSEM_KERNEL_MODULE_REQUEST */ + "kernel_load_data", /* TSEM_KERNEL_LOAD_DATA */ + "kernel_read_file", /* TSEM_KERNEL_READ_FILE */ + "sb_mount", /* TSEM_SB_MOUNT */ + "sb_umount", /* TSEM_SB_UMOUNT */ + "sb_remount", /* TSEM_SB_REMOUNT */ + "sb_pivotroot", /* TSEM_SB_PIVOTROOT */ + "sb_statfs", /* TSEM_SB_STATFS */ + "move_mount", /* TSEM_MOVE_MOUNT */ + "shm_associate", /* TSEM_SHM_ASSOCIATE */ + "shm_shmctl", /* TSEM_SHM_SHMCTL */ + "shm_shmat", /* TSEM_SHM_SHMAT */ + "sem_associate", /* TSEM_SEM_ASSOCIATE */ + "sem_semctl", /* TSEM_SEM_SEMCTL */ + "sem_semop", /* TSEM_SEM_SEMOP */ + "syslog", /* TSEM_SYSLOG */ + "settime", /* TSEM_SETTIME */ + "quotactl", /* TSEM_QUOTACTL */ + "quota_on", /* TSEM_QUOTA_ON */ + "msg_queue_associate", /* TSEM_MSG_QUEUE_ASSOCIATE */ + "msg_queue_msgctl", /* TSEM_MSG_QUEUE_MSGCTL */ + "msg_queue_msgsnd", /* TSEM_MSG_QUEUE_MSGSND */ + "msg_queue_msgrcv", /* TSEM_MSG_QUEUE_MSGRCV */ + "ipc_permission", /* TSEM_IPC_PERMISSION */ + "key_alloc", /* TSEM_KEY_ALLOC */ + "key_permission", /* TSEM_KEY_PERMISSION */ + "netlink_send", /* TSEM_NETLINK_SEND */ + "inode_create", /* TSEM_INODE_CREATE */ + "inode_link", /* TSEM_INODE_LINK */ + "inode_unlink", /* TSEM_INODE_UNLINK */ + "inode_symlink", /* TSEM_INODE_SYMLINK */ + "inode_mkdir", /* TSEM_INODE_MKDIR */ + "inode_rmdir", /* TSEM_INODE_RMDIR */ + "inode_mknod", /* TSEM_INODE_MKNOD */ + "inode_rename", /* TSEM_INODE_RENAME */ + "inode_setattr", /* TSEM_INODE_SETATTR */ + "inode_getattr", /* TSEM_INODE_GETATTR */ + "inode_setxattr", /* TSEM_INODE_SETXATTR */ + "inode_getxattr", /* TSEM_INODE_GETXATTR */ + "inode_listxattr", /* TSEM_INODE_LISTXATTR */ + "inode_removexattr", /* TSEM_INODE_REMOVEXATTR */ + "inode_killpriv", /* TSEM_INODE_KILLPRIV */ + "tun_dev_create", /* TSEM_TUN_DEV_CREATE */ + "tun_dev_attach_queue", /* TSEM_TUN_DEV_ATTACH_QUEUE */ + "tun_dev_attach", /* TSEM_TUN_DEV_ATTACH */ + "tun_dev_open", /* TSEM_TUN_DEV_OPEN */ + "bpf", /* TSEM_BPF */ + "bpf_map", /* TSEM_BPF_MAP */ + "bpf_prog", /* TSEM_BPF_PROG */ + "ptrace_access_check", /* TSEM_PTRACE_ACCESS_CHECK */ + "capable", /* TSEM_CAPABLE */ + "capget", /* TSEM_CAPGET */ + "capset" /* TSEM_CAPSET */ +}; + +static const unsigned long pseudo_filesystems[] = { + PROC_SUPER_MAGIC, + SYSFS_MAGIC, + DEBUGFS_MAGIC, + TMPFS_MAGIC, + DEVPTS_SUPER_MAGIC, + BINFMTFS_MAGIC, + SECURITYFS_MAGIC, + SELINUX_MAGIC, + SMACK_MAGIC, + CGROUP_SUPER_MAGIC, + CGROUP2_SUPER_MAGIC, + NSFS_MAGIC, + EFIVARFS_MAGIC +}; + +static int __init set_magazine_size(char *magazine_value) +{ + if (kstrtouint(magazine_value, 0, &magazine_size)) + pr_warn("tsem: Failed to parse root cache size.\n"); + + if (!magazine_size) { + pr_warn("tsem: Forcing non-zero cache size.\n"); + magazine_size = TSEM_ROOT_MAGAZINE_SIZE; + } + + pr_info("tsem: Setting default root cache size to %u.\n", + magazine_size); + return 1; +} +__setup("tsem_cache=", set_magazine_size); + +static int __init set_modeling_mode(char *mode_argument) +{ + unsigned int lp; + + for (lp = 0; lp < ARRAY_SIZE(mode_arguments); ++lp) { + if (!strcmp(mode_argument, mode_arguments[lp].key)) { + tsem_mode = mode_arguments[lp].mode; + return 1; + } + } + + pr_warn("tsem: Unknown tsem_mode %s specified, using full modeling.\n", + mode_argument); + return 1; +} +__setup("tsem_mode=", set_modeling_mode); + +static int __init set_default_hash_function(char *hash_function) +{ + + default_hash_function = hash_function; + return 1; +} +__setup("tsem_digest=", set_default_hash_function); + +static int __init set_locked_status(char *str) +{ + tsem_nsmgr_lock(true); + pr_info("tsem: Model state locked by command-line request.\n"); + return 1; +} +__setup("tsem_locked", set_locked_status); + +static bool bypass_event(const enum tsem_event_type event) +{ + if (tsem_context(current)->ops->bypasses[event]) + return true; + if (tsem_mode == NO_ROOT_MODELING && !tsem_context(current)->id) + return true; + return false; +} + +static bool pseudo_filesystem(struct inode *inode) +{ + unsigned int lp; + + for (lp = 0; lp < ARRAY_SIZE(pseudo_filesystems); ++lp) + if (inode->i_sb->s_magic == pseudo_filesystems[lp]) + return true; + return false; +} + +static int untrusted_task(struct tsem_event *ep) +{ + int retn = 0; + struct tsem_context *ctx = tsem_context(current); + + if (ctx->external) { + retn = tsem_export_action(ep->event, ep->locked); + if (retn) + return retn; + } else + pr_warn("Untrusted event %s: model_ns=%lld, comm=%s, pid=%d\n", + tsem_names[ep->event], ctx->id, current->comm, + task_pid_nr(current)); + + if (ctx->actions[ep->event] == TSEM_ACTION_EPERM) + retn = -EPERM; + return retn; +} + +static int dispatch_event(struct tsem_event *ep) +{ + int retn; + + if (unlikely(tsem_task_untrusted(current))) { + retn = untrusted_task(ep); + goto done; + } + + retn = tsem_event_init(ep); + if (retn > 0) { + if (!tsem_context(current)->external) + retn = tsem_model_event(ep); + else + retn = tsem_export_event(ep); + } + + done: + tsem_event_put(ep); + return retn; +} + +static int tsem_file_open(struct file *file) +{ + struct inode *inode = file_inode(file); + struct tsem_event *ep; + + if (static_branch_unlikely(&tsem_not_ready)) + return 0; + if (bypass_event(TSEM_FILE_OPEN)) + return 0; + if (unlikely(tsem_inode(inode)->status == TSEM_INODE_CONTROL_PLANE)) { + if (capable(CAP_MAC_ADMIN)) + return 0; + else + return -EPERM; + } + + if (!S_ISREG(inode->i_mode)) + return 0; + if (tsem_inode(inode)->status == TSEM_INODE_COLLECTING) + return 0; + + ep = tsem_event_allocate(TSEM_FILE_OPEN, NOLOCK); + if (!ep) + return -ENOMEM; + + ep->CELL.file.in.file = file; + ep->CELL.file.in.pseudo_file = pseudo_filesystem(inode); + + return dispatch_event(ep); +} + +static int tsem_mmap_file(struct file *file, unsigned long prot, + unsigned long flags, unsigned long extra) +{ + struct inode *inode = NULL; + struct tsem_event *ep; + + if (static_branch_unlikely(&tsem_not_ready)) + return 0; + if (bypass_event(TSEM_MMAP_FILE)) + return 0; + + if (!file && !(prot & PROT_EXEC)) + return 0; + if (file) { + inode = file_inode(file); + if (!S_ISREG(inode->i_mode)) + return 0; + if (pseudo_filesystem(inode)) + return 0; + } + + ep = tsem_event_allocate(TSEM_MMAP_FILE, NOLOCK); + if (!ep) + return -ENOMEM; + + ep->CELL.mmap_file.anonymous = file == NULL ? 1 : 0; + ep->CELL.mmap_file.file.in.file = file; + ep->CELL.mmap_file.prot = prot; + ep->CELL.mmap_file.flags = flags; + + return dispatch_event(ep); +} + +static int tsem_file_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct tsem_event *ep; + + if (bypass_event(TSEM_FILE_IOCTL)) + return 0; + + ep = tsem_event_allocate(TSEM_FILE_IOCTL, NOLOCK); + if (!ep) + return -ENOMEM; + + ep->CELL.file.cmd = cmd; + ep->CELL.file.in.file = file; + ep->CELL.file.in.pseudo_file = pseudo_filesystem(file_inode(file)); + + return dispatch_event(ep); +} + +static int tsem_file_lock(struct file *file, unsigned int cmd) +{ + struct tsem_event *ep; + + if (bypass_event(TSEM_FILE_LOCK)) + return 0; + + ep = tsem_event_allocate(TSEM_FILE_LOCK, NOLOCK); + if (!ep) + return -ENOMEM; + + ep->CELL.file.cmd = cmd; + ep->CELL.file.in.file = file; + ep->CELL.file.in.pseudo_file = pseudo_filesystem(file_inode(file)); + + return dispatch_event(ep); +} + +static int tsem_file_fcntl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct tsem_event *ep; + + if (static_branch_unlikely(&tsem_not_ready)) + return 0; + if (bypass_event(TSEM_FILE_FCNTL)) + return 0; + + ep = tsem_event_allocate(TSEM_FILE_FCNTL, NOLOCK); + if (!ep) + return -ENOMEM; + + ep->CELL.file.cmd = cmd; + ep->CELL.file.in.file = file; + ep->CELL.file.in.pseudo_file = pseudo_filesystem(file_inode(file)); + + return dispatch_event(ep); +} + +static int tsem_file_receive(struct file *file) +{ + struct tsem_event *ep; + + if (bypass_event(TSEM_FILE_RECEIVE)) + return 0; + + ep = tsem_event_allocate(TSEM_FILE_RECEIVE, NOLOCK); + if (!ep) + return -ENOMEM; + + ep->CELL.file.in.file = file; + ep->CELL.file.in.pseudo_file = pseudo_filesystem(file_inode(file)); + + return dispatch_event(ep); +} + +static int tsem_task_alloc(struct task_struct *new, unsigned long flags) +{ + struct tsem_task *old_task = tsem_task(current); + struct tsem_task *new_task = tsem_task(new); + + new_task->instance = old_task->instance; + new_task->p_instance = old_task->instance; + + new_task->trust_status = old_task->trust_status; + new_task->context = old_task->context; + memcpy(new_task->task_id, old_task->task_id, HASH_MAX_DIGESTSIZE); + memcpy(new_task->p_task_id, old_task->task_id, HASH_MAX_DIGESTSIZE); + + if (!new_task->context->id) + return 0; + + kref_get(&new_task->context->kref); + memcpy(new_task->task_key, old_task->task_key, HASH_MAX_DIGESTSIZE); + return 0; +} + +static void tsem_task_free(struct task_struct *task) +{ + struct tsem_context *ctx = tsem_context(task); + + if (ctx->id) + tsem_ns_put(ctx); +} + +static int tsem_task_kill(struct task_struct *target, + struct kernel_siginfo *info, int sig, + const struct cred *cred) +{ + bool cross_model; + struct tsem_event *ep; + struct tsem_context *src_ctx = tsem_context(current); + struct tsem_context *tgt_ctx = tsem_context(target); + + if (bypass_event(TSEM_TASK_KILL)) + return 0; + + cross_model = src_ctx->id != tgt_ctx->id; + + if (info != SEND_SIG_NOINFO && SI_FROMKERNEL(info)) + return 0; + if (sig == SIGURG) + return 0; + if (!capable(CAP_MAC_ADMIN) && + has_capability_noaudit(target, CAP_MAC_ADMIN)) + return -EPERM; + if (!capable(CAP_MAC_ADMIN) && cross_model) + return -EPERM; + + ep = tsem_event_allocate(TSEM_TASK_KILL, LOCK); + if (!ep) + return -ENOMEM; + + ep->CELL.task_kill.signal = sig; + ep->CELL.task_kill.cross_model = cross_model; + memcpy(ep->CELL.task_kill.target, tsem_task(target)->task_id, + tsem_digestsize()); + + return dispatch_event(ep); +} + +static int tsem_ptrace_access_check(struct task_struct *child, + unsigned int mode) +{ + struct tsem_event *ep; + + if (bypass_event(TSEM_PTRACE_ACCESS_CHECK)) + return 0; + + ep = tsem_event_allocate(TSEM_PTRACE_ACCESS_CHECK, LOCK); + if (!ep) + return -ENOMEM; + + ep->CELL.task_kill.u.resource = mode; + memcpy(ep->CELL.task_kill.target, tsem_task(child)->task_id, + tsem_digestsize()); + + return dispatch_event(ep); +} + +static int tsem_ptrace_traceme(struct task_struct *parent) +{ + struct tsem_event *ep; + + if (bypass_event(TSEM_PTRACE_TRACEME)) + return 0; + + ep = tsem_event_allocate(TSEM_PTRACE_TRACEME, LOCK); + if (!ep) + return -ENOMEM; + + memcpy(ep->CELL.task_kill.source, tsem_task(parent)->task_id, + tsem_digestsize()); + + return dispatch_event(ep); +} + +static int tsem_capget(const struct task_struct *target, + kernel_cap_t *effective, kernel_cap_t *inheritable, + kernel_cap_t *permitted) +{ + struct tsem_event *ep; + + if (bypass_event(TSEM_CAPGET)) + return 0; + + ep = tsem_event_allocate(TSEM_CAPGET, LOCK); + if (!ep) + return -ENOMEM; + + ep->CELL.capability.effective = *effective; + ep->CELL.capability.inheritable = *inheritable; + ep->CELL.capability.permitted = *permitted; + memcpy(ep->CELL.capability.target, tsem_task(target)->task_id, + tsem_digestsize()); + + return dispatch_event(ep); +} + +static int tsem_capset(struct cred *new, const struct cred *old, + const kernel_cap_t *effective, + const kernel_cap_t *inheritable, + const kernel_cap_t *permitted) +{ + struct tsem_event *ep; + + if (bypass_event(TSEM_CAPSET)) + return 0; + + ep = tsem_event_allocate(TSEM_CAPSET, LOCK); + if (!ep) + return -ENOMEM; + + ep->CELL.capability.effective = *effective; + ep->CELL.capability.inheritable = *inheritable; + ep->CELL.capability.permitted = *permitted; + + return dispatch_event(ep); +} + +static int tsem_capable(const struct cred *cred, struct user_namespace *ns, + int cap, unsigned int opts) +{ + struct tsem_event *ep; + + if (static_branch_unlikely(&tsem_not_ready)) + return 0; + if (bypass_event(TSEM_CAPABLE)) + return 0; + + ep = tsem_event_allocate(TSEM_CAPABLE, LOCK); + if (!ep) + return -ENOMEM; + + ep->CELL.capability.cap = cap; + ep->CELL.capability.opts = opts; + + return dispatch_event(ep); +} + +static int tsem_task_setpgid(struct task_struct *p, pid_t pgid) +{ + struct tsem_event *ep; + struct task_struct *src; + + if (bypass_event(TSEM_TASK_SETPGID)) + return 0; + + ep = tsem_event_allocate(TSEM_TASK_SETPGID, LOCK); + if (!ep) + return -ENOMEM; + + memcpy(ep->CELL.task_kill.target, tsem_task(p)->task_id, + tsem_digestsize()); + + if (!pgid) + memcpy(ep->CELL.task_kill.source, tsem_task(p)->task_id, + tsem_digestsize()); + else { + rcu_read_lock(); + src = find_task_by_vpid(pgid); + rcu_read_unlock(); + if (src) + memcpy(ep->CELL.task_kill.source, + tsem_task(src)->task_id, tsem_digestsize()); + } + + return dispatch_event(ep); +} + +static int tsem_task_getpgid(struct task_struct *p) +{ + struct tsem_event *ep; + + if (bypass_event(TSEM_TASK_GETPGID)) + return 0; + + ep = tsem_event_allocate(TSEM_TASK_GETPGID, LOCK); + if (!ep) + return -ENOMEM; + + memcpy(ep->CELL.task_kill.target, tsem_task(p)->task_id, + tsem_digestsize()); + + return dispatch_event(ep); +} + +static int tsem_task_getsid(struct task_struct *p) +{ + struct tsem_event *ep; + + if (bypass_event(TSEM_TASK_GETSID)) + return 0; + + ep = tsem_event_allocate(TSEM_TASK_GETSID, LOCK); + if (!ep) + return -ENOMEM; + + memcpy(ep->CELL.task_kill.target, tsem_task(p)->task_id, + tsem_digestsize()); + + return dispatch_event(ep); +} + +static int tsem_task_setnice(struct task_struct *p, int nice) +{ + struct tsem_event *ep; + + if (bypass_event(TSEM_TASK_SETNICE)) + return 0; + + ep = tsem_event_allocate(TSEM_TASK_SETNICE, LOCK); + if (!ep) + return -ENOMEM; + + ep->CELL.task_kill.u.value = nice; + memcpy(ep->CELL.task_kill.target, tsem_task(p)->task_id, + tsem_digestsize()); + + return dispatch_event(ep); +} + +static int tsem_task_setioprio(struct task_struct *p, int ioprio) +{ + struct tsem_event *ep; + + if (bypass_event(TSEM_TASK_SETIOPRIO)) + return 0; + + ep = tsem_event_allocate(TSEM_TASK_SETIOPRIO, NOLOCK); + if (!ep) + return -ENOMEM; + + ep->CELL.task_kill.u.value = ioprio; + memcpy(ep->CELL.task_kill.target, tsem_task(p)->task_id, + tsem_digestsize()); + + return dispatch_event(ep); +} + +static int tsem_task_getioprio(struct task_struct *p) +{ + struct tsem_event *ep; + + if (bypass_event(TSEM_TASK_GETIOPRIO)) + return 0; + + ep = tsem_event_allocate(TSEM_TASK_GETIOPRIO, NOLOCK); + if (!ep) + return -ENOMEM; + + memcpy(ep->CELL.task_kill.target, tsem_task(p)->task_id, + tsem_digestsize()); + + return dispatch_event(ep); +} + +static int tsem_task_prlimit(const struct cred *cred, const struct cred *tcred, + unsigned int flags) +{ + struct tsem_event *ep; + + if (bypass_event(TSEM_TASK_PRLIMIT)) + return 0; + + ep = tsem_event_allocate(TSEM_TASK_PRLIMIT, LOCK); + if (!ep) + return -ENOMEM; + + ep->CELL.task_prlimit.flags = flags; + ep->CELL.task_prlimit.in.cred = cred; + ep->CELL.task_prlimit.in.tcred = tcred; + + return dispatch_event(ep); +} + +static int tsem_task_setrlimit(struct task_struct *p, unsigned int resource, + struct rlimit *new_rlim) +{ + struct tsem_event *ep; + + if (bypass_event(TSEM_TASK_SETRLIMIT)) + return 0; + + ep = tsem_event_allocate(TSEM_TASK_SETRLIMIT, LOCK); + if (!ep) + return -ENOMEM; + + ep->CELL.task_kill.u.resource = resource; + ep->CELL.task_kill.cur = new_rlim->rlim_cur; + ep->CELL.task_kill.max = new_rlim->rlim_max; + memcpy(ep->CELL.task_kill.target, tsem_task(p)->task_id, + tsem_digestsize()); + + return dispatch_event(ep); +} + +static int tsem_task_setscheduler(struct task_struct *p) +{ + struct tsem_event *ep; + + if (bypass_event(TSEM_TASK_SETSCHEDULER)) + return 0; + + ep = tsem_event_allocate(TSEM_TASK_SETSCHEDULER, LOCK); + if (!ep) + return -ENOMEM; + + memcpy(ep->CELL.task_kill.target, tsem_task(p)->task_id, + tsem_digestsize()); + + return dispatch_event(ep); +} + +static int tsem_task_getscheduler(struct task_struct *p) +{ + struct tsem_event *ep; + + if (bypass_event(TSEM_TASK_GETSCHEDULER)) + return 0; + + ep = tsem_event_allocate(TSEM_TASK_GETSCHEDULER, LOCK); + if (!ep) + return -ENOMEM; + + memcpy(ep->CELL.task_kill.target, tsem_task(p)->task_id, + tsem_digestsize()); + + return dispatch_event(ep); +} + +static int tsem_task_prctl(int option, unsigned long arg2, unsigned long arg3, + unsigned long arg4, unsigned long arg5) +{ + struct tsem_event *ep; + + if (bypass_event(TSEM_TASK_PRCTL)) + return 0; + + ep = tsem_event_allocate(TSEM_TASK_PRCTL, LOCK); + if (!ep) + return -ENOMEM; + + ep->CELL.task_prctl.option = option; + ep->CELL.task_prctl.arg2 = arg2; + ep->CELL.task_prctl.arg3 = arg3; + ep->CELL.task_prctl.arg4 = arg4; + ep->CELL.task_prctl.arg5 = arg5; + + return dispatch_event(ep); +} + +static void tsem_bprm_committed_creds(const struct linux_binprm *bprm) +{ + u8 task_id[HASH_MAX_DIGESTSIZE]; + + if (static_branch_unlikely(&tsem_not_ready)) + return; + + if (tsem_map_task(bprm->file, task_id)) + memset(task_id, 0xff, sizeof(task_id)); + + tsem_task(current)->instance = atomic64_inc_return(&task_instance); + memcpy(tsem_task(current)->task_id, task_id, tsem_digestsize()); +} + +static int tsem_inode_alloc_security(struct inode *inode) +{ + struct tsem_inode *tsip = tsem_inode(inode); + + mutex_init(&tsip->digest_mutex); + INIT_LIST_HEAD(&tsip->digest_list); + + mutex_init(&tsip->create_mutex); + INIT_LIST_HEAD(&tsip->create_list); + + mutex_init(&tsip->instance_mutex); + INIT_LIST_HEAD(&tsip->instance_list); + + return 0; +} + +static int tsem_inode_init_security(struct inode *inode, struct inode *dir, + const struct qstr *qstr, + struct xattr *xattrs, int *xattr_count) +{ + u8 *owner = tsem_task(current)->task_id; + struct tsem_inode *tsip = tsem_inode(inode); + struct tsem_inode_instance *entry, *retn = NULL; + + mutex_lock(&tsem_inode(dir)->create_mutex); + list_for_each_entry(entry, &tsem_inode(dir)->create_list, list) { + if (!memcmp(entry->owner, owner, tsem_digestsize()) && + !strcmp(qstr->name, entry->pathname)) { + retn = entry; + break; + } + } + + if (retn) { + tsip->created = true; + tsip->creator = retn->creator; + tsip->instance = retn->instance; + memcpy(tsip->owner, retn->owner, tsem_digestsize()); + list_del(&retn->list); + } + mutex_unlock(&tsem_inode(dir)->create_mutex); + + return -EOPNOTSUPP; +} + +static void _release_inode_instances(u64 id, struct tsem_inode *tsip) +{ + struct tsem_inode_instance *owner, *tmp_owner; + + mutex_lock(&tsip->instance_mutex); + list_for_each_entry_safe(owner, tmp_owner, &tsip->instance_list, + list) { + if (id == owner->creator) { + list_del(&owner->list); + kfree(owner); + } + } + mutex_unlock(&tsip->instance_mutex); +} + +static void tsem_inode_free_security(struct inode *inode) +{ + struct tsem_inode_instance *owner, *tmp_owner; + struct tsem_inode_digest *digest, *tmp_digest; + struct tsem_inode_entry *entry, *tmp_entry; + struct tsem_context *ctx = tsem_context(current); + + mutex_lock(&ctx->inode_mutex); + list_for_each_entry_safe(entry, tmp_entry, &ctx->inode_list, list) { + if (entry->tsip == tsem_inode(inode)) { + list_del(&entry->list); + _release_inode_instances(ctx->id, entry->tsip); + kfree(entry); + } + } + mutex_unlock(&ctx->inode_mutex); + + list_for_each_entry_safe(digest, tmp_digest, + &tsem_inode(inode)->digest_list, list) { + list_del(&digest->list); + kfree(digest->name); + kfree(digest); + } + + list_for_each_entry_safe(owner, tmp_owner, + &tsem_inode(inode)->create_list, list) { + list_del(&owner->list); + kfree(owner); + } + + list_for_each_entry_safe(owner, tmp_owner, + &tsem_inode(inode)->instance_list, list) { + list_del(&owner->list); + kfree(owner); + } +} + +static int tsem_unix_stream_connect(struct sock *sock, struct sock *other, + struct sock *newsk) +{ + struct tsem_event *ep; + + if (bypass_event(TSEM_UNIX_STREAM_CONNECT)) + return 0; + + ep = tsem_event_allocate(TSEM_UNIX_STREAM_CONNECT, LOCK); + if (!ep) + return -ENOMEM; + + ep->CELL.socket.in.socka = sock; + ep->CELL.socket.in.sockb = other; + + return dispatch_event(ep); +} + +static int tsem_unix_may_send(struct socket *sock, struct socket *other) +{ + struct tsem_event *ep; + + if (bypass_event(TSEM_UNIX_MAY_SEND)) + return 0; + + ep = tsem_event_allocate(TSEM_UNIX_MAY_SEND, LOCK); + if (!ep) + return -ENOMEM; + + ep->CELL.socket.in.socka = sock->sk; + ep->CELL.socket.in.sockb = other->sk; + + return dispatch_event(ep); +} + +static int tsem_socket_post_create(struct socket *sock, int family, int type, + int protocol, int kern) +{ + struct tsem_inode *tsip = tsem_inode(SOCK_INODE(sock)); + + if (static_branch_unlikely(&tsem_not_ready)) + return 0; + + memcpy(tsip->owner, tsem_task(current)->task_id, tsem_digestsize()); + return 0; +} + +static int tsem_socket_create(int family, int type, int protocol, int kern) +{ + struct tsem_event *ep; + + if (static_branch_unlikely(&tsem_not_ready)) + return 0; + if (bypass_event(TSEM_SOCKET_CREATE)) + return 0; + + ep = tsem_event_allocate(TSEM_SOCKET_CREATE, NOLOCK); + if (!ep) + return -ENOMEM; + + ep->CELL.socket.out.socka.family = family; + ep->CELL.socket.out.socka.type = type; + ep->CELL.socket.out.socka.protocol = protocol; + ep->CELL.socket.out.socka.kern = kern; + + return dispatch_event(ep); +} + +static int tsem_socket_connect(struct socket *sock, struct sockaddr *addr, + int addr_len) +{ + struct tsem_event *ep; + + if (bypass_event(TSEM_SOCKET_CONNECT)) + return 0; + + ep = tsem_event_allocate(TSEM_SOCKET_CONNECT, NOLOCK); + if (!ep) + return -ENOMEM; + + ep->CELL.socket.in.socka = sock->sk; + ep->CELL.socket.in.addr = addr; + ep->CELL.socket.value = addr_len; + + return dispatch_event(ep); +} + +static int tsem_socket_bind(struct socket *sock, struct sockaddr *addr, + int addr_len) + +{ + struct tsem_event *ep; + + if (bypass_event(TSEM_SOCKET_BIND)) + return 0; + + ep = tsem_event_allocate(TSEM_SOCKET_BIND, NOLOCK); + if (!ep) + return -ENOMEM; + + ep->CELL.socket.in.socka = sock->sk; + ep->CELL.socket.in.addr = addr; + ep->CELL.socket.value = addr_len; + + return dispatch_event(ep); +} + +static int tsem_socket_accept(struct socket *sock, struct socket *newsock) +{ + struct tsem_event *ep; + + if (bypass_event(TSEM_SOCKET_ACCEPT)) + return 0; + + ep = tsem_event_allocate(TSEM_SOCKET_ACCEPT, NOLOCK); + if (!ep) + return -ENOMEM; + + ep->CELL.socket.in.socka = sock->sk; + + return dispatch_event(ep); +} + +static int tsem_socket_listen(struct socket *sock, int backlog) + +{ + struct sock *sk = sock->sk; + struct tsem_event *ep; + + if (bypass_event(TSEM_SOCKET_LISTEN)) + return 0; + + ep = tsem_event_allocate(TSEM_SOCKET_LISTEN, NOLOCK); + if (!ep) + return -ENOMEM; + + ep->CELL.socket.value = backlog; + ep->CELL.socket.in.socka = sk; + + return dispatch_event(ep); +} + +static int tsem_socket_socketpair(struct socket *socka, struct socket *sockb) +{ + struct sock *ska = socka->sk, *skb = sockb->sk; + struct tsem_event *ep; + + if (bypass_event(TSEM_SOCKET_SOCKETPAIR)) + return 0; + + ep = tsem_event_allocate(TSEM_SOCKET_SOCKETPAIR, NOLOCK); + if (!ep) + return -ENOMEM; + + ep->CELL.socket.in.socka = ska; + ep->CELL.socket.in.sockb = skb; + + return dispatch_event(ep); +} + +static int tsem_socket_sendmsg(struct socket *sock, struct msghdr *msgmsg, + int size) +{ + struct sock *sk = sock->sk; + struct tsem_event *ep; + + if (bypass_event(TSEM_SOCKET_SENDMSG)) + return 0; + + ep = tsem_event_allocate(TSEM_SOCKET_SENDMSG, NOLOCK); + if (!ep) + return -ENOMEM; + + ep->CELL.socket.in.socka = sk; + ep->CELL.socket.in.addr = msgmsg->msg_name; + + return dispatch_event(ep); +} + +static int tsem_socket_recvmsg(struct socket *sock, struct msghdr *msgmsg, + int size, int flags) +{ + struct sock *sk = sock->sk; + struct tsem_event *ep; + + if (bypass_event(TSEM_SOCKET_RECVMSG)) + return 0; + + ep = tsem_event_allocate(TSEM_SOCKET_RECVMSG, NOLOCK); + if (!ep) + return -ENOMEM; + + ep->CELL.socket.in.socka = sk; + if (msgmsg->msg_name && msgmsg->msg_namelen > 0) + ep->CELL.socket.in.addr = msgmsg->msg_name; + + return dispatch_event(ep); +} + +static int tsem_socket_getsockname(struct socket *sock) +{ + struct sock *sk = sock->sk; + struct tsem_event *ep; + + if (bypass_event(TSEM_SOCKET_GETSOCKNAME)) + return 0; + + ep = tsem_event_allocate(TSEM_SOCKET_GETSOCKNAME, NOLOCK); + if (!ep) + return -ENOMEM; + + ep->CELL.socket.in.socka = sk; + + return dispatch_event(ep); +} + +static int tsem_socket_getpeername(struct socket *sock) +{ + struct sock *sk = sock->sk; + struct tsem_event *ep; + + if (bypass_event(TSEM_SOCKET_GETPEERNAME)) + return 0; + + ep = tsem_event_allocate(TSEM_SOCKET_GETPEERNAME, NOLOCK); + if (!ep) + return -ENOMEM; + + ep->CELL.socket.in.socka = sk; + + return dispatch_event(ep); +} + +static int tsem_socket_setsockopt(struct socket *sock, int level, int optname) +{ + struct sock *sk = sock->sk; + struct tsem_event *ep; + + if (bypass_event(TSEM_SOCKET_SETSOCKOPT)) + return 0; + + ep = tsem_event_allocate(TSEM_SOCKET_SETSOCKOPT, NOLOCK); + if (!ep) + return -ENOMEM; + + ep->CELL.socket.value = level; + ep->CELL.socket.optname = optname; + ep->CELL.socket.in.socka = sk; + + return dispatch_event(ep); +} + +static int tsem_socket_shutdown(struct socket *sock, int how) +{ + struct sock *sk = sock->sk; + struct tsem_event *ep; + + if (bypass_event(TSEM_SOCKET_SHUTDOWN)) + return 0; + + ep = tsem_event_allocate(TSEM_SOCKET_SHUTDOWN, NOLOCK); + if (!ep) + return -ENOMEM; + + ep->CELL.socket.value = how; + ep->CELL.socket.in.socka = sk; + + return dispatch_event(ep); +} + +static int tsem_kernel_module_request(char *kmod_name) +{ + struct tsem_event *ep; + + if (static_branch_unlikely(&tsem_not_ready)) + return 0; + if (bypass_event(TSEM_KERNEL_MODULE_REQUEST)) + return 0; + + ep = tsem_event_allocate(TSEM_KERNEL_MODULE_REQUEST, NOLOCK); + if (!ep) + return -ENOMEM; + + ep->CELL.kernel.in.kmod_name = kmod_name; + + return dispatch_event(ep); +} + +static int tsem_kernel_load_data(enum kernel_load_data_id id, bool contents) +{ + struct tsem_event *ep; + + if (bypass_event(TSEM_KERNEL_LOAD_DATA)) + return 0; + + ep = tsem_event_allocate(TSEM_KERNEL_LOAD_DATA, NOLOCK); + if (!ep) + return -ENOMEM; + + ep->CELL.kernel.id = id; + ep->CELL.kernel.contents = contents; + + return dispatch_event(ep); +} + + +static int tsem_kernel_read_file(struct file *file, + enum kernel_read_file_id id, bool contents) +{ + struct tsem_event *ep; + + if (bypass_event(TSEM_KERNEL_READ_FILE)) + return 0; + + ep = tsem_event_allocate(TSEM_KERNEL_READ_FILE, NOLOCK); + if (!ep) + return -ENOMEM; + + ep->CELL.kernel.id = id; + ep->CELL.kernel.contents = contents; + ep->CELL.kernel.in.file = file; + + return dispatch_event(ep); +} + +static int tsem_sb_mount(const char *dev_name, const struct path *path, + const char *type, unsigned long flags, void *data) +{ + struct tsem_event *ep; + + if (static_branch_unlikely(&tsem_not_ready)) + return 0; + if (bypass_event(TSEM_SB_MOUNT)) + return 0; + + ep = tsem_event_allocate(TSEM_SB_MOUNT, NOLOCK); + if (!ep) + return -ENOMEM; + + ep->CELL.sb.flags = flags; + ep->CELL.sb.in.dev_name = dev_name; + ep->CELL.sb.in.path = path; + ep->CELL.sb.in.type = type; + + return dispatch_event(ep); +} + +static int tsem_sb_umount(struct vfsmount *mnt, int flags) +{ + struct tsem_event *ep; + + if (bypass_event(TSEM_SB_UMOUNT)) + return 0; + + ep = tsem_event_allocate(TSEM_SB_UMOUNT, NOLOCK); + if (!ep) + return -ENOMEM; + + ep->CELL.sb.flags = flags; + ep->CELL.sb.in.dentry = mnt->mnt_root; + + return dispatch_event(ep); +} + +static int tsem_sb_remount(struct super_block *sb, void *mnt_opts) +{ + struct tsem_event *ep; + + if (static_branch_unlikely(&tsem_not_ready)) + return 0; + if (bypass_event(TSEM_SB_REMOUNT)) + return 0; + + ep = tsem_event_allocate(TSEM_SB_REMOUNT, NOLOCK); + if (!ep) + return -ENOMEM; + + ep->CELL.sb.in.sb = sb; + + return dispatch_event(ep); +} + +static int tsem_sb_pivotroot(const struct path *old_path, + const struct path *new_path) +{ + struct tsem_event *ep; + + if (bypass_event(TSEM_SB_PIVOTROOT)) + return 0; + + ep = tsem_event_allocate(TSEM_SB_PIVOTROOT, NOLOCK); + if (!ep) + return -ENOMEM; + + ep->CELL.sb.in.path = old_path; + ep->CELL.sb.in.path2 = new_path; + + return dispatch_event(ep); +} + +static int tsem_sb_statfs(struct dentry *dentry) +{ + struct tsem_event *ep; + + if (bypass_event(TSEM_SB_STATFS)) + return 0; + + ep = tsem_event_allocate(TSEM_SB_STATFS, NOLOCK); + if (!ep) + return -ENOMEM; + + ep->CELL.sb.in.dentry = dentry; + + return dispatch_event(ep); +} + +static int tsem_move_mount(const struct path *from_path, + const struct path *to_path) +{ + struct tsem_event *ep; + + if (bypass_event(TSEM_MOVE_MOUNT)) + return 0; + + ep = tsem_event_allocate(TSEM_MOVE_MOUNT, NOLOCK); + if (!ep) + return -ENOMEM; + + ep->CELL.sb.in.path = from_path; + ep->CELL.sb.in.path2 = to_path; + + return dispatch_event(ep); +} + +static int tsem_shm_associate(struct kern_ipc_perm *perm, int shmflg) +{ + struct tsem_event *ep; + + if (bypass_event(TSEM_SHM_ASSOCIATE)) + return 0; + + ep = tsem_event_allocate(TSEM_SHM_ASSOCIATE, LOCK); + if (!ep) + return -ENOMEM; + + ep->CELL.ipc.in.perm = perm; + ep->CELL.ipc.value = shmflg; + + return dispatch_event(ep); +} + +static int tsem_shm_shmctl(struct kern_ipc_perm *perm, int cmd) +{ + struct tsem_event *ep; + + if (bypass_event(TSEM_SHM_SHMCTL)) + return 0; + + ep = tsem_event_allocate(TSEM_SHM_SHMCTL, LOCK); + if (!ep) + return -ENOMEM; + + ep->CELL.ipc.in.perm = perm; + ep->CELL.ipc.value = cmd; + + return dispatch_event(ep); +} + +static int tsem_shm_shmat(struct kern_ipc_perm *perm, char __user *shmaddr, + int shmflg) +{ + struct tsem_event *ep; + + if (bypass_event(TSEM_SHM_SHMAT)) + return 0; + + ep = tsem_event_allocate(TSEM_SHM_SHMAT, LOCK); + if (!ep) + return -ENOMEM; + + ep->CELL.ipc.in.perm = perm; + ep->CELL.ipc.value = shmflg; + + return dispatch_event(ep); +} + +static int tsem_sem_associate(struct kern_ipc_perm *perm, int semflg) +{ + struct tsem_event *ep; + + if (bypass_event(TSEM_SEM_ASSOCIATE)) + return 0; + + ep = tsem_event_allocate(TSEM_SEM_ASSOCIATE, LOCK); + if (!ep) + return -ENOMEM; + + ep->CELL.ipc.in.perm = perm; + ep->CELL.ipc.value = semflg; + + return dispatch_event(ep); +} + +static int tsem_sem_semctl(struct kern_ipc_perm *perm, int cmd) +{ + struct tsem_event *ep; + + if (bypass_event(TSEM_SEM_SEMCTL)) + return 0; + + ep = tsem_event_allocate(TSEM_SEM_SEMCTL, LOCK); + if (!ep) + return -ENOMEM; + + ep->CELL.ipc.in.perm = perm; + ep->CELL.ipc.value = cmd; + + return dispatch_event(ep); +} + +static int tsem_sem_semop(struct kern_ipc_perm *perm, struct sembuf *sops, + unsigned int nsops, int alter) +{ + struct tsem_event *ep; + + if (bypass_event(TSEM_SEM_SEMOP)) + return 0; + + ep = tsem_event_allocate(TSEM_SEM_SEMOP, LOCK); + if (!ep) + return -ENOMEM; + + ep->CELL.ipc.in.perm = perm; + ep->CELL.ipc.nsops = nsops; + ep->CELL.ipc.value = alter; + + return dispatch_event(ep); +} + +static int tsem_syslog(int type) +{ + struct tsem_event *ep; + + if (bypass_event(TSEM_SYSLOG)) + return 0; + + ep = tsem_event_allocate(TSEM_SYSLOG, NOLOCK); + if (!ep) + return -ENOMEM; + + ep->CELL.value = type; + + return dispatch_event(ep); +} + +static int tsem_settime(const struct timespec64 *ts, const struct timezone *tz) +{ + struct tsem_event *ep; + + if (bypass_event(TSEM_SETTIME)) + return 0; + + ep = tsem_event_allocate(TSEM_SETTIME, NOLOCK); + if (!ep) + return -ENOMEM; + + if (ts) { + ep->CELL.time.have_ts = true; + ep->CELL.time.seconds = ts->tv_sec; + ep->CELL.time.nsecs = ts->tv_nsec; + } + if (tz) { + ep->CELL.time.have_tz = true; + ep->CELL.time.minuteswest = tz->tz_minuteswest; + ep->CELL.time.dsttime = tz->tz_dsttime; + } + + return dispatch_event(ep); +} + +static int tsem_quotactl(int cmds, int type, int id, + const struct super_block *sb) +{ + struct tsem_event *ep; + + if (bypass_event(TSEM_QUOTACTL)) + return 0; + + ep = tsem_event_allocate(TSEM_QUOTACTL, NOLOCK); + if (!ep) + return -ENOMEM; + + ep->CELL.quota.cmds = cmds; + ep->CELL.quota.type = type; + ep->CELL.quota.id = id; + ep->CELL.quota.in.sb = sb; + + return dispatch_event(ep); +} + +static int tsem_quota_on(struct dentry *dentry) +{ + struct tsem_event *ep; + + if (bypass_event(TSEM_QUOTA_ON)) + return 0; + + ep = tsem_event_allocate(TSEM_QUOTA_ON, NOLOCK); + if (!ep) + return -ENOMEM; + + ep->CELL.quota.in.dentry = dentry; + + return dispatch_event(ep); +} + +static int tsem_msg_queue_associate(struct kern_ipc_perm *perm, int msqflg) +{ + struct tsem_event *ep; + + if (bypass_event(TSEM_MSG_QUEUE_ASSOCIATE)) + return 0; + + ep = tsem_event_allocate(TSEM_MSG_QUEUE_ASSOCIATE, LOCK); + if (!ep) + return -ENOMEM; + + ep->CELL.ipc.in.perm = perm; + ep->CELL.ipc.value = msqflg; + + return dispatch_event(ep); +} + +static int tsem_msg_queue_msgsnd(struct kern_ipc_perm *perm, + struct msg_msg *msgmsg, int msqflg) + +{ + struct tsem_event *ep; + + if (bypass_event(TSEM_MSG_QUEUE_MSGSND)) + return 0; + + ep = tsem_event_allocate(TSEM_MSG_QUEUE_MSGSND, LOCK); + if (!ep) + return -ENOMEM; + + ep->CELL.ipc.in.perm = perm; + ep->CELL.ipc.value = msqflg; + + return dispatch_event(ep); +} + +static int tsem_msg_queue_msgctl(struct kern_ipc_perm *perm, int cmd) +{ + struct tsem_event *ep; + + if (bypass_event(TSEM_MSG_QUEUE_MSGCTL)) + return 0; + + ep = tsem_event_allocate(TSEM_MSG_QUEUE_MSGCTL, LOCK); + if (!ep) + return -ENOMEM; + + ep->CELL.ipc.in.perm = perm; + ep->CELL.ipc.value = cmd; + + return dispatch_event(ep); +} + +static int tsem_msg_queue_msgrcv(struct kern_ipc_perm *perm, + struct msg_msg *msgmsg, + struct task_struct *target, long type, + int mode) +{ + struct tsem_event *ep; + + if (bypass_event(TSEM_MSG_QUEUE_MSGRCV)) + return 0; + + ep = tsem_event_allocate(TSEM_MSG_QUEUE_MSGRCV, LOCK); + if (!ep) + return -ENOMEM; + + ep->CELL.ipc.in.perm = perm; + ep->CELL.ipc.in.target = target; + ep->CELL.ipc.type = type; + ep->CELL.ipc.value = mode; + + return dispatch_event(ep); +} + +static int tsem_ipc_alloc(struct kern_ipc_perm *kipc) +{ + struct tsem_ipc *tipc = tsem_ipc(kipc); + + memcpy(tipc->owner, tsem_task(current)->task_id, tsem_digestsize()); + return 0; +} + +static int tsem_ipc_permission(struct kern_ipc_perm *ipcp, short flag) +{ + struct tsem_event *ep; + + if (bypass_event(TSEM_IPC_PERMISSION)) + return 0; + + ep = tsem_event_allocate(TSEM_IPC_PERMISSION, LOCK); + if (!ep) + return -ENOMEM; + + ep->CELL.ipc.perm_flag = flag; + ep->CELL.ipc.in.perm = ipcp; + + return dispatch_event(ep); +} + +#ifdef CONFIG_KEYS +static int tsem_key_alloc(struct key *key, const struct cred *cred, + unsigned long flags) +{ + struct tsem_event *ep; + + if (static_branch_unlikely(&tsem_not_ready)) + return 0; + if (bypass_event(TSEM_KEY_ALLOC)) + return 0; + + ep = tsem_event_allocate(TSEM_KEY_ALLOC, NOLOCK); + if (!ep) + return -ENOMEM; + + ep->CELL.key.flags = flags; + ep->CELL.key.in.cred = cred; + + return dispatch_event(ep); +} + +static int tsem_key_permission(key_ref_t key_ref, const struct cred *cred, + unsigned int perm) +{ + struct tsem_event *ep; + + if (static_branch_unlikely(&tsem_not_ready)) + return 0; + if (bypass_event(TSEM_KEY_PERMISSION)) + return 0; + + ep = tsem_event_allocate(TSEM_KEY_PERMISSION, LOCK); + if (!ep) + return -ENOMEM; + + ep->CELL.key.flags = perm; + ep->CELL.key.in.cred = cred; + ep->CELL.key.in.ref = key_ref; + + return dispatch_event(ep); +} +#endif + +static int tsem_netlink_send(struct sock *sk, struct sk_buff *skb) +{ + struct tsem_event *ep; + + if (bypass_event(TSEM_NETLINK_SEND)) + return 0; + + ep = tsem_event_allocate(TSEM_NETLINK_SEND, NOLOCK); + if (!ep) + return -ENOMEM; + + ep->CELL.netlink.in.sock = sk; + ep->CELL.netlink.in.parms = (struct netlink_skb_parms *) skb->cb; + + return dispatch_event(ep); +} + +static int tsem_inode_create(struct inode *dir, struct dentry *dentry, + umode_t mode) +{ + struct tsem_event *ep; + + if (static_branch_unlikely(&tsem_not_ready)) + return 0; + if (bypass_event(TSEM_INODE_CREATE)) + return 0; + + ep = tsem_event_allocate(TSEM_INODE_CREATE, NOLOCK); + if (!ep) + return -ENOMEM; + + ep->CELL.inode.in.dir = dir; + ep->CELL.inode.in.dentry = dentry; + ep->CELL.inode.mode = mode; + + return dispatch_event(ep); +} + +static int tsem_inode_link(struct dentry *old_dentry, struct inode *dir, + struct dentry *new_dentry) +{ + struct tsem_event *ep; + + if (static_branch_unlikely(&tsem_not_ready)) + return 0; + if (bypass_event(TSEM_INODE_LINK)) + return 0; + + ep = tsem_event_allocate(TSEM_INODE_LINK, NOLOCK); + if (!ep) + return -ENOMEM; + + ep->CELL.inode.in.dir = dir; + ep->CELL.inode.in.dentry = old_dentry; + ep->CELL.inode.in.new_dentry = new_dentry; + ep->CELL.inode.mode = 0; + + return dispatch_event(ep); +} + +static int tsem_inode_unlink(struct inode *dir, struct dentry *dentry) +{ + struct tsem_event *ep; + + if (static_branch_unlikely(&tsem_not_ready)) + return 0; + if (bypass_event(TSEM_INODE_UNLINK)) + return 0; + + ep = tsem_event_allocate(TSEM_INODE_UNLINK, NOLOCK); + if (!ep) + return -ENOMEM; + + ep->CELL.inode.in.dir = dir; + ep->CELL.inode.in.dentry = dentry; + ep->CELL.inode.mode = 0; + + return dispatch_event(ep); +} + +static int tsem_inode_symlink(struct inode *dir, struct dentry *dentry, + const char *old_name) +{ + struct tsem_event *ep; + + if (static_branch_unlikely(&tsem_not_ready)) + return 0; + if (bypass_event(TSEM_INODE_SYMLINK)) + return 0; + + ep = tsem_event_allocate(TSEM_INODE_SYMLINK, NOLOCK); + if (!ep) + return -ENOMEM; + + ep->CELL.inode.in.dir = dir; + ep->CELL.inode.in.dentry = dentry; + ep->CELL.inode.in.old_name = old_name; + + return dispatch_event(ep); +} + +static int tsem_inode_mkdir(struct inode *dir, struct dentry *dentry, + umode_t mode) +{ + struct tsem_event *ep; + + if (static_branch_unlikely(&tsem_not_ready)) + return 0; + if (bypass_event(TSEM_INODE_MKDIR)) + return 0; + + ep = tsem_event_allocate(TSEM_INODE_MKDIR, NOLOCK); + if (!ep) + return -ENOMEM; + + ep->CELL.inode.in.dir = dir; + ep->CELL.inode.in.dentry = dentry; + ep->CELL.inode.mode = mode; + + return dispatch_event(ep); +} + +static int tsem_inode_rmdir(struct inode *dir, struct dentry *dentry) +{ + struct tsem_event *ep; + + if (bypass_event(TSEM_INODE_RMDIR)) + return 0; + + ep = tsem_event_allocate(TSEM_INODE_RMDIR, NOLOCK); + if (!ep) + return -ENOMEM; + + ep->CELL.inode.in.dir = dir; + ep->CELL.inode.in.dentry = dentry; + ep->CELL.inode.mode = 0; + + return dispatch_event(ep); +} + +static int tsem_inode_rename(struct inode *old_dir, struct dentry *old_dentry, + struct inode *new_dir, struct dentry *new_dentry) +{ + struct tsem_event *ep; + + if (bypass_event(TSEM_INODE_RENAME)) + return 0; + + ep = tsem_event_allocate(TSEM_INODE_RENAME, NOLOCK); + if (!ep) + return -ENOMEM; + + ep->CELL.inode.in.dir = old_dir; + ep->CELL.inode.in.new_dir = new_dir; + ep->CELL.inode.in.dentry = old_dentry; + ep->CELL.inode.in.new_dentry = new_dentry; + + return dispatch_event(ep); +} + +static int tsem_inode_mknod(struct inode *dir, struct dentry *dentry, + umode_t mode, dev_t dev) +{ + struct tsem_event *ep; + + if (static_branch_unlikely(&tsem_not_ready)) + return 0; + if (bypass_event(TSEM_INODE_MKNOD)) + return 0; + + ep = tsem_event_allocate(TSEM_INODE_MKNOD, NOLOCK); + if (!ep) + return -ENOMEM; + + ep->CELL.inode.in.dir = dir; + ep->CELL.inode.in.dentry = dentry; + ep->CELL.inode.mode = mode; + ep->CELL.inode.dev = dev; + + return dispatch_event(ep); +} + +static int tsem_inode_setattr(struct mnt_idmap *idmap, struct dentry *dentry, + struct iattr *attr) +{ + struct tsem_event *ep; + + if (static_branch_unlikely(&tsem_not_ready)) + return 0; + if (bypass_event(TSEM_INODE_SETATTR)) + return 0; + + ep = tsem_event_allocate(TSEM_INODE_SETATTR, NOLOCK); + if (!ep) + return -ENOMEM; + + ep->CELL.inode_attr.in.dentry = dentry; + ep->CELL.inode_attr.in.iattr = attr; + + return dispatch_event(ep); +} + +static int tsem_inode_getattr(const struct path *path) +{ + struct tsem_event *ep; + + if (static_branch_unlikely(&tsem_not_ready)) + return 0; + if (bypass_event(TSEM_INODE_GETATTR)) + return 0; + + ep = tsem_event_allocate(TSEM_INODE_GETATTR, NOLOCK); + if (!ep) + return -ENOMEM; + + ep->CELL.inode_attr.in.path = path; + + return dispatch_event(ep); +} + +static int tsem_inode_setxattr(struct mnt_idmap *idmap, + struct dentry *dentry, const char *name, + const void *value, size_t size, int flags) +{ + struct tsem_event *ep; + + if (bypass_event(TSEM_INODE_SETXATTR)) + return 0; + + ep = tsem_event_allocate(TSEM_INODE_SETXATTR, NOLOCK); + if (!ep) + return -ENOMEM; + + ep->CELL.inode_xattr.in.dentry = dentry; + ep->CELL.inode_xattr.in.name = name; + ep->CELL.inode_xattr.in.value = value; + ep->CELL.inode_xattr.in.size = size; + ep->CELL.inode_xattr.in.flags = flags; + + return dispatch_event(ep); +} + +static int tsem_inode_getxattr(struct dentry *dentry, const char *name) +{ + struct tsem_event *ep = NULL; + + if (static_branch_unlikely(&tsem_not_ready)) + return 0; + if (bypass_event(TSEM_INODE_GETXATTR)) + return 0; + + ep = tsem_event_allocate(TSEM_INODE_GETXATTR, NOLOCK); + if (!ep) + return -ENOMEM; + + ep->CELL.inode_xattr.in.dentry = dentry; + ep->CELL.inode_xattr.in.name = name; + + return dispatch_event(ep); +} + +static int tsem_inode_listxattr(struct dentry *dentry) +{ + struct tsem_event *ep; + + if (static_branch_unlikely(&tsem_not_ready)) + return 0; + if (bypass_event(TSEM_INODE_LISTXATTR)) + return 0; + + ep = tsem_event_allocate(TSEM_INODE_LISTXATTR, NOLOCK); + if (!ep) + return -ENOMEM; + + ep->CELL.inode_xattr.in.dentry = dentry; + + return dispatch_event(ep); +} + +static int tsem_inode_removexattr(struct mnt_idmap *idmap, + struct dentry *dentry, const char *name) +{ + struct tsem_event *ep; + + if (bypass_event(TSEM_INODE_REMOVEXATTR)) + return 0; + + ep = tsem_event_allocate(TSEM_INODE_REMOVEXATTR, NOLOCK); + if (!ep) + return -ENOMEM; + + ep->CELL.inode_xattr.in.dentry = dentry; + ep->CELL.inode_xattr.in.name = name; + + return dispatch_event(ep); +} + +static int tsem_inode_killpriv(struct mnt_idmap *idmap, + struct dentry *dentry) +{ + struct tsem_event *ep; + + if (bypass_event(TSEM_INODE_KILLPRIV)) + return 0; + + ep = tsem_event_allocate(TSEM_INODE_KILLPRIV, NOLOCK); + if (!ep) + return -ENOMEM; + + ep->CELL.inode.in.dentry = dentry; + + return dispatch_event(ep); +} + +static int tsem_tun_dev_create(void) +{ + struct tsem_event *ep; + + if (bypass_event(TSEM_TUN_DEV_CREATE)) + return 0; + + ep = tsem_event_allocate(TSEM_TUN_DEV_CREATE, NOLOCK); + if (!ep) + return -ENOMEM; + ep->no_params = true; + + return dispatch_event(ep); +} + +static int tsem_tun_dev_attach_queue(void *security) +{ + struct tsem_event *ep; + + if (bypass_event(TSEM_TUN_DEV_ATTACH_QUEUE)) + return 0; + + ep = tsem_event_allocate(TSEM_TUN_DEV_ATTACH_QUEUE, NOLOCK); + if (!ep) + return -ENOMEM; + ep->no_params = true; + + return dispatch_event(ep); +} + +static int tsem_tun_dev_attach(struct sock *sk, void *security) +{ + struct tsem_event *ep; + + if (bypass_event(TSEM_TUN_DEV_ATTACH)) + return 0; + + ep = tsem_event_allocate(TSEM_TUN_DEV_ATTACH, NOLOCK); + if (!ep) + return -ENOMEM; + + ep->CELL.socket.in.socka = sk; + + return dispatch_event(ep); +} + +static int tsem_tun_dev_open(void *security) +{ + struct tsem_event *ep; + + if (bypass_event(TSEM_TUN_DEV_OPEN)) + return 0; + + ep = tsem_event_allocate(TSEM_TUN_DEV_OPEN, NOLOCK); + if (!ep) + return -ENOMEM; + ep->no_params = true; + + return dispatch_event(ep); +} + +#ifdef CONFIG_BPF_SYSCALL +static int tsem_bpf(int cmd, union bpf_attr *attr, unsigned int size) +{ + struct tsem_event *ep; + + if (bypass_event(TSEM_BPF)) + return 0; + + ep = tsem_event_allocate(TSEM_BPF, NOLOCK); + if (!ep) + return -ENOMEM; + + ep->CELL.bpf.bpf.cmd = cmd; + ep->CELL.bpf.bpf.size = size; + + return dispatch_event(ep); +} + +static int tsem_bpf_map(struct bpf_map *map, fmode_t fmode) +{ + struct tsem_event *ep; + + if (bypass_event(TSEM_BPF_MAP)) + return 0; + + ep = tsem_event_allocate(TSEM_BPF_MAP, NOLOCK); + if (!ep) + return -ENOMEM; + + ep->CELL.bpf.map.fmode = fmode; + ep->CELL.bpf.map.map_type = map->map_type; + + return dispatch_event(ep); +} + +static int tsem_bpf_prog(struct bpf_prog *prog) +{ + struct tsem_event *ep; + + if (bypass_event(TSEM_BPF_PROG)) + return 0; + + ep = tsem_event_allocate(TSEM_BPF_PROG, NOLOCK); + if (!ep) + return -ENOMEM; + + ep->CELL.bpf.prog.type = prog->type; + ep->CELL.bpf.prog.attach_type = prog->expected_attach_type; + + return dispatch_event(ep); +} +#endif + +static struct security_hook_list tsem_hooks[] __ro_after_init = { + LSM_HOOK_INIT(task_alloc, tsem_task_alloc), + LSM_HOOK_INIT(task_free, tsem_task_free), + LSM_HOOK_INIT(task_kill, tsem_task_kill), + LSM_HOOK_INIT(task_setpgid, tsem_task_setpgid), + LSM_HOOK_INIT(task_getpgid, tsem_task_getpgid), + LSM_HOOK_INIT(task_getsid, tsem_task_getsid), + LSM_HOOK_INIT(task_setnice, tsem_task_setnice), + LSM_HOOK_INIT(task_setioprio, tsem_task_setioprio), + LSM_HOOK_INIT(task_getioprio, tsem_task_getioprio), + LSM_HOOK_INIT(task_prlimit, tsem_task_prlimit), + LSM_HOOK_INIT(task_setrlimit, tsem_task_setrlimit), + LSM_HOOK_INIT(task_setscheduler, tsem_task_setscheduler), + LSM_HOOK_INIT(task_getscheduler, tsem_task_getscheduler), + LSM_HOOK_INIT(task_prctl, tsem_task_prctl), + + LSM_HOOK_INIT(ptrace_access_check, tsem_ptrace_access_check), + LSM_HOOK_INIT(ptrace_traceme, tsem_ptrace_traceme), + + LSM_HOOK_INIT(capget, tsem_capget), + LSM_HOOK_INIT(capset, tsem_capset), + LSM_HOOK_INIT(capable, tsem_capable), + + LSM_HOOK_INIT(bprm_committed_creds, tsem_bprm_committed_creds), + + LSM_HOOK_INIT(inode_alloc_security, tsem_inode_alloc_security), + LSM_HOOK_INIT(inode_init_security, tsem_inode_init_security), + LSM_HOOK_INIT(inode_free_security, tsem_inode_free_security), + + LSM_HOOK_INIT(file_open, tsem_file_open), + LSM_HOOK_INIT(mmap_file, tsem_mmap_file), + LSM_HOOK_INIT(file_ioctl, tsem_file_ioctl), + LSM_HOOK_INIT(file_lock, tsem_file_lock), + LSM_HOOK_INIT(file_fcntl, tsem_file_fcntl), + LSM_HOOK_INIT(file_receive, tsem_file_receive), + + LSM_HOOK_INIT(unix_stream_connect, tsem_unix_stream_connect), + LSM_HOOK_INIT(unix_may_send, tsem_unix_may_send), + + LSM_HOOK_INIT(socket_post_create, tsem_socket_post_create), + LSM_HOOK_INIT(socket_create, tsem_socket_create), + LSM_HOOK_INIT(socket_connect, tsem_socket_connect), + LSM_HOOK_INIT(socket_bind, tsem_socket_bind), + LSM_HOOK_INIT(socket_accept, tsem_socket_accept), + LSM_HOOK_INIT(socket_listen, tsem_socket_listen), + LSM_HOOK_INIT(socket_socketpair, tsem_socket_socketpair), + LSM_HOOK_INIT(socket_sendmsg, tsem_socket_sendmsg), + LSM_HOOK_INIT(socket_recvmsg, tsem_socket_recvmsg), + LSM_HOOK_INIT(socket_getsockname, tsem_socket_getsockname), + LSM_HOOK_INIT(socket_getpeername, tsem_socket_getpeername), + LSM_HOOK_INIT(socket_setsockopt, tsem_socket_setsockopt), + LSM_HOOK_INIT(socket_shutdown, tsem_socket_shutdown), + + LSM_HOOK_INIT(kernel_module_request, tsem_kernel_module_request), + LSM_HOOK_INIT(kernel_load_data, tsem_kernel_load_data), + LSM_HOOK_INIT(kernel_read_file, tsem_kernel_read_file), + + LSM_HOOK_INIT(sb_mount, tsem_sb_mount), + LSM_HOOK_INIT(sb_umount, tsem_sb_umount), + LSM_HOOK_INIT(sb_remount, tsem_sb_remount), + LSM_HOOK_INIT(sb_pivotroot, tsem_sb_pivotroot), + LSM_HOOK_INIT(sb_statfs, tsem_sb_statfs), + LSM_HOOK_INIT(move_mount, tsem_move_mount), + + LSM_HOOK_INIT(shm_alloc_security, tsem_ipc_alloc), + LSM_HOOK_INIT(shm_associate, tsem_shm_associate), + LSM_HOOK_INIT(shm_shmctl, tsem_shm_shmctl), + LSM_HOOK_INIT(shm_shmat, tsem_shm_shmat), + + LSM_HOOK_INIT(sem_alloc_security, tsem_ipc_alloc), + LSM_HOOK_INIT(sem_associate, tsem_sem_associate), + LSM_HOOK_INIT(sem_semctl, tsem_sem_semctl), + LSM_HOOK_INIT(sem_semop, tsem_sem_semop), + + LSM_HOOK_INIT(syslog, tsem_syslog), + LSM_HOOK_INIT(settime, tsem_settime), + + LSM_HOOK_INIT(quotactl, tsem_quotactl), + LSM_HOOK_INIT(quota_on, tsem_quota_on), + + LSM_HOOK_INIT(msg_queue_alloc_security, tsem_ipc_alloc), + LSM_HOOK_INIT(msg_queue_associate, tsem_msg_queue_associate), + LSM_HOOK_INIT(msg_queue_msgctl, tsem_msg_queue_msgctl), + LSM_HOOK_INIT(msg_queue_msgsnd, tsem_msg_queue_msgsnd), + LSM_HOOK_INIT(msg_queue_msgrcv, tsem_msg_queue_msgrcv), + + LSM_HOOK_INIT(ipc_permission, tsem_ipc_permission), + +#ifdef CONFIG_KEYS + LSM_HOOK_INIT(key_alloc, tsem_key_alloc), + LSM_HOOK_INIT(key_permission, tsem_key_permission), +#endif + + LSM_HOOK_INIT(netlink_send, tsem_netlink_send), + + LSM_HOOK_INIT(inode_create, tsem_inode_create), + LSM_HOOK_INIT(inode_link, tsem_inode_link), + LSM_HOOK_INIT(inode_unlink, tsem_inode_unlink), + LSM_HOOK_INIT(inode_symlink, tsem_inode_symlink), + LSM_HOOK_INIT(inode_mkdir, tsem_inode_mkdir), + LSM_HOOK_INIT(inode_rmdir, tsem_inode_rmdir), + LSM_HOOK_INIT(inode_mknod, tsem_inode_mknod), + LSM_HOOK_INIT(inode_rename, tsem_inode_rename), + LSM_HOOK_INIT(inode_setattr, tsem_inode_setattr), + LSM_HOOK_INIT(inode_getattr, tsem_inode_getattr), + LSM_HOOK_INIT(inode_setxattr, tsem_inode_setxattr), + LSM_HOOK_INIT(inode_getxattr, tsem_inode_getxattr), + LSM_HOOK_INIT(inode_listxattr, tsem_inode_listxattr), + LSM_HOOK_INIT(inode_removexattr, tsem_inode_removexattr), + LSM_HOOK_INIT(inode_killpriv, tsem_inode_killpriv), + + LSM_HOOK_INIT(tun_dev_create, tsem_tun_dev_create), + LSM_HOOK_INIT(tun_dev_attach_queue, tsem_tun_dev_attach_queue), + LSM_HOOK_INIT(tun_dev_attach, tsem_tun_dev_attach), + LSM_HOOK_INIT(tun_dev_open, tsem_tun_dev_open), + +#ifdef CONFIG_BPF_SYSCALL + LSM_HOOK_INIT(bpf, tsem_bpf), + LSM_HOOK_INIT(bpf_map, tsem_bpf_map), + LSM_HOOK_INIT(bpf_prog, tsem_bpf_prog) +#endif +}; + +static int configure_root_digest(void) +{ + int retn = 0; + char *digest = NULL; + u8 zero_digest[HASH_MAX_DIGESTSIZE]; + struct crypto_shash *tfm; + SHASH_DESC_ON_STACK(shash, tfm); + + if (default_hash_function && crypto_has_shash(default_hash_function, + 0, 0)) { + digest = default_hash_function; + pr_warn("tsem: Using digest %s from command-line.\n", digest); + } + if (!digest && default_hash_function) + pr_warn("tsem: Unknown root digest %s, using sha256.\n", + default_hash_function); + if (!digest) + digest = "sha256"; + + tsem_context(current)->digestname = kstrdup(digest, GFP_KERNEL); + if (!tsem_context(current)->digestname) + return -ENOMEM; + + tfm = crypto_alloc_shash(digest, 0, 0); + if (IS_ERR(tfm)) + return PTR_ERR(tfm); + + shash->tfm = tfm; + retn = crypto_shash_digest(shash, NULL, 0, zero_digest); + if (retn) + goto done; + + tsem_context(current)->tfm = tfm; + memcpy(root_context.zero_digest, zero_digest, + crypto_shash_digestsize(tfm)); + + done: + if (retn) { + kfree(tsem_context(current)->digestname); + crypto_free_shash(tfm); + } + + return retn; +} + +static int __init set_ready(void) +{ + int retn; + + if (!tsem_available) + return 0; + + retn = configure_root_digest(); + if (retn) + goto done; + + retn = tsem_model_add_aggregate(); + if (retn) + goto done; + + retn = tsem_fs_init(); + if (retn) + goto done; + + if (tsem_mode == ROOT_EXPORT_ONLY) { + retn = tsem_ns_export_root(magazine_size); + if (retn) + goto done; + } + + pr_info("tsem: Now active.\n"); + static_branch_disable(&tsem_not_ready); + + done: + return retn; +} + +late_initcall(set_ready); + +/** + * tesm_init() - Register Trusted Security Event Modeling LSM. + * + * This function is responsible for initializing the TSEM LSM. It is + * invoked at the fs_initcall level. In addition to configuring the + * LSM hooks this function initializes the Trusted Modeling Agent + * context including the event actions. The cache from which + * the tsem_event description structures is also initialized. + * + * Return: If the TSEM LSM is successfully initialized a value of zero + * is returned. A non-zero error code is returned if + * initialization fails. Currently the only failure mode can + * come from the initialization of the tsem_event cache. + */ +static int __init tsem_init(void) +{ + int retn; + char *msg; + struct tsem_task *tsk = tsem_task(current); + struct tsem_context *ctx = &root_context; + struct tsem_model *model = &root_model; + + security_add_hooks(tsem_hooks, ARRAY_SIZE(tsem_hooks), &tsem_lsmid); + + tsk->context = ctx; + kref_init(&ctx->kref); + kref_get(&ctx->kref); + + mutex_init(&ctx->inode_mutex); + INIT_LIST_HEAD(&ctx->inode_list); + + root_context.ops = &tsem_model0_ops; + root_context.model = &root_model; + + retn = tsem_event_cache_init(); + if (retn) + return retn; + + retn = tsem_model_cache_init(model, magazine_size); + if (retn) + goto done; + + retn = tsem_export_cache_init(); + if (retn) + goto done; + + retn = tsem_event_magazine_allocate(ctx, magazine_size); + if (retn) + goto done; + memcpy(ctx->actions, tsem_root_actions, sizeof(tsem_root_actions)); + + switch (tsem_mode) { + case FULL_MODELING: + msg = "full modeling"; + break; + case NO_ROOT_MODELING: + msg = "namespace only modeling"; + break; + case ROOT_EXPORT_ONLY: + msg = "root export only"; + break; + } + pr_info("tsem: Initialized %s.\n", msg); + + tsem_available = true; + tsk->trust_status = TSEM_TASK_TRUSTED; + retn = 0; + + done: + if (retn) { + tsem_event_magazine_free(ctx); + tsem_model_magazine_free(model); + } + return retn; +} + +DEFINE_LSM(tsem) = { + .name = "tsem", + .init = tsem_init, + .blobs = &tsem_blob_sizes, +}; From patchwork Mon Aug 26 10:37:19 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Dr. Greg" X-Patchwork-Id: 13777621 X-Patchwork-Delegate: paul@paul-moore.com Received: from blizzard.enjellic.com (wind.enjellic.com [76.10.64.91]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 6DBE914EC4E; Mon, 26 Aug 2024 10:50:07 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=76.10.64.91 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1724669409; cv=none; b=RgL7eFPN77iwV1YKalpjqfTzoxhecbM5Q8aXNpDPFLu6YI8f5tHLcwUkRrryJGxF/bGKB4tD6AGGmS2WSUUraRMG0IFMqKRK1y+IKxZWFo0BPtYw1OxUOwBr0qWavbD/BAqSOzOI333QTVJhMikXu9lwdopLTheA0E154yE9oOc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1724669409; c=relaxed/simple; bh=+8yWqdXXLHGTFoBRqwclamyaWePmRERrr6FXaYFygho=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=Na27IwKosyxDT/Jk2QEFDQwV0Iqw0B/XSgKlMzvLljUrJOAvMrXYyXMSXbRtUT/fHJ54uk1QYPYm4Pu2Kmbbwi2GWa1uZZ1kIRdD8iwsqp2k4dDOZ5kJJG3Fhx9SWMe+rIWiYbyX4YTD0wQYenejsvdgIk/XLz9KfdiMqRIgp70= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=enjellic.com; spf=pass smtp.mailfrom=enjellic.com; arc=none smtp.client-ip=76.10.64.91 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=enjellic.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=enjellic.com Received: from blizzard.enjellic.com (localhost [127.0.0.1]) by blizzard.enjellic.com (8.15.2/8.15.2) with ESMTP id 47QAbVfg003421; Mon, 26 Aug 2024 05:37:31 -0500 Received: (from greg@localhost) by blizzard.enjellic.com (8.15.2/8.15.2/Submit) id 47QAbVYw003420; Mon, 26 Aug 2024 05:37:31 -0500 X-Authentication-Warning: blizzard.enjellic.com: greg set sender to greg@enjellic.com using -f From: Greg Wettstein To: linux-security-module@vger.kernel.org, linux-kernel@vger.kernel.org Cc: jmorris@namei.org Subject: [PATCH v4 05/14] Add root domain trust implementation. Date: Mon, 26 Aug 2024 05:37:19 -0500 Message-Id: <20240826103728.3378-6-greg@enjellic.com> X-Mailer: git-send-email 2.39.1 In-Reply-To: <20240826103728.3378-1-greg@enjellic.com> References: <20240826103728.3378-1-greg@enjellic.com> Precedence: bulk X-Mailing-List: linux-security-module@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 The trust.c contains the support infrastructure for anchoring the root modeling domain to a Trusted Platform Modul (TPM) implementation if one is available. The Platform Configuation Register (PCR) that is selected for the trust root is extended with the security state coefficients that each security event maps into. Also included is functionality that computes the hardware platform aggregate measurement that is the linear extension sum over PCR register 0 through 8. The hardware aggregate measurement is generated and maintained in multiple cryptographic digest forms depending on the cryptographic hash functions used in the security modeling namespaces that request the value of the hardware aggregate. --- security/tsem/trust.c | 261 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 261 insertions(+) create mode 100644 security/tsem/trust.c diff --git a/security/tsem/trust.c b/security/tsem/trust.c new file mode 100644 index 000000000000..762bca735afc --- /dev/null +++ b/security/tsem/trust.c @@ -0,0 +1,261 @@ +// SPDX-License-Identifier: GPL-2.0-only + +/* + * Copyright (C) 2024 Enjellic Systems Development, LLC + * Author: Dr. Greg Wettstein + * + * On a platform that has access to a TPM device, this file is + * responsible for maintaining a root of trust based on that device. + * + * As with other trusted platform implementations the root of trust is + * a linear extension measurement maintained in a Platform + * Configuration Register (PCR) on the TPM device. + * + * The PCR is extended with each unique security state coefficient + * that is generated by the model being implemented for the + * root security modeling namespace. As with other trusted systems + * the value of the PCR will be variable depending on scheduling + * artifacts that are experienced by the root modeling namespace. + * + * TSEM uses a strategy of executing the TPM extension transactions + * using an ordered workqueue rather than doing them at the time the + * security event is processed. This is done both for performance + * reasons and the fact that the coefficient extensions may arise from + * security events that are being invoked by processes running in + * atomic context. + * + * If the trust_init() function detects the presence of a TPM an + * ordered workqueue with the following name is created: + * + * tsem_tpm + * + * The tsem_trust_add_event() function places a work request + * containing a description of the event on this workqueue that will + * then asynchronously extend the security state coefficient of the + * event into the PCR being used to measure the trust state of + * the root security modeling namespace. + * + * This file is also responsible for providing the hardware aggregate + * measurement for injection into both internally and externally + * modeled namespaces. The hardware aggregate value is the linear + * extension sum of PCR registers 0 through 8. + * + * Since TSEM supports multiple cryptographic digest functions on a + * namespace by namespace basis the hardware aggregate value is + * maintained in multiple digest forms. The tsem_trust_aggregate() + * function returns a pointer to the digest value for the + * cryptographic digest function that is being used by the security + * modeling namespace in effect for the calling process. + */ + +#include + +#include "tsem.h" + +static struct workqueue_struct *tpm_update_wq; + +static u8 zero_aggregate[HASH_MAX_DIGESTSIZE]; + +static struct tpm_chip *tpm; + +static struct tpm_digest *digests; + +struct hardware_aggregate { + struct list_head list; + char *name; + u8 value[HASH_MAX_DIGESTSIZE]; +}; + +DEFINE_MUTEX(hardware_aggregate_mutex); +LIST_HEAD(hardware_aggregate_list); + +static struct hardware_aggregate *find_aggregate(void) +{ + struct hardware_aggregate *aggregate; + + list_for_each_entry(aggregate, &hardware_aggregate_list, list) { + if (!strcmp(aggregate->name, + tsem_context(current)->digestname)) + goto done; + } + aggregate = NULL; + + done: + return aggregate; +} + +static struct hardware_aggregate *add_aggregate(u8 *new_aggregate) +{ + struct hardware_aggregate *aggregate; + + aggregate = kzalloc(sizeof(*aggregate), GFP_KERNEL); + if (!aggregate) + return NULL; + + aggregate->name = kstrdup(tsem_context(current)->digestname, + GFP_KERNEL); + if (!aggregate->name) { + kfree(aggregate); + return NULL; + } + memcpy(aggregate->value, new_aggregate, tsem_digestsize()); + + list_add(&aggregate->list, &hardware_aggregate_list); + + return aggregate; +} + +/** + * tsem_trust_aggregate() - Return a pointer to the hardware aggregate. + * + * This function returns a pointer to the hardware aggregate encoded + * with the hash function for the current modeling domain. + * + * Return: A pointer is returned to the hardware aggregate value that + * has been cached. + */ +u8 *tsem_trust_aggregate(void) +{ + u8 aggregate[HASH_MAX_DIGESTSIZE], *retn = zero_aggregate; + u16 size; + unsigned int lp; + struct tpm_digest pcr; + struct hardware_aggregate *hw_aggregate; + SHASH_DESC_ON_STACK(shash, tfm); + + if (!tpm) + return retn; + + mutex_lock(&hardware_aggregate_mutex); + + hw_aggregate = find_aggregate(); + if (hw_aggregate) { + retn = hw_aggregate->value; + goto done; + } + + shash->tfm = tsem_digest(); + if (crypto_shash_init(shash)) + goto done; + + if (tpm_is_tpm2(tpm)) + pcr.alg_id = TPM_ALG_SHA256; + else + pcr.alg_id = TPM_ALG_SHA1; + memset(pcr.digest, '\0', TPM_MAX_DIGEST_SIZE); + + for (lp = 0; lp < tpm->nr_allocated_banks; lp++) { + if (pcr.alg_id == tpm->allocated_banks[lp].alg_id) { + size = tpm->allocated_banks[lp].digest_size; + break; + } + } + + for (lp = 0; lp < 8; ++lp) { + if (tpm_pcr_read(tpm, lp, &pcr)) + goto done; + if (crypto_shash_update(shash, pcr.digest, size)) + goto done; + } + if (!crypto_shash_final(shash, aggregate)) { + hw_aggregate = add_aggregate(aggregate); + if (hw_aggregate) + retn = hw_aggregate->value; + } + + done: + mutex_unlock(&hardware_aggregate_mutex); + + if (retn == zero_aggregate) + pr_warn("tsem: Error generating platform aggregate\n"); + + return retn; +} + +static void tpm_update_worker(struct work_struct *work) +{ + int amt, bank, digestsize; + struct tsem_event *ep; + + ep = container_of(work, struct tsem_event, work); + digestsize = ep->digestsize; + + for (bank = 0; bank < tpm->nr_allocated_banks; bank++) { + if (tpm->allocated_banks[bank].digest_size > digestsize) { + amt = digestsize; + memset(digests[bank].digest, '\0', + tpm->allocated_banks[bank].digest_size); + } else + amt = tpm->allocated_banks[bank].digest_size; + memcpy(digests[bank].digest, ep->mapping, amt); + } + + if (tpm_pcr_extend(tpm, CONFIG_SECURITY_TSEM_ROOT_MODEL_PCR, + digests)) + pr_warn("tsem: Failed TPM update.\n"); + + tsem_event_put(ep); +} + +/** + * tsem_trust_add_point() - Add a measurement to the trust root. + * @ep: A pointer to the security event description whose measurement + * is to be extended into the TPM. + * + * This function extends the platform configuration register being + * used to document the hardware root of trust for internally modeled + * domains with a security event coefficient value. + * + * Return: If the extension fails the error return value from the + * TPM command is returned, otherwise a value of zero is + * returned. + */ +int tsem_trust_add_event(struct tsem_event *ep) +{ + bool retn; + + if (!tpm) + return 0; + + tsem_event_get(ep); + ep->digestsize = tsem_digestsize(); + + INIT_WORK(&ep->work, tpm_update_worker); + retn = queue_work(tpm_update_wq, &ep->work); + + return 0; +} + +static int __init trust_init(void) +{ + int retn = -EINVAL, lp; + + tpm = tpm_default_chip(); + if (!tpm) + return retn; + + tpm_update_wq = alloc_ordered_workqueue("tsem_tpm", 0); + if (IS_ERR(tpm_update_wq)) { + retn = PTR_ERR(tpm_update_wq); + goto done; + } + + digests = kcalloc(tpm->nr_allocated_banks, sizeof(*digests), GFP_NOFS); + if (!digests) { + tpm = NULL; + return retn; + } + for (lp = 0; lp < tpm->nr_allocated_banks; lp++) + digests[lp].alg_id = tpm->allocated_banks[lp].alg_id; + retn = 0; + + done: + if (retn) { + destroy_workqueue(tpm_update_wq); + kfree(digests); + } + + return retn; +} + +device_initcall_sync(trust_init); From patchwork Mon Aug 26 10:37:20 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Dr. Greg" X-Patchwork-Id: 13777630 X-Patchwork-Delegate: paul@paul-moore.com Received: from blizzard.enjellic.com (wind.enjellic.com [76.10.64.91]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 1799C17BEA7; Mon, 26 Aug 2024 10:50:46 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=76.10.64.91 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1724669449; cv=none; b=kIS6AxhqwalJzACSN+oiO+ug43C817pysTCvUJyvMGGEXx40VfiW+Zbbqkzj3qZt+JyVtFP8LX9gO6QuynfIzAGj9gvvOB1VBiMJApXBm8YaZpvzi+ynucalUo0jS6cUHQQPBUKzXzYAtAGMWEpQW3p3Hyplm6KgrkORDXIom1I= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1724669449; c=relaxed/simple; bh=voJSXXHd61ZTmOlbPSqR+RzaCi6fmvrfPwJ0oHpsFXQ=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=JnCnRZB/GWCgeV6evFjGabilNw03yfffJGI4PvRfYaXVbLoy9vHrk5r5xHSFtwYmci/n1fu3hHe/hXsCZ8UQnEtChCVyJljmanTZJvogrCs744zHKMIw3rViD91DWy3ATTeObD/1WtvOrsNqMGKpabtikDTWA02WYVGY7I6bETg= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=enjellic.com; spf=pass smtp.mailfrom=enjellic.com; arc=none smtp.client-ip=76.10.64.91 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=enjellic.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=enjellic.com Received: from blizzard.enjellic.com (localhost [127.0.0.1]) by blizzard.enjellic.com (8.15.2/8.15.2) with ESMTP id 47QAbVKL003425; Mon, 26 Aug 2024 05:37:31 -0500 Received: (from greg@localhost) by blizzard.enjellic.com (8.15.2/8.15.2/Submit) id 47QAbVBd003424; Mon, 26 Aug 2024 05:37:31 -0500 X-Authentication-Warning: blizzard.enjellic.com: greg set sender to greg@enjellic.com using -f From: Greg Wettstein To: linux-security-module@vger.kernel.org, linux-kernel@vger.kernel.org Cc: jmorris@namei.org Subject: [PATCH v4 06/14] Implement TSEM control plane. Date: Mon, 26 Aug 2024 05:37:20 -0500 Message-Id: <20240826103728.3378-7-greg@enjellic.com> X-Mailer: git-send-email 2.39.1 In-Reply-To: <20240826103728.3378-1-greg@enjellic.com> References: <20240826103728.3378-1-greg@enjellic.com> Precedence: bulk X-Mailing-List: linux-security-module@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 The fs.c file contains the implementation of the TSEM control plane that is surfaced through the following directory heirarchy in the securityfs filesystem mount /sys/kernel/security/tsem The following file documents the interfaces provided by the control plane: Documentation/ABI/testing/tsem The directory heirarchy partitions into the following two branches /sys/kernel/security/tsem/internal_tma /sys/kernel/security/tsem/external_tma Where the internal_tma directory surfaces the characteristics of the in kernel Trusted Modeling Agent implementation. When an externally modeled namespace is created a file is created in the external_tma directory that is named for the security context identifier of the namespace that was created. An external trust orchestrator uses this file to read security events that occur in the context of the namespace. This file also contains the functionality that generates the JSON encoded security event descriptions. These descriptions encode the information used as input to TSEM security models. --- security/tsem/fs.c | 2304 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 2304 insertions(+) create mode 100644 security/tsem/fs.c diff --git a/security/tsem/fs.c b/security/tsem/fs.c new file mode 100644 index 000000000000..04762dd5fd5c --- /dev/null +++ b/security/tsem/fs.c @@ -0,0 +1,2304 @@ +// SPDX-License-Identifier: GPL-2.0-only + +/* + * Copyright (C) 2024 Enjellic Systems Development, LLC + * Author: Dr. Greg Wettstein + * + * This file implements the TSEM control plane that is surfaced + * through the following directory heirarchy: + * + * /sys/kernel/security/tsem + * + * The TSEM ABI documentation documents the directories and files that + * are implemented in this directory heirarchy. + * + * A significant portion of the functions in this file are used to + * generate the JSON encoding of a security event description. This + * encoding is surfaced to the tsem_export_show() function through the + * tsem_fs_show_trajectory() function. This latter function is also + * used by the trajectory and forensics pseudo files that output the + * characteristics of security events modeled by the internal trusted + * modeling agent implementation. + * + * The pseudo-files implemented are context sensitive in that they + * provide output describing the security modeling namespace that the + * process that accesses the file is running in. + */ + +#include +#include + +#include "tsem.h" +#include "nsmgr.h" + +static struct dentry *tsem_dir; +static struct dentry *control; +static struct dentry *id; +static struct dentry *aggregate; +static struct dentry *internal_tma; +static struct dentry *model; +static struct dentry *forensics; +static struct dentry *forensics_counts; +static struct dentry *forensics_coeff; +static struct dentry *trajectory; +static struct dentry *trajectory_counts; +static struct dentry *trajectory_coeff; +static struct dentry *measurement; +static struct dentry *state; +static struct dentry *external_tma; + +struct control_commands { + char *cmd; + enum tsem_control_type type; +}; + +static const char * const control_commands[] = { + "internal", + "external", + "export", + "enforce", + "seal", + "trusted", + "untrusted", + "state", + "pseudonym", + "base", + "lock" +}; + +enum namespace_argument_type { + NS_MODEL = 0, + NS_REF, + NS_DIGEST, + NS_KEY, + NS_CACHE +}; + +static const char * const namespace_arguments[] = { + "model", + "nsref", + "digest", + "key", + "cache" +}; + +enum control_argument_type { + CONTROL_KEY = 0, + CONTROL_PID +}; + +static const char * const control_arguments[] = { + "key", + "pid" +}; + +static bool can_access_fs(void) +{ + struct tsem_context *ctx = tsem_context(current); + + if (ctx->external) + return false; + if (capable(CAP_MAC_ADMIN)) + return true; + if (ctx->sealed) + return false; + return true; +} + +static int control_COE(unsigned long cmd, pid_t pid, char *keystr) +{ + bool wakeup = false; + int retn = -ESRCH; + u8 event_key[HASH_MAX_DIGESTSIZE]; + struct task_struct *COE; + struct tsem_task *task; + struct tsem_task *tma = tsem_task(current); + + rcu_read_lock(); + COE = find_task_by_vpid(pid); + if (COE != NULL) { + task = tsem_task(COE); + if (tsem_context(COE)->id != tma->tma_for_ns) { + retn = -EINVAL; + goto done; + } + + retn = tsem_ns_event_key(task->task_key, keystr, event_key); + if (retn) + goto done; + + if (memcmp(tma->task_key, event_key, tsem_digestsize())) { + retn = -EINVAL; + goto done; + } + + if (cmd == TSEM_CONTROL_UNTRUSTED) + task->trust_status = TSEM_TASK_UNTRUSTED; + if (cmd == TSEM_CONTROL_TRUSTED) { + task->trust_status &= ~TSEM_TASK_TRUST_PENDING; + if (tsem_task_trusted(COE)) + task->trust_status = TSEM_TASK_TRUSTED; + } + retn = 0; + wakeup = true; + } + + done: + rcu_read_unlock(); + + if (retn == -EINVAL) + pr_warn("tsem: Invalid process release request.\n"); + + if (wakeup) + wake_up_process(COE); + + return retn; +} + +static int config_COE(unsigned long cmd, char *arg) +{ + char **argv, *argp, *key = NULL; + int argc, retn = -EINVAL; + unsigned int lp; + long pid = 0; + enum control_argument_type control_arg; + + if (!*arg) + return retn; + + argv = argv_split(GFP_KERNEL, arg, &argc); + if (!argv) + return -ENOMEM; + + for (lp = 0; lp < argc; ++lp) { + argp = strchr(argv[lp], '='); + if (!argp) + goto done; + *argp++ = '\0'; + + control_arg = match_string(control_arguments, + ARRAY_SIZE(control_arguments), + argv[lp]); + if (control_arg < 0) + goto done; + + switch (control_arg) { + case CONTROL_KEY: + key = argp; + if (strlen(key) != tsem_digestsize()*2) + goto done; + break; + case CONTROL_PID: + if (kstrtol(argp, 0, &pid)) + goto done; + break; + } + } + + if (!key || !pid) + goto done; + retn = control_COE(cmd, pid, key); + + done: + argv_free(argv); + return retn; +} + +static int config_context(unsigned long cmd, char *bufr) +{ + int retn = -EINVAL; + unsigned int lp; + struct tsem_context *ctx = tsem_context(current); + + if (ctx->sealed) + return -EPERM; + + if (cmd == TSEM_CONTROL_SEAL) { + ctx->sealed = true; + retn = 0; + } + + if (cmd == TSEM_CONTROL_ENFORCE) { + for (lp = 0; lp < ARRAY_SIZE(tsem_root_actions); ++lp) + ctx->actions[lp] = TSEM_ACTION_EPERM; + retn = 0; + } + + return retn; +} + +static int config_point(enum tsem_control_type type, char *arg) +{ + char *argp; + int retn = -EINVAL; + u8 mapping[HASH_MAX_DIGESTSIZE]; + + if (!arg) + goto done; + + argp = strchr(arg, '='); + if (!argp) + goto done; + *argp++ = '\0'; + + if (strcmp(arg, "value")) + goto done; + + if (strlen(argp) != tsem_digestsize()*2) + goto done; + if (hex2bin(mapping, argp, tsem_digestsize())) + goto done; + + if (type == TSEM_CONTROL_MAP_STATE) + retn = tsem_model_load_point(mapping); + else if (type == TSEM_CONTROL_MAP_PSEUDONYM) + retn = tsem_model_load_pseudonym(mapping); + else { + tsem_model_load_base(mapping); + retn = 0; + } + + done: + return retn; +} + +static int config_namespace(enum tsem_control_type type, const char *arg) +{ + char **argv, *argp, *digest = "sha256", *key = NULL; + int argc, retn = -EINVAL; + unsigned int lp, cache_size = TSEM_MAGAZINE_SIZE_INTERNAL; + enum namespace_argument_type ns_arg; + enum tsem_ns_reference ns_ref = TSEM_NS_INITIAL; + const struct tsem_context_ops *ops = &tsem_model0_ops; + + if (type == TSEM_CONTROL_EXTERNAL || type == TSEM_CONTROL_EXPORT) + cache_size = TSEM_MAGAZINE_SIZE_EXTERNAL; + + if (!arg) { + if (type == TSEM_CONTROL_EXTERNAL) + return retn; + return tsem_ns_create(type, digest, ns_ref, key, cache_size, + ops); + } + + argv = argv_split(GFP_KERNEL, arg, &argc); + if (!argv) + return -ENOMEM; + + for (lp = 0; lp < argc; ++lp) { + argp = strchr(argv[lp], '='); + if (!argp) + goto done; + *argp++ = '\0'; + + ns_arg = match_string(namespace_arguments, + ARRAY_SIZE(namespace_arguments), argv[lp]); + if (ns_arg < 0) + goto done; + + switch (ns_arg) { + case NS_MODEL: + ops = tsem_nsmgr_get(argp); + if (!ops) + goto done; + break; + case NS_REF: + if (!strcmp(argp, "current")) + ns_ref = TSEM_NS_CURRENT; + else if (!strcmp(argp, "initial")) + ns_ref = TSEM_NS_INITIAL; + else + goto done; + break; + case NS_DIGEST: + digest = argp; + if (!crypto_has_shash(digest, 0, 0)) + goto done; + break; + case NS_KEY: + key = argp; + if (strlen(key) % 2) + goto done; + break; + case NS_CACHE: + if (kstrtouint(argp, 0, &cache_size)) + goto done; + if (!cache_size) + goto done; + break; + default: + break; + } + } + + if (type == TSEM_CONTROL_EXTERNAL && !key) + goto done; + + retn = tsem_ns_create(type, digest, ns_ref, key, cache_size, ops); + + done: + argv_free(argv); + return retn; +} + +static void show_creds(struct seq_file *c, char *key, char *term, + struct tsem_COE *cp) +{ + tsem_fs_show_field(c, key); + tsem_fs_show_key(c, "uid", ",", "%u", cp->uid); + tsem_fs_show_key(c, "euid", ",", "%u", cp->euid); + tsem_fs_show_key(c, "suid", ",", "%u", cp->suid); + tsem_fs_show_key(c, "gid", ",", "%u", cp->gid); + tsem_fs_show_key(c, "egid", ",", "%u", cp->egid); + tsem_fs_show_key(c, "sgid", ",", "%u", cp->sgid); + tsem_fs_show_key(c, "fsuid", ",", "%u", cp->fsuid); + tsem_fs_show_key(c, "fsgid", ",", "%u", cp->fsgid); + tsem_fs_show_key(c, "capeff", ",", "0x%llx", cp->capeff.value); + tsem_fs_show_key(c, "securebits", "}", "%u", cp->securebits); + + seq_puts(c, term); +} + +static void show_event(struct seq_file *c, struct tsem_event *ep) +{ + tsem_fs_show_field(c, "event"); + if (ep->pid) + tsem_fs_show_key(c, "pid", ",", "%u", ep->pid); + tsem_fs_show_key(c, "context", ",", "%llu", ep->context); + tsem_fs_show_key(c, "number", ",", "%llu", ep->event_number); + tsem_fs_show_key(c, "process", ",", "%s", ep->comm); + tsem_fs_show_key(c, "type", ",", "%s", tsem_names[ep->event]); + tsem_fs_show_key(c, "ttd", ",", "%llu", ep->instance); + tsem_fs_show_key(c, "p_ttd", ",", "%llu", ep->p_instance); + tsem_fs_show_key(c, "task_id", ",", "%*phN", tsem_digestsize(), + ep->task_id); + tsem_fs_show_key(c, "p_task_id", ",", "%*phN", tsem_digestsize(), + ep->p_task_id); + tsem_fs_show_key(c, "ts", "}, ", "%llu", ep->timestamp); + + tsem_fs_show_field(c, "COE"); + tsem_fs_show_key(c, "uid", ",", "%u", ep->COE.uid); + tsem_fs_show_key(c, "euid", ",", "%u", ep->COE.euid); + tsem_fs_show_key(c, "suid", ",", "%u", ep->COE.suid); + tsem_fs_show_key(c, "gid", ",", "%u", ep->COE.gid); + tsem_fs_show_key(c, "egid", ",", "%u", ep->COE.egid); + tsem_fs_show_key(c, "sgid", ",", "%u", ep->COE.sgid); + tsem_fs_show_key(c, "fsuid", ",", "%u", ep->COE.fsuid); + tsem_fs_show_key(c, "fsgid", ",", "%u", ep->COE.fsgid); + tsem_fs_show_key(c, "capeff", "}, ", "0x%llx", ep->COE.capeff.value); + + tsem_fs_show_field(c, tsem_names[ep->event]); +} + +static void show_path(struct seq_file *c, char *key, char *term, + struct tsem_path *path) +{ + tsem_fs_show_field(c, key); + + if (path->dev) { + tsem_fs_show_field(c, "dev"); + tsem_fs_show_key(c, "major", ",", "%u", MAJOR(path->dev)); + tsem_fs_show_key(c, "minor", "}, ", "%u", MINOR(path->dev)); + } + + if (path->created) { + tsem_fs_show_key(c, "owner", ",", "%*phN", tsem_digestsize(), + path->owner); + tsem_fs_show_key(c, "instance", ",", "%llu", path->instance); + } + + tsem_fs_show_key(c, "pathname", "}", "%s", path->pathname); + seq_puts(c, term); +} + +static void show_inode(struct seq_file *c, char *key, char *term, + struct tsem_inode_cell *inode) +{ + tsem_fs_show_field(c, key); + tsem_fs_show_key(c, "uid", ",", "%u", inode->uid); + tsem_fs_show_key(c, "gid", ",", "%u", inode->gid); + tsem_fs_show_key(c, "mode", ",", "0%o", inode->mode); + tsem_fs_show_key(c, "s_magic", ",", "0x%0x", inode->s_magic); + tsem_fs_show_key(c, "s_id", ",", "%s", inode->s_id); + tsem_fs_show_key(c, "s_uuid", "}", "%*phN", sizeof(inode->s_uuid), + inode->s_uuid); + + seq_puts(c, term); +} + +static void show_dentry(struct seq_file *c, char *key, char *term, + struct tsem_dentry *dentry) +{ + tsem_fs_show_field(c, key); + if (dentry->have_inode) + show_inode(c, "inode", ", ", &dentry->inode); + + show_path(c, "path", "}", &dentry->path); + + seq_puts(c, term); +} + +static void show_file(struct seq_file *c, char *term, + struct tsem_file_args *args) +{ + tsem_fs_show_field(c, "file"); + tsem_fs_show_key(c, "flags", ",", "%u", args->out.flags); + + show_inode(c, "inode", ", ", &args->out.inode); + show_path(c, "path", ", ", &args->out.path); + tsem_fs_show_key(c, "digest", "}", "%*phN", tsem_digestsize(), + args->out.digest); + + seq_puts(c, term); +} + +static void show_inode_create(struct seq_file *c, struct tsem_event *ep) +{ + struct tsem_inode_args *args = &ep->CELL.inode; + + show_event(c, ep); + + show_inode(c, "dir", ", ", &args->out.dir); + show_dentry(c, "dentry", ", ", &args->out.dentry); + tsem_fs_show_key(c, "mode", "}", "0%o", args->mode); +} + +static void show_inode_remove(struct seq_file *c, struct tsem_event *ep) +{ + struct tsem_inode_args *args = &ep->CELL.inode; + + show_event(c, ep); + + show_inode(c, "dir", ", ", &args->out.dir); + show_dentry(c, "dentry", "}", &args->out.dentry); +} + +static void show_inode_link(struct seq_file *c, struct tsem_event *ep) +{ + struct tsem_inode_args *args = &ep->CELL.inode; + + show_event(c, ep); + + show_dentry(c, "old_dentry", ", ", &args->out.dentry); + show_inode(c, "dir", ", ", &args->out.dir); + show_dentry(c, "new_dentry", "}", &args->out.new_dentry); +} + +static void show_syslog(struct seq_file *c, struct tsem_event *ep) +{ + show_event(c, ep); + tsem_fs_show_key(c, "type", "}", "%d", ep->CELL.value); +} + +static void show_settime(struct seq_file *c, struct tsem_event *ep) +{ + struct tsem_time_args *args = &ep->CELL.time; + + show_event(c, ep); + + if (args->have_ts) { + tsem_fs_show_field(c, "ts"); + tsem_fs_show_key(c, "seconds", ",", "%d", args->seconds); + tsem_fs_show_key(c, "nsecs", args->have_tz ? "}, " : "}", + "%d", args->nsecs); + } + + if (args->have_tz) { + tsem_fs_show_field(c, "tz"); + tsem_fs_show_key(c, "minuteswest", ",", "%d", + args->minuteswest); + tsem_fs_show_key(c, "dsttime", "}", "%d", args->dsttime); + } + + seq_putc(c, '}'); +} + +static void show_inode_symlink(struct seq_file *c, struct tsem_event *ep) +{ + struct tsem_inode_args *args = &ep->CELL.inode; + + show_event(c, ep); + + show_inode(c, "dir", ", ", &args->out.dir); + show_dentry(c, "dentry", ", ", &args->out.dentry); + tsem_fs_show_key(c, "old_name", "}", "%s", args->out.old_name); +} + +static void show_inode_mknod(struct seq_file *c, struct tsem_event *ep) +{ + struct tsem_inode_args *args = &ep->CELL.inode; + + show_event(c, ep); + + show_inode(c, "dir", ", ", &args->out.dir); + + show_dentry(c, "dentry", ", ", &args->out.dentry); + tsem_fs_show_key(c, "mode", ",", "0%o", args->mode); + tsem_fs_show_field(c, "dev"); + tsem_fs_show_key(c, "major", ",", "%u", MAJOR(args->dev)); + tsem_fs_show_key(c, "minor", "}}", "%u", MINOR(args->dev)); +} + +static void show_inode_rename(struct seq_file *c, struct tsem_event *ep) +{ + struct tsem_inode_args *args = &ep->CELL.inode; + + show_event(c, ep); + + show_inode(c, "old_dir", ", ", &args->out.dir); + show_dentry(c, "old_dentry", ", ", &args->out.dentry); + show_inode(c, "new_dir", ", ", &args->out.new_dir); + show_dentry(c, "new_dentry", "}", &args->out.new_dentry); +} + +static void show_inode_killpriv(struct seq_file *c, struct tsem_event *ep) +{ + struct tsem_inode_args *args = &ep->CELL.inode; + + show_event(c, ep); + + show_dentry(c, "dentry", "}", &args->out.dentry); +} + +static void show_file_open(struct seq_file *c, struct tsem_event *ep) +{ + show_event(c, ep); + + show_file(c, "}", &ep->CELL.file); +} + +static void show_mmap(struct seq_file *c, struct tsem_event *ep) +{ + struct tsem_mmap_file_args *args = &ep->CELL.mmap_file; + + show_event(c, ep); + + if (!args->anonymous) + show_file(c, ", ", &args->file); + tsem_fs_show_key(c, "prot", ",", "%u", args->prot); + tsem_fs_show_key(c, "flags", "}", "%u", args->flags); +} + +static void show_file_ioctl(struct seq_file *c, struct tsem_event *ep) +{ + struct tsem_file_args *args = &ep->CELL.file; + + show_event(c, ep); + + show_file(c, ", ", args); + tsem_fs_show_key(c, "cmd", "}", "%u", args->cmd); +} + +static void show_socket_info(struct seq_file *c, const char *key, char *term, + struct tsem_socket *args) +{ + tsem_fs_show_field(c, key); + tsem_fs_show_key(c, "family", ",", "%d", args->family); + tsem_fs_show_key(c, "type", ",", "%d", args->type); + tsem_fs_show_key(c, "protocol", ",", "%d", args->protocol); + tsem_fs_show_key(c, "owner", "}", "%*phN", tsem_digestsize(), + args->owner); + + seq_puts(c, term); +} + +static void show_netlink(struct seq_file *c, struct tsem_event *ep) +{ + struct tsem_netlink_args *args = &ep->CELL.netlink; + + show_event(c, ep); + + show_socket_info(c, "sock", ", ", &args->out.sock); + tsem_fs_show_key(c, "uid", ",", "%u", args->out.uid); + tsem_fs_show_key(c, "gid", ",", "%u", args->out.gid); + tsem_fs_show_key(c, "portid", ",", "%u", args->out.portid); + tsem_fs_show_key(c, "dst_group", ",", "%u", args->out.dst_group); + tsem_fs_show_key(c, "flags", ",", "%u", args->out.flags); + tsem_fs_show_key(c, "nsid_set", ",", "%u", args->out.nsid_set); + tsem_fs_show_key(c, "nsid", "}", "%d", args->out.nsid); +} + +static void show_ipc_cred(struct seq_file *c, char *key, char *term, + struct tsem_ipc_args *args) +{ + tsem_fs_show_field(c, key); + tsem_fs_show_key(c, "uid", ",", "%u", args->out.perm.uid); + tsem_fs_show_key(c, "gid", ",", "%u", args->out.perm.gid); + tsem_fs_show_key(c, "cuid", ",", "%u", args->out.perm.cuid); + tsem_fs_show_key(c, "cgid", ",", "%u", args->out.perm.cgid); + tsem_fs_show_key(c, "mode", ",", "0%o", args->out.perm.mode); + tsem_fs_show_key(c, "owner", "}", "%*phN", tsem_digestsize(), + args->out.owner); + + seq_puts(c, term); +} + +static void show_ipc_permission(struct seq_file *c, struct tsem_event *ep) +{ + struct tsem_ipc_args *args = &ep->CELL.ipc; + + show_event(c, ep); + + show_ipc_cred(c, "ipcp", ", ", args); + tsem_fs_show_key(c, "flag", "}", "%u", args->perm_flag); +} + +static void show_ipc_shm_value(struct seq_file *c, struct tsem_event *ep, + char *name) +{ + struct tsem_ipc_args *args = &ep->CELL.ipc; + + show_event(c, ep); + + show_ipc_cred(c, "perm", ", ", args); + tsem_fs_show_key(c, name, "}", "%d", args->perm_flag); +} + +static void show_msg_queue_msgrcv(struct seq_file *c, struct tsem_event *ep) + +{ + struct tsem_ipc_args *args = &ep->CELL.ipc; + + show_event(c, ep); + + show_ipc_cred(c, "perm", ", ", args); + tsem_fs_show_key(c, "target", ",", "%*phN", tsem_digestsize(), + args->out.target); + tsem_fs_show_key(c, "type", ",", "%ld", args->type); + tsem_fs_show_key(c, "mode", "}", "%d", args->value); +} + +static void show_sem_semop(struct seq_file *c, struct tsem_event *ep) +{ + struct tsem_ipc_args *args = &ep->CELL.ipc; + + show_event(c, ep); + + show_ipc_cred(c, "perm", ", ", args); + tsem_fs_show_key(c, "nsops", ",", "%u", args->nsops); + tsem_fs_show_key(c, "alter", "}", "%d", args->value); +} + +static void show_socket_pair(struct seq_file *c, struct tsem_event *ep, + char *first, char *second) +{ + struct tsem_socket_args *args = &ep->CELL.socket; + + show_event(c, ep); + + show_socket_info(c, first, ", ", &args->out.socka); + show_socket_info(c, second, "}", &args->out.sockb); +} + +static void show_socket_create(struct seq_file *c, struct tsem_event *ep) +{ + struct tsem_socket_args *args = &ep->CELL.socket; + + show_event(c, ep); + + tsem_fs_show_key(c, "family", ",", "%u", args->out.socka.family); + tsem_fs_show_key(c, "type", ",", "%u", args->out.socka.type); + tsem_fs_show_key(c, "protocol", ",", "%u", args->out.socka.protocol); + tsem_fs_show_key(c, "kern", "}", "%u", args->out.socka.kern); +} + +static void show_socket_connect_bind(struct seq_file *c, struct tsem_event *ep) +{ + struct sockaddr_in *ipv4; + struct sockaddr_in6 *ipv6; + struct tsem_socket_args *args = &ep->CELL.socket; + + show_event(c, ep); + + show_socket_info(c, "sock", ", ", &args->out.socka); + + tsem_fs_show_field(c, "addr"); + switch (args->out.socka.family) { + case AF_INET: + ipv4 = (struct sockaddr_in *) &args->out.ipv4; + tsem_fs_show_field(c, "af_inet"); + tsem_fs_show_key(c, "port", ",", "%u", ipv4->sin_port); + tsem_fs_show_key(c, "address", "}}}", "%u", + ipv4->sin_addr.s_addr); + break; + case AF_INET6: + ipv6 = (struct sockaddr_in6 *) &args->out.ipv6; + tsem_fs_show_field(c, "af_inet6"); + tsem_fs_show_key(c, "port", ",", "%u", ipv6->sin6_port); + tsem_fs_show_key(c, "flow", ",", "%u", ipv6->sin6_flowinfo); + tsem_fs_show_key(c, "scope", ",", "%u", ipv6->sin6_scope_id); + tsem_fs_show_key(c, "address", "}}}", "%*phN", + (int) sizeof(ipv6->sin6_addr.in6_u.u6_addr8), + ipv6->sin6_addr.in6_u.u6_addr8); + break; + case AF_UNIX: + tsem_fs_show_field(c, "af_unix"); + tsem_fs_show_key(c, "address", "}}}", "%s", args->out.path); + break; + default: + tsem_fs_show_field(c, "af_other"); + tsem_fs_show_key(c, "address", "}}}", "%*phN", + tsem_digestsize(), args->out.mapping); + break; + } +} + +static void show_socket_accept(struct seq_file *c, struct tsem_event *ep) +{ + char *p; + int size; + struct tsem_socket_args *args = &ep->CELL.socket; + + show_event(c, ep); + + show_socket_info(c, "sock", ", ", &args->out.socka); + + tsem_fs_show_field(c, "addr"); + switch (args->out.socka.family) { + case AF_INET: + tsem_fs_show_field(c, "af_inet"); + tsem_fs_show_key(c, "port", ",", "%u", + args->out.ipv4.sin_port); + tsem_fs_show_key(c, "address", "}}}", "%u", + args->out.ipv4.sin_addr); + break; + case AF_INET6: + tsem_fs_show_field(c, "af_inet6"); + tsem_fs_show_key(c, "port", ",", "%u", + args->out.ipv6.sin6_port); + p = args->out.ipv6.sin6_addr.in6_u.u6_addr8; + size = sizeof(args->out.ipv6.sin6_addr.in6_u.u6_addr8); + tsem_fs_show_key(c, "address", "}}}", "%*phN", size, p); + break; + case AF_UNIX: + tsem_fs_show_field(c, "af_unix"); + tsem_fs_show_key(c, "address", "}}}", "%s", args->out.path); + break; + default: + tsem_fs_show_field(c, "af_other"); + tsem_fs_show_key(c, "address", "}}}", "%*phN", + tsem_digestsize(), args->out.mapping); + break; + } +} + +static void show_socket_value(struct seq_file *c, struct tsem_event *ep, + char *key) +{ + struct tsem_socket_args *args = &ep->CELL.socket; + + show_event(c, ep); + + show_socket_info(c, "sock", ", ", &args->out.socka); + tsem_fs_show_key(c, key, "}", "%d", args->value); +} + +static void show_socket_msg(struct seq_file *c, struct tsem_event *ep) +{ + int size; + struct sockaddr_in *ipv4; + struct sockaddr_in6 *ipv6; + struct tsem_socket_args *args = &ep->CELL.socket; + + show_event(c, ep); + + show_socket_info(c, "sock", args->out.have_addr ? ", " : "}", + &args->out.socka); + + if (args->out.have_addr) { + tsem_fs_show_field(c, "addr"); + switch (args->out.socka.family) { + case AF_INET: + ipv4 = &args->out.ipv4; + tsem_fs_show_field(c, "af_inet"); + tsem_fs_show_key(c, "port", ",", "%u", ipv4->sin_port); + tsem_fs_show_key(c, "address", "}}}", "%u", + ipv4->sin_addr.s_addr); + break; + case AF_INET6: + ipv6 = &args->out.ipv6; + size = sizeof(ipv6->sin6_addr.in6_u.u6_addr8); + tsem_fs_show_field(c, "af_inet6"); + tsem_fs_show_key(c, "port", ",", "%u", + ipv6->sin6_port); + tsem_fs_show_key(c, "address", "}}}", "%*phN", size, + ipv6->sin6_addr.in6_u.u6_addr8); + break; + } + } +} + +static void show_socket_argument(struct seq_file *c, struct tsem_event *ep) +{ + show_event(c, ep); + + show_socket_info(c, "sock", "}", &ep->CELL.socket.out.socka); +} + +static void show_socket_setsockopt(struct seq_file *c, struct tsem_event *ep) +{ + show_event(c, ep); + + show_socket_info(c, "sock", ", ", &ep->CELL.socket.out.socka); + tsem_fs_show_key(c, "level", ",", "%d", ep->CELL.socket.value); + tsem_fs_show_key(c, "optname", "}", "%d", ep->CELL.socket.optname); +} + +static void show_kernel_module_request(struct seq_file *c, + struct tsem_event *ep) +{ + struct tsem_kernel_args *args = &ep->CELL.kernel; + + show_event(c, ep); + tsem_fs_show_key(c, "kmod_name", "}", "%s", args->out.kmod_name); +} + +static void show_kernel_load_data(struct seq_file *c, struct tsem_event *ep) +{ + struct tsem_kernel_args *args = &ep->CELL.kernel; + + show_event(c, ep); + tsem_fs_show_key(c, "id", ",", "%d", args->id); + tsem_fs_show_key(c, "contents", "}", "%d", args->contents); +} + +static void show_kernel_read_file(struct seq_file *c, struct tsem_event *ep) +{ + struct tsem_kernel_args *args = &ep->CELL.kernel; + + show_event(c, ep); + + show_file(c, ", ", &args->out.file); + tsem_fs_show_key(c, "id", ",", "%d", args->id); + tsem_fs_show_key(c, "contents", "}", "%d", args->contents); +} + +static void show_task_kill(struct seq_file *c, struct tsem_event *ep) +{ + struct tsem_task_kill_args *args = &ep->CELL.task_kill; + + show_event(c, ep); + + tsem_fs_show_key(c, "target", ",", "%*phN", tsem_digestsize(), + args->target); + tsem_fs_show_key(c, "sig", ",", "%u", args->signal); + tsem_fs_show_key(c, "cross_ns", "}", "%u", args->cross_model); +} + +static void show_ptrace_access_check(struct seq_file *c, + struct tsem_event *ep) +{ + struct tsem_task_kill_args *args = &ep->CELL.task_kill; + + show_event(c, ep); + + tsem_fs_show_key(c, "child", ", ", "%*phN", tsem_digestsize(), + args->target); + tsem_fs_show_key(c, "mode", "}", "%u", args->u.resource); +} + +static void show_task_ptraceme(struct seq_file *c, struct tsem_event *ep) +{ + struct tsem_task_kill_args *args = &ep->CELL.task_kill; + + show_event(c, ep); + + tsem_fs_show_key(c, "source", "}", "%*phN", tsem_digestsize(), + args->source); +} + +static void show_capget(struct seq_file *c, struct tsem_event *ep) +{ + struct tsem_capability_args *args = &ep->CELL.capability; + + show_event(c, ep); + + tsem_fs_show_key(c, "target", ",", "%*phN", tsem_digestsize(), + args->target); + tsem_fs_show_key(c, "effective", ",", "0x%llx", args->effective); + tsem_fs_show_key(c, "inheritable", ",", "0x%llx", args->inheritable); + tsem_fs_show_key(c, "permitted", "}", "0x%llx", args->permitted); +} + +static void show_capset(struct seq_file *c, struct tsem_event *ep) +{ + struct tsem_capability_args *args = &ep->CELL.capability; + + show_event(c, ep); + + tsem_fs_show_key(c, "effective", ",", "0x%llx", args->effective); + tsem_fs_show_key(c, "inheritable", ",", "0x%llx", args->inheritable); + tsem_fs_show_key(c, "permitted", "}", "0x%llx", args->permitted); +} + +static void show_capable(struct seq_file *c, struct tsem_event *ep) +{ + struct tsem_capability_args *args = &ep->CELL.capability; + + show_event(c, ep); + + tsem_fs_show_key(c, "cap", ",", "%d", args->cap); + tsem_fs_show_key(c, "opts", "}", "%u", args->opts); +} + +static void show_task_setpgid(struct seq_file *c, struct tsem_event *ep) +{ + struct tsem_task_kill_args *args = &ep->CELL.task_kill; + + show_event(c, ep); + + tsem_fs_show_key(c, "task", ",", "%*phN", tsem_digestsize(), + args->target); + tsem_fs_show_key(c, "source", "}", "%*phN", tsem_digestsize(), + args->source); +} + +static void show_task_getpgid(struct seq_file *c, struct tsem_event *ep) +{ + struct tsem_task_kill_args *args = &ep->CELL.task_kill; + + show_event(c, ep); + + tsem_fs_show_key(c, "task", "}", "%*phN", tsem_digestsize(), + args->target); +} + +static void show_task_nice(struct seq_file *c, struct tsem_event *ep) +{ + struct tsem_task_kill_args *args = &ep->CELL.task_kill; + + show_event(c, ep); + + tsem_fs_show_key(c, "task", ",", "%*phN", tsem_digestsize(), + args->target); + tsem_fs_show_key(c, "nice", "}", "%d", args->u.value); +} + +static void show_task_prlimit(struct seq_file *c, struct tsem_event *ep) +{ + struct tsem_task_prlimit_args *args = &ep->CELL.task_prlimit; + + show_event(c, ep); + + show_creds(c, "cred", ", ", &args->out.cred); + show_creds(c, "tcred", ", ", &args->out.tcred); + tsem_fs_show_key(c, "flags", "}", "%d", args->flags); +} + +static void show_task_setrlimit(struct seq_file *c, struct tsem_event *ep) +{ + struct tsem_task_kill_args *args = &ep->CELL.task_kill; + + show_event(c, ep); + + tsem_fs_show_key(c, "task", ",", "%*phN", tsem_digestsize(), + args->target); + tsem_fs_show_field(c, "new_rlim"); + tsem_fs_show_key(c, "resource", ",", "%u", args->u.resource); + tsem_fs_show_key(c, "current", ",", "%llu", args->cur); + tsem_fs_show_key(c, "max", "}", "%llu", args->max); + seq_putc(c, '}'); +} + +static void show_task_prctl(struct seq_file *c, struct tsem_event *ep) +{ + struct tsem_task_prctl_args *args = &ep->CELL.task_prctl; + + show_event(c, ep); + + tsem_fs_show_key(c, "option", ",", "%d", args->option); + tsem_fs_show_key(c, "arg2", ",", "%llu", args->arg2); + tsem_fs_show_key(c, "arg3", ",", "%llu", args->arg3); + tsem_fs_show_key(c, "arg4", ",", "%llu", args->arg4); + tsem_fs_show_key(c, "arg5", "}", "%llu", args->arg5); +} + +static void show_task_value(struct seq_file *c, struct tsem_event *ep, + char *key) +{ + struct tsem_task_kill_args *args = &ep->CELL.task_kill; + + show_event(c, ep); + + tsem_fs_show_key(c, "task", ",", "%*phN", tsem_digestsize(), + args->target); + tsem_fs_show_key(c, key, "}", "%d", args->u.value); +} + +static void show_inode_getattr(struct seq_file *c, struct tsem_event *ep) +{ + struct tsem_inode_attr_args *args = &ep->CELL.inode_attr; + + show_event(c, ep); + + show_dentry(c, "path", "}", &args->out.dentry); +} + +static void show_inode_setattr(struct seq_file *c, struct tsem_event *ep) +{ + struct tsem_inode_attr_args *args = &ep->CELL.inode_attr; + + show_event(c, ep); + + show_dentry(c, "dentry", ", ", &args->out.dentry); + tsem_fs_show_field(c, "attr"); + tsem_fs_show_key(c, "valid", ",", "%u", args->out.valid); + tsem_fs_show_key(c, "mode", ",", "0%o", args->out.mode); + tsem_fs_show_key(c, "uid", ",", "%u", args->out.uid); + tsem_fs_show_key(c, "gid", ",", "%u", args->out.gid); + tsem_fs_show_key(c, "size", "}", "%lu", args->out.size); + seq_putc(c, '}'); +} + +static void show_inode_setxattr(struct seq_file *c, struct tsem_event *ep) +{ + struct tsem_inode_xattr_args *args = &ep->CELL.inode_xattr; + + show_event(c, ep); + + show_dentry(c, "dentry", ", ", &args->out.dentry); + tsem_fs_show_key(c, "name", ",", "%s", args->out.name); + tsem_fs_show_key(c, "value", ",", "%s", args->out.encoded_value); + tsem_fs_show_key(c, "flags", "}", "%d", args->out.flags); +} + +static void show_inode_getxattr(struct seq_file *c, struct tsem_event *ep) +{ + struct tsem_inode_xattr_args *args = &ep->CELL.inode_xattr; + + show_event(c, ep); + + show_dentry(c, "dentry", ", ", &args->out.dentry); + tsem_fs_show_key(c, "name", "}", "%s", args->out.name); +} + +static void show_inode_listxattr(struct seq_file *c, struct tsem_event *ep) +{ + struct tsem_inode_xattr_args *args = &ep->CELL.inode_xattr; + + show_event(c, ep); + + show_dentry(c, "dentry", "}", &args->out.dentry); +} + +static void show_key_alloc(struct seq_file *c, struct tsem_event *ep) +{ + struct tsem_key_args *args = &ep->CELL.key; + + show_event(c, ep); + + show_creds(c, "cred", ", ", &args->out.cred); + tsem_fs_show_key(c, "flags", "}", "%llu", args->flags); +} + +static void show_key_permission(struct seq_file *c, struct tsem_event *ep) +{ + struct tsem_key_args *args = &ep->CELL.key; + + show_event(c, ep); + + tsem_fs_show_field(c, "key_ref"); + tsem_fs_show_key(c, "possessed", ",", "%u", args->out.possessed); + tsem_fs_show_key(c, "uid", ",", "%u", args->out.uid); + tsem_fs_show_key(c, "gid", ",", "%u", args->out.gid); + tsem_fs_show_key(c, "flags", "}, ", "%lu", args->out.flags); + show_creds(c, "cred", ", ", &args->out.cred); + tsem_fs_show_key(c, "perm", "}", "%u", args->out.perm); +} + +static void show_sb_mount(struct seq_file *c, struct tsem_event *ep) +{ + struct tsem_sb_args *args = &ep->CELL.sb; + + show_event(c, ep); + + if (args->out.dev_name) + tsem_fs_show_key(c, "dev_name", ",", "%s", args->out.dev_name); + + show_path(c, "path", ", ", &args->out.path); + + if (args->out.type) + tsem_fs_show_key(c, "type", ",", "%s", args->out.type); + + tsem_fs_show_key(c, "flags", "}", "%lu", args->flags); +} + +static void show_sb_umount(struct seq_file *c, struct tsem_event *ep) +{ + struct tsem_sb_args *args = &ep->CELL.sb; + + show_event(c, ep); + + show_dentry(c, "mnt", ", ", &args->out.dentry); + tsem_fs_show_key(c, "flags", "}", "%d", args->flags); +} + +static void show_sb_remount(struct seq_file *c, struct tsem_event *ep) +{ + struct tsem_sb_args *args = &ep->CELL.sb; + + show_event(c, ep); + + tsem_fs_show_field(c, "sb"); + show_dentry(c, "dentry", ", ", &args->out.dentry); + tsem_fs_show_key(c, "fstype", ",", "%s", args->out.type); + tsem_fs_show_key(c, "s_flags", "}", "%lu", args->flags); + seq_putc(c, '}'); +} + +static void show_sb_statfs(struct seq_file *c, struct tsem_event *ep) +{ + struct tsem_sb_args *args = &ep->CELL.sb; + + show_event(c, ep); + + show_dentry(c, "dentry", "}", &args->out.dentry); +} + +static void show_move_path(struct seq_file *c, struct tsem_event *ep, + char *path1, char *path2) +{ + struct tsem_sb_args *args = &ep->CELL.sb; + + show_event(c, ep); + + show_path(c, path1, ", ", &args->out.path); + show_path(c, path2, "}", &args->out.path2); +} + +static void show_quotactl(struct seq_file *c, struct tsem_event *ep) +{ + struct tsem_quota_args *args = &ep->CELL.quota; + + show_event(c, ep); + + tsem_fs_show_key(c, "cmds", ",", "%d", args->cmds); + tsem_fs_show_key(c, "type", ",", "%d", args->type); + tsem_fs_show_key(c, "id", ",", "%d", args->id); + + tsem_fs_show_field(c, "sb"); + show_dentry(c, "dentry", ", ", &args->out.dentry); + tsem_fs_show_key(c, "fstype", ",", "%s", args->out.fstype); + tsem_fs_show_key(c, "s_flags", "}", "%d", args->out.s_flags); + seq_putc(c, '}'); +} + +static void show_quotaon(struct seq_file *c, struct tsem_event *ep) +{ + struct tsem_quota_args *args = &ep->CELL.quota; + + show_event(c, ep); + + show_dentry(c, "dentry", "}", &args->out.dentry); +} + +static void show_bpf(struct seq_file *c, struct tsem_event *ep) +{ + struct tsem_bpf_args *args = &ep->CELL.bpf; + + show_event(c, ep); + + tsem_fs_show_key(c, "cmd", ",", "%d", args->bpf.cmd); + tsem_fs_show_field(c, "attr"); + tsem_fs_show_key(c, "size", "}", "%u", args->bpf.size); + seq_putc(c, '}'); +} + +static void show_bpf_map(struct seq_file *c, struct tsem_event *ep) +{ + struct tsem_bpf_args *args = &ep->CELL.bpf; + + show_event(c, ep); + + tsem_fs_show_field(c, "map"); + tsem_fs_show_key(c, "map_type", "}, ", "%d", args->map.map_type); + tsem_fs_show_key(c, "fmode", "}", "%u", args->map.fmode); +} + +static void show_bpf_prog(struct seq_file *c, struct tsem_event *ep) +{ + struct tsem_bpf_args *args = &ep->CELL.bpf; + + show_event(c, ep); + + tsem_fs_show_field(c, "prog"); + tsem_fs_show_key(c, "type", ",", "%d", args->prog.type); + tsem_fs_show_key(c, "attach_type", "}", "%d", args->prog.attach_type); + seq_putc(c, '}'); +} + +static void show_event_generic(struct seq_file *c, struct tsem_event *ep) +{ + show_event(c, ep); + seq_putc(c, '}'); +} + +static void *trajectory_start(struct seq_file *c, loff_t *pos) +{ + struct list_head *end; + struct tsem_model *model = tsem_model(current); + + spin_lock(&model->trajectory_lock); + end = model->trajectory_list.prev; + spin_unlock(&model->trajectory_lock); + + mutex_lock(&model->trajectory_end_mutex); + model->trajectory_end = end; + + return seq_list_start(&model->trajectory_list, *pos); +} + +static void *trajectory_next(struct seq_file *c, void *p, loff_t *pos) +{ + struct list_head *next = ((struct list_head *) p)->next; + struct tsem_model *model = tsem_model(current); + + if (!model->trajectory_end) { + ++*pos; + return NULL; + } + + if (next == model->trajectory_end) + model->trajectory_end = NULL; + + return seq_list_next(p, &model->trajectory_list, pos); +} + +static void trajectory_stop(struct seq_file *c, void *pos) +{ + struct tsem_model *model = tsem_model(current); + + mutex_unlock(&model->trajectory_end_mutex); +} + +static int trajectory_show(struct seq_file *c, void *trajectory) +{ + struct tsem_event *ep; + + ep = list_entry(trajectory, struct tsem_event, list); + + seq_putc(c, '{'); + tsem_fs_show_trajectory(c, ep); + seq_puts(c, "}\n"); + + return 0; +} + +static const struct seq_operations trajectory_seqops = { + .start = trajectory_start, + .next = trajectory_next, + .stop = trajectory_stop, + .show = trajectory_show +}; + +static int trajectory_open(struct inode *inode, struct file *file) +{ + if (!can_access_fs()) + return -EACCES; + return seq_open(file, &trajectory_seqops); +} + +static const struct file_operations trajectory_ops = { + .open = trajectory_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + +static void *trajectory_count_start(struct seq_file *c, loff_t *pos) +{ + struct list_head *end; + struct tsem_model *model = tsem_model(current); + + spin_lock(&model->point_lock); + end = model->point_list.prev; + spin_unlock(&model->point_lock); + + mutex_lock(&model->point_end_mutex); + model->point_end = end; + + return seq_list_start(&model->point_list, *pos); +} + +static void *trajectory_count_next(struct seq_file *c, void *p, loff_t *pos) +{ + struct list_head *next = ((struct list_head *) p)->next; + struct tsem_model *model = tsem_model(current); + + if (!model->point_end) { + ++*pos; + return NULL; + } + + if (next == model->point_end) + model->point_end = NULL; + + return seq_list_next(p, &model->point_list, pos); +} + +static void trajectory_count_stop(struct seq_file *c, void *pos) +{ + struct tsem_model *model = tsem_model(current); + + mutex_unlock(&model->point_end_mutex); +} + +static int trajectory_count_show(struct seq_file *c, void *point) +{ + struct tsem_event_point *pt; + + pt = list_entry(point, struct tsem_event_point, list); + if (!pt->valid) + return 0; + + seq_printf(c, "%llu\n", pt->count); + return 0; +} + +static const struct seq_operations trajectory_count_seqops = { + .start = trajectory_count_start, + .next = trajectory_count_next, + .stop = trajectory_count_stop, + .show = trajectory_count_show +}; + +static int trajectory_count_open(struct inode *inode, struct file *file) +{ + if (!can_access_fs()) + return -EACCES; + return seq_open(file, &trajectory_count_seqops); +} + +static const struct file_operations trajectory_count_ops = { + .open = trajectory_count_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + +static void *trajectory_point_start(struct seq_file *c, loff_t *pos) +{ + struct list_head *end; + struct tsem_model *model = tsem_model(current); + + spin_lock(&model->point_lock); + end = model->point_list.prev; + spin_unlock(&model->point_lock); + + mutex_lock(&model->point_end_mutex); + model->point_end = end; + + return seq_list_start(&model->point_list, *pos); +} + +static void *trajectory_point_next(struct seq_file *c, void *p, loff_t *pos) +{ + struct list_head *next = ((struct list_head *) p)->next; + struct tsem_model *model = tsem_model(current); + + if (!model->point_end) { + ++*pos; + return NULL; + } + + if (next == model->point_end) + model->point_end = NULL; + + return seq_list_next(p, &model->point_list, pos); +} + +static void trajectory_point_stop(struct seq_file *c, void *pos) +{ + struct tsem_model *model = tsem_model(current); + + mutex_unlock(&model->point_end_mutex); +} + +static int trajectory_point_show(struct seq_file *c, void *point) +{ + struct tsem_event_point *pt; + + pt = list_entry(point, struct tsem_event_point, list); + if (!pt->valid) + return 0; + + seq_printf(c, "%*phN\n", tsem_digestsize(), pt->point); + return 0; +} + +static const struct seq_operations trajectory_point_seqops = { + .start = trajectory_point_start, + .next = trajectory_point_next, + .stop = trajectory_point_stop, + .show = trajectory_point_show +}; + +static int trajectory_point_open(struct inode *inode, struct file *file) +{ + if (!can_access_fs()) + return -EACCES; + return seq_open(file, &trajectory_point_seqops); +} + +static const struct file_operations trajectory_point_ops = { + .open = trajectory_point_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + +static int open_control(struct inode *inode, struct file *filp) +{ + if (!capable(CAP_MAC_ADMIN)) + return -EACCES; + if (!(filp->f_flags & O_WRONLY)) + return -EACCES; + return 0; +} + +static ssize_t write_control(struct file *file, const char __user *buf, + size_t datalen, loff_t *ppos) +{ + char *p, *arg, cmdbufr[128]; + ssize_t retn = -EINVAL; + enum tsem_control_type type; + + if (*ppos != 0) + goto done; + if (datalen > sizeof(cmdbufr)-1) + goto done; + + memset(cmdbufr, '\0', sizeof(cmdbufr)); + if (copy_from_user(cmdbufr, buf, datalen)) { + retn = -EFAULT; + goto done; + } + + p = strchr(cmdbufr, '\n'); + if (!p) + goto done; + *p = '\0'; + + arg = strchr(cmdbufr, ' '); + if (arg != NULL) { + *arg = '\0'; + ++arg; + } + + type = match_string(control_commands, ARRAY_SIZE(control_commands), + cmdbufr); + if (type < 0) + goto done; + + switch (type) { + case TSEM_CONTROL_EXTERNAL: + case TSEM_CONTROL_INTERNAL: + case TSEM_CONTROL_EXPORT: + retn = config_namespace(type, arg); + break; + case TSEM_CONTROL_ENFORCE: + case TSEM_CONTROL_SEAL: + retn = config_context(type, cmdbufr); + break; + case TSEM_CONTROL_TRUSTED: + case TSEM_CONTROL_UNTRUSTED: + retn = config_COE(type, arg); + break; + case TSEM_CONTROL_MAP_STATE: + case TSEM_CONTROL_MAP_PSEUDONYM: + case TSEM_CONTROL_MAP_BASE: + retn = config_point(type, arg); + break; + case TSEM_CONTROL_LOCK: + retn = tsem_nsmgr_lock(false); + break; + } + +done: + if (!retn) + retn = datalen; + return retn; +} + +static int release_control(struct inode *inode, struct file *file) +{ + return 0; +} + +static const struct file_operations control_ops = { + .open = open_control, + .write = write_control, + .release = release_control, + .llseek = generic_file_llseek, +}; + +static void *forensics_start(struct seq_file *c, loff_t *pos) +{ + struct list_head *end; + struct tsem_model *model = tsem_model(current); + + spin_lock(&model->forensics_lock); + end = model->forensics_list.prev; + spin_unlock(&model->forensics_lock); + + mutex_lock(&model->forensics_end_mutex); + model->forensics_end = end; + + return seq_list_start(&model->forensics_list, *pos); +} + +static void *forensics_next(struct seq_file *c, void *p, loff_t *pos) +{ + struct list_head *next = ((struct list_head *) p)->next; + struct tsem_model *model = tsem_model(current); + + if (!model->forensics_end) { + ++*pos; + return NULL; + } + + if (next == model->forensics_end) + model->forensics_end = NULL; + + return seq_list_next(p, &model->forensics_list, pos); +} + +static void forensics_stop(struct seq_file *c, void *pos) +{ + struct tsem_model *model = tsem_model(current); + + mutex_unlock(&model->forensics_end_mutex); +} + +static int forensics_show(struct seq_file *c, void *event) +{ + struct tsem_event *ep; + + ep = list_entry(event, struct tsem_event, list); + + seq_putc(c, '{'); + tsem_fs_show_trajectory(c, ep); + seq_puts(c, "}\n"); + + return 0; +} + +static const struct seq_operations forensics_seqops = { + .start = forensics_start, + .next = forensics_next, + .stop = forensics_stop, + .show = forensics_show +}; + +static int forensics_open(struct inode *inode, struct file *file) +{ + if (!can_access_fs()) + return -EACCES; + return seq_open(file, &forensics_seqops); +} + +static const struct file_operations forensics_ops = { + .open = forensics_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + +static void *forensics_point_start(struct seq_file *c, loff_t *pos) +{ + struct list_head *end; + struct tsem_model *model = tsem_model(current); + + spin_lock(&model->point_lock); + end = model->point_list.prev; + spin_unlock(&model->point_lock); + + mutex_lock(&model->point_end_mutex); + model->point_end = end; + + return seq_list_start(&model->point_list, *pos); +} + +static void *forensics_point_next(struct seq_file *c, void *p, loff_t *pos) +{ + struct list_head *next = ((struct list_head *) p)->next; + struct tsem_model *model = tsem_model(current); + + if (!model->point_end) { + ++*pos; + return NULL; + } + + if (next == model->point_end) + model->point_end = NULL; + + return seq_list_next(p, &model->point_list, pos); +} + +static void forensics_point_stop(struct seq_file *c, void *pos) +{ + struct tsem_model *model = tsem_model(current); + + mutex_unlock(&model->point_end_mutex); +} + +static int forensics_point_show(struct seq_file *c, void *point) +{ + struct tsem_event_point *pt; + + pt = list_entry(point, struct tsem_event_point, list); + if (pt->valid) + return 0; + + seq_printf(c, "%*phN\n", tsem_digestsize(), pt->point); + return 0; +} + +static const struct seq_operations forensics_point_seqops = { + .start = forensics_point_start, + .next = forensics_point_next, + .stop = forensics_point_stop, + .show = forensics_point_show +}; + +static int forensics_point_open(struct inode *inode, struct file *file) +{ + if (!can_access_fs()) + return -EACCES; + return seq_open(file, &forensics_point_seqops); +} + +static const struct file_operations forensics_point_ops = { + .open = forensics_point_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + +static void *forensics_count_start(struct seq_file *c, loff_t *pos) +{ + struct list_head *end; + struct tsem_model *model = tsem_model(current); + + spin_lock(&model->point_lock); + end = model->point_list.prev; + spin_unlock(&model->point_lock); + + mutex_lock(&model->point_end_mutex); + model->point_end = end; + + return seq_list_start(&model->point_list, *pos); +} + +static void *forensics_count_next(struct seq_file *c, void *p, loff_t *pos) +{ + struct list_head *next = ((struct list_head *) p)->next; + struct tsem_model *model = tsem_model(current); + + if (!model->point_end) { + ++*pos; + return NULL; + } + + if (next == model->point_end) + model->point_end = NULL; + + return seq_list_next(p, &model->point_list, pos); +} + +static void forensics_count_stop(struct seq_file *c, void *pos) +{ + struct tsem_model *model = tsem_model(current); + + mutex_unlock(&model->point_end_mutex); +} + +static int forensics_count_show(struct seq_file *c, void *point) +{ + struct tsem_event_point *pt; + + pt = list_entry(point, struct tsem_event_point, list); + if (pt->valid) + return 0; + + seq_printf(c, "%llu\n", pt->count); + return 0; +} + +static const struct seq_operations forensics_count_seqops = { + .start = forensics_count_start, + .next = forensics_count_next, + .stop = forensics_count_stop, + .show = forensics_count_show +}; + +static int forensics_count_open(struct inode *inode, struct file *file) +{ + if (!can_access_fs()) + return -EACCES; + return seq_open(file, &forensics_count_seqops); +} + +static const struct file_operations forensics_count_ops = { + .open = forensics_count_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + +static int measurement_show(struct seq_file *c, void *event) +{ + struct tsem_model *model = tsem_model(current); + + seq_printf(c, "%*phN\n", tsem_digestsize(), model->measurement); + return 0; +} + +static int measurement_open(struct inode *inode, struct file *file) +{ + if (!can_access_fs()) + return -EACCES; + return single_open(file, &measurement_show, NULL); +} + +static const struct file_operations measurement_ops = { + .open = measurement_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + +static int id_show(struct seq_file *c, void *event) +{ + seq_printf(c, "%llu\n", tsem_context(current)->id); + return 0; +} + +static int id_open(struct inode *inode, struct file *file) +{ + struct tsem_context *ctx = tsem_context(current); + + if (ctx->sealed) + return -EACCES; + return single_open(file, &id_show, NULL); +} + +static const struct file_operations id_ops = { + .open = id_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + +static int state_show(struct seq_file *m, void *v) +{ + struct tsem_model *model = tsem_model(current); + + tsem_model_compute_state(); + seq_printf(m, "%*phN\n", tsem_digestsize(), model->state); + return 0; +} + +static int state_open(struct inode *inode, struct file *file) +{ + if (!can_access_fs()) + return -EACCES; + return single_open(file, &state_show, NULL); +} + +static const struct file_operations state_ops = { + .open = state_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + +static int aggregate_show(struct seq_file *m, void *v) +{ + seq_printf(m, "%*phN\n", tsem_digestsize(), tsem_trust_aggregate()); + return 0; +} + +static int aggregate_open(struct inode *inode, struct file *file) +{ + if (!can_access_fs()) + return -EACCES; + return single_open(file, &aggregate_show, NULL); +} + +static const struct file_operations aggregate_ops = { + .open = aggregate_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + +static __poll_t export_poll(struct file *file, struct poll_table_struct *wait) +{ + struct tsem_context *ctx = tsem_context(current); + + if (!ctx->external) + return -ENOENT; + + poll_wait(file, &ctx->external->wq, wait); + + if (ctx->external->have_event) { + ctx->external->have_event = false; + return EPOLLIN | EPOLLRDNORM; + } + return 0; +} + +static int export_open(struct inode *inode, struct file *file) +{ + if (!capable(CAP_MAC_ADMIN)) + return -EACCES; + return single_open(file, &tsem_export_show, NULL); +} + +static const struct file_operations export_ops = { + .open = export_open, + .poll = export_poll, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + +/** + * tsem_fs_create_external() - Create an external TMA update file. + * @id: A pointer to the ASCII representation of the modeling domain + * that the export file is being created for. + * + * This function is used to create a pseudo-file that will output security + * event descriptions for a namespace. This routine will create the + * following file: + * + * /sys/kernel/security/tsem/ExternalTMA/N + * + * Where N is replaced with the security model context identifier. + * + * Return: If creation of the update file is successful a pointer to the + * dentry of the file is returned. If an error was encountered + * the pointer with an encoded code will be returned. + */ +struct dentry *tsem_fs_create_external(const char *name) +{ + struct dentry *dentry; + + dentry = securityfs_create_file(name, 0400, external_tma, NULL, + &export_ops); + if (!IS_ERR(dentry)) + tsem_inode(dentry->d_inode)->status = TSEM_INODE_CONTROL_PLANE; + return dentry; +} + +/** + * tsem_fs_show_trajectory() - Generate the output of a security event. + * @sf: A pointer to the seq_file structure to which output will + * be set. + * @ep: A pointer to the event description that is to be output. + * + * This function is used to generate a record that will be output to + * the pseudo-file that outputs the security events for the + * domain being modeled. + */ +void tsem_fs_show_trajectory(struct seq_file *c, struct tsem_event *ep) +{ + if (ep->no_params) + show_event_generic(c, ep); + + switch (ep->event) { + case TSEM_SYSLOG: + show_syslog(c, ep); + break; + case TSEM_SETTIME: + show_settime(c, ep); + break; + case TSEM_NETLINK_SEND: + show_netlink(c, ep); + break; + case TSEM_IPC_PERMISSION: + show_ipc_permission(c, ep); + break; + case TSEM_SHM_ASSOCIATE: + case TSEM_SHM_SHMAT: + show_ipc_shm_value(c, ep, "shmflg"); + break; + case TSEM_SHM_SHMCTL: + case TSEM_SEM_SEMCTL: + case TSEM_MSG_QUEUE_MSGCTL: + show_ipc_shm_value(c, ep, "cmd"); + break; + case TSEM_SEM_SEMOP: + show_sem_semop(c, ep); + break; + case TSEM_SEM_ASSOCIATE: + show_ipc_shm_value(c, ep, "semflg"); + break; + case TSEM_MSG_QUEUE_ASSOCIATE: + case TSEM_MSG_QUEUE_MSGSND: + show_ipc_shm_value(c, ep, "msqflg"); + break; + case TSEM_MSG_QUEUE_MSGRCV: + show_msg_queue_msgrcv(c, ep); + break; + case TSEM_INODE_CREATE: + case TSEM_INODE_MKDIR: + show_inode_create(c, ep); + break; + case TSEM_INODE_RMDIR: + case TSEM_INODE_UNLINK: + show_inode_remove(c, ep); + break; + case TSEM_INODE_LINK: + show_inode_link(c, ep); + break; + case TSEM_INODE_SYMLINK: + show_inode_symlink(c, ep); + break; + case TSEM_INODE_MKNOD: + show_inode_mknod(c, ep); + break; + case TSEM_INODE_RENAME: + show_inode_rename(c, ep); + break; + case TSEM_INODE_KILLPRIV: + show_inode_killpriv(c, ep); + break; + case TSEM_FILE_OPEN: + case TSEM_FILE_RECEIVE: + show_file_open(c, ep); + break; + case TSEM_MMAP_FILE: + show_mmap(c, ep); + break; + case TSEM_FILE_IOCTL: + case TSEM_FILE_LOCK: + case TSEM_FILE_FCNTL: + show_file_ioctl(c, ep); + break; + case TSEM_UNIX_STREAM_CONNECT: + case TSEM_UNIX_MAY_SEND: + show_socket_pair(c, ep, "sock", "other"); + break; + case TSEM_SOCKET_SOCKETPAIR: + show_socket_pair(c, ep, "socka", "sockb"); + break; + case TSEM_SOCKET_CREATE: + show_socket_create(c, ep); + break; + case TSEM_SOCKET_CONNECT: + case TSEM_SOCKET_BIND: + show_socket_connect_bind(c, ep); + break; + case TSEM_SOCKET_SENDMSG: + case TSEM_SOCKET_RECVMSG: + show_socket_msg(c, ep); + break; + case TSEM_SOCKET_GETSOCKNAME: + case TSEM_SOCKET_GETPEERNAME: + case TSEM_TUN_DEV_ATTACH_QUEUE: + show_socket_argument(c, ep); + break; + case TSEM_SOCKET_SETSOCKOPT: + show_socket_setsockopt(c, ep); + break; + case TSEM_SOCKET_ACCEPT: + show_socket_accept(c, ep); + break; + case TSEM_SOCKET_LISTEN: + show_socket_value(c, ep, "backlog"); + break; + case TSEM_SOCKET_SHUTDOWN: + show_socket_value(c, ep, "how"); + break; + case TSEM_KERNEL_MODULE_REQUEST: + show_kernel_module_request(c, ep); + break; + case TSEM_KERNEL_LOAD_DATA: + show_kernel_load_data(c, ep); + break; + case TSEM_KERNEL_READ_FILE: + show_kernel_read_file(c, ep); + break; + case TSEM_TASK_KILL: + show_task_kill(c, ep); + break; + case TSEM_PTRACE_ACCESS_CHECK: + show_ptrace_access_check(c, ep); + break; + case TSEM_PTRACE_TRACEME: + show_task_ptraceme(c, ep); + break; + case TSEM_CAPGET: + show_capget(c, ep); + break; + case TSEM_CAPSET: + show_capset(c, ep); + break; + case TSEM_CAPABLE: + show_capable(c, ep); + break; + case TSEM_TASK_SETPGID: + show_task_setpgid(c, ep); + break; + case TSEM_TASK_GETPGID: + case TSEM_TASK_GETSID: + case TSEM_TASK_GETIOPRIO: + case TSEM_TASK_SETSCHEDULER: + case TSEM_TASK_GETSCHEDULER: + show_task_getpgid(c, ep); + break; + case TSEM_TASK_SETNICE: + show_task_nice(c, ep); + break; + case TSEM_TASK_SETIOPRIO: + show_task_value(c, ep, "ioprio"); + break; + case TSEM_TASK_PRLIMIT: + show_task_prlimit(c, ep); + break; + case TSEM_TASK_SETRLIMIT: + show_task_setrlimit(c, ep); + break; + case TSEM_TASK_PRCTL: + show_task_prctl(c, ep); + break; + case TSEM_INODE_GETATTR: + show_inode_getattr(c, ep); + break; + case TSEM_INODE_SETATTR: + show_inode_setattr(c, ep); + break; + case TSEM_INODE_SETXATTR: + show_inode_setxattr(c, ep); + break; + case TSEM_INODE_GETXATTR: + case TSEM_INODE_REMOVEXATTR: + show_inode_getxattr(c, ep); + break; + case TSEM_INODE_LISTXATTR: + show_inode_listxattr(c, ep); + break; + case TSEM_KEY_ALLOC: + show_key_alloc(c, ep); + break; + case TSEM_KEY_PERMISSION: + show_key_permission(c, ep); + break; + case TSEM_SB_MOUNT: + show_sb_mount(c, ep); + break; + case TSEM_SB_UMOUNT: + show_sb_umount(c, ep); + break; + case TSEM_SB_REMOUNT: + show_sb_remount(c, ep); + break; + case TSEM_SB_STATFS: + show_sb_statfs(c, ep); + break; + case TSEM_SB_PIVOTROOT: + show_move_path(c, ep, "old_path", "new_path"); + break; + case TSEM_MOVE_MOUNT: + show_move_path(c, ep, "from_path", "to_path"); + break; + case TSEM_QUOTACTL: + show_quotactl(c, ep); + break; + case TSEM_QUOTA_ON: + show_quotaon(c, ep); + break; + case TSEM_BPF: + show_bpf(c, ep); + break; + case TSEM_BPF_MAP: + show_bpf_map(c, ep); + break; + case TSEM_BPF_PROG: + show_bpf_prog(c, ep); + break; + default: + break; + } +} + +/** + * tesm_fs_show_field() - Output a JSON field description + * @sf: A pointer to the seq_file structure that the field description + * is to be written to. + * @f: A pointer to null terminated character buffer containing the + * name of the field to encode + * + * This function is used to generate a JSON field description that + * is used to name a sequence of key/value pairs describing the + * characteristcis of the field. + */ +void tsem_fs_show_field(struct seq_file *c, const char *field) +{ + seq_printf(c, "\"%s\": {", field); +} + +/** + * tesm_fs_tsem_fs_show_key() - Output a JSON key/value pair + * @sf: A pointer to the seq_file structure that the field description + * is to be written to. + * @key: A pointer to the null-terminated character buffer containing + * the key description. + * @term: A pointer to a null-terminated character buffer containing + * the string that is to be used for terminating the key/value + * pair. + * @fmt: The printf format that is to be used for formatting the + * value of the key. + * + * This function is a variadic function that is used to encode a + * JSON key/value pair that provides one of characteristics of an + * event description field. + */ +void tsem_fs_show_key(struct seq_file *c, char *key, char *term, char *fmt, + ...) +{ + va_list args; + + seq_printf(c, "\"%s\": \"", key); + + va_start(args, fmt); + seq_vprintf(c, fmt, args); + va_end(args); + + if (term[0] == ',') + seq_printf(c, "\"%s ", term); + else + seq_printf(c, "\"%s", term); +} + +static bool _init_inode(struct dentry *dp) +{ + if (IS_ERR(dp)) + return false; + + tsem_inode(dp->d_inode)->status = TSEM_INODE_CONTROL_PLANE; + return true; +} + +/** + * tesm_fs_init() - Initialize the TSEM control filesystem heirarchy + * + * This function is called as part of the TSEM LSM initialization + * process. The purpose of this function is to create the TSEM + * control plane, based on the securityfs filesystem, by creating the + * /sys/kernel/security/tsem directory and populating that directory + * with the control plane files and internal TMA model information + * files. The /sys/kernel/security/tsem/ExternalTMA directory is + * also created. This directory will be used to hold the modeling + * domain specific files that will emit the security event descriptions + * for the domain. + * + * Return: If filesystem initialization is successful a return code of 0 + * is returned. A negative return value is returned if an error + * is encountered. + */ +int __init tsem_fs_init(void) +{ + int retn = -1; + + tsem_dir = securityfs_create_dir("tsem", NULL); + if (!_init_inode(tsem_dir)) + goto done; + + control = securityfs_create_file("control", 0200, tsem_dir, NULL, + &control_ops); + if (!_init_inode(control)) + goto err; + + id = securityfs_create_file("id", 0400, tsem_dir, NULL, &id_ops); + if (!_init_inode(id)) + goto err; + + aggregate = securityfs_create_file("aggregate", 0400, tsem_dir, NULL, + &aggregate_ops); + if (!_init_inode(aggregate)) + goto err; + + internal_tma = securityfs_create_dir("internal_tma", tsem_dir); + if (!_init_inode(internal_tma)) + goto err; + + model = securityfs_create_dir("model0", internal_tma); + if (!_init_inode(model)) + goto err; + + forensics = securityfs_create_file("forensics", 0400, model, NULL, + &forensics_ops); + if (!_init_inode(forensics)) + goto err; + + forensics_counts = securityfs_create_file("forensics_counts", 0400, + model, NULL, + &forensics_count_ops); + if (!_init_inode(forensics_counts)) + goto err; + + forensics_coeff = securityfs_create_file("forensics_coefficients", + 0400, model, NULL, + &forensics_point_ops); + if (!_init_inode(forensics_coeff)) + goto err; + + trajectory = securityfs_create_file("trajectory", 0400, model, NULL, + &trajectory_ops); + if (!_init_inode(trajectory)) + goto err; + + trajectory_counts = securityfs_create_file("trajectory_counts", 0400, + model, NULL, + &trajectory_count_ops); + if (!_init_inode(trajectory_counts)) + goto err; + + trajectory_coeff = securityfs_create_file("trajectory_coefficients", + 0400, model, NULL, + &trajectory_point_ops); + if (!_init_inode(trajectory_coeff)) + goto err; + + measurement = securityfs_create_file("measurement", 0400, + model, NULL, + &measurement_ops); + if (!_init_inode(measurement)) + goto err; + + state = securityfs_create_file("state", 0400, model, NULL, + &state_ops); + if (!_init_inode(state)) + goto err; + + external_tma = securityfs_create_dir("external_tma", tsem_dir); + if (!_init_inode(external_tma)) + goto err; + + retn = 0; + + done: + return retn; + + err: + securityfs_remove(tsem_dir); + securityfs_remove(control); + securityfs_remove(id); + securityfs_remove(aggregate); + securityfs_remove(internal_tma); + securityfs_remove(model); + securityfs_remove(forensics); + securityfs_remove(forensics_counts); + securityfs_remove(forensics_coeff); + securityfs_remove(trajectory); + securityfs_remove(trajectory_counts); + securityfs_remove(trajectory_coeff); + securityfs_remove(measurement); + securityfs_remove(state); + securityfs_remove(external_tma); + + return retn; +} From patchwork Mon Aug 26 10:37:21 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Dr. Greg" X-Patchwork-Id: 13777625 X-Patchwork-Delegate: paul@paul-moore.com Received: from blizzard.enjellic.com (wind.enjellic.com [76.10.64.91]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 204DF170A11; Mon, 26 Aug 2024 10:50:24 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=76.10.64.91 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1724669427; cv=none; b=QDvrcZBxNZFMpsf1yhFBgPGnQOOAE3RrNlUmTpYzniSqqFCZ0qR8w1UrkwBtoqW98aYDhYC5El3sWX9i+1YNkaQZCv+LHuS9uJniwMwMgQcRpgp8/GzG7Ef3BeE4SFx3pobwU/spF9uRWY64zAwujxOmpuymHTer1HIeg69LmKA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1724669427; c=relaxed/simple; bh=aVJOlXiet+dgYttI41OTey+YsAuz9WNL+K8Ty3dGVJU=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=F787m+ETkMUBP64NCDM/TyERn8J2DA1Mq8h4X74K+kCrzAJHTUDFQ3giqyIHru7OuXV2tFwZj5PXv+Nocf2b98z/SDmq7jdbkdkhgpp9V1IZ3W5D/nGWIdV725+IOcq91zT53g2f3pSabADRIIpk5OvXLmWoj8rcrhL0uIXlkTQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=enjellic.com; spf=pass smtp.mailfrom=enjellic.com; arc=none smtp.client-ip=76.10.64.91 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=enjellic.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=enjellic.com Received: from blizzard.enjellic.com (localhost [127.0.0.1]) by blizzard.enjellic.com (8.15.2/8.15.2) with ESMTP id 47QAbVw6003429; Mon, 26 Aug 2024 05:37:31 -0500 Received: (from greg@localhost) by blizzard.enjellic.com (8.15.2/8.15.2/Submit) id 47QAbVdv003428; Mon, 26 Aug 2024 05:37:31 -0500 X-Authentication-Warning: blizzard.enjellic.com: greg set sender to greg@enjellic.com using -f From: Greg Wettstein To: linux-security-module@vger.kernel.org, linux-kernel@vger.kernel.org Cc: jmorris@namei.org Subject: [PATCH v4 07/14] Add namespace implementation. Date: Mon, 26 Aug 2024 05:37:21 -0500 Message-Id: <20240826103728.3378-8-greg@enjellic.com> X-Mailer: git-send-email 2.39.1 In-Reply-To: <20240826103728.3378-1-greg@enjellic.com> References: <20240826103728.3378-1-greg@enjellic.com> Precedence: bulk X-Mailing-List: linux-security-module@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 The TSEM LSM has its own 'namespace' implementation for restricting the scope of a security model that is independent of other resource namespaces but which acts in much the same manner. The TSEM control plane is used to signal that a process should leave the root security modeling namespace and institute a new subordinate modeling namespace. Process that derive from the lead process all contribute security event descriptions to the model that is being used to prove the security behavior of the namespace. Each modeling domain has a unique numeric identifier that is implemented as an unsigned 64 bit value that is incremented each time a new security modeling namespace is created. This guarantees that every security modeling namespace will have a unique identifier associated with it. The id value of 0 is reserved for the root security modeling namespace. Each modeling domain is designated as either internally or externally modeled. An internally modeled domain has its security model implemented by a Trusted Modeling Agent (TMA) implementation that is run in the context of the kernel. Externally modeled domains have a description of the security event exported to a trust orchestrator running in userspace. That trust orchestrator has an associated Trusted Modeling Agent that implements the root of trust for the security namespace. A process that exports a security event description is scheduled away into an interruptible sleep state, with the exception of event handlers that are running in atomic context. The trust orchestrator that created the external modeling domain is responsible for using the TSEM control plane to wake the process up and set the trust status of the process to be trusted or untrusted. The trust orchestrator also has the responsibility of addressing the status of a workload that has generated a model violating event that occurs in atomic context. The namespace exists until the last task running in the context of the namespace exits. The resources associated with the namespace are released at that time. --- security/tsem/namespace.c | 530 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 530 insertions(+) create mode 100644 security/tsem/namespace.c diff --git a/security/tsem/namespace.c b/security/tsem/namespace.c new file mode 100644 index 000000000000..e265a1cc7d09 --- /dev/null +++ b/security/tsem/namespace.c @@ -0,0 +1,530 @@ +// SPDX-License-Identifier: GPL-2.0-only + +/* + * Copyright (C) 2024 Enjellic Systems Development, LLC + * Author: Dr. Greg Wettstein + * + * This file is responsible for managing the TSEM namespace + * implementation that allows security models to be implemented for + * workloads that are isolated from the root security modeling + * namespace. The namespaces that TSEM implements are + * non-heirarchical and are at only a single depth below the root + * security modeling namespace. + * + * Security modeling namespaces are identified by a 64-bit context + * identity with context identity 0 being reserved for the root + * security modeling namespace. + * + * The modeling of a namspace can be done by either an internal + * trusted modeling agent implementation or by a trusted modeling + * agent implementation associated with an external trust + * orchestrator. + * + * This file is responsible for creating and setting up a tsem_context + * structure that defines the modeling context being used by a + * security modeling namespace. This structure encapsulates + * functionality that is generic to the namespace, such as the + * cryptographic digest function that is used for security coefficient + * mapping. + * + * Information for managing externally modeled namespaces are + * maintained in the tsem_context structure while information for + * internally modeled namespaces is maintained in the tsem_model + * structure. These structures are populated in the tsem_context + * structure depending on whether or not an internal or externally + * modeled namespace is being created. + * + * A tsem_context structure has a kref structure that is used to track + * the number of uses of a context. This reference count is + * incremented each time a task is allocated in the context of a + * security modeling namespace and decremented when a task exists. + * + * The release of the last reference to a context causes the structure + * and its embedded structures to be released, thus freeing the + * resources that have been allocated for modeling the namespace. + */ + +#include "tsem.h" +#include "nsmgr.h" + +static u64 context_id; + +struct context_key { + struct list_head list; + u64 context_id; + u8 key[HASH_MAX_DIGESTSIZE]; +}; + +DEFINE_MUTEX(context_id_mutex); +LIST_HEAD(context_id_list); + +static void remove_task_key(u64 context_id) +{ + struct context_key *entry, *tmp_entry; + + list_for_each_entry_safe(entry, tmp_entry, &context_id_list, list) { + if (context_id == entry->context_id) { + list_del(&entry->list); + kfree(entry); + break; + } + } +} + +static int generate_task_key(const char *keystr, u64 context_id, + struct tsem_task *t_ttask, + struct tsem_task *p_ttask) +{ + int retn; + bool found_key, valid_key = false; + unsigned int size = tsem_digestsize(); + struct context_key *entry; + + while (!valid_key) { + get_random_bytes(t_ttask->task_key, size); + retn = tsem_ns_event_key(t_ttask->task_key, keystr, + p_ttask->task_key); + if (retn) + goto done; + + if (list_empty(&context_id_list)) + break; + + found_key = false; + list_for_each_entry(entry, &context_id_list, list) { + if (!memcmp(entry->key, p_ttask->task_key, size)) { + found_key = true; + break; + } + } + if (!found_key) + valid_key = true; + } + + entry = kzalloc(sizeof(*entry), GFP_KERNEL); + if (!entry) { + retn = -ENOMEM; + goto done; + } + + entry->context_id = context_id; + memcpy(entry->key, p_ttask->task_key, size); + list_add_tail(&entry->list, &context_id_list); + retn = 0; + + done: + return retn; +} + +static struct tsem_external *allocate_external(u64 context_id, + const char *keystr) +{ + int retn = -ENOMEM; + char bufr[20 + 1]; + struct tsem_external *external; + struct tsem_task *t_ttask = tsem_task(current); + struct tsem_task *p_ttask = tsem_task(current->real_parent); + + external = kzalloc(sizeof(*external), GFP_KERNEL); + if (!external) + goto done; + + retn = generate_task_key(keystr, context_id, t_ttask, p_ttask); + if (retn) + goto done; + + spin_lock_init(&external->export_lock); + INIT_LIST_HEAD(&external->export_list); + + init_waitqueue_head(&external->wq); + + scnprintf(bufr, sizeof(bufr), "%llu", context_id); + external->dentry = tsem_fs_create_external(bufr); + if (IS_ERR(external->dentry)) { + retn = PTR_ERR(external->dentry); + external->dentry = NULL; + } else + retn = 0; + + done: + if (retn) { + memset(t_ttask->task_key, '\0', tsem_digestsize()); + memset(p_ttask->task_key, '\0', tsem_digestsize()); + kfree(external); + remove_task_key(context_id); + external = ERR_PTR(retn); + } else + p_ttask->tma_for_ns = context_id; + + return external; +} + +static struct tsem_external *allocate_export(u64 context_id) +{ + int retn = -ENOMEM; + char bufr[20 + 1]; + struct tsem_external *external; + + external = kzalloc(sizeof(*external), GFP_KERNEL); + if (!external) + goto done; + + spin_lock_init(&external->export_lock); + INIT_LIST_HEAD(&external->export_list); + + init_waitqueue_head(&external->wq); + + scnprintf(bufr, sizeof(bufr), "%llu", context_id); + external->dentry = tsem_fs_create_external(bufr); + if (IS_ERR(external->dentry)) { + retn = PTR_ERR(external->dentry); + external->dentry = NULL; + } else + retn = 0; + + done: + if (retn) { + kfree(external); + external = ERR_PTR(retn); + } + return external; +} + +static void _release_inode_instances(u64 id, struct tsem_inode *tsip) +{ + struct tsem_inode_instance *owner, *tmp_owner; + + mutex_lock(&tsip->instance_mutex); + list_for_each_entry_safe(owner, tmp_owner, &tsip->instance_list, + list) { + if (id == owner->creator) { + list_del(&owner->list); + kfree(owner); + } + } + mutex_unlock(&tsip->instance_mutex); +} + +static void wq_put(struct work_struct *work) +{ + struct tsem_context *ctx; + struct tsem_inode_entry *ie, *tmp_ie; + + ctx = container_of(work, struct tsem_context, work); + + mutex_lock(&ctx->inode_mutex); + list_for_each_entry_safe(ie, tmp_ie, &ctx->inode_list, list) { + list_del(&ie->list); + _release_inode_instances(ctx->id, ie->tsip); + kfree(ie); + } + mutex_unlock(&ctx->inode_mutex); + + if (ctx->external) { + mutex_lock(&context_id_mutex); + remove_task_key(ctx->id); + mutex_unlock(&context_id_mutex); + + securityfs_remove(ctx->external->dentry); + tsem_export_magazine_free(ctx->external); + kfree(ctx->external); + } else + tsem_model_free(ctx); + + tsem_nsmgr_put(ctx->ops); + + crypto_free_shash(ctx->tfm); + tsem_event_magazine_free(ctx); + kfree(ctx->digestname); + kfree(ctx); +} + +static void ns_free(struct kref *kref) +{ + struct tsem_context *ctx; + + ctx = container_of(kref, struct tsem_context, kref); + + INIT_WORK(&ctx->work, wq_put); + if (!queue_work(system_wq, &ctx->work)) + WARN_ON_ONCE(1); +} + +/** + * tsem_ns_put() - Release a reference to a modeling context. + * @ctx: A pointer to the TMA context for which a reference is + * to be released. + * + * This function is called to release a reference to a TMA modeling + * domain. The release of the last reference calls the ns_free() + * function that schedules the actual work to release the resources + * associated with the namespace to a workqueue. + */ +void tsem_ns_put(struct tsem_context *ctx) +{ + kref_put(&ctx->kref, ns_free); +} + +/** + * tsem_ns_event_key() - Generate TMA authentication key. + * @task_key: A pointer to the buffer containing the task identification + * key that was randomly generated for the modeling domain. + * @keystr: A pointer to the buffer containing the TMA authentication key + * in ASCII hexadecimal form. + * + * This function generates the authentication key that will be used + * to validate a call by a TMA to set the trust status of the process. + * + * Return: This function returns 0 if the key was properly generated + * or a negative value if a hashing error occurred. + */ +int tsem_ns_event_key(u8 *task_key, const char *keystr, u8 *key) +{ + bool retn; + u8 tma_key[HASH_MAX_DIGESTSIZE]; + SHASH_DESC_ON_STACK(shash, tfm); + + retn = hex2bin(tma_key, keystr, tsem_digestsize()); + if (retn) + return -EINVAL; + + shash->tfm = tsem_digest(); + retn = crypto_shash_init(shash); + if (retn) + return retn; + + retn = crypto_shash_update(shash, task_key, tsem_digestsize()); + if (retn) + return retn; + + return crypto_shash_finup(shash, tma_key, tsem_digestsize(), key); +} + +static struct crypto_shash *configure_digest(const char *digest, + char **digestname, + u8 *zero_digest) +{ + int retn; + struct crypto_shash *tfm; + SHASH_DESC_ON_STACK(shash, tfm); + + *digestname = kstrdup(digest, GFP_KERNEL); + if (!*digestname) + return ERR_PTR(-ENOMEM); + + tfm = crypto_alloc_shash(digest, 0, 0); + if (IS_ERR(tfm)) + return tfm; + + shash->tfm = tfm; + retn = crypto_shash_digest(shash, NULL, 0, zero_digest); + if (retn) { + crypto_free_shash(tfm); + tfm = NULL; + } + + return tfm; +} + +/** + * tsem_ns_create() - Create a TSEM modeling namespace. + * @type: The type of namespace being created. + * @digest: A null terminated character buffer containing the name + * of the hash function that is to be used for the modeling + * domain. + * @ns: The enumeration type that specifies whether the security + * event descriptions should reference the initial user + * namespace or the current user namespace. + * @key: A pointer to a null-terminated buffer containing the key + * that will be used to authenticate the TMA's ability to set + * the trust status of a process. + * @cache_size: The number of entries to be implemented in the + * atomic allocation magazines for the security modeling + * namespace being created. + * @ops: A pointer to the security namespace definitions and + * operations for the namespace being created. + * + * This function is used to create either an internally or externally + * modeled TSEM namespace. The type of the namespace to be created + * is specified with the tsem_control_type enumeration value. A + * request for an internally model namespace causes a new structure to be + * allocated that will hold the description of the security model. + * An externally modeled domain will have a control structure allocated + * that manages the export of security event descriptions to the + * trust orchestrator that is responsible for running the TMA + * implementation. + * + * Return: This function returns 0 if the namespace was created and + * a negative error value on error. + */ +int tsem_ns_create(const enum tsem_control_type type, const char *digest, + const enum tsem_ns_reference ns, const char *key, + unsigned int cache_size, const struct tsem_context_ops *ops) +{ + u8 zero_digest[HASH_MAX_DIGESTSIZE]; + char *use_digest; + int retn = -ENOMEM; + u64 new_id; + struct tsem_task *tsk = tsem_task(current); + struct tsem_context *new_ctx; + struct tsem_model *model = NULL; + struct crypto_shash *tfm; + + tfm = configure_digest(digest, &use_digest, zero_digest); + if (IS_ERR(tfm)) + return PTR_ERR(tfm); + + new_ctx = kzalloc(sizeof(*new_ctx), GFP_KERNEL); + if (!new_ctx) + return retn; + + mutex_lock(&context_id_mutex); + new_id = context_id + 1; + + retn = tsem_event_magazine_allocate(new_ctx, cache_size); + if (retn) + goto done; + + if (type == TSEM_CONTROL_INTERNAL) { + model = tsem_model_allocate(cache_size); + if (!model) + goto done; + new_ctx->model = model; + } + if (type == TSEM_CONTROL_EXTERNAL) { + if (crypto_shash_digestsize(tfm)*2 != strlen(key)) { + retn = -EINVAL; + goto done; + } + + new_ctx->external = allocate_external(new_id, key); + if (IS_ERR(new_ctx->external)) { + retn = PTR_ERR(new_ctx->external); + new_ctx->external = NULL; + goto done; + } + + retn = tsem_export_magazine_allocate(new_ctx->external, + cache_size); + if (retn) + goto done; + } + if (type == TSEM_CONTROL_EXPORT) { + new_ctx->external = allocate_export(new_id); + if (IS_ERR(new_ctx->external)) { + retn = PTR_ERR(new_ctx->external); + new_ctx->external = NULL; + goto done; + } + + retn = tsem_export_magazine_allocate(new_ctx->external, + cache_size); + if (retn) + goto done; + + new_ctx->external->export_only = true; + } + + + kref_init(&new_ctx->kref); + + new_ctx->id = new_id; + new_ctx->tfm = tfm; + new_ctx->ops = ops; + new_ctx->digestname = use_digest; + new_ctx->timestamp = ktime_get_boottime_ns(); + memcpy(new_ctx->zero_digest, zero_digest, + crypto_shash_digestsize(tfm)); + + mutex_init(&new_ctx->inode_mutex); + INIT_LIST_HEAD(&new_ctx->inode_list); + + if (ns == TSEM_NS_CURRENT) + new_ctx->use_current_ns = true; + memcpy(new_ctx->actions, tsk->context->actions, + sizeof(new_ctx->actions)); + retn = 0; + + done: + if (retn) { + if (type != TSEM_CONTROL_EXPORT) + remove_task_key(new_id); + crypto_free_shash(tfm); + tsem_event_magazine_free(new_ctx); + kfree(use_digest); + if (new_ctx->external) + tsem_export_magazine_free(new_ctx->external); + kfree(new_ctx->external); + kfree(new_ctx); + kfree(model); + } else { + context_id = new_id; + tsk->context = new_ctx; + if (type == TSEM_CONTROL_EXTERNAL) + retn = tsem_export_aggregate(); + if (type == TSEM_CONTROL_INTERNAL) + retn = tsem_model_add_aggregate(); + } + + mutex_unlock(&context_id_mutex); + return retn; +} + +/** + * tsem_ns_export_root() - Configure root namespace for export only modeling. + * @magazine_size: The number of entries to be implemented in the event + * cache. + * + * This function is called to setup the root security modeling namespace + * for the export of security event descriptions in tsem_mode=2 + * operation. + * + * Return: This function returns 0 if the setup of the root namespace + * for export was successul and a negative error value if + * the setup fails. + */ +int tsem_ns_export_root(unsigned int magazine_size) +{ + int retn = -ENOMEM; + struct tsem_context *new_ctx; + struct tsem_task *tsk = tsem_task(current); + + new_ctx = kzalloc(sizeof(*new_ctx), GFP_KERNEL); + if (!new_ctx) + return retn; + *new_ctx = *tsem_context(current); + + new_ctx->external = allocate_export(tsem_context(current)->id); + if (IS_ERR(new_ctx->external)) { + retn = PTR_ERR(new_ctx->external); + new_ctx->external = NULL; + goto done; + } + + retn = tsem_export_magazine_allocate(new_ctx->external, magazine_size); + if (retn) + goto done; + + new_ctx->external->export_only = true; + + done: + if (retn) { + tsem_event_magazine_free(new_ctx); + if (new_ctx->external) + tsem_export_magazine_free(new_ctx->external); + kfree(new_ctx); + } else { + new_ctx->tfm = tsk->context->tfm; + new_ctx->timestamp = ktime_get_boottime_ns(); + new_ctx->digestname = tsk->context->digestname; + memcpy(new_ctx->zero_digest, tsk->context->zero_digest, + crypto_shash_digestsize(tsk->context->tfm)); + + mutex_init(&new_ctx->inode_mutex); + INIT_LIST_HEAD(&new_ctx->inode_list); + + tsk->context = new_ctx; + } + + return retn; +} From patchwork Mon Aug 26 10:37:22 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Dr. Greg" X-Patchwork-Id: 13777624 X-Patchwork-Delegate: paul@paul-moore.com Received: from blizzard.enjellic.com (wind.enjellic.com [76.10.64.91]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 60CF216EBF6; Mon, 26 Aug 2024 10:50:20 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=76.10.64.91 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1724669422; cv=none; b=earGbvlDvU1tstO7p6m7lzZ3eJsHDEvaTpjuwzvF8VJtyJYhN8BPHp8k8qPq3Mb8LCToULzI6PFwlDKR4c38eeO3vRCKeAhP6r+j1vhwoE+Ek4yKKGRW5aZZ1j4SMDbhOGOH34BLdtD2A1iRvdCd2jvyXWkaJcqwoWmel/sYrhU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1724669422; c=relaxed/simple; bh=/z+JnqtbVSHg9W4PWhw3uBaucGxOC9eOIFDgNO4XJMk=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=aHSDbsdTtnu0bw4mCZplgKSr0kI/yxRc2GbJ7LmMsVRnvsSTeC98S5yGEwTOw/YGO79P0HlXIvtSMI75gW+m73xUgb6O3SxG94fUeLOH4DcjhdEm/qSMhN2QmA9U3GPEt7gqEOxL+/InB1xNwH9+LlVnJNTKELUjSnJ2up3zR4Q= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=enjellic.com; spf=pass smtp.mailfrom=enjellic.com; arc=none smtp.client-ip=76.10.64.91 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=enjellic.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=enjellic.com Received: from blizzard.enjellic.com (localhost [127.0.0.1]) by blizzard.enjellic.com (8.15.2/8.15.2) with ESMTP id 47QAbVss003433; Mon, 26 Aug 2024 05:37:31 -0500 Received: (from greg@localhost) by blizzard.enjellic.com (8.15.2/8.15.2/Submit) id 47QAbVqG003432; Mon, 26 Aug 2024 05:37:31 -0500 X-Authentication-Warning: blizzard.enjellic.com: greg set sender to greg@enjellic.com using -f From: Greg Wettstein To: linux-security-module@vger.kernel.org, linux-kernel@vger.kernel.org Cc: jmorris@namei.org Subject: [PATCH v4 08/14] Add security event description export facility. Date: Mon, 26 Aug 2024 05:37:22 -0500 Message-Id: <20240826103728.3378-9-greg@enjellic.com> X-Mailer: git-send-email 2.39.1 In-Reply-To: <20240826103728.3378-1-greg@enjellic.com> References: <20240826103728.3378-1-greg@enjellic.com> Precedence: bulk X-Mailing-List: linux-security-module@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 The functionality for surfacing security model events to an external modeling domain is implemented in the export.c file. ASCII descriptions of the events are presented to a userspace trust orchestrator through the following pseudo-files: /sys/kernel/security/tsem/external_tma/N Where N is replaced with security namespace identifier. This identifier is a 64-bit integer value incremented each time a namespace is created The following event types are exported: export_event async_event aggregate_event log_event An export_event is used to surface the JSON encoded description of a security event to the trust orchestrator responsible for managing the security modeling namespace. The async_event is identical to an export_event but the alternate definition is used to indicate to the trust orchestrator that the process that generated the event is running in atomic context. The aggregate_event is used to provide the hardware platform measurement value to the trust orchestrator for integration into the security model by the associated trusted modeling agent. A log_event is used to provide descriptions of security events that are invoked by untrusted processes. The /sys/kernel/security/tsem/external_tma/N pseudo-files implement a pollable interface that the trust orchestrators can use to wait on events. After placing the event description into the device queue a process that is not running in atomic context is placed in an interruptible sleep state. After the TMA completes modeling of the event, the trust orchestrator is responsible for using the TSEM control plane to wake the process that exported the event and set its status to either trusted or untrusted. Security modeling namespaces, including the root modeling namespace, can be configured to run in export only mode. In this mode the security event descriptions are asynchronously exported to the trust orchestrator for driving applications such as machine learning models or other Host Intrusion Detection schemes. --- security/tsem/export.c | 429 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 429 insertions(+) create mode 100644 security/tsem/export.c diff --git a/security/tsem/export.c b/security/tsem/export.c new file mode 100644 index 000000000000..ce2a1a967a87 --- /dev/null +++ b/security/tsem/export.c @@ -0,0 +1,429 @@ +// SPDX-License-Identifier: GPL-2.0-only + +/* + * Copyright (C) 2024 Enjellic Systems Development, LLC + * Author: Dr. Greg Wettstein + * + * This file implements the export of security relevant events to + * an external trust orchestrator. The primary TSEM security + * documentation describes the types of events that are exported. + * + * The structures used to export each event are provided either by the + * kmem_cache implementation maintained in this file or from a + * magazine of structures that are maintained for events that are + * running in atomic context. + * + * The events are exported through the following control plane file: + * + * /sys/kernel/security/tsem/external_tma/N + * + * Where N is a filename consisting of the context identifier of the + * security modeling namespace that generated the event. + * + * A description of the security relevant event being exported is + * encoded in JSON format. The TSEM ABI documentation has a + * description of the encoding that is used. + * + * For export events that describe a security event the JSON encoding + * of the event description is provided by the + * tsem_fs_show_trajectory() function. This is the same function that + * is used to generate the security event descriptions for internally + * modeled namespaces. + * + * Processes that are generating security events in non-atomic context + * are put to sleep and placed on a wait queue to be scheduled back + * into execution by the external trust orchestrator after the event + * is modeled for conformance with the enforced security model. + * + * Processes running in atomic context export the event and continue + * to run. A trust orchestrator is responsible for determing how to + * address a workload that generates a model violating event, either + * by shutting down the workload or generating a security alert via + * the trust orchestration + * + * Security modeling namespaces, including the root namespace, can + * also be configured for export only mode where the only purpose of + * the export event is to hand a description of the event to + * userspace. This type of export is handled in the same manner as + * an event being modeled that is running in atomic context. + */ + +#include + +#include "tsem.h" + +enum export_type { + AGGREGATE_EVENT = 1, + EXPORT_EVENT, + EXPORT_ASYNC_EVENT, + LOG_EVENT +}; + +struct action_description { + enum export_type type; + enum tsem_action_type action; + char comm[TASK_COMM_LEN]; +}; + +struct export_event { + struct list_head list; + enum export_type type; + union { + u8 *aggregate[HASH_MAX_DIGESTSIZE]; + struct tsem_event *ep; + struct action_description action; + } u; +}; + +static const char * const tsem_actions[TSEM_ACTION_CNT] = { + "LOG", + "DENY" +}; + +static struct kmem_cache *export_cachep; + +static void refill_export_magazine(struct work_struct *work) +{ + struct export_event *exp; + struct tsem_external *ext; + struct tsem_work *ws; + + ws = container_of(work, struct tsem_work, work); + ext = ws->u.ext; + + exp = kmem_cache_zalloc(export_cachep, GFP_KERNEL); + if (!exp) { + pr_warn("tsem: Cannot refill event magazine.\n"); + return; + } + + spin_lock(&ws->u.ext->magazine_lock); + ws->u.ext->magazine[ws->index] = exp; + clear_bit(ws->index, ws->u.ext->magazine_index); + + /* + * The following memory barrier is used to cause the magazine + * index to be visible after the refill of the cache slot. + */ + smp_mb__after_atomic(); + spin_unlock(&ws->u.ext->magazine_lock); +} + +static struct export_event *allocate_export(bool locked) +{ + unsigned int index; + struct export_event *exp = NULL; + struct tsem_external *ext = tsem_context(current)->external; + + if (!locked) + return kmem_cache_zalloc(export_cachep, GFP_KERNEL); + + spin_lock(&ext->magazine_lock); + index = find_first_zero_bit(ext->magazine_index, ext->magazine_size); + if (index < ext->magazine_size) { + exp = ext->magazine[index]; + ext->ws[index].index = index; + ext->ws[index].u.ext = ext; + set_bit(index, ext->magazine_index); + + /* + * Similar to the issue noted in the refill_event_magazine() + * function, this barrier is used to cause the consumption + * of the cache entry to become visible. + + */ + smp_mb__after_atomic(); + } + + spin_unlock(&ext->magazine_lock); + + if (exp) { + INIT_WORK(&ext->ws[index].work, refill_export_magazine); + queue_work(system_highpri_wq, &ext->ws[index].work); + return exp; + } + + pr_warn("tsem: Fail export allocation comm %s ns %llu cs %u.\n", + current->comm, tsem_context(current)->id, ext->magazine_size); + return NULL; +} + +static void trigger_event(struct tsem_context *ctx) +{ + ctx->external->have_event = true; + wake_up_interruptible(&ctx->external->wq); +} + +int tsem_export_show(struct seq_file *sf, void *v) +{ + struct export_event *exp = NULL; + struct tsem_context *ctx = tsem_context(current); + + if (!ctx->id && !ctx->external) + return -ENODATA; + + spin_lock(&ctx->external->export_lock); + if (!list_empty(&ctx->external->export_list)) { + exp = list_first_entry(&ctx->external->export_list, + struct export_event, list); + list_del(&exp->list); + } + spin_unlock(&ctx->external->export_lock); + + if (!exp) + return -ENODATA; + + seq_putc(sf, '{'); + tsem_fs_show_field(sf, "export"); + + switch (exp->type) { + case AGGREGATE_EVENT: + tsem_fs_show_key(sf, "type", "}, ", "%s", "aggregate"); + tsem_fs_show_field(sf, "aggregate"); + tsem_fs_show_key(sf, "value", "}", "%*phN", tsem_digestsize(), + exp->u.aggregate); + break; + + case EXPORT_EVENT: + tsem_fs_show_key(sf, "type", "}, ", "%s", "event"); + tsem_fs_show_trajectory(sf, exp->u.ep); + tsem_event_put(exp->u.ep); + break; + + case EXPORT_ASYNC_EVENT: + tsem_fs_show_key(sf, "type", "}, ", "%s", "async_event"); + tsem_fs_show_trajectory(sf, exp->u.ep); + tsem_event_put(exp->u.ep); + break; + + case LOG_EVENT: + tsem_fs_show_key(sf, "type", "}, ", "%s", "log"); + tsem_fs_show_field(sf, "log"); + tsem_fs_show_key(sf, "process", ",", "%s", exp->u.action.comm); + tsem_fs_show_key(sf, "event", ",", "%s", + tsem_names[exp->u.action.type]); + tsem_fs_show_key(sf, "action", "}", "%s", + tsem_actions[exp->u.action.action]); + break; + } + seq_puts(sf, "}\n"); + + kmem_cache_free(export_cachep, exp); + return 0; +} + +/** + * tsem_export_event() - Export a security event description. + * @event: The TSEM event type number for which the log event is being + * generated. + * + * This function queues for export to an external modeling agent a + * security event description. + * + * Return: This function returns 0 if the export was successful or + * an error value if it was not. + */ +int tsem_export_event(struct tsem_event *ep) +{ + int retn = 0; + struct tsem_task *task = tsem_task(current); + struct tsem_context *ctx = task->context; + struct export_event *exp; + + exp = allocate_export(ep->locked); + if (!exp) + return -ENOMEM; + + exp->type = ep->locked ? EXPORT_ASYNC_EVENT : EXPORT_EVENT; + exp->u.ep = ep; + tsem_event_get(ep); + + spin_lock(&ctx->external->export_lock); + list_add_tail(&exp->list, &ctx->external->export_list); + spin_unlock(&ctx->external->export_lock); + + if (ctx->external->export_only || ep->locked) { + trigger_event(ctx); + return 0; + } + + task->trust_status |= TSEM_TASK_TRUST_PENDING; + trigger_event(ctx); + + while (task->trust_status & TSEM_TASK_TRUST_PENDING) { + set_current_state(TASK_INTERRUPTIBLE); + schedule(); + if (signal_pending(current)) { + if (sigismember(¤t->pending.signal, SIGKILL) || + sigismember(¤t->signal->shared_pending.signal, + SIGKILL)) + task->trust_status = TSEM_TASK_UNTRUSTED; + } + } + + return retn; +} + +/** + * tsem_export_action() - Exports the action taken to a security violation. + * @event: The TSEM event type number for which the log event is being + * generated. + * @locked: A boolean flag indicating whether or not the security hook + * being reported on is called in atomic context. + * + * This function queues for export a description of an event that + * was being disciplined. + * + * Return: This function returns 0 if the export was successful or + * an error value if it was not. + */ +int tsem_export_action(enum tsem_event_type event, bool locked) +{ + struct tsem_context *ctx = tsem_context(current); + struct export_event *exp; + + exp = allocate_export(locked); + if (!exp) { + pr_warn("tsem: domain %llu failed export allocation.\n", + ctx->id); + return -ENOMEM; + } + + exp->type = LOG_EVENT; + exp->u.action.type = event; + exp->u.action.action = ctx->actions[event]; + strscpy(exp->u.action.comm, current->comm, sizeof(exp->u.action.comm)); + + spin_lock(&ctx->external->export_lock); + list_add_tail(&exp->list, &ctx->external->export_list); + spin_unlock(&ctx->external->export_lock); + + trigger_event(ctx); + + return 0; +} + +/** + * tsem_export_aggregate() - Exports the hardware aggregate value. + * + * This function exports the hardware aggregate measurement for + * the platform on which the TSEM LSM is being run on. + * + * Return: This function returns a value of 0 if the export was + * successful or a non-zero return value if the export was + * not successful. + */ +int tsem_export_aggregate(void) +{ + struct tsem_context *ctx = tsem_context(current); + struct export_event *exp; + + exp = kmem_cache_zalloc(export_cachep, GFP_KERNEL); + if (!exp) + return -ENOMEM; + + exp->type = AGGREGATE_EVENT; + memcpy(exp->u.aggregate, tsem_trust_aggregate(), tsem_digestsize()); + + spin_lock(&ctx->external->export_lock); + list_add_tail(&exp->list, &ctx->external->export_list); + spin_unlock(&ctx->external->export_lock); + + trigger_event(ctx); + + return 0; +} + +/** + * tsem export_magazine_allocate() - Allocate a TSEM export magazine. + * @ctx: A pointer to the external modeling context that the magazine is + * to be allocated for. + * @size: The number of entries to be created in the magazine. + + * The security event export magazine is an array of export_event + * structures that are used to service security hooks that are called + * in atomic context. Each external modeling domain has a magazine + * allocated to it and this function allocates and initializes the + * memory structures needed to manage that magazine. + + * Return: This function returns a value of zero on success and a negative + * error code on failure. + */ +int tsem_export_magazine_allocate(struct tsem_external *ext, size_t size) +{ + unsigned int lp; + int retn = -ENOMEM; + + ext->magazine_size = size; + + spin_lock_init(&ext->magazine_lock); + + ext->magazine_index = bitmap_zalloc(ext->magazine_size, GFP_KERNEL); + if (!ext->magazine_index) + return retn; + + ext->magazine = kcalloc(ext->magazine_size, sizeof(*ext->magazine), + GFP_KERNEL); + if (!ext->magazine) + goto done; + + for (lp = 0; lp < ext->magazine_size; ++lp) { + ext->magazine[lp] = kmem_cache_zalloc(export_cachep, + GFP_KERNEL); + if (!ext->magazine[lp]) + goto done; + } + + ext->ws = kcalloc(ext->magazine_size, sizeof(*ext->ws), GFP_KERNEL); + if (ext->ws) + retn = 0; + + done: + if (retn) + tsem_export_magazine_free(ext); + + return retn; +} + +/** + * tsem export_magazine_free() - Releases a TSEM export magazine + * @ctx: A pointer to the external modeling context whose magazine is + * to be released. + * + * The function is used to free the memory that was allocated by + * the tsem_export_magazine_allocate() function for an extenral + * modeling context. + */ +void tsem_export_magazine_free(struct tsem_external *ext) +{ + unsigned int lp; + + for (lp = 0; lp < ext->magazine_size; ++lp) + kmem_cache_free(export_cachep, ext->magazine[lp]); + + bitmap_free(ext->magazine_index); + kfree(ext->ws); + kfree(ext->magazine); +} + +/** + * tsem export_cache_init() - Initialize the TSEM export cache. + * + * This function is called by the TSEM initialization function and sets + * up a cache for export structures that are called by security event + * descriptions that are generated in atomix context + * + * Return: This function returns a value of zero on success and a negative + * error code on failure. + */ +int __init tsem_export_cache_init(void) +{ + + export_cachep = kmem_cache_create("tsem_export_cache", + sizeof(struct export_event), 0, + SLAB_PANIC, 0); + if (!export_cachep) + return -ENOMEM; + + return 0; +} From patchwork Mon Aug 26 10:37:23 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Dr. Greg" X-Patchwork-Id: 13777628 X-Patchwork-Delegate: paul@paul-moore.com Received: from blizzard.enjellic.com (wind.enjellic.com [76.10.64.91]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 62FF714A4FB; Mon, 26 Aug 2024 10:50:37 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=76.10.64.91 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1724669440; cv=none; b=tDdGZ7hyfwcCKPhhOL+JrbUXkbVL4kZUmg763tmiu6LVzSa57uEuhhbiWket8XdnbeAPMSe7ULkAIA/KeU/fK79ZrXkjUtWYM80/EvtX9kY+Raybn90fX5z/XM/f9bH1wEtfRhMpDOPgO6ZRhrcgyqNKlknuJG8PyWQPHHj+n5U= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1724669440; c=relaxed/simple; bh=t3et2Jvj+lrQe0nbfcgBOKovdFJRfiTrtDBPDSNsNvk=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=VsxIwZHLt1BCzcAsHrcrgL29oeOdMXj7e8521r5LqOP6vsp6rPxyb20qZ4h0FXjb4KWp1EZwuZxxDii1Eya4KVpro/Y5piSHrsmO7naAtOHMzcHZQYoN3t4eja8iTiJJD67Dt7btjq/RvAj8j/eHLV1/tuXvGc22OzygZAOr+C0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=enjellic.com; spf=pass smtp.mailfrom=enjellic.com; arc=none smtp.client-ip=76.10.64.91 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=enjellic.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=enjellic.com Received: from blizzard.enjellic.com (localhost [127.0.0.1]) by blizzard.enjellic.com (8.15.2/8.15.2) with ESMTP id 47QAbV76003437; Mon, 26 Aug 2024 05:37:31 -0500 Received: (from greg@localhost) by blizzard.enjellic.com (8.15.2/8.15.2/Submit) id 47QAbV6r003436; Mon, 26 Aug 2024 05:37:31 -0500 X-Authentication-Warning: blizzard.enjellic.com: greg set sender to greg@enjellic.com using -f From: Greg Wettstein To: linux-security-module@vger.kernel.org, linux-kernel@vger.kernel.org Cc: jmorris@namei.org Subject: [PATCH v4 09/14] Add event processing implementation. Date: Mon, 26 Aug 2024 05:37:23 -0500 Message-Id: <20240826103728.3378-10-greg@enjellic.com> X-Mailer: git-send-email 2.39.1 In-Reply-To: <20240826103728.3378-1-greg@enjellic.com> References: <20240826103728.3378-1-greg@enjellic.com> Precedence: bulk X-Mailing-List: linux-security-module@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 The event.c file implements support for packaging the description of an event into its Context Of Execution (COE) and CELL components for subsequent modeling by an internal Trusted Modeling Agent or export to a trust orchestrator. The tsem_event_allocate() function is called by every security event handler to allocate a structure that will be used to retain the characteristics of the security event until the namespace terminates or the event is exported to userspace. The tsem_init_init() function converts the parameters supplied to the security handler to the values that will be retained. The event description structures are allocated from a kmem_cache based allocation system. In addition to allocations from this source, a magazine of pre-allocated structures is used to serve requests for security events by processes running in atomic context. The magazine slots are refilled by work functions dispatched to the system high priority workqueue. --- security/tsem/event.c | 1846 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1846 insertions(+) create mode 100644 security/tsem/event.c diff --git a/security/tsem/event.c b/security/tsem/event.c new file mode 100644 index 000000000000..349108be77c8 --- /dev/null +++ b/security/tsem/event.c @@ -0,0 +1,1846 @@ +// SPDX-License-Identifier: GPL-2.0-only + +/* + * Copyright (C) 2024 Enjellic Systems Development, LLC + * Author: Dr. Greg Wettstein + * + * This file handles the creation and population of the tsem_event + * structure that describes each security event that occurs. The + * structures that are held in the CELL union of the tsem_event + * structure are used to deliver the characterizing parameters of a + * security into the tsem_event_init() function. + * + * Most of the structures used to characterize a security event use + * a strategy where a union is used to enclose an 'in' and 'out' + * structure. The parameters that are relevant to the TSEM + * characterization of an event are placed in the 'in' structure. The + * routines in this function are responsible for propagating these + * characteristics into the 'out' structure for the lifetime of the + * structure. + */ + +#include +#include +#include +#include +#include + +#include "../integrity/integrity.h" +#include "tsem.h" + +static struct kmem_cache *event_cachep; +static bool created_inode(struct tsem_inode *tsip) +{ + return tsip->created && tsip->creator == tsem_context(current)->id; +} + +static int register_directory(struct tsem_inode *tsip) +{ + int retn = 0; + struct tsem_inode_entry *entry = NULL; + struct tsem_context *ctx = tsem_context(current); + + mutex_lock(&ctx->inode_mutex); + list_for_each_entry(entry, &ctx->inode_list, list) { + if (entry->tsip == tsip) + goto done; + } + + entry = kzalloc(sizeof(*entry), GFP_KERNEL); + if (!entry) { + retn = -ENOMEM; + goto done; + } + + entry->tsip = tsip; + list_add_tail(&entry->list, &ctx->inode_list); + + done: + mutex_unlock(&ctx->inode_mutex); + return retn; +} + +static int register_inode_create(struct inode *dir, u64 instance, + char *pathname) +{ + char *p; + int retn; + struct tsem_inode_instance *tio = NULL; + struct tsem_inode *tsip = tsem_inode(dir); + + p = strrchr(pathname, '/'); + if (!p) + return -EINVAL; + + if (tsem_context(current)->id) { + retn = register_directory(tsip); + if (retn) + return retn; + } + + tio = kzalloc(sizeof(*tio), GFP_KERNEL); + if (!tio) + return -ENOMEM; + + tio->pathname = ++p; + tio->creator = tsem_context(current)->id; + tio->instance = instance; + memcpy(tio->owner, tsem_task(current)->task_id, tsem_digestsize()); + + mutex_lock(&tsip->create_mutex); + list_add_tail(&tio->list, &tsip->create_list); + mutex_unlock(&tsip->create_mutex); + + return 0; +} + +static int get_root(struct dentry *dentry, struct tsem_path *path) +{ + int size, retn = 0; + char *p, *p1, *pathbuffer = NULL; + struct super_block *sb = dentry->d_sb; + struct inode *inode = d_backing_inode(sb->s_root); + + if (MAJOR(sb->s_dev) || inode->i_op->rename) + path->dev = sb->s_dev; + else { + if (dentry->d_op && dentry->d_op->d_dname) { + pathbuffer = __getname(); + p = dentry->d_op->d_dname(dentry, pathbuffer, 4096); + p1 = strchr(p, ':'); + if (p1) + *p1 = '\0'; + + size = strlen(p) + 3; + path->pathname = kmalloc(size, GFP_KERNEL); + if (IS_ERR(path->pathname)) { + retn = PTR_ERR(path->pathname); + goto done; + } + + strscpy(path->pathname, p, size); + strcat(path->pathname, ":/"); + __putname(pathbuffer); + } else { + p = "nodev:/"; + size = strlen(p) + 1; + + path->pathname = kmalloc(size, GFP_KERNEL); + if (IS_ERR(path->pathname)) { + retn = PTR_ERR(path->pathname); + goto done; + } + + strscpy(path->pathname, p, size); + } + } + + done: + return retn; +} + +static char *get_path(const struct path *path) +{ + int retn = 0; + const char *pathname = NULL; + char *retpath, *pathbuffer = NULL; + + pathbuffer = __getname(); + if (pathbuffer) { + pathname = d_absolute_path(path, pathbuffer, PATH_MAX); + if (IS_ERR(pathname)) { + __putname(pathbuffer); + pathbuffer = NULL; + pathname = NULL; + } + } + + if (pathname) + retpath = kstrdup(pathname, GFP_KERNEL); + else + retpath = kstrdup(path->dentry->d_name.name, GFP_KERNEL); + if (!retpath) + retn = -ENOMEM; + + if (pathbuffer) + __putname(pathbuffer); + if (retn) + retpath = ERR_PTR(retn); + return retpath; +} + +static char *get_path_dentry(const struct dentry *dentry) +{ + int retn = 0; + const char *pathname = NULL; + char *retpath, *pathbuffer = NULL; + + pathbuffer = __getname(); + if (pathbuffer) { + pathname = dentry_path_raw(dentry, pathbuffer, PATH_MAX); + if (IS_ERR(pathname)) { + __putname(pathbuffer); + pathbuffer = NULL; + pathname = NULL; + } + } + + if (pathname) + retpath = kstrdup(pathname, GFP_KERNEL); + else + retpath = kstrdup(dentry->d_name.name, GFP_KERNEL); + if (!retpath) + retn = -ENOMEM; + + if (pathbuffer) + __putname(pathbuffer); + if (retn) + retpath = ERR_PTR(retn); + return retpath; +} + +static void _fill_mount_path(struct tsem_inode *tsip, struct tsem_path *path) +{ + struct tsem_inode_instance *entry, *retn = NULL; + + mutex_lock(&tsem_model(current)->mount_mutex); + list_for_each_entry(entry, &tsem_model(current)->mount_list, list) { + if (!strcmp(entry->pathname, path->pathname)) { + retn = entry; + break; + } + } + + if (retn) { + path->created = true; + path->creator = retn->creator; + path->instance = retn->instance; + memcpy(path->owner, retn->owner, tsem_digestsize()); + } + mutex_unlock(&tsem_model(current)->mount_mutex); +} + +static int fill_path(const struct path *in, struct tsem_path *path) +{ + int retn; + struct tsem_model *model = tsem_model(current); + struct tsem_inode *tsip = tsem_inode(d_backing_inode(in->dentry)); + + if (created_inode(tsip)) { + path->created = tsip->created; + path->creator = tsip->creator; + path->instance = tsip->instance; + memcpy(path->owner, tsip->owner, tsem_digestsize()); + } + + retn = get_root(in->dentry, path); + if (retn) + goto done; + + if (path->dev) { + path->pathname = get_path(in); + if (IS_ERR(path->pathname)) + retn = PTR_ERR(path->pathname); + } + + if (model && !list_empty(&model->mount_list)) + _fill_mount_path(tsip, path); + + done: + return retn; +} + +static int fill_path_dentry(struct dentry *dentry, struct tsem_path *path) +{ + int retn; + + retn = get_root(dentry, path); + if (retn) + goto done; + + path->pathname = get_path_dentry(dentry); + if (IS_ERR(path->pathname)) + retn = PTR_ERR(path->pathname); + + done: + return retn; +} + +static struct tsem_inode_digest *find_digest(struct tsem_inode *tsip) +{ + struct tsem_inode_digest *digest; + + list_for_each_entry(digest, &tsip->digest_list, list) { + if (!strcmp(digest->name, tsem_context(current)->digestname)) + return digest; + } + + return NULL; +} + +static struct tsem_inode_digest *add_digest(struct tsem_context *ctx, + struct tsem_inode *tsip) +{ + struct tsem_inode_digest *digest; + + digest = kzalloc(sizeof(*digest), GFP_KERNEL); + if (!digest) + return NULL; + + digest->name = kstrdup(tsem_context(current)->digestname, GFP_KERNEL); + if (!digest->name) + return NULL; + + list_add(&digest->list, &tsip->digest_list); + + return digest; +} + +static struct file *open_event_file(struct file *file, unsigned int *status) +{ + int flags; + struct file *alt_file; + + if (!(file->f_mode & FMODE_CAN_READ)) { + file->f_mode |= FMODE_CAN_READ; + *status |= 4; + } + if (file->f_mode & FMODE_READ) + return file; + + flags = file->f_flags & ~(O_WRONLY | O_APPEND | O_TRUNC | O_CREAT | + O_NOCTTY | O_EXCL); + flags |= O_RDONLY; + + alt_file = dentry_open(&file->f_path, flags, file->f_cred); + if (!IS_ERR(alt_file)) { + *status |= 1; + return alt_file; + } + + file->f_flags |= FMODE_READ; + *status |= 2; + return file; +} + +static int get_file_digest(struct file *file, struct inode *inode, + loff_t size, u8 *digest) +{ + u8 *bufr; + int retn = 0, rsize; + unsigned int open_status = 0; + loff_t posn = 0; + struct file *read_file; + SHASH_DESC_ON_STACK(shash, tfm); + + shash->tfm = tsem_digest(); + retn = crypto_shash_init(shash); + if (retn) + goto done; + + bufr = kzalloc(PAGE_SIZE, GFP_KERNEL); + if (!bufr) { + retn = -ENOMEM; + goto done; + } + + if (!likely(file->f_op->read || file->f_op->read_iter)) { + retn = -EINVAL; + goto done; + } + read_file = open_event_file(file, &open_status); + + while (posn < size) { + rsize = integrity_kernel_read(read_file, posn, bufr, 4096); + if (rsize < 0) { + retn = rsize; + break; + } + if (rsize == 0) + break; + + posn += rsize; + retn = crypto_shash_update(shash, bufr, rsize); + if (retn) + break; + } + + kfree(bufr); + if (!retn) + retn = crypto_shash_final(shash, digest); + + done: + if (open_status & 1) + fput(read_file); + if (open_status & 2) + file->f_flags &= ~FMODE_READ; + if (open_status & 4) + file->f_flags &= ~FMODE_CAN_READ; + return retn; +} + +static int add_file_digest(struct file *file, bool pseudo_file, + struct tsem_file_args *args) +{ + int retn = 0; + u8 measurement[HASH_MAX_DIGESTSIZE]; + loff_t size; + struct inode *inode; + struct tsem_inode *tsip; + struct tsem_inode_digest *digest; + struct tsem_context *ctx = tsem_context(current); + + inode = file_inode(file); + tsip = tsem_inode(inode); + + if (created_inode(tsip) || pseudo_file || !S_ISREG(inode->i_mode)) { + memcpy(args->out.digest, ctx->zero_digest, tsem_digestsize()); + return 0; + } + + mutex_lock(&tsip->digest_mutex); + if (!ctx->external) { + retn = tsem_model_has_pseudonym(tsip, args->out.path.pathname); + if (retn < 0) + goto done; + if (retn) { + memcpy(args->out.digest, ctx->zero_digest, + tsem_digestsize()); + retn = 0; + goto done; + } + } + + size = i_size_read(inode); + if (!size) { + memcpy(args->out.digest, ctx->zero_digest, tsem_digestsize()); + goto done; + } + + digest = find_digest(tsip); + + if (digest && inode_eq_iversion(inode, digest->version) && + tsip->status == TSEM_INODE_COLLECTED) { + memcpy(args->out.digest, digest->value, tsem_digestsize()); + goto done; + } + + tsip->status = TSEM_INODE_COLLECTING; + retn = get_file_digest(file, inode, size, measurement); + if (retn) { + tsip->status = 0; + goto done; + } + + if (!digest) { + digest = add_digest(ctx, tsip); + if (!digest) { + retn = -ENOMEM; + goto done; + } + } + + memcpy(args->out.digest, measurement, tsem_digestsize()); + memcpy(digest->value, measurement, tsem_digestsize()); + digest->version = inode_query_iversion(inode); + tsip->status = TSEM_INODE_COLLECTED; + + done: + mutex_unlock(&tsip->digest_mutex); + return retn; +} + +static void fill_inode(struct inode *inode, struct tsem_inode_cell *ip) +{ + struct user_namespace *ns; + + if (tsem_context(current)->use_current_ns) + ns = current_user_ns(); + else + ns = &init_user_ns; + + ip->uid = from_kuid(ns, inode->i_uid); + ip->gid = from_kgid(ns, inode->i_gid); + ip->mode = inode->i_mode; + ip->s_magic = inode->i_sb->s_magic; + memcpy(ip->s_id, inode->i_sb->s_id, sizeof(ip->s_id)); + memcpy(ip->s_uuid, inode->i_sb->s_uuid.b, sizeof(ip->s_uuid)); +} + +static void fill_creds(const struct cred *cp, struct tsem_COE *tcp) +{ + struct user_namespace *ns; + + if (tsem_context(current)->use_current_ns) + ns = current_user_ns(); + else + ns = &init_user_ns; + + tcp->uid = from_kuid(ns, cp->uid); + tcp->euid = from_kuid(ns, cp->euid); + tcp->suid = from_kuid(ns, cp->suid); + + tcp->gid = from_kgid(ns, cp->gid); + tcp->egid = from_kgid(ns, cp->egid); + tcp->sgid = from_kgid(ns, cp->sgid); + + tcp->fsuid = from_kuid(ns, cp->fsuid); + tcp->fsgid = from_kgid(ns, cp->fsgid); + + tcp->capeff.mask = cp->cap_effective; + + tcp->securebits = cp->securebits; +} + +static int fill_dentry(struct dentry *dp, struct tsem_dentry *dentry) +{ + int retn; + struct tsem_inode *tsip; + + retn = fill_path_dentry(dp, &dentry->path); + if (retn) + return retn; + + if (d_backing_inode(dp)) { + dentry->have_inode = true; + fill_inode(d_backing_inode(dp), &dentry->inode); + + tsip = tsem_inode(d_backing_inode(dp)); + dentry->path.created = tsip->created; + dentry->path.creator = tsip->creator; + dentry->path.instance = tsip->instance; + memcpy(dentry->path.owner, tsem_task(current)->task_id, + tsem_digestsize()); + } + + return 0; +} + +static int fill_dentry_path(const struct path *path, + struct tsem_dentry *dentry) +{ + int retn; + struct tsem_inode *tsip; + + retn = fill_path(path, &dentry->path); + if (retn) + return retn; + + if (d_backing_inode(path->dentry)) { + dentry->have_inode = true; + fill_inode(d_backing_inode(path->dentry), &dentry->inode); + + tsip = tsem_inode(d_backing_inode(path->dentry)); + dentry->path.created = tsip->created; + dentry->path.creator = tsip->creator; + dentry->path.instance = tsip->instance; + memcpy(dentry->path.owner, tsem_task(current)->task_id, + tsem_digestsize()); + } + + return 0; +} + +static u64 _get_task_inode_instance(struct tsem_inode *tsip) +{ + u64 instance = 0; + u8 *task_id = tsem_task(current)->task_id; + struct tsem_inode_instance *entry, *owner = NULL; + + mutex_lock(&tsip->instance_mutex); + list_for_each_entry(entry, &tsip->instance_list, list) { + if (entry->creator == tsem_context(current)->id && + !memcmp(entry->owner, task_id, tsem_digestsize())) { + owner = entry; + break; + } + } + + if (owner) + instance = ++owner->instance; + else { + entry = kzalloc(sizeof(*entry), GFP_KERNEL); + if (entry) { + instance = 1; + entry->instance = instance; + entry->creator = tsem_context(current)->id; + memcpy(entry->owner, task_id, tsem_digestsize()); + list_add_tail(&entry->list, &tsip->instance_list); + } + } + + mutex_unlock(&tsip->instance_mutex); + return instance; +} + +static int get_inode(struct tsem_inode_args *args) +{ + int retn; + struct inode *dir = args->in.dir; + struct dentry *dentry = args->in.dentry; + struct tsem_inode *tsip = tsem_inode(d_backing_inode(dentry)); + + memset(&args->out, '\0', sizeof(args->out)); + + retn = fill_dentry(dentry, &args->out.dentry); + if (retn) + return retn; + args->out.dentry.path.created = tsip->created; + args->out.dentry.path.creator = tsip->creator; + args->out.dentry.path.instance = tsip->instance; + memcpy(args->out.dentry.path.owner, tsip->owner, tsem_digestsize()); + + fill_inode(dir, &args->out.dir); + + return retn; +} + +static int get_inode_create(struct tsem_inode_args *args) +{ + int retn; + u64 instance; + struct inode *dir = args->in.dir; + struct dentry *dentry = args->in.dentry; + + memset(&args->out, '\0', sizeof(args->out)); + + instance = _get_task_inode_instance(tsem_inode(dir)); + if (!instance) + return -ENOMEM; + + retn = fill_dentry(dentry, &args->out.dentry); + if (retn) + return retn; + + args->out.dentry.path.created = true; + args->out.dentry.path.creator = tsem_context(current)->id; + args->out.dentry.path.instance = instance; + memcpy(args->out.dentry.path.owner, tsem_task(current)->task_id, + tsem_digestsize()); + + fill_inode(dir, &args->out.dir); + + retn = register_inode_create(dir, instance, + args->out.dentry.path.pathname); + if (retn) + kfree(args->out.dentry.path.pathname); + + return retn; +} + +static int get_inode_link(struct tsem_inode_args *args) +{ + int retn; + struct inode *dir = args->in.dir; + struct dentry *dentry = args->in.dentry; + struct dentry *new_dentry = args->in.new_dentry; + + memset(&args->out, '\0', sizeof(args->out)); + + retn = fill_dentry(dentry, &args->out.dentry); + if (retn) + goto done; + + retn = fill_dentry(new_dentry, &args->out.new_dentry); + if (retn) + goto done; + + fill_inode(dir, &args->out.dir); + + done: + if (retn) { + kfree(args->out.dentry.path.pathname); + kfree(args->out.new_dentry.path.pathname); + } + + return retn; +} + +static int get_inode_symlink(struct tsem_inode_args *args) +{ + int retn; + const char *old_name = args->in.old_name; + struct inode *dir = args->in.dir; + struct dentry *dentry = args->in.dentry; + + memset(&args->out, '\0', sizeof(args->out)); + + retn = fill_dentry(dentry, &args->out.dentry); + if (retn) + return retn; + + fill_inode(dir, &args->out.dir); + + args->out.old_name = kstrdup(old_name, GFP_KERNEL); + if (!args->out.old_name) { + kfree(args->out.dentry.path.pathname); + retn = -ENOMEM; + } + + return retn; +} + +static int get_inode_mknod(struct tsem_inode_args *args) +{ + int retn; + struct inode *dir = args->in.dir; + struct dentry *dentry = args->in.dentry; + + memset(&args->out, '\0', sizeof(args->out)); + + retn = fill_dentry(dentry, &args->out.dentry); + if (retn) + return retn; + + fill_inode(dir, &args->out.dir); + + return 0; +} + +static int get_inode_rename(struct tsem_inode_args *args) +{ + int retn; + struct inode *old_dir = args->in.dir; + struct inode *new_dir = args->in.new_dir; + struct dentry *dentry = args->in.dentry; + struct dentry *new_dentry = args->in.new_dentry; + + memset(&args->out, '\0', sizeof(args->out)); + + retn = fill_dentry(dentry, &args->out.dentry); + if (retn) + return retn; + + retn = fill_dentry(new_dentry, &args->out.new_dentry); + if (retn) { + kfree(args->out.dentry.path.pathname); + return retn; + } + + fill_inode(old_dir, &args->out.dir); + fill_inode(new_dir, &args->out.new_dir); + + return 0; +} + +static int get_inode_killpriv(struct tsem_inode_args *args) +{ + struct dentry *dentry = args->in.dentry; + + memset(&args->out, '\0', sizeof(args->out)); + + return fill_dentry(dentry, &args->out.dentry); +} + +static int get_file_cell(struct tsem_file_args *args) +{ + int retn = 1; + bool pseudo_file = args->in.pseudo_file; + struct file *file = args->in.file; + struct inode *inode = file_inode(file); + + memset(&args->out, '\0', sizeof(args->out)); + inode_lock(inode); + + retn = fill_path(&file->f_path, &args->out.path); + if (retn) + goto done; + + args->out.flags = file->f_flags; + fill_inode(inode, &args->out.inode); + + retn = add_file_digest(file, pseudo_file, args); + if (retn) + kfree(args->out.path.pathname); + + done: + inode_unlock(inode); + return retn; +} + +static void get_prlimit(struct tsem_task_prlimit_args *args) +{ + const struct cred *cred = args->in.cred; + const struct cred *tcred = args->in.tcred; + + memset(&args->out, '\0', sizeof(args->out)); + + fill_creds(cred, &args->out.cred); + fill_creds(tcred, &args->out.tcred); +} + +static void get_prctl(struct tsem_task_prctl_args *args) +{ + if (args->option == PR_SET_NAME || args->option == PR_GET_NAME) + args->arg2 = 0; +} + +static void get_socket(struct sock *sock, struct tsem_socket *args) +{ + args->family = sock->sk_family; + args->type = sock->sk_type; + args->protocol = sock->sk_protocol; + args->kern = sock->sk_kern_sock; + memcpy(args->owner, tsem_inode(SOCK_INODE(sock->sk_socket))->owner, + tsem_digestsize()); +} + +static int get_socket_address(struct tsem_socket_args *args, + struct sockaddr *addr) +{ + u8 *p; + int retn, size; + SHASH_DESC_ON_STACK(shash, tfm); + + shash->tfm = tsem_digest(); + retn = crypto_shash_init(shash); + if (retn) + goto done; + + p = (u8 *) addr->sa_data; + size = args->value - offsetof(struct sockaddr, sa_data); + retn = crypto_shash_digest(shash, p, size, args->out.mapping); + + done: + return retn; +} + +static int get_socket_cell(struct tsem_socket_args *args) + +{ + int size, retn = 0; + struct sock *socka = args->in.socka; + struct sockaddr *addr = args->in.addr; + + memset(&args->out, '\0', sizeof(args->out)); + + get_socket(socka, &args->out.socka); + + switch (args->out.socka.family) { + case AF_INET: + memcpy(&args->out.ipv4, addr, sizeof(args->out.ipv4)); + break; + case AF_INET6: + memcpy(&args->out.ipv6, addr, sizeof(args->out.ipv6)); + break; + case AF_UNIX: + memset(args->out.path, '\0', sizeof(args->out.path)); + size = args->value - offsetof(struct sockaddr_un, sun_path); + strscpy(args->out.path, addr->sa_data, size); + break; + default: + retn = get_socket_address(args, addr); + break; + } + + return retn; +} + +static void get_socket_accept(struct tsem_socket_args *args) +{ + char *p; + int size; + const struct in6_addr *ipv6; + struct sock *socka = args->in.socka; + + memset(&args->out, '\0', sizeof(args->out)); + + get_socket(socka, &args->out.socka); + + switch (args->out.socka.family) { + case AF_INET: + args->out.ipv4.sin_port = socka->sk_num; + args->out.ipv4.sin_addr.s_addr = socka->sk_rcv_saddr; + break; + + case AF_INET6: + ipv6 = inet6_rcv_saddr(socka); + if (ipv6) { + args->out.ipv6.sin6_port = socka->sk_num; + args->out.ipv6.sin6_addr = *ipv6; + } + break; + + case AF_UNIX: + memset(args->out.path, '\0', sizeof(args->out.path)); + p = unix_sk(socka)->addr->name->sun_path; + size = unix_sk(socka)->addr->len - + offsetof(struct sockaddr_un, sun_path); + strscpy(args->out.path, p, size); + break; + + default: + memcpy(args->out.mapping, tsem_context(current)->zero_digest, + tsem_digestsize()); + break; + } +} + +static void get_socket_pair(struct tsem_socket_args *args) +{ + struct sock *socka = args->in.socka; + struct sock *sockb = args->in.sockb; + + memset(&args->out, '\0', sizeof(args->out)); + + get_socket(socka, &args->out.socka); + get_socket(sockb, &args->out.sockb); +} + +static void get_socket_msg(struct tsem_socket_args *args) + +{ + struct sock *socka = args->in.socka; + void *addr = args->in.addr; + + memset(&args->out, '\0', sizeof(args->out)); + + get_socket(socka, &args->out.socka); + if (addr) { + if (args->out.socka.family == AF_INET) { + args->out.have_addr = true; + args->out.ipv4 = *(struct sockaddr_in *) addr; + } + if (args->out.socka.family == AF_INET6) { + args->out.have_addr = true; + args->out.ipv6 = *(struct sockaddr_in6 *) addr; + } + } +} + +static void get_socket_argument(struct tsem_socket_args *args) + +{ + struct sock *socka = args->in.socka; + + memset(&args->out, '\0', sizeof(args->out)); + get_socket(socka, &args->out.socka); +} + +static int get_inode_getattr(struct tsem_inode_attr_args *args) +{ + const struct path *path = args->in.path; + + memset(&args->out, '\0', sizeof(args->out)); + + return fill_dentry_path(path, &args->out.dentry); +} + +static int get_inode_setattr(struct tsem_inode_attr_args *args) +{ + int retn; + struct user_namespace *ns; + struct dentry *dentry = args->in.dentry; + struct iattr *iattr = args->in.iattr; + + memset(&args->out, '\0', sizeof(args->out)); + + retn = fill_dentry(dentry, &args->out.dentry); + if (retn) + goto done; + + if (tsem_context(current)->use_current_ns) + ns = current_user_ns(); + else + ns = &init_user_ns; + + args->out.valid = iattr->ia_valid; + if (args->out.valid & ATTR_MODE) + args->out.mode = iattr->ia_mode; + if (args->out.valid & ATTR_UID) + args->out.uid = from_kuid(ns, iattr->ia_uid); + if (args->out.valid & ATTR_GID) + args->out.gid = from_kgid(ns, iattr->ia_gid); + if (args->out.valid & ATTR_SIZE) + args->out.size = iattr->ia_size; + + done: + return retn; +} + +static int get_inode_setxattr(struct tsem_inode_xattr_args *args) +{ + int retn; + const char *name = args->in.name; + struct dentry *dentry = args->in.dentry; + const void *value = args->in.value; + size_t size = args->in.size; + int flags = args->in.flags; + + memset(&args->out, '\0', sizeof(args->out)); + + args->out.size = size; + args->out.flags = flags; + + retn = fill_dentry(dentry, &args->out.dentry); + if (retn) + return retn; + + args->out.name = kstrdup(name, GFP_KERNEL); + if (!args->out.name) { + retn = -ENOMEM; + goto done; + } + + args->out.value = kmalloc(size, GFP_KERNEL); + if (!args->out.value) + retn = -ENOMEM; + memcpy(args->out.value, value, size); + + args->out.encoded_value = kzalloc(BASE64_CHARS(size) + 1, GFP_KERNEL); + if (!args->out.encoded_value) { + retn = -ENOMEM; + goto done; + } + base64_encode(args->out.value, size, args->out.encoded_value); + + done: + if (retn) { + kfree(args->out.name); + kfree(args->out.value); + kfree(args->out.encoded_value); + } + return retn; +} + +static int get_inode_getxattr(struct tsem_inode_xattr_args *args) +{ + int retn; + const char *name = args->in.name; + struct dentry *dentry = args->in.dentry; + + memset(&args->out, '\0', sizeof(args->out)); + + retn = fill_dentry(dentry, &args->out.dentry); + if (retn) + return retn; + + args->out.name = kstrdup(name, GFP_KERNEL); + if (!args->out.name) + retn = -ENOMEM; + + if (retn) + kfree(args->out.name); + return retn; +} + +static int get_inode_listxattr(struct tsem_inode_xattr_args *args) +{ + struct dentry *dentry = args->in.dentry; + + memset(&args->out, '\0', sizeof(args->out)); + + return fill_dentry(dentry, &args->out.dentry); +} + +static int get_kernel_module(struct tsem_kernel_args *args) +{ + int retn = 0; + char *kmod_name = args->in.kmod_name; + + memset(&args->out, '\0', sizeof(args->out)); + + args->out.kmod_name = kstrdup(kmod_name, GFP_KERNEL); + if (!args->out.kmod_name) + retn = -ENOMEM; + + args->out.kmod_name = kstrdup(kmod_name, GFP_KERNEL); + if (!args->out.kmod_name) + retn = -ENOMEM; + + return retn; +} + +static int get_kernel_file(struct tsem_kernel_args *args) +{ + int retn = 0; + struct file *file = args->in.file; + struct inode *inode = file_inode(file); + + memset(&args->out, '\0', sizeof(args->out)); + inode_lock(inode); + + retn = fill_path(&file->f_path, &args->out.file.out.path); + if (retn) + goto done; + + args->out.file.out.flags = file->f_flags; + fill_inode(inode, &args->out.file.out.inode); + + if (!S_ISREG(inode->i_mode)) + goto done; + + retn = add_file_digest(file, false, &args->out.file); + if (retn) + kfree(args->out.file.out.path.pathname); + + done: + inode_unlock(inode); + return retn; +} + +static int get_sb_mount(struct tsem_sb_args *args) +{ + int retn = -ENOMEM; + struct tsem_model *model = tsem_model(current); + struct tsem_inode_instance *tsio = NULL; + const char *dev_name = args->in.dev_name; + const char *type = args->in.type; + const struct path *path = args->in.path; + + memset(&args->out, '\0', sizeof(args->out)); + + if (dev_name) { + args->out.dev_name = kstrdup(dev_name, GFP_KERNEL); + if (!args->out.dev_name) + goto done; + } + + if (type) { + args->out.type = kstrdup(type, GFP_KERNEL); + if (!args->out.type) + goto done; + } + + retn = fill_path(path, &args->out.path); + if (retn) + goto done; + + if (model && args->out.path.created) { + tsio = kzalloc(sizeof(*tsio), GFP_KERNEL); + if (!tsio) { + retn = -ENOMEM; + goto done; + } + + tsio->creator = args->out.path.creator; + tsio->instance = args->out.path.instance; + tsio->pathname = args->out.path.pathname; + memcpy(tsio->owner, args->out.path.owner, tsem_digestsize()); + + mutex_lock(&model->mount_mutex); + list_add_tail(&tsio->list, &model->mount_list); + mutex_unlock(&model->mount_mutex); + } + + done: + if (retn) { + kfree(args->out.dev_name); + kfree(args->out.type); + kfree(tsio); + } + return retn; +} + +static int get_sb_umount(struct tsem_sb_args *args) +{ + struct dentry *dentry = args->in.dentry; + + memset(&args->out, '\0', sizeof(args->out)); + + return fill_dentry(dentry, &args->out.dentry); +} + +static int get_sb_remount(struct tsem_sb_args *args) +{ + int retn = -ENOMEM; + struct super_block *sb = args->in.sb; + + memset(&args->out, '\0', sizeof(args->out)); + + args->flags = sb->s_flags; + + args->out.type = kstrdup(sb->s_type->name, GFP_KERNEL); + if (!args->out.type) + goto done; + + retn = fill_dentry(sb->s_root, &args->out.dentry); + + done: + if (retn) { + kfree(args->out.type); + kfree(args->out.dentry.path.pathname); + } + + return retn; +} + +static void get_netlink(struct tsem_netlink_args *args) +{ + struct sock *sock = args->in.sock; + struct netlink_skb_parms *np = args->in.parms; + struct user_namespace *ns; + + memset(&args->out, '\0', sizeof(args->out)); + get_socket(sock, &args->out.sock); + + if (tsem_context(current)->use_current_ns) + ns = current_user_ns(); + else + ns = &init_user_ns; + args->out.uid = from_kuid(ns, np->creds.uid); + args->out.gid = from_kgid(ns, np->creds.gid); + + args->out.portid = np->portid; + args->out.dst_group = np->dst_group; + args->out.flags = np->flags; + args->out.nsid_set = np->nsid_is_set; + args->out.nsid = np->nsid; +} + +static void get_ipc_cred(struct kern_ipc_perm *perm, struct tsem_ipc_perm *tp) +{ + struct user_namespace *ns; + + if (tsem_context(current)->use_current_ns) + ns = current_user_ns(); + else + ns = &init_user_ns; + + tp->uid = from_kuid(ns, perm->uid); + tp->gid = from_kgid(ns, perm->gid); + tp->cuid = from_kuid(ns, perm->cuid); + tp->cgid = from_kgid(ns, perm->cgid); + tp->mode = perm->mode; +} + +static void get_ipc_permission(struct tsem_ipc_args *args) +{ + struct kern_ipc_perm *perm = args->in.perm; + + memset(&args->out, '\0', sizeof(args->out)); + + get_ipc_cred(perm, &args->out.perm); + memcpy(args->out.owner, tsem_ipc(perm)->owner, tsem_digestsize()); +} + +static void get_msg_queue_msgrcv(struct tsem_ipc_args *args) +{ + struct kern_ipc_perm *perm = args->in.perm; + struct tsem_task *target = tsem_task(args->in.target); + + memset(&args->out, '\0', sizeof(args->out)); + + get_ipc_cred(perm, &args->out.perm); + memcpy(args->out.owner, tsem_ipc(perm)->owner, tsem_digestsize()); + memcpy(args->out.target, target->task_id, tsem_digestsize()); +} + +static void get_key_alloc(struct tsem_key_args *args) +{ + const struct cred *cred = args->in.cred; + + memset(&args->out, '\0', sizeof(args->out)); + fill_creds(cred, &args->out.cred); +} + +static void get_key_perm(struct tsem_key_args *args) +{ + const struct cred *cred = args->in.cred; + struct user_namespace *ns; + const key_ref_t ref = args->in.ref; + struct key *key = key_ref_to_ptr(ref); + + memset(&args->out, '\0', sizeof(args->out)); + fill_creds(cred, &args->out.cred); + + args->out.possessed = is_key_possessed(ref); + + if (tsem_context(current)->use_current_ns) + ns = current_user_ns(); + else + ns = &init_user_ns; + args->out.uid = from_kuid(ns, key->uid); + args->out.gid = from_kgid(ns, key->gid); + + args->out.perm = key->perm; + args->out.flags = key->flags; +} + +static int get_sb_statfs(struct tsem_sb_args *args) +{ + struct dentry *dentry = args->in.dentry; + + memset(&args->out, '\0', sizeof(args->out)); + + return fill_dentry(dentry, &args->out.dentry); +} + +static int get_move_mount(struct tsem_sb_args *args) +{ + int retn; + const struct path *old_path = args->in.path; + const struct path *new_path = args->in.path2; + + memset(&args->out, '\0', sizeof(args->out)); + + retn = fill_path(old_path, &args->out.path); + if (!retn) + retn = fill_path(new_path, &args->out.path2); + + return retn; +} + +static int get_quotactl(struct tsem_quota_args *args) +{ + int retn; + const struct super_block *sb = args->in.sb; + + memset(&args->out, '\0', sizeof(args->out)); + + args->out.s_flags = sb->s_flags; + + args->out.fstype = kstrdup(sb->s_type->name, GFP_KERNEL); + if (!args->out.fstype) + goto done; + + retn = fill_dentry(sb->s_root, &args->out.dentry); + + done: + if (retn) + kfree(args->out.fstype); + return retn; +} + +static int get_quotaon(struct tsem_quota_args *args) +{ + struct dentry *dentry = args->in.dentry; + + memset(&args->out, '\0', sizeof(args->out)); + + return fill_dentry(dentry, &args->out.dentry); +} + +static void event_free(struct tsem_event *ep) +{ + switch (ep->event) { + case TSEM_KERNEL_MODULE_REQUEST: + kfree(ep->CELL.kernel.out.kmod_name); + break; + case TSEM_KERNEL_READ_FILE: + kfree(ep->CELL.kernel.out.file.out.path.pathname); + break; + case TSEM_INODE_CREATE: + case TSEM_INODE_MKDIR: + case TSEM_INODE_RMDIR: + case TSEM_INODE_UNLINK: + case TSEM_INODE_MKNOD: + case TSEM_INODE_KILLPRIV: + kfree(ep->CELL.inode.out.dentry.path.pathname); + break; + case TSEM_INODE_LINK: + kfree(ep->CELL.inode.out.dentry.path.pathname); + kfree(ep->CELL.inode.out.new_dentry.path.pathname); + break; + case TSEM_INODE_SYMLINK: + kfree(ep->CELL.inode.out.old_name); + kfree(ep->CELL.inode.out.dentry.path.pathname); + break; + case TSEM_INODE_RENAME: + kfree(ep->CELL.inode.out.dentry.path.pathname); + kfree(ep->CELL.inode.out.new_dentry.path.pathname); + break; + case TSEM_FILE_OPEN: + case TSEM_BPRM_COMMITTED_CREDS: + case TSEM_FILE_IOCTL: + case TSEM_FILE_LOCK: + case TSEM_FILE_FCNTL: + case TSEM_FILE_RECEIVE: + kfree(ep->CELL.file.out.path.pathname); + break; + case TSEM_MMAP_FILE: + kfree(ep->CELL.mmap_file.file.out.path.pathname); + break; + case TSEM_INODE_GETATTR: + case TSEM_INODE_SETATTR: + kfree(ep->CELL.inode_attr.out.dentry.path.pathname); + break; + case TSEM_INODE_SETXATTR: + case TSEM_INODE_GETXATTR: + case TSEM_INODE_REMOVEXATTR: + case TSEM_INODE_LISTXATTR: + kfree(ep->CELL.inode_xattr.out.dentry.path.pathname); + kfree(ep->CELL.inode_xattr.out.name); + kfree(ep->CELL.inode_xattr.out.value); + kfree(ep->CELL.inode_xattr.out.encoded_value); + break; + case TSEM_SB_MOUNT: + kfree(ep->CELL.sb.out.dev_name); + kfree(ep->CELL.sb.out.type); + kfree(ep->CELL.sb.out.path.pathname); + break; + case TSEM_SB_UMOUNT: + kfree(ep->CELL.sb.out.dentry.path.pathname); + break; + case TSEM_SB_REMOUNT: + kfree(ep->CELL.sb.out.type); + kfree(ep->CELL.sb.out.dentry.path.pathname); + break; + case TSEM_SB_PIVOTROOT: + case TSEM_MOVE_MOUNT: + kfree(ep->CELL.sb.out.path.pathname); + kfree(ep->CELL.sb.out.path2.pathname); + break; + case TSEM_QUOTACTL: + kfree(ep->CELL.quota.out.fstype); + kfree(ep->CELL.quota.out.dentry.path.pathname); + break; + case TSEM_QUOTA_ON: + kfree(ep->CELL.quota.out.dentry.path.pathname); + break; + case TSEM_SB_STATFS: + kfree(ep->CELL.sb.out.dentry.path.pathname); + break; + default: + break; + } +} + +static void get_COE(struct tsem_COE *COE) +{ + struct user_namespace *ns; + + if (tsem_context(current)->use_current_ns) + ns = current_user_ns(); + else + ns = &init_user_ns; + + COE->uid = from_kuid(ns, current_uid()); + COE->euid = from_kuid(ns, current_euid()); + COE->suid = from_kuid(ns, current_suid()); + + COE->gid = from_kgid(ns, current_gid()); + COE->egid = from_kgid(ns, current_egid()); + COE->sgid = from_kgid(ns, current_sgid()); + + COE->fsuid = from_kuid(ns, current_fsuid()); + COE->fsgid = from_kgid(ns, current_fsgid()); + + COE->capeff.mask = current_cred()->cap_effective; +} + +/** + * tsem_event_generate() - Generate the default event description. + * @ep: A pointer to the structure that describes the event parameters. + * + * This function is responsible for initializing the data that describes + * the security event and is persisted for the lifetime that the event + * description is maintained. + * + * Return: In the event of an error this function returns an error code + * as a negative return value. A value of zero indicates that + * the event should be bypassed. A positive value indicates + * the event should be modeled. + */ + +int tsem_event_generate(struct tsem_event *ep) +{ + int retn = 0; + + switch (ep->event) { + case TSEM_NETLINK_SEND: + get_netlink(&ep->CELL.netlink); + break; + case TSEM_IPC_PERMISSION: + case TSEM_SHM_ASSOCIATE: + case TSEM_SHM_SHMCTL: + case TSEM_SHM_SHMAT: + case TSEM_SEM_ASSOCIATE: + case TSEM_SEM_SEMCTL: + case TSEM_SEM_SEMOP: + case TSEM_MSG_QUEUE_ASSOCIATE: + case TSEM_MSG_QUEUE_MSGCTL: + case TSEM_MSG_QUEUE_MSGSND: + get_ipc_permission(&ep->CELL.ipc); + break; + case TSEM_MSG_QUEUE_MSGRCV: + get_msg_queue_msgrcv(&ep->CELL.ipc); + break; + case TSEM_KEY_ALLOC: + get_key_alloc(&ep->CELL.key); + break; + case TSEM_KEY_PERMISSION: + get_key_perm(&ep->CELL.key); + break; + case TSEM_INODE_RMDIR: + case TSEM_INODE_UNLINK: + retn = get_inode(&ep->CELL.inode); + break; + case TSEM_INODE_CREATE: + case TSEM_INODE_MKDIR: + retn = get_inode_create(&ep->CELL.inode); + break; + case TSEM_INODE_LINK: + retn = get_inode_link(&ep->CELL.inode); + break; + case TSEM_INODE_SYMLINK: + retn = get_inode_symlink(&ep->CELL.inode); + break; + case TSEM_INODE_MKNOD: + retn = get_inode_mknod(&ep->CELL.inode); + break; + case TSEM_INODE_RENAME: + retn = get_inode_rename(&ep->CELL.inode); + break; + case TSEM_INODE_KILLPRIV: + retn = get_inode_killpriv(&ep->CELL.inode); + break; + case TSEM_FILE_OPEN: + case TSEM_BPRM_COMMITTED_CREDS: + case TSEM_FILE_IOCTL: + case TSEM_FILE_LOCK: + case TSEM_FILE_FCNTL: + case TSEM_FILE_RECEIVE: + retn = get_file_cell(&ep->CELL.file); + break; + case TSEM_MMAP_FILE: + if (!ep->CELL.mmap_file.anonymous) + retn = get_file_cell(&ep->CELL.mmap_file.file); + break; + case TSEM_TASK_PRLIMIT: + get_prlimit(&ep->CELL.task_prlimit); + break; + case TSEM_TASK_PRCTL: + get_prctl(&ep->CELL.task_prctl); + break; + case TSEM_UNIX_STREAM_CONNECT: + case TSEM_UNIX_MAY_SEND: + case TSEM_SOCKET_SOCKETPAIR: + get_socket_pair(&ep->CELL.socket); + break; + case TSEM_SOCKET_CONNECT: + case TSEM_SOCKET_BIND: + retn = get_socket_cell(&ep->CELL.socket); + break; + case TSEM_SOCKET_ACCEPT: + get_socket_accept(&ep->CELL.socket); + break; + case TSEM_SOCKET_LISTEN: + case TSEM_SOCKET_GETSOCKNAME: + case TSEM_SOCKET_GETPEERNAME: + case TSEM_SOCKET_SETSOCKOPT: + case TSEM_SOCKET_SHUTDOWN: + case TSEM_TUN_DEV_ATTACH_QUEUE: + get_socket_argument(&ep->CELL.socket); + break; + case TSEM_SOCKET_SENDMSG: + case TSEM_SOCKET_RECVMSG: + get_socket_msg(&ep->CELL.socket); + break; + case TSEM_INODE_GETATTR: + retn = get_inode_getattr(&ep->CELL.inode_attr); + break; + case TSEM_INODE_SETATTR: + retn = get_inode_setattr(&ep->CELL.inode_attr); + break; + case TSEM_INODE_SETXATTR: + retn = get_inode_setxattr(&ep->CELL.inode_xattr); + break; + case TSEM_INODE_GETXATTR: + case TSEM_INODE_REMOVEXATTR: + retn = get_inode_getxattr(&ep->CELL.inode_xattr); + break; + case TSEM_INODE_LISTXATTR: + retn = get_inode_listxattr(&ep->CELL.inode_xattr); + break; + case TSEM_KERNEL_MODULE_REQUEST: + retn = get_kernel_module(&ep->CELL.kernel); + break; + case TSEM_KERNEL_READ_FILE: + retn = get_kernel_file(&ep->CELL.kernel); + break; + case TSEM_SB_MOUNT: + retn = get_sb_mount(&ep->CELL.sb); + break; + case TSEM_SB_UMOUNT: + retn = get_sb_umount(&ep->CELL.sb); + break; + case TSEM_SB_REMOUNT: + retn = get_sb_remount(&ep->CELL.sb); + break; + case TSEM_SB_STATFS: + retn = get_sb_statfs(&ep->CELL.sb); + break; + case TSEM_SB_PIVOTROOT: + case TSEM_MOVE_MOUNT: + retn = get_move_mount(&ep->CELL.sb); + break; + case TSEM_QUOTACTL: + retn = get_quotactl(&ep->CELL.quota); + break; + case TSEM_QUOTA_ON: + retn = get_quotaon(&ep->CELL.quota); + break; + default: + break; + } + + if (!retn) { + ep->event_free = event_free; + return 1; + } + return retn; +} +EXPORT_SYMBOL_GPL(tsem_event_generate); + +/** + * tsem_event_init() - Initialize a security event description structure. + * @ep: A pointer to the tsem_event structure that describes the + * security event. + * + * This function is responsible for initializing the tsem_event structure + * and populating it based on the event type. + * + * Return: In the event of an error this function returns an error code + * as a negative return value. A value of zero indicates that + * the event should be bypassed. A positive value indicates + * the event should be modeled. + */ +int tsem_event_init(struct tsem_event *ep) +{ + int retn = 1; + u64 timestamp = ktime_get_boottime_ns(); + struct tsem_task *task = tsem_task(current); + + ep->pid = task_pid_nr(current); + ep->context = tsem_context(current)->id; + ep->instance = task->instance; + ep->p_instance = task->p_instance; + ep->timestamp = timestamp - tsem_context(current)->timestamp; + memcpy(ep->comm, current->comm, sizeof(ep->comm)); + memcpy(ep->task_id, task->task_id, tsem_digestsize()); + memcpy(ep->p_task_id, task->p_task_id, tsem_digestsize()); + + get_COE(&ep->COE); + + if (ep->event == TSEM_BPRM_COMMITTED_CREDS) + return tsem_event_generate(ep); + + if (!ep->no_params) + retn = tsem_context(current)->ops->init(ep); + + if (retn > 0) + ep->event_number = ++tsem_context(current)->event_number; + return retn; +} + +/** + * tsem_free_event() - Free a security event description. + * @ep: A pointer to the security event description that is to be freed. + * + * This function is responsible for freeing the resources that were + * allocated by the tsem_event_allocate() function. + */ +static void tsem_event_free(struct kref *kref) +{ + struct tsem_event *ep; + + ep = container_of(kref, struct tsem_event, kref); + + if (ep->event_free) + ep->event_free(ep); + kmem_cache_free(event_cachep, ep); +} + +/** + * tsem_event_put() - Release a referenceto a TSEM event description. + * + * This function is called each time the use of a TSEM event description + * is dropped. + */ +void tsem_event_put(struct tsem_event *ep) +{ + kref_put(&ep->kref, tsem_event_free); +} + +static void refill_event_magazine(struct work_struct *work) +{ + unsigned int index; + struct tsem_event *ep; + struct tsem_work *ws; + + ws = container_of(work, struct tsem_work, work); + + ep = kmem_cache_zalloc(event_cachep, GFP_KERNEL); + if (!ep) { + pr_warn("tsem: Cannot refill event magazine.\n"); + return; + } + + spin_lock(&ws->u.ctx->magazine_lock); + ws->u.ctx->magazine[ws->index] = ep; + clear_bit(ws->index, ws->u.ctx->magazine_index); + + /* + * The following memory barrier is used to cause the magazine + * index to be visible after the refill of the cache slot. + */ + smp_mb__after_atomic(); + spin_unlock(&ws->u.ctx->magazine_lock); + + if (index >= ws->u.ctx->magazine_size) { + kmem_cache_free(event_cachep, ep); + WARN_ONCE(true, "Refilling event magazine with no slots.\n"); + } +} + +/** + * tsem_event_get() - Obtain a reference to a TSEM event description. + * + * This function is called on each invocation of the tsem_task_free + * function to release one of the references on the TMA modeling + * structure. + */ +void tsem_event_get(struct tsem_event *ep) +{ + kref_get(&ep->kref); +} + +/** + * tsem_event_allocate() - Allocate a TSEM event description structure. + * @locked: A boolean flag used to indicate if the allocation is being + * done in atomic context and must be serviced from the + * pre-allocated event description structures. + * + * Return: This function returns a pointer to the allocated structure or + * a NULL pointer in the event of an allocation failure. + */ +struct tsem_event *tsem_event_allocate(enum tsem_event_type event, bool locked) +{ + unsigned int index; + struct tsem_event *ep = NULL; + struct tsem_context *ctx = tsem_context(current); + + if (!locked) { + ep = kmem_cache_zalloc(event_cachep, GFP_KERNEL); + if (ep) { + ep->event = event; + kref_init(&ep->kref); + } + return ep; + } + + spin_lock(&ctx->magazine_lock); + index = find_first_zero_bit(ctx->magazine_index, ctx->magazine_size); + if (index < ctx->magazine_size) { + ep = ctx->magazine[index]; + ctx->ws[index].index = index; + ctx->ws[index].u.ctx = ctx; + set_bit(index, ctx->magazine_index); + + /* + * Similar to the issue noted in the refill_event_magazine() + * function, this barrier is used to cause the consumption + * of the cache entry to become visible. + + */ + smp_mb__after_atomic(); + } + + spin_unlock(&ctx->magazine_lock); + + if (ep) { + INIT_WORK(&ctx->ws[index].work, refill_event_magazine); + queue_work(system_highpri_wq, &ctx->ws[index].work); + ep->event = event; + ep->locked = true; + kref_init(&ep->kref); + return ep; + } + + pr_warn("tsem: Fail event allocation comm %s ns %llu cs %u.\n", + current->comm, tsem_context(current)->id, ctx->magazine_size); + return NULL; +} + +/** + * tsem event_magazine_allocate() - Allocate a TSEM event magazine. + * @ctx: A pointer to the modeling context that the magazine is + * to be allocated for. + * @size: The number of entries to be created in the magazine. + + * The security modeling event magazine is an array of tsem_event + * structures that are used to service security hooks that are called + * in atomic context. Each modeling domain/namespace has a magazine + * allocated to it and this function allocates and initializes the + * memory structures needed to manage that magazine. + + * Return: This function returns a value of zero on success and a negative + * error code on failure. + */ +int tsem_event_magazine_allocate(struct tsem_context *ctx, size_t size) +{ + unsigned int lp; + int retn = -ENOMEM; + + ctx->magazine_size = size; + + spin_lock_init(&ctx->magazine_lock); + + ctx->magazine_index = bitmap_zalloc(ctx->magazine_size, GFP_KERNEL); + if (!ctx->magazine_index) + return retn; + + ctx->magazine = kcalloc(ctx->magazine_size, sizeof(*ctx->magazine), + GFP_KERNEL); + if (!ctx->magazine) + goto done; + + for (lp = 0; lp < ctx->magazine_size; ++lp) { + ctx->magazine[lp] = kmem_cache_zalloc(event_cachep, + GFP_KERNEL); + if (!ctx->magazine[lp]) + goto done; + } + + ctx->ws = kcalloc(ctx->magazine_size, sizeof(*ctx->ws), GFP_KERNEL); + if (ctx->ws) + retn = 0; + + done: + if (retn) + tsem_event_magazine_free(ctx); + + return retn; +} + +/** + * tsem event_magazine_free() - Releases a TSEM event magazine. + * @ctx: A pointer to the modeling context whose magazine is to be + * released. + * + * The function is used to free the memory that was allocated by + * the tsem_event_magazine_allocate() function for a security + * modeling context. + */ +void tsem_event_magazine_free(struct tsem_context *ctx) +{ + unsigned int lp; + + for (lp = 0; lp < ctx->magazine_size; ++lp) + kmem_cache_free(event_cachep, ctx->magazine[lp]); + + bitmap_free(ctx->magazine_index); + kfree(ctx->ws); + kfree(ctx->magazine); +} + +/** + * tsem event_cache_init() - Initialize the TSEM event cache. + * + * This function is called by the TSEM initialization function and sets + * up the cache that will be used to allocate tsem_event structures. + * + * Return: This function returns a value of zero on success and a negative + * error code on failure. + */ +int __init tsem_event_cache_init(void) +{ + event_cachep = kmem_cache_create("tsem_event_cache", + sizeof(struct tsem_event), 0, + SLAB_PANIC, 0); + if (!event_cachep) + return -ENOMEM; + + return 0; +} From patchwork Mon Aug 26 10:37:24 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Dr. Greg" X-Patchwork-Id: 13777627 X-Patchwork-Delegate: paul@paul-moore.com Received: from blizzard.enjellic.com (wind.enjellic.com [76.10.64.91]) by smtp.subspace.kernel.org (Postfix) with ESMTP id A0B5615DBBA; Mon, 26 Aug 2024 10:50:32 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=76.10.64.91 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1724669435; cv=none; b=m/JzPnEZx5ep1yKiGh2YOZy0sUdrODuTHjHdqdVdVWomOWdAI9vRA5D9cLt9t8TH/olH8KzZgVVulsa3f1ygG+DkvUEiAPT7zpKzFwS3JU9no0NHIbS4n5ClhiaXVj+1fJHRsSTC7/aEkW9wFHqLVc1ULSKKrbNhZ1o1nTpIEqY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1724669435; c=relaxed/simple; bh=vnX/pFmg+ITdvFyGXRGzoQFPJehSyw0HieAGNhxqhgg=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=XMNOoRsyGtUXdn4tHXuZ+GtZbT7Z3cDMDoKQ1dU4e98TvexsL9DHxTGBlcg6BjaNVZ1W2rIJ/oEeOqVbL7uzxjrbJmo5L1bndeQ1IjU5hiJbDwrjSsC7B1jOd+EPOlB+RyanTRYAWDNTPXNAEnJtiQUIsH+vEJCO3Ly6RxJ1Bdg= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=enjellic.com; spf=pass smtp.mailfrom=enjellic.com; arc=none smtp.client-ip=76.10.64.91 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=enjellic.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=enjellic.com Received: from blizzard.enjellic.com (localhost [127.0.0.1]) by blizzard.enjellic.com (8.15.2/8.15.2) with ESMTP id 47QAbV2Y003441; Mon, 26 Aug 2024 05:37:31 -0500 Received: (from greg@localhost) by blizzard.enjellic.com (8.15.2/8.15.2/Submit) id 47QAbVDc003440; Mon, 26 Aug 2024 05:37:31 -0500 X-Authentication-Warning: blizzard.enjellic.com: greg set sender to greg@enjellic.com using -f From: Greg Wettstein To: linux-security-module@vger.kernel.org, linux-kernel@vger.kernel.org Cc: jmorris@namei.org Subject: [PATCH v4 10/14] Implement security event mapping. Date: Mon, 26 Aug 2024 05:37:24 -0500 Message-Id: <20240826103728.3378-11-greg@enjellic.com> X-Mailer: git-send-email 2.39.1 In-Reply-To: <20240826103728.3378-1-greg@enjellic.com> References: <20240826103728.3378-1-greg@enjellic.com> Precedence: bulk X-Mailing-List: linux-security-module@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 The map.c file is responsible for implenting the description of a security event into a security state coefficient. The following documentation file, provided with the TSEM implementation, contains a description of the generative functions used to create the coefficients. Documentation/admin-guide/LSM/tsem.rst The mapping process takes a security event description, that was packaged by the event.c file, and uses that description to drive the coefficient generation process. How the parameters are mapped into the coefficients is responsible for implementing the security model enforced by the internal Trusted Modeling Agent implementation. --- security/tsem/map.c | 1536 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1536 insertions(+) create mode 100644 security/tsem/map.c diff --git a/security/tsem/map.c b/security/tsem/map.c new file mode 100644 index 000000000000..6a72b9567b99 --- /dev/null +++ b/security/tsem/map.c @@ -0,0 +1,1536 @@ +// SPDX-License-Identifier: GPL-2.0-only + +/* + * Copyright (C) 2024 Enjellic Systems Development, LLC + * Author: Dr. Greg Wettstein + * + * This file is responsible for mapping the characteristics of + * security events into a security state coefficient for the internal + * trusted modeling agent implementation. The primary TSEM + * documentation describes the generative functions that are used to + * conduct this mapping. + * + * The tsem_map_event() function is called by the tsem_model_event() + * to generate the security state coefficients for the internal + * modeling implementation. + * + * The other major role of this file is to provide the tsem_map_task() + * function that is used to generate the TASK_ID for a process. This + * function is called from the tsem_bprm_committed_creds() function. + */ + +#include +#include + +#include "tsem.h" + +static int add_u16(struct shash_desc *shash, u16 value) +{ + return crypto_shash_update(shash, (char *) &value, sizeof(value)); +} + +static int add_u32(struct shash_desc *shash, u32 value) +{ + return crypto_shash_update(shash, (char *) &value, sizeof(value)); +} + +static int add_u64(struct shash_desc *shash, u64 value) +{ + return crypto_shash_update(shash, (char *) &value, sizeof(value)); +} + +static int add_str(struct shash_desc *shash, char *str) +{ + u32 value; + u8 *p; + int retn; + int size; + + p = (u8 *) &value; + value = strlen(str); + size = sizeof(value); + retn = crypto_shash_update(shash, p, size); + if (retn) + goto done; + + p = (u8 *) str; + size = strlen(str); + retn = crypto_shash_update(shash, p, size); + + done: + return retn; +} + +static int get_COE_mapping(struct tsem_event *ep, u8 *mapping) +{ + int retn = 0; + SHASH_DESC_ON_STACK(shash, tfm); + + shash->tfm = tsem_digest(); + retn = crypto_shash_init(shash); + if (retn) + goto done; + + retn = add_u32(shash, ep->COE.uid); + if (retn) + goto done; + + retn = add_u32(shash, ep->COE.euid); + if (retn) + goto done; + + retn = add_u32(shash, ep->COE.suid); + if (retn) + goto done; + + retn = add_u32(shash, ep->COE.gid); + if (retn) + goto done; + + retn = add_u32(shash, ep->COE.egid); + if (retn) + goto done; + + retn = add_u32(shash, ep->COE.sgid); + if (retn) + goto done; + + retn = add_u32(shash, ep->COE.fsuid); + if (retn) + goto done; + + retn = add_u32(shash, ep->COE.fsgid); + if (retn) + goto done; + + retn = add_u64(shash, ep->COE.capeff.value); + if (retn) + goto done; + + retn = crypto_shash_final(shash, mapping); + + done: + return retn; +} + +static int add_temp_path(struct shash_desc *shash, char *pathname, + u64 instance, u8 *owner) +{ + char *p, ch = '\0'; + int retn; + + p = strrchr(pathname, '/'); + if (!p) + return -EINVAL; + + ++p; + ch = *p; + if (ch) + *p = '\0'; + retn = add_str(shash, pathname); + if (ch) + *p = ch; + if (retn) + goto done; + + retn = add_u64(shash, instance); + if (retn) + goto done; + + retn = crypto_shash_update(shash, owner, tsem_digestsize()); + + done: + return retn; +} + +static int add_path(struct shash_desc *shash, struct tsem_path *path) +{ + int retn; + + if (path->dev) { + retn = add_u32(shash, MAJOR(path->dev)); + if (retn) + goto done; + retn = add_u32(shash, MINOR(path->dev)); + if (retn) + goto done; + } + + if (path->created) { + retn = add_temp_path(shash, path->pathname, path->instance, + path->owner); + } else + retn = add_str(shash, path->pathname); + + done: + return retn; +} + +static int add_inode(struct shash_desc *shash, struct tsem_inode_cell *inode) +{ + u32 value; + u8 *p = (u8 *) &value; + int retn; + int size = sizeof(value); + + value = inode->uid; + retn = crypto_shash_update(shash, p, size); + if (retn) + goto done; + + value = inode->gid; + retn = crypto_shash_update(shash, p, size); + if (retn) + goto done; + + value = inode->mode; + retn = crypto_shash_update(shash, p, size); + if (retn) + goto done; + + value = inode->s_magic; + retn = crypto_shash_update(shash, p, size); + if (retn) + goto done; + + p = (u8 *) inode->s_id; + size = sizeof(inode->s_id); + retn = crypto_shash_update(shash, p, size); + if (retn) + goto done; + + if (inode->s_magic == TMPFS_MAGIC) + p = (u8 *) uuid_null.b; + else + p = (u8 *) inode->s_uuid; + size = sizeof(inode->s_uuid); + retn = crypto_shash_update(shash, p, size); + + done: + return retn; +} + +static int add_dentry(struct shash_desc *shash, struct tsem_dentry *dentry) +{ + int retn; + + if (dentry->have_inode) + retn = add_inode(shash, &dentry->inode); + + if (dentry->path.dev) { + retn = add_u32(shash, MAJOR(dentry->path.dev)); + if (retn) + goto done; + retn = add_u32(shash, MINOR(dentry->path.dev)); + if (retn) + goto done; + } + + if (dentry->path.created && + dentry->path.creator == tsem_context(current)->id) { + retn = add_temp_path(shash, dentry->path.pathname, + dentry->path.instance, + dentry->path.owner); + } else + retn = add_str(shash, dentry->path.pathname); + + done: + return retn; +} + +static int add_file(struct shash_desc *shash, struct tsem_file_args *args) +{ + int retn; + + retn = add_u32(shash, args->out.flags); + if (retn) + goto done; + + retn = add_inode(shash, &args->out.inode); + if (retn) + goto done; + + retn = add_path(shash, &args->out.path); + if (retn) + goto done; + + retn = crypto_shash_update(shash, args->out.digest, tsem_digestsize()); + + done: + return retn; +} + +static int add_creds(struct shash_desc *shash, struct tsem_COE *cp) +{ + int retn; + + retn = add_u32(shash, cp->uid); + if (!retn) + goto done; + + retn = add_u32(shash, cp->euid); + if (retn) + goto done; + + retn = add_u32(shash, cp->suid); + if (retn) + goto done; + + retn = add_u32(shash, cp->gid); + if (retn) + goto done; + + retn = add_u32(shash, cp->egid); + if (retn) + goto done; + + retn = add_u32(shash, cp->sgid); + if (retn) + goto done; + + retn = add_u32(shash, cp->fsuid); + if (retn) + goto done; + + retn = add_u32(shash, cp->fsgid); + if (retn) + goto done; + + retn = add_u64(shash, cp->capeff.value); + if (retn) + goto done; + + retn = add_u32(shash, cp->securebits); + + done: + return retn; +} + +static int add_socket(struct shash_desc *shash, struct tsem_socket *args) +{ + int retn; + + retn = add_u32(shash, args->family); + if (retn) + goto done; + + retn = add_u32(shash, args->type); + if (retn) + goto done; + + retn = add_u32(shash, args->protocol); + if (retn) + goto done; + + retn = crypto_shash_update(shash, args->owner, sizeof(args->owner)); + + done: + return retn; +} + +static int add_task(struct shash_desc *shash, u8 *task_id) +{ + return crypto_shash_update(shash, task_id, tsem_digestsize()); +} + +static int add_ipc_cred(struct shash_desc *shash, struct tsem_ipc_args *args) +{ + int retn; + + retn = add_u32(shash, args->out.perm.uid); + if (retn) + goto done; + + retn = add_u32(shash, args->out.perm.gid); + if (retn) + goto done; + + retn = add_u32(shash, args->out.perm.cuid); + if (retn) + goto done; + + retn = add_u32(shash, args->out.perm.cgid); + if (retn) + goto done; + + retn = add_u16(shash, args->out.perm.mode); + if (retn) + goto done; + + retn = crypto_shash_update(shash, args->out.owner, tsem_digestsize()); + + done: + return retn; +} + +static int add_socket_connect_bind(struct shash_desc *shash, + struct tsem_event *ep) +{ + int retn; + char *p; + size_t size; + struct tsem_socket_args *args = &ep->CELL.socket; + + retn = add_socket(shash, &args->out.socka); + if (retn) + goto done; + + switch (args->out.socka.family) { + case AF_INET: + retn = add_u16(shash, args->out.ipv4.sin_port); + if (retn) + goto done; + + retn = add_u32(shash, args->out.ipv4.sin_addr.s_addr); + break; + + case AF_INET6: + retn = add_u16(shash, args->out.ipv6.sin6_port); + if (retn) + goto done; + + p = (u8 *) args->out.ipv6.sin6_addr.in6_u.u6_addr8; + size = sizeof(args->out.ipv6.sin6_addr.in6_u.u6_addr8); + retn = crypto_shash_update(shash, p, size); + if (retn) + goto done; + + retn = add_u32(shash, args->out.ipv6.sin6_flowinfo); + if (retn) + goto done; + + retn = add_u32(shash, args->out.ipv6.sin6_scope_id); + break; + + case AF_UNIX: + p = args->out.path; + size = strlen(args->out.path); + retn = crypto_shash_update(shash, p, size); + break; + + default: + p = (u8 *) args->out.mapping; + size = tsem_digestsize(); + retn = crypto_shash_update(shash, p, size); + break; + } + + done: + return retn; +} + +static int add_socket_accept(struct shash_desc *shash, struct tsem_event *ep) +{ + char *p; + int retn, size; + struct tsem_socket_args *args = &ep->CELL.socket; + + retn = add_socket(shash, &args->out.socka); + if (retn) + goto done; + + switch (args->out.socka.family) { + case AF_INET: + retn = add_u16(shash, args->out.ipv4.sin_port); + if (retn) + goto done; + + retn = add_u32(shash, args->out.ipv4.sin_addr.s_addr); + break; + + case AF_INET6: + retn = add_u16(shash, args->out.ipv6.sin6_port); + if (retn) + goto done; + + p = (u8 *) args->out.ipv6.sin6_addr.in6_u.u6_addr8; + size = sizeof(args->out.ipv6.sin6_addr.in6_u.u6_addr8); + retn = crypto_shash_update(shash, p, size); + break; + + case AF_UNIX: + p = args->out.path; + size = strlen(args->out.path); + retn = crypto_shash_update(shash, p, size); + break; + + default: + p = args->out.mapping; + size = tsem_digestsize(); + retn = crypto_shash_update(shash, p, size); + break; + } + + done: + return retn; +} + +static int add_socket_msg(struct shash_desc *shash, struct tsem_event *ep) +{ + char *p; + int size, retn; + struct sockaddr_in *ipv4; + struct sockaddr_in6 *ipv6; + struct tsem_socket_args *args = &ep->CELL.socket; + + retn = add_socket(shash, &args->out.socka); + if (retn) + goto done; + + if (!args->out.have_addr) + goto done; + + if (args->out.socka.family == AF_INET) { + ipv4 = &args->out.ipv4; + retn = add_u16(shash, ipv4->sin_port); + if (retn) + goto done; + + retn = add_u32(shash, ipv4->sin_addr.s_addr); + if (retn) + goto done; + } + if (args->out.socka.family == AF_INET6) { + ipv6 = &args->out.ipv6; + retn = add_u16(shash, ipv6->sin6_port); + if (retn) + goto done; + + p = (u8 *) &ipv6->sin6_addr.in6_u.u6_addr8; + size = sizeof(ipv6->sin6_addr.in6_u.u6_addr8); + retn = crypto_shash_update(shash, p, size); + if (retn) + goto done; + } + + done: + return retn; +} + +static int add_xattr(struct shash_desc *shash, struct tsem_event *ep) +{ + int retn; + struct tsem_inode_xattr_args *args = &ep->CELL.inode_xattr; + + retn = add_dentry(shash, &args->out.dentry); + if (retn) + return retn; + + if (ep->event == TSEM_INODE_LISTXATTR) + return 0; + + retn = add_str(shash, args->out.name); + if (retn) + return retn; + + if (ep->event == TSEM_INODE_GETXATTR || + ep->event == TSEM_INODE_REMOVEXATTR) + return 0; + + retn = crypto_shash_update(shash, args->out.value, args->out.size); + if (retn) + return retn; + + return add_u32(shash, args->out.flags); +} + +static int add_inode_rename(struct shash_desc *shash, struct tsem_event *ep) +{ + int retn; + struct tsem_inode_args *args = &ep->CELL.inode; + + retn = add_inode(shash, &args->out.dir); + if (retn) + goto done; + + retn = add_dentry(shash, &args->out.dentry); + if (retn) + goto done; + + retn = add_inode(shash, &args->out.new_dir); + if (retn) + goto done; + + retn = add_dentry(shash, &args->out.new_dentry); + + done: + return retn; + +} + +static int add_inode_create(struct shash_desc *shash, struct tsem_event *ep) +{ + int retn = 0; + struct tsem_inode_args *args = &ep->CELL.inode; + + retn = add_inode(shash, &args->out.dir); + if (retn) + goto done; + + retn = add_dentry(shash, &ep->CELL.inode.out.dentry); + if (retn) + goto done; + retn = add_u16(shash, ep->CELL.inode.mode); + + done: + return retn; +} + +static int add_sb_umount(struct shash_desc *shash, struct tsem_event *ep) +{ + int retn; + struct tsem_sb_args *args = &ep->CELL.sb; + + retn = add_dentry(shash, &args->out.dentry); + if (retn) + goto done; + + retn = add_u32(shash, args->flags); + + done: + return retn; +} + +static int add_sb_remount(struct shash_desc *shash, struct tsem_event *ep) +{ + int retn; + struct tsem_sb_args *args = &ep->CELL.sb; + + retn = add_dentry(shash, &args->out.dentry); + if (retn) + goto done; + + retn = add_str(shash, ep->CELL.sb.out.type); + if (retn) + goto done; + + retn = add_u64(shash, ep->CELL.sb.flags); + + done: + return retn; +} + +static int add_inode_link(struct shash_desc *shash, struct tsem_event *ep) +{ + int retn; + struct tsem_inode_args *args = &ep->CELL.inode; + + retn = add_dentry(shash, &args->out.dentry); + if (retn) + goto done; + + retn = add_inode(shash, &args->out.dir); + if (retn) + goto done; + + retn = add_dentry(shash, &args->out.new_dentry); + + done: + return retn; +} + +static int add_quotactl(struct shash_desc *shash, struct tsem_event *ep) +{ + int retn; + struct tsem_quota_args *args = &ep->CELL.quota; + + retn = add_u32(shash, args->cmds); + if (retn) + goto done; + + retn = add_u32(shash, args->type); + if (retn) + goto done; + + retn = add_u32(shash, args->id); + if (retn) + goto done; + + retn = add_dentry(shash, &args->out.dentry); + if (retn) + goto done; + + retn = add_str(shash, args->out.fstype); + if (retn) + goto done; + + retn = add_u64(shash, args->out.s_flags); + + done: + return retn; +} + +static int add_mmap_file(struct shash_desc *shash, struct tsem_event *ep) +{ + int retn; + struct tsem_mmap_file_args *args = &ep->CELL.mmap_file; + + if (!args->anonymous) { + retn = add_file(shash, &args->file); + if (retn) + goto done; + } + + retn = add_u32(shash, args->prot); + if (retn) + goto done; + + retn = add_u32(shash, args->flags); + + done: + return retn; +} + +static int add_task_setrlimit(struct shash_desc *shash, struct tsem_event *ep) +{ + int retn; + struct tsem_task_kill_args *args = &ep->CELL.task_kill; + + retn = add_task(shash, args->target); + if (retn) + goto done; + + retn = add_u32(shash, args->u.resource); + if (retn) + goto done; + + retn = add_u64(shash, args->cur); + if (retn) + goto done; + + retn = add_u64(shash, args->max); + + done: + return retn; +} + +static int add_move_path(struct shash_desc *shash, struct tsem_event *ep) +{ + int retn; + struct tsem_sb_args *args = &ep->CELL.sb; + + retn = add_path(shash, &args->out.path); + if (retn) + goto done; + + add_path(shash, &args->out.path2); + + done: + return retn; +} + +static int add_task_kill(struct shash_desc *shash, struct tsem_event *ep) +{ + int retn; + struct tsem_task_kill_args *args = &ep->CELL.task_kill; + + retn = add_task(shash, args->target); + if (retn) + goto done; + + retn = add_u32(shash, args->signal); + if (retn) + goto done; + + retn = add_u32(shash, args->cross_model); + + done: + return retn; +} + +static int add_ptrace_access_check(struct shash_desc *shash, + struct tsem_event *ep) +{ + int retn; + struct tsem_task_kill_args *args = &ep->CELL.task_kill; + + retn = add_task(shash, args->target); + if (retn) + goto done; + + retn = add_u32(shash, args->u.resource); + + done: + return retn; +} + +static int add_capget(struct shash_desc *shash, struct tsem_event *ep) +{ + int retn; + struct tsem_capability_args *args = &ep->CELL.capability; + + retn = add_task(shash, args->target); + if (retn) + goto done; + + retn = add_u64(shash, args->effective.val); + if (retn) + goto done; + + retn = add_u64(shash, args->inheritable.val); + if (retn) + goto done; + + retn = add_u64(shash, args->permitted.val); + + done: + return retn; +} + +static int add_capset(struct shash_desc *shash, struct tsem_event *ep) +{ + int retn; + struct tsem_capability_args *args = &ep->CELL.capability; + + retn = add_u64(shash, args->effective.val); + if (retn) + goto done; + + retn = add_u64(shash, args->inheritable.val); + if (retn) + goto done; + + retn = add_u64(shash, args->permitted.val); + + done: + return retn; +} + +static int add_capable(struct shash_desc *shash, struct tsem_event *ep) +{ + int retn; + struct tsem_capability_args *args = &ep->CELL.capability; + + retn = add_u32(shash, args->cap); + if (retn) + goto done; + + retn = add_u32(shash, args->opts); + + done: + return retn; +} + +static int add_key_permission(struct shash_desc *shash, struct tsem_event *ep) +{ + int retn; + struct tsem_key_args *args = &ep->CELL.key; + + retn = add_u32(shash, args->out.possessed); + if (retn) + goto done; + + retn = add_u16(shash, args->out.uid); + if (retn) + goto done; + + retn = add_u16(shash, args->out.gid); + if (retn) + goto done; + + retn = add_u64(shash, args->out.flags); + if (retn) + goto done; + + retn = add_creds(shash, &args->out.cred); + if (retn) + goto done; + + retn = add_u32(shash, args->out.perm); + + done: + return retn; +} + +static int add_inode_setattr(struct shash_desc *shash, struct tsem_event *ep) +{ + int retn; + struct tsem_inode_attr_args *args = &ep->CELL.inode_attr; + + retn = add_dentry(shash, &args->out.dentry); + if (retn) + goto done; + + retn = add_u32(shash, args->out.valid); + if (retn) + goto done; + + retn = add_u32(shash, args->out.mode); + if (retn) + goto done; + + retn = add_u32(shash, args->out.uid); + if (retn) + goto done; + + retn = add_u32(shash, args->out.gid); + if (retn) + goto done; + + retn = add_u64(shash, args->out.size); + + done: + return retn; +} + +static int add_bpf(struct shash_desc *shash, struct tsem_event *ep) +{ + int retn; + struct tsem_bpf_args *args = &ep->CELL.bpf; + + retn = add_u32(shash, args->bpf.cmd); + if (!retn) + goto done; + + retn = add_u32(shash, args->bpf.size); + + done: + return retn; +} + +static int add_bpf_map(struct shash_desc *shash, struct tsem_event *ep) +{ + int retn; + struct tsem_bpf_args *args = &ep->CELL.bpf; + + retn = add_u32(shash, args->map.map_type); + if (!retn) + goto done; + + retn = add_u32(shash, args->map.fmode); + + done: + return retn; +} + +static int add_bpf_prog(struct shash_desc *shash, struct tsem_event *ep) +{ + int retn; + struct tsem_bpf_args *args = &ep->CELL.bpf; + + retn = add_u32(shash, args->prog.type); + if (!retn) + goto done; + + retn = add_u32(shash, args->prog.attach_type); + + done: + return retn; +} + +static int add_settime(struct shash_desc *shash, struct tsem_event *ep) +{ + int retn; + struct tsem_time_args *args = &ep->CELL.time; + + if (args->have_ts) { + retn = add_u64(shash, ep->CELL.time.seconds); + if (retn) + goto done; + + retn = add_u64(shash, ep->CELL.time.nsecs); + if (retn) + goto done; + } + + if (args->have_tz) { + retn = add_u32(shash, ep->CELL.time.minuteswest); + if (retn) + goto done; + + retn = add_u32(shash, ep->CELL.time.dsttime); + if (retn) + goto done; + } + + done: + return retn; +} + +static int get_cell_mapping(struct tsem_event *ep, u8 *mapping) +{ + int retn = 0, size; + u8 *p; + SHASH_DESC_ON_STACK(shash, tfm); + + shash->tfm = tsem_digest(); + retn = crypto_shash_init(shash); + if (retn) + goto done; + + switch (ep->event) { + case TSEM_NETLINK_SEND: + retn = add_socket(shash, &ep->CELL.socket.out.socka); + if (retn) + goto done; + + retn = add_u32(shash, ep->CELL.netlink.out.uid); + if (retn) + goto done; + + retn = add_u32(shash, ep->CELL.netlink.out.gid); + if (retn) + goto done; + + retn = add_u32(shash, ep->CELL.netlink.out.portid); + if (retn) + goto done; + + retn = add_u32(shash, ep->CELL.netlink.out.dst_group); + if (retn) + goto done; + + retn = add_u32(shash, ep->CELL.netlink.out.flags); + if (retn) + goto done; + + if (ep->CELL.netlink.out.nsid_set) + retn = add_u32(shash, ep->CELL.netlink.out.flags); + break; + + case TSEM_IPC_PERMISSION: + retn = add_ipc_cred(shash, &ep->CELL.ipc); + if (retn) + goto done; + + retn = add_u16(shash, ep->CELL.ipc.perm_flag); + break; + + case TSEM_SHM_ASSOCIATE: + case TSEM_SHM_SHMCTL: + case TSEM_SHM_SHMAT: + case TSEM_SEM_ASSOCIATE: + case TSEM_SEM_SEMCTL: + case TSEM_MSG_QUEUE_ASSOCIATE: + case TSEM_MSG_QUEUE_MSGCTL: + retn = add_ipc_cred(shash, &ep->CELL.ipc); + if (retn) + goto done; + + retn = add_u32(shash, ep->CELL.ipc.value); + break; + + case TSEM_MSG_QUEUE_MSGRCV: + retn = add_ipc_cred(shash, &ep->CELL.ipc); + if (retn) + goto done; + + p = ep->CELL.ipc.out.target; + size = tsem_digestsize(); + retn = crypto_shash_update(shash, p, size); + if (retn) + goto done; + + retn = add_u64(shash, ep->CELL.ipc.type); + if (retn) + goto done; + + retn = add_u32(shash, ep->CELL.ipc.value); + break; + + case TSEM_SEM_SEMOP: + retn = add_ipc_cred(shash, &ep->CELL.ipc); + if (retn) + goto done; + + retn = add_u32(shash, ep->CELL.ipc.nsops); + if (retn) + goto done; + + retn = add_u32(shash, ep->CELL.ipc.value); + break; + + case TSEM_INODE_CREATE: + retn = add_inode_create(shash, ep); + break; + + case TSEM_INODE_MKDIR: + retn = add_inode(shash, &ep->CELL.inode.out.dir); + if (retn) + goto done; + + retn = add_dentry(shash, &ep->CELL.inode.out.dentry); + if (retn) + goto done; + + retn = add_u16(shash, ep->CELL.inode.mode); + break; + + case TSEM_INODE_RMDIR: + case TSEM_INODE_UNLINK: + retn = add_inode(shash, &ep->CELL.inode.out.dir); + if (retn) + goto done; + + retn = add_dentry(shash, &ep->CELL.inode.out.dentry); + break; + + case TSEM_SYSLOG: + retn = add_u32(shash, ep->CELL.value); + break; + + case TSEM_SETTIME: + retn = add_settime(shash, ep); + break; + + case TSEM_INODE_LINK: + retn = add_inode_link(shash, ep); + break; + + case TSEM_INODE_SYMLINK: + retn = add_inode(shash, &ep->CELL.inode.out.dir); + if (retn) + goto done; + + retn = add_dentry(shash, &ep->CELL.inode.out.dentry); + if (retn) + goto done; + + retn = add_str(shash, ep->CELL.inode.out.old_name); + break; + + case TSEM_INODE_MKNOD: + retn = add_inode(shash, &ep->CELL.inode.out.dir); + if (retn) + goto done; + + retn = add_dentry(shash, &ep->CELL.inode.out.dentry); + if (retn) + goto done; + + retn = add_u16(shash, ep->CELL.inode.mode); + if (retn) + goto done; + + retn = add_u32(shash, ep->CELL.inode.dev); + break; + + case TSEM_INODE_RENAME: + retn = add_inode_rename(shash, ep); + break; + + case TSEM_INODE_KILLPRIV: + retn = add_dentry(shash, &ep->CELL.inode.out.dentry); + break; + + case TSEM_FILE_OPEN: + case TSEM_BPRM_COMMITTED_CREDS: + retn = add_file(shash, &ep->CELL.file); + break; + + case TSEM_FILE_IOCTL: + case TSEM_FILE_LOCK: + case TSEM_FILE_FCNTL: + retn = add_file(shash, &ep->CELL.file); + if (retn) + goto done; + + retn = add_u32(shash, ep->CELL.file.cmd); + break; + + case TSEM_FILE_RECEIVE: + retn = add_file(shash, &ep->CELL.file); + break; + + case TSEM_MMAP_FILE: + retn = add_mmap_file(shash, ep); + break; + + case TSEM_UNIX_STREAM_CONNECT: + case TSEM_UNIX_MAY_SEND: + case TSEM_SOCKET_SOCKETPAIR: + retn = add_socket(shash, &ep->CELL.socket.out.socka); + if (retn) + goto done; + + retn = add_socket(shash, &ep->CELL.socket.out.sockb); + break; + + case TSEM_SOCKET_SENDMSG: + case TSEM_SOCKET_RECVMSG: + retn = add_socket_msg(shash, ep); + break; + + case TSEM_SOCKET_GETSOCKNAME: + case TSEM_SOCKET_GETPEERNAME: + case TSEM_TUN_DEV_ATTACH_QUEUE: + retn = add_socket(shash, &ep->CELL.socket.out.socka); + break; + + case TSEM_SOCKET_CREATE: + retn = add_u32(shash, ep->CELL.socket.out.socka.family); + if (retn) + goto done; + + retn = add_u32(shash, ep->CELL.socket.out.socka.type); + if (retn) + goto done; + + retn = add_u32(shash, ep->CELL.socket.out.socka.protocol); + if (retn) + goto done; + + retn = add_u32(shash, ep->CELL.socket.out.socka.kern); + break; + + case TSEM_SOCKET_CONNECT: + case TSEM_SOCKET_BIND: + retn = add_socket_connect_bind(shash, ep); + break; + + case TSEM_SOCKET_ACCEPT: + retn = add_socket_accept(shash, ep); + break; + + case TSEM_SOCKET_LISTEN: + case TSEM_SOCKET_SHUTDOWN: + retn = add_socket(shash, &ep->CELL.socket.out.socka); + if (retn) + goto done; + + retn = add_u32(shash, ep->CELL.socket.value); + break; + + case TSEM_SOCKET_SETSOCKOPT: + retn = add_socket(shash, &ep->CELL.socket.out.socka); + if (retn) + goto done; + + retn = add_u32(shash, ep->CELL.socket.value); + if (retn) + goto done; + + retn = add_u32(shash, ep->CELL.socket.optname); + break; + + case TSEM_KERNEL_MODULE_REQUEST: + retn = add_str(shash, ep->CELL.kernel.out.kmod_name); + break; + + case TSEM_KERNEL_LOAD_DATA: + retn = add_u32(shash, ep->CELL.kernel.id); + if (retn) + goto done; + + retn = add_u32(shash, ep->CELL.kernel.contents); + break; + + case TSEM_KERNEL_READ_FILE: + retn = add_file(shash, &ep->CELL.kernel.out.file); + if (retn) + goto done; + + retn = add_u32(shash, ep->CELL.kernel.id); + if (retn) + goto done; + + retn = add_u32(shash, ep->CELL.kernel.contents); + break; + + case TSEM_TASK_KILL: + retn = add_task_kill(shash, ep); + break; + + case TSEM_PTRACE_ACCESS_CHECK: + retn = add_ptrace_access_check(shash, ep); + break; + + case TSEM_PTRACE_TRACEME: + retn = add_task(shash, ep->CELL.task_kill.source); + break; + + case TSEM_CAPGET: + retn = add_capget(shash, ep); + break; + + case TSEM_CAPSET: + retn = add_capset(shash, ep); + break; + + case TSEM_CAPABLE: + retn = add_capable(shash, ep); + break; + + case TSEM_TASK_SETPGID: + p = ep->CELL.task_kill.target; + size = sizeof(ep->CELL.task_kill.target); + retn = crypto_shash_update(shash, p, size); + if (retn) + goto done; + + p = ep->CELL.task_kill.source; + size = sizeof(ep->CELL.task_kill.source); + retn = crypto_shash_update(shash, p, size); + break; + + case TSEM_TASK_GETPGID: + case TSEM_TASK_GETSID: + case TSEM_TASK_GETIOPRIO: + case TSEM_TASK_SETSCHEDULER: + case TSEM_TASK_GETSCHEDULER: + retn = add_task(shash, ep->CELL.task_kill.target); + break; + + case TSEM_TASK_SETNICE: + case TSEM_TASK_SETIOPRIO: + p = ep->CELL.task_kill.target; + size = sizeof(ep->CELL.task_kill.target); + retn = crypto_shash_update(shash, p, size); + if (retn) + goto done; + + retn = add_u32(shash, ep->CELL.task_kill.u.value); + break; + + case TSEM_TASK_PRLIMIT: + retn = add_creds(shash, &ep->CELL.task_prlimit.out.cred); + if (retn) + goto done; + + retn = add_creds(shash, &ep->CELL.task_prlimit.out.tcred); + if (retn) + goto done; + + retn = add_u32(shash, ep->CELL.task_prlimit.flags); + break; + + case TSEM_TASK_SETRLIMIT: + retn = add_task_setrlimit(shash, ep); + break; + + case TSEM_TASK_PRCTL: + retn = add_u32(shash, ep->CELL.task_prctl.option); + if (retn) + goto done; + + if (ep->CELL.task_prctl.option != PR_GET_PDEATHSIG) + retn = add_u64(shash, ep->CELL.task_prctl.arg2); + else + retn = add_u64(shash, 0); + if (retn) + goto done; + + retn = add_u64(shash, ep->CELL.task_prctl.arg4); + if (retn) + goto done; + + retn = add_u64(shash, ep->CELL.task_prctl.arg5); + break; + + case TSEM_INODE_GETATTR: + retn = add_dentry(shash, &ep->CELL.inode_attr.out.dentry); + break; + + case TSEM_INODE_SETATTR: + retn = add_inode_setattr(shash, ep); + break; + + case TSEM_INODE_SETXATTR: + case TSEM_INODE_GETXATTR: + case TSEM_INODE_REMOVEXATTR: + case TSEM_INODE_LISTXATTR: + retn = add_xattr(shash, ep); + break; + + case TSEM_KEY_ALLOC: + retn = add_creds(shash, &ep->CELL.key.out.cred); + if (retn) + goto done; + + retn = add_u64(shash, ep->CELL.key.flags); + break; + + case TSEM_KEY_PERMISSION: + retn = add_key_permission(shash, ep); + break; + + case TSEM_SB_MOUNT: + if (ep->CELL.sb.out.dev_name) { + retn = add_str(shash, ep->CELL.sb.out.dev_name); + if (retn) + goto done; + } + + retn = add_path(shash, &ep->CELL.sb.out.path); + if (retn) + goto done; + + if (ep->CELL.sb.out.type) { + retn = add_str(shash, ep->CELL.sb.out.type); + if (retn) + goto done; + } + + retn = add_u64(shash, ep->CELL.sb.flags); + break; + + case TSEM_SB_UMOUNT: + retn = add_sb_umount(shash, ep); + break; + + case TSEM_SB_REMOUNT: + retn = add_sb_remount(shash, ep); + break; + + case TSEM_SB_STATFS: + retn = add_dentry(shash, &ep->CELL.sb.out.dentry); + break; + + case TSEM_SB_PIVOTROOT: + case TSEM_MOVE_MOUNT: + retn = add_move_path(shash, ep); + break; + + case TSEM_QUOTACTL: + retn = add_quotactl(shash, ep); + break; + + case TSEM_QUOTA_ON: + retn = add_dentry(shash, &ep->CELL.quota.out.dentry); + break; + + case TSEM_BPF: + retn = add_bpf(shash, ep); + break; + + case TSEM_BPF_MAP: + retn = add_bpf_map(shash, ep); + break; + + case TSEM_BPF_PROG: + retn = add_bpf_prog(shash, ep); + break; + + default: + p = (u8 *) tsem_names[ep->event]; + size = strlen(tsem_names[ep->event]); + retn = crypto_shash_update(shash, p, size); + if (retn) + goto done; + + p = tsem_context(current)->zero_digest; + size = tsem_digestsize(); + retn = crypto_shash_update(shash, p, size); + break; + } + + done: + if (!retn) + retn = crypto_shash_final(shash, mapping); + + if (ep->event == TSEM_INODE_SETXATTR) { + kfree(ep->CELL.inode_xattr.out.value); + ep->CELL.inode_xattr.out.value = NULL; + } + + return retn; +} + +static int get_event_mapping(int event, u8 *p_task_id, u8 *task_id, + u8 *COE_id, u8 *cell_id, u8 *mapping) +{ + int retn = 0; + u32 event_id = (u32) event; + SHASH_DESC_ON_STACK(shash, tfm); + + shash->tfm = tsem_digest(); + retn = crypto_shash_init(shash); + if (retn) + goto done; + + retn = crypto_shash_update(shash, tsem_names[event_id], + strlen(tsem_names[event_id])); + if (retn) + goto done; + + retn = crypto_shash_update(shash, p_task_id, tsem_digestsize()); + if (retn) + goto done; + + retn = crypto_shash_update(shash, task_id, tsem_digestsize()); + if (retn) + goto done; + + retn = crypto_shash_update(shash, COE_id, tsem_digestsize()); + if (retn) + goto done; + + retn = crypto_shash_finup(shash, cell_id, tsem_digestsize(), mapping); + + done: + return retn; +} + +static int map_event(struct tsem_event *ep, u8 *p_task_id, u8 *task_id, + u8 *event_mapping) +{ + int retn; + u8 COE_mapping[HASH_MAX_DIGESTSIZE]; + u8 cell_mapping[HASH_MAX_DIGESTSIZE]; + + retn = get_COE_mapping(ep, COE_mapping); + if (retn) + goto done; + + retn = get_cell_mapping(ep, cell_mapping); + if (retn) + goto done; + + retn = get_event_mapping(ep->event, p_task_id, task_id, COE_mapping, + cell_mapping, event_mapping); + done: + return retn; +} + +/** + * tsem_map_task() - Create the task identity description structure. + * @file: A pointer to the file structure defining the executable. + * @task_id: Pointer to the buffer that the task id will be copied to. + * + * This function creates the security event state point that will be used + * as the task identifier for the generation of security state points + * that are created by the process that task identifier is assigned to. + * + * Return: This function returns 0 if the mapping was successfully + * created and an error value otherwise. + */ +int tsem_map_task(struct file *file, u8 *task_id) +{ + int retn; + u8 null_taskid[HASH_MAX_DIGESTSIZE]; + struct tsem_event *ep; + + ep = tsem_event_allocate(TSEM_BPRM_COMMITTED_CREDS, false); + if (!ep) + return -ENOMEM; + + ep->CELL.file.in.file = file; + retn = tsem_event_init(ep); + if (retn > 0) { + memset(null_taskid, '\0', tsem_digestsize()); + retn = map_event(ep, tsem_task(current)->p_task_id, + null_taskid, task_id); + } + + tsem_event_put(ep); + return retn; +} + +/** + * tsem_map_event() - Create a security event mapping. + * @event: The number of the event to be mapped. + * @params: A pointer to the structure containing the event description + * parameters. + * + * This function creates a structure to describe a security event + * and maps the event into a security state coefficient. + * + * Return: This function returns a value of zero on success and a negative + * error code on failure. + */ +int tsem_map_event(struct tsem_event *ep) +{ + struct tsem_task *task = tsem_task(current); + + return map_event(ep, task->p_task_id, task->task_id, ep->mapping); +} From patchwork Mon Aug 26 10:37:25 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Dr. Greg" X-Patchwork-Id: 13777626 X-Patchwork-Delegate: paul@paul-moore.com Received: from blizzard.enjellic.com (wind.enjellic.com [76.10.64.91]) by smtp.subspace.kernel.org (Postfix) with ESMTP id DA1531714B3; Mon, 26 Aug 2024 10:50:27 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=76.10.64.91 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1724669430; cv=none; b=HHNtV3pZ5T1T+y4nley7KYS+jsQ8DBtT2vZZ69bugeNV5/YQ4pgKVnXlriFCO3Hp7KdCyuztxd4OnZ7WfjK7voAkNYO7UiDWG8T1sCwvxNsauBx9aBW2zdRY+1v25XQk3jzhb2W8sIdjXD8tR7IamMlhMr8HFlgAPvP/WEAv/BA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1724669430; c=relaxed/simple; bh=3LssYctTpg/gzClVmDqIZ5f7/5HW7bsMHzLhYZuLblc=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=FrSUFVmhYY0I8yqRgbeT2Z8r3a0AeP8FhnK3VDGrzGMyNGOoVzLgG2NQle6Nv0Rb8cgFiQ221JTzTCx1PenCJrPv9LEjA2c/0Wkk4TPaCghbxI3+ChDhlO5rFSKZVBpBeubsJgx5+7vCeIaQqoMSe4Niwo8CD5ZWUxnYn2iRXgY= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=enjellic.com; spf=pass smtp.mailfrom=enjellic.com; arc=none smtp.client-ip=76.10.64.91 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=enjellic.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=enjellic.com Received: from blizzard.enjellic.com (localhost [127.0.0.1]) by blizzard.enjellic.com (8.15.2/8.15.2) with ESMTP id 47QAbVex003445; Mon, 26 Aug 2024 05:37:31 -0500 Received: (from greg@localhost) by blizzard.enjellic.com (8.15.2/8.15.2/Submit) id 47QAbVW8003444; Mon, 26 Aug 2024 05:37:31 -0500 X-Authentication-Warning: blizzard.enjellic.com: greg set sender to greg@enjellic.com using -f From: Greg Wettstein To: linux-security-module@vger.kernel.org, linux-kernel@vger.kernel.org Cc: jmorris@namei.org Subject: [PATCH v4 11/14] Implement the internal Trusted Modeling Agent. Date: Mon, 26 Aug 2024 05:37:25 -0500 Message-Id: <20240826103728.3378-12-greg@enjellic.com> X-Mailer: git-send-email 2.39.1 In-Reply-To: <20240826103728.3378-1-greg@enjellic.com> References: <20240826103728.3378-1-greg@enjellic.com> Precedence: bulk X-Mailing-List: linux-security-module@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 The model.c file is responsible for managing the security state information for a security modeling namespace. Important to note is that the map.c file is responsible for generating the coefficients and hence the implementation of the model. The model.c file maintains the state information and makes the decision with respect to whether or not a presented event is consistent with the model being enforced. By default the TMA runs in free modeling mode where all security coefficients and there associated descriptions are considered valid and retained as the security execution trajectory for the namespace. The model implementation can be 'sealed' through the TSEM control plane, a condition that causes any state points not registered in the model to be considered a 'forensics' event. The description of such an event is added to the forensics execution trajectory for the model. A forensics event does not result in permission to the event to be denied unless the model is placed in 'enforcing' mode. The TMA implementation also supports the definition of 'base' point that is binary value equal in length to the cryptographic hash function being used by the security modeling namespace. This base point is used to linear extend each security state coefficient before it is added to the model. This allows a relying part to confirm the 'freshnes' of the functional state of the model. The modeling implementation supports two different functional values for the model being implemented. The classic linear extension sum of all the security state points and a value referred to as the 'state' of the security model. The classic linear extension sum is similar to what would be implemented by a Platform Configuration Register in a Trusted Platform Module. It can be used verify the order in which security event occurred but is also fragile with respect to process scheduling dependencies, particularly on SMP platforms. The 'state' value is designed to make the measurement value invariant to these scheduling induced variations. The state value is computed by sorting the security event state points in the model in big-endian (natural hash byte order) format and then computing the extension sum over this sorted vector of points. This value verifies that the model has only existed in a prescribed set of security states. --- security/tsem/model.c | 758 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 758 insertions(+) create mode 100644 security/tsem/model.c diff --git a/security/tsem/model.c b/security/tsem/model.c new file mode 100644 index 000000000000..013a24b88c83 --- /dev/null +++ b/security/tsem/model.c @@ -0,0 +1,758 @@ +// SPDX-License-Identifier: GPL-2.0-only + +/* + * Copyright (C) 2024 Enjellic Systems Development, LLC + * Author: Dr. Greg Wettstein + + * This file is responsible for maintaining the security model state + * information for the internal trusted modeling agent implementation. + * + * The state information consists of security event descriptions that + * have been experienced by a security modeling namespace and the + * security state coefficients that they map into. Only unique + * descriptions and their coefficients are maintained for maintained + * for each security modeling namespace. + * + * An important point to note is that the routines in this file do not + * implement a security model. The mapping of security event + * descriptions into coefficients, in the map.c file, is the + * functionality that actually implements the model. Future + * implementations of TSEM are anticipated to provide alternate models + * by providing alternative implementations of the mapping routines. + * + * The generative functions used to create the security state + * coefficients managed by this file are fully described in the + * primary TSEM documentation file. + * + * As with the event.c and export.c files this file must handle events + * that run in both atomic and non-atomic context. For processes that + * run in non-atomic context the structures that represent a security + * state coefficient are provided by the kmem_cache structure + * implement in this file. + * + * In order to support processes that are running in atomic context a + * magazine of structure is maintained as well. Allocations from this + * magazine are replaced with a function that runs in the context of + * an asynchronous workqueue. + */ + +#include +#include + +#include "tsem.h" + +struct pseudonym { + struct list_head list; + u8 mapping[HASH_MAX_DIGESTSIZE]; +}; + +static struct kmem_cache *point_cachep; + +static void refill_point_magazine(struct work_struct *work) +{ + struct tsem_event_point *tep; + struct tsem_work *ws; + + ws = container_of(work, struct tsem_work, work); + + tep = kmem_cache_zalloc(point_cachep, GFP_KERNEL); + if (!tep) { + pr_warn("tsem: Cannot refill model point magazine.\n"); + return; + } + + spin_lock(&ws->u.model->magazine_lock); + ws->u.model->magazine[ws->index] = tep; + clear_bit(ws->index, ws->u.model->magazine_index); + + /* + * The following memory barrier is used to cause the magazine + * index to be visible after the refill of the cache slot. + */ + smp_mb__after_atomic(); + + spin_unlock(&ws->u.model->magazine_lock); +} + +static struct tsem_event_point *alloc_event_point(struct tsem_model *model, + bool locked) +{ + unsigned int index; + struct tsem_event_point *tep = NULL; + + if (!locked) + return kmem_cache_zalloc(point_cachep, GFP_KERNEL); + + spin_lock(&model->magazine_lock); + index = find_first_zero_bit(model->magazine_index, + model->magazine_size); + if (index < model->magazine_size) { + tep = model->magazine[index]; + model->ws[index].index = index; + model->ws[index].u.model = model; + set_bit(index, model->magazine_index); + + /* + * Similar to the issue noted in the refill_point_magazine(), + * function, this barrier is used to cause the consumption + * of the cache entry to become visible. + */ + smp_mb__after_atomic(); + } + spin_unlock(&model->magazine_lock); + + if (tep) { + INIT_WORK(&model->ws[index].work, refill_point_magazine); + queue_work(system_wq, &model->ws[index].work); + return tep; + } + + pr_warn("tsem: Fail model point allocation comm %s ns %llu cs %u.\n", + current->comm, tsem_context(current)->id, + model->magazine_size); + return NULL; + +} + +static int magazine_allocate(struct tsem_model *model, size_t size) +{ + unsigned int lp; + int retn = -ENOMEM; + + model->magazine_size = size; + + spin_lock_init(&model->magazine_lock); + + model->magazine_index = bitmap_zalloc(model->magazine_size, + GFP_KERNEL); + if (!model->magazine_index) + return retn; + + model->magazine = kcalloc(model->magazine_size, + sizeof(*model->magazine), GFP_KERNEL); + if (!model->magazine) + goto done; + + for (lp = 0; lp < model->magazine_size; ++lp) { + model->magazine[lp] = kmem_cache_zalloc(point_cachep, + GFP_KERNEL); + if (!model->magazine[lp]) + goto done; + } + + model->ws = kcalloc(model->magazine_size, sizeof(*model->ws), + GFP_KERNEL); + if (model->ws) + retn = 0; + + done: + if (retn) + tsem_model_magazine_free(model); + + return retn; +} + +static int generate_pseudonym(char *pathname, u8 *pseudonym) +{ + int retn = 0; + u32 size; + SHASH_DESC_ON_STACK(shash, tfm); + + shash->tfm = tsem_digest(); + retn = crypto_shash_init(shash); + if (retn) + goto done; + + size = strlen(pathname); + retn = crypto_shash_update(shash, (u8 *) &size, sizeof(size)); + if (retn) + goto done; + + retn = crypto_shash_finup(shash, pathname, size, pseudonym); + + done: + return retn; +} + +static struct tsem_event_point *have_point(u8 *point) +{ + struct tsem_event_point *entry, *retn = NULL; + struct tsem_context *ctx = tsem_context(current); + struct tsem_model *model = ctx->model; + + spin_lock(&model->point_lock); + list_for_each_entry(entry, &model->point_list, list) { + if (!memcmp(entry->point, point, tsem_digestsize())) { + retn = entry; + goto done; + } + } + + done: + spin_unlock(&model->point_lock); + return retn; +} + +static struct tsem_event_point *add_event_point(u8 *point, bool valid, + bool locked) +{ + struct tsem_event_point *entry; + struct tsem_model *model = tsem_model(current); + + entry = alloc_event_point(model, locked); + if (!entry) + return ERR_PTR(-ENOMEM); + + entry->valid = valid; + memcpy(entry->point, point, tsem_digestsize()); + + spin_lock(&model->point_lock); + ++model->point_count; + list_add_tail(&entry->list, &model->point_list); + spin_unlock(&model->point_lock); + + return entry; +} + +static int add_trajectory_point(struct tsem_event *ep) +{ + struct tsem_model *model = tsem_model(current); + + ep->pid = 0; + tsem_event_get(ep); + + spin_lock(&model->trajectory_lock); + list_add_tail(&ep->list, &model->trajectory_list); + spin_unlock(&model->trajectory_lock); + + return 0; +} + +static int add_forensic_point(struct tsem_event *ep) +{ + struct tsem_model *model = tsem_model(current); + + ep->pid = 0; + tsem_event_get(ep); + + spin_lock(&model->forensics_lock); + list_add_tail(&ep->list, &model->forensics_list); + spin_unlock(&model->forensics_lock); + + return 0; +} + +static int get_host_measurement(u8 *id, u8 *digest) +{ + int retn; + struct tsem_model *model = tsem_model(current); + SHASH_DESC_ON_STACK(shash, tfm); + + shash->tfm = tsem_digest(); + retn = crypto_shash_init(shash); + if (retn) + goto done; + + retn = crypto_shash_update(shash, model->base, tsem_digestsize()); + if (retn) + goto done; + + retn = crypto_shash_finup(shash, id, tsem_digestsize(), digest); + + done: + return retn; +} + +static int update_events_measurement(struct tsem_event *ep) +{ + int retn; + u8 digest[HASH_MAX_DIGESTSIZE]; + struct tsem_context *ctx = tsem_context(current); + struct tsem_model *model = ctx->model; + SHASH_DESC_ON_STACK(shash, tfm); + + retn = get_host_measurement(ep->mapping, digest); + if (retn) + goto done; + + shash->tfm = tsem_digest(); + retn = crypto_shash_init(shash); + if (retn) + goto done; + + retn = crypto_shash_update(shash, model->measurement, + tsem_digestsize()); + if (retn) + goto done; + + retn = crypto_shash_finup(shash, digest, tsem_digestsize(), + model->measurement); + if (retn) + goto done; + + if (!tsem_context(current)->id) + retn = tsem_trust_add_event(ep); + + done: + return retn; +} + +static int state_sort(const void *a, const void *b) +{ + unsigned int lp, retn = 0; + struct tsem_event_point *ap, *bp; + + ap = *((struct tsem_event_point **) a); + bp = *((struct tsem_event_point **) b); + + for (lp = 0; lp < tsem_digestsize(); ++lp) { + if (ap->point[lp] == bp->point[lp]) + continue; + + if (ap->point[lp] < bp->point[lp]) + retn = -1; + else + retn = 1; + goto done; + } + + done: + return retn; +} + +/** + * tesm_model_compute_state() - Calculate a security model state value. + * + * The function generates the state value of the current modeling domain. + */ +void tsem_model_compute_state(void) +{ + u8 state[HASH_MAX_DIGESTSIZE]; + int retn; + unsigned int lp, count, pt_count = 0; + struct list_head *end; + struct tsem_event_point *end_point, *entry, **points = NULL; + struct tsem_model *model = tsem_model(current); + SHASH_DESC_ON_STACK(shash, tfm); + + shash->tfm = tsem_digest(); + retn = crypto_shash_init(shash); + if (retn) + goto done; + + memset(state, '\0', sizeof(state)); + retn = crypto_shash_update(shash, state, tsem_digestsize()); + if (retn) + goto done; + + retn = get_host_measurement(tsem_trust_aggregate(), state); + if (retn) + goto done; + + retn = crypto_shash_finup(shash, state, tsem_digestsize(), state); + if (retn) + goto done; + + spin_lock(&model->point_lock); + end = model->point_list.prev; + count = model->point_count; + spin_unlock(&model->point_lock); + + points = vmalloc(sizeof(*points) * count); + if (!points) { + retn = -ENOMEM; + goto done; + } + + end_point = container_of(end, struct tsem_event_point, list); + list_for_each_entry(entry, &model->point_list, list) { + points[pt_count++] = entry; + if (end_point == entry) + break; + } + sort(points, count, sizeof(*points), state_sort, NULL); + + memcpy(model->state, state, tsem_digestsize()); + for (lp = 0; lp < pt_count; ++lp) { + entry = points[lp]; + + if (get_host_measurement(entry->point, state)) + goto done; + + if (crypto_shash_init(shash)) + goto done; + if (crypto_shash_update(shash, model->state, + tsem_digestsize())) + goto done; + if (crypto_shash_finup(shash, state, tsem_digestsize(), + model->state)) + goto done; + } + + done: + if (retn) + memset(model->state, '\0', tsem_digestsize()); + + vfree(points); +} + +/** + * tsem_model_has_pseudonym() - Test for a model pseudonym. + * @tsip: A pointer to the TSEM inode security structure. + * @file: A pointer to the tsem_file_args structure characterizing the + * file whose pseudonym is to be checked. + * + * This function is used to test whether a pseudonym has been + * declared for a modeling domain. It is up to the caller to + * populate the event description structure with a suitable + * value for the pseudonym digest. + * + * Return: If an error occurs during the pseudonym probe a negative + * return value is returned. A zero return value indicates that + * a pseudonym was not present. A value of one indicates that a + * pseudonym has been defined. + */ +int tsem_model_has_pseudonym(struct tsem_inode *tsip, char *pathname) +{ + int retn = 0; + u8 pseudo_mapping[HASH_MAX_DIGESTSIZE]; + struct tsem_model *model = tsem_model(current); + struct pseudonym *entry; + + retn = generate_pseudonym(pathname, pseudo_mapping); + if (retn) + goto done; + + mutex_lock(&model->pseudonym_mutex); + list_for_each_entry(entry, &model->pseudonym_list, list) { + if (!memcmp(entry->mapping, pseudo_mapping, + tsem_digestsize())) { + retn = 1; + goto done; + } + } + retn = 0; + + done: + mutex_unlock(&model->pseudonym_mutex); + return retn; +} + +/** + * tesm_model_event() - Inject a security event into a modeling domain. + * @ep: A pointer to the event description structure. + * + * This function is the entry point for the in kernel Trusted Modeling + * Agent (TMA). It takes a description of an event encoded in a + * tsem_event structure and generates and updates the security model + * description. + * + * Return: If an error occurs during the injection of an event into a + * model a negative error value is returned. A value of zero + * is returned if the event was successfully modeled. The + * security status of the event is returned by encoding the value + * in the bad_COE member of the tsem_task structure. + */ +int tsem_model_event(struct tsem_event *ep) +{ + int retn; + struct tsem_event_point *point; + struct tsem_task *task = tsem_task(current); + struct tsem_context *ctx = task->context; + + retn = ctx->ops->map(ep); + if (retn) + return retn; + + point = have_point(ep->mapping); + if (point) { + ++point->count; + if (!point->valid) + task->trust_status = TSEM_TASK_UNTRUSTED; + return 0; + } + + retn = update_events_measurement(ep); + if (retn) + return retn; + + retn = -ENOMEM; + if (ctx->sealed) { + point = add_event_point(ep->mapping, false, ep->locked); + if (point) { + retn = add_forensic_point(ep); + task->trust_status = TSEM_TASK_UNTRUSTED; + } + } else { + point = add_event_point(ep->mapping, true, ep->locked); + if (point) + retn = add_trajectory_point(ep); + } + + if (!retn) + ++point->count; + return retn; +} + +/** + * tesm_model_load_point() - Load a security state event into a model. + * @point: A pointer to the array containing the security state + * point to be added to the model. + * + * This function takes the binary representation of a security state + * point and loads it into the current model domain. + * + * Return: If an error occurs during the processing of the security state + * point a negative return value is returned. A return value of + * zero indicates the point was successfully loaded into the domain. + */ +int tsem_model_load_point(u8 *point) +{ + int retn = -ENOMEM; + struct tsem_event *ep; + struct tsem_context *ctx = tsem_context(current); + + if (have_point(point)) + return 0; + + if (!add_event_point(point, true, false)) + return retn; + + if (!ctx->model->have_aggregate) { + retn = tsem_model_add_aggregate(); + if (retn) + return retn; + + ctx->model->have_aggregate = true; + } + + ep = tsem_event_allocate(0, false); + if (!ep) + return retn; + + kref_init(&ep->kref); + memcpy(ep->mapping, point, tsem_digestsize()); + retn = update_events_measurement(ep); + + tsem_event_put(ep); + return retn; +} + +/** + * tesm_model_load_pseudonym() - Load a pseudonym state point to a model. + * @mapping: A pointer to the array containing the pseudonym state + * point that is to be added to the model. + * + * This function takes the binary representation of a file pseudonym + * and declares the presence of the pseudonym in the modeling domain. + * + * Return: If an error occurs during the processing of the pseudonym + * state point a negative return value is returned. A return + * value of zero indicates the point was successfully loaded + * into the model. + */ +int tsem_model_load_pseudonym(u8 *mapping) +{ + struct pseudonym *psp = NULL; + struct tsem_model *model = tsem_model(current); + + psp = kzalloc(sizeof(*psp), GFP_KERNEL); + if (!psp) + return -ENOMEM; + memcpy(psp->mapping, mapping, tsem_digestsize()); + + mutex_lock(&model->pseudonym_mutex); + list_add_tail(&psp->list, &model->pseudonym_list); + mutex_unlock(&model->pseudonym_mutex); + return 0; +} + +/** + * tesm_model_load_base() - Load a model base point. + * @mapping: A pointer to the array containing the base point to be + * set for the model. + * + * This function takes the binary representation of a base point and + * sets this point as the base point for the model. + */ +void tsem_model_load_base(u8 *mapping) +{ + struct tsem_model *model = tsem_model(current); + + memcpy(model->base, mapping, tsem_digestsize()); +} + +/** + * tesm_model_add_aggregate() - Add the hardware aggregate to a model. + * + * This function adds the hardware aggregate value to an internally + * modeled security domain. + * + * Return: If an error occurs during the injection of the aggregate + * value into the model a negative error value is returned. + * A return value of zero indicates the aggregate was + * successfully added. + */ +int tsem_model_add_aggregate(void) +{ + int retn; + struct tsem_event *ep; + + ep = tsem_event_allocate(0, false); + if (!ep) + return -ENOMEM; + + kref_init(&ep->kref); + ep->digestsize = tsem_digestsize(); + memcpy(ep->mapping, tsem_trust_aggregate(), ep->digestsize); + + retn = update_events_measurement(ep); + tsem_event_put(ep); + + return retn; +} + +/** + * tsem_model_allocate() - Allocates a kernel TMA modeling structure. + * @size: The number of slots in the event point magazine for the model. + * + * This function allocates and initializes a tsem_model structure + * that is used to hold modeling information for an in kernel + * modeling domain. + * + * Return: On success a pointer to the model description structure is + * returned. If an error occurs an error return value is + * encoded in the returned pointer. + */ +struct tsem_model *tsem_model_allocate(size_t size) +{ + struct tsem_model *model = NULL; + + model = kzalloc(sizeof(*model), GFP_KERNEL); + if (!model) + return NULL; + + spin_lock_init(&model->point_lock); + INIT_LIST_HEAD(&model->point_list); + mutex_init(&model->point_end_mutex); + + spin_lock_init(&model->trajectory_lock); + INIT_LIST_HEAD(&model->trajectory_list); + mutex_init(&model->trajectory_end_mutex); + + spin_lock_init(&model->forensics_lock); + INIT_LIST_HEAD(&model->forensics_list); + mutex_init(&model->forensics_end_mutex); + + mutex_init(&model->pseudonym_mutex); + INIT_LIST_HEAD(&model->pseudonym_list); + + mutex_init(&model->mount_mutex); + INIT_LIST_HEAD(&model->mount_list); + + if (magazine_allocate(model, size)) { + kfree(model); + model = NULL; + } + + return model; +} + +/** + * tsem_model_free() - Frees an a kernel TMA description structure. + * @ctx: A pointer to the TMA modeling description structure whose + * model definition is to be deleted. + * + * This function is called when the last reference to a kernel + * based TMA model description structure is released. + */ +void tsem_model_free(struct tsem_context *ctx) +{ + struct tsem_event_point *ep, *tmp_ep; + struct tsem_event *tentry, *tmp_tentry; + struct pseudonym *sentry, *tmp_sentry; + struct tsem_inode_instance *ientry, *tmp_ientry; + struct tsem_model *model = ctx->model; + + list_for_each_entry_safe(ep, tmp_ep, &model->point_list, list) { + list_del(&ep->list); + kmem_cache_free(point_cachep, ep); + } + + list_for_each_entry_safe(sentry, tmp_sentry, &model->pseudonym_list, + list) { + list_del(&sentry->list); + kfree(sentry); + } + + list_for_each_entry_safe(tentry, tmp_tentry, &model->trajectory_list, + list) { + list_del(&tentry->list); + tsem_event_put(tentry); + } + + list_for_each_entry_safe(ientry, tmp_ientry, &model->mount_list, + list) { + list_del(&ientry->list); + kfree(ientry); + } + + if (ctx->sealed) { + list_for_each_entry_safe(tentry, tmp_tentry, + &model->forensics_list, list) { + list_del(&tentry->list); + tsem_event_put(tentry); + } + } + + tsem_model_magazine_free(model); + kfree(model); +} + +/** + * tsem_model_magazine_free: Free the event point magazine for a model domain. + * @model: A pointer to the model whose magazine is to be freed. + * + * This function releases all of the components of an event point + * magazine that has been allocated for a modeling domain. + */ +void tsem_model_magazine_free(struct tsem_model *model) +{ + unsigned int lp; + + for (lp = 0; lp < model->magazine_size; ++lp) + kmem_cache_free(point_cachep, model->magazine[lp]); + + bitmap_free(model->magazine_index); + kfree(model->ws); + kfree(model->magazine); +} + +/** + * tsem model_init() - Initialize the TSEM event point cache. + * @model: A pointer to the model that is being initialized. + * @size: The number of slots in the event point magazine for the root + * model. + * + * This function is called by the primary TSEM initialization function + * and sets up the cache that will be used to dispense tsem_event_point + * structures for security events that are called in atomic context. + * + * Return: This function returns a value of zero on success and a negative + * error code on failure. + */ +int __init tsem_model_cache_init(struct tsem_model *model, size_t size) +{ + point_cachep = kmem_cache_create("tsem_event_point_cache", + sizeof(struct tsem_event_point), 0, + SLAB_PANIC, 0); + if (!point_cachep) + return -ENOMEM; + + if (magazine_allocate(model, size)) { + kmem_cache_destroy(point_cachep); + return -ENOMEM; + } + + return 0; +} From patchwork Mon Aug 26 10:37:26 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Dr. Greg" X-Patchwork-Id: 13777619 X-Patchwork-Delegate: paul@paul-moore.com Received: from blizzard.enjellic.com (wind.enjellic.com [76.10.64.91]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 7223C152160; Mon, 26 Aug 2024 10:49:59 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=76.10.64.91 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1724669401; cv=none; b=aFjKIYjde8Y4jZSo+krpncui0bd3fGHboPvvxlfgDPTICJ4WY2AbPha5NnvgqYH6kcLyf1AGmQkt0z3AaJ+BhDl08iycF3g3vaZ0GSXcHuo08546z2pGJb7jSFAbJhOA1szk7zdogzc0gJVH20tphECl8weYCsrjgRDOLigMHkQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1724669401; c=relaxed/simple; bh=H1sKbPQmQY8ikHWY6DyWg8kD7vsi9dQ7844hbzNVmHo=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=j6hsOKbKRhHDA0fDyK9hcitRvvvxCPKIyM6SgZZCOrOs8oiHbWBXQWj6EggR+U72GguaJdoyol9petI55kfE9JvqlmfiP8Qvk7VrLl5Dbul5dZbCZxUsr+jT6faJZ8FbtKhv56h0WJrfXqaf5XKn9O4oPh/at1jhLYSNyladIKk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=enjellic.com; spf=pass smtp.mailfrom=enjellic.com; arc=none smtp.client-ip=76.10.64.91 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=enjellic.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=enjellic.com Received: from blizzard.enjellic.com (localhost [127.0.0.1]) by blizzard.enjellic.com (8.15.2/8.15.2) with ESMTP id 47QAbVZa003449; Mon, 26 Aug 2024 05:37:31 -0500 Received: (from greg@localhost) by blizzard.enjellic.com (8.15.2/8.15.2/Submit) id 47QAbVEM003448; Mon, 26 Aug 2024 05:37:31 -0500 X-Authentication-Warning: blizzard.enjellic.com: greg set sender to greg@enjellic.com using -f From: Greg Wettstein To: linux-security-module@vger.kernel.org, linux-kernel@vger.kernel.org Cc: jmorris@namei.org Subject: [PATCH v4 12/14] Implement configuration and methods for default model. Date: Mon, 26 Aug 2024 05:37:26 -0500 Message-Id: <20240826103728.3378-13-greg@enjellic.com> X-Mailer: git-send-email 2.39.1 In-Reply-To: <20240826103728.3378-1-greg@enjellic.com> References: <20240826103728.3378-1-greg@enjellic.com> Precedence: bulk X-Mailing-List: linux-security-module@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 The implementation of a TSEM model consists of three separate components: - bypasses array - event initialization method - event coefficient mapping method. Boolean true values in the bypasses array is used to indicate that the event should be completely bypassed. The event initialization initializes the event description structure using the characteristics of the event. The event coefficient mapping method is responsible for generating the security event coefficient from the event description structure. The model0.c file provides the model components for the default deterministic model implemented by the kernel. --- security/tsem/model0.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 security/tsem/model0.c diff --git a/security/tsem/model0.c b/security/tsem/model0.c new file mode 100644 index 000000000000..347ed8584842 --- /dev/null +++ b/security/tsem/model0.c @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: GPL-2.0-only + +/* + * Copyright (C) 2024 Enjellic Systems Development, LLC + * Author: Dr. Greg Wettstein + * + * This file contains methods and definitions for the 'model0' + * security model implementation. This is the default model that + * TSEM implements. + */ + +#include "tsem.h" + +static bool event_bypasses[TSEM_EVENT_CNT]; + +const struct tsem_context_ops tsem_model0_ops = { + .name = "model0", + .bypasses = event_bypasses, + .init = tsem_event_generate, + .map = tsem_map_event +}; From patchwork Mon Aug 26 10:37:27 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Dr. Greg" X-Patchwork-Id: 13777622 X-Patchwork-Delegate: paul@paul-moore.com Received: from blizzard.enjellic.com (wind.enjellic.com [76.10.64.91]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 608B1155C87; Mon, 26 Aug 2024 10:50:12 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=76.10.64.91 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1724669414; cv=none; b=KaHQqIUxBkA+wSEtxnReVcH5pQgznxEaGFnVWBAWKGQvDO1HRuqlBxKzsRP3kdj48rMVOABgm6YoYmAF1bg1JO7dScDwI/OCcCM/VfrbSVDxg4CbBG9Jj8rac3nWJyNA5bdW0JQNiNN2BjF4XOUPw5sM+2qty5nJ6F+V2BqhMVo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1724669414; c=relaxed/simple; bh=g9whlb+2ufFaVEpQMZxnUUuaozKwg0yP2/yN1qqfTDs=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=U+CXPYJ5Xpk92P9xJb4YbjiwZCcRwP3r5nL4bUSt6qYDGDR5WH78iXEmDr7rgPf1mnKxOwgGeuji31j1LyxeIAYU8YenWwHTLaJ7Cvd7/KhHHRe321+rrApfLq38EQwc7Qj/8kUys3Na8xoozh6G19iw+RkmawuaN0UK7k5BZSg= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=enjellic.com; spf=pass smtp.mailfrom=enjellic.com; arc=none smtp.client-ip=76.10.64.91 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=enjellic.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=enjellic.com Received: from blizzard.enjellic.com (localhost [127.0.0.1]) by blizzard.enjellic.com (8.15.2/8.15.2) with ESMTP id 47QAbWqB003453; Mon, 26 Aug 2024 05:37:32 -0500 Received: (from greg@localhost) by blizzard.enjellic.com (8.15.2/8.15.2/Submit) id 47QAbW2c003452; Mon, 26 Aug 2024 05:37:32 -0500 X-Authentication-Warning: blizzard.enjellic.com: greg set sender to greg@enjellic.com using -f From: Greg Wettstein To: linux-security-module@vger.kernel.org, linux-kernel@vger.kernel.org Cc: jmorris@namei.org Subject: [PATCH v4 13/14] Implement infrastructure for loadable security models. Date: Mon, 26 Aug 2024 05:37:27 -0500 Message-Id: <20240826103728.3378-14-greg@enjellic.com> X-Mailer: git-send-email 2.39.1 In-Reply-To: <20240826103728.3378-1-greg@enjellic.com> References: <20240826103728.3378-1-greg@enjellic.com> Precedence: bulk X-Mailing-List: linux-security-module@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 The default deterministic security model can be replaced through the use of models that are implemented with kernel loadable modules. Kernel loadable modules can be used to replace the following components of the TSEM modeling infrastructure: - event bypasses - event initialization - event mapping An alternate security model is specified for a security modeling namespace by using the following key/value pair as an argument to the creation of the security modeling namespace: model=MODEL_NAME Where MODEL_NAME is the name of the model compiled into the kernel loadable model that implements the model. Befor any model can be used it must be registered through the infrastructure supplied by this commit, namely the nsmgr.c file. Addition or removal of modules can be locked, either through the TSEM control plame or by the 'tsem_locked' kernel command-line parameter. Locking of the registation process is an irreversible process, once locked the registration/removal process will be locked until the system is rebooted. --- security/tsem/nsmgr.c | 255 ++++++++++++++++++++++++++++++++++++++++++ security/tsem/nsmgr.h | 48 ++++++++ 2 files changed, 303 insertions(+) create mode 100644 security/tsem/nsmgr.c create mode 100644 security/tsem/nsmgr.h diff --git a/security/tsem/nsmgr.c b/security/tsem/nsmgr.c new file mode 100644 index 000000000000..0b34c18feb01 --- /dev/null +++ b/security/tsem/nsmgr.c @@ -0,0 +1,255 @@ +// SPDX-License-Identifier: GPL-2.0-only + +/* + * Copyright (C) 2024 Enjellic Systems Development, LLC + * Author: Dr. Greg Wettstein + * + * This file contains infrastructure for managing the security models + * for TSEM that are implemented by kernel loadable modules. Any model + * provided by a kernel loadable module must be registered before it + * can be specified for a security modeling namespace. + * + * The registration of additional models, or the removal of existing + * models, can be prevented by 'locking' the registration process. + * This locking can be implemented either through the TSEM control + * plane or by specifying the 'tsem_locked' kernel command-line + * parameter. + */ + +#include "tsem.h" +#include "nsmgr.h" + +static bool locked; +static bool setup_locked __ro_after_init; + +struct model { + struct list_head list; + const struct tsem_context_ops *ops; + struct module *module; +}; + +DEFINE_MUTEX(model_list_mutex); +LIST_HEAD(model_list); + + +static struct model *find_model(const char *name) +{ + struct model *found = NULL, *entry = NULL; + + list_for_each_entry(entry, &model_list, list) { + if (!strcmp(entry->ops->name, name)) { + found = entry; + break; + } + } + return found; +} + +/** + * tsem_nsmgr_put() - Release a reference to a TSEM security model. + * + * @ops: A pointer to the tsem_context_ops structure that defines + * this model. + * + * This function is the counter part to tsem_nsmgr_get() function. + * A check is made to determine if a module is supplying this model and + * if so the reference that was taken to the module when the security + * namespace is created is released. + */ +void tsem_nsmgr_put(const struct tsem_context_ops *ops) +{ + struct model *model; + + if (!strcmp(tsem_model0_ops.name, ops->name)) + return; + + mutex_lock(&model_list_mutex); + model = find_model(ops->name); + if (!model) { + pr_warn("tsem: Attempt to put an unknown model.\n"); + goto done; + } + if (model->module) + module_put(model->module); + + done: + mutex_unlock(&model_list_mutex); +} + +/** + * tsem_nsmgr_get() - Obtain a reference to a TSEM security model. + * + * @name: A null terminated character buffer containing the name of + * the security model to obtain a reference for. + * + * This function is used to determine whether or not TSEM has access + * to a security model named by the called. Upon success of locating + * the named security model a tsem_context_ops structure is returned + * that implements the model. In addition a reference is taken to + * the module in order to prevent its release while a security modeling + * namespace is using the model. + * + * Return: If the named security model is not available a NULL pointer + * is returned. If the model is available a pointer to the + * tsem_context_ops structure that implements the model is + * returned. + */ +const struct tsem_context_ops *tsem_nsmgr_get(const char *name) +{ + struct model *model; + const struct tsem_context_ops *retn = NULL; + + mutex_lock(&model_list_mutex); + model = find_model(name); + mutex_unlock(&model_list_mutex); + + if (!model) { + if (request_module("%s", name)) + return NULL; + + mutex_lock(&model_list_mutex); + model = find_model(name); + mutex_unlock(&model_list_mutex); + if (!model) + return NULL; + } + + if (model && try_module_get(model->module)) + retn = model->ops; + return retn; +} + +/** + * tsem_nsmgr_lock() - Lock the TSEM loadable module infrastructure. + * + * @by_setup: This boolean value is used to signal that the request to + * lock the model state is being done by the system setup. + * + * This function disables the registration of additional loadable + * modules for model implementation. It also raises the reference count + * for each modules that is loaded to prevent the current modules from + * being unloaded. + * + * Return: This function returns 0 if the modeling infrastructure was + * locked or a negative value if locking fails. + */ +int tsem_nsmgr_lock(const bool by_setup) +{ + struct model *entry = NULL; + + if (by_setup) { + setup_locked = true; + return 0; + } + + if (setup_locked) + return -EINVAL; + if (!capable(CAP_MAC_ADMIN)) + return -EPERM; + if (tsem_context(current)->id) + return -EINVAL; + if (locked) + return -EINVAL; + locked = true; + + mutex_lock(&model_list_mutex); + list_for_each_entry(entry, &model_list, list) { + __module_get(entry->module); + } + mutex_unlock(&model_list_mutex); + + pr_info("tsem: Model state is now locked.\n"); + return 0; +} + +/** + * tsem_nsmgr_register() - Register a TSEM security namespace model. + * @ops: A pointer to a tsem_context_ops structure that describes + * the model that will be implemented. + * @module: A pointer to the module implementing the security namespace + * model. + * + * This function is used by loadable modules to register a security + * model that is to be available for use by security modeling namespaces. + * + * Return: This function returns 0 if the model was successfully registered + * or a negative error value if registration failed. + */ +int tsem_nsmgr_register(const struct tsem_context_ops *ops, + struct module *module) +{ + int retn = 0; + struct model *model = NULL; + + if (setup_locked || locked) { + pr_warn("tsem: Attempt to register model in locked state.\n"); + return -EINVAL; + } + if (!capable(CAP_MAC_ADMIN)) + return -EPERM; + if (!strcmp(tsem_model0_ops.name, ops->name)) + return -EINVAL; + + mutex_lock(&model_list_mutex); + if (find_model(ops->name)) { + pr_warn("tsem: Attempt to insert identical model: %s\n", + ops->name); + retn = -EEXIST; + goto done; + } + + model = kzalloc(sizeof(*model), GFP_KERNEL); + if (!model) { + retn = -ENOMEM; + goto done; + } + + model->ops = ops; + model->module = module; + list_add_tail(&model->list, &model_list); + pr_info("tsem: Registered model: '%s'\n", ops->name); + + done: + mutex_unlock(&model_list_mutex); + return retn; +} +EXPORT_SYMBOL_GPL(tsem_nsmgr_register); + +/** + * tsem_nsmgr_release() - Release a TSEM security namespace model. + * @name: A null terminated character buffer containing the name + * of the model being implemented. + * + * This function is used to release the use of security modeling + * namespace model. + * + * Return: This function returns 0 if the model was successfully released + * or a negative error value if release failed. + */ +int tsem_nsmgr_release(const struct tsem_context_ops *ops) +{ + int retn = 0; + struct model *model; + + if (setup_locked || locked) { + pr_warn("tsem: Attempt to release model in locked state.\n"); + return -EINVAL; + } + + mutex_lock(&model_list_mutex); + + model = find_model(ops->name); + if (!model) { + pr_warn("tsem: Model '%s' not found for release.\n", + ops->name); + retn = -EINVAL; + goto done; + } + list_del(&model->list); + + done: + mutex_unlock(&model_list_mutex); + pr_info("tsem: Released model: '%s'\n", ops->name); + return retn; +} +EXPORT_SYMBOL_GPL(tsem_nsmgr_release); diff --git a/security/tsem/nsmgr.h b/security/tsem/nsmgr.h new file mode 100644 index 000000000000..cb4d30a4962d --- /dev/null +++ b/security/tsem/nsmgr.h @@ -0,0 +1,48 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +/* + * Copyright (C) 2024 Enjellic Systems Development, LLC + * Author: Dr. Greg Wettstein + * + * This header file contains declarations for globally visible + * functionality surrounding the registration of security models + * provided in the form of loadable modules. + * + * This header file detects whether or not kernel modules are enabled + * and if not provides a stub function that causes an attempt to + * specify an alternate security model to fail. + */ + +#ifdef CONFIG_MODULES +extern void tsem_nsmgr_put(const struct tsem_context_ops *ops); +extern const struct tsem_context_ops *tsem_nsmgr_get(const char *name); +extern int tsem_nsmgr_lock(const bool); +extern int tsem_nsmgr_register(const struct tsem_context_ops *ops, + struct module *module); +extern int tsem_nsmgr_release(const struct tsem_context_ops *ops); +#else +static inline void tsem_nsmgr_put(const struct tsem_context_ops *ops) +{ +} + +static inline int tsem_nsmgr_lock(const bool by_setup) +{ + return EOPNOTSUPP; +} + +static inline const struct tsem_context_ops *tsem_nsmgr_get(const char *name) +{ + return NULL; +} + +static inline int tsem_nsmgr_register(const struct tsem_context_ops *ops, + struct module *module) +{ + return -EOPNOTSUPP; +} + +static inline int tsem_nsmgr_release(const struct tsem_context_ops *ops) +{ + return -EOPNOTSUPP; +} +#endif From patchwork Mon Aug 26 10:37:28 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Dr. Greg" X-Patchwork-Id: 13777620 X-Patchwork-Delegate: paul@paul-moore.com Received: from blizzard.enjellic.com (wind.enjellic.com [76.10.64.91]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 0D1F8152160; Mon, 26 Aug 2024 10:50:03 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=76.10.64.91 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1724669406; cv=none; b=Sn5CJbjHGQiviMmRdWPGxFANZX4D4mqljz2GAYGHZXba+E52M5cAGtnx6dPmuICtKue4T7CDOBEOQQ7IyzQgLNv9gSzKzGFhQnNOpZfhWWUQ6WVAj31gHz+iY7O98qHro5KH0ZMu7i7QBrn1Fv0RqNZik6qx7I1iNar67/5CtgI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1724669406; c=relaxed/simple; bh=tsijDg1QpHf/XW2pCyQGKWUItm4MMvjUNdWU+pyh3k8=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=E0TjKxM6pdpomHFVFOOSDvZu/cmonOS2KnbB3bcULBnMTNf3FarFFGRPyaerxDuxFTvYwNHanofPXO1nvtVEEgLcXNnkvDPwIkZQT6Jg/QymSDn7ZWEbtFx614mhmGc0MZgt06PvCVubz0BM/lixxUmX6nBVJF9uOovW9tMZoHM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=enjellic.com; spf=pass smtp.mailfrom=enjellic.com; arc=none smtp.client-ip=76.10.64.91 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=enjellic.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=enjellic.com Received: from blizzard.enjellic.com (localhost [127.0.0.1]) by blizzard.enjellic.com (8.15.2/8.15.2) with ESMTP id 47QAbWmR003457; Mon, 26 Aug 2024 05:37:32 -0500 Received: (from greg@localhost) by blizzard.enjellic.com (8.15.2/8.15.2/Submit) id 47QAbW6d003456; Mon, 26 Aug 2024 05:37:32 -0500 X-Authentication-Warning: blizzard.enjellic.com: greg set sender to greg@enjellic.com using -f From: Greg Wettstein To: linux-security-module@vger.kernel.org, linux-kernel@vger.kernel.org Cc: jmorris@namei.org Subject: [PATCH v4 14/14] Activate the configuration and build of the TSEM LSM. Date: Mon, 26 Aug 2024 05:37:28 -0500 Message-Id: <20240826103728.3378-15-greg@enjellic.com> X-Mailer: git-send-email 2.39.1 In-Reply-To: <20240826103728.3378-1-greg@enjellic.com> References: <20240826103728.3378-1-greg@enjellic.com> Precedence: bulk X-Mailing-List: linux-security-module@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Complete the implementation by integrating TSEM into the configuration and kernel build infrastructure. This includes registration of TSEM with the LSM infrastructure and the assignment of an LSM identifier number. --- include/uapi/linux/lsm.h | 1 + security/Kconfig | 11 ++++++----- security/Makefile | 1 + security/security.c | 3 ++- security/tsem/Kconfig | 36 ++++++++++++++++++++++++++++++++++++ security/tsem/Makefile | 6 ++++++ 6 files changed, 52 insertions(+), 6 deletions(-) create mode 100644 security/tsem/Kconfig create mode 100644 security/tsem/Makefile diff --git a/include/uapi/linux/lsm.h b/include/uapi/linux/lsm.h index 33d8c9f4aa6b..6b63c158c1df 100644 --- a/include/uapi/linux/lsm.h +++ b/include/uapi/linux/lsm.h @@ -64,6 +64,7 @@ struct lsm_ctx { #define LSM_ID_LANDLOCK 110 #define LSM_ID_IMA 111 #define LSM_ID_EVM 112 +#define LSM_ID_TSEM 113 /* * LSM_ATTR_XXX definitions identify different LSM attributes diff --git a/security/Kconfig b/security/Kconfig index 412e76f1575d..a7802eb29034 100644 --- a/security/Kconfig +++ b/security/Kconfig @@ -192,6 +192,7 @@ source "security/yama/Kconfig" source "security/safesetid/Kconfig" source "security/lockdown/Kconfig" source "security/landlock/Kconfig" +source "security/tsem/Kconfig" source "security/integrity/Kconfig" @@ -231,11 +232,11 @@ endchoice config LSM string "Ordered list of enabled LSMs" - default "landlock,lockdown,yama,loadpin,safesetid,smack,selinux,tomoyo,apparmor,bpf" if DEFAULT_SECURITY_SMACK - default "landlock,lockdown,yama,loadpin,safesetid,apparmor,selinux,smack,tomoyo,bpf" if DEFAULT_SECURITY_APPARMOR - default "landlock,lockdown,yama,loadpin,safesetid,tomoyo,bpf" if DEFAULT_SECURITY_TOMOYO - default "landlock,lockdown,yama,loadpin,safesetid,bpf" if DEFAULT_SECURITY_DAC - default "landlock,lockdown,yama,loadpin,safesetid,selinux,smack,tomoyo,apparmor,bpf" + default "tsem,landlock,lockdown,yama,loadpin,safesetid,smack,selinux,tomoyo,apparmor,bpf" if DEFAULT_SECURITY_SMACK + default "tsem,landlock,lockdown,yama,loadpin,safesetid,apparmor,selinux,smack,tomoyo,bpf" if DEFAULT_SECURITY_APPARMOR + default "tsem,landlock,lockdown,yama,loadpin,safesetid,tomoyo,bpf" if DEFAULT_SECURITY_TOMOYO + default "tsem,landlock,lockdown,yama,loadpin,safesetid,bpf" if DEFAULT_SECURITY_DAC + default "tsem,landlock,lockdown,yama,loadpin,safesetid,selinux,smack,tomoyo,apparmor,bpf" help A comma-separated list of LSMs, in initialization order. Any LSMs left off this list, except for those with order diff --git a/security/Makefile b/security/Makefile index 59f238490665..1d4e0a698a2d 100644 --- a/security/Makefile +++ b/security/Makefile @@ -25,6 +25,7 @@ obj-$(CONFIG_SECURITY_LOCKDOWN_LSM) += lockdown/ obj-$(CONFIG_CGROUPS) += device_cgroup.o obj-$(CONFIG_BPF_LSM) += bpf/ obj-$(CONFIG_SECURITY_LANDLOCK) += landlock/ +obj-$(CONFIG_SECURITY_TSEM) += tsem/ # Object integrity file lists obj-$(CONFIG_INTEGRITY) += integrity/ diff --git a/security/security.c b/security/security.c index e5ca08789f74..1dfd85293ad4 100644 --- a/security/security.c +++ b/security/security.c @@ -51,7 +51,8 @@ (IS_ENABLED(CONFIG_BPF_LSM) ? 1 : 0) + \ (IS_ENABLED(CONFIG_SECURITY_LANDLOCK) ? 1 : 0) + \ (IS_ENABLED(CONFIG_IMA) ? 1 : 0) + \ - (IS_ENABLED(CONFIG_EVM) ? 1 : 0)) + (IS_ENABLED(CONFIG_EVM) ? 1 : 0) + \ + (IS_ENABLED(CONFIG_SECURITY_TSEM) ? 1 : 0)) /* * These are descriptions of the reasons that can be passed to the diff --git a/security/tsem/Kconfig b/security/tsem/Kconfig new file mode 100644 index 000000000000..2e9d54eb3acc --- /dev/null +++ b/security/tsem/Kconfig @@ -0,0 +1,36 @@ +config SECURITY_TSEM + bool "Trusted Security Event Modeling" + depends on SECURITY + depends on NET && INET + select SECURITY_NETWORK + select SECURITYFS + select CRYPTO + select CRYPTO_SHA256 + select CRYPTO_HASH_INFO + select TCG_TPM if HAS_IOMEM && !UML + select TCG_TIS if TCG_TPM && X86 + select TCG_CRB if TCG_TPM && ACPI + default n + help + This option selects support for Trusted Security Event + Modeling (TSEM). TSEM implements the ability to model + the security state of either the system at large or in a + restricted namespace on the basis of the LSM security + events and attributes that occur in the scope of the model. + The model may be implemented either in the kernel proper + or exported to an external Trusted Modeling Agent (TMA). + If you are unsure how to answer this question, answer N. + +config SECURITY_TSEM_ROOT_MODEL_PCR + int "TPM PCR index for root domain" + depends on SECURITY_TSEM + range 8 14 + default 11 + help + This configuration variable determines the TPM Platform + Configuration Register (PCR) that the coefficients of + security events for the root modeling domain are extended + into. The default value is one register above the default + value that IMA uses for its integrity measurements, in order + to avoid a conflict between the two sub-systems. If unsure, + leave the value at its default value of 11. diff --git a/security/tsem/Makefile b/security/tsem/Makefile new file mode 100644 index 000000000000..5b26edbe02b0 --- /dev/null +++ b/security/tsem/Makefile @@ -0,0 +1,6 @@ +obj-$(CONFIG_SECURITY_TSEM) := tsem.o model.o namespace.o map.o event.o fs.o \ + export.o trust.o model0.o + +ifdef CONFIG_MODULES +obj-y += nsmgr.o +endif