Annotation of src/usr.bin/ssh/ssh-pkcs11-helper.c, Revision 1.2
1.1 markus 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:
36: struct pkcs11_keyinfo {
37: Key *key;
38: char *providername;
39: TAILQ_ENTRY(pkcs11_keyinfo) next;
40: };
41:
42: TAILQ_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 */
51: Buffer iqueue;
52: Buffer oqueue;
53:
54: static void
55: add_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:
65: static void
66: del_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 */
82: static Key *
83: lookup_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:
95: static void
96: send_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:
105: static void
106: process_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:
138: static void
139: process_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:
158: static void
159: process_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:
198: static void
199: process(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:
252: void
253: cleanup_exit(int i)
254: {
255: /* XXX */
256: _exit(i);
257: }
258:
259: int
260: main(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];
1.2 ! deraadt 268: extern char *optarg;
! 269: extern char *__progname;
1.1 markus 270:
271: TAILQ_INIT(&pkcs11_keylist);
272: pkcs11_init(0);
273:
274: log_init(__progname, log_level, log_facility, log_stderr);
275:
276: in = STDIN_FILENO;
277: out = STDOUT_FILENO;
278:
279: max = 0;
280: if (in > max)
281: max = in;
282: if (out > max)
283: max = out;
284:
285: buffer_init(&iqueue);
286: buffer_init(&oqueue);
287:
288: set_size = howmany(max + 1, NFDBITS) * sizeof(fd_mask);
289: rset = (fd_set *)xmalloc(set_size);
290: wset = (fd_set *)xmalloc(set_size);
291:
292: for (;;) {
293: memset(rset, 0, set_size);
294: memset(wset, 0, set_size);
295:
296: /*
297: * Ensure that we can read a full buffer and handle
298: * the worst-case length packet it can generate,
299: * otherwise apply backpressure by stopping reads.
300: */
301: if (buffer_check_alloc(&iqueue, sizeof(buf)) &&
302: buffer_check_alloc(&oqueue, MAX_MSG_LENGTH))
303: FD_SET(in, rset);
304:
305: olen = buffer_len(&oqueue);
306: if (olen > 0)
307: FD_SET(out, wset);
308:
309: if (select(max+1, rset, wset, NULL, NULL) < 0) {
310: if (errno == EINTR)
311: continue;
312: error("select: %s", strerror(errno));
313: cleanup_exit(2);
314: }
315:
316: /* copy stdin to iqueue */
317: if (FD_ISSET(in, rset)) {
318: len = read(in, buf, sizeof buf);
319: if (len == 0) {
320: debug("read eof");
321: cleanup_exit(0);
322: } else if (len < 0) {
323: error("read: %s", strerror(errno));
324: cleanup_exit(1);
325: } else {
326: buffer_append(&iqueue, buf, len);
327: }
328: }
329: /* send oqueue to stdout */
330: if (FD_ISSET(out, wset)) {
331: len = write(out, buffer_ptr(&oqueue), olen);
332: if (len < 0) {
333: error("write: %s", strerror(errno));
334: cleanup_exit(1);
335: } else {
336: buffer_consume(&oqueue, len);
337: }
338: }
339:
340: /*
341: * Process requests from client if we can fit the results
342: * into the output buffer, otherwise stop processing input
343: * and let the output queue drain.
344: */
345: if (buffer_check_alloc(&oqueue, MAX_MSG_LENGTH))
346: process();
347: }
348: }