315 lines
8.0 KiB
C
315 lines
8.0 KiB
C
/*
|
|
* Copyright (C) 2014-2015 Andreas Steffen
|
|
* HSR Hochschule fuer Technik Rapperswil
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License as published by the
|
|
* Free Software Foundation; either version 2 of the License, or (at your
|
|
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
* for more details.
|
|
*/
|
|
|
|
#include "seg_env.h"
|
|
|
|
#include "imcv.h"
|
|
#include "pa_tnc/pa_tnc_msg.h"
|
|
#include "ietf/ietf_attr_pa_tnc_error.h"
|
|
#include "tcg/seg/tcg_seg_attr_seg_env.h"
|
|
|
|
#include <utils/debug.h>
|
|
#include <bio/bio_reader.h>
|
|
#include <bio/bio_writer.h>
|
|
|
|
#define BASE_ATTR_ID_PREFIX 0xFF
|
|
|
|
typedef struct private_seg_env_t private_seg_env_t;
|
|
|
|
/**
|
|
* Private data of a seg_env_t object.
|
|
*/
|
|
struct private_seg_env_t {
|
|
|
|
/**
|
|
* Public seg_env_t interface.
|
|
*/
|
|
seg_env_t public;
|
|
|
|
/**
|
|
* Base Attribute ID
|
|
*/
|
|
uint32_t base_attr_id;
|
|
|
|
/**
|
|
* Base Attribute
|
|
*/
|
|
pa_tnc_attr_t *base_attr;
|
|
|
|
/**
|
|
* Base Attribute Info to be used for PA-TNC error messages
|
|
*/
|
|
u_char base_attr_info[8];
|
|
|
|
/**
|
|
* Base Attribute needs more segment data
|
|
*/
|
|
bool need_more;
|
|
|
|
/**
|
|
* Pointer to remaining attribute data to be sent
|
|
*/
|
|
chunk_t data;
|
|
|
|
/**
|
|
* Maximum PA-TNC attribute segment size
|
|
*/
|
|
uint32_t max_seg_size;
|
|
|
|
};
|
|
|
|
METHOD(seg_env_t, get_base_attr_id, uint32_t,
|
|
private_seg_env_t *this)
|
|
{
|
|
return this->base_attr_id;
|
|
}
|
|
|
|
METHOD(seg_env_t, get_base_attr, pa_tnc_attr_t*,
|
|
private_seg_env_t *this)
|
|
{
|
|
return this->need_more ? NULL : this->base_attr->get_ref(this->base_attr);
|
|
}
|
|
|
|
METHOD(seg_env_t, get_base_attr_info, chunk_t,
|
|
private_seg_env_t *this)
|
|
{
|
|
return chunk_create(this->base_attr_info, 8);
|
|
}
|
|
|
|
METHOD(seg_env_t, first_segment, pa_tnc_attr_t*,
|
|
private_seg_env_t *this, size_t max_attr_len)
|
|
{
|
|
pa_tnc_attr_t *seg_env_attr;
|
|
bio_writer_t *writer;
|
|
pen_type_t type;
|
|
chunk_t segment_data, value;
|
|
size_t seg_size;
|
|
uint8_t flags, seg_env_flags;
|
|
|
|
/* compute size of first segment */
|
|
seg_size = max_attr_len ? min(this->max_seg_size,
|
|
max_attr_len - PA_TNC_ATTR_HEADER_SIZE
|
|
- TCG_SEG_ATTR_SEG_ENV_HEADER)
|
|
: this->max_seg_size;
|
|
|
|
/* get components of base attribute header and data */
|
|
flags = this->base_attr->get_noskip_flag(this->base_attr) ?
|
|
PA_TNC_ATTR_FLAG_NOSKIP : PA_TNC_ATTR_FLAG_NONE;
|
|
type = this->base_attr->get_type(this->base_attr);
|
|
|
|
/* attribute data going into the first segment */
|
|
segment_data = this->data;
|
|
segment_data.len = seg_size - PA_TNC_ATTR_HEADER_SIZE;
|
|
|
|
/* build encoding of the base attribute header and first segment data */
|
|
writer = bio_writer_create(this->max_seg_size);
|
|
writer->write_uint8 (writer, flags);
|
|
writer->write_uint24(writer, type.vendor_id);
|
|
writer->write_uint32(writer, type.type);
|
|
writer->write_uint32(writer, PA_TNC_ATTR_HEADER_SIZE + this->data.len);
|
|
writer->write_data (writer, segment_data);
|
|
value = writer->extract_buf(writer);
|
|
writer->destroy(writer);
|
|
this->data = chunk_skip(this->data, segment_data.len);
|
|
|
|
DBG2(DBG_TNC, "creating first segment for base attribute ID %d (%d bytes)",
|
|
this->base_attr_id, seg_size);
|
|
|
|
seg_env_flags = SEG_ENV_FLAG_START | SEG_ENV_FLAG_MORE;
|
|
seg_env_attr = tcg_seg_attr_seg_env_create(value, seg_env_flags,
|
|
this->base_attr_id);
|
|
chunk_free(&value);
|
|
|
|
return seg_env_attr;
|
|
}
|
|
|
|
METHOD(seg_env_t, next_segment, pa_tnc_attr_t*,
|
|
private_seg_env_t *this, bool *last)
|
|
{
|
|
pa_tnc_attr_t *seg_env_attr;
|
|
chunk_t segment_data;
|
|
uint8_t seg_env_flags;
|
|
bool is_last_segment;
|
|
|
|
if (this->data.len == 0)
|
|
{
|
|
/* no more attribute data to segment available */
|
|
return NULL;
|
|
}
|
|
|
|
/* attribute data going into the next segment */
|
|
segment_data = this->data;
|
|
segment_data.len = min(this->max_seg_size, this->data.len);
|
|
this->data = chunk_skip(this->data, segment_data.len);
|
|
|
|
is_last_segment = (this->data.len == 0);
|
|
if (last)
|
|
{
|
|
*last = is_last_segment;
|
|
}
|
|
DBG2(DBG_TNC, "creating %s segment for base attribute ID %d (%d bytes)",
|
|
is_last_segment ? "last" : "next", this->base_attr_id,
|
|
segment_data.len);
|
|
|
|
seg_env_flags = is_last_segment ? SEG_ENV_FLAG_NONE : SEG_ENV_FLAG_MORE;
|
|
seg_env_attr = tcg_seg_attr_seg_env_create(segment_data, seg_env_flags,
|
|
this->base_attr_id);
|
|
|
|
return seg_env_attr;
|
|
}
|
|
|
|
METHOD(seg_env_t, add_segment, bool,
|
|
private_seg_env_t *this, chunk_t segment, pa_tnc_attr_t **error)
|
|
{
|
|
pen_type_t type, error_code;
|
|
uint32_t attr_offset;
|
|
chunk_t msg_info;
|
|
status_t status;
|
|
|
|
this->base_attr->add_segment(this->base_attr, segment);
|
|
status = this->base_attr->process(this->base_attr, &attr_offset);
|
|
|
|
if (status != SUCCESS && status != NEED_MORE)
|
|
{
|
|
type = this->base_attr->get_type(this->base_attr);
|
|
if (type.vendor_id == PEN_IETF && type.type == IETF_ATTR_PA_TNC_ERROR)
|
|
{
|
|
/* error while processing a PA-TNC error attribute - abort */
|
|
return FALSE;
|
|
}
|
|
error_code = pen_type_create(PEN_IETF, PA_ERROR_INVALID_PARAMETER);
|
|
msg_info = get_base_attr_info(this);
|
|
*error = ietf_attr_pa_tnc_error_create_with_offset(error_code,
|
|
msg_info, PA_TNC_ATTR_HEADER_SIZE + attr_offset);
|
|
return FALSE;
|
|
}
|
|
this->need_more = (status == NEED_MORE);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
METHOD(seg_env_t, destroy, void,
|
|
private_seg_env_t *this)
|
|
{
|
|
DESTROY_IF(this->base_attr);
|
|
free(this);
|
|
}
|
|
|
|
/**
|
|
* See header
|
|
*/
|
|
seg_env_t *seg_env_create(uint32_t base_attr_id, pa_tnc_attr_t *base_attr,
|
|
uint32_t max_seg_size)
|
|
{
|
|
private_seg_env_t *this;
|
|
chunk_t value;
|
|
|
|
base_attr->build(base_attr);
|
|
value = base_attr->get_value(base_attr);
|
|
|
|
/**
|
|
* The PA-TNC attribute header must not be segmented and
|
|
* there must be at least a first and one next segment
|
|
*/
|
|
if (max_seg_size < PA_TNC_ATTR_HEADER_SIZE ||
|
|
max_seg_size >= PA_TNC_ATTR_HEADER_SIZE + value.len)
|
|
{
|
|
base_attr->destroy(base_attr);
|
|
return NULL;
|
|
}
|
|
|
|
INIT(this,
|
|
.public = {
|
|
.get_base_attr_id = _get_base_attr_id,
|
|
.get_base_attr = _get_base_attr,
|
|
.get_base_attr_info = _get_base_attr_info,
|
|
.first_segment = _first_segment,
|
|
.next_segment = _next_segment,
|
|
.add_segment = _add_segment,
|
|
.destroy = _destroy,
|
|
},
|
|
.base_attr_id = base_attr_id,
|
|
.base_attr = base_attr,
|
|
.max_seg_size = max_seg_size,
|
|
.data = base_attr->get_value(base_attr),
|
|
);
|
|
|
|
return &this->public;
|
|
}
|
|
|
|
/**
|
|
* See header
|
|
*/
|
|
seg_env_t *seg_env_create_from_data(uint32_t base_attr_id, chunk_t data,
|
|
uint32_t max_seg_size, pa_tnc_attr_t** error)
|
|
{
|
|
private_seg_env_t *this;
|
|
pen_type_t type, error_code;
|
|
bio_reader_t *reader;
|
|
chunk_t msg_info;
|
|
uint32_t offset = 0, attr_offset;
|
|
status_t status;
|
|
|
|
INIT(this,
|
|
.public = {
|
|
.get_base_attr_id = _get_base_attr_id,
|
|
.get_base_attr = _get_base_attr,
|
|
.get_base_attr_info = _get_base_attr_info,
|
|
.first_segment = _first_segment,
|
|
.next_segment = _next_segment,
|
|
.add_segment = _add_segment,
|
|
.destroy = _destroy,
|
|
},
|
|
.base_attr_id = base_attr_id,
|
|
.max_seg_size = max_seg_size,
|
|
);
|
|
|
|
/* create info field to be used by PA-TNC error messages */
|
|
memset(this->base_attr_info, 0xff, 4);
|
|
htoun32(this->base_attr_info + 4, base_attr_id);
|
|
msg_info = get_base_attr_info(this);
|
|
|
|
/* extract from base attribute segment from data */
|
|
reader = bio_reader_create(data);
|
|
this->base_attr = imcv_pa_tnc_attributes->create(imcv_pa_tnc_attributes,
|
|
reader, TRUE, &offset, msg_info, error);
|
|
reader->destroy(reader);
|
|
|
|
if (!this->base_attr)
|
|
{
|
|
destroy(this);
|
|
return NULL;
|
|
}
|
|
status = this->base_attr->process(this->base_attr, &attr_offset);
|
|
|
|
if (status != SUCCESS && status != NEED_MORE)
|
|
{
|
|
type = this->base_attr->get_type(this->base_attr);
|
|
if (!(type.vendor_id == PEN_IETF &&
|
|
type.type == IETF_ATTR_PA_TNC_ERROR))
|
|
{
|
|
error_code = pen_type_create(PEN_IETF, PA_ERROR_INVALID_PARAMETER);
|
|
*error = ietf_attr_pa_tnc_error_create_with_offset(error_code,
|
|
msg_info, PA_TNC_ATTR_HEADER_SIZE + attr_offset);
|
|
}
|
|
destroy(this);
|
|
return NULL;
|
|
}
|
|
this->need_more = (status == NEED_MORE);
|
|
|
|
return &this->public;
|
|
}
|
|
|