1 /*
2 * Copyright (C) 2016 Michael Brown <mbrown@fensystems.co.uk>.
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation; either version 2 of the
7 * License, or any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17 * 02110-1301, USA.
18 *
19 * You can also choose to distribute this program under the terms of
20 * the Unmodified Binary Distribution Licence (as given in the file
21 * COPYING.UBDL), provided that you have satisfied its requirements.
22 */
23
24 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
25
26 #include <stdio.h>
27 #include <errno.h>
28 #include <getopt.h>
29 #include <ipxe/x509.h>
30 #include <ipxe/certstore.h>
31 #include <ipxe/image.h>
32 #include <ipxe/command.h>
33 #include <ipxe/parseopt.h>
34 #include <usr/imgmgmt.h>
35 #include <usr/certmgmt.h>
36
37 /** @file
38 *
39 * Certificate management commands
40 *
41 */
42
43 /** "cert<xxx>" options */
44 struct cert_options {
45 /** Certificate subject name */
46 char *name;
47 /** Keep certificate file after parsing */
48 int keep;
49 };
50
51 /** "cert<xxx>" option list */
52 static union {
53 /* "certstore" takes both options */
54 struct option_descriptor certstore[2];
55 /* "certstat" takes only --subject */
56 struct option_descriptor certstat[1];
57 /* "certfree" takes only --subject */
58 struct option_descriptor certfree[1];
59 } opts = {
60 .certstore = {
61 OPTION_DESC ( "subject", 's', required_argument,
62 struct cert_options, name, parse_string ),
63 OPTION_DESC ( "keep", 'k', no_argument,
64 struct cert_options, keep, parse_flag ),
65 },
66 };
67
68 /** A "cert<xxx>" command descriptor */
69 struct cert_command_descriptor {
70 /** Command descriptor */
71 struct command_descriptor cmd;
72 /** Payload
73 *
74 * @v cert X.509 certificate
75 * @ret rc Return status code
76 */
77 int ( * payload ) ( struct x509_certificate *cert );
78 };
79
80 /**
81 * Construct "cert<xxx>" command descriptor
82 *
83 * @v _struct Options structure type
84 * @v _options Option descriptor array
85 * @v _min_args Minimum number of non-option arguments
86 * @v _max_args Maximum number of non-option arguments
87 * @v _usage Command usage
88 * @v _payload Payload method
89 * @ret _command Command descriptor
90 */
91 #define CERT_COMMAND_DESC( _struct, _options, _min_args, _max_args, \
92 _usage, _payload ) \
93 { \
94 .cmd = COMMAND_DESC ( _struct, _options, _min_args, \
95 _max_args, _usage ), \
96 .payload = _payload, \
97 }
98
99 /**
100 * Execute "cert<xxx>" command
101 *
102 * @v argc Argument count
103 * @v argv Argument list
104 * @v certcmd Command descriptor
105 * @ret rc Return status code
106 */
cert_exec(int argc,char ** argv,struct cert_command_descriptor * certcmd)107 static int cert_exec ( int argc, char **argv,
108 struct cert_command_descriptor *certcmd ) {
109 struct command_descriptor *cmd = &certcmd->cmd;
110 struct cert_options opts;
111 struct image *image = NULL;
112 struct x509_certificate *cert;
113 struct x509_certificate *tmp;
114 unsigned int count = 0;
115 size_t offset = 0;
116 int next;
117 int rc;
118
119 /* Parse options */
120 if ( ( rc = parse_options ( argc, argv, cmd, &opts ) ) != 0 )
121 goto err_parse;
122
123 /* Acquire image, if applicable */
124 if ( ( optind < argc ) &&
125 ( ( rc = imgacquire ( argv[optind], 0, &image ) ) != 0 ) )
126 goto err_acquire;
127
128 /* Get first entry in certificate store */
129 tmp = list_first_entry ( &certstore.links, struct x509_certificate,
130 store.list );
131
132 /* Iterate over certificates */
133 while ( 1 ) {
134
135 /* Get next certificate from image or store as applicable */
136 if ( image ) {
137
138 /* Get next certificate from image */
139 if ( offset >= image->len )
140 break;
141 next = image_x509 ( image, offset, &cert );
142 if ( next < 0 ) {
143 rc = next;
144 printf ( "Could not parse certificate: %s\n",
145 strerror ( rc ) );
146 goto err_x509;
147 }
148 offset = next;
149
150 } else {
151
152 /* Get next certificate from store */
153 cert = tmp;
154 if ( ! cert )
155 break;
156 tmp = list_next_entry ( tmp, &certstore.links,
157 store.list );
158 x509_get ( cert );
159 }
160
161 /* Skip non-matching names, if a name was specified */
162 if ( opts.name && ( x509_check_name ( cert, opts.name ) != 0 )){
163 x509_put ( cert );
164 continue;
165 }
166
167 /* Execute payload */
168 if ( ( rc = certcmd->payload ( cert ) ) != 0 ) {
169 x509_put ( cert );
170 goto err_payload;
171 }
172
173 /* Count number of certificates processed */
174 count++;
175
176 /* Drop reference to certificate */
177 x509_put ( cert );
178 }
179
180 /* Fail if a name was specified and no matching certificates
181 * were found.
182 */
183 if ( opts.name && ( count == 0 ) ) {
184 printf ( "\"%s\" : no such certificate\n", opts.name );
185 rc = -ENOENT;
186 goto err_none;
187 }
188
189 err_none:
190 err_payload:
191 err_x509:
192 if ( image && ( ! opts.keep ) )
193 unregister_image ( image );
194 err_acquire:
195 err_parse:
196 return rc;
197 }
198
199 /**
200 * "certstat" payload
201 *
202 * @v cert X.509 certificate
203 * @ret rc Return status code
204 */
certstat_payload(struct x509_certificate * cert)205 static int certstat_payload ( struct x509_certificate *cert ) {
206
207 certstat ( cert );
208 return 0;
209 }
210
211 /** "certstat" command descriptor */
212 static struct cert_command_descriptor certstat_cmd =
213 CERT_COMMAND_DESC ( struct cert_options, opts.certstat, 0, 0, NULL,
214 certstat_payload );
215
216 /**
217 * The "certstat" command
218 *
219 * @v argc Argument count
220 * @v argv Argument list
221 * @ret rc Return status code
222 */
certstat_exec(int argc,char ** argv)223 static int certstat_exec ( int argc, char **argv ) {
224
225 return cert_exec ( argc, argv, &certstat_cmd );
226 }
227
228 /**
229 * "certstore" payload
230 *
231 * @v cert X.509 certificate
232 * @ret rc Return status code
233 */
certstore_payload(struct x509_certificate * cert)234 static int certstore_payload ( struct x509_certificate *cert ) {
235
236 /* Mark certificate as having been added explicitly */
237 cert->flags |= X509_FL_EXPLICIT;
238
239 return 0;
240 }
241
242 /** "certstore" command descriptor */
243 static struct cert_command_descriptor certstore_cmd =
244 CERT_COMMAND_DESC ( struct cert_options, opts.certstore, 0, 1,
245 "[<uri|image>]", certstore_payload );
246
247 /**
248 * The "certstore" command
249 *
250 * @v argc Argument count
251 * @v argv Argument list
252 * @ret rc Return status code
253 */
certstore_exec(int argc,char ** argv)254 static int certstore_exec ( int argc, char **argv ) {
255
256 return cert_exec ( argc, argv, &certstore_cmd );
257 }
258
259 /**
260 * "certfree" payload
261 *
262 * @v cert X.509 certificate
263 * @ret rc Return status code
264 */
certfree_payload(struct x509_certificate * cert)265 static int certfree_payload ( struct x509_certificate *cert ) {
266
267 /* Remove from certificate store */
268 certstore_del ( cert );
269
270 return 0;
271 }
272
273 /** "certfree" command descriptor */
274 static struct cert_command_descriptor certfree_cmd =
275 CERT_COMMAND_DESC ( struct cert_options, opts.certfree, 0, 0, NULL,
276 certfree_payload );
277
278 /**
279 * The "certfree" command
280 *
281 * @v argc Argument count
282 * @v argv Argument list
283 * @ret rc Return status code
284 */
certfree_exec(int argc,char ** argv)285 static int certfree_exec ( int argc, char **argv ) {
286
287 return cert_exec ( argc, argv, &certfree_cmd );
288 }
289
290 /** Certificate management commands */
291 struct command certmgmt_commands[] __command = {
292 {
293 .name = "certstat",
294 .exec = certstat_exec,
295 },
296 {
297 .name = "certstore",
298 .exec = certstore_exec,
299 },
300 {
301 .name = "certfree",
302 .exec = certfree_exec,
303 },
304 };
305