Merge branches 'hammer2' and 'master' of ssh://crater.dragonflybsd.org/repository...
[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
MD
38/*
39 * Synchronously negotiate crypto for a new session. This must occur
40 * within 10 seconds or the connection is error'd out.
41 *
42 * We work off the IP address and/or reverse DNS. The IP address is
43 * checked first, followed by the IP address at various levels of granularity,
44 * followed by the full domain name and domain names at various levels of
45 * granularity.
46 *
47 * /etc/hammer2/remote/<name>.pub - Contains a public key
48 * /etc/hammer2/remote/<name>.none - Indicates no encryption (empty file)
49 * (e.g. localhost.none).
50 *
51 * We first attempt to locate a public key file based on the peer address or
52 * peer FQDN.
53 *
54 * <name>.none - No further negotiation is needed. We simply return.
55 * All communication proceeds without encryption.
56 * No public key handshake occurs in this situation.
57 * (both ends must match).
58 *
59 * <name>.pub - We have located the public key for the peer. Both
60 * sides transmit a block encrypted with their private
61 * keys and the peer's public key.
62 *
63 * Both sides receive a block and decrypt it.
64 *
65 * Both sides formulate a reply using the decrypted
66 * block and transmit it.
67 *
68 * communication proceeds with the negotiated session
69 * key (typically AES-256-CBC).
70 *
71 * If we fail to locate the appropriate file and no floating.db exists the
72 * connection is terminated without further action.
73 *
74 * If floating.db exists the connection proceeds with a floating negotiation.
75 */
76typedef union {
77 struct sockaddr sa;
78 struct sockaddr_in sa_in;
79 struct sockaddr_in6 sa_in6;
80} sockaddr_any_t;
81
82void
83hammer2_crypto_negotiate(hammer2_iocom_t *iocom)
84{
85 sockaddr_any_t sa;
86 socklen_t salen = sizeof(sa);
87 char peername[128];
88 char realname[128];
89 hammer2_handshake_t handtx;
90 hammer2_handshake_t handrx;
5cf97ec5
MD
91 char buf1[sizeof(handtx)];
92 char buf2[sizeof(handtx)];
62efe6ec
MD
93 char *ptr;
94 char *path;
95 struct stat st;
96 FILE *fp;
97 RSA *keys[3] = { NULL, NULL, NULL };
98 size_t i;
99 size_t blksize;
100 size_t blkmask;
101 ssize_t n;
102 int fd;
103
104 /*
105 * Get the peer IP address for the connection as a string.
106 */
107 if (getpeername(iocom->sock_fd, &sa.sa, &salen) < 0) {
108 iocom->ioq_rx.error = HAMMER2_IOQ_ERROR_NOPEER;
109 iocom->flags |= HAMMER2_IOCOMF_EOF;
110 if (DebugOpt)
111 fprintf(stderr, "accept: getpeername() failed\n");
112 goto done;
113 }
114 if (getnameinfo(&sa.sa, salen, peername, sizeof(peername),
115 NULL, 0, NI_NUMERICHOST) < 0) {
116 iocom->ioq_rx.error = HAMMER2_IOQ_ERROR_NOPEER;
117 iocom->flags |= HAMMER2_IOCOMF_EOF;
118 if (DebugOpt)
119 fprintf(stderr, "accept: cannot decode sockaddr\n");
120 goto done;
121 }
122 if (DebugOpt) {
123 if (realhostname_sa(realname, sizeof(realname),
124 &sa.sa, salen) == HOSTNAME_FOUND) {
125 fprintf(stderr, "accept from %s (%s)\n",
126 peername, realname);
127 } else {
128 fprintf(stderr, "accept from %s\n", peername);
129 }
130 }
131
132 /*
133 * Find the remote host's public key
9b8b748f
MD
134 *
135 * If the link is not to be encrypted (<ip>.none located) we shortcut
136 * the handshake entirely. No buffers are exchanged.
62efe6ec
MD
137 */
138 asprintf(&path, "%s/%s.pub", HAMMER2_PATH_REMOTE, peername);
139 if ((fp = fopen(path, "r")) == NULL) {
140 free(path);
141 asprintf(&path, "%s/%s.none",
142 HAMMER2_PATH_REMOTE, peername);
143 if (stat(path, &st) < 0) {
144 iocom->ioq_rx.error = HAMMER2_IOQ_ERROR_NORKEY;
145 iocom->flags |= HAMMER2_IOCOMF_EOF;
146 if (DebugOpt)
147 fprintf(stderr, "auth failure: unknown host\n");
148 goto done;
149 }
150 if (DebugOpt)
151 fprintf(stderr, "auth succeeded, unencrypted link\n");
9b8b748f 152 goto done;
62efe6ec
MD
153 }
154 if (fp) {
155 keys[0] = PEM_read_RSA_PUBKEY(fp, NULL, NULL, NULL);
156 fclose(fp);
157 if (keys[0] == NULL) {
158 iocom->ioq_rx.error = HAMMER2_IOQ_ERROR_KEYFMT;
159 iocom->flags |= HAMMER2_IOCOMF_EOF;
160 if (DebugOpt)
161 fprintf(stderr,
162 "auth failure: bad key format\n");
163 goto done;
164 }
165 }
166
167 /*
168 * Get our public and private keys
169 */
170 free(path);
171 asprintf(&path, HAMMER2_DEFAULT_DIR "/rsa.pub");
172 if ((fp = fopen(path, "r")) == NULL) {
173 iocom->ioq_rx.error = HAMMER2_IOQ_ERROR_NOLKEY;
174 iocom->flags |= HAMMER2_IOCOMF_EOF;
175 goto done;
176 }
177 keys[1] = PEM_read_RSA_PUBKEY(fp, NULL, NULL, NULL);
178 fclose(fp);
179 if (keys[1] == NULL) {
180 iocom->ioq_rx.error = HAMMER2_IOQ_ERROR_KEYFMT;
181 iocom->flags |= HAMMER2_IOCOMF_EOF;
182 if (DebugOpt)
183 fprintf(stderr, "auth failure: bad host key format\n");
184 goto done;
185 }
186
187 free(path);
188 asprintf(&path, HAMMER2_DEFAULT_DIR "/rsa.prv");
189 if ((fp = fopen(path, "r")) == NULL) {
190 iocom->ioq_rx.error = HAMMER2_IOQ_ERROR_NOLKEY;
191 iocom->flags |= HAMMER2_IOCOMF_EOF;
192 if (DebugOpt)
193 fprintf(stderr, "auth failure: bad host key format\n");
194 goto done;
195 }
196 keys[2] = PEM_read_RSAPrivateKey(fp, NULL, NULL, NULL);
197 fclose(fp);
198 if (keys[2] == NULL) {
199 iocom->ioq_rx.error = HAMMER2_IOQ_ERROR_KEYFMT;
200 iocom->flags |= HAMMER2_IOCOMF_EOF;
201 if (DebugOpt)
202 fprintf(stderr, "auth failure: bad host key format\n");
203 goto done;
204 }
205 free(path);
206 path = NULL;
207
208 /*
209 * public key encrypt/decrypt block size.
210 */
211 if (keys[0]) {
212 blksize = (size_t)RSA_size(keys[0]);
213 if (blksize != (size_t)RSA_size(keys[1]) ||
214 blksize != (size_t)RSA_size(keys[2]) ||
215 sizeof(handtx) % blksize != 0) {
216 iocom->ioq_rx.error = HAMMER2_IOQ_ERROR_KEYFMT;
217 iocom->flags |= HAMMER2_IOCOMF_EOF;
218 if (DebugOpt)
219 fprintf(stderr, "auth failure: "
220 "key size mismatch\n");
221 goto done;
222 }
223 } else {
224 blksize = sizeof(handtx);
225 }
226 blkmask = blksize - 1;
227
228 bzero(&handrx, sizeof(handrx));
229 bzero(&handtx, sizeof(handtx));
230
231 /*
232 * Fill all unused fields (particular all junk fields) with random
233 * data, and also set the session key.
234 */
235 fd = open("/dev/urandom", O_RDONLY);
236 if (fd < 0 ||
237 fstat(fd, &st) < 0 || /* something wrong */
238 S_ISREG(st.st_mode) || /* supposed to be a RNG dev! */
239 read(fd, &handtx, sizeof(handtx)) != sizeof(handtx)) {
240urandfail:
241 if (fd >= 0)
242 close(fd);
243 iocom->ioq_rx.error = HAMMER2_IOQ_ERROR_BADURANDOM;
244 iocom->flags |= HAMMER2_IOCOMF_EOF;
245 if (DebugOpt)
246 fprintf(stderr, "auth failure: bad rng\n");
247 goto done;
248 }
249 if (bcmp(&handrx, &handtx, sizeof(handtx)) == 0)
250 goto urandfail; /* read all zeros */
251 close(fd);
5cf97ec5 252 /* ERR_load_crypto_strings(); openssl debugging */
62efe6ec
MD
253
254 /*
255 * Handshake with the remote.
256 *
257 * Encrypt with my private and remote's public
258 * Decrypt with my private and remote's public
259 *
260 * When encrypting we have to make sure our buffer fits within the
261 * modulus, which typically requires bit 7 o the first byte to be
262 * zero. To be safe make sure that bit 7 and bit 6 is zero.
263 */
264 snprintf(handtx.quickmsg, sizeof(handtx.quickmsg), "Testing 1 2 3");
265 handtx.magic = HAMMER2_MSGHDR_MAGIC;
266 handtx.version = 1;
267 handtx.flags = 0;
268 assert(sizeof(handtx.verf) * 4 == sizeof(handtx.sess));
269 bzero(handtx.verf, sizeof(handtx.verf));
270
271 handtx.pad1[0] &= 0x3f; /* message must fit within modulus */
272 handtx.pad2[0] &= 0x3f; /* message must fit within modulus */
273
274 for (i = 0; i < sizeof(handtx.sess); ++i)
275 handtx.verf[i / 4] ^= handtx.sess[i];
276
277 /*
278 * Write handshake buffer to remote
279 */
280 for (i = 0; i < sizeof(handtx); i += blksize) {
281 ptr = (char *)&handtx + i;
282 if (keys[0]) {
283 /*
284 * Since we are double-encrypting we have to make
285 * sure that the result of the first stage does
286 * not blow out the modulus for the second stage.
287 *
288 * The pointer is pointing to the pad*[] area so
289 * we can mess with that until the first stage
290 * is legal.
291 */
292 do {
293 ++*(int *)(ptr + 4);
5cf97ec5 294 if (RSA_private_encrypt(blksize, ptr, buf1,
62efe6ec
MD
295 keys[2], RSA_NO_PADDING) < 0) {
296 iocom->ioq_rx.error =
297 HAMMER2_IOQ_ERROR_KEYXCHGFAIL;
298 }
5cf97ec5 299 } while (buf1[0] & 0xC0);
62efe6ec 300
5cf97ec5 301 if (RSA_public_encrypt(blksize, buf1, buf2,
62efe6ec
MD
302 keys[0], RSA_NO_PADDING) < 0) {
303 iocom->ioq_rx.error =
304 HAMMER2_IOQ_ERROR_KEYXCHGFAIL;
305 }
306 }
5cf97ec5 307 if (write(iocom->sock_fd, buf2, blksize) != (ssize_t)blksize) {
62efe6ec
MD
308 fprintf(stderr, "WRITE ERROR\n");
309 }
310 }
311 if (iocom->ioq_rx.error) {
312 iocom->flags |= HAMMER2_IOCOMF_EOF;
313 if (DebugOpt)
314 fprintf(stderr, "auth failure: key exchange failure "
315 "during encryption\n");
316 goto done;
317 }
318
319 /*
320 * Read handshake buffer from remote
321 */
322 i = 0;
323 while (i < sizeof(handrx)) {
324 ptr = (char *)&handrx + i;
325 n = read(iocom->sock_fd, ptr, blksize - (i & blkmask));
326 if (n <= 0)
327 break;
328 ptr -= (i & blkmask);
329 i += n;
330 if (keys[0] && (i & blkmask) == 0) {
5cf97ec5 331 if (RSA_private_decrypt(blksize, ptr, buf1,
62efe6ec
MD
332 keys[2], RSA_NO_PADDING) < 0)
333 iocom->ioq_rx.error =
334 HAMMER2_IOQ_ERROR_KEYXCHGFAIL;
5cf97ec5 335 if (RSA_public_decrypt(blksize, buf1, ptr,
62efe6ec
MD
336 keys[0], RSA_NO_PADDING) < 0)
337 iocom->ioq_rx.error =
338 HAMMER2_IOQ_ERROR_KEYXCHGFAIL;
339 }
340 }
341 if (iocom->ioq_rx.error) {
342 iocom->flags |= HAMMER2_IOCOMF_EOF;
343 if (DebugOpt)
344 fprintf(stderr, "auth failure: key exchange failure "
345 "during decryption\n");
346 goto done;
347 }
348
349 /*
350 * Validate the received data. Try to make this a constant-time
351 * algorithm.
352 */
353 if (i != sizeof(handrx)) {
354keyxchgfail:
355 iocom->ioq_rx.error = HAMMER2_IOQ_ERROR_KEYXCHGFAIL;
356 iocom->flags |= HAMMER2_IOCOMF_EOF;
357 if (DebugOpt)
358 fprintf(stderr, "auth failure: key exchange failure\n");
359 goto done;
360 }
361
362 if (handrx.magic == HAMMER2_MSGHDR_MAGIC_REV) {
363 handrx.version = bswap16(handrx.version);
364 handrx.flags = bswap32(handrx.flags);
365 }
366 for (i = 0; i < sizeof(handrx.sess); ++i)
367 handrx.verf[i / 4] ^= handrx.sess[i];
368 n = 0;
369 for (i = 0; i < sizeof(handrx.verf); ++i)
370 n += handrx.verf[i];
371 if (handrx.version != 1)
372 ++n;
373 if (n != 0)
374 goto keyxchgfail;
375
5cf97ec5
MD
376 /*
377 * Calculate the session key and initialize the iv[].
378 */
379 assert(HAMMER2_AES_KEY_SIZE * 2 == sizeof(handrx.sess));
380 for (i = 0; i < HAMMER2_AES_KEY_SIZE; ++i) {
381 iocom->sess[i] = handrx.sess[i] ^ handtx.sess[i];
382 iocom->ioq_rx.iv[i] = handrx.sess[HAMMER2_AES_KEY_SIZE + i] ^
383 handtx.sess[HAMMER2_AES_KEY_SIZE + i];
384 iocom->ioq_tx.iv[i] = handrx.sess[HAMMER2_AES_KEY_SIZE + i] ^
385 handtx.sess[HAMMER2_AES_KEY_SIZE + i];
62efe6ec 386 }
5cf97ec5
MD
387 printf("sess: ");
388 for (i = 0; i < HAMMER2_AES_KEY_SIZE; ++i)
389 printf("%02x", (unsigned char)iocom->sess[i]);
390 printf("\n");
391 printf("iv: ");
392 for (i = 0; i < HAMMER2_AES_KEY_SIZE; ++i)
393 printf("%02x", (unsigned char)iocom->ioq_rx.iv[i]);
394 printf("\n");
395
396 EVP_CIPHER_CTX_init(&iocom->ioq_rx.ctx);
397 EVP_DecryptInit_ex(&iocom->ioq_rx.ctx, HAMMER2_AES_TYPE_EVP, NULL,
398 iocom->sess, iocom->ioq_rx.iv);
399 EVP_CIPHER_CTX_set_padding(&iocom->ioq_rx.ctx, 0);
400
401 EVP_CIPHER_CTX_init(&iocom->ioq_tx.ctx);
402 EVP_EncryptInit_ex(&iocom->ioq_tx.ctx, HAMMER2_AES_TYPE_EVP, NULL,
403 iocom->sess, iocom->ioq_tx.iv);
404 EVP_CIPHER_CTX_set_padding(&iocom->ioq_tx.ctx, 0);
405
406 iocom->flags |= HAMMER2_IOCOMF_CRYPTED;
407
408 if (DebugOpt)
409 fprintf(stderr, "auth success: %s\n", handrx.quickmsg);
62efe6ec
MD
410done:
411 if (path)
412 free(path);
413 if (keys[0])
414 RSA_free(keys[0]);
415 if (keys[1])
416 RSA_free(keys[1]);
417 if (keys[1])
418 RSA_free(keys[2]);
419}
5cf97ec5
MD
420
421/*
422 * Decrypt pending data in the ioq's fifo. The data is decrypted in-place.
423 */
424void
425hammer2_crypto_decrypt(hammer2_iocom_t *iocom, hammer2_ioq_t *ioq)
426{
427 int p_len;
428 int n;
429 int i;
430 char buf[512];
431
432 if ((iocom->flags & HAMMER2_IOCOMF_CRYPTED) == 0)
433 return;
434 p_len = ioq->fifo_end - ioq->fifo_cdx;
435 p_len &= ~HAMMER2_AES_KEY_MASK;
436 if (p_len == 0)
437 return;
438 for (i = 0; i < p_len; i += n) {
439 n = (p_len - i > (int)sizeof(buf)) ?
440 (int)sizeof(buf) : p_len - i;
441 bcopy(ioq->buf + ioq->fifo_cdx + i, buf, n);
442 EVP_DecryptUpdate(&ioq->ctx,
443 ioq->buf + ioq->fifo_cdx + i, &n,
444 buf, n);
445 }
446 ioq->fifo_cdx += p_len;
447}
448
449/*
450 * Decrypt data in the message's auxilary buffer. The data is decrypted
451 * in-place.
452 */
453void
454hammer2_crypto_decrypt_aux(hammer2_iocom_t *iocom, hammer2_ioq_t *ioq,
455 hammer2_msg_t *msg, int already)
456{
457 int p_len;
458 int n;
459 int i;
460 char buf[512];
461
462 if ((iocom->flags & HAMMER2_IOCOMF_CRYPTED) == 0)
463 return;
464 p_len = msg->aux_size;
465 assert((p_len & HAMMER2_AES_KEY_MASK) == 0);
466 if (p_len == 0)
467 return;
468 i = already;
469 while (i < p_len) {
470 n = (p_len - i > (int)sizeof(buf)) ?
471 (int)sizeof(buf) : p_len - i;
472 bcopy(msg->aux_data + i, buf, n);
473 EVP_DecryptUpdate(&ioq->ctx,
474 msg->aux_data + i, &n,
475 buf, n);
476 i += n;
477 }
478#if 0
479 EVP_DecryptUpdate(&iocom->ioq_rx.ctx,
480 msg->aux_data, &p_len,
481 msg->aux_data, p_len);
482#endif
483}
484
485int
486hammer2_crypto_encrypt(hammer2_iocom_t *iocom, hammer2_ioq_t *ioq,
487 struct iovec *iov, int n)
488{
489 int p_len;
490 int i;
491 int already;
492 int nmax;
493
494 if ((iocom->flags & HAMMER2_IOCOMF_CRYPTED) == 0)
495 return (n);
496 nmax = sizeof(ioq->buf) - ioq->fifo_cdx; /* max new bytes */
497 already = ioq->fifo_cdx - ioq->fifo_beg; /* already encrypted */
498
499 for (i = 0; i < n; ++i) {
500 p_len = iov[i].iov_len;
501 if (p_len <= already) {
502 already -= p_len;
503 continue;
504 }
505 p_len -= already;
506 if (p_len > nmax)
507 p_len = nmax;
508 EVP_EncryptUpdate(&ioq->ctx,
509 ioq->buf + ioq->fifo_cdx, &p_len,
510 (char *)iov[i].iov_base + already, p_len);
511 ioq->fifo_cdx += p_len;
512 ioq->fifo_end += p_len;
513 nmax -= p_len;
514 if (nmax == 0)
515 break;
516 already = 0;
517 }
518 iov[0].iov_base = ioq->buf + ioq->fifo_beg;
519 iov[0].iov_len = ioq->fifo_cdx - ioq->fifo_beg;
520
521 return (1);
522}
523
524void
525hammer2_crypto_encrypt_wrote(hammer2_iocom_t *iocom, hammer2_ioq_t *ioq,
526 int nact)
527{
528 if ((iocom->flags & HAMMER2_IOCOMF_CRYPTED) == 0)
529 return;
530 if (nact == 0)
531 return;
532 ioq->fifo_beg += nact;
533 if (ioq->fifo_beg == ioq->fifo_end) {
534 ioq->fifo_beg = 0;
535 ioq->fifo_cdx = 0;
536 ioq->fifo_end = 0;
537 }
538}