💾 Archived View for gemini.rmf-dev.com › repo › Vaati › Vgmi › files › 128c7659c95f282b0f9ac97384de6… captured on 2023-01-29 at 17:01:55. Gemini links have been rewritten to link to archived content
⬅️ Previous capture (2023-01-29)
-=-=-=-=-=-=-
0 /* See LICENSE file for copyright and license details. */
1 #include <stdlib.h>
2 #ifdef __linux__
3 #define OPENSSL_API_COMPAT 0x10101000L
4 #endif
5 #include <openssl/bn.h>
6 #include <openssl/evp.h>
7 #include <openssl/rsa.h>
8 #include <stdio.h>
9 #include <tls.h>
10 #include <openssl/x509.h>
11 #include <openssl/pem.h>
12 #include <openssl/err.h>
13 #include <string.h>
14 #include <strings.h>
15 #include <pwd.h>
16 #include <unistd.h>
17 #include <sys/stat.h>
18 #include <fcntl.h>
19 #ifdef __linux__
20 #include <sys/random.h>
21 #endif
22 #include "gemini.h"
23 #include "sandbox.h"
24 #include "str.h"
25 #include "xdg.h"
26
27 char home_path[1024];
28 char download_path[1024];
29 char config_path[1024];
30 const char* download_str = "Downloads";
31
32 int home_fd = -1;
33 int gethomefd() {
34 if (home_fd > -1)
35 return home_fd;
36 struct passwd *pw = getpwuid(geteuid());
37 if (!pw) return -1;
38 home_fd = open(pw->pw_dir, O_DIRECTORY);
39 strlcpy(home_path, pw->pw_dir, sizeof(home_path));
40 #ifndef DISABLE_XDG
41 if (!xdg_path(download_path, sizeof(download_path))) {
42 return home_fd;
43 }
44 #endif
45 snprintf(download_path, sizeof(download_path), "%s/%s",
46 home_path, download_str);
47 return home_fd;
48 }
49
50 int download_fd = -1;
51 int getdownloadfd() {
52 if (download_fd > -1)
53 return download_fd;
54 if (home_fd == -1 && gethomefd() == -1)
55 return -1;
56 #ifndef DISABLE_XDG
57 download_fd = open(download_path, O_DIRECTORY);
58 if (download_fd > -1) return download_fd;
59 #endif
60 download_fd = openat(home_fd, download_str, O_DIRECTORY);
61 if (download_fd > -1) return download_fd;
62
63 if (mkdirat(home_fd, download_str, 0700))
64 return -1;
65
66 download_fd = openat(home_fd, download_str, O_DIRECTORY);
67 if (download_fd > -1) return download_fd;
68
69 return -1;
70 }
71
72 int config_fd = -1;
73 int getconfigfd() {
74 if (config_fd > -1)
75 return config_fd;
76 if (home_fd == -1 && gethomefd() == -1)
77 return -1;
78 // check if .config exists first
79 int fd = openat(home_fd, ".config", O_DIRECTORY);
80 if (fd < 0) {
81 mkdirat(home_fd, ".config", 0700);
82 fd = openat(home_fd, ".config", O_DIRECTORY);
83 if (fd < 0)
84 return -1;
85 }
86 config_fd = openat(fd, "vgmi", O_DIRECTORY);
87 if (config_fd < 0) {
88 mkdirat(fd, "vgmi", 0700);
89 config_fd = openat(fd, "vgmi", O_DIRECTORY);
90 if (config_fd < 0)
91 return -1;
92 }
93 close(fd);
94 snprintf(config_path, sizeof(config_path), "%s/%s",
95 home_path, "/.config/vgmi");
96 return config_fd;
97 }
98
99 int cert_getpath(const char* host, char* crt, size_t crt_len,
100 char* key, size_t key_len) {
101 int len = strnlen(host, 1024);
102 if (strlcpy(crt, host, crt_len) >= crt_len - 4)
103 goto getpath_overflow;
104 if (strlcpy(key, host, key_len) >= key_len - 4)
105 goto getpath_overflow;
106 if (strlcpy(&crt[len], ".crt", crt_len - len) + len >= crt_len)
107 goto getpath_overflow;
108 if (strlcpy(&key[len], ".key", key_len - len) + len >= key_len)
109 goto getpath_overflow;
110 return len + 4;
111 getpath_overflow:
112 snprintf(client.tab->error,
113 sizeof(client.tab->error),
114 "The hostname is too long %s", host);
115 return -1;
116 }
117
118 #ifdef SANDBOX_SUN
119 #undef cert_create
120 #endif
121 int cert_create(char* host, char* error, int errlen) {
122 FILE* f = NULL;
123 int fd;
124 int ret = 1;
125 EVP_PKEY* pkey;
126 pkey = EVP_PKEY_new();
127 RSA* rsa = RSA_new();
128 BIGNUM* bne = BN_new();
129 X509* x509 = X509_new();
130 if (BN_set_word(bne, 65537) != 1) goto failed;
131 if (RSA_generate_key_ex(rsa, 2048, bne, NULL) != 1) goto failed;
132
133 EVP_PKEY_assign_RSA(pkey, rsa);
134 int id;
135 #ifdef __linux__
136 getrandom(&id, sizeof(id), GRND_RANDOM);
137 #else
138 arc4random_buf(&id, sizeof(id));
139 #endif
140 if (ASN1_INTEGER_set(X509_get_serialNumber(x509), id) != 1)
141 goto failed;
142
143 X509_gmtime_adj(X509_getm_notBefore(x509), 0);
144 X509_gmtime_adj(X509_getm_notAfter(x509), 157680000L);
145
146 if (X509_set_pubkey(x509, pkey) != 1) goto failed;
147
148 X509_NAME* name = X509_get_subject_name(x509);
149 if (X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC,
150 (unsigned char*)host, -1, -1, 0) != 1)
151 goto failed;
152
153 if (X509_set_issuer_name(x509, name) != 1) goto failed;
154 if (X509_sign(x509, pkey, EVP_sha1()) == 0) goto failed;
155
156 char key[1024];
157 char crt[1024];
158 if (cert_getpath(host, crt, sizeof(crt), key, sizeof(key)) == -1)
159 goto skip_error;
160
161 // Key
162 fd = openat(config_fd, key, O_CREAT|O_WRONLY, 0600);
163 if (fd < 0) {
164 snprintf(error, errlen, "Failed to open %s : %s",
165 key, strerror(errno));
166 goto skip_error;
167 }
168 f = fdopen(fd, "wb");
169 #ifdef SANDBOX_FREEBSD
170 if (makefd_writeonly(fd)) {
171 snprintf(error, errlen, "Failed to limit %s", key);
172 goto skip_error;
173 }
174 #endif
175 if (!f) {
176 snprintf(error, errlen, "Failed to write to %s : %s",
177 key, strerror(errno));
178 goto skip_error;
179 }
180 if (PEM_write_PrivateKey(f, pkey, NULL, NULL, 0, NULL, NULL) != 1)
181 goto failed;
182 fclose(f);
183
184 // Certificate
185 fd = openat(config_fd, crt, O_CREAT|O_WRONLY, 0600);
186 if (fd < 0) {
187 snprintf(error, errlen, "Failed to open %s", crt);
188 goto skip_error;
189 }
190 f = fdopen(fd, "wb");
191 #ifdef SANDBOX_FREEBSD
192 if (makefd_writeonly(fd)) {
193 snprintf(error, errlen, "Failed to limit %s", crt);
194 goto skip_error;
195 }
196 #endif
197 if (!f) {
198 snprintf(error, errlen, "Failed to write to %s", crt);
199 goto skip_error;
200 }
201 if (PEM_write_X509(f, x509) != 1)
202 goto failed;
203 fclose(f);
204
205 f = NULL;
206 ret = 0;
207 goto skip_error;
208 failed:
209 snprintf(error, errlen, "Failed to generate certificate");
210 skip_error:
211 if (f) fclose(f);
212 BN_free(bne);
213 EVP_PKEY_free(pkey);
214 X509_free(x509);
215 //RSA_free(rsa);
216 //if (ret) client.input.error = 1;
217 return ret;
218 }
219
220 int fatalI();
221 void fatal();
222
223 struct cert {
224 struct cert* next;
225 char hash[256];
226 char host[1024];
227 unsigned long long start;
228 unsigned long long end;
229 };
230 struct cert* first_cert = NULL;
231 struct cert* last_cert = NULL;
232
233 void cert_add(char* host, const char* hash, unsigned long long start,
234 unsigned long long end) {
235 struct cert* cert_ptr = malloc(sizeof(struct cert));
236 if (!cert_ptr) {
237 fatal();
238 return;
239 }
240 cert_ptr->next = NULL;
241 if (!cert_ptr) {
242 fatal();
243 return;
244 }
245 if (!first_cert) {
246 last_cert = first_cert = cert_ptr;
247 } else {
248 last_cert->next = cert_ptr;
249 last_cert = cert_ptr;
250 }
251 strlcpy(last_cert->hash, hash, sizeof(first_cert->hash));
252 strlcpy(last_cert->host, host, sizeof(first_cert->host));
253 last_cert->start = start;
254 last_cert->end = end;
255 }
256
257 int cert_load() {
258 config_fd = getconfigfd();
259 if (config_fd < 0) return -1;
260 int known_hosts = openat(config_fd, "known_hosts", 0);
261 if (known_hosts < 0) return 0;
262 FILE* f = fdopen(known_hosts, "r");
263 if (!f)
264 return -1;
265 fseek(f, 0, SEEK_END);
266 size_t length = ftell(f);
267 fseek(f, 0, SEEK_SET);
268 char* data = malloc(length);
269 if (!data) return fatalI();
270 if (fread(data, 1, length, f) != length) {
271 fclose(f);
272 return -1;
273 }
274 fclose(f);
275 char* ptr = data;
276 char* host = ptr;
277 char* hash = NULL;
278 char* start = NULL;
279 char* end = NULL;
280 while (ptr < data + length) {
281 if (ptr == data + length - 1) goto add;
282 if (*ptr == ' ' || *ptr == '\t' || (host?(*ptr == '\n'):0)) {
283 *ptr = '\0';
284 ptr++;
285 while (*ptr == ' ' || *ptr == '\t' || *ptr == '\n')
286 ptr++;
287 add:
288 if (!hash) {
289 hash = ptr;
290 ptr++;
291 continue;
292 }
293 if (!start) {
294 start = ptr;
295 ptr++;
296 continue;
297 }
298 if (!end) {
299 end = ptr;
300 ptr++;
301 continue;
302 }
303 cert_add(host, hash,
304 strtoull(start, NULL, 10),
305 strtoull(end, NULL, 10));
306 host = ptr;
307 end = start = hash = NULL;
308 } else if (*ptr == '\n') {
309 host = ptr+1;
310 end = start = hash = NULL;
311 }
312 ptr++;
313 }
314 free(data);
315 return 0;
316 }
317
318 int cert_loadcert(const char* host, struct cert_cache* cert) {
319 char crt[1024];
320 char key[1024];
321 if (cert_getpath(host, crt, sizeof(crt), key, sizeof(key)) == -1) {
322 return -1;
323 }
324 size_t crt_pos = 0;
325 size_t key_pos = 0;
326 int crt_fd = openat(config_fd, crt, 0);
327 if (crt_fd < 0) {
328 return -2;
329 }
330 int key_fd = openat(config_fd, key, 0);
331 if (key_fd < 0) {
332 close(key_fd);
333 return -2;
334 }
335 FILE* crt_f = fdopen(crt_fd, "rb");
336 FILE* key_f = fdopen(key_fd, "rb");
337 #ifdef SANDBOX_FREEBSD
338 makefd_readonly(crt_fd);
339 makefd_readonly(key_fd);
340 #endif
341 if (!crt_f || !key_f) {
342 close(crt_fd);
343 close(key_fd);
344 return -3;
345 }
346 fseek(crt_f, 0, SEEK_END);
347 crt_pos = ftell(crt_f);
348 fseek(key_f, 0, SEEK_END);
349 key_pos = ftell(key_f);
350
351 cert->crt = malloc(crt_pos);
352 if (!cert->crt) return fatalI();
353 cert->key = malloc(key_pos);
354 if (!cert->key) return fatalI();
355
356 fseek(crt_f, 0, SEEK_SET);
357 fseek(key_f, 0, SEEK_SET);
358 if (fread(cert->crt, 1, crt_pos, crt_f) != crt_pos ||
359 fread(cert->key, 1, key_pos, key_f) != key_pos) {
360 fclose(crt_f);
361 fclose(key_f);
362 return -3;
363 }
364
365 fclose(crt_f);
366 fclose(key_f);
367 cert->crt[crt_pos - 1] = '\0';
368 cert->key[key_pos - 1] = '\0';
369 cert->crt_len = crt_pos;
370 cert->key_len = key_pos;
371 strlcpy(cert->host, host, sizeof(cert->host));
372 return 0;
373 }
374
375 int cert_getcert(char* host, int reload) {
376 int index = 0;
377 while (client.certs && index < client.certs_size) {
378 if (!strcmp(client.certs[index].host, host)) {
379 if (reload) {
380 reload = 2;
381 break;
382 }
383 return index;
384 }
385 index++;
386 }
387 if (reload != 2) reload = 0;
388
389 struct cert_cache cert = {0};
390 #ifdef SANDBOX_SUN
391 if (send(rd_pair[1], &RD_CERTIFICATE, sizeof(SBC), 0) != sizeof(SBC))
392 return -1;
393 uint16_t i16 = strlen(host);
394 if (send(rd_pair[1], &i16, sizeof(i16), 0) != sizeof(i16))
395 return -1;
396 if (send(rd_pair[1], host, i16, 0) != i16)
397 return -1;
398 char c;
399 if (recv(rd_pair[1], &c, sizeof(c), 0) != 1 || c)
400 return -1;
401
402 if (recv(rd_pair[1], &cert.crt_len, sizeof(cert.crt_len), 0) !=
403 sizeof(cert.crt_len))
404 return -1;
405 if (recv(rd_pair[1], &cert.key_len, sizeof(cert.key_len), 0) !=
406 sizeof(cert.key_len))
407 return -1;
408 cert.crt = malloc(cert.crt_len);
409 if (!cert.crt) return -1;
410 cert.key = malloc(cert.key_len);
411 if (!cert.key) {
412 free(cert.crt);
413 return -1;
414 }
415 if (recv(rd_pair[1], cert.crt, cert.crt_len, 0) != (signed)cert.crt_len
416 || recv(rd_pair[1], cert.key, cert.key_len, 0) != (signed)cert.key_len)
417 {
418 free(cert.crt);
419 free(cert.key);
420 return -1;
421 }
422 #else
423 if (cert_loadcert(host, &cert))
424 return -1;
425 #endif
426
427 if (!reload) {
428 client.certs = realloc(client.certs,
429 sizeof(*client.certs) * (index + 1));
430 if (!client.certs) return fatalI();
431 bzero(&client.certs[index], sizeof(*client.certs));
432 } else {
433 free(client.certs[index].crt);
434 free(client.certs[index].key);
435 }
436 client.certs[index] = cert;
437 if (!reload)
438 client.certs_size++;
439 return index;
440 }
441
442 #ifdef SANDBOX_SUN
443 int cert_rewrite() {
444 int fd = wr_pair[1];
445 if (send(fd, &WR_KNOWNHOSTS, sizeof(SBC), 0) != sizeof(SBC))
446 return -3;
447 #else
448 int cert_rewrite() {
449 int cfd = getconfigfd();
450 if (cfd < 0) return -1;
451
452 int fd = openat(cfd, "known_hosts",
453 O_CREAT|O_WRONLY|O_CLOEXEC|O_TRUNC, 0600);
454 if (fd == -1)
455 return -2;
456 #ifdef SANDBOX_FREEBSD
457 if (makefd_writeonly(fd))
458 return -3;
459 #endif
460
461 #endif
462 char buf[2048];
463 for (struct cert* cert = first_cert; cert; cert = cert->next) {
464 int len = snprintf(buf, 2048, "%s %s %llu %llu\n",
465 cert->host, cert->hash,
466 cert->start, cert->end);
467 if (write(fd, buf, len) != len) {
468 #ifdef SANDBOX_SUN
469 send(fd, &WR_END, sizeof(SBC), 0);
470 #else
471 close(fd);
472 #endif
473 return -1;
474 }
475 }
476 #ifdef SANDBOX_SUN
477 send(fd, &WR_END, sizeof(SBC), 0);
478 #else
479 close(fd);
480 #endif
481 return 0;
482 }
483
484 int cert_forget(char* host) {
485 struct cert* prev = NULL;
486 for (struct cert* cert = first_cert; cert; cert = cert->next) {
487 if (strcmp(host, cert->host)) {
488 prev = cert;
489 continue;
490 }
491 if (prev)
492 prev->next = cert->next;
493 if (cert->next == NULL)
494 last_cert = prev;
495 free(cert);
496 return cert_rewrite();
497 }
498 return -1;
499 }
500
501 int cert_verify(char* host, const char* hash,
502 unsigned long long start,
503 unsigned long long end) {
504 struct cert* found = NULL;
505 for (struct cert* cert = first_cert; cert; cert = cert->next) {
506 if (strcmp(host, cert->host)) continue;
507 found = cert;
508 break;
509 }
510 unsigned long long now = time(NULL);
511 if (found) {
512 if (found->start < now && found->end > now)
513 return strcmp(found->hash, hash) ? -6 : 0;
514 return -5; // expired
515 }
516 #ifdef SANDBOX_SUN
517 int fd = wr_pair[1];
518 if (send(fd, &WR_KNOWNHOST_ADD, sizeof(SBC), 0) != sizeof(SBC))
519 return -3;
520 #else
521 int cfd = getconfigfd();
522 if (cfd < 0) return -1;
523
524 int fd = openat(cfd, "known_hosts", O_CREAT|O_APPEND|O_WRONLY, 0600);
525 if (fd == -1)
526 return -2;
527 if (!fdopen(fd, "a")) return -3;
528 #ifdef SANDBOX_FREEBSD
529 if (makefd_writeonly(fd))
530 return -3;
531 #endif
532 #endif
533 char buf[2048];
534 int len = snprintf(buf, 2048, "%s %s %lld %lld\n",
535 host, hash, start, end);
536 if (write(fd, buf, len) != len) {
537 close(fd);
538 return -4;
539 }
540
541 #ifdef SANDBOX_SUN
542 send(fd, &WR_END, sizeof(SBC), 0);
543 #else
544 close(fd);
545 #endif
546 cert_add(host, hash, start, end);
547 return 0;
548 }
549
550 void cert_free() {
551 struct cert *cert, *next_cert;
552 cert = first_cert;
553 while (cert) {
554 next_cert = cert->next;
555 free(cert);
556 cert = next_cert;
557 }
558 for (int i = 0; i < client.certs_size; i++) {
559 free(client.certs[i].crt);
560 free(client.certs[i].key);
561 }
562 free(client.certs);
563 if (config_fd > 0)
564 close(config_fd);
565 if (download_fd > 0)
566 close(download_fd);
567 if (home_fd > 0)
568 close(home_fd);
569 }
570