ssh-pkcs11-client.c revision 1.17
1/* $NetBSD: ssh-pkcs11-client.c,v 1.17 2021/03/05 17:47:16 christos Exp $ */ 2/* $OpenBSD: ssh-pkcs11-client.c,v 1.17 2020/10/18 11:32:02 djm Exp $ */ 3 4/* 5 * Copyright (c) 2010 Markus Friedl. All rights reserved. 6 * Copyright (c) 2014 Pedro Martelletto. All rights reserved. 7 * 8 * Permission to use, copy, modify, and distribute this software for any 9 * purpose with or without fee is hereby granted, provided that the above 10 * copyright notice and this permission notice appear in all copies. 11 * 12 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 13 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 14 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 15 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 16 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 17 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 18 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 19 */ 20#include "includes.h" 21__RCSID("$NetBSD: ssh-pkcs11-client.c,v 1.17 2021/03/05 17:47:16 christos Exp $"); 22 23#include <sys/types.h> 24#include <sys/time.h> 25#include <sys/socket.h> 26 27#include <stdarg.h> 28#include <string.h> 29#include <unistd.h> 30#include <errno.h> 31 32#include <openssl/ecdsa.h> 33#include <openssl/rsa.h> 34 35#include "pathnames.h" 36#include "xmalloc.h" 37#include "sshbuf.h" 38#include "log.h" 39#include "misc.h" 40#include "sshkey.h" 41#include "authfd.h" 42#include "atomicio.h" 43#include "ssh-pkcs11.h" 44#include "ssherr.h" 45 46/* borrows code from sftp-server and ssh-agent */ 47 48static int fd = -1; 49static pid_t pid = -1; 50 51static void 52send_msg(struct sshbuf *m) 53{ 54 u_char buf[4]; 55 size_t mlen = sshbuf_len(m); 56 int r; 57 58 POKE_U32(buf, mlen); 59 if (atomicio(vwrite, fd, buf, 4) != 4 || 60 atomicio(vwrite, fd, sshbuf_mutable_ptr(m), 61 sshbuf_len(m)) != sshbuf_len(m)) 62 error("write to helper failed"); 63 if ((r = sshbuf_consume(m, mlen)) != 0) 64 fatal_fr(r, "consume"); 65} 66 67static int 68recv_msg(struct sshbuf *m) 69{ 70 u_int l, len; 71 u_char c, buf[1024]; 72 int r; 73 74 if ((len = atomicio(read, fd, buf, 4)) != 4) { 75 error("read from helper failed: %u", len); 76 return (0); /* XXX */ 77 } 78 len = PEEK_U32(buf); 79 if (len > 256 * 1024) 80 fatal("response too long: %u", len); 81 /* read len bytes into m */ 82 sshbuf_reset(m); 83 while (len > 0) { 84 l = len; 85 if (l > sizeof(buf)) 86 l = sizeof(buf); 87 if (atomicio(read, fd, buf, l) != l) { 88 error("response from helper failed."); 89 return (0); /* XXX */ 90 } 91 if ((r = sshbuf_put(m, buf, l)) != 0) 92 fatal_fr(r, "sshbuf_put"); 93 len -= l; 94 } 95 if ((r = sshbuf_get_u8(m, &c)) != 0) 96 fatal_fr(r, "parse type"); 97 return c; 98} 99 100int 101pkcs11_init(int interactive) 102{ 103 return (0); 104} 105 106void 107pkcs11_terminate(void) 108{ 109 if (fd >= 0) 110 close(fd); 111} 112 113static int 114rsa_encrypt(int flen, const u_char *from, u_char *to, RSA *rsa, int padding) 115{ 116 struct sshkey *key = NULL; 117 struct sshbuf *msg = NULL; 118 u_char *blob = NULL, *signature = NULL; 119 size_t blen, slen = 0; 120 int r, ret = -1; 121 122 if (padding != RSA_PKCS1_PADDING) 123 goto fail; 124 key = sshkey_new(KEY_UNSPEC); 125 if (key == NULL) { 126 error_f("sshkey_new failed"); 127 goto fail; 128 } 129 key->type = KEY_RSA; 130 RSA_up_ref(rsa); 131 key->rsa = rsa; 132 if ((r = sshkey_to_blob(key, &blob, &blen)) != 0) { 133 error_fr(r, "encode key"); 134 goto fail; 135 } 136 if ((msg = sshbuf_new()) == NULL) 137 fatal_f("sshbuf_new failed"); 138 if ((r = sshbuf_put_u8(msg, SSH2_AGENTC_SIGN_REQUEST)) != 0 || 139 (r = sshbuf_put_string(msg, blob, blen)) != 0 || 140 (r = sshbuf_put_string(msg, from, flen)) != 0 || 141 (r = sshbuf_put_u32(msg, 0)) != 0) 142 fatal_fr(r, "compose"); 143 send_msg(msg); 144 sshbuf_reset(msg); 145 146 if (recv_msg(msg) == SSH2_AGENT_SIGN_RESPONSE) { 147 if ((r = sshbuf_get_string(msg, &signature, &slen)) != 0) 148 fatal_fr(r, "parse"); 149 if (slen <= (size_t)RSA_size(rsa)) { 150 memcpy(to, signature, slen); 151 ret = slen; 152 } 153 free(signature); 154 } 155 fail: 156 free(blob); 157 sshkey_free(key); 158 sshbuf_free(msg); 159 return (ret); 160} 161 162static ECDSA_SIG * 163ecdsa_do_sign(const unsigned char *dgst, int dgst_len, const BIGNUM *inv, 164 const BIGNUM *rp, EC_KEY *ec) 165{ 166 struct sshkey *key = NULL; 167 struct sshbuf *msg = NULL; 168 ECDSA_SIG *ret = NULL; 169 const u_char *cp; 170 u_char *blob = NULL, *signature = NULL; 171 size_t blen, slen = 0; 172 int r, nid; 173 174 nid = sshkey_ecdsa_key_to_nid(ec); 175 if (nid < 0) { 176 error_f("couldn't get curve nid"); 177 goto fail; 178 } 179 180 key = sshkey_new(KEY_UNSPEC); 181 if (key == NULL) { 182 error_f("sshkey_new failed"); 183 goto fail; 184 } 185 key->ecdsa = ec; 186 key->ecdsa_nid = nid; 187 key->type = KEY_ECDSA; 188 EC_KEY_up_ref(ec); 189 190 if ((r = sshkey_to_blob(key, &blob, &blen)) != 0) { 191 error_fr(r, "encode key"); 192 goto fail; 193 } 194 if ((msg = sshbuf_new()) == NULL) 195 fatal_f("sshbuf_new failed"); 196 if ((r = sshbuf_put_u8(msg, SSH2_AGENTC_SIGN_REQUEST)) != 0 || 197 (r = sshbuf_put_string(msg, blob, blen)) != 0 || 198 (r = sshbuf_put_string(msg, dgst, dgst_len)) != 0 || 199 (r = sshbuf_put_u32(msg, 0)) != 0) 200 fatal_fr(r, "compose"); 201 send_msg(msg); 202 sshbuf_reset(msg); 203 204 if (recv_msg(msg) == SSH2_AGENT_SIGN_RESPONSE) { 205 if ((r = sshbuf_get_string(msg, &signature, &slen)) != 0) 206 fatal_fr(r, "parse"); 207 cp = signature; 208 ret = d2i_ECDSA_SIG(NULL, &cp, slen); 209 free(signature); 210 } 211 212 fail: 213 free(blob); 214 sshkey_free(key); 215 sshbuf_free(msg); 216 return (ret); 217} 218 219static RSA_METHOD *helper_rsa; 220static EC_KEY_METHOD *helper_ecdsa; 221 222/* redirect private key crypto operations to the ssh-pkcs11-helper */ 223static void 224wrap_key(struct sshkey *k) 225{ 226 if (k->type == KEY_RSA) 227 RSA_set_method(k->rsa, helper_rsa); 228 else if (k->type == KEY_ECDSA) 229 EC_KEY_set_method(k->ecdsa, helper_ecdsa); 230 else 231 fatal_f("unknown key type"); 232} 233 234static int 235pkcs11_start_helper_methods(void) 236{ 237 if (helper_ecdsa != NULL) 238 return (0); 239 240 int (*orig_sign)(int, const unsigned char *, int, unsigned char *, 241 unsigned int *, const BIGNUM *, const BIGNUM *, EC_KEY *) = NULL; 242 if (helper_ecdsa != NULL) 243 return (0); 244 helper_ecdsa = EC_KEY_METHOD_new(EC_KEY_OpenSSL()); 245 if (helper_ecdsa == NULL) 246 return (-1); 247 EC_KEY_METHOD_get_sign(helper_ecdsa, &orig_sign, NULL, NULL); 248 EC_KEY_METHOD_set_sign(helper_ecdsa, orig_sign, NULL, ecdsa_do_sign); 249 250 if ((helper_rsa = RSA_meth_dup(RSA_get_default_method())) == NULL) 251 fatal_f("RSA_meth_dup failed"); 252 if (!RSA_meth_set1_name(helper_rsa, "ssh-pkcs11-helper") || 253 !RSA_meth_set_priv_enc(helper_rsa, rsa_encrypt)) 254 fatal_f("failed to prepare method"); 255 256 return (0); 257} 258 259static int 260pkcs11_start_helper(void) 261{ 262 int pair[2]; 263 const char *helper, *verbosity = NULL; 264 265 if (log_level_get() >= SYSLOG_LEVEL_DEBUG1) 266 verbosity = "-vvv"; 267 268 if (pkcs11_start_helper_methods() == -1) { 269 error("pkcs11_start_helper_methods failed"); 270 return (-1); 271 } 272 273 if (socketpair(AF_UNIX, SOCK_STREAM, 0, pair) == -1) { 274 error("socketpair: %s", strerror(errno)); 275 return (-1); 276 } 277 if ((pid = fork()) == -1) { 278 error("fork: %s", strerror(errno)); 279 return (-1); 280 } else if (pid == 0) { 281 if ((dup2(pair[1], STDIN_FILENO) == -1) || 282 (dup2(pair[1], STDOUT_FILENO) == -1)) { 283 fprintf(stderr, "dup2: %s\n", strerror(errno)); 284 _exit(1); 285 } 286 close(pair[0]); 287 close(pair[1]); 288 helper = getenv("SSH_PKCS11_HELPER"); 289 if (helper == NULL || strlen(helper) == 0) 290 helper = _PATH_SSH_PKCS11_HELPER; 291 debug_f("starting %s %s", helper, 292 verbosity == NULL ? "" : verbosity); 293 execlp(helper, helper, verbosity, (char *)NULL); 294 fprintf(stderr, "exec: %s: %s\n", helper, strerror(errno)); 295 _exit(1); 296 } 297 close(pair[1]); 298 fd = pair[0]; 299 return (0); 300} 301 302int 303pkcs11_add_provider(char *name, char *pin, struct sshkey ***keysp, 304 char ***labelsp) 305{ 306 struct sshkey *k; 307 int r, type; 308 u_char *blob; 309 char *label; 310 size_t blen; 311 u_int nkeys, i; 312 struct sshbuf *msg; 313 314 if (fd < 0 && pkcs11_start_helper() < 0) 315 return (-1); 316 317 if ((msg = sshbuf_new()) == NULL) 318 fatal_f("sshbuf_new failed"); 319 if ((r = sshbuf_put_u8(msg, SSH_AGENTC_ADD_SMARTCARD_KEY)) != 0 || 320 (r = sshbuf_put_cstring(msg, name)) != 0 || 321 (r = sshbuf_put_cstring(msg, pin)) != 0) 322 fatal_fr(r, "compose"); 323 send_msg(msg); 324 sshbuf_reset(msg); 325 326 type = recv_msg(msg); 327 if (type == SSH2_AGENT_IDENTITIES_ANSWER) { 328 if ((r = sshbuf_get_u32(msg, &nkeys)) != 0) 329 fatal_fr(r, "parse nkeys"); 330 *keysp = xcalloc(nkeys, sizeof(struct sshkey *)); 331 if (labelsp) 332 *labelsp = xcalloc(nkeys, sizeof(char *)); 333 for (i = 0; i < nkeys; i++) { 334 /* XXX clean up properly instead of fatal() */ 335 if ((r = sshbuf_get_string(msg, &blob, &blen)) != 0 || 336 (r = sshbuf_get_cstring(msg, &label, NULL)) != 0) 337 fatal_fr(r, "parse key"); 338 if ((r = sshkey_from_blob(blob, blen, &k)) != 0) 339 fatal_fr(r, "decode key"); 340 wrap_key(k); 341 (*keysp)[i] = k; 342 if (labelsp) 343 (*labelsp)[i] = label; 344 else 345 free(label); 346 free(blob); 347 } 348 } else if (type == SSH2_AGENT_FAILURE) { 349 if ((r = sshbuf_get_u32(msg, &nkeys)) != 0) 350 nkeys = -1; 351 } else { 352 nkeys = -1; 353 } 354 sshbuf_free(msg); 355 return (nkeys); 356} 357 358int 359pkcs11_del_provider(char *name) 360{ 361 int r, ret = -1; 362 struct sshbuf *msg; 363 364 if ((msg = sshbuf_new()) == NULL) 365 fatal_f("sshbuf_new failed"); 366 if ((r = sshbuf_put_u8(msg, SSH_AGENTC_REMOVE_SMARTCARD_KEY)) != 0 || 367 (r = sshbuf_put_cstring(msg, name)) != 0 || 368 (r = sshbuf_put_cstring(msg, "")) != 0) 369 fatal_fr(r, "compose"); 370 send_msg(msg); 371 sshbuf_reset(msg); 372 373 if (recv_msg(msg) == SSH_AGENT_SUCCESS) 374 ret = 0; 375 sshbuf_free(msg); 376 return (ret); 377} 378