Merge branches 'hammer2' and 'master' of ssh://crater.dragonflybsd.org/repository...
[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         asprintf(&path, "%s/%s.pub", HAMMER2_PATH_REMOTE, peername);
136         if ((fp = fopen(path, "r")) == NULL) {
137                 free(path);
138                 asprintf(&path, "%s/%s.none",
139                          HAMMER2_PATH_REMOTE, peername);
140                 if (stat(path, &st) < 0) {
141                         iocom->ioq_rx.error = HAMMER2_IOQ_ERROR_NORKEY;
142                         iocom->flags |= HAMMER2_IOCOMF_EOF;
143                         if (DebugOpt)
144                                 fprintf(stderr, "auth failure: unknown host\n");
145                         goto done;
146                 }
147                 if (DebugOpt)
148                         fprintf(stderr, "auth succeeded, unencrypted link\n");
149         }
150         if (fp) {
151                 keys[0] = PEM_read_RSA_PUBKEY(fp, NULL, NULL, NULL);
152                 fclose(fp);
153                 if (keys[0] == NULL) {
154                         iocom->ioq_rx.error = HAMMER2_IOQ_ERROR_KEYFMT;
155                         iocom->flags |= HAMMER2_IOCOMF_EOF;
156                         if (DebugOpt)
157                                 fprintf(stderr,
158                                         "auth failure: bad key format\n");
159                         goto done;
160                 }
161         }
162
163         /*
164          * Get our public and private keys
165          */
166         free(path);
167         asprintf(&path, HAMMER2_DEFAULT_DIR "/rsa.pub");
168         if ((fp = fopen(path, "r")) == NULL) {
169                 iocom->ioq_rx.error = HAMMER2_IOQ_ERROR_NOLKEY;
170                 iocom->flags |= HAMMER2_IOCOMF_EOF;
171                 goto done;
172         }
173         keys[1] = PEM_read_RSA_PUBKEY(fp, NULL, NULL, NULL);
174         fclose(fp);
175         if (keys[1] == NULL) {
176                 iocom->ioq_rx.error = HAMMER2_IOQ_ERROR_KEYFMT;
177                 iocom->flags |= HAMMER2_IOCOMF_EOF;
178                 if (DebugOpt)
179                         fprintf(stderr, "auth failure: bad host key format\n");
180                 goto done;
181         }
182
183         free(path);
184         asprintf(&path, HAMMER2_DEFAULT_DIR "/rsa.prv");
185         if ((fp = fopen(path, "r")) == NULL) {
186                 iocom->ioq_rx.error = HAMMER2_IOQ_ERROR_NOLKEY;
187                 iocom->flags |= HAMMER2_IOCOMF_EOF;
188                 if (DebugOpt)
189                         fprintf(stderr, "auth failure: bad host key format\n");
190                 goto done;
191         }
192         keys[2] = PEM_read_RSAPrivateKey(fp, NULL, NULL, NULL);
193         fclose(fp);
194         if (keys[2] == NULL) {
195                 iocom->ioq_rx.error = HAMMER2_IOQ_ERROR_KEYFMT;
196                 iocom->flags |= HAMMER2_IOCOMF_EOF;
197                 if (DebugOpt)
198                         fprintf(stderr, "auth failure: bad host key format\n");
199                 goto done;
200         }
201         free(path);
202         path = NULL;
203
204         /*
205          * public key encrypt/decrypt block size.
206          */
207         if (keys[0]) {
208                 blksize = (size_t)RSA_size(keys[0]);
209                 if (blksize != (size_t)RSA_size(keys[1]) ||
210                     blksize != (size_t)RSA_size(keys[2]) ||
211                     sizeof(handtx) % blksize != 0) {
212                         iocom->ioq_rx.error = HAMMER2_IOQ_ERROR_KEYFMT;
213                         iocom->flags |= HAMMER2_IOCOMF_EOF;
214                         if (DebugOpt)
215                                 fprintf(stderr, "auth failure: "
216                                                 "key size mismatch\n");
217                         goto done;
218                 }
219         } else {
220                 blksize = sizeof(handtx);
221         }
222         blkmask = blksize - 1;
223
224         bzero(&handrx, sizeof(handrx));
225         bzero(&handtx, sizeof(handtx));
226
227         /*
228          * Fill all unused fields (particular all junk fields) with random
229          * data, and also set the session key.
230          */
231         fd = open("/dev/urandom", O_RDONLY);
232         if (fd < 0 ||
233             fstat(fd, &st) < 0 ||       /* something wrong */
234             S_ISREG(st.st_mode) ||      /* supposed to be a RNG dev! */
235             read(fd, &handtx, sizeof(handtx)) != sizeof(handtx)) {
236 urandfail:
237                 if (fd >= 0)
238                         close(fd);
239                 iocom->ioq_rx.error = HAMMER2_IOQ_ERROR_BADURANDOM;
240                 iocom->flags |= HAMMER2_IOCOMF_EOF;
241                 if (DebugOpt)
242                         fprintf(stderr, "auth failure: bad rng\n");
243                 goto done;
244         }
245         if (bcmp(&handrx, &handtx, sizeof(handtx)) == 0)
246                 goto urandfail;                 /* read all zeros */
247         close(fd);
248         /* ERR_load_crypto_strings(); openssl debugging */
249
250         /*
251          * Handshake with the remote.
252          *
253          *      Encrypt with my private and remote's public
254          *      Decrypt with my private and remote's public
255          *
256          * When encrypting we have to make sure our buffer fits within the
257          * modulus, which typically requires bit 7 o the first byte to be
258          * zero.  To be safe make sure that bit 7 and bit 6 is zero.
259          */
260         snprintf(handtx.quickmsg, sizeof(handtx.quickmsg), "Testing 1 2 3");
261         handtx.magic = HAMMER2_MSGHDR_MAGIC;
262         handtx.version = 1;
263         handtx.flags = 0;
264         assert(sizeof(handtx.verf) * 4 == sizeof(handtx.sess));
265         bzero(handtx.verf, sizeof(handtx.verf));
266
267         handtx.pad1[0] &= 0x3f; /* message must fit within modulus */
268         handtx.pad2[0] &= 0x3f; /* message must fit within modulus */
269
270         for (i = 0; i < sizeof(handtx.sess); ++i)
271                 handtx.verf[i / 4] ^= handtx.sess[i];
272
273         /*
274          * Write handshake buffer to remote
275          */
276         for (i = 0; i < sizeof(handtx); i += blksize) {
277                 ptr = (char *)&handtx + i;
278                 if (keys[0]) {
279                         /*
280                          * Since we are double-encrypting we have to make
281                          * sure that the result of the first stage does
282                          * not blow out the modulus for the second stage.
283                          *
284                          * The pointer is pointing to the pad*[] area so
285                          * we can mess with that until the first stage
286                          * is legal.
287                          */
288                         do {
289                                 ++*(int *)(ptr + 4);
290                                 if (RSA_private_encrypt(blksize, ptr, buf1,
291                                             keys[2], RSA_NO_PADDING) < 0) {
292                                         iocom->ioq_rx.error =
293                                                 HAMMER2_IOQ_ERROR_KEYXCHGFAIL;
294                                 }
295                         } while (buf1[0] & 0xC0);
296
297                         if (RSA_public_encrypt(blksize, buf1, buf2,
298                                             keys[0], RSA_NO_PADDING) < 0) {
299                                 iocom->ioq_rx.error =
300                                         HAMMER2_IOQ_ERROR_KEYXCHGFAIL;
301                         }
302                 }
303                 if (write(iocom->sock_fd, buf2, blksize) != (ssize_t)blksize) {
304                         fprintf(stderr, "WRITE ERROR\n");
305                 }
306         }
307         if (iocom->ioq_rx.error) {
308                 iocom->flags |= HAMMER2_IOCOMF_EOF;
309                 if (DebugOpt)
310                         fprintf(stderr, "auth failure: key exchange failure "
311                                         "during encryption\n");
312                 goto done;
313         }
314
315         /*
316          * Read handshake buffer from remote
317          */
318         i = 0;
319         while (i < sizeof(handrx)) {
320                 ptr = (char *)&handrx + i;
321                 n = read(iocom->sock_fd, ptr, blksize - (i & blkmask));
322                 if (n <= 0)
323                         break;
324                 ptr -= (i & blkmask);
325                 i += n;
326                 if (keys[0] && (i & blkmask) == 0) {
327                         if (RSA_private_decrypt(blksize, ptr, buf1,
328                                            keys[2], RSA_NO_PADDING) < 0)
329                                 iocom->ioq_rx.error =
330                                                 HAMMER2_IOQ_ERROR_KEYXCHGFAIL;
331                         if (RSA_public_decrypt(blksize, buf1, ptr,
332                                            keys[0], RSA_NO_PADDING) < 0)
333                                 iocom->ioq_rx.error =
334                                                 HAMMER2_IOQ_ERROR_KEYXCHGFAIL;
335                 }
336         }
337         if (iocom->ioq_rx.error) {
338                 iocom->flags |= HAMMER2_IOCOMF_EOF;
339                 if (DebugOpt)
340                         fprintf(stderr, "auth failure: key exchange failure "
341                                         "during decryption\n");
342                 goto done;
343         }
344
345         /*
346          * Validate the received data.  Try to make this a constant-time
347          * algorithm.
348          */
349         if (i != sizeof(handrx)) {
350 keyxchgfail:
351                 iocom->ioq_rx.error = HAMMER2_IOQ_ERROR_KEYXCHGFAIL;
352                 iocom->flags |= HAMMER2_IOCOMF_EOF;
353                 if (DebugOpt)
354                         fprintf(stderr, "auth failure: key exchange failure\n");
355                 goto done;
356         }
357
358         if (handrx.magic == HAMMER2_MSGHDR_MAGIC_REV) {
359                 handrx.version = bswap16(handrx.version);
360                 handrx.flags = bswap32(handrx.flags);
361         }
362         for (i = 0; i < sizeof(handrx.sess); ++i)
363                 handrx.verf[i / 4] ^= handrx.sess[i];
364         n = 0;
365         for (i = 0; i < sizeof(handrx.verf); ++i)
366                 n += handrx.verf[i];
367         if (handrx.version != 1)
368                 ++n;
369         if (n != 0)
370                 goto keyxchgfail;
371
372         /*
373          * Calculate the session key and initialize the iv[].
374          */
375         assert(HAMMER2_AES_KEY_SIZE * 2 == sizeof(handrx.sess));
376         for (i = 0; i < HAMMER2_AES_KEY_SIZE; ++i) {
377                 iocom->sess[i] = handrx.sess[i] ^ handtx.sess[i];
378                 iocom->ioq_rx.iv[i] = handrx.sess[HAMMER2_AES_KEY_SIZE + i] ^
379                                       handtx.sess[HAMMER2_AES_KEY_SIZE + i];
380                 iocom->ioq_tx.iv[i] = handrx.sess[HAMMER2_AES_KEY_SIZE + i] ^
381                                       handtx.sess[HAMMER2_AES_KEY_SIZE + i];
382         }
383         printf("sess: ");
384         for (i = 0; i < HAMMER2_AES_KEY_SIZE; ++i)
385                 printf("%02x", (unsigned char)iocom->sess[i]);
386         printf("\n");
387         printf("iv: ");
388         for (i = 0; i < HAMMER2_AES_KEY_SIZE; ++i)
389                 printf("%02x", (unsigned char)iocom->ioq_rx.iv[i]);
390         printf("\n");
391
392         EVP_CIPHER_CTX_init(&iocom->ioq_rx.ctx);
393         EVP_DecryptInit_ex(&iocom->ioq_rx.ctx, HAMMER2_AES_TYPE_EVP, NULL,
394                            iocom->sess, iocom->ioq_rx.iv);
395         EVP_CIPHER_CTX_set_padding(&iocom->ioq_rx.ctx, 0);
396
397         EVP_CIPHER_CTX_init(&iocom->ioq_tx.ctx);
398         EVP_EncryptInit_ex(&iocom->ioq_tx.ctx, HAMMER2_AES_TYPE_EVP, NULL,
399                            iocom->sess, iocom->ioq_tx.iv);
400         EVP_CIPHER_CTX_set_padding(&iocom->ioq_tx.ctx, 0);
401
402         iocom->flags |= HAMMER2_IOCOMF_CRYPTED;
403
404         if (DebugOpt)
405                 fprintf(stderr, "auth success: %s\n", handrx.quickmsg);
406 done:
407         if (path)
408                 free(path);
409         if (keys[0])
410                 RSA_free(keys[0]);
411         if (keys[1])
412                 RSA_free(keys[1]);
413         if (keys[1])
414                 RSA_free(keys[2]);
415 }
416
417 /*
418  * Decrypt pending data in the ioq's fifo.  The data is decrypted in-place.
419  */
420 void
421 hammer2_crypto_decrypt(hammer2_iocom_t *iocom, hammer2_ioq_t *ioq)
422 {
423         int p_len;
424         int n;
425         int i;
426         char buf[512];
427
428         if ((iocom->flags & HAMMER2_IOCOMF_CRYPTED) == 0)
429                 return;
430         p_len = ioq->fifo_end - ioq->fifo_cdx;
431         p_len &= ~HAMMER2_AES_KEY_MASK;
432         if (p_len == 0)
433                 return;
434         for (i = 0; i < p_len; i += n) {
435                 n = (p_len - i > (int)sizeof(buf)) ?
436                         (int)sizeof(buf) : p_len - i;
437                 bcopy(ioq->buf + ioq->fifo_cdx + i, buf, n);
438                 EVP_DecryptUpdate(&ioq->ctx,
439                                   ioq->buf + ioq->fifo_cdx + i, &n,
440                                   buf, n);
441         }
442         ioq->fifo_cdx += p_len;
443 }
444
445 /*
446  * Decrypt data in the message's auxilary buffer.  The data is decrypted
447  * in-place.
448  */
449 void
450 hammer2_crypto_decrypt_aux(hammer2_iocom_t *iocom, hammer2_ioq_t *ioq,
451                            hammer2_msg_t *msg, int already)
452 {
453         int p_len;
454         int n;
455         int i;
456         char buf[512];
457
458         if ((iocom->flags & HAMMER2_IOCOMF_CRYPTED) == 0)
459                 return;
460         p_len = msg->aux_size;
461         assert((p_len & HAMMER2_AES_KEY_MASK) == 0);
462         if (p_len == 0)
463                 return;
464         i = already;
465         while (i < p_len) {
466                 n = (p_len - i > (int)sizeof(buf)) ?
467                         (int)sizeof(buf) : p_len - i;
468                 bcopy(msg->aux_data + i, buf, n);
469                 EVP_DecryptUpdate(&ioq->ctx,
470                                   msg->aux_data + i, &n,
471                                   buf, n);
472                 i += n;
473         }
474 #if 0
475         EVP_DecryptUpdate(&iocom->ioq_rx.ctx,
476                           msg->aux_data, &p_len,
477                           msg->aux_data, p_len);
478 #endif
479 }
480
481 int
482 hammer2_crypto_encrypt(hammer2_iocom_t *iocom, hammer2_ioq_t *ioq,
483                        struct iovec *iov, int n)
484 {
485         int p_len;
486         int i;
487         int already;
488         int nmax;
489
490         if ((iocom->flags & HAMMER2_IOCOMF_CRYPTED) == 0)
491                 return (n);
492         nmax = sizeof(ioq->buf) - ioq->fifo_cdx;        /* max new bytes */
493         already = ioq->fifo_cdx - ioq->fifo_beg;        /* already encrypted */
494
495         for (i = 0; i < n; ++i) {
496                 p_len = iov[i].iov_len;
497                 if (p_len <= already) {
498                         already -= p_len;
499                         continue;
500                 }
501                 p_len -= already;
502                 if (p_len > nmax)
503                         p_len = nmax;
504                 EVP_EncryptUpdate(&ioq->ctx,
505                                   ioq->buf + ioq->fifo_cdx, &p_len,
506                                   (char *)iov[i].iov_base + already, p_len);
507                 ioq->fifo_cdx += p_len;
508                 ioq->fifo_end += p_len;
509                 nmax -= p_len;
510                 if (nmax == 0)
511                         break;
512                 already = 0;
513         }
514         iov[0].iov_base = ioq->buf + ioq->fifo_beg;
515         iov[0].iov_len = ioq->fifo_cdx - ioq->fifo_beg;
516
517         return (1);
518 }
519
520 void
521 hammer2_crypto_encrypt_wrote(hammer2_iocom_t *iocom, hammer2_ioq_t *ioq,
522                              int nact)
523 {
524         if ((iocom->flags & HAMMER2_IOCOMF_CRYPTED) == 0)
525                 return;
526         if (nact == 0)
527                 return;
528         ioq->fifo_beg += nact;
529         if (ioq->fifo_beg == ioq->fifo_end) {
530                 ioq->fifo_beg = 0;
531                 ioq->fifo_cdx = 0;
532                 ioq->fifo_end = 0;
533         }
534 }