Skip to content

Commit

Permalink
reTurn: read user/password data from a file specified by UserDatabase…
Browse files Browse the repository at this point in the history
…File (contributed by Catalin Usurelu)

git-svn-id: https://svn.resiprocate.org/rep/resiprocate/main@10208 ddefafc4-47db-0310-ae44-fa13212b10f2
  • Loading branch information
dpocock committed May 12, 2013
1 parent edd144f commit ba61ec1
Show file tree
Hide file tree
Showing 4 changed files with 219 additions and 13 deletions.
184 changes: 177 additions & 7 deletions reTurn/ReTurnConfig.cxx
@@ -1,6 +1,13 @@
#include <fstream>
#include <iostream>
#include <string>
#include <sstream>

#include "ReTurnConfig.hxx"

#include "ReTurnSubsystem.hxx"
#include "rutil/ParseBuffer.hxx"
#include <rutil/Logger.hxx>

#define RESIPROCATE_SUBSYSTEM ReTurnSubsystem::RETURN

Expand Down Expand Up @@ -84,22 +91,163 @@ void ReTurnConfig::parseConfig(int argc, char** argv, const resip::Data& default
}
#endif

Data user(getConfigData("LongTermAuthUsername", ""));
Data password(getConfigData("LongTermAuthPassword", ""));
// TODO: For ShortTermCredentials use mAuthenticationCredentials[username] = password;


// LongTermCredentials

Data usersDatabase(getConfigData("UserDatabaseFile", ""));

if(user.size() == 0 || password.size() == 0)
if(usersDatabase.size() == 0)
{
throw ConfigParse::Exception("Missing or invalid credentials (LongTermAuthUsername/LongTermAuthPassword)", __FILE__, __LINE__);
throw ConfigParse::Exception("Missing user database option! Expected \"UserDatabaseFile = file location\".", __FILE__, __LINE__);
}

mAuthenticationCredentials[user] = password;
authParse(usersDatabase);
calcUserAuthData();
}

ReTurnConfig::~ReTurnConfig()
{
}

void
ReTurnConfig::addUser(const resip::Data& username, const resip::Data& password, const resip::Data& realm)
{
mRealmUsersAuthenticaionCredentials[std::make_pair(username, realm)] = password;
RealmUsers& realmUsers(mUsers[realm]);

UserAuthData newUser(UserAuthData::createFromPassword(username, realm, password));
realmUsers.insert(pair<resip::Data,UserAuthData>(username, newUser));
}

void
ReTurnConfig::authParse(const resip::Data& accountDatabaseFilename)
{
std::ifstream accountDatabaseFile(accountDatabaseFilename.c_str());
std::string sline;
int lineNbr = 0;
if(!accountDatabaseFile)
{
throw ReTurnConfig::Exception("Error opening/reading user database file!", __FILE__, __LINE__);
}

while(std::getline(accountDatabaseFile, sline))
{
AccountState accountState;
Data username;
Data password;
Data realm;
Data state;
Data line(sline);
ParseBuffer pb(line);

lineNbr++;

// Jump over empty lines.
if(line.size() == 0)
{
continue;
}

pb.skipWhitespace();
const char * anchor = pb.position();

pb.skipToOneOf(" :");

if (pb.eof())
{
ErrLog(<< "Missing or invalid credentials at line " << lineNbr);
continue;
}

pb.data(username, anchor);

pb.skipToChar(':');
if (!pb.eof())
{
pb.skipChar(':');
pb.skipWhitespace();
}

anchor = pb.position();
pb.skipToOneOf(" :");

if (pb.eof())
{
ErrLog(<< "Missing or invalid credentials at line " << lineNbr);
continue;
}

pb.data(password, anchor);

pb.skipToChar(':');
if (!pb.eof())
{
pb.skipChar(':');
pb.skipWhitespace();
}

anchor = pb.position();
pb.skipToOneOf(" :");

if (pb.eof())
{
ErrLog(<< "Missing or invalid credentials at line " << lineNbr);
continue;
}

pb.data(realm, anchor);

pb.skipToChar(':');
if (!pb.eof())
{
pb.skipChar(':');
pb.skipWhitespace();
}

anchor = pb.position();
pb.skipToOneOf(" \t\n");

pb.data(state, anchor);
state.lowercase();

if (state.size() != 0)
{

if(state == "authorized")
{
accountState = AUTHORIZED;
}
else if(state == "restricted")
{
accountState = RESTRICTED;
}
else if(state == "refused")
{
accountState = REFUSED;
}
else
{
ErrLog(<< "Invalid state value at line " << lineNbr << ", state= " << state);
continue;
}
}
else
{
ErrLog(<< "Missing state value at line " << lineNbr);
continue;
}

if(accountState != REFUSED) {
addUser(username, password, realm);
}
}

accountDatabaseFile.close();
}


void
ReTurnConfig::calcUserAuthData()
{
Expand All @@ -125,14 +273,22 @@ ReTurnConfig::printHelpText(int argc, char **argv)
std::cerr << " " << removePath(argv[0]) << " reTurnServer.config --LogLevel=INFO" << std::endl;
}

bool
// ShortTermAuthentication
bool
ReTurnConfig::isUserNameValid(const resip::Data& username) const
{
std::map<resip::Data,resip::Data>::const_iterator it = mAuthenticationCredentials.find(username);
return it != mAuthenticationCredentials.end();
}

const Data&
// LongTermAuthentication
bool
ReTurnConfig::isUserNameValid(const resip::Data& username, const resip::Data& realm) const
{
return getUser(username, realm) != NULL;
}

const Data&
ReTurnConfig::getPasswordForUsername(const Data& username) const
{
std::map<resip::Data,resip::Data>::const_iterator it = mAuthenticationCredentials.find(username);
Expand All @@ -146,6 +302,20 @@ ReTurnConfig::getPasswordForUsername(const Data& username) const
}
}

const Data&
ReTurnConfig::getPasswordForUsername(const Data& username, const resip::Data& realm) const
{
std::map<RealmUserPair, resip::Data>::const_iterator it = mRealmUsersAuthenticaionCredentials.find(std::make_pair(username, realm));
if(it != mRealmUsersAuthenticaionCredentials.end())
{
return it->second;
}
else
{
return Data::Empty;
}
}

const UserAuthData*
ReTurnConfig::getUser(const resip::Data& userName, const resip::Data& realm) const
{
Expand Down
25 changes: 25 additions & 0 deletions reTurn/ReTurnConfig.hxx
Expand Up @@ -6,16 +6,29 @@
#include <rutil/ConfigParse.hxx>
#include <rutil/Data.hxx>
#include <rutil/Log.hxx>
#include <rutil/BaseException.hxx>

#include <reTurn/UserAuthData.hxx>

namespace reTurn {

typedef std::map<resip::Data,reTurn::UserAuthData> RealmUsers;
typedef std::pair<resip::Data, resip::Data> RealmUserPair;

class ReTurnConfig : public resip::ConfigParse
{
public:
class Exception : public resip::BaseException
{
public:
Exception(const resip::Data& msg,
const resip::Data& file,
const int line)
: resip::BaseException(msg, file, line) {}
protected:
virtual const char* name() const { return "ReTurnConfig::Exception"; }
};

ReTurnConfig();
virtual ~ReTurnConfig();

Expand All @@ -30,6 +43,13 @@ public:
ShortTermPassword = 1,
LongTermPassword = 2
} AuthenticationMode;

typedef enum
{
AUTHORIZED,
RESTRICTED,
REFUSED
} AccountState;

unsigned short mTurnPort;
unsigned short mTlsTurnPort;
Expand All @@ -41,6 +61,7 @@ public:
resip::Data mAuthenticationRealm;
std::map<resip::Data,resip::Data> mAuthenticationCredentials;
std::map<resip::Data,RealmUsers> mUsers;
std::map<RealmUserPair, resip::Data> mRealmUsersAuthenticaionCredentials;
unsigned long mNonceLifetime;

unsigned short mAllocationPortRangeMin;
Expand All @@ -63,8 +84,12 @@ public:
resip::Data mRunAsGroup;

bool isUserNameValid(const resip::Data& username) const;
bool isUserNameValid(const resip::Data& username, const resip::Data& realm) const;
const resip::Data& getPasswordForUsername(const resip::Data& username) const;
const resip::Data& getPasswordForUsername(const resip::Data& username, const resip::Data& realm) const;
const UserAuthData* getUser(const resip::Data& userName, const resip::Data& realm) const;
void addUser(const resip::Data& username, const resip::Data& password, const resip::Data& realm);
void authParse(const resip::Data& accountDatabaseFilename);

protected:
void calcUserAuthData();
Expand Down
13 changes: 10 additions & 3 deletions reTurn/RequestHandler.cxx
Expand Up @@ -316,7 +316,7 @@ RequestHandler::handleAuthentication(StunMessage& request, StunMessage& response

// !slg! need to determine whether the USERNAME contains a known entity, and in the case of a long-term
// credential, known within the realm of the REALM attribute of the request
if (getConfig().mAuthenticationMode == ReTurnConfig::LongTermPassword && !getConfig().isUserNameValid(*request.mUsername))
if (getConfig().mAuthenticationMode == ReTurnConfig::LongTermPassword && !getConfig().isUserNameValid(*request.mUsername, *request.mRealm))
{
WarningLog(<< "Invalid username: " << *request.mUsername << ". Sending 401. Sender=" << request.mRemoteTuple);
buildErrorResponse(response, 401, "Unauthorized", getConfig().mAuthenticationMode == ReTurnConfig::LongTermPassword ? getConfig().mAuthenticationRealm.c_str() : 0);
Expand All @@ -332,9 +332,16 @@ RequestHandler::handleAuthentication(StunMessage& request, StunMessage& response

if(getConfig().mAuthenticationMode != ReTurnConfig::NoAuthentication)
{
request.calculateHmacKey(hmacKey, getConfig().getPasswordForUsername(*request.mUsername));
if(request.mHasRealm) // Longterm authenicationmode
{
request.calculateHmacKey(hmacKey, getConfig().getPasswordForUsername(*request.mUsername, *request.mRealm));
}
else
{
request.calculateHmacKey(hmacKey, getConfig().getPasswordForUsername(*request.mUsername));
}
}

if(!request.checkMessageIntegrity(hmacKey))
{
WarningLog(<< "MessageIntegrity is bad. Sending 401. Sender=" << request.mRemoteTuple);
Expand Down
10 changes: 7 additions & 3 deletions reTurn/reTurnServer.config
Expand Up @@ -95,9 +95,6 @@ AuthenticationRealm = reTurn

# It is highly recommended that these values are NOT left at their
# default setting
LongTermAuthUsername = test
LongTermAuthPassword = 1234


########################################################
# TURN Allocation settings
Expand Down Expand Up @@ -146,3 +143,10 @@ TlsTempDhFilename = dh512.pem
# TLS server private key certificate password required to read
# from PEM file. Leave blank if key is not encrypted.
TlsPrivateKeyPassword =

# File containing user authentication data.
# The format of each line is:
# login:password:realm:state
# The state field can be: "authorized", "refused"or "restricted" ("restricted" means
# the account has bandwidth restrictions).
UserDatabaseFile = users.txt

0 comments on commit ba61ec1

Please sign in to comment.