Skip to content

Commit

Permalink
algorithm not needed anymore when decoding the token + add tests for …
Browse files Browse the repository at this point in the history
…all algorithms
  • Loading branch information
AnthonyDeroche committed Nov 27, 2016
1 parent 217abea commit 01fc41a
Show file tree
Hide file tree
Showing 6 changed files with 177 additions and 126 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ install:
- make
- sudo make install
before_script:
- pip install pyjwt requests
- pip install pyjwt requests cryptography
- chmod ugo+x tests/debian_tests.sh
- cd tests
script: ./debian_tests.sh
156 changes: 86 additions & 70 deletions mod_authnz_jwt.c
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
#define DEFAULT_FORM_USERNAME "user"
#define DEFAULT_FORM_PASSWORD "password"
#define DEFAULT_ATTRIBUTE_USERNAME "user"
#define DEFAULT_SIGNATURE_ALGORITHM "HS256"


/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ CONFIGURATION STRUCTURE ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
Expand Down Expand Up @@ -143,7 +144,7 @@ static int create_token(request_rec *r, char** token_str, const char* username);
static int auth_jwt_authn_with_token(request_rec *r);

static void get_encode_key(request_rec* r, const char* algorithm, unsigned char* key);
static void get_decode_key(request_rec* r, const char* algorithm, unsigned char* key);
static void get_decode_key(request_rec* r, unsigned char* key);
static int token_check(request_rec *r, jwt_t **jwt, const char *token, const unsigned char *key);
static int token_decode(jwt_t **jwt, const char* token, const unsigned char *key);
static int token_new(jwt_t **jwt);
Expand All @@ -152,11 +153,12 @@ static long token_get_claim_int(jwt_t *token, const char* claim);
static int token_add_claim(jwt_t *jwt, const char *claim, const char *val);
static int token_add_claim_int(jwt_t *jwt, const char *claim, long val);
static void token_free(jwt_t *token);
static int token_set_alg(jwt_t *jwt, jwt_alg_t alg, const unsigned char *key);
static int token_set_alg(request_rec *r, jwt_t *jwt, const char* alg, const unsigned char *key);
static char *token_encode_str(jwt_t *jwt);
static char** token_get_claim_array_of_string(request_rec* r, jwt_t *token, const char* claim, int* len);
static json_t* token_get_claim_array(request_rec* r, jwt_t *token, const char* claim);
static json_t* token_get_claim_json(request_rec* r, jwt_t *token, const char* claim);
static const char* token_get_alg(jwt_t *jwt);


/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ DECLARE DIRECTIVES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
Expand Down Expand Up @@ -309,7 +311,7 @@ static void* get_config_value(request_rec *r, jwt_directive directive){
}else if(sconf->signature_algorithm){
value = (void*)sconf->signature_algorithm;
}else{
return NULL;
return DEFAULT_SIGNATURE_ALGORITHM;
}
break;
case dir_signature_shared_secret:
Expand Down Expand Up @@ -764,37 +766,12 @@ static int create_token(request_rec *r, char** token_str, const char* username){
int* exp_delay_ptr = (int*)get_config_value(r, dir_exp_delay);
int* nbf_delay_ptr = (int*)get_config_value(r, dir_nbf_delay);



jwt_alg_t algorithm;
if(!strcmp(signature_algorithm, "HS512")){
algorithm = JWT_ALG_HS512;
}else if(!strcmp(signature_algorithm, "HS384")){
algorithm = JWT_ALG_HS384;
}else if(!strcmp(signature_algorithm, "HS256")){
algorithm = JWT_ALG_HS256;
}else if(!strcmp(signature_algorithm, "RS512")){
algorithm = JWT_ALG_RS512;
}else if(!strcmp(signature_algorithm, "RS384")){
algorithm = JWT_ALG_RS384;
}else if(!strcmp(signature_algorithm, "RS256")){
algorithm = JWT_ALG_RS256;
}else if(!strcmp(signature_algorithm, "ES512")){
algorithm = JWT_ALG_ES512;
}else if(!strcmp(signature_algorithm, "ES384")){
algorithm = JWT_ALG_ES384;
}else if(!strcmp(signature_algorithm, "ES256")){
algorithm = JWT_ALG_ES256;
}else{
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(55304)
"Unknown algorithm %s", signature_algorithm);
return HTTP_INTERNAL_SERVER_ERROR;
}

ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(55305)
"auth_jwt create_token: using algorithm %s...", signature_algorithm);

token_set_alg(token, algorithm, sign_key);
if(token_set_alg(r, token, signature_algorithm, sign_key)!=0){
return HTTP_INTERNAL_SERVER_ERROR;
}


time_t now = time(NULL);
Expand Down Expand Up @@ -952,11 +929,9 @@ static int auth_jwt_authn_with_token(request_rec *r){
char* authorization_header = (char*)apr_table_get( r->headers_in, "Authorization");
char* token_str;

char* signature_algorithm = (char*)get_config_value(r, dir_signature_algorithm);


unsigned char key[MAX_KEY_LEN] = { 0 };
get_decode_key(r, signature_algorithm, key);

get_decode_key(r, key);

if(strlen((const char*)key)==0){
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(55403)
Expand All @@ -976,11 +951,15 @@ static int auth_jwt_authn_with_token(request_rec *r){
token_str = authorization_header+7;
jwt_t* token;
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(55405)
"auth_jwt authn: checking signature with algorithm %s and fields correctness...", signature_algorithm);
"auth_jwt authn: checking signature and fields correctness...");
rv = token_check(r, &token, token_str, key);

if(OK == rv){
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(55406)
"auth_jwt authn: signature is correct");
const char* found_alg = token_get_alg(token);
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(55405)
"auth_jwt authn: algorithm found is %s", found_alg);
const char* attribute_username = (const char*)get_config_value(r, dir_attribute_username);
char* maybe_user = (char *)token_get_claim(token, attribute_username);
if(maybe_user == NULL){
Expand Down Expand Up @@ -1014,11 +993,6 @@ static int auth_jwt_authn_with_token(request_rec *r){
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ TOKEN OPERATIONS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */

static void get_encode_key(request_rec *r, const char* signature_algorithm, unsigned char* key){
if(!signature_algorithm){
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(55500)
"Signature algorithm is NULL. This error should not happen since a default algorithm is set");
return;
}

if(strcmp(signature_algorithm, "HS512")==0 || strcmp(signature_algorithm, "HS384")==0 || strcmp(signature_algorithm, "HS256")==0){
char* signature_shared_secret = (char*)get_config_value(r, dir_signature_shared_secret);
Expand Down Expand Up @@ -1060,30 +1034,26 @@ static void get_encode_key(request_rec *r, const char* signature_algorithm, unsi
}
}

static void get_decode_key(request_rec *r, const char* signature_algorithm, unsigned char* key){
if(!signature_algorithm){
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(55506)
"Signature algorithm is NULL. This error should not happen since a default algorithm is set");
static void get_decode_key(request_rec *r, unsigned char* key){
char* signature_public_key_file = (char*)get_config_value(r, dir_signature_public_key_file);
char* signature_shared_secret = (char*)get_config_value(r, dir_signature_shared_secret);

if(!signature_shared_secret && !signature_public_key_file){
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(55507)
"You must specify either AuthJWTSignatureSharedSecret directive or AuthJWTSignaturePublicKeyFile directive in configuration for decoding process");
return;
}
}

if(strcmp(signature_algorithm, "HS512")==0 || strcmp(signature_algorithm, "HS384")==0 || strcmp(signature_algorithm, "HS256")==0){
char* signature_shared_secret = (char*)get_config_value(r, dir_signature_shared_secret);
if(!signature_shared_secret){
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(55507)
"You must specify AuthJWTSignatureSharedSecret directive in configuration with algorithm %s", signature_algorithm);
return;
}
strcpy((char*)key,(const char*)signature_shared_secret);
}
else if(strcmp(signature_algorithm, "RS512")==0 || strcmp(signature_algorithm, "RS384")==0 || strcmp(signature_algorithm, "RS256")==0 ||
strcmp(signature_algorithm, "ES512")==0 || strcmp(signature_algorithm, "ES384")==0 || strcmp(signature_algorithm, "ES256")==0){
char* signature_public_key_file = (char*)get_config_value(r, dir_signature_public_key_file);
if(!signature_public_key_file){
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(55508)
"You must specify AuthJWTSignaturePublicKeyFile directive in configuration with algorithm %s", signature_algorithm);
return;
}
if(signature_shared_secret && signature_public_key_file){
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(55507)
"Conflict in configuration: you must specify either AuthJWTSignatureSharedSecret directive or AuthJWTSignaturePublicKeyFile directive but not both in the same block");
return;
}

if(signature_shared_secret){
strcpy((char*)key,(const char*)signature_shared_secret);
}
else if(signature_public_key_file){
apr_status_t rv;
apr_file_t* key_fd = NULL;
rv = apr_file_open(&key_fd, signature_public_key_file, APR_READ, APR_OS_DEFAULT, r->pool);
Expand All @@ -1100,11 +1070,7 @@ static void get_decode_key(request_rec *r, const char* signature_algorithm, unsi
return;
}
apr_file_close(key_fd);
} else {
//unknown algorithm
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(55511)
"Unknown algorithm %s", signature_algorithm);
}
}
}

static int token_new(jwt_t **jwt){
Expand Down Expand Up @@ -1277,8 +1243,58 @@ static json_t* token_get_claim_json(request_rec *r, jwt_t *token, const char* cl
return json;
}

static int token_set_alg(jwt_t *jwt, jwt_alg_t alg, const unsigned char *key){
return jwt_set_alg(jwt, alg, key, strlen((const char*)key));
static int token_set_alg(request_rec *r, jwt_t *jwt, const char* signature_algorithm, const unsigned char *key){
jwt_alg_t algorithm;
if(!strcmp(signature_algorithm, "HS512")){
algorithm = JWT_ALG_HS512;
}else if(!strcmp(signature_algorithm, "HS384")){
algorithm = JWT_ALG_HS384;
}else if(!strcmp(signature_algorithm, "HS256")){
algorithm = JWT_ALG_HS256;
}else if(!strcmp(signature_algorithm, "RS512")){
algorithm = JWT_ALG_RS512;
}else if(!strcmp(signature_algorithm, "RS384")){
algorithm = JWT_ALG_RS384;
}else if(!strcmp(signature_algorithm, "RS256")){
algorithm = JWT_ALG_RS256;
}else if(!strcmp(signature_algorithm, "ES512")){
algorithm = JWT_ALG_ES512;
}else if(!strcmp(signature_algorithm, "ES384")){
algorithm = JWT_ALG_ES384;
}else if(!strcmp(signature_algorithm, "ES256")){
algorithm = JWT_ALG_ES256;
}else{
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(55304)
"Unknown algorithm %s", signature_algorithm);
return 1;
}
return jwt_set_alg(jwt, algorithm, key, strlen((const char*)key));
}

static const char* token_get_alg(jwt_t *jwt){
jwt_alg_t algorithm = jwt_get_alg(jwt);
switch(algorithm){
case JWT_ALG_HS256:
return "HS256";
case JWT_ALG_HS384:
return "HS384";
case JWT_ALG_HS512:
return "HS512";
case JWT_ALG_RS256:
return "RS256";
case JWT_ALG_RS384:
return "RS384";
case JWT_ALG_RS512:
return "RS512";
case JWT_ALG_ES256:
return "ES256";
case JWT_ALG_ES384:
return "ES384";
case JWT_ALG_ES512:
return "ES512";
default:
return NULL;
}
}

static void token_free(jwt_t *token){
Expand Down
36 changes: 0 additions & 36 deletions tests/apache_hmac.conf

This file was deleted.

61 changes: 61 additions & 0 deletions tests/apache_jwt.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
<VirtualHost *:80>
ServerName testjwt.local
DocumentRoot /var/www/testjwt/

AuthJWTExpDelay 1800
AuthJWTNbfDelay 0
AuthJWTIss testjwt.local
AuthJWTAud tests
AuthJWTLeeway 10

LogLevel auth_jwt:debug
RewriteEngine On

Alias "/hmac_secured" "/var/www/testjwt"
Alias "/rsa_secured" "/var/www/testjwt"
Alias "/ec_secured" "/var/www/testjwt"

<Directory /var/www/testjwt/>
AllowOverride None
Options -Indexes
Require all granted
</Directory>

<Location "/hmac_secured">
AuthJWTSignatureSharedSecret secret
AllowOverride None
Options -Indexes
AuthType jwt
AuthName "private area"
Require valid-user
</Location>

<Location "/rsa_secured">
AuthJWTSignaturePublicKeyFile /tmp/rsa-pub.pem
AllowOverride None
Options -Indexes
AuthType jwt
AuthName "private area"
Require valid-user
</Location>

<Location "/ec_secured">
AuthJWTSignaturePublicKeyFile /tmp/ec-pub.pem
AllowOverride None
Options -Indexes
AuthType jwt
AuthName "private area"
Require valid-user
</Location>

<Location "/jwt_login">
AuthJWTSignatureAlgorithm HS256
AuthJWTSignatureSharedSecret secret
SetHandler jwt-login-handler
AuthJWTProvider file
AuthUserFile /var/www/jwt.htpasswd
</Location>

ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>
25 changes: 17 additions & 8 deletions tests/debian_tests.sh
Original file line number Diff line number Diff line change
@@ -1,17 +1,26 @@
#!/bin/bash
set -ev
sudo cp apache_hmac.conf /etc/apache2/sites-available/
sudo mkdir -p /var/www/testjwt/jwt_secured/
sudo touch /var/www/testjwt/jwt_secured/index.html
sudo cp apache_jwt.conf /etc/apache2/sites-available/
sudo cp jwt.htpasswd /var/www/jwt.htpasswd
sudo mkdir -p /var/www/testjwt/
sudo touch /var/www/testjwt/index.html

if ! sudo a2query -s apache_hmac > /dev/null; then
sudo a2ensite apache_hmac
sudo service apache2 restart
openssl ecparam -name secp256k1 -genkey -noout -out /tmp/ec-priv.pem
openssl ec -in /tmp/ec-priv.pem -pubout -out /tmp/ec-pub.pem

openssl genpkey -algorithm RSA -out /tmp/rsa-priv.pem -pkeyopt rsa_keygen_bits:4096
openssl rsa -pubout -in /tmp/rsa-priv.pem -out /tmp/rsa-pub.pem

if ! sudo a2query -m rewrite > /dev/null; then
sudo a2enmod rewrite
fi
if ! sudo a2query -s apache_jwt > /dev/null; then
sudo a2ensite apache_jwt
fi
sudo service apache2 restart

if ! grep -q "hmac.testjwt.local" /etc/hosts; then
echo "127.0.0.1 hmac.testjwt.local" | sudo tee --append /etc/hosts > /dev/null
if ! grep -q "testjwt.local" /etc/hosts; then
echo "127.0.0.1 testjwt.local" | sudo tee --append /etc/hosts > /dev/null
fi

python3 -m unittest discover . -v -f --locals
Loading

0 comments on commit 01fc41a

Please sign in to comment.