Skip to content

Commit bb99293

Browse files
authored
Merge pull request #25 from cdoco/develop
Fix #23 -> Segfault with multiple jwt_decode using RSA
2 parents 35e3066 + 9cdb7a1 commit bb99293

File tree

3 files changed

+84
-33
lines changed

3 files changed

+84
-33
lines changed

jwt.c

+42-32
Original file line numberDiff line numberDiff line change
@@ -200,18 +200,15 @@ void jwt_b64_url_encode_ex(char *str)
200200
/* base64 encode */
201201
char *jwt_b64_url_encode(zend_string *input)
202202
{
203-
zend_string *b64_str = NULL;
204-
b64_str = php_base64_encode((const unsigned char *)ZSTR_VAL(input), ZSTR_LEN(input));
203+
zend_string *b64_str = php_base64_encode((const unsigned char *)ZSTR_VAL(input), ZSTR_LEN(input));
205204

206205
/* replace str */
207-
zend_string *new = zend_string_dup(b64_str, 0);
206+
char *new = estrdup(ZSTR_VAL(b64_str));
207+
jwt_b64_url_encode_ex(new);
208208

209-
jwt_b64_url_encode_ex(ZSTR_VAL(new));
210-
211-
zend_string_free(new);
212209
zend_string_free(b64_str);
213210

214-
return ZSTR_VAL(new);
211+
return new;
215212
}
216213

217214
/* base64 decode */
@@ -450,9 +447,9 @@ int jwt_parse_options(zval *options)
450447
static void php_jwt_encode(INTERNAL_FUNCTION_PARAMETERS) {
451448
zval *payload = NULL, header;
452449
zend_string *key = NULL;
453-
smart_str json_header = {0}, json_payload = {0}, segments = {0};
450+
smart_str json_header = {0}, json_payload = {0};
454451

455-
char *sig = NULL, *alg = "HS256";
452+
char *sig = NULL, *alg = "HS256", *buf = NULL;
456453
unsigned int sig_len;
457454
size_t alg_len;
458455
jwt_t *jwt = NULL;
@@ -481,61 +478,74 @@ static void php_jwt_encode(INTERNAL_FUNCTION_PARAMETERS) {
481478

482479
/* json encode */
483480
php_json_encode(&json_header, &header, 0);
481+
char *header_b64 = jwt_b64_url_encode(json_header.s);
482+
484483
php_json_encode(&json_payload, payload, 0);
484+
char *payload_b64 = jwt_b64_url_encode(json_payload.s);
485485

486486
zval_ptr_dtor(&header);
487-
488-
/* base64 encode */
489-
smart_str_appends(&segments, jwt_b64_url_encode(json_header.s));
490-
smart_str_appends(&segments, ".");
491-
smart_str_appends(&segments, jwt_b64_url_encode(json_payload.s));
492-
493487
smart_str_free(&json_header);
494488
smart_str_free(&json_payload);
495489

490+
buf = (char *)emalloc(strlen(header_b64) + strlen(payload_b64) + 1);
491+
strcpy(buf, header_b64);
492+
strcat(buf, ".");
493+
strcat(buf, payload_b64);
494+
495+
efree(header_b64);
496+
efree(payload_b64);
497+
496498
/* sign */
497499
if (jwt->alg == JWT_ALG_NONE) {
498500
/* alg none */
499-
smart_str_appendl(&segments, ".", 1);
501+
buf = (char *)erealloc(buf, strlen(buf) + 1);
502+
strcat(buf, ".");
500503
} else {
501504
/* set jwt struct */
502505
jwt->key = key;
503-
jwt->str = segments.s;
506+
jwt->str = zend_string_init(buf, strlen(buf), 0);
504507

505508
/* sign */
506509
if (jwt_sign(jwt, &sig, &sig_len)) {
507510
zend_throw_exception(spl_ce_DomainException, "OpenSSL unable to sign data", 0);
511+
zend_string_free(jwt->str);
508512
goto encode_done;
509513
}
510514

511515
/* string concatenation */
512-
smart_str_appends(&segments, ".");
513-
514516
zend_string *sig_str = zend_string_init(sig, sig_len, 0);
517+
char *sig_b64 = jwt_b64_url_encode(sig_str);
515518

516-
smart_str_appends(&segments, jwt_b64_url_encode(sig_str));
519+
char *tmp = (char *)emalloc(strlen(sig_b64) + strlen(buf) + 1);
520+
sprintf(tmp, "%s.%s", buf, sig_b64);
521+
522+
efree(buf);
523+
buf = tmp;
524+
525+
efree(sig_b64);
526+
zend_string_free(jwt->str);
517527
zend_string_free(sig_str);
518528
}
519529

520-
smart_str_0(&segments);
521-
522530
encode_done:
523531
/* free */
524532
if (sig)
525533
efree(sig);
526534

527535
jwt_free(jwt);
528536

529-
if (segments.s) {
530-
RETURN_STR(segments.s);
531-
}
537+
char *ret = alloca(strlen(buf));
538+
strcpy(ret, buf);
539+
efree(buf);
540+
541+
RETURN_STRING(ret);
532542
}
533543

534544
/* Jwt decode */
535545
static void php_jwt_decode(INTERNAL_FUNCTION_PARAMETERS) {
536546
zend_string *token = NULL, *key = NULL;
537547
zval *options = NULL;
538-
smart_str segments = {0};
548+
smart_str buf = {0};
539549
char *body = NULL, *sig = NULL;
540550
jwt_t *jwt = NULL;
541551

@@ -614,17 +624,16 @@ static void php_jwt_decode(INTERNAL_FUNCTION_PARAMETERS) {
614624
/* set jwt struct */
615625
jwt->key = key;
616626

617-
smart_str_appends(&segments, head);
618-
smart_str_appends(&segments, ".");
619-
smart_str_appends(&segments, body);
627+
smart_str_appends(&buf, head);
628+
smart_str_appends(&buf, ".");
629+
smart_str_appends(&buf, body);
620630

621-
jwt->str = segments.s;
631+
jwt->str = buf.s;
622632

623633
if (jwt_verify(jwt, sig)) {
624634
zend_throw_exception(jwt_signature_invalid_cex, "Signature verification failed", 0);
635+
goto decode_done;
625636
}
626-
627-
smart_str_free(&segments);
628637
}
629638

630639
/* verify body */
@@ -635,6 +644,7 @@ static void php_jwt_decode(INTERNAL_FUNCTION_PARAMETERS) {
635644
decode_done:
636645
efree(head);
637646
jwt_free(jwt);
647+
smart_str_free(&buf);
638648
}
639649

640650
/* function jwt_encode() */

tests/015.phpt

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ $hmackey = "example-hmac-key";
88

99
try {
1010
$decoded_token = jwt_decode('eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJkYXRhIjp7Im5hbWUiOiJaaUhhbmcgR2FvIiwiYWRtaW4iOnRydWV9LCJzdWIiOiIxMjM0NTY3ODkwIiwibmJmIjoxNTQ2ODQ4CJhdWQiOiJ5eSJ9.fDqiF-cCIvlcscIdz7dcFJoYGBcvHtI6MWB5IWG0VHA', $hmackey, ['algorithm' => 'HS256']);
11-
} catch (UnexpectedValueException $e) {
11+
} catch (SignatureInvalidException $e) {
1212
// Handle expired token
1313
echo "FAIL\n";
1414
}

tests/016.phpt

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
--TEST--
2+
ISSUE #23 Segfault with multiple jwt_decode using RSA
3+
--SKIPIF--
4+
<?php if (!extension_loaded("jwt")) print "skip"; ?>
5+
--FILE--
6+
<?php
7+
function generateKeyPair()
8+
{
9+
$key = openssl_pkey_new([
10+
'digest_alg' => 'sha512',
11+
'private_key_bits' => 1024,
12+
'private_key_type' => OPENSSL_KEYTYPE_RSA,
13+
]);
14+
openssl_pkey_export($key, $private);
15+
$public = openssl_pkey_get_details($key)['key'];
16+
openssl_pkey_free($key);
17+
return [$public, $private];
18+
}
19+
20+
list($apub, $apriv) = generateKeyPair();
21+
list($bpub, $bpriv) = generateKeyPair();
22+
23+
$payload = ['message' => 'hello world'];
24+
$token = jwt_encode($payload, $apriv, 'RS512');
25+
$decoded = jwt_decode($token, $apub, ['algorithm' => 'RS512']);
26+
print_r($decoded);
27+
28+
$payload = ['message' => 'hello world 2'];
29+
$token = jwt_encode($payload, $bpriv, 'RS512');
30+
$decoded = jwt_decode($token, $bpub, ['algorithm' => 'RS512']);
31+
print_r($decoded);
32+
?>
33+
--EXPECT--
34+
Array
35+
(
36+
[message] => hello world
37+
)
38+
Array
39+
(
40+
[message] => hello world 2
41+
)

0 commit comments

Comments
 (0)