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