1 /*
2  * Copyright (c) 2021 Omar Polo <op@omarpolo.com>
3  *
4  * Permission to use, copy, modify, and distribute this software for any
5  * purpose with or without fee is hereby granted, provided that the above
6  * copyright notice and this permission notice appear in all copies.
7  *
8  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15  */
16 
17 #include "gmid.h"
18 
19 #include <errno.h>
20 #include <string.h>
21 
22 #include <openssl/bn.h>
23 #include <openssl/pem.h>
24 #include <openssl/x509_vfy.h>
25 #include <openssl/x509v3.h>
26 
27 int
starts_with(const char * str,const char * prefix)28 starts_with(const char *str, const char *prefix)
29 {
30 	size_t i;
31 
32 	if (prefix == NULL)
33 		return 0;
34 
35 	for (i = 0; prefix[i] != '\0'; ++i)
36 		if (str[i] != prefix[i])
37 			return 0;
38 	return 1;
39 }
40 
41 int
ends_with(const char * str,const char * sufx)42 ends_with(const char *str, const char *sufx)
43 {
44 	size_t i, j;
45 
46 	i = strlen(str);
47 	j = strlen(sufx);
48 
49 	if (j > i)
50 		return 0;
51 
52 	i -= j;
53 	for (j = 0; str[i] != '\0'; i++, j++)
54 		if (str[i] != sufx[j])
55 			return 0;
56 	return 1;
57 }
58 
59 ssize_t
filesize(int fd)60 filesize(int fd)
61 {
62 	ssize_t len;
63 
64 	if ((len = lseek(fd, 0, SEEK_END)) == -1)
65 		return -1;
66 	if (lseek(fd, 0, SEEK_SET) == -1)
67 		return -1;
68 	return len;
69 }
70 
71 char *
absolutify_path(const char * path)72 absolutify_path(const char *path)
73 {
74 	char *wd, *r;
75 
76 	if (*path == '/') {
77 		if ((r = strdup(path)) == NULL)
78 			err(1, "strdup");
79 		return r;
80 	}
81 
82 	wd = getcwd(NULL, 0);
83 	if (asprintf(&r, "%s/%s", wd, path) == -1)
84 		err(1, "asprintf");
85 	free(wd);
86 	return r;
87 }
88 
89 char *
xstrdup(const char * s)90 xstrdup(const char *s)
91 {
92 	char *d;
93 
94 	if ((d = strdup(s)) == NULL)
95 		err(1, "strdup");
96 	return d;
97 }
98 
99 void *
xcalloc(size_t nmemb,size_t size)100 xcalloc(size_t nmemb, size_t size)
101 {
102 	void *d;
103 
104 	if ((d = calloc(nmemb, size)) == NULL)
105 		err(1, "calloc");
106 	return d;
107 }
108 
109 void
gen_certificate(const char * hostname,const char * certpath,const char * keypath)110 gen_certificate(const char *hostname, const char *certpath, const char *keypath)
111 {
112 	BIGNUM		*e;
113 	EVP_PKEY	*pkey;
114 	RSA		*rsa;
115 	X509		*x509;
116 	X509_NAME	*name;
117 	FILE		*f;
118 	const unsigned char *host = (const unsigned char*)hostname;
119 
120 	log_notice(NULL,
121 	    "generating new certificate for %s (it could take a while)",
122 	    host);
123 
124 	if ((pkey = EVP_PKEY_new()) == NULL)
125                 fatal("couldn't create a new private key");
126 
127 	if ((rsa = RSA_new()) == NULL)
128 		fatal("couldn't generate rsa");
129 
130 	if ((e = BN_new()) == NULL)
131 		fatal("couldn't allocate a bignum");
132 
133 	BN_set_word(e, RSA_F4);
134 	if (!RSA_generate_key_ex(rsa, 4096, e, NULL))
135 		fatal("couldn't generate a rsa key");
136 
137 	if (!EVP_PKEY_assign_RSA(pkey, rsa))
138 		fatal("couldn't assign the key");
139 
140 	if ((x509 = X509_new()) == NULL)
141 		fatal("couldn't generate the X509 certificate");
142 
143 	ASN1_INTEGER_set(X509_get_serialNumber(x509), 0);
144 	X509_gmtime_adj(X509_get_notBefore(x509), 0);
145 	X509_gmtime_adj(X509_get_notAfter(x509), 315360000L); /* 10 years */
146 	X509_set_version(x509, 3);
147 
148 	if (!X509_set_pubkey(x509, pkey))
149 		fatal("couldn't set the public key");
150 
151 	name = X509_get_subject_name(x509);
152 	if (!X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC, host, -1, -1, 0))
153 		fatal("couldn't add CN to cert");
154 	X509_set_issuer_name(x509, name);
155 
156 	if (!X509_sign(x509, pkey, EVP_sha256()))
157                 fatal("couldn't sign the certificate");
158 
159 	if ((f = fopen(keypath, "w")) == NULL)
160 		fatal("fopen(%s): %s", keypath, strerror(errno));
161 	if (!PEM_write_PrivateKey(f, pkey, NULL, NULL, 0, NULL, NULL))
162 		fatal("couldn't write private key");
163 	fclose(f);
164 
165 	if ((f = fopen(certpath, "w")) == NULL)
166 		fatal("fopen(%s): %s", certpath, strerror(errno));
167 	if (!PEM_write_X509(f, x509))
168 		fatal("couldn't write cert");
169 	fclose(f);
170 
171 	BN_free(e);
172 	X509_free(x509);
173 	RSA_free(rsa);
174 }
175 
176 X509_STORE *
load_ca(const char * path)177 load_ca(const char *path)
178 {
179 	FILE		*f = NULL;
180 	X509		*x = NULL;
181 	X509_STORE	*store;
182 
183 	if ((store = X509_STORE_new()) == NULL)
184 		return NULL;
185 
186 	if ((f = fopen(path, "r")) == NULL)
187 		goto err;
188 
189 	if ((x = PEM_read_X509(f, NULL, NULL, NULL)) == NULL)
190 		goto err;
191 
192 	if (X509_check_ca(x) == 0)
193 		goto err;
194 
195 	if (!X509_STORE_add_cert(store, x))
196 		goto err;
197 
198 	X509_free(x);
199 	fclose(f);
200 	return store;
201 
202 err:
203 	X509_STORE_free(store);
204 	if (x != NULL)
205 		X509_free(x);
206 	if (f != NULL)
207 		fclose(f);
208 	return NULL;
209 }
210 
211 int
validate_against_ca(X509_STORE * ca,const uint8_t * chain,size_t len)212 validate_against_ca(X509_STORE *ca, const uint8_t *chain, size_t len)
213 {
214 	X509		*client;
215 	BIO		*m;
216 	X509_STORE_CTX	*ctx = NULL;
217 	int		 ret = 0;
218 
219 	if ((m = BIO_new_mem_buf(chain, len)) == NULL)
220 		return 0;
221 
222 	if ((client = PEM_read_bio_X509(m, NULL, NULL, NULL)) == NULL)
223 		goto end;
224 
225 	if ((ctx = X509_STORE_CTX_new()) == NULL)
226 		goto end;
227 
228 	if (!X509_STORE_CTX_init(ctx, ca, client, NULL))
229 		goto end;
230 
231 	ret = X509_verify_cert(ctx);
232 
233 end:
234 	BIO_free(m);
235 	if (client != NULL)
236 		X509_free(client);
237 	if (ctx != NULL)
238 		X509_STORE_CTX_free(ctx);
239 	return ret;
240 }
241 
242 void
dispatch_imsg(struct imsgbuf * ibuf,imsg_handlerfn ** handlers,size_t size)243 dispatch_imsg(struct imsgbuf *ibuf, imsg_handlerfn **handlers, size_t size)
244 {
245 	struct imsg	imsg;
246 	size_t		datalen, i;
247 	ssize_t		n;
248 
249 	if ((n = imsg_read(ibuf)) == -1) {
250 		if (errno == EAGAIN || errno == EWOULDBLOCK)
251 			return;
252 		_exit(1);
253 	}
254 
255 	if (n == 0)
256 		_exit(1);
257 
258 	for (;;) {
259 		if ((n = imsg_get(ibuf, &imsg)) == -1)
260 			_exit(1);
261 		if (n == 0)
262 			return;
263 		datalen = imsg.hdr.len - IMSG_HEADER_SIZE;
264 		i = imsg.hdr.type;
265 		if (i > (size / sizeof(imsg_handlerfn*)) || handlers[i] == NULL)
266 			abort();
267 		handlers[i](ibuf, &imsg, datalen);
268 		imsg_free(&imsg);
269 	}
270 }
271