Annotation of src/usr.bin/ssh/ssh-pkcs11-helper.c, Revision 1.9
1.9 ! djm 1: /* $OpenBSD: ssh-pkcs11-helper.c,v 1.8 2014/06/24 01:13:21 djm Exp $ */
1.1 markus 2: /*
3: * Copyright (c) 2010 Markus Friedl. All rights reserved.
4: *
5: * Permission to use, copy, modify, and distribute this software for any
6: * purpose with or without fee is hereby granted, provided that the above
7: * copyright notice and this permission notice appear in all copies.
8: *
9: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13: * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14: * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15: * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16: */
17:
1.9 ! djm 18: #include <sys/types.h>
! 19: #include <sys/param.h>
1.1 markus 20: #include <sys/queue.h>
21: #include <sys/time.h>
22:
23: #include <stdarg.h>
24: #include <string.h>
25: #include <unistd.h>
26: #include <errno.h>
27:
28: #include "xmalloc.h"
29: #include "buffer.h"
30: #include "log.h"
31: #include "misc.h"
32: #include "key.h"
33: #include "authfd.h"
34: #include "ssh-pkcs11.h"
35:
36: /* borrows code from sftp-server and ssh-agent */
37:
38: struct pkcs11_keyinfo {
39: Key *key;
40: char *providername;
41: TAILQ_ENTRY(pkcs11_keyinfo) next;
42: };
43:
44: TAILQ_HEAD(, pkcs11_keyinfo) pkcs11_keylist;
45:
46: #define MAX_MSG_LENGTH 10240 /*XXX*/
47:
48: /* helper */
49: #define get_int() buffer_get_int(&iqueue);
50: #define get_string(lenp) buffer_get_string(&iqueue, lenp);
51:
52: /* input and output queue */
53: Buffer iqueue;
54: Buffer oqueue;
55:
56: static void
57: add_key(Key *k, char *name)
58: {
59: struct pkcs11_keyinfo *ki;
60:
61: ki = xcalloc(1, sizeof(*ki));
62: ki->providername = xstrdup(name);
63: ki->key = k;
64: TAILQ_INSERT_TAIL(&pkcs11_keylist, ki, next);
65: }
66:
67: static void
68: del_keys_by_name(char *name)
69: {
70: struct pkcs11_keyinfo *ki, *nxt;
71:
72: for (ki = TAILQ_FIRST(&pkcs11_keylist); ki; ki = nxt) {
73: nxt = TAILQ_NEXT(ki, next);
74: if (!strcmp(ki->providername, name)) {
75: TAILQ_REMOVE(&pkcs11_keylist, ki, next);
1.6 djm 76: free(ki->providername);
1.1 markus 77: key_free(ki->key);
78: free(ki);
79: }
80: }
81: }
82:
83: /* lookup matching 'private' key */
84: static Key *
85: lookup_key(Key *k)
86: {
87: struct pkcs11_keyinfo *ki;
88:
89: TAILQ_FOREACH(ki, &pkcs11_keylist, next) {
90: debug("check %p %s", ki, ki->providername);
91: if (key_equal(k, ki->key))
92: return (ki->key);
93: }
94: return (NULL);
95: }
96:
97: static void
98: send_msg(Buffer *m)
99: {
100: int mlen = buffer_len(m);
101:
102: buffer_put_int(&oqueue, mlen);
103: buffer_append(&oqueue, buffer_ptr(m), mlen);
104: buffer_consume(m, mlen);
105: }
106:
107: static void
108: process_add(void)
109: {
110: char *name, *pin;
111: Key **keys;
112: int i, nkeys;
113: u_char *blob;
114: u_int blen;
115: Buffer msg;
116:
117: buffer_init(&msg);
118: name = get_string(NULL);
119: pin = get_string(NULL);
120: if ((nkeys = pkcs11_add_provider(name, pin, &keys)) > 0) {
121: buffer_put_char(&msg, SSH2_AGENT_IDENTITIES_ANSWER);
122: buffer_put_int(&msg, nkeys);
123: for (i = 0; i < nkeys; i++) {
1.7 djm 124: if (key_to_blob(keys[i], &blob, &blen) == 0)
125: continue;
1.1 markus 126: buffer_put_string(&msg, blob, blen);
127: buffer_put_cstring(&msg, name);
1.6 djm 128: free(blob);
1.1 markus 129: add_key(keys[i], name);
130: }
1.6 djm 131: free(keys);
1.1 markus 132: } else {
133: buffer_put_char(&msg, SSH_AGENT_FAILURE);
134: }
1.6 djm 135: free(pin);
136: free(name);
1.1 markus 137: send_msg(&msg);
138: buffer_free(&msg);
139: }
140:
141: static void
142: process_del(void)
143: {
144: char *name, *pin;
145: Buffer msg;
146:
147: buffer_init(&msg);
148: name = get_string(NULL);
149: pin = get_string(NULL);
150: del_keys_by_name(name);
151: if (pkcs11_del_provider(name) == 0)
152: buffer_put_char(&msg, SSH_AGENT_SUCCESS);
153: else
154: buffer_put_char(&msg, SSH_AGENT_FAILURE);
1.6 djm 155: free(pin);
156: free(name);
1.1 markus 157: send_msg(&msg);
158: buffer_free(&msg);
159: }
160:
161: static void
162: process_sign(void)
163: {
164: u_char *blob, *data, *signature = NULL;
165: u_int blen, dlen, slen = 0;
1.8 djm 166: int ok = -1;
1.1 markus 167: Key *key, *found;
168: Buffer msg;
169:
170: blob = get_string(&blen);
171: data = get_string(&dlen);
1.4 dtucker 172: (void)get_int(); /* XXX ignore flags */
1.1 markus 173:
174: if ((key = key_from_blob(blob, blen)) != NULL) {
175: if ((found = lookup_key(key)) != NULL) {
1.8 djm 176: #ifdef WITH_OPENSSL
177: int ret;
178:
1.1 markus 179: slen = RSA_size(key->rsa);
180: signature = xmalloc(slen);
181: if ((ret = RSA_private_encrypt(dlen, data, signature,
182: found->rsa, RSA_PKCS1_PADDING)) != -1) {
183: slen = ret;
184: ok = 0;
185: }
1.8 djm 186: #endif /* WITH_OPENSSL */
1.1 markus 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: }
1.6 djm 197: free(data);
198: free(blob);
199: free(signature);
1.1 markus 200: send_msg(&msg);
201: buffer_free(&msg);
202: }
203:
204: static void
205: process(void)
206: {
207: u_int msg_len;
208: u_int buf_len;
209: u_int consumed;
210: u_int type;
211: u_char *cp;
212:
213: buf_len = buffer_len(&iqueue);
214: if (buf_len < 5)
215: return; /* Incomplete message. */
216: cp = buffer_ptr(&iqueue);
217: msg_len = get_u32(cp);
218: if (msg_len > MAX_MSG_LENGTH) {
219: error("bad message len %d", msg_len);
220: cleanup_exit(11);
221: }
222: if (buf_len < msg_len + 4)
223: return;
224: buffer_consume(&iqueue, 4);
225: buf_len -= 4;
226: type = buffer_get_char(&iqueue);
227: switch (type) {
228: case SSH_AGENTC_ADD_SMARTCARD_KEY:
229: debug("process_add");
230: process_add();
231: break;
232: case SSH_AGENTC_REMOVE_SMARTCARD_KEY:
233: debug("process_del");
234: process_del();
235: break;
236: case SSH2_AGENTC_SIGN_REQUEST:
237: debug("process_sign");
238: process_sign();
239: break;
240: default:
241: error("Unknown message %d", type);
242: break;
243: }
244: /* discard the remaining bytes from the current packet */
245: if (buf_len < buffer_len(&iqueue)) {
246: error("iqueue grew unexpectedly");
247: cleanup_exit(255);
248: }
249: consumed = buf_len - buffer_len(&iqueue);
250: if (msg_len < consumed) {
251: error("msg_len %d < consumed %d", msg_len, consumed);
252: cleanup_exit(255);
253: }
254: if (msg_len > consumed)
255: buffer_consume(&iqueue, msg_len - consumed);
256: }
257:
258: void
259: cleanup_exit(int i)
260: {
261: /* XXX */
262: _exit(i);
263: }
264:
265: int
266: main(int argc, char **argv)
267: {
268: fd_set *rset, *wset;
269: int in, out, max, log_stderr = 0;
270: ssize_t len, olen, set_size;
271: SyslogFacility log_facility = SYSLOG_FACILITY_AUTH;
272: LogLevel log_level = SYSLOG_LEVEL_ERROR;
273: char buf[4*4096];
1.2 deraadt 274: extern char *__progname;
1.1 markus 275:
276: TAILQ_INIT(&pkcs11_keylist);
277: pkcs11_init(0);
278:
279: log_init(__progname, log_level, log_facility, log_stderr);
280:
281: in = STDIN_FILENO;
282: out = STDOUT_FILENO;
283:
284: max = 0;
285: if (in > max)
286: max = in;
287: if (out > max)
288: max = out;
289:
290: buffer_init(&iqueue);
291: buffer_init(&oqueue);
292:
293: set_size = howmany(max + 1, NFDBITS) * sizeof(fd_mask);
294: rset = (fd_set *)xmalloc(set_size);
295: wset = (fd_set *)xmalloc(set_size);
296:
297: for (;;) {
298: memset(rset, 0, set_size);
299: memset(wset, 0, set_size);
300:
301: /*
302: * Ensure that we can read a full buffer and handle
303: * the worst-case length packet it can generate,
304: * otherwise apply backpressure by stopping reads.
305: */
306: if (buffer_check_alloc(&iqueue, sizeof(buf)) &&
307: buffer_check_alloc(&oqueue, MAX_MSG_LENGTH))
308: FD_SET(in, rset);
309:
310: olen = buffer_len(&oqueue);
311: if (olen > 0)
312: FD_SET(out, wset);
313:
314: if (select(max+1, rset, wset, NULL, NULL) < 0) {
315: if (errno == EINTR)
316: continue;
317: error("select: %s", strerror(errno));
318: cleanup_exit(2);
319: }
320:
321: /* copy stdin to iqueue */
322: if (FD_ISSET(in, rset)) {
323: len = read(in, buf, sizeof buf);
324: if (len == 0) {
325: debug("read eof");
326: cleanup_exit(0);
327: } else if (len < 0) {
328: error("read: %s", strerror(errno));
329: cleanup_exit(1);
330: } else {
331: buffer_append(&iqueue, buf, len);
332: }
333: }
334: /* send oqueue to stdout */
335: if (FD_ISSET(out, wset)) {
336: len = write(out, buffer_ptr(&oqueue), olen);
337: if (len < 0) {
338: error("write: %s", strerror(errno));
339: cleanup_exit(1);
340: } else {
341: buffer_consume(&oqueue, len);
342: }
343: }
344:
345: /*
346: * Process requests from client if we can fit the results
347: * into the output buffer, otherwise stop processing input
348: * and let the output queue drain.
349: */
350: if (buffer_check_alloc(&oqueue, MAX_MSG_LENGTH))
351: process();
352: }
353: }