fea58ed2774998d4f36f0a85294d12cb0b5b9120
[strongswan.git] / src / aikpub2 / aikpub2.c
1 /*
2 * Copyright (C) 2016 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 <utils/debug.h>
20 #include <utils/optionsfrom.h>
21
22 #include <syslog.h>
23 #include <getopt.h>
24 #include <errno.h>
25
26 /* default directory where AIK keys are stored */
27 #define AIK_DIR IPSEC_CONFDIR "/pts/"
28
29 /* default name of AIK public key blob */
30 #define DEFAULT_FILENAME_AIKPUBKEY AIK_DIR "aikPub.der"
31
32 /* logging */
33 static bool log_to_stderr = TRUE;
34 static bool log_to_syslog = TRUE;
35 static level_t default_loglevel = 1;
36
37 /* options read by optionsfrom */
38 options_t *options;
39
40 chunk_t aik_pubkey;
41 chunk_t aik_keyid;
42
43 /**
44 * logging function for aikpub2
45 */
46 static void aikpub2_dbg(debug_t group, level_t level, char *fmt, ...)
47 {
48 char buffer[8192];
49 char *current = buffer, *next;
50 va_list args;
51
52 if (level <= default_loglevel)
53 {
54 if (log_to_stderr)
55 {
56 va_start(args, fmt);
57 vfprintf(stderr, fmt, args);
58 va_end(args);
59 fprintf(stderr, "\n");
60 }
61 if (log_to_syslog)
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 }
81 }
82
83 /**
84 * Initialize logging to stderr/syslog
85 */
86 static void init_log(const char *program)
87 {
88 dbg = aikpub2_dbg;
89
90 if (log_to_stderr)
91 {
92 setbuf(stderr, NULL);
93 }
94 if (log_to_syslog)
95 {
96 openlog(program, LOG_CONS | LOG_NDELAY | LOG_PID, LOG_AUTHPRIV);
97 }
98 }
99
100 /**
101 * @brief exit aikgen
102 *
103 * @param status 0 = OK, -1 = general discomfort
104 */
105 static void exit_aikpub2(err_t message, ...)
106 {
107 int status = 0;
108
109 free(aik_pubkey.ptr);
110 free(aik_keyid.ptr);
111 options->destroy(options);
112
113 /* print any error message to stderr */
114 if (message != NULL && *message != '\0')
115 {
116 va_list args;
117 char m[8192];
118
119 va_start(args, message);
120 vsnprintf(m, sizeof(m), message, args);
121 va_end(args);
122
123 fprintf(stderr, "aikpub2 error: %s\n", m);
124 status = -1;
125 }
126 library_deinit();
127 exit(status);
128 }
129
130 /**
131 * @brief prints the usage of the program to the stderr output
132 *
133 * If message is set, program is exited with 1 (error)
134 * @param message message in case of an error
135 */
136 static void usage(const char *message)
137 {
138 fprintf(stderr,
139 "Usage: aikpub2 --handle <handle> --out <filename>\n"
140 " [--force] [--quiet] [--debug <level>]\n"
141 " aikpub2 --help\n"
142 "\n"
143 "Options:\n"
144 " --handle (-H) TSS 2.0 AIK object handle\n"
145 " --out (-o) AIK public key in PKCS #1 format\n"
146 " --force (-f) force to overwrite existing files\n"
147 " --help (-h) show usage and exit\n"
148 "\n"
149 "Debugging output:\n"
150 " --debug (-l) changes the log level (-1..4, default: 1)\n"
151 " --quiet (-q) do not write log output to stderr\n"
152 );
153 exit_aikpub2(message);
154 }
155
156
157 /**
158 * @brief main of aikpub2 which extracts an Attestation Identity Key (AIK)
159 *
160 * @param argc number of arguments
161 * @param argv pointer to the argument values
162 */
163 int main(int argc, char *argv[])
164 {
165 /* external values */
166 extern char * optarg;
167 extern int optind;
168
169 char *aik_out_filename = DEFAULT_FILENAME_AIKPUBKEY;
170 uint32_t aik_handle = 0;
171 bool force = FALSE;
172 hasher_t *hasher;
173 tpm_tss_t *tpm;
174
175 atexit(library_deinit);
176 if (!library_init(NULL, "aikpub2"))
177 {
178 exit(SS_RC_LIBSTRONGSWAN_INTEGRITY);
179 }
180 if (lib->integrity &&
181 !lib->integrity->check_file(lib->integrity, "aikpub2", argv[0]))
182 {
183 fprintf(stderr, "integrity check of aikpub2 failed\n");
184 exit(SS_RC_DAEMON_INTEGRITY);
185 }
186
187 /* initialize global variables */
188 options = options_create();
189
190 for (;;)
191 {
192 static const struct option long_opts[] = {
193 /* name, has_arg, flag, val */
194 { "help", no_argument, NULL, 'h' },
195 { "optionsfrom", required_argument, NULL, '+' },
196 { "handle", required_argument, NULL, 'H' },
197 { "in", required_argument, NULL, 'i' },
198 { "out", required_argument, NULL, 'o' },
199 { "force", no_argument, NULL, 'f' },
200 { "quiet", no_argument, NULL, 'q' },
201 { "debug", required_argument, NULL, 'l' },
202 { 0,0,0,0 }
203 };
204
205 /* parse next option */
206 int c = getopt_long(argc, argv, "h+:H:i:o:fql:", long_opts, NULL);
207
208 switch (c)
209 {
210 case EOF: /* end of flags */
211 break;
212
213 case 'h': /* --help */
214 usage(NULL);
215
216 case '+': /* --optionsfrom <filename> */
217 if (!options->from(options, optarg, &argc, &argv, optind))
218 {
219 exit_aikpub2("optionsfrom failed");
220 }
221 continue;
222
223 case 'H': /* --handle <handle> */
224 aik_handle = strtoll(optarg, NULL, 16);
225 continue;
226
227 case 'o': /* --out <filename> */
228 aik_out_filename = optarg;
229 continue;
230
231 case 'f': /* --force */
232 force = TRUE;
233 continue;
234
235 case 'q': /* --quiet */
236 log_to_stderr = FALSE;
237 continue;
238
239 case 'l': /* --debug <level> */
240 default_loglevel = atoi(optarg);
241 continue;
242
243 default:
244 usage("unknown option");
245 }
246 /* break from loop */
247 break;
248 }
249
250 init_log("aikpub2");
251
252 if (!lib->plugins->load(lib->plugins,
253 lib->settings->get_str(lib->settings, "aikpub2.load", PLUGINS)))
254 {
255 exit_aikpub2("plugin loading failed");
256 }
257 if (!aik_handle)
258 {
259 usage("--handle option is required");
260 }
261
262 /* try to find a TPM 2.0 */
263 tpm = tpm_tss_probe(TPM_VERSION_2_0);
264 if (!tpm)
265 {
266 exit_aikpub2("no TPM 2.0 found");
267 }
268
269 /* get AIK public key from TPM */
270 aik_pubkey = tpm->get_public(tpm, aik_handle);
271 tpm->destroy(tpm);
272
273 /* exit if AIK public key retrieval failed */
274 if (aik_pubkey.len == 0)
275 {
276 exit_aikpub2("retrieval of AIK public key failed");
277 }
278
279 /* store AIK subjectPublicKeyInfo to file */
280 if (!chunk_write(aik_pubkey, aik_out_filename, 0022, force))
281 {
282 exit_aikpub2("could not write AIK public key file '%s': %s",
283 aik_out_filename, strerror(errno));
284 }
285 DBG1(DBG_LIB, "AIK public key written to '%s' (%u bytes)",
286 aik_out_filename, aik_pubkey.len);
287
288 /* AIK keyid derived from subjectPublicKeyInfo encoding */
289 hasher = lib->crypto->create_hasher(lib->crypto, HASH_SHA1);
290 if (!hasher)
291 {
292 exit_aikpub2("SHA1 hash algorithm not supported");
293 }
294 if (!hasher->allocate_hash(hasher, aik_pubkey, &aik_keyid))
295 {
296 hasher->destroy(hasher);
297 exit_aikpub2("computing SHA1 fingerprint failed");
298 }
299 hasher->destroy(hasher);
300
301 DBG1(DBG_LIB, "AIK keyid: %#B", &aik_keyid);
302
303 exit_aikpub2(NULL);
304 return -1; /* should never be reached */
305 }