hammer2 - Major crypto API cleanup
[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  * Setup crypto for pthreads
40  */
41 static pthread_mutex_t *crypto_locks;
42 int crypto_count;
43
44 static
45 unsigned long
46 hammer2_crypto_id_callback(void)
47 {
48         return ((unsigned long)(uintptr_t)pthread_self());
49 }
50
51 static
52 void
53 hammer2_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
64 void
65 hammer2_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 /*
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  */
111 typedef union {
112         struct sockaddr sa;
113         struct sockaddr_in sa_in;
114         struct sockaddr_in6 sa_in6;
115 } sockaddr_any_t;
116
117 void
118 hammer2_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;
126         char buf1[sizeof(handtx)];
127         char buf2[sizeof(handtx)];
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
169          *
170          * If the link is not to be encrypted (<ip>.none located) we shortcut
171          * the handshake entirely.  No buffers are exchanged.
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");
187                 goto done;
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)) {
275 urandfail:
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);
287         /* ERR_load_crypto_strings(); openssl debugging */
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);
329                                 if (RSA_private_encrypt(blksize, ptr, buf1,
330                                             keys[2], RSA_NO_PADDING) < 0) {
331                                         iocom->ioq_rx.error =
332                                                 HAMMER2_IOQ_ERROR_KEYXCHGFAIL;
333                                 }
334                         } while (buf1[0] & 0xC0);
335
336                         if (RSA_public_encrypt(blksize, buf1, buf2,
337                                             keys[0], RSA_NO_PADDING) < 0) {
338                                 iocom->ioq_rx.error =
339                                         HAMMER2_IOQ_ERROR_KEYXCHGFAIL;
340                         }
341                 }
342                 if (write(iocom->sock_fd, buf2, blksize) != (ssize_t)blksize) {
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) {
366                         if (RSA_private_decrypt(blksize, ptr, buf1,
367                                            keys[2], RSA_NO_PADDING) < 0)
368                                 iocom->ioq_rx.error =
369                                                 HAMMER2_IOQ_ERROR_KEYXCHGFAIL;
370                         if (RSA_public_decrypt(blksize, buf1, ptr,
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)) {
389 keyxchgfail:
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
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];
421         }
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);
445 done:
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 }
455
456 /*
457  * Decrypt pending data in the ioq's fifo.  The data is decrypted in-place.
458  */
459 void
460 hammer2_crypto_decrypt(hammer2_iocom_t *iocom __unused, hammer2_ioq_t *ioq)
461 {
462         int p_len;
463         int n;
464         int i;
465         char buf[512];
466
467         p_len = ioq->fifo_end - ioq->fifo_cdn;
468         p_len &= ~HAMMER2_AES_KEY_MASK;
469
470         if (p_len == 0)
471                 return;
472         for (i = 0; i < p_len; i += n) {
473                 n = (p_len - i > (int)sizeof(buf)) ?
474                         (int)sizeof(buf) : p_len - i;
475                 bcopy(ioq->buf + ioq->fifo_cdx + i, buf, n);
476                 EVP_DecryptUpdate(&ioq->ctx,
477                                   ioq->buf + ioq->fifo_cdx + i, &n,
478                                   buf, n);
479         }
480         ioq->fifo_cdx += p_len;
481         ioq->fifo_cdn += p_len;
482 }
483
484 /*
485  * *nactp is set to the number of ORIGINAL bytes consumed by the encrypter.
486  * The FIFO may contain more data.
487  */
488 int
489 hammer2_crypto_encrypt(hammer2_iocom_t *iocom __unused, hammer2_ioq_t *ioq,
490                        struct iovec *iov, int n, size_t *nactp)
491 {
492         int p_len;
493         int i;
494         size_t nmax;
495
496         nmax = sizeof(ioq->buf) - ioq->fifo_end;        /* max new bytes */
497
498         *nactp = 0;
499         for (i = 0; i < n && nmax; ++i) {
500                 p_len = iov[i].iov_len;
501                 assert((p_len & HAMMER2_AES_KEY_MASK) == 0);
502                 if ((size_t)p_len > nmax)
503                         p_len = (int)nmax;
504                 *nactp += (size_t)p_len;        /* plaintext count */
505                 EVP_EncryptUpdate(&ioq->ctx,
506                                   ioq->buf + ioq->fifo_cdx, &p_len,
507                                   (char *)iov[i].iov_base, p_len);
508                 assert((size_t)p_len == iov[i].iov_len);
509                 ioq->fifo_cdx += (size_t)p_len; /* crypted count */
510                 ioq->fifo_cdn += (size_t)p_len; /* crypted count */
511                 ioq->fifo_end += (size_t)p_len;
512                 nmax -= (size_t)p_len;
513                 if (nmax == 0)
514                         break;
515         }
516         iov[0].iov_base = ioq->buf + ioq->fifo_beg;
517         iov[0].iov_len = ioq->fifo_cdx - ioq->fifo_beg;
518
519         return (1);
520 }