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:server
26  * @short_description: Representation of the current server
27  *
28  * It holds the data about a provider, other providers it knows, which
29  * certificates to use, etc.
30  **/
31 
32 #include "../xml/private.h"
33 #include <xmlsec/base64.h>
34 #include <xmlsec/xmltree.h>
35 
36 #include <config.h>
37 #include "server.h"
38 #include "providerprivate.h"
39 #include "serverprivate.h"
40 #include "../saml-2.0/serverprivate.h"
41 #include "../utils.h"
42 #include "../debug.h"
43 #include "../lasso_config.h"
44 #ifdef LASSO_WSF_ENABLED
45 #include "../id-wsf/id_ff_extensions_private.h"
46 #include "../id-wsf-2.0/serverprivate.h"
47 #endif
48 
49 #define RSA_SHA1 "RSA_SHA1"
50 #define DSA_SHA1 "DSA_SHA1"
51 #define HMAC_SHA1 "HMAC_SHA1"
52 #define RSA_SHA256 "RSA_SHA256"
53 #define HMAC_SHA256 "HMAC_SHA256"
54 #define RSA_SHA384 "RSA_SHA384"
55 #define HMAC_SHA384 "HMAC_SHA384"
56 #define RSA_SHA512 "RSA_SHA512"
57 #define HMAC_SHA512 "HMAC_SHA512"
58 
59 /*****************************************************************************/
60 /* public methods                                                            */
61 /*****************************************************************************/
62 
63 static lasso_error_t
lasso_server_add_provider_helper(LassoServer * server,LassoProviderRole role,const gchar * metadata,const gchar * public_key,const gchar * ca_cert_chain,LassoProvider * (* provider_constructor)(LassoProviderRole role,const char * metadata,const char * public_key,const char * ca_cert_chain))64 lasso_server_add_provider_helper(LassoServer *server, LassoProviderRole role,
65 		const gchar *metadata, const gchar *public_key, const gchar *ca_cert_chain,
66 		LassoProvider *(*provider_constructor)(LassoProviderRole role,
67 		const char *metadata, const char *public_key, const char *ca_cert_chain))
68 {
69 	LassoProvider *provider;
70 	lasso_error_t rc = 0;
71 
72 	g_return_val_if_fail(LASSO_IS_SERVER(server), LASSO_PARAM_ERROR_BAD_TYPE_OR_NULL_OBJ);
73 	g_return_val_if_fail(metadata != NULL, LASSO_PARAM_ERROR_INVALID_VALUE);
74 
75 	provider = provider_constructor(role, metadata, public_key, ca_cert_chain);
76 	goto_cleanup_if_fail_with_rc(provider != NULL, critical_error(LASSO_SERVER_ERROR_ADD_PROVIDER_FAILED));
77 
78 	provider->role = role;
79 
80 	if (LASSO_PROVIDER(server)->private_data->conformance == LASSO_PROTOCOL_SAML_2_0 && provider->private_data->conformance != LASSO_PROTOCOL_SAML_2_0) {
81 		goto_cleanup_with_rc(LASSO_SERVER_ERROR_ADD_PROVIDER_PROTOCOL_MISMATCH);
82 	}
83 
84 	if (LASSO_PROVIDER(server)->private_data->conformance == LASSO_PROTOCOL_LIBERTY_1_2
85 		&& provider->private_data->conformance > LASSO_PROTOCOL_LIBERTY_1_2) {
86 		goto_cleanup_with_rc(LASSO_SERVER_ERROR_ADD_PROVIDER_PROTOCOL_MISMATCH);
87 	}
88 
89 	lasso_server_add_provider2(server, provider);
90 
91 cleanup:
92 	lasso_release_gobject(provider);
93 	return rc;
94 }
95 
96 /**
97  * lasso_server_add_provider:
98  * @server: a #LassoServer
99  * @role: provider role, identity provider or service provider
100  * @metadata: path to the provider metadata file
101  * @public_key:(allow-none): provider public key file (may be a certificate) or NULL
102  * @ca_cert_chain:(allow-none): provider CA certificate chain file or NULL
103  *
104  * Creates a new #LassoProvider and makes it known to the @server
105  *
106  * Return value: 0 on success; a negative value if an error occured.
107  **/
108 lasso_error_t
lasso_server_add_provider(LassoServer * server,LassoProviderRole role,const gchar * metadata,const gchar * public_key,const gchar * ca_cert_chain)109 lasso_server_add_provider(LassoServer *server, LassoProviderRole role,
110 		const gchar *metadata, const gchar *public_key, const gchar *ca_cert_chain)
111 {
112 	return lasso_server_add_provider_helper(server, role, metadata,
113 			public_key, ca_cert_chain, lasso_provider_new);
114 }
115 
116 /**
117  * lasso_server_add_provider2:
118  * @server: a #LassoServer object
119  * @provider: a #LassoProvider object
120  *
121  * Add @provider to the list of known providers object of @server.
122  *
123  * Return 0 if successful, LASSO_PARAM_ERROR_BAD_TYPE_OR_NULL_OBJ otherwise.
124  */
125 lasso_error_t
lasso_server_add_provider2(LassoServer * server,LassoProvider * provider)126 lasso_server_add_provider2(LassoServer *server, LassoProvider *provider)
127 {
128 	lasso_bad_param(SERVER, server);
129 	lasso_bad_param(PROVIDER, provider);
130 	g_return_val_if_fail(provider->ProviderID, LASSO_PARAM_ERROR_NON_INITIALIZED_OBJECT);
131 	g_return_val_if_fail(server->providers, LASSO_PARAM_ERROR_NON_INITIALIZED_OBJECT);
132 
133 	g_hash_table_insert(server->providers, g_strdup(provider->ProviderID), g_object_ref(provider));
134 
135 	return 0;
136 }
137 
138 /**
139  * lasso_server_add_provider_from_buffer:
140  * @server: a #LassoServer
141  * @role: provider role, identity provider or service provider
142  * @metadata: a string buffer containg the metadata file for a new provider
143  * @public_key:(allow-none): provider public key file (may be a certificate) or NULL
144  * @ca_cert_chain:(allow-none): provider CA certificate chain file or NULL
145  *
146  * Creates a new #LassoProvider and makes it known to the @server
147  *
148  * Return value: 0 on success; a negative value if an error occured.
149  **/
150 gint
lasso_server_add_provider_from_buffer(LassoServer * server,LassoProviderRole role,const gchar * metadata,const gchar * public_key,const gchar * ca_cert_chain)151 lasso_server_add_provider_from_buffer(LassoServer *server, LassoProviderRole role,
152 		const gchar *metadata, const gchar *public_key, const gchar *ca_cert_chain)
153 {
154 	return lasso_server_add_provider_helper(server, role, metadata,
155 			public_key, ca_cert_chain, lasso_provider_new_from_buffer);
156 }
157 
158 
159 /**
160  * lasso_server_destroy:
161  * @server: a #LassoServer
162  *
163  * Destroys a server.
164  **/
165 void
lasso_server_destroy(LassoServer * server)166 lasso_server_destroy(LassoServer *server)
167 {
168 	lasso_node_destroy(LASSO_NODE(server));
169 }
170 
171 
172 /**
173  * lasso_server_set_encryption_private_key:
174  * @server: a #LassoServer
175  * @filename_or_buffer:(allow-none): file name of the encryption key to load or its content as a
176  * NULL-terminated string.
177  *
178  * Load an encryption private key from a file and set it in the server object
179  *
180  * If @filename_or_buffer is NULL, it frees the currently setted key.
181  *
182  * Return value: 0 on success; another value if an error occured.
183  * Deprecated: 2.3: Use lasso_server_set_encryption_private_key_with_password() instead.
184  **/
185 int
lasso_server_set_encryption_private_key(LassoServer * server,const gchar * filename_or_buffer)186 lasso_server_set_encryption_private_key(LassoServer *server, const gchar *filename_or_buffer)
187 {
188 	return lasso_server_set_encryption_private_key_with_password(server, filename_or_buffer,
189 			NULL);
190 }
191 
192 /**
193  * lasso_server_set_encryption_private_key_with_password:
194  * @server: a #LassoServer
195  * @filename_or_buffer:(allow-none): file name of the encryption key to load or its content as a
196  * NULL-terminated string.
197  * @password:(allow-none): an optional password to decrypt the encryption key.
198  *
199  * Load an encryption private key from a file and set it in the server object. If @password is
200  * non-NULL try to decrypt the key with it.
201  *
202  * If @filename_or_buffer is NULL, it frees the currently setted key.
203  *
204  * Return value: 0 on success; another value if an error occured.
205  * Since: 2.3
206  **/
207 int
lasso_server_set_encryption_private_key_with_password(LassoServer * server,const gchar * filename_or_buffer,const gchar * password)208 lasso_server_set_encryption_private_key_with_password(LassoServer *server,
209 		const gchar *filename_or_buffer, const gchar *password)
210 {
211 	if (filename_or_buffer) {
212 		xmlSecKey *key = lasso_xmlsec_load_private_key(filename_or_buffer, password,
213 				server->signature_method, NULL);
214 		if (! key || ! (xmlSecKeyGetType(key) & xmlSecKeyDataTypePrivate)) {
215 			return LASSO_SERVER_ERROR_SET_ENCRYPTION_PRIVATE_KEY_FAILED;
216 		}
217 		lasso_list_add_new_sec_key(server->private_data->encryption_private_keys, key);
218 	}
219 
220 	return 0;
221 }
222 
223 /**
224  * lasso_server_load_affiliation:
225  * @server: a #LassoServer
226  * @filename: file name of the affiliation metadata to load
227  *
228  * Load an affiliation metadata file into @server; this must be called after
229  * providers have been added to @server.
230  *
231  * Return value: 0 on success; another value if an error occured.
232  **/
233 int
lasso_server_load_affiliation(LassoServer * server,const gchar * filename)234 lasso_server_load_affiliation(LassoServer *server, const gchar *filename)
235 {
236 	LassoProvider *provider = LASSO_PROVIDER(server);
237 	xmlDoc *doc;
238 	xmlNode *node;
239 	int rc = 0;
240 
241 	doc = lasso_xml_parse_file(filename);
242 	goto_cleanup_if_fail_with_rc (doc != NULL, LASSO_XML_ERROR_INVALID_FILE);
243 
244 	node = xmlDocGetRootElement(doc);
245 	goto_cleanup_if_fail_with_rc (node != NULL && node->ns != NULL, LASSO_XML_ERROR_NODE_NOT_FOUND);
246 
247 	if (provider->private_data->conformance == LASSO_PROTOCOL_SAML_2_0) {
248 		rc = lasso_saml20_server_load_affiliation(server, node);
249 	} else {
250 		/* affiliations are not supported in ID-FF 1.2 mode */
251 		rc = LASSO_ERROR_UNIMPLEMENTED;
252 	}
253 cleanup:
254 	lasso_release_doc(doc);
255 	return rc;
256 }
257 
258 /**
259  * lasso_server_get_endpoint_url_by_id:
260  * @server: a #LassoServer
261  * @provider_id: the EntityID whose endpoints will be examined.
262  * @endpoint_description: string describing criteria used to select endpoint.
263  *
264  * Locate the provider in the server's list of providers, then select an
265  * endpoint given the @endpoint_description and return than endpoint's URL.
266  * If the provider cannot be found or if the provider does not have a
267  * matching endpoint NULL will be returned.
268  *
269  * Returns: url (must be freed by caller)
270  */
271 gchar *
lasso_server_get_endpoint_url_by_id(const LassoServer * server,const gchar * provider_id,const gchar * endpoint_description)272 lasso_server_get_endpoint_url_by_id(const LassoServer *server, const gchar *provider_id,
273 									const gchar *endpoint_description)
274 {
275 	LassoProvider *provider;
276 	gchar *url = NULL;
277 
278 	provider = lasso_server_get_provider(server, provider_id);
279 	if (!provider) return NULL;
280 
281 	url = lasso_provider_get_metadata_one(provider, endpoint_description);
282 
283 	return url;
284 }
285 
286 /*****************************************************************************/
287 /* private methods                                                           */
288 /*****************************************************************************/
289 
290 static struct XmlSnippet schema_snippets[] = {
291 	{ "PrivateKeyFilePath", SNIPPET_CONTENT, G_STRUCT_OFFSET(LassoServer, private_key), NULL,
292 		NULL, NULL},
293 	{ "PrivateKeyPassword", SNIPPET_CONTENT,
294 		G_STRUCT_OFFSET(LassoServer, private_key_password), NULL, NULL, NULL},
295 	{ "CertificateFilePath", SNIPPET_CONTENT, G_STRUCT_OFFSET(LassoServer, certificate), NULL,
296 		NULL, NULL},
297 	{ "SignatureMethod", SNIPPET_ATTRIBUTE, 0, NULL, NULL, NULL },
298 	{ "Providers", SNIPPET_LIST_NODES, 0, NULL, NULL, NULL },
299 	{ "ServerDumpVersion", SNIPPET_ATTRIBUTE, 0, NULL, NULL, NULL },
300 #ifdef LASSO_WSF_ENABLED
301 	{ "Services", SNIPPET_LIST_NODES, 0, NULL, NULL, NULL },
302 	{ "SvcMDs", SNIPPET_LIST_NODES, 0, NULL, NULL, NULL },
303 #endif
304 
305 	{NULL, 0, 0, NULL, NULL, NULL}
306 };
307 
308 static LassoNodeClass *parent_class = NULL;
309 
310 static void
add_provider_childnode(G_GNUC_UNUSED gchar * key,LassoProvider * value,xmlNode * xmlnode)311 add_provider_childnode(G_GNUC_UNUSED gchar *key, LassoProvider *value, xmlNode *xmlnode)
312 {
313 	xmlAddChild(xmlnode, lasso_node_get_xmlNode(LASSO_NODE(value), TRUE));
314 }
315 
316 
317 static xmlNode*
get_xmlNode(LassoNode * node,gboolean lasso_dump)318 get_xmlNode(LassoNode *node, gboolean lasso_dump)
319 {
320 	LassoServer *server = LASSO_SERVER(node);
321 	char *signature_methods[] = { NULL,
322 		RSA_SHA1, DSA_SHA1, HMAC_SHA1,
323 		RSA_SHA256, HMAC_SHA256,
324 		RSA_SHA384, HMAC_SHA384,
325 		RSA_SHA512, HMAC_SHA512,
326 	};
327 	xmlNode *xmlnode = NULL, *ret_xmlnode = NULL;
328 
329 	xmlnode = parent_class->get_xmlNode(node, lasso_dump);
330 	xmlSetProp(xmlnode, (xmlChar*)"ServerDumpVersion", (xmlChar*)"2");
331 	if (server->signature_method >= G_N_ELEMENTS(signature_methods))
332 		goto cleanup;
333 	xmlSetProp(xmlnode, (xmlChar*)"SignatureMethod",
334 			(xmlChar*)signature_methods[server->signature_method]);
335 
336 	/* Providers */
337 	if (g_hash_table_size(server->providers)) {
338 		xmlNode *t;
339 		t = xmlNewTextChild(xmlnode, NULL, (xmlChar*)"Providers", NULL);
340 		g_hash_table_foreach(server->providers,
341 				(GHFunc)add_provider_childnode, t);
342 	}
343 
344 #ifdef LASSO_WSF_ENABLED
345 	lasso_server_dump_id_wsf_services(server, xmlnode);
346 	lasso_server_dump_id_wsf20_svcmds(server, xmlnode);
347 #endif
348 
349 	xmlCleanNs(xmlnode);
350 	lasso_transfer_xml_node(ret_xmlnode, xmlnode);
351 
352 cleanup:
353 	lasso_release_xml_node(xmlnode);
354 	return ret_xmlnode;
355 }
356 
357 
358 static int
init_from_xml(LassoNode * node,xmlNode * xmlnode)359 init_from_xml(LassoNode *node, xmlNode *xmlnode)
360 {
361 	LassoServer *server = LASSO_SERVER(node);
362 	xmlNode *t;
363 	xmlChar *s;
364 	int rc = 0;
365 
366 	rc = parent_class->init_from_xml(node, xmlnode);
367 
368 	if (server->private_key) {
369 		lasso_server_set_encryption_private_key_with_password(server, server->private_key,
370 				server->private_key_password);
371 	}
372 	if (rc)
373 		return rc;
374 
375 	s = xmlGetProp(xmlnode, (xmlChar*)"SignatureMethod");
376 	if (lasso_strisequal((char*) s, RSA_SHA1))
377 		server->signature_method = LASSO_SIGNATURE_METHOD_RSA_SHA1;
378 	else if (lasso_strisequal((char*) s, DSA_SHA1))
379 		server->signature_method = LASSO_SIGNATURE_METHOD_DSA_SHA1;
380 	else if (lasso_strisequal((char*) s, HMAC_SHA1))
381 		server->signature_method = LASSO_SIGNATURE_METHOD_HMAC_SHA1;
382 	else if (lasso_strisequal((char*) s, RSA_SHA256))
383 		server->signature_method = LASSO_SIGNATURE_METHOD_RSA_SHA256;
384 	else if (lasso_strisequal((char*) s, HMAC_SHA256))
385 		server->signature_method = LASSO_SIGNATURE_METHOD_HMAC_SHA256;
386 	else if (lasso_strisequal((char*) s, RSA_SHA384))
387 		server->signature_method = LASSO_SIGNATURE_METHOD_RSA_SHA384;
388 	else if (lasso_strisequal((char*) s, HMAC_SHA384))
389 		server->signature_method = LASSO_SIGNATURE_METHOD_HMAC_SHA384;
390 	else if (lasso_strisequal((char*) s, RSA_SHA512))
391 		server->signature_method = LASSO_SIGNATURE_METHOD_RSA_SHA512;
392 	else if (lasso_strisequal((char*) s, HMAC_SHA512))
393 		server->signature_method = LASSO_SIGNATURE_METHOD_HMAC_SHA512;
394 	else {
395 		warning("Unable to rebuild a LassoServer object from XML, bad SignatureMethod: %s",
396 			s);
397 		goto_cleanup_with_rc(LASSO_XML_ERROR_OBJECT_CONSTRUCTION_FAILED);
398 	}
399 
400 	t = xmlSecGetNextElementNode(xmlnode->children);
401 	while (t) {
402 		/* Providers */
403 		if (strcmp((char*)t->name, "Providers") == 0) {
404 			xmlNode *t2 = xmlSecGetNextElementNode(t->children);
405 
406 			while (t2) {
407 				LassoProvider *p;
408 
409 				p = g_object_new(LASSO_TYPE_PROVIDER, NULL);
410 				lasso_check_good_rc(lasso_node_init_from_xml((LassoNode*)p,
411 							t2))
412 				if (lasso_provider_load_public_key(p, LASSO_PUBLIC_KEY_SIGNING)) {
413 					g_hash_table_insert(server->providers,
414 							g_strdup(p->ProviderID), p);
415 				} else {
416 					critical("Failed to load signing public key for %s.",
417 							p->ProviderID);
418 					lasso_release_gobject(p);
419 					goto_cleanup_with_rc(
420 						LASSO_XML_ERROR_OBJECT_CONSTRUCTION_FAILED);
421 				}
422 				t2 = xmlSecGetNextElementNode(t2->next);
423 			}
424 		}
425 
426 #ifdef LASSO_WSF_ENABLED
427 		lasso_server_init_id_wsf_services(server, t);
428 		lasso_server_init_id_wsf20_svcmds(server, t);
429 #endif
430 
431 		t = xmlSecGetNextElementNode(t->next);
432 	}
433 
434 cleanup:
435 	lasso_release_xml_string(s);
436 
437 	return 0;
438 }
439 
440 
441 static gboolean
get_first_providerID(gchar * key,G_GNUC_UNUSED gpointer value,char ** providerID)442 get_first_providerID(gchar *key, G_GNUC_UNUSED gpointer value, char **providerID)
443 {
444 	*providerID = key;
445 	return TRUE;
446 }
447 
448 static gboolean
get_first_providerID_by_role(G_GNUC_UNUSED gchar * key,gpointer value,LassoProviderRole role)449 get_first_providerID_by_role(G_GNUC_UNUSED gchar *key, gpointer value, LassoProviderRole role) {
450 	LassoProvider *provider = (LassoProvider*)value;
451 	if (provider->role == role || role == LASSO_PROVIDER_ROLE_ANY) {
452 		return TRUE;
453 	}
454 	return FALSE;
455 }
456 
457 /**
458  * lasso_server_get_first_providerID_by_role
459  * @server: a #LassoServer
460  * @role: the #LassoProviderRole of the researched provider
461  *
462  * Looks up and returns the provider ID of known provider with the given role.
463  *
464  * Return value: the provider ID, NULL if there are no providers. This string
465  *     must be freed by the caller.
466  */
467 gchar *
lasso_server_get_first_providerID_by_role(const LassoServer * server,LassoProviderRole role)468 lasso_server_get_first_providerID_by_role(const LassoServer *server, LassoProviderRole role)
469 {
470 	LassoProvider *a_provider;
471 	a_provider = LASSO_PROVIDER(g_hash_table_find(server->providers,
472 		(GHRFunc) get_first_providerID_by_role,
473 		(gpointer)role));
474 	if (a_provider) {
475 		return g_strdup(a_provider->ProviderID);
476 	} else {
477 		return NULL;
478 	}
479 }
480 
481 /**
482  * lasso_server_get_first_providerID:
483  * @server: a #LassoServer
484  *
485  * Looks up and returns the provider ID of a known provider
486  *
487  * Return value:(transfer full)(allow-none): the provider ID, NULL if there are no providers.  This
488  * string must be freed by the caller.
489  **/
490 gchar*
lasso_server_get_first_providerID(LassoServer * server)491 lasso_server_get_first_providerID(LassoServer *server)
492 {
493 	gchar *providerID = NULL;
494 
495 	g_hash_table_find(server->providers, (GHRFunc)get_first_providerID, &providerID);
496 	return g_strdup(providerID);
497 }
498 
499 
500 /**
501  * lasso_server_get_provider:
502  * @server: a #LassoServer
503  * @providerID: the provider ID
504  *
505  * Looks up for a #LassoProvider whose ID is @providerID and returns it.
506  *
507  * Return value: (transfer none): the #LassoProvider, NULL if it was not found.  The
508  *     #LassoProvider is owned by Lasso and should not be freed.
509  **/
510 LassoProvider*
lasso_server_get_provider(const LassoServer * server,const gchar * providerID)511 lasso_server_get_provider(const LassoServer *server, const gchar *providerID)
512 {
513 	if (! LASSO_IS_SERVER(server) || providerID == NULL || strlen(providerID) == 0) {
514 		return NULL;
515 	}
516 	return g_hash_table_lookup(server->providers, providerID);
517 }
518 
519 
520 static gboolean
get_providerID_with_hash(gchar * key,G_GNUC_UNUSED gpointer value,char ** providerID)521 get_providerID_with_hash(gchar *key, G_GNUC_UNUSED gpointer value, char **providerID)
522 {
523 	char *hash = *providerID;
524 	xmlChar *hash_providerID;
525 	char *b64_hash_providerID;
526 
527 	hash_providerID = (xmlChar*)lasso_sha1(key);
528 	b64_hash_providerID = (char*)xmlSecBase64Encode(hash_providerID, 20, 0);
529 	xmlFree(hash_providerID);
530 
531 	if (strcmp(b64_hash_providerID, hash) == 0) {
532 		xmlFree(b64_hash_providerID);
533 		*providerID = key;
534 		return TRUE;
535 	}
536 	xmlFree(b64_hash_providerID);
537 
538 	return FALSE;
539 }
540 
541 
542 /**
543  * lasso_server_get_providerID_from_hash:
544  * @server: a #LassoServer
545  * @b64_hash: the base64-encoded provider ID hash
546  *
547  * Looks up a #LassoProvider whose ID hash is @b64_hash and returns its
548  * provider ID.
549  *
550  * Return value:(transfer full)(allow-none): the provider ID, NULL if it was not found.
551  **/
552 gchar*
lasso_server_get_providerID_from_hash(LassoServer * server,gchar * b64_hash)553 lasso_server_get_providerID_from_hash(LassoServer *server, gchar *b64_hash)
554 {
555 	gchar *providerID = b64_hash; /* kludge */
556 
557 	if (g_hash_table_find(server->providers, (GHRFunc)get_providerID_with_hash, &providerID))
558 		return g_strdup(providerID);
559 	return NULL;
560 }
561 
562 typedef struct {
563 	GList *provider_list;
564 	LassoProvider *provider;
565 	LassoProviderRole role;
566 	LassoMdProtocolType protocol_type;
567 	LassoHttpMethod http_method;
568 } FilteredProviderListContext;
569 
570 static void
filter_provider_list(G_GNUC_UNUSED gpointer key,gpointer value,gpointer user_data)571 filter_provider_list(G_GNUC_UNUSED gpointer key, gpointer value, gpointer user_data)
572 {
573 	LassoProvider *remote_provider = (LassoProvider*)value;
574 	FilteredProviderListContext *context = (FilteredProviderListContext*)user_data;
575 
576 	if (remote_provider->role == context->role) {
577 		if (lasso_provider_accept_http_method(context->provider, remote_provider,
578 											  context->protocol_type, context->http_method, FALSE)) {
579 			lasso_list_add_string(context->provider_list, remote_provider->ProviderID);
580 		}
581 	}
582 }
583 
584 
585 /**
586  * lasso_server_get_filtered_provider_list
587  * @server: a #LassoServer
588  * @role: each returned provider will match this #LassoProviderRole
589  * @protocol_type: provider must have endpoint matching #LassoMdProtocolType and @http_method
590  * @http_method: provider must have endpoint matching #LassoHttpMethod and @protocol_type
591  *
592  * Iterate over the @server providers and build a list of provider EntityID's who
593  * have the specified @role and at least one endpoint matching the
594  * @protocol_type and @http_method. Return a #GList list of EntityID's at the
595  * @provider_list pointer. The caller is responsible for freeing the @provider_list
596  * by calling lasso_release_list_of_strings().
597  *
598  * Return value:(transfer full)(element-type string):  #GList of matching provider EntityID's returned here.
599  */
600 GList *
lasso_server_get_filtered_provider_list(const LassoServer * server,LassoProviderRole role,LassoMdProtocolType protocol_type,LassoHttpMethod http_method)601 lasso_server_get_filtered_provider_list(const LassoServer *server, LassoProviderRole role,
602 										LassoMdProtocolType protocol_type,
603 										LassoHttpMethod http_method)
604 {
605 	FilteredProviderListContext context;
606 
607 	context.provider_list = NULL;
608 	context.provider = LASSO_PROVIDER(server);
609 	context.role = role;
610 	context.protocol_type = protocol_type;
611 	context.http_method = http_method;
612 
613 	g_hash_table_foreach(server->providers,
614 						 filter_provider_list, &context);
615 
616 	return context.provider_list;
617 }
618 
619 /*****************************************************************************/
620 /* overridden parent class methods                                           */
621 /*****************************************************************************/
622 
623 static void
dispose(GObject * object)624 dispose(GObject *object)
625 {
626 	LassoServer *server = LASSO_SERVER(object);
627 
628 	if (! server->private_data || server->private_data->dispose_has_run == TRUE) {
629 		return;
630 	}
631 	server->private_data->dispose_has_run = TRUE;
632 
633 	lasso_release_list_of_sec_key(server->private_data->encryption_private_keys);
634 
635 	lasso_release_list_of_gobjects(server->private_data->svc_metadatas);
636 
637 	lasso_release_ghashtable(server->services);
638 
639 	/* free allocated memory for hash tables */
640 	lasso_mem_debug("LassoServer", "Providers", server->providers);
641 	lasso_release_ghashtable(server->providers);
642 
643 	G_OBJECT_CLASS(parent_class)->dispose(G_OBJECT(server));
644 }
645 
646 static void
finalize(GObject * object)647 finalize(GObject *object)
648 {
649 	LassoServer *server = LASSO_SERVER(object);
650 	int i = 0;
651 
652 	lasso_release(server->private_key);
653 	if (server->private_key_password) {
654 		/* don't use memset() because it may be optimised away by
655 		 * compiler (since the string is freed just after */
656 		while (server->private_key_password[i])
657 			server->private_key_password[i++] = 0;
658 		lasso_release(server->private_key_password);
659 	}
660 	lasso_release(server->certificate);
661 	lasso_release(server->private_data);
662 
663 	G_OBJECT_CLASS(parent_class)->finalize(G_OBJECT(server));
664 }
665 
666 /*****************************************************************************/
667 /* instance and class init functions                                         */
668 /*****************************************************************************/
669 
670 static void
instance_init(LassoServer * server)671 instance_init(LassoServer *server)
672 {
673 	server->private_data = g_new0(LassoServerPrivate, 1);
674 	server->private_data->dispose_has_run = FALSE;
675 	server->private_data->encryption_private_keys = NULL;
676 	server->private_data->svc_metadatas = NULL;
677 
678 	server->providers = g_hash_table_new_full(
679 			g_str_hash, g_str_equal, g_free,
680 			g_object_unref);
681 
682 	server->private_key = NULL;
683 	server->private_key_password = NULL;
684 	server->certificate = NULL;
685 	server->signature_method = LASSO_SIGNATURE_METHOD_RSA_SHA1;
686 
687 	server->services = g_hash_table_new_full(g_str_hash, g_str_equal,
688 			(GDestroyNotify)g_free,
689 			g_object_unref);
690 }
691 
692 static void
class_init(LassoServerClass * klass,void * unused G_GNUC_UNUSED)693 class_init(LassoServerClass *klass, void *unused G_GNUC_UNUSED)
694 {
695 	LassoNodeClass *nclass = LASSO_NODE_CLASS(klass);
696 
697 	parent_class = g_type_class_peek_parent(klass);
698 	nclass->node_data = g_new0(LassoNodeClassData, 1);
699 	lasso_node_class_set_nodename(nclass, "Server");
700 	lasso_node_class_set_ns(nclass, LASSO_LASSO_HREF, LASSO_LASSO_PREFIX);
701 	lasso_node_class_add_snippets(nclass, schema_snippets);
702 
703 	nclass->get_xmlNode = get_xmlNode;
704 	nclass->init_from_xml = init_from_xml;
705 
706 	G_OBJECT_CLASS(klass)->dispose = dispose;
707 	G_OBJECT_CLASS(klass)->finalize = finalize;
708 }
709 
710 GType
lasso_server_get_type()711 lasso_server_get_type()
712 {
713 	static GType this_type = 0;
714 
715 	if (!this_type) {
716 		static const GTypeInfo this_info = {
717 			sizeof (LassoServerClass),
718 			NULL,
719 			NULL,
720 			(GClassInitFunc) class_init,
721 			NULL,
722 			NULL,
723 			sizeof(LassoServer),
724 			0,
725 			(GInstanceInitFunc) instance_init,
726 			NULL
727 		};
728 
729 		this_type = g_type_register_static(LASSO_TYPE_PROVIDER,
730 				"LassoServer", &this_info, 0);
731 	}
732 	return this_type;
733 }
734 
735 /**
736  * lasso_server_new:
737  * @metadata: path to the provider metadata file or NULL, for a LECP server
738  * @private_key:(allow-none): path to the the server private key file or NULL
739  * @private_key_password:(allow-none): password to private key if it is encrypted, or NULL
740  * @certificate:(allow-none): path to the server certificate file, or NULL
741  *
742  * Creates a new #LassoServer.
743  *
744  * Return value: a newly created #LassoServer object; or NULL if an error
745  *      occured
746  **/
747 LassoServer*
lasso_server_new(const gchar * metadata,const gchar * private_key,const gchar * private_key_password,const gchar * certificate)748 lasso_server_new(const gchar *metadata,
749 		const gchar *private_key,
750 		const gchar *private_key_password,
751 		const gchar *certificate)
752 {
753 	LassoServer *server;
754 
755 	server = g_object_new(LASSO_TYPE_SERVER, NULL);
756 
757 	/* metadata can be NULL (if server is a LECP) */
758 	if (metadata != NULL) {
759 		if (lasso_provider_load_metadata(LASSO_PROVIDER(server), metadata) == FALSE) {
760 			message(G_LOG_LEVEL_CRITICAL,
761 					"Failed to load metadata from %s.", metadata);
762 			lasso_release_gobject(server);
763 			return NULL;
764 		}
765 	}
766 
767 	lasso_assign_string(server->certificate, certificate);
768 	if (private_key) {
769 		lasso_assign_string(server->private_key, private_key);
770 		lasso_assign_string(server->private_key_password, private_key_password);
771 		if (lasso_server_set_encryption_private_key_with_password(server, private_key,
772 				private_key_password) != 0) {
773 			message(G_LOG_LEVEL_WARNING, "Cannot load the private key");
774 			lasso_release_gobject(server);
775 			return NULL;
776 		}
777 	}
778 	lasso_provider_load_public_key(&server->parent, LASSO_PUBLIC_KEY_SIGNING);
779 	lasso_provider_load_public_key(&server->parent, LASSO_PUBLIC_KEY_ENCRYPTION);
780 
781 	return server;
782 }
783 
784 /**
785  * lasso_server_new_from_buffers:
786  * @metadata: NULL terminated string containing the content of an ID-FF 1.2 metadata file
787  * @private_key_content:(allow-none): NULL terminated string containing a PEM formatted private key
788  * @private_key_password:(allow-none): a NULL terminated string which is the optional password of
789  * the private key
790  * @certificate_content:(allow-none): NULL terminated string containing a PEM formatted X509
791  * certificate
792  *
793  * Creates a new #LassoServer.
794  *
795  * Return value: a newly created #LassoServer object; or NULL if an error occured
796  */
797 LassoServer*
lasso_server_new_from_buffers(const char * metadata,const char * private_key_content,const char * private_key_password,const char * certificate_content)798 lasso_server_new_from_buffers(const char *metadata, const char *private_key_content, const char
799 		*private_key_password, const char *certificate_content)
800 {
801 	LassoServer *server;
802 
803 	server = g_object_new(LASSO_TYPE_SERVER, NULL);
804 	/* metadata can be NULL (if server is a LECP) */
805 	if (metadata != NULL) {
806 		if (lasso_provider_load_metadata_from_buffer(LASSO_PROVIDER(server), metadata) == FALSE) {
807 			message(G_LOG_LEVEL_CRITICAL,
808 					"Failed to load metadata from preloaded buffer");
809 			lasso_release_gobject(server);
810 			return NULL;
811 		}
812 	}
813 	lasso_assign_string(server->certificate, certificate_content);
814 	if (private_key_content) {
815 		lasso_assign_string(server->private_key, private_key_content);
816 		lasso_assign_string(server->private_key_password, private_key_password);
817 
818 		if (lasso_server_set_encryption_private_key_with_password(server, private_key_content,
819 				private_key_password) != 0) {
820 			message(G_LOG_LEVEL_WARNING, "Cannot load the private key");
821 			lasso_release_gobject(server);
822 			return NULL;
823 		}
824 	}
825 	lasso_provider_load_public_key(&server->parent, LASSO_PUBLIC_KEY_SIGNING);
826 	lasso_provider_load_public_key(&server->parent, LASSO_PUBLIC_KEY_ENCRYPTION);
827 
828 	return server;
829 }
830 /**
831  * lasso_server_new_from_dump:
832  * @dump: XML server dump
833  *
834  * Restores the @dump to a new #LassoServer.
835  *
836  * Return value: a newly created #LassoServer; or NULL if an error occured
837  **/
838 LassoServer*
lasso_server_new_from_dump(const gchar * dump)839 lasso_server_new_from_dump(const gchar *dump)
840 {
841 	LassoServer *server;
842 
843 	server = (LassoServer*)lasso_node_new_from_dump(dump);
844 	if (! LASSO_IS_SERVER(server)) {
845 		lasso_release_gobject(server);
846 	}
847 	return server;
848 }
849 
850 /**
851  * lasso_server_dump:
852  * @server: a #LassoServer
853  *
854  * Dumps @server content to an XML string.
855  *
856  * Return value:(transfer full): the dump string.  It must be freed by the caller.
857  **/
858 gchar*
lasso_server_dump(LassoServer * server)859 lasso_server_dump(LassoServer *server)
860 {
861 	return lasso_node_dump(LASSO_NODE(server));
862 }
863 
864 /**
865  * lasso_server_get_private_key:
866  * @server: a #LassoServer object
867  *
868  * Return value:(transfer full): a newly created #xmlSecKey object.
869  */
870 xmlSecKey*
lasso_server_get_private_key(LassoServer * server)871 lasso_server_get_private_key(LassoServer *server)
872 {
873 	if (! LASSO_IS_SERVER(server))
874 		return NULL;
875 
876 	if (! server->private_key)
877 		return NULL;
878 
879 	return lasso_xmlsec_load_private_key(server->private_key, server->private_key_password,
880 			server->signature_method, server->certificate);
881 }
882 
883 /**
884  * lasso_server_get_signature_context_for_provider:
885  * @server: a #LassoServer object
886  * @provider: a #LassoProvider object
887  *
888  * Find the key and signature method to sign messages adressed to @provider. If @provider has an
889  * override over the private key of the @server object, use this override.
890  *
891  * The returned context content is now owned by the caller, if it must survives the @server or
892  * @provider object life, the key should be copied.
893  *
894  * Return value: 0 if successful, an error code otherwise.
895  *
896  */
897 lasso_error_t
lasso_server_get_signature_context_for_provider(LassoServer * server,LassoProvider * provider,LassoSignatureContext * signature_context)898 lasso_server_get_signature_context_for_provider(LassoServer *server,
899 		LassoProvider *provider, LassoSignatureContext *signature_context)
900 {
901 	lasso_error_t rc = 0;
902 	LassoSignatureContext *private_context = NULL;
903 
904 	lasso_bad_param(SERVER, server);
905 	lasso_null_param(signature_context);
906 
907 	if (provider) {
908 		lasso_bad_param(PROVIDER, provider);
909 		private_context = &provider->private_data->signature_context;
910 	}
911 
912 	if (private_context && lasso_validate_signature_method(private_context->signature_method)) {
913 		lasso_assign_signature_context(*signature_context, *private_context);
914 	} else {
915 		rc = lasso_server_get_signature_context(server, signature_context);
916 	}
917 
918 	return rc;
919 
920 }
921 
922 /**
923  * lasso_server_get_signature_context:
924  * @server: a #LassoServer object
925  * @context: a pointer to an allocated and initialized #LassoSignatureContext structure
926  *
927  * Try to create a signature context for this server. Beware that you should better use
928  * lasso_server_get_signature_context_for_provider() or
929  * lasso_server_get_signature_context_for_provider_by_name() in mot of the case when you know the
930  * target for your signature, because the provider could have special signature needs, like using a
931  * shared secret signature.
932  *
933  * Return value: 0 if successful, an error code otherwise.
934  */
935 lasso_error_t
lasso_server_get_signature_context(LassoServer * server,LassoSignatureContext * context)936 lasso_server_get_signature_context(LassoServer *server, LassoSignatureContext *context)
937 {
938 	lasso_bad_param(SERVER, server);
939 	lasso_null_param(context);
940 
941 	lasso_assign_new_signature_context(*context,
942 			lasso_make_signature_context_from_path_or_string(
943 				server->private_key, server->private_key_password,
944 				server->signature_method, server->certificate));
945 	if (! lasso_validate_signature_context(*context)) {
946 		return LASSO_DS_ERROR_PRIVATE_KEY_LOAD_FAILED;
947 	}
948 	return 0;
949 }
950 
951 /**
952  * lasso_server_get_signature_context_for_provider_by_name:
953  * @server: a #LassoServer object
954  * @provider_id: the identifier of a known provider
955  *
956  * Find the key and signature method to sign messages adressed to @provider. If @provider has an
957  * override over the private key of the @server object, use this override.
958  *
959  * The returned context content is now owned by the caller, if it must survives the @server or
960  * provider object life, the key should be copied.
961  *
962  * Return value: 0 if successful, an error code otherwise.
963  *
964  */
965 lasso_error_t
lasso_server_get_signature_context_for_provider_by_name(LassoServer * server,const char * provider_id,LassoSignatureContext * signature_context)966 lasso_server_get_signature_context_for_provider_by_name(LassoServer *server,
967 		const char *provider_id, LassoSignatureContext *signature_context)
968 {
969 	LassoProvider *provider;
970 	lasso_bad_param(SERVER, server);
971 
972 	provider = lasso_server_get_provider(server, provider_id);
973 	return lasso_server_get_signature_context_for_provider(server,
974 			provider, signature_context);
975 }
976 
977 /**
978  * lasso_server_set_signature_for_provider_by_name:
979  * @server: a #LassoServer object
980  * @provider_id: the identifier of a known provider
981  * @node: a #LassoNode object
982  *
983  * Return value: 0 if successful, an error code otherwise.
984  */
985 lasso_error_t
lasso_server_set_signature_for_provider_by_name(LassoServer * server,const char * provider_id,LassoNode * node)986 lasso_server_set_signature_for_provider_by_name(LassoServer *server, const char *provider_id, LassoNode *node)
987 {
988 	LassoSignatureContext context = LASSO_SIGNATURE_CONTEXT_NONE;
989 	lasso_error_t rc = 0;
990 
991 	lasso_check_good_rc(lasso_server_get_signature_context_for_provider_by_name(server,
992 				provider_id, &context));
993 	lasso_node_set_signature(node, context);
994 cleanup:
995 	return rc;
996 }
997 
998 /**
999  * lasso_server_export_to_query_for_provider_by_name:
1000  * @server: a #LassoServer object
1001  * @provider_id: the identifier of a known provider
1002  * @node: a #LassoNode object
1003  *
1004  * Return value: 0 if successful, an error code otherwise.
1005  */
1006 lasso_error_t
lasso_server_export_to_query_for_provider_by_name(LassoServer * server,const char * provider_id,LassoNode * node,char ** out)1007 lasso_server_export_to_query_for_provider_by_name(LassoServer *server, const char *provider_id, LassoNode *node, char **out)
1008 {
1009 	LassoSignatureContext context = LASSO_SIGNATURE_CONTEXT_NONE;
1010 	lasso_error_t rc = 0;
1011 	char *query = NULL;
1012 
1013 	lasso_check_good_rc(lasso_server_get_signature_context_for_provider_by_name(server,
1014 				provider_id, &context));
1015 	query = lasso_node_build_query(node);
1016 	goto_cleanup_if_fail_with_rc(query, LASSO_PROFILE_ERROR_BUILDING_QUERY_FAILED);
1017 	if (lasso_validate_signature_method(context.signature_method)) {
1018 		lasso_assign_new_string(query, lasso_query_sign(query, context));
1019 	}
1020 	goto_cleanup_if_fail_with_rc(query,
1021 			LASSO_PROFILE_ERROR_BUILDING_QUERY_FAILED);
1022 	lasso_assign_new_string(*out, query);
1023 cleanup:
1024 	lasso_assign_new_signature_context(context, LASSO_SIGNATURE_CONTEXT_NONE);
1025 	return rc;
1026 }
1027 
1028 /**
1029  * lasso_server_get_encryption_private_keys:
1030  * @server: a #LassoServer object
1031  *
1032  * Return:(transfer none)(element-type xmlSecKeyPtr): a GList of xmlSecKey object, it is owned by the #LassoServer object, so do not
1033  * free it.
1034  */
1035 GList*
lasso_server_get_encryption_private_keys(LassoServer * server)1036 lasso_server_get_encryption_private_keys(LassoServer *server)
1037 {
1038 	if (! LASSO_IS_SERVER(server))
1039 		return NULL;
1040 
1041 	if (! server->private_data)
1042 		return NULL;
1043 
1044 	return server->private_data->encryption_private_keys;
1045 }
1046 
1047 /**
1048  * lasso_server_load_metadata:
1049  * @server: a #LassoServer object
1050  * @role: a #LassoProviderRole value
1051  * @federation_file: path to a SAML 2.0 metadata file
1052  * @trusted_roots:(allow-none): a PEM encoded files containing the certificates to check signatures
1053  * on the metadata file (optional)
1054  * @blacklisted_entity_ids:(allow-none)(element-type string): a list of EntityID which should not be
1055  * loaded, can be NULL.
1056  * @loaded_entity_ids:(transfer full)(element-type string)(allow-none): an output parameter for the
1057  * list of the loaded EntityID, can be NULL.
1058  * @flags: flags modifying the behaviour for checking signatures on EntityDescriptor and
1059  * EntitiesDescriptors nodes.
1060  *
1061  * Load all the SAML 2.0 entities from @federation_file which contains a declaration for @role. If
1062  * @trusted_roots is non-NULL, use it to check a signature on the metadata file, otherwise ignore
1063  * signature validation.
1064  *
1065  * Return value: 0 on success, an error code otherwise, among:
1066  * <itemizedlist>
1067  * <listitem><para>
1068  * LASSO_PARAM_ERROR_BAD_TYPE_OR_NULL_OBJ if server is not a #LassoServer object or @role is not a
1069  * valid role value,
1070  * </para></listitem>
1071  * <listitem><para>
1072  * LASSO_DS_ERROR_CA_CERT_CHAIN_LOAD_FAILED if the @trusted_root file cannot be loaded,
1073  * </para></listitem>
1074  * </itemizedlist>
1075  */
1076 lasso_error_t
lasso_server_load_metadata(LassoServer * server,LassoProviderRole role,const gchar * federation_file,const gchar * trusted_roots,GList * blacklisted_entity_ids,GList ** loaded_entity_ids,LassoServerLoadMetadataFlag flags)1077 lasso_server_load_metadata(LassoServer *server, LassoProviderRole role, const gchar *federation_file,
1078 		const gchar *trusted_roots, GList *blacklisted_entity_ids,
1079 		GList **loaded_entity_ids, LassoServerLoadMetadataFlag flags)
1080 {
1081 	xmlDoc *doc = NULL;
1082 	xmlNode *root = NULL;
1083 	xmlSecKeysMngr *keys_mngr = NULL;
1084 	lasso_error_t rc = 0;
1085 
1086 	lasso_bad_param(SERVER, server);
1087 	g_return_val_if_fail(role == LASSO_PROVIDER_ROLE_SP || role == LASSO_PROVIDER_ROLE_IDP,
1088 			LASSO_PARAM_ERROR_BAD_TYPE_OR_NULL_OBJ);
1089 
1090 	if (flags == LASSO_SERVER_LOAD_METADATA_FLAG_DEFAULT) {
1091 		flags = LASSO_SERVER_LOAD_METADATA_FLAG_CHECK_ENTITIES_DESCRIPTOR_SIGNATURE
1092 			| LASSO_SERVER_LOAD_METADATA_FLAG_CHECK_ENTITY_DESCRIPTOR_SIGNATURE
1093 			| LASSO_SERVER_LOAD_METADATA_FLAG_INHERIT_SIGNATURE;
1094 	}
1095 
1096 	if (trusted_roots) {
1097 		keys_mngr = lasso_load_certs_from_pem_certs_chain_file(trusted_roots);
1098 		lasso_return_val_if_fail(keys_mngr != NULL,
1099 				LASSO_DS_ERROR_CA_CERT_CHAIN_LOAD_FAILED);
1100 	}
1101 	doc = lasso_xml_parse_file(federation_file);
1102 	goto_cleanup_if_fail_with_rc(doc, LASSO_SERVER_ERROR_INVALID_XML);
1103 	root = xmlDocGetRootElement(doc);
1104 	if (lasso_strisequal((char*)root->ns->href, LASSO_SAML2_METADATA_HREF)) {
1105 		lasso_check_good_rc(lasso_saml20_server_load_metadata(server, role, doc, root,
1106 					blacklisted_entity_ids, loaded_entity_ids, keys_mngr, flags));
1107 	} else {
1108 		goto_cleanup_with_rc(LASSO_ERROR_UNIMPLEMENTED);
1109 	}
1110 
1111 cleanup:
1112 	lasso_release_key_manager(keys_mngr);
1113 	lasso_release_doc(doc);
1114 	return rc;
1115 }
1116