0 /* See LICENSE file for copyright and license details. */
1 #ifdef __linux__
2 #define OPENSSL_API_COMPAT 0x10101000L
3 #endif
4 #include <openssl/bn.h>
5 #include <openssl/evp.h>
6 #include <openssl/rsa.h>
7 #include <stdio.h>
8 #include <tls.h>
9 #include <openssl/x509.h>
10 #include <openssl/pem.h>
11 #include <openssl/err.h>
12 #include <string.h>
13 #include <pwd.h>
14 #include <unistd.h>
15 #include <sys/stat.h>
16 #ifdef __linux__
17 #include <sys/random.h>
18 #include <bsd/string.h>
19 #endif
20 #include "gemini.h"
21
22 char homefolder[1024];
23 int homepath_cached = 0;
24 int gethomefolder(char* path, size_t len) {
25 if (homepath_cached)
26 return strlcpy(path, homefolder, len);
27 struct passwd *pw = getpwuid(geteuid());
28 if (!pw) return 0;
29 size_t length = strlcpy(path, pw->pw_dir, len);
30 if (length >= len) return -1;
31 homepath_cached = 1;
32 strlcpy(homefolder, path, sizeof(homefolder));
33 return length;
34 }
35
36 char downloadfolder[1024];
37 int downloadpath_cached = 0;
38 int getdownloadfolder(char* path, size_t len) {
39 if (downloadpath_cached)
40 return strlcpy(path, downloadfolder, len);
41 size_t length = 0;
42 if (!homepath_cached)
43 length = gethomefolder(downloadfolder, sizeof(downloadfolder));
44 else
45 length = strlcpy(downloadfolder, homefolder, sizeof(downloadfolder));
46 length += strlcpy(&downloadfolder[length],
47 "/Downloads", sizeof(downloadfolder) - length);
48 struct stat _stat;
49 if (stat(downloadfolder, &_stat) && mkdir(downloadfolder, 0700))
50 return -1;
51 downloadpath_cached = 1;
52 strlcpy(path, downloadfolder, len);
53 return length;
54 }
55
56 char cachefolder[1024];
57 int cachepath_cached = 0;
58 int getcachefolder(char* path, size_t len) {
59 if (cachepath_cached)
60 return strlcpy(path, cachefolder, len);
61 int ret = gethomefolder(path, len);
62 if (ret == -1) return -1;
63 size_t length = ret;
64 if (length >= len) return -1;
65 length += strlcpy(&path[length], "/.cache/vgmi", len - length);
66 if (length >= len) return -1;
67 struct stat _stat;
68 if (stat(path, &_stat) && mkdir(path, 0700)) return -1;
69 cachepath_cached = 1;
70 strlcpy(cachefolder, path, sizeof(cachefolder));
71 return length;
72 }
73
74 int cert_getpath(char* host, char* crt, size_t crt_len, char* key, size_t key_len) {
75 char path[1024];
76 int ret = getcachefolder(path, sizeof(path));
77 if (ret < 1) {
78 snprintf(client.tabs[client.tab].error,
79 sizeof(client.tabs[client.tab].error),
80 "Failed to get cache directory");
81 return -1;
82 }
83 size_t len = ret;
84 if (len+1 >= sizeof(path))
85 goto getpath_overflow;
86 path[len] = '/';
87 path[++len] = '\0';
88 len += strlcpy(&path[len], host, sizeof(path) - len);
89 if (len >= sizeof(path))
90 goto getpath_overflow;
91 if (strlcpy(crt, path, crt_len) >= crt_len)
92 goto getpath_overflow;
93 if (strlcpy(key, path, key_len) >= key_len)
94 goto getpath_overflow;
95 if (crt_len - len < 4 || key_len - len < 4)
96 goto getpath_overflow;
97 if (strlcpy(&crt[len], ".crt", crt_len - len) + len >= crt_len)
98 goto getpath_overflow;
99 if (strlcpy(&key[len], ".key", key_len - len) + len >= key_len)
100 goto getpath_overflow;
101 return len+4;
102 getpath_overflow:
103 snprintf(client.tabs[client.tab].error,
104 sizeof(client.tabs[client.tab].error),
105 "The cache folder path is too long %!s(MISSING)", path);
106 return -1;
107 }
108
109 int cert_create(char* host) {
110 FILE *f = NULL;
111 int ret = 1;
112 EVP_PKEY* pkey;
113 pkey = EVP_PKEY_new();
114 RSA* rsa = RSA_new();
115 BIGNUM* bne = BN_new();
116 X509* x509 = X509_new();
117 if (BN_set_word(bne, 65537) != 1) goto failed;
118 if (RSA_generate_key_ex(rsa, 2048, bne, NULL) != 1) goto failed;
119
120 EVP_PKEY_assign_RSA(pkey, rsa);
121 int id;
122 #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__)
123 arc4random_buf(&id, sizeof(id));
124 #else
125 getrandom(&id, sizeof(id), GRND_RANDOM);
126 #endif
127 if (ASN1_INTEGER_set(X509_get_serialNumber(x509), id) != 1)
128 goto failed;
129
130 X509_gmtime_adj(X509_getm_notBefore(x509), 0);
131 X509_gmtime_adj(X509_getm_notAfter(x509), 157680000L);
132
133 if (X509_set_pubkey(x509, pkey) != 1) goto failed;
134
135 X509_NAME* name = X509_get_subject_name(x509);
136 if (X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC,
137 (unsigned char *)host, -1, -1, 0) != 1)
138 goto failed;
139
140 if (X509_set_issuer_name(x509, name) != 1) goto failed;
141 if (X509_sign(x509, pkey, EVP_sha1()) == 0) goto failed;
142
143 char key[1024];
144 char crt[1024];
145 if (cert_getpath(host, crt, sizeof(crt), key, sizeof(key)) == -1)
146 goto skip_error;
147
148 f = fopen(key, "wb");
149 if (!f) {
150 snprintf(client.tabs[client.tab].error,
151 sizeof(client.tabs[client.tab].error),
152 "Failed to write to %!s(MISSING)", key);
153 goto skip_error;
154 }
155 if (PEM_write_PrivateKey(f, pkey, NULL, NULL, 0, NULL, NULL) != 1)
156 goto failed;
157 fclose(f);
158
159 f = fopen(crt, "wb");
160 if (!f) {
161 snprintf(client.tabs[client.tab].error,
162 sizeof(client.tabs[client.tab].error),
163 "Failed to write to %!s(MISSING)", crt);
164 goto skip_error;
165 }
166 if (PEM_write_X509(f, x509) != 1)
167 goto failed;
168 fclose(f);
169 f = NULL;
170 ret = 0;
171 goto skip_error;
172 failed:
173 snprintf(client.tabs[client.tab].error,
174 sizeof(client.tabs[client.tab].error),
175 "Failed to generate certificate");
176 skip_error:
177 if (f) fclose(f);
178 BN_free(bne);
179 EVP_PKEY_free(pkey);
180 X509_free(x509);
181 //RSA_free(rsa);
182 if (ret) client.input.error = 1;
183 return ret;
184 }
185