GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/ssh/lib/../authfd.c Lines: 7 222 3.2 %
Date: 2017-11-07 Branches: 2 210 1.0 %

Line Branch Exec Source
1
/* $OpenBSD: authfd.c,v 1.105 2017/07/01 13:50:45 djm Exp $ */
2
/*
3
 * Author: Tatu Ylonen <ylo@cs.hut.fi>
4
 * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
5
 *                    All rights reserved
6
 * Functions for connecting the local authentication agent.
7
 *
8
 * As far as I am concerned, the code I have written for this software
9
 * can be used freely for any purpose.  Any derived versions of this
10
 * software must be clearly marked as such, and if the derived work is
11
 * incompatible with the protocol description in the RFC file, it must be
12
 * called by a name other than "ssh" or "Secure Shell".
13
 *
14
 * SSH2 implementation,
15
 * Copyright (c) 2000 Markus Friedl.  All rights reserved.
16
 *
17
 * Redistribution and use in source and binary forms, with or without
18
 * modification, are permitted provided that the following conditions
19
 * are met:
20
 * 1. Redistributions of source code must retain the above copyright
21
 *    notice, this list of conditions and the following disclaimer.
22
 * 2. Redistributions in binary form must reproduce the above copyright
23
 *    notice, this list of conditions and the following disclaimer in the
24
 *    documentation and/or other materials provided with the distribution.
25
 *
26
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
27
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
28
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
29
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
30
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
31
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
32
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
33
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
34
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
35
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36
 */
37
38
39
#include <sys/types.h>
40
#include <sys/un.h>
41
#include <sys/socket.h>
42
43
#include <fcntl.h>
44
#include <stdlib.h>
45
#include <signal.h>
46
#include <string.h>
47
#include <unistd.h>
48
#include <errno.h>
49
50
#include "xmalloc.h"
51
#include "ssh.h"
52
#include "sshbuf.h"
53
#include "sshkey.h"
54
#include "authfd.h"
55
#include "cipher.h"
56
#include "compat.h"
57
#include "log.h"
58
#include "atomicio.h"
59
#include "misc.h"
60
#include "ssherr.h"
61
62
#define MAX_AGENT_IDENTITIES	2048		/* Max keys in agent reply */
63
#define MAX_AGENT_REPLY_LEN	(256 * 1024) 	/* Max bytes in agent reply */
64
65
/* macro to check for "agent failure" message */
66
#define agent_failed(x) \
67
    ((x == SSH_AGENT_FAILURE) || \
68
    (x == SSH_COM_AGENT2_FAILURE) || \
69
    (x == SSH2_AGENT_FAILURE))
70
71
/* Convert success/failure response from agent to a err.h status */
72
static int
73
decode_reply(u_char type)
74
{
75
	if (agent_failed(type))
76
		return SSH_ERR_AGENT_FAILURE;
77
	else if (type == SSH_AGENT_SUCCESS)
78
		return 0;
79
	else
80
		return SSH_ERR_INVALID_FORMAT;
81
}
82
83
/* Returns the number of the authentication fd, or -1 if there is none. */
84
int
85
ssh_get_authentication_socket(int *fdp)
86
{
87
	const char *authsocket;
88
	int sock, oerrno;
89
2
	struct sockaddr_un sunaddr;
90
91
1
	if (fdp != NULL)
92
1
		*fdp = -1;
93
94
1
	authsocket = getenv(SSH_AUTHSOCKET_ENV_NAME);
95
1
	if (!authsocket)
96
1
		return SSH_ERR_AGENT_NOT_PRESENT;
97
98
	memset(&sunaddr, 0, sizeof(sunaddr));
99
	sunaddr.sun_family = AF_UNIX;
100
	strlcpy(sunaddr.sun_path, authsocket, sizeof(sunaddr.sun_path));
101
102
	if ((sock = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
103
		return SSH_ERR_SYSTEM_ERROR;
104
105
	/* close on exec */
106
	if (fcntl(sock, F_SETFD, FD_CLOEXEC) == -1 ||
107
	    connect(sock, (struct sockaddr *)&sunaddr, sizeof(sunaddr)) < 0) {
108
		oerrno = errno;
109
		close(sock);
110
		errno = oerrno;
111
		return SSH_ERR_SYSTEM_ERROR;
112
	}
113
	if (fdp != NULL)
114
		*fdp = sock;
115
	else
116
		close(sock);
117
	return 0;
118
1
}
119
120
/* Communicate with agent: send request and read reply */
121
static int
122
ssh_request_reply(int sock, struct sshbuf *request, struct sshbuf *reply)
123
{
124
	int r;
125
	size_t l, len;
126
	char buf[1024];
127
128
	/* Get the length of the message, and format it in the buffer. */
129
	len = sshbuf_len(request);
130
	put_u32(buf, len);
131
132
	/* Send the length and then the packet to the agent. */
133
	if (atomicio(vwrite, sock, buf, 4) != 4 ||
134
	    atomicio(vwrite, sock, (u_char *)sshbuf_ptr(request),
135
	    sshbuf_len(request)) != sshbuf_len(request))
136
		return SSH_ERR_AGENT_COMMUNICATION;
137
	/*
138
	 * Wait for response from the agent.  First read the length of the
139
	 * response packet.
140
	 */
141
	if (atomicio(read, sock, buf, 4) != 4)
142
	    return SSH_ERR_AGENT_COMMUNICATION;
143
144
	/* Extract the length, and check it for sanity. */
145
	len = get_u32(buf);
146
	if (len > MAX_AGENT_REPLY_LEN)
147
		return SSH_ERR_INVALID_FORMAT;
148
149
	/* Read the rest of the response in to the buffer. */
150
	sshbuf_reset(reply);
151
	while (len > 0) {
152
		l = len;
153
		if (l > sizeof(buf))
154
			l = sizeof(buf);
155
		if (atomicio(read, sock, buf, l) != l)
156
			return SSH_ERR_AGENT_COMMUNICATION;
157
		if ((r = sshbuf_put(reply, buf, l)) != 0)
158
			return r;
159
		len -= l;
160
	}
161
	return 0;
162
}
163
164
/*
165
 * Closes the agent socket if it should be closed (depends on how it was
166
 * obtained).  The argument must have been returned by
167
 * ssh_get_authentication_socket().
168
 */
169
void
170
ssh_close_authentication_socket(int sock)
171
{
172
	if (getenv(SSH_AUTHSOCKET_ENV_NAME))
173
		close(sock);
174
}
175
176
/* Lock/unlock agent */
177
int
178
ssh_lock_agent(int sock, int lock, const char *password)
179
{
180
	int r;
181
	u_char type = lock ? SSH_AGENTC_LOCK : SSH_AGENTC_UNLOCK;
182
	struct sshbuf *msg;
183
184
	if ((msg = sshbuf_new()) == NULL)
185
		return SSH_ERR_ALLOC_FAIL;
186
	if ((r = sshbuf_put_u8(msg, type)) != 0 ||
187
	    (r = sshbuf_put_cstring(msg, password)) != 0)
188
		goto out;
189
	if ((r = ssh_request_reply(sock, msg, msg)) != 0)
190
		goto out;
191
	if ((r = sshbuf_get_u8(msg, &type)) != 0)
192
		goto out;
193
	r = decode_reply(type);
194
 out:
195
	sshbuf_free(msg);
196
	return r;
197
}
198
199
200
static int
201
deserialise_identity2(struct sshbuf *ids, struct sshkey **keyp, char **commentp)
202
{
203
	int r;
204
	char *comment = NULL;
205
	const u_char *blob;
206
	size_t blen;
207
208
	if ((r = sshbuf_get_string_direct(ids, &blob, &blen)) != 0 ||
209
	    (r = sshbuf_get_cstring(ids, &comment, NULL)) != 0)
210
		goto out;
211
	if ((r = sshkey_from_blob(blob, blen, keyp)) != 0)
212
		goto out;
213
	if (commentp != NULL) {
214
		*commentp = comment;
215
		comment = NULL;
216
	}
217
	r = 0;
218
 out:
219
	free(comment);
220
	return r;
221
}
222
223
/*
224
 * Fetch list of identities held by the agent.
225
 */
226
int
227
ssh_fetch_identitylist(int sock, struct ssh_identitylist **idlp)
228
{
229
	u_char type;
230
	u_int32_t num, i;
231
	struct sshbuf *msg;
232
	struct ssh_identitylist *idl = NULL;
233
	int r;
234
235
	/*
236
	 * Send a message to the agent requesting for a list of the
237
	 * identities it can represent.
238
	 */
239
	if ((msg = sshbuf_new()) == NULL)
240
		return SSH_ERR_ALLOC_FAIL;
241
	if ((r = sshbuf_put_u8(msg, SSH2_AGENTC_REQUEST_IDENTITIES)) != 0)
242
		goto out;
243
244
	if ((r = ssh_request_reply(sock, msg, msg)) != 0)
245
		goto out;
246
247
	/* Get message type, and verify that we got a proper answer. */
248
	if ((r = sshbuf_get_u8(msg, &type)) != 0)
249
		goto out;
250
	if (agent_failed(type)) {
251
		r = SSH_ERR_AGENT_FAILURE;
252
		goto out;
253
	} else if (type != SSH2_AGENT_IDENTITIES_ANSWER) {
254
		r = SSH_ERR_INVALID_FORMAT;
255
		goto out;
256
	}
257
258
	/* Get the number of entries in the response and check it for sanity. */
259
	if ((r = sshbuf_get_u32(msg, &num)) != 0)
260
		goto out;
261
	if (num > MAX_AGENT_IDENTITIES) {
262
		r = SSH_ERR_INVALID_FORMAT;
263
		goto out;
264
	}
265
	if (num == 0) {
266
		r = SSH_ERR_AGENT_NO_IDENTITIES;
267
		goto out;
268
	}
269
270
	/* Deserialise the response into a list of keys/comments */
271
	if ((idl = calloc(1, sizeof(*idl))) == NULL ||
272
	    (idl->keys = calloc(num, sizeof(*idl->keys))) == NULL ||
273
	    (idl->comments = calloc(num, sizeof(*idl->comments))) == NULL) {
274
		r = SSH_ERR_ALLOC_FAIL;
275
		goto out;
276
	}
277
	for (i = 0; i < num;) {
278
		if ((r = deserialise_identity2(msg, &(idl->keys[i]),
279
		    &(idl->comments[i]))) != 0) {
280
			if (r == SSH_ERR_KEY_TYPE_UNKNOWN) {
281
				/* Gracefully skip unknown key types */
282
				num--;
283
				continue;
284
			} else
285
				goto out;
286
		}
287
		i++;
288
	}
289
	idl->nkeys = num;
290
	*idlp = idl;
291
	idl = NULL;
292
	r = 0;
293
 out:
294
	sshbuf_free(msg);
295
	if (idl != NULL)
296
		ssh_free_identitylist(idl);
297
	return r;
298
}
299
300
void
301
ssh_free_identitylist(struct ssh_identitylist *idl)
302
{
303
	size_t i;
304
305
	if (idl == NULL)
306
		return;
307
	for (i = 0; i < idl->nkeys; i++) {
308
		if (idl->keys != NULL)
309
			sshkey_free(idl->keys[i]);
310
		if (idl->comments != NULL)
311
			free(idl->comments[i]);
312
	}
313
	free(idl);
314
}
315
316
/*
317
 * Sends a challenge (typically from a server via ssh(1)) to the agent,
318
 * and waits for a response from the agent.
319
 * Returns true (non-zero) if the agent gave the correct answer, zero
320
 * otherwise.
321
 */
322
323
324
/* encode signature algoritm in flag bits, so we can keep the msg format */
325
static u_int
326
agent_encode_alg(const struct sshkey *key, const char *alg)
327
{
328
	if (alg != NULL && key->type == KEY_RSA) {
329
		if (strcmp(alg, "rsa-sha2-256") == 0)
330
			return SSH_AGENT_RSA_SHA2_256;
331
		else if (strcmp(alg, "rsa-sha2-512") == 0)
332
			return SSH_AGENT_RSA_SHA2_512;
333
	}
334
	return 0;
335
}
336
337
/* ask agent to sign data, returns err.h code on error, 0 on success */
338
int
339
ssh_agent_sign(int sock, const struct sshkey *key,
340
    u_char **sigp, size_t *lenp,
341
    const u_char *data, size_t datalen, const char *alg, u_int compat)
342
{
343
	struct sshbuf *msg;
344
	u_char *blob = NULL, type;
345
	size_t blen = 0, len = 0;
346
	u_int flags = 0;
347
	int r = SSH_ERR_INTERNAL_ERROR;
348
349
	*sigp = NULL;
350
	*lenp = 0;
351
352
	if (datalen > SSH_KEY_MAX_SIGN_DATA_SIZE)
353
		return SSH_ERR_INVALID_ARGUMENT;
354
	if (compat & SSH_BUG_SIGBLOB)
355
		flags |= SSH_AGENT_OLD_SIGNATURE;
356
	if ((msg = sshbuf_new()) == NULL)
357
		return SSH_ERR_ALLOC_FAIL;
358
	if ((r = sshkey_to_blob(key, &blob, &blen)) != 0)
359
		goto out;
360
	flags |= agent_encode_alg(key, alg);
361
	if ((r = sshbuf_put_u8(msg, SSH2_AGENTC_SIGN_REQUEST)) != 0 ||
362
	    (r = sshbuf_put_string(msg, blob, blen)) != 0 ||
363
	    (r = sshbuf_put_string(msg, data, datalen)) != 0 ||
364
	    (r = sshbuf_put_u32(msg, flags)) != 0)
365
		goto out;
366
	if ((r = ssh_request_reply(sock, msg, msg)) != 0)
367
		goto out;
368
	if ((r = sshbuf_get_u8(msg, &type)) != 0)
369
		goto out;
370
	if (agent_failed(type)) {
371
		r = SSH_ERR_AGENT_FAILURE;
372
		goto out;
373
	} else if (type != SSH2_AGENT_SIGN_RESPONSE) {
374
		r = SSH_ERR_INVALID_FORMAT;
375
		goto out;
376
	}
377
	if ((r = sshbuf_get_string(msg, sigp, &len)) != 0)
378
		goto out;
379
	*lenp = len;
380
	r = 0;
381
 out:
382
	if (blob != NULL) {
383
		explicit_bzero(blob, blen);
384
		free(blob);
385
	}
386
	sshbuf_free(msg);
387
	return r;
388
}
389
390
/* Encode key for a message to the agent. */
391
392
393
static int
394
ssh_encode_identity_ssh2(struct sshbuf *b, struct sshkey *key,
395
    const char *comment)
396
{
397
	int r;
398
399
	if ((r = sshkey_private_serialize(key, b)) != 0 ||
400
	    (r = sshbuf_put_cstring(b, comment)) != 0)
401
		return r;
402
	return 0;
403
}
404
405
static int
406
encode_constraints(struct sshbuf *m, u_int life, u_int confirm)
407
{
408
	int r;
409
410
	if (life != 0) {
411
		if ((r = sshbuf_put_u8(m, SSH_AGENT_CONSTRAIN_LIFETIME)) != 0 ||
412
		    (r = sshbuf_put_u32(m, life)) != 0)
413
			goto out;
414
	}
415
	if (confirm != 0) {
416
		if ((r = sshbuf_put_u8(m, SSH_AGENT_CONSTRAIN_CONFIRM)) != 0)
417
			goto out;
418
	}
419
	r = 0;
420
 out:
421
	return r;
422
}
423
424
/*
425
 * Adds an identity to the authentication server.
426
 * This call is intended only for use by ssh-add(1) and like applications.
427
 */
428
int
429
ssh_add_identity_constrained(int sock, struct sshkey *key, const char *comment,
430
    u_int life, u_int confirm)
431
{
432
	struct sshbuf *msg;
433
	int r, constrained = (life || confirm);
434
	u_char type;
435
436
	if ((msg = sshbuf_new()) == NULL)
437
		return SSH_ERR_ALLOC_FAIL;
438
439
	switch (key->type) {
440
#ifdef WITH_OPENSSL
441
	case KEY_RSA:
442
	case KEY_RSA_CERT:
443
	case KEY_DSA:
444
	case KEY_DSA_CERT:
445
	case KEY_ECDSA:
446
	case KEY_ECDSA_CERT:
447
#endif
448
	case KEY_ED25519:
449
	case KEY_ED25519_CERT:
450
		type = constrained ?
451
		    SSH2_AGENTC_ADD_ID_CONSTRAINED :
452
		    SSH2_AGENTC_ADD_IDENTITY;
453
		if ((r = sshbuf_put_u8(msg, type)) != 0 ||
454
		    (r = ssh_encode_identity_ssh2(msg, key, comment)) != 0)
455
			goto out;
456
		break;
457
	default:
458
		r = SSH_ERR_INVALID_ARGUMENT;
459
		goto out;
460
	}
461
	if (constrained &&
462
	    (r = encode_constraints(msg, life, confirm)) != 0)
463
		goto out;
464
	if ((r = ssh_request_reply(sock, msg, msg)) != 0)
465
		goto out;
466
	if ((r = sshbuf_get_u8(msg, &type)) != 0)
467
		goto out;
468
	r = decode_reply(type);
469
 out:
470
	sshbuf_free(msg);
471
	return r;
472
}
473
474
/*
475
 * Removes an identity from the authentication server.
476
 * This call is intended only for use by ssh-add(1) and like applications.
477
 */
478
int
479
ssh_remove_identity(int sock, struct sshkey *key)
480
{
481
	struct sshbuf *msg;
482
	int r;
483
	u_char type, *blob = NULL;
484
	size_t blen;
485
486
	if ((msg = sshbuf_new()) == NULL)
487
		return SSH_ERR_ALLOC_FAIL;
488
489
	if (key->type != KEY_UNSPEC) {
490
		if ((r = sshkey_to_blob(key, &blob, &blen)) != 0)
491
			goto out;
492
		if ((r = sshbuf_put_u8(msg,
493
		    SSH2_AGENTC_REMOVE_IDENTITY)) != 0 ||
494
		    (r = sshbuf_put_string(msg, blob, blen)) != 0)
495
			goto out;
496
	} else {
497
		r = SSH_ERR_INVALID_ARGUMENT;
498
		goto out;
499
	}
500
	if ((r = ssh_request_reply(sock, msg, msg)) != 0)
501
		goto out;
502
	if ((r = sshbuf_get_u8(msg, &type)) != 0)
503
		goto out;
504
	r = decode_reply(type);
505
 out:
506
	if (blob != NULL) {
507
		explicit_bzero(blob, blen);
508
		free(blob);
509
	}
510
	sshbuf_free(msg);
511
	return r;
512
}
513
514
/*
515
 * Add/remove an token-based identity from the authentication server.
516
 * This call is intended only for use by ssh-add(1) and like applications.
517
 */
518
int
519
ssh_update_card(int sock, int add, const char *reader_id, const char *pin,
520
    u_int life, u_int confirm)
521
{
522
	struct sshbuf *msg;
523
	int r, constrained = (life || confirm);
524
	u_char type;
525
526
	if (add) {
527
		type = constrained ?
528
		    SSH_AGENTC_ADD_SMARTCARD_KEY_CONSTRAINED :
529
		    SSH_AGENTC_ADD_SMARTCARD_KEY;
530
	} else
531
		type = SSH_AGENTC_REMOVE_SMARTCARD_KEY;
532
533
	if ((msg = sshbuf_new()) == NULL)
534
		return SSH_ERR_ALLOC_FAIL;
535
	if ((r = sshbuf_put_u8(msg, type)) != 0 ||
536
	    (r = sshbuf_put_cstring(msg, reader_id)) != 0 ||
537
	    (r = sshbuf_put_cstring(msg, pin)) != 0)
538
		goto out;
539
	if (constrained &&
540
	    (r = encode_constraints(msg, life, confirm)) != 0)
541
		goto out;
542
	if ((r = ssh_request_reply(sock, msg, msg)) != 0)
543
		goto out;
544
	if ((r = sshbuf_get_u8(msg, &type)) != 0)
545
		goto out;
546
	r = decode_reply(type);
547
 out:
548
	sshbuf_free(msg);
549
	return r;
550
}
551
552
/*
553
 * Removes all identities from the agent.
554
 * This call is intended only for use by ssh-add(1) and like applications.
555
 *
556
 * This supports the SSH protocol 1 message to because, when clearing all
557
 * keys from an agent, we generally want to clear both protocol v1 and v2
558
 * keys.
559
 */
560
int
561
ssh_remove_all_identities(int sock, int version)
562
{
563
	struct sshbuf *msg;
564
	u_char type = (version == 1) ?
565
	    SSH_AGENTC_REMOVE_ALL_RSA_IDENTITIES :
566
	    SSH2_AGENTC_REMOVE_ALL_IDENTITIES;
567
	int r;
568
569
	if ((msg = sshbuf_new()) == NULL)
570
		return SSH_ERR_ALLOC_FAIL;
571
	if ((r = sshbuf_put_u8(msg, type)) != 0)
572
		goto out;
573
	if ((r = ssh_request_reply(sock, msg, msg)) != 0)
574
		goto out;
575
	if ((r = sshbuf_get_u8(msg, &type)) != 0)
576
		goto out;
577
	r = decode_reply(type);
578
 out:
579
	sshbuf_free(msg);
580
	return r;
581
}