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