blob: 37050e04df7c9d541788d097dd06c839f42d1a67 [file] [log] [blame]
Damien Miller7ea845e2010-02-12 09:21:02 +11001/*
2 * Copyright (c) 2010 Markus Friedl. All rights reserved.
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
Damien Miller8ad0fbd2010-02-12 09:49:06 +110017#include "includes.h"
18
Damien Millerdfa41562010-02-12 10:06:28 +110019#ifdef ENABLE_PKCS11
20
Damien Miller7ea845e2010-02-12 09:21:02 +110021#include <sys/types.h>
Damien Miller8ad0fbd2010-02-12 09:49:06 +110022#ifdef HAVE_SYS_TIME_H
23# include <sys/time.h>
24#endif
Damien Miller7ea845e2010-02-12 09:21:02 +110025#include <sys/socket.h>
26
27#include <stdarg.h>
28#include <string.h>
29#include <unistd.h>
30#include <errno.h>
31
32#include "pathnames.h"
33#include "xmalloc.h"
34#include "buffer.h"
35#include "log.h"
36#include "misc.h"
37#include "key.h"
38#include "authfd.h"
39#include "atomicio.h"
40#include "ssh-pkcs11.h"
41
42/* borrows code from sftp-server and ssh-agent */
43
44int fd = -1;
45pid_t pid = -1;
46
47static void
48send_msg(Buffer *m)
49{
50 u_char buf[4];
51 int mlen = buffer_len(m);
52
53 put_u32(buf, mlen);
54 if (atomicio(vwrite, fd, buf, 4) != 4 ||
55 atomicio(vwrite, fd, buffer_ptr(m),
56 buffer_len(m)) != buffer_len(m))
57 error("write to helper failed");
58 buffer_consume(m, mlen);
59}
60
61static int
62recv_msg(Buffer *m)
63{
64 u_int l, len;
65 u_char buf[1024];
66
67 if ((len = atomicio(read, fd, buf, 4)) != 4) {
68 error("read from helper failed: %u", len);
69 return (0); /* XXX */
70 }
71 len = get_u32(buf);
72 if (len > 256 * 1024)
73 fatal("response too long: %u", len);
74 /* read len bytes into m */
75 buffer_clear(m);
76 while (len > 0) {
77 l = len;
78 if (l > sizeof(buf))
79 l = sizeof(buf);
80 if (atomicio(read, fd, buf, l) != l) {
81 error("response from helper failed.");
82 return (0); /* XXX */
83 }
84 buffer_append(m, buf, l);
85 len -= l;
86 }
87 return (buffer_get_char(m));
88}
89
90int
91pkcs11_init(int interactive)
92{
93 return (0);
94}
95
96void
97pkcs11_terminate(void)
98{
99 close(fd);
100}
101
102static int
103pkcs11_rsa_private_encrypt(int flen, const u_char *from, u_char *to, RSA *rsa,
104 int padding)
105{
106 Key key;
107 u_char *blob, *signature = NULL;
108 u_int blen, slen = 0;
109 int ret = -1;
110 Buffer msg;
111
112 if (padding != RSA_PKCS1_PADDING)
113 return (-1);
114 key.type = KEY_RSA;
115 key.rsa = rsa;
116 if (key_to_blob(&key, &blob, &blen) == 0)
117 return -1;
118 buffer_init(&msg);
119 buffer_put_char(&msg, SSH2_AGENTC_SIGN_REQUEST);
120 buffer_put_string(&msg, blob, blen);
121 buffer_put_string(&msg, from, flen);
122 buffer_put_int(&msg, 0);
123 xfree(blob);
124 send_msg(&msg);
125
126 if (recv_msg(&msg) == SSH2_AGENT_SIGN_RESPONSE) {
127 signature = buffer_get_string(&msg, &slen);
128 if (slen <= (u_int)RSA_size(rsa)) {
129 memcpy(to, signature, slen);
130 ret = slen;
131 }
132 xfree(signature);
133 }
134 return (ret);
135}
136
137/* redirect the private key encrypt operation to the ssh-pkcs11-helper */
138static int
139wrap_key(RSA *rsa)
140{
141 static RSA_METHOD helper_rsa;
142
143 memcpy(&helper_rsa, RSA_get_default_method(), sizeof(helper_rsa));
144 helper_rsa.name = "ssh-pkcs11-helper";
145 helper_rsa.rsa_priv_enc = pkcs11_rsa_private_encrypt;
146 RSA_set_method(rsa, &helper_rsa);
147 return (0);
148}
149
150static int
151pkcs11_start_helper(void)
152{
153 int pair[2];
154
155 if (socketpair(AF_UNIX, SOCK_STREAM, 0, pair) == -1) {
156 error("socketpair: %s", strerror(errno));
157 return (-1);
158 }
159 if ((pid = fork()) == -1) {
160 error("fork: %s", strerror(errno));
161 return (-1);
162 } else if (pid == 0) {
163 if ((dup2(pair[1], STDIN_FILENO) == -1) ||
164 (dup2(pair[1], STDOUT_FILENO) == -1)) {
165 fprintf(stderr, "dup2: %s\n", strerror(errno));
166 _exit(1);
167 }
168 close(pair[0]);
169 close(pair[1]);
170 execlp(_PATH_SSH_PKCS11_HELPER, _PATH_SSH_PKCS11_HELPER,
171 (char *) 0);
172 fprintf(stderr, "exec: %s: %s\n", _PATH_SSH_PKCS11_HELPER,
173 strerror(errno));
174 _exit(1);
175 }
176 close(pair[1]);
177 fd = pair[0];
178 return (0);
179}
180
181int
182pkcs11_add_provider(char *name, char *pin, Key ***keysp)
183{
184 Key *k;
185 int i, nkeys;
186 u_char *blob;
187 u_int blen;
188 Buffer msg;
189
190 if (fd < 0 && pkcs11_start_helper() < 0)
191 return (-1);
192
193 buffer_init(&msg);
194 buffer_put_char(&msg, SSH_AGENTC_ADD_SMARTCARD_KEY);
195 buffer_put_cstring(&msg, name);
196 buffer_put_cstring(&msg, pin);
197 send_msg(&msg);
198 buffer_clear(&msg);
199
200 if (recv_msg(&msg) == SSH2_AGENT_IDENTITIES_ANSWER) {
201 nkeys = buffer_get_int(&msg);
202 *keysp = xcalloc(nkeys, sizeof(Key *));
203 for (i = 0; i < nkeys; i++) {
204 blob = buffer_get_string(&msg, &blen);
205 xfree(buffer_get_string(&msg, NULL));
206 k = key_from_blob(blob, blen);
207 wrap_key(k->rsa);
208 (*keysp)[i] = k;
209 xfree(blob);
210 }
211 } else {
212 nkeys = -1;
213 }
214 buffer_free(&msg);
215 return (nkeys);
216}
217
218int
219pkcs11_del_provider(char *name)
220{
221 int ret = -1;
222 Buffer msg;
223
224 buffer_init(&msg);
225 buffer_put_char(&msg, SSH_AGENTC_REMOVE_SMARTCARD_KEY);
226 buffer_put_cstring(&msg, name);
227 buffer_put_cstring(&msg, "");
228 send_msg(&msg);
229 buffer_clear(&msg);
230
231 if (recv_msg(&msg) == SSH_AGENT_SUCCESS)
232 ret = 0;
233 buffer_free(&msg);
234 return (ret);
235}
Damien Millerdfa41562010-02-12 10:06:28 +1100236
237#endif /* ENABLE_PKCS11 */