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