Authentication and RADIUS/PortaOne related changes.

git-svn-id: http://voip.null.ro/svn/yate@740 acf43c95-373e-0410-b603-e72c3f656dc1
This commit is contained in:
paulc 2006-04-04 19:06:02 +00:00
parent 27af822dd2
commit 4f78175a12
5 changed files with 120 additions and 40 deletions

View File

@ -20,10 +20,10 @@
;short_number=false ;short_number=false
; auth_priority: integer: Priority of the user.auth handler ; auth_priority: integer: Priority of the user.auth handler
;auth_priority=50 ;auth_priority=70
; acct_priority: integer: Priority of the call.cdr handler ; acct_priority: integer: Priority of the call.cdr handler
;acct_priority=50 ;acct_priority=70
[portabill] [portabill]

View File

@ -503,6 +503,11 @@ bool SIPEngine::checkUser(const String& username, const String& realm, const Str
return false; return false;
} }
bool SIPEngine::checkAuth(bool noUser, const SIPMessage* message, GenObject* userData)
{
return message && noUser && checkUser("","","",message->method,message->uri,"",message,userData);
}
// response = md5(md5(username:realm:password):nonce:md5(method:uri)) // response = md5(md5(username:realm:password):nonce:md5(method:uri))
void SIPEngine::buildAuth(const String& username, const String& realm, const String& passwd, void SIPEngine::buildAuth(const String& username, const String& realm, const String& passwd,
const String& nonce, const String& method, const String& uri, String& response) const String& nonce, const String& method, const String& uri, String& response)
@ -532,6 +537,7 @@ int SIPEngine::authUser(const SIPMessage* message, String& user, bool proxy, Gen
{ {
if (!message) if (!message)
return -1; return -1;
bool noUser = true;
const char* hdr = proxy ? "Proxy-Authorization" : "Authorization"; const char* hdr = proxy ? "Proxy-Authorization" : "Authorization";
const ObjList* l = &message->header; const ObjList* l = &message->header;
for (; l; l = l->next()) { for (; l; l = l->next()) {
@ -555,6 +561,7 @@ int SIPEngine::authUser(const SIPMessage* message, String& user, bool proxy, Gen
long age = nonceAge(nonce); long age = nonceAge(nonce);
if (age < 0) if (age < 0)
continue; continue;
noUser = false;
XDebug(this,DebugAll,"authUser nonce age is %ld",age); XDebug(this,DebugAll,"authUser nonce age is %ld",age);
String res(t->getParam("response")); String res(t->getParam("response"));
delQuotes(res); delQuotes(res);
@ -575,6 +582,9 @@ int SIPEngine::authUser(const SIPMessage* message, String& user, bool proxy, Gen
return age; return age;
} }
} }
// we got no auth headers - try to authenticate by other means
if (checkAuth(noUser,message,userData))
return 0;
return -1; return -1;
} }

View File

@ -1022,6 +1022,16 @@ public:
const String& method, const String& uri, const String& response, const String& method, const String& uri, const String& response,
const SIPMessage* message, GenObject* userData); const SIPMessage* message, GenObject* userData);
/**
* Authenticate a message by other means than user credentials. By default
* it calls @ref checkUser with empty user credential fields
* @param noUser No plausible user credentials were detected so far
* @param message Message that is to be authenticated
* @param userData Pointer to an optional object passed from @ref authUser
* @return True if message is authenticated, false if verification failed
*/
virtual bool checkAuth(bool noUser, const SIPMessage* message, GenObject* userData);
/** /**
* Detect the proper credentials for any user in the engine * Detect the proper credentials for any user in the engine
* @param message Pointer to the message to check * @param message Pointer to the message to check

View File

@ -348,7 +348,7 @@ public:
bool addAttribute(const char* attrib, int val); bool addAttribute(const char* attrib, int val);
bool addAttribute(const char* attrib, unsigned char subType, const char* val, bool emptyOk = false); bool addAttribute(const char* attrib, unsigned char subType, const char* val, bool emptyOk = false);
void addAttributes(NamedList& params, NamedList* list); void addAttributes(NamedList& params, NamedList* list);
bool prepareAttributes(NamedList& params, bool forAcct = false); bool prepareAttributes(NamedList& params, bool forAcct = true, String* user = 0);
bool returnAttributes(NamedList& params, const ObjList* attributes); bool returnAttributes(NamedList& params, const ObjList* attributes);
static bool fillRandom(DataBlock& data, int len); static bool fillRandom(DataBlock& data, int len);
@ -441,6 +441,18 @@ static void portaBillingRoute(NamedList& params, const ObjList* attributes)
route << rsep << tmp; route << rsep << tmp;
} }
} }
else if (tmp.startSkip("CLI:",false)) {
if (tmp) {
Debug(&__plugin,DebugCall,"PortaBilling setting caller '%s'",tmp.c_str());
params.setParam("caller",tmp);
}
}
else if (tmp.startSkip("CompleteNumber:",false)) {
if (tmp) {
Debug(&__plugin,DebugCall,"PortaBilling setting called '%s'",tmp.c_str());
params.setParam("called",tmp);
}
}
} }
if (route) { if (route) {
Debug(&__plugin,DebugCall,"PortaBilling returned route '%s'",route.c_str()); Debug(&__plugin,DebugCall,"PortaBilling returned route '%s'",route.c_str());
@ -1171,7 +1183,7 @@ void RadiusClient::addAttributes(NamedList& params, NamedList* list)
} }
// Find matching NAS section and populate attributes accordingly // Find matching NAS section and populate attributes accordingly
bool RadiusClient::prepareAttributes(NamedList& params, bool forAcct) bool RadiusClient::prepareAttributes(NamedList& params, bool forAcct, String* user)
{ {
const char* caller = params.getValue("caller"); const char* caller = params.getValue("caller");
const char* called = 0; const char* called = 0;
@ -1187,7 +1199,17 @@ bool RadiusClient::prepareAttributes(NamedList& params, bool forAcct)
if (!called) if (!called)
called = params.getValue("called"); called = params.getValue("called");
} }
const char* username = params.getValue("username",caller); const char* username = params.getValue("username");
if (!username)
username = params.getValue("authname");
if (!username) {
if (forAcct)
username = caller;
// we were unable to build an username
// don't even send such a request to PortaOne
if (s_pb_enabled && !username)
return false;
}
Lock lock(s_cfgMutex); Lock lock(s_cfgMutex);
NamedList* nasSect = 0; NamedList* nasSect = 0;
String nasName; String nasName;
@ -1262,6 +1284,8 @@ bool RadiusClient::prepareAttributes(NamedList& params, bool forAcct)
addAttribute("Called-Station-Id",called); addAttribute("Called-Station-Id",called);
addAttributes(params,nasSect); addAttributes(params,nasSect);
addAttributes(params,servSect); addAttributes(params,servSect);
if (user)
*user = username;
return true; return true;
} }
@ -1297,37 +1321,54 @@ bool AuthHandler::received(Message& msg)
if (proto.null()) if (proto.null())
return false; return false;
RadiusClient radclient; RadiusClient radclient;
if (!radclient.prepareAttributes(msg,false)) // preserve the actually authenticated username in case we succeed
String user;
if (!radclient.prepareAttributes(msg,false,&user))
return false; return false;
// TODO: process plaintext password
if ((proto == "digest") || (proto == "sip")) { if ((proto == "digest") || (proto == "sip")) {
// mandatory auth parameters const char* resp = msg.getValue("response");
if (!radclient.addAttribute("Digest-Response",msg.getValue("response"))) const char* nonce = msg.getValue("nonce");
return false; const char* method = msg.getValue("method");
if (!( const char* uri = msg.getValue("uri");
radclient.addAttribute("Digest-Attributes",Digest_Nonce,msg.getValue("nonce")) && const char* user = msg.getValue("username");
radclient.addAttribute("Digest-Attributes",Digest_Method,msg.getValue("method")) && if (resp && nonce && method && uri && user) {
radclient.addAttribute("Digest-Attributes",Digest_URI,msg.getValue("uri")) && // mandatory auth parameters
radclient.addAttribute("Digest-Attributes",Digest_UserName,msg.getValue("username")) if (!(
)) radclient.addAttribute("Digest-Response",resp) &&
return false; radclient.addAttribute("Digest-Attributes",Digest_Nonce,nonce) &&
// optional auth parameters radclient.addAttribute("Digest-Attributes",Digest_Method,method) &&
radclient.addAttribute("Digest-Attributes",Digest_Realm,msg.getValue("realm")); radclient.addAttribute("Digest-Attributes",Digest_URI,uri) &&
radclient.addAttribute("Digest-Attributes",Digest_Algo,msg.getValue("algorithm","MD5")); radclient.addAttribute("Digest-Attributes",Digest_UserName,user)
radclient.addAttribute("Digest-Attributes",Digest_QOP,msg.getValue("qop")); ))
ObjList result; return false;
if (radclient.doAuthenticate(&result) != AuthSuccess) // optional auth parameters
return false; radclient.addAttribute("Digest-Attributes",Digest_Realm,msg.getValue("realm"));
radclient.returnAttributes(msg,&result); radclient.addAttribute("Digest-Attributes",Digest_Algo,msg.getValue("algorithm","MD5"));
if (s_pb_enabled) radclient.addAttribute("Digest-Attributes",Digest_QOP,msg.getValue("qop"));
portaBillingRoute(msg,&result); }
// signal we don't return a password
msg.retValue().clear();
return true;
} }
else {
Debug(&__plugin,DebugMild,"Protocol '%s' not supported!",proto.c_str()); String address = msg.getValue("address");
// suppress any port number - IMHO this is stupid
int sep = address.find(':');
if (sep >= 0)
address = address.substr(0,sep);
radclient.addAttribute("h323-remote-address",address);
ObjList result;
if (radclient.doAuthenticate(&result) != AuthSuccess)
return false; return false;
} // copy back the username we actually authenticated
if (user)
msg.setParam("username",user);
// and pick whatever other parameters we want to return
radclient.returnAttributes(msg,&result);
if (s_pb_enabled)
portaBillingRoute(msg,&result);
// signal we don't return a password
msg.retValue().clear();
return true;
} }
@ -1387,7 +1428,7 @@ bool AcctHandler::received(Message& msg)
return false; return false;
RadiusClient radclient; RadiusClient radclient;
if (!radclient.prepareAttributes(msg,true)) if (!radclient.prepareAttributes(msg))
return false; return false;
// create a Cisco-compatible conference ID // create a Cisco-compatible conference ID
@ -1510,8 +1551,8 @@ void RadiusModule::initialize()
m_init = true; m_init = true;
setup(); setup();
Engine::install(new AuthHandler(s_cfg.getIntValue("general","auth_priority",50))); Engine::install(new AuthHandler(s_cfg.getIntValue("general","auth_priority",70)));
Engine::install(new AcctHandler(s_cfg.getIntValue("general","acct_priority",50))); Engine::install(new AcctHandler(s_cfg.getIntValue("general","acct_priority",70)));
} }
/* vi: set ts=8 sw=4 sts=4 noet: */ /* vi: set ts=8 sw=4 sts=4 noet: */

View File

@ -889,14 +889,18 @@ bool YateSIPEngine::checkUser(const String& username, const String& realm, const
const String& method, const String& uri, const String& response, const String& method, const String& uri, const String& response,
const SIPMessage* message, GenObject* userData) const SIPMessage* message, GenObject* userData)
{ {
NamedList* params = YOBJECT(NamedList,userData);
Message m("user.auth"); Message m("user.auth");
m.addParam("protocol","sip"); m.addParam("protocol","sip");
m.addParam("username",username); if (username) {
m.addParam("realm",realm); m.addParam("username",username);
m.addParam("nonce",nonce); m.addParam("realm",realm);
m.addParam("nonce",nonce);
m.addParam("response",response);
}
m.addParam("method",method); m.addParam("method",method);
m.addParam("uri",uri); m.addParam("uri",uri);
m.addParam("response",response);
if (message) { if (message) {
m.addParam("ip_host",message->getParty()->getPartyAddr()); m.addParam("ip_host",message->getParty()->getPartyAddr());
m.addParam("ip_port",String(message->getParty()->getPartyPort())); m.addParam("ip_port",String(message->getParty()->getPartyPort()));
@ -907,7 +911,6 @@ bool YateSIPEngine::checkUser(const String& username, const String& realm, const
} }
} }
NamedList* params = YOBJECT(NamedList,userData);
if (params) { if (params) {
const char* str = params->getValue("caller"); const char* str = params->getValue("caller");
if (str) if (str)
@ -923,6 +926,22 @@ bool YateSIPEngine::checkUser(const String& username, const String& realm, const
// empty password returned means authentication succeeded // empty password returned means authentication succeeded
if (m.retValue().null()) if (m.retValue().null())
return copyAuthParams(params,m); return copyAuthParams(params,m);
// check for refusals
if (m.retValue() == "-") {
if (params) {
const char* err = m.getValue("error");
if (err)
params->setParam("error",err);
err = m.getValue("reason");
if (err)
params->setParam("reason",err);
}
return false;
}
// password works only with username
if (!username)
return false;
String res; String res;
buildAuth(username,realm,m.retValue(),nonce,method,uri,res); buildAuth(username,realm,m.retValue(),nonce,method,uri,res);
if (res == response) if (res == response)