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 #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