]>
Commit | Line | Data |
---|---|---|
17102f7b MW |
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 | ||
20 | #include <debug.h> | |
21 | ||
22 | typedef struct private_tls_socket_t private_tls_socket_t; | |
23 | typedef struct private_tls_application_t private_tls_application_t; | |
24 | ||
25 | struct private_tls_application_t { | |
26 | ||
27 | /** | |
28 | * Implements tls_application layer. | |
29 | */ | |
30 | tls_application_t application; | |
31 | ||
32 | /** | |
33 | * Chunk of data to send | |
34 | */ | |
35 | chunk_t out; | |
36 | ||
37 | /** | |
38 | * Chunk of data received | |
39 | */ | |
40 | chunk_t in; | |
41 | }; | |
42 | ||
43 | /** | |
44 | * Private data of an tls_socket_t object. | |
45 | */ | |
46 | struct private_tls_socket_t { | |
47 | ||
48 | /** | |
49 | * Public tls_socket_t interface. | |
50 | */ | |
51 | tls_socket_t public; | |
52 | ||
53 | /** | |
54 | * TLS application implementation | |
55 | */ | |
56 | private_tls_application_t app; | |
57 | ||
58 | /** | |
59 | * TLS stack | |
60 | */ | |
61 | tls_t *tls; | |
62 | ||
63 | /** | |
64 | * Underlying OS socket | |
65 | */ | |
66 | int fd; | |
67 | }; | |
68 | ||
69 | METHOD(tls_application_t, process, status_t, | |
7e432eff | 70 | private_tls_application_t *this, bio_reader_t *reader) |
17102f7b MW |
71 | { |
72 | chunk_t data; | |
73 | ||
74 | if (!reader->read_data(reader, reader->remaining(reader), &data)) | |
75 | { | |
76 | return FAILED; | |
77 | } | |
78 | this->in = chunk_cat("mc", this->in, data); | |
79 | return NEED_MORE; | |
80 | } | |
81 | ||
82 | METHOD(tls_application_t, build, status_t, | |
7e432eff | 83 | private_tls_application_t *this, bio_writer_t *writer) |
17102f7b MW |
84 | { |
85 | if (this->out.len) | |
86 | { | |
87 | writer->write_data(writer, this->out); | |
88 | this->out = chunk_empty; | |
89 | return NEED_MORE; | |
90 | } | |
91 | return INVALID_STATE; | |
92 | } | |
93 | ||
94 | /** | |
95 | * TLS data exchange loop | |
96 | */ | |
97 | static bool exchange(private_tls_socket_t *this, bool wr) | |
98 | { | |
ecd98efa | 99 | char buf[1024]; |
17102f7b MW |
100 | ssize_t len; |
101 | int round = 0; | |
102 | ||
103 | for (round = 0; TRUE; round++) | |
104 | { | |
ecd98efa | 105 | while (TRUE) |
17102f7b | 106 | { |
ecd98efa MW |
107 | len = sizeof(buf); |
108 | switch (this->tls->build(this->tls, buf, &len, NULL)) | |
17102f7b | 109 | { |
ecd98efa MW |
110 | case NEED_MORE: |
111 | case ALREADY_DONE: | |
112 | len = write(this->fd, buf, len); | |
113 | if (len == -1) | |
114 | { | |
115 | return FALSE; | |
116 | } | |
117 | continue; | |
118 | case INVALID_STATE: | |
119 | break; | |
120 | default: | |
121 | return FALSE; | |
17102f7b | 122 | } |
ecd98efa | 123 | break; |
17102f7b MW |
124 | } |
125 | if (wr) | |
126 | { | |
127 | if (this->app.out.len == 0) | |
128 | { /* all data written */ | |
129 | return TRUE; | |
130 | } | |
131 | } | |
132 | else | |
133 | { | |
134 | if (this->app.in.len) | |
135 | { /* some data received */ | |
136 | return TRUE; | |
137 | } | |
138 | if (round > 0) | |
139 | { /* did some handshaking, return empty chunk to not block */ | |
140 | return TRUE; | |
141 | } | |
142 | } | |
143 | len = read(this->fd, buf, sizeof(buf)); | |
144 | if (len <= 0) | |
145 | { | |
146 | return FALSE; | |
147 | } | |
ecd98efa | 148 | if (this->tls->process(this->tls, buf, len) != NEED_MORE) |
17102f7b MW |
149 | { |
150 | return FALSE; | |
151 | } | |
152 | } | |
153 | } | |
154 | ||
155 | METHOD(tls_socket_t, read_, bool, | |
156 | private_tls_socket_t *this, chunk_t *buf) | |
157 | { | |
158 | if (exchange(this, FALSE)) | |
159 | { | |
160 | *buf = this->app.in; | |
161 | this->app.in = chunk_empty; | |
162 | return TRUE; | |
163 | } | |
164 | return FALSE; | |
165 | } | |
166 | ||
167 | METHOD(tls_socket_t, write_, bool, | |
168 | private_tls_socket_t *this, chunk_t buf) | |
169 | { | |
170 | this->app.out = buf; | |
171 | if (exchange(this, TRUE)) | |
172 | { | |
173 | return TRUE; | |
174 | } | |
175 | return FALSE; | |
176 | } | |
177 | ||
6b012164 MW |
178 | METHOD(tls_socket_t, get_fd, int, |
179 | private_tls_socket_t *this) | |
180 | { | |
181 | return this->fd; | |
182 | } | |
183 | ||
17102f7b MW |
184 | METHOD(tls_socket_t, destroy, void, |
185 | private_tls_socket_t *this) | |
186 | { | |
187 | this->tls->destroy(this->tls); | |
188 | free(this->app.in.ptr); | |
189 | free(this); | |
190 | } | |
191 | ||
192 | /** | |
193 | * See header | |
194 | */ | |
195 | tls_socket_t *tls_socket_create(bool is_server, identification_t *server, | |
6a5c86b7 | 196 | identification_t *peer, int fd, tls_cache_t *cache) |
17102f7b MW |
197 | { |
198 | private_tls_socket_t *this; | |
199 | ||
200 | INIT(this, | |
201 | .public = { | |
202 | .read = _read_, | |
203 | .write = _write_, | |
6b012164 | 204 | .get_fd = _get_fd, |
17102f7b MW |
205 | .destroy = _destroy, |
206 | }, | |
207 | .app = { | |
208 | .application = { | |
209 | .build = _build, | |
210 | .process = _process, | |
211 | .destroy = (void*)nop, | |
212 | }, | |
213 | }, | |
214 | .fd = fd, | |
215 | ); | |
216 | ||
217 | this->tls = tls_create(is_server, server, peer, TLS_PURPOSE_GENERIC, | |
6a5c86b7 | 218 | &this->app.application, cache); |
17102f7b MW |
219 | if (!this->tls) |
220 | { | |
221 | free(this); | |
222 | return NULL; | |
223 | } | |
224 | ||
225 | return &this->public; | |
226 | } |