mirror of
				https://github.com/strongswan/strongswan.git
				synced 2025-11-04 00:00:51 -05:00 
			
		
		
		
	Implemented SWID REST API
This commit is contained in:
		
							parent
							
								
									8c26db8c62
								
							
						
					
					
						commit
						344c9f91f3
					
				@ -12,10 +12,12 @@ imcv_LTLIBRARIES = imv-swid.la
 | 
			
		||||
imv_swid_la_LIBADD = \
 | 
			
		||||
	$(top_builddir)/src/libimcv/libimcv.la \
 | 
			
		||||
	$(top_builddir)/src/libpts/libpts.la \
 | 
			
		||||
	$(top_builddir)/src/libstrongswan/libstrongswan.la
 | 
			
		||||
	$(top_builddir)/src/libstrongswan/libstrongswan.la \
 | 
			
		||||
	-ljson
 | 
			
		||||
 | 
			
		||||
imv_swid_la_SOURCES = \
 | 
			
		||||
	imv_swid.c imv_swid_state.h imv_swid_state.c \
 | 
			
		||||
	imv_swid_agent.h imv_swid_agent.c
 | 
			
		||||
	imv_swid_agent.h imv_swid_agent.c \
 | 
			
		||||
	imv_swid_rest.h imv_swid_rest.c
 | 
			
		||||
 | 
			
		||||
imv_swid_la_LDFLAGS = -module -avoid-version -no-undefined
 | 
			
		||||
 | 
			
		||||
@ -13,8 +13,12 @@
 | 
			
		||||
 * for more details.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#define _GNU_SOURCE
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
 | 
			
		||||
#include "imv_swid_agent.h"
 | 
			
		||||
#include "imv_swid_state.h"
 | 
			
		||||
#include "imv_swid_rest.h"
 | 
			
		||||
 | 
			
		||||
#include "libpts.h"
 | 
			
		||||
#include "swid/swid_error.h"
 | 
			
		||||
@ -67,6 +71,11 @@ struct private_imv_swid_agent_t {
 | 
			
		||||
	 */
 | 
			
		||||
	imv_agent_t *agent;
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * REST API to strongTNC manager
 | 
			
		||||
	 */
 | 
			
		||||
	imv_swid_rest_t *rest_api;
 | 
			
		||||
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
METHOD(imv_agent_if_t, bind_functions, TNC_Result,
 | 
			
		||||
@ -101,7 +110,6 @@ static TNC_Result receive_msg(private_imv_swid_agent_t *this,
 | 
			
		||||
{
 | 
			
		||||
	imv_swid_state_t *swid_state;
 | 
			
		||||
	imv_msg_t *out_msg;
 | 
			
		||||
	imv_session_t *session;
 | 
			
		||||
	enumerator_t *enumerator;
 | 
			
		||||
	pa_tnc_attr_t *attr;
 | 
			
		||||
	TNC_Result result;
 | 
			
		||||
@ -115,20 +123,15 @@ static TNC_Result receive_msg(private_imv_swid_agent_t *this,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	swid_state = (imv_swid_state_t*)state;
 | 
			
		||||
	session = state->get_session(state);
 | 
			
		||||
 | 
			
		||||
	/* analyze PA-TNC attributes */
 | 
			
		||||
	enumerator = in_msg->create_attribute_enumerator(in_msg);
 | 
			
		||||
	while (enumerator->enumerate(enumerator, &attr))
 | 
			
		||||
	{
 | 
			
		||||
		pen_type_t type;
 | 
			
		||||
		uint32_t request_id, last_eid, eid_epoch;
 | 
			
		||||
		uint32_t request_id = 0, last_eid, eid_epoch;
 | 
			
		||||
		swid_inventory_t *inventory;
 | 
			
		||||
		int tag_count;
 | 
			
		||||
		char *tag_item;
 | 
			
		||||
		imv_workitem_t *workitem, *found = NULL;
 | 
			
		||||
		enumerator_t *et, *ew;
 | 
			
		||||
		
 | 
			
		||||
		pen_type_t type;
 | 
			
		||||
 | 
			
		||||
		type = attr->get_type(attr);
 | 
			
		||||
 | 
			
		||||
		if (type.vendor_id == PEN_IETF && type.type == IETF_ATTR_PA_TNC_ERROR)
 | 
			
		||||
@ -137,7 +140,7 @@ static TNC_Result receive_msg(private_imv_swid_agent_t *this,
 | 
			
		||||
			pen_type_t error_code;
 | 
			
		||||
			chunk_t msg_info, description;
 | 
			
		||||
			bio_reader_t *reader;
 | 
			
		||||
			uint32_t request_id = 0, max_attr_size;
 | 
			
		||||
			uint32_t max_attr_size;
 | 
			
		||||
			bool success;
 | 
			
		||||
 | 
			
		||||
			error_attr = (ietf_attr_pa_tnc_error_t*)attr;
 | 
			
		||||
@ -200,8 +203,7 @@ static TNC_Result receive_msg(private_imv_swid_agent_t *this,
 | 
			
		||||
			case TCG_SWID_TAG_ID_INVENTORY:
 | 
			
		||||
			{
 | 
			
		||||
				tcg_swid_attr_tag_id_inv_t *attr_cast;
 | 
			
		||||
				swid_tag_id_t *tag_id;
 | 
			
		||||
				chunk_t tag_creator, unique_sw_id;
 | 
			
		||||
				int tag_id_count;
 | 
			
		||||
 | 
			
		||||
				state->set_action_flags(state, IMV_SWID_ATTR_TAG_ID_INV);
 | 
			
		||||
 | 
			
		||||
@ -209,26 +211,21 @@ static TNC_Result receive_msg(private_imv_swid_agent_t *this,
 | 
			
		||||
				request_id = attr_cast->get_request_id(attr_cast);
 | 
			
		||||
				last_eid = attr_cast->get_last_eid(attr_cast, &eid_epoch);
 | 
			
		||||
				inventory = attr_cast->get_inventory(attr_cast);
 | 
			
		||||
				tag_item = "tag ID";
 | 
			
		||||
				DBG2(DBG_IMV, "received SWID %s inventory for request %d "
 | 
			
		||||
							  "at eid %d of epoch 0x%08x", tag_item,
 | 
			
		||||
							   request_id, last_eid, eid_epoch);
 | 
			
		||||
				tag_id_count = inventory->get_count(inventory);
 | 
			
		||||
 | 
			
		||||
				et = inventory->create_enumerator(inventory);
 | 
			
		||||
				while (et->enumerate(et, &tag_id))
 | 
			
		||||
				DBG2(DBG_IMV, "received SWID tag ID inventory with %d items "
 | 
			
		||||
							  "for request %d at eid %d of epoch 0x%08x",
 | 
			
		||||
							   tag_id_count, request_id, last_eid, eid_epoch);
 | 
			
		||||
 | 
			
		||||
				if (request_id == swid_state->get_request_id(swid_state))
 | 
			
		||||
				{
 | 
			
		||||
					tag_creator = tag_id->get_tag_creator(tag_id);
 | 
			
		||||
					unique_sw_id = tag_id->get_unique_sw_id(tag_id, NULL);
 | 
			
		||||
					DBG3(DBG_IMV, "  %.*s_%.*s",
 | 
			
		||||
						 tag_creator.len, tag_creator.ptr,
 | 
			
		||||
						 unique_sw_id.len, unique_sw_id.ptr);
 | 
			
		||||
					swid_state->set_swid_inventory(swid_state, inventory);
 | 
			
		||||
					swid_state->set_count(swid_state, tag_id_count, 0);
 | 
			
		||||
				}
 | 
			
		||||
				et->destroy(et);
 | 
			
		||||
 | 
			
		||||
				if (request_id == 0)
 | 
			
		||||
				else
 | 
			
		||||
				{
 | 
			
		||||
					/* TODO handle subscribed messages */
 | 
			
		||||
					break;
 | 
			
		||||
					DBG1(DBG_IMV, "no workitem found for SWID tag ID inventory "
 | 
			
		||||
								  "with request ID %d", request_id);
 | 
			
		||||
				}
 | 
			
		||||
				break;
 | 
			
		||||
			 }
 | 
			
		||||
@ -237,6 +234,10 @@ static TNC_Result receive_msg(private_imv_swid_agent_t *this,
 | 
			
		||||
				tcg_swid_attr_tag_inv_t *attr_cast;
 | 
			
		||||
				swid_tag_t *tag;
 | 
			
		||||
				chunk_t tag_encoding;
 | 
			
		||||
				json_object *jarray, *jstring;
 | 
			
		||||
				char *tag_str;
 | 
			
		||||
				int tag_count;
 | 
			
		||||
				enumerator_t *e;
 | 
			
		||||
 | 
			
		||||
				state->set_action_flags(state, IMV_SWID_ATTR_TAG_INV);
 | 
			
		||||
 | 
			
		||||
@ -244,51 +245,50 @@ static TNC_Result receive_msg(private_imv_swid_agent_t *this,
 | 
			
		||||
				request_id = attr_cast->get_request_id(attr_cast);
 | 
			
		||||
				last_eid = attr_cast->get_last_eid(attr_cast, &eid_epoch);
 | 
			
		||||
				inventory = attr_cast->get_inventory(attr_cast);
 | 
			
		||||
				tag_item = "tag";
 | 
			
		||||
				DBG2(DBG_IMV, "received SWID %s inventory for request %d "
 | 
			
		||||
							  "at eid %d of epoch 0x%08x", tag_item,
 | 
			
		||||
							   request_id, last_eid, eid_epoch);
 | 
			
		||||
				tag_count = inventory->get_count(inventory);
 | 
			
		||||
 | 
			
		||||
				et = inventory->create_enumerator(inventory);
 | 
			
		||||
				while (et->enumerate(et, &tag))
 | 
			
		||||
				{
 | 
			
		||||
					tag_encoding = tag->get_encoding(tag);
 | 
			
		||||
					DBG3(DBG_IMV, "%.*s", tag_encoding.len, tag_encoding.ptr);
 | 
			
		||||
				}
 | 
			
		||||
				et->destroy(et);
 | 
			
		||||
				DBG2(DBG_IMV, "received SWID tag inventory with %d items for "
 | 
			
		||||
							  "request %d at eid %d of epoch 0x%08x",
 | 
			
		||||
							   tag_count, request_id, last_eid, eid_epoch);
 | 
			
		||||
 | 
			
		||||
				if (request_id == 0)
 | 
			
		||||
 | 
			
		||||
				if (request_id == swid_state->get_request_id(swid_state))
 | 
			
		||||
				{
 | 
			
		||||
					/* TODO handle subscribed messages */
 | 
			
		||||
					break;
 | 
			
		||||
					swid_state->set_count(swid_state, 0, tag_count);
 | 
			
		||||
 | 
			
		||||
					if (this->rest_api)
 | 
			
		||||
					{
 | 
			
		||||
						jarray = json_object_new_array();
 | 
			
		||||
 | 
			
		||||
						e = inventory->create_enumerator(inventory);
 | 
			
		||||
						while (e->enumerate(e, &tag))
 | 
			
		||||
						{
 | 
			
		||||
							tag_encoding = tag->get_encoding(tag);
 | 
			
		||||
							tag_str = strndup(tag_encoding.ptr, tag_encoding.len);
 | 
			
		||||
							DBG3(DBG_IMV, "%s", tag_str);
 | 
			
		||||
							jstring = json_object_new_string(tag_str);
 | 
			
		||||
							json_object_array_add(jarray, jstring);
 | 
			
		||||
							free(tag_str);
 | 
			
		||||
						}
 | 
			
		||||
						e->destroy(e);
 | 
			
		||||
 | 
			
		||||
						if (this->rest_api->post(this->rest_api,
 | 
			
		||||
								"swid/add-tags/", jarray, NULL) != SUCCESS)
 | 
			
		||||
						{
 | 
			
		||||
							DBG1(DBG_IMV, "error in REST API add-tags request");
 | 
			
		||||
						}
 | 
			
		||||
						json_object_put(jarray);
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
				else
 | 
			
		||||
				{
 | 
			
		||||
					DBG1(DBG_IMV, "no workitem found for SWID tag inventory "
 | 
			
		||||
								  "with request ID %d", request_id);
 | 
			
		||||
				}
 | 
			
		||||
				break;
 | 
			
		||||
			}
 | 
			
		||||
			default:
 | 
			
		||||
				continue;
 | 
			
		||||
		 }
 | 
			
		||||
 | 
			
		||||
		ew = session->create_workitem_enumerator(session);
 | 
			
		||||
		while (ew->enumerate(ew, &workitem))
 | 
			
		||||
		{
 | 
			
		||||
			if (workitem->get_id(workitem) == request_id)
 | 
			
		||||
			{
 | 
			
		||||
				found = workitem;
 | 
			
		||||
				break;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		if (found)
 | 
			
		||||
		{
 | 
			
		||||
			/* accumulate the swid tag [ID] count */
 | 
			
		||||
			tag_count = inventory->get_count(inventory);
 | 
			
		||||
			swid_state->set_count(swid_state, tag_count);
 | 
			
		||||
		}
 | 
			
		||||
		else
 | 
			
		||||
		{
 | 
			
		||||
			DBG1(DBG_IMV, "no workitem found for SWID %s inventory "
 | 
			
		||||
						  "with request ID %d", tag_item, request_id);
 | 
			
		||||
		}
 | 
			
		||||
		ew->destroy(ew);
 | 
			
		||||
	}
 | 
			
		||||
	enumerator->destroy(enumerator);
 | 
			
		||||
 | 
			
		||||
@ -382,7 +382,7 @@ METHOD(imv_agent_if_t, batch_ending, TNC_Result,
 | 
			
		||||
		return TNC_RESULT_SUCCESS;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* create an empty out message - we might need it */
 | 
			
		||||
	/* Create an empty out message - we might need it */
 | 
			
		||||
	out_msg = imv_msg_create(this->agent, state, id, imv_id, TNC_IMCID_ANY,
 | 
			
		||||
							 msg_types[0]);
 | 
			
		||||
 | 
			
		||||
@ -403,6 +403,7 @@ METHOD(imv_agent_if_t, batch_ending, TNC_Result,
 | 
			
		||||
		return this->agent->provide_recommendation(this->agent, state);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Look for SWID tag workitem and create SWID tag request */
 | 
			
		||||
	if (handshake_state == IMV_SWID_STATE_INIT &&
 | 
			
		||||
		session->get_policy_started(session))
 | 
			
		||||
	{
 | 
			
		||||
@ -431,13 +432,14 @@ METHOD(imv_agent_if_t, batch_ending, TNC_Result,
 | 
			
		||||
					flags |= TCG_SWID_ATTR_REQ_FLAG_C;
 | 
			
		||||
				}
 | 
			
		||||
				request_id = workitem->get_id(workitem);
 | 
			
		||||
 | 
			
		||||
				swid_state->set_request_id(swid_state, request_id);
 | 
			
		||||
				attr = tcg_swid_attr_req_create(flags, request_id, 0);
 | 
			
		||||
				out_msg->add_attribute(out_msg, attr);
 | 
			
		||||
				workitem->set_imv_id(workitem, imv_id);
 | 
			
		||||
				no_workitems = FALSE;
 | 
			
		||||
				DBG2(DBG_IMV, "IMV %d issues SWID request %d",
 | 
			
		||||
						 imv_id, request_id);
 | 
			
		||||
				break;
 | 
			
		||||
			}
 | 
			
		||||
			enumerator->destroy(enumerator);
 | 
			
		||||
 | 
			
		||||
@ -462,30 +464,140 @@ METHOD(imv_agent_if_t, batch_ending, TNC_Result,
 | 
			
		||||
	{
 | 
			
		||||
		TNC_IMV_Evaluation_Result eval;
 | 
			
		||||
		TNC_IMV_Action_Recommendation rec;
 | 
			
		||||
		char result_str[BUF_LEN], *tag_item;
 | 
			
		||||
		int tag_count;
 | 
			
		||||
		char result_str[BUF_LEN], *error_str = "", *command;
 | 
			
		||||
		char *target, *separator;
 | 
			
		||||
		int tag_id_count, tag_count, i;
 | 
			
		||||
		chunk_t tag_creator, unique_sw_id;
 | 
			
		||||
		json_object *jrequest, *jresponse, *jvalue;
 | 
			
		||||
		tcg_swid_attr_req_t *cast_attr;
 | 
			
		||||
		swid_tag_id_t *tag_id;
 | 
			
		||||
		status_t status = SUCCESS;
 | 
			
		||||
 | 
			
		||||
		enumerator = session->create_workitem_enumerator(session);
 | 
			
		||||
		while (enumerator->enumerate(enumerator, &workitem))
 | 
			
		||||
		if (this->rest_api)
 | 
			
		||||
		{
 | 
			
		||||
			if (workitem->get_type(workitem) == IMV_WORKITEM_SWID_TAGS)
 | 
			
		||||
			if (asprintf(&command, "sessions/%d/swid_measurement/",
 | 
			
		||||
						 session->get_session_id(session, NULL, NULL)) < 0)
 | 
			
		||||
			{
 | 
			
		||||
				swid_state->get_count(swid_state, &tag_count);
 | 
			
		||||
				tag_item = (received & IMV_SWID_ATTR_TAG_INV) ?	"" : " ID";
 | 
			
		||||
				snprintf(result_str, BUF_LEN, "received inventory of %d "
 | 
			
		||||
						"SWID tag%s%s", tag_count, tag_item,
 | 
			
		||||
						(tag_count == 1) ? "" : "s");
 | 
			
		||||
				session->remove_workitem(session, enumerator);
 | 
			
		||||
 | 
			
		||||
				eval = TNC_IMV_EVALUATION_RESULT_COMPLIANT;
 | 
			
		||||
				rec = workitem->set_result(workitem, result_str, eval);
 | 
			
		||||
				state->update_recommendation(state, rec, eval);
 | 
			
		||||
				imcv_db->finalize_workitem(imcv_db, workitem);
 | 
			
		||||
				workitem->destroy(workitem);
 | 
			
		||||
				break;
 | 
			
		||||
				error_str = "allocation of command string failed";
 | 
			
		||||
				status = FAILED;
 | 
			
		||||
			}
 | 
			
		||||
			else
 | 
			
		||||
			{
 | 
			
		||||
				jrequest = swid_state->get_swid_inventory(swid_state);
 | 
			
		||||
				status = this->rest_api->post(this->rest_api, command,
 | 
			
		||||
											  jrequest, &jresponse);
 | 
			
		||||
				if (status == FAILED)
 | 
			
		||||
				{
 | 
			
		||||
					error_str = "error in REST API swid_measurement request";
 | 
			
		||||
				}
 | 
			
		||||
				free(command);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		enumerator->destroy(enumerator);
 | 
			
		||||
 | 
			
		||||
		switch (status)
 | 
			
		||||
		{
 | 
			
		||||
			case SUCCESS:
 | 
			
		||||
				enumerator = session->create_workitem_enumerator(session);
 | 
			
		||||
				while (enumerator->enumerate(enumerator, &workitem))
 | 
			
		||||
				{
 | 
			
		||||
					if (workitem->get_type(workitem) == IMV_WORKITEM_SWID_TAGS)
 | 
			
		||||
					{
 | 
			
		||||
						swid_state->get_count(swid_state, &tag_id_count,
 | 
			
		||||
														  &tag_count);
 | 
			
		||||
						snprintf(result_str, BUF_LEN, "received inventory of "
 | 
			
		||||
								 "%d SWID tag ID%s and %d SWID tag%s",
 | 
			
		||||
								 tag_id_count, (tag_id_count == 1) ? "" : "s",
 | 
			
		||||
								 tag_count, (tag_count == 1) ? "" : "s");
 | 
			
		||||
						session->remove_workitem(session, enumerator);
 | 
			
		||||
 | 
			
		||||
						eval = TNC_IMV_EVALUATION_RESULT_COMPLIANT;
 | 
			
		||||
						rec = workitem->set_result(workitem, result_str, eval);
 | 
			
		||||
						state->update_recommendation(state, rec, eval);
 | 
			
		||||
						imcv_db->finalize_workitem(imcv_db, workitem);
 | 
			
		||||
						workitem->destroy(workitem);
 | 
			
		||||
						break;
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
				enumerator->destroy(enumerator);
 | 
			
		||||
				break;
 | 
			
		||||
			case NEED_MORE:
 | 
			
		||||
				if (received & IMV_SWID_ATTR_TAG_INV)
 | 
			
		||||
				{
 | 
			
		||||
					error_str = "not all requested SWID tags were received";
 | 
			
		||||
					status = FAILED;
 | 
			
		||||
					json_object_put(jresponse);
 | 
			
		||||
					break;
 | 
			
		||||
				}
 | 
			
		||||
				if (json_object_get_type(jresponse) != json_type_array)
 | 
			
		||||
				{
 | 
			
		||||
					error_str = "response was not a json_array";
 | 
			
		||||
					status = FAILED;
 | 
			
		||||
					json_object_put(jresponse);
 | 
			
		||||
					break;
 | 
			
		||||
				}
 | 
			
		||||
				attr = tcg_swid_attr_req_create(TCG_SWID_ATTR_REQ_FLAG_NONE,
 | 
			
		||||
								swid_state->get_request_id(swid_state), 0);
 | 
			
		||||
				cast_attr = (tcg_swid_attr_req_t*)attr;
 | 
			
		||||
 | 
			
		||||
				tag_id_count = json_object_array_length(jresponse);
 | 
			
		||||
				DBG1(DBG_IMV, "%d SWID tag targets", tag_id_count);
 | 
			
		||||
 | 
			
		||||
				for (i = 0; i < tag_id_count; i++)
 | 
			
		||||
				{
 | 
			
		||||
					jvalue = json_object_array_get_idx(jresponse, i);
 | 
			
		||||
					if (json_object_get_type(jvalue) != json_type_string)
 | 
			
		||||
					{
 | 
			
		||||
						error_str = "json_string element expected in json_array";
 | 
			
		||||
						status = FAILED;
 | 
			
		||||
						json_object_put(jresponse);
 | 
			
		||||
						break;
 | 
			
		||||
					}
 | 
			
		||||
					target = json_object_get_string(jvalue);
 | 
			
		||||
					DBG1(DBG_IMV, "  %s", target);
 | 
			
		||||
 | 
			
		||||
					/* Separate target into tag_creator and unique_sw_id */
 | 
			
		||||
					separator = strchr(target, '_');
 | 
			
		||||
					if (!separator)
 | 
			
		||||
					{
 | 
			
		||||
						error_str = "separation of regid from "
 | 
			
		||||
									"unique software ID failed";
 | 
			
		||||
						break;
 | 
			
		||||
					}
 | 
			
		||||
					tag_creator = chunk_create(target, separator - target);
 | 
			
		||||
					separator++;
 | 
			
		||||
					unique_sw_id = chunk_create(separator, strlen(target) -
 | 
			
		||||
												tag_creator.len - 1);
 | 
			
		||||
					tag_id = swid_tag_id_create(tag_creator, unique_sw_id,
 | 
			
		||||
												chunk_empty);
 | 
			
		||||
					cast_attr->add_target(cast_attr, tag_id);
 | 
			
		||||
				}
 | 
			
		||||
				json_object_put(jresponse);
 | 
			
		||||
 | 
			
		||||
				out_msg->add_attribute(out_msg, attr);
 | 
			
		||||
				break;
 | 
			
		||||
			case FAILED:
 | 
			
		||||
			default:
 | 
			
		||||
				break;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (status == FAILED)
 | 
			
		||||
		{
 | 
			
		||||
			enumerator = session->create_workitem_enumerator(session);
 | 
			
		||||
			while (enumerator->enumerate(enumerator, &workitem))
 | 
			
		||||
			{
 | 
			
		||||
				if (workitem->get_type(workitem) == IMV_WORKITEM_SWID_TAGS)
 | 
			
		||||
				{
 | 
			
		||||
					session->remove_workitem(session, enumerator);
 | 
			
		||||
					eval = TNC_IMV_EVALUATION_RESULT_ERROR;
 | 
			
		||||
					rec = workitem->set_result(workitem, error_str, eval);
 | 
			
		||||
					state->update_recommendation(state, rec, eval);
 | 
			
		||||
					imcv_db->finalize_workitem(imcv_db, workitem);
 | 
			
		||||
					workitem->destroy(workitem);
 | 
			
		||||
					break;
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			enumerator->destroy(enumerator);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* finalized all workitems ? */
 | 
			
		||||
@ -528,6 +640,7 @@ METHOD(imv_agent_if_t, solicit_recommendation, TNC_Result,
 | 
			
		||||
METHOD(imv_agent_if_t, destroy, void,
 | 
			
		||||
	private_imv_swid_agent_t *this)
 | 
			
		||||
{
 | 
			
		||||
	DESTROY_IF(this->rest_api);
 | 
			
		||||
	this->agent->destroy(this->agent);
 | 
			
		||||
	free(this);
 | 
			
		||||
	libpts_deinit();
 | 
			
		||||
@ -541,6 +654,7 @@ imv_agent_if_t *imv_swid_agent_create(const char *name, TNC_IMVID id,
 | 
			
		||||
{
 | 
			
		||||
	private_imv_swid_agent_t *this;
 | 
			
		||||
	imv_agent_t *agent;
 | 
			
		||||
	char *rest_api_uri;
 | 
			
		||||
 | 
			
		||||
	agent = imv_agent_create(name, msg_types, countof(msg_types), id,
 | 
			
		||||
							 actual_version);
 | 
			
		||||
@ -562,6 +676,12 @@ imv_agent_if_t *imv_swid_agent_create(const char *name, TNC_IMVID id,
 | 
			
		||||
		.agent = agent,
 | 
			
		||||
	);
 | 
			
		||||
 | 
			
		||||
	rest_api_uri = lib->settings->get_str(lib->settings,
 | 
			
		||||
						"%s.plugins.imv-swid.rest_api_uri", NULL, lib->ns);
 | 
			
		||||
	if (rest_api_uri)
 | 
			
		||||
	{
 | 
			
		||||
		this->rest_api = imv_swid_rest_create(rest_api_uri);
 | 
			
		||||
	}
 | 
			
		||||
	libpts_init();
 | 
			
		||||
 | 
			
		||||
	return &this->public;
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										115
									
								
								src/libpts/plugins/imv_swid/imv_swid_rest.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										115
									
								
								src/libpts/plugins/imv_swid/imv_swid_rest.c
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,115 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2014 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.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#define _GNU_SOURCE
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
 | 
			
		||||
#include "imv_swid_rest.h"
 | 
			
		||||
 | 
			
		||||
typedef struct private_imv_swid_rest_t private_imv_swid_rest_t;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Private data of an imv_swid_rest_t object.
 | 
			
		||||
 */
 | 
			
		||||
struct private_imv_swid_rest_t {
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Public members of imv_swid_rest_t
 | 
			
		||||
	 */
 | 
			
		||||
	imv_swid_rest_t public;
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * URI of REST API
 | 
			
		||||
	 */
 | 
			
		||||
	char *uri;
 | 
			
		||||
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#define HTTP_STATUS_CODE_PRECONDITION_FAILED	412
 | 
			
		||||
 | 
			
		||||
METHOD(imv_swid_rest_t, post, status_t,
 | 
			
		||||
	private_imv_swid_rest_t *this, char *command, json_object *jrequest,
 | 
			
		||||
	json_object **jresponse)
 | 
			
		||||
{
 | 
			
		||||
	struct json_tokener *tokener;
 | 
			
		||||
	chunk_t data, response = chunk_empty;
 | 
			
		||||
	u_int timeout = 30;
 | 
			
		||||
	status_t status;
 | 
			
		||||
	char *uri;
 | 
			
		||||
	int code;
 | 
			
		||||
 | 
			
		||||
	if (asprintf(&uri, "%s%s",this->uri, command) < 0)
 | 
			
		||||
	{
 | 
			
		||||
		return FAILED;
 | 
			
		||||
	}
 | 
			
		||||
	data = chunk_from_str(json_object_to_json_string(jrequest));
 | 
			
		||||
 | 
			
		||||
	status = lib->fetcher->fetch(lib->fetcher, uri, &response,
 | 
			
		||||
				FETCH_TIMEOUT, timeout,
 | 
			
		||||
				FETCH_REQUEST_DATA, data,
 | 
			
		||||
				FETCH_REQUEST_TYPE, "application/json; charset=utf-8",
 | 
			
		||||
				FETCH_REQUEST_HEADER, "Accept: application/json",
 | 
			
		||||
				FETCH_REQUEST_HEADER, "Expect:",
 | 
			
		||||
				FETCH_RESPONSE_CODE, &code,
 | 
			
		||||
				FETCH_END);
 | 
			
		||||
	free(uri);
 | 
			
		||||
 | 
			
		||||
	if (status == SUCCESS)
 | 
			
		||||
	{
 | 
			
		||||
		return 	SUCCESS;
 | 
			
		||||
	}
 | 
			
		||||
	if (code != HTTP_STATUS_CODE_PRECONDITION_FAILED || !response.ptr)
 | 
			
		||||
	{
 | 
			
		||||
		return FAILED;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (jresponse)
 | 
			
		||||
	{
 | 
			
		||||
		/* Parse HTTP response into a JSON object */
 | 
			
		||||
		tokener = json_tokener_new();
 | 
			
		||||
		*jresponse = json_tokener_parse_ex(tokener, response.ptr, response.len);
 | 
			
		||||
		json_tokener_free(tokener);
 | 
			
		||||
	}
 | 
			
		||||
	free(response.ptr);
 | 
			
		||||
 | 
			
		||||
	return NEED_MORE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
METHOD(imv_swid_rest_t, destroy, void,
 | 
			
		||||
	private_imv_swid_rest_t *this)
 | 
			
		||||
{
 | 
			
		||||
	free(this->uri);
 | 
			
		||||
	free(this);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Described in header.
 | 
			
		||||
 */
 | 
			
		||||
imv_swid_rest_t *imv_swid_rest_create(char *uri)
 | 
			
		||||
{
 | 
			
		||||
	private_imv_swid_rest_t *this;
 | 
			
		||||
 | 
			
		||||
	INIT(this,
 | 
			
		||||
		.public = {
 | 
			
		||||
			.post = _post,
 | 
			
		||||
			.destroy = _destroy,
 | 
			
		||||
		},
 | 
			
		||||
		.uri = strdup(uri),
 | 
			
		||||
	);
 | 
			
		||||
 | 
			
		||||
	return &this->public;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										62
									
								
								src/libpts/plugins/imv_swid/imv_swid_rest.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								src/libpts/plugins/imv_swid/imv_swid_rest.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,62 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2013-2014 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.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @defgroup imv_swid imv_swid
 | 
			
		||||
 * @ingroup libimcv_plugins
 | 
			
		||||
 *
 | 
			
		||||
 * @defgroup imv_swid_rest_t imv_swid_rest
 | 
			
		||||
 * @{ @ingroup imv_swid
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef IMV_SWID_REST_H_
 | 
			
		||||
#define IMV_SWID_REST_H_
 | 
			
		||||
 | 
			
		||||
#include <library.h>
 | 
			
		||||
 | 
			
		||||
#include <json/json.h>
 | 
			
		||||
 | 
			
		||||
typedef struct imv_swid_rest_t imv_swid_rest_t;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Public REST interface
 | 
			
		||||
 */
 | 
			
		||||
struct imv_swid_rest_t {
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Post a HTTP request including a JSON object
 | 
			
		||||
	 *
 | 
			
		||||
	 * @param jreq		JSON object in HTTP request
 | 
			
		||||
	 * @param jresp		JSON object in HTTP response if NEED_MORE
 | 
			
		||||
	 * @return			Status (SUCCESS, NEED_MORE or FAILED)
 | 
			
		||||
	 */
 | 
			
		||||
	status_t (*post)(imv_swid_rest_t *this, char *command, json_object *jreq,
 | 
			
		||||
					 json_object **jresp);
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Destroy imv_swid_rest_t object
 | 
			
		||||
	 */
 | 
			
		||||
	void (*destroy)(imv_swid_rest_t *this);
 | 
			
		||||
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Create an imv_swid_rest_t instance
 | 
			
		||||
 *
 | 
			
		||||
 * @param uri			REST URI (http://username:password@hostname[:port]/api/)
 | 
			
		||||
 */
 | 
			
		||||
imv_swid_rest_t* imv_swid_rest_create(char *uri);
 | 
			
		||||
 | 
			
		||||
#endif /** IMV_SWID_REST_H_ @}*/
 | 
			
		||||
@ -14,9 +14,11 @@
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "imv_swid_state.h"
 | 
			
		||||
#include "imv/imv_lang_string.h"
 | 
			
		||||
#include "imv/imv_reason_string.h"
 | 
			
		||||
#include "imv/imv_remediation_string.h"
 | 
			
		||||
 | 
			
		||||
#include <imv/imv_lang_string.h>
 | 
			
		||||
#include <imv/imv_reason_string.h>
 | 
			
		||||
#include <imv/imv_remediation_string.h>
 | 
			
		||||
#include <swid/swid_tag_id.h>
 | 
			
		||||
 | 
			
		||||
#include <tncif_policy.h>
 | 
			
		||||
 | 
			
		||||
@ -96,9 +98,24 @@ struct private_imv_swid_state_t {
 | 
			
		||||
	imv_remediation_string_t *remediation_string;
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Number of processed SWID Tags or SWID Tag IDs
 | 
			
		||||
	 * SWID Tag Request ID
 | 
			
		||||
	 */
 | 
			
		||||
	int count;
 | 
			
		||||
	uint32_t request_id;
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Number of processed SWID Tag IDs
 | 
			
		||||
	 */
 | 
			
		||||
	int tag_id_count;
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Number of processed SWID Tags
 | 
			
		||||
	 */
 | 
			
		||||
	int tag_count;
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * JSON array containing an inventory of SWID Tag IDs
 | 
			
		||||
	 */
 | 
			
		||||
	json_object *jarray;
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Angel count
 | 
			
		||||
@ -215,6 +232,7 @@ METHOD(imv_state_t, get_remediation_instructions, bool,
 | 
			
		||||
METHOD(imv_state_t, destroy, void,
 | 
			
		||||
	private_imv_swid_state_t *this)
 | 
			
		||||
{
 | 
			
		||||
	json_object_put(this->jarray);
 | 
			
		||||
	DESTROY_IF(this->session);
 | 
			
		||||
	DESTROY_IF(this->reason_string);
 | 
			
		||||
	DESTROY_IF(this->remediation_string);
 | 
			
		||||
@ -233,18 +251,68 @@ METHOD(imv_swid_state_t, get_handshake_state, imv_swid_handshake_state_t,
 | 
			
		||||
	return this->handshake_state;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
METHOD(imv_swid_state_t, set_count, void,
 | 
			
		||||
	private_imv_swid_state_t *this, int count)
 | 
			
		||||
METHOD(imv_swid_state_t, set_request_id, void,
 | 
			
		||||
	private_imv_swid_state_t *this, uint32_t request_id)
 | 
			
		||||
{
 | 
			
		||||
	this->count           += count;
 | 
			
		||||
	this->request_id = request_id;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
METHOD(imv_swid_state_t, get_request_id, uint32_t,
 | 
			
		||||
	private_imv_swid_state_t *this)
 | 
			
		||||
{
 | 
			
		||||
	return this->request_id;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
METHOD(imv_swid_state_t, set_swid_inventory, void,
 | 
			
		||||
    private_imv_swid_state_t *this, swid_inventory_t *inventory)
 | 
			
		||||
{
 | 
			
		||||
	chunk_t tag_creator, unique_sw_id;
 | 
			
		||||
	char software_id[256];
 | 
			
		||||
	json_object *jstring;
 | 
			
		||||
	swid_tag_id_t *tag_id;
 | 
			
		||||
	enumerator_t *enumerator;
 | 
			
		||||
 | 
			
		||||
	enumerator = inventory->create_enumerator(inventory);
 | 
			
		||||
	while (enumerator->enumerate(enumerator, &tag_id))
 | 
			
		||||
	{
 | 
			
		||||
		/* Construct software ID from tag creator and unique software ID */
 | 
			
		||||
		tag_creator = tag_id->get_tag_creator(tag_id);
 | 
			
		||||
		unique_sw_id = tag_id->get_unique_sw_id(tag_id, NULL);
 | 
			
		||||
		snprintf(software_id, 256, "%.*s_%.*s",
 | 
			
		||||
				 tag_creator.len, tag_creator.ptr,
 | 
			
		||||
				 unique_sw_id.len, unique_sw_id.ptr);
 | 
			
		||||
		DBG3(DBG_IMV, "  %s", software_id);
 | 
			
		||||
 | 
			
		||||
		/* Add software ID to JSON array */
 | 
			
		||||
		jstring = json_object_new_string(software_id);
 | 
			
		||||
		json_object_array_add(this->jarray, jstring);
 | 
			
		||||
	}
 | 
			
		||||
	enumerator->destroy(enumerator);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
METHOD(imv_swid_state_t, get_swid_inventory, json_object*,
 | 
			
		||||
	private_imv_swid_state_t *this)
 | 
			
		||||
{
 | 
			
		||||
	return this->jarray;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
METHOD(imv_swid_state_t, set_count, void,
 | 
			
		||||
	private_imv_swid_state_t *this, int tag_id_count, int tag_count)
 | 
			
		||||
{
 | 
			
		||||
	this->tag_id_count += tag_id_count;
 | 
			
		||||
	this->tag_count += tag_count;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
METHOD(imv_swid_state_t, get_count, void,
 | 
			
		||||
	private_imv_swid_state_t *this, int *count)
 | 
			
		||||
	private_imv_swid_state_t *this, int *tag_id_count, int *tag_count)
 | 
			
		||||
{
 | 
			
		||||
	if (count)
 | 
			
		||||
	if (tag_id_count)
 | 
			
		||||
	{
 | 
			
		||||
		*count = this->count;
 | 
			
		||||
		*tag_id_count = this->tag_id_count;
 | 
			
		||||
	}
 | 
			
		||||
	if (tag_count)
 | 
			
		||||
	{
 | 
			
		||||
		*tag_count = this->tag_count;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -290,6 +358,10 @@ imv_state_t *imv_swid_state_create(TNC_ConnectionID connection_id)
 | 
			
		||||
			},
 | 
			
		||||
			.set_handshake_state = _set_handshake_state,
 | 
			
		||||
			.get_handshake_state = _get_handshake_state,
 | 
			
		||||
			.set_request_id = _set_request_id,
 | 
			
		||||
			.get_request_id = _get_request_id,
 | 
			
		||||
			.set_swid_inventory = _set_swid_inventory,
 | 
			
		||||
			.get_swid_inventory = _get_swid_inventory,
 | 
			
		||||
			.set_count = _set_count,
 | 
			
		||||
			.get_count = _get_count,
 | 
			
		||||
			.set_angel_count = _set_angel_count,
 | 
			
		||||
@ -299,6 +371,7 @@ imv_state_t *imv_swid_state_create(TNC_ConnectionID connection_id)
 | 
			
		||||
		.rec = TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION,
 | 
			
		||||
		.eval = TNC_IMV_EVALUATION_RESULT_DONT_KNOW,
 | 
			
		||||
		.connection_id = connection_id,
 | 
			
		||||
		.jarray = json_object_new_array(),
 | 
			
		||||
	);
 | 
			
		||||
 | 
			
		||||
	return &this->public.interface;
 | 
			
		||||
 | 
			
		||||
@ -25,8 +25,11 @@
 | 
			
		||||
#define IMV_SWID_STATE_H_
 | 
			
		||||
 | 
			
		||||
#include <imv/imv_state.h>
 | 
			
		||||
#include <swid/swid_inventory.h>
 | 
			
		||||
#include <library.h>
 | 
			
		||||
 | 
			
		||||
#include <json/json.h>
 | 
			
		||||
 | 
			
		||||
typedef struct imv_swid_state_t imv_swid_state_t;
 | 
			
		||||
typedef enum imv_swid_handshake_state_t imv_swid_handshake_state_t;
 | 
			
		||||
 | 
			
		||||
@ -65,30 +68,60 @@ struct imv_swid_state_t {
 | 
			
		||||
	imv_swid_handshake_state_t (*get_handshake_state)(imv_swid_state_t *this);
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Set [or with multiple attributes increment] SWID Tag [ID] counters
 | 
			
		||||
	 * Set the SWID request ID
 | 
			
		||||
	 *
 | 
			
		||||
	 * @param count				Number of received SWID Tags or SWID Tag IDs
 | 
			
		||||
	 * @param request_id		SWID request ID to be set
 | 
			
		||||
	 */
 | 
			
		||||
	void (*set_count)(imv_swid_state_t *this, int count);
 | 
			
		||||
	void (*set_request_id)(imv_swid_state_t *this, uint32_t request_id);
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Get the SWID request ID
 | 
			
		||||
	 *
 | 
			
		||||
	 * @return					SWID request ID
 | 
			
		||||
	 */
 | 
			
		||||
	uint32_t (*get_request_id)(imv_swid_state_t *this);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Set or extend the SWID Tag ID inventory in the state
 | 
			
		||||
     *
 | 
			
		||||
     * @param inventory			SWID Tags ID inventory to be added
 | 
			
		||||
     */
 | 
			
		||||
    void (*set_swid_inventory)(imv_swid_state_t *this, swid_inventory_t *inventory);
 | 
			
		||||
 | 
			
		||||
   /**
 | 
			
		||||
     * Get the encoding of the complete SWID Tag ID inventory
 | 
			
		||||
     *
 | 
			
		||||
     * @return			       SWID Tags ID inventory as a JSON array
 | 
			
		||||
     */
 | 
			
		||||
    json_object* (*get_swid_inventory)(imv_swid_state_t *this);
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Set [or with multiple attributes increment] SWID Tag [ID] counters
 | 
			
		||||
	 *
 | 
			
		||||
	 * @param count				Number of received SWID Tags or SWID Tag IDs
 | 
			
		||||
	 * @param tag_id_count		Number of received SWID Tag IDs
 | 
			
		||||
	 * @param tag_count			Number of received SWID Tags
 | 
			
		||||
	 */
 | 
			
		||||
	void (*get_count)(imv_swid_state_t *this, int *count);
 | 
			
		||||
	void (*set_count)(imv_swid_state_t *this, int tag_id_count, int tag_count);
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Set [or with multiple attributes increment] SWID Tag [ID] counters
 | 
			
		||||
	 *
 | 
			
		||||
	 * @param tag_id_count		Number of received SWID Tag IDs
 | 
			
		||||
	 * @param tag_count			Number of received SWID Tags
 | 
			
		||||
	 */
 | 
			
		||||
	void (*get_count)(imv_swid_state_t *this, int *tag_id_count, int *tag_count);
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Increase/Decrease the ITA Angel count
 | 
			
		||||
	 *
 | 
			
		||||
	 * @param start			TRUE increases and FALSE decreases count by one
 | 
			
		||||
	 * @param start				TRUE increases and FALSE decreases count by one
 | 
			
		||||
	 */
 | 
			
		||||
	void (*set_angel_count)(imv_swid_state_t *this, bool start);
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Get the ITA Angel count
 | 
			
		||||
	 *
 | 
			
		||||
	 * @return				ITA Angel count
 | 
			
		||||
	 * @return					ITA Angel count
 | 
			
		||||
	 */
 | 
			
		||||
	int (*get_angel_count)(imv_swid_state_t *this);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user