/* $Id$ * * Lasso - A free implementation of the Liberty Alliance specifications. * * Copyright (C) 2004 Entr'ouvert * http://lasso.entrouvert.org * * Authors: Nicolas Clapies * Valery Febvre * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include xmlChar * lasso_build_random_sequence(guint8 size) { int i, val; xmlChar *seq; g_return_val_if_fail(size > 0, NULL); seq = xmlMalloc(size+1); for (i=0; i= 32 && size <= 40) || size == 0, NULL); if (size == 0) size = 32; id = xmlMalloc(size+1+1); /* one for _ and one for \0 */ /* build hex number (<= 2^exp-1) */ id[0] = '_'; for (i=1; istr, (xmlSecSize) cert->len, xmlSecKeyDataFormatPem, xmlSecKeyDataTypeTrusted); g_string_free(cert, TRUE); cert = NULL; if (ret < 0) { goto error; } } else if (cert != NULL && line != NULL && line[0] != '\0') { g_string_append(cert, line); } else { debug("Empty line found in the CA certificate chain file") } /* free last line read */ if (line != NULL) { g_free(line); line = NULL; } } goto done; error: if (line != NULL) { g_free(line); line = NULL; } xmlSecKeysMngrDestroy(keys_mngr); keys_mngr = NULL; done: g_io_channel_shutdown(gioc, TRUE, NULL); return keys_mngr; } /** * lasso_query_get_value: * @query: a query (an url-encoded node) * @param: the parameter * * Returns the value of the given @param * * Return value: a string or NULL if no parameter found **/ GPtrArray * lasso_query_get_value(const gchar *query, const xmlChar *param) { guint i; GData *gd; GPtrArray *tmp_array, *array = NULL; gd = lasso_query_to_dict(query); tmp_array = (GPtrArray *)g_datalist_get_data(&gd, (gchar *)param); /* create a copy of tmp_array */ if (tmp_array != NULL) { array = g_ptr_array_new(); for(i=0; ilen; i++) g_ptr_array_add(array, g_strdup(g_ptr_array_index(tmp_array, i))); } g_datalist_clear(&gd); return array; } static void gdata_query_to_dict_destroy_notify(gpointer data) { guint i; GPtrArray *array = data; for (i=0; ilen; i++) { g_free(array->pdata[i]); } g_ptr_array_free(array, TRUE); } /** * lasso_query_to_dict: * @query: the query (an url-encoded node) * * Explodes query to build a dictonary. * Dictionary values are stored in GPtrArray. * The caller is responsible for freeing returned object by calling * g_datalist_clear() function. * * Return value: a dictonary **/ GData * lasso_query_to_dict(const gchar *query) { GData *gd = NULL; gchar **sa1, **sa2, **sa3; xmlChar *str_unescaped; GPtrArray *gpa; guint i, j; g_datalist_init(&gd); i = 0; sa1 = g_strsplit(query, "&", 0); while (sa1[i++] != NULL) { /* split of key=value to get (key, value) sub-strings */ sa2 = g_strsplit(sa1[i-1], "=", 0); /* if no key / value found, then continue */ if (sa2 == NULL) { continue; } /* if only a key but no value, then continue */ if (sa2[1] == NULL || xmlStrEqual(sa2[1], "")) { continue; } /* split of value to get mutli values sub-strings separated by SPACE char */ str_unescaped = lasso_str_unescape(sa2[1]); sa3 = g_strsplit(str_unescaped, " ", 0); if (sa3 == NULL) { g_strfreev(sa2); continue; } xmlFree(str_unescaped); gpa = g_ptr_array_new(); j = 0; while (sa3[j++] != NULL) { g_ptr_array_add(gpa, g_strdup(sa3[j-1])); } /* add key => values in dict */ g_datalist_set_data_full(&gd, sa2[0], gpa, gdata_query_to_dict_destroy_notify); g_strfreev(sa3); g_strfreev(sa2); } g_strfreev(sa1); return gd; } /** * lasso_query_verify_signature: * @query: a query (an url-encoded and signed node) * @sender_public_key_file: the sender public key * @recipient_private_key_file: the recipient private key * * Verifies the query signature. * * Return value: 0 if signature is valid * a positive value if signature was not found or is invalid * a negative value if an error occurs during verification **/ int lasso_query_verify_signature(const gchar *query, const xmlChar *sender_public_key_file, const xmlChar *recipient_private_key_file) { GData *gd; xmlDocPtr doc; xmlNodePtr sigNode, sigValNode; xmlSecDSigCtxPtr dsigCtx; xmlChar *str_unescaped; xmlChar *sigAlg; gchar **str_split; gint ret = 0; /* split query, the signature MUST be the last param of the query */ str_split = g_strsplit(query, "&Signature=", 0); if (str_split[1] == NULL) { return LASSO_DS_ERROR_SIGNATURE_NOT_FOUND; } /* get SigAlg in query (left part) */ gd = lasso_query_to_dict(str_split[0]); sigAlg = lasso_g_ptr_array_index((GPtrArray *)g_datalist_get_data(&gd, "SigAlg"), 0); /* sign the query (without the signature param) */ if (xmlStrEqual(sigAlg, xmlSecHrefRsaSha1)) { doc = lasso_str_sign(str_split[0], lassoSignatureMethodRsaSha1, recipient_private_key_file); } else if (xmlStrEqual(sigAlg, xmlSecHrefDsaSha1)) { doc = lasso_str_sign(str_split[0], lassoSignatureMethodDsaSha1, recipient_private_key_file); } else { message(G_LOG_LEVEL_CRITICAL, lasso_strerror(LASSO_DS_ERROR_INVALID_SIGALG)); ret = LASSO_DS_ERROR_INVALID_SIGALG; goto done; } /* replace doc signature value by the TRUE signature value found in the query */ sigValNode = xmlSecFindNode(xmlDocGetRootElement(doc), xmlSecNodeSignatureValue, xmlSecDSigNs); str_unescaped = lasso_str_unescape(str_split[1]); xmlNodeSetContent(sigValNode, str_unescaped); xmlFree(str_unescaped); /* start to verify the signature */ /* find start node */ sigNode = xmlSecFindNode(xmlDocGetRootElement(doc), xmlSecNodeSignature, xmlSecDSigNs); if (sigNode == NULL) { message(G_LOG_LEVEL_CRITICAL, lasso_strerror(LASSO_DS_ERROR_SIGNATURE_NOT_FOUND), ""); ret = LASSO_DS_ERROR_SIGNATURE_NOT_FOUND; goto done; } /* create signature context */ dsigCtx = xmlSecDSigCtxCreate(NULL); if(dsigCtx == NULL) { message(G_LOG_LEVEL_CRITICAL, lasso_strerror(LASSO_DS_ERROR_CONTEXT_CREATION_FAILED)); ret = LASSO_DS_ERROR_CONTEXT_CREATION_FAILED; goto done; } /* load public key */ dsigCtx->signKey = xmlSecCryptoAppKeyLoad(sender_public_key_file, xmlSecKeyDataFormatPem, NULL, NULL, NULL); if(dsigCtx->signKey == NULL) { message(G_LOG_LEVEL_CRITICAL, lasso_strerror(LASSO_DS_ERROR_PUBLIC_KEY_LOAD_FAILED), sender_public_key_file); ret = LASSO_DS_ERROR_PUBLIC_KEY_LOAD_FAILED; goto done; } /* verify signature */ if(xmlSecDSigCtxVerify(dsigCtx, sigNode) < 0) { message(G_LOG_LEVEL_CRITICAL, lasso_strerror(LASSO_DS_ERROR_SIGNATURE_VERIFICATION_FAILED), ""); ret = LASSO_DS_ERROR_SIGNATURE_VERIFICATION_FAILED; goto done; } if(dsigCtx->status == xmlSecDSigStatusSucceeded) { ret = 0; } else { message(G_LOG_LEVEL_CRITICAL, lasso_strerror(LASSO_DS_ERROR_INVALID_SIGNATURE), ""); ret = LASSO_DS_ERROR_INVALID_SIGNATURE; } done: /* cleanup */ g_strfreev(str_split); g_datalist_clear(&gd); if(dsigCtx != NULL) { xmlSecDSigCtxDestroy(dsigCtx); } if(doc != NULL) { xmlFreeDoc(doc); } return ret; } /** * lasso_sha1: * @str: a string * * Builds the SHA-1 message digest (cryptographic hash) of @str * * Return value: 20-bytes buffer allocated with xmlMalloc **/ xmlChar* lasso_sha1(xmlChar *str) { xmlChar *md; if (str != NULL) { md = xmlMalloc(20); return SHA1(str, strlen(str), md); } return NULL; } /** * lasso_str_escape: * @str: a string * * Escapes the given string @str. * * Return value: a new escaped string or NULL in case of error. **/ xmlChar * lasso_str_escape(xmlChar *str) { /* value returned must be xmlFree() */ return xmlURIEscapeStr(str, NULL); } xmlChar * lasso_str_hash(xmlChar *str, const char *private_key_file) { xmlDocPtr doc; xmlChar *b64_digest, *digest = g_new0(xmlChar, 21); gint i; doc = lasso_str_sign(str, lassoSignatureMethodRsaSha1, private_key_file); b64_digest = xmlNodeGetContent(xmlSecFindNode( xmlDocGetRootElement(doc), xmlSecNodeDigestValue, xmlSecDSigNs)); i = xmlSecBase64Decode(b64_digest, digest, 21); xmlFree(b64_digest); xmlFreeDoc(doc); /* value returned must be xmlFree() */ return digest; } /** * lasso_str_sign: * @str: * @sign_method: * @private_key_file: * * * * Return value: **/ xmlDocPtr lasso_str_sign(xmlChar *str, lassoSignatureMethod sign_method, const char *private_key_file) { /* FIXME : renamed fct into lasso_query_add_signature SHOULD returned a query (xmlChar) instead of xmlDoc */ xmlDocPtr doc = xmlNewDoc("1.0"); xmlNodePtr envelope = xmlNewNode(NULL, "Envelope"); xmlNodePtr cdata, data = xmlNewNode(NULL, "Data"); xmlNodePtr signNode = NULL; xmlNodePtr refNode = NULL; xmlNodePtr keyInfoNode = NULL; xmlSecDSigCtxPtr dsigCtx = NULL; /* create doc */ xmlNewNs(envelope, "urn:envelope", NULL); cdata = xmlNewCDataBlock(doc, str, strlen(str)); xmlAddChild(envelope, data); xmlAddChild(data, cdata); xmlAddChild((xmlNodePtr)doc, envelope); /* create signature template for enveloped signature */ switch (sign_method) { case lassoSignatureMethodRsaSha1: signNode = xmlSecTmplSignatureCreate(doc, xmlSecTransformExclC14NId, xmlSecTransformRsaSha1Id, NULL); break; case lassoSignatureMethodDsaSha1: signNode = xmlSecTmplSignatureCreate(doc, xmlSecTransformExclC14NId, xmlSecTransformDsaSha1Id, NULL); break; } if (signNode == NULL) { message(G_LOG_LEVEL_CRITICAL, "Failed to create signature template\n"); goto done; } /* add node to the doc */ xmlAddChild(xmlDocGetRootElement(doc), signNode); /* add reference */ refNode = xmlSecTmplSignatureAddReference(signNode, xmlSecTransformSha1Id, NULL, NULL, NULL); if (refNode == NULL) { message(G_LOG_LEVEL_CRITICAL, "Failed to add reference to signature template\n"); goto done; } /* add enveloped transform */ if (xmlSecTmplReferenceAddTransform(refNode, xmlSecTransformEnvelopedId) == NULL) { message(G_LOG_LEVEL_CRITICAL, "Failed to add enveloped transform to reference\n"); goto done; } /* add */ keyInfoNode = xmlSecTmplSignatureEnsureKeyInfo(signNode, NULL); if (keyInfoNode == NULL) { message(G_LOG_LEVEL_CRITICAL, "Failed to add key info\n"); goto done; } /* create signature context */ dsigCtx = xmlSecDSigCtxCreate(NULL); if (dsigCtx == NULL) { message(G_LOG_LEVEL_CRITICAL, "Failed to create signature context\n"); goto done; } /* load private key */ dsigCtx->signKey = xmlSecCryptoAppKeyLoad(private_key_file, xmlSecKeyDataFormatPem, NULL, NULL, NULL); if (dsigCtx->signKey == NULL) { message(G_LOG_LEVEL_CRITICAL, "Failed to load private pem key from \"%s\"\n", private_key_file); goto done; } /* sign the template */ if (xmlSecDSigCtxSign(dsigCtx, signNode) < 0) { message(G_LOG_LEVEL_CRITICAL, "Signature failed\n"); goto done; } /* xmlDocDump(stdout, doc); */ xmlSecDSigCtxDestroy(dsigCtx); /* doc must be freed be caller */ return doc; done: /* cleanup */ if (dsigCtx != NULL) { xmlSecDSigCtxDestroy(dsigCtx); } if (doc != NULL) { xmlFreeDoc(doc); } return NULL; } /** * lasso_str_unescape: * @str: an escaped string * * Unescapes the given string @str. * * Return value: a new unescaped string or NULL in case of error. **/ xmlChar * lasso_str_unescape(xmlChar *str) { return xmlURIUnescapeString(str, 0, NULL); }