added token validation to zms/zmu/zmuser

pull/2616/head
Pliable Pixels 2019-05-08 10:55:32 -04:00
parent e8f79f3254
commit b293592e4c
6 changed files with 87 additions and 19 deletions

View File

@ -139,6 +139,70 @@ User *zmLoadUser( const char *username, const char *password ) {
}
User *zmLoadTokenUser (std::string jwt_token_str, bool use_remote_addr ) {
std::string key = config.auth_hash_secret;
std::string remote_addr = "";
if (use_remote_addr) {
remote_addr = std::string(getenv( "REMOTE_ADDR" ));
if ( remote_addr == "" ) {
Warning( "Can't determine remote address, using null" );
remote_addr = "";
}
key += remote_addr;
}
Info ("Inside zmLoadTokenUser, formed key=%s", key.c_str());
auto decoded = jwt::decode(jwt_token_str);
auto verifier = jwt::verify()
.allow_algorithm(jwt::algorithm::hs256{ key })
.with_issuer("ZoneMinder");
try {
verifier.verify(decoded);
}
catch (const Exception &e) {
Error( "Unable to verify token: %s", e.getMessage().c_str() );
return 0;
}
// token is valid and not expired
if (decoded.has_payload_claim("user")) {
// We only need to check if user is enabled in DB and pass on
// correct access permissions
std::string username = decoded.get_payload_claim("user").as_string();
Info ("Got %s as user claim from token", username.c_str());
char sql[ZM_SQL_MED_BUFSIZ] = "";
snprintf(sql, sizeof(sql),
"SELECT Id, Username, Enabled, Stream+0, Events+0, Control+0, Monitors+0, System+0, MonitorIds"
" FROM Users where Username = '%s' and Enabled = 1", username.c_str() );
MYSQL_RES *result = mysql_store_result(&dbconn);
if ( !result ) {
Error("Can't use query result: %s", mysql_error(&dbconn));
exit(mysql_errno(&dbconn));
}
int n_users = mysql_num_rows(result);
if ( n_users != 1 ) {
mysql_free_result(result);
Warning("Unable to authenticate user %s", username);
return NULL;
}
MYSQL_ROW dbrow = mysql_fetch_row(result);
User *user = new User(dbrow);
Info ("Authenticated user '%s' via token", username.c_str());
return user;
}
else {
Error ("User not found in claim");
return 0;
}
}
// Function to validate an authentication string
User *zmLoadAuthUser( const char *auth, bool use_remote_addr ) {
#if HAVE_DECL_MD5 || HAVE_DECL_GNUTLS_FINGERPRINT

View File

@ -77,6 +77,7 @@ public:
User *zmLoadUser( const char *username, const char *password=0 );
User *zmLoadAuthUser( const char *auth, bool use_remote_addr );
User *zmLoadTokenUser( std::string jwt, bool use_remote_addr);
bool checkUser ( const char *username);
bool checkPass (const char *password);

View File

@ -70,6 +70,7 @@ int main( int argc, const char *argv[] ) {
std::string username;
std::string password;
char auth[64] = "";
std::string jwt_token_str = "";
unsigned int connkey = 0;
unsigned int playback_buffer = 0;
@ -158,6 +159,10 @@ int main( int argc, const char *argv[] ) {
playback_buffer = atoi(value);
} else if ( !strcmp( name, "auth" ) ) {
strncpy( auth, value, sizeof(auth)-1 );
} else if ( !strcmp( name, "token" ) ) {
jwt_token_str = value;
Info("ZMS: JWT token found: %s", jwt_token_str.c_str());
} else if ( !strcmp( name, "user" ) ) {
username = UriDecode( value );
} else if ( !strcmp( name, "pass" ) ) {
@ -181,11 +186,15 @@ int main( int argc, const char *argv[] ) {
if ( config.opt_use_auth ) {
User *user = 0;
if ( strcmp(config.auth_relay, "none") == 0 ) {
if (jwt_token_str != "") {
user = zmLoadTokenUser(jwt_token_str, config.auth_hash_ips);
}
else if ( strcmp(config.auth_relay, "none") == 0 ) {
if ( checkUser(username.c_str()) ) {
user = zmLoadUser(username.c_str());
} else {
Error("")
Error("Bad username");
}
} else {

View File

@ -138,6 +138,7 @@ void Usage(int status=-1) {
" -U, --username <username> : When running in authenticated mode the username and\n"
" -P, --password <password> : password combination of the given user\n"
" -A, --auth <authentication> : Pass authentication hash string instead of user details\n"
" -T, --token <token> : Pass JWT token string instead of user details\n"
"", stderr );
exit(status);
@ -263,6 +264,7 @@ int main(int argc, char *argv[]) {
char *username = 0;
char *password = 0;
char *auth = 0;
std::string jwt_token_str = "";
#if ZM_HAS_V4L
#if ZM_HAS_V4L2
int v4lVersion = 2;
@ -378,6 +380,9 @@ int main(int argc, char *argv[]) {
case 'A':
auth = optarg;
break;
case 'T':
jwt_token_str = std::string(optarg);
break;
#if ZM_HAS_V4L
case 'V':
v4lVersion = (atoi(optarg)==1)?1:2;
@ -438,10 +443,13 @@ int main(int argc, char *argv[]) {
user = zmLoadUser(username);
} else {
if ( !(username && password) && !auth ) {
Error("Username and password or auth string must be supplied");
if ( !(username && password) && !auth && (jwt_token_str=="")) {
Error("Username and password or auth/token string must be supplied");
exit_zmu(-1);
}
if (jwt_token_str != "") {
user = zmLoadTokenUser(jwt_token_str, false);
}
if ( auth ) {
user = zmLoadAuthUser(auth, false);
}

View File

@ -105,7 +105,7 @@ class HostController extends AppController {
// print ("relay=".$zmAuthRelay." haship=".$zmAuthHashIps." remote ip=".$_SERVER['REMOTE_ADDR']);
$expireAt = $issuedAt + $ttl * 3600;
$expireAt = $issuedAt + 30; // TEST REMOVE
$expireAt = $issuedAt + 60; // TEST REMOVE
$token = array(
"iss" => "ZoneMinder",

View File

@ -215,20 +215,6 @@ function validateToken ($token) {
return array(false, "No such user/credentials");
}
// We are NOT checking against session username for now...
/*
// at this stage, token is valid, but lets validate user with session user
ZM\Info ("JWT user is ".$jwt['user']);
if ($jwt['user'] != $_SESSION['username']) {
ZM\Error ("Unable to authenticate user. Token doesn't belong to current user");
return false;
} else {
ZM\Info ("Token validated for user:".$_SESSION['username']);
return $user;
}
*/
}
function getAuthUser($auth) {