]> code.citadel.org Git - citadel.git/blob - webcit-ng/tls2.c~
d3bc51a0f51483dc564c0754fe4ec11ae6f66f33
[citadel.git] / webcit-ng / tls2.c~
1 // Functions in this module handle SSL encryption when WebCit is running
2 // as an HTTPS server.
3 //
4 // Copyright (c) 1996-2022 by the citadel.org team
5 //
6 // This program is open source software.  Use, duplication, or
7 // disclosure are subject to the GNU General Public License v3.
8
9 #include "webcit.h"
10
11 SSL_CTX *ssl_ctx;               // global SSL context
12 char key_file[PATH_MAX] = "";
13 char cert_file[PATH_MAX] = "";
14 char *ssl_cipher_list = DEFAULT_SSL_CIPHER_LIST;
15
16
17 // Set the private key and certificate chain for the global SSL Context.
18 // This is called during initialization, and can be called again later if the certificate changes.
19 void bind_to_key_and_certificate(void)
20 {
21         SSL_CTX *old_ctx, *new_ctx;
22
23         if (!(new_ctx = SSL_CTX_new(SSLv23_server_method()))) {
24                 syslog(LOG_WARNING, "SSL_CTX_new failed: %s",
25                        ERR_reason_error_string(ERR_get_error()));
26                 return;
27         }
28
29         syslog(LOG_INFO, "Requesting cipher list: %s", ssl_cipher_list);
30         if (!(SSL_CTX_set_cipher_list(new_ctx, ssl_cipher_list))) {
31                 syslog(LOG_WARNING, "SSL_CTX_set_cipher_list failed: %s",
32                        ERR_reason_error_string(ERR_get_error()));
33                 return;
34         }
35
36         if (IsEmptyStr(key_file)) {
37                 snprintf(key_file, sizeof key_file, "%s/keys/citadel.key",
38                          ctdl_dir);
39         }
40         if (IsEmptyStr(cert_file)) {
41                 snprintf(cert_file, sizeof key_file, "%s/keys/citadel.cer",
42                          ctdl_dir);
43         }
44
45         syslog(LOG_DEBUG,
46                "crypto: [re]installing key \"%s\" and certificate \"%s\"",
47                key_file, cert_file);
48
49         SSL_CTX_use_certificate_chain_file(new_ctx, cert_file);
50         SSL_CTX_use_PrivateKey_file(new_ctx, key_file, SSL_FILETYPE_PEM);
51
52         if (!SSL_CTX_check_private_key(new_ctx)) {
53                 syslog(LOG_WARNING,
54                        "crypto: cannot install certificate: %s",
55                        ERR_reason_error_string(ERR_get_error()));
56         }
57
58         old_ctx = ssl_ctx;
59         ssl_ctx = new_ctx;
60         sleep(1);
61         SSL_CTX_free(old_ctx);
62 }
63
64
65 // Initialize ssl engine, load certs and initialize openssl internals
66 void init_ssl(void)
67 {
68
69         // Initialize the OpenSSL library
70         SSL_load_error_strings();
71         ERR_load_crypto_strings();
72         OpenSSL_add_all_algorithms();
73         SSL_library_init();
74
75         // Now try to bind to the key and certificate.
76         bind_to_key_and_certificate();
77 }
78
79
80 // Check the modification time of the key and certificate -- reload if they changed
81 void update_key_and_cert_if_needed(void)
82 {
83         static time_t previous_mtime = 0;
84         struct stat keystat;
85         struct stat certstat;
86
87         if (stat(key_file, &keystat) != 0) {
88                 syslog(LOG_ERR, "%s: %s", key_file, strerror(errno));
89                 return;
90         }
91         if (stat(cert_file, &certstat) != 0) {
92                 syslog(LOG_ERR, "%s: %s", cert_file, strerror(errno));
93                 return;
94         }
95
96         if ((keystat.st_mtime + certstat.st_mtime) != previous_mtime) {
97                 bind_to_key_and_certificate();
98                 previous_mtime = keystat.st_mtime + certstat.st_mtime;
99         }
100 }
101
102
103 // starts SSL/TLS encryption for the current session.
104 void starttls(struct client_handle *ch)
105 {
106         int retval, bits, alg_bits;
107
108         if (!ssl_ctx) {
109                 return;
110         }
111         // Check the modification time of the key and certificate -- reload if they changed
112         update_key_and_cert_if_needed();
113
114         if (!(ch->ssl_handle = SSL_new(ssl_ctx))) {
115                 syslog(LOG_WARNING, "SSL_new failed: %s",
116                        ERR_reason_error_string(ERR_get_error()));
117                 return;
118         }
119         if (!(SSL_set_fd(ch->ssl_handle, ch->sock))) {
120                 syslog(LOG_WARNING, "SSL_set_fd failed: %s",
121                        ERR_reason_error_string(ERR_get_error()));
122                 SSL_free(ch->ssl_handle);
123                 return;
124         }
125         retval = SSL_accept(ch->ssl_handle);
126         if (retval < 1) {
127                 syslog(LOG_WARNING, "SSL_accept failed: %s",
128                        ERR_reason_error_string(ERR_get_error()));
129         } else {
130                 syslog(LOG_INFO, "SSL_accept success");
131         }
132         bits =
133             SSL_CIPHER_get_bits(SSL_get_current_cipher(ch->ssl_handle),
134                                 &alg_bits);
135         syslog(LOG_INFO, "SSL/TLS using %s on %s (%d of %d bits)",
136                SSL_CIPHER_get_name(SSL_get_current_cipher(ch->ssl_handle)),
137                SSL_CIPHER_get_version(SSL_get_current_cipher
138                                       (ch->ssl_handle)), bits, alg_bits);
139         syslog(LOG_INFO, "SSL started");
140 }
141
142
143 // shuts down the TLS connection
144 void endtls(struct client_handle *ch)
145 {
146         syslog(LOG_INFO, "Ending SSL/TLS");
147         if (ch->ssl_handle != NULL) {
148                 SSL_shutdown(ch->ssl_handle);
149                 SSL_get_SSL_CTX(ch->ssl_handle);
150                 SSL_free(ch->ssl_handle);
151         }
152         ch->ssl_handle = NULL;
153 }
154
155
156 // Send binary data to the client encrypted.
157 int client_write_ssl(struct client_handle *ch, char *buf, int nbytes)
158 {
159         int retval;
160         int nremain;
161         char junk[1];
162
163         if (ch->ssl_handle == NULL)
164                 return (-1);
165
166         nremain = nbytes;
167         while (nremain > 0) {
168                 if (SSL_want_write(ch->ssl_handle)) {
169                         if ((SSL_read(ch->ssl_handle, junk, 0)) < 1) {
170                                 syslog(LOG_WARNING,
171                                        "SSL_read in client_write: %s",
172                                        ERR_reason_error_string
173                                        (ERR_get_error()));
174                         }
175                 }
176                 retval =
177                     SSL_write(ch->ssl_handle, &buf[nbytes - nremain],
178                               nremain);
179                 if (retval < 1) {
180                         long errval;
181
182                         errval = SSL_get_error(ch->ssl_handle, retval);
183                         if (errval == SSL_ERROR_WANT_READ
184                             || errval == SSL_ERROR_WANT_WRITE) {
185                                 sleep(1);
186                                 continue;
187                         }
188                         syslog(LOG_WARNING, "SSL_write: %s",
189                                ERR_reason_error_string(ERR_get_error()));
190                         if (retval == -1) {
191                                 syslog(LOG_WARNING, "errno is %d", errno);
192                                 endtls(ch);
193                         }
194                         return -1;
195                 }
196                 nremain -= retval;
197         }
198         return 0;
199 }
200
201
202 // read data from the encrypted layer
203 int client_read_ssl(struct client_handle *ch, char *buf, int nbytes)
204 {
205         int bytes_read = 0;
206         int rlen = 0;
207         char junk[1];
208
209         if (ch->ssl_handle == NULL)
210                 return (-1);
211
212         while (bytes_read < nbytes) {
213                 if (SSL_want_read(ch->ssl_handle)) {
214                         if ((SSL_write(ch->ssl_handle, junk, 0)) < 1) {
215                                 syslog(LOG_WARNING,
216                                        "SSL_write in client_read");
217                         }
218                 }
219                 rlen =
220                     SSL_read(ch->ssl_handle, &buf[bytes_read],
221                              nbytes - bytes_read);
222                 if (rlen < 1) {
223                         long errval;
224                         errval = SSL_get_error(ch->ssl_handle, rlen);
225                         if (errval == SSL_ERROR_WANT_READ
226                             || errval == SSL_ERROR_WANT_WRITE) {
227                                 sleep(1);
228                                 continue;
229                         }
230                         syslog(LOG_WARNING, "SSL_read error %ld", errval);
231                         endtls(ch);
232                         return (-1);
233                 }
234                 bytes_read += rlen;
235         }
236         return (bytes_read);
237 }