prototype implementation using the pb_tnc_message_t class
[strongswan.git] / src / libcharon / plugins / tnccs_20 / tnccs_20.c
1 /*
2 * Copyright (C) 2010 Sansar Choinyanbuu
3 * 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_20.h"
17 #include "tnccs_20_types.h"
18 #include "messages/pb_tnc_message.h"
19 #include "messages/pb_pa_message.h"
20
21 #include <debug.h>
22 #include <daemon.h>
23 #include <threading/mutex.h>
24 #include <tnc/tncif.h>
25 #include <tnc/tncifimv_names.h>
26 #include <tnc/tnccs/tnccs.h>
27
28 typedef struct private_tnccs_20_t private_tnccs_20_t;
29
30 /**
31 * Private data of a tnccs_20_t object.
32 */
33 struct private_tnccs_20_t {
34
35 /**
36 * Public tls_t interface.
37 */
38 tls_t public;
39
40 /**
41 * TNCC if TRUE, TNCS if FALSE
42 */
43 bool is_server;
44
45 /**
46 * Connection ID assigned to this TNCCS connection
47 */
48 TNC_ConnectionID connection_id;
49
50 /**
51 * Batch being constructed
52 */
53 chunk_t batch;
54
55 /**
56 * Mutex locking the batch in construction
57 */
58 mutex_t *mutex;
59
60 /**
61 * Flag set by IMC/IMV RequestHandshakeRetry() function
62 */
63 bool request_handshake_retry;
64
65 /**
66 * Set of IMV recommendations (TNC Server only)
67 */
68 recommendations_t *recs;
69 };
70
71 /**
72 * PB-TNC Message (see section 4.2 of RFC 5793)
73 *
74 * 0 1 2 3
75 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
76 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
77 * | Flags | PB-TNC Vendor ID |
78 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
79 * | PB-TNC Message Type |
80 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
81 * | PB-TNC Message Length |
82 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
83 * | PB-TNC Message Value (Variable Length) |
84 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
85 */
86
87 #define PB_TNC_HEADER_SIZE 12
88 #define PB_TNC_NOSKIP_FLAG (1<<7)
89 #define PB_TNC_IETF_VENDOR_ID 0x000000
90
91 static chunk_t build_pb_tnc_msg(pb_tnc_msg_type_t msg_type, chunk_t msg_value)
92 {
93 chunk_t msg, msg_header;
94 tls_writer_t *writer;
95 size_t msg_len;
96
97 msg_len = PB_TNC_HEADER_SIZE + msg_value.len;
98 writer = tls_writer_create(PB_TNC_HEADER_SIZE);
99 writer->write_uint8 (writer, PB_TNC_NOSKIP_FLAG);
100 writer->write_uint24(writer, PB_TNC_IETF_VENDOR_ID);
101 writer->write_uint32(writer, msg_type);
102 writer->write_uint32(writer, msg_len);
103 msg_header = writer->get_buf(writer);
104 msg = chunk_cat("cm", msg_header, msg_value);
105 writer->destroy(writer);
106
107 DBG2(DBG_TNC, "building %N message (%u bytes)", pb_tnc_msg_type_names,
108 msg_type, msg_len);
109 DBG3(DBG_TNC,"%B", &msg);
110
111 return msg;
112 }
113
114 static status_t process_pb_tnc_msg(tls_reader_t *reader,
115 pb_tnc_message_t **pb_tnc_msg)
116 {
117 u_int8_t flags;
118 u_int32_t vendor_id, msg_type;
119 size_t msg_len;
120 chunk_t msg, msg_value;
121
122 msg = reader->peek(reader);
123
124 if (msg.len < PB_TNC_HEADER_SIZE)
125 {
126 DBG1(DBG_TNC, "%u bytes insufficient to parse PB-TNC message header",
127 msg.len);
128 return FAILED;
129 }
130 reader->read_uint8 (reader, &flags);
131 reader->read_uint24(reader, &vendor_id);
132 reader->read_uint32(reader, &msg_type);
133 reader->read_uint32(reader, &msg_len);
134
135 DBG2(DBG_TNC, "processing PB-TNC message (%u bytes)", msg_len);
136
137 if (msg_len < PB_TNC_HEADER_SIZE)
138 {
139 DBG1(DBG_TNC, "%u bytes too small for PB-TNC message length", msg_len);
140 return FAILED;
141 }
142 if (msg_len > msg.len)
143 {
144 DBG1(DBG_TNC, "%u bytes insufficient to parse PB-TNC message", msg.len);
145 return FAILED;
146 }
147 msg.len = msg_len;
148 DBG3(DBG_TNC, "%B", &msg);
149 reader->read_data(reader, msg_len - PB_TNC_HEADER_SIZE, &msg_value);
150
151 if (vendor_id != PB_TNC_IETF_VENDOR_ID || msg_type > PB_MSG_ROOF)
152 {
153 if (flags & PB_TNC_NOSKIP_FLAG)
154 {
155 DBG1(DBG_TNC, "cannot process PB-TNC message with Vendor ID 0x%06x "
156 " and type 0x%08x", vendor_id, msg);
157 return FAILED;
158 }
159 else
160 {
161 DBG1(DBG_TNC, "ignore PB-TNC message with Vendor ID 0x%06x "
162 " and type 0x%08x", vendor_id, msg);
163 return INVALID_STATE;
164 }
165 }
166 DBG2(DBG_TNC, "processing %N message (%u bytes)", pb_tnc_msg_type_names,
167 msg_type, msg_value.len);
168 *pb_tnc_msg = pb_tnc_message_create(msg_type, msg_value);
169
170 return SUCCESS;
171 }
172
173 METHOD(tnccs_t, send_message, void,
174 private_tnccs_20_t* this, TNC_IMCID imc_id, TNC_IMVID imv_id,
175 TNC_BufferReference msg,
176 TNC_UInt32 msg_len,
177 TNC_MessageType msg_type)
178 {
179 pb_tnc_message_t *pb_pa_msg;
180 chunk_t pb_tnc_msg;
181 TNC_MessageSubtype msg_sub_type;
182 TNC_VendorID msg_vendor_id;
183
184 msg_sub_type = msg_type & TNC_SUBTYPE_ANY;
185 msg_vendor_id = (msg_type >> 8) & TNC_VENDORID_ANY;
186
187 pb_pa_msg = pb_pa_message_create(msg_vendor_id, msg_sub_type, imc_id, imv_id,
188 chunk_create(msg, msg_len));
189 pb_tnc_msg = build_pb_tnc_msg(PB_MSG_PA, pb_pa_msg->get_encoding(pb_pa_msg));
190 pb_pa_msg->destroy(pb_pa_msg);
191
192 this->mutex->lock(this->mutex);
193 this->batch = chunk_cat("mm", this->batch, pb_tnc_msg);
194 this->mutex->unlock(this->mutex);
195 }
196
197 METHOD(tls_t, process, status_t,
198 private_tnccs_20_t *this, void *buf, size_t buflen)
199 {
200 tls_reader_t *reader;
201 pb_tnc_message_t *msg = NULL;
202 status_t status;
203 char *pos;
204 size_t len;
205
206 if (this->is_server && !this->connection_id)
207 {
208 this->connection_id = charon->tnccs->create_connection(charon->tnccs,
209 (tnccs_t*)this, _send_message,
210 &this->request_handshake_retry, &this->recs);
211 if (!this->connection_id)
212 {
213 return FAILED;
214 }
215 charon->imvs->notify_connection_change(charon->imvs,
216 this->connection_id, TNC_CONNECTION_STATE_CREATE);
217
218 /* Test reason enumerator */
219 {
220 chunk_t reason, reason_2 = { "virus alarm", 11 };
221 chunk_t reason_lang, reason_lang_2 = { "en, ru", 6 };
222 TNC_IMVID id;
223 enumerator_t *enumerator;
224
225 this->recs->set_reason_string(this->recs, 2, reason_2);
226 this->recs->set_reason_language(this->recs, 2, reason_lang_2);
227
228 enumerator = this->recs->create_reason_enumerator(this->recs);
229 while (enumerator->enumerate(enumerator, &id, &reason, &reason_lang))
230 {
231 DBG1(DBG_TNC, "IMV %u: reason = '%.*s', lang = '%.*s'",
232 id, reason.len, reason.ptr, reason_lang.len, reason_lang.ptr);
233 }
234 enumerator->destroy(enumerator);
235 }
236 }
237 DBG1(DBG_TNC, "received TNCCS Batch (%u bytes) for Connection ID %u",
238 buflen, this->connection_id);
239
240 pos = strchr(buf, '|');
241 if (pos)
242 {
243 pos++;
244 len = buflen - (pos - (char*)buf);
245 }
246 else
247 {
248 pos = buf;
249 len = buflen;
250 }
251 reader = tls_reader_create(chunk_create(pos, len));
252 while (reader->remaining(reader) > 0)
253 {
254 switch (process_pb_tnc_msg(reader, &msg))
255 {
256 case SUCCESS:
257 break;
258 case INVALID_STATE:
259 continue;
260 default:
261 reader->destroy(reader);
262 return FAILED;
263 }
264
265 status = msg->process(msg);
266 if (status != SUCCESS)
267 {
268 msg->destroy(msg);
269 return status;
270 }
271
272 switch (msg->get_type(msg))
273 {
274 case PB_MSG_PA:
275 {
276 TNC_MessageType msg_type;
277 u_int32_t vendor_id, subtype;
278 chunk_t msg_body;
279 pb_pa_message_t *pb_pa_msg;
280
281 pb_pa_msg = (pb_pa_message_t*)msg;
282 vendor_id = pb_pa_msg->get_vendor_id(pb_pa_msg, &subtype);
283 msg_type = (vendor_id << 8) | subtype;
284 msg_body = pb_pa_msg->get_body(pb_pa_msg);
285
286 if (this->is_server)
287 {
288 charon->imvs->receive_message(charon->imvs,
289 this->connection_id, msg_body.ptr, msg_body.len,
290 msg_type);
291 }
292 else
293 {
294 charon->imcs->receive_message(charon->imcs,
295 this->connection_id, msg_body.ptr, msg_body.len,
296 msg_type);
297 }
298 break;
299 }
300 case PB_MSG_ERROR:
301 break;
302 case PB_MSG_EXPERIMENTAL:
303 break;
304 case PB_MSG_LANGUAGE_PREFERENCE:
305 break;
306 case PB_MSG_ASSESSMENT_RESULT:
307 case PB_MSG_ACCESS_RECOMMENDATION:
308 case PB_MSG_REMEDIATION_PARAMETERS:
309 case PB_MSG_REASON_STRING:
310 break;
311 }
312 msg->destroy(msg);
313 }
314 reader->destroy(reader);
315
316 if (this->is_server)
317 {
318 charon->imvs->batch_ending(charon->imvs, this->connection_id);
319 }
320 else
321 {
322 charon->imcs->batch_ending(charon->imcs, this->connection_id);
323 }
324
325 return NEED_MORE;
326 }
327
328 METHOD(tls_t, build, status_t,
329 private_tnccs_20_t *this, void *buf, size_t *buflen, size_t *msglen)
330 {
331 char *msg = this->is_server ? "tncs->tncc 2.0|" : "tncc->tncs 2.0|";
332 size_t len;
333
334 this->mutex->lock(this->mutex);
335 this->batch = chunk_cat("cm", chunk_create(msg, strlen(msg)), this->batch);
336 this->mutex->unlock(this->mutex);
337
338 if (!this->is_server && !this->connection_id)
339 {
340 this->connection_id = charon->tnccs->create_connection(charon->tnccs,
341 (tnccs_t*)this, _send_message,
342 &this->request_handshake_retry, NULL);
343 if (!this->connection_id)
344 {
345 return FAILED;
346 }
347 charon->imcs->notify_connection_change(charon->imcs,
348 this->connection_id, TNC_CONNECTION_STATE_CREATE);
349 charon->imcs->notify_connection_change(charon->imcs,
350 this->connection_id, TNC_CONNECTION_STATE_HANDSHAKE);
351 charon->imcs->begin_handshake(charon->imcs, this->connection_id);
352 }
353
354 this->mutex->lock(this->mutex);
355 len = this->batch.len;
356 *msglen = len;
357 *buflen = len;
358 memcpy(buf, this->batch.ptr, len);
359 chunk_free(&this->batch);
360 this->mutex->unlock(this->mutex);
361
362 DBG1(DBG_TNC, "sending TNCCS Batch (%d bytes) for Connection ID %u",
363 len, this->connection_id);
364
365 return ALREADY_DONE;
366 }
367
368 METHOD(tls_t, is_server, bool,
369 private_tnccs_20_t *this)
370 {
371 return this->is_server;
372 }
373
374 METHOD(tls_t, get_purpose, tls_purpose_t,
375 private_tnccs_20_t *this)
376 {
377 return TLS_PURPOSE_EAP_TNC;
378 }
379
380 METHOD(tls_t, is_complete, bool,
381 private_tnccs_20_t *this)
382 {
383 TNC_IMV_Action_Recommendation rec;
384 TNC_IMV_Evaluation_Result eval;
385
386 if (this->recs && this->recs->have_recommendation(this->recs, &rec, &eval))
387 {
388 DBG2(DBG_TNC, "Final recommendation '%N' and evaluation '%N'",
389 action_recommendation_names, rec, evaluation_result_names, eval);
390
391 return charon->imvs->enforce_recommendation(charon->imvs, rec);
392 }
393 else
394 {
395 return FALSE;
396 }
397 }
398
399 METHOD(tls_t, get_eap_msk, chunk_t,
400 private_tnccs_20_t *this)
401 {
402 return chunk_empty;
403 }
404
405 METHOD(tls_t, destroy, void,
406 private_tnccs_20_t *this)
407 {
408 charon->tnccs->remove_connection(charon->tnccs, this->connection_id);
409 this->mutex->destroy(this->mutex);
410 free(this->batch.ptr);
411 free(this);
412 }
413
414 /**
415 * See header
416 */
417 tls_t *tnccs_20_create(bool is_server)
418 {
419 private_tnccs_20_t *this;
420
421 INIT(this,
422 .public = {
423 .process = _process,
424 .build = _build,
425 .is_server = _is_server,
426 .get_purpose = _get_purpose,
427 .is_complete = _is_complete,
428 .get_eap_msk = _get_eap_msk,
429 .destroy = _destroy,
430 },
431 .is_server = is_server,
432 .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
433 );
434
435 return &this->public;
436 }