1 /* $Id$
2  *
3  * Lasso - A free implementation of the Liberty Alliance specifications.
4  *
5  * Copyright (C) 2004-2007 Entr'ouvert
6  * http://lasso.entrouvert.org
7  *
8  * Authors: See AUTHORS file in top-level directory.
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, see <http://www.gnu.org/licenses/>.
22  */
23 
24 /**
25  * SECTION:node
26  * @short_description: Base class for all Lasso objects
27  *
28  * #LassoNode is the base class for Lasso objects; just a step over GObject as
29  * defined in glib.
30  *
31  */
32 
33 #include "private.h"
34 #include <ctype.h>
35 #include <errno.h>
36 #include <string.h>
37 
38 #include <xmlsec/base64.h>
39 #include <xmlsec/xmltree.h>
40 #include <xmlsec/xmldsig.h>
41 #include <xmlsec/templates.h>
42 #include <xmlsec/crypto.h>
43 #include <xmlsec/xmlenc.h>
44 #include <xmlsec/openssl/crypto.h>
45 #include <xmlsec/openssl/x509.h>
46 
47 #include "xml.h"
48 #include "xml_enc.h"
49 #include "saml_name_identifier.h"
50 #include "../utils.h"
51 #include "../registry.h"
52 #include "../debug.h"
53 #include "soap-1.1/soap_envelope.h"
54 #include "soap-1.1/soap_body.h"
55 #include "misc_text_node.h"
56 #include "../lasso_config.h"
57 #ifdef LASSO_WSF_ENABLED
58 #include "idwsf_strings.h"
59 #include "id-wsf-2.0/idwsf2_strings.h"
60 #endif
61 
62 /* Needed for ECP */
63 #include "saml-2.0/samlp2_idp_list.h"
64 #include "paos_request.h"
65 #include "ecp/ecp_request.h"
66 #include "ecp/ecp_response.h"
67 #include "ecp/ecp_relaystate.h"
68 
69 #include "../key.h"
70 
71 #define LOG_LEVEL_XML_DEBUG G_LOG_LEVEL_DEBUG /* change to G_LOG_LEVEL_WARNING so it shows in Apache log */
72 
73 static void lasso_node_build_xmlNode_from_snippets(LassoNode *node, LassoNodeClass *class, xmlNode *xmlnode,
74 		struct XmlSnippet *snippets, gboolean lasso_dump);
75 static struct XmlSnippet* find_xml_snippet_by_name(LassoNode *node, char *name, LassoNodeClass **class_p);
76 static gboolean set_value_at_path(LassoNode *node, char *path, char *query_value);
77 static char* get_value_by_path(LassoNode *node, char *path, struct XmlSnippet *xml_snippet);
78 static gboolean find_path(LassoNode *node, char *path, LassoNode **value_node,
79 		LassoNodeClass **class_p, struct XmlSnippet **snippet);
80 
81 static void lasso_node_add_signature_template(LassoNode *node, xmlNode *xmlnode,
82 		struct XmlSnippet *snippet_signature);
83 static void lasso_node_traversal(LassoNode *node, void (*do_to_node)(LassoNode *node, SnippetType type), SnippetType type);
84 
85 static LassoNode* lasso_node_new_from_xmlNode_with_type(xmlNode *xmlnode, char *typename);
86 static void lasso_node_remove_original_xmlnode(LassoNode *node, SnippetType type);
87 
88 GHashTable *dst_services_by_href = NULL; /* ID-WSF 1 extra DST services, indexed on href */
89 GHashTable *dst_services_by_prefix = NULL; /* ID-WSF 1 extra DST services, indexed on prefix */
90 
91 GHashTable *idwsf2_dst_services_by_href = NULL; /* ID-WSF 2 DST services, indexed on href */
92 GHashTable *idwsf2_dst_services_by_prefix = NULL; /* ID-WSF 2 DST services, indexed on prefix */
93 
94 /*****************************************************************************/
95 /* global methods                                                            */
96 /*****************************************************************************/
97 
98 /**
99  * lasso_register_dst_service:
100  * @prefix: prefix of DST service
101  * @href: href of DST service
102  *
103  * Registers prefix and href of a custom data service template service.
104  **/
105 void
lasso_register_dst_service(const gchar * prefix,const gchar * href)106 lasso_register_dst_service(const gchar *prefix, const gchar *href)
107 {
108 	if (dst_services_by_href == NULL) {
109 		dst_services_by_href = g_hash_table_new_full(
110 				g_str_hash, g_str_equal, g_free, g_free);
111 		dst_services_by_prefix = g_hash_table_new_full(
112 				g_str_hash, g_str_equal, g_free, g_free);
113 	}
114 	g_hash_table_insert(dst_services_by_prefix, g_strdup(prefix), g_strdup(href));
115 	g_hash_table_insert(dst_services_by_href, g_strdup(href), g_strdup(prefix));
116 }
117 
118 void
lasso_register_idwsf2_dst_service(const gchar * prefix,const gchar * href)119 lasso_register_idwsf2_dst_service(const gchar *prefix, const gchar *href)
120 {
121 	if (idwsf2_dst_services_by_href == NULL) {
122 		idwsf2_dst_services_by_href = g_hash_table_new_full(
123 				g_str_hash, g_str_equal, g_free, g_free);
124 		idwsf2_dst_services_by_prefix = g_hash_table_new_full(
125 				g_str_hash, g_str_equal, g_free, g_free);
126 	}
127 	g_hash_table_insert(idwsf2_dst_services_by_prefix, g_strdup(prefix), g_strdup(href));
128 	g_hash_table_insert(idwsf2_dst_services_by_href, g_strdup(href), g_strdup(prefix));
129 }
130 
131 gchar*
lasso_get_prefix_for_dst_service_href(const gchar * href)132 lasso_get_prefix_for_dst_service_href(const gchar *href)
133 {
134 	if (dst_services_by_href == NULL)
135 		return NULL;
136 
137 	return g_strdup(g_hash_table_lookup(dst_services_by_href, href));
138 }
139 
140 gchar*
lasso_get_prefix_for_idwsf2_dst_service_href(const gchar * href)141 lasso_get_prefix_for_idwsf2_dst_service_href(const gchar *href)
142 {
143 	if (idwsf2_dst_services_by_href == NULL)
144 		return NULL;
145 
146 	return g_strdup(g_hash_table_lookup(idwsf2_dst_services_by_href, href));
147 }
148 
149 
150 /*****************************************************************************/
151 /* virtual public methods                                                    */
152 /*****************************************************************************/
153 
154 static char*
_lasso_node_export_to_xml(LassoNode * node,gboolean format,gboolean dump,int level)155 _lasso_node_export_to_xml(LassoNode *node, gboolean format, gboolean dump, int level)
156 {
157 	xmlNode *xmlnode;
158 	char *ret;
159 
160 	g_return_val_if_fail (LASSO_IS_NODE(node), NULL);
161 
162 	xmlnode = lasso_node_get_xmlNode(node, dump);
163 	if (xmlnode == NULL) {
164 		return NULL;
165 	}
166 	ret = lasso_xmlnode_to_string(xmlnode, format, level);
167 	xmlFreeNode(xmlnode);
168 
169 	return ret;
170 }
171 
172 /**
173  * lasso_node_dump:
174  * @node: a #LassoNode
175  *
176  * Dumps @node.  All datas in object are dumped in an XML format.
177  *
178  * Return value:(transfer full): a full XML dump of @node.  The string must be freed by the
179  *     caller.
180  **/
181 char*
lasso_node_dump(LassoNode * node)182 lasso_node_dump(LassoNode *node)
183 {
184 	return _lasso_node_export_to_xml(node, FALSE, TRUE, 0);
185 }
186 
187 /**
188  * lasso_node_debug:
189  * @node: a #LassoNode
190  * @level:(default 10): the indentation depth, i.e. the depth of the last nodes to be indented.
191  *
192  * Create a debug dump for @node, it is pretty printed so any contained signature will be
193  * uncheckable.
194  *
195  * Return value:(transfer full): a full indented and so human readable dump of @node. The string must be freed by
196  * the caller.
197  */
198 char*
lasso_node_debug(LassoNode * node,int level)199 lasso_node_debug(LassoNode *node, int level)
200 {
201 	return _lasso_node_export_to_xml(node, TRUE, TRUE, level);
202 }
203 
204 /**
205  * lasso_node_destroy:
206  * @node: a #LassoNode
207  *
208  * Destroys the #LassoNode.
209  **/
210 void
lasso_node_destroy(LassoNode * node)211 lasso_node_destroy(LassoNode *node)
212 {
213 	if (node == NULL) {
214 		return;
215 	}
216 	if (LASSO_IS_NODE(node)) {
217 		LassoNodeClass *class = LASSO_NODE_GET_CLASS(node);
218 		class->destroy(node);
219 	}
220 }
221 
222 /**
223  * lasso_node_export_to_base64:
224  * @node: a #LassoNode
225  *
226  * Exports @node to a base64-encoded message.
227  *
228  * Return value: a base64-encoded export of @node.  The string must be freed by
229  *      the caller.
230  **/
231 char*
lasso_node_export_to_base64(LassoNode * node)232 lasso_node_export_to_base64(LassoNode *node)
233 {
234 	char *str;
235 	char *ret;
236 
237 	g_return_val_if_fail(LASSO_IS_NODE(node), NULL);
238 
239 	str = lasso_node_export_to_xml(node);
240 	if (str == NULL)
241 		return NULL;
242 	ret = (char*)xmlSecBase64Encode(BAD_CAST str, strlen(str), 0);
243 	lasso_release_string(str);
244 	return ret;
245 }
246 
247 /**
248  * lasso_node_export_to_ecp_soap_response:
249  * @node: a #LassoNode
250  *
251  * Exports @node to a ECP SOAP message.
252  *
253  * Return value: a ECP SOAP export of @node.  The string must be freed by the
254  *      caller.
255  **/
256 char*
lasso_node_export_to_ecp_soap_response(LassoNode * node,const char * assertionConsumerURL)257 lasso_node_export_to_ecp_soap_response(LassoNode *node, const char *assertionConsumerURL)
258 {
259 	char *ret = NULL;
260 	LassoNode *ecp_response = NULL;
261 	GList *headers = NULL;
262 
263 	lasso_return_null_if_fail(LASSO_IS_NODE(node));
264 	lasso_return_null_if_fail(assertionConsumerURL);
265 
266 	/* Build the soap header elements */
267 	ecp_response = lasso_ecp_response_new(assertionConsumerURL);
268 	goto_cleanup_if_fail(ecp_response);
269 	lasso_list_add_new_gobject(headers, ecp_response);
270 
271 	/* Create soap envelope and serialize into an xml document */
272 	ret = lasso_node_export_to_soap_with_headers(node, headers);
273 
274  cleanup:
275 	lasso_release_list_of_gobjects(headers);
276 	return ret;
277 }
278 
279 /**
280  * lasso_node_export_to_paos_request:
281  * @node: a #LassoNode
282  *
283  * Exports @node to a PAOS message.
284  *
285  * Deprecated, use lasso_node_export_to_paos_request_full() instead
286  *
287  * Return value: a PAOS export of @node.  The string must be freed by the
288  *      caller.
289  **/
290 char *
lasso_node_export_to_paos_request(LassoNode * node,const char * issuer,const char * responseConsumerURL,const char * relay_state)291 lasso_node_export_to_paos_request(LassoNode *node, const char *issuer,
292 		const char *responseConsumerURL, const char *relay_state)
293 {
294 	return lasso_node_export_to_paos_request_full(node, issuer, responseConsumerURL,
295 												  NULL, relay_state, TRUE, NULL, NULL);
296 }
297 
298 /**
299  * lasso_node_export_to_paos_request_full:
300  * @node:
301  * @issuer:
302  * @responseConsumerURL:
303  * @message_id: (allow-none):
304  * @relay_state: (allow-none):
305  * @is_passive:
306  * @provider_name: (allow-none):
307  * @idp_list: (allow-none):
308  *
309  * Creates a new SOAP message. The SOAP headers include a PaosRequst,
310  * a EcpRequest and optionally a EcpRelayState. The SOAP body contains
311  * the @node parameters.
312  *
313  * Returns: string containing a PAOS request. The string must be freed
314  * by the caller.
315  **/
316 char *
lasso_node_export_to_paos_request_full(LassoNode * node,const char * issuer,const char * responseConsumerURL,const char * message_id,const char * relay_state,gboolean is_passive,gchar * provider_name,LassoSamlp2IDPList * idp_list)317 lasso_node_export_to_paos_request_full(LassoNode *node, const char *issuer,
318 									   const char *responseConsumerURL, const char *message_id,
319 									   const char *relay_state, gboolean is_passive, gchar *provider_name,
320 									   LassoSamlp2IDPList *idp_list)
321 {
322 	char *ret = NULL;
323 	LassoNode *paos_request = NULL;
324 	LassoNode *ecp_request = NULL;
325 	LassoNode *ecp_relaystate = NULL;
326 	GList *headers = NULL;
327 
328 	lasso_return_null_if_fail(LASSO_IS_NODE(node));
329 	lasso_return_null_if_fail(issuer);
330 	lasso_return_null_if_fail(responseConsumerURL);
331 
332 	/* Build the soap header elements */
333 	paos_request = lasso_paos_request_new(responseConsumerURL, message_id);
334 	goto_cleanup_if_fail(paos_request);
335 	lasso_list_add_new_gobject(headers, paos_request);
336 
337 	ecp_request = lasso_ecp_request_new(issuer, is_passive, provider_name, idp_list);
338 	goto_cleanup_if_fail(ecp_request);
339 	lasso_list_add_new_gobject(headers, ecp_request);
340 
341 	if (relay_state) {
342 		ecp_relaystate = lasso_ecp_relay_state_new(relay_state);
343 		goto_cleanup_if_fail(ecp_relaystate);
344 		lasso_list_add_new_gobject(headers, ecp_relaystate);
345 	}
346 
347 	/* Create soap envelope and serialize into an xml document */
348 	ret = lasso_node_export_to_soap_with_headers(node, headers);
349 
350  cleanup:
351 	lasso_release_list_of_gobjects(headers);
352 	return ret;
353 }
354 
355 /**
356  * lasso_node_export_to_query:
357  * @node: a #LassoNode
358  * @sign_method:(default 1): the Signature transform method
359  * @private_key_file:(allow-none): the path to the private key (may be NULL)
360  *
361  * Exports @node to a HTTP query string.  If @private_key_file is NULL,
362  * query won't be signed.
363  *
364  * Return value: a HTTP query export of @node.  The string must be freed by the
365  *      caller.
366  **/
367 char*
lasso_node_export_to_query(LassoNode * node,LassoSignatureMethod sign_method,const char * private_key_file)368 lasso_node_export_to_query(LassoNode *node, LassoSignatureMethod sign_method,
369 		const char *private_key_file)
370 {
371 	return lasso_node_export_to_query_with_password(node, sign_method, private_key_file, NULL);
372 }
373 
374 /**
375  * lasso_node_export_to_query_with_password:
376  * @node: a #LassoNode
377  * @sign_method:(default 1): the Signature transform method
378  * @private_key_file:(allow-none): the path to the private key (may be NULL)
379  * @private_key_file_password:(allow-none): the password needed to decrypt the private key
380  *
381  * Exports @node to a HTTP query string.  If @private_key_file is NULL,
382  * query won't be signed.
383  *
384  * Return value: a HTTP query export of @node.  The string must be freed by the
385  *      caller.
386  **/
387 char*
lasso_node_export_to_query_with_password(LassoNode * node,LassoSignatureMethod sign_method,const char * private_key_file,const char * private_key_file_password)388 lasso_node_export_to_query_with_password(LassoNode *node,
389 		LassoSignatureMethod sign_method, const char *private_key_file,
390 		const char *private_key_file_password)
391 {
392 	char *unsigned_query, *query = NULL;
393 	LassoSignatureContext context = LASSO_SIGNATURE_CONTEXT_NONE;
394 
395 	g_return_val_if_fail(LASSO_IS_NODE(node), NULL);
396 
397 	context.signature_method = sign_method;
398 	context.signature_key = lasso_xmlsec_load_private_key(private_key_file,
399 			private_key_file_password, sign_method, NULL);
400 
401 	if (! context.signature_key) {
402 		return NULL;
403 	}
404 
405 	unsigned_query = lasso_node_build_query(node);
406 	if (unsigned_query){
407 		query = lasso_query_sign(unsigned_query, context);
408 		if (query) {
409 			lasso_release(unsigned_query);
410 			unsigned_query = query;
411 		}
412 	}
413 	lasso_release_sec_key(context.signature_key);
414 	return unsigned_query;
415 }
416 
417 /**
418  * lasso_node_export_to_xml:
419  * @node: a #LassoNode
420  *
421  * Exports @node to an xml message.
422  *
423  * Return value: an xml export of @node.  The string must be freed by the
424  *      caller.
425  **/
426 gchar*
lasso_node_export_to_xml(LassoNode * node)427 lasso_node_export_to_xml(LassoNode *node)
428 {
429 	return _lasso_node_export_to_xml(node, FALSE, FALSE, 0);
430 }
431 
432 /**
433  * lasso_node_export_to_soap:
434  * @node: a #LassoNode
435  *
436  * Exports @node to a SOAP message.
437  *
438  * Return value: a SOAP export of @node.  The string must be freed by the
439  *      caller.
440  **/
441 char*
lasso_node_export_to_soap(LassoNode * node)442 lasso_node_export_to_soap(LassoNode *node)
443 {
444 	LassoSoapEnvelope *envelope;
445 	LassoSoapBody *body;
446 	char *ret;
447 
448 	g_return_val_if_fail(LASSO_IS_NODE(node), NULL);
449 
450 	body = lasso_soap_body_new();
451 	envelope = lasso_soap_envelope_new(body);
452 	lasso_list_add_gobject(body->any, node);
453 	ret = lasso_node_export_to_xml((LassoNode*)envelope);
454 	lasso_release_gobject(envelope);
455 	lasso_release_gobject(body);
456 	return ret;
457 }
458 
459 /**
460  * lasso_node_export_to_soap_with_headers:
461  * @node: a #LassoNode, becomes the SOAP body
462  * @headers: (allow-none): #GList of #LassNode
463  *
464  * Exports @node to a SOAP message. The @node becomes the SOAP body.
465  * each header in the #headers list is added to the SOAP header if non-NULL.
466  * @headers is permitted to be an empty list (e.g. NULL).
467  *
468  * <example>
469  * <title>Create SOAP envelope with variable number of header nodes</title>
470  *
471  * <para>You need to form a SOAP message with authn_request as the body and
472  * paos_request, ecp_request and ecp_relaystate as SOAP header elements.
473  * It is possible one or more of these may be NULL and should be skipped.</para>
474  * <programlisting>
475  * char *text = NULL;
476  * LassoNode *paos_request = NULL;
477  * LassoNode *ecp_request = NULL;
478  * LassoNode *ecp_relaystate = NULL;
479  * GList *headers = NULL;
480  *
481  * paos_request = lasso_paos_request_new(responseConsumerURL, message_id);
482  * ecp_request = lasso_ecp_request_new(issuer, is_passive, provider_name, idp_list);
483  *
484  * lasso_list_add_new_gobject(headers, paos_request);
485  * lasso_list_add_new_gobject(headers, ecp_request);
486  * lasso_list_add_new_gobject(headers, ecp_relaystate);
487  *
488  * text = lasso_node_export_to_soap_with_headers(node, headers);
489  *
490  * lasso_release_list_of_gobjects(headers);
491  * </programlisting>
492  * </example>
493  *
494  * Return value: a SOAP export of @node.  The string must be freed by the
495  *      caller.
496  **/
497 char*
lasso_node_export_to_soap_with_headers(LassoNode * node,GList * headers)498 lasso_node_export_to_soap_with_headers(LassoNode *node, GList *headers)
499 {
500 	GList *i;
501 	LassoSoapEnvelope *envelope = NULL;
502 	LassoNode *header = NULL;
503 	char *ret = NULL;
504 
505 	g_return_val_if_fail(LASSO_IS_NODE(node), NULL);
506 
507 	envelope = lasso_soap_envelope_new_full();
508 	lasso_list_add_gobject(envelope->Body->any, node);
509 
510 	lasso_foreach(i, headers) {
511 		header = i->data;
512 		if (!header) continue;
513 
514 		goto_cleanup_if_fail(LASSO_IS_NODE(header));
515 		lasso_list_add_gobject(envelope->Header->Other, header); /* adds ref */
516 	}
517 
518 	ret = lasso_node_export_to_xml((LassoNode*)envelope);
519 
520  cleanup:
521 	lasso_release_gobject(envelope);
522 	return ret;
523 }
524 
525 /**
526  * lasso_node_encrypt:
527  * @lasso_node: a #LassoNode to encrypt
528  * @encryption_public_key : RSA public key the node will be encrypted with
529  *
530  * Generate a DES key and encrypt it with the RSA key.
531  * Then encrypt @lasso_node with the DES key.
532  *
533  * Return value: an xmlNode which is the @node in an encrypted fashion.
534  * It must be freed by the caller.
535  **/
536 LassoSaml2EncryptedElement*
lasso_node_encrypt(LassoNode * lasso_node,xmlSecKey * encryption_public_key,LassoEncryptionSymKeyType encryption_sym_key_type,const char * recipient)537 lasso_node_encrypt(LassoNode *lasso_node, xmlSecKey *encryption_public_key,
538 		LassoEncryptionSymKeyType encryption_sym_key_type, const char *recipient)
539 {
540 	xmlDocPtr doc = NULL;
541 	xmlNodePtr orig_node = NULL;
542 	LassoSaml2EncryptedElement *encrypted_element = NULL, *ret = NULL;
543 	xmlSecKeysMngrPtr key_manager = NULL;
544 	xmlNodePtr key_info_node = NULL;
545 	xmlNodePtr encrypted_key_node = NULL;
546 	xmlNodePtr encrypted_data = NULL;
547 	xmlNodePtr key_info_node2 = NULL;
548 	xmlSecEncCtxPtr enc_ctx = NULL;
549 	xmlSecTransformId xmlsec_encryption_sym_key_type;
550 	xmlSecKey *duplicate = NULL;
551 
552 	if (encryption_public_key == NULL || !xmlSecKeyIsValid(encryption_public_key)) {
553 		message(G_LOG_LEVEL_WARNING, "Invalid encryption key");
554 		goto cleanup;
555 	}
556 
557 	/* Create a document to contain the node to encrypt */
558 	doc = xmlNewDoc((xmlChar*)"1.0");
559 	orig_node = lasso_node_get_xmlNode(lasso_node, FALSE);
560 	xmlDocSetRootElement(doc, orig_node);
561 
562 	/* Get the symetric key type */
563 	switch (encryption_sym_key_type) {
564 		case LASSO_ENCRYPTION_SYM_KEY_TYPE_AES_256:
565 			xmlsec_encryption_sym_key_type = xmlSecTransformAes256CbcId;
566 			break;
567 		case LASSO_ENCRYPTION_SYM_KEY_TYPE_3DES:
568 			xmlsec_encryption_sym_key_type = xmlSecTransformDes3CbcId;
569 			break;
570 		case LASSO_ENCRYPTION_SYM_KEY_TYPE_AES_128:
571 		default:
572 			xmlsec_encryption_sym_key_type = xmlSecTransformAes128CbcId;
573 			break;
574 	}
575 
576 	/* Create encryption template for a specific symetric key type */
577 	/* saml-core 2.2.4 line 498:
578 	 * The Type attribute SHOULD be present and, if present, MUST contain a value of
579 	 * http://www.w3.org/2001/04/xmlenc#Element. */
580 	encrypted_data = xmlSecTmplEncDataCreate(doc,
581 			xmlsec_encryption_sym_key_type,	NULL, xmlSecTypeEncElement, NULL, NULL);
582 
583 	if (encrypted_data == NULL) {
584 		message(G_LOG_LEVEL_WARNING, "Failed to create encryption template");
585 		goto cleanup;
586 	}
587 
588 	if (xmlSecTmplEncDataEnsureCipherValue(encrypted_data) == NULL) {
589 		message(G_LOG_LEVEL_WARNING, "Failed to add CipherValue node");
590 		goto cleanup;
591 	}
592 
593 	/* create and initialize keys manager, we use a simple list based
594 	 * keys manager, implement your own xmlSecKeysStore klass if you need
595 	 * something more sophisticated
596 	 */
597 	key_manager = xmlSecKeysMngrCreate();
598 	if (key_manager == NULL) {
599 		message(G_LOG_LEVEL_WARNING, "Failed to create keys manager");
600 		goto cleanup;
601 	}
602 
603 	if (xmlSecCryptoAppDefaultKeysMngrInit(key_manager) < 0) {
604 		message(G_LOG_LEVEL_WARNING, "Failed to initialize keys manager");
605 		goto cleanup;
606 	}
607 
608 	/* add key to keys manager, from now on keys manager is responsible
609 	 * for destroying key
610 	 */
611 	duplicate = xmlSecKeyDuplicate(encryption_public_key);
612 	if (xmlSecCryptoAppDefaultKeysMngrAdoptKey(key_manager, duplicate) < 0) {
613 		lasso_release_sec_key(duplicate);
614 		goto cleanup;
615 	}
616 
617 	/* add <dsig:KeyInfo/> */
618 	key_info_node = xmlSecTmplEncDataEnsureKeyInfo(encrypted_data, NULL);
619 	if (key_info_node == NULL) {
620 		message(G_LOG_LEVEL_WARNING, "Failed to add key info");
621 		goto cleanup;
622 	}
623 
624 	/* add <enc:EncryptedKey/> to store the encrypted session key */
625 	encrypted_key_node = xmlSecTmplKeyInfoAddEncryptedKey(key_info_node,
626 			xmlSecTransformRsaPkcs1Id, NULL, NULL, (xmlChar*)recipient);
627 	if (encrypted_key_node == NULL) {
628 		message(G_LOG_LEVEL_WARNING, "Failed to add encrypted key");
629 		goto cleanup;
630 	}
631 
632 	/* we want to put encrypted key in the <enc:CipherValue/> node */
633 	if (xmlSecTmplEncDataEnsureCipherValue(encrypted_key_node) == NULL) {
634 		message(G_LOG_LEVEL_WARNING, "Failed to add CipherValue node");
635 		goto cleanup;
636 	}
637 
638 	/* add <dsig:KeyInfo/> and <dsig:KeyName/> nodes to <enc:EncryptedKey/> */
639 	key_info_node2 = xmlSecTmplEncDataEnsureKeyInfo(encrypted_key_node, NULL);
640 	if (key_info_node2 == NULL) {
641 		message(G_LOG_LEVEL_WARNING, "Failed to add key info");
642 		goto cleanup;
643 	}
644 	/* check id of the key */
645 	if (xmlSecKeyGetData(encryption_public_key, xmlSecOpenSSLKeyDataRsaId) != 0) {
646 		xmlNode *key_value = xmlSecTmplKeyInfoAddKeyValue(key_info_node2);
647 		if (key_value == NULL) {
648 			message(G_LOG_LEVEL_WARNING, "Failed to add key value");
649 			goto cleanup;
650 		}
651 	} else { /* it must be a certificate */
652 		xmlNodePtr x509_data;
653 		x509_data = xmlSecTmplKeyInfoAddX509Data(key_info_node2);
654 		if (x509_data == NULL) {
655 			message(G_LOG_LEVEL_WARNING, "Failed to add X509 data");
656 			goto cleanup;
657 		}
658 	}
659 
660 
661 
662 
663 	/* create encryption context */
664 	enc_ctx = (xmlSecEncCtxPtr)xmlSecEncCtxCreate(key_manager);
665 	if (enc_ctx == NULL) {
666 		message(G_LOG_LEVEL_WARNING, "Failed to create encryption context");
667 		goto cleanup;
668 	}
669 
670 	/* generate a symetric key */
671 	switch (encryption_sym_key_type) {
672 		case LASSO_ENCRYPTION_SYM_KEY_TYPE_AES_256:
673 			enc_ctx->encKey = xmlSecKeyGenerate(xmlSecKeyDataAesId, 256,
674 					xmlSecKeyDataTypeSession);
675 			break;
676 		case LASSO_ENCRYPTION_SYM_KEY_TYPE_3DES:
677 			enc_ctx->encKey = xmlSecKeyGenerate(xmlSecKeyDataDesId, 192,
678 					xmlSecKeyDataTypeSession);
679 			break;
680 		case LASSO_ENCRYPTION_SYM_KEY_TYPE_AES_128:
681 		default:
682 			enc_ctx->encKey = xmlSecKeyGenerate(xmlSecKeyDataAesId, 128,
683 					xmlSecKeyDataTypeSession);
684 			break;
685 	}
686 
687 	if (enc_ctx->encKey == NULL) {
688 		message(G_LOG_LEVEL_WARNING, "Failed to generate session des key");
689 		goto cleanup;
690 	}
691 
692 
693 	/* encrypt the data */
694 	if (xmlSecEncCtxXmlEncrypt(enc_ctx, encrypted_data, orig_node) < 0) {
695 		message(G_LOG_LEVEL_WARNING, "Encryption failed");
696 		goto cleanup;
697 	}
698 
699 
700 	/* Create a new EncryptedElement */
701 	encrypted_element = LASSO_SAML2_ENCRYPTED_ELEMENT(lasso_saml2_encrypted_element_new());
702 	lasso_assign_gobject(encrypted_element->original_data, lasso_node);
703 	lasso_assign_xml_node(encrypted_element->EncryptedData, xmlDocGetRootElement(doc));
704 	lasso_transfer_gobject(ret, encrypted_element);
705 
706 cleanup:
707 	lasso_release_key_manager(key_manager);
708 	lasso_release_gobject(encrypted_element);
709 	lasso_release_encrypt_context(enc_ctx);
710 	lasso_release_doc(doc);
711 
712 	return ret;
713 }
714 
715 
716 /**
717  * lasso_node_init_from_query:
718  * @node: a #LassoNode (or derived class)
719  * @query: the query string
720  *
721  * Initialiazes @node fields with data from @query string.
722  *
723  * Return value: %TRUE if success
724  **/
725 gboolean
lasso_node_init_from_query(LassoNode * node,const char * query)726 lasso_node_init_from_query(LassoNode *node, const char *query)
727 {
728 	LassoNodeClass *class;
729 	xmlChar **query_fields;
730 	int i;
731 	gboolean rc;
732 
733 	g_return_val_if_fail(LASSO_IS_NODE(node), FALSE);
734 	class = LASSO_NODE_GET_CLASS(node);
735 
736 	query_fields = lasso_urlencoded_to_strings(query);
737 	rc = class->init_from_query(node, (char**)query_fields);
738 	for (i = 0; query_fields[i]; i++) {
739 		xmlFree(query_fields[i]);
740 		query_fields[i] = NULL;
741 	}
742 	lasso_release_array_of_xml_strings(query_fields);
743 	return rc;
744 }
745 
746 
747 /**
748  * lasso_node_init_from_xml:
749  * @node: a #LassoNode (or derived class)
750  * @xmlnode: the libxml2 node
751  *
752  * Initialiazes @node fields with data from @xmlnode XML node.
753  *
754  * Return value: 0 on success; or a negative value otherwise.
755  **/
756 int
lasso_node_init_from_xml(LassoNode * node,xmlNode * xmlnode)757 lasso_node_init_from_xml(LassoNode *node, xmlNode *xmlnode)
758 {
759 	LassoNodeClass *class;
760 
761 	g_return_val_if_fail(LASSO_IS_NODE(node), LASSO_XML_ERROR_OBJECT_CONSTRUCTION_FAILED);
762 	class = LASSO_NODE_GET_CLASS(node);
763 
764 	return class->init_from_xml(node, xmlnode);
765 }
766 
767 /**
768  * lasso_node_build_query:
769  * @node: a #LassoNode
770  *
771  * Build an HTTP query from the given LassoNode, this is a pure virtual
772  * function, you must overload it in subclass.
773  *
774  * Return value: a newly allocated string containing the query if it succeed,
775  * or NULL otherwise.
776  */
777 char*
lasso_node_build_query(LassoNode * node)778 lasso_node_build_query(LassoNode *node)
779 {
780 	LassoNodeClass *class;
781 	g_return_val_if_fail (LASSO_IS_NODE(node), NULL);
782 
783 	class = LASSO_NODE_GET_CLASS(node);
784 	return class->build_query(node);
785 }
786 
787 static LassoNodeClassData*
lasso_legacy_get_signature_node_data(LassoNode * node,LassoNodeClass ** out_klass)788 lasso_legacy_get_signature_node_data(LassoNode *node, LassoNodeClass **out_klass)
789 {
790 	LassoNodeClass *klass = NULL;
791 	LassoNodeClassData *node_data = NULL;
792 
793 	klass = LASSO_NODE_GET_CLASS(node);
794 	/* find a klass defining a signature */
795 	while (klass && LASSO_IS_NODE_CLASS(klass)) {
796 		if (klass->node_data && klass->node_data->sign_type_offset) {
797 			if (out_klass) {
798 				*out_klass = klass;
799 			}
800 			node_data = klass->node_data;
801 			break;
802 		}
803 		klass = g_type_class_peek_parent(klass);
804 	}
805 
806 	return node_data;
807 }
808 
809 static gboolean
lasso_legacy_extract_and_copy_signature_parameters(LassoNode * node,LassoNodeClassData * node_data)810 lasso_legacy_extract_and_copy_signature_parameters(LassoNode *node, LassoNodeClassData *node_data)
811 {
812 	LassoSignatureMethod signature_method = LASSO_SIGNATURE_METHOD_NONE;
813 	char *private_key_file = NULL;
814 	char *certificate_file = NULL;
815 
816 	if (! node_data) {
817 		return FALSE;
818 	}
819 	signature_method = G_STRUCT_MEMBER(LassoSignatureMethod, node,
820 			node_data->sign_method_offset);
821 	private_key_file = G_STRUCT_MEMBER(char *, node, node_data->private_key_file_offset);
822 	certificate_file = G_STRUCT_MEMBER(char *, node, node_data->certificate_file_offset);
823 	if (! lasso_validate_signature_method(signature_method)) {
824 		return FALSE;
825 	}
826 	if (lasso_node_set_signature(node,
827 			lasso_make_signature_context_from_path_or_string(private_key_file, NULL,
828 				signature_method, certificate_file)) != 0) {
829 		return FALSE;
830 	}
831 	return TRUE;
832 }
833 
834 /**
835  * lasso_node_get_xmlNode:
836  * @node: a #LassoNode
837  * @lasso_dump: whether to include lasso-specific nodes
838  *
839  * Builds an XML representation of @node.
840  *
841  * Return value: a new xmlNode.  It must be freed by the caller.
842  **/
843 xmlNode*
lasso_node_get_xmlNode(LassoNode * node,gboolean lasso_dump)844 lasso_node_get_xmlNode(LassoNode *node, gboolean lasso_dump)
845 {
846 	xmlNode *xmlnode = NULL;
847 	LassoSignatureContext context;
848 	LassoNodeClassData *node_data;
849 
850 	g_return_val_if_fail (LASSO_IS_NODE(node), NULL);
851 	xmlnode = LASSO_NODE_GET_CLASS(node)->get_xmlNode(node, lasso_dump);
852 	node_data = lasso_legacy_get_signature_node_data(node, NULL);
853 	context = lasso_node_get_signature(node);
854 	/* support for legacy way to put a signature on a node */
855 	if (! lasso_validate_signature_context(context)) {
856 		if (lasso_legacy_extract_and_copy_signature_parameters(node, node_data))
857 			context = lasso_node_get_signature(node);
858 	}
859 	if (! lasso_dump && node_data && xmlnode && lasso_validate_signature_context(context)) {
860 		int rc;
861 		char *id_attribute = G_STRUCT_MEMBER(char*, node,
862 				node_data->id_attribute_offset);
863 
864 		rc = lasso_sign_node(xmlnode, context, node_data->id_attribute_name,
865 				id_attribute);
866 		if (rc != 0) {
867 			warning("Signing of %s:%s failed: %s", xmlnode->ns->prefix,
868 					xmlnode->name, lasso_strerror(rc));
869 			lasso_release_xml_node(xmlnode);
870 		}
871 	}
872 
873 	return xmlnode;
874 }
875 
876 /**
877  * lasso_node_cleanup_original_xmlnodes:
878  *
879  * @node: a #LassoNode
880  *
881  * Traverse the #LassoNode tree starting at Node and remove keeped xmlNode if one is found.
882  *
883  * Return value: None
884  */
885 void
lasso_node_cleanup_original_xmlnodes(LassoNode * node)886 lasso_node_cleanup_original_xmlnodes(LassoNode *node)
887 {
888 	lasso_node_traversal(node, lasso_node_remove_original_xmlnode, 0);
889 }
890 
891 static GQuark original_xmlnode_quark;
892 static GQuark custom_element_quark;
893 
894 /**
895  * lasso_node_get_original_xmlnode:
896  * @node: a #LassoNode
897  *
898  * Retrieve the original xmlNode eventually associated to this #LassoNode.
899  *
900  * Return value:(transfer none): an #xmlNodePtr or NULL.
901  */
902 xmlNodePtr
lasso_node_get_original_xmlnode(LassoNode * node)903 lasso_node_get_original_xmlnode(LassoNode *node)
904 {
905 	return g_object_get_qdata(G_OBJECT(node), original_xmlnode_quark);
906 }
907 
original_xmlnode_free(void * node)908 static void original_xmlnode_free(void *node) {
909 	xmlNode *xnode = (xmlNode*)node;
910 
911 
912 	if (node) {
913 		if (lasso_flag_memory_debug) {
914 			fprintf(stderr, "freeing original xmlnode %s (at %p)\n", xnode->name, xnode);
915 		}
916 		xmlFreeNode(xnode);
917 	}
918 }
919 
920 /**
921  * lasso_node_set_original_xmlnode:
922  * @node: the #LassoNode object
923  * @xmlnode: an #xmlNode
924  *
925  * Set the underlying XML representation of the object.
926  *
927  */
928 void
lasso_node_set_original_xmlnode(LassoNode * node,xmlNode * xmlnode)929 lasso_node_set_original_xmlnode(LassoNode *node, xmlNode* xmlnode)
930 {
931 	if (xmlnode) {
932 		xmlNode *copy = NULL;
933 		xmlNode *parent = xmlnode->parent;
934 
935 		copy = xmlCopyNode(xmlnode, 1);
936 		/* excl-c14n can move some namespace declarations at the point where the document is
937 		 * cut, to simulate it we copy on the new node all namespaces from the parents of
938 		 * the node which are not shadowed by another declaration on this node or one of its
939 		 * parent. */
940 		while (parent && parent->type == XML_ELEMENT_NODE) {
941 			xmlNs *ns_def = parent->nsDef;
942 			xmlNs *local_ns_def;
943 			while (ns_def) {
944 				int ok = 1;
945 				local_ns_def = copy->nsDef;
946 				while (local_ns_def) {
947 					if (lasso_strisequal((char*)local_ns_def->prefix, (char*)ns_def->prefix)) {
948 						ok = 0;
949 						break;
950 					}
951 					local_ns_def = local_ns_def->next;
952 				}
953 				if (ok) {
954 					xmlNewNs(copy, ns_def->href, ns_def->prefix);
955 				}
956 				ns_def = ns_def->next;
957 			}
958 			parent = parent->parent;
959 		}
960 
961 		if (lasso_flag_memory_debug) {
962 			fprintf(stderr, "setting original xmlnode (at %p) on node %s:%p\n", copy, G_OBJECT_TYPE_NAME (node), node);
963 		}
964 		g_object_set_qdata_full(G_OBJECT(node), original_xmlnode_quark, copy, (GDestroyNotify)original_xmlnode_free);
965 	} else {
966 		if (lasso_flag_memory_debug) {
967 			fprintf(stderr, "clearing original xmlnode on node %p\n", node);
968 		}
969 		g_object_set_qdata_full(G_OBJECT(node), original_xmlnode_quark, NULL, (GDestroyNotify)original_xmlnode_free);
970 	}
971 }
972 
973 struct _CustomElement {
974 	char *prefix;
975 	char *href;
976 	char *nodename;
977 	GHashTable *namespaces;
978 	LassoSignatureContext signature_context;
979 	xmlSecKey *encryption_public_key;
980 	LassoEncryptionSymKeyType encryption_sym_key_type;
981 };
982 
983 static struct _CustomElement *
_lasso_node_new_custom_element()984 _lasso_node_new_custom_element()
985 {
986 	struct _CustomElement *ret = g_new0(struct _CustomElement, 1);
987 	ret->namespaces = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
988 	return ret;
989 }
990 
991 static void
_lasso_node_free_custom_element(struct _CustomElement * custom_element)992 _lasso_node_free_custom_element(struct _CustomElement *custom_element)
993 {
994 	if (custom_element) {
995 		lasso_release_string(custom_element->prefix);
996 		lasso_release_string(custom_element->href);
997 		lasso_release_string(custom_element->nodename);
998 		lasso_release_ghashtable(custom_element->namespaces);
999 		lasso_release_sec_key(custom_element->encryption_public_key);
1000 		lasso_release_sec_key(custom_element->signature_context.signature_key);
1001 	}
1002 	lasso_release(custom_element);
1003 }
1004 
1005 /**
1006  * _lasso_node_get_custom_element:
1007  * @node: a #LassoNode object
1008  *
1009  * Return the eventually attached custom namespace object
1010  *
1011  * Return value: NULL or an #_CustomElement structure.
1012  */
1013 static struct _CustomElement*
_lasso_node_get_custom_element(LassoNode * node)1014 _lasso_node_get_custom_element(LassoNode *node)
1015 {
1016 	if (! LASSO_NODE(node))
1017 		return NULL;
1018 	return g_object_get_qdata((GObject*)node, custom_element_quark);
1019 }
1020 
1021 static struct _CustomElement*
_lasso_node_get_custom_element_or_create(LassoNode * node)1022 _lasso_node_get_custom_element_or_create(LassoNode *node)
1023 {
1024 	struct _CustomElement *custom_element;
1025 
1026 	if (! LASSO_IS_NODE(node))
1027 		return NULL;
1028 
1029 	custom_element = _lasso_node_get_custom_element(node);
1030 	if (! custom_element) {
1031 		custom_element = _lasso_node_new_custom_element();
1032 		g_object_set_qdata_full((GObject*)node, custom_element_quark,
1033 				custom_element,
1034 				(GDestroyNotify)_lasso_node_free_custom_element);
1035 	}
1036 	return custom_element;
1037 }
1038 
1039 
1040 /**
1041  * lasso_node_set_custom_namespace:
1042  * @node: a #LassoNode object
1043  * @prefix: the prefix to use for the definition
1044  * @href: the URI of the namespace
1045  *
1046  * Set a custom namespace for an object instance, use it with object existing a lot of revision of
1047  * the nearly same namespace.
1048  */
1049 void
lasso_node_set_custom_namespace(LassoNode * node,const char * prefix,const char * href)1050 lasso_node_set_custom_namespace(LassoNode *node, const char *prefix, const char *href)
1051 {
1052 	struct _CustomElement *custom_element;
1053 
1054 	custom_element = _lasso_node_get_custom_element_or_create(node);
1055 	g_return_if_fail (custom_element != NULL);
1056 
1057 	lasso_assign_string(custom_element->prefix, prefix);
1058 	lasso_assign_string(custom_element->href, href);
1059 }
1060 
1061 /**
1062  * lasso_node_set_signature:
1063  * @node: a #LassoNode object
1064  * @signature_context: a #LassoSignatureContext structure
1065  *
1066  * Setup a signature on @node.
1067  *
1068  * Return value: 0 if successful, an error code otherwise.
1069  */
1070 int
lasso_node_set_signature(LassoNode * node,LassoSignatureContext context)1071 lasso_node_set_signature(LassoNode *node, LassoSignatureContext context)
1072 {
1073 	struct _CustomElement *custom_element;
1074 	int rc = 0;
1075 
1076 	lasso_bad_param(NODE, node);
1077 	custom_element = _lasso_node_get_custom_element_or_create(node);
1078 	g_return_val_if_fail (custom_element != NULL, LASSO_PARAM_ERROR_BAD_TYPE_OR_NULL_OBJ);
1079 
1080 	if (custom_element->signature_context.signature_key) {
1081 		lasso_release_sec_key(custom_element->signature_context.signature_key);
1082 	}
1083 	custom_element->signature_context.signature_method = context.signature_method;
1084 	lasso_assign_new_sec_key(custom_element->signature_context.signature_key,
1085 			context.signature_key);
1086 	return rc;
1087 }
1088 
1089 /**
1090  * lasso_node_get_signature:
1091  * @node: a #LassoNode object
1092  * @type: an output for the signature type
1093  * @method: an output for the signature method
1094  * @private_key: an output for the private key
1095  * @private_key_password: an output for the private key password
1096  * @certificate: an output for the certificate
1097  *
1098  * Return signature parameters stored with this node.
1099  */
1100 LassoSignatureContext
lasso_node_get_signature(LassoNode * node)1101 lasso_node_get_signature(LassoNode *node)
1102 {
1103 	struct _CustomElement *custom_element;
1104 
1105 	g_return_val_if_fail (LASSO_IS_NODE(node), LASSO_SIGNATURE_CONTEXT_NONE);
1106 	custom_element = _lasso_node_get_custom_element(node);
1107 	if (! custom_element) {
1108 		return LASSO_SIGNATURE_CONTEXT_NONE;
1109 	}
1110 	return custom_element->signature_context;
1111 }
1112 
1113 /**
1114  * lasso_node_set_encryption:
1115  * @node: a @LassoNode object
1116  * @encryption_public_key: an #xmlSecKey used to crypt the session key
1117  * @encryption_sym_key_type: the kind of session key to use
1118  *
1119  * Setup a node for future encryption. It is read by saml2:EncryptedElement for eventually
1120  * encrypting nodes.
1121  *
1122  * Return value: 0 if successful, LASSO_PARAM_ERROR_BAD_TYPE_OR_NULL_OBJ if node is not a
1123  * #LassoNode.
1124  */
1125 void
lasso_node_set_encryption(LassoNode * node,xmlSecKey * encryption_public_key,LassoEncryptionSymKeyType encryption_sym_key_type)1126 lasso_node_set_encryption(LassoNode *node, xmlSecKey *encryption_public_key,
1127 		LassoEncryptionSymKeyType encryption_sym_key_type)
1128 {
1129 	struct _CustomElement *custom_element;
1130 
1131 	g_return_if_fail(LASSO_IS_NODE(node));
1132 	if (encryption_public_key) {
1133 		custom_element = _lasso_node_get_custom_element_or_create(node);
1134 		if (! custom_element) {
1135 			return;
1136 		}
1137 	} else {
1138 		custom_element = _lasso_node_get_custom_element(node);
1139 		if (! custom_element) {
1140 			return;
1141 		}
1142 		lasso_release_sec_key(custom_element->encryption_public_key);
1143 		return;
1144 	}
1145 	lasso_assign_sec_key(custom_element->encryption_public_key,
1146 			encryption_public_key);
1147 	if (encryption_sym_key_type < LASSO_ENCRYTPION_SYM_KEY_TYPE_LAST) {
1148 		custom_element->encryption_sym_key_type = encryption_sym_key_type;
1149 	} else {
1150 		custom_element->encryption_sym_key_type = LASSO_ENCRYPTION_SYM_KEY_TYPE_DEFAULT;
1151 	}
1152 }
1153 
1154 /**
1155  * lasso_node_get_encryption:
1156  * @node: a #LassoNode object
1157  * @encryption_public_key_ptr: a pointer on a pointer to an #xmlSecKey object, to hold the the
1158  * public key used to encrypt the session key
1159  * @encryption_sym_key_type: a pointer on a #LassoEncryptionSymKeyType
1160  *
1161  * Lookup eventual configuration for encrypting the given node.
1162  */
1163 void
lasso_node_get_encryption(LassoNode * node,xmlSecKey ** encryption_public_key,LassoEncryptionSymKeyType * encryption_sym_key_type)1164 lasso_node_get_encryption(LassoNode *node, xmlSecKey **encryption_public_key,
1165 		LassoEncryptionSymKeyType *encryption_sym_key_type)
1166 {
1167 	struct _CustomElement *custom_element;
1168 
1169 	g_return_if_fail(LASSO_IS_NODE(node));
1170 	custom_element = _lasso_node_get_custom_element(node);
1171 	if (custom_element && custom_element->encryption_public_key) {
1172 		lasso_assign_sec_key(*encryption_public_key,
1173 				custom_element->encryption_public_key);
1174 		*encryption_sym_key_type = custom_element->encryption_sym_key_type;
1175 	}
1176 }
1177 
1178 /**
1179  * lasso_node_set_custom_nodename:
1180  * @node: a #LassoNode object
1181  * @nodename: the name to use for the node
1182  *
1183  * Set a custom nodename for an object instance, use it with object implement a schema type and not
1184  * a real element.
1185  */
1186 void
lasso_node_set_custom_nodename(LassoNode * node,const char * nodename)1187 lasso_node_set_custom_nodename(LassoNode *node, const char *nodename)
1188 {
1189 	struct _CustomElement *custom_element;
1190 
1191 	custom_element = _lasso_node_get_custom_element_or_create(node);
1192 	g_return_if_fail (custom_element != NULL);
1193 
1194 	lasso_assign_string(custom_element->nodename, nodename);
1195 }
1196 
1197 /**
1198  * lasso_node_add_custom_namespace:
1199  * @prefix: prefix name
1200  * @href: href url
1201  *
1202  * Add a custom namespace declaration at this node level
1203  */
1204 void
lasso_node_add_custom_namespace(LassoNode * node,const char * prefix,const char * href)1205 lasso_node_add_custom_namespace(LassoNode *node, const char *prefix,
1206 		const char *href)
1207 {
1208 	struct _CustomElement *custom_element;
1209 
1210 	custom_element = _lasso_node_get_custom_element_or_create(node);
1211 	g_return_if_fail(custom_element != NULL);
1212 
1213 	g_hash_table_insert(custom_element->namespaces, g_strdup(prefix), g_strdup(href));
1214 }
1215 
1216 /*****************************************************************************/
1217 /* implementation methods                                                    */
1218 /*****************************************************************************/
1219 
1220 static void
lasso_node_remove_original_xmlnode(LassoNode * node,SnippetType type)1221 lasso_node_remove_original_xmlnode(LassoNode *node, SnippetType type) {
1222 	LassoNodeClass *class;
1223 	class = LASSO_NODE_GET_CLASS(node);
1224 
1225 	if (class->node_data->keep_xmlnode || type & SNIPPET_KEEP_XMLNODE) {
1226 		lasso_node_set_original_xmlnode(node, NULL);
1227 	}
1228 }
1229 
1230 static void
lasso_node_traversal(LassoNode * node,void (* do_to_node)(LassoNode * node,SnippetType type),SnippetType type)1231 lasso_node_traversal(LassoNode *node, void (*do_to_node)(LassoNode *node, SnippetType type), SnippetType type) {
1232 	LassoNodeClass *class;
1233 	struct XmlSnippet *snippet;
1234 
1235 	if (node == NULL || do_to_node == NULL) {
1236 		return;
1237 	}
1238 	class = LASSO_NODE_GET_CLASS(node);
1239 	do_to_node(node, type);
1240 
1241 	while (class && LASSO_IS_NODE_CLASS(class) && class->node_data) {
1242 		GType g_type = G_TYPE_FROM_CLASS(class);
1243 		snippet = class->node_data->snippets;
1244 		while (snippet->name != NULL) {
1245 			SnippetType type;
1246 			void **value = SNIPPET_STRUCT_MEMBER_P(node, g_type, snippet);
1247 
1248 			type = snippet->type & 0xff;
1249 			switch (type) {
1250 				case SNIPPET_NODE:
1251 				case SNIPPET_NODE_IN_CHILD:
1252 					lasso_node_traversal(*value, do_to_node, snippet->type);
1253 					break;
1254 				case SNIPPET_LIST_NODES:
1255 					{
1256 						GList *list = *value;
1257 						while (list != NULL) {
1258 							if (list->data) {
1259 								lasso_node_traversal(LASSO_NODE(list->data), do_to_node, snippet->type);
1260 							}
1261 							list = g_list_next(list);
1262 						}
1263 					}
1264 					break;
1265 				case SNIPPET_UNUSED1:
1266 					g_assert_not_reached();
1267 				default:
1268 					break;
1269 			}
1270 			snippet++;
1271 		}
1272 		class = g_type_class_peek_parent(class);
1273 	}
1274 }
1275 
1276 static void
lasso_node_impl_destroy(LassoNode * node)1277 lasso_node_impl_destroy(LassoNode *node)
1278 {
1279 	g_object_unref(G_OBJECT(node));
1280 }
1281 #define trace_snippet(format, args...) \
1282 	lasso_trace(format "%s.%s\n", ## args, G_OBJECT_TYPE_NAME(node), snippet->name)
1283 
1284 /**
1285  * _lasso_node_collect_namespaces:
1286  * @namespaces: a pointer to a pointer on a #GHashTable
1287  * @node: an #xmlNode pointer
1288  *
1289  * Follow the parent link of the @node to collect all declared namespaces, it is usefull for content
1290  * that need to be interpreted with respect to declared namespaces (XPath for example).
1291  */
1292 void
_lasso_node_collect_namespaces(GHashTable ** namespaces,xmlNode * node)1293 _lasso_node_collect_namespaces(GHashTable **namespaces, xmlNode *node)
1294 {
1295 	if (*namespaces == NULL) {
1296 		*namespaces = g_hash_table_new_full( g_str_hash, g_str_equal, g_free, g_free);
1297 	}
1298 	while (node) {
1299 		if (node->type == XML_ELEMENT_NODE) {
1300 			xmlNs *nsDef = node->nsDef;
1301 			while (nsDef) {
1302 				if (nsDef->prefix && nsDef->href) {
1303 					g_hash_table_insert(*namespaces, g_strdup((char*)nsDef->prefix),
1304 							g_strdup((char*)nsDef->href));
1305 				}
1306 				nsDef = nsDef->next;
1307 			}
1308 		}
1309 		node = node->parent;
1310 	}
1311 }
1312 
1313 gboolean
lasso_get_integer_attribute(xmlNode * node,xmlChar * attribute_name,xmlChar * ns_href,int * integer,long int low,long int high)1314 lasso_get_integer_attribute(xmlNode *node, xmlChar *attribute_name, xmlChar *ns_href, int *integer, long int low, long int high) {
1315 	xmlChar *content = NULL;
1316 	gboolean rc = FALSE;
1317 	long int what;
1318 
1319 	g_assert (integer);
1320 	content = xmlGetNsProp(node, attribute_name, ns_href);
1321 	if (! content)
1322 		goto cleanup;
1323 	if (! lasso_string_to_xsd_integer((char*)content, &what))
1324 		goto cleanup;
1325 	if (what < low || what >= high)
1326 		goto cleanup;
1327 	*integer = what;
1328 	rc = TRUE;
1329 cleanup:
1330 	lasso_release_xml_string(content);
1331 	return rc;
1332 }
1333 
1334 static inline gboolean
lasso_equal_namespace(xmlNs * t1,xmlNs * t2)1335 lasso_equal_namespace(xmlNs *t1, xmlNs *t2) {
1336 	return t1 && t2 && (t1 == t2 ||
1337 			lasso_strisequal((char*)t1->href, (char*)t2->href));
1338 }
1339 
1340 static void
snippet_set_value(LassoNode * node,LassoNodeClass * class,struct XmlSnippet * snippet,xmlChar * content)1341 snippet_set_value(LassoNode *node, LassoNodeClass *class, struct XmlSnippet *snippet, xmlChar *content) {
1342 	void *value;
1343 	GType g_type = G_TYPE_FROM_CLASS(class);
1344 
1345 	/* If not offset, it means it is handled by an adhoc init_from_xml */
1346 	if (! snippet->offset && ! (snippet->type & SNIPPET_PRIVATE)) {
1347 		return;
1348 	}
1349 	value = SNIPPET_STRUCT_MEMBER_P(node, g_type, snippet);
1350 	if (snippet->type & SNIPPET_INTEGER) {
1351 		int val = strtol((char*)content, NULL, 10);
1352 		if (((val == INT_MIN || val == INT_MAX) && errno == ERANGE)
1353 				|| errno == EINVAL || val < 0) {
1354 			if (snippet->type & SNIPPET_OPTIONAL_NEG) {
1355 				val = -1;
1356 			} else {
1357 				val = 0;
1358 			}
1359 		}
1360 		(*(int*)value) = val;
1361 	} else if (snippet->type & SNIPPET_BOOLEAN) {
1362 		int val = 0;
1363 		if (strcmp((char*)content, "true") == 0) {
1364 			val = 1;
1365 		} else if (strcmp((char*)content, "1") == 0) {
1366 			val = 1;
1367 		}
1368 		(*(int*)value) = val;
1369 	} else {
1370 		lasso_assign_string((*(char**)value), (char*)content);
1371 		if (lasso_flag_memory_debug == TRUE) {
1372 			fprintf(stderr, "   setting prop %s/%s to value %p: %s\n",
1373 					G_OBJECT_TYPE_NAME(node), snippet->name, *(void**)value, (char*)content);
1374 		}
1375 	}
1376 }
1377 
1378 gboolean
next_node_snippet(GSList ** class_iter_p,struct XmlSnippet ** snippet_p)1379 next_node_snippet(GSList **class_iter_p, struct XmlSnippet **snippet_p)
1380 {
1381 	while (*class_iter_p) {
1382 		if (*snippet_p) {
1383 			if ((*snippet_p)->name) {
1384 				SnippetType type = (*snippet_p)->type;
1385 				/* special case for ArtifactResponse */
1386 				if (type & SNIPPET_ANY && (type & 0xff) == SNIPPET_NODE)
1387 					return TRUE;
1388 				if (! (type & SNIPPET_ANY) && (*snippet_p)->name[0] != '\0') {
1389 					switch (type & 0xff) {
1390 						case SNIPPET_NODE:
1391 						case SNIPPET_NODE_IN_CHILD:
1392 						case SNIPPET_LIST_XMLNODES:
1393 						case SNIPPET_LIST_CONTENT:
1394 						case SNIPPET_LIST_NODES:
1395 						case SNIPPET_EXTENSION:
1396 						case SNIPPET_XMLNODE:
1397 						case SNIPPET_CONTENT:
1398 						case SNIPPET_SIGNATURE:
1399 							return TRUE;
1400 						default:
1401 							break;
1402 					}
1403 				}
1404 				++*snippet_p;
1405 			} else {
1406 				*class_iter_p = g_slist_next(*class_iter_p);
1407 				*snippet_p = NULL;
1408 			}
1409 		} else {
1410 			*snippet_p = ((LassoNodeClass*)(*class_iter_p)->data)
1411 						->node_data->snippets;
1412 		}
1413 	}
1414 	return FALSE;
1415 }
1416 
1417 static inline gboolean
is_snippet_type(struct XmlSnippet * snippet,SnippetType simple_type)1418 is_snippet_type(struct XmlSnippet *snippet, SnippetType simple_type) {
1419 	return (snippet->type & 0xff) == simple_type;
1420 }
1421 
1422 static inline gboolean
is_snippet_mandatory(struct XmlSnippet * snippet)1423 is_snippet_mandatory(struct XmlSnippet *snippet)
1424 {
1425 	return snippet->type & SNIPPET_MANDATORY ? TRUE : FALSE;
1426 }
1427 
1428 static inline gboolean
is_snippet_multiple(struct XmlSnippet * snippet)1429 is_snippet_multiple(struct XmlSnippet *snippet)
1430 {
1431 	switch (snippet->type & 0xff) {
1432 		case SNIPPET_LIST_XMLNODES:
1433 		case SNIPPET_LIST_CONTENT:
1434 		case SNIPPET_LIST_NODES:
1435 		case SNIPPET_EXTENSION:
1436 			return TRUE;
1437 		default:
1438 			return FALSE;
1439 	}
1440 }
1441 
1442 static inline gboolean
node_match_snippet(xmlNode * parent,xmlNode * node,struct XmlSnippet * snippet)1443 node_match_snippet(xmlNode *parent, xmlNode *node, struct XmlSnippet *snippet)
1444 {
1445 	/* special case of ArtifactResponse */
1446 	if (snippet->type & SNIPPET_ANY) {
1447 		message(LOG_LEVEL_XML_DEBUG, "Matching node %s vs SNIPPET_ANY: SUCCESS", node->name);
1448 		return TRUE;
1449 	} else {
1450 		if (!lasso_strisequal(snippet->name, (char*)node->name)) {
1451 			message(LOG_LEVEL_XML_DEBUG, "Matching node %s vs snippet %s: FAILURE names don't match", node->name, snippet->name);
1452 			return FALSE;
1453 		}
1454 
1455 		if ((snippet->ns_uri == NULL) && lasso_equal_namespace(parent->ns, node->ns)) {
1456 			message(LOG_LEVEL_XML_DEBUG, "Matching node %s vs snippet %s: SUCCESS namespace prefixes match", node->name, snippet->name);
1457 			return TRUE;
1458 		}
1459 
1460 		if ((node->ns != NULL) && lasso_strisequal((char*)node->ns->href, snippet->ns_uri)) {
1461 			message(LOG_LEVEL_XML_DEBUG, "Matching node %s vs snippet %s: SUCCESS namespace URIs match", node->name, snippet->name);
1462 			return TRUE;
1463 		}
1464 
1465 		message(LOG_LEVEL_XML_DEBUG, "Matching node %s vs snippet %s: FAILURE", node->name, snippet->name);
1466 		return FALSE;
1467 	}
1468 }
1469 
1470 /** FIXME: return a real error code */
1471 static int
lasso_node_impl_init_from_xml(LassoNode * node,xmlNode * xmlnode)1472 lasso_node_impl_init_from_xml(LassoNode *node, xmlNode *xmlnode)
1473 {
1474 	struct XmlSnippet *snippet;
1475 	xmlNode *t;
1476 	LassoNodeClass *class;
1477 	void *value;
1478 	SnippetType type;
1479 	struct XmlSnippet *snippet_any = NULL;
1480 	struct XmlSnippet *snippet_any_attribute = NULL;
1481 	GType g_type_collect_namespaces = 0, g_type_any = 0, g_type_any_attribute = 0;
1482 	struct XmlSnippet *snippet_collect_namespaces = NULL;
1483 	struct XmlSnippet *snippet_signature = NULL;
1484 	gboolean keep_xmlnode = FALSE;
1485 	GSList *class_list = NULL;
1486 	GSList *class_iter = NULL;
1487 	xmlAttr *attr = NULL;
1488 	GType g_type = 0;
1489 	LassoNodeClass *node_class;
1490 	gint rc = 0;
1491 
1492 	message(LOG_LEVEL_XML_DEBUG, "lasso_node_impl_init_from_xml <%s>", xmlnode->name);
1493 
1494 	if (! xmlnode) {
1495 		rc = 1;
1496 		goto cleanup;
1497 	}
1498 
1499 	node_class = class = LASSO_NODE_GET_CLASS(node);
1500 	/* No node_data no initialization possible */
1501 	if (! class->node_data) {
1502 		message(G_LOG_LEVEL_WARNING, "Class %s has no node_data so no initialization "
1503 				"is possible", G_OBJECT_CLASS_NAME(class));
1504 		rc = 1;
1505 		goto cleanup;
1506 	}
1507 
1508 	/* check node href and name match the node_data */
1509 	{
1510 		gboolean name_mismatch = FALSE;
1511 		LassoNodeClass *check_class = class;
1512 
1513 		/* if node is an xsi subtype of a real element, find the real element class */
1514 		while (check_class->node_data->xsi_sub_type) {
1515 			check_class = g_type_class_peek_parent(check_class);
1516 		}
1517 
1518 		name_mismatch = name_mismatch || lasso_strisnotequal((char*)check_class->node_data->node_name, (char*)xmlnode->name);
1519 		name_mismatch = name_mismatch || ! lasso_equal_namespace(check_class->node_data->ns, xmlnode->ns);
1520 		if (xmlnode->ns) {
1521 			name_mismatch = name_mismatch && lasso_strisnotequal(
1522 					G_OBJECT_CLASS_NAME(class),
1523 					lasso_registry_default_get_mapping((char*)xmlnode->ns->href,
1524 									   (char*)xmlnode->name,
1525 									   LASSO_LASSO_HREF));
1526 		}
1527 		if (name_mismatch) {
1528 			warning("lasso_node_impl_init_from_xml: expected name an href do not match node, expected %s:%s received %s:%s",
1529 					class->node_data->ns ? class->node_data->ns->href : NULL, class->node_data->node_name,
1530 					xmlnode->ns ? xmlnode->ns->href : NULL, xmlnode->name);
1531 			rc = 1;
1532 			goto cleanup;
1533 		}
1534 	}
1535 
1536 	/* Collect special snippets like SNIPPET_COLLECT_NAMESPACES, SNIPPET_ANY, SNIPPET_ATTRIBUTE
1537 	 * or SNIPPET_SIGNATURE, and initialize class_list in reverse. */
1538 	while (class && LASSO_IS_NODE_CLASS(class)) {
1539 		if (class->node_data) {
1540 			GType g_type = G_TYPE_FROM_CLASS(class);
1541 			keep_xmlnode |= class->node_data->keep_xmlnode;
1542 			if (class->node_data->snippets)
1543 				class_list = g_slist_prepend(class_list, class);
1544 			for (snippet = class->node_data->snippets; snippet && snippet->name; snippet++) {
1545 				type = snippet->type & 0xff;
1546 
1547 				if (snippet->name && snippet->name[0] == '\0' && type ==
1548 						SNIPPET_COLLECT_NAMESPACES) {
1549 					snippet_collect_namespaces = snippet;
1550 					g_type_collect_namespaces = g_type;
1551 				} else if (type == SNIPPET_SIGNATURE) {
1552 					snippet_signature = snippet;
1553 				} else if (type == SNIPPET_ATTRIBUTE && snippet->type & SNIPPET_ANY) {
1554 					g_type_any_attribute = g_type;
1555 					snippet_any_attribute = snippet;
1556 				} else if (type == SNIPPET_TEXT_CHILD) {
1557 					xmlChar *tmp = xmlNodeGetContent(xmlnode);
1558 					snippet_set_value(node, class, snippet, tmp);
1559 					lasso_release_xml_string(tmp);
1560 				} else if (type != SNIPPET_ATTRIBUTE && type != SNIPPET_NODE && snippet->type & SNIPPET_ANY) {
1561 					if (! snippet_any) {
1562 						g_type_any = g_type;
1563 						snippet_any = snippet;
1564 					} else {
1565 						message(G_LOG_LEVEL_CRITICAL, "Two 'any' node snippet for class %s",
1566 								g_type_name(G_TYPE_FROM_INSTANCE(node)));
1567 					}
1568 				}
1569 			}
1570 		}
1571 		class = g_type_class_peek_parent(class);
1572 	}
1573 
1574 	/* If any class asked for keeping the xmlNode, keep it around */
1575 	if (keep_xmlnode) {
1576 		lasso_node_set_original_xmlnode(node, xmlnode);
1577 	}
1578 
1579 	/** Collect attributes */
1580 	for (attr = xmlnode->properties; attr; attr = attr->next) {
1581 		xmlChar *content;
1582 		content = xmlNodeGetContent((xmlNode*)attr);
1583 		int ok = 0;
1584 
1585 		/* Skip xsi:type if it was used to find the node class */
1586 		if (attr->ns && lasso_strisequal((char*)attr->name, "type") &&
1587 				lasso_strisequal((char*)attr->ns->href, LASSO_XSI_HREF)) {
1588 			char *colon = strchr((char*)content, ':');
1589 			if (colon) {
1590 				xmlNs *ns;
1591 				*colon = '\0';
1592 				ns = xmlSearchNs(NULL, xmlnode, content);
1593 				*colon = ':';
1594 				if (ns && lasso_strisequal((char*)ns->href, (char*)node_class->node_data->ns->href)
1595 						&& lasso_strisequal(&colon[1], node_class->node_data->node_name)) {
1596 					lasso_release_xml_string(content);
1597 					continue;
1598 				}
1599 			}
1600 		}
1601 
1602 		for (class_iter = class_list; class_iter; class_iter = class_iter->next) {
1603 			class = class_iter->data;
1604 			for (snippet = class->node_data->snippets;
1605 					snippet && snippet->name; snippet++) {
1606 				type = snippet->type & 0xff;
1607 				/* assign attribute content if attribute has the same name as the
1608 				 * snippet and:
1609 				 * - the snippet and the attribute have no namespace
1610 				 * - the snippet has no namespace but the attribute has the same
1611 				 *   namespace as the node
1612 				 * - the snippet and the node have a namespace, which are equal.
1613 				 */
1614 				if (type != SNIPPET_ATTRIBUTE)
1615 					continue;
1616 				if (! lasso_strisequal((char*)attr->name, (char*)snippet->name))
1617 					continue;
1618 				if (attr->ns) {
1619 					gboolean same_namespace, given_namespace;
1620 
1621 					same_namespace = lasso_equal_namespace(attr->ns,
1622 							xmlnode->ns) && ! snippet->ns_uri;
1623 					given_namespace = snippet->ns_uri &&
1624 						lasso_strisequal((char*)attr->ns->href,
1625 								snippet->ns_uri);
1626 					if (! same_namespace && ! given_namespace)
1627 						break;
1628 				}
1629 				snippet_set_value(node, class, snippet, content);
1630 				ok = 1;
1631 				break;
1632 			}
1633 		}
1634 		if (! ok && attr->ns && snippet_any_attribute) {
1635 			GHashTable **any_attribute;
1636 			gchar *key;
1637 
1638 			any_attribute = SNIPPET_STRUCT_MEMBER_P(node, g_type_any_attribute,
1639 					snippet_any_attribute);
1640 			if (*any_attribute == NULL) {
1641 				*any_attribute = g_hash_table_new_full(g_str_hash, g_str_equal,
1642 						g_free, g_free);
1643 			}
1644 			if (lasso_equal_namespace(attr->ns, xmlnode->ns)) {
1645 				key = g_strdup((char*)attr->name);
1646 			} else {
1647 				key = g_strdup_printf("{%s}%s", attr->ns->href, attr->name);
1648 			}
1649 			g_hash_table_insert(*any_attribute, key, g_strdup((char*)content));
1650 			lasso_release_xml_string(content);
1651 		} else if (! ok) {
1652 			if (! attr->ns || (lasso_strisnotequal((char*)attr->ns->href, LASSO_XSI_HREF) &&
1653 					   lasso_strisnotequal((char*)attr->ns->href, LASSO_XML_HREF))) {
1654 				warning("lasso_node_impl_init_from_xml: Unexpected attribute: {%s}%s = %s",
1655 						attr->ns ? attr->ns->href : NULL, attr->name, content);
1656 			}
1657 		}
1658 		lasso_release_xml_string(content);
1659 	}
1660 
1661 	/* Collect children nodes in reverse order of class parents (older parent first), skip non
1662 	 * node and ANY snippets) */
1663 	class_iter = class_list;
1664 	snippet = ((LassoNodeClass*)class_iter->data)->node_data->snippets;
1665 	next_node_snippet(&class_iter, &snippet);
1666 	for (t = xmlnode->children; t && class_iter && snippet; t = t->next) {
1667 		/* Only collect text node if:
1668 		 * - there is a LIST_XMLNODES any snippet
1669 		 * - there is a LIST_NODES any snippet with the ALLOW_TEXT modifier
1670 		 */
1671 		if (t->type == XML_TEXT_NODE && snippet_any &&
1672 				(is_snippet_type(snippet_any, SNIPPET_LIST_XMLNODES)
1673 				 || (is_snippet_type(snippet_any, SNIPPET_LIST_NODES) &&
1674 					 (snippet_any->type & SNIPPET_ALLOW_TEXT)))) {
1675 			GList **location = SNIPPET_STRUCT_MEMBER_P(node, g_type_any, snippet_any);
1676 			if (is_snippet_type(snippet_any, SNIPPET_LIST_XMLNODES)) {
1677 				lasso_list_add_xml_node(*location, t);
1678 			} else {
1679 				lasso_list_add_new_gobject(*location,
1680 						lasso_node_new_from_xmlNode_with_type(t,
1681 							"LassoMiscTextNode"));
1682 			}
1683 		} else if (t->type == XML_COMMENT_NODE || t->type == XML_PI_NODE || t->type == XML_TEXT_NODE) {
1684 			/* ignore comments */
1685 			continue;
1686 		} else if (t->type == XML_ELEMENT_NODE) {
1687 			LassoNode *subnode = NULL;
1688 			xmlNode *first_child = NULL;
1689 			GList **list = NULL;
1690 			xmlChar *content = NULL;
1691 			gboolean match = FALSE;
1692 			struct XmlSnippet *matched_snippet = NULL;
1693 
1694 #define ADVANCE_MATCH \
1695 				if (snippet->type & SNIPPET_JUMP_ON_MATCH) { \
1696 					snippet += (ptrdiff_t)SNIPPET_JUMP_OFFSET(snippet->type); \
1697 				}  else { \
1698 					snippet++; \
1699 				} \
1700 				next_node_snippet(&class_iter, &snippet);
1701 #define ADVANCE_MISS \
1702 				if (snippet->type & SNIPPET_JUMP_ON_MISS) { \
1703 					snippet += (ptrdiff_t)SNIPPET_JUMP_OFFSET(snippet->type); \
1704 				}  else { \
1705 					snippet++; \
1706 				} \
1707 				next_node_snippet(&class_iter, &snippet);
1708 #define ERROR \
1709 				message(G_LOG_LEVEL_CRITICAL, "Element <%s:%s> was not expected at this location inside element <%s>. " \
1710 						"Please ensure the XML correctly follows XML schema", \
1711 						(t->ns == NULL) ? "<noprefix>" : ((t->ns->prefix == NULL)? (char*)t->ns->href : (char*)t->ns->prefix), \
1712 						t->name, \
1713 						xmlnode->name); \
1714 				rc = 1; \
1715 				goto cleanup;
1716 			/* Find a matching snippet */
1717 			while (class_iter && snippet) {
1718 				gboolean mandatory = is_snippet_mandatory(snippet);
1719 				gboolean multiple = is_snippet_multiple(snippet);
1720 
1721 				if ((match = node_match_snippet(xmlnode, t, snippet))) {
1722 					matched_snippet = snippet;
1723 					class = class_iter->data;
1724 					g_type = G_TYPE_FROM_CLASS(class);
1725 					value = SNIPPET_STRUCT_MEMBER_P(node, g_type, snippet);
1726 					list = value;
1727 					if (! multiple || (snippet->type & SNIPPET_JUMP_ON_MATCH)) {
1728 						ADVANCE_MATCH;
1729 					}
1730 					break;
1731 				} else {
1732 					if (mandatory) {
1733 						break;
1734 					} else {
1735 						ADVANCE_MISS;
1736 					}
1737 				}
1738 			}
1739 			if (! match) {
1740 				ERROR;
1741 			}
1742 #undef ADVANCE
1743 #undef ERROR
1744 
1745 			if (matched_snippet->offset || (matched_snippet->type & SNIPPET_PRIVATE)) {
1746 				switch (matched_snippet->type & 0xff) {
1747 					case SNIPPET_LIST_NODES:
1748 					case SNIPPET_NODE:
1749 						subnode = lasso_node_new_from_xmlNode_with_type(t,
1750 								matched_snippet->class_name);
1751 						if (subnode == NULL) {
1752 							message(G_LOG_LEVEL_CRITICAL, "Failed to create LassoNode from XML node");
1753 						}
1754 						else {
1755 							if (is_snippet_type(matched_snippet, SNIPPET_NODE)) {
1756 								lasso_assign_new_gobject(*(LassoNode**)value, subnode);
1757 							}
1758 							else {
1759 								lasso_list_add_new_gobject(*list, subnode);
1760 							}
1761 						}
1762 						break;
1763 					case SNIPPET_NODE_IN_CHILD:
1764 						first_child = xmlSecGetNextElementNode(t->children);
1765 						if (first_child) {
1766 							subnode = lasso_node_new_from_xmlNode_with_type(first_child,
1767 										matched_snippet->class_name);
1768 							lasso_assign_new_gobject(*(LassoNode**)value, subnode);
1769 						}
1770 						break;
1771 					case SNIPPET_XMLNODE:
1772 						lasso_assign_xml_node(*(xmlNode**)value, t);
1773 						break;
1774 					case SNIPPET_LIST_XMLNODES:
1775 					case SNIPPET_EXTENSION:
1776 						lasso_list_add_xml_node(*list, t);
1777 						break;
1778 					case SNIPPET_CONTENT:
1779 					case SNIPPET_LIST_CONTENT:
1780 						content = xmlNodeGetContent(t);
1781 						if (is_snippet_type(matched_snippet, SNIPPET_CONTENT)) {
1782 							snippet_set_value(node, class, matched_snippet, content);
1783 						} else { /* only list of string-like xsd:type supported */
1784 							lasso_list_add_string(*list, (char*)content);
1785 						}
1786 						lasso_release_xml_string(content);
1787 						break;
1788 					case SNIPPET_SIGNATURE:
1789 						/* We ignore it */
1790 						break;
1791 					default:
1792 						g_assert_not_reached();
1793 
1794 				}
1795 			}
1796 			/* When creating a new LassoNode and option KEEP_XMLNODE is present,
1797 			 * we attached the xmlNode to the LassoNode */
1798 			if (subnode && (matched_snippet->type & SNIPPET_KEEP_XMLNODE)) {
1799 				lasso_node_set_original_xmlnode(subnode, t);
1800 			}
1801 		} else {
1802 			g_assert_not_reached();
1803 		}
1804 	}
1805 	if (t) { /* t is an ELEMENT that dont match any snippet, when taken in order */
1806 		if (snippet_any && is_snippet_type(snippet_any, SNIPPET_LIST_XMLNODES)) {
1807 			value = SNIPPET_STRUCT_MEMBER_P(node, g_type_any, snippet_any);
1808 			GList **list = value;
1809 			for (; t; t = t->next) {
1810 				lasso_list_add_xml_node(*list, t);
1811 			}
1812 		} else if (snippet_any && is_snippet_type(snippet_any, SNIPPET_LIST_NODES)) {
1813 			value = SNIPPET_STRUCT_MEMBER_P(node, g_type_any, snippet_any);
1814 			GList **list = value;
1815 			for (; t; t = t->next) {
1816 				LassoNode *subnode = NULL;
1817 
1818 				if (t->type == XML_TEXT_NODE && (snippet_any->type &
1819 							SNIPPET_ALLOW_TEXT)) {
1820 					lasso_list_add_new_gobject(*list,
1821 							lasso_node_new_from_xmlNode_with_type(t,
1822 								"LassoMiscTextNode"));
1823 				} else if (t->type == XML_ELEMENT_NODE) {
1824 					subnode = lasso_node_new_from_xmlNode_with_type(t,
1825 							snippet_any->class_name);
1826 					if (subnode && (snippet_any->type & SNIPPET_KEEP_XMLNODE)) {
1827 						lasso_node_set_original_xmlnode(subnode, t);
1828 					}
1829 					if (! subnode) {
1830 						subnode = (LassoNode*)
1831 							lasso_misc_text_node_new_with_xml_node(t);
1832 					}
1833 					lasso_list_add_new_gobject(*list, subnode);
1834 				}
1835 			}
1836 		} else if (snippet_any) {
1837 			g_assert_not_reached();
1838 		} else {
1839 			for (; t; t = t->next) {
1840 				if (t->type == XML_ELEMENT_NODE) {
1841 					critical("lasso_node_impl_init_from_xml: Cannot match "
1842 							"element {%s}%s with a snippet of "
1843 							"class %s",
1844 							t->ns ?  t->ns->href : NULL, t->name,
1845 							g_type_name(G_TYPE_FROM_INSTANCE(node)));
1846 					rc = 1;
1847 					goto cleanup;
1848 				}
1849 			}
1850 		}
1851 	}
1852 
1853 	/* Collect namespaces on the current node */
1854 	if (snippet_collect_namespaces) {
1855 		void *value = SNIPPET_STRUCT_MEMBER_P(node, g_type_collect_namespaces,
1856 				snippet_collect_namespaces);
1857 		_lasso_node_collect_namespaces(value, xmlnode);
1858 	}
1859 
1860 	/* Collect signature parameters */
1861 	{
1862 			LassoSignatureMethod method = 0;
1863 			xmlChar *private_key = NULL;
1864 			xmlChar *private_key_password = NULL;
1865 			xmlChar *certificate = NULL;
1866 			LassoSignatureContext signature_context = LASSO_SIGNATURE_CONTEXT_NONE;
1867 
1868 		while (snippet_signature) {
1869 			int what;
1870 			if (! lasso_get_integer_attribute(xmlnode, LASSO_SIGNATURE_METHOD_ATTRIBUTE,
1871 						BAD_CAST LASSO_LIB_HREF, &what,
1872 						LASSO_SIGNATURE_METHOD_RSA_SHA1,
1873 						LASSO_SIGNATURE_METHOD_LAST))
1874 				break;
1875 			method = what;
1876 			if (! lasso_get_integer_attribute(xmlnode, LASSO_SIGNATURE_METHOD_ATTRIBUTE,
1877 					BAD_CAST LASSO_LIB_HREF, &what, LASSO_SIGNATURE_TYPE_NONE+1,
1878 					LASSO_SIGNATURE_TYPE_LAST))
1879 				break;
1880 			private_key_password = xmlGetNsProp(xmlnode, LASSO_PRIVATE_KEY_PASSWORD_ATTRIBUTE,
1881 				BAD_CAST LASSO_LIB_HREF);
1882 			if (! private_key)
1883 				break;
1884 			private_key = xmlGetNsProp(xmlnode, LASSO_PRIVATE_KEY_ATTRIBUTE, BAD_CAST
1885 				LASSO_LIB_HREF);
1886 			certificate = xmlGetNsProp(xmlnode, LASSO_CERTIFICATE_ATTRIBUTE, BAD_CAST
1887 				LASSO_LIB_HREF);
1888 
1889 			signature_context.signature_method = method;
1890 			signature_context.signature_key = lasso_xmlsec_load_private_key((char*) private_key,
1891 					(char*) private_key_password, method, (char*) certificate);
1892 			lasso_node_set_signature(node, signature_context);
1893 			break;
1894 		}
1895 		lasso_release_xml_string(private_key);
1896 		lasso_release_xml_string(private_key_password);
1897 		lasso_release_xml_string(certificate);
1898 	}
1899 cleanup:
1900 	lasso_release_slist(class_list);
1901 	message(LOG_LEVEL_XML_DEBUG, "lasso_node_impl_init_from_xml </%s> rc=%d", xmlnode->name, rc);
1902 	return rc;
1903 }
1904 #undef trace_snippet
1905 
1906 /**
1907  * lasso_node_remove_signature:
1908  * @node: a #LassoNode object
1909  *
1910  * Remove any signature setup on this node.
1911  */
1912 void
lasso_node_remove_signature(LassoNode * node)1913 lasso_node_remove_signature(LassoNode *node) {
1914        LassoNodeClass *klass;
1915 
1916        if (! LASSO_IS_NODE(node))
1917                return;
1918        klass = LASSO_NODE_GET_CLASS(node);
1919        /* follow the class parenting chain */
1920        while (klass && LASSO_IS_NODE_CLASS(klass)) {
1921                if (klass && klass->node_data && klass->node_data->sign_type_offset != 0) {
1922                        G_STRUCT_MEMBER(LassoSignatureType, node, klass->node_data->sign_type_offset) =
1923                                LASSO_SIGNATURE_TYPE_NONE;
1924                }
1925                klass = g_type_class_peek_parent(klass);
1926        }
1927        lasso_node_set_signature(node, LASSO_SIGNATURE_CONTEXT_NONE);
1928 }
1929 
1930 /*****************************************************************************/
1931 /* private methods                                                           */
1932 /*****************************************************************************/
1933 
1934 static void
_xmlnode_add_custom_namespace(const char * prefix,const char * href,xmlNode * xmlnode)1935 _xmlnode_add_custom_namespace(const char *prefix, const char *href, xmlNode *xmlnode)
1936 {
1937 	xmlNs *existing = NULL;
1938 
1939 	existing = xmlSearchNs(NULL, xmlnode, BAD_CAST prefix);
1940 	if (existing) {
1941 		if (lasso_strisnotequal((char *)existing->href,href)) {
1942 			message(G_LOG_LEVEL_CRITICAL, "Cannot add namespace %s='%s' to node %s, "
1943 					"namespace already exists with another href", prefix, href,
1944 					(char*)xmlnode->name);
1945 		}
1946 		return;
1947 	}
1948 	xmlNewNs(xmlnode, BAD_CAST href, BAD_CAST prefix);
1949 }
1950 
1951 static char*
lasso_node_impl_build_query(LassoNode * node)1952 lasso_node_impl_build_query(LassoNode *node)
1953 {
1954 	return lasso_node_build_query_from_snippets(node);
1955 }
1956 
1957 static xmlNode*
lasso_node_impl_get_xmlNode(LassoNode * node,gboolean lasso_dump)1958 lasso_node_impl_get_xmlNode(LassoNode *node, gboolean lasso_dump)
1959 {
1960 	LassoNodeClass *class = LASSO_NODE_GET_CLASS(node);
1961 	LassoNodeClass *version_class = NULL;
1962 	xmlNode *xmlnode;
1963 	xmlNs *ns = NULL;
1964 	GSList *list_classes = NULL, *iter_classes = NULL;
1965 	LassoNode *value_node;
1966 	struct XmlSnippet *version_snippet;
1967 	struct _CustomElement *custom_element;
1968 	LassoNodeClass *xsi_sub_type_data_class = NULL;
1969 	LassoNodeClass *node_name_class = class;
1970 
1971 	while (node_name_class->node_data->xsi_sub_type) {
1972 		node_name_class= g_type_class_peek_parent(node_name_class);
1973 	}
1974 	if (node_name_class != class) {
1975 		xsi_sub_type_data_class = class;
1976 	}
1977 	g_assert(node_name_class && node_name_class->node_data &&
1978 			node_name_class->node_data->node_name);
1979 
1980 	/* Create node in its namespace */
1981 	xmlnode = xmlNewNode(NULL, (xmlChar*)node_name_class->node_data->node_name);
1982 	if (node_name_class->node_data->ns) {
1983 		ns = get_or_define_ns(xmlnode, node_name_class->node_data->ns->href,
1984 				node_name_class->node_data->ns->prefix);
1985 		xmlSetNs(xmlnode, ns);
1986 	}
1987 	/* If subtype, set an xsi:type attribute */
1988 	if (xsi_sub_type_data_class) {
1989 		set_xsi_type(xmlnode,
1990 				xsi_sub_type_data_class->node_data->ns->prefix,
1991 				xsi_sub_type_data_class->node_data->ns->href,
1992 				BAD_CAST xsi_sub_type_data_class->node_data->node_name);
1993 	}
1994 	custom_element = _lasso_node_get_custom_element(node);
1995 
1996 	/* collect all classes in reverse order */
1997 	while (class && LASSO_IS_NODE_CLASS(class)) {
1998 		if (class->node_data && class->node_data->snippets)
1999 			list_classes = g_slist_prepend(list_classes, class);
2000 		class = g_type_class_peek_parent(class);
2001 	}
2002 
2003 	/* set a custom namespace if one is found */
2004 	if (custom_element != NULL) {
2005 		if (custom_element->href) {
2006 			xmlChar *prefix = BAD_CAST (custom_element->prefix);
2007 			xmlNs *ns = NULL, *oldns = NULL;
2008 
2009 			oldns = xmlSearchNs(NULL, xmlnode, prefix);
2010 			if (prefix && oldns) {
2011 				prefix = NULL;
2012 			}
2013 			// remove existing default namespace
2014 			if (prefix == NULL) {
2015 				xmlNs *cur = xmlnode->nsDef, *last = NULL;
2016 				while (cur) {
2017 					if (cur->prefix == NULL) {
2018 						if (last) {
2019 							last->next = cur->next;
2020 						} else {
2021 							xmlnode->nsDef = cur->next;
2022 						}
2023 						xmlFreeNs(cur);
2024 					}
2025 					last = cur;
2026 					cur = cur->next;
2027 				}
2028 			}
2029 			ns = xmlNewNs(xmlnode, (xmlChar*)custom_element->href,
2030 					(xmlChar*)custom_element->prefix);
2031 			/* skip the base class namespace, it is replaced by the custom one */
2032 			xmlSetNs(xmlnode, ns);
2033 		}
2034 		if (custom_element->nodename) {
2035 			xmlNodeSetName(xmlnode, BAD_CAST (custom_element->nodename));
2036 		}
2037 		g_hash_table_foreach(custom_element->namespaces,
2038 				(GHFunc)_xmlnode_add_custom_namespace, xmlnode);
2039 	}
2040 
2041 
2042 	for (iter_classes = list_classes; iter_classes; iter_classes = g_slist_next(iter_classes)) {
2043 		class = iter_classes->data;
2044 		lasso_node_build_xmlNode_from_snippets(node,
2045 				(LassoNodeClass*)class, xmlnode,
2046 				class->node_data->snippets,
2047 				lasso_dump);
2048 	}
2049 
2050 	xmlCleanNs(xmlnode);
2051 
2052 	/* backward compatibility with Liberty ID-FF 1.1; */
2053 	if (find_path(node, "MajorVersion", &value_node, &version_class, &version_snippet) == TRUE) {
2054 		int *value;
2055 		int major_version, minor_version;
2056 
2057 		value = SNIPPET_STRUCT_MEMBER_P(value_node, G_TYPE_FROM_CLASS(version_class),
2058 				version_snippet);
2059 		major_version = *value;
2060 
2061 		if (find_path(node, "MinorVersion", &value_node, &version_class, &version_snippet) == TRUE) {
2062 			value = SNIPPET_STRUCT_MEMBER_P(value_node, G_TYPE_FROM_CLASS(version_class),
2063 					version_snippet);
2064 			minor_version = *value;
2065 		} else {
2066 			minor_version = 0;
2067 		}
2068 
2069 		if (strcmp((char*)xmlnode->ns->href, LASSO_LIB_HREF) == 0) {
2070 			if (major_version == 1 && minor_version == 0) {
2071 				xmlFree((xmlChar*)xmlnode->ns->href); /* warning: discard const */
2072 				xmlnode->ns->href = xmlStrdup((xmlChar*)
2073 						"http://projectliberty.org/schemas/core/2002/12");
2074 			}
2075 		}
2076 	}
2077 
2078 	g_slist_free(list_classes);
2079 	return xmlnode;
2080 }
2081 
2082 /*****************************************************************************/
2083 /* overridden parent class methods                                           */
2084 /*****************************************************************************/
2085 
2086 static GObjectClass *parent_class = NULL;
2087 
2088 static void
lasso_node_dispose(GObject * object)2089 lasso_node_dispose(GObject *object)
2090 {
2091 	LassoNodeClass *class;
2092 	struct XmlSnippet *snippet;
2093 	SnippetType type;
2094 
2095 	if (lasso_flag_memory_debug == TRUE) {
2096 		fprintf(stderr, "dispose of %s (at %p)\n", G_OBJECT_TYPE_NAME(object), object);
2097 	}
2098 
2099 	class = LASSO_NODE_GET_CLASS(object);
2100 
2101 	while (class && LASSO_IS_NODE_CLASS(class) && class->node_data) {
2102 		for (snippet = class->node_data->snippets; snippet && snippet->name; snippet++) {
2103 			void **value = SNIPPET_STRUCT_MEMBER_P(object, G_TYPE_FROM_CLASS(class), snippet);
2104 			type = snippet->type & 0xff;
2105 
2106 			if (! snippet->offset && ! (snippet->type & SNIPPET_PRIVATE))
2107 				continue;
2108 			if (snippet->type & SNIPPET_BOOLEAN)
2109 				continue;
2110 			if (snippet->type & SNIPPET_INTEGER)
2111 				continue;
2112 
2113 			if (*value == NULL)
2114 				continue;
2115 
2116 			if (lasso_flag_memory_debug == TRUE) {
2117 				fprintf(stderr, "  freeing %s/%s (at %p)\n",
2118 						G_OBJECT_TYPE_NAME(object), snippet->name, *value);
2119 			}
2120 			switch (type) {
2121 				case SNIPPET_NODE:
2122 				case SNIPPET_NODE_IN_CHILD:
2123 					lasso_release_gobject(*value);
2124 					break;
2125 				case SNIPPET_XMLNODE:
2126 					xmlFreeNode(*value);
2127 					break;
2128 				case SNIPPET_LIST_NODES:
2129 					lasso_release_list_of_gobjects((*(GList**)value));
2130 					break;
2131 				case SNIPPET_EXTENSION:
2132 				case SNIPPET_LIST_XMLNODES:
2133 					lasso_release_list_of_xml_node(*(GList**)value);
2134 					break;
2135 				case SNIPPET_LIST_CONTENT:
2136 					lasso_release_list_of_strings(*(GList**)value);
2137 					break;
2138 				case SNIPPET_CONTENT:
2139 				case SNIPPET_TEXT_CHILD:
2140 				case SNIPPET_ATTRIBUTE: {
2141 								if (snippet->type & SNIPPET_ANY) {
2142 									if (*value) {
2143 										lasso_release_ghashtable(*value);
2144 									}
2145 								} else {
2146 									lasso_release_string(*(char**)value);
2147 								}
2148 							} break;
2149 				case SNIPPET_SIGNATURE:
2150 							break; /* no real element here */
2151 				case SNIPPET_COLLECT_NAMESPACES:
2152 					if (*value) {
2153 						lasso_release_ghashtable(*value);
2154 					}
2155 					break;
2156 				default:
2157 							fprintf(stderr, "%d\n", type);
2158 							g_assert_not_reached();
2159 			}
2160 
2161 			if (type != SNIPPET_SIGNATURE) {
2162 				/* Signature snippet is not something to free,
2163 				 * so don't set the value to NULL */
2164 				*value = NULL;
2165 			}
2166 		}
2167 		class = g_type_class_peek_parent(class);
2168 	}
2169 
2170 	parent_class->dispose(object);
2171 }
2172 
2173 /*****************************************************************************/
2174 /* instance and class init functions                                         */
2175 /*****************************************************************************/
2176 
2177 static gboolean
init_from_query(LassoNode * node,char ** query_fields)2178 init_from_query(LassoNode *node, char **query_fields)
2179 {
2180 	return lasso_node_init_from_query_fields(node, query_fields);
2181 }
2182 
2183 static void
class_init(LassoNodeClass * class,void * unused G_GNUC_UNUSED)2184 class_init(LassoNodeClass *class, void *unused G_GNUC_UNUSED)
2185 {
2186 	GObjectClass *gobject_class = G_OBJECT_CLASS(class);
2187 
2188 	parent_class = g_type_class_peek_parent(class);
2189 	/* virtual public methods */
2190 	class->destroy = lasso_node_impl_destroy;
2191 	class->init_from_query = init_from_query;
2192 	class->init_from_xml = lasso_node_impl_init_from_xml;
2193 
2194 	/* virtual private methods */
2195 	class->build_query = lasso_node_impl_build_query;
2196 	class->get_xmlNode = lasso_node_impl_get_xmlNode;
2197 
2198 	/* override */
2199 	gobject_class->dispose = lasso_node_dispose;
2200 
2201 	original_xmlnode_quark = g_quark_from_static_string("lasso_original_xmlnode");
2202 	custom_element_quark = g_quark_from_static_string("lasso_custom_element");
2203 	class->node_data = NULL;
2204 }
2205 
2206 static void
base_class_finalize(LassoNodeClass * class)2207 base_class_finalize(LassoNodeClass *class)
2208 {
2209 	if (class->node_data) {
2210 		LassoNodeClassData *data = class->node_data;
2211 
2212 		if (data->ns) {
2213 			xmlFreeNs(data->ns);
2214 		}
2215 		if (data->node_name) {
2216 			lasso_release(data->node_name);
2217 		}
2218 		lasso_release(class->node_data);
2219 		class->node_data = NULL;
2220 	}
2221 }
2222 
2223 GType
lasso_node_get_type()2224 lasso_node_get_type()
2225 {
2226 	static GType this_type = 0;
2227 
2228 	if (!this_type) {
2229 		static const GTypeInfo this_info = {
2230 			sizeof (LassoNodeClass),
2231 			NULL,
2232 			(GBaseFinalizeFunc) base_class_finalize,
2233 			(GClassInitFunc) class_init,
2234 			NULL,
2235 			NULL,
2236 			sizeof(LassoNode),
2237 			0,
2238 			NULL,
2239 			NULL,
2240 		};
2241 
2242 		this_type = g_type_register_static(G_TYPE_OBJECT , "LassoNode", &this_info, 0);
2243 	}
2244 	return this_type;
2245 }
2246 
2247 /**
2248  * lasso_node_new:
2249  *
2250  * Creates a new #LassoNode.
2251  *
2252  * Return value: a newly created #LassoNode object
2253  **/
2254 LassoNode*
lasso_node_new()2255 lasso_node_new()
2256 {
2257 	return g_object_new(LASSO_TYPE_NODE, NULL);
2258 }
2259 
2260 /**
2261  * lasso_node_new_from_dump:
2262  * @dump: XML object dump
2263  *
2264  * Restores the @dump to a new #LassoNode subclass.
2265  *
2266  * Return value: a newly created object; or NULL if an error occured.
2267  **/
2268 LassoNode*
lasso_node_new_from_dump(const char * dump)2269 lasso_node_new_from_dump(const char *dump)
2270 {
2271 	LassoNode *node;
2272 	xmlDoc *doc;
2273 
2274 	if (dump == NULL)
2275 		return NULL;
2276 
2277 	doc = lasso_xml_parse_memory(dump, strlen(dump));
2278 	if (doc == NULL)
2279 		return NULL;
2280 
2281 	node = lasso_node_new_from_xmlNode(xmlDocGetRootElement(doc));
2282 
2283 	lasso_release_doc(doc);
2284 	return node;
2285 }
2286 
2287 
2288 /**
2289  * lasso_node_new_from_soap:
2290  * @soap: the SOAP message
2291  *
2292  * Parses SOAP message and creates a new Lasso object with the right class.
2293  *
2294  * Return value: node if success; NULL otherwise
2295  **/
2296 LassoNode*
lasso_node_new_from_soap(const char * soap)2297 lasso_node_new_from_soap(const char *soap)
2298 {
2299 	xmlDoc *doc;
2300 	xmlNode *xmlnode;
2301 	LassoNode *node = NULL;
2302 
2303 	doc = lasso_xml_parse_memory(soap, strlen(soap));
2304 	if (doc == NULL) {
2305 		return NULL;
2306 	}
2307 	xmlnode = lasso_xml_get_soap_content(xmlDocGetRootElement(doc));
2308 	if (xmlnode == NULL) {
2309 		return NULL;
2310 	}
2311 	node = lasso_node_new_from_xmlNode(xmlnode);
2312 
2313 	lasso_release_doc(doc);
2314 
2315 	return node;
2316 }
2317 
2318 /* How finding a typename from an xmlNode works ?
2319  *
2320  * There is three way to get to a typename:
2321  * 1. by an xsi:type QName attribute, that we resolve
2322  * 2. by constructing a QName from the namespace of the xsi:type and the name of the node
2323  * 3. by resolving the QName of the node
2324  *
2325  * To resolve a typename you must map the QName using the default registry object, or use
2326  * prefix_from_href_and_nodename() to mat the QName to a prefix used to build the typename with this
2327  * template: typename = "Lasso" + prefix + name_part(QName).
2328  *
2329  * The resolving algorithm is in the function _type_name_from_href_and_nodename().
2330  *
2331  * The prefix extraction in prefix_from_href_and_nodename().
2332  *
2333  */
2334 static const char *
prefix_from_href_and_nodename(const xmlChar * href,G_GNUC_UNUSED const xmlChar * nodename)2335 prefix_from_href_and_nodename(const xmlChar *href, G_GNUC_UNUSED const xmlChar *nodename) {
2336 	char *prefix = NULL;
2337 #ifdef LASSO_WSF_ENABLED
2338 	char *tmp = NULL;
2339 #endif
2340 
2341 	if (strcmp((char*)href, LASSO_LASSO_HREF) == 0)
2342 		prefix = "";
2343 	else if (strcmp((char*)href, LASSO_SAML_ASSERTION_HREF) == 0)
2344 		prefix = "Saml";
2345 	else if (strcmp((char*)href, LASSO_SAML_PROTOCOL_HREF) == 0)
2346 		prefix = "Samlp";
2347 	else if (strcmp((char*)href, LASSO_LIB_HREF) == 0)
2348 		prefix = "Lib";
2349 	else if (strcmp((char*)href, LASSO_SAML2_ASSERTION_HREF) == 0)
2350 		prefix = "Saml2";
2351 	else if (strcmp((char*)href, LASSO_SAML2_PROTOCOL_HREF) == 0)
2352 		prefix = "Samlp2";
2353 	else if (strcmp((char*)href, LASSO_ECP_HREF) == 0)
2354 		prefix = "Ecp";
2355 	else if (strcmp((char*)href, LASSO_PAOS_HREF) == 0)
2356 		prefix = "Paos";
2357 	else if (strcmp((char*)href, LASSO_SOAP_ENV_HREF) == 0)
2358 		prefix = "Soap";
2359 	else if (strcmp((char*)href, LASSO_DS_HREF) == 0)
2360 		prefix = "Ds";
2361 #ifdef LASSO_WSF_ENABLED
2362 	else if (strcmp((char*)href, LASSO_SOAP_BINDING_HREF) == 0)
2363 		prefix = "SoapBinding";
2364 	else if (strcmp((char*)href, LASSO_SOAP_BINDING_EXT_HREF) == 0)
2365 		prefix = "SoapBindingExt";
2366 	else if (strcmp((char*)href, LASSO_DISCO_HREF) == 0)
2367 		prefix = "Disco";
2368 	else if (strcmp((char*)href, LASSO_IS_HREF) == 0)
2369 		prefix = "Is";
2370 	else if (strcmp((char*)href, LASSO_SA_HREF) == 0)
2371 		prefix = "Sa";
2372 	else if (strcmp((char*)href, LASSO_WSSE_HREF) == 0)
2373 		prefix = "WsSec1";
2374 	else if (strcmp((char*)href, LASSO_WSSE1_HREF) == 0)
2375 		prefix = "WsSec1";
2376 	else if (strcmp((char*)href, LASSO_IDWSF2_DISCOVERY_HREF) == 0)
2377 		prefix = "IdWsf2Disco";
2378 	else if (strcmp((char*)href, LASSO_IDWSF2_SBF_HREF) == 0)
2379 		prefix = "IdWsf2Sbf";
2380 	else if (strcmp((char*)href, LASSO_IDWSF2_SB2_HREF) == 0)
2381 		prefix = "IdWsf2Sb2";
2382 	else if (strcmp((char*)href, LASSO_IDWSF2_UTIL_HREF) == 0)
2383 		prefix = "IdWsf2Util";
2384 	else if (strcmp((char*)href, LASSO_IDWSF2_SEC_HREF) == 0)
2385 		prefix = "IdWsf2Sec";
2386 	else if (strcmp((char*)href, LASSO_IDWSF2_IMS_HREF) == 0)
2387 		prefix = "IdWsf2Ims";
2388 	else if (strcmp((char*)href, LASSO_IDWSF2_IS_HREF) == 0)
2389 		prefix = "IdWsf2Is";
2390 	else if (strcmp((char*)href, LASSO_IDWSF2_PS_HREF) == 0)
2391 		prefix = "IdWsf2Ps";
2392 	else if (strcmp((char*)href, LASSO_IDWSF2_SUBS_HREF) == 0)
2393 		prefix = "IdWsf2Subs";
2394 	else if (strcmp((char*)href, LASSO_IDWSF2_SUBSREF_HREF) == 0)
2395 		prefix = "IdWsf2SubsRef";
2396 	else if (strcmp((char*)href, LASSO_WSA_HREF) == 0)
2397 		prefix = "WsAddr";
2398 	else if ((tmp = lasso_get_prefix_for_idwsf2_dst_service_href((char*)href))
2399 			!= NULL) {
2400 		/* ID-WSF 2 Profile */
2401 		prefix = "IdWsf2DstRef";
2402 		lasso_release_string(tmp);
2403 	} else if ((tmp = lasso_get_prefix_for_dst_service_href((char*)href))
2404 			!= NULL) {
2405 		/* ID-WSF 1 Profile */
2406 		prefix = "Dst";
2407 		lasso_release_string(tmp);
2408 	}
2409 
2410 	if (prefix != NULL && strcmp(prefix, "Dst") == 0 && strcmp((char*)nodename, "Status") == 0)
2411 		prefix = "Utility";
2412 	else if (prefix != NULL && strcmp(prefix, "Disco") == 0 && strcmp((char*)nodename, "Status") == 0)
2413 		prefix = "Utility";
2414 	else if (prefix != NULL && strcmp(prefix, "Sa") == 0 && strcmp((char*)nodename, "Status") == 0)
2415 		prefix = "Utility";
2416 #endif
2417 
2418 	return prefix;
2419 }
2420 
2421 /*
2422  * _type_name_from_href_and_nodename:
2423  * @href: the href part of a QName
2424  * @nodename: the name part of a QName
2425  *
2426  * Return value: a typename string if one if found that exists, NULL otherwise.
2427  */
2428 static char*
_type_name_from_href_and_nodename(char * href,char * nodename)2429 _type_name_from_href_and_nodename(char *href, char *nodename) {
2430 	const char *prefix = prefix_from_href_and_nodename(BAD_CAST (href), BAD_CAST (nodename));
2431 	char *typename = NULL;
2432 
2433 	if (!href || !nodename)
2434 		return NULL;
2435 
2436 	/* FIXME: hardcoded mappings */
2437 	if (strcmp(nodename, "SvcMD") == 0) {
2438 		typename = g_strdup("LassoIdWsf2DiscoSvcMetadata");
2439 	} else if (prefix != NULL && strcmp(prefix, "IdWsf2DstRef") == 0 && strcmp(nodename, "Status") == 0) {
2440 		typename = g_strdup("LassoIdWsf2UtilStatus");
2441 	} else if (prefix != NULL && strcmp(prefix, "WsSec1") == 0 && strcmp(nodename, "Security") == 0) {
2442 		typename = g_strdup("LassoWsSec1SecurityHeader");
2443 	} else if (prefix != NULL && strcmp(prefix, "Soap") == 0 && strcmp(nodename, "detail") == 0) {
2444 		typename = g_strdup("LassoSoapDetail");
2445 	} else {
2446 		/* first try with registered mappings */
2447 		const char *ctypename = lasso_registry_default_get_mapping(href, nodename, LASSO_LASSO_HREF);
2448 		if (ctypename) {
2449 			typename = g_strdup(ctypename);
2450 		}
2451 		/* finally try the default behaviour */
2452 		if (prefix != NULL && typename == NULL) {
2453 			typename = g_strdup_printf("Lasso%s%s", prefix, nodename);
2454 		}
2455 	}
2456 
2457 	/* Does it really exist ? */
2458 	if (typename && g_type_from_name (typename) == 0) {
2459 		lasso_release_string(typename);
2460 	}
2461 
2462 	return typename;
2463 }
2464 
2465 /**
2466  * _lasso_node_new_from_xmlNode:
2467  * @node: an xmlNode
2468  *
2469  * Builds a new #LassoNode from an xmlNode.
2470  *
2471  * Return value: a new node
2472  **/
2473 static LassoNode*
_lasso_node_new_from_xmlNode(xmlNode * xmlnode)2474 _lasso_node_new_from_xmlNode(xmlNode *xmlnode)
2475 {
2476 	char *typename = NULL;
2477 	xmlChar *xsitype = NULL;
2478 	LassoNode *node = NULL;
2479 	gboolean fromXsi = FALSE;
2480 
2481 	xsitype = xmlGetNsProp(xmlnode, (xmlChar*)"type", (xmlChar*)LASSO_XSI_HREF);
2482 	if (xsitype) {
2483 		xmlChar *xmlPrefix, *separator;
2484 		xmlNsPtr xsiNs = NULL;
2485 		char *xsiNodeName = NULL;
2486 
2487 		/** Honor xsi:type  */
2488 		xmlPrefix = (xmlChar*)xsitype;
2489 		separator = (xmlChar*)strchr((char*)xsitype, ':');
2490 		if (separator != NULL) {
2491 			xmlPrefix = (xmlChar*)g_strndup((char*)xmlPrefix, (size_t)(separator - xmlPrefix));
2492 			xsiNs = xmlSearchNs(NULL, xmlnode, xmlPrefix);
2493 			if (xsiNs != NULL) {
2494 				xsiNodeName = g_strdup((char*)(separator+1));
2495 				if (strcmp((char*)xsiNs->href, LASSO_LASSO_HREF) == 0) {
2496 					typename = g_strdup(xsiNodeName);
2497 				}
2498 			}
2499 			lasso_release(xmlPrefix);
2500 		}
2501 		if (! typename && xsiNs && xsiNodeName) {
2502 			typename = _type_name_from_href_and_nodename ((char*)xsiNs->href, xsiNodeName);
2503 		}
2504 		if (! typename && xsiNs) {
2505 			typename = _type_name_from_href_and_nodename ((char*)xsiNs->href, (char*)xmlnode->name);
2506 		}
2507 		lasso_release_xml_string(xsitype);
2508 		if (xsiNodeName)
2509 			lasso_release_string(xsiNodeName);
2510 		if (typename)
2511 			fromXsi = TRUE;
2512 	}
2513 
2514 	if (typename == NULL && xmlnode->ns && xmlnode->ns->href) {
2515 		typename = _type_name_from_href_and_nodename ((char*)xmlnode->ns->href, (char*)xmlnode->name);
2516 	}
2517 
2518 	if (typename) {
2519 		node = lasso_node_new_from_xmlNode_with_type(xmlnode, typename);
2520 	}
2521 	if (! node) {
2522 		goto cleanup;
2523 	}
2524 	if (! fromXsi) {
2525 		/* if the typename was not obtained via xsi:type but through mapping of the element
2526 		 * name then keep the element name */
2527 		if (LASSO_NODE_GET_CLASS(node)->node_data &&
2528 				LASSO_NODE_GET_CLASS(node)->node_data->node_name &&
2529 				lasso_strisnotequal((char*)xmlnode->name,
2530 					LASSO_NODE_GET_CLASS(node)->node_data->node_name))
2531 		{
2532 			lasso_node_set_custom_nodename(node, (char*)xmlnode->name);
2533 		}
2534 
2535 		if (xmlnode->ns && (LASSO_NODE_GET_CLASS(node)->node_data == NULL ||
2536 					LASSO_NODE_GET_CLASS(node)->node_data->ns == NULL ||
2537 					lasso_xmlstrisnotequal(xmlnode->ns->href,
2538 						LASSO_NODE_GET_CLASS(node)->node_data->ns->href)))
2539 		{
2540 			lasso_node_set_custom_namespace(node, (char*)xmlnode->ns->prefix,
2541 					(char*)xmlnode->ns->href);
2542 		}
2543 
2544 
2545 	}
2546 cleanup:
2547 	lasso_release(typename);
2548 
2549 	return node;
2550 }
2551 
2552 /**
2553  * lasso_node_new_from_xmlNode:
2554  * @node: an xmlNode
2555  *
2556  * Builds a new #LassoNode from an xmlNode.
2557  *
2558  * Return value: a new node
2559  **/
2560 LassoNode*
lasso_node_new_from_xmlNode(xmlNode * xmlnode)2561 lasso_node_new_from_xmlNode(xmlNode *xmlnode)
2562 {
2563 	if (xmlnode == NULL || xmlnode->ns == NULL) {
2564 		message(G_LOG_LEVEL_CRITICAL, "Unable to build a LassoNode from a xmlNode");
2565 		return NULL;
2566 	}
2567 	return _lasso_node_new_from_xmlNode(xmlnode);
2568 }
2569 
2570 static LassoNode*
lasso_node_new_from_xmlNode_with_type(xmlNode * xmlnode,char * typename)2571 lasso_node_new_from_xmlNode_with_type(xmlNode *xmlnode, char *typename)
2572 {
2573 	GType gtype;
2574 	LassoNode *node;
2575 	int rc = 0;
2576 
2577 	message(LOG_LEVEL_XML_DEBUG, "Processing node '%s' with type '%s'", xmlnode->name, typename);
2578 
2579 	if (typename == NULL) {
2580 		return _lasso_node_new_from_xmlNode(xmlnode); /* will auto-detect */
2581 	}
2582 
2583 	gtype = g_type_from_name(typename);
2584 	if (gtype == 0) {
2585 		message(G_LOG_LEVEL_CRITICAL, "Unable to get g_type from name '%s'", typename);
2586 		return NULL;
2587 	}
2588 
2589 
2590 	node = g_object_new(gtype, NULL);
2591 	if (lasso_flag_memory_debug == TRUE) {
2592 		fprintf(stderr, "allocation of %s (for xmlNode %p) : %p\n", g_type_name(gtype), xmlnode, node);
2593 	}
2594 	rc = lasso_node_init_from_xml(node, xmlnode);
2595 	if (rc) {
2596 		message(G_LOG_LEVEL_CRITICAL, "Lasso node initialization failed for node '%s', type '%s': error %d", xmlnode->name, typename, rc);
2597 		lasso_node_destroy(node);
2598 		return NULL;
2599 	}
2600 
2601 	return node;
2602 }
2603 
2604 static gboolean
is_base64(const char * message)2605 is_base64(const char *message)
2606 {
2607 	const char *c;
2608 
2609 	c = message;
2610 	while (*c != 0 && (isalnum((int)*c) || *c == '+' || *c == '/' || *c == '\n' || *c == '\r')) c++;
2611 	while (*c == '=' || *c == '\n' || *c == '\r') c++; /* trailing = */
2612 
2613 	if (*c == 0)
2614 		return TRUE;
2615 
2616 	return FALSE;
2617 }
2618 
2619 
2620 /**
2621  * lasso_node_init_from_message_with_format:
2622  * @node: a #LassoNode (or derived class)
2623  * @message: a Liberty message
2624  * @constraint: LASSO_MESSAGE_FORMAT_UNKNOWN or the format the message must be in
2625  * @doc_out: a pointer to store the resulting #xmlDoc structure
2626  * @node_out: a pointer to store the resulting content #xmlNode
2627  *
2628  * Parses @message and initialiazes @node fields with data from it.  Message type may be base64,
2629  * SOAP, XML or query string, correct type is found automatically if contraint is
2630  * LASSO_MESSAGE_FORMAT_UNKNOWN or is limited to the value given.
2631  * If the format is one of LASSO_MESSAGE_FORMAT_XML or LASSO_MESSAGE_FORMAT_XML or
2632  * LASSO_MESSAGE_FORMAT_BASE64 the resulting #xmlDoc and #xmlNode of the message can be retrieved.
2633  *
2634  * Return value: a #LassoMessageFormat value.
2635  **/
2636 LassoMessageFormat
lasso_node_init_from_message_with_format(LassoNode * node,const char * message,LassoMessageFormat constraint,xmlDoc ** doc_out,xmlNode ** root_out)2637 lasso_node_init_from_message_with_format(LassoNode *node, const char *message, LassoMessageFormat constraint, xmlDoc **doc_out, xmlNode **root_out)
2638 {
2639 	char *msg = NULL;
2640 	gboolean b64 = FALSE;
2641 	LassoMessageFormat rc = LASSO_MESSAGE_FORMAT_ERROR;
2642 	xmlDoc *doc = NULL;
2643 	xmlNode *root = NULL;
2644 	gboolean any = constraint == LASSO_MESSAGE_FORMAT_UNKNOWN;
2645 
2646 	msg = (char*)message;
2647 
2648 	/* BASE64 case */
2649 	if (any || constraint == LASSO_MESSAGE_FORMAT_BASE64) {
2650 		if (message[0] != 0 && is_base64(message)) {
2651 			int rc = 0;
2652 
2653 			msg = g_malloc(strlen(message));
2654 			rc = xmlSecBase64Decode((xmlChar*)message, (xmlChar*)msg, strlen(message));
2655 			if (rc >= 0) {
2656 				b64 = TRUE;
2657 			} else {
2658 				lasso_release(msg);
2659 				msg = (char*)message;
2660 			}
2661 		}
2662 	}
2663 
2664 	/* XML case */
2665 	if (any || constraint == LASSO_MESSAGE_FORMAT_XML ||
2666 		constraint == LASSO_MESSAGE_FORMAT_BASE64 ||
2667 		constraint == LASSO_MESSAGE_FORMAT_SOAP) {
2668 		if (strchr(msg, '<')) {
2669 			doc = lasso_xml_parse_memory(msg, strlen(msg));
2670 			if (doc == NULL) {
2671 				rc = LASSO_MESSAGE_FORMAT_UNKNOWN;
2672 				goto cleanup;
2673 			}
2674 			root = xmlDocGetRootElement(doc);
2675 
2676 			if (any || constraint == LASSO_MESSAGE_FORMAT_SOAP) {
2677 				gboolean is_soap = FALSE;
2678 
2679 				is_soap = lasso_xml_is_soap(root);
2680 				if (is_soap) {
2681 					root = lasso_xml_get_soap_content(root);
2682 				}
2683 				rc = lasso_node_init_from_xml(node, root);
2684 				if (rc != 0) {
2685 					rc = LASSO_MESSAGE_FORMAT_XSCHEMA_ERROR;
2686 					goto cleanup;
2687 
2688 				}
2689 				if (is_soap) {
2690 					rc = LASSO_MESSAGE_FORMAT_SOAP;
2691 					goto cleanup;
2692 				}
2693 				if (b64) {
2694 					lasso_release(msg);
2695 					rc = LASSO_MESSAGE_FORMAT_BASE64;
2696 					goto cleanup;
2697 				}
2698 				rc = LASSO_MESSAGE_FORMAT_XML;
2699 				goto cleanup;
2700 			}
2701 		}
2702 	}
2703 
2704 	/* HTTP query CASE */
2705 	if (any || constraint == LASSO_MESSAGE_FORMAT_QUERY) {
2706 		if (strchr(msg, '&') || strchr(msg, '=')) {
2707 			/* XXX: detect SAML artifact messages to return a different status code ? */
2708 			if (lasso_node_init_from_query(node, msg) == FALSE) {
2709 				goto cleanup;
2710 			}
2711 			rc = LASSO_MESSAGE_FORMAT_QUERY;
2712 			goto cleanup;
2713 		}
2714 	}
2715 
2716 cleanup:
2717 	if (doc_out) {
2718 		*doc_out = doc;
2719 		if (root_out) {
2720 			*root_out = root;
2721 		}
2722 	} else {
2723 		lasso_release_doc(doc);
2724 	}
2725 	return rc;
2726 }
2727 
2728 /**
2729  * lasso_node_init_from_message:
2730  * @node: a #LassoNode (or derived class)
2731  * @message: a Liberty message
2732  *
2733  * Parses @message and initialiazes @node fields with data from it.  Message
2734  * type may be base64, SOAP, XML or query string, correct type is found
2735  * automatically.
2736  *
2737  * Return value: a #LassoMessageFormat value.
2738  **/
2739 LassoMessageFormat
lasso_node_init_from_message(LassoNode * node,const char * message)2740 lasso_node_init_from_message(LassoNode *node, const char *message)
2741 {
2742 	return lasso_node_init_from_message_with_format(node, message, LASSO_MESSAGE_FORMAT_UNKNOWN, NULL, NULL);
2743 }
2744 
2745 /**
2746  * lasso_node_class_add_snippets:
2747  * @klass: object class
2748  * @snippets: array of XmlSnippet (NULL terminated)
2749  **/
2750 void
lasso_node_class_add_snippets(LassoNodeClass * klass,struct XmlSnippet * snippets)2751 lasso_node_class_add_snippets(LassoNodeClass *klass, struct XmlSnippet *snippets)
2752 {
2753 	klass->node_data->snippets = snippets;
2754 }
2755 
2756 
2757 /**
2758  * lasso_node_class_add_query_snippets:
2759  * @klass: object class
2760  * @snippets: array of QuerySnippet (NULL terminated)
2761  **/
2762 void
lasso_node_class_add_query_snippets(LassoNodeClass * klass,struct QuerySnippet * snippets)2763 lasso_node_class_add_query_snippets(LassoNodeClass *klass, struct QuerySnippet *snippets)
2764 {
2765 	klass->node_data->query_snippets = snippets;
2766 }
2767 
2768 /**
2769  * lasso_node_class_set_nodename:
2770  * @klass: object class
2771  * @name: name for element node
2772  **/
2773 void
lasso_node_class_set_nodename(LassoNodeClass * klass,char * name)2774 lasso_node_class_set_nodename(LassoNodeClass *klass, char *name)
2775 {
2776 	if (klass->node_data->node_name)
2777 		lasso_release(klass->node_data->node_name);
2778 	klass->node_data->node_name = g_strdup(name);
2779 }
2780 
2781 
2782 /**
2783  * lasso_node_class_set_ns:
2784  * @klass: object class
2785  * @href: namespace uri
2786  * @prefix: namespace prefix
2787  **/
2788 void
lasso_node_class_set_ns(LassoNodeClass * klass,char * href,char * prefix)2789 lasso_node_class_set_ns(LassoNodeClass *klass, char *href, char *prefix)
2790 {
2791 	if (klass->node_data->ns)
2792 		xmlFreeNs(klass->node_data->ns);
2793 	klass->node_data->ns = xmlNewNs(NULL, (xmlChar*)href, (xmlChar*)prefix);
2794 }
2795 
2796 static void
snippet_dump_any(gchar * key,gchar * value,xmlNode * xmlnode)2797 snippet_dump_any(gchar *key, gchar *value, xmlNode *xmlnode)
2798 {
2799 	if (! key)
2800 		return;
2801 	if (! value)
2802 		return;
2803 	/* element tree syntax for setting namespaces */
2804 	if (key && key[0] == '{') {
2805 		char *end = strchr(key, '}');
2806 		char *ns_uri;
2807 		xmlNs *ns;
2808 		if (! end) {
2809 			message(G_LOG_LEVEL_WARNING, "Invalid attribute name: %s", key);
2810 			return;
2811 		}
2812 		ns_uri = g_strndup(key+1, end-(key+1));
2813 		ns = get_or_define_ns(xmlnode, BAD_CAST ns_uri, NULL);
2814 		xmlSetNsProp(xmlnode, ns, BAD_CAST &end[1], BAD_CAST value);
2815 	} else {
2816 		xmlSetProp(xmlnode, BAD_CAST key, BAD_CAST value);
2817 	}
2818 }
2819 
2820 static void
apply_snippet_ns(struct XmlSnippet * snippet,xmlNode * xmlnode)2821 apply_snippet_ns(struct XmlSnippet *snippet, xmlNode *xmlnode)
2822 {
2823 	xmlNs *ns;
2824 
2825 	if (! xmlnode)
2826 		return;
2827 	if (snippet->ns_uri) {
2828 		if (! xmlnode->ns || !lasso_strisequal((char*)xmlnode->ns->href, (char*)snippet->ns_uri)) {
2829 			ns = get_or_define_ns(xmlnode, BAD_CAST snippet->ns_uri, BAD_CAST snippet->ns_name);
2830 			xmlSetNs(xmlnode, ns);
2831 		}
2832 		/* If not a any snippet, apply given Name, what about xsi:type ? */
2833 	}
2834 	if (! (snippet->type & SNIPPET_ANY) && ! lasso_strisempty(snippet->name) &&
2835 			lasso_strisnotequal((char*)xmlnode->name, (char*)snippet->name))
2836 		xmlNodeSetName(xmlnode, BAD_CAST snippet->name);
2837 }
2838 
2839 static void
lasso_node_build_xmlNode_from_snippets(LassoNode * node,LassoNodeClass * class,xmlNode * xmlnode,struct XmlSnippet * snippets,gboolean lasso_dump)2840 lasso_node_build_xmlNode_from_snippets(LassoNode *node, LassoNodeClass *class, xmlNode *xmlnode,
2841 		struct XmlSnippet *snippets, gboolean lasso_dump)
2842 {
2843 	struct XmlSnippet *snippet;
2844 	GType g_type;
2845 	xmlNode *t;
2846 	GList *elem;
2847 	struct XmlSnippet *snippet_any_attribute = NULL;
2848 
2849 	g_type = G_TYPE_FROM_CLASS(class);
2850 
2851 	snippet = snippets;
2852 	while (snippet && snippet->name) {
2853 		void *value = NULL;
2854 		int int_value = 0;
2855 		gboolean bool_value = FALSE;
2856 		char *str = NULL;
2857 		gboolean optional = snippet->type & SNIPPET_OPTIONAL;
2858 		gboolean optional_neg = snippet->type & SNIPPET_OPTIONAL_NEG;
2859 		gboolean multiple = is_snippet_multiple(snippet);
2860 
2861 		if (! snippet->offset && ! (snippet->type & SNIPPET_PRIVATE)) {
2862 			goto advance;
2863 		}
2864 		if (lasso_dump == FALSE && snippet->type & SNIPPET_LASSO_DUMP) {
2865 			goto advance;
2866 		}
2867 		if ((snippet->type & 0xff) == SNIPPET_ATTRIBUTE && (snippet->type & SNIPPET_ANY)) {
2868 			snippet_any_attribute = snippet;
2869 			goto advance;
2870 		}
2871 		/* special treatment for 1-* list of nodes, without we would serialize them twice */
2872 		if (multiple && (snippet->type & SNIPPET_JUMP_ON_MATCH && SNIPPET_JUMP_OFFSET(snippet->type) > 0)) {
2873 			snippet++;
2874 			continue;
2875 		}
2876 
2877 		// convert input type to string if needed
2878 		if (snippet->type & SNIPPET_INTEGER) {
2879 			int_value = SNIPPET_STRUCT_MEMBER(int, node, g_type, snippet);
2880 			if (int_value == 0 && optional) {
2881 				goto advance;
2882 			}
2883 			if (int_value == -1 && optional_neg) {
2884 				goto advance;
2885 			}
2886 			str = g_strdup_printf("%i", int_value);
2887 		} else if (snippet->type & SNIPPET_BOOLEAN) {
2888 			bool_value = SNIPPET_STRUCT_MEMBER(gboolean, node, g_type, snippet);
2889 			if (bool_value == FALSE  && optional) {
2890 				goto advance;
2891 			}
2892 			str = bool_value ? "true" : "false";
2893 		} else {
2894 			value = SNIPPET_STRUCT_MEMBER(void *, node, g_type, snippet);
2895 			if (value == NULL) {
2896 				goto advance;
2897 			}
2898 			str = value;
2899 		}
2900 
2901 		// output type
2902 		switch (snippet->type & 0xff) {
2903 			case SNIPPET_ATTRIBUTE:
2904 				if (snippet->ns_name) {
2905 					xmlNsPtr ns;
2906 
2907 					ns = xmlNewNs(xmlnode, (xmlChar*)snippet->ns_uri, (xmlChar*)snippet->ns_name);
2908 					xmlSetNsProp(xmlnode, ns, (xmlChar*)snippet->name, (xmlChar*)str);
2909 				} else {
2910 					xmlSetProp(xmlnode, (xmlChar*)snippet->name, (xmlChar*)str);
2911 				}
2912 				break;
2913 			case SNIPPET_TEXT_CHILD:
2914 				xmlAddChild(xmlnode, xmlNewText((xmlChar*)str));
2915 				break;
2916 			case SNIPPET_NODE:
2917 				{
2918 					xmlNode *t2;
2919 					t2 = lasso_node_get_xmlNode(LASSO_NODE(value), lasso_dump);
2920 					apply_snippet_ns(snippet, t2);
2921 					xmlAddChild(xmlnode, t2);
2922 				} break;
2923 			case SNIPPET_CONTENT:
2924 				xmlNewTextChild(xmlnode, NULL,
2925 						(xmlChar*)snippet->name, (xmlChar*)str);
2926 				break;
2927 			case SNIPPET_NODE_IN_CHILD:
2928 				t = xmlNewTextChild(xmlnode, NULL, (xmlChar*)snippet->name, NULL);
2929 				xmlAddChild(t, lasso_node_get_xmlNode(
2930 							LASSO_NODE(value), lasso_dump));
2931 				break;
2932 			case SNIPPET_LIST_NODES:
2933 				elem = (GList *)value;
2934 				while (elem) {
2935 					xmlNode *subnode = lasso_node_get_xmlNode(
2936 							LASSO_NODE(elem->data), lasso_dump);
2937 					if (subnode) {
2938 						apply_snippet_ns(snippet, subnode);
2939 						xmlAddChild(xmlnode, subnode);
2940 					}
2941 					elem = g_list_next(elem);
2942 				}
2943 				break;
2944 			case SNIPPET_LIST_CONTENT:
2945 				/* sequence of simple elements (no children,
2946 				 * no attrs, just content) */
2947 				elem = (GList *)value;
2948 				while (elem) {
2949 					xmlNode *subnode;
2950 					subnode = xmlNewTextChild(xmlnode, NULL,
2951 							(xmlChar*)snippet->name,
2952 							(xmlChar*)(elem->data));
2953 					apply_snippet_ns(snippet, subnode);
2954 					elem = g_list_next(elem);
2955 				}
2956 				break;
2957 			case SNIPPET_LIST_XMLNODES:
2958 			case SNIPPET_EXTENSION:
2959 				elem = (GList *)value;
2960 				while (elem) {
2961 					xmlAddChild(xmlnode, xmlCopyNode(elem->data, 1));
2962 					elem = g_list_next(elem);
2963 				}
2964 				break;
2965 			case SNIPPET_XMLNODE:
2966 				xmlAddChild(xmlnode, xmlCopyNode((xmlNode *)value, 1));
2967 				break;
2968 			case SNIPPET_SIGNATURE:
2969 				lasso_node_add_signature_template(node, xmlnode, snippet);
2970 				break;
2971 			case SNIPPET_COLLECT_NAMESPACES:
2972 				break;
2973 			case SNIPPET_UNUSED1:
2974 				g_assert_not_reached();
2975 		}
2976 		if (snippet->type & SNIPPET_INTEGER) {
2977 			lasso_release(str);
2978 		}
2979 	advance:
2980 		if ((snippet->type & SNIPPET_JUMP_ON_MATCH) && SNIPPET_JUMP_OFFSET(snippet->type) > 0 && value) {
2981 			snippet += SNIPPET_JUMP_OFFSET(snippet->type);
2982 		} else if (!value && (snippet->type & SNIPPET_JUMP_ON_MISS) && SNIPPET_JUMP_OFFSET(snippet->type) > 0 && value) {
2983 			snippet += SNIPPET_JUMP_OFFSET(snippet->type);
2984 		} else {
2985 			snippet++;
2986 		}
2987 	}
2988 
2989 	if (snippet_any_attribute) {
2990 		GHashTable *value = SNIPPET_STRUCT_MEMBER(GHashTable *, node, g_type,
2991 				snippet_any_attribute);
2992 		if (value) {
2993 			g_hash_table_foreach(value, (GHFunc)snippet_dump_any, xmlnode);
2994 		}
2995 	}
2996 }
2997 
2998 static void
lasso_node_add_signature_template(LassoNode * node,xmlNode * xmlnode,struct XmlSnippet * snippet_signature)2999 lasso_node_add_signature_template(LassoNode *node, xmlNode *xmlnode,
3000 		struct XmlSnippet *snippet_signature)
3001 {
3002 	LassoNodeClass *klass = NULL;
3003 	LassoNodeClassData *node_data = NULL;
3004 	LassoSignatureContext context;
3005 	char *id = NULL;
3006 
3007 	node_data = lasso_legacy_get_signature_node_data(node, &klass);
3008 	if (! node_data)
3009 		return;
3010 
3011 	if (node_data->sign_type_offset == 0)
3012 		return;
3013 
3014 	context = lasso_node_get_signature(node);
3015 	if (! lasso_validate_signature_context(context))
3016 		if (lasso_legacy_extract_and_copy_signature_parameters(node, node_data))
3017 			context = lasso_node_get_signature(node);
3018 
3019 	if (snippet_signature->offset) {
3020 		id = SNIPPET_STRUCT_MEMBER(char *, node, G_TYPE_FROM_CLASS(klass), snippet_signature);
3021 	}
3022 
3023 	lasso_xmlnode_add_saml2_signature_template(xmlnode, context, id);
3024 }
3025 
3026 static struct XmlSnippet*
find_xml_snippet_by_name(LassoNode * node,char * name,LassoNodeClass ** class_p)3027 find_xml_snippet_by_name(LassoNode *node, char *name, LassoNodeClass **class_p)
3028 {
3029 	LassoNodeClass *class;
3030 	struct XmlSnippet *snippet;
3031 
3032 	class = LASSO_NODE_GET_CLASS(node);
3033 	while (class && LASSO_IS_NODE_CLASS(class) && class->node_data) {
3034 		for (snippet = class->node_data->snippets;
3035 				snippet && snippet->name && strcmp(snippet->name, name) != 0;
3036 				snippet++) ;
3037 		if (snippet && snippet->name) {
3038 			*class_p = class;
3039 			return snippet;
3040 		}
3041 		class = g_type_class_peek_parent(class);
3042 	}
3043 	*class_p = NULL;
3044 	return NULL;
3045 }
3046 
3047 static gboolean
find_path(LassoNode * node,char * path,LassoNode ** value_node,LassoNodeClass ** class_p,struct XmlSnippet ** snippet)3048 find_path(LassoNode *node, char *path, LassoNode **value_node, LassoNodeClass **class_p, struct XmlSnippet **snippet)
3049 {
3050 	char *s, *t;
3051 	struct XmlSnippet *tsnippet = NULL;
3052 	LassoNode *tnode = node;
3053 
3054 	*class_p = NULL;
3055 	s = path;
3056 	while (s) {
3057 		t = strchr(s, '/');
3058 		if (t) *t = 0;
3059 		tsnippet = find_xml_snippet_by_name(tnode, s, class_p);
3060 		if (t) {
3061 			tnode = SNIPPET_STRUCT_MEMBER(LassoNode *, tnode, G_TYPE_FROM_CLASS(*class_p),
3062 					tsnippet);
3063 			if (tnode == NULL)
3064 				return FALSE;
3065 
3066 			s = t+1;
3067 		} else {
3068 			s = NULL;
3069 		}
3070 	}
3071 
3072 	if (tsnippet == NULL)
3073 		return FALSE;
3074 
3075 	*snippet = tsnippet;
3076 	*value_node = tnode;
3077 	return TRUE;
3078 }
3079 
3080 
3081 static char*
get_value_by_path(LassoNode * node,char * path,struct XmlSnippet * xml_snippet)3082 get_value_by_path(LassoNode *node, char *path, struct XmlSnippet *xml_snippet)
3083 {
3084 	struct XmlSnippet *snippet;
3085 	LassoNode *value_node;
3086 	LassoNodeClass *class;
3087 	GType g_type;
3088 
3089 	if (find_path(node, path, &value_node, &class, &snippet) != TRUE)
3090 		return NULL;
3091 	g_type = G_TYPE_FROM_CLASS(class);
3092 
3093 	*xml_snippet = *snippet;
3094 
3095 	if (snippet->type & SNIPPET_BOOLEAN) {
3096 		gboolean v = SNIPPET_STRUCT_MEMBER(gboolean, value_node, g_type, snippet);
3097 		return v ? g_strdup("true") : g_strdup("false");
3098 	} else if (snippet->type & SNIPPET_INTEGER) {
3099 		int v = SNIPPET_STRUCT_MEMBER(int, value_node, g_type, snippet);
3100 		return g_strdup_printf("%d", v);
3101 	} else if (snippet->type == SNIPPET_NODE) {
3102 		LassoNode *value = SNIPPET_STRUCT_MEMBER(LassoNode *, value_node, g_type, snippet);
3103 		return lasso_node_build_query(value);
3104 	} else if (snippet->type == SNIPPET_EXTENSION) {
3105 		/* convert all of the <lib:Extension> into a string, already
3106 		 * escaped for URI usage */
3107 		GList *value = SNIPPET_STRUCT_MEMBER(GList *, value_node, g_type, snippet);
3108 		xmlChar *s, *s2;
3109 		GString *result = g_string_new("");
3110 		while (value) {
3111 			xmlNode *t = value->data;
3112 			xmlNode *c;
3113 
3114 			/* attributes */
3115 #if 0
3116 			xmlAttr *a;
3117 			for (a = t->properties; a; a = a->next) {
3118 				if (result->len)
3119 					g_string_append(result, "&");
3120 				s = xmlGetProp(t, a->name);
3121 				g_string_append(result, a->name);
3122 				g_string_append(result, "=");
3123 				s2 = lasso_xmlURIEscapeStr(s, NULL);
3124 				g_string_append(result, s2);
3125 				xmlFree(s2);
3126 				xmlFree(s);
3127 			}
3128 #endif
3129 
3130 			/* children (only simple ones and 1-level deep) */
3131 			for (c = t->children; c; c = c->next) {
3132 				if (c->type != XML_ELEMENT_NODE)
3133 					continue;
3134 				if (c->children->type != XML_TEXT_NODE)
3135 					continue;
3136 				if (c->properties != NULL)
3137 					continue;
3138 				if (result->len)
3139 					g_string_append(result, "&");
3140 				g_string_append(result, (char*)c->name);
3141 				g_string_append(result, "=");
3142 				s = xmlNodeGetContent(c);
3143 				s2 = lasso_xmlURIEscapeStr(s, NULL);
3144 				g_string_append(result, (char*)s2);
3145 				xmlFree(s2);
3146 				xmlFree(s);
3147 			}
3148 
3149 			value = g_list_next(value);
3150 		}
3151 		if (result->len == 0) {
3152 			lasso_release_gstring(result, TRUE);
3153 			return NULL;
3154 		}
3155 		return g_string_free(result, FALSE);
3156 	} else if (snippet->type == SNIPPET_LIST_CONTENT) {
3157 		/* not clear in spec; concat values with spaces */
3158 		GList *value = SNIPPET_STRUCT_MEMBER(GList *, value_node, g_type, snippet);
3159 		GString *result = g_string_new("");
3160 		while (value) {
3161 			result = g_string_append(result, (char*)value->data);
3162 			if (value->next)
3163 				result = g_string_append(result, " ");
3164 			value = value->next;
3165 		}
3166 		if (result->len == 0) {
3167 			lasso_release_gstring(result, TRUE);
3168 			return NULL;
3169 		}
3170 		return g_string_free(result, FALSE);
3171 	} else {
3172 		char *value = SNIPPET_STRUCT_MEMBER(char *, value_node, g_type, snippet);
3173 		if (value == NULL) return NULL;
3174 		return g_strdup(value);
3175 	}
3176 	return NULL;
3177 }
3178 
3179 static gboolean
set_value_at_path(LassoNode * node,char * path,char * query_value)3180 set_value_at_path(LassoNode *node, char *path, char *query_value)
3181 {
3182 	struct XmlSnippet *snippet;
3183 	LassoNode *value_node;
3184 	LassoNodeClass *class;
3185 	GType g_type;
3186 	void *value;
3187 
3188 	if (find_path(node, path, &value_node, &class, &snippet) != TRUE)
3189 		return FALSE;
3190 	g_type = G_TYPE_FROM_CLASS(class);
3191 
3192 	value = SNIPPET_STRUCT_MEMBER_P(value_node, g_type, snippet);
3193 
3194 	if (snippet->type & SNIPPET_INTEGER) {
3195 		int val = atoi(query_value);
3196 		(*(int*)value) = val;
3197 	} else if (snippet->type & SNIPPET_BOOLEAN) {
3198 		int val = (strcmp(query_value, "true") == 0);
3199 		(*(int*)value) = val;
3200 	} else if (snippet->type == SNIPPET_NODE) {
3201 		LassoNode *v = *(LassoNode**)value;
3202 		if (v == NULL) {
3203 			message(G_LOG_LEVEL_CRITICAL, "building node from query; unknown subnode");
3204 			g_assert_not_reached();
3205 		}
3206 		LASSO_NODE_GET_CLASS(v)->init_from_query(v, &query_value);
3207 	} else if (snippet->type == SNIPPET_LIST_CONTENT) {
3208 		char **elems = g_strsplit(query_value, " ", 0);
3209 		int i;
3210 		GList *l = NULL;
3211 		for (i = 0; elems[i]; i++) {
3212 			l = g_list_append(l, g_strdup(elems[i]));
3213 		}
3214 		g_strfreev(elems);
3215 		(*(GList**)value) = l;
3216 	} else {
3217 		(*(char**)value) = g_strdup(query_value);
3218 	}
3219 
3220 	return TRUE;
3221 }
3222 
3223 
3224 gchar*
lasso_node_build_query_from_snippets(LassoNode * node)3225 lasso_node_build_query_from_snippets(LassoNode *node)
3226 {
3227 	int i;
3228 	char path[100];
3229 	char *v;
3230 	GString *s;
3231 	xmlChar *t;
3232 	LassoNodeClass *class = LASSO_NODE_GET_CLASS(node);
3233 	struct QuerySnippet *query_snippets = NULL;
3234 	struct XmlSnippet xml_snippet;
3235 
3236 	while (class && LASSO_IS_NODE_CLASS(class) && class->node_data) {
3237 		if (class->node_data && class->node_data->query_snippets) {
3238 			query_snippets = class->node_data->query_snippets;
3239 			break;
3240 		}
3241 		class = g_type_class_peek_parent(class);
3242 	}
3243 	if (query_snippets == NULL)
3244 		return NULL;
3245 
3246 	s = g_string_sized_new(2000);
3247 
3248 	for (i=0; query_snippets[i].path; i++) {
3249 		g_strlcpy(path, query_snippets[i].path, 100);
3250 		v = get_value_by_path(node, path, &xml_snippet);
3251 		if (v && xml_snippet.type == SNIPPET_EXTENSION) {
3252 			if (s->len)
3253 				g_string_append(s, "&");
3254 			g_string_append(s, v);
3255 			lasso_release(v);
3256 			continue;
3257 		}
3258 		if (v) {
3259 			char *field_name = query_snippets[i].field_name;
3260 			if (field_name == NULL)
3261 				field_name = query_snippets[i].path;
3262 			if (s->len)
3263 				g_string_append(s, "&");
3264 			g_string_append(s, field_name);
3265 			g_string_append(s, "=");
3266 			t = lasso_xmlURIEscapeStr((xmlChar*)v, NULL);
3267 			g_string_append(s, (char*)t);
3268 			xmlFree(t);
3269 		}
3270 		if (v)
3271 			lasso_release(v);
3272 	}
3273 
3274 	return g_string_free(s, FALSE);
3275 }
3276 
3277 
3278 gboolean
lasso_node_init_from_query_fields(LassoNode * node,char ** query_fields)3279 lasso_node_init_from_query_fields(LassoNode *node, char **query_fields)
3280 {
3281 	int i, j;
3282 	char *field, *t;
3283 	LassoNodeClass *class = LASSO_NODE_GET_CLASS(node);
3284 	struct QuerySnippet *query_snippets = NULL;
3285 	gboolean has_extension = FALSE;
3286 
3287 	while (class && LASSO_IS_NODE_CLASS(class) && class->node_data) {
3288 		if (class->node_data && class->node_data->query_snippets) {
3289 			query_snippets = class->node_data->query_snippets;
3290 			break;
3291 		}
3292 		class = g_type_class_peek_parent(class);
3293 	}
3294 	if (query_snippets == NULL)
3295 		return FALSE;
3296 
3297 	for (i = 0; (field = query_fields[i]); i++) {
3298 		t = strchr(field, '=');
3299 		if (t == NULL)
3300 			continue;
3301 		*t = 0;
3302 
3303 		for (j=0; query_snippets[j].path; j++) {
3304 			char *field_name = query_snippets[j].field_name;
3305 			char path[100];
3306 
3307 			g_strlcpy(path, query_snippets[j].path, 100);
3308 
3309 			if (field_name == NULL)
3310 				field_name = query_snippets[j].path;
3311 			if (strcmp(field_name, "Extension") == 0) {
3312 				has_extension = TRUE;
3313 				continue;
3314 			}
3315 			if (strcmp(field, field_name) != 0)
3316 				continue;
3317 			set_value_at_path(node, path, t+1);
3318 			break;
3319 		}
3320 		if (query_snippets[j].path == NULL && has_extension &&
3321 				strcmp(field, "SigAlg") != 0 && strcmp(field, "Signature") != 0) {
3322 			/* got to the end without finding anything; and has
3323 			 * Extension; build it */
3324 			struct XmlSnippet *extension_snippet;
3325 			LassoNode *value_node;
3326 			LassoNodeClass *class;
3327 			GList **value;
3328 			xmlNode *xmlnode, *xmlchild;
3329 			if (find_path(node, "Extension", &value_node, &class, &extension_snippet) == TRUE) {
3330 				GType g_type = G_TYPE_FROM_CLASS(class);
3331 				value = SNIPPET_STRUCT_MEMBER_P(value_node, g_type,
3332 						extension_snippet);
3333 				if (*value) {
3334 					xmlnode = (*value)->data;
3335 				} else {
3336 					xmlnode = xmlNewNode(xmlNewNs(NULL,
3337 								(xmlChar*)LASSO_LIB_HREF,
3338 								(xmlChar*)LASSO_LIB_PREFIX),
3339 							(xmlChar*)"Extension");
3340 				}
3341 				xmlchild = xmlNewNode(NULL, (xmlChar*)field);
3342 				xmlAddChild(xmlchild, xmlNewText((xmlChar*)t+1));
3343 				xmlAddChild(xmlnode, xmlchild);
3344 				if (! *value)
3345 					*value = g_list_append(*value, xmlnode);
3346 			}
3347 		}
3348 		*t = '=';
3349 	}
3350 
3351 	return TRUE;
3352 }
3353 
3354 gboolean
lasso_node_init_from_saml2_query_fields(LassoNode * node,char ** query_fields,G_GNUC_UNUSED char ** relay_state)3355 lasso_node_init_from_saml2_query_fields(LassoNode *node, char **query_fields, G_GNUC_UNUSED char **relay_state)
3356 {
3357 	int i;
3358 	char *field, *t;
3359 	char *req = NULL;
3360 	char *enc = NULL;
3361 	gboolean rc;
3362 
3363 	for (i=0; (field=query_fields[i]); i++) {
3364 		t = strchr(field, '=');
3365 		if (t == NULL)
3366 			continue;
3367 		*t = 0;
3368 		if (strcmp(field, LASSO_SAML2_FIELD_ENCODING) == 0) {
3369 			enc = t+1;
3370 			continue;
3371 		}
3372 		if (strcmp(field, LASSO_SAML2_FIELD_REQUEST) == 0 || strcmp(field, LASSO_SAML2_FIELD_RESPONSE) == 0) {
3373 			req = t+1;
3374 			continue;
3375 		}
3376 	}
3377 
3378 	if (enc && strcmp(enc, LASSO_SAML2_DEFLATE_ENCODING) != 0) {
3379 		/* unknown encoding */
3380 		message(G_LOG_LEVEL_CRITICAL, "Unknown URL encoding: %s", enc);
3381 		return FALSE;
3382 	}
3383 
3384 	if (req == NULL) {
3385 		return FALSE;
3386 	}
3387 
3388 	rc = lasso_node_init_from_deflated_query_part(node, req);
3389 	if (rc == FALSE) {
3390 		return rc;
3391 	}
3392 
3393 	return TRUE;
3394 }
3395 
3396 static void
xmlDeclareNs(xmlNode * root_node,xmlNode * node)3397 xmlDeclareNs(xmlNode *root_node, xmlNode *node)
3398 {
3399 	xmlNs *ns;
3400 	xmlNode *t;
3401 
3402 	if (strcmp((char*)node->name, "Signature") == 0)
3403 		return;
3404 
3405 	for (ns = node->nsDef; ns; ns = ns->next) {
3406 		if (ns->prefix && strcmp((char*)ns->prefix, "xsi") != 0) {
3407 			xmlNewNs(root_node, ns->href, ns->prefix);
3408 		}
3409 	}
3410 	for (t = node->children; t; t = t->next) {
3411 		if (t->type == XML_ELEMENT_NODE) {
3412 			xmlDeclareNs(root_node, t);
3413 		}
3414 	}
3415 }
3416 
3417 static inline int
sameNs(xmlNs * ns1,xmlNs * ns2)3418 sameNs(xmlNs *ns1, xmlNs *ns2)
3419 {
3420 	/* this checks ns->prefix instead of ns->href so it is possible to
3421 	 * merge down to an earlier version of liberty namespace
3422 	 */
3423 	return (ns1 == NULL && ns2 == NULL) || (
3424 			ns1 && ns2 && ns1->prefix && ns2->prefix &&
3425 			strcmp((char*)ns1->prefix, (char*)ns2->prefix) == 0 &&
3426 			strcmp((char*)ns1->href, (char*)ns2->href) == 0);
3427 }
3428 
3429 static void
xmlPropUseNsDef(xmlNs * ns,xmlNode * node)3430 xmlPropUseNsDef(xmlNs *ns, xmlNode *node)
3431 {
3432 	xmlAttr *attr;
3433 
3434 	for (attr = node->properties; attr; attr = attr->next) {
3435 		if (sameNs(ns, attr->ns)) {
3436 			attr->ns = ns;
3437 		}
3438 	}
3439 }
3440 
3441 static void
xmlUseNsDef(xmlNs * ns,xmlNode * node)3442 xmlUseNsDef(xmlNs *ns, xmlNode *node)
3443 {
3444 	xmlNode *t;
3445 	xmlNs *ns2;
3446 	xmlNs *ns3 = NULL;
3447 
3448 	xmlPropUseNsDef(ns, node);
3449 	if (sameNs(ns, node->ns)) {
3450 		node->ns = ns;
3451 	}
3452 
3453 	for (t = node->children; t; t = t->next) {
3454 		if (t->type == XML_ELEMENT_NODE)
3455 			xmlUseNsDef(ns, t);
3456 	}
3457 
3458 	if (sameNs(node->nsDef, ns)) {
3459 		ns3 = node->nsDef;
3460 		node->nsDef = node->nsDef->next;
3461 		xmlFreeNs(ns3);
3462 	} else if (node->nsDef) {
3463 		for (ns2 = node->nsDef; ns2->next; ns2 = ns2->next) {
3464 			if (sameNs(ns2->next, ns)) {
3465 				ns3 = ns2->next;
3466 				ns2->next = ns2->next->next;
3467 				xmlFreeNs(ns3);
3468 				if (ns2->next == NULL)
3469 					break;
3470 			}
3471 		}
3472 	}
3473 }
3474 
3475 /**
3476  * xmlCleanNs
3477  * @root_node: the root #xmlNode where to start the cleaning.
3478  *
3479  * xmlCleanNs removes duplicate xml namespace declarations and merge them on
3480  * the @root_node.
3481  **/
3482 void
xmlCleanNs(xmlNode * root_node)3483 xmlCleanNs(xmlNode *root_node)
3484 {
3485 	xmlNs *ns;
3486 	xmlNode *t;
3487 
3488 	for (t = root_node->children; t; t = t->next)
3489 		if (t->type == XML_ELEMENT_NODE)
3490 			xmlDeclareNs(root_node, t);
3491 
3492 	for (ns = root_node->nsDef; ns; ns = ns->next) {
3493 		for (t = root_node->children; t; t = t->next)
3494 			if (t->type == XML_ELEMENT_NODE)
3495 				xmlUseNsDef(ns, t);
3496 	}
3497 }
3498 
3499 void
xml_insure_namespace(xmlNode * xmlnode,xmlNs * ns,gboolean force,gchar * ns_href,gchar * ns_prefix)3500 xml_insure_namespace(xmlNode *xmlnode, xmlNs *ns, gboolean force, gchar *ns_href, gchar *ns_prefix)
3501 {
3502 	xmlNode *t = xmlnode->children;
3503 
3504 	if (ns == NULL) {
3505 		for (ns = xmlnode->nsDef; ns; ns = ns->next) {
3506 			if (ns->href && lasso_strisequal((gchar *)ns->href,ns_href)) {
3507 				break;
3508 			}
3509 		}
3510 		if (ns == NULL) {
3511 			ns = xmlNewNs(xmlnode, (xmlChar*)ns_href, (xmlChar*)ns_prefix);
3512 		}
3513 	}
3514 
3515 	xmlSetNs(xmlnode, ns);
3516 	while (t) {
3517 		if (t->type == XML_ELEMENT_NODE && (force == TRUE || t->ns == NULL)) {
3518 			xml_insure_namespace(t, ns, force, NULL, NULL);
3519 		}
3520 		t = t->next;
3521 	}
3522 }
3523 
3524 /**
3525  * lasso_node_get_xmlnode_for_any_type:
3526  * @node: a #LassoNode.
3527  * @xmlnode: the #xmlNode returned.
3528  *
3529  * Return value: a xmlNode completed with the content of the produced by the get_xmlNode virtual
3530  * method of the parent class.
3531  */
3532 xmlNode*
lasso_node_get_xmlnode_for_any_type(LassoNode * node,xmlNode * cur)3533 lasso_node_get_xmlnode_for_any_type(LassoNode *node, xmlNode *cur)
3534 {
3535 	xmlNode *original_xmlnode;
3536 
3537 	original_xmlnode = lasso_node_get_original_xmlnode(node);
3538 	if (cur) {
3539 		if (original_xmlnode) {
3540 			xmlNode *children = xmlCopyNodeList(original_xmlnode->children);
3541 			xmlAttr *attrs = xmlCopyPropList(cur, original_xmlnode->properties);
3542 			if (cur->properties == NULL) {
3543 				cur->properties = attrs;
3544 			} else {
3545 				xmlAttr *it = cur->properties;
3546 				while (it->next) {
3547 					it = it->next;
3548 				}
3549 				it->next = attrs;
3550 			}
3551 			xmlAddChildList(cur, children);
3552 			return cur;
3553 		} else {
3554 			return cur;
3555 		}
3556 	} else {
3557 		if (original_xmlnode) {
3558 			return xmlCopyNode(original_xmlnode, 1);
3559 		} else {
3560 			return cur;
3561 		}
3562 	}
3563 }
3564 
3565 /**
3566  * lasso_node_get_name:
3567  * @node: a #LassoNode
3568  *
3569  * Return the XML element name for this object, the one that would be used in the XML dump of this
3570  * object.
3571  *
3572  * Return value: the name of the object, the value must not be stored.
3573  */
3574 const char*
lasso_node_get_name(LassoNode * node)3575 lasso_node_get_name(LassoNode *node)
3576 {
3577 	struct _CustomElement *custom_element;
3578 	LassoNodeClass *klass;
3579 	g_return_val_if_fail(LASSO_IS_NODE(node), NULL);
3580 
3581 	custom_element = _lasso_node_get_custom_element(node);
3582 	if (custom_element && custom_element->nodename) {
3583 		return custom_element->nodename;
3584 	}
3585 	klass = LASSO_NODE_GET_CLASS(node);
3586 	return klass->node_data->node_name;
3587 }
3588 
3589 /**
3590  * lasso_node_get_name:
3591  * @node: a #LassoNode
3592  *
3593  * Return the XML element name for this object, the one that would be used in the XML dump of this
3594  * object.
3595  *
3596  * Return value: the name of the object, the value must not be stored.
3597  */
3598 const char*
lasso_node_get_namespace(LassoNode * node)3599 lasso_node_get_namespace(LassoNode *node)
3600 {
3601 	struct _CustomElement *custom_element;
3602 	LassoNodeClass *klass;
3603 	g_return_val_if_fail(LASSO_IS_NODE(node), NULL);
3604 
3605 	custom_element = _lasso_node_get_custom_element(node);
3606 	if (custom_element && custom_element->nodename) {
3607 		return custom_element->href;
3608 	}
3609 	klass = LASSO_NODE_GET_CLASS(node);
3610 	if (klass->node_data && klass->node_data->ns)
3611 		return (const char*)klass->node_data->ns->href;
3612 	return NULL;
3613 }
3614 
3615 
3616 /**
3617  * lasso_node_export_to_saml2_query:
3618  * @node: the #LassoNode object to pass as a query
3619  * @param_name: the key value for the query string parameter
3620  * @url:(allow-none): an optional URL to prepend to the query string
3621  * @key:(allow-none): a #LassoKey object
3622  *
3623  * Export a node as signed query string, the node must support serialization as a query.
3624  *
3625  * Return value: an HTTP URL or query string if successful, NULL otherwise.
3626  */
3627 char*
lasso_node_export_to_saml2_query(LassoNode * node,const char * param_name,const char * url,LassoKey * key)3628 lasso_node_export_to_saml2_query(LassoNode *node, const char *param_name, const char *url,
3629 		LassoKey *key)
3630 {
3631 	char *value = NULL, *query = NULL, *signed_query = NULL, *result = NULL;
3632 	xmlChar *encoded_param = NULL;
3633 
3634 	value = lasso_node_build_deflated_query(node);
3635 	if (! value)
3636 		goto cleanup;
3637 	encoded_param = lasso_xmlURIEscapeStr(BAD_CAST param_name, NULL);
3638 	if (! encoded_param)
3639 		goto cleanup;
3640 	query = g_strdup_printf("%s=%s", encoded_param, value);
3641 	if (! query)
3642 		goto cleanup;
3643 	if (LASSO_IS_KEY(key)) {
3644 		signed_query = lasso_key_query_sign(key, query);
3645 	} else {
3646 		lasso_transfer_string(signed_query, query);
3647 	}
3648 	if (! signed_query)
3649 		goto cleanup;
3650 	if (url) {
3651 		result = lasso_concat_url_query(url, signed_query);
3652 	} else {
3653 		lasso_transfer_string(result, signed_query);
3654 	}
3655 
3656 cleanup:
3657 	lasso_release_string(value);
3658 	lasso_release_xml_string(encoded_param);
3659 	lasso_release_string(query);
3660 	lasso_release_string(signed_query);
3661 	return result;
3662 }
3663 
3664 /**
3665  * lasso_node_new_from_saml2_query:
3666  * @url_or_qs: an URL containing a query string or a query string only
3667  * @param_name: the key value for the query string parameter to extract as a #LassoNode.
3668  * @key:(allow-none): a #LassoKey object
3669  *
3670  * Verify the signature on a SAML-2 encoded query string and return the encoded node.
3671  *
3672  * Return value: a newly build #LassoNode if successful, NULL otherwise.
3673  */
3674 LassoNode*
lasso_node_new_from_saml2_query(const char * url_or_qs,const char * param_name,LassoKey * key)3675 lasso_node_new_from_saml2_query(const char *url_or_qs, const char *param_name, LassoKey *key)
3676 {
3677 	char *needle = NULL;
3678 	LassoNode *result = NULL;
3679 
3680 	if (! url_or_qs || ! param_name)
3681 		return NULL;
3682 	needle = strchr(url_or_qs, '?');
3683 	if (needle) {
3684 		url_or_qs = (const char*)(needle+1);
3685 	}
3686 	if (key) {
3687 		goto_cleanup_if_fail(lasso_key_query_verify(key, url_or_qs) == 0);
3688 	}
3689 cleanup:
3690 	return result;
3691 }
3692