Skip to content

Commit ef8d920

Browse files
LarsNordin-LNdatawillco007
authored andcommitted
Add passphrase support to _libssh2_pem_parse_memory (libssh2#1746)
Notes: Adds passphrase support to _libssh2_pem_parse_memory() and made _libssh2_pem_parse() call this function. Added two errors when the pem algorithm is not supported or MD5 is not supported during PEM parsing. Updated wincng.h to enable MD5. This resolves issue libssh2#1047 --------- Credit: Lars Nordin Co-authored-by: Will Cosgrove <will@panic.com>
1 parent 0dc4357 commit ef8d920

5 files changed

Lines changed: 100 additions & 103 deletions

File tree

src/libssh2_priv.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1239,6 +1239,7 @@ int _libssh2_pem_parse(LIBSSH2_SESSION * session,
12391239
int _libssh2_pem_parse_memory(LIBSSH2_SESSION * session,
12401240
const char *headerbegin,
12411241
const char *headerend,
1242+
const unsigned char *passphrase,
12421243
const char *filedata, size_t filedata_len,
12431244
unsigned char **data, size_t *datalen);
12441245
/* OpenSSL keys */

src/os400qc3.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2249,13 +2249,15 @@ _libssh2_rsa_new_private_frommemory(libssh2_rsa_ctx **rsa,
22492249
--> PKCS#8 EncryptedPrivateKeyInfo */
22502250
ret = _libssh2_pem_parse_memory(session,
22512251
beginencprivkeyhdr, endencprivkeyhdr,
2252+
passphrase,
22522253
filedata, filedata_len, &data, &datalen);
22532254

22542255
/* Try with "PRIVATE KEY" PEM armor.
22552256
--> PKCS#8 PrivateKeyInfo or EncryptedPrivateKeyInfo */
22562257
if(ret)
22572258
ret = _libssh2_pem_parse_memory(session,
22582259
beginprivkeyhdr, endprivkeyhdr,
2260+
passphrase,
22592261
filedata, filedata_len,
22602262
&data, &datalen);
22612263

src/pem.c

Lines changed: 93 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,9 @@
3939
*/
4040

4141
#include "libssh2_priv.h"
42+
#ifdef LIBSSH2_WINCNG
43+
#include "wincng.h" // get MD5 functions
44+
#endif
4245

4346
static int
4447
readline(char *line, int line_size, FILE * fp)
@@ -112,32 +115,78 @@ _libssh2_pem_parse(LIBSSH2_SESSION * session,
112115
const char *headerend,
113116
const unsigned char *passphrase,
114117
FILE * fp, unsigned char **data, size_t *datalen)
118+
{
119+
int ret;
120+
char *filedata = NULL;
121+
size_t filedata_len;
122+
123+
fseek(fp, 0L, SEEK_END);
124+
filedata_len = ftell(fp);
125+
fseek(fp, 0L, 0);
126+
127+
filedata = LIBSSH2_ALLOC(session, filedata_len);
128+
if(!filedata) {
129+
_libssh2_error(session, LIBSSH2_ERROR_ALLOC,
130+
"Unable to allocate memory for PEM parsing");
131+
ret = -1;
132+
goto out;
133+
}
134+
135+
if(fread(filedata, 1, filedata_len, fp) != filedata_len) {
136+
_libssh2_error(session, LIBSSH2_ERROR_FILE,
137+
"Bad read in PEM parsing");
138+
ret = -1;
139+
goto out;
140+
}
141+
142+
ret = _libssh2_pem_parse_memory(session, headerbegin, headerend,
143+
passphrase, filedata, filedata_len,
144+
data, datalen);
145+
146+
out:
147+
if(filedata) {
148+
LIBSSH2_FREE(session, filedata);
149+
}
150+
return ret;
151+
}
152+
153+
int
154+
_libssh2_pem_parse_memory(LIBSSH2_SESSION * session,
155+
const char *headerbegin,
156+
const char *headerend,
157+
const unsigned char *passphrase,
158+
const char *filedata, size_t filedata_len,
159+
unsigned char **data, size_t *datalen)
115160
{
116161
char line[LINE_SIZE];
117162
unsigned char iv[LINE_SIZE];
118163
char *b64data = NULL;
119164
size_t b64datalen = 0;
165+
size_t off = 0;
120166
int ret;
121-
const LIBSSH2_CRYPT_METHOD *method = NULL;
167+
const LIBSSH2_CRYPT_METHOD* method = NULL;
122168

123169
do {
124170
*line = '\0';
125171

126-
if(readline(line, LINE_SIZE, fp)) {
172+
if(readline_memory(line, LINE_SIZE, filedata, filedata_len, &off)) {
127173
return -1;
128174
}
175+
176+
if(!*line)
177+
break;
129178
} while(strcmp(line, headerbegin) != 0);
130179

131-
if(readline(line, LINE_SIZE, fp)) {
180+
if(readline_memory(line, LINE_SIZE, filedata, filedata_len, &off)) {
132181
return -1;
133182
}
134183

135184
if(passphrase &&
136-
memcmp(line, crypt_annotation, strlen(crypt_annotation)) == 0) {
137-
const LIBSSH2_CRYPT_METHOD **all_methods, *cur_method;
185+
memcmp(line, crypt_annotation, strlen(crypt_annotation)) == 0) {
186+
const LIBSSH2_CRYPT_METHOD** all_methods, * cur_method;
138187
int i;
139188

140-
if(readline(line, LINE_SIZE, fp)) {
189+
if(readline_memory(line, LINE_SIZE, filedata, filedata_len, &off)) {
141190
ret = -1;
142191
goto out;
143192
}
@@ -146,31 +195,37 @@ _libssh2_pem_parse(LIBSSH2_SESSION * session,
146195
/* !checksrc! disable EQUALSNULL 1 */
147196
while((cur_method = *all_methods++) != NULL) {
148197
if(*cur_method->pem_annotation &&
149-
memcmp(line, cur_method->pem_annotation,
150-
strlen(cur_method->pem_annotation)) == 0) {
198+
memcmp(line, cur_method->pem_annotation,
199+
strlen(cur_method->pem_annotation)) == 0) {
151200
method = cur_method;
152201
memcpy(iv, line + strlen(method->pem_annotation) + 1,
153-
2*method->iv_len);
202+
2 * method->iv_len);
154203
}
155204
}
156205

157206
/* None of the available crypt methods were able to decrypt the key */
158-
if(!method)
159-
return -1;
207+
if(!method) {
208+
_libssh2_error(session, LIBSSH2_ERROR_ALGO_UNSUPPORTED,
209+
"Unable to decrypt PEM, unsupported algorithm");
210+
ret = -1;
211+
goto out;
212+
}
160213

161214
/* Decode IV from hex */
162215
for(i = 0; i < method->iv_len; ++i) {
163-
iv[i] = (unsigned char)(hex_decode(iv[2*i]) << 4);
164-
iv[i] |= hex_decode(iv[2*i + 1]);
216+
iv[i] = (unsigned char)(hex_decode(iv[2 * i]) << 4);
217+
iv[i] |= hex_decode(iv[2 * i + 1]);
165218
}
166219

167220
/* skip to the next line */
168-
if(readline(line, LINE_SIZE, fp)) {
221+
if(readline_memory(line, LINE_SIZE, filedata, filedata_len, &off)) {
169222
ret = -1;
170223
goto out;
171224
}
172225
}
173226

227+
*line = '\0';
228+
174229
do {
175230
if(*line) {
176231
char *tmp;
@@ -191,7 +246,7 @@ _libssh2_pem_parse(LIBSSH2_SESSION * session,
191246

192247
*line = '\0';
193248

194-
if(readline(line, LINE_SIZE, fp)) {
249+
if(readline_memory(line, LINE_SIZE, filedata, filedata_len, &off)) {
195250
ret = -1;
196251
goto out;
197252
}
@@ -217,39 +272,40 @@ _libssh2_pem_parse(LIBSSH2_SESSION * session,
217272
if(method) {
218273
#if LIBSSH2_MD5_PEM
219274
/* Set up decryption */
220-
int free_iv = 0, free_secret = 0, len_decrypted = 0, padding = 0;
275+
int free_iv = 0, free_secret = 0, len_decrypted = 0;
276+
size_t padding = 0;
221277
int blocksize = method->blocksize;
222278
void *abstract;
223-
unsigned char secret[2*MD5_DIGEST_LENGTH];
279+
unsigned char secret[2 * MD5_DIGEST_LENGTH];
224280
libssh2_md5_ctx fingerprint_ctx;
225281

226282
/* Perform key derivation (PBKDF1/MD5) */
227283
if(!libssh2_md5_init(&fingerprint_ctx) ||
228-
!libssh2_md5_update(fingerprint_ctx, passphrase,
229-
strlen((const char *)passphrase)) ||
230-
!libssh2_md5_update(fingerprint_ctx, iv, 8) ||
231-
!libssh2_md5_final(fingerprint_ctx, secret)) {
284+
!libssh2_md5_update(fingerprint_ctx, passphrase,
285+
strlen((const char *)passphrase)) ||
286+
!libssh2_md5_update(fingerprint_ctx, iv, 8) ||
287+
!libssh2_md5_final(fingerprint_ctx, secret)) {
232288
ret = -1;
233289
goto out;
234290
}
235291
if(method->secret_len > MD5_DIGEST_LENGTH) {
236292
if(!libssh2_md5_init(&fingerprint_ctx) ||
237-
!libssh2_md5_update(fingerprint_ctx,
238-
secret, MD5_DIGEST_LENGTH) ||
239-
!libssh2_md5_update(fingerprint_ctx,
240-
passphrase,
241-
strlen((const char *)passphrase)) ||
242-
!libssh2_md5_update(fingerprint_ctx, iv, 8) ||
243-
!libssh2_md5_final(fingerprint_ctx,
244-
secret + MD5_DIGEST_LENGTH)) {
293+
!libssh2_md5_update(fingerprint_ctx,
294+
secret, MD5_DIGEST_LENGTH) ||
295+
!libssh2_md5_update(fingerprint_ctx,
296+
passphrase,
297+
strlen((const char *)passphrase)) ||
298+
!libssh2_md5_update(fingerprint_ctx, iv, 8) ||
299+
!libssh2_md5_final(fingerprint_ctx,
300+
secret + MD5_DIGEST_LENGTH)) {
245301
ret = -1;
246302
goto out;
247303
}
248304
}
249305

250306
/* Initialize the decryption */
251307
if(method->init(session, method, iv, &free_iv, secret,
252-
&free_secret, 0, &abstract)) {
308+
&free_secret, 0, &abstract)) {
253309
_libssh2_explicit_zero((char *)secret, sizeof(secret));
254310
_libssh2_explicit_zero(*data, *datalen);
255311
LIBSSH2_FREE(session, *data);
@@ -284,11 +340,11 @@ _libssh2_pem_parse(LIBSSH2_SESSION * session,
284340
else {
285341
while(len_decrypted <= (int)*datalen - blocksize) {
286342
if(method->crypt(session, 0, *data + len_decrypted, blocksize,
287-
&abstract,
288-
len_decrypted == 0 ? FIRST_BLOCK :
289-
((len_decrypted == (int)*datalen - blocksize) ?
290-
LAST_BLOCK : MIDDLE_BLOCK)
291-
)) {
343+
&abstract,
344+
len_decrypted == 0 ? FIRST_BLOCK :
345+
((len_decrypted == (int)*datalen - blocksize) ?
346+
LAST_BLOCK : MIDDLE_BLOCK)
347+
)) {
292348
ret = LIBSSH2_ERROR_DECRYPT;
293349
_libssh2_explicit_zero((char *)secret, sizeof(secret));
294350
method->dtor(session, &abstract);
@@ -317,6 +373,8 @@ _libssh2_pem_parse(LIBSSH2_SESSION * session,
317373
_libssh2_explicit_zero((char *)secret, sizeof(secret));
318374
method->dtor(session, &abstract);
319375
#else
376+
_libssh2_error(session, LIBSSH2_ERROR_ALGO_UNSUPPORTED,
377+
"Unable to decrypt PEM, MD5 not enabled");
320378
ret = -1;
321379
goto out;
322380
#endif
@@ -331,74 +389,6 @@ _libssh2_pem_parse(LIBSSH2_SESSION * session,
331389
return ret;
332390
}
333391

334-
int
335-
_libssh2_pem_parse_memory(LIBSSH2_SESSION * session,
336-
const char *headerbegin,
337-
const char *headerend,
338-
const char *filedata, size_t filedata_len,
339-
unsigned char **data, size_t *datalen)
340-
{
341-
char line[LINE_SIZE];
342-
char *b64data = NULL;
343-
size_t b64datalen = 0;
344-
size_t off = 0;
345-
int ret;
346-
347-
do {
348-
*line = '\0';
349-
350-
if(readline_memory(line, LINE_SIZE, filedata, filedata_len, &off)) {
351-
return -1;
352-
}
353-
} while(strcmp(line, headerbegin) != 0);
354-
355-
*line = '\0';
356-
357-
do {
358-
if(*line) {
359-
char *tmp;
360-
size_t linelen;
361-
362-
linelen = strlen(line);
363-
tmp = LIBSSH2_REALLOC(session, b64data, b64datalen + linelen);
364-
if(!tmp) {
365-
_libssh2_error(session, LIBSSH2_ERROR_ALLOC,
366-
"Unable to allocate memory for PEM parsing");
367-
ret = -1;
368-
goto out;
369-
}
370-
memcpy(tmp + b64datalen, line, linelen);
371-
b64data = tmp;
372-
b64datalen += linelen;
373-
}
374-
375-
*line = '\0';
376-
377-
if(readline_memory(line, LINE_SIZE, filedata, filedata_len, &off)) {
378-
ret = -1;
379-
goto out;
380-
}
381-
} while(strcmp(line, headerend) != 0);
382-
383-
if(!b64data) {
384-
return -1;
385-
}
386-
387-
if(_libssh2_base64_decode(session, (char **) data, datalen,
388-
b64data, b64datalen)) {
389-
ret = -1;
390-
goto out;
391-
}
392-
393-
ret = 0;
394-
out:
395-
if(b64data) {
396-
_libssh2_explicit_zero(b64data, b64datalen);
397-
LIBSSH2_FREE(session, b64data);
398-
}
399-
return ret;
400-
}
401-
402392
/* OpenSSH formatted keys */
403393
#define AUTH_MAGIC "openssh-key-v1"
404394
#define OPENSSH_HEADER_BEGIN "-----BEGIN OPENSSH PRIVATE KEY-----"

src/wincng.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -949,13 +949,15 @@ static int _libssh2_wincng_load_private_memory(LIBSSH2_SESSION *session,
949949
if(ret && tryLoadRSA) {
950950
ret = _libssh2_pem_parse_memory(session,
951951
PEM_RSA_HEADER, PEM_RSA_FOOTER,
952+
passphrase,
952953
privatekeydata, privatekeydata_len,
953954
&data, &datalen);
954955
}
955956

956957
if(ret && tryLoadDSA) {
957958
ret = _libssh2_pem_parse_memory(session,
958959
PEM_DSA_HEADER, PEM_DSA_FOOTER,
960+
passphrase,
959961
privatekeydata, privatekeydata_len,
960962
&data, &datalen);
961963
}

src/wincng.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@
5353
#include <windows.h>
5454
#include <bcrypt.h>
5555

56+
#define LIBSSH2_MD5_ENABLE
57+
#define LIBSSH2_MD5_PEM_ENABLE
5658
#define LIBSSH2_MD5 1
5759

5860
#define LIBSSH2_HMAC_RIPEMD 0

0 commit comments

Comments
 (0)