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