|
Line 0
Link Here
|
|
|
1 |
/* |
| 2 |
* Copyright(c) 2005-2006 Alon Bar-Lev. All rights reserved. |
| 3 |
* |
| 4 |
* The _ssh_from_x509 is dirived of Tatu and Markus work. |
| 5 |
* Copyright(c) 2006 Alon bar-Lev <alon.barlev@gmail.com>. All rights reserved. |
| 6 |
* Copyright(c) 2000, 2001 Markus Friedl. All rights reserved. |
| 7 |
* Copyright(c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
| 8 |
* |
| 9 |
* Redistribution and use in source and binary forms, with or without |
| 10 |
* modification, are permitted provided that the following conditions |
| 11 |
* are met: |
| 12 |
* 1. Redistributions of source code must retain the above copyright |
| 13 |
* notice, this list of conditions and the following disclaimer. |
| 14 |
* 2. Redistributions in binary form must reproduce the above copyright |
| 15 |
* notice, this list of conditions and the following disclaimer in the |
| 16 |
* documentation and/or other materials provided with the distribution. |
| 17 |
* |
| 18 |
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
| 19 |
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
| 20 |
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
| 21 |
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
| 22 |
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES(INCLUDING, BUT |
| 23 |
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| 24 |
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| 25 |
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 26 |
*(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
| 27 |
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 28 |
*/ |
| 29 |
|
| 30 |
#ifdef ENABLE_PKCS11 |
| 31 |
|
| 32 |
#include <string.h> |
| 33 |
#include <unistd.h> |
| 34 |
#include <sys/wait.h> |
| 35 |
#include <errno.h> |
| 36 |
#include <pkcs11-helper-1.0/pkcs11h-certificate.h> |
| 37 |
#include <pkcs11-helper-1.0/pkcs11h-openssl.h> |
| 38 |
#include "openssl/pem.h" |
| 39 |
#include "misc.h" |
| 40 |
#include "xmalloc.h" |
| 41 |
#include "log.h" |
| 42 |
#include "pkcs11.h" |
| 43 |
|
| 44 |
static char * |
| 45 |
_ssh_from_x509(X509 *); |
| 46 |
|
| 47 |
static time_t |
| 48 |
__mytime(void) |
| 49 |
{ |
| 50 |
return time(NULL); |
| 51 |
} |
| 52 |
|
| 53 |
static void |
| 54 |
__mysleep(const unsigned long usec) |
| 55 |
{ |
| 56 |
usleep((unsigned)usec); |
| 57 |
} |
| 58 |
|
| 59 |
static int |
| 60 |
__mygettimeofday(struct timeval *tv) |
| 61 |
{ |
| 62 |
return gettimeofday(tv, NULL); |
| 63 |
} |
| 64 |
|
| 65 |
static pkcs11h_engine_system_t s_pkcs11h_sys_engine = { |
| 66 |
xmalloc, |
| 67 |
xfree, |
| 68 |
__mytime, |
| 69 |
__mysleep, |
| 70 |
__mygettimeofday |
| 71 |
}; |
| 72 |
|
| 73 |
static LogLevel |
| 74 |
_pkcs11_msg_pkcs112openssh(const unsigned flags) |
| 75 |
{ |
| 76 |
LogLevel openssh_flags; |
| 77 |
|
| 78 |
switch (flags) { |
| 79 |
case PKCS11H_LOG_DEBUG2: |
| 80 |
openssh_flags = SYSLOG_LEVEL_DEBUG3; |
| 81 |
break; |
| 82 |
case PKCS11H_LOG_DEBUG1: |
| 83 |
openssh_flags = SYSLOG_LEVEL_DEBUG2; |
| 84 |
break; |
| 85 |
case PKCS11H_LOG_INFO: |
| 86 |
openssh_flags = SYSLOG_LEVEL_INFO; |
| 87 |
break; |
| 88 |
case PKCS11H_LOG_WARN: |
| 89 |
openssh_flags = SYSLOG_LEVEL_ERROR; |
| 90 |
break; |
| 91 |
case PKCS11H_LOG_ERROR: |
| 92 |
openssh_flags = SYSLOG_LEVEL_FATAL; |
| 93 |
break; |
| 94 |
default: |
| 95 |
openssh_flags = SYSLOG_LEVEL_FATAL; |
| 96 |
break; |
| 97 |
} |
| 98 |
|
| 99 |
return openssh_flags; |
| 100 |
} |
| 101 |
|
| 102 |
static unsigned |
| 103 |
_pkcs11_msg_openssh2pkcs11(const LogLevel flags) |
| 104 |
{ |
| 105 |
unsigned pkcs11_flags; |
| 106 |
|
| 107 |
switch (flags) { |
| 108 |
case SYSLOG_LEVEL_DEBUG3: |
| 109 |
pkcs11_flags = PKCS11H_LOG_DEBUG2; |
| 110 |
break; |
| 111 |
case SYSLOG_LEVEL_DEBUG2: |
| 112 |
pkcs11_flags = PKCS11H_LOG_DEBUG1; |
| 113 |
break; |
| 114 |
case SYSLOG_LEVEL_INFO: |
| 115 |
pkcs11_flags = PKCS11H_LOG_INFO; |
| 116 |
break; |
| 117 |
case SYSLOG_LEVEL_ERROR: |
| 118 |
pkcs11_flags = PKCS11H_LOG_WARN; |
| 119 |
break; |
| 120 |
case SYSLOG_LEVEL_FATAL: |
| 121 |
pkcs11_flags = PKCS11H_LOG_ERROR; |
| 122 |
break; |
| 123 |
default: |
| 124 |
pkcs11_flags = PKCS11H_LOG_ERROR; |
| 125 |
break; |
| 126 |
} |
| 127 |
|
| 128 |
return pkcs11_flags; |
| 129 |
} |
| 130 |
|
| 131 |
static void |
| 132 |
_pkcs11_openssh_log(void *const global_data, unsigned flags, |
| 133 |
const char *const format, va_list args) |
| 134 |
{ |
| 135 |
(void) global_data; |
| 136 |
|
| 137 |
do_log(_pkcs11_msg_pkcs112openssh(flags), format, args); |
| 138 |
} |
| 139 |
|
| 140 |
static PKCS11H_BOOL |
| 141 |
_pkcs11_ssh_token_prompt(void *const global_data, |
| 142 |
void *const user_data, |
| 143 |
const pkcs11h_token_id_t token, IN const unsigned retry) |
| 144 |
{ |
| 145 |
(void) global_data; |
| 146 |
(void) user_data; |
| 147 |
(void) retry; |
| 148 |
|
| 149 |
return ask_permission("Please insert token '%s'", token->display); |
| 150 |
} |
| 151 |
|
| 152 |
static PKCS11H_BOOL |
| 153 |
_pkcs11_ssh_pin_prompt(void *const global_data, |
| 154 |
void *const user_data, const pkcs11h_token_id_t token, |
| 155 |
const unsigned retry, char *const pin, const size_t pin_max) |
| 156 |
{ |
| 157 |
char prompt[1024]; |
| 158 |
char *passphrase = NULL; |
| 159 |
PKCS11H_BOOL ret = FALSE; |
| 160 |
|
| 161 |
(void) global_data; |
| 162 |
(void) user_data; |
| 163 |
(void) retry; |
| 164 |
|
| 165 |
if (snprintf(prompt, sizeof(prompt), "Please enter PIN for token '%s': ", |
| 166 |
token->display) < 0) |
| 167 |
goto cleanup; |
| 168 |
|
| 169 |
passphrase = read_passphrase(prompt, RP_ALLOW_EOF); |
| 170 |
|
| 171 |
if (passphrase == NULL || strlen(passphrase) == 0 || |
| 172 |
strlen(passphrase) > pin_max-1) |
| 173 |
goto cleanup; |
| 174 |
|
| 175 |
strncpy(pin, passphrase, pin_max); |
| 176 |
|
| 177 |
ret = TRUE; |
| 178 |
|
| 179 |
cleanup: |
| 180 |
|
| 181 |
if (passphrase != NULL) |
| 182 |
xfree(passphrase); |
| 183 |
|
| 184 |
return ret; |
| 185 |
} |
| 186 |
|
| 187 |
static int |
| 188 |
_pkcs11_convert_to_ssh_key(const pkcs11h_certificate_id_t certificate_id, Key **const key, |
| 189 |
char **const comment, const int pin_cache_period) |
| 190 |
{ |
| 191 |
pkcs11h_certificate_t certificate = NULL; |
| 192 |
pkcs11h_certificate_id_t certificate_id_new = NULL; |
| 193 |
pkcs11h_openssl_session_t openssl_session = NULL; |
| 194 |
Key *internal_key = NULL; |
| 195 |
char *internal_comment = NULL; |
| 196 |
RSA *rsa = NULL; |
| 197 |
size_t temp; |
| 198 |
CK_RV rv = CKR_OK; |
| 199 |
|
| 200 |
*key = NULL; |
| 201 |
*comment = NULL; |
| 202 |
|
| 203 |
debug3("PKCS#11: _pkcs11_convert_to_ssh_key - entered - certificate=%p, " |
| 204 |
"key=%p, comment=%p, pin_cache_period=%d", (void *) certificate, |
| 205 |
(void *) key, (void *) comment, pin_cache_period); |
| 206 |
|
| 207 |
if ((rv = pkcs11h_certificate_create(certificate_id, NULL, |
| 208 |
PKCS11H_PROMPT_MASK_ALLOW_ALL, pin_cache_period, |
| 209 |
&certificate)) != CKR_OK) { |
| 210 |
error("PKCS#11: Cannot get certificate %ld-'%s'", rv, |
| 211 |
pkcs11h_getMessage(rv)); |
| 212 |
goto cleanup; |
| 213 |
} |
| 214 |
|
| 215 |
/* |
| 216 |
* New certificate_id is constructed from certificate |
| 217 |
* blob so that it will contian the proper description. |
| 218 |
*/ |
| 219 |
|
| 220 |
if ((rv = pkcs11h_certificate_getCertificateBlob(certificate, |
| 221 |
NULL, &temp)) != CKR_OK) { |
| 222 |
error("PKCS#11: Cannot get certificate blob %ld-'%s'", rv, |
| 223 |
pkcs11h_getMessage(rv)); |
| 224 |
goto cleanup; |
| 225 |
} |
| 226 |
|
| 227 |
if ((rv = pkcs11h_certificate_getCertificateId(certificate, |
| 228 |
&certificate_id_new)) != CKR_OK) { |
| 229 |
error("PKCS#11: Cannot get certificate_id %ld-'%s'", rv, |
| 230 |
pkcs11h_getMessage(rv)); |
| 231 |
goto cleanup; |
| 232 |
} |
| 233 |
|
| 234 |
if ((internal_comment = xstrdup(certificate_id_new->displayName)) == NULL) { |
| 235 |
error("PKCS#11: Memory allocation error"); |
| 236 |
goto cleanup; |
| 237 |
} |
| 238 |
|
| 239 |
if ((openssl_session = |
| 240 |
pkcs11h_openssl_createSession(certificate)) == NULL) { |
| 241 |
error("PKCS#11: Cannot initialize openssl session"); |
| 242 |
goto cleanup; |
| 243 |
} |
| 244 |
|
| 245 |
/* |
| 246 |
* will be release by openssl_session |
| 247 |
*/ |
| 248 |
certificate = NULL; |
| 249 |
|
| 250 |
if ((rsa = pkcs11h_openssl_session_getRSA(openssl_session)) == NULL) { |
| 251 |
error("PKCS#11: Unable get rsa object"); |
| 252 |
goto cleanup; |
| 253 |
} |
| 254 |
|
| 255 |
internal_key = key_new_private(KEY_UNSPEC); |
| 256 |
internal_key->flags |= KEY_FLAG_EXT; |
| 257 |
internal_key->rsa = rsa; |
| 258 |
rsa = NULL; |
| 259 |
internal_key->type = KEY_RSA; |
| 260 |
|
| 261 |
*key = internal_key; |
| 262 |
internal_key = NULL; |
| 263 |
*comment = internal_comment; |
| 264 |
internal_comment = NULL; |
| 265 |
|
| 266 |
cleanup: |
| 267 |
if (internal_key != NULL) { |
| 268 |
key_free(internal_key); |
| 269 |
internal_key = NULL; |
| 270 |
} |
| 271 |
|
| 272 |
if (internal_comment != NULL) { |
| 273 |
xfree(internal_comment); |
| 274 |
internal_comment = NULL; |
| 275 |
} |
| 276 |
|
| 277 |
if (rsa != NULL) { |
| 278 |
RSA_free (rsa); |
| 279 |
rsa = NULL; |
| 280 |
} |
| 281 |
|
| 282 |
if (openssl_session != NULL) { |
| 283 |
pkcs11h_openssl_freeSession(openssl_session); |
| 284 |
openssl_session = NULL; |
| 285 |
} |
| 286 |
|
| 287 |
if (certificate_id_new != NULL) { |
| 288 |
pkcs11h_certificate_freeCertificateId(certificate_id_new); |
| 289 |
certificate_id_new = NULL; |
| 290 |
} |
| 291 |
|
| 292 |
if (certificate != NULL) { |
| 293 |
pkcs11h_certificate_freeCertificate(certificate); |
| 294 |
certificate = NULL; |
| 295 |
} |
| 296 |
|
| 297 |
debug3("PKCS#11: _pkcs11_convert_to_ssh_key - return *key=%p", (void *) *key); |
| 298 |
|
| 299 |
return *key != NULL; |
| 300 |
} |
| 301 |
|
| 302 |
int |
| 303 |
pkcs11_initialize(const int protected_authentication, const int pin_cache_period) |
| 304 |
{ |
| 305 |
CK_RV rv = CKR_FUNCTION_FAILED; |
| 306 |
|
| 307 |
debug3("PKCS#11: pkcs11_initialize - entered protected_authentication=%d, " |
| 308 |
"pin_cache_period=%d", protected_authentication, pin_cache_period); |
| 309 |
|
| 310 |
if ((rv = pkcs11h_engine_setSystem(&s_pkcs11h_sys_engine)) != CKR_OK) { |
| 311 |
error("PKCS#11: Cannot set system engine %ld-'%s'", rv, |
| 312 |
pkcs11h_getMessage(rv)); |
| 313 |
goto cleanup; |
| 314 |
} |
| 315 |
|
| 316 |
if ((rv = pkcs11h_initialize()) != CKR_OK) { |
| 317 |
error("PKCS#11: Cannot initialize %ld-'%s'", rv, |
| 318 |
pkcs11h_getMessage(rv)); |
| 319 |
goto cleanup; |
| 320 |
} |
| 321 |
|
| 322 |
if ((rv = pkcs11h_setLogHook(_pkcs11_openssh_log, NULL)) != CKR_OK) { |
| 323 |
error("PKCS#11: Cannot set hooks %ld-'%s'", rv, |
| 324 |
pkcs11h_getMessage(rv)); |
| 325 |
goto cleanup; |
| 326 |
} |
| 327 |
|
| 328 |
pkcs11h_setLogLevel(_pkcs11_msg_openssh2pkcs11(get_log_level ())); |
| 329 |
|
| 330 |
if ((rv = pkcs11h_setTokenPromptHook(_pkcs11_ssh_token_prompt, |
| 331 |
NULL)) != CKR_OK) { |
| 332 |
error("PKCS#11: Cannot set hooks %ld-'%s'", rv, |
| 333 |
pkcs11h_getMessage(rv)); |
| 334 |
goto cleanup; |
| 335 |
} |
| 336 |
|
| 337 |
if ((rv = pkcs11h_setPINPromptHook(_pkcs11_ssh_pin_prompt, |
| 338 |
NULL)) != CKR_OK) { |
| 339 |
error("PKCS#11: Cannot set hooks %ld-'%s'", rv, |
| 340 |
pkcs11h_getMessage(rv)); |
| 341 |
goto cleanup; |
| 342 |
} |
| 343 |
|
| 344 |
if ((rv = pkcs11h_setProtectedAuthentication(protected_authentication)) != |
| 345 |
CKR_OK) { |
| 346 |
error("PKCS#11: Cannot set protected authentication mode %ld-'%s'", |
| 347 |
rv, pkcs11h_getMessage(rv)); |
| 348 |
goto cleanup; |
| 349 |
} |
| 350 |
|
| 351 |
if ((rv = pkcs11h_setPINCachePeriod(pin_cache_period)) != CKR_OK) { |
| 352 |
error("PKCS#11: Cannot set PIN cache period %ld-'%s'", rv, |
| 353 |
pkcs11h_getMessage(rv)); |
| 354 |
goto cleanup; |
| 355 |
} |
| 356 |
|
| 357 |
rv = CKR_OK; |
| 358 |
|
| 359 |
cleanup: |
| 360 |
debug3("PKCS#11: pkcs11_initialize - return rv=%ld-'%s'", |
| 361 |
rv, pkcs11h_getMessage(rv)); |
| 362 |
|
| 363 |
return rv == CKR_OK; |
| 364 |
} |
| 365 |
|
| 366 |
void |
| 367 |
pkcs11_terminate(void) |
| 368 |
{ |
| 369 |
debug3("PKCS#11: pkcs11_terminate - entered"); |
| 370 |
|
| 371 |
pkcs11h_terminate(); |
| 372 |
|
| 373 |
debug3("PKCS#11: pkcs11_terminate - return"); |
| 374 |
} |
| 375 |
|
| 376 |
void |
| 377 |
pkcs11_free_provider(PKCS11Provider *const provider) { |
| 378 |
if (provider != NULL) { |
| 379 |
if (provider->provider != NULL) |
| 380 |
xfree(provider->provider); |
| 381 |
xfree(provider); |
| 382 |
} |
| 383 |
} |
| 384 |
|
| 385 |
PKCS11Provider * |
| 386 |
pkcs11_parse_provider(const char *const info) |
| 387 |
{ |
| 388 |
PKCS11Provider *provider = NULL; |
| 389 |
PKCS11Provider *ret = NULL; |
| 390 |
char *split[4]; |
| 391 |
char *s = NULL; |
| 392 |
char *p; |
| 393 |
int i; |
| 394 |
|
| 395 |
if (info == NULL) |
| 396 |
goto cleanup; |
| 397 |
|
| 398 |
if ((provider = (PKCS11Provider *) xmalloc(sizeof(*provider))) == NULL) |
| 399 |
goto cleanup; |
| 400 |
|
| 401 |
memset(provider, 0, sizeof(*provider)); |
| 402 |
memset(split, 0, sizeof(split)); |
| 403 |
|
| 404 |
if ((s=xstrdup(info)) == NULL) |
| 405 |
goto cleanup; |
| 406 |
|
| 407 |
p = s; |
| 408 |
i=0; |
| 409 |
while(i<4 && p != NULL) { |
| 410 |
char *t; |
| 411 |
if ((t = strchr(p, ':')) != NULL) |
| 412 |
*t = '\x0'; |
| 413 |
split[i++] = p; |
| 414 |
if (t != NULL) |
| 415 |
p = t+1; |
| 416 |
else |
| 417 |
p = NULL; |
| 418 |
} |
| 419 |
|
| 420 |
/* |
| 421 |
* provider[:protected_authentication[:private_mode[:cert_is_private]]] |
| 422 |
* string:1|0:hex:1|0 |
| 423 |
*/ |
| 424 |
if (split[0] != NULL) |
| 425 |
provider->provider = xstrdup(split[0]); |
| 426 |
if (split[1] != NULL) |
| 427 |
provider->protected_authentication = atoi(split[1]) != 0; |
| 428 |
if (split[2] != NULL) |
| 429 |
sscanf(split[2], "%x", &provider->private_mode); |
| 430 |
if (split[3] != NULL) |
| 431 |
provider->cert_is_private = atoi(split[3]) != 0; |
| 432 |
|
| 433 |
if (provider->provider == NULL || strlen(provider->provider) == 0) |
| 434 |
goto cleanup; |
| 435 |
|
| 436 |
ret = provider; |
| 437 |
provider = NULL; |
| 438 |
|
| 439 |
cleanup: |
| 440 |
if (s != NULL) |
| 441 |
xfree(s); |
| 442 |
|
| 443 |
if (provider != NULL) |
| 444 |
pkcs11_free_provider(provider); |
| 445 |
|
| 446 |
return ret; |
| 447 |
} |
| 448 |
|
| 449 |
int |
| 450 |
pkcs11_add_provider(const PKCS11Provider *const provider) |
| 451 |
{ |
| 452 |
CK_RV rv = CKR_OK; |
| 453 |
|
| 454 |
debug3("PKCS#11: pkcs11_add_provider - entered - provider='%s', " |
| 455 |
"protected_authentication=%d, private_mode='%08x', cert_is_private=%d", |
| 456 |
provider->provider, provider->protected_authentication ? 1 : 0, |
| 457 |
provider->private_mode, provider->cert_is_private ? 1 : 0); |
| 458 |
|
| 459 |
debug("PKCS#11: Adding PKCS#11 provider '%s'", provider->provider); |
| 460 |
|
| 461 |
if (rv == CKR_OK && |
| 462 |
(rv = pkcs11h_addProvider(provider->provider, provider->provider, |
| 463 |
provider->protected_authentication, provider->private_mode, |
| 464 |
PKCS11H_SLOTEVENT_METHOD_AUTO, |
| 465 |
0, provider->cert_is_private)) != CKR_OK) { |
| 466 |
error("PKCS#11: Cannot initialize provider '%s' %ld-'%s'", |
| 467 |
provider->provider, rv, pkcs11h_getMessage(rv)); |
| 468 |
} |
| 469 |
|
| 470 |
debug3("PKCS#11: pkcs11_add_provider - return rv=%ld-'%s'", |
| 471 |
rv, pkcs11h_getMessage(rv)); |
| 472 |
|
| 473 |
return rv == CKR_OK; |
| 474 |
} |
| 475 |
|
| 476 |
PKCS11Id * |
| 477 |
pkcs11_id_new(void) |
| 478 |
{ |
| 479 |
PKCS11Id *id = (PKCS11Id *) xmalloc(sizeof(*id)); |
| 480 |
if (id != NULL) { |
| 481 |
memset(id, 0, sizeof(*id)); |
| 482 |
id->pin_cache_period = PKCS11H_PIN_CACHE_INFINITE; |
| 483 |
} |
| 484 |
return id; |
| 485 |
} |
| 486 |
|
| 487 |
void |
| 488 |
pkcs11_id_free(PKCS11Id *const id) |
| 489 |
{ |
| 490 |
if (id != NULL) |
| 491 |
xfree(id); |
| 492 |
} |
| 493 |
|
| 494 |
int |
| 495 |
pkcs11_get_key(const PKCS11Id *const id, Key **const key, |
| 496 |
char **const comment) |
| 497 |
{ |
| 498 |
pkcs11h_certificate_id_t certificate_id = NULL; |
| 499 |
CK_RV rv = CKR_OK; |
| 500 |
|
| 501 |
debug3("PKCS#11: pkcs11_get_key - entered - id=%p, key=%p, " |
| 502 |
"comment=%p", (const void *) id, (void *) key, (void *) comment); |
| 503 |
|
| 504 |
debug3("PKCS#11: pkcs11_get_key - id - id=%s, " |
| 505 |
"pin_cache_period=%d, cert_file=%s", |
| 506 |
id->id, id->pin_cache_period, id->cert_file); |
| 507 |
|
| 508 |
if (pkcs11h_certificate_deserializeCertificateId(&certificate_id, id->id)) { |
| 509 |
error("PKCS#11: Cannot deserialize id %ld-'%s'", rv, |
| 510 |
pkcs11h_getMessage(rv)); |
| 511 |
goto cleanup; |
| 512 |
} |
| 513 |
|
| 514 |
if (id->cert_file != NULL && id->cert_file[0] != '\x0') { |
| 515 |
X509 *x509 = NULL; |
| 516 |
unsigned char *p = NULL; |
| 517 |
unsigned char *certificate_blob = NULL; |
| 518 |
size_t certificate_blob_size = 0; |
| 519 |
int size; |
| 520 |
FILE *fp = NULL; |
| 521 |
|
| 522 |
if ((fp = fopen(id->cert_file, "r")) == NULL) { |
| 523 |
error("PKCS#11: Cannot open file '%s'", id->cert_file); |
| 524 |
goto cleanup1; |
| 525 |
} |
| 526 |
|
| 527 |
if (!PEM_read_X509(fp, &x509, NULL, 0)) { |
| 528 |
x509 = NULL; |
| 529 |
error("PKCS#11: Cannot read PEM from file '%s'", id->cert_file); |
| 530 |
goto cleanup1; |
| 531 |
} |
| 532 |
|
| 533 |
if ((size = i2d_X509(x509, NULL)) < 0) { |
| 534 |
error("PKCS#11: Cannot read decode certificate"); |
| 535 |
goto cleanup1; |
| 536 |
} |
| 537 |
certificate_blob_size = (size_t)size; |
| 538 |
|
| 539 |
if ((certificate_blob = |
| 540 |
(unsigned char *) xmalloc(certificate_blob_size)) == NULL) { |
| 541 |
error("PKCS#11: Cannot allocate memory"); |
| 542 |
goto cleanup1; |
| 543 |
} |
| 544 |
|
| 545 |
/* |
| 546 |
* i2d_X509 increments p!!! |
| 547 |
*/ |
| 548 |
p = certificate_blob; |
| 549 |
|
| 550 |
if ((size = i2d_X509(x509, &p)) < 0) { |
| 551 |
error("PKCS#11: Cannot read decode certificate"); |
| 552 |
goto cleanup1; |
| 553 |
} |
| 554 |
certificate_blob_size = (size_t)size; |
| 555 |
|
| 556 |
if (pkcs11h_certificate_setCertificateIdCertificateBlob |
| 557 |
(certificate_id, certificate_blob, certificate_blob_size) |
| 558 |
!= CKR_OK) { |
| 559 |
error("PKCS#11: Cannot set certificate blob %ld-'%s'", rv, |
| 560 |
pkcs11h_getMessage(rv)); |
| 561 |
goto cleanup1; |
| 562 |
} |
| 563 |
|
| 564 |
cleanup1: |
| 565 |
if (x509 != NULL) { |
| 566 |
X509_free(x509); |
| 567 |
x509 = NULL; |
| 568 |
} |
| 569 |
|
| 570 |
if (certificate_blob != NULL) { |
| 571 |
xfree(certificate_blob); |
| 572 |
certificate_blob = NULL; |
| 573 |
} |
| 574 |
|
| 575 |
if (fp != NULL) { |
| 576 |
fclose(fp); |
| 577 |
fp = NULL; |
| 578 |
} |
| 579 |
} |
| 580 |
|
| 581 |
_pkcs11_convert_to_ssh_key(certificate_id, key, comment, id->pin_cache_period); |
| 582 |
|
| 583 |
cleanup: |
| 584 |
|
| 585 |
if (certificate_id != NULL) { |
| 586 |
pkcs11h_certificate_freeCertificateId(certificate_id); |
| 587 |
certificate_id = NULL; |
| 588 |
} |
| 589 |
|
| 590 |
debug3("PKCS#11: pkcs11_get_key - return rv=%ld, *key=%p", rv, ( void *) *key); |
| 591 |
|
| 592 |
return *key != NULL; |
| 593 |
} |
| 594 |
|
| 595 |
int |
| 596 |
pkcs11_get_keys(Key ***const keys, char ***const comments) |
| 597 |
{ |
| 598 |
#define PKCS11_MAX_KEYS 10 |
| 599 |
Key **internal_keys = NULL; |
| 600 |
char **internal_comments = NULL; |
| 601 |
pkcs11h_certificate_id_list_t user_certificates = NULL; |
| 602 |
pkcs11h_certificate_id_list_t current = NULL; |
| 603 |
CK_RV rv = CKR_FUNCTION_FAILED; |
| 604 |
int i; |
| 605 |
|
| 606 |
debug3("PKCS#11: pkcs11_get_keys - entered - sshkey=%p, " |
| 607 |
"comment=%p", |
| 608 |
(void *) keys, (void *) comments); |
| 609 |
|
| 610 |
*keys = NULL; |
| 611 |
*comments = NULL; |
| 612 |
|
| 613 |
if((internal_keys = xmalloc((PKCS11_MAX_KEYS+1)*sizeof(*internal_keys))) == NULL) { |
| 614 |
rv = CKR_HOST_MEMORY; |
| 615 |
goto cleanup; |
| 616 |
} |
| 617 |
|
| 618 |
if((internal_comments = xmalloc((PKCS11_MAX_KEYS+1)*sizeof(*internal_comments))) == NULL) { |
| 619 |
rv = CKR_HOST_MEMORY; |
| 620 |
goto cleanup; |
| 621 |
} |
| 622 |
|
| 623 |
memset(internal_keys, 0, (PKCS11_MAX_KEYS+1)*sizeof(*internal_keys)); |
| 624 |
memset(internal_comments, 0, (PKCS11_MAX_KEYS+1)*sizeof(*internal_comments)); |
| 625 |
|
| 626 |
if ((rv = pkcs11h_certificate_enumCertificateIds( |
| 627 |
PKCS11H_ENUM_METHOD_CACHE_EXIST, NULL, |
| 628 |
PKCS11H_PROMPT_MASK_ALLOW_ALL, NULL, |
| 629 |
&user_certificates)) != CKR_OK) { |
| 630 |
error("PKCS#11: Cannot enumerate certificates %ld-'%s'", rv, |
| 631 |
pkcs11h_getMessage(rv)); |
| 632 |
goto cleanup; |
| 633 |
} |
| 634 |
|
| 635 |
i = 0; |
| 636 |
for (current = user_certificates; current != NULL && i<PKCS11_MAX_KEYS; |
| 637 |
current = current->next) { |
| 638 |
|
| 639 |
if (_pkcs11_convert_to_ssh_key(current->certificate_id, &internal_keys[i], |
| 640 |
&internal_comments[i], PKCS11H_PIN_CACHE_INFINITE)) { |
| 641 |
i++; |
| 642 |
} |
| 643 |
} |
| 644 |
|
| 645 |
*keys = internal_keys; |
| 646 |
internal_keys = NULL; |
| 647 |
*comments = internal_comments; |
| 648 |
internal_comments = NULL; |
| 649 |
rv = CKR_OK; |
| 650 |
|
| 651 |
cleanup: |
| 652 |
if (user_certificates != NULL) { |
| 653 |
pkcs11h_certificate_freeCertificateIdList(user_certificates); |
| 654 |
user_certificates = NULL; |
| 655 |
} |
| 656 |
|
| 657 |
if (internal_keys != NULL) { |
| 658 |
Key **t = internal_keys; |
| 659 |
while (*t != NULL) { |
| 660 |
key_free(*t); |
| 661 |
t++; |
| 662 |
} |
| 663 |
xfree(internal_keys); |
| 664 |
} |
| 665 |
|
| 666 |
if (internal_comments != NULL) { |
| 667 |
char **t = internal_comments; |
| 668 |
while (*t != NULL) { |
| 669 |
xfree(*t); |
| 670 |
t++; |
| 671 |
} |
| 672 |
xfree(internal_comments); |
| 673 |
} |
| 674 |
|
| 675 |
debug3("PKCS#11: pkcs11_get_keys - return rv=%ld, *keys=%p", rv, (void *) *keys); |
| 676 |
|
| 677 |
return *keys != NULL; |
| 678 |
#undef PKCS11_MAX_KEYS |
| 679 |
} |
| 680 |
|
| 681 |
void |
| 682 |
pkcs11_show_ids(void) |
| 683 |
{ |
| 684 |
pkcs11h_certificate_id_list_t user_certificates = NULL; |
| 685 |
pkcs11h_certificate_id_list_t current = NULL; |
| 686 |
CK_RV rv = CKR_FUNCTION_FAILED; |
| 687 |
|
| 688 |
if ((rv = pkcs11h_certificate_enumCertificateIds( |
| 689 |
PKCS11H_ENUM_METHOD_CACHE_EXIST, NULL, |
| 690 |
PKCS11H_PROMPT_MASK_ALLOW_ALL, NULL, |
| 691 |
&user_certificates)) != CKR_OK) { |
| 692 |
error("PKCS#11: Cannot enumerate certificates %ld-'%s'", rv, |
| 693 |
pkcs11h_getMessage(rv)); |
| 694 |
goto cleanup; |
| 695 |
} |
| 696 |
|
| 697 |
for (current = user_certificates; current != NULL; |
| 698 |
current = current->next) { |
| 699 |
|
| 700 |
pkcs11h_certificate_t certificate = NULL; |
| 701 |
X509 *x509 = NULL; |
| 702 |
|
| 703 |
BIO *bio = NULL; |
| 704 |
char dn[1024] = { 0 }; |
| 705 |
char serial[1024] = { 0 }; |
| 706 |
char *ser = NULL; |
| 707 |
char *ssh_key = NULL; |
| 708 |
size_t ser_len = 0; |
| 709 |
int n; |
| 710 |
|
| 711 |
if ((rv = pkcs11h_certificate_serializeCertificateId(NULL, |
| 712 |
&ser_len, current->certificate_id)) != CKR_OK) { |
| 713 |
error("PKCS#11: Cannot serialize certificate id " |
| 714 |
"certificates %ld-'%s'", rv, |
| 715 |
pkcs11h_getMessage(rv)); |
| 716 |
goto cleanup1; |
| 717 |
} |
| 718 |
|
| 719 |
if ((ser = (char *) xmalloc(ser_len)) == NULL) { |
| 720 |
error("PKCS#11: Cannot allocate memory"); |
| 721 |
goto cleanup1; |
| 722 |
} |
| 723 |
|
| 724 |
if ((rv = pkcs11h_certificate_serializeCertificateId(ser, |
| 725 |
&ser_len, current->certificate_id)) != CKR_OK) { |
| 726 |
error("PKCS#11: Cannot serialize certificate " |
| 727 |
"id certificates %ld-'%s'", |
| 728 |
rv, pkcs11h_getMessage(rv)); |
| 729 |
goto cleanup1; |
| 730 |
} |
| 731 |
|
| 732 |
if ((rv = pkcs11h_certificate_create(current->certificate_id, |
| 733 |
NULL, PKCS11H_PROMPT_MASK_ALLOW_ALL, |
| 734 |
PKCS11H_PIN_CACHE_INFINITE, &certificate)) != CKR_OK) { |
| 735 |
error("PKCS#11: Cannot create certificate %ld-'%s'", rv, |
| 736 |
pkcs11h_getMessage(rv)); |
| 737 |
goto cleanup1; |
| 738 |
} |
| 739 |
|
| 740 |
if ((x509 = pkcs11h_openssl_getX509(certificate)) == NULL) { |
| 741 |
error("PKCS#11: Cannot get X509"); |
| 742 |
goto cleanup1; |
| 743 |
} |
| 744 |
|
| 745 |
X509_NAME_oneline(X509_get_subject_name(x509), dn, sizeof(dn)); |
| 746 |
|
| 747 |
if ((bio = BIO_new(BIO_s_mem())) == NULL) { |
| 748 |
error("PKCS#11: Cannot create BIO"); |
| 749 |
goto cleanup1; |
| 750 |
} |
| 751 |
|
| 752 |
i2a_ASN1_INTEGER(bio, X509_get_serialNumber(x509)); |
| 753 |
n = BIO_read(bio, serial, sizeof(serial) - 1); |
| 754 |
if (n < 0) |
| 755 |
serial[0] = '\x0'; |
| 756 |
else |
| 757 |
serial[n] = 0; |
| 758 |
|
| 759 |
printf(("\n" |
| 760 |
"********************************************\n" |
| 761 |
"IDENTITY:\n" |
| 762 |
" DN: %s\n" |
| 763 |
" Serial: %s\n" |
| 764 |
" Serialized id: %s\n" |
| 765 |
"\n" " Certificate:\n"), dn, serial, ser); |
| 766 |
PEM_write_X509(stdout, x509); |
| 767 |
|
| 768 |
if ((ssh_key = _ssh_from_x509(x509)) != NULL) { |
| 769 |
printf(("\n" " SSH:\n" "%s\n"), ssh_key); |
| 770 |
|
| 771 |
xfree(ssh_key); |
| 772 |
} |
| 773 |
|
| 774 |
cleanup1: |
| 775 |
if (x509 != NULL) { |
| 776 |
X509_free(x509); |
| 777 |
x509 = NULL; |
| 778 |
} |
| 779 |
|
| 780 |
if (bio != NULL) { |
| 781 |
BIO_free_all(bio); |
| 782 |
bio = NULL; |
| 783 |
} |
| 784 |
|
| 785 |
if (certificate != NULL) { |
| 786 |
pkcs11h_certificate_freeCertificate(certificate); |
| 787 |
certificate = NULL; |
| 788 |
} |
| 789 |
|
| 790 |
if (ser != NULL) { |
| 791 |
xfree(ser); |
| 792 |
ser = NULL; |
| 793 |
} |
| 794 |
} |
| 795 |
|
| 796 |
cleanup: |
| 797 |
if (user_certificates != NULL) { |
| 798 |
pkcs11h_certificate_freeCertificateIdList(user_certificates); |
| 799 |
user_certificates = NULL; |
| 800 |
} |
| 801 |
} |
| 802 |
|
| 803 |
static char * |
| 804 |
_ssh_from_x509(X509 * x509) |
| 805 |
{ |
| 806 |
#define PUT_32BIT(cp, value) ( \ |
| 807 |
(cp)[0] = (unsigned char)((value) >> 24), \ |
| 808 |
(cp)[1] = (unsigned char)((value) >> 16), \ |
| 809 |
(cp)[2] = (unsigned char)((value) >> 8), \ |
| 810 |
(cp)[3] = (unsigned char)((value) >> 0) ) |
| 811 |
|
| 812 |
EVP_PKEY *pubkey = NULL; |
| 813 |
BIO *bio = NULL, *bio2 = NULL, *bio64 = NULL; |
| 814 |
unsigned char *blob = NULL, *buffer = NULL; |
| 815 |
char *ret = NULL; |
| 816 |
size_t blobsize = 0, retsize = 0; |
| 817 |
size_t bytes_name = 0, bytes_exponent = 0, bytes_modulus = 0; |
| 818 |
unsigned char *bp; |
| 819 |
char *p; |
| 820 |
int n; |
| 821 |
const char *keyname = NULL; |
| 822 |
int ok = 0; |
| 823 |
|
| 824 |
if ((pubkey = X509_get_pubkey(x509)) == NULL) |
| 825 |
goto cleanup; |
| 826 |
|
| 827 |
if ((bio64 = BIO_new(BIO_f_base64())) == NULL) |
| 828 |
goto cleanup; |
| 829 |
|
| 830 |
if ((bio = BIO_new(BIO_s_mem())) == NULL) |
| 831 |
goto cleanup; |
| 832 |
|
| 833 |
if ((bio2 = BIO_push(bio64, bio)) == NULL) |
| 834 |
goto cleanup; |
| 835 |
|
| 836 |
if (pubkey->type != EVP_PKEY_RSA) |
| 837 |
goto cleanup; |
| 838 |
|
| 839 |
keyname = "ssh-rsa"; |
| 840 |
|
| 841 |
bytes_name = strlen(keyname); |
| 842 |
bytes_exponent = (size_t)BN_num_bytes(pubkey->pkey.rsa->e); |
| 843 |
bytes_modulus = (size_t)BN_num_bytes(pubkey->pkey.rsa->n); |
| 844 |
|
| 845 |
blobsize = (4 + bytes_name + 4 + ((unsigned)bytes_exponent + 1) + 4 + |
| 846 |
((unsigned)bytes_modulus + 1) + 1); |
| 847 |
|
| 848 |
if ((blob = (unsigned char *) xmalloc(blobsize)) == NULL) |
| 849 |
goto cleanup; |
| 850 |
|
| 851 |
if ((buffer = (unsigned char *) xmalloc(blobsize)) == NULL) |
| 852 |
goto cleanup; |
| 853 |
|
| 854 |
bp = blob; |
| 855 |
|
| 856 |
PUT_32BIT(bp, bytes_name), bp += 4; |
| 857 |
memcpy(bp, keyname, bytes_name), bp += (ssize_t)bytes_name; |
| 858 |
|
| 859 |
BN_bn2bin(pubkey->pkey.rsa->e, buffer); |
| 860 |
if (buffer[0] & 0x80) { |
| 861 |
// highest bit set would indicate a negative number. |
| 862 |
// to avoid this, we have to spend an extra byte: |
| 863 |
PUT_32BIT(bp, bytes_exponent + 1), bp += 4; |
| 864 |
*(bp++) = 0; |
| 865 |
} else |
| 866 |
PUT_32BIT(bp, bytes_exponent), bp += 4; |
| 867 |
|
| 868 |
memcpy(bp, buffer, bytes_exponent), bp += (ssize_t)bytes_exponent; |
| 869 |
|
| 870 |
BN_bn2bin(pubkey->pkey.rsa->n, buffer); |
| 871 |
if (buffer[0] & 0x80) { |
| 872 |
PUT_32BIT(bp, bytes_modulus + 1), bp += 4; |
| 873 |
*(bp++) = 0; |
| 874 |
} else |
| 875 |
PUT_32BIT(bp, bytes_modulus), bp += 4; |
| 876 |
|
| 877 |
memcpy(bp, buffer, bytes_modulus), bp += (ssize_t)bytes_modulus; |
| 878 |
|
| 879 |
if (BIO_write(bio2, blob, (int)(bp - blob)) == -1) |
| 880 |
goto cleanup; |
| 881 |
|
| 882 |
if (BIO_flush(bio2) == -1) |
| 883 |
goto cleanup; |
| 884 |
|
| 885 |
/* |
| 886 |
* Allocate the newline too... We will remove them later |
| 887 |
* For MS, allocate return as well. |
| 888 |
*/ |
| 889 |
retsize = strlen(keyname) + 1 + (blobsize * 4 / 3) + (blobsize * 2 / 50) + |
| 890 |
10 + 1; |
| 891 |
|
| 892 |
if ((ret = (char *) xmalloc(retsize)) == NULL) |
| 893 |
goto cleanup; |
| 894 |
|
| 895 |
if (snprintf(ret, retsize, "%s ", keyname) < 0) |
| 896 |
goto cleanup; |
| 897 |
|
| 898 |
if ((n = BIO_read(bio, ret + (ssize_t)strlen(ret), |
| 899 |
(int)(retsize - strlen(ret) - 1))) == -1) |
| 900 |
goto cleanup; |
| 901 |
|
| 902 |
ret[(int)strlen(keyname) + 1 + n] = '\x0'; |
| 903 |
|
| 904 |
while ((p = strchr(ret, '\n')) != NULL) |
| 905 |
memmove(p, p + 1, strlen(p) + 1); |
| 906 |
while ((p = strchr(ret, '\r')) != NULL) |
| 907 |
memmove(p, p + 1, strlen(p) + 1); |
| 908 |
|
| 909 |
ok = 1; |
| 910 |
|
| 911 |
cleanup: |
| 912 |
if (bio != NULL) { |
| 913 |
BIO_free_all(bio); |
| 914 |
bio = NULL; |
| 915 |
} |
| 916 |
|
| 917 |
if (pubkey != NULL) { |
| 918 |
EVP_PKEY_free(pubkey); |
| 919 |
pubkey = NULL; |
| 920 |
} |
| 921 |
|
| 922 |
if (buffer != NULL) { |
| 923 |
xfree(buffer); |
| 924 |
buffer = NULL; |
| 925 |
} |
| 926 |
|
| 927 |
if (blob != NULL) { |
| 928 |
xfree(blob); |
| 929 |
blob = NULL; |
| 930 |
} |
| 931 |
|
| 932 |
if (!ok) { |
| 933 |
if (ret != NULL) { |
| 934 |
xfree(ret); |
| 935 |
ret = NULL; |
| 936 |
} |
| 937 |
} |
| 938 |
|
| 939 |
return ret; |
| 940 |
|
| 941 |
#undef PUT_32BIT |
| 942 |
} |
| 943 |
|
| 944 |
#endif /* ENABLE_PKCS11 */ |