1
by Frederic Peters
Import upstream version 0.4.1 |
1 |
/* $Id: tools.c,v 1.34 2004/09/01 09:59:53 fpeters Exp $
|
2 |
*
|
|
3 |
* Lasso - A free implementation of the Liberty Alliance specifications.
|
|
4 |
*
|
|
5 |
* Copyright (C) 2004 Entr'ouvert
|
|
6 |
* http://lasso.entrouvert.org
|
|
7 |
*
|
|
8 |
* Authors: Nicolas Clapies <nclapies@entrouvert.com>
|
|
9 |
* Valery Febvre <vfebvre@easter-eggs.com>
|
|
10 |
*
|
|
11 |
* This program is free software; you can redistribute it and/or modify
|
|
12 |
* it under the terms of the GNU General Public License as published by
|
|
13 |
* the Free Software Foundation; either version 2 of the License, or
|
|
14 |
* (at your option) any later version.
|
|
15 |
*
|
|
16 |
* This program is distributed in the hope that it will be useful,
|
|
17 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
18 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
19 |
* GNU General Public License for more details.
|
|
20 |
*
|
|
21 |
* You should have received a copy of the GNU General Public License
|
|
22 |
* along with this program; if not, write to the Free Software
|
|
23 |
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
24 |
*/
|
|
25 |
||
26 |
#include <string.h> |
|
27 |
||
28 |
#include <libxml/uri.h> |
|
29 |
||
30 |
#include <openssl/sha.h> |
|
31 |
||
32 |
#include <xmlsec/xmltree.h> |
|
33 |
#include <xmlsec/base64.h> |
|
34 |
#include <xmlsec/xmldsig.h> |
|
35 |
#include <xmlsec/templates.h> |
|
36 |
#include <xmlsec/crypto.h> |
|
37 |
||
38 |
#include <lasso/xml/tools.h> |
|
39 |
||
40 |
xmlChar * |
|
41 |
lasso_build_random_sequence(guint8 size) |
|
42 |
{
|
|
43 |
int i, val; |
|
44 |
xmlChar *seq; |
|
45 |
||
46 |
g_return_val_if_fail(size > 0, NULL); |
|
47 |
||
48 |
seq = xmlMalloc(size+1); |
|
49 |
||
50 |
for (i=0; i<size; i++) { |
|
51 |
val = g_random_int_range(0, 16); |
|
52 |
if (val < 10) |
|
53 |
seq[i] = 48 + val; |
|
54 |
else
|
|
55 |
seq[i] = 65 + val-10; |
|
56 |
}
|
|
57 |
seq[size] = '\0'; |
|
58 |
||
59 |
return seq; |
|
60 |
}
|
|
61 |
||
62 |
/**
|
|
63 |
* lasso_build_unique_id:
|
|
64 |
* @size: the ID's length (between 32 and 40)
|
|
65 |
*
|
|
66 |
* Builds an ID which has an unicity probability of 2^(-size*4).
|
|
67 |
*
|
|
68 |
* Return value: a "unique" ID (begin always with _ character)
|
|
69 |
**/
|
|
70 |
xmlChar * |
|
71 |
lasso_build_unique_id(guint8 size) |
|
72 |
{
|
|
73 |
/*
|
|
74 |
The probability of 2 randomly chosen identifiers being identical MUST be
|
|
75 |
less than 2^-128 and SHOULD be less than 2^-160.
|
|
76 |
so we must have 128 <= exp <= 160
|
|
77 |
we could build a 128-bit binary number but hexa system is shorter
|
|
78 |
32 <= hexa number size <= 40
|
|
79 |
*/
|
|
80 |
int i, val; |
|
81 |
xmlChar *id; /* , *enc_id; */ |
|
82 |
||
83 |
g_return_val_if_fail((size >= 32 && size <= 40) || size == 0, NULL); |
|
84 |
||
85 |
if (size == 0) size = 32; |
|
86 |
id = g_malloc(size+1+1); /* one for _ and one for \0 */ |
|
87 |
||
88 |
/* build hex number (<= 2^exp-1) */
|
|
89 |
id[0] = '_'; |
|
90 |
for (i=1; i<size+1; i++) { |
|
91 |
val = g_random_int_range(0, 16); |
|
92 |
if (val < 10) |
|
93 |
id[i] = 48 + val; |
|
94 |
else
|
|
95 |
id[i] = 65 + val-10; |
|
96 |
}
|
|
97 |
id[size+1] = '\0'; |
|
98 |
||
99 |
/* base64 encoding of build string */
|
|
100 |
/* enc_id = xmlSecBase64Encode((const xmlChar *)id, size+1, 0); */
|
|
101 |
||
102 |
/* g_free(id); */
|
|
103 |
/* return (enc_id); */
|
|
104 |
return id; |
|
105 |
}
|
|
106 |
||
107 |
/**
|
|
108 |
* lasso_doc_get_node_content:
|
|
109 |
* @doc: a doc
|
|
110 |
* @name: the name
|
|
111 |
*
|
|
112 |
* Gets the value of the first node having given @name.
|
|
113 |
*
|
|
114 |
* Return value: a node value or NULL if no node found or if no content is
|
|
115 |
* available
|
|
116 |
**/
|
|
117 |
xmlChar * |
|
118 |
lasso_doc_get_node_content(xmlDocPtr doc, const xmlChar *name) |
|
119 |
{
|
|
120 |
xmlNodePtr node; |
|
121 |
||
122 |
/* FIXME: bad namespace used */
|
|
123 |
node = xmlSecFindNode(xmlDocGetRootElement(doc), name, xmlSecDSigNs); |
|
124 |
if (node != NULL) |
|
125 |
/* val returned must be xmlFree() */
|
|
126 |
return xmlNodeGetContent(node); |
|
127 |
else
|
|
128 |
return NULL; |
|
129 |
}
|
|
130 |
||
131 |
/**
|
|
132 |
* lasso_g_ptr_array_index:
|
|
133 |
* @a: a GPtrArray
|
|
134 |
* @i: the index
|
|
135 |
*
|
|
136 |
* Gets the pointer at the given index @i of the pointer array.
|
|
137 |
*
|
|
138 |
* Return value: the pointer at the given index.
|
|
139 |
**/
|
|
140 |
xmlChar* |
|
141 |
lasso_g_ptr_array_index(GPtrArray *a, guint i) |
|
142 |
{
|
|
143 |
if (a != NULL) { |
|
144 |
return g_ptr_array_index(a, i); |
|
145 |
}
|
|
146 |
else { |
|
147 |
return NULL; |
|
148 |
}
|
|
149 |
}
|
|
150 |
||
151 |
/**
|
|
152 |
* lasso_get_current_time:
|
|
153 |
*
|
|
154 |
* Returns the current time, format is "yyyy-mm-ddThh:mm:ssZ".
|
|
155 |
*
|
|
156 |
* Return value: a string
|
|
157 |
**/
|
|
158 |
gchar * |
|
159 |
lasso_get_current_time() |
|
160 |
{
|
|
161 |
struct tm *tm; |
|
162 |
GTimeVal time_val; |
|
163 |
gchar *ret = g_new0(gchar, 21); |
|
164 |
||
165 |
g_get_current_time(&time_val); |
|
166 |
tm = localtime(&(time_val.tv_sec)); |
|
167 |
strftime((char *)ret, 21, "%Y-%m-%dT%H:%M:%SZ", tm); |
|
168 |
||
169 |
return ret; |
|
170 |
}
|
|
171 |
||
172 |
/**
|
|
173 |
* lasso_query_get_value:
|
|
174 |
* @query: a query (an url-encoded node)
|
|
175 |
* @param: the parameter
|
|
176 |
*
|
|
177 |
* Returns the value of the given @param
|
|
178 |
*
|
|
179 |
* Return value: a string or NULL if no parameter found
|
|
180 |
**/
|
|
181 |
GPtrArray * |
|
182 |
lasso_query_get_value(const gchar *query, |
|
183 |
const xmlChar *param) |
|
184 |
{
|
|
185 |
guint i; |
|
186 |
GData *gd; |
|
187 |
GPtrArray *tmp_array, *array = NULL; |
|
188 |
||
189 |
gd = lasso_query_to_dict(query); |
|
190 |
tmp_array = (GPtrArray *)g_datalist_get_data(&gd, (gchar *)param); |
|
191 |
/* create a copy of tmp_array */
|
|
192 |
if (tmp_array != NULL) { |
|
193 |
array = g_ptr_array_new(); |
|
194 |
for(i=0; i<tmp_array->len; i++) |
|
195 |
g_ptr_array_add(array, g_strdup(g_ptr_array_index(tmp_array, i))); |
|
196 |
}
|
|
197 |
g_datalist_clear(&gd); |
|
198 |
return array; |
|
199 |
}
|
|
200 |
||
201 |
static void |
|
202 |
gdata_query_to_dict_destroy_notify(gpointer data) |
|
203 |
{
|
|
204 |
guint i; |
|
205 |
GPtrArray *array = data; |
|
206 |
||
207 |
for (i=0; i<array->len; i++) { |
|
208 |
g_free(array->pdata[i]); |
|
209 |
}
|
|
210 |
g_ptr_array_free(array, TRUE); |
|
211 |
}
|
|
212 |
||
213 |
/**
|
|
214 |
* lasso_query_to_dict:
|
|
215 |
* @query: the query (an url-encoded node)
|
|
216 |
*
|
|
217 |
* Explodes query to build a dictonary.
|
|
218 |
* Dictionary values are stored in GPtrArray.
|
|
219 |
* The caller is responsible for freeing returned object by calling
|
|
220 |
* g_datalist_clear() function.
|
|
221 |
*
|
|
222 |
* Return value: a dictonary
|
|
223 |
**/
|
|
224 |
GData * |
|
225 |
lasso_query_to_dict(const gchar *query) |
|
226 |
{
|
|
227 |
GData *gd = NULL; |
|
228 |
gchar **sa1, **sa2, **sa3; |
|
229 |
xmlChar *str_unescaped; |
|
230 |
GPtrArray *gpa; |
|
231 |
guint i, j; |
|
232 |
||
233 |
g_datalist_init(&gd); |
|
234 |
||
235 |
i = 0; |
|
236 |
sa1 = g_strsplit(query, "&", 0); |
|
237 |
||
238 |
while (sa1[i++] != NULL) { |
|
239 |
/* split of key=value to get (key, value) sub-strings */
|
|
240 |
sa2 = g_strsplit(sa1[i-1], "=", 0); |
|
241 |
/* if no key / value found, then continue */
|
|
242 |
if (sa2 == NULL) { |
|
243 |
continue; |
|
244 |
}
|
|
245 |
/* if only a key but no value, then continue */
|
|
246 |
if (sa2[1] == NULL) { |
|
247 |
continue; |
|
248 |
}
|
|
249 |
||
250 |
/* printf("%s => ", sa2[0]); */
|
|
251 |
/* split of value to get mutli values sub-strings separated by SPACE char */
|
|
252 |
str_unescaped = lasso_str_unescape(sa2[1]); |
|
253 |
sa3 = g_strsplit(str_unescaped, " ", 0); |
|
254 |
if (sa3 == NULL) { |
|
255 |
g_strfreev(sa2); |
|
256 |
continue; |
|
257 |
}
|
|
258 |
||
259 |
xmlFree(str_unescaped); |
|
260 |
gpa = g_ptr_array_new(); |
|
261 |
j = 0; |
|
262 |
while (sa3[j++] != NULL) { |
|
263 |
g_ptr_array_add(gpa, g_strdup(sa3[j-1])); |
|
264 |
/* printf("%s, ", sa3[j-1]); */
|
|
265 |
}
|
|
266 |
/* printf("\n"); */
|
|
267 |
/* add key => values in dict */
|
|
268 |
g_datalist_set_data_full(&gd, sa2[0], gpa, |
|
269 |
gdata_query_to_dict_destroy_notify); |
|
270 |
g_strfreev(sa3); |
|
271 |
g_strfreev(sa2); |
|
272 |
}
|
|
273 |
g_strfreev(sa1); |
|
274 |
||
275 |
return gd; |
|
276 |
}
|
|
277 |
||
278 |
/**
|
|
279 |
* lasso_query_verify_signature:
|
|
280 |
* @query: a query (an url-encoded and signed node)
|
|
281 |
* @sender_public_key_file: the sender public key
|
|
282 |
* @recipient_private_key_file: the recipient private key
|
|
283 |
*
|
|
284 |
* Verifys the query's signature.
|
|
285 |
*
|
|
286 |
* Return value: 1 if signature is valid, 0 if invalid, 2 if no signature found
|
|
287 |
* and -1 if an error occurs.
|
|
288 |
**/
|
|
289 |
int
|
|
290 |
lasso_query_verify_signature(const gchar *query, |
|
291 |
const xmlChar *sender_public_key_file, |
|
292 |
const xmlChar *recipient_private_key_file) |
|
293 |
{
|
|
294 |
xmlDocPtr doc; |
|
295 |
xmlNodePtr sigNode, sigValNode; |
|
296 |
xmlSecDSigCtxPtr dsigCtx; |
|
297 |
xmlChar *str_unescaped; |
|
298 |
gchar **str_split; |
|
299 |
/*
|
|
300 |
0: signature invalid
|
|
301 |
1: signature ok
|
|
302 |
2: signature not found
|
|
303 |
-1: error during verification
|
|
304 |
*/
|
|
305 |
gint ret = -1; |
|
306 |
||
307 |
/* split query, signature (must be last param) */
|
|
308 |
str_split = g_strsplit(query, "&Signature=", 0); |
|
309 |
if (str_split[1] == NULL) |
|
310 |
return 2; |
|
311 |
/* re-create doc to verify (signed + enrypted) */
|
|
312 |
doc = lasso_str_sign(str_split[0], |
|
313 |
lassoSignatureMethodRsaSha1, |
|
314 |
recipient_private_key_file); |
|
315 |
sigValNode = xmlSecFindNode(xmlDocGetRootElement(doc), |
|
316 |
xmlSecNodeSignatureValue, |
|
317 |
xmlSecDSigNs); |
|
318 |
/* set SignatureValue content */
|
|
319 |
str_unescaped = lasso_str_unescape(str_split[1]); |
|
320 |
xmlNodeSetContent(sigValNode, str_unescaped); |
|
321 |
g_free(str_unescaped); |
|
322 |
||
323 |
g_strfreev(str_split); |
|
324 |
/*xmlDocDump(stdout, doc);*/
|
|
325 |
||
326 |
/* find start node */
|
|
327 |
sigNode = xmlSecFindNode(xmlDocGetRootElement(doc), |
|
328 |
xmlSecNodeSignature, xmlSecDSigNs); |
|
329 |
||
330 |
/* create signature context */
|
|
331 |
dsigCtx = xmlSecDSigCtxCreate(NULL); |
|
332 |
if(dsigCtx == NULL) { |
|
333 |
message(G_LOG_LEVEL_CRITICAL, "Failed to create signature context\n"); |
|
334 |
goto done; |
|
335 |
}
|
|
336 |
||
337 |
/* load public key */
|
|
338 |
dsigCtx->signKey = xmlSecCryptoAppKeyLoad(sender_public_key_file, |
|
339 |
xmlSecKeyDataFormatPem, |
|
340 |
NULL, NULL, NULL); |
|
341 |
if(dsigCtx->signKey == NULL) { |
|
342 |
message(G_LOG_LEVEL_CRITICAL, "Failed to load public pem key from \"%s\"\n", |
|
343 |
sender_public_key_file); |
|
344 |
goto done; |
|
345 |
}
|
|
346 |
||
347 |
/* Verify signature */
|
|
348 |
if(xmlSecDSigCtxVerify(dsigCtx, sigNode) < 0) { |
|
349 |
message(G_LOG_LEVEL_CRITICAL, "Signature verify failed\n"); |
|
350 |
ret = 0; |
|
351 |
goto done; |
|
352 |
}
|
|
353 |
||
354 |
/* print verification result to stdout and return */
|
|
355 |
if(dsigCtx->status == xmlSecDSigStatusSucceeded) { |
|
356 |
ret = 1; |
|
357 |
}
|
|
358 |
else { |
|
359 |
ret = 0; |
|
360 |
}
|
|
361 |
||
362 |
done: |
|
363 |
/* cleanup */
|
|
364 |
if(dsigCtx != NULL) { |
|
365 |
xmlSecDSigCtxDestroy(dsigCtx); |
|
366 |
}
|
|
367 |
||
368 |
if(doc != NULL) { |
|
369 |
xmlFreeDoc(doc); |
|
370 |
}
|
|
371 |
return ret; |
|
372 |
}
|
|
373 |
||
374 |
/**
|
|
375 |
* lasso_sha1:
|
|
376 |
* @str: a string
|
|
377 |
*
|
|
378 |
* Builds the SHA-1 message digest (cryptographic hash) of @str
|
|
379 |
*
|
|
380 |
* Return value: a 20 bytes length string
|
|
381 |
**/
|
|
382 |
xmlChar* |
|
383 |
lasso_sha1(xmlChar *str) |
|
384 |
{
|
|
385 |
unsigned char *md; |
|
386 |
||
387 |
if (str != NULL) { |
|
388 |
md = xmlMalloc(20); |
|
389 |
return SHA1(str, strlen(str), md); |
|
390 |
}
|
|
391 |
||
392 |
return NULL; |
|
393 |
}
|
|
394 |
||
395 |
/**
|
|
396 |
* lasso_str_escape:
|
|
397 |
* @str: a string
|
|
398 |
*
|
|
399 |
* Escapes the given string @str.
|
|
400 |
*
|
|
401 |
* Return value: a new escaped string or NULL in case of error.
|
|
402 |
**/
|
|
403 |
xmlChar * |
|
404 |
lasso_str_escape(xmlChar *str) |
|
405 |
{
|
|
406 |
/* value returned must be xmlFree() */
|
|
407 |
return xmlURIEscapeStr((const xmlChar *)str, NULL); |
|
408 |
}
|
|
409 |
||
410 |
xmlChar * |
|
411 |
lasso_str_hash(xmlChar *str, |
|
412 |
const char *private_key_file) |
|
413 |
{
|
|
414 |
xmlDocPtr doc; |
|
415 |
xmlChar *b64_digest, *digest = g_new0(xmlChar, 21); |
|
416 |
gint i; |
|
417 |
||
418 |
doc = lasso_str_sign(str, |
|
419 |
lassoSignatureMethodRsaSha1, |
|
420 |
private_key_file); |
|
421 |
b64_digest = xmlNodeGetContent(xmlSecFindNode( |
|
422 |
xmlDocGetRootElement(doc), |
|
423 |
xmlSecNodeDigestValue, |
|
424 |
xmlSecDSigNs)); |
|
425 |
i = xmlSecBase64Decode(b64_digest, digest, 21); |
|
426 |
/* printf("Decoded string %s length is %d\n", digest, i); */
|
|
427 |
xmlFree(b64_digest); |
|
428 |
xmlFreeDoc(doc); |
|
429 |
/* value returned must be xmlFree() */
|
|
430 |
return digest; |
|
431 |
}
|
|
432 |
||
433 |
/**
|
|
434 |
* lasso_str_sign:
|
|
435 |
* @str:
|
|
436 |
* @sign_method:
|
|
437 |
* @private_key_file:
|
|
438 |
*
|
|
439 |
*
|
|
440 |
*
|
|
441 |
* Return value:
|
|
442 |
**/
|
|
443 |
xmlDocPtr
|
|
444 |
lasso_str_sign(xmlChar *str, |
|
445 |
lassoSignatureMethod sign_method, |
|
446 |
const char *private_key_file) |
|
447 |
{
|
|
448 |
/* FIXME : renamed fct into lasso_query_add_signature
|
|
449 |
SHOULD returned a query (xmlChar) instead of xmlDoc */
|
|
450 |
xmlDocPtr doc = xmlNewDoc("1.0"); |
|
451 |
xmlNodePtr envelope = xmlNewNode(NULL, "Envelope"); |
|
452 |
xmlNodePtr cdata, data = xmlNewNode(NULL, "Data"); |
|
453 |
xmlNodePtr signNode = NULL; |
|
454 |
xmlNodePtr refNode = NULL; |
|
455 |
xmlNodePtr keyInfoNode = NULL; |
|
456 |
xmlSecDSigCtxPtr dsigCtx = NULL; |
|
457 |
||
458 |
/* create doc */
|
|
459 |
xmlNewNs(envelope, "urn:envelope", NULL); |
|
460 |
cdata = xmlNewCDataBlock(doc, str, strlen(str)); |
|
461 |
xmlAddChild(envelope, data); |
|
462 |
xmlAddChild(data, cdata); |
|
463 |
xmlAddChild((xmlNodePtr)doc, envelope); |
|
464 |
||
465 |
/* create signature template for enveloped signature */
|
|
466 |
switch (sign_method) { |
|
467 |
case lassoSignatureMethodRsaSha1: |
|
468 |
signNode = xmlSecTmplSignatureCreate(doc, xmlSecTransformExclC14NId, |
|
469 |
xmlSecTransformRsaSha1Id, NULL); |
|
470 |
break; |
|
471 |
case lassoSignatureMethodDsaSha1: |
|
472 |
signNode = xmlSecTmplSignatureCreate(doc, xmlSecTransformExclC14NId, |
|
473 |
xmlSecTransformDsaSha1Id, NULL); |
|
474 |
break; |
|
475 |
}
|
|
476 |
||
477 |
if (signNode == NULL) { |
|
478 |
message(G_LOG_LEVEL_CRITICAL, "Failed to create signature template\n"); |
|
479 |
goto done; |
|
480 |
}
|
|
481 |
||
482 |
/* add <dsig:Signature/> node to the doc */
|
|
483 |
xmlAddChild(xmlDocGetRootElement(doc), signNode); |
|
484 |
||
485 |
/* add reference */
|
|
486 |
refNode = xmlSecTmplSignatureAddReference(signNode, xmlSecTransformSha1Id, |
|
487 |
NULL, NULL, NULL); |
|
488 |
if (refNode == NULL) { |
|
489 |
message(G_LOG_LEVEL_CRITICAL, "Failed to add reference to signature template\n"); |
|
490 |
goto done; |
|
491 |
}
|
|
492 |
||
493 |
/* add enveloped transform */
|
|
494 |
if (xmlSecTmplReferenceAddTransform(refNode, |
|
495 |
xmlSecTransformEnvelopedId) == NULL) { |
|
496 |
message(G_LOG_LEVEL_CRITICAL, "Failed to add enveloped transform to reference\n"); |
|
497 |
goto done; |
|
498 |
}
|
|
499 |
||
500 |
/* add <dsig:KeyInfo/> and <dsig:KeyName/> nodes to put key name in the
|
|
501 |
signed document */
|
|
502 |
keyInfoNode = xmlSecTmplSignatureEnsureKeyInfo(signNode, NULL); |
|
503 |
if (keyInfoNode == NULL) { |
|
504 |
message(G_LOG_LEVEL_CRITICAL, "Failed to add key info\n"); |
|
505 |
goto done; |
|
506 |
}
|
|
507 |
||
508 |
if (xmlSecTmplKeyInfoAddKeyName(keyInfoNode, NULL) == NULL) { |
|
509 |
message(G_LOG_LEVEL_CRITICAL, "Failed to add key name\n"); |
|
510 |
goto done; |
|
511 |
}
|
|
512 |
||
513 |
/* create signature context */
|
|
514 |
dsigCtx = xmlSecDSigCtxCreate(NULL); |
|
515 |
if (dsigCtx == NULL) { |
|
516 |
message(G_LOG_LEVEL_CRITICAL, "Failed to create signature context\n"); |
|
517 |
goto done; |
|
518 |
}
|
|
519 |
||
520 |
/* load private key */
|
|
521 |
dsigCtx->signKey = xmlSecCryptoAppKeyLoad(private_key_file, |
|
522 |
xmlSecKeyDataFormatPem, |
|
523 |
NULL, NULL, NULL); |
|
524 |
if (dsigCtx->signKey == NULL) { |
|
525 |
message(G_LOG_LEVEL_CRITICAL, "Failed to load private pem key from \"%s\"\n", |
|
526 |
private_key_file); |
|
527 |
goto done; |
|
528 |
}
|
|
529 |
||
530 |
/* sign the template */
|
|
531 |
if (xmlSecDSigCtxSign(dsigCtx, signNode) < 0) { |
|
532 |
message(G_LOG_LEVEL_CRITICAL, "Signature failed\n"); |
|
533 |
goto done; |
|
534 |
}
|
|
535 |
||
536 |
/* xmlDocDump(stdout, doc); */
|
|
537 |
xmlSecDSigCtxDestroy(dsigCtx); |
|
538 |
/* doc must be freed be caller */
|
|
539 |
return doc; |
|
540 |
||
541 |
done: |
|
542 |
/* cleanup */
|
|
543 |
if (dsigCtx != NULL) { |
|
544 |
xmlSecDSigCtxDestroy(dsigCtx); |
|
545 |
}
|
|
546 |
||
547 |
if (doc != NULL) { |
|
548 |
xmlFreeDoc(doc); |
|
549 |
}
|
|
550 |
return NULL; |
|
551 |
}
|
|
552 |
||
553 |
/**
|
|
554 |
* lasso_str_unescape:
|
|
555 |
* @str: an escaped string
|
|
556 |
*
|
|
557 |
* Unescapes the given string @str.
|
|
558 |
*
|
|
559 |
* Return value: a new unescaped string or NULL in case of error.
|
|
560 |
**/
|
|
561 |
xmlChar * |
|
562 |
lasso_str_unescape(xmlChar *str) |
|
563 |
{
|
|
564 |
xmlChar *ret; |
|
565 |
||
566 |
ret = g_malloc(strlen(str) * 2); /* XXX why *2? strlen(str) should be enough */ |
|
567 |
xmlURIUnescapeString((const char *)str, 0, ret); |
|
568 |
return ret; |
|
569 |
}
|