23d652f129dfe3e05cee6a0a79132c05fcc469bf
[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 exit(status);
121 }
122
123 /**
124 * @brief prints the usage of the program to the stderr output
125 *
126 * If message is set, program is exited with 1 (error)
127 * @param message message in case of an error
128 */
129 static void usage(const char *message)
130 {
131 fprintf(stderr,
132 "Usage: tpm_extendpcr [--alg <name>] --pcr <nr> --digest <hex>|--in"
133 " <file>\n"
134 " [--hash] [--out <file>] [--quiet]"
135 " [--debug <level>]\n"
136 " tpm_extendpcr --help\n"
137 "\n"
138 "Options:\n"
139 " --alg (-a) hash algorithm (sha1|sha256)\n"
140 " --pcr (-p) platform configuration register (0..23)\n"
141 " --digest (-d) digest in hex format to be extended\n"
142 " --in (-i) binary input file with digest to be extended\n"
143 " --hash (-x) prehash the input file to create digest\n"
144 " --out (-o) binary output file with updated PCR value\n"
145 " --help (-h) show usage and exit\n"
146 "\n"
147 "Debugging output:\n"
148 " --debug (-l) changes the log level (-1..4, default: 1)\n"
149 " --quiet (-q) do not write log output to stderr\n"
150 );
151 exit_tpm_extendpcr(message);
152 }
153
154 /**
155 * @brief main of tpm_extendpcr which extends digest into a PCR
156 *
157 * @param argc number of arguments
158 * @param argv pointer to the argument values
159 */
160 int main(int argc, char *argv[])
161 {
162 hash_algorithm_t alg = HASH_SHA1;
163 hasher_t *hasher = NULL;
164 char *infile = NULL, *outfile = NULL;
165 uint32_t pcr = 16;
166 bool hash = FALSE;
167
168 if (!library_init(NULL, "tpm_extendpcr"))
169 {
170 exit(SS_RC_LIBSTRONGSWAN_INTEGRITY);
171 }
172 atexit(library_deinit);
173 if (lib->integrity &&
174 !lib->integrity->check_file(lib->integrity, "tpm_extendpcr", argv[0]))
175 {
176 fprintf(stderr, "integrity check of tpm_extendpcr failed\n");
177 exit(SS_RC_DAEMON_INTEGRITY);
178 }
179 if (!libtpmtss_init())
180 {
181 fprintf(stderr, "libtpmtss initialization failed\n");
182 exit(SS_RC_INITIALIZATION_FAILED);
183 }
184 atexit(libtpmtss_deinit);
185
186 for (;;)
187 {
188 static const struct option long_opts[] = {
189 /* name, has_arg, flag, val */
190 { "help", no_argument, NULL, 'h' },
191 { "alg", required_argument, NULL, 'a' },
192 { "pcr", required_argument, NULL, 'p' },
193 { "digest", required_argument, NULL, 'd' },
194 { "in", required_argument, NULL, 'i' },
195 { "hash", no_argument, NULL, 'x' },
196 { "out", required_argument, NULL, 'o' },
197 { "quiet", no_argument, NULL, 'q' },
198 { "debug", required_argument, NULL, 'l' },
199 { 0,0,0,0 }
200 };
201
202 /* parse next option */
203 int c = getopt_long(argc, argv, "ha:p:d:i:xo:ql:", long_opts, NULL);
204
205 switch (c)
206 {
207 case EOF: /* end of flags */
208 break;
209
210 case 'h': /* --help */
211 usage(NULL);
212
213 case 'a': /* --alg <name> */
214 if (!enum_from_name(hash_algorithm_short_names, optarg, &alg))
215 {
216 usage("unsupported hash algorithm");
217 }
218 continue;
219 case 'p': /* --pcr <nr> */
220 pcr = atoi(optarg);
221 continue;
222
223 case 'd': /* --digest <hex> */
224 digest = chunk_from_hex(chunk_from_str(optarg), NULL);
225 continue;
226
227 case 'i': /* --in <file> */
228 infile = optarg;
229 continue;
230
231 case 'x': /* --hash */
232 hash = TRUE;
233 continue;
234
235 case 'o': /* --out <file> */
236 outfile = optarg;
237 continue;
238
239 case 'q': /* --quiet */
240 log_to_stderr = FALSE;
241 continue;
242
243 case 'l': /* --debug <level> */
244 default_loglevel = atoi(optarg);
245 continue;
246
247 default:
248 usage("unknown option");
249 }
250 /* break from loop */
251 break;
252 }
253
254 init_log("tpm_extendpcr");
255
256 if (!lib->plugins->load(lib->plugins,
257 lib->settings->get_str(lib->settings, "tpm_extendpcr.load",
258 "sha1 sha2")))
259 {
260 exit_tpm_extendpcr("plugin loading failed");
261 }
262
263 /* try to find a TPM */
264 tpm = tpm_tss_probe(TPM_VERSION_ANY);
265 if (!tpm)
266 {
267 exit_tpm_extendpcr("no TPM found");
268 }
269
270 /* read digest from file */
271 if (digest.len == 0)
272 {
273 chunk_t *chunk;
274
275 if (!infile)
276 {
277 exit_tpm_extendpcr("--digest or --in option required");
278 }
279 chunk = chunk_map(infile, FALSE);
280 if (!chunk)
281 {
282 exit_tpm_extendpcr("reading input file failed");
283 }
284 if (hash)
285 {
286 hasher = lib->crypto->create_hasher(lib->crypto, alg);
287 if (!hasher || !hasher->allocate_hash(hasher, *chunk, &digest))
288 {
289 DESTROY_IF(hasher);
290 chunk_unmap(chunk);
291 exit_tpm_extendpcr("prehashing infile failed");
292 }
293 hasher->destroy(hasher);
294 }
295 else
296 {
297 digest = chunk_clone(*chunk);
298 }
299 chunk_unmap(chunk);
300 }
301 DBG1(DBG_PTS, "Digest: %#B", &digest);
302
303 /* extend digest into PCR */
304 if (!tpm->extend_pcr(tpm, pcr, &pcr_value, digest, alg))
305 {
306 exit_tpm_extendpcr("extending PCR failed");
307 }
308 DBG1(DBG_PTS, "PCR %02u: %#B", pcr, &pcr_value);
309
310 /* write PCR value to file */
311 if (outfile)
312 {
313 if (!chunk_write(pcr_value, outfile, 022, TRUE))
314 {
315 DBG1(DBG_PTS, "writing '%s' failed", outfile);
316 }
317 }
318 chunk_free(&pcr_value);
319
320 exit_tpm_extendpcr(NULL);
321 return -1; /* should never be reached */
322 }