handle zero size Base64 conversions
[strongswan.git] / src / libcharon / plugins / tnccs_11 / messages / imc_imv_msg.c
1 /*
2 * Copyright (C) 2006 Mike McCauley (mikem@open.com.au)
3 * Copyright (C) 2010 Andreas Steffen, HSR Hochschule fuer Technik Rapperswil
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 "imc_imv_msg.h"
17
18 #include <tnc/tnccs/tnccs.h>
19 #include <debug.h>
20 #include <utils/lexparser.h>
21
22 typedef struct private_imc_imv_msg_t private_imc_imv_msg_t;
23
24 #define BYTES_PER_LINE 57
25
26 /**
27 * Private data of a imc_imv_msg_t object.
28 *
29 */
30 struct private_imc_imv_msg_t {
31 /**
32 * Public imc_imv_msg_t interface.
33 */
34 imc_imv_msg_t public;
35
36 /**
37 * TNCCS message type
38 */
39 tnccs_msg_type_t type;
40
41 /**
42 * XML-encoded message node
43 */
44 xmlNodePtr node;
45
46 /**
47 * IMC-IMV message type
48 */
49 TNC_MessageType msg_type;
50
51 /**
52 * IMC-IMV message body
53 */
54 chunk_t msg_body;
55
56 };
57
58 /**
59 * Encodes message data into multiple base64-encoded lines
60 */
61 static chunk_t encode_base64(chunk_t data)
62 {
63 chunk_t encoding;
64 u_char *pos;
65 size_t b64_chars, b64_lines;
66
67 /* handle empty message data object */
68 if (data.len == 0)
69 {
70 encoding = chunk_alloc(1);
71 *encoding.ptr = '\0';
72 return encoding;
73 }
74
75 /* compute and allocate maximum size of base64 object */
76 b64_chars = 4 * ((data.len + 2) / 3);
77 b64_lines = (data.len + BYTES_PER_LINE - 1) / BYTES_PER_LINE;
78 encoding = chunk_alloc(b64_chars + b64_lines);
79 pos = encoding.ptr;
80
81 /* encode lines */
82 while (b64_lines--)
83 {
84 chunk_t data_line, b64_line;
85
86 data_line = chunk_create(data.ptr, min(data.len, BYTES_PER_LINE));
87 data.ptr += data_line.len;
88 data.len -= data_line.len;
89 b64_line = chunk_to_base64(data_line, pos);
90 pos += b64_line.len;
91 *pos = '\n';
92 pos++;
93 }
94 /* terminate last line with NULL character instead of newline */
95 *(pos-1) = '\0';
96
97 return encoding;
98 }
99
100 /**
101 * Decodes message data from multiple base64-encoded lines
102 */
103 static chunk_t decode_base64(chunk_t data)
104 {
105 chunk_t decoding, data_line, b64_line;
106 u_char *pos;
107
108 /* compute and allocate maximum size of decoded message data */
109 decoding = chunk_alloc(3 * ((data.len + 3) / 4));
110 pos = decoding.ptr;
111 decoding.len = 0;
112
113 while (fetchline(&data, &b64_line))
114 {
115 data_line = chunk_from_base64(b64_line, pos);
116 pos += data_line.len;
117 decoding.len += data_line.len;
118 }
119
120 return decoding;
121 }
122
123 METHOD(tnccs_msg_t, get_type, tnccs_msg_type_t,
124 private_imc_imv_msg_t *this)
125 {
126 return this->type;
127 }
128
129 METHOD(tnccs_msg_t, get_node, xmlNodePtr,
130 private_imc_imv_msg_t *this)
131 {
132 return this->node;
133 }
134
135 METHOD(tnccs_msg_t, destroy, void,
136 private_imc_imv_msg_t *this)
137 {
138 free(this->msg_body.ptr);
139 free(this);
140 }
141
142 METHOD(imc_imv_msg_t, get_msg_type, TNC_MessageType,
143 private_imc_imv_msg_t *this)
144 {
145 return this->msg_type;
146 }
147
148 METHOD(imc_imv_msg_t, get_msg_body, chunk_t,
149 private_imc_imv_msg_t *this)
150 {
151 return this->msg_body;
152 }
153
154 /**
155 * See header
156 */
157 tnccs_msg_t *imc_imv_msg_create_from_node(xmlNodePtr node, linked_list_t *errors)
158 {
159 private_imc_imv_msg_t *this;
160 xmlNsPtr ns;
161 xmlNodePtr cur;
162 xmlChar *content;
163 chunk_t b64_body;
164
165 INIT(this,
166 .public = {
167 .tnccs_msg_interface = {
168 .get_type = _get_type,
169 .get_node = _get_node,
170 .destroy = _destroy,
171 },
172 .get_msg_type = _get_msg_type,
173 .get_msg_body = _get_msg_body,
174 },
175 .type = IMC_IMV_MSG,
176 .node = node,
177 );
178
179 ns = node->ns;
180 cur = node->xmlChildrenNode;
181 while (cur)
182 {
183 if (streq((char*)cur->name, "Type") && cur->ns == ns)
184 {
185 content = xmlNodeGetContent(cur);
186 this->msg_type = strtoul((char*)content, NULL, 16);
187 xmlFree(content);
188 }
189 else if (streq((char*)cur->name, "Base64") && cur->ns == ns)
190 {
191 content = xmlNodeGetContent(cur);
192 b64_body = chunk_create((char*)content, strlen((char*)content));
193 this->msg_body = decode_base64(b64_body);
194 xmlFree(content);
195 }
196 cur = cur->next;
197 }
198
199 return &this->public.tnccs_msg_interface;
200 }
201
202 /**
203 * See header
204 */
205 tnccs_msg_t *imc_imv_msg_create(TNC_MessageType msg_type, chunk_t msg_body)
206 {
207 private_imc_imv_msg_t *this;
208 chunk_t b64_body;
209 char buf[10]; /* big enough for hex-encoded message type */
210 xmlNodePtr n;
211
212 INIT(this,
213 .public = {
214 .tnccs_msg_interface = {
215 .get_type = _get_type,
216 .get_node = _get_node,
217 .destroy = _destroy,
218 },
219 .get_msg_type = _get_msg_type,
220 .get_msg_body = _get_msg_body,
221 },
222 .type = IMC_IMV_MSG,
223 .node = xmlNewNode(NULL, BAD_CAST "IMC-IMV-Message"),
224 .msg_type = msg_type,
225 .msg_body = chunk_clone(msg_body),
226 );
227
228 /* add the message type number in hex */
229 n = xmlNewNode(NULL, BAD_CAST "Type");
230 snprintf(buf, 10, "%08x", this->msg_type);
231 xmlNodeSetContent(n, BAD_CAST buf);
232 xmlAddChild(this->node, n);
233
234 /* encode the message as a Base64 node */
235 n = xmlNewNode(NULL, BAD_CAST "Base64");
236 b64_body = encode_base64(this->msg_body);
237 xmlNodeSetContent(n, BAD_CAST b64_body.ptr);
238 xmlAddChild(this->node, n);
239 free(b64_body.ptr);
240
241 return &this->public.tnccs_msg_interface;
242 }