vici: Make childless initiation of IKE_SAs configurable
[strongswan.git] / src / aikgen / aikgen.c
1 /*
2 * Copyright (C) 2014-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 #include <credentials/certificates/x509.h>
22 #include <credentials/keys/public_key.h>
23
24 #include <syslog.h>
25 #include <getopt.h>
26 #include <errno.h>
27
28 /* default directory where AIK keys are stored */
29 #define AIK_DIR IPSEC_CONFDIR "/pts/"
30
31 /* default name of AIK private key blob */
32 #define DEFAULT_FILENAME_AIKBLOB AIK_DIR "aikBlob.bin"
33
34 /* default name of AIK public key */
35 #define DEFAULT_FILENAME_AIKPUBKEY AIK_DIR "aikPub.der"
36
37 /* logging */
38 static bool log_to_stderr = TRUE;
39 static bool log_to_syslog = TRUE;
40 static level_t default_loglevel = 1;
41
42 /* options read by optionsfrom */
43 options_t *options;
44
45 /* global variables */
46 certificate_t *cacert;
47 public_key_t *ca_pubkey;
48 chunk_t ca_modulus;
49 chunk_t aik_pubkey;
50 chunk_t aik_keyid;
51 tpm_tss_t *tpm;
52
53 /**
54 * logging function for aikgen
55 */
56 static void aikgen_dbg(debug_t group, level_t level, char *fmt, ...)
57 {
58 char buffer[8192];
59 char *current = buffer, *next;
60 va_list args;
61
62 if (level <= default_loglevel)
63 {
64 if (log_to_stderr)
65 {
66 va_start(args, fmt);
67 vfprintf(stderr, fmt, args);
68 va_end(args);
69 fprintf(stderr, "\n");
70 }
71 if (log_to_syslog)
72 {
73 /* write in memory buffer first */
74 va_start(args, fmt);
75 vsnprintf(buffer, sizeof(buffer), fmt, args);
76 va_end(args);
77
78 /* do a syslog with every line */
79 while (current)
80 {
81 next = strchr(current, '\n');
82 if (next)
83 {
84 *(next++) = '\0';
85 }
86 syslog(LOG_INFO, "%s\n", current);
87 current = next;
88 }
89 }
90 }
91 }
92
93 /**
94 * Initialize logging to stderr/syslog
95 */
96 static void init_log(const char *program)
97 {
98 dbg = aikgen_dbg;
99
100 if (log_to_stderr)
101 {
102 setbuf(stderr, NULL);
103 }
104 if (log_to_syslog)
105 {
106 openlog(program, LOG_CONS | LOG_NDELAY | LOG_PID, LOG_AUTHPRIV);
107 }
108 }
109
110 /**
111 * @brief exit aikgen
112 *
113 * @param status 0 = OK, -1 = general discomfort
114 */
115 static void exit_aikgen(err_t message, ...)
116 {
117 int status = 0;
118
119 DESTROY_IF(tpm);
120 DESTROY_IF(cacert);
121 DESTROY_IF(ca_pubkey);
122 free(ca_modulus.ptr);
123 free(aik_pubkey.ptr);
124 free(aik_keyid.ptr);
125 options->destroy(options);
126
127 /* print any error message to stderr */
128 if (message != NULL && *message != '\0')
129 {
130 va_list args;
131 char m[8192];
132
133 va_start(args, message);
134 vsnprintf(m, sizeof(m), message, args);
135 va_end(args);
136
137 fprintf(stderr, "aikgen error: %s\n", m);
138 status = -1;
139 }
140 library_deinit();
141 exit(status);
142 }
143
144 /**
145 * @brief prints the usage of the program to the stderr output
146 *
147 * If message is set, program is exited with 1 (error)
148 * @param message message in case of an error
149 */
150 static void usage(const char *message)
151 {
152 fprintf(stderr,
153 "Usage: aikgen --cacert|capubkey <filename>"
154 " [--aikblob <filename>] [--aikpubkey <filename>] \n"
155 " [--idreq <filename>] [--force]"
156 " [--quiet] [--debug <level>]\n"
157 " aikgen --help\n"
158 "\n"
159 "Options:\n"
160 " --cacert (-c) certificate of [privacy] CA\n"
161 " --capubkey (-k) public key of [privacy] CA\n"
162 " --aikblob (-b) encrypted blob with AIK private key\n"
163 " --aikpubkey (-p) AIK public key\n"
164 " --idreq (-i) encrypted identity request\n"
165 " --force (-f) force to overwrite existing files\n"
166 " --help (-h) show usage and exit\n"
167 "\n"
168 "Debugging output:\n"
169 " --debug (-l) changes the log level (-1..4, default: 1)\n"
170 " --quiet (-q) do not write log output to stderr\n"
171 );
172 exit_aikgen(message);
173 }
174
175 /**
176 * @brief main of aikgen which generates an Attestation Identity Key (AIK)
177 *
178 * @param argc number of arguments
179 * @param argv pointer to the argument values
180 */
181 int main(int argc, char *argv[])
182 {
183 /* external values */
184 extern char * optarg;
185 extern int optind;
186
187 char *cacert_filename = NULL;
188 char *capubkey_filename = NULL;
189 char *aikblob_filename = DEFAULT_FILENAME_AIKBLOB;
190 char *aikpubkey_filename = DEFAULT_FILENAME_AIKPUBKEY;
191 char *idreq_filename = NULL;
192 bool force = FALSE;
193 chunk_t identity_req;
194 chunk_t aik_blob;
195 hasher_t *hasher;
196
197 atexit(library_deinit);
198 if (!library_init(NULL, "aikgen"))
199 {
200 exit(SS_RC_LIBSTRONGSWAN_INTEGRITY);
201 }
202 if (lib->integrity &&
203 !lib->integrity->check_file(lib->integrity, "aikgen", argv[0]))
204 {
205 fprintf(stderr, "integrity check of aikgen failed\n");
206 exit(SS_RC_DAEMON_INTEGRITY);
207 }
208
209 /* initialize global variables */
210 options = options_create();
211
212 for (;;)
213 {
214 static const struct option long_opts[] = {
215 /* name, has_arg, flag, val */
216 { "help", no_argument, NULL, 'h' },
217 { "optionsfrom", required_argument, NULL, '+' },
218 { "cacert", required_argument, NULL, 'c' },
219 { "capubkey", required_argument, NULL, 'k' },
220 { "aikblob", required_argument, NULL, 'b' },
221 { "aikpubkey", required_argument, NULL, 'p' },
222 { "idreq", required_argument, NULL, 'i' },
223 { "force", no_argument, NULL, 'f' },
224 { "quiet", no_argument, NULL, 'q' },
225 { "debug", required_argument, NULL, 'l' },
226 { 0,0,0,0 }
227 };
228
229 /* parse next option */
230 int c = getopt_long(argc, argv, "ho:c:b:p:fqd:", long_opts, NULL);
231
232 switch (c)
233 {
234 case EOF: /* end of flags */
235 break;
236
237 case 'h': /* --help */
238 usage(NULL);
239
240 case '+': /* --optionsfrom <filename> */
241 if (!options->from(options, optarg, &argc, &argv, optind))
242 {
243 exit_aikgen("optionsfrom failed");
244 }
245 continue;
246
247 case 'c': /* --cacert <filename> */
248 cacert_filename = optarg;
249 continue;
250
251 case 'k': /* --capubkey <filename> */
252 capubkey_filename = optarg;
253 continue;
254
255 case 'b': /* --aikblob <filename> */
256 aikblob_filename = optarg;
257 continue;
258
259 case 'p': /* --aikpubkey <filename> */
260 aikpubkey_filename = optarg;
261 continue;
262
263 case 'i': /* --idreq <filename> */
264 idreq_filename = optarg;
265 continue;
266
267 case 'f': /* --force */
268 force = TRUE;
269 continue;
270
271 case 'q': /* --quiet */
272 log_to_stderr = FALSE;
273 continue;
274
275 case 'l': /* --debug <level> */
276 default_loglevel = atoi(optarg);
277 continue;
278
279 default:
280 usage("unknown option");
281 }
282 /* break from loop */
283 break;
284 }
285
286 init_log("aikgen");
287
288 if (!lib->plugins->load(lib->plugins,
289 lib->settings->get_str(lib->settings, "aikgen.load", PLUGINS)))
290 {
291 exit_aikgen("plugin loading failed");
292 }
293
294 /* read certificate of [privacy] CA if it exists */
295 if (cacert_filename)
296 {
297 cacert = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509,
298 BUILD_FROM_FILE, cacert_filename, BUILD_END);
299 if (!cacert)
300 {
301 exit_aikgen("could not read ca certificate file '%s'",
302 cacert_filename);
303 }
304 }
305
306 /* optionally read public key of [privacy CA] if it exists */
307 if (!cacert)
308 {
309 if (!capubkey_filename)
310 {
311 usage("either --cacert or --capubkey option is required");
312 }
313 cacert = lib->creds->create(lib->creds, CRED_CERTIFICATE,
314 CERT_TRUSTED_PUBKEY, BUILD_FROM_FILE,
315 capubkey_filename, BUILD_END);
316 if (!cacert)
317 {
318 exit_aikgen("could not read ca public key file '%s'",
319 capubkey_filename);
320 }
321 }
322
323 /* extract public key from CA certificate or trusted CA public key */
324 ca_pubkey = cacert->get_public_key(cacert);
325 if (!ca_pubkey)
326 {
327 exit_aikgen("could not extract ca public key");
328 }
329 if (ca_pubkey->get_type(ca_pubkey) != KEY_RSA ||
330 ca_pubkey->get_keysize(ca_pubkey) != 2048)
331 {
332 exit_aikgen("CA public key must be RSA 2048 but is %N %d",
333 key_type_names, ca_pubkey->get_type(ca_pubkey),
334 ca_pubkey->get_keysize(ca_pubkey));
335 }
336 if (!ca_pubkey->get_encoding(ca_pubkey, PUBKEY_RSA_MODULUS, &ca_modulus))
337 {
338 exit_aikgen("could not extract RSA modulus from CA public key");
339 }
340
341 /* try to find a TPM 1.2 */
342 tpm = tpm_tss_probe(TPM_VERSION_1_2);
343 if (!tpm)
344 {
345 exit_aikgen("no TPM 1.2 found");
346 }
347
348 if (!tpm->generate_aik(tpm, ca_modulus, &aik_blob, &aik_pubkey,
349 &identity_req))
350 {
351 exit_aikgen("could not generate AIK");
352 }
353
354 /* optionally output identity request encrypted with CA public key */
355 if (idreq_filename)
356 {
357 if (!chunk_write(identity_req, idreq_filename, 0022, force))
358 {
359 exit_aikgen("could not write AIK identity request file '%s': %s",
360 idreq_filename, strerror(errno));
361 }
362 DBG1(DBG_LIB, "AIK identity request written to '%s' (%u bytes)",
363 idreq_filename, identity_req.len);
364 }
365
366 /* output AIK private key blob */
367 if (!chunk_write(aik_blob, aikblob_filename, 0022, force))
368 {
369 exit_aikgen("could not write AIK blob file '%s': %s",
370 aikblob_filename, strerror(errno));
371 }
372 DBG1(DBG_LIB, "AIK private key blob written to '%s' (%u bytes)",
373 aikblob_filename, aik_blob.len);
374
375 /* output AIK public key */
376 if (!chunk_write(aik_pubkey, aikpubkey_filename, 0022, force))
377 {
378 exit_aikgen("could not write AIK public key file '%s': %s",
379 aikpubkey_filename, strerror(errno));
380 }
381 DBG1(DBG_LIB, "AIK public key written to '%s' (%u bytes)",
382 aikpubkey_filename, aik_pubkey.len);
383
384 /* display AIK keyid derived from subjectPublicKeyInfo encoding */
385 hasher = lib->crypto->create_hasher(lib->crypto, HASH_SHA1);
386 if (!hasher || !hasher->allocate_hash(hasher, aik_pubkey, &aik_keyid))
387 {
388 DESTROY_IF(hasher);
389 exit_aikgen("SHA1 hash algorithm not supported, computation of AIK "
390 "keyid failed");
391 }
392 hasher->destroy(hasher);
393 DBG1(DBG_LIB, "AIK keyid: %#B", &aik_keyid);
394
395 exit_aikgen(NULL);
396 return -1; /* should never be reached */
397 }