blob: d376153fa7e4555e632b969da17208f1ed2f80b0 [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 Miller7ea845e2010-02-12 09:21:02 +110019#include <sys/types.h>
Damien Miller8ad0fbd2010-02-12 09:49:06 +110020#ifdef HAVE_SYS_TIME_H
21# include <sys/time.h>
22#endif
Damien Miller7ea845e2010-02-12 09:21:02 +110023#include <sys/socket.h>
24
25#include <stdarg.h>
26#include <string.h>
27#include <unistd.h>
28#include <errno.h>
29
30#include "pathnames.h"
31#include "xmalloc.h"
32#include "buffer.h"
33#include "log.h"
34#include "misc.h"
35#include "key.h"
36#include "authfd.h"
37#include "atomicio.h"
38#include "ssh-pkcs11.h"
39
40/* borrows code from sftp-server and ssh-agent */
41
42int fd = -1;
43pid_t pid = -1;
44
45static void
46send_msg(Buffer *m)
47{
48 u_char buf[4];
49 int mlen = buffer_len(m);
50
51 put_u32(buf, mlen);
52 if (atomicio(vwrite, fd, buf, 4) != 4 ||
53 atomicio(vwrite, fd, buffer_ptr(m),
54 buffer_len(m)) != buffer_len(m))
55 error("write to helper failed");
56 buffer_consume(m, mlen);
57}
58
59static int
60recv_msg(Buffer *m)
61{
62 u_int l, len;
63 u_char buf[1024];
64
65 if ((len = atomicio(read, fd, buf, 4)) != 4) {
66 error("read from helper failed: %u", len);
67 return (0); /* XXX */
68 }
69 len = get_u32(buf);
70 if (len > 256 * 1024)
71 fatal("response too long: %u", len);
72 /* read len bytes into m */
73 buffer_clear(m);
74 while (len > 0) {
75 l = len;
76 if (l > sizeof(buf))
77 l = sizeof(buf);
78 if (atomicio(read, fd, buf, l) != l) {
79 error("response from helper failed.");
80 return (0); /* XXX */
81 }
82 buffer_append(m, buf, l);
83 len -= l;
84 }
85 return (buffer_get_char(m));
86}
87
88int
89pkcs11_init(int interactive)
90{
91 return (0);
92}
93
94void
95pkcs11_terminate(void)
96{
97 close(fd);
98}
99
100static int
101pkcs11_rsa_private_encrypt(int flen, const u_char *from, u_char *to, RSA *rsa,
102 int padding)
103{
104 Key key;
105 u_char *blob, *signature = NULL;
106 u_int blen, slen = 0;
107 int ret = -1;
108 Buffer msg;
109
110 if (padding != RSA_PKCS1_PADDING)
111 return (-1);
112 key.type = KEY_RSA;
113 key.rsa = rsa;
114 if (key_to_blob(&key, &blob, &blen) == 0)
115 return -1;
116 buffer_init(&msg);
117 buffer_put_char(&msg, SSH2_AGENTC_SIGN_REQUEST);
118 buffer_put_string(&msg, blob, blen);
119 buffer_put_string(&msg, from, flen);
120 buffer_put_int(&msg, 0);
121 xfree(blob);
122 send_msg(&msg);
123
124 if (recv_msg(&msg) == SSH2_AGENT_SIGN_RESPONSE) {
125 signature = buffer_get_string(&msg, &slen);
126 if (slen <= (u_int)RSA_size(rsa)) {
127 memcpy(to, signature, slen);
128 ret = slen;
129 }
130 xfree(signature);
131 }
132 return (ret);
133}
134
135/* redirect the private key encrypt operation to the ssh-pkcs11-helper */
136static int
137wrap_key(RSA *rsa)
138{
139 static RSA_METHOD helper_rsa;
140
141 memcpy(&helper_rsa, RSA_get_default_method(), sizeof(helper_rsa));
142 helper_rsa.name = "ssh-pkcs11-helper";
143 helper_rsa.rsa_priv_enc = pkcs11_rsa_private_encrypt;
144 RSA_set_method(rsa, &helper_rsa);
145 return (0);
146}
147
148static int
149pkcs11_start_helper(void)
150{
151 int pair[2];
152
153 if (socketpair(AF_UNIX, SOCK_STREAM, 0, pair) == -1) {
154 error("socketpair: %s", strerror(errno));
155 return (-1);
156 }
157 if ((pid = fork()) == -1) {
158 error("fork: %s", strerror(errno));
159 return (-1);
160 } else if (pid == 0) {
161 if ((dup2(pair[1], STDIN_FILENO) == -1) ||
162 (dup2(pair[1], STDOUT_FILENO) == -1)) {
163 fprintf(stderr, "dup2: %s\n", strerror(errno));
164 _exit(1);
165 }
166 close(pair[0]);
167 close(pair[1]);
168 execlp(_PATH_SSH_PKCS11_HELPER, _PATH_SSH_PKCS11_HELPER,
169 (char *) 0);
170 fprintf(stderr, "exec: %s: %s\n", _PATH_SSH_PKCS11_HELPER,
171 strerror(errno));
172 _exit(1);
173 }
174 close(pair[1]);
175 fd = pair[0];
176 return (0);
177}
178
179int
180pkcs11_add_provider(char *name, char *pin, Key ***keysp)
181{
182 Key *k;
183 int i, nkeys;
184 u_char *blob;
185 u_int blen;
186 Buffer msg;
187
188 if (fd < 0 && pkcs11_start_helper() < 0)
189 return (-1);
190
191 buffer_init(&msg);
192 buffer_put_char(&msg, SSH_AGENTC_ADD_SMARTCARD_KEY);
193 buffer_put_cstring(&msg, name);
194 buffer_put_cstring(&msg, pin);
195 send_msg(&msg);
196 buffer_clear(&msg);
197
198 if (recv_msg(&msg) == SSH2_AGENT_IDENTITIES_ANSWER) {
199 nkeys = buffer_get_int(&msg);
200 *keysp = xcalloc(nkeys, sizeof(Key *));
201 for (i = 0; i < nkeys; i++) {
202 blob = buffer_get_string(&msg, &blen);
203 xfree(buffer_get_string(&msg, NULL));
204 k = key_from_blob(blob, blen);
205 wrap_key(k->rsa);
206 (*keysp)[i] = k;
207 xfree(blob);
208 }
209 } else {
210 nkeys = -1;
211 }
212 buffer_free(&msg);
213 return (nkeys);
214}
215
216int
217pkcs11_del_provider(char *name)
218{
219 int ret = -1;
220 Buffer msg;
221
222 buffer_init(&msg);
223 buffer_put_char(&msg, SSH_AGENTC_REMOVE_SMARTCARD_KEY);
224 buffer_put_cstring(&msg, name);
225 buffer_put_cstring(&msg, "");
226 send_msg(&msg);
227 buffer_clear(&msg);
228
229 if (recv_msg(&msg) == SSH_AGENT_SUCCESS)
230 ret = 0;
231 buffer_free(&msg);
232 return (ret);
233}