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