Skip to content

Commit 1a4155b

Browse files
committed
Fix #23 -> Segfault with multiple jwt_decode using RSA
1 parent 63c7bfb commit 1a4155b

File tree

2 files changed

+80
-31
lines changed

2 files changed

+80
-31
lines changed

jwt.c

+39-31
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,72 @@ 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);
518+
519+
buf = (char *)erealloc(buf, strlen(sig_b64) + strlen(buf) + 1);
520+
strcat(buf, ".");
521+
strcat(buf, sig_b64);
515522

516-
smart_str_appends(&segments, jwt_b64_url_encode(sig_str));
523+
efree(sig_b64);
524+
zend_string_free(jwt->str);
517525
zend_string_free(sig_str);
518526
}
519527

520-
smart_str_0(&segments);
521-
522528
encode_done:
523529
/* free */
524530
if (sig)
525531
efree(sig);
526532

527533
jwt_free(jwt);
528534

529-
if (segments.s) {
530-
RETURN_STR(segments.s);
531-
}
535+
char *ret = alloca(strlen(buf));
536+
strcpy(ret, buf);
537+
efree(buf);
538+
539+
RETURN_STRING(ret);
532540
}
533541

534542
/* Jwt decode */
535543
static void php_jwt_decode(INTERNAL_FUNCTION_PARAMETERS) {
536544
zend_string *token = NULL, *key = NULL;
537545
zval *options = NULL;
538-
smart_str segments = {0};
546+
smart_str buf = {0};
539547
char *body = NULL, *sig = NULL;
540548
jwt_t *jwt = NULL;
541549

@@ -614,17 +622,17 @@ static void php_jwt_decode(INTERNAL_FUNCTION_PARAMETERS) {
614622
/* set jwt struct */
615623
jwt->key = key;
616624

617-
smart_str_appends(&segments, head);
618-
smart_str_appends(&segments, ".");
619-
smart_str_appends(&segments, body);
625+
smart_str_appends(&buf, head);
626+
smart_str_appends(&buf, ".");
627+
smart_str_appends(&buf, body);
620628

621-
jwt->str = segments.s;
629+
jwt->str = buf.s;
622630

623631
if (jwt_verify(jwt, sig)) {
624632
zend_throw_exception(jwt_signature_invalid_cex, "Signature verification failed", 0);
625633
}
626634

627-
smart_str_free(&segments);
635+
smart_str_free(&buf);
628636
}
629637

630638
/* verify body */

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)