Implemented add_segment method for PA-TNC attributes
[strongswan.git] / src / libimcv / seg / seg_env.c
1 /*
2 * Copyright (C) 2014 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 "seg_env.h"
17
18 #include "imcv.h"
19 #include "pa_tnc/pa_tnc_msg.h"
20 #include "ietf/ietf_attr_pa_tnc_error.h"
21 #include "tcg/seg/tcg_seg_attr_seg_env.h"
22
23 #include <utils/debug.h>
24 #include <bio/bio_reader.h>
25 #include <bio/bio_writer.h>
26
27 #define BASE_ATTR_ID_PREFIX 0xFF
28
29 typedef struct private_seg_env_t private_seg_env_t;
30
31 /**
32 * Private data of a seg_env_t object.
33 */
34 struct private_seg_env_t {
35
36 /**
37 * Public seg_env_t interface.
38 */
39 seg_env_t public;
40
41 /**
42 * Base Attribute ID
43 */
44 uint32_t base_attr_id;
45
46 /**
47 * Base Attribute
48 */
49 pa_tnc_attr_t *base_attr;
50
51 /**
52 * Base Attribute Info to be used for PA-TNC error messages
53 */
54 u_char base_attr_info[8];
55
56 /**
57 * Base Attribute needs more segment data
58 */
59 bool need_more;
60
61 /**
62 * Pointer to remaining attribute data to be sent
63 */
64 chunk_t data;
65
66 /**
67 * Maximum PA-TNC attribute segment size
68 */
69 uint32_t max_seg_size;
70
71 };
72
73 METHOD(seg_env_t, get_base_attr_id, uint32_t,
74 private_seg_env_t *this)
75 {
76 return this->base_attr_id;
77 }
78
79 METHOD(seg_env_t, get_base_attr, pa_tnc_attr_t*,
80 private_seg_env_t *this)
81 {
82 return this->need_more ? NULL : this->base_attr->get_ref(this->base_attr);
83 }
84
85 METHOD(seg_env_t, get_base_attr_info, chunk_t,
86 private_seg_env_t *this)
87 {
88 return chunk_create(this->base_attr_info, 8);
89 }
90
91 METHOD(seg_env_t, first_segment, pa_tnc_attr_t*,
92 private_seg_env_t *this)
93 {
94 pa_tnc_attr_t *seg_env_attr;
95 bio_writer_t *writer;
96 pen_type_t type;
97 chunk_t segment_data, value;
98 uint8_t flags, seg_env_flags;
99
100 /* get components of base attribute header and data */
101 flags = this->base_attr->get_noskip_flag(this->base_attr) ?
102 PA_TNC_ATTR_FLAG_NOSKIP : PA_TNC_ATTR_FLAG_NONE;
103 type = this->base_attr->get_type(this->base_attr);
104
105 /* attribute data going into the first segment */
106 segment_data = this->data;
107 segment_data.len = this->max_seg_size - PA_TNC_ATTR_HEADER_SIZE;
108
109 /* build encoding of the base attribute header and first segment data */
110 writer = bio_writer_create(this->max_seg_size);
111 writer->write_uint8 (writer, flags);
112 writer->write_uint24(writer, type.vendor_id);
113 writer->write_uint32(writer, type.type);
114 writer->write_uint32(writer, PA_TNC_ATTR_HEADER_SIZE + this->data.len);
115 writer->write_data (writer, segment_data);
116 value = writer->extract_buf(writer);
117 writer->destroy(writer);
118 this->data = chunk_skip(this->data, segment_data.len);
119
120 DBG2(DBG_TNC, "creating first segment for base attribute ID %d (%d bytes)",
121 this->base_attr_id, this->max_seg_size);
122
123 seg_env_flags = SEG_ENV_FLAG_START | SEG_ENV_FLAG_MORE;
124 seg_env_attr = tcg_seg_attr_seg_env_create(value, seg_env_flags,
125 this->base_attr_id);
126 chunk_free(&value);
127
128 return seg_env_attr;
129 }
130
131 METHOD(seg_env_t, next_segment, pa_tnc_attr_t*,
132 private_seg_env_t *this, bool *last)
133 {
134 pa_tnc_attr_t *seg_env_attr;
135 chunk_t segment_data;
136 uint8_t seg_env_flags;
137 bool is_last_segment;
138
139 if (this->data.len == 0)
140 {
141 /* no more attribute data to segment available */
142 return NULL;
143 }
144
145 /* attribute data going into the next segment */
146 segment_data = this->data;
147 segment_data.len = min(this->max_seg_size, this->data.len);
148 this->data = chunk_skip(this->data, segment_data.len);
149
150 is_last_segment = (this->data.len == 0);
151 if (last)
152 {
153 *last = is_last_segment;
154 }
155 DBG2(DBG_TNC, "creating %s segment for base attribute ID %d (%d bytes)",
156 is_last_segment ? "last" : "next", this->base_attr_id,
157 segment_data.len);
158
159 seg_env_flags = is_last_segment ? SEG_ENV_FLAG_NONE : SEG_ENV_FLAG_MORE;
160 seg_env_attr = tcg_seg_attr_seg_env_create(segment_data, seg_env_flags,
161 this->base_attr_id);
162
163 return seg_env_attr;
164 }
165
166 METHOD(seg_env_t, add_segment, bool,
167 private_seg_env_t *this, chunk_t segment, pa_tnc_attr_t **error)
168 {
169 pen_type_t type, error_code;
170 uint32_t attr_offset;
171 chunk_t msg_info;
172 status_t status;
173
174 /* not all attributes might have implemented the add_segment method */
175 if (!this->base_attr->add_segment)
176 {
177 return FALSE;
178 }
179 this->base_attr->add_segment(this->base_attr, segment);
180 status = this->base_attr->process(this->base_attr, &attr_offset);
181
182 if (status != SUCCESS && status != NEED_MORE)
183 {
184 type = this->base_attr->get_type(this->base_attr);
185 if (type.vendor_id == PEN_IETF && type.type == IETF_ATTR_PA_TNC_ERROR)
186 {
187 /* error while processing a PA-TNC error attribute - abort */
188 return FALSE;
189 }
190 error_code = pen_type_create(PEN_IETF, PA_ERROR_INVALID_PARAMETER);
191 msg_info = get_base_attr_info(this);
192 *error = ietf_attr_pa_tnc_error_create_with_offset(error_code,
193 msg_info, PA_TNC_ATTR_HEADER_SIZE + attr_offset);
194 return FALSE;
195 }
196 this->need_more = (status == NEED_MORE);
197
198 return TRUE;
199 }
200
201 METHOD(seg_env_t, destroy, void,
202 private_seg_env_t *this)
203 {
204 DESTROY_IF(this->base_attr);
205 free(this);
206 }
207
208 /**
209 * See header
210 */
211 seg_env_t *seg_env_create(uint32_t base_attr_id, pa_tnc_attr_t *base_attr,
212 uint32_t max_seg_size)
213 {
214 private_seg_env_t *this;
215 chunk_t value;
216
217 base_attr->build(base_attr);
218 value = base_attr->get_value(base_attr);
219
220 /**
221 * The PA-TNC attribute header must not be segmented and
222 * there must be at least a first and one next segment
223 */
224 if (max_seg_size < PA_TNC_ATTR_HEADER_SIZE ||
225 max_seg_size >= PA_TNC_ATTR_HEADER_SIZE + value.len)
226 {
227 return NULL;
228 }
229
230 INIT(this,
231 .public = {
232 .get_base_attr_id = _get_base_attr_id,
233 .get_base_attr = _get_base_attr,
234 .get_base_attr_info = _get_base_attr_info,
235 .first_segment = _first_segment,
236 .next_segment = _next_segment,
237 .add_segment = _add_segment,
238 .destroy = _destroy,
239 },
240 .base_attr_id = base_attr_id,
241 .base_attr = base_attr->get_ref(base_attr),
242 .max_seg_size = max_seg_size,
243 .data = base_attr->get_value(base_attr),
244 );
245
246 return &this->public;
247 }
248
249 /**
250 * See header
251 */
252 seg_env_t *seg_env_create_from_data(uint32_t base_attr_id, chunk_t data,
253 uint32_t max_seg_size, pa_tnc_attr_t** error)
254 {
255 private_seg_env_t *this;
256 pen_type_t type, error_code;
257 bio_reader_t *reader;
258 chunk_t msg_info;
259 uint32_t offset = 0, attr_offset;
260 status_t status;
261
262 INIT(this,
263 .public = {
264 .get_base_attr_id = _get_base_attr_id,
265 .get_base_attr = _get_base_attr,
266 .get_base_attr_info = _get_base_attr_info,
267 .first_segment = _first_segment,
268 .next_segment = _next_segment,
269 .add_segment = _add_segment,
270 .destroy = _destroy,
271 },
272 .base_attr_id = base_attr_id,
273 .max_seg_size = max_seg_size,
274 );
275
276 /* create info field to be used by PA-TNC error messages */
277 memset(this->base_attr_info, 0xff, 4);
278 htoun32(this->base_attr_info + 4, base_attr_id);
279 msg_info = get_base_attr_info(this);
280
281 /* extract from base attribute segment from data */
282 reader = bio_reader_create(data);
283 this->base_attr = imcv_pa_tnc_attributes->create(imcv_pa_tnc_attributes,
284 reader, TRUE, &offset, msg_info, error);
285 reader->destroy(reader);
286
287 if (!this->base_attr)
288 {
289 destroy(this);
290 return NULL;
291 }
292 status = this->base_attr->process(this->base_attr, &attr_offset);
293
294 if (status != SUCCESS && status != NEED_MORE)
295 {
296 type = this->base_attr->get_type(this->base_attr);
297 if (!(type.vendor_id == PEN_IETF &&
298 type.type == IETF_ATTR_PA_TNC_ERROR))
299 {
300 error_code = pen_type_create(PEN_IETF, PA_ERROR_INVALID_PARAMETER);
301 *error = ietf_attr_pa_tnc_error_create_with_offset(error_code,
302 msg_info, PA_TNC_ATTR_HEADER_SIZE + attr_offset);
303 }
304 destroy(this);
305 return NULL;
306 }
307 this->need_more = (status == NEED_MORE);
308
309 return &this->public;
310 }
311