cd0f369d6bd4663341cd8b8effc1f6243724e45b
[platform/upstream/p11-kit.git] / tools / extract.c
1 /*
2  * Copyright (c) 2013, Red Hat Inc.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *
8  *     * Redistributions of source code must retain the above
9  *       copyright notice, this list of conditions and the
10  *       following disclaimer.
11  *     * Redistributions in binary form must reproduce the
12  *       above copyright notice, this list of conditions and
13  *       the following disclaimer in the documentation and/or
14  *       other materials provided with the distribution.
15  *     * The names of contributors to this software may not be
16  *       used to endorse or promote products derived from this
17  *       software without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
22  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
23  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
24  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
25  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
26  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
27  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
29  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
30  * DAMAGE.
31  *
32  * Author: Stef Walter <stefw@redhat.com>
33  */
34
35 #include "config.h"
36
37 #include "attrs.h"
38 #include "compat.h"
39 #include "debug.h"
40 #include "extract.h"
41 #include "iter.h"
42 #include "message.h"
43 #include "oid.h"
44 #include "pkcs11.h"
45 #include "pkcs11x.h"
46 #include "save.h"
47 #include "tool.h"
48
49 #include <assert.h>
50 #include <ctype.h>
51 #include <getopt.h>
52 #include <stdint.h>
53 #include <stdio.h>
54 #include <stdlib.h>
55 #include <string.h>
56
57 static bool
58 filter_argument (const char *optarg,
59                  P11KitUri **uri,
60                  CK_ATTRIBUTE **match,
61                  int *flags)
62 {
63         CK_ATTRIBUTE *attrs;
64         int ret;
65
66         CK_OBJECT_CLASS vcertificate = CKO_CERTIFICATE;
67         CK_ULONG vauthority = 2;
68         CK_CERTIFICATE_TYPE vx509 = CKC_X_509;
69
70         CK_ATTRIBUTE certificate = { CKA_CLASS, &vcertificate, sizeof (vcertificate) };
71         CK_ATTRIBUTE authority = { CKA_CERTIFICATE_CATEGORY, &vauthority, sizeof (vauthority) };
72         CK_ATTRIBUTE x509 = { CKA_CERTIFICATE_TYPE, &vx509, sizeof (vx509) };
73
74         if (strncmp (optarg, "pkcs11:", 7) == 0) {
75                 if (*uri != NULL) {
76                         p11_message ("only one pkcs11 uri filter may be specified");
77                         return false;
78                 }
79                 *uri = p11_kit_uri_new ();
80                 ret = p11_kit_uri_parse (optarg, P11_KIT_URI_FOR_OBJECT_ON_TOKEN_AND_MODULE, *uri);
81                 if (ret != P11_KIT_URI_OK) {
82                         p11_message ("couldn't parse pkcs11 uri filter: %s", optarg);
83                         return false;
84                 }
85                 return true;
86         }
87
88         if (strcmp (optarg, "ca-anchors") == 0) {
89                 attrs = p11_attrs_build (NULL, &certificate, &authority, &x509, NULL);
90                 *flags |= P11_EXTRACT_ANCHORS | P11_EXTRACT_COLLAPSE;
91
92         } else if (strcmp (optarg, "trust-policy") == 0) {
93                 attrs = p11_attrs_build (NULL, &certificate, &x509, NULL);
94                 *flags |= P11_EXTRACT_ANCHORS | P11_EXTRACT_BLACKLIST | P11_EXTRACT_COLLAPSE;
95
96         } else if (strcmp (optarg, "blacklist") == 0) {
97                 attrs = p11_attrs_build (NULL, &certificate, &x509, NULL);
98                 *flags |= P11_EXTRACT_BLACKLIST | P11_EXTRACT_COLLAPSE;
99
100         } else if (strcmp (optarg, "certificates") == 0) {
101                 attrs = p11_attrs_build (NULL, &certificate, &x509, NULL);
102                 *flags |= P11_EXTRACT_COLLAPSE;
103
104         } else {
105                 p11_message ("unsupported or unrecognized filter: %s", optarg);
106                 return false;
107         }
108
109         if (*match != NULL) {
110                 p11_message ("a conflicting filter has already been specified");
111                 p11_attrs_free (attrs);
112                 return false;
113         }
114
115         *match = attrs;
116         return true;
117 }
118
119 static int
120 is_valid_oid_rough (const char *string)
121 {
122         size_t len;
123
124         len = strlen (string);
125
126         /* Rough check if a valid OID */
127         return (strspn (string, "0123456789.") == len &&
128                 !strstr (string, "..") && string[0] != '\0' && string[0] != '.' &&
129                 string[len - 1] != '.');
130 }
131
132 static bool
133 purpose_argument (const char *optarg,
134                   p11_extract_info *ex)
135 {
136         const char *oid;
137
138         if (strcmp (optarg, "server-auth") == 0) {
139                 oid = P11_OID_SERVER_AUTH_STR;
140         } else if (strcmp (optarg, "client-auth") == 0) {
141                 oid = P11_OID_CLIENT_AUTH_STR;
142         } else if (strcmp (optarg, "email-protection") == 0 || strcmp (optarg, "email") == 0) {
143                 oid = P11_OID_EMAIL_PROTECTION_STR;
144         } else if (strcmp (optarg, "code-signing") == 0) {
145                 oid = P11_OID_CODE_SIGNING_STR;
146         } else if (strcmp (optarg, "ipsec-end-system") == 0) {
147                 oid = P11_OID_IPSEC_END_SYSTEM_STR;
148         } else if (strcmp (optarg, "ipsec-tunnel") == 0) {
149                 oid = P11_OID_IPSEC_TUNNEL_STR;
150         } else if (strcmp (optarg, "ipsec-user") == 0) {
151                 oid = P11_OID_IPSEC_USER_STR;
152         } else if (strcmp (optarg, "time-stamping") == 0) {
153                 oid = P11_OID_TIME_STAMPING_STR;
154         } else if (is_valid_oid_rough (optarg)) {
155                 oid = optarg;
156         } else {
157                 p11_message ("unsupported or unregonized purpose: %s", optarg);
158                 return false;
159         }
160
161         p11_extract_info_limit_purpose (ex, oid);
162         return true;
163 }
164
165 static bool
166 format_argument (const char *optarg,
167                  p11_extract_func *func)
168 {
169         int i;
170
171         /*
172          * Certain formats do not support expressive trust information.
173          * So the caller should limit the supported purposes when asking
174          * for trust information.
175          */
176
177         static const struct {
178                 const char *format;
179                 p11_extract_func func;
180         } formats[] = {
181                 { "x509-file", p11_extract_x509_file, },
182                 { "x509-directory", p11_extract_x509_directory, },
183                 { "pem-bundle", p11_extract_pem_bundle, },
184                 { "pem-directory", p11_extract_pem_directory },
185                 { "java-cacerts", p11_extract_jks_cacerts },
186                 { "openssl-bundle", p11_extract_openssl_bundle },
187                 { "openssl-directory", p11_extract_openssl_directory },
188                 { NULL },
189         };
190
191         if (*func != NULL) {
192                 p11_message ("a format was already specified");
193                 return false;
194         }
195
196         for (i = 0; formats[i].format != NULL; i++) {
197                 if (strcmp (optarg, formats[i].format) == 0) {
198                         *func = formats[i].func;
199                         break;
200                 }
201         }
202
203         if (*func == NULL) {
204                 p11_message ("unsupported or unrecognized format: %s", optarg);
205                 return false;
206         }
207
208         return true;
209 }
210
211 static void
212 limit_modules_if_necessary (CK_FUNCTION_LIST_PTR *modules,
213                             int flags)
214 {
215         char *string;
216         int i, out;
217
218         /*
219          * We only "believe" the CKA_TRUSTED and CKA_X_DISTRUSTED attributes
220          * we get from modules explicitly marked as containing trust-policy.
221          */
222
223         if ((flags & (P11_EXTRACT_ANCHORS | P11_EXTRACT_BLACKLIST)) == 0)
224                 return;
225
226         /* Count the number of modules */
227         for (out = 0; modules[out] != NULL; out++);
228
229         if (out == 0)
230                 return;
231
232         /* TODO: This logic will move once we merge our p11-kit managed code */
233         for (i = 0, out = 0; modules[i] != NULL; i++) {
234                 string = p11_kit_registered_option (modules[i], "trust-policy");
235                 if (string && strcmp (string, "yes") == 0)
236                         modules[out++] = modules[i];
237                 else if (string && strcmp (string, "no") != 0)
238                         p11_message ("skipping module with invalid 'trust-policy' setting: %s", string);
239                 free (string);
240         }
241
242         if (out == 0)
243                 p11_message ("no modules containing trust policy are registered");
244 }
245
246 static bool
247 validate_filter_and_format (p11_extract_info *ex,
248                             p11_extract_func func,
249                             CK_ATTRIBUTE *match)
250 {
251         int i;
252
253         /*
254          * These are the extract functions that contain purpose information.
255          * If we're being asked to export anchors, and the extract function does
256          * not support, and the caller has not specified a purpose, then add a
257          * default purpose to limit to.
258          */
259
260         static p11_extract_func supports_trust_policy[] = {
261                 p11_extract_openssl_bundle,
262                 p11_extract_openssl_directory,
263                 NULL
264         };
265
266         for (i = 0; supports_trust_policy[i] != NULL; i++) {
267                 if (func == supports_trust_policy[i])
268                         return true;
269         }
270
271         if ((ex->flags & P11_EXTRACT_ANCHORS) &&
272             (ex->flags & P11_EXTRACT_BLACKLIST)) {
273                 /*
274                  * If we're extracting *both* anchors and blacklist, then we must have
275                  * a format that can represent the different types of information.
276                  */
277
278                 p11_message ("format does not support trust policy");
279                 return false;
280
281         } else if (ex->flags & P11_EXTRACT_ANCHORS) {
282
283                 /*
284                  * If we're extracting anchors, then we must have either limited the
285                  * purposes, or have a format that can represent multiple purposes.
286                  */
287
288                 if (!ex->limit_to_purposes) {
289                         p11_message ("format does not support multiple purposes, defaulting to 'server-auth'");
290                         p11_extract_info_limit_purpose (ex, P11_OID_SERVER_AUTH_STR);
291                 }
292         }
293
294         return true;
295 }
296
297 int
298 p11_tool_extract (int argc,
299                   char **argv)
300 {
301         p11_extract_func format = NULL;
302         CK_FUNCTION_LIST_PTR *modules;
303         P11KitIter *iter;
304         p11_extract_info ex;
305         CK_ATTRIBUTE *match;
306         P11KitUri *uri;
307         int opt = 0;
308         CK_RV rv;
309         int ret;
310
311         enum {
312                 opt_overwrite = 'f',
313                 opt_verbose = 'v',
314                 opt_quiet = 'q',
315                 opt_help = 'h',
316                 opt_filter = 1000,
317                 opt_purpose,
318                 opt_format,
319                 opt_comment,
320         };
321
322         struct option options[] = {
323                 { "filter", required_argument, NULL, opt_filter },
324                 { "format", required_argument, NULL, opt_format },
325                 { "purpose", required_argument, NULL, opt_purpose },
326                 { "overwrite", no_argument, NULL, opt_overwrite },
327                 { "comment", no_argument, NULL, opt_comment },
328                 { "verbose", no_argument, NULL, opt_verbose },
329                 { "quiet", no_argument, NULL, opt_quiet },
330                 { "help", no_argument, NULL, opt_help },
331                 { 0 },
332         };
333
334         p11_tool_desc usages[] = {
335                 { 0, "usage: p11-kit extract --format=<output> <destination>" },
336                 { opt_filter,
337                   "filter of what to export\n"
338                   "  ca-anchors        certificate anchors (default)\n"
339                   "  blacklist         blacklisted certificates\n"
340                   "  trust-policy      anchors and blacklist\n"
341                   "  certificates      all certificates\n"
342                   "  pkcs11:object=xx  a PKCS#11 URI",
343                   "what",
344                 },
345                 { opt_format,
346                   "format to extract to\n"
347                   "  x509-file         DER X.509 certificate file\n"
348                   "  x509-directory    directory of X.509 certificates\n"
349                   "  pem-bundle        file containing multiple PEM blocks\n"
350                   "  pem-directory     directory of PEM files\n"
351                   "  openssl-bundle    OpenSSL specific PEM bundle\n"
352                   "  openssl-directory directory of OpenSSL specific files\n"
353                   "  java-cacerts      java keystore cacerts file",
354                   "type"
355                 },
356                 { opt_purpose,
357                   "limit to certificates usable for the purpose\n"
358                   "  server-auth       for authenticating servers\n"
359                   "  client-auth       for authenticating clients\n"
360                   "  email             for email protection\n"
361                   "  code-signing      for authenticating signed code\n"
362                   "  1.2.3.4.5...      an arbitrary object id",
363                   "usage"
364                 },
365                 { opt_overwrite, "overwrite output file or directory" },
366                 { opt_comment, "add comments to bundles if possible" },
367                 { opt_verbose, "show verbose debug output", },
368                 { opt_quiet, "supress command output", },
369                 { 0 },
370         };
371
372         match = NULL;
373         uri = NULL;
374
375         p11_extract_info_init (&ex);
376
377         while ((opt = p11_tool_getopt (argc, argv, options)) != -1) {
378                 switch (opt) {
379                 case opt_verbose:
380                 case opt_quiet:
381                         break;
382
383                 case opt_overwrite:
384                         ex.flags |= P11_SAVE_OVERWRITE;
385                         break;
386                 case opt_comment:
387                         ex.flags |= P11_EXTRACT_COMMENT;
388                         break;
389                 case opt_filter:
390                         if (!filter_argument (optarg, &uri, &match, &ex.flags))
391                                 return 2;
392                         break;
393                 case opt_purpose:
394                         if (!purpose_argument (optarg, &ex))
395                                 return 2;
396                         break;
397                 case opt_format:
398                         if (!format_argument (optarg, &format))
399                                 return 2;
400                         break;
401                 case 'h':
402                         p11_tool_usage (usages, options);
403                         return 0;
404                 case '?':
405                         return 2;
406                 default:
407                         assert_not_reached ();
408                         break;
409                 }
410         } while (opt != -1);
411
412         argc -= optind;
413         argv += optind;
414
415         if (argc != 1) {
416                 p11_message ("specify one destination file or directory");
417                 return 2;
418         }
419         ex.destination = argv[0];
420
421         if (!format) {
422                 p11_message ("no output format specified");
423                 return 2;
424         }
425
426         /* If nothing that was useful to enumerate was specified, then bail */
427         if (uri == NULL && match == NULL) {
428                 p11_message ("no filter specified, defaulting to 'ca-anchors'");
429                 filter_argument ("ca-anchors", &uri, &match, &ex.flags);
430         }
431
432         if (!validate_filter_and_format (&ex, format, match))
433                 return 1;
434
435         if (uri && p11_kit_uri_any_unrecognized (uri))
436                 p11_message ("uri contained unrecognized components, nothing will be extracted");
437
438         rv = p11_kit_initialize_registered ();
439         if (rv != CKR_OK) {
440                 p11_message ("couldn't initialize registered modules: %s", p11_kit_strerror (rv));
441                 return 1;
442         }
443
444         modules = p11_kit_registered_modules ();
445         limit_modules_if_necessary (modules, ex.flags);
446
447         iter = p11_kit_iter_new (uri);
448
449         p11_kit_iter_add_callback (iter, p11_extract_info_load_filter, &ex, NULL);
450         p11_kit_iter_add_filter (iter, match, p11_attrs_count (match));
451
452         p11_kit_iter_begin (iter, modules);
453
454         ret = (format) (iter, &ex) ? 0 : 1;
455
456         p11_extract_info_cleanup (&ex);
457         p11_kit_iter_free (iter);
458         p11_kit_uri_free (uri);
459         free (modules);
460
461         p11_kit_finalize_registered ();
462         return ret;
463 }