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  * The effect of defining the _DEFAULT_SOURCE macro is equivalent to
26  * the effect of explicitly defining three macros in earlier glibc
27  * versions: -D_BSD_SOURCE -D_SVID_SOURCE -D_POSIX_C_SOURCE=200809C
28  */
29 #define _DEFAULT_SOURCE
30 /* permit importation of strptime for glibc2 */
31 #if !defined(__sun)
32 #define _XOPEN_SOURCE
33 #endif
34 /* permit importation of timegm for glibc2, wait for people to complain it does not work on their
35  * system. */
36 #define _BSD_SOURCE
37 #include "private.h"
38 #include <string.h>
39 #include <strings.h>
40 #include <time.h>
41 #include <ctype.h>
42 #include <stdarg.h>
43 
44 #include <libxml/uri.h>
45 #include <libxml/parser.h>
46 #include <libxml/parserInternals.h>
47 #include <libxml/xmlIO.h>
48 
49 #include <openssl/pem.h>
50 #include <openssl/sha.h>
51 #include <openssl/engine.h>
52 #include <openssl/hmac.h>
53 #include <openssl/evp.h>
54 
55 #include <xmlsec/base64.h>
56 #include <xmlsec/crypto.h>
57 #include <xmlsec/templates.h>
58 #include <xmlsec/xmldsig.h>
59 #include <xmlsec/xmltree.h>
60 #include <xmlsec/errors.h>
61 #include <xmlsec/openssl/x509.h>
62 #include <xmlsec/openssl/crypto.h>
63 
64 #include <zlib.h>
65 
66 #include <glib.h>
67 #include "xml.h"
68 #include "xml_enc.h"
69 #include "saml-2.0/saml2_assertion.h"
70 #include <unistd.h>
71 #include "../debug.h"
72 #include "../utils.h"
73 #include <stdarg.h>
74 #include <ctype.h>
75 #include "../lasso_config.h"
76 
77 /**
78  * SECTION:saml2_utils
79  * @short_description: Misc functions used inside Lasso
80  * @stability: Internal
81  */
82 
83 /* A query string can be 3 times larger than the byte string value, because of the octet encoding
84  * %xx */
85 const int query_string_attribute_length_limit = 8192 * 3;
86 static xmlSecKeyPtr lasso_get_public_key_from_private_key_file(const char *private_key_file);
87 static gboolean is_base64(const char *message);
88 static void xmlDetectSAX2(xmlParserCtxtPtr ctxt);
89 
90 /**
91  * lasso_build_random_sequence:
92  * @buffer: buffer to fill with random sequence
93  * @size: the sequence size in byte (character)
94  *
95  * Builds a random sequence of [0-9A-F] characters of size @size.
96  *
97  * Return value: None
98  **/
99 void
lasso_build_random_sequence(char * buffer,unsigned int size)100 lasso_build_random_sequence(char *buffer, unsigned int size)
101 {
102 	char *t;
103 	unsigned int rnd, i;
104 
105 	t = buffer;
106 	while (t-buffer < (int)size) {
107 		rnd = g_random_int();
108 		for (i=0; i<sizeof(int); i++) {
109 			*(t++) = '0' + ((rnd>>i*4)&0xf);
110 			if (*(t-1) > '9') *(t-1) += 7;
111 		}
112 	}
113 }
114 
115 /**
116  * lasso_build_unique_id:
117  * @size: the ID's length (between 32 and 40)
118  *
119  * Builds an ID which has an unicity probability of 2^(-size*4).
120  *
121  * Return value:(transfer full): a "unique" ID (begin always with _ character)
122  **/
123 char*
lasso_build_unique_id(unsigned int size)124 lasso_build_unique_id(unsigned int size)
125 {
126 	/*
127 	 * When generating one-time-use identifiers for Principals, in the
128 	 * case that a pseudorandom technique is employed, the probability
129 	 * of two randomly chosen identifiers being identical MUST be less
130 	 * than or equal to 2-128 and SHOULD be less than or equal to 2-160.
131 	 * These levels correspond, respectively, to use of strong 128-bit
132 	 * and 160-bit hash functions, in conjunction with sufficient input
133 	 * entropy.
134 	 *   -- 3.1.4 Name Identifier Construction
135 	 *      in « Liberty ID-FF Protocols and Schema Specification »
136 	 */
137 	char *result;
138 
139 	g_assert(size >= 32);
140 
141 	result = g_malloc(size+2); /* trailing \0 and leading _ */
142 	result[0] = '_';
143 	lasso_build_random_sequence(result+1, size);
144 	result[size+1] = 0;
145 	return result;
146 }
147 
148 /**
149  * lasso_time_to_iso_8601_gmt:
150  * @now: a #time_t value
151  *
152  * Format the given time as an ISO 8601 date-time value in UTC.
153  *
154  * Return value:(transfer full): an ISO 9601 formatted string.
155  */
156 char*
lasso_time_to_iso_8601_gmt(time_t now)157 lasso_time_to_iso_8601_gmt(time_t now)
158 {
159 	struct tm *tm;
160 	char *ret;
161 
162 	ret = g_malloc(21);
163 	tm = gmtime(&now);
164 	strftime(ret, 21, "%Y-%m-%dT%H:%M:%SZ", tm);
165 
166 	return ret;
167 }
168 
169 /**
170  * lasso_get_current_time:
171  *
172  * Returns the current time, format is "yyyy-mm-ddThh:mm:ssZ".
173  *
174  * Return value: a string
175  **/
176 char*
lasso_get_current_time()177 lasso_get_current_time()
178 {
179 	return lasso_time_to_iso_8601_gmt(time(NULL));
180 }
181 
182 static const char xsdtime_format1[] = "dddd-dd-ddTdd:dd:ddZ";
183 static const char xsdtime_format2[] = "dddd-dd-ddTdd:dd:dd.?Z";
184 
185 static gboolean
xsdtime_match_format(const char * xsdtime,const char * format)186 xsdtime_match_format(const char *xsdtime, const char *format)
187 {
188 	while (*format && *xsdtime) {
189 		if (*format == 'd' && isdigit(*xsdtime)) {
190 			++format;
191 			++xsdtime;
192 		} else if (*format == '?') {
193 			while (isdigit(*xsdtime))
194 				++xsdtime;
195 			++format;
196 		} else if (*format == *xsdtime) {
197 			++format;
198 			++xsdtime;
199 		} else {
200 			break;
201 		}
202 	}
203 	if (*format == '\0' && *xsdtime == '\0') {
204 		return TRUE;
205 	} else {
206 		return FALSE;
207 	}
208 }
209 
210 /**
211  * lasso_iso_8601_gmt_to_time_t:
212  * @xsdtime: an xsd time value
213  *
214  * Return value: a corresponding time_t value if possible.
215  */
216 time_t
lasso_iso_8601_gmt_to_time_t(const char * xsdtime)217 lasso_iso_8601_gmt_to_time_t(const char *xsdtime)
218 {
219 	struct tm tm;
220 	char *strptime_ret;
221 
222 	if (xsdtime == NULL) {
223 		return -1;
224 	}
225 
226 	if (xsdtime_match_format(xsdtime, xsdtime_format1)) {
227 		strptime_ret = strptime (xsdtime, "%Y-%m-%dT%H:%M:%SZ", &tm);
228 		if (strptime_ret == NULL) {
229 			return -1;
230 		}
231 	} else if (xsdtime_match_format(xsdtime, xsdtime_format2)) {
232 		strptime_ret = strptime (xsdtime, "%Y-%m-%dT%H:%M:%S.", &tm);
233 		if (strptime_ret == NULL) {
234 			return -1;
235 		}
236 	} else {
237             return -1;
238         }
239 	return timegm(&tm);
240 }
241 
242 /**
243  * lasso_get_pem_file_type:
244  * @pem_file: a pem file
245  *
246  * Gets the type of a pem file.
247  *
248  * Return value: the pem file type
249  **/
250 LassoPemFileType
lasso_get_pem_file_type(const char * pem_file)251 lasso_get_pem_file_type(const char *pem_file)
252 {
253 	BIO* bio;
254 	EVP_PKEY *pkey;
255 	X509 *cert;
256 	LassoPemFileType type = LASSO_PEM_FILE_TYPE_UNKNOWN;
257 
258 	g_return_val_if_fail(pem_file != NULL, LASSO_PARAM_ERROR_INVALID_VALUE);
259 
260 	bio = BIO_new_file(pem_file, "rb");
261 	if (bio == NULL) {
262 		message(G_LOG_LEVEL_CRITICAL, "Failed to open %s pem file", pem_file);
263 		return LASSO_PEM_FILE_TYPE_UNKNOWN;
264 	}
265 
266 	pkey = PEM_read_bio_PUBKEY(bio, NULL, NULL, NULL);
267 	if (pkey != NULL) {
268 		type = LASSO_PEM_FILE_TYPE_PUB_KEY;
269 		EVP_PKEY_free(pkey);
270 	} else {
271 		if (BIO_reset(bio) == 0) {
272 			pkey = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL);
273 			if (pkey != NULL) {
274 				type = LASSO_PEM_FILE_TYPE_PRIVATE_KEY;
275 				EVP_PKEY_free(pkey);
276 			} else {
277 				if (BIO_reset(bio) == 0) {
278 					cert = PEM_read_bio_X509(bio, NULL, NULL, NULL);
279 					if (cert != NULL) {
280 						type = LASSO_PEM_FILE_TYPE_CERT;
281 						X509_free(cert);
282 					}
283 				}
284 			}
285 		}
286 	}
287 	BIO_free(bio);
288 
289 	return type;
290 }
291 
292 /**
293  * lasso_get_public_key_from_pem_file:
294  * @file: the name of a file containing a public key
295  *
296  * Load a public key from a file in the PEM format.
297  *
298  * Returns: a #xmlSecKey if one is found, NULL otherwise.
299  */
lasso_get_public_key_from_pem_file(const char * file)300 xmlSecKeyPtr lasso_get_public_key_from_pem_file(const char *file) {
301 	LassoPemFileType file_type;
302 	xmlSecKeyPtr pub_key = NULL;
303 
304 	file_type = lasso_get_pem_file_type(file);
305 	switch (file_type) {
306 		case LASSO_PEM_FILE_TYPE_UNKNOWN:
307 			message(G_LOG_LEVEL_CRITICAL, "PEM file type unknown: %s", file);
308 			break; /* with a warning ? */
309 		case LASSO_PEM_FILE_TYPE_CERT:
310 			pub_key = lasso_get_public_key_from_pem_cert_file(file);
311 			break;
312 		case LASSO_PEM_FILE_TYPE_PUB_KEY:
313 			pub_key = xmlSecCryptoAppKeyLoad(file,
314 					xmlSecKeyDataFormatPem, NULL, NULL, NULL);
315 			break;
316 		case LASSO_PEM_FILE_TYPE_PRIVATE_KEY:
317 			pub_key = lasso_get_public_key_from_private_key_file(file);
318 
319 			break; /* with a warning ? */
320 	}
321 	return pub_key;
322 }
323 /**
324  * lasso_get_public_key_from_pem_cert_file:
325  * @pem_cert_file: an X509 pem certificate file
326  *
327  * Gets the public key in an X509 pem certificate file.
328  *
329  * Return value: a public key or NULL if an error occurs.
330  **/
331 xmlSecKeyPtr
lasso_get_public_key_from_pem_cert_file(const char * pem_cert_file)332 lasso_get_public_key_from_pem_cert_file(const char *pem_cert_file)
333 {
334 	FILE *fd;
335 	X509 *pem_cert;
336 	xmlSecKeyDataPtr data;
337 	xmlSecKeyPtr key = NULL;
338 
339 	g_return_val_if_fail(pem_cert_file != NULL, NULL);
340 
341 	/* load pem certificate from file */
342 	fd = fopen(pem_cert_file, "r");
343 	if (fd == NULL) {
344 		message(G_LOG_LEVEL_CRITICAL, "Failed to open %s pem certificate file",
345 				pem_cert_file);
346 		return NULL;
347 	}
348 	/* read the pem X509 certificate */
349 	pem_cert = PEM_read_X509(fd, NULL, NULL, NULL);
350 	fclose(fd);
351 	if (pem_cert == NULL) {
352 		message(G_LOG_LEVEL_CRITICAL, "Failed to read X509 certificate");
353 		return NULL;
354 	}
355 
356 	/* get public key value in certificate */
357 	data = xmlSecOpenSSLX509CertGetKey(pem_cert);
358 	if (data != NULL) {
359 		/* create key and set key value */
360 		key = xmlSecKeyCreate();
361 		xmlSecKeySetValue(key, data);
362 	} else {
363 		message(G_LOG_LEVEL_CRITICAL,
364 				"Failed to get the public key in the X509 certificate");
365 	}
366 	X509_free(pem_cert);
367 
368 	return key;
369 }
370 
371 /**
372  * lasso_get_public_key_from_private_key_file:
373  * @private_key_file: the name of a file containing a private key in PEM format
374  *
375  * Load a public key from a private key.
376  *
377  * Returns: a new $xmlSecKey containing the private key
378  */
379 static xmlSecKeyPtr
lasso_get_public_key_from_private_key_file(const char * private_key_file)380 lasso_get_public_key_from_private_key_file(const char *private_key_file)
381 {
382 	return xmlSecCryptoAppKeyLoad(private_key_file,
383 			xmlSecKeyDataFormatPem, NULL, NULL, NULL);
384 }
385 
386 /**
387  * lasso_load_certs_from_pem_certs_chain_file:
388  * @pem_certs_chain_file: a CA certificate chain file
389  *
390  * Creates a keys manager and loads inside all the CA certificates of
391  * @pem_certs_chain_file. Caller is responsible for freeing it with
392  * xmlSecKeysMngrDestroy() function.
393  *
394  * Return value: a newly allocated keys manager or NULL if an error occurs.
395  **/
396 xmlSecKeysMngrPtr
lasso_load_certs_from_pem_certs_chain_file(const char * pem_certs_chain_file)397 lasso_load_certs_from_pem_certs_chain_file(const char* pem_certs_chain_file)
398 {
399 	xmlSecKeysMngrPtr keys_mngr = NULL;
400 	GIOChannel *gioc = NULL;
401 	gchar *line = NULL;
402 	gsize len, pos;
403 	GString *cert = NULL;
404 	gint ret;
405 	gint certificates = 0;
406 
407 	/* No file just return NULL */
408 	goto_cleanup_if_fail (pem_certs_chain_file && strlen(pem_certs_chain_file) != 0);
409 	gioc = g_io_channel_new_file(pem_certs_chain_file, "r", NULL);
410 	if (! gioc) {
411 		message(G_LOG_LEVEL_CRITICAL, "Cannot open chain file %s", pem_certs_chain_file);
412 		goto cleanup;
413 	}
414 
415 	keys_mngr = xmlSecKeysMngrCreate();
416 	if (keys_mngr == NULL) {
417 		message(G_LOG_LEVEL_CRITICAL,
418 				lasso_strerror(LASSO_DS_ERROR_KEYS_MNGR_CREATION_FAILED));
419 		goto cleanup;
420 	}
421 
422 	/* initialize keys manager */
423 	if (xmlSecCryptoAppDefaultKeysMngrInit(keys_mngr) < 0) {
424 		message(G_LOG_LEVEL_CRITICAL,
425 				lasso_strerror(LASSO_DS_ERROR_KEYS_MNGR_INIT_FAILED));
426 		xmlSecKeysMngrDestroy(keys_mngr);
427 		goto cleanup;
428 	}
429 
430 	while (g_io_channel_read_line(gioc, &line, &len, &pos, NULL) == G_IO_STATUS_NORMAL) {
431 		if (line != NULL && g_strstr_len(line, 64, "BEGIN CERTIFICATE") != NULL) {
432 			cert = g_string_new(line);
433 		} else if (cert != NULL && line != NULL && g_strstr_len(line, 64, "END CERTIFICATE") != NULL) {
434 			g_string_append(cert, line);
435 			/* load the new certificate found in the keys manager */
436 			/* create keys manager */
437 			ret = xmlSecCryptoAppKeysMngrCertLoadMemory(keys_mngr,
438 					(const xmlSecByte*) cert->str,
439 					(xmlSecSize) cert->len,
440 					xmlSecKeyDataFormatPem,
441 					xmlSecKeyDataTypeTrusted);
442 			if (ret < 0) {
443 				goto cleanup;
444 			}
445 			certificates++;
446 			lasso_release_gstring(cert, TRUE);
447 			cert = NULL;
448 		} else if (cert != NULL && line != NULL && line[0] != '\0') {
449 			g_string_append(cert, line);
450 		}
451 		/* free last line read */
452 		lasso_release_string(line);
453 	}
454 
455 cleanup:
456 	if (gioc) {
457 		g_io_channel_shutdown(gioc, TRUE, NULL);
458 		g_io_channel_unref(gioc);
459 	}
460 	if (cert)
461 		lasso_release_gstring(cert, TRUE);
462 	if (certificates == 0)
463 		lasso_release_key_manager(keys_mngr);
464 	lasso_release_string(line);
465 
466 	return keys_mngr;
467 }
468 
469 /*
470  * lasso_query_sign:
471  * @query: a query (an url-encoded node)
472  * @sign_method: the Signature transform method
473  * @private_key_file: the private key
474  * @private_key_file_password: the private key password
475  *
476  * Signs a query (url-encoded message).
477  *
478  * Return value: a newly allocated query signed or NULL if an error occurs.
479  **/
480 char*
lasso_query_sign(char * query,LassoSignatureContext context)481 lasso_query_sign(char *query, LassoSignatureContext context)
482 {
483 	char *digest = NULL; /* 160 bit buffer */
484 	RSA *rsa = NULL;
485 	DSA *dsa = NULL;
486 	unsigned char *sigret = NULL;
487 	unsigned int siglen = 0;
488 	xmlChar *b64_sigret = NULL, *e_b64_sigret = NULL;
489 	char *new_query = NULL, *s_new_query = NULL;
490 	int status = 0;
491 	const xmlChar *algo_href = NULL;
492 	char *hmac_key;
493 	size_t hmac_key_length;
494 	const EVP_MD *md = NULL;
495 	xmlSecKey *key;
496 	xmlSecKeyData *key_data;
497 	unsigned int sigret_size = 0;
498 	LassoSignatureMethod sign_method;
499         lasso_error_t rc = 0;
500 
501 	g_return_val_if_fail(query != NULL, NULL);
502 	g_return_val_if_fail(lasso_validate_signature_method(context.signature_method), NULL);
503 
504 	key = context.signature_key;
505 	sign_method = context.signature_method;
506 	key_data = xmlSecKeyGetValue(key);
507 
508 
509 	/* add SigAlg */
510 	switch (sign_method) {
511 		case LASSO_SIGNATURE_METHOD_RSA_SHA1:
512 			algo_href = xmlSecHrefRsaSha1;
513 			break;
514 		case LASSO_SIGNATURE_METHOD_DSA_SHA1:
515 			algo_href = xmlSecHrefDsaSha1;
516 			break;
517 		case LASSO_SIGNATURE_METHOD_HMAC_SHA1:
518 			algo_href = xmlSecHrefHmacSha1;
519 			break;
520 		case LASSO_SIGNATURE_METHOD_RSA_SHA256:
521 			algo_href = xmlSecHrefRsaSha256;
522 			break;
523 		case LASSO_SIGNATURE_METHOD_HMAC_SHA256:
524 			algo_href = xmlSecHrefHmacSha256;
525 			break;
526 		case LASSO_SIGNATURE_METHOD_RSA_SHA384:
527 			algo_href = xmlSecHrefRsaSha384;
528 			break;
529 		case LASSO_SIGNATURE_METHOD_HMAC_SHA384:
530 			algo_href = xmlSecHrefHmacSha384;
531 			break;
532 		case LASSO_SIGNATURE_METHOD_RSA_SHA512:
533 			algo_href = xmlSecHrefRsaSha512;
534 			break;
535 		case LASSO_SIGNATURE_METHOD_HMAC_SHA512:
536 			algo_href = xmlSecHrefHmacSha512;
537 			break;
538 		case LASSO_SIGNATURE_METHOD_NONE:
539 		case LASSO_SIGNATURE_METHOD_LAST:
540 			g_assert_not_reached();
541 	}
542 
543 	{
544 		const char *t = (char*)lasso_xmlURIEscapeStr(algo_href, NULL);
545 		new_query = g_strdup_printf("%s&SigAlg=%s", query, t);
546 		xmlFree(BAD_CAST t);
547 	}
548 
549 	/* build buffer digest */
550 	switch (sign_method) {
551 		case LASSO_SIGNATURE_METHOD_RSA_SHA1:
552 		case LASSO_SIGNATURE_METHOD_DSA_SHA1:
553 			digest = lasso_sha1(new_query);
554 			break;
555 		case LASSO_SIGNATURE_METHOD_RSA_SHA256:
556 			digest = lasso_sha256(new_query);
557 			break;
558 		case LASSO_SIGNATURE_METHOD_RSA_SHA384:
559 			digest = lasso_sha384(new_query);
560 			break;
561 		case LASSO_SIGNATURE_METHOD_RSA_SHA512:
562 			digest = lasso_sha512(new_query);
563 		default:
564 			break;
565 	}
566 	switch (sign_method) {
567 		case LASSO_SIGNATURE_METHOD_RSA_SHA1:
568 		case LASSO_SIGNATURE_METHOD_DSA_SHA1:
569 		case LASSO_SIGNATURE_METHOD_RSA_SHA256:
570 		case LASSO_SIGNATURE_METHOD_RSA_SHA384:
571 		case LASSO_SIGNATURE_METHOD_RSA_SHA512:
572 			if (digest == NULL) {
573 				message(G_LOG_LEVEL_CRITICAL, "Failed to build the buffer digest");
574 				goto done;
575 			}
576 		default:
577 			break;
578 	}
579 	/* extract the OpenSSL key */
580 	switch (sign_method) {
581 		case LASSO_SIGNATURE_METHOD_RSA_SHA1:
582 		case LASSO_SIGNATURE_METHOD_RSA_SHA256:
583 		case LASSO_SIGNATURE_METHOD_RSA_SHA384:
584 		case LASSO_SIGNATURE_METHOD_RSA_SHA512:
585 			rsa = xmlSecOpenSSLKeyDataRsaGetRsa(key_data);
586 			g_assert(rsa);
587 			/* alloc memory for sigret */
588 			sigret_size = RSA_size(rsa);
589 			break;
590 		case LASSO_SIGNATURE_METHOD_DSA_SHA1:
591 			dsa = xmlSecOpenSSLKeyDataDsaGetDsa(key_data);
592 			g_assert(dsa);
593 			/* alloc memory for sigret */
594 			sigret_size = DSA_size(dsa);
595 			break;
596 		case LASSO_SIGNATURE_METHOD_HMAC_SHA1:
597 		case LASSO_SIGNATURE_METHOD_HMAC_SHA256:
598 		case LASSO_SIGNATURE_METHOD_HMAC_SHA384:
599 		case LASSO_SIGNATURE_METHOD_HMAC_SHA512:
600 			if ((rc = lasso_get_hmac_key(key, (void**)&hmac_key,
601 										 &hmac_key_length))) {
602 				message(G_LOG_LEVEL_CRITICAL, "Failed to get hmac key (%s)", lasso_strerror(rc));
603 				goto done;
604 			}
605 			g_assert(hmac_key);
606 			md = EVP_sha1();
607 			sigret_size = EVP_MD_size(md);
608 			/* key should be at least 128 bits long */
609 			if (hmac_key_length < 16) {
610 				critical("HMAC key should be at least 128 bits long");
611 				goto done;
612 			}
613 			break;
614 		default:
615 			g_assert_not_reached();
616 	}
617 	sigret = (unsigned char *)g_malloc (sigret_size);
618 
619 	switch (sign_method) {
620 		case LASSO_SIGNATURE_METHOD_RSA_SHA1:
621 			/* sign digest message */
622 			status = RSA_sign(NID_sha1, (unsigned char*)digest, SHA_DIGEST_LENGTH, sigret,
623 					&siglen, rsa);
624 			break;
625 		case LASSO_SIGNATURE_METHOD_RSA_SHA256:
626 			/* sign digest message */
627 			status = RSA_sign(NID_sha256, (unsigned char*)digest, SHA256_DIGEST_LENGTH, sigret,
628 					&siglen, rsa);
629 			break;
630 		case LASSO_SIGNATURE_METHOD_RSA_SHA384:
631 			/* sign digest message */
632 			status = RSA_sign(NID_sha384, (unsigned char*)digest, SHA384_DIGEST_LENGTH, sigret,
633 					&siglen, rsa);
634 			break;
635 		case LASSO_SIGNATURE_METHOD_RSA_SHA512:
636 			/* sign digest message */
637 			status = RSA_sign(NID_sha512, (unsigned char*)digest, SHA512_DIGEST_LENGTH, sigret,
638 					&siglen, rsa);
639 			break;
640 		case LASSO_SIGNATURE_METHOD_DSA_SHA1:
641 			status = DSA_sign(NID_sha1, (unsigned char*)digest, SHA_DIGEST_LENGTH, sigret,
642 					&siglen, dsa);
643 			break;
644 		case LASSO_SIGNATURE_METHOD_HMAC_SHA1:
645 		case LASSO_SIGNATURE_METHOD_HMAC_SHA256:
646 		case LASSO_SIGNATURE_METHOD_HMAC_SHA384:
647 		case LASSO_SIGNATURE_METHOD_HMAC_SHA512:
648 			HMAC(md, hmac_key, hmac_key_length, (unsigned char *)new_query,
649 					strlen(new_query), sigret, &siglen);
650 			status = 1;
651 			break;
652 		case LASSO_SIGNATURE_METHOD_LAST:
653 		case LASSO_SIGNATURE_METHOD_NONE:
654 			g_assert_not_reached();
655 	}
656 
657 	g_assert(siglen == sigret_size);
658 
659 	if (status == 0) {
660 		goto done;
661 	}
662 
663 	/* Base64 encode the signature value */
664 	b64_sigret = xmlSecBase64Encode(sigret, sigret_size, 0);
665 	/* escape b64_sigret */
666 	e_b64_sigret = lasso_xmlURIEscapeStr((xmlChar*)b64_sigret, NULL);
667 
668 	/* add signature */
669 	switch (sign_method) {
670 		case LASSO_SIGNATURE_METHOD_RSA_SHA1:
671 		case LASSO_SIGNATURE_METHOD_DSA_SHA1:
672 		case LASSO_SIGNATURE_METHOD_HMAC_SHA1:
673 		case LASSO_SIGNATURE_METHOD_RSA_SHA256:
674 		case LASSO_SIGNATURE_METHOD_HMAC_SHA256:
675 		case LASSO_SIGNATURE_METHOD_RSA_SHA384:
676 		case LASSO_SIGNATURE_METHOD_HMAC_SHA384:
677 		case LASSO_SIGNATURE_METHOD_RSA_SHA512:
678 		case LASSO_SIGNATURE_METHOD_HMAC_SHA512:
679 			s_new_query = g_strdup_printf("%s&Signature=%s", new_query, (char*)
680 					e_b64_sigret);
681 			break;
682 		case LASSO_SIGNATURE_METHOD_NONE:
683 		case LASSO_SIGNATURE_METHOD_LAST:
684 			g_assert_not_reached();
685 	}
686 
687 done:
688 	lasso_release(new_query);
689 	lasso_release_string(digest);
690 	lasso_release(sigret);
691 	lasso_release_xml_string(b64_sigret);
692 	lasso_release_xml_string(e_b64_sigret);
693 
694 	return s_new_query;
695 }
696 
697 LassoNode*
lasso_assertion_encrypt(LassoSaml2Assertion * assertion,char * recipient)698 lasso_assertion_encrypt(LassoSaml2Assertion *assertion, char *recipient)
699 {
700 	xmlSecKey *encryption_public_key = NULL;
701 	LassoEncryptionSymKeyType encryption_sym_key_type = 0;
702 	LassoNode *ret = NULL;
703 
704 	lasso_node_get_encryption((LassoNode*)assertion, &encryption_public_key,
705 			&encryption_sym_key_type);
706 	if (! encryption_public_key) {
707 		return NULL;
708 	}
709 
710 	ret = LASSO_NODE(lasso_node_encrypt(LASSO_NODE(assertion),
711 		encryption_public_key, encryption_sym_key_type, recipient));
712 	lasso_release_sec_key(encryption_public_key);
713 	return ret;
714 
715 }
716 
717 static lasso_error_t
lasso_query_verify_helper(const char * signed_content,const char * b64_signature,const char * algorithm,const xmlSecKey * key)718 lasso_query_verify_helper(const char *signed_content, const char *b64_signature, const char *algorithm,
719 		const xmlSecKey *key)
720 {
721 	RSA *rsa = NULL;
722 	DSA *dsa = NULL;
723 	char *digest = NULL;
724 	xmlSecByte *signature = NULL;
725 	int key_size = 0;
726 	unsigned char *hmac_key = NULL;
727 	size_t hmac_key_length = 0;
728 	const EVP_MD *md = NULL;
729 	lasso_error_t rc = 0;
730 	LassoSignatureMethod method = LASSO_SIGNATURE_METHOD_NONE;
731 	size_t digest_size = 1;
732 	int type = -1;
733 
734 	if (lasso_strisequal(algorithm, (char*)xmlSecHrefRsaSha1)) {
735 		goto_cleanup_if_fail_with_rc(key->value->id == xmlSecOpenSSLKeyDataRsaId,
736 				LASSO_DS_ERROR_INVALID_SIGALG)
737 		rsa = xmlSecOpenSSLKeyDataRsaGetRsa(key->value);
738 		key_size = RSA_size(rsa);
739 		method = LASSO_SIGNATURE_METHOD_RSA_SHA1;
740 		digest_size = SHA_DIGEST_LENGTH;
741 		type = NID_sha1;
742 	} else if (lasso_strisequal(algorithm, (char*)xmlSecHrefDsaSha1)) {
743 		goto_cleanup_if_fail_with_rc(key->value->id == xmlSecOpenSSLKeyDataDsaId, LASSO_DS_ERROR_INVALID_SIGALG);
744 		dsa = xmlSecOpenSSLKeyDataDsaGetDsa(key->value);
745 		key_size = DSA_size(dsa);
746 		method = LASSO_SIGNATURE_METHOD_DSA_SHA1;
747 		digest_size = SHA_DIGEST_LENGTH;
748 		type = NID_sha1;
749 	} else if (lasso_strisequal(algorithm, (char*)xmlSecHrefRsaSha256)) {
750 		goto_cleanup_if_fail_with_rc(key->value->id == xmlSecOpenSSLKeyDataRsaId,
751 				LASSO_DS_ERROR_INVALID_SIGALG)
752 		rsa = xmlSecOpenSSLKeyDataRsaGetRsa(key->value);
753 		key_size = RSA_size(rsa);
754 		method = LASSO_SIGNATURE_METHOD_RSA_SHA256;
755 		digest_size = SHA256_DIGEST_LENGTH;
756 		type = NID_sha256;
757 	} else if (lasso_strisequal(algorithm, (char*)xmlSecHrefRsaSha384)) {
758 		goto_cleanup_if_fail_with_rc(key->value->id == xmlSecOpenSSLKeyDataRsaId,
759 				LASSO_DS_ERROR_INVALID_SIGALG)
760 		rsa = xmlSecOpenSSLKeyDataRsaGetRsa(key->value);
761 		key_size = RSA_size(rsa);
762 		method = LASSO_SIGNATURE_METHOD_RSA_SHA384;
763 		digest_size = SHA384_DIGEST_LENGTH;
764 		type = NID_sha384;
765 	} else if (lasso_strisequal(algorithm, (char*)xmlSecHrefRsaSha512)) {
766 		goto_cleanup_if_fail_with_rc(key->value->id == xmlSecOpenSSLKeyDataRsaId,
767 				LASSO_DS_ERROR_INVALID_SIGALG)
768 		rsa = xmlSecOpenSSLKeyDataRsaGetRsa(key->value);
769 		key_size = RSA_size(rsa);
770 		method = LASSO_SIGNATURE_METHOD_RSA_SHA512;
771 		digest_size = SHA512_DIGEST_LENGTH;
772 		type = NID_sha512;
773 	} else if (lasso_strisequal(algorithm, (char*)xmlSecHrefHmacSha1)) {
774 		lasso_check_good_rc(lasso_get_hmac_key(key, (void**)&hmac_key, &hmac_key_length));
775 		md = EVP_sha1();
776 		key_size = EVP_MD_size(md);
777 		method = LASSO_SIGNATURE_METHOD_HMAC_SHA1;
778 	} else if (lasso_strisequal(algorithm, (char*)xmlSecHrefHmacSha256)) {
779 		lasso_check_good_rc(lasso_get_hmac_key(key, (void**)&hmac_key, &hmac_key_length));
780 		md = EVP_sha256();
781 		key_size = EVP_MD_size(md);
782 		method = LASSO_SIGNATURE_METHOD_HMAC_SHA256;
783 	} else if (lasso_strisequal(algorithm, (char*)xmlSecHrefHmacSha384)) {
784 		lasso_check_good_rc(lasso_get_hmac_key(key, (void**)&hmac_key, &hmac_key_length));
785 		md = EVP_sha384();
786 		key_size = EVP_MD_size(md);
787 		method = LASSO_SIGNATURE_METHOD_HMAC_SHA384;
788 	} else if (lasso_strisequal(algorithm, (char*)xmlSecHrefHmacSha512)) {
789 		lasso_check_good_rc(lasso_get_hmac_key(key, (void**)&hmac_key, &hmac_key_length));
790 		md = EVP_sha512();
791 		key_size = EVP_MD_size(md);
792 		method = LASSO_SIGNATURE_METHOD_HMAC_SHA512;
793 	} else {
794 		goto_cleanup_with_rc(LASSO_DS_ERROR_INVALID_SIGALG);
795 	}
796 	/* decode signature */
797 	signature = g_malloc(key_size+1);
798 	goto_cleanup_if_fail_with_rc(
799 			xmlSecBase64Decode((xmlChar*)b64_signature, signature, key_size+1) != 0,
800 			LASSO_DS_ERROR_INVALID_SIGNATURE);
801 	/* digest */
802 	switch (method) {
803 		case LASSO_SIGNATURE_METHOD_RSA_SHA1:
804 		case LASSO_SIGNATURE_METHOD_DSA_SHA1:
805 			digest = lasso_sha1(signed_content);
806 			break;
807 		case LASSO_SIGNATURE_METHOD_RSA_SHA256:
808 			digest = lasso_sha256(signed_content);
809 			break;
810 		case LASSO_SIGNATURE_METHOD_RSA_SHA384:
811 			digest = lasso_sha384(signed_content);
812 			break;
813 		case LASSO_SIGNATURE_METHOD_RSA_SHA512:
814 			digest = lasso_sha512(signed_content);
815 			break;
816 		case LASSO_SIGNATURE_METHOD_HMAC_SHA1:
817 		case LASSO_SIGNATURE_METHOD_HMAC_SHA256:
818 		case LASSO_SIGNATURE_METHOD_HMAC_SHA384:
819 		case LASSO_SIGNATURE_METHOD_HMAC_SHA512:
820 			break;
821 		default:
822 			g_assert_not_reached();
823 	}
824 	/* verify signature */
825 	switch (method) {
826 		case LASSO_SIGNATURE_METHOD_RSA_SHA1:
827 		case LASSO_SIGNATURE_METHOD_RSA_SHA256:
828 		case LASSO_SIGNATURE_METHOD_RSA_SHA384:
829 		case LASSO_SIGNATURE_METHOD_RSA_SHA512:
830 			goto_cleanup_if_fail_with_rc(
831 					RSA_verify(
832 						type,
833 						(unsigned char*)digest,
834 						digest_size,
835 						signature,
836 						key_size, rsa) == 1,
837 					LASSO_DS_ERROR_INVALID_SIGNATURE);
838 			break;
839 		case LASSO_SIGNATURE_METHOD_DSA_SHA1:
840 			goto_cleanup_if_fail_with_rc(
841 					DSA_verify(
842 						type,
843 						(unsigned char*)digest,
844 						digest_size,
845 						signature,
846 						key_size, dsa) == 1,
847 					LASSO_DS_ERROR_INVALID_SIGNATURE);
848 			break;
849 		case LASSO_SIGNATURE_METHOD_HMAC_SHA1:
850 		case LASSO_SIGNATURE_METHOD_HMAC_SHA256:
851 		case LASSO_SIGNATURE_METHOD_HMAC_SHA384:
852 		case LASSO_SIGNATURE_METHOD_HMAC_SHA512:
853 			digest = g_malloc(key_size);
854 			HMAC(md, hmac_key, hmac_key_length, (unsigned char*)signed_content,
855 				strlen(signed_content), (unsigned char*)digest, NULL);
856 
857 			goto_cleanup_if_fail_with_rc(lasso_crypto_memequal(digest, signature,
858 						key_size),
859 					LASSO_DS_ERROR_INVALID_SIGNATURE);
860 			break;
861 		case LASSO_SIGNATURE_METHOD_NONE:
862 		case LASSO_SIGNATURE_METHOD_LAST:
863 			g_assert_not_reached();
864 	}
865 cleanup:
866 	lasso_release_string(digest);
867 	lasso_release_string(signature);
868 	return rc;
869 
870 }
871 
872 /**
873  * lasso_query_verify_signature:
874  * @query: a query (an url-encoded message)
875  * @sender_public_key: the query sender public key
876  *
877  * Verifies the query signature.
878  *
879  * Return value: 0 if signature is valid
880  * a positive value if signature was not found or is invalid
881  * a negative value if an error occurs during verification
882  **/
883 lasso_error_t
lasso_query_verify_signature(const char * query,const xmlSecKey * sender_public_key)884 lasso_query_verify_signature(const char *query, const xmlSecKey *sender_public_key)
885 {
886 	gchar **str_split = NULL;
887 	char *b64_signature = NULL;
888 	char *sig_alg = NULL;
889 	char *usig_alg = NULL;
890 	lasso_error_t rc = 0;
891 
892 	g_return_val_if_fail(query != NULL, LASSO_PARAM_ERROR_INVALID_VALUE);
893 
894 	if (lasso_flag_verify_signature == FALSE) {
895 		return 0;
896 	}
897 
898 	g_return_val_if_fail(sender_public_key != NULL, LASSO_PARAM_ERROR_INVALID_VALUE);
899 	g_return_val_if_fail(sender_public_key->value != NULL, LASSO_PARAM_ERROR_INVALID_VALUE);
900 
901 	/* split query, the signature MUST be the last param of the query
902 	 * actually there could be more params in the URL; but they wouldn't be
903 	 * covered by the signature */
904 
905 	str_split = g_strsplit(query, "&Signature=", 0);
906 	if (str_split[0] == NULL || str_split[1] == NULL)
907 		goto_cleanup_with_rc(LASSO_DS_ERROR_SIGNATURE_NOT_FOUND);
908 	sig_alg = strstr(str_split[0], "&SigAlg=");
909 	if (sig_alg == NULL)
910 		goto_cleanup_with_rc(LASSO_DS_ERROR_INVALID_SIGALG);
911 	sig_alg = strchr(sig_alg, '=')+1;
912 	usig_alg = xmlURIUnescapeString(sig_alg, 0, NULL);
913 	/* insure there is only the signature in str_split[1] */
914 	if (strchr(str_split[1], '&')) {
915 		strchr(str_split[1], '&')[0] = 0;
916 	}
917 
918 	/* get signature (unescape + base64 decode) */
919 	b64_signature = (char*)xmlURIUnescapeString(str_split[1], 0, NULL);
920 	lasso_check_good_rc(lasso_query_verify_helper(str_split[0],
921 				b64_signature, usig_alg, sender_public_key));
922 
923 
924 cleanup:
925 	if (b64_signature)
926 		xmlFree(b64_signature);
927 	if (usig_alg)
928 		xmlFree(usig_alg);
929 	g_strfreev(str_split);
930 	return rc;
931 }
932 
933 /**
934  * lasso_saml2_query_verify_signature:
935  * @query: a query string
936  * @sender_public_key: the #xmlSecKey for the sender
937  *
938  * Verify a query signature following SAML 2.0 semantic.
939  *
940  * Return value: 0 if signature is validated, an error code otherwise.
941  */
942 int
lasso_saml2_query_verify_signature(const char * query,const xmlSecKey * sender_public_key)943 lasso_saml2_query_verify_signature(const char *query, const xmlSecKey *sender_public_key)
944 {
945 	char *b64_signature = NULL;
946 	char *query_copy = NULL;
947 	char *signed_query = NULL;
948 	char *i = NULL;
949 	char **components = NULL, **j = NULL;
950 	int n = 0;
951 	char *saml_request_response = NULL;
952 	char *relaystate = NULL;
953 	char *sig_alg = NULL, *usig_alg = NULL;
954 	lasso_error_t rc = 0;
955 
956 	lasso_return_val_if_fail(query != NULL, LASSO_PARAM_ERROR_INVALID_VALUE);
957 	lasso_return_val_if_fail(lasso_flag_verify_signature, 0);
958 	lasso_return_val_if_fail(sender_public_key != NULL, LASSO_PARAM_ERROR_INVALID_VALUE);
959 	lasso_return_val_if_fail(sender_public_key->value != NULL, LASSO_PARAM_ERROR_INVALID_VALUE);
960 
961 	/* extract fields */
962 	i = query_copy = g_strdup(query);
963 	n = 1;
964 	while (*i) {
965 		if (*i == '&' || *i == ';')
966 			n++;
967 		i++;
968 	}
969 	components = g_new0(char*, n+1);
970 	components[n] = NULL;
971 	n = 0;
972 	i = query_copy;
973 	components[n] = query_copy;
974 	n += 1;
975 	while (*i) {
976 		if (*i == '&' || *i == ';') {
977 			*i = '\0';
978 			components[n] = i + 1;
979 			n++;
980 		}
981 		i++;
982 	}
983 	/* extract specific fields */
984 	j = components;
985 #define match_field(x) \
986 	(strncmp(x "=", *j, sizeof(x)) == 0)
987 #define value strchr(*j, '=') + 1
988 	while (*j) {
989 		if (match_field(LASSO_SAML2_FIELD_RESPONSE)
990 				|| match_field(LASSO_SAML2_FIELD_REQUEST)) {
991 			saml_request_response = *j;
992 		} else if (match_field(LASSO_SAML2_FIELD_RELAYSTATE)) {
993 			relaystate = *j;
994 		} else if (match_field(LASSO_SAML2_FIELD_SIGALG)) {
995 			sig_alg = *j;
996 		} else if (match_field(LASSO_SAML2_FIELD_SIGNATURE)) {
997 			b64_signature = value;
998 			b64_signature = xmlURIUnescapeString(b64_signature, 0, NULL);
999 		}
1000 		++j;
1001 	}
1002 #undef match_field
1003 #undef value
1004 
1005 	if (! saml_request_response) {
1006 		message(G_LOG_LEVEL_CRITICAL, "SAMLRequest or SAMLResponse missing in query");
1007 		goto_cleanup_with_rc(LASSO_PROFILE_ERROR_INVALID_QUERY);
1008 	}
1009 
1010 	if (! sig_alg) {
1011 		goto_cleanup_with_rc(LASSO_DS_ERROR_INVALID_SIGALG);
1012 	}
1013 	if (! b64_signature) {
1014 		goto_cleanup_with_rc(LASSO_DS_ERROR_SIGNATURE_NOT_FOUND);
1015 	}
1016 	/* build the signed query */
1017 	if (relaystate) {
1018 		signed_query = g_strconcat(saml_request_response, "&", relaystate, "&", sig_alg, NULL);
1019 	} else {
1020 		signed_query = g_strconcat(saml_request_response, "&", sig_alg, NULL);
1021 	}
1022 
1023 	sig_alg = strchr(sig_alg, '=')+1;
1024 	if (! sig_alg) {
1025 		goto_cleanup_with_rc(LASSO_DS_ERROR_INVALID_SIGALG);
1026 	}
1027 	usig_alg = xmlURIUnescapeString(sig_alg, 0, NULL);
1028 	lasso_check_good_rc(lasso_query_verify_helper(signed_query, b64_signature, usig_alg,
1029 				sender_public_key));
1030 
1031 
1032 cleanup:
1033 	if (b64_signature)
1034 		xmlFree(b64_signature);
1035 	if (usig_alg)
1036 		xmlFree(usig_alg);
1037 	lasso_release(components);
1038 	lasso_release(query_copy);
1039 	lasso_release(signed_query);
1040 
1041 	return rc;
1042 }
1043 
1044 /**
1045  * lasso_sha1:
1046  * @str: a string
1047  *
1048  * Builds the SHA-1 message digest (cryptographic hash) of @str
1049  *
1050  * Return value: 20-bytes buffer allocated with g_malloc
1051  **/
1052 char*
lasso_sha1(const char * str)1053 lasso_sha1(const char *str)
1054 {
1055 	xmlChar *md;
1056 
1057 	if (str == NULL)
1058 		return NULL;
1059 
1060 	md = g_malloc(20);
1061 	return (char*)SHA1((unsigned char*)str, strlen(str), md);
1062 }
1063 
1064 /**
1065  * lasso_sha256:
1066  * @str: a string
1067  *
1068  * Builds the SHA-256 message digest (cryptographic hash) of @str
1069  *
1070  * Return value: 32-bytes buffer allocated with g_malloc
1071  **/
1072 char*
lasso_sha256(const char * str)1073 lasso_sha256(const char *str)
1074 {
1075 	xmlChar *md;
1076 
1077 	if (str == NULL)
1078 		return NULL;
1079 
1080 	md = g_malloc(32);
1081 	return (char*)SHA256((unsigned char*)str, strlen(str), md);
1082 }
1083 
1084 /**
1085  * lasso_sha384:
1086  * @str: a string
1087  *
1088  * Builds the SHA-384 message digest (cryptographic hash) of @str
1089  *
1090  * Return value: 48-bytes buffer allocated with g_malloc
1091  **/
1092 char*
lasso_sha384(const char * str)1093 lasso_sha384(const char *str)
1094 {
1095 	xmlChar *md;
1096 
1097 	if (str == NULL)
1098 		return NULL;
1099 
1100 	md = g_malloc(48);
1101 	return (char*)SHA384((unsigned char*)str, strlen(str), md);
1102 }
1103 
1104 /**
1105  * lasso_sha512:
1106  * @str: a string
1107  *
1108  * Builds the SHA-512 message digest (cryptographic hash) of @str
1109  *
1110  * Return value: 64-bytes buffer allocated with g_malloc
1111  **/
1112 char*
lasso_sha512(const char * str)1113 lasso_sha512(const char *str)
1114 {
1115 	xmlChar *md;
1116 
1117 	if (str == NULL)
1118 		return NULL;
1119 
1120 	md = g_malloc(64);
1121 	return (char*)SHA512((unsigned char*)str, strlen(str), md);
1122 }
1123 
1124 
1125 /**
1126  * lasso_urlencoded_to_strings:
1127  * @str: a query string
1128  *
1129  * Parse a query string and separate it into an char* array of its components.
1130  *
1131  * The returned array must be deallocated.
1132  */
1133 xmlChar**
lasso_urlencoded_to_strings(const char * str)1134 lasso_urlencoded_to_strings(const char *str)
1135 {
1136 	int i, n=1;
1137 	char *st, *st2;
1138 	xmlChar **result;
1139 
1140 	g_assert(str);
1141 	/* count components */
1142 	st = (char*)str;
1143 	while (*st) {
1144 		if (*st == '&' || *st == ';')
1145 			n++;
1146 		st++;
1147 	}
1148 
1149 	/* allocate result array */
1150 	result = g_new0(xmlChar*, n+1);
1151 	result[n] = NULL;
1152 
1153 	/* tokenize */
1154 	st = st2 = (char*)str;
1155 	i = 0;
1156 	while(1) {
1157 		if (*st == '&' || *st == ';' || *st == '\0') {
1158 			ptrdiff_t len = st - st2;
1159 
1160 			g_assert(i < n+1);
1161 			if (len) {
1162 				result[i] = (xmlChar*)xmlURIUnescapeString(st2, len, NULL);
1163 			} else {
1164 				result[i] = g_malloc0(1);
1165 			}
1166 			i++;
1167 			if (*st == '\0')
1168 				break;
1169 			st2 = st + 1;
1170 		}
1171 		st++;
1172 	}
1173 
1174 	return result;
1175 }
1176 
_lasso_xmlsec_password_callback()1177 void _lasso_xmlsec_password_callback() {
1178 }
1179 
1180 /**
1181  * lasso_sign_node:
1182  * @xmlnode: the xmlnode to sign
1183  * @id_attr_name: (allow-none): an ID attribute to reference the xmlnode in the signature
1184  * @id_value: (allow-none): value of the ID attribute
1185  * @private_key_file: the path to a key file, or the key itself PEM encoded.
1186  * @certificate_file: (allow-none): the path to a certificate file to place in the KeyInfo, or the certificate
1187  * itself PEM encoded.
1188  *
1189  * Sign an xmlnode, use the given attribute to reference or create an envelopped signature,
1190  * eventually place a certificate in the KeyInfo node. The signature template must already be
1191  * present on the xmlnode.
1192  *
1193  * Return value: 0 if successful, an error code otherwise.
1194  */
1195 int
lasso_sign_node(xmlNode * xmlnode,LassoSignatureContext context,const char * id_attr_name,const char * id_value)1196 lasso_sign_node(xmlNode *xmlnode, LassoSignatureContext context, const char *id_attr_name,
1197 		const char *id_value)
1198 {
1199 	xmlDoc *doc = NULL;
1200 	xmlNode *sign_tmpl = NULL, *old_parent = NULL;
1201 	xmlSecDSigCtx *dsig_ctx = NULL;
1202 	xmlAttr *id_attr = NULL;
1203 	lasso_error_t rc = 0;
1204 
1205 	g_return_val_if_fail(context.signature_method, LASSO_DS_ERROR_INVALID_SIGALG);
1206 	g_return_val_if_fail(context.signature_key, LASSO_DS_ERROR_PRIVATE_KEY_LOAD_FAILED);
1207 
1208 	sign_tmpl = xmlSecFindNode(xmlnode, xmlSecNodeSignature, xmlSecDSigNs);
1209 	goto_cleanup_if_fail_with_rc(sign_tmpl != NULL,
1210 			LASSO_DS_ERROR_SIGNATURE_TEMPLATE_NOT_FOUND);
1211 
1212 	doc = xmlNewDoc((xmlChar*)"1.0");
1213 	old_parent = xmlnode->parent;
1214 	xmlnode->parent = NULL;
1215 	xmlDocSetRootElement(doc, xmlnode);
1216 	xmlSetTreeDoc(sign_tmpl, doc);
1217 	if (id_attr_name && id_value) {
1218 		id_attr = xmlHasProp(xmlnode, (xmlChar*)id_attr_name);
1219 		xmlAddID(NULL, doc, (xmlChar*)id_value, id_attr);
1220 	}
1221 
1222 	dsig_ctx = xmlSecDSigCtxCreate(NULL);
1223 	lasso_assign_sec_key(dsig_ctx->signKey, context.signature_key);
1224 	if (xmlSecDSigCtxSign(dsig_ctx, sign_tmpl) < 0) {
1225 		goto_cleanup_with_rc(LASSO_DS_ERROR_SIGNATURE_FAILED);
1226 	}
1227 
1228 cleanup:
1229 	if (doc) {
1230 		xmlRemoveID(doc, id_attr);
1231 		xmlUnlinkNode(xmlnode);
1232 		lasso_release_doc(doc);
1233 		xmlnode->parent = old_parent;
1234 		xmlSetTreeDoc(xmlnode, NULL);
1235 	}
1236 	lasso_release_signature_context(dsig_ctx);
1237 	return rc;
1238 }
1239 
1240 gchar*
lasso_node_build_deflated_query(LassoNode * node)1241 lasso_node_build_deflated_query(LassoNode *node)
1242 {
1243 	/* actually deflated and b64'ed and url-escaped */
1244 	xmlNode *xmlnode;
1245 	gchar *result;
1246 
1247 	xmlnode = lasso_node_get_xmlNode(node, FALSE);
1248 	result = lasso_xmlnode_build_deflated_query(xmlnode);
1249 	xmlFreeNode(xmlnode);
1250 	return result;
1251 }
1252 
1253 gchar*
lasso_xmlnode_build_deflated_query(xmlNode * xmlnode)1254 lasso_xmlnode_build_deflated_query(xmlNode *xmlnode)
1255 {
1256 	xmlOutputBuffer *output_buffer;
1257 	xmlBuffer *buffer;
1258 	xmlCharEncodingHandlerPtr handler = NULL;
1259 	xmlChar *ret, *b64_ret;
1260 	char *rret;
1261 	unsigned long in_len;
1262 	int rc = 0;
1263 	z_stream stream;
1264 
1265 	handler = xmlFindCharEncodingHandler("utf-8");
1266 	buffer = xmlBufferCreate();
1267 	output_buffer = xmlOutputBufferCreateBuffer(buffer, handler);
1268 	xmlNodeDumpOutput(output_buffer, NULL, xmlnode, 0, 0, NULL);
1269 	xmlOutputBufferClose(output_buffer);
1270 	xmlBufferAdd(buffer, BAD_CAST "", 1);
1271 
1272 	in_len = strlen((char*)xmlBufferContent(buffer));
1273 	ret = g_malloc(in_len * 2);
1274 		/* deflating should never increase the required size but we are
1275 		 * more conservative than that.  Twice the size should be
1276 		 * enough. */
1277 
1278 	stream.next_in = (xmlChar*)xmlBufferContent(buffer);
1279 	stream.avail_in = in_len;
1280 	stream.next_out = ret;
1281 	stream.avail_out = in_len * 2;
1282 
1283 	stream.zalloc = NULL;
1284 	stream.zfree = NULL;
1285 	stream.opaque = NULL;
1286 
1287 	/* -MAX_WBITS to disable zib headers */
1288 	rc = deflateInit2(&stream, Z_DEFAULT_COMPRESSION,
1289 		Z_DEFLATED, -MAX_WBITS, 5, 0);
1290 	if (rc == Z_OK) {
1291 		rc = deflate(&stream, Z_FINISH);
1292 		if (rc != Z_STREAM_END) {
1293 			deflateEnd(&stream);
1294 			if (rc == Z_OK) {
1295 				rc = Z_BUF_ERROR;
1296 			}
1297 		} else {
1298 			rc = deflateEnd(&stream);
1299 		}
1300 	}
1301 	xmlBufferFree(buffer);
1302 	if (rc != Z_OK) {
1303 		lasso_release(ret);
1304 		message(G_LOG_LEVEL_CRITICAL, "Failed to deflate");
1305 		return NULL;
1306 	}
1307 
1308 	b64_ret = xmlSecBase64Encode(ret, stream.total_out, 0);
1309 	lasso_release(ret);
1310 
1311 	ret = lasso_xmlURIEscapeStr(b64_ret, NULL);
1312 	rret = g_strdup((char*)ret);
1313 	xmlFree(b64_ret);
1314 	xmlFree(ret);
1315 
1316 	return rret;
1317 }
1318 
1319 void
lasso_get_query_string_param_value(const char * qs,const char * param_key,const char ** value,size_t * length)1320 lasso_get_query_string_param_value(const char *qs, const char *param_key, const char **value,
1321 		size_t *length)
1322 {
1323 	size_t key_size = strlen(param_key);
1324 
1325 	*value = NULL;
1326 	*length = 0;
1327 	while (qs) {
1328 		if (strncmp(qs, param_key, key_size) == 0 &&
1329 				qs[key_size] == '=')
1330 		{
1331 			char *end;
1332 			*value = &qs[key_size+1];
1333 			end = strchr(*value, '&');
1334 			if (! end) {
1335 				end = strchr(*value, ';');
1336 			}
1337 			if (end) {
1338 				*length = (ptrdiff_t)(end - *value);
1339 			} else {
1340 				*length = strlen(*value);
1341 			}
1342 			return;
1343 		}
1344 		qs = strchr(qs, '&');
1345 	}
1346 }
1347 
1348 unsigned char*
lasso_inflate(unsigned char * input,size_t len)1349 lasso_inflate(unsigned char *input, size_t len)
1350 {
1351 	z_stream zstr;
1352 	unsigned char *output;
1353 	int z_err;
1354 
1355 	zstr.zalloc = NULL;
1356 	zstr.zfree = NULL;
1357 	zstr.opaque = NULL;
1358 
1359 	output = g_malloc(len*20);
1360 	zstr.avail_in = len;
1361 	zstr.next_in = (unsigned char*)input;
1362 	zstr.total_in = 0;
1363 	zstr.avail_out = len*20;
1364 	zstr.total_out = 0;
1365 	zstr.next_out = output;
1366 
1367 	z_err = inflateInit2(&zstr, -MAX_WBITS);
1368 	if (z_err != Z_OK) {
1369 		message(G_LOG_LEVEL_CRITICAL, "Failed to inflateInit");
1370 		lasso_release_string(output);
1371 		return FALSE;
1372 	}
1373 
1374 	z_err = inflate(&zstr, Z_FINISH);
1375 	if (z_err != Z_STREAM_END) {
1376 		message(G_LOG_LEVEL_CRITICAL, "Failed to inflate");
1377 		inflateEnd(&zstr);
1378 		lasso_release_string(output);
1379 		return NULL;
1380 	}
1381 	output[zstr.total_out] = 0;
1382 	inflateEnd(&zstr);
1383 
1384 	return output;
1385 }
1386 
1387 
1388 gboolean
lasso_node_init_from_deflated_query_part(LassoNode * node,char * deflate_string)1389 lasso_node_init_from_deflated_query_part(LassoNode *node, char *deflate_string)
1390 {
1391 	int len;
1392 	xmlChar *b64_zre, *zre, *re;
1393 	xmlDoc *doc;
1394 	xmlNode *root;
1395 
1396 	b64_zre = (xmlChar*)xmlURIUnescapeString(deflate_string, 0, NULL);
1397 	len = strlen((char*)b64_zre);
1398 	zre = xmlMalloc(len*4);
1399 	len = xmlSecBase64Decode(b64_zre, zre, len*4);
1400 	xmlFree(b64_zre);
1401 	if (len == -1) {
1402 		message(G_LOG_LEVEL_CRITICAL, "Failed to base64-decode query");
1403 		xmlFree(zre);
1404 		return FALSE;
1405 	}
1406 
1407 	re = lasso_inflate(zre, len);
1408 	xmlFree(zre);
1409 
1410 	if (! re)
1411 		return FALSE;
1412 
1413 	doc = lasso_xml_parse_memory((char*)re, strlen((char*)re));
1414 	lasso_release_string(re);
1415 
1416 	root = xmlDocGetRootElement(doc);
1417 	lasso_node_init_from_xml(node, root);
1418 	lasso_release_doc(doc);
1419 
1420 	return TRUE;
1421 }
1422 
1423 char*
lasso_concat_url_query(const char * url,const char * query)1424 lasso_concat_url_query(const char *url, const char *query)
1425 {
1426 	if (strchr(url, '?')) {
1427 		return g_strdup_printf("%s&%s", url, query);
1428 	} else {
1429 		return g_strdup_printf("%s?%s", url, query);
1430 	}
1431 }
1432 
structuredErrorFunc(void * userData,xmlErrorPtr error)1433 static void structuredErrorFunc (void *userData, xmlErrorPtr error) {
1434 		*(int*)userData = error->code;
1435 }
1436 
1437 /**
1438  * lasso_eval_xpath_expression:
1439  * @xpath_ctx: the XPath context object
1440  * @expression: a string containg the XPath expression to evaluate
1441  * @xpath_object_ptr: pointer to an output variable to store the resulting XPath object, can be
1442  * NULL.
1443  * @xpath_error_code: pointer to an output variable to store an eventual XPath error code, can be
1444  * NULL.
1445  *
1446  * Evaluates a given XPath expression in the given XPath context. Eventually return an XPath object
1447  * and/or an error code.
1448  *
1449  * Return value: TRUE if no error occurred during evaluation, FALSE otherwise.
1450  */
1451 gboolean
lasso_eval_xpath_expression(xmlXPathContextPtr xpath_ctx,const char * expression,xmlXPathObjectPtr * xpath_object_ptr,int * xpath_error_code)1452 lasso_eval_xpath_expression(xmlXPathContextPtr xpath_ctx, const char *expression,
1453 		xmlXPathObjectPtr *xpath_object_ptr, int *xpath_error_code)
1454 {
1455 	xmlXPathObject *xpath_object = NULL;
1456 	int errorCode = 0;
1457 	xmlStructuredErrorFunc oldStructuredErrorFunc;
1458 	gboolean rc = TRUE;
1459 
1460 	g_return_val_if_fail(xpath_ctx != NULL && expression != NULL, FALSE);
1461 
1462 	if (xpath_error_code) { /* reset */
1463 		*xpath_error_code = 0;
1464 	}
1465 	oldStructuredErrorFunc = xpath_ctx->error;
1466 	xpath_ctx->error = structuredErrorFunc;
1467 	xpath_ctx->userData = &errorCode;
1468 	xpath_object = xmlXPathEvalExpression((xmlChar*)expression, xpath_ctx);
1469 	xpath_ctx->error = oldStructuredErrorFunc;
1470 	xpath_ctx->userData = NULL;
1471 
1472 	if (xpath_object) {
1473 		if (xpath_object_ptr) {
1474 			lasso_transfer_xpath_object(*xpath_object_ptr, xpath_object);
1475 		}
1476 	} else {
1477 		rc = FALSE;
1478 	}
1479 
1480 	if (xpath_error_code && errorCode) {
1481 		*xpath_error_code = errorCode;
1482 	}
1483 	lasso_release_xpath_object(xpath_object);
1484 
1485 	return rc;
1486 }
1487 
1488 static gboolean
lasso_saml_constrain_dsigctxt(xmlSecDSigCtxPtr dsigCtx)1489 lasso_saml_constrain_dsigctxt(xmlSecDSigCtxPtr dsigCtx) {
1490 	/* Limit allowed transforms for signature and reference processing */
1491 	if((xmlSecDSigCtxEnableSignatureTransform(dsigCtx, xmlSecTransformInclC14NId) < 0) ||
1492 			(xmlSecDSigCtxEnableSignatureTransform(dsigCtx, xmlSecTransformExclC14NId) < 0) ||
1493 			(xmlSecDSigCtxEnableSignatureTransform(dsigCtx, xmlSecTransformInclC14NWithCommentsId) < 0) ||
1494 			(xmlSecDSigCtxEnableSignatureTransform(dsigCtx, xmlSecTransformExclC14NWithCommentsId) < 0) ||
1495 			(xmlSecDSigCtxEnableSignatureTransform(dsigCtx, xmlSecTransformInclC14N11Id) < 0) ||
1496 			(xmlSecDSigCtxEnableSignatureTransform(dsigCtx, xmlSecTransformInclC14N11WithCommentsId) < 0) ||
1497 			(xmlSecDSigCtxEnableSignatureTransform(dsigCtx, xmlSecTransformSha1Id) < 0) ||
1498 			(xmlSecDSigCtxEnableSignatureTransform(dsigCtx, xmlSecTransformHmacSha1Id) < 0) ||
1499 			(xmlSecDSigCtxEnableSignatureTransform(dsigCtx, xmlSecTransformDsaSha1Id) < 0) ||
1500 			(xmlSecDSigCtxEnableSignatureTransform(dsigCtx, xmlSecTransformRsaSha1Id) < 0) ||
1501 			(xmlSecDSigCtxEnableSignatureTransform(dsigCtx, xmlSecTransformSha256Id) < 0) ||
1502 			(xmlSecDSigCtxEnableSignatureTransform(dsigCtx, xmlSecTransformHmacSha256Id) < 0) ||
1503 			(xmlSecDSigCtxEnableSignatureTransform(dsigCtx, xmlSecTransformRsaSha256Id) < 0) ||
1504 			(xmlSecDSigCtxEnableSignatureTransform(dsigCtx, xmlSecTransformSha384Id) < 0) ||
1505 			(xmlSecDSigCtxEnableSignatureTransform(dsigCtx, xmlSecTransformHmacSha384Id) < 0) ||
1506 			(xmlSecDSigCtxEnableSignatureTransform(dsigCtx, xmlSecTransformRsaSha384Id) < 0) ||
1507 			(xmlSecDSigCtxEnableSignatureTransform(dsigCtx, xmlSecTransformSha512Id) < 0) ||
1508 			(xmlSecDSigCtxEnableSignatureTransform(dsigCtx, xmlSecTransformHmacSha512Id) < 0) ||
1509 			(xmlSecDSigCtxEnableSignatureTransform(dsigCtx, xmlSecTransformRsaSha512Id) < 0)
1510 			) {
1511 
1512 		message(G_LOG_LEVEL_CRITICAL, "Error: failed to limit allowed signature transforms");
1513 		return FALSE;
1514 	}
1515 	if((xmlSecDSigCtxEnableReferenceTransform(dsigCtx, xmlSecTransformInclC14NId) < 0) ||
1516 			(xmlSecDSigCtxEnableReferenceTransform(dsigCtx, xmlSecTransformExclC14NId) < 0) ||
1517 			(xmlSecDSigCtxEnableSignatureTransform(dsigCtx, xmlSecTransformInclC14NWithCommentsId) < 0) ||
1518 			(xmlSecDSigCtxEnableSignatureTransform(dsigCtx, xmlSecTransformExclC14NWithCommentsId) < 0) ||
1519 			(xmlSecDSigCtxEnableSignatureTransform(dsigCtx, xmlSecTransformInclC14N11Id) < 0) ||
1520 			(xmlSecDSigCtxEnableSignatureTransform(dsigCtx, xmlSecTransformInclC14N11WithCommentsId) < 0) ||
1521 			(xmlSecDSigCtxEnableReferenceTransform(dsigCtx, xmlSecTransformSha1Id) < 0) ||
1522 			(xmlSecDSigCtxEnableReferenceTransform(dsigCtx, xmlSecTransformSha256Id) < 0) ||
1523 			(xmlSecDSigCtxEnableReferenceTransform(dsigCtx, xmlSecTransformSha384Id) < 0) ||
1524 			(xmlSecDSigCtxEnableReferenceTransform(dsigCtx, xmlSecTransformSha512Id) < 0) ||
1525 			(xmlSecDSigCtxEnableReferenceTransform(dsigCtx, xmlSecTransformEnvelopedId) < 0)) {
1526 
1527 		message(G_LOG_LEVEL_CRITICAL, "Error: failed to limit allowed reference transforms");
1528 		return FALSE;
1529 	}
1530 
1531 	/* Limit possible key info to X509, RSA and DSA */
1532 	if((xmlSecPtrListAdd(&(dsigCtx->keyInfoReadCtx.enabledKeyData), BAD_CAST xmlSecKeyDataX509Id) < 0) ||
1533 			(xmlSecPtrListAdd(&(dsigCtx->keyInfoReadCtx.enabledKeyData), BAD_CAST xmlSecKeyDataHmacId) < 0) ||
1534 			(xmlSecPtrListAdd(&(dsigCtx->keyInfoReadCtx.enabledKeyData), BAD_CAST xmlSecKeyDataRsaId) < 0) ||
1535 			(xmlSecPtrListAdd(&(dsigCtx->keyInfoReadCtx.enabledKeyData), BAD_CAST xmlSecKeyDataDsaId) < 0)) {
1536 		message(G_LOG_LEVEL_CRITICAL, "Error: failed to limit allowed key data");
1537 		return FALSE;
1538 	}
1539 	return TRUE;
1540 }
1541 
1542 /**
1543  * lasso_verify_signature:
1544  * @signed_node: an #xmlNode containing an enveloped xmlDSig signature
1545  * @doc: (allow-none): the eventual #xmlDoc from which the node is extracted, if none is given then it will be
1546  * created
1547  * @id_attr_name: the id attribune name for this node
1548  * @keys_manager: (allow-none): an #xmlSecKeysMnr containing the CA cert chain, to validate the key in the
1549  * signature if there is one.
1550  * @public_key: (allow-none): a public key to validate the signature, if present the function ignore the key
1551  * contained in the signature.
1552  * @signature_verification_option: flag to specify option about signature validation, see
1553  * #SignatureVerificationOption.
1554  * @uri_references: if the signature references multiple nodes, return them as a list of node IDs.
1555  *
1556  * This function validate a signature on an xmlNode following the instructions given in the document
1557  * Assertions and Protocol or the OASIS Security Markup Language (SAML) V1.1.
1558  *
1559  * The only kind of references that are accepted in thoses signatures are node ID references,
1560  * looking like &#35;xxx;.
1561  *
1562  * Beware that it does not validate every needed properties for a SAML assertion, request or
1563  * response to be acceptable.
1564  *
1565  * Return: 0 if signature was validated, and error code otherwise.
1566  */
1567 
1568 int
lasso_verify_signature(xmlNode * signed_node,xmlDoc * doc,const char * id_attr_name,xmlSecKeysMngr * keys_manager,xmlSecKey * public_key,SignatureVerificationOption signature_verification_option,GList ** uri_references)1569 lasso_verify_signature(xmlNode *signed_node, xmlDoc *doc, const char *id_attr_name,
1570 		xmlSecKeysMngr *keys_manager, xmlSecKey *public_key,
1571 		SignatureVerificationOption signature_verification_option,
1572 		GList **uri_references)
1573 {
1574 	int rc = LASSO_DS_ERROR_SIGNATURE_VERIFICATION_FAILED;
1575 	xmlNodePtr signature = NULL;
1576 	xmlSecDSigCtx *dsigCtx = NULL;
1577 	xmlChar *id = NULL;
1578 	char *reference_uri = NULL;
1579 	xmlSecDSigReferenceCtx *dsig_reference_ctx = NULL;
1580 	gboolean free_the_doc = FALSE;
1581 
1582 	g_return_val_if_fail(signed_node && (keys_manager || public_key),
1583 			LASSO_PARAM_ERROR_INVALID_VALUE);
1584 
1585 	if (lasso_flag_verify_signature == FALSE) {
1586 		return 0;
1587 	}
1588 	/* Find signature as direct child. */
1589 	signature = xmlSecFindChild(signed_node, xmlSecNodeSignature, xmlSecDSigNs);
1590 	goto_cleanup_if_fail_with_rc (signature, LASSO_DS_ERROR_SIGNATURE_NOT_FOUND);
1591 
1592 	/* Create a temporary doc, if needed */
1593 	if (doc == NULL) {
1594 		doc = xmlNewDoc((xmlChar*)XML_DEFAULT_VERSION);
1595 		goto_cleanup_if_fail_with_rc(doc, LASSO_ERROR_OUT_OF_MEMORY);
1596 		xmlDocSetRootElement(doc, signed_node);
1597 		free_the_doc = TRUE;
1598 	}
1599 
1600 	/* Find ID */
1601 	if (id_attr_name) {
1602 		id = xmlGetProp(signed_node, (xmlChar*)id_attr_name);
1603 		if (id && (xmlGetID(doc, id) == NULL)) {
1604 			xmlAddID(NULL, doc, id, xmlHasProp(signed_node, (xmlChar*)id_attr_name));
1605 		}
1606 	}
1607 
1608 	/* Create DSig context */
1609 	dsigCtx = xmlSecDSigCtxCreate(keys_manager);
1610 	goto_cleanup_if_fail_with_rc(doc, LASSO_DS_ERROR_CONTEXT_CREATION_FAILED);
1611 	/* XXX: Is xmlSecTransformUriTypeSameEmpty permitted ?
1612 	 * I would say yes only if signed_node == signature->parent. */
1613 	dsigCtx->enabledReferenceUris = 0;
1614 	dsigCtx->enabledReferenceUris |= xmlSecTransformUriTypeSameDocument;
1615 	if (signature_verification_option & EMPTY_URI) {
1616 		dsigCtx->enabledReferenceUris |= xmlSecTransformUriTypeEmpty;
1617 	}
1618 
1619 	goto_cleanup_if_fail_with_rc(lasso_saml_constrain_dsigctxt(dsigCtx),
1620 			LASSO_DS_ERROR_SIGNATURE_VERIFICATION_FAILED);
1621 	/* Given a public key use it to validate the signature ! */
1622 	if (public_key) {
1623 		dsigCtx->signKey = xmlSecKeyDuplicate(public_key);
1624 	}
1625 
1626 	/* Verify signature */
1627 	goto_cleanup_if_fail_with_rc(xmlSecDSigCtxVerify(dsigCtx, signature) >= 0,
1628 			LASSO_DS_ERROR_SIGNATURE_VERIFICATION_FAILED);
1629 	goto_cleanup_if_fail_with_rc(dsigCtx->status == xmlSecDSigStatusSucceeded,
1630 			LASSO_DS_ERROR_SIGNATURE_VERIFICATION_FAILED);
1631 
1632 	/* There should be only one reference */
1633 	goto_cleanup_if_fail_with_rc(((signature_verification_option & NO_SINGLE_REFERENCE) == 0) ||
1634 			xmlSecPtrListGetSize(&(dsigCtx->signedInfoReferences)) == 1, LASSO_DS_ERROR_TOO_MUCH_REFERENCES);
1635 	/* The reference should be to the signed node */
1636 	{
1637 		gboolean ok = FALSE;
1638 		reference_uri = g_strdup_printf("#%s", id);
1639 		dsig_reference_ctx = (xmlSecDSigReferenceCtx*)
1640 			xmlSecPtrListGetItem(&(dsigCtx->signedInfoReferences), 0);
1641 		ok |= dsig_reference_ctx != 0 &&
1642 			lasso_strisequal((char*)dsig_reference_ctx->uri, reference_uri);
1643 		ok |= (signature_verification_option & EMPTY_URI)
1644 			&& xmlDocGetRootElement(doc) == signed_node
1645 			&& dsig_reference_ctx != NULL
1646 			&& lasso_strisequal((char*)dsig_reference_ctx->uri, "");
1647 		goto_cleanup_if_fail_with_rc(ok,
1648 				LASSO_DS_ERROR_INVALID_REFERENCE_FOR_SAML);
1649 	}
1650 	/* Keep URI of all nodes signed if asked */
1651 	if (uri_references) {
1652 		gint size = xmlSecPtrListGetSize(&(dsigCtx->signedInfoReferences));
1653 		int i;
1654 		for (i = 0; i < size; ++i) {
1655 
1656 			dsig_reference_ctx = (xmlSecDSigReferenceCtx*)xmlSecPtrListGetItem(&(dsigCtx->signedInfoReferences), i);
1657 			if (dsig_reference_ctx == NULL ||
1658                             dsig_reference_ctx->uri == NULL) {
1659 				message(G_LOG_LEVEL_CRITICAL, "dsig_reference_ctx->uri cannot be null");
1660 				continue;
1661 			}
1662 			lasso_list_add_xml_string(*uri_references, dsig_reference_ctx->uri);
1663 		}
1664 	}
1665 
1666 	if (dsigCtx->status == xmlSecDSigStatusSucceeded) {
1667 		rc = 0;
1668 	}
1669 
1670 cleanup:
1671 	lasso_release_string(reference_uri);
1672 	lasso_release_signature_context(dsigCtx);
1673 	if (free_the_doc) {
1674 		xmlUnlinkNode(signed_node);
1675 		xmlSetTreeDoc(signed_node, NULL);
1676 		lasso_release_doc(doc);
1677 	}
1678 	lasso_release_string(id);
1679 	return rc;
1680 }
1681 
1682 /**
1683  * lasso_xml_next_element_node:
1684  * @node:                the pointer to an XML node.
1685  *
1686  * Seraches for the next element node.
1687  *
1688  * Returns: the pointer to next element node or NULL if it is not found.
1689  */
1690 xmlNodePtr
lasso_xml_next_element_node(xmlNodePtr node)1691 lasso_xml_next_element_node(xmlNodePtr node)
1692 {
1693 
1694     for (; node != NULL && node->type != XML_ELEMENT_NODE; node = node->next);
1695     return node;
1696 }
1697 
1698 /**
1699  * lasso_xml_get_node_ns_href:
1700  * @node: the pointer to node.
1701  *
1702  * Get's node's namespace href.
1703  *
1704  * Returns: node's namespace href.
1705  */
1706 const xmlChar*
lasso_xml_get_node_ns_href(const xmlNodePtr node)1707 lasso_xml_get_node_ns_href(const xmlNodePtr node)
1708 {
1709     xmlNsPtr ns;
1710 
1711     if (node == NULL) {
1712         return NULL;
1713     }
1714 
1715     /* do we have a namespace in the node? */
1716     if (node->ns != NULL) {
1717         return node->ns->href;
1718     }
1719 
1720     /* search for default namespace */
1721     ns = xmlSearchNs(node->doc, node, NULL);
1722     if (ns != NULL) {
1723         return ns->href;
1724     }
1725 
1726     return NULL;
1727 }
1728 
1729 /**
1730  * lasso_xml_is_element_node:
1731  * @node: the pointer to an XML node.
1732  * @name: the name,
1733  * @ns:   the namespace href.
1734  *
1735  * Checks that the node has a given name and a given namespace href.
1736  *
1737  * Returns: true if the node matches false otherwise.
1738  */
1739 gboolean
lasso_xml_is_element_node(const xmlNodePtr node,const xmlChar * name,const xmlChar * ns)1740 lasso_xml_is_element_node(const xmlNodePtr node,
1741                           const xmlChar *name, const xmlChar *ns)
1742 {
1743     if (node == NULL) {
1744         return FALSE;
1745     }
1746 
1747     return (node->type == XML_ELEMENT_NODE &&
1748             xmlStrEqual(node->name, name) &&
1749             xmlStrEqual(lasso_xml_get_node_ns_href(node), ns));
1750 }
1751 
1752 gboolean
lasso_xml_is_soap(xmlNode * root)1753 lasso_xml_is_soap(xmlNode *root)
1754 {
1755     return lasso_xml_is_element_node(root, BAD_CAST "Envelope",
1756                                      BAD_CAST LASSO_SOAP_ENV_HREF);
1757 }
1758 
1759 /**
1760  * lasso_xml_soap11_get_header:
1761  * @envelope_node: the pointer to <soap:Envelope> node.
1762  *
1763  * Gets pointer to the <soap:Header> node.
1764  *
1765  * Returns: pointer to <soap:Header> node or NULL if an error occurs.
1766  */
1767 xmlNodePtr
lasso_xml_soap11_get_header(xmlNodePtr envelope_node)1768 lasso_xml_soap11_get_header(xmlNodePtr envelope_node)
1769 {
1770     xmlNodePtr node;
1771 
1772     if (envelope_node == NULL) {
1773         return NULL;
1774     }
1775 
1776     /* optional Header node is first */
1777     node = lasso_xml_next_element_node(envelope_node->children);
1778     if (lasso_xml_is_element_node(node, BAD_CAST "Header",
1779                                   BAD_CAST LASSO_SOAP_ENV_HREF)) {
1780         return node;
1781     }
1782 
1783     return NULL;
1784 }
1785 
1786 /**
1787  * lasso_xml_soap11_get_body:
1788  * @envelope_node: the pointer to <soap:Envelope> node.
1789  *
1790  * Gets pointer to the <soap:Body> node.
1791  *
1792  * Returns: pointer to <soap:Body> node or NULL if an error occurs.
1793  */
1794 xmlNodePtr
lasso_xml_soap11_get_body(xmlNodePtr envelope_node)1795 lasso_xml_soap11_get_body(xmlNodePtr envelope_node)
1796 {
1797     xmlNodePtr node;
1798 
1799     if (envelope_node == NULL) {
1800         return NULL;
1801     }
1802 
1803     /* optional Header node first */
1804     node = lasso_xml_next_element_node(envelope_node->children);
1805     if (lasso_xml_is_element_node(node, BAD_CAST "Header",
1806                                   BAD_CAST LASSO_SOAP_ENV_HREF)) {
1807         node = lasso_xml_next_element_node(node->next);
1808     }
1809 
1810     /* Body node is next */
1811     if (!lasso_xml_is_element_node(node, BAD_CAST "Body",
1812                                    BAD_CAST  LASSO_SOAP_ENV_HREF)) {
1813         return NULL;
1814     }
1815 
1816     return node;
1817 }
1818 
1819 xmlNode*
lasso_xml_get_soap_content(xmlNode * root)1820 lasso_xml_get_soap_content(xmlNode *root)
1821 {
1822 	gboolean is_soap11 = FALSE;
1823 	xmlNode *content = NULL;
1824 
1825 	is_soap11 = lasso_xml_is_element_node(root, BAD_CAST "Envelope",
1826                                               BAD_CAST LASSO_SOAP_ENV_HREF);
1827 	if (is_soap11) {
1828 		xmlNode *body;
1829 
1830 		if (is_soap11) {
1831 			body = lasso_xml_soap11_get_body(root);
1832 		}
1833 		if (body) {
1834 			content = xmlSecGetNextElementNode(body->children);
1835 		}
1836 	}
1837 
1838 	return content;
1839 }
1840 
1841 LassoMessageFormat
lasso_xml_parse_message(const char * message,LassoMessageFormat constraint,xmlDoc ** doc_out,xmlNode ** root_out)1842 lasso_xml_parse_message(const char *message, LassoMessageFormat constraint, xmlDoc **doc_out, xmlNode **root_out)
1843 {
1844 	char *msg = NULL;
1845 	gboolean b64 = FALSE;
1846 	LassoMessageFormat rc = LASSO_MESSAGE_FORMAT_UNKNOWN;
1847 	xmlDoc *doc = NULL;
1848 	xmlNode *root = NULL;
1849 	gboolean any = constraint == LASSO_MESSAGE_FORMAT_UNKNOWN;
1850 
1851 	msg = (char*)message;
1852 
1853 	/* BASE64 case */
1854 	if (any || constraint == LASSO_MESSAGE_FORMAT_BASE64) {
1855 		if (message[0] != 0 && is_base64(message)) {
1856 			msg = g_malloc(strlen(message));
1857 			rc = xmlSecBase64Decode((xmlChar*)message, (xmlChar*)msg, strlen(message));
1858 			if (rc >= 0) {
1859 				b64 = TRUE;
1860 			} else {
1861 				lasso_release(msg);
1862 				msg = (char*)message;
1863 			}
1864 		}
1865 	}
1866 
1867 	/* XML case */
1868 	if (any || constraint == LASSO_MESSAGE_FORMAT_BASE64 ||
1869 		constraint == LASSO_MESSAGE_FORMAT_XML ||
1870 		constraint == LASSO_MESSAGE_FORMAT_SOAP) {
1871 		if (strchr(msg, '<')) {
1872 			doc = lasso_xml_parse_memory(msg, strlen(msg));
1873 			if (doc == NULL) {
1874 				rc = LASSO_MESSAGE_FORMAT_UNKNOWN;
1875 				goto cleanup;
1876 			}
1877 			root = xmlDocGetRootElement(doc);
1878 
1879 			if (any || constraint == LASSO_MESSAGE_FORMAT_SOAP) {
1880 				gboolean is_soap = FALSE;
1881 
1882 				is_soap = lasso_xml_is_soap(root);
1883 				if (is_soap) {
1884 					root = lasso_xml_get_soap_content(root);
1885 				}
1886 				if (! root) {
1887 					rc = LASSO_MESSAGE_FORMAT_ERROR;
1888 					goto cleanup;
1889 				}
1890 				if (is_soap) {
1891 					rc = LASSO_MESSAGE_FORMAT_SOAP;
1892 					goto cleanup;
1893 				}
1894 				if (b64) {
1895 					lasso_release(msg);
1896 					rc = LASSO_MESSAGE_FORMAT_BASE64;
1897 					goto cleanup;
1898 				}
1899 				rc = LASSO_MESSAGE_FORMAT_XML;
1900 				goto cleanup;
1901 			}
1902 		}
1903 	}
1904 
1905 cleanup:
1906 	if (doc_out) {
1907 		*doc_out = doc;
1908 		if (root_out) {
1909 			*root_out = root;
1910 		}
1911 	} else {
1912 		lasso_release_doc(doc);
1913 		lasso_release_xml_node(root);
1914 	}
1915 	return rc;
1916 }
1917 
1918 static gboolean
is_base64(const char * message)1919 is_base64(const char *message)
1920 {
1921 	const char *c;
1922 
1923 	c = message;
1924 	while (*c != 0 && (isalnum((int)*c) || *c == '+' || *c == '/' || *c == '\n' || *c == '\r')) c++;
1925 	while (*c == '=' || *c == '\n' || *c == '\r') c++; /* trailing = */
1926 
1927 	if (*c == 0)
1928 		return TRUE;
1929 
1930 	return FALSE;
1931 }
1932 
1933 /**
1934  * lasso_node_decrypt_xmlnode
1935  * @encrypted_element: an EncrytpedData #xmlNode
1936  * @encrypted_keys: a #GList of EncrytpedKey #xmlNode
1937  * @encryption_private_key : a private key to decrypt the node
1938  * @output: a pointer a #LassoNode variable to store the decrypted element
1939  *
1940  * Try to decrypt an encrypted element.
1941  *
1942  * Return value: 0 if successful,
1943  * LASSO_DS_ERROR_DECRYPTION_FAILED if decrypted failed,
1944  * LASSO_XML_ERROR_OBJECT_CONSTRUCTION_FAILED if construction of a #LassoNode from the decrypted
1945  * content failed,
1946  * LASSO_DS_ERROR_CONTEXT_CREATION_FAILED if some context initialization failed.
1947  **/
1948 int
lasso_node_decrypt_xmlnode(xmlNode * encrypted_element,GList * encrypted_keys,xmlSecKey * encryption_private_key,LassoNode ** output)1949 lasso_node_decrypt_xmlnode(xmlNode* encrypted_element,
1950 		GList *encrypted_keys,
1951 		xmlSecKey *encryption_private_key,
1952 		LassoNode **output)
1953 {
1954 	xmlDocPtr doc = NULL;
1955 	xmlDocPtr doc2 = NULL;
1956 	xmlSecEncCtxPtr encCtx = NULL;
1957 	xmlSecKeyPtr sym_key = NULL;
1958 	xmlSecBufferPtr key_buffer = NULL;
1959 	LassoNode *decrypted_node = NULL;
1960 	xmlNodePtr encrypted_data_node = NULL;
1961 	xmlNodePtr encrypted_key_node = NULL;
1962 	xmlNodePtr encryption_method_node = NULL;
1963 	xmlChar *algorithm = NULL;
1964 	xmlSecKeyDataId key_type;
1965 	GList *i = NULL;
1966 	int rc = LASSO_XMLENC_ERROR_INVALID_ENCRYPTED_DATA;
1967 
1968 	if (encryption_private_key == NULL || !xmlSecKeyIsValid(encryption_private_key)) {
1969 		message(G_LOG_LEVEL_WARNING, "Invalid decryption key");
1970 		rc = LASSO_PROFILE_ERROR_MISSING_ENCRYPTION_PRIVATE_KEY;
1971 		goto cleanup;
1972 	}
1973 
1974 	/* Need to duplicate it because xmlSecEncCtxDestroy(encCtx); will destroy it */
1975 	encryption_private_key = xmlSecKeyDuplicate(encryption_private_key);
1976 
1977 	encrypted_data_node = xmlCopyNode(encrypted_element, 1);
1978 
1979 	/* Get the encryption algorithm for EncryptedData in its EncryptionMethod node */
1980 	encryption_method_node = xmlSecTmplEncDataGetEncMethodNode(encrypted_data_node);
1981 	if (encryption_method_node == NULL) {
1982 		message(G_LOG_LEVEL_WARNING, "No EncryptionMethod node in EncryptedData");
1983 		goto cleanup;
1984 	}
1985 	algorithm = xmlGetProp(encryption_method_node, (xmlChar *)"Algorithm");
1986 	if (algorithm == NULL) {
1987 		message(G_LOG_LEVEL_WARNING, "No EncryptionMethod");
1988 		goto cleanup;
1989 	}
1990 	if (strstr((char*)algorithm , "#aes")) {
1991 		key_type = xmlSecKeyDataAesId;
1992 	} else if (strstr((char*)algorithm , "des")) {
1993 		key_type = xmlSecKeyDataDesId;
1994 	} else {
1995 		message(G_LOG_LEVEL_WARNING, "Unknown EncryptionMethod");
1996 		goto cleanup;
1997 	}
1998 
1999 	/* Get the EncryptedKey */
2000 	if (encrypted_keys != NULL) {
2001 		for (i = encrypted_keys; i; i = g_list_next(i)) {
2002 			if (i->data == NULL)
2003 				continue;
2004 			if (strcmp((char*)((xmlNode*)i->data)->name, "EncryptedKey") == 0) {
2005 				encrypted_key_node = xmlCopyNode((xmlNode*)(i->data), 1);
2006 				break;
2007 			}
2008 		}
2009 	} else {
2010 		/* Look an EncryptedKey inside the EncryptedData */
2011 		xmlNodePtr key_info;
2012 		do {
2013 			key_info = xmlSecFindChild(encrypted_data_node, xmlSecNodeKeyInfo, xmlSecDSigNs);
2014 			if (! key_info)
2015 				break;
2016 			encrypted_key_node = xmlSecFindChild(key_info, xmlSecNodeEncryptedKey, xmlSecEncNs);
2017 		} while (0);
2018 	}
2019 
2020 	if (encrypted_key_node == NULL) {
2021 		message(G_LOG_LEVEL_WARNING, "No EncryptedKey node");
2022 		goto cleanup;
2023 	}
2024 
2025 	/* Create a document to contain the node to decrypt */
2026 	doc = xmlNewDoc((xmlChar*)"1.0");
2027 	xmlDocSetRootElement(doc, encrypted_data_node);
2028 
2029 	doc2 = xmlNewDoc((xmlChar*)"1.0");
2030 	xmlDocSetRootElement(doc2, encrypted_key_node);
2031 
2032 	/* create encryption context to decrypt EncryptedKey */
2033 	encCtx = xmlSecEncCtxCreate(NULL);
2034 	if (encCtx == NULL) {
2035 		message(G_LOG_LEVEL_WARNING, "Failed to create encryption context");
2036 		rc = LASSO_DS_ERROR_CONTEXT_CREATION_FAILED;
2037 		goto cleanup;
2038 	}
2039 	encCtx->encKey = encryption_private_key;
2040 	encCtx->mode = xmlEncCtxModeEncryptedKey;
2041 
2042 	/* decrypt the EncryptedKey */
2043 	key_buffer = xmlSecEncCtxDecryptToBuffer(encCtx, encrypted_key_node);
2044 	if (key_buffer != NULL) {
2045 		sym_key = xmlSecKeyReadBuffer(key_type, key_buffer);
2046 	}
2047 	rc = LASSO_DS_ERROR_ENCRYPTION_FAILED;
2048 	if (sym_key == NULL) {
2049 		goto cleanup;
2050 	}
2051 
2052 	/* create encryption context to decrypt EncryptedData */
2053 	xmlSecEncCtxDestroy(encCtx);
2054 	encCtx = xmlSecEncCtxCreate(NULL);
2055 	if (encCtx == NULL) {
2056 		message(G_LOG_LEVEL_WARNING, "Failed to create encryption context");
2057 		rc = LASSO_DS_ERROR_CONTEXT_CREATION_FAILED;
2058 		goto cleanup;
2059 	}
2060 	encCtx->encKey = sym_key;
2061 	encCtx->mode = xmlEncCtxModeEncryptedData;
2062 
2063 	/* decrypt the EncryptedData */
2064 	if ((xmlSecEncCtxDecrypt(encCtx, encrypted_data_node) < 0) || (encCtx->result == NULL)) {
2065 		rc = LASSO_XMLENC_ERROR_INVALID_ENCRYPTED_DATA;
2066 		message(G_LOG_LEVEL_WARNING, "EncryptedData decryption failed");
2067 		goto cleanup;
2068 	}
2069 
2070 	decrypted_node = lasso_node_new_from_xmlNode(doc->children);
2071 	if (decrypted_node) {
2072 		rc = 0;
2073 	} else {
2074 		rc = LASSO_XML_ERROR_OBJECT_CONSTRUCTION_FAILED;
2075 	}
2076 	if (output) {
2077 		lasso_assign_gobject(*output, decrypted_node);
2078 	}
2079 
2080 cleanup:
2081 	if (doc == NULL && encrypted_data_node) {
2082 		xmlFreeNode(encrypted_data_node);
2083 	}
2084 	if (doc2 == NULL && encrypted_key_node) {
2085 		xmlFreeNode(encrypted_key_node);
2086 	}
2087 	if (encCtx) {
2088 		xmlSecEncCtxDestroy(encCtx);
2089 	}
2090 	lasso_release_doc(doc);
2091 	lasso_release_doc(doc2);
2092 	lasso_release_gobject(decrypted_node);
2093 	lasso_release_xml_string(algorithm);
2094 
2095 	return rc;
2096 }
2097 
xml_logv(int log_level,const char * msg,va_list arg_ptr)2098 static void xml_logv(int log_level, const char *msg, va_list arg_ptr) {
2099 	char buffer[512], *escaped;
2100 
2101 	vsnprintf(buffer, 512, msg, arg_ptr);
2102 	escaped = g_strescape(buffer, NULL);
2103 	g_log(LASSO_LOG_DOMAIN, log_level, "libxml2: %s", escaped);
2104 	lasso_release_string(escaped);
2105 }
2106 
__xmlWarningFunc(G_GNUC_UNUSED void * userData,const char * msg,...)2107 static void __xmlWarningFunc(G_GNUC_UNUSED void *userData, const char *msg, ...) {
2108 	va_list arg_ptr;
2109 
2110 	va_start(arg_ptr, msg);
2111 	xml_logv(G_LOG_LEVEL_WARNING, msg, arg_ptr);
2112         va_end(arg_ptr);
2113 }
2114 
__xmlErrorFunc(G_GNUC_UNUSED void * userData,const char * msg,...)2115 static void __xmlErrorFunc(G_GNUC_UNUSED void *userData, const char *msg, ...) {
2116 	va_list arg_ptr;
2117 
2118 	va_start(arg_ptr, msg);
2119 	xml_logv(G_LOG_LEVEL_CRITICAL, msg, arg_ptr);
2120         va_end(arg_ptr);
2121 }
2122 
2123 /**
2124  * lasso_xml_parse_memory:
2125  * @buffer:  an pointer to a char array
2126  * @size:  the size of the array
2127  *
2128  * Parse an XML in-memory block and build a tree; exactly like xmlParseMemory
2129  * safe two exceptions:
2130  * <itemizedlist>
2131  * <listitem><para>
2132  *  it won't download anything from the network (XML_PARSE_NONET)
2133  * </listitem></para>
2134  * <listitem><para>
2135  *  it will refuse documents with a DTD (for security reason)
2136  * </para></listitem>
2137  * </itemizedlist>
2138  *
2139  * Return value: the resulting document tree
2140  **/
2141 xmlDocPtr
lasso_xml_parse_memory(const char * buffer,int size)2142 lasso_xml_parse_memory(const char *buffer, int size) {
2143 	return lasso_xml_parse_memory_with_error(buffer, size, NULL);
2144 }
2145 
2146 xmlDocPtr
lasso_xml_parse_memory_with_error(const char * buffer,int size,xmlError * error)2147 lasso_xml_parse_memory_with_error(const char *buffer, int size, xmlError *error) {
2148 	xmlDocPtr ret;
2149 	xmlParserCtxtPtr ctxt;
2150 
2151 	ctxt = xmlCreateMemoryParserCtxt(buffer, size);
2152 	if (ctxt == NULL) {
2153 		return NULL;
2154 	}
2155 	xmlDetectSAX2(ctxt);
2156 	if (ctxt->errNo == XML_ERR_NO_MEMORY) {
2157 		return NULL;
2158 	}
2159 	ctxt->recovery = 0;
2160 	xmlCtxtUseOptions(ctxt, XML_PARSE_NONET);
2161 	if (error) {
2162 		ctxt->sax->warning = NULL;
2163 		ctxt->sax->error = NULL;
2164 		ctxt->sax->fatalError = NULL;
2165 	} else {
2166 		/* reroute errors through GLib logger */
2167 		ctxt->sax->warning = __xmlWarningFunc;
2168 		ctxt->sax->error = __xmlErrorFunc;
2169 	}
2170 
2171 	xmlParseDocument(ctxt);
2172 
2173 	if (error) {
2174 		xmlCopyError(&ctxt->lastError, error);
2175 	}
2176 
2177 	if (ctxt->wellFormed && ctxt->myDoc->intSubset != NULL) {
2178 		message(G_LOG_LEVEL_WARNING, "Denied message with DTD content");
2179 		ctxt->wellFormed = 0;
2180 	}
2181 
2182 	if (ctxt->wellFormed) {
2183 		ret = ctxt->myDoc;
2184 	} else {
2185 		ret = NULL;
2186 		lasso_release_doc(ctxt->myDoc);
2187 		ctxt->myDoc = NULL;
2188 	}
2189 	xmlFreeParserCtxt(ctxt);
2190 
2191 	return ret;
2192 }
2193 
2194 /**
2195  * lasso_xml_parse_file:
2196  * @filepath: the file path
2197  *
2198  * Parse an XML file, report errors through GLib logger with the Lasso domain
2199  *
2200  * Return value: a newly create #xmlDoc object if successful, NULL otherwise.
2201  */
2202 xmlDocPtr
lasso_xml_parse_file(const char * filepath)2203 lasso_xml_parse_file(const char *filepath)
2204 {
2205 	char *file_content;
2206 	size_t file_length;
2207 	GError *error = NULL;
2208 
2209 	if (g_file_get_contents(filepath, &file_content, &file_length, &error)) {
2210 		xmlDocPtr ret;
2211 
2212 		ret = lasso_xml_parse_memory(file_content, file_length);
2213 		lasso_release(file_content);
2214 		return ret;
2215 	} else {
2216 		message(G_LOG_LEVEL_CRITICAL, "Cannot read XML file %s: %s", filepath, error->message);
2217 		g_error_free(error);
2218 		return NULL;
2219 	}
2220 }
2221 
2222 /* (almost) straight from libxml2 internal API */
2223 static void
xmlDetectSAX2(xmlParserCtxtPtr ctxt)2224 xmlDetectSAX2(xmlParserCtxtPtr ctxt) {
2225 	if (ctxt == NULL) return;
2226 #ifdef LIBXML_SAX1_ENABLED
2227 	if ((ctxt->sax != NULL) && (ctxt->sax->initialized == XML_SAX2_MAGIC) &&
2228 			((ctxt->sax->startElementNs != NULL) ||
2229 			 (ctxt->sax->endElementNs != NULL)))
2230 		ctxt->sax2 = 1;
2231 #else
2232 	ctxt->sax2 = 1;
2233 #endif /* LIBXML_SAX1_ENABLED */
2234 
2235 	ctxt->str_xml = xmlDictLookup(ctxt->dict, BAD_CAST "xml", 3);
2236 	ctxt->str_xmlns = xmlDictLookup(ctxt->dict, BAD_CAST "xmlns", 5);
2237 	ctxt->str_xml_ns = xmlDictLookup(ctxt->dict, XML_XML_NAMESPACE, 36);
2238 	if ((ctxt->str_xml==NULL) || (ctxt->str_xmlns==NULL) ||
2239 			(ctxt->str_xml_ns == NULL)) {
2240 		ctxt->errNo = XML_ERR_NO_MEMORY;
2241 	}
2242 }
2243 
2244 /**
2245  * lasso_get_relaystate_from_query:
2246  * @query: a C-string containing the query part of an URL
2247  *
2248  * Extracts the relaystate argument contained in an URL query string.
2249  *
2250  * Return value: NULL if not relaystate is present in the URL, the RelayState decoded value
2251  * otherwise.
2252  */
2253 char *
lasso_get_relaystate_from_query(const char * query)2254 lasso_get_relaystate_from_query(const char *query) {
2255 	const char *start = NULL, *end = NULL;
2256 	char *result = NULL;
2257 
2258 	if (query == NULL)
2259 		return NULL;
2260 	if (strncmp(query, LASSO_SAML2_FIELD_RELAYSTATE "=", sizeof(LASSO_SAML2_FIELD_RELAYSTATE
2261 				"=") - 1) == 0) {
2262 		start = query + sizeof(LASSO_SAML2_FIELD_RELAYSTATE);
2263 	}
2264 	if (! start) {
2265 		if (! start) {
2266 			start = strstr(query, "&RelayState=");
2267 		}
2268 		if (! start) {
2269 			start = strstr(query, ";RelayState=");
2270 		}
2271 		if (start) {
2272 			start += sizeof(LASSO_SAML2_FIELD_RELAYSTATE "=");
2273 		}
2274 	}
2275 	if (start) {
2276 		ptrdiff_t length;
2277 		const char *end2;
2278 
2279 		end = strchr(start, '&');
2280 		end2 = strchr(start, ';');
2281 		if ((end2 != NULL) && ((end == NULL) || (end2 < end))) {
2282 			end = end2;
2283 		}
2284 		if (end) {
2285 			length = end-start;
2286 		} else {
2287 			length = strlen(start);
2288 		}
2289 		if (length > query_string_attribute_length_limit) {
2290 			message(G_LOG_LEVEL_WARNING, "Received a RelayState of size %ti > %u",
2291 					length, query_string_attribute_length_limit);
2292 		}
2293 		if (length) {
2294 			result = xmlURIUnescapeString(start, length, NULL);
2295 		} else {
2296 			result = g_malloc0(1);
2297 		}
2298 	}
2299 	return result;
2300 }
2301 
2302 /**
2303  * lasso_url_add_parameters:
2304  * @url: the original URL
2305  * @free: whether to free the URL parameter
2306  * @...: pairs of strings, key, value, followed by NULL
2307  *
2308  * Iterate over all pairs of key,value, and concatenate them to @url encoded as "&key=value", where
2309  * key and value are url-encoded.
2310  * If free is true and at least one pair was given, url is freed. If url is NULL, the first
2311  * ampersand is omitted.
2312  *
2313  * Return value: a newly allocated string, or url.
2314  */
2315 char*
lasso_url_add_parameters(char * url,gboolean free,...)2316 lasso_url_add_parameters(char *url,
2317 		gboolean free, ...)
2318 {
2319 	char *old_url = url, *new_url = NULL;
2320 	xmlChar *encoded_key = NULL, *encoded_value;
2321 	va_list ap;
2322 
2323 	va_start(ap, free);
2324 
2325 	while (1) {
2326 		char *key;
2327 		char *value;
2328 
2329 		key = va_arg(ap, char*);
2330 		if (! key) {
2331 			break;
2332 		}
2333 		encoded_key = lasso_xmlURIEscapeStr((xmlChar*)key, NULL);
2334 		goto_cleanup_if_fail(encoded_key);
2335 
2336 		value = va_arg(ap, char*);
2337 		if (! value) {
2338 			message(G_LOG_LEVEL_CRITICAL, "lasso_url_add_parameter: key without a value !!");
2339 			break;
2340 		}
2341 		encoded_value = lasso_xmlURIEscapeStr((xmlChar*)value, NULL);
2342 		goto_cleanup_if_fail(encoded_value);
2343 
2344 		if (old_url) {
2345 			new_url = g_strdup_printf("%s&%s=%s", old_url, (char*)encoded_key, (char*)encoded_value);
2346 		} else {
2347 			new_url = g_strdup_printf("%s=%s", (char*)encoded_key, (char*)encoded_value);
2348 		}
2349 		if (old_url != url) {
2350 			lasso_release_string(old_url);
2351 		}
2352 		old_url = new_url;
2353 
2354 		lasso_release_xml_string(encoded_key);
2355 		lasso_release_xml_string(encoded_value);
2356 	}
2357 cleanup:
2358 	va_end(ap);
2359 	if (free && new_url != url) {
2360 		lasso_release(url);
2361 	}
2362 	lasso_release_xml_string(encoded_key);
2363 
2364 	return new_url;
2365 }
2366 
2367 xmlSecKey*
_lasso_xmlsec_load_key_from_buffer(const char * buffer,size_t length,const char * password,LassoSignatureMethod signature_method,const char * certificate)2368 _lasso_xmlsec_load_key_from_buffer(const char *buffer, size_t length, const char *password,
2369 		LassoSignatureMethod signature_method, const char *certificate)
2370 {
2371 	int i = 0;
2372 	xmlSecKeyDataFormat key_formats[] = {
2373 		xmlSecKeyDataFormatPem,
2374 		xmlSecKeyDataFormatCertPem,
2375 		xmlSecKeyDataFormatDer,
2376 		xmlSecKeyDataFormatBinary,
2377 		xmlSecKeyDataFormatCertDer,
2378 		xmlSecKeyDataFormatPkcs8Der,
2379 		xmlSecKeyDataFormatPkcs8Pem,
2380 		0
2381 	};
2382 	xmlSecKeyDataFormat cert_formats[] = {
2383 		xmlSecKeyDataFormatCertPem,
2384 		xmlSecKeyDataFormatCertDer,
2385 		0
2386 	};
2387 	xmlSecKey *private_key = NULL;
2388 
2389 	xmlSecErrorsDefaultCallbackEnableOutput(FALSE);
2390 	switch (signature_method) {
2391 		case LASSO_SIGNATURE_METHOD_RSA_SHA1:
2392 		case LASSO_SIGNATURE_METHOD_DSA_SHA1:
2393 		case LASSO_SIGNATURE_METHOD_RSA_SHA256:
2394 		case LASSO_SIGNATURE_METHOD_RSA_SHA384:
2395 		case LASSO_SIGNATURE_METHOD_RSA_SHA512:
2396 			for (i = 0; key_formats[i] && private_key == NULL; i++) {
2397 				private_key = xmlSecCryptoAppKeyLoadMemory((xmlSecByte*)buffer, length,
2398 						key_formats[i], password, NULL, NULL);
2399 			}
2400 			break;
2401 		case LASSO_SIGNATURE_METHOD_HMAC_SHA1:
2402 		case LASSO_SIGNATURE_METHOD_HMAC_SHA256:
2403 		case LASSO_SIGNATURE_METHOD_HMAC_SHA384:
2404 		case LASSO_SIGNATURE_METHOD_HMAC_SHA512:
2405 			private_key = xmlSecKeyReadMemory(xmlSecKeyDataHmacId, (xmlSecByte*)buffer, length);
2406 			if (private_key) {
2407 				xmlSecKeySetName(private_key, BAD_CAST "shared");
2408 			}
2409 			break;
2410 		case LASSO_SIGNATURE_METHOD_LAST:
2411 		case LASSO_SIGNATURE_METHOD_NONE:
2412 			g_assert_not_reached();
2413 	}
2414 	goto_cleanup_if_fail(private_key != NULL);
2415 	if (certificate) {
2416 		int done = 0;
2417 
2418 		switch (signature_method) {
2419 			case LASSO_SIGNATURE_METHOD_RSA_SHA1:
2420 			case LASSO_SIGNATURE_METHOD_DSA_SHA1:
2421 			case LASSO_SIGNATURE_METHOD_RSA_SHA256:
2422 			case LASSO_SIGNATURE_METHOD_RSA_SHA384:
2423 			case LASSO_SIGNATURE_METHOD_RSA_SHA512:
2424 
2425 				for (i=0; cert_formats[i]; i++) {
2426 					if (xmlSecCryptoAppKeyCertLoad(private_key, certificate, cert_formats[i])
2427 							== 0) {
2428 						done = 1;
2429 						break;
2430 					}
2431 					if (xmlSecCryptoAppKeyCertLoadMemory(private_key, BAD_CAST certificate,
2432 								strlen(certificate), cert_formats[i]) == 0) {
2433 						done = 1;
2434 						break;
2435 					}
2436 				}
2437 				if (done == 0) {
2438 					warning("Unable to load certificate: %s", certificate);
2439 				}
2440 				break;
2441 			default:
2442 				warning("Attaching a certificate for signature only "
2443 						"works with DSA and RSA algorithms.");
2444 		}
2445 	}
2446 cleanup:
2447 	xmlSecErrorsDefaultCallbackEnableOutput(TRUE);
2448 	return private_key;
2449 }
2450 /**
2451  * lasso_base64_decode:
2452  * @from: the source base64 encoded string
2453  * @buffer: an output argument to place the resulting buffer pointer
2454  * @buffer_len: an output argument to place the resulting buffer length
2455  *
2456  * Decode the given string as Base64 and allocate a buffer for the decoded content, place the
2457  * pointer to the buffer in @buffer and the length in @buffer_len
2458  *
2459  * Return value: TRUE if successful, FALSE otherwise.
2460  */
2461 gboolean
lasso_base64_decode(const char * from,char ** buffer,int * buffer_len)2462 lasso_base64_decode(const char *from, char **buffer, int *buffer_len)
2463 {
2464 	size_t len = strlen(from);
2465 	int ret;
2466 
2467 	/* base64 map 4 bytes to 3 */
2468 	len = len / 4 + (len % 4 ? 1 : 0);
2469 	len *= 3;
2470 	len += 1; /* zero byte */
2471 	*buffer = g_malloc0(len);
2472 
2473 	xmlSecErrorsDefaultCallbackEnableOutput(FALSE);
2474 	ret = xmlSecBase64Decode(BAD_CAST from, BAD_CAST *buffer, len);
2475 	xmlSecErrorsDefaultCallbackEnableOutput(TRUE);
2476 	if (ret <= 0) {
2477 		lasso_release_string(*buffer);
2478 		return FALSE;
2479 	}
2480 	*buffer_len = ret;
2481 	return TRUE;
2482 }
2483 
2484 /**
2485  * lasso_xmlURIEscapeStr:
2486  * @from: the source URI string
2487  * @list: optional list of characters not to escape
2488  *
2489  * Drop-in replacement for libxml2 xmlURIEscapeStr(), but encoding
2490  * everything but [A-Za-z0-9._~-] which are the unreserved chartacters
2491  * for RFC3986 section 2.3
2492  *
2493  * Return value: a buffer containing the URL-encoded string or NULL on error
2494  */
2495 xmlChar *
lasso_xmlURIEscapeStr(const xmlChar * from,const xmlChar * list)2496 lasso_xmlURIEscapeStr(const xmlChar *from, const xmlChar *list)
2497 {
2498 	size_t len = 0;
2499 	const xmlChar *fp;
2500 	xmlChar *result;
2501 	int ri;
2502 
2503 	if (list == NULL)
2504 		list = "";
2505 
2506 	for (fp = from; *fp; fp++) {
2507 		if (isalnum(*fp) || strchr("._~-", *fp) || strchr(list, *fp))
2508 			len++;
2509 		else
2510 			len += 3;
2511 	}
2512 
2513 	result = g_malloc0(len + 1);
2514 	ri = 0;
2515 
2516 	for (fp = from; *fp; fp++) {
2517 		if (isalnum(*fp) || strchr("._~-", *fp) || strchr(list, *fp)) {
2518 			result[ri++] = *fp;
2519 		} else {
2520 			int msb = (*fp & 0xf0) >> 4;
2521 			int lsb = *fp & 0x0f;
2522 
2523 			result[ri++] = '%';
2524 			result[ri++] = (msb > 9) ? 'A' + msb - 10 : '0' + msb;
2525 			result[ri++] = (lsb > 9) ? 'A' + lsb - 10 : '0' + lsb;
2526 		}
2527 	}
2528 
2529 	result[ri++] = '\0';
2530 
2531 	return result;
2532 }
2533 
2534 /**
2535  * lasso_xmlsec_load_private_key_from_buffer:
2536  * @buffer: a buffer containing a key in any format
2537  * @length: length of the buffer
2538  * @password: eventually a password
2539  */
2540 xmlSecKey*
lasso_xmlsec_load_private_key_from_buffer(const char * buffer,size_t length,const char * password,LassoSignatureMethod signature_method,const char * certificate)2541 lasso_xmlsec_load_private_key_from_buffer(const char *buffer, size_t length, const char *password,
2542 		LassoSignatureMethod signature_method, const char *certificate) {
2543 	xmlSecKey *private_key = NULL;
2544 
2545 	private_key = _lasso_xmlsec_load_key_from_buffer(buffer, length, password, signature_method, certificate);
2546 
2547 	/* special lasso metadata hack */
2548 	if (! private_key) {
2549 		char *out = NULL;
2550 		int len;
2551 
2552 		if (lasso_base64_decode(buffer, &out, &len)) {
2553 			private_key = _lasso_xmlsec_load_key_from_buffer((char*)out, len, password,
2554 					signature_method, certificate);
2555 		}
2556 		lasso_release_string(out);
2557 	}
2558 
2559 	return private_key;
2560 }
2561 
2562 xmlSecKey*
lasso_xmlsec_load_private_key(const char * filename_or_buffer,const char * password,LassoSignatureMethod signature_method,const char * certificate)2563 lasso_xmlsec_load_private_key(const char *filename_or_buffer, const char *password, LassoSignatureMethod signature_method, const char *certificate) {
2564 	char *buffer = NULL;
2565 	size_t length;
2566 	xmlSecKey *ret;
2567 
2568 	if (! filename_or_buffer)
2569 		return NULL;
2570 
2571 	if (g_file_get_contents(filename_or_buffer, &buffer, &length, NULL)) {
2572 		ret = lasso_xmlsec_load_private_key_from_buffer(buffer, length, password, signature_method, certificate);
2573 	} else {
2574 		ret = lasso_xmlsec_load_private_key_from_buffer(filename_or_buffer,
2575 				strlen(filename_or_buffer), password, signature_method,
2576 				certificate);
2577 	}
2578 	lasso_release_string(buffer);
2579 	return ret;
2580 
2581 }
2582 
2583 gboolean
lasso_get_base64_content(xmlNode * node,char ** content,size_t * length)2584 lasso_get_base64_content(xmlNode *node, char **content, size_t *length) {
2585 	xmlChar *base64, *stripped_base64;
2586 	xmlChar *result;
2587 	int base64_length;
2588 	int rc = 0;
2589 
2590 	if (! node || ! content || ! length)
2591 		return FALSE;
2592 
2593 	base64 = xmlNodeGetContent(node);
2594 	if (! base64)
2595 		return FALSE;
2596 	stripped_base64 = base64;
2597 	/* skip spaces */
2598 	while (*stripped_base64 && isspace(*stripped_base64))
2599 		stripped_base64++;
2600 
2601 	base64_length = strlen((char*)stripped_base64);
2602 	result = g_new(xmlChar, base64_length);
2603 	xmlSecErrorsDefaultCallbackEnableOutput(FALSE);
2604 	rc = xmlSecBase64Decode(stripped_base64, result, base64_length);
2605 	xmlSecErrorsDefaultCallbackEnableOutput(TRUE);
2606 	xmlFree(base64);
2607 	if (rc < 0) {
2608 		return FALSE;
2609 	} else {
2610 		*content = (char*)g_memdup(result, rc);
2611 		xmlFree(result);
2612 		*length = rc;
2613 		return TRUE;
2614 	}
2615 }
2616 
2617 xmlSecKeyPtr
lasso_xmlsec_load_key_info(xmlNode * key_descriptor)2618 lasso_xmlsec_load_key_info(xmlNode *key_descriptor)
2619 {
2620 	xmlSecKeyPtr key, result = NULL;
2621 	xmlNodePtr key_info = NULL;
2622 	xmlSecKeyInfoCtx ctx = {0};
2623 	xmlSecKeysMngr *keys_mngr = NULL;
2624 	xmlNodePtr key_value = NULL;
2625 	int rc = 0;
2626 	xmlChar *content = NULL;
2627 	X509 *cert = NULL;
2628 	xmlSecKeyDataPtr cert_data = NULL;
2629 	xmlSecKeyDataPtr cert_key = NULL;
2630 
2631 	if (! key_descriptor)
2632 		return NULL;
2633 
2634 	key_info = xmlSecFindChild(key_descriptor, xmlSecNodeKeyInfo, xmlSecDSigNs);
2635 	if (! key_info)
2636 		return NULL;
2637 	keys_mngr = xmlSecKeysMngrCreate();
2638 	rc = xmlSecCryptoAppDefaultKeysMngrInit(keys_mngr);
2639 	if (rc < 0) {
2640 		goto next;
2641 	}
2642 	rc = xmlSecKeyInfoCtxInitialize(&ctx, keys_mngr);
2643 	if (rc < 0) {
2644 		goto next;
2645 	}
2646 	ctx.flags = XMLSEC_KEYINFO_FLAGS_DONT_STOP_ON_KEY_FOUND
2647 		| XMLSEC_KEYINFO_FLAGS_X509DATA_DONT_VERIFY_CERTS;
2648 	ctx.mode = xmlSecKeyInfoModeRead;
2649 	ctx.keyReq.keyId = xmlSecKeyDataIdUnknown;
2650 	ctx.keyReq.keyType = xmlSecKeyDataTypePublic;
2651 	ctx.keyReq.keyUsage = xmlSecKeyDataUsageAny;
2652 	ctx.certsVerificationDepth = 0;
2653 
2654 	key = xmlSecKeyCreate();
2655 	if (lasso_flag_pem_public_key) {
2656 		xmlSecErrorsDefaultCallbackEnableOutput(FALSE);
2657 	}
2658 	rc = xmlSecKeyInfoNodeRead(key_info, key, &ctx);
2659 	if (lasso_flag_pem_public_key) {
2660 		xmlSecErrorsDefaultCallbackEnableOutput(TRUE);
2661 	}
2662 	xmlSecKeyInfoCtxFinalize(&ctx);
2663 
2664 	if (rc != 0) {
2665 		goto next;
2666 	}
2667 	/* reference, do not free */
2668 	cert_data = xmlSecKeyGetData(key, xmlSecOpenSSLKeyDataX509Id);
2669 
2670 	if (! cert_data) {
2671 		goto next;
2672 	}
2673 	/* reference, do not free */
2674 	cert = xmlSecOpenSSLKeyDataX509GetCert(cert_data, 0);
2675 	if (! cert) {
2676 		goto next;
2677 	}
2678 	/* new value, free */
2679 	cert_key = xmlSecOpenSSLX509CertGetKey(cert);
2680 	rc = xmlSecKeySetValue(key, cert_key);
2681 	if (rc < 0) {
2682 		xmlSecKeyDataDestroy(cert_key);
2683 	}
2684 next:
2685 	/* We found a key, exit */
2686 	if (xmlSecKeyIsValid(key)) {
2687 		result = key;
2688 		key = NULL;
2689 		goto cleanup;
2690 	}
2691 	xmlSecKeyDestroy(key);
2692 	if (lasso_flag_pem_public_key) {
2693 		if (! (key_value = xmlSecFindChild(key_info, xmlSecNodeKeyValue, xmlSecDSigNs)) &&
2694 			 ! (key_value = xmlSecFindNode(key_info, xmlSecNodeX509Certificate, xmlSecDSigNs)))  {
2695 			goto cleanup;
2696 		}
2697 
2698 		content = xmlNodeGetContent(key_value);
2699 		if (content) {
2700 			result = lasso_xmlsec_load_private_key_from_buffer((char*)content,
2701 					strlen((char*)content), NULL, LASSO_SIGNATURE_METHOD_RSA_SHA1, NULL);
2702 			xmlFree(content);
2703 		}
2704 	}
2705 
2706 cleanup:
2707 	lasso_release_key_manager(keys_mngr);
2708 	return result;
2709 }
2710 
2711 /**
2712  * lasso_xmlnode_to_string:
2713  * @xmlnode: an #xmlNode structure
2714  * @format: whether to allow formatting (it break XML signatures)
2715  *
2716  * Transform an XML node to a C string
2717  *
2718  * Return value: a newly allocated C string
2719  */
2720 char*
lasso_xmlnode_to_string(xmlNode * node,gboolean format,int level)2721 lasso_xmlnode_to_string(xmlNode *node, gboolean format, int level)
2722 {
2723 	xmlOutputBufferPtr output_buffer;
2724 	xmlBuffer *buffer;
2725 	char *str;
2726 
2727 	if (! node)
2728 		return NULL;
2729 
2730 	buffer = xmlBufferCreate();
2731 	output_buffer = xmlOutputBufferCreateBuffer(buffer, NULL);
2732 	xmlNodeDumpOutput(output_buffer, NULL, node, level, format ? 1 : 0, NULL);
2733 	xmlOutputBufferClose(output_buffer);
2734 	xmlBufferAdd(buffer, BAD_CAST "", 1);
2735 	/* do not mix XML and GLib strings, so we must copy */
2736 	str = g_strdup((char*)xmlBufferContent(buffer));
2737 	xmlBufferFree(buffer);
2738 
2739 	return str;
2740 }
2741 
2742 /**
2743  * lasso_string_to_xsd_integer:
2744  * @saml2_assertion: a #LassoSaml2Assertion object
2745  * @integer: a long int variable to store the result
2746  *
2747  * Parse a string using the xsd:integer schema.
2748  *
2749  * Return value: TRUE if successful, FALSE otherwise.
2750  */
2751 gboolean
lasso_string_to_xsd_integer(const char * str,long int * integer)2752 lasso_string_to_xsd_integer(const char *str, long int *integer)
2753 {
2754 	const char *save = str;
2755 
2756 	if (! str)
2757 		return FALSE;
2758 	while (isspace(*str))
2759 		str++;
2760 	if (*str == '+' || *str == '-')
2761 		str++;
2762 	while (isdigit(*str))
2763 		str++;
2764 	while (isspace(*str))
2765 		str++;
2766 	if (*str)
2767 		return FALSE;
2768 	*integer = strtol(save, NULL, 10);
2769 	if ((*integer == LONG_MAX || *integer == LONG_MIN) && errno == ERANGE)
2770 		return FALSE;
2771 	return TRUE;
2772 }
2773 
2774 void
lasso_set_string_from_prop(char ** str,xmlNode * node,xmlChar * name,xmlChar * ns)2775 lasso_set_string_from_prop(char **str, xmlNode *node, xmlChar *name, xmlChar *ns)
2776 {
2777 	xmlChar *value;
2778 
2779 	g_assert(str);
2780 	g_assert(node);
2781 	value = xmlGetNsProp(node, name, ns);
2782 	if (value) {
2783 		lasso_assign_string(*str, (char*)value);
2784 	}
2785 	lasso_release_xml_string(value);
2786 }
2787 
2788 
2789 /**
2790  * lasso_log_set_handler:
2791  * @log_levels: the log levels to apply the log handler for. To handle fatal
2792  *   and recursive messages as well, combine the log levels with the
2793  *   #G_LOG_FLAG_FATAL and #G_LOG_FLAG_RECURSION bit flags.
2794  * @log_func: the log handler function.
2795  * @user_data: data passed to the log handler.
2796  *
2797  * Sets the log handler for a domain and a set of log levels.  To handle fatal
2798  * and recursive messages the @log_levels parameter must be combined with the
2799  * #G_LOG_FLAG_FATAL and #G_LOG_FLAG_RECURSION bit flags.
2800  *
2801  * Note that since the #G_LOG_LEVEL_ERROR log level is always fatal, if you
2802  * want to set a handler for this log level you must combine it with
2803  * #G_LOG_FLAG_FATAL.
2804  *
2805  * Returns: the id of the new handler.
2806  **/
2807 guint
lasso_log_set_handler(GLogLevelFlags log_levels,GLogFunc log_func,gpointer user_data)2808 lasso_log_set_handler(GLogLevelFlags log_levels, GLogFunc log_func, gpointer user_data)
2809 {
2810 	return g_log_set_handler(LASSO_LOG_DOMAIN, log_levels, log_func, user_data);
2811 }
2812 
2813 /**
2814  * lasso_log_remove_handler:
2815  * @handler_id: the id of the handler, which was returned in
2816  *   lasso_log_set_handler().
2817  *
2818  * Removes the log handler.
2819  **/
2820 void
lasso_log_remove_handler(guint handler_id)2821 lasso_log_remove_handler(guint handler_id)
2822 {
2823 	g_log_remove_handler(LASSO_LOG_DOMAIN, handler_id);
2824 }
2825 
2826 /**
2827  * lasso_get_hmac_key:
2828  * @key: an #xmlSecKey object
2829  * @buffer: a byte buffer of size @size
2830  * @size: the size of @buffer as bytes
2831  *
2832  * Extract the symetric HMAC key from the #xmlSecKey structure and place a pointer to i into the
2833  * buffer variable.
2834  *
2835  * Return value: 0 if successful, an error code otherwise.
2836  */
2837 lasso_error_t
lasso_get_hmac_key(const xmlSecKey * key,void ** buffer,size_t * size)2838 lasso_get_hmac_key(const xmlSecKey *key, void **buffer, size_t *size)
2839 {
2840 	xmlSecKeyDataPtr key_data;
2841 	xmlSecBufferPtr key_data_buffer;
2842 
2843 	lasso_null_param(key);
2844 	lasso_null_param(buffer);
2845 	lasso_null_param(size);
2846 
2847 	if (key->value->id != xmlSecKeyDataHmacId) {
2848 		return LASSO_PARAM_ERROR_INVALID_VALUE;
2849 	}
2850 	key_data = xmlSecKeyGetValue((xmlSecKeyPtr)key);
2851 	g_return_val_if_fail(key_data, LASSO_PARAM_ERROR_INVALID_VALUE);
2852 	key_data_buffer = xmlSecKeyDataBinaryValueGetBuffer(key_data);
2853 	g_return_val_if_fail(key_data_buffer, LASSO_PARAM_ERROR_INVALID_VALUE);
2854 	*buffer = xmlSecBufferGetData(key_data_buffer);
2855 	*size = xmlSecBufferGetSize(key_data_buffer);
2856 	g_return_val_if_fail(*buffer && *size, LASSO_PARAM_ERROR_INVALID_VALUE);
2857 	return 0;
2858 }
2859 
2860 /**
2861  * lasso_make_signature_context_from_buffer:
2862  * @buffer: a byte buffer of size @length
2863  * @length: the size of @buffer as bytes
2864  * @password: an eventual password to decoded the private key contained in @buffer
2865  * @signature_method: the signature method to associate to this key
2866  * @certificate: a certificate as a file path or PEM encoded in a NULL-terminated string, to
2867  * associate with the key, it will be used to fill the KeyInfo node in an eventual signature.
2868  *
2869  * Load a signature key and return an initialized #LassoSignatureContext structure. If the structure
2870  * contains a new #xmlSecKey it must be freed by the caller. If your must store it. use
2871  * lasso_assign_new_signature_context and not lasso_assign_signature_context which is gonna
2872  * duplicate the key and so make a leak.
2873  *
2874  * Return value: an initialized LassoSignatureContext containing a freshly created @xmlSecKey object
2875  * successful, LASSO_SIGNATURE_CONTEXT_NONE otherwise. The caller must free the #xmlSecKey.
2876  */
2877 LassoSignatureContext
lasso_make_signature_context_from_buffer(const void * buffer,size_t length,const char * password,LassoSignatureMethod signature_method,const char * certificate)2878 lasso_make_signature_context_from_buffer(const void *buffer, size_t length, const char *password,
2879 		LassoSignatureMethod signature_method, const char *certificate) {
2880 	LassoSignatureContext context = LASSO_SIGNATURE_CONTEXT_NONE;
2881 
2882 	context.signature_key = lasso_xmlsec_load_private_key_from_buffer(buffer, length, password,
2883 			signature_method, certificate);
2884 	if (context.signature_key) {
2885 		context.signature_method = signature_method;
2886 	}
2887 	return context;
2888 }
2889 
2890 /**
2891  * lasso_make_signature_context_from_path_or_string:
2892  * @filename_or_buffer: a file path of a string containing the key PEM or Base64 encoded
2893  * @password: an eventual password to decoded the private key contained in @buffer
2894  * @signature_method: the signature method to associate to this key
2895  * @certificate: a certificate as a file path or PEM encoded in a NULL-terminated string, to
2896  * associate with the key, it will be used to fill the KeyInfo node in an eventual signature.
2897  *
2898  * Load a signature key and return an initialized #LassoSignatureContext structure. If the structure
2899  * contains a new #xmlSecKey it must be freed by the caller. If your must store it. use
2900  * lasso_assign_new_signature_context and not lasso_assign_signature_context which is gonna
2901  * duplicate the key and so make a leak.
2902  *
2903  * Return value: an initialized LassoSignatureContext containing a freshly created @xmlSecKey object
2904  * successful, LASSO_SIGNATURE_CONTEXT_NONE otherwise.
2905  */
2906 LassoSignatureContext
lasso_make_signature_context_from_path_or_string(char * filename_or_buffer,const char * password,LassoSignatureMethod signature_method,const char * certificate)2907 lasso_make_signature_context_from_path_or_string(char *filename_or_buffer, const char *password,
2908 		LassoSignatureMethod signature_method, const char *certificate) {
2909 	LassoSignatureContext context = LASSO_SIGNATURE_CONTEXT_NONE;
2910 
2911 	context.signature_key = lasso_xmlsec_load_private_key(filename_or_buffer, password,
2912 			signature_method, certificate);
2913 	if (context.signature_key) {
2914 		context.signature_method = signature_method;
2915 	}
2916 	return context;
2917 }
2918 
2919 xmlNs *
get_or_define_ns(xmlNode * xmlnode,const xmlChar * ns_uri,const xmlChar * advised_prefix)2920 get_or_define_ns(xmlNode *xmlnode, const xmlChar *ns_uri, const xmlChar *advised_prefix) {
2921 	xmlNs *ns;
2922 	char prefix[20];
2923 	int i = 1;
2924 
2925 	ns = xmlSearchNsByHref(NULL, xmlnode, ns_uri);
2926 	if (ns)
2927 		return ns;
2928 	/* Try with the advised prefix */
2929 	if (advised_prefix) {
2930 		ns = xmlSearchNs(NULL, xmlnode, BAD_CAST advised_prefix);
2931 		if (! ns) { /* If not taken, use it */
2932 			return xmlNewNs(xmlnode, ns_uri, BAD_CAST advised_prefix);
2933 		}
2934 	}
2935 	/* Create a prefix from scratch */
2936 	do {
2937 		sprintf(prefix, "ns%u", i);
2938 		i++;
2939 		ns = xmlSearchNs(NULL, xmlnode, BAD_CAST prefix);
2940 	} while (ns);
2941 	return xmlNewNs(xmlnode, ns_uri, BAD_CAST prefix);
2942 }
2943 
2944 
2945 void
set_qname_attribute(xmlNode * node,const xmlChar * attribute_ns_prefix,const xmlChar * attribute_ns_href,const xmlChar * attribute_name,const xmlChar * prefix,const xmlChar * href,const xmlChar * name)2946 set_qname_attribute(xmlNode *node,
2947 		const xmlChar *attribute_ns_prefix,
2948 		const xmlChar *attribute_ns_href,
2949 		const xmlChar *attribute_name,
2950 		const xmlChar *prefix,
2951 		const xmlChar *href,
2952 		const xmlChar *name) {
2953 	xmlNs *type_ns;
2954 	xmlNs *xsi_ns;
2955 	xmlChar *value;
2956 
2957 	xsi_ns = get_or_define_ns(node, attribute_ns_href, attribute_ns_prefix);
2958 	type_ns = get_or_define_ns(node, href, prefix);
2959 	value = BAD_CAST g_strdup_printf("%s:%s", type_ns->prefix, name);
2960 	xmlSetNsProp(node, xsi_ns, attribute_name, value);
2961 	lasso_release_string(value);
2962 }
2963 
2964 void
set_xsi_type(xmlNode * node,const xmlChar * type_ns_prefix,const xmlChar * type_ns_href,const xmlChar * type_name)2965 set_xsi_type(xmlNode *node,
2966 		const xmlChar *type_ns_prefix,
2967 		const xmlChar *type_ns_href,
2968 		const xmlChar *type_name) {
2969 	set_qname_attribute(node,
2970 			BAD_CAST LASSO_XSI_PREFIX,
2971 			BAD_CAST LASSO_XSI_HREF,
2972 			BAD_CAST "type",
2973 			type_ns_prefix,
2974 			type_ns_href,
2975 			type_name);
2976 }
2977 
2978 void
lasso_xmlnode_add_saml2_signature_template(xmlNode * node,LassoSignatureContext context,const char * id)2979 lasso_xmlnode_add_saml2_signature_template(xmlNode *node, LassoSignatureContext context,
2980 		const char *id) {
2981 	xmlSecTransformId transform_id;
2982 	xmlNode *existing_signature = NULL, *signature = NULL, *reference, *key_info;
2983 	char *uri;
2984 
2985 	g_assert(id);
2986 
2987 	if (! lasso_validate_signature_context(context) || ! node)
2988 		return;
2989 
2990 	switch (context.signature_method) {
2991 		case LASSO_SIGNATURE_METHOD_RSA_SHA1:
2992 			transform_id = xmlSecTransformRsaSha1Id;
2993 			break;
2994 		case LASSO_SIGNATURE_METHOD_DSA_SHA1:
2995 			transform_id = xmlSecTransformDsaSha1Id;
2996 			break;
2997 		case LASSO_SIGNATURE_METHOD_HMAC_SHA1:
2998 			transform_id = xmlSecTransformHmacSha1Id;
2999 			break;
3000 		case LASSO_SIGNATURE_METHOD_RSA_SHA256:
3001 			transform_id = xmlSecTransformRsaSha256Id;
3002 			break;
3003 		case LASSO_SIGNATURE_METHOD_HMAC_SHA256:
3004 			transform_id = xmlSecTransformHmacSha256Id;
3005 			break;
3006 		case LASSO_SIGNATURE_METHOD_RSA_SHA384:
3007 			transform_id = xmlSecTransformRsaSha384Id;
3008 			break;
3009 		case LASSO_SIGNATURE_METHOD_HMAC_SHA384:
3010 			transform_id = xmlSecTransformHmacSha384Id;
3011 			break;
3012 		case LASSO_SIGNATURE_METHOD_RSA_SHA512:
3013 			transform_id = xmlSecTransformRsaSha512Id;
3014 			break;
3015 		case LASSO_SIGNATURE_METHOD_HMAC_SHA512:
3016 			transform_id = xmlSecTransformHmacSha512Id;
3017 			break;
3018 		default:
3019 			g_assert_not_reached();
3020 	}
3021 	existing_signature = xmlSecFindChild(node, xmlSecNodeSignature, xmlSecDSigNs);
3022 	signature = xmlSecTmplSignatureCreate(NULL,
3023 			xmlSecTransformExclC14NId,
3024 			transform_id, NULL);
3025 	if (existing_signature) {
3026 		xmlSecReplaceNode(existing_signature, signature);
3027 	} else {
3028 		xmlAddChild(node, signature);
3029 	}
3030 
3031 	/* choose a digest for handling references based on the chosen signature algorithm */
3032 	{
3033 		xmlSecTransformId digest_method_id;
3034 		switch (context.signature_method) {
3035 			case LASSO_SIGNATURE_METHOD_RSA_SHA1:
3036 			case LASSO_SIGNATURE_METHOD_DSA_SHA1:
3037 			case LASSO_SIGNATURE_METHOD_HMAC_SHA1:
3038 				digest_method_id = xmlSecTransformSha1Id;
3039 				break;
3040 			case LASSO_SIGNATURE_METHOD_RSA_SHA256:
3041 			case LASSO_SIGNATURE_METHOD_HMAC_SHA256:
3042 				digest_method_id = xmlSecTransformSha256Id;
3043 				break;
3044 			case LASSO_SIGNATURE_METHOD_RSA_SHA384:
3045 			case LASSO_SIGNATURE_METHOD_HMAC_SHA384:
3046 				digest_method_id = xmlSecTransformSha384Id;
3047 				break;
3048 			case LASSO_SIGNATURE_METHOD_RSA_SHA512:
3049 			case LASSO_SIGNATURE_METHOD_HMAC_SHA512:
3050 				digest_method_id = xmlSecTransformSha384Id;
3051 				break;
3052 			default:
3053 				g_assert_not_reached();
3054 		}
3055 		/* Normally the signature is son of the signed node, which holds an Id attribute, but in
3056 		 * other cases, set snippet->offset to 0 and use xmlSecTmpSignatureAddReference from another
3057 		 * node get_xmlNode virtual method to add the needed reference.
3058 		 */
3059 		uri = g_strdup_printf("#%s", id);
3060 		reference = xmlSecTmplSignatureAddReference(signature, digest_method_id, NULL,
3061 				(xmlChar*)uri, NULL);
3062 		lasso_release(uri);
3063 	}
3064 
3065 	/* add enveloped transform */
3066 	xmlSecTmplReferenceAddTransform(reference, xmlSecTransformEnvelopedId);
3067 	/* add exclusive C14N transform */
3068 	xmlSecTmplReferenceAddTransform(reference, xmlSecTransformExclC14NId);
3069 	/* if the key is the public part of an asymetric key, add its certificate or the key itself */
3070 	switch (context.signature_method) {
3071 		case LASSO_SIGNATURE_METHOD_RSA_SHA1:
3072 		case LASSO_SIGNATURE_METHOD_DSA_SHA1:
3073 		case LASSO_SIGNATURE_METHOD_RSA_SHA256:
3074 		case LASSO_SIGNATURE_METHOD_RSA_SHA384:
3075 		case LASSO_SIGNATURE_METHOD_RSA_SHA512:
3076 			/* asymetric cryptography methods */
3077 			key_info = xmlSecTmplSignatureEnsureKeyInfo(signature, NULL);
3078 			if (xmlSecKeyGetData(context.signature_key, xmlSecOpenSSLKeyDataX509Id)) {
3079 				/* add <dsig:KeyInfo/> */
3080 				xmlSecTmplKeyInfoAddX509Data(key_info);
3081 			} else {
3082 				xmlSecTmplKeyInfoAddKeyValue(key_info);
3083 			}
3084 			break;
3085 		case LASSO_SIGNATURE_METHOD_HMAC_SHA1:
3086 		case LASSO_SIGNATURE_METHOD_HMAC_SHA256:
3087 		case LASSO_SIGNATURE_METHOD_HMAC_SHA384:
3088 		case LASSO_SIGNATURE_METHOD_HMAC_SHA512:
3089 			if (context.signature_key->name) {
3090 				key_info = xmlSecTmplSignatureEnsureKeyInfo(signature, NULL);
3091 				xmlSecTmplKeyInfoAddKeyName(key_info, NULL);
3092 
3093 			}
3094 			break;
3095 		default:
3096 			g_assert_not_reached();
3097 	}
3098 }
3099 
3100 
3101 /**
3102  * lasso_get_saml_message:
3103  * @query_fields: a NULL terminated array of char* representing a parsed query string.
3104  *
3105  * Return the first SAMLRequest or SAMLResponse value in the query string array.
3106  */
3107 static char*
lasso_get_saml_message(xmlChar ** query_fields)3108 lasso_get_saml_message(xmlChar **query_fields) {
3109 	int i = 0;
3110 	char *enc = NULL;
3111 	char *message = NULL;
3112 	char *saml_message = NULL;
3113 	char *decoded_message = NULL;
3114 	xmlChar *field = NULL;
3115 	char *t = NULL;
3116 	int rc = 0;
3117 	int len = 0;
3118 
3119 	for (i=0; (field=query_fields[i]); i++) {
3120 		t = strchr((char*)field, '=');
3121 		if (t == NULL)
3122 			continue;
3123 		*t = 0;
3124 		if (strcmp((char*)field, LASSO_SAML2_FIELD_ENCODING) == 0) {
3125 			enc = t+1;
3126 			continue;
3127 		}
3128 		if (strcmp((char*)field, LASSO_SAML2_FIELD_REQUEST) == 0 || strcmp((char*)field, LASSO_SAML2_FIELD_RESPONSE) == 0) {
3129 			message = t+1;
3130 			continue;
3131 		}
3132 	}
3133 	if (message == NULL) {
3134 		return NULL;
3135 	}
3136 	if (enc && strcmp(enc, LASSO_SAML2_DEFLATE_ENCODING) != 0) {
3137 		/* unknown encoding */
3138 		debug("Unknown URL encoding: %64s", enc);
3139 		return NULL;
3140 	}
3141 	len = strlen(message);
3142 	decoded_message = g_malloc(len);
3143 	if (! is_base64(message)) {
3144 		debug("message is not base64");
3145 		goto cleanup;
3146 	}
3147 	rc = xmlSecBase64Decode((xmlChar*)message, (xmlChar*)decoded_message, len);
3148 	if (rc < 0) {
3149 		debug("could not decode redirect SAML message");
3150 		goto cleanup;
3151 	}
3152 	/* rc contains the length of the result */
3153 	saml_message = (char*)lasso_inflate((unsigned char*) decoded_message, rc);
3154 cleanup:
3155 	if (decoded_message) {
3156 		lasso_release(decoded_message);
3157 	}
3158 	return saml_message;
3159 }
3160 
3161 /**
3162  * lasso_xmltextreader_from_message:
3163  * @message: the HTTP query, POST content or SOAP message
3164  *
3165  * Try to parse the passed message and create an xmlTextReader from it.
3166  */
3167 xmlTextReader *
lasso_xmltextreader_from_message(const char * message,char ** to_free)3168 lasso_xmltextreader_from_message(const char *message, char **to_free) {
3169 	size_t len = strlen(message);
3170 	char *needle;
3171 	xmlChar **query_fields = NULL;
3172 	char *decoded_message = NULL;
3173 	xmlTextReader *reader = NULL;
3174 
3175 	g_assert(to_free);
3176 	/* Differentiate SOAP from others */
3177 	if (message[0] != '<') {
3178 		needle = strchr(message, '=');
3179 		/* Differentiate redirect binding from POST */
3180 		if (needle && message[len-1] != '=') {
3181 			query_fields = lasso_urlencoded_to_strings(message);
3182 			message = *to_free = lasso_get_saml_message(query_fields);
3183 			if (! message) {
3184 				goto cleanup;
3185 			}
3186 			len = strlen(message);
3187 		} else { /* POST */
3188 			int rc = 0;
3189 
3190 			if (! is_base64(message)) {
3191 				debug("POST message is not base64");
3192 				goto cleanup;
3193 			}
3194 			decoded_message = g_malloc(len);
3195 			rc = xmlSecBase64Decode((xmlChar*)message, (xmlChar*)decoded_message, len);
3196 			if (rc < 0) {
3197 				debug("could not decode POST SAML message");
3198 				goto cleanup;
3199 			}
3200 			len = rc;
3201 			decoded_message[len] = '\0';
3202 			message = *to_free = decoded_message;
3203 			decoded_message = NULL;
3204 		}
3205 	}
3206 
3207 	if (message[0] == '<') // XML case
3208 		reader = xmlReaderForMemory(message, len, "", NULL, XML_PARSE_NONET);
3209 
3210 cleanup:
3211 	if (query_fields)
3212 		lasso_release_array_of_xml_strings(query_fields);
3213 	if (decoded_message)
3214 		lasso_release_string(decoded_message);
3215 	return reader;
3216 }
3217