From bf7b5918fcc07713a29b9ca32f766b65b15a4ec2 Mon Sep 17 00:00:00 2001 From: José Bollo Date: Wed, 9 Dec 2015 14:35:04 +0100 Subject: refactoring sources MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: Id6d52eee86b706958972e9b345ec0d4d1e488146 Signed-off-by: José Bollo --- Makefile.am | 32 +---- configure.ac | 8 +- src/Makefile.am | 32 +++++ src/wgt-config-xml.c | 236 ++++++++++++++++++++++++++++++++ src/wgt-locales.c | 121 +++++++++++++++++ src/wgt-rootdir.c | 97 ++++++++++++++ src/wgt-strings.c | 31 +++++ src/wgt.h | 69 ++++++++++ src/wgtpkg-base64.c | 203 ++++++++++++++++++++++++++++ src/wgtpkg-certs.c | 80 +++++++++++ src/wgtpkg-digsig.c | 358 +++++++++++++++++++++++++++++++++++++++++++++++++ src/wgtpkg-files.c | 313 +++++++++++++++++++++++++++++++++++++++++++ src/wgtpkg-install.c | 84 ++++++++++++ src/wgtpkg-pack.c | 167 +++++++++++++++++++++++ src/wgtpkg-sign.c | 199 +++++++++++++++++++++++++++ src/wgtpkg-verbose.c | 36 +++++ src/wgtpkg-workdir.c | 166 +++++++++++++++++++++++ src/wgtpkg-xmlsec.c | 371 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/wgtpkg-zip.c | 342 +++++++++++++++++++++++++++++++++++++++++++++++ src/wgtpkg.h | 125 +++++++++++++++++ wgt-config-xml.c | 236 -------------------------------- wgt-locales.c | 121 ----------------- wgt-rootdir.c | 97 -------------- wgt-strings.c | 31 ----- wgt.h | 69 ---------- wgtpkg-base64.c | 203 ---------------------------- wgtpkg-certs.c | 80 ----------- wgtpkg-digsig.c | 358 ------------------------------------------------- wgtpkg-files.c | 313 ------------------------------------------- wgtpkg-install.c | 84 ------------ wgtpkg-pack.c | 167 ----------------------- wgtpkg-sign.c | 199 --------------------------- wgtpkg-verbose.c | 36 ----- wgtpkg-workdir.c | 166 ----------------------- wgtpkg-xmlsec.c | 371 --------------------------------------------------- wgtpkg-zip.c | 342 ----------------------------------------------- wgtpkg.h | 125 ----------------- 37 files changed, 3036 insertions(+), 3032 deletions(-) create mode 100644 src/Makefile.am create mode 100644 src/wgt-config-xml.c create mode 100644 src/wgt-locales.c create mode 100644 src/wgt-rootdir.c create mode 100644 src/wgt-strings.c create mode 100644 src/wgt.h create mode 100644 src/wgtpkg-base64.c create mode 100644 src/wgtpkg-certs.c create mode 100644 src/wgtpkg-digsig.c create mode 100644 src/wgtpkg-files.c create mode 100644 src/wgtpkg-install.c create mode 100644 src/wgtpkg-pack.c create mode 100644 src/wgtpkg-sign.c create mode 100644 src/wgtpkg-verbose.c create mode 100644 src/wgtpkg-workdir.c create mode 100644 src/wgtpkg-xmlsec.c create mode 100644 src/wgtpkg-zip.c create mode 100644 src/wgtpkg.h delete mode 100644 wgt-config-xml.c delete mode 100644 wgt-locales.c delete mode 100644 wgt-rootdir.c delete mode 100644 wgt-strings.c delete mode 100644 wgt.h delete mode 100644 wgtpkg-base64.c delete mode 100644 wgtpkg-certs.c delete mode 100644 wgtpkg-digsig.c delete mode 100644 wgtpkg-files.c delete mode 100644 wgtpkg-install.c delete mode 100644 wgtpkg-pack.c delete mode 100644 wgtpkg-sign.c delete mode 100644 wgtpkg-verbose.c delete mode 100644 wgtpkg-workdir.c delete mode 100644 wgtpkg-xmlsec.c delete mode 100644 wgtpkg-zip.c delete mode 100644 wgtpkg.h diff --git a/Makefile.am b/Makefile.am index 6c76395..f268924 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,32 +1,2 @@ -bin_PROGRAMS = wgtpkg-install wgtpkg-pack wgtpkg-sign - -COMMONSRCS = \ - wgtpkg-base64.c \ - wgtpkg-certs.c \ - wgtpkg-digsig.c \ - wgtpkg-files.c \ - wgtpkg-verbose.c \ - wgtpkg-workdir.c \ - wgtpkg-xmlsec.c \ - wgtpkg-zip.c - -WGTSRCS = \ - wgt-config-xml.c \ - wgt-locales.c \ - wgt-rootdir.c \ - wgt-strings.c - -AM_CFLAGS = -Wall -Wno-pointer-sign -AM_CFLAGS += -ffunction-sections -fdata-sections -AM_CFLAGS += ${ZIP_CFLAGS} ${XML2_CFLAGS} ${OPENSSL_CFLAGS} ${XMLSEC_CFLAGS} - -AM_LDFLAGS = -Wl,--gc-sections - -LDADD = ${ZIP_LIBS} ${XML2_LIBS} ${OPENSSL_LIBS} ${XMLSEC_LIBS} - -wgtpkg_install_SOURCES = wgtpkg-install.c ${WGTSRCS} ${COMMONSRCS} - -wgtpkg_sign_SOURCES = wgtpkg-sign.c ${COMMONSRCS} - -wgtpkg_pack_SOURCES = wgtpkg-pack.c ${COMMONSRCS} +SUBDIRS = src diff --git a/configure.ac b/configure.ac index 5cf0a62..ea099b0 100644 --- a/configure.ac +++ b/configure.ac @@ -5,8 +5,8 @@ AC_PREREQ([2.69]) AC_INIT([wgtpkg], [1.0], [wgtpkg@iot.bzh]) AM_INIT_AUTOMAKE #AM_INIT_AUTOMAKE([-Wall -Wno-pointer-sign]) -AC_CONFIG_SRCDIR([wgtpkg-files.c]) -AC_CONFIG_HEADERS([config.h]) +AC_CONFIG_SRCDIR([src/wgtpkg-files.c]) +AC_CONFIG_HEADERS([src/config.h]) # Checks for programs. #AC_PROG_CXX @@ -37,6 +37,8 @@ AC_TYPE_SSIZE_T AC_FUNC_MALLOC AC_FUNC_REALLOC AC_CHECK_FUNCS([atexit memmove mkdir realpath rmdir stpcpy strrchr strtoul]) +AC_CHECK_FUNCS([strcasecmp strdup strndup]) -AC_CONFIG_FILES([Makefile]) +AC_CONFIG_FILES([Makefile + src/Makefile]) AC_OUTPUT diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..6c76395 --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,32 @@ +bin_PROGRAMS = wgtpkg-install wgtpkg-pack wgtpkg-sign + +COMMONSRCS = \ + wgtpkg-base64.c \ + wgtpkg-certs.c \ + wgtpkg-digsig.c \ + wgtpkg-files.c \ + wgtpkg-verbose.c \ + wgtpkg-workdir.c \ + wgtpkg-xmlsec.c \ + wgtpkg-zip.c + +WGTSRCS = \ + wgt-config-xml.c \ + wgt-locales.c \ + wgt-rootdir.c \ + wgt-strings.c + +AM_CFLAGS = -Wall -Wno-pointer-sign +AM_CFLAGS += -ffunction-sections -fdata-sections +AM_CFLAGS += ${ZIP_CFLAGS} ${XML2_CFLAGS} ${OPENSSL_CFLAGS} ${XMLSEC_CFLAGS} + +AM_LDFLAGS = -Wl,--gc-sections + +LDADD = ${ZIP_LIBS} ${XML2_LIBS} ${OPENSSL_LIBS} ${XMLSEC_LIBS} + +wgtpkg_install_SOURCES = wgtpkg-install.c ${WGTSRCS} ${COMMONSRCS} + +wgtpkg_sign_SOURCES = wgtpkg-sign.c ${COMMONSRCS} + +wgtpkg_pack_SOURCES = wgtpkg-pack.c ${COMMONSRCS} + diff --git a/src/wgt-config-xml.c b/src/wgt-config-xml.c new file mode 100644 index 0000000..f92ae3a --- /dev/null +++ b/src/wgt-config-xml.c @@ -0,0 +1,236 @@ +/* + 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 +#include +#include +#include +#include + +#include +#include +#include + + +#include "wgt.h" + +static xmlDocPtr configxml = NULL; + +static xmlNodePtr next(xmlNodePtr node, const char *type) +{ + while (node && node->type != XML_ELEMENT_NODE && strcmp(type, node->name)) + node = node->next; + return node; +} + +static xmlNodePtr first(const char *type) +{ + xmlNodePtr root; + if (configxml) { + root = xmlDocGetRootElement(configxml); + if (root) + return next(root->children, type); + } + return NULL; +} + +static int scorelang(xmlNodePtr node) +{ + char *lang = xmlNodeGetLang(node); + int score = locales_score(lang); + xmlFree(lang); + return score; +} + +static xmlNodePtr element_based_localisation(const char *type) +{ + xmlNodePtr resu, elem; + int sr, s; + + resu = first(type); + if (resu) { + sr = scorelang(resu); + elem = next(resu->next, type); + while (resu) { + s = scorelang(elem); + if (s < sr) { + resu = elem; + sr = s; + } + elem = next(elem->next, type); + } + } + return resu; +} + +void confixml_close() +{ + if (configxml) { + xmlFreeDoc(configxml); + configxml = NULL; + } +} + +int confixml_open() +{ + int fd; + assert(!configxml); + fd = widget_open_read(_config_xml_); + if (fd < 0) { + syslog(LOG_ERR, "can't open config file %s", _config_xml_); + return fd; + } + configxml = xmlReadFd(fd, "_config_xml_", NULL, 0); + close(fd); + if (configxml == NULL) { + syslog(LOG_ERR, "xml parse of config file %s failed", _config_xml_); + return -1; + } + return 0; +} + +/* elements based on localisation */ +xmlNodePtr confixml_name() +{ + return element_based_localisation(_name_); +} + +xmlNodePtr confixml_description() +{ + return element_based_localisation(_description_); +} + +xmlNodePtr confixml_license() +{ + return element_based_localisation(_license_); +} + +/* elements based on path localisation */ +xmlNodePtr confixml_author() +{ + return first(_author_); +} + +xmlNodePtr confixml_content() +{ + return first(_content_); +} + +/* element multiple */ + +xmlNodePtr confixml_first_feature() +{ + return first(_feature_); +} + +xmlNodePtr confixml_next_feature(xmlNodePtr node) +{ + return next(node->next, _feature_); +} + +xmlNodePtr confixml_first_preference() +{ + return first(_preference_); +} + +xmlNodePtr confixml_next_preference(xmlNodePtr node) +{ + return next(node->next, _preference_); +} + +xmlNodePtr confixml_first_icon() +{ + return first(_icon_); +} + +xmlNodePtr confixml_next_icon(xmlNodePtr node) +{ + return next(node->next, _icon_); +} + +/* best sized icon */ + +static int score_dim(xmlNodePtr ref, xmlNodePtr x, const char *dim, int request) +{ + int r, iref, ix; + char *sref, *sx; + + sref = xmlGetProp(ref, dim); + if (sref) { + iref = atoi(sref); + xmlFree(sref); + sx = xmlGetProp(x, dim); + if (sx) { + /* sref && sx */ + ix = atoi(sx); + xmlFree(sx); + if (ix >= request) { + if (iref >= request) + r = ix - iref; + else + r = request - ix; + } else { + if (iref >= request) + r = iref - request; + else + r = iref - ix; + } + } else { + /* sref && !sx */ + if (iref >= request) + r = iref - request; + else + r = 0; + } + } else { + sx = xmlGetProp(x, dim); + if (sx) { + /* !sref && sx */ + ix = atoi(sx); + xmlFree(sx); + if (ix >= request) + r = request - ix; + else + r = 0; + } else { + /* !sref && !sx */ + r = 0; + } + } + return r; +} + +static int better_icon(xmlNodePtr ref, xmlNodePtr x, int width, int height) +{ + int sw = score_dim(ref, x, _width_, width); + int sh = score_dim(ref, x, _height_, height); + return sw+sh < 0; +} + +xmlNodePtr confixml_icon(int width, int height) +{ + xmlNodePtr resu, icon; + + resu = confixml_first_icon(); + icon = confixml_next_icon(resu); + while (icon) { + if (better_icon(resu, icon, width, height)) + resu = icon; + icon = confixml_next_icon(icon); + } + return resu; +} + diff --git a/src/wgt-locales.c b/src/wgt-locales.c new file mode 100644 index 0000000..9b97cc4 --- /dev/null +++ b/src/wgt-locales.c @@ -0,0 +1,121 @@ +/* + 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. +*/ + + +#define _GNU_SOURCE +#include +#include +#include +#include + +#include "wgt.h" + +struct locarr { + int count; + char **arr; +}; + +static struct locarr locarr = { 0, NULL }; + +static int add(const char *locstr, int length) +{ + int i; + char *item, **ptr; + + item = strndup(locstr, length); + if (item != NULL) { + for (i = 0 ; item[i] ; i++) + item[i] = tolower(item[i]); + for (i = 0 ; i < locarr.count ; i++) + if (!strcmp(item, locarr.arr[i])) { + free(item); + return 0; + } + + ptr = realloc(locarr.arr, (1 + locarr.count) * sizeof(locarr.arr[0])); + if (ptr) { + locarr.arr = ptr; + locarr.arr[locarr.count++] = item; + return 0; + } + free(item); + } + errno = ENOMEM; + return -1; +} + +void locales_reset() +{ + while (locarr.count) + free(locarr.arr[--locarr.count]); +} + +int locales_add(const char *locstr) +{ + const char *stop, *next; + while (*locstr) { + stop = strchrnul(locstr, ','); + next = stop + !!*stop; + while (locstr != stop) { + if (add(locstr, stop - locstr)) + return -1; + do { stop--; } while(stop > locstr && *stop != '-'); + } + locstr = next; + } + return 0; +} + +int locales_score(const char *lang) +{ + int i; + + if (lang) + for (i = 0 ; i < locarr.count ; i++) + if (!strcasecmp(lang, locarr.arr[i])) + return i; + + return INT_MAX; +} + +char *locales_locate_file(const char *filename) +{ + int i; + char path[PATH_MAX]; + char * result; + + for (i = 0 ; i < locarr.count ; i++) { + if (snprintf(path, sizeof path, "locales/%s/%s", locarr.arr[i], filename) >= (int)(sizeof path)) { + errno = EINVAL; + return NULL; + } + if (widget_has(path)) { + result = strdup(path); + if (!result) + errno = ENOMEM; + return result; + } + } + if (widget_has(filename)) { + result = strdup(filename); + if (!result) + errno = ENOMEM; + return result; + } + errno = ENOENT; + return NULL; +} + diff --git a/src/wgt-rootdir.c b/src/wgt-rootdir.c new file mode 100644 index 0000000..4df1705 --- /dev/null +++ b/src/wgt-rootdir.c @@ -0,0 +1,97 @@ +/* + 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. +*/ + +#define _GNU_SOURCE + +#include +#include +#include + +#include "wgt.h" + + +static int rootfd = AT_FDCWD; + +int widget_set_rootdir(const char *pathname) +{ + int rfd; + + if (!pathname) + rfd = AT_FDCWD; + else { + rfd = openat(AT_FDCWD, pathname, O_PATH|O_DIRECTORY); + if (rfd < 0) + return rfd; + } + if (rootfd >= 0) + close(rootfd); + rootfd = AT_FDCWD; + return 0; +} + +static int validsubpath(const char *subpath) +{ + int l = 0, i = 0; + if (subpath[i] == '/') + return 0; + while(subpath[i]) { + switch(subpath[i++]) { + case '.': + if (!subpath[i]) + break; + if (subpath[i] == '/') { + i++; + break; + } + if (subpath[i++] == '.') { + if (!subpath[i]) { + l--; + break; + } + if (subpath[i++] == '/') { + l--; + break; + } + } + default: + while(subpath[i] && subpath[i] != '/') + i++; + l++; + case '/': + break; + } + } + return l >= 0; +} + +int widget_has(const char *filename) +{ + if (!validsubpath(filename)) { + errno = EINVAL; + return -1; + } + return 0 == faccessat(rootfd, filename, F_OK, 0); +} + +int widget_open_read(const char *filename) +{ + if (!validsubpath(filename)) { + errno = EINVAL; + return -1; + } + return openat(rootfd, filename, O_RDONLY); +} + diff --git a/src/wgt-strings.c b/src/wgt-strings.c new file mode 100644 index 0000000..a2fff39 --- /dev/null +++ b/src/wgt-strings.c @@ -0,0 +1,31 @@ +/* + 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 "wgt.h" + +const char _config_xml_[] = "config.xml"; +const char _name_[] = "name"; +const char _description_[] = "description"; +const char _author_[] = "author"; +const char _license_[] = "license"; +const char _icon_[] = "icon"; +const char _content_[] = "content"; +const char _feature_[] = "feature"; +const char _preference_[] = "preference"; +const char _width_[] = "width"; +const char _height_[] = "height"; + + diff --git a/src/wgt.h b/src/wgt.h new file mode 100644 index 0000000..d95e65b --- /dev/null +++ b/src/wgt.h @@ -0,0 +1,69 @@ +/* + 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 +#include "config.h" + + +/**************************************************************/ +/* from wgt-config-xml */ + +extern int confixml_open(); +extern void confixml_close(); +extern xmlNodePtr confixml_name(); +extern xmlNodePtr confixml_description(); +extern xmlNodePtr confixml_license(); +extern xmlNodePtr confixml_author(); +extern xmlNodePtr confixml_content(); +extern xmlNodePtr confixml_icon(int width, int height); +extern xmlNodePtr confixml_first_feature(); +extern xmlNodePtr confixml_next_feature(xmlNodePtr node); +extern xmlNodePtr confixml_first_preference(); +extern xmlNodePtr confixml_next_preference(xmlNodePtr node); +extern xmlNodePtr confixml_first_icon(); +extern xmlNodePtr confixml_next_icon(xmlNodePtr node); + +/**************************************************************/ +/* from wgt-locales */ + +extern void locales_reset(); +extern int locales_add(const char *locstr); +extern int locales_score(const char *lang); +extern char *locales_locate_file(const char *filename); + +/**************************************************************/ +/* from wgt-rootdir */ + +extern int widget_set_rootdir(const char *pathname); +extern int widget_has(const char *filename); +extern int widget_open_read(const char *filename); + +/**************************************************************/ +/* from wgt-strings */ + +extern const char _config_xml_[]; +extern const char _name_[]; +extern const char _description_[]; +extern const char _author_[]; +extern const char _license_[]; +extern const char _icon_[]; +extern const char _content_[]; +extern const char _feature_[]; +extern const char _preference_[]; +extern const char _width_[]; +extern const char _height_[]; + diff --git a/src/wgtpkg-base64.c b/src/wgtpkg-base64.c new file mode 100644 index 0000000..5cf6f8a --- /dev/null +++ b/src/wgtpkg-base64.c @@ -0,0 +1,203 @@ +/* + 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 +#include +#include + +#include "wgtpkg.h" + +static char tob64(char x) +{ + if (x < 26) + return 'A' + x; + if (x < 52) + return 'a' + x - 26; + if (x < 62) + return '0' + x - 52; + return x == 62 ? '+' : '/'; +} + +char *base64encw(const char *buffer, int length, int width) +{ + int remain, in, out; + char *result; + + if (width == 0 || width % 4) { + syslog(LOG_ERR, "bad width in base64enc"); + return NULL; + } + result = malloc(2 + 4 * ((length + 2) / 3) + (length / width)); + if (result == NULL) { + syslog(LOG_ERR, "malloc failed in base64enc"); + return NULL; + } + in = out = 0; + remain = length; + while (remain >= 3) { + if (out % (width + 1) == width) + result[out++] = '\n'; + result[out] = tob64((buffer[in] >> 2) & '\x3f'); + result[out+1] = tob64(((buffer[in] << 4) & '\x30') | ((buffer[in+1] >> 4) & '\x0f')); + result[out+2] = tob64(((buffer[in+1] << 2) & '\x3c') | ((buffer[in+2] >> 6) & '\x03')); + result[out+3] = tob64(buffer[in+2] & '\x3f'); + remain -= 3; + in += 3; + out += 4; + } + if (remain != 0) { + if (out % (width + 1) == width) + result[out++] = '\n'; + result[out] = tob64((buffer[in] >> 2) & '\x3f'); + if (remain == 1) { + result[out+1] = tob64((buffer[in] << 4) & '\x30'); + result[out+2] = '='; + } else { + result[out+1] = tob64(((buffer[in] << 4) & '\x30') | ((buffer[in+1] >> 4) & '\x0f')); + result[out+2] = tob64((buffer[in+1] << 2) & '\x3c'); + } + result[out+3] = '='; + out += 4; + } + result[out] = 0; + return result; +} + +char *base64enc(const char *buffer, int length) +{ + return base64encw(buffer, length, 76); +} + +static char fromb64(char x) +{ + if ('A' <= x && x <= 'Z') + return x - 'A'; + if ('a' <= x && x <= 'z') + return x - 'a' + 26; + if ('0' <= x && x <= '9') + return x - '0' + 52; + if (x == '+') + return 62; + if (x == '/') + return 63; + if (x == '=') + return '@'; + return 'E'; +} + +int base64dec(const char *buffer, char **output) +{ + int len, in, out; + char *result; + unsigned char x0, x1, x2, x3; + + len = strlen(buffer); + result = malloc(3 * ((3 + len) / 4)); + if (result == NULL) { + syslog(LOG_ERR, "malloc failed in base64dec"); + return -1; + } + in = out = 0; + while (buffer[in] == '\r' || buffer[in] == '\n') + in++; + while (buffer[in]) { + if (in + 4 > len) { + syslog(LOG_ERR, "unexpected input size in base64dec"); + free(result); + return -1; + } + x0 = (unsigned char)fromb64(buffer[in]); + x1 = (unsigned char)fromb64(buffer[in+1]); + x2 = (unsigned char)fromb64(buffer[in+2]); + x3 = (unsigned char)fromb64(buffer[in+3]); + in += 4; + if (x0 == 'E' || x1 == 'E' || x2 == 'E' || x3 == 'E') { + syslog(LOG_ERR, "unexpected input character in base64dec"); + free(result); + return -1; + } + if (x0 == '@' || x1 == '@' || (x2 == '@' && x3 != '@')) { + syslog(LOG_ERR, "unexpected termination character in base64dec"); + free(result); + return -1; + } + result[out] = (char)((x0 << 2) | (x1 >> 4)); + result[out+1] = (char)((x1 << 4) | ((x2 >> 2) & 15)); + result[out+2] = (char)((x2 << 6) | (x3 & 63)); + while (buffer[in] == '\r' || buffer[in] == '\n') + in++; + if (x3 != '@') + out += 3; + else if (!buffer[in]) + out += 1 + (x2 != '@'); + else { + syslog(LOG_ERR, "unexpected continuation in base64dec"); + free(result); + return -1; + } + } + *output = result; + return out; +} + +int base64eq(const char *buf1, const char *buf2) +{ + for(;;) { + while(*buf1 == '\n' || *buf1 == '\r') + buf1++; + while(*buf2 == '\n' || *buf2 == '\r') + buf2++; + if (*buf1 != *buf2) + return 0; + if (!*buf1) + return 1; + buf1++; + buf2++; + } +} + +#ifdef TESTBASE64 +#include +#include + +int main(int ac, char **av) +{ + char buffer[32768]; + int fd, l0, l1, l2; + char *p1, *p2; + + while(*++av) { + fd = open(*av, O_RDONLY); + if (fd < 0) continue; + l0 = read(fd, buffer, sizeof buffer); + if (l0 <= 0) continue; + close(fd); + p1 = base64enc(buffer, l0); + if (!p1) continue; + l1 = strlen(p1); + l2 = base64dec(p1, &p2); + if (l2 <= 0) continue; +printf("[[[%.*s]]]\n",l2,p2); + if (l0 != l2) printf("length mismatch\n"); + else if (memcmp(buffer, p2, l0)) printf("content mismatch\n"); + free(p1); + free(p2); + } + return 0; +} + +#endif diff --git a/src/wgtpkg-certs.c b/src/wgtpkg-certs.c new file mode 100644 index 0000000..1d8b976 --- /dev/null +++ b/src/wgtpkg-certs.c @@ -0,0 +1,80 @@ +/* + 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 +#include + +#include "wgtpkg.h" + +struct x509l { + int count; + X509 **certs; +}; + +static struct x509l certificates = { .count = 0, .certs = NULL }; + +static int add_certificate_x509(X509 *x) +{ + X509 **p = realloc(certificates.certs, (certificates.count + 1) * sizeof(X509*)); + if (!p) { + syslog(LOG_ERR, "reallocation failed for certificate"); + return -1; + } + certificates.certs = p; + p[certificates.count++] = x; + return 0; +} + +static int add_certificate_bin(const char *bin, int len) +{ + int rc; + const char *b, *e; + b = bin; + e = bin + len; + while (len) { + X509 *x = d2i_X509(NULL, (const unsigned char **)&b, e-b); + if (x == NULL) { + syslog(LOG_ERR, "d2i_X509 failed"); + return -1; + } + rc = add_certificate_x509(x); + if (rc) { + X509_free(x); + return rc; + } + } + return 0; +} + +int add_certificate_b64(const char *b64) +{ + char *d; + int l = base64dec(b64, &d); + if (l > 0) { + l = add_certificate_bin(d, l); + free(d); + } + return l; +} + +void clear_certificates() +{ + while(certificates.count) + X509_free(certificates.certs[--certificates.count]); +} + + diff --git a/src/wgtpkg-digsig.c b/src/wgtpkg-digsig.c new file mode 100644 index 0000000..284acd1 --- /dev/null +++ b/src/wgtpkg-digsig.c @@ -0,0 +1,358 @@ +/* + 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 +#include +#include + +#include +#include +#include + + +#include "wgtpkg.h" + + + +static const char uri_role_author[] = "http://www.w3.org/ns/widgets-digsig#role-author"; +static const char uri_role_distributor[] = "http://www.w3.org/ns/widgets-digsig#role-distributor"; +static const char uri_profile[] = "http://www.w3.org/ns/widgets-digsig#profile"; + + +/* global data */ +static xmlDocPtr document; /* the document */ + +/* facility to get the first element node (skip text nodes) starting with 'node' */ +static xmlNodePtr next_element(xmlNodePtr node) +{ + while (node && node->type != XML_ELEMENT_NODE) + node = node->next; + return node; +} + +/* is the 'node' an element node of 'name'? */ +static int is_element(xmlNodePtr node, const char *name) +{ + return node->type == XML_ELEMENT_NODE + && !strcmp(name, node->name); +} + +/* is the 'node' an element node of 'name'? */ +static int is_node(xmlNodePtr node, const char *name) +{ + return node != NULL && is_element(node, name); +} + +#if 0 +/* facility to get the first element node (skip text nodes) starting with 'node' */ +static xmlNodePtr next_element_type(xmlNodePtr node, const char *name) +{ + while (node && node->type != XML_ELEMENT_NODE && strcmp(name, node->name)) + node = node->next; + return node; +} + +/* search the element node of id. NOTE : not optimized at all */ +static xmlNodePtr search_for(const char *attrname, const char *value) +{ + char *val; + xmlNodePtr iter, next; + xmlNodePtr result; + + result = NULL; + iter = xmlDocGetRootElement(document); + while (iter != NULL) { + val = xmlGetProp(iter, attrname); + if (val != NULL && !strcmp(val, value)) { + if (result != NULL) { + syslog(LOG_ERR, "duplicated %s %s", attrname, value); + free(val); + return NULL; + } + result = iter; + } + free(val); + next = next_element(iter->children); + if (next == NULL) { + /* no child, try sibling */ + next = next_element(iter->next); + if (next == NULL) { + iter = iter->parent; + while (iter != NULL && next == NULL) { + next = next_element(iter->next); + iter = iter->parent; + } + } + } + iter = next; + } + if (result == NULL) + syslog(LOG_ERR, "node of %s '%s' not found", attrname, value); + return result; +} + +/* search the element node of id. NOTE : not optimized at all */ +static xmlNodePtr search_id(const char *id) +{ + return search_for("Id", id); +} +#endif + +/* check the digest of one element */ +static int check_one_reference(xmlNodePtr ref) +{ + int rc; + char *uri; + xmlURIPtr u; + struct filedesc *fdesc; + + /* start */ + rc = -1; + + /* get the uri */ + uri = xmlGetProp(ref, "URI"); + if (uri == NULL) { + syslog(LOG_ERR, "attribute URI of element not found"); + goto error; + } + + /* parse the uri */ + u = xmlParseURI(uri); + if (!u) { + syslog(LOG_ERR, "error while parsing URI %s", uri); + goto error2; + } + + /* check that unexpected parts are not there */ + if (u->scheme || u->opaque || u->authority || u->server || u->user || u->query) { + syslog(LOG_ERR, "unexpected uri component in %s", uri); + goto error3; + } + + /* check path and fragment */ + if (!u->path && !u->fragment) { + syslog(LOG_ERR, "invalid uri %s", uri); + goto error3; + } + if (u->path && u->fragment) { + syslog(LOG_ERR, "not allowed to sign foreign fragment in %s", uri); + goto error3; + } + + if (u->path) { + /* check that the path is valid */ + fdesc = file_of_name(u->path); + if (fdesc == NULL) { + syslog(LOG_ERR, "reference to unknown file %s", u->path); + goto error3; + } + if (fdesc->type != type_file) { + syslog(LOG_ERR, "reference to directory %s", u->path); + goto error3; + } + if ((fdesc->flags & flag_distributor_signature) != 0) { + syslog(LOG_ERR, "reference to signature %s", u->path); + goto error3; + } + fdesc->flags |= flag_referenced; + rc = 0; + } else { + rc = 0; + } + +error3: + xmlFreeURI(u); +error2: + xmlFree(uri); +error: + return rc; +} + +static int check_references(xmlNodePtr sinfo) +{ + xmlNodePtr elem; + + elem = sinfo->children; + while (elem != NULL) { + if (is_element(elem, "Reference")) + if (check_one_reference(elem)) + return -1; + elem = elem->next; + } + return 0; +} + +static int get_certificates(xmlNodePtr kinfo) +{ + xmlNodePtr n1, n2; + char *b; + int rc; + + n1 = kinfo->children; + while (n1) { + if (is_element(n1, "X509Data")) { + n2 = n1->children; + while (n2) { + if (is_element(n2, "X509Certificate")) { + b = xmlNodeGetContent(n2); + if (b == NULL) { + syslog(LOG_ERR, "xmlNodeGetContent of X509Certificate failed"); + return -1; + } + rc = add_certificate_b64(b); + xmlFree(b); + if (rc) + return rc; + } + n2 = n2->next; + } + } + n1 = n1->next; + } + return 0; +} + +/* checks the current document */ +static int checkdocument() +{ + int rc; + xmlNodePtr sinfo, svalue, kinfo, objs, rootsig; + + rc = -1; + + rootsig = xmlDocGetRootElement(document); + if (!is_node(rootsig, "Signature")) { + syslog(LOG_ERR, "root element not found"); + goto error; + } + + sinfo = next_element(rootsig->children); + if (!is_node(sinfo, "SignedInfo")) { + syslog(LOG_ERR, "element not found"); + goto error; + } + + svalue = next_element(sinfo->next); + if (!is_node(svalue, "SignatureValue")) { + syslog(LOG_ERR, "element not found"); + goto error; + } + + kinfo = next_element(svalue->next); + if (is_node(kinfo, "KeyInfo")) { + objs = kinfo->next; + } else { + objs = kinfo; + kinfo = NULL; + } + + rc = check_references(sinfo); + if (rc) + goto error; + + rc = xmlsec_verify(rootsig); + if (rc) + goto error; + + rc = get_certificates(kinfo); + +error: + return rc; +} + +/* verify the digital signature of the file described by 'fdesc' */ +int verify_digsig(struct filedesc *fdesc) +{ + int res; + + assert ((fdesc->flags & flag_signature) != 0); + debug("-- checking file %s",fdesc->name); + + /* reset the flags */ + file_clear_flags(); + clear_certificates(); + + /* reads and xml parses the signature file */ + document = xmlReadFile(fdesc->name, NULL, 0); + if (document == NULL) { + syslog(LOG_ERR, "xml parse of file %s failed", fdesc->name); + return -1; + } + + res = checkdocument(); + if (res) + syslog(LOG_ERR, "previous error was during check of file %s", fdesc->name); + + xmlFreeDoc(document); + return res; +} + +/* check all the signature files */ +int check_all_signatures() +{ + int rc, irc; + unsigned int i, n; + struct filedesc *fdesc; + + n = signature_count(); + rc = 0; + for (i = n ; i-- > 0 ; ) { + fdesc = signature_of_index(i); + irc = verify_digsig(fdesc); + if (!irc) + rc = irc; + } + + return rc; +} + +/* create a signature of 'index' (0 for author, other values for distributors) +using the private 'key' (filename) and the certificates 'certs' (filenames) +as trusted chain */ +int create_digsig(int index, const char *key, const char **certs) +{ + struct filedesc *fdesc; + xmlDocPtr doc; + int rc, len; + + rc = -1; + + /* create the doc */ + doc = xmlsec_create(index, key, certs); + if (doc == NULL) + goto error; + + /* instanciate the filename */ + fdesc = create_signature(index); + if (fdesc == NULL) + goto error2; + + /* save the doc as file */ + len = xmlSaveFormatFileEnc(fdesc->name, doc, NULL, 0); + if (len < 0) { + syslog(LOG_ERR, "xmlSaveFormatFileEnc to %s failed", fdesc->name); + goto error2; + } + + rc = 0; +error2: + xmlFreeDoc(doc); +error: + return rc; +} + + diff --git a/src/wgtpkg-files.c b/src/wgtpkg-files.c new file mode 100644 index 0000000..06aac83 --- /dev/null +++ b/src/wgtpkg-files.c @@ -0,0 +1,313 @@ +/* + 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. +*/ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include + +#include "wgtpkg.h" + +struct fdb { + unsigned int count; + struct filedesc **files; +}; + +static struct fdb allfiles = { .count = 0, .files = NULL }; +static struct fdb allsignatures = { .count = 0, .files = NULL }; + +static const char author_file[] = "author-signature.xml"; +static const char distributor_file_prefix[] = "signature"; +static const char distributor_file_suffix[] = ".xml"; + +static unsigned int what_signature(const char *name) +{ + unsigned int len, id, nid; + + if (!strcmp(name, author_file)) + return UINT_MAX; + + len = sizeof(distributor_file_prefix)-1; + if (memcmp(name, distributor_file_prefix, len)) + return 0; + if (name[len] <= '0' || name[len] > '9') + return 0; + id = (unsigned int)(name[len++] - '0'); + while ('0' <= name[len] && name[len] <= '9') { + nid = 10 * id + (unsigned int)(name[len++] - '0'); + if (nid < id || nid == UINT_MAX) { + syslog(LOG_WARNING, "number too big for %s", name); + return 0; + } + id = nid; + } + if (strcmp(name+len, distributor_file_suffix)) + return 0; + + return id; +} + +static struct filedesc *get_filedesc(const char *name, int create) +{ + int cmp; + unsigned int low, up, mid, sig; + struct filedesc *result, **grow; + + /* search */ + low = 0; + up = allfiles.count; + while(low < up) { + mid = (low + up) >> 1; + result = allfiles.files[mid]; + cmp = strcmp(result->name, name); + if (!cmp) + return result; /* found */ + if (cmp > 0) + up = mid; + else + low = mid + 1; + } + + /* not found, can create ? */ + if (!create) + return NULL; + + sig = what_signature(name); + + /* allocations */ + grow = realloc(allfiles.files, (allfiles.count + 1) * sizeof(struct filedesc *)); + if (grow == NULL) { + syslog(LOG_ERR, "realloc failed in get_filedesc"); + return NULL; + } + allfiles.files = grow; + + if (sig) { + grow = realloc(allsignatures.files, (allsignatures.count + 1) * sizeof(struct filedesc *)); + if (grow == NULL) { + syslog(LOG_ERR, "second realloc failed in get_filedesc"); + return NULL; + } + allsignatures.files = grow; + } + + result = malloc(sizeof(struct filedesc) + strlen(name)); + if (!result) { + syslog(LOG_ERR, "calloc failed in get_filedesc"); + return NULL; + } + + /* initialisation */ + result->type = type_unset; + result->flags = sig == 0 ? 0 : sig == UINT_MAX ? flag_author_signature : flag_distributor_signature; + result->zindex = 0; + result->signum = sig; + strcpy(result->name, name); + + /* insertion */ + if (low < allfiles.count) + memmove(allfiles.files+low+1, allfiles.files+low, (allfiles.count - low) * sizeof(struct filedesc *)); + allfiles.files[low] = result; + allfiles.count++; + if (sig) { + for (low = 0 ; low < allsignatures.count && sig > allsignatures.files[low]->signum ; low++); + if (low < allsignatures.count) + memmove(allsignatures.files+low+1, allsignatures.files+low, (allsignatures.count - low) * sizeof(struct filedesc *)); + allsignatures.files[low] = result; + allsignatures.count++; + } + + return result; +} + + +static struct filedesc *file_add(const char *name, enum entrytype type) +{ + struct filedesc *desc; + + desc = get_filedesc(name, 1); + if (!desc) + errno = ENOMEM; + else if (desc->type == type_unset) + desc->type = type; + else { + syslog(LOG_ERR, "redeclaration of %s in file_add", name); + errno = EEXIST; + desc = NULL; + } + return desc; +} + +void file_reset() +{ + unsigned int i; + + allsignatures.count = 0; + for (i = 0 ; i < allfiles.count ; i++) + free(allfiles.files[i]); + allfiles.count = 0; +} + +unsigned int file_count() +{ + return allfiles.count; +} + +struct filedesc *file_of_index(unsigned int index) +{ + assert(index < allfiles.count); + return allfiles.files[index]; +} + +struct filedesc *file_of_name(const char *name) +{ + return get_filedesc(name, 0); +} + +struct filedesc *file_add_directory(const char *name) +{ + return file_add(name, type_directory); +} + +struct filedesc *file_add_file(const char *name) +{ + return file_add(name, type_file); +} + +unsigned int signature_count() +{ + return allsignatures.count; +} + +struct filedesc *signature_of_index(unsigned int index) +{ + assert(index < allsignatures.count); + return allsignatures.files[index]; +} + +struct filedesc *get_signature(unsigned int number) +{ + unsigned int idx; + + if (number == 0) + number = UINT_MAX; + for (idx = 0 ; idx < allsignatures.count ; idx++) + if (allsignatures.files[idx]->signum == number) + return allsignatures.files[idx]; + return NULL; +} + +struct filedesc *create_signature(unsigned int number) +{ + struct filedesc *result; + char *name; + int len; + + result = NULL; + if (number == 0 || number == UINT_MAX) + len = asprintf(&name, "%s", author_file); + else + len = asprintf(&name, "%s%u%s", distributor_file_prefix, number, distributor_file_suffix); + + if (len < 0) + syslog(LOG_ERR, "asprintf failed in create_signature"); + else { + assert(len > 0); + result = file_of_name(name); + if (result == NULL) + result = file_add_file(name); + free(name); + } + + return result; +} + +/* remove flags that are not related to being signature */ +void file_clear_flags() +{ + unsigned int i; + for (i = 0 ; i < allfiles.count ; i++) + allfiles.files[i]->flags &= flag_signature; +} + +static int fill_files_rec(char name[PATH_MAX], int offset) +{ + int len, err; + DIR *dir; + struct dirent *ent; + + if (offset == 0) + dir = opendir("."); + else { + dir = opendir(name); + name[offset++] = '/'; + } + if (!dir) { + syslog(LOG_ERR, "opendir %.*s failed in zwr", offset, name); + return -1; + } + + ent = readdir(dir); + while (ent != NULL) { + len = strlen(ent->d_name); + if (ent->d_name[0] == '.' && (len == 1 || + (ent->d_name[1] == '.' && len == 2))) + ; + else if (offset + len >= PATH_MAX) { + closedir(dir); + syslog(LOG_ERR, "name too long in fill_files_rec"); + errno = ENAMETOOLONG; + return -1; + } else { + memcpy(name + offset, ent->d_name, 1+len); + switch (ent->d_type) { + case DT_DIR: + if (file_add_directory(name) == NULL) { + closedir(dir); + return -1; + } + err = fill_files_rec(name, offset + len); + if (err) { + closedir(dir); + return err; + } + break; + case DT_REG: + if (file_add_file(name) == NULL) { + closedir(dir); + return -1; + } + break; + default: + break; + } + } + ent = readdir(dir); + } + + closedir(dir); + return 0; +} + +int fill_files() +{ + char name[PATH_MAX]; + return fill_files_rec(name, 0); +} + diff --git a/src/wgtpkg-install.c b/src/wgtpkg-install.c new file mode 100644 index 0000000..7a88ebf --- /dev/null +++ b/src/wgtpkg-install.c @@ -0,0 +1,84 @@ +/* + 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. +*/ + +#define _BSD_SOURCE /* see readdir */ + +#include +#include +#include +#include +#include +#include +#include + +#include "wgtpkg.h" + +/* install the widget of the file */ +static void install(const char *wgtfile) +{ + notice("-- INSTALLING widget %s", wgtfile); + + if (enter_workdir(1)) + goto error; + + if (zread(wgtfile, 0)) + goto error; + + if (check_all_signatures()) + goto error; + + return; + +error: + return; + exit(1); +} + +/* install the widgets of the list */ +int main(int ac, char **av) +{ + int i, kwd; + + openlog("wgtpkg-install", LOG_PERROR, LOG_AUTH); + + xmlsec_init(); + + ac = verbose_scan_args(ac, av); + + /* canonic names for files */ + for (i = 1 ; av[i] != NULL ; i++) + if ((av[i] = realpath(av[i], NULL)) == NULL) { + syslog(LOG_ERR, "error while getting realpath of %dth argument", i); + return 1; + } + + /* workdir */ + kwd = 1; + if (make_workdir(kwd)) { + syslog(LOG_ERR, "failed to create a working directory"); + return 1; + } + if (!kwd) + atexit(remove_workdir); + + /* install widgets */ + for (av++ ; *av ; av++) + install(*av); + + exit(0); + return 0; +} + diff --git a/src/wgtpkg-pack.c b/src/wgtpkg-pack.c new file mode 100644 index 0000000..a8aaa05 --- /dev/null +++ b/src/wgtpkg-pack.c @@ -0,0 +1,167 @@ +/* + 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. +*/ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "wgtpkg.h" + +#if !defined(MAXCERT) +#define MAXCERT 20 +#endif +#if !defined(DEFAULT_KEY_FILE) +#define DEFAULT_KEY_FILE "key.pem" +#endif +#if !defined(DEFAULT_CERT_FILE) +#define DEFAULT_CERT_FILE "cert.pem" +#endif + +const char appname[] = "wgtpkg-pack"; + +static void usage() +{ + printf( + "usage: %s [-f] [-o wgtfile] directory\n" + "\n" + " -o wgtfile the output widget file\n" + " -f force overwriting\n" + " -q quiet\n" + " -v verbose\n" + "\n", + appname + ); +} + +static struct option options[] = { + { "output", required_argument, NULL, 'o' }, + { "force", no_argument, NULL, 'f' }, + { "help", no_argument, NULL, 'h' }, + { "quiet", no_argument, NULL, 'q' }, + { "verbose", no_argument, NULL, 'v' }, + { NULL, 0, NULL, 0 } +}; + +/* install the widgets of the list */ +int main(int ac, char **av) +{ + int i, force; + char *wgtfile, *directory, *x; + struct stat s; + + openlog(appname, LOG_PERROR, LOG_USER); + + force = 0; + wgtfile = directory = NULL; + for (;;) { + i = getopt_long(ac, av, "qvhfo:", options, NULL); + if (i < 0) + break; + switch (i) { + case 'o': + wgtfile = optarg; + break; + case 'q': + if (verbosity) + verbosity--; + break; + case 'v': + verbosity++; + break; + case 'f': + force = 1; + break; + case 'h': + usage(); + return 0; + case ':': + syslog(LOG_ERR, "missing argument"); + return 1; + default: + syslog(LOG_ERR, "unrecognized option"); + return 1; + } + } + + /* remaining arguments and final checks */ + if (optind >= ac) { + syslog(LOG_ERR, "no directory set"); + return 1; + } + directory = av[optind++]; + if (optind < ac) { + syslog(LOG_ERR, "extra parameters found"); + return 1; + } + + /* set default values */ + if (wgtfile == NULL && 0 > asprintf(&wgtfile, "%s.wgt", directory)) { + syslog(LOG_ERR, "asprintf failed"); + return 1; + } + + /* check values */ + if (stat(directory, &s)) { + syslog(LOG_ERR, "can't find directory %s", directory); + return 1; + } + if (!S_ISDIR(s.st_mode)) { + syslog(LOG_ERR, "%s isn't a directory", directory); + return 1; + } + if (access(wgtfile, F_OK) == 0 && force == 0) { + syslog(LOG_ERR, "can't overwrite existing %s", wgtfile); + return 1; + } + + notice("-- PACKING widget %s from directory %s", wgtfile, directory); + + /* creates an existing widget (for realpath it must exist) */ + i = open(wgtfile, O_WRONLY|O_CREAT|O_NOCTTY|O_NONBLOCK, 0666); + if (i < 0) { + syslog(LOG_ERR, "can't write widget %s", wgtfile); + return 1; + } + close(i); + + /* compute absolutes paths */ + x = realpath(wgtfile, NULL); + if (x == NULL) { + syslog(LOG_ERR, "realpath failed for %s",wgtfile); + return 1; + } + wgtfile = x; + + /* set and enter the workdir */ + if (set_workdir(directory, 0) || enter_workdir(0)) + return 1; + + + if (fill_files()) + return 1; + + return !!zwrite(wgtfile); +} + + diff --git a/src/wgtpkg-sign.c b/src/wgtpkg-sign.c new file mode 100644 index 0000000..cd506fc --- /dev/null +++ b/src/wgtpkg-sign.c @@ -0,0 +1,199 @@ +/* + 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. +*/ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "wgtpkg.h" + +#if !defined(MAXCERT) +#define MAXCERT 20 +#endif +#if !defined(DEFAULT_KEY_FILE) +#define DEFAULT_KEY_FILE "key.pem" +#endif +#if !defined(DEFAULT_CERT_FILE) +#define DEFAULT_CERT_FILE "cert.pem" +#endif + +const char appname[] = "wgtpkg-sign"; + +static unsigned int get_number(const char *value) +{ + char *end; + unsigned long int val; + + val = strtoul(value, &end, 10); + if (*end || 0 == val || val >= UINT_MAX || *value == '-') { + syslog(LOG_ERR, "bad number value %s", value); + exit(1); + } + return (unsigned int)val; +} + +static void usage() +{ + printf( + "usage: %s [-f] [-k keyfile] [-c certfile]... [-o wgtfile] [-d number | -a] directory\n" + "\n" + " -k keyfile the private key to use for author signing\n" + " -c certfile the certificate(s) to use for author signing\n" + " -d number the number of the distributor signature (zero for automatic)\n" + " -a the author signature\n" + " -f force overwriting\n" + " -q quiet\n" + " -v verbose\n" + "\n", + appname + ); +} + +static struct option options[] = { + { "key", required_argument, NULL, 'k' }, + { "certificate", required_argument, NULL, 'c' }, + { "distributor", required_argument, NULL, 'd' }, + { "author", no_argument, NULL, 'a' }, + { "force", no_argument, NULL, 'f' }, + { "help", no_argument, NULL, 'h' }, + { "quiet", no_argument, NULL, 'q' }, + { "verbose", no_argument, NULL, 'v' }, + { NULL, 0, NULL, 0 } +}; + +/* install the widgets of the list */ +int main(int ac, char **av) +{ + int i, force, ncert, author; + unsigned int number; + char *keyfile, *certfiles[MAXCERT+1], *directory, **x; + struct stat s; + + openlog(appname, LOG_PERROR, LOG_USER); + + force = ncert = author = 0; + number = UINT_MAX; + keyfile = directory = NULL; + for (;;) { + i = getopt_long(ac, av, "hfak:c:d:", options, NULL); + if (i < 0) + break; + switch (i) { + case 'c': + if (ncert == MAXCERT) { + syslog(LOG_ERR, "maximum count of certificates reached"); + return 1; + } + certfiles[ncert++] = optarg; + continue; + case 'k': x = &keyfile; break; + case 'd': number = get_number(optarg); continue; + case 'f': force = 1; continue; + case 'a': author = 1; continue; + case 'h': usage(); return 0; + case ':': + syslog(LOG_ERR, "missing argument"); + return 1; + default: + syslog(LOG_ERR, "unrecognized option"); + return 1; + } + if (*x != NULL) { + syslog(LOG_ERR, "option set twice"); + return 1; + } + *x = optarg; + } + + /* remaining arguments and final checks */ + if (optind >= ac) { + syslog(LOG_ERR, "no directory set"); + return 1; + } + directory = av[optind++]; + if (optind < ac) { + syslog(LOG_ERR, "extra parameters found"); + return 1; + } + + /* set default values */ + if (keyfile == NULL) + keyfile = DEFAULT_KEY_FILE; + if (ncert == 0) + certfiles[ncert++] = DEFAULT_CERT_FILE; + + /* check values */ + if (stat(directory, &s)) { + syslog(LOG_ERR, "can't find directory %s", directory); + return 1; + } + if (!S_ISDIR(s.st_mode)) { + syslog(LOG_ERR, "%s isn't a directory", directory); + return 1; + } + if (access(keyfile, R_OK) != 0) { + syslog(LOG_ERR, "can't access private key %s", keyfile); + return 1; + } + for(i = 0 ; i < ncert ; i++) + if (access(certfiles[i], R_OK) != 0) { + syslog(LOG_ERR, "can't access certificate %s", certfiles[i]); + return 1; + } + + /* init xmlsec module */ + if (xmlsec_init()) + return 1; + + + /* compute absolutes paths */ +#define rp(x) do { char *p = realpath(x, NULL); if (p != NULL) x = p; else { syslog(LOG_ERR, "realpath failed for %s",x); return 1; } } while(0) + rp(keyfile); + for(i = 0 ; i < ncert ; i++) + rp(certfiles[i]); +#undef rp + + /* set and enter the workdir */ + if (set_workdir(directory, 0) || enter_workdir(0)) + return 1; + + if (fill_files()) + return 1; + + if (author) + number = 0; + else if (number == UINT_MAX) + for (number = 1; get_signature(number) != NULL ; number++); + + if (!force && get_signature(number) != NULL) { + syslog(LOG_ERR, "can't overwrite existing signature %s", get_signature(number)->name); + return 1; + } + + notice("-- SIGNING content of directory %s for number %u", directory, number); + + certfiles[ncert] = NULL; + return !!create_digsig(number, keyfile, (const char**)certfiles); +} + diff --git a/src/wgtpkg-verbose.c b/src/wgtpkg-verbose.c new file mode 100644 index 0000000..1472a90 --- /dev/null +++ b/src/wgtpkg-verbose.c @@ -0,0 +1,36 @@ +/* + 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 + +int verbosity = 1; + +int verbose_scan_args(int argc, char **argv) +{ + int i, r; + for (i=r=0 ; i < argc ; i++) { + if (!strcmp(argv[i], "-q")) + verbosity = verbosity ? verbosity-1 : 0; + else if (!strcmp(argv[i], "-v")) + verbosity++; + else + argv[r++] = argv[i]; + } + argv[r] = NULL; + return r; +} + + diff --git a/src/wgtpkg-workdir.c b/src/wgtpkg-workdir.c new file mode 100644 index 0000000..0c184a5 --- /dev/null +++ b/src/wgtpkg-workdir.c @@ -0,0 +1,166 @@ +/* + 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 +#include +#include +#include +#include +#include + +#include "wgtpkg.h" + +#ifndef PREDIR +#define PREDIR "./" +#endif + +static int mode = 0700; +static char workdir[PATH_MAX]; + +/* removes recursively the content of a directory */ +static int clean_dir() +{ + int cr; + DIR *dir; + struct dirent *ent; + struct { + struct dirent entry; + char spare[PATH_MAX]; + } entry; + + dir = opendir("."); + if (dir == NULL) { + syslog(LOG_ERR, "opendir failed in clean_dir"); + return -1; + } + + cr = -1; + for (;;) { + if (readdir_r(dir, &entry.entry, &ent) != 0) { + syslog(LOG_ERR, "readdir_r failed in clean_dir"); + goto error; + } + if (ent == NULL) + break; + if (ent->d_name[0] == '.' && (ent->d_name[1] == 0 + || (ent->d_name[1] == '.' && ent->d_name[2] == 0))) + continue; + cr = unlink(ent->d_name); + if (!cr) + continue; + if (errno != EISDIR) { + syslog(LOG_ERR, "unlink of %s failed in clean_dir", ent->d_name); + goto error; + } + if (chdir(ent->d_name)) { + syslog(LOG_ERR, "enter directory %s failed in clean_dir", ent->d_name); + goto error; + } + cr = clean_dir(); + if (cr) + goto error; + if (chdir("..")) + goto error; + cr = rmdir(ent->d_name); + if (cr) { + syslog(LOG_ERR, "rmdir of %s failed in clean_dir", ent->d_name); + goto error; + } + } + cr = 0; +error: + closedir(dir); + return cr; +} + +/* removes the content of the working directory */ +int enter_workdir(int clean) +{ + int rc = chdir(workdir); + if (rc) + syslog(LOG_ERR, "entring workdir %s failed", workdir); + else if (clean) + rc = clean_dir(); + return rc; +} + +/* removes the working directory */ +void remove_workdir() +{ + enter_workdir(1); + chdir(".."); + unlink(workdir); +} + +int set_workdir(const char *name, int create) +{ + int rc; + size_t length; + struct stat s; + + /* check the length */ + length = strlen(name); + if (length >= sizeof workdir) { + syslog(LOG_ERR, "workdir name too long"); + return -1; + } + + rc = stat(name, &s); + if (rc) { + if (!create) { + syslog(LOG_ERR, "no workdir %s", name); + return -1; + } + rc = mkdir(name, mode); + if (rc) { + syslog(LOG_ERR, "can't create workdir %s", name); + return -1; + } + + } else if (!S_ISDIR(s.st_mode)) { + syslog(LOG_ERR, "%s isn't a directory", name); + return -1; + } + memcpy(workdir, name, 1+length); + return 0; +} + +/* install the widgets of the list */ +int make_workdir(int reuse) +{ + int i; + + /* create a temporary directory */ + for (i = 0 ; ; i++) { + if (i == INT_MAX) { + syslog(LOG_ERR, "exhaustion of workdirs"); + return -1; + } + sprintf(workdir, PREDIR "PACK%d", i); + if (!mkdir(workdir, mode)) + break; + if (errno != EEXIST) { + syslog(LOG_ERR, "error in creation of workdir %s: %m", workdir); + return -1; + } + if (reuse) + break; + } + + return 0; +} + diff --git a/src/wgtpkg-xmlsec.c b/src/wgtpkg-xmlsec.c new file mode 100644 index 0000000..843ea2b --- /dev/null +++ b/src/wgtpkg-xmlsec.c @@ -0,0 +1,371 @@ +/* + 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 +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + + +#include "wgtpkg.h" + +static int initstatus; +static int initdone; +static xmlSecKeysMngrPtr keymgr; + +#ifndef CA_ROOT_DIRECTORY +#define CA_ROOT_DIRECTORY "./ca-certificates" +#endif + +/* checks if a file match uri (should not be a distributor signature) */ +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; +} + +/* open the file of uri */ +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; +} + +/* read the opened file */ +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; +} + +/* close the opened file */ +static int file_close_cb(void *context) +{ + return (int)fclose((FILE*)context); +} + +/* echo an error message */ +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 : "?"); +} + +/* fills database with trusted keys */ +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; + +} + +/* initialisation of access to xmlsec */ +int xmlsec_init() +{ + + if (initdone) + goto end; + + initdone = 1; + initstatus = -1; + + if(xmlSecInit() < 0) { + syslog(LOG_ERR, "xmlSecInit failed."); + goto end; + } + +#ifdef XMLSEC_CRYPTO_DYNAMIC_LOADING + if(xmlSecCryptoDLLoadLibrary(XMLSEC_CRYPTO) < 0) { + syslog(LOG_ERR, "xmlSecCryptoDLLoadLibrary %s failed.", XMLSEC_CRYPTO); + goto end; + } +#endif + + 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; +} + +/* shuting down accesses to xmlsec */ +void xmlsec_shutdown() +{ + xmlSecKeysMngrDestroy(keymgr); + + xmlSecCryptoShutdown(); + + xmlSecCryptoAppShutdown(); + + xmlSecShutdown(); +} + +/* verify a signature */ +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; +} + +/* templates for properties of signature files */ +static const struct { const char *id; const char *xml; } properties[2] = { + { + .id = "AuthorSignature", /* template of properties for author signature */ + .xml = + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + }, + { + .id = "DistributorSignature", /* template of properties for distributor signature */ + .xml = + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + } +}; + +/* create a signature of 'index' (0 for author, other values for distributors) +using the private 'key' (filename) and the certificates 'certs' (filenames) +as trusted chain */ +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; +} + diff --git a/src/wgtpkg-zip.c b/src/wgtpkg-zip.c new file mode 100644 index 0000000..9e51538 --- /dev/null +++ b/src/wgtpkg-zip.c @@ -0,0 +1,342 @@ +/* + 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. +*/ + +#define _BSD_SOURCE /* see readdir */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "wgtpkg.h" + + +#if !defined(MODE_OF_FILE_CREATION) +#define MODE_OF_FILE_CREATION 0640 +#endif +#if !defined(MODE_OF_DIRECTORY_CREATION) +#define MODE_OF_DIRECTORY_CREATION 0750 +#endif + +static int is_valid_filename(const char *filename) +{ + int lastsp = 0; + int index = 0; + unsigned char c; + + c = (unsigned char)filename[index]; + while (c) { + if ((c < 0x1f) + || ((lastsp = (c == 0x20)) && index == 0) + || c == 0x7f || c == 0x3c || c == 0x3e + || c == 0x3a || c == 0x22 + || c == 0x5c || c == 0x7c || c == 0x3f + || c == 0x2a || c == 0x5e || c == 0x60 + || c == 0x7b || c == 0x7d || c == 0x21) + return 0; + c = (unsigned char)filename[++index]; + } + return !lastsp; +} + +static int create_directory(char *file, int mode) +{ + int rc; + char *last = strrchr(file, '/'); + if (last != NULL) + *last = 0; + rc = mkdir(file, mode); + if (rc) { + if (errno == EEXIST) + rc = 0; + else if (errno == ENOENT) { + rc = create_directory(file, mode); + if (!rc) + rc = mkdir(file, mode); + } + } + if (rc) + syslog(LOG_ERR, "can't create directory %s", file); + if (last != NULL) + *last = '/'; + return rc; +} + +static int create_file(char *file, int fmode, int dmode) +{ + int fd = creat(file, fmode); + if (fd < 0 && errno == ENOENT) { + if (!create_directory(file, dmode)) + fd = creat(file, fmode); + } + if (fd < 0) + syslog(LOG_ERR, "can't create file %s", file); + return fd; +} + +/* read (extract) 'zipfile' in current directory */ +int zread(const char *zipfile, unsigned long long maxsize) +{ + struct filedesc *fdesc; + int err, fd, len; + struct zip *zip; + zip_int64_t z64; + unsigned int count, index; + struct zip_file *zfile; + struct zip_stat zstat; + char buffer[32768]; + ssize_t sizr, sizw; + size_t esize; + + /* open the zip file */ + zip = zip_open(zipfile, ZIP_CHECKCONS, &err); + if (!zip) { + syslog(LOG_ERR, "Can't connect to file %s", zipfile); + return -1; + } + + z64 = zip_get_num_entries(zip, 0); + if (z64 < 0 || z64 > UINT_MAX) { + syslog(LOG_ERR, "too many entries in %s", zipfile); + goto error; + } + count = (unsigned int)z64; + + /* records the files */ + file_reset(); + esize = 0; + for (index = 0 ; index < count ; index++) { + err = zip_stat_index(zip, index, ZIP_FL_ENC_GUESS, &zstat); + /* check the file name */ + if (!is_valid_filename(zstat.name)) { + syslog(LOG_ERR, "invalid entry %s found in %s", zstat.name, zipfile); + goto error; + } + if (zstat.name[0] == '/') { + syslog(LOG_ERR, "absolute entry %s found in %s", zstat.name, zipfile); + goto error; + } + len = strlen(zstat.name); + if (len == 0) { + syslog(LOG_ERR, "empty entry found in %s", zipfile); + goto error; + } + if (zstat.size == 0) { + /* directory name */ + if (zstat.name[len - 1] != '/') { + syslog(LOG_ERR, "bad directory name %s in %s", zstat.name, zipfile); + goto error; + } + /* record */ + fdesc = file_add_directory(zstat.name); + } else { + /* directory name */ + if (zstat.name[len - 1] == '/') { + syslog(LOG_ERR, "bad file name %s in %s", zstat.name, zipfile); + goto error; + } + /* get the size */ + esize += zstat.size; + /* record */ + fdesc = file_add_file(zstat.name); + } + if (!fdesc) + goto error; + fdesc->zindex = index; + } + + /* check the size */ + if (maxsize && esize > maxsize) { + syslog(LOG_ERR, "extracted size %zu greater than allowed size %llu", esize, maxsize); + goto error; + } + + /* unpack the recorded files */ + assert(count == file_count()); + for (index = 0 ; index < count ; index++) { + fdesc = file_of_index(index); + assert(fdesc != NULL); + err = zip_stat_index(zip, fdesc->zindex, ZIP_FL_ENC_GUESS, &zstat); + assert(zstat.name[0] != '/'); + if (zstat.size == 0) { + /* directory name */ + err = create_directory((char*)zstat.name, MODE_OF_DIRECTORY_CREATION); + if (err && errno != EEXIST) + goto error; + } else { + /* file name */ + zfile = zip_fopen_index(zip, fdesc->zindex, 0); + if (!zfile) { + syslog(LOG_ERR, "Can't open %s in %s", zstat.name, zipfile); + goto error; + } + fd = create_file((char*)zstat.name, MODE_OF_FILE_CREATION, MODE_OF_DIRECTORY_CREATION); + if (fd < 0) + goto errorz; + /* extract */ + z64 = zstat.size; + while (z64) { + sizr = zip_fread(zfile, buffer, sizeof buffer); + if (sizr < 0) { + syslog(LOG_ERR, "error while reading %s in %s", zstat.name, zipfile); + goto errorzf; + } + sizw = write(fd, buffer, sizr); + if (sizw < 0) { + syslog(LOG_ERR, "error while writing %s", zstat.name); + goto errorzf; + } + z64 -= sizw; + } + close(fd); + zip_fclose(zfile); + } + } + + zip_close(zip); + return 0; + +errorzf: + close(fd); +errorz: + zip_fclose(zfile); +error: + zip_close(zip); + return -1; +} + +struct zws { + struct zip *zip; + char name[PATH_MAX]; + char buffer[32768]; +}; + +static int zwr(struct zws *zws, int offset) +{ + int len, err; + DIR *dir; + struct dirent *ent; + zip_int64_t z64; + struct zip_source *zsrc; + + if (offset == 0) + dir = opendir("."); + else { + dir = opendir(zws->name); + zws->name[offset++] = '/'; + } + if (!dir) { + syslog(LOG_ERR, "opendir %.*s failed in zwr", offset, zws->name); + return -1; + } + + ent = readdir(dir); + while (ent != NULL) { + len = strlen(ent->d_name); + if (ent->d_name[0] == '.' && (len == 1 || + (ent->d_name[1] == '.' && len == 2))) + ; + else if (offset + len >= sizeof(zws->name)) { + syslog(LOG_ERR, "name too long in zwr"); + errno = ENAMETOOLONG; + goto error; + } else { + memcpy(zws->name + offset, ent->d_name, 1+len); + if (!is_valid_filename(ent->d_name)) { + syslog(LOG_ERR, "invalid name %s", zws->name); + goto error; + } + switch (ent->d_type) { + case DT_DIR: + z64 = zip_dir_add(zws->zip, zws->name, ZIP_FL_ENC_UTF_8); + if (z64 < 0) { + syslog(LOG_ERR, "zip_dir_add of %s failed", zws->name); + goto error; + } + err = zwr(zws, offset + len); + if (err) + goto error; + break; + case DT_REG: + zsrc = zip_source_file(zws->zip, zws->name, 0, 0); + if (zsrc == NULL) { + syslog(LOG_ERR, "zip_source_file of %s failed", zws->name); + goto error; + } + z64 = zip_file_add(zws->zip, zws->name, zsrc, ZIP_FL_ENC_UTF_8); + if (z64 < 0) { + syslog(LOG_ERR, "zip_file_add of %s failed", zws->name); + zip_source_free(zsrc); + goto error; + } + break; + default: + break; + } + } + ent = readdir(dir); + } + + closedir(dir); + return 0; +error: + closedir(dir); + return -1; +} + +/* write (pack) content of the current directory in 'zipfile' */ +int zwrite(const char *zipfile) +{ + int err; + struct zws zws; + + zws.zip = zip_open(zipfile, ZIP_CREATE|ZIP_TRUNCATE, &err); + if (!zws.zip) { + syslog(LOG_ERR, "Can't open %s for write", zipfile); + return -1; + } + + err = zwr(&zws, 0); + zip_close(zws.zip); + return err; +} + + +#if defined(TEST_READ) +int main(int ac, char **av) +{ + for(av++ ; *av ; av++) + zread(*av); + return 0; +} +#endif + +#if defined(TEST_WRITE) +int main(int ac, char **av) +{ + for(av++ ; *av ; av++) + zwrite(*av); + return 0; +} +#endif + diff --git a/src/wgtpkg.h b/src/wgtpkg.h new file mode 100644 index 0000000..d35b7f4 --- /dev/null +++ b/src/wgtpkg.h @@ -0,0 +1,125 @@ +/* + 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 +#include "config.h" + +struct filedesc; + +/**************************************************************/ +/* from wgtpkg-base64 */ + +extern char *base64encw(const char *buffer, int length, int width); +extern char *base64enc(const char *buffer, int length); +extern int base64dec(const char *buffer, char **output); +extern int base64eq(const char *buf1, const char *buf2); + +/**************************************************************/ +/* from wgtpkg-certs */ + +extern void clear_certificates(); +extern int add_certificate_b64(const char *b64); + +/**************************************************************/ +/* from wgtpkg-digsig */ + +/* verify the digital signature in file */ +extern int verify_digsig(struct filedesc *fdesc); + +/* create a digital signature */ +extern int create_digsig(int index, const char *key, const char **certs); + +/* check the signatures of the current directory */ +extern int check_all_signatures(); + +/**************************************************************/ +/* from wgtpkg-files */ + +enum entrytype { + type_unset = 0, + type_file = 1, + type_directory = 2 +}; + +enum fileflag { + flag_referenced = 1, + flag_opened = 2, + flag_author_signature = 4, + flag_distributor_signature = 8, + flag_signature = 12 +}; + +struct filedesc { + enum entrytype type; + unsigned int flags; + unsigned int signum; + unsigned int zindex; + char name[1]; +}; + +extern void file_reset(); +extern void file_clear_flags(); +extern unsigned int file_count(); +extern struct filedesc *file_of_index(unsigned int index); +extern struct filedesc *file_of_name(const char *name); +extern struct filedesc *file_add_directory(const char *name); +extern struct filedesc *file_add_file(const char *name); +extern int fill_files(); + +extern unsigned int signature_count(); +extern struct filedesc *signature_of_index(unsigned int index); +extern struct filedesc *create_signature(unsigned int number); +extern struct filedesc *get_signature(unsigned int number); + +extern int file_set_prop(struct filedesc *file, const char *name, const char *value); +extern const char *file_get_prop(struct filedesc *file, const char *name); + +/**************************************************************/ +/* from wgtpkg-verbose */ +extern int verbosity; +#define warning(...) do{if(verbosity)syslog(LOG_WARNING,__VA_ARGS__);}while(0) +#define notice(...) do{if(verbosity)syslog(LOG_NOTICE,__VA_ARGS__);}while(0) +#define info(...) do{if(verbosity)syslog(LOG_INFO,__VA_ARGS__);}while(0) +#define debug(...) do{if(verbosity>1)syslog(LOG_DEBUG,__VA_ARGS__);}while(0) +extern int verbose_scan_args(int argc, char **argv); + +/**************************************************************/ +/* from wgtpkg-workdir */ + +extern int enter_workdir(int clean); +extern void remove_workdir(); +extern int make_workdir(int reuse); +extern int set_workdir(const char *name, int create); + +/**************************************************************/ +/* from wgtpkg-xmlsec */ + +extern int xmlsec_init(); +extern void xmlsec_shutdown(); +extern int xmlsec_verify(xmlNodePtr node); +extern xmlDocPtr xmlsec_create(int index, const char *key, const char **certs); + +/**************************************************************/ +/* from wgtpkg-zip */ + +/* read (extract) 'zipfile' in current directory */ +extern int zread(const char *zipfile, unsigned long long maxsize); + +/* write (pack) content of the current directory in 'zipfile' */ +extern int zwrite(const char *zipfile); + + diff --git a/wgt-config-xml.c b/wgt-config-xml.c deleted file mode 100644 index f92ae3a..0000000 --- a/wgt-config-xml.c +++ /dev/null @@ -1,236 +0,0 @@ -/* - 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 -#include -#include -#include -#include - -#include -#include -#include - - -#include "wgt.h" - -static xmlDocPtr configxml = NULL; - -static xmlNodePtr next(xmlNodePtr node, const char *type) -{ - while (node && node->type != XML_ELEMENT_NODE && strcmp(type, node->name)) - node = node->next; - return node; -} - -static xmlNodePtr first(const char *type) -{ - xmlNodePtr root; - if (configxml) { - root = xmlDocGetRootElement(configxml); - if (root) - return next(root->children, type); - } - return NULL; -} - -static int scorelang(xmlNodePtr node) -{ - char *lang = xmlNodeGetLang(node); - int score = locales_score(lang); - xmlFree(lang); - return score; -} - -static xmlNodePtr element_based_localisation(const char *type) -{ - xmlNodePtr resu, elem; - int sr, s; - - resu = first(type); - if (resu) { - sr = scorelang(resu); - elem = next(resu->next, type); - while (resu) { - s = scorelang(elem); - if (s < sr) { - resu = elem; - sr = s; - } - elem = next(elem->next, type); - } - } - return resu; -} - -void confixml_close() -{ - if (configxml) { - xmlFreeDoc(configxml); - configxml = NULL; - } -} - -int confixml_open() -{ - int fd; - assert(!configxml); - fd = widget_open_read(_config_xml_); - if (fd < 0) { - syslog(LOG_ERR, "can't open config file %s", _config_xml_); - return fd; - } - configxml = xmlReadFd(fd, "_config_xml_", NULL, 0); - close(fd); - if (configxml == NULL) { - syslog(LOG_ERR, "xml parse of config file %s failed", _config_xml_); - return -1; - } - return 0; -} - -/* elements based on localisation */ -xmlNodePtr confixml_name() -{ - return element_based_localisation(_name_); -} - -xmlNodePtr confixml_description() -{ - return element_based_localisation(_description_); -} - -xmlNodePtr confixml_license() -{ - return element_based_localisation(_license_); -} - -/* elements based on path localisation */ -xmlNodePtr confixml_author() -{ - return first(_author_); -} - -xmlNodePtr confixml_content() -{ - return first(_content_); -} - -/* element multiple */ - -xmlNodePtr confixml_first_feature() -{ - return first(_feature_); -} - -xmlNodePtr confixml_next_feature(xmlNodePtr node) -{ - return next(node->next, _feature_); -} - -xmlNodePtr confixml_first_preference() -{ - return first(_preference_); -} - -xmlNodePtr confixml_next_preference(xmlNodePtr node) -{ - return next(node->next, _preference_); -} - -xmlNodePtr confixml_first_icon() -{ - return first(_icon_); -} - -xmlNodePtr confixml_next_icon(xmlNodePtr node) -{ - return next(node->next, _icon_); -} - -/* best sized icon */ - -static int score_dim(xmlNodePtr ref, xmlNodePtr x, const char *dim, int request) -{ - int r, iref, ix; - char *sref, *sx; - - sref = xmlGetProp(ref, dim); - if (sref) { - iref = atoi(sref); - xmlFree(sref); - sx = xmlGetProp(x, dim); - if (sx) { - /* sref && sx */ - ix = atoi(sx); - xmlFree(sx); - if (ix >= request) { - if (iref >= request) - r = ix - iref; - else - r = request - ix; - } else { - if (iref >= request) - r = iref - request; - else - r = iref - ix; - } - } else { - /* sref && !sx */ - if (iref >= request) - r = iref - request; - else - r = 0; - } - } else { - sx = xmlGetProp(x, dim); - if (sx) { - /* !sref && sx */ - ix = atoi(sx); - xmlFree(sx); - if (ix >= request) - r = request - ix; - else - r = 0; - } else { - /* !sref && !sx */ - r = 0; - } - } - return r; -} - -static int better_icon(xmlNodePtr ref, xmlNodePtr x, int width, int height) -{ - int sw = score_dim(ref, x, _width_, width); - int sh = score_dim(ref, x, _height_, height); - return sw+sh < 0; -} - -xmlNodePtr confixml_icon(int width, int height) -{ - xmlNodePtr resu, icon; - - resu = confixml_first_icon(); - icon = confixml_next_icon(resu); - while (icon) { - if (better_icon(resu, icon, width, height)) - resu = icon; - icon = confixml_next_icon(icon); - } - return resu; -} - diff --git a/wgt-locales.c b/wgt-locales.c deleted file mode 100644 index 9b97cc4..0000000 --- a/wgt-locales.c +++ /dev/null @@ -1,121 +0,0 @@ -/* - 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. -*/ - - -#define _GNU_SOURCE -#include -#include -#include -#include - -#include "wgt.h" - -struct locarr { - int count; - char **arr; -}; - -static struct locarr locarr = { 0, NULL }; - -static int add(const char *locstr, int length) -{ - int i; - char *item, **ptr; - - item = strndup(locstr, length); - if (item != NULL) { - for (i = 0 ; item[i] ; i++) - item[i] = tolower(item[i]); - for (i = 0 ; i < locarr.count ; i++) - if (!strcmp(item, locarr.arr[i])) { - free(item); - return 0; - } - - ptr = realloc(locarr.arr, (1 + locarr.count) * sizeof(locarr.arr[0])); - if (ptr) { - locarr.arr = ptr; - locarr.arr[locarr.count++] = item; - return 0; - } - free(item); - } - errno = ENOMEM; - return -1; -} - -void locales_reset() -{ - while (locarr.count) - free(locarr.arr[--locarr.count]); -} - -int locales_add(const char *locstr) -{ - const char *stop, *next; - while (*locstr) { - stop = strchrnul(locstr, ','); - next = stop + !!*stop; - while (locstr != stop) { - if (add(locstr, stop - locstr)) - return -1; - do { stop--; } while(stop > locstr && *stop != '-'); - } - locstr = next; - } - return 0; -} - -int locales_score(const char *lang) -{ - int i; - - if (lang) - for (i = 0 ; i < locarr.count ; i++) - if (!strcasecmp(lang, locarr.arr[i])) - return i; - - return INT_MAX; -} - -char *locales_locate_file(const char *filename) -{ - int i; - char path[PATH_MAX]; - char * result; - - for (i = 0 ; i < locarr.count ; i++) { - if (snprintf(path, sizeof path, "locales/%s/%s", locarr.arr[i], filename) >= (int)(sizeof path)) { - errno = EINVAL; - return NULL; - } - if (widget_has(path)) { - result = strdup(path); - if (!result) - errno = ENOMEM; - return result; - } - } - if (widget_has(filename)) { - result = strdup(filename); - if (!result) - errno = ENOMEM; - return result; - } - errno = ENOENT; - return NULL; -} - diff --git a/wgt-rootdir.c b/wgt-rootdir.c deleted file mode 100644 index 4df1705..0000000 --- a/wgt-rootdir.c +++ /dev/null @@ -1,97 +0,0 @@ -/* - 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. -*/ - -#define _GNU_SOURCE - -#include -#include -#include - -#include "wgt.h" - - -static int rootfd = AT_FDCWD; - -int widget_set_rootdir(const char *pathname) -{ - int rfd; - - if (!pathname) - rfd = AT_FDCWD; - else { - rfd = openat(AT_FDCWD, pathname, O_PATH|O_DIRECTORY); - if (rfd < 0) - return rfd; - } - if (rootfd >= 0) - close(rootfd); - rootfd = AT_FDCWD; - return 0; -} - -static int validsubpath(const char *subpath) -{ - int l = 0, i = 0; - if (subpath[i] == '/') - return 0; - while(subpath[i]) { - switch(subpath[i++]) { - case '.': - if (!subpath[i]) - break; - if (subpath[i] == '/') { - i++; - break; - } - if (subpath[i++] == '.') { - if (!subpath[i]) { - l--; - break; - } - if (subpath[i++] == '/') { - l--; - break; - } - } - default: - while(subpath[i] && subpath[i] != '/') - i++; - l++; - case '/': - break; - } - } - return l >= 0; -} - -int widget_has(const char *filename) -{ - if (!validsubpath(filename)) { - errno = EINVAL; - return -1; - } - return 0 == faccessat(rootfd, filename, F_OK, 0); -} - -int widget_open_read(const char *filename) -{ - if (!validsubpath(filename)) { - errno = EINVAL; - return -1; - } - return openat(rootfd, filename, O_RDONLY); -} - diff --git a/wgt-strings.c b/wgt-strings.c deleted file mode 100644 index a2fff39..0000000 --- a/wgt-strings.c +++ /dev/null @@ -1,31 +0,0 @@ -/* - 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 "wgt.h" - -const char _config_xml_[] = "config.xml"; -const char _name_[] = "name"; -const char _description_[] = "description"; -const char _author_[] = "author"; -const char _license_[] = "license"; -const char _icon_[] = "icon"; -const char _content_[] = "content"; -const char _feature_[] = "feature"; -const char _preference_[] = "preference"; -const char _width_[] = "width"; -const char _height_[] = "height"; - - diff --git a/wgt.h b/wgt.h deleted file mode 100644 index d95e65b..0000000 --- a/wgt.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - 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 -#include "config.h" - - -/**************************************************************/ -/* from wgt-config-xml */ - -extern int confixml_open(); -extern void confixml_close(); -extern xmlNodePtr confixml_name(); -extern xmlNodePtr confixml_description(); -extern xmlNodePtr confixml_license(); -extern xmlNodePtr confixml_author(); -extern xmlNodePtr confixml_content(); -extern xmlNodePtr confixml_icon(int width, int height); -extern xmlNodePtr confixml_first_feature(); -extern xmlNodePtr confixml_next_feature(xmlNodePtr node); -extern xmlNodePtr confixml_first_preference(); -extern xmlNodePtr confixml_next_preference(xmlNodePtr node); -extern xmlNodePtr confixml_first_icon(); -extern xmlNodePtr confixml_next_icon(xmlNodePtr node); - -/**************************************************************/ -/* from wgt-locales */ - -extern void locales_reset(); -extern int locales_add(const char *locstr); -extern int locales_score(const char *lang); -extern char *locales_locate_file(const char *filename); - -/**************************************************************/ -/* from wgt-rootdir */ - -extern int widget_set_rootdir(const char *pathname); -extern int widget_has(const char *filename); -extern int widget_open_read(const char *filename); - -/**************************************************************/ -/* from wgt-strings */ - -extern const char _config_xml_[]; -extern const char _name_[]; -extern const char _description_[]; -extern const char _author_[]; -extern const char _license_[]; -extern const char _icon_[]; -extern const char _content_[]; -extern const char _feature_[]; -extern const char _preference_[]; -extern const char _width_[]; -extern const char _height_[]; - diff --git a/wgtpkg-base64.c b/wgtpkg-base64.c deleted file mode 100644 index 5cf6f8a..0000000 --- a/wgtpkg-base64.c +++ /dev/null @@ -1,203 +0,0 @@ -/* - 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 -#include -#include - -#include "wgtpkg.h" - -static char tob64(char x) -{ - if (x < 26) - return 'A' + x; - if (x < 52) - return 'a' + x - 26; - if (x < 62) - return '0' + x - 52; - return x == 62 ? '+' : '/'; -} - -char *base64encw(const char *buffer, int length, int width) -{ - int remain, in, out; - char *result; - - if (width == 0 || width % 4) { - syslog(LOG_ERR, "bad width in base64enc"); - return NULL; - } - result = malloc(2 + 4 * ((length + 2) / 3) + (length / width)); - if (result == NULL) { - syslog(LOG_ERR, "malloc failed in base64enc"); - return NULL; - } - in = out = 0; - remain = length; - while (remain >= 3) { - if (out % (width + 1) == width) - result[out++] = '\n'; - result[out] = tob64((buffer[in] >> 2) & '\x3f'); - result[out+1] = tob64(((buffer[in] << 4) & '\x30') | ((buffer[in+1] >> 4) & '\x0f')); - result[out+2] = tob64(((buffer[in+1] << 2) & '\x3c') | ((buffer[in+2] >> 6) & '\x03')); - result[out+3] = tob64(buffer[in+2] & '\x3f'); - remain -= 3; - in += 3; - out += 4; - } - if (remain != 0) { - if (out % (width + 1) == width) - result[out++] = '\n'; - result[out] = tob64((buffer[in] >> 2) & '\x3f'); - if (remain == 1) { - result[out+1] = tob64((buffer[in] << 4) & '\x30'); - result[out+2] = '='; - } else { - result[out+1] = tob64(((buffer[in] << 4) & '\x30') | ((buffer[in+1] >> 4) & '\x0f')); - result[out+2] = tob64((buffer[in+1] << 2) & '\x3c'); - } - result[out+3] = '='; - out += 4; - } - result[out] = 0; - return result; -} - -char *base64enc(const char *buffer, int length) -{ - return base64encw(buffer, length, 76); -} - -static char fromb64(char x) -{ - if ('A' <= x && x <= 'Z') - return x - 'A'; - if ('a' <= x && x <= 'z') - return x - 'a' + 26; - if ('0' <= x && x <= '9') - return x - '0' + 52; - if (x == '+') - return 62; - if (x == '/') - return 63; - if (x == '=') - return '@'; - return 'E'; -} - -int base64dec(const char *buffer, char **output) -{ - int len, in, out; - char *result; - unsigned char x0, x1, x2, x3; - - len = strlen(buffer); - result = malloc(3 * ((3 + len) / 4)); - if (result == NULL) { - syslog(LOG_ERR, "malloc failed in base64dec"); - return -1; - } - in = out = 0; - while (buffer[in] == '\r' || buffer[in] == '\n') - in++; - while (buffer[in]) { - if (in + 4 > len) { - syslog(LOG_ERR, "unexpected input size in base64dec"); - free(result); - return -1; - } - x0 = (unsigned char)fromb64(buffer[in]); - x1 = (unsigned char)fromb64(buffer[in+1]); - x2 = (unsigned char)fromb64(buffer[in+2]); - x3 = (unsigned char)fromb64(buffer[in+3]); - in += 4; - if (x0 == 'E' || x1 == 'E' || x2 == 'E' || x3 == 'E') { - syslog(LOG_ERR, "unexpected input character in base64dec"); - free(result); - return -1; - } - if (x0 == '@' || x1 == '@' || (x2 == '@' && x3 != '@')) { - syslog(LOG_ERR, "unexpected termination character in base64dec"); - free(result); - return -1; - } - result[out] = (char)((x0 << 2) | (x1 >> 4)); - result[out+1] = (char)((x1 << 4) | ((x2 >> 2) & 15)); - result[out+2] = (char)((x2 << 6) | (x3 & 63)); - while (buffer[in] == '\r' || buffer[in] == '\n') - in++; - if (x3 != '@') - out += 3; - else if (!buffer[in]) - out += 1 + (x2 != '@'); - else { - syslog(LOG_ERR, "unexpected continuation in base64dec"); - free(result); - return -1; - } - } - *output = result; - return out; -} - -int base64eq(const char *buf1, const char *buf2) -{ - for(;;) { - while(*buf1 == '\n' || *buf1 == '\r') - buf1++; - while(*buf2 == '\n' || *buf2 == '\r') - buf2++; - if (*buf1 != *buf2) - return 0; - if (!*buf1) - return 1; - buf1++; - buf2++; - } -} - -#ifdef TESTBASE64 -#include -#include - -int main(int ac, char **av) -{ - char buffer[32768]; - int fd, l0, l1, l2; - char *p1, *p2; - - while(*++av) { - fd = open(*av, O_RDONLY); - if (fd < 0) continue; - l0 = read(fd, buffer, sizeof buffer); - if (l0 <= 0) continue; - close(fd); - p1 = base64enc(buffer, l0); - if (!p1) continue; - l1 = strlen(p1); - l2 = base64dec(p1, &p2); - if (l2 <= 0) continue; -printf("[[[%.*s]]]\n",l2,p2); - if (l0 != l2) printf("length mismatch\n"); - else if (memcmp(buffer, p2, l0)) printf("content mismatch\n"); - free(p1); - free(p2); - } - return 0; -} - -#endif diff --git a/wgtpkg-certs.c b/wgtpkg-certs.c deleted file mode 100644 index 1d8b976..0000000 --- a/wgtpkg-certs.c +++ /dev/null @@ -1,80 +0,0 @@ -/* - 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 -#include - -#include "wgtpkg.h" - -struct x509l { - int count; - X509 **certs; -}; - -static struct x509l certificates = { .count = 0, .certs = NULL }; - -static int add_certificate_x509(X509 *x) -{ - X509 **p = realloc(certificates.certs, (certificates.count + 1) * sizeof(X509*)); - if (!p) { - syslog(LOG_ERR, "reallocation failed for certificate"); - return -1; - } - certificates.certs = p; - p[certificates.count++] = x; - return 0; -} - -static int add_certificate_bin(const char *bin, int len) -{ - int rc; - const char *b, *e; - b = bin; - e = bin + len; - while (len) { - X509 *x = d2i_X509(NULL, (const unsigned char **)&b, e-b); - if (x == NULL) { - syslog(LOG_ERR, "d2i_X509 failed"); - return -1; - } - rc = add_certificate_x509(x); - if (rc) { - X509_free(x); - return rc; - } - } - return 0; -} - -int add_certificate_b64(const char *b64) -{ - char *d; - int l = base64dec(b64, &d); - if (l > 0) { - l = add_certificate_bin(d, l); - free(d); - } - return l; -} - -void clear_certificates() -{ - while(certificates.count) - X509_free(certificates.certs[--certificates.count]); -} - - diff --git a/wgtpkg-digsig.c b/wgtpkg-digsig.c deleted file mode 100644 index 284acd1..0000000 --- a/wgtpkg-digsig.c +++ /dev/null @@ -1,358 +0,0 @@ -/* - 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 -#include -#include - -#include -#include -#include - - -#include "wgtpkg.h" - - - -static const char uri_role_author[] = "http://www.w3.org/ns/widgets-digsig#role-author"; -static const char uri_role_distributor[] = "http://www.w3.org/ns/widgets-digsig#role-distributor"; -static const char uri_profile[] = "http://www.w3.org/ns/widgets-digsig#profile"; - - -/* global data */ -static xmlDocPtr document; /* the document */ - -/* facility to get the first element node (skip text nodes) starting with 'node' */ -static xmlNodePtr next_element(xmlNodePtr node) -{ - while (node && node->type != XML_ELEMENT_NODE) - node = node->next; - return node; -} - -/* is the 'node' an element node of 'name'? */ -static int is_element(xmlNodePtr node, const char *name) -{ - return node->type == XML_ELEMENT_NODE - && !strcmp(name, node->name); -} - -/* is the 'node' an element node of 'name'? */ -static int is_node(xmlNodePtr node, const char *name) -{ - return node != NULL && is_element(node, name); -} - -#if 0 -/* facility to get the first element node (skip text nodes) starting with 'node' */ -static xmlNodePtr next_element_type(xmlNodePtr node, const char *name) -{ - while (node && node->type != XML_ELEMENT_NODE && strcmp(name, node->name)) - node = node->next; - return node; -} - -/* search the element node of id. NOTE : not optimized at all */ -static xmlNodePtr search_for(const char *attrname, const char *value) -{ - char *val; - xmlNodePtr iter, next; - xmlNodePtr result; - - result = NULL; - iter = xmlDocGetRootElement(document); - while (iter != NULL) { - val = xmlGetProp(iter, attrname); - if (val != NULL && !strcmp(val, value)) { - if (result != NULL) { - syslog(LOG_ERR, "duplicated %s %s", attrname, value); - free(val); - return NULL; - } - result = iter; - } - free(val); - next = next_element(iter->children); - if (next == NULL) { - /* no child, try sibling */ - next = next_element(iter->next); - if (next == NULL) { - iter = iter->parent; - while (iter != NULL && next == NULL) { - next = next_element(iter->next); - iter = iter->parent; - } - } - } - iter = next; - } - if (result == NULL) - syslog(LOG_ERR, "node of %s '%s' not found", attrname, value); - return result; -} - -/* search the element node of id. NOTE : not optimized at all */ -static xmlNodePtr search_id(const char *id) -{ - return search_for("Id", id); -} -#endif - -/* check the digest of one element */ -static int check_one_reference(xmlNodePtr ref) -{ - int rc; - char *uri; - xmlURIPtr u; - struct filedesc *fdesc; - - /* start */ - rc = -1; - - /* get the uri */ - uri = xmlGetProp(ref, "URI"); - if (uri == NULL) { - syslog(LOG_ERR, "attribute URI of element not found"); - goto error; - } - - /* parse the uri */ - u = xmlParseURI(uri); - if (!u) { - syslog(LOG_ERR, "error while parsing URI %s", uri); - goto error2; - } - - /* check that unexpected parts are not there */ - if (u->scheme || u->opaque || u->authority || u->server || u->user || u->query) { - syslog(LOG_ERR, "unexpected uri component in %s", uri); - goto error3; - } - - /* check path and fragment */ - if (!u->path && !u->fragment) { - syslog(LOG_ERR, "invalid uri %s", uri); - goto error3; - } - if (u->path && u->fragment) { - syslog(LOG_ERR, "not allowed to sign foreign fragment in %s", uri); - goto error3; - } - - if (u->path) { - /* check that the path is valid */ - fdesc = file_of_name(u->path); - if (fdesc == NULL) { - syslog(LOG_ERR, "reference to unknown file %s", u->path); - goto error3; - } - if (fdesc->type != type_file) { - syslog(LOG_ERR, "reference to directory %s", u->path); - goto error3; - } - if ((fdesc->flags & flag_distributor_signature) != 0) { - syslog(LOG_ERR, "reference to signature %s", u->path); - goto error3; - } - fdesc->flags |= flag_referenced; - rc = 0; - } else { - rc = 0; - } - -error3: - xmlFreeURI(u); -error2: - xmlFree(uri); -error: - return rc; -} - -static int check_references(xmlNodePtr sinfo) -{ - xmlNodePtr elem; - - elem = sinfo->children; - while (elem != NULL) { - if (is_element(elem, "Reference")) - if (check_one_reference(elem)) - return -1; - elem = elem->next; - } - return 0; -} - -static int get_certificates(xmlNodePtr kinfo) -{ - xmlNodePtr n1, n2; - char *b; - int rc; - - n1 = kinfo->children; - while (n1) { - if (is_element(n1, "X509Data")) { - n2 = n1->children; - while (n2) { - if (is_element(n2, "X509Certificate")) { - b = xmlNodeGetContent(n2); - if (b == NULL) { - syslog(LOG_ERR, "xmlNodeGetContent of X509Certificate failed"); - return -1; - } - rc = add_certificate_b64(b); - xmlFree(b); - if (rc) - return rc; - } - n2 = n2->next; - } - } - n1 = n1->next; - } - return 0; -} - -/* checks the current document */ -static int checkdocument() -{ - int rc; - xmlNodePtr sinfo, svalue, kinfo, objs, rootsig; - - rc = -1; - - rootsig = xmlDocGetRootElement(document); - if (!is_node(rootsig, "Signature")) { - syslog(LOG_ERR, "root element not found"); - goto error; - } - - sinfo = next_element(rootsig->children); - if (!is_node(sinfo, "SignedInfo")) { - syslog(LOG_ERR, "element not found"); - goto error; - } - - svalue = next_element(sinfo->next); - if (!is_node(svalue, "SignatureValue")) { - syslog(LOG_ERR, "element not found"); - goto error; - } - - kinfo = next_element(svalue->next); - if (is_node(kinfo, "KeyInfo")) { - objs = kinfo->next; - } else { - objs = kinfo; - kinfo = NULL; - } - - rc = check_references(sinfo); - if (rc) - goto error; - - rc = xmlsec_verify(rootsig); - if (rc) - goto error; - - rc = get_certificates(kinfo); - -error: - return rc; -} - -/* verify the digital signature of the file described by 'fdesc' */ -int verify_digsig(struct filedesc *fdesc) -{ - int res; - - assert ((fdesc->flags & flag_signature) != 0); - debug("-- checking file %s",fdesc->name); - - /* reset the flags */ - file_clear_flags(); - clear_certificates(); - - /* reads and xml parses the signature file */ - document = xmlReadFile(fdesc->name, NULL, 0); - if (document == NULL) { - syslog(LOG_ERR, "xml parse of file %s failed", fdesc->name); - return -1; - } - - res = checkdocument(); - if (res) - syslog(LOG_ERR, "previous error was during check of file %s", fdesc->name); - - xmlFreeDoc(document); - return res; -} - -/* check all the signature files */ -int check_all_signatures() -{ - int rc, irc; - unsigned int i, n; - struct filedesc *fdesc; - - n = signature_count(); - rc = 0; - for (i = n ; i-- > 0 ; ) { - fdesc = signature_of_index(i); - irc = verify_digsig(fdesc); - if (!irc) - rc = irc; - } - - return rc; -} - -/* create a signature of 'index' (0 for author, other values for distributors) -using the private 'key' (filename) and the certificates 'certs' (filenames) -as trusted chain */ -int create_digsig(int index, const char *key, const char **certs) -{ - struct filedesc *fdesc; - xmlDocPtr doc; - int rc, len; - - rc = -1; - - /* create the doc */ - doc = xmlsec_create(index, key, certs); - if (doc == NULL) - goto error; - - /* instanciate the filename */ - fdesc = create_signature(index); - if (fdesc == NULL) - goto error2; - - /* save the doc as file */ - len = xmlSaveFormatFileEnc(fdesc->name, doc, NULL, 0); - if (len < 0) { - syslog(LOG_ERR, "xmlSaveFormatFileEnc to %s failed", fdesc->name); - goto error2; - } - - rc = 0; -error2: - xmlFreeDoc(doc); -error: - return rc; -} - - diff --git a/wgtpkg-files.c b/wgtpkg-files.c deleted file mode 100644 index 06aac83..0000000 --- a/wgtpkg-files.c +++ /dev/null @@ -1,313 +0,0 @@ -/* - 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. -*/ -#define _GNU_SOURCE -#include -#include -#include -#include -#include -#include -#include - -#include "wgtpkg.h" - -struct fdb { - unsigned int count; - struct filedesc **files; -}; - -static struct fdb allfiles = { .count = 0, .files = NULL }; -static struct fdb allsignatures = { .count = 0, .files = NULL }; - -static const char author_file[] = "author-signature.xml"; -static const char distributor_file_prefix[] = "signature"; -static const char distributor_file_suffix[] = ".xml"; - -static unsigned int what_signature(const char *name) -{ - unsigned int len, id, nid; - - if (!strcmp(name, author_file)) - return UINT_MAX; - - len = sizeof(distributor_file_prefix)-1; - if (memcmp(name, distributor_file_prefix, len)) - return 0; - if (name[len] <= '0' || name[len] > '9') - return 0; - id = (unsigned int)(name[len++] - '0'); - while ('0' <= name[len] && name[len] <= '9') { - nid = 10 * id + (unsigned int)(name[len++] - '0'); - if (nid < id || nid == UINT_MAX) { - syslog(LOG_WARNING, "number too big for %s", name); - return 0; - } - id = nid; - } - if (strcmp(name+len, distributor_file_suffix)) - return 0; - - return id; -} - -static struct filedesc *get_filedesc(const char *name, int create) -{ - int cmp; - unsigned int low, up, mid, sig; - struct filedesc *result, **grow; - - /* search */ - low = 0; - up = allfiles.count; - while(low < up) { - mid = (low + up) >> 1; - result = allfiles.files[mid]; - cmp = strcmp(result->name, name); - if (!cmp) - return result; /* found */ - if (cmp > 0) - up = mid; - else - low = mid + 1; - } - - /* not found, can create ? */ - if (!create) - return NULL; - - sig = what_signature(name); - - /* allocations */ - grow = realloc(allfiles.files, (allfiles.count + 1) * sizeof(struct filedesc *)); - if (grow == NULL) { - syslog(LOG_ERR, "realloc failed in get_filedesc"); - return NULL; - } - allfiles.files = grow; - - if (sig) { - grow = realloc(allsignatures.files, (allsignatures.count + 1) * sizeof(struct filedesc *)); - if (grow == NULL) { - syslog(LOG_ERR, "second realloc failed in get_filedesc"); - return NULL; - } - allsignatures.files = grow; - } - - result = malloc(sizeof(struct filedesc) + strlen(name)); - if (!result) { - syslog(LOG_ERR, "calloc failed in get_filedesc"); - return NULL; - } - - /* initialisation */ - result->type = type_unset; - result->flags = sig == 0 ? 0 : sig == UINT_MAX ? flag_author_signature : flag_distributor_signature; - result->zindex = 0; - result->signum = sig; - strcpy(result->name, name); - - /* insertion */ - if (low < allfiles.count) - memmove(allfiles.files+low+1, allfiles.files+low, (allfiles.count - low) * sizeof(struct filedesc *)); - allfiles.files[low] = result; - allfiles.count++; - if (sig) { - for (low = 0 ; low < allsignatures.count && sig > allsignatures.files[low]->signum ; low++); - if (low < allsignatures.count) - memmove(allsignatures.files+low+1, allsignatures.files+low, (allsignatures.count - low) * sizeof(struct filedesc *)); - allsignatures.files[low] = result; - allsignatures.count++; - } - - return result; -} - - -static struct filedesc *file_add(const char *name, enum entrytype type) -{ - struct filedesc *desc; - - desc = get_filedesc(name, 1); - if (!desc) - errno = ENOMEM; - else if (desc->type == type_unset) - desc->type = type; - else { - syslog(LOG_ERR, "redeclaration of %s in file_add", name); - errno = EEXIST; - desc = NULL; - } - return desc; -} - -void file_reset() -{ - unsigned int i; - - allsignatures.count = 0; - for (i = 0 ; i < allfiles.count ; i++) - free(allfiles.files[i]); - allfiles.count = 0; -} - -unsigned int file_count() -{ - return allfiles.count; -} - -struct filedesc *file_of_index(unsigned int index) -{ - assert(index < allfiles.count); - return allfiles.files[index]; -} - -struct filedesc *file_of_name(const char *name) -{ - return get_filedesc(name, 0); -} - -struct filedesc *file_add_directory(const char *name) -{ - return file_add(name, type_directory); -} - -struct filedesc *file_add_file(const char *name) -{ - return file_add(name, type_file); -} - -unsigned int signature_count() -{ - return allsignatures.count; -} - -struct filedesc *signature_of_index(unsigned int index) -{ - assert(index < allsignatures.count); - return allsignatures.files[index]; -} - -struct filedesc *get_signature(unsigned int number) -{ - unsigned int idx; - - if (number == 0) - number = UINT_MAX; - for (idx = 0 ; idx < allsignatures.count ; idx++) - if (allsignatures.files[idx]->signum == number) - return allsignatures.files[idx]; - return NULL; -} - -struct filedesc *create_signature(unsigned int number) -{ - struct filedesc *result; - char *name; - int len; - - result = NULL; - if (number == 0 || number == UINT_MAX) - len = asprintf(&name, "%s", author_file); - else - len = asprintf(&name, "%s%u%s", distributor_file_prefix, number, distributor_file_suffix); - - if (len < 0) - syslog(LOG_ERR, "asprintf failed in create_signature"); - else { - assert(len > 0); - result = file_of_name(name); - if (result == NULL) - result = file_add_file(name); - free(name); - } - - return result; -} - -/* remove flags that are not related to being signature */ -void file_clear_flags() -{ - unsigned int i; - for (i = 0 ; i < allfiles.count ; i++) - allfiles.files[i]->flags &= flag_signature; -} - -static int fill_files_rec(char name[PATH_MAX], int offset) -{ - int len, err; - DIR *dir; - struct dirent *ent; - - if (offset == 0) - dir = opendir("."); - else { - dir = opendir(name); - name[offset++] = '/'; - } - if (!dir) { - syslog(LOG_ERR, "opendir %.*s failed in zwr", offset, name); - return -1; - } - - ent = readdir(dir); - while (ent != NULL) { - len = strlen(ent->d_name); - if (ent->d_name[0] == '.' && (len == 1 || - (ent->d_name[1] == '.' && len == 2))) - ; - else if (offset + len >= PATH_MAX) { - closedir(dir); - syslog(LOG_ERR, "name too long in fill_files_rec"); - errno = ENAMETOOLONG; - return -1; - } else { - memcpy(name + offset, ent->d_name, 1+len); - switch (ent->d_type) { - case DT_DIR: - if (file_add_directory(name) == NULL) { - closedir(dir); - return -1; - } - err = fill_files_rec(name, offset + len); - if (err) { - closedir(dir); - return err; - } - break; - case DT_REG: - if (file_add_file(name) == NULL) { - closedir(dir); - return -1; - } - break; - default: - break; - } - } - ent = readdir(dir); - } - - closedir(dir); - return 0; -} - -int fill_files() -{ - char name[PATH_MAX]; - return fill_files_rec(name, 0); -} - diff --git a/wgtpkg-install.c b/wgtpkg-install.c deleted file mode 100644 index 7a88ebf..0000000 --- a/wgtpkg-install.c +++ /dev/null @@ -1,84 +0,0 @@ -/* - 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. -*/ - -#define _BSD_SOURCE /* see readdir */ - -#include -#include -#include -#include -#include -#include -#include - -#include "wgtpkg.h" - -/* install the widget of the file */ -static void install(const char *wgtfile) -{ - notice("-- INSTALLING widget %s", wgtfile); - - if (enter_workdir(1)) - goto error; - - if (zread(wgtfile, 0)) - goto error; - - if (check_all_signatures()) - goto error; - - return; - -error: - return; - exit(1); -} - -/* install the widgets of the list */ -int main(int ac, char **av) -{ - int i, kwd; - - openlog("wgtpkg-install", LOG_PERROR, LOG_AUTH); - - xmlsec_init(); - - ac = verbose_scan_args(ac, av); - - /* canonic names for files */ - for (i = 1 ; av[i] != NULL ; i++) - if ((av[i] = realpath(av[i], NULL)) == NULL) { - syslog(LOG_ERR, "error while getting realpath of %dth argument", i); - return 1; - } - - /* workdir */ - kwd = 1; - if (make_workdir(kwd)) { - syslog(LOG_ERR, "failed to create a working directory"); - return 1; - } - if (!kwd) - atexit(remove_workdir); - - /* install widgets */ - for (av++ ; *av ; av++) - install(*av); - - exit(0); - return 0; -} - diff --git a/wgtpkg-pack.c b/wgtpkg-pack.c deleted file mode 100644 index a8aaa05..0000000 --- a/wgtpkg-pack.c +++ /dev/null @@ -1,167 +0,0 @@ -/* - 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. -*/ - -#define _GNU_SOURCE - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "wgtpkg.h" - -#if !defined(MAXCERT) -#define MAXCERT 20 -#endif -#if !defined(DEFAULT_KEY_FILE) -#define DEFAULT_KEY_FILE "key.pem" -#endif -#if !defined(DEFAULT_CERT_FILE) -#define DEFAULT_CERT_FILE "cert.pem" -#endif - -const char appname[] = "wgtpkg-pack"; - -static void usage() -{ - printf( - "usage: %s [-f] [-o wgtfile] directory\n" - "\n" - " -o wgtfile the output widget file\n" - " -f force overwriting\n" - " -q quiet\n" - " -v verbose\n" - "\n", - appname - ); -} - -static struct option options[] = { - { "output", required_argument, NULL, 'o' }, - { "force", no_argument, NULL, 'f' }, - { "help", no_argument, NULL, 'h' }, - { "quiet", no_argument, NULL, 'q' }, - { "verbose", no_argument, NULL, 'v' }, - { NULL, 0, NULL, 0 } -}; - -/* install the widgets of the list */ -int main(int ac, char **av) -{ - int i, force; - char *wgtfile, *directory, *x; - struct stat s; - - openlog(appname, LOG_PERROR, LOG_USER); - - force = 0; - wgtfile = directory = NULL; - for (;;) { - i = getopt_long(ac, av, "qvhfo:", options, NULL); - if (i < 0) - break; - switch (i) { - case 'o': - wgtfile = optarg; - break; - case 'q': - if (verbosity) - verbosity--; - break; - case 'v': - verbosity++; - break; - case 'f': - force = 1; - break; - case 'h': - usage(); - return 0; - case ':': - syslog(LOG_ERR, "missing argument"); - return 1; - default: - syslog(LOG_ERR, "unrecognized option"); - return 1; - } - } - - /* remaining arguments and final checks */ - if (optind >= ac) { - syslog(LOG_ERR, "no directory set"); - return 1; - } - directory = av[optind++]; - if (optind < ac) { - syslog(LOG_ERR, "extra parameters found"); - return 1; - } - - /* set default values */ - if (wgtfile == NULL && 0 > asprintf(&wgtfile, "%s.wgt", directory)) { - syslog(LOG_ERR, "asprintf failed"); - return 1; - } - - /* check values */ - if (stat(directory, &s)) { - syslog(LOG_ERR, "can't find directory %s", directory); - return 1; - } - if (!S_ISDIR(s.st_mode)) { - syslog(LOG_ERR, "%s isn't a directory", directory); - return 1; - } - if (access(wgtfile, F_OK) == 0 && force == 0) { - syslog(LOG_ERR, "can't overwrite existing %s", wgtfile); - return 1; - } - - notice("-- PACKING widget %s from directory %s", wgtfile, directory); - - /* creates an existing widget (for realpath it must exist) */ - i = open(wgtfile, O_WRONLY|O_CREAT|O_NOCTTY|O_NONBLOCK, 0666); - if (i < 0) { - syslog(LOG_ERR, "can't write widget %s", wgtfile); - return 1; - } - close(i); - - /* compute absolutes paths */ - x = realpath(wgtfile, NULL); - if (x == NULL) { - syslog(LOG_ERR, "realpath failed for %s",wgtfile); - return 1; - } - wgtfile = x; - - /* set and enter the workdir */ - if (set_workdir(directory, 0) || enter_workdir(0)) - return 1; - - - if (fill_files()) - return 1; - - return !!zwrite(wgtfile); -} - - diff --git a/wgtpkg-sign.c b/wgtpkg-sign.c deleted file mode 100644 index cd506fc..0000000 --- a/wgtpkg-sign.c +++ /dev/null @@ -1,199 +0,0 @@ -/* - 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. -*/ - -#define _GNU_SOURCE - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "wgtpkg.h" - -#if !defined(MAXCERT) -#define MAXCERT 20 -#endif -#if !defined(DEFAULT_KEY_FILE) -#define DEFAULT_KEY_FILE "key.pem" -#endif -#if !defined(DEFAULT_CERT_FILE) -#define DEFAULT_CERT_FILE "cert.pem" -#endif - -const char appname[] = "wgtpkg-sign"; - -static unsigned int get_number(const char *value) -{ - char *end; - unsigned long int val; - - val = strtoul(value, &end, 10); - if (*end || 0 == val || val >= UINT_MAX || *value == '-') { - syslog(LOG_ERR, "bad number value %s", value); - exit(1); - } - return (unsigned int)val; -} - -static void usage() -{ - printf( - "usage: %s [-f] [-k keyfile] [-c certfile]... [-o wgtfile] [-d number | -a] directory\n" - "\n" - " -k keyfile the private key to use for author signing\n" - " -c certfile the certificate(s) to use for author signing\n" - " -d number the number of the distributor signature (zero for automatic)\n" - " -a the author signature\n" - " -f force overwriting\n" - " -q quiet\n" - " -v verbose\n" - "\n", - appname - ); -} - -static struct option options[] = { - { "key", required_argument, NULL, 'k' }, - { "certificate", required_argument, NULL, 'c' }, - { "distributor", required_argument, NULL, 'd' }, - { "author", no_argument, NULL, 'a' }, - { "force", no_argument, NULL, 'f' }, - { "help", no_argument, NULL, 'h' }, - { "quiet", no_argument, NULL, 'q' }, - { "verbose", no_argument, NULL, 'v' }, - { NULL, 0, NULL, 0 } -}; - -/* install the widgets of the list */ -int main(int ac, char **av) -{ - int i, force, ncert, author; - unsigned int number; - char *keyfile, *certfiles[MAXCERT+1], *directory, **x; - struct stat s; - - openlog(appname, LOG_PERROR, LOG_USER); - - force = ncert = author = 0; - number = UINT_MAX; - keyfile = directory = NULL; - for (;;) { - i = getopt_long(ac, av, "hfak:c:d:", options, NULL); - if (i < 0) - break; - switch (i) { - case 'c': - if (ncert == MAXCERT) { - syslog(LOG_ERR, "maximum count of certificates reached"); - return 1; - } - certfiles[ncert++] = optarg; - continue; - case 'k': x = &keyfile; break; - case 'd': number = get_number(optarg); continue; - case 'f': force = 1; continue; - case 'a': author = 1; continue; - case 'h': usage(); return 0; - case ':': - syslog(LOG_ERR, "missing argument"); - return 1; - default: - syslog(LOG_ERR, "unrecognized option"); - return 1; - } - if (*x != NULL) { - syslog(LOG_ERR, "option set twice"); - return 1; - } - *x = optarg; - } - - /* remaining arguments and final checks */ - if (optind >= ac) { - syslog(LOG_ERR, "no directory set"); - return 1; - } - directory = av[optind++]; - if (optind < ac) { - syslog(LOG_ERR, "extra parameters found"); - return 1; - } - - /* set default values */ - if (keyfile == NULL) - keyfile = DEFAULT_KEY_FILE; - if (ncert == 0) - certfiles[ncert++] = DEFAULT_CERT_FILE; - - /* check values */ - if (stat(directory, &s)) { - syslog(LOG_ERR, "can't find directory %s", directory); - return 1; - } - if (!S_ISDIR(s.st_mode)) { - syslog(LOG_ERR, "%s isn't a directory", directory); - return 1; - } - if (access(keyfile, R_OK) != 0) { - syslog(LOG_ERR, "can't access private key %s", keyfile); - return 1; - } - for(i = 0 ; i < ncert ; i++) - if (access(certfiles[i], R_OK) != 0) { - syslog(LOG_ERR, "can't access certificate %s", certfiles[i]); - return 1; - } - - /* init xmlsec module */ - if (xmlsec_init()) - return 1; - - - /* compute absolutes paths */ -#define rp(x) do { char *p = realpath(x, NULL); if (p != NULL) x = p; else { syslog(LOG_ERR, "realpath failed for %s",x); return 1; } } while(0) - rp(keyfile); - for(i = 0 ; i < ncert ; i++) - rp(certfiles[i]); -#undef rp - - /* set and enter the workdir */ - if (set_workdir(directory, 0) || enter_workdir(0)) - return 1; - - if (fill_files()) - return 1; - - if (author) - number = 0; - else if (number == UINT_MAX) - for (number = 1; get_signature(number) != NULL ; number++); - - if (!force && get_signature(number) != NULL) { - syslog(LOG_ERR, "can't overwrite existing signature %s", get_signature(number)->name); - return 1; - } - - notice("-- SIGNING content of directory %s for number %u", directory, number); - - certfiles[ncert] = NULL; - return !!create_digsig(number, keyfile, (const char**)certfiles); -} - diff --git a/wgtpkg-verbose.c b/wgtpkg-verbose.c deleted file mode 100644 index 1472a90..0000000 --- a/wgtpkg-verbose.c +++ /dev/null @@ -1,36 +0,0 @@ -/* - 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 - -int verbosity = 1; - -int verbose_scan_args(int argc, char **argv) -{ - int i, r; - for (i=r=0 ; i < argc ; i++) { - if (!strcmp(argv[i], "-q")) - verbosity = verbosity ? verbosity-1 : 0; - else if (!strcmp(argv[i], "-v")) - verbosity++; - else - argv[r++] = argv[i]; - } - argv[r] = NULL; - return r; -} - - diff --git a/wgtpkg-workdir.c b/wgtpkg-workdir.c deleted file mode 100644 index 0c184a5..0000000 --- a/wgtpkg-workdir.c +++ /dev/null @@ -1,166 +0,0 @@ -/* - 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 -#include -#include -#include -#include -#include - -#include "wgtpkg.h" - -#ifndef PREDIR -#define PREDIR "./" -#endif - -static int mode = 0700; -static char workdir[PATH_MAX]; - -/* removes recursively the content of a directory */ -static int clean_dir() -{ - int cr; - DIR *dir; - struct dirent *ent; - struct { - struct dirent entry; - char spare[PATH_MAX]; - } entry; - - dir = opendir("."); - if (dir == NULL) { - syslog(LOG_ERR, "opendir failed in clean_dir"); - return -1; - } - - cr = -1; - for (;;) { - if (readdir_r(dir, &entry.entry, &ent) != 0) { - syslog(LOG_ERR, "readdir_r failed in clean_dir"); - goto error; - } - if (ent == NULL) - break; - if (ent->d_name[0] == '.' && (ent->d_name[1] == 0 - || (ent->d_name[1] == '.' && ent->d_name[2] == 0))) - continue; - cr = unlink(ent->d_name); - if (!cr) - continue; - if (errno != EISDIR) { - syslog(LOG_ERR, "unlink of %s failed in clean_dir", ent->d_name); - goto error; - } - if (chdir(ent->d_name)) { - syslog(LOG_ERR, "enter directory %s failed in clean_dir", ent->d_name); - goto error; - } - cr = clean_dir(); - if (cr) - goto error; - if (chdir("..")) - goto error; - cr = rmdir(ent->d_name); - if (cr) { - syslog(LOG_ERR, "rmdir of %s failed in clean_dir", ent->d_name); - goto error; - } - } - cr = 0; -error: - closedir(dir); - return cr; -} - -/* removes the content of the working directory */ -int enter_workdir(int clean) -{ - int rc = chdir(workdir); - if (rc) - syslog(LOG_ERR, "entring workdir %s failed", workdir); - else if (clean) - rc = clean_dir(); - return rc; -} - -/* removes the working directory */ -void remove_workdir() -{ - enter_workdir(1); - chdir(".."); - unlink(workdir); -} - -int set_workdir(const char *name, int create) -{ - int rc; - size_t length; - struct stat s; - - /* check the length */ - length = strlen(name); - if (length >= sizeof workdir) { - syslog(LOG_ERR, "workdir name too long"); - return -1; - } - - rc = stat(name, &s); - if (rc) { - if (!create) { - syslog(LOG_ERR, "no workdir %s", name); - return -1; - } - rc = mkdir(name, mode); - if (rc) { - syslog(LOG_ERR, "can't create workdir %s", name); - return -1; - } - - } else if (!S_ISDIR(s.st_mode)) { - syslog(LOG_ERR, "%s isn't a directory", name); - return -1; - } - memcpy(workdir, name, 1+length); - return 0; -} - -/* install the widgets of the list */ -int make_workdir(int reuse) -{ - int i; - - /* create a temporary directory */ - for (i = 0 ; ; i++) { - if (i == INT_MAX) { - syslog(LOG_ERR, "exhaustion of workdirs"); - return -1; - } - sprintf(workdir, PREDIR "PACK%d", i); - if (!mkdir(workdir, mode)) - break; - if (errno != EEXIST) { - syslog(LOG_ERR, "error in creation of workdir %s: %m", workdir); - return -1; - } - if (reuse) - break; - } - - return 0; -} - diff --git a/wgtpkg-xmlsec.c b/wgtpkg-xmlsec.c deleted file mode 100644 index 843ea2b..0000000 --- a/wgtpkg-xmlsec.c +++ /dev/null @@ -1,371 +0,0 @@ -/* - 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 -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - - -#include "wgtpkg.h" - -static int initstatus; -static int initdone; -static xmlSecKeysMngrPtr keymgr; - -#ifndef CA_ROOT_DIRECTORY -#define CA_ROOT_DIRECTORY "./ca-certificates" -#endif - -/* checks if a file match uri (should not be a distributor signature) */ -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; -} - -/* open the file of uri */ -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; -} - -/* read the opened file */ -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; -} - -/* close the opened file */ -static int file_close_cb(void *context) -{ - return (int)fclose((FILE*)context); -} - -/* echo an error message */ -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 : "?"); -} - -/* fills database with trusted keys */ -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; - -} - -/* initialisation of access to xmlsec */ -int xmlsec_init() -{ - - if (initdone) - goto end; - - initdone = 1; - initstatus = -1; - - if(xmlSecInit() < 0) { - syslog(LOG_ERR, "xmlSecInit failed."); - goto end; - } - -#ifdef XMLSEC_CRYPTO_DYNAMIC_LOADING - if(xmlSecCryptoDLLoadLibrary(XMLSEC_CRYPTO) < 0) { - syslog(LOG_ERR, "xmlSecCryptoDLLoadLibrary %s failed.", XMLSEC_CRYPTO); - goto end; - } -#endif - - 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; -} - -/* shuting down accesses to xmlsec */ -void xmlsec_shutdown() -{ - xmlSecKeysMngrDestroy(keymgr); - - xmlSecCryptoShutdown(); - - xmlSecCryptoAppShutdown(); - - xmlSecShutdown(); -} - -/* verify a signature */ -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; -} - -/* templates for properties of signature files */ -static const struct { const char *id; const char *xml; } properties[2] = { - { - .id = "AuthorSignature", /* template of properties for author signature */ - .xml = - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - }, - { - .id = "DistributorSignature", /* template of properties for distributor signature */ - .xml = - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - } -}; - -/* create a signature of 'index' (0 for author, other values for distributors) -using the private 'key' (filename) and the certificates 'certs' (filenames) -as trusted chain */ -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; -} - diff --git a/wgtpkg-zip.c b/wgtpkg-zip.c deleted file mode 100644 index 9e51538..0000000 --- a/wgtpkg-zip.c +++ /dev/null @@ -1,342 +0,0 @@ -/* - 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. -*/ - -#define _BSD_SOURCE /* see readdir */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "wgtpkg.h" - - -#if !defined(MODE_OF_FILE_CREATION) -#define MODE_OF_FILE_CREATION 0640 -#endif -#if !defined(MODE_OF_DIRECTORY_CREATION) -#define MODE_OF_DIRECTORY_CREATION 0750 -#endif - -static int is_valid_filename(const char *filename) -{ - int lastsp = 0; - int index = 0; - unsigned char c; - - c = (unsigned char)filename[index]; - while (c) { - if ((c < 0x1f) - || ((lastsp = (c == 0x20)) && index == 0) - || c == 0x7f || c == 0x3c || c == 0x3e - || c == 0x3a || c == 0x22 - || c == 0x5c || c == 0x7c || c == 0x3f - || c == 0x2a || c == 0x5e || c == 0x60 - || c == 0x7b || c == 0x7d || c == 0x21) - return 0; - c = (unsigned char)filename[++index]; - } - return !lastsp; -} - -static int create_directory(char *file, int mode) -{ - int rc; - char *last = strrchr(file, '/'); - if (last != NULL) - *last = 0; - rc = mkdir(file, mode); - if (rc) { - if (errno == EEXIST) - rc = 0; - else if (errno == ENOENT) { - rc = create_directory(file, mode); - if (!rc) - rc = mkdir(file, mode); - } - } - if (rc) - syslog(LOG_ERR, "can't create directory %s", file); - if (last != NULL) - *last = '/'; - return rc; -} - -static int create_file(char *file, int fmode, int dmode) -{ - int fd = creat(file, fmode); - if (fd < 0 && errno == ENOENT) { - if (!create_directory(file, dmode)) - fd = creat(file, fmode); - } - if (fd < 0) - syslog(LOG_ERR, "can't create file %s", file); - return fd; -} - -/* read (extract) 'zipfile' in current directory */ -int zread(const char *zipfile, unsigned long long maxsize) -{ - struct filedesc *fdesc; - int err, fd, len; - struct zip *zip; - zip_int64_t z64; - unsigned int count, index; - struct zip_file *zfile; - struct zip_stat zstat; - char buffer[32768]; - ssize_t sizr, sizw; - size_t esize; - - /* open the zip file */ - zip = zip_open(zipfile, ZIP_CHECKCONS, &err); - if (!zip) { - syslog(LOG_ERR, "Can't connect to file %s", zipfile); - return -1; - } - - z64 = zip_get_num_entries(zip, 0); - if (z64 < 0 || z64 > UINT_MAX) { - syslog(LOG_ERR, "too many entries in %s", zipfile); - goto error; - } - count = (unsigned int)z64; - - /* records the files */ - file_reset(); - esize = 0; - for (index = 0 ; index < count ; index++) { - err = zip_stat_index(zip, index, ZIP_FL_ENC_GUESS, &zstat); - /* check the file name */ - if (!is_valid_filename(zstat.name)) { - syslog(LOG_ERR, "invalid entry %s found in %s", zstat.name, zipfile); - goto error; - } - if (zstat.name[0] == '/') { - syslog(LOG_ERR, "absolute entry %s found in %s", zstat.name, zipfile); - goto error; - } - len = strlen(zstat.name); - if (len == 0) { - syslog(LOG_ERR, "empty entry found in %s", zipfile); - goto error; - } - if (zstat.size == 0) { - /* directory name */ - if (zstat.name[len - 1] != '/') { - syslog(LOG_ERR, "bad directory name %s in %s", zstat.name, zipfile); - goto error; - } - /* record */ - fdesc = file_add_directory(zstat.name); - } else { - /* directory name */ - if (zstat.name[len - 1] == '/') { - syslog(LOG_ERR, "bad file name %s in %s", zstat.name, zipfile); - goto error; - } - /* get the size */ - esize += zstat.size; - /* record */ - fdesc = file_add_file(zstat.name); - } - if (!fdesc) - goto error; - fdesc->zindex = index; - } - - /* check the size */ - if (maxsize && esize > maxsize) { - syslog(LOG_ERR, "extracted size %zu greater than allowed size %llu", esize, maxsize); - goto error; - } - - /* unpack the recorded files */ - assert(count == file_count()); - for (index = 0 ; index < count ; index++) { - fdesc = file_of_index(index); - assert(fdesc != NULL); - err = zip_stat_index(zip, fdesc->zindex, ZIP_FL_ENC_GUESS, &zstat); - assert(zstat.name[0] != '/'); - if (zstat.size == 0) { - /* directory name */ - err = create_directory((char*)zstat.name, MODE_OF_DIRECTORY_CREATION); - if (err && errno != EEXIST) - goto error; - } else { - /* file name */ - zfile = zip_fopen_index(zip, fdesc->zindex, 0); - if (!zfile) { - syslog(LOG_ERR, "Can't open %s in %s", zstat.name, zipfile); - goto error; - } - fd = create_file((char*)zstat.name, MODE_OF_FILE_CREATION, MODE_OF_DIRECTORY_CREATION); - if (fd < 0) - goto errorz; - /* extract */ - z64 = zstat.size; - while (z64) { - sizr = zip_fread(zfile, buffer, sizeof buffer); - if (sizr < 0) { - syslog(LOG_ERR, "error while reading %s in %s", zstat.name, zipfile); - goto errorzf; - } - sizw = write(fd, buffer, sizr); - if (sizw < 0) { - syslog(LOG_ERR, "error while writing %s", zstat.name); - goto errorzf; - } - z64 -= sizw; - } - close(fd); - zip_fclose(zfile); - } - } - - zip_close(zip); - return 0; - -errorzf: - close(fd); -errorz: - zip_fclose(zfile); -error: - zip_close(zip); - return -1; -} - -struct zws { - struct zip *zip; - char name[PATH_MAX]; - char buffer[32768]; -}; - -static int zwr(struct zws *zws, int offset) -{ - int len, err; - DIR *dir; - struct dirent *ent; - zip_int64_t z64; - struct zip_source *zsrc; - - if (offset == 0) - dir = opendir("."); - else { - dir = opendir(zws->name); - zws->name[offset++] = '/'; - } - if (!dir) { - syslog(LOG_ERR, "opendir %.*s failed in zwr", offset, zws->name); - return -1; - } - - ent = readdir(dir); - while (ent != NULL) { - len = strlen(ent->d_name); - if (ent->d_name[0] == '.' && (len == 1 || - (ent->d_name[1] == '.' && len == 2))) - ; - else if (offset + len >= sizeof(zws->name)) { - syslog(LOG_ERR, "name too long in zwr"); - errno = ENAMETOOLONG; - goto error; - } else { - memcpy(zws->name + offset, ent->d_name, 1+len); - if (!is_valid_filename(ent->d_name)) { - syslog(LOG_ERR, "invalid name %s", zws->name); - goto error; - } - switch (ent->d_type) { - case DT_DIR: - z64 = zip_dir_add(zws->zip, zws->name, ZIP_FL_ENC_UTF_8); - if (z64 < 0) { - syslog(LOG_ERR, "zip_dir_add of %s failed", zws->name); - goto error; - } - err = zwr(zws, offset + len); - if (err) - goto error; - break; - case DT_REG: - zsrc = zip_source_file(zws->zip, zws->name, 0, 0); - if (zsrc == NULL) { - syslog(LOG_ERR, "zip_source_file of %s failed", zws->name); - goto error; - } - z64 = zip_file_add(zws->zip, zws->name, zsrc, ZIP_FL_ENC_UTF_8); - if (z64 < 0) { - syslog(LOG_ERR, "zip_file_add of %s failed", zws->name); - zip_source_free(zsrc); - goto error; - } - break; - default: - break; - } - } - ent = readdir(dir); - } - - closedir(dir); - return 0; -error: - closedir(dir); - return -1; -} - -/* write (pack) content of the current directory in 'zipfile' */ -int zwrite(const char *zipfile) -{ - int err; - struct zws zws; - - zws.zip = zip_open(zipfile, ZIP_CREATE|ZIP_TRUNCATE, &err); - if (!zws.zip) { - syslog(LOG_ERR, "Can't open %s for write", zipfile); - return -1; - } - - err = zwr(&zws, 0); - zip_close(zws.zip); - return err; -} - - -#if defined(TEST_READ) -int main(int ac, char **av) -{ - for(av++ ; *av ; av++) - zread(*av); - return 0; -} -#endif - -#if defined(TEST_WRITE) -int main(int ac, char **av) -{ - for(av++ ; *av ; av++) - zwrite(*av); - return 0; -} -#endif - diff --git a/wgtpkg.h b/wgtpkg.h deleted file mode 100644 index d35b7f4..0000000 --- a/wgtpkg.h +++ /dev/null @@ -1,125 +0,0 @@ -/* - 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 -#include "config.h" - -struct filedesc; - -/**************************************************************/ -/* from wgtpkg-base64 */ - -extern char *base64encw(const char *buffer, int length, int width); -extern char *base64enc(const char *buffer, int length); -extern int base64dec(const char *buffer, char **output); -extern int base64eq(const char *buf1, const char *buf2); - -/**************************************************************/ -/* from wgtpkg-certs */ - -extern void clear_certificates(); -extern int add_certificate_b64(const char *b64); - -/**************************************************************/ -/* from wgtpkg-digsig */ - -/* verify the digital signature in file */ -extern int verify_digsig(struct filedesc *fdesc); - -/* create a digital signature */ -extern int create_digsig(int index, const char *key, const char **certs); - -/* check the signatures of the current directory */ -extern int check_all_signatures(); - -/**************************************************************/ -/* from wgtpkg-files */ - -enum entrytype { - type_unset = 0, - type_file = 1, - type_directory = 2 -}; - -enum fileflag { - flag_referenced = 1, - flag_opened = 2, - flag_author_signature = 4, - flag_distributor_signature = 8, - flag_signature = 12 -}; - -struct filedesc { - enum entrytype type; - unsigned int flags; - unsigned int signum; - unsigned int zindex; - char name[1]; -}; - -extern void file_reset(); -extern void file_clear_flags(); -extern unsigned int file_count(); -extern struct filedesc *file_of_index(unsigned int index); -extern struct filedesc *file_of_name(const char *name); -extern struct filedesc *file_add_directory(const char *name); -extern struct filedesc *file_add_file(const char *name); -extern int fill_files(); - -extern unsigned int signature_count(); -extern struct filedesc *signature_of_index(unsigned int index); -extern struct filedesc *create_signature(unsigned int number); -extern struct filedesc *get_signature(unsigned int number); - -extern int file_set_prop(struct filedesc *file, const char *name, const char *value); -extern const char *file_get_prop(struct filedesc *file, const char *name); - -/**************************************************************/ -/* from wgtpkg-verbose */ -extern int verbosity; -#define warning(...) do{if(verbosity)syslog(LOG_WARNING,__VA_ARGS__);}while(0) -#define notice(...) do{if(verbosity)syslog(LOG_NOTICE,__VA_ARGS__);}while(0) -#define info(...) do{if(verbosity)syslog(LOG_INFO,__VA_ARGS__);}while(0) -#define debug(...) do{if(verbosity>1)syslog(LOG_DEBUG,__VA_ARGS__);}while(0) -extern int verbose_scan_args(int argc, char **argv); - -/**************************************************************/ -/* from wgtpkg-workdir */ - -extern int enter_workdir(int clean); -extern void remove_workdir(); -extern int make_workdir(int reuse); -extern int set_workdir(const char *name, int create); - -/**************************************************************/ -/* from wgtpkg-xmlsec */ - -extern int xmlsec_init(); -extern void xmlsec_shutdown(); -extern int xmlsec_verify(xmlNodePtr node); -extern xmlDocPtr xmlsec_create(int index, const char *key, const char **certs); - -/**************************************************************/ -/* from wgtpkg-zip */ - -/* read (extract) 'zipfile' in current directory */ -extern int zread(const char *zipfile, unsigned long long maxsize); - -/* write (pack) content of the current directory in 'zipfile' */ -extern int zwrite(const char *zipfile); - - -- cgit