diff options
Diffstat (limited to 'src/libtls')
-rw-r--r-- | src/libtls/Makefile.in | 40 | ||||
-rw-r--r-- | src/libtls/tls.c | 32 | ||||
-rw-r--r-- | src/libtls/tls.h | 14 | ||||
-rw-r--r-- | src/libtls/tls_fragmentation.c | 8 | ||||
-rw-r--r-- | src/libtls/tls_handshake.h | 14 | ||||
-rw-r--r-- | src/libtls/tls_peer.c | 23 | ||||
-rw-r--r-- | src/libtls/tls_peer.h | 8 | ||||
-rw-r--r-- | src/libtls/tls_server.c | 54 | ||||
-rw-r--r-- | src/libtls/tls_server.h | 7 | ||||
-rw-r--r-- | src/libtls/tls_socket.c | 272 | ||||
-rw-r--r-- | src/libtls/tls_socket.h | 37 |
11 files changed, 385 insertions, 124 deletions
diff --git a/src/libtls/Makefile.in b/src/libtls/Makefile.in index d54545aac..a98c5a6d6 100644 --- a/src/libtls/Makefile.in +++ b/src/libtls/Makefile.in @@ -1,4 +1,4 @@ -# Makefile.in generated by automake 1.11.3 from Makefile.am. +# Makefile.in generated by automake 1.11.6 from Makefile.am. # @configure_input@ # Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, @@ -17,6 +17,23 @@ VPATH = @srcdir@ +am__make_dryrun = \ + { \ + am__dry=no; \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + echo 'am--echo: ; @echo "AM" OK' | $(MAKE) -f - 2>/dev/null \ + | grep '^AM OK$$' >/dev/null || am__dry=yes;; \ + *) \ + for am__flg in $$MAKEFLAGS; do \ + case $$am__flg in \ + *=*|--*) ;; \ + *n*) am__dry=yes; break;; \ + esac; \ + done;; \ + esac; \ + test $$am__dry = yes; \ + } pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ @@ -105,6 +122,11 @@ LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ $(LDFLAGS) -o $@ SOURCES = $(libtls_la_SOURCES) DIST_SOURCES = $(libtls_la_SOURCES) +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac am__nobase_tls_include_HEADERS_DIST = tls_protection.h \ tls_compression.h tls_fragmentation.h tls_alert.h tls_crypto.h \ tls_prf.h tls_socket.h tls_eap.h tls_cache.h tls_peer.h \ @@ -126,6 +148,8 @@ BTLIB = @BTLIB@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ +CHECK_CFLAGS = @CHECK_CFLAGS@ +CHECK_LIBS = @CHECK_LIBS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CYGPATH_W = @CYGPATH_W@ @@ -142,6 +166,7 @@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GPERF = @GPERF@ +GPRBUILD = @GPRBUILD@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ @@ -210,8 +235,6 @@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ attest_plugins = @attest_plugins@ -axis2c_CFLAGS = @axis2c_CFLAGS@ -axis2c_LIBS = @axis2c_LIBS@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ @@ -267,7 +290,6 @@ nm_ca_dir = @nm_ca_dir@ nm_plugins = @nm_plugins@ oldincludedir = @oldincludedir@ openac_plugins = @openac_plugins@ -p_plugins = @p_plugins@ pcsclite_CFLAGS = @pcsclite_CFLAGS@ pcsclite_LIBS = @pcsclite_LIBS@ pdfdir = @pdfdir@ @@ -350,7 +372,6 @@ $(ACLOCAL_M4): $(am__aclocal_m4_deps) $(am__aclocal_m4_deps): install-ipseclibLTLIBRARIES: $(ipseclib_LTLIBRARIES) @$(NORMAL_INSTALL) - test -z "$(ipseclibdir)" || $(MKDIR_P) "$(DESTDIR)$(ipseclibdir)" @list='$(ipseclib_LTLIBRARIES)'; test -n "$(ipseclibdir)" || list=; \ list2=; for p in $$list; do \ if test -f $$p; then \ @@ -358,6 +379,8 @@ install-ipseclibLTLIBRARIES: $(ipseclib_LTLIBRARIES) else :; fi; \ done; \ test -z "$$list2" || { \ + echo " $(MKDIR_P) '$(DESTDIR)$(ipseclibdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(ipseclibdir)" || exit 1; \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(ipseclibdir)'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(ipseclibdir)"; \ } @@ -429,15 +452,18 @@ clean-libtool: -rm -rf .libs _libs install-nobase_tls_includeHEADERS: $(nobase_tls_include_HEADERS) @$(NORMAL_INSTALL) - test -z "$(tls_includedir)" || $(MKDIR_P) "$(DESTDIR)$(tls_includedir)" @list='$(nobase_tls_include_HEADERS)'; test -n "$(tls_includedir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(tls_includedir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(tls_includedir)" || exit 1; \ + fi; \ $(am__nobase_list) | while read dir files; do \ xfiles=; for file in $$files; do \ if test -f "$$file"; then xfiles="$$xfiles $$file"; \ else xfiles="$$xfiles $(srcdir)/$$file"; fi; done; \ test -z "$$xfiles" || { \ test "x$$dir" = x. || { \ - echo "$(MKDIR_P) '$(DESTDIR)$(tls_includedir)/$$dir'"; \ + echo " $(MKDIR_P) '$(DESTDIR)$(tls_includedir)/$$dir'"; \ $(MKDIR_P) "$(DESTDIR)$(tls_includedir)/$$dir"; }; \ echo " $(INSTALL_HEADER) $$xfiles '$(DESTDIR)$(tls_includedir)/$$dir'"; \ $(INSTALL_HEADER) $$xfiles "$(DESTDIR)$(tls_includedir)/$$dir" || exit $$?; }; \ diff --git a/src/libtls/tls.c b/src/libtls/tls.c index dea08e3eb..6d33d843d 100644 --- a/src/libtls/tls.c +++ b/src/libtls/tls.c @@ -107,16 +107,6 @@ struct private_tls_t { bool is_server; /** - * Server identity - */ - identification_t *server; - - /** - * Peer identity - */ - identification_t *peer; - - /** * Negotiated TLS version */ tls_version_t version; @@ -359,6 +349,18 @@ METHOD(tls_t, is_server, bool, return this->is_server; } +METHOD(tls_t, get_server_id, identification_t*, + private_tls_t *this) +{ + return this->handshake->get_server_id(this->handshake); +} + +METHOD(tls_t, get_peer_id, identification_t*, + private_tls_t *this) +{ + return this->handshake->get_peer_id(this->handshake); +} + METHOD(tls_t, get_version, tls_version_t, private_tls_t *this) { @@ -421,8 +423,6 @@ METHOD(tls_t, destroy, void, this->fragmentation->destroy(this->fragmentation); this->crypto->destroy(this->crypto); this->handshake->destroy(this->handshake); - DESTROY_IF(this->peer); - this->server->destroy(this->server); DESTROY_IF(this->application); this->alert->destroy(this->alert); @@ -457,6 +457,8 @@ tls_t *tls_create(bool is_server, identification_t *server, .process = _process, .build = _build, .is_server = _is_server, + .get_server_id = _get_server_id, + .get_peer_id = _get_peer_id, .get_version = _get_version, .set_version = _set_version, .get_purpose = _get_purpose, @@ -466,8 +468,6 @@ tls_t *tls_create(bool is_server, identification_t *server, }, .is_server = is_server, .version = TLS_1_2, - .server = server->clone(server), - .peer = peer ? peer->clone(peer) : NULL, .application = application, .purpose = purpose, ); @@ -477,12 +477,12 @@ tls_t *tls_create(bool is_server, identification_t *server, if (is_server) { this->handshake = &tls_server_create(&this->public, this->crypto, - this->alert, this->server, this->peer)->handshake; + this->alert, server, peer)->handshake; } else { this->handshake = &tls_peer_create(&this->public, this->crypto, - this->alert, this->peer, this->server)->handshake; + this->alert, peer, server)->handshake; } this->fragmentation = tls_fragmentation_create(this->handshake, this->alert, this->application); diff --git a/src/libtls/tls.h b/src/libtls/tls.h index 6b4876f73..7f45b1e09 100644 --- a/src/libtls/tls.h +++ b/src/libtls/tls.h @@ -193,6 +193,20 @@ struct tls_t { bool (*is_server)(tls_t *this); /** + * Return the server identity. + * + * @return server identity + */ + identification_t* (*get_server_id)(tls_t *this); + + /** + * Return the peer identity. + * + * @return peer identity + */ + identification_t* (*get_peer_id)(tls_t *this); + + /** * Get the negotiated TLS/SSL version. * * @return negotiated TLS version diff --git a/src/libtls/tls_fragmentation.c b/src/libtls/tls_fragmentation.c index c76376b43..6e4347e3c 100644 --- a/src/libtls/tls_fragmentation.c +++ b/src/libtls/tls_fragmentation.c @@ -223,7 +223,7 @@ static status_t process_application(private_tls_fragmentation_t *this, continue; case SUCCESS: this->application_finished = TRUE; - return SUCCESS; + /* FALL */ case FAILED: default: this->alert->add(this->alert, TLS_FATAL, TLS_CLOSE_NOTIFY); @@ -368,7 +368,7 @@ static status_t build_application(private_tls_fragmentation_t *this) break; case SUCCESS: this->application_finished = TRUE; - break; + /* FALL */ case FAILED: default: this->alert->add(this->alert, TLS_FATAL, TLS_CLOSE_NOTIFY); @@ -391,6 +391,10 @@ METHOD(tls_fragmentation_t, build, status_t, this->state = ALERT_SENT; return INVALID_STATE; case ALERT_SENT: + if (this->application_finished) + { + return SUCCESS; + } return FAILED; case ALERT_NONE: break; diff --git a/src/libtls/tls_handshake.h b/src/libtls/tls_handshake.h index bea0024eb..7fa660c58 100644 --- a/src/libtls/tls_handshake.h +++ b/src/libtls/tls_handshake.h @@ -84,6 +84,20 @@ struct tls_handshake_t { bool (*finished)(tls_handshake_t *this); /** + * Get the peer identity authenticated/to authenticate during handshake. + * + * @return peer identity + */ + identification_t* (*get_peer_id)(tls_handshake_t *this); + + /** + * Get the server identity authenticated/to authenticate during handshake. + * + * @return server identity + */ + identification_t* (*get_server_id)(tls_handshake_t *this); + + /** * Destroy a tls_handshake_t. */ void (*destroy)(tls_handshake_t *this); diff --git a/src/libtls/tls_peer.c b/src/libtls/tls_peer.c index 622df4035..b429da300 100644 --- a/src/libtls/tls_peer.c +++ b/src/libtls/tls_peer.c @@ -665,6 +665,8 @@ METHOD(tls_handshake_t, process, status_t, { return process_certreq(this, reader); } + /* no cert request, server does not want to authenticate us */ + DESTROY_IF(this->peer); this->peer = NULL; /* fall through since TLS_CERTIFICATE_REQUEST is optional */ case STATE_CERTREQ_RECEIVED: @@ -850,6 +852,7 @@ static status_t send_certificate(private_tls_peer_t *this, { DBG1(DBG_TLS, "no TLS peer certificate found for '%Y', " "skipping client authentication", this->peer); + this->peer->destroy(this->peer); this->peer = NULL; } @@ -1132,11 +1135,25 @@ METHOD(tls_handshake_t, finished, bool, return this->state == STATE_FINISHED_RECEIVED; } +METHOD(tls_handshake_t, get_peer_id, identification_t*, + private_tls_peer_t *this) +{ + return this->peer; +} + +METHOD(tls_handshake_t, get_server_id, identification_t*, + private_tls_peer_t *this) +{ + return this->server; +} + METHOD(tls_handshake_t, destroy, void, private_tls_peer_t *this) { DESTROY_IF(this->private); DESTROY_IF(this->dh); + DESTROY_IF(this->peer); + this->server->destroy(this->server); this->peer_auth->destroy(this->peer_auth); this->server_auth->destroy(this->server_auth); free(this->hashsig.ptr); @@ -1161,6 +1178,8 @@ tls_peer_t *tls_peer_create(tls_t *tls, tls_crypto_t *crypto, tls_alert_t *alert .cipherspec_changed = _cipherspec_changed, .change_cipherspec = _change_cipherspec, .finished = _finished, + .get_peer_id = _get_peer_id, + .get_server_id = _get_server_id, .destroy = _destroy, }, }, @@ -1168,8 +1187,8 @@ tls_peer_t *tls_peer_create(tls_t *tls, tls_crypto_t *crypto, tls_alert_t *alert .tls = tls, .crypto = crypto, .alert = alert, - .peer = peer, - .server = server, + .peer = peer ? peer->clone(peer) : NULL, + .server = server->clone(server), .peer_auth = auth_cfg_create(), .server_auth = auth_cfg_create(), ); diff --git a/src/libtls/tls_peer.h b/src/libtls/tls_peer.h index f773ea72e..e4ff6f83c 100644 --- a/src/libtls/tls_peer.h +++ b/src/libtls/tls_peer.h @@ -41,11 +41,15 @@ struct tls_peer_t { /** * Create a tls_peer instance. -* + * + * If a peer identity is given, but the client does not get requested or is + * otherwise unable to perform client authentication, NULL is returned in + * tls_handshake_t.get_peer_id() instead of the peer identity. + * * @param tls TLS stack * @param crypto TLS crypto helper * @param alert TLS alert handler - * @param peer peer identity + * @param peer peer identity, NULL to skip client authentication * @param server server identity */ tls_peer_t *tls_peer_create(tls_t *tls, tls_crypto_t *crypto, tls_alert_t *alert, diff --git a/src/libtls/tls_server.c b/src/libtls/tls_server.c index ec42b67fc..aeb5a714f 100644 --- a/src/libtls/tls_server.c +++ b/src/libtls/tls_server.c @@ -80,6 +80,11 @@ struct private_tls_server_t { identification_t *peer; /** + * Is it acceptable if we couldn't verify the peer certificate? + */ + bool peer_auth_optional; + + /** * State we are in */ server_state_t state; @@ -367,6 +372,12 @@ static status_t process_certificate(private_tls_server_t *this, DBG1(DBG_TLS, "received TLS peer certificate '%Y'", cert->get_subject(cert)); first = FALSE; + if (this->peer == NULL) + { /* apply identity to authenticate */ + this->peer = cert->get_subject(cert); + this->peer = this->peer->clone(this->peer); + this->peer_auth_optional = TRUE; + } } else { @@ -550,13 +561,22 @@ static status_t process_cert_verify(private_tls_server_t *this, { DBG1(DBG_TLS, "no trusted certificate found for '%Y' to verify TLS peer", this->peer); - this->alert->add(this->alert, TLS_FATAL, TLS_CERTIFICATE_UNKNOWN); - return NEED_MORE; + if (!this->peer_auth_optional) + { /* client authentication is required */ + this->alert->add(this->alert, TLS_FATAL, TLS_CERTIFICATE_UNKNOWN); + return NEED_MORE; + } + /* reset peer identity, we couldn't authenticate it */ + this->peer->destroy(this->peer); + this->peer = NULL; + this->state = STATE_KEY_EXCHANGE_RECEIVED; + } + else + { + this->state = STATE_CERT_VERIFY_RECEIVED; } - this->crypto->append_handshake(this->crypto, TLS_CERTIFICATE_VERIFY, reader->peek(reader)); - this->state = STATE_CERT_VERIFY_RECEIVED; return NEED_MORE; } @@ -979,11 +999,7 @@ METHOD(tls_handshake_t, build, status_t, } /* otherwise fall through to next state */ case STATE_KEY_EXCHANGE_SENT: - if (this->peer) - { - return send_certificate_request(this, type, writer); - } - /* otherwise fall through to next state */ + return send_certificate_request(this, type, writer); case STATE_CERTREQ_SENT: return send_hello_done(this, type, writer); case STATE_CIPHERSPEC_CHANGED_OUT: @@ -1045,11 +1061,25 @@ METHOD(tls_handshake_t, finished, bool, return this->state == STATE_FINISHED_SENT; } +METHOD(tls_handshake_t, get_peer_id, identification_t*, + private_tls_server_t *this) +{ + return this->peer; +} + +METHOD(tls_handshake_t, get_server_id, identification_t*, + private_tls_server_t *this) +{ + return this->server; +} + METHOD(tls_handshake_t, destroy, void, private_tls_server_t *this) { DESTROY_IF(this->private); DESTROY_IF(this->dh); + DESTROY_IF(this->peer); + this->server->destroy(this->server); this->peer_auth->destroy(this->peer_auth); this->server_auth->destroy(this->server_auth); free(this->hashsig.ptr); @@ -1075,14 +1105,16 @@ tls_server_t *tls_server_create(tls_t *tls, .cipherspec_changed = _cipherspec_changed, .change_cipherspec = _change_cipherspec, .finished = _finished, + .get_peer_id = _get_peer_id, + .get_server_id = _get_server_id, .destroy = _destroy, }, }, .tls = tls, .crypto = crypto, .alert = alert, - .server = server, - .peer = peer, + .server = server->clone(server), + .peer = peer ? peer->clone(peer) : NULL, .state = STATE_INIT, .peer_auth = auth_cfg_create(), .server_auth = auth_cfg_create(), diff --git a/src/libtls/tls_server.h b/src/libtls/tls_server.h index 6289dc8eb..d6b8de153 100644 --- a/src/libtls/tls_server.h +++ b/src/libtls/tls_server.h @@ -42,11 +42,16 @@ struct tls_server_t { /** * Create a tls_server instance. * + * If a peer identity is given, the client must authenticate with a valid + * certificate for this identity, or the connection fails. If peer is NULL, + * but the client authenticates nonetheless, the authenticated identity + * gets returned by tls_handshake_t.get_peer_id(). + * * @param tls TLS stack * @param crypto TLS crypto helper * @param alert TLS alert handler * @param server server identity - * @param peer peer identity + * @param peer peer identity, or NULL */ tls_server_t *tls_server_create(tls_t *tls, tls_crypto_t *crypto, tls_alert_t *alert, diff --git a/src/libtls/tls_socket.c b/src/libtls/tls_socket.c index 75b714e30..4ba964000 100644 --- a/src/libtls/tls_socket.c +++ b/src/libtls/tls_socket.c @@ -42,14 +42,39 @@ struct private_tls_application_t { tls_application_t application; /** - * Chunk of data to send + * Output buffer to write to */ chunk_t out; /** - * Chunk of data received + * Number of bytes written to out + */ + size_t out_done; + + /** + * Input buffer to read to */ chunk_t in; + + /** + * Number of bytes read to in + */ + size_t in_done; + + /** + * Cached input data + */ + chunk_t cache; + + /** + * Bytes consumed in cache + */ + size_t cache_done; + + /** + * Close TLS connection? + */ + bool close; }; /** @@ -82,22 +107,44 @@ METHOD(tls_application_t, process, status_t, private_tls_application_t *this, bio_reader_t *reader) { chunk_t data; + size_t len; - if (!reader->read_data(reader, reader->remaining(reader), &data)) + if (this->close) { - return FAILED; + return SUCCESS; + } + len = min(reader->remaining(reader), this->in.len - this->in_done); + if (len) + { /* copy to read buffer as much as fits in */ + if (!reader->read_data(reader, len, &data)) + { + return FAILED; + } + memcpy(this->in.ptr + this->in_done, data.ptr, data.len); + this->in_done += data.len; + } + else + { /* read buffer is full, cache for next read */ + if (!reader->read_data(reader, reader->remaining(reader), &data)) + { + return FAILED; + } + this->cache = chunk_cat("mc", this->cache, data); } - this->in = chunk_cat("mc", this->in, data); return NEED_MORE; } METHOD(tls_application_t, build, status_t, private_tls_application_t *this, bio_writer_t *writer) { - if (this->out.len) + if (this->close) + { + return SUCCESS; + } + if (this->out.len > this->out_done) { writer->write_data(writer, this->out); - this->out = chunk_empty; + this->out_done = this->out.len; return NEED_MORE; } return INVALID_STATE; @@ -106,11 +153,12 @@ METHOD(tls_application_t, build, status_t, /** * TLS data exchange loop */ -static bool exchange(private_tls_socket_t *this, bool wr) +static bool exchange(private_tls_socket_t *this, bool wr, bool block) { char buf[CRYPTO_BUF_SIZE], *pos; - ssize_t len, out; - int round = 0; + ssize_t in, out; + size_t len; + int round = 0, flags; for (round = 0; TRUE; round++) { @@ -137,6 +185,8 @@ static bool exchange(private_tls_socket_t *this, bool wr) continue; case INVALID_STATE: break; + case SUCCESS: + return TRUE; default: return FALSE; } @@ -144,55 +194,97 @@ static bool exchange(private_tls_socket_t *this, bool wr) } if (wr) { - if (this->app.out.len == 0) + if (this->app.out_done == this->app.out.len) { /* all data written */ return TRUE; } } else { - if (this->app.in.len) - { /* some data received */ + if (this->app.in_done == this->app.in.len) + { /* buffer fully received */ return TRUE; } - if (round > 0) - { /* did some handshaking, return empty chunk to not block */ - return TRUE; + } + + flags = 0; + if (this->app.out_done == this->app.out.len) + { + if (!block || this->app.in_done) + { + flags |= MSG_DONTWAIT; } } - len = read(this->fd, buf, sizeof(buf)); - if (len <= 0) + in = recv(this->fd, buf, sizeof(buf), flags); + if (in < 0) { + if (errno == EAGAIN || errno == EWOULDBLOCK) + { + if (this->app.in_done == 0) + { + /* reading, nothing got yet, and call would block */ + errno = EWOULDBLOCK; + this->app.in_done = -1; + } + return TRUE; + } return FALSE; } - if (this->tls->process(this->tls, buf, len) != NEED_MORE) + if (in == 0) + { /* EOF */ + return TRUE; + } + switch (this->tls->process(this->tls, buf, in)) { - return FALSE; + case NEED_MORE: + break; + case SUCCESS: + return TRUE; + default: + return FALSE; } } } -METHOD(tls_socket_t, read_, bool, - private_tls_socket_t *this, chunk_t *buf) +METHOD(tls_socket_t, read_, ssize_t, + private_tls_socket_t *this, void *buf, size_t len, bool block) { - if (exchange(this, FALSE)) + if (this->app.cache.len) { - *buf = this->app.in; - this->app.in = chunk_empty; - return TRUE; + size_t cache; + + cache = min(len, this->app.cache.len - this->app.cache_done); + memcpy(buf, this->app.cache.ptr + this->app.cache_done, cache); + + this->app.cache_done += cache; + if (this->app.cache_done == this->app.cache.len) + { + chunk_free(&this->app.cache); + this->app.cache_done = 0; + } + return cache; } - return FALSE; + this->app.in.ptr = buf; + this->app.in.len = len; + this->app.in_done = 0; + if (exchange(this, FALSE, block)) + { + return this->app.in_done; + } + return -1; } -METHOD(tls_socket_t, write_, bool, - private_tls_socket_t *this, chunk_t buf) +METHOD(tls_socket_t, write_, ssize_t, + private_tls_socket_t *this, void *buf, size_t len) { - this->app.out = buf; - if (exchange(this, TRUE)) + this->app.out.ptr = buf; + this->app.out.len = len; + this->app.out_done = 0; + if (exchange(this, TRUE, FALSE)) { - return TRUE; + return this->app.out_done; } - return FALSE; + return -1; } METHOD(tls_socket_t, splice, bool, @@ -200,68 +292,85 @@ METHOD(tls_socket_t, splice, bool, { char buf[PLAIN_BUF_SIZE], *pos; fd_set set; - chunk_t data; - ssize_t len; - bool old; + ssize_t in, out; + bool old, plain_eof = FALSE, crypto_eof = FALSE; - while (TRUE) + while (!plain_eof && !crypto_eof) { FD_ZERO(&set); FD_SET(rfd, &set); FD_SET(this->fd, &set); old = thread_cancelability(TRUE); - len = select(max(rfd, this->fd) + 1, &set, NULL, NULL, NULL); + in = select(max(rfd, this->fd) + 1, &set, NULL, NULL, NULL); thread_cancelability(old); - if (len == -1) + if (in == -1) { DBG1(DBG_TLS, "TLS select error: %s", strerror(errno)); return FALSE; } - if (FD_ISSET(this->fd, &set)) + while (!plain_eof && FD_ISSET(this->fd, &set)) { - if (!read_(this, &data)) - { - DBG2(DBG_TLS, "TLS read error/disconnect"); - return TRUE; - } - pos = data.ptr; - while (data.len) + in = read_(this, buf, sizeof(buf), FALSE); + switch (in) { - len = write(wfd, pos, data.len); - if (len == -1) - { - free(data.ptr); - DBG1(DBG_TLS, "TLS plain write error: %s", strerror(errno)); - return FALSE; - } - data.len -= len; - pos += len; + case 0: + plain_eof = TRUE; + break; + case -1: + if (errno != EWOULDBLOCK) + { + DBG1(DBG_TLS, "TLS read error: %s", strerror(errno)); + return FALSE; + } + break; + default: + pos = buf; + while (in) + { + out = write(wfd, pos, in); + if (out == -1) + { + DBG1(DBG_TLS, "TLS plain write error: %s", + strerror(errno)); + return FALSE; + } + in -= out; + pos += out; + } + continue; } - free(data.ptr); + break; } - if (FD_ISSET(rfd, &set)) + if (!crypto_eof && FD_ISSET(rfd, &set)) { - len = read(rfd, buf, sizeof(buf)); - if (len > 0) - { - if (!write_(this, chunk_create(buf, len))) - { - DBG1(DBG_TLS, "TLS write error"); - return FALSE; - } - } - else + in = read(rfd, buf, sizeof(buf)); + switch (in) { - if (len < 0) - { + case 0: + crypto_eof = TRUE; + break; + case -1: DBG1(DBG_TLS, "TLS plain read error: %s", strerror(errno)); return FALSE; - } - return TRUE; + default: + pos = buf; + while (in) + { + out = write_(this, pos, in); + if (out == -1) + { + DBG1(DBG_TLS, "TLS write error"); + return FALSE; + } + in -= out; + pos += out; + } + break; } } } + return TRUE; } METHOD(tls_socket_t, get_fd, int, @@ -270,11 +379,26 @@ METHOD(tls_socket_t, get_fd, int, return this->fd; } +METHOD(tls_socket_t, get_server_id, identification_t*, + private_tls_socket_t *this) +{ + return this->tls->get_server_id(this->tls); +} + +METHOD(tls_socket_t, get_peer_id, identification_t*, + private_tls_socket_t *this) +{ + return this->tls->get_peer_id(this->tls); +} + METHOD(tls_socket_t, destroy, void, private_tls_socket_t *this) { + /* send a TLS close notify if not done yet */ + this->app.close = TRUE; + write_(this, NULL, 0); + free(this->app.cache.ptr); this->tls->destroy(this->tls); - free(this->app.in.ptr); free(this); } @@ -292,6 +416,8 @@ tls_socket_t *tls_socket_create(bool is_server, identification_t *server, .write = _write_, .splice = _splice, .get_fd = _get_fd, + .get_server_id = _get_server_id, + .get_peer_id = _get_peer_id, .destroy = _destroy, }, .app = { diff --git a/src/libtls/tls_socket.h b/src/libtls/tls_socket.h index edd05fd29..75130a4d3 100644 --- a/src/libtls/tls_socket.h +++ b/src/libtls/tls_socket.h @@ -35,24 +35,27 @@ typedef struct tls_socket_t tls_socket_t; struct tls_socket_t { /** - * Read data from secured socket, return allocated chunk. + * Read data from secured socket. * * This call is blocking, you may use select() on the underlying socket to - * wait for data. If the there was non-application data available, the - * read function can return an empty chunk. + * wait for data. If "block" is FALSE and no application data is available, + * the function returns -1 and sets errno to EWOULDBLOCK. * - * @param data pointer to allocate received data - * @return TRUE if data received successfully + * @param buf buffer to write received data to + * @param len size of buffer + * @param block TRUE to block this call, FALSE to fail if it would block + * @return number of bytes read, 0 on EOF, -1 on error */ - bool (*read)(tls_socket_t *this, chunk_t *data); + ssize_t (*read)(tls_socket_t *this, void *buf, size_t len, bool block); /** - * Write a chunk of data over the secured socket. + * Write data over the secured socket. * - * @param data data to send - * @return TRUE if data sent successfully + * @param buf data to send + * @param len number of bytes to write from buf + * @return number of bytes written, -1 on error */ - bool (*write)(tls_socket_t *this, chunk_t data); + ssize_t (*write)(tls_socket_t *this, void *buf, size_t len); /** * Read/write plain data from file descriptor. @@ -74,6 +77,20 @@ struct tls_socket_t { int (*get_fd)(tls_socket_t *this); /** + * Return the server identity. + * + * @return server identity + */ + identification_t* (*get_server_id)(tls_socket_t *this); + + /** + * Return the peer identity. + * + * @return peer identity + */ + identification_t* (*get_peer_id)(tls_socket_t *this); + + /** * Destroy a tls_socket_t. */ void (*destroy)(tls_socket_t *this); |