hammer2 - SPAN protocol work
[dragonfly.git] / sbin / hammer2 / crypto.c
CommitLineData
62efe6ec
MD
1/*
2 * Copyright (c) 2011-2012 The DragonFly Project. All rights reserved.
3 *
4 * This code is derived from software contributed to The DragonFly Project
5 * by Matthew Dillon <dillon@dragonflybsd.org>
6 * by Venkatesh Srinivas <vsrinivas@dragonflybsd.org>
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 *
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in
16 * the documentation and/or other materials provided with the
17 * distribution.
18 * 3. Neither the name of The DragonFly Project nor the names of its
19 * contributors may be used to endorse or promote products derived
20 * from this software without specific, prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
25 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
26 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
27 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
28 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
29 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
30 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
31 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
32 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 */
35
36#include "hammer2.h"
37
62efe6ec 38/*
02454b3e
MD
39 * Setup crypto for pthreads
40 */
41static pthread_mutex_t *crypto_locks;
42int crypto_count;
43
44static
45unsigned long
46hammer2_crypto_id_callback(void)
47{
48 return ((unsigned long)(uintptr_t)pthread_self());
49}
50
51static
52void
53hammer2_crypto_locking_callback(int mode, int type,
54 const char *file __unused, int line __unused)
55{
56 assert(type >= 0 && type < crypto_count);
57 if (mode & CRYPTO_LOCK) {
58 pthread_mutex_lock(&crypto_locks[type]);
59 } else {
60 pthread_mutex_unlock(&crypto_locks[type]);
61 }
62}
63
64void
65hammer2_crypto_setup(void)
66{
67 crypto_count = CRYPTO_num_locks();
68 crypto_locks = calloc(crypto_count, sizeof(crypto_locks[0]));
69 CRYPTO_set_id_callback(hammer2_crypto_id_callback);
70 CRYPTO_set_locking_callback(hammer2_crypto_locking_callback);
71}
72
73/*
62efe6ec
MD
74 * Synchronously negotiate crypto for a new session. This must occur
75 * within 10 seconds or the connection is error'd out.
76 *
77 * We work off the IP address and/or reverse DNS. The IP address is
78 * checked first, followed by the IP address at various levels of granularity,
79 * followed by the full domain name and domain names at various levels of
80 * granularity.
81 *
82 * /etc/hammer2/remote/<name>.pub - Contains a public key
83 * /etc/hammer2/remote/<name>.none - Indicates no encryption (empty file)
84 * (e.g. localhost.none).
85 *
86 * We first attempt to locate a public key file based on the peer address or
87 * peer FQDN.
88 *
89 * <name>.none - No further negotiation is needed. We simply return.
90 * All communication proceeds without encryption.
91 * No public key handshake occurs in this situation.
92 * (both ends must match).
93 *
94 * <name>.pub - We have located the public key for the peer. Both
95 * sides transmit a block encrypted with their private
96 * keys and the peer's public key.
97 *
98 * Both sides receive a block and decrypt it.
99 *
100 * Both sides formulate a reply using the decrypted
101 * block and transmit it.
102 *
103 * communication proceeds with the negotiated session
104 * key (typically AES-256-CBC).
105 *
106 * If we fail to locate the appropriate file and no floating.db exists the
107 * connection is terminated without further action.
108 *
109 * If floating.db exists the connection proceeds with a floating negotiation.
110 */
111typedef union {
112 struct sockaddr sa;
113 struct sockaddr_in sa_in;
114 struct sockaddr_in6 sa_in6;
115} sockaddr_any_t;
116
117void
118hammer2_crypto_negotiate(hammer2_iocom_t *iocom)
119{
120 sockaddr_any_t sa;
121 socklen_t salen = sizeof(sa);
122 char peername[128];
123 char realname[128];
124 hammer2_handshake_t handtx;
125 hammer2_handshake_t handrx;
5cf97ec5
MD
126 char buf1[sizeof(handtx)];
127 char buf2[sizeof(handtx)];
62efe6ec
MD
128 char *ptr;
129 char *path;
130 struct stat st;
131 FILE *fp;
132 RSA *keys[3] = { NULL, NULL, NULL };
133 size_t i;
134 size_t blksize;
135 size_t blkmask;
136 ssize_t n;
137 int fd;
138
139 /*
140 * Get the peer IP address for the connection as a string.
141 */
142 if (getpeername(iocom->sock_fd, &sa.sa, &salen) < 0) {
143 iocom->ioq_rx.error = HAMMER2_IOQ_ERROR_NOPEER;
144 iocom->flags |= HAMMER2_IOCOMF_EOF;
145 if (DebugOpt)
146 fprintf(stderr, "accept: getpeername() failed\n");
147 goto done;
148 }
149 if (getnameinfo(&sa.sa, salen, peername, sizeof(peername),
150 NULL, 0, NI_NUMERICHOST) < 0) {
151 iocom->ioq_rx.error = HAMMER2_IOQ_ERROR_NOPEER;
152 iocom->flags |= HAMMER2_IOCOMF_EOF;
153 if (DebugOpt)
154 fprintf(stderr, "accept: cannot decode sockaddr\n");
155 goto done;
156 }
157 if (DebugOpt) {
158 if (realhostname_sa(realname, sizeof(realname),
159 &sa.sa, salen) == HOSTNAME_FOUND) {
160 fprintf(stderr, "accept from %s (%s)\n",
161 peername, realname);
162 } else {
163 fprintf(stderr, "accept from %s\n", peername);
164 }
165 }
166
167 /*
168 * Find the remote host's public key
9b8b748f
MD
169 *
170 * If the link is not to be encrypted (<ip>.none located) we shortcut
171 * the handshake entirely. No buffers are exchanged.
62efe6ec
MD
172 */
173 asprintf(&path, "%s/%s.pub", HAMMER2_PATH_REMOTE, peername);
174 if ((fp = fopen(path, "r")) == NULL) {
175 free(path);
176 asprintf(&path, "%s/%s.none",
177 HAMMER2_PATH_REMOTE, peername);
178 if (stat(path, &st) < 0) {
179 iocom->ioq_rx.error = HAMMER2_IOQ_ERROR_NORKEY;
180 iocom->flags |= HAMMER2_IOCOMF_EOF;
181 if (DebugOpt)
182 fprintf(stderr, "auth failure: unknown host\n");
183 goto done;
184 }
185 if (DebugOpt)
186 fprintf(stderr, "auth succeeded, unencrypted link\n");
9b8b748f 187 goto done;
62efe6ec
MD
188 }
189 if (fp) {
190 keys[0] = PEM_read_RSA_PUBKEY(fp, NULL, NULL, NULL);
191 fclose(fp);
192 if (keys[0] == NULL) {
193 iocom->ioq_rx.error = HAMMER2_IOQ_ERROR_KEYFMT;
194 iocom->flags |= HAMMER2_IOCOMF_EOF;
195 if (DebugOpt)
196 fprintf(stderr,
197 "auth failure: bad key format\n");
198 goto done;
199 }
200 }
201
202 /*
203 * Get our public and private keys
204 */
205 free(path);
206 asprintf(&path, HAMMER2_DEFAULT_DIR "/rsa.pub");
207 if ((fp = fopen(path, "r")) == NULL) {
208 iocom->ioq_rx.error = HAMMER2_IOQ_ERROR_NOLKEY;
209 iocom->flags |= HAMMER2_IOCOMF_EOF;
210 goto done;
211 }
212 keys[1] = PEM_read_RSA_PUBKEY(fp, NULL, NULL, NULL);
213 fclose(fp);
214 if (keys[1] == NULL) {
215 iocom->ioq_rx.error = HAMMER2_IOQ_ERROR_KEYFMT;
216 iocom->flags |= HAMMER2_IOCOMF_EOF;
217 if (DebugOpt)
218 fprintf(stderr, "auth failure: bad host key format\n");
219 goto done;
220 }
221
222 free(path);
223 asprintf(&path, HAMMER2_DEFAULT_DIR "/rsa.prv");
224 if ((fp = fopen(path, "r")) == NULL) {
225 iocom->ioq_rx.error = HAMMER2_IOQ_ERROR_NOLKEY;
226 iocom->flags |= HAMMER2_IOCOMF_EOF;
227 if (DebugOpt)
228 fprintf(stderr, "auth failure: bad host key format\n");
229 goto done;
230 }
231 keys[2] = PEM_read_RSAPrivateKey(fp, NULL, NULL, NULL);
232 fclose(fp);
233 if (keys[2] == NULL) {
234 iocom->ioq_rx.error = HAMMER2_IOQ_ERROR_KEYFMT;
235 iocom->flags |= HAMMER2_IOCOMF_EOF;
236 if (DebugOpt)
237 fprintf(stderr, "auth failure: bad host key format\n");
238 goto done;
239 }
240 free(path);
241 path = NULL;
242
243 /*
244 * public key encrypt/decrypt block size.
245 */
246 if (keys[0]) {
247 blksize = (size_t)RSA_size(keys[0]);
248 if (blksize != (size_t)RSA_size(keys[1]) ||
249 blksize != (size_t)RSA_size(keys[2]) ||
250 sizeof(handtx) % blksize != 0) {
251 iocom->ioq_rx.error = HAMMER2_IOQ_ERROR_KEYFMT;
252 iocom->flags |= HAMMER2_IOCOMF_EOF;
253 if (DebugOpt)
254 fprintf(stderr, "auth failure: "
255 "key size mismatch\n");
256 goto done;
257 }
258 } else {
259 blksize = sizeof(handtx);
260 }
261 blkmask = blksize - 1;
262
263 bzero(&handrx, sizeof(handrx));
264 bzero(&handtx, sizeof(handtx));
265
266 /*
267 * Fill all unused fields (particular all junk fields) with random
268 * data, and also set the session key.
269 */
270 fd = open("/dev/urandom", O_RDONLY);
271 if (fd < 0 ||
272 fstat(fd, &st) < 0 || /* something wrong */
273 S_ISREG(st.st_mode) || /* supposed to be a RNG dev! */
274 read(fd, &handtx, sizeof(handtx)) != sizeof(handtx)) {
275urandfail:
276 if (fd >= 0)
277 close(fd);
278 iocom->ioq_rx.error = HAMMER2_IOQ_ERROR_BADURANDOM;
279 iocom->flags |= HAMMER2_IOCOMF_EOF;
280 if (DebugOpt)
281 fprintf(stderr, "auth failure: bad rng\n");
282 goto done;
283 }
284 if (bcmp(&handrx, &handtx, sizeof(handtx)) == 0)
285 goto urandfail; /* read all zeros */
286 close(fd);
5cf97ec5 287 /* ERR_load_crypto_strings(); openssl debugging */
62efe6ec
MD
288
289 /*
290 * Handshake with the remote.
291 *
292 * Encrypt with my private and remote's public
293 * Decrypt with my private and remote's public
294 *
295 * When encrypting we have to make sure our buffer fits within the
296 * modulus, which typically requires bit 7 o the first byte to be
297 * zero. To be safe make sure that bit 7 and bit 6 is zero.
298 */
299 snprintf(handtx.quickmsg, sizeof(handtx.quickmsg), "Testing 1 2 3");
300 handtx.magic = HAMMER2_MSGHDR_MAGIC;
301 handtx.version = 1;
302 handtx.flags = 0;
303 assert(sizeof(handtx.verf) * 4 == sizeof(handtx.sess));
304 bzero(handtx.verf, sizeof(handtx.verf));
305
306 handtx.pad1[0] &= 0x3f; /* message must fit within modulus */
307 handtx.pad2[0] &= 0x3f; /* message must fit within modulus */
308
309 for (i = 0; i < sizeof(handtx.sess); ++i)
310 handtx.verf[i / 4] ^= handtx.sess[i];
311
312 /*
313 * Write handshake buffer to remote
314 */
315 for (i = 0; i < sizeof(handtx); i += blksize) {
316 ptr = (char *)&handtx + i;
317 if (keys[0]) {
318 /*
319 * Since we are double-encrypting we have to make
320 * sure that the result of the first stage does
321 * not blow out the modulus for the second stage.
322 *
323 * The pointer is pointing to the pad*[] area so
324 * we can mess with that until the first stage
325 * is legal.
326 */
327 do {
328 ++*(int *)(ptr + 4);
5cf97ec5 329 if (RSA_private_encrypt(blksize, ptr, buf1,
62efe6ec
MD
330 keys[2], RSA_NO_PADDING) < 0) {
331 iocom->ioq_rx.error =
332 HAMMER2_IOQ_ERROR_KEYXCHGFAIL;
333 }
5cf97ec5 334 } while (buf1[0] & 0xC0);
62efe6ec 335
5cf97ec5 336 if (RSA_public_encrypt(blksize, buf1, buf2,
62efe6ec
MD
337 keys[0], RSA_NO_PADDING) < 0) {
338 iocom->ioq_rx.error =
339 HAMMER2_IOQ_ERROR_KEYXCHGFAIL;
340 }
341 }
5cf97ec5 342 if (write(iocom->sock_fd, buf2, blksize) != (ssize_t)blksize) {
62efe6ec
MD
343 fprintf(stderr, "WRITE ERROR\n");
344 }
345 }
346 if (iocom->ioq_rx.error) {
347 iocom->flags |= HAMMER2_IOCOMF_EOF;
348 if (DebugOpt)
349 fprintf(stderr, "auth failure: key exchange failure "
350 "during encryption\n");
351 goto done;
352 }
353
354 /*
355 * Read handshake buffer from remote
356 */
357 i = 0;
358 while (i < sizeof(handrx)) {
359 ptr = (char *)&handrx + i;
360 n = read(iocom->sock_fd, ptr, blksize - (i & blkmask));
361 if (n <= 0)
362 break;
363 ptr -= (i & blkmask);
364 i += n;
365 if (keys[0] && (i & blkmask) == 0) {
5cf97ec5 366 if (RSA_private_decrypt(blksize, ptr, buf1,
62efe6ec
MD
367 keys[2], RSA_NO_PADDING) < 0)
368 iocom->ioq_rx.error =
369 HAMMER2_IOQ_ERROR_KEYXCHGFAIL;
5cf97ec5 370 if (RSA_public_decrypt(blksize, buf1, ptr,
62efe6ec
MD
371 keys[0], RSA_NO_PADDING) < 0)
372 iocom->ioq_rx.error =
373 HAMMER2_IOQ_ERROR_KEYXCHGFAIL;
374 }
375 }
376 if (iocom->ioq_rx.error) {
377 iocom->flags |= HAMMER2_IOCOMF_EOF;
378 if (DebugOpt)
379 fprintf(stderr, "auth failure: key exchange failure "
380 "during decryption\n");
381 goto done;
382 }
383
384 /*
385 * Validate the received data. Try to make this a constant-time
386 * algorithm.
387 */
388 if (i != sizeof(handrx)) {
389keyxchgfail:
390 iocom->ioq_rx.error = HAMMER2_IOQ_ERROR_KEYXCHGFAIL;
391 iocom->flags |= HAMMER2_IOCOMF_EOF;
392 if (DebugOpt)
393 fprintf(stderr, "auth failure: key exchange failure\n");
394 goto done;
395 }
396
397 if (handrx.magic == HAMMER2_MSGHDR_MAGIC_REV) {
398 handrx.version = bswap16(handrx.version);
399 handrx.flags = bswap32(handrx.flags);
400 }
401 for (i = 0; i < sizeof(handrx.sess); ++i)
402 handrx.verf[i / 4] ^= handrx.sess[i];
403 n = 0;
404 for (i = 0; i < sizeof(handrx.verf); ++i)
405 n += handrx.verf[i];
406 if (handrx.version != 1)
407 ++n;
408 if (n != 0)
409 goto keyxchgfail;
410
5cf97ec5
MD
411 /*
412 * Calculate the session key and initialize the iv[].
413 */
414 assert(HAMMER2_AES_KEY_SIZE * 2 == sizeof(handrx.sess));
415 for (i = 0; i < HAMMER2_AES_KEY_SIZE; ++i) {
416 iocom->sess[i] = handrx.sess[i] ^ handtx.sess[i];
417 iocom->ioq_rx.iv[i] = handrx.sess[HAMMER2_AES_KEY_SIZE + i] ^
418 handtx.sess[HAMMER2_AES_KEY_SIZE + i];
419 iocom->ioq_tx.iv[i] = handrx.sess[HAMMER2_AES_KEY_SIZE + i] ^
420 handtx.sess[HAMMER2_AES_KEY_SIZE + i];
62efe6ec 421 }
5cf97ec5
MD
422 printf("sess: ");
423 for (i = 0; i < HAMMER2_AES_KEY_SIZE; ++i)
424 printf("%02x", (unsigned char)iocom->sess[i]);
425 printf("\n");
426 printf("iv: ");
427 for (i = 0; i < HAMMER2_AES_KEY_SIZE; ++i)
428 printf("%02x", (unsigned char)iocom->ioq_rx.iv[i]);
429 printf("\n");
430
431 EVP_CIPHER_CTX_init(&iocom->ioq_rx.ctx);
432 EVP_DecryptInit_ex(&iocom->ioq_rx.ctx, HAMMER2_AES_TYPE_EVP, NULL,
433 iocom->sess, iocom->ioq_rx.iv);
434 EVP_CIPHER_CTX_set_padding(&iocom->ioq_rx.ctx, 0);
435
436 EVP_CIPHER_CTX_init(&iocom->ioq_tx.ctx);
437 EVP_EncryptInit_ex(&iocom->ioq_tx.ctx, HAMMER2_AES_TYPE_EVP, NULL,
438 iocom->sess, iocom->ioq_tx.iv);
439 EVP_CIPHER_CTX_set_padding(&iocom->ioq_tx.ctx, 0);
440
441 iocom->flags |= HAMMER2_IOCOMF_CRYPTED;
442
443 if (DebugOpt)
444 fprintf(stderr, "auth success: %s\n", handrx.quickmsg);
62efe6ec
MD
445done:
446 if (path)
447 free(path);
448 if (keys[0])
449 RSA_free(keys[0]);
450 if (keys[1])
451 RSA_free(keys[1]);
452 if (keys[1])
453 RSA_free(keys[2]);
454}
5cf97ec5
MD
455
456/*
457 * Decrypt pending data in the ioq's fifo. The data is decrypted in-place.
458 */
459void
460hammer2_crypto_decrypt(hammer2_iocom_t *iocom, hammer2_ioq_t *ioq)
461{
462 int p_len;
463 int n;
464 int i;
465 char buf[512];
466
467 if ((iocom->flags & HAMMER2_IOCOMF_CRYPTED) == 0)
468 return;
469 p_len = ioq->fifo_end - ioq->fifo_cdx;
470 p_len &= ~HAMMER2_AES_KEY_MASK;
471 if (p_len == 0)
472 return;
473 for (i = 0; i < p_len; i += n) {
474 n = (p_len - i > (int)sizeof(buf)) ?
475 (int)sizeof(buf) : p_len - i;
476 bcopy(ioq->buf + ioq->fifo_cdx + i, buf, n);
477 EVP_DecryptUpdate(&ioq->ctx,
478 ioq->buf + ioq->fifo_cdx + i, &n,
479 buf, n);
480 }
481 ioq->fifo_cdx += p_len;
482}
483
484/*
485 * Decrypt data in the message's auxilary buffer. The data is decrypted
486 * in-place.
487 */
488void
489hammer2_crypto_decrypt_aux(hammer2_iocom_t *iocom, hammer2_ioq_t *ioq,
490 hammer2_msg_t *msg, int already)
491{
492 int p_len;
493 int n;
494 int i;
495 char buf[512];
496
497 if ((iocom->flags & HAMMER2_IOCOMF_CRYPTED) == 0)
498 return;
499 p_len = msg->aux_size;
500 assert((p_len & HAMMER2_AES_KEY_MASK) == 0);
501 if (p_len == 0)
502 return;
503 i = already;
504 while (i < p_len) {
505 n = (p_len - i > (int)sizeof(buf)) ?
506 (int)sizeof(buf) : p_len - i;
507 bcopy(msg->aux_data + i, buf, n);
508 EVP_DecryptUpdate(&ioq->ctx,
509 msg->aux_data + i, &n,
510 buf, n);
511 i += n;
512 }
513#if 0
514 EVP_DecryptUpdate(&iocom->ioq_rx.ctx,
515 msg->aux_data, &p_len,
516 msg->aux_data, p_len);
517#endif
518}
519
520int
521hammer2_crypto_encrypt(hammer2_iocom_t *iocom, hammer2_ioq_t *ioq,
02454b3e 522 struct iovec *iov, int n, size_t *nmaxp)
5cf97ec5
MD
523{
524 int p_len;
525 int i;
526 int already;
527 int nmax;
528
529 if ((iocom->flags & HAMMER2_IOCOMF_CRYPTED) == 0)
530 return (n);
531 nmax = sizeof(ioq->buf) - ioq->fifo_cdx; /* max new bytes */
532 already = ioq->fifo_cdx - ioq->fifo_beg; /* already encrypted */
533
534 for (i = 0; i < n; ++i) {
535 p_len = iov[i].iov_len;
536 if (p_len <= already) {
537 already -= p_len;
538 continue;
539 }
540 p_len -= already;
02454b3e 541 p_len &= ~HAMMER2_AES_KEY_MASK;
5cf97ec5
MD
542 if (p_len > nmax)
543 p_len = nmax;
544 EVP_EncryptUpdate(&ioq->ctx,
545 ioq->buf + ioq->fifo_cdx, &p_len,
546 (char *)iov[i].iov_base + already, p_len);
547 ioq->fifo_cdx += p_len;
548 ioq->fifo_end += p_len;
549 nmax -= p_len;
550 if (nmax == 0)
551 break;
552 already = 0;
553 }
554 iov[0].iov_base = ioq->buf + ioq->fifo_beg;
555 iov[0].iov_len = ioq->fifo_cdx - ioq->fifo_beg;
02454b3e 556 *nmaxp = (size_t)(ioq->fifo_cdx - ioq->fifo_beg);
5cf97ec5
MD
557
558 return (1);
559}
560
561void
562hammer2_crypto_encrypt_wrote(hammer2_iocom_t *iocom, hammer2_ioq_t *ioq,
563 int nact)
564{
565 if ((iocom->flags & HAMMER2_IOCOMF_CRYPTED) == 0)
566 return;
567 if (nact == 0)
568 return;
569 ioq->fifo_beg += nact;
570 if (ioq->fifo_beg == ioq->fifo_end) {
571 ioq->fifo_beg = 0;
572 ioq->fifo_cdx = 0;
573 ioq->fifo_end = 0;
574 }
575}