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:login
26  * @short_description: Single Sign-On and Federation Profile
27  *
28  * The Single Sign On process allows a user to log in once to an identity
29  * provider (IdP), and to be then transparently loged in to the required
30  * service providers (SP) belonging to the IP "circle of trust".  Subordinating
31  * different identities of the same user within a circle of trust to a unique
32  * IP is called "Identity Federation".  The liberty Alliance specifications
33  * allows, thanks to this federation, strong and unique authentication coupled
34  * with control by the user of his personal informations. The explicit user
35  * agreement is necessary before proceeding to Identity Federation.
36  *
37  * <para>
38  * The service provider must implement the following process:
39  * <itemizedlist>
40  *  <listitem><para>creating an authentication request with
41  *  lasso_login_init_authn_request();</para></listitem>
42  *  <listitem><para>sending it to the identity provider with
43  *  lasso_login_build_authn_request_msg();</para></listitem>
44  *  <listitem><para>receiving and processing the answer:
45  *    <itemizedlist>
46  *      <listitem>either an authentication response with
47  *      lasso_login_process_authn_response_msg()</listitem>
48  *      <listitem>or an artifact with lasso_login_init_request() then sending the
49  *      request to the IdP with lasso_login_build_request_msg() and processing the
50  *      new answer with lasso_login_process_response_msg().</listitem>
51  *    </itemizedlist>
52  *    </para></listitem>
53  * </itemizedlist>
54  * </para>
55  *
56  * <para>Our first example shows how to initiate a request toward an ID-FF 1.2 or SAML 2.0 identity
57  * provider. It supposes that we already initialized a #LassoServer object with the metadatas or our
58  * provider (and its private key if we want to sign the request), and that we added the metadatas of
59  * the targetted IdP with the method lasso_server_add_provider().  </para>
60  *
61  * <example>
62  * <title>Service Provider Login URL</title>
63  * <programlisting>
64  * LassoLogin *login;
65  * int rc; // hold return codes
66  *
67  * login = lasso_login_new(server);
68  * rc = lasso_login_init_authn_request(login, "http://identity-provider-id/",
69  *                 LASSO_HTTP_METHOD_REDIRECT);
70  * if (rc != 0) {
71  *   ... // handle errors, most of them are related to bad initialization
72  * }
73  *
74  * // customize AuthnRequest
75  * // protocolProfile is the protocolProfile of the provider http://identity-provider-id/
76  * if (protocolProfile == LASSO_LIBERTY_1_2) {
77  *         LassoLibAuthnRequest *request = LASSO_LIB_AUTHN_REQUEST(LASSO_PROFILE(login)->request);
78  *         request->NameIDPolicy = strdup(LASSO_LIB_NAMEID_POLICY_TYPE_FEDERATED);
79  *         request->ForceAuthn = TRUE;
80  *         request->IsPassive = FALSE;
81  *         // tell the IdP how to return the response
82  *         request->ProtocolProfile = strdup(LASSO_LIB_PROTOCOL_PROFILE_BRWS_ART);
83  * } else if (protocolProfile == LASSO_SAML_2_0) {
84  *         LassoSamlp2AuthnRequest *request = LASSO_SAMLP2_AUTHN_REQUEST(LASSO_PROFILE(login)->request);
85  *         if (request->NameIDPolicy->Format) {
86  *                 g_free(request->NameIDPolicy->Format);
87  *         }
88  *         request->NameIDPolicy->Format = g_strdup(LASSO_NAME_IDENTIFIER_FORMAT_PERSISTENT);
89  *         // Allow creation of new federation
90  *         //
91  *         request->NameIDPolicy->AllowCreate = 1;
92  *         request->ForceAuthn = TRUE;
93  *         request->IsPassive = FALSE;
94  *         // tell the IdP how to return the response
95  *         if (request->ProtocolBinding) {
96  *                  g_free(request->ProtocolBinding);
97  *         }
98  *         // here we expect an artifact response, it could be post, redirect or PAOS.
99  *         request->ProtocolBinding = g_strdup(LASSO_SAML2_METADATA_BINDING_ARTIFACT);
100    }
101  * // Lasso will choose whether to sign the request by looking at the IdP
102  * // metadatas and at our metadatas, but you can always force him to sign or to
103  * // not sign using the method lasso_profile_set_signature_hint() on the
104  * // LassoLogin object.
105  *
106  * rc = lasso_login_build_authn_request_msg(login);
107  * if (rc != 0) {
108       .... // handle errors
109       // could be that the requested binding (POST, Redirect, etc..) is not supported (LASSO_PROFILE_ERROR_UNSUPPORTED_PROFILE)
110       // or that we could not sign the request (LASSO_PROFILE_ERROR_BUILDING_QUERY_FAILED).
111  * }
112  *
113  * // redirect user to identity provider
114    // we chose the Redirect binding, so we have to generate a redirect HTTP response to the URL returned by Lasso
115  * printf("Location: %s\n\nRedirected to IdP\n", LASSO_PROFILE(login)->msg_url);
116  * </programlisting>
117  * </example>
118  *
119  * <para>Next example shows how to receive the response from the identity
120  * provider for ID-FF 1.2.</para>
121  *
122  * <example>
123  * <title>Service Provider Assertion Consumer Service URL for ID-FF 1.2</title>
124  * <programlisting>
125  * LassoLogin *login;
126  * char *request_method = getenv("REQUEST_METHOD");
127  * char *artifact_msg = NULL, *lares = NULL, *lareq = NULL;
128  * char *name_identifier;
129  * lassoHttpMethod method;
130  * int rc = 0;
131  *
132  * login = lasso_login_new(server);
133  * if (strcmp(request_method, "GET") == 0) {
134  *         artifact_msg = getenv("QUERY_STRING");
135  *         method = LASSO_HTTP_METHOD_REDIRECT;
136  * } else {
137  *         // read submitted form; if it has a LAREQ field, put it in lareq,
138  *         // if it has a LARES field, put it in lares
139  *         if (lareq) {
140  *                 artifact_msg = lareq;
141  *         } else if (lares) {
142  *                 response_msg = lares;
143  *         } else {
144  *                 // bail out
145  *         }
146  *         method = LASSO_HTTP_METHOD_POST;
147  * }
148  *
149  * if (artifact_msg) {
150  *         // we received an artifact response,
151  *         // it means we did not really receive the response,
152  *         // only a token to redeem the real response from the identity
153  *         // provider through a SOAP resolution call
154  *         rc = lasso_login_init_request(login, artifact_msg, method);
155  *         if (rc != 0) {
156  *                   ... // handle errors
157  *                   // there is usually no error at this step, only
158  *                   // if the IdP response is malformed
159  *         }
160  *         rc = lasso_login_build_request_msg(login);
161  *         if (rc != 0) {
162  *                   ... // handle errors
163  *                   // as for AuthnRequest generation, it generally is caused
164  *                   // by a bad initialization like an impossibility to load
165  *                   // the private key.
166  *         }
167  *         // makes a SOAP call, soap_call is NOT a Lasso function
168  *         soap_answer_msg = soap_call(LASSO_PROFILE(login)->msg_url,
169  *                         LASSO_PROFILE(login)->msg_body);
170  *         rc = lasso_login_process_response_msg(login, soap_answer_msg);
171  *         if (rc != 0) {
172  *                   ... // handle errors
173  *                   // here you can know if the IdP refused the request,
174  *         }
175  * } else if (response_msg) {
176  *         lasso_login_process_authn_response_msg(login, response_msg);
177  * }
178  *
179  * // looks up name_identifier in local file, database, whatever and gets back
180  * // two things: identity_dump and session_dump
181  * name_identifier = LASSO_PROFILE(login)->nameIdentifier
182  * lasso_profile_set_identity_from_dump(LASSO_PROFILE(login), identity_dump);
183  * lasso_profile_set_session_from_dump(LASSO_PROFILE(login), session_dump);
184  *
185  * lasso_login_accept_sso(login);
186  *
187  * if (lasso_profile_is_identity_dirty(LASSO_PROFILE(login))) {
188  *         LassoIdentity *identity;
189  *         char *identity_dump;
190  *         identity = lasso_profile_get_identity(LASSO_PROFILE(login));
191  *         identity_dump = lasso_identity_dump(identity);
192  *         // record identity_dump in file, database...
193  * }
194  *
195  * if (lasso_profile_is_session_dirty(LASSO_PROFILE(login))) {
196  *         LassoSession *session;
197  *         char *session_dump;
198  *         session = lasso_profile_get_session(LASSO_PROFILE(login));
199  *         session_dump = lasso_session_dump(session);
200  *         // record session_dump in file, database...
201  * }
202  *
203  * // redirect user anywhere
204  * printf("Location: %s\n\nRedirected to site root\n", login->msg_url);
205  * </programlisting>
206  * </example>
207  *
208  * <para>The implement an IdP you must create a single sign-on service endpoint, the needed APIs for
209  * this are lasso_login_process_authn_request_msg(), lasso_login_validate_request_msg(),
210  * lasso_login_build_assertion(), lasso_login_build_authn_response_msg() and
211  * lasso_login_build_artifact_msg(). You will have to chose between
212  * lasso_login_build_authn_response_msg() and lasso_login_build_artifact_msg() depending on the
213  * requested protocol for the response by the service provider</para>
214  *
215  * <example>
216  * <title>Identity provider single sign-on service</title>
217  * <programlisting>
218  * LassoLogin *login;
219  * char *request_method = getenv("REQUEST_METHOD");
220  * char *artifact_msg = NULL, *lares = NULL, *lareq = NULL;
221  * char *name_identifier;
222  * lassoHttpMethod method;
223  * int rc = 0;
224  *
225  * login = lasso_login_new(server);
226  * if (strcmp(request_method, 'GET')) { // AuthnRequest send with the HTTP-Redirect binding
227  *     //
228  *     lasso_profile_set_signature_verify_hint(LASSO_PROFILE(login),
229  *             LASSO_PROFILE_SIGNATURE_VERIFY_HINT_FORCE);
230  *     rc = lasso_process_authn_request_msg(login, getenv("QUERY_STRING"));
231  *     if (rc != 0) {
232  *         // handle errors
233  *     }
234  *
235  *
236  * } else {
237  *
238  * </programlisting>
239  * </example>
240  *
241  */
242 
243 #include <xmlsec/base64.h>
244 
245 #include <config.h>
246 #include "lasso_config.h"
247 #include "../utils.h"
248 #include "../debug.h"
249 #include "login.h"
250 #include "provider.h"
251 #include "../xml/private.h"
252 #include "../xml/lib_authentication_statement.h"
253 #include "../xml/lib_subject.h"
254 #include "../xml/saml_advice.h"
255 #include "../xml/saml_attribute.h"
256 #include "../xml/saml_attribute_value.h"
257 #include "../xml/saml_audience_restriction_condition.h"
258 #include "../xml/saml_conditions.h"
259 #include "../xml/samlp_response.h"
260 #include "../xml/saml-2.0/saml2_encrypted_element.h"
261 #include "../xml/misc_text_node.h"
262 
263 
264 #include "profileprivate.h"
265 #include "providerprivate.h"
266 #include "serverprivate.h"
267 #include "sessionprivate.h"
268 #include "identityprivate.h"
269 #include "loginprivate.h"
270 #include "../saml-2.0/loginprivate.h"
271 #include "../lasso_config.h"
272 
273 #ifdef LASSO_WSF_ENABLED
274 #include "../id-wsf/id_ff_extensions_private.h"
275 #endif
276 
277 #define LASSO_LOGIN_GET_PRIVATE(o) \
278 	   (G_TYPE_INSTANCE_GET_PRIVATE ((o), LASSO_TYPE_LOGIN, LassoLoginPrivate))
279 
280 
281 static void lasso_login_build_assertion_artifact(LassoLogin *login);
282 
283 /*****************************************************************************/
284 /* static methods/functions */
285 /*****************************************************************************/
286 
287 /**
288  * lasso_login_build_assertion:
289  * @login: a #LassoLogin
290  * @authenticationMethod: the authentication method
291  * @authenticationInstant: the time at which the authentication took place
292  * @notBefore: the earliest time instant at which the assertion is valid
293  * @notOnOrAfter: the time instant at which the assertion has expired
294  *
295  * Builds an assertion and stores it in profile session.
296  * @authenticationInstant, reauthenticateOnOrAfter, @notBefore and
297  * @notOnOrAfter may be NULL.  If @authenticationInstant is NULL, the current
298  * time will be used.  Time values must be encoded in UTC.
299  *
300  * Construct the authentication assertion for the response. It must be called after validating the
301  * request using lasso_login_validate_request_msg(). The created assertion is accessed using
302  * lasso_login_get_assertion().
303  *
304  * Return value: 0 on success; or
305  * <itemizedlist>
306  * <listitem><para>
307  * #LASSO_PARAM_ERROR_BAD_TYPE_OR_NULL_OBJ if login is not a #LassoLogin object,
308  * </para></listitem>
309  * <listitem><para>
310  * #LASSO_PROFILE_ERROR_IDENTITY_NOT_FOUND if no identity object was found in the login profile object.
311  * </para></listitem>
312  * <listitem><para>
313  * #LASSO_PROFILE_ERROR_MISSING_RESPONSE if no response object is present ( it is normally initialized
314  * by lasso_login_process_authn_request_msg() )
315  * </para></listitem>
316  * <listitem><para>
317  * #LASSO_PROFILE_ERROR_FEDERATION_NOT_FOUND if a #LASSO_SAML2_NAME_IDENTIFIER_FORMAT_PERSISTENT or #LASSO_SAML2_NAME_IDENTIFIER_FORMAT_ENCRYPTED NameID format is asked and no corresponding federation was found in the #LassoIdentity object,
318  * </para></listitem>
319  * <listitem><para>
320  * #LASSO_SERVER_ERROR_PROVIDER_NOT_FOUND if encryption is needed and the request issuing provider is unknown (it as not been registered in the #LassoServer object),
321  * </para></listitem>
322  * <listitem><para>
323  * #LASSO_DS_ERROR_ENCRYPTION_FAILED if encryption is needed but it failed,
324  * </para></listitem>
325  * </itemizedlist>
326  *
327  **/
328 int
lasso_login_build_assertion(LassoLogin * login,const char * authenticationMethod,const char * authenticationInstant,const char * reauthenticateOnOrAfter,const char * notBefore,const char * notOnOrAfter)329 lasso_login_build_assertion(LassoLogin *login,
330 		const char *authenticationMethod,
331 		const char *authenticationInstant,
332 		const char *reauthenticateOnOrAfter,
333 		const char *notBefore,
334 		const char *notOnOrAfter)
335 {
336 	LassoSamlAssertion *assertion;
337 	LassoLibAuthenticationStatement *as;
338 	LassoSamlNameIdentifier *nameIdentifier = NULL;
339 	LassoProfile *profile;
340 	LassoFederation *federation;
341 	LassoProvider *provider = NULL;
342 	LassoSaml2EncryptedElement *encrypted_element = NULL;
343 	LassoSamlSubjectStatementAbstract *ss;
344 	lasso_error_t rc = 0;
345 
346 	g_return_val_if_fail(LASSO_IS_LOGIN(login), LASSO_PARAM_ERROR_BAD_TYPE_OR_NULL_OBJ);
347 
348 	profile = LASSO_PROFILE(login);
349 
350 	if (profile->identity == NULL)
351 		return LASSO_PROFILE_ERROR_IDENTITY_NOT_FOUND;
352 
353 	IF_SAML2(profile) {
354 		return lasso_saml20_login_build_assertion(login,
355 				authenticationMethod, authenticationInstant,
356 				notBefore, notOnOrAfter);
357 	}
358 
359 	federation = g_hash_table_lookup(profile->identity->federations,
360 			profile->remote_providerID);
361 
362 	assertion = LASSO_SAML_ASSERTION(lasso_lib_assertion_new_full(
363 			LASSO_PROVIDER(profile->server)->ProviderID,
364 			LASSO_SAMLP_REQUEST_ABSTRACT(profile->request)->RequestID,
365 			profile->remote_providerID, notBefore, notOnOrAfter));
366 
367 	if (strcmp(login->nameIDPolicy, LASSO_LIB_NAMEID_POLICY_TYPE_ONE_TIME) == 0 ||
368 			federation == NULL) {
369 		/* if NameIDPolicy is 'onetime', don't use a federation */
370 		nameIdentifier = lasso_saml_name_identifier_new();
371 		lasso_assign_new_string(nameIdentifier->content, lasso_build_unique_id(32));
372 		lasso_assign_string(nameIdentifier->NameQualifier,
373 				LASSO_PROVIDER(profile->server)->ProviderID);
374 		lasso_assign_string(nameIdentifier->Format,
375 				LASSO_LIB_NAME_IDENTIFIER_FORMAT_ONE_TIME);
376 
377 		as = lasso_lib_authentication_statement_new_full(authenticationMethod,
378 				authenticationInstant, reauthenticateOnOrAfter,
379 				NULL, nameIdentifier);
380 		lasso_assign_new_gobject(profile->nameIdentifier, LASSO_NODE(nameIdentifier));
381 	} else {
382 		as = lasso_lib_authentication_statement_new_full(authenticationMethod,
383 				authenticationInstant, reauthenticateOnOrAfter,
384 				LASSO_SAML_NAME_IDENTIFIER(federation->remote_nameIdentifier),
385 				LASSO_SAML_NAME_IDENTIFIER(federation->local_nameIdentifier));
386 	}
387 
388 	/* Encrypt NameID */
389 	provider = lasso_server_get_provider(profile->server, profile->remote_providerID);
390 	ss = LASSO_SAML_SUBJECT_STATEMENT_ABSTRACT(as);
391 	if (provider
392 			&& (lasso_provider_get_encryption_mode(provider) & LASSO_ENCRYPTION_MODE_NAMEID)) {
393 		encrypted_element = LASSO_SAML2_ENCRYPTED_ELEMENT(lasso_node_encrypt(
394 					LASSO_NODE(ss->Subject->NameIdentifier),
395 					lasso_provider_get_encryption_public_key(provider),
396 					lasso_provider_get_encryption_sym_key_type(provider),
397 					provider->ProviderID));
398 		if (encrypted_element != NULL) {
399 			lasso_assign_new_gobject(ss->Subject->EncryptedNameIdentifier, encrypted_element);
400 			lasso_release_gobject(ss->Subject->NameIdentifier);
401 		}
402 	}
403 
404 	/* add session index */
405 	if (lasso_provider_get_first_http_method(&login->parent.server->parent,
406 				provider, LASSO_MD_PROTOCOL_TYPE_SINGLE_LOGOUT) != LASSO_HTTP_METHOD_NONE) {
407 		lasso_assign_string(as->SessionIndex, assertion->AssertionID);
408 	}
409 
410 	assertion->AuthenticationStatement = LASSO_SAML_AUTHENTICATION_STATEMENT(as);
411 
412 	/* Save signing material in assertion private datas to be able to sign later */
413 	lasso_check_good_rc(lasso_server_set_signature_for_provider_by_name(login->parent.server,
414 			profile->remote_providerID, (LassoNode*)assertion));
415 
416 	lasso_list_add_gobject(LASSO_SAMLP_RESPONSE(profile->response)->Assertion,
417 			assertion);
418 
419 #ifdef LASSO_WSF_ENABLED
420 	lasso_login_assertion_add_discovery(login, assertion);
421 #endif
422 
423 	/* store assertion in session object */
424 	if (profile->session == NULL) {
425 		profile->session = lasso_session_new();
426 	}
427 	lasso_assign_gobject(login->assertion, LASSO_SAML_ASSERTION(assertion));
428 	lasso_check_good_rc(lasso_session_add_assertion(profile->session, profile->remote_providerID,
429 			LASSO_NODE(assertion)));
430 
431 	if (LASSO_SAMLP_REQUEST_ABSTRACT(profile->request)->MajorVersion == 1 &&
432 			LASSO_SAMLP_REQUEST_ABSTRACT(profile->request)->MinorVersion < 2) {
433 		/* pre-id-ff 1.2, saml 1.0 */
434 
435 		/* needs assertion artifact */
436 		lasso_login_build_assertion_artifact(login);
437 
438 		assertion->MinorVersion = 0;
439 
440 		ss = LASSO_SAML_SUBJECT_STATEMENT_ABSTRACT(assertion->AuthenticationStatement);
441 		ss->Subject = LASSO_SAML_SUBJECT(lasso_saml_subject_new());
442 		ss->Subject->NameIdentifier = LASSO_SAML_NAME_IDENTIFIER(g_object_ref(profile->nameIdentifier));
443 		ss->Subject->SubjectConfirmation = lasso_saml_subject_confirmation_new();
444 		/* liberty-architecture-bindings-profiles-v1.1.pdf, page 24, line 729 */
445 		lasso_list_add_string(ss->Subject->SubjectConfirmation->ConfirmationMethod,
446 			LASSO_SAML_CONFIRMATION_METHOD_ARTIFACT01);
447 		lasso_assign_string(ss->Subject->SubjectConfirmation->SubjectConfirmationData,
448 			login->assertionArtifact);
449 
450 		if (nameIdentifier) {
451 			/* draft-liberty-idff-protocols-schemas-1.2-errata-v2.0.pdf */
452 			lasso_release_string(nameIdentifier->NameQualifier);
453 			lasso_release_string(nameIdentifier->Format);
454 		}
455 	}
456 
457 cleanup:
458 	lasso_release_gobject(assertion);
459 	return rc;
460 }
461 
462 /**
463  * lasso_login_must_ask_for_consent_private:
464  * @login: a #LassoLogin
465  *
466  * Evaluates if it is necessary to ask the consent of the Principal.
467  * This method doesn't take the isPassive value into account.
468  *
469  * Return value: TRUE if consent should be asked, FALSE otherwise.
470  **/
471 static gboolean
lasso_login_must_ask_for_consent_private(LassoLogin * login)472 lasso_login_must_ask_for_consent_private(LassoLogin *login)
473 {
474 	char *nameIDPolicy, *consent;
475 	LassoProfile *profile = LASSO_PROFILE(login);
476 	LassoFederation *federation = NULL;
477 
478 	nameIDPolicy = LASSO_LIB_AUTHN_REQUEST(profile->request)->NameIDPolicy;
479 
480 	if (nameIDPolicy == NULL || strcmp(nameIDPolicy, LASSO_LIB_NAMEID_POLICY_TYPE_NONE) == 0)
481 		return FALSE;
482 
483 	if (strcmp(nameIDPolicy, LASSO_LIB_NAMEID_POLICY_TYPE_ONE_TIME) == 0)
484 		return FALSE;
485 
486 	if (strcmp(nameIDPolicy, LASSO_LIB_NAMEID_POLICY_TYPE_FEDERATED) != 0 &&
487 			strcmp(nameIDPolicy, LASSO_LIB_NAMEID_POLICY_TYPE_ANY) != 0) {
488 		message(G_LOG_LEVEL_CRITICAL, "Unknown NameIDPolicy: %s", nameIDPolicy);
489 		/* NameIDPolicy is considered empty (None value) if its value is unknown/invalid */
490 		return TRUE;
491 	}
492 
493 	if (profile->identity != NULL) {
494 		federation = g_hash_table_lookup(profile->identity->federations,
495 				profile->remote_providerID);
496 		if (federation)
497 			return FALSE;
498 	}
499 
500 	consent = LASSO_LIB_AUTHN_REQUEST(profile->request)->consent;
501 	if (consent == NULL)
502 		return TRUE;
503 
504 	if (strcmp(consent, LASSO_LIB_CONSENT_OBTAINED) == 0)
505 		return FALSE;
506 
507 	if (strcmp(consent, LASSO_LIB_CONSENT_OBTAINED_PRIOR) == 0)
508 		return FALSE;
509 
510 	if (strcmp(consent, LASSO_LIB_CONSENT_OBTAINED_CURRENT_IMPLICIT) == 0)
511 		return FALSE;
512 
513 	if (strcmp(consent, LASSO_LIB_CONSENT_OBTAINED_CURRENT_EXPLICIT) == 0)
514 		return FALSE;
515 
516 	if (strcmp(consent, LASSO_LIB_CONSENT_UNAVAILABLE) == 0)
517 		return TRUE;
518 
519 	if (strcmp(consent, LASSO_LIB_CONSENT_INAPPLICABLE) == 0)
520 		return TRUE;
521 
522 	message(G_LOG_LEVEL_CRITICAL, "Unknown consent value: %s", consent);
523 	/* we consider consent as empty if its value is unknown/invalid */
524 	return TRUE;
525 }
526 
527 /**
528  * lasso_login_process_federation:
529  * @login: a #LassoLogin
530  * @is_consent_obtained: whether user consent has been obtained
531  *
532  * Return value: 0 on success; or a negative value otherwise.
533  **/
534 static gint
lasso_login_process_federation(LassoLogin * login,gboolean is_consent_obtained)535 lasso_login_process_federation(LassoLogin *login, gboolean is_consent_obtained)
536 {
537 	LassoFederation *federation = NULL;
538 	LassoProfile *profile;
539 	char *nameIDPolicy;
540 	gint ret = 0;
541 
542 	g_return_val_if_fail(LASSO_IS_LOGIN(login), LASSO_PARAM_ERROR_BAD_TYPE_OR_NULL_OBJ);
543 
544 	profile = LASSO_PROFILE(login);
545 
546 	/* verify if identity already exists else create it */
547 	if (profile->identity == NULL) {
548 		profile->identity = lasso_identity_new();
549 	}
550 
551 	/* get nameIDPolicy in lib:AuthnRequest */
552 	nameIDPolicy = LASSO_LIB_AUTHN_REQUEST(profile->request)->NameIDPolicy;
553 	if (nameIDPolicy == NULL)
554 		nameIDPolicy = LASSO_LIB_NAMEID_POLICY_TYPE_NONE;
555 	lasso_assign_string(login->nameIDPolicy, nameIDPolicy);
556 
557 	/* if nameIDPolicy is 'onetime' => nothing to do */
558 	if (strcmp(nameIDPolicy, LASSO_LIB_NAMEID_POLICY_TYPE_ONE_TIME) == 0) {
559 		return 0;
560 	}
561 
562 	/* search a federation in the identity */
563 	federation = g_hash_table_lookup(profile->identity->federations,
564 			profile->remote_providerID);
565 
566 	if (strcmp(nameIDPolicy, LASSO_LIB_NAMEID_POLICY_TYPE_NONE) == 0) {
567 		/* a federation MUST exist */
568 		if (federation == NULL) {
569 			/* if protocolProfile is LASSO_LOGIN_PROTOCOL_PROFILE_BRWS_POST
570 			 * set StatusCode to FederationDoesNotExist in lib:AuthnResponse
571 			 */
572 			lasso_profile_set_response_status(LASSO_PROFILE(login),
573 					LASSO_LIB_STATUS_CODE_FEDERATION_DOES_NOT_EXIST);
574 			return LASSO_LOGIN_ERROR_FEDERATION_NOT_FOUND;
575 		}
576 
577 		lasso_assign_gobject(LASSO_PROFILE(login)->nameIdentifier,
578 			LASSO_SAML_NAME_IDENTIFIER(federation->local_nameIdentifier));
579 		return 0;
580 	}
581 
582 	if (strcmp(nameIDPolicy, LASSO_LIB_NAMEID_POLICY_TYPE_FEDERATED) != 0 &&
583 			strcmp(nameIDPolicy, LASSO_LIB_NAMEID_POLICY_TYPE_ANY) != 0) {
584 		return critical_error(LASSO_LOGIN_ERROR_INVALID_NAMEIDPOLICY);
585 	}
586 
587 	/* consent is necessary, it should be obtained via consent attribute
588 	 * in lib:AuthnRequest or IDP should ask the Principal
589 	 */
590 	if (lasso_login_must_ask_for_consent_private(login) && !is_consent_obtained) {
591 		if (strcmp(nameIDPolicy, LASSO_LIB_NAMEID_POLICY_TYPE_ANY) == 0) {
592 			/* if the NameIDPolicy element is 'any' and if the policy
593 			 * for the Principal forbids federation, then evaluation
594 			 * MAY proceed as if the value was 'onetime'.
595 			 */
596 			lasso_assign_string(login->nameIDPolicy, LASSO_LIB_NAMEID_POLICY_TYPE_ONE_TIME);
597 			return 0;
598 		}
599 
600 		/* if protocolProfile is LASSO_LOGIN_PROTOCOL_PROFILE_BRWS_POST
601 		 * set StatusCode to FederationDoesNotExist in lib:AuthnResponse
602 		 */
603 		/* FIXME : is it the correct value for the StatusCode ? */
604 		lasso_profile_set_response_status(LASSO_PROFILE(login),
605 				LASSO_LIB_STATUS_CODE_FEDERATION_DOES_NOT_EXIST);
606 		return LASSO_LOGIN_ERROR_CONSENT_NOT_OBTAINED;
607 	}
608 
609 	if (federation == NULL) {
610 		federation = lasso_federation_new(LASSO_PROFILE(login)->remote_providerID);
611 		lasso_federation_build_local_name_identifier(federation,
612 				LASSO_PROVIDER(LASSO_PROFILE(login)->server)->ProviderID,
613 				LASSO_LIB_NAME_IDENTIFIER_FORMAT_FEDERATED,
614 				NULL);
615 		lasso_identity_add_federation(LASSO_PROFILE(login)->identity, federation);
616 	}
617 
618 	lasso_assign_gobject(LASSO_PROFILE(login)->nameIdentifier,
619 		LASSO_SAML_NAME_IDENTIFIER(federation->local_nameIdentifier));
620 
621 	return ret;
622 }
623 
624 static gint
lasso_login_process_response_status_and_assertion(LassoLogin * login)625 lasso_login_process_response_status_and_assertion(LassoLogin *login)
626 {
627 	LassoProvider *idp;
628 	LassoSamlpResponse *response;
629 	char *status_value;
630 	LassoSamlSubjectStatementAbstract *sssa = NULL;
631 	LassoSamlSubjectStatementAbstract *sas = NULL;
632 	int rc = 0;
633 
634 	g_return_val_if_fail(LASSO_IS_LOGIN(login), LASSO_PARAM_ERROR_BAD_TYPE_OR_NULL_OBJ);
635 
636 	response = LASSO_SAMLP_RESPONSE(LASSO_PROFILE(login)->response);
637 
638 	if (response->Status == NULL || ! LASSO_IS_SAMLP_STATUS(response->Status) ||
639 			response->Status->StatusCode == NULL ||
640 			response->Status->StatusCode->Value == NULL) {
641 		return LASSO_PROFILE_ERROR_MISSING_STATUS_CODE;
642 	}
643 
644 	status_value = response->Status->StatusCode->Value;
645 	if (status_value && strcmp(status_value, LASSO_SAML_STATUS_CODE_SUCCESS) != 0) {
646 		if (strcmp(status_value, LASSO_SAML_STATUS_CODE_REQUEST_DENIED) == 0)
647 			return LASSO_LOGIN_ERROR_REQUEST_DENIED;
648 		if (strcmp(status_value, LASSO_SAML_STATUS_CODE_RESPONDER) == 0) {
649 			/* samlp:Responder */
650 			if (response->Status->StatusCode->StatusCode &&
651 					response->Status->StatusCode->StatusCode->Value) {
652 				status_value = response->Status->StatusCode->StatusCode->Value;
653 				if (strcmp(status_value,
654 					LASSO_LIB_STATUS_CODE_FEDERATION_DOES_NOT_EXIST) == 0) {
655 					return LASSO_LOGIN_ERROR_FEDERATION_NOT_FOUND;
656 				}
657 				if (strcmp(status_value,
658 						LASSO_LIB_STATUS_CODE_UNKNOWN_PRINCIPAL) == 0) {
659 					return LASSO_LOGIN_ERROR_UNKNOWN_PRINCIPAL;
660 				}
661 			}
662 		}
663 		return LASSO_LOGIN_ERROR_STATUS_NOT_SUCCESS;
664 	}
665 
666 	if (response->Assertion) {
667 		LassoProfile *profile = LASSO_PROFILE(login);
668 		LassoSamlAssertion *assertion = response->Assertion->data;
669 		LassoLibAssertion *lib_assertion = NULL;
670 
671 		if (LASSO_IS_LIB_ASSERTION(assertion)) {
672 			lib_assertion = LASSO_LIB_ASSERTION(assertion);
673 		}
674 
675 		idp = lasso_server_get_provider(profile->server, profile->remote_providerID);
676 		if (idp == NULL) {
677 			return LASSO_SERVER_ERROR_PROVIDER_NOT_FOUND;
678 		}
679 
680 		/* Validate AuthnRequest RequestID and InResponseTo */
681 		{
682 			char *previous_reqid = login->private_data->request_id;
683 			if (previous_reqid) {
684 				if (lib_assertion == NULL ||
685 					lib_assertion->InResponseTo == NULL ||
686 					strcmp(lib_assertion->InResponseTo, previous_reqid) != 0) {
687 					return critical_error(LASSO_LOGIN_ERROR_ASSERTION_DOES_NOT_MATCH_REQUEST_ID);
688 				}
689 			}
690 		}
691 
692 		/* If the status of the signature verification process is not 0, we try to verify on
693 		 * the assertion */
694 		if (profile->signature_status != 0) {
695 			xmlNode *assertion_xmlnode;
696 			gchar *assertion_issuer;
697 
698 			assertion_xmlnode = lasso_node_get_original_xmlnode(LASSO_NODE(assertion));
699 			assertion_issuer = (gchar*)xmlGetProp(assertion_xmlnode, (xmlChar*)"Issuer");
700 			goto_cleanup_if_fail_with_rc(assertion_issuer, LASSO_PROFILE_ERROR_MISSING_ISSUER);
701 			goto_cleanup_if_fail_with_rc(strcmp(assertion_issuer, profile->remote_providerID) == 0,
702 					LASSO_PROFILE_ERROR_INVALID_ISSUER);
703 
704 			if (assertion_xmlnode) {
705 				profile->signature_status = lasso_provider_verify_saml_signature(idp, assertion_xmlnode, NULL);
706 				goto_cleanup_if_fail_with_rc(profile->signature_status == 0, profile->signature_status);
707 			}
708 		}
709 
710 		lasso_release_gobject(profile->nameIdentifier);
711 
712 		/* Retrieve the name identifier from one of the statements */
713 		if (assertion->AuthenticationStatement) {
714 			sssa = LASSO_SAML_SUBJECT_STATEMENT_ABSTRACT(
715 					assertion->AuthenticationStatement);
716 			if (sssa->Subject && sssa->Subject->NameIdentifier) {
717 				lasso_assign_gobject(profile->nameIdentifier,
718 						LASSO_NODE(sssa->Subject->NameIdentifier));
719 			}
720 		}
721 
722 		if (profile->nameIdentifier == NULL && assertion->AttributeStatement) {
723 			sas = LASSO_SAML_SUBJECT_STATEMENT_ABSTRACT(assertion->AttributeStatement);
724 			if (sas->Subject && sas->Subject->NameIdentifier) {
725 				lasso_assign_gobject(profile->nameIdentifier,
726 						LASSO_NODE(sas->Subject->NameIdentifier));
727 			}
728 		}
729 
730 		if (profile->nameIdentifier == NULL) {
731 			return LASSO_PROFILE_ERROR_NAME_IDENTIFIER_NOT_FOUND;
732 		}
733 
734 	}
735 cleanup:
736 
737 	return rc;
738 }
739 
740 /*****************************************************************************/
741 /* public methods */
742 /*****************************************************************************/
743 
744 /**
745  * lasso_login_accept_sso:
746  * @login: a #LassoLogin
747  *
748  * Gets the assertion of the response and adds it to the #LassoSession object.
749  * Builds a federation with the 2 name identifiers of the assertion
750  * and adds it into the identity.
751  * If the session or the identity are NULL, they are created.
752  *
753  * Return value: 0 on success; or
754  * <itemizedlist>
755  * <listitem><para>
756  * #LASSO_PARAM_ERROR_BAD_TYPE_OR_NULL_OBJ if login is not a #LassoLogin object,
757  * </para></listitem>
758  * <listitem><para>
759  * #LASSO_PROFILE_ERROR_MISSING_RESPONSE if no response is present in the login profile object;
760  * usually because no call to lasso_login_process_authn_response_msg was done;
761  * </para></listitem>
762  * <listitem><para>
763  * #LASSO_PROFILE_ERROR_MISSING_ASSERTION if the response does not contain an assertion,
764  * </para></listitem>
765  * <listitem><para>
766  * #LASSO_PROFILE_ERROR_NAME_IDENTIFIER_NOT_FOUND if the assertion does not contain a NameID element,
767  * </para></listitem>
768  * <listitem><para>
769  * #LASSO_PROFILE_ERROR_MISSING_NAME_IDENTIFIER same as
770  * #LASSO_PROFILE_ERROR_NAME_IDENTIFIER_NOT_FOUND,
771  * </para></listitem>
772  * <listitem><para>
773  * #LASSO_LOGIN_ERROR_ASSERTION_REPLAY if the assertion has already been used.
774  * </para></listitem>
775  * </itemizedlist>
776  **/
777 gint
lasso_login_accept_sso(LassoLogin * login)778 lasso_login_accept_sso(LassoLogin *login)
779 {
780 	LassoProfile *profile;
781 	LassoSamlAssertion *assertion;
782 	LassoSamlNameIdentifier *ni, *idp_ni = NULL;
783 	LassoFederation *federation;
784 	LassoSamlSubjectStatementAbstract *authentication_statement;
785 
786 	g_return_val_if_fail(LASSO_IS_LOGIN(login), LASSO_PARAM_ERROR_BAD_TYPE_OR_NULL_OBJ);
787 
788 	profile = LASSO_PROFILE(login);
789 
790 	if (profile->identity == NULL)
791 		profile->identity = lasso_identity_new();
792 
793 	if (profile->session == NULL)
794 		profile->session = lasso_session_new();
795 
796 	if (profile->response == NULL)
797 		return LASSO_PROFILE_ERROR_MISSING_RESPONSE;
798 
799 	IF_SAML2(profile) {
800 		return lasso_saml20_login_accept_sso(login);
801 	}
802 
803 	if (LASSO_SAMLP_RESPONSE(profile->response)->Assertion == NULL)
804 		return LASSO_PROFILE_ERROR_MISSING_ASSERTION;
805 
806 	assertion = LASSO_SAMLP_RESPONSE(profile->response)->Assertion->data;
807 	if (assertion == NULL)
808 		return LASSO_PROFILE_ERROR_MISSING_ASSERTION;
809 
810 	lasso_session_add_assertion(profile->session, profile->remote_providerID,
811 			LASSO_NODE(assertion));
812 
813 	authentication_statement = LASSO_SAML_SUBJECT_STATEMENT_ABSTRACT(
814 			assertion->AuthenticationStatement);
815 	if (authentication_statement->Subject == NULL)
816 		return LASSO_PROFILE_ERROR_NAME_IDENTIFIER_NOT_FOUND;
817 
818 	ni = authentication_statement->Subject->NameIdentifier;
819 
820 	if (ni == NULL)
821 		return LASSO_PROFILE_ERROR_NAME_IDENTIFIER_NOT_FOUND;
822 
823 	if (LASSO_IS_LIB_SUBJECT(authentication_statement->Subject)) {
824 		idp_ni = LASSO_LIB_SUBJECT(
825 				authentication_statement->Subject)->IDPProvidedNameIdentifier;
826 	}
827 
828 	/* create federation, only if nameidentifier format is Federated */
829 	if (ni->Format && strcmp(ni->Format, LASSO_LIB_NAME_IDENTIFIER_FORMAT_FEDERATED) == 0) {
830 		federation = lasso_federation_new(LASSO_PROFILE(login)->remote_providerID);
831 		if (ni != NULL && idp_ni != NULL) {
832 			federation->local_nameIdentifier = LASSO_NODE(g_object_ref(ni));
833 			federation->remote_nameIdentifier = LASSO_NODE(g_object_ref(idp_ni));
834 		} else {
835 			federation->remote_nameIdentifier = LASSO_NODE(g_object_ref(ni));
836 		}
837 		/* add federation in identity */
838 		lasso_identity_add_federation(LASSO_PROFILE(login)->identity, federation);
839 	}
840 
841 	return 0;
842 }
843 
844 static void
lasso_login_build_assertion_artifact(LassoLogin * login)845 lasso_login_build_assertion_artifact(LassoLogin *login)
846 {
847 	xmlSecByte samlArt[42], *b64_samlArt;
848 	char *identityProviderSuccinctID;
849 
850 	identityProviderSuccinctID = lasso_sha1(
851 			LASSO_PROVIDER(LASSO_PROFILE(login)->server)->ProviderID);
852 
853 	/* Artifact Format is described in "Binding Profiles", 3.2.2.2. */
854 	memcpy(samlArt, "\000\003", 2); /* type code */
855 	memcpy(samlArt+2, identityProviderSuccinctID, 20);
856 	lasso_build_random_sequence((char*)samlArt+22, 20);
857 
858 	xmlFree(identityProviderSuccinctID);
859 	b64_samlArt = xmlSecBase64Encode(samlArt, 42, 0);
860 
861 	lasso_assign_string(login->assertionArtifact, (char*)b64_samlArt);
862 	lasso_assign_string(login->parent.private_data->artifact,
863 			(char*)b64_samlArt);
864 	lasso_release_xml_string(b64_samlArt);
865 }
866 
867 /**
868  * lasso_login_build_artifact_msg:
869  * @login: a #LassoLogin
870  * @http_method: the HTTP method to send the artifact (REDIRECT or POST)
871  *
872  * Builds a SAML artifact. Depending of the HTTP method, the data for the sending of
873  * the artifact are stored in @msg_url (REDIRECT) or @msg_url, @msg_body and
874  * @msg_relayState (POST).
875  *
876  * Return value: 0 on success; or
877  * <itemizedlist>
878  * <listitem><para>
879  * LASSO_PARAM_ERROR_BAD_TYPE_OR_NULL_OBJ if login is not a #LassoLogin object,
880  * </para></listitem>
881  * <listitem><para>
882  * LASSO_PROFILE_ERROR_MISSING_REMOTE_PROVIDERID if no remote provider ID was setup in the login
883  * profile object, it's usually done by lasso_login_process_authn_request_msg,
884  * </para></listitem>
885  * <listitem><para>
886  * LASSO_PROFILE_ERROR_INVALID_HTTP_METHOD if the HTTP method is neither LASSO_HTTP_METHOD_REDIRECT
887  * or LASSO_HTTP_METHOD_POST (ID-FF 1.2 case) or neither LASSO_HTTP_METHOD_ARTIFACT_GET or
888  * LASSO_HTTP_METHOD_ARTIFACT_POST (SAML 2.0 case) for SAML 2.0),
889  * </para></listitem>
890  * <listitem><para>
891  * LASSO_PROFILE_ERROR_INVALID_PROTOCOLPROFILE if the current protocolProfile is not
892  * </para></listitem>
893  * <listitem><para>
894  * LASSO_LOGIN_PROTOCOL_PROFILE_BRWS_ART (only for ID-FF 1.2),
895  * </para></listitem>
896  * <listitem><para>
897  * LASSO_SERVER_ERROR_PROVIDER_NOT_FOUND if the remote provider is not known to our server object
898  * which impeach us to find a service endpoint,
899  * </para></listitem>
900  * <listitem><para>
901  * LASSO_PROFILE_ERROR_MISSING_RESPONSE if the response object is missing,
902  * </para></listitem>
903  * <listitem><para>
904  * LASSO_PROFILE_ERROR_MISSING_STATUS_CODE if the response object is missing a status code,
905  * </para></listitem>
906  *</itemizedlist>
907  *
908  **/
909 gint
lasso_login_build_artifact_msg(LassoLogin * login,LassoHttpMethod http_method)910 lasso_login_build_artifact_msg(LassoLogin *login, LassoHttpMethod http_method)
911 {
912 	LassoProvider *remote_provider = NULL;
913 	LassoProfile *profile = NULL;
914 	gchar *url = NULL;
915 	xmlChar *b64_samlArt = NULL;
916 	xmlChar *relayState = NULL;
917 	gint rc = 0;
918 
919 	g_return_val_if_fail(LASSO_IS_LOGIN(login), LASSO_PARAM_ERROR_BAD_TYPE_OR_NULL_OBJ);
920 
921 	profile = LASSO_PROFILE(login);
922 	lasso_profile_clean_msg_info(profile);
923 
924 	if (profile->remote_providerID == NULL) {
925 		/* this means lasso_login_init_request was not called before */
926 		goto_cleanup_with_rc(LASSO_PROFILE_ERROR_MISSING_REMOTE_PROVIDERID);
927 	}
928 
929 	IF_SAML2(profile) {
930 		return lasso_saml20_login_build_artifact_msg(login, http_method);
931 	}
932 
933 	if (http_method != LASSO_HTTP_METHOD_REDIRECT && http_method != LASSO_HTTP_METHOD_POST) {
934 		goto_cleanup_with_rc(LASSO_PROFILE_ERROR_INVALID_HTTP_METHOD);
935 	}
936 
937 	/* ProtocolProfile must be BrwsArt */
938 	if (login->protocolProfile != LASSO_LOGIN_PROTOCOL_PROFILE_BRWS_ART) {
939 		goto_cleanup_with_rc(LASSO_PROFILE_ERROR_INVALID_PROTOCOLPROFILE);
940 	}
941 
942 	/* build artifact infos */
943 	remote_provider = lasso_server_get_provider(profile->server, profile->remote_providerID);
944 	if (LASSO_IS_PROVIDER(remote_provider) == FALSE)
945 		goto_cleanup_with_rc(LASSO_SERVER_ERROR_PROVIDER_NOT_FOUND);
946 
947 	url = lasso_provider_get_assertion_consumer_service_url(remote_provider,
948 			LASSO_LIB_AUTHN_REQUEST(profile->request)->AssertionConsumerServiceID);
949 	if (url == NULL) {
950 		/* from draft-liberty-idff-protocols-schema-1.2-errata-v2.0.pdf
951 		 * paragraph starting line 768,
952 		 *
953 		 * If the <AssertionConsumerServiceID> element is provided,
954 		 * then the identity provider MUST search for the value among
955 		 * the id attributes in the <AssertionConsumerServiceURL>
956 		 * elements in the provider's metadata to determine the URL
957 		 * to use. If no match can be found, then the provider MUST
958 		 * return an error with a second-level <samlp:StatusCode> of
959 		 * lib:InvalidAssertionConsumerServiceIndex to the default URL
960 		 */
961 		lasso_profile_set_response_status(profile,
962 				LASSO_LIB_STATUS_CODE_INVALID_ASSERTION_CONSUMER_SERVICE_INDEX);
963 		url = lasso_provider_get_assertion_consumer_service_url(
964 				remote_provider, NULL);
965 	}
966 
967 	/* it may have been created in lasso_login_build_assertion */
968 	if (login->assertionArtifact == NULL)
969 		lasso_login_build_assertion_artifact(login);
970 
971 	if (login->assertion) {
972 		LassoSamlAssertion *assertion = login->assertion;
973 		LassoSamlSubjectStatementAbstract *ss;
974 
975 		ss = LASSO_SAML_SUBJECT_STATEMENT_ABSTRACT(assertion->AuthenticationStatement);
976 		/* Subject and SubjectConfirmation should never be NULL
977 		 * because they're built by Lasso
978 		 */
979 		if (ss->Subject != NULL && ss->Subject->SubjectConfirmation != NULL) {
980 			if (assertion->MajorVersion == 1 && assertion->MinorVersion == 0) {
981 				lasso_list_add_string(ss->Subject->SubjectConfirmation->ConfirmationMethod,
982 					LASSO_SAML_CONFIRMATION_METHOD_ARTIFACT01);
983 			} else {
984 				lasso_list_add_string(ss->Subject->SubjectConfirmation->ConfirmationMethod,
985 						LASSO_SAML_CONFIRMATION_METHOD_ARTIFACT);
986 			}
987 		}
988 	}
989 
990 	b64_samlArt = xmlStrdup((xmlChar*)login->assertionArtifact);
991 	relayState = lasso_xmlURIEscapeStr(
992 			(xmlChar*)LASSO_LIB_AUTHN_REQUEST(profile->request)->RelayState, NULL);
993 
994 	if (http_method == LASSO_HTTP_METHOD_REDIRECT) {
995 		xmlChar *escaped_artifact = lasso_xmlURIEscapeStr(b64_samlArt, NULL);
996 		gchar *query = NULL;
997 
998 		if (relayState == NULL) {
999 			query = g_strdup_printf("SAMLart=%s", escaped_artifact);
1000 		} else {
1001 			query = g_strdup_printf("SAMLart=%s&RelayState=%s",
1002 					escaped_artifact, relayState);
1003 		}
1004 		lasso_assign_new_string(profile->msg_url, lasso_concat_url_query(url, query));
1005 		lasso_release_string(query);
1006 		lasso_release_xml_string(escaped_artifact);
1007 	}
1008 
1009 	if (http_method == LASSO_HTTP_METHOD_POST) {
1010 		lasso_assign_string(profile->msg_url, url);
1011 		lasso_assign_string(profile->msg_body, (char*)b64_samlArt);
1012 		if (relayState != NULL) {
1013 			lasso_assign_string(profile->msg_relayState, (char*)relayState);
1014 		}
1015 	}
1016 
1017 	if (strcmp(LASSO_SAMLP_RESPONSE(profile->response)->Status->StatusCode->Value,
1018 				LASSO_SAML_STATUS_CODE_SUCCESS) != 0) {
1019 		if (profile->session == NULL)
1020 			profile->session = lasso_session_new();
1021 
1022 		lasso_session_add_status(profile->session, profile->remote_providerID,
1023 				LASSO_NODE(g_object_ref(LASSO_SAMLP_RESPONSE(profile->response)->Status)));
1024 	} else {
1025 		lasso_session_remove_status(profile->session, profile->remote_providerID);
1026 	}
1027 
1028 	/* store the response as the artifact message */
1029 	lasso_check_good_rc(lasso_server_set_signature_for_provider_by_name(
1030 				profile->server,
1031 				profile->remote_providerID,
1032 				profile->response));
1033 	/* comply with the new way of storing artifacts */
1034 	lasso_assign_string(profile->private_data->artifact,
1035 			login->assertionArtifact);
1036 	/* Artifact profile for ID-FF 1.2 is special, this is not the full message which is relayed
1037 	 * but only its assertion content, the Response container is changed from a
1038 	 * lib:AuthnResponse to a samlp:Response.
1039 	 */
1040 	lasso_assign_new_string(profile->private_data->artifact_message,
1041 			lasso_node_export_to_xml((LassoNode*)login->assertion));
1042 cleanup:
1043 	lasso_release_string(url);
1044 	lasso_release_xml_string(b64_samlArt);
1045 	lasso_release_xml_string(relayState);
1046 	return rc;
1047 }
1048 
1049 /**
1050  * lasso_login_build_authn_request_msg:
1051  * @login: a #LassoLogin
1052  *
1053  * Converts profile authentication request (@request member) into a Liberty message, either an URL
1054  * in HTTP-Redirect profile or an URL and a field value in Browser-POST (form) profile.
1055  *
1056  * The URL is set into the @msg_url member and the eventual field value (LAREQ) is set into the
1057  * @msg_body member.
1058  *
1059  * Return value: 0 on success; or
1060  * <itemizedlist>
1061  * <listitem><para>
1062  * #LASSO_PARAM_ERROR_BAD_TYPE_OR_NULL_OBJ if login is not a #LassoLogin object,
1063  * </para></listitem>
1064  * <listitem><para>
1065  * #LASSO_PROFILE_ERROR_MISSING_REMOTE_PROVIDERID if not remote provider ID was setup&160;- it usually
1066  * means that lasso_login_init_request() was not called before,
1067  * </para></listitem>
1068  * <listitem><para>
1069  * #LASSO_SERVER_ERROR_PROVIDER_NOT_FOUND if the remote provider ID is not registered in the server
1070  * object,
1071  * </para></listitem>
1072  * <listitem><para>
1073  * #LASSO_PROFILE_ERROR_UNSUPPORTED_PROFILE if the SSO profile is not supported by the targeted
1074  * provider,
1075  * </para></listitem>
1076  * <listitem><para>
1077  * #LASSO_PROFILE_ERROR_BUILDING_QUERY_FAILED if the building of the query part of the redirect URL
1078  * or of the body of the POST content failed&160;- it only happens with the #LASSO_HTTP_METHOD_REDIRECT,
1079  * #LASSO_HTTP_METHOD_POST, #LASSO_HTTP_METHOD_ARTIFACT_GET and
1080  * #LASSO_HTTP_METHOD_ARTIFACT_POST bindings&160;-,
1081  * </para></listitem>
1082  * <listitem><para>
1083  * #LASSO_PROFILE_ERROR_UNKNOWN_PROFILE_URL if the metadata of the remote provider does not contain
1084  * an url for the SSO profile,
1085  * </para></listitem>
1086  * <listitem><para>
1087  * #LASSO_PROFILE_ERROR_INVALID_REQUEST if the request object is not of the needed type, is usually
1088  * means that lasso_login_init_request() was not called before,
1089  * </para></listitem>
1090  * <listitem><para>
1091  * #LASSO_PROFILE_MISSING_REQUEST if the request object is missing,
1092  * </para></listitem>
1093  * <listitem><para>
1094  * #LASSO_PROFILE_ERROR_INVALID_HTTP_METHOD if the current setted @http_method on the #LassoLogin
1095  * object is invalid.
1096  * </para></listitem>
1097  * </itemizedlist>
1098  **/
1099 lasso_error_t
lasso_login_build_authn_request_msg(LassoLogin * login)1100 lasso_login_build_authn_request_msg(LassoLogin *login)
1101 {
1102 	LassoProvider *provider, *remote_provider;
1103 	LassoProfile *profile;
1104 	char *md_authnRequestsSigned, *url, *query = NULL, *lareq, *protocolProfile;
1105 	LassoProviderRole role, remote_role;
1106 	gboolean must_sign;
1107 	gint rc = 0;
1108 
1109 	g_return_val_if_fail(LASSO_IS_LOGIN(login), LASSO_PARAM_ERROR_BAD_TYPE_OR_NULL_OBJ);
1110 	profile = LASSO_PROFILE(login);
1111 	lasso_profile_clean_msg_info(profile);
1112 
1113 	/* With PAOS ECP there is no remote provider, don't check for it, go straight to saml2.0 */
1114 	if (login->http_method == LASSO_HTTP_METHOD_PAOS) {
1115 		return lasso_saml20_login_build_authn_request_msg(login);
1116 	}
1117 	if (profile->remote_providerID == NULL) {
1118 		/* this means lasso_login_init_request was not called before */
1119 		return critical_error(LASSO_PROFILE_ERROR_MISSING_REMOTE_PROVIDERID);
1120 	}
1121 
1122 	provider = LASSO_PROVIDER(profile->server);
1123 	remote_provider = lasso_server_get_provider(profile->server, profile->remote_providerID);
1124 	if (LASSO_IS_PROVIDER(remote_provider) == FALSE) {
1125 		return critical_error(LASSO_SERVER_ERROR_PROVIDER_NOT_FOUND);
1126 	}
1127 
1128 	IF_SAML2(profile) {
1129 		return lasso_saml20_login_build_authn_request_msg(login);
1130 	}
1131 
1132 	protocolProfile = LASSO_LIB_AUTHN_REQUEST(profile->request)->ProtocolProfile;
1133 	if (protocolProfile == NULL)
1134 		protocolProfile = LASSO_LIB_PROTOCOL_PROFILE_BRWS_ART;
1135 
1136 	role = provider->role;
1137 	provider->role = LASSO_PROVIDER_ROLE_SP; /* we act as an SP for sure here */
1138 	remote_role = remote_provider->role;
1139 	remote_provider->role = LASSO_PROVIDER_ROLE_IDP; /* and remote is IdP */
1140 
1141 	if (lasso_provider_has_protocol_profile(remote_provider,
1142 				LASSO_MD_PROTOCOL_TYPE_SINGLE_SIGN_ON, protocolProfile) == FALSE) {
1143 		provider->role = role;
1144 		remote_provider->role = remote_role;
1145 		return LASSO_PROFILE_ERROR_UNSUPPORTED_PROFILE;
1146 	}
1147 
1148 	/* check if authnRequest must be signed */
1149 	md_authnRequestsSigned = lasso_provider_get_metadata_one(provider, "AuthnRequestsSigned");
1150 	must_sign = (md_authnRequestsSigned && strcmp(md_authnRequestsSigned, "true") == 0);
1151 	lasso_release_string(md_authnRequestsSigned);
1152 
1153 	/* restore original roles */
1154 	provider->role = role;
1155 	remote_provider->role = remote_role;
1156 
1157 	if (login->http_method == LASSO_HTTP_METHOD_REDIRECT) {
1158 		/* REDIRECT -> query */
1159 		if (must_sign) {
1160 			lasso_check_good_rc(lasso_server_export_to_query_for_provider_by_name(profile->server,
1161 						profile->remote_providerID,
1162 						profile->request, &query));
1163 		} else {
1164 			query = lasso_node_build_query(LASSO_NODE(profile->request));
1165 		}
1166 		if (query == NULL) {
1167 			return critical_error(LASSO_PROFILE_ERROR_BUILDING_QUERY_FAILED);
1168 		}
1169 
1170 		/* get SingleSignOnServiceURL metadata */
1171 		url = lasso_provider_get_metadata_one(remote_provider, "SingleSignOnServiceURL");
1172 		if (url == NULL) {
1173 			return critical_error(LASSO_PROFILE_ERROR_UNKNOWN_PROFILE_URL);
1174 		}
1175 
1176 		lasso_assign_new_string(profile->msg_url, lasso_concat_url_query(url, query));
1177 		lasso_release_string(profile->msg_body);
1178 		lasso_release_string(query);
1179 		lasso_release_string(url);
1180 	}
1181 	if (login->http_method == LASSO_HTTP_METHOD_POST) {
1182 		if (must_sign) {
1183 			lasso_server_set_signature_for_provider_by_name(profile->server,
1184 					profile->remote_providerID,
1185 					profile->request);
1186 		}
1187 		lareq = lasso_node_export_to_base64(profile->request);
1188 
1189 		if (lareq == NULL) {
1190 			return critical_error(LASSO_PROFILE_ERROR_BUILDING_QUERY_FAILED);
1191 		}
1192 
1193 		lasso_assign_new_string(profile->msg_url, lasso_provider_get_metadata_one(
1194 				remote_provider, "SingleSignOnServiceURL"));
1195 		lasso_assign_new_string(profile->msg_body, lareq);
1196 	}
1197 
1198 cleanup:
1199 	return rc;
1200 }
1201 
1202 /**
1203  * lasso_login_build_authn_response_msg:
1204  * @login: a #LassoLogin
1205  *
1206  * Converts profile authentication response (@response member) into a Liberty
1207  * message.
1208  *
1209  * The URL is set into the @msg_url member and the field value (LARES) is set
1210  * into the @msg_body member.
1211  *
1212  * Return value: 0 on success; or
1213  * <itemizedlist>
1214  * <listitem><para>
1215  * LASSO_PARAM_ERROR_BAD_TYPE_OR_NULL_OBJ if login is not a #LassoLogin object,
1216  * </para></listitem>
1217  * <listitem><para>
1218  * LASSO_PROFILE_ERROR_INVALID_PROTOCOLPROFILE if the current protocol profile is not
1219  * </para></listitem>
1220  * <listitem><para>
1221  * LASSO_LOGIN_PROTOCOL_PROFILE_BRWS_POST or LASSO_LOGIN_PROTOCOL_PROFILE_BRWS_LECP,
1222  * </para></listitem>
1223  * <listitem><para>
1224  * LASSO_SERVER_ERROR_PROVIDER_NOT_FOUND if the remote provider ID is not registered in the server
1225  * object,
1226  * </para></listitem>
1227  * <listitem><para>
1228  * LASSO_PROFILE_ERROR_UNKNOWN_PROFILE_URL if the metadata of the remote provider does not contain
1229  * an URL for the assertion consuming service,
1230  * </para></listitem>
1231  * <listitem><para>
1232  * LASSO_PROFILE_ERROR_MISSING_SERVER the server object is needed to sign a message and it is
1233  * missing,
1234  * </para></listitem>
1235  * <listitem><para>
1236  * LASSO_DS_ERROR_PRIVATE_KEY_LOAD_FAILED the private key for signing could not be found,
1237  * </para></listitem>
1238  * <listitem><para>
1239  * LASSO_PROFILE_ERROR_MISSING_RESPONSE if the response object is missing,
1240  * </para></listitem>
1241  * <listitem><para>
1242  * LASSO_PROFILE_ERROR_UNSUPPORTED_PROFILE if the SSO profile is not supported by the targeted
1243  * provider,
1244  * </para></listitem>
1245  * <listitem><para>
1246  * LASSO_PROFILE_BUILDING_QUERY_FAILED if using #LASSO_HTTP_METHOD_REDIRECT building of the redirect
1247  * URL failed,
1248  * </para></listitem>
1249  * <listitem><para>
1250  * LASSO_PROFILE_BUILDING_MSG_FAILED if using #LASSO_HTTP_METHOD_POST, #LASSO_HTTP_METHOD_SOAP or
1251  * #LASSO_HTTP_METHOD_PAOS and building the @msg_body failed.
1252  * </para></listitem>
1253  * </itemizedlist>
1254  *
1255  **/
1256 gint
lasso_login_build_authn_response_msg(LassoLogin * login)1257 lasso_login_build_authn_response_msg(LassoLogin *login)
1258 {
1259 	LassoProvider *remote_provider = NULL;
1260 	LassoProfile *profile = NULL;
1261 	lasso_error_t rc = 0;
1262 
1263 	g_return_val_if_fail(LASSO_IS_LOGIN(login), LASSO_PARAM_ERROR_BAD_TYPE_OR_NULL_OBJ);
1264 
1265 	profile = LASSO_PROFILE(login);
1266 	lasso_profile_clean_msg_info(profile);
1267 
1268 	IF_SAML2(profile) {
1269 		return lasso_saml20_login_build_authn_response_msg(login);
1270 	}
1271 
1272 	/* ProtocolProfile must be BrwsPost */
1273 	if (login->protocolProfile != LASSO_LOGIN_PROTOCOL_PROFILE_BRWS_POST &&
1274 			login->protocolProfile != LASSO_LOGIN_PROTOCOL_PROFILE_BRWS_LECP) {
1275 		return critical_error(LASSO_PROFILE_ERROR_INVALID_PROTOCOLPROFILE);
1276 	}
1277 
1278 	if (login->assertion) {
1279 		LassoSamlAssertion *assertion = login->assertion;
1280 		LassoSamlSubjectStatementAbstract *ss;
1281 		ss = LASSO_SAML_SUBJECT_STATEMENT_ABSTRACT(assertion->AuthenticationStatement);
1282 		if (ss->Subject && ss->Subject->SubjectConfirmation) {
1283 			lasso_list_add_string(ss->Subject->SubjectConfirmation->ConfirmationMethod,
1284 					LASSO_SAML_CONFIRMATION_METHOD_BEARER);
1285 		}
1286 	}
1287 
1288 	/* Countermeasure: The issuer should sign <lib:AuthnResponse> messages.
1289 	 * (binding and profiles (1.2errata2, page 65) */
1290 	lasso_check_good_rc(lasso_server_set_signature_for_provider_by_name(
1291 				profile->server,
1292 				profile->remote_providerID,
1293 				profile->response));
1294 
1295 	/* build an lib:AuthnResponse base64 encoded */
1296 	lasso_assign_new_string(profile->msg_body,
1297 			lasso_node_export_to_base64(LASSO_NODE(profile->response)));
1298 
1299 	remote_provider = lasso_server_get_provider(profile->server, profile->remote_providerID);
1300 	if (LASSO_IS_PROVIDER(remote_provider) == FALSE)
1301 		return critical_error(LASSO_SERVER_ERROR_PROVIDER_NOT_FOUND);
1302 	lasso_assign_new_string(profile->msg_url, lasso_provider_get_assertion_consumer_service_url(remote_provider,
1303 			LASSO_LIB_AUTHN_REQUEST(profile->request)->AssertionConsumerServiceID));
1304 	if (profile->msg_url == NULL) {
1305 		return LASSO_PROFILE_ERROR_UNKNOWN_PROFILE_URL;
1306 	}
1307 cleanup:
1308 	return rc;
1309 }
1310 
1311 /**
1312  * lasso_login_build_request_msg:
1313  * @login: a #LassoLogin
1314  *
1315  * Produce a SOAP Artifact Resolve message. It must follows a call to
1316  * lasso_login_init_request() on the artifact message.
1317  * Converts  artifact request into a Liberty SOAP message.
1318  *
1319  * The URL is set into the @msg_url member and the SOAP message is set into the
1320  * @msg_body member. You should POST the @msg_body to the @msg_url afterward.
1321  *
1322  * Return value: 0 on success; or
1323  * LASSO_PARAM_ERROR_BAD_TYPE_OR_NULL_OBJ if login is not a #LassoLogin object,
1324  * LASSO_PROFILE_ERROR_MISSING_REMOTE_PROVIDERID if not remote provider ID was setup -- it usually
1325  * means that lasso_login_init_request was not called before,
1326  * LASSO_SERVER_ERROR_PROVIDER_NOT_FOUND if the remote provider ID is not registered in the server
1327  * object.
1328  *
1329  **/
1330 gint
lasso_login_build_request_msg(LassoLogin * login)1331 lasso_login_build_request_msg(LassoLogin *login)
1332 {
1333 	LassoProvider *remote_provider;
1334 	LassoProfile *profile;
1335 	lasso_error_t rc = 0;
1336 
1337 	g_return_val_if_fail(LASSO_IS_LOGIN(login), LASSO_PARAM_ERROR_BAD_TYPE_OR_NULL_OBJ);
1338 
1339 	profile = LASSO_PROFILE(login);
1340 	lasso_profile_clean_msg_info(profile);
1341 
1342 	IF_SAML2(profile) {
1343 		return lasso_saml20_login_build_request_msg(login);
1344 	}
1345 
1346 	if (profile->remote_providerID == NULL) {
1347 		/* this means lasso_login_init_request was not called before */
1348 		return critical_error(LASSO_PROFILE_ERROR_MISSING_REMOTE_PROVIDERID);
1349 	}
1350 
1351 	lasso_check_good_rc(lasso_server_set_signature_for_provider_by_name(
1352 				profile->server,
1353 				profile->remote_providerID,
1354 				profile->request));
1355 	lasso_assign_new_string(profile->msg_body, lasso_node_export_to_soap(profile->request));
1356 
1357 	remote_provider = lasso_server_get_provider(profile->server, profile->remote_providerID);
1358 	if (LASSO_IS_PROVIDER(remote_provider) == FALSE) {
1359 		return critical_error(LASSO_SERVER_ERROR_PROVIDER_NOT_FOUND);
1360 	}
1361 	lasso_assign_new_string(profile->msg_url, lasso_provider_get_metadata_one(remote_provider, "SoapEndpoint"));
1362 cleanup:
1363 	return rc;
1364 }
1365 
1366 /**
1367  * lasso_login_build_response_msg:
1368  * @login: a #LassoLogin
1369  * @remote_providerID: service provider ID
1370  *
1371  * Converts profile assertion response (@response member) into a Liberty SOAP
1372  * messageresponse message.
1373  *
1374  * The URL is set into the @msg_url member and the SOAP message is set into the
1375  * @msg_body member.
1376  *
1377  * Return value: 0 on success; or a negative value otherwise.
1378  * LASSO_PARAM_ERROR_BAD_TYPE_OR_NULL_OBJ if login is not a #LassoLogin object,
1379  * LASSO_PROFILE_ERROR_SESSION_NOT_FOUND if no session object was found in the login profile object
1380  * -- it should be created by lasso_login_build_assertion() if you did not set it manually before
1381  *  calling lasso_login_build_assertion().
1382  *
1383  **/
1384 gint
lasso_login_build_response_msg(LassoLogin * login,gchar * remote_providerID)1385 lasso_login_build_response_msg(LassoLogin *login, gchar *remote_providerID)
1386 {
1387 	LassoProvider *remote_provider = NULL;
1388 	LassoProfile *profile = NULL;
1389 	lasso_error_t rc = 0;
1390 
1391 	g_return_val_if_fail(LASSO_IS_LOGIN(login), LASSO_PARAM_ERROR_BAD_TYPE_OR_NULL_OBJ);
1392 	profile = LASSO_PROFILE(login);
1393 	lasso_profile_clean_msg_info(profile);
1394 
1395 	IF_SAML2(profile) {
1396 		return lasso_saml20_login_build_response_msg(login);
1397 	}
1398 
1399 	lasso_assign_new_gobject(profile->response, lasso_samlp_response_new());
1400 	lasso_assign_string(LASSO_SAMLP_RESPONSE_ABSTRACT(profile->response)->InResponseTo,
1401 		LASSO_SAMLP_REQUEST_ABSTRACT(profile->request)->RequestID);
1402 	if (LASSO_SAMLP_REQUEST_ABSTRACT(profile->request)->MajorVersion == 1 &&
1403 			LASSO_SAMLP_REQUEST_ABSTRACT(profile->request)->MinorVersion == 0) {
1404 		/* this is a SAML 1.0 request, must create SAML 1.0 response */
1405 		LASSO_SAMLP_RESPONSE_ABSTRACT(profile->response)->MinorVersion = 0;
1406 	}
1407 
1408 	if (remote_providerID != NULL) {
1409 		lasso_assign_string(profile->remote_providerID, remote_providerID);
1410 		remote_provider = lasso_server_get_provider(profile->server, profile->remote_providerID);
1411 		rc = lasso_provider_verify_signature(remote_provider,
1412 				login->private_data->soap_request_msg,
1413 				"RequestID", LASSO_MESSAGE_FORMAT_SOAP);
1414 		lasso_release_string(login->private_data->soap_request_msg);
1415 
1416 		/* lasso_profile_set_session_from_dump has not been called */
1417 		if (profile->session == NULL) {
1418 			rc = LASSO_PROFILE_ERROR_SESSION_NOT_FOUND;
1419 		}
1420 
1421 		/* change status code into RequestDenied if signature is
1422 		 * invalid or not found or if an error occurs during
1423 		 * verification */
1424 		if (rc != 0) {
1425 			lasso_profile_set_response_status(profile,
1426 					LASSO_SAML_STATUS_CODE_REQUEST_DENIED);
1427 		}
1428 
1429 		if (rc == 0) {
1430 			/* get assertion in session and add it in response */
1431 			LassoSamlAssertion *assertion;
1432 			LassoSamlpStatus *status;
1433 
1434 			status = LASSO_SAMLP_STATUS(lasso_session_get_status(
1435 						profile->session, remote_providerID));
1436 			assertion = LASSO_SAML_ASSERTION(
1437 					lasso_session_get_assertion(profile->session,
1438 						profile->remote_providerID));
1439 			if (status) {
1440 				lasso_assign_gobject(LASSO_SAMLP_RESPONSE(profile->response)->Status,
1441 					status);
1442 				lasso_session_remove_status(profile->session,
1443 						remote_providerID);
1444 			} else if (assertion) {
1445 				lasso_list_add_gobject(LASSO_SAMLP_RESPONSE(profile->response)->Assertion,
1446 					assertion);
1447 				lasso_profile_set_response_status(profile,
1448 						LASSO_SAML_STATUS_CODE_SUCCESS);
1449 				lasso_session_remove_status(profile->session, remote_providerID);
1450 			} else if (profile->private_data->artifact_message) {
1451 				xmlDoc *doc;
1452 				char *artifact_message = profile->private_data->artifact_message;
1453 				doc = lasso_xml_parse_memory(artifact_message,
1454 						strlen(artifact_message));
1455 				lasso_profile_set_response_status(profile,
1456 						LASSO_SAML_STATUS_CODE_SUCCESS);
1457 				lasso_list_add_new_gobject(((LassoSamlpResponse*)profile->response)->Assertion,
1458 						lasso_misc_text_node_new_with_xml_node(xmlDocGetRootElement(doc)));
1459 				lasso_release_doc(doc);
1460 			}
1461 		}
1462 	} else {
1463 		lasso_profile_set_response_status(profile, LASSO_SAML_STATUS_CODE_REQUEST_DENIED);
1464 	}
1465 
1466 	lasso_check_good_rc(lasso_server_set_signature_for_provider_by_name(
1467 				profile->server,
1468 				profile->remote_providerID,
1469 				profile->response));
1470 	lasso_assign_new_string(profile->msg_body, lasso_node_export_to_soap(profile->response));
1471 
1472 cleanup:
1473 	return rc;
1474 }
1475 
1476 /**
1477  * lasso_login_destroy:
1478  * @login: a #LassoLogin
1479  *
1480  * Destroys a #LassoLogin object.
1481  *
1482  * @Deprecated: Since #2.2.1, use g_object_unref() instead.
1483  **/
1484 void
lasso_login_destroy(LassoLogin * login)1485 lasso_login_destroy(LassoLogin *login)
1486 {
1487 	lasso_release_gobject(login);
1488 }
1489 
1490 /**
1491  * lasso_login_init_authn_request:
1492  * @login: a #LassoLogin
1493  * @remote_providerID:(allow-none): the providerID of the identity provider (may be NULL)
1494  * @http_method:(default LASSO_HTTP_METHOD_REDIRECT): HTTP method to use for request transmission
1495  *
1496  * <para>Initializes a new AuthnRequest from current service provider to remote
1497  * identity provider specified in @remote_providerID (if NULL the first known
1498  * identity provider is used).</para>
1499  *
1500  * <para>For ID-FF 1.2 the default NameIDPolicy in an AuthnRequest is None, which imply that a
1501  * federation must already exist on the IdP side.</para>
1502  *
1503  * <para>For SAML 2.0 the default NameIDPolicy is the first listed in the metadatas of the current
1504  * provider, or if none is specified, Transient, which ask the IdP to give a one-time
1505  * federation</para>
1506  *
1507  * Return value: 0 on success; or
1508  * <itemizedlist>
1509  * <listitem><para>LASSO_PARAM_ERROR_BAD_TYPE_OR_NULL_OBJ if login is not a #LassoLogin object,</para></listitem>
1510  * <listitem><para>LASSO_PROFILE_ERROR_MISSING_REMOTE_PROVIDERID if @remote_providerID is NULL and no default remote
1511  * provider could be found from the server object -- usually the first one in the order of adding to
1512  * the server object --,</para></listitem>
1513  * <listitem><para>LASSO_SERVER_ERROR_PROVIDER_NOT_FOUND if the @remote_providerID is not known to our server object.</para></listitem>
1514  * <listitem><para>LASSO_PROFILE_ERROR_INVALID_HTTP_METHOD if the HTTP method is neither LASSO_HTTP_METHOD_REDIRECT
1515  * or LASSO_HTTP_METHOD_POST,</para></listitem>
1516  * <listitem><para>LASSO_PROFILE_ERROR_BUILDING_REQUEST_FAILED if creation of the request object failed.</para></listitem>
1517  * </itemizedlist>
1518  *
1519  **/
1520 gint
lasso_login_init_authn_request(LassoLogin * login,const gchar * remote_providerID,LassoHttpMethod http_method)1521 lasso_login_init_authn_request(LassoLogin *login, const gchar *remote_providerID,
1522 		LassoHttpMethod http_method)
1523 {
1524 	LassoProfile *profile;
1525 	LassoProvider *remote_provider = NULL;
1526 	LassoServer *server = NULL;
1527 	LassoSamlpRequestAbstract *request;
1528 	lasso_error_t rc = 0;
1529 
1530 	g_return_val_if_fail(LASSO_IS_LOGIN(login), LASSO_PARAM_ERROR_BAD_TYPE_OR_NULL_OBJ);
1531 
1532 	profile = LASSO_PROFILE(login);
1533 	lasso_extract_node_or_fail(server, profile->server, SERVER,
1534 			LASSO_PROFILE_ERROR_MISSING_SERVER);
1535 
1536 	/* clean state */
1537 	lasso_release_string (profile->remote_providerID);
1538 	lasso_release_gobject (profile->request);
1539 
1540 	server->parent.role = LASSO_PROVIDER_ROLE_SP;
1541 
1542 	/* With PAOS ECP there is no remote provider, don't check for it, go straight to saml2.0 */
1543 	if (http_method == LASSO_HTTP_METHOD_PAOS) {
1544 		return lasso_saml20_login_init_authn_request(login, http_method);
1545 	}
1546 
1547 	if (remote_providerID != NULL) {
1548 		lasso_assign_string(profile->remote_providerID, remote_providerID);
1549 	} else {
1550 		lasso_assign_new_string(profile->remote_providerID, lasso_server_get_first_providerID_by_role(profile->server, LASSO_PROVIDER_ROLE_IDP));
1551 		if (profile->remote_providerID == NULL) {
1552 			return critical_error(LASSO_PROFILE_ERROR_MISSING_REMOTE_PROVIDERID);
1553 		}
1554 	}
1555 
1556 	remote_provider = lasso_server_get_provider(profile->server, profile->remote_providerID);
1557 	if (LASSO_IS_PROVIDER(remote_provider) == FALSE)
1558 		return critical_error(LASSO_SERVER_ERROR_PROVIDER_NOT_FOUND);
1559 
1560 	remote_provider->role = LASSO_PROVIDER_ROLE_IDP;
1561 
1562 	IF_SAML2(profile) {
1563 		return lasso_saml20_login_init_authn_request(login, http_method);
1564 	}
1565 
1566 	if (http_method != LASSO_HTTP_METHOD_REDIRECT && http_method != LASSO_HTTP_METHOD_POST) {
1567 		return critical_error(LASSO_PROFILE_ERROR_INVALID_HTTP_METHOD);
1568 	}
1569 
1570 	login->http_method = http_method;
1571 
1572 	lasso_assign_new_gobject(profile->request, LASSO_NODE(lasso_lib_authn_request_new()));
1573 	if (profile->request == NULL) {
1574 		return critical_error(LASSO_PROFILE_ERROR_BUILDING_REQUEST_FAILED);
1575 	}
1576 
1577 	request = LASSO_SAMLP_REQUEST_ABSTRACT(profile->request);
1578 	request->RequestID = lasso_build_unique_id(32);
1579 	lasso_assign_string(login->private_data->request_id, request->RequestID);
1580 	request->MajorVersion = LASSO_LIB_MAJOR_VERSION_N;
1581 	request->MinorVersion = LASSO_LIB_MINOR_VERSION_N;
1582 	if (remote_provider &&
1583 		lasso_provider_get_protocol_conformance(remote_provider) < LASSO_PROTOCOL_LIBERTY_1_2) {
1584 		request->MajorVersion = 1;
1585 		request->MinorVersion = 0;
1586 	}
1587 	lasso_assign_new_string(request->IssueInstant, lasso_get_current_time());
1588 	lasso_assign_string(LASSO_LIB_AUTHN_REQUEST(profile->request)->ProviderID,
1589 			LASSO_PROVIDER(profile->server)->ProviderID);
1590 	lasso_assign_string(LASSO_LIB_AUTHN_REQUEST(profile->request)->RelayState,
1591 			profile->msg_relayState);
1592 
1593 cleanup:
1594 
1595 	return rc;
1596 }
1597 
1598 
1599 /**
1600  * lasso_login_init_request:
1601  * @login: a #LassoLogin
1602  * @response_msg: the authentication response received
1603  * @response_http_method: the method used to receive the authentication
1604  *      response
1605  *
1606  * Initializes an artifact request. @response_msg is either the query string
1607  * (in redirect mode) or the form LAREQ field (in browser-post mode).
1608  * It should only be used if you received an artifact message, @response_msg must be content of the
1609  * artifact field for the POST artifact binding of the query string for the REDIRECT artifact
1610  * binding. You must set the @response_http_method argument according to the way you received the
1611  * artifact message.
1612  *
1613  * Return value: 0 on success; or
1614  * <itemizedlist>
1615  * <listitem>
1616  * <para>
1617  * LASSO_PARAM_ERROR_BAD_TYPE_OR_NULL_OBJ if login is not a #LassoLogin object,
1618  * </para>
1619  * </listitem>
1620  * <listitem>
1621  * <para>
1622  * LASSO_PARAM_ERROR_INVALID_VALUE if @response_msg is NULL,
1623  * </para>
1624  * </listitem>
1625  * <listitem>
1626  * <para>
1627  * LASSO_PROFILE_ERROR_INVALID_HTTP_METHOD if the HTTP method is neither LASSO_HTTP_METHOD_REDIRECT
1628  * or LASSO_HTTP_METHOD_POST (in the ID-FF 1.2 case) or neither LASSO_HTTP_METHOD_ARTIFACT_GET or
1629  * LASSO_HTTP_METHOD_ARTIFACT_POST (in the SAML 2.0 case),
1630  * </para>
1631  * </listitem>
1632  * <listitem>
1633  * <para>
1634  * LASSO_PROFILE_ERROR_MISSING_ARTIFACT if no artifact field was found in the query string (only
1635  * possible for the LASSO_HTTP_METHOD_REDIRECT case),
1636  * </para>
1637  * </listitem>
1638  * <listitem>
1639  * <para>
1640  * LASSO_PROFILE_ERROR_INVALID_ARTIFACT if decoding of the artifact failed -- whether because
1641  * the base64 encoding is invalid or because the type code is wrong --,
1642  * </para>
1643  * </listitem>
1644  * <listitem>
1645  * <para>
1646  * LASSO_PROFILE_ERROR_MISSING_REMOTE_PROVIDERID if no provider ID could be found corresponding to
1647  * the hash contained in the artifact.
1648  * </para>
1649  * </listitem>
1650  * </itemizedlist>
1651  *
1652  **/
1653 gint
lasso_login_init_request(LassoLogin * login,gchar * response_msg,LassoHttpMethod response_http_method)1654 lasso_login_init_request(LassoLogin *login, gchar *response_msg,
1655 		LassoHttpMethod response_http_method)
1656 {
1657 	xmlChar **query_fields;
1658 	gint ret = 0;
1659 	int i;
1660 	char *artifact_b64 = NULL, *provider_succinct_id_b64;
1661 	char provider_succinct_id[21];
1662 	char artifact[43];
1663 	LassoSamlpRequestAbstract *request;
1664 	LassoProfile *profile;
1665 
1666 	g_return_val_if_fail(LASSO_IS_LOGIN(login), LASSO_PARAM_ERROR_BAD_TYPE_OR_NULL_OBJ);
1667 	g_return_val_if_fail(response_msg != NULL, LASSO_PARAM_ERROR_INVALID_VALUE);
1668 
1669 	profile = LASSO_PROFILE(login);
1670 	IF_SAML2(profile) {
1671 		return lasso_saml20_login_init_request(login, response_msg,
1672 				response_http_method);
1673 	}
1674 	if (response_http_method != LASSO_HTTP_METHOD_REDIRECT &&
1675 			response_http_method != LASSO_HTTP_METHOD_POST) {
1676 		return critical_error(LASSO_PROFILE_ERROR_INVALID_HTTP_METHOD);
1677 	}
1678 
1679 	/* rebuild response (artifact) */
1680 	if (response_http_method == LASSO_HTTP_METHOD_REDIRECT) {
1681 		query_fields = lasso_urlencoded_to_strings(response_msg);
1682 		for (i=0; query_fields[i]; i++) {
1683 			if (strncmp((char*)query_fields[i], "SAMLart=", 8) == 0) {
1684 				lasso_assign_string(artifact_b64, (char*)query_fields[i]+8);
1685 			}
1686 			if (strncmp((char*)query_fields[i], "RelayState=", 11) == 0) {
1687 				lasso_assign_string(profile->msg_relayState, (char*)query_fields[i]+11);
1688 			}
1689 		}
1690 		lasso_release_array_of_xml_strings(query_fields);
1691 		if (artifact_b64 == NULL) {
1692 			return LASSO_PROFILE_ERROR_MISSING_ARTIFACT;
1693 		}
1694 	}
1695 	if (response_http_method == LASSO_HTTP_METHOD_POST) {
1696 		lasso_assign_string(artifact_b64, response_msg);
1697 	}
1698 
1699 	i = xmlSecBase64Decode((xmlChar*)artifact_b64, (xmlChar*)artifact, 43);
1700 	if (i < 0 || i > 42) {
1701 		lasso_release_string(artifact_b64);
1702 		return LASSO_PROFILE_ERROR_INVALID_ARTIFACT;
1703 	}
1704 
1705 	if (artifact[0] != 0 || artifact[1] != 3) { /* wrong type code */
1706 		lasso_release_string(artifact_b64);
1707 		return LASSO_PROFILE_ERROR_INVALID_ARTIFACT;
1708 	}
1709 
1710 	memcpy(provider_succinct_id, artifact+2, 20);
1711 	provider_succinct_id[20] = 0;
1712 
1713 	provider_succinct_id_b64 = (char*)xmlSecBase64Encode((xmlChar*)provider_succinct_id, 20, 0);
1714 
1715 	lasso_assign_new_string(profile->remote_providerID, lasso_server_get_providerID_from_hash(
1716 			profile->server, provider_succinct_id_b64));
1717 	xmlFree(provider_succinct_id_b64);
1718 	if (profile->remote_providerID == NULL) {
1719 		return critical_error(LASSO_PROFILE_ERROR_MISSING_REMOTE_PROVIDERID);
1720 	}
1721 
1722 	request = LASSO_SAMLP_REQUEST_ABSTRACT(lasso_samlp_request_new());
1723 	request->RequestID = lasso_build_unique_id(32);
1724 	request->MajorVersion = LASSO_SAML_MAJOR_VERSION_N;
1725 	request->MinorVersion = LASSO_SAML_MINOR_VERSION_N;
1726 	lasso_assign_new_string(request->IssueInstant, lasso_get_current_time());
1727 	LASSO_SAMLP_REQUEST(request)->AssertionArtifact = artifact_b64;
1728 	lasso_assign_new_gobject(profile->request, LASSO_NODE(request));
1729 
1730 	return ret;
1731 }
1732 
1733 /**
1734  * lasso_login_init_idp_initiated_authn_request:
1735  * @login: a #LassoLogin.
1736  * @remote_providerID: the providerID of the remote service provider (may be
1737  *      NULL)
1738  *
1739  * <para>Generates an authentication response without matching authentication
1740  * request.</para>
1741  *
1742  * <para>The choice of NameIDFormat is the same as for lasso_login_init_authn_request() but with the
1743  * target @remote_providerID as the current provider</para>
1744  *
1745  * <para>If @remote_providerID is NULL, the first known provider is used.</para>
1746  *
1747  * Return value: 0 on success; or a negative value otherwise. Error codes are the same as
1748  * lasso_login_init_authn_request().
1749  **/
1750 gint
lasso_login_init_idp_initiated_authn_request(LassoLogin * login,const gchar * remote_providerID)1751 lasso_login_init_idp_initiated_authn_request(LassoLogin *login,
1752 		const gchar *remote_providerID)
1753 {
1754 	int rc = 0;
1755 	LassoProfile *profile;
1756 
1757 	profile = LASSO_PROFILE(login);
1758 
1759 	IF_SAML2(profile) {
1760 		return lasso_saml20_login_init_idp_initiated_authn_request(login,
1761 				remote_providerID);
1762 	}
1763 
1764 	rc = lasso_login_init_authn_request(login, remote_providerID, LASSO_HTTP_METHOD_POST);
1765 	if (rc)
1766 		return rc;
1767 
1768 	/* no RequestID attribute or it would be used in response assertion */
1769 	lasso_release_string(LASSO_SAMLP_REQUEST_ABSTRACT(profile->request)->RequestID);
1770 	lasso_assign_string(LASSO_LIB_AUTHN_REQUEST(profile->request)->NameIDPolicy,
1771 			LASSO_LIB_NAMEID_POLICY_TYPE_ANY);
1772 
1773 	return 0;
1774 }
1775 
1776 /**
1777  * lasso_login_must_ask_for_consent:
1778  * @login: a #LassoLogin
1779  *
1780  * Evaluates if consent must be asked to the Principal to federate him.
1781  *
1782  * Return value: %TRUE if consent must be asked
1783  **/
1784 gboolean
lasso_login_must_ask_for_consent(LassoLogin * login)1785 lasso_login_must_ask_for_consent(LassoLogin *login)
1786 {
1787 	LassoProfile *profile = LASSO_PROFILE(login);
1788 
1789 	IF_SAML2(profile) {
1790 		return lasso_saml20_login_must_ask_for_consent(login);
1791 	}
1792 
1793 	if (LASSO_LIB_AUTHN_REQUEST(LASSO_PROFILE(login)->request)->IsPassive) {
1794 		return FALSE;
1795 	}
1796 
1797 	return lasso_login_must_ask_for_consent_private(login);
1798 }
1799 
1800 
1801 /**
1802  * lasso_login_must_authenticate:
1803  * @login: a #LassoLogin
1804  *
1805  * Evaluates if user must be authenticated.
1806  *
1807  * Return value: %TRUE if user must be authenticated
1808  **/
1809 gboolean
lasso_login_must_authenticate(LassoLogin * login)1810 lasso_login_must_authenticate(LassoLogin *login)
1811 {
1812 	LassoLibAuthnRequest *request;
1813 	LassoProfile *profile;
1814 	gboolean matched = TRUE;
1815 	GList *assertions = NULL;
1816 
1817 	g_return_val_if_fail(LASSO_IS_LOGIN(login), LASSO_PARAM_ERROR_BAD_TYPE_OR_NULL_OBJ);
1818 	profile = LASSO_PROFILE(login);
1819 
1820 	IF_SAML2(profile) {
1821 		return lasso_saml20_login_must_authenticate(login);
1822 	}
1823 
1824 	request = LASSO_LIB_AUTHN_REQUEST(LASSO_PROFILE(login)->request);
1825 	if (request == NULL) {
1826 		return critical_error(LASSO_PROFILE_ERROR_MISSING_REQUEST);
1827 	}
1828 
1829 	if (request->ForceAuthn == TRUE && request->IsPassive == FALSE)
1830 		return TRUE;
1831 
1832 	assertions = lasso_session_get_assertions(profile->session, NULL);
1833 	if (request->RequestAuthnContext) {
1834 		char *comparison = request->RequestAuthnContext->AuthnContextComparison;
1835 		char *class_ref;
1836 		GList *class_refs = request->RequestAuthnContext->AuthnContextClassRef;
1837 		GList *t1, *t2;
1838 		int compa = -1;
1839 
1840 		if (comparison == NULL || strcmp(comparison, "exact") == 0) {
1841 			compa = 0;
1842 		} else if (strcmp(comparison, "minimum") == 0) {
1843 			message(G_LOG_LEVEL_CRITICAL, "'minimum' comparison is not implemented");
1844 			compa = 1;
1845 		} else if (strcmp(comparison, "better") == 0) {
1846 			message(G_LOG_LEVEL_CRITICAL, "'better' comparison is not implemented");
1847 			compa = 2;
1848 		}
1849 
1850 		if (class_refs) {
1851 			matched = FALSE;
1852 		}
1853 
1854 		for (t1 = class_refs; t1 && !matched; t1 = g_list_next(t1)) {
1855 			class_ref = t1->data;
1856 			for (t2 = assertions; t2 && !matched; t2 = g_list_next(t2)) {
1857 				LassoSamlAssertion *assertion;
1858 				LassoSamlAuthenticationStatement *as;
1859 				char *method;
1860 
1861 				if (LASSO_IS_SAML_ASSERTION(t2->data) == FALSE) {
1862 					continue;
1863 				}
1864 
1865 				assertion = t2->data;
1866 
1867 				as = LASSO_SAML_AUTHENTICATION_STATEMENT(
1868 						assertion->AuthenticationStatement);
1869 				method = as->AuthenticationMethod;
1870 
1871 				if (strcmp(method, LASSO_SAML_AUTHENTICATION_METHOD_PASSWORD) == 0)
1872 				{
1873 					/* mapping between SAML authentication
1874 					 * methods and Liberty authentication
1875 					 * context is not possible (excepted on
1876 					 * that one)
1877 					 */
1878 					method = LASSO_LIB_AUTHN_CONTEXT_CLASS_REF_PASSWORD;
1879 				}
1880 
1881 				switch (compa) {
1882 				case 1: /* minimum */
1883 					/* XXX: implement 'minimum' comparison */
1884 				case 2: /* better */
1885 					/* XXX: implement 'better' comparison */
1886 				case 0: /* exact */
1887 					if (strcmp(method, class_ref) == 0) {
1888 						matched = TRUE;
1889 					}
1890 					break;
1891 				default: /* inever reached */
1892 					break;
1893 				}
1894 				if (matched == TRUE) {
1895 					break;
1896 				}
1897 			}
1898 		}
1899 
1900 	} else {
1901 		/* if nothing specific was asked; don't look for any
1902 		 * particular assertions, one is enough
1903 		 */
1904 		matched = (profile->session != NULL && \
1905 				lasso_session_count_assertions(profile->session) > 0);
1906 	}
1907 	lasso_release_list(assertions);
1908 
1909 	if (matched == FALSE && request->IsPassive == FALSE)
1910 		return TRUE;
1911 
1912 	if (LASSO_PROFILE(login)->identity == NULL && request->IsPassive &&
1913 			login->protocolProfile == LASSO_LOGIN_PROTOCOL_PROFILE_BRWS_POST) {
1914 		lasso_profile_set_response_status(LASSO_PROFILE(login),
1915 				LASSO_LIB_STATUS_CODE_NO_PASSIVE);
1916 		return FALSE;
1917 	}
1918 
1919 	return FALSE;
1920 }
1921 
1922 /**
1923  * lasso_login_process_authn_request_msg:
1924  * @login: a #LassoLogin
1925  * @authn_request_msg: the authentication request received
1926  *
1927  * Processes received authentication request, checks it is signed correctly,
1928  * checks if requested protocol profile is supported, etc.
1929  *
1930  * Return value: 0 on success; or
1931  * <itemizedlist>
1932  * <listitem>
1933  * <para>
1934  * #LASSO_PARAM_ERROR_BAD_TYPE_OR_NULL_OBJ if login is no a #LassoLogin object,
1935  * </para>
1936  * </listitem>
1937  * <listitem>
1938  * <para>
1939  * #LASSO_PROFILE_ERROR_MISSING_REQUEST if @authn_request_msg is #NULL and no request as actually
1940  * been processed or initialized &#151; see lasso_login_init_idp_initiated_authn_request(),
1941  *
1942  * </para>
1943  * </listitem>
1944  * <listitem>
1945  * <para>
1946  * #LASSO_PROFILE_ERROR_INVALID_MSG if the content of @authn_request_msg cannot be parsed to as a
1947  * valid lib:AuthnRequest messages for any support binding (mainly HTTP-Redirect, HTTP-Post and
1948  * SOAP),
1949  * </para>
1950  * </listitem>
1951  * <listitem>
1952  * <para>
1953  *
1954  * #LASSO_PROFILE_ERROR_MISSING_ISSUER if the parsed samlp2:AuthnRequest does not have a proper Issuer element,
1955  * </para>
1956  * </listitem>
1957  * <listitem>
1958  * <para>
1959  *
1960  * #LASSO_PROFILE_ERROR_INVALID_REQUEST if the parsed message does not validate as a valid
1961  * samlp2:AuthnRequest (SAMLv2) i.e. if there is no Issuer, or mutually exclusive attributes are
1962  * used (ProtocolBinding and AssertionConsumerServiceIndex),
1963  * </para>
1964  * </listitem>
1965  * <listitem>
1966  * <para>
1967  *
1968  * #LASSO_PROFILE_ERROR_INVALID_PROTOCOLPROFILE if the protocolProfile (ID-FFv1.2) or the
1969  * protocolBinding (SAMLv2) is unsupported by Lasso,
1970  * </para>
1971  * </listitem>
1972  * <listitem>
1973  * <para>
1974  *
1975  * #LASSO_PROFILE_ERROR_UNSUPPORTED_PROFILE if the protocolProfile (ID-FFv1.2) or the protocolBinding
1976  * (SAMLv2) for the AssertionConsumer is unsupported by this provider implementation as indicated by
1977  * its metadata file,
1978  * </para>
1979  * </listitem>
1980  * <listitem>
1981  * <para>
1982  *
1983  * #LASSO_PROFILE_ERROR_UNKNOWN_PROVIDER, or
1984  * #LASSO_SERVER_ERROR_PROVIDER_NOT_FOUND if the metadata for the issuer of the request are absent
1985  * from the #LassoServer object of this profile,
1986  * </para>
1987  * </listitem>
1988  * <listitem>
1989  * <para>
1990  *
1991  * #LASSO_DS_ERROR_SIGNATURE_NOT_FOUND if no signature could be found and signature validation is
1992  * forced &#151; by the service provider metadata with the AuthnRequestsSigned attribute
1993  * (ID-FFv1.2&SAMLv2), the attribute WantAuthnRequestsSigned in the identity provider metadata file
1994  * (SAMLv2) or as advised by the lasso_profile_set_signature_verify_hint() method),
1995  * </para>
1996  * </listitem>
1997  * <listitem>
1998  * <para>
1999  *
2000  * #LASSO_DS_ERROR_SIGNATURE_VERIFICATION_FAILED if the signature validation failed on a present
2001  * signature,
2002  * </para>
2003  * </listitem>
2004  * <listitem>
2005  * <para>
2006  * #LASSO_DS_ERROR_INVALID_SIGNATURE if the signature was malformed and a signature was present,
2007  * </para>
2008  * </listitem>
2009  * </itemizedlist>
2010  *
2011  **/
2012 gint
lasso_login_process_authn_request_msg(LassoLogin * login,const char * authn_request_msg)2013 lasso_login_process_authn_request_msg(LassoLogin *login, const char *authn_request_msg)
2014 {
2015 	LassoProvider *remote_provider;
2016 	gchar *protocolProfile;
2017 	gchar *authnRequestSigned;
2018 	gboolean must_verify_signature = FALSE;
2019 	gint ret = 0;
2020 	LassoLibAuthnRequest *request;
2021 	LassoMessageFormat format;
2022 	LassoProfile *profile;
2023 
2024 	g_return_val_if_fail(LASSO_IS_LOGIN(login), LASSO_PARAM_ERROR_BAD_TYPE_OR_NULL_OBJ);
2025 
2026 	profile = LASSO_PROFILE(login);
2027 
2028 	IF_SAML2(profile) {
2029 		return lasso_saml20_login_process_authn_request_msg(login, authn_request_msg);
2030 	}
2031 
2032 	if (authn_request_msg == NULL) {
2033 		format = 0;
2034 		if (profile->request == NULL) {
2035 			return critical_error(LASSO_PROFILE_ERROR_MISSING_REQUEST);
2036 		}
2037 
2038 		/* LibAuthnRequest already set by lasso_login_init_idp_initiated_authn_request() */
2039 		request = LASSO_LIB_AUTHN_REQUEST(profile->request);
2040 	} else {
2041 		request = lasso_lib_authn_request_new();
2042 		format = lasso_node_init_from_message(LASSO_NODE(request), authn_request_msg);
2043 		if (format == LASSO_MESSAGE_FORMAT_UNKNOWN ||
2044 				format == LASSO_MESSAGE_FORMAT_ERROR) {
2045 			return critical_error(LASSO_PROFILE_ERROR_INVALID_MSG);
2046 		}
2047 
2048 		lasso_assign_new_gobject(profile->request, request);
2049 		if (! LASSO_IS_LIB_AUTHN_REQUEST(profile->request)) {
2050 			lasso_release_gobject(profile->request);
2051 			return LASSO_PROFILE_ERROR_INVALID_MSG;
2052 		}
2053 
2054 		/* get remote ProviderID */
2055 		lasso_assign_string(profile->remote_providerID,
2056 				LASSO_LIB_AUTHN_REQUEST(profile->request)->ProviderID);
2057 
2058 		/* get RelayState */
2059 		lasso_assign_string(profile->msg_relayState, request->RelayState);
2060 	}
2061 
2062 
2063 	/* get ProtocolProfile in lib:AuthnRequest */
2064 	protocolProfile = LASSO_LIB_AUTHN_REQUEST(profile->request)->ProtocolProfile;
2065 	if (protocolProfile == NULL ||
2066 			strcmp(protocolProfile, LASSO_LIB_PROTOCOL_PROFILE_BRWS_ART) == 0) {
2067 		protocolProfile = LASSO_LIB_PROTOCOL_PROFILE_BRWS_ART;
2068 		login->protocolProfile = LASSO_LOGIN_PROTOCOL_PROFILE_BRWS_ART;
2069 	} else if (strcmp(protocolProfile, LASSO_LIB_PROTOCOL_PROFILE_BRWS_POST) == 0) {
2070 		protocolProfile = LASSO_LIB_PROTOCOL_PROFILE_BRWS_POST;
2071 		login->protocolProfile = LASSO_LOGIN_PROTOCOL_PROFILE_BRWS_POST;
2072 	} else if (strcmp(protocolProfile, LASSO_LIB_PROTOCOL_PROFILE_BRWS_LECP) == 0) {
2073 		protocolProfile = LASSO_LIB_PROTOCOL_PROFILE_BRWS_LECP;
2074 		login->protocolProfile = LASSO_LOGIN_PROTOCOL_PROFILE_BRWS_LECP;
2075 	} else {
2076 		return critical_error(LASSO_PROFILE_ERROR_INVALID_PROTOCOLPROFILE);
2077 	}
2078 
2079 	/* check if requested single sign on protocol profile is supported */
2080 	LASSO_PROVIDER(profile->server)->role = LASSO_PROVIDER_ROLE_IDP;
2081 	if (lasso_provider_has_protocol_profile(
2082 				LASSO_PROVIDER(profile->server),
2083 				LASSO_MD_PROTOCOL_TYPE_SINGLE_SIGN_ON,
2084 				protocolProfile) == FALSE) {
2085 		return critical_error(LASSO_PROFILE_ERROR_UNSUPPORTED_PROFILE);
2086 	}
2087 
2088 	/* Check authnRequest signature. */
2089 	if (authn_request_msg != NULL) {
2090 		LassoProfileSignatureVerifyHint sig_verify_hint;
2091 
2092 		sig_verify_hint = lasso_profile_get_signature_verify_hint(profile);
2093 		remote_provider = lasso_server_get_provider(profile->server, profile->remote_providerID);
2094 		if (remote_provider == NULL) {
2095 			return critical_error(LASSO_SERVER_ERROR_PROVIDER_NOT_FOUND);
2096 		}
2097 		/* Is authnRequest signed ? */
2098 		must_verify_signature = TRUE;
2099 		authnRequestSigned = lasso_provider_get_metadata_one(
2100 				remote_provider, "AuthnRequestsSigned");
2101 		if (authnRequestSigned != NULL) {
2102 			must_verify_signature = strcmp(authnRequestSigned, "true") == 0;
2103 			lasso_release_string(authnRequestSigned);
2104 		}
2105 		if (sig_verify_hint == LASSO_PROFILE_SIGNATURE_VERIFY_HINT_FORCE) {
2106 			must_verify_signature = TRUE;
2107 		}
2108 		if (sig_verify_hint == LASSO_PROFILE_SIGNATURE_VERIFY_HINT_IGNORE) {
2109 			must_verify_signature = FALSE;
2110 		}
2111 		/* reset the signature_status, and if signature validation was not really needed
2112 		 * just choke on the presence of an invalid signature, if no signature just goes on
2113 		 * */
2114 		profile->signature_status = 0;
2115 		if (must_verify_signature) {
2116 			ret = lasso_provider_verify_signature(remote_provider,
2117 					authn_request_msg, "RequestID", format);
2118 			if (profile == LASSO_PROFILE_SIGNATURE_VERIFY_HINT_MAYBE && ret !=
2119 					LASSO_DS_ERROR_SIGNATURE_NOT_FOUND) {
2120 				profile->signature_status = ret;
2121 			}
2122 		}
2123 	}
2124 
2125 	/* create LibAuthnResponse */
2126 	lasso_assign_new_gobject(profile->response, lasso_lib_authn_response_new(
2127 			LASSO_PROVIDER(profile->server)->ProviderID,
2128 			LASSO_LIB_AUTHN_REQUEST(profile->request)));
2129 	if (LASSO_SAMLP_REQUEST_ABSTRACT(profile->request)->MajorVersion == 1 &&
2130 			LASSO_SAMLP_REQUEST_ABSTRACT(profile->request)->MinorVersion < 2) {
2131 		/* pre-id-ff 1.2, move accordingly */
2132 		LASSO_SAMLP_RESPONSE_ABSTRACT(profile->response)->MajorVersion = 1;
2133 		LASSO_SAMLP_RESPONSE_ABSTRACT(profile->response)->MinorVersion = 0;
2134 	}
2135 
2136 
2137 	return ret;
2138 }
2139 
2140 /**
2141  * lasso_login_process_authn_response_msg:
2142  * @login: a #LassoLogin
2143  * @authn_response_msg: the authentication response received
2144  *
2145  * Processes received authentication response.
2146  *
2147  * Return value: 0 on success; or
2148  * <itemizedlist>
2149  * <listitem><para>#LASSO_PARAM_ERROR_BAD_TYPE_OR_NULL_OBJ if login is not a #LassoLogin
2150  * object,</para></listitem>
2151  * <listitem><para>#LASSO_PARAM_ERROR_INVALID_VALUE if authn_response_msg is NULL,</para></listitem>
2152  * <listitem><para>#LASSO_SERVER_ERROR_PROVIDER_NOT_FOUND, if the issuing
2153  * provider of the assertion is not registered in the #LassoServer object,</para></listitem>
2154  * <listitem><para>#LASSO_PROFILE_ERROR_MISSING_ISSUER if the parsed samlp2:AuthnRequest does not
2155  * have a proper Issuer element, </para></listitem>
2156  * <listitem><para>#LASSO_PROFILE_ERROR_MISSING_STATUS_CODE if the reponse is missing a
2157  * <literal>StatusCode</literal> element,</para></listitem>
2158  * <listitem><para>#LASSO_PROFILE_STATUS_NOT_SUCCESS_ERROR if the identity provider returned a
2159  * failure response,</para></listitem>
2160  * <listitem><para>#LASSO_PROFILE_ERROR_REQUEST_DENIED</para> if the identity provider returned the
2161  * specific status code <literal>RequestDenied</literal>,</listitem>
2162  * <listitem><para>#LASSO_PROFILE_ERROR_INVALID_MSG if the message is not a #LassoSamlpResponse
2163  * (ID-FF 1.2) or a #LassoSamlp2ResponseMsg (SAML 2.0),</para></listitem>
2164  * <listitem><para>#LASSO_PROFILE_ERROR_UNSUPPORTED_PROFILE, if the received message format does not
2165  * correspond to a binding supported by this function, the only supported binding by this function
2166  * is HTTP POST,</para></listitem>
2167  * <listitem><para>#LASSO_PROFILE_ERROR_MISSING_SERVER the server object is needed to sign a message
2168  * and it is missing,</para></listitem>
2169  * <listitem><para>#LASSO_PROFILE_ERROR_CANNOT_VERIFY_SIGNATURE if the validation of the signature
2170  * of the message failed, a specific error code is available in
2171  * <literal>login->parent.signature_status</literal></para></listitem>
2172  * <listitem><para>#LASSO_LOGIN_ERROR_ASSERTION_DOES_NOT_MATCH_REQUEST_ID if the received response
2173  * does not match the saved AuthenticationRequest ID,</para></listitem>
2174  * <listitem><para>#LASSO_PROFILE_ERROR_INVALID_ISSUER if the assertion issuer does not match the
2175  * AuthenticationResponse issuer,</para></listitem>
2176  * <listitem><para>#LASSO_PROFILE_ERROR_NAME_IDENTIFIER_NOT_FOUND if not NameID could be found or
2177  * decoded,</para></listitem>
2178  * </itemizedlist>
2179  **/
2180 gint
lasso_login_process_authn_response_msg(LassoLogin * login,gchar * authn_response_msg)2181 lasso_login_process_authn_response_msg(LassoLogin *login, gchar *authn_response_msg)
2182 {
2183 	LassoMessageFormat format;
2184 	LassoProvider *remote_provider;
2185 	LassoProfile *profile;
2186 
2187 	g_return_val_if_fail(LASSO_IS_LOGIN(login), LASSO_PARAM_ERROR_BAD_TYPE_OR_NULL_OBJ);
2188 	g_return_val_if_fail(authn_response_msg != NULL, LASSO_PARAM_ERROR_INVALID_VALUE);
2189 
2190 	profile = LASSO_PROFILE(login);
2191 
2192 	IF_SAML2(profile) {
2193 		return lasso_saml20_login_process_authn_response_msg(login, authn_response_msg);
2194 	}
2195 
2196 	/* clean state */
2197 	lasso_release_string (profile->remote_providerID);
2198 	lasso_release_gobject(profile->response);
2199 
2200 	lasso_assign_new_gobject(profile->response, lasso_lib_authn_response_new(NULL, NULL));
2201 	format = lasso_node_init_from_message(
2202 			LASSO_NODE(profile->response), authn_response_msg);
2203 	if (format == LASSO_MESSAGE_FORMAT_UNKNOWN || format == LASSO_MESSAGE_FORMAT_ERROR) {
2204 		return critical_error(LASSO_PROFILE_ERROR_INVALID_MSG);
2205 	}
2206 
2207 	lasso_assign_string(profile->remote_providerID,
2208 			LASSO_LIB_AUTHN_RESPONSE(profile->response)->ProviderID);
2209 
2210 	if (profile->remote_providerID == NULL) {
2211 		return critical_error(LASSO_SERVER_ERROR_PROVIDER_NOT_FOUND);
2212 	}
2213 
2214 	remote_provider = lasso_server_get_provider(profile->server, profile->remote_providerID);
2215 	if (LASSO_IS_PROVIDER(remote_provider) == FALSE)
2216 		return critical_error(LASSO_SERVER_ERROR_PROVIDER_NOT_FOUND);
2217 
2218 	lasso_assign_string(profile->msg_relayState, LASSO_LIB_AUTHN_RESPONSE(
2219 			profile->response)->RelayState);
2220 
2221 	profile->signature_status = lasso_provider_verify_signature(
2222 			remote_provider, authn_response_msg, "ResponseID", format);
2223 
2224 	if (profile->signature_status) {
2225 		return profile->signature_status;
2226 	}
2227 	return lasso_login_process_response_status_and_assertion(login);
2228 }
2229 
2230 
2231 /**
2232  * lasso_login_process_request_msg:
2233  * @login: a #LassoLogin
2234  * @request_msg: the artifact request received
2235  *
2236  * Processes received artifact request.
2237  *
2238  * Return value: 0 on success; or a negative value otherwise.
2239  **/
2240 gint
lasso_login_process_request_msg(LassoLogin * login,gchar * request_msg)2241 lasso_login_process_request_msg(LassoLogin *login, gchar *request_msg)
2242 {
2243 	gint ret = 0;
2244 	LassoProfile *profile = LASSO_PROFILE(login);
2245 
2246 	g_return_val_if_fail(LASSO_IS_LOGIN(login), LASSO_PARAM_ERROR_BAD_TYPE_OR_NULL_OBJ);
2247 	g_return_val_if_fail(request_msg != NULL, LASSO_PARAM_ERROR_INVALID_VALUE);
2248 
2249 	IF_SAML2(profile) {
2250 		return lasso_saml20_login_process_request_msg(login, request_msg);
2251 	}
2252 
2253 	/* rebuild samlp:Request with request_msg */
2254 	lasso_assign_new_gobject(profile->request, lasso_node_new_from_soap(request_msg));
2255 	if (profile->request == NULL) {
2256 		return critical_error(LASSO_PROFILE_ERROR_INVALID_MSG);
2257 	}
2258 	/* get AssertionArtifact */
2259 	lasso_assign_string(login->assertionArtifact,
2260 			LASSO_SAMLP_REQUEST(profile->request)->AssertionArtifact);
2261 	lasso_assign_string(login->parent.private_data->artifact,
2262 			login->assertionArtifact);
2263 
2264 	/* Keep a copy of request msg so signature can be verified when we get
2265 	 * the providerId in lasso_login_build_response_msg()
2266 	 */
2267 	lasso_assign_string(login->private_data->soap_request_msg, request_msg);
2268 
2269 	return ret;
2270 }
2271 
2272 
2273 /**
2274  * lasso_login_process_response_msg:
2275  * @login: a #LassoLogin
2276  * @response_msg: the assertion response received
2277  *
2278  * Processes received assertion response.
2279  *
2280  * Return value: 0 on success; or
2281  * <itemizedlist>
2282  * <listitem><para>#LASSO_PARAM_ERROR_BAD_TYPE_OR_NULL_OBJ if login is not a #LassoLogin object,</para></listitem>
2283  * <listitem><para>#LASSO_PARAM_ERROR_INVALID_VALUE if response_msg is NULL,</para></listitem>
2284  * <listitem><para>#LASSO_PROFILE_ERROR_INVALID_MSG if the message is not a #LassoSamlpResponse (ID-FF 1.2) or a #LassoSamlp2ResponseMsg (SAML 2.0),</para></listitem>
2285  * <listitem><para>#LASSO_PROFILE_ERROR_RESPONSE_DOES_NOT_MATCH_REQUEST if the response does not refer to the request or if the response refer to an unknown request and <link linkend="strict-checking"><literal>strict-checking</literal></link> is activated ,</para></listitem>
2286  * <listitem><para>#LASSO_LOGIN_ERROR_REQUEST_DENIED the identity provided
2287  * returned a failure status of "RequestDenied"</para></listitem>
2288  * <listitem><para>#LASSO_LOGIN_ERROR_FEDERATION_NOT_FOUND if creation of a new
2289  * federation was not allowed and none existed,</para></listitem>
2290  * <listitem><para>#LASSO_LOGIN_ERROR_UNKNOWN_PRINCIPAL if authentication failed
2291  * or/and if the user cancelled the authentication,</para></listitem>
2292  * <listitem><para>#LASSO_LOGIN_ERROR_STATUS_NOT_SUCCESS, if the response status
2293  * is a failure but we have no more precise error code to report it, you must
2294  * look at the second level status in the response,</para></listitem>
2295  * <listitem><para>#LASSO_SERVER_ERROR_PROVIDER_NOT_FOUND, if the issuing
2296  * provider of the assertion is unknown,</para></listitem>
2297  * <listitem><para>#LASSO_PROFILE_ERROR_INVALID_ISSUER the issuer of the
2298  * assertion received, is not the expected one</para></listitem>
2299  * <listitem><para>#LASSO_PROFILE_ERROR_NAME_IDENTIFIER_NOT_FOUND no statement was fournd, or none statement contains a subject with a name identifier,</para></listitem>
2300  * <listitem><para>#LASSO_PROFILE_ERROR_MISSING_STATUS_CODE if the reponse is
2301  * missing a <literal>StatusCode</literal> element,</para></listitem>
2302  * <listitem><para>#LASSO_PROFILE_ERROR_MISSING_ASSERTION if the message does
2303  * not contain any assertion.</para></listitem>
2304  * </itemizedlist>
2305  **/
2306 gint
lasso_login_process_response_msg(LassoLogin * login,gchar * response_msg)2307 lasso_login_process_response_msg(LassoLogin *login, gchar *response_msg)
2308 {
2309 	LassoProfile *profile;
2310 
2311 	g_return_val_if_fail(LASSO_IS_LOGIN(login), LASSO_PARAM_ERROR_BAD_TYPE_OR_NULL_OBJ);
2312 	g_return_val_if_fail(response_msg != NULL, LASSO_PARAM_ERROR_INVALID_VALUE);
2313 
2314 	profile = LASSO_PROFILE(login);
2315 
2316 	IF_SAML2(profile) {
2317 		return lasso_saml20_login_process_response_msg(login, response_msg);
2318 	}
2319 
2320 
2321 	/* rebuild samlp:Response with response_msg */
2322 	lasso_assign_new_gobject(profile->response, lasso_node_new_from_soap(response_msg));
2323 	if (! LASSO_IS_SAMLP_RESPONSE(profile->response) ) {
2324 		lasso_release_gobject(profile->response);
2325 		return critical_error(LASSO_PROFILE_ERROR_INVALID_MSG);
2326 	}
2327 
2328 	/* Validate RequestID and InResponseTo */
2329 	if (profile->request || lasso_flag_strict_checking) {
2330 		char *request_id = NULL;
2331 		char *response_to = NULL;
2332 
2333 		if (LASSO_IS_SAMLP_REQUEST(profile->request)) {
2334 			request_id = LASSO_SAMLP_REQUEST_ABSTRACT(profile->request)->RequestID;
2335 		}
2336 		response_to = LASSO_SAMLP_RESPONSE_ABSTRACT(profile->response)->InResponseTo;
2337 
2338 		if ((! request_id && response_to) || /* response to an unknown request, only with
2339 							strict checking */
2340 		    (profile->request && ! response_to) || /* not a response to our request, because
2341 							     no ref */
2342 		    /* not a response to our request because of mismatch */
2343 		    (request_id && response_to && strcmp(request_id, response_to) != 0)) {
2344 			return critical_error(LASSO_PROFILE_ERROR_RESPONSE_DOES_NOT_MATCH_REQUEST);
2345 		} /* else no request and no inResponseTo, IDP initiated response, ok */
2346 	}
2347 
2348 	/* In the artifact profile we cannot verify the signature on the message, we must wait the
2349 	 * verification on the assertion, so for the moment the signature verification failed. */
2350 	profile->signature_status = LASSO_DS_ERROR_SIGNATURE_VERIFICATION_FAILED;
2351 
2352 	xmlNode *signed_response;
2353 	signed_response = lasso_node_get_original_xmlnode(LASSO_NODE(profile->response));
2354 	if (signed_response && profile->remote_providerID) {
2355 		LassoProvider *idp;
2356 
2357 		idp = LASSO_PROVIDER(lasso_server_get_provider(profile->server,
2358 					profile->remote_providerID));
2359 		profile->signature_status = lasso_provider_verify_saml_signature(idp,
2360 				signed_response, NULL);
2361 	}
2362 
2363 	return lasso_login_process_response_status_and_assertion(login);
2364 }
2365 
2366 
2367 
2368 /*****************************************************************************/
2369 /* private methods                                                           */
2370 /*****************************************************************************/
2371 
2372 static struct XmlSnippet schema_snippets[] = {
2373 	{ "AssertionArtifact", SNIPPET_CONTENT, G_STRUCT_OFFSET(LassoLogin, assertionArtifact), NULL, NULL, NULL},
2374 	{ "NameIDPolicy", SNIPPET_CONTENT, G_STRUCT_OFFSET(LassoLogin, nameIDPolicy), NULL, NULL, NULL},
2375 	{ "Assertion", SNIPPET_NODE_IN_CHILD, G_STRUCT_OFFSET(LassoLogin, assertion), NULL, NULL, NULL},
2376 	{ "RequestID", SNIPPET_CONTENT | SNIPPET_PRIVATE,
2377 		G_STRUCT_OFFSET(LassoLoginPrivate, request_id), NULL, NULL, NULL},
2378 	{ "LoginDumpVersion", SNIPPET_ATTRIBUTE, 0, NULL, NULL, NULL},
2379 	{ "ProtocolProfile", SNIPPET_CONTENT, 0, NULL, NULL, NULL},
2380 	{NULL, 0, 0, NULL, NULL, NULL}
2381 };
2382 
2383 static LassoNodeClass *parent_class = NULL;
2384 
2385 static xmlNode*
get_xmlNode(LassoNode * node,gboolean lasso_dump)2386 get_xmlNode(LassoNode *node, gboolean lasso_dump)
2387 {
2388 	xmlNode *xmlnode;
2389 	LassoLogin *login = LASSO_LOGIN(node);
2390 
2391 	xmlnode = parent_class->get_xmlNode(node, lasso_dump);
2392 	xmlSetProp(xmlnode, (xmlChar*)"LoginDumpVersion", (xmlChar*)"2");
2393 
2394 	if (login->protocolProfile == LASSO_LOGIN_PROTOCOL_PROFILE_BRWS_ART)
2395 		xmlNewTextChild(xmlnode, NULL, (xmlChar*)"ProtocolProfile", (xmlChar*)"Artifact");
2396 	else if (login->protocolProfile == LASSO_LOGIN_PROTOCOL_PROFILE_BRWS_POST)
2397 		xmlNewTextChild(xmlnode, NULL, (xmlChar*)"ProtocolProfile", (xmlChar*)"POST");
2398 	else if (login->protocolProfile == LASSO_LOGIN_PROTOCOL_PROFILE_REDIRECT)
2399 		xmlNewTextChild(xmlnode, NULL, (xmlChar*)"ProtocolProfile", (xmlChar*)"Redirect");
2400 
2401 	return xmlnode;
2402 }
2403 
2404 static int
init_from_xml(LassoNode * node,xmlNode * xmlnode)2405 init_from_xml(LassoNode *node, xmlNode *xmlnode)
2406 {
2407 	LassoLogin *login = LASSO_LOGIN(node);
2408 	xmlNode *t;
2409 	int rc = 0;
2410 
2411 	rc = parent_class->init_from_xml(node, xmlnode);
2412 	if (rc) return rc;
2413 
2414 	t = xmlnode->children;
2415 	while (t) {
2416 		if (t->type != XML_ELEMENT_NODE) {
2417 			t = t->next;
2418 			continue;
2419 		}
2420 		if (strcmp((char*)t->name, "ProtocolProfile") == 0) {
2421 			char *s;
2422 			s = (char*)xmlNodeGetContent(t);
2423 			if (strcmp(s, "Artifact") == 0)
2424 				login->protocolProfile = LASSO_LOGIN_PROTOCOL_PROFILE_BRWS_ART;
2425 			else if (strcmp(s, "POST") == 0)
2426 				login->protocolProfile = LASSO_LOGIN_PROTOCOL_PROFILE_BRWS_POST;
2427 			else if (strcmp(s, "Redirect") == 0)
2428 				login->protocolProfile = LASSO_LOGIN_PROTOCOL_PROFILE_REDIRECT;
2429 			xmlFree(s);
2430 		}
2431 		t = t->next;
2432 	}
2433 	return 0;
2434 }
2435 
2436 
2437 /*****************************************************************************/
2438 /* overridden parent class methods                                           */
2439 /*****************************************************************************/
2440 
2441 static void
dispose(GObject * object)2442 dispose(GObject *object)
2443 {
2444 	LassoLogin *login = LASSO_LOGIN(object);
2445 
2446 	lasso_release_string(login->private_data->soap_request_msg);
2447 	lasso_release_gobject(login->private_data->saml2_assertion);
2448 
2449 #ifdef LASSO_WSF_ENABLED
2450 	lasso_release_gobject(login->private_data->resourceId);
2451 	lasso_release_gobject(login->private_data->encryptedResourceId);
2452 #endif
2453 	lasso_release_string(login->private_data->request_id);
2454 	G_OBJECT_CLASS(parent_class)->dispose(object);
2455 }
2456 
2457 /*****************************************************************************/
2458 /* instance and class init functions */
2459 /*****************************************************************************/
2460 
2461 static void
instance_init(LassoLogin * login)2462 instance_init(LassoLogin *login)
2463 {
2464 	login->private_data = LASSO_LOGIN_GET_PRIVATE(login);
2465 	login->protocolProfile = 0;
2466 	login->assertionArtifact = NULL;
2467 	login->nameIDPolicy = NULL;
2468 	login->http_method = 0;
2469 }
2470 
2471 static void
class_init(LassoLoginClass * klass,void * unused G_GNUC_UNUSED)2472 class_init(LassoLoginClass *klass, void *unused G_GNUC_UNUSED)
2473 {
2474 	LassoNodeClass *nclass = LASSO_NODE_CLASS(klass);
2475 
2476 	parent_class = g_type_class_peek_parent(klass);
2477 	nclass->get_xmlNode = get_xmlNode;
2478 	nclass->init_from_xml = init_from_xml;
2479 	nclass->node_data = g_new0(LassoNodeClassData, 1);
2480 	lasso_node_class_set_nodename(nclass, "Login");
2481 	lasso_node_class_set_ns(nclass, LASSO_LASSO_HREF, LASSO_LASSO_PREFIX);
2482 	lasso_node_class_add_snippets(nclass, schema_snippets);
2483 	g_type_class_add_private(klass, sizeof(LassoLoginPrivate));
2484 
2485 	G_OBJECT_CLASS(klass)->dispose = dispose;
2486 }
2487 
2488 GType
lasso_login_get_type()2489 lasso_login_get_type()
2490 {
2491 	static GType this_type = 0;
2492 
2493 	if (!this_type) {
2494 		static const GTypeInfo this_info = {
2495 			sizeof(LassoLoginClass),
2496 			NULL,
2497 			NULL,
2498 			(GClassInitFunc) class_init,
2499 			NULL,
2500 			NULL,
2501 			sizeof(LassoLogin),
2502 			0,
2503 			(GInstanceInitFunc) instance_init,
2504 			NULL
2505 		};
2506 
2507 		this_type = g_type_register_static(LASSO_TYPE_PROFILE,
2508 				"LassoLogin", &this_info, 0);
2509 	}
2510 	return this_type;
2511 }
2512 
2513 /**
2514  * lasso_login_new
2515  * @server: the #LassoServer
2516  *
2517  * Creates a new #LassoLogin.
2518  *
2519  * Return value: a newly created #LassoLogin object; or NULL if an error
2520  *     occured
2521  **/
2522 LassoLogin*
lasso_login_new(LassoServer * server)2523 lasso_login_new(LassoServer *server)
2524 {
2525 	LassoLogin *login = NULL;
2526 
2527 	g_return_val_if_fail(LASSO_IS_SERVER(server), NULL);
2528 
2529 	login = g_object_new(LASSO_TYPE_LOGIN, NULL);
2530 	lasso_assign_gobject(LASSO_PROFILE(login)->server, server);
2531 
2532 	return login;
2533 }
2534 
2535 /**
2536  * lasso_login_new_from_dump:
2537  * @server: the #LassoServer
2538  * @dump: XML login dump
2539  *
2540  * Restores the @dump to a new #LassoLogin.
2541  *
2542  * Return value: a newly created #LassoLogin; or NULL if an error occured.
2543  **/
2544 LassoLogin*
lasso_login_new_from_dump(LassoServer * server,const gchar * dump)2545 lasso_login_new_from_dump(LassoServer *server, const gchar *dump)
2546 {
2547 	LassoLogin *login;
2548 
2549 	login = (LassoLogin*)lasso_node_new_from_dump(dump);
2550 	if (! LASSO_IS_LOGIN(login)) {
2551 		lasso_release_gobject(login);
2552 	} else {
2553 		lasso_assign_gobject(login->parent.server, server);
2554 	}
2555 	return login;
2556 }
2557 
2558 /**
2559  * lasso_login_dump:
2560  * @login: a #LassoLogin
2561  *
2562  * Dumps @login content to an XML string.
2563  *
2564  * Return value:(transfer full): the dump string.  It must be freed by the caller.
2565  **/
2566 gchar*
lasso_login_dump(LassoLogin * login)2567 lasso_login_dump(LassoLogin *login)
2568 {
2569 	return lasso_node_dump(LASSO_NODE(login));
2570 }
2571 
2572 
2573 /**
2574  * lasso_login_validate_request_msg:
2575  * @login: a #LassoLogin
2576  * @authentication_result: whether user has authenticated succesfully
2577  * @is_consent_obtained: whether user consent has been obtained
2578  *
2579  * Initializes a response to the authentication request received.
2580  *
2581  * Return value: 0 on success; or
2582  * <itemizedlist>
2583  * <listitem><para>#LASSO_PARAM_ERROR_BAD_TYPE_OR_NULL_OBJ if login is not a #LassoLogin object,</para></listitem>
2584  * <listitem><para>#LASSO_LOGIN_ERROR_REQUEST_DENIED</para> if @authentication_result if FALSE,</listitem>
2585  * <listitem><para>#LASSO_LOGIN_ERROR_INVALID_SIGNATURE if signature validation of the request
2586  * failed,</para></listitem>
2587  * <listitem><para>#LASSO_LOGIN_ERROR_UNSIGNED_AUTHN_REQUEST if no signature was present on the
2588  * request,</para></listitem>
2589  * <listitem><para>#LASSO_LOGIN_ERROR_FEDERATION_NOT_FOUND if federation policy is
2590  * #LASSO_LIB_NAMEID_POLICY_TYPE_NONE and no federation was found in the #LassoIdentity object
2591  * (ID-FF 1.2 case)</para></listitem>
2592  * <listitem><para>#LASSO_LOGIN_ERROR_INVALID_NAMEIDPOLICY if request policy is not one of
2593  * #LASSO_LIB_NAMEID_POLICY_TYPE_FEDERATED or #LASSO_LIB_NAMEID_POLICY_TYPE_ANY (ID-FF 1.2 case) or if no NameID policy was defined or the AllowCreate request flag is FALSE (SAML 2.0 case),</para></listitem>
2594  * <listitem><para>#LASSO_LOGIN_ERROR_CONSENT_NOT_OBTAINED if @is_consent_obtained is FALSE and
2595  * conssent was necessary (for example if the request does not communicate that consent was already
2596  * obtained from the user),</para></listitem>
2597  * <listitem><para>#LASSO_SERVER_ERROR_PROVIDER_NOT_FOUND if the requesting provider is unknown,</para></listitem>
2598  * </itemizedlist>
2599  **/
2600 int
lasso_login_validate_request_msg(LassoLogin * login,gboolean authentication_result,gboolean is_consent_obtained)2601 lasso_login_validate_request_msg(LassoLogin *login, gboolean authentication_result,
2602 		gboolean is_consent_obtained)
2603 {
2604 	LassoProfile *profile;
2605 	gint ret = 0;
2606 
2607 	g_return_val_if_fail(LASSO_IS_LOGIN(login), LASSO_PARAM_ERROR_BAD_TYPE_OR_NULL_OBJ);
2608 
2609 	profile = LASSO_PROFILE(login);
2610 
2611 	IF_SAML2(profile) {
2612 		return lasso_saml20_login_validate_request_msg(login,
2613 				authentication_result, is_consent_obtained);
2614 	}
2615 
2616 	/* modify AuthnResponse StatusCode if user authentication is not OK */
2617 	if (authentication_result == FALSE) {
2618 		lasso_profile_set_response_status(profile,
2619 				LASSO_LIB_STATUS_CODE_UNKNOWN_PRINCIPAL);
2620 		return LASSO_LOGIN_ERROR_REQUEST_DENIED;
2621 	}
2622 
2623 	/* if signature is not OK => modify AuthnResponse StatusCode */
2624 	if (profile->signature_status == LASSO_DS_ERROR_INVALID_SIGNATURE) {
2625 		lasso_profile_set_response_status(profile,
2626 				LASSO_LIB_STATUS_CODE_INVALID_SIGNATURE);
2627 		return LASSO_LOGIN_ERROR_INVALID_SIGNATURE;
2628 	}
2629 
2630 	if (profile->signature_status == LASSO_DS_ERROR_SIGNATURE_NOT_FOUND) {
2631 		/* Unsigned AuthnRequest */
2632 		lasso_profile_set_response_status(profile,
2633 				LASSO_LIB_STATUS_CODE_UNSIGNED_AUTHN_REQUEST);
2634 		return LASSO_LOGIN_ERROR_UNSIGNED_AUTHN_REQUEST;
2635 	}
2636 
2637 	if (profile->signature_status == 0 && authentication_result == TRUE) {
2638 		/* process federation */
2639 		ret = lasso_login_process_federation(login, is_consent_obtained);
2640 		if (ret != 0)
2641 			return ret;
2642 	}
2643 
2644 	lasso_profile_set_response_status(profile, LASSO_SAML_STATUS_CODE_SUCCESS);
2645 
2646 	return ret;
2647 }
2648 
2649 int
lasso_login_process_paos_response_msg(LassoLogin * login,gchar * msg)2650 lasso_login_process_paos_response_msg(LassoLogin *login, gchar *msg)
2651 {
2652 	LassoProfile *profile;
2653 
2654 	g_return_val_if_fail(LASSO_IS_LOGIN(login), LASSO_PARAM_ERROR_BAD_TYPE_OR_NULL_OBJ);
2655 	g_return_val_if_fail(msg != NULL, LASSO_PARAM_ERROR_INVALID_VALUE);
2656 
2657 	profile = LASSO_PROFILE(login);
2658 
2659 	IF_SAML2(profile) {
2660 		return lasso_saml20_login_process_paos_response_msg(login, msg);
2661 	}
2662 
2663 	return 0;
2664 }
2665 
2666 /**
2667  * lasso_login_get_assertion:
2668  * @login: a #LassoLogin object
2669  *
2670  * Return the last build assertion.
2671  *
2672  * Return value: a #LassoNode representing the build assertion (generally a #LassoSamlAssertion when
2673  * using ID-FF 1.2 or a #LassoSaml2Assertion when using SAML 2.0)
2674  */
2675 LassoNode*
lasso_login_get_assertion(LassoLogin * login)2676 lasso_login_get_assertion(LassoLogin *login)
2677 {
2678 	g_return_val_if_fail (LASSO_IS_LOGIN (login), NULL);
2679 
2680 	if (login->private_data && login->private_data->saml2_assertion)
2681 		return (LassoNode*)g_object_ref(login->private_data->saml2_assertion);
2682 
2683 	return (LassoNode*)g_object_ref(login->assertion);
2684 }
2685