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