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