From edbef3e759f287672cc4ca03b1883abc99f160d1 Mon Sep 17 00:00:00 2001 From: Ken Moore Date: Wed, 10 Feb 2016 12:43:06 -0500 Subject: [PATCH] Add a full chain of SSL Certificate management fucntions to the authorization manager, and setup the main "auth" API call to use the cert system if no password was supplied for authorization. While here, also move the location of the server config file to /usr/local/etc/sysadm.conf, and print out that location in the main server log file. --- src/server/AuthorizationManager.cpp | 104 ++++++++++++++++++++++++++-- src/server/AuthorizationManager.h | 8 ++- src/server/EventWatcher.cpp | 1 - src/server/SslServer.h | 2 +- src/server/WebSocket.cpp | 13 +++- src/server/main.cpp | 3 +- 6 files changed, 119 insertions(+), 12 deletions(-) diff --git a/src/server/AuthorizationManager.cpp b/src/server/AuthorizationManager.cpp index 9429419..070beb4 100644 --- a/src/server/AuthorizationManager.cpp +++ b/src/server/AuthorizationManager.cpp @@ -68,6 +68,55 @@ bool AuthorizationManager::hasFullAccess(QString token){ return ok; } +//SSL Certificate register/revoke/list +bool AuthorizationManager::RegisterCertificate(QString token, QSslCertificate cert){ + if(!checkAuth(token)){ return false; } + QString user = hashID(token).section("::::",2,2); //get the user name from the currently-valid token + CONFIG->setValue("RegisteredCerts/"+user+"/"+QString(cert.publicKey().toPem()), cert.toText()); + return true; +} + +bool AuthorizationManager::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 + QString cuser = hashID(token).section("::::",2,2); + if(user.isEmpty()){ user = cuser; } //only probe current user + if(user !=cuser){ + //Check permissions for this cross-user action + if(!hasFullAccess(token)){ return false; } + } + //Check that the given cert exists first + if( !CONFIG->contains("RegisteredCerts/"+user+"/"+key) ){ return false; } + CONFIG->remove("RegisteredCerts/"+user+"/"+key); + return true; +} + +QJsonObject AuthorizationManager::ListCertificates(QString token){ + QJsonObject obj; + QStringList keys; //Format: "RegisteredCerts//" + if( hasFullAccess(token) ){ + //Read all user's certs + keys = CONFIG->allKeys().filter("RegisteredCerts/"); + }else{ + //Only list certs for current user + QString cuser = hashID(token).section("::::",2,2); + keys = CONFIG->allKeys().filter("RegisteredCerts/"+cuser+"/"); + } + keys.sort(); + //Now put the known keys into the output structure arranged by username/key + QJsonObject user; QString username; + for(int i=0; ivalue(keys[i]).toString() ); //just in case the key has additional "/" in it + } + if(!user.isEmpty() && !username.isEmpty()){ obj.insert(username, user); } + + return obj; +} + +//Generic functions int AuthorizationManager::checkAuthTimeoutSecs(QString token){ //Return the number of seconds that a token is valid for if(!HASH.contains(token)){ return 0; } //invalid token @@ -96,7 +145,7 @@ QString AuthorizationManager::LoginUP(QHostAddress host, QString user, QString p }else{ ok = true; //allow local access for users without password } - + qDebug() << "User Login Attempt:" << user << " Success:" << ok << " IP:" << host.toString(); LogManager::log(LogManager::HOST, QString("User Login Attempt: ")+user+" Success: "+(ok?"true":"false")+" IP: "+host.toString() ); if(!ok){ @@ -108,10 +157,53 @@ QString AuthorizationManager::LoginUP(QHostAddress host, QString user, QString p }else{ //valid login - generate a new token for it ClearHostFail(host.toString()); - return generateNewToken(isOperator); + return generateNewToken(isOperator, user); } } +QString AuthorizationManager::LoginUC(QHostAddress host, QString user, QList certs){ + //Login w/ username & SSL certificate + bool localhost = ( (host== QHostAddress::LocalHost) || (host== QHostAddress::LocalHostIPv6) || (host.toString()=="::ffff:127.0.0.1") ); + bool ok = false; + //First check that the user is valid on the system and part of the operator group + bool isOperator = false; + if(user!="root" && user!="toor"){ + QStringList groups = getUserGroups(user); + if(groups.contains("wheel")){ isOperator = true; } //full-access user + else if(!groups.contains("operator")){ + return ""; //user not allowed access if not in either of the wheel/operator groups + } + }else{ isOperator = true; } + qDebug() << "Check username/certificate combination" << user << localhost; + + //Need to check the registered certificates for the designated user + if(!localhost || user=="root" || user=="toor"){ + for(int i=0; icontains("RegisteredCerts/"+user+"/"+QString(certs[i].publicKey().toPem()) ) ){ + //Cert was registered - check expiration info + // TO-DO + ok = true; + } + } + }else{ + ok = true; //allow local access for users without password + } + + qDebug() << "User Login Attempt:" << user << " Success:" << ok << " IP:" << host.toString(); + LogManager::log(LogManager::HOST, QString("User Login Attempt: ")+user+" Success: "+(ok?"true":"false")+" IP: "+host.toString() ); + if(!ok){ + //invalid login + //Bump the fail count for this host + bool overlimit = BumpFailCount(host.toString()); + if(overlimit){ emit BlockHost(host); } + return (overlimit ? "REFUSED" : ""); + }else{ + //valid login - generate a new token for it + ClearHostFail(host.toString()); + return generateNewToken(isOperator, user); + } +} + QString AuthorizationManager::LoginService(QHostAddress host, QString service){ bool localhost = ( (host== QHostAddress::LocalHost) || (host== QHostAddress::LocalHostIPv6) ); @@ -136,13 +228,13 @@ QString AuthorizationManager::LoginService(QHostAddress host, QString service){ }else{ return ""; } - }else{ return generateNewToken(false); }//services are never given operator privileges + }else{ return generateNewToken(false, service); }//services are never given operator privileges } // ========================= // PRIVATE // ========================= -QString AuthorizationManager::generateNewToken(bool isOp){ +QString AuthorizationManager::generateNewToken(bool isOp, QString user){ QString tok; for(int i=0; i certs); //Login w/ username & SSL certificate QString LoginService(QHostAddress host, QString service); //Login a particular automated service private: QHash HASH; QHash IPFAIL; - QString generateNewToken(bool isOperator); + QString generateNewToken(bool isOperator, QString name); QStringList getUserGroups(QString user); //Failure count management diff --git a/src/server/EventWatcher.cpp b/src/server/EventWatcher.cpp index 34756e7..4e5aee4 100644 --- a/src/server/EventWatcher.cpp +++ b/src/server/EventWatcher.cpp @@ -25,7 +25,6 @@ EventWatcher::~EventWatcher(){ } void EventWatcher::start(){ - // - DISPATCH Events starting = true; // - Life Preserver Events WatcherUpdate(LPLOG); //load it initially (will also add it to the watcher); diff --git a/src/server/SslServer.h b/src/server/SslServer.h index 021d4ec..54e8b5b 100644 --- a/src/server/SslServer.h +++ b/src/server/SslServer.h @@ -30,7 +30,7 @@ public: protected: void incomingConnection(qintptr socketDescriptor){ QSslSocket *serverSocket = new QSslSocket(this); - qDebug() << "New Ssl Connection:"; + //qDebug() << "New Ssl Connection:"; //setup any supported encruption types here serverSocket->setSslConfiguration(QSslConfiguration::defaultConfiguration()); serverSocket->setProtocol(SSLVERSION); diff --git a/src/server/WebSocket.cpp b/src/server/WebSocket.cpp index 31b9580..c58b79c 100644 --- a/src/server/WebSocket.cpp +++ b/src/server/WebSocket.cpp @@ -153,11 +153,20 @@ void WebSocket::EvaluateRequest(const RestInputStruct &REQ){ if(DEBUG){ qDebug() << "Authenticate Peer:" << SOCKET->peerAddress().toString(); } //Now do the auth if(out.in_struct.name=="auth" && out.in_struct.args.isObject() ){ - //username/password authentication + //username/[password/cert] authentication QString user, pass; if(out.in_struct.args.toObject().contains("username")){ user = JsonValueToString(out.in_struct.args.toObject().value("username")); } if(out.in_struct.args.toObject().contains("password")){ pass = JsonValueToString(out.in_struct.args.toObject().value("password")); } - SockAuthToken = AUTHSYSTEM->LoginUP(host, user, pass); + if(!pass.isEmpty()){ + //Use the given password + SockAuthToken = AUTHSYSTEM->LoginUP(host, user, pass); + }else{ + //No password - use the current SSL certificates instead + QList certs; + if(SOCKET!=0){ certs = SOCKET->sslConfiguration().peerCertificateChain(); } + else if(TSOCKET!=0){ certs = TSOCKET->peerCertificateChain(); } + SockAuthToken = AUTHSYSTEM->LoginUC(host, user, certs); + } }else if(out.in_struct.name == "auth_token" && out.in_struct.args.isObject()){ SockAuthToken = JsonValueToString(out.in_struct.args.toObject().value("token")); }else if(out.in_struct.name == "auth_clear"){ diff --git a/src/server/main.cpp b/src/server/main.cpp index e4ee487..44c1550 100644 --- a/src/server/main.cpp +++ b/src/server/main.cpp @@ -13,7 +13,7 @@ #define DEBUG 0 //Create any global classes -QSettings *CONFIG = new QSettings("PCBSD","sysadm"); +QSettings *CONFIG = new QSettings("/usr/local/etc/sysadm.conf", QSettings::IniFormat); EventWatcher *EVENTS = new EventWatcher(); Dispatcher *DISPATCHER = new Dispatcher(); bool WS_MODE = false; @@ -91,6 +91,7 @@ int main( int argc, char ** argv ) //Start the daemon int ret = 1; //error return value if( w->startServer(port, websocket) ){ + qDebug() << " - Configuration File:" << CONFIG->fileName(); QThread TBACK, TBACK2; EVENTS->moveToThread(&TBACK); DISPATCHER->moveToThread(&TBACK2);