Add API call for list/register/revoke SSL Certificate management (auth system: alternate for the user/password combo). I don't have a way to test this just yet (still need to write the other side of the system in the client first), but here is the expected inputs:

Namespace: "sysadm"
Name: "settings"
Arguments structure needs the "action" variable/value for all calls:

Action: "list_ssl_certs"
 - No additional input needed: will list the known/registered certificates organized by <username> : { <public_key> : <certificate as text> }

Action: "register_ssl_cert"
Example Payload: {"action" : "register_ssl_cert", "pub_key" : <public_key> }
The <public_key> string needs to match the public key of one of the certificates currently loaded into the server/client connection. This will register that certificate on the server and allow that user to authenticate without a password as long as that same certificate is loaded up in any future connections. No special outputs are send back (just overall error/ok status).

Action: "revoke_ssl_cert"
Example Payload: {"action" : "revoke_ssl_cert", "pub_key" : <public_key>, "user" : <optional-username> }
The <public_key> string needs to match one of the keys given by the list function (does not need to match any currently-loaded certs). The "user" field is optional, and allows a connection with full admin privileges to revoke certs belonging to other users.

Note about current user/connection permissions level:
If the current user has full admin access, the "list_ssl_certs" API call will return the registered certificates for all users on the system - otherwise it will only return the certificates for the current user. Similarly, the "revoke_ssl_cert" may be used to remove certs registered to other users only if the current user/connection has full admin access - otherwise it may only be used to manage the current user's certificates.
This commit is contained in:
Ken Moore
2016-02-10 13:26:46 -05:00
parent bea5f61858
commit 06edd43945
4 changed files with 51 additions and 10 deletions

View File

@@ -90,8 +90,7 @@ bool AuthorizationManager::RevokeCertificate(QString token, QString key, QString
return true;
}
QJsonObject AuthorizationManager::ListCertificates(QString token){
QJsonObject obj;
void AuthorizationManager::ListCertificates(QString token, QJsonObject *out){
QStringList keys; //Format: "RegisteredCerts/<user>/<key>"
if( hasFullAccess(token) ){
//Read all user's certs
@@ -106,14 +105,12 @@ QJsonObject AuthorizationManager::ListCertificates(QString token){
QJsonObject user; QString username;
for(int i=0; i<keys.length(); i++){
if(username!=keys[i].section("/",1,1)){
if(!user.isEmpty()){ obj.insert(username, user); user = QJsonObject(); } //save the current info to the output
if(!user.isEmpty()){ out->insert(username, user); user = QJsonObject(); } //save the current info to the output
username = keys[i].section("/",1,1); //save the new username for later
}
user.insert(keys[i].section("/",2,3000), CONFIG->value(keys[i]).toString() ); //just in case the key has additional "/" in it
}
if(!user.isEmpty() && !username.isEmpty()){ obj.insert(username, user); }
return obj;
if(!user.isEmpty() && !username.isEmpty()){ out->insert(username, user); }
}
//Generic functions

View File

@@ -19,10 +19,10 @@ public:
bool checkAuth(QString token); //see if the given token is valid
bool hasFullAccess(QString token); //see if the token is associated with a full-access account
//SSL Certificate register/revoke/list
//SSL Certificate register/revoke/list (should only run if the current token is valid)
bool RegisterCertificate(QString token, QSslCertificate cert); //if token is valid, register the given cert for future logins
bool RevokeCertificate(QString token, QString key, QString user=""); //user will be the current user if not empty - cannot touch other user's certs without full perms on current session
QJsonObject ListCertificates(QString token);
void ListCertificates(QString token, QJsonObject *out);
int checkAuthTimeoutSecs(QString token); //Return the number of seconds that a token is valid for

View File

@@ -31,7 +31,9 @@ RestOutputStruct::ExitCode WebSocket::AvailableSubsystems(bool allaccess, QJsonO
<namespace2/name2> : <read/write/other>,
}
*/
// - server settings (always available)
out->insert("sysadm/settings","read/write");
// - syscache
if(QFile::exists("/var/run/syscache.pipe")){
out->insert("rpc/syscache","read"); //no write to syscache - only reads
@@ -95,7 +97,9 @@ RestOutputStruct::ExitCode WebSocket::EvaluateBackendRequest(const RestInputStru
}
//Go through and forward this request to the appropriate sub-system
if(namesp=="rpc" && name=="dispatcher"){
if(namesp=="sysadm" && name=="settings"){
return EvaluateSysadmSettingsRequest(IN.args, out);
}else if(namesp=="rpc" && name=="dispatcher"){
return EvaluateDispatcherRequest(IN.fullaccess, IN.args, out);
}else if(namesp=="sysadm" && name=="beadm"){
return EvaluateSysadmBEADMRequest(IN.args, out);
@@ -119,6 +123,40 @@ RestOutputStruct::ExitCode WebSocket::EvaluateBackendRequest(const RestInputStru
}
// === SYSADM SETTINGS ===
RestOutputStruct::ExitCode WebSocket::EvaluateSysadmSettingsRequest(const QJsonValue in_args, QJsonObject *out){
if(!in_args.isObject()){ return RestOutputStruct::BADREQUEST; }
QJsonObject argsO = in_args.toObject();
QStringList keys = argsO.keys();
if(!keys.contains("action")){ return RestOutputStruct::BADREQUEST; }
QString act = argsO.value("action").toString();
bool ok = false;
if(act=="register_ssl_cert" && keys.contains("pub_key")){
//Additional arguments: "pub_key" (String), and the cert with that key must already be loaded into the connection
QString pub_key = argsO.value("pub_key").toString();\
//Now find the currently-loaded certificate with the given public key
QList<QSslCertificate> certs;
if(SOCKET!=0){ certs = SOCKET->sslConfiguration().peerCertificateChain(); }
else if(TSOCKET!=0){ certs = TSOCKET->peerCertificateChain(); }
for(int i=0; i<certs.length() && !ok; i++){
if(certs[i].publicKey().toPem()==pub_key){
//Certificate found - register it
ok = AUTHSYSTEM->RegisterCertificate(SockAuthToken, certs[i]);
}
}
}else if(act=="list_ssl_certs"){
AUTHSYSTEM->ListCertificates(SockAuthToken, out);
ok = true; //always works for current user (even if nothing found)
}else if(act=="revoke_ssl_cert" && keys.contains("pub_key") ){
//Additional arguments: "user" (optional), "pub_key" (String)
QString user; if(keys.contains("user")){ user = argsO.value("user").toString(); }
ok = AUTHSYSTEM->RevokeCertificate(SockAuthToken,argsO.value("pub_key").toString(), user);
}
if(ok){ return RestOutputStruct::OK; }
else{ return RestOutputStruct::BADREQUEST; }
}
//==== SYSCACHE ====
RestOutputStruct::ExitCode WebSocket::EvaluateSyscacheRequest(const QJsonValue in_args, QJsonObject *out){
//syscache only needs a list of sub-commands at the moment (might change later)

View File

@@ -43,8 +43,14 @@ private:
RestOutputStruct::ExitCode AvailableSubsystems(bool fullaccess, QJsonObject *out);
// -- Main subsystem parser
RestOutputStruct::ExitCode EvaluateBackendRequest(const RestInputStruct&, QJsonObject *out);
// -- Individual subsystems
// -- Server Settings Modification API
RestOutputStruct::ExitCode EvaluateSysadmSettingsRequest(const QJsonValue in_args, QJsonObject *out);
// -- rpc syscache API
RestOutputStruct::ExitCode EvaluateSyscacheRequest(const QJsonValue in_args, QJsonObject *out);
// -- rpc dispatcher API
RestOutputStruct::ExitCode EvaluateDispatcherRequest(bool allaccess, const QJsonValue in_args, QJsonObject *out);
// -- sysadm beadm API
RestOutputStruct::ExitCode EvaluateSysadmBEADMRequest(const QJsonValue in_args, QJsonObject *out);