2820 lines
77 KiB
C
2820 lines
77 KiB
C
/* $Id$
|
|
*
|
|
* Lasso - A free implementation of the Liberty Alliance specifications.
|
|
*
|
|
* Copyright (C) 2004-2007 Entr'ouvert
|
|
* http://lasso.entrouvert.org
|
|
*
|
|
* Authors: See AUTHORS file in top-level directory.
|
|
*
|
|
* 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.
|
|
*
|
|
* 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.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*/
|
|
|
|
/**
|
|
* SECTION:node
|
|
* @short_description: Base class for all Lasso objects
|
|
*
|
|
* #LassoNode is the base class for Lasso objects; just a step over GObject as
|
|
* defined in glib.
|
|
*
|
|
*/
|
|
|
|
#include "private.h"
|
|
#include <ctype.h>
|
|
|
|
|
|
#include <xmlsec/base64.h>
|
|
#include <xmlsec/xmltree.h>
|
|
#include <xmlsec/xmldsig.h>
|
|
#include <xmlsec/templates.h>
|
|
#include <xmlsec/crypto.h>
|
|
#include <xmlsec/xmlenc.h>
|
|
|
|
#include "xml.h"
|
|
#include "xml_enc.h"
|
|
#include "saml_name_identifier.h"
|
|
#include "../utils.h"
|
|
#include "../registry.h"
|
|
#include "../debug.h"
|
|
#include "./soap-1.1/soap_envelope.h"
|
|
#include "./soap-1.1/soap_body.h"
|
|
|
|
static void lasso_node_build_xmlNode_from_snippets(LassoNode *node, xmlNode *xmlnode,
|
|
struct XmlSnippet *snippets, gboolean lasso_dump);
|
|
static struct XmlSnippet* find_xml_snippet_by_name(LassoNode *node, char *name);
|
|
static gboolean set_value_at_path(LassoNode *node, char *path, char *query_value);
|
|
static char* get_value_by_path(LassoNode *node, char *path, struct XmlSnippet *xml_snippet);
|
|
static gboolean find_path(LassoNode *node, char *path, LassoNode **value_node,
|
|
struct XmlSnippet **snippet);
|
|
|
|
static void lasso_node_add_signature_template(LassoNode *node, xmlNode *xmlnode,
|
|
struct XmlSnippet *snippet_signature);
|
|
static void lasso_node_traversal(LassoNode *node, void (*do_to_node)(LassoNode *node, SnippetType type), SnippetType type);
|
|
|
|
static LassoNode* lasso_node_new_from_xmlNode_with_type(xmlNode *xmlnode, char *typename);
|
|
static void lasso_node_remove_original_xmlnode(LassoNode *node, SnippetType type);
|
|
|
|
GHashTable *dst_services_by_href = NULL; /* ID-WSF 1 extra DST services, indexed on href */
|
|
GHashTable *dst_services_by_prefix = NULL; /* ID-WSF 1 extra DST services, indexed on prefix */
|
|
|
|
GHashTable *idwsf2_dst_services_by_href = NULL; /* ID-WSF 2 DST services, indexed on href */
|
|
GHashTable *idwsf2_dst_services_by_prefix = NULL; /* ID-WSF 2 DST services, indexed on prefix */
|
|
|
|
/*****************************************************************************/
|
|
/* global methods */
|
|
/*****************************************************************************/
|
|
|
|
|
|
/**
|
|
* lasso_register_dst_service:
|
|
* @prefix: prefix of DST service
|
|
* @href: href of DST service
|
|
*
|
|
* Registers prefix and href of a custom data service template service.
|
|
**/
|
|
void
|
|
lasso_register_dst_service(const gchar *prefix, const gchar *href)
|
|
{
|
|
if (dst_services_by_href == NULL) {
|
|
dst_services_by_href = g_hash_table_new_full(
|
|
g_str_hash, g_str_equal, g_free, g_free);
|
|
dst_services_by_prefix = g_hash_table_new_full(
|
|
g_str_hash, g_str_equal, g_free, g_free);
|
|
}
|
|
g_hash_table_insert(dst_services_by_prefix, g_strdup(prefix), g_strdup(href));
|
|
g_hash_table_insert(dst_services_by_href, g_strdup(href), g_strdup(prefix));
|
|
}
|
|
|
|
void
|
|
lasso_register_idwsf2_dst_service(const gchar *prefix, const gchar *href)
|
|
{
|
|
if (idwsf2_dst_services_by_href == NULL) {
|
|
idwsf2_dst_services_by_href = g_hash_table_new_full(
|
|
g_str_hash, g_str_equal, g_free, g_free);
|
|
idwsf2_dst_services_by_prefix = g_hash_table_new_full(
|
|
g_str_hash, g_str_equal, g_free, g_free);
|
|
}
|
|
g_hash_table_insert(idwsf2_dst_services_by_prefix, g_strdup(prefix), g_strdup(href));
|
|
g_hash_table_insert(idwsf2_dst_services_by_href, g_strdup(href), g_strdup(prefix));
|
|
}
|
|
|
|
gchar*
|
|
lasso_get_prefix_for_dst_service_href(const gchar *href)
|
|
{
|
|
if (strcmp(href, LASSO_PP10_HREF) == 0)
|
|
return g_strdup(LASSO_PP10_PREFIX);
|
|
if (strcmp(href, LASSO_PP11_HREF) == 0)
|
|
return g_strdup(LASSO_PP11_PREFIX);
|
|
if (strcmp(href, LASSO_EP_HREF) == 0)
|
|
return g_strdup(LASSO_EP_PREFIX);
|
|
|
|
if (dst_services_by_href == NULL)
|
|
return NULL;
|
|
|
|
return g_strdup(g_hash_table_lookup(dst_services_by_href, href));
|
|
}
|
|
|
|
gchar*
|
|
lasso_get_prefix_for_idwsf2_dst_service_href(const gchar *href)
|
|
{
|
|
if (idwsf2_dst_services_by_href == NULL)
|
|
return NULL;
|
|
|
|
return g_strdup(g_hash_table_lookup(idwsf2_dst_services_by_href, href));
|
|
}
|
|
|
|
|
|
/*****************************************************************************/
|
|
/* virtual public methods */
|
|
/*****************************************************************************/
|
|
|
|
static char*
|
|
_lasso_node_export_to_xml(LassoNode *node, gboolean format, gboolean dump, int level)
|
|
{
|
|
xmlNode *xmlnode;
|
|
char *ret;
|
|
|
|
g_return_val_if_fail (LASSO_IS_NODE(node), NULL);
|
|
|
|
xmlnode = lasso_node_get_xmlNode(node, dump);
|
|
ret = lasso_xmlnode_to_string(xmlnode, format, level);
|
|
xmlFreeNode(xmlnode);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* lasso_node_dump:
|
|
* @node: a #LassoNode
|
|
*
|
|
* Dumps @node. All datas in object are dumped in an XML format.
|
|
*
|
|
* Return value: a full XML dump of @node. The string must be freed by the
|
|
* caller.
|
|
**/
|
|
char*
|
|
lasso_node_dump(LassoNode *node)
|
|
{
|
|
return _lasso_node_export_to_xml(node, FALSE, TRUE, 0);
|
|
}
|
|
|
|
/**
|
|
* laso_node_debug:
|
|
* @node: a #LassoNode
|
|
*
|
|
* Create a debug dump for @node, it is pretty printed so any contained signature will be
|
|
* uncheckable.
|
|
*
|
|
* Return value: a full indented and so human readable dump of @node. The string must be freed by
|
|
* the caller.
|
|
*/
|
|
char*
|
|
lasso_node_debug(LassoNode *node)
|
|
{
|
|
return _lasso_node_export_to_xml(node, TRUE, TRUE, 5);
|
|
}
|
|
|
|
/**
|
|
* lasso_node_destroy:
|
|
* @node: a #LassoNode
|
|
*
|
|
* Destroys the #LassoNode.
|
|
**/
|
|
void
|
|
lasso_node_destroy(LassoNode *node)
|
|
{
|
|
if (node == NULL) {
|
|
return;
|
|
}
|
|
if (LASSO_IS_NODE(node)) {
|
|
LassoNodeClass *class = LASSO_NODE_GET_CLASS(node);
|
|
class->destroy(node);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* lasso_node_export_to_base64:
|
|
* @node: a #LassoNode
|
|
*
|
|
* Exports @node to a base64-encoded message.
|
|
*
|
|
* Return value: a base64-encoded export of @node. The string must be freed by
|
|
* the caller.
|
|
**/
|
|
char*
|
|
lasso_node_export_to_base64(LassoNode *node)
|
|
{
|
|
char *str;
|
|
char *ret;
|
|
|
|
g_return_val_if_fail(LASSO_IS_NODE(node), NULL);
|
|
|
|
str = lasso_node_export_to_xml(node);
|
|
ret = (char*)xmlSecBase64Encode(BAD_CAST str, strlen(str), 0);
|
|
lasso_release_string(str);
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* lasso_node_export_to_ecp_soap_response:
|
|
* @node: a #LassoNode
|
|
*
|
|
* Exports @node to a ECP SOAP message.
|
|
*
|
|
* Return value: a ECP SOAP export of @node. The string must be freed by the
|
|
* caller.
|
|
**/
|
|
char*
|
|
lasso_node_export_to_ecp_soap_response(LassoNode *node, const char *assertionConsumerURL)
|
|
{
|
|
xmlNode *envelope, *body, *message, *header, *ecp_response;
|
|
xmlNs *soap_env_ns, *ecp_ns;
|
|
char *ret;
|
|
|
|
g_return_val_if_fail(LASSO_IS_NODE(node), NULL);
|
|
|
|
message = lasso_node_get_xmlNode(node, FALSE);
|
|
|
|
envelope = xmlNewNode(NULL, (xmlChar*)"Envelope");
|
|
soap_env_ns = xmlNewNs(envelope,
|
|
(xmlChar*)LASSO_SOAP_ENV_HREF, (xmlChar*)LASSO_SOAP_ENV_PREFIX);
|
|
xmlSetNs(envelope, soap_env_ns);
|
|
|
|
header = xmlNewTextChild(envelope, NULL, (xmlChar*)"Header", NULL);
|
|
|
|
/* ECP response header block */
|
|
ecp_response = xmlNewNode(NULL, (xmlChar*)"Response");
|
|
ecp_ns = xmlNewNs(ecp_response, (xmlChar*)LASSO_ECP_HREF, (xmlChar*)LASSO_ECP_PREFIX);
|
|
xmlSetNs(ecp_response, ecp_ns);
|
|
xmlSetNsProp(ecp_response, soap_env_ns, (xmlChar*)"mustUnderstand", (xmlChar*)"1");
|
|
xmlSetNsProp(ecp_response, soap_env_ns,
|
|
(xmlChar*)"actor", (xmlChar*)LASSO_SOAP_ENV_ACTOR);
|
|
xmlSetProp(ecp_response, (xmlChar*)"AssertionConsumerServiceURL",
|
|
(const xmlChar*)assertionConsumerURL);
|
|
xmlAddChild(header, ecp_response);
|
|
|
|
/* Body block */
|
|
body = xmlNewTextChild(envelope, NULL, (xmlChar*)"Body", NULL);
|
|
xmlAddChild(body, message);
|
|
|
|
/* dump */
|
|
ret = lasso_xmlnode_to_string(envelope, FALSE, 0);
|
|
xmlFreeNode(envelope);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* lasso_node_export_to_paos_request:
|
|
* @node: a #LassoNode
|
|
*
|
|
* Exports @node to a PAOS message.
|
|
*
|
|
* Return value: a PAOS export of @node. The string must be freed by the
|
|
* caller.
|
|
**/
|
|
char*
|
|
lasso_node_export_to_paos_request(LassoNode *node, const char *issuer,
|
|
const char *responseConsumerURL, const char *relay_state)
|
|
{
|
|
xmlNode *envelope, *body, *header, *paos_request, *ecp_request, *ecp_relay_state, *message;
|
|
xmlNs *soap_env_ns, *saml_ns, *ecp_ns;
|
|
char *ret;
|
|
|
|
g_return_val_if_fail(LASSO_IS_NODE(node), NULL);
|
|
|
|
message = lasso_node_get_xmlNode(node, FALSE);
|
|
|
|
envelope = xmlNewNode(NULL, (xmlChar*)"Envelope");
|
|
soap_env_ns = xmlNewNs(envelope,
|
|
(xmlChar*)LASSO_SOAP_ENV_HREF, (xmlChar*)LASSO_SOAP_ENV_PREFIX);
|
|
xmlSetNs(envelope, soap_env_ns);
|
|
|
|
header = xmlNewTextChild(envelope, NULL, (xmlChar*)"Header", NULL);
|
|
|
|
/* PAOS request header block */
|
|
paos_request = xmlNewNode(NULL, (xmlChar*)"Request");
|
|
xmlSetNs(paos_request, xmlNewNs(paos_request,
|
|
(xmlChar*)LASSO_PAOS_HREF, (xmlChar*)LASSO_PAOS_PREFIX));
|
|
xmlSetProp(paos_request, (xmlChar*)"service", (xmlChar*)LASSO_ECP_HREF);
|
|
xmlSetProp(paos_request, (xmlChar*)"responseConsumerURL",
|
|
(const xmlChar*)responseConsumerURL);
|
|
xmlSetNsProp(paos_request, soap_env_ns, (xmlChar*)"mustUnderstand", (xmlChar*)"1");
|
|
xmlSetNsProp(paos_request, soap_env_ns, (xmlChar*)"actor", (xmlChar*)LASSO_SOAP_ENV_ACTOR);
|
|
xmlAddChild(header, paos_request);
|
|
|
|
/* ECP request header block */
|
|
ecp_request = xmlNewNode(NULL, (xmlChar*)"Request");
|
|
ecp_ns = xmlNewNs(ecp_request, (xmlChar*)LASSO_ECP_HREF, (xmlChar*)LASSO_ECP_PREFIX);
|
|
xmlSetNs(ecp_request, ecp_ns);
|
|
xmlSetProp(ecp_request, (xmlChar*)"responseConsumerURL",
|
|
(const xmlChar*)responseConsumerURL);
|
|
xmlSetNsProp(ecp_request, soap_env_ns, (xmlChar*)"mustUnderstand", (xmlChar*)"1");
|
|
xmlSetNsProp(ecp_request, soap_env_ns, (xmlChar*)"actor", (xmlChar*)LASSO_SOAP_ENV_ACTOR);
|
|
saml_ns = xmlNewNs(ecp_request,
|
|
(xmlChar*)LASSO_SAML2_ASSERTION_HREF,
|
|
(xmlChar*)LASSO_SAML2_ASSERTION_PREFIX);
|
|
xmlNewTextChild(ecp_request, saml_ns, (xmlChar*)"Issuer", (const xmlChar*)issuer);
|
|
xmlAddChild(header, ecp_request);
|
|
|
|
/* ECP relay state block */
|
|
if (relay_state) {
|
|
ecp_relay_state = xmlNewNode(NULL, (xmlChar*)"RelayState");
|
|
xmlNodeSetContent(ecp_relay_state, (const xmlChar*)relay_state);
|
|
ecp_ns = xmlNewNs(ecp_relay_state, (xmlChar*)LASSO_ECP_HREF,
|
|
(xmlChar*)LASSO_ECP_PREFIX);
|
|
xmlSetNs(ecp_relay_state, ecp_ns);
|
|
xmlSetNsProp(ecp_relay_state, soap_env_ns,
|
|
(xmlChar*)"mustUnderstand", (xmlChar*)"1");
|
|
xmlSetNsProp(ecp_relay_state, soap_env_ns,
|
|
(xmlChar*)"actor", (xmlChar*)LASSO_SOAP_ENV_ACTOR);
|
|
xmlAddChild(header, ecp_relay_state);
|
|
}
|
|
|
|
/* Body block */
|
|
body = xmlNewTextChild(envelope, NULL, (xmlChar*)"Body", NULL);
|
|
xmlAddChild(body, message);
|
|
|
|
ret = lasso_xmlnode_to_string(envelope, FALSE, 0);
|
|
|
|
xmlFreeNode(envelope);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* lasso_node_export_to_query:
|
|
* @node: a #LassoNode
|
|
* @sign_method: the Signature transform method
|
|
* @private_key_file: the path to the private key (may be NULL)
|
|
*
|
|
* Exports @node to a HTTP query string. If @private_key_file is NULL,
|
|
* query won't be signed.
|
|
*
|
|
* Return value: a HTTP query export of @node. The string must be freed by the
|
|
* caller.
|
|
**/
|
|
char*
|
|
lasso_node_export_to_query(LassoNode *node,
|
|
LassoSignatureMethod sign_method, const char *private_key_file)
|
|
{
|
|
char *unsigned_query, *query = NULL;
|
|
|
|
g_return_val_if_fail(LASSO_IS_NODE(node), NULL);
|
|
|
|
unsigned_query = lasso_node_build_query(node);
|
|
if (private_key_file) {
|
|
query = lasso_query_sign(unsigned_query, sign_method, private_key_file);
|
|
} else {
|
|
lasso_transfer_string(query, unsigned_query);
|
|
}
|
|
lasso_release(unsigned_query);
|
|
|
|
return query;
|
|
}
|
|
|
|
/**
|
|
* lasso_node_export_to_xml:
|
|
* @node: a #LassoNode
|
|
*
|
|
* Exports @node to an xml message.
|
|
*
|
|
* Return value: an xml export of @node. The string must be freed by the
|
|
* caller.
|
|
**/
|
|
gchar*
|
|
lasso_node_export_to_xml(LassoNode *node)
|
|
{
|
|
return _lasso_node_export_to_xml(node, FALSE, FALSE, 0);
|
|
}
|
|
|
|
/**
|
|
* lasso_node_export_to_soap:
|
|
* @node: a #LassoNode
|
|
*
|
|
* Exports @node to a SOAP message.
|
|
*
|
|
* Return value: a SOAP export of @node. The string must be freed by the
|
|
* caller.
|
|
**/
|
|
char*
|
|
lasso_node_export_to_soap(LassoNode *node)
|
|
{
|
|
LassoSoapEnvelope *envelope;
|
|
LassoSoapBody *body;
|
|
char *ret;
|
|
|
|
g_return_val_if_fail(LASSO_IS_NODE(node), NULL);
|
|
|
|
body = lasso_soap_body_new();
|
|
envelope = lasso_soap_envelope_new(body);
|
|
lasso_list_add_gobject(body->any, node);
|
|
ret = lasso_node_export_to_xml((LassoNode*)envelope);
|
|
lasso_release_gobject(envelope);
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* lasso_node_encrypt:
|
|
* @lasso_node: a #LassoNode to encrypt
|
|
* @encryption_public_key : RSA public key the node will be encrypted with
|
|
*
|
|
* Generate a DES key and encrypt it with the RSA key.
|
|
* Then encrypt @lasso_node with the DES key.
|
|
*
|
|
* Return value: an xmlNode which is the @node in an encrypted fashion.
|
|
* It must be freed by the caller.
|
|
**/
|
|
LassoSaml2EncryptedElement*
|
|
lasso_node_encrypt(LassoNode *lasso_node, xmlSecKey *encryption_public_key,
|
|
LassoEncryptionSymKeyType encryption_sym_key_type, const char *recipient)
|
|
{
|
|
xmlDocPtr doc = NULL;
|
|
xmlNodePtr orig_node = NULL;
|
|
LassoSaml2EncryptedElement *encrypted_element = NULL, *ret = NULL;
|
|
xmlSecKeysMngrPtr key_manager = NULL;
|
|
xmlNodePtr key_info_node = NULL;
|
|
xmlNodePtr encrypted_key_node = NULL;
|
|
xmlNodePtr encrypted_data = NULL;
|
|
xmlNodePtr key_info_node2 = NULL;
|
|
xmlSecEncCtxPtr enc_ctx = NULL;
|
|
xmlSecTransformId xmlsec_encryption_sym_key_type;
|
|
xmlSecKey *duplicate = NULL;
|
|
|
|
if (encryption_public_key == NULL || !xmlSecKeyIsValid(encryption_public_key)) {
|
|
message(G_LOG_LEVEL_WARNING, "Invalid encryption key");
|
|
goto cleanup;
|
|
}
|
|
|
|
/* Create a document to contain the node to encrypt */
|
|
doc = xmlNewDoc((xmlChar*)"1.0");
|
|
orig_node = lasso_node_get_xmlNode(lasso_node, FALSE);
|
|
xmlDocSetRootElement(doc, orig_node);
|
|
|
|
/* Get the symetric key type */
|
|
switch (encryption_sym_key_type) {
|
|
case LASSO_ENCRYPTION_SYM_KEY_TYPE_AES_256:
|
|
xmlsec_encryption_sym_key_type = xmlSecTransformAes256CbcId;
|
|
break;
|
|
case LASSO_ENCRYPTION_SYM_KEY_TYPE_3DES:
|
|
xmlsec_encryption_sym_key_type = xmlSecTransformDes3CbcId;
|
|
break;
|
|
case LASSO_ENCRYPTION_SYM_KEY_TYPE_AES_128:
|
|
default:
|
|
xmlsec_encryption_sym_key_type = xmlSecTransformAes128CbcId;
|
|
break;
|
|
}
|
|
|
|
/* Create encryption template for a specific symetric key type */
|
|
/* saml-core 2.2.4 line 498:
|
|
* The Type attribute SHOULD be present and, if present, MUST contain a value of
|
|
* http://www.w3.org/2001/04/xmlenc#Element. */
|
|
encrypted_data = xmlSecTmplEncDataCreate(doc,
|
|
xmlsec_encryption_sym_key_type, NULL, xmlSecTypeEncElement, NULL, NULL);
|
|
|
|
if (encrypted_data == NULL) {
|
|
message(G_LOG_LEVEL_WARNING, "Failed to create encryption template");
|
|
goto cleanup;
|
|
}
|
|
|
|
if (xmlSecTmplEncDataEnsureCipherValue(encrypted_data) == NULL) {
|
|
message(G_LOG_LEVEL_WARNING, "Failed to add CipherValue node");
|
|
goto cleanup;
|
|
}
|
|
|
|
/* create and initialize keys manager, we use a simple list based
|
|
* keys manager, implement your own xmlSecKeysStore klass if you need
|
|
* something more sophisticated
|
|
*/
|
|
key_manager = xmlSecKeysMngrCreate();
|
|
if (key_manager == NULL) {
|
|
message(G_LOG_LEVEL_WARNING, "Failed to create keys manager");
|
|
goto cleanup;
|
|
}
|
|
|
|
if (xmlSecCryptoAppDefaultKeysMngrInit(key_manager) < 0) {
|
|
message(G_LOG_LEVEL_WARNING, "Failed to initialize keys manager");
|
|
goto cleanup;
|
|
}
|
|
|
|
/* add key to keys manager, from now on keys manager is responsible
|
|
* for destroying key
|
|
*/
|
|
duplicate = xmlSecKeyDuplicate(encryption_public_key);
|
|
if (xmlSecCryptoAppDefaultKeysMngrAdoptKey(key_manager, duplicate) < 0) {
|
|
lasso_release_sec_key(duplicate);
|
|
goto cleanup;
|
|
}
|
|
|
|
/* add <dsig:KeyInfo/> */
|
|
key_info_node = xmlSecTmplEncDataEnsureKeyInfo(encrypted_data, NULL);
|
|
if (key_info_node == NULL) {
|
|
message(G_LOG_LEVEL_WARNING, "Failed to add key info");
|
|
goto cleanup;
|
|
}
|
|
|
|
/* add <enc:EncryptedKey/> to store the encrypted session key */
|
|
encrypted_key_node = xmlSecTmplKeyInfoAddEncryptedKey(key_info_node,
|
|
xmlSecTransformRsaPkcs1Id, NULL, NULL, (xmlChar*)recipient);
|
|
if (encrypted_key_node == NULL) {
|
|
message(G_LOG_LEVEL_WARNING, "Failed to add encrypted key");
|
|
goto cleanup;
|
|
}
|
|
|
|
/* we want to put encrypted key in the <enc:CipherValue/> node */
|
|
if (xmlSecTmplEncDataEnsureCipherValue(encrypted_key_node) == NULL) {
|
|
message(G_LOG_LEVEL_WARNING, "Failed to add CipherValue node");
|
|
goto cleanup;
|
|
}
|
|
|
|
/* add <dsig:KeyInfo/> and <dsig:KeyName/> nodes to <enc:EncryptedKey/> */
|
|
key_info_node2 = xmlSecTmplEncDataEnsureKeyInfo(encrypted_key_node, NULL);
|
|
if (key_info_node2 == NULL) {
|
|
message(G_LOG_LEVEL_WARNING, "Failed to add key info");
|
|
goto cleanup;
|
|
}
|
|
|
|
/* create encryption context */
|
|
enc_ctx = (xmlSecEncCtxPtr)xmlSecEncCtxCreate(key_manager);
|
|
if (enc_ctx == NULL) {
|
|
message(G_LOG_LEVEL_WARNING, "Failed to create encryption context");
|
|
goto cleanup;
|
|
}
|
|
|
|
/* generate a symetric key */
|
|
switch (encryption_sym_key_type) {
|
|
case LASSO_ENCRYPTION_SYM_KEY_TYPE_AES_256:
|
|
enc_ctx->encKey = xmlSecKeyGenerate(xmlSecKeyDataAesId, 256,
|
|
xmlSecKeyDataTypeSession);
|
|
break;
|
|
case LASSO_ENCRYPTION_SYM_KEY_TYPE_3DES:
|
|
enc_ctx->encKey = xmlSecKeyGenerate(xmlSecKeyDataDesId, 192,
|
|
xmlSecKeyDataTypeSession);
|
|
break;
|
|
case LASSO_ENCRYPTION_SYM_KEY_TYPE_AES_128:
|
|
default:
|
|
enc_ctx->encKey = xmlSecKeyGenerate(xmlSecKeyDataAesId, 128,
|
|
xmlSecKeyDataTypeSession);
|
|
break;
|
|
}
|
|
|
|
if (enc_ctx->encKey == NULL) {
|
|
message(G_LOG_LEVEL_WARNING, "Failed to generate session des key");
|
|
goto cleanup;
|
|
}
|
|
|
|
|
|
/* encrypt the data */
|
|
if (xmlSecEncCtxXmlEncrypt(enc_ctx, encrypted_data, orig_node) < 0) {
|
|
message(G_LOG_LEVEL_WARNING, "Encryption failed");
|
|
goto cleanup;
|
|
}
|
|
|
|
|
|
/* Create a new EncryptedElement */
|
|
encrypted_element = LASSO_SAML2_ENCRYPTED_ELEMENT(lasso_saml2_encrypted_element_new());
|
|
lasso_assign_gobject(encrypted_element->original_data, lasso_node);
|
|
lasso_list_add_xml_node(encrypted_element->EncryptedKey, encrypted_key_node);
|
|
lasso_assign_xml_node(encrypted_element->EncryptedData, xmlDocGetRootElement(doc));
|
|
lasso_transfer_gobject(ret, encrypted_element);
|
|
|
|
cleanup:
|
|
lasso_release_key_manager(key_manager);
|
|
lasso_release_gobject(encrypted_element);
|
|
lasso_release_encrypt_context(enc_ctx);
|
|
lasso_release_doc(doc);
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
/**
|
|
* lasso_node_init_from_query:
|
|
* @node: a #LassoNode (or derived class)
|
|
* @query: the query string
|
|
*
|
|
* Initialiazes @node fields with data from @query string.
|
|
*
|
|
* Return value: %TRUE if success
|
|
**/
|
|
gboolean
|
|
lasso_node_init_from_query(LassoNode *node, const char *query)
|
|
{
|
|
LassoNodeClass *class;
|
|
char **query_fields;
|
|
int i;
|
|
gboolean rc;
|
|
|
|
g_return_val_if_fail(LASSO_IS_NODE(node), FALSE);
|
|
class = LASSO_NODE_GET_CLASS(node);
|
|
|
|
query_fields = urlencoded_to_strings(query);
|
|
rc = class->init_from_query(node, query_fields);
|
|
for (i = 0; query_fields[i]; i++) {
|
|
xmlFree(query_fields[i]);
|
|
query_fields[i] = NULL;
|
|
}
|
|
g_free(query_fields);
|
|
return rc;
|
|
}
|
|
|
|
|
|
/**
|
|
* lasso_node_init_from_xml:
|
|
* @node: a #LassoNode (or derived class)
|
|
* @xmlnode: the libxml2 node
|
|
*
|
|
* Initialiazes @node fields with data from @xmlnode XML node.
|
|
*
|
|
* Return value: 0 on success; or a negative value otherwise.
|
|
**/
|
|
int
|
|
lasso_node_init_from_xml(LassoNode *node, xmlNode *xmlnode)
|
|
{
|
|
LassoNodeClass *class;
|
|
|
|
g_return_val_if_fail(LASSO_IS_NODE(node), LASSO_XML_ERROR_OBJECT_CONSTRUCTION_FAILED);
|
|
class = LASSO_NODE_GET_CLASS(node);
|
|
|
|
return class->init_from_xml(node, xmlnode);
|
|
}
|
|
|
|
/**
|
|
* lasso_node_build_query:
|
|
* @node: a #LassoNode
|
|
*
|
|
* Build an HTTP query from the given LassoNode, this is a pure virtual
|
|
* function, you must overload it in subclass.
|
|
*
|
|
* Return value: a newly allocated string containing the query if it succeed,
|
|
* or NULL otherwise.
|
|
*/
|
|
char*
|
|
lasso_node_build_query(LassoNode *node)
|
|
{
|
|
LassoNodeClass *class;
|
|
g_return_val_if_fail (LASSO_IS_NODE(node), NULL);
|
|
|
|
class = LASSO_NODE_GET_CLASS(node);
|
|
return class->build_query(node);
|
|
}
|
|
|
|
|
|
/**
|
|
* lasso_node_get_xmlNode:
|
|
* @node: a #LassoNode
|
|
* @lasso_dump: whether to include lasso-specific nodes
|
|
*
|
|
* Builds an XML representation of @node.
|
|
*
|
|
* Return value: a new xmlNode. It must be freed by the caller.
|
|
**/
|
|
xmlNode*
|
|
lasso_node_get_xmlNode(LassoNode *node, gboolean lasso_dump)
|
|
{
|
|
LassoNodeClass *class;
|
|
g_return_val_if_fail (LASSO_IS_NODE(node), NULL);
|
|
class = LASSO_NODE_GET_CLASS(node);
|
|
return class->get_xmlNode(node, lasso_dump);
|
|
}
|
|
|
|
/**
|
|
* lasso_node_cleanup_original_xmlnodes:
|
|
*
|
|
* @node: a #LassoNode
|
|
*
|
|
* Traverse the #LassoNode tree starting at Node and remove keeped xmlNode if one is found.
|
|
*
|
|
* Return value: None
|
|
*/
|
|
void
|
|
lasso_node_cleanup_original_xmlnodes(LassoNode *node)
|
|
{
|
|
lasso_node_traversal(node, lasso_node_remove_original_xmlnode, 0);
|
|
}
|
|
|
|
static GQuark original_xmlnode_quark;
|
|
static GQuark custom_element_quark;
|
|
|
|
/**
|
|
* lasso_node_get_original_xmlnode:
|
|
* @node: a #LassoNode
|
|
*
|
|
* Retrieve the original xmlNode eventually associated to this #LassoNode.
|
|
*
|
|
* Return value: an #xmlNodePtr or NULL.
|
|
*/
|
|
xmlNodePtr
|
|
lasso_node_get_original_xmlnode(LassoNode *node)
|
|
{
|
|
return g_object_get_qdata(G_OBJECT(node), original_xmlnode_quark);
|
|
}
|
|
|
|
static void original_xmlnode_free(void *node) {
|
|
xmlNode *xnode = (xmlNode*)node;
|
|
|
|
|
|
if (node) {
|
|
if (lasso_flag_memory_debug) {
|
|
fprintf(stderr, "freeing original xmlnode %s (at %p)\n", xnode->name, xnode);
|
|
}
|
|
xmlFreeNode(xnode);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* lasso_node_set_original_xmlnode:
|
|
* @node: the #LassoNode object
|
|
* @xmlnode: an #xmlNode
|
|
*
|
|
* Set the underlying XML representation of the object.
|
|
*
|
|
*/
|
|
void
|
|
lasso_node_set_original_xmlnode(LassoNode *node, xmlNode* xmlnode)
|
|
{
|
|
if (xmlnode) {
|
|
xmlNode *copy = NULL;
|
|
|
|
copy = xmlCopyNode(xmlnode, 1);
|
|
if (lasso_flag_memory_debug) {
|
|
fprintf(stderr, "setting original xmlnode (at %p) on node %s:%p\n", copy, G_OBJECT_TYPE_NAME (node), node);
|
|
}
|
|
g_object_set_qdata_full(G_OBJECT(node), original_xmlnode_quark, copy, (GDestroyNotify)original_xmlnode_free);
|
|
} else {
|
|
if (lasso_flag_memory_debug) {
|
|
fprintf(stderr, "clearing original xmlnode on node %p\n", node);
|
|
}
|
|
g_object_set_qdata_full(G_OBJECT(node), original_xmlnode_quark, NULL, (GDestroyNotify)original_xmlnode_free);
|
|
}
|
|
}
|
|
|
|
struct _CustomElement {
|
|
char *prefix;
|
|
char *href;
|
|
char *nodename;
|
|
};
|
|
|
|
static struct _CustomElement *
|
|
_lasso_node_new_custom_element()
|
|
{
|
|
struct _CustomElement *ret = g_new0(struct _CustomElement, 1);
|
|
return ret;
|
|
}
|
|
|
|
static void
|
|
_lasso_node_free_custom_element(struct _CustomElement *custom_element)
|
|
{
|
|
lasso_release_string(custom_element->prefix);
|
|
lasso_release_string(custom_element->href);
|
|
lasso_release_string(custom_element->nodename);
|
|
lasso_release(custom_element);
|
|
}
|
|
|
|
/**
|
|
* _lasso_node_get_custom_element:
|
|
* @node: a #LassoNode object
|
|
*
|
|
* Return the eventually attached custom namespace object
|
|
*
|
|
* Return value: NULL or an #_CustomElement structure.
|
|
*/
|
|
static struct _CustomElement*
|
|
_lasso_node_get_custom_element(LassoNode *node)
|
|
{
|
|
if (! LASSO_NODE(node))
|
|
return NULL;
|
|
return g_object_get_qdata((GObject*)node, custom_element_quark);
|
|
}
|
|
|
|
static struct _CustomElement*
|
|
_lasso_node_get_custom_element_or_create(LassoNode *node)
|
|
{
|
|
struct _CustomElement *custom_element;
|
|
|
|
if (! LASSO_IS_NODE(node))
|
|
return NULL;
|
|
|
|
custom_element = _lasso_node_get_custom_element(node);
|
|
if (! custom_element) {
|
|
custom_element = _lasso_node_new_custom_element();
|
|
g_object_set_qdata_full((GObject*)node, custom_element_quark,
|
|
custom_element,
|
|
(GDestroyNotify)_lasso_node_free_custom_element);
|
|
}
|
|
return custom_element;
|
|
}
|
|
|
|
|
|
/**
|
|
* lasso_node_set_custom_namespace:
|
|
* @node: a #LassoNode object
|
|
* @prefix: the prefix to use for the definition
|
|
* @href: the URI of the namespace
|
|
*
|
|
* Set a custom namespace for an object instance, use it with object existing a lot of revision of
|
|
* the nearly same namespace.
|
|
*/
|
|
void
|
|
lasso_node_set_custom_namespace(LassoNode *node, const char *prefix, const char *href)
|
|
{
|
|
struct _CustomElement *custom_element;
|
|
|
|
custom_element = _lasso_node_get_custom_element_or_create(node);
|
|
g_return_if_fail (custom_element != NULL);
|
|
|
|
lasso_assign_string(custom_element->prefix, prefix);
|
|
lasso_assign_string(custom_element->href, href);
|
|
}
|
|
|
|
/**
|
|
* lasso_node_set_custom_nodename:
|
|
* @node: a #LassoNode object
|
|
* @nodename: the name to use for the node
|
|
*
|
|
* Set a custom nodename for an object instance, use it with object implement a schema type and not
|
|
* a real element.
|
|
*/
|
|
void
|
|
lasso_node_set_custom_nodename(LassoNode *node, const char *nodename)
|
|
{
|
|
struct _CustomElement *custom_element;
|
|
|
|
custom_element = _lasso_node_get_custom_element_or_create(node);
|
|
g_return_if_fail (custom_element != NULL);
|
|
|
|
lasso_assign_string(custom_element->nodename, nodename);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* implementation methods */
|
|
/*****************************************************************************/
|
|
|
|
static void
|
|
lasso_node_remove_original_xmlnode(LassoNode *node, SnippetType type) {
|
|
LassoNodeClass *class;
|
|
class = LASSO_NODE_GET_CLASS(node);
|
|
|
|
if (class->node_data->keep_xmlnode || type & SNIPPET_KEEP_XMLNODE) {
|
|
lasso_node_set_original_xmlnode(node, NULL);
|
|
}
|
|
}
|
|
|
|
static void
|
|
lasso_node_traversal(LassoNode *node, void (*do_to_node)(LassoNode *node, SnippetType type), SnippetType type) {
|
|
LassoNodeClass *class;
|
|
struct XmlSnippet *snippet;
|
|
|
|
if (node == NULL) {
|
|
return;
|
|
}
|
|
class = LASSO_NODE_GET_CLASS(node);
|
|
if (class == NULL || class->node_data == NULL || do_to_node == NULL) {
|
|
return;
|
|
}
|
|
do_to_node(node, type);
|
|
|
|
snippet = class->node_data->snippets;
|
|
while (snippet->name != NULL) {
|
|
SnippetType type;
|
|
void **value = G_STRUCT_MEMBER_P(node, snippet->offset);
|
|
|
|
type = snippet->type & 0xff;
|
|
switch (type) {
|
|
case SNIPPET_NODE:
|
|
case SNIPPET_NAME_IDENTIFIER:
|
|
case SNIPPET_NODE_IN_CHILD:
|
|
lasso_node_traversal(*value, do_to_node, snippet->type);
|
|
break;
|
|
case SNIPPET_LIST_NODES:
|
|
{
|
|
GList *list = *value;
|
|
while (list != NULL) {
|
|
if (list->data) {
|
|
lasso_node_traversal(LASSO_NODE(list->data), do_to_node, snippet->type);
|
|
}
|
|
list = g_list_next(list);
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
snippet++;
|
|
}
|
|
}
|
|
|
|
static void
|
|
lasso_node_impl_destroy(LassoNode *node)
|
|
{
|
|
g_object_unref(G_OBJECT(node));
|
|
}
|
|
#define trace_snippet(format, args...) \
|
|
lasso_trace(format "%s.%s\n", ## args, G_OBJECT_TYPE_NAME(node), snippet->name)
|
|
|
|
/** FIXME: return a real error code */
|
|
static int
|
|
lasso_node_impl_init_from_xml(LassoNode *node, xmlNode *xmlnode)
|
|
{
|
|
struct XmlSnippet *snippet;
|
|
xmlNode *t;
|
|
LassoNodeClass *class;
|
|
void *value;
|
|
SnippetType type;
|
|
struct XmlSnippet *snippet_any = NULL;
|
|
struct XmlSnippet *snippet_any_attribute = NULL;
|
|
GSList *unknown_nodes = NULL;
|
|
GSList *known_attributes = NULL;
|
|
|
|
class = LASSO_NODE_GET_CLASS(node);
|
|
|
|
/* What do you want me to initialize ? */
|
|
if (! xmlnode)
|
|
return 1;
|
|
|
|
/* No node_data no initialization possible */
|
|
if (! class->node_data) {
|
|
message(G_LOG_LEVEL_WARNING, "Class %s has no node_data so no initialization is possible", G_OBJECT_CLASS_NAME(class));
|
|
return 0;
|
|
}
|
|
|
|
if (class->node_data->keep_xmlnode) {
|
|
lasso_node_set_original_xmlnode(node, xmlnode);
|
|
}
|
|
if (lasso_flag_memory_debug == TRUE) {
|
|
fprintf(stderr, "Initializing %s (at %p)\n", G_OBJECT_TYPE_NAME(node), node);
|
|
}
|
|
|
|
while (class && LASSO_IS_NODE_CLASS(class) && class->node_data) {
|
|
lasso_trace(" initializing %s\n", G_OBJECT_CLASS_NAME(class));
|
|
|
|
for (t = xmlnode->children; t; t = t->next) {
|
|
if (t->type == XML_TEXT_NODE) {
|
|
for (snippet = class->node_data->snippets;
|
|
snippet && snippet->name; snippet++) {
|
|
GList **location = NULL;
|
|
|
|
type = snippet->type & 0xff;
|
|
value = G_STRUCT_MEMBER_P(node, snippet->offset);
|
|
|
|
if (type == SNIPPET_LIST_XMLNODES) {
|
|
location = value;
|
|
*location = g_list_append(
|
|
*location, xmlCopyNode(t, 1));
|
|
trace_snippet(" adding xmlNode %p", g_list_last(*location)->data);
|
|
} else if (type == SNIPPET_LIST_NODES &&
|
|
snippet->type & SNIPPET_ALLOW_TEXT) {
|
|
LassoNode *text_node;
|
|
text_node = lasso_node_new_from_xmlNode_with_type(t,
|
|
"LassoMiscTextNode");
|
|
location = value;
|
|
*location = g_list_append(*location, text_node);
|
|
trace_snippet(" adding LassoMiscTextNode %p", text_node);
|
|
}
|
|
continue;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
if (t->type != XML_ELEMENT_NODE)
|
|
continue;
|
|
|
|
for (snippet = class->node_data->snippets;
|
|
snippet && snippet->name; snippet++) {
|
|
void *tmp = NULL;
|
|
type = snippet->type & 0xff;
|
|
value = G_STRUCT_MEMBER_P(node, snippet->offset);
|
|
|
|
if ((snippet->type & SNIPPET_ANY) && type != SNIPPET_ATTRIBUTE) {
|
|
snippet_any = snippet;
|
|
}
|
|
|
|
if (strcmp((char*)t->name, snippet->name) != 0 && snippet->name[0])
|
|
continue;
|
|
|
|
if (type == SNIPPET_NODE) {
|
|
tmp = lasso_node_new_from_xmlNode_with_type(t,
|
|
snippet->class_name);
|
|
} else if (type == SNIPPET_NODE_IN_CHILD) {
|
|
xmlNode *t2 = t->children;
|
|
while (t2 && t2->type != XML_ELEMENT_NODE)
|
|
t2 = t2->next;
|
|
if (t2) {
|
|
tmp = lasso_node_new_from_xmlNode_with_type(t2,
|
|
snippet->class_name);
|
|
}
|
|
} else if (type == SNIPPET_CONTENT) {
|
|
tmp = xmlNodeGetContent(t);
|
|
} else if (type == SNIPPET_NAME_IDENTIFIER) {
|
|
tmp = lasso_saml_name_identifier_new_from_xmlNode(t);
|
|
} else if (type == SNIPPET_LIST_NODES) {
|
|
GList **location = value;
|
|
LassoNode *n;
|
|
n = lasso_node_new_from_xmlNode_with_type(t,
|
|
snippet->class_name);
|
|
if (n == NULL && snippet_any == snippet &&
|
|
t->properties == NULL && t->children &&
|
|
t->children->type == XML_TEXT_NODE &&
|
|
t->children->next == NULL) {
|
|
/* unknown, but no attributes, and content
|
|
* is text ? -> use generic object */
|
|
n = lasso_node_new_from_xmlNode_with_type(t,
|
|
"LassoMiscTextNode");
|
|
}
|
|
if (n && snippet->type & SNIPPET_KEEP_XMLNODE &&
|
|
! LASSO_NODE_GET_CLASS(n)->node_data->keep_xmlnode) {
|
|
lasso_node_set_original_xmlnode(n, t);
|
|
}
|
|
|
|
if (n) {
|
|
*location = g_list_append(*location, n);
|
|
trace_snippet(" adding %p of type %s(%s) to ",
|
|
n, G_OBJECT_TYPE_NAME(n),
|
|
snippet->class_name);
|
|
} else {
|
|
/* failed to do sth with */
|
|
message(G_LOG_LEVEL_WARNING,
|
|
"Failed to do sth with %s",
|
|
t->name);
|
|
}
|
|
} else if (type == SNIPPET_LIST_CONTENT) {
|
|
GList **location = value;
|
|
xmlChar *s = xmlNodeGetContent(t);
|
|
lasso_list_add_string(*location, (char*)s);
|
|
trace_snippet(" adding text %s as content to ", s);
|
|
lasso_release_xml_string(s);
|
|
} else if (type == SNIPPET_EXTENSION ||
|
|
type == SNIPPET_LIST_XMLNODES) {
|
|
GList **location = value;
|
|
*location = g_list_append(*location, xmlCopyNode(t, 1));
|
|
trace_snippet(" adding xmlNode %p to ", g_list_last(*location)->data);
|
|
} else if (type == SNIPPET_XMLNODE) {
|
|
tmp = xmlCopyNode(t, 1);
|
|
}
|
|
|
|
if (tmp == NULL)
|
|
break;
|
|
|
|
if (type == SNIPPET_XMLNODE || type == SNIPPET_NODE || type == SNIPPET_NODE_IN_CHILD || type ==
|
|
SNIPPET_NAME_IDENTIFIER) {
|
|
if (snippet->type & SNIPPET_KEEP_XMLNODE && !
|
|
LASSO_NODE_GET_CLASS(tmp)->node_data->keep_xmlnode)
|
|
{
|
|
lasso_trace(" setting original xmlNode of %p (%s) to %p", tmp, G_OBJECT_TYPE_NAME(tmp), t)
|
|
lasso_node_set_original_xmlnode(tmp, t);
|
|
}
|
|
if (type == SNIPPET_XMLNODE) {
|
|
trace_snippet(" setting xmlNode %p as ", tmp);
|
|
} else {
|
|
trace_snippet(" setting %p of type %s (wanted %s) as ", tmp,
|
|
G_OBJECT_TYPE_NAME(tmp),
|
|
snippet->class_name);
|
|
}
|
|
*(void**)value = tmp;
|
|
tmp = NULL;
|
|
} else if (snippet->type & SNIPPET_INTEGER) {
|
|
int val = atoi(tmp);
|
|
(*(int*)value) = val;
|
|
trace_snippet(" setting integer %i for ", val);
|
|
xmlFree(tmp);
|
|
tmp = NULL;
|
|
} else if (snippet->type & SNIPPET_BOOLEAN) {
|
|
int val = 0;
|
|
if (strcmp((char*)tmp, "true") == 0) {
|
|
val = 1;
|
|
} else if (strcmp((char*)tmp, "1") == 0) {
|
|
val = 1;
|
|
}
|
|
trace_snippet(" setting bool %s for ", val ? "TRUE" : "FALSE");
|
|
(*(int*)value) = val;
|
|
xmlFree(tmp);
|
|
tmp = NULL;
|
|
} else {
|
|
lasso_release_string(*(char**)value);
|
|
*(char**)value = g_strdup(tmp);
|
|
trace_snippet(" setting text %s as value for ", (char*)tmp);
|
|
if (lasso_flag_memory_debug == TRUE) {
|
|
fprintf(stderr, " setting field %s/%s to value %p: %s\n", G_OBJECT_TYPE_NAME(node), snippet->name, *(void**)value, (char*)tmp);
|
|
}
|
|
lasso_release_xml_string((*(xmlChar**)&tmp));
|
|
tmp = NULL;
|
|
}
|
|
|
|
break;
|
|
}
|
|
if ((snippet == NULL || snippet->name == NULL) && snippet_any) {
|
|
if (g_slist_find(unknown_nodes, t) == NULL)
|
|
unknown_nodes = g_slist_append(unknown_nodes, t);
|
|
} else {
|
|
unknown_nodes = g_slist_remove(unknown_nodes, t);
|
|
}
|
|
}
|
|
|
|
for (snippet = class->node_data->snippets; snippet && snippet->name; snippet++) {
|
|
void *tmp = NULL;
|
|
type = snippet->type & 0xff;
|
|
|
|
value = G_STRUCT_MEMBER_P(node, snippet->offset);
|
|
if (type == SNIPPET_ATTRIBUTE) {
|
|
if (snippet->type & SNIPPET_ANY) {
|
|
snippet_any_attribute = snippet;
|
|
continue;
|
|
}
|
|
tmp = xmlGetProp(xmlnode, (xmlChar*)snippet->name);
|
|
known_attributes = g_slist_append(known_attributes, snippet->name);
|
|
}
|
|
if (type == SNIPPET_TEXT_CHILD)
|
|
tmp = xmlNodeGetContent(xmlnode);
|
|
if (tmp == NULL)
|
|
continue;
|
|
|
|
if (snippet->type & SNIPPET_INTEGER) {
|
|
int val = atoi(tmp);
|
|
(*(int*)value) = val;
|
|
} else if (snippet->type & SNIPPET_BOOLEAN) {
|
|
int val = 0;
|
|
if (strcmp((char*)tmp, "true") == 0) {
|
|
val = 1;
|
|
} else if (strcmp((char*)tmp, "1") == 0) {
|
|
val = 1;
|
|
}
|
|
(*(int*)value) = val;
|
|
} else {
|
|
lasso_assign_string((*(char**)value), tmp);
|
|
if (lasso_flag_memory_debug == TRUE) {
|
|
fprintf(stderr, " setting prop %s/%s to value %p: %s\n",
|
|
G_OBJECT_TYPE_NAME(node), snippet->name, *(void**)value, (char*)tmp);
|
|
}
|
|
}
|
|
xmlFree(tmp);
|
|
}
|
|
|
|
class = g_type_class_peek_parent(class);
|
|
}
|
|
|
|
if (unknown_nodes && snippet_any) {
|
|
xmlNode *t = unknown_nodes->data;
|
|
void *tmp;
|
|
value = G_STRUCT_MEMBER_P(node, snippet_any->offset);
|
|
tmp = lasso_node_new_from_xmlNode_with_type(t, snippet_any->class_name);
|
|
(*(char**)value) = tmp;
|
|
}
|
|
|
|
if (snippet_any_attribute) {
|
|
GHashTable **any_attribute;
|
|
GSList *tmp_attr;
|
|
xmlAttr *node_attr;
|
|
|
|
any_attribute = G_STRUCT_MEMBER_P(node, snippet_any_attribute->offset);
|
|
if (*any_attribute == NULL) {
|
|
*any_attribute = g_hash_table_new_full(
|
|
g_str_hash, g_str_equal, g_free, g_free);
|
|
}
|
|
|
|
for (node_attr = xmlnode->properties; node_attr; node_attr = node_attr->next) {
|
|
xmlChar *attr_name = (xmlChar*)node_attr->name;
|
|
gboolean known_attr = FALSE;
|
|
for (tmp_attr = known_attributes; tmp_attr;
|
|
tmp_attr = g_slist_next(tmp_attr)) {
|
|
if (strcmp(tmp_attr->data, (char*)attr_name) == 0) {
|
|
known_attr = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
if (known_attr == FALSE) {
|
|
xmlChar *tmp = xmlGetProp(xmlnode, attr_name);
|
|
g_hash_table_insert(*any_attribute,
|
|
g_strdup((char*)attr_name), g_strdup((char*)tmp));
|
|
xmlFree(tmp);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
if (unknown_nodes) {
|
|
g_slist_free(unknown_nodes);
|
|
}
|
|
|
|
if (known_attributes) {
|
|
g_slist_free(known_attributes);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
#undef trace_snippet
|
|
|
|
/**
|
|
* lasso_node_remove_signature:
|
|
* @node: a #LassoNode object
|
|
*
|
|
* Remove any signature setup on this node.
|
|
*/
|
|
void
|
|
lasso_node_remove_signature(LassoNode *node) {
|
|
LassoNodeClass *klass;
|
|
|
|
if (! LASSO_IS_NODE(node))
|
|
return;
|
|
klass = LASSO_NODE_GET_CLASS(node);
|
|
/* follow the class parenting chain */
|
|
while (klass && LASSO_IS_NODE_CLASS(klass)) {
|
|
if (klass && klass->node_data && klass->node_data->sign_type_offset != 0) {
|
|
G_STRUCT_MEMBER(LassoSignatureType, node, klass->node_data->sign_type_offset) =
|
|
LASSO_SIGNATURE_TYPE_NONE;
|
|
}
|
|
klass = g_type_class_peek_parent(klass);
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* private methods */
|
|
/*****************************************************************************/
|
|
|
|
static char*
|
|
lasso_node_impl_build_query(LassoNode *node)
|
|
{
|
|
return lasso_node_build_query_from_snippets(node);
|
|
}
|
|
|
|
static xmlNode*
|
|
lasso_node_impl_get_xmlNode(LassoNode *node, gboolean lasso_dump)
|
|
{
|
|
LassoNodeClass *class = LASSO_NODE_GET_CLASS(node);
|
|
xmlNode *xmlnode;
|
|
xmlNs *ns;
|
|
GList *list_ns = NULL, *list_classes = NULL, *t;
|
|
LassoNode *value_node;
|
|
struct XmlSnippet *version_snippet;
|
|
struct _CustomElement *custom_element;
|
|
|
|
if (class->node_data == NULL)
|
|
return NULL;
|
|
|
|
xmlnode = xmlNewNode(NULL, (xmlChar*)class->node_data->node_name);
|
|
custom_element = _lasso_node_get_custom_element(node);
|
|
/* collect namespaces in the order of ancestor classes, nearer first */
|
|
while (class && LASSO_IS_NODE_CLASS(class) && class->node_data) {
|
|
if (class->node_data->ns && (! custom_element || ! custom_element->href || class != LASSO_NODE_GET_CLASS(node)))
|
|
list_ns = g_list_append(list_ns, class->node_data->ns);
|
|
list_classes = g_list_append(list_classes, class);
|
|
class = g_type_class_peek_parent(class);
|
|
}
|
|
|
|
/* create the namespaces */
|
|
t = g_list_first(list_ns);
|
|
while (t) {
|
|
ns = t->data;
|
|
xmlNewNs(xmlnode, ns->href, ns->prefix);
|
|
t = g_list_next(t);
|
|
}
|
|
g_list_free(list_ns);
|
|
/* first NS defined is the namespace of the element */
|
|
xmlSetNs(xmlnode, xmlnode->nsDef);
|
|
|
|
/* set a custom namespace if one is found */
|
|
if (custom_element != NULL) {
|
|
if (custom_element->href) {
|
|
xmlChar *prefix = BAD_CAST (custom_element->prefix);
|
|
xmlNs *ns = NULL, *oldns = NULL;
|
|
|
|
oldns = xmlSearchNs(NULL, xmlnode, prefix);
|
|
if (prefix && oldns) {
|
|
prefix = NULL;
|
|
}
|
|
// remove existing default namespace
|
|
if (prefix == NULL) {
|
|
xmlNs *cur = xmlnode->nsDef, *last = NULL;
|
|
while (cur) {
|
|
if (cur->prefix == NULL) {
|
|
if (last) {
|
|
last->next = cur->next;
|
|
} else {
|
|
xmlnode->nsDef = cur->next;
|
|
}
|
|
xmlFreeNs(cur);
|
|
}
|
|
last = cur;
|
|
cur = cur->next;
|
|
}
|
|
}
|
|
ns = xmlNewNs(xmlnode, (xmlChar*)custom_element->href,
|
|
(xmlChar*)custom_element->prefix);
|
|
/* skip the base class namespace, it is replaced by the custom one */
|
|
xmlSetNs(xmlnode, ns);
|
|
}
|
|
if (custom_element->nodename) {
|
|
xmlNodeSetName(xmlnode, BAD_CAST (custom_element->nodename));
|
|
}
|
|
}
|
|
|
|
|
|
t = g_list_last(list_classes);
|
|
while (t) {
|
|
class = t->data;
|
|
lasso_node_build_xmlNode_from_snippets(node, xmlnode,
|
|
class->node_data->snippets, lasso_dump);
|
|
t = g_list_previous(t);
|
|
}
|
|
g_list_free(list_classes);
|
|
|
|
xmlCleanNs(xmlnode);
|
|
|
|
/* backward compatibility with Liberty ID-FF 1.1; */
|
|
if (find_path(node, "MajorVersion", &value_node, &version_snippet) == TRUE) {
|
|
int *value;
|
|
int major_version, minor_version;
|
|
|
|
value = G_STRUCT_MEMBER_P(value_node, version_snippet->offset);
|
|
major_version = *value;
|
|
|
|
find_path(node, "MinorVersion", &value_node, &version_snippet);
|
|
value = G_STRUCT_MEMBER_P(value_node, version_snippet->offset);
|
|
minor_version = *value;
|
|
|
|
if (strcmp((char*)xmlnode->ns->href, LASSO_LIB_HREF) == 0) {
|
|
if (major_version == 1 && minor_version == 0) {
|
|
xmlFree((xmlChar*)xmlnode->ns->href); /* warning: discard const */
|
|
xmlnode->ns->href = xmlStrdup((xmlChar*)
|
|
"http://projectliberty.org/schemas/core/2002/12");
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
return xmlnode;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* overridden parent class methods */
|
|
/*****************************************************************************/
|
|
|
|
static GObjectClass *parent_class = NULL;
|
|
|
|
static void
|
|
lasso_node_dispose(GObject *object)
|
|
{
|
|
LassoNodeClass *class;
|
|
struct XmlSnippet *snippet;
|
|
SnippetType type;
|
|
|
|
if (lasso_flag_memory_debug == TRUE) {
|
|
fprintf(stderr, "dispose of %s (at %p)\n", G_OBJECT_TYPE_NAME(object), object);
|
|
}
|
|
|
|
class = LASSO_NODE_GET_CLASS(object);
|
|
|
|
while (class && LASSO_IS_NODE_CLASS(class) && class->node_data) {
|
|
for (snippet = class->node_data->snippets; snippet && snippet->name; snippet++) {
|
|
void **value = G_STRUCT_MEMBER_P(object, snippet->offset);
|
|
type = snippet->type & 0xff;
|
|
|
|
if (snippet->type & SNIPPET_BOOLEAN)
|
|
continue;
|
|
if (snippet->type & SNIPPET_INTEGER)
|
|
continue;
|
|
|
|
if (*value == NULL)
|
|
continue;
|
|
|
|
if (lasso_flag_memory_debug == TRUE) {
|
|
fprintf(stderr, " freeing %s/%s (at %p)\n",
|
|
G_OBJECT_TYPE_NAME(object), snippet->name, *value);
|
|
}
|
|
switch (type) {
|
|
case SNIPPET_NODE:
|
|
case SNIPPET_NAME_IDENTIFIER:
|
|
case SNIPPET_NODE_IN_CHILD:
|
|
lasso_release_gobject(*value);
|
|
break;
|
|
case SNIPPET_XMLNODE:
|
|
xmlFreeNode(*value);
|
|
break;
|
|
case SNIPPET_LIST_NODES:
|
|
lasso_release_list_of_gobjects((*(GList**)value));
|
|
break;
|
|
case SNIPPET_EXTENSION:
|
|
case SNIPPET_LIST_XMLNODES:
|
|
lasso_release_list_of_xml_node(*(GList**)value);
|
|
break;
|
|
case SNIPPET_LIST_CONTENT:
|
|
lasso_release_list_of_strings(*(GList**)value);
|
|
break;
|
|
case SNIPPET_CONTENT:
|
|
case SNIPPET_TEXT_CHILD:
|
|
case SNIPPET_ATTRIBUTE: {
|
|
if (snippet->type & SNIPPET_ANY) {
|
|
if (*value) {
|
|
g_hash_table_destroy(*value);
|
|
}
|
|
} else {
|
|
lasso_release_string(*(char**)value);
|
|
}
|
|
} break;
|
|
case SNIPPET_SIGNATURE:
|
|
break; /* no real element here */
|
|
default:
|
|
fprintf(stderr, "%d\n", type);
|
|
g_assert_not_reached();
|
|
}
|
|
|
|
if (type != SNIPPET_SIGNATURE) {
|
|
/* Signature snippet is not something to free,
|
|
* so don't set the value to NULL */
|
|
*value = NULL;
|
|
}
|
|
}
|
|
class = g_type_class_peek_parent(class);
|
|
}
|
|
|
|
parent_class->dispose(object);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* instance and class init functions */
|
|
/*****************************************************************************/
|
|
|
|
static gboolean
|
|
init_from_query(LassoNode *node, char **query_fields)
|
|
{
|
|
return lasso_node_init_from_query_fields(node, query_fields);
|
|
}
|
|
|
|
static void
|
|
class_init(LassoNodeClass *class)
|
|
{
|
|
GObjectClass *gobject_class = G_OBJECT_CLASS(class);
|
|
|
|
parent_class = g_type_class_peek_parent(class);
|
|
/* virtual public methods */
|
|
class->destroy = lasso_node_impl_destroy;
|
|
class->init_from_query = init_from_query;
|
|
class->init_from_xml = lasso_node_impl_init_from_xml;
|
|
|
|
/* virtual private methods */
|
|
class->build_query = lasso_node_impl_build_query;
|
|
class->get_xmlNode = lasso_node_impl_get_xmlNode;
|
|
|
|
/* override */
|
|
gobject_class->dispose = lasso_node_dispose;
|
|
|
|
original_xmlnode_quark = g_quark_from_static_string("lasso_original_xmlnode");
|
|
custom_element_quark = g_quark_from_static_string("lasso_custom_element");
|
|
class->node_data = NULL;
|
|
}
|
|
|
|
static void
|
|
base_class_finalize(LassoNodeClass *class)
|
|
{
|
|
if (class->node_data) {
|
|
LassoNodeClassData *data = class->node_data;
|
|
|
|
if (data->ns) {
|
|
xmlFreeNs(data->ns);
|
|
}
|
|
if (data->node_name) {
|
|
g_free(data->node_name);
|
|
}
|
|
g_free(class->node_data);
|
|
class->node_data = NULL;
|
|
}
|
|
}
|
|
|
|
GType
|
|
lasso_node_get_type()
|
|
{
|
|
static GType this_type = 0;
|
|
|
|
if (!this_type) {
|
|
static const GTypeInfo this_info = {
|
|
sizeof (LassoNodeClass),
|
|
NULL,
|
|
(GBaseFinalizeFunc) base_class_finalize,
|
|
(GClassInitFunc) class_init,
|
|
NULL,
|
|
NULL,
|
|
sizeof(LassoNode),
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
};
|
|
|
|
this_type = g_type_register_static(G_TYPE_OBJECT , "LassoNode", &this_info, 0);
|
|
}
|
|
return this_type;
|
|
}
|
|
|
|
/**
|
|
* lasso_node_new:
|
|
*
|
|
* Creates a new #LassoNode.
|
|
*
|
|
* Return value: a newly created #LassoNode object
|
|
**/
|
|
LassoNode*
|
|
lasso_node_new()
|
|
{
|
|
return g_object_new(LASSO_TYPE_NODE, NULL);
|
|
}
|
|
|
|
/**
|
|
* lasso_node_new_from_dump:
|
|
* @dump: XML object dump
|
|
*
|
|
* Restores the @dump to a new #LassoNode subclass.
|
|
*
|
|
* Return value: a newly created object; or NULL if an error occured.
|
|
**/
|
|
LassoNode*
|
|
lasso_node_new_from_dump(const char *dump)
|
|
{
|
|
LassoNode *node;
|
|
xmlDoc *doc;
|
|
|
|
if (dump == NULL)
|
|
return NULL;
|
|
|
|
doc = lasso_xml_parse_memory(dump, strlen(dump));
|
|
if (doc == NULL)
|
|
return NULL;
|
|
|
|
node = lasso_node_new_from_xmlNode(xmlDocGetRootElement(doc));
|
|
|
|
lasso_release_doc(doc);
|
|
return node;
|
|
}
|
|
|
|
|
|
/**
|
|
* lasso_node_new_from_soap:
|
|
* @soap: the SOAP message
|
|
*
|
|
* Parses SOAP message and creates a new Lasso object with the right class.
|
|
*
|
|
* Return value: node if success; NULL otherwise
|
|
**/
|
|
LassoNode*
|
|
lasso_node_new_from_soap(const char *soap)
|
|
{
|
|
xmlDoc *doc;
|
|
xmlNode *xmlnode;
|
|
LassoNode *node = NULL;
|
|
|
|
doc = lasso_xml_parse_memory(soap, strlen(soap));
|
|
if (doc == NULL) {
|
|
return NULL;
|
|
}
|
|
xmlnode = lasso_xml_get_soap_content(xmlDocGetRootElement(doc));
|
|
if (xmlnode == NULL) {
|
|
return NULL;
|
|
}
|
|
node = lasso_node_new_from_xmlNode(xmlnode);
|
|
|
|
lasso_release_doc(doc);
|
|
|
|
return node;
|
|
}
|
|
|
|
/* How finding a typename from an xmlNode works ?
|
|
*
|
|
* There is three way to get to a typename:
|
|
* 1. by an xsi:type QName attribute, that we resolve
|
|
* 2. by constructing a QName from the namespace of the xsi:type and the name of the node
|
|
* 3. by resolving the QName of the node
|
|
*
|
|
* To resolve a typename you must map the QName using the default registry object, or use
|
|
* prefix_from_href_and_nodename() to mat the QName to a prefix used to build the typename with this
|
|
* template: typename = "Lasso" + prefix + name_part(QName).
|
|
*
|
|
* The resolving algorithm is in the function _type_name_from_href_and_nodename().
|
|
*
|
|
* The prefix extraction in prefix_from_href_and_nodename().
|
|
*
|
|
*/
|
|
static const char *
|
|
prefix_from_href_and_nodename(const xmlChar *href, const xmlChar *nodename) {
|
|
char *prefix = NULL;
|
|
char *tmp = NULL;
|
|
|
|
if (strcmp((char*)href, LASSO_LASSO_HREF) == 0)
|
|
prefix = "";
|
|
else if (strcmp((char*)href, LASSO_SAML_ASSERTION_HREF) == 0)
|
|
prefix = "Saml";
|
|
else if (strcmp((char*)href, LASSO_SAML_PROTOCOL_HREF) == 0)
|
|
prefix = "Samlp";
|
|
else if (strcmp((char*)href, LASSO_LIB_HREF) == 0)
|
|
prefix = "Lib";
|
|
else if (strcmp((char*)href, LASSO_SAML2_ASSERTION_HREF) == 0)
|
|
prefix = "Saml2";
|
|
else if (strcmp((char*)href, LASSO_SAML2_PROTOCOL_HREF) == 0)
|
|
prefix = "Samlp2";
|
|
else if (strcmp((char*)href, LASSO_SOAP_ENV_HREF) == 0)
|
|
prefix = "Soap";
|
|
else if (strcmp((char*)href, LASSO_SOAP_BINDING_HREF) == 0)
|
|
prefix = "SoapBinding";
|
|
else if (strcmp((char*)href, LASSO_SOAP_BINDING_EXT_HREF) == 0)
|
|
prefix = "SoapBindingExt";
|
|
else if (strcmp((char*)href, LASSO_DISCO_HREF) == 0)
|
|
prefix = "Disco";
|
|
else if (strcmp((char*)href, LASSO_DS_HREF) == 0)
|
|
prefix = "Ds";
|
|
else if (strcmp((char*)href, LASSO_IS_HREF) == 0)
|
|
prefix = "Is";
|
|
else if (strcmp((char*)href, LASSO_SA_HREF) == 0)
|
|
prefix = "Sa";
|
|
else if (strcmp((char*)href, LASSO_WSSE_HREF) == 0)
|
|
prefix = "WsSec1";
|
|
else if (strcmp((char*)href, LASSO_WSSE1_HREF) == 0)
|
|
prefix = "WsSec1";
|
|
else if (strcmp((char*)href, LASSO_IDWSF2_DISCOVERY_HREF) == 0)
|
|
prefix = "IdWsf2Disco";
|
|
else if (strcmp((char*)href, LASSO_IDWSF2_SBF_HREF) == 0)
|
|
prefix = "IdWsf2Sbf";
|
|
else if (strcmp((char*)href, LASSO_IDWSF2_SB2_HREF) == 0)
|
|
prefix = "IdWsf2Sb2";
|
|
else if (strcmp((char*)href, LASSO_IDWSF2_UTIL_HREF) == 0)
|
|
prefix = "IdWsf2Util";
|
|
else if (strcmp((char*)href, LASSO_IDWSF2_SEC_HREF) == 0)
|
|
prefix = "IdWsf2Sec";
|
|
else if (strcmp((char*)href, LASSO_IDWSF2_IMS_HREF) == 0)
|
|
prefix = "IdWsf2Ims";
|
|
else if (strcmp((char*)href, LASSO_IDWSF2_IS_HREF) == 0)
|
|
prefix = "IdWsf2Is";
|
|
else if (strcmp((char*)href, LASSO_IDWSF2_PS_HREF) == 0)
|
|
prefix = "IdWsf2Ps";
|
|
else if (strcmp((char*)href, LASSO_IDWSF2_SUBS_HREF) == 0)
|
|
prefix = "IdWsf2Subs";
|
|
else if (strcmp((char*)href, LASSO_IDWSF2_SUBSREF_HREF) == 0)
|
|
prefix = "IdWsf2SubsRef";
|
|
else if (strcmp((char*)href, LASSO_WSA_HREF) == 0)
|
|
prefix = "WsAddr";
|
|
#if 0 /* Desactivate DGME lib special casing */
|
|
else if (strcmp((char*)href, "urn:dgme:msp:ed:2007-01") == 0)
|
|
/* FIXME: new namespaces should be possible to add from another library than lasso */
|
|
prefix = "DgmeMspEd";
|
|
#endif
|
|
else if ((tmp = lasso_get_prefix_for_idwsf2_dst_service_href((char*)href))
|
|
!= NULL) {
|
|
/* ID-WSF 2 Profile */
|
|
prefix = "IdWsf2DstRef";
|
|
lasso_release_string(tmp);
|
|
} else if ((tmp = lasso_get_prefix_for_dst_service_href((char*)href))
|
|
!= NULL) {
|
|
/* ID-WSF 1 Profile */
|
|
prefix = "Dst";
|
|
lasso_release_string(tmp);
|
|
}
|
|
|
|
if (prefix != NULL && strcmp(prefix, "Dst") == 0 && strcmp((char*)nodename, "Status") == 0)
|
|
prefix = "Utility";
|
|
else if (prefix != NULL && strcmp(prefix, "Disco") == 0 && strcmp((char*)nodename, "Status") == 0)
|
|
prefix = "Utility";
|
|
else if (prefix != NULL && strcmp(prefix, "Sa") == 0 && strcmp((char*)nodename, "Status") == 0)
|
|
prefix = "Utility";
|
|
|
|
return prefix;
|
|
}
|
|
|
|
/**
|
|
* _type_name_from_href_and_nodename:
|
|
* @href: the href part of a QName
|
|
* @nodename: the name part of a QName
|
|
*
|
|
* Return value: a typename string if one if found that exists, NULL otherwise.
|
|
*/
|
|
static char*
|
|
_type_name_from_href_and_nodename(char *href, char *nodename) {
|
|
const char *prefix = prefix_from_href_and_nodename(BAD_CAST (href), BAD_CAST (nodename));
|
|
char *typename = NULL;
|
|
|
|
if (!href || !nodename)
|
|
return NULL;
|
|
|
|
/* FIXME: hardcoded mappings */
|
|
if (strcmp(nodename, "SvcMD") == 0) {
|
|
typename = g_strdup("LassoIdWsf2DiscoSvcMetadata");
|
|
} else if (prefix != NULL && strcmp(prefix, "IdWsf2DstRef") == 0 && strcmp(nodename, "Status") == 0) {
|
|
typename = g_strdup("LassoIdWsf2UtilStatus");
|
|
} else if (prefix != NULL && strcmp(prefix, "WsSec1") == 0 && strcmp(nodename, "Security") == 0) {
|
|
typename = g_strdup("LassoWsSec1SecurityHeader");
|
|
} else if (prefix != NULL && strcmp(prefix, "Soap") == 0 && strcmp(nodename, "detail") == 0) {
|
|
typename = g_strdup("LassoSoapDetail");
|
|
} else {
|
|
/* first try with registered mappings */
|
|
const char *ctypename = lasso_registry_default_get_mapping(href, nodename, LASSO_LASSO_HREF);
|
|
if (ctypename) {
|
|
typename = g_strdup(ctypename);
|
|
}
|
|
/* finally try the default behaviour */
|
|
if (prefix != NULL && typename == NULL) {
|
|
typename = g_strdup_printf("Lasso%s%s", prefix, nodename);
|
|
}
|
|
}
|
|
|
|
/* Does it really exist ? */
|
|
if (typename && g_type_from_name (typename) == 0) {
|
|
lasso_release_string(typename);
|
|
}
|
|
|
|
return typename;
|
|
}
|
|
|
|
/**
|
|
* lasso_node_new_from_xmlNode:
|
|
* @node: an xmlNode
|
|
*
|
|
* Builds a new #LassoNode from an xmlNode.
|
|
*
|
|
* Return value: a new node
|
|
**/
|
|
LassoNode*
|
|
lasso_node_new_from_xmlNode(xmlNode *xmlnode)
|
|
{
|
|
char *typename = NULL;
|
|
xmlChar *xsitype = NULL;
|
|
LassoNode *node = NULL;
|
|
gboolean fromXsi = FALSE;
|
|
|
|
if (xmlnode == NULL || xmlnode->ns == NULL) {
|
|
message(G_LOG_LEVEL_CRITICAL, "Unable to build a LassoNode from a xmlNode");
|
|
return NULL;
|
|
}
|
|
|
|
xsitype = xmlGetNsProp(xmlnode, (xmlChar*)"type", (xmlChar*)LASSO_XSI_HREF);
|
|
if (xsitype) {
|
|
xmlChar *xmlPrefix, *separator;
|
|
xmlNsPtr xsiNs = NULL;
|
|
char *xsiNodeName = NULL;
|
|
|
|
/** Honor xsi:type */
|
|
xmlPrefix = (xmlChar*)xsitype;
|
|
separator = (xmlChar*)strchr((char*)xsitype, ':');
|
|
if (separator != NULL) {
|
|
xmlPrefix = (xmlChar*)g_strndup((char*)xmlPrefix, (size_t)(separator - xmlPrefix));
|
|
xsiNs = xmlSearchNs(NULL, xmlnode, xmlPrefix);
|
|
if (xsiNs != NULL) {
|
|
xsiNodeName = g_strdup((char*)(separator+1));
|
|
if (strcmp((char*)xsiNs->href, LASSO_LASSO_HREF) == 0) {
|
|
typename = g_strdup(xsiNodeName);
|
|
}
|
|
}
|
|
lasso_release(xmlPrefix);
|
|
}
|
|
if (! typename && xsiNs && xsiNodeName) {
|
|
typename = _type_name_from_href_and_nodename ((char*)xsiNs->href, xsiNodeName);
|
|
}
|
|
if (! typename && xsiNs) {
|
|
typename = _type_name_from_href_and_nodename ((char*)xsiNs->href, (char*)xmlnode->name);
|
|
}
|
|
lasso_release_xml_string(xsitype);
|
|
if (xsiNodeName)
|
|
lasso_release_string(xsiNodeName);
|
|
if (typename)
|
|
fromXsi = TRUE;
|
|
}
|
|
|
|
if (typename == NULL) {
|
|
typename = _type_name_from_href_and_nodename ((char*)xmlnode->ns->href, (char*)xmlnode->name);
|
|
}
|
|
|
|
if (typename) {
|
|
node = lasso_node_new_from_xmlNode_with_type(xmlnode, typename);
|
|
}
|
|
if (! node) {
|
|
goto cleanup;
|
|
}
|
|
if (! fromXsi) {
|
|
/* if the typename was not obtained via xsi:type but through mapping of the element
|
|
* name then keep the element name */
|
|
if (LASSO_NODE_GET_CLASS(node)->node_data && LASSO_NODE_GET_CLASS(node)->node_data->node_name && g_strcmp0((char*)xmlnode->name,
|
|
LASSO_NODE_GET_CLASS(node)->node_data->node_name) != 0) {
|
|
lasso_node_set_custom_nodename(node, (char*)xmlnode->name);
|
|
}
|
|
|
|
if (xmlnode->ns && (LASSO_NODE_GET_CLASS(node)->node_data == NULL ||
|
|
LASSO_NODE_GET_CLASS(node)->node_data->ns == NULL ||
|
|
g_strcmp0((char*)xmlnode->ns->href,
|
|
(char*)LASSO_NODE_GET_CLASS(node)->node_data->ns->href)
|
|
!= 0)) {
|
|
lasso_node_set_custom_namespace(node, (char*)xmlnode->ns->prefix,
|
|
(char*)xmlnode->ns->href);
|
|
}
|
|
|
|
|
|
}
|
|
cleanup:
|
|
lasso_release(typename);
|
|
|
|
return node;
|
|
}
|
|
|
|
|
|
static LassoNode*
|
|
lasso_node_new_from_xmlNode_with_type(xmlNode *xmlnode, char *typename)
|
|
{
|
|
GType gtype;
|
|
LassoNode *node;
|
|
int rc;
|
|
|
|
if (typename == NULL)
|
|
return lasso_node_new_from_xmlNode(xmlnode); /* will auto-detect */
|
|
|
|
gtype = g_type_from_name(typename);
|
|
if (gtype == 0)
|
|
return NULL;
|
|
|
|
|
|
node = g_object_new(gtype, NULL);
|
|
if (lasso_flag_memory_debug == TRUE) {
|
|
fprintf(stderr, "allocation of %s (for xmlNode %p) : %p\n", g_type_name(gtype), xmlnode, node);
|
|
}
|
|
rc = lasso_node_init_from_xml(node, xmlnode);
|
|
if (rc) {
|
|
lasso_node_destroy(node);
|
|
return NULL;
|
|
}
|
|
|
|
return node;
|
|
}
|
|
|
|
static gboolean
|
|
is_base64(const char *message)
|
|
{
|
|
const char *c;
|
|
|
|
c = message;
|
|
while (*c != 0 && (isalnum((int)*c) || *c == '+' || *c == '/' || *c == '\n' || *c == '\r')) c++;
|
|
while (*c == '=' || *c == '\n' || *c == '\r') c++; /* trailing = */
|
|
|
|
if (*c == 0)
|
|
return TRUE;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
/**
|
|
* lasso_node_init_from_message_with_format:
|
|
* @node: a #LassoNode (or derived class)
|
|
* @message: a Liberty message
|
|
* @constraint: LASSO_MESSAGE_FORMAT_UNKNOWN or the format the message must be in
|
|
* @doc_out: a pointer to store the resulting #xmlDoc structure
|
|
* @node_out: a pointer to store the resulting content #xmlNode
|
|
*
|
|
* Parses @message and initialiazes @node fields with data from it. Message type may be base64,
|
|
* SOAP, XML or query string, correct type is found automatically if contraint is
|
|
* LASSO_MESSAGE_FORMAT_UNKNOWN or is limited to the value given.
|
|
* If the format is one of LASSO_MESSAGE_FORMAT_XML or LASSO_MESSAGE_FORMAT_XML or
|
|
* LASSO_MESSAGE_FORMAT_BASE64 the resulting #xmlDoc and #xmlNode of the message can be retrieved.
|
|
*
|
|
* Return value: a #LassoMessageFormat value.
|
|
**/
|
|
LassoMessageFormat
|
|
lasso_node_init_from_message_with_format(LassoNode *node, const char *message, LassoMessageFormat constraint, xmlDoc **doc_out, xmlNode **root_out)
|
|
{
|
|
char *msg = NULL;
|
|
gboolean b64 = FALSE;
|
|
LassoMessageFormat rc = LASSO_MESSAGE_FORMAT_ERROR;
|
|
xmlDoc *doc = NULL;
|
|
xmlNode *root = NULL;
|
|
gboolean any = constraint == LASSO_MESSAGE_FORMAT_UNKNOWN;
|
|
|
|
msg = (char*)message;
|
|
|
|
/* BASE64 case */
|
|
if (any || constraint == LASSO_MESSAGE_FORMAT_BASE64) {
|
|
if (message[0] != 0 && is_base64(message)) {
|
|
int rc;
|
|
|
|
msg = g_malloc(strlen(message));
|
|
rc = xmlSecBase64Decode((xmlChar*)message, (xmlChar*)msg, strlen(message));
|
|
if (rc >= 0) {
|
|
b64 = TRUE;
|
|
} else {
|
|
g_free(msg);
|
|
msg = (char*)message;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* XML case */
|
|
if (any || constraint == LASSO_MESSAGE_FORMAT_XML ||
|
|
constraint == LASSO_MESSAGE_FORMAT_BASE64 ||
|
|
constraint == LASSO_MESSAGE_FORMAT_SOAP) {
|
|
if (strchr(msg, '<')) {
|
|
doc = lasso_xml_parse_memory(msg, strlen(msg));
|
|
if (doc == NULL) {
|
|
rc = LASSO_MESSAGE_FORMAT_UNKNOWN;
|
|
goto cleanup;
|
|
}
|
|
root = xmlDocGetRootElement(doc);
|
|
|
|
if (any || constraint == LASSO_MESSAGE_FORMAT_SOAP) {
|
|
gboolean is_soap = FALSE;
|
|
|
|
is_soap = lasso_xml_is_soap(root);
|
|
if (is_soap) {
|
|
root = lasso_xml_get_soap_content(root);
|
|
}
|
|
rc = lasso_node_init_from_xml(node, root);
|
|
if (rc != 0) {
|
|
rc = LASSO_MESSAGE_FORMAT_XSCHEMA_ERROR;
|
|
goto cleanup;
|
|
|
|
}
|
|
if (is_soap) {
|
|
rc = LASSO_MESSAGE_FORMAT_SOAP;
|
|
goto cleanup;
|
|
}
|
|
if (b64) {
|
|
g_free(msg);
|
|
rc = LASSO_MESSAGE_FORMAT_BASE64;
|
|
goto cleanup;
|
|
}
|
|
rc = LASSO_MESSAGE_FORMAT_XML;
|
|
goto cleanup;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* HTTP query CASE */
|
|
if (any || constraint == LASSO_MESSAGE_FORMAT_QUERY) {
|
|
if (strchr(msg, '&') || strchr(msg, '=')) {
|
|
/* XXX: detect SAML artifact messages to return a different status code ? */
|
|
if (lasso_node_init_from_query(node, msg) == FALSE) {
|
|
goto cleanup;
|
|
}
|
|
rc = LASSO_MESSAGE_FORMAT_QUERY;
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
cleanup:
|
|
if (doc_out) {
|
|
*doc_out = doc;
|
|
if (root_out) {
|
|
*root_out = root;
|
|
}
|
|
} else {
|
|
lasso_release_doc(doc);
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* lasso_node_init_from_message:
|
|
* @node: a #LassoNode (or derived class)
|
|
* @message: a Liberty message
|
|
*
|
|
* Parses @message and initialiazes @node fields with data from it. Message
|
|
* type may be base64, SOAP, XML or query string, correct type is found
|
|
* automatically.
|
|
*
|
|
* Return value: a #LassoMessageFormat value.
|
|
**/
|
|
LassoMessageFormat
|
|
lasso_node_init_from_message(LassoNode *node, const char *message)
|
|
{
|
|
return lasso_node_init_from_message_with_format(node, message, LASSO_MESSAGE_FORMAT_UNKNOWN, NULL, NULL);
|
|
}
|
|
|
|
/**
|
|
* lasso_node_class_add_snippets:
|
|
* @klass: object class
|
|
* @snippets: array of XmlSnippet (NULL terminated)
|
|
**/
|
|
void
|
|
lasso_node_class_add_snippets(LassoNodeClass *klass, struct XmlSnippet *snippets)
|
|
{
|
|
klass->node_data->snippets = snippets;
|
|
}
|
|
|
|
|
|
/**
|
|
* lasso_node_class_add_query_snippets:
|
|
* @klass: object class
|
|
* @snippets: array of QuerySnippet (NULL terminated)
|
|
**/
|
|
void
|
|
lasso_node_class_add_query_snippets(LassoNodeClass *klass, struct QuerySnippet *snippets)
|
|
{
|
|
klass->node_data->query_snippets = snippets;
|
|
}
|
|
|
|
/**
|
|
* lasso_node_class_set_nodename:
|
|
* @klass: object class
|
|
* @name: name for element node
|
|
**/
|
|
void
|
|
lasso_node_class_set_nodename(LassoNodeClass *klass, char *name)
|
|
{
|
|
if (klass->node_data->node_name)
|
|
g_free(klass->node_data->node_name);
|
|
klass->node_data->node_name = g_strdup(name);
|
|
}
|
|
|
|
|
|
/**
|
|
* lasso_node_class_set_ns:
|
|
* @klass: object class
|
|
* @href: namespace uri
|
|
* @prefix: namespace prefix
|
|
**/
|
|
void
|
|
lasso_node_class_set_ns(LassoNodeClass *klass, char *href, char *prefix)
|
|
{
|
|
if (klass->node_data->ns)
|
|
xmlFreeNs(klass->node_data->ns);
|
|
klass->node_data->ns = xmlNewNs(NULL, (xmlChar*)href, (xmlChar*)prefix);
|
|
}
|
|
|
|
|
|
static void
|
|
snippet_dump_any(gchar *key, gchar *value, xmlNode *xmlnode)
|
|
{
|
|
xmlSetProp(xmlnode, (xmlChar*)key, (xmlChar*)value);
|
|
}
|
|
|
|
|
|
static void
|
|
lasso_node_build_xmlNode_from_snippets(LassoNode *node, xmlNode *xmlnode,
|
|
struct XmlSnippet *snippets, gboolean lasso_dump)
|
|
{
|
|
struct XmlSnippet *snippet;
|
|
SnippetType type;
|
|
xmlNode *t;
|
|
xmlNs *xmlns;
|
|
GList *elem;
|
|
struct XmlSnippet *snippet_any_attribute = NULL;
|
|
|
|
for (snippet = snippets; snippet && snippet->name; snippet++) {
|
|
void *value = G_STRUCT_MEMBER(void*, node, snippet->offset);
|
|
char *str = value;
|
|
type = snippet->type & 0xff;
|
|
|
|
if (lasso_dump == FALSE && snippet->type & SNIPPET_LASSO_DUMP)
|
|
continue;
|
|
|
|
if (type == SNIPPET_ATTRIBUTE && snippet->type & SNIPPET_ANY) {
|
|
snippet_any_attribute = snippet;
|
|
continue;
|
|
}
|
|
if (value == NULL && (!(snippet->type & SNIPPET_BOOLEAN ||
|
|
snippet->type & SNIPPET_INTEGER) ||
|
|
snippet->type & SNIPPET_OPTIONAL))
|
|
continue;
|
|
|
|
if (snippet->type & SNIPPET_OPTIONAL_NEG && GPOINTER_TO_INT(value) == -1)
|
|
continue;
|
|
|
|
/* XXX: not sure it is 64-bits clean */
|
|
if (snippet->type & SNIPPET_BOOLEAN)
|
|
str = GPOINTER_TO_INT(value) ? "true" : "false";
|
|
if (snippet->type & SNIPPET_INTEGER)
|
|
str = g_strdup_printf("%d", GPOINTER_TO_INT(value));
|
|
|
|
switch (type) {
|
|
case SNIPPET_ATTRIBUTE:
|
|
if (snippet->ns_name) {
|
|
xmlNsPtr ns;
|
|
|
|
ns = xmlNewNs(xmlnode, (xmlChar*)snippet->ns_uri, (xmlChar*)snippet->ns_name);
|
|
xmlSetNsProp(xmlnode, ns, (xmlChar*)snippet->name, (xmlChar*)str);
|
|
} else {
|
|
xmlSetProp(xmlnode, (xmlChar*)snippet->name, (xmlChar*)str);
|
|
}
|
|
break;
|
|
case SNIPPET_TEXT_CHILD:
|
|
xmlAddChild(xmlnode, xmlNewText((xmlChar*)str));
|
|
break;
|
|
case SNIPPET_NODE:
|
|
{
|
|
xmlNode *t2;
|
|
t2 = lasso_node_get_xmlNode(LASSO_NODE(value), lasso_dump);
|
|
if (snippet->class_name)
|
|
xmlNodeSetName(t2, (xmlChar*)snippet->name);
|
|
xmlAddChild(xmlnode, t2);
|
|
} break;
|
|
case SNIPPET_CONTENT:
|
|
xmlNewTextChild(xmlnode, NULL,
|
|
(xmlChar*)snippet->name, (xmlChar*)str);
|
|
break;
|
|
case SNIPPET_NAME_IDENTIFIER:
|
|
xmlns = xmlNewNs(NULL, (xmlChar*)LASSO_LIB_HREF,
|
|
(xmlChar*)LASSO_LIB_PREFIX);
|
|
t = xmlAddChild(xmlnode, lasso_node_get_xmlNode(
|
|
LASSO_NODE(value), lasso_dump));
|
|
xmlNodeSetName(t, (xmlChar*)snippet->name);
|
|
xmlSetNs(t, xmlns);
|
|
break;
|
|
case SNIPPET_NODE_IN_CHILD:
|
|
t = xmlNewTextChild(xmlnode, NULL, (xmlChar*)snippet->name, NULL);
|
|
xmlAddChild(t, lasso_node_get_xmlNode(
|
|
LASSO_NODE(value), lasso_dump));
|
|
break;
|
|
case SNIPPET_LIST_NODES:
|
|
elem = (GList *)value;
|
|
while (elem) {
|
|
xmlNode *subnode = lasso_node_get_xmlNode(
|
|
LASSO_NODE(elem->data), lasso_dump);
|
|
if (subnode) {
|
|
if (snippet->name && snippet->name[0]) {
|
|
xmlNodeSetName(subnode,
|
|
(xmlChar*)snippet->name);
|
|
}
|
|
xmlAddChild(xmlnode, subnode);
|
|
}
|
|
elem = g_list_next(elem);
|
|
}
|
|
break;
|
|
case SNIPPET_LIST_CONTENT:
|
|
/* sequence of simple elements (no children,
|
|
* no attrs, just content) */
|
|
elem = (GList *)value;
|
|
while (elem) {
|
|
xmlNs *content_ns = NULL;
|
|
if (snippet->ns_name) {
|
|
content_ns = xmlNewNs(xmlnode,
|
|
(const xmlChar*)snippet->ns_uri,
|
|
(const xmlChar*)snippet->ns_name);
|
|
}
|
|
xmlNewTextChild(xmlnode, content_ns,
|
|
(xmlChar*)snippet->name,
|
|
(xmlChar*)(elem->data));
|
|
elem = g_list_next(elem);
|
|
}
|
|
break;
|
|
case SNIPPET_LIST_XMLNODES:
|
|
case SNIPPET_EXTENSION:
|
|
elem = (GList *)value;
|
|
while (elem) {
|
|
xmlAddChild(xmlnode, xmlCopyNode(elem->data, 1));
|
|
elem = g_list_next(elem);
|
|
}
|
|
break;
|
|
case SNIPPET_XMLNODE:
|
|
xmlAddChild(xmlnode, xmlCopyNode((xmlNode *)value, 1));
|
|
break;
|
|
case SNIPPET_SIGNATURE:
|
|
lasso_node_add_signature_template(node, xmlnode, snippet);
|
|
break;
|
|
case SNIPPET_INTEGER:
|
|
case SNIPPET_BOOLEAN:
|
|
case SNIPPET_LASSO_DUMP:
|
|
case SNIPPET_OPTIONAL:
|
|
case SNIPPET_OPTIONAL_NEG:
|
|
case SNIPPET_ALLOW_TEXT:
|
|
case SNIPPET_ANY:
|
|
case SNIPPET_KEEP_XMLNODE:
|
|
g_assert_not_reached();
|
|
}
|
|
if (snippet->type & SNIPPET_INTEGER)
|
|
g_free(str);
|
|
}
|
|
|
|
if (snippet_any_attribute) {
|
|
GHashTable *value = G_STRUCT_MEMBER(GHashTable*, node,
|
|
snippet_any_attribute->offset);
|
|
if (value) {
|
|
g_hash_table_foreach(value, (GHFunc)snippet_dump_any, xmlnode);
|
|
}
|
|
}
|
|
}
|
|
|
|
static
|
|
void lasso_node_add_signature_template(LassoNode *node, xmlNode *xmlnode,
|
|
struct XmlSnippet *snippet_signature)
|
|
{
|
|
LassoNodeClass *klass = LASSO_NODE_GET_CLASS(node);
|
|
LassoSignatureType sign_type;
|
|
LassoSignatureMethod sign_method;
|
|
xmlNode *signature = NULL, *reference, *key_info, *t;
|
|
char *uri;
|
|
char *id;
|
|
|
|
while (klass && LASSO_IS_NODE_CLASS(klass) && klass->node_data) {
|
|
if (klass->node_data->sign_type_offset)
|
|
break;
|
|
klass = g_type_class_peek_parent(klass);
|
|
}
|
|
|
|
if (klass->node_data->sign_type_offset == 0)
|
|
return;
|
|
|
|
sign_type = G_STRUCT_MEMBER(
|
|
LassoSignatureType, node,
|
|
klass->node_data->sign_type_offset);
|
|
sign_method = G_STRUCT_MEMBER(
|
|
LassoSignatureMethod, node,
|
|
klass->node_data->sign_method_offset);
|
|
|
|
if (sign_type == LASSO_SIGNATURE_TYPE_NONE)
|
|
return;
|
|
|
|
if (sign_method == LASSO_SIGNATURE_METHOD_RSA_SHA1) {
|
|
signature = xmlSecTmplSignatureCreate(NULL,
|
|
xmlSecTransformExclC14NId,
|
|
xmlSecTransformRsaSha1Id, NULL);
|
|
} else {
|
|
signature = xmlSecTmplSignatureCreate(NULL,
|
|
xmlSecTransformExclC14NId,
|
|
xmlSecTransformDsaSha1Id, NULL);
|
|
}
|
|
/* XXX: get out if signature == NULL ? */
|
|
xmlAddChild(xmlnode, signature);
|
|
|
|
id = G_STRUCT_MEMBER(char*, node, snippet_signature->offset);
|
|
uri = g_strdup_printf("#%s", id);
|
|
reference = xmlSecTmplSignatureAddReference(signature,
|
|
xmlSecTransformSha1Id, NULL, (xmlChar*)uri, NULL);
|
|
g_free(uri);
|
|
|
|
/* add enveloped transform */
|
|
xmlSecTmplReferenceAddTransform(reference, xmlSecTransformEnvelopedId);
|
|
/* add exclusive C14N transform */
|
|
xmlSecTmplReferenceAddTransform(reference, xmlSecTransformExclC14NId);
|
|
|
|
if (sign_type == LASSO_SIGNATURE_TYPE_WITHX509) {
|
|
/* add <dsig:KeyInfo/> */
|
|
key_info = xmlSecTmplSignatureEnsureKeyInfo(signature, NULL);
|
|
t = xmlSecTmplKeyInfoAddX509Data(key_info);
|
|
}
|
|
}
|
|
|
|
static struct XmlSnippet*
|
|
find_xml_snippet_by_name(LassoNode *node, char *name)
|
|
{
|
|
LassoNodeClass *class;
|
|
struct XmlSnippet *snippet;
|
|
|
|
class = LASSO_NODE_GET_CLASS(node);
|
|
while (class && LASSO_IS_NODE_CLASS(class) && class->node_data) {
|
|
for (snippet = class->node_data->snippets;
|
|
snippet && snippet->name && strcmp(snippet->name, name) != 0;
|
|
snippet++) ;
|
|
if (snippet && snippet->name)
|
|
return snippet;
|
|
class = g_type_class_peek_parent(class);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static gboolean
|
|
find_path(LassoNode *node, char *path, LassoNode **value_node, struct XmlSnippet **snippet)
|
|
{
|
|
char *s, *t;
|
|
struct XmlSnippet *tsnippet = NULL;
|
|
LassoNode *tnode = node;
|
|
|
|
s = path;
|
|
while (s-1) {
|
|
t = strchr(s, '/');
|
|
if (t) *t = 0;
|
|
tsnippet = find_xml_snippet_by_name(tnode, s);
|
|
if (t) {
|
|
tnode = G_STRUCT_MEMBER(LassoNode*, tnode, tsnippet->offset);
|
|
if (tnode == NULL)
|
|
return FALSE;
|
|
}
|
|
s = t+1;
|
|
}
|
|
|
|
if (tsnippet == NULL)
|
|
return FALSE;
|
|
|
|
*snippet = tsnippet;
|
|
*value_node = tnode;
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
static char*
|
|
get_value_by_path(LassoNode *node, char *path, struct XmlSnippet *xml_snippet)
|
|
{
|
|
struct XmlSnippet *snippet;
|
|
LassoNode *value_node;
|
|
|
|
if (find_path(node, path, &value_node, &snippet) != TRUE)
|
|
return NULL;
|
|
|
|
*xml_snippet = *snippet;
|
|
|
|
if (snippet->type & SNIPPET_BOOLEAN) {
|
|
gboolean v = G_STRUCT_MEMBER(gboolean, value_node, snippet->offset);
|
|
return v ? g_strdup("true") : g_strdup("false");
|
|
} else if (snippet->type & SNIPPET_INTEGER) {
|
|
int v = G_STRUCT_MEMBER(int, value_node, snippet->offset);
|
|
return g_strdup_printf("%d", v);
|
|
} else if (snippet->type == SNIPPET_NODE) {
|
|
LassoNode *value = G_STRUCT_MEMBER(LassoNode*, value_node, snippet->offset);
|
|
return lasso_node_build_query(value);
|
|
} else if (snippet->type == SNIPPET_EXTENSION) {
|
|
/* convert all of the <lib:Extension> into a string, already
|
|
* escaped for URI usage */
|
|
GList *value = G_STRUCT_MEMBER(GList*, value_node, snippet->offset);
|
|
xmlChar *s, *s2;
|
|
GString *result = g_string_new("");
|
|
while (value) {
|
|
xmlNode *t = value->data;
|
|
xmlNode *c;
|
|
|
|
/* attributes */
|
|
#if 0
|
|
xmlAttr *a;
|
|
for (a = t->properties; a; a = a->next) {
|
|
if (result->len)
|
|
g_string_append(result, "&");
|
|
s = xmlGetProp(t, a->name);
|
|
g_string_append(result, a->name);
|
|
g_string_append(result, "=");
|
|
s2 = xmlURIEscapeStr(s, NULL);
|
|
g_string_append(result, s2);
|
|
xmlFree(s2);
|
|
xmlFree(s);
|
|
}
|
|
#endif
|
|
|
|
/* children (only simple ones and 1-level deep) */
|
|
for (c = t->children; c; c = c->next) {
|
|
if (c->type != XML_ELEMENT_NODE)
|
|
continue;
|
|
if (c->children->type != XML_TEXT_NODE)
|
|
continue;
|
|
if (c->properties != NULL)
|
|
continue;
|
|
if (result->len)
|
|
g_string_append(result, "&");
|
|
g_string_append(result, (char*)c->name);
|
|
g_string_append(result, "=");
|
|
s = xmlNodeGetContent(c);
|
|
s2 = xmlURIEscapeStr(s, NULL);
|
|
g_string_append(result, (char*)s2);
|
|
xmlFree(s2);
|
|
xmlFree(s);
|
|
}
|
|
|
|
value = g_list_next(value);
|
|
}
|
|
if (result->len == 0) {
|
|
g_string_free(result, TRUE);
|
|
return NULL;
|
|
}
|
|
return g_string_free(result, FALSE);
|
|
} else if (snippet->type == SNIPPET_LIST_CONTENT) {
|
|
/* not clear in spec; concat values with spaces */
|
|
GList *value = G_STRUCT_MEMBER(GList*, value_node, snippet->offset);
|
|
GString *result = g_string_new("");
|
|
while (value) {
|
|
result = g_string_append(result, (char*)value->data);
|
|
if (value->next)
|
|
result = g_string_append(result, " ");
|
|
value = value->next;
|
|
}
|
|
if (result->len == 0) {
|
|
g_string_free(result, TRUE);
|
|
return NULL;
|
|
}
|
|
return g_string_free(result, FALSE);
|
|
} else {
|
|
char *value = G_STRUCT_MEMBER(char*, value_node, snippet->offset);
|
|
if (value == NULL) return NULL;
|
|
return g_strdup(value);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static gboolean
|
|
set_value_at_path(LassoNode *node, char *path, char *query_value)
|
|
{
|
|
struct XmlSnippet *snippet;
|
|
LassoNode *value_node;
|
|
void *value;
|
|
|
|
if (find_path(node, path, &value_node, &snippet) != TRUE)
|
|
return FALSE;
|
|
|
|
value = G_STRUCT_MEMBER_P(value_node, snippet->offset);
|
|
|
|
if (snippet->type & SNIPPET_INTEGER) {
|
|
int val = atoi(query_value);
|
|
(*(int*)value) = val;
|
|
} else if (snippet->type & SNIPPET_BOOLEAN) {
|
|
int val = (strcmp(query_value, "true") == 0);
|
|
(*(int*)value) = val;
|
|
} else if (snippet->type == SNIPPET_NODE) {
|
|
LassoNode *v = *(LassoNode**)value;
|
|
if (v == NULL) {
|
|
message(G_LOG_LEVEL_CRITICAL, "building node from query; unknown subnode");
|
|
g_assert_not_reached();
|
|
}
|
|
LASSO_NODE_GET_CLASS(v)->init_from_query(v, &query_value);
|
|
} else if (snippet->type == SNIPPET_LIST_CONTENT) {
|
|
char **elems = g_strsplit(query_value, " ", 0);
|
|
int i;
|
|
GList *l = NULL;
|
|
for (i = 0; elems[i]; i++) {
|
|
l = g_list_append(l, g_strdup(elems[i]));
|
|
}
|
|
g_strfreev(elems);
|
|
(*(GList**)value) = l;
|
|
} else {
|
|
(*(char**)value) = g_strdup(query_value);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
gchar*
|
|
lasso_node_build_query_from_snippets(LassoNode *node)
|
|
{
|
|
int i;
|
|
char path[100];
|
|
char *v;
|
|
GString *s;
|
|
xmlChar *t;
|
|
LassoNodeClass *class = LASSO_NODE_GET_CLASS(node);
|
|
struct QuerySnippet *query_snippets = NULL;
|
|
struct XmlSnippet xml_snippet;
|
|
|
|
while (class && LASSO_IS_NODE_CLASS(class) && class->node_data) {
|
|
if (class->node_data && class->node_data->query_snippets) {
|
|
query_snippets = class->node_data->query_snippets;
|
|
break;
|
|
}
|
|
class = g_type_class_peek_parent(class);
|
|
}
|
|
if (query_snippets == NULL)
|
|
return NULL;
|
|
|
|
s = g_string_sized_new(2000);
|
|
|
|
for (i=0; query_snippets[i].path; i++) {
|
|
g_strlcpy(path, query_snippets[i].path, 100);
|
|
v = get_value_by_path(node, path, &xml_snippet);
|
|
if (v && xml_snippet.type == SNIPPET_EXTENSION) {
|
|
if (s->len)
|
|
g_string_append(s, "&");
|
|
g_string_append(s, v);
|
|
g_free(v);
|
|
continue;
|
|
}
|
|
if (v) {
|
|
char *field_name = query_snippets[i].field_name;
|
|
if (field_name == NULL)
|
|
field_name = query_snippets[i].path;
|
|
if (s->len)
|
|
g_string_append(s, "&");
|
|
g_string_append(s, field_name);
|
|
g_string_append(s, "=");
|
|
t = xmlURIEscapeStr((xmlChar*)v, NULL);
|
|
g_string_append(s, (char*)t);
|
|
xmlFree(t);
|
|
}
|
|
if (v)
|
|
g_free(v);
|
|
}
|
|
|
|
return g_string_free(s, FALSE);
|
|
}
|
|
|
|
|
|
gboolean
|
|
lasso_node_init_from_query_fields(LassoNode *node, char **query_fields)
|
|
{
|
|
int i, j;
|
|
char *field, *t;
|
|
LassoNodeClass *class = LASSO_NODE_GET_CLASS(node);
|
|
struct QuerySnippet *query_snippets = NULL;
|
|
gboolean has_extension = FALSE;
|
|
|
|
while (class && LASSO_IS_NODE_CLASS(class) && class->node_data) {
|
|
if (class->node_data && class->node_data->query_snippets) {
|
|
query_snippets = class->node_data->query_snippets;
|
|
break;
|
|
}
|
|
class = g_type_class_peek_parent(class);
|
|
}
|
|
if (query_snippets == NULL)
|
|
return FALSE;
|
|
|
|
for (i = 0; (field = query_fields[i]); i++) {
|
|
t = strchr(field, '=');
|
|
if (t == NULL)
|
|
continue;
|
|
*t = 0;
|
|
|
|
for (j=0; query_snippets[j].path; j++) {
|
|
char *field_name = query_snippets[j].field_name;
|
|
char path[100];
|
|
|
|
g_strlcpy(path, query_snippets[j].path, 100);
|
|
|
|
if (field_name == NULL)
|
|
field_name = query_snippets[j].path;
|
|
if (strcmp(field_name, "Extension") == 0) {
|
|
has_extension = TRUE;
|
|
continue;
|
|
}
|
|
if (strcmp(field, field_name) != 0)
|
|
continue;
|
|
set_value_at_path(node, path, t+1);
|
|
break;
|
|
}
|
|
if (query_snippets[j].path == NULL && has_extension &&
|
|
strcmp(field, "SigAlg") != 0 && strcmp(field, "Signature") != 0) {
|
|
/* got to the end without finding anything; and has
|
|
* Extension; build it */
|
|
struct XmlSnippet *extension_snippet;
|
|
LassoNode *value_node;
|
|
GList **value;
|
|
xmlNode *xmlnode, *xmlchild;
|
|
if (find_path(node, "Extension", &value_node, &extension_snippet) == TRUE) {
|
|
value = G_STRUCT_MEMBER_P(value_node, extension_snippet->offset);
|
|
if (*value) {
|
|
xmlnode = (*value)->data;
|
|
} else {
|
|
xmlnode = xmlNewNode(xmlNewNs(NULL,
|
|
(xmlChar*)LASSO_LIB_HREF,
|
|
(xmlChar*)LASSO_LIB_PREFIX),
|
|
(xmlChar*)"Extension");
|
|
}
|
|
xmlchild = xmlNewNode(NULL, (xmlChar*)field);
|
|
xmlAddChild(xmlchild, xmlNewText((xmlChar*)t+1));
|
|
xmlAddChild(xmlnode, xmlchild);
|
|
if (! *value)
|
|
*value = g_list_append(*value, xmlnode);
|
|
}
|
|
}
|
|
*t = '=';
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
lasso_node_init_from_saml2_query_fields(LassoNode *node, char **query_fields, G_GNUC_UNUSED char **relay_state)
|
|
{
|
|
int i;
|
|
char *field, *t;
|
|
char *req = NULL;
|
|
char *enc = NULL;
|
|
gboolean rc;
|
|
|
|
for (i=0; (field=query_fields[i]); i++) {
|
|
t = strchr(field, '=');
|
|
if (t == NULL)
|
|
continue;
|
|
*t = 0;
|
|
if (strcmp(field, LASSO_SAML2_FIELD_ENCODING) == 0) {
|
|
enc = t+1;
|
|
continue;
|
|
}
|
|
if (strcmp(field, LASSO_SAML2_FIELD_REQUEST) == 0 || strcmp(field, LASSO_SAML2_FIELD_RESPONSE) == 0) {
|
|
req = t+1;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (enc && strcmp(enc, LASSO_SAML2_DEFLATE_ENCODING) != 0) {
|
|
/* unknown encoding */
|
|
message(G_LOG_LEVEL_CRITICAL, "Unknown URL encoding: %s", enc);
|
|
return FALSE;
|
|
}
|
|
|
|
if (req == NULL) {
|
|
return FALSE;
|
|
}
|
|
|
|
rc = lasso_node_init_from_deflated_query_part(node, req);
|
|
if (rc == FALSE) {
|
|
return rc;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
xmlDeclareNs(xmlNode *root_node, xmlNode *node)
|
|
{
|
|
xmlNs *ns, *ns2;
|
|
xmlNode *t;
|
|
|
|
if (strcmp((char*)node->name, "Signature") == 0)
|
|
return;
|
|
|
|
for (ns = node->nsDef; ns; ns = ns->next) {
|
|
if (ns->prefix && strcmp((char*)ns->prefix, "xsi") != 0) {
|
|
ns2 = xmlNewNs(root_node, ns->href, ns->prefix);
|
|
}
|
|
}
|
|
for (t = node->children; t; t = t->next) {
|
|
if (t->type == XML_ELEMENT_NODE) {
|
|
xmlDeclareNs(root_node, t);
|
|
}
|
|
}
|
|
}
|
|
|
|
static inline int
|
|
sameNs(xmlNs *ns1, xmlNs *ns2)
|
|
{
|
|
/* this checks ns->prefix instead of ns->href so it is possible to
|
|
* merge down to an earlier version of liberty namespace
|
|
*/
|
|
return (ns1 == NULL && ns2 == NULL) || (
|
|
ns1 && ns2 && ns1->prefix && ns2->prefix &&
|
|
strcmp((char*)ns1->prefix, (char*)ns2->prefix) == 0 &&
|
|
strcmp((char*)ns1->href, (char*)ns2->href) == 0);
|
|
}
|
|
|
|
static void
|
|
xmlPropUseNsDef(xmlNs *ns, xmlNode *node)
|
|
{
|
|
xmlAttr *attr;
|
|
|
|
for (attr = node->properties; attr; attr = attr->next) {
|
|
if (sameNs(ns, attr->ns)) {
|
|
attr->ns = ns;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
xmlUseNsDef(xmlNs *ns, xmlNode *node)
|
|
{
|
|
xmlNode *t;
|
|
xmlNs *ns2;
|
|
xmlNs *ns3 = NULL;
|
|
|
|
xmlPropUseNsDef(ns, node);
|
|
if (sameNs(ns, node->ns)) {
|
|
node->ns = ns;
|
|
}
|
|
|
|
for (t = node->children; t; t = t->next) {
|
|
if (t->type == XML_ELEMENT_NODE)
|
|
xmlUseNsDef(ns, t);
|
|
}
|
|
|
|
if (sameNs(node->nsDef, ns)) {
|
|
ns3 = node->nsDef;
|
|
node->nsDef = node->nsDef->next;
|
|
xmlFreeNs(ns3);
|
|
} else if (node->nsDef) {
|
|
for (ns2 = node->nsDef; ns2->next; ns2 = ns2->next) {
|
|
if (sameNs(ns2->next, ns)) {
|
|
ns3 = ns2->next;
|
|
ns2->next = ns2->next->next;
|
|
xmlFreeNs(ns3);
|
|
if (ns2->next == NULL)
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* xmlCleanNs
|
|
* @root_node: the root #xmlNode where to start the cleaning.
|
|
*
|
|
* xmlCleanNs removes duplicate xml namespace declarations and merge them on
|
|
* the @root_node.
|
|
**/
|
|
void
|
|
xmlCleanNs(xmlNode *root_node)
|
|
{
|
|
xmlNs *ns;
|
|
xmlNode *t;
|
|
|
|
for (t = root_node->children; t; t = t->next)
|
|
if (t->type == XML_ELEMENT_NODE)
|
|
xmlDeclareNs(root_node, t);
|
|
|
|
for (ns = root_node->nsDef; ns; ns = ns->next) {
|
|
for (t = root_node->children; t; t = t->next)
|
|
if (t->type == XML_ELEMENT_NODE)
|
|
xmlUseNsDef(ns, t);
|
|
}
|
|
}
|
|
|
|
void
|
|
xml_insure_namespace(xmlNode *xmlnode, xmlNs *ns, gboolean force, gchar *ns_href, gchar *ns_prefix)
|
|
{
|
|
xmlNode *t = xmlnode->children;
|
|
|
|
if (ns == NULL) {
|
|
for (ns = xmlnode->nsDef; ns; ns = ns->next) {
|
|
if (strcmp((gchar*)ns->href, ns_href) == 0) {
|
|
break;
|
|
}
|
|
}
|
|
if (ns == NULL) {
|
|
ns = xmlNewNs(xmlnode, (xmlChar*)ns_href, (xmlChar*)ns_prefix);
|
|
}
|
|
}
|
|
|
|
xmlSetNs(xmlnode, ns);
|
|
while (t) {
|
|
if (t->type == XML_ELEMENT_NODE && (force == TRUE || t->ns == NULL)) {
|
|
xml_insure_namespace(t, ns, force, NULL, NULL);
|
|
}
|
|
t = t->next;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* lasso_node_get_xmlnode_for_any_type:
|
|
* @node: a #LassoNode.
|
|
* @xmlnode: the #xmlNode returned.
|
|
*
|
|
* Return value: a xmlNode completed with the content of the produced by the get_xmlNode virtual
|
|
* method of the parent class.
|
|
*/
|
|
xmlNode*
|
|
lasso_node_get_xmlnode_for_any_type(LassoNode *node, xmlNode *cur)
|
|
{
|
|
|
|
xmlNode *original_xmlnode;
|
|
|
|
original_xmlnode = lasso_node_get_original_xmlnode(node);
|
|
if (cur) {
|
|
if (original_xmlnode) {
|
|
xmlNode *children = xmlCopyNodeList(original_xmlnode->children);
|
|
xmlCopyPropList(cur, original_xmlnode->properties);
|
|
xmlAddChildList(cur, children);
|
|
return cur;
|
|
} else {
|
|
return cur;
|
|
}
|
|
} else {
|
|
if (original_xmlnode) {
|
|
return xmlCopyNode(original_xmlnode, 1);
|
|
} else {
|
|
return cur;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* lasso_node_get_name:
|
|
* @node: a #LassoNode
|
|
*
|
|
* Return the XML element name for this object, the one that would be used in the XML dump of this
|
|
* object.
|
|
*
|
|
* Return value: the name of the object, the value must not be stored.
|
|
*/
|
|
const char*
|
|
lasso_node_get_name(LassoNode *node)
|
|
{
|
|
struct _CustomElement *custom_element;
|
|
LassoNodeClass *klass;
|
|
g_return_val_if_fail(LASSO_IS_NODE(node), NULL);
|
|
|
|
custom_element = _lasso_node_get_custom_element(node);
|
|
if (custom_element && custom_element->nodename) {
|
|
return custom_element->nodename;
|
|
}
|
|
klass = LASSO_NODE_GET_CLASS(node);
|
|
return klass->node_data->node_name;
|
|
}
|
|
|
|
/**
|
|
* lasso_node_get_name:
|
|
* @node: a #LassoNode
|
|
*
|
|
* Return the XML element name for this object, the one that would be used in the XML dump of this
|
|
* object.
|
|
*
|
|
* Return value: the name of the object, the value must not be stored.
|
|
*/
|
|
const char*
|
|
lasso_node_get_namespace(LassoNode *node)
|
|
{
|
|
struct _CustomElement *custom_element;
|
|
LassoNodeClass *klass;
|
|
g_return_val_if_fail(LASSO_IS_NODE(node), NULL);
|
|
|
|
custom_element = _lasso_node_get_custom_element(node);
|
|
if (custom_element && custom_element->nodename) {
|
|
return custom_element->href;
|
|
}
|
|
klass = LASSO_NODE_GET_CLASS(node);
|
|
if (klass->node_data && klass->node_data->ns)
|
|
return (const char*)klass->node_data->ns->href;
|
|
return NULL;
|
|
}
|