]> git.proxmox.com Git - swtpm.git/blame - src/swtpm_localca/swtpm_localca.c
swtpm_localca: Use return from main rather than exit to resolve ASAN issue
[swtpm.git] / src / swtpm_localca / swtpm_localca.c
CommitLineData
e689684c
SB
1/* SPDX-License-Identifier: BSD-3-Clause */
2/*
3 * swtpm_localca.c: A tool for creating TPM 1.2 and TPM 2 certificates localy or using pkcs11
4 *
5 * Author: Stefan Berger, stefanb@linux.ibm.com
6 *
7 * Copyright (c) IBM Corporation, 2021
8 */
9
10#include "config.h"
11
12#include <errno.h>
13#include <fcntl.h>
14#include <getopt.h>
15#include <pwd.h>
16#include <regex.h>
17#include <stdio.h>
18#include <stdlib.h>
19#include <string.h>
20#include <unistd.h>
21#include <sys/file.h>
22#include <sys/stat.h>
23#include <sys/types.h>
24
25#include <glib.h>
26
27#include "swtpm_utils.h"
28#include "swtpm_localca_conf.h"
29#include "swtpm_localca_utils.h"
30
31#define SETUP_TPM2_F 1
32/* for TPM 2 EK */
33#define ALLOW_SIGNING_F 2
34#define DECRYPTION_F 4
35
36/* Default logging goes to stderr */
37gchar *gl_LOGFILE = NULL;
38
39#define LOCALCA_OPTIONS "swtpm-localca.options"
40#define LOCALCA_CONFIG "swtpm-localca.conf"
41
42#if defined __APPLE__
43# define CERTTOOL_NAME "gnutls-certtool"
44#else
45# define CERTTOOL_NAME "certtool"
46#endif
47
48/* initialize the path of the options and config files */
49static int init(gchar **options_file, gchar **config_file)
50{
51 const char *xch = getenv("XDG_CONFIG_HOME");
52 const char *home = getenv("HOME");
53 char path[PATH_MAX];
54 const char *p = NULL;
55 int ret = 0;
56
57 if (xch != NULL &&
58 (p = pathjoin(path, sizeof(path), xch, LOCALCA_OPTIONS, NULL)) != NULL &&
59 access(p, R_OK) == 0) {
60 /* p is good */
61 } else if (home != NULL &&
62 (p = pathjoin(path, sizeof(path), home, ".config", LOCALCA_OPTIONS)) != NULL &&
63 access(p, R_OK) == 0) {
64 /* p is good */
65 } else {
66 p = pathjoin(path, sizeof(path), G_DIR_SEPARATOR_S, SYSCONFDIR, LOCALCA_OPTIONS);
67 }
68 *options_file = g_strdup(p);
69
70 if (xch != NULL &&
71 (p = pathjoin(path, sizeof(path), xch, LOCALCA_CONFIG, NULL)) != NULL &&
72 access(p, R_OK) == 0) {
73 /* p is good */
74 } else if (home != NULL &&
75 (p = pathjoin(path, sizeof(path), home, ".config", LOCALCA_CONFIG)) != NULL &&
76 access(p, R_OK) == 0) {
77 /* p is good */
78 } else {
79 p = pathjoin(path, sizeof(path), G_DIR_SEPARATOR_S, SYSCONFDIR, LOCALCA_CONFIG);
80 }
81 *config_file = g_strdup(p);
82
83 return ret;
84}
85
86/* Run the certtool command line prepared in cmd. Display error message
87 * in case of failure and also display the keyfile if something goes wrong.
88 */
89static int run_certtool(gchar **cmd, gchar **env, const char *msg, gchar *keyfile)
90{
91 g_autofree gchar *standard_error = NULL;
92 gint exit_status;
93 GError *error = NULL;
94 gboolean success;
95
96 success = g_spawn_sync(NULL, cmd, env, G_SPAWN_STDOUT_TO_DEV_NULL, NULL, NULL,
97 NULL, &standard_error, &exit_status, &error);
98 if (!success || exit_status != 0) {
99 logerr(gl_LOGFILE, "%s" , msg);
100 if (keyfile)
101 logerr(gl_LOGFILE, " %s:", keyfile);
102 if (!success) {
103 logerr(gl_LOGFILE, "%s\n", error->message);
104 g_error_free(error);
105 } else {
106 logerr(gl_LOGFILE, "%s\n", standard_error);
107 }
108 return 1;
109 }
110 return 0;
111}
112
113/* Create a root CA key and cert and a local CA key and cert. The latter will be
114 * used for signing the TPM certs.
115 */
116static int create_localca_cert(const gchar *lockfile, const gchar *statedir,
117 const gchar *signkey, const gchar *signkey_password,
118 const gchar *issuercert)
119{
120 int lockfd;
121 int ret = 1;
122 struct stat statbuf;
123 int template1_file_fd = -1;
124 int template2_file_fd = -1;
125 g_autofree gchar *template1_file = NULL;
126 g_autofree gchar *template2_file = NULL;
127 gchar **certtool_env = NULL;
128
129 lockfd = lock_file(lockfile);
130 if (lockfd < 0)
131 return 1;
132
133 if (stat(statedir, &statbuf) != 0) {
134 if (makedir(statedir, "statedir") != 0)
135 goto error;
136 }
137
138 if (access(signkey, R_OK) != 0) {
139 g_autofree gchar *directory = g_path_get_dirname(signkey);
140 g_autofree gchar *cakey = g_strjoin(G_DIR_SEPARATOR_S, directory, "swtpm-localca-rootca-privkey.pem", NULL);
141 g_autofree gchar *cacert = g_strjoin(G_DIR_SEPARATOR_S, directory, "swtpm-localca-rootca-cert.pem", NULL);
142 const gchar *swtpm_rootca_password = g_getenv("SWTPM_ROOTCA_PASSWORD");
143 g_autofree gchar *certtool = g_find_program_in_path(CERTTOOL_NAME);
144 g_autofree gchar **cmd = NULL;
145 g_autofree gchar *fc = NULL;
146 const char *filecontent;
147
148 if (certtool == NULL) {
149 logerr(gl_LOGFILE, "Could not find %s in PATH.\n", CERTTOOL_NAME);
150 goto error;
151 }
152
153 /* generate the root-CA's private key */
154 cmd = concat_arrays(cmd, (gchar*[]){
155 (gchar *)certtool, "--generate-privkey", "--outfile", cakey, NULL
156 }, TRUE);
157 if (swtpm_rootca_password != NULL)
158 cmd = concat_arrays(cmd, (gchar*[]){
159 "--password", (gchar *)swtpm_rootca_password, NULL
160 }, TRUE);
161 if (run_certtool(cmd, certtool_env, "Could not create root-CA key", cakey))
162 goto error;
163
164 if (chmod(cakey, S_IRUSR | S_IWUSR | S_IRGRP) != 0) {
165 logerr(gl_LOGFILE, "Could not chmod %s: %s\n", cakey, strerror(errno));
166 goto error;
167 }
168
169 certtool_env = g_environ_setenv(NULL, "PATH", g_getenv("PATH"), TRUE);
170
171 /* create the root-CA's cert */
172 filecontent = "cn=swtpm-localca-rootca\n"
173 "ca\n"
174 "cert_signing_key\n"
71c353a7 175 "expiration_days = 3600\n";
e689684c
SB
176 template1_file_fd = write_to_tempfile(&template1_file,
177 (const unsigned char *)filecontent, strlen(filecontent));
178 if (template1_file_fd < 0)
179 goto error;
180
181 g_free(cmd);
182 cmd = concat_arrays(NULL,
183 (gchar *[]) {
184 certtool,
185 "--generate-self-signed",
186 "--template", template1_file,
187 "--outfile", cacert,
188 "--load-privkey", cakey,
189 NULL
190 }, FALSE);
191 if (swtpm_rootca_password != NULL)
192 certtool_env = g_environ_setenv(certtool_env, "GNUTLS_PIN", swtpm_rootca_password, TRUE);
193
194 if (run_certtool(cmd, certtool_env, "Could not create root-CA:", NULL))
195 goto error;
196
197 g_free(cmd);
198
199 /* create the intermediate CA's key */
200 cmd = concat_arrays(NULL,
201 (gchar *[]) {
202 certtool, "--generate-privkey", "--outfile", (gchar *)signkey, NULL
203 }, FALSE);
204 if (signkey_password != NULL)
205 cmd = concat_arrays(cmd, (gchar *[]){
206 "--password", (gchar *)signkey_password, NULL},
207 TRUE);
208 if (run_certtool(cmd, certtool_env, "Could not create local-CA key", cakey))
209 goto error;
210
211 if (chmod(signkey, S_IRUSR | S_IWUSR | S_IRGRP) != 0) {
212 logerr(gl_LOGFILE, "Could not chmod %s: %s\n", signkey, strerror(errno));
213 goto error;
214 }
215
216 filecontent = "cn=swtpm-localca\n"
217 "ca\n"
218 "cert_signing_key\n"
71c353a7 219 "expiration_days = 3600\n";
e689684c
SB
220 if (swtpm_rootca_password != NULL && signkey_password != NULL)
221 fc = g_strdup_printf("%spassword = %s\n", filecontent, swtpm_rootca_password);
222 else
223 fc = g_strdup(filecontent);
224
225 template2_file_fd = write_to_tempfile(&template2_file,
226 (const unsigned char *)fc, strlen(fc));
227 if (template2_file_fd < 0)
228 goto error;
229
230 g_free(cmd);
231 cmd = concat_arrays(NULL,
232 (gchar *[]) {
233 certtool,
234 "--generate-certificate",
235 "--template", template2_file,
236 "--outfile", (gchar *)issuercert,
237 "--load-privkey", (gchar *)signkey,
238 "--load-ca-privkey", cakey,
239 "--load-ca-certificate", cacert,
240 NULL
241 }, FALSE);
242 if (signkey_password != NULL)
243 certtool_env = g_environ_setenv(certtool_env, "GNUTLS_PIN", signkey_password, TRUE);
244 else if (swtpm_rootca_password != NULL)
245 certtool_env = g_environ_setenv(certtool_env, "GNUTLS_PIN", swtpm_rootca_password, TRUE);
246
247 if (run_certtool(cmd, certtool_env, "Could not create local-CA:", NULL))
248 goto error;
249 }
250
251 ret = 0;
252
253error:
254 if (template1_file_fd >= 0)
255 close(template1_file_fd);
256 if (template1_file != NULL)
257 unlink(template1_file);
258
259 if (template2_file_fd >= 0)
260 close(template2_file_fd);
261 if (template2_file != NULL)
262 unlink(template2_file);
263 g_strfreev(certtool_env);
264
265 unlock_file(lockfd);
266
267 return ret;
268}
269
270/* Extract the ECC parameters from a string like x=12,y=34,id=secp384r1.
271 * This function returns 1 on error, 2 if the ECC parameters could be extracted
272 * and 0 if no parameters could be extracted (likely a modulus).
273 */
274static gboolean extract_ecc_params(const gchar *ekparams, gchar **ecc_x, gchar **ecc_y, gchar **ecc_curveid)
275{
276 regmatch_t pmatch[5];
277 regex_t preg;
278 int ret;
279
280 if (regcomp(&preg, "x=([0-9A-Fa-f]+),y=([0-9A-Fa-f]+)(,id=([^,]+))?",
281 REG_EXTENDED) != 0) {
282 logerr(gl_LOGFILE, "Internal error: Could not compile regex\n");
283 return 1;
284 }
285
286 ret = 0;
287 if (regexec(&preg, ekparams, 5, pmatch, 0) == 0) {
288 *ecc_x = g_strndup(&ekparams[pmatch[1].rm_so],
289 pmatch[1].rm_eo - pmatch[1].rm_so);
290 *ecc_y = g_strndup(&ekparams[pmatch[2].rm_so],
291 pmatch[2].rm_eo - pmatch[2].rm_so);
292 if (pmatch[4].rm_so > 0 && pmatch[4].rm_eo > 0)
293 *ecc_curveid = g_strndup(&ekparams[pmatch[4].rm_so],
294 pmatch[4].rm_eo - pmatch[4].rm_so);
295 ret = 2;
296 }
297
298 regfree(&preg);
299
300 return ret;
301}
302
303/* Get the next serial number from the certserial file; if it contains
304 * a non-numeric content start over with serial number '1'.
305 */
306static int get_next_serial(const gchar *certserial, const gchar *lockfile,
307 gchar **serial_str)
308{
309 g_autofree gchar *buffer = NULL;
310 size_t buffer_len;
311 unsigned long long serial, serial_n;
312 char *endptr = NULL;
313 int lockfd;
314 int ret = 1;
315
316 lockfd = lock_file(lockfile);
317 if (lockfd < 0)
318 return 1;
319
320 if (access(certserial, R_OK) != 0)
321 write_file(certserial, (unsigned char *)"1", 1);
322 if (read_file(certserial, &buffer, &buffer_len) != 0)
323 goto error;
324
325 if (buffer_len > 0) {
326 serial = strtoull(buffer, &endptr, 10);
327 if (*endptr == '\0') {
328 serial_n = serial + 1;
329 } else {
330 serial_n = 1;
331 }
332 } else {
333 serial_n = 1;
334 }
335 *serial_str = g_strdup_printf("%llu", serial_n);
336 write_file(certserial, (unsigned char *)*serial_str, strlen(*serial_str));
337 ret = 0;
338
339error:
340 unlock_file(lockfd);
341
342 return ret;
343}
344
345/* Create a TPM 1.2 or TPM 2 EK or platform cert */
346static int create_cert(unsigned long flags, const gchar *typ, const gchar *directory,
347 gchar *ekparams, const gchar *vmid, gchar **tpm_spec_params,
348 gchar **tpm_attr_params, const gchar *signkey,
349 const gchar *signkey_password, const gchar *issuercert,
350 const gchar *parentkey_password, gchar **swtpm_cert_env,
351 const gchar *certserial, const gchar *lockfile,
352 const gchar *optsfile)
353{
354 gchar ** optsfile_lines = NULL;
355 g_autofree gchar **options = NULL;
356 g_autofree gchar **keyparams = NULL;
357 g_autofree gchar **cmd = NULL;
358 g_autofree gchar *subject = NULL;
359 g_autofree gchar *ecc_x = NULL;
360 g_autofree gchar *ecc_y = NULL;
361 g_autofree gchar *ecc_curveid = NULL;
362 g_autofree gchar *certfile = NULL;
363 g_autofree gchar *serial_str = NULL;
364 gchar **to_free = NULL;
365 gchar **split;
366 const char *certtype;
367 int signkey_pwd_fd = -1;
368 int parentkey_pwd_fd = -1;
369 g_autofree gchar *signkey_pwd_file = NULL;
370 g_autofree gchar *signkey_pwd_file_param = NULL;
371 g_autofree gchar *parentkey_pwd_file = NULL;
372 g_autofree gchar *parentkey_pwd_file_param = NULL;
373 gboolean success;
374 g_autofree gchar *standard_output = NULL;
375 g_autofree gchar *standard_error = NULL;
376 g_autofree gchar *swtpm_cert_path = NULL;
377 GError *error = NULL;
378 gint exit_status;
379 int ret = 1;
380 size_t i, j;
381
382 swtpm_cert_path = g_find_program_in_path("swtpm_cert");
383 if (swtpm_cert_path == NULL) {
384 logerr(gl_LOGFILE, "Could not find swtpm_cert in PATH.\n");
385 return 1;
386 }
387
388 if (get_next_serial(certserial, lockfile, &serial_str) != 0)
389 return 1;
390
391 /* try to read the optsfile */
392 read_file_lines(optsfile, &optsfile_lines);
393
394 /* split each line from the optsfile and add the stripped parameters to options */
395 for (i = 0; optsfile_lines != NULL && optsfile_lines[i] != NULL; i++) {
396 gchar *chomped = g_strchomp(optsfile_lines[i]);
397 if (strlen(chomped) == 0)
398 continue;
399
400 split = g_strsplit(chomped, " ", -1);
401 for (j = 0; split[j] != NULL; j++) {
402 chomped = g_strchomp(split[j]);
403 if (strlen(chomped) > 0) {
404 gchar *to_add = g_strdup(chomped);
405 options = concat_arrays(options, (gchar *[]){to_add, NULL}, TRUE);
406 /* need to collect this also to free later on */
407 to_free = concat_arrays(to_free, (gchar *[]){to_add, NULL}, TRUE);
408 }
409 }
410 g_strfreev(split);
411 }
412
413 if (vmid != NULL)
414 subject = g_strdup_printf("CN=%s", vmid);
415 else
416 subject = g_strdup("CN=unknown");
417
418 if (flags & SETUP_TPM2_F)
419 options = concat_arrays(options, (gchar *[]){"--tpm2", NULL}, TRUE);
420 else
421 options = concat_arrays(options, (gchar *[]){"--add-header", NULL}, TRUE);
422
423 if (strcmp(typ, "ek") == 0) {
424 if (flags & ALLOW_SIGNING_F)
425 options = concat_arrays(options, (gchar *[]){"--allow-signing", NULL}, TRUE);
426 if (flags & DECRYPTION_F)
427 options = concat_arrays(options, (gchar *[]){"--decryption", NULL}, TRUE);
428 }
429
430 switch (extract_ecc_params(ekparams, &ecc_x, &ecc_y, &ecc_curveid)) {
431 case 1:
432 goto error;
433 case 2:
434 keyparams = concat_arrays((gchar *[]){
435 "--ecc-x", ecc_x,
436 "--ecc-y", ecc_y,
437 NULL
438 },
439 NULL, FALSE);
440 if (ecc_curveid != NULL)
441 keyparams = concat_arrays(keyparams,
442 (gchar *[]){
443 "--ecc-curveid", ecc_curveid,
444 NULL
445 }, TRUE);
446 break;
447 case 0:
448 keyparams = concat_arrays((gchar *[]){
449 "--modulus", ekparams,
450 NULL},
451 NULL, FALSE);
452 break;
453 }
454
455 cmd = concat_arrays((gchar *[]){
456 swtpm_cert_path, "--subject", subject, NULL
457 }, options, FALSE);
458
459 if (signkey_password != NULL) {
460 signkey_pwd_fd = write_to_tempfile(&signkey_pwd_file,
461 (unsigned char *)signkey_password, strlen(signkey_password));
462 if (signkey_pwd_fd < 0)
463 goto error;
464
465 signkey_pwd_file_param = g_strdup_printf("file:%s", signkey_pwd_file);
466 cmd = concat_arrays(cmd, (gchar*[]){"--signkey-pwd", signkey_pwd_file_param, NULL}, TRUE);
467 }
468 if (parentkey_password != NULL) {
469 parentkey_pwd_fd = write_to_tempfile(&parentkey_pwd_file,
470 (unsigned char *)parentkey_password, strlen(parentkey_password));
471 if (parentkey_pwd_fd < 0)
472 goto error;
473
474 parentkey_pwd_file_param = g_strdup_printf("file:%s", parentkey_pwd_file);
475 cmd = concat_arrays(cmd, (gchar*[]){"--parentkey-pwd", parentkey_pwd_file_param, NULL}, TRUE);
476 }
477
478 if (strcmp(typ, "ek") == 0)
479 cmd = concat_arrays(cmd, tpm_spec_params, TRUE);
480
481 cmd = concat_arrays(cmd, tpm_attr_params, TRUE);
482
483 if (strcmp(typ, "platform") == 0) {
484 certfile = g_strjoin(G_DIR_SEPARATOR_S, directory, "platform.cert", NULL);
485 cmd = concat_arrays(cmd,
486 (gchar *[]){
487 "--type", "platform",
488 "--out-cert", certfile,
489 NULL},
490 TRUE);
491 } else {
492 certfile = g_strjoin(G_DIR_SEPARATOR_S, directory, "ek.cert", NULL);
493 cmd = concat_arrays(cmd,
494 (gchar *[]){
495 "--out-cert", certfile,
496 NULL
497 }, TRUE);
498 }
499
500 cmd = concat_arrays(cmd, keyparams, TRUE);
501 cmd = concat_arrays(cmd, (gchar *[]){
502 "--signkey", (gchar *)signkey,
503 "--issuercert", (gchar *)issuercert,
71c353a7 504 "--days", "3600",
e689684c
SB
505 "--serial", (gchar *)serial_str,
506 NULL
507 }, TRUE);
508
509 if (strcmp(typ, "ek") == 0)
510 certtype = "EK";
511 else
512 certtype = "platform";
513#if 0
514 {
515 g_autofree gchar *join = g_strjoinv(" ", cmd);
516 fprintf(stderr, "Starting: %s\n", join);
517 }
518#endif
519 success = g_spawn_sync(NULL, cmd, swtpm_cert_env, G_SPAWN_DEFAULT, NULL, NULL,
520 &standard_output, &standard_error, &exit_status, &error);
521 if (!success) {
522 logerr(gl_LOGFILE, "Could not run swtpm_cert: %s\n", error);
523 g_error_free(error);
524 goto error;
525 }
526 if (exit_status != 0) {
527 logerr(gl_LOGFILE, "Could not create %s certificate locally\n", certtype);
528 logerr(gl_LOGFILE, "%s\n", standard_error);
529 goto error;
530 }
531
532 logit(gl_LOGFILE, "Successfully created %s certificate locally.\n", certtype);
533 ret = 0;
534
535error:
536 g_strfreev(optsfile_lines);
537 g_strfreev(to_free);
538
539 if (signkey_pwd_fd >= 0)
540 close(signkey_pwd_fd);
541 if (signkey_pwd_file)
542 unlink(signkey_pwd_file);
543
544 if (parentkey_pwd_fd >= 0)
545 close(parentkey_pwd_fd);
546 if (parentkey_pwd_file)
547 unlink(parentkey_pwd_file);
548
549 return ret;
550}
551
552static void usage(const char *prgname)
553{
554 printf(
555 "Usage: %s [options]\n"
556 "\n"
557 "The following options are supported:\n"
558 "\n"
559 "--type type The type of certificate to create: 'ek' or 'platform'\n"
560 "--ek key-param The modulus of an RSA key or x=...,y=,... for an EC key\n"
561 "--dir directory The directory to write the resulting certificate into\n"
562 "--vmid vmid The ID of the virtual machine\n"
563 "--optsfile file A file containing options to pass to swtpm_cert\n"
564 "--configfile file A file containing configuration parameters for directory,\n"
565 " signing key and password and certificate to use\n"
566 "--logfile file A file to write a log into\n"
567 "--tpm-spec-family s The implemented spec family, e.g., '2.0'\n"
568 "--tpm-spec-revision i The spec revision of the TPM as integer; e.g., 146\n"
569 "--tpm-spec-level i The spec level of the TPM; must be an integer; e.g. 0\n"
570 "--tpm-manufacturer s The manufacturer of the TPM; e.g., id:00001014\n"
571 "--tpm-model s The model of the TPM; e.g., 'swtpm'\n"
572 "--tpm-version i The (firmware) version of the TPM; e.g., id:20160511\n"
573 "--tpm2 Generate a certificate for a TPM 2\n"
574 "--allow-signing The TPM 2's EK can be used for signing\n"
575 "--decryption The TPM 2's EK can be used for decryption\n"
576 "--help, -h, -? Display this help screen and exit\n"
577 "\n"
578 "\n"
579 "The following environment variables are supported:\n"
580 "\n"
581 "SWTPM_ROOTCA_PASSWORD The root CA's private key password\n"
582 "\n", prgname);
583}
584
585int main(int argc, char *argv[])
586{
587 int opt, option_index = 0;
84c10ba7 588 static const struct option long_options[] = {
e689684c
SB
589 {"type", required_argument, NULL, 't'},
590 {"ek", required_argument, NULL, 'e'},
591 {"dir", required_argument, NULL, 'd'},
592 {"vmid", required_argument, NULL, 'v'},
593 {"optsfile", required_argument, NULL, 'o'},
594 {"configfile", required_argument, NULL, 'c'},
595 {"logfile", required_argument, NULL, 'l'},
596 {"tpm-spec-family", required_argument, NULL, 'f'},
597 {"tpm-spec-revision", required_argument, NULL, 'r'},
598 {"tpm-spec-level", required_argument, NULL, '1'},
599 {"tpm-manufacturer", required_argument, NULL, 'a'},
600 {"tpm-model", required_argument, NULL, 'm'},
601 {"tpm-version", required_argument, NULL, 's'},
602 {"tpm2", no_argument, NULL, '2'},
603 {"allow-signing", no_argument, NULL, 'i'},
604 {"decryption", no_argument, NULL, 'y'},
605 {"help", no_argument, NULL, 'h'},
606 };
607 g_autofree gchar *default_options_file = NULL;
608 g_autofree gchar *default_config_file = NULL;
609 g_autofree gchar *optsfile = NULL;
610 g_autofree gchar *configfile = NULL;
611 unsigned long flags = 0;
612 g_autofree gchar *typ =g_strdup("");
613 g_autofree gchar *ekparams = g_strdup("");
614 g_autofree gchar *directory = g_strdup("."); /* default to current directory */
615 g_autofree gchar *vmid = NULL;
616 g_autofree gchar *lockfile = NULL;
617 g_autofree gchar *statedir = NULL;
618 g_autofree gchar *signkey = NULL;
619 g_autofree gchar *signkey_password = NULL;
620 g_autofree gchar *parentkey_password = NULL;
621 g_autofree gchar *issuercert = NULL;
622 g_autofree gchar *certserial = NULL;
623 gchar **tpm_spec_params = NULL;
624 gchar **tpm_attr_params = NULL;
625 gchar **config_file_lines = NULL;
626 gchar **swtpm_cert_env = NULL;
627 const struct passwd *curr_user;
628 struct stat statbuf;
629 int ret = 1;
630
631 if (init(&default_options_file, &default_config_file) < 0)
632 goto error;
633 optsfile = g_strdup(default_options_file);
634 configfile = g_strdup(default_config_file);
635
636 while ((opt = getopt_long(argc, argv, "h?",
637 long_options, &option_index)) != -1) {
638 switch (opt) {
639 case 't': /* --type */
640 g_free(typ);
641 typ = g_strdup(optarg);
642 break;
643 case 'e': /* --ek */
644 g_free(ekparams);
645 ekparams = g_strdup(optarg);
646 break;
647 case 'd': /* --dir */
648 g_free(directory);
649 directory = g_strdup(optarg);
650 break;
651 case 'v': /* --vmid */
652 g_free(vmid);
653 vmid = g_strdup(optarg);
654 break;
655 case 'o': /* --optsfile */
656 g_free(optsfile);
657 optsfile = g_strdup(optarg);
658 break;
659 case 'c': /* --configfile */
660 g_free(configfile);
661 configfile = g_strdup(optarg);
662 break;
663 case 'l': /* --logfile */
664 g_free(gl_LOGFILE);
665 gl_LOGFILE = g_strdup(optarg);
666 break;
667 case 'f': /* --tpm-spec-family */
668 case 'r': /* --tpm-spec-revision */
669 case '1': /* --tpm-spec-level */
670 tpm_spec_params = concat_arrays(tpm_spec_params,
671 (gchar *[]) {
672 g_strdup_printf("--%s", long_options[option_index].name), g_strdup(optarg), NULL
673 }, TRUE);
674 break;
675 case 'a': /* --tpm-manufacturer */
676 case 'm': /* --tpm-model */
677 case 's': /* --tpm-version */
678 tpm_attr_params = concat_arrays(tpm_attr_params,
679 (gchar *[]) {
680 g_strdup_printf("--%s", long_options[option_index].name), g_strdup(optarg), NULL
681 }, TRUE);
682 break;
683 case '2': /* --tpm2 */
684 flags |= SETUP_TPM2_F;
685 break;
686 case 'i': /* --allow-signing */
687 flags |= ALLOW_SIGNING_F;
688 break;
689 case 'y': /* --decryption */
690 flags |= DECRYPTION_F;
691 break;
692 case '?':
693 case 'h': /* --help */
694 usage(argv[0]);
695 ret = 0;
696 goto out;
697 default:
698 fprintf(stderr, "Unknown option code %d\n", opt);
699 usage(argv[0]);
700 goto error;
701 }
702 }
703
704 curr_user = getpwuid(getuid());
705
706 if (gl_LOGFILE != NULL) {
707 FILE *tmpfile;
708
709 if (stat(gl_LOGFILE, &statbuf) == 0 &&
710 (statbuf.st_mode & S_IFMT) == S_IFLNK) {
711 fprintf(stderr, "Logfile must not be a symlink.\n");
712 goto error;
713 }
714 tmpfile = fopen(gl_LOGFILE, "a"); // do not truncate
715 if (tmpfile == NULL) {
716 fprintf(stderr, "Cannot write to logfile %s.\n", gl_LOGFILE);
717 goto error;
718 }
719 fclose(tmpfile);
720 }
721
722 if (access(optsfile, R_OK) != 0) {
723 logerr(gl_LOGFILE, "Need read rights on options file %s for user %s.\n",
724 optsfile, curr_user ? curr_user->pw_name : "<unknown>");
725 goto error;
726 }
727
728 if (access(configfile, R_OK) != 0) {
729 logerr(gl_LOGFILE, "Need read rights on config file %s for user %s.\n",
730 configfile, curr_user ? curr_user->pw_name : "<unknown>");
731 goto error;
732 }
733
734 if (read_file_lines(configfile, &config_file_lines) != 0)
735 goto error;
736
737 statedir = get_config_value(config_file_lines, "statedir", NULL);
738 if (statedir == NULL) {
739 logerr(gl_LOGFILE, "Missing 'statedir' config value in config file %s.\n", configfile);
740 goto error;
741 }
742 if (makedir(statedir, "statedir") != 0)
743 goto error;
744 if (access(statedir, W_OK | R_OK) != 0) {
745 logerr(gl_LOGFILE, "Need read/write rights on statedir %s for user %s.\n",
746 statedir, curr_user ? curr_user->pw_name : "<unknown>");
747 goto error;
748 }
749
750 lockfile = g_strjoin(G_DIR_SEPARATOR_S, statedir, ".lock.swtpm-localca", NULL);
751 if (stat(lockfile, &statbuf) == 0 &&
752 access(lockfile, W_OK | R_OK) != 0) {
753 logerr(gl_LOGFILE, "Need read/write rights on %s for user %s.\n",
754 lockfile, curr_user ? curr_user->pw_name : "<unknown>");
755 goto error;
756 }
757
758 signkey = get_config_value(config_file_lines, "signingkey", NULL);
759 if (signkey == NULL) {
760 logerr(gl_LOGFILE, "Missing 'signingkey' config value in config file %s.\n",
761 configfile);
762 goto error;
763 }
764
765 if (!g_str_has_prefix(signkey, "tpmkey:file=") &&
766 !g_str_has_prefix(signkey, "tpmkey:uuid=") &&
767 !g_str_has_prefix(signkey, "pkcs11:")) {
768 g_autofree gchar *d = g_path_get_dirname(signkey);
769 if (makedir(d, "signkey") != 0)
770 goto error;
771 }
772
773 signkey_password = get_config_value(config_file_lines, "signingkey_password", NULL);
774 parentkey_password = get_config_value(config_file_lines, "parentkey_password", NULL);
775
776 issuercert = get_config_value(config_file_lines, "issuercert", NULL);
777 if (issuercert == NULL) {
778 logerr(gl_LOGFILE, "Missing 'issuercert' config value in config file %s.\n", configfile);
779 goto error;
780 }
781 {
782 g_autofree gchar *d = g_path_get_dirname(issuercert);
783 if (makedir(d, "issuercert") != 0)
784 goto error;
785 }
786
787 swtpm_cert_env = g_get_environ();
788
789 // TPM keys are GNUTLS URIs...
790 if (g_str_has_prefix(signkey, "tpmkey:file=") || g_str_has_prefix(signkey, "tpmkey:uuid=")) {
2bf7bd18
SB
791 g_autofree gchar *tss_tcsd_hostname = NULL;
792 g_autofree gchar *tss_tcsd_port = NULL;
793
794 tss_tcsd_hostname = get_config_value(config_file_lines,
795 "TSS_TCSD_HOSTNAME", "localhost");
796 tss_tcsd_port = get_config_value(config_file_lines,
797 "TSS_TCSD_PORT", "30003");
e689684c
SB
798 swtpm_cert_env = g_environ_setenv(swtpm_cert_env,
799 "TSS_TCSD_HOSTNAME", tss_tcsd_hostname, TRUE);
800 swtpm_cert_env = g_environ_setenv(swtpm_cert_env,
801 "TSS_TCSD_PORT", tss_tcsd_port, TRUE);
802
803 logit(gl_LOGFILE, "CA uses a GnuTLS TPM key; using TSS_TCSD_HOSTNAME=%s " \
804 "TSS_TCSD_PORT=%s\n", tss_tcsd_hostname, tss_tcsd_port);
805 } else if (g_str_has_prefix(signkey, "pkcs11:")) {
806 gchar *tmp = str_replace(signkey, "\\;", ";"); /* historical reasons ... */
807 g_free(signkey);
808 signkey = tmp;
809
810 if (signkey_password != NULL) {
811 swtpm_cert_env = g_environ_setenv(swtpm_cert_env,
812 "SWTPM_PKCS11_PIN", g_strdup(signkey_password), TRUE);
2c270f04
SB
813 logit(gl_LOGFILE, "CA uses a PKCS#11 key; using SWTPM_PKCS11_PIN\n");
814 } else {
815 g_autofree gchar *swtpm_pkcs11_pin = NULL;
816
817 swtpm_pkcs11_pin = get_config_value(config_file_lines,
818 "SWTPM_PKCS11_PIN", "swtpm-tpmca");
819 swtpm_cert_env = g_environ_setenv(swtpm_cert_env,
820 "SWTPM_PKCS11_PIN", swtpm_pkcs11_pin, TRUE);
821 logit(gl_LOGFILE, "CA uses a PKCS#11 key; using SWTPM_PKCS11_PIN\n");
e689684c
SB
822 }
823 ret = get_config_envvars(config_file_lines, &swtpm_cert_env);
824 if (ret != 0)
825 goto error;
826 } else {
827 if (access(signkey, R_OK) != 0) {
828 if (stat(signkey, &statbuf) == 0) {
829 logerr(gl_LOGFILE, "Need read rights on signing key %s for user %s.\n",
830 signkey, curr_user ? curr_user->pw_name : "<unknown>");
831 goto error;
832 }
833
834 logit(gl_LOGFILE, "Creating root CA and a local CA's signing key and issuer cert.\n");
835 if (create_localca_cert(lockfile, statedir, signkey, signkey_password,
836 issuercert) != 0) {
837 logerr(gl_LOGFILE, "Error creating local CA's signing key and cert.\n");
838 goto error;
839 }
840
841 if (access(signkey, R_OK) != 0) {
842 logerr(gl_LOGFILE, "Need read rights on signing key %s for user %s.\n",
843 signkey, curr_user ? curr_user->pw_name : "<unknown>");
844 goto error;
845 }
846 }
847 }
848
849 if (access(issuercert, R_OK) != 0) {
850 logerr(gl_LOGFILE, "Need read rights on issuer certificate %s for user %s.\n",
851 issuercert, curr_user ? curr_user->pw_name : "<unknown>");
852 goto error;
853 }
854
855 {
856 g_autofree gchar *d = NULL;
857 g_autofree gchar *p = g_strjoin(G_DIR_SEPARATOR_S, statedir, "certserial", NULL);
858
859 certserial = get_config_value(config_file_lines, "certserial", p);
860 d = g_path_get_dirname(certserial);
861 if (makedir(d, "certserial") != 0)
862 goto error;
863 }
864
865 ret = create_cert(flags, typ, directory, ekparams, vmid, tpm_spec_params, tpm_attr_params,
866 signkey, signkey_password, issuercert, parentkey_password, swtpm_cert_env,
867 certserial, lockfile, optsfile);
868
869out:
870error:
871 g_strfreev(tpm_attr_params);
872 g_strfreev(tpm_spec_params);
873
1c3417f7 874 return ret;
e689684c 875}