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                 ret = -1;
180                 goto out;
181             }
182             memcpy(tmp + b64datalen, line, linelen);
183             b64data = tmp;
184             b64datalen += linelen;
185         }
186 
187         *line = '\0';
188 
189         if(readline(line, LINE_SIZE, fp)) {
190             ret = -1;
191             goto out;
192         }
193     } while(strcmp(line, headerend) != 0);
194 
195     if(!b64data) {
196         return -1;
197     }
198 
199     if(libssh2_base64_decode(session, (char **) data, datalen,
200                               b64data, b64datalen)) {
201         ret = -1;
202         goto out;
203     }
204 
205     if(method) {
206         /* Set up decryption */
207         int free_iv = 0, free_secret = 0, len_decrypted = 0, padding = 0;
208         int blocksize = method->blocksize;
209         void *abstract;
210         unsigned char secret[2*MD5_DIGEST_LENGTH];
211         libssh2_md5_ctx fingerprint_ctx;
212 
213         /* Perform key derivation (PBKDF1/MD5) */
214         if(!libssh2_md5_init(&fingerprint_ctx)) {
215             ret = -1;
216             goto out;
217         }
218         libssh2_md5_update(fingerprint_ctx, passphrase,
219                            strlen((char *)passphrase));
220         libssh2_md5_update(fingerprint_ctx, iv, 8);
221         libssh2_md5_final(fingerprint_ctx, secret);
222         if(method->secret_len > MD5_DIGEST_LENGTH) {
223             if(!libssh2_md5_init(&fingerprint_ctx)) {
224                 ret = -1;
225                 goto out;
226             }
227             libssh2_md5_update(fingerprint_ctx, secret, MD5_DIGEST_LENGTH);
228             libssh2_md5_update(fingerprint_ctx, passphrase,
229                                strlen((char *)passphrase));
230             libssh2_md5_update(fingerprint_ctx, iv, 8);
231             libssh2_md5_final(fingerprint_ctx, secret + MD5_DIGEST_LENGTH);
232         }
233 
234         /* Initialize the decryption */
235         if(method->init(session, method, iv, &free_iv, secret,
236                          &free_secret, 0, &abstract)) {
237             _libssh2_explicit_zero((char *)secret, sizeof(secret));
238             LIBSSH2_FREE(session, data);
239             ret = -1;
240             goto out;
241         }
242 
243         if(free_secret) {
244             _libssh2_explicit_zero((char *)secret, sizeof(secret));
245         }
246 
247         /* Do the actual decryption */
248         if((*datalen % blocksize) != 0) {
249             _libssh2_explicit_zero((char *)secret, sizeof(secret));
250             method->dtor(session, &abstract);
251             _libssh2_explicit_zero(*data, *datalen);
252             LIBSSH2_FREE(session, *data);
253             ret = -1;
254             goto out;
255         }
256 
257         while(len_decrypted <= (int)*datalen - blocksize) {
258             if(method->crypt(session, *data + len_decrypted, blocksize,
259                               &abstract)) {
260                 ret = LIBSSH2_ERROR_DECRYPT;
261                 _libssh2_explicit_zero((char *)secret, sizeof(secret));
262                 method->dtor(session, &abstract);
263                 _libssh2_explicit_zero(*data, *datalen);
264                 LIBSSH2_FREE(session, *data);
265                 goto out;
266             }
267 
268             len_decrypted += blocksize;
269         }
270 
271         /* Account for padding */
272         padding = (*data)[*datalen - 1];
273         memset(&(*data)[*datalen-padding], 0, padding);
274         *datalen -= padding;
275 
276         /* Clean up */
277         _libssh2_explicit_zero((char *)secret, sizeof(secret));
278         method->dtor(session, &abstract);
279     }
280 
281     ret = 0;
282   out:
283     if(b64data) {
284         _libssh2_explicit_zero(b64data, b64datalen);
285         LIBSSH2_FREE(session, b64data);
286     }
287     return ret;
288 }
289 
290 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)291 _libssh2_pem_parse_memory(LIBSSH2_SESSION * session,
292                           const char *headerbegin,
293                           const char *headerend,
294                           const char *filedata, size_t filedata_len,
295                           unsigned char **data, unsigned int *datalen)
296 {
297     char line[LINE_SIZE];
298     char *b64data = NULL;
299     unsigned int b64datalen = 0;
300     size_t off = 0;
301     int ret;
302 
303     do {
304         *line = '\0';
305 
306         if(readline_memory(line, LINE_SIZE, filedata, filedata_len, &off)) {
307             return -1;
308         }
309     }
310     while(strcmp(line, headerbegin) != 0);
311 
312     *line = '\0';
313 
314     do {
315         if(*line) {
316             char *tmp;
317             size_t linelen;
318 
319             linelen = strlen(line);
320             tmp = LIBSSH2_REALLOC(session, b64data, b64datalen + linelen);
321             if(!tmp) {
322                 ret = -1;
323                 goto out;
324             }
325             memcpy(tmp + b64datalen, line, linelen);
326             b64data = tmp;
327             b64datalen += linelen;
328         }
329 
330         *line = '\0';
331 
332         if(readline_memory(line, LINE_SIZE, filedata, filedata_len, &off)) {
333             ret = -1;
334             goto out;
335         }
336     } while(strcmp(line, headerend) != 0);
337 
338     if(!b64data) {
339         return -1;
340     }
341 
342     if(libssh2_base64_decode(session, (char **) data, datalen,
343                               b64data, b64datalen)) {
344         ret = -1;
345         goto out;
346     }
347 
348     ret = 0;
349   out:
350     if(b64data) {
351         _libssh2_explicit_zero(b64data, b64datalen);
352         LIBSSH2_FREE(session, b64data);
353     }
354     return ret;
355 }
356 
357 /* OpenSSH formatted keys */
358 #define AUTH_MAGIC "openssh-key-v1"
359 #define OPENSSH_HEADER_BEGIN "-----BEGIN OPENSSH PRIVATE KEY-----"
360 #define OPENSSH_HEADER_END "-----END OPENSSH PRIVATE KEY-----"
361 
362 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)363 _libssh2_openssh_pem_parse_data(LIBSSH2_SESSION * session,
364                                 const unsigned char *passphrase,
365                                 const char *b64data, size_t b64datalen,
366                                 struct string_buf **decrypted_buf)
367 {
368     const LIBSSH2_CRYPT_METHOD *method = NULL;
369     struct string_buf decoded, decrypted, kdf_buf;
370     unsigned char *ciphername = NULL;
371     unsigned char *kdfname = NULL;
372     unsigned char *kdf = NULL;
373     unsigned char *buf = NULL;
374     unsigned char *salt = NULL;
375     uint32_t nkeys, check1, check2;
376     uint32_t rounds = 0;
377     unsigned char *key = NULL;
378     unsigned char *key_part = NULL;
379     unsigned char *iv_part = NULL;
380     unsigned char *f = NULL;
381     unsigned int f_len = 0;
382     int ret = 0, keylen = 0, ivlen = 0, total_len = 0;
383     size_t kdf_len = 0, tmp_len = 0, salt_len = 0;
384 
385     if(decrypted_buf)
386         *decrypted_buf = NULL;
387 
388     /* decode file */
389     if(libssh2_base64_decode(session, (char **)&f, &f_len,
390                              b64data, b64datalen)) {
391        ret = -1;
392        goto out;
393     }
394 
395     /* Parse the file */
396     decoded.data = (unsigned char *)f;
397     decoded.dataptr = (unsigned char *)f;
398     decoded.len = f_len;
399 
400     if(decoded.len < strlen(AUTH_MAGIC)) {
401         ret = _libssh2_error(session, LIBSSH2_ERROR_PROTO, "key too short");
402         goto out;
403     }
404 
405     if(strncmp((char *) decoded.dataptr, AUTH_MAGIC,
406                strlen(AUTH_MAGIC)) != 0) {
407         ret = _libssh2_error(session, LIBSSH2_ERROR_PROTO,
408                              "key auth magic mismatch");
409         goto out;
410     }
411 
412     decoded.dataptr += strlen(AUTH_MAGIC) + 1;
413 
414     if(_libssh2_get_string(&decoded, &ciphername, &tmp_len) ||
415        tmp_len == 0) {
416         ret = _libssh2_error(session, LIBSSH2_ERROR_PROTO,
417                              "ciphername is missing");
418         goto out;
419     }
420 
421     if(_libssh2_get_string(&decoded, &kdfname, &tmp_len) ||
422        tmp_len == 0) {
423         ret = _libssh2_error(session, LIBSSH2_ERROR_PROTO,
424                        "kdfname is missing");
425         goto out;
426     }
427 
428     if(_libssh2_get_string(&decoded, &kdf, &kdf_len)) {
429         ret = _libssh2_error(session, LIBSSH2_ERROR_PROTO,
430                              "kdf is missing");
431         goto out;
432     }
433     else {
434         kdf_buf.data = kdf;
435         kdf_buf.dataptr = kdf;
436         kdf_buf.len = kdf_len;
437     }
438 
439     if((passphrase == NULL || strlen((const char *)passphrase) == 0) &&
440         strcmp((const char *)ciphername, "none") != 0) {
441         /* passphrase required */
442         ret = LIBSSH2_ERROR_KEYFILE_AUTH_FAILED;
443         goto out;
444     }
445 
446     if(strcmp((const char *)kdfname, "none") != 0 &&
447        strcmp((const char *)kdfname, "bcrypt") != 0) {
448         ret = _libssh2_error(session, LIBSSH2_ERROR_PROTO,
449                              "unknown cipher");
450         goto out;
451     }
452 
453     if(!strcmp((const char *)kdfname, "none") &&
454        strcmp((const char *)ciphername, "none") != 0) {
455         ret =_libssh2_error(session, LIBSSH2_ERROR_PROTO,
456                             "invalid format");
457         goto out;
458     }
459 
460     if(_libssh2_get_u32(&decoded, &nkeys) != 0 || nkeys != 1) {
461         ret = _libssh2_error(session, LIBSSH2_ERROR_PROTO,
462                              "Multiple keys are unsupported");
463         goto out;
464     }
465 
466     /* unencrypted public key */
467 
468     if(_libssh2_get_string(&decoded, &buf, &tmp_len) || tmp_len == 0) {
469         ret = _libssh2_error(session, LIBSSH2_ERROR_PROTO,
470                              "Invalid private key; "
471                              "expect embedded public key");
472         goto out;
473     }
474 
475     if(_libssh2_get_string(&decoded, &buf, &tmp_len) || tmp_len == 0) {
476         ret = _libssh2_error(session, LIBSSH2_ERROR_PROTO,
477                        "Private key data not found");
478         goto out;
479     }
480 
481     /* decode encrypted private key */
482     decrypted.data = decrypted.dataptr = buf;
483     decrypted.len = tmp_len;
484 
485     if(ciphername && strcmp((const char *)ciphername, "none") != 0) {
486         const LIBSSH2_CRYPT_METHOD **all_methods, *cur_method;
487 
488         all_methods = libssh2_crypt_methods();
489         while((cur_method = *all_methods++)) {
490             if(*cur_method->name &&
491                 memcmp(ciphername, cur_method->name,
492                        strlen(cur_method->name)) == 0) {
493                     method = cur_method;
494                 }
495         }
496 
497         /* None of the available crypt methods were able to decrypt the key */
498 
499         if(method == NULL) {
500             ret = _libssh2_error(session, LIBSSH2_ERROR_PROTO,
501                                 "No supported cipher found");
502             goto out;
503         }
504     }
505 
506     if(method) {
507         int free_iv = 0, free_secret = 0, len_decrypted = 0;
508         int blocksize;
509         void *abstract = NULL;
510 
511         keylen = method->secret_len;
512         ivlen = method->iv_len;
513         total_len = keylen + ivlen;
514 
515         key = LIBSSH2_CALLOC(session, total_len);
516         if(key == NULL) {
517             ret = _libssh2_error(session, LIBSSH2_ERROR_PROTO,
518                            "Could not alloc key");
519             goto out;
520         }
521 
522         if(strcmp((const char *)kdfname, "bcrypt") == 0 &&
523            passphrase != NULL) {
524             if((_libssh2_get_string(&kdf_buf, &salt, &salt_len)) ||
525                 (_libssh2_get_u32(&kdf_buf, &rounds) != 0) ) {
526                 ret = _libssh2_error(session, LIBSSH2_ERROR_PROTO,
527                                      "kdf contains unexpected values");
528                 LIBSSH2_FREE(session, key);
529                 goto out;
530             }
531 
532             if(_libssh2_bcrypt_pbkdf((const char *)passphrase,
533                                      strlen((const char *)passphrase),
534                                      salt, salt_len, key,
535                                      keylen + ivlen, rounds) < 0) {
536                 ret = _libssh2_error(session, LIBSSH2_ERROR_DECRYPT,
537                                      "invalid format");
538                 LIBSSH2_FREE(session, key);
539                 goto out;
540             }
541         }
542         else {
543             ret = _libssh2_error(session, LIBSSH2_ERROR_KEYFILE_AUTH_FAILED,
544                                             "bcrypted without passphrase");
545             LIBSSH2_FREE(session, key);
546             goto out;
547         }
548 
549         /* Set up decryption */
550         blocksize = method->blocksize;
551 
552         key_part = LIBSSH2_CALLOC(session, keylen);
553         if(key_part == NULL) {
554             ret = _libssh2_error(session, LIBSSH2_ERROR_PROTO,
555                                  "Could not alloc key part");
556             goto out;
557         }
558 
559         iv_part = LIBSSH2_CALLOC(session, ivlen);
560         if(iv_part == NULL) {
561             ret = _libssh2_error(session, LIBSSH2_ERROR_PROTO,
562                                  "Could not alloc iv part");
563             goto out;
564         }
565 
566         memcpy(key_part, key, keylen);
567         memcpy(iv_part, key + keylen, ivlen);
568 
569         /* Initialize the decryption */
570         if(method->init(session, method, iv_part, &free_iv, key_part,
571                          &free_secret, 0, &abstract)) {
572             ret = LIBSSH2_ERROR_DECRYPT;
573             goto out;
574         }
575 
576         /* Do the actual decryption */
577         if((decrypted.len % blocksize) != 0) {
578             method->dtor(session, &abstract);
579             ret = LIBSSH2_ERROR_DECRYPT;
580             goto out;
581         }
582 
583         while((size_t)len_decrypted <= decrypted.len - blocksize) {
584             if(method->crypt(session, decrypted.data + len_decrypted,
585                              blocksize,
586                              &abstract)) {
587                 ret = LIBSSH2_ERROR_DECRYPT;
588                 method->dtor(session, &abstract);
589                 goto out;
590             }
591 
592             len_decrypted += blocksize;
593         }
594 
595         /* No padding */
596 
597         method->dtor(session, &abstract);
598     }
599 
600     /* Check random bytes match */
601 
602     if(_libssh2_get_u32(&decrypted, &check1) != 0 ||
603        _libssh2_get_u32(&decrypted, &check2) != 0 ||
604        check1 != check2) {
605        _libssh2_error(session, LIBSSH2_ERROR_PROTO,
606                       "Private key unpack failed (correct password?)");
607        ret = LIBSSH2_ERROR_KEYFILE_AUTH_FAILED;
608        goto out;
609     }
610 
611     if(decrypted_buf != NULL) {
612         /* copy data to out-going buffer */
613         struct string_buf *out_buf = _libssh2_string_buf_new(session);
614         if(!out_buf) {
615             ret = _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
616                                  "Unable to allocate memory for "
617                                  "decrypted struct");
618             goto out;
619         }
620 
621         out_buf->data = LIBSSH2_CALLOC(session, decrypted.len);
622         if(out_buf->data == NULL) {
623             ret = _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
624                                  "Unable to allocate memory for "
625                                  "decrypted struct");
626             _libssh2_string_buf_free(session, out_buf);
627             goto out;
628         }
629         memcpy(out_buf->data, decrypted.data, decrypted.len);
630         out_buf->dataptr = out_buf->data +
631             (decrypted.dataptr - decrypted.data);
632         out_buf->len = decrypted.len;
633 
634         *decrypted_buf = out_buf;
635     }
636 
637 out:
638 
639     /* Clean up */
640     if(key) {
641         _libssh2_explicit_zero(key, total_len);
642         LIBSSH2_FREE(session, key);
643     }
644     if(key_part) {
645         _libssh2_explicit_zero(key_part, keylen);
646         LIBSSH2_FREE(session, key_part);
647     }
648     if(iv_part) {
649         _libssh2_explicit_zero(iv_part, ivlen);
650         LIBSSH2_FREE(session, iv_part);
651     }
652     if(f) {
653         _libssh2_explicit_zero(f, f_len);
654         LIBSSH2_FREE(session, f);
655     }
656 
657     return ret;
658 }
659 
660 int
_libssh2_openssh_pem_parse(LIBSSH2_SESSION * session,const unsigned char * passphrase,FILE * fp,struct string_buf ** decrypted_buf)661 _libssh2_openssh_pem_parse(LIBSSH2_SESSION * session,
662                            const unsigned char *passphrase,
663                            FILE * fp, struct string_buf **decrypted_buf)
664 {
665     char line[LINE_SIZE];
666     char *b64data = NULL;
667     unsigned int b64datalen = 0;
668     int ret = 0;
669 
670     /* read file */
671 
672     do {
673         *line = '\0';
674 
675         if(readline(line, LINE_SIZE, fp)) {
676             return -1;
677         }
678     }
679     while(strcmp(line, OPENSSH_HEADER_BEGIN) != 0);
680 
681     if(readline(line, LINE_SIZE, fp)) {
682         return -1;
683     }
684 
685     do {
686         if(*line) {
687             char *tmp;
688             size_t linelen;
689 
690             linelen = strlen(line);
691             tmp = LIBSSH2_REALLOC(session, b64data, b64datalen + linelen);
692             if(!tmp) {
693                 ret = -1;
694                 goto out;
695             }
696             memcpy(tmp + b64datalen, line, linelen);
697             b64data = tmp;
698             b64datalen += linelen;
699         }
700 
701         *line = '\0';
702 
703         if(readline(line, LINE_SIZE, fp)) {
704             ret = -1;
705             goto out;
706         }
707     } while(strcmp(line, OPENSSH_HEADER_END) != 0);
708 
709     if(!b64data) {
710         return -1;
711     }
712 
713     ret = _libssh2_openssh_pem_parse_data(session,
714                                           passphrase,
715                                           (const char *)b64data,
716                                           (size_t)b64datalen,
717                                           decrypted_buf);
718 
719     if(b64data) {
720         _libssh2_explicit_zero(b64data, b64datalen);
721         LIBSSH2_FREE(session, b64data);
722     }
723 
724 out:
725 
726     return ret;
727 }
728 
729 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)730 _libssh2_openssh_pem_parse_memory(LIBSSH2_SESSION * session,
731                                   const unsigned char *passphrase,
732                                   const char *filedata, size_t filedata_len,
733                                   struct string_buf **decrypted_buf)
734 {
735     char line[LINE_SIZE];
736     char *b64data = NULL;
737     unsigned int b64datalen = 0;
738     size_t off = 0;
739     int ret;
740 
741     if(filedata == NULL || filedata_len <= 0) {
742         return -1;
743     }
744 
745     do {
746 
747         *line = '\0';
748 
749         if(off >= filedata_len) {
750             return -1;
751         }
752 
753         if(readline_memory(line, LINE_SIZE, filedata, filedata_len, &off)) {
754             return -1;
755         }
756     }
757     while(strcmp(line, OPENSSH_HEADER_BEGIN) != 0);
758 
759     *line = '\0';
760 
761     do {
762         if (*line) {
763             char *tmp;
764             size_t linelen;
765 
766             linelen = strlen(line);
767             tmp = LIBSSH2_REALLOC(session, b64data, b64datalen + linelen);
768             if(!tmp) {
769                 ret = -1;
770                 goto out;
771             }
772             memcpy(tmp + b64datalen, line, linelen);
773             b64data = tmp;
774             b64datalen += linelen;
775         }
776 
777         *line = '\0';
778 
779         if(off >= filedata_len) {
780             ret = -1;
781             goto out;
782         }
783 
784         if(readline_memory(line, LINE_SIZE, filedata, filedata_len, &off)) {
785             ret = -1;
786             goto out;
787         }
788     } while(strcmp(line, OPENSSH_HEADER_END) != 0);
789 
790     if(!b64data) {
791         return -1;
792     }
793 
794     ret = _libssh2_openssh_pem_parse_data(session, passphrase, b64data,
795                                           b64datalen, decrypted_buf);
796 
797 out:
798     if(b64data) {
799         _libssh2_explicit_zero(b64data, b64datalen);
800         LIBSSH2_FREE(session, b64data);
801     }
802     return ret;
803 
804 }
805 
806 static int
read_asn1_length(const unsigned char * data,unsigned int datalen,unsigned int * len)807 read_asn1_length(const unsigned char *data,
808                  unsigned int datalen, unsigned int *len)
809 {
810     unsigned int lenlen;
811     int nextpos;
812 
813     if(datalen < 1) {
814         return -1;
815     }
816     *len = data[0];
817 
818     if(*len >= 0x80) {
819         lenlen = *len & 0x7F;
820         *len = data[1];
821         if(1 + lenlen > datalen) {
822             return -1;
823         }
824         if(lenlen > 1) {
825             *len <<= 8;
826             *len |= data[2];
827         }
828     }
829     else {
830         lenlen = 0;
831     }
832 
833     nextpos = 1 + lenlen;
834     if(lenlen > 2 || 1 + lenlen + *len > datalen) {
835         return -1;
836     }
837 
838     return nextpos;
839 }
840 
841 int
_libssh2_pem_decode_sequence(unsigned char ** data,unsigned int * datalen)842 _libssh2_pem_decode_sequence(unsigned char **data, unsigned int *datalen)
843 {
844     unsigned int len;
845     int lenlen;
846 
847     if(*datalen < 1) {
848         return -1;
849     }
850 
851     if((*data)[0] != '\x30') {
852         return -1;
853     }
854 
855     (*data)++;
856     (*datalen)--;
857 
858     lenlen = read_asn1_length(*data, *datalen, &len);
859     if(lenlen < 0 || lenlen + len != *datalen) {
860         return -1;
861     }
862 
863     *data += lenlen;
864     *datalen -= lenlen;
865 
866     return 0;
867 }
868 
869 int
_libssh2_pem_decode_integer(unsigned char ** data,unsigned int * datalen,unsigned char ** i,unsigned int * ilen)870 _libssh2_pem_decode_integer(unsigned char **data, unsigned int *datalen,
871                             unsigned char **i, unsigned int *ilen)
872 {
873     unsigned int len;
874     int lenlen;
875 
876     if(*datalen < 1) {
877         return -1;
878     }
879 
880     if((*data)[0] != '\x02') {
881         return -1;
882     }
883 
884     (*data)++;
885     (*datalen)--;
886 
887     lenlen = read_asn1_length(*data, *datalen, &len);
888     if(lenlen < 0 || lenlen + len > *datalen) {
889         return -1;
890     }
891 
892     *data += lenlen;
893     *datalen -= lenlen;
894 
895     *i = *data;
896     *ilen = len;
897 
898     *data += len;
899     *datalen -= len;
900 
901     return 0;
902 }
903