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