google / google/zerocopy

Features to support SE Linux policy file parsing

Open
#731 0 comments 0 reactions 0 assignees View on GitHub
customer-request
Dominant language
Rust
Stars
2.6k
Forks
179
Avg merge
1d 19h
Merged PRs (30d)
29

Description

Fuchsia is [using zerocopy](https://fxrev.dev/952718) to parse SE Linux policy files. They have found it difficult to use zerocopy to achieve all of their goals. As far as I can tell, this stems from three requirements which zerocopy is not currently able to support:
- The SE Linux policy file format is a nested tree structure (see e.g. the [`ExtensibleBitmap`](https://fuchsia-review.googlesource.com/c/fuchsia/+/952718/8/src/starnix/lib/selinux_policy/src/extensible_bitmap.rs) type), and so converting from a `&[u8]` to a typed representation without copying or allocating would require producing a self-referential object.
- The SE Linux policy file format includes multiple variable-length objects stored within a single larger object, and so a typed representation would need to support this as well.
- Fuchsia wants to parse the format once and produce a parsed representation which can be stored for a long time. This requires transmuting from a type such as `Vec` into another owned representation.

At the time of writing, zerocopy is good at reasoning about the layouts of Rust types, and permits transmutation of reference-y types (`&T`, `Ref<_, T>`, etc) and of values (`T`). However:
- Since Rust supports neither self-referential types nor types with multiple unsized fields, there is no native Rust type that can be written whose layout corresponds to the layout of an SE Linux policy file.
- Zerocopy does not support transmutation of boxed values such as `Box`, `Vec`, etc.

In order to better support this use case, we would need to support the following:
- Self-referential objects
- Objects with multiple unsized fields
- Transmutation of container types: #114

It may also be worth considering a generalization of "objects with multiple unsized fields" to generally support *any* layout which does not correspond to a Rust type.

# Appendix

@angea has provided this description of SE Linux's policy file format.

 

![selinux-policy-file-format](https://github.com/google/zerocopy/assets/1046063/ea218d82-eae2-4a15-a972-22b5420093f5)

## Overview

- The format is a pure sequence of structures:
- no jump, no pointer, no gap, no code.
- arrays are specified by count of element in advance.
- Most structure declaration are present in every file of this format,
even if empty. In this case, their 'count' will be just set to null,
but present.

## Basic types

All integers are Little-Endian, with no encoding.

### Integers

The default structure to store numbers is [uint32](#uint32).
Used in vast majority of fields, only a handful are 8 bit or 16 bit,
and one is 64 bit.

#### uint32
`0a 00 00 00` => 10

#### uint8
`0b` => 11

#### uint16
`0c 0d` => 513

#### uint64

`01 02 03 04 05 06 07 08` is read as the value
0x0807060504030201 (578437695752307201 in decimal)

### string

- ASCII string (typically lowercase alphanum, with some limited punctuation
(`/`, `_``)). No null terminator, no padding, no alignment, no codepage, no
encoding.
- with a fixed length as [uint32](#uint32) that is declared before in the
file, but not necessarily right before.

Ex:
`08 00 00 00 .S .E . .L .i .n .u .x` declares a length of 8
then a 8 byte string "SE Linux".

## File structure

1. Header, at offset 0

Name | Type | Notes
:-- | :-- | :--
magic | char\[4] | `F9 7C FF 8C`
*length* | [uint32](#uint32) | `8`
signature | char\[*length*] | 'SE Linux'
version | [uint32](#uint32) | in range [`30`:`33`]
configuration | [uint32](#uint32) | bitmask (*)
symbols count | [uint32](#uint32)
object context number | [uint32](#uint32)
policy capabilities | [extensible bitmap](#extensible-bitmap)
permissive bitmap | [extensible bitmap](#extensible-bitmap)

2. Symbols
1. Common symbols

Name | Type
:-- | :--
primary names count | [uint32](#uint32)
*count* | [uint32](#uint32)
array | [common symbol](#common-symbol)\[*count*]

2. Classes

Name | Type
:-- | :--
primary names count | [uint32](#uint32)
*count* | [uint32](#uint32)
array | [class](#class)\[*count*]

3. Roles

Name | Type
:-- | :--
primary names count | [uint32](#uint32)
*count* | [uint32](#uint32)
array | [role](#role)\[*count*]

4. Types

Name | Type
:-- | :--
primary names count | [uint32](#uint32)
*count* | [uint32](#uint32)
array | [type](#type)\[*count*]

5. Users

Name | Type
:-- | :--
primary names count | [uint32](#uint32)
*count* | [uint32](#uint32)
array | [user](#user)\[*count*]

6. Conditional Booleans

Name | Type
:-- | :--
primary names count | [uint32](#uint32)
*count* | [uint32](#uint32)
array | [conditional boolean](#conditional-boolean)\[*count*]

7. Sensitivities

Name | Type
:-- | :--
primary names count | [uint32](#uint32)
*count* | [uint32](#uint32)
array | [sensitivity](#sensitivity)\[*count*]

8. Categories

Name | Type
:-- | :--
primary names count | [uint32](#uint32)
*count* | [uint32](#uint32)
array | [category](#category)\[*count*]

3. Access Vector table

Name | Type
:-- | :--
*count* | [uint32](#uint32)
array | [access vector](#access-vector)\[*count*]

4. [Conditional list](#conditional-list)
5. [Role transition list](#role-transition-list)
6. [Role Allow list](#role-allow-list)
7. [Filename transition list](#filename-transition-list)
8. [Object contexts](#object-context)
1. Initial SID
2. File system
3. TCP & UDP port
4. Network interface
5. Node
6. FS use
7. IPv6 node
8. InfiniBand partition key (version 31 or later)
9. InfiniBand end port (version 31 or later)

9. Generic file systems contexts (GenFS)

Name | Type
:-- | :--
*count* | [uint32](#uint32)
array | [GenFS context](#genfs-context)\[*count*]

10. Range

Name | Type
:-- | :--
*count* | [uint32](#uint32)
array | [range translation](#range-translation)\[*count*]

11. Attribute map array
1. array: [extensible bitmap](#extensible-bitmap)\[Header
->Symbols[type=4].primary names count]

Notes:

- (*) `configuration`
- If bit `0` is set: MultiLevelSecurity is enabled.
- other bits are undocumented.

### Extensible bitmap

Name | Type | Notes
:-- | :--|:--
map size | [uint32](#uint32) | `0x40`
high bit | [uint32](#uint32)
*count* | [uint32](#uint32)
array | [map item](#map-item)\[*count*]

#### Map item

Name | Type
:-- | :--
start bit | [uint32](#uint32)
map | [uint64](#uint64)

### Context

Name | Type
:-- | :--
user | [uint32](#uint32)
role | [uint32](#uint32)
type | [uint32](#uint32)
mls range | [mls range](#mls-range)

### MLS range

Name | Type | Presence
:-- | :--|:--
*count* | [uint32](#uint32)
sensitivity low | [uint32](#uint32)
sensitivity high | [uint32](#uint32) | if *count* > 1
low categories | [extensible bitmap](#extensible-bitmap) |
high categories | [extensible bitmap](#extensible-bitmap) | if *count* > 1

### GenFS context

Name | Type
:-- | :--
*length* | [uint32](#uint32)
type | char\[*length*]
*count* | [uint32](#uint32)
array | [FS context](#fs-context)\[*count*]

#### FS context

Name | Type
:-- | :--
*length* | [uint32](#uint32)
name | char\[*length*]
class | [uint32](#uint32)
context | [context](#context)

### Range translation

Name | Type
:-- | :--
source type | [uint32](#uint32)
target type | [uint32](#uint32)
target class | [uint32](#uint32)
mls range | [mls range](#mls-range)

### Object context

1. initial SID

Name | Type
:-- | :--
*count* | [uint32](#uint32)
array | [initial sid](#initial-sid)\[*count*]

2. file system

Name | Type
:-- | :--
*count* | [uint32](#uint32)
array | [file system](#file-system)\[*count*]

3. TCP & UDP port

Name | Type
:-- | :--
*count* | [uint32](#uint32)
array | [port](#port)\[*count*]

4. network interface

Name | Type
:-- | :--
*count* | [uint32](#uint32)
array | [network interface](#network-interface)\[*count*]

5. node

Name | Type
:-- | :--
*count* | [uint32](#uint32)
array | [node](#node)\[*count*]

6. fs use

Name | Type
:-- | :--
*count* | [uint32](#uint32)
array | [FS use](#fs-use)\[*count*]

7. IPv6 node

Name | Type
:-- | :--
*count* | [uint32](#uint32)
array | [IPv6 node](#ipv6-node)\[*count*]

8. If *Header.version* >= `31`
8. InfiniBand partition key

Name | Type
:-- | :--
*count* | [uint32](#uint32)
array | [InfiniBand PKEY](#infiniband-pkey)\[*count*]

9. InfiniBand end port

Name | Type
:-- | :--
*count* | [uint32](#uint32)
array | [InfiniBand End Port](#infiniband-end-port)\[*count*]

*Object context count*:

- is set in the header
- should be `7` if version is `30`, `9` if `31` or higher.

#### Initial SID

Name | Type
:-- | :--
sid | [uint32](#uint32)
context | [context](#context)

#### File system

Name | Type
:-- | :--
*length* | [uint32](#uint32)
context name | char\[*length*]
context1 | [context](#context)
context2 | [context](#context)

#### Port

Name | Type
:-- | :--
protocol | [uint32](#uint32)
low port | [uint32](#uint32)
high port | [uint32](#uint32)
context | [context](#context)

#### Network interface

Name | Type
:-- | :--
*length* | [uint32](#uint32)
context name | char\[*length*]
context1 | [context](#context)
context2 | [context](#context)

#### Node

Name | Type
:-- | :--
node address | [uint32](#uint32)
node mask | [uint32](#uint32)
context | [context](#context)

#### FS use

Name | Type
:-- | :--
behavior | [uint32](#uint32)
*length* | [uint32](#uint32)
name | char\[*length*]
context | [context](#context)

#### IPv6 node

Name | Type
:-- | :--
node adresses | [uint32](#uint32)\[4]
node masks | [uint32](#uint32)\[4]
context | [context](#context)

#### InfiniBand PKEY

Name | Type
:-- | :--
low | [uint32](#uint32)
high | [uint32](#uint32)
context | [context](#context)

#### InfiniBand End Port

Name | Type
:-- | :--
*length* | [uint32](#uint32)
port | [uint32](#uint32)
name | char\[*length*]
context | [context](#context)

### Filename transition list

Name | Type
:-- | :--
*count* | [uint32](#uint32)
array | [filename-transition](#filename-transition)\[*count*]

#### Filename transition

if *Header_version* < 33:

- then \[filename transition] = \[[filename transition old](#filename-transition-old)]
- else \[filename transition] = \[[filename transition new](#filename-transition-new)]

##### Filename transition old

Name | Type
:-- | :--
*length* | [uint32](#uint32)
filename | char\[*length*]
bit | [uint32](#uint32)
transition type | [uint32](#uint32)
transition class | [uint32](#uint32)
old type | [uint32](#uint32)

##### Filename transition new

Name | Type
:-- | :--
*length* | [uint32](#uint32)
filename | char\[*length*]
transition type | [uint32](#uint32)
transition class | [uint32](#uint32)
*count* | [uint32](#uint32)
array | [ftnew item](#ftnew-item)\[*count*]

###### ftnew item

Name | Type
:-- | :--
s types | [extensible bitmap](#extensible-bitmap)
out type | [uint32](#uint32)

### Conditional list

Name | Type
:-- | :--
*count* | [uint32](#uint32)
array | [conditional node](#conditional-node)\[*count*]

#### Conditional node

Name | Type
:-- | :--
state | [uint32](#uint32)
*count* | [uint32](#uint32)
array | [node item](#node-item)\[*count*]
true list | [access vector list](#access-vector-list)
false list | [access vector list](#access-vector-list)

#### Node item
Name | Type
:-- | :--
type | [uint32](#uint32)
boolean | [uint32](#uint32)

### Access vector list

Name | Type
:-- | :--
*count* | [uint32](#uint32)
array | [access vector](#access-vector)\[*count*]

#### Access vector

Name | Type
:-- | :--
source type | [uint16](#uint16)
target type | [uint16](#uint16)
class type | [uint16](#uint16)
*specified* | [uint16](#uint16)

if *specified* & 0x700:

Name | Type
:-- | :--
xperms specified | [uint8](#uint8)
xperms driver | [uint8](#uint8)
permissions | [uint32](#uint8)\[8]

else:

Name | Type
:-- | :--
data | [uint32](#uint32)

### Role transition list

Name | Type
:-- | :--
*count* | [uint32](#uint32)
array | [role transition](#role-transition)\[*count*]

#### Role transition

Name | Type
:-- | :--
role | [uint32](#uint32)
type | [uint32](#uint32)
new role | [uint32](#uint32)
tclass | [uint32](#uint32)

#### Role set

Name | Type
:-- | :--
roles | [extensible bitmap](#extensible-bitmap)
flags | [uint32](#uint32)

### Role allow list

Name | Type
:-- | :--
*count* | [uint32](#uint32)
array | [role allow](#role-allow)\[*count*]

#### Role allow

Name | Type
:-- | :--
role | [uint32](#uint32)
new role | [uint32](#uint32)

### Symbols

#### Common symbol

Name | Type
:-- | :--
*length* | [uint32](#uint32)
value | [uint32](#uint32)
primary names count | [uint32](#uint32)
*count* | [uint32](#uint32)
key | char\[*length*]
permission table | [permission](#permission)\[*count*]

##### Permission

Name | Type
:-- | :--
*length* | [uint32](#uint32)
value | [uint32](#uint32)
key | char\[*length*]

#### Class

Name | Type
:-- | :--
*key length* | [uint32](#uint32)
*common key length* | [uint32](#uint32)
value | [uint32](#uint32)
primary names count | [uint32](#uint32)
*elements count* | [uint32](#uint32)
*constraint count* | [uint32](#uint32)
key | char\[*key length*]
common key | char\[*common key length*]
permission table | [permission](#permission)\[*elements count*]
constraint table | [constraint-list](#constraint-list)\[*constraint count*]
*validate transition count* | [uint32](#uint32)
validate transition rules | [constraint-list](#constraint-list)\[*validate transition count*]
default user | [uint32](#uint32)
default role | [uint32](#uint32)
default range | [uint32](#uint32)
default type | [uint32](#uint32)

##### Constraint list

Name | Type
:-- | :--
permissions | [uint32](#uint32)
*count* | [uint32](#uint32)
array | [constraint](#constraint)\[*count*]

##### Constraint

Name | Type | Presence
:-- | :--|:--
*type* | [uint32](#uint32) |
attribute | [uint32](#uint32) |
operand | [uint32](#uint32) |
names | [extensible bitmap](#extensible-bitmap) | if *type* == 5
names type set | [type set](#type-set) | if *type* == 5

##### Type set

Name | Type
:-- | :--
types | [extensible bitmap](#extensible-bitmap)
negative set | [extensible bitmap](#extensible-bitmap)
flags | [uint32](#uint32)

##### Role

Name | Type
:-- | :--
*length* | [uint32](#uint32)
value | [uint32](#uint32)
bounds | [uint32](#uint32)
key | char\[*length*]
role dominates | [extensible bitmap](#extensible-bitmap)
role types | [extensible bitmap](#extensible-bitmap)

#### Type

Name | Type
:-- | :--
*length* | [uint32](#uint32)
value | [uint32](#uint32)
properties | [uint32](#uint32)
bounds | [uint32](#uint32)
key | char\[*length*]

#### User

Name | Type
:-- | :--
*length* | [uint32](#uint32)
value | [uint32](#uint32)
bounds | [uint32](#uint32)
key | char\[*length*]
user roles | [extensible bitmap](#extensible-bitmap)
expanded range | [mls range](#mls-range)
default level | [mls level](#mls-level)

##### MLS level

Name | Type
:-- | :--
sensitivity | [uint32](#uint32)
categories | [extensible bitmap](#extensible-bitmap)

#### Conditional Boolean

Name | Type
:-- | :--
value | [uint32](#uint32)
state | [uint32](#uint32)
*length* | [uint32](#uint32)
key | char\[*length*]

#### Sensitivity

Name | Type
:-- | :--
*length* | [uint32](#uint32)
is alias | [uint32](#uint32)
key | char\[*length*]
level | [mls level](#mls-level)

#### Category

Name | Type
:-- | :--
*length* | [uint32](#uint32)
value | [uint32](#uint32)
is alias | [uint32](#uint32)
key | char\[*length*]

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.