| 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 <openssl/rsa.h> /* public/private key functions */ |
| 37 | #include <openssl/pem.h> /* public/private key file load */ |
| 38 | #include <openssl/err.h> |
| 39 | #include <openssl/evp.h> /* aes_256_cbc functions */ |
| 40 | |
| 41 | /*************************************************************************** |
| 42 | * CRYPTO HANDSHAKE * |
| 43 | *************************************************************************** |
| 44 | * |
| 45 | * The initial public-key exchange is implementing by transmitting a |
| 46 | * 512-byte buffer to the other side in a symmetrical fashion. This |
| 47 | * buffer contains the following: |
| 48 | * |
| 49 | * (1) A random session key. 512 bits is specified. We use aes_256_cbc() |
| 50 | * and initialize the key with the first 256 bits and the iv[] with |
| 51 | * the second. Note that the transmitted and received session |
| 52 | * keys are XOR'd together to create the session key used for |
| 53 | * communications (so even if the verifier is compromised the session |
| 54 | * will still be gobbly gook if the public key has not been completely |
| 55 | * broken). |
| 56 | * |
| 57 | * (2) A verifier to determine that the decode was successful. It encodes |
| 58 | * an XOR of each group of 4 bytes from the session key. |
| 59 | * |
| 60 | * (3) Additional configuration and additional random data. |
| 61 | * |
| 62 | * - The hammer2 message header magic for endian detect |
| 63 | * |
| 64 | * - The hammer2 protocol version. The two sides agree on the |
| 65 | * smaller of the two. |
| 66 | * |
| 67 | * - All unused fields (junk*) are filled with random data. |
| 68 | * |
| 69 | * This structure must be exactly 512 bytes and expects to use 256-byte |
| 70 | * RSA keys. |
| 71 | */ |
| 72 | struct hammer2_handshake { |
| 73 | char pad1[8]; /* 000 */ |
| 74 | uint16_t magic; /* 008 HAMMER2_MSGHDR_MAGIC for endian detect */ |
| 75 | uint16_t version; /* 00A hammer2 protocol version */ |
| 76 | uint32_t flags; /* 00C protocol extension flags */ |
| 77 | uint8_t sess[64]; /* 010 512-bit session key */ |
| 78 | uint8_t verf[16]; /* 050 verifier = ~sess */ |
| 79 | char quickmsg[32]; /* 060 reason for connecting */ |
| 80 | char junk080[128]; /* 080-0FF */ |
| 81 | char pad2[8]; /* 100-107 */ |
| 82 | char junk100[256-8]; /* 108-1FF */ |
| 83 | }; |
| 84 | |
| 85 | typedef struct hammer2_handshake hammer2_handshake_t; |
| 86 | |
| 87 | |
| 88 | #define HAMMER2_CRYPTO_CHUNK_SIZE HAMMER2_MSG_ALIGN |
| 89 | #define HAMMER2_MAX_IV_SIZE 32 |
| 90 | |
| 91 | #define HAMMER2_CRYPTO_GCM_IV_FIXED_SIZE 4 |
| 92 | #define HAMMER2_CRYPTO_GCM_IV_SIZE 12 |
| 93 | #define HAMMER2_CRYPTO_GCM_KEY_SIZE 32 |
| 94 | #define HAMMER2_CRYPTO_GCM_TAG_SIZE 16 |
| 95 | |
| 96 | #define HAMMER2_CRYPTO_ALGO_GCM_IDX 0 |
| 97 | |
| 98 | #define HAMMER2_CRYPTO_ALGO HAMMER2_CRYPTO_ALGO_GCM_IDX |
| 99 | |
| 100 | |
| 101 | |
| 102 | |
| 103 | /*************************************************************************** |
| 104 | * LOW LEVEL MESSAGING * |
| 105 | *************************************************************************** |
| 106 | * |
| 107 | * hammer2_msg - A standalone copy of a message, typically referenced by |
| 108 | * or embedded in other structures, or used with I/O queues. |
| 109 | * |
| 110 | * These structures are strictly temporary, so they do not have to be |
| 111 | * particularly optimized for size. All possible message headers are |
| 112 | * directly embedded (any), and the message may contain a reference |
| 113 | * to allocated auxillary data. The structure is recycled quite often |
| 114 | * by a connection. |
| 115 | * |
| 116 | * This structure is typically not used for storing persistent message |
| 117 | * state (see hammer2_persist for that). |
| 118 | */ |
| 119 | struct hammer2_iocom; |
| 120 | struct hammer2_persist; |
| 121 | struct hammer2_state; |
| 122 | struct hammer2_router; |
| 123 | struct hammer2_msg; |
| 124 | |
| 125 | TAILQ_HEAD(hammer2_state_queue, hammer2_state); |
| 126 | TAILQ_HEAD(hammer2_msg_queue, hammer2_msg); |
| 127 | RB_HEAD(hammer2_state_tree, hammer2_state); |
| 128 | RB_HEAD(hammer2_router_tree, hammer2_router); |
| 129 | |
| 130 | struct h2span_link; |
| 131 | struct h2span_relay; |
| 132 | struct h2span_conn; |
| 133 | |
| 134 | struct hammer2_state { |
| 135 | RB_ENTRY(hammer2_state) rbnode; /* indexed by msgid */ |
| 136 | struct hammer2_iocom *iocom; |
| 137 | struct hammer2_router *router; /* if routed */ |
| 138 | uint32_t txcmd; /* mostly for CMDF flags */ |
| 139 | uint32_t rxcmd; /* mostly for CMDF flags */ |
| 140 | uint64_t msgid; /* {spanid,msgid} uniq */ |
| 141 | int flags; |
| 142 | int error; |
| 143 | struct hammer2_msg *msg; |
| 144 | void (*func)(struct hammer2_msg *); |
| 145 | union { |
| 146 | void *any; |
| 147 | struct h2span_link *link; |
| 148 | struct h2span_conn *conn; |
| 149 | struct h2span_relay *relay; |
| 150 | } any; |
| 151 | }; |
| 152 | |
| 153 | #define HAMMER2_STATE_INSERTED 0x0001 |
| 154 | #define HAMMER2_STATE_DYNAMIC 0x0002 |
| 155 | #define HAMMER2_STATE_NODEID 0x0004 /* manages a node id */ |
| 156 | |
| 157 | struct hammer2_msg { |
| 158 | TAILQ_ENTRY(hammer2_msg) qentry; |
| 159 | struct hammer2_router *router; |
| 160 | struct hammer2_state *state; |
| 161 | size_t hdr_size; |
| 162 | size_t aux_size; |
| 163 | char *aux_data; |
| 164 | hammer2_msg_any_t any; |
| 165 | }; |
| 166 | |
| 167 | typedef struct hammer2_state hammer2_state_t; |
| 168 | typedef struct hammer2_msg hammer2_msg_t; |
| 169 | typedef struct hammer2_msg_queue hammer2_msg_queue_t; |
| 170 | |
| 171 | int hammer2_state_cmp(hammer2_state_t *state1, hammer2_state_t *state2); |
| 172 | RB_PROTOTYPE(hammer2_state_tree, hammer2_state, rbnode, hammer2_state_cmp); |
| 173 | |
| 174 | /* |
| 175 | * hammer2_ioq - An embedded component of hammer2_conn, holds state |
| 176 | * for the buffering and parsing of incoming and outgoing messages. |
| 177 | * |
| 178 | * cdx - beg - processed buffer data, encrypted or decrypted |
| 179 | * end - cdn - unprocessed buffer data not yet encrypted or decrypted |
| 180 | */ |
| 181 | struct hammer2_ioq { |
| 182 | enum { HAMMER2_MSGQ_STATE_HEADER1, |
| 183 | HAMMER2_MSGQ_STATE_HEADER2, |
| 184 | HAMMER2_MSGQ_STATE_AUXDATA1, |
| 185 | HAMMER2_MSGQ_STATE_AUXDATA2, |
| 186 | HAMMER2_MSGQ_STATE_ERROR } state; |
| 187 | size_t fifo_beg; /* buffered data */ |
| 188 | size_t fifo_cdx; /* cdx-beg processed */ |
| 189 | size_t fifo_cdn; /* end-cdn unprocessed */ |
| 190 | size_t fifo_end; |
| 191 | size_t hbytes; /* header size */ |
| 192 | size_t abytes; /* aux_data size */ |
| 193 | int error; |
| 194 | int seq; /* salt sequencer */ |
| 195 | int msgcount; |
| 196 | EVP_CIPHER_CTX ctx; |
| 197 | char iv[HAMMER2_MAX_IV_SIZE]; /* encrypt or decrypt iv[] */ |
| 198 | hammer2_msg_t *msg; |
| 199 | hammer2_msg_queue_t msgq; |
| 200 | char buf[HAMMER2_MSGBUF_SIZE]; /* staging buffer */ |
| 201 | }; |
| 202 | |
| 203 | typedef struct hammer2_ioq hammer2_ioq_t; |
| 204 | |
| 205 | #define HAMMER2_IOQ_ERROR_SYNC 1 /* bad magic / out of sync */ |
| 206 | #define HAMMER2_IOQ_ERROR_EOF 2 /* unexpected EOF */ |
| 207 | #define HAMMER2_IOQ_ERROR_SOCK 3 /* read() error on socket */ |
| 208 | #define HAMMER2_IOQ_ERROR_FIELD 4 /* invalid field */ |
| 209 | #define HAMMER2_IOQ_ERROR_HCRC 5 /* core header crc bad */ |
| 210 | #define HAMMER2_IOQ_ERROR_XCRC 6 /* ext header crc bad */ |
| 211 | #define HAMMER2_IOQ_ERROR_ACRC 7 /* aux data crc bad */ |
| 212 | #define HAMMER2_IOQ_ERROR_STATE 8 /* bad state */ |
| 213 | #define HAMMER2_IOQ_ERROR_NOPEER 9 /* bad socket peer */ |
| 214 | #define HAMMER2_IOQ_ERROR_NORKEY 10 /* no remote keyfile found */ |
| 215 | #define HAMMER2_IOQ_ERROR_NOLKEY 11 /* no local keyfile found */ |
| 216 | #define HAMMER2_IOQ_ERROR_KEYXCHGFAIL 12 /* key exchange failed */ |
| 217 | #define HAMMER2_IOQ_ERROR_KEYFMT 13 /* key file format problem */ |
| 218 | #define HAMMER2_IOQ_ERROR_BADURANDOM 14 /* /dev/urandom is bad */ |
| 219 | #define HAMMER2_IOQ_ERROR_MSGSEQ 15 /* message sequence error */ |
| 220 | #define HAMMER2_IOQ_ERROR_EALREADY 16 /* ignore this message */ |
| 221 | #define HAMMER2_IOQ_ERROR_TRANS 17 /* state transaction issue */ |
| 222 | #define HAMMER2_IOQ_ERROR_IVWRAP 18 /* IVs exhaused */ |
| 223 | #define HAMMER2_IOQ_ERROR_MACFAIL 19 /* MAC of encryption algorithm failed */ |
| 224 | #define HAMMER2_IOQ_ERROR_ALGO 20 /* Misc. encryption algorithm error */ |
| 225 | |
| 226 | #define HAMMER2_IOQ_MAXIOVEC 16 |
| 227 | |
| 228 | /* |
| 229 | * hammer2_router - governs the routing of a message. Passed into |
| 230 | * hammer2_msg_write. |
| 231 | * |
| 232 | * The router is either connected to an iocom (socket) directly, or |
| 233 | * connected to a SPAN transaction (h2span_link structure for outgoing) |
| 234 | * or to a SPAN transaction (h2span_relay structure for incoming). |
| 235 | */ |
| 236 | struct hammer2_router { |
| 237 | RB_ENTRY(hammer2_router) rbnode; /* indexed by target */ |
| 238 | struct hammer2_iocom *iocom; |
| 239 | struct h2span_link *link; /* may be NULL */ |
| 240 | struct h2span_relay *relay; /* may be NULL */ |
| 241 | void (*signal_callback)(struct hammer2_router *); |
| 242 | void (*rcvmsg_callback)(struct hammer2_msg *); |
| 243 | void (*altmsg_callback)(struct hammer2_iocom *); |
| 244 | struct hammer2_state_tree staterd_tree; /* active messages */ |
| 245 | struct hammer2_state_tree statewr_tree; /* active messages */ |
| 246 | hammer2_msg_queue_t txmsgq; /* tx msgq from remote */ |
| 247 | uint64_t target; /* for routing */ |
| 248 | int flags; |
| 249 | int refs; /* refs prevent destruction */ |
| 250 | }; |
| 251 | |
| 252 | #define HAMMER2_ROUTER_CONNECTED 0x0001 /* on global RB tree */ |
| 253 | #define HAMMER2_ROUTER_DELETED 0x0002 /* parent structure destroyed */ |
| 254 | |
| 255 | typedef struct hammer2_router hammer2_router_t; |
| 256 | |
| 257 | int hammer2_router_cmp(hammer2_router_t *router1, hammer2_router_t *router2); |
| 258 | RB_PROTOTYPE(hammer2_router_tree, hammer2_router, rbnode, hammer2_router_cmp); |
| 259 | |
| 260 | /* |
| 261 | * hammer2_iocom - governs a messaging stream connection |
| 262 | */ |
| 263 | struct hammer2_iocom { |
| 264 | hammer2_ioq_t ioq_rx; |
| 265 | hammer2_ioq_t ioq_tx; |
| 266 | hammer2_msg_queue_t freeq; /* free msgs hdr only */ |
| 267 | hammer2_msg_queue_t freeq_aux; /* free msgs w/aux_data */ |
| 268 | int sock_fd; /* comm socket or pipe */ |
| 269 | int alt_fd; /* thread signal, tty, etc */ |
| 270 | int wakeupfds[2]; /* pipe wakes up iocom thread */ |
| 271 | int flags; |
| 272 | int rxmisc; |
| 273 | int txmisc; |
| 274 | struct hammer2_router *router; |
| 275 | pthread_mutex_t mtx; /* mutex for state*tree/rmsgq */ |
| 276 | }; |
| 277 | |
| 278 | typedef struct hammer2_iocom hammer2_iocom_t; |
| 279 | |
| 280 | #define HAMMER2_IOCOMF_EOF 0x00000001 /* EOF or ERROR on desc */ |
| 281 | #define HAMMER2_IOCOMF_RREQ 0x00000002 /* request read-data event */ |
| 282 | #define HAMMER2_IOCOMF_WREQ 0x00000004 /* request write-avail event */ |
| 283 | #define HAMMER2_IOCOMF_RWORK 0x00000008 /* immediate work pending */ |
| 284 | #define HAMMER2_IOCOMF_WWORK 0x00000010 /* immediate work pending */ |
| 285 | #define HAMMER2_IOCOMF_PWORK 0x00000020 /* immediate work pending */ |
| 286 | #define HAMMER2_IOCOMF_ARWORK 0x00000040 /* immediate work pending */ |
| 287 | #define HAMMER2_IOCOMF_AWWORK 0x00000080 /* immediate work pending */ |
| 288 | #define HAMMER2_IOCOMF_SWORK 0x00000100 /* immediate work pending */ |
| 289 | #define HAMMER2_IOCOMF_CRYPTED 0x00000200 /* encrypt enabled */ |
| 290 | |
| 291 | /* |
| 292 | * Crypto algorithm table and related typedefs. |
| 293 | */ |
| 294 | |
| 295 | typedef int (*algo_init_fn)(hammer2_ioq_t *, char *, int, char *, int, int); |
| 296 | typedef int (*algo_enc_fn)(hammer2_ioq_t *, char *, char *, int, int *); |
| 297 | typedef int (*algo_dec_fn)(hammer2_ioq_t *, char *, char *, int, int *); |
| 298 | |
| 299 | struct crypto_algo { |
| 300 | const char *name; |
| 301 | int keylen; |
| 302 | int taglen; |
| 303 | algo_init_fn init; |
| 304 | algo_enc_fn enc_chunk; |
| 305 | algo_dec_fn dec_chunk; |
| 306 | }; |