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:lecp
26  * @short_description: Liberty Enabled Client and Proxy Profile (ID-FF)
27  *
28  **/
29 
30 #include "../xml/private.h"
31 #include <libxml/xpath.h>
32 #include <libxml/xpathInternals.h>
33 
34 #include "lecp.h"
35 #include "profileprivate.h"
36 #include "../utils.h"
37 
38 #include "../utils.h"
39 
40 /*****************************************************************************/
41 /* public methods                                                            */
42 /*****************************************************************************/
43 
44 
45 /**
46  * lasso_lecp_build_authn_request_envelope_msg:
47  * @lecp: a #LassoLecp
48  *
49  * Builds an enveloped authentication request message.  Sets @msg_body to that
50  * message.
51  *
52  * Return value: 0 on success; or a negative value otherwise.
53  **/
54 gint
lasso_lecp_build_authn_request_envelope_msg(LassoLecp * lecp)55 lasso_lecp_build_authn_request_envelope_msg(LassoLecp *lecp)
56 {
57 	LassoProfile *profile;
58 	gchar *assertionConsumerServiceURL;
59 	xmlNode *msg;
60 
61 	g_return_val_if_fail(LASSO_IS_LECP(lecp), LASSO_PARAM_ERROR_BAD_TYPE_OR_NULL_OBJ);
62 
63 	profile = LASSO_PROFILE(lecp);
64 
65 	assertionConsumerServiceURL = lasso_provider_get_assertion_consumer_service_url(
66 			LASSO_PROVIDER(profile->server), NULL);
67 	if (assertionConsumerServiceURL == NULL) {
68 		return critical_error(LASSO_PROFILE_ERROR_UNKNOWN_PROFILE_URL);
69 	}
70 
71 	if (profile->request == NULL) {
72 		return LASSO_PROFILE_ERROR_MISSING_REQUEST;
73 	}
74 
75 	lasso_assign_new_gobject(lecp->authnRequestEnvelope, lasso_lib_authn_request_envelope_new_full(
76 			LASSO_LIB_AUTHN_REQUEST(profile->request),
77 			LASSO_PROVIDER(profile->server)->ProviderID,
78 			assertionConsumerServiceURL));
79 	if (lecp->authnRequestEnvelope == NULL) {
80 		return critical_error(LASSO_PROFILE_ERROR_BUILDING_REQUEST_FAILED);
81 	}
82 
83 	LASSO_SAMLP_REQUEST_ABSTRACT(lecp->authnRequestEnvelope->AuthnRequest)->private_key_file =
84 		LASSO_PROFILE(lecp)->server->private_key;
85 	LASSO_SAMLP_REQUEST_ABSTRACT(lecp->authnRequestEnvelope->AuthnRequest)->certificate_file =
86 		LASSO_PROFILE(lecp)->server->certificate;
87 	msg = lasso_node_get_xmlNode(LASSO_NODE(lecp->authnRequestEnvelope), FALSE);
88 
89 	lasso_assign_new_string(profile->msg_body, lasso_xmlnode_to_string(msg, 0, 0))
90 
91 	if (profile->msg_body == NULL) {
92 		return LASSO_PROFILE_ERROR_BUILDING_REQUEST_FAILED;
93 	}
94 
95 	return 0;
96 }
97 
98 /**
99  * lasso_lecp_build_authn_request_msg:
100  * @lecp: a #LassoLecp
101  *
102  * Builds an authentication request. The data for the sending of the request are
103  * stored in @msg_url and @msg_body (SOAP POST).
104  *
105  * Return value: 0 on success; or a negative value otherwise.
106  **/
107 int
lasso_lecp_build_authn_request_msg(LassoLecp * lecp)108 lasso_lecp_build_authn_request_msg(LassoLecp *lecp)
109 {
110 	LassoProfile *profile;
111 	LassoProvider *remote_provider;
112 
113 	g_return_val_if_fail(LASSO_IS_LECP(lecp), LASSO_PARAM_ERROR_BAD_TYPE_OR_NULL_OBJ);
114 
115 	profile = LASSO_PROFILE(lecp);
116 
117 	if (profile->remote_providerID == NULL) {
118 		return critical_error(LASSO_PROFILE_ERROR_MISSING_REMOTE_PROVIDERID);
119 	}
120 
121 	remote_provider = lasso_server_get_provider(profile->server, profile->remote_providerID);
122 	if (remote_provider == NULL) {
123 		return critical_error(LASSO_SERVER_ERROR_PROVIDER_NOT_FOUND);
124 	}
125 
126 	lasso_assign_new_string(profile->msg_url, lasso_provider_get_metadata_one(
127 			remote_provider, "SingleSignOnServiceURL"));
128 	/* msg_body has usally been set in
129 	 * lasso_lecp_process_authn_request_envelope_msg() */
130 	if (profile->msg_body == NULL)
131 		return critical_error(LASSO_PROFILE_ERROR_BUILDING_MESSAGE_FAILED);
132 
133 	return 0;
134 }
135 
136 
137 /**
138  * lasso_lecp_build_authn_response_msg:
139  * @lecp: a #LassoLecp
140  *
141  * Builds the lecp authentication response message (base64).  Sets @msg_body to
142  * that message.
143  *
144  * Return value: 0 on success; or a negative value otherwise.
145  **/
146 int
lasso_lecp_build_authn_response_msg(LassoLecp * lecp)147 lasso_lecp_build_authn_response_msg(LassoLecp *lecp)
148 {
149 	LassoProfile *profile;
150 
151 	g_return_val_if_fail(LASSO_IS_LECP(lecp), LASSO_PARAM_ERROR_BAD_TYPE_OR_NULL_OBJ);
152 
153 	profile = LASSO_PROFILE(lecp);
154 	lasso_profile_clean_msg_info(profile);
155 
156 	lasso_assign_string(profile->msg_url, lecp->assertionConsumerServiceURL);
157 	if (profile->msg_url == NULL) {
158 		return critical_error(LASSO_PROFILE_ERROR_UNKNOWN_PROFILE_URL);
159 	}
160 	lasso_assign_new_string(profile->msg_body, lasso_node_export_to_base64(LASSO_NODE(profile->response)));
161 	if (profile->msg_body == NULL) {
162 		return critical_error(LASSO_PROFILE_ERROR_BUILDING_MESSAGE_FAILED);
163 	}
164 
165 	return 0;
166 }
167 
168 
169 /**
170  * lasso_lecp_build_authn_response_envelope_msg:
171  * @lecp: a #LassoLecp
172  *
173  * Builds the enveloped LECP authentication response message (SOAP message).
174  * Sets @msg_body to that message.
175  *
176  * Return value: 0 on success; or a negative value otherwise.
177  **/
178 gint
lasso_lecp_build_authn_response_envelope_msg(LassoLecp * lecp)179 lasso_lecp_build_authn_response_envelope_msg(LassoLecp *lecp)
180 {
181 	LassoProfile  *profile;
182 	LassoProvider *provider;
183 	gchar *assertionConsumerServiceURL;
184 
185 	g_return_val_if_fail(LASSO_IS_LECP(lecp), LASSO_PARAM_ERROR_BAD_TYPE_OR_NULL_OBJ);
186 
187 	profile = LASSO_PROFILE(lecp);
188 
189 	if (LASSO_IS_LIB_AUTHN_RESPONSE(profile->response) == FALSE) {
190 		return LASSO_PROFILE_ERROR_MISSING_RESPONSE;
191 	}
192 
193 	provider = lasso_server_get_provider(profile->server, profile->remote_providerID);
194 	if (provider == NULL) {
195 		return critical_error(LASSO_SERVER_ERROR_PROVIDER_NOT_FOUND);
196 	}
197 
198 	/* build lib:AuthnResponse */
199 	lasso_login_build_authn_response_msg(LASSO_LOGIN(lecp));
200 
201 	assertionConsumerServiceURL = lasso_provider_get_assertion_consumer_service_url(
202 			provider, NULL);
203 	if (assertionConsumerServiceURL == NULL) {
204 		return critical_error(LASSO_PROFILE_ERROR_UNKNOWN_PROFILE_URL);
205 	}
206 
207 	lasso_release (LASSO_PROFILE(lecp)->msg_body);
208 	lasso_release (LASSO_PROFILE(lecp)->msg_url);
209 
210 	lasso_assign_new_gobject(lecp->authnResponseEnvelope, lasso_lib_authn_response_envelope_new(
211 			LASSO_LIB_AUTHN_RESPONSE(profile->response),
212 			assertionConsumerServiceURL));
213 	LASSO_SAMLP_RESPONSE_ABSTRACT(lecp->authnResponseEnvelope->AuthnResponse
214 			)->private_key_file = profile->server->private_key;
215 	LASSO_SAMLP_RESPONSE_ABSTRACT(lecp->authnResponseEnvelope->AuthnResponse
216 			)->certificate_file = profile->server->certificate;
217 	profile->msg_body = lasso_node_export_to_soap(LASSO_NODE(lecp->authnResponseEnvelope));
218 
219 	if (profile->msg_body == NULL) {
220 		return critical_error(LASSO_PROFILE_ERROR_BUILDING_MESSAGE_FAILED);
221 	}
222 
223 	return 0;
224 }
225 
226 /**
227  * lasso_lecp_init_authn_request:
228  * @lecp: a #LassoLecp
229  * @remote_providerID: the providerID of the identity provider. When NULL, the
230  *     first known identity provider is used.
231  *
232  * Initializes a new lib:AuthnRequest.
233  *
234  * Return value: 0 on success; or a negative value otherwise.
235  **/
236 int
lasso_lecp_init_authn_request(LassoLecp * lecp,const char * remote_providerID)237 lasso_lecp_init_authn_request(LassoLecp *lecp, const char *remote_providerID)
238 {
239 	gint res;
240 
241 	g_return_val_if_fail(LASSO_IS_LECP(lecp), LASSO_PARAM_ERROR_BAD_TYPE_OR_NULL_OBJ);
242 
243 	/* FIXME : BAD usage of http_method
244 	   using POST method so that the lib:AuthnRequest is initialize with
245 	   a signature template */
246 	res = lasso_login_init_authn_request(LASSO_LOGIN(lecp), remote_providerID,
247 			LASSO_HTTP_METHOD_POST);
248 
249 	return res;
250 }
251 
252 
253 /**
254  * lasso_lecp_process_authn_request_msg:
255  * @lecp: a #LassoLecp
256  * @authn_request_msg: the authentication request received
257  *
258  * Processes received authentication request, checks it is signed correctly,
259  * checks if requested protocol profile is supported, etc.
260  *
261  * Return value: 0 on success; or a negative value otherwise.
262  **/
263 int
lasso_lecp_process_authn_request_msg(LassoLecp * lecp,const char * authn_request_msg)264 lasso_lecp_process_authn_request_msg(LassoLecp *lecp, const char *authn_request_msg)
265 {
266 	g_return_val_if_fail(LASSO_IS_LECP(lecp), LASSO_PARAM_ERROR_BAD_TYPE_OR_NULL_OBJ);
267 	g_return_val_if_fail(authn_request_msg != NULL, LASSO_PARAM_ERROR_INVALID_VALUE);
268 
269 	return lasso_login_process_authn_request_msg(LASSO_LOGIN(lecp), authn_request_msg);
270 }
271 
272 
273 /**
274  * lasso_lecp_process_authn_request_envelope_msg:
275  * @lecp: a #LassoLecp
276  * @request_msg: the enveloped authentication request received
277  *
278  * Processes received enveloped authentication request, extracts the
279  * authentication request out of it.
280  *
281  * Return value: 0 on success; or a negative value otherwise.
282  **/
283 int
lasso_lecp_process_authn_request_envelope_msg(LassoLecp * lecp,const char * request_msg)284 lasso_lecp_process_authn_request_envelope_msg(LassoLecp *lecp, const char *request_msg)
285 {
286 	xmlDoc *doc;
287 	xmlXPathContext *xpathCtx;
288 	xmlXPathObject *xpathObj;
289 	xmlNode *soap_envelope, *soap_body, *authn_request;
290 
291 	g_return_val_if_fail(LASSO_IS_LECP(lecp), LASSO_PARAM_ERROR_BAD_TYPE_OR_NULL_OBJ);
292 	g_return_val_if_fail(request_msg != NULL, LASSO_PARAM_ERROR_INVALID_VALUE);
293 
294 	doc = xmlParseMemory(request_msg, strlen(request_msg));
295 	xpathCtx = xmlXPathNewContext(doc);
296 	xmlXPathRegisterNs(xpathCtx, (xmlChar*)"lib", (xmlChar*)LASSO_LIB_HREF);
297 	/* TODO: will need to use another href for id-ff 1.1 support */
298 	xpathObj = xmlXPathEvalExpression((xmlChar*)"//lib:AuthnRequest", xpathCtx);
299 
300 	if (xpathObj == NULL) {
301 		xmlXPathFreeContext(xpathCtx);
302 		return critical_error(LASSO_PROFILE_ERROR_INVALID_MSG);
303 	}
304 
305 	if (xpathObj->nodesetval == NULL || xpathObj->nodesetval->nodeNr == 0) {
306 		xmlXPathFreeContext(xpathCtx);
307 		xmlXPathFreeObject(xpathObj);
308 		return critical_error(LASSO_PROFILE_ERROR_INVALID_MSG);
309 	}
310 
311 	authn_request = xmlCopyNode(xpathObj->nodesetval->nodeTab[0], 1);
312 	xmlXPathFreeContext(xpathCtx);
313 	xmlXPathFreeObject(xpathObj);
314 	lasso_release_doc(doc);
315 	xpathCtx = NULL;
316 	xpathObj = NULL;
317 	doc = NULL;
318 
319 	soap_envelope = xmlNewNode(NULL, (xmlChar*)"Envelope");
320 	xmlSetNs(soap_envelope, xmlNewNs(soap_envelope,
321 				(xmlChar*)LASSO_SOAP_ENV_HREF, (xmlChar*)LASSO_SOAP_ENV_PREFIX));
322 
323 	soap_body = xmlNewTextChild(soap_envelope, NULL, (xmlChar*)"Body", NULL);
324 	xmlAddChild(soap_body, authn_request);
325 
326 	lasso_assign_new_string(LASSO_PROFILE(lecp)->msg_body,
327 			lasso_xmlnode_to_string(soap_envelope, 0, 0));
328 	xmlFreeNode(soap_envelope);
329 
330 
331 	return 0;
332 }
333 
334 
335 /**
336  * lasso_lecp_process_authn_response_envelope_msg:
337  * @lecp: a #LassoLecp
338  * @response_msg: the enveloped authentication response received
339  *
340  * Processes received enveloped authentication response, extracts the
341  * authentication response out of it and stores it in @response.
342  *
343  * Return value: 0 on success; or a negative value otherwise.
344  **/
345 int
lasso_lecp_process_authn_response_envelope_msg(LassoLecp * lecp,const char * response_msg)346 lasso_lecp_process_authn_response_envelope_msg(LassoLecp *lecp, const char *response_msg)
347 {
348 	LassoProfile *profile;
349 	LassoMessageFormat format;
350 
351 	g_return_val_if_fail(LASSO_IS_LECP(lecp), LASSO_PARAM_ERROR_BAD_TYPE_OR_NULL_OBJ);
352 	g_return_val_if_fail(response_msg != NULL, LASSO_PARAM_ERROR_INVALID_VALUE);
353 
354 	profile = LASSO_PROFILE(lecp);
355 
356 	lecp->authnResponseEnvelope = lasso_lib_authn_response_envelope_new(NULL, NULL);
357 	format = lasso_node_init_from_message(LASSO_NODE(lecp->authnResponseEnvelope),
358 			response_msg);
359 	if (format == LASSO_MESSAGE_FORMAT_UNKNOWN || format == LASSO_MESSAGE_FORMAT_ERROR) {
360 		return critical_error(LASSO_PROFILE_ERROR_INVALID_MSG);
361 	}
362 
363 	profile->response = LASSO_NODE(g_object_ref(lecp->authnResponseEnvelope->AuthnResponse));
364 	if (profile->response == NULL) {
365 		return LASSO_PROFILE_ERROR_MISSING_RESPONSE;
366 	}
367 
368 	lecp->assertionConsumerServiceURL = g_strdup(
369 			lecp->authnResponseEnvelope->AssertionConsumerServiceURL);
370 	if (lecp->assertionConsumerServiceURL == NULL){
371 		return critical_error(LASSO_PROFILE_ERROR_UNKNOWN_PROFILE_URL);
372 	}
373 
374 	return 0;
375 }
376 
377 /**
378  * lasso_lecp_destroy:
379  * @lecp: a #LassoLecp
380  *
381  * Destroys a #LassoLecp object
382  *
383  **/
384 void
lasso_lecp_destroy(LassoLecp * lecp)385 lasso_lecp_destroy(LassoLecp *lecp)
386 {
387 	lasso_node_destroy(LASSO_NODE(lecp));
388 }
389 
390 /*****************************************************************************/
391 /* private methods                                                           */
392 /*****************************************************************************/
393 
394 static LassoNodeClass *parent_class = NULL;
395 
396 /*****************************************************************************/
397 /* overridden parent class methods                                           */
398 /*****************************************************************************/
399 
400 static void
finalize(GObject * object)401 finalize(GObject *object)
402 {
403 	G_OBJECT_CLASS(parent_class)->finalize(object);
404 }
405 
406 /*****************************************************************************/
407 /* instance and class init functions                                         */
408 /*****************************************************************************/
409 
410 static void
instance_init(LassoLecp * lecp)411 instance_init(LassoLecp *lecp)
412 {
413 	lecp->authnRequestEnvelope = NULL;
414 	lecp->authnResponseEnvelope = NULL;
415 	lecp->assertionConsumerServiceURL = NULL;
416 }
417 
418 static void
class_init(LassoLecpClass * klass,void * unused G_GNUC_UNUSED)419 class_init(LassoLecpClass *klass, void *unused G_GNUC_UNUSED)
420 {
421 	LassoNodeClass *nclass = LASSO_NODE_CLASS(klass);
422 	parent_class = g_type_class_peek_parent(klass);
423 
424 	G_OBJECT_CLASS(klass)->finalize = finalize;
425 	nclass->node_data = g_new0(LassoNodeClassData, 1);
426 	lasso_node_class_set_nodename(nclass, "Lecp");
427 	lasso_node_class_set_ns(nclass, LASSO_LASSO_HREF, LASSO_LASSO_PREFIX);
428 }
429 
430 GType
lasso_lecp_get_type()431 lasso_lecp_get_type()
432 {
433 	static GType this_type = 0;
434 
435 	if (!this_type) {
436 		static const GTypeInfo this_info = {
437 			sizeof (LassoLecpClass),
438 			NULL,
439 			NULL,
440 			(GClassInitFunc) class_init,
441 			NULL,
442 			NULL,
443 			sizeof(LassoLecp),
444 			0,
445 			(GInstanceInitFunc) instance_init,
446 			NULL
447 		};
448 
449 		this_type = g_type_register_static(LASSO_TYPE_LOGIN,
450 				"LassoLecp", &this_info, 0);
451 	}
452 	return this_type;
453 }
454 
455 /**
456  * lasso_lecp_new
457  * @server: the #LassoServer
458  *
459  * Creates a new #LassoLecp.
460  *
461  * Return value: a newly created #LassoLecp object; or NULL if an error
462  *     occured
463  **/
464 LassoLecp*
lasso_lecp_new(LassoServer * server)465 lasso_lecp_new(LassoServer *server)
466 {
467 	LassoLecp *lecp;
468 
469 	lecp = g_object_new(LASSO_TYPE_LECP, NULL);
470 	LASSO_PROFILE(lecp)->server = g_object_ref(server);
471 
472 	return lecp;
473 }
474