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