💾 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

View Raw

More Information

⬅️ 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