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