Added ability to authenticate even observer only (non-admin) connections.

Added initial timeout for non-authenticated connections, default 30s.


git-svn-id: http://voip.null.ro/svn/yate@2937 acf43c95-373e-0410-b603-e72c3f656dc1
This commit is contained in:
paulc 2009-11-12 15:00:24 +00:00
parent 1e71d664fa
commit 4bdb65c58c
2 changed files with 169 additions and 107 deletions

View File

@ -10,9 +10,17 @@
; header: string: Header string to display on connect
;header=YATE ${version}-${release} (http://YATE.null.ro) ready.
; password: string: Password required to authenticate, default empty!
; password: string: Password required to authenticate as admin, default empty!
;password=
; userpass: string: Password to authenticate as observer user, default empty!
;userpass=
; timeout: int: Timeout until authentication succeeds in msec
; Defaults to waiting 30s until closing an unauthenticated connection
; Set to zero to disable else enforced minimum value is 5000 ms (5s)
;timeout=30000
; telnet: bool: Initiate TELNET negotiation on connect
;telnet=yes

View File

@ -89,15 +89,20 @@ static const char* s_rnow[] =
static const CommandInfo s_cmdInfo[] =
{
// Unauthenticated commands
{ "quit", 0, 0, "Disconnect this control session from Yate" },
{ "echo", "[on|off]", s_bools, "Show or turn remote echo on or off" },
{ "help", "[command]", 0, "Provide help on all or given command" },
{ "auth", "password", 0, "Authenticate so you can access privileged commands" },
// User commands
{ "status", "[overview] [modulename]", s_oview, "Shows status of all or selected modules or channels" },
{ "uptime", 0, 0, "Show information on how long Yate has run" },
{ "echo", "[on|off]", s_bools, "Show or turn remote echo on or off" },
{ "machine", "[on|off]", s_bools, "Show or turn machine output mode on or off" },
{ "output", "[on|off]", s_bools, "Show or turn local output on or off" },
{ "color", "[on|off]", s_bools, "Show status or turn local colorization on or off" },
{ "auth", "password", 0, "Authenticate so you can access priviledged commands" },
// Admin commands
{ "debug", "[module] [level|on|off]", s_level, "Show or change debugging level globally or per module" },
#ifdef HAVE_COREDUMPER
{ "coredump", "[filename]", 0, "Dumps memory image of running Yate to a file" },
@ -131,8 +136,8 @@ Socket s_sock;
Mutex s_mutex(true,"RManager");
//we gonna create here the list with all the new connections.
static ObjList connectionlist;
static ObjList s_connList;
class RManagerThread : public Thread
{
public:
@ -144,6 +149,11 @@ private:
class Connection : public GenObject, public Thread
{
public:
enum Level {
None = 0,
User,
Admin
};
Connection(Socket* sock, const char* addr);
~Connection();
@ -161,9 +171,10 @@ public:
{ writeStr(s.safe(),s.length()); }
inline const String& address() const
{ return m_address; }
void checkTimer(u_int64_t time);
static Connection *checkCreate(Socket* sock, const char* addr = 0);
private:
bool m_auth;
Level m_auth;
bool m_debug;
bool m_output;
bool m_colorize;
@ -173,6 +184,7 @@ private:
unsigned char m_escmode;
bool m_echoing;
bool m_beeping;
u_int64_t m_timeout;
String m_buffer;
String m_address;
String m_lastcmd;
@ -198,7 +210,7 @@ public:
static void dbg_remote_func(const char *buf, int level)
{
s_mutex.lock();
ObjList *p = &connectionlist;
ObjList* p = &s_connList;
for (; p; p=p->next()) {
Connection *con = static_cast<Connection *>(p->get());
if (con)
@ -247,12 +259,12 @@ Connection *Connection::checkCreate(Socket* sock, const char* addr)
Connection::Connection(Socket* sock, const char* addr)
: Thread("RManager Connection"),
m_auth(false), m_debug(false), m_output(s_output), m_colorize(false), m_machine(false),
m_auth(None), m_debug(false), m_output(s_output), m_colorize(false), m_machine(false),
m_socket(sock), m_lastch(0), m_escmode(0), m_echoing(false), m_beeping(false),
m_address(addr)
m_timeout(0), m_address(addr)
{
s_mutex.lock();
connectionlist.append(this);
s_connList.append(this);
s_mutex.unlock();
}
@ -261,7 +273,7 @@ Connection::~Connection()
m_debug = false;
m_output = false;
s_mutex.lock();
connectionlist.remove(this,false);
s_connList.remove(this,false);
s_mutex.unlock();
Output("Closing connection to %s",m_address.c_str());
delete m_socket;
@ -284,7 +296,16 @@ void Connection::run()
Debug("RManager",DebugMild, "Failed to set tcp socket to TCP_NODELAY mode: %s", strerror(m_socket->error()));
Output("Remote connection from %s",m_address.c_str());
m_auth = !s_cfg.getValue("general","password");
if (s_cfg.getValue("general","userpass")) {
int tout = s_cfg.getIntValue("general","timeout",30000);
if (tout > 0) {
if (tout < 5000)
tout = 5000;
m_timeout = Time::now() + 1000 * tout;
}
}
else
m_auth = s_cfg.getValue("general","password") ? User : Admin;
String hdr = s_cfg.getValue("general","header","YATE ${version}-${release} (http://YATE.null.ro) ready.");
Engine::runParams().replaceParams(hdr);
if (s_cfg.getBoolValue("general","telnet",true)) {
@ -296,7 +317,7 @@ void Connection::run()
hdr.clear();
}
unsigned char buffer[128];
for (;;) {
while (m_socket && m_socket->valid()) {
Thread::check();
bool readok = false;
bool error = false;
@ -328,7 +349,7 @@ void Connection::run()
Debug("RManager",DebugWarn,"socket select error %d on %d",errno,m_socket->handle());
return;
}
}
}
}
// generates a beep - just one per processed buffer
@ -478,7 +499,7 @@ bool Connection::processChar(unsigned char c)
m_output = m_debug = false;
else if (m_output) {
m_output = false;
if ((m_debug = m_auth))
if ((m_debug = (m_auth >= Admin)))
Debugger::enableOutput(true);
}
else
@ -684,7 +705,8 @@ bool Connection::autoComplete()
Regexp r("^debug [^ ]\\+$");
if (r.matches(partLine))
completeWords(m.retValue(),s_level,partWord);
Engine::dispatch(m);
if (m_auth >= Admin)
Engine::dispatch(m);
if (m.retValue().null())
return false;
if (m.retValue().find('\t') < 0) {
@ -733,13 +755,96 @@ bool Connection::processLine(const char *line)
line = 0;
m_buffer.clear();
if (str.startSkip("quit"))
{
writeStr(m_machine ? "%%=quit\r\n" : "Goodbye!\r\n");
return true;
}
else if (str.startSkip("echo"))
{
str >> m_echoing;
str = "Remote echo: ";
str += (m_echoing ? "on\r\n" : "off\r\n");
writeStr(str);
return false;
}
else if (str.startSkip("help") || str.startSkip("?"))
{
Message m("engine.help");
if (str)
{
const CommandInfo* info = s_cmdInfo;
for (; info->name; info++) {
if (str == info->name) {
str = " ";
str << info->name;
if (info->args)
str << " " << info->args;
str << "\r\n" << info->desc << "\r\n";
writeStr(str);
return false;
}
}
m.addParam("line",str);
if ((m_auth >= Admin) && Engine::dispatch(m))
writeStr(m.retValue());
else
writeStr("No help for '"+str+"'\r\n");
}
else
{
m.retValue() = "Available commands:\r\n";
const CommandInfo* info = s_cmdInfo;
for (; info->name; info++) {
m.retValue() << " " << info->name;
if (info->args)
m.retValue() << " " << info->args;
m.retValue() << "\r\n";
}
if (m_auth >= Admin)
Engine::dispatch(m);
writeStr(m.retValue());
}
return false;
}
else if (str.startSkip("auth"))
{
if (m_auth >= Admin) {
writeStr(m_machine ? "%%=auth:success\r\n" : "You are already authenticated as admin!\r\n");
return false;
}
const char* pass = s_cfg.getValue("general","password");
if (pass && (str == pass)) {
Output("Authenticated admin connection %s",m_address.c_str());
m_auth = Admin;
m_timeout = 0;
writeStr(m_machine ? "%%=auth:success\r\n" : "Authenticated successfully as admin!\r\n");
}
else if ((pass = s_cfg.getValue("general","userpass")) && (str == pass)) {
if (m_auth < User) {
Output("Authenticated user connection %s",m_address.c_str());
m_auth = User;
m_timeout = 0;
writeStr(m_machine ? "%%=auth:success\r\n" : "Authenticated successfully as user!\r\n");
}
else
writeStr(m_machine ? "%%=auth:success\r\n" : "You are already authenticated as user!\r\n");
}
else
writeStr(m_machine ? "%%=auth:fail=badpass\r\n" : "Bad authentication password!\r\n");
return false;
}
if (m_auth < User) {
writeStr(m_machine ? "%%=*:fail=noauth\r\n" : "Not authenticated!\r\n");
return false;
}
if (str.startSkip("status"))
{
Message m("engine.status");
if (str.startSkip("overview"))
m.addParam("details",String::boolText(false));
if (str.null() || (str == "rmanager"))
m.retValue() << "name=rmanager,type=misc;conn=" << connectionlist.count() << "\r\n";
m.retValue() << "name=rmanager,type=misc;conn=" << s_connList.count() << "\r\n";
if (!str.null()) {
m.addParam("module",str);
str = ":" + str;
@ -750,11 +855,23 @@ bool Connection::processLine(const char *line)
writeStr(str);
return false;
}
else if (str.startSkip("echo"))
else if (str.startSkip("uptime"))
{
str >> m_echoing;
str = "Remote echo: ";
str += (m_echoing ? "on\r\n" : "off\r\n");
str.clear();
u_int32_t t = SysUsage::secRunTime();
if (m_machine) {
str << "%%=uptime:" << (unsigned int)t;
(str << ":").append(SysUsage::runTime(SysUsage::UserTime));
(str << ":").append(SysUsage::runTime(SysUsage::KernelTime));
}
else {
char buf[64];
::sprintf(buf,"%u:%02u:%02u (%u)",t / 3600,(t / 60) % 60,t % 60,t);
str << "Uptime: " << buf;
(str << " user: ").append(SysUsage::runTime(SysUsage::UserTime));
(str << " kernel: ").append(SysUsage::runTime(SysUsage::KernelTime));
}
str << "\r\n";
writeStr(str);
return false;
}
@ -782,85 +899,7 @@ bool Connection::processLine(const char *line)
writeStr(str);
return false;
}
else if (str.startSkip("uptime"))
{
str.clear();
u_int32_t t = SysUsage::secRunTime();
if (m_machine) {
str << "%%=uptime:" << (unsigned int)t;
(str << ":").append(SysUsage::runTime(SysUsage::UserTime));
(str << ":").append(SysUsage::runTime(SysUsage::KernelTime));
}
else {
char buf[64];
::sprintf(buf,"%u:%02u:%02u (%u)",t / 3600,(t / 60) % 60,t % 60,t);
str << "Uptime: " << buf;
(str << " user: ").append(SysUsage::runTime(SysUsage::UserTime));
(str << " kernel: ").append(SysUsage::runTime(SysUsage::KernelTime));
}
str << "\r\n";
writeStr(str);
return false;
}
else if (str.startSkip("quit"))
{
writeStr(m_machine ? "%%=quit\r\n" : "Goodbye!\r\n");
return true;
}
else if (str.startSkip("help") || str.startSkip("?"))
{
Message m("engine.help");
if (str)
{
const CommandInfo* info = s_cmdInfo;
for (; info->name; info++) {
if (str == info->name) {
str = " ";
str << info->name;
if (info->args)
str << " " << info->args;
str << "\r\n" << info->desc << "\r\n";
writeStr(str);
return false;
}
}
m.addParam("line",str);
if (Engine::dispatch(m))
writeStr(m.retValue());
else
writeStr("No help for '"+str+"'\r\n");
}
else
{
m.retValue() = "Available commands:\r\n";
const CommandInfo* info = s_cmdInfo;
for (; info->name; info++) {
m.retValue() << " " << info->name;
if (info->args)
m.retValue() << " " << info->args;
m.retValue() << "\r\n";
}
Engine::dispatch(m);
writeStr(m.retValue());
}
return false;
}
else if (str.startSkip("auth"))
{
if (m_auth) {
writeStr(m_machine ? "%%=auth:success\r\n" : "You are already authenticated!\r\n");
return false;
}
if (str == s_cfg.getValue("general","password")) {
Output("Authenticated connection %s",m_address.c_str());
m_auth = true;
writeStr(m_machine ? "%%=auth:success\r\n" : "Authenticated successfully!\r\n");
}
else
writeStr(m_machine ? "%%=auth:fail=badpass\r\n" : "Bad authentication password!\r\n");
return false;
}
if (!m_auth) {
if (m_auth < Admin) {
writeStr(m_machine ? "%%=*:fail=noauth\r\n" : "Not authenticated!\r\n");
return false;
}
@ -1113,15 +1152,30 @@ void Connection::writeStr(const char *str, int len)
}
}
void Connection::checkTimer(u_int64_t time)
{
if (!m_timeout)
return;
if (time < m_timeout)
return;
m_timeout = 0;
if (m_socket)
m_socket->terminate();
}
void RHook::dispatched(const Message& msg, bool handled)
{
u_int64_t t = 0;
if (msg == "engine.timer")
t = msg.msgTime();
s_mutex.lock();
ObjList *p = &connectionlist;
for (; p; p=p->next()) {
Connection *con = static_cast<Connection *>(p->get());
if (con)
con->writeStr(msg,handled);
ObjList* p = s_connList.skipNull();
for (; p; p = p->skipNext()) {
Connection* c = static_cast<Connection*>(p->get());
if (t)
c->checkTimer(t);
c->writeStr(msg,handled);
}
s_mutex.unlock();
};
@ -1143,7 +1197,7 @@ RManager::~RManager()
bool RManager::isBusy() const
{
return (connectionlist.count() != 0);
return (s_connList.count() != 0);
}
void RManager::initialize()
@ -1190,7 +1244,7 @@ void RManager::initialize()
s_sock.terminate();
return;
}
// don't bother to install handlers until we are listening
if (m_first) {
m_first = false;