tls: move parse buffer to state structure
[siplcs.git] / src / core / sipe-tls.c
blobadf6abe39b1906a49cdce165392759dd97ae8963
1 /**
2 * @file sipe-tls.c
4 * pidgin-sipe
6 * Copyright (C) 2011 SIPE Project <http://sipe.sourceforge.net/>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 * TLS Protocol Version 1.0/1.1 - Handshake Messages
26 * TLS-DSK uses the handshake messages during authentication and session key
27 * exchange. This module *ONLY* implements this part of the TLS specification!
29 * Specification references:
31 * - RFC2246: http://www.ietf.org/rfc/rfc2246.txt
32 * - RFC3546: http://www.ietf.org/rfc/rfc3546.txt
33 * - RFC4346: http://www.ietf.org/rfc/rfc4346.txt
36 #include <stdlib.h>
37 #include <string.h>
38 #include <stdarg.h>
40 #include <glib.h>
42 #include "sipe-common.h"
43 #include "sipe-backend.h"
44 #include "sipe-cert-crypto.h"
45 #include "sipe-tls.h"
48 * Private part of TLS state tracking
50 enum tls_handshake_state {
51 TLS_HANDSHAKE_STATE_START,
52 TLS_HANDSHAKE_STATE_SERVER_HELLO,
53 TLS_HANDSHAKE_STATE_FINISHED,
54 TLS_HANDSHAKE_STATE_COMPLETED,
55 TLS_HANDSHAKE_STATE_FAILED
58 struct tls_internal_state {
59 struct sipe_tls_state common;
60 gpointer certificate;
61 enum tls_handshake_state state;
62 const guchar *parse_buffer;
63 gsize parse_length;
67 * TLS message debugging
69 static void debug_hex(GString *str,
70 struct tls_internal_state *state)
72 const guchar *bytes;
73 gsize length;
74 gint count;
76 if (!str) return;
78 bytes = state->parse_buffer;
79 length = state->parse_length;
80 count = -1;
82 while (length-- > 0) {
83 if (++count == 0) {
84 /* do nothing */;
85 } else if ((count % 16) == 0) {
86 g_string_append(str, "\n");
87 } else if ((count % 8) == 0) {
88 g_string_append(str, " ");
90 g_string_append_printf(str, " %02X", *bytes++);
92 g_string_append(str, "\n");
95 static void debug_print(GString *str,
96 const gchar *string)
98 if (str)
99 g_string_append(str, string);
102 static void debug_printf(GString *str,
103 const gchar *format,
104 ...) G_GNUC_PRINTF(2, 3);
105 static void debug_printf(GString *str,
106 const gchar *format,
107 ...)
109 va_list ap;
111 if (!str) return;
113 va_start(ap, format);
114 g_string_append_vprintf(str, format, ap);
115 va_end(ap);
120 * TLS message parsing
122 struct parse_descriptor {
123 int temporary;
126 struct msg_descriptor {
127 guint type;
128 const gchar *description;
129 const struct parse_descriptor *parse;
132 static void free_parsed_data(gpointer parsed_data)
134 if (!parsed_data)
135 return;
138 static gpointer generic_parser(struct tls_internal_state *state,
139 gboolean incoming,
140 GString *str,
141 const struct parse_descriptor *desc,
142 gpointer parsed_data)
144 /* temporary */
145 debug_hex(str, state);
146 (void)parsed_data;
147 (void)incoming;
148 (void)desc;
149 return(NULL);
152 #define TLS_HANDSHAKE_HEADER_LENGTH 4
153 #define TLS_HANDSHAKE_OFFSET_TYPE 0
154 #define TLS_HANDSHAKE_TYPE_CLIENT_HELLO 1
155 #define TLS_HANDSHAKE_TYPE_SERVER_HELLO 2
156 #define TLS_HANDSHAKE_TYPE_CERTIFICATE 11
157 #define TLS_HANDSHAKE_TYPE_CERTIFICATE_REQ 13
158 #define TLS_HANDSHAKE_TYPE_SERVER_HELLO_DONE 14
159 #define TLS_HANDSHAKE_OFFSET_LENGTH 1
161 static gpointer handshake_parse(struct tls_internal_state *state,
162 gboolean incoming,
163 GString *str)
165 static const struct msg_descriptor const handshake_descriptors[] = {
166 { TLS_HANDSHAKE_TYPE_CLIENT_HELLO, "Client Hello", NULL},
167 { TLS_HANDSHAKE_TYPE_SERVER_HELLO, "Server Hello", NULL},
168 { TLS_HANDSHAKE_TYPE_CERTIFICATE, "Certificate", NULL},
169 { TLS_HANDSHAKE_TYPE_CERTIFICATE_REQ, "Certificate Request", NULL},
170 { TLS_HANDSHAKE_TYPE_SERVER_HELLO_DONE, "Server Hello Done", NULL}
172 #define HANDSHAKE_DESCRIPTORS (sizeof(handshake_descriptors)/sizeof(struct msg_descriptor))
174 const guchar *bytes = state->parse_buffer;
175 gsize length = state->parse_length;
176 gpointer parsed_data = NULL;
177 gboolean success = FALSE;
179 while (length > 0) {
180 const struct msg_descriptor *desc;
181 gsize msg_length;
182 guint i, msg_type;
184 /* header check */
185 if (length < TLS_HANDSHAKE_HEADER_LENGTH) {
186 debug_print(str, "CORRUPTED HANDSHAKE HEADER");
187 break;
190 /* msg length check */
191 msg_length = (bytes[TLS_HANDSHAKE_OFFSET_LENGTH] << 16) +
192 (bytes[TLS_HANDSHAKE_OFFSET_LENGTH + 1] << 8) +
193 bytes[TLS_HANDSHAKE_OFFSET_LENGTH + 2];
194 if (msg_length > length) {
195 debug_print(str, "HANDSHAKE MESSAGE TOO LONG");
196 break;
199 /* msg type */
200 msg_type = bytes[TLS_HANDSHAKE_OFFSET_TYPE];
201 for (desc = handshake_descriptors, i = 0;
202 i < HANDSHAKE_DESCRIPTORS;
203 desc++, i++) {
204 if (msg_type == desc->type)
205 break;
208 debug_printf(str, "TLS handshake (%" G_GSIZE_FORMAT " bytes) (%d)",
209 msg_length, msg_type);
211 state->parse_buffer = bytes + TLS_HANDSHAKE_HEADER_LENGTH;
212 state->parse_length = msg_length;
214 if (i < HANDSHAKE_DESCRIPTORS) {
215 debug_printf(str, "%s\n", desc->description);
216 parsed_data = generic_parser(state,
217 incoming,
218 str,
219 desc->parse,
220 parsed_data);
221 /* temporary */
222 #if 0
223 if (!parsed_data)
224 break;
225 #endif
226 } else {
227 debug_print(str, "ignored\n");
228 debug_hex(str, state);
231 /* next message */
232 bytes += TLS_HANDSHAKE_HEADER_LENGTH + msg_length;
233 length -= TLS_HANDSHAKE_HEADER_LENGTH + msg_length;
234 if (length > 0) {
235 debug_print(str, "------\n");
236 } else {
237 success = TRUE;
241 if (!success) {
242 free_parsed_data(parsed_data);
243 parsed_data = NULL;
246 return(parsed_data);
249 #define TLS_RECORD_HEADER_LENGTH 5
250 #define TLS_RECORD_OFFSET_TYPE 0
251 #define TLS_RECORD_TYPE_HANDSHAKE 22
252 #define TLS_RECORD_OFFSET_MAJOR 1
253 #define TLS_RECORD_OFFSET_MINOR 2
254 #define TLS_RECORD_OFFSET_LENGTH 3
256 /* NOTE: we don't support record fragmentation */
257 static gpointer tls_record_parse(struct tls_internal_state *state,
258 gboolean incoming)
260 const guchar *bytes = incoming ? state->common.in_buffer : state->common.out_buffer;
261 gsize length = incoming ? state->common.in_length : state->common.out_length;
262 gpointer parsed_data = NULL;
263 GString *str = NULL;
264 const gchar *version = NULL;
265 guchar major;
266 guchar minor;
267 gsize record_length;
268 guint content_type;
270 if (sipe_backend_debug_enabled()) {
271 str = g_string_new("");
272 debug_printf(str, "TLS MESSAGE %s\n",
273 incoming ? "INCOMING" : "OUTGOING");
276 /* truncated header check */
277 if (length < TLS_RECORD_HEADER_LENGTH) {
278 SIPE_DEBUG_ERROR("tls_record_parse: too short TLS record header (%" G_GSIZE_FORMAT " bytes)",
279 length);
280 return(NULL);
283 /* protocol version check */
284 major = bytes[TLS_RECORD_OFFSET_MAJOR];
285 minor = bytes[TLS_RECORD_OFFSET_MINOR];
286 if (major < 3) {
287 SIPE_DEBUG_ERROR_NOFORMAT("tls_record_parse: SSL1/2 not supported");
288 return(NULL);
290 if (major == 3) {
291 switch (minor) {
292 case 0:
293 SIPE_DEBUG_ERROR_NOFORMAT("tls_record_parse: SSL3.0 not supported");
294 return(NULL);
295 case 1:
296 version = "1.0 (RFC2246)";
297 break;
298 case 2:
299 version = "1.1 (RFC4346)";
300 break;
303 if (!version) {
304 /* should be backwards compatible */
305 version = "<future protocol version>";
308 /* record length check */
309 record_length = TLS_RECORD_HEADER_LENGTH +
310 (bytes[TLS_RECORD_OFFSET_LENGTH] << 8) +
311 bytes[TLS_RECORD_OFFSET_LENGTH + 1];
312 if (record_length > length) {
313 SIPE_DEBUG_ERROR_NOFORMAT("tls_record_parse: record too long");
314 return(NULL);
317 /* TLS record header OK */
318 debug_printf(str, "TLS %s record (%" G_GSIZE_FORMAT " bytes)\n",
319 version, length);
320 content_type = bytes[TLS_RECORD_OFFSET_TYPE];
321 state->parse_buffer = bytes + TLS_RECORD_HEADER_LENGTH;
322 state->parse_length = length - TLS_RECORD_HEADER_LENGTH;
324 switch (content_type) {
325 case TLS_RECORD_TYPE_HANDSHAKE:
326 parsed_data = handshake_parse(state, incoming, str);
327 break;
329 default:
330 debug_printf(str, "TLS ignored type %d\n", content_type);
331 debug_hex(str, state);
332 break;
335 if (str) {
336 SIPE_DEBUG_INFO_NOFORMAT(str->str);
337 g_string_free(str, TRUE);
340 return(parsed_data);
343 static const guchar const client_hello[] = {
345 #if 0
346 /* Extracted from log file */
347 /* TLS Record */
348 0x16, /* ContenType: handshake(22) */
349 0x03, 0x01, /* ProtocolVersion: 3.1 (= TLS 1.0) */
350 0x00, 0x48, /* length: 72 bytes */
351 /* TLS Record fragment -> 72 bytes */
352 /* Handshake (header) */
353 0x01, /* msg_type: client_hello(1) */
354 0x00, 0x00, 0x44, /* length: 68 bytes */
355 /* Handshake (body) */
356 /* ClientHello */
357 0x03, 0x01, /* ProtocolVersion: 3.1 (= TLS 1.0) */
358 /* Random: (32 bytes) */
359 0x4e, 0x81, 0xa7, 0x63, /* uint32 gmt_unix_time */
360 0x15, 0xfd, 0x06, 0x46, /* random_bytes[28] */
361 0x0a, 0xb2, 0xdf, 0xf0,
362 0x85, 0x14, 0xac, 0x60,
363 0x7e, 0xda, 0x48, 0x3c,
364 0xb2, 0xad, 0x5b, 0x0f,
365 0xf3, 0xe4, 0x4e, 0x5d,
366 0x4b, 0x9f, 0x8e, 0xd6,
367 /* session_id: (0..32 bytes) */
368 0x00, /* = 0 -> no SessionID */
369 /* cipher_suites: (2..2^16-1 bytes) */
370 0x00, 0x16, /* = 22 bytes -> 11 CipherSuites */
371 0x00, 0x04, /* TLS_RSA_WITH_RC4_128_MD5 */
372 0x00, 0x05, /* TLS_RSA_WITH_RC4_128_SHA */
373 0x00, 0x0a, /* TLS_RSA_WITH_3DES_EDE_CBC_SHA */
374 0x00, 0x09, /* TLS_RSA_WITH_DES_CBC_SHA */
375 0x00, 0x64, /* NON-STANDARD */
376 0x00, 0x62, /* NON-STANDARD */
377 0x00, 0x03, /* TLS_RSA_EXPORT_WITH_RC4_40_MD5 */
378 0x00, 0x06, /* TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5 */
379 0x00, 0x13, /* TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA */
380 0x00, 0x12, /* TLS_DHE_DSS_WITH_DES_CBC_SHA */
381 0x00, 0x63, /* NON-STANDARD */
382 /* compr_methods: (1..2^8-1 bytes) */
383 0x01, /* = 1 byte -> 1 CompressionMethod */
384 0x00, /* null(0) */
385 /* TLS Extended Client Hello (RFC3546) */
386 /* extensions: (0..2^16-1) */
387 0x00, 0x05, /* = 5 bytes */
388 0xff, 0x01, /* ExtensionType: (= 0xFF01) */
389 /* extension_data: (0..2^16-1 byt) */
390 0x00, 0x01, /* = 1 byte */
391 0x00
392 #else
393 /* TLS Record */
394 0x16, /* ContenType: handshake(22) */
395 0x03, 0x01, /* ProtocolVersion: 3.1 (= TLS 1.0) */
396 0x00, 0x31, /* length: 49 bytes */
397 /* TLS Record fragment -> 72 bytes */
398 /* Handshake (header) */
399 0x01, /* msg_type: client_hello(1) */
400 0x00, 0x00, 0x2d, /* length: 45 bytes */
401 /* Handshake (body) */
402 /* ClientHello */
403 0x03, 0x01, /* ProtocolVersion: 3.1 (= TLS 1.0) */
404 /* Random: (32 bytes) */
405 #define GMT_OFFSET 11
406 0x4e, 0x81, 0xa7, 0x63, /* uint32 gmt_unix_time */
407 #define RANDOM_OFFSET 15
408 0x15, 0xfd, 0x06, 0x46, /* random_bytes[28] */
409 0x0a, 0xb2, 0xdf, 0xf0,
410 0x85, 0x14, 0xac, 0x60,
411 0x7e, 0xda, 0x48, 0x3c,
412 0xb2, 0xad, 0x5b, 0x0f,
413 0xf3, 0xe4, 0x4e, 0x5d,
414 0x4b, 0x9f, 0x8e, 0xd6,
415 /* session_id: (0..32 bytes) */
416 0x00, /* = 0 -> no SessionID */
417 /* cipher_suites: (2..2^16-1 bytes) */
418 0x00, 0x06, /* = 6 bytes -> 3 CipherSuites */
419 0x00, 0x04, /* TLS_RSA_WITH_RC4_128_MD5 */
420 0x00, 0x05, /* TLS_RSA_WITH_RC4_128_SHA */
421 0x00, 0x03, /* TLS_RSA_EXPORT_WITH_RC4_40_MD5 */
422 /* compr_methods: (1..2^8-1 bytes) */
423 0x01, /* = 1 byte -> 1 CompressionMethod */
424 0x00 /* null(0) */
425 #endif
428 static gboolean tls_client_hello(struct tls_internal_state *state)
430 guchar *msg = g_memdup(client_hello, sizeof(client_hello));
431 guint32 now = time(NULL);
432 guint32 now_N = GUINT32_TO_BE(now);
433 guchar *p;
434 guint i;
436 memcpy(msg + GMT_OFFSET, &now_N, sizeof(now_N));
437 for (p = msg + RANDOM_OFFSET, i = 0; i < 2; i++)
438 *p++ = rand() & 0xFF;
440 state->common.out_buffer = msg;
441 state->common.out_length = sizeof(client_hello);
442 state->state = TLS_HANDSHAKE_STATE_SERVER_HELLO;
444 tls_record_parse(state, FALSE);
446 return(TRUE);
449 static gboolean tls_server_hello(struct tls_internal_state *state)
451 gpointer parsed_data = tls_record_parse(state, TRUE);
453 if (!parsed_data)
454 return(FALSE);
456 /* temporary */
457 state->common.out_buffer = NULL;
458 state->common.out_length = 0;
459 state->state = TLS_HANDSHAKE_STATE_FINISHED;
461 tls_record_parse(state, FALSE);
463 return(state->common.out_buffer != NULL);
466 static gboolean tls_finished(struct tls_internal_state *state)
468 gpointer parsed_data = tls_record_parse(state, TRUE);
470 if (!parsed_data)
471 return(FALSE);
473 /* TBD: data is really not needed? */
474 free_parsed_data(parsed_data);
476 state->common.out_buffer = NULL;
477 state->common.out_length = 0;
478 state->state = TLS_HANDSHAKE_STATE_COMPLETED;
480 /* temporary */
481 return(TRUE);
484 /* Public API */
486 struct sipe_tls_state *sipe_tls_start(gpointer certificate)
488 struct tls_internal_state *state;
490 if (!certificate)
491 return(NULL);
493 state = g_new0(struct tls_internal_state, 1);
494 state->certificate = certificate;
495 state->state = TLS_HANDSHAKE_STATE_START;
497 return((struct sipe_tls_state *) state);
500 gboolean sipe_tls_next(struct sipe_tls_state *state)
502 struct tls_internal_state *internal = (struct tls_internal_state *) state;
503 gboolean success = FALSE;
505 if (!state)
506 return(FALSE);
508 state->out_buffer = NULL;
510 switch (internal->state) {
511 case TLS_HANDSHAKE_STATE_START:
512 success = tls_client_hello(internal);
513 break;
515 case TLS_HANDSHAKE_STATE_SERVER_HELLO:
516 success = tls_server_hello(internal);
517 break;
519 case TLS_HANDSHAKE_STATE_FINISHED:
520 success = tls_finished(internal);
521 break;
523 case TLS_HANDSHAKE_STATE_COMPLETED:
524 case TLS_HANDSHAKE_STATE_FAILED:
525 /* This should not happen */
526 SIPE_DEBUG_ERROR_NOFORMAT("sipe_tls_next: called in incorrect state!");
527 break;
530 if (!success) {
531 internal->state = TLS_HANDSHAKE_STATE_FAILED;
534 return(success);
537 void sipe_tls_free(struct sipe_tls_state *state)
539 if (state) {
540 g_free(state->session_key);
541 g_free(state->out_buffer);
542 g_free(state);
547 Local Variables:
548 mode: c
549 c-file-style: "bsd"
550 indent-tabs-mode: t
551 tab-width: 8
552 End: