248764d04a99a2b9012db4dcd60e7170b251a3ed
[dragonfly.git] / sbin / hammer2 / crypto.c
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
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  */
76 typedef union {
77         struct sockaddr sa;
78         struct sockaddr_in sa_in;
79         struct sockaddr_in6 sa_in6;
80 } sockaddr_any_t;
81
82 void
83 hammer2_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;
91         char buf1[sizeof(handtx)];
92         char buf2[sizeof(handtx)];
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
134          *
135          * If the link is not to be encrypted (<ip>.none located) we shortcut
136          * the handshake entirely.  No buffers are exchanged.
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");
152                 goto done;
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)) {
240 urandfail:
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);
252         /* ERR_load_crypto_strings(); openssl debugging */
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);
294                                 if (RSA_private_encrypt(blksize, ptr, buf1,
295                                             keys[2], RSA_NO_PADDING) < 0) {
296                                         iocom->ioq_rx.error =
297                                                 HAMMER2_IOQ_ERROR_KEYXCHGFAIL;
298                                 }
299                         } while (buf1[0] & 0xC0);
300
301                         if (RSA_public_encrypt(blksize, buf1, buf2,
302                                             keys[0], RSA_NO_PADDING) < 0) {
303                                 iocom->ioq_rx.error =
304                                         HAMMER2_IOQ_ERROR_KEYXCHGFAIL;
305                         }
306                 }
307                 if (write(iocom->sock_fd, buf2, blksize) != (ssize_t)blksize) {
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) {
331                         if (RSA_private_decrypt(blksize, ptr, buf1,
332                                            keys[2], RSA_NO_PADDING) < 0)
333                                 iocom->ioq_rx.error =
334                                                 HAMMER2_IOQ_ERROR_KEYXCHGFAIL;
335                         if (RSA_public_decrypt(blksize, buf1, ptr,
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)) {
354 keyxchgfail:
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
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];
386         }
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);
410 done:
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 }
420
421 /*
422  * Decrypt pending data in the ioq's fifo.  The data is decrypted in-place.
423  */
424 void
425 hammer2_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  */
453 void
454 hammer2_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
485 int
486 hammer2_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
524 void
525 hammer2_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 }