1*190cef3dSDag-Erling Smørgrav /* $OpenBSD: ssh-pkcs11-client.c,v 1.10 2018/07/09 21:59:10 markus Exp $ */ 2b15c8340SDag-Erling Smørgrav /* 3b15c8340SDag-Erling Smørgrav * Copyright (c) 2010 Markus Friedl. All rights reserved. 4b15c8340SDag-Erling Smørgrav * 5b15c8340SDag-Erling Smørgrav * Permission to use, copy, modify, and distribute this software for any 6b15c8340SDag-Erling Smørgrav * purpose with or without fee is hereby granted, provided that the above 7b15c8340SDag-Erling Smørgrav * copyright notice and this permission notice appear in all copies. 8b15c8340SDag-Erling Smørgrav * 9b15c8340SDag-Erling Smørgrav * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10b15c8340SDag-Erling Smørgrav * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11b15c8340SDag-Erling Smørgrav * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12b15c8340SDag-Erling Smørgrav * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13b15c8340SDag-Erling Smørgrav * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14b15c8340SDag-Erling Smørgrav * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15b15c8340SDag-Erling Smørgrav * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16b15c8340SDag-Erling Smørgrav */ 17b15c8340SDag-Erling Smørgrav 18b15c8340SDag-Erling Smørgrav #include "includes.h" 19b15c8340SDag-Erling Smørgrav 20b15c8340SDag-Erling Smørgrav #ifdef ENABLE_PKCS11 21b15c8340SDag-Erling Smørgrav 22b15c8340SDag-Erling Smørgrav #include <sys/types.h> 23b15c8340SDag-Erling Smørgrav #ifdef HAVE_SYS_TIME_H 24b15c8340SDag-Erling Smørgrav # include <sys/time.h> 25b15c8340SDag-Erling Smørgrav #endif 26b15c8340SDag-Erling Smørgrav #include <sys/socket.h> 27b15c8340SDag-Erling Smørgrav 28b15c8340SDag-Erling Smørgrav #include <stdarg.h> 29b15c8340SDag-Erling Smørgrav #include <string.h> 30b15c8340SDag-Erling Smørgrav #include <unistd.h> 31b15c8340SDag-Erling Smørgrav #include <errno.h> 32b15c8340SDag-Erling Smørgrav 33a0ee8cc6SDag-Erling Smørgrav #include <openssl/rsa.h> 34a0ee8cc6SDag-Erling Smørgrav 35b15c8340SDag-Erling Smørgrav #include "pathnames.h" 36b15c8340SDag-Erling Smørgrav #include "xmalloc.h" 37*190cef3dSDag-Erling Smørgrav #include "sshbuf.h" 38b15c8340SDag-Erling Smørgrav #include "log.h" 39b15c8340SDag-Erling Smørgrav #include "misc.h" 40*190cef3dSDag-Erling Smørgrav #include "sshkey.h" 41b15c8340SDag-Erling Smørgrav #include "authfd.h" 42b15c8340SDag-Erling Smørgrav #include "atomicio.h" 43b15c8340SDag-Erling Smørgrav #include "ssh-pkcs11.h" 44*190cef3dSDag-Erling Smørgrav #include "ssherr.h" 45b15c8340SDag-Erling Smørgrav 46b15c8340SDag-Erling Smørgrav /* borrows code from sftp-server and ssh-agent */ 47b15c8340SDag-Erling Smørgrav 48b15c8340SDag-Erling Smørgrav int fd = -1; 49b15c8340SDag-Erling Smørgrav pid_t pid = -1; 50b15c8340SDag-Erling Smørgrav 51b15c8340SDag-Erling Smørgrav static void 52*190cef3dSDag-Erling Smørgrav send_msg(struct sshbuf *m) 53b15c8340SDag-Erling Smørgrav { 54b15c8340SDag-Erling Smørgrav u_char buf[4]; 55*190cef3dSDag-Erling Smørgrav size_t mlen = sshbuf_len(m); 56*190cef3dSDag-Erling Smørgrav int r; 57b15c8340SDag-Erling Smørgrav 58*190cef3dSDag-Erling Smørgrav POKE_U32(buf, mlen); 59b15c8340SDag-Erling Smørgrav if (atomicio(vwrite, fd, buf, 4) != 4 || 60*190cef3dSDag-Erling Smørgrav atomicio(vwrite, fd, sshbuf_mutable_ptr(m), 61*190cef3dSDag-Erling Smørgrav sshbuf_len(m)) != sshbuf_len(m)) 62b15c8340SDag-Erling Smørgrav error("write to helper failed"); 63*190cef3dSDag-Erling Smørgrav if ((r = sshbuf_consume(m, mlen)) != 0) 64*190cef3dSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 65b15c8340SDag-Erling Smørgrav } 66b15c8340SDag-Erling Smørgrav 67b15c8340SDag-Erling Smørgrav static int 68*190cef3dSDag-Erling Smørgrav recv_msg(struct sshbuf *m) 69b15c8340SDag-Erling Smørgrav { 70b15c8340SDag-Erling Smørgrav u_int l, len; 71*190cef3dSDag-Erling Smørgrav u_char c, buf[1024]; 72*190cef3dSDag-Erling Smørgrav int r; 73b15c8340SDag-Erling Smørgrav 74b15c8340SDag-Erling Smørgrav if ((len = atomicio(read, fd, buf, 4)) != 4) { 75b15c8340SDag-Erling Smørgrav error("read from helper failed: %u", len); 76b15c8340SDag-Erling Smørgrav return (0); /* XXX */ 77b15c8340SDag-Erling Smørgrav } 78*190cef3dSDag-Erling Smørgrav len = PEEK_U32(buf); 79b15c8340SDag-Erling Smørgrav if (len > 256 * 1024) 80b15c8340SDag-Erling Smørgrav fatal("response too long: %u", len); 81b15c8340SDag-Erling Smørgrav /* read len bytes into m */ 82*190cef3dSDag-Erling Smørgrav sshbuf_reset(m); 83b15c8340SDag-Erling Smørgrav while (len > 0) { 84b15c8340SDag-Erling Smørgrav l = len; 85b15c8340SDag-Erling Smørgrav if (l > sizeof(buf)) 86b15c8340SDag-Erling Smørgrav l = sizeof(buf); 87b15c8340SDag-Erling Smørgrav if (atomicio(read, fd, buf, l) != l) { 88b15c8340SDag-Erling Smørgrav error("response from helper failed."); 89b15c8340SDag-Erling Smørgrav return (0); /* XXX */ 90b15c8340SDag-Erling Smørgrav } 91*190cef3dSDag-Erling Smørgrav if ((r = sshbuf_put(m, buf, l)) != 0) 92*190cef3dSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 93b15c8340SDag-Erling Smørgrav len -= l; 94b15c8340SDag-Erling Smørgrav } 95*190cef3dSDag-Erling Smørgrav if ((r = sshbuf_get_u8(m, &c)) != 0) 96*190cef3dSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 97*190cef3dSDag-Erling Smørgrav return c; 98b15c8340SDag-Erling Smørgrav } 99b15c8340SDag-Erling Smørgrav 100b15c8340SDag-Erling Smørgrav int 101b15c8340SDag-Erling Smørgrav pkcs11_init(int interactive) 102b15c8340SDag-Erling Smørgrav { 103b15c8340SDag-Erling Smørgrav return (0); 104b15c8340SDag-Erling Smørgrav } 105b15c8340SDag-Erling Smørgrav 106b15c8340SDag-Erling Smørgrav void 107b15c8340SDag-Erling Smørgrav pkcs11_terminate(void) 108b15c8340SDag-Erling Smørgrav { 10947dd1d1bSDag-Erling Smørgrav if (fd >= 0) 110b15c8340SDag-Erling Smørgrav close(fd); 111b15c8340SDag-Erling Smørgrav } 112b15c8340SDag-Erling Smørgrav 113b15c8340SDag-Erling Smørgrav static int 114b15c8340SDag-Erling Smørgrav pkcs11_rsa_private_encrypt(int flen, const u_char *from, u_char *to, RSA *rsa, 115b15c8340SDag-Erling Smørgrav int padding) 116b15c8340SDag-Erling Smørgrav { 1174f52dfbbSDag-Erling Smørgrav struct sshkey key; /* XXX */ 118b15c8340SDag-Erling Smørgrav u_char *blob, *signature = NULL; 119*190cef3dSDag-Erling Smørgrav size_t blen, slen = 0; 120*190cef3dSDag-Erling Smørgrav int r, ret = -1; 121*190cef3dSDag-Erling Smørgrav struct sshbuf *msg; 122b15c8340SDag-Erling Smørgrav 123b15c8340SDag-Erling Smørgrav if (padding != RSA_PKCS1_PADDING) 124b15c8340SDag-Erling Smørgrav return (-1); 125b15c8340SDag-Erling Smørgrav key.type = KEY_RSA; 126b15c8340SDag-Erling Smørgrav key.rsa = rsa; 127*190cef3dSDag-Erling Smørgrav if ((r = sshkey_to_blob(&key, &blob, &blen)) != 0) { 128*190cef3dSDag-Erling Smørgrav error("%s: sshkey_to_blob: %s", __func__, ssh_err(r)); 129b15c8340SDag-Erling Smørgrav return -1; 130*190cef3dSDag-Erling Smørgrav } 131*190cef3dSDag-Erling Smørgrav if ((msg = sshbuf_new()) == NULL) 132*190cef3dSDag-Erling Smørgrav fatal("%s: sshbuf_new failed", __func__); 133*190cef3dSDag-Erling Smørgrav if ((r = sshbuf_put_u8(msg, SSH2_AGENTC_SIGN_REQUEST)) != 0 || 134*190cef3dSDag-Erling Smørgrav (r = sshbuf_put_string(msg, blob, blen)) != 0 || 135*190cef3dSDag-Erling Smørgrav (r = sshbuf_put_string(msg, from, flen)) != 0 || 136*190cef3dSDag-Erling Smørgrav (r = sshbuf_put_u32(msg, 0)) != 0) 137*190cef3dSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 138e4a9863fSDag-Erling Smørgrav free(blob); 139*190cef3dSDag-Erling Smørgrav send_msg(msg); 140*190cef3dSDag-Erling Smørgrav sshbuf_reset(msg); 141b15c8340SDag-Erling Smørgrav 142*190cef3dSDag-Erling Smørgrav if (recv_msg(msg) == SSH2_AGENT_SIGN_RESPONSE) { 143*190cef3dSDag-Erling Smørgrav if ((r = sshbuf_get_string(msg, &signature, &slen)) != 0) 144*190cef3dSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 145*190cef3dSDag-Erling Smørgrav if (slen <= (size_t)RSA_size(rsa)) { 146b15c8340SDag-Erling Smørgrav memcpy(to, signature, slen); 147b15c8340SDag-Erling Smørgrav ret = slen; 148b15c8340SDag-Erling Smørgrav } 149e4a9863fSDag-Erling Smørgrav free(signature); 150b15c8340SDag-Erling Smørgrav } 151*190cef3dSDag-Erling Smørgrav sshbuf_free(msg); 152b15c8340SDag-Erling Smørgrav return (ret); 153b15c8340SDag-Erling Smørgrav } 154b15c8340SDag-Erling Smørgrav 155b15c8340SDag-Erling Smørgrav /* redirect the private key encrypt operation to the ssh-pkcs11-helper */ 156b15c8340SDag-Erling Smørgrav static int 157b15c8340SDag-Erling Smørgrav wrap_key(RSA *rsa) 158b15c8340SDag-Erling Smørgrav { 159b15c8340SDag-Erling Smørgrav static RSA_METHOD helper_rsa; 160b15c8340SDag-Erling Smørgrav 161b15c8340SDag-Erling Smørgrav memcpy(&helper_rsa, RSA_get_default_method(), sizeof(helper_rsa)); 162b15c8340SDag-Erling Smørgrav helper_rsa.name = "ssh-pkcs11-helper"; 163b15c8340SDag-Erling Smørgrav helper_rsa.rsa_priv_enc = pkcs11_rsa_private_encrypt; 164b15c8340SDag-Erling Smørgrav RSA_set_method(rsa, &helper_rsa); 165b15c8340SDag-Erling Smørgrav return (0); 166b15c8340SDag-Erling Smørgrav } 167b15c8340SDag-Erling Smørgrav 168b15c8340SDag-Erling Smørgrav static int 169b15c8340SDag-Erling Smørgrav pkcs11_start_helper(void) 170b15c8340SDag-Erling Smørgrav { 171b15c8340SDag-Erling Smørgrav int pair[2]; 172b15c8340SDag-Erling Smørgrav 173b15c8340SDag-Erling Smørgrav if (socketpair(AF_UNIX, SOCK_STREAM, 0, pair) == -1) { 174b15c8340SDag-Erling Smørgrav error("socketpair: %s", strerror(errno)); 175b15c8340SDag-Erling Smørgrav return (-1); 176b15c8340SDag-Erling Smørgrav } 177b15c8340SDag-Erling Smørgrav if ((pid = fork()) == -1) { 178b15c8340SDag-Erling Smørgrav error("fork: %s", strerror(errno)); 179b15c8340SDag-Erling Smørgrav return (-1); 180b15c8340SDag-Erling Smørgrav } else if (pid == 0) { 181b15c8340SDag-Erling Smørgrav if ((dup2(pair[1], STDIN_FILENO) == -1) || 182b15c8340SDag-Erling Smørgrav (dup2(pair[1], STDOUT_FILENO) == -1)) { 183b15c8340SDag-Erling Smørgrav fprintf(stderr, "dup2: %s\n", strerror(errno)); 184b15c8340SDag-Erling Smørgrav _exit(1); 185b15c8340SDag-Erling Smørgrav } 186b15c8340SDag-Erling Smørgrav close(pair[0]); 187b15c8340SDag-Erling Smørgrav close(pair[1]); 188b15c8340SDag-Erling Smørgrav execlp(_PATH_SSH_PKCS11_HELPER, _PATH_SSH_PKCS11_HELPER, 189acc1a9efSDag-Erling Smørgrav (char *)NULL); 190b15c8340SDag-Erling Smørgrav fprintf(stderr, "exec: %s: %s\n", _PATH_SSH_PKCS11_HELPER, 191b15c8340SDag-Erling Smørgrav strerror(errno)); 192b15c8340SDag-Erling Smørgrav _exit(1); 193b15c8340SDag-Erling Smørgrav } 194b15c8340SDag-Erling Smørgrav close(pair[1]); 195b15c8340SDag-Erling Smørgrav fd = pair[0]; 196b15c8340SDag-Erling Smørgrav return (0); 197b15c8340SDag-Erling Smørgrav } 198b15c8340SDag-Erling Smørgrav 199b15c8340SDag-Erling Smørgrav int 200*190cef3dSDag-Erling Smørgrav pkcs11_add_provider(char *name, char *pin, struct sshkey ***keysp) 201b15c8340SDag-Erling Smørgrav { 2024f52dfbbSDag-Erling Smørgrav struct sshkey *k; 203*190cef3dSDag-Erling Smørgrav int r; 204b15c8340SDag-Erling Smørgrav u_char *blob; 205*190cef3dSDag-Erling Smørgrav size_t blen; 206*190cef3dSDag-Erling Smørgrav u_int nkeys, i; 207*190cef3dSDag-Erling Smørgrav struct sshbuf *msg; 208b15c8340SDag-Erling Smørgrav 209b15c8340SDag-Erling Smørgrav if (fd < 0 && pkcs11_start_helper() < 0) 210b15c8340SDag-Erling Smørgrav return (-1); 211b15c8340SDag-Erling Smørgrav 212*190cef3dSDag-Erling Smørgrav if ((msg = sshbuf_new()) == NULL) 213*190cef3dSDag-Erling Smørgrav fatal("%s: sshbuf_new failed", __func__); 214*190cef3dSDag-Erling Smørgrav if ((r = sshbuf_put_u8(msg, SSH_AGENTC_ADD_SMARTCARD_KEY)) != 0 || 215*190cef3dSDag-Erling Smørgrav (r = sshbuf_put_cstring(msg, name)) != 0 || 216*190cef3dSDag-Erling Smørgrav (r = sshbuf_put_cstring(msg, pin)) != 0) 217*190cef3dSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 218*190cef3dSDag-Erling Smørgrav send_msg(msg); 219*190cef3dSDag-Erling Smørgrav sshbuf_reset(msg); 220b15c8340SDag-Erling Smørgrav 221*190cef3dSDag-Erling Smørgrav if (recv_msg(msg) == SSH2_AGENT_IDENTITIES_ANSWER) { 222*190cef3dSDag-Erling Smørgrav if ((r = sshbuf_get_u32(msg, &nkeys)) != 0) 223*190cef3dSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 224*190cef3dSDag-Erling Smørgrav *keysp = xcalloc(nkeys, sizeof(struct sshkey *)); 225b15c8340SDag-Erling Smørgrav for (i = 0; i < nkeys; i++) { 226*190cef3dSDag-Erling Smørgrav /* XXX clean up properly instead of fatal() */ 227*190cef3dSDag-Erling Smørgrav if ((r = sshbuf_get_string(msg, &blob, &blen)) != 0 || 228*190cef3dSDag-Erling Smørgrav (r = sshbuf_skip_string(msg)) != 0) 229*190cef3dSDag-Erling Smørgrav fatal("%s: buffer error: %s", 230*190cef3dSDag-Erling Smørgrav __func__, ssh_err(r)); 231*190cef3dSDag-Erling Smørgrav if ((r = sshkey_from_blob(blob, blen, &k)) != 0) 232*190cef3dSDag-Erling Smørgrav fatal("%s: bad key: %s", __func__, ssh_err(r)); 233b15c8340SDag-Erling Smørgrav wrap_key(k->rsa); 234b15c8340SDag-Erling Smørgrav (*keysp)[i] = k; 235e4a9863fSDag-Erling Smørgrav free(blob); 236b15c8340SDag-Erling Smørgrav } 237b15c8340SDag-Erling Smørgrav } else { 238b15c8340SDag-Erling Smørgrav nkeys = -1; 239b15c8340SDag-Erling Smørgrav } 240*190cef3dSDag-Erling Smørgrav sshbuf_free(msg); 241b15c8340SDag-Erling Smørgrav return (nkeys); 242b15c8340SDag-Erling Smørgrav } 243b15c8340SDag-Erling Smørgrav 244b15c8340SDag-Erling Smørgrav int 245b15c8340SDag-Erling Smørgrav pkcs11_del_provider(char *name) 246b15c8340SDag-Erling Smørgrav { 247*190cef3dSDag-Erling Smørgrav int r, ret = -1; 248*190cef3dSDag-Erling Smørgrav struct sshbuf *msg; 249b15c8340SDag-Erling Smørgrav 250*190cef3dSDag-Erling Smørgrav if ((msg = sshbuf_new()) == NULL) 251*190cef3dSDag-Erling Smørgrav fatal("%s: sshbuf_new failed", __func__); 252*190cef3dSDag-Erling Smørgrav if ((r = sshbuf_put_u8(msg, SSH_AGENTC_REMOVE_SMARTCARD_KEY)) != 0 || 253*190cef3dSDag-Erling Smørgrav (r = sshbuf_put_cstring(msg, name)) != 0 || 254*190cef3dSDag-Erling Smørgrav (r = sshbuf_put_cstring(msg, "")) != 0) 255*190cef3dSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 256*190cef3dSDag-Erling Smørgrav send_msg(msg); 257*190cef3dSDag-Erling Smørgrav sshbuf_reset(msg); 258b15c8340SDag-Erling Smørgrav 259*190cef3dSDag-Erling Smørgrav if (recv_msg(msg) == SSH_AGENT_SUCCESS) 260b15c8340SDag-Erling Smørgrav ret = 0; 261*190cef3dSDag-Erling Smørgrav sshbuf_free(msg); 262b15c8340SDag-Erling Smørgrav return (ret); 263b15c8340SDag-Erling Smørgrav } 264b15c8340SDag-Erling Smørgrav 265b15c8340SDag-Erling Smørgrav #endif /* ENABLE_PKCS11 */ 266