2a97a622caf1857bff87c3e09aeba67cec21c6a1
[src/app-framework-main.git] / src / wgtpkg-sign.c
1 /*
2  Copyright 2015, 2016, 2017 IoT.bzh
3
4  author: José Bollo <jose.bollo@iot.bzh>
5
6  Licensed under the Apache License, Version 2.0 (the "License");
7  you may not use this file except in compliance with the License.
8  You may obtain a copy of the License at
9
10      http://www.apache.org/licenses/LICENSE-2.0
11
12  Unless required by applicable law or agreed to in writing, software
13  distributed under the License is distributed on an "AS IS" BASIS,
14  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  See the License for the specific language governing permissions and
16  limitations under the License.
17 */
18
19 #define _GNU_SOURCE
20
21 #include <stdlib.h>
22 #include <stdio.h>
23 #include <unistd.h>
24 #include <limits.h>
25 #include <errno.h>
26 #include <getopt.h>
27 #include <sys/stat.h>
28 #include <fcntl.h>
29
30 #include <libxml/tree.h>
31
32 #include "verbose.h"
33 #include "wgtpkg-files.h"
34 #include "wgtpkg-workdir.h"
35 #include "wgtpkg-digsig.h"
36 #include "wgtpkg-xmlsec.h"
37
38 #if !defined(MAXCERT)
39 #define MAXCERT 20
40 #endif
41 #if !defined(DEFAULT_KEY_FILE)
42 #define DEFAULT_KEY_FILE "key.pem"
43 #endif
44 #if !defined(DEFAULT_CERT_FILE)
45 #define DEFAULT_CERT_FILE "cert.pem"
46 #endif
47
48 const char appname[] = "wgtpkg-sign";
49
50 static unsigned int get_number(const char *value)
51 {
52         char *end;
53         unsigned long int val;
54
55         val = strtoul(value, &end, 10);
56         if (*end || 0 == val || val >= UINT_MAX || *value == '-') {
57                 ERROR("bad number value %s", value);
58                 exit(1);
59         }
60         return (unsigned int)val;
61 }
62
63 static void version()
64 {
65         printf(
66                 "\n"
67                 "  %s  version="AFM_VERSION"\n"
68                 "\n"
69                 "  Copyright (C) 2015, 2016, 2017 \"IoT.bzh\"\n"
70                 "  AFB comes with ABSOLUTELY NO WARRANTY.\n"
71                 "  Licence Apache 2\n"
72                 "\n",
73                 appname
74         );
75 }
76
77 static void usage()
78 {
79         printf(
80                 "usage: %s [-f] [-k keyfile] [-c certfile]... [-d number | -a] directory\n"
81                 "\n"
82                 "   -k keyfile       the private key to use for author signing\n"
83                 "   -c certfile      the certificate(s) to use for author signing\n"
84                 "   -d number        the number of the distributor signature (zero for automatic)\n"
85                 "   -a               the author signature\n"
86                 "   -f               force overwriting\n"
87                 "   -q               quiet\n"
88                 "   -v               verbose\n"
89                 "   -V               version\n"
90                 "\n",
91                 appname
92         );
93 }
94
95 static struct option options[] = {
96         { "key",         required_argument, NULL, 'k' },
97         { "certificate", required_argument, NULL, 'c' },
98         { "distributor", required_argument, NULL, 'd' },
99         { "author",      no_argument,       NULL, 'a' },
100         { "force",       no_argument,       NULL, 'f' },
101         { "help",        no_argument,       NULL, 'h' },
102         { "quiet",       no_argument,       NULL, 'q' },
103         { "verbose",     no_argument,       NULL, 'v' },
104         { "version",     no_argument,       NULL, 'V' },
105         { NULL, 0, NULL, 0 }
106 };
107
108 /* install the widgets of the list */
109 int main(int ac, char **av)
110 {
111         int i, force, ncert, author;
112         unsigned int number;
113         char *keyfile, *certfiles[MAXCERT+1], *directory, **x;
114         struct stat s;
115
116         LOGUSER(appname);
117
118         force = ncert = author = 0;
119         number = UINT_MAX;
120         keyfile = directory = NULL;
121         for (;;) {
122                 i = getopt_long(ac, av, "hfqvVak:c:d:", options, NULL);
123                 if (i < 0)
124                         break;
125                 switch (i) {
126                 case 'c':
127                         if (ncert == MAXCERT) {
128                                 ERROR("maximum count of certificates reached");
129                                 return 1;
130                         }
131                         certfiles[ncert++] = optarg;
132                         continue;
133                 case 'k': x = &keyfile; break;
134                 case 'd': number = get_number(optarg); continue;
135                 case 'f': force = 1; continue;
136                 case 'a': author = 1; continue;
137                 case 'h': usage(); return 0;
138                 case 'V': version(); return 0;
139                 case 'q':
140                         if (verbosity)
141                                 verbosity--;
142                         break;
143                 case 'v':
144                         verbosity++;
145                         break;
146                 case ':':
147                         ERROR("missing argument");
148                         return 1;
149                 default:
150                         ERROR("unrecognized option");
151                         return 1;
152                 }
153                 if (*x != NULL) {
154                         ERROR("option set twice");
155                         return 1;
156                 }
157                 *x = optarg;
158         }
159
160         /* remaining arguments and final checks */
161         if (optind >= ac) {
162                 ERROR("no directory set");
163                 return 1;
164         }
165         directory = av[optind++];
166         if (optind < ac) {
167                 ERROR("extra parameters found");
168                 return 1;
169         }
170
171         /* set default values */
172         if (keyfile == NULL)
173                 keyfile = DEFAULT_KEY_FILE;
174         if (ncert == 0)
175                 certfiles[ncert++] = DEFAULT_CERT_FILE;
176
177         /* check values */
178         if (stat(directory, &s)) {
179                 ERROR("can't find directory %s", directory);
180                 return 1;
181         }
182         if (!S_ISDIR(s.st_mode)) {
183                 ERROR("%s isn't a directory", directory);
184                 return 1;
185         }
186         if (access(keyfile, R_OK) != 0) {
187                 ERROR("can't access private key %s", keyfile);
188                 return 1;
189         }
190         for(i = 0 ; i < ncert ; i++) 
191                 if (access(certfiles[i], R_OK) != 0) {
192                         ERROR("can't access certificate %s", certfiles[i]);
193                         return 1;
194                 }
195
196         /* init xmlsec module */
197         if (xmlsec_init())
198                 return 1;
199
200
201         /* compute absolutes paths */
202 #define rp(x) do { char *p = realpath(x, NULL); if (p != NULL) x = p; else { ERROR("realpath failed for %s",x); return 1; } } while(0)
203         rp(keyfile);
204         for(i = 0 ; i < ncert ; i++) 
205                 rp(certfiles[i]);
206 #undef rp
207
208         /* set and enter the workdir */
209         if (set_workdir(directory, 0))
210                 return 1;
211
212         if (fill_files())
213                 return 1;
214
215         if (author)
216                 number = 0;
217         else if (number == UINT_MAX)
218                 for (number = 1; get_signature(number) != NULL ; number++);
219
220         if (!force && get_signature(number) != NULL) {
221                 ERROR("can't overwrite existing signature %s", get_signature(number)->name);
222                 return 1;
223         }
224
225         NOTICE("-- SIGNING content of directory %s for number %u", directory, number);
226
227         certfiles[ncert] = NULL;
228         return !!create_digsig(number, keyfile, (const char**)certfiles);
229 }
230