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