1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
//! Kernel Capabilities declaration
//!
//! Every program loaded by Sunrise has to declare the kernel capabilities it wishes
//! to use. Upon doing a privileged action (such as using a syscall, or creating
//! an event for an IRQ), the kernel will check that the process was allowed to
//! take this action.
//!
//! The main use-case is to make privilege escalation more complicated. In Sunrise,
//! an exploit only grants the capabilities of the process that was vulnerable,
//! requiring more pivoting in order to gain better accesses. For instance,
//! a vulnerability in the browser does not give rights to access the filesystem.
//!
//! Programs declare their capabilities by putting them in the .kernel_caps
//! section of their ELF executable. Each capability is encoded on an u32. We
//! provide convenience functions to generate those capabilities. Most programs
//! will want to use the `capabilities!` macro in order to generate this section.
//!
//! The capabilities macro takes two arrays: the first contains a list of syscall
//! numbers, and the second contains a list of raw capabilities. Syscalls are
//! handled specially in order to make them easier to declare.
//!
//! In addition, programs are expected to provide a `KipHeader`, telling the
//! kernel various information about how to start the process - such as how much
//! memory it should allocate for the stack, or what the default priority of the
//! process is. Those are provided by the `kip_header` macro.
//!
//! # Example
//!
//! ```
//! extern crate sunrise_libuser;
//! use sunrise_libuser::{syscalls, caps, capabilities, kip_header};
//! use sunrise_libuser::caps::ProcessCategory;
//! kip_header!(HEADER = caps::KipHeader {
//!     magic: *b"KIP1",
//!     name: *b"test\0\0\0\0\0\0\0\0",
//!     title_id: 0x0200000000001000,
//!     process_category: ProcessCategory::KernelBuiltin,
//!     main_thread_priority: 0,
//!     default_cpu_core: 0,
//!     reserved: 0,
//!     flags: 0,
//!     stack_page_count: 16,
//! });
//!
//! capabilities!(CAPABILITIES = Capabilities {
//!     svcs: [
//!         syscalls::nr::SetHeapSize,
//!         syscalls::nr::QueryMemory,
//!         syscalls::nr::ExitProcess,
//!         syscalls::nr::CreateThread,
//!         syscalls::nr::StartThread,
//!         syscalls::nr::ExitThread,
//!         syscalls::nr::MapSharedMemory,
//!         syscalls::nr::UnmapSharedMemory,
//!         syscalls::nr::CloseHandle,
//!         syscalls::nr::WaitSynchronization,
//!         syscalls::nr::ConnectToNamedPort,
//!         syscalls::nr::SendSyncRequestWithUserBuffer,
//!         syscalls::nr::OutputDebugString,
//!         syscalls::nr::CreateSharedMemory,
//!         syscalls::nr::CreateInterruptEvent,
//!         syscalls::nr::SleepThread
//!     ],
//!     raw_caps: [caps::ioport(0x60), caps::ioport(0x64), caps::irq_pair(1, 0x3FF)],
//! });
//! ```

/// Define the capabilities array in the .kernel_caps section. Has the following
/// syntax:
///
/// ```no_run
/// extern crate sunrise_libuser;
/// use sunrise_libuser::{syscalls, caps, capabilities};
/// capabilities!(CAPABILITIES = Capabilities {
///     svcs: [
///         // Array of syscall numbers.
///         syscalls::nr::SetHeapSize,
///         syscalls::nr::ExitProcess,
///     ],
///     raw_caps: [
///          // Array of raw kernel capabilities.
///          caps::ioport(0x60), caps::irq_pair(1, 0x3FF)
///     ]
/// });
/// ```
///
/// The order of the capabilities member is not important, and trailing comas are allowed.
#[macro_export]
macro_rules! capabilities {
    ($ident:ident = Capabilities {
        $($item:tt : [$($itemval:expr),* $(,)*]),* $(,)*
    }) => {
        capabilities!(@handle_item $ident, curcount=6, svcs=[], rawcaps=[], $($item: [$($itemval),*],)*);
    };
    (@handle_item $ident:ident, curcount=$count:expr, svcs=[], rawcaps=[$($rawcaps:expr),*],
      svcs: [$($svcs:expr),*], $($next:tt : [$($nextval:expr),*],)*) =>
    {
        capabilities!(@handle_item $ident, curcount=$count, svcs=[$($svcs),*], rawcaps=[$($rawcaps),*], $($next: [$($nextval),*],)*);
    };
    (@handle_item $ident:ident, curcount=$count:expr, svcs=[$($svcs:expr),*], rawcaps=[],
      raw_caps: [$($raw_caps:expr),*], $($next:tt : [$($nextval:expr),*],)*) =>
    {
        capabilities!(@handle_item $ident, curcount=$count + capabilities!(@count_elems $($raw_caps,)*),
                      svcs=[$($svcs),*], rawcaps=[$($raw_caps),*], $(next: [$($nextval),*],)*);
    };
    (@handle_item $ident:ident, curcount=$count:expr, svcs=[$($svcs:expr),*], rawcaps=[$($raw_caps:expr),*],) => {
        #[cfg_attr(not(test), link_section = ".kernel_caps")]
        #[used]
        static $ident: [u32; $count] = {
            let mut kacs = [
                // first 6 are SVCs
                0 << 29 | 0b1111,
                1 << 29 | 0b1111,
                2 << 29 | 0b1111,
                3 << 29 | 0b1111,
                4 << 29 | 0b1111,
                5 << 29 | 0b1111,
                $($raw_caps,)*
            ];
            capabilities!(@generate_svc kacs, [$($svcs),*]);

            kacs
        };
    };
    (@generate_svc $kac_svcs:ident, [$($svc:expr),*]) => {
        $($kac_svcs[$svc / 24] |= 1 << (($svc % 24) + 5);)*
    };
    (@count_elems) => {
        0
    };
    (@count_elems $val:expr, $($vals:expr,)*) => {
        1 + capabilities!(@count_elems $($vals,)*)
    };
}

pub use sunrise_libkern::process::{KipHeader, ProcessCategory};

/// Define the kernel built-ins in the .kip_header section. Has the following
/// syntax:
///
/// ```no_run
/// extern crate sunrise_libuser;
/// use sunrise_libuser::{caps, kip_header};
/// use sunrise_libuser::caps::ProcessCategory;
/// kip_header!(HEADER = caps::KipHeader {
///     magic: *b"KIP1",
///     name: *b"test\0\0\0\0\0\0\0\0",
///     title_id: 0x0200000000001000,
///     process_category: ProcessCategory::KernelBuiltin,
///     main_thread_priority: 0,
///     default_cpu_core: 0,
///     flags: 0,
///     reserved: 0,
///     stack_page_count: 16,
/// });
/// ```
///
/// Order of the fields does not matter. Every value is configurable.
#[macro_export]
macro_rules! kip_header {
    ($header:ident = $expr:expr) => {
        #[cfg_attr(not(test), link_section = ".kip_header")]
        #[used]
        static $header: $crate::caps::KipHeader = $expr;
    }
}

// TODO: Libuser: capability declaration functions should use type-safe integers.
// BODY: Most of the capability declaration functions use weirdly shaped integers
// BODY: like 12-bits or 21-bits. There is a great crate called [`ux`](https://docs.rs/ux)
// BODY: that provides such weirdly shaped integers. Unfortunately, we cannot
// BODY: create them at compile-time because that would require asserts/panics in
// BODY: const fns.
// BODY:
// BODY: It'd be interesting to revisit this when const fn assertions gets
// BODY: implemented.

/// Create a kernel flag capability. Specifies the lowest/highest priority this
/// process is allowed to take, and which CPUs it is allowed to access.
#[allow(clippy::cast_lossless)] // Can't use From::from in const fn
pub const fn kernel_flags(lowest_prio: u32, highest_prio: u32, lowest_cpuid: u8, highest_cpuid: u8) -> u32 {
    0b111 | ((lowest_prio & 0x3F) << 4) | ((highest_prio & 0x3F) << 10)
        | ((lowest_cpuid as u32) << 16) | ((highest_cpuid as u32) << 24)
}

// TODO: Libuser: implement MapIoOrNormalRange capability.
// BODY: This capability is a bit of a pain. It requires inserting a pair of
// BODY: capabilities inside of the kcap array, so it'll probably require special
// BODY: handling similar to how we handle syscalls.

/// Maps the given physical memory page at a random address on process startup.
pub const fn map_normal_page(page: u32) -> u32 {
    0b1111111 | (page << 8)
}

/// Allows the process to use the given IO Ports directly (through the in/out).
#[allow(clippy::cast_lossless)] // Can't use From::from in const fn
pub const fn ioport(ioport: u16) -> u32 {
   0b1111111111 | ((ioport as u32) << 11)
}

/// Allows the process to create an IRQEvent for those IRQs. Each IRQ should be
/// under or equal to 0xFF, or equal to 0x3FF, in which case the IRQ will be
/// ignored.
#[allow(clippy::cast_lossless)] // Can't use From::from in const fn
pub const fn irq_pair(irq1: u16, irq2: u16) -> u32 {
    0b11111111111 | ((irq1 as u32 & 0x3FF) << 12) | ((irq2 as u32 & 0x3FF) << 22)
}

/// Declare the type of the application. 0 is a sysmodule, 1 is an application,
/// 2 is an applet. Only one application can run at a time.
pub const fn application_type(app_type: u32) -> u32 {
    0b1111111111111 | (app_type & 0b111 << 14)
}

/// The minimum kernel version this process expects.
pub const fn kernel_release_version(version: u32) -> u32 {
    0b11111111111111 | (version << 15)
}

/// Declare the maximum number of live handles this process is allowed to have
/// open.
pub const fn handle_table_size(size: u32) -> u32 {
    0b111111111111111 | ((size & 0x1FF) << 16)
}

/// Declares whether this application can be debugged (e.g. it allows the use
/// of the debug syscalls on it), and whether it can debug other processes.
#[allow(clippy::cast_lossless)] // Can't use From::from in const fn
pub const fn debug_flags(can_be_debugged: bool, can_debug_others: bool) -> u32 {
    0b1111111111111111 | ((can_be_debugged as u32) << 17) | ((can_debug_others as u32) << 18)
}