ea185e782b8ca6cdaf635654763a1dac91bd3f0f
[strongswan.git] / src / libcharon / plugins / tnccs_11 / tnccs_11.c
1 /*
2 * Copyright (C) 2010 Andreas Steffen
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_11.h"
17 #include "batch/tnccs_batch.h"
18 #include "messages/tnccs_msg.h"
19 #include "messages/imc_imv_msg.h"
20 #include "messages/tnccs_preferred_language_msg.h"
21
22 #include <daemon.h>
23 #include <debug.h>
24 #include <threading/mutex.h>
25 #include <tnc/tncif.h>
26 #include <tnc/tncifimv_names.h>
27 #include <tnc/tnccs/tnccs.h>
28
29 typedef struct private_tnccs_11_t private_tnccs_11_t;
30
31 /**
32 * Private data of a tnccs_11_t object.
33 */
34 struct private_tnccs_11_t {
35
36 /**
37 * Public tls_t interface.
38 */
39 tls_t public;
40
41 /**
42 * TNCC if TRUE, TNCS if FALSE
43 */
44 bool is_server;
45
46 /**
47 * Connection ID assigned to this TNCCS connection
48 */
49 TNC_ConnectionID connection_id;
50
51 /**
52 * Last TNCCS batch ID
53 */
54 int batch_id;
55
56 /**
57 * TNCCS batch being constructed
58 */
59 tnccs_batch_t *batch;
60
61 /**
62 * Mutex locking the batch in construction
63 */
64 mutex_t *mutex;
65
66 /**
67 * Flag set by IMC/IMV RequestHandshakeRetry() function
68 */
69 bool request_handshake_retry;
70
71 /**
72 * Set of IMV recommendations (TNC Server only)
73 */
74 recommendations_t *recs;
75 };
76
77 METHOD(tnccs_t, send_msg, void,
78 private_tnccs_11_t* this, TNC_IMCID imc_id, TNC_IMVID imv_id,
79 TNC_BufferReference msg,
80 TNC_UInt32 msg_len,
81 TNC_MessageType msg_type)
82 {
83 tnccs_msg_t *tnccs_msg;
84
85 tnccs_msg = imc_imv_msg_create(msg_type, chunk_create(msg, msg_len));
86
87 /* adding an IMC-IMV Message to TNCCS batch */
88 this->mutex->lock(this->mutex);
89 if (!this->batch)
90 {
91 this->batch = tnccs_batch_create(this->is_server, ++this->batch_id);
92 }
93 this->batch->add_msg(this->batch, tnccs_msg);
94 this->mutex->unlock(this->mutex);
95 }
96
97 /**
98 * Handle a single TNCCS message according to its type
99 */
100 static void handle_message(private_tnccs_11_t *this, tnccs_msg_t *msg)
101 {
102 switch (msg->get_type(msg))
103 {
104 case IMC_IMV_MSG:
105 {
106 imc_imv_msg_t *imc_imv_msg;
107 TNC_MessageType msg_type;
108 chunk_t msg_body;
109
110 imc_imv_msg = (imc_imv_msg_t*)msg;
111 msg_type = imc_imv_msg->get_msg_type(imc_imv_msg);
112 msg_body = imc_imv_msg->get_msg_body(imc_imv_msg);
113
114 DBG2(DBG_TNC, "handling IMC_IMV message type 0x%08x", msg_type);
115
116 if (this->is_server)
117 {
118 charon->imvs->receive_message(charon->imvs,
119 this->connection_id, msg_body.ptr, msg_body.len, msg_type);
120 }
121 else
122 {
123 charon->imcs->receive_message(charon->imcs,
124 this->connection_id, msg_body.ptr, msg_body.len,msg_type);
125 }
126 break;
127 }
128 case TNCCS_MSG_PREFERRED_LANGUAGE:
129 {
130 tnccs_preferred_language_msg_t *lang_msg;
131 char *lang;
132
133 lang_msg = (tnccs_preferred_language_msg_t*)msg;
134 lang = lang_msg->get_preferred_language(lang_msg);
135
136 DBG2(DBG_TNC, "setting preferred language to '%s'", lang);
137 this->recs->set_preferred_language(this->recs,
138 chunk_create(lang, strlen(lang)));
139 break;
140 }
141 default:
142 break;
143 }
144 }
145
146 METHOD(tls_t, process, status_t,
147 private_tnccs_11_t *this, void *buf, size_t buflen)
148 {
149 chunk_t data;
150 tnccs_batch_t *batch;
151 status_t status;
152
153 if (this->is_server && !this->connection_id)
154 {
155 this->connection_id = charon->tnccs->create_connection(charon->tnccs,
156 (tnccs_t*)this, _send_msg,
157 &this->request_handshake_retry, &this->recs);
158 if (!this->connection_id)
159 {
160 return FAILED;
161 }
162 charon->imvs->notify_connection_change(charon->imvs,
163 this->connection_id, TNC_CONNECTION_STATE_CREATE);
164 }
165
166 data = chunk_create(buf, buflen);
167 DBG1(DBG_TNC, "received TNCCS Batch (%u bytes) for Connection ID %u",
168 data.len, this->connection_id);
169 DBG3(DBG_TNC, "%.*s", data.len, data.ptr);
170 batch = tnccs_batch_create_from_data(this->is_server, ++this->batch_id, data);
171 status = batch->process(batch);
172
173 if (status != FAILED)
174 {
175 enumerator_t *enumerator;
176 tnccs_msg_t *msg;
177
178 enumerator = batch->create_msg_enumerator(batch);
179 while (enumerator->enumerate(enumerator, &msg))
180 {
181 handle_message(this, msg);
182 }
183 enumerator->destroy(enumerator);
184
185 }
186 batch->destroy(batch);
187
188 return NEED_MORE;
189 }
190
191 METHOD(tls_t, build, status_t,
192 private_tnccs_11_t *this, void *buf, size_t *buflen, size_t *msglen)
193 {
194 status_t status;
195
196 /* Initialize the connection */
197 if (!this->is_server && !this->connection_id)
198 {
199 tnccs_msg_t *msg;
200 char *pref_lang;
201
202 this->connection_id = charon->tnccs->create_connection(charon->tnccs,
203 (tnccs_t*)this, _send_msg,
204 &this->request_handshake_retry, NULL);
205 if (!this->connection_id)
206 {
207 return FAILED;
208 }
209
210 /* Create TNCCS-PreferredLanguage message */
211 pref_lang = charon->imcs->get_preferred_language(charon->imcs);
212 msg = tnccs_preferred_language_msg_create(pref_lang);
213 this->mutex->lock(this->mutex);
214 this->batch = tnccs_batch_create(this->is_server, ++this->batch_id);
215 this->batch->add_msg(this->batch, msg);
216 this->mutex->unlock(this->mutex);
217
218 charon->imcs->notify_connection_change(charon->imcs,
219 this->connection_id, TNC_CONNECTION_STATE_CREATE);
220 charon->imcs->notify_connection_change(charon->imcs,
221 this->connection_id, TNC_CONNECTION_STATE_HANDSHAKE);
222 charon->imcs->begin_handshake(charon->imcs, this->connection_id);
223 }
224
225 /* Do not allow any asynchronous IMCs or IMVs to add additional messages */
226 this->mutex->lock(this->mutex);
227
228 if (this->batch)
229 {
230 chunk_t data;
231
232 this->batch->build(this->batch);
233 data = this->batch->get_encoding(this->batch);
234 DBG1(DBG_TNC, "sending TNCCS Batch (%d bytes) for Connection ID %u",
235 data.len, this->connection_id);
236 DBG3(DBG_TNC, "%.*s", data.len, data.ptr);
237 *msglen = data.len;
238
239 if (data.len > *buflen)
240 {
241 DBG1(DBG_TNC, "fragmentation of TNCCS batch not supported yet");
242 }
243 else
244 {
245 *buflen = data.len;
246 }
247 memcpy(buf, data.ptr, *buflen);
248 this->batch->destroy(this->batch);
249 this->batch = NULL;
250 status = ALREADY_DONE;
251 }
252 else
253 {
254 DBG1(DBG_TNC, "no TNCCS Batch to send");
255 status = INVALID_STATE;
256 }
257 this->mutex->unlock(this->mutex);
258
259 return status;
260 }
261
262 METHOD(tls_t, is_server, bool,
263 private_tnccs_11_t *this)
264 {
265 return this->is_server;
266 }
267
268 METHOD(tls_t, get_purpose, tls_purpose_t,
269 private_tnccs_11_t *this)
270 {
271 return TLS_PURPOSE_EAP_TNC;
272 }
273
274 METHOD(tls_t, is_complete, bool,
275 private_tnccs_11_t *this)
276 {
277 TNC_IMV_Action_Recommendation rec;
278 TNC_IMV_Evaluation_Result eval;
279
280 if (this->recs && this->recs->have_recommendation(this->recs, &rec, &eval))
281 {
282 DBG2(DBG_TNC, "Final recommendation is '%N' and evaluation is '%N'",
283 action_recommendation_names, rec, evaluation_result_names, eval);
284
285 return charon->imvs->enforce_recommendation(charon->imvs, rec);
286 }
287 else
288 {
289 return FALSE;
290 }
291 }
292
293 METHOD(tls_t, get_eap_msk, chunk_t,
294 private_tnccs_11_t *this)
295 {
296 return chunk_empty;
297 }
298
299 METHOD(tls_t, destroy, void,
300 private_tnccs_11_t *this)
301 {
302 charon->tnccs->remove_connection(charon->tnccs, this->connection_id);
303 this->mutex->destroy(this->mutex);
304 DESTROY_IF(this->batch);
305 free(this);
306 }
307
308 /**
309 * See header
310 */
311 tls_t *tnccs_11_create(bool is_server)
312 {
313 private_tnccs_11_t *this;
314
315 INIT(this,
316 .public = {
317 .process = _process,
318 .build = _build,
319 .is_server = _is_server,
320 .get_purpose = _get_purpose,
321 .is_complete = _is_complete,
322 .get_eap_msk = _get_eap_msk,
323 .destroy = _destroy,
324 },
325 .is_server = is_server,
326 .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
327 );
328
329 return &this->public;
330 }