Annotation of embedaddon/strongswan/src/libtls/tls_socket.c, revision 1.1
1.1 ! misho 1: /*
! 2: * Copyright (C) 2010 Martin Willi
! 3: * Copyright (C) 2010 revosec AG
! 4: *
! 5: * This program is free software; you can redistribute it and/or modify it
! 6: * under the terms of the GNU General Public License as published by the
! 7: * Free Software Foundation; either version 2 of the License, or (at your
! 8: * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
! 9: *
! 10: * This program is distributed in the hope that it will be useful, but
! 11: * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
! 12: * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
! 13: * for more details.
! 14: */
! 15:
! 16: #include "tls_socket.h"
! 17:
! 18: #include <unistd.h>
! 19: #include <errno.h>
! 20:
! 21: #include <utils/debug.h>
! 22: #include <threading/thread.h>
! 23:
! 24: /**
! 25: * Buffer size for plain side I/O
! 26: */
! 27: #define PLAIN_BUF_SIZE TLS_MAX_FRAGMENT_LEN
! 28:
! 29: /**
! 30: * Buffer size for encrypted side I/O
! 31: */
! 32: #define CRYPTO_BUF_SIZE TLS_MAX_FRAGMENT_LEN + 2048
! 33:
! 34: typedef struct private_tls_socket_t private_tls_socket_t;
! 35: typedef struct private_tls_application_t private_tls_application_t;
! 36:
! 37: struct private_tls_application_t {
! 38:
! 39: /**
! 40: * Implements tls_application layer.
! 41: */
! 42: tls_application_t application;
! 43:
! 44: /**
! 45: * Output buffer to write to
! 46: */
! 47: chunk_t out;
! 48:
! 49: /**
! 50: * Number of bytes written to out
! 51: */
! 52: size_t out_done;
! 53:
! 54: /**
! 55: * Input buffer to read to
! 56: */
! 57: chunk_t in;
! 58:
! 59: /**
! 60: * Number of bytes read to in
! 61: */
! 62: size_t in_done;
! 63:
! 64: /**
! 65: * Cached input data
! 66: */
! 67: chunk_t cache;
! 68:
! 69: /**
! 70: * Bytes consumed in cache
! 71: */
! 72: size_t cache_done;
! 73:
! 74: /**
! 75: * Close TLS connection?
! 76: */
! 77: bool close;
! 78: };
! 79:
! 80: /**
! 81: * Private data of an tls_socket_t object.
! 82: */
! 83: struct private_tls_socket_t {
! 84:
! 85: /**
! 86: * Public tls_socket_t interface.
! 87: */
! 88: tls_socket_t public;
! 89:
! 90: /**
! 91: * TLS application implementation
! 92: */
! 93: private_tls_application_t app;
! 94:
! 95: /**
! 96: * TLS stack
! 97: */
! 98: tls_t *tls;
! 99:
! 100: /**
! 101: * Underlying OS socket
! 102: */
! 103: int fd;
! 104: };
! 105:
! 106: METHOD(tls_application_t, process, status_t,
! 107: private_tls_application_t *this, bio_reader_t *reader)
! 108: {
! 109: chunk_t data;
! 110: size_t len;
! 111:
! 112: if (this->close)
! 113: {
! 114: return SUCCESS;
! 115: }
! 116: len = min(reader->remaining(reader), this->in.len - this->in_done);
! 117: if (len)
! 118: { /* copy to read buffer as much as fits in */
! 119: if (!reader->read_data(reader, len, &data))
! 120: {
! 121: return FAILED;
! 122: }
! 123: memcpy(this->in.ptr + this->in_done, data.ptr, data.len);
! 124: this->in_done += data.len;
! 125: }
! 126: else
! 127: { /* read buffer is full, cache for next read */
! 128: if (!reader->read_data(reader, reader->remaining(reader), &data))
! 129: {
! 130: return FAILED;
! 131: }
! 132: this->cache = chunk_cat("mc", this->cache, data);
! 133: }
! 134: return NEED_MORE;
! 135: }
! 136:
! 137: METHOD(tls_application_t, build, status_t,
! 138: private_tls_application_t *this, bio_writer_t *writer)
! 139: {
! 140: if (this->close)
! 141: {
! 142: return SUCCESS;
! 143: }
! 144: if (this->out.len > this->out_done)
! 145: {
! 146: writer->write_data(writer, this->out);
! 147: this->out_done = this->out.len;
! 148: return NEED_MORE;
! 149: }
! 150: return INVALID_STATE;
! 151: }
! 152:
! 153: /**
! 154: * TLS data exchange loop
! 155: */
! 156: static bool exchange(private_tls_socket_t *this, bool wr, bool block)
! 157: {
! 158: char buf[CRYPTO_BUF_SIZE], *pos;
! 159: ssize_t in, out;
! 160: size_t len;
! 161: int flags;
! 162:
! 163: while (TRUE)
! 164: {
! 165: while (TRUE)
! 166: {
! 167: len = sizeof(buf);
! 168: switch (this->tls->build(this->tls, buf, &len, NULL))
! 169: {
! 170: case NEED_MORE:
! 171: case ALREADY_DONE:
! 172: pos = buf;
! 173: while (len)
! 174: {
! 175: out = write(this->fd, pos, len);
! 176: if (out == -1)
! 177: {
! 178: DBG1(DBG_TLS, "TLS crypto write error: %s",
! 179: strerror(errno));
! 180: return FALSE;
! 181: }
! 182: len -= out;
! 183: pos += out;
! 184: }
! 185: continue;
! 186: case INVALID_STATE:
! 187: break;
! 188: case SUCCESS:
! 189: return TRUE;
! 190: default:
! 191: return FALSE;
! 192: }
! 193: break;
! 194: }
! 195: if (wr)
! 196: {
! 197: if (this->app.out_done == this->app.out.len)
! 198: { /* all data written */
! 199: return TRUE;
! 200: }
! 201: }
! 202: else
! 203: {
! 204: if (this->app.in_done == this->app.in.len)
! 205: { /* buffer fully received */
! 206: return TRUE;
! 207: }
! 208: }
! 209:
! 210: flags = 0;
! 211: if (this->app.out_done == this->app.out.len)
! 212: {
! 213: if (!block || this->app.in_done)
! 214: {
! 215: flags |= MSG_DONTWAIT;
! 216: }
! 217: }
! 218: in = recv(this->fd, buf, sizeof(buf), flags);
! 219: if (in < 0)
! 220: {
! 221: if (errno == EAGAIN || errno == EWOULDBLOCK)
! 222: {
! 223: if (this->app.in_done == 0)
! 224: {
! 225: /* reading, nothing got yet, and call would block */
! 226: errno = EWOULDBLOCK;
! 227: this->app.in_done = -1;
! 228: }
! 229: return TRUE;
! 230: }
! 231: return FALSE;
! 232: }
! 233: if (in == 0)
! 234: { /* EOF */
! 235: return TRUE;
! 236: }
! 237: switch (this->tls->process(this->tls, buf, in))
! 238: {
! 239: case NEED_MORE:
! 240: break;
! 241: case SUCCESS:
! 242: return TRUE;
! 243: default:
! 244: return FALSE;
! 245: }
! 246: }
! 247: }
! 248:
! 249: METHOD(tls_socket_t, read_, ssize_t,
! 250: private_tls_socket_t *this, void *buf, size_t len, bool block)
! 251: {
! 252: if (this->app.cache.len)
! 253: {
! 254: size_t cache;
! 255:
! 256: cache = min(len, this->app.cache.len - this->app.cache_done);
! 257: memcpy(buf, this->app.cache.ptr + this->app.cache_done, cache);
! 258:
! 259: this->app.cache_done += cache;
! 260: if (this->app.cache_done == this->app.cache.len)
! 261: {
! 262: chunk_free(&this->app.cache);
! 263: this->app.cache_done = 0;
! 264: }
! 265: return cache;
! 266: }
! 267: this->app.in.ptr = buf;
! 268: this->app.in.len = len;
! 269: this->app.in_done = 0;
! 270: if (exchange(this, FALSE, block))
! 271: {
! 272: return this->app.in_done;
! 273: }
! 274: return -1;
! 275: }
! 276:
! 277: METHOD(tls_socket_t, write_, ssize_t,
! 278: private_tls_socket_t *this, void *buf, size_t len)
! 279: {
! 280: this->app.out.ptr = buf;
! 281: this->app.out.len = len;
! 282: this->app.out_done = 0;
! 283: if (exchange(this, TRUE, FALSE))
! 284: {
! 285: return this->app.out_done;
! 286: }
! 287: return -1;
! 288: }
! 289:
! 290: METHOD(tls_socket_t, splice, bool,
! 291: private_tls_socket_t *this, int rfd, int wfd)
! 292: {
! 293: char buf[PLAIN_BUF_SIZE], *pos;
! 294: ssize_t in, out;
! 295: bool old, plain_eof = FALSE, crypto_eof = FALSE;
! 296: struct pollfd pfd[] = {
! 297: { .fd = this->fd, .events = POLLIN, },
! 298: { .fd = rfd, .events = POLLIN, },
! 299: };
! 300:
! 301: while (!plain_eof && !crypto_eof)
! 302: {
! 303: old = thread_cancelability(TRUE);
! 304: in = poll(pfd, countof(pfd), -1);
! 305: thread_cancelability(old);
! 306: if (in == -1)
! 307: {
! 308: DBG1(DBG_TLS, "TLS select error: %s", strerror(errno));
! 309: return FALSE;
! 310: }
! 311: while (!plain_eof && pfd[0].revents & (POLLIN | POLLHUP | POLLNVAL))
! 312: {
! 313: in = read_(this, buf, sizeof(buf), FALSE);
! 314: switch (in)
! 315: {
! 316: case 0:
! 317: plain_eof = TRUE;
! 318: break;
! 319: case -1:
! 320: if (errno != EWOULDBLOCK)
! 321: {
! 322: DBG1(DBG_TLS, "TLS read error: %s", strerror(errno));
! 323: return FALSE;
! 324: }
! 325: break;
! 326: default:
! 327: pos = buf;
! 328: while (in)
! 329: {
! 330: out = write(wfd, pos, in);
! 331: if (out == -1)
! 332: {
! 333: DBG1(DBG_TLS, "TLS plain write error: %s",
! 334: strerror(errno));
! 335: return FALSE;
! 336: }
! 337: in -= out;
! 338: pos += out;
! 339: }
! 340: continue;
! 341: }
! 342: break;
! 343: }
! 344: if (!crypto_eof && pfd[1].revents & (POLLIN | POLLHUP | POLLNVAL))
! 345: {
! 346: in = read(rfd, buf, sizeof(buf));
! 347: switch (in)
! 348: {
! 349: case 0:
! 350: crypto_eof = TRUE;
! 351: break;
! 352: case -1:
! 353: DBG1(DBG_TLS, "TLS plain read error: %s", strerror(errno));
! 354: return FALSE;
! 355: default:
! 356: pos = buf;
! 357: while (in)
! 358: {
! 359: out = write_(this, pos, in);
! 360: if (out == -1)
! 361: {
! 362: DBG1(DBG_TLS, "TLS write error");
! 363: return FALSE;
! 364: }
! 365: in -= out;
! 366: pos += out;
! 367: }
! 368: break;
! 369: }
! 370: }
! 371: }
! 372: return TRUE;
! 373: }
! 374:
! 375: METHOD(tls_socket_t, get_fd, int,
! 376: private_tls_socket_t *this)
! 377: {
! 378: return this->fd;
! 379: }
! 380:
! 381: METHOD(tls_socket_t, get_server_id, identification_t*,
! 382: private_tls_socket_t *this)
! 383: {
! 384: return this->tls->get_server_id(this->tls);
! 385: }
! 386:
! 387: METHOD(tls_socket_t, get_peer_id, identification_t*,
! 388: private_tls_socket_t *this)
! 389: {
! 390: return this->tls->get_peer_id(this->tls);
! 391: }
! 392:
! 393: METHOD(tls_socket_t, destroy, void,
! 394: private_tls_socket_t *this)
! 395: {
! 396: /* send a TLS close notify if not done yet */
! 397: this->app.close = TRUE;
! 398: write_(this, NULL, 0);
! 399: free(this->app.cache.ptr);
! 400: this->tls->destroy(this->tls);
! 401: free(this);
! 402: }
! 403:
! 404: /**
! 405: * See header
! 406: */
! 407: tls_socket_t *tls_socket_create(bool is_server, identification_t *server,
! 408: identification_t *peer, int fd, tls_cache_t *cache,
! 409: tls_version_t max_version, bool nullok)
! 410: {
! 411: private_tls_socket_t *this;
! 412: tls_purpose_t purpose;
! 413:
! 414: INIT(this,
! 415: .public = {
! 416: .read = _read_,
! 417: .write = _write_,
! 418: .splice = _splice,
! 419: .get_fd = _get_fd,
! 420: .get_server_id = _get_server_id,
! 421: .get_peer_id = _get_peer_id,
! 422: .destroy = _destroy,
! 423: },
! 424: .app = {
! 425: .application = {
! 426: .build = _build,
! 427: .process = _process,
! 428: .destroy = (void*)nop,
! 429: },
! 430: },
! 431: .fd = fd,
! 432: );
! 433:
! 434: if (nullok)
! 435: {
! 436: purpose = TLS_PURPOSE_GENERIC_NULLOK;
! 437: }
! 438: else
! 439: {
! 440: purpose = TLS_PURPOSE_GENERIC;
! 441: }
! 442:
! 443: this->tls = tls_create(is_server, server, peer, purpose,
! 444: &this->app.application, cache);
! 445: if (!this->tls)
! 446: {
! 447: free(this);
! 448: return NULL;
! 449: }
! 450: this->tls->set_version(this->tls, max_version);
! 451:
! 452: return &this->public;
! 453: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>