1 /* Copyright (C) 2007 The Written Word, Inc.
2 * Copyright (C) 2008, Simon Josefsson
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms,
6 * with or without modification, are permitted provided
7 * that the following conditions are met:
8 *
9 * Redistributions of source code must retain the above
10 * copyright notice, this list of conditions and the
11 * following disclaimer.
12 *
13 * Redistributions in binary form must reproduce the above
14 * copyright notice, this list of conditions and the following
15 * disclaimer in the documentation and/or other materials
16 * provided with the distribution.
17 *
18 * Neither the name of the copyright holder nor the names
19 * of any other contributors may be used to endorse or
20 * promote products derived from this software without
21 * specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
24 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
25 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
26 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
28 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
29 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
30 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
31 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
32 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
33 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
34 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
35 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
36 * OF SUCH DAMAGE.
37 */
38
39 #include "libssh2_priv.h"
40
41 static int
readline(char * line,int line_size,FILE * fp)42 readline(char *line, int line_size, FILE * fp)
43 {
44 size_t len;
45
46 if(!line) {
47 return -1;
48 }
49 if(!fgets(line, line_size, fp)) {
50 return -1;
51 }
52
53 if(*line) {
54 len = strlen(line);
55 if(len > 0 && line[len - 1] == '\n') {
56 line[len - 1] = '\0';
57 }
58 }
59
60 if(*line) {
61 len = strlen(line);
62 if(len > 0 && line[len - 1] == '\r') {
63 line[len - 1] = '\0';
64 }
65 }
66
67 return 0;
68 }
69
70 static int
readline_memory(char * line,size_t line_size,const char * filedata,size_t filedata_len,size_t * filedata_offset)71 readline_memory(char *line, size_t line_size,
72 const char *filedata, size_t filedata_len,
73 size_t *filedata_offset)
74 {
75 size_t off, len;
76
77 off = *filedata_offset;
78
79 for(len = 0; off + len < filedata_len && len < line_size - 1; len++) {
80 if(filedata[off + len] == '\n' ||
81 filedata[off + len] == '\r') {
82 break;
83 }
84 }
85
86 if(len) {
87 memcpy(line, filedata + off, len);
88 *filedata_offset += len;
89 }
90
91 line[len] = '\0';
92 *filedata_offset += 1;
93
94 return 0;
95 }
96
97 #define LINE_SIZE 128
98
99 static const char *crypt_annotation = "Proc-Type: 4,ENCRYPTED";
100
hex_decode(char digit)101 static unsigned char hex_decode(char digit)
102 {
103 return (digit >= 'A') ? 0xA + (digit - 'A') : (digit - '0');
104 }
105
106 int
_libssh2_pem_parse(LIBSSH2_SESSION * session,const char * headerbegin,const char * headerend,const unsigned char * passphrase,FILE * fp,unsigned char ** data,unsigned int * datalen)107 _libssh2_pem_parse(LIBSSH2_SESSION * session,
108 const char *headerbegin,
109 const char *headerend,
110 const unsigned char *passphrase,
111 FILE * fp, unsigned char **data, unsigned int *datalen)
112 {
113 char line[LINE_SIZE];
114 unsigned char iv[LINE_SIZE];
115 char *b64data = NULL;
116 unsigned int b64datalen = 0;
117 int ret;
118 const LIBSSH2_CRYPT_METHOD *method = NULL;
119
120 do {
121 *line = '\0';
122
123 if(readline(line, LINE_SIZE, fp)) {
124 return -1;
125 }
126 }
127 while(strcmp(line, headerbegin) != 0);
128
129 if(readline(line, LINE_SIZE, fp)) {
130 return -1;
131 }
132
133 if(passphrase &&
134 memcmp(line, crypt_annotation, strlen(crypt_annotation)) == 0) {
135 const LIBSSH2_CRYPT_METHOD **all_methods, *cur_method;
136 int i;
137
138 if(readline(line, LINE_SIZE, fp)) {
139 ret = -1;
140 goto out;
141 }
142
143 all_methods = libssh2_crypt_methods();
144 while((cur_method = *all_methods++)) {
145 if(*cur_method->pem_annotation &&
146 memcmp(line, cur_method->pem_annotation,
147 strlen(cur_method->pem_annotation)) == 0) {
148 method = cur_method;
149 memcpy(iv, line + strlen(method->pem_annotation) + 1,
150 2*method->iv_len);
151 }
152 }
153
154 /* None of the available crypt methods were able to decrypt the key */
155 if(method == NULL)
156 return -1;
157
158 /* Decode IV from hex */
159 for(i = 0; i < method->iv_len; ++i) {
160 iv[i] = hex_decode(iv[2*i]) << 4;
161 iv[i] |= hex_decode(iv[2*i + 1]);
162 }
163
164 /* skip to the next line */
165 if(readline(line, LINE_SIZE, fp)) {
166 ret = -1;
167 goto out;
168 }
169 }
170
171 do {
172 if(*line) {
173 char *tmp;
174 size_t linelen;
175
176 linelen = strlen(line);
177 tmp = LIBSSH2_REALLOC(session, b64data, b64datalen + linelen);
178 if(!tmp) {
179 _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
180 "Unable to allocate memory for PEM parsing");
181 ret = -1;
182 goto out;
183 }
184 memcpy(tmp + b64datalen, line, linelen);
185 b64data = tmp;
186 b64datalen += linelen;
187 }
188
189 *line = '\0';
190
191 if(readline(line, LINE_SIZE, fp)) {
192 ret = -1;
193 goto out;
194 }
195 } while(strcmp(line, headerend) != 0);
196
197 if(!b64data) {
198 return -1;
199 }
200
201 if(libssh2_base64_decode(session, (char **) data, datalen,
202 b64data, b64datalen)) {
203 ret = -1;
204 goto out;
205 }
206
207 if(method) {
208 /* Set up decryption */
209 int free_iv = 0, free_secret = 0, len_decrypted = 0, padding = 0;
210 int blocksize = method->blocksize;
211 void *abstract;
212 unsigned char secret[2*MD5_DIGEST_LENGTH];
213 libssh2_md5_ctx fingerprint_ctx;
214
215 /* Perform key derivation (PBKDF1/MD5) */
216 if(!libssh2_md5_init(&fingerprint_ctx)) {
217 ret = -1;
218 goto out;
219 }
220 libssh2_md5_update(fingerprint_ctx, passphrase,
221 strlen((char *)passphrase));
222 libssh2_md5_update(fingerprint_ctx, iv, 8);
223 libssh2_md5_final(fingerprint_ctx, secret);
224 if(method->secret_len > MD5_DIGEST_LENGTH) {
225 if(!libssh2_md5_init(&fingerprint_ctx)) {
226 ret = -1;
227 goto out;
228 }
229 libssh2_md5_update(fingerprint_ctx, secret, MD5_DIGEST_LENGTH);
230 libssh2_md5_update(fingerprint_ctx, passphrase,
231 strlen((char *)passphrase));
232 libssh2_md5_update(fingerprint_ctx, iv, 8);
233 libssh2_md5_final(fingerprint_ctx, secret + MD5_DIGEST_LENGTH);
234 }
235
236 /* Initialize the decryption */
237 if(method->init(session, method, iv, &free_iv, secret,
238 &free_secret, 0, &abstract)) {
239 _libssh2_explicit_zero((char *)secret, sizeof(secret));
240 LIBSSH2_FREE(session, data);
241 ret = -1;
242 goto out;
243 }
244
245 if(free_secret) {
246 _libssh2_explicit_zero((char *)secret, sizeof(secret));
247 }
248
249 /* Do the actual decryption */
250 if((*datalen % blocksize) != 0) {
251 _libssh2_explicit_zero((char *)secret, sizeof(secret));
252 method->dtor(session, &abstract);
253 _libssh2_explicit_zero(*data, *datalen);
254 LIBSSH2_FREE(session, *data);
255 ret = -1;
256 goto out;
257 }
258
259 while(len_decrypted <= (int)*datalen - blocksize) {
260 if(method->crypt(session, *data + len_decrypted, blocksize,
261 &abstract)) {
262 ret = LIBSSH2_ERROR_DECRYPT;
263 _libssh2_explicit_zero((char *)secret, sizeof(secret));
264 method->dtor(session, &abstract);
265 _libssh2_explicit_zero(*data, *datalen);
266 LIBSSH2_FREE(session, *data);
267 goto out;
268 }
269
270 len_decrypted += blocksize;
271 }
272
273 /* Account for padding */
274 padding = (*data)[*datalen - 1];
275 memset(&(*data)[*datalen-padding], 0, padding);
276 *datalen -= padding;
277
278 /* Clean up */
279 _libssh2_explicit_zero((char *)secret, sizeof(secret));
280 method->dtor(session, &abstract);
281 }
282
283 ret = 0;
284 out:
285 if(b64data) {
286 _libssh2_explicit_zero(b64data, b64datalen);
287 LIBSSH2_FREE(session, b64data);
288 }
289 return ret;
290 }
291
292 int
_libssh2_pem_parse_memory(LIBSSH2_SESSION * session,const char * headerbegin,const char * headerend,const char * filedata,size_t filedata_len,unsigned char ** data,unsigned int * datalen)293 _libssh2_pem_parse_memory(LIBSSH2_SESSION * session,
294 const char *headerbegin,
295 const char *headerend,
296 const char *filedata, size_t filedata_len,
297 unsigned char **data, unsigned int *datalen)
298 {
299 char line[LINE_SIZE];
300 char *b64data = NULL;
301 unsigned int b64datalen = 0;
302 size_t off = 0;
303 int ret;
304
305 do {
306 *line = '\0';
307
308 if(readline_memory(line, LINE_SIZE, filedata, filedata_len, &off)) {
309 return -1;
310 }
311 }
312 while(strcmp(line, headerbegin) != 0);
313
314 *line = '\0';
315
316 do {
317 if(*line) {
318 char *tmp;
319 size_t linelen;
320
321 linelen = strlen(line);
322 tmp = LIBSSH2_REALLOC(session, b64data, b64datalen + linelen);
323 if(!tmp) {
324 _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
325 "Unable to allocate memory for PEM parsing");
326 ret = -1;
327 goto out;
328 }
329 memcpy(tmp + b64datalen, line, linelen);
330 b64data = tmp;
331 b64datalen += linelen;
332 }
333
334 *line = '\0';
335
336 if(readline_memory(line, LINE_SIZE, filedata, filedata_len, &off)) {
337 ret = -1;
338 goto out;
339 }
340 } while(strcmp(line, headerend) != 0);
341
342 if(!b64data) {
343 return -1;
344 }
345
346 if(libssh2_base64_decode(session, (char **) data, datalen,
347 b64data, b64datalen)) {
348 ret = -1;
349 goto out;
350 }
351
352 ret = 0;
353 out:
354 if(b64data) {
355 _libssh2_explicit_zero(b64data, b64datalen);
356 LIBSSH2_FREE(session, b64data);
357 }
358 return ret;
359 }
360
361 /* OpenSSH formatted keys */
362 #define AUTH_MAGIC "openssh-key-v1"
363 #define OPENSSH_HEADER_BEGIN "-----BEGIN OPENSSH PRIVATE KEY-----"
364 #define OPENSSH_HEADER_END "-----END OPENSSH PRIVATE KEY-----"
365
366 static int
_libssh2_openssh_pem_parse_data(LIBSSH2_SESSION * session,const unsigned char * passphrase,const char * b64data,size_t b64datalen,struct string_buf ** decrypted_buf)367 _libssh2_openssh_pem_parse_data(LIBSSH2_SESSION * session,
368 const unsigned char *passphrase,
369 const char *b64data, size_t b64datalen,
370 struct string_buf **decrypted_buf)
371 {
372 const LIBSSH2_CRYPT_METHOD *method = NULL;
373 struct string_buf decoded, decrypted, kdf_buf;
374 unsigned char *ciphername = NULL;
375 unsigned char *kdfname = NULL;
376 unsigned char *kdf = NULL;
377 unsigned char *buf = NULL;
378 unsigned char *salt = NULL;
379 uint32_t nkeys, check1, check2;
380 uint32_t rounds = 0;
381 unsigned char *key = NULL;
382 unsigned char *key_part = NULL;
383 unsigned char *iv_part = NULL;
384 unsigned char *f = NULL;
385 unsigned int f_len = 0;
386 int ret = 0, keylen = 0, ivlen = 0, total_len = 0;
387 size_t kdf_len = 0, tmp_len = 0, salt_len = 0;
388
389 if(decrypted_buf)
390 *decrypted_buf = NULL;
391
392 /* decode file */
393 if(libssh2_base64_decode(session, (char **)&f, &f_len,
394 b64data, b64datalen)) {
395 ret = -1;
396 goto out;
397 }
398
399 /* Parse the file */
400 decoded.data = (unsigned char *)f;
401 decoded.dataptr = (unsigned char *)f;
402 decoded.len = f_len;
403
404 if(decoded.len < strlen(AUTH_MAGIC)) {
405 ret = _libssh2_error(session, LIBSSH2_ERROR_PROTO, "key too short");
406 goto out;
407 }
408
409 if(strncmp((char *) decoded.dataptr, AUTH_MAGIC,
410 strlen(AUTH_MAGIC)) != 0) {
411 ret = _libssh2_error(session, LIBSSH2_ERROR_PROTO,
412 "key auth magic mismatch");
413 goto out;
414 }
415
416 decoded.dataptr += strlen(AUTH_MAGIC) + 1;
417
418 if(_libssh2_get_string(&decoded, &ciphername, &tmp_len) ||
419 tmp_len == 0) {
420 ret = _libssh2_error(session, LIBSSH2_ERROR_PROTO,
421 "ciphername is missing");
422 goto out;
423 }
424
425 if(_libssh2_get_string(&decoded, &kdfname, &tmp_len) ||
426 tmp_len == 0) {
427 ret = _libssh2_error(session, LIBSSH2_ERROR_PROTO,
428 "kdfname is missing");
429 goto out;
430 }
431
432 if(_libssh2_get_string(&decoded, &kdf, &kdf_len)) {
433 ret = _libssh2_error(session, LIBSSH2_ERROR_PROTO,
434 "kdf is missing");
435 goto out;
436 }
437 else {
438 kdf_buf.data = kdf;
439 kdf_buf.dataptr = kdf;
440 kdf_buf.len = kdf_len;
441 }
442
443 if((passphrase == NULL || strlen((const char *)passphrase) == 0) &&
444 strcmp((const char *)ciphername, "none") != 0) {
445 /* passphrase required */
446 ret = LIBSSH2_ERROR_KEYFILE_AUTH_FAILED;
447 goto out;
448 }
449
450 if(strcmp((const char *)kdfname, "none") != 0 &&
451 strcmp((const char *)kdfname, "bcrypt") != 0) {
452 ret = _libssh2_error(session, LIBSSH2_ERROR_PROTO,
453 "unknown cipher");
454 goto out;
455 }
456
457 if(!strcmp((const char *)kdfname, "none") &&
458 strcmp((const char *)ciphername, "none") != 0) {
459 ret =_libssh2_error(session, LIBSSH2_ERROR_PROTO,
460 "invalid format");
461 goto out;
462 }
463
464 if(_libssh2_get_u32(&decoded, &nkeys) != 0 || nkeys != 1) {
465 ret = _libssh2_error(session, LIBSSH2_ERROR_PROTO,
466 "Multiple keys are unsupported");
467 goto out;
468 }
469
470 /* unencrypted public key */
471
472 if(_libssh2_get_string(&decoded, &buf, &tmp_len) || tmp_len == 0) {
473 ret = _libssh2_error(session, LIBSSH2_ERROR_PROTO,
474 "Invalid private key; "
475 "expect embedded public key");
476 goto out;
477 }
478
479 if(_libssh2_get_string(&decoded, &buf, &tmp_len) || tmp_len == 0) {
480 ret = _libssh2_error(session, LIBSSH2_ERROR_PROTO,
481 "Private key data not found");
482 goto out;
483 }
484
485 /* decode encrypted private key */
486 decrypted.data = decrypted.dataptr = buf;
487 decrypted.len = tmp_len;
488
489 if(ciphername && strcmp((const char *)ciphername, "none") != 0) {
490 const LIBSSH2_CRYPT_METHOD **all_methods, *cur_method;
491
492 all_methods = libssh2_crypt_methods();
493 while((cur_method = *all_methods++)) {
494 if(*cur_method->name &&
495 memcmp(ciphername, cur_method->name,
496 strlen(cur_method->name)) == 0) {
497 method = cur_method;
498 }
499 }
500
501 /* None of the available crypt methods were able to decrypt the key */
502
503 if(method == NULL) {
504 ret = _libssh2_error(session, LIBSSH2_ERROR_PROTO,
505 "No supported cipher found");
506 goto out;
507 }
508 }
509
510 if(method) {
511 int free_iv = 0, free_secret = 0, len_decrypted = 0;
512 int blocksize;
513 void *abstract = NULL;
514
515 keylen = method->secret_len;
516 ivlen = method->iv_len;
517 total_len = keylen + ivlen;
518
519 key = LIBSSH2_CALLOC(session, total_len);
520 if(key == NULL) {
521 ret = _libssh2_error(session, LIBSSH2_ERROR_PROTO,
522 "Could not alloc key");
523 goto out;
524 }
525
526 if(strcmp((const char *)kdfname, "bcrypt") == 0 &&
527 passphrase != NULL) {
528 if((_libssh2_get_string(&kdf_buf, &salt, &salt_len)) ||
529 (_libssh2_get_u32(&kdf_buf, &rounds) != 0) ) {
530 ret = _libssh2_error(session, LIBSSH2_ERROR_PROTO,
531 "kdf contains unexpected values");
532 LIBSSH2_FREE(session, key);
533 goto out;
534 }
535
536 if(_libssh2_bcrypt_pbkdf((const char *)passphrase,
537 strlen((const char *)passphrase),
538 salt, salt_len, key,
539 keylen + ivlen, rounds) < 0) {
540 ret = _libssh2_error(session, LIBSSH2_ERROR_DECRYPT,
541 "invalid format");
542 LIBSSH2_FREE(session, key);
543 goto out;
544 }
545 }
546 else {
547 ret = _libssh2_error(session, LIBSSH2_ERROR_KEYFILE_AUTH_FAILED,
548 "bcrypted without passphrase");
549 LIBSSH2_FREE(session, key);
550 goto out;
551 }
552
553 /* Set up decryption */
554 blocksize = method->blocksize;
555
556 key_part = LIBSSH2_CALLOC(session, keylen);
557 if(key_part == NULL) {
558 ret = _libssh2_error(session, LIBSSH2_ERROR_PROTO,
559 "Could not alloc key part");
560 goto out;
561 }
562
563 iv_part = LIBSSH2_CALLOC(session, ivlen);
564 if(iv_part == NULL) {
565 ret = _libssh2_error(session, LIBSSH2_ERROR_PROTO,
566 "Could not alloc iv part");
567 goto out;
568 }
569
570 memcpy(key_part, key, keylen);
571 memcpy(iv_part, key + keylen, ivlen);
572
573 /* Initialize the decryption */
574 if(method->init(session, method, iv_part, &free_iv, key_part,
575 &free_secret, 0, &abstract)) {
576 ret = LIBSSH2_ERROR_DECRYPT;
577 goto out;
578 }
579
580 /* Do the actual decryption */
581 if((decrypted.len % blocksize) != 0) {
582 method->dtor(session, &abstract);
583 ret = LIBSSH2_ERROR_DECRYPT;
584 goto out;
585 }
586
587 while((size_t)len_decrypted <= decrypted.len - blocksize) {
588 if(method->crypt(session, decrypted.data + len_decrypted,
589 blocksize,
590 &abstract)) {
591 ret = LIBSSH2_ERROR_DECRYPT;
592 method->dtor(session, &abstract);
593 goto out;
594 }
595
596 len_decrypted += blocksize;
597 }
598
599 /* No padding */
600
601 method->dtor(session, &abstract);
602 }
603
604 /* Check random bytes match */
605
606 if(_libssh2_get_u32(&decrypted, &check1) != 0 ||
607 _libssh2_get_u32(&decrypted, &check2) != 0 ||
608 check1 != check2) {
609 _libssh2_error(session, LIBSSH2_ERROR_PROTO,
610 "Private key unpack failed (correct password?)");
611 ret = LIBSSH2_ERROR_KEYFILE_AUTH_FAILED;
612 goto out;
613 }
614
615 if(decrypted_buf != NULL) {
616 /* copy data to out-going buffer */
617 struct string_buf *out_buf = _libssh2_string_buf_new(session);
618 if(!out_buf) {
619 ret = _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
620 "Unable to allocate memory for "
621 "decrypted struct");
622 goto out;
623 }
624
625 out_buf->data = LIBSSH2_CALLOC(session, decrypted.len);
626 if(out_buf->data == NULL) {
627 ret = _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
628 "Unable to allocate memory for "
629 "decrypted struct");
630 _libssh2_string_buf_free(session, out_buf);
631 goto out;
632 }
633 memcpy(out_buf->data, decrypted.data, decrypted.len);
634 out_buf->dataptr = out_buf->data +
635 (decrypted.dataptr - decrypted.data);
636 out_buf->len = decrypted.len;
637
638 *decrypted_buf = out_buf;
639 }
640
641 out:
642
643 /* Clean up */
644 if(key) {
645 _libssh2_explicit_zero(key, total_len);
646 LIBSSH2_FREE(session, key);
647 }
648 if(key_part) {
649 _libssh2_explicit_zero(key_part, keylen);
650 LIBSSH2_FREE(session, key_part);
651 }
652 if(iv_part) {
653 _libssh2_explicit_zero(iv_part, ivlen);
654 LIBSSH2_FREE(session, iv_part);
655 }
656 if(f) {
657 _libssh2_explicit_zero(f, f_len);
658 LIBSSH2_FREE(session, f);
659 }
660
661 return ret;
662 }
663
664 int
_libssh2_openssh_pem_parse(LIBSSH2_SESSION * session,const unsigned char * passphrase,FILE * fp,struct string_buf ** decrypted_buf)665 _libssh2_openssh_pem_parse(LIBSSH2_SESSION * session,
666 const unsigned char *passphrase,
667 FILE * fp, struct string_buf **decrypted_buf)
668 {
669 char line[LINE_SIZE];
670 char *b64data = NULL;
671 unsigned int b64datalen = 0;
672 int ret = 0;
673
674 /* read file */
675
676 do {
677 *line = '\0';
678
679 if(readline(line, LINE_SIZE, fp)) {
680 return -1;
681 }
682 }
683 while(strcmp(line, OPENSSH_HEADER_BEGIN) != 0);
684
685 if(readline(line, LINE_SIZE, fp)) {
686 return -1;
687 }
688
689 do {
690 if(*line) {
691 char *tmp;
692 size_t linelen;
693
694 linelen = strlen(line);
695 tmp = LIBSSH2_REALLOC(session, b64data, b64datalen + linelen);
696 if(!tmp) {
697 _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
698 "Unable to allocate memory for PEM parsing");
699 ret = -1;
700 goto out;
701 }
702 memcpy(tmp + b64datalen, line, linelen);
703 b64data = tmp;
704 b64datalen += linelen;
705 }
706
707 *line = '\0';
708
709 if(readline(line, LINE_SIZE, fp)) {
710 ret = -1;
711 goto out;
712 }
713 } while(strcmp(line, OPENSSH_HEADER_END) != 0);
714
715 if(!b64data) {
716 return -1;
717 }
718
719 ret = _libssh2_openssh_pem_parse_data(session,
720 passphrase,
721 (const char *)b64data,
722 (size_t)b64datalen,
723 decrypted_buf);
724
725 if(b64data) {
726 _libssh2_explicit_zero(b64data, b64datalen);
727 LIBSSH2_FREE(session, b64data);
728 }
729
730 out:
731
732 return ret;
733 }
734
735 int
_libssh2_openssh_pem_parse_memory(LIBSSH2_SESSION * session,const unsigned char * passphrase,const char * filedata,size_t filedata_len,struct string_buf ** decrypted_buf)736 _libssh2_openssh_pem_parse_memory(LIBSSH2_SESSION * session,
737 const unsigned char *passphrase,
738 const char *filedata, size_t filedata_len,
739 struct string_buf **decrypted_buf)
740 {
741 char line[LINE_SIZE];
742 char *b64data = NULL;
743 unsigned int b64datalen = 0;
744 size_t off = 0;
745 int ret;
746
747 if(filedata == NULL || filedata_len <= 0)
748 return _libssh2_error(session, LIBSSH2_ERROR_PROTO,
749 "Error parsing PEM: filedata missing");
750
751 do {
752
753 *line = '\0';
754
755 if(off >= filedata_len)
756 return _libssh2_error(session, LIBSSH2_ERROR_PROTO,
757 "Error parsing PEM: offset out of bounds");
758
759 if(readline_memory(line, LINE_SIZE, filedata, filedata_len, &off)) {
760 return -1;
761 }
762 }
763 while(strcmp(line, OPENSSH_HEADER_BEGIN) != 0);
764
765 *line = '\0';
766
767 do {
768 if (*line) {
769 char *tmp;
770 size_t linelen;
771
772 linelen = strlen(line);
773 tmp = LIBSSH2_REALLOC(session, b64data, b64datalen + linelen);
774 if(!tmp) {
775 ret = _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
776 "Unable to allocate memory for "
777 "PEM parsing");
778 goto out;
779 }
780 memcpy(tmp + b64datalen, line, linelen);
781 b64data = tmp;
782 b64datalen += linelen;
783 }
784
785 *line = '\0';
786
787 if(off >= filedata_len) {
788 ret = _libssh2_error(session, LIBSSH2_ERROR_PROTO,
789 "Error parsing PEM: offset out of bounds");
790 goto out;
791 }
792
793 if(readline_memory(line, LINE_SIZE, filedata, filedata_len, &off)) {
794 ret = -1;
795 goto out;
796 }
797 } while(strcmp(line, OPENSSH_HEADER_END) != 0);
798
799 if(!b64data)
800 return _libssh2_error(session, LIBSSH2_ERROR_PROTO,
801 "Error parsing PEM: base 64 data missing");
802
803 ret = _libssh2_openssh_pem_parse_data(session, passphrase, b64data,
804 b64datalen, decrypted_buf);
805
806 out:
807 if(b64data) {
808 _libssh2_explicit_zero(b64data, b64datalen);
809 LIBSSH2_FREE(session, b64data);
810 }
811 return ret;
812
813 }
814
815 static int
read_asn1_length(const unsigned char * data,unsigned int datalen,unsigned int * len)816 read_asn1_length(const unsigned char *data,
817 unsigned int datalen, unsigned int *len)
818 {
819 unsigned int lenlen;
820 int nextpos;
821
822 if(datalen < 1) {
823 return -1;
824 }
825 *len = data[0];
826
827 if(*len >= 0x80) {
828 lenlen = *len & 0x7F;
829 *len = data[1];
830 if(1 + lenlen > datalen) {
831 return -1;
832 }
833 if(lenlen > 1) {
834 *len <<= 8;
835 *len |= data[2];
836 }
837 }
838 else {
839 lenlen = 0;
840 }
841
842 nextpos = 1 + lenlen;
843 if(lenlen > 2 || 1 + lenlen + *len > datalen) {
844 return -1;
845 }
846
847 return nextpos;
848 }
849
850 int
_libssh2_pem_decode_sequence(unsigned char ** data,unsigned int * datalen)851 _libssh2_pem_decode_sequence(unsigned char **data, unsigned int *datalen)
852 {
853 unsigned int len;
854 int lenlen;
855
856 if(*datalen < 1) {
857 return -1;
858 }
859
860 if((*data)[0] != '\x30') {
861 return -1;
862 }
863
864 (*data)++;
865 (*datalen)--;
866
867 lenlen = read_asn1_length(*data, *datalen, &len);
868 if(lenlen < 0 || lenlen + len != *datalen) {
869 return -1;
870 }
871
872 *data += lenlen;
873 *datalen -= lenlen;
874
875 return 0;
876 }
877
878 int
_libssh2_pem_decode_integer(unsigned char ** data,unsigned int * datalen,unsigned char ** i,unsigned int * ilen)879 _libssh2_pem_decode_integer(unsigned char **data, unsigned int *datalen,
880 unsigned char **i, unsigned int *ilen)
881 {
882 unsigned int len;
883 int lenlen;
884
885 if(*datalen < 1) {
886 return -1;
887 }
888
889 if((*data)[0] != '\x02') {
890 return -1;
891 }
892
893 (*data)++;
894 (*datalen)--;
895
896 lenlen = read_asn1_length(*data, *datalen, &len);
897 if(lenlen < 0 || lenlen + len > *datalen) {
898 return -1;
899 }
900
901 *data += lenlen;
902 *datalen -= lenlen;
903
904 *i = *data;
905 *ilen = len;
906
907 *data += len;
908 *datalen -= len;
909
910 return 0;
911 }
912