1 /*
2  * Modified in July 2012 by Dhiru Kholia <dhiru at openwall.com> to be
3  * standalone and compilable.
4  *
5  * p-ppk-crack v0.5 made by michu@neophob.com -- PuTTY private key cracker
6  *
7  * Source code based on putty svn version, see [1] for licensing information.
8  *
9  * [1] http://www.chiark.greenend.org.uk/~sgtatham/putty/licence.html
10  */
11 
12 #ifndef PUTTY_COMMON_H
13 #define PUTTY_COMMON_H
14 #if AC_BUILT
15 #include "autoconfig.h"
16 #endif
17 
18 #include <stdint.h>
19 #include <stddef.h>  /* for size_t */
20 #include <string.h>  /* for memcpy() */
21 #include <stdio.h>
22 #include <string.h>
23 #include <stdlib.h>
24 #if !AC_BUILT || HAVE_LIMITS_H
25 #include <limits.h>
26 #endif
27 #include <sys/types.h>
28 #if !AC_BUILT || HAVE_FCNTL_H
29 #include <fcntl.h>
30 #endif
31 #include "memory.h"
32 #include "jumbo.h"
33 #if _MSC_VER
34 #include <io.h>
35 #endif
36 
37 #ifndef FALSE
38 #define FALSE 0
39 #endif
40 #ifndef TRUE
41 #define TRUE 1
42 #endif
43 
44 struct ssh2_userkey {
45 	const struct ssh_signkey *alg;  /* the key algorithm */
46 	void *data;  /* the key data */
47 	char *comment;  /* the key comment */
48 };
49 
50 enum {
51 	SSH_KEYTYPE_UNOPENABLE,
52 	SSH_KEYTYPE_UNKNOWN,
53 	SSH_KEYTYPE_SSH1, SSH_KEYTYPE_SSH2,
54 	SSH_KEYTYPE_OPENSSH, SSH_KEYTYPE_SSHCOM
55 };
56 
57 static int base64_decode_atom(char *atom, unsigned char *out);
58 #endif
59 
60 typedef struct Filename {
61 	char path[4096];
62 } Filename;
63 
64 static char header[40], *b, *encryption, *comment, *mac;
65 static const char *putty_error = NULL;
66 static int i, is_mac, old_fmt;
67 static char alg[32];
68 static int cipher, cipherblk;
69 static unsigned char *public_blob, *private_blob;
70 static int public_blob_len, private_blob_len;
71 
read_body(FILE * fp)72 static char *read_body(FILE * fp)
73 {
74 	char *text;
75 	int len;
76 	int size;
77 	int c;
78 
79 	size = 128 * 1024;
80 	text = (char*)malloc(size);
81 	if (!text) {
82 		fprintf(stderr, "malloc failed in read_body, exiting!\n");
83 		exit(-1);
84 	}
85 	len = 0;
86 	text[len] = '\0';
87 
88 	while (1) {
89 		c = fgetc(fp);
90 		if (c == '\r' || c == '\n') {
91 			c = fgetc(fp);
92 			if (c != '\r' && c != '\n' && c != EOF)
93 				ungetc(c, fp);
94 			return text;
95 		}
96 		if (c == EOF) {
97 			MEM_FREE(text);
98 			return NULL;
99 		}
100 		text[len++] = c;
101 		text[len] = '\0';
102 	}
103 }
104 
read_blob(FILE * fp,int nlines,int * bloblen)105 static unsigned char *read_blob(FILE * fp, int nlines, int *bloblen)
106 {
107 	unsigned char *blob;
108 	char *line;
109 	int linelen, len;
110 	int i, j, k;
111 
112 	/* Sanity check nlines */
113 	if (nlines < 0 || nlines > (1024 * 1024))
114 		return NULL;
115 
116 	/* We expect at most 64 base64 characters, ie 48 real bytes, per line. */
117 	blob = (unsigned char*)malloc(48 * nlines);
118 	if (!blob) {
119 		fprintf(stderr, "malloc failed in read_blob, exiting!\n");
120 		exit(-1);
121 	}
122 	len = 0;
123 	for (i = 0; i < nlines; i++) {
124 		line = read_body(fp);
125 		if (!line) {
126 			MEM_FREE(blob);
127 			return NULL;
128 		}
129 		linelen = strlen(line);
130 		if (linelen % 4 != 0 || linelen > 64) {
131 			MEM_FREE(blob);
132 			MEM_FREE(line);
133 			line = NULL;
134 			return NULL;
135 		}
136 		for (j = 0; j < linelen; j += 4) {
137 			k = base64_decode_atom(line + j, blob + len);
138 			if (!k) {
139 				MEM_FREE(blob);
140 				MEM_FREE(line);
141 				return NULL;
142 			}
143 			len += k;
144 		}
145 		MEM_FREE(line);
146 	}
147 	*bloblen = len;
148 
149 	return blob;
150 }
151 
read_header(FILE * fp,char * header)152 static int read_header(FILE * fp, char *header)
153 {
154 	int len = 39;
155 	int c;
156 
157 	while (len > 0) {
158 		c = fgetc(fp);
159 		if (c == '\n' || c == '\r' || c == EOF)
160 			return 0;  /* failure */
161 		if (c == ':') {
162 			c = fgetc(fp);
163 			if (c != ' ')
164 				return 0;
165 			*header = '\0';
166 			return 1;  /* success! */
167 		}
168 		if (len == 0)
169 			return 0;  /* failure */
170 		*header++ = c;
171 		len--;
172 	}
173 	return 0;  /* failure */
174 }
175 
176 
init_LAME(const Filename * filename)177 static int init_LAME(const Filename *filename) {
178 	FILE *fp;
179 
180 	encryption = comment = mac = NULL;
181 	public_blob = private_blob = NULL;
182 
183 	fp = fopen(filename->path, "rb" );
184 	if (!fp) {
185 		putty_error = "can't open file";
186 		goto error;
187 	}
188 
189 	/* Read the first header line which contains the key type. */
190 	if (!read_header(fp, header))
191 		goto error;
192 	if (0 == strcmp(header, "PuTTY-User-Key-File-2")) {
193 		old_fmt = 0;
194 	} else if (0 == strcmp(header, "PuTTY-User-Key-File-1")) {
195 		/* this is an old key file; warn and then continue */
196 		// old_keyfile_warning();
197 		old_fmt = 1;
198 	} else {
199 		putty_error = "not a PuTTY SSH-2 private key";
200 		goto error;
201 	}
202 	putty_error = "file format error";
203 	if ((b = read_body(fp)) == NULL)
204 		goto error;
205 	/* Select key algorithm structure. */
206 	if (!strcmp(b, "ssh-rsa"))
207 		strcpy(alg, "ssh-rsa");
208 	else if (!strcmp(b, "ssh-dss"))
209 		strcpy(alg, "ssh-dss");
210 	else if (!strcmp(b, "ecdsa-sha2-nistp256"))
211 		strcpy(alg, "ecdsa-sha2-nistp256");
212 	else if (strlen(b) < sizeof(alg) - 1)
213 		strcpy(alg, b);
214 	MEM_FREE(b);
215 
216 	/* Read the Encryption header line. */
217 	if (!read_header(fp, header) || 0 != strcmp(header, "Encryption"))
218 		goto error;
219 	if ((encryption = read_body(fp)) == NULL)
220 		goto error;
221 	if (!strcmp(encryption, "aes256-cbc")) {
222 		cipher = 1;
223 		cipherblk = 16;
224 	} else if (!strcmp(encryption, "none")) {
225 		cipher = 0;
226 		cipherblk = 1;
227 	} else {
228 		MEM_FREE(encryption);
229 		goto error;
230 	}
231 
232 	/* Read the Comment header line. */
233 	if (!read_header(fp, header) || 0 != strcmp(header, "Comment"))
234 		goto error;
235 	if ((comment = read_body(fp)) == NULL)
236 		goto error;
237 
238 	/* Read the Public-Lines header line and the public blob. */
239 	if (!read_header(fp, header) || 0 != strcmp(header, "Public-Lines"))
240 		goto error;
241 	if ((b = read_body(fp)) == NULL)
242 		goto error;
243 	i = atoi(b);
244 	MEM_FREE(b);
245 	if ((public_blob = read_blob(fp, i, &public_blob_len)) == NULL)
246 		goto error;
247 
248 	/* Read the Private-Lines header line and the Private blob. */
249 	if (!read_header(fp, header) || 0 != strcmp(header, "Private-Lines"))
250 		goto error;
251 	if ((b = read_body(fp)) == NULL)
252 		goto error;
253 	i = atoi(b);
254 	MEM_FREE(b);
255 	if ((private_blob = read_blob(fp, i, &private_blob_len)) == NULL)
256 		goto error;
257 
258 	/* Read the Private-MAC or Private-Hash header line. */
259 	if (!read_header(fp, header))
260 		goto error;
261 	if (0 == strcmp(header, "Private-MAC")) {
262 		if ((mac = read_body(fp)) == NULL)
263 			goto error;
264 		is_mac = 1;
265 	} else if (0 == strcmp(header, "Private-Hash") && old_fmt) {
266 		if ((mac = read_body(fp)) == NULL)
267 			goto error;
268 		is_mac = 0;
269 	} else
270 		goto error;
271 
272 	fclose(fp);
273 	fp = NULL;
274 	return 0;
275 
276 error:
277 	if (fp)
278 		fclose(fp);
279 	MEM_FREE(comment);
280 	MEM_FREE(encryption);
281 	MEM_FREE(mac);
282 	MEM_FREE(public_blob);
283 	MEM_FREE(private_blob);
284 	return 1;
285 }
286 
print_hex(unsigned char * str,int len)287 static void print_hex(unsigned char *str, int len)
288 {
289 	int i;
290 	for (i = 0; i < len; ++i)
291 		printf("%02x", str[i]);
292 }
293 
LAME_ssh2_load_userkey(char * path,const char ** errorstr)294 static void LAME_ssh2_load_userkey(char *path, const char **errorstr)
295 {
296 	const char *ext[] = {".ppk"};
297 	char *fname;
298 	/*
299 	* Decrypt the private blob.
300 	*/
301 	if (cipher) {
302 		if (private_blob_len % cipherblk)
303 			goto error;
304 	}
305 
306 	{
307 		fname = strip_suffixes(basename(path), ext, 1);
308 		printf("%s:$putty$%d*%d*%d*%d*%s*%d*", fname, cipher, cipherblk, is_mac, old_fmt, mac, public_blob_len);
309 		print_hex(public_blob, public_blob_len);
310 		printf("*%d*", private_blob_len);
311 		print_hex(private_blob, private_blob_len);
312 		if (!old_fmt) {
313 			printf("*%s*%s*%s\n", alg, encryption, comment);
314 		}
315 		else {
316 			printf("\n");
317 		}
318 		MEM_FREE(comment);
319 		return;
320 	}
321 error:
322 	fprintf(stderr, "Something failed!\n");
323 	MEM_FREE(comment);
324 	MEM_FREE(encryption);
325 	MEM_FREE(mac);
326 	MEM_FREE(public_blob);
327 	MEM_FREE(private_blob);
328 }
329 
f_open(const Filename * filename,char const * mode,int is_private)330 static FILE *f_open(const Filename *filename, char const *mode, int is_private)
331 {
332 	if (!is_private) {
333 		return fopen(filename->path, mode);
334 	} else {
335 		int fd;
336 		fd = open(filename->path, O_WRONLY | O_CREAT | O_TRUNC, 0600);
337 		if (fd < 0)
338 			return NULL;
339 		return fdopen(fd, mode);
340 	}
341 }
342 
343 /* ----------------------------------------------------------------------
344  * A function to determine the type of a private key file. Returns
345  * 0 on failure, 1 or 2 on success.
346  */
347 #define rsa_signature "SSH PRIVATE KEY FILE FORMAT 1.1\n"
348 
key_type(const Filename * filename)349 static int key_type(const Filename *filename)
350 {
351 	FILE *fp;
352 	char buf[32];
353 	const char putty2_sig[] = "PuTTY-User-Key-File-";
354 	const char sshcom_sig[] = "---- BEGIN SSH2 ENCRYPTED PRIVAT";
355 	const char openssh_sig[] = "-----BEGIN ";
356 	int i;
357 
358 	fp = f_open(filename, "r", FALSE);
359 	if (!fp)
360 		return SSH_KEYTYPE_UNOPENABLE;
361 	i = fread(buf, 1, sizeof(buf), fp);
362 	fclose(fp);
363 	if (i < 0)
364 		return SSH_KEYTYPE_UNOPENABLE;
365 	if (i < 32)
366 		return SSH_KEYTYPE_UNKNOWN;
367 	if (!memcmp(buf, rsa_signature, sizeof(rsa_signature)-1))
368 		return SSH_KEYTYPE_SSH1;
369 	if (!memcmp(buf, putty2_sig, sizeof(putty2_sig)-1))
370 		return SSH_KEYTYPE_SSH2;
371 	if (!memcmp(buf, openssh_sig, sizeof(openssh_sig)-1))
372 		return SSH_KEYTYPE_OPENSSH;
373 	if (!memcmp(buf, sshcom_sig, sizeof(sshcom_sig)-1))
374 		return SSH_KEYTYPE_SSHCOM;
375 	return SSH_KEYTYPE_UNKNOWN;	       /* unrecognised or EOF */
376 }
377 
ssh2_userkey_encrypted(const Filename * filename,char ** commentptr)378 static int ssh2_userkey_encrypted(const Filename *filename, char **commentptr)
379 {
380 	FILE *fp;
381 	char header[40], *b, *comment;
382 	int ret;
383 
384 	if (commentptr)
385 		*commentptr = NULL;
386 
387 	fp = f_open(filename, "rb", FALSE);
388 	if (!fp)
389 		return 0;
390 	if (!read_header(fp, header)
391 			|| (0 != strcmp(header, "PuTTY-User-Key-File-2") &&
392 				0 != strcmp(header, "PuTTY-User-Key-File-1"))) {
393 		fclose(fp);
394 		return 0;
395 	}
396 	if ((b = read_body(fp)) == NULL) {
397 		fclose(fp);
398 		return 0;
399 	}
400 	MEM_FREE(b);  /* we don't care about key type here */
401 	/* Read the Encryption header line. */
402 	if (!read_header(fp, header) || 0 != strcmp(header, "Encryption")) {
403 		fclose(fp);
404 		return 0;
405 	}
406 	if ((b = read_body(fp)) == NULL) {
407 		fclose(fp);
408 		return 0;
409 	}
410 
411 	/* Read the Comment header line. */
412 	if (!read_header(fp, header) || 0 != strcmp(header, "Comment")) {
413 		fclose(fp);
414 		MEM_FREE(b);
415 		return 1;
416 	}
417 	if ((comment = read_body(fp)) == NULL) {
418 		fclose(fp);
419 		MEM_FREE(b);
420 		return 1;
421 	}
422 
423 	if (commentptr)
424 		*commentptr = comment;
425 
426 	fclose(fp);
427 	if (!strcmp(b, "aes256-cbc"))
428 		ret = 1;
429 	else
430 		ret = 0;
431 	MEM_FREE(b);
432 	return ret;
433 }
434 
base64_decode_atom(char * atom,unsigned char * out)435 static int base64_decode_atom(char *atom, unsigned char *out)
436 {
437 	int vals[4];
438 	int i, v, len;
439 	unsigned word;
440 	char c;
441 
442 	for (i = 0; i < 4; i++) {
443 		c = atom[i];
444 		if (c >= 'A' && c <= 'Z')
445 			v = c - 'A';
446 		else if (c >= 'a' && c <= 'z')
447 			v = c - 'a' + 26;
448 		else if (c >= '0' && c <= '9')
449 			v = c - '0' + 52;
450 		else if (c == '+')
451 			v = 62;
452 		else if (c == '/')
453 			v = 63;
454 		else if (c == '=')
455 			v = -1;
456 		else
457 			return 0;  /* invalid atom */
458 		vals[i] = v;
459 	}
460 
461 	if (vals[0] == -1 || vals[1] == -1)
462 		return 0;
463 	if (vals[2] == -1 && vals[3] != -1)
464 		return 0;
465 
466 	if (vals[3] != -1)
467 		len = 3;
468 	else if (vals[2] != -1)
469 		len = 2;
470 	else
471 		len = 1;
472 
473 	word = ((vals[0] << 18) |
474 			(vals[1] << 12) | ((vals[2] & 0x3F) << 6) | (vals[3] & 0x3F));
475 	out[0] = (word >> 16) & 0xFF;
476 	if (len > 1)
477 		out[1] = (word >> 8) & 0xFF;
478 	if (len > 2)
479 		out[2] = word & 0xFF;
480 	return len;
481 }
482 
process_file(const char * fname)483 static void process_file(const char *fname)
484 {
485 	FILE *fp;
486 	int type, realtype;
487 	char *comment;
488 	Filename filename;
489 	int needs_pass = 0;
490 	const char *errmsg = NULL;
491 
492 	/* check if file exists */
493 	if ((fp = fopen(fname, "r")) == NULL) {
494 		fprintf(stderr, "Error: Cannot open %s.\n", fname);
495 		return;
496 	}
497 	fclose(fp);
498 
499 	strcpy(filename.path, fname);
500 
501 	// src: winpgen.c
502 	type = realtype = key_type(&filename);
503 	if (type != SSH_KEYTYPE_SSH1 && type != SSH_KEYTYPE_SSH2) {
504 		fprintf(stderr, "Error: Couldn't load private key (%s)\n", filename.path);
505 		return;
506 	}
507 	if (type == SSH_KEYTYPE_SSH1) {
508 		fprintf(stderr, "%s : SSH1 RSA private keys are not supported currently!\n", filename.path);
509 		return;
510 
511 	}
512 	if (type != SSH_KEYTYPE_SSH1 && type != SSH_KEYTYPE_SSH2) {
513 		realtype = type;
514 	}
515 
516 	comment = NULL;
517 	if (realtype == SSH_KEYTYPE_SSH2) {
518 		needs_pass = ssh2_userkey_encrypted(&filename, &comment);
519 		if (needs_pass == 0) {
520 			fprintf(stderr, "%s : this private key doesn't need a passphrase!\n", fname);
521 			goto out;
522 		}
523 	}
524 
525 	if (init_LAME(&filename)==1) {
526 		fprintf(stderr, "error, not valid private key!\n");
527 		goto out;
528 	}
529 	if (type == SSH_KEYTYPE_SSH1) {
530 		fprintf(stderr, "SSH1 key type not supported!\n");
531 		goto out;
532 	} else { // SSH_KEYTYPE_SSH2
533 		if (realtype == type) {
534 			LAME_ssh2_load_userkey(filename.path, &errmsg);
535 		}
536 	}
537 
538 out:
539 
540 	MEM_FREE(comment);
541 	MEM_FREE(encryption);
542 	MEM_FREE(mac);
543 	MEM_FREE(public_blob);
544 	MEM_FREE(private_blob);
545 }
546 
547 #ifdef HAVE_LIBFUZZER
LLVMFuzzerTestOneInput(const uint8_t * data,size_t size)548 int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
549 {
550 	int fd;
551 	char name[] = "/tmp/libFuzzer-XXXXXX";
552 
553 	fd = mkstemp(name);
554 	if (fd < 0) {
555 		fprintf(stderr, "Problem detected while creating the input file, %s, aborting!\n", strerror(errno));
556 		exit(-1);
557 	}
558 	write(fd, data, size);
559 	close(fd);
560 	process_file(name);
561 	remove(name);
562 
563 	return 0;
564 }
565 #else
main(int argc,char ** argv)566 int main(int argc, char **argv)
567 {
568 	int i;
569 
570 	if (argc < 2) {
571 		printf( "Usage: putty2john [.ppk PuTTY-Private-Key-File(s)]\n");
572 		printf( "\nKey types supported: RSA, DSA, ECDSA, ED25519\n");
573 		exit(1);
574 	}
575 
576 	for (i = 1; i < argc; i++)
577 		process_file(argv[i]);
578 
579 	return 0;
580 }
581 #endif
582