tls: internalize state handling
[siplcs.git] / src / core / sipe-tls.c
bloba1e808765e3e60251c6308a916f9e01fd7d4bc30
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;
65 * TLS message debugging
67 static void debug_hex(GString *str,
68 const guchar *bytes,
69 gsize length)
71 gint count = -1;
73 if (!str) return;
75 while (length-- > 0) {
76 if (++count == 0) {
77 /* do nothing */;
78 } else if ((count % 16) == 0) {
79 g_string_append(str, "\n");
80 } else if ((count % 8) == 0) {
81 g_string_append(str, " ");
83 g_string_append_printf(str, " %02X", *bytes++);
85 g_string_append(str, "\n");
88 static void debug_print(GString *str,
89 const gchar *string)
91 if (str)
92 g_string_append(str, string);
95 static void debug_printf(GString *str,
96 const gchar *format,
97 ...) G_GNUC_PRINTF(2, 3);
98 static void debug_printf(GString *str,
99 const gchar *format,
100 ...)
102 va_list ap;
104 if (!str) return;
106 va_start(ap, format);
107 g_string_append_vprintf(str, format, ap);
108 va_end(ap);
113 * TLS message parsing
115 struct parse_descriptor {
116 int temporary;
119 struct msg_descriptor {
120 guint type;
121 const gchar *description;
122 const struct parse_descriptor *parse;
125 static void free_parsed_data(gpointer parsed_data)
127 if (!parsed_data)
128 return;
131 static gpointer generic_parser(const guchar *bytes,
132 gsize length,
133 gboolean incoming,
134 GString *str,
135 const struct parse_descriptor *desc,
136 gpointer parsed_data)
138 /* temporary */
139 debug_hex(str, bytes, length);
140 (void)parsed_data;
141 (void)incoming;
142 (void)desc;
143 return(NULL);
146 #define TLS_HANDSHAKE_HEADER_LENGTH 4
147 #define TLS_HANDSHAKE_OFFSET_TYPE 0
148 #define TLS_HANDSHAKE_TYPE_CLIENT_HELLO 1
149 #define TLS_HANDSHAKE_TYPE_SERVER_HELLO 2
150 #define TLS_HANDSHAKE_TYPE_CERTIFICATE 11
151 #define TLS_HANDSHAKE_TYPE_CERTIFICATE_REQ 13
152 #define TLS_HANDSHAKE_TYPE_SERVER_HELLO_DONE 14
153 #define TLS_HANDSHAKE_OFFSET_LENGTH 1
155 static gpointer handshake_parse(const guchar *bytes,
156 gsize length,
157 gboolean incoming,
158 GString *str)
160 static const struct msg_descriptor const handshake_descriptors[] = {
161 { TLS_HANDSHAKE_TYPE_CLIENT_HELLO, "Client Hello", NULL},
162 { TLS_HANDSHAKE_TYPE_SERVER_HELLO, "Server Hello", NULL},
163 { TLS_HANDSHAKE_TYPE_CERTIFICATE, "Certificate", NULL},
164 { TLS_HANDSHAKE_TYPE_CERTIFICATE_REQ, "Certificate Request", NULL},
165 { TLS_HANDSHAKE_TYPE_SERVER_HELLO_DONE, "Server Hello Done", NULL}
167 #define HANDSHAKE_DESCRIPTORS (sizeof(handshake_descriptors)/sizeof(struct msg_descriptor))
169 gpointer parsed_data = NULL;
170 gboolean success = FALSE;
172 while (length > 0) {
173 const struct msg_descriptor *desc;
174 gsize msg_length;
175 guint i, msg_type;
177 /* header check */
178 if (length < TLS_HANDSHAKE_HEADER_LENGTH) {
179 debug_print(str, "CORRUPTED HANDSHAKE HEADER");
180 break;
183 /* msg length check */
184 msg_length = (bytes[TLS_HANDSHAKE_OFFSET_LENGTH] << 16) +
185 (bytes[TLS_HANDSHAKE_OFFSET_LENGTH + 1] << 8) +
186 bytes[TLS_HANDSHAKE_OFFSET_LENGTH + 2];
187 if (msg_length > length) {
188 debug_print(str, "HANDSHAKE MESSAGE TOO LONG");
189 break;
192 /* msg type */
193 msg_type = bytes[TLS_HANDSHAKE_OFFSET_TYPE];
194 for (desc = handshake_descriptors, i = 0;
195 i < HANDSHAKE_DESCRIPTORS;
196 desc++, i++) {
197 if (msg_type == desc->type)
198 break;
201 debug_printf(str, "TLS handshake (%" G_GSIZE_FORMAT " bytes) (%d)",
202 msg_length, msg_type);
204 length -= TLS_HANDSHAKE_HEADER_LENGTH;
205 bytes += TLS_HANDSHAKE_HEADER_LENGTH;
207 if (i < HANDSHAKE_DESCRIPTORS) {
208 debug_printf(str, "%s\n", desc->description);
209 parsed_data = generic_parser(bytes,
210 msg_length,
211 incoming,
212 str,
213 desc->parse,
214 parsed_data);
215 /* temporary */
216 #if 0
217 if (!parsed_data)
218 break;
219 #endif
220 } else {
221 debug_print(str, "ignored\n");
222 debug_hex(str, bytes, msg_length);
225 /* next message */
226 length -= msg_length;
227 bytes += msg_length;
228 if (length > 0) {
229 debug_print(str, "------\n");
230 } else {
231 success = TRUE;
235 if (!success) {
236 free_parsed_data(parsed_data);
237 parsed_data = NULL;
240 return(parsed_data);
243 #define TLS_RECORD_HEADER_LENGTH 5
244 #define TLS_RECORD_OFFSET_TYPE 0
245 #define TLS_RECORD_TYPE_HANDSHAKE 22
246 #define TLS_RECORD_OFFSET_MAJOR 1
247 #define TLS_RECORD_OFFSET_MINOR 2
248 #define TLS_RECORD_OFFSET_LENGTH 3
250 /* NOTE: we don't support record fragmentation */
251 static gpointer tls_record_parse(const guchar *bytes,
252 gsize length,
253 gboolean incoming)
255 gpointer parsed_data = NULL;
256 GString *str = NULL;
257 const gchar *version = NULL;
258 guchar major;
259 guchar minor;
260 gsize record_length;
261 guint content_type;
263 if (sipe_backend_debug_enabled()) {
264 str = g_string_new("");
265 debug_printf(str, "TLS MESSAGE %s\n",
266 incoming ? "INCOMING" : "OUTGOING");
269 /* truncated header check */
270 if (length < TLS_RECORD_HEADER_LENGTH) {
271 SIPE_DEBUG_ERROR("tls_record_parse: too short TLS record header (%" G_GSIZE_FORMAT " bytes)",
272 length);
273 return(NULL);
276 /* protocol version check */
277 major = bytes[TLS_RECORD_OFFSET_MAJOR];
278 minor = bytes[TLS_RECORD_OFFSET_MINOR];
279 if (major < 3) {
280 SIPE_DEBUG_ERROR_NOFORMAT("tls_record_parse: SSL1/2 not supported");
281 return(NULL);
283 if (major == 3) {
284 switch (minor) {
285 case 0:
286 SIPE_DEBUG_ERROR_NOFORMAT("tls_record_parse: SSL3.0 not supported");
287 return(NULL);
288 case 1:
289 version = "1.0 (RFC2246)";
290 break;
291 case 2:
292 version = "1.1 (RFC4346)";
293 break;
296 if (!version) {
297 /* should be backwards compatible */
298 version = "<future protocol version>";
301 /* record length check */
302 record_length = TLS_RECORD_HEADER_LENGTH +
303 (bytes[TLS_RECORD_OFFSET_LENGTH] << 8) +
304 bytes[TLS_RECORD_OFFSET_LENGTH + 1];
305 if (record_length > length) {
306 SIPE_DEBUG_ERROR_NOFORMAT("tls_record_parse: record too long");
307 return(NULL);
310 /* TLS record header OK */
311 debug_printf(str, "TLS %s record (%" G_GSIZE_FORMAT " bytes)\n",
312 version, length);
313 content_type = bytes[TLS_RECORD_OFFSET_TYPE];
314 length -= TLS_RECORD_HEADER_LENGTH;
315 bytes += TLS_RECORD_HEADER_LENGTH;
317 switch (content_type) {
318 case TLS_RECORD_TYPE_HANDSHAKE:
319 parsed_data = handshake_parse(bytes, length, incoming, str);
320 break;
322 default:
323 debug_printf(str, "TLS ignored type %d\n", content_type);
324 debug_hex(str, bytes, length);
325 break;
328 if (str) {
329 SIPE_DEBUG_INFO_NOFORMAT(str->str);
330 g_string_free(str, TRUE);
333 return(parsed_data);
336 static const guchar const client_hello[] = {
338 #if 0
339 /* Extracted from log file */
340 /* TLS Record */
341 0x16, /* ContenType: handshake(22) */
342 0x03, 0x01, /* ProtocolVersion: 3.1 (= TLS 1.0) */
343 0x00, 0x48, /* length: 72 bytes */
344 /* TLS Record fragment -> 72 bytes */
345 /* Handshake (header) */
346 0x01, /* msg_type: client_hello(1) */
347 0x00, 0x00, 0x44, /* length: 68 bytes */
348 /* Handshake (body) */
349 /* ClientHello */
350 0x03, 0x01, /* ProtocolVersion: 3.1 (= TLS 1.0) */
351 /* Random: (32 bytes) */
352 0x4e, 0x81, 0xa7, 0x63, /* uint32 gmt_unix_time */
353 0x15, 0xfd, 0x06, 0x46, /* random_bytes[28] */
354 0x0a, 0xb2, 0xdf, 0xf0,
355 0x85, 0x14, 0xac, 0x60,
356 0x7e, 0xda, 0x48, 0x3c,
357 0xb2, 0xad, 0x5b, 0x0f,
358 0xf3, 0xe4, 0x4e, 0x5d,
359 0x4b, 0x9f, 0x8e, 0xd6,
360 /* session_id: (0..32 bytes) */
361 0x00, /* = 0 -> no SessionID */
362 /* cipher_suites: (2..2^16-1 bytes) */
363 0x00, 0x16, /* = 22 bytes -> 11 CipherSuites */
364 0x00, 0x04, /* TLS_RSA_WITH_RC4_128_MD5 */
365 0x00, 0x05, /* TLS_RSA_WITH_RC4_128_SHA */
366 0x00, 0x0a, /* TLS_RSA_WITH_3DES_EDE_CBC_SHA */
367 0x00, 0x09, /* TLS_RSA_WITH_DES_CBC_SHA */
368 0x00, 0x64, /* NON-STANDARD */
369 0x00, 0x62, /* NON-STANDARD */
370 0x00, 0x03, /* TLS_RSA_EXPORT_WITH_RC4_40_MD5 */
371 0x00, 0x06, /* TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5 */
372 0x00, 0x13, /* TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA */
373 0x00, 0x12, /* TLS_DHE_DSS_WITH_DES_CBC_SHA */
374 0x00, 0x63, /* NON-STANDARD */
375 /* compr_methods: (1..2^8-1 bytes) */
376 0x01, /* = 1 byte -> 1 CompressionMethod */
377 0x00, /* null(0) */
378 /* TLS Extended Client Hello (RFC3546) */
379 /* extensions: (0..2^16-1) */
380 0x00, 0x05, /* = 5 bytes */
381 0xff, 0x01, /* ExtensionType: (= 0xFF01) */
382 /* extension_data: (0..2^16-1 byt) */
383 0x00, 0x01, /* = 1 byte */
384 0x00
385 #else
386 /* TLS Record */
387 0x16, /* ContenType: handshake(22) */
388 0x03, 0x01, /* ProtocolVersion: 3.1 (= TLS 1.0) */
389 0x00, 0x31, /* length: 49 bytes */
390 /* TLS Record fragment -> 72 bytes */
391 /* Handshake (header) */
392 0x01, /* msg_type: client_hello(1) */
393 0x00, 0x00, 0x2d, /* length: 45 bytes */
394 /* Handshake (body) */
395 /* ClientHello */
396 0x03, 0x01, /* ProtocolVersion: 3.1 (= TLS 1.0) */
397 /* Random: (32 bytes) */
398 #define GMT_OFFSET 11
399 0x4e, 0x81, 0xa7, 0x63, /* uint32 gmt_unix_time */
400 #define RANDOM_OFFSET 15
401 0x15, 0xfd, 0x06, 0x46, /* random_bytes[28] */
402 0x0a, 0xb2, 0xdf, 0xf0,
403 0x85, 0x14, 0xac, 0x60,
404 0x7e, 0xda, 0x48, 0x3c,
405 0xb2, 0xad, 0x5b, 0x0f,
406 0xf3, 0xe4, 0x4e, 0x5d,
407 0x4b, 0x9f, 0x8e, 0xd6,
408 /* session_id: (0..32 bytes) */
409 0x00, /* = 0 -> no SessionID */
410 /* cipher_suites: (2..2^16-1 bytes) */
411 0x00, 0x06, /* = 6 bytes -> 3 CipherSuites */
412 0x00, 0x04, /* TLS_RSA_WITH_RC4_128_MD5 */
413 0x00, 0x05, /* TLS_RSA_WITH_RC4_128_SHA */
414 0x00, 0x03, /* TLS_RSA_EXPORT_WITH_RC4_40_MD5 */
415 /* compr_methods: (1..2^8-1 bytes) */
416 0x01, /* = 1 byte -> 1 CompressionMethod */
417 0x00 /* null(0) */
418 #endif
421 static gboolean tls_client_hello(struct tls_internal_state *state)
423 guchar *msg = g_memdup(client_hello, sizeof(client_hello));
424 guint32 now = time(NULL);
425 guint32 now_N = GUINT32_TO_BE(now);
426 guchar *p;
427 guint i;
429 memcpy(msg + GMT_OFFSET, &now_N, sizeof(now_N));
430 for (p = msg + RANDOM_OFFSET, i = 0; i < 2; i++)
431 *p++ = rand() & 0xFF;
433 state->common.out_buffer = msg;
434 state->common.out_length = sizeof(client_hello);
435 state->state = TLS_HANDSHAKE_STATE_SERVER_HELLO;
437 tls_record_parse(msg, sizeof(client_hello), FALSE);
439 return(TRUE);
442 static gboolean tls_server_hello(struct tls_internal_state *state)
444 gpointer parsed_data = tls_record_parse(state->common.in_buffer,
445 state->common.in_length,
446 TRUE);
448 if (!parsed_data)
449 return(FALSE);
451 /* temporary */
452 state->common.out_buffer = NULL;
453 state->common.out_length = 0;
454 state->state = TLS_HANDSHAKE_STATE_FINISHED;
456 tls_record_parse(state->common.out_buffer,
457 state->common.out_length,
458 FALSE);
460 return(state->common.out_buffer != NULL);
463 static gboolean tls_finished(struct tls_internal_state *state)
465 gpointer parsed_data = tls_record_parse(state->common.in_buffer,
466 state->common.in_length,
467 TRUE);
469 if (!parsed_data)
470 return(FALSE);
472 /* TBD: data is really not needed? */
473 free_parsed_data(parsed_data);
475 state->common.out_buffer = NULL;
476 state->common.out_length = 0;
477 state->state = TLS_HANDSHAKE_STATE_COMPLETED;
479 /* temporary */
480 return(TRUE);
483 /* Public API */
485 struct sipe_tls_state *sipe_tls_start(gpointer certificate)
487 struct tls_internal_state *state;
489 if (!certificate)
490 return(NULL);
492 state = g_new0(struct tls_internal_state, 1);
493 state->certificate = certificate;
494 state->state = TLS_HANDSHAKE_STATE_START;
496 return((struct sipe_tls_state *) state);
499 gboolean sipe_tls_next(struct sipe_tls_state *state)
501 struct tls_internal_state *internal = (struct tls_internal_state *) state;
502 gboolean success = FALSE;
504 if (!state)
505 return(FALSE);
507 state->out_buffer = NULL;
509 switch (internal->state) {
510 case TLS_HANDSHAKE_STATE_START:
511 success = tls_client_hello(internal);
512 break;
514 case TLS_HANDSHAKE_STATE_SERVER_HELLO:
515 success = tls_server_hello(internal);
516 break;
518 case TLS_HANDSHAKE_STATE_FINISHED:
519 success = tls_finished(internal);
520 break;
522 case TLS_HANDSHAKE_STATE_COMPLETED:
523 case TLS_HANDSHAKE_STATE_FAILED:
524 /* This should not happen */
525 SIPE_DEBUG_ERROR_NOFORMAT("sipe_tls_next: called in incorrect state!");
526 break;
529 if (!success) {
530 internal->state = TLS_HANDSHAKE_STATE_FAILED;
533 return(success);
536 void sipe_tls_free(struct sipe_tls_state *state)
538 if (state) {
539 g_free(state->session_key);
540 g_free(state->out_buffer);
541 g_free(state);
546 Local Variables:
547 mode: c
548 c-file-style: "bsd"
549 indent-tabs-mode: t
550 tab-width: 8
551 End: