blob: d840b1b880548b12309d8f9572abb49cb4b821c1
1 | /* |
2 | * Copyright (c) 2013 INSIDE Secure Corporation |
3 | * Copyright (c) PeerSec Networks, 2002-2011 |
4 | * All Rights Reserved |
5 | * |
6 | * The latest version of this code is available at http://www.matrixssl.org |
7 | * |
8 | * This software is open source; you can redistribute it and/or modify |
9 | * it under the terms of the GNU General Public License as published by |
10 | * the Free Software Foundation; either version 2 of the License, or |
11 | * (at your option) any later version. |
12 | * |
13 | * This program is distributed in WITHOUT ANY WARRANTY; without even the |
14 | * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
15 | * See the GNU General Public License for more details. |
16 | * |
17 | * You should have received a copy of the GNU General Public License |
18 | * along with this program; if not, write to the Free Software |
19 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
20 | * http://www.gnu.org/copyleft/gpl.html |
21 | */ |
22 | #include <errno.h> |
23 | #include <stdlib.h> |
24 | #include <unistd.h> |
25 | #include <stdarg.h> |
26 | #include <fcntl.h> |
27 | #include <stdio.h> |
28 | #include <time.h> |
29 | #include <poll.h> |
30 | #include <sys/socket.h> |
31 | |
32 | #include "matrixssl/matrixsslApi.h" |
33 | |
34 | //#warning "DO NOT USE THESE DEFAULT KEYS IN PRODUCTION ENVIRONMENTS." |
35 | |
36 | /* |
37 | * If supporting client authentication, pick ONE identity to auto select a |
38 | * certificate and private key that support desired algorithms. |
39 | */ |
40 | #define ID_RSA /* RSA Certificate and Key */ |
41 | |
42 | #define USE_HEADER_KEYS |
43 | |
44 | /* If the algorithm type is supported, load a CA for it */ |
45 | #ifdef USE_HEADER_KEYS |
46 | /* CAs */ |
47 | # include "sampleCerts/RSA/ALL_RSA_CAS.h" |
48 | /* Identity Certs and Keys for use with Client Authentication */ |
49 | # ifdef ID_RSA |
50 | # define EXAMPLE_RSA_KEYS |
51 | # include "sampleCerts/RSA/2048_RSA.h" |
52 | # include "sampleCerts/RSA/2048_RSA_KEY.h" |
53 | # endif |
54 | #endif |
55 | |
56 | static ssize_t safe_write(int fd, const void *buf, size_t count) |
57 | { |
58 | ssize_t n; |
59 | |
60 | do { |
61 | n = write(fd, buf, count); |
62 | } while (n < 0 && errno == EINTR); |
63 | |
64 | return n; |
65 | } |
66 | |
67 | static ssize_t full_write(int fd, const void *buf, size_t len) |
68 | { |
69 | ssize_t cc; |
70 | ssize_t total; |
71 | |
72 | total = 0; |
73 | |
74 | while (len) { |
75 | cc = safe_write(fd, buf, len); |
76 | |
77 | if (cc < 0) { |
78 | if (total) { |
79 | /* we already wrote some! */ |
80 | /* user can do another write to know the error code */ |
81 | return total; |
82 | } |
83 | return cc; /* write() returns -1 on failure. */ |
84 | } |
85 | |
86 | total += cc; |
87 | buf = ((const char *)buf) + cc; |
88 | len -= cc; |
89 | } |
90 | |
91 | return total; |
92 | } |
93 | |
94 | static void say(const char *s, ...) |
95 | { |
96 | char buf[256]; |
97 | va_list p; |
98 | int sz; |
99 | |
100 | va_start(p, s); |
101 | sz = vsnprintf(buf, sizeof(buf), s, p); |
102 | full_write(STDERR_FILENO, buf, sz >= 0 && sz < sizeof(buf) ? sz : strlen(buf)); |
103 | va_end(p); |
104 | } |
105 | |
106 | static void die(const char *s, ...) |
107 | { |
108 | char buf[256]; |
109 | va_list p; |
110 | int sz; |
111 | |
112 | va_start(p, s); |
113 | sz = vsnprintf(buf, sizeof(buf), s, p); |
114 | full_write(STDERR_FILENO, buf, sz >= 0 && sz < sizeof(buf) ? sz : strlen(buf)); |
115 | exit(1); |
116 | va_end(p); |
117 | } |
118 | |
119 | #if 0 |
120 | # define dbg(...) say(__VA_ARGS__) |
121 | #else |
122 | # define dbg(...) ((void)0) |
123 | #endif |
124 | |
125 | static struct pollfd pfd[2] = { |
126 | { -1, POLLIN|POLLERR|POLLHUP, 0 }, |
127 | { -1, POLLIN|POLLERR|POLLHUP, 0 }, |
128 | }; |
129 | #define STDIN pfd[0] |
130 | #define NETWORK pfd[1] |
131 | #define STDIN_READY() (pfd[0].revents & (POLLIN|POLLERR|POLLHUP)) |
132 | #define NETWORK_READY() (pfd[1].revents & (POLLIN|POLLERR|POLLHUP)) |
133 | |
134 | static int wait_for_input(void) |
135 | { |
136 | if (STDIN.fd == NETWORK.fd) /* means both are -1 */ |
137 | exit(0); |
138 | dbg("polling\n"); |
139 | STDIN.revents = NETWORK.revents = 0; |
140 | return poll(pfd, 2, -1); |
141 | } |
142 | |
143 | static int32 certCb(ssl_t *ssl, psX509Cert_t *cert, int32 alert) |
144 | { |
145 | /* Example to allow anonymous connections based on a define */ |
146 | if (alert > 0) { |
147 | return SSL_ALLOW_ANON_CONNECTION; // = 254 |
148 | } |
149 | #if 0 |
150 | /* Validate the 'not before' and 'not after' dates, etc */ |
151 | return PS_FAILURE; /* if we don't like this cert */ |
152 | #endif |
153 | return PS_SUCCESS; |
154 | } |
155 | |
156 | static void close_conn_and_exit(ssl_t *ssl, int fd) |
157 | { |
158 | unsigned char *buf; |
159 | int len; |
160 | |
161 | fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK); |
162 | /* Quick attempt to send a closure alert, don't worry about failure */ |
163 | if (matrixSslEncodeClosureAlert(ssl) >= 0) { |
164 | len = matrixSslGetOutdata(ssl, &buf); |
165 | if (len > 0) { |
166 | len = safe_write(fd, buf, len); |
167 | //if (len > 0) { |
168 | // matrixSslSentData(ssl, len); |
169 | //} |
170 | } |
171 | } |
172 | //matrixSslDeleteSession(ssl); |
173 | shutdown(fd, SHUT_WR); |
174 | exit(0); |
175 | } |
176 | |
177 | static int encode_data(ssl_t *ssl, const void *data, int len) |
178 | { |
179 | unsigned char *buf; |
180 | int available; |
181 | |
182 | available = matrixSslGetWritebuf(ssl, &buf, len); |
183 | if (available < 0) |
184 | die("matrixSslGetWritebuf\n"); |
185 | if (len > available) |
186 | die("len > available\n"); |
187 | memcpy(buf, data, len); |
188 | if (matrixSslEncodeWritebuf(ssl, len) < 0) |
189 | die("matrixSslEncodeWritebuf\n"); |
190 | return len; |
191 | } |
192 | |
193 | static void flush_to_net(ssl_t *ssl, int fd) |
194 | { |
195 | int rc; |
196 | int len; |
197 | unsigned char *buf; |
198 | |
199 | while ((len = matrixSslGetOutdata(ssl, &buf)) > 0) { |
200 | dbg("writing net %d bytes\n", len); |
201 | if (full_write(fd, buf, len) != len) |
202 | die("write to network\n"); |
203 | rc = matrixSslSentData(ssl, len); |
204 | if (rc < 0) |
205 | die("matrixSslSentData\n"); |
206 | } |
207 | } |
208 | |
209 | static void do_io_until_eof_and_exit(int fd, sslKeys_t *keys) |
210 | { |
211 | int rc; |
212 | int len; |
213 | uint32_t len32u; |
214 | sslSessionId_t *sid; |
215 | ssl_t *ssl; |
216 | unsigned char *buf; |
217 | |
218 | NETWORK.fd = fd; |
219 | /* Note! STDIN.fd is disabled (-1) until SSL handshake is over: |
220 | * we do not attempt to feed any user data to MatrixSSL |
221 | * before it is ready. |
222 | */ |
223 | |
224 | matrixSslNewSessionId(&sid); |
225 | rc = matrixSslNewClientSession(&ssl, keys, sid, 0, certCb, NULL, NULL, 0); |
226 | dbg("matrixSslNewClientSession:rc=%d\n", rc); |
227 | if (rc != MATRIXSSL_REQUEST_SEND) |
228 | die("matrixSslNewClientSession\n"); |
229 | |
230 | len = 0; /* only to suppress compiler warning */ |
231 | again: |
232 | switch (rc) { |
233 | case MATRIXSSL_REQUEST_SEND: |
234 | dbg("MATRIXSSL_REQUEST_SEND\n"); |
235 | flush_to_net(ssl, fd); |
236 | goto poll_input; |
237 | |
238 | case 0: |
239 | dbg("rc==0\n"); |
240 | flush_to_net(ssl, fd); |
241 | goto poll_input; |
242 | |
243 | case MATRIXSSL_REQUEST_CLOSE: |
244 | /* what does this mean if we are here? */ |
245 | dbg("MATRIXSSL_REQUEST_CLOSE\n"); |
246 | close_conn_and_exit(ssl, fd); |
247 | |
248 | case MATRIXSSL_HANDSHAKE_COMPLETE: |
249 | dbg("MATRIXSSL_HANDSHAKE_COMPLETE\n"); |
250 | /* Init complete, can start reading local user's data: */ |
251 | STDIN.fd = STDIN_FILENO; |
252 | poll_input: |
253 | wait_for_input(); |
254 | if (STDIN_READY()) { |
255 | char ibuf[4 * 1024]; |
256 | dbg("reading stdin\n"); |
257 | len = read(STDIN_FILENO, ibuf, sizeof(ibuf)); |
258 | if (len < 0) |
259 | die("read error on stdin\n"); |
260 | if (len == 0) |
261 | STDIN.fd = -1; |
262 | else { |
263 | len = encode_data(ssl, ibuf, len); |
264 | if (len) { |
265 | rc = MATRIXSSL_REQUEST_SEND; |
266 | dbg("rc=%d\n", rc); |
267 | goto again; |
268 | } |
269 | } |
270 | } |
271 | read_network: |
272 | if (NETWORK_READY()) { |
273 | dbg("%s%s%s\n", |
274 | (pfd[1].revents & POLLIN) ? "POLLIN" : "", |
275 | (pfd[1].revents & POLLERR) ? "|POLLERR" : "", |
276 | (pfd[1].revents & POLLHUP) ? "|POLLHUP" : "" |
277 | ); |
278 | len = matrixSslGetReadbuf(ssl, &buf); |
279 | if (len <= 0) |
280 | die("matrixSslGetReadbuf\n"); |
281 | dbg("reading net up to %d\n", len); |
282 | len = read(fd, buf, len); |
283 | dbg("reading net:%d\n", len); |
284 | if (len < 0) |
285 | die("read error on network\n"); |
286 | if (len == 0) /*eof*/ |
287 | NETWORK.fd = -1; |
288 | len32u = len; |
289 | rc = matrixSslReceivedData(ssl, len, &buf, &len32u); |
290 | dbg("matrixSslReceivedData:rc=%d\n", rc); |
291 | len = len32u; |
292 | if (rc < 0) |
293 | die("matrixSslReceivedData\n"); |
294 | } |
295 | goto again; |
296 | |
297 | case MATRIXSSL_APP_DATA: |
298 | dbg("MATRIXSSL_APP_DATA: writing stdout\n"); |
299 | do { |
300 | if (full_write(STDOUT_FILENO, buf, len) != len) |
301 | die("write to stdout\n"); |
302 | len32u = len; |
303 | rc = matrixSslProcessedData(ssl, &buf, &len32u); |
304 | //this was seen returning rc=0: |
305 | dbg("matrixSslProcessedData:rc=%d\n", rc); |
306 | len = len32u; |
307 | } while (rc == MATRIXSSL_APP_DATA); |
308 | if (pfd[1].fd == -1) { |
309 | /* Already saw EOF on network, and we processed |
310 | * and wrote out all ssl data. Signal it: |
311 | */ |
312 | close(STDOUT_FILENO); |
313 | } |
314 | goto again; |
315 | |
316 | case MATRIXSSL_REQUEST_RECV: |
317 | dbg("MATRIXSSL_REQUEST_RECV\n"); |
318 | wait_for_input(); |
319 | goto read_network; |
320 | |
321 | case MATRIXSSL_RECEIVED_ALERT: |
322 | dbg("MATRIXSSL_RECEIVED_ALERT\n"); |
323 | /* The first byte of the buffer is the level */ |
324 | /* The second byte is the description */ |
325 | if (buf[0] == SSL_ALERT_LEVEL_FATAL) |
326 | die("Fatal alert\n"); |
327 | /* Closure alert is normal (and best) way to close */ |
328 | if (buf[1] == SSL_ALERT_CLOSE_NOTIFY) |
329 | close_conn_and_exit(ssl, fd); |
330 | die("Warning alert\n"); |
331 | len32u = len; |
332 | rc = matrixSslProcessedData(ssl, &buf, &len32u); |
333 | dbg("matrixSslProcessedData:rc=%d\n", rc); |
334 | len = len32u; |
335 | goto again; |
336 | |
337 | default: |
338 | /* If rc < 0 it is an error */ |
339 | die("bad rc:%d\n", rc); |
340 | } |
341 | } |
342 | |
343 | static sslKeys_t* make_keys(void) |
344 | { |
345 | int rc, CAstreamLen; |
346 | char *CAstream; |
347 | sslKeys_t *keys; |
348 | |
349 | if (matrixSslNewKeys(&keys) < 0) |
350 | die("matrixSslNewKeys\n"); |
351 | |
352 | #ifdef USE_HEADER_KEYS |
353 | /* |
354 | * In-memory based keys |
355 | * Build the CA list first for potential client auth usage |
356 | */ |
357 | CAstream = NULL; |
358 | CAstreamLen = sizeof(RSACAS); |
359 | if (CAstreamLen > 0) { |
360 | CAstream = psMalloc(NULL, CAstreamLen); |
361 | memcpy(CAstream, RSACAS, sizeof(RSACAS)); |
362 | } |
363 | |
364 | #ifdef ID_RSA |
365 | rc = matrixSslLoadRsaKeysMem(keys, RSA2048, sizeof(RSA2048), |
366 | RSA2048KEY, sizeof(RSA2048KEY), (unsigned char*)CAstream, |
367 | CAstreamLen); |
368 | if (rc < 0) |
369 | die("matrixSslLoadRsaKeysMem\n"); |
370 | #endif |
371 | |
372 | if (CAstream) |
373 | psFree(CAstream); |
374 | #endif /* USE_HEADER_KEYS */ |
375 | return keys; |
376 | } |
377 | |
378 | int main(int argc, char **argv) |
379 | { |
380 | int fd; |
381 | char *fd_str; |
382 | |
383 | if (!argv[1]) |
384 | die("Syntax error\n"); |
385 | if (argv[1][0] != '-') |
386 | die("Syntax error\n"); |
387 | if (argv[1][1] != 'd') |
388 | die("Syntax error\n"); |
389 | fd_str = argv[1] + 2; |
390 | if (!fd_str[0]) |
391 | fd_str = argv[2]; |
392 | if (!fd_str || fd_str[0] < '0' || fd_str[0] > '9') |
393 | die("Syntax error\n"); |
394 | |
395 | fd = atoi(fd_str); |
396 | if (fd < 3) |
397 | die("Syntax error\n"); |
398 | |
399 | if (matrixSslOpen() < 0) |
400 | die("matrixSslOpen\n"); |
401 | |
402 | do_io_until_eof_and_exit(fd, make_keys()); |
403 | /* does not return */ |
404 | |
405 | return 0; |
406 | } |
407 |