]> git.ipfire.org Git - thirdparty/squid.git/blob - src/ssl.cc
fix headers to allow inclusion into C++ source
[thirdparty/squid.git] / src / ssl.cc
1
2 /*
3 * $Id: ssl.cc,v 1.122 2002/09/15 06:40:57 robertc Exp $
4 *
5 * DEBUG: section 26 Secure Sockets Layer Proxy
6 * AUTHOR: Duane Wessels
7 *
8 * SQUID Web Proxy Cache http://www.squid-cache.org/
9 * ----------------------------------------------------------
10 *
11 * Squid is the result of efforts by numerous individuals from
12 * the Internet community; see the CONTRIBUTORS file for full
13 * details. Many organizations have provided support for Squid's
14 * development; see the SPONSORS file for full details. Squid is
15 * Copyrighted (C) 2001 by the Regents of the University of
16 * California; see the COPYRIGHT file for full details. Squid
17 * incorporates software developed and/or copyrighted by other
18 * sources; see the CREDITS file for full details.
19 *
20 * This program is free software; you can redistribute it and/or modify
21 * it under the terms of the GNU General Public License as published by
22 * the Free Software Foundation; either version 2 of the License, or
23 * (at your option) any later version.
24 *
25 * This program is distributed in the hope that it will be useful,
26 * but WITHOUT ANY WARRANTY; without even the implied warranty of
27 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 * GNU General Public License for more details.
29 *
30 * You should have received a copy of the GNU General Public License
31 * along with this program; if not, write to the Free Software
32 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
33 *
34 */
35
36 #include "squid.h"
37
38 typedef struct {
39 char *url;
40 char *host; /* either request->host or proxy host */
41 u_short port;
42 request_t *request;
43 FwdServer *servers;
44 struct {
45 int fd;
46 int len;
47 char *buf;
48 } client, server;
49 size_t *size_ptr; /* pointer to size in an ConnStateData for logging */
50 int *status_ptr; /* pointer to status for logging */
51 #if DELAY_POOLS
52 delay_id delay_id;
53 #endif
54 } SslStateData;
55
56 static const char *const conn_established = "HTTP/1.0 200 Connection established\r\n\r\n";
57
58 static CNCB sslConnectDone;
59 static ERCB sslErrorComplete;
60 static PF sslServerClosed;
61 static PF sslClientClosed;
62 static PF sslReadClient;
63 static PF sslReadServer;
64 static PF sslTimeout;
65 static PF sslWriteClient;
66 static PF sslWriteServer;
67 static PSC sslPeerSelectComplete;
68 static void sslStateFree(SslStateData * sslState);
69 static void sslConnected(int fd, void *);
70 static void sslProxyConnected(int fd, void *);
71 static void sslSetSelect(SslStateData * sslState);
72 #if DELAY_POOLS
73 static DEFER sslDeferServerRead;
74 #endif
75
76 static void
77 sslServerClosed(int fd, void *data)
78 {
79 SslStateData *sslState = data;
80 debug(26, 3) ("sslServerClosed: FD %d\n", fd);
81 assert(fd == sslState->server.fd);
82 sslState->server.fd = -1;
83 if (sslState->client.fd == -1)
84 sslStateFree(sslState);
85 }
86
87 static void
88 sslClientClosed(int fd, void *data)
89 {
90 SslStateData *sslState = data;
91 debug(26, 3) ("sslClientClosed: FD %d\n", fd);
92 assert(fd == sslState->client.fd);
93 sslState->client.fd = -1;
94 if (sslState->server.fd == -1)
95 sslStateFree(sslState);
96 }
97
98 static void
99 sslStateFree(SslStateData * sslState)
100 {
101 debug(26, 3) ("sslStateFree: sslState=%p\n", sslState);
102 assert(sslState != NULL);
103 assert(sslState->client.fd == -1);
104 assert(sslState->server.fd == -1);
105 safe_free(sslState->server.buf);
106 safe_free(sslState->client.buf);
107 safe_free(sslState->url);
108 fwdServersFree(&sslState->servers);
109 sslState->host = NULL;
110 requestUnlink(sslState->request);
111 sslState->request = NULL;
112 #if DELAY_POOLS
113 delayUnregisterDelayIdPtr(&sslState->delay_id);
114 #endif
115 cbdataFree(sslState);
116 }
117
118 #if DELAY_POOLS
119 static int
120 sslDeferServerRead(int fdnotused, void *data)
121 {
122 SslStateData *s = data;
123 int i = delayBytesWanted(s->delay_id, 0, INT_MAX);
124 if (i == INT_MAX)
125 return 0;
126 if (i == 0)
127 return 1;
128 return -1;
129 }
130 #endif
131
132 static void
133 sslSetSelect(SslStateData * sslState)
134 {
135 size_t read_sz = SQUID_TCP_SO_RCVBUF;
136 assert(sslState->server.fd > -1 || sslState->client.fd > -1);
137 if (sslState->client.fd > -1) {
138 if (sslState->server.len > 0) {
139 commSetSelect(sslState->client.fd,
140 COMM_SELECT_WRITE,
141 sslWriteClient,
142 sslState,
143 0);
144 }
145 if (sslState->client.len < read_sz) {
146 commSetSelect(sslState->client.fd,
147 COMM_SELECT_READ,
148 sslReadClient,
149 sslState,
150 Config.Timeout.read);
151 }
152 } else if (sslState->client.len == 0) {
153 comm_close(sslState->server.fd);
154 }
155 if (sslState->server.fd > -1) {
156 if (sslState->client.len > 0) {
157 commSetSelect(sslState->server.fd,
158 COMM_SELECT_WRITE,
159 sslWriteServer,
160 sslState,
161 0);
162 }
163 #if DELAY_POOLS
164 /*
165 * If this was allowed to return 0, there would be a possibility
166 * of the socket becoming "hung" with data accumulating but no
167 * write handler (server.len==0) and no read handler (!(0<0)) and
168 * no data flowing in the other direction. Hence the argument of
169 * 1 as min.
170 */
171 read_sz = delayBytesWanted(sslState->delay_id, 1, read_sz);
172 #endif
173 if (sslState->server.len < read_sz) {
174 /* Have room to read more */
175 commSetSelect(sslState->server.fd,
176 COMM_SELECT_READ,
177 sslReadServer,
178 sslState,
179 Config.Timeout.read);
180 }
181 } else if (sslState->client.fd == -1) {
182 /* client already closed, nothing more to do */
183 } else if (sslState->server.len == 0) {
184 comm_close(sslState->client.fd);
185 }
186 }
187
188 /* Read from server side and queue it for writing to the client */
189 static void
190 sslReadServer(int fd, void *data)
191 {
192 SslStateData *sslState = data;
193 int len;
194 size_t read_sz = SQUID_TCP_SO_RCVBUF - sslState->server.len;
195 assert(fd == sslState->server.fd);
196 debug(26, 3) ("sslReadServer: FD %d, reading %d bytes at offset %d\n",
197 fd, (int) read_sz, sslState->server.len);
198 errno = 0;
199 #if DELAY_POOLS
200 read_sz = delayBytesWanted(sslState->delay_id, 1, read_sz);
201 #endif
202 statCounter.syscalls.sock.reads++;
203 len = FD_READ_METHOD(fd, sslState->server.buf + sslState->server.len, read_sz);
204 debug(26, 3) ("sslReadServer: FD %d, read %d bytes\n", fd, len);
205 if (len > 0) {
206 fd_bytes(fd, len, FD_READ);
207 #if DELAY_POOLS
208 delayBytesIn(sslState->delay_id, len);
209 #endif
210 kb_incr(&statCounter.server.all.kbytes_in, len);
211 kb_incr(&statCounter.server.other.kbytes_in, len);
212 sslState->server.len += len;
213 }
214 cbdataInternalLock(sslState); /* ??? should be locked by the caller... */
215 if (len < 0) {
216 debug(50, ignoreErrno(errno) ? 3 : 1)
217 ("sslReadServer: FD %d: read failure: %s\n", fd, xstrerror());
218 if (!ignoreErrno(errno))
219 comm_close(fd);
220 } else if (len == 0) {
221 comm_close(sslState->server.fd);
222 }
223 if (cbdataReferenceValid(sslState))
224 sslSetSelect(sslState);
225 cbdataInternalUnlock(sslState); /* ??? */
226 }
227
228 /* Read from client side and queue it for writing to the server */
229 static void
230 sslReadClient(int fd, void *data)
231 {
232 SslStateData *sslState = data;
233 int len;
234 assert(fd == sslState->client.fd);
235 debug(26, 3) ("sslReadClient: FD %d, reading %d bytes at offset %d\n",
236 fd, SQUID_TCP_SO_RCVBUF - sslState->client.len,
237 sslState->client.len);
238 statCounter.syscalls.sock.reads++;
239 len = FD_READ_METHOD(fd,
240 sslState->client.buf + sslState->client.len,
241 SQUID_TCP_SO_RCVBUF - sslState->client.len);
242 debug(26, 3) ("sslReadClient: FD %d, read %d bytes\n", fd, len);
243 if (len > 0) {
244 fd_bytes(fd, len, FD_READ);
245 kb_incr(&statCounter.client_http.kbytes_in, len);
246 sslState->client.len += len;
247 }
248 cbdataInternalLock(sslState); /* ??? should be locked by the caller... */
249 if (len < 0) {
250 int level = 1;
251 #ifdef ECONNRESET
252 if (errno == ECONNRESET)
253 level = 2;
254 #endif
255 if (ignoreErrno(errno))
256 level = 3;
257 debug(50, level) ("sslReadClient: FD %d: read failure: %s\n",
258 fd, xstrerror());
259 if (!ignoreErrno(errno))
260 comm_close(fd);
261 } else if (len == 0) {
262 comm_close(fd);
263 }
264 if (cbdataReferenceValid(sslState))
265 sslSetSelect(sslState);
266 cbdataInternalUnlock(sslState); /* ??? */
267 }
268
269 /* Writes data from the client buffer to the server side */
270 static void
271 sslWriteServer(int fd, void *data)
272 {
273 SslStateData *sslState = data;
274 int len;
275 assert(fd == sslState->server.fd);
276 debug(26, 3) ("sslWriteServer: FD %d, %d bytes to write\n",
277 fd, sslState->client.len);
278 statCounter.syscalls.sock.writes++;
279 len = FD_WRITE_METHOD(fd,
280 sslState->client.buf,
281 sslState->client.len);
282 debug(26, 3) ("sslWriteServer: FD %d, %d bytes written\n", fd, len);
283 if (len > 0) {
284 fd_bytes(fd, len, FD_WRITE);
285 kb_incr(&statCounter.server.all.kbytes_out, len);
286 kb_incr(&statCounter.server.other.kbytes_out, len);
287 assert(len <= sslState->client.len);
288 sslState->client.len -= len;
289 if (sslState->client.len > 0) {
290 /* we didn't write the whole thing */
291 xmemmove(sslState->client.buf,
292 sslState->client.buf + len,
293 sslState->client.len);
294 }
295 }
296 cbdataInternalLock(sslState); /* ??? should be locked by the caller... */
297 if (len < 0) {
298 debug(50, ignoreErrno(errno) ? 3 : 1)
299 ("sslWriteServer: FD %d: write failure: %s.\n", fd, xstrerror());
300 if (!ignoreErrno(errno))
301 comm_close(fd);
302 }
303 if (cbdataReferenceValid(sslState))
304 sslSetSelect(sslState);
305 cbdataInternalUnlock(sslState); /* ??? */
306 }
307
308 /* Writes data from the server buffer to the client side */
309 static void
310 sslWriteClient(int fd, void *data)
311 {
312 SslStateData *sslState = data;
313 int len;
314 assert(fd == sslState->client.fd);
315 debug(26, 3) ("sslWriteClient: FD %d, %d bytes to write\n",
316 fd, sslState->server.len);
317 statCounter.syscalls.sock.writes++;
318 len = FD_WRITE_METHOD(fd,
319 sslState->server.buf,
320 sslState->server.len);
321 debug(26, 3) ("sslWriteClient: FD %d, %d bytes written\n", fd, len);
322 if (len > 0) {
323 fd_bytes(fd, len, FD_WRITE);
324 kb_incr(&statCounter.client_http.kbytes_out, len);
325 assert(len <= sslState->server.len);
326 sslState->server.len -= len;
327 /* increment total object size */
328 if (sslState->size_ptr)
329 *sslState->size_ptr += len;
330 if (sslState->server.len > 0) {
331 /* we didn't write the whole thing */
332 xmemmove(sslState->server.buf,
333 sslState->server.buf + len,
334 sslState->server.len);
335 }
336 }
337 cbdataInternalLock(sslState); /* ??? should be locked by the caller... */
338 if (len < 0) {
339 debug(50, ignoreErrno(errno) ? 3 : 1)
340 ("sslWriteClient: FD %d: write failure: %s.\n", fd, xstrerror());
341 if (!ignoreErrno(errno))
342 comm_close(fd);
343 }
344 if (cbdataReferenceValid(sslState))
345 sslSetSelect(sslState);
346 cbdataInternalUnlock(sslState); /* ??? */
347 }
348
349 static void
350 sslTimeout(int fd, void *data)
351 {
352 SslStateData *sslState = data;
353 debug(26, 3) ("sslTimeout: FD %d\n", fd);
354 if (sslState->client.fd > -1)
355 comm_close(sslState->client.fd);
356 if (sslState->server.fd > -1)
357 comm_close(sslState->server.fd);
358 }
359
360 static void
361 sslConnected(int fd, void *data)
362 {
363 SslStateData *sslState = data;
364 debug(26, 3) ("sslConnected: FD %d sslState=%p\n", fd, sslState);
365 *sslState->status_ptr = HTTP_OK;
366 xstrncpy(sslState->server.buf, conn_established, SQUID_TCP_SO_RCVBUF);
367 sslState->server.len = strlen(conn_established);
368 sslSetSelect(sslState);
369 }
370
371 static void
372 sslErrorComplete(int fdnotused, void *data, size_t sizenotused)
373 {
374 SslStateData *sslState = data;
375 assert(sslState != NULL);
376 if (sslState->client.fd > -1)
377 comm_close(sslState->client.fd);
378 if (sslState->server.fd > -1)
379 comm_close(sslState->server.fd);
380 }
381
382
383 static void
384 sslConnectDone(int fdnotused, comm_err_t status, void *data)
385 {
386 SslStateData *sslState = data;
387 request_t *request = sslState->request;
388 ErrorState *err = NULL;
389 if (sslState->servers->_peer)
390 hierarchyNote(&sslState->request->hier, sslState->servers->code,
391 sslState->servers->_peer->host);
392 else if (Config.onoff.log_ip_on_direct)
393 hierarchyNote(&sslState->request->hier, sslState->servers->code,
394 fd_table[sslState->server.fd].ipaddr);
395 else
396 hierarchyNote(&sslState->request->hier, sslState->servers->code,
397 sslState->host);
398 if (status == COMM_ERR_DNS) {
399 debug(26, 4) ("sslConnect: Unknown host: %s\n", sslState->host);
400 err = errorCon(ERR_DNS_FAIL, HTTP_NOT_FOUND);
401 *sslState->status_ptr = HTTP_NOT_FOUND;
402 err->request = requestLink(request);
403 err->dnsserver_msg = xstrdup(dns_error_message);
404 err->callback = sslErrorComplete;
405 err->callback_data = sslState;
406 errorSend(sslState->client.fd, err);
407 } else if (status != COMM_OK) {
408 err = errorCon(ERR_CONNECT_FAIL, HTTP_SERVICE_UNAVAILABLE);
409 *sslState->status_ptr = HTTP_SERVICE_UNAVAILABLE;
410 err->xerrno = errno;
411 err->host = xstrdup(sslState->host);
412 err->port = sslState->port;
413 err->request = requestLink(request);
414 err->callback = sslErrorComplete;
415 err->callback_data = sslState;
416 errorSend(sslState->client.fd, err);
417 } else {
418 if (sslState->servers->_peer)
419 sslProxyConnected(sslState->server.fd, sslState);
420 else
421 sslConnected(sslState->server.fd, sslState);
422 commSetTimeout(sslState->server.fd,
423 Config.Timeout.read,
424 sslTimeout,
425 sslState);
426 #if DELAY_POOLS
427 commSetDefer(sslState->server.fd, sslDeferServerRead, sslState);
428 #endif
429 }
430 }
431
432 CBDATA_TYPE(SslStateData);
433 void
434 sslStart(clientHttpRequest * http, size_t * size_ptr, int *status_ptr)
435 {
436 /* Create state structure. */
437 SslStateData *sslState = NULL;
438 int sock;
439 ErrorState *err = NULL;
440 aclCheck_t ch;
441 int answer;
442 int fd = http->conn->fd;
443 request_t *request = http->request;
444 char *url = http->uri;
445 /*
446 * client_addr == no_addr indicates this is an "internal" request
447 * from peer_digest.c, asn.c, netdb.c, etc and should always
448 * be allowed. yuck, I know.
449 */
450 if (request->client_addr.s_addr != no_addr.s_addr) {
451 /*
452 * Check if this host is allowed to fetch MISSES from us (miss_access)
453 */
454 memset(&ch, '\0', sizeof(aclCheck_t));
455 ch.src_addr = request->client_addr;
456 ch.my_addr = request->my_addr;
457 ch.my_port = request->my_port;
458 ch.request = request;
459 answer = aclCheckFast(Config.accessList.miss, &ch);
460 if (answer == 0) {
461 err = errorCon(ERR_FORWARDING_DENIED, HTTP_FORBIDDEN);
462 *status_ptr = HTTP_FORBIDDEN;
463 err->request = requestLink(request);
464 err->src_addr = request->client_addr;
465 errorSend(fd, err);
466 return;
467 }
468 }
469 debug(26, 3) ("sslStart: '%s %s'\n",
470 RequestMethodStr[request->method], url);
471 statCounter.server.all.requests++;
472 statCounter.server.other.requests++;
473 /* Create socket. */
474 sock = comm_openex(SOCK_STREAM,
475 0,
476 getOutgoingAddr(request),
477 0,
478 COMM_NONBLOCKING,
479 getOutgoingTOS(request),
480 url);
481 if (sock == COMM_ERROR) {
482 debug(26, 4) ("sslStart: Failed because we're out of sockets.\n");
483 err = errorCon(ERR_SOCKET_FAILURE, HTTP_INTERNAL_SERVER_ERROR);
484 *status_ptr = HTTP_INTERNAL_SERVER_ERROR;
485 err->xerrno = errno;
486 err->request = requestLink(request);
487 errorSend(fd, err);
488 return;
489 }
490 CBDATA_INIT_TYPE(SslStateData);
491 sslState = cbdataAlloc(SslStateData);
492 #if DELAY_POOLS
493 sslState->delay_id = delayClient(http);
494 delayRegisterDelayIdPtr(&sslState->delay_id);
495 #endif
496 sslState->url = xstrdup(url);
497 sslState->request = requestLink(request);
498 sslState->size_ptr = size_ptr;
499 sslState->status_ptr = status_ptr;
500 sslState->client.fd = fd;
501 sslState->server.fd = sock;
502 sslState->server.buf = xmalloc(SQUID_TCP_SO_RCVBUF);
503 sslState->client.buf = xmalloc(SQUID_TCP_SO_RCVBUF);
504 comm_add_close_handler(sslState->server.fd,
505 sslServerClosed,
506 sslState);
507 comm_add_close_handler(sslState->client.fd,
508 sslClientClosed,
509 sslState);
510 commSetTimeout(sslState->client.fd,
511 Config.Timeout.lifetime,
512 sslTimeout,
513 sslState);
514 commSetTimeout(sslState->server.fd,
515 Config.Timeout.connect,
516 sslTimeout,
517 sslState);
518 peerSelect(request,
519 NULL,
520 sslPeerSelectComplete,
521 sslState);
522 /*
523 * Disable the client read handler until peer selection is complete
524 * Take control away from client_side.c.
525 */
526 commSetSelect(sslState->client.fd, COMM_SELECT_READ, NULL, NULL, 0);
527 }
528
529 static void
530 sslProxyConnected(int fd, void *data)
531 {
532 SslStateData *sslState = data;
533 MemBuf mb;
534 HttpHeader hdr_out;
535 Packer p;
536 http_state_flags flags;
537 debug(26, 3) ("sslProxyConnected: FD %d sslState=%p\n", fd, sslState);
538 memset(&flags, '\0', sizeof(flags));
539 flags.proxying = sslState->request->flags.proxying;
540 memBufDefInit(&mb);
541 memBufPrintf(&mb, "CONNECT %s HTTP/1.0\r\n", sslState->url);
542 httpBuildRequestHeader(sslState->request,
543 sslState->request,
544 NULL, /* StoreEntry */
545 &hdr_out,
546 sslState->client.fd,
547 flags); /* flags */
548 packerToMemInit(&p, &mb);
549 httpHeaderPackInto(&hdr_out, &p);
550 httpHeaderClean(&hdr_out);
551 packerClean(&p);
552 memBufAppend(&mb, "\r\n", 2);
553 xstrncpy(sslState->client.buf, mb.buf, SQUID_TCP_SO_RCVBUF);
554 debug(26, 3) ("sslProxyConnected: Sending {%s}\n", sslState->client.buf);
555 sslState->client.len = mb.size;
556 memBufClean(&mb);
557 commSetTimeout(sslState->server.fd,
558 Config.Timeout.read,
559 sslTimeout,
560 sslState);
561 sslSetSelect(sslState);
562 }
563
564 static void
565 sslPeerSelectComplete(FwdServer * fs, void *data)
566 {
567 SslStateData *sslState = data;
568 request_t *request = sslState->request;
569 peer *g = NULL;
570 if (fs == NULL) {
571 ErrorState *err;
572 err = errorCon(ERR_CANNOT_FORWARD, HTTP_SERVICE_UNAVAILABLE);
573 *sslState->status_ptr = HTTP_SERVICE_UNAVAILABLE;
574 err->request = requestLink(sslState->request);
575 err->callback = sslErrorComplete;
576 err->callback_data = sslState;
577 errorSend(sslState->client.fd, err);
578 return;
579 }
580 sslState->servers = fs;
581 sslState->host = fs->_peer ? fs->_peer->host : request->host;
582 if (fs->_peer == NULL) {
583 sslState->port = request->port;
584 } else if (fs->_peer->http_port != 0) {
585 sslState->port = fs->_peer->http_port;
586 } else if ((g = peerFindByName(fs->_peer->host))) {
587 sslState->port = g->http_port;
588 } else {
589 sslState->port = CACHE_HTTP_PORT;
590 }
591 if (fs->_peer) {
592 sslState->request->peer_login = fs->_peer->login;
593 sslState->request->flags.proxying = 1;
594 } else {
595 sslState->request->peer_login = NULL;
596 sslState->request->flags.proxying = 0;
597 }
598 #if DELAY_POOLS
599 /* no point using the delayIsNoDelay stuff since ssl is nice and simple */
600 if (g && g->options.no_delay && sslState->delay_id) {
601 delayUnregisterDelayIdPtr(&sslState->delay_id);
602 sslState->delay_id = 0;
603 }
604 #endif
605 commConnectStart(sslState->server.fd,
606 sslState->host,
607 sslState->port,
608 sslConnectDone,
609 sslState);
610 }