diff options
Diffstat (limited to 'wgtpkg-xmlsec.c')
-rw-r--r-- | wgtpkg-xmlsec.c | 352 |
1 files changed, 352 insertions, 0 deletions
diff --git a/wgtpkg-xmlsec.c b/wgtpkg-xmlsec.c new file mode 100644 index 0000000..5c65217 --- /dev/null +++ b/wgtpkg-xmlsec.c @@ -0,0 +1,352 @@ +/* + Copyright 2015 IoT.bzh + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + + +#include <syslog.h> +#include <unistd.h> +#include <stdio.h> +#include <dirent.h> +#include <string.h> +#include <assert.h> + +#include <libxml/tree.h> +#include <xmlsec/xmlsec.h> +#include <xmlsec/xmltree.h> +#include <xmlsec/xmldsig.h> +#include <xmlsec/crypto.h> +#include <xmlsec/templates.h> +#include <xmlsec/errors.h> +#include <xmlsec/io.h> + + +#include "wgtpkg.h" + +static int initstatus; +static int initdone; +static xmlSecKeysMngrPtr keymgr; + +#ifndef CA_ROOT_DIRECTORY +#define CA_ROOT_DIRECTORY "./ca-certificates" +#endif + +static int file_match_cb(const char *uri) +{ + struct filedesc *fdesc = file_of_name(uri); + return fdesc != NULL && fdesc->type == type_file && (fdesc->flags & flag_distributor_signature) == 0; +} + +static void *file_open_cb(const char *file) +{ + struct filedesc *fdesc; + FILE *f; + + fdesc = file_of_name(file); + if (fdesc == NULL) { + syslog(LOG_ERR, "shouldn't open uri %s", file); + return NULL; + } + + f = fopen(file, "r"); + if (f == NULL) + syslog(LOG_ERR, "can't open file %s for reading", file); + else + fdesc->flags |= flag_opened; + + return f; +} + +static int file_read_cb(void *context, char *buffer, int len) +{ + size_t r = fread(buffer, 1, len, (FILE*)context); + return r ? (int)r : feof((FILE*)context) ? 0 : - 1; +} + +static int file_close_cb(void *context) +{ + return (int)fclose((FILE*)context); +} + +static void errors_cb(const char *file, int line, const char *func, const char *errorObject, const char *errorSubject, int reason, const char *msg) +{ + syslog(LOG_ERR, "xmlSec error %3d: %s (subject=\"%s\", object=\"%s\")", reason, msg, errorSubject ? errorSubject : "?", errorObject ? errorObject : "?"); +} + +static int fill_trusted_keys() +{ + int err; + DIR *dir; + struct dirent *ent; + char path[PATH_MAX], *e; + + e = stpcpy(path, CA_ROOT_DIRECTORY); + dir = opendir(path); + if (!dir) { + syslog(LOG_ERR, "opendir %s failed in fill_trusted_keys", path); + return -1; + } + + *e++ = '/'; + ent = readdir(dir); + while (ent != NULL) { + if (ent->d_type == DT_REG) { + strcpy(e, ent->d_name); + err = xmlSecCryptoAppKeysMngrCertLoad(keymgr, path, xmlSecKeyDataFormatPem, xmlSecKeyDataTypeTrusted); + if (err < 0) { + syslog(LOG_ERR, "xmlSecCryptoAppKeysMngrCertLoadMemory failed for %s", path); + closedir(dir); + return -1; + } + } + ent = readdir(dir); + } + + closedir(dir); + return 0; + +} + +int xmlsec_init() +{ + + if (initdone) + goto end; + + initdone = 1; + initstatus = -1; + + if(xmlSecInit() < 0) { + syslog(LOG_ERR, "xmlSecInit failed."); + goto end; + } + + if(xmlSecCryptoAppInit(NULL) < 0) { + syslog(LOG_ERR, "xmlSecCryptoAppInit failed."); + goto end; + } + + if(xmlSecCryptoInit() < 0) { + syslog(LOG_ERR, "xmlSecCryptoInit failed."); + goto end; + } + + xmlSecErrorsSetCallback(errors_cb); + + xmlSecIOCleanupCallbacks(); + if (xmlSecIORegisterCallbacks(file_match_cb, + file_open_cb, file_read_cb, file_close_cb)) { + syslog(LOG_ERR, "xmlSecIORegisterCallbacks failed."); + goto end; + } + + keymgr = xmlSecKeysMngrCreate(); + if (keymgr == NULL) { + syslog(LOG_ERR, "xmlSecKeysMngrCreate failed."); + goto end; + } + + if(xmlSecCryptoAppDefaultKeysMngrInit(keymgr) < 0) { + syslog(LOG_ERR, "xmlSecCryptoAppDefaultKeysMngrInit failed."); + goto end; + } + fill_trusted_keys(); + + initstatus = 0; +end: + return initstatus; +} + + +void xmlsec_shutdown() +{ + xmlSecKeysMngrDestroy(keymgr); + + xmlSecCryptoShutdown(); + + xmlSecCryptoAppShutdown(); + + xmlSecShutdown(); +} + +int xmlsec_verify(xmlNodePtr node) +{ + int rc; + xmlSecDSigCtxPtr dsigctx; + + assert(initdone && !initstatus); + + dsigctx = xmlSecDSigCtxCreate(keymgr); + if (dsigctx == NULL) { + syslog(LOG_ERR, "xmlSecDSigCtxCreate failed."); + rc = -1; + } else { + rc = xmlSecDSigCtxVerify(dsigctx, node); + if (rc) + syslog(LOG_ERR, "xmlSecDSigCtxVerify failed."); + else if (dsigctx->status != xmlSecDSigStatusSucceeded) { + syslog(LOG_ERR, "invalid signature."); + rc = -1; + } + xmlSecDSigCtxDestroy(dsigctx); + } + + return rc; +} + +static const struct { const char *id; const char *xml; } properties[2] = { + { + .id = "AuthorSignature", + .xml = + "<SignatureProperties xmlns:dsp=\"http://www.w3.org/2009/xmldsig-properties\">" + "<SignatureProperty Id=\"profile\" Target=\"#AuthorSignature\">" + "<dsp:Profile URI=\"http://www.w3.org/ns/widgets-digsig#profile\"></dsp:Profile>" + "</SignatureProperty>" + "<SignatureProperty Id=\"role\" Target=\"#AuthorSignature\">" + "<dsp:Role URI=\"http://www.w3.org/ns/widgets-digsig#role-author\"></dsp:Role>" + "</SignatureProperty>" + "<SignatureProperty Id=\"identifier\" Target=\"#AuthorSignature\">" + "<dsp:Identifier></dsp:Identifier>" + "</SignatureProperty>" + "</SignatureProperties>" + }, + { + .id = "DistributorSignature", + .xml = + "<SignatureProperties xmlns:dsp=\"http://www.w3.org/2009/xmldsig-properties\">" + "<SignatureProperty Id=\"profile\" Target=\"#DistributorSignature\">" + "<dsp:Profile URI=\"http://www.w3.org/ns/widgets-digsig#profile\"></dsp:Profile>" + "</SignatureProperty>" + "<SignatureProperty Id=\"role\" Target=\"#DistributorSignature\">" + "<dsp:Role URI=\"http://www.w3.org/ns/widgets-digsig#role-distributor\"></dsp:Role>" + "</SignatureProperty>" + "<SignatureProperty Id=\"identifier\" Target=\"#DistributorSignature\">" + "<dsp:Identifier></dsp:Identifier>" + "</SignatureProperty>" + "</SignatureProperties>" + } +}; + +xmlDocPtr xmlsec_create(int index, const char *key, const char **certs) +{ + unsigned int i, fc, mask; + struct filedesc *fdesc; + xmlNodePtr sign, obj, ref, kinfo, props; + xmlDocPtr doc; + int rc; + xmlSecDSigCtxPtr dsigctx; + + assert(initdone && !initstatus); + + /* create the document */ + doc = xmlNewDoc("1.0"); + if (doc == NULL) { + syslog(LOG_ERR, "xmlNewDoc failed"); + goto error; + } + + /* create the root signature node */ + sign = xmlSecTmplSignatureCreate(doc, xmlSecTransformInclC14N11Id, xmlSecTransformRsaSha256Id, properties[!!index].id); + if (sign == NULL) { + syslog(LOG_ERR, "xmlSecTmplSignatureCreate failed"); + goto error2; + } + xmlDocSetRootElement(doc, sign); + + /* create the object and its reference */ + obj = xmlSecTmplSignatureAddObject(sign, "prop", NULL, NULL); + if (obj == NULL) { + syslog(LOG_ERR, "xmlSecTmplSignatureAddObject failed"); + goto error2; + } + rc = xmlParseBalancedChunkMemory(doc, NULL, NULL, 0, properties[!!index].xml, &props); + if (rc) { + syslog(LOG_ERR, "xmlParseBalancedChunkMemory failed"); + goto error2; + } + if (NULL == xmlAddChild(obj, props)) { + syslog(LOG_ERR, "filling object node failed"); + xmlFreeNode(obj); + goto error2; + } + + /* create references to files */ + mask = index ? flag_distributor_signature : flag_signature; + fc = file_count(); + for (i = 0 ; i < fc ; i++) { + fdesc = file_of_index(i); + if (fdesc->type == type_file && (fdesc->flags & mask) == 0) { + ref = xmlSecTmplSignatureAddReference(sign, xmlSecTransformSha256Id, NULL, fdesc->name, NULL); + if (ref == NULL) { + syslog(LOG_ERR, "creation of reference to %s failed", fdesc->name); + goto error2; + } + } + } + + /* create reference to object having properties */ + ref = xmlSecTmplSignatureAddReference(sign, xmlSecTransformSha256Id, NULL, "#prop", NULL); + if (ref == NULL) { + syslog(LOG_ERR, "creation of reference to #prop failed"); + goto error2; + } + if (NULL == xmlSecTmplReferenceAddTransform(ref, xmlSecTransformInclC14N11Id)) { + syslog(LOG_ERR, "setting transform reference to #prop failed"); + goto error2; + } + + /* adds the X509 data */ + kinfo = xmlSecTmplSignatureEnsureKeyInfo(sign, NULL); + if (kinfo == NULL) { + syslog(LOG_ERR, "xmlSecTmplSignatureEnsureKeyInfo failed"); + goto error2; + } + if (NULL == xmlSecTmplKeyInfoAddX509Data(kinfo)) { + syslog(LOG_ERR, "xmlSecTmplKeyInfoAddX509Data failed"); + goto error2; + } + + /* sign now */ + dsigctx = xmlSecDSigCtxCreate(keymgr); + if (dsigctx == NULL) { + syslog(LOG_ERR, "xmlSecDSigCtxCreate failed."); + goto error3; + } + dsigctx->signKey = xmlSecCryptoAppKeyLoad(key, xmlSecKeyDataFormatPem, NULL, NULL, NULL); + if (dsigctx->signKey == NULL) { + syslog(LOG_ERR, "loading key %s failed.", key); + goto error3; + } + while (*certs) { + if(xmlSecCryptoAppKeyCertLoad(dsigctx->signKey, *certs, xmlSecKeyDataFormatPem) < 0) { + syslog(LOG_ERR, "loading certificate %s failed.", *certs); + goto error3; + } + certs++; + } + if(xmlSecDSigCtxSign(dsigctx, sign) < 0) { + syslog(LOG_ERR, "signing the document failed."); + goto error3; + } + xmlSecDSigCtxDestroy(dsigctx); + return doc; + +error3: + xmlSecDSigCtxDestroy(dsigctx); +error2: + xmlFreeDoc(doc); +error: + return NULL; +} + |