libssh2

Форк
0
/
pem.c 
955 строк · 27.2 Кб
1
/* Copyright (C) The Written Word, Inc.
2
 * Copyright (C) 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
 * SPDX-License-Identifier: BSD-3-Clause
39
 */
40

41
#include "libssh2_priv.h"
42

43
static int
44
readline(char *line, int line_size, FILE * fp)
45
{
46
    size_t len;
47

48
    if(!line) {
49
        return -1;
50
    }
51
    if(!fgets(line, line_size, fp)) {
52
        return -1;
53
    }
54

55
    if(*line) {
56
        len = strlen(line);
57
        if(len > 0 && line[len - 1] == '\n') {
58
            line[len - 1] = '\0';
59
        }
60
    }
61

62
    if(*line) {
63
        len = strlen(line);
64
        if(len > 0 && line[len - 1] == '\r') {
65
            line[len - 1] = '\0';
66
        }
67
    }
68

69
    return 0;
70
}
71

72
static int
73
readline_memory(char *line, size_t line_size,
74
                const char *filedata, size_t filedata_len,
75
                size_t *filedata_offset)
76
{
77
    size_t off, len;
78

79
    off = *filedata_offset;
80

81
    for(len = 0; off + len < filedata_len && len < line_size - 1; len++) {
82
        if(filedata[off + len] == '\n' ||
83
            filedata[off + len] == '\r') {
84
                break;
85
        }
86
    }
87

88
    if(len) {
89
        memcpy(line, filedata + off, len);
90
        *filedata_offset += len;
91
    }
92

93
    line[len] = '\0';
94
    *filedata_offset += 1;
95

96
    return 0;
97
}
98

99
#define LINE_SIZE 128
100

101
static const char *crypt_annotation = "Proc-Type: 4,ENCRYPTED";
102

103
static unsigned char hex_decode(char digit)
104
{
105
    return (unsigned char)
106
        ((digit >= 'A') ? (0xA + (digit - 'A')) : (digit - '0'));
107
}
108

109
int
110
_libssh2_pem_parse(LIBSSH2_SESSION * session,
111
                   const char *headerbegin,
112
                   const char *headerend,
113
                   const unsigned char *passphrase,
114
                   FILE * fp, unsigned char **data, size_t *datalen)
115
{
116
    char line[LINE_SIZE];
117
    unsigned char iv[LINE_SIZE];
118
    char *b64data = NULL;
119
    size_t b64datalen = 0;
120
    int ret;
121
    const LIBSSH2_CRYPT_METHOD *method = NULL;
122

123
    do {
124
        *line = '\0';
125

126
        if(readline(line, LINE_SIZE, fp)) {
127
            return -1;
128
        }
129
    }
130
    while(strcmp(line, headerbegin) != 0);
131

132
    if(readline(line, LINE_SIZE, fp)) {
133
        return -1;
134
    }
135

136
    if(passphrase &&
137
            memcmp(line, crypt_annotation, strlen(crypt_annotation)) == 0) {
138
        const LIBSSH2_CRYPT_METHOD **all_methods, *cur_method;
139
        int i;
140

141
        if(readline(line, LINE_SIZE, fp)) {
142
            ret = -1;
143
            goto out;
144
        }
145

146
        all_methods = libssh2_crypt_methods();
147
        /* !checksrc! disable EQUALSNULL 1 */
148
        while((cur_method = *all_methods++) != NULL) {
149
            if(*cur_method->pem_annotation &&
150
                    memcmp(line, cur_method->pem_annotation,
151
                           strlen(cur_method->pem_annotation)) == 0) {
152
                method = cur_method;
153
                memcpy(iv, line + strlen(method->pem_annotation) + 1,
154
                       2*method->iv_len);
155
            }
156
        }
157

158
        /* None of the available crypt methods were able to decrypt the key */
159
        if(!method)
160
            return -1;
161

162
        /* Decode IV from hex */
163
        for(i = 0; i < method->iv_len; ++i) {
164
            iv[i]  = (unsigned char)(hex_decode(iv[2*i]) << 4);
165
            iv[i] |= hex_decode(iv[2*i + 1]);
166
        }
167

168
        /* skip to the next line */
169
        if(readline(line, LINE_SIZE, fp)) {
170
            ret = -1;
171
            goto out;
172
        }
173
    }
174

175
    do {
176
        if(*line) {
177
            char *tmp;
178
            size_t linelen;
179

180
            linelen = strlen(line);
181
            tmp = LIBSSH2_REALLOC(session, b64data, b64datalen + linelen);
182
            if(!tmp) {
183
                _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
184
                               "Unable to allocate memory for PEM parsing");
185
                ret = -1;
186
                goto out;
187
            }
188
            memcpy(tmp + b64datalen, line, linelen);
189
            b64data = tmp;
190
            b64datalen += linelen;
191
        }
192

193
        *line = '\0';
194

195
        if(readline(line, LINE_SIZE, fp)) {
196
            ret = -1;
197
            goto out;
198
        }
199
    } while(strcmp(line, headerend) != 0);
200

201
    if(!b64data) {
202
        return -1;
203
    }
204

205
    if(_libssh2_base64_decode(session, (char **) data, datalen,
206
                              b64data, b64datalen)) {
207
        ret = -1;
208
        goto out;
209
    }
210

211
    if(method) {
212
#if LIBSSH2_MD5_PEM
213
        /* Set up decryption */
214
        int free_iv = 0, free_secret = 0, len_decrypted = 0, padding = 0;
215
        int blocksize = method->blocksize;
216
        void *abstract;
217
        unsigned char secret[2*MD5_DIGEST_LENGTH];
218
        libssh2_md5_ctx fingerprint_ctx;
219

220
        /* Perform key derivation (PBKDF1/MD5) */
221
        if(!libssh2_md5_init(&fingerprint_ctx) ||
222
           !libssh2_md5_update(fingerprint_ctx, passphrase,
223
                               strlen((char *)passphrase)) ||
224
           !libssh2_md5_update(fingerprint_ctx, iv, 8) ||
225
           !libssh2_md5_final(fingerprint_ctx, secret)) {
226
            ret = -1;
227
            goto out;
228
        }
229
        if(method->secret_len > MD5_DIGEST_LENGTH) {
230
            if(!libssh2_md5_init(&fingerprint_ctx) ||
231
               !libssh2_md5_update(fingerprint_ctx,
232
                                   secret, MD5_DIGEST_LENGTH) ||
233
               !libssh2_md5_update(fingerprint_ctx,
234
                                   passphrase, strlen((char *)passphrase)) ||
235
               !libssh2_md5_update(fingerprint_ctx, iv, 8) ||
236
               !libssh2_md5_final(fingerprint_ctx,
237
                                  secret + MD5_DIGEST_LENGTH)) {
238
                ret = -1;
239
                goto out;
240
            }
241
        }
242

243
        /* Initialize the decryption */
244
        if(method->init(session, method, iv, &free_iv, secret,
245
                         &free_secret, 0, &abstract)) {
246
            _libssh2_explicit_zero((char *)secret, sizeof(secret));
247
            LIBSSH2_FREE(session, data);
248
            ret = -1;
249
            goto out;
250
        }
251

252
        if(free_secret) {
253
            _libssh2_explicit_zero((char *)secret, sizeof(secret));
254
        }
255

256
        /* Do the actual decryption */
257
        if((*datalen % blocksize) != 0) {
258
            _libssh2_explicit_zero((char *)secret, sizeof(secret));
259
            method->dtor(session, &abstract);
260
            _libssh2_explicit_zero(*data, *datalen);
261
            LIBSSH2_FREE(session, *data);
262
            ret = -1;
263
            goto out;
264
        }
265

266
        while(len_decrypted <= (int)*datalen - blocksize) {
267
            if(method->crypt(session, *data + len_decrypted, blocksize,
268
                             &abstract,
269
                             len_decrypted == 0 ? FIRST_BLOCK :
270
                               ((len_decrypted == (int)*datalen - blocksize) ?
271
                              LAST_BLOCK : MIDDLE_BLOCK)
272
                             )) {
273
                ret = LIBSSH2_ERROR_DECRYPT;
274
                _libssh2_explicit_zero((char *)secret, sizeof(secret));
275
                method->dtor(session, &abstract);
276
                _libssh2_explicit_zero(*data, *datalen);
277
                LIBSSH2_FREE(session, *data);
278
                goto out;
279
            }
280

281
            len_decrypted += blocksize;
282
        }
283

284
        /* Account for padding */
285
        padding = (*data)[*datalen - 1];
286
        memset(&(*data)[*datalen-padding], 0, padding);
287
        *datalen -= padding;
288

289
        /* Clean up */
290
        _libssh2_explicit_zero((char *)secret, sizeof(secret));
291
        method->dtor(session, &abstract);
292
#else
293
        ret = -1;
294
        goto out;
295
#endif
296
    }
297

298
    ret = 0;
299
out:
300
    if(b64data) {
301
        _libssh2_explicit_zero(b64data, b64datalen);
302
        LIBSSH2_FREE(session, b64data);
303
    }
304
    return ret;
305
}
306

307
int
308
_libssh2_pem_parse_memory(LIBSSH2_SESSION * session,
309
                          const char *headerbegin,
310
                          const char *headerend,
311
                          const char *filedata, size_t filedata_len,
312
                          unsigned char **data, size_t *datalen)
313
{
314
    char line[LINE_SIZE];
315
    char *b64data = NULL;
316
    size_t b64datalen = 0;
317
    size_t off = 0;
318
    int ret;
319

320
    do {
321
        *line = '\0';
322

323
        if(readline_memory(line, LINE_SIZE, filedata, filedata_len, &off)) {
324
            return -1;
325
        }
326
    }
327
    while(strcmp(line, headerbegin) != 0);
328

329
    *line = '\0';
330

331
    do {
332
        if(*line) {
333
            char *tmp;
334
            size_t linelen;
335

336
            linelen = strlen(line);
337
            tmp = LIBSSH2_REALLOC(session, b64data, b64datalen + linelen);
338
            if(!tmp) {
339
                _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
340
                               "Unable to allocate memory for PEM parsing");
341
                ret = -1;
342
                goto out;
343
            }
344
            memcpy(tmp + b64datalen, line, linelen);
345
            b64data = tmp;
346
            b64datalen += linelen;
347
        }
348

349
        *line = '\0';
350

351
        if(readline_memory(line, LINE_SIZE, filedata, filedata_len, &off)) {
352
            ret = -1;
353
            goto out;
354
        }
355
    } while(strcmp(line, headerend) != 0);
356

357
    if(!b64data) {
358
        return -1;
359
    }
360

361
    if(_libssh2_base64_decode(session, (char **) data, datalen,
362
                              b64data, b64datalen)) {
363
        ret = -1;
364
        goto out;
365
    }
366

367
    ret = 0;
368
out:
369
    if(b64data) {
370
        _libssh2_explicit_zero(b64data, b64datalen);
371
        LIBSSH2_FREE(session, b64data);
372
    }
373
    return ret;
374
}
375

376
/* OpenSSH formatted keys */
377
#define AUTH_MAGIC "openssh-key-v1"
378
#define OPENSSH_HEADER_BEGIN "-----BEGIN OPENSSH PRIVATE KEY-----"
379
#define OPENSSH_HEADER_END "-----END OPENSSH PRIVATE KEY-----"
380

381
static int
382
_libssh2_openssh_pem_parse_data(LIBSSH2_SESSION * session,
383
                                const unsigned char *passphrase,
384
                                const char *b64data, size_t b64datalen,
385
                                struct string_buf **decrypted_buf)
386
{
387
    const LIBSSH2_CRYPT_METHOD *method = NULL;
388
    struct string_buf decoded, decrypted, kdf_buf;
389
    unsigned char *ciphername = NULL;
390
    unsigned char *kdfname = NULL;
391
    unsigned char *kdf = NULL;
392
    unsigned char *buf = NULL;
393
    unsigned char *salt = NULL;
394
    uint32_t nkeys, check1, check2;
395
    uint32_t rounds = 0;
396
    unsigned char *key = NULL;
397
    unsigned char *key_part = NULL;
398
    unsigned char *iv_part = NULL;
399
    unsigned char *f = NULL;
400
    size_t f_len = 0;
401
    int ret = 0, keylen = 0, ivlen = 0, total_len = 0;
402
    size_t kdf_len = 0, tmp_len = 0, salt_len = 0;
403

404
    if(decrypted_buf)
405
        *decrypted_buf = NULL;
406

407
    /* decode file */
408
    if(_libssh2_base64_decode(session, (char **)&f, &f_len,
409
                              b64data, b64datalen)) {
410
        ret = -1;
411
        goto out;
412
    }
413

414
    /* Parse the file */
415
    decoded.data = (unsigned char *)f;
416
    decoded.dataptr = (unsigned char *)f;
417
    decoded.len = f_len;
418

419
    if(decoded.len < strlen(AUTH_MAGIC)) {
420
        ret = _libssh2_error(session, LIBSSH2_ERROR_PROTO, "key too short");
421
        goto out;
422
    }
423

424
    if(strncmp((char *) decoded.dataptr, AUTH_MAGIC,
425
               strlen(AUTH_MAGIC)) != 0) {
426
        ret = _libssh2_error(session, LIBSSH2_ERROR_PROTO,
427
                             "key auth magic mismatch");
428
        goto out;
429
    }
430

431
    decoded.dataptr += strlen(AUTH_MAGIC) + 1;
432

433
    if(_libssh2_get_string(&decoded, &ciphername, &tmp_len) ||
434
       tmp_len == 0) {
435
        ret = _libssh2_error(session, LIBSSH2_ERROR_PROTO,
436
                             "ciphername is missing");
437
        goto out;
438
    }
439

440
    if(_libssh2_get_string(&decoded, &kdfname, &tmp_len) ||
441
       tmp_len == 0) {
442
        ret = _libssh2_error(session, LIBSSH2_ERROR_PROTO,
443
                             "kdfname is missing");
444
        goto out;
445
    }
446

447
    if(_libssh2_get_string(&decoded, &kdf, &kdf_len)) {
448
        ret = _libssh2_error(session, LIBSSH2_ERROR_PROTO,
449
                             "kdf is missing");
450
        goto out;
451
    }
452
    else {
453
        kdf_buf.data = kdf;
454
        kdf_buf.dataptr = kdf;
455
        kdf_buf.len = kdf_len;
456
    }
457

458
    if((!passphrase || strlen((const char *)passphrase) == 0) &&
459
        strcmp((const char *)ciphername, "none") != 0) {
460
        /* passphrase required */
461
        ret = LIBSSH2_ERROR_KEYFILE_AUTH_FAILED;
462
        goto out;
463
    }
464

465
    if(strcmp((const char *)kdfname, "none") != 0 &&
466
       strcmp((const char *)kdfname, "bcrypt") != 0) {
467
        ret = _libssh2_error(session, LIBSSH2_ERROR_PROTO,
468
                             "unknown cipher");
469
        goto out;
470
    }
471

472
    if(!strcmp((const char *)kdfname, "none") &&
473
       strcmp((const char *)ciphername, "none") != 0) {
474
        ret = _libssh2_error(session, LIBSSH2_ERROR_PROTO,
475
                             "invalid format");
476
        goto out;
477
    }
478

479
    if(_libssh2_get_u32(&decoded, &nkeys) != 0 || nkeys != 1) {
480
        ret = _libssh2_error(session, LIBSSH2_ERROR_PROTO,
481
                             "Multiple keys are unsupported");
482
        goto out;
483
    }
484

485
    /* unencrypted public key */
486

487
    if(_libssh2_get_string(&decoded, &buf, &tmp_len) || tmp_len == 0) {
488
        ret = _libssh2_error(session, LIBSSH2_ERROR_PROTO,
489
                             "Invalid private key; "
490
                             "expect embedded public key");
491
        goto out;
492
    }
493

494
    if(_libssh2_get_string(&decoded, &buf, &tmp_len) || tmp_len == 0) {
495
        ret = _libssh2_error(session, LIBSSH2_ERROR_PROTO,
496
                             "Private key data not found");
497
        goto out;
498
    }
499

500
    /* decode encrypted private key */
501
    decrypted.data = decrypted.dataptr = buf;
502
    decrypted.len = tmp_len;
503

504
    if(ciphername && strcmp((const char *)ciphername, "none") != 0) {
505
        const LIBSSH2_CRYPT_METHOD **all_methods, *cur_method;
506

507
        all_methods = libssh2_crypt_methods();
508
        /* !checksrc! disable EQUALSNULL 1 */
509
        while((cur_method = *all_methods++) != NULL) {
510
            if(*cur_method->name &&
511
                memcmp(ciphername, cur_method->name,
512
                       strlen(cur_method->name)) == 0) {
513
                    method = cur_method;
514
                }
515
        }
516

517
        /* None of the available crypt methods were able to decrypt the key */
518

519
        if(!method) {
520
            ret = _libssh2_error(session, LIBSSH2_ERROR_PROTO,
521
                                 "No supported cipher found");
522
            goto out;
523
        }
524
    }
525

526
    if(method) {
527
        int free_iv = 0, free_secret = 0, len_decrypted = 0;
528
        int blocksize;
529
        void *abstract = NULL;
530

531
        keylen = method->secret_len;
532
        ivlen = method->iv_len;
533
        total_len = keylen + ivlen;
534

535
        key = LIBSSH2_CALLOC(session, total_len);
536
        if(!key) {
537
            ret = _libssh2_error(session, LIBSSH2_ERROR_PROTO,
538
                                 "Could not alloc key");
539
            goto out;
540
        }
541

542
        if(strcmp((const char *)kdfname, "bcrypt") == 0 && passphrase) {
543
            if((_libssh2_get_string(&kdf_buf, &salt, &salt_len)) ||
544
                (_libssh2_get_u32(&kdf_buf, &rounds) != 0)) {
545
                ret = _libssh2_error(session, LIBSSH2_ERROR_PROTO,
546
                                     "kdf contains unexpected values");
547
                LIBSSH2_FREE(session, key);
548
                goto out;
549
            }
550

551
            if(_libssh2_bcrypt_pbkdf((const char *)passphrase,
552
                                     strlen((const char *)passphrase),
553
                                     salt, salt_len, key,
554
                                     keylen + ivlen, rounds) < 0) {
555
                ret = _libssh2_error(session, LIBSSH2_ERROR_DECRYPT,
556
                                     "invalid format");
557
                LIBSSH2_FREE(session, key);
558
                goto out;
559
            }
560
        }
561
        else {
562
            ret = _libssh2_error(session, LIBSSH2_ERROR_KEYFILE_AUTH_FAILED,
563
                                 "bcrypted without passphrase");
564
            LIBSSH2_FREE(session, key);
565
            goto out;
566
        }
567

568
        /* Set up decryption */
569
        blocksize = method->blocksize;
570

571
        key_part = LIBSSH2_CALLOC(session, keylen);
572
        if(!key_part) {
573
            ret = _libssh2_error(session, LIBSSH2_ERROR_PROTO,
574
                                 "Could not alloc key part");
575
            goto out;
576
        }
577

578
        iv_part = LIBSSH2_CALLOC(session, ivlen);
579
        if(!iv_part) {
580
            ret = _libssh2_error(session, LIBSSH2_ERROR_PROTO,
581
                                 "Could not alloc iv part");
582
            goto out;
583
        }
584

585
        memcpy(key_part, key, keylen);
586
        memcpy(iv_part, key + keylen, ivlen);
587

588
        /* Initialize the decryption */
589
        if(method->init(session, method, iv_part, &free_iv, key_part,
590
                        &free_secret, 0, &abstract)) {
591
            ret = LIBSSH2_ERROR_DECRYPT;
592
            goto out;
593
        }
594

595
        /* Do the actual decryption */
596
        if((decrypted.len % blocksize) != 0) {
597
            method->dtor(session, &abstract);
598
            ret = LIBSSH2_ERROR_DECRYPT;
599
            goto out;
600
        }
601

602
        while((size_t)len_decrypted <= decrypted.len - blocksize) {
603
            /* We always pass MIDDLE_BLOCK here because OpenSSH Key Files
604
             * do not use AAD to authenticate the length.
605
             * Furthermore, the authentication tag is appended after the
606
             * encrypted key, and the length of the authentication tag is
607
             * not included in the key length, so we check it after the
608
             * loop.
609
             */
610
            if(method->crypt(session, decrypted.data + len_decrypted,
611
                             blocksize,
612
                             &abstract,
613
                             MIDDLE_BLOCK)) {
614
                ret = LIBSSH2_ERROR_DECRYPT;
615
                method->dtor(session, &abstract);
616
                goto out;
617
            }
618

619
            len_decrypted += blocksize;
620
        }
621

622
        /* No padding */
623

624
        /* for the AES GCM methods, the 16 byte authentication tag is
625
         * appended to the encrypted key */
626
        if(strcmp(method->name, "aes256-gcm@openssh.com") == 0 ||
627
           strcmp(method->name, "aes128-gcm@openssh.com") == 0) {
628
            if(!_libssh2_check_length(&decoded, 16)) {
629
                ret = _libssh2_error(session, LIBSSH2_ERROR_PROTO,
630
                                     "GCM auth tag missing");
631
                method->dtor(session, &abstract);
632
                goto out;
633
            }
634
            if(method->crypt(session, decoded.dataptr, 16, &abstract,
635
                             LAST_BLOCK)) {
636
                ret = _libssh2_error(session, LIBSSH2_ERROR_DECRYPT,
637
                                     "GCM auth tag invalid");
638
                method->dtor(session, &abstract);
639
                goto out;
640
            }
641
            decoded.dataptr += 16;
642
        }
643

644
        method->dtor(session, &abstract);
645
    }
646

647
    /* Check random bytes match */
648

649
    if(_libssh2_get_u32(&decrypted, &check1) != 0 ||
650
       _libssh2_get_u32(&decrypted, &check2) != 0 ||
651
       check1 != check2) {
652
        _libssh2_error(session, LIBSSH2_ERROR_PROTO,
653
                       "Private key unpack failed (correct password?)");
654
        ret = LIBSSH2_ERROR_KEYFILE_AUTH_FAILED;
655
        goto out;
656
    }
657

658
    if(decrypted_buf) {
659
        /* copy data to out-going buffer */
660
        struct string_buf *out_buf = _libssh2_string_buf_new(session);
661
        if(!out_buf) {
662
            ret = _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
663
                                 "Unable to allocate memory for "
664
                                 "decrypted struct");
665
            goto out;
666
        }
667

668
        out_buf->data = LIBSSH2_CALLOC(session, decrypted.len);
669
        if(!out_buf->data) {
670
            ret = _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
671
                                 "Unable to allocate memory for "
672
                                 "decrypted struct");
673
            _libssh2_string_buf_free(session, out_buf);
674
            goto out;
675
        }
676
        memcpy(out_buf->data, decrypted.data, decrypted.len);
677
        out_buf->dataptr = out_buf->data +
678
            (decrypted.dataptr - decrypted.data);
679
        out_buf->len = decrypted.len;
680

681
        *decrypted_buf = out_buf;
682
    }
683

684
out:
685

686
    /* Clean up */
687
    if(key) {
688
        _libssh2_explicit_zero(key, total_len);
689
        LIBSSH2_FREE(session, key);
690
    }
691
    if(key_part) {
692
        _libssh2_explicit_zero(key_part, keylen);
693
        LIBSSH2_FREE(session, key_part);
694
    }
695
    if(iv_part) {
696
        _libssh2_explicit_zero(iv_part, ivlen);
697
        LIBSSH2_FREE(session, iv_part);
698
    }
699
    if(f) {
700
        _libssh2_explicit_zero(f, f_len);
701
        LIBSSH2_FREE(session, f);
702
    }
703

704
    return ret;
705
}
706

707
int
708
_libssh2_openssh_pem_parse(LIBSSH2_SESSION * session,
709
                           const unsigned char *passphrase,
710
                           FILE * fp, struct string_buf **decrypted_buf)
711
{
712
    char line[LINE_SIZE];
713
    char *b64data = NULL;
714
    size_t b64datalen = 0;
715
    int ret = 0;
716

717
    /* read file */
718

719
    do {
720
        *line = '\0';
721

722
        if(readline(line, LINE_SIZE, fp)) {
723
            return -1;
724
        }
725
    }
726
    while(strcmp(line, OPENSSH_HEADER_BEGIN) != 0);
727

728
    if(readline(line, LINE_SIZE, fp)) {
729
        return -1;
730
    }
731

732
    do {
733
        if(*line) {
734
            char *tmp;
735
            size_t linelen;
736

737
            linelen = strlen(line);
738
            tmp = LIBSSH2_REALLOC(session, b64data, b64datalen + linelen);
739
            if(!tmp) {
740
                _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
741
                               "Unable to allocate memory for PEM parsing");
742
                ret = -1;
743
                goto out;
744
            }
745
            memcpy(tmp + b64datalen, line, linelen);
746
            b64data = tmp;
747
            b64datalen += linelen;
748
        }
749

750
        *line = '\0';
751

752
        if(readline(line, LINE_SIZE, fp)) {
753
            ret = -1;
754
            goto out;
755
        }
756
    } while(strcmp(line, OPENSSH_HEADER_END) != 0);
757

758
    if(!b64data) {
759
        return -1;
760
    }
761

762
    ret = _libssh2_openssh_pem_parse_data(session,
763
                                          passphrase,
764
                                          (const char *)b64data,
765
                                          b64datalen,
766
                                          decrypted_buf);
767

768
    if(b64data) {
769
        _libssh2_explicit_zero(b64data, b64datalen);
770
        LIBSSH2_FREE(session, b64data);
771
    }
772

773
out:
774

775
    return ret;
776
}
777

778
int
779
_libssh2_openssh_pem_parse_memory(LIBSSH2_SESSION * session,
780
                                  const unsigned char *passphrase,
781
                                  const char *filedata, size_t filedata_len,
782
                                  struct string_buf **decrypted_buf)
783
{
784
    char line[LINE_SIZE];
785
    char *b64data = NULL;
786
    size_t b64datalen = 0;
787
    size_t off = 0;
788
    int ret;
789

790
    if(!filedata || filedata_len <= 0)
791
        return _libssh2_error(session, LIBSSH2_ERROR_PROTO,
792
                              "Error parsing PEM: filedata missing");
793

794
    do {
795

796
        *line = '\0';
797

798
        if(off >= filedata_len)
799
            return _libssh2_error(session, LIBSSH2_ERROR_PROTO,
800
                                  "Error parsing PEM: "
801
                                  "OpenSSH header not found");
802

803
        if(readline_memory(line, LINE_SIZE, filedata, filedata_len, &off)) {
804
            return -1;
805
        }
806
    }
807
    while(strcmp(line, OPENSSH_HEADER_BEGIN) != 0);
808

809
    *line = '\0';
810

811
    do {
812
        if(*line) {
813
            char *tmp;
814
            size_t linelen;
815

816
            linelen = strlen(line);
817
            tmp = LIBSSH2_REALLOC(session, b64data, b64datalen + linelen);
818
            if(!tmp) {
819
                ret = _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
820
                                     "Unable to allocate memory for "
821
                                     "PEM parsing");
822
                goto out;
823
            }
824
            memcpy(tmp + b64datalen, line, linelen);
825
            b64data = tmp;
826
            b64datalen += linelen;
827
        }
828

829
        *line = '\0';
830

831
        if(off >= filedata_len) {
832
            ret = _libssh2_error(session, LIBSSH2_ERROR_PROTO,
833
                                 "Error parsing PEM: offset out of bounds");
834
            goto out;
835
        }
836

837
        if(readline_memory(line, LINE_SIZE, filedata, filedata_len, &off)) {
838
            ret = -1;
839
            goto out;
840
        }
841
    } while(strcmp(line, OPENSSH_HEADER_END) != 0);
842

843
    if(!b64data)
844
        return _libssh2_error(session, LIBSSH2_ERROR_PROTO,
845
                              "Error parsing PEM: base 64 data missing");
846

847
    ret = _libssh2_openssh_pem_parse_data(session, passphrase, b64data,
848
                                          b64datalen, decrypted_buf);
849

850
out:
851
    if(b64data) {
852
        _libssh2_explicit_zero(b64data, b64datalen);
853
        LIBSSH2_FREE(session, b64data);
854
    }
855
    return ret;
856

857
}
858

859
static int
860
read_asn1_length(const unsigned char *data,
861
                 size_t datalen, size_t *len)
862
{
863
    unsigned int lenlen;
864
    int nextpos;
865

866
    if(datalen < 1) {
867
        return -1;
868
    }
869
    *len = data[0];
870

871
    if(*len >= 0x80) {
872
        lenlen = *len & 0x7F;
873
        *len = data[1];
874
        if(1 + lenlen > datalen) {
875
            return -1;
876
        }
877
        if(lenlen > 1) {
878
            *len <<= 8;
879
            *len |= data[2];
880
        }
881
    }
882
    else {
883
        lenlen = 0;
884
    }
885

886
    nextpos = 1 + lenlen;
887
    if(lenlen > 2 || 1 + lenlen + *len > datalen) {
888
        return -1;
889
    }
890

891
    return nextpos;
892
}
893

894
int
895
_libssh2_pem_decode_sequence(unsigned char **data, size_t *datalen)
896
{
897
    size_t len;
898
    int lenlen;
899

900
    if(*datalen < 1) {
901
        return -1;
902
    }
903

904
    if((*data)[0] != '\x30') {
905
        return -1;
906
    }
907

908
    (*data)++;
909
    (*datalen)--;
910

911
    lenlen = read_asn1_length(*data, *datalen, &len);
912
    if(lenlen < 0 || lenlen + len != *datalen) {
913
        return -1;
914
    }
915

916
    *data += lenlen;
917
    *datalen -= lenlen;
918

919
    return 0;
920
}
921

922
int
923
_libssh2_pem_decode_integer(unsigned char **data, size_t *datalen,
924
                            unsigned char **i, unsigned int *ilen)
925
{
926
    size_t len;
927
    int lenlen;
928

929
    if(*datalen < 1) {
930
        return -1;
931
    }
932

933
    if((*data)[0] != '\x02') {
934
        return -1;
935
    }
936

937
    (*data)++;
938
    (*datalen)--;
939

940
    lenlen = read_asn1_length(*data, *datalen, &len);
941
    if(lenlen < 0 || lenlen + len > *datalen) {
942
        return -1;
943
    }
944

945
    *data += lenlen;
946
    *datalen -= lenlen;
947

948
    *i = *data;
949
    *ilen = (unsigned int)len;
950

951
    *data += len;
952
    *datalen -= len;
953

954
    return 0;
955
}
956

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.