2 * Copyright (C) Ilari Liusvaara 2010
4 * This code is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
9 #include "ssh_localkey.h"
16 #include <sys/socket.h>
17 #include <sys/types.h>
20 #include "git-compat-util.h"
23 #define SSH2_AGENTC_SIGN_REQUEST 13
24 #define SSH_AGENT_FAILURE 5
25 #define SSH_AGENT_SUCCESS 6
26 #define SSH2_AGENT_SIGN_RESPONSE 14
28 #define MAX_REPLY_SIZE 1048576
31 static unsigned long decode_uint32(const unsigned char *ptr
)
34 v
|= ((unsigned long)ptr
[0] << 24);
35 v
|= ((unsigned long)ptr
[1] << 16);
36 v
|= ((unsigned long)ptr
[2] << 8);
37 v
|= ((unsigned long)ptr
[3]);
42 * Extract actual signature from ssh-agent reply. The return value should be
43 * freed. The reply has the component lengths present.
45 static unsigned char *extract_actual_key(const unsigned char *keyreply
,
46 size_t keyreply_len
, const char *expected_type
, size_t *key_len
)
50 if (keyreply_len
< 1) {
51 error("Malformed reply from SSH agent: No packet type present");
54 if (keyreply
[0] == SSH_AGENT_FAILURE
) {
55 error("SSH agent failed to sign the challenge");
58 if (keyreply
[0] != SSH2_AGENT_SIGN_RESPONSE
) {
59 error("SSH agent is confused: Returned packet type %u when type %u was expected",
60 keyreply
[0], SSH2_AGENT_SIGN_RESPONSE
);
63 if (keyreply_len
< 5) {
64 error("Malformed signature reply from SSH agent: Signature length incomplete");
67 rlen
= decode_uint32(keyreply
+ 1);
69 if (keyreply_len
< 5 + rlen
) {
70 error("Malformed signature reply from SSH agent: Signature incomplete");
73 /* Strip packet header. */
77 if (keyreply_len
< 4) {
78 error("Malformed signature reply from SSH agent: Keytype header incomplete");
81 rlen
= decode_uint32(keyreply
);
82 if (keyreply_len
< 4 + rlen
) {
83 error("Malformed signature reply from SSH agent: Keytype incomplete");
86 if (rlen
!= strlen(expected_type
) || memcmp(keyreply
+ 4,
87 expected_type
, rlen
)) {
88 error("SSH agent returned signature of wrong type: Expected '%s', got '%.*s'",
89 expected_type
, (int)rlen
, keyreply
+ 4);
94 keyreply_len
-= 4 + rlen
;
96 if (keyreply_len
< 4) {
97 error("Malformed signature reply from SSH agent: Signature blob length incomplete");
100 rlen
= decode_uint32(keyreply
);
101 if (keyreply_len
< 4 + rlen
) {
102 error("Malformed signature reply from SSH agent: Signature blob incomplete");
106 /* Extract the signature blob. */
110 if (!strcmp(expected_type
, "ssh-rsa")) {
113 ret
= xmalloc(keyreply_len
+ 5);
114 if (keyreply
[0] > 128) {
115 /* Zero-pad to avoid negative value. */
116 encode_uint32(ret
, keyreply_len
+ 1);
118 memcpy(ret
+ 5, keyreply
, keyreply_len
);
119 *key_len
= 5 + keyreply_len
;
121 encode_uint32(ret
, keyreply_len
);
122 memcpy(ret
+ 4, keyreply
, keyreply_len
);
123 *key_len
= 4 + keyreply_len
;
126 } else if (!strcmp(expected_type
, "ssh-dss")) {
130 ret
= xmalloc(keyreply_len
+ 10);
131 if (keyreply_len
% 2) {
132 error("Malformed DSA signature, length is odd.");
138 if (keyreply
[0] > 128) {
139 /* Zero-pad to avoid negative value. */
140 encode_uint32(ret
, keyreply_len
/ 2 + 1);
144 encode_uint32(ret
, keyreply_len
/ 2);
147 memcpy(ret
+ offset
, keyreply
, keyreply_len
/ 2);
148 offset
+= keyreply_len
/ 2;
151 if (keyreply
[keyreply_len
/ 2] > 128) {
152 /* Zero-pad to avoid negative value. */
153 encode_uint32(ret
+ offset
, keyreply_len
/ 2 + 1);
157 encode_uint32(ret
+ offset
, keyreply_len
/ 2);
160 memcpy(ret
+ offset
, keyreply
+ keyreply_len
/ 2,
162 offset
+= keyreply_len
/ 2;
167 error("Unknown signature type '%s'", expected_type
);
175 /* Extract SecSH public key. */
176 static unsigned char *extract_secsh_pubkey(const unsigned char *file
,
177 size_t file_len
, size_t *key_len
)
179 const unsigned char *file2
;
182 /* Skip to begin line. */
183 if (*file
== '\r' || *file
== '\n') {
184 /* Skip the empty lines. */
185 file
= next_line(file
, &file_len
);
187 error("Malformed SecSH public key (no header)");
191 file
= next_line(file
, &file_len
);
193 error("Malformed SecSH public key (no header)");
197 file
= base64_blob_start(file
, &file_len
);
199 file_len2
= file_len
;
201 if (file_len2
== 29 && !memcmp(file2
,
202 "---- END SSH2 PUBLIC KEY ----", 29)) {
203 error("Malformed SecSH public key (no encoded blob)");
206 if (!memcmp(file2
, "---- END SSH2 PUBLIC KEY ----\r", 30)) {
207 error("Malformed SecSH public key (no encoded blob)");
210 if (!memcmp(file2
, "---- END SSH2 PUBLIC KEY ----\n", 30)) {
211 error("Malformed SecSH public key (no encoded blob)");
215 file2
= next_line(file2
, &file_len2
);
217 error("Malformed SecSH public key (no trailer)");
220 if (file_len2
== 29 && !memcmp(file2
,
221 "---- END SSH2 PUBLIC KEY ----", 29)) {
222 file_len
= file2
- file
;
225 if (!memcmp(file2
, "---- END SSH2 PUBLIC KEY ----\r", 30)) {
226 file_len
= file2
- file
;
229 if (!memcmp(file
, "---- END SSH2 PUBLIC KEY ----\n", 30)) {
230 file_len
= file2
- file
;
234 return decode_base64_chunk(file
, file_len
, key_len
);
237 /* Is SecSH format key? */
238 static int is_secsh_pubkey(const unsigned char *file
, size_t file_len
)
242 if (!memcmp(file
, "---- BEGIN SSH2 PUBLIC KEY ----\r", 32))
244 if (!memcmp(file
, "---- BEGIN SSH2 PUBLIC KEY ----\n", 32))
246 file
= next_line(file
, &file_len
);
249 if (!memcmp(file
, "---- BEGIN SSH2 PUBLIC KEY ----\r", 32))
251 if (!memcmp(file
, "---- BEGIN SSH2 PUBLIC KEY ----\n", 32))
256 /* Extract OpenSSH public key. */
257 static unsigned char *extract_openssh_pubkey(const unsigned char *file
,
258 size_t file_len
, size_t *key_len
)
260 const unsigned char *base
;
261 const unsigned char *sep2
;
263 base
= (const unsigned char*)strchr((const char*)file
, ' ');
265 error("Malformed OpenSSH pubkey file");
270 sep2
= (const unsigned char*)strchr((const char*)base
, ' ');
272 blob_size
= strlen((const char*)base
);
274 blob_size
= sep2
- base
;
276 return decode_base64_chunk(base
, blob_size
, key_len
);
279 /* Is OpenSSH format key? */
280 static int is_openssh_pubkey(const unsigned char *file
, size_t file_len
)
284 if (!memcmp(file
, "ssh-rsa ", 8))
286 if (!memcmp(file
, "ssh-dss ", 8))
292 * Extract actual key blob from encoded key. The return value should be
293 * freed. The reply has the component lengths present.
295 unsigned char *extract_key_from_file(const unsigned char *file
,
296 size_t file_len
, size_t *key_len
)
298 if (is_openssh_pubkey(file
, file_len
))
299 return extract_openssh_pubkey(file
, file_len
, key_len
);
300 if (is_secsh_pubkey(file
, file_len
))
301 return extract_secsh_pubkey(file
, file_len
, key_len
);
302 error("Unknown public key file format");
306 void dump_blob(const char *name
, const unsigned char *blob
, size_t bloblen
)
310 fprintf(stderr
, "------- START %s (%u bytes) ------\n", name
, (unsigned)bloblen
);
311 for(i
= 0; i
< bloblen
; i
++) {
312 fprintf(stderr
, "%02X ", blob
[i
]);
314 fprintf(stderr
, "\n");
317 fprintf(stderr
, "\n");
318 fprintf(stderr
, "-------- END %s -------\n", name
);
322 int write_to_agent(int fd
, const unsigned char *ptr
, size_t size
)
326 r
= write(fd
, ptr
, size
);
331 error("Connection to ssh-agent unexpectedly lost");
334 else if (errno
== EINTR
|| errno
== EAGAIN
)
337 error("Error writing to ssh-agent: %s",
345 int read_in_agent(int fd
, unsigned char *ptr
, size_t size
)
349 r
= read(fd
, ptr
, size
);
354 error("Connection to ssh-agent unexpectedly lost");
357 else if (errno
== EINTR
|| errno
== EAGAIN
)
360 error("Error reading from ssh-agent: %s",
368 int write_packet_to_agent(int fd
, const unsigned char *payload
, size_t len
)
370 unsigned char tmp
[4];
371 encode_uint32(tmp
, len
);
372 if (write_to_agent(fd
, tmp
, 4) < 0)
374 if (write_to_agent(fd
, payload
, len
) < 0)
379 unsigned char *read_packet_from_agent(int fd
, size_t *len
)
382 unsigned char tmp
[4];
383 if (read_in_agent(fd
, tmp
, 4) < 0)
385 *len
= decode_uint32(tmp
);
386 if (*len
> MAX_REPLY_SIZE
) {
387 error("Reply from SSH agent too large (size %zu, maximum allowed %zu)",
388 *len
, (size_t)MAX_REPLY_SIZE
);
392 if (read_in_agent(fd
, ret
, *len
) < 0) {
399 static unsigned char *agent_do_io_cycle(int fd
, const unsigned char *payload
,
400 size_t payload_len
, size_t *reply_len
)
402 if (write_packet_to_agent(fd
, payload
, payload_len
) < 0)
404 return read_packet_from_agent(fd
, reply_len
);
407 int ssh_agent_sock
= -1;
411 const char *agentpath
;
412 struct sockaddr_un addr
;
414 if (ssh_agent_sock
>= 0)
416 if (ssh_agent_sock
< -1)
419 agentpath
= getenv("SSH_AUTH_SOCK");
425 addr
.sun_family
= AF_UNIX
;
426 strcpy(addr
.sun_path
, agentpath
);
428 ssh_agent_sock
= socket(AF_UNIX
, SOCK_STREAM
, 0);
429 if (ssh_agent_sock
< 0) {
430 error("Can't create socket: %s",
436 if (connect(ssh_agent_sock
, (struct sockaddr
*)&addr
, sizeof(addr
)) < 0) {
437 error("Can't connect to ssh-agent(%s): %s", agentpath
,
439 close(ssh_agent_sock
);
446 unsigned char *sign_using_ssh_agent(const char *keyname
,
447 const unsigned char *kblob
, size_t kblob_size
,
448 const unsigned char *challenge
, size_t challenge_size
,
449 size_t *signature_length
, const char *type
)
451 unsigned char *request
;
452 unsigned char *reply
;
454 size_t request_length
;
455 unsigned char *signblocks
= NULL
;
457 /* 43 = 1 (type) + 8 (2 lengths) + 4 (flags) */
458 request
= xmalloc(13 + kblob_size
+ challenge_size
);
459 request_length
= 13 + kblob_size
+ challenge_size
;
461 request
[0] = SSH2_AGENTC_SIGN_REQUEST
;
462 encode_uint32(request
+ 1, kblob_size
);
463 memcpy(request
+ 5, kblob
, kblob_size
);
464 encode_uint32(request
+ 5 + kblob_size
, challenge_size
);
465 memcpy(request
+ 9 + kblob_size
, challenge
, challenge_size
);
466 encode_uint32(request
+ 9 + kblob_size
+ challenge_size
, 0);
468 if (init_ssh_agent() < 0)
471 reply
= agent_do_io_cycle(ssh_agent_sock
, request
, request_length
,
476 signblocks
= extract_actual_key(reply
, reply_length
, type
,
478 /* extract_actual_key can fail, but we don't print any error for that. */
486 static const char *check_blob(const unsigned char* blob
, size_t blob_size
)
491 const char *type
= NULL
;
493 for (round
= 0; round
< rounds
; round
++) {
495 if (offset
+ 4 < offset
|| blob_size
< offset
+ 4) {
496 error("Component length header incomplete (component=%u/%u, blob_size=%zu, offset=%zu",
497 round
+ 1, rounds
, blob_size
, offset
);
500 len
= decode_uint32(blob
+ offset
);
501 if (offset
+ len
+ 4 < offset
+ 4 || blob_size
< offset
+ len
+ 4) {
502 error("Component incomplete (component=%u/%u, blob_size=%zu, offset=%zu, len=%lu)",
503 round
+ 1, rounds
, blob_size
, offset
, len
);
506 if (round
== 0 && len
== 7 && !memcmp(blob
+ offset
+ 4,
510 } else if (round
== 0 && len
== 7 && !memcmp(blob
+ offset
+ 4,
514 } else if (round
== 0) {
515 error("Read key has unknown key type (neither ssh-rsa nor ssh-dss)");
520 if (offset
!= blob_size
) {
521 error("Garbage after end of key");
530 #define HASHALGO GCRY_MD_SHA256
533 void do_ssh_preauth(const char *ssh_user
)
535 send_ssh_authentication(NULL
, ssh_user
);
538 void send_ssh_authentication(struct user
*dispatcher
, const char *ssh_user
)
540 unsigned char *x
= NULL
;
544 char pathtemplate
[MAXPATH
];
545 unsigned char key
[MAXKEY
];
547 unsigned char block
[DATABLOCK
] = {0};
549 unsigned char *signature
= NULL
;
550 size_t signature_length
;
553 if (strlen(ssh_user
) > MAXPATH
- 32)
554 die("Key name too long");
555 sprintf(pathtemplate
, "~/.ssh/%s.pub", ssh_user
);
556 path
= expand_path(pathtemplate
);
559 /* Read the pubkey file. */
560 filp
= fopen(path
, "r");
562 error("Can't open '%s'", path
);
565 keysize
= fread(key
, 1, MAXKEY
, filp
);
567 error("Can't read '%s'", path
);
571 error("Keyfile '%s' too large", path
);
576 x
= extract_key_from_file(key
, keysize
, &i
);
580 die("Can't read any public key named '%s'",
583 type
= check_blob(x
, i
);
585 die("Malformed public key for '%s'", ssh_user
);
589 gnutls_session_t session
;
590 unsigned char hash
[HASHLEN
];
591 const gnutls_datum_t
*cert
;
593 session
= user_get_tls(dispatcher
);
595 die("SSH auth requires TLS");
596 cert
= gnutls_certificate_get_peers(session
, &lsize
);
598 die("Unable to get server certificate (server didn't send one?)");
600 gcry_md_hash_buffer(HASHALGO
, hash
, cert
->data
, cert
->size
);
601 s
= gnutls_prf(session
, 14, "ssh-key-verify", 0, HASHLEN
,
602 (const char*)hash
, DATABLOCK
, (char*)block
);
604 die("Can't compute challenge: %s",
608 signature
= sign_using_ssh_agent(ssh_user
,
609 x
, i
, block
, DATABLOCK
, &signature_length
,
612 signature
= sign_using_local_key(ssh_user
,
613 x
, i
, block
, DATABLOCK
, &signature_length
,
616 die("Can't sign using key '%s'", ssh_user
);
621 in
= user_get_red_in(dispatcher
);
622 sprintf(tmp
, "%04x", (unsigned)(8 + i
+ signature_length
));
623 cbuffer_write(in
, (const unsigned char*)tmp
, 4);
624 cbuffer_write(in
, (const unsigned char*)"ssh ", 4);
625 cbuffer_write(in
, x
, i
);
626 cbuffer_write(in
, signature
, signature_length
);