// =============================== // 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 //Stuff for OpenSSL to work #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 40 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){ if(token.isEmpty() || token.length() < TOKENLENGTH){ return; } //not a valid token //clear an authorization token QString id = hashID(token); //qDebug() << "Clear Auth:" << id; 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; } //SSL Certificate register/revoke/list bool AuthorizationManager::RegisterCertificate(QString token, QString pubkey, QString nickname, QString email){ if(!checkAuth(token)){ return false; } QString user = hashID(token).section("::::",2,2); //get the user name from the currently-valid token //NOTE: The public key should be a base64 encoded string CONFIG->setValue("RegisteredCerts/"+user+"/"+pubkey, "Nickname: "+nickname+"\nEmail: "+email+"\nDate Registered: "+QDateTime::currentDateTime().toString(Qt::ISODate) ); 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); //Check that the given cert exists first if( !CONFIG->contains("RegisteredCerts/"+cuser+"/"+key) ){ return false; } CONFIG->remove("RegisteredCerts/"+cuser+"/"+key); return true; } void AuthorizationManager::ListCertificates(QString token, QJsonObject *out){ QStringList keys; //Format: "RegisteredCerts//" //Read all user's certs keys = CONFIG->allKeys().filter("RegisteredCerts/"); //qDebug() << "Found SSL Keys to List:" << keys; keys.sort(); //Now put the known keys into the output structure arranged by username/key QJsonObject user; QString username; for(int i=0; iinsert(username, user); user = QJsonObject(); } //save the current info to the output username = keys[i].section("/",1,1); //save the new username for later } QString info = CONFIG->value(keys[i]).toString(); QString key = keys[i].section("/",2,-1);//just in case the key has additional "/" in it user.insert(key,info); } if(!user.isEmpty() && !username.isEmpty()){ out->insert(username, user); } } //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 return QDateTime::currentDateTime().secsTo( HASH[token] ); } // == Token Generation functions //Stage 1 SSL Login Check: Generation of random string for this user QString AuthorizationManager::GenerateEncCheckString(){ QString key; for(int i=0; i HASH[pubkeys[i]]){ //Note: normally only 1 request per user at a time, but it is possible for a couple different clients to try // and authenticate as the same user (but different keys) at nearly the same time - so keep a short valid-string time frame (<30 seconds) // to mitigate this possibility (need to prevent the second user-auth request from invalidating the first before the first auth handshake is finished) HASH.remove(pubkeys[i]); //initstring expired - go ahead and remove it to reduce calc time later } } //Now re-use the "pubkeys" variable for the public SSL keys QString user; pubkeys = CONFIG->allKeys().filter("RegisteredCerts/"); //Format: "RegisteredCerts//" //qDebug() << " - Check pubkeys";// << pubkeys; 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(CONFIG->value("blacklist_settings/block_minutes",60).toInt()*60) > QDateTime::currentDateTime() ){ fails = keys[0].section("::::",1,1).toInt(); } } fails++; IPFAIL.insert(host+"::::"+QString::number(fails), QDateTime::currentDateTime() ); return (fails>=CONFIG->value("blacklist_settings/fails_to_block",2).toInt()); } void AuthorizationManager::ClearHostFail(QString host){ QStringList keys = QStringList(IPFAIL.keys()).filter(host+"::::"); for(int i=0; i