Adds 2017 to copyrights
[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 usage()
64 {
65         printf(
66                 "usage: %s [-f] [-k keyfile] [-c certfile]... [-d number | -a] directory\n"
67                 "\n"
68                 "   -k keyfile       the private key to use for author signing\n"
69                 "   -c certfile      the certificate(s) to use for author signing\n"
70                 "   -d number        the number of the distributor signature (zero for automatic)\n"
71                 "   -a               the author signature\n"
72                 "   -f               force overwriting\n"
73                 "   -q               quiet\n"
74                 "   -v               verbose\n"
75                 "\n",
76                 appname
77         );
78 }
79
80 static struct option options[] = {
81         { "key",         required_argument, NULL, 'k' },
82         { "certificate", required_argument, NULL, 'c' },
83         { "distributor", required_argument, NULL, 'd' },
84         { "author",      no_argument,       NULL, 'a' },
85         { "force",       no_argument,       NULL, 'f' },
86         { "help",        no_argument,       NULL, 'h' },
87         { "quiet",       no_argument,       NULL, 'q' },
88         { "verbose",     no_argument,       NULL, 'v' },
89         { NULL, 0, NULL, 0 }
90 };
91
92 /* install the widgets of the list */
93 int main(int ac, char **av)
94 {
95         int i, force, ncert, author;
96         unsigned int number;
97         char *keyfile, *certfiles[MAXCERT+1], *directory, **x;
98         struct stat s;
99
100         LOGUSER(appname);
101
102         force = ncert = author = 0;
103         number = UINT_MAX;
104         keyfile = directory = NULL;
105         for (;;) {
106                 i = getopt_long(ac, av, "hfqvak:c:d:", options, NULL);
107                 if (i < 0)
108                         break;
109                 switch (i) {
110                 case 'c':
111                         if (ncert == MAXCERT) {
112                                 ERROR("maximum count of certificates reached");
113                                 return 1;
114                         }
115                         certfiles[ncert++] = optarg;
116                         continue;
117                 case 'k': x = &keyfile; break;
118                 case 'd': number = get_number(optarg); continue;
119                 case 'f': force = 1; continue;
120                 case 'a': author = 1; continue;
121                 case 'h': usage(); return 0;
122                 case 'q':
123                         if (verbosity)
124                                 verbosity--;
125                         break;
126                 case 'v':
127                         verbosity++;
128                         break;
129                 case ':':
130                         ERROR("missing argument");
131                         return 1;
132                 default:
133                         ERROR("unrecognized option");
134                         return 1;
135                 }
136                 if (*x != NULL) {
137                         ERROR("option set twice");
138                         return 1;
139                 }
140                 *x = optarg;
141         }
142
143         /* remaining arguments and final checks */
144         if (optind >= ac) {
145                 ERROR("no directory set");
146                 return 1;
147         }
148         directory = av[optind++];
149         if (optind < ac) {
150                 ERROR("extra parameters found");
151                 return 1;
152         }
153
154         /* set default values */
155         if (keyfile == NULL)
156                 keyfile = DEFAULT_KEY_FILE;
157         if (ncert == 0)
158                 certfiles[ncert++] = DEFAULT_CERT_FILE;
159
160         /* check values */
161         if (stat(directory, &s)) {
162                 ERROR("can't find directory %s", directory);
163                 return 1;
164         }
165         if (!S_ISDIR(s.st_mode)) {
166                 ERROR("%s isn't a directory", directory);
167                 return 1;
168         }
169         if (access(keyfile, R_OK) != 0) {
170                 ERROR("can't access private key %s", keyfile);
171                 return 1;
172         }
173         for(i = 0 ; i < ncert ; i++) 
174                 if (access(certfiles[i], R_OK) != 0) {
175                         ERROR("can't access certificate %s", certfiles[i]);
176                         return 1;
177                 }
178
179         /* init xmlsec module */
180         if (xmlsec_init())
181                 return 1;
182
183
184         /* compute absolutes paths */
185 #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)
186         rp(keyfile);
187         for(i = 0 ; i < ncert ; i++) 
188                 rp(certfiles[i]);
189 #undef rp
190
191         /* set and enter the workdir */
192         if (set_workdir(directory, 0))
193                 return 1;
194
195         if (fill_files())
196                 return 1;
197
198         if (author)
199                 number = 0;
200         else if (number == UINT_MAX)
201                 for (number = 1; get_signature(number) != NULL ; number++);
202
203         if (!force && get_signature(number) != NULL) {
204                 ERROR("can't overwrite existing signature %s", get_signature(number)->name);
205                 return 1;
206         }
207
208         NOTICE("-- SIGNING content of directory %s for number %u", directory, number);
209
210         certfiles[ncert] = NULL;
211         return !!create_digsig(number, keyfile, (const char**)certfiles);
212 }
213