Go Back

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 #include "util.h"

27

28 char home_path[1024];

29 char download_path[1024];

30 char config_path[1024];

31 const char* download_str = "Downloads";

32

33 int home_fd = -1;

34 int gethomefd() {

35 if (home_fd > -1)

36 return home_fd;

37 struct passwd *pw = getpwuid(geteuid());

38 if (!pw) return -1;

39 home_fd = open(pw->pw_dir, O_DIRECTORY);

40 STRLCPY(home_path, pw->pw_dir);

41 #ifndef DISABLE_XDG

42 if (!xdg_path(download_path, sizeof(download_path))) {

43 return home_fd;

44 }

45 #endif

46 snprintf(download_path, sizeof(download_path), "%s/%s",

47 home_path, download_str);

48 return home_fd;

49 }

50

51 int download_fd = -1;

52 int getdownloadfd() {

53 if (download_fd > -1)

54 return download_fd;

55 if (home_fd == -1 && gethomefd() == -1)

56 return -1;

57 #ifndef DISABLE_XDG

58 download_fd = open(download_path, O_DIRECTORY);

59 if (download_fd > -1) return download_fd;

60 #endif

61 download_fd = openat(home_fd, download_str, O_DIRECTORY);

62 if (download_fd > -1) return download_fd;

63

64 if (mkdirat(home_fd, download_str, 0700))

65 return -1;

66

67 download_fd = openat(home_fd, download_str, O_DIRECTORY);

68 if (download_fd > -1) return download_fd;

69

70 return -1;

71 }

72

73 int config_fd = -1;

74 int getconfigfd() {

75 if (config_fd > -1)

76 return config_fd;

77 if (home_fd == -1 && gethomefd() == -1)

78 return -1;

79 // check if .config exists first

80 int fd = openat(home_fd, ".config", O_DIRECTORY);

81 if (fd < 0) {

82 mkdirat(home_fd, ".config", 0700);

83 fd = openat(home_fd, ".config", O_DIRECTORY);

84 if (fd < 0)

85 return -1;

86 }

87 config_fd = openat(fd, "vgmi", O_DIRECTORY);

88 if (config_fd < 0) {

89 mkdirat(fd, "vgmi", 0700);

90 config_fd = openat(fd, "vgmi", O_DIRECTORY);

91 if (config_fd < 0)

92 return -1;

93 }

94 close(fd);

95 snprintf(config_path, sizeof(config_path), "%s/%s",

96 home_path, "/.config/vgmi");

97 return config_fd;

98 }

99

100 int cert_getpath(const char* host, char* crt, size_t crt_len,

101 char* key, size_t key_len) {

102 int len = strnlen(host, 1024);

103 if (strlcpy(crt, host, crt_len) >= crt_len - 4)

104 goto getpath_overflow;

105 if (strlcpy(key, host, key_len) >= key_len - 4)

106 goto getpath_overflow;

107 if (strlcpy(&crt[len], ".crt", crt_len - len) + len >= crt_len)

108 goto getpath_overflow;

109 if (strlcpy(&key[len], ".key", key_len - len) + len >= key_len)

110 goto getpath_overflow;

111 return len + 4;

112 getpath_overflow:

113 snprintf(client.tab->error,

114 sizeof(client.tab->error),

115 "The hostname is too long %s", host);

116 return -1;

117 }

118

119 #ifdef SANDBOX_SUN

120 #undef cert_create

121 #endif

122 int cert_create(char* host, char* error, int errlen) {

123 FILE* f = NULL;

124 int fd;

125 int ret = 1;

126 EVP_PKEY* pkey;

127 pkey = EVP_PKEY_new();

128 RSA* rsa = RSA_new();

129 BIGNUM* bne = BN_new();

130 X509* x509 = X509_new();

131 if (BN_set_word(bne, 65537) != 1) goto failed;

132 if (RSA_generate_key_ex(rsa, 2048, bne, NULL) != 1) goto failed;

133

134 EVP_PKEY_assign_RSA(pkey, rsa);

135 int id;

136 #ifdef __linux__

137 getrandom(&id, sizeof(id), GRND_RANDOM);

138 #else

139 arc4random_buf(&id, sizeof(id));

140 #endif

141 if (ASN1_INTEGER_set(X509_get_serialNumber(x509), id) != 1)

142 goto failed;

143

144 X509_gmtime_adj(X509_getm_notBefore(x509), 0);

145 X509_gmtime_adj(X509_getm_notAfter(x509), 157680000L);

146

147 if (X509_set_pubkey(x509, pkey) != 1) goto failed;

148

149 X509_NAME* name = X509_get_subject_name(x509);

150 if (X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC,

151 (unsigned char*)host, -1, -1, 0) != 1)

152 goto failed;

153

154 if (X509_set_issuer_name(x509, name) != 1) goto failed;

155 if (X509_sign(x509, pkey, EVP_sha1()) == 0) goto failed;

156

157 char key[1024];

158 char crt[1024];

159 if (cert_getpath(host, crt, sizeof(crt), key, sizeof(key)) == -1)

160 goto skip_error;

161

162 // Key

163 fd = openat(config_fd, key, O_CREAT|O_WRONLY, 0600);

164 if (fd < 0) {

165 snprintf(error, errlen, "Failed to open %s : %s",

166 key, strerror(errno));

167 goto skip_error;

168 }

169 f = fdopen(fd, "wb");

170 #ifdef SANDBOX_FREEBSD

171 if (makefd_writeonly(fd)) {

172 snprintf(error, errlen, "Failed to limit %s", key);

173 goto skip_error;

174 }

175 #endif

176 if (!f) {

177 snprintf(error, errlen, "Failed to write to %s : %s",

178 key, strerror(errno));

179 goto skip_error;

180 }

181 if (PEM_write_PrivateKey(f, pkey, NULL, NULL, 0, NULL, NULL) != 1)

182 goto failed;

183 fclose(f);

184

185 // Certificate

186 fd = openat(config_fd, crt, O_CREAT|O_WRONLY, 0600);

187 if (fd < 0) {

188 snprintf(error, errlen, "Failed to open %s", crt);

189 goto skip_error;

190 }

191 f = fdopen(fd, "wb");

192 #ifdef SANDBOX_FREEBSD

193 if (makefd_writeonly(fd)) {

194 snprintf(error, errlen, "Failed to limit %s", crt);

195 goto skip_error;

196 }

197 #endif

198 if (!f) {

199 snprintf(error, errlen, "Failed to write to %s", crt);

200 goto skip_error;

201 }

202 if (PEM_write_X509(f, x509) != 1)

203 goto failed;

204 fclose(f);

205

206 f = NULL;

207 ret = 0;

208 goto skip_error;

209 failed:

210 snprintf(error, errlen, "Failed to generate certificate");

211 skip_error:

212 if (f) fclose(f);

213 BN_free(bne);

214 EVP_PKEY_free(pkey);

215 X509_free(x509);

216 //RSA_free(rsa);

217 //if (ret) client.input.error = 1;

218 return ret;

219 }

220

221 int fatalI();

222 void fatal();

223

224 struct cert {

225 struct cert* next;

226 char hash[256];

227 char host[1024];

228 unsigned long long start;

229 unsigned long long end;

230 };

231 struct cert* first_cert = NULL;

232 struct cert* last_cert = NULL;

233

234 void cert_add(char* host, const char* hash, unsigned long long start,

235 unsigned long long end) {

236 struct cert* cert_ptr = malloc(sizeof(struct cert));

237 if (!cert_ptr) {

238 fatal();

239 return;

240 }

241 cert_ptr->next = NULL;

242 if (!cert_ptr) {

243 fatal();

244 return;

245 }

246 if (!first_cert) {

247 last_cert = first_cert = cert_ptr;

248 } else {

249 last_cert->next = cert_ptr;

250 last_cert = cert_ptr;

251 }

252 STRLCPY(last_cert->hash, hash);

253 STRLCPY(last_cert->host, host);

254 last_cert->start = start;

255 last_cert->end = end;

256 }

257

258 int cert_load() {

259 config_fd = getconfigfd();

260 if (config_fd < 0) return -1;

261 int known_hosts = openat(config_fd, "known_hosts", 0);

262 if (known_hosts < 0) return 0;

263 FILE* f = fdopen(known_hosts, "r");

264 if (!f)

265 return -1;

266 fseek(f, 0, SEEK_END);

267 size_t length = ftell(f);

268 fseek(f, 0, SEEK_SET);

269 char* data = malloc(length);

270 if (!data) return fatalI();

271 if (fread(data, 1, length, f) != length) {

272 fclose(f);

273 return -1;

274 }

275 fclose(f);

276 char* ptr = data;

277 char* host = ptr;

278 char* hash = NULL;

279 char* start = NULL;

280 char* end = NULL;

281 while (ptr < data + length) {

282 if (ptr == data + length - 1) goto add;

283 if (*ptr == ' ' || *ptr == '\t' || (host?(*ptr == '\n'):0)) {

284 *ptr = '\0';

285 ptr++;

286 while (*ptr == ' ' || *ptr == '\t' || *ptr == '\n')

287 ptr++;

288 add:

289 if (!hash) {

290 hash = ptr;

291 ptr++;

292 continue;

293 }

294 if (!start) {

295 start = ptr;

296 ptr++;

297 continue;

298 }

299 if (!end) {

300 end = ptr;

301 ptr++;

302 continue;

303 }

304 cert_add(host, hash,

305 strtoull(start, NULL, 10),

306 strtoull(end, NULL, 10));

307 host = ptr;

308 end = start = hash = NULL;

309 } else if (*ptr == '\n') {

310 host = ptr+1;

311 end = start = hash = NULL;

312 }

313 ptr++;

314 }

315 free(data);

316 return 0;

317 }

318

319 int cert_loadcert(const char* host, struct cert_cache* cert) {

320 char crt[1024];

321 char key[1024];

322 if (cert_getpath(host, crt, sizeof(crt), key, sizeof(key)) == -1) {

323 return -1;

324 }

325 size_t crt_pos = 0;

326 size_t key_pos = 0;

327 int crt_fd = openat(config_fd, crt, O_RDONLY);

328 if (crt_fd < 0) {

329 return -2;

330 }

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

332 if (key_fd < 0) {

333 close(key_fd);

334 return -2;

335 }

336 FILE* crt_f = fdopen(crt_fd, "rb");

337 FILE* key_f = fdopen(key_fd, "rb");

338 #ifdef SANDBOX_FREEBSD

339 makefd_readonly(crt_fd);

340 makefd_readonly(key_fd);

341 #endif

342 if (!crt_f || !key_f) {

343 if (crt_f)

344 fclose(crt_f);

345 else

346 close(crt_fd);

347 if (key_f)

348 fclose(key_f);

349 else

350 close(key_fd);

351 return -3;

352 }

353 fseek(crt_f, 0, SEEK_END);

354 crt_pos = ftell(crt_f);

355 fseek(key_f, 0, SEEK_END);

356 key_pos = ftell(key_f);

357

358 cert->crt = malloc(crt_pos);

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

360 cert->key = malloc(key_pos);

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

362

363 fseek(crt_f, 0, SEEK_SET);

364 fseek(key_f, 0, SEEK_SET);

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

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

367 fclose(crt_f);

368 fclose(key_f);

369 return -3;

370 }

371

372 fclose(crt_f);

373 fclose(key_f);

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

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

376 cert->crt_len = crt_pos;

377 cert->key_len = key_pos;

378 STRLCPY(cert->host, host);

379 return 0;

380 }

381

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

383 int index = 0;

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

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

386 if (reload) {

387 reload = 2;

388 break;

389 }

390 return index;

391 }

392 index++;

393 }

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

395

396 struct cert_cache cert = {0};

397 #ifdef SANDBOX_SUN

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

399 return -1;

400 uint16_t i16 = strlen(host);

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

402 return -1;

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

404 return -1;

405 char c;

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

407 return -1;

408

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

410 sizeof(cert.crt_len))

411 return -1;

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

413 sizeof(cert.key_len))

414 return -1;

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

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

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

418 if (!cert.key) {

419 free(cert.crt);

420 return -1;

421 }

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

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

424 {

425 free(cert.crt);

426 free(cert.key);

427 return -1;

428 }

429 #else

430 if (cert_loadcert(host, &cert))

431 return -1;

432 #endif

433

434 if (!reload) {

435 client.certs = realloc(client.certs,

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

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

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

439 } else {

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

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

442 }

443 client.certs[index] = cert;

444 if (!reload)

445 client.certs_size++;

446 return index;

447 }

448

449 #ifdef SANDBOX_SUN

450 int cert_rewrite() {

451 int fd = wr_pair[1];

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

453 return -3;

454 #else

455 int cert_rewrite() {

456 int cfd = getconfigfd();

457 if (cfd < 0) return -1;

458

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

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

461 if (fd == -1)

462 return -2;

463 #ifdef SANDBOX_FREEBSD

464 if (makefd_writeonly(fd))

465 return -3;

466 #endif

467

468 #endif

469 char buf[2048];

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

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

472 cert->host, cert->hash,

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

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

475 #ifdef SANDBOX_SUN

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

477 #else

478 close(fd);

479 #endif

480 return -1;

481 }

482 }

483 #ifdef SANDBOX_SUN

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

485 #else

486 close(fd);

487 #endif

488 return 0;

489 }

490

491 int cert_forget(char* host) {

492 struct cert* prev = NULL;

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

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

495 prev = cert;

496 continue;

497 }

498 if (prev)

499 prev->next = cert->next;

500 if (cert->next == NULL)

501 last_cert = prev;

502 free(cert);

503 return cert_rewrite();

504 }

505 return -1;

506 }

507

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

509 unsigned long long start,

510 unsigned long long end) {

511 struct cert* found = NULL;

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

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

514 found = cert;

515 break;

516 }

517 unsigned long long now = time(NULL);

518 if (found) {

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

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

521 return -5; // expired

522 }

523 #ifdef SANDBOX_SUN

524 int fd = wr_pair[1];

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

526 return -3;

527 #else

528 int cfd = getconfigfd();

529 if (cfd < 0) return -1;

530

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

532 if (fd == -1)

533 return -2;

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

535 #ifdef SANDBOX_FREEBSD

536 if (makefd_writeonly(fd))

537 return -3;

538 #endif

539 #endif

540 char buf[2048];

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

542 host, hash, start, end);

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

544 close(fd);

545 return -4;

546 }

547

548 #ifdef SANDBOX_SUN

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

550 #else

551 close(fd);

552 #endif

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

554 return 0;

555 }

556

557 void cert_free() {

558 struct cert *cert, *next_cert;

559 cert = first_cert;

560 while (cert) {

561 next_cert = cert->next;

562 free(cert);

563 cert = next_cert;

564 }

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

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

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

568 }

569 free(client.certs);

570 if (config_fd > 0)

571 close(config_fd);

572 if (download_fd > 0)

573 close(download_fd);

574 if (home_fd > 0)

575 close(home_fd);

576 }

577

578 struct ignore_entry {

579 char host[1024];

580 struct ignore_entry *next;

581 };

582 struct ignore_entry *ignore_list = NULL;

583 int cert_ignore_expiration(const char *host) {

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

585 if (!entry) return -1;

586 STRLCPY(entry->host, host);

587 entry->next = ignore_list;

588 ignore_list = entry;

589 return 0;

590 }

591

592 int cert_should_ignore(const char *host) {

593 struct ignore_entry *entry = ignore_list;

594 while (entry) {

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

596 return 1;

597 entry = entry->next;

598 }

599 return 0;

600 }

601