1 /* 2 * Copyright (c) 2008 The DragonFly Project. All rights reserved. 3 * 4 * This code is derived from software contributed to The DragonFly Project 5 * by Matthias Schmidt <matthias@dragonflybsd.org>, University of Marburg, 6 * Germany. 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 * $DragonFly: src/libexec/dma/crypto.c,v 1.4 2008/09/30 17:47:21 swildner Exp $ 36 */ 37 38 #ifdef HAVE_CRYPTO 39 40 #include <openssl/x509.h> 41 #include <openssl/md5.h> 42 #include <openssl/ssl.h> 43 #include <openssl/err.h> 44 #include <openssl/pem.h> 45 #include <openssl/rand.h> 46 47 #include <syslog.h> 48 49 #include "dma.h" 50 51 extern struct config *config; 52 53 static int 54 init_cert_file(struct qitem *it, SSL_CTX *ctx, const char *path) 55 { 56 int error; 57 58 /* Load certificate into ctx */ 59 error = SSL_CTX_use_certificate_chain_file(ctx, path); 60 if (error < 1) { 61 syslog(LOG_ERR, "%s: SSL: Cannot load certificate: %s", 62 it->queueid, path); 63 return (-1); 64 } 65 66 /* Add private key to ctx */ 67 error = SSL_CTX_use_PrivateKey_file(ctx, path, SSL_FILETYPE_PEM); 68 if (error < 1) { 69 syslog(LOG_ERR, "%s: SSL: Cannot load private key: %s", 70 it->queueid, path); 71 return (-1); 72 } 73 74 /* 75 * Check the consistency of a private key with the corresponding 76 * certificate 77 */ 78 error = SSL_CTX_check_private_key(ctx); 79 if (error < 1) { 80 syslog(LOG_ERR, "%s: SSL: Cannot check private key: %s", 81 it->queueid, path); 82 return (-1); 83 } 84 85 return (0); 86 } 87 88 int 89 smtp_init_crypto(struct qitem *it, int fd, int feature) 90 { 91 SSL_CTX *ctx = NULL; 92 SSL_METHOD *meth = NULL; 93 X509 *cert; 94 int error; 95 96 /* Init SSL library */ 97 SSL_library_init(); 98 99 meth = TLSv1_client_method(); 100 101 ctx = SSL_CTX_new(meth); 102 if (ctx == NULL) { 103 syslog(LOG_ERR, "%s: remote delivery deferred:" 104 " SSL init failed: %m", it->queueid); 105 return (2); 106 } 107 108 /* User supplied a certificate */ 109 if (config->certfile != NULL) 110 init_cert_file(it, ctx, config->certfile); 111 112 /* 113 * If the user wants STARTTLS, we have to send EHLO here 114 */ 115 if (((feature & SECURETRANS) != 0) && 116 (feature & STARTTLS) != 0) { 117 /* TLS init phase, disable SSL_write */ 118 config->features |= NOSSL; 119 120 send_remote_command(fd, "EHLO %s", hostname()); 121 if (read_remote(fd, 0, NULL) == 2) { 122 send_remote_command(fd, "STARTTLS"); 123 if (read_remote(fd, 0, NULL) != 2) { 124 syslog(LOG_ERR, "%s: remote delivery failed:" 125 " STARTTLS not available: %m", it->queueid); 126 config->features &= ~NOSSL; 127 return (-1); 128 } 129 } 130 /* End of TLS init phase, enable SSL_write/read */ 131 config->features &= ~NOSSL; 132 } 133 134 config->ssl = SSL_new(ctx); 135 if (config->ssl == NULL) { 136 syslog(LOG_ERR, "%s: remote delivery deferred:" 137 " SSL struct creation failed:", it->queueid); 138 return (2); 139 } 140 141 /* Set ssl to work in client mode */ 142 SSL_set_connect_state(config->ssl); 143 144 /* Set fd for SSL in/output */ 145 error = SSL_set_fd(config->ssl, fd); 146 if (error == 0) { 147 error = SSL_get_error(config->ssl, error); 148 syslog(LOG_ERR, "%s: remote delivery deferred:" 149 " SSL set fd failed (%d): %m", it->queueid, error); 150 return (2); 151 } 152 153 /* Open SSL connection */ 154 error = SSL_connect(config->ssl); 155 if (error < 0) { 156 syslog(LOG_ERR, "%s: remote delivery failed:" 157 " SSL handshake fataly failed: %m", it->queueid); 158 return (-1); 159 } 160 161 /* Get peer certificate */ 162 cert = SSL_get_peer_certificate(config->ssl); 163 if (cert == NULL) { 164 syslog(LOG_ERR, "%s: remote delivery deferred:" 165 " Peer did not provied certificate: %m", it->queueid); 166 } 167 X509_free(cert); 168 169 return (0); 170 } 171 172 /* 173 * hmac_md5() taken out of RFC 2104. This RFC was written by H. Krawczyk, 174 * M. Bellare and R. Canetti. 175 * 176 * text pointer to data stream 177 * text_len length of data stream 178 * key pointer to authentication key 179 * key_len length of authentication key 180 * digest caller digest to be filled int 181 */ 182 void 183 hmac_md5(unsigned char *text, int text_len, unsigned char *key, int key_len, 184 caddr_t digest) 185 { 186 MD5_CTX context; 187 unsigned char k_ipad[65]; /* inner padding - 188 * key XORd with ipad 189 */ 190 unsigned char k_opad[65]; /* outer padding - 191 * key XORd with opad 192 */ 193 unsigned char tk[16]; 194 int i; 195 /* if key is longer than 64 bytes reset it to key=MD5(key) */ 196 if (key_len > 64) { 197 198 MD5_CTX tctx; 199 200 MD5_Init(&tctx); 201 MD5_Update(&tctx, key, key_len); 202 MD5_Final(tk, &tctx); 203 204 key = tk; 205 key_len = 16; 206 } 207 208 /* 209 * the HMAC_MD5 transform looks like: 210 * 211 * MD5(K XOR opad, MD5(K XOR ipad, text)) 212 * 213 * where K is an n byte key 214 * ipad is the byte 0x36 repeated 64 times 215 * 216 * opad is the byte 0x5c repeated 64 times 217 * and text is the data being protected 218 */ 219 220 /* start out by storing key in pads */ 221 bzero( k_ipad, sizeof k_ipad); 222 bzero( k_opad, sizeof k_opad); 223 bcopy( key, k_ipad, key_len); 224 bcopy( key, k_opad, key_len); 225 226 /* XOR key with ipad and opad values */ 227 for (i=0; i<64; i++) { 228 k_ipad[i] ^= 0x36; 229 k_opad[i] ^= 0x5c; 230 } 231 /* 232 * perform inner MD5 233 */ 234 MD5_Init(&context); /* init context for 1st 235 * pass */ 236 MD5_Update(&context, k_ipad, 64); /* start with inner pad */ 237 MD5_Update(&context, text, text_len); /* then text of datagram */ 238 MD5_Final(digest, &context); /* finish up 1st pass */ 239 /* 240 * perform outer MD5 241 */ 242 MD5_Init(&context); /* init context for 2nd 243 * pass */ 244 MD5_Update(&context, k_opad, 64); /* start with outer pad */ 245 MD5_Update(&context, digest, 16); /* then results of 1st 246 * hash */ 247 MD5_Final(digest, &context); /* finish up 2nd pass */ 248 } 249 250 /* 251 * CRAM-MD5 authentication 252 */ 253 int 254 smtp_auth_md5(struct qitem *it, int fd, char *login, char *password) 255 { 256 unsigned char buffer[BUF_SIZE], digest[BUF_SIZE], ascii_digest[33]; 257 char *temp; 258 int len, i; 259 static char hextab[] = "0123456789abcdef"; 260 261 temp = calloc(BUF_SIZE, 1); 262 memset(buffer, 0, sizeof(buffer)); 263 memset(digest, 0, sizeof(digest)); 264 memset(ascii_digest, 0, sizeof(ascii_digest)); 265 266 /* Send AUTH command according to RFC 2554 */ 267 send_remote_command(fd, "AUTH CRAM-MD5"); 268 if (read_remote(fd, sizeof(buffer), buffer) != 3) { 269 syslog(LOG_ERR, "%s: smarthost authentification:" 270 " AUTH cram-md5 not available: %m", it->queueid); 271 /* if cram-md5 is not available */ 272 return (-1); 273 } 274 275 /* skip 3 char status + 1 char space */ 276 base64_decode(buffer + 4, temp); 277 hmac_md5(temp, strlen(temp), password, strlen(password), digest); 278 279 ascii_digest[32] = 0; 280 for (i = 0; i < 16; i++) { 281 ascii_digest[2*i] = hextab[digest[i] >> 4]; 282 ascii_digest[2*i+1] = hextab[digest[i] & 15]; 283 } 284 285 /* prepare answer */ 286 snprintf(buffer, BUF_SIZE, "%s %s", login, ascii_digest); 287 288 /* temp will be allocated inside base64_encode again */ 289 free(temp); 290 /* encode answer */ 291 len = base64_encode(buffer, strlen(buffer), &temp); 292 if (len <= 0) 293 return (-1); 294 295 /* send answer */ 296 send_remote_command(fd, "%s", temp); 297 if (read_remote(fd, 0, NULL) != 2) { 298 syslog(LOG_ERR, "%s: remote delivery deferred:" 299 " AUTH cram-md5 failed: %m", it->queueid); 300 return (-2); 301 } 302 303 return (0); 304 } 305 306 #endif /* HAVE_CRYPTO */ 307