encode_uint32 second parameter should be uint32_t, not unsigned long
[git-daemon2.git] / client / ssh.c
blob533d26b5e303642a29bf08c87ad94d0ab2fdd0eb
1 /*
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.
7 */
8 #include "ssh.h"
9 #include "ssh_localkey.h"
10 #include "home.h"
11 #include "base64.h"
12 #include <stdlib.h>
13 #include <string.h>
14 #include <errno.h>
15 #include <sys/un.h>
16 #include <sys/socket.h>
17 #include <sys/types.h>
18 #include <unistd.h>
19 #include <gcrypt.h>
20 #include "git-compat-util.h"
21 #include <stdlib.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
30 /* Decode uint32. */
31 static unsigned long decode_uint32(const unsigned char *ptr)
33 unsigned long v = 0;
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]);
38 return v;
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)
48 unsigned long rlen;
50 if (keyreply_len < 1) {
51 error("Malformed reply from SSH agent: No packet type present");
52 return NULL;
54 if (keyreply[0] == SSH_AGENT_FAILURE) {
55 error("SSH agent failed to sign the challenge");
56 return NULL;
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);
61 return NULL;
63 if (keyreply_len < 5) {
64 error("Malformed signature reply from SSH agent: Signature length incomplete");
65 return NULL;
67 rlen = decode_uint32(keyreply + 1);
69 if (keyreply_len < 5 + rlen) {
70 error("Malformed signature reply from SSH agent: Signature incomplete");
71 return NULL;
73 /* Strip packet header. */
74 keyreply += 5;
75 keyreply_len = rlen;
77 if (keyreply_len < 4) {
78 error("Malformed signature reply from SSH agent: Keytype header incomplete");
79 return NULL;
81 rlen = decode_uint32(keyreply);
82 if (keyreply_len < 4 + rlen) {
83 error("Malformed signature reply from SSH agent: Keytype incomplete");
84 return NULL;
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);
90 return NULL;
92 /* Strip key type. */
93 keyreply += 4 + rlen;
94 keyreply_len -= 4 + rlen;
96 if (keyreply_len < 4) {
97 error("Malformed signature reply from SSH agent: Signature blob length incomplete");
98 return NULL;
100 rlen = decode_uint32(keyreply);
101 if (keyreply_len < 4 + rlen) {
102 error("Malformed signature reply from SSH agent: Signature blob incomplete");
103 return NULL;
106 /* Extract the signature blob. */
107 keyreply += 4;
108 keyreply_len = rlen;
110 if (!strcmp(expected_type, "ssh-rsa")) {
111 unsigned char *ret;
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);
117 ret[4] = 0;
118 memcpy(ret + 5, keyreply, keyreply_len);
119 *key_len = 5 + keyreply_len;
120 } else {
121 encode_uint32(ret, keyreply_len);
122 memcpy(ret + 4, keyreply, keyreply_len);
123 *key_len = 4 + keyreply_len;
125 return ret;
126 } else if (!strcmp(expected_type, "ssh-dss")) {
127 unsigned char *ret;
128 unsigned offset = 0;
130 ret = xmalloc(keyreply_len + 10);
131 if (keyreply_len % 2) {
132 error("Malformed DSA signature, length is odd.");
133 free(ret);
134 return NULL;
137 /* Encode r. */
138 if (keyreply[0] > 128) {
139 /* Zero-pad to avoid negative value. */
140 encode_uint32(ret, keyreply_len / 2 + 1);
141 ret[offset + 4] = 0;
142 offset += 5;
143 } else {
144 encode_uint32(ret, keyreply_len / 2);
145 offset += 4;
147 memcpy(ret + offset, keyreply, keyreply_len / 2);
148 offset += keyreply_len / 2;
150 /* Encode s. */
151 if (keyreply[keyreply_len / 2] > 128) {
152 /* Zero-pad to avoid negative value. */
153 encode_uint32(ret + offset, keyreply_len / 2 + 1);
154 ret[offset + 4] = 0;
155 offset += 5;
156 } else {
157 encode_uint32(ret + offset, keyreply_len / 2);
158 offset += 4;
160 memcpy(ret + offset, keyreply + keyreply_len / 2,
161 keyreply_len / 2);
162 offset += keyreply_len / 2;
164 *key_len = offset;
165 return ret;
166 } else {
167 error("Unknown signature type '%s'", expected_type);
168 return NULL;
171 return NULL;
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;
180 size_t file_len2;
182 /* Skip to begin line. */
183 if (*file == '\r' || *file == '\n') {
184 /* Skip the empty lines. */
185 file = next_line(file, &file_len);
186 if (!file) {
187 error("Malformed SecSH public key (no header)");
188 return NULL;
191 file = next_line(file, &file_len);
192 if (!file) {
193 error("Malformed SecSH public key (no header)");
194 return NULL;
197 file = base64_blob_start(file, &file_len);
198 file2 = file;
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)");
204 return NULL;
206 if (!memcmp(file2, "---- END SSH2 PUBLIC KEY ----\r", 30)) {
207 error("Malformed SecSH public key (no encoded blob)");
208 return NULL;
210 if (!memcmp(file2, "---- END SSH2 PUBLIC KEY ----\n", 30)) {
211 error("Malformed SecSH public key (no encoded blob)");
212 return NULL;
214 while(1) {
215 file2 = next_line(file2, &file_len2);
216 if (!file2) {
217 error("Malformed SecSH public key (no trailer)");
218 return NULL;
220 if (file_len2 == 29 && !memcmp(file2,
221 "---- END SSH2 PUBLIC KEY ----", 29)) {
222 file_len = file2 - file;
223 break;
225 if (!memcmp(file2, "---- END SSH2 PUBLIC KEY ----\r", 30)) {
226 file_len = file2 - file;
227 break;
229 if (!memcmp(file, "---- END SSH2 PUBLIC KEY ----\n", 30)) {
230 file_len = file2 - file;
231 break;
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)
240 if (file_len < 32)
241 return 0;
242 if (!memcmp(file, "---- BEGIN SSH2 PUBLIC KEY ----\r", 32))
243 return 1;
244 if (!memcmp(file, "---- BEGIN SSH2 PUBLIC KEY ----\n", 32))
245 return 1;
246 file = next_line(file, &file_len);
247 if (file_len < 32)
248 return 0;
249 if (!memcmp(file, "---- BEGIN SSH2 PUBLIC KEY ----\r", 32))
250 return 1;
251 if (!memcmp(file, "---- BEGIN SSH2 PUBLIC KEY ----\n", 32))
252 return 1;
253 return 0;
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;
262 size_t blob_size;
263 base = (const unsigned char*)strchr((const char*)file, ' ');
264 if (!base) {
265 error("Malformed OpenSSH pubkey file");
266 return NULL;
268 base++;
270 sep2 = (const unsigned char*)strchr((const char*)base, ' ');
271 if (!sep2)
272 blob_size = strlen((const char*)base);
273 else
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)
282 if (file_len < 8)
283 return 0;
284 if (!memcmp(file, "ssh-rsa ", 8))
285 return 1;
286 if (!memcmp(file, "ssh-dss ", 8))
287 return 1;
288 return 0;
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");
303 return NULL;
306 void dump_blob(const char *name, const unsigned char *blob, size_t bloblen)
309 size_t i;
310 fprintf(stderr, "------- START %s (%u bytes) ------\n", name, (unsigned)bloblen);
311 for(i = 0; i < bloblen; i++) {
312 fprintf(stderr, "%02X ", blob[i]);
313 if (i % 16 == 15)
314 fprintf(stderr, "\n");
316 if (i % 16)
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)
324 while (size > 0) {
325 ssize_t r;
326 r = write(fd, ptr, size);
327 if (r > 0) {
328 ptr += r;
329 size -= r;
330 } else if (r == 0) {
331 error("Connection to ssh-agent unexpectedly lost");
332 return -1;
334 else if (errno == EINTR || errno == EAGAIN)
335 continue;
336 else {
337 error("Error writing to ssh-agent: %s",
338 strerror(errno));
339 return -1;
342 return 0;
345 int read_in_agent(int fd, unsigned char *ptr, size_t size)
347 while (size > 0) {
348 ssize_t r;
349 r = read(fd, ptr, size);
350 if (r > 0) {
351 ptr += r;
352 size -= r;
353 } else if (r == 0) {
354 error("Connection to ssh-agent unexpectedly lost");
355 return -1;
357 else if (errno == EINTR || errno == EAGAIN)
358 continue;
359 else {
360 error("Error reading from ssh-agent: %s",
361 strerror(errno));
362 return -1;
365 return 0;
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)
373 return -1;
374 if (write_to_agent(fd, payload, len) < 0)
375 return -1;
376 return 0;
379 unsigned char *read_packet_from_agent(int fd, size_t *len)
381 unsigned char *ret;
382 unsigned char tmp[4];
383 if (read_in_agent(fd, tmp, 4) < 0)
384 return NULL;
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);
389 return NULL;
391 ret = xmalloc(*len);
392 if (read_in_agent(fd, ret, *len) < 0) {
393 free(ret);
394 return NULL;
396 return ret;
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)
403 return NULL;
404 return read_packet_from_agent(fd, reply_len);
407 int ssh_agent_sock = -1;
409 int init_ssh_agent()
411 const char *agentpath;
412 struct sockaddr_un addr;
414 if (ssh_agent_sock >= 0)
415 return 0;
416 if (ssh_agent_sock < -1)
417 return -1;
419 agentpath = getenv("SSH_AUTH_SOCK");
420 if (!agentpath) {
421 ssh_agent_sock = -2;
422 return -1;
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",
431 strerror(errno));
432 ssh_agent_sock = -2;
433 return -1;
436 if (connect(ssh_agent_sock, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
437 error("Can't connect to ssh-agent(%s): %s", agentpath,
438 strerror(errno));
439 close(ssh_agent_sock);
440 ssh_agent_sock = -2;
441 return -1;
443 return 0;
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;
453 size_t reply_length;
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)
469 goto out_request;
471 reply = agent_do_io_cycle(ssh_agent_sock, request, request_length,
472 &reply_length);
473 if (!reply)
474 goto out_request;
476 signblocks = extract_actual_key(reply, reply_length, type,
477 signature_length);
478 /* extract_actual_key can fail, but we don't print any error for that. */
480 free(reply);
481 out_request:
482 free(request);
483 return signblocks;
486 static const char *check_blob(const unsigned char* blob, size_t blob_size)
488 size_t offset = 0;
489 unsigned round = 0;
490 unsigned rounds = 1;
491 const char *type = NULL;
493 for (round = 0; round < rounds; round++) {
494 unsigned long len;
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);
498 return NULL;
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);
504 return NULL;
506 if (round == 0 && len == 7 && !memcmp(blob + offset + 4,
507 "ssh-rsa", 7)) {
508 rounds = 3;
509 type = "ssh-rsa";
510 } else if (round == 0 && len == 7 && !memcmp(blob + offset + 4,
511 "ssh-dss", 7)) {
512 rounds = 5;
513 type = "ssh-dss";
514 } else if (round == 0) {
515 error("Read key has unknown key type (neither ssh-rsa nor ssh-dss)");
516 return NULL;
518 offset += len + 4;
520 if (offset != blob_size) {
521 error("Garbage after end of key");
522 return NULL;
524 return type;
527 #define MAXPATH 4096
528 #define MAXKEY 65536
529 #define DATABLOCK 32
530 #define HASHALGO GCRY_MD_SHA256
531 #define HASHLEN 32
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;
541 size_t i;
542 FILE* filp;
543 char *path;
544 char pathtemplate[MAXPATH];
545 unsigned char key[MAXKEY];
546 size_t keysize;
547 unsigned char block[DATABLOCK] = {0};
549 unsigned char *signature = NULL;
550 size_t signature_length;
551 const char *type;
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);
558 if (!x) {
559 /* Read the pubkey file. */
560 filp = fopen(path, "r");
561 if (!filp) {
562 error("Can't open '%s'", path);
563 goto out_readfile;
565 keysize = fread(key, 1, MAXKEY, filp);
566 if (ferror(filp)) {
567 error("Can't read '%s'", path);
568 goto out_readfile;
570 if (!feof(filp)) {
571 error("Keyfile '%s' too large", path);
572 goto out_readfile;
574 fclose(filp);
576 x = extract_key_from_file(key, keysize, &i);
578 out_readfile:
579 if (!x)
580 die("Can't read any public key named '%s'",
581 ssh_user);
583 type = check_blob(x, i);
584 if (!type)
585 die("Malformed public key for '%s'", ssh_user);
587 if (dispatcher) {
588 int s;
589 gnutls_session_t session;
590 unsigned char hash[HASHLEN];
591 const gnutls_datum_t *cert;
592 unsigned int lsize;
593 session = user_get_tls(dispatcher);
594 if (!session)
595 die("SSH auth requires TLS");
596 cert = gnutls_certificate_get_peers(session, &lsize);
597 if (!cert)
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);
603 if (s < 0)
604 die("Can't compute challenge: %s",
605 gnutls_strerror(s));
607 if (!signature)
608 signature = sign_using_ssh_agent(ssh_user,
609 x, i, block, DATABLOCK, &signature_length,
610 type);
611 if (!signature)
612 signature = sign_using_local_key(ssh_user,
613 x, i, block, DATABLOCK, &signature_length,
614 type);
615 if (!signature)
616 die("Can't sign using key '%s'", ssh_user);
618 if (dispatcher) {
619 struct cbuffer* in;
620 char tmp[5];
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);
629 free(signature);
630 free(x);