cleaned up XML code in tnccs-11 plugin
[strongswan.git] / src / libcharon / plugins / tnccs_11 / batch / tnccs_batch.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 "tnccs_batch.h"
17 #include "messages/tnccs_error_msg.h"
18
19 #include <tnc/tnccs/tnccs.h>
20
21 #include <collections/linked_list.h>
22 #include <utils/debug.h>
23
24 #include <libxml/parser.h>
25
26 #define TNCCS_NS "http://www.trustedcomputinggroup.org/IWG/TNC/1_0/IF_TNCCS#"
27 #define SCHEMA_NS "http://www.w3.org/2001/XMLSchema-instance"
28 #define TNCCS_XSD "https://www.trustedcomputinggroup.org/XML/SCHEMA/TNCCS_1.0.xsd"
29
30 typedef struct private_tnccs_batch_t private_tnccs_batch_t;
31
32 /**
33 * Private data of a tnccs_batch_t object.
34 *
35 */
36 struct private_tnccs_batch_t {
37 /**
38 * Public tnccs_batch_t interface.
39 */
40 tnccs_batch_t public;
41
42 /**
43 * Batch ID
44 */
45 int batch_id;
46
47 /**
48 * TNCC if TRUE, TNCS if FALSE
49 */
50 bool is_server;
51
52 /**
53 * linked list of TNCCS messages
54 */
55 linked_list_t *messages;
56
57 /**
58 * linked list of TNCCS error messages
59 */
60 linked_list_t *errors;
61
62 /**
63 * XML document
64 */
65 xmlDocPtr doc;
66
67 /**
68 * Encoded message
69 */
70 chunk_t encoding;
71 };
72
73 METHOD(tnccs_batch_t, get_encoding, chunk_t,
74 private_tnccs_batch_t *this)
75 {
76 return this->encoding;
77 }
78
79 METHOD(tnccs_batch_t, add_msg, void,
80 private_tnccs_batch_t *this, tnccs_msg_t* msg)
81 {
82 xmlNodePtr root;
83
84 DBG2(DBG_TNC, "adding %N message", tnccs_msg_type_names,
85 msg->get_type(msg));
86 this->messages->insert_last(this->messages, msg);
87 root = xmlDocGetRootElement(this->doc);
88 xmlAddChild(root, msg->get_node(msg));
89 }
90
91 METHOD(tnccs_batch_t, build, void,
92 private_tnccs_batch_t *this)
93 {
94 xmlChar *xmlbuf;
95 int buf_size;
96
97 xmlDocDumpFormatMemory(this->doc, &xmlbuf, &buf_size, 1);
98 this->encoding = chunk_create(xmlbuf, buf_size);
99 this->encoding = chunk_clone(this->encoding);
100 xmlFree(xmlbuf);
101 }
102
103 METHOD(tnccs_batch_t, process, status_t,
104 private_tnccs_batch_t *this)
105 {
106 tnccs_msg_t *tnccs_msg, *msg;
107 tnccs_error_type_t error_type = TNCCS_ERROR_OTHER;
108 char *error_msg, buf[BUF_LEN];
109 xmlNodePtr cur;
110 xmlNsPtr ns;
111 xmlChar *batchid, *recipient;
112 int batch_id;
113
114 this->doc = xmlParseMemory(this->encoding.ptr, this->encoding.len);
115 if (!this->doc)
116 {
117 error_type = TNCCS_ERROR_MALFORMED_BATCH;
118 error_msg = "failed to parse XML message";
119 goto fatal;
120 }
121
122 /* check out the XML document */
123 cur = xmlDocGetRootElement(this->doc);
124 if (!cur)
125 {
126 error_type = TNCCS_ERROR_MALFORMED_BATCH;
127 error_msg = "empty XML document";
128 goto fatal;
129 }
130
131 /* check TNCCS namespace */
132 ns = xmlSearchNsByHref(this->doc, cur, TNCCS_NS);
133 if (!ns)
134 {
135 error_type = TNCCS_ERROR_MALFORMED_BATCH;
136 error_msg = "TNCCS namespace not found";
137 goto fatal;
138 }
139
140 /* check XML document type */
141 if (xmlStrcmp(cur->name, "TNCCS-Batch"))
142 {
143 error_type = TNCCS_ERROR_MALFORMED_BATCH;
144 error_msg = buf;
145 snprintf(buf, BUF_LEN, "wrong XML document type '%s', expected TNCCS-Batch",
146 cur->name);
147 goto fatal;
148 }
149
150 /* check presence of BatchID property */
151 batchid = xmlGetProp(cur, "BatchId");
152 if (!batchid)
153 {
154 error_type = TNCCS_ERROR_INVALID_BATCH_ID;
155 error_msg = "BatchId is missing";
156 goto fatal;
157 }
158
159 /* check BatchID */
160 batch_id = atoi((char*)batchid);
161 xmlFree(batchid);
162 if (batch_id != this->batch_id)
163 {
164 error_type = TNCCS_ERROR_INVALID_BATCH_ID;
165 error_msg = buf;
166 snprintf(buf, BUF_LEN, "BatchId %d expected, got %d", this->batch_id,
167 batch_id);
168 goto fatal;
169 }
170
171 /* check presence of Recipient property */
172 recipient = xmlGetProp(cur, "Recipient");
173 if (!recipient)
174 {
175 error_type = TNCCS_ERROR_INVALID_RECIPIENT_TYPE;
176 error_msg = "Recipient is missing";
177 goto fatal;
178 }
179
180 /* check recipient */
181 if (!streq(recipient, this->is_server ? "TNCS" : "TNCC"))
182 {
183 error_type = TNCCS_ERROR_INVALID_RECIPIENT_TYPE;
184 error_msg = buf;
185 snprintf(buf, BUF_LEN, "message recipient expected '%s', got '%s'",
186 this->is_server ? "TNCS" : "TNCC", recipient);
187 xmlFree(recipient);
188 goto fatal;
189 }
190 xmlFree(recipient);
191
192 DBG2(DBG_TNC, "processing TNCCS Batch #%d", batch_id);
193
194 /* Now walk the tree, handling message nodes as we go */
195 for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next)
196 {
197 /* ignore empty or blank nodes */
198 if (xmlIsBlankNode(cur))
199 {
200 continue;
201 }
202
203 /* ignore nodes with wrong namespace */
204 if (cur->ns != ns)
205 {
206 DBG1(DBG_TNC, "ignoring message node '%s' having wrong namespace",
207 cur->name);
208 continue;
209 }
210
211 tnccs_msg = tnccs_msg_create_from_node(cur, this->errors);
212
213 /* exit if a message parsing error occurred */
214 if (this->errors->get_count(this->errors) > 0)
215 {
216 return FAILED;
217 }
218
219 /* ignore unrecognized messages */
220 if (!tnccs_msg)
221 {
222 continue;
223 }
224
225 this->messages->insert_last(this->messages, tnccs_msg);
226 }
227 return SUCCESS;
228
229 fatal:
230 msg = tnccs_error_msg_create(error_type, error_msg);
231 this->errors->insert_last(this->errors, msg);
232 return FAILED;
233 }
234
235 METHOD(tnccs_batch_t, create_msg_enumerator, enumerator_t*,
236 private_tnccs_batch_t *this)
237 {
238 return this->messages->create_enumerator(this->messages);
239 }
240
241 METHOD(tnccs_batch_t, create_error_enumerator, enumerator_t*,
242 private_tnccs_batch_t *this)
243 {
244 return this->errors->create_enumerator(this->errors);
245 }
246
247 METHOD(tnccs_batch_t, destroy, void,
248 private_tnccs_batch_t *this)
249 {
250 this->messages->destroy_offset(this->messages,
251 offsetof(tnccs_msg_t, destroy));
252 this->errors->destroy_offset(this->errors,
253 offsetof(tnccs_msg_t, destroy));
254 xmlFreeDoc(this->doc);
255 free(this->encoding.ptr);
256 free(this);
257 }
258
259 /**
260 * See header
261 */
262 tnccs_batch_t* tnccs_batch_create(bool is_server, int batch_id)
263 {
264 private_tnccs_batch_t *this;
265 xmlNodePtr n;
266 xmlNsPtr ns_xsi;
267 char buf[12];
268
269 INIT(this,
270 .public = {
271 .get_encoding = _get_encoding,
272 .add_msg = _add_msg,
273 .build = _build,
274 .process = _process,
275 .create_msg_enumerator = _create_msg_enumerator,
276 .create_error_enumerator = _create_error_enumerator,
277 .destroy = _destroy,
278 },
279 .is_server = is_server,
280 .messages = linked_list_create(),
281 .errors = linked_list_create(),
282 .batch_id = batch_id,
283 .doc = xmlNewDoc("1.0"),
284 );
285
286 DBG2(DBG_TNC, "creating TNCCS Batch #%d", this->batch_id);
287 n = xmlNewNode(NULL, "TNCCS-Batch");
288 xmlNewNs(n, TNCCS_NS, NULL);
289 ns_xsi = xmlNewNs(n, SCHEMA_NS, "xsi");
290 snprintf(buf, sizeof(buf), "%d", batch_id);
291 xmlNewProp(n, "BatchId", buf);
292 xmlNewProp(n, "Recipient", this->is_server ? "TNCC" : "TNCS");
293 xmlNewNsProp(n, ns_xsi, "schemaLocation", TNCCS_NS " " TNCCS_XSD);
294 xmlDocSetRootElement(this->doc, n);
295
296 return &this->public;
297 }
298
299 /**
300 * See header
301 */
302 tnccs_batch_t* tnccs_batch_create_from_data(bool is_server, int batch_id, chunk_t data)
303 {
304 private_tnccs_batch_t *this;
305
306 INIT(this,
307 .public = {
308 .get_encoding = _get_encoding,
309 .add_msg = _add_msg,
310 .build = _build,
311 .process = _process,
312 .create_msg_enumerator = _create_msg_enumerator,
313 .create_error_enumerator = _create_error_enumerator,
314 .destroy = _destroy,
315 },
316 .is_server = is_server,
317 .batch_id = batch_id,
318 .messages = linked_list_create(),
319 .errors = linked_list_create(),
320 .encoding = chunk_clone(data),
321 );
322
323 return &this->public;
324 }
325