Security fixes, copied from ssh.c: check string length versus packet
[u/mdw/putty] / scpssh.c
1 /*
2 * scpssh.c - SSH implementation for PuTTY Secure Copy
3 * Joris van Rantwijk, Aug 1999.
4 * Based on PuTTY ssh.c by Simon Tatham.
5 */
6
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <string.h>
10 #include <winsock.h>
11
12 #include "putty.h"
13 #include "ssh.h"
14 #include "scp.h"
15
16 #define SSH_MSG_DISCONNECT 1
17 #define SSH_SMSG_PUBLIC_KEY 2
18 #define SSH_CMSG_SESSION_KEY 3
19 #define SSH_CMSG_USER 4
20 #define SSH_CMSG_AUTH_PASSWORD 9
21 #define SSH_CMSG_EXEC_CMD 13
22 #define SSH_SMSG_SUCCESS 14
23 #define SSH_SMSG_FAILURE 15
24 #define SSH_CMSG_STDIN_DATA 16
25 #define SSH_SMSG_STDOUT_DATA 17
26 #define SSH_SMSG_STDERR_DATA 18
27 #define SSH_CMSG_EOF 19
28 #define SSH_SMSG_EXIT_STATUS 20
29 #define SSH_CMSG_EXIT_CONFIRMATION 33
30 #define SSH_MSG_IGNORE 32
31 #define SSH_MSG_DEBUG 36
32
33 #define GET_32BIT(cp) \
34 (((unsigned long)(unsigned char)(cp)[0] << 24) | \
35 ((unsigned long)(unsigned char)(cp)[1] << 16) | \
36 ((unsigned long)(unsigned char)(cp)[2] << 8) | \
37 ((unsigned long)(unsigned char)(cp)[3]))
38
39 #define PUT_32BIT(cp, value) { \
40 (cp)[0] = (unsigned char)((value) >> 24); \
41 (cp)[1] = (unsigned char)((value) >> 16); \
42 (cp)[2] = (unsigned char)((value) >> 8); \
43 (cp)[3] = (unsigned char)(value); }
44
45 static SOCKET s = INVALID_SOCKET;
46
47 static unsigned char session_key[32];
48 static struct ssh_cipher *cipher = NULL;
49
50 static char *savedhost;
51
52 struct Packet {
53 long length;
54 int type;
55 unsigned long crc;
56 unsigned char *data;
57 unsigned char *body;
58 long maxlen;
59 };
60
61 static struct Packet pktin = { 0, 0, 0, NULL, 0 };
62 static struct Packet pktout = { 0, 0, 0, NULL, 0 };
63
64
65 static void s_write (char *buf, int len) {
66 while (len > 0) {
67 int i = send (s, buf, len, 0);
68 noise_ultralight(i);
69 if (i <= 0)
70 fatalbox("Lost connection while sending");
71 len -= i, buf += i;
72 }
73 }
74
75 static int s_read (char *buf, int len) {
76 int ret = 0;
77 while (len > 0) {
78 int i = recv (s, buf, len, 0);
79 noise_ultralight(i);
80 if (i > 0)
81 len -= i, buf += i, ret += i;
82 else
83 return i;
84 }
85 return ret;
86 }
87
88 /*
89 * Read and decrypt one incoming SSH packet.
90 */
91 static void get_packet(void)
92 {
93 unsigned char buf[4];
94 int ret;
95 int len, pad, biglen;
96 unsigned long realcrc, gotcrc;
97
98 next_packet:
99
100 pktin.type = 0;
101 pktin.length = 0;
102
103 ret = s_read(buf, 4);
104 if (ret != 4) {
105 closesocket(s);
106 s = INVALID_SOCKET;
107 return;
108 }
109
110 len = GET_32BIT(buf);
111
112 #ifdef FWHACK
113 if (len == 0x52656d6f) {
114 len = 0x300;
115 }
116 #endif
117
118 pad = 8 - (len % 8);
119 biglen = len + pad;
120 len -= 5; /* type and CRC */
121
122 pktin.length = len;
123 if (pktin.maxlen < biglen) {
124 pktin.maxlen = biglen;
125 #ifdef MSCRYPTOAPI
126 /* allocate enough buffer space for extra block
127 * for MS CryptEncrypt() */
128 pktin.data = (pktin.data == NULL) ?
129 smalloc(biglen+8) : srealloc(pktin.data, biglen+8);
130 #else
131 pktin.data = (pktin.data == NULL) ?
132 smalloc(biglen) : srealloc(pktin.data, biglen);
133 #endif
134 }
135
136 ret = s_read(pktin.data, biglen);
137 if (ret != biglen) {
138 closesocket(s);
139 s = INVALID_SOCKET;
140 return;
141 }
142
143 if (cipher)
144 cipher->decrypt(pktin.data, biglen);
145
146 pktin.type = pktin.data[pad];
147 pktin.body = pktin.data + pad + 1;
148
149 realcrc = crc32(pktin.data, biglen-4);
150 gotcrc = (pktin.data[biglen-4] << 24);
151 gotcrc |= (pktin.data[biglen-3] << 16);
152 gotcrc |= (pktin.data[biglen-2] << 8);
153 gotcrc |= (pktin.data[biglen-1] << 0);
154 if (gotcrc != realcrc) {
155 fatalbox("Incorrect CRC received on packet");
156 }
157
158 if (pktin.type == SSH_MSG_DEBUG) {
159 if (verbose) {
160 int len = GET_32BIT(pktin.body);
161 fprintf(stderr, "Remote: ");
162 fwrite(pktin.body + 4, len, 1, stderr);
163 fprintf(stderr, "\n");
164 }
165 goto next_packet;
166 }
167 if (pktin.type == SSH_MSG_IGNORE) {
168 goto next_packet;
169 }
170 }
171
172 static void s_wrpkt_start(int type, int len) {
173 int pad, biglen;
174
175 len += 5; /* type and CRC */
176 pad = 8 - (len%8);
177 biglen = len + pad;
178
179 pktout.length = len-5;
180 if (pktout.maxlen < biglen) {
181 pktout.maxlen = biglen;
182 #ifdef MSCRYPTOAPI
183 /* Allocate enough buffer space for extra block
184 * for MS CryptEncrypt() */
185 pktout.data = (pktout.data == NULL ? malloc(biglen+8) :
186 realloc(pktout.data, biglen+8));
187 #else
188 pktout.data = (pktout.data == NULL ? malloc(biglen+4) :
189 realloc(pktout.data, biglen+4));
190 #endif
191 if (!pktout.data)
192 fatalbox("Out of memory");
193 }
194
195 pktout.type = type;
196 pktout.body = pktout.data+4+pad+1;
197 }
198
199 static void s_wrpkt(void) {
200 int pad, len, biglen, i;
201 unsigned long crc;
202
203 len = pktout.length + 5; /* type and CRC */
204 pad = 8 - (len%8);
205 biglen = len + pad;
206
207 pktout.body[-1] = pktout.type;
208 for (i=0; i<pad; i++)
209 pktout.data[i+4] = random_byte();
210 crc = crc32(pktout.data+4, biglen-4);
211 PUT_32BIT(pktout.data+biglen, crc);
212 PUT_32BIT(pktout.data, len);
213
214 if (cipher)
215 cipher->encrypt(pktout.data+4, biglen);
216
217 s_write(pktout.data, biglen+4);
218 }
219
220 static int do_ssh_init(void) {
221 char c;
222 char version[10];
223 char vstring[40];
224 int i;
225
226 #ifdef FWHACK
227 i = 0;
228 while (s_read(&c, 1) == 1) {
229 if (c == 'S' && i < 2) i++;
230 else if (c == 'S' && i == 2) i = 2;
231 else if (c == 'H' && i == 2) break;
232 else i = 0;
233 }
234 #else
235 if (s_read(&c,1) != 1 || c != 'S') return 0;
236 if (s_read(&c,1) != 1 || c != 'S') return 0;
237 if (s_read(&c,1) != 1 || c != 'H') return 0;
238 #endif
239 if (s_read(&c,1) != 1 || c != '-') return 0;
240 i = 0;
241 while (1) {
242 if (s_read(&c,1) != 1)
243 return 0;
244 if (i >= 0) {
245 if (c == '-') {
246 version[i] = '\0';
247 i = -1;
248 } else if (i < sizeof(version)-1)
249 version[i++] = c;
250 }
251 else if (c == '\n')
252 break;
253 }
254
255 sprintf(vstring, "SSH-%s-7.7.7\n",
256 (strcmp(version, "1.5") <= 0 ? version : "1.5"));
257 s_write(vstring, strlen(vstring));
258 return 1;
259 }
260
261
262 /*
263 * Login on the server and request execution of the command.
264 */
265 static void ssh_login(char *username, char *cmd)
266 {
267 int i, j, len;
268 unsigned char session_id[16];
269 unsigned char *rsabuf, *keystr1, *keystr2;
270 unsigned char cookie[8];
271 struct RSAKey servkey, hostkey;
272 struct MD5Context md5c;
273 unsigned long supported_ciphers_mask;
274 int cipher_type;
275
276 extern struct ssh_cipher ssh_3des;
277 extern struct ssh_cipher ssh_blowfish;
278
279 get_packet();
280
281 if (pktin.type != SSH_SMSG_PUBLIC_KEY)
282 fatalbox("Public key packet not received");
283
284 memcpy(cookie, pktin.body, 8);
285
286 MD5Init(&md5c);
287
288 i = makekey(pktin.body+8, &servkey, &keystr1);
289 j = makekey(pktin.body+8+i, &hostkey, &keystr2);
290
291 supported_ciphers_mask = GET_32BIT(pktin.body+12+i+j);
292
293 MD5Update(&md5c, keystr2, hostkey.bytes);
294 MD5Update(&md5c, keystr1, servkey.bytes);
295 MD5Update(&md5c, pktin.body, 8);
296
297 MD5Final(session_id, &md5c);
298
299 for (i=0; i<32; i++)
300 session_key[i] = random_byte();
301
302 len = (hostkey.bytes > servkey.bytes ? hostkey.bytes : servkey.bytes);
303
304 rsabuf = malloc(len);
305 if (!rsabuf)
306 fatalbox("Out of memory");
307
308 /*
309 * Verify the host key.
310 */
311 {
312 /*
313 * First format the key into a string.
314 */
315 int len = rsastr_len(&hostkey);
316 char *keystr = malloc(len);
317 if (!keystr)
318 fatalbox("Out of memory");
319 rsastr_fmt(keystr, &hostkey);
320 verify_ssh_host_key(savedhost, keystr);
321 free(keystr);
322 }
323
324 for (i=0; i<32; i++) {
325 rsabuf[i] = session_key[i];
326 if (i < 16)
327 rsabuf[i] ^= session_id[i];
328 }
329
330 if (hostkey.bytes > servkey.bytes) {
331 rsaencrypt(rsabuf, 32, &servkey);
332 rsaencrypt(rsabuf, servkey.bytes, &hostkey);
333 } else {
334 rsaencrypt(rsabuf, 32, &hostkey);
335 rsaencrypt(rsabuf, hostkey.bytes, &servkey);
336 }
337
338 cipher_type = cfg.cipher == CIPHER_BLOWFISH ? SSH_CIPHER_BLOWFISH :
339 SSH_CIPHER_3DES;
340 if ((supported_ciphers_mask & (1 << cipher_type)) == 0) {
341 fprintf(stderr, "Selected cipher not supported, falling back to 3DES\n");
342 cipher_type = SSH_CIPHER_3DES;
343 }
344
345 s_wrpkt_start(SSH_CMSG_SESSION_KEY, len+15);
346 pktout.body[0] = cipher_type;
347 memcpy(pktout.body+1, cookie, 8);
348 pktout.body[9] = (len*8) >> 8;
349 pktout.body[10] = (len*8) & 0xFF;
350 memcpy(pktout.body+11, rsabuf, len);
351 pktout.body[len+11] = pktout.body[len+12] = 0; /* protocol flags */
352 pktout.body[len+13] = pktout.body[len+14] = 0;
353 s_wrpkt();
354
355 free(rsabuf);
356
357 cipher = cipher_type == SSH_CIPHER_BLOWFISH ? &ssh_blowfish :
358 &ssh_3des;
359 cipher->sesskey(session_key);
360
361 get_packet();
362
363 if (pktin.type != SSH_SMSG_SUCCESS)
364 fatalbox("Encryption not successfully enabled");
365
366 if (verbose)
367 fprintf(stderr, "Logging in as \"%s\".\n", username);
368 s_wrpkt_start(SSH_CMSG_USER, 4+strlen(username));
369 pktout.body[0] = pktout.body[1] = pktout.body[2] = 0;
370 pktout.body[3] = strlen(username);
371 memcpy(pktout.body+4, username, strlen(username));
372 s_wrpkt();
373
374 get_packet();
375
376 while (pktin.type == SSH_SMSG_FAILURE) {
377 char password[100];
378 char prompt[200];
379 sprintf(prompt, "%s@%s's password: ", username, savedhost);
380 ssh_get_password(prompt, password, 100);
381 s_wrpkt_start(SSH_CMSG_AUTH_PASSWORD, 4+strlen(password));
382 pktout.body[0] = pktout.body[1] = pktout.body[2] = 0;
383 pktout.body[3] = strlen(password);
384 memcpy(pktout.body+4, password, strlen(password));
385 s_wrpkt();
386 memset(password, 0, strlen(password));
387 get_packet();
388 if (pktin.type == SSH_SMSG_FAILURE) {
389 fprintf(stderr, "Access denied\n");
390 } else if (pktin.type != SSH_SMSG_SUCCESS) {
391 fatalbox("Strange packet received, type %d", pktin.type);
392 }
393 }
394
395 /* Execute command */
396 if (verbose)
397 fprintf(stderr, "Sending command: %s\n", cmd);
398 i = strlen(cmd);
399 s_wrpkt_start(SSH_CMSG_EXEC_CMD, 4+i);
400 PUT_32BIT(pktout.body, i);
401 memcpy(pktout.body+4, cmd, i);
402 s_wrpkt();
403 }
404
405
406 /*
407 * Receive a block of data over the SSH link. Block until
408 * all data is available. Return nr of bytes read (0 if lost connection).
409 */
410 int ssh_recv(unsigned char *buf, int len)
411 {
412 static int pending_input_len = 0;
413 static unsigned char *pending_input_ptr;
414 int to_read = len;
415
416 if (pending_input_len >= to_read) {
417 memcpy(buf, pending_input_ptr, to_read);
418 pending_input_ptr += to_read;
419 pending_input_len -= to_read;
420 return len;
421 }
422
423 if (pending_input_len > 0) {
424 memcpy(buf, pending_input_ptr, pending_input_len);
425 buf += pending_input_len;
426 to_read -= pending_input_len;
427 pending_input_len = 0;
428 }
429
430 if (s == INVALID_SOCKET)
431 return 0;
432 while (to_read > 0) {
433 get_packet();
434 if (s == INVALID_SOCKET)
435 return 0;
436 if (pktin.type == SSH_SMSG_STDOUT_DATA) {
437 int plen = GET_32BIT(pktin.body);
438 if (plen+4 != pktin.length) {
439 fprintf(stderr, "Received data packet with bogus string length"
440 ", ignoring\n");
441 } else if (plen <= to_read) {
442 memcpy(buf, pktin.body + 4, plen);
443 buf += plen;
444 to_read -= plen;
445 } else {
446 memcpy(buf, pktin.body + 4, to_read);
447 pending_input_len = plen - to_read;
448 pending_input_ptr = pktin.body + 4 + to_read;
449 to_read = 0;
450 }
451 } else if (pktin.type == SSH_SMSG_STDERR_DATA) {
452 int plen = GET_32BIT(pktin.body);
453 if (plen+4 != pktin.length) {
454 fprintf(stderr, "Received data packet with bogus string length"
455 ", ignoring\n");
456 } else
457 fwrite(pktin.body + 4, plen, 1, stderr);
458 } else if (pktin.type == SSH_MSG_DISCONNECT) {
459 } else if (pktin.type == SSH_SMSG_SUCCESS ||
460 pktin.type == SSH_SMSG_FAILURE) {
461 } else if (pktin.type == SSH_SMSG_EXIT_STATUS) {
462 if (verbose)
463 fprintf(stderr, "Remote exit status %d\n",
464 GET_32BIT(pktin.body));
465 s_wrpkt_start(SSH_CMSG_EXIT_CONFIRMATION, 0);
466 s_wrpkt();
467 if (verbose)
468 fprintf(stderr, "Closing connection\n");
469 closesocket(s);
470 s = INVALID_SOCKET;
471 }
472 }
473
474 return len;
475 }
476
477
478 /*
479 * Send a block of data over the SSH link.
480 * Block until all data is sent.
481 */
482 void ssh_send(unsigned char *buf, int len)
483 {
484 if (s == INVALID_SOCKET)
485 return;
486 s_wrpkt_start(SSH_CMSG_STDIN_DATA, 4 + len);
487 PUT_32BIT(pktout.body, len);
488 memcpy(pktout.body + 4, buf, len);
489 s_wrpkt();
490 }
491
492
493 /*
494 * Send an EOF notification to the server.
495 */
496 void ssh_send_eof(void)
497 {
498 if (s == INVALID_SOCKET)
499 return;
500 s_wrpkt_start(SSH_CMSG_EOF, 0);
501 s_wrpkt();
502 }
503
504
505 /*
506 * Set up the connection, login on the remote host and
507 * start execution of a command.
508 *
509 * Returns an error message, or NULL on success.
510 *
511 * Also places the canonical host name into `realhost'.
512 */
513 char *ssh_init(char *host, int port, char *cmd, char **realhost) {
514 SOCKADDR_IN addr;
515 struct hostent *h;
516 unsigned long a;
517 #ifdef FWHACK
518 char *FWhost;
519 int FWport;
520 #endif
521
522 #ifdef MSCRYPTOAPI
523 if(crypto_startup() == 0)
524 return "Microsoft high encryption pack not installed!";
525 #endif
526
527 savedhost = malloc(1+strlen(host));
528 if (!savedhost)
529 fatalbox("Out of memory");
530 strcpy(savedhost, host);
531
532 #ifdef FWHACK
533 FWhost = host;
534 FWport = port;
535 host = FWSTR;
536 port = 23;
537 #endif
538
539 /*
540 * Try to find host.
541 */
542 if ( (a = inet_addr(host)) == (unsigned long) INADDR_NONE) {
543 if ( (h = gethostbyname(host)) == NULL)
544 switch (WSAGetLastError()) {
545 case WSAENETDOWN: return "Network is down";
546 case WSAHOST_NOT_FOUND: case WSANO_DATA:
547 return "Host does not exist";
548 case WSATRY_AGAIN: return "Host not found";
549 default: return "gethostbyname: unknown error";
550 }
551 memcpy (&a, h->h_addr, sizeof(a));
552 *realhost = h->h_name;
553 } else
554 *realhost = host;
555 #ifdef FWHACK
556 *realhost = FWhost;
557 #endif
558 a = ntohl(a);
559
560 if (port < 0)
561 port = 22; /* default ssh port */
562
563 /*
564 * Open socket.
565 */
566 s = socket(AF_INET, SOCK_STREAM, 0);
567 if (s == INVALID_SOCKET)
568 switch (WSAGetLastError()) {
569 case WSAENETDOWN: return "Network is down";
570 case WSAEAFNOSUPPORT: return "TCP/IP support not present";
571 default: return "socket(): unknown error";
572 }
573
574 /*
575 * Bind to local address.
576 */
577 addr.sin_family = AF_INET;
578 addr.sin_addr.s_addr = htonl(INADDR_ANY);
579 addr.sin_port = htons(0);
580 if (bind (s, (struct sockaddr *)&addr, sizeof(addr)) == SOCKET_ERROR)
581 switch (WSAGetLastError()) {
582 case WSAENETDOWN: return "Network is down";
583 default: return "bind(): unknown error";
584 }
585
586 /*
587 * Connect to remote address.
588 */
589 addr.sin_addr.s_addr = htonl(a);
590 addr.sin_port = htons((short)port);
591 if (connect (s, (struct sockaddr *)&addr, sizeof(addr)) == SOCKET_ERROR)
592 switch (WSAGetLastError()) {
593 case WSAENETDOWN: return "Network is down";
594 case WSAECONNREFUSED: return "Connection refused";
595 case WSAENETUNREACH: return "Network is unreachable";
596 case WSAEHOSTUNREACH: return "No route to host";
597 default: return "connect(): unknown error";
598 }
599
600 #ifdef FWHACK
601 send(s, "connect ", 8, 0);
602 send(s, FWhost, strlen(FWhost), 0);
603 {
604 char buf[20];
605 sprintf(buf, " %d\n", FWport);
606 send (s, buf, strlen(buf), 0);
607 }
608 #endif
609
610 random_init();
611
612 if (!do_ssh_init())
613 return "Protocol initialisation error";
614
615 ssh_login(cfg.username, cmd);
616
617 return NULL;
618 }
619
620 /* end */