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