Added add_segment() method to TCG/PTS 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 this->base_attr->add_segment(this->base_attr, segment);
175 status = this->base_attr->process(this->base_attr, &attr_offset);
176
177 if (status != SUCCESS && status != NEED_MORE)
178 {
179 type = this->base_attr->get_type(this->base_attr);
180 if (type.vendor_id == PEN_IETF && type.type == IETF_ATTR_PA_TNC_ERROR)
181 {
182 /* error while processing a PA-TNC error attribute - abort */
183 return FALSE;
184 }
185 error_code = pen_type_create(PEN_IETF, PA_ERROR_INVALID_PARAMETER);
186 msg_info = get_base_attr_info(this);
187 *error = ietf_attr_pa_tnc_error_create_with_offset(error_code,
188 msg_info, PA_TNC_ATTR_HEADER_SIZE + attr_offset);
189 return FALSE;
190 }
191 this->need_more = (status == NEED_MORE);
192
193 return TRUE;
194 }
195
196 METHOD(seg_env_t, destroy, void,
197 private_seg_env_t *this)
198 {
199 DESTROY_IF(this->base_attr);
200 free(this);
201 }
202
203 /**
204 * See header
205 */
206 seg_env_t *seg_env_create(uint32_t base_attr_id, pa_tnc_attr_t *base_attr,
207 uint32_t max_seg_size)
208 {
209 private_seg_env_t *this;
210 chunk_t value;
211
212 base_attr->build(base_attr);
213 value = base_attr->get_value(base_attr);
214
215 /**
216 * The PA-TNC attribute header must not be segmented and
217 * there must be at least a first and one next segment
218 */
219 if (max_seg_size < PA_TNC_ATTR_HEADER_SIZE ||
220 max_seg_size >= PA_TNC_ATTR_HEADER_SIZE + value.len)
221 {
222 return NULL;
223 }
224
225 INIT(this,
226 .public = {
227 .get_base_attr_id = _get_base_attr_id,
228 .get_base_attr = _get_base_attr,
229 .get_base_attr_info = _get_base_attr_info,
230 .first_segment = _first_segment,
231 .next_segment = _next_segment,
232 .add_segment = _add_segment,
233 .destroy = _destroy,
234 },
235 .base_attr_id = base_attr_id,
236 .base_attr = base_attr->get_ref(base_attr),
237 .max_seg_size = max_seg_size,
238 .data = base_attr->get_value(base_attr),
239 );
240
241 return &this->public;
242 }
243
244 /**
245 * See header
246 */
247 seg_env_t *seg_env_create_from_data(uint32_t base_attr_id, chunk_t data,
248 uint32_t max_seg_size, pa_tnc_attr_t** error)
249 {
250 private_seg_env_t *this;
251 pen_type_t type, error_code;
252 bio_reader_t *reader;
253 chunk_t msg_info;
254 uint32_t offset = 0, attr_offset;
255 status_t status;
256
257 INIT(this,
258 .public = {
259 .get_base_attr_id = _get_base_attr_id,
260 .get_base_attr = _get_base_attr,
261 .get_base_attr_info = _get_base_attr_info,
262 .first_segment = _first_segment,
263 .next_segment = _next_segment,
264 .add_segment = _add_segment,
265 .destroy = _destroy,
266 },
267 .base_attr_id = base_attr_id,
268 .max_seg_size = max_seg_size,
269 );
270
271 /* create info field to be used by PA-TNC error messages */
272 memset(this->base_attr_info, 0xff, 4);
273 htoun32(this->base_attr_info + 4, base_attr_id);
274 msg_info = get_base_attr_info(this);
275
276 /* extract from base attribute segment from data */
277 reader = bio_reader_create(data);
278 this->base_attr = imcv_pa_tnc_attributes->create(imcv_pa_tnc_attributes,
279 reader, TRUE, &offset, msg_info, error);
280 reader->destroy(reader);
281
282 if (!this->base_attr)
283 {
284 destroy(this);
285 return NULL;
286 }
287 status = this->base_attr->process(this->base_attr, &attr_offset);
288
289 if (status != SUCCESS && status != NEED_MORE)
290 {
291 type = this->base_attr->get_type(this->base_attr);
292 if (!(type.vendor_id == PEN_IETF &&
293 type.type == IETF_ATTR_PA_TNC_ERROR))
294 {
295 error_code = pen_type_create(PEN_IETF, PA_ERROR_INVALID_PARAMETER);
296 *error = ietf_attr_pa_tnc_error_create_with_offset(error_code,
297 msg_info, PA_TNC_ATTR_HEADER_SIZE + attr_offset);
298 }
299 destroy(this);
300 return NULL;
301 }
302 this->need_more = (status == NEED_MORE);
303
304 return &this->public;
305 }
306