1 /*
2  * Lasso - A free implementation of the Liberty Alliance specifications.
3  *
4  * Copyright (C) 2004-2011 Entr'ouvert
5  * http://lasso.entrouvert.org
6  *
7  * Authors: See AUTHORS file in top-level directory.
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, see <http://www.gnu.org/licenses/>.
21  */
22 
23 #include "key.h"
24 #include "keyprivate.h"
25 #include "xml/private.h"
26 #include "xmlsec/xmltree.h"
27 
28 /*****************************************************************************/
29 /* private methods                                                           */
30 /*****************************************************************************/
31 
32 struct _LassoKeyPrivate {
33 	enum _LassoKeyType type;
34 	union {
35 		LassoSignatureContext signature;
36 	} context;
37 };
38 
39 #define LASSO_KEY_GET_PRIVATE(o) \
40 	   (G_TYPE_INSTANCE_GET_PRIVATE ((o), LASSO_TYPE_KEY, LassoKeyPrivate))
41 
42 static struct XmlSnippet schema_snippets[] = {
43 	{NULL, 0, 0, NULL, NULL, NULL}
44 };
45 
46 static LassoNodeClass *parent_class = NULL;
47 
48 
49 /*****************************************************************************/
50 /* instance and class init functions                                         */
51 /*****************************************************************************/
52 
53 static void
instance_init(LassoKey * key)54 instance_init(LassoKey *key)
55 {
56 	key->private_data = LASSO_KEY_GET_PRIVATE(key);
57 }
58 
59 static void
dispose(GObject * g_object)60 dispose(GObject *g_object)
61 {
62 	LassoKey *key = (LassoKey*)g_object;
63 
64 	if (key->private_data) {
65 		switch (key->private_data->type) {
66 			case LASSO_KEY_TYPE_FOR_SIGNATURE:
67 				lasso_assign_new_signature_context(
68 						key->private_data->context.signature,
69 						LASSO_SIGNATURE_CONTEXT_NONE);
70 				break;
71 		}
72 	}
73 
74 	G_OBJECT_CLASS(parent_class)->dispose(G_OBJECT(key));
75 }
76 
77 static void
class_init(LassoKeyClass * klass,void * unused G_GNUC_UNUSED)78 class_init(LassoKeyClass *klass, void *unused G_GNUC_UNUSED)
79 {
80 	LassoNodeClass *nclass = LASSO_NODE_CLASS(klass);
81 
82 	parent_class = g_type_class_peek_parent(klass);
83 	nclass->node_data = g_new0(LassoNodeClassData, 1);
84 	lasso_node_class_set_nodename(nclass, "Key");
85 	lasso_node_class_set_ns(nclass, LASSO_LASSO_HREF, LASSO_LASSO_PREFIX);
86 	lasso_node_class_add_snippets(nclass, schema_snippets);
87 	g_type_class_add_private(klass, sizeof(LassoKeyPrivate));
88 	G_OBJECT_CLASS(klass)->dispose = dispose;
89 }
90 
91 GType
lasso_key_get_type()92 lasso_key_get_type()
93 {
94 	static GType this_type = 0;
95 
96 	if (!this_type) {
97 		static const GTypeInfo this_info = {
98 			sizeof (LassoKeyClass),
99 			NULL,
100 			NULL,
101 			(GClassInitFunc) class_init,
102 			NULL,
103 			NULL,
104 			sizeof(LassoKey),
105 			0,
106 			(GInstanceInitFunc) instance_init,
107 			NULL
108 		};
109 
110 		this_type = g_type_register_static(LASSO_TYPE_NODE,
111 				"LassoKey", &this_info, 0);
112 	}
113 	return this_type;
114 }
115 
116 static LassoKey*
lasso_key_new()117 lasso_key_new()
118 {
119 	return g_object_new(LASSO_TYPE_KEY, NULL);
120 }
121 
122 static LassoKey*
lasso_key_new_for_signature_from_context(LassoSignatureContext context)123 lasso_key_new_for_signature_from_context(LassoSignatureContext context) {
124 	LassoKey *key = lasso_key_new();
125 
126 	key->private_data->type = LASSO_KEY_TYPE_FOR_SIGNATURE;
127 	lasso_assign_new_signature_context(
128 			key->private_data->context.signature, context);
129 	if (! lasso_validate_signature_context(key->private_data->context.signature)) {
130 		lasso_release_gobject(key);
131 	}
132 	return key;
133 }
134 
135 /**
136  * lasso_key_new_for_signature_from_file:
137  * @filename_or_buffer: a file path of a string containing the key PEM or Base64 encoded
138  * @password: an eventual password to decoded the private key contained in @buffer
139  * @signature_method: the signature method to associate to this key
140  * @certificate: a certificate as a file path or PEM encoded in a NULL-terminated string, to
141  * associate with the key, it will be used to fill the KeyInfo node in an eventual signature.
142  *
143  * Create a new #LassoKey object, you can use it to sign XML message or to specify the key of a
144  * provider.
145  *
146  * Return value:(transfer full): a newly allocated #LassoKey object
147  */
148 LassoKey*
lasso_key_new_for_signature_from_file(char * filename_or_buffer,char * password,LassoSignatureMethod signature_method,char * certificate)149 lasso_key_new_for_signature_from_file(char *filename_or_buffer,
150 		char *password,
151 		LassoSignatureMethod signature_method,
152 		char *certificate) {
153 	return lasso_key_new_for_signature_from_context(
154 			lasso_make_signature_context_from_path_or_string(filename_or_buffer,
155 				password,
156 				signature_method,
157 				certificate));
158 }
159 
160 /**
161  * lasso_key_new_for_signature_from_memory:
162  * @buffer: a byte buffer of size @size
163  * @size: the size of @buffer
164  * @password: an eventual password to decoded the private key contained in @buffer
165  * @signature_method: the signature method to associate to this key
166  * @certificate: a certificate as a file path or PEM encoded in a NULL-terminated string, to
167  * associate with the key, it will be used to fill the KeyInfo node in an eventual signature.
168  *
169  * Create a new #LassoKey object, you can use it to sign XML message or to specify the key of a
170  * provider.
171  *
172  * Return value:(transfer full): a newly allocated #LassoKey object
173  */
174 LassoKey*
lasso_key_new_for_signature_from_memory(const void * buffer,size_t size,char * password,LassoSignatureMethod signature_method,char * certificate)175 lasso_key_new_for_signature_from_memory(const void *buffer,
176 		size_t size,
177 		char *password,
178 		LassoSignatureMethod signature_method,
179 		char *certificate)
180 {
181 	return lasso_key_new_for_signature_from_context(
182 			lasso_make_signature_context_from_buffer(buffer,
183 				size,
184 				password,
185 				signature_method,
186 				certificate));
187 }
188 
189 /**
190  * lasso_key_new_for_signature_from_base64_string:
191  * @base64_string: a NULL-terminated string containing a base64 encode representation of the key
192  * @password: an eventual password to decoded the private key contained in @buffer
193  * @signature_method: the signature method to associate to this key
194  * @certificate: a certificate as a file path or PEM encoded in a NULL-terminated string, to
195  * associate with the key, it will be used to fill the KeyInfo node in an eventual signature.
196  *
197  * Create a new #LassoKey object, you can use it to sign XML message or to specify the key of a
198  * provider.
199  *
200  * Return value:(transfer full): a newly allocated #LassoKey object
201  */
202 LassoKey*
lasso_key_new_for_signature_from_base64_string(char * base64_string,char * password,LassoSignatureMethod signature_method,char * certificate)203 lasso_key_new_for_signature_from_base64_string(char *base64_string,
204 		char *password,
205 		LassoSignatureMethod signature_method,
206 		char *certificate)
207 {
208 	LassoKey *key = NULL;
209 	char *buffer = NULL;
210 	int length = 0;
211 
212 	if (lasso_base64_decode(base64_string, &buffer, &length)) {
213 		key = lasso_key_new_for_signature_from_context(
214 				lasso_make_signature_context_from_buffer(buffer,
215 					length,
216 					password,
217 					signature_method,
218 					certificate));
219 		lasso_release_string(buffer);
220 	}
221 	return key;
222 }
223 
224 static xmlNode *
find_xmlnode_with_saml2_id(xmlNode * xmlnode,const char * id)225 find_xmlnode_with_saml2_id(xmlNode *xmlnode, const char *id)
226 {
227 	xmlNode *found = NULL;
228 	xmlNode *t;
229 
230 	if (! xmlnode)
231 		return NULL;
232 
233 	if (xmlHasProp(xmlnode, BAD_CAST "ID")) {
234 		xmlChar *value;
235 
236 		value = xmlGetProp(xmlnode, BAD_CAST "ID");
237 		if (lasso_strisequal((char*)value, id)) {
238 			found = xmlnode;
239 		}
240 		xmlFree(value);
241 	}
242 	if (found) {
243 		return found;
244 	}
245 	t = xmlSecGetNextElementNode(xmlnode->children);
246 	while (t) {
247 		found = find_xmlnode_with_saml2_id(t, id);
248 		if (found) {
249 			return found;
250 		}
251 		t = xmlSecGetNextElementNode(t->next);
252 	}
253 	return NULL;
254 }
255 
256 /**
257  * lasso_key_saml2_xml_verify:
258  * @key: a #LassoKey object
259  * @id: the value of the ID attribute of signed node
260  * @document: the document containing the signed node
261  *
262  * Verify the first signature node child of the node with the given id. It follows from the profile
263  * of XMLDsig used by the SAML 2.0 specification.
264  *
265  * Return value: 0 if the signature validate, an error code otherwise.
266  */
267 lasso_error_t
lasso_key_saml2_xml_verify(LassoKey * key,char * id,xmlNode * document)268 lasso_key_saml2_xml_verify(LassoKey *key, char *id, xmlNode *document)
269 {
270 	xmlNode *signed_node;
271 	LassoSignatureContext signature_context;
272 
273 
274 	signed_node = find_xmlnode_with_saml2_id(document, id);
275 	if (! signed_node) {
276 		return LASSO_DS_ERROR_INVALID_REFERENCE_FOR_SAML;
277 	}
278 	signature_context = lasso_key_get_signature_context(key);
279 	return lasso_verify_signature(signed_node, signed_node->doc, "ID", NULL,
280 			signature_context.signature_key, NO_OPTION, NULL);
281 }
282 
283 /**
284  * lasso_key_saml2_xml_sign:
285  * @key: a #LassoKey object
286  * @id: the value of the ID attribute of signed node
287  * @document: the document containing the signed node
288  *
289  * Sign the first signature node child of the node with the given id. It no signature node is found
290  * a new one is added at the end of the children list of the signed node.
291  *
292  * The passed document node is modified in-place.
293  *
294  * Return value: The modified xmlNode object, or NULL if the signature failed.
295  */
296 xmlNode*
lasso_key_saml2_xml_sign(LassoKey * key,const char * id,xmlNode * document)297 lasso_key_saml2_xml_sign(LassoKey *key, const char *id, xmlNode *document)
298 {
299 	xmlNode *signed_node;
300 	LassoSignatureContext signature_context;
301 
302 	signed_node = find_xmlnode_with_saml2_id(document, id);
303 	if (! signed_node) {
304 		return NULL;
305 	}
306 	signature_context = lasso_key_get_signature_context(key);
307 	lasso_xmlnode_add_saml2_signature_template(signed_node, signature_context, id);
308 	if (lasso_sign_node(signed_node, signature_context,
309 			"ID", id) == 0) {
310 		return document;
311 	} else {
312 		return NULL;
313 	}
314 }
315 
316 /**
317  * lasso_key_query_verify:
318  * key: a #LassoKey object
319  * query: a raw HTTP query string
320  *
321  * Check if this query string contains a proper SAML2 signature for this key.
322  *
323  * Return value: 0 if a valid signature was found, an error code otherwise.
324  */
325 lasso_error_t
lasso_key_query_verify(LassoKey * key,const char * query)326 lasso_key_query_verify(LassoKey *key, const char *query)
327 {
328 	LassoSignatureContext signature_context;
329 	lasso_bad_param(KEY, key);
330 
331 	signature_context = lasso_key_get_signature_context(key);
332 	if (! lasso_validate_signature_context(signature_context))
333 		return LASSO_ERROR_UNDEFINED;
334 	return lasso_saml2_query_verify_signature(query, signature_context.signature_key);
335 }
336 
337 /**
338  * lasso_key_query_verify:
339  * key: a #LassoKey object
340  * query: a raw HTTP query string
341  *
342  * Sign the given query string using the given key.
343  *
344  * Return value: the signed query string.
345  */
346 char*
lasso_key_query_sign(LassoKey * key,const char * query)347 lasso_key_query_sign(LassoKey *key, const char *query)
348 {
349 	LassoSignatureContext signature_context;
350 
351 	if (! LASSO_IS_KEY(key))
352 		return NULL;
353 	signature_context = lasso_key_get_signature_context(key);
354 	if (! lasso_validate_signature_context(signature_context))
355 		return NULL;
356 	return lasso_query_sign((char*)query, signature_context);
357 }
358 
359 /**
360  * lasso_key_get_signature_context:
361  * @key: a #LassoKey object
362  *
363  * Private method to extract the signature context embedded in a LassoKey object.
364  *
365  * Return value: a #LassoSignatureContext structure value.
366  */
367 LassoSignatureContext
lasso_key_get_signature_context(LassoKey * key)368 lasso_key_get_signature_context(LassoKey *key) {
369 	if (key->private_data && key->private_data->type == LASSO_KEY_TYPE_FOR_SIGNATURE) {
370 		return key->private_data->context.signature;
371 	}
372 	return LASSO_SIGNATURE_CONTEXT_NONE;
373 }
374 
375 /**
376  * lasso_key_get_key_type:
377  * @key: a #LassoKey object
378  *
379  * Return the type of key, i.e. which operation it supports.
380  */
381 LassoKeyType
lasso_key_get_key_type(LassoKey * key)382 lasso_key_get_key_type(LassoKey *key) {
383 	lasso_return_val_if_fail(LASSO_IS_KEY(key),
384 			LASSO_KEY_TYPE_FOR_SIGNATURE);
385 	return key->private_data->type;
386 }
387