summaryrefslogtreecommitdiff
path: root/ssh-pkcs11-helper.c
diff options
context:
space:
mode:
authorDamien Miller <djm@mindrot.org>2010-02-12 09:21:02 +1100
committerDamien Miller <djm@mindrot.org>2010-02-12 09:21:02 +1100
commit7ea845e48df6d34a333ebbe79380cba0938d02a5 (patch)
tree44ab0d3fdfe0560b7ca92f5747e9dd5d012aea18 /ssh-pkcs11-helper.c
parent17751bcab25681d341442fdc2386a30a6bea345e (diff)
- markus@cvs.openbsd.org 2010/02/08 10:50:20
[pathnames.h readconf.c readconf.h scp.1 sftp.1 ssh-add.1 ssh-add.c] [ssh-agent.c ssh-keygen.1 ssh-keygen.c ssh.1 ssh.c ssh_config.5] replace our obsolete smartcard code with PKCS#11. ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-11/v2-20/pkcs-11v2-20.pdf ssh(1) and ssh-keygen(1) use dlopen(3) directly to talk to a PKCS#11 provider (shared library) while ssh-agent(1) delegates PKCS#11 to a forked a ssh-pkcs11-helper process. PKCS#11 is currently a compile time option. feedback and ok djm@; inspired by patches from Alon Bar-Lev `
Diffstat (limited to 'ssh-pkcs11-helper.c')
-rw-r--r--ssh-pkcs11-helper.c349
1 files changed, 349 insertions, 0 deletions
diff --git a/ssh-pkcs11-helper.c b/ssh-pkcs11-helper.c
new file mode 100644
index 000000000..f9962709b
--- /dev/null
+++ b/ssh-pkcs11-helper.c
@@ -0,0 +1,349 @@
1/*
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
17#include <sys/queue.h>
18#include <sys/types.h>
19#include <sys/time.h>
20
21#include <stdarg.h>
22#include <string.h>
23#include <unistd.h>
24#include <errno.h>
25
26#include "xmalloc.h"
27#include "buffer.h"
28#include "log.h"
29#include "misc.h"
30#include "key.h"
31#include "authfd.h"
32#include "ssh-pkcs11.h"
33
34/* borrows code from sftp-server and ssh-agent */
35
36struct pkcs11_keyinfo {
37 Key *key;
38 char *providername;
39 TAILQ_ENTRY(pkcs11_keyinfo) next;
40};
41
42TAILQ_HEAD(, pkcs11_keyinfo) pkcs11_keylist;
43
44#define MAX_MSG_LENGTH 10240 /*XXX*/
45
46/* helper */
47#define get_int() buffer_get_int(&iqueue);
48#define get_string(lenp) buffer_get_string(&iqueue, lenp);
49
50/* input and output queue */
51Buffer iqueue;
52Buffer oqueue;
53
54static void
55add_key(Key *k, char *name)
56{
57 struct pkcs11_keyinfo *ki;
58
59 ki = xcalloc(1, sizeof(*ki));
60 ki->providername = xstrdup(name);
61 ki->key = k;
62 TAILQ_INSERT_TAIL(&pkcs11_keylist, ki, next);
63}
64
65static void
66del_keys_by_name(char *name)
67{
68 struct pkcs11_keyinfo *ki, *nxt;
69
70 for (ki = TAILQ_FIRST(&pkcs11_keylist); ki; ki = nxt) {
71 nxt = TAILQ_NEXT(ki, next);
72 if (!strcmp(ki->providername, name)) {
73 TAILQ_REMOVE(&pkcs11_keylist, ki, next);
74 xfree(ki->providername);
75 key_free(ki->key);
76 free(ki);
77 }
78 }
79}
80
81/* lookup matching 'private' key */
82static Key *
83lookup_key(Key *k)
84{
85 struct pkcs11_keyinfo *ki;
86
87 TAILQ_FOREACH(ki, &pkcs11_keylist, next) {
88 debug("check %p %s", ki, ki->providername);
89 if (key_equal(k, ki->key))
90 return (ki->key);
91 }
92 return (NULL);
93}
94
95static void
96send_msg(Buffer *m)
97{
98 int mlen = buffer_len(m);
99
100 buffer_put_int(&oqueue, mlen);
101 buffer_append(&oqueue, buffer_ptr(m), mlen);
102 buffer_consume(m, mlen);
103}
104
105static void
106process_add(void)
107{
108 char *name, *pin;
109 Key **keys;
110 int i, nkeys;
111 u_char *blob;
112 u_int blen;
113 Buffer msg;
114
115 buffer_init(&msg);
116 name = get_string(NULL);
117 pin = get_string(NULL);
118 if ((nkeys = pkcs11_add_provider(name, pin, &keys)) > 0) {
119 buffer_put_char(&msg, SSH2_AGENT_IDENTITIES_ANSWER);
120 buffer_put_int(&msg, nkeys);
121 for (i = 0; i < nkeys; i++) {
122 key_to_blob(keys[i], &blob, &blen);
123 buffer_put_string(&msg, blob, blen);
124 buffer_put_cstring(&msg, name);
125 xfree(blob);
126 add_key(keys[i], name);
127 }
128 xfree(keys);
129 } else {
130 buffer_put_char(&msg, SSH_AGENT_FAILURE);
131 }
132 xfree(pin);
133 xfree(name);
134 send_msg(&msg);
135 buffer_free(&msg);
136}
137
138static void
139process_del(void)
140{
141 char *name, *pin;
142 Buffer msg;
143
144 buffer_init(&msg);
145 name = get_string(NULL);
146 pin = get_string(NULL);
147 del_keys_by_name(name);
148 if (pkcs11_del_provider(name) == 0)
149 buffer_put_char(&msg, SSH_AGENT_SUCCESS);
150 else
151 buffer_put_char(&msg, SSH_AGENT_FAILURE);
152 xfree(pin);
153 xfree(name);
154 send_msg(&msg);
155 buffer_free(&msg);
156}
157
158static void
159process_sign(void)
160{
161 u_char *blob, *data, *signature = NULL;
162 u_int blen, dlen, slen = 0;
163 int ok = -1, flags, ret;
164 Key *key, *found;
165 Buffer msg;
166
167 blob = get_string(&blen);
168 data = get_string(&dlen);
169 flags = get_int(); /* XXX ignore */
170
171 if ((key = key_from_blob(blob, blen)) != NULL) {
172 if ((found = lookup_key(key)) != NULL) {
173 slen = RSA_size(key->rsa);
174 signature = xmalloc(slen);
175 if ((ret = RSA_private_encrypt(dlen, data, signature,
176 found->rsa, RSA_PKCS1_PADDING)) != -1) {
177 slen = ret;
178 ok = 0;
179 }
180 }
181 key_free(key);
182 }
183 buffer_init(&msg);
184 if (ok == 0) {
185 buffer_put_char(&msg, SSH2_AGENT_SIGN_RESPONSE);
186 buffer_put_string(&msg, signature, slen);
187 } else {
188 buffer_put_char(&msg, SSH_AGENT_FAILURE);
189 }
190 xfree(data);
191 xfree(blob);
192 if (signature != NULL)
193 xfree(signature);
194 send_msg(&msg);
195 buffer_free(&msg);
196}
197
198static void
199process(void)
200{
201 u_int msg_len;
202 u_int buf_len;
203 u_int consumed;
204 u_int type;
205 u_char *cp;
206
207 buf_len = buffer_len(&iqueue);
208 if (buf_len < 5)
209 return; /* Incomplete message. */
210 cp = buffer_ptr(&iqueue);
211 msg_len = get_u32(cp);
212 if (msg_len > MAX_MSG_LENGTH) {
213 error("bad message len %d", msg_len);
214 cleanup_exit(11);
215 }
216 if (buf_len < msg_len + 4)
217 return;
218 buffer_consume(&iqueue, 4);
219 buf_len -= 4;
220 type = buffer_get_char(&iqueue);
221 switch (type) {
222 case SSH_AGENTC_ADD_SMARTCARD_KEY:
223 debug("process_add");
224 process_add();
225 break;
226 case SSH_AGENTC_REMOVE_SMARTCARD_KEY:
227 debug("process_del");
228 process_del();
229 break;
230 case SSH2_AGENTC_SIGN_REQUEST:
231 debug("process_sign");
232 process_sign();
233 break;
234 default:
235 error("Unknown message %d", type);
236 break;
237 }
238 /* discard the remaining bytes from the current packet */
239 if (buf_len < buffer_len(&iqueue)) {
240 error("iqueue grew unexpectedly");
241 cleanup_exit(255);
242 }
243 consumed = buf_len - buffer_len(&iqueue);
244 if (msg_len < consumed) {
245 error("msg_len %d < consumed %d", msg_len, consumed);
246 cleanup_exit(255);
247 }
248 if (msg_len > consumed)
249 buffer_consume(&iqueue, msg_len - consumed);
250}
251
252void
253cleanup_exit(int i)
254{
255 /* XXX */
256 _exit(i);
257}
258
259int
260main(int argc, char **argv)
261{
262 fd_set *rset, *wset;
263 int in, out, max, log_stderr = 0;
264 ssize_t len, olen, set_size;
265 SyslogFacility log_facility = SYSLOG_FACILITY_AUTH;
266 LogLevel log_level = SYSLOG_LEVEL_ERROR;
267 char buf[4*4096];
268
269 TAILQ_INIT(&pkcs11_keylist);
270 pkcs11_init(0);
271
272 extern char *optarg;
273 extern char *__progname;
274
275 log_init(__progname, log_level, log_facility, log_stderr);
276
277 in = STDIN_FILENO;
278 out = STDOUT_FILENO;
279
280 max = 0;
281 if (in > max)
282 max = in;
283 if (out > max)
284 max = out;
285
286 buffer_init(&iqueue);
287 buffer_init(&oqueue);
288
289 set_size = howmany(max + 1, NFDBITS) * sizeof(fd_mask);
290 rset = (fd_set *)xmalloc(set_size);
291 wset = (fd_set *)xmalloc(set_size);
292
293 for (;;) {
294 memset(rset, 0, set_size);
295 memset(wset, 0, set_size);
296
297 /*
298 * Ensure that we can read a full buffer and handle
299 * the worst-case length packet it can generate,
300 * otherwise apply backpressure by stopping reads.
301 */
302 if (buffer_check_alloc(&iqueue, sizeof(buf)) &&
303 buffer_check_alloc(&oqueue, MAX_MSG_LENGTH))
304 FD_SET(in, rset);
305
306 olen = buffer_len(&oqueue);
307 if (olen > 0)
308 FD_SET(out, wset);
309
310 if (select(max+1, rset, wset, NULL, NULL) < 0) {
311 if (errno == EINTR)
312 continue;
313 error("select: %s", strerror(errno));
314 cleanup_exit(2);
315 }
316
317 /* copy stdin to iqueue */
318 if (FD_ISSET(in, rset)) {
319 len = read(in, buf, sizeof buf);
320 if (len == 0) {
321 debug("read eof");
322 cleanup_exit(0);
323 } else if (len < 0) {
324 error("read: %s", strerror(errno));
325 cleanup_exit(1);
326 } else {
327 buffer_append(&iqueue, buf, len);
328 }
329 }
330 /* send oqueue to stdout */
331 if (FD_ISSET(out, wset)) {
332 len = write(out, buffer_ptr(&oqueue), olen);
333 if (len < 0) {
334 error("write: %s", strerror(errno));
335 cleanup_exit(1);
336 } else {
337 buffer_consume(&oqueue, len);
338 }
339 }
340
341 /*
342 * Process requests from client if we can fit the results
343 * into the output buffer, otherwise stop processing input
344 * and let the output queue drain.
345 */
346 if (buffer_check_alloc(&oqueue, MAX_MSG_LENGTH))
347 process();
348 }
349}