tls: first crack at generic message parser
[siplcs.git] / src / core / sipe-tls.c
blob9b7e396fa08851dd23186ca57e5834d757f4b045
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;
64 GHashTable *data;
65 GString *debug;
69 * TLS message debugging
71 static void debug_hex(struct tls_internal_state *state,
72 gsize alternative_length)
74 GString *str = state->debug;
75 const guchar *bytes;
76 gsize length;
77 gint count;
79 if (!str) return;
81 bytes = state->parse_buffer;
82 length = alternative_length ? alternative_length : state->parse_length;
83 count = -1;
85 while (length-- > 0) {
86 if (++count == 0) {
87 /* do nothing */;
88 } else if ((count % 16) == 0) {
89 g_string_append(str, "\n");
90 } else if ((count % 8) == 0) {
91 g_string_append(str, " ");
93 g_string_append_printf(str, " %02X", *bytes++);
95 g_string_append(str, "\n");
98 #define debug_print(state, string) \
99 if (state->debug) g_string_append(state->debug, string)
100 #define debug_printf(state, format, ...) \
101 if (state->debug) g_string_append_printf(state->debug, format, __VA_ARGS__)
104 * Low-level data conversion routines
106 * - host alignment agnostic, i.e. can fetch a word from uneven address
107 * - TLS -> host endianess conversion
108 * - no length check, caller has to do it
109 * - don't modify state
111 static guint lowlevel_integer_to_host(const guchar *bytes,
112 gsize length)
114 guint sum = 0;
115 while (length--) sum = (sum << 8) + *bytes++;
116 return(sum);
120 * Simple data type parser routines
122 static gboolean parse_length_check(struct tls_internal_state *state,
123 const gchar *label,
124 gsize length)
126 if (length > state->parse_length) {
127 SIPE_DEBUG_ERROR("parse_length_check: '%s' expected %" G_GSIZE_FORMAT " bytes, remaining %" G_GSIZE_FORMAT,
128 label, length, state->parse_length);
129 return(FALSE);
131 return(TRUE);
134 static gboolean parse_integer_quiet(struct tls_internal_state *state,
135 const gchar *label,
136 gsize length,
137 guint *result)
139 if (!parse_length_check(state, label, length)) return(FALSE);
140 *result = lowlevel_integer_to_host(state->parse_buffer, length);
141 state->parse_buffer += length;
142 state->parse_length -= length;
143 return(TRUE);
146 static gboolean parse_integer(struct tls_internal_state *state,
147 const gchar *label,
148 gsize length,
149 guint *result)
151 if (!parse_integer_quiet(state, label, length, result)) return(FALSE);
152 debug_printf(state, "'%s/INTEGER%" G_GSIZE_FORMAT " = %d\n",
153 label, length, *result);
154 return(TRUE);
157 struct tls_parsed_integer {
158 guint value;
160 static gboolean parse_integer_store(struct tls_internal_state *state,
161 const gchar *label,
162 gsize length)
164 guint value;
165 if (!parse_integer(state, label, length, &value)) return(FALSE);
166 if (state->data) {
167 struct tls_parsed_integer *save = g_new0(struct tls_parsed_integer, 1);
168 save->value = value;
169 g_hash_table_insert(state->data, (gpointer) label, save);
171 return(TRUE);
175 * TLS message parsing
177 #define TLS_TYPE_FIXED 0x00
178 #define TLS_TYPE_ARRAY 0x01
179 #define TLS_TYPE_VECTOR 0x02
181 #define TLS_VECTOR_MAX8 255 /* 2^8 - 1 */
182 #define TLS_VECTOR_MAX16 65535 /* 2^16 - 1 */
183 #define TLS_VECTOR_MAX24 16777215 /* 2^24 - 1 */
185 struct parse_descriptor;
186 typedef gboolean parse_func(struct tls_internal_state *state,
187 const struct parse_descriptor *desc);
188 struct parse_descriptor {
189 const gchar *label;
190 parse_func *parser;
191 guint type;
192 gsize min; /* 0 for fixed/array */
193 gsize max;
196 #define TLS_PARSE_DESCRIPTOR_END { NULL, NULL, 0, 0, 0 }
198 static const struct parse_descriptor const ClientHello[] = {
199 { "Client Protocol Version", NULL, TLS_TYPE_FIXED, 0, 2 },
200 { "Random", NULL, TLS_TYPE_ARRAY, 0, 32 },
201 { "SessionID", NULL, TLS_TYPE_VECTOR, 0, 32 },
202 { "CipherSuite", NULL, TLS_TYPE_VECTOR, 2, TLS_VECTOR_MAX16 },
203 { "CompressionMethod", NULL, TLS_TYPE_VECTOR, 1, TLS_VECTOR_MAX8 },
204 TLS_PARSE_DESCRIPTOR_END
207 static const struct parse_descriptor const ServerHello[] = {
208 { "Server Protocol Version", NULL, TLS_TYPE_FIXED, 0, 2 },
209 { "Random", NULL, TLS_TYPE_ARRAY, 0, 32 },
210 { "SessionID", NULL, TLS_TYPE_VECTOR, 0, 32 },
211 { "CipherSuite", NULL, TLS_TYPE_FIXED, 0, 2 },
212 { "CompressionMethod", NULL, TLS_TYPE_FIXED, 0, 1 },
213 TLS_PARSE_DESCRIPTOR_END
216 static const struct parse_descriptor const Certificate[] = {
217 { "Certificate", NULL, TLS_TYPE_VECTOR, 0, TLS_VECTOR_MAX24 },
218 TLS_PARSE_DESCRIPTOR_END
221 static const struct parse_descriptor const CertificateRequest[] = {
222 { "CertificateType", NULL, TLS_TYPE_VECTOR, 1, TLS_VECTOR_MAX8 },
223 { "DistinguishedName", NULL, TLS_TYPE_VECTOR, 0, TLS_VECTOR_MAX16 },
224 TLS_PARSE_DESCRIPTOR_END
227 static const struct parse_descriptor const ServerHelloDone[] = {
228 TLS_PARSE_DESCRIPTOR_END
231 static gboolean parse_ignored_field(struct tls_internal_state *state,
232 const struct parse_descriptor *desc)
234 gboolean success = FALSE;
236 switch (desc->type) {
237 case TLS_TYPE_FIXED:
239 guint value;
240 success = parse_integer(state, desc->label, desc->max, &value);
242 break;
244 case TLS_TYPE_ARRAY:
245 if (parse_length_check(state, desc->label, desc->max)) {
246 /* temporary */
247 debug_printf(state, "%s/ARRAY[%" G_GSIZE_FORMAT "]\n",
248 desc->label, desc->max);
249 debug_hex(state, desc->max);
250 state->parse_buffer += desc->max;
251 state->parse_length -= desc->max;
252 success = TRUE;
254 break;
256 case TLS_TYPE_VECTOR:
258 guint length;
259 if (parse_integer_quiet(state, desc->label,
260 (desc->max > TLS_VECTOR_MAX16) ? 3 :
261 (desc->max > TLS_VECTOR_MAX8) ? 2 : 1,
262 &length)) {
264 if (length < desc->min) {
265 SIPE_DEBUG_ERROR("generic_parser: too short vector type %d (minimum %" G_GSIZE_FORMAT ")",
266 length, desc->min);
267 } else {
268 /* temporary */
269 debug_printf(state, "%s/VECTOR<%d>\n",
270 desc->label, length);
271 if (length)
272 debug_hex(state, length);
273 state->parse_buffer += length;
274 state->parse_length -= length;
275 success = TRUE;
279 break;
281 default:
282 SIPE_DEBUG_ERROR("generic_parser: unknown descriptor type %d",
283 desc->type);
284 break;
287 return(success);
290 static gboolean generic_parser(struct tls_internal_state *state,
291 const struct parse_descriptor *desc)
293 while (desc->label) {
294 if (desc->parser) {
295 /* TBD... */
296 (void)parse_integer_store;
297 } else {
298 if (!parse_ignored_field(state, desc))
299 return(FALSE);
301 desc++;
304 return(TRUE);
307 #define TLS_HANDSHAKE_HEADER_LENGTH 4
308 #define TLS_HANDSHAKE_OFFSET_TYPE 0
309 #define TLS_HANDSHAKE_TYPE_CLIENT_HELLO 1
310 #define TLS_HANDSHAKE_TYPE_SERVER_HELLO 2
311 #define TLS_HANDSHAKE_TYPE_CERTIFICATE 11
312 #define TLS_HANDSHAKE_TYPE_CERTIFICATE_REQ 13
313 #define TLS_HANDSHAKE_TYPE_SERVER_HELLO_DONE 14
314 #define TLS_HANDSHAKE_OFFSET_LENGTH 1
316 struct msg_descriptor {
317 guint type;
318 const gchar *description;
319 const struct parse_descriptor *parse;
322 static gboolean handshake_parse(struct tls_internal_state *state)
324 static const struct msg_descriptor const handshake_descriptors[] = {
325 { TLS_HANDSHAKE_TYPE_CLIENT_HELLO, "Client Hello", ClientHello},
326 { TLS_HANDSHAKE_TYPE_SERVER_HELLO, "Server Hello", ServerHello},
327 { TLS_HANDSHAKE_TYPE_CERTIFICATE, "Certificate", Certificate},
328 { TLS_HANDSHAKE_TYPE_CERTIFICATE_REQ, "Certificate Request", CertificateRequest},
329 { TLS_HANDSHAKE_TYPE_SERVER_HELLO_DONE, "Server Hello Done", ServerHelloDone}
331 #define HANDSHAKE_DESCRIPTORS (sizeof(handshake_descriptors)/sizeof(struct msg_descriptor))
333 const guchar *bytes = state->parse_buffer;
334 gsize length = state->parse_length;
335 gboolean success = FALSE;
337 while (length > 0) {
338 const struct msg_descriptor *desc;
339 gsize msg_length;
340 guint i, msg_type;
342 /* header check */
343 if (length < TLS_HANDSHAKE_HEADER_LENGTH) {
344 debug_print(state, "CORRUPTED HANDSHAKE HEADER");
345 break;
348 /* msg length check */
349 msg_length = lowlevel_integer_to_host(bytes + TLS_HANDSHAKE_OFFSET_LENGTH,
351 if (msg_length > length) {
352 debug_print(state, "HANDSHAKE MESSAGE TOO LONG");
353 break;
356 /* msg type */
357 msg_type = bytes[TLS_HANDSHAKE_OFFSET_TYPE];
358 for (desc = handshake_descriptors, i = 0;
359 i < HANDSHAKE_DESCRIPTORS;
360 desc++, i++) {
361 if (msg_type == desc->type)
362 break;
365 debug_printf(state, "TLS handshake (%" G_GSIZE_FORMAT " bytes) (%d)",
366 msg_length, msg_type);
368 state->parse_buffer = bytes + TLS_HANDSHAKE_HEADER_LENGTH;
369 state->parse_length = msg_length;
371 if (i < HANDSHAKE_DESCRIPTORS) {
372 debug_printf(state, "%s\n", desc->description);
373 success = generic_parser(state, desc->parse);
374 if (!success)
375 break;
376 } else {
377 debug_print(state, "ignored\n");
378 debug_hex(state, 0);
381 /* next message */
382 bytes += TLS_HANDSHAKE_HEADER_LENGTH + msg_length;
383 length -= TLS_HANDSHAKE_HEADER_LENGTH + msg_length;
384 if (length > 0) {
385 debug_print(state, "------\n");
386 } else {
387 success = TRUE;
391 return(success);
394 static void free_parse_data(struct tls_internal_state *state)
396 if (state->data) {
397 g_hash_table_destroy(state->data);
398 state->data = NULL;
402 #define TLS_RECORD_HEADER_LENGTH 5
403 #define TLS_RECORD_OFFSET_TYPE 0
404 #define TLS_RECORD_TYPE_HANDSHAKE 22
405 #define TLS_RECORD_OFFSET_MAJOR 1
406 #define TLS_RECORD_OFFSET_LENGTH 3
408 /* NOTE: we don't support record fragmentation */
409 static gboolean tls_record_parse(struct tls_internal_state *state,
410 gboolean incoming)
412 const guchar *bytes = incoming ? state->common.in_buffer : state->common.out_buffer;
413 gsize length = incoming ? state->common.in_length : state->common.out_length;
414 guint version;
415 const gchar *version_str;
416 gsize record_length;
417 gboolean success = FALSE;
419 debug_printf(state, "TLS MESSAGE %s\n", incoming ? "INCOMING" : "OUTGOING");
421 /* truncated header check */
422 if (length < TLS_RECORD_HEADER_LENGTH) {
423 SIPE_DEBUG_ERROR("tls_record_parse: too short TLS record header (%" G_GSIZE_FORMAT " bytes)",
424 length);
425 return(FALSE);
428 /* protocol version check */
429 version = lowlevel_integer_to_host(bytes + TLS_RECORD_OFFSET_MAJOR, 2);
430 if (version < 0x0301) {
431 SIPE_DEBUG_ERROR_NOFORMAT("tls_record_parse: SSL1/2/3 not supported");
432 return(FALSE);
434 switch (version) {
435 case 0x0301:
436 version_str = "1.0 (RFC2246)";
437 break;
438 case 0x0302:
439 version_str = "1.1 (RFC4346)";
440 break;
441 default:
442 version_str = "<future protocol version>";
443 break;
446 /* record length check */
447 record_length = TLS_RECORD_HEADER_LENGTH +
448 lowlevel_integer_to_host(bytes + TLS_RECORD_OFFSET_LENGTH, 2);
449 if (record_length > length) {
450 SIPE_DEBUG_ERROR_NOFORMAT("tls_record_parse: record too long");
451 return(FALSE);
454 /* TLS record header OK */
455 debug_printf(state, "TLS %s record (%" G_GSIZE_FORMAT " bytes)\n",
456 version_str, length);
457 state->parse_buffer = bytes + TLS_RECORD_HEADER_LENGTH;
458 state->parse_length = length - TLS_RECORD_HEADER_LENGTH;
460 /* Collect parser data for incoming messages */
461 if (incoming)
462 state->data = g_hash_table_new_full(g_str_hash, g_str_equal,
463 NULL, g_free);
465 switch (bytes[TLS_RECORD_OFFSET_TYPE]) {
466 case TLS_RECORD_TYPE_HANDSHAKE:
467 success = handshake_parse(state);
468 break;
470 default:
471 debug_print(state, "Unsupported TLS message\n");
472 debug_hex(state, 0);
473 break;
476 if (!success)
477 free_parse_data(state);
479 if (state->debug) {
480 SIPE_DEBUG_INFO_NOFORMAT(state->debug->str);
481 g_string_truncate(state->debug, 0);
484 return(success);
487 static const guchar const client_hello[] = {
489 #if 0
490 /* Extracted from log file */
491 /* TLS Record */
492 0x16, /* ContenType: handshake(22) */
493 0x03, 0x01, /* ProtocolVersion: 3.1 (= TLS 1.0) */
494 0x00, 0x48, /* length: 72 bytes */
495 /* TLS Record fragment -> 72 bytes */
496 /* Handshake (header) */
497 0x01, /* msg_type: client_hello(1) */
498 0x00, 0x00, 0x44, /* length: 68 bytes */
499 /* Handshake (body) */
500 /* ClientHello */
501 0x03, 0x01, /* ProtocolVersion: 3.1 (= TLS 1.0) */
502 /* Random: (32 bytes) */
503 0x4e, 0x81, 0xa7, 0x63, /* uint32 gmt_unix_time */
504 0x15, 0xfd, 0x06, 0x46, /* random_bytes[28] */
505 0x0a, 0xb2, 0xdf, 0xf0,
506 0x85, 0x14, 0xac, 0x60,
507 0x7e, 0xda, 0x48, 0x3c,
508 0xb2, 0xad, 0x5b, 0x0f,
509 0xf3, 0xe4, 0x4e, 0x5d,
510 0x4b, 0x9f, 0x8e, 0xd6,
511 /* session_id: (0..32 bytes) */
512 0x00, /* = 0 -> no SessionID */
513 /* cipher_suites: (2..2^16-1 bytes) */
514 0x00, 0x16, /* = 22 bytes -> 11 CipherSuites */
515 0x00, 0x04, /* TLS_RSA_WITH_RC4_128_MD5 */
516 0x00, 0x05, /* TLS_RSA_WITH_RC4_128_SHA */
517 0x00, 0x0a, /* TLS_RSA_WITH_3DES_EDE_CBC_SHA */
518 0x00, 0x09, /* TLS_RSA_WITH_DES_CBC_SHA */
519 0x00, 0x64, /* NON-STANDARD */
520 0x00, 0x62, /* NON-STANDARD */
521 0x00, 0x03, /* TLS_RSA_EXPORT_WITH_RC4_40_MD5 */
522 0x00, 0x06, /* TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5 */
523 0x00, 0x13, /* TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA */
524 0x00, 0x12, /* TLS_DHE_DSS_WITH_DES_CBC_SHA */
525 0x00, 0x63, /* NON-STANDARD */
526 /* compr_methods: (1..2^8-1 bytes) */
527 0x01, /* = 1 byte -> 1 CompressionMethod */
528 0x00, /* null(0) */
529 /* TLS Extended Client Hello (RFC3546) */
530 /* extensions: (0..2^16-1) */
531 0x00, 0x05, /* = 5 bytes */
532 0xff, 0x01, /* ExtensionType: (= 0xFF01) */
533 /* extension_data: (0..2^16-1 byt) */
534 0x00, 0x01, /* = 1 byte */
535 0x00
536 #else
537 /* TLS Record */
538 0x16, /* ContenType: handshake(22) */
539 0x03, 0x01, /* ProtocolVersion: 3.1 (= TLS 1.0) */
540 0x00, 0x31, /* length: 49 bytes */
541 /* TLS Record fragment -> 72 bytes */
542 /* Handshake (header) */
543 0x01, /* msg_type: client_hello(1) */
544 0x00, 0x00, 0x2d, /* length: 45 bytes */
545 /* Handshake (body) */
546 /* ClientHello */
547 0x03, 0x01, /* ProtocolVersion: 3.1 (= TLS 1.0) */
548 /* Random: (32 bytes) */
549 #define GMT_OFFSET 11
550 0x4e, 0x81, 0xa7, 0x63, /* uint32 gmt_unix_time */
551 #define RANDOM_OFFSET 15
552 0x15, 0xfd, 0x06, 0x46, /* random_bytes[28] */
553 0x0a, 0xb2, 0xdf, 0xf0,
554 0x85, 0x14, 0xac, 0x60,
555 0x7e, 0xda, 0x48, 0x3c,
556 0xb2, 0xad, 0x5b, 0x0f,
557 0xf3, 0xe4, 0x4e, 0x5d,
558 0x4b, 0x9f, 0x8e, 0xd6,
559 /* session_id: (0..32 bytes) */
560 0x00, /* = 0 -> no SessionID */
561 /* cipher_suites: (2..2^16-1 bytes) */
562 0x00, 0x06, /* = 6 bytes -> 3 CipherSuites */
563 0x00, 0x04, /* TLS_RSA_WITH_RC4_128_MD5 */
564 0x00, 0x05, /* TLS_RSA_WITH_RC4_128_SHA */
565 0x00, 0x03, /* TLS_RSA_EXPORT_WITH_RC4_40_MD5 */
566 /* compr_methods: (1..2^8-1 bytes) */
567 0x01, /* = 1 byte -> 1 CompressionMethod */
568 0x00 /* null(0) */
569 #endif
572 static gboolean tls_client_hello(struct tls_internal_state *state)
574 guchar *msg = g_memdup(client_hello, sizeof(client_hello));
575 guint32 now = time(NULL);
576 guint32 now_N = GUINT32_TO_BE(now);
577 guchar *p;
578 guint i;
580 memcpy(msg + GMT_OFFSET, &now_N, sizeof(now_N));
581 for (p = msg + RANDOM_OFFSET, i = 0; i < 2; i++)
582 *p++ = rand() & 0xFF;
584 state->common.out_buffer = msg;
585 state->common.out_length = sizeof(client_hello);
586 state->state = TLS_HANDSHAKE_STATE_SERVER_HELLO;
588 if (sipe_backend_debug_enabled())
589 state->debug = g_string_new("");
591 tls_record_parse(state, FALSE);
593 return(TRUE);
596 static gboolean tls_server_hello(struct tls_internal_state *state)
598 if (!tls_record_parse(state, TRUE))
599 return(FALSE);
601 /* temporary */
602 free_parse_data(state);
603 state->common.out_buffer = NULL;
604 state->common.out_length = 0;
605 state->state = TLS_HANDSHAKE_STATE_FINISHED;
607 tls_record_parse(state, FALSE);
609 return(state->common.out_buffer != NULL);
612 static gboolean tls_finished(struct tls_internal_state *state)
614 if (!tls_record_parse(state, TRUE))
615 return(FALSE);
617 /* TBD: data is really not needed? */
618 free_parse_data(state);
620 state->common.out_buffer = NULL;
621 state->common.out_length = 0;
622 state->state = TLS_HANDSHAKE_STATE_COMPLETED;
624 /* temporary */
625 return(TRUE);
628 /* Public API */
630 struct sipe_tls_state *sipe_tls_start(gpointer certificate)
632 struct tls_internal_state *state;
634 if (!certificate)
635 return(NULL);
637 state = g_new0(struct tls_internal_state, 1);
638 state->certificate = certificate;
639 state->state = TLS_HANDSHAKE_STATE_START;
641 return((struct sipe_tls_state *) state);
644 gboolean sipe_tls_next(struct sipe_tls_state *state)
646 struct tls_internal_state *internal = (struct tls_internal_state *) state;
647 gboolean success = FALSE;
649 if (!state)
650 return(FALSE);
652 state->out_buffer = NULL;
654 switch (internal->state) {
655 case TLS_HANDSHAKE_STATE_START:
656 success = tls_client_hello(internal);
657 break;
659 case TLS_HANDSHAKE_STATE_SERVER_HELLO:
660 success = tls_server_hello(internal);
661 break;
663 case TLS_HANDSHAKE_STATE_FINISHED:
664 success = tls_finished(internal);
665 break;
667 case TLS_HANDSHAKE_STATE_COMPLETED:
668 case TLS_HANDSHAKE_STATE_FAILED:
669 /* This should not happen */
670 SIPE_DEBUG_ERROR_NOFORMAT("sipe_tls_next: called in incorrect state!");
671 break;
674 if (!success) {
675 internal->state = TLS_HANDSHAKE_STATE_FAILED;
678 return(success);
681 void sipe_tls_free(struct sipe_tls_state *state)
683 if (state) {
684 struct tls_internal_state *internal = (struct tls_internal_state *) state;
686 free_parse_data(internal);
687 if (internal->debug)
688 g_string_free(internal->debug, TRUE);
689 g_free(state->session_key);
690 g_free(state->out_buffer);
691 g_free(state);
696 Local Variables:
697 mode: c
698 c-file-style: "bsd"
699 indent-tabs-mode: t
700 tab-width: 8
701 End: