Skip to content

Commit bb0a504

Browse files
privileged service loading with protection
1 parent 379ea7d commit bb0a504

3 files changed

Lines changed: 56 additions & 18 deletions

File tree

src/kernel/filesystem.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -503,7 +503,9 @@ export abstract class AbstractFileSystem {
503503
absolute_path === "/sys" ||
504504
absolute_path.startsWith("/sys/") ||
505505
absolute_path === "/boot" ||
506-
absolute_path.startsWith("/boot/");
506+
absolute_path.startsWith("/boot/") ||
507+
absolute_path === "/etc/services/privileged/" ||
508+
absolute_path.startsWith("/etc/services/privileged/");
507509

508510
if (is_protected) {
509511
throw new ReadOnlyError(absolute_path);

src/programs/core/ignition/services.ts

Lines changed: 48 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import type {Kernel, SpawnResult} from "../../../kernel";
22

33
const SERVICES_DIR = "/etc/services/";
4+
const PRIVILEGED_SERVICES_DIR = "/etc/services/privileged/";
45

56
interface ServiceRestartPolicyBase {
67
on: "failure" | "always" | "never";
@@ -27,8 +28,9 @@ interface ServiceFile {
2728
// TODO: do something with name
2829
// TODO: do something with max_retries
2930

30-
interface ServiceFileWithId extends ServiceFile {
31+
interface ServiceFileWithExtra extends ServiceFile {
3132
id: string;
33+
privileged: boolean;
3234
}
3335

3436
const CLEAN_EXIT_CODES = new Set([0, 143]); // 0 = success, 143 = SIGTERM
@@ -54,7 +56,7 @@ export type ServiceStatus = ServiceStatusRunning | ServiceStatusNotRunning;
5456
export class ServiceManager {
5557
readonly #kernel: Kernel;
5658

57-
readonly #service_files: Map<string, ServiceFileWithId> = new Map();
59+
readonly #service_files: Map<string, ServiceFileWithExtra> = new Map();
5860
readonly #running_services: Map<string, SpawnResult> = new Map(); // service ID to spawn result
5961
readonly #should_be_running_services: Set<string> = new Set();
6062
readonly #failed_services: Set<string> = new Set();
@@ -73,6 +75,38 @@ export class ServiceManager {
7375

7476
const service_files = await fs.list_dir(SERVICES_DIR);
7577

78+
// TODO: DRY
79+
80+
let privileged_service_files: string[] | null = null;
81+
if(await fs.exists(PRIVILEGED_SERVICES_DIR)) {
82+
privileged_service_files = await fs.list_dir(PRIVILEGED_SERVICES_DIR);
83+
84+
for (const file_name of privileged_service_files) {
85+
if (file_name.endsWith(".service.json")) {
86+
const file_path = fs.join(PRIVILEGED_SERVICES_DIR, file_name);
87+
const file_content = await fs.read_file(file_path) as string;
88+
89+
try {
90+
const service_data = JSON.parse(file_content) as ServiceFile;
91+
const service_id = file_name.substring(0, file_name.length - ".service.json".length);
92+
93+
// TODO: validate service_data here
94+
95+
const service: ServiceFileWithExtra = {
96+
id: service_id,
97+
...service_data,
98+
privileged: true
99+
};
100+
101+
// add or update service file
102+
this.#service_files.set(service_id, service);
103+
} catch (e) {
104+
console.error(`Failed to parse service file ${file_name}:`, e);
105+
}
106+
}
107+
}
108+
}
109+
76110
// load each service file
77111
for (const file_name of service_files) {
78112
if (file_name.endsWith(".service.json")) {
@@ -83,11 +117,17 @@ export class ServiceManager {
83117
const service_data = JSON.parse(file_content) as ServiceFile;
84118
const service_id = file_name.substring(0, file_name.length - ".service.json".length);
85119

120+
if ("privileged" in service_data) {
121+
// dont let them try to bypass it by putting privileged in the service file
122+
delete service_data.privileged;
123+
}
124+
86125
// TODO: validate service_data here
87126

88-
const service: ServiceFileWithId = {
127+
const service: ServiceFileWithExtra = {
89128
id: service_id,
90-
...service_data
129+
...service_data,
130+
privileged: false
91131
};
92132

93133
// add or update service file
@@ -100,7 +140,7 @@ export class ServiceManager {
100140

101141
// remove any services that no longer exist
102142
for (const existing_service_id of this.#service_files.keys()) {
103-
if (!service_files.includes(existing_service_id + ".service.json")) {
143+
if (!service_files.includes(existing_service_id + ".service.json") && !(privileged_service_files && privileged_service_files.includes(existing_service_id + ".service.json"))) {
104144
this.#service_files.delete(existing_service_id);
105145
}
106146
}
@@ -111,6 +151,8 @@ export class ServiceManager {
111151
const temp_mark: Set<string> = new Set();
112152
const result: string[] = [];
113153

154+
// TODO: should privileged have priority?
155+
114156
const visit = (service_id: string) => {
115157
if (visited.has(service_id)) {
116158
return;
@@ -166,7 +208,7 @@ export class ServiceManager {
166208

167209
let spawn_result: SpawnResult;
168210
try {
169-
spawn_result = this.#kernel.spawn(service.exec, service.args || []);
211+
spawn_result = this.#kernel.spawn(service.exec, service.args || [], undefined, service.privileged);
170212
} catch (e) {
171213
console.error(`Failed to start service ${service_id}:`, e);
172214
return;

src/programs/telnetd.ts

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { Program } from "../types";
1+
import type {PrivilegedProgram} from "../types";
22
import {AbstractTerminal, KeyEvent} from "../kernel/term_ctl";
33
import {UserspaceClientSocket} from "../kernel/network";
44

@@ -182,25 +182,19 @@ export default {
182182
completion: async () => [],
183183
main: async (data) => {
184184
// extract from data to make code less verbose
185-
const { term, process, kernel: userspace_kernel } = data;
185+
const { term, process, kernel } = data;
186186

187-
if (!userspace_kernel.has_network_manager()) {
187+
if (!kernel.has_network_manager()) {
188188
term.writeln(`${term.ansi.PREFABS.error}No network manager found. This program requires a network manager to function.${term.ansi.STYLE.reset_all}`);
189189
return 1;
190190
}
191191

192-
const net_manager = userspace_kernel.get_network_manager();
192+
const net_manager = kernel.get_network_manager();
193193
if (!await net_manager.is_up(true)) {
194194
term.writeln(`${term.ansi.PREFABS.error}Network is down!${term.ansi.STYLE.reset_all}`);
195195
return 1;
196196
}
197197

198-
// TODO: this will just run as an optional privileged service so wont be needed (once i figure out what privileged services will be) for now just running by hand
199-
const kernel = await userspace_kernel.request_privilege("To establish a virtual terminal for Telnet.");
200-
if (!kernel) {
201-
return 1;
202-
}
203-
204198
const server = await process.network_listen(2323);
205199
server.add_event_listener("connection", async (socket) => {
206200
const session_term = new TelnetTerminal(socket);
@@ -243,4 +237,4 @@ export default {
243237
process.detach();
244238
return 0;
245239
}
246-
} as Program;
240+
} as PrivilegedProgram;

0 commit comments

Comments
 (0)