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