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, O_RDONLY);

327 if (crt_fd < 0) {

328 return -2;

329 }

330 int key_fd = openat(config_fd, key, O_RDONLY);

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 if (crt_f)

343 fclose(crt_f);

344 else

345 close(crt_fd);

346 if (key_f)

347 fclose(key_f);

348 else

349 close(key_fd);

350 return -3;

351 }

352 fseek(crt_f, 0, SEEK_END);

353 crt_pos = ftell(crt_f);

354 fseek(key_f, 0, SEEK_END);

355 key_pos = ftell(key_f);

356

357 cert->crt = malloc(crt_pos);

358 if (!cert->crt) return fatalI();

359 cert->key = malloc(key_pos);

360 if (!cert->key) return fatalI();

361

362 fseek(crt_f, 0, SEEK_SET);

363 fseek(key_f, 0, SEEK_SET);

364 if (fread(cert->crt, 1, crt_pos, crt_f) != crt_pos ||

365 fread(cert->key, 1, key_pos, key_f) != key_pos) {

366 fclose(crt_f);

367 fclose(key_f);

368 return -3;

369 }

370

371 fclose(crt_f);

372 fclose(key_f);

373 cert->crt[crt_pos - 1] = '\0';

374 cert->key[key_pos - 1] = '\0';

375 cert->crt_len = crt_pos;

376 cert->key_len = key_pos;

377 strlcpy(cert->host, host, sizeof(cert->host));

378 return 0;

379 }

380

381 int cert_getcert(char* host, int reload) {

382 int index = 0;

383 while (client.certs && index < client.certs_size) {

384 if (!strcmp(client.certs[index].host, host)) {

385 if (reload) {

386 reload = 2;

387 break;

388 }

389 return index;

390 }

391 index++;

392 }

393 if (reload != 2) reload = 0;

394

395 struct cert_cache cert = {0};

396 #ifdef SANDBOX_SUN

397 if (send(rd_pair[1], &RD_CERTIFICATE, sizeof(SBC), 0) != sizeof(SBC))

398 return -1;

399 uint16_t i16 = strlen(host);

400 if (send(rd_pair[1], &i16, sizeof(i16), 0) != sizeof(i16))

401 return -1;

402 if (send(rd_pair[1], host, i16, 0) != i16)

403 return -1;

404 char c;

405 if (recv(rd_pair[1], &c, sizeof(c), 0) != 1 || c)

406 return -1;

407

408 if (recv(rd_pair[1], &cert.crt_len, sizeof(cert.crt_len), 0) !=

409 sizeof(cert.crt_len))

410 return -1;

411 if (recv(rd_pair[1], &cert.key_len, sizeof(cert.key_len), 0) !=

412 sizeof(cert.key_len))

413 return -1;

414 cert.crt = malloc(cert.crt_len);

415 if (!cert.crt) return -1;

416 cert.key = malloc(cert.key_len);

417 if (!cert.key) {

418 free(cert.crt);

419 return -1;

420 }

421 if (recv(rd_pair[1], cert.crt, cert.crt_len, 0) != (signed)cert.crt_len

422 || recv(rd_pair[1], cert.key, cert.key_len, 0) != (signed)cert.key_len)

423 {

424 free(cert.crt);

425 free(cert.key);

426 return -1;

427 }

428 #else

429 if (cert_loadcert(host, &cert))

430 return -1;

431 #endif

432

433 if (!reload) {

434 client.certs = realloc(client.certs,

435 sizeof(*client.certs) * (index + 1));

436 if (!client.certs) return fatalI();

437 bzero(&client.certs[index], sizeof(*client.certs));

438 } else {

439 free(client.certs[index].crt);

440 free(client.certs[index].key);

441 }

442 client.certs[index] = cert;

443 if (!reload)

444 client.certs_size++;

445 return index;

446 }

447

448 #ifdef SANDBOX_SUN

449 int cert_rewrite() {

450 int fd = wr_pair[1];

451 if (send(fd, &WR_KNOWNHOSTS, sizeof(SBC), 0) != sizeof(SBC))

452 return -3;

453 #else

454 int cert_rewrite() {

455 int cfd = getconfigfd();

456 if (cfd < 0) return -1;

457

458 int fd = openat(cfd, "known_hosts",

459 O_CREAT|O_WRONLY|O_CLOEXEC|O_TRUNC, 0600);

460 if (fd == -1)

461 return -2;

462 #ifdef SANDBOX_FREEBSD

463 if (makefd_writeonly(fd))

464 return -3;

465 #endif

466

467 #endif

468 char buf[2048];

469 for (struct cert* cert = first_cert; cert; cert = cert->next) {

470 int len = snprintf(buf, 2048, "%s %s %llu %llu\n",

471 cert->host, cert->hash,

472 cert->start, cert->end);

473 if (write(fd, buf, len) != len) {

474 #ifdef SANDBOX_SUN

475 send(fd, &WR_END, sizeof(SBC), 0);

476 #else

477 close(fd);

478 #endif

479 return -1;

480 }

481 }

482 #ifdef SANDBOX_SUN

483 send(fd, &WR_END, sizeof(SBC), 0);

484 #else

485 close(fd);

486 #endif

487 return 0;

488 }

489

490 int cert_forget(char* host) {

491 struct cert* prev = NULL;

492 for (struct cert* cert = first_cert; cert; cert = cert->next) {

493 if (strcmp(host, cert->host)) {

494 prev = cert;

495 continue;

496 }

497 if (prev)

498 prev->next = cert->next;

499 if (cert->next == NULL)

500 last_cert = prev;

501 free(cert);

502 return cert_rewrite();

503 }

504 return -1;

505 }

506

507 int cert_verify(char* host, const char* hash,

508 unsigned long long start,

509 unsigned long long end) {

510 struct cert* found = NULL;

511 for (struct cert* cert = first_cert; cert; cert = cert->next) {

512 if (strcmp(host, cert->host)) continue;

513 found = cert;

514 break;

515 }

516 unsigned long long now = time(NULL);

517 if (found) {

518 if (found->start < now && found->end > now)

519 return strcmp(found->hash, hash) ? -6 : 0;

520 return -5; // expired

521 }

522 #ifdef SANDBOX_SUN

523 int fd = wr_pair[1];

524 if (send(fd, &WR_KNOWNHOST_ADD, sizeof(SBC), 0) != sizeof(SBC))

525 return -3;

526 #else

527 int cfd = getconfigfd();

528 if (cfd < 0) return -1;

529

530 int fd = openat(cfd, "known_hosts", O_CREAT|O_APPEND|O_WRONLY, 0600);

531 if (fd == -1)

532 return -2;

533 if (!fdopen(fd, "a")) return -3;

534 #ifdef SANDBOX_FREEBSD

535 if (makefd_writeonly(fd))

536 return -3;

537 #endif

538 #endif

539 char buf[2048];

540 int len = snprintf(buf, 2048, "%s %s %lld %lld\n",

541 host, hash, start, end);

542 if (write(fd, buf, len) != len) {

543 close(fd);

544 return -4;

545 }

546

547 #ifdef SANDBOX_SUN

548 send(fd, &WR_END, sizeof(SBC), 0);

549 #else

550 close(fd);

551 #endif

552 cert_add(host, hash, start, end);

553 return 0;

554 }

555

556 void cert_free() {

557 struct cert *cert, *next_cert;

558 cert = first_cert;

559 while (cert) {

560 next_cert = cert->next;

561 free(cert);

562 cert = next_cert;

563 }

564 for (int i = 0; i < client.certs_size; i++) {

565 free(client.certs[i].crt);

566 free(client.certs[i].key);

567 }

568 free(client.certs);

569 if (config_fd > 0)

570 close(config_fd);

571 if (download_fd > 0)

572 close(download_fd);

573 if (home_fd > 0)

574 close(home_fd);

575 }

576

577 struct ignore_entry {

578 char host[1024];

579 struct ignore_entry *next;

580 };

581 struct ignore_entry *ignore_list = NULL;

582 int cert_ignore_expiration(const char *host) {

583 struct ignore_entry *entry = malloc(sizeof(struct ignore_entry));

584 if (!entry) return -1;

585 strlcpy(entry->host, host, sizeof(entry->host));

586 entry->next = ignore_list;

587 ignore_list = entry;

588 return 0;

589 }

590

591 int cert_should_ignore(const char *host) {

592 struct ignore_entry *entry = ignore_list;

593 while (entry) {

594 if (!strncmp(entry->host, host, sizeof(entry->host)))

595 return 1;

596 entry = entry->next;

597 }

598 return 0;

599 }

600