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