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
|
||||
|
||||
; 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=50
|
||||
;acct_priority=70
|
||||
|
||||
|
||||
[portabill]
|
||||
|
|
|
@ -503,6 +503,11 @@ bool SIPEngine::checkUser(const String& username, const String& realm, const Str
|
|||
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))
|
||||
void SIPEngine::buildAuth(const String& username, const String& realm, const String& passwd,
|
||||
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)
|
||||
return -1;
|
||||
bool noUser = true;
|
||||
const char* hdr = proxy ? "Proxy-Authorization" : "Authorization";
|
||||
const ObjList* l = &message->header;
|
||||
for (; l; l = l->next()) {
|
||||
|
@ -555,6 +561,7 @@ int SIPEngine::authUser(const SIPMessage* message, String& user, bool proxy, Gen
|
|||
long age = nonceAge(nonce);
|
||||
if (age < 0)
|
||||
continue;
|
||||
noUser = false;
|
||||
XDebug(this,DebugAll,"authUser nonce age is %ld",age);
|
||||
String res(t->getParam("response"));
|
||||
delQuotes(res);
|
||||
|
@ -575,6 +582,9 @@ int SIPEngine::authUser(const SIPMessage* message, String& user, bool proxy, Gen
|
|||
return age;
|
||||
}
|
||||
}
|
||||
// we got no auth headers - try to authenticate by other means
|
||||
if (checkAuth(noUser,message,userData))
|
||||
return 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
|
|
@ -1022,6 +1022,16 @@ public:
|
|||
const String& method, const String& uri, const String& response,
|
||||
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
|
||||
* @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, unsigned char subType, const char* val, bool emptyOk = false);
|
||||
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);
|
||||
static bool fillRandom(DataBlock& data, int len);
|
||||
|
||||
|
@ -441,6 +441,18 @@ static void portaBillingRoute(NamedList& params, const ObjList* attributes)
|
|||
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) {
|
||||
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
|
||||
bool RadiusClient::prepareAttributes(NamedList& params, bool forAcct)
|
||||
bool RadiusClient::prepareAttributes(NamedList& params, bool forAcct, String* user)
|
||||
{
|
||||
const char* caller = params.getValue("caller");
|
||||
const char* called = 0;
|
||||
|
@ -1187,7 +1199,17 @@ bool RadiusClient::prepareAttributes(NamedList& params, bool forAcct)
|
|||
if (!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);
|
||||
NamedList* nasSect = 0;
|
||||
String nasName;
|
||||
|
@ -1262,6 +1284,8 @@ bool RadiusClient::prepareAttributes(NamedList& params, bool forAcct)
|
|||
addAttribute("Called-Station-Id",called);
|
||||
addAttributes(params,nasSect);
|
||||
addAttributes(params,servSect);
|
||||
if (user)
|
||||
*user = username;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1297,37 +1321,54 @@ bool AuthHandler::received(Message& msg)
|
|||
if (proto.null())
|
||||
return false;
|
||||
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;
|
||||
// TODO: process plaintext password
|
||||
if ((proto == "digest") || (proto == "sip")) {
|
||||
// mandatory auth parameters
|
||||
if (!radclient.addAttribute("Digest-Response",msg.getValue("response")))
|
||||
return false;
|
||||
if (!(
|
||||
radclient.addAttribute("Digest-Attributes",Digest_Nonce,msg.getValue("nonce")) &&
|
||||
radclient.addAttribute("Digest-Attributes",Digest_Method,msg.getValue("method")) &&
|
||||
radclient.addAttribute("Digest-Attributes",Digest_URI,msg.getValue("uri")) &&
|
||||
radclient.addAttribute("Digest-Attributes",Digest_UserName,msg.getValue("username"))
|
||||
))
|
||||
return false;
|
||||
// optional auth parameters
|
||||
radclient.addAttribute("Digest-Attributes",Digest_Realm,msg.getValue("realm"));
|
||||
radclient.addAttribute("Digest-Attributes",Digest_Algo,msg.getValue("algorithm","MD5"));
|
||||
radclient.addAttribute("Digest-Attributes",Digest_QOP,msg.getValue("qop"));
|
||||
ObjList result;
|
||||
if (radclient.doAuthenticate(&result) != AuthSuccess)
|
||||
return false;
|
||||
radclient.returnAttributes(msg,&result);
|
||||
if (s_pb_enabled)
|
||||
portaBillingRoute(msg,&result);
|
||||
// signal we don't return a password
|
||||
msg.retValue().clear();
|
||||
return true;
|
||||
const char* resp = msg.getValue("response");
|
||||
const char* nonce = msg.getValue("nonce");
|
||||
const char* method = msg.getValue("method");
|
||||
const char* uri = msg.getValue("uri");
|
||||
const char* user = msg.getValue("username");
|
||||
if (resp && nonce && method && uri && user) {
|
||||
// mandatory auth parameters
|
||||
if (!(
|
||||
radclient.addAttribute("Digest-Response",resp) &&
|
||||
radclient.addAttribute("Digest-Attributes",Digest_Nonce,nonce) &&
|
||||
radclient.addAttribute("Digest-Attributes",Digest_Method,method) &&
|
||||
radclient.addAttribute("Digest-Attributes",Digest_URI,uri) &&
|
||||
radclient.addAttribute("Digest-Attributes",Digest_UserName,user)
|
||||
))
|
||||
return false;
|
||||
// optional auth parameters
|
||||
radclient.addAttribute("Digest-Attributes",Digest_Realm,msg.getValue("realm"));
|
||||
radclient.addAttribute("Digest-Attributes",Digest_Algo,msg.getValue("algorithm","MD5"));
|
||||
radclient.addAttribute("Digest-Attributes",Digest_QOP,msg.getValue("qop"));
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
// 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;
|
||||
|
||||
RadiusClient radclient;
|
||||
if (!radclient.prepareAttributes(msg,true))
|
||||
if (!radclient.prepareAttributes(msg))
|
||||
return false;
|
||||
|
||||
// create a Cisco-compatible conference ID
|
||||
|
@ -1510,8 +1551,8 @@ void RadiusModule::initialize()
|
|||
m_init = true;
|
||||
setup();
|
||||
|
||||
Engine::install(new AuthHandler(s_cfg.getIntValue("general","auth_priority",50)));
|
||||
Engine::install(new AcctHandler(s_cfg.getIntValue("general","acct_priority",50)));
|
||||
Engine::install(new AuthHandler(s_cfg.getIntValue("general","auth_priority",70)));
|
||||
Engine::install(new AcctHandler(s_cfg.getIntValue("general","acct_priority",70)));
|
||||
}
|
||||
|
||||
/* 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 SIPMessage* message, GenObject* userData)
|
||||
{
|
||||
NamedList* params = YOBJECT(NamedList,userData);
|
||||
|
||||
Message m("user.auth");
|
||||
m.addParam("protocol","sip");
|
||||
m.addParam("username",username);
|
||||
m.addParam("realm",realm);
|
||||
m.addParam("nonce",nonce);
|
||||
if (username) {
|
||||
m.addParam("username",username);
|
||||
m.addParam("realm",realm);
|
||||
m.addParam("nonce",nonce);
|
||||
m.addParam("response",response);
|
||||
}
|
||||
m.addParam("method",method);
|
||||
m.addParam("uri",uri);
|
||||
m.addParam("response",response);
|
||||
if (message) {
|
||||
m.addParam("ip_host",message->getParty()->getPartyAddr());
|
||||
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) {
|
||||
const char* str = params->getValue("caller");
|
||||
if (str)
|
||||
|
@ -923,6 +926,22 @@ bool YateSIPEngine::checkUser(const String& username, const String& realm, const
|
|||
// empty password returned means authentication succeeded
|
||||
if (m.retValue().null())
|
||||
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;
|
||||
buildAuth(username,realm,m.retValue(),nonce,method,uri,res);
|
||||
if (res == response)
|
||||
|
|
Loading…
Reference in New Issue