// =============================== // PC-BSD REST/JSON API Server // Available under the 3-clause BSD License // Written by: Ken Moore July 2015 // ================================= #include "AuthorizationManager.h" #include "globals.h" // Stuff for PAM to work #include #include #include #include #include #include #include #include //Internal defines // -- token management #define TIMEOUTSECS 900 // (15 minutes) time before a token becomes invalid #define AUTHCHARS QString("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789") #define TOKENLENGTH 20 // -- Connection failure limitations #define AUTHFAILLIMIT 5 //number of sequential failures before IP is blocked for a time #define FAILOVERMINS 10 //after this many minutes without a new login attempt the failure count will reset AuthorizationManager::AuthorizationManager() : QObject(){ HASH.clear(); IPFAIL.clear(); //initialize the random number generator (need to generate auth tokens) qsrand(QDateTime::currentMSecsSinceEpoch()); } AuthorizationManager::~AuthorizationManager(){ } // == Token Interaction functions == void AuthorizationManager::clearAuth(QString token){ //clear an authorization token QString id = hashID(token); if(!id.isEmpty()){ HASH.remove(id); } } bool AuthorizationManager::checkAuth(QString token){ //see if the given token is valid bool ok = false; QString id = hashID(token); if(!id.isEmpty()){ //Also verify that the token has not timed out ok = (HASH[id] > QDateTime::currentDateTime()); if(ok){ HASH.insert(id, QDateTime::currentDateTime().addSecs(TIMEOUTSECS)); } //valid - bump the timestamp } return ok; } bool AuthorizationManager::hasFullAccess(QString token){ bool ok = false; QString id = hashID(token); if(!id.isEmpty()){ //Also verify that the token has not timed out if( HASH[id] > QDateTime::currentDateTime() ){ ok = id.section("::::",1,1)=="operator"; } } return ok; } int AuthorizationManager::checkAuthTimeoutSecs(QString token){ //Return the number of seconds that a token is valid for if(!HASH.contains(token)){ return 0; } //invalid token return QDateTime::currentDateTime().secsTo( HASH[token] ); } // == Token Generation functions QString AuthorizationManager::LoginUP(QHostAddress host, QString user, QString pass){ //Login w/ username & password 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/password" << user << pass << localhost; //Need to run the full username/password through PAM if(!localhost || user=="root" || user=="toor"){ ok = pam_checkPW(user,pass); }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); } } QString AuthorizationManager::LoginService(QHostAddress host, QString service){ bool localhost = ( (host== QHostAddress::LocalHost) || (host== QHostAddress::LocalHostIPv6) ); //Login a particular automated service qDebug() << "Service Login Attempt:" << service << " Success:" << localhost; if(!localhost){ return ""; } //invalid - services must be local for access //Check that the service is valid on the system bool isok = false; if(service!="root" && service!="toor" && localhost){ QStringList groups = getUserGroups(service); isok = (groups.contains(service) && !groups.contains("wheel") && !groups.contains("operator")); } //Now generate a new token and send it back if(!isok){ //invalid login if(!localhost){ //Bump the fail count for this host bool overlimit = BumpFailCount(host.toString()); if(overlimit){ emit BlockHost(host); } return (overlimit ? "REFUSED" : ""); }else{ return ""; } }else{ return generateNewToken(false); }//services are never given operator privileges } // ========================= // PRIVATE // ========================= QString AuthorizationManager::generateNewToken(bool isOp){ QString tok; for(int i=0; i::::" QStringList keys = QStringList(IPFAIL.keys()).filter(host+"::::"); int fails = 0; if(!keys.isEmpty()){ //Take the existing key/value and put a new one in (this limits the filter to 1 value maximum) QDateTime last = IPFAIL.take(keys[0]); if(last.addSecs(FAILOVERMINS*60) > QDateTime::currentDateTime() ){ fails = keys[0].section("::::",1,1).toInt(); } } fails++; IPFAIL.insert(host+"::::"+QString::number(fails), QDateTime::currentDateTime() ); return (fails>=AUTHFAILLIMIT); } void AuthorizationManager::ClearHostFail(QString host){ QStringList keys = QStringList(IPFAIL.keys()).filter(host+"::::"); for(int i=0; i