1 |
lefcha |
1.1 |
#include <stdio.h> |
2 |
|
|
#include <string.h> |
3 |
|
|
#include <ctype.h> |
4 |
|
|
#include <limits.h> |
5 |
|
|
#include <sys/types.h> |
6 |
|
|
#include <sys/stat.h> |
7 |
|
|
#include <fcntl.h> |
8 |
|
|
|
9 |
|
|
#include "config.h" |
10 |
|
|
#include "imapfilter.h" |
11 |
|
|
#include "pathnames.h" |
12 |
|
|
|
13 |
|
|
#ifdef SSL_TLS |
14 |
|
|
#include <openssl/ssl.h> |
15 |
|
|
#include <openssl/x509.h> |
16 |
|
|
#include <openssl/pem.h> |
17 |
|
|
|
18 |
|
|
|
19 |
|
|
extern unsigned int options; |
20 |
|
|
extern unsigned int flags; |
21 |
|
|
|
22 |
|
|
|
23 |
|
|
int check_cert(X509 * pcert, unsigned char *pmd, unsigned int *pmdlen); |
24 |
|
|
void print_cert(X509 * cert, unsigned char *md, unsigned int *mdlen); |
25 |
|
|
int write_cert(X509 * cert); |
26 |
|
|
int mismatch_cert(void); |
27 |
|
|
|
28 |
|
|
|
29 |
|
|
/* |
30 |
|
|
* Get SSL/TLS certificate check it, maybe ask user about it and act |
31 |
|
|
* accordingly. |
32 |
|
|
*/ |
33 |
|
|
int |
34 |
|
|
get_cert(conn_t * conn) |
35 |
|
|
{ |
36 |
|
|
X509 *cert; |
37 |
|
|
unsigned char md[EVP_MAX_MD_SIZE]; |
38 |
|
|
unsigned int mdlen; |
39 |
|
|
|
40 |
|
|
mdlen = 0; |
41 |
|
|
|
42 |
|
|
if (!(cert = SSL_get_peer_certificate(conn->ssl))) |
43 |
|
|
return ERROR_SSL; |
44 |
|
|
|
45 |
|
|
if (!(X509_digest(cert, EVP_md5(), md, &mdlen))) |
46 |
|
|
return ERROR_SSL; |
47 |
|
|
|
48 |
|
|
switch (check_cert(cert, md, &mdlen)) { |
49 |
|
|
case SSL_CERT_NONEXISTENT: |
50 |
|
|
print_cert(cert, md, &mdlen); |
51 |
|
|
if (flags & FLAG_DAEMON_MODE || |
52 |
|
|
write_cert(cert) == SSL_CERT_ACTION_REJECT) |
53 |
|
|
goto abort; |
54 |
|
|
break; |
55 |
|
|
case SSL_CERT_MISMATCH: |
56 |
|
|
print_cert(cert, md, &mdlen); |
57 |
|
|
if (flags & FLAG_DAEMON_MODE || |
58 |
|
|
mismatch_cert() == SSL_CERT_ACTION_ABORT) |
59 |
|
|
goto abort; |
60 |
|
|
break; |
61 |
|
|
case SSL_CERT_OK: |
62 |
|
|
if (options & OPTION_DETAILS_VERBOSE) |
63 |
|
|
print_cert(cert, md, &mdlen); |
64 |
|
|
} |
65 |
|
|
|
66 |
|
|
X509_free(cert); |
67 |
|
|
return 0; |
68 |
|
|
|
69 |
|
|
abort: |
70 |
|
|
X509_free(cert); |
71 |
|
|
return ERROR_SSL; |
72 |
|
|
} |
73 |
|
|
|
74 |
|
|
|
75 |
|
|
/* |
76 |
|
|
* Check if the SSL/TLS certificate exists in the certificates file. |
77 |
|
|
*/ |
78 |
|
|
int |
79 |
|
|
check_cert(X509 * pcert, unsigned char *pmd, unsigned int *pmdlen) |
80 |
|
|
{ |
81 |
|
|
int r; |
82 |
|
|
FILE *fd; |
83 |
|
|
X509 *cert; |
84 |
|
|
unsigned char md[EVP_MAX_MD_SIZE]; |
85 |
|
|
unsigned int mdlen; |
86 |
|
|
|
87 |
|
|
r = SSL_CERT_NONEXISTENT; |
88 |
|
|
cert = NULL; |
89 |
|
|
|
90 |
|
|
if (!exists_file(PATHNAME_CERT_FILE)) |
91 |
|
|
return SSL_CERT_NONEXISTENT; |
92 |
|
|
|
93 |
|
|
fd = fopen(PATHNAME_CERT_FILE, "r"); |
94 |
|
|
if (fd == NULL) |
95 |
|
|
return ERROR_FILE_OPEN; |
96 |
|
|
|
97 |
|
|
while ((cert = PEM_read_X509(fd, &cert, NULL, NULL)) != NULL) { |
98 |
|
|
if (X509_subject_name_cmp(cert, pcert) != 0 || |
99 |
|
|
X509_issuer_name_cmp(cert, pcert) != 0) |
100 |
|
|
continue; |
101 |
|
|
|
102 |
|
|
if (!X509_digest(cert, EVP_md5(), md, &mdlen) || |
103 |
|
|
*pmdlen != mdlen) |
104 |
|
|
continue; |
105 |
|
|
|
106 |
|
|
if (memcmp(pmd, md, mdlen) != 0) { |
107 |
|
|
r = SSL_CERT_MISMATCH; |
108 |
|
|
break; |
109 |
|
|
} |
110 |
|
|
r = SSL_CERT_OK; |
111 |
|
|
break; |
112 |
|
|
} |
113 |
|
|
|
114 |
|
|
fclose(fd); |
115 |
|
|
X509_free(cert); |
116 |
|
|
|
117 |
|
|
return r; |
118 |
|
|
} |
119 |
|
|
|
120 |
|
|
|
121 |
|
|
/* |
122 |
|
|
* Print information about the SSL/TLS certificate. |
123 |
|
|
*/ |
124 |
|
|
void |
125 |
|
|
print_cert(X509 * cert, unsigned char *md, unsigned int *mdlen) |
126 |
|
|
{ |
127 |
|
|
unsigned int i; |
128 |
|
|
char *c; |
129 |
|
|
|
130 |
|
|
c = X509_NAME_oneline(X509_get_subject_name(cert), NULL, 0); |
131 |
|
|
printf("Server certificate subject: %s\n", c); |
132 |
|
|
xfree(c); |
133 |
|
|
|
134 |
|
|
c = X509_NAME_oneline(X509_get_issuer_name(cert), NULL, 0); |
135 |
|
|
printf("Server certificate issuer: %s\n", c); |
136 |
|
|
xfree(c); |
137 |
|
|
|
138 |
|
|
printf("Server key fingerprint: "); |
139 |
|
|
for (i = 0; i < *mdlen; i++) |
140 |
|
|
printf(i != *mdlen - 1 ? "%02X:" : "%02X\n", md[i]); |
141 |
|
|
} |
142 |
|
|
|
143 |
|
|
|
144 |
|
|
/* |
145 |
|
|
* Write the SSL/TLS certificate after asking the user to accept/reject it. |
146 |
|
|
*/ |
147 |
|
|
int |
148 |
|
|
write_cert(X509 * cert) |
149 |
|
|
{ |
150 |
|
|
FILE *fd; |
151 |
|
|
char c, buf[LINE_MAX]; |
152 |
|
|
|
153 |
|
|
do { |
154 |
|
|
printf("(R)eject, accept (t)emporarily or " |
155 |
|
|
"accept (p)ermanently? "); |
156 |
|
|
fgets(buf, LINE_MAX, stdin); |
157 |
|
|
c = tolower(*buf); |
158 |
|
|
} while (c != 'r' && c != 't' && c != 'p'); |
159 |
|
|
|
160 |
|
|
if (c == 'r') |
161 |
|
|
return SSL_CERT_ACTION_REJECT; |
162 |
|
|
else if (c == 't') |
163 |
|
|
return SSL_CERT_ACTION_ACCEPT; |
164 |
|
|
|
165 |
|
|
create_file(PATHNAME_CERT_FILE, S_IRUSR | S_IWUSR); |
166 |
|
|
|
167 |
|
|
fd = fopen(PATHNAME_CERT_FILE, "a"); |
168 |
|
|
if (fd == NULL) |
169 |
|
|
return SSL_CERT_ACTION_REJECT; |
170 |
|
|
|
171 |
|
|
PEM_write_X509(fd, cert); |
172 |
|
|
|
173 |
|
|
fclose(fd); |
174 |
|
|
|
175 |
|
|
return SSL_CERT_ACTION_ACCEPT; |
176 |
|
|
} |
177 |
|
|
|
178 |
|
|
|
179 |
|
|
/* |
180 |
|
|
* Ask user to proceed, while a fingerprint mismatch in the SSL/TLS |
181 |
|
|
* certificate was found. |
182 |
|
|
*/ |
183 |
|
|
int |
184 |
|
|
mismatch_cert(void) |
185 |
|
|
{ |
186 |
|
|
char c, buf[LINE_MAX]; |
187 |
|
|
|
188 |
|
|
do { |
189 |
|
|
printf("WARNING: SSL/TLS certificate fingerprint mismatch.\n" |
190 |
|
|
"Proceed with the connection (y/n)? "); |
191 |
|
|
fgets(buf, LINE_MAX, stdin); |
192 |
|
|
c = tolower(*buf); |
193 |
|
|
} while (c != 'y' && c != 'n'); |
194 |
|
|
|
195 |
|
|
if (c == 'y') |
196 |
|
|
return SSL_CERT_ACTION_CONTINUE; |
197 |
|
|
else |
198 |
|
|
return SSL_CERT_ACTION_ABORT; |
199 |
|
|
} |
200 |
|
|
#endif /* SSL_TLS */ |