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);