d03e96f9b3100dbc5a69be5b46af68eb916c9b71
[strongswan.git] / src / pki / pki.c
1 /*
2 * Copyright (C) 2012-2018 Tobias Brunner
3 * Copyright (C) 2009 Martin Willi
4 * HSR Hochschule fuer Technik Rapperswil
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2 of the License, or (at your
9 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * for more details.
15 */
16
17 #define _GNU_SOURCE
18 #include "command.h"
19 #include "pki.h"
20
21 #include <time.h>
22 #include <unistd.h>
23 #include <fcntl.h>
24
25 #include <utils/debug.h>
26 #include <credentials/sets/mem_cred.h>
27 #include <credentials/sets/callback_cred.h>
28
29 /**
30 * Convert a form string to a encoding type
31 */
32 bool get_form(char *form, cred_encoding_type_t *enc, credential_type_t type)
33 {
34 if (streq(form, "der"))
35 {
36 switch (type)
37 {
38 case CRED_CERTIFICATE:
39 *enc = CERT_ASN1_DER;
40 return TRUE;
41 case CRED_PRIVATE_KEY:
42 *enc = PRIVKEY_ASN1_DER;
43 return TRUE;
44 case CRED_PUBLIC_KEY:
45 /* der encoded keys usually contain the complete
46 * SubjectPublicKeyInfo */
47 *enc = PUBKEY_SPKI_ASN1_DER;
48 return TRUE;
49 default:
50 return FALSE;
51 }
52 }
53 else if (streq(form, "pem"))
54 {
55 switch (type)
56 {
57 case CRED_CERTIFICATE:
58 *enc = CERT_PEM;
59 return TRUE;
60 case CRED_PRIVATE_KEY:
61 *enc = PRIVKEY_PEM;
62 return TRUE;
63 case CRED_PUBLIC_KEY:
64 *enc = PUBKEY_PEM;
65 return TRUE;
66 default:
67 return FALSE;
68 }
69 }
70 else if (streq(form, "pgp"))
71 {
72 switch (type)
73 {
74 case CRED_PRIVATE_KEY:
75 *enc = PRIVKEY_PGP;
76 return TRUE;
77 case CRED_PUBLIC_KEY:
78 *enc = PUBKEY_PGP;
79 return TRUE;
80 default:
81 return FALSE;
82 }
83 }
84 else if (streq(form, "dnskey"))
85 {
86 switch (type)
87 {
88 case CRED_PUBLIC_KEY:
89 *enc = PUBKEY_DNSKEY;
90 return TRUE;
91 default:
92 return FALSE;
93 }
94 }
95 else if (streq(form, "sshkey"))
96 {
97 switch (type)
98 {
99 case CRED_PUBLIC_KEY:
100 *enc = PUBKEY_SSHKEY;
101 return TRUE;
102 default:
103 return FALSE;
104 }
105 }
106 return FALSE;
107 }
108
109 /**
110 * Convert a time string to struct tm using strptime format
111 */
112 static bool convert_time(char *str, char *format, struct tm *tm)
113 {
114 #ifdef HAVE_STRPTIME
115
116 char *end;
117
118 if (!format)
119 {
120 format = "%d.%m.%y %T";
121 }
122
123 end = strptime(str, format, tm);
124 if (end == NULL || *end != '\0')
125 {
126 return FALSE;
127 }
128 return TRUE;
129
130 #else /* !HAVE_STRPTIME */
131
132 if (format)
133 {
134 fprintf(stderr, "custom datetime string format not supported\n");
135 return FALSE;
136 }
137
138 if (sscanf(str, "%d.%d.%d %d:%d:%d",
139 &tm->tm_mday, &tm->tm_mon, &tm->tm_year,
140 &tm->tm_hour, &tm->tm_min, &tm->tm_sec) != 6)
141 {
142 return FALSE;
143 }
144 /* strptime() interprets two-digit years > 68 as 19xx, do the same here.
145 * mktime() expects years based on 1900 */
146 if (tm->tm_year <= 68)
147 {
148 tm->tm_year += 100;
149 }
150 else if (tm->tm_year >= 1900)
151 { /* looks like four digits? */
152 tm->tm_year -= 1900;
153 }
154 /* month is specified from 0-11 */
155 tm->tm_mon--;
156 /* automatically detect daylight saving time */
157 tm->tm_isdst = -1;
158 return TRUE;
159
160 #endif /* !HAVE_STRPTIME */
161 }
162
163 /**
164 * See header
165 */
166 bool calculate_lifetime(char *format, char *nbstr, char *nastr, time_t span,
167 time_t *nb, time_t *na)
168 {
169 struct tm tm;
170 time_t now;
171
172 now = time(NULL);
173
174 localtime_r(&now, &tm);
175 if (nbstr)
176 {
177 if (!convert_time(nbstr, format, &tm))
178 {
179 return FALSE;
180 }
181 }
182 *nb = mktime(&tm);
183 if (*nb == -1)
184 {
185 return FALSE;
186 }
187
188 localtime_r(&now, &tm);
189 if (nastr)
190 {
191 if (!convert_time(nastr, format, &tm))
192 {
193 return FALSE;
194 }
195 }
196 *na = mktime(&tm);
197 if (*na == -1)
198 {
199 return FALSE;
200 }
201
202 if (!nbstr && nastr)
203 {
204 *nb = *na - span;
205 }
206 else if (!nastr)
207 {
208 *na = *nb + span;
209 }
210 return TRUE;
211 }
212
213 /**
214 * Set output file mode appropriate for credential encoding form on Windows
215 */
216 void set_file_mode(FILE *stream, cred_encoding_type_t enc)
217 {
218 #ifdef WIN32
219 int fd;
220
221 switch (enc)
222 {
223 case CERT_PEM:
224 case PRIVKEY_PEM:
225 case PUBKEY_PEM:
226 /* keep default text mode */
227 return;
228 default:
229 /* switch to binary mode */
230 break;
231 }
232 fd = fileno(stream);
233 if (fd != -1)
234 {
235 _setmode(fd, _O_BINARY);
236 }
237 #endif
238 }
239
240 /**
241 * Determine a default hash algorithm for the given key
242 */
243 static hash_algorithm_t get_default_digest(private_key_t *private)
244 {
245 enumerator_t *enumerator;
246 signature_params_t *params;
247 hash_algorithm_t alg = HASH_UNKNOWN;
248
249 enumerator = signature_schemes_for_key(private->get_type(private),
250 private->get_keysize(private));
251 if (enumerator->enumerate(enumerator, &params))
252 {
253 alg = hasher_from_signature_scheme(params->scheme, params->params);
254 }
255 enumerator->destroy(enumerator);
256
257 /* default to SHA-256 */
258 return alg == HASH_UNKNOWN ? HASH_SHA256 : alg;
259 }
260
261 /*
262 * Described in header
263 */
264 signature_params_t *get_signature_scheme(private_key_t *private,
265 hash_algorithm_t digest, bool pss)
266 {
267 signature_params_t *scheme, *selected = NULL;
268 enumerator_t *enumerator;
269
270 if (private->supported_signature_schemes)
271 {
272 enumerator = private->supported_signature_schemes(private);
273 while (enumerator->enumerate(enumerator, &scheme))
274 {
275 if (private->get_type(private) == KEY_RSA &&
276 pss != (scheme->scheme == SIGN_RSA_EMSA_PSS))
277 {
278 continue;
279 }
280 if (digest == HASH_UNKNOWN ||
281 digest == hasher_from_signature_scheme(scheme->scheme,
282 scheme->params))
283 {
284 selected = signature_params_clone(scheme);
285 break;
286 }
287 }
288 enumerator->destroy(enumerator);
289 return selected;
290 }
291
292 if (digest == HASH_UNKNOWN)
293 {
294 digest = get_default_digest(private);
295 }
296 if (private->get_type(private) == KEY_RSA && pss)
297 {
298 rsa_pss_params_t pss_params = {
299 .hash = digest,
300 .mgf1_hash = digest,
301 .salt_len = RSA_PSS_SALT_LEN_DEFAULT,
302 };
303 signature_params_t pss_scheme = {
304 .scheme = SIGN_RSA_EMSA_PSS,
305 .params = &pss_params,
306 };
307 rsa_pss_params_set_salt_len(&pss_params, 0);
308 scheme = signature_params_clone(&pss_scheme);
309 }
310 else
311 {
312 INIT(scheme,
313 .scheme = signature_scheme_from_oid(
314 hasher_signature_algorithm_to_oid(digest,
315 private->get_type(private))),
316 );
317 }
318 return scheme;
319 }
320
321 /*
322 * Described in header
323 */
324 traffic_selector_t* parse_ts(char *str)
325 {
326 ts_type_t type = TS_IPV4_ADDR_RANGE;
327 char *to, from[64];
328
329 if (strchr(str, ':'))
330 {
331 type = TS_IPV6_ADDR_RANGE;
332 }
333 to = strchr(str, '-');
334 if (to)
335 {
336 snprintf(from, sizeof(from), "%.*s", (int)(to - str), str);
337 to++;
338 return traffic_selector_create_from_string(0, type, from, 0, to, 65535);
339 }
340 return traffic_selector_create_from_cidr(str, 0, 0, 65535);
341 }
342
343 /**
344 * Callback credential set pki uses
345 */
346 static callback_cred_t *cb_set;
347
348 /**
349 * Credential set to cache entered secrets
350 */
351 static mem_cred_t *cb_creds;
352
353 static shared_key_type_t prompted;
354
355 /**
356 * Callback function to receive credentials
357 */
358 static shared_key_t* cb(void *data, shared_key_type_t type,
359 identification_t *me, identification_t *other,
360 id_match_t *match_me, id_match_t *match_other)
361 {
362 char buf[64], *label, *secret = NULL;
363 shared_key_t *shared;
364
365 if (prompted == type)
366 {
367 return NULL;
368 }
369 switch (type)
370 {
371 case SHARED_PIN:
372 label = "Smartcard PIN";
373 break;
374 case SHARED_PRIVATE_KEY_PASS:
375 label = "Private key passphrase";
376 break;
377 default:
378 return NULL;
379 }
380 snprintf(buf, sizeof(buf), "%s: ", label);
381 #ifdef HAVE_GETPASS
382 secret = getpass(buf);
383 #endif
384 if (secret && strlen(secret))
385 {
386 prompted = type;
387 if (match_me)
388 {
389 *match_me = ID_MATCH_PERFECT;
390 }
391 if (match_other)
392 {
393 *match_other = ID_MATCH_NONE;
394 }
395 shared = shared_key_create(type, chunk_clone(chunk_from_str(secret)));
396 /* cache password in case it is required more than once */
397 cb_creds->add_shared(cb_creds, shared, NULL);
398 return shared->get_ref(shared);
399 }
400 return NULL;
401 }
402
403 /**
404 * Register PIN/Passphrase callback function
405 */
406 static void add_callback()
407 {
408 cb_set = callback_cred_create_shared(cb, NULL);
409 lib->credmgr->add_set(lib->credmgr, &cb_set->set);
410 cb_creds = mem_cred_create();
411 lib->credmgr->add_set(lib->credmgr, &cb_creds->set);
412 }
413
414 /**
415 * Unregister PIN/Passphrase callback function
416 */
417 static void remove_callback()
418 {
419 lib->credmgr->remove_set(lib->credmgr, &cb_creds->set);
420 cb_creds->destroy(cb_creds);
421 lib->credmgr->remove_set(lib->credmgr, &cb_set->set);
422 cb_set->destroy(cb_set);
423 }
424
425 /**
426 * Library initialization and operation parsing
427 */
428 int main(int argc, char *argv[])
429 {
430 atexit(library_deinit);
431 if (!library_init(NULL, "pki"))
432 {
433 exit(SS_RC_LIBSTRONGSWAN_INTEGRITY);
434 }
435 if (lib->integrity &&
436 !lib->integrity->check_file(lib->integrity, "pki", argv[0]))
437 {
438 fprintf(stderr, "integrity check of pki failed\n");
439 exit(SS_RC_DAEMON_INTEGRITY);
440 }
441 if (!lib->plugins->load(lib->plugins,
442 lib->settings->get_str(lib->settings, "pki.load", PLUGINS)))
443 {
444 exit(SS_RC_INITIALIZATION_FAILED);
445 }
446
447 add_callback();
448 atexit(remove_callback);
449 return command_dispatch(argc, argv);
450 }