libtpmtss: Read RSA public key exponent instead of assuming its value
[strongswan.git] / src / tpm_extendpcr / tpm_extendpcr.c
1 /*
2 * Copyright (C) 2017 Andreas Steffen
3 * HSR Hochschule fuer Technik Rapperswil
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * for more details.
14 */
15
16 #include <tpm_tss.h>
17
18 #include <library.h>
19 #include <crypto/hashers/hasher.h>
20 #include <utils/debug.h>
21
22 #include <syslog.h>
23 #include <getopt.h>
24 #include <errno.h>
25
26
27 /* logging */
28 static bool log_to_stderr = TRUE;
29 static bool log_to_syslog = TRUE;
30 static level_t default_loglevel = 1;
31
32 /* global variables */
33 tpm_tss_t *tpm;
34 chunk_t digest;
35 chunk_t pcr_value;
36
37 /**
38 * logging function for tpm_extendpcr
39 */
40 static void tpm_extendpcr_dbg(debug_t group, level_t level, char *fmt, ...)
41 {
42 char buffer[8192];
43 char *current = buffer, *next;
44 va_list args;
45
46 if (level <= default_loglevel)
47 {
48 if (log_to_stderr)
49 {
50 va_start(args, fmt);
51 vfprintf(stderr, fmt, args);
52 va_end(args);
53 fprintf(stderr, "\n");
54 }
55 if (log_to_syslog)
56 {
57 /* write in memory buffer first */
58 va_start(args, fmt);
59 vsnprintf(buffer, sizeof(buffer), fmt, args);
60 va_end(args);
61
62 /* do a syslog with every line */
63 while (current)
64 {
65 next = strchr(current, '\n');
66 if (next)
67 {
68 *(next++) = '\0';
69 }
70 syslog(LOG_INFO, "%s\n", current);
71 current = next;
72 }
73 }
74 }
75 }
76
77 /**
78 * Initialize logging to stderr/syslog
79 */
80 static void init_log(const char *program)
81 {
82 dbg = tpm_extendpcr_dbg;
83
84 if (log_to_stderr)
85 {
86 setbuf(stderr, NULL);
87 }
88 if (log_to_syslog)
89 {
90 openlog(program, LOG_CONS | LOG_NDELAY | LOG_PID, LOG_AUTHPRIV);
91 }
92 }
93
94 /**
95 * @brief exit tpm_extendpcr
96 *
97 * @param status 0 = OK, -1 = general discomfort
98 */
99 static void exit_tpm_extendpcr(err_t message, ...)
100 {
101 int status = 0;
102
103 DESTROY_IF(tpm);
104 chunk_free(&digest);
105 chunk_free(&pcr_value);
106
107 /* print any error message to stderr */
108 if (message != NULL && *message != '\0')
109 {
110 va_list args;
111 char m[8192];
112
113 va_start(args, message);
114 vsnprintf(m, sizeof(m), message, args);
115 va_end(args);
116
117 fprintf(stderr, "tpm_extendpcr error: %s\n", m);
118 status = -1;
119 }
120 library_deinit();
121 exit(status);
122 }
123
124 /**
125 * @brief prints the usage of the program to the stderr output
126 *
127 * If message is set, program is exited with 1 (error)
128 * @param message message in case of an error
129 */
130 static void usage(const char *message)
131 {
132 fprintf(stderr,
133 "Usage: tpm_extendpcr [--alg <name>] --pcr <nr> --digest <hex>|--in"
134 " <file>\n"
135 " [--hash] [--out <file>] [--quiet]"
136 " [--debug <level>]\n"
137 " tpm_extendpcr --help\n"
138 "\n"
139 "Options:\n"
140 " --alg (-a) hash algorithm (sha1|sha256)\n"
141 " --pcr (-p) platform configuration register (0..23)\n"
142 " --digest (-d) digest in hex format to be extended\n"
143 " --in (-i) binary input file with digest to be extended\n"
144 " --hash (-x) prehash the input file to create digest\n"
145 " --out (-o) binary output file with updated PCR value\n"
146 " --help (-h) show usage and exit\n"
147 "\n"
148 "Debugging output:\n"
149 " --debug (-l) changes the log level (-1..4, default: 1)\n"
150 " --quiet (-q) do not write log output to stderr\n"
151 );
152 exit_tpm_extendpcr(message);
153 }
154
155 /**
156 * @brief main of tpm_extendpcr which extends digest into a PCR
157 *
158 * @param argc number of arguments
159 * @param argv pointer to the argument values
160 */
161 int main(int argc, char *argv[])
162 {
163 hash_algorithm_t alg = HASH_SHA1;
164 hasher_t *hasher = NULL;
165 char *infile = NULL, *outfile = NULL;
166 uint32_t pcr = 16;
167 bool hash = FALSE;
168
169 atexit(library_deinit);
170 if (!library_init(NULL, "tpm_extendpcr"))
171 {
172 exit(SS_RC_LIBSTRONGSWAN_INTEGRITY);
173 }
174 if (lib->integrity &&
175 !lib->integrity->check_file(lib->integrity, "tpm_extendpcr", argv[0]))
176 {
177 fprintf(stderr, "integrity check of tpm_extendpcr failed\n");
178 exit(SS_RC_DAEMON_INTEGRITY);
179 }
180
181 for (;;)
182 {
183 static const struct option long_opts[] = {
184 /* name, has_arg, flag, val */
185 { "help", no_argument, NULL, 'h' },
186 { "alg", required_argument, NULL, 'a' },
187 { "pcr", required_argument, NULL, 'p' },
188 { "digest", required_argument, NULL, 'd' },
189 { "in", required_argument, NULL, 'i' },
190 { "hash", no_argument, NULL, 'x' },
191 { "out", required_argument, NULL, 'o' },
192 { "quiet", no_argument, NULL, 'q' },
193 { "debug", required_argument, NULL, 'l' },
194 { 0,0,0,0 }
195 };
196
197 /* parse next option */
198 int c = getopt_long(argc, argv, "ha:p:d:i:xo:ql:", long_opts, NULL);
199
200 switch (c)
201 {
202 case EOF: /* end of flags */
203 break;
204
205 case 'h': /* --help */
206 usage(NULL);
207
208 case 'a': /* --alg <name> */
209 if (!enum_from_name(hash_algorithm_short_names, optarg, &alg))
210 {
211 usage("unsupported hash algorithm");
212 }
213 continue;
214 case 'p': /* --pcr <nr> */
215 pcr = atoi(optarg);
216 continue;
217
218 case 'd': /* --digest <hex> */
219 digest = chunk_from_hex(chunk_from_str(optarg), NULL);
220 continue;
221
222 case 'i': /* --in <file> */
223 infile = optarg;
224 continue;
225
226 case 'x': /* --hash */
227 hash = TRUE;
228 continue;
229
230 case 'o': /* --out <file> */
231 outfile = optarg;
232 continue;
233
234 case 'q': /* --quiet */
235 log_to_stderr = FALSE;
236 continue;
237
238 case 'l': /* --debug <level> */
239 default_loglevel = atoi(optarg);
240 continue;
241
242 default:
243 usage("unknown option");
244 }
245 /* break from loop */
246 break;
247 }
248
249 init_log("tpm_extendpcr");
250
251 if (!lib->plugins->load(lib->plugins,
252 lib->settings->get_str(lib->settings, "tpm_extendpcr.load",
253 "tpm sha1 sha2")))
254 {
255 exit_tpm_extendpcr("plugin loading failed");
256 }
257
258 /* try to find a TPM */
259 tpm = tpm_tss_probe(TPM_VERSION_ANY);
260 if (!tpm)
261 {
262 exit_tpm_extendpcr("no TPM found");
263 }
264
265 /* read digest from file */
266 if (digest.len == 0)
267 {
268 chunk_t *chunk;
269
270 if (!infile)
271 {
272 exit_tpm_extendpcr("--digest or --in option required");
273 }
274 chunk = chunk_map(infile, FALSE);
275 if (!chunk)
276 {
277 exit_tpm_extendpcr("reading input file failed");
278 }
279 if (hash)
280 {
281 hasher = lib->crypto->create_hasher(lib->crypto, alg);
282 if (!hasher || !hasher->allocate_hash(hasher, *chunk, &digest))
283 {
284 DESTROY_IF(hasher);
285 chunk_unmap(chunk);
286 exit_tpm_extendpcr("prehashing infile failed");
287 }
288 hasher->destroy(hasher);
289 }
290 else
291 {
292 digest = chunk_clone(*chunk);
293 }
294 chunk_unmap(chunk);
295 }
296 DBG1(DBG_PTS, "Digest: %#B", &digest);
297
298 /* extend digest into PCR */
299 if (!tpm->extend_pcr(tpm, pcr, &pcr_value, digest, alg))
300 {
301 exit_tpm_extendpcr("extending PCR failed");
302 }
303 DBG1(DBG_PTS, "PCR %02u: %#B", pcr, &pcr_value);
304
305 /* write PCR value to file */
306 if (outfile)
307 {
308 if (!chunk_write(pcr_value, outfile, 022, TRUE))
309 {
310 DBG1(DBG_PTS, "writing '%s' failed", outfile);
311 }
312 }
313 chunk_free(&pcr_value);
314
315 exit_tpm_extendpcr(NULL);
316 return -1; /* should never be reached */
317 }