💾 Archived View for gemini.rmf-dev.com › repo › Vaati › JailWarden › files › dfb243bc6fffa2ddf4faf12… captured on 2023-05-24 at 18:14:45. Gemini links have been rewritten to link to archived content
⬅️ Previous capture (2023-03-20)
-=-=-=-=-=-=-
0 /*
1 * Copyright (c) 2021 RMF <rawmonk@firemail.cc>
2 *
3 * Permission to use, copy, modify, and distribute this software for any
4 * purpose with or without fee is hereby granted, provided that the above
5 * copyright notice and this permission notice appear in all copies.
6 *
7 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
8 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
10 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
12 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
13 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
14 */
15
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <unistd.h>
19 #include <sys/param.h>
20 #include <sys/jail.h>
21 #include <sys/types.h>
22 #include <sys/stat.h>
23 #include <sys/wait.h>
24 #include <jail.h>
25 #include <string.h>
26 #include <cjson/cJSON.h>
27 #include "sha256.h"
28
29 #define CONF_PATH "/usr/local/etc/jailwarden.conf"
30
31 char** global_check;
32 char** global_ignore;
33 int global_check_len;
34 int global_ignore_len;
35 char* data_dir;
36
37 typedef struct cjail {
38 int id;
39 char* name;
40 char** check;
41 char** ignore;
42 int check_len;
43 int ignore_len;
44 } cjail;
45 cjail* jails = NULL;
46 int jails_len = 0;
47 int jail_arg = -1;
48 int jail_selected = -1;
49 char* jail_selected_name = NULL;
50
51 int to_ignore(char* path, int jid) {
52 for(int i=0; i<global_ignore_len; i++)
53 if(strcmp(path,global_ignore[i])==0) return 1;
54 for(int i=0; i<jails[jid].ignore_len; i++)
55 if(strcmp(path,jails[jid].ignore[i])==0) { printf("|%s| {%s} (%d)\n",path,jails[jid].ignore[i],jid);
56 return 1; }
57 return 0;
58 }
59
60 #include <sys/syslimits.h>
61 #include <dirent.h>
62 int list_files(FILE* f, char* path, int jid) {
63 DIR * d;
64 d = opendir(path);
65
66 if(!d) {
67 printf ("[%d][Error] Cannot open directory '%s'\n", jails[jid].id, path);
68 return 0;
69 }
70 int count = 0;
71 while (1) {
72 struct dirent * entry;
73 entry = readdir (d);
74 if (!entry) {
75 break;
76 }
77 if(strcmp(entry->d_name,"..")==0||strcmp(entry->d_name,".")==0) continue;
78 char npath[PATH_MAX];
79 strcpy(npath, path);
80 strcat(npath, "/");
81 strcat(npath, entry->d_name);
82 if(to_ignore(npath, jid)) continue; //return count;
83 if(entry->d_type==4) {
84 count += list_files(f, npath, jid);
85 } else if(entry->d_type==8) {
86 unsigned char buf[32];
87 if(sha256_file(npath, buf)) continue;
88 fprintf(f, "%s ", npath);
89 for(int i=0; i<32; i++) fprintf(f, "%02X", buf[i]);
90 fprintf(f, "\n");
91 count++;
92 }
93 }
94
95 if (closedir (d)) {
96 printf("[%d][Error] Could not close '%s'\n", jails[jid].id, path);
97 }
98 return count;
99 }
100
101 int db_write(FILE* f, int jid) {
102 int count = 0;
103 for(int i=0; i<global_check_len; i++)
104 count += list_files(f, global_check[i], jid);
105 for(int i=0; i<jails[jid].check_len; i++) {
106 for(int j=0; j<global_check_len; j++) {
107 if(strlen(global_check[j])>strlen(jails[jid].check[i])) {
108 if(memcmp(jails[jid].check[i],global_check[j],strlen(jails[jid].check[i]))==0) continue;
109 } else {
110 if(memcmp(jails[jid].check[i],global_check[j],strlen(global_check[j]))==0) continue;
111 }
112 }
113 count += list_files(f, jails[jid].check[i], jid);
114 }
115 printf("[%d][Info] %d file fingerprints updated succesfully\n", jails[jid].id, count);
116 return 0;
117 }
118
119 int db_read(FILE* f, int jid) {
120 char buf[PATH_MAX*2];
121 char * line = NULL;
122 size_t len = 0;
123 ssize_t read;
124
125 int count = 0;
126 int wrong = 0;
127 while ((read = getline(&line, &len, f)) != -1) {
128 line[read-1] = 0;
129 char* hash = strrchr(line,' ');
130 hash[0] = 0;
131 hash++;
132 uint8_t h[32];
133 int ret = sha256_file(line,h);
134 if(ret==-1) {
135 printf("[%d][Warning] %s was deleted\n", jails[jid].id, line);
136 wrong++;
137 continue;
138 } else if(ret==-2) {
139 printf("[%d][Warning] %s became too big to be verified\n", jails[jid].id, line);
140 wrong++;
141 continue;
142 }
143 int same = 1;
144 for(int i=0; i<64; i+=2) {
145 char nbuf[3];
146 nbuf[0] = hash[i];
147 nbuf[1] = hash[i+1];
148 nbuf[2] = 0;
149 uint8_t i8 = strtol(nbuf, NULL, 16);
150 if(i8!=h[i/2]) {
151 same=0;
152 break;
153 }
154 }
155 if(!same) {
156 printf("[%d][Warning] Wrong hash, %s, %s != ", jails[jid].id, line, hash);
157 for(int i=0; i<32; i++) printf("%02X", (unsigned char)h[i]);
158 printf("\n");
159 wrong++;
160 }
161 count++;
162
163 }
164 if(wrong)
165 printf("[%d][Warning] %d files verified, %d %s changed\n", jails[jid].id, count, wrong, wrong==1?"file":"files");
166 else
167 printf("[%d][Info] %d files verified succesfully\n", jails[jid].id, count);
168 return wrong;
169 }
170
171 #include <sys/uio.h>
172 #include <errno.h>
173 int fetch_jails() {
174 struct iovec iov[4];
175 u_int niov;
176 int ret;
177 int flags = 0;
178 int jid = 0;
179 char empty[] = "";
180 int jail_id;
181
182 for(int i=0; 1; i++) {
183
184 *(const void **)&iov[0].iov_base = "lastjid";
185 iov[0].iov_len = strlen("lastjid")+1;
186 iov[1].iov_base = &jid;
187 iov[1].iov_len = sizeof(jid);
188
189 if( ( jail_id = jail_get(iov, 2, flags) ) == -1 ) {
190 if( errno == ENOENT ) {
191 jails_len = i;
192 return i;
193 }
194 else {
195 jails_len = i;
196 return -1;
197 }
198 }
199 if(jail_id==-1) {
200 jails_len = i;
201 return i;
202 }
203
204 if(jails==NULL) jails = malloc(sizeof(cjail));
205 else jails = realloc(jails, sizeof(cjail)*(i+1));
206 memset(&jails[i], 0, sizeof(cjail));
207 jails[i].id = jail_id;
208 jails[i].name = jail_getname(jail_id);
209
210 jid = jail_id;
211 }
212 }
213
214 int main(int argc, char* argv[]) {
215
216 if(geteuid() != 0) {
217 printf("%s: Program must be run as root\n", argv[0]);
218 return -1;
219 }
220
221 if(argc<2) {
222 printf("%s: Not enough arguments\n", argv[0]);
223 arg_err:
224 printf("Usage: %s <check|update> [jail id]\n", argv[0]);
225 return -1;
226 }
227
228 int read;
229 if(strcmp(argv[1],"check")==0) read = 1;
230 else if(strcmp(argv[1],"update")==0) read = 0;
231 else {
232 printf("%s: Wrong arguments\n", argv[0]);
233 goto arg_err;
234 }
235
236 if(argc>2) {
237 jail_arg = atoi(argv[2]);
238 if(jail_arg<1) {
239 printf("%s: Invalid jail id\n", argv[0]);
240 goto arg_err;
241 }
242 jail_selected_name = jail_getname(jail_arg);
243 if(jail_selected_name==NULL) {
244 printf("%s: No jail with the id %d found\n", argv[0], jail_arg);
245 goto arg_err;
246 }
247 printf("[INFO] %s jail selected\n", jail_selected_name);
248 }
249
250 FILE* conf = fopen(CONF_PATH, "rb");
251 if(!conf) {
252 printf("[ERROR] No configuration file found at %s\n", CONF_PATH);
253 return -1;
254 }
255 fseek(conf, 0, SEEK_END);
256 int len = ftell(conf);
257 fseek(conf, 0, SEEK_SET);
258 char* data = malloc(len);
259 fread(data, 1, len, conf);
260 fclose(conf);
261 cJSON *json = cJSON_ParseWithLength(data, len);
262 free(data);
263 if(!json) {
264 conf_err:
265 printf("[ERROR] Invalid configuration file %s\n",cJSON_GetErrorPtr());
266 return -1;
267 }
268
269 cJSON *json_dir = cJSON_GetObjectItem(json, "data_dir");
270 if(!json_dir) goto conf_err;
271 if(!cJSON_IsString(json_dir)) goto conf_err;
272 data_dir = cJSON_GetStringValue(json_dir);
273 struct stat st = {0};
274 if (stat(data_dir, &st) == -1) {
275 if(mkdir(data_dir, 0700)==-1) {
276 printf("[ERROR] Failed to create data directory\n");
277 return -1;
278 }
279 }
280
281 int fetched = 0;
282 if(jail_selected_name == NULL) {
283 cJSON *json_fetch = cJSON_GetObjectItem(json, "all_jails");
284 if(json_fetch) {
285 if(!cJSON_IsBool(json_fetch)) goto conf_err;
286 if(cJSON_IsTrue(json_fetch)) {
287 if(fetch_jails()==-1) {
288 printf("[ERROR] Failed to list jails\n");
289 return -1;
290 }
291 if(jails_len==0) {
292 printf("[ERROR] No jail is currently running\n");
293 return -1;
294 }
295 fetched = 1;
296 }
297 }
298 }
299
300 cJSON *json_jails = cJSON_GetObjectItem(json, "jails");
301 if(!json_jails) goto conf_err;
302 len = cJSON_GetArraySize(json_jails);
303 if(jails==NULL)
304 jails = malloc(sizeof(cjail)*(jail_selected==-1?len:1));
305 int i=0;
306 for(; i<len; i++) {
307 cjail jail;
308 memset(&jail, 0, sizeof(cjail));
309 cJSON* item = cJSON_GetArrayItem(json_jails, i);
310 if(!item) break;
311 if(!item->string) break;
312 int global = 0;
313 if(strcmp(item->string,"*")==0) global = 1;
314 else jail.id = jail_getid(item->string);
315 if(!global) {
316 if(jail_selected_name!=NULL&&strcmp(item->string,jail_selected_name))
317 continue;
318 if(jail.id==-1) {
319 printf("[ERROR] No jail named %s\n", item->string);
320 return -1;
321 }
322 jail.name = malloc(strlen(item->string));
323 strcpy(jail.name,item->string);
324 }
325 cJSON* check = cJSON_GetObjectItem(item,"check");
326 if(check) {
327 int l = cJSON_GetArraySize(check);
328 if(global) global_check_len = l;
329 else jail.check_len = l;
330 char*** ptr = global?&global_check:&jail.check;
331 *ptr = malloc(sizeof(void*)*l);
332 for(int j=0; j<l; j++) {
333 cJSON* sitem = cJSON_GetArrayItem(check, j);
334 if(!cJSON_IsString(sitem)) {
335 break;
336 }
337 char* str = malloc(strlen(sitem->valuestring));
338 strcpy(str,sitem->valuestring);
339 (*ptr)[j] = str;
340 }
341 }
342 cJSON* ignore = cJSON_GetObjectItem(item,"ignore");
343 if(ignore) {
344 int l = cJSON_GetArraySize(ignore);
345 if(global) global_ignore_len = l;
346 else jail.ignore_len = l;
347 char*** ptr = global?&global_ignore:&jail.ignore;
348 *ptr = malloc(sizeof(void*)*l);
349 for(int j=0; j<l; j++) {
350 cJSON* sitem = cJSON_GetArrayItem(ignore, j);
351 if(!cJSON_IsString(sitem)) {
352 break;
353 }
354 char* str = malloc(strlen(sitem->valuestring));
355 strcpy(str,sitem->valuestring);
356 (*ptr)[j] = str;
357 }
358 }
359 if(global) continue;
360 if(fetched) {
361 for(int j=0; j<jails_len; j++) if(jails[j].id==jail.id) {
362 free(jails[j].name);
363 jails[j]=jail;
364 }
365 } else {
366 jails[jails_len] = jail;
367 jails_len++;
368 if(jail.id == jail_arg)
369 jail_selected = jails_len-1;
370 }
371 }
372 if(i!=len) goto conf_err;
373 cJSON_Delete(json);
374
375 if(jails_len==0) {
376 memset(jails, 0, sizeof(cjail));
377 jails[0].id=jail_arg;
378 jails[0].name=jail_selected_name;
379 jails_len++;
380 }
381
382 int parent = 0;
383 for(int i=0; i<jails_len; i++) {
384 if (fork() == 0) {
385 printf("[%d][Info] %s database for jail %s\n", jails[i].id, read?"Verifying":"Writting", jails[i].name);
386 char* path = malloc(strlen(data_dir)+strlen(jails[i].name)+5);
387 strcpy(path, data_dir);
388 strcat(path, "/");
389 strcat(path, jails[i].name);
390 strcat(path, ".dat");
391 FILE* db = fopen(path, read?"rb":"wb");
392 if(!db) {
393 printf("[%d][Error] There's no database for jail %s yet, aborting verification\n", jails[i].id, jails[i].name);
394 return -1;
395 }
396 free(path);
397
398 int ret = jail_attach(jails[i].id);
399 if(ret==-1) {
400 printf("[%d][Error] Cannot attach process to jail %s\n", jails[i].id, jails[i].name);
401 return -1;
402 }
403
404 if(read) db_read(db, i);
405 else db_write(db, i);
406
407 fclose(db);
408 printf("[%d][Info] Database for jail %s %s succesfully\n", jails[i].id, jails[i].name, read?"verified":"updated");
409 return 0;
410 } else parent = 1;
411 }
412 for(int i=0; i<jails_len&&parent; i++)
413 wait(NULL);
414 return 0;
415 }
416