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