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 <pwd.h>
15 #include <unistd.h>
16 #include <sys/stat.h>
17 #include <fcntl.h>
18 #ifdef __linux__
19 #include <sys/random.h>
20 #endif
21 #include "gemini.h"
22 #include "sandbox.h"
23 #include "str.h"
24
25 char home_path[1024];
26 char download_path[1024];
27 char config_path[1024];
28 const char* download_str = "Downloads";
29
30 int home_fd = -1;
31 int gethomefd() {
32 if (home_fd > -1)
33 return home_fd;
34 struct passwd *pw = getpwuid(geteuid());
35 if (!pw) return -1;
36 home_fd = open(pw->pw_dir, O_DIRECTORY);
37 strlcpy(home_path, pw->pw_dir, sizeof(home_path));
38 snprintf(download_path, sizeof(download_path), "%!s(MISSING)/%!s(MISSING)", home_path, download_str);
39 return home_fd;
40 }
41
42 int download_fd = -1;
43 int getdownloadfd() {
44 if (download_fd > -1)
45 return download_fd;
46 if (home_fd == -1 && gethomefd() == -1)
47 return -1;
48 download_fd = openat(home_fd, download_str, O_DIRECTORY);
49 if (download_fd < 0) {
50 mkdirat(home_fd, download_str, 0700);
51 download_fd = openat(home_fd, download_str, O_DIRECTORY);
52 if (download_fd < 0)
53 return -1;
54 }
55 return download_fd;
56 }
57
58 int config_fd = -1;
59 int getconfigfd() {
60 if (config_fd > -1)
61 return config_fd;
62 if (home_fd == -1 && gethomefd() == -1)
63 return -1;
64 // check if .config exists first
65 int fd = openat(home_fd, ".config", O_DIRECTORY);
66 if (fd < 0) {
67 mkdirat(home_fd, ".config", 0700);
68 fd = openat(home_fd, ".config", O_DIRECTORY);
69 if (fd < 0)
70 return -1;
71 }
72 config_fd = openat(fd, "vgmi", O_DIRECTORY);
73 if (config_fd < 0) {
74 mkdirat(fd, "vgmi", 0700);
75 config_fd = openat(fd, "vgmi", O_DIRECTORY);
76 if (config_fd < 0)
77 return -1;
78 }
79 close(fd);
80 snprintf(config_path, sizeof(config_path), "%!s(MISSING)/%!s(MISSING)", home_path, "/.config/vgmi");
81 return config_fd;
82 }
83
84 int cert_getpath(char* host, char* crt, size_t crt_len, char* key, size_t key_len) {
85 int len = strnlen(host, 1024);
86 if (strlcpy(crt, host, crt_len) >= crt_len - 4)
87 goto getpath_overflow;
88 if (strlcpy(key, host, key_len) >= key_len - 4)
89 goto getpath_overflow;
90 if (strlcpy(&crt[len], ".crt", crt_len - len) + len >= crt_len)
91 goto getpath_overflow;
92 if (strlcpy(&key[len], ".key", key_len - len) + len >= key_len)
93 goto getpath_overflow;
94 return len+4;
95 getpath_overflow:
96 snprintf(client.tabs[client.tab].error,
97 sizeof(client.tabs[client.tab].error),
98 "The hostname is too long %!s(MISSING)", host);
99 return -1;
100 }
101
102 int cert_create(char* host, char* error, int errlen) {
103 FILE* f = NULL;
104 int fd;
105 int ret = 1;
106 EVP_PKEY* pkey;
107 pkey = EVP_PKEY_new();
108 RSA* rsa = RSA_new();
109 BIGNUM* bne = BN_new();
110 X509* x509 = X509_new();
111 if (BN_set_word(bne, 65537) != 1) goto failed;
112 if (RSA_generate_key_ex(rsa, 2048, bne, NULL) != 1) goto failed;
113
114 EVP_PKEY_assign_RSA(pkey, rsa);
115 int id;
116 #ifdef __linux__
117 getrandom(&id, sizeof(id), GRND_RANDOM);
118 #else
119 arc4random_buf(&id, sizeof(id));
120 #endif
121 if (ASN1_INTEGER_set(X509_get_serialNumber(x509), id) != 1)
122 goto failed;
123
124 X509_gmtime_adj(X509_getm_notBefore(x509), 0);
125 X509_gmtime_adj(X509_getm_notAfter(x509), 157680000L);
126
127 if (X509_set_pubkey(x509, pkey) != 1) goto failed;
128
129 X509_NAME* name = X509_get_subject_name(x509);
130 if (X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC,
131 (unsigned char *)host, -1, -1, 0) != 1)
132 goto failed;
133
134 if (X509_set_issuer_name(x509, name) != 1) goto failed;
135 if (X509_sign(x509, pkey, EVP_sha1()) == 0) goto failed;
136
137 char key[1024];
138 char crt[1024];
139 if (cert_getpath(host, crt, sizeof(crt), key, sizeof(key)) == -1)
140 goto skip_error;
141
142 // Key
143 fd = openat(config_fd, key, O_CREAT|O_WRONLY, 0600);
144 if (fd < 0) {
145 snprintf(error, errlen, "Failed to open %!s(MISSING) : %!s(MISSING)", key, strerror(errno));
146 goto skip_error;
147 }
148 f = fdopen(fd, "wb");
149 #ifdef __FreeBSD__
150 if (makefd_writeonly(fd)) {
151 snprintf(error, errlen, "Failed to limit %!s(MISSING)", key);
152 goto skip_error;
153 }
154 #endif
155 if (!f) {
156 snprintf(error, errlen, "Failed to write to %!s(MISSING) : %!s(MISSING)",
157 key, strerror(errno));
158 goto skip_error;
159 }
160 if (PEM_write_PrivateKey(f, pkey, NULL, NULL, 0, NULL, NULL) != 1)
161 goto failed;
162 fclose(f);
163
164 // Certificate
165 fd = openat(config_fd, crt, O_CREAT|O_WRONLY, 0600);
166 if (fd < 0) {
167 snprintf(error, errlen, "Failed to open %!s(MISSING)", crt);
168 goto skip_error;
169 }
170 f = fdopen(fd, "wb");
171 #ifdef __FreeBSD__
172 if (makefd_writeonly(fd)) {
173 snprintf(error, errlen, "Failed to limit %!s(MISSING)", crt);
174 goto skip_error;
175 }
176 #endif
177 if (!f) {
178 snprintf(error, errlen, "Failed to write to %!s(MISSING)", crt);
179 goto skip_error;
180 }
181 if (PEM_write_X509(f, x509) != 1)
182 goto failed;
183 fclose(f);
184
185 f = NULL;
186 ret = 0;
187 goto skip_error;
188 failed:
189 snprintf(error, errlen, "Failed to generate certificate");
190 skip_error:
191 if (f) fclose(f);
192 BN_free(bne);
193 EVP_PKEY_free(pkey);
194 X509_free(x509);
195 //RSA_free(rsa);
196 //if (ret) client.input.error = 1;
197 return ret;
198 }
199
200 int fatalI();
201 void fatal();
202
203 struct cert;
204 struct cert {
205 struct cert* next;
206 char hash[256];
207 char host[1024];
208 unsigned long long start;
209 unsigned long long end;
210 };
211 struct cert* first_cert = NULL;
212 struct cert* last_cert = NULL;
213
214 void cert_add(char* host, const char* hash, unsigned long long start, unsigned long long end) {
215 struct cert* cert_ptr = malloc(sizeof(struct cert));
216 if (!cert_ptr) {
217 fatal();
218 return;
219 }
220 cert_ptr->next = NULL;
221 if (!cert_ptr) {
222 fatal();
223 return;
224 }
225 if (!first_cert) {
226 last_cert = first_cert = cert_ptr;
227 } else {
228 last_cert->next = cert_ptr;
229 last_cert = cert_ptr;
230 }
231 strlcpy(last_cert->hash, hash, sizeof(first_cert->hash));
232 strlcpy(last_cert->host, host, sizeof(first_cert->host));
233 last_cert->start = start;
234 last_cert->end = end;
235 }
236
237 int cert_load() {
238 config_fd = getconfigfd();
239 if (config_fd < 0) return -1;
240 int known_hosts = openat(config_fd, "known_hosts", 0);
241 if (known_hosts < 0) return 0;
242 FILE* f = fdopen(known_hosts, "r");
243 if (!f)
244 return -1;
245 fseek(f, 0, SEEK_END);
246 size_t length = ftell(f);
247 fseek(f, 0, SEEK_SET);
248 char* data = malloc(length);
249 if (!data) return fatalI();
250 if (fread(data, 1, length, f) != length) {
251 fclose(f);
252 return -1;
253 }
254 fclose(f);
255 char* ptr = data;
256 char* host = ptr;
257 char* hash = NULL;
258 char* start = NULL;
259 char* end = NULL;
260 while (ptr < data + length) {
261 if (*ptr == ' ' || *ptr == '\t' || (host?(*ptr == '\n'):0)) {
262 *ptr = '\0';
263 ptr++;
264 while (*ptr == ' ' || *ptr == '\t' || *ptr == '\n') ptr++;
265 if (!hash) {
266 hash = ptr;
267 ptr++;
268 continue;
269 }
270 if (!start) {
271 start = ptr;
272 ptr++;
273 continue;
274 }
275 if (!end) {
276 end = ptr;
277 ptr++;
278 continue;
279 }
280
281 cert_add(host, hash, atoi(start), atoi(end));
282 host = ptr;
283 end = start = hash = NULL;
284 } else if (*ptr == '\n') {
285 host = ptr+1;
286 end = start = hash = NULL;
287 }
288 ptr++;
289 }
290 free(data);
291 return 0;
292 }
293
294 int cert_getcert(char* host) {
295
296 int index = 0;
297 while (client.certs && index < client.certs_size) {
298 if (!strcmp(client.certs[index].host, host)) return index;
299 index++;
300 }
301
302 char crt[1024];
303 char key[1024];
304 if (cert_getpath(host, crt, sizeof(crt), key, sizeof(key)) == -1) {
305 return -1;
306 }
307 size_t crt_pos = 0;
308 size_t key_pos = 0;
309 int crt_fd = openat(config_fd, crt, 0);
310 if (crt_fd < 0) {
311 return -2;
312 }
313 int key_fd = openat(config_fd, key, 0);
314 if (key_fd < 0) {
315 close(key_fd);
316 return -2;
317 }
318 FILE* crt_f = fdopen(crt_fd, "rb");
319 FILE* key_f = fdopen(key_fd, "rb");
320 #ifdef __FreeBSD__
321 makefd_readonly(crt_fd);
322 makefd_readonly(key_fd);
323 #endif
324 if (!crt_f || !key_f) {
325 close(crt_fd);
326 close(key_fd);
327 return -3;
328 }
329 fseek(crt_f, 0, SEEK_END);
330 crt_pos = ftell(crt_f);
331 fseek(key_f, 0, SEEK_END);
332 key_pos = ftell(key_f);
333
334 client.certs = realloc(client.certs, sizeof(*client.certs) * (index + 1));
335 if (!client.certs) return fatalI();
336 bzero(&client.certs[index], sizeof(*client.certs));
337 client.certs[index].crt = malloc(crt_pos);
338 if (!client.certs[index].crt) return fatalI();
339 client.certs[index].key = malloc(key_pos);
340 if (!client.certs[index].key) return fatalI();
341
342 fseek(crt_f, 0, SEEK_SET);
343 fseek(key_f, 0, SEEK_SET);
344 if (fread(client.certs[index].crt, 1, crt_pos, crt_f) != crt_pos ||
345 fread(client.certs[index].key, 1, key_pos, key_f) != key_pos) {
346 fclose(crt_f);
347 fclose(key_f);
348 return -3;
349 }
350
351 fclose(crt_f);
352 fclose(key_f);
353 client.certs[index].crt[crt_pos-1] = '\0';
354 client.certs[index].key[key_pos-1] = '\0';
355 client.certs[index].crt_len = crt_pos;
356 client.certs[index].key_len = key_pos;
357 strlcpy(client.certs[index].host, host, sizeof(client.certs[index].host));
358 client.certs_size++;
359 return index;
360 }
361
362 int cert_rewrite() {
363 int cfd = getconfigfd();
364 if (cfd < 0) return -1;
365
366 int fd = openat(cfd, "known_hosts", O_CREAT|O_WRONLY, 0600);
367 if (fd == -1)
368 return -2;
369 if (!fdopen(fd, "w")) return -3;
370 #ifdef __FreeBSD__
371 if (makefd_writeonly(fd))
372 return -3;
373 #endif
374 char buf[2048];
375 for (struct cert* cert = first_cert; cert; cert = cert->next) {
376 int len = snprintf(buf, 2048, "%!s(MISSING) %!s(MISSING) %!l(MISSING)ld %!l(MISSING)ld\n",
377 cert->host, cert->hash,
378 cert->start, cert->end);
379 if (write(fd, buf, len) != len) {
380 close(fd);
381 return -1;
382 }
383 }
384 close(fd);
385 return 0;
386 }
387
388 int cert_forget(char* host) {
389 struct cert* prev = NULL;
390 for (struct cert* cert = first_cert; cert; cert = cert->next) {
391 if (!strcmp(host, cert->host)) {
392 prev->next = cert->next;
393 free(cert);
394 cert_rewrite();
395 return 0;
396 }
397 prev = cert;
398 }
399 return -1;
400 }
401
402 int cert_verify(char* host, const char* hash,
403 unsigned long long start,
404 unsigned long long end) {
405 struct cert* found = NULL;
406 for (struct cert* cert = first_cert; cert; cert = cert->next) {
407 if (!strcmp(host, cert->host)) {
408 found = cert;
409 break;
410 }
411 }
412 unsigned long long now = time(NULL);
413 if (found && found->start < now && found->end > now)
414 return strcmp(found->hash, hash)?1:0;
415
416 int cfd = getconfigfd();
417 if (cfd < 0) return -1;
418
419 int fd = openat(cfd, "known_hosts", O_CREAT|O_APPEND|O_WRONLY, 0600);
420 if (fd == -1)
421 return -2;
422 if (!fdopen(fd, "a")) return -3;
423 #ifdef __FreeBSD__
424 if (makefd_writeonly(fd))
425 return -3;
426 #endif
427 char buf[2048];
428 int len = snprintf(buf, 2048, "%!s(MISSING) %!s(MISSING) %!l(MISSING)ld %!l(MISSING)ld\n",
429 host, hash, start, end);
430 if (write(fd, buf, len) != len) {
431 close(fd);
432 return -4;
433 }
434
435 close(fd);
436 cert_add(host, hash, start, end);
437 return 0;
438 }
439
440 void cert_free() {
441 struct cert *cert, *next_cert;
442 cert = first_cert;
443 while (cert) {
444 next_cert = cert->next;
445 free(cert);
446 cert = next_cert;
447 }
448 for (int i = 0; i < client.certs_size; i++) {
449 free(client.certs[i].crt);
450 free(client.certs[i].key);
451 }
452 free(client.certs);
453 if (config_fd > 0)
454 close(config_fd);
455 if (download_fd > 0)
456 close(download_fd);
457 if (home_fd > 0)
458 close(home_fd);
459 }
460