/* $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 #include #include #include #include #include #include #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 */ 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 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 node */ if (xmlSecTmplEncDataEnsureCipherValue(encrypted_key_node) == NULL) { message(G_LOG_LEVEL_WARNING, "Failed to add CipherValue node"); goto cleanup; } /* add and nodes to */ 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 */ 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 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; }