htparser: Added some missing includes.
[ashd.git] / src / ssl-openssl.c
CommitLineData
3c5954e9
FT
1/*
2 ashd - A Sane HTTP Daemon
3 Copyright (C) 2008 Fredrik Tolf <fredrik@dolda2000.com>
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17*/
18
19#include <fcntl.h>
20#include <unistd.h>
e3d4500a 21#include <string.h>
3c5954e9 22#include <sys/socket.h>
e3d4500a 23#include <netinet/in.h>
3c5954e9
FT
24#include <arpa/inet.h>
25
26#ifdef HAVE_CONFIG_H
27#include <config.h>
28#endif
29#include <utils.h>
30#include <mt.h>
31#include <mtio.h>
32#include <req.h>
33#include <log.h>
34#include <bufio.h>
35
36#include "htparser.h"
37
ecda1e6a
FT
38#ifdef HAVE_OPENSSL
39
3c5954e9
FT
40#include <openssl/ssl.h>
41#include <openssl/err.h>
42
43struct sslport {
44 int fd, sport;
45 SSL_CTX *ctx;
46};
47
48struct sslconn {
49 struct sslport *port;
50 int fd;
51 SSL *ssl;
52 struct sockaddr *name;
53 socklen_t namelen;
54};
55
56static int tlsblock(int fd, int err, int to)
57{
58 if(err == SSL_ERROR_WANT_READ) {
59 if(block(fd, EV_READ, to) <= 0)
60 return(1);
61 return(0);
62 } else if(err == SSL_ERROR_WANT_WRITE) {
63 if(block(fd, EV_WRITE, to) <= 0)
64 return(1);
65 return(0);
66 } else {
67 return(1);
68 }
69}
70
71static ssize_t sslread(void *cookie, void *buf, size_t len)
72{
73 struct sslconn *sdat = cookie;
74 int ret, err, nb;
75 size_t off;
76
77 off = 0;
78 while(off < len) {
79 nb = ((len - off) > INT_MAX) ? INT_MAX : (len - off);
80 if((ret = SSL_read(sdat->ssl, buf, nb)) <= 0) {
81 if(off > 0)
82 return(off);
83 err = SSL_get_error(sdat->ssl, ret);
84 if(err == SSL_ERROR_ZERO_RETURN) {
85 return(0);
86 } else if((err == SSL_ERROR_WANT_READ) || (err == SSL_ERROR_WANT_WRITE)) {
87 if(tlsblock(sdat->fd, err, 60)) {
88 errno = ETIMEDOUT;
89 return(-1);
90 }
91 } else {
92 if(err != SSL_ERROR_SYSCALL)
93 errno = EPROTO;
94 return(-1);
95 }
96 } else {
97 off += ret;
98 }
99 }
100 return(off);
101}
102
103static ssize_t sslwrite(void *cookie, const void *buf, size_t len)
104{
105 struct sslconn *sdat = cookie;
106 int ret, err, nb;
107 size_t off;
108
109 off = 0;
110 while(off < len) {
111 nb = ((len - off) > INT_MAX) ? INT_MAX : (len - off);
112 if((ret = SSL_write(sdat->ssl, buf, nb)) <= 0) {
113 if(off > 0)
114 return(off);
115 err = SSL_get_error(sdat->ssl, ret);
116 if((err == SSL_ERROR_WANT_READ) || (err == SSL_ERROR_WANT_WRITE)) {
117 if(tlsblock(sdat->fd, err, 60)) {
118 errno = ETIMEDOUT;
119 return(-1);
120 }
121 } else {
122 if(err != SSL_ERROR_SYSCALL)
123 errno = EIO;
124 return(-1);
125 }
126 } else {
127 off += ret;
128 }
129 }
130 return(off);
131}
132
133static int sslclose(void *cookie)
134{
135 return(0);
136}
137
138static struct bufioops iofuns = {
139 .read = sslread,
140 .write = sslwrite,
141 .close = sslclose,
142};
143
144static int initreq(struct conn *conn, struct hthead *req)
145{
146 struct sslconn *sdat = conn->pdata;
147 struct sockaddr_storage sa;
148 socklen_t salen;
149
150 headappheader(req, "X-Ash-Address", formathaddress(sdat->name, sdat->namelen));
151 if(sdat->name->sa_family == AF_INET)
152 headappheader(req, "X-Ash-Port", sprintf3("%i", ntohs(((struct sockaddr_in *)sdat->name)->sin_port)));
153 else if(sdat->name->sa_family == AF_INET6)
154 headappheader(req, "X-Ash-Port", sprintf3("%i", ntohs(((struct sockaddr_in6 *)sdat->name)->sin6_port)));
155 salen = sizeof(sa);
156 if(!getsockname(sdat->fd, (struct sockaddr *)&sa, &salen))
157 headappheader(req, "X-Ash-Server-Address", formathaddress((struct sockaddr *)&sa, salen));
158 headappheader(req, "X-Ash-Server-Port", sprintf3("%i", sdat->port->sport));
159 headappheader(req, "X-Ash-Protocol", "https");
160 return(0);
161}
162
163static void servessl(struct muth *muth, va_list args)
164{
165 vavar(int, fd);
166 vavar(struct sockaddr_storage, name);
167 vavar(struct sslport *, pd);
168 int ret;
169 SSL *ssl;
170 struct conn conn;
171 struct sslconn sdat;
172
173 fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);
174 ssl = SSL_new(pd->ctx);
175 SSL_set_fd(ssl, fd);
176 while((ret = SSL_accept(ssl)) <= 0) {
177 if(tlsblock(fd, SSL_get_error(ssl, ret), 60))
178 goto out;
179 }
180 memset(&conn, 0, sizeof(conn));
181 memset(&sdat, 0, sizeof(sdat));
182 conn.pdata = &sdat;
183 conn.initreq = initreq;
184 sdat.port = pd;
185 sdat.fd = fd;
186 sdat.ssl = ssl;
187 sdat.name = (struct sockaddr *)&name;
188 sdat.namelen = sizeof(name);
189 serve(bioopen(&sdat, &iofuns), fd, &conn);
190 while((ret = SSL_shutdown(ssl)) < 0) {
191 if(tlsblock(fd, SSL_get_error(ssl, ret), 60))
192 goto out;
193 }
194out:
195 SSL_free(ssl);
196 close(fd);
197}
198
199static void listenloop(struct muth *muth, va_list args)
200{
201 vavar(struct sslport *, pd);
202 int i, ns, n;
203 struct sockaddr_storage name;
204 socklen_t namelen;
205
206 fcntl(pd->fd, F_SETFL, fcntl(pd->fd, F_GETFL) | O_NONBLOCK);
207 while(1) {
208 namelen = sizeof(name);
209 if(block(pd->fd, EV_READ, 0) == 0)
210 goto out;
211 for(n = 0; n < 100; n++) {
212 if((ns = accept(pd->fd, (struct sockaddr *)&name, &namelen)) < 0) {
213 if(errno == EAGAIN)
214 break;
215 if(errno == ECONNABORTED)
216 continue;
217 flog(LOG_ERR, "accept: %s", strerror(errno));
218 goto out;
219 }
220 mustart(servessl, ns, name, pd);
221 }
222 }
223
224out:
225 close(pd->fd);
226 free(pd);
227 for(i = 0; i < listeners.d; i++) {
228 if(listeners.b[i] == muth)
229 bufdel(listeners, i);
230 }
231}
232
233void handleossl(int argc, char **argp, char **argv)
234{
235 int i, port, fd;
236 SSL_CTX *ctx;
237 char *crtfile, *keyfile;
238 struct sslport *pd;
239
240 ctx = SSL_CTX_new(TLS_server_method());
241 if(!ctx) {
242 flog(LOG_ERR, "ssl: could not create context: %s", ERR_error_string(ERR_get_error(), NULL));
243 exit(1);
244 }
245 port = 443;
246 for(i = 0; i < argc; i++) {
247 if(!strcmp(argp[i], "help")) {
248 printf("ssl handler parameters:\n");
249 printf("\tcert=CERT-FILE [mandatory]\n");
250 printf("\t\tThe name of the file to read the certificate from.\n");
251 printf("\tkey=KEY-FILE [same as CERT-FILE]\n");
252 printf("\t\tThe name of the file to read the private key from.\n");
253 printf("\tport=PORT [443]\n");
254 printf("\t\tThe TCP port to listen on.\n");
255 exit(0);
256 } else if(!strcmp(argp[i], "cert")) {
257 crtfile = argv[i];
258 } else if(!strcmp(argp[i], "key")) {
259 keyfile = argv[i];
260 } else if(!strcmp(argp[i], "port")) {
261 port = atoi(argv[i]);
262 } else {
263 flog(LOG_ERR, "unknown parameter `%s' to ssl handler", argp[i]);
264 exit(1);
265 }
266 }
267 if(crtfile == NULL) {
268 flog(LOG_ERR, "ssl: needs certificate file at the very least");
269 exit(1);
270 }
271 if(keyfile == NULL)
272 keyfile = crtfile;
273 if(SSL_CTX_use_certificate_file(ctx, crtfile, SSL_FILETYPE_PEM) <= 0) {
274 flog(LOG_ERR, "ssl: could not load certificate: %s", ERR_error_string(ERR_get_error(), NULL));
275 exit(1);
276 }
277 if(SSL_CTX_use_PrivateKey_file(ctx, keyfile, SSL_FILETYPE_PEM) <= 0) {
278 flog(LOG_ERR, "ssl: could not load certificate: %s", ERR_error_string(ERR_get_error(), NULL));
279 exit(1);
280 }
281 if(!SSL_CTX_check_private_key(ctx)) {
282 flog(LOG_ERR, "ssl: key and certificate do not match");
283 exit(1);
284 }
285 if((fd = listensock6(port)) < 0) {
286 flog(LOG_ERR, "could not listen on IPv65 port (port %i): %s", port, strerror(errno));
287 exit(1);
288 }
289 omalloc(pd);
290 pd->fd = fd;
291 pd->sport = port;
292 pd->ctx = ctx;
293 bufadd(listeners, mustart(listenloop, pd));
294 if((fd = listensock4(port)) < 0) {
295 if(errno != EADDRINUSE) {
296 flog(LOG_ERR, "could not listen on IPv4 port (port %i): Is", port, strerror(errno));
297 exit(1);
298 }
299 } else {
300 omalloc(pd);
301 pd->fd = fd;
302 pd->sport = port;
303 pd->ctx = ctx;
304 bufadd(listeners, mustart(listenloop, pd));
305 }
306}
ecda1e6a
FT
307
308#endif