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:
parent
27af822dd2
commit
4f78175a12
|
@ -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]
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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: */
|
||||||
|
|
|
@ -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)
|
||||||
|
|
Loading…
Reference in New Issue