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