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 --- 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 +++++++++++++++++ 18 files changed, 3030 insertions(+) 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 (limited to 'src') 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); + + -- cgit 1.2.3-korg