authfd.c revision 63249
1/*
2 *
3 * authfd.c
4 *
5 * Author: Tatu Ylonen <ylo@cs.hut.fi>
6 *
7 * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
8 *                    All rights reserved
9 *
10 * Created: Wed Mar 29 01:30:28 1995 ylo
11 *
12 * Functions for connecting the local authentication agent.
13 *
14 * $FreeBSD: head/crypto/openssh/authfd.c 60576 2000-05-15 05:24:25Z kris $
15 */
16
17#include "includes.h"
18RCSID("$Id: authfd.c,v 1.19 2000/04/29 18:11:52 markus Exp $");
19
20#include "ssh.h"
21#include "rsa.h"
22#include "authfd.h"
23#include "buffer.h"
24#include "bufaux.h"
25#include "xmalloc.h"
26#include "getput.h"
27
28#include <openssl/rsa.h>
29
30/* Returns the number of the authentication fd, or -1 if there is none. */
31
32int
33ssh_get_authentication_socket()
34{
35	const char *authsocket;
36	int sock;
37	struct sockaddr_un sunaddr;
38
39	authsocket = getenv(SSH_AUTHSOCKET_ENV_NAME);
40	if (!authsocket)
41		return -1;
42
43	sunaddr.sun_family = AF_UNIX;
44	strlcpy(sunaddr.sun_path, authsocket, sizeof(sunaddr.sun_path));
45
46	sock = socket(AF_UNIX, SOCK_STREAM, 0);
47	if (sock < 0)
48		return -1;
49
50	/* close on exec */
51	if (fcntl(sock, F_SETFD, 1) == -1) {
52		close(sock);
53		return -1;
54	}
55	if (connect(sock, (struct sockaddr *) & sunaddr, sizeof(sunaddr)) < 0) {
56		close(sock);
57		return -1;
58	}
59	return sock;
60}
61
62/*
63 * Closes the agent socket if it should be closed (depends on how it was
64 * obtained).  The argument must have been returned by
65 * ssh_get_authentication_socket().
66 */
67
68void
69ssh_close_authentication_socket(int sock)
70{
71	if (getenv(SSH_AUTHSOCKET_ENV_NAME))
72		close(sock);
73}
74
75/*
76 * Opens and connects a private socket for communication with the
77 * authentication agent.  Returns the file descriptor (which must be
78 * shut down and closed by the caller when no longer needed).
79 * Returns NULL if an error occurred and the connection could not be
80 * opened.
81 */
82
83AuthenticationConnection *
84ssh_get_authentication_connection()
85{
86	AuthenticationConnection *auth;
87	int sock;
88
89	sock = ssh_get_authentication_socket();
90
91	/*
92	 * Fail if we couldn't obtain a connection.  This happens if we
93	 * exited due to a timeout.
94	 */
95	if (sock < 0)
96		return NULL;
97
98	auth = xmalloc(sizeof(*auth));
99	auth->fd = sock;
100	buffer_init(&auth->packet);
101	buffer_init(&auth->identities);
102	auth->howmany = 0;
103
104	return auth;
105}
106
107/*
108 * Closes the connection to the authentication agent and frees any associated
109 * memory.
110 */
111
112void
113ssh_close_authentication_connection(AuthenticationConnection *ac)
114{
115	buffer_free(&ac->packet);
116	buffer_free(&ac->identities);
117	close(ac->fd);
118	xfree(ac);
119}
120
121/*
122 * Returns the first authentication identity held by the agent.
123 * Returns true if an identity is available, 0 otherwise.
124 * The caller must initialize the integers before the call, and free the
125 * comment after a successful call (before calling ssh_get_next_identity).
126 */
127
128int
129ssh_get_first_identity(AuthenticationConnection *auth,
130		       BIGNUM *e, BIGNUM *n, char **comment)
131{
132	unsigned char msg[8192];
133	int len, l;
134
135	/*
136	 * Send a message to the agent requesting for a list of the
137	 * identities it can represent.
138	 */
139	msg[0] = 0;
140	msg[1] = 0;
141	msg[2] = 0;
142	msg[3] = 1;
143	msg[4] = SSH_AGENTC_REQUEST_RSA_IDENTITIES;
144	if (atomicio(write, auth->fd, msg, 5) != 5) {
145		error("write auth->fd: %.100s", strerror(errno));
146		return 0;
147	}
148	/* Read the length of the response.  XXX implement timeouts here. */
149	len = 4;
150	while (len > 0) {
151		l = read(auth->fd, msg + 4 - len, len);
152		if (l <= 0) {
153			error("read auth->fd: %.100s", strerror(errno));
154			return 0;
155		}
156		len -= l;
157	}
158
159	/*
160	 * Extract the length, and check it for sanity.  (We cannot trust
161	 * authentication agents).
162	 */
163	len = GET_32BIT(msg);
164	if (len < 1 || len > 256 * 1024)
165		fatal("Authentication reply message too long: %d\n", len);
166
167	/* Read the packet itself. */
168	buffer_clear(&auth->identities);
169	while (len > 0) {
170		l = len;
171		if (l > sizeof(msg))
172			l = sizeof(msg);
173		l = read(auth->fd, msg, l);
174		if (l <= 0)
175			fatal("Incomplete authentication reply.");
176		buffer_append(&auth->identities, (char *) msg, l);
177		len -= l;
178	}
179
180	/* Get message type, and verify that we got a proper answer. */
181	buffer_get(&auth->identities, (char *) msg, 1);
182	if (msg[0] != SSH_AGENT_RSA_IDENTITIES_ANSWER)
183		fatal("Bad authentication reply message type: %d", msg[0]);
184
185	/* Get the number of entries in the response and check it for sanity. */
186	auth->howmany = buffer_get_int(&auth->identities);
187	if (auth->howmany > 1024)
188		fatal("Too many identities in authentication reply: %d\n", auth->howmany);
189
190	/* Return the first entry (if any). */
191	return ssh_get_next_identity(auth, e, n, comment);
192}
193
194/*
195 * Returns the next authentication identity for the agent.  Other functions
196 * can be called between this and ssh_get_first_identity or two calls of this
197 * function.  This returns 0 if there are no more identities.  The caller
198 * must free comment after a successful return.
199 */
200
201int
202ssh_get_next_identity(AuthenticationConnection *auth,
203		      BIGNUM *e, BIGNUM *n, char **comment)
204{
205	unsigned int bits;
206
207	/* Return failure if no more entries. */
208	if (auth->howmany <= 0)
209		return 0;
210
211	/*
212	 * Get the next entry from the packet.  These will abort with a fatal
213	 * error if the packet is too short or contains corrupt data.
214	 */
215	bits = buffer_get_int(&auth->identities);
216	buffer_get_bignum(&auth->identities, e);
217	buffer_get_bignum(&auth->identities, n);
218	*comment = buffer_get_string(&auth->identities, NULL);
219
220	if (bits != BN_num_bits(n))
221		log("Warning: identity keysize mismatch: actual %d, announced %u",
222		    BN_num_bits(n), bits);
223
224	/* Decrement the number of remaining entries. */
225	auth->howmany--;
226
227	return 1;
228}
229
230/*
231 * Generates a random challenge, sends it to the agent, and waits for
232 * response from the agent.  Returns true (non-zero) if the agent gave the
233 * correct answer, zero otherwise.  Response type selects the style of
234 * response desired, with 0 corresponding to protocol version 1.0 (no longer
235 * supported) and 1 corresponding to protocol version 1.1.
236 */
237
238int
239ssh_decrypt_challenge(AuthenticationConnection *auth,
240		      BIGNUM* e, BIGNUM *n, BIGNUM *challenge,
241		      unsigned char session_id[16],
242		      unsigned int response_type,
243		      unsigned char response[16])
244{
245	Buffer buffer;
246	unsigned char buf[8192];
247	int len, l, i;
248
249	/* Response type 0 is no longer supported. */
250	if (response_type == 0)
251		fatal("Compatibility with ssh protocol version 1.0 no longer supported.");
252
253	/* Format a message to the agent. */
254	buf[0] = SSH_AGENTC_RSA_CHALLENGE;
255	buffer_init(&buffer);
256	buffer_append(&buffer, (char *) buf, 1);
257	buffer_put_int(&buffer, BN_num_bits(n));
258	buffer_put_bignum(&buffer, e);
259	buffer_put_bignum(&buffer, n);
260	buffer_put_bignum(&buffer, challenge);
261	buffer_append(&buffer, (char *) session_id, 16);
262	buffer_put_int(&buffer, response_type);
263
264	/* Get the length of the message, and format it in the buffer. */
265	len = buffer_len(&buffer);
266	PUT_32BIT(buf, len);
267
268	/* Send the length and then the packet to the agent. */
269	if (atomicio(write, auth->fd, buf, 4) != 4 ||
270	    atomicio(write, auth->fd, buffer_ptr(&buffer),
271	    buffer_len(&buffer)) != buffer_len(&buffer)) {
272		error("Error writing to authentication socket.");
273error_cleanup:
274		buffer_free(&buffer);
275		return 0;
276	}
277	/*
278	 * Wait for response from the agent.  First read the length of the
279	 * response packet.
280	 */
281	len = 4;
282	while (len > 0) {
283		l = read(auth->fd, buf + 4 - len, len);
284		if (l <= 0) {
285			error("Error reading response length from authentication socket.");
286			goto error_cleanup;
287		}
288		len -= l;
289	}
290
291	/* Extract the length, and check it for sanity. */
292	len = GET_32BIT(buf);
293	if (len > 256 * 1024)
294		fatal("Authentication response too long: %d", len);
295
296	/* Read the rest of the response in tothe buffer. */
297	buffer_clear(&buffer);
298	while (len > 0) {
299		l = len;
300		if (l > sizeof(buf))
301			l = sizeof(buf);
302		l = read(auth->fd, buf, l);
303		if (l <= 0) {
304			error("Error reading response from authentication socket.");
305			goto error_cleanup;
306		}
307		buffer_append(&buffer, (char *) buf, l);
308		len -= l;
309	}
310
311	/* Get the type of the packet. */
312	buffer_get(&buffer, (char *) buf, 1);
313
314	/* Check for agent failure message. */
315	if (buf[0] == SSH_AGENT_FAILURE) {
316		log("Agent admitted failure to authenticate using the key.");
317		goto error_cleanup;
318	}
319	/* Now it must be an authentication response packet. */
320	if (buf[0] != SSH_AGENT_RSA_RESPONSE)
321		fatal("Bad authentication response: %d", buf[0]);
322
323	/*
324	 * Get the response from the packet.  This will abort with a fatal
325	 * error if the packet is corrupt.
326	 */
327	for (i = 0; i < 16; i++)
328		response[i] = buffer_get_char(&buffer);
329
330	/* The buffer containing the packet is no longer needed. */
331	buffer_free(&buffer);
332
333	/* Correct answer. */
334	return 1;
335}
336
337/*
338 * Adds an identity to the authentication server.  This call is not meant to
339 * be used by normal applications.
340 */
341
342int
343ssh_add_identity(AuthenticationConnection *auth,
344		 RSA * key, const char *comment)
345{
346	Buffer buffer;
347	unsigned char buf[8192];
348	int len, l, type;
349
350	/* Format a message to the agent. */
351	buffer_init(&buffer);
352	buffer_put_char(&buffer, SSH_AGENTC_ADD_RSA_IDENTITY);
353	buffer_put_int(&buffer, BN_num_bits(key->n));
354	buffer_put_bignum(&buffer, key->n);
355	buffer_put_bignum(&buffer, key->e);
356	buffer_put_bignum(&buffer, key->d);
357	/* To keep within the protocol: p < q for ssh. in SSL p > q */
358	buffer_put_bignum(&buffer, key->iqmp);	/* ssh key->u */
359	buffer_put_bignum(&buffer, key->q);	/* ssh key->p, SSL key->q */
360	buffer_put_bignum(&buffer, key->p);	/* ssh key->q, SSL key->p */
361	buffer_put_string(&buffer, comment, strlen(comment));
362
363	/* Get the length of the message, and format it in the buffer. */
364	len = buffer_len(&buffer);
365	PUT_32BIT(buf, len);
366
367	/* Send the length and then the packet to the agent. */
368	if (atomicio(write, auth->fd, buf, 4) != 4 ||
369	    atomicio(write, auth->fd, buffer_ptr(&buffer),
370	    buffer_len(&buffer)) != buffer_len(&buffer)) {
371		error("Error writing to authentication socket.");
372error_cleanup:
373		buffer_free(&buffer);
374		return 0;
375	}
376	/* Wait for response from the agent.  First read the length of the
377	   response packet. */
378	len = 4;
379	while (len > 0) {
380		l = read(auth->fd, buf + 4 - len, len);
381		if (l <= 0) {
382			error("Error reading response length from authentication socket.");
383			goto error_cleanup;
384		}
385		len -= l;
386	}
387
388	/* Extract the length, and check it for sanity. */
389	len = GET_32BIT(buf);
390	if (len > 256 * 1024)
391		fatal("Add identity response too long: %d", len);
392
393	/* Read the rest of the response in tothe buffer. */
394	buffer_clear(&buffer);
395	while (len > 0) {
396		l = len;
397		if (l > sizeof(buf))
398			l = sizeof(buf);
399		l = read(auth->fd, buf, l);
400		if (l <= 0) {
401			error("Error reading response from authentication socket.");
402			goto error_cleanup;
403		}
404		buffer_append(&buffer, (char *) buf, l);
405		len -= l;
406	}
407
408	/* Get the type of the packet. */
409	type = buffer_get_char(&buffer);
410	switch (type) {
411	case SSH_AGENT_FAILURE:
412		buffer_free(&buffer);
413		return 0;
414	case SSH_AGENT_SUCCESS:
415		buffer_free(&buffer);
416		return 1;
417	default:
418		fatal("Bad response to add identity from authentication agent: %d",
419		      type);
420	}
421	/* NOTREACHED */
422	return 0;
423}
424
425/*
426 * Removes an identity from the authentication server.  This call is not
427 * meant to be used by normal applications.
428 */
429
430int
431ssh_remove_identity(AuthenticationConnection *auth, RSA *key)
432{
433	Buffer buffer;
434	unsigned char buf[8192];
435	int len, l, type;
436
437	/* Format a message to the agent. */
438	buffer_init(&buffer);
439	buffer_put_char(&buffer, SSH_AGENTC_REMOVE_RSA_IDENTITY);
440	buffer_put_int(&buffer, BN_num_bits(key->n));
441	buffer_put_bignum(&buffer, key->e);
442	buffer_put_bignum(&buffer, key->n);
443
444	/* Get the length of the message, and format it in the buffer. */
445	len = buffer_len(&buffer);
446	PUT_32BIT(buf, len);
447
448	/* Send the length and then the packet to the agent. */
449	if (atomicio(write, auth->fd, buf, 4) != 4 ||
450	    atomicio(write, auth->fd, buffer_ptr(&buffer),
451	    buffer_len(&buffer)) != buffer_len(&buffer)) {
452		error("Error writing to authentication socket.");
453error_cleanup:
454		buffer_free(&buffer);
455		return 0;
456	}
457	/*
458	 * Wait for response from the agent.  First read the length of the
459	 * response packet.
460	 */
461	len = 4;
462	while (len > 0) {
463		l = read(auth->fd, buf + 4 - len, len);
464		if (l <= 0) {
465			error("Error reading response length from authentication socket.");
466			goto error_cleanup;
467		}
468		len -= l;
469	}
470
471	/* Extract the length, and check it for sanity. */
472	len = GET_32BIT(buf);
473	if (len > 256 * 1024)
474		fatal("Remove identity response too long: %d", len);
475
476	/* Read the rest of the response in tothe buffer. */
477	buffer_clear(&buffer);
478	while (len > 0) {
479		l = len;
480		if (l > sizeof(buf))
481			l = sizeof(buf);
482		l = read(auth->fd, buf, l);
483		if (l <= 0) {
484			error("Error reading response from authentication socket.");
485			goto error_cleanup;
486		}
487		buffer_append(&buffer, (char *) buf, l);
488		len -= l;
489	}
490
491	/* Get the type of the packet. */
492	type = buffer_get_char(&buffer);
493	switch (type) {
494	case SSH_AGENT_FAILURE:
495		buffer_free(&buffer);
496		return 0;
497	case SSH_AGENT_SUCCESS:
498		buffer_free(&buffer);
499		return 1;
500	default:
501		fatal("Bad response to remove identity from authentication agent: %d",
502		      type);
503	}
504	/* NOTREACHED */
505	return 0;
506}
507
508/*
509 * Removes all identities from the agent.  This call is not meant to be used
510 * by normal applications.
511 */
512
513int
514ssh_remove_all_identities(AuthenticationConnection *auth)
515{
516	Buffer buffer;
517	unsigned char buf[8192];
518	int len, l, type;
519
520	/* Get the length of the message, and format it in the buffer. */
521	PUT_32BIT(buf, 1);
522	buf[4] = SSH_AGENTC_REMOVE_ALL_RSA_IDENTITIES;
523
524	/* Send the length and then the packet to the agent. */
525	if (atomicio(write, auth->fd, buf, 5) != 5) {
526		error("Error writing to authentication socket.");
527		return 0;
528	}
529	/*
530	 * Wait for response from the agent.  First read the length of the
531	 * response packet.
532	 */
533	len = 4;
534	while (len > 0) {
535		l = read(auth->fd, buf + 4 - len, len);
536		if (l <= 0) {
537			error("Error reading response length from authentication socket.");
538			return 0;
539		}
540		len -= l;
541	}
542
543	/* Extract the length, and check it for sanity. */
544	len = GET_32BIT(buf);
545	if (len > 256 * 1024)
546		fatal("Remove identity response too long: %d", len);
547
548	/* Read the rest of the response into the buffer. */
549	buffer_init(&buffer);
550	while (len > 0) {
551		l = len;
552		if (l > sizeof(buf))
553			l = sizeof(buf);
554		l = read(auth->fd, buf, l);
555		if (l <= 0) {
556			error("Error reading response from authentication socket.");
557			buffer_free(&buffer);
558			return 0;
559		}
560		buffer_append(&buffer, (char *) buf, l);
561		len -= l;
562	}
563
564	/* Get the type of the packet. */
565	type = buffer_get_char(&buffer);
566	switch (type) {
567	case SSH_AGENT_FAILURE:
568		buffer_free(&buffer);
569		return 0;
570	case SSH_AGENT_SUCCESS:
571		buffer_free(&buffer);
572		return 1;
573	default:
574		fatal("Bad response to remove identity from authentication agent: %d",
575		      type);
576	}
577	/* NOTREACHED */
578	return 0;
579}
580