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 #include "../xml/private.h"
25 #include <xmlsec/base64.h>
26 
27 #include "../utils.h"
28 #include "providerprivate.h"
29 #include "profileprivate.h"
30 #include "profile.h"
31 #include "provider.h"
32 
33 #include "../id-ff/providerprivate.h"
34 #include "../id-ff/profile.h"
35 #include "../id-ff/profileprivate.h"
36 #include "../id-ff/serverprivate.h"
37 #include "../id-ff/sessionprivate.h"
38 #include "../id-ff/login.h"
39 
40 #include "../xml/private.h"
41 #include "../xml/soap-1.1/soap_envelope.h"
42 #include "../xml/saml-2.0/samlp2_request_abstract.h"
43 #include "../xml/saml-2.0/samlp2_artifact_resolve.h"
44 #include "../xml/saml-2.0/samlp2_artifact_response.h"
45 #include "../xml/saml-2.0/samlp2_authn_request.h"
46 #include "../xml/saml-2.0/samlp2_name_id_mapping_response.h"
47 #include "../xml/saml-2.0/samlp2_status_response.h"
48 #include "../xml/saml-2.0/samlp2_response.h"
49 #include "../xml/saml-2.0/saml2_assertion.h"
50 #include "../xml/saml-2.0/saml2_xsd.h"
51 #include "../xml/soap-1.1/soap_envelope.h"
52 #include "../xml/misc_text_node.h"
53 #include "../utils.h"
54 #include "../debug.h"
55 
56 
57 
58 static char* lasso_saml20_profile_build_artifact(LassoProvider *provider);
59 static int lasso_saml20_profile_export_to_query(LassoProfile *profile, LassoNode *msg, char **query,
60 		LassoSignatureContext context);
61 static gint lasso_profile_saml20_build_artifact_get_request_msg(LassoProfile *profile,
62 		const char *service);
63 static gint lasso_profile_saml20_build_artifact_post_request_msg(LassoProfile *profile,
64 		const char *service);
65 static gint lasso_profile_saml20_build_artifact_get_response_msg(LassoProfile *profile,
66 		const char *service);
67 static gint lasso_profile_saml20_build_artifact_post_response_msg(LassoProfile *profile,
68 		const char *service);
69 static char* lasso_saml20_profile_generate_artifact(LassoProfile *profile, int part);
70 
71 #define check_msg_body \
72 	if (! profile->msg_body) { \
73 		return critical_error(LASSO_PROFILE_ERROR_BUILDING_MESSAGE_FAILED); \
74 	}
75 
76 /*
77  * Helper functions
78  */
79 static int
get_provider(LassoProfile * profile,LassoProvider ** provider_out)80 get_provider(LassoProfile *profile, LassoProvider **provider_out)
81 {
82 	LassoProvider *provider;
83 	LassoServer *server;
84 	int rc = 0;
85 
86 	lasso_bad_param(PROFILE, profile);
87 
88 	lasso_extract_node_or_fail(server, profile->server, SERVER,
89 			LASSO_PROFILE_ERROR_MISSING_SERVER);
90 	provider = lasso_server_get_provider(server, profile->remote_providerID);
91 	if (! provider) {
92 		return LASSO_SERVER_ERROR_PROVIDER_NOT_FOUND;
93 	}
94 
95 	*provider_out = provider;
96 cleanup:
97 	return rc;
98 }
99 
100 static char *
get_url(LassoProvider * provider,const char * service,const char * binding)101 get_url(LassoProvider *provider, const char *service, const char *binding)
102 {
103 	char *meta;
104 	char *result;
105 
106 	meta = g_strdup_printf("%s %s", service, binding);
107 	result = lasso_provider_get_metadata_one(provider, meta);
108 	lasso_release_string(meta);
109 	return result;
110 }
111 
112 static char *
get_response_url(LassoProvider * provider,const char * service,const char * binding)113 get_response_url(LassoProvider *provider, const char *service, const char *binding)
114 {
115 	char *meta;
116 	char *result;
117 
118 	meta = g_strdup_printf("%s %s ResponseLocation", service, binding);
119 	result = lasso_provider_get_metadata_one(provider, meta);
120 	lasso_release_string(meta);
121 	if (! result) {
122 		result = get_url(provider, service, binding);
123 	}
124 	return result;
125 }
126 
127 static const char*
http_method_to_binding(LassoHttpMethod method)128 http_method_to_binding(LassoHttpMethod method) {
129 	switch (method) {
130 		case LASSO_HTTP_METHOD_POST:
131 			return "HTTP-POST";
132 		case LASSO_HTTP_METHOD_REDIRECT:
133 			return "HTTP-Redirect";
134 		case LASSO_HTTP_METHOD_SOAP:
135 			return "SOAP";
136 		case LASSO_HTTP_METHOD_ARTIFACT_GET:
137 		case LASSO_HTTP_METHOD_ARTIFACT_POST:
138 			return "HTTP-Artifact";
139 		case LASSO_HTTP_METHOD_PAOS:
140 			return "PAOS";
141 		default:
142 			return "";
143 	}
144 }
145 
146 /*
147  * Artifact Handling functions
148  */
149 
150 /**
151  * lasso_saml20_profile_generate_artifact
152  * @profile: a #LassoProfile
153  * @part: 0 for request, 1 for response
154  *
155  * Generates an artifact for current request or response and sets @profile
156  * attributes accordingly.
157  *
158  * Return value: the generated artifact (internally allocated, don't free)
159  **/
160 static char*
lasso_saml20_profile_generate_artifact(LassoProfile * profile,int part)161 lasso_saml20_profile_generate_artifact(LassoProfile *profile, int part)
162 {
163 	LassoNode *what = NULL;
164 	lasso_assign_new_string(profile->private_data->artifact,
165 			lasso_saml20_profile_build_artifact(&profile->server->parent));
166 	if (part == 0) {
167 		what = profile->request;
168 	} else if (part == 1) {
169 		what = profile->response;
170 	} else {
171 		/* XXX: RequestDenied here? */
172 	}
173 	/* Remove signature at the response level, if needed if will be on the ArtifactResponse */
174 	lasso_node_remove_signature(what);
175 	/* Keep an XML copy of the response for later retrieval */
176 	lasso_assign_new_string(profile->private_data->artifact_message,
177 			lasso_node_export_to_xml(what));
178 
179 	return profile->private_data->artifact;
180 }
181 
182 
183 static char*
lasso_saml20_profile_build_artifact(LassoProvider * provider)184 lasso_saml20_profile_build_artifact(LassoProvider *provider)
185 {
186 	xmlSecByte samlArt[44], *b64_samlArt = NULL;
187 	char *source_succinct_id = NULL;
188 	char *ret = NULL;
189 	unsigned short index;
190 
191 	source_succinct_id = lasso_sha1(provider->ProviderID);
192 	/* XXX: unchecked return value*/
193 	goto_cleanup_if_fail(lasso_saml20_provider_get_artifact_resolution_service_index(provider,
194 				&index) == 0);
195 	/* Artifact Format is described in saml-bindings-2.0-os, 3.6.4.2. */
196 	memcpy(samlArt, "\000\004", 2); /* type code */
197 	samlArt[2] = 0xFF & (index >> 8);
198 	samlArt[3] = 0xFF & index;
199 	memcpy(samlArt+4, source_succinct_id, 20);
200 	lasso_build_random_sequence((char*)samlArt+24, 20);
201 
202 	b64_samlArt = xmlSecBase64Encode(samlArt, 44, 0);
203 
204 	ret = g_strdup((char*)b64_samlArt);
205 cleanup:
206 	if (ret == NULL) {
207 		warning("Unable to find an artifact resolution service for entity id %s with %d",
208 				provider->ProviderID, provider->role);
209 	}
210 	lasso_release_string(source_succinct_id);
211 	lasso_release_xml_string(b64_samlArt);
212 
213 	return ret;
214 }
215 
216 /*
217  * this function factorize all case for producing SAML artifact messages
218  */
219 static gint
lasso_profile_saml20_build_artifact_msg(LassoProfile * profile,const char * url,int request_or_response,int get_or_post)220 lasso_profile_saml20_build_artifact_msg(LassoProfile *profile,
221 		const char *url, int request_or_response, int get_or_post)
222 {
223 	char *artifact = lasso_saml20_profile_generate_artifact(profile, request_or_response);
224 
225 	if (artifact == NULL) {
226 		return critical_error(LASSO_PROFILE_ERROR_BUILDING_QUERY_FAILED);
227 	}
228 	/* hack... */
229 	if (LASSO_IS_LOGIN(profile)) {
230 		LassoLogin *login = (LassoLogin*)profile;
231 		lasso_assign_string(login->assertionArtifact, artifact);
232 	}
233 
234 	if (get_or_post == 0) {
235 		char *query;
236 		if (profile->msg_relayState) {
237 			query = lasso_url_add_parameters(NULL, 0, LASSO_SAML2_FIELD_ARTIFACT, artifact, "RelayState",
238 								profile->msg_relayState, NULL);
239 		} else {
240 			query = lasso_url_add_parameters(NULL, 0, LASSO_SAML2_FIELD_ARTIFACT, artifact, NULL);
241 		}
242 		lasso_assign_new_string(profile->msg_url,
243 			lasso_concat_url_query(url, query));
244 		lasso_release_string(query);
245 	} else {
246 		lasso_assign_string(profile->msg_url, url);
247 		lasso_assign_string(profile->msg_body, artifact);
248 	}
249 	return 0;
250 }
251 
252 enum {
253  REQUEST = 0,
254  RESPONSE = 1,
255  GET = 0,
256  POST = 1
257 };
258 
259 static gint
lasso_profile_saml20_build_artifact_get_request_msg(LassoProfile * profile,const char * url)260 lasso_profile_saml20_build_artifact_get_request_msg(LassoProfile *profile, const char *url)
261 {
262 	return lasso_profile_saml20_build_artifact_msg(profile, url, REQUEST, GET);
263 }
264 
265 static gint
lasso_profile_saml20_build_artifact_post_request_msg(LassoProfile * profile,const char * url)266 lasso_profile_saml20_build_artifact_post_request_msg(LassoProfile *profile, const char *url)
267 {
268 	return lasso_profile_saml20_build_artifact_msg(profile, url, REQUEST, POST);
269 }
270 
271 static gint
lasso_profile_saml20_build_artifact_get_response_msg(LassoProfile * profile,const char * url)272 lasso_profile_saml20_build_artifact_get_response_msg(LassoProfile *profile, const char *url)
273 {
274 	return lasso_profile_saml20_build_artifact_msg(profile, url, RESPONSE, GET);
275 }
276 
277 static gint
lasso_profile_saml20_build_artifact_post_response_msg(LassoProfile * profile,const char * url)278 lasso_profile_saml20_build_artifact_post_response_msg(LassoProfile *profile, const char *url)
279 {
280 	return lasso_profile_saml20_build_artifact_msg(profile, url, RESPONSE, POST);
281 }
282 
283 int
lasso_saml20_profile_init_artifact_resolve(LassoProfile * profile,LassoProviderRole remote_provider_role,const char * msg,LassoHttpMethod method)284 lasso_saml20_profile_init_artifact_resolve(LassoProfile *profile,
285 		LassoProviderRole remote_provider_role, const char *msg, LassoHttpMethod method)
286 {
287 	xmlChar **query_fields;
288 	char *artifact_b64 = NULL;
289 	xmlChar *provider_succinct_id_b64 = NULL;
290 	char *provider_succinct_id[21];
291 	char artifact[45];
292 	LassoSamlp2RequestAbstract *request = NULL;
293 	LassoProvider *remote_provider = NULL;
294 	int i = 0;
295 	int rc = 0;
296 	unsigned short index_endpoint = 0;
297 
298 	if (method == LASSO_HTTP_METHOD_ARTIFACT_GET) {
299 		query_fields = lasso_urlencoded_to_strings(msg);
300 		for (i=0; query_fields[i]; i++) {
301 			if (strncmp((char*)query_fields[i], LASSO_SAML2_FIELD_ARTIFACT "=", 8) == 0) {
302 				lasso_assign_string(artifact_b64, (char*)query_fields[i]+8);
303 			}
304 		}
305 		lasso_release_array_of_xml_strings(query_fields);
306 		if (artifact_b64 == NULL) {
307 			return LASSO_PROFILE_ERROR_MISSING_ARTIFACT;
308 		}
309 	} else if (method == LASSO_HTTP_METHOD_ARTIFACT_POST) {
310 		artifact_b64 = g_strdup(msg);
311 	} else {
312 		return critical_error(LASSO_PROFILE_ERROR_INVALID_HTTP_METHOD);
313 	}
314 
315 	i = xmlSecBase64Decode((xmlChar*)artifact_b64, (xmlChar*)artifact, 45);
316 	if (i < 0 || i > 44) {
317 		lasso_release_string(artifact_b64);
318 		return LASSO_PROFILE_ERROR_INVALID_ARTIFACT;
319 	}
320 
321 	if (artifact[0] != 0 || artifact[1] != 4) { /* wrong type code */
322 		lasso_release_string(artifact_b64);
323 		return LASSO_PROFILE_ERROR_INVALID_ARTIFACT;
324 	}
325 
326 	memcpy(provider_succinct_id, artifact+4, 20);
327 	provider_succinct_id[20] = 0;
328 
329 	provider_succinct_id_b64 = xmlSecBase64Encode((xmlChar*)provider_succinct_id, 20, 0);
330 
331 	lasso_assign_new_string(profile->remote_providerID, lasso_server_get_providerID_from_hash(
332 			profile->server, (char*)provider_succinct_id_b64));
333 	lasso_release_xml_string(provider_succinct_id_b64);
334 	if (profile->remote_providerID == NULL) {
335 		return LASSO_SERVER_ERROR_PROVIDER_NOT_FOUND;
336 	}
337 
338 	/* resolve the resolver url using the endpoint index in the artifact string */
339 	remote_provider = lasso_server_get_provider(profile->server, profile->remote_providerID);
340 	index_endpoint = (artifact[2] << 16) + artifact[3];
341 	lasso_assign_string(profile->msg_url, lasso_saml20_provider_get_endpoint_url(remote_provider,
342 			remote_provider_role,
343 			LASSO_SAML2_METADATA_ELEMENT_ARTIFACT_RESOLUTION_SERVICE, NULL, FALSE,
344 			FALSE, index_endpoint));
345 	if (! profile->msg_url) {
346 		debug("looking for index endpoint %d", index_endpoint);
347 		return LASSO_PROFILE_ERROR_ENDPOINT_INDEX_NOT_FOUND;
348 	}
349 
350 
351 	lasso_assign_new_gobject(profile->request, lasso_samlp2_artifact_resolve_new());
352 	request = LASSO_SAMLP2_REQUEST_ABSTRACT(profile->request);
353 	lasso_assign_new_string(LASSO_SAMLP2_ARTIFACT_RESOLVE(request)->Artifact, artifact_b64);
354 	request->ID = lasso_build_unique_id(32);
355 	lasso_assign_string(request->Version, "2.0");
356 	request->Issuer = LASSO_SAML2_NAME_ID(lasso_saml2_name_id_new_with_string(
357 			LASSO_PROVIDER(profile->server)->ProviderID));
358 	request->IssueInstant = lasso_get_current_time();
359 
360 	lasso_check_good_rc(lasso_profile_saml20_setup_message_signature(profile,
361 				(LassoNode*)request));
362 
363 cleanup:
364 	return rc;
365 }
366 
367 int
lasso_saml20_profile_process_artifact_resolve(LassoProfile * profile,const char * msg)368 lasso_saml20_profile_process_artifact_resolve(LassoProfile *profile, const char *msg)
369 {
370 	LassoProvider *remote_provider;
371 	int rc = 0;
372 	LassoProfileSignatureVerifyHint sig_verify_hint;
373 
374 	/* FIXME: parse only one time the message, reuse the parsed document for signature
375 	 * validation */
376 	lasso_assign_new_gobject(profile->request, lasso_node_new_from_soap(msg));
377 	if (profile->request == NULL) {
378 		return critical_error(LASSO_PROFILE_ERROR_INVALID_MSG);
379 	}
380 	if (! LASSO_IS_SAMLP2_ARTIFACT_RESOLVE(profile->request)) {
381 		return critical_error(LASSO_PROFILE_ERROR_INVALID_MSG);
382 	}
383 	lasso_assign_string(profile->private_data->artifact,
384 			LASSO_SAMLP2_ARTIFACT_RESOLVE(profile->request)->Artifact);
385 
386 	sig_verify_hint = lasso_profile_get_signature_verify_hint(profile);
387 
388 	lasso_assign_string(profile->remote_providerID, LASSO_SAMLP2_REQUEST_ABSTRACT(
389 			profile->request)->Issuer->content);
390 	remote_provider = lasso_server_get_provider(profile->server, profile->remote_providerID);
391 
392 	goto_cleanup_if_fail_with_rc(remote_provider, LASSO_PROFILE_ERROR_UNKNOWN_PROVIDER);
393 
394 	if (sig_verify_hint != LASSO_PROFILE_SIGNATURE_VERIFY_HINT_IGNORE) {
395 		profile->signature_status = lasso_provider_verify_signature(remote_provider, msg, "ID",
396 				LASSO_MESSAGE_FORMAT_SOAP);
397 	}
398 
399 	switch (lasso_profile_get_signature_verify_hint(profile)) {
400 		case LASSO_PROFILE_SIGNATURE_VERIFY_HINT_MAYBE:
401 		case LASSO_PROFILE_SIGNATURE_VERIFY_HINT_FORCE:
402 			rc = profile->signature_status;
403 			break;
404 		case LASSO_PROFILE_SIGNATURE_VERIFY_HINT_IGNORE:
405 			break;
406 		default:
407 			g_assert(0);
408 			break;
409 	}
410 
411 cleanup:
412 	return rc;
413 }
414 
415 int
lasso_saml20_profile_build_artifact_response(LassoProfile * profile)416 lasso_saml20_profile_build_artifact_response(LassoProfile *profile)
417 {
418 	LassoSamlp2StatusResponse *response = NULL;
419 	int rc = 0;
420 
421 	if ( ! LASSO_IS_SAMLP2_REQUEST_ABSTRACT(profile->request)) {
422 		return LASSO_PROFILE_ERROR_MISSING_REQUEST;
423 	}
424 	/* Setup the response */
425 	response = LASSO_SAMLP2_STATUS_RESPONSE(lasso_samlp2_artifact_response_new());
426 	lasso_assign_new_gobject(profile->response, response);
427 	response->ID = lasso_build_unique_id(32);
428 	lasso_assign_string(response->Version, "2.0");
429 	response->Issuer = LASSO_SAML2_NAME_ID(lasso_saml2_name_id_new_with_string(
430 			LASSO_PROVIDER(profile->server)->ProviderID));
431 	response->IssueInstant = lasso_get_current_time();
432 	lasso_assign_string(response->InResponseTo, LASSO_SAMLP2_REQUEST_ABSTRACT(profile->request)->ID);
433 	/* Add content */
434 	if (profile->private_data->artifact_message) {
435 		xmlDoc *doc;
436 		xmlNode *node;
437 		char *content = profile->private_data->artifact_message;
438 		doc = lasso_xml_parse_memory(content, strlen(content));
439 		if (doc) {
440 			node = xmlDocGetRootElement(doc);
441 			lasso_assign_new_gobject(LASSO_SAMLP2_ARTIFACT_RESPONSE(response)->any,
442 					lasso_misc_text_node_new_with_xml_node(node));
443 			lasso_release_doc(doc);
444 			lasso_saml20_profile_set_response_status(profile,
445 					LASSO_SAML2_STATUS_CODE_SUCCESS, NULL);
446 		} else {
447 			lasso_saml20_profile_set_response_status(profile,
448 					LASSO_SAML2_STATUS_CODE_RESPONDER,
449 					LASSO_PRIVATE_STATUS_CODE_FAILED_TO_RESTORE_ARTIFACT);
450 		}
451 	} else {
452 		/* if no artifact is present, it is a success anyway */
453 		lasso_saml20_profile_set_response_status(profile,
454 				LASSO_SAML2_STATUS_CODE_SUCCESS, NULL);
455 	}
456 	/* Setup the signature */
457 	lasso_check_good_rc(lasso_profile_saml20_setup_message_signature(profile,
458 				(LassoNode*)response));
459 	/* Serialize the message */
460 	lasso_assign_new_string(profile->msg_body, lasso_node_export_to_soap(profile->response));
461 cleanup:
462 	return rc;
463 }
464 
465 int
lasso_saml20_profile_process_artifact_response(LassoProfile * profile,const char * msg)466 lasso_saml20_profile_process_artifact_response(LassoProfile *profile, const char *msg)
467 {
468 	LassoSamlp2ArtifactResponse *artifact_response;
469 	int rc = 0;
470 
471 	artifact_response = (LassoSamlp2ArtifactResponse*)lasso_samlp2_artifact_response_new();
472 	lasso_check_good_rc(lasso_saml20_profile_process_any_response(profile,
473 				&artifact_response->parent, NULL, msg));
474 	/* XXX: check signature status */
475 	goto_cleanup_if_fail_with_rc(profile->response != NULL,
476 			critical_error(LASSO_PROFILE_ERROR_INVALID_RESPONSE));
477 	if (artifact_response->any == NULL) {
478 		rc = LASSO_PROFILE_ERROR_MISSING_RESPONSE;
479 	} else {
480 		if (LASSO_IS_SAMLP2_REQUEST_ABSTRACT(artifact_response->any)) {
481 			lasso_assign_gobject(profile->request, artifact_response->any);
482 		} else if (LASSO_IS_SAMLP2_STATUS_RESPONSE(artifact_response->any)) {
483 			lasso_assign_gobject(profile->response, artifact_response->any);
484 		} else {
485 			rc = LASSO_PROFILE_ERROR_INVALID_RESPONSE;
486 		}
487 	}
488 
489 cleanup:
490 	lasso_release_gobject(artifact_response);
491 	return rc;
492 }
493 
494 /**
495  * lasso_saml20_profile_is_saml_query:
496  * @query: HTTP query string
497  *
498  * Tests the query string to know if the URL is called as the result of a
499  * SAML redirect (action initiated elsewhere) or not.
500  *
501  * Return value: TRUE if SAML query, FALSE otherwise
502  **/
503 gboolean
lasso_profile_is_saml_query(const gchar * query)504 lasso_profile_is_saml_query(const gchar *query)
505 {
506 	gchar *parameters[] = {
507 		LASSO_SAML2_FIELD_REQUEST "=", LASSO_SAML2_FIELD_RESPONSE "=",
508 		LASSO_SAML2_FIELD_ARTIFACT "=", NULL };
509 	gint i;
510 
511 	g_return_val_if_fail(query, FALSE);
512 	for (i=0; parameters[i]; i++) {
513 		if (strstr(query, parameters[i]))
514 			return TRUE;
515 	}
516 
517 	return FALSE;
518 }
519 
520 static void
lasso_saml20_profile_set_session_from_dump_decrypt(LassoSaml2Assertion * assertion,LassoProfile * profile)521 lasso_saml20_profile_set_session_from_dump_decrypt(
522 		LassoSaml2Assertion *assertion, LassoProfile *profile)
523 {
524 	if (LASSO_IS_SAML2_ASSERTION(assertion) == FALSE) {
525 		return;
526 	}
527 
528 	if (assertion->Subject != NULL && ! assertion->Subject->NameID && assertion->Subject->EncryptedID != NULL) {
529 		if (assertion->Subject->EncryptedID->original_data) { /* already decrypted */
530 			lasso_assign_gobject(assertion->Subject->NameID,
531 				assertion->Subject->EncryptedID->original_data);
532 		lasso_release_gobject(assertion->Subject->EncryptedID);
533 		} else { /* decrypt */
534 			int rc;
535 			GList *encryption_private_keys =
536 				lasso_server_get_encryption_private_keys(profile->server);
537 
538 			rc = LASSO_PROFILE_ERROR_MISSING_ENCRYPTION_PRIVATE_KEY;
539 			lasso_foreach_full_begin(xmlSecKey*, encryption_private_key, it,
540 					encryption_private_keys);
541 			{
542 				rc = lasso_saml2_encrypted_element_decrypt(
543 						assertion->Subject->EncryptedID,
544 						encryption_private_key,
545 						(LassoNode**)&assertion->Subject->NameID);
546 				if (rc == 0)
547 					break;
548 			}
549 			lasso_foreach_full_end();
550 
551 			if (rc == 0) {
552 				lasso_release_gobject(assertion->Subject->EncryptedID);
553 			} else {
554 				message(G_LOG_LEVEL_WARNING, "Could not decrypt EncrypteID from"
555 						" assertion in session dump: %s", lasso_strerror(rc));
556 			}
557 		}
558 	}
559 }
560 
561 gint
lasso_saml20_profile_set_session_from_dump(LassoProfile * profile)562 lasso_saml20_profile_set_session_from_dump(LassoProfile *profile)
563 {
564 	GList *assertions = NULL;
565 
566 	lasso_bad_param(PROFILE, profile);
567 
568 	if (lasso_session_count_assertions(profile->session) > 0) {
569 		assertions = lasso_session_get_assertions(profile->session, NULL);
570 
571 		g_list_foreach(assertions,
572 				(GFunc)lasso_saml20_profile_set_session_from_dump_decrypt,
573 				profile);
574 		lasso_release_list(assertions);
575 	}
576 
577 	return 0;
578 }
579 
580 /**
581  * lasso_saml20_profile_process_name_identifier_decryption:
582  * @profile: the #LassoProfile object
583  * @name_id: the field containing the #LassoSaml2NameID object
584  * @encrypted_id: the field containing an encrypted #LassoSaml2NameID as a
585  * #LassoSaml2EncryptedElement
586  *
587  * Place content of the NameID in the profile nameIdentifier field, if no NameID is present but an
588  * EncryptedElement is, then decrypt it, store it in place of the name_id field and in the
589  * nameIdentifier field of the profile.
590  *
591  * Return value: 0 if successful,
592  * LASSO_PROFILE_ERROR_MISSING_NAME_IDENTIFIER if no NameID can be found,
593  * LASSO_PROFILE_ERROR_MISSING_ENCRYPTION_PRIVATE_KEY if an encryption element is present but no no
594  * decryption key.
595  */
596 gint
lasso_saml20_profile_process_name_identifier_decryption(LassoProfile * profile,LassoSaml2NameID ** name_id,LassoSaml2EncryptedElement ** encrypted_id)597 lasso_saml20_profile_process_name_identifier_decryption(LassoProfile *profile,
598 		LassoSaml2NameID **name_id,
599 		LassoSaml2EncryptedElement **encrypted_id)
600 {
601 	int rc = 0;
602 
603 	lasso_bad_param(PROFILE, profile);
604 	lasso_null_param(name_id);
605 	lasso_null_param(encrypted_id);
606 
607 	if (*name_id == NULL && *encrypted_id != NULL) {
608 		if (! LASSO_IS_SAML2_ENCRYPTED_ELEMENT(*encrypted_id)) {
609 			return LASSO_PROFILE_ERROR_MISSING_NAME_IDENTIFIER;
610 		}
611 		rc = LASSO_PROFILE_ERROR_MISSING_ENCRYPTION_PRIVATE_KEY;
612 		lasso_foreach_full_begin(xmlSecKey*, encryption_private_key, it,
613 				lasso_server_get_encryption_private_keys(profile->server));
614 		{
615 			rc = lasso_saml2_encrypted_element_decrypt(*encrypted_id, encryption_private_key,
616 					&profile->nameIdentifier);
617 			if (rc == 0)
618 				break;
619 		}
620 		lasso_foreach_full_end();
621 
622 		if (rc)
623 			goto cleanup;
624 		if (! LASSO_IS_SAML2_NAME_ID(profile->nameIdentifier)) {
625 			rc = LASSO_PROFILE_ERROR_MISSING_NAME_IDENTIFIER;
626 			goto cleanup;
627 		}
628 
629 		// swap the node contents
630 		lasso_assign_gobject(*name_id, LASSO_SAML2_NAME_ID(profile->nameIdentifier));
631 		lasso_release_gobject(*encrypted_id);
632 	} else {
633 		lasso_assign_gobject(profile->nameIdentifier, (LassoNode*)*name_id);
634 	}
635 cleanup:
636 	return rc;
637 }
638 
639 /*
640  * Request handling functions
641  */
642 
643 /**
644  * lasso_saml20_profile_process_any_request:
645  * @profile: a #LassoProfile object
646  * @request_node: a #LassoNode object which will be initialized with the content of @request_msg
647  * @request_msg: a string containing the request message as a SOAP XML message, a query string of
648  * the content of SAMLRequest POST field.
649  *
650  * Parse a request message, initialize the given node object with it, try to extract basic SAML
651  * profile information like the remote_provider_id or the name_id and validate the signature.
652  *
653  * Signature validation status is accessible in profile->signature_status, beware that if signature
654  * validation fails no error code will be returned, you must explicitely verify the
655  * profile->signature_status code.
656  *
657  * Return value: 0 if parsing is successful (even if signature validation fails), and otherwise,
658  * LASSO_PROFILE_ERROR_INVALID_MSG, LASSO_PROFILE_ERROR_UNSUPPORTED_PROFILE, *
659  * LASSO_SERVER_ERROR_PROVIDER_NOT_FOUND.
660  */
661 int
lasso_saml20_profile_process_any_request(LassoProfile * profile,LassoNode * request_node,const char * request_msg)662 lasso_saml20_profile_process_any_request(LassoProfile *profile,
663 		LassoNode *request_node,
664 		const char *request_msg)
665 {
666 	int rc = 0;
667 	LassoProvider *remote_provider = NULL;
668 	LassoSamlp2RequestAbstract *request_abstract = NULL;
669 	LassoMessageFormat format;
670 	xmlDoc *doc = NULL;
671 	xmlNode *content = NULL;
672 
673 	lasso_bad_param(PROFILE, profile);
674 
675 	/* reset signature_status */
676 	profile->signature_status = 0;
677 	format = lasso_node_init_from_message_with_format(request_node,
678 		request_msg, LASSO_MESSAGE_FORMAT_UNKNOWN, &doc, &content);
679 	if (format <= LASSO_MESSAGE_FORMAT_UNKNOWN) {
680 		rc = LASSO_PROFILE_ERROR_INVALID_MSG;
681 		goto cleanup;
682 	}
683 	switch (format) {
684 		case LASSO_MESSAGE_FORMAT_BASE64:
685 			profile->http_request_method = LASSO_HTTP_METHOD_POST;
686 			break;
687 		case LASSO_MESSAGE_FORMAT_SOAP:
688 			profile->http_request_method = LASSO_HTTP_METHOD_SOAP;
689 			break;
690 		case LASSO_MESSAGE_FORMAT_QUERY:
691 			profile->http_request_method = LASSO_HTTP_METHOD_REDIRECT;
692 			break;
693 		default:
694 			rc = LASSO_PROFILE_ERROR_UNSUPPORTED_PROFILE;
695 			goto cleanup;
696 	}
697 	lasso_assign_gobject(profile->request, request_node);
698 	if (format == LASSO_MESSAGE_FORMAT_QUERY) {
699 		lasso_assign_new_string(profile->msg_relayState,
700 			lasso_get_relaystate_from_query(request_msg));
701 	}
702 
703 	lasso_extract_node_or_fail(request_abstract, profile->request, SAMLP2_REQUEST_ABSTRACT,
704 			LASSO_PROFILE_ERROR_INVALID_MSG);
705 	goto_cleanup_if_fail_with_rc(LASSO_IS_SAML2_NAME_ID(request_abstract->Issuer),
706 			LASSO_PROFILE_ERROR_MISSING_ISSUER);
707 	lasso_assign_string(profile->remote_providerID, request_abstract->Issuer->content);
708 
709 	rc = get_provider(profile, &remote_provider);
710 	goto_cleanup_if_fail(rc == 0);
711 
712 	/* verify the signature at the request level */
713 	if (content && doc && format != LASSO_MESSAGE_FORMAT_QUERY) {
714 		profile->signature_status =
715 			lasso_provider_verify_saml_signature(remote_provider, content, doc);
716 	} else if (format == LASSO_MESSAGE_FORMAT_QUERY) {
717 		profile->signature_status =
718 			lasso_provider_verify_query_signature(remote_provider, request_msg);
719 	} else {
720 		profile->signature_status = LASSO_PROFILE_ERROR_CANNOT_VERIFY_SIGNATURE;
721 	}
722 
723 cleanup:
724 
725 	lasso_release_doc(doc);
726 	return rc;
727 }
728 
729 int
lasso_saml20_profile_process_soap_request(LassoProfile * profile,const char * request_msg)730 lasso_saml20_profile_process_soap_request(LassoProfile *profile,
731 		const char *request_msg)
732 {
733 	int rc = 0;
734 	LassoSaml2NameID *issuer = NULL;
735 	LassoProvider *remote_provider = NULL;
736 	LassoSamlp2RequestAbstract *request_abstract = NULL;
737 
738 	lasso_bad_param(PROFILE, profile);
739 
740 	profile->signature_status = 0;
741 	lasso_assign_new_gobject(profile->request, lasso_node_new_from_soap(request_msg));
742 	profile->http_request_method = LASSO_HTTP_METHOD_SOAP;
743 	lasso_extract_node_or_fail(request_abstract, profile->request, SAMLP2_REQUEST_ABSTRACT,
744 			LASSO_PROFILE_ERROR_INVALID_MSG);
745 	lasso_extract_node_or_fail(issuer, request_abstract->Issuer, SAML2_NAME_ID,
746 			LASSO_PROFILE_ERROR_MISSING_ISSUER);
747 	lasso_assign_string(profile->remote_providerID, issuer->content);
748 
749 	rc = get_provider(profile, &remote_provider);
750 	goto_cleanup_if_fail(rc == 0);
751 
752 	profile->signature_status = lasso_provider_verify_signature(
753 			remote_provider, request_msg, "ID", LASSO_MESSAGE_FORMAT_SOAP);
754 
755 	switch (lasso_profile_get_signature_verify_hint(profile)) {
756 		case LASSO_PROFILE_SIGNATURE_VERIFY_HINT_FORCE:
757 		case LASSO_PROFILE_SIGNATURE_VERIFY_HINT_MAYBE:
758 			rc = profile->signature_status;
759 			break;
760 		case LASSO_PROFILE_SIGNATURE_VERIFY_HINT_IGNORE:
761 			break;
762 		case LASSO_PROFILE_SIGNATURE_VERIFY_HINT_LAST:
763 			g_assert_not_reached();
764 			break;
765 	}
766 
767 cleanup:
768 	return rc;
769 }
770 
771 int
lasso_saml20_profile_init_request(LassoProfile * profile,const char * remote_provider_id,gboolean first_in_session,LassoSamlp2RequestAbstract * request_abstract,LassoHttpMethod http_method,LassoMdProtocolType protocol_type)772 lasso_saml20_profile_init_request(LassoProfile *profile,
773 		const char *remote_provider_id,
774 		gboolean first_in_session,
775 		LassoSamlp2RequestAbstract *request_abstract,
776 		LassoHttpMethod http_method,
777 		LassoMdProtocolType protocol_type)
778 {
779 	LassoServer *server = NULL;
780 	LassoSession *session = NULL;
781 	LassoProvider *remote_provider = NULL;
782 	LassoSaml2NameID *name_id = NULL;
783 	char *remote_provider_id_auto = NULL;
784 	int rc = 0;
785 
786 	lasso_bad_param(PROFILE, profile);
787 	lasso_bad_param(SAMLP2_REQUEST_ABSTRACT, request_abstract);
788 
789 	if (http_method != LASSO_HTTP_METHOD_ANY &&
790 			http_method != LASSO_HTTP_METHOD_REDIRECT &&
791 			http_method != LASSO_HTTP_METHOD_POST &&
792 			http_method != LASSO_HTTP_METHOD_ARTIFACT_GET &&
793 			http_method != LASSO_HTTP_METHOD_ARTIFACT_POST &&
794 			http_method != LASSO_HTTP_METHOD_SOAP &&
795 			http_method != LASSO_HTTP_METHOD_PAOS) {
796 		return critical_error(LASSO_PROFILE_ERROR_INVALID_HTTP_METHOD);
797 	}
798 
799 	/* verify server and session object */
800 	lasso_extract_node_or_fail(server, profile->server, SERVER,
801 			LASSO_PROFILE_ERROR_MISSING_SERVER);
802 	if (LASSO_IS_SESSION(profile->session)) {
803 		session = profile->session;
804 	}
805 
806 	/*
807 	 * With PAOS the ECP client determines the remote provider.
808 	 * Everthing in the following block of code depends on
809 	 * establishing the remote provider and subsequetnly operating
810 	 * on the remote provider.
811 	 */
812 	if (http_method != LASSO_HTTP_METHOD_PAOS) {
813 		/* set remote provider Id */
814 		if (! remote_provider_id) {
815 			if (first_in_session) {
816 				if (! session) {
817 					return LASSO_PROFILE_ERROR_SESSION_NOT_FOUND;
818 				}
819 				remote_provider_id_auto = lasso_session_get_provider_index(session, 0);
820 			} else {
821 				remote_provider_id_auto = lasso_server_get_first_providerID(server);
822 			}
823 		}
824 		if (! remote_provider_id && ! remote_provider_id_auto) {
825 			rc = LASSO_PROFILE_ERROR_CANNOT_FIND_A_PROVIDER;
826 			goto cleanup;
827 		}
828 		if (remote_provider_id) {
829 			lasso_assign_string(profile->remote_providerID, remote_provider_id);
830 		} else {
831 			lasso_assign_new_string(profile->remote_providerID, remote_provider_id_auto);
832 		}
833 		rc = get_provider(profile, &remote_provider);
834 		if (rc)
835 			goto cleanup;
836 		/* set the name identifier */
837 		name_id = (LassoSaml2NameID*)lasso_profile_get_nameIdentifier(profile);
838 		if (LASSO_IS_SAML2_NAME_ID(name_id)) {
839 			lasso_assign_gobject(profile->nameIdentifier, (LassoNode*)name_id);
840 		}
841 
842 		/* verify that this provider supports the current http method */
843 		if (http_method == LASSO_HTTP_METHOD_ANY) {
844 			http_method = lasso_saml20_provider_get_first_http_method((LassoProvider*)server,
845 					remote_provider, protocol_type);
846 		}
847 		if (http_method == LASSO_HTTP_METHOD_NONE) {
848 			rc = LASSO_PROFILE_ERROR_UNSUPPORTED_PROFILE;
849 			goto cleanup;
850 		}
851 		if (! lasso_saml20_provider_accept_http_method(
852 					(LassoProvider*)server,
853 					remote_provider,
854 					protocol_type,
855 					http_method,
856 					TRUE)) {
857 			rc = LASSO_PROFILE_ERROR_UNSUPPORTED_PROFILE;
858 		}
859 	}
860 	profile->http_request_method = http_method;
861 
862 	/* initialize request fields */
863 	lasso_assign_new_string(request_abstract->ID, lasso_build_unique_id(32));
864 	lasso_assign_string(request_abstract->Version, "2.0");
865 	lasso_assign_new_gobject(request_abstract->Issuer,
866 			LASSO_SAML2_NAME_ID(lasso_saml2_name_id_new_with_string(
867 					LASSO_PROVIDER(profile->server)->ProviderID)));
868 	lasso_assign_new_string(request_abstract->IssueInstant, lasso_get_current_time());
869 	lasso_assign_gobject(profile->request, LASSO_NODE(request_abstract));
870 
871 	/* set signature */
872 	lasso_check_good_rc(lasso_profile_saml20_setup_message_signature(profile, profile->request));
873 
874 cleanup:
875 	return rc;
876 }
877 
878 static int
lasso_saml20_profile_build_redirect_request_msg(LassoProfile * profile,const char * url)879 lasso_saml20_profile_build_redirect_request_msg(LassoProfile *profile, const char *url)
880 {
881 	return lasso_saml20_profile_build_http_redirect(profile,
882 			profile->request,
883 			url);
884 }
885 
886 static int
lasso_saml20_profile_build_post_request_msg(LassoProfile * profile,const char * url)887 lasso_saml20_profile_build_post_request_msg(LassoProfile *profile,
888 		const char *url)
889 {
890 	lasso_assign_string(profile->msg_url, url);
891 	lasso_assign_new_string(profile->msg_body,
892 			lasso_node_export_to_base64(profile->request));
893 	check_msg_body;
894 	return 0;
895 }
896 
897 static int
lasso_saml20_profile_build_soap_request_msg(LassoProfile * profile,const char * url)898 lasso_saml20_profile_build_soap_request_msg(LassoProfile *profile, const char *url)
899 {
900 	lasso_assign_string(profile->msg_url, url);
901 	lasso_assign_new_string(profile->msg_body,
902 			lasso_node_export_to_soap(profile->request));
903 	check_msg_body;
904 	return 0;
905 }
906 
907 /*
908  * the url parameters is special for this function, it does not give the destination of the message
909  * (it's implicit for the caller of this function) but where response should be posted later).
910  */
911 static int
lasso_profile_saml20_build_paos_request_msg(LassoProfile * profile,const char * url)912 lasso_profile_saml20_build_paos_request_msg(LassoProfile *profile, const char *url)
913 {
914 	int rc = 0;
915     LassoSamlp2AuthnRequest *request;
916 	LassoSamlp2IDPList *idp_list = NULL;
917 	char *message_id = NULL;
918 
919 	lasso_extract_node_or_fail(request, profile->request, SAMLP2_AUTHN_REQUEST,
920 							   LASSO_PROFILE_ERROR_MISSING_REQUEST);
921 
922 	if (lasso_profile_get_idp_list(profile)) {
923 		lasso_extract_node_or_fail(idp_list,
924 								   lasso_profile_get_idp_list(profile),
925 								   SAMLP2_IDP_LIST,
926 								   LASSO_PROFILE_ERROR_INVALID_IDP_LIST);
927 	}
928 
929 	message_id = lasso_profile_get_message_id(profile);
930 
931 	lasso_assign_new_string(profile->msg_body,
932 			lasso_node_export_to_paos_request_full(profile->request,
933 												   profile->server->parent.ProviderID, url,
934 												   message_id,
935 												   profile->msg_relayState,
936 												   request->IsPassive, request->ProviderName,
937 												   idp_list));
938 
939 	check_msg_body;
940 
941 cleanup:
942 	lasso_release_string(message_id);
943 	return rc;
944 }
945 
946 int
lasso_saml20_profile_build_request_msg(LassoProfile * profile,const char * service,LassoHttpMethod method,const char * _url)947 lasso_saml20_profile_build_request_msg(LassoProfile *profile, const char *service,
948 		LassoHttpMethod method, const char *_url)
949 {
950 	char *made_url = NULL, *url;
951 	int rc = 0;
952 
953 	lasso_bad_param(PROFILE, profile);
954 
955 	lasso_profile_clean_msg_info(profile);
956 	url = (char*)_url;
957 
958 	/* check presence of a request */
959 	if (! LASSO_IS_SAMLP2_REQUEST_ABSTRACT(profile->request)) {
960 		return critical_error(LASSO_PROFILE_ERROR_MISSING_REQUEST);
961 	}
962 
963 	/* if not explicitely given, automatically determine an URI from the metadatas */
964 	if (url == NULL) {
965 		LassoProvider *provider;
966 
967 		lasso_check_good_rc(get_provider(profile, &provider));
968 		made_url = url = get_url(provider, service, http_method_to_binding(method));
969 	}
970 
971 
972 	// Usage of the Destination attribute on a request is mandated only
973 	// in "3.4.5.2" and "3.5.5.2" in saml-bindings-2.0-os for signed requests
974 	// and is marked as optional in the XSD schema otherwise.
975 	// PAOS is a special case because an SP does not select an IdP - ECP does
976 	// it instead. Therefore, this attribute needs to be left unpopulated.
977 	if (method == LASSO_HTTP_METHOD_PAOS) {
978 		lasso_release_string(((LassoSamlp2RequestAbstract*)profile->request)->Destination);
979 	} else if (url) {
980 		lasso_assign_string(((LassoSamlp2RequestAbstract*)profile->request)->Destination,
981 				url);
982 	} else {
983 		rc = LASSO_PROFILE_ERROR_UNKNOWN_PROFILE_URL;
984 		goto cleanup;
985 	}
986 
987 	switch (method) {
988 		case LASSO_HTTP_METHOD_SOAP:
989 			rc = lasso_saml20_profile_build_soap_request_msg(profile, url);
990 			break;
991 		case LASSO_HTTP_METHOD_POST:
992 			rc = lasso_saml20_profile_build_post_request_msg(profile, url);
993 			break;
994 		case LASSO_HTTP_METHOD_REDIRECT:
995 			rc = lasso_saml20_profile_build_redirect_request_msg(profile, url);
996 			break;
997 		case LASSO_HTTP_METHOD_ARTIFACT_GET:
998 			rc = lasso_profile_saml20_build_artifact_get_request_msg(profile, url);
999 			break;
1000 		case LASSO_HTTP_METHOD_ARTIFACT_POST:
1001 			rc = lasso_profile_saml20_build_artifact_post_request_msg(profile, url);
1002 			break;
1003 		case LASSO_HTTP_METHOD_PAOS:
1004 			rc = lasso_profile_saml20_build_paos_request_msg(profile, url);
1005 			break;
1006 		default:
1007 			rc = LASSO_PROFILE_ERROR_INVALID_HTTP_METHOD;
1008 			break;
1009 	}
1010 
1011 cleanup:
1012 	lasso_release_string(made_url);
1013 	return rc;
1014 
1015 }
1016 
1017 /*
1018  * Response handling functions
1019  */
1020 
1021 int
lasso_saml20_profile_set_response_status(LassoProfile * profile,const char * code1,const char * code2)1022 lasso_saml20_profile_set_response_status(LassoProfile *profile,
1023 		const char *code1, const char *code2)
1024 {
1025 	LassoSamlp2StatusResponse *status_response = NULL;
1026 	LassoSamlp2Status *status = NULL;
1027 	LassoSamlp2StatusCode *status_code1 = NULL;
1028 	LassoSamlp2StatusCode *status_code2 = NULL;
1029 	int rc = 0;
1030 
1031 	lasso_bad_param(PROFILE, profile);
1032 	lasso_null_param(code1);
1033 	lasso_extract_node_or_fail(status_response, profile->response, SAMLP2_STATUS_RESPONSE,
1034 			LASSO_PROFILE_ERROR_MISSING_RESPONSE);
1035 
1036 	if (! LASSO_IS_SAMLP2_STATUS(status_response->Status)) {
1037 		lasso_assign_new_gobject(status_response->Status,
1038 				(LassoSamlp2Status*)lasso_samlp2_status_new());
1039 	}
1040 	status = status_response->Status;
1041 	if (! LASSO_IS_SAMLP2_STATUS_CODE(status->StatusCode)) {
1042 		lasso_assign_new_gobject(status->StatusCode,
1043 				(LassoSamlp2StatusCode*)lasso_samlp2_status_code_new());
1044 	}
1045 	status_code1 = status->StatusCode;
1046 	lasso_assign_string(status_code1->Value, code1);
1047 
1048 	if (code2) {
1049 		if (! LASSO_IS_SAMLP2_STATUS_CODE(status_code1->StatusCode)) {
1050 			lasso_assign_new_gobject(status_code1->StatusCode,
1051 					(LassoSamlp2StatusCode*)lasso_samlp2_status_code_new());
1052 		}
1053 		status_code2 = status_code1->StatusCode;
1054 		lasso_assign_string(status_code2->Value, code2);
1055 	}
1056 
1057 cleanup:
1058 	return rc;
1059 }
1060 
1061 
1062 int
lasso_saml20_profile_init_response(LassoProfile * profile,LassoSamlp2StatusResponse * status_response,const char * status_code1,const char * status_code2)1063 lasso_saml20_profile_init_response(LassoProfile *profile, LassoSamlp2StatusResponse *status_response,
1064 		const char *status_code1, const char *status_code2)
1065 {
1066 	int rc = 0;
1067 
1068 	lasso_bad_param(PROFILE, profile);
1069 	if (! LASSO_IS_SAMLP2_STATUS_RESPONSE(status_response))
1070 		return LASSO_PROFILE_ERROR_MISSING_RESPONSE;
1071 	lasso_assign_gobject(profile->response, status_response);
1072 
1073 	lasso_assign_new_string(status_response->ID, lasso_build_unique_id(32));
1074 	lasso_assign_string(status_response->Version, "2.0");
1075 	if (LASSO_IS_SERVER(profile->server)) {
1076 		lasso_assign_new_gobject(status_response->Issuer,
1077 				LASSO_SAML2_NAME_ID(lasso_saml2_name_id_new_with_string(
1078 						profile->server->parent.ProviderID)));
1079 	}
1080 	lasso_assign_new_string(status_response->IssueInstant, lasso_get_current_time());
1081 	if (LASSO_IS_SAMLP2_REQUEST_ABSTRACT(profile->request)) {
1082 		lasso_assign_string(status_response->InResponseTo,
1083 				((LassoSamlp2RequestAbstract*)profile->request)->ID);
1084 	}
1085 	lasso_check_good_rc(lasso_profile_saml20_setup_message_signature(profile,
1086 				profile->response));
1087 	if (status_code1) {
1088 		lasso_saml20_profile_set_response_status(profile,
1089 				status_code1, status_code2);
1090 	}
1091 
1092 cleanup:
1093 	return rc;
1094 }
1095 
1096 int
lasso_saml20_profile_validate_request(LassoProfile * profile,gboolean needs_identity,LassoSamlp2StatusResponse * status_response,LassoProvider ** provider_out)1097 lasso_saml20_profile_validate_request(LassoProfile *profile, gboolean needs_identity,
1098 		LassoSamlp2StatusResponse *status_response, LassoProvider **provider_out)
1099 {
1100 	int rc = 0;
1101 	LassoSamlp2RequestAbstract *request_abstract = NULL;
1102 	LassoSaml2NameID *issuer = NULL;
1103 	LassoProvider *provider = NULL;
1104 
1105 	lasso_bad_param(PROFILE, profile);
1106 	lasso_bad_param(SAMLP2_STATUS_RESPONSE, status_response);
1107 	/* verify request presence */
1108 	lasso_extract_node_or_fail(request_abstract, profile->request, SAMLP2_REQUEST_ABSTRACT,
1109 			LASSO_PROFILE_ERROR_MISSING_REQUEST);
1110 	/* look for identity object */
1111 	if (needs_identity) {
1112 		goto_cleanup_if_fail_with_rc(LASSO_IS_IDENTITY(profile->identity),
1113 				LASSO_PROFILE_ERROR_IDENTITY_NOT_FOUND);
1114 	}
1115 
1116 	/* extract provider */
1117 	lasso_extract_node_or_fail(issuer, request_abstract->Issuer, SAML2_NAME_ID,
1118 			LASSO_PROFILE_ERROR_MISSING_ISSUER);
1119 	lasso_assign_string(profile->remote_providerID, issuer->content);
1120 	rc = get_provider(profile, &provider);
1121 	if (rc)
1122 		goto cleanup;
1123 
1124 	/* init the response */
1125 	lasso_saml20_profile_init_response(profile, status_response,
1126 			LASSO_SAML2_STATUS_CODE_SUCCESS, NULL);
1127 
1128 	switch (lasso_profile_get_signature_verify_hint(profile)) {
1129 		case LASSO_PROFILE_SIGNATURE_VERIFY_HINT_MAYBE:
1130 		case LASSO_PROFILE_SIGNATURE_VERIFY_HINT_FORCE:
1131 			if (profile->signature_status) {
1132 				lasso_saml20_profile_set_response_status(profile,
1133 						LASSO_SAML2_STATUS_CODE_REQUESTER,
1134 						LASSO_LIB_STATUS_CODE_INVALID_SIGNATURE);
1135 				return profile->signature_status;
1136 			}
1137 		case LASSO_PROFILE_SIGNATURE_VERIFY_HINT_IGNORE:
1138 			break;
1139 		case LASSO_PROFILE_SIGNATURE_VERIFY_HINT_LAST:
1140 			g_assert_not_reached();
1141 	}
1142 
1143 cleanup:
1144 	if (provider && provider_out)
1145 		*provider_out = provider;
1146 	return rc;
1147 
1148 }
1149 
1150 /**
1151  * lasso_saml20_profile_export_to_query:
1152  * @profile: a #LassoProfile
1153  * @msg: a #LassoNode to export as a query
1154  * @query: an ouput variable to store the result
1155  * @signature_method: the signature method for signing the query
1156  * @private_key_file:(allow-none): the private key to eventually sign the query
1157  * @private_key_password:(allow-none): the password of the private key if there is one
1158  *
1159  * Create a query following the DEFLATE encoding of the SAML 2.0 HTTP
1160  * Redirect binding. If the root message node has an XML signature, signature is removed and query
1161  * is signed.
1162  *
1163  * Return value: 0 if successful, an error code otherwise.
1164  */
1165 static int
lasso_saml20_profile_export_to_query(LassoProfile * profile,LassoNode * msg,char ** query,LassoSignatureContext context)1166 lasso_saml20_profile_export_to_query(LassoProfile *profile, LassoNode *msg, char **query,
1167 		LassoSignatureContext context) {
1168 	char *unsigned_query = NULL;
1169 	char *result = NULL;
1170 	int rc = 0;
1171 
1172 	unsigned_query = lasso_node_build_query(msg);
1173 	goto_cleanup_if_fail_with_rc(unsigned_query != NULL,
1174 			LASSO_PROFILE_ERROR_BUILDING_QUERY_FAILED);
1175 	if (profile->msg_relayState) {
1176 		unsigned_query = lasso_url_add_parameters(unsigned_query, 1, "RelayState",
1177 				profile->msg_relayState, NULL);
1178 
1179 		if (strlen(profile->msg_relayState) > 80) {
1180 			message(G_LOG_LEVEL_WARNING, "Encoded a RelayState of more than 80 bytes, "
1181 					"see #3.4.3 of saml-bindings-2.0-os");
1182 		}
1183 	}
1184 	if (lasso_validate_signature_method(context.signature_method)) {
1185 		result = lasso_query_sign(unsigned_query, context);
1186 		goto_cleanup_if_fail_with_rc(result != NULL,
1187 				LASSO_PROFILE_ERROR_BUILDING_QUERY_FAILED);
1188 		lasso_transfer_string(*query, result);
1189 	} else {
1190 		lasso_transfer_string(*query, unsigned_query);
1191 	}
1192 cleanup:
1193 	lasso_release_string(unsigned_query);
1194 	lasso_release_string(result);
1195 	return rc;
1196 }
1197 
1198 /**
1199  * lasso_saml20_profile_build_http_redirect:
1200  * @profile: a #LassoProfile object
1201  * @msg: a #LassoNode object representing a SAML 2.0 message
1202  * @must_sign: wheter to sign the query message using query signatures
1203  * @url: the URL where the query is targeted
1204  *
1205  * Build an HTTP URL with a query-string following the SAML 2.0 HTTP-Redirect binding rules,
1206  * eventually sign it. Any signature at the message level is removed.
1207  *
1208  * Return value: 0 if successful, an error code otherwise.
1209  */
1210 gint
lasso_saml20_profile_build_http_redirect(LassoProfile * profile,LassoNode * msg,const char * url)1211 lasso_saml20_profile_build_http_redirect(LassoProfile *profile,
1212 	LassoNode *msg,
1213 	const char *url)
1214 {
1215 	char *query = NULL;
1216 	int rc = 0;
1217 	LassoSignatureContext context = LASSO_SIGNATURE_CONTEXT_NONE;
1218 
1219 	goto_cleanup_if_fail_with_rc (url != NULL, LASSO_PROFILE_ERROR_UNKNOWN_PROFILE_URL);
1220 	/* if message is signed, remove XML signature, add query signature */
1221 	lasso_assign_signature_context(context, lasso_node_get_signature(msg));
1222 	if (lasso_validate_signature_method(context.signature_method)) {
1223 		lasso_node_remove_signature(msg);
1224 	}
1225 	lasso_check_good_rc(lasso_saml20_profile_export_to_query(profile, msg, &query, context));
1226 
1227 	lasso_assign_new_string(profile->msg_url, lasso_concat_url_query(url, query));
1228 	lasso_release(profile->msg_body);
1229 	lasso_release(query);
1230 	lasso_assign_new_signature_context(context, LASSO_SIGNATURE_CONTEXT_NONE);
1231 
1232 cleanup:
1233 	return rc;
1234 }
1235 
1236 static int
lasso_saml20_profile_build_redirect_response_msg(LassoProfile * profile,const char * url)1237 lasso_saml20_profile_build_redirect_response_msg(LassoProfile *profile, const char *url)
1238 {
1239 	return lasso_saml20_profile_build_http_redirect(profile,
1240 			profile->response,
1241 			url);
1242 }
1243 
1244 static int
lasso_saml20_profile_build_post_response_msg(LassoProfile * profile,const char * url)1245 lasso_saml20_profile_build_post_response_msg(LassoProfile *profile, const char *url)
1246 {
1247 	lasso_assign_string(profile->msg_url, url);
1248 	lasso_assign_new_string(profile->msg_body, lasso_node_export_to_base64(profile->response));
1249 	check_msg_body;
1250 	return 0;
1251 }
1252 
1253 static int
lasso_saml20_profile_build_soap_response_msg(LassoProfile * profile)1254 lasso_saml20_profile_build_soap_response_msg(LassoProfile *profile)
1255 {
1256 	lasso_release_string(profile->msg_url);
1257 	lasso_assign_new_string(profile->msg_body, lasso_node_export_to_soap(profile->response));
1258 	check_msg_body;
1259 	return 0;
1260 }
1261 
1262 
1263 int
lasso_saml20_profile_build_response_msg(LassoProfile * profile,char * service,LassoHttpMethod method,const char * _url)1264 lasso_saml20_profile_build_response_msg(LassoProfile *profile, char *service,
1265 		LassoHttpMethod method, const char *_url)
1266 {
1267 	LassoProvider *provider;
1268 	char *made_url = NULL, *url;
1269 	int rc = 0;
1270 
1271 	lasso_bad_param(PROFILE, profile);
1272 
1273 	lasso_profile_clean_msg_info(profile);
1274 	lasso_check_good_rc(get_provider(profile, &provider));
1275 	url = (char*)_url;
1276 
1277 	/* check presence of a request */
1278 	if (! LASSO_IS_SAMLP2_STATUS_RESPONSE(profile->response)) {
1279 		return critical_error(LASSO_PROFILE_ERROR_MISSING_RESPONSE);
1280 	}
1281 
1282 	/* if not explicitely given, automatically determine an URI from the metadatas */
1283 	if (url == NULL && service && method != LASSO_HTTP_METHOD_SOAP) {
1284 		made_url = url = get_response_url(provider, service, http_method_to_binding(method));
1285 	}
1286 
1287 	/* only asynchronous bindings needs an URL for the response, SOAP does not need it, and PAOS
1288 	 * is special (response is a SOAP request !?! ) */
1289 	if (! url) {
1290 		switch (method) {
1291 			case LASSO_HTTP_METHOD_POST:
1292 			case LASSO_HTTP_METHOD_REDIRECT:
1293 			case LASSO_HTTP_METHOD_ARTIFACT_GET:
1294 			case LASSO_HTTP_METHOD_ARTIFACT_POST:
1295 			case LASSO_HTTP_METHOD_PAOS:
1296 				goto_cleanup_with_rc(critical_error(LASSO_PROFILE_ERROR_UNKNOWN_PROFILE_URL));
1297 			default:
1298 				break;
1299 		}
1300 	}
1301 
1302 	if (url) {
1303 		lasso_assign_string(((LassoSamlp2StatusResponse*)profile->response)->Destination,
1304 				url);
1305 	}
1306 
1307 	switch (method) {
1308 		case LASSO_HTTP_METHOD_POST:
1309 			rc = lasso_saml20_profile_build_post_response_msg(profile, url);
1310 			break;
1311 		case LASSO_HTTP_METHOD_REDIRECT:
1312 			rc = lasso_saml20_profile_build_redirect_response_msg(profile, url);
1313 			break;
1314 		case LASSO_HTTP_METHOD_SOAP:
1315 			rc = lasso_saml20_profile_build_soap_response_msg(profile);
1316 			break;
1317 		case LASSO_HTTP_METHOD_ARTIFACT_GET:
1318 			rc = lasso_profile_saml20_build_artifact_get_response_msg(profile, url);
1319 			break;
1320 		case LASSO_HTTP_METHOD_ARTIFACT_POST:
1321 			rc = lasso_profile_saml20_build_artifact_post_response_msg(profile, url);
1322 			break;
1323 		default:
1324 			rc= LASSO_PROFILE_ERROR_UNSUPPORTED_PROFILE;
1325 			break;
1326 	}
1327 
1328 cleanup:
1329 	lasso_release_string(made_url);
1330 	return rc;
1331 }
1332 
1333 static gboolean
_lasso_saml20_is_valid_issuer(LassoSaml2NameID * name_id)1334 _lasso_saml20_is_valid_issuer(LassoSaml2NameID *name_id) {
1335 	if (! LASSO_IS_SAML2_NAME_ID(name_id))
1336 		return FALSE;
1337 
1338 	if (name_id->Format &&
1339 			lasso_strisnotequal(name_id->Format,LASSO_SAML2_NAME_IDENTIFIER_FORMAT_ENTITY))
1340 	{
1341 		return FALSE;
1342 	}
1343 	return TRUE;
1344 }
1345 
1346 /**
1347  * lasso_saml20_profile_process_any_response:
1348  * @profile: the SAML 2.0 #LassoProfile object
1349  * @status_response: the prototype for the response object
1350  * @response_msg: the content of the response message
1351  *
1352  * Generic method for SAML 2.0 protocol message handling.
1353  *
1354  * It tries to validate a signature on the response msg, the result of this operation is kept inside
1355  * profile->signature_status. Use it afterward in your specific profile. Beware that it does not
1356  * return an error code if signature validation failed. It let's specific profile accept unsigned
1357  * messages.
1358  *
1359  * Return value: 0 if successful, an error code otherwise.
1360  */
1361 int
lasso_saml20_profile_process_any_response(LassoProfile * profile,LassoSamlp2StatusResponse * status_response,LassoHttpMethod * response_method,const char * response_msg)1362 lasso_saml20_profile_process_any_response(LassoProfile *profile,
1363 		LassoSamlp2StatusResponse *status_response,
1364 		LassoHttpMethod *response_method,
1365 		const char *response_msg)
1366 {
1367 	int rc = 0;
1368 	LassoProvider *remote_provider = NULL;
1369 	LassoServer *server = NULL;
1370 	LassoSamlp2StatusResponse *response_abstract = NULL;
1371 	LassoSamlp2Status *status = NULL;
1372 	LassoSamlp2StatusCode *status_code1 = NULL;
1373 	LassoMessageFormat format;
1374 	gboolean missing_issuer = FALSE;
1375 
1376 	xmlDoc *doc = NULL;
1377 	xmlNode *content = NULL;
1378 
1379 	lasso_bad_param(PROFILE, profile);
1380 	lasso_bad_param(SAMLP2_STATUS_RESPONSE, status_response);
1381 
1382 	/* reset signature_status */
1383 	profile->signature_status = 0;
1384 	format = lasso_node_init_from_message_with_format((LassoNode*)status_response,
1385 		response_msg, LASSO_MESSAGE_FORMAT_UNKNOWN, &doc, &content);
1386 	if (format <= LASSO_MESSAGE_FORMAT_UNKNOWN) {
1387 		rc = LASSO_PROFILE_ERROR_INVALID_MSG;
1388 		goto cleanup;
1389 	}
1390 	if (response_method) {
1391 		switch (format) {
1392 			case LASSO_MESSAGE_FORMAT_SOAP:
1393 				*response_method = LASSO_HTTP_METHOD_SOAP;
1394 				break;
1395 			case LASSO_MESSAGE_FORMAT_QUERY:
1396 				*response_method = LASSO_HTTP_METHOD_REDIRECT;
1397 				break;
1398 			case LASSO_MESSAGE_FORMAT_BASE64:
1399 				*response_method = LASSO_HTTP_METHOD_POST;
1400 				break;
1401 			default:
1402 				return LASSO_PROFILE_ERROR_UNSUPPORTED_PROFILE;
1403 		}
1404 	}
1405 	lasso_assign_gobject(profile->response, (LassoNode*)status_response);
1406 	lasso_extract_node_or_fail(response_abstract, profile->response, SAMLP2_STATUS_RESPONSE,
1407 			LASSO_PROFILE_ERROR_INVALID_MSG);
1408 	lasso_extract_node_or_fail(server, profile->server, SERVER,
1409 			LASSO_PROFILE_ERROR_MISSING_SERVER);
1410 	if (_lasso_saml20_is_valid_issuer(response_abstract->Issuer)) {
1411 		lasso_assign_string(profile->remote_providerID, response_abstract->Issuer->content);
1412 		remote_provider = lasso_server_get_provider(server, profile->remote_providerID);
1413 	} else {
1414 		missing_issuer = TRUE;
1415 	}
1416 
1417 	if (remote_provider) {
1418 		/* verify the signature at the message level */
1419 		if (content && doc && format != LASSO_MESSAGE_FORMAT_QUERY) {
1420 			profile->signature_status =
1421 				lasso_provider_verify_saml_signature(remote_provider, content, doc);
1422 		} else if (format == LASSO_MESSAGE_FORMAT_QUERY) {
1423 			profile->signature_status =
1424 				lasso_provider_verify_query_signature(remote_provider, response_msg);
1425 		} else {
1426 			profile->signature_status = LASSO_DS_ERROR_SIGNATURE_VERIFICATION_FAILED;
1427 		}
1428 	} else {
1429 		rc = LASSO_SERVER_ERROR_PROVIDER_NOT_FOUND;
1430 		profile->signature_status = LASSO_SERVER_ERROR_PROVIDER_NOT_FOUND;
1431 		goto cleanup;
1432 	}
1433 
1434 	/* verify status code */
1435 	lasso_extract_node_or_fail(status, status_response->Status, SAMLP2_STATUS,
1436 			LASSO_PROFILE_ERROR_MISSING_STATUS_CODE);
1437 	lasso_extract_node_or_fail(status_code1, status->StatusCode, SAMLP2_STATUS_CODE,
1438 			LASSO_PROFILE_ERROR_MISSING_STATUS_CODE);
1439 	if (lasso_strisnotequal(status_code1->Value,LASSO_SAML2_STATUS_CODE_SUCCESS))
1440 	{
1441 		LassoSamlp2StatusCode *status_code2 = status_code1->StatusCode;
1442 		rc = LASSO_PROFILE_ERROR_STATUS_NOT_SUCCESS;
1443 
1444 		if (!status_code2)
1445 			goto cleanup;
1446 
1447 		if (!status_code2->Value)
1448 			goto cleanup;
1449 		/* FIXME: what to do with secondary status code ? */
1450 		if (lasso_strisequal(status_code2->Value, LASSO_SAML2_STATUS_CODE_REQUEST_DENIED)) {
1451 			rc = LASSO_PROFILE_ERROR_REQUEST_DENIED;
1452 		}
1453 	}
1454 
1455 cleanup:
1456 	lasso_release_doc(doc);
1457 	if (rc) {
1458 		return rc;
1459 	}
1460 	switch (lasso_profile_get_signature_verify_hint(profile)) {
1461 		case LASSO_PROFILE_SIGNATURE_VERIFY_HINT_MAYBE:
1462 		case LASSO_PROFILE_SIGNATURE_VERIFY_HINT_FORCE:
1463 			if (profile->signature_status) {
1464 				return LASSO_PROFILE_ERROR_CANNOT_VERIFY_SIGNATURE;
1465 			}
1466 		case LASSO_PROFILE_SIGNATURE_VERIFY_HINT_IGNORE:
1467 			break;
1468 		case LASSO_PROFILE_SIGNATURE_VERIFY_HINT_LAST:
1469 			g_assert_not_reached();
1470 	}
1471 	if (missing_issuer) {
1472 		return LASSO_PROFILE_ERROR_MISSING_ISSUER;
1473 	}
1474 	return 0;
1475 }
1476 
1477 /**
1478  * lasso_saml20_profile_process_soap_response:
1479  *
1480  * Generic method for processing SAML 2.0 protocol message as a SOAP response.
1481  *
1482  * Return value: 0 if successful; an error code otherwise.
1483  */
1484 int
lasso_saml20_profile_process_soap_response(LassoProfile * profile,const char * response_msg)1485 lasso_saml20_profile_process_soap_response(LassoProfile *profile,
1486 		const char *response_msg)
1487 {
1488 	return lasso_saml20_profile_process_soap_response_with_headers(
1489 				profile, response_msg, NULL);
1490 }
1491 
1492 /**
1493  * lasso_saml20_profile_process_soap_response_with_headers:
1494  * @profile: the SAML 2.0 #LassoProfile object
1495  * @response_msg: xml response message
1496  * @header_return: If non-NULL the soap headers are returned at this
1497  *                 pointer as a #LassoSoapHeader object.
1498  *
1499  * Generic method for processing SAML 2.0 protocol message as a SOAP response.
1500  * The SOAP headers are returned via the header_return parameter
1501  * if the parameter is non-NULL. The caller is responsible for freeing
1502  * the SOAP header by calling lasso_release_gobject().
1503  *
1504  * Return value: 0 if successful; an error code otherwise.
1505  */
1506 int
lasso_saml20_profile_process_soap_response_with_headers(LassoProfile * profile,const char * response_msg,LassoSoapHeader ** header_return)1507 lasso_saml20_profile_process_soap_response_with_headers(LassoProfile *profile,
1508 		const char *response_msg, LassoSoapHeader **header_return)
1509 {
1510 	int rc = 0;
1511 	LassoSoapEnvelope *envelope = NULL;
1512 	LassoSoapHeader *header = NULL;
1513 	LassoSoapBody *body = NULL;
1514 	LassoSaml2NameID *issuer = NULL;
1515 	LassoProvider *remote_provider = NULL;
1516 	LassoServer *server = NULL;
1517 	LassoSamlp2StatusResponse *response_abstract = NULL;
1518 
1519 	lasso_bad_param(PROFILE, profile);
1520 	lasso_null_param(response_msg);
1521 	if (header_return) {
1522 		*header_return = NULL;
1523 	}
1524 
1525 	profile->signature_status = 0;
1526 
1527 	/* Get the SOAP envelope */
1528 	lasso_extract_node_or_fail(envelope, lasso_soap_envelope_new_from_message(response_msg),
1529 							   SOAP_ENVELOPE, LASSO_PROFILE_ERROR_INVALID_SOAP_MSG);
1530 
1531 	/* Get and validate the SOAP body, assign it to the profile response */
1532 	lasso_extract_node_or_fail(body, envelope->Body, SOAP_BODY,
1533 							   LASSO_SOAP_ERROR_MISSING_BODY);
1534 	if (body->any && LASSO_IS_NODE(body->any->data)) {
1535 		lasso_assign_gobject(profile->response, body->any->data);
1536 	} else {
1537 		lasso_release_gobject(profile->response);
1538 		goto_cleanup_with_rc(LASSO_SOAP_ERROR_MISSING_BODY);
1539 	}
1540 
1541 	/* Get the optional SOAP header, validate it, optionally return it */
1542 	if (envelope->Header) {
1543 		lasso_extract_node_or_fail(header, envelope->Header, SOAP_HEADER,
1544 								   LASSO_PROFILE_ERROR_INVALID_SOAP_MSG);
1545 	}
1546 	if (header_return) {
1547 		if (header) {
1548 			lasso_assign_gobject(*header_return, header);
1549 		}
1550 	}
1551 
1552 	/* Extract and validate the response data */
1553 	lasso_extract_node_or_fail(response_abstract, profile->response, SAMLP2_STATUS_RESPONSE,
1554 			LASSO_PROFILE_ERROR_INVALID_MSG);
1555 	lasso_extract_node_or_fail(server, profile->server, SERVER,
1556 			LASSO_PROFILE_ERROR_MISSING_SERVER);
1557 	lasso_extract_node_or_fail(issuer, response_abstract->Issuer, SAML2_NAME_ID,
1558 			LASSO_PROFILE_ERROR_MISSING_ISSUER);
1559 	lasso_assign_string(profile->remote_providerID, issuer->content);
1560 
1561 	remote_provider = lasso_server_get_provider(server, profile->remote_providerID);
1562 	if (remote_provider == NULL) {
1563 		rc = LASSO_SERVER_ERROR_PROVIDER_NOT_FOUND;
1564 		goto cleanup;
1565 	}
1566 
1567 	profile->signature_status = lasso_provider_verify_signature(
1568 			remote_provider, response_msg, "ID", LASSO_MESSAGE_FORMAT_SOAP);
1569 	switch (lasso_profile_get_signature_verify_hint(profile)) {
1570 		case LASSO_PROFILE_SIGNATURE_VERIFY_HINT_MAYBE:
1571 		case LASSO_PROFILE_SIGNATURE_VERIFY_HINT_FORCE:
1572 			rc = profile->signature_status;
1573 			break;
1574 		case LASSO_PROFILE_SIGNATURE_VERIFY_HINT_IGNORE:
1575 			break;
1576 		default:
1577 			g_assert(0);
1578 			break;
1579 	}
1580 
1581 cleanup:
1582 	lasso_release_gobject(envelope);
1583 	return rc;
1584 }
1585 
1586 gint
lasso_saml20_profile_build_http_redirect_query_simple(LassoProfile * profile,LassoNode * msg,const char * profile_name,gboolean is_response)1587 lasso_saml20_profile_build_http_redirect_query_simple(LassoProfile *profile,
1588 		LassoNode *msg,
1589 		const char *profile_name,
1590 		gboolean is_response)
1591 {
1592 	char *idx = NULL;
1593 	char *url = NULL;
1594 	LassoProvider *remote_provider = NULL;
1595 	int rc = 0;
1596 
1597 
1598 	goto_cleanup_if_fail_with_rc(profile->remote_providerID != NULL,
1599 			LASSO_PROFILE_ERROR_MISSING_REMOTE_PROVIDERID);
1600 	remote_provider = lasso_server_get_provider(profile->server,
1601 				profile->remote_providerID);
1602 	goto_cleanup_if_fail_with_rc(LASSO_IS_PROVIDER(remote_provider),
1603 			LASSO_SERVER_ERROR_PROVIDER_NOT_FOUND);
1604 	if (is_response) {
1605 		idx = g_strdup_printf("%s HTTP-Redirect ResponseLocation", profile_name);
1606 		url = lasso_provider_get_metadata_one(remote_provider, idx);
1607 		lasso_release(idx);
1608 	}
1609 	if (url == NULL) {
1610 		idx = g_strdup_printf("%s HTTP-Redirect", profile_name);
1611 		url = lasso_provider_get_metadata_one(remote_provider, idx);
1612 		lasso_release(idx);
1613 	}
1614 	/* remove signature at the message level */
1615 	rc = lasso_saml20_profile_build_http_redirect(profile, msg, url);
1616 cleanup:
1617 	lasso_release(url);
1618 	return rc;
1619 }
1620 
1621 gint
lasso_profile_saml20_setup_message_signature(LassoProfile * profile,LassoNode * request_or_response)1622 lasso_profile_saml20_setup_message_signature(LassoProfile *profile, LassoNode *request_or_response)
1623 {
1624 	lasso_bad_param(PROFILE, profile);
1625 	LassoSignatureContext context = LASSO_SIGNATURE_CONTEXT_NONE;
1626 	lasso_error_t rc = 0;
1627 
1628 	switch (lasso_profile_get_signature_hint(profile)) {
1629 		case LASSO_PROFILE_SIGNATURE_HINT_MAYBE:
1630 			if (! lasso_flag_sign_messages) {
1631 				message(G_LOG_LEVEL_WARNING, "message should be signed but no-sign-messages flag is " \
1632 						"activated, so it won't be");
1633 				return 0;
1634 			}
1635 			break;
1636 		case LASSO_PROFILE_SIGNATURE_HINT_FORBID:
1637 			return 0;
1638 		default:
1639 			break;
1640 	}
1641 
1642 	if (! LASSO_IS_SERVER(profile->server)) {
1643 		return LASSO_PROFILE_ERROR_MISSING_SERVER;
1644 	}
1645 	lasso_check_good_rc(lasso_server_get_signature_context_for_provider_by_name(profile->server,
1646 				profile->remote_providerID, &context));
1647 	lasso_check_good_rc(lasso_node_set_signature(request_or_response, context));
1648 cleanup:
1649 	return rc;
1650 }
1651 
1652 /**
1653  * lasso_saml20_profile_setup_subject:
1654  * @profile: a #LassoProfile object
1655  * @subject: a #LassoSaml2Subject object
1656  *
1657  * Encrypt subject if necessary.
1658  */
1659 int
lasso_saml20_profile_setup_subject(LassoProfile * profile,LassoSaml2Subject * subject)1660 lasso_saml20_profile_setup_subject(LassoProfile *profile,
1661 		LassoSaml2Subject *subject)
1662 {
1663 	LassoProvider *remote_provider;
1664 
1665 	remote_provider = lasso_server_get_provider(profile->server, profile->remote_providerID);
1666 	g_return_val_if_fail (LASSO_IS_PROVIDER(remote_provider), LASSO_ERROR_CAST_FAILED);
1667 	if (! (lasso_provider_get_encryption_mode(remote_provider) & LASSO_ENCRYPTION_MODE_NAMEID)) {
1668 		return 0;
1669 	}
1670 	return lasso_saml20_profile_setup_encrypted_node(remote_provider,
1671 			(LassoNode**)subject->NameID,
1672 			(LassoNode**)subject->EncryptedID);
1673 }
1674 
1675 gint
lasso_saml20_profile_setup_encrypted_node(LassoProvider * provider,LassoNode ** node_to_encrypt,LassoNode ** node_destination)1676 lasso_saml20_profile_setup_encrypted_node(LassoProvider *provider,
1677 		LassoNode **node_to_encrypt, LassoNode **node_destination)
1678 {
1679 	LassoNode *encrypted_node;
1680 
1681 	if (! LASSO_IS_PROVIDER(provider)) {
1682 		return critical_error(LASSO_SERVER_ERROR_PROVIDER_NOT_FOUND);
1683 	}
1684 	encrypted_node = (LassoNode*)lasso_node_encrypt(*node_to_encrypt,
1685 			lasso_provider_get_encryption_public_key(provider),
1686 			lasso_provider_get_encryption_sym_key_type(provider),
1687 			provider->ProviderID);
1688 	if (! encrypted_node) {
1689 		return LASSO_DS_ERROR_ENCRYPTION_FAILED;
1690 	}
1691 	lasso_assign_new_gobject(*node_destination, encrypted_node);
1692 	lasso_release_gobject(*node_to_encrypt);
1693 	return 0;
1694 }
1695 
1696 /**
1697  * Check the profile->signature_status flag, if signature validation is activated, report it as an
1698  * error, if not not return 0.
1699  */
1700 int
lasso_saml20_profile_check_signature_status(LassoProfile * profile)1701 lasso_saml20_profile_check_signature_status(LassoProfile *profile) {
1702 	int rc = 0;
1703 
1704 	if (profile->signature_status) {
1705 		switch (lasso_profile_get_signature_verify_hint(profile)) {
1706 			case LASSO_PROFILE_SIGNATURE_VERIFY_HINT_MAYBE:
1707 			case LASSO_PROFILE_SIGNATURE_VERIFY_HINT_FORCE:
1708 				rc = profile->signature_status;
1709 				break;
1710 			case LASSO_PROFILE_SIGNATURE_VERIFY_HINT_IGNORE:
1711 				break;
1712 			case LASSO_PROFILE_SIGNATURE_VERIFY_HINT_LAST:
1713 				g_assert_not_reached();
1714 				break;
1715 		}
1716 	}
1717 
1718 	return rc;
1719 }
1720