]>
Commit | Line | Data |
---|---|---|
c125e34b SB |
1 | /* SPDX-License-Identifier: BSD-3-Clause */ |
2 | /* | |
3 | * swtpm_setup.c: Tool to simulate TPM 1.2 & TPM 2 manufacturing | |
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 <getopt.h> | |
14 | #include <grp.h> | |
15 | #include <limits.h> | |
16 | #include <pwd.h> | |
17 | #include <stdlib.h> | |
18 | #include <stdio.h> | |
19 | #include <string.h> | |
20 | #include <unistd.h> | |
21 | #include <sys/stat.h> | |
22 | #include <fcntl.h> | |
23 | #include <sys/types.h> | |
d766b58d | 24 | #include <sys/wait.h> |
c125e34b SB |
25 | |
26 | #include <glib.h> | |
27 | #include <glib/gstdio.h> | |
28 | #include <glib/gprintf.h> | |
29 | ||
30 | #include <glib-object.h> | |
31 | #include <json-glib/json-glib.h> | |
32 | ||
f2aa3274 SB |
33 | #include <libtpms/tpm_nvfilename.h> |
34 | ||
c125e34b SB |
35 | #include "swtpm.h" |
36 | #include "swtpm_setup_conf.h" | |
37 | #include "swtpm_setup_utils.h" | |
38 | #include "swtpm_utils.h" | |
39 | ||
40 | #include <openssl/sha.h> | |
41 | ||
42 | /* default values for passwords */ | |
43 | #define DEFAULT_OWNER_PASSWORD "ooo" | |
44 | #define DEFAULT_SRK_PASSWORD "sss" | |
45 | ||
8422d068 SB |
46 | #define SETUP_CREATE_EK_F (1 << 0) |
47 | #define SETUP_TAKEOWN_F (1 << 1) | |
48 | #define SETUP_EK_CERT_F (1 << 2) | |
49 | #define SETUP_PLATFORM_CERT_F (1 << 3) | |
50 | #define SETUP_LOCK_NVRAM_F (1 << 4) | |
51 | #define SETUP_SRKPASS_ZEROS_F (1 << 5) | |
52 | #define SETUP_OWNERPASS_ZEROS_F (1 << 6) | |
53 | #define SETUP_STATE_OVERWRITE_F (1 << 7) | |
54 | #define SETUP_STATE_NOT_OVERWRITE_F (1 << 8) | |
55 | #define SETUP_TPM2_F (1 << 9) | |
56 | #define SETUP_ALLOW_SIGNING_F (1 << 10) | |
57 | #define SETUP_TPM2_ECC_F (1 << 11) | |
58 | #define SETUP_CREATE_SPK_F (1 << 12) | |
59 | #define SETUP_DISPLAY_RESULTS_F (1 << 13) | |
60 | #define SETUP_DECRYPTION_F (1 << 14) | |
61 | #define SETUP_WRITE_EK_CERT_FILES_F (1 << 15) | |
c125e34b SB |
62 | |
63 | /* default configuration file */ | |
64 | #define SWTPM_SETUP_CONF "swtpm_setup.conf" | |
65 | ||
c125e34b SB |
66 | /* Default logging goes to stderr */ |
67 | gchar *gl_LOGFILE = NULL; | |
68 | ||
69 | #define DEFAULT_RSA_KEYSIZE 2048 | |
70 | ||
71 | ||
72 | static const struct flag_to_certfile { | |
73 | unsigned long flag; | |
74 | const char *filename; | |
75 | const char *type; | |
76 | } flags_to_certfiles[] = { | |
78559edd | 77 | {.flag = SETUP_EK_CERT_F , .filename = "ek.cert", .type = "ek" }, |
c125e34b SB |
78 | {.flag = SETUP_PLATFORM_CERT_F, .filename = "platform.cert", .type = "platform" }, |
79 | {.flag = 0, .filename = NULL, .type = NULL}, | |
80 | }; | |
81 | ||
82 | /* initialize the path of the config_file */ | |
83 | static int init(gchar **config_file) | |
84 | { | |
874c3338 SB |
85 | const gchar *configdir = g_get_user_config_dir(); |
86 | ||
87 | *config_file = g_build_filename(configdir, SWTPM_SETUP_CONF, NULL); | |
88 | if (access(*config_file, R_OK) != 0) { | |
89 | g_free(*config_file); | |
90 | *config_file = g_build_filename(SYSCONFDIR, SWTPM_SETUP_CONF, NULL); | |
c125e34b | 91 | } |
c125e34b | 92 | |
874c3338 | 93 | return 0; |
c125e34b SB |
94 | } |
95 | ||
96 | /* Get the spec and attributes parameters from swtpm */ | |
97 | static int tpm_get_specs_and_attributes(struct swtpm *swtpm, gchar ***params) | |
98 | { | |
99 | int ret; | |
100 | g_autofree gchar *json = NULL; | |
101 | JsonParser *jp = NULL; | |
102 | GError *error = NULL; | |
103 | JsonReader *jr = NULL; | |
104 | JsonNode *root; | |
105 | static const struct parse_rule { | |
106 | const char *node1; | |
107 | const char *node2; | |
108 | gboolean is_int; | |
109 | const char *optname; | |
110 | } parser_rules[7] = { | |
111 | {"TPMSpecification", "family", FALSE, "--tpm-spec-family"}, | |
112 | {"TPMSpecification", "level", TRUE, "--tpm-spec-level"}, | |
113 | {"TPMSpecification", "revision", TRUE, "--tpm-spec-revision"}, | |
114 | {"TPMAttributes", "manufacturer", FALSE, "--tpm-manufacturer"}, | |
115 | {"TPMAttributes", "model", FALSE, "--tpm-model"}, | |
116 | {"TPMAttributes", "version", FALSE, "--tpm-version"}, | |
117 | {NULL, NULL, FALSE, NULL}, | |
118 | }; | |
119 | size_t idx; | |
120 | ||
121 | ret = swtpm->cops->ctrl_get_tpm_specs_and_attrs(swtpm, &json); | |
122 | if (ret != 0) { | |
123 | logerr(gl_LOGFILE, "Could not get the TPM spec and attribute parameters.\n"); | |
124 | return 1; | |
125 | } | |
126 | ||
127 | jp = json_parser_new(); | |
128 | ||
129 | if (!json_parser_load_from_data(jp, json, -1, &error)) { | |
130 | logerr(gl_LOGFILE, "JSON parser failed: %s\n", error->message); | |
131 | g_error_free(error); | |
132 | goto error; | |
133 | } | |
134 | ||
135 | *params = NULL; | |
136 | root = json_parser_get_root(jp); | |
137 | ||
138 | for (idx = 0; parser_rules[idx].node1 != NULL; idx++) { | |
139 | jr = json_reader_new(root); | |
140 | if (json_reader_read_member(jr, parser_rules[idx].node1) && | |
141 | json_reader_read_member(jr, parser_rules[idx].node2)) { | |
142 | gchar *str; | |
143 | ||
144 | if (parser_rules[idx].is_int) | |
145 | str = g_strdup_printf("%ld", (long)json_reader_get_int_value(jr)); | |
146 | else | |
147 | str = g_strdup(json_reader_get_string_value(jr)); | |
148 | ||
149 | *params = concat_arrays(*params, | |
150 | (gchar*[]){ | |
151 | g_strdup(parser_rules[idx].optname), | |
152 | str, | |
153 | NULL | |
154 | }, TRUE); | |
155 | } else { | |
156 | logerr(gl_LOGFILE, "Could not find [%s][%s] in '%s'\n", | |
157 | parser_rules[idx].node1, parser_rules[idx].node2, json); | |
158 | ret = 1; | |
159 | break; | |
160 | } | |
161 | g_object_unref(jr); | |
162 | jr = NULL; | |
163 | } | |
164 | ||
165 | if (ret) { | |
166 | g_strfreev(*params); | |
167 | *params = NULL; | |
168 | g_object_unref(jr); | |
169 | } | |
170 | error: | |
171 | g_object_unref(jp); | |
172 | ||
173 | return ret; | |
174 | } | |
175 | ||
176 | /* Call an external tool to create the certificates */ | |
177 | static int call_create_certs(unsigned long flags, const gchar *configfile, const gchar *certsdir, | |
178 | const gchar *ekparam, const gchar *vmid, struct swtpm *swtpm) | |
179 | { | |
180 | gchar **config_file_lines = NULL; /* must free */ | |
181 | g_autofree gchar *create_certs_tool = NULL; | |
182 | g_autofree gchar *create_certs_tool_config = NULL; | |
183 | g_autofree gchar *create_certs_tool_options = NULL; | |
184 | g_autofree gchar **cmd = NULL; | |
185 | gchar **params = NULL; /* must free */ | |
186 | g_autofree gchar *prgname = NULL; | |
187 | gboolean success; | |
188 | gint exit_status; | |
189 | size_t idx, j; | |
190 | gchar *s; | |
191 | int ret; | |
192 | ||
193 | ret = tpm_get_specs_and_attributes(swtpm, ¶ms); | |
194 | if (ret != 0) | |
195 | goto error; | |
196 | ||
197 | ret = read_file_lines(configfile, &config_file_lines); | |
198 | if (ret != 0) | |
199 | goto error; | |
200 | ||
201 | create_certs_tool = get_config_value(config_file_lines, "create_certs_tool"); | |
202 | create_certs_tool_config = get_config_value(config_file_lines, "create_certs_tool_config"); | |
203 | create_certs_tool_options = get_config_value(config_file_lines, "create_certs_tool_options"); | |
204 | ||
205 | ret = 0; | |
206 | ||
207 | if (create_certs_tool != NULL) { | |
208 | g_autofree gchar *create_certs_tool_path = g_find_program_in_path(create_certs_tool); | |
209 | if (create_certs_tool_path == NULL) { | |
210 | logerr(gl_LOGFILE, "Could not find %s in PATH.\n", create_certs_tool); | |
211 | ret = 1; | |
212 | goto error; | |
213 | } | |
214 | ||
215 | if (flags & SETUP_TPM2_F) { | |
216 | params = concat_arrays(params, | |
217 | (gchar*[]){ | |
218 | g_strdup("--tpm2"), | |
219 | NULL | |
220 | }, TRUE); | |
221 | } | |
222 | cmd = concat_arrays((gchar*[]) { | |
223 | create_certs_tool_path, | |
224 | "--type", "_", /* '_' must be at index '2' ! */ | |
225 | "--ek", (gchar *)ekparam, | |
226 | "--dir", (gchar *)certsdir, | |
227 | NULL | |
228 | }, NULL, FALSE); | |
229 | if (gl_LOGFILE != NULL) | |
230 | cmd = concat_arrays(cmd, (gchar*[]){"--logfile", (gchar *)gl_LOGFILE, NULL}, TRUE); | |
231 | if (vmid != NULL) | |
232 | cmd = concat_arrays(cmd, (gchar*[]){"--vmid", (gchar *)vmid, NULL}, TRUE); | |
233 | cmd = concat_arrays(cmd, params, TRUE); | |
234 | if (create_certs_tool_config != NULL) | |
235 | cmd = concat_arrays(cmd, (gchar*[]){"--configfile", create_certs_tool_config, NULL}, TRUE); | |
236 | if (create_certs_tool_options != NULL) | |
237 | cmd = concat_arrays(cmd, (gchar*[]){"--optsfile", create_certs_tool_options, NULL}, TRUE); | |
238 | ||
239 | s = g_strrstr(create_certs_tool, G_DIR_SEPARATOR_S); | |
240 | if (s) | |
241 | prgname = strdup(&s[1]); | |
242 | else | |
243 | prgname = strdup(create_certs_tool); | |
244 | ||
245 | for (idx = 0; flags_to_certfiles[idx].filename != NULL; idx++) { | |
246 | if (flags & flags_to_certfiles[idx].flag) { | |
247 | g_autofree gchar *standard_output = NULL; | |
c40fceb3 | 248 | g_autofree gchar *standard_error = NULL; |
c125e34b SB |
249 | GError *error = NULL; |
250 | gchar **lines; | |
251 | ||
252 | cmd[2] = (gchar *)flags_to_certfiles[idx].type; /* replaces the "_" above */ | |
253 | ||
254 | s = g_strjoinv(" ", cmd); | |
255 | logit(gl_LOGFILE, " Invoking %s\n", s); | |
256 | g_free(s); | |
257 | ||
c40fceb3 SB |
258 | success = g_spawn_sync(NULL, cmd, NULL, 0, NULL, NULL, |
259 | &standard_output, &standard_error, &exit_status, &error); | |
c125e34b SB |
260 | if (!success) { |
261 | logerr(gl_LOGFILE, "An error occurred running %s: %s\n", | |
262 | create_certs_tool, error->message); | |
263 | g_error_free(error); | |
264 | ret = 1; | |
265 | break; | |
266 | } else if (exit_status != 0) { | |
267 | logerr(gl_LOGFILE, "%s exit with status %d: %s\n", | |
c40fceb3 | 268 | prgname, WEXITSTATUS(exit_status), standard_error); |
c125e34b SB |
269 | ret = 1; |
270 | break; | |
271 | } | |
272 | ||
273 | lines = g_strsplit(standard_output, "\n", -1); | |
274 | for (j = 0; lines[j] != NULL; j++) { | |
275 | if (strlen(lines[j]) > 0) | |
276 | logit(gl_LOGFILE, "%s: %s\n", prgname, lines[j]); | |
277 | } | |
278 | g_strfreev(lines); | |
279 | ||
280 | g_free(standard_output); | |
281 | standard_output = NULL; | |
c40fceb3 SB |
282 | g_free(standard_error); |
283 | standard_error = NULL; | |
c125e34b SB |
284 | } |
285 | } | |
286 | } | |
287 | ||
288 | error: | |
289 | g_strfreev(config_file_lines); | |
290 | g_strfreev(params); | |
291 | ||
292 | return ret; | |
293 | } | |
294 | ||
78559edd SB |
295 | static char *create_ek_certfile_name(const gchar *user_certsdir, const gchar *key_description) |
296 | { | |
297 | g_autofree gchar *filename = g_strdup_printf("ek-%s.crt", key_description); | |
298 | ||
299 | return g_strjoin(G_DIR_SEPARATOR_S, user_certsdir, filename, NULL); | |
300 | } | |
301 | ||
302 | /* | |
303 | * Remove the cert file unless the user wants a copy of it (EK only). | |
304 | */ | |
305 | static int certfile_move_or_delete(unsigned long flags, gboolean is_ek, const gchar *certfile, | |
306 | const gchar *user_certsdir, const gchar *key_description) | |
307 | { | |
308 | g_autofree gchar *content = NULL; | |
309 | g_autofree gchar *cf = NULL; | |
310 | gsize content_length; | |
311 | GError *error = NULL; | |
312 | size_t offset = 0; | |
313 | ||
314 | if (is_ek && (flags & SETUP_WRITE_EK_CERT_FILES_F) && user_certsdir != NULL) { | |
315 | if (!g_file_get_contents(certfile, &content, &content_length, &error)) | |
316 | goto error; | |
317 | ||
318 | cf = create_ek_certfile_name(user_certsdir, key_description); | |
319 | if (!(flags & SETUP_TPM2_F)) { | |
320 | /* A TPM 1.2 certificate has a 7 byte header at the beginning | |
321 | * that we now remove */ | |
322 | if (content_length >= 8) | |
323 | offset = 7; | |
324 | } | |
325 | if (!g_file_set_contents(cf, &content[offset], content_length - offset, | |
326 | &error)) | |
327 | goto error; | |
328 | if (g_chmod(cf, S_IRUSR | S_IWUSR | S_IRGRP) < 0) { | |
329 | logerr(gl_LOGFILE, "Failed to chmod file '%s': %s\n", cf, strerror(errno)); | |
330 | goto error_unlink; | |
331 | } | |
332 | } | |
333 | unlink(certfile); | |
334 | ||
335 | return 0; | |
336 | ||
337 | error: | |
338 | logerr(gl_LOGFILE, "%s\n", error->message); | |
339 | g_error_free(error); | |
340 | ||
341 | error_unlink: | |
342 | unlink(certfile); | |
343 | ||
344 | return 1; | |
345 | } | |
346 | ||
c125e34b SB |
347 | /* Create EK and certificate for a TPM 2 */ |
348 | static int tpm2_create_ek_and_cert(unsigned long flags, const gchar *config_file, | |
349 | const gchar *certsdir, const gchar *vmid, | |
78559edd SB |
350 | unsigned int rsa_keysize, struct swtpm2 *swtpm2, |
351 | const gchar *user_certsdir) | |
c125e34b SB |
352 | { |
353 | g_autofree gchar *filecontent = NULL; | |
354 | size_t filecontent_len; | |
355 | g_autofree gchar *certfile = NULL; | |
356 | g_autofree gchar *ekparam = NULL; | |
78559edd | 357 | const char *key_description; |
c125e34b SB |
358 | size_t idx; |
359 | int ret; | |
360 | ||
361 | if (flags & SETUP_CREATE_EK_F) { | |
362 | ret = swtpm2->ops->create_ek(&swtpm2->swtpm, !!(flags & SETUP_TPM2_ECC_F), rsa_keysize, | |
363 | !!(flags & SETUP_ALLOW_SIGNING_F), | |
364 | !!(flags & SETUP_DECRYPTION_F), | |
365 | !!(flags & SETUP_LOCK_NVRAM_F), | |
78559edd | 366 | &ekparam, &key_description); |
c125e34b SB |
367 | if (ret != 0) |
368 | return 1; | |
369 | } | |
370 | ||
371 | if (flags & (SETUP_EK_CERT_F | SETUP_PLATFORM_CERT_F)) { | |
372 | ret = call_create_certs(flags, config_file, certsdir, ekparam, vmid, &swtpm2->swtpm); | |
373 | if (ret != 0) | |
374 | return 1; | |
375 | ||
376 | for (idx = 0; flags_to_certfiles[idx].filename; idx++) { | |
377 | if (flags & flags_to_certfiles[idx].flag) { | |
378 | g_free(certfile); | |
379 | certfile = g_strjoin(G_DIR_SEPARATOR_S, certsdir, flags_to_certfiles[idx].filename, NULL); | |
380 | ||
381 | g_free(filecontent); | |
382 | filecontent = NULL; | |
383 | ret = read_file(certfile, &filecontent, &filecontent_len); | |
384 | if (ret != 0) | |
385 | return 1; | |
386 | ||
387 | if (flags_to_certfiles[idx].flag == SETUP_EK_CERT_F) { | |
388 | ret = swtpm2->ops->write_ek_cert_nvram(&swtpm2->swtpm, | |
389 | !!(flags & SETUP_TPM2_ECC_F), rsa_keysize, | |
390 | !!(flags & SETUP_LOCK_NVRAM_F), | |
391 | (const unsigned char*)filecontent, filecontent_len); | |
392 | } else { | |
393 | ret = swtpm2->ops->write_platform_cert_nvram(&swtpm2->swtpm, | |
394 | !!(flags & SETUP_LOCK_NVRAM_F), | |
395 | (const unsigned char *)filecontent, filecontent_len); | |
396 | } | |
c125e34b | 397 | |
78559edd SB |
398 | if (ret != 0) { |
399 | unlink(certfile); | |
400 | return 1; | |
401 | } | |
402 | ||
403 | if (certfile_move_or_delete(flags, !!(flags_to_certfiles[idx].flag & SETUP_EK_CERT_F), | |
404 | certfile, user_certsdir, key_description) != 0) | |
c125e34b SB |
405 | return 1; |
406 | } | |
407 | } | |
408 | } | |
409 | ||
410 | return 0; | |
411 | } | |
412 | ||
413 | /* Create endorsement keys and certificates for a TPM 2 */ | |
414 | static int tpm2_create_eks_and_certs(unsigned long flags, const gchar *config_file, | |
415 | const gchar *certsdir, const gchar *vmid, | |
78559edd SB |
416 | unsigned int rsa_keysize, struct swtpm2 *swtpm2, |
417 | const gchar *user_certsdir) | |
c125e34b SB |
418 | { |
419 | int ret; | |
420 | ||
421 | /* 1st key will be RSA */ | |
422 | flags = flags & ~SETUP_TPM2_ECC_F; | |
78559edd SB |
423 | ret = tpm2_create_ek_and_cert(flags, config_file, certsdir, vmid, rsa_keysize, swtpm2, |
424 | user_certsdir); | |
c125e34b SB |
425 | if (ret != 0) |
426 | return 1; | |
427 | ||
428 | /* 2nd key will be an ECC; no more platform cert */ | |
429 | flags = (flags & ~SETUP_PLATFORM_CERT_F) | SETUP_TPM2_ECC_F; | |
78559edd SB |
430 | return tpm2_create_ek_and_cert(flags, config_file, certsdir, vmid, rsa_keysize, swtpm2, |
431 | user_certsdir); | |
c125e34b SB |
432 | } |
433 | ||
a5cc0bf6 SB |
434 | /* Get the default PCR banks from the config file and if nothing can |
435 | be found there use the DEFAULT_PCR_BANKS #define. | |
436 | */ | |
437 | static gchar *get_default_pcr_banks(const gchar *config_file) | |
438 | { | |
439 | g_auto(GStrv) config_file_lines = NULL; | |
440 | gchar *pcr_banks; | |
441 | int ret; | |
442 | ||
443 | ret = read_file_lines(config_file, &config_file_lines); | |
444 | if (ret != 0) | |
445 | return NULL; | |
446 | ||
447 | pcr_banks = get_config_value(config_file_lines, "active_pcr_banks"); | |
448 | if (pcr_banks) | |
449 | g_strstrip(pcr_banks); | |
450 | if (pcr_banks == NULL || strlen(pcr_banks) == 0) { | |
451 | g_free(pcr_banks); | |
452 | pcr_banks = g_strdup(DEFAULT_PCR_BANKS); | |
453 | } | |
454 | return pcr_banks; | |
455 | } | |
456 | ||
87755f8c SB |
457 | /* Activate the given list of PCR banks. If pcr_banks is '-' then leave |
458 | * the configuration as-is. | |
459 | */ | |
460 | static int tpm2_activate_pcr_banks(struct swtpm2 *swtpm2, | |
461 | const gchar *pcr_banks) | |
462 | { | |
463 | g_autofree gchar *active_pcr_banks_join = NULL; | |
464 | g_autofree gchar *all_pcr_banks_join = NULL; | |
465 | g_auto(GStrv) active_pcr_banks = NULL; | |
466 | g_auto(GStrv) all_pcr_banks = NULL; | |
467 | g_auto(GStrv) pcr_banks_l = NULL; | |
468 | struct swtpm *swtpm = &swtpm2->swtpm; | |
469 | int ret = 0; | |
470 | ||
471 | if (g_str_equal(pcr_banks, "-")) | |
472 | return 0; | |
473 | ||
474 | ret = swtpm2->ops->get_all_pcr_banks(swtpm, &all_pcr_banks); | |
475 | if (ret != 0) | |
476 | return ret; | |
477 | ||
478 | pcr_banks_l = g_strsplit(pcr_banks, ",", -1); | |
479 | ret = swtpm2->ops->set_active_pcr_banks(swtpm, pcr_banks_l, all_pcr_banks, | |
480 | &active_pcr_banks); | |
481 | if (ret != 0) | |
482 | return ret; | |
483 | ||
484 | active_pcr_banks_join = g_strjoinv(",", active_pcr_banks); | |
485 | all_pcr_banks_join = g_strjoinv(",", all_pcr_banks); | |
486 | logit(gl_LOGFILE, "Successfully activated PCR banks %s among %s.\n", | |
487 | active_pcr_banks_join, all_pcr_banks_join); | |
488 | ||
489 | return 0; | |
490 | } | |
491 | ||
c125e34b SB |
492 | /* Simulate manufacturing a TPM 2: create keys and certificates */ |
493 | static int init_tpm2(unsigned long flags, gchar **swtpm_prg_l, const gchar *config_file, | |
494 | const gchar *tpm2_state_path, const gchar *vmid, const gchar *pcr_banks, | |
495 | const gchar *swtpm_keyopt, int *fds_to_pass, size_t n_fds_to_pass, | |
20ca1eb3 ET |
496 | unsigned int rsa_keysize, const gchar *certsdir, |
497 | const gchar *user_certsdir) | |
c125e34b | 498 | { |
c125e34b SB |
499 | struct swtpm2 *swtpm2; |
500 | struct swtpm *swtpm; | |
501 | int ret; | |
502 | ||
503 | swtpm2 = swtpm2_new(swtpm_prg_l, tpm2_state_path, swtpm_keyopt, gl_LOGFILE, | |
504 | fds_to_pass, n_fds_to_pass); | |
505 | if (swtpm2 == NULL) | |
506 | return 1; | |
507 | swtpm = &swtpm2->swtpm; | |
508 | ||
509 | ret = swtpm->cops->start(swtpm); | |
510 | if (ret != 0) { | |
511 | logerr(gl_LOGFILE, "Could not start the TPM 2.\n"); | |
512 | goto error; | |
513 | } | |
514 | ||
515 | if ((flags & SETUP_CREATE_SPK_F)) { | |
516 | ret = swtpm2->ops->create_spk(swtpm, !!(flags & SETUP_TPM2_ECC_F), rsa_keysize); | |
517 | if (ret != 0) | |
518 | goto destroy; | |
519 | } | |
520 | ||
78559edd SB |
521 | ret = tpm2_create_eks_and_certs(flags, config_file, certsdir, vmid, rsa_keysize, swtpm2, |
522 | user_certsdir); | |
c125e34b SB |
523 | if (ret != 0) |
524 | goto destroy; | |
525 | ||
87755f8c SB |
526 | ret = tpm2_activate_pcr_banks(swtpm2, pcr_banks); |
527 | if (ret != 0) | |
528 | goto destroy; | |
c125e34b SB |
529 | |
530 | ret = swtpm2->ops->shutdown(swtpm); | |
531 | ||
532 | destroy: | |
533 | swtpm->cops->destroy(swtpm); | |
534 | ||
535 | error: | |
536 | swtpm_free(swtpm); | |
537 | ||
538 | return ret; | |
539 | } | |
540 | ||
541 | /* Create the owner password digest */ | |
542 | static void tpm12_get_ownerpass_digest(unsigned long flags, const gchar *ownerpass, | |
543 | unsigned char ownerpass_digest[SHA_DIGEST_LENGTH]) | |
544 | { | |
545 | const gchar zeros[SHA_DIGEST_LENGTH]= {0, }; | |
546 | size_t len; | |
547 | ||
548 | if (ownerpass == NULL) { | |
549 | if (flags & SETUP_OWNERPASS_ZEROS_F) { | |
550 | ownerpass = zeros; | |
551 | len = sizeof(zeros); | |
552 | } else { | |
553 | ownerpass = DEFAULT_OWNER_PASSWORD; | |
554 | len = strlen(ownerpass); | |
555 | } | |
556 | } else { | |
557 | len = strlen(ownerpass); | |
558 | } | |
559 | SHA1((const unsigned char *)ownerpass, len, ownerpass_digest); | |
560 | } | |
561 | ||
562 | /* Create the SRK password digest */ | |
563 | static void tpm12_get_srkpass_digest(unsigned long flags, const gchar *srkpass, | |
564 | unsigned char srkpass_digest[SHA_DIGEST_LENGTH]) | |
565 | { | |
566 | const gchar zeros[SHA_DIGEST_LENGTH]= {0, }; | |
567 | size_t len; | |
568 | ||
569 | if (srkpass == NULL) { | |
570 | if (flags & SETUP_SRKPASS_ZEROS_F) { | |
571 | srkpass = zeros; | |
572 | len = sizeof(zeros); | |
573 | } else { | |
574 | srkpass = DEFAULT_SRK_PASSWORD; | |
575 | len = strlen(srkpass); | |
576 | } | |
577 | } else { | |
578 | len = strlen(srkpass); | |
579 | } | |
580 | SHA1((const unsigned char *)srkpass, len, srkpass_digest); | |
581 | } | |
582 | ||
583 | /* Take ownership of a TPM 1.2 */ | |
584 | static int tpm12_take_ownership(unsigned long flags, const gchar *ownerpass, | |
585 | const gchar *srkpass, gchar *pubek, size_t pubek_len, | |
586 | struct swtpm12 *swtpm12) | |
587 | { | |
588 | unsigned char ownerpass_digest[SHA_DIGEST_LENGTH]; | |
589 | unsigned char srkpass_digest[SHA_DIGEST_LENGTH]; | |
590 | ||
591 | tpm12_get_ownerpass_digest(flags, ownerpass, ownerpass_digest); | |
592 | tpm12_get_srkpass_digest(flags, srkpass, srkpass_digest); | |
593 | ||
594 | return swtpm12->ops->take_ownership(&swtpm12->swtpm, ownerpass_digest, srkpass_digest, | |
595 | (const unsigned char *)pubek, pubek_len); | |
596 | } | |
597 | ||
598 | /* Create the certificates for a TPM 1.2 */ | |
599 | static int tpm12_create_certs(unsigned long flags, const gchar *config_file, | |
600 | const gchar *certsdir, const gchar *ekparam, | |
78559edd SB |
601 | const gchar *vmid, struct swtpm12 *swtpm12, |
602 | const gchar *user_certsdir) | |
c125e34b SB |
603 | { |
604 | g_autofree gchar *filecontent = NULL; | |
605 | g_autofree gchar *certfile = NULL; | |
606 | gsize filecontent_len; | |
607 | size_t idx; | |
608 | int ret; | |
609 | ||
610 | ret = call_create_certs(flags, config_file, certsdir, ekparam, vmid, &swtpm12->swtpm); | |
611 | if (ret != 0) | |
612 | return 1; | |
613 | ||
614 | for (idx = 0; flags_to_certfiles[idx].filename; idx++) { | |
615 | if (flags & flags_to_certfiles[idx].flag) { | |
616 | g_free(certfile); | |
617 | certfile = g_strjoin(G_DIR_SEPARATOR_S, certsdir, | |
618 | flags_to_certfiles[idx].filename, NULL); | |
619 | ||
620 | g_free(filecontent); | |
621 | filecontent = NULL; | |
622 | ret = read_file(certfile, &filecontent, &filecontent_len); | |
623 | if (ret != 0) | |
624 | return 1; | |
625 | ||
626 | if (flags_to_certfiles[idx].flag == SETUP_EK_CERT_F) { | |
627 | ret = swtpm12->ops->write_ek_cert_nvram(&swtpm12->swtpm, | |
628 | (const unsigned char*)filecontent, filecontent_len); | |
629 | if (ret == 0) | |
630 | logit(gl_LOGFILE, "Successfully created NVRAM area for EK certificate.\n"); | |
631 | } else { | |
632 | ret = swtpm12->ops->write_platform_cert_nvram(&swtpm12->swtpm, | |
633 | (const unsigned char*)filecontent, filecontent_len); | |
634 | if (ret == 0) | |
635 | logit(gl_LOGFILE, "Successfully created NVRAM area for Platform certificate.\n"); | |
636 | } | |
78559edd SB |
637 | |
638 | if (ret != 0) { | |
639 | unlink(certfile); | |
640 | return 1; | |
641 | } | |
642 | ||
643 | if (certfile_move_or_delete(flags, !!(flags_to_certfiles[idx].flag & SETUP_EK_CERT_F), | |
644 | certfile, user_certsdir, "rsa2048") != 0) | |
c125e34b SB |
645 | return 1; |
646 | } | |
647 | } | |
648 | ||
649 | return 0; | |
650 | } | |
651 | ||
652 | /* Simulate manufacturing a TPM 1.2: create keys and certificate and possibly take ownership */ | |
653 | static int init_tpm(unsigned long flags, gchar **swtpm_prg_l, const gchar *config_file, | |
654 | const gchar *tpm_state_path, const gchar *ownerpass, const gchar *srkpass, | |
655 | const gchar *vmid, const gchar *swtpm_keyopt, | |
20ca1eb3 ET |
656 | int *fds_to_pass, size_t n_fds_to_pass, const gchar *certsdir, |
657 | const gchar *user_certsdir) | |
c125e34b | 658 | { |
c125e34b SB |
659 | struct swtpm12 *swtpm12; |
660 | struct swtpm *swtpm; | |
661 | g_autofree gchar *pubek = NULL; | |
662 | size_t pubek_len; | |
663 | int ret = 1; | |
664 | ||
665 | swtpm12 = swtpm12_new(swtpm_prg_l, tpm_state_path, swtpm_keyopt, gl_LOGFILE, | |
666 | fds_to_pass, n_fds_to_pass); | |
667 | if (swtpm12 == NULL) | |
668 | return 1; | |
669 | swtpm = &swtpm12->swtpm; | |
670 | ||
671 | ret = swtpm->cops->start(swtpm); | |
672 | if (ret != 0) { | |
673 | logerr(gl_LOGFILE, "Could not start the TPM 1.2.\n"); | |
674 | goto error; | |
675 | } | |
676 | ||
677 | ret = swtpm12->ops->run_swtpm_bios(swtpm); | |
678 | if (ret != 0) | |
679 | goto destroy; | |
680 | ||
681 | if ((flags & SETUP_CREATE_EK_F)) { | |
682 | ret = swtpm12->ops->create_endorsement_key_pair(swtpm, &pubek, &pubek_len); | |
683 | if (ret != 0) | |
684 | goto destroy; | |
685 | ||
686 | logit(gl_LOGFILE, "Successfully created EK.\n"); | |
687 | ||
688 | /* can only take owernship if created an EK */ | |
689 | if ((flags & SETUP_TAKEOWN_F)) { | |
690 | ret = tpm12_take_ownership(flags, ownerpass, srkpass, pubek, pubek_len, swtpm12); | |
691 | if (ret != 0) | |
692 | goto destroy; | |
693 | ||
694 | logit(gl_LOGFILE, "Successfully took ownership of the TPM.\n"); | |
695 | } | |
696 | ||
697 | /* can only create EK cert if created an EK */ | |
698 | if ((flags & SETUP_EK_CERT_F)) { | |
699 | g_autofree gchar *ekparam = print_as_hex((unsigned char *)pubek, pubek_len); | |
700 | ||
78559edd SB |
701 | ret = tpm12_create_certs(flags, config_file, certsdir, ekparam, vmid, swtpm12, |
702 | user_certsdir); | |
c125e34b SB |
703 | if (ret != 0) |
704 | goto destroy; | |
705 | } | |
706 | } | |
707 | ||
708 | if ((flags & SETUP_LOCK_NVRAM_F)) { | |
709 | ret = swtpm12->ops->nv_lock(swtpm); | |
710 | if (ret == 0) | |
711 | logit(gl_LOGFILE, "Successfully locked NVRAM access.\n"); | |
712 | } | |
713 | ||
714 | destroy: | |
715 | swtpm->cops->destroy(swtpm); | |
716 | ||
717 | error: | |
718 | swtpm_free(swtpm); | |
719 | ||
720 | return ret; | |
721 | } | |
722 | ||
723 | /* Check whether we are allowed to overwrite existing state. | |
724 | * This function returns 2 if the state exists but flag is set to not to overwrite it, | |
725 | * 0 in case we can overwrite it, 1 if the state exists. | |
726 | */ | |
e0d2c0ed ET |
727 | static int check_state_overwrite(gchar **swtpm_prg_l, unsigned int flags, |
728 | const char *tpm_state_path) | |
c125e34b | 729 | { |
e0d2c0ed ET |
730 | gboolean success; |
731 | g_autofree gchar *standard_output = NULL; | |
732 | int exit_status = 0; | |
733 | g_autoptr(GError) error = NULL; | |
734 | g_autofree gchar **argv = NULL; | |
81371f66 | 735 | g_autofree gchar *statearg = g_strdup_printf("backend-uri=%s", tpm_state_path); |
7b7dcbb8 | 736 | g_autofree gchar *logop = NULL; |
e0d2c0ed | 737 | g_autofree gchar **my_argv = NULL; |
c125e34b | 738 | |
e0d2c0ed ET |
739 | my_argv = concat_arrays((gchar*[]) { |
740 | "--print-states", | |
741 | "--tpmstate", | |
81371f66 | 742 | statearg, |
e0d2c0ed ET |
743 | NULL |
744 | }, NULL, FALSE); | |
745 | ||
f2aa3274 | 746 | if (flags & SETUP_TPM2_F) |
e0d2c0ed | 747 | my_argv = concat_arrays(my_argv, (gchar*[]) { "--tpm2", NULL }, TRUE); |
c125e34b | 748 | |
7b7dcbb8 ET |
749 | if (gl_LOGFILE != NULL) { |
750 | logop = g_strdup_printf("file=%s", gl_LOGFILE); | |
751 | my_argv = concat_arrays(my_argv, (gchar*[]){"--log", logop, NULL}, TRUE); | |
752 | } | |
753 | ||
e0d2c0ed | 754 | argv = concat_arrays(swtpm_prg_l, my_argv, FALSE); |
7b7dcbb8 | 755 | |
e0d2c0ed ET |
756 | success = g_spawn_sync(NULL, argv, NULL, G_SPAWN_STDERR_TO_DEV_NULL, NULL, NULL, |
757 | &standard_output, NULL, &exit_status, &error); | |
758 | if (!success) { | |
759 | logerr(gl_LOGFILE, "Could not start swtpm '%s': %s\n", swtpm_prg_l[0], error->message); | |
760 | return 1; | |
761 | } | |
762 | ||
763 | if (exit_status != 0) { | |
764 | logerr(gl_LOGFILE, "%s exit with status %d: %s\n", | |
765 | swtpm_prg_l[0], exit_status, standard_output); | |
c125e34b | 766 | return 1; |
e0d2c0ed | 767 | } |
c125e34b | 768 | |
f2aa3274 | 769 | if (g_strstr_len(standard_output, -1, TPM_PERMANENT_ALL_NAME) != NULL) { |
e0d2c0ed | 770 | /* State file exists */ |
c125e34b SB |
771 | if (flags & SETUP_STATE_NOT_OVERWRITE_F) { |
772 | logit(gl_LOGFILE, "Not overwriting existing state file.\n"); | |
773 | return 2; | |
774 | } | |
775 | if (flags & SETUP_STATE_OVERWRITE_F) | |
776 | return 0; | |
f2aa3274 | 777 | logerr(gl_LOGFILE, "Found existing TPM state '%s'.\n", TPM_PERMANENT_ALL_NAME); |
c125e34b SB |
778 | return 1; |
779 | } | |
780 | ||
781 | return 0; | |
782 | } | |
783 | ||
c125e34b SB |
784 | static void versioninfo(void) |
785 | { | |
786 | printf("TPM emulator setup tool version %d.%d.%d\n", | |
787 | SWTPM_VER_MAJOR, SWTPM_VER_MINOR, SWTPM_VER_MICRO); | |
788 | } | |
789 | ||
790 | static void usage(const char *prgname, const char *default_config_file) | |
791 | { | |
792 | versioninfo(); | |
793 | printf( | |
794 | "Usage: %s [options]\n" | |
795 | "\n" | |
796 | "The following options are supported:\n" | |
797 | "\n" | |
798 | "--runas <user> : Run this program under the given user's account.\n" | |
799 | "\n" | |
a4555cb8 SR |
800 | "--tpm-state <dir>: Path where the TPM's state will be written to;\n" |
801 | " this is a mandatory argument. Prefix with dir:// to" | |
802 | " use directory backend, or file:// to use linear file.\n" | |
c125e34b SB |
803 | "\n" |
804 | "--tpmstate <dir> : This is an alias for --tpm-state <dir>.\n" | |
805 | "\n" | |
806 | "--tpm <executable>\n" | |
807 | " : Path to the TPM executable; this is an optional argument and\n" | |
808 | " by default 'swtpm' in the PATH is used.\n" | |
809 | "\n" | |
810 | "--swtpm_ioctl <executable>\n" | |
811 | " : Path to the swtpm_ioctl executable; this is deprecated\n" | |
812 | " argument.\n" | |
813 | "\n" | |
814 | "--tpm2 : Setup a TPM 2; by default a TPM 1.2 is setup.\n" | |
815 | "\n" | |
816 | "--createek : Create the EK; for a TPM 2 an RSA and ECC EK will be\n" | |
817 | " created\n" | |
818 | "\n" | |
819 | "--allow-signing : Create an EK that can be used for signing;\n" | |
820 | " this option requires --tpm2.\n" | |
821 | " Note: Careful, this option will create a non-standard EK!\n" | |
822 | "\n" | |
823 | "--decryption : Create an EK that can be used for key encipherment;\n" | |
824 | " this is the default unless --allow-signing is given;\n" | |
825 | " this option requires --tpm2.\n" | |
826 | "\n" | |
827 | "--ecc : This option allows to create a TPM 2's ECC key as storage\n" | |
828 | " primary key; a TPM 2 always gets an RSA and an ECC EK key.\n" | |
829 | "\n" | |
830 | "--take-ownership : Take ownership; this option implies --createek\n" | |
831 | " --ownerpass <password>\n" | |
832 | " : Provide custom owner password; default is %s\n" | |
833 | " --owner-well-known:\n" | |
834 | " : Use an owner password of 20 zero bytes\n" | |
835 | " --srkpass <password>\n" | |
836 | " : Provide custom SRK password; default is %s\n" | |
837 | " --srk-well-known:\n" | |
838 | " : Use an SRK password of 20 zero bytes\n" | |
839 | "--create-ek-cert : Create an EK certificate; this implies --createek\n" | |
840 | "\n" | |
841 | "--create-platform-cert\n" | |
842 | " : Create a platform certificate; this implies --create-ek-cert\n" | |
843 | "\n" | |
844 | "--create-spk : Create storage primary key; this requires --tpm2\n" | |
845 | "\n" | |
846 | "--lock-nvram : Lock NVRAM access\n" | |
847 | "\n" | |
848 | "--display : At the end display as much info as possible about the\n" | |
849 | " configuration of the TPM\n" | |
850 | "\n" | |
851 | "--config <config file>\n" | |
852 | " : Path to configuration file; default is %s\n" | |
853 | "\n" | |
854 | "--logfile <logfile>\n" | |
855 | " : Path to log file; default is logging to stderr\n" | |
856 | "\n" | |
857 | "--keyfile <keyfile>\n" | |
858 | " : Path to a key file containing the encryption key for the\n" | |
859 | " TPM to encrypt its persistent state with. The content\n" | |
860 | " must be a 32 hex digit number representing a 128bit AES key.\n" | |
861 | " This parameter will be passed to the TPM using\n" | |
862 | " '--key file=<file>'.\n" | |
863 | "\n" | |
864 | "--keyfile-fd <fd>: Like --keyfile but a file descriptor is given to read the\n" | |
865 | " encryption key from.\n" | |
866 | "\n" | |
867 | "--pwdfile <pwdfile>\n" | |
868 | " : Path to a file containing a passphrase from which the\n" | |
869 | " TPM will derive the 128bit AES key. The passphrase can be\n" | |
870 | " 32 bytes long.\n" | |
871 | " This parameter will be passed to the TPM using\n" | |
872 | " '--key pwdfile=<file>'.\n" | |
873 | "\n" | |
874 | "--pwdfile-fd <fd>: Like --pwdfile but a file descriptor is given to to read\n" | |
875 | " the passphrase from.\n" | |
876 | "\n" | |
877 | "--cipher <cipher>: The cipher to use; either aes-128-cbc or aes-256-cbc;\n" | |
878 | " the default is aes-128-cbc; the same cipher must be\n" | |
879 | " used on the swtpm command line\n" | |
880 | "\n" | |
881 | "--overwrite : Overwrite existing TPM state by re-initializing it; if this\n" | |
882 | " option is not given, this program will return an error if\n" | |
883 | " existing state is detected\n" | |
884 | "\n" | |
885 | "--not-overwrite : Do not overwrite existing TPM state but silently end\n" | |
886 | "\n" | |
887 | "--pcr-banks <banks>\n" | |
888 | " : Set of PCR banks to activate. Provide a comma separated list\n" | |
889 | " like 'sha1,sha256'. '-' to skip and leave all banks active.\n" | |
890 | " Default: %s\n" | |
891 | "\n" | |
892 | "--rsa-keysize <keysize>\n" | |
893 | " : The RSA key size of the EK key; 3072 bits may be supported\n" | |
894 | " if libtpms supports it.\n" | |
895 | " Default: %u\n" | |
896 | "\n" | |
78559edd SB |
897 | "--write-ek-cert-files <directory>\n" |
898 | " : Write EK cert files into the given directory\n" | |
899 | "\n" | |
c125e34b SB |
900 | "--tcsd-system-ps-file <file>\n" |
901 | " : This option is deprecated and has no effect.\n" | |
902 | "\n" | |
903 | "--print-capabilities\n" | |
904 | " : Print JSON formatted capabilites added after v0.1 and exit.\n" | |
905 | "\n" | |
2b607237 SB |
906 | "--create-config-files [[overwrite][,root]]\n" |
907 | " : Create swtpm_setup and swtpm-localca config files for a\n" | |
908 | " user account.\n" | |
909 | " overwrite: overwrite any existing files\n" | |
910 | " root: allow to create files under root's home directory\n" | |
a7254fab | 911 | " skip-if-exist: if any file exists exit without error\n" |
2b607237 | 912 | "\n" |
c125e34b SB |
913 | "--version : Display version and exit\n" |
914 | "\n" | |
634e6705 | 915 | "--help,-h : Display this help screen\n\n", |
c125e34b SB |
916 | prgname, |
917 | DEFAULT_OWNER_PASSWORD, | |
918 | DEFAULT_SRK_PASSWORD, | |
919 | default_config_file, | |
920 | DEFAULT_PCR_BANKS, | |
921 | DEFAULT_RSA_KEYSIZE | |
922 | ); | |
923 | } | |
924 | ||
3eac2477 SB |
925 | static int get_supported_tpm_versions(gchar **swtpm_prg_l, gboolean *swtpm_has_tpm12, |
926 | gboolean *swtpm_has_tpm2) | |
927 | { | |
928 | gboolean success; | |
929 | g_autofree gchar *standard_output = NULL; | |
930 | int exit_status = 0; | |
931 | g_autoptr(GError) error = NULL; | |
932 | g_autofree gchar **argv = NULL; | |
933 | gchar *my_argv[] = { "--print-capabilities", NULL }; | |
7b7dcbb8 | 934 | g_autofree gchar *logop = NULL; |
3eac2477 SB |
935 | |
936 | argv = concat_arrays(swtpm_prg_l, my_argv, FALSE); | |
7b7dcbb8 ET |
937 | |
938 | if (gl_LOGFILE != NULL) { | |
939 | logop = g_strdup_printf("file=%s", gl_LOGFILE); | |
940 | argv = concat_arrays(argv, (gchar*[]){"--log", logop, NULL}, TRUE); | |
941 | } | |
942 | ||
3eac2477 SB |
943 | success = g_spawn_sync(NULL, argv, NULL, G_SPAWN_STDERR_TO_DEV_NULL, NULL, NULL, |
944 | &standard_output, NULL, &exit_status, &error); | |
945 | if (!success) { | |
946 | logerr(gl_LOGFILE, "Could not start swtpm '%s': %s\n", swtpm_prg_l[0], error->message); | |
947 | return 1; | |
948 | } | |
949 | ||
950 | *swtpm_has_tpm12 = g_strstr_len(standard_output, -1, "\"tpm-1.2\"") != NULL; | |
951 | *swtpm_has_tpm2 = g_strstr_len(standard_output, -1, "\"tpm-2.0\"") != NULL; | |
952 | ||
953 | return 0; | |
954 | } | |
955 | ||
c125e34b SB |
956 | /* Get the support RSA key sizes. |
957 | * This function returns an array of ints like the following | |
958 | * - [ 1024, 2048, 3072 ] | |
959 | * - [] (empty array, indicating only 2048 bit RSA keys are supported) | |
960 | */ | |
961 | static int get_rsa_keysizes(unsigned long flags, gchar **swtpm_prg_l, | |
962 | unsigned int **keysizes, size_t *n_keysizes) | |
963 | { | |
964 | gboolean success; | |
965 | gchar *standard_output = NULL; | |
966 | int exit_status = 0; | |
967 | GError *error = NULL; | |
968 | int ret = 1; | |
969 | const gchar *needle = "\"rsa-keysize-"; | |
970 | unsigned int keysize; | |
971 | gchar **argv = NULL; | |
972 | char *p; | |
973 | int n; | |
7b7dcbb8 | 974 | g_autofree gchar *logop = NULL; |
c125e34b SB |
975 | |
976 | *n_keysizes = 0; | |
977 | ||
978 | if (flags & SETUP_TPM2_F) { | |
979 | gchar *my_argv[] = { "--tpm2", "--print-capabilities", NULL }; | |
980 | ||
981 | argv = concat_arrays(swtpm_prg_l, my_argv, FALSE); | |
982 | ||
7b7dcbb8 ET |
983 | if (gl_LOGFILE != NULL) { |
984 | logop = g_strdup_printf("file=%s", gl_LOGFILE); | |
985 | argv = concat_arrays(argv, (gchar*[]){"--log", logop, NULL}, TRUE); | |
986 | } | |
987 | ||
c125e34b SB |
988 | success = g_spawn_sync(NULL, argv, NULL, G_SPAWN_STDERR_TO_DEV_NULL, NULL, NULL, |
989 | &standard_output, NULL, &exit_status, &error); | |
990 | if (!success) { | |
991 | logerr(gl_LOGFILE, "Could not start swtpm '%s': %s\n", swtpm_prg_l[0], error->message); | |
a59eead4 | 992 | g_error_free(error); |
c125e34b SB |
993 | goto error; |
994 | } | |
995 | ||
996 | p = standard_output; | |
997 | /* A crude way of parsing the json output just looking for "rsa-keysize-%u" */ | |
998 | while ((p = g_strstr_len(p, -1, needle)) != NULL) { | |
999 | p += strlen(needle); | |
1000 | n = sscanf(p, "%u\"", &keysize); | |
1001 | if (n == 1) { | |
1002 | *keysizes = g_realloc(*keysizes, (*n_keysizes + 1) * sizeof(unsigned int)); | |
1003 | (*keysizes)[*n_keysizes] = keysize; | |
1004 | (*n_keysizes)++; | |
1005 | } | |
1006 | } | |
1007 | } | |
1008 | ret = 0; | |
1009 | ||
1010 | error: | |
1011 | g_free(argv); | |
1012 | g_free(standard_output); | |
1013 | ||
1014 | return ret; | |
1015 | } | |
1016 | ||
1017 | /* Return the RSA key size capabilities in a NULL-terminated array */ | |
1018 | static int get_rsa_keysize_caps(unsigned long flags, gchar **swtpm_prg_l, | |
1019 | gchar ***keysize_strs) | |
1020 | { | |
1021 | unsigned int *keysizes = NULL; | |
1022 | size_t n_keysizes = 0; | |
1023 | size_t i, j; | |
1024 | int ret = get_rsa_keysizes(flags, swtpm_prg_l, &keysizes, &n_keysizes); | |
1025 | if (ret) | |
1026 | return ret; | |
1027 | ||
1028 | *keysize_strs = g_malloc0(sizeof(char *) * (n_keysizes + 1)); | |
1029 | for (i = 0, j = 0; i < n_keysizes; i++) { | |
1030 | if (keysizes[i] >= 2048) | |
1031 | (*keysize_strs)[j++] = g_strdup_printf("tpm2-rsa-keysize-%u", keysizes[i]); | |
1032 | } | |
1033 | ||
1034 | g_free(keysizes); | |
1035 | ||
1036 | return 0; | |
1037 | } | |
1038 | ||
1039 | /* Print teh JSON object of swtpm_setup's capabilities */ | |
3eac2477 SB |
1040 | static int print_capabilities(char **swtpm_prg_l, gboolean swtpm_has_tpm12, |
1041 | gboolean swtpm_has_tpm2) | |
c125e34b SB |
1042 | { |
1043 | g_autofree gchar *param = g_strdup(""); | |
1044 | gchar **keysize_strs = NULL; | |
1045 | gchar *tmp; | |
1046 | size_t i; | |
1047 | int ret = 0; | |
1048 | ||
1049 | ret = get_rsa_keysize_caps(SETUP_TPM2_F, swtpm_prg_l, &keysize_strs); | |
1050 | if (ret) | |
1051 | return 1; | |
1052 | ||
1053 | for (i = 0; keysize_strs[i] != NULL; i++) { | |
1054 | tmp = g_strdup_printf("%s, \"%s\"", param, keysize_strs[i]); | |
1055 | g_free(param); | |
1056 | param = tmp; | |
1057 | } | |
1058 | ||
1059 | printf("{ \"type\": \"swtpm_setup\", " | |
3eac2477 | 1060 | "\"features\": [ %s%s\"cmdarg-keyfile-fd\", \"cmdarg-pwdfile-fd\", \"tpm12-not-need-root\"" |
2b607237 | 1061 | ", \"cmdarg-write-ek-cert-files\", \"cmdarg-create-config-files\"" |
155ccdf5 MAL |
1062 | "%s ], " |
1063 | "\"version\": \"" VERSION "\" " | |
3eac2477 SB |
1064 | "}\n", |
1065 | swtpm_has_tpm12 ? "\"tpm-1.2\", " : "", | |
1066 | swtpm_has_tpm2 ? "\"tpm-2.0\", " : "", | |
1067 | param); | |
c125e34b SB |
1068 | |
1069 | g_strfreev(keysize_strs); | |
1070 | ||
1071 | return 0; | |
1072 | } | |
1073 | ||
1074 | static int change_process_owner(const char *user) | |
1075 | { | |
1076 | char *endptr; | |
1077 | unsigned long long uid = strtoull(user, &endptr, 10); | |
1078 | gid_t gid; | |
1079 | struct passwd *passwd; | |
1080 | int ret = 1; | |
1081 | ||
1082 | if (*endptr != '\0') { | |
1083 | /* assuming a name */ | |
1084 | passwd = getpwnam(user); | |
1085 | if (passwd == NULL) { | |
1086 | logerr(gl_LOGFILE, "Error: User '%s' does not exist.\n", user); | |
1087 | goto error; | |
1088 | } | |
1089 | ||
1090 | if (initgroups(passwd->pw_name, passwd->pw_gid) != 0) { | |
1091 | logerr(gl_LOGFILE, "Error: initgroups() failed: %s\n", strerror(errno)); | |
1092 | goto error; | |
1093 | } | |
1094 | ||
1095 | gid = passwd->pw_gid; | |
1096 | uid = passwd->pw_uid; | |
1097 | } else { | |
1098 | if (uid > 0xffffffff) { | |
1099 | logerr(gl_LOGFILE, "Error: uid %s outside valid range.\n", user); | |
1100 | goto error; | |
1101 | } | |
1102 | gid = (gid_t)uid; | |
1103 | } | |
1104 | ||
1105 | if (setgid(gid) != 0) { | |
1106 | logerr(gl_LOGFILE, "Error: setgid(%d) failed: %s\n", gid, strerror(errno)); | |
1107 | goto error; | |
1108 | } | |
1109 | ||
1110 | if (setuid(uid) != 0) { | |
1111 | logerr(gl_LOGFILE, "Error: setuid(%d) failed: %s\n", uid, strerror(errno)); | |
1112 | goto error; | |
1113 | } | |
1114 | ||
1115 | ret = 0; | |
1116 | ||
1117 | error: | |
1118 | return ret; | |
1119 | } | |
1120 | ||
2b607237 SB |
1121 | static int handle_create_config_files(const char *optarg) |
1122 | { | |
1123 | g_auto(GStrv) tokens = NULL; | |
1124 | gboolean overwrite = FALSE; | |
1125 | gboolean root_flag = FALSE; | |
a7254fab | 1126 | gboolean skip_if_exist = FALSE; |
2b607237 SB |
1127 | |
1128 | if (optarg) { | |
1129 | tokens = g_strsplit_set(optarg, ", ", -1); | |
1130 | overwrite = g_strv_contains((const gchar **)tokens, "overwrite"); | |
1131 | root_flag = g_strv_contains((const gchar **)tokens, "root"); | |
a7254fab SB |
1132 | skip_if_exist = g_strv_contains((const gchar **)tokens, "skip-if-exist"); |
1133 | if (overwrite && skip_if_exist) { | |
1134 | fprintf(stderr, "Error: overwrite and skip-if-exist cannot both be used\n"); | |
1135 | return 1; | |
1136 | } | |
2b607237 SB |
1137 | } |
1138 | ||
a7254fab | 1139 | return create_config_files(overwrite, root_flag, skip_if_exist); |
2b607237 SB |
1140 | } |
1141 | ||
c125e34b SB |
1142 | int main(int argc, char *argv[]) |
1143 | { | |
1144 | int opt, option_index = 0; | |
a69388c0 | 1145 | static const struct option long_options[] = { |
c125e34b SB |
1146 | {"tpm-state", required_argument, NULL, 't'}, |
1147 | {"tpmstate", required_argument, NULL, 't'}, /* alias for tpm-state */ | |
1148 | {"tpm", required_argument, NULL, 'T'}, | |
1149 | {"swtpm_ioctl", required_argument, NULL, '_'}, | |
1150 | {"tpm2", no_argument, NULL, '2'}, | |
1151 | {"ecc", no_argument, NULL, 'e'}, | |
1152 | {"createek", no_argument, NULL, 'c'}, | |
1153 | {"create-spk", no_argument, NULL, 'C'}, | |
1154 | {"take-ownership", no_argument, NULL, 'o'}, | |
1155 | {"ownerpass", required_argument, NULL, 'O'}, | |
1156 | {"owner-well-known", no_argument, NULL, 'w'}, | |
1157 | {"srkpass", required_argument, NULL, 'S'}, | |
1158 | {"srk-well-known", no_argument, NULL, 's'}, | |
1159 | {"create-ek-cert", no_argument, NULL, 'E'}, | |
1160 | {"create-platform-cert", no_argument, NULL, 'P'}, | |
1161 | {"lock-nvram", no_argument, NULL, 'L'}, | |
1162 | {"display", no_argument, NULL, 'i'}, | |
1163 | {"config", required_argument, NULL, 'f'}, | |
1164 | {"vmid", required_argument, NULL, 'm'}, | |
1165 | {"keyfile", required_argument, NULL, 'x'}, | |
1166 | {"keyfile-fd", required_argument, NULL, 'X'}, | |
1167 | {"pwdfile", required_argument, NULL, 'k'}, | |
1168 | {"pwdfile-fd", required_argument, NULL, 'K'}, | |
1169 | {"cipher", required_argument, NULL, 'p'}, | |
1170 | {"runas", required_argument, NULL, 'r'}, | |
1171 | {"logfile", required_argument, NULL, 'l'}, | |
1172 | {"overwrite", no_argument, NULL, 'v'}, | |
1173 | {"not-overwrite", no_argument, NULL, 'V'}, | |
1174 | {"allow-signing", no_argument, NULL, 'a'}, | |
1175 | {"decryption", no_argument, NULL, 'd'}, | |
1176 | {"pcr-banks", required_argument, NULL, 'b'}, | |
1177 | {"rsa-keysize", required_argument, NULL, 'A'}, | |
78559edd | 1178 | {"write-ek-cert-files", required_argument, NULL, '3'}, |
2b607237 | 1179 | {"create-config-files", optional_argument, NULL, 'u'}, |
c125e34b SB |
1180 | {"tcsd-system-ps-file", required_argument, NULL, 'F'}, |
1181 | {"version", no_argument, NULL, '1'}, | |
1182 | {"print-capabilities", no_argument, NULL, 'y'}, | |
1183 | {"help", no_argument, NULL, 'h'}, | |
1184 | {NULL, 0, NULL, 0} | |
1185 | }; | |
1186 | unsigned long flags = 0; | |
1187 | g_autofree gchar *swtpm_prg = NULL; | |
1188 | g_autofree gchar *tpm_state_path = NULL; | |
81371f66 SR |
1189 | struct swtpm_backend_ops *backend_ops = &swtpm_backend_dir; |
1190 | void *backend_state = NULL; | |
c125e34b SB |
1191 | g_autofree gchar *config_file = NULL; |
1192 | g_autofree gchar *ownerpass = NULL; | |
1193 | gboolean got_ownerpass = FALSE; | |
1194 | g_autofree gchar *srkpass = NULL; | |
1195 | gboolean got_srkpass = FALSE; | |
1196 | g_autofree gchar *vmid = NULL; | |
1197 | g_autofree gchar *pcr_banks = NULL; | |
1198 | gboolean printcapabilities = FALSE; | |
1199 | g_autofree gchar *keyfile = NULL; | |
1200 | long int keyfile_fd = -1; | |
1201 | g_autofree gchar *pwdfile = NULL; | |
1202 | long int pwdfile_fd = -1; | |
1203 | g_autofree gchar *cipher = g_strdup("aes-128-cbc"); | |
1204 | g_autofree gchar *rsa_keysize_str = g_strdup_printf("%d", DEFAULT_RSA_KEYSIZE); | |
1205 | unsigned int rsa_keysize; | |
1206 | g_autofree gchar *swtpm_keyopt = NULL; | |
1207 | g_autofree gchar *runas = NULL; | |
20ca1eb3 | 1208 | g_autofree gchar *certsdir = NULL; |
78559edd | 1209 | g_autofree gchar *user_certsdir = NULL; |
c125e34b SB |
1210 | gchar *tmp; |
1211 | gchar **swtpm_prg_l = NULL; | |
1212 | gchar **tmp_l = NULL; | |
1213 | size_t i, n; | |
1214 | struct stat statbuf; | |
1215 | const struct passwd *curr_user; | |
1216 | struct group *curr_grp; | |
1217 | char *endptr; | |
8def57fa SB |
1218 | gboolean swtpm_has_tpm12 = FALSE; |
1219 | gboolean swtpm_has_tpm2 = FALSE; | |
c125e34b SB |
1220 | int fds_to_pass[1] = { -1 }; |
1221 | unsigned n_fds_to_pass = 0; | |
1222 | char tmpbuffer[200]; | |
1223 | time_t now; | |
1224 | struct tm *tm; | |
1225 | int ret = 1; | |
20ca1eb3 | 1226 | g_autoptr(GError) error = NULL; |
c125e34b SB |
1227 | |
1228 | if (init(&config_file) < 0) | |
1229 | goto error; | |
1230 | ||
1231 | swtpm_prg = g_find_program_in_path("swtpm"); | |
1232 | if (swtpm_prg) { | |
1233 | tmp = g_strconcat(swtpm_prg, " socket", NULL); | |
1234 | g_free(swtpm_prg); | |
1235 | swtpm_prg = tmp; | |
1236 | } | |
1237 | ||
1238 | while ((opt = getopt_long(argc, argv, "h?", | |
1239 | long_options, &option_index)) != -1) { | |
1240 | switch (opt) { | |
1241 | case 't': /* --tpmstate, --tpm-state */ | |
1242 | g_free(tpm_state_path); | |
81371f66 SR |
1243 | if (strncmp(optarg, "dir://", 6) == 0) { |
1244 | tpm_state_path = g_strdup(optarg); | |
6f8b8c62 SR |
1245 | } else if (strncmp(optarg, "file://", 7) == 0) { |
1246 | tpm_state_path = g_strdup(optarg); | |
1247 | backend_ops = &swtpm_backend_file; | |
81371f66 SR |
1248 | } else { |
1249 | /* always prefix with dir:// so we can pass verbatim to swtpm */ | |
1250 | tpm_state_path = g_strconcat("dir://", optarg, NULL); | |
1251 | } | |
c125e34b SB |
1252 | break; |
1253 | case 'T': /* --tpm */ | |
1254 | g_free(swtpm_prg); | |
1255 | swtpm_prg = g_strdup(optarg); | |
1256 | break; | |
1257 | case '_': /* --swtpm_ioctl */ | |
1258 | fprintf(stdout, "Warning: --swtpm_ioctl is deprecated and has no effect."); | |
1259 | break; | |
1260 | case '2': /* --tpm2 */ | |
1261 | flags |= SETUP_TPM2_F; | |
1262 | break; | |
1263 | case 'e': /* --ecc */ | |
1264 | flags |= SETUP_TPM2_ECC_F; | |
1265 | break; | |
1266 | case 'c': /* --createek */ | |
1267 | flags |= SETUP_CREATE_EK_F; | |
1268 | break; | |
1269 | case 'C': /* --create-spk */ | |
1270 | flags |= SETUP_CREATE_SPK_F; | |
1271 | break; | |
1272 | case 'o': /* --take-ownership */ | |
1273 | flags |= SETUP_CREATE_EK_F | SETUP_TAKEOWN_F; | |
1274 | break; | |
1275 | case 'O': /* --ownerpass */ | |
1276 | g_free(ownerpass); | |
1277 | ownerpass = g_strdup(optarg); | |
1278 | got_ownerpass = TRUE; | |
1279 | break; | |
1280 | case 'w': /* --owner-well-known */ | |
1281 | flags |= SETUP_OWNERPASS_ZEROS_F; | |
1282 | got_ownerpass = TRUE; | |
1283 | break; | |
1284 | case 'S': /* --srk-pass */ | |
1285 | g_free(srkpass); | |
1286 | srkpass = g_strdup(optarg); | |
1287 | got_srkpass = TRUE; | |
1288 | break; | |
1289 | case 's': /* --srk-well-known */ | |
1290 | flags |= SETUP_SRKPASS_ZEROS_F; | |
1291 | got_srkpass = TRUE; | |
1292 | break; | |
1293 | case 'E': /* --create-ek-cert */ | |
1294 | flags |= SETUP_CREATE_EK_F | SETUP_EK_CERT_F; | |
1295 | break; | |
1296 | case 'P': /* --create-platform-cert */ | |
1297 | flags |= SETUP_CREATE_EK_F | SETUP_PLATFORM_CERT_F; | |
1298 | break; | |
1299 | case 'L': /* --lock-nvram */ | |
1300 | flags |= SETUP_LOCK_NVRAM_F; | |
1301 | break; | |
1302 | case 'i': /* --display */ | |
1303 | flags |= SETUP_DISPLAY_RESULTS_F; | |
1304 | break; | |
1305 | case 'f': /* --config */ | |
1306 | g_free(config_file); | |
1307 | config_file = g_strdup(optarg); | |
1308 | break; | |
1309 | case 'm': /* --vmid */ | |
1310 | g_free(vmid); | |
1311 | vmid = g_strdup(optarg); | |
1312 | break; | |
1313 | case 'x': /* --keyfile */ | |
1314 | g_free(keyfile); | |
1315 | keyfile = g_strdup(optarg); | |
1316 | break; | |
1317 | case 'X': /* --pwdfile-fd' */ | |
1318 | keyfile_fd = strtoull(optarg, &endptr, 10); | |
1319 | if (*endptr != '\0' && keyfile_fd >= INT_MAX) { | |
1320 | fprintf(stderr, "Invalid file descriptor '%s'\n", optarg); | |
1321 | goto error; | |
1322 | } | |
1323 | break; | |
1324 | case 'k': /* --pwdfile */ | |
1325 | g_free(pwdfile); | |
1326 | pwdfile = g_strdup(optarg); | |
1327 | break; | |
1328 | case 'K': /* --pwdfile-fd' */ | |
1329 | pwdfile_fd = strtoull(optarg, &endptr, 10); | |
1330 | if (*endptr != '\0' || pwdfile_fd >= INT_MAX) { | |
1331 | fprintf(stderr, "Invalid file descriptor '%s'\n", optarg); | |
1332 | goto error; | |
1333 | } | |
1334 | break; | |
1335 | case 'p': /* --cipher */ | |
1336 | g_free(cipher); | |
1337 | cipher = g_strdup(optarg); | |
1338 | break; | |
1339 | case 'r': /* --runas */ | |
1340 | g_free(runas); | |
1341 | runas = g_strdup(optarg); | |
1342 | break; | |
1343 | case 'l': /* --logfile */ | |
1344 | g_free(gl_LOGFILE); | |
1345 | gl_LOGFILE = g_strdup(optarg); | |
1346 | break; | |
1347 | case 'v': /* --overwrite */ | |
1348 | flags |= SETUP_STATE_OVERWRITE_F; | |
1349 | break; | |
1350 | case 'V': /* --not-overwrite */ | |
1351 | flags |= SETUP_STATE_NOT_OVERWRITE_F; | |
1352 | break; | |
1353 | case 'a': /* --allow-signing */ | |
1354 | flags |= SETUP_ALLOW_SIGNING_F; | |
1355 | break; | |
1356 | case 'd': /* --decryption */ | |
1357 | flags |= SETUP_DECRYPTION_F; | |
1358 | break; | |
1359 | case 'b': /* --pcr-banks */ | |
1360 | tmp = g_strconcat(pcr_banks ? pcr_banks: "", | |
1361 | pcr_banks ? "," : "", g_strstrip(optarg), NULL); | |
1362 | g_free(pcr_banks); | |
1363 | pcr_banks = tmp; | |
1364 | break; | |
1365 | case 'A': /* --rsa-keysize */ | |
1366 | g_free(rsa_keysize_str); | |
1367 | rsa_keysize_str = strdup(optarg); | |
1368 | break; | |
78559edd SB |
1369 | case '3': /* --write-ek-cert-files */ |
1370 | g_free(user_certsdir); | |
1371 | user_certsdir = g_strdup(optarg); | |
1372 | flags |= SETUP_WRITE_EK_CERT_FILES_F; | |
1373 | break; | |
2b607237 SB |
1374 | case 'u': |
1375 | if (optarg == NULL && optind < argc && argv[optind][0] != '0') | |
1376 | optarg = argv[optind++]; | |
1377 | ret = handle_create_config_files(optarg); | |
1378 | goto out; | |
c125e34b SB |
1379 | case 'F': /* --tcsd-system-ps-file */ |
1380 | printf("Warning: --tcsd-system-ps-file is deprecated and has no effect."); | |
1381 | break; | |
1382 | case '1': /* --version */ | |
1383 | versioninfo(); | |
1384 | ret = 0; | |
1385 | goto error; | |
1386 | case 'y': /* --print-capabilities */ | |
1387 | printcapabilities = TRUE; | |
1388 | break; | |
1389 | case '?': | |
1390 | case 'h': /* --help */ | |
1391 | usage(argv[0], config_file); | |
634e6705 SB |
1392 | if (opt == 'h') |
1393 | ret = 0; | |
c125e34b SB |
1394 | goto out; |
1395 | default: | |
1396 | fprintf(stderr, "Unknown option code %d\n", opt); | |
1397 | usage(argv[0], config_file); | |
1398 | goto error; | |
1399 | } | |
1400 | } | |
1401 | ||
1402 | if (swtpm_prg == NULL) { | |
1403 | logerr(gl_LOGFILE, | |
1404 | "Default TPM 'swtpm' could not be found and was not provided using --tpm\n."); | |
1405 | goto error; | |
1406 | } | |
1407 | ||
1408 | swtpm_prg_l = split_cmdline(swtpm_prg); | |
1409 | tmp = g_find_program_in_path(swtpm_prg_l[0]); | |
1410 | if (!tmp) { | |
afef7050 | 1411 | logerr(gl_LOGFILE, "swtpm at %s is not an executable.\n", swtpm_prg_l[0]); |
c125e34b SB |
1412 | goto error; |
1413 | } | |
1414 | g_free(tmp); | |
1415 | ||
3eac2477 SB |
1416 | |
1417 | ret = get_supported_tpm_versions(swtpm_prg_l, &swtpm_has_tpm12, &swtpm_has_tpm2); | |
1418 | if (ret != 0) | |
1419 | goto error; | |
1420 | ||
c125e34b | 1421 | if (printcapabilities) { |
3eac2477 | 1422 | ret = print_capabilities(swtpm_prg_l, swtpm_has_tpm12, swtpm_has_tpm2); |
c125e34b SB |
1423 | goto out; |
1424 | } | |
1425 | ||
3eac2477 SB |
1426 | if ((flags & SETUP_TPM2_F) != 0 && !swtpm_has_tpm2) { |
1427 | logerr(gl_LOGFILE, "swtpm at %s does not support TPM 2\n", swtpm_prg_l[0]); | |
1428 | goto error; | |
1429 | } else if ((flags & SETUP_TPM2_F) == 0 && !swtpm_has_tpm12){ | |
1430 | logerr(gl_LOGFILE, "swtpm at %s does not support TPM 1.2\n", swtpm_prg_l[0]); | |
1431 | goto error; | |
1432 | } | |
1433 | ||
c125e34b SB |
1434 | if (runas) { |
1435 | ret = change_process_owner(runas); | |
1436 | if (ret != 0) | |
1437 | goto error; | |
1438 | } | |
1439 | ||
1440 | if (!got_ownerpass) | |
1441 | ownerpass = g_strdup(DEFAULT_OWNER_PASSWORD); | |
1442 | if (!got_srkpass) | |
1443 | srkpass = g_strdup(DEFAULT_SRK_PASSWORD); | |
1444 | ||
c125e34b SB |
1445 | if (gl_LOGFILE != NULL) { |
1446 | FILE *tmpfile; | |
1447 | if (stat(gl_LOGFILE, &statbuf) == 0 && | |
1448 | (statbuf.st_mode & S_IFMT) == S_IFLNK) { | |
1449 | fprintf(stderr, "Logfile must not be a symlink.\n"); | |
1450 | goto error; | |
1451 | } | |
1452 | tmpfile = fopen(gl_LOGFILE, "a"); | |
1453 | if (tmpfile == NULL) { | |
1454 | fprintf(stderr, "Cannot write to logfile %s.\n", gl_LOGFILE); | |
1455 | goto error; | |
1456 | } | |
1457 | fclose(tmpfile); | |
1458 | } | |
1459 | ||
1460 | curr_user = getpwuid(getuid()); | |
1461 | ||
1462 | // Check tpm_state_path directory and access rights | |
1463 | if (tpm_state_path == NULL) { | |
1464 | logerr(gl_LOGFILE, "--tpm-state must be provided\n"); | |
1465 | goto error; | |
1466 | } | |
81371f66 SR |
1467 | |
1468 | backend_state = backend_ops->parse_backend(tpm_state_path); | |
1469 | if (!backend_state) | |
1470 | goto error; | |
1471 | ||
1472 | if (backend_ops->check_access(backend_state, R_OK|W_OK, curr_user) != 0) | |
c125e34b | 1473 | goto error; |
c125e34b | 1474 | |
78559edd SB |
1475 | if ((flags & SETUP_WRITE_EK_CERT_FILES_F)) { |
1476 | if (check_directory_access(user_certsdir, W_OK, curr_user) != 0) | |
1477 | goto error; | |
1478 | } | |
1479 | ||
c125e34b SB |
1480 | if (flags & SETUP_TPM2_F) { |
1481 | if (flags & SETUP_TAKEOWN_F) { | |
1482 | logerr(gl_LOGFILE, "Taking ownership is not supported for TPM 2.\n"); | |
1483 | goto error; | |
1484 | } | |
1485 | } else { | |
1486 | if (flags & SETUP_TPM2_ECC_F) { | |
1487 | logerr(gl_LOGFILE, "--ecc requires --tpm2.\n"); | |
1488 | goto error; | |
1489 | } | |
1490 | if (flags & SETUP_CREATE_SPK_F) { | |
1491 | logerr(gl_LOGFILE, "--create-spk requires --tpm2.\n"); | |
1492 | goto error; | |
1493 | } | |
1494 | } | |
1495 | ||
e0d2c0ed | 1496 | ret = check_state_overwrite(swtpm_prg_l, flags, tpm_state_path); |
c125e34b SB |
1497 | if (ret == 1) { |
1498 | goto error; | |
1499 | } else if (ret == 2) { | |
1500 | ret = 0; | |
1501 | goto out; | |
1502 | } | |
1503 | ||
81371f66 | 1504 | ret = backend_ops->delete_state(backend_state); |
c125e34b SB |
1505 | if (ret != 0) |
1506 | goto error; | |
1507 | ||
c125e34b SB |
1508 | if (access(config_file, R_OK) != 0) { |
1509 | logerr(gl_LOGFILE, "User %s cannot read config file %s.\n", | |
1510 | curr_user ? curr_user->pw_name : "<unknown>", config_file); | |
1511 | goto error; | |
1512 | } | |
1513 | ||
a5cc0bf6 SB |
1514 | /* check pcr_banks; read from config file if not given */ |
1515 | tmp_l = g_strsplit(pcr_banks ? pcr_banks : "", ",", -1); | |
1516 | for (i = 0, n = 0; tmp_l[i]; i++) { | |
1517 | g_strstrip(tmp_l[i]); | |
1518 | n += strlen(tmp_l[i]); | |
1519 | } | |
1520 | g_strfreev(tmp_l); | |
1521 | if (n == 0) { | |
1522 | g_free(pcr_banks); | |
1523 | pcr_banks = get_default_pcr_banks(config_file); | |
1524 | } | |
1525 | ||
c125e34b SB |
1526 | if (cipher != NULL) { |
1527 | if (strcmp(cipher, "aes-128-cbc") != 0 && | |
1528 | strcmp(cipher, "aes-cbc") != 0 && | |
1529 | strcmp(cipher, "aes-256-cbc") != 0) { | |
1530 | logerr(gl_LOGFILE, "Unsupported cipher %s.\n", cipher); | |
1531 | goto error; | |
1532 | } | |
1533 | tmp = g_strdup_printf(",mode=%s", cipher); | |
1534 | g_free(cipher); | |
1535 | cipher = tmp; | |
1536 | } | |
1537 | ||
1538 | if (keyfile != NULL) { | |
1539 | if (access(keyfile, R_OK) != 0) { | |
1540 | logerr(gl_LOGFILE, "User %s cannot read keyfile %s.\n", | |
1541 | curr_user ? curr_user->pw_name : "<unknown>", keyfile); | |
1542 | goto error; | |
1543 | } | |
1544 | swtpm_keyopt = g_strdup_printf("file=%s%s", keyfile, cipher); | |
1545 | logit(gl_LOGFILE, " The TPM's state will be encrypted with a provided key.\n"); | |
1546 | } else if (pwdfile != NULL) { | |
1547 | if (access(pwdfile, R_OK) != 0) { | |
1548 | logerr(gl_LOGFILE, "User %s cannot read passphrase file %s.\n", | |
1549 | curr_user ? curr_user->pw_name : "<unknown>", pwdfile); | |
1550 | goto error; | |
1551 | } | |
1552 | swtpm_keyopt = g_strdup_printf("pwdfile=%s%s", pwdfile, cipher); | |
1553 | logit(gl_LOGFILE, " The TPM's state will be encrypted using a key derived from a passphrase.\n"); | |
1554 | } else if (keyfile_fd >= 0) { | |
1555 | fds_to_pass[n_fds_to_pass++] = keyfile_fd; | |
1556 | swtpm_keyopt = g_strdup_printf("fd=%ld%s", keyfile_fd, cipher); | |
1557 | logit(gl_LOGFILE, " The TPM's state will be encrypted with a provided key (fd).\n"); | |
1558 | } else if (pwdfile_fd >= 0) { | |
1559 | fds_to_pass[n_fds_to_pass++] = pwdfile_fd; | |
1560 | swtpm_keyopt = g_strdup_printf("pwdfd=%ld%s", pwdfile_fd, cipher); | |
1561 | logit(gl_LOGFILE, " The TPM's state will be encrypted using a key derived from a passphrase (fd).\n"); | |
1562 | } | |
1563 | ||
1564 | if (strcmp(rsa_keysize_str, "max") == 0) { | |
1565 | unsigned int *keysizes = NULL; | |
1566 | size_t n_keysizes; | |
1567 | ||
1568 | ret = get_rsa_keysizes(flags, swtpm_prg_l, &keysizes, &n_keysizes); | |
1569 | if (ret) | |
1570 | goto error; | |
1571 | g_free(rsa_keysize_str); | |
1572 | if (n_keysizes > 0) { | |
1573 | /* last one is the biggest one */ | |
1574 | rsa_keysize_str = g_strdup_printf("%u", keysizes[n_keysizes - 1]); | |
1575 | } else { | |
1576 | rsa_keysize_str = g_strdup("2048"); | |
1577 | } | |
1578 | g_free(keysizes); | |
1579 | } | |
1580 | if (strcmp(rsa_keysize_str, "2048") == 0 || strcmp(rsa_keysize_str, "3072") == 0) { | |
1581 | unsigned int *keysizes = NULL; | |
1582 | size_t n_keysizes; | |
1583 | gboolean found = FALSE; | |
1584 | ||
1585 | ret = get_rsa_keysizes(flags, swtpm_prg_l, &keysizes, &n_keysizes); | |
1586 | if (ret) | |
1587 | goto error; | |
1588 | ||
1589 | rsa_keysize = strtoull(rsa_keysize_str, NULL, 10); | |
1590 | for (i = 0; i < n_keysizes && found == FALSE; i++) | |
1591 | found = (keysizes[i] == rsa_keysize); | |
1592 | if (!found && rsa_keysize != 2048) { | |
1593 | logerr(gl_LOGFILE, "%u bit RSA keys are not supported by libtpms.\n", rsa_keysize); | |
1594 | goto error; | |
1595 | } | |
1596 | g_free(keysizes); | |
1597 | } else { | |
1598 | logit(gl_LOGFILE, "Unsupported RSA key size %s.\n", rsa_keysize_str); | |
1599 | goto error; | |
1600 | } | |
1601 | ||
1602 | now = time(NULL); | |
1603 | tm = localtime(&now); | |
1604 | if (strftime(tmpbuffer, sizeof(tmpbuffer), "%a %d %h %Y %I:%M:%S %p %Z", tm) == 0) { | |
1605 | logerr(gl_LOGFILE, "Could not format time/date string.\n"); | |
1606 | goto error; | |
1607 | } | |
1608 | curr_grp = getgrgid(getgid()); | |
1609 | logit(gl_LOGFILE, "Starting vTPM manufacturing as %s:%s @ %s\n", | |
1610 | curr_user ? curr_user->pw_name : "<unknown>", | |
1611 | curr_grp ? curr_grp->gr_name : "<unknown>", | |
1612 | tmpbuffer); | |
1613 | ||
20ca1eb3 ET |
1614 | if (flags & (SETUP_EK_CERT_F | SETUP_PLATFORM_CERT_F)) { |
1615 | certsdir = g_dir_make_tmp("swtpm_setup.certs.XXXXXX", &error); | |
1616 | if (certsdir == NULL) { | |
1617 | logerr(gl_LOGFILE, "Could not create temporary directory for certs: %s\n", | |
1618 | error->message); | |
1619 | goto error; | |
1620 | } | |
1621 | } | |
1622 | ||
c125e34b SB |
1623 | if ((flags & SETUP_TPM2_F) == 0) { |
1624 | ret = init_tpm(flags, swtpm_prg_l, config_file, tpm_state_path, ownerpass, srkpass, vmid, | |
20ca1eb3 | 1625 | swtpm_keyopt, fds_to_pass, n_fds_to_pass, certsdir, user_certsdir); |
c125e34b SB |
1626 | } else { |
1627 | ret = init_tpm2(flags, swtpm_prg_l, config_file, tpm_state_path, vmid, pcr_banks, | |
20ca1eb3 ET |
1628 | swtpm_keyopt, fds_to_pass, n_fds_to_pass, rsa_keysize, certsdir, |
1629 | user_certsdir); | |
c125e34b SB |
1630 | } |
1631 | ||
1632 | if (ret == 0) { | |
1633 | logit(gl_LOGFILE, "Successfully authored TPM state.\n"); | |
1634 | } else { | |
1635 | logerr(gl_LOGFILE, "An error occurred. Authoring the TPM state failed.\n"); | |
81371f66 | 1636 | backend_ops->delete_state(backend_state); |
c125e34b SB |
1637 | } |
1638 | ||
1639 | now = time(NULL); | |
1640 | tm = localtime(&now); | |
1641 | if (strftime(tmpbuffer, sizeof(tmpbuffer), "%a %d %h %Y %I:%M:%S %p %Z", tm) == 0) { | |
1642 | logerr(gl_LOGFILE, "Could not format time/date string.\n"); | |
1643 | goto error; | |
1644 | } | |
1645 | logit(gl_LOGFILE, "Ending vTPM manufacturing @ %s\n", | |
1646 | tmpbuffer); | |
1647 | ||
1648 | out: | |
20ca1eb3 ET |
1649 | if (certsdir && g_rmdir(certsdir) != 0) |
1650 | logerr(gl_LOGFILE, "Could not remove temporary directory for certs: %s\n", | |
1651 | strerror(errno)); | |
1652 | ||
81371f66 SR |
1653 | if (backend_ops && backend_state) |
1654 | backend_ops->free_backend(backend_state); | |
c125e34b SB |
1655 | g_strfreev(swtpm_prg_l); |
1656 | g_free(gl_LOGFILE); | |
1657 | ||
b30a16ed | 1658 | return ret; |
68ac6478 SB |
1659 | |
1660 | error: | |
1661 | ret = 1; | |
1662 | goto out; | |
c125e34b | 1663 | } |