View | Details | Raw Unified | Return to bug 2319 | Differences between
and this patch

Collapse All | Expand All

(-)a/Makefile.in (+1 lines)
Lines 101-106 SSHDOBJS=sshd.o auth-rhosts.o auth-passwd.o auth-rsa.o auth-rh-rsa.o \ Link Here
101
	auth2-none.o auth2-passwd.o auth2-pubkey.o \
101
	auth2-none.o auth2-passwd.o auth2-pubkey.o \
102
	monitor_mm.o monitor.o monitor_wrap.o kexdhs.o kexgexs.o kexecdhs.o \
102
	monitor_mm.o monitor.o monitor_wrap.o kexdhs.o kexgexs.o kexecdhs.o \
103
	kexc25519s.o auth-krb5.o \
103
	kexc25519s.o auth-krb5.o \
104
	auth-u2f.o \
104
	auth2-gss.o gss-serv.o gss-serv-krb5.o \
105
	auth2-gss.o gss-serv.o gss-serv-krb5.o \
105
	loginrec.o auth-pam.o auth-shadow.o auth-sia.o md5crypt.o \
106
	loginrec.o auth-pam.o auth-shadow.o auth-sia.o md5crypt.o \
106
	sftp-server.o sftp-common.o \
107
	sftp-server.o sftp-common.o \
(-)a/audit-linux.c (+1 lines)
Lines 113-118 audit_event(ssh_audit_event_t event) Link Here
113
	case SSH_AUTH_FAIL_PUBKEY:
113
	case SSH_AUTH_FAIL_PUBKEY:
114
	case SSH_AUTH_FAIL_HOSTBASED:
114
	case SSH_AUTH_FAIL_HOSTBASED:
115
	case SSH_AUTH_FAIL_GSSAPI:
115
	case SSH_AUTH_FAIL_GSSAPI:
116
	case SSH_AUTH_FAIL_U2F:
116
	case SSH_INVALID_USER:
117
	case SSH_INVALID_USER:
117
		linux_audit_record_event(-1, audit_username(), NULL,
118
		linux_audit_record_event(-1, audit_username(), NULL,
118
			get_remote_ipaddr(), "sshd", 0);
119
			get_remote_ipaddr(), "sshd", 0);
(-)a/audit.c (+3 lines)
Lines 63-68 audit_classify_auth(const char *method) Link Here
63
		return SSH_AUTH_FAIL_HOSTBASED;
63
		return SSH_AUTH_FAIL_HOSTBASED;
64
	else if (strcmp(method, "gssapi-with-mic") == 0)
64
	else if (strcmp(method, "gssapi-with-mic") == 0)
65
		return SSH_AUTH_FAIL_GSSAPI;
65
		return SSH_AUTH_FAIL_GSSAPI;
66
	else if (strcmp(method, "u2f") == 0)
67
		return SSH_AUTH_FAIL_U2F;
66
	else
68
	else
67
		return SSH_AUDIT_UNKNOWN;
69
		return SSH_AUDIT_UNKNOWN;
68
}
70
}
Lines 98-103 audit_event_lookup(ssh_audit_event_t ev) Link Here
98
		{SSH_AUTH_FAIL_PUBKEY,		"AUTH_FAIL_PUBKEY"},
100
		{SSH_AUTH_FAIL_PUBKEY,		"AUTH_FAIL_PUBKEY"},
99
		{SSH_AUTH_FAIL_HOSTBASED,	"AUTH_FAIL_HOSTBASED"},
101
		{SSH_AUTH_FAIL_HOSTBASED,	"AUTH_FAIL_HOSTBASED"},
100
		{SSH_AUTH_FAIL_GSSAPI,		"AUTH_FAIL_GSSAPI"},
102
		{SSH_AUTH_FAIL_GSSAPI,		"AUTH_FAIL_GSSAPI"},
103
		{SSH_AUTH_FAIL_U2F,		"AUTH_FAIL_U2F"},
101
		{SSH_INVALID_USER,		"INVALID_USER"},
104
		{SSH_INVALID_USER,		"INVALID_USER"},
102
		{SSH_NOLOGIN,			"NOLOGIN"},
105
		{SSH_NOLOGIN,			"NOLOGIN"},
103
		{SSH_CONNECTION_CLOSE,		"CONNECTION_CLOSE"},
106
		{SSH_CONNECTION_CLOSE,		"CONNECTION_CLOSE"},
(-)a/audit.h (+1 lines)
Lines 39-44 enum ssh_audit_event_type { Link Here
39
	SSH_AUTH_FAIL_PUBKEY,	/* ssh2 pubkey or ssh1 rsa */
39
	SSH_AUTH_FAIL_PUBKEY,	/* ssh2 pubkey or ssh1 rsa */
40
	SSH_AUTH_FAIL_HOSTBASED,	/* ssh2 hostbased or ssh1 rhostsrsa */
40
	SSH_AUTH_FAIL_HOSTBASED,	/* ssh2 hostbased or ssh1 rhostsrsa */
41
	SSH_AUTH_FAIL_GSSAPI,
41
	SSH_AUTH_FAIL_GSSAPI,
42
	SSH_AUTH_FAIL_U2F,
42
	SSH_INVALID_USER,
43
	SSH_INVALID_USER,
43
	SSH_NOLOGIN,		/* denied by /etc/nologin, not implemented */
44
	SSH_NOLOGIN,		/* denied by /etc/nologin, not implemented */
44
	SSH_CONNECTION_CLOSE,	/* closed after attempting auth or session */
45
	SSH_CONNECTION_CLOSE,	/* closed after attempting auth or session */
(-)a/auth-u2f.c (+561 lines)
Line 0 Link Here
1
#include "includes.h"
2
3
#ifdef U2F
4
5
#include <ctype.h>
6
#include <openssl/x509.h>
7
#include <u2f-host.h>
8
#include <fcntl.h>
9
10
#include "key.h"
11
#include "hostfile.h"
12
#include "auth.h"
13
#include "ssh.h"
14
#include "ssh2.h"
15
#include "log.h"
16
#include "dispatch.h"
17
#include "misc.h"
18
#include "servconf.h"
19
#include "packet.h"
20
#include "digest.h"
21
#include "xmalloc.h"
22
#include "monitor_wrap.h"
23
24
// Evaluates to the maximum size that base64-encoding 'size' bytes can have,
25
// including one byte for a trailing NULL byte.
26
#define BASE64_ENCODED_SIZE(size) (((size)+2)/3)*4 + 1
27
28
// Evaluates to the maximum size that base64-decoding 'size' bytes can have.
29
#define BASE64_DECODED_SIZE(size) (size * 3/4)
30
31
extern ServerOptions options;
32
33
static void input_userauth_u2f_auth_response(int, u_int32_t, void *);
34
static void input_userauth_u2f_register_response(int type, u_int32_t seq, void *ctxt);
35
36
static const int u2f_challenge_len = 32;
37
// TODO: to what should we set the appid?
38
static const char *appid = "ssh://localhost";
39
40
void u2f_sha256(u_char *dest, u_char *src, size_t srclen) {
41
	struct ssh_digest_ctx *ctx = ssh_digest_start(SSH_DIGEST_SHA256);
42
	ssh_digest_update(ctx, src, srclen);
43
	ssh_digest_final(ctx, dest, ssh_digest_bytes(SSH_DIGEST_SHA256));
44
}
45
46
/* We can get away without a JSON parser because all values in the JSON
47
 * messages used in U2F are (websafe) base64 encoded, therefore we don’t need
48
 * to care about escaping at all. We can just look for the starting double
49
 * quote and take everything until the next double quote.
50
 */
51
static char *
52
extract_json_string(const char *json, const char *key)
53
{
54
	char *quotedkey;
55
	char *keypos;
56
	char *value;
57
	char *end;
58
	int quotedkeylen;
59
60
	quotedkeylen = xasprintf(&quotedkey, "\"%s\"", key);
61
	if ((keypos = strstr(json, quotedkey)) == NULL)
62
		return NULL;
63
64
	keypos += quotedkeylen;
65
	if (*keypos == ':')
66
		keypos++;
67
	while (*keypos != '\0' && isspace(*keypos))
68
		keypos++;
69
	if (*keypos != '"')
70
		return NULL;
71
	keypos++;
72
	value = xstrdup(keypos);
73
	if ((end = strchr(value, '"')) == NULL) {
74
		free(value);
75
		return NULL;
76
	}
77
	*end = '\0';
78
	return value;
79
}
80
81
static int
82
urlsafe_base64_decode(const char *base64, u_char *buffer, size_t bufferlen)
83
{
84
	// U2F uses urlsafe base64, which replaces + with - and / with _, so we
85
	// need to revert that before base64 decoding.
86
	char *replaced;
87
	char *pos;
88
89
	replaced = xstrdup(base64);
90
	while ((pos = strchr(replaced, '-')) != NULL)
91
        *pos = '+';
92
	while ((pos = strchr(replaced, '_')) != NULL)
93
		*pos = '/';
94
95
	return b64_pton(replaced, buffer, bufferlen);
96
}
97
98
static int
99
urlsafe_base64_encode(u_char const *src, size_t srclength, char *target, size_t targsize)
100
{
101
	char *pos;
102
	int len;
103
104
	if ((len = b64_ntop(src, srclength, target, targsize)) == -1)
105
		return -1;
106
107
	while ((pos = strchr(target, '+')) != NULL)
108
		*pos = '-';
109
110
	while ((pos = strchr(target, '/')) != NULL)
111
		*pos = '_';
112
113
	return len;
114
}
115
116
static Key*
117
read_keyfile(FILE *fp, char *filename, struct passwd *pw, u_long *linenum)
118
{
119
	// TODO: do we need to use a different constant here?
120
	char line[SSH_MAX_PUBKEY_BYTES];
121
	Key *found = NULL;
122
123
	while (read_keyfile_line(fp, filename, line, sizeof(line), linenum) != -1) {
124
		char *cp, *key_options;
125
		if (found != NULL)
126
			key_free(found);
127
		found = key_new(KEY_U2F);
128
		// TODO: auth_clear_options();?
129
130
		/* Skip leading whitespace, empty and comment lines. */
131
        for (cp = line; *cp == ' ' || *cp == '\t'; cp++)
132
            ;
133
        if (!*cp || *cp == '\n' || *cp == '#')
134
            continue;
135
136
		debug("reading key from line %lu", *linenum);
137
		if (key_read(found, &cp) != 1) {
138
			debug("key_read failed, skipping line %lu", *linenum);
139
			continue;
140
		}
141
		debug("key type: %d (u2f = %d)", found->type, KEY_U2F);
142
		if (found->type == KEY_U2F) {
143
		//if (key_equal(found, key)) {
144
			//if (auth_parse_options(pw, key_options, filename, *linenum) != 1)
145
			//	continue;
146
			// TODO: calculate and display a fingerprint of the key handle and pubkey?
147
			debug("matching key found: file %s, line %lu", filename, *linenum);
148
			// TODO: store multiple matches in authctx->methoddata, or rather authctxt->keys? (see sshconnect2.c)
149
			return found;
150
		}
151
	}
152
	return NULL;
153
}
154
155
/*
156
 * Read a key from the key files.
157
 */
158
Key*
159
read_user_u2f_key(struct passwd *pw, u_int key_idx)
160
{
161
	size_t i;
162
	// TODO: It might not be safe to pass the key back to the unprivileged
163
	// process. It probably is, but we should review this.
164
165
	// In the first step, we need to go through all u2f keys that we have and
166
	// collect their key handles.
167
	for (i = 0; i < options.num_authkeys_files; i++) {
168
		FILE *fp;
169
		char *file;
170
		Key *key = NULL;
171
		u_long linenum = 0;
172
		if (strcasecmp(options.authorized_keys_files[i], "none") == 0)
173
			continue;
174
		file = expand_authorized_keys(options.authorized_keys_files[i], pw);
175
		debug("need to check %s", file);
176
		fp = fopen(file, "r");
177
		do
178
		{
179
			// TODO: Hackish way to allow getting more than one key
180
			key_free(key);
181
			key = read_keyfile(fp, file, pw, &linenum);
182
		}
183
		while(key_idx-- > 0);
184
		fclose(fp);
185
		free(file);
186
		if (key != NULL)
187
			return key;
188
	}
189
	return NULL;
190
}
191
192
static int
193
userauth_u2f_register(Authctxt *authctxt)
194
{
195
	u_char random[u2f_challenge_len];
196
	char challenge[BASE64_ENCODED_SIZE(sizeof(random))];
197
	char *json;
198
199
	arc4random_buf(random, sizeof(random));
200
	if (urlsafe_base64_encode(random, sizeof(random), challenge, sizeof(challenge)) == -1)
201
		fatal("urlsafe_base64_encode(arc4random_buf()) failed");
202
203
	xasprintf(&json, "{\"challenge\": \"%s\", \"version\": \"U2F_V2\", \"appId\": \"%s\"}",
204
		challenge, appid);
205
206
	packet_start(SSH2_MSG_USERAUTH_INFO_REQUEST);
207
	packet_put_cstring(json);
208
	packet_send();
209
	free(json);
210
	dispatch_set(SSH2_MSG_USERAUTH_INFO_RESPONSE,
211
		&input_userauth_u2f_register_response);
212
	authctxt->postponed = 1;
213
	return 0;
214
}
215
216
static int
217
userauth_u2f_authenticate(Authctxt *authctxt)
218
{
219
	char challenge[BASE64_ENCODED_SIZE(u2f_challenge_len)];
220
	char pubkey[BASE64_ENCODED_SIZE(U2F_PUBKEY_LEN)];
221
	char *keyhandle;
222
	char *json;
223
	Key *key;
224
	u_int idx = 0;
225
226
	// Get multiple keys by increasing idx until key == NULL
227
	// TODO: send multiple challenges for all keys (or something)
228
	if ((key = PRIVSEP(read_user_u2f_key(authctxt->pw, idx))) == NULL) {
229
		error("U2F authentication impossible: no ssh-u2f keys found in the authorized keys file(s).");
230
		return (0);
231
	}
232
233
	packet_start(SSH2_MSG_USERAUTH_INFO_REQUEST);
234
	authctxt->u2f_challenge = xmalloc(u2f_challenge_len);
235
	arc4random_buf(authctxt->u2f_challenge, u2f_challenge_len);
236
	authctxt->u2f_key = key;
237
238
	if (urlsafe_base64_encode(authctxt->u2f_challenge, u2f_challenge_len,
239
			challenge, sizeof(challenge)) == -1)
240
		fatal("urlsafe_base64_encode(arc4random_buf()) failed");
241
242
	if (urlsafe_base64_encode(key->u2f_pubkey, U2F_PUBKEY_LEN, pubkey, sizeof(pubkey)) == -1)
243
		fatal("urlsafe_base64_encode(key->u2f_pubkey) failed");
244
245
	keyhandle = xmalloc(BASE64_ENCODED_SIZE(key->u2f_key_handle_len));
246
	if (urlsafe_base64_encode(key->u2f_key_handle, key->u2f_key_handle_len,
247
				keyhandle, BASE64_ENCODED_SIZE(key->u2f_key_handle_len)) == -1)
248
		fatal("urlsafe_base64_encode(key->u2f_key_handle) failed");
249
250
	xasprintf(&json, "{\"challenge\": \"%s\", \"keyHandle\": \"%s\", \"appId\": \"%s\"}",
251
		challenge, keyhandle, appid);
252
	packet_put_cstring(json);
253
	free(json);
254
	free(keyhandle);
255
	packet_send();
256
257
	dispatch_set(SSH2_MSG_USERAUTH_INFO_RESPONSE,
258
		&input_userauth_u2f_auth_response);
259
	authctxt->postponed = 1;
260
	return (0);
261
}
262
263
static int
264
userauth_u2f(Authctxt *authctxt)
265
{
266
	int mode = packet_get_int();
267
	packet_check_eom();
268
	// TODO: shared constants
269
	if (mode == 0) {
270
		debug("Starting U2F registration");
271
		return userauth_u2f_register(authctxt);
272
	} else if (mode == 1) {
273
		debug("Starting U2F authentication");
274
		return userauth_u2f_authenticate(authctxt);
275
	} else {
276
		error("Unknown U2F mode %d requested by the client.", mode);
277
		return 0;
278
	}
279
}
280
281
static void
282
input_userauth_u2f_register_response(int type, u_int32_t seq, void *ctxt)
283
{
284
#define u2f_bounds_check(necessary_bytes) do { \
285
	if (restlen < necessary_bytes) { \
286
		error("U2F response too short: need %d bytes, but only %d remaining", \
287
			necessary_bytes, restlen); \
288
		goto out; \
289
	} \
290
} while (0)
291
292
#define u2f_advance(parsed_bytes) do { \
293
	int advance = parsed_bytes; \
294
	walk += advance; \
295
	restlen -= advance; \
296
} while (0)
297
298
    Authctxt *authctxt = ctxt;
299
	char *response, *regdata = NULL, *clientdata = NULL;
300
	u_char *decoded = NULL;
301
	u_char *walk = NULL;
302
	u_char *keyhandle = NULL;
303
	u_char *pubkey = NULL;
304
	u_char *signature = NULL;
305
	u_char *dummy = NULL;
306
	u_char *cdecoded = NULL;
307
	X509 *x509 = NULL;
308
	EVP_PKEY *pkey = NULL;
309
	EVP_MD_CTX mdctx;
310
	int restlen;
311
	int khlen;
312
	int cdecodedlen;
313
	int err;
314
	char errorbuf[4096];
315
	u_char digest[ssh_digest_bytes(SSH_DIGEST_SHA256)];
316
317
	authctxt->postponed = 0;
318
319
	response = packet_get_string(NULL);
320
	packet_check_eom();
321
	if ((regdata = extract_json_string(response, "registrationData")) == NULL) {
322
		error("U2F Response not JSON, or does not contain \"registrationData\"");
323
		goto out;
324
	}
325
326
	decoded = xmalloc(BASE64_DECODED_SIZE(strlen(regdata)));
327
	restlen = urlsafe_base64_decode(regdata, decoded, BASE64_DECODED_SIZE(strlen(regdata)));
328
	walk = decoded;
329
330
	// Header (magic byte)
331
	u2f_bounds_check(1);
332
	if (walk[0] != 0x05) {
333
		error("U2F response does not start with magic byte 0x05");
334
		goto out;
335
	}
336
	u2f_advance(1);
337
338
	// Length of the public key
339
	u2f_bounds_check(U2F_PUBKEY_LEN);
340
	pubkey = walk;
341
	u2f_advance(U2F_PUBKEY_LEN);
342
343
	// Length of the key handle
344
	u2f_bounds_check(1);
345
	khlen = walk[0];
346
	u2f_advance(1);
347
348
	// Key handle
349
	u2f_bounds_check(khlen);
350
	keyhandle = walk;
351
	u2f_advance(khlen);
352
353
	// Attestation certificate
354
	u2f_bounds_check(1);
355
	signature = walk;
356
	if ((x509 = d2i_X509(NULL, &signature, restlen)) == NULL) {
357
		error("U2F response contains an invalid attestation certificate.");
358
		goto out;
359
	}
360
361
	// U2F dictates that the length of the certificate should be determined by
362
	// encoding the certificate using DER.
363
	u2f_advance(i2d_X509(x509, &dummy));
364
	free(dummy);
365
366
	// Ensure we have at least one byte of signature.
367
	u2f_bounds_check(1);
368
369
	if ((clientdata = extract_json_string(response, "clientData")) == NULL) {
370
		error("U2F response JSON lacks the \"clientData\" key.");
371
		goto out;
372
	}
373
374
	cdecoded = xmalloc(BASE64_DECODED_SIZE(strlen(clientdata)));
375
	cdecodedlen = urlsafe_base64_decode(clientdata, cdecoded, BASE64_DECODED_SIZE(strlen(clientdata)));
376
	pkey = X509_get_pubkey(x509);
377
378
	if ((err = EVP_VerifyInit(&mdctx, EVP_ecdsa())) != 1) {
379
		ERR_error_string(ERR_get_error(), errorbuf);
380
		fatal("EVP_VerifyInit() failed: %s (reason: %s)",
381
				errorbuf, ERR_reason_error_string(err));
382
	}
383
	EVP_VerifyUpdate(&mdctx, "\0", 1);
384
	u2f_sha256(digest, appid, strlen(appid));
385
	EVP_VerifyUpdate(&mdctx, digest, sizeof(digest));
386
	u2f_sha256(digest, cdecoded, cdecodedlen);
387
	EVP_VerifyUpdate(&mdctx, digest, sizeof(digest));
388
	EVP_VerifyUpdate(&mdctx, keyhandle, khlen);
389
	EVP_VerifyUpdate(&mdctx, pubkey, U2F_PUBKEY_LEN);
390
391
	if ((err = EVP_VerifyFinal(&mdctx, walk, restlen, pkey)) == -1) {
392
		ERR_error_string(ERR_get_error(), errorbuf);
393
		error("Verifying the U2F registration signature failed: %s (reason: %s)",
394
				errorbuf, ERR_reason_error_string(err));
395
		goto out;
396
	}
397
398
	{
399
		/* Send the client a ssh-u2f line to append to the authorized_keys file
400
		 * (in order to register the security key that was just used). */
401
		char *authorizedkey;
402
		char key[U2F_PUBKEY_LEN + khlen];
403
		char key64[BASE64_ENCODED_SIZE(sizeof(key))];
404
405
		memcpy(key, pubkey, U2F_PUBKEY_LEN);
406
		memcpy(key+U2F_PUBKEY_LEN, keyhandle, khlen);
407
408
		if (b64_ntop(key, sizeof(key), key64, sizeof(key64)) == -1)
409
			fatal("b64_ntop(key)");
410
411
		xasprintf(&authorizedkey, "ssh-u2f %s my security key", key64);
412
		packet_start(SSH2_MSG_USERAUTH_INFO_REQUEST);
413
		packet_put_cstring(authorizedkey);
414
		packet_send();
415
		free(authorizedkey);
416
		dispatch_set(SSH2_MSG_USERAUTH_INFO_RESPONSE, NULL);
417
	}
418
419
out:
420
	free(regdata);
421
	free(clientdata);
422
	free(decoded);
423
	free(cdecoded);
424
	if (x509 != NULL)
425
		X509_free(x509);
426
	if (pkey != NULL)
427
		EVP_PKEY_free(pkey);
428
    userauth_finish(authctxt, 0, "u2f", NULL);
429
#undef u2f_bounds_check
430
#undef u2f_advance
431
}
432
433
int
434
verify_u2f_user(Key *key, u_char *dgst, size_t dgstlen, u_char *sig, size_t siglen)
435
{
436
	char errorbuf[4096];
437
	int ret = 0;
438
	EC_KEY *ec;
439
	u_char *p;
440
	/* To save bytes, the (common) public key prefix is not included in U2F
441
	 * messages itself. */
442
	const int prefix_len = 26;
443
	const int total_len = U2F_PUBKEY_LEN + prefix_len;
444
	u_char user_pubkey[total_len] =
445
		"\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a"
446
		"\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00";
447
448
	memcpy(user_pubkey+prefix_len, key->u2f_pubkey, U2F_PUBKEY_LEN);
449
450
	p = user_pubkey;
451
	if ((ec = d2i_EC_PUBKEY(NULL, &p, total_len)) == NULL) {
452
		ERR_error_string(ERR_get_error(), errorbuf);
453
		error("Verifying U2F authentication signature failed: "
454
				"d2i_EC_PUBKEY() failed: %s (reason: %s)",
455
				errorbuf, ERR_reason_error_string(ERR_get_error()));
456
		return 0;
457
	}
458
459
	if ((ret = ECDSA_verify(0, dgst, dgstlen, sig, siglen, ec)) == -1) {
460
		ERR_error_string(ERR_get_error(), errorbuf);
461
		error("Verifying U2F authentication signature failed: "
462
				"ECDSA_verify() failed: %s (reason: %s)",
463
				errorbuf, ERR_reason_error_string(ERR_get_error()));
464
		goto out;
465
	}
466
467
	debug("U2F authentication signature verified.");
468
469
out:
470
	EC_KEY_free(ec);
471
	return (ret == 1);
472
}
473
474
// TODO: can we send multiple authrequests at the same time, so that we don’t
475
// need multiple round-trips but still support multiple security keys
476
// TODO: use auth_info() so that in log messages about accepted auths we will see a message that identifies the key. perhaps we can just use the human readable suffix that you can specify in the authorized_keys file(s)?
477
static void
478
input_userauth_u2f_auth_response(int type, u_int32_t seq, void *ctxt)
479
{
480
	int authenticated = 0;
481
    Authctxt *authctxt = ctxt;
482
	u_char digest[ssh_digest_bytes(SSH_DIGEST_SHA256)];
483
	char *sig = NULL;
484
	char *clientdata = NULL;
485
	u_char *decoded = NULL;
486
	int decodedlen;
487
	u_char *cdecoded = NULL;
488
	int cdecodedlen;
489
    char *resp = packet_get_string(NULL);
490
    packet_check_eom();
491
492
	if ((sig = extract_json_string(resp, "signatureData")) == NULL) {
493
		error("U2F Response not JSON, or does not contain \"signatureData\"");
494
		goto out;
495
	}
496
497
	if (*sig == '\0') {
498
		error("U2F authentication failed: empty signature. "
499
				"Probably the key is not registered (i.e. the configured "
500
				"key handle/pubkey do not exist on the security key you are using)");
501
		goto out;
502
	}
503
504
	decoded = xmalloc(BASE64_DECODED_SIZE(strlen(sig)));
505
	decodedlen = urlsafe_base64_decode(sig, decoded, BASE64_DECODED_SIZE(strlen(sig)));
506
	// Ensure that the user presence byte, the counter and at least one byte of
507
	// signature are present.
508
	if (decodedlen <= (sizeof(u_char) + sizeof(u_int32_t))) {
509
		error("Decoded U2F signature too short (%d bytes, expected more than %d bytes)",
510
				decodedlen, sizeof(u_char) + sizeof(u_int32_t));
511
		goto out;
512
	}
513
	if ((decoded[0] & 0x01) != 0x01) {
514
		error("No user presence detected. Please touch your security key upon "
515
				"being prompted when retrying.");
516
		goto out;
517
	}
518
	u_int32_t counter = ntohl(*((u_int32_t*)(decoded + sizeof(u_char))));
519
	// TODO: Ideally, we would verify that this counter never decreases to
520
	// detect cloned security keys.
521
	debug("usage counter = %d\n", counter);
522
523
	struct sha_digest_ctx *sha256ctx = ssh_digest_start(SSH_DIGEST_SHA256);
524
	u2f_sha256(digest, appid, strlen(appid));
525
	ssh_digest_update(sha256ctx, digest, sizeof(digest));
526
	ssh_digest_update(sha256ctx, decoded, sizeof(u_char));
527
	ssh_digest_update(sha256ctx, decoded+1, 4 * sizeof(u_char));
528
529
	if ((clientdata = extract_json_string(resp, "clientData")) == NULL) {
530
		error("U2F response JSON lacks the \"clientData\" key.");
531
		goto out;
532
	}
533
534
	// TODO: verify that the challenge and appid is identical to what we sent out, to verify that there was no man in the middle.
535
536
	cdecoded = xmalloc(BASE64_DECODED_SIZE(strlen(clientdata)));
537
	cdecodedlen = urlsafe_base64_decode(clientdata, cdecoded, BASE64_DECODED_SIZE(strlen(clientdata)));
538
	u2f_sha256(digest, cdecoded, cdecodedlen);
539
	ssh_digest_update(sha256ctx, digest, sizeof(digest));
540
	ssh_digest_final(sha256ctx, digest, sizeof(digest));
541
542
	authenticated = PRIVSEP(verify_u2f_user(
543
		authctxt->u2f_key, digest, sizeof(digest), decoded+5, decodedlen-5));
544
545
	authctxt->postponed = 0;
546
	dispatch_set(SSH2_MSG_USERAUTH_INFO_RESPONSE, NULL);
547
out:
548
	free(sig);
549
	free(clientdata);
550
	free(decoded);
551
	free(cdecoded);
552
	userauth_finish(authctxt, authenticated, "u2f", NULL);
553
}
554
555
Authmethod method_u2f = {
556
	"u2f",
557
	userauth_u2f,
558
	&options.u2f_authentication
559
};
560
561
#endif /* U2F */
(-)a/auth.h (+11 lines)
Lines 73-78 struct Authctxt { Link Here
73
	char		*krb5_ticket_file;
73
	char		*krb5_ticket_file;
74
	char		*krb5_ccname;
74
	char		*krb5_ccname;
75
#endif
75
#endif
76
#ifdef U2F
77
	Key         *u2f_key;
78
	u_char      *u2f_challenge;
79
#endif
76
	Buffer		*loginmsg;
80
	Buffer		*loginmsg;
77
	void		*methoddata;
81
	void		*methoddata;
78
};
82
};
Lines 124-129 int user_key_allowed(struct passwd *, Key *); Link Here
124
void	 pubkey_auth_info(Authctxt *, const Key *, const char *, ...)
128
void	 pubkey_auth_info(Authctxt *, const Key *, const char *, ...)
125
	    __attribute__((__format__ (printf, 3, 4)));
129
	    __attribute__((__format__ (printf, 3, 4)));
126
130
131
#ifdef U2F
132
#define U2F_PUBKEY_LEN 65
133
134
Key	 *read_user_u2f_key(struct passwd *, u_int);
135
int	 verify_u2f_user(Key *, u_char *, size_t, u_char *, size_t);
136
#endif
137
127
struct stat;
138
struct stat;
128
int	 auth_secure_path(const char *, struct stat *, const char *, uid_t,
139
int	 auth_secure_path(const char *, struct stat *, const char *, uid_t,
129
    char *, size_t);
140
    char *, size_t);
(-)a/auth2.c (+6 lines)
Lines 72-77 extern Authmethod method_hostbased; Link Here
72
#ifdef GSSAPI
72
#ifdef GSSAPI
73
extern Authmethod method_gssapi;
73
extern Authmethod method_gssapi;
74
#endif
74
#endif
75
#ifdef U2F
76
extern Authmethod method_u2f;
77
#endif
75
78
76
Authmethod *authmethods[] = {
79
Authmethod *authmethods[] = {
77
	&method_none,
80
	&method_none,
Lines 79-84 Authmethod *authmethods[] = { Link Here
79
#ifdef GSSAPI
82
#ifdef GSSAPI
80
	&method_gssapi,
83
	&method_gssapi,
81
#endif
84
#endif
85
#ifdef U2F
86
	&method_u2f,
87
#endif
82
	&method_passwd,
88
	&method_passwd,
83
	&method_kbdint,
89
	&method_kbdint,
84
	&method_hostbased,
90
	&method_hostbased,
(-)a/config.h.in (-3 / +8 lines)
Lines 1555-1563 Link Here
1555
/* Define if you want S/Key support */
1555
/* Define if you want S/Key support */
1556
#undef SKEY
1556
#undef SKEY
1557
1557
1558
/* Define if you want U2F support */
1559
#undef U2F
1560
1561
/* Define if your skeychallenge() function takes 4 arguments (NetBSD) */
1558
/* Define if your skeychallenge() function takes 4 arguments (NetBSD) */
1562
#undef SKEYCHALLENGE_4ARG
1559
#undef SKEYCHALLENGE_4ARG
1563
1560
Lines 1610-1615 Link Here
1610
/* syslog_r function is safe to use in in a signal handler */
1607
/* syslog_r function is safe to use in in a signal handler */
1611
#undef SYSLOG_R_SAFE_IN_SIGHAND
1608
#undef SYSLOG_R_SAFE_IN_SIGHAND
1612
1609
1610
/* Enable U2F support (using libu2f-host) */
1611
#undef U2F
1612
1613
/* Support passwords > 8 chars */
1613
/* Support passwords > 8 chars */
1614
#undef UNIXWARE_LONG_PASSWORDS
1614
#undef UNIXWARE_LONG_PASSWORDS
1615
1615
Lines 1689-1694 Link Here
1689
/* Define if xauth is found in your path */
1689
/* Define if xauth is found in your path */
1690
#undef XAUTH_PATH
1690
#undef XAUTH_PATH
1691
1691
1692
/* Enable large inode numbers on Mac OS X 10.5.  */
1693
#ifndef _DARWIN_USE_64_BIT_INODE
1694
# define _DARWIN_USE_64_BIT_INODE 1
1695
#endif
1696
1692
/* Number of bits in a file offset, on hosts where this is settable. */
1697
/* Number of bits in a file offset, on hosts where this is settable. */
1693
#undef _FILE_OFFSET_BITS
1698
#undef _FILE_OFFSET_BITS
1694
1699
(-)a/monitor.c (+63 lines)
Lines 185-190 int mm_answer_audit_event(int, Buffer *); Link Here
185
int mm_answer_audit_command(int, Buffer *);
185
int mm_answer_audit_command(int, Buffer *);
186
#endif
186
#endif
187
187
188
#ifdef U2F
189
int mm_answer_read_user_u2f_key(int, Buffer *);
190
int mm_answer_verify_u2f_user(int, Buffer *);
191
#endif
192
188
static int monitor_read_log(struct monitor *);
193
static int monitor_read_log(struct monitor *);
189
194
190
static Authctxt *authctxt;
195
static Authctxt *authctxt;
Lines 256-261 struct mon_table mon_dispatch_proto20[] = { Link Here
256
    {MONITOR_REQ_GSSUSEROK, MON_AUTH, mm_answer_gss_userok},
261
    {MONITOR_REQ_GSSUSEROK, MON_AUTH, mm_answer_gss_userok},
257
    {MONITOR_REQ_GSSCHECKMIC, MON_ISAUTH, mm_answer_gss_checkmic},
262
    {MONITOR_REQ_GSSCHECKMIC, MON_ISAUTH, mm_answer_gss_checkmic},
258
#endif
263
#endif
264
#ifdef U2F
265
    {MONITOR_REQ_READUSERU2FKEY, MON_ISAUTH, mm_answer_read_user_u2f_key},
266
    {MONITOR_REQ_VERIFYU2FUSER, MON_AUTH, mm_answer_verify_u2f_user},
267
#endif
259
    {0, 0, NULL}
268
    {0, 0, NULL}
260
};
269
};
261
270
Lines 1752-1757 mm_answer_audit_event(int socket, Buffer *m) Link Here
1752
	case SSH_AUTH_FAIL_PUBKEY:
1761
	case SSH_AUTH_FAIL_PUBKEY:
1753
	case SSH_AUTH_FAIL_HOSTBASED:
1762
	case SSH_AUTH_FAIL_HOSTBASED:
1754
	case SSH_AUTH_FAIL_GSSAPI:
1763
	case SSH_AUTH_FAIL_GSSAPI:
1764
	case SSH_AUTH_FAIL_U2F:
1755
	case SSH_LOGIN_EXCEED_MAXTRIES:
1765
	case SSH_LOGIN_EXCEED_MAXTRIES:
1756
	case SSH_LOGIN_ROOT_DENIED:
1766
	case SSH_LOGIN_ROOT_DENIED:
1757
	case SSH_CONNECTION_CLOSE:
1767
	case SSH_CONNECTION_CLOSE:
Lines 2164-2166 mm_answer_gss_userok(int sock, Buffer *m) Link Here
2164
}
2174
}
2165
#endif /* GSSAPI */
2175
#endif /* GSSAPI */
2166
2176
2177
#ifdef U2F
2178
int
2179
mm_answer_read_user_u2f_key(int sock, Buffer *m)
2180
{
2181
	Key *key;
2182
	u_int key_idx;
2183
	u_char *blob = NULL;
2184
	u_int blen = 0;
2185
2186
	key_idx = buffer_get_int(m);
2187
	buffer_clear(m);
2188
2189
	key = read_user_u2f_key(authctxt->pw, key_idx);
2190
	buffer_put_int(m, key == NULL ? 1 : 0);
2191
	if (key != NULL)
2192
	{
2193
		if (key_to_blob(key, &blob, &blen) == 0)
2194
			fatal("%s: key_to_blob failed", __func__);
2195
		buffer_put_string(m, blob, blen);
2196
		debug3("%s: sending key", __func__);
2197
	} else {
2198
		debug3("%s: no key to send", __func__);
2199
	}
2200
2201
	mm_request_send(sock, MONITOR_ANS_READUSERU2FKEY, m);
2202
	return (0);
2203
}
2204
2205
int
2206
mm_answer_verify_u2f_user(int sock, Buffer *m)
2207
{
2208
	int authenticated = 0;
2209
	Key *key;
2210
	u_char *blob, *dgst, *sig;
2211
	size_t bloblen, dgstlen, siglen;
2212
2213
	blob = buffer_get_string(m, &bloblen);
2214
	key = key_from_blob(blob, bloblen);
2215
	dgst = buffer_get_string(m, &dgstlen);
2216
	sig = buffer_get_string(m, &siglen);
2217
2218
	buffer_clear(m);
2219
2220
	authenticated = verify_u2f_user(key, dgst, dgstlen, sig, siglen);
2221
	buffer_put_int(m, authenticated);
2222
2223
	auth_method = "u2f";
2224
	mm_request_send(sock, MONITOR_ANS_VERIFYU2FUSER, m);
2225
2226
	key_free(key);
2227
	return authenticated;
2228
}
2229
#endif /* U2F */
(-)a/monitor.h (+2 lines)
Lines 56-61 enum monitor_reqtype { Link Here
56
	MONITOR_REQ_GSSUSEROK = 46, MONITOR_ANS_GSSUSEROK = 47,
56
	MONITOR_REQ_GSSUSEROK = 46, MONITOR_ANS_GSSUSEROK = 47,
57
	MONITOR_REQ_GSSCHECKMIC = 48, MONITOR_ANS_GSSCHECKMIC = 49,
57
	MONITOR_REQ_GSSCHECKMIC = 48, MONITOR_ANS_GSSCHECKMIC = 49,
58
	MONITOR_REQ_TERM = 50,
58
	MONITOR_REQ_TERM = 50,
59
	MONITOR_REQ_READUSERU2FKEY = 52, MONITOR_ANS_READUSERU2FKEY = 53,
60
	MONITOR_REQ_VERIFYU2FUSER = 54, MONITOR_ANS_VERIFYU2FUSER = 55,
59
61
60
	MONITOR_REQ_PAM_START = 100,
62
	MONITOR_REQ_PAM_START = 100,
61
	MONITOR_REQ_PAM_ACCOUNT = 102, MONITOR_ANS_PAM_ACCOUNT = 103,
63
	MONITOR_REQ_PAM_ACCOUNT = 102, MONITOR_ANS_PAM_ACCOUNT = 103,
(-)a/monitor_wrap.c (+59 lines)
Lines 1300-1302 mm_ssh_gssapi_userok(char *user) Link Here
1300
}
1300
}
1301
#endif /* GSSAPI */
1301
#endif /* GSSAPI */
1302
1302
1303
#ifdef U2F
1304
Key *
1305
mm_read_user_u2f_key(struct passwd *pw, u_int key_idx)
1306
{
1307
	Buffer m;
1308
	Key *key = NULL;
1309
	u_char *blob;
1310
	u_int blen;
1311
	u_int is_null;
1312
1313
	debug3("%s entering", __func__);
1314
1315
	buffer_init(&m);
1316
	buffer_put_int(&m, key_idx);
1317
1318
	mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_READUSERU2FKEY, &m);
1319
	mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_READUSERU2FKEY, &m);
1320
1321
	is_null = buffer_get_int(&m);
1322
	if (is_null == 0) {
1323
		blob = buffer_get_string(&m, &blen);
1324
		if ((key = key_from_blob(blob, blen)) == NULL)
1325
			fatal("%s: key_from_blob failed", __func__);
1326
1327
		free(blob);
1328
	}
1329
1330
	buffer_free(&m);
1331
	return key;
1332
}
1333
1334
int
1335
mm_verify_u2f_user(Key *key, u_char * dgst, size_t dgstlen, u_char * sig, size_t siglen)
1336
{
1337
	int authenticated = 0;
1338
	Buffer m;
1339
	u_char *blob;
1340
	u_int blen;
1341
1342
	debug3("%s entering", __func__);
1343
1344
	if (key_to_blob(key, &blob, &blen) == 0)
1345
		fatal("%s: key_to_blob failed", __func__);
1346
	buffer_init(&m);
1347
	buffer_put_string(&m, blob, blen);
1348
	free(blob);
1349
1350
	buffer_put_string(&m, dgst, dgstlen);
1351
	buffer_put_string(&m, sig, siglen);
1352
1353
	mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_VERIFYU2FUSER, &m);
1354
	mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_VERIFYU2FUSER, &m);
1355
1356
	authenticated = buffer_get_int(&m);
1357
	buffer_free(&m);
1358
1359
	return authenticated;
1360
}
1361
#endif /* U2F */
(-)a/monitor_wrap.h (+4 lines)
Lines 53-58 int mm_key_verify(Key *, u_char *, u_int, u_char *, u_int); Link Here
53
int mm_auth_rsa_key_allowed(struct passwd *, BIGNUM *, Key **);
53
int mm_auth_rsa_key_allowed(struct passwd *, BIGNUM *, Key **);
54
int mm_auth_rsa_verify_response(Key *, BIGNUM *, u_char *);
54
int mm_auth_rsa_verify_response(Key *, BIGNUM *, u_char *);
55
BIGNUM *mm_auth_rsa_generate_challenge(Key *);
55
BIGNUM *mm_auth_rsa_generate_challenge(Key *);
56
#ifdef U2F
57
Key *mm_read_user_u2f_key(struct passwd *, u_int);
58
int mm_verify_u2f_user(Key *, u_char *, size_t, u_char *, size_t);
59
#endif
56
60
57
#ifdef GSSAPI
61
#ifdef GSSAPI
58
OM_uint32 mm_ssh_gssapi_server_ctx(Gssctxt **, gss_OID);
62
OM_uint32 mm_ssh_gssapi_server_ctx(Gssctxt **, gss_OID);
(-)a/readconf.h (+1 lines)
Lines 46-51 typedef struct { Link Here
46
					/* Try S/Key or TIS, authentication. */
46
					/* Try S/Key or TIS, authentication. */
47
	int     gss_authentication;	/* Try GSS authentication */
47
	int     gss_authentication;	/* Try GSS authentication */
48
	int     gss_deleg_creds;	/* Delegate GSS credentials */
48
	int     gss_deleg_creds;	/* Delegate GSS credentials */
49
	int     u2f_authentication;
49
	int     password_authentication;	/* Try password
50
	int     password_authentication;	/* Try password
50
						 * authentication. */
51
						 * authentication. */
51
	int     kbd_interactive_authentication; /* Try keyboard-interactive auth. */
52
	int     kbd_interactive_authentication; /* Try keyboard-interactive auth. */
(-)a/servconf.c (+17 lines)
Lines 109-114 initialize_server_options(ServerOptions *options) Link Here
109
	options->kerberos_ticket_cleanup = -1;
109
	options->kerberos_ticket_cleanup = -1;
110
	options->kerberos_get_afs_token = -1;
110
	options->kerberos_get_afs_token = -1;
111
	options->gss_authentication=-1;
111
	options->gss_authentication=-1;
112
	options->u2f_authentication = -1;
112
	options->gss_cleanup_creds = -1;
113
	options->gss_cleanup_creds = -1;
113
	options->password_authentication = -1;
114
	options->password_authentication = -1;
114
	options->kbd_interactive_authentication = -1;
115
	options->kbd_interactive_authentication = -1;
Lines 250-255 fill_default_server_options(ServerOptions *options) Link Here
250
		options->kerberos_get_afs_token = 0;
251
		options->kerberos_get_afs_token = 0;
251
	if (options->gss_authentication == -1)
252
	if (options->gss_authentication == -1)
252
		options->gss_authentication = 0;
253
		options->gss_authentication = 0;
254
	if (options->u2f_authentication == -1)
255
		options->u2f_authentication = 1;
253
	if (options->gss_cleanup_creds == -1)
256
	if (options->gss_cleanup_creds == -1)
254
		options->gss_cleanup_creds = 1;
257
		options->gss_cleanup_creds = 1;
255
	if (options->password_authentication == -1)
258
	if (options->password_authentication == -1)
Lines 353-358 typedef enum { Link Here
353
	sHostbasedUsesNameFromPacketOnly, sClientAliveInterval,
356
	sHostbasedUsesNameFromPacketOnly, sClientAliveInterval,
354
	sClientAliveCountMax, sAuthorizedKeysFile,
357
	sClientAliveCountMax, sAuthorizedKeysFile,
355
	sGssAuthentication, sGssCleanupCreds, sAcceptEnv, sPermitTunnel,
358
	sGssAuthentication, sGssCleanupCreds, sAcceptEnv, sPermitTunnel,
359
	sU2FAuthentication,
356
	sMatch, sPermitOpen, sForceCommand, sChrootDirectory,
360
	sMatch, sPermitOpen, sForceCommand, sChrootDirectory,
357
	sUsePrivilegeSeparation, sAllowAgentForwarding,
361
	sUsePrivilegeSeparation, sAllowAgentForwarding,
358
	sHostCertificate,
362
	sHostCertificate,
Lines 425-430 static struct { Link Here
425
	{ "gssapiauthentication", sUnsupported, SSHCFG_ALL },
429
	{ "gssapiauthentication", sUnsupported, SSHCFG_ALL },
426
	{ "gssapicleanupcredentials", sUnsupported, SSHCFG_GLOBAL },
430
	{ "gssapicleanupcredentials", sUnsupported, SSHCFG_GLOBAL },
427
#endif
431
#endif
432
#ifdef U2F
433
	{ "u2fauthentication", sU2FAuthentication, SSHCFG_ALL },
434
#else
435
	{ "u2fauthentication", sUnsupported, SSHCFG_ALL },
436
#endif
428
	{ "passwordauthentication", sPasswordAuthentication, SSHCFG_ALL },
437
	{ "passwordauthentication", sPasswordAuthentication, SSHCFG_ALL },
429
	{ "kbdinteractiveauthentication", sKbdInteractiveAuthentication, SSHCFG_ALL },
438
	{ "kbdinteractiveauthentication", sKbdInteractiveAuthentication, SSHCFG_ALL },
430
	{ "challengeresponseauthentication", sChallengeResponseAuthentication, SSHCFG_GLOBAL },
439
	{ "challengeresponseauthentication", sChallengeResponseAuthentication, SSHCFG_GLOBAL },
Lines 1108-1113 process_server_config_line(ServerOptions *options, char *line, Link Here
1108
		intptr = &options->gss_cleanup_creds;
1117
		intptr = &options->gss_cleanup_creds;
1109
		goto parse_flag;
1118
		goto parse_flag;
1110
1119
1120
	case sU2FAuthentication:
1121
		intptr = &options->u2f_authentication;
1122
		goto parse_flag;
1123
1111
	case sPasswordAuthentication:
1124
	case sPasswordAuthentication:
1112
		intptr = &options->password_authentication;
1125
		intptr = &options->password_authentication;
1113
		goto parse_flag;
1126
		goto parse_flag;
Lines 1792-1797 copy_set_server_options(ServerOptions *dst, ServerOptions *src, int preauth) Link Here
1792
1805
1793
	M_CP_INTOPT(password_authentication);
1806
	M_CP_INTOPT(password_authentication);
1794
	M_CP_INTOPT(gss_authentication);
1807
	M_CP_INTOPT(gss_authentication);
1808
	M_CP_INTOPT(u2f_authentication);
1795
	M_CP_INTOPT(rsa_authentication);
1809
	M_CP_INTOPT(rsa_authentication);
1796
	M_CP_INTOPT(pubkey_authentication);
1810
	M_CP_INTOPT(pubkey_authentication);
1797
	M_CP_INTOPT(kerberos_authentication);
1811
	M_CP_INTOPT(kerberos_authentication);
Lines 2044-2049 dump_config(ServerOptions *o) Link Here
2044
	dump_cfg_fmtint(sGssAuthentication, o->gss_authentication);
2058
	dump_cfg_fmtint(sGssAuthentication, o->gss_authentication);
2045
	dump_cfg_fmtint(sGssCleanupCreds, o->gss_cleanup_creds);
2059
	dump_cfg_fmtint(sGssCleanupCreds, o->gss_cleanup_creds);
2046
#endif
2060
#endif
2061
#ifdef U2F
2062
	dump_cfg_fmtint(sU2FAuthentication, o->u2f_authentication);
2063
#endif
2047
	dump_cfg_fmtint(sPasswordAuthentication, o->password_authentication);
2064
	dump_cfg_fmtint(sPasswordAuthentication, o->password_authentication);
2048
	dump_cfg_fmtint(sKbdInteractiveAuthentication,
2065
	dump_cfg_fmtint(sKbdInteractiveAuthentication,
2049
	    o->kbd_interactive_authentication);
2066
	    o->kbd_interactive_authentication);
(-)a/servconf.h (+1 lines)
Lines 118-123 typedef struct { Link Here
118
						 * authentication. */
118
						 * authentication. */
119
	int     kbd_interactive_authentication;	/* If true, permit */
119
	int     kbd_interactive_authentication;	/* If true, permit */
120
	int     challenge_response_authentication;
120
	int     challenge_response_authentication;
121
	int     u2f_authentication;
121
	int     permit_empty_passwd;	/* If false, do not permit empty
122
	int     permit_empty_passwd;	/* If false, do not permit empty
122
					 * passwords. */
123
					 * passwords. */
123
	int     permit_user_env;	/* If true, read ~/.ssh/environment */
124
	int     permit_user_env;	/* If true, read ~/.ssh/environment */
(-)a/ssh.c (+10 lines)
Lines 78-83 Link Here
78
#include "openbsd-compat/openssl-compat.h"
78
#include "openbsd-compat/openssl-compat.h"
79
#include "openbsd-compat/sys-queue.h"
79
#include "openbsd-compat/sys-queue.h"
80
80
81
#ifdef U2F
82
#include <u2f-host.h>
83
#endif
84
81
#include "xmalloc.h"
85
#include "xmalloc.h"
82
#include "ssh.h"
86
#include "ssh.h"
83
#include "ssh1.h"
87
#include "ssh1.h"
Lines 843-848 main(int ac, char **av) Link Here
843
#ifdef WITH_OPENSSL
847
#ifdef WITH_OPENSSL
844
	OpenSSL_add_all_algorithms();
848
	OpenSSL_add_all_algorithms();
845
	ERR_load_crypto_strings();
849
	ERR_load_crypto_strings();
850
// TODO: SSL_load_error_strings(), requires -lssl i think?
851
#endif
852
853
#ifdef U2F
854
	if (u2fh_global_init(0) != U2FH_OK)
855
		fatal("u2fh_global_init() failed");
846
#endif
856
#endif
847
857
848
	/* Initialize the command to execute on remote host. */
858
	/* Initialize the command to execute on remote host. */
(-)a/sshconnect2.c (+160 lines)
Lines 30-35 Link Here
30
#include <sys/socket.h>
30
#include <sys/socket.h>
31
#include <sys/wait.h>
31
#include <sys/wait.h>
32
#include <sys/stat.h>
32
#include <sys/stat.h>
33
#include <time.h>
33
34
34
#include <errno.h>
35
#include <errno.h>
35
#include <fcntl.h>
36
#include <fcntl.h>
Lines 44-49 Link Here
44
#include <vis.h>
45
#include <vis.h>
45
#endif
46
#endif
46
47
48
#ifdef U2F
49
#include <u2f-host.h>
50
#endif
51
47
#include "openbsd-compat/sys-queue.h"
52
#include "openbsd-compat/sys-queue.h"
48
53
49
#include "xmalloc.h"
54
#include "xmalloc.h"
Lines 308-313 void input_gssapi_error(int, u_int32_t, void *); Link Here
308
void	input_gssapi_errtok(int, u_int32_t, void *);
313
void	input_gssapi_errtok(int, u_int32_t, void *);
309
#endif
314
#endif
310
315
316
#ifdef U2F
317
int userauth_u2f(Authctxt *authctxt);
318
void input_userauth_u2f_req(int type, u_int32_t seq, void *ctxt);
319
void input_userauth_u2f_register(int type, u_int32_t seq, void *ctxt);
320
void input_userauth_u2f_register_response(int type, u_int32_t seq, void *ctxt);
321
#endif
322
311
void	userauth(Authctxt *, char *);
323
void	userauth(Authctxt *, char *);
312
324
313
static int sign_and_send_pubkey(Authctxt *, Identity *);
325
static int sign_and_send_pubkey(Authctxt *, Identity *);
Lines 327-332 Authmethod authmethods[] = { Link Here
327
		&options.gss_authentication,
339
		&options.gss_authentication,
328
		NULL},
340
		NULL},
329
#endif
341
#endif
342
#ifdef U2F
343
    {"u2f",
344
        userauth_u2f,
345
        NULL,
346
        &options.u2f_authentication,
347
        NULL},
348
#endif
330
	{"hostbased",
349
	{"hostbased",
331
		userauth_hostbased,
350
		userauth_hostbased,
332
		NULL,
351
		NULL,
Lines 838-843 input_gssapi_error(int type, u_int32_t plen, void *ctxt) Link Here
838
}
857
}
839
#endif /* GSSAPI */
858
#endif /* GSSAPI */
840
859
860
#ifdef U2F
861
int
862
userauth_u2f(Authctxt *authctxt)
863
{
864
    // first step: we dont send anything, but install a custom dispatcher.
865
    debug("sshconnect2:userauth_u2f");
866
867
	// TODO: is this the right way to disable the method after registration?
868
    if (authctxt->info_req_seen) {
869
		dispatch_set(SSH2_MSG_USERAUTH_INFO_REQUEST, NULL);
870
		return 0;
871
	}
872
873
    packet_start(SSH2_MSG_USERAUTH_REQUEST);
874
    packet_put_cstring(authctxt->server_user);
875
    packet_put_cstring(authctxt->service);
876
    packet_put_cstring(authctxt->method->name);
877
	// TODO: shared constants
878
	// TODO: how can we make the user chose between registration and authentication?
879
	packet_put_int(1);
880
    packet_send();
881
882
    dispatch_set(SSH2_MSG_USERAUTH_INFO_REQUEST, &input_userauth_u2f_req);
883
    //dispatch_set(SSH2_MSG_USERAUTH_INFO_REQUEST, &input_userauth_u2f_register);
884
    return 1;
885
}
886
887
static void
888
wait_for_u2f_devices(u2fh_devs *devs)
889
{
890
	time_t looking;
891
	int attempts = 0;
892
	u2fh_rc rc;
893
894
	// The U2F implementation considerations recommend 3 seconds as the time a
895
	// client implementation should grant for security keys to respond. We wait
896
	// 3 times that for the user to insert a security key (and it being
897
	// detected).
898
	looking = time(NULL);
899
	do {
900
		if ((rc = u2fh_devs_discover(devs, NULL)) != U2FH_OK && attempts++ == 0)
901
			error("Please insert and touch your security key");
902
		if (rc != U2FH_OK)
903
			usleep(50);
904
	} while (rc != U2FH_OK && (time(NULL) - looking) <= 9);
905
	if (rc != U2FH_OK)
906
		fatal("No U2F devices found (%s). Did you plug in your security key?",
907
				u2fh_strerror(rc));
908
909
	if (attempts == 0)
910
		error("Please touch your security key");
911
}
912
913
void
914
input_userauth_u2f_register(int type, u_int32_t seq, void *ctxt)
915
{
916
	Authctxt *authctxt = ctxt;
917
	char *challenge, *response;
918
	u2fh_devs *devs = NULL;
919
	u2fh_rc rc;
920
	// TODO: is it okay to use this as origin? or rather the host fingerprint?
921
	const char *origin = options.host_key_alias ?  options.host_key_alias :
922
		authctxt->host;
923
924
	if (authctxt == NULL)
925
		fatal("input_userauth_u2f_register: no authentication context");
926
927
	authctxt->info_req_seen = 1;
928
929
	challenge = packet_get_string(NULL);
930
	packet_check_eom();
931
932
	if ((rc = u2fh_devs_init(&devs)) != U2FH_OK)
933
		fatal("u2fh_devs_init() failed: %s", u2fh_strerror(rc));
934
935
	wait_for_u2f_devices(devs);
936
937
	if ((rc = u2fh_register(devs, challenge, origin, &response, U2FH_REQUEST_USER_PRESENCE)) != U2FH_OK)
938
		fatal("u2fh_register() failed: %s", u2fh_strerror(rc));
939
940
	u2fh_devs_done(devs);
941
942
	packet_start(SSH2_MSG_USERAUTH_INFO_RESPONSE);
943
	packet_put_cstring(response);
944
	packet_send();
945
946
	free(response);
947
	dispatch_set(SSH2_MSG_USERAUTH_INFO_REQUEST, NULL);
948
	dispatch_set(SSH2_MSG_USERAUTH_INFO_REQUEST, &input_userauth_u2f_register_response);
949
}
950
951
void
952
input_userauth_u2f_register_response(int type, u_int32_t seq, void *ctxt)
953
{
954
	char *response = packet_get_string(NULL);
955
	// TODO: print
956
	error("r = %s", response);
957
}
958
959
void
960
input_userauth_u2f_req(int type, u_int32_t seq, void *ctxt)
961
{
962
	Authctxt *authctxt = ctxt;
963
	char *challenge, *response;
964
	u_int num_prompts, i;
965
	int echo = 0;
966
	u2fh_devs *devs = NULL;
967
	u2fh_rc rc;
968
	time_t looking;
969
	const char *origin = options.host_key_alias ?  options.host_key_alias :
970
		authctxt->host;
971
972
	if (authctxt == NULL)
973
		fatal("input_userauth_u2f_req: no authentication context");
974
975
	authctxt->info_req_seen = 1;
976
977
	challenge = packet_get_string(NULL);
978
	packet_check_eom();
979
980
	if ((rc = u2fh_devs_init(&devs)) != U2FH_OK)
981
		fatal("u2fh_devs_init() failed: %s", u2fh_strerror(rc));
982
983
	wait_for_u2f_devices(devs);
984
985
	// TODO: refactor with input_userauth_u2f_register(), the following line is the only one that is different :)
986
	if ((rc = u2fh_authenticate(devs, challenge, origin, &response, U2FH_REQUEST_USER_PRESENCE)) != U2FH_OK)
987
		fatal("u2fh_authenticate() failed: %s", u2fh_strerror(rc));
988
989
	u2fh_devs_done(devs);
990
991
	packet_start(SSH2_MSG_USERAUTH_INFO_RESPONSE);
992
	packet_put_cstring(response);
993
	packet_send();
994
995
	free(response);
996
	dispatch_set(SSH2_MSG_USERAUTH_INFO_REQUEST, NULL);
997
}
998
999
#endif /* U2F */
1000
841
int
1001
int
842
userauth_none(Authctxt *authctxt)
1002
userauth_none(Authctxt *authctxt)
843
{
1003
{
(-)a/sshd.c (+1 lines)
Lines 1562-1567 main(int ac, char **av) Link Here
1562
1562
1563
#ifdef WITH_OPENSSL
1563
#ifdef WITH_OPENSSL
1564
	OpenSSL_add_all_algorithms();
1564
	OpenSSL_add_all_algorithms();
1565
// TODO: SSL_load_error_strings(), requires -lssl i think?
1565
#endif
1566
#endif
1566
1567
1567
	/* If requested, redirect the logs to the specified logfile. */
1568
	/* If requested, redirect the logs to the specified logfile. */
(-)a/sshkey.c (+85 lines)
Lines 52-57 Link Here
52
#include "digest.h"
52
#include "digest.h"
53
#define SSHKEY_INTERNAL
53
#define SSHKEY_INTERNAL
54
#include "sshkey.h"
54
#include "sshkey.h"
55
#include "key.h"
56
#include "hostfile.h"
57
#include "auth.h"
55
58
56
/* openssh private key file format */
59
/* openssh private key file format */
57
#define MARK_BEGIN		"-----BEGIN OPENSSH PRIVATE KEY-----\n"
60
#define MARK_BEGIN		"-----BEGIN OPENSSH PRIVATE KEY-----\n"
Lines 110-115 static const struct keytype keytypes[] = { Link Here
110
	{ "ssh-dss-cert-v00@openssh.com", "DSA-CERT-V00",
113
	{ "ssh-dss-cert-v00@openssh.com", "DSA-CERT-V00",
111
	    KEY_DSA_CERT_V00, 0, 1 },
114
	    KEY_DSA_CERT_V00, 0, 1 },
112
#endif /* WITH_OPENSSL */
115
#endif /* WITH_OPENSSL */
116
	{ "ssh-u2f", "U2F", KEY_U2F, 0, 0 },
113
	{ NULL, NULL, -1, -1, 0 }
117
	{ NULL, NULL, -1, -1, 0 }
114
};
118
};
115
119
Lines 508-513 sshkey_new(int type) Link Here
508
		break;
512
		break;
509
	case KEY_UNSPEC:
513
	case KEY_UNSPEC:
510
		break;
514
		break;
515
	case KEY_U2F:
516
		debug("key_new");
517
		break;
511
	default:
518
	default:
512
		free(k);
519
		free(k);
513
		return NULL;
520
		return NULL;
Lines 790-795 to_blob_buf(const struct sshkey *key, struct sshbuf *b, int force_plain) Link Here
790
		    key->ed25519_pk, ED25519_PK_SZ)) != 0)
797
		    key->ed25519_pk, ED25519_PK_SZ)) != 0)
791
			return ret;
798
			return ret;
792
		break;
799
		break;
800
#ifdef U2F
801
	case KEY_U2F:
802
		if (key->u2f_pubkey == NULL)
803
			return SSH_ERR_INVALID_ARGUMENT;
804
		if ((ret = sshbuf_put_cstring(b, typename)) != 0 ||
805
		    (ret = sshbuf_put_string(b, key->u2f_pubkey, U2F_PUBKEY_LEN)) != 0 ||
806
		    (ret = sshbuf_put_string(b,
807
				key->u2f_key_handle, key->u2f_key_handle_len)) != 0)
808
			return ret;
809
		break;
810
#endif
793
	default:
811
	default:
794
		return SSH_ERR_KEY_TYPE_UNKNOWN;
812
		return SSH_ERR_KEY_TYPE_UNKNOWN;
795
	}
813
	}
Lines 1188-1193 sshkey_read(struct sshkey *ret, char **cpp) Link Here
1188
1206
1189
	cp = *cpp;
1207
	cp = *cpp;
1190
1208
1209
	debug("sshkey_read");
1210
	debug("ret = %p", ret);
1211
	debug("sshkey_read, ret->type = %d", ret->type);
1212
1191
	switch (ret->type) {
1213
	switch (ret->type) {
1192
	case KEY_RSA1:
1214
	case KEY_RSA1:
1193
#ifdef WITH_SSH1
1215
#ifdef WITH_SSH1
Lines 1208-1213 sshkey_read(struct sshkey *ret, char **cpp) Link Here
1208
		retval = 0;
1230
		retval = 0;
1209
#endif /* WITH_SSH1 */
1231
#endif /* WITH_SSH1 */
1210
		break;
1232
		break;
1233
	case KEY_U2F:
1234
		space = strchr(cp, ' ');
1235
		if (space == NULL)
1236
			return SSH_ERR_INVALID_FORMAT;
1237
		*space = '\0';
1238
		type = sshkey_type_from_name(cp);
1239
		if (type == KEY_UNSPEC)
1240
			return SSH_ERR_INVALID_FORMAT;
1241
		cp = space+1;
1242
		if (*cp == '\0')
1243
			return SSH_ERR_INVALID_FORMAT;
1244
		if (ret->type == KEY_UNSPEC) {
1245
			ret->type = type;
1246
		} else if (ret->type != type)
1247
			return SSH_ERR_KEY_TYPE_MISMATCH;
1248
		cp = space+1;
1249
		/* trim comment */
1250
		space = strchr(cp, ' ');
1251
		if (space)
1252
			*space = '\0';
1253
		blob = sshbuf_new();
1254
		if ((r = sshbuf_b64tod(blob, cp)) != 0) {
1255
			sshbuf_free(blob);
1256
			return r;
1257
		}
1258
		// TODO: why do we _need_ to use malloc here? xmalloc gives memory that crashes!
1259
		ret->u2f_pubkey = malloc(U2F_PUBKEY_LEN);
1260
		memcpy(ret->u2f_pubkey, sshbuf_ptr(blob), U2F_PUBKEY_LEN);
1261
		ret->u2f_key_handle_len = sshbuf_len(blob) - U2F_PUBKEY_LEN;
1262
		ret->u2f_key_handle = malloc(ret->u2f_key_handle_len);
1263
		memcpy(ret->u2f_key_handle, sshbuf_ptr(blob) + U2F_PUBKEY_LEN, ret->u2f_key_handle_len);
1264
		sshbuf_free(blob);
1265
		retval = (r >= 0) ? 0 : 1;
1266
		break;
1211
	case KEY_UNSPEC:
1267
	case KEY_UNSPEC:
1212
	case KEY_RSA:
1268
	case KEY_RSA:
1213
	case KEY_DSA:
1269
	case KEY_DSA:
Lines 1329-1334 sshkey_read(struct sshkey *ret, char **cpp) Link Here
1329
	default:
1385
	default:
1330
		return SSH_ERR_INVALID_ARGUMENT;
1386
		return SSH_ERR_INVALID_ARGUMENT;
1331
	}
1387
	}
1388
	debug("retval = %d", retval);
1332
	return retval;
1389
	return retval;
1333
}
1390
}
1334
1391
Lines 1909-1914 sshkey_from_blob_internal(const u_char *blob, size_t blen, Link Here
1909
#if defined(WITH_OPENSSL) && defined(OPENSSL_HAS_ECC)
1966
#if defined(WITH_OPENSSL) && defined(OPENSSL_HAS_ECC)
1910
	EC_POINT *q = NULL;
1967
	EC_POINT *q = NULL;
1911
#endif /* WITH_OPENSSL && OPENSSL_HAS_ECC */
1968
#endif /* WITH_OPENSSL && OPENSSL_HAS_ECC */
1969
#ifdef U2F
1970
	u_char *khandle = NULL;
1971
#endif
1912
1972
1913
#ifdef DEBUG_PK /* XXX */
1973
#ifdef DEBUG_PK /* XXX */
1914
	dump_base64(stderr, blob, blen);
1974
	dump_base64(stderr, blob, blen);
Lines 2046-2051 sshkey_from_blob_internal(const u_char *blob, size_t blen, Link Here
2046
		key->ed25519_pk = pk;
2106
		key->ed25519_pk = pk;
2047
		pk = NULL;
2107
		pk = NULL;
2048
		break;
2108
		break;
2109
#ifdef U2F
2110
	case KEY_U2F:
2111
		if ((ret = sshbuf_get_string(b, &pk, &len)) != 0)
2112
			goto out;
2113
		if (len != U2F_PUBKEY_LEN) {
2114
			ret = SSH_ERR_INVALID_FORMAT;
2115
			goto out;
2116
		}
2117
		if ((ret = sshbuf_get_string(b, &khandle, &len)) != 0)
2118
			goto out;
2119
		if ((key = sshkey_new(type)) == NULL) {
2120
			ret = SSH_ERR_ALLOC_FAIL;
2121
			goto out;
2122
		}
2123
		key->u2f_pubkey = pk;
2124
		key->u2f_key_handle_len = len;
2125
		key->u2f_key_handle = khandle;
2126
		pk = NULL;
2127
		khandle = NULL;
2128
		ret = SSH_ERR_ALLOC_FAIL;
2129
		break;
2130
#endif
2049
	case KEY_UNSPEC:
2131
	case KEY_UNSPEC:
2050
		if ((key = sshkey_new(type)) == NULL) {
2132
		if ((key = sshkey_new(type)) == NULL) {
2051
			ret = SSH_ERR_ALLOC_FAIL;
2133
			ret = SSH_ERR_ALLOC_FAIL;
Lines 2079-2084 sshkey_from_blob_internal(const u_char *blob, size_t blen, Link Here
2079
	if (q != NULL)
2161
	if (q != NULL)
2080
		EC_POINT_free(q);
2162
		EC_POINT_free(q);
2081
#endif /* WITH_OPENSSL && OPENSSL_HAS_ECC */
2163
#endif /* WITH_OPENSSL && OPENSSL_HAS_ECC */
2164
#ifdef U2F
2165
	free(khandle);
2166
#endif
2082
	return ret;
2167
	return ret;
2083
}
2168
}
2084
2169
(-)a/sshkey.h (-1 / +6 lines)
Lines 64-69 enum sshkey_types { Link Here
64
	KEY_ED25519_CERT,
64
	KEY_ED25519_CERT,
65
	KEY_RSA_CERT_V00,
65
	KEY_RSA_CERT_V00,
66
	KEY_DSA_CERT_V00,
66
	KEY_DSA_CERT_V00,
67
	KEY_U2F,
67
	KEY_UNSPEC
68
	KEY_UNSPEC
68
};
69
};
69
70
Lines 110-115 struct sshkey { Link Here
110
	u_char	*ed25519_sk;
111
	u_char	*ed25519_sk;
111
	u_char	*ed25519_pk;
112
	u_char	*ed25519_pk;
112
	struct sshkey_cert *cert;
113
	struct sshkey_cert *cert;
114
#ifdef U2F
115
	u_char *u2f_pubkey;
116
	u_int   u2f_key_handle_len;
117
	u_char *u2f_key_handle;
118
#endif
113
};
119
};
114
120
115
#define	ED25519_SK_SZ	crypto_sign_ed25519_SECRETKEYBYTES
121
#define	ED25519_SK_SZ	crypto_sign_ed25519_SECRETKEYBYTES
116
- 

Return to bug 2319