1296781Sdes/* $OpenBSD: ssh-pkcs11-client.c,v 1.6 2015/12/11 00:20:04 mmcc Exp $ */
2204861Sdes/*
3204861Sdes * Copyright (c) 2010 Markus Friedl.  All rights reserved.
4204861Sdes *
5204861Sdes * Permission to use, copy, modify, and distribute this software for any
6204861Sdes * purpose with or without fee is hereby granted, provided that the above
7204861Sdes * copyright notice and this permission notice appear in all copies.
8204861Sdes *
9204861Sdes * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10204861Sdes * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11204861Sdes * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12204861Sdes * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13204861Sdes * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14204861Sdes * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15204861Sdes * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16204861Sdes */
17204861Sdes
18204861Sdes#include "includes.h"
19204861Sdes
20204861Sdes#ifdef ENABLE_PKCS11
21204861Sdes
22204861Sdes#include <sys/types.h>
23204861Sdes#ifdef HAVE_SYS_TIME_H
24204861Sdes# include <sys/time.h>
25204861Sdes#endif
26204861Sdes#include <sys/socket.h>
27204861Sdes
28204861Sdes#include <stdarg.h>
29204861Sdes#include <string.h>
30204861Sdes#include <unistd.h>
31204861Sdes#include <errno.h>
32204861Sdes
33295367Sdes#include <openssl/rsa.h>
34295367Sdes
35204861Sdes#include "pathnames.h"
36204861Sdes#include "xmalloc.h"
37204861Sdes#include "buffer.h"
38204861Sdes#include "log.h"
39204861Sdes#include "misc.h"
40204861Sdes#include "key.h"
41204861Sdes#include "authfd.h"
42204861Sdes#include "atomicio.h"
43204861Sdes#include "ssh-pkcs11.h"
44204861Sdes
45204861Sdes/* borrows code from sftp-server and ssh-agent */
46204861Sdes
47204861Sdesint fd = -1;
48204861Sdespid_t pid = -1;
49204861Sdes
50204861Sdesstatic void
51204861Sdessend_msg(Buffer *m)
52204861Sdes{
53204861Sdes	u_char buf[4];
54204861Sdes	int mlen = buffer_len(m);
55204861Sdes
56204861Sdes	put_u32(buf, mlen);
57204861Sdes	if (atomicio(vwrite, fd, buf, 4) != 4 ||
58204861Sdes	    atomicio(vwrite, fd, buffer_ptr(m),
59204861Sdes	    buffer_len(m)) != buffer_len(m))
60204861Sdes		error("write to helper failed");
61204861Sdes	buffer_consume(m, mlen);
62204861Sdes}
63204861Sdes
64204861Sdesstatic int
65204861Sdesrecv_msg(Buffer *m)
66204861Sdes{
67204861Sdes	u_int l, len;
68204861Sdes	u_char buf[1024];
69204861Sdes
70204861Sdes	if ((len = atomicio(read, fd, buf, 4)) != 4) {
71204861Sdes		error("read from helper failed: %u", len);
72204861Sdes		return (0); /* XXX */
73204861Sdes	}
74204861Sdes	len = get_u32(buf);
75204861Sdes	if (len > 256 * 1024)
76204861Sdes		fatal("response too long: %u", len);
77204861Sdes	/* read len bytes into m */
78204861Sdes	buffer_clear(m);
79204861Sdes	while (len > 0) {
80204861Sdes		l = len;
81204861Sdes		if (l > sizeof(buf))
82204861Sdes			l = sizeof(buf);
83204861Sdes		if (atomicio(read, fd, buf, l) != l) {
84204861Sdes			error("response from helper failed.");
85204861Sdes			return (0); /* XXX */
86204861Sdes		}
87204861Sdes		buffer_append(m, buf, l);
88204861Sdes		len -= l;
89204861Sdes	}
90204861Sdes	return (buffer_get_char(m));
91204861Sdes}
92204861Sdes
93204861Sdesint
94204861Sdespkcs11_init(int interactive)
95204861Sdes{
96204861Sdes	return (0);
97204861Sdes}
98204861Sdes
99204861Sdesvoid
100204861Sdespkcs11_terminate(void)
101204861Sdes{
102204861Sdes	close(fd);
103204861Sdes}
104204861Sdes
105204861Sdesstatic int
106204861Sdespkcs11_rsa_private_encrypt(int flen, const u_char *from, u_char *to, RSA *rsa,
107204861Sdes    int padding)
108204861Sdes{
109204861Sdes	Key key;
110204861Sdes	u_char *blob, *signature = NULL;
111204861Sdes	u_int blen, slen = 0;
112204861Sdes	int ret = -1;
113204861Sdes	Buffer msg;
114204861Sdes
115204861Sdes	if (padding != RSA_PKCS1_PADDING)
116204861Sdes		return (-1);
117204861Sdes	key.type = KEY_RSA;
118204861Sdes	key.rsa = rsa;
119204861Sdes	if (key_to_blob(&key, &blob, &blen) == 0)
120204861Sdes		return -1;
121204861Sdes	buffer_init(&msg);
122204861Sdes	buffer_put_char(&msg, SSH2_AGENTC_SIGN_REQUEST);
123204861Sdes	buffer_put_string(&msg, blob, blen);
124204861Sdes	buffer_put_string(&msg, from, flen);
125204861Sdes	buffer_put_int(&msg, 0);
126255767Sdes	free(blob);
127204861Sdes	send_msg(&msg);
128240075Sdes	buffer_clear(&msg);
129204861Sdes
130204861Sdes	if (recv_msg(&msg) == SSH2_AGENT_SIGN_RESPONSE) {
131204861Sdes		signature = buffer_get_string(&msg, &slen);
132204861Sdes		if (slen <= (u_int)RSA_size(rsa)) {
133204861Sdes			memcpy(to, signature, slen);
134204861Sdes			ret = slen;
135204861Sdes		}
136255767Sdes		free(signature);
137204861Sdes	}
138240075Sdes	buffer_free(&msg);
139204861Sdes	return (ret);
140204861Sdes}
141204861Sdes
142204861Sdes/* redirect the private key encrypt operation to the ssh-pkcs11-helper */
143204861Sdesstatic int
144204861Sdeswrap_key(RSA *rsa)
145204861Sdes{
146204861Sdes	static RSA_METHOD helper_rsa;
147204861Sdes
148204861Sdes	memcpy(&helper_rsa, RSA_get_default_method(), sizeof(helper_rsa));
149204861Sdes	helper_rsa.name = "ssh-pkcs11-helper";
150204861Sdes	helper_rsa.rsa_priv_enc = pkcs11_rsa_private_encrypt;
151204861Sdes	RSA_set_method(rsa, &helper_rsa);
152204861Sdes	return (0);
153204861Sdes}
154204861Sdes
155204861Sdesstatic int
156204861Sdespkcs11_start_helper(void)
157204861Sdes{
158204861Sdes	int pair[2];
159204861Sdes
160204861Sdes	if (socketpair(AF_UNIX, SOCK_STREAM, 0, pair) == -1) {
161204861Sdes		error("socketpair: %s", strerror(errno));
162204861Sdes		return (-1);
163204861Sdes	}
164204861Sdes	if ((pid = fork()) == -1) {
165204861Sdes		error("fork: %s", strerror(errno));
166204861Sdes		return (-1);
167204861Sdes	} else if (pid == 0) {
168204861Sdes		if ((dup2(pair[1], STDIN_FILENO) == -1) ||
169204861Sdes		    (dup2(pair[1], STDOUT_FILENO) == -1)) {
170204861Sdes			fprintf(stderr, "dup2: %s\n", strerror(errno));
171204861Sdes			_exit(1);
172204861Sdes		}
173204861Sdes		close(pair[0]);
174204861Sdes		close(pair[1]);
175204861Sdes		execlp(_PATH_SSH_PKCS11_HELPER, _PATH_SSH_PKCS11_HELPER,
176296781Sdes		    (char *)NULL);
177204861Sdes		fprintf(stderr, "exec: %s: %s\n", _PATH_SSH_PKCS11_HELPER,
178204861Sdes		    strerror(errno));
179204861Sdes		_exit(1);
180204861Sdes	}
181204861Sdes	close(pair[1]);
182204861Sdes	fd = pair[0];
183204861Sdes	return (0);
184204861Sdes}
185204861Sdes
186204861Sdesint
187204861Sdespkcs11_add_provider(char *name, char *pin, Key ***keysp)
188204861Sdes{
189204861Sdes	Key *k;
190204861Sdes	int i, nkeys;
191204861Sdes	u_char *blob;
192204861Sdes	u_int blen;
193204861Sdes	Buffer msg;
194204861Sdes
195204861Sdes	if (fd < 0 && pkcs11_start_helper() < 0)
196204861Sdes		return (-1);
197204861Sdes
198204861Sdes	buffer_init(&msg);
199204861Sdes	buffer_put_char(&msg, SSH_AGENTC_ADD_SMARTCARD_KEY);
200204861Sdes	buffer_put_cstring(&msg, name);
201204861Sdes	buffer_put_cstring(&msg, pin);
202204861Sdes	send_msg(&msg);
203204861Sdes	buffer_clear(&msg);
204204861Sdes
205204861Sdes	if (recv_msg(&msg) == SSH2_AGENT_IDENTITIES_ANSWER) {
206204861Sdes		nkeys = buffer_get_int(&msg);
207204861Sdes		*keysp = xcalloc(nkeys, sizeof(Key *));
208204861Sdes		for (i = 0; i < nkeys; i++) {
209204861Sdes			blob = buffer_get_string(&msg, &blen);
210255767Sdes			free(buffer_get_string(&msg, NULL));
211204861Sdes			k = key_from_blob(blob, blen);
212204861Sdes			wrap_key(k->rsa);
213204861Sdes			(*keysp)[i] = k;
214255767Sdes			free(blob);
215204861Sdes		}
216204861Sdes	} else {
217204861Sdes		nkeys = -1;
218204861Sdes	}
219204861Sdes	buffer_free(&msg);
220204861Sdes	return (nkeys);
221204861Sdes}
222204861Sdes
223204861Sdesint
224204861Sdespkcs11_del_provider(char *name)
225204861Sdes{
226204861Sdes	int ret = -1;
227204861Sdes	Buffer msg;
228204861Sdes
229204861Sdes	buffer_init(&msg);
230204861Sdes	buffer_put_char(&msg, SSH_AGENTC_REMOVE_SMARTCARD_KEY);
231204861Sdes	buffer_put_cstring(&msg, name);
232204861Sdes	buffer_put_cstring(&msg, "");
233204861Sdes	send_msg(&msg);
234204861Sdes	buffer_clear(&msg);
235204861Sdes
236204861Sdes	if (recv_msg(&msg) == SSH_AGENT_SUCCESS)
237204861Sdes		ret = 0;
238204861Sdes	buffer_free(&msg);
239204861Sdes	return (ret);
240204861Sdes}
241204861Sdes
242204861Sdes#endif /* ENABLE_PKCS11 */
243