1 /* $Id$
2  *
3  * Lasso - A free implementation of the Liberty Alliance specifications.
4  *
5  * Copyright (C) 2004-2007 Entr'ouvert
6  * http://lasso.entrouvert.org
7  *
8  * Authors: See AUTHORS file in top-level directory.
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, see <http://www.gnu.org/licenses/>.
22  */
23 
24 /**
25  * SECTION:profile
26  * @short_description: Base class for all identity profiles
27  *
28  **/
29 
30 #include "../xml/private.h"
31 #include <libxml/xpath.h>
32 #include <libxml/xpathInternals.h>
33 
34 #include "../xml/samlp_response.h"
35 #include "../xml/samlp_request.h"
36 #include "../xml/lib_authn_response.h"
37 #include "../xml/lib_status_response.h"
38 
39 #include "profile.h"
40 #include "profileprivate.h"
41 #include "providerprivate.h"
42 #include "sessionprivate.h"
43 
44 #include "../saml-2.0/profileprivate.h"
45 #include "../xml/saml-2.0/saml2_name_id.h"
46 #include "../xml/saml_name_identifier.h"
47 #include "../xml/saml-2.0/saml2_assertion.h"
48 #include "../xml/soap-1.1/soap_fault.h"
49 #include "../utils.h"
50 #include "../debug.h"
51 #ifdef LASSO_WSF_ENABLED
52 #include "../xml/idwsf_strings.h"
53 #include "../xml/id-wsf-2.0/idwsf2_strings.h"
54 #endif
55 #include "../lasso_config.h"
56 
57 #include <stdio.h>
58 
59 /*****************************************************************************/
60 /* public functions                                                          */
61 /*****************************************************************************/
62 
63 static LassoNode*
_lasso_saml_assertion_get_name_id(LassoSamlAssertion * assertion)64 _lasso_saml_assertion_get_name_id(LassoSamlAssertion *assertion)
65 {
66 	LassoSamlAuthenticationStatement *authn_statement;
67 	LassoSamlSubject *subject;
68 
69 	goto_cleanup_if_fail(LASSO_IS_SAML_ASSERTION(assertion));
70 	authn_statement = assertion->AuthenticationStatement;
71 	goto_cleanup_if_fail(LASSO_IS_SAML_AUTHENTICATION_STATEMENT(authn_statement));
72 	subject = authn_statement->parent.Subject;
73 	goto_cleanup_if_fail(LASSO_IS_SAML_SUBJECT(subject));
74 	if (LASSO_IS_SAML_NAME_IDENTIFIER(subject->NameIdentifier))
75 		return (LassoNode*)subject->NameIdentifier;
76 cleanup:
77 	return NULL;
78 }
79 
80 static LassoNode*
_lasso_saml2_assertion_get_name_id(LassoSaml2Assertion * assertion)81 _lasso_saml2_assertion_get_name_id(LassoSaml2Assertion *assertion)
82 {
83 	LassoSaml2Subject *subject;
84 
85 	goto_cleanup_if_fail(LASSO_SAML2_ASSERTION(assertion));
86 	subject = assertion->Subject;
87 	goto_cleanup_if_fail(LASSO_SAML2_SUBJECT(subject));
88 	if (LASSO_IS_SAML2_NAME_ID(subject->NameID))
89 		return (LassoNode*)subject->NameID;
90 
91 cleanup:
92 	return NULL;
93 }
94 
95 /**
96  * lasso_profile_get_nameIdentifier:
97  * @profile: a #LassoProfile
98  *
99  * Looks up appropriate federation in object and gets the service provider name
100  * identifier (which is actually a #LassoSamlNameIdentifier in ID-FF 1.2 and
101  * #LassoSaml2NameID in SAML 2.0).
102  *
103  * Return value:(transfer none): the name identifier or NULL if none was found.  The #LassoNode
104  *     object is internally allocated and must not be freed by the caller.
105  **/
106 LassoNode*
lasso_profile_get_nameIdentifier(LassoProfile * profile)107 lasso_profile_get_nameIdentifier(LassoProfile *profile)
108 {
109 	LassoProvider *remote_provider;
110 	LassoFederation *federation;
111 	const char *name_id_sp_name_qualifier;
112 
113 	if (!LASSO_IS_PROFILE(profile)) {
114 		return NULL;
115 	}
116 
117 	if (profile->remote_providerID == NULL)
118 		return NULL;
119 
120 	/* For transient federations, we must look at assertions no federation object exists */
121 	if (LASSO_IS_SESSION(profile->session)) {
122 		LassoNode *assertion, *name_id;
123 
124 		assertion = lasso_session_get_assertion(profile->session,
125 				profile->remote_providerID);
126 
127 		name_id = _lasso_saml_assertion_get_name_id((LassoSamlAssertion*)assertion);
128 		if (name_id)
129 			return name_id;
130 		name_id = _lasso_saml2_assertion_get_name_id((LassoSaml2Assertion*)assertion);
131 		if (name_id)
132 			return name_id;
133 	}
134 	/* beware, it is not a real loop ! */
135 	if (LASSO_IS_IDENTITY(profile->identity)) do {
136 		remote_provider = lasso_server_get_provider(profile->server, profile->remote_providerID);
137 		if (remote_provider == NULL)
138 			break;
139 
140 		name_id_sp_name_qualifier = lasso_provider_get_sp_name_qualifier(remote_provider);
141 		if (name_id_sp_name_qualifier == NULL)
142 			break;
143 
144 		federation = g_hash_table_lookup(
145 				profile->identity->federations,
146 				name_id_sp_name_qualifier);
147 		if (federation == NULL)
148 			break;
149 
150 		if (federation->remote_nameIdentifier)
151 			return federation->remote_nameIdentifier;
152 		return federation->local_nameIdentifier;
153 	} while (FALSE);
154 
155 	return NULL;
156 }
157 
158 /**
159  * lasso_profile_get_request_type_from_soap_msg:
160  * @soap: the SOAP message
161  *
162  * Looks up and return the type of the request in a SOAP message.
163  *
164  * Return value: the type of request
165  **/
166 LassoRequestType
lasso_profile_get_request_type_from_soap_msg(const gchar * soap)167 lasso_profile_get_request_type_from_soap_msg(const gchar *soap)
168 {
169 	xmlDoc *doc;
170 	xmlXPathContext *xpathCtx;
171 	xmlXPathObject *xpathObj;
172 	LassoRequestType type = LASSO_REQUEST_TYPE_INVALID;
173 	const char *name = NULL;
174 	xmlNs *ns = NULL;
175 	xmlError error;
176 
177 	memset(&error, 0, sizeof(xmlError));
178 	if (soap == NULL)
179 		return LASSO_REQUEST_TYPE_INVALID;
180 
181 	doc = lasso_xml_parse_memory_with_error(soap, strlen(soap), &error);
182 	if (! doc) {
183 		message(G_LOG_LEVEL_WARNING, "Invalid soap message: %s", error.message);
184 		type = LASSO_REQUEST_TYPE_INVALID;
185 		goto cleanup;
186 	}
187 	xpathCtx = xmlXPathNewContext(doc);
188 	xmlXPathRegisterNs(xpathCtx, (xmlChar*)"s", (xmlChar*)LASSO_SOAP_ENV_HREF);
189 	xpathObj = xmlXPathEvalExpression((xmlChar*)"//s:Body/*", xpathCtx);
190 
191 	if (xpathObj && xpathObj->nodesetval && xpathObj->nodesetval->nodeNr) {
192 		name = (char*)xpathObj->nodesetval->nodeTab[0]->name;
193 		ns = xpathObj->nodesetval->nodeTab[0]->ns;
194 	}
195 
196 	if (name == NULL || ns == NULL) {
197 		message(G_LOG_LEVEL_WARNING, "Invalid SOAP request");
198 	} else if (strcmp(name, "Request") == 0) {
199 		type = LASSO_REQUEST_TYPE_LOGIN;
200 	} else if (strcmp(name, "LogoutRequest") == 0) {
201 		type = LASSO_REQUEST_TYPE_LOGOUT;
202 	} else if (strcmp(name, "FederationTerminationNotification") == 0) {
203 		type = LASSO_REQUEST_TYPE_DEFEDERATION;
204 	} else if (strcmp(name, "RegisterNameIdentifierRequest") == 0) {
205 		type = LASSO_REQUEST_TYPE_NAME_REGISTRATION;
206 	} else if (strcmp(name, "NameIdentifierMappingRequest") == 0) {
207 		type = LASSO_REQUEST_TYPE_NAME_IDENTIFIER_MAPPING;
208 	} else if (strcmp(name, "AuthnRequest") == 0) {
209 		type = LASSO_REQUEST_TYPE_LECP;
210 	} else if (strcmp(name, "ManageNameIDRequest") == 0) {
211 		type = LASSO_REQUEST_TYPE_NAME_ID_MANAGEMENT;
212 #ifdef LASSO_WSF_ENABLED
213 	} else if (strcmp(name, "Query") == 0) {
214 		if (strcmp((char*)ns->href, LASSO_DISCO_HREF) == 0) {
215 			type = LASSO_REQUEST_TYPE_DISCO_QUERY;
216 		} else if (strcmp((char*)ns->href, LASSO_IDWSF2_DISCOVERY_HREF) == 0) {
217 			type = LASSO_REQUEST_TYPE_IDWSF2_DISCO_QUERY;
218 		} else {
219 			type = LASSO_REQUEST_TYPE_DST_QUERY;
220 		}
221 	} else if (strcmp(name, "Modify") == 0) {
222 		if (strcmp((char*)ns->href, LASSO_DISCO_HREF) == 0) {
223 			type = LASSO_REQUEST_TYPE_DISCO_MODIFY;
224 		} else {
225 			type = LASSO_REQUEST_TYPE_DST_MODIFY;
226 		}
227 	} else if (strcmp(name, "SASLRequest") == 0) {
228 		type = LASSO_REQUEST_TYPE_SASL_REQUEST;
229 	} else if (strcmp(name, "SvcMDRegister") == 0) {
230 		type = LASSO_REQUEST_TYPE_IDWSF2_DISCO_SVCMD_REGISTER;
231 	} else if (strcmp(name, "SvcMDAssociationAdd") == 0) {
232 		type = LASSO_REQUEST_TYPE_IDWSF2_DISCO_SVCMD_ASSOCIATION_ADD;
233 #endif
234 	} else {
235 		message(G_LOG_LEVEL_WARNING, "Unknown node name : %s", name);
236 	}
237 
238 	xmlXPathFreeObject(xpathObj);
239 	xmlXPathFreeContext(xpathCtx);
240 cleanup:
241 	lasso_release_doc(doc);
242 	xmlResetError(&error);
243 	return type;
244 }
245 
246 /**
247  * lasso_profile_is_liberty_query:
248  * @query: HTTP query string
249  *
250  * Tests the query string to know if the URL is called as the result of a
251  * Liberty redirect (action initiated elsewhere) or not.
252  *
253  * Return value: TRUE if Liberty query, FALSE otherwise
254  **/
255 gboolean
lasso_profile_is_liberty_query(const gchar * query)256 lasso_profile_is_liberty_query(const gchar *query)
257 {
258 	/* logic is that a lasso query always has some parameters (RequestId,
259 	 * MajorVersion, MinorVersion, IssueInstant, ProviderID,
260 	 * NameIdentifier, NameQualifier, Format).  If three of them are there;
261 	 * it's a lasso query, possibly broken, but a lasso query nevertheless.
262 	 */
263 	gchar *parameters[] = {
264 		"RequestId=", "MajorVersion=", "MinorVersion=", "IssueInstant=",
265 		"ProviderID=", "NameIdentifier=", "NameQualifier=", "Format=",
266 		NULL };
267 	gint i, n = 0;
268 
269 	for (i=0; parameters[i] && n < 3; i++) {
270 		if (strstr(query, parameters[i]))
271 			n++;
272 	}
273 
274 	return (n == 3);
275 }
276 
277 
278 /*****************************************************************************/
279 /* public methods                                                            */
280 /*****************************************************************************/
281 
282 
283 /**
284  * lasso_profile_get_identity:
285  * @profile: a #LassoProfile
286  *
287  * Gets the identity bound to @profile.
288  *
289  * Return value:(transfer none): the identity or NULL if it none was found.  The #LassoIdentity
290  *      object is internally allocated and must not be freed by the caller.
291  **/
292 LassoIdentity*
lasso_profile_get_identity(LassoProfile * profile)293 lasso_profile_get_identity(LassoProfile *profile)
294 {
295 	if (profile->identity && g_hash_table_size(profile->identity->federations))
296 		return profile->identity;
297 	return NULL;
298 }
299 
300 
301 /**
302  * lasso_profile_get_session:
303  * @profile: a #LassoProfile
304  *
305  * Gets the session bound to @profile.
306  *
307  * Return value:(transfer none): the session or NULL if it none was found.  The #LassoSession
308  *      object is internally allocated and must not be freed by the caller.
309  **/
310 LassoSession*
lasso_profile_get_session(LassoProfile * profile)311 lasso_profile_get_session(LassoProfile *profile)
312 {
313 	if (profile->session == NULL)
314 		return NULL;
315 
316 	if (lasso_session_is_empty(profile->session))
317 		return NULL;
318 
319 	return profile->session;
320 }
321 
322 
323 /**
324  * lasso_profile_is_identity_dirty:
325  * @profile: a #LassoProfile
326  *
327  * Checks whether identity has been modified (and should therefore be saved).
328  *
329  * Return value: %TRUE if identity has changed
330  **/
331 gboolean
lasso_profile_is_identity_dirty(LassoProfile * profile)332 lasso_profile_is_identity_dirty(LassoProfile *profile)
333 {
334 	return (profile->identity && profile->identity->is_dirty);
335 }
336 
337 
338 /**
339  * lasso_profile_is_session_dirty:
340  * @profile: a #LassoProfile
341  *
342  * Checks whether session has been modified (and should therefore be saved).
343  *
344  * Return value: %TRUE if session has changed
345  **/
346 gboolean
lasso_profile_is_session_dirty(LassoProfile * profile)347 lasso_profile_is_session_dirty(LassoProfile *profile)
348 {
349 	return LASSO_IS_SESSION(profile->session) && lasso_session_is_dirty(profile->session);
350 }
351 
352 
353 void
lasso_profile_set_response_status(LassoProfile * profile,const char * statusCodeValue)354 lasso_profile_set_response_status(LassoProfile *profile, const char *statusCodeValue)
355 {
356 	LassoSamlpStatus *status;
357 
358 	/* protocols-schema 1.2 (errata 2.0), page 9
359 	 *
360 	 * 3.1.9. Response Status Codes
361 	 *
362 	 * All Liberty response messages use <samlp: StatusCode> elements to
363 	 * indicate the status of a corresponding request.  Responders MUST
364 	 * comply with the rules governing <samlp: StatusCode> elements
365 	 * specified in [SAMLCore11] regarding the use of nested second-, or
366 	 * lower-level response codes to provide specific information relating
367 	 * to particular errors. A number of status codes are defined within
368 	 * the Liberty namespace for use with this specification.
369 	 */
370 
371 	status = lasso_samlp_status_new();
372 	status->StatusCode = lasso_samlp_status_code_new();
373 
374 	if (strncmp(statusCodeValue, "samlp:", 6) == 0) {
375 		status->StatusCode->Value = g_strdup(statusCodeValue);
376 	} else {
377 		status->StatusCode->Value = g_strdup(LASSO_SAML_STATUS_CODE_RESPONDER);
378 		status->StatusCode->StatusCode = lasso_samlp_status_code_new();
379 		status->StatusCode->StatusCode->Value = g_strdup(statusCodeValue);
380 	}
381 
382 	if (LASSO_IS_SAMLP_RESPONSE(profile->response)) {
383 		LassoSamlpResponse *response = LASSO_SAMLP_RESPONSE(profile->response);
384 		lasso_assign_new_gobject(response->Status, status);
385 		return;
386 	}
387 	if (LASSO_IS_LIB_STATUS_RESPONSE(profile->response)) {
388 		LassoLibStatusResponse *response = LASSO_LIB_STATUS_RESPONSE(profile->response);
389 		lasso_assign_new_gobject(response->Status, status);
390 		return;
391 	}
392 
393 	message(G_LOG_LEVEL_CRITICAL, "Failed to set status");
394 	g_assert_not_reached();
395 }
396 
397 void
lasso_profile_clean_msg_info(LassoProfile * profile)398 lasso_profile_clean_msg_info(LassoProfile *profile)
399 {
400 	lasso_release_string(profile->msg_url);
401 	lasso_release_string(profile->msg_body);
402 }
403 
404 /**
405  * lasso_profile_set_identity_from_dump:
406  * @profile: a #LassoProfile
407  * @dump: XML identity dump
408  *
409  * Builds a new #LassoIdentity object from XML dump and binds it to @profile.
410  *
411  * Return value: 0 on success; or a negative value otherwise.
412  **/
413 gint
lasso_profile_set_identity_from_dump(LassoProfile * profile,const gchar * dump)414 lasso_profile_set_identity_from_dump(LassoProfile *profile, const gchar *dump)
415 {
416 	g_return_val_if_fail(dump != NULL, LASSO_PARAM_ERROR_INVALID_VALUE);
417 
418 	lasso_assign_new_gobject(profile->identity, lasso_identity_new_from_dump(dump));
419 	if (profile->identity == NULL) {
420 		return critical_error(LASSO_PROFILE_ERROR_BAD_IDENTITY_DUMP);
421 	}
422 
423 	return 0;
424 }
425 
426 
427 /**
428  * lasso_profile_set_session_from_dump:
429  * @profile: a #LassoProfile
430  * @dump: XML session dump
431  *
432  * Builds a new #LassoSession object from XML dump and binds it to @profile.
433  *
434  * Return value: 0 on success; or a negative value otherwise.
435  **/
436 gint
lasso_profile_set_session_from_dump(LassoProfile * profile,const gchar * dump)437 lasso_profile_set_session_from_dump(LassoProfile *profile, const gchar *dump)
438 {
439 	g_return_val_if_fail(dump != NULL, LASSO_PARAM_ERROR_INVALID_VALUE);
440 
441 	lasso_assign_new_gobject(profile->session, lasso_session_new_from_dump(dump));
442 	if (profile->session == NULL)
443 		return critical_error(LASSO_PROFILE_ERROR_BAD_SESSION_DUMP);
444 
445 	IF_SAML2(profile) {
446 		lasso_saml20_profile_set_session_from_dump(profile);
447 	}
448 
449 	return 0;
450 }
451 
452 /**
453  * lasso_profile_get_artifact:
454  * @profile: a #LassoProfile object
455  *
456  * Return the artifact token
457  *
458  * Return value:(transfer full)(allow-none): a newly allocated string or NULL.
459  */
460 char*
lasso_profile_get_artifact(LassoProfile * profile)461 lasso_profile_get_artifact(LassoProfile *profile)
462 {
463 	return g_strdup(profile->private_data->artifact);
464 }
465 
466 /**
467  * lasso_profile_get_artifact_message:
468  * @profile: a #LassoProfile object
469  *
470  * Return the artifact message.
471  *
472  * Return value:(transfer full)(allow-none): a newly allocated string or NULL
473  */
474 char*
lasso_profile_get_artifact_message(LassoProfile * profile)475 lasso_profile_get_artifact_message(LassoProfile *profile)
476 {
477 	return g_strdup(profile->private_data->artifact_message);
478 }
479 
480 /**
481  * lasso_profile_set_artifact_message:
482  * @profile: a #LassoProfile object
483  * @message: the artifact message content
484  *
485  * Set @message as the content for the ArtifactResolve response.
486  *
487  */
488 void
lasso_profile_set_artifact_message(LassoProfile * profile,const char * message)489 lasso_profile_set_artifact_message(LassoProfile *profile, const char *message)
490 {
491 	if (! LASSO_IS_PROFILE(profile)) {
492 		message(G_LOG_LEVEL_CRITICAL, "set_artifact_message called on something not a" \
493 			"LassoProfile object: %p", profile);
494 		return;
495 	}
496 	lasso_assign_string(profile->private_data->artifact_message, message);
497 }
498 
499 /**
500  * lasso_profile_get_server:
501  * @profile: a #LassoProfile object
502  *
503  * Return the #LassoServer linked to this profile object. A profile object should always contains
504  * one. It allows to find metadatas of other providers and to know our own metadatas.
505  *
506  * Return value: (transfer none): a #LassoServer or NULL if profile is not a #LassoProfile or no
507  * #LassoServer object was setup at the creation of this profile.
508  */
509 LassoServer*
lasso_profile_get_server(LassoProfile * profile)510 lasso_profile_get_server(LassoProfile *profile)
511 {
512 	g_return_val_if_fail(LASSO_IS_PROFILE(profile), NULL);
513 
514 	if (profile->server) {
515 		if (LASSO_IS_SERVER(profile->server)) {
516 			return profile->server;
517 		} else {
518 			message(G_LOG_LEVEL_WARNING, "profile->server contains a non LassoServer object");
519 		}
520 	}
521 
522 	return NULL;
523 }
524 
525 
526 /**
527  * lasso_profile_get_message_id:
528  * @profile: a #LassoProfile object
529  *
530  * Return the messge ID.
531  *
532  * Return value:(transfer full)(allow-none): a newly allocated string or NULL
533  */
534 char*
lasso_profile_get_message_id(LassoProfile * profile)535 lasso_profile_get_message_id(LassoProfile *profile)
536 {
537 	return g_strdup(profile->private_data->message_id);
538 }
539 
540 /**
541  * lasso_profile_set_message_id:
542  * @profile: a #LassoProfile object
543  * @message_id: the message ID
544  *
545  * Set @message_id for the current conversation
546  *
547  */
548 void
lasso_profile_set_message_id(LassoProfile * profile,const char * message_id)549 lasso_profile_set_message_id(LassoProfile *profile, const char *message_id)
550 {
551 	if (! LASSO_IS_PROFILE(profile)) {
552 		message(G_LOG_LEVEL_CRITICAL, "set_message_id called on something not a" \
553 			"LassoProfile object: %p", profile);
554 		return;
555 	}
556 	lasso_assign_string(profile->private_data->message_id, message_id);
557 }
558 
559 /**
560  * lasso_profile_get_idp_list:
561  * @profile: a #LassoProfile object
562  *
563  * Return the messge ID.
564  *
565  * Return value: a #LassoNode, when using SAML 2.0 a #LassoSamlp2IDPList,
566  * when using ID-FF a #LassoLibIDPList.
567  */
568 LassoNode*
lasso_profile_get_idp_list(LassoProfile * profile)569 lasso_profile_get_idp_list(LassoProfile *profile)
570 {
571 	return profile->private_data->idp_list;
572 }
573 
574 /**
575  * lasso_profile_set_idp_list:
576  * @profile: a #LassoProfile object
577  * @idp_list: a #LassoNode, when using SAML 2.0 a #LassoSamlp2IDPList,
578  * when using ID-FF a #LassoLibIDPList.
579  *
580  * Set @idp_list for the current conversation
581  *
582  */
583 void
lasso_profile_set_idp_list(LassoProfile * profile,const LassoNode * idp_list)584 lasso_profile_set_idp_list(LassoProfile *profile, const LassoNode *idp_list)
585 {
586 	if (! LASSO_IS_PROFILE(profile)) {
587 		message(G_LOG_LEVEL_CRITICAL, "set_idp_list called on something not a" \
588 			"LassoProfile object: %p", profile);
589 		return;
590 	}
591 	lasso_assign_gobject(profile->private_data->idp_list, idp_list);
592 }
593 
594 /*****************************************************************************/
595 /* private methods                                                           */
596 /*****************************************************************************/
597 
598 static struct XmlSnippet schema_snippets[] = {
599 	{ "Request", SNIPPET_NODE_IN_CHILD, G_STRUCT_OFFSET(LassoProfile, request), NULL, NULL, NULL},
600 	{ "Response", SNIPPET_NODE_IN_CHILD, G_STRUCT_OFFSET(LassoProfile, response), NULL, NULL, NULL},
601 	{ "NameIdentifier", SNIPPET_NODE_IN_CHILD,
602 		G_STRUCT_OFFSET(LassoProfile, nameIdentifier), NULL, NULL, NULL},
603 	{ "RemoteProviderID", SNIPPET_CONTENT, G_STRUCT_OFFSET(LassoProfile, remote_providerID),
604 		NULL, NULL, NULL},
605 	{ "MsgUrl", SNIPPET_CONTENT, G_STRUCT_OFFSET(LassoProfile, msg_url), NULL, NULL, NULL},
606 	{ "MsgBody", SNIPPET_CONTENT, G_STRUCT_OFFSET(LassoProfile, msg_body), NULL, NULL, NULL},
607 	{ "MsgRelayState", SNIPPET_CONTENT, G_STRUCT_OFFSET(LassoProfile, msg_relayState), NULL,
608 		NULL, NULL},
609 	{ "HttpRequestMethod", SNIPPET_CONTENT | SNIPPET_INTEGER,
610 		G_STRUCT_OFFSET(LassoProfile, http_request_method), NULL, NULL, NULL},
611 	{ "Artifact", SNIPPET_CONTENT | SNIPPET_PRIVATE, G_STRUCT_OFFSET(LassoProfilePrivate,
612 			artifact), NULL, NULL, NULL },
613 	{ "ArtifactMessage", SNIPPET_CONTENT | SNIPPET_PRIVATE, G_STRUCT_OFFSET(LassoProfilePrivate,
614 			artifact_message), NULL, NULL, NULL },
615 	{NULL, 0, 0, NULL, NULL, NULL}
616 };
617 
618 static LassoNodeClass *parent_class = NULL;
619 
620 /**
621  * lasso_profile_set_signature_hint:
622  * @profile: a #LassoProfile object
623  * @signature_hint: wheter next produced messages should be signed or not (or let Lasso choose from
624  * implicit information).
625  *
626  * By default each profile will choose to sign or not its messages, this method allow to force or
627  * forbid the signature of messages, on a per transaction basis.
628  */
629 void
lasso_profile_set_signature_hint(LassoProfile * profile,LassoProfileSignatureHint signature_hint)630 lasso_profile_set_signature_hint(LassoProfile *profile, LassoProfileSignatureHint signature_hint)
631 {
632 	if (! LASSO_IS_PROFILE(profile) || ! profile->private_data)
633 		return;
634 	profile->private_data->signature_hint = signature_hint;
635 }
636 
637 /**
638  * lasso_profile_get_signature_hint:
639  * @profile: a #LassoProfile object
640  *
641  * Return the value of the signature hint attribute (see lasso_profile_set_signature_hint()).
642  *
643  * Return value: a value in the enum type #LassoProfileSignatureHint.
644  */
645 LassoProfileSignatureHint
lasso_profile_get_signature_hint(LassoProfile * profile)646 lasso_profile_get_signature_hint(LassoProfile *profile)
647 {
648 	LassoProfileSignatureVerifyHint signature_verify_hint;
649 	if (! LASSO_IS_PROFILE(profile) || ! profile->private_data)
650 		return LASSO_PROFILE_SIGNATURE_HINT_MAYBE;
651 	signature_verify_hint = profile->private_data->signature_verify_hint;
652 	if (signature_verify_hint >= LASSO_PROFILE_SIGNATURE_VERIFY_HINT_LAST) {
653 		message(G_LOG_LEVEL_WARNING, "%u is an invalid signature verify hint",
654 				signature_verify_hint);
655 		return LASSO_PROFILE_SIGNATURE_HINT_MAYBE;
656 	}
657 	return profile->private_data->signature_hint;
658 }
659 
660 /**
661  * lasso_profile_set_signature_verify_hint:
662  * @profile: a #LassoProfile object
663  * @signature_verify_hint: whether next received message signatures should be checked or not (or let
664  * Lasso choose from implicit information).
665  *
666  * By default each profile will choose to verify or not its messages, this method allow to force or
667  * forbid the signature of messages, on a per transaction basis.
668  */
669 void
lasso_profile_set_signature_verify_hint(LassoProfile * profile,LassoProfileSignatureVerifyHint signature_verify_hint)670 lasso_profile_set_signature_verify_hint(LassoProfile *profile,
671 		LassoProfileSignatureVerifyHint signature_verify_hint)
672 {
673 	if (! LASSO_IS_PROFILE(profile) || ! profile->private_data)
674 		return;
675 	if (signature_verify_hint >= LASSO_PROFILE_SIGNATURE_VERIFY_HINT_LAST) {
676 		message(G_LOG_LEVEL_WARNING, "%i is an invalid argument for " __FUNCTION__,
677 				signature_verify_hint);
678 		return;
679 	}
680 	profile->private_data->signature_verify_hint = signature_verify_hint;
681 }
682 
683 /**
684  * lasso_profile_get_signature_verify_hint:
685  * @profile: a #LassoProfile object
686  *
687  * Return the value of the signature verify hint attribute (see
688  * lasso_profile_set_signature_verify_hint()).
689  *
690  * Return value: a value in the enum type #LassoProfileSignatureVerifyHint.
691  */
692 LassoProfileSignatureVerifyHint
lasso_profile_get_signature_verify_hint(LassoProfile * profile)693 lasso_profile_get_signature_verify_hint(LassoProfile *profile)
694 {
695 	if (! LASSO_IS_PROFILE(profile) || ! profile->private_data)
696 		return LASSO_PROFILE_SIGNATURE_HINT_MAYBE;
697 	return profile->private_data->signature_verify_hint;
698 }
699 
700 
701 /**
702  * lasso_profile_set_soap_fault_response:
703  * @profile: a #LassoProfile object
704  * @faultcode: the code for the SOAP fault
705  * @faultstring:(allow-none): the description for the SOAP fault
706  * @details:(element-type LassoNode)(allow-none): a list of nodes to add as details
707  *
708  * Set the response to a SOAP fault, using @faultcode, @faultstring, and @details to initialize it.
709  *
710  * Return value: 0 if successful, an error code otherwise.
711  */
712 gint
lasso_profile_set_soap_fault_response(LassoProfile * profile,const char * faultcode,const char * faultstring,GList * details)713 lasso_profile_set_soap_fault_response(LassoProfile *profile, const char *faultcode,
714 		const char *faultstring, GList *details)
715 {
716 	LassoSoapFault *fault;
717 
718 	if (! LASSO_IS_SOAP_FAULT(profile->response)) {
719 		lasso_release_gobject(profile->response);
720 		profile->response = (LassoNode*)lasso_soap_fault_new();
721 	}
722 	fault = (LassoSoapFault*)profile->response;
723 	lasso_assign_string(fault->faultcode, faultcode);
724 	lasso_assign_string(fault->faultstring, faultstring);
725 	if (details) {
726 		if (! fault->Detail) {
727 			fault->Detail = lasso_soap_detail_new();
728 		}
729 		lasso_assign_list_of_gobjects(fault->Detail->any, details);
730 	} else {
731 		lasso_release_gobject(fault->Detail);
732 	}
733 	return 0;
734 }
735 
736 /**
737  * lasso_profile_sso_role_with:
738  * @profile: a #LassoProfile object
739  * @remote_provider_id: the identifier of a provider
740  *
741  * Returns whether the current provider is a service provider relatively to another provider. It
742  * uses the #LassoProfile.identity to find if a federation qualifier by the given provider exists or
743  * the reverse.
744  *
745  * Return value: #LASSO_PROVIDER_ROLE_NONE if nothing can be said, #LASSO_PROVIDER_ROLE_SP if a
746  * federation qualifier by @remote_provider_id exists or #LASSO_PROVIDER_ROLE_IDP if a federation
747  * qualifier by our own #LassoProvider.ProviderID exists.
748  */
lasso_profile_sso_role_with(LassoProfile * profile,const char * remote_provider_id)749 LassoProviderRole lasso_profile_sso_role_with(LassoProfile *profile, const char *remote_provider_id)
750 {
751 	LassoFederation *federation = NULL;
752 	const char *name_qualifier = NULL;
753 	const char *provider_id = NULL;
754 
755 
756 	g_return_val_if_fail(LASSO_IS_PROFILE(profile) && remote_provider_id,
757 			LASSO_PROVIDER_ROLE_NONE);
758 
759 	if (profile->server) {
760 		provider_id = profile->server->parent.ProviderID;
761 	}
762 
763 	federation = lasso_identity_get_federation(profile->identity, remote_provider_id);
764 	if (! federation)
765 		return LASSO_PROVIDER_ROLE_NONE;
766 
767 	/* coherency check */
768 	g_return_val_if_fail(lasso_strisequal(federation->remote_providerID,remote_provider_id),
769 			LASSO_PROVIDER_ROLE_NONE);
770 
771 	if (LASSO_IS_SAML2_NAME_ID(federation->local_nameIdentifier)) {
772 		LassoSaml2NameID *name_id = (LassoSaml2NameID*)federation->local_nameIdentifier;
773 		name_qualifier = name_id->NameQualifier;
774 	} else if (LASSO_IS_SAML_NAME_IDENTIFIER(federation->local_nameIdentifier)) {
775 		LassoSamlNameIdentifier *name_id;
776 
777 		name_id = (LassoSamlNameIdentifier*)federation->local_nameIdentifier;
778 		name_qualifier = name_id->NameQualifier;
779 	} else {
780 		message(G_LOG_LEVEL_WARNING, "a federation without a NameID was found");
781 		return LASSO_PROVIDER_ROLE_NONE;
782 	}
783 	if (lasso_strisequal(remote_provider_id,name_qualifier)) {
784 		return LASSO_PROVIDER_ROLE_SP;
785 	} else if (lasso_strisequal(provider_id,name_qualifier)) {
786 		return LASSO_PROVIDER_ROLE_IDP;
787 	}
788 	return LASSO_PROVIDER_ROLE_NONE;
789 }
790 
791 /**
792  * lasso_profile_get_signature_status:
793  * @profile: a #LassoProfile object
794  *
795  * Returns the signature status from the last parsed message.
796  *
797  * Return value: 0 if no error from signature checking occurred, an error code otherwise.
798  */
799 gint
lasso_profile_get_signature_status(LassoProfile * profile)800 lasso_profile_get_signature_status(LassoProfile *profile)
801 {
802 	lasso_bad_param(PROFILE, profile);
803 
804 	return profile->signature_status;
805 }
806 
807 static xmlChar *
extract_issuer(xmlTextReader * reader)808 extract_issuer(xmlTextReader *reader)
809 {
810 	const xmlChar *name;
811 	const xmlChar *ns_uri;
812 	xmlNode *node;
813 
814 	name = xmlTextReaderConstLocalName(reader);
815 	ns_uri = xmlTextReaderConstNamespaceUri(reader);
816 
817 	if (strcmp((const char*)name, "Issuer"))
818 		return NULL;
819 	if (strcmp((const char*)ns_uri, LASSO_SAML2_ASSERTION_HREF))
820 		return NULL;
821 	node = xmlTextReaderExpand(reader);
822 	return xmlNodeGetContent(node);
823 }
824 
825 
826 /**
827  * lasso_profile_get_issuer:
828  * @message: the HTTP query, POST content or SOAP message
829  *
830  * Extract the issuer of a message.
831  *
832  * Return value:(transfer full): Returns the issuer of the given message.
833  */
834 char*
lasso_profile_get_issuer(const char * message)835 lasso_profile_get_issuer(const char *message)
836 {
837 	xmlTextReader *reader;
838 	char *result = NULL;
839 	int count = 0, ret;
840 	xmlChar *xml_result = NULL;
841 	char *to_free = NULL;
842 
843 
844 	reader = lasso_xmltextreader_from_message(message, &to_free);
845 	if (! reader)
846 		goto cleanup;
847 	ret = xmlTextReaderRead(reader);
848 	while (ret == 1) {
849 		int node_type = xmlTextReaderNodeType(reader);
850 		if (node_type == 1) {
851 			count += 1;
852 			xml_result = extract_issuer(reader);
853 			if (xml_result)
854 				break;
855 		}
856 		if (count == 3) {
857 			break;
858 		}
859 		ret = xmlTextReaderRead(reader);
860 	}
861 	if (! xml_result)
862 		goto cleanup;
863 	result = g_strdup((char *)xml_result);
864 cleanup:
865 	if (xml_result)
866 		lasso_release_xml_string(xml_result);
867 	if (reader)
868 		xmlFreeTextReader(reader);
869 	if (to_free)
870 		lasso_release(to_free);
871 	return result;
872 }
873 
874 /**
875  * lasso_profile_get_request_id:
876  * @message: the HTTP query, POST content or SOAP message
877  *
878  * Extract the issuer of a message.
879  *
880  * Return value:(transfer full): Returns the issuer of the given message.
881  */
882 char*
lasso_profile_get_in_response_to(const char * message)883 lasso_profile_get_in_response_to(const char *message)
884 {
885 	xmlTextReader *reader;
886 	char *result = NULL;
887 	int ret;
888 	int node_type = 0;
889 	xmlChar *xml_result = NULL;
890 	char *to_free = NULL;
891 
892 
893 	reader = lasso_xmltextreader_from_message(message, &to_free);
894 	if (! reader)
895 		goto cleanup;
896 	ret = xmlTextReaderRead(reader);
897 	while (ret == 1) {
898 		node_type = xmlTextReaderNodeType(reader);
899 		if (node_type == 1) {
900 			break;
901 		}
902 		ret = xmlTextReaderRead(reader);
903 	}
904 	if (node_type != 1)
905 		goto cleanup;
906 	xml_result = xmlTextReaderGetAttribute(reader, BAD_CAST "InResponseTo");
907 	if (! xml_result)
908 		goto cleanup;
909 	result = g_strdup((char*)xml_result);
910 cleanup:
911 	if (reader)
912 		xmlFreeTextReader(reader);
913 	if (xml_result)
914 		lasso_release_xml_string(xml_result);
915 	if (to_free)
916 		lasso_release(to_free);
917 	return result;
918 }
919 
920 /*****************************************************************************/
921 /* overridden parent class methods                                           */
922 /*****************************************************************************/
923 
924 static void
dispose(GObject * object)925 dispose(GObject *object)
926 {
927 	LassoProfile *profile = LASSO_PROFILE(object);
928 
929 	if (profile->private_data->dispose_has_run) {
930 		return;
931 	}
932 	profile->private_data->dispose_has_run = TRUE;
933 
934 
935 	lasso_mem_debug("LassoProfile", "Server", profile->server);
936 	lasso_release_gobject(profile->server);
937 
938 	lasso_mem_debug("LassoProfile", "Identity", profile->identity);
939 	lasso_release_gobject(profile->identity);
940 
941 	lasso_mem_debug("LassoProfile", "Session", profile->session);
942 	lasso_release_gobject(profile->session);
943 
944 	lasso_release_string(profile->private_data->artifact);
945 	lasso_release_string(profile->private_data->artifact_message);
946 
947 	G_OBJECT_CLASS(parent_class)->dispose(G_OBJECT(profile));
948 
949 	lasso_release_gobject(profile->private_data->idp_list);
950 	lasso_release_string(profile->private_data->message_id);
951 }
952 
953 /*****************************************************************************/
954 /* instance and class init functions                                         */
955 /*****************************************************************************/
956 
957 static void
instance_init(LassoProfile * profile)958 instance_init(LassoProfile *profile)
959 {
960 	profile->private_data = LASSO_PROFILE_GET_PRIVATE(profile);
961 	profile->private_data->dispose_has_run = FALSE;
962 	profile->private_data->artifact = NULL;
963 	profile->private_data->artifact_message = NULL;
964 	profile->private_data->signature_hint = LASSO_PROFILE_SIGNATURE_HINT_MAYBE;
965 	profile->private_data->message_id = NULL;
966 	profile->private_data->idp_list = NULL;
967 
968 	profile->server = NULL;
969 	profile->request = NULL;
970 	profile->response = NULL;
971 	profile->nameIdentifier = NULL;
972 	profile->remote_providerID = NULL;
973 	profile->msg_url = NULL;
974 	profile->msg_body = NULL;
975 	profile->msg_relayState = NULL;
976 
977 	profile->identity = NULL;
978 	profile->session = NULL;
979 	profile->signature_status = 0;
980 }
981 
982 static void
class_init(LassoProfileClass * klass,void * unused G_GNUC_UNUSED)983 class_init(LassoProfileClass *klass, void *unused G_GNUC_UNUSED)
984 {
985 	LassoNodeClass *nclass = LASSO_NODE_CLASS(klass);
986 
987 	parent_class = g_type_class_peek_parent(klass);
988 	nclass->node_data = g_new0(LassoNodeClassData, 1);
989 	lasso_node_class_set_nodename(nclass, "Profile");
990 	lasso_node_class_set_ns(nclass, LASSO_LASSO_HREF, LASSO_LASSO_PREFIX);
991 	lasso_node_class_add_snippets(nclass, schema_snippets);
992 	g_type_class_add_private(klass, sizeof(LassoProfilePrivate));
993 
994 	G_OBJECT_CLASS(klass)->dispose = dispose;
995 }
996 
997 GType
lasso_profile_get_type()998 lasso_profile_get_type()
999 {
1000 	static GType this_type = 0;
1001 
1002 	if (!this_type) {
1003 		static const GTypeInfo this_info = {
1004 			sizeof(LassoProfileClass),
1005 			NULL,
1006 			NULL,
1007 			(GClassInitFunc) class_init,
1008 			NULL,
1009 			NULL,
1010 			sizeof(LassoProfile),
1011 			0,
1012 			(GInstanceInitFunc) instance_init,
1013 			NULL
1014 		};
1015 
1016 		this_type = g_type_register_static(LASSO_TYPE_NODE,
1017 				"LassoProfile", &this_info, 0);
1018 	}
1019 	return this_type;
1020 }
1021 
1022