Implemented c2s non sasl authentication. Added listener for direct c2s SSL connections. Restrict client resources with the same name while authenticating using non sasl.
git-svn-id: http://voip.null.ro/svn/yate@2943 acf43c95-373e-0410-b603-e72c3f656dc1
This commit is contained in:
parent
5e64836656
commit
aa0b6ec97d
|
@ -21,6 +21,15 @@
|
|||
; Defaults to no
|
||||
;c2s_tlsrequired=
|
||||
|
||||
; c2s_allowunsecureplainauth: boolean: Allow user plain password authentication on
|
||||
; unsecured stream
|
||||
; Defaults to no
|
||||
;c2s_allowunsecureplainauth=
|
||||
|
||||
; c2s_oldstyleauth: boolean: Enable old style (XEP 0078) user authentication
|
||||
; Defaults to yes
|
||||
;c2s_oldstyleauth=
|
||||
|
||||
; stream_readbuffer: integer: The length of the stream read buffer
|
||||
; Defaults to 8192 if missing or invalid. Minimum allowed value is 1024
|
||||
;stream_readbuffer=8192
|
||||
|
@ -115,10 +124,16 @@
|
|||
; These are the default values for some known types (only if this parameter is missing)
|
||||
; c2s 5222
|
||||
; s2s 5269
|
||||
; There is no default value for external component listeners
|
||||
; There is no default value for external component listeners or c2s SSL listeners
|
||||
;port=
|
||||
|
||||
; backlog: integer: Maximum length of the queue of pending connections
|
||||
; Set it to 0 for system maximum
|
||||
; Defaults to 5 if missing or invalid
|
||||
;backlog=5
|
||||
|
||||
; sslcontext: string: The SSL context of a c2s listener using encryption
|
||||
; The context will be used to encrypt the socket before starting a new stream
|
||||
; This parameter is ignored if type is not c2s
|
||||
;sslcontext=
|
||||
|
||||
|
|
|
@ -805,7 +805,7 @@ void JBEngine::cleanup(bool final, bool waitTerminate)
|
|||
}
|
||||
|
||||
// Accept an incoming stream connection. Build a stream
|
||||
bool JBEngine::acceptConn(Socket* sock, SocketAddr& remote, JBStream::Type t)
|
||||
bool JBEngine::acceptConn(Socket* sock, SocketAddr& remote, JBStream::Type t, bool ssl)
|
||||
{
|
||||
if (!sock)
|
||||
return false;
|
||||
|
@ -815,9 +815,13 @@ bool JBEngine::acceptConn(Socket* sock, SocketAddr& remote, JBStream::Type t)
|
|||
remote.host().c_str(),remote.port(),lookup(t,JBStream::s_typeName));
|
||||
return false;
|
||||
}
|
||||
if (ssl && t != JBStream::c2s) {
|
||||
Debug(this,DebugStub,"SSL connection on non c2s stream");
|
||||
return false;
|
||||
}
|
||||
JBStream* s = 0;
|
||||
if (t == JBStream::c2s)
|
||||
s = new JBClientStream(this,sock);
|
||||
s = new JBClientStream(this,sock,ssl);
|
||||
else if (t == JBStream::s2s)
|
||||
s = new JBServerStream(this,sock,false);
|
||||
else if (t == JBStream::comp)
|
||||
|
|
|
@ -129,7 +129,7 @@ static inline unsigned int timerMultiplier(JBStream* stream)
|
|||
* JBStream
|
||||
*/
|
||||
// Incoming
|
||||
JBStream::JBStream(JBEngine* engine, Socket* socket, Type t)
|
||||
JBStream::JBStream(JBEngine* engine, Socket* socket, Type t, bool ssl)
|
||||
: Mutex(true,"JBStream"),
|
||||
m_sasl(0),
|
||||
m_state(Idle), m_flags(0), m_xmlns(XMPPNamespace::Count), m_lastEvent(0),
|
||||
|
@ -141,11 +141,13 @@ JBStream::JBStream(JBEngine* engine, Socket* socket, Type t)
|
|||
m_incoming(true), m_terminateEvent(0),
|
||||
m_xmlDom(0), m_socket(0), m_socketFlags(0), m_connectPort(0)
|
||||
{
|
||||
if (ssl)
|
||||
m_flags |= (StreamSecured | StreamTls);
|
||||
m_engine->buildStreamName(m_name);
|
||||
debugName(m_name);
|
||||
debugChain(m_engine);
|
||||
Debug(this,DebugAll,"JBStream::JBStream(%p,%p,%s) incoming [%p]",
|
||||
engine,socket,typeName(),this);
|
||||
Debug(this,DebugAll,"JBStream::JBStream(%p,%p,%s,%s) incoming [%p]",
|
||||
engine,socket,typeName(),String::boolText(ssl),this);
|
||||
setXmlns();
|
||||
// Don't restart incoming streams
|
||||
m_flags |= NoAutoRestart;
|
||||
|
@ -480,15 +482,21 @@ void JBStream::start(XMPPFeatureList* features, XmlElement* caps)
|
|||
m_features.clear();
|
||||
if (features)
|
||||
m_features.add(*features);
|
||||
// Set secured flag if we don't advertise TLS
|
||||
if (!(flag(StreamSecured) || m_features.get(XMPPNamespace::Tls)))
|
||||
if (flag(StreamRemoteVer1)) {
|
||||
// Set secured flag if we don't advertise TLS
|
||||
if (!(flag(StreamSecured) || m_features.get(XMPPNamespace::Tls)))
|
||||
setSecured();
|
||||
// Set authenticated flag if we don't advertise authentication mechanisms
|
||||
if (flag(StreamSecured)) {
|
||||
if (flag(StreamAuthenticated))
|
||||
m_features.remove(XMPPNamespace::Sasl);
|
||||
else if (!m_features.get(XMPPNamespace::Sasl))
|
||||
m_flags |= JBStream::StreamAuthenticated;
|
||||
}
|
||||
}
|
||||
else if (m_type == c2s) {
|
||||
// c2s using non-sasl auth
|
||||
setSecured();
|
||||
// Set authenticated flag if we don't advertise authentication mechanisms
|
||||
if (flag(StreamSecured)) {
|
||||
if (flag(StreamAuthenticated))
|
||||
m_features.remove(XMPPNamespace::Sasl);
|
||||
else if (!m_features.get(XMPPNamespace::Sasl))
|
||||
m_flags |= JBStream::StreamAuthenticated;
|
||||
}
|
||||
// Send start and features
|
||||
XmlElement* s = buildStreamStart();
|
||||
|
@ -510,7 +518,8 @@ void JBStream::start(XMPPFeatureList* features, XmlElement* caps)
|
|||
}
|
||||
|
||||
// Authenticate an incoming stream
|
||||
bool JBStream::authenticated(bool ok, const String& rsp, XMPPError::Type error)
|
||||
bool JBStream::authenticated(bool ok, const String& rsp, XMPPError::Type error,
|
||||
const char* username, const char* id, const char* resource)
|
||||
{
|
||||
Lock lock(this);
|
||||
if (m_state != Auth || !incoming())
|
||||
|
@ -519,20 +528,38 @@ bool JBStream::authenticated(bool ok, const String& rsp, XMPPError::Type error)
|
|||
String::boolText(ok),rsp.safe(),XMPPUtils::s_error[error].c_str(),
|
||||
m_local.c_str(),this);
|
||||
if (ok) {
|
||||
m_flags |= StreamAuthenticated;
|
||||
if (m_type == c2s) {
|
||||
// Set remote party node if not provided
|
||||
if (m_type == c2s && m_sasl && m_sasl->m_params && !m_remote.node()) {
|
||||
m_remote.set(m_sasl->m_params->getValue("username"),m_local.domain(),"");
|
||||
Debug(this,DebugAll,"Remote party set to '%s' [%p]",m_remote.c_str(),this);
|
||||
}
|
||||
m_features.remove(XMPPNamespace::Sasl);
|
||||
String text;
|
||||
if (m_sasl)
|
||||
if (m_sasl) {
|
||||
// Set remote party node if provided
|
||||
if (!TelEngine::null(username)) {
|
||||
m_remote.set(username,m_local.domain(),"");
|
||||
Debug(this,DebugAll,"Remote party set to '%s' [%p]",m_remote.c_str(),this);
|
||||
}
|
||||
String text;
|
||||
m_sasl->buildAuthRspReply(text,rsp);
|
||||
XmlElement* s = XMPPUtils::createElement(XmlTag::Success,
|
||||
XMPPNamespace::Sasl,text);
|
||||
ok = sendStreamXml(WaitStart,s);
|
||||
XmlElement* s = XMPPUtils::createElement(XmlTag::Success,
|
||||
XMPPNamespace::Sasl,text);
|
||||
ok = sendStreamXml(WaitStart,s);
|
||||
}
|
||||
else if (m_features.get(XMPPNamespace::IqAuth)) {
|
||||
// Set remote party if not provided
|
||||
if (!TelEngine::null(username))
|
||||
m_remote.set(username,m_local.domain(),resource);
|
||||
else
|
||||
m_remote.resource(resource);
|
||||
if (m_remote.isFull()) {
|
||||
Debug(this,DebugAll,"Remote party set to '%s' [%p]",m_remote.c_str(),this);
|
||||
XmlElement* rsp = XMPPUtils::createIqResult(0,0,id,
|
||||
XMPPUtils::createElement(XmlTag::Query,XMPPNamespace::IqAuth));
|
||||
ok = sendStreamXml(Running,rsp);
|
||||
if (!ok)
|
||||
m_remote.set(m_local.domain());
|
||||
}
|
||||
else
|
||||
terminate(0,true,0,XMPPError::Internal);
|
||||
}
|
||||
else
|
||||
terminate(0,true,0,XMPPError::Internal);
|
||||
}
|
||||
else if (m_type == s2s) {
|
||||
XmlElement* rsp = XMPPUtils::createDialbackResult(m_local,m_remote,true);
|
||||
|
@ -542,11 +569,24 @@ bool JBStream::authenticated(bool ok, const String& rsp, XMPPError::Type error)
|
|||
XmlElement* rsp = XMPPUtils::createElement(XmlTag::Handshake);
|
||||
ok = sendStreamXml(Running,rsp);
|
||||
}
|
||||
if (ok) {
|
||||
m_features.remove(XMPPNamespace::Sasl);
|
||||
m_features.remove(XMPPNamespace::IqAuth);
|
||||
m_flags |= StreamAuthenticated;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (m_type == c2s) {
|
||||
XmlElement* failure = XMPPUtils::createFailure(XMPPNamespace::Sasl,error);
|
||||
ok = sendStreamXml(Features,failure);
|
||||
XmlElement* rsp = 0;
|
||||
if (m_sasl)
|
||||
rsp = XMPPUtils::createFailure(XMPPNamespace::Sasl,error);
|
||||
else {
|
||||
rsp = XMPPUtils::createIq(XMPPUtils::IqError,0,0,id);
|
||||
if (TelEngine::null(id))
|
||||
rsp->addChild(XMPPUtils::createElement(XmlTag::Query,XMPPNamespace::IqAuth));
|
||||
rsp->addChild(XMPPUtils::createError(XMPPError::TypeAuth,error));
|
||||
}
|
||||
ok = sendStreamXml(Features,rsp);
|
||||
}
|
||||
else if (m_type == s2s) {
|
||||
XmlElement* rsp = XMPPUtils::createDialbackResult(m_local,m_remote,false);
|
||||
|
@ -919,7 +959,8 @@ void JBStream::checkTimeouts(u_int64_t time)
|
|||
// Reset the stream's connection. Build a new XML parser if the socket is valid
|
||||
void JBStream::resetConnection(Socket* sock)
|
||||
{
|
||||
XDebug(this,DebugAll,"JBStream::resetConnection(%p) [%p]",sock,this);
|
||||
DDebug(this,DebugAll,"JBStream::resetConnection(%p) current=%p [%p]",
|
||||
sock,m_socket,this);
|
||||
// Release the old one
|
||||
if (m_socket) {
|
||||
// Wait for the socket to become available (not reading or writing)
|
||||
|
@ -978,7 +1019,8 @@ XmlElement* JBStream::buildStreamStart()
|
|||
start->setAttribute(XmlElement::s_ns,XMPPUtils::s_ns[m_xmlns]);
|
||||
start->setAttributeValid("from",m_local.bare());
|
||||
start->setAttributeValid("to",m_remote.bare());
|
||||
start->setAttribute("version","1.0");
|
||||
if (outgoing() || flag(StreamRemoteVer1))
|
||||
start->setAttribute("version","1.0");
|
||||
start->setAttribute("xml:lang","en");
|
||||
return start;
|
||||
}
|
||||
|
@ -1051,9 +1093,9 @@ bool JBStream::processStreamStart(const XmlElement* xml)
|
|||
m_flags |= StreamRemoteVer1;
|
||||
else if (remoteVersion < 1) {
|
||||
if (m_type == c2s)
|
||||
error = XMPPError::UnsupportedVersion;
|
||||
XDebug(this,DebugAll,"c2s stream start with version < 1 [%p]",this);
|
||||
else if (m_type == s2s) {
|
||||
// Accept invalid/unsupported version on if TLS is not required
|
||||
// Accept invalid/unsupported version only if TLS is not required
|
||||
if (!flag(TlsRequired)) {
|
||||
// Check dialback
|
||||
if (!xml->hasAttribute("xmlns:db",XMPPUtils::s_ns[XMPPNamespace::Dialback])) {
|
||||
|
@ -1150,15 +1192,21 @@ bool JBStream::checkStanzaRecv(XmlElement* xml, JabberID& from, JabberID& to)
|
|||
|
||||
// RFC 3920bis 5.2: Accept stanzas only if the stream was authenticated
|
||||
// Accept IQs in jabber:iq:register namespace
|
||||
// Accept IQs in jabber:iq:auth namespace
|
||||
// They might be received on a non authenticated stream)
|
||||
if (!flag(StreamAuthenticated)) {
|
||||
bool isIq = XMPPUtils::isTag(*xml,XmlTag::Iq,m_xmlns);
|
||||
bool valid = isIq && XMPPUtils::findFirstChild(*xml,XmlTag::Count,
|
||||
XMPPNamespace::IqRegister);
|
||||
// Outgoing client stream: check register responses
|
||||
if (!valid && outgoing()) {
|
||||
JBClientStream* c2s = clientStream();
|
||||
valid = c2s && c2s->isRegisterId(*xml);
|
||||
JBClientStream* c2s = clientStream();
|
||||
if (!valid && c2s) {
|
||||
// Outgoing client stream: check register responses
|
||||
// Incoming client stream: check auth stanzas
|
||||
if (outgoing())
|
||||
valid = c2s->isRegisterId(*xml);
|
||||
else
|
||||
valid = isIq && XMPPUtils::findFirstChild(*xml,XmlTag::Count,
|
||||
XMPPNamespace::IqAuth);
|
||||
}
|
||||
if (!valid) {
|
||||
terminate(0,false,xml,XMPPError::NotAuthorized,
|
||||
|
@ -1640,18 +1688,26 @@ bool JBStream::processFeaturesIn(XmlElement* xml, const JabberID& from, const Ja
|
|||
if (!m_features.get(ns)) {
|
||||
// Check for some features that can be negotiated via 'iq' elements
|
||||
if (m_type == c2s && *t == XMPPUtils::s_tag[XmlTag::Iq] && ns == m_xmlns) {
|
||||
int chTag = XmlTag::Count;
|
||||
int chNs = XMPPNamespace::Count;
|
||||
XmlElement* child = xml->findFirstChild();
|
||||
int chNs = child ? XMPPUtils::ns(*child) : XMPPNamespace::Count;
|
||||
bool bindOk = chNs == XMPPNamespace::Bind && m_features.get(XMPPNamespace::Bind);
|
||||
bool regOk = !bindOk && chNs == XMPPNamespace::IqRegister;
|
||||
if (child)
|
||||
XMPPUtils::getTag(*child,chTag,chNs);
|
||||
// Bind
|
||||
if (bindOk) {
|
||||
if (chNs == XMPPNamespace::Bind && m_features.get(XMPPNamespace::Bind)) {
|
||||
// We've sent bind feature
|
||||
// Don't accept it if not authenticated and TLS/SASL must be negotiated
|
||||
if (!flag(StreamAuthenticated)) {
|
||||
XMPPFeature* tls = m_features.get(XMPPNamespace::Tls);
|
||||
if (tls && tls->required()) {
|
||||
XmlElement* e = XMPPUtils::createError(xml,XMPPError::TypeWait,
|
||||
XMPPError::EncryptionRequired);
|
||||
sendStreamXml(m_state,e);
|
||||
return true;
|
||||
}
|
||||
XMPPFeature* sasl = m_features.get(XMPPNamespace::Sasl);
|
||||
if ((tls && tls->required()) || (sasl && sasl->required())) {
|
||||
XMPPFeature* iqAuth = m_features.get(XMPPNamespace::IqAuth);
|
||||
if ((sasl && sasl->required()) || (iqAuth && iqAuth->required())) {
|
||||
XmlElement* e = XMPPUtils::createError(xml,XMPPError::TypeAuth,
|
||||
XMPPError::NotAllowed);
|
||||
sendStreamXml(m_state,e);
|
||||
|
@ -1662,14 +1718,55 @@ bool JBStream::processFeaturesIn(XmlElement* xml, const JabberID& from, const Ja
|
|||
m_flags |= StreamSecured | StreamAuthenticated;
|
||||
m_features.remove(XMPPNamespace::Tls);
|
||||
m_features.remove(XMPPNamespace::Sasl);
|
||||
m_features.remove(XMPPNamespace::IqAuth);
|
||||
changeState(Running);
|
||||
return processRunning(xml,from,to);
|
||||
}
|
||||
// Register
|
||||
if (regOk) {
|
||||
else if (chNs == XMPPNamespace::IqRegister) {
|
||||
// Register
|
||||
m_events.append(new JBEvent(JBEvent::Iq,this,xml,xml->findFirstChild()));
|
||||
return true;
|
||||
}
|
||||
else if (chNs == XMPPNamespace::IqAuth) {
|
||||
XMPPUtils::IqType type = XMPPUtils::iqType(xml->attribute("type"));
|
||||
bool req = type == XMPPUtils::IqGet || type == XMPPUtils::IqSet;
|
||||
// Stream non SASL auth
|
||||
// Check if we support it
|
||||
if (!m_features.get(XMPPNamespace::IqAuth)) {
|
||||
if (req) {
|
||||
XmlElement* e = XMPPUtils::createError(xml,XMPPError::TypeCancel,
|
||||
XMPPError::NotAllowed);
|
||||
return sendStreamXml(m_state,e);
|
||||
}
|
||||
return dropXml(xml,"unexpected jabber:iq:auth element");
|
||||
}
|
||||
if (flag(StreamRemoteVer1)) {
|
||||
XMPPFeature* tls = m_features.get(XMPPNamespace::Tls);
|
||||
if (tls && tls->required()) {
|
||||
XmlElement* e = XMPPUtils::createError(xml,XMPPError::TypeWait,
|
||||
XMPPError::EncryptionRequired);
|
||||
sendStreamXml(m_state,e);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (chTag != XmlTag::Query) {
|
||||
if (req) {
|
||||
XmlElement* e = XMPPUtils::createError(xml,XMPPError::TypeModify,
|
||||
XMPPError::FeatureNotImpl);
|
||||
sendStreamXml(m_state,e);
|
||||
return true;
|
||||
}
|
||||
return dropXml(xml,"expecting iq auth with 'query' child");
|
||||
}
|
||||
// Send it to the uppper layer
|
||||
if (type == XMPPUtils::IqSet) {
|
||||
m_events.append(new JBEvent(JBEvent::Auth,this,xml,xml->findFirstChild()));
|
||||
changeState(Auth);
|
||||
}
|
||||
else
|
||||
m_events.append(new JBEvent(JBEvent::Iq,this,xml,xml->findFirstChild()));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// s2s waiting for dialback
|
||||
if (m_type == s2s) {
|
||||
|
@ -1736,7 +1833,7 @@ bool JBStream::processFeaturesIn(XmlElement* xml, const JabberID& from, const Ja
|
|||
}
|
||||
return true;
|
||||
}
|
||||
// Stream auth
|
||||
// Stream SASL auth
|
||||
if (ns == XMPPNamespace::Sasl) {
|
||||
if (*t != XMPPUtils::s_tag[XmlTag::Auth])
|
||||
return dropXml(xml,"expecting sasl 'auth' element");
|
||||
|
@ -1885,8 +1982,8 @@ void JBStream::eventTerminated(const JBEvent* ev)
|
|||
/*
|
||||
* JBClientStream
|
||||
*/
|
||||
JBClientStream::JBClientStream(JBEngine* engine, Socket* socket)
|
||||
: JBStream(engine,socket,c2s),
|
||||
JBClientStream::JBClientStream(JBEngine* engine, Socket* socket, bool ssl)
|
||||
: JBStream(engine,socket,c2s,ssl),
|
||||
m_userData(0), m_registerReq(0)
|
||||
{
|
||||
}
|
||||
|
@ -1926,7 +2023,7 @@ void JBClientStream::bind(const String& resource, const char* id, XMPPError::Typ
|
|||
m_features.remove(XMPPNamespace::Bind);
|
||||
}
|
||||
|
||||
// Request account setup (or info) an outgoing stream
|
||||
// Request account setup (or info) on outgoing stream
|
||||
bool JBClientStream::requestRegister(bool data, bool set, const String& newPass)
|
||||
{
|
||||
if (incoming())
|
||||
|
|
|
@ -964,6 +964,34 @@ XmlElement* XMPPUtils::createRegisterQuery(IqType type, const char* from,
|
|||
return iq;
|
||||
}
|
||||
|
||||
// Build a jabber:iq:auth 'iq' set element
|
||||
XmlElement* XMPPUtils::createIqAuthSet(const char* id, const char* username,
|
||||
const char* resource, const char* authStr, bool digest)
|
||||
{
|
||||
XmlElement* iq = createIq(IqSet,0,0,id);
|
||||
XmlElement* q = createElement(XmlTag::Query,XMPPNamespace::IqAuth);
|
||||
iq->addChild(q);
|
||||
q->addChild(createElement(XmlTag::Username,username));
|
||||
q->addChild(createElement(XmlTag::Resource,resource));
|
||||
q->addChild(createElement(digest ? XmlTag::Digest : XmlTag::Password,authStr));
|
||||
return iq;
|
||||
}
|
||||
|
||||
// Build a jabber:iq:auth 'iq' offer in response to a 'get' request
|
||||
XmlElement* XMPPUtils::createIqAuthOffer(const char* id, bool digest, bool plain)
|
||||
{
|
||||
XmlElement* iq = createIq(IqResult,0,0,id);
|
||||
XmlElement* q = createElement(XmlTag::Query,XMPPNamespace::IqAuth);
|
||||
iq->addChild(q);
|
||||
q->addChild(createElement(XmlTag::Username));
|
||||
q->addChild(createElement(XmlTag::Resource));
|
||||
if (digest)
|
||||
q->addChild(createElement(XmlTag::Digest));
|
||||
if (plain)
|
||||
q->addChild(createElement(XmlTag::Password));
|
||||
return iq;
|
||||
}
|
||||
|
||||
// Find an element's first child element in a given namespace
|
||||
XmlElement* XMPPUtils::findFirstChild(const XmlElement& xml, int t, int ns)
|
||||
{
|
||||
|
|
|
@ -1261,6 +1261,39 @@ public:
|
|||
const char* to, const char* id,
|
||||
XmlElement* child1 = 0, XmlElement* child2 = 0, XmlElement* child3 = 0);
|
||||
|
||||
/**
|
||||
* Build a jabber:iq:auth 'iq' get element
|
||||
* @param id Element 'id' attribute
|
||||
* @return A valid XmlElement pointer
|
||||
*/
|
||||
static inline XmlElement* createIqAuthGet(const char* id) {
|
||||
XmlElement* iq = createIq(IqGet,0,0,id);
|
||||
iq->addChild(createElement(XmlTag::Query,XMPPNamespace::IqAuth));
|
||||
return iq;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a jabber:iq:auth 'iq' set element
|
||||
* @param id Element 'id' attribute
|
||||
* @param username The username
|
||||
* @param resource The resource
|
||||
* @param authStr Authentication string
|
||||
* @param digest True if authentication string is a digest, false if it's a plain password
|
||||
* @return A valid XmlElement pointer
|
||||
*/
|
||||
static XmlElement* createIqAuthSet(const char* id, const char* username,
|
||||
const char* resource, const char* authStr, bool digest);
|
||||
|
||||
/**
|
||||
* Build a jabber:iq:auth 'iq' offer in response to a 'get' request
|
||||
* @param id Element 'id' attribute
|
||||
* @param digest Offer digest authentication
|
||||
* @param plain Offer plain password authentication
|
||||
* @return A valid XmlElement pointer
|
||||
*/
|
||||
static XmlElement* createIqAuthOffer(const char* id, bool digest = true,
|
||||
bool plain = false);
|
||||
|
||||
/**
|
||||
* Build an register query element used to create/set username/password
|
||||
* @param from The 'from' attribute
|
||||
|
|
|
@ -761,10 +761,14 @@ public:
|
|||
* false if authentication failed
|
||||
* @param rsp Optional success response content. Ignored if not authenticated
|
||||
* @param error Failure reason. Ignored if authenticated
|
||||
* @param username Authenticated user
|
||||
* @param id Non SASL auth response id
|
||||
* @param resource Client resource to set when non SASL authentication is used
|
||||
* @return False if stream state is incorrect
|
||||
*/
|
||||
bool authenticated(bool ok, const String& rsp = String::empty(),
|
||||
XMPPError::Type error = XMPPError::NotAuthorized);
|
||||
XMPPError::Type error = XMPPError::NotAuthorized,
|
||||
const char* username = 0, const char* id = 0, const char* resource = 0);
|
||||
|
||||
/**
|
||||
* Terminate the stream. Send stream end tag or error.
|
||||
|
@ -809,6 +813,17 @@ public:
|
|||
inline const char* typeName()
|
||||
{ return lookup(type(),s_typeName); }
|
||||
|
||||
/**
|
||||
* Build a SHA1 digest from stream id and secret
|
||||
* @param buf Destination buffer
|
||||
* @param secret The secret
|
||||
*/
|
||||
inline void buildSha1Digest(String& buf, const String& secret) {
|
||||
SHA1 sha(id() + secret);
|
||||
buf = sha.hexDigest();
|
||||
buf.toLower();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the string representation of this stream
|
||||
* @return Stream name
|
||||
|
@ -850,8 +865,9 @@ protected:
|
|||
* @param engine Engine owning this stream
|
||||
* @param socket The socket
|
||||
* @param t Stream type as enumeration
|
||||
* @param ssl True if the socket is already using SSL/TLS
|
||||
*/
|
||||
JBStream(JBEngine* engine, Socket* socket, Type t);
|
||||
JBStream(JBEngine* engine, Socket* socket, Type t, bool ssl = false);
|
||||
|
||||
/**
|
||||
* Constructor. Build an outgoing stream
|
||||
|
@ -1177,8 +1193,9 @@ public:
|
|||
* Constructor. Build an incoming stream from a socket
|
||||
* @param engine Engine owning this stream
|
||||
* @param socket The socket
|
||||
* @param ssl True if the socket is already using SSL/TLS
|
||||
*/
|
||||
JBClientStream(JBEngine* engine, Socket* socket);
|
||||
JBClientStream(JBEngine* engine, Socket* socket, bool ssl = false);
|
||||
|
||||
/**
|
||||
* Constructor. Build an outgoing stream
|
||||
|
@ -1227,7 +1244,7 @@ public:
|
|||
XMPPError::Type error = XMPPError::NoError);
|
||||
|
||||
/**
|
||||
* Request account register or change an outgoing stream.
|
||||
* Request account register or change on outgoing stream.
|
||||
* This method is thread safe
|
||||
* @param data True to request registration/change, false to request info
|
||||
* @param set True to request new user registration, false to remove account from server
|
||||
|
@ -1395,17 +1412,6 @@ public:
|
|||
*/
|
||||
bool sendDialback();
|
||||
|
||||
/**
|
||||
* Build a component handshake from stream id and secret as defined in XEP 0114
|
||||
* @param buf Destination buffer
|
||||
* @param secret The secret
|
||||
*/
|
||||
inline void buildHandshake(String& buf, const String& secret) {
|
||||
SHA1 sha(id() + secret);
|
||||
buf = sha.hexDigest();
|
||||
buf.toLower();
|
||||
}
|
||||
|
||||
/**
|
||||
* Start a component stream (reply to received stream start)
|
||||
* @param local Local domain
|
||||
|
@ -1638,9 +1644,10 @@ public:
|
|||
* @param sock Accepted socket
|
||||
* @param remote Remote ip and port
|
||||
* @param t Expected stream type
|
||||
* @param ssl True if the socket is already using SSL/TLS
|
||||
* @return True on success
|
||||
*/
|
||||
bool acceptConn(Socket* sock, SocketAddr& remote, JBStream::Type t);
|
||||
bool acceptConn(Socket* sock, SocketAddr& remote, JBStream::Type t, bool ssl = false);
|
||||
|
||||
/**
|
||||
* Find a stream by its name. This method is thread safe
|
||||
|
|
|
@ -245,6 +245,9 @@ public:
|
|||
// The given event is always valid and carry a valid element
|
||||
XmlElement* processIqRegister(JBEvent* ev, JBStream::Type sType, XMPPUtils::IqType t,
|
||||
const String& domain, int flags);
|
||||
// Process all incoming jabber:iq:auth stanzas
|
||||
// The given event is always valid and carry a valid element
|
||||
XmlElement* processIqAuth(JBEvent* ev, JBStream::Type sType, XMPPUtils::IqType t, int flags);
|
||||
// Handle disco info requests addressed to the server
|
||||
XmlElement* discoInfo(JBEvent* ev, JBStream::Type sType);
|
||||
// Handle disco items requests addressed to the server
|
||||
|
@ -280,6 +283,13 @@ public:
|
|||
void completeStreamRemote(String& str, const String& partWord, JBStream::Type t);
|
||||
// Complete stream name starting with partWord
|
||||
void completeStreamName(String& str, const String& partWord);
|
||||
// Remove a resource from binding resources list
|
||||
inline void removeBindingResource(const JabberID& user) {
|
||||
Lock lock(this);
|
||||
ObjList* o = user ? findBindingRes(user) : 0;
|
||||
if (o)
|
||||
o->remove();
|
||||
}
|
||||
// Program name and version to be advertised on request
|
||||
String m_progName;
|
||||
String m_progVersion;
|
||||
|
@ -295,8 +305,18 @@ private:
|
|||
return o;
|
||||
return 0;
|
||||
}
|
||||
// Add a resource to binding resources list. Make sure the resource is unique
|
||||
// Return true on success
|
||||
bool bindingResource(const JabberID& user);
|
||||
inline ObjList* findBindingRes(const JabberID& user) {
|
||||
for (ObjList* o = m_bindingResources.skipNull(); o; o = o->skipNext())
|
||||
if (user == *static_cast<JabberID*>(o->get()))
|
||||
return o;
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool m_c2sTlsRequired; // TLS is required on c2s streams
|
||||
bool m_allowUnsecurePlainAuth; // Allow user plain password auth on unsecured streams
|
||||
ObjList m_domains; // Domains serviced by this engine
|
||||
ObjList m_dynamicDomains; // Dynamic domains (components or items)
|
||||
ObjList m_restrictedResources; // Resource names the users can't use
|
||||
|
@ -305,6 +325,7 @@ private:
|
|||
XMPPFeatureList m_c2sFeatures; // Server features to advertise on c2s streams
|
||||
XMPPFeatureList m_features; // Server features to advertise on non c2s streams
|
||||
String m_dialbackSecret; // Server dialback secret used to build keys
|
||||
ObjList m_bindingResources; // List of resources in bind process
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -400,6 +421,8 @@ class UserAuthMessage : public Message
|
|||
public:
|
||||
// Fill the message with authentication data
|
||||
UserAuthMessage(JBEvent* ev);
|
||||
~UserAuthMessage();
|
||||
JabberID m_bindingUser;
|
||||
protected:
|
||||
// Check accepted and returned value. Calls stream's authenticated() method
|
||||
virtual void dispatched(bool accepted);
|
||||
|
@ -450,6 +473,19 @@ public:
|
|||
m_engine(engine), m_type(t), m_address(addr), m_port(port),
|
||||
m_backlog(backlog)
|
||||
{}
|
||||
// Build a SSL/TLS c2s stream listener
|
||||
// engine The engine to be notified when a connection request is received
|
||||
// addr Address to bind
|
||||
// port Port to bind
|
||||
// backlog Maximum length of the queue of pending connections, 0 for system maximum
|
||||
// prio Thread priority
|
||||
inline TcpListener(const char* name, JBEngine* engine, const char* context,
|
||||
const char* addr, int port, unsigned int backlog = 0,
|
||||
Thread::Priority prio = Thread::Normal)
|
||||
: Thread("TcpListener",prio), String(name),
|
||||
m_engine(engine), m_type(JBStream::c2s), m_address(addr), m_port(port),
|
||||
m_backlog(backlog), m_sslContext(context)
|
||||
{}
|
||||
// Remove from plugin
|
||||
virtual ~TcpListener();
|
||||
protected:
|
||||
|
@ -465,6 +501,7 @@ private:
|
|||
String m_address;
|
||||
int m_port;
|
||||
unsigned int m_backlog; // Pending connections queue length
|
||||
String m_sslContext; // SSL/TLS context
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -680,6 +717,13 @@ static void completeStreamType(String& buf, const String& part, bool addAll = fa
|
|||
Module::itemComplete(buf,*d,part);
|
||||
}
|
||||
|
||||
// Retrieve an element's child text
|
||||
static const String& getChildText(XmlElement& xml, int tag, int ns)
|
||||
{
|
||||
XmlElement* ch = XMPPUtils::findFirstChild(xml,tag,ns);
|
||||
return ch ? ch->getText() : String::empty();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* YJBEntityCapsList
|
||||
|
@ -718,7 +762,8 @@ void YJBEntityCapsList::capsAdded(JBEntityCaps* caps)
|
|||
* YJBEngine
|
||||
*/
|
||||
YJBEngine::YJBEngine()
|
||||
: m_c2sTlsRequired(false)
|
||||
: m_c2sTlsRequired(false),
|
||||
m_allowUnsecurePlainAuth(false)
|
||||
{
|
||||
m_c2sReceive = new YStreamSetReceive(this,10,"c2s/recv");
|
||||
m_c2sProcess = new YStreamSetProcess(this,10,"c2s/process");
|
||||
|
@ -760,6 +805,8 @@ void YJBEngine::initialize(const NamedList* params, bool first)
|
|||
if (!params)
|
||||
params = &dummy;
|
||||
|
||||
m_allowUnsecurePlainAuth = params->getBoolValue("c2s_allowunsecureplainauth");
|
||||
|
||||
// Serviced domains
|
||||
// Check if an existing domain is no longer accepted
|
||||
// Terminate all streams having local party the deleted domain
|
||||
|
@ -824,6 +871,11 @@ void YJBEngine::initialize(const NamedList* params, bool first)
|
|||
else
|
||||
m_remoteDomain.m_flags &= ~JBStream::TlsRequired;
|
||||
|
||||
// Allow old style client auth
|
||||
m_c2sFeatures.remove(XMPPNamespace::IqAuth);
|
||||
if (params->getBoolValue("c2s_oldstyleauth",true))
|
||||
m_c2sFeatures.add(XmlTag::Auth,XMPPNamespace::IqAuth);
|
||||
|
||||
// Program name and version to be advertised on request
|
||||
if (!m_progName) {
|
||||
m_progName = "Yate";
|
||||
|
@ -1543,14 +1595,33 @@ void YJBEngine::processStartIn(JBEvent* ev)
|
|||
if (ev->stream()->type() == JBStream::c2s)
|
||||
ev->stream()->setTlsRequired(m_c2sTlsRequired);
|
||||
|
||||
// Don't advertise any features if version is not 1
|
||||
XMPPFeatureList features;
|
||||
|
||||
// Stream version is not 1
|
||||
if (!ev->stream()->flag(JBStream::StreamRemoteVer1)) {
|
||||
ev->stream()->start();
|
||||
XMPPError::Type error = XMPPError::NoError;
|
||||
if (ev->stream()->type() == JBStream::c2s) {
|
||||
lock();
|
||||
bool ok = m_c2sFeatures.get(XMPPNamespace::IqAuth);
|
||||
unlock();
|
||||
if (ok) {
|
||||
if (ev->stream()->flag(JBStream::StreamTls) ||
|
||||
!ev->stream()->flag(JBStream::TlsRequired))
|
||||
features.add(XmlTag::Auth,XMPPNamespace::IqAuth,true);
|
||||
else
|
||||
error = XMPPError::EncryptionRequired;
|
||||
}
|
||||
else
|
||||
error = XMPPError::UnsupportedVersion;
|
||||
}
|
||||
if (error == XMPPError::NoError)
|
||||
ev->stream()->start(&features);
|
||||
else
|
||||
ev->stream()->terminate(-1,true,0,error);
|
||||
return;
|
||||
}
|
||||
|
||||
// Set stream features
|
||||
XMPPFeatureList features;
|
||||
// Add TLS if not secured
|
||||
if (!ev->stream()->flag(JBStream::StreamSecured))
|
||||
features.add(XmlTag::Starttls,XMPPNamespace::Tls,
|
||||
|
@ -1566,20 +1637,13 @@ void YJBEngine::processStartIn(JBEvent* ev)
|
|||
bool addCaps = false;
|
||||
if (!(tls && tls->required())) {
|
||||
addCaps = true;
|
||||
// Stream secured
|
||||
// Add SASL if stream is not authenticated
|
||||
if (!ev->stream()->flag(JBStream::StreamAuthenticated)) {
|
||||
int mech = 0;
|
||||
switch (ev->stream()->type()) {
|
||||
case JBStream::c2s:
|
||||
mech = XMPPUtils::AuthMD5 | XMPPUtils::AuthPlain;
|
||||
break;
|
||||
case JBStream::s2s:
|
||||
mech = XMPPUtils::AuthMD5 | XMPPUtils::AuthPlain;
|
||||
break;
|
||||
}
|
||||
if (mech)
|
||||
features.add(new XMPPFeatureSasl(mech,true));
|
||||
// Add SASL auth if stream is not authenticated
|
||||
if (!ev->stream()->flag(JBStream::StreamAuthenticated) &&
|
||||
ev->stream()->type() == JBStream::c2s) {
|
||||
int mech = XMPPUtils::AuthMD5;
|
||||
if (ev->stream()->flag(JBStream::StreamTls) || m_allowUnsecurePlainAuth)
|
||||
mech |= XMPPUtils::AuthPlain;
|
||||
features.add(new XMPPFeatureSasl(mech,true));
|
||||
}
|
||||
// Add register if enabled
|
||||
if (addReg)
|
||||
|
@ -1607,18 +1671,90 @@ void YJBEngine::processStartIn(JBEvent* ev)
|
|||
// The given event is always valid and carry a valid stream
|
||||
void YJBEngine::processAuthIn(JBEvent* ev)
|
||||
{
|
||||
UserAuthMessage* m = new UserAuthMessage(ev);
|
||||
XMPPError::Type error = XMPPError::NoError;
|
||||
ev->stream()->lock();
|
||||
bool plain = ev->stream()->m_sasl && ev->stream()->m_sasl->m_plain &&
|
||||
!ev->stream()->flag(JBStream::StreamTls);
|
||||
ev->stream()->unlock();
|
||||
if (plain) {
|
||||
// TODO: check if the remote party is allowed to use plain
|
||||
// password auth on unsecured transport
|
||||
ev->releaseStream();
|
||||
ev->stream()->authenticated(false,String::empty(),XMPPError::EncryptionRequired);
|
||||
return;
|
||||
if (ev->stream()->type() == JBStream::c2s) {
|
||||
bool allowPlain = ev->stream()->flag(JBStream::StreamTls) ||
|
||||
m_allowUnsecurePlainAuth;
|
||||
while (true) {
|
||||
// Stream is using SASL auth
|
||||
if (ev->stream()->m_sasl) {
|
||||
XDebug(this,DebugAll,"processAuthIn(%s) c2s sasl",ev->stream()->name());
|
||||
if (ev->stream()->m_sasl->m_plain && !allowPlain) {
|
||||
error = XMPPError::EncryptionRequired;
|
||||
break;
|
||||
}
|
||||
if (ev->stream()->m_sasl->m_params) {
|
||||
m->copyParams(*(ev->stream()->m_sasl->m_params));
|
||||
// Override username: set it to bare jid
|
||||
String* user = ev->stream()->m_sasl->m_params->getParam("username");
|
||||
if (!TelEngine::null(user))
|
||||
m->setParam("username",*user + "@" + ev->stream()->local().domain());
|
||||
}
|
||||
break;
|
||||
}
|
||||
// Check non SASL request
|
||||
XmlElement* q = ev->child();
|
||||
if (q) {
|
||||
int t,ns;
|
||||
if (XMPPUtils::getTag(*q,t,ns)) {
|
||||
if (t != XmlTag::Query || ns != XMPPNamespace::IqAuth) {
|
||||
error = XMPPError::ServiceUnavailable;
|
||||
break;
|
||||
}
|
||||
XDebug(this,DebugAll,"processAuthIn(%s) c2s non sasl",ev->stream()->name());
|
||||
JabberID user(getChildText(*q,XmlTag::Username,XMPPNamespace::IqAuth),
|
||||
ev->stream()->local().domain(),
|
||||
getChildText(*q,XmlTag::Resource,XMPPNamespace::IqAuth));
|
||||
if (!user.resource()) {
|
||||
error = XMPPError::NotAcceptable;
|
||||
break;
|
||||
}
|
||||
if (user.bare())
|
||||
m->addParam("username",user.bare());
|
||||
const String& pwd = getChildText(*q,XmlTag::Password,XMPPNamespace::IqAuth);
|
||||
if (pwd) {
|
||||
if (allowPlain)
|
||||
m->addParam("password",pwd);
|
||||
else {
|
||||
error = XMPPError::EncryptionRequired;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
const String& d = getChildText(*q,XmlTag::Digest,XMPPNamespace::IqAuth);
|
||||
if (d)
|
||||
m->addParam("digest",d);
|
||||
}
|
||||
// Make sure the resource is unique
|
||||
if (!bindingResource(user)) {
|
||||
error = XMPPError::Conflict;
|
||||
break;
|
||||
}
|
||||
else
|
||||
m->m_bindingUser = user;
|
||||
m->addParam("instance",user.resource());
|
||||
break;
|
||||
}
|
||||
}
|
||||
error = XMPPError::Internal;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (ev->stream()->type() == JBStream::comp) {
|
||||
XDebug(this,DebugAll,"processAuthIn(%s) component handshake",ev->stream()->name());
|
||||
m->setParam("username",ev->stream()->remote());
|
||||
m->setParam("handshake",ev->text());
|
||||
}
|
||||
ev->stream()->unlock();
|
||||
if (error == XMPPError::NoError)
|
||||
Engine::enqueue(m);
|
||||
else {
|
||||
ev->releaseStream();
|
||||
ev->stream()->authenticated(false,String::empty(),error,0,ev->id());
|
||||
TelEngine::destruct(m);
|
||||
}
|
||||
Engine::enqueue(new UserAuthMessage(ev));
|
||||
}
|
||||
|
||||
// Process Bind events
|
||||
|
@ -1630,38 +1766,32 @@ void YJBEngine::processBind(JBEvent* ev)
|
|||
ev->sendStanzaError(XMPPError::ServiceUnavailable);
|
||||
return;
|
||||
}
|
||||
String resource;
|
||||
XmlElement* res = XMPPUtils::findFirstChild(*ev->child(),XmlTag::Resource);
|
||||
if (res) {
|
||||
// Lock the engine to prevent other stream to bind the same resource
|
||||
lock();
|
||||
resource = res->getText();
|
||||
if (resource) {
|
||||
if (restrictedResource(resource))
|
||||
resource.clear();
|
||||
else {
|
||||
Message* m = __plugin.message("resource.notify");
|
||||
m->addParam("operation","query");
|
||||
m->addParam("nodata",String::boolText(true));
|
||||
m->addParam("contact",c2s->remote().bare());
|
||||
m->addParam("instance",resource);
|
||||
if (Engine::dispatch(*m))
|
||||
resource.clear();
|
||||
TelEngine::destruct(m);
|
||||
}
|
||||
c2s->lock();
|
||||
JabberID jid(c2s->local());
|
||||
c2s->unlock();
|
||||
jid.resource(getChildText(*ev->child(),XmlTag::Resource,XMPPNamespace::Bind));
|
||||
if (jid.resource() && !bindingResource(jid))
|
||||
jid.resource("");
|
||||
if (!jid.resource()) {
|
||||
for (int i = 0; i < 3; i++) {
|
||||
MD5 md5(c2s->id());
|
||||
jid.resource(md5.hexDigest());
|
||||
if (bindingResource(jid))
|
||||
break;
|
||||
jid.resource("");
|
||||
}
|
||||
unlock();
|
||||
}
|
||||
if (!resource) {
|
||||
MD5 md5(c2s->id());
|
||||
resource = md5.hexDigest();
|
||||
bool ok = false;
|
||||
if (jid.resource()) {
|
||||
Message* m = userRegister(*c2s,true,jid.resource());
|
||||
ok = Engine::dispatch(m);
|
||||
TelEngine::destruct(m);
|
||||
}
|
||||
Message* m = userRegister(*c2s,true,resource);
|
||||
if (Engine::dispatch(m))
|
||||
c2s->bind(resource,ev->id());
|
||||
if (ok)
|
||||
c2s->bind(jid.resource(),ev->id());
|
||||
else
|
||||
ev->sendStanzaError(XMPPError::NotAuthorized);
|
||||
TelEngine::destruct(m);
|
||||
removeBindingResource(jid);
|
||||
}
|
||||
|
||||
// Process stream Running, Destroy, Terminated events
|
||||
|
@ -1904,6 +2034,29 @@ XmlElement* YJBEngine::processIqRegister(JBEvent* ev, JBStream::Type sType,
|
|||
return rsp;
|
||||
}
|
||||
|
||||
// Process all incoming jabber:iq:auth stanzas
|
||||
// The given event is always valid and carry a valid element
|
||||
XmlElement* YJBEngine::processIqAuth(JBEvent* ev, JBStream::Type sType, XMPPUtils::IqType t,
|
||||
int flags)
|
||||
{
|
||||
if (sType != JBStream::c2s) {
|
||||
Debug(this,DebugInfo,"processIqAuth(%p) type=%s on non-client stream",
|
||||
ev,ev->stanzaType().c_str());
|
||||
// Iq auth not allowed from other servers
|
||||
if (t == XMPPUtils::IqGet || t == XMPPUtils::IqSet)
|
||||
return ev->buildIqError(false,XMPPError::NotAllowed);
|
||||
return 0;
|
||||
}
|
||||
DDebug(this,DebugAll,"processIqAuth(%p) type=%s",ev,ev->stanzaType().c_str());
|
||||
// Ignore responses
|
||||
if (t != XMPPUtils::IqGet && t != XMPPUtils::IqSet)
|
||||
return 0;
|
||||
if (t == XMPPUtils::IqGet)
|
||||
return XMPPUtils::createIqAuthOffer(ev->id(),true,
|
||||
m_allowUnsecurePlainAuth || (flags & JBStream::StreamTls));
|
||||
return ev->buildIqError(false,XMPPError::ServiceUnavailable);
|
||||
}
|
||||
|
||||
// Handle disco info requests addressed to the server
|
||||
XmlElement* YJBEngine::discoInfo(JBEvent* ev, JBStream::Type sType)
|
||||
{
|
||||
|
@ -2243,6 +2396,26 @@ void YJBEngine::notifyDbVerifyResult(const JabberID& local, const JabberID& remo
|
|||
TelEngine::destruct(notify);
|
||||
}
|
||||
|
||||
// Add a resource to binding resources list. Make sure the resource is unique
|
||||
// Return true on success
|
||||
bool YJBEngine::bindingResource(const JabberID& user)
|
||||
{
|
||||
Lock lock(this);
|
||||
if (!user.resource() || restrictedResource(user.resource()) ||
|
||||
findBindingRes(user))
|
||||
return false;
|
||||
Message* m = __plugin.message("resource.notify");
|
||||
m->addParam("operation","query");
|
||||
m->addParam("nodata",String::boolText(true));
|
||||
m->addParam("contact",user.bare());
|
||||
m->addParam("instance",user.resource());
|
||||
bool ok = !Engine::dispatch(*m);
|
||||
TelEngine::destruct(m);
|
||||
if (ok)
|
||||
m_bindingResources.append(new JabberID(user));
|
||||
return ok;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* JBPendingJob
|
||||
|
@ -2646,6 +2819,15 @@ void JBPendingWorker::processIq(JBPendingJob& job)
|
|||
else
|
||||
job.sendIqErrorStanza(XMPPError::ServiceUnavailable);
|
||||
return;
|
||||
// XEP-0078 Non SASL authentication
|
||||
case XMPPNamespace::IqAuth:
|
||||
if (job.m_serverTarget) {
|
||||
rsp = s_jabber->processIqAuth(ev,job.m_streamType,t,job.m_flags);
|
||||
job.sendStanza(rsp,false);
|
||||
}
|
||||
else
|
||||
job.sendIqErrorStanza(XMPPError::ServiceUnavailable);
|
||||
return;
|
||||
default: ;
|
||||
}
|
||||
|
||||
|
@ -2698,30 +2880,22 @@ UserAuthMessage::UserAuthMessage(JBEvent* ev)
|
|||
: Message("user.auth"),
|
||||
m_stream(ev->stream()->toString()), m_streamType((JBStream::Type)ev->stream()->type())
|
||||
{
|
||||
XDebug(&__plugin,DebugAll,"UserAuthMessage stream=%s type=%u",
|
||||
m_stream.c_str(),m_streamType);
|
||||
XDebug(&__plugin,DebugAll,"UserAuthMessage stream=%s type=%u [%p]",
|
||||
m_stream.c_str(),m_streamType,this);
|
||||
__plugin.complete(*this);
|
||||
addParam("streamtype",ev->stream()->typeName());
|
||||
ev->stream()->lock();
|
||||
if (ev->stream()->m_sasl && ev->stream()->m_sasl->m_params) {
|
||||
copyParams(*(ev->stream()->m_sasl->m_params));
|
||||
const char* username = ev->stream()->m_sasl->m_params->getValue("username");
|
||||
String user;
|
||||
if (username)
|
||||
user << username << "@";
|
||||
user << ev->stream()->local().domain();
|
||||
setParam("username",user);
|
||||
}
|
||||
else if (m_streamType == JBStream::comp) {
|
||||
setParam("username",ev->stream()->remote());
|
||||
setParam("handshake",ev->text());
|
||||
}
|
||||
ev->stream()->unlock();
|
||||
SocketAddr addr;
|
||||
if (ev->stream()->remoteAddr(addr)) {
|
||||
addParam("ip_host",addr.host());
|
||||
addParam("ip_port",String(addr.port()));
|
||||
}
|
||||
addParam("requestid",ev->id());
|
||||
}
|
||||
|
||||
UserAuthMessage::~UserAuthMessage()
|
||||
{
|
||||
if (m_bindingUser)
|
||||
s_jabber->removeBindingResource(m_bindingUser);
|
||||
}
|
||||
|
||||
// Check accepted and returned value. Calls stream's authenticated() method
|
||||
|
@ -2732,6 +2906,7 @@ void UserAuthMessage::dispatched(bool accepted)
|
|||
accepted,stream,m_stream.c_str(),m_streamType);
|
||||
bool ok = false;
|
||||
String rspValue;
|
||||
JabberID username = getValue("username");
|
||||
// Use a while() to break to the end
|
||||
while (stream) {
|
||||
Lock lock(stream);
|
||||
|
@ -2742,37 +2917,50 @@ void UserAuthMessage::dispatched(bool accepted)
|
|||
if (!(accepted || retValue()))
|
||||
break;
|
||||
// Returned password works only with username
|
||||
if (!getValue("username"))
|
||||
if (!username)
|
||||
break;
|
||||
// Check credentials
|
||||
if (m_streamType != JBStream::comp) {
|
||||
String* rsp = getParam("response");
|
||||
if (rsp) {
|
||||
if (!stream->m_sasl)
|
||||
break;
|
||||
if (stream->m_sasl->m_plain)
|
||||
ok = (*rsp == retValue());
|
||||
else {
|
||||
if (m_streamType == JBStream::c2s) {
|
||||
if (stream->m_sasl) {
|
||||
XDebug(&__plugin,DebugAll,"UserAuthMessage checking c2s sasl [%p]",this);
|
||||
String* rsp = getParam("response");
|
||||
if (rsp) {
|
||||
if (stream->m_sasl->m_plain)
|
||||
ok = (*rsp == retValue());
|
||||
else {
|
||||
String digest;
|
||||
stream->m_sasl->buildMD5Digest(digest,retValue(),true);
|
||||
ok = (*rsp == digest);
|
||||
if (ok)
|
||||
stream->m_sasl->buildMD5Digest(rspValue,retValue(),false);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
XDebug(&__plugin,DebugAll,"UserAuthMessage checking c2s non-sasl [%p]",this);
|
||||
String* auth = getParam("digest");
|
||||
if (auth) {
|
||||
String digest;
|
||||
stream->m_sasl->buildMD5Digest(digest,retValue(),true);
|
||||
ok = (*rsp == digest);
|
||||
if (ok)
|
||||
stream->m_sasl->buildMD5Digest(rspValue,retValue(),false);
|
||||
stream->buildSha1Digest(digest,retValue());
|
||||
ok = (digest == *auth);
|
||||
}
|
||||
else {
|
||||
auth = getParam("password");
|
||||
ok = auth && (*auth == retValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
JBServerStream* comp = stream->serverStream();
|
||||
if (comp) {
|
||||
String digest;
|
||||
comp->buildHandshake(digest,retValue());
|
||||
ok = (digest == getValue("handshake"));
|
||||
}
|
||||
else if (stream->type() == JBStream::comp) {
|
||||
XDebug(&__plugin,DebugAll,"UserAuthMessage checking component handshake [%p]",this);
|
||||
String digest;
|
||||
stream->buildSha1Digest(digest,retValue());
|
||||
ok = (digest == getValue("handshake"));
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (stream)
|
||||
stream->authenticated(ok,rspValue);
|
||||
stream->authenticated(ok,rspValue,XMPPError::NotAuthorized,username.node(),
|
||||
getValue("requestid"),getValue("instance"));
|
||||
TelEngine::destruct(stream);
|
||||
}
|
||||
|
||||
|
@ -2825,12 +3013,31 @@ TcpListener::~TcpListener()
|
|||
__plugin.listener(this,false);
|
||||
}
|
||||
|
||||
// Objects added to socket.ssl message when incoming connection is using SSL
|
||||
class RefSocket : public RefObject
|
||||
{
|
||||
public:
|
||||
inline RefSocket(Socket** sock)
|
||||
: m_socket(sock)
|
||||
{}
|
||||
virtual void* getObject(const String& name) const {
|
||||
if (name == "Socket*")
|
||||
return (void*)m_socket;
|
||||
return RefObject::getObject(name);
|
||||
}
|
||||
Socket** m_socket;
|
||||
private:
|
||||
RefSocket() {}
|
||||
};
|
||||
|
||||
// Bind and listen
|
||||
void TcpListener::run()
|
||||
{
|
||||
__plugin.listener(this,true);
|
||||
DDebug(&__plugin,DebugAll,"TcpListener(%s) '%s:%d' type='%s' start running [%p]",
|
||||
c_str(),m_address.safe(),m_port,lookup(m_type,JBStream::s_typeName),this);
|
||||
DDebug(&__plugin,DebugAll,
|
||||
"TcpListener(%s) '%s:%d' type='%s' context=%s start running [%p]",
|
||||
c_str(),m_address.safe(),m_port,lookup(m_type,JBStream::s_typeName),
|
||||
m_sslContext.c_str(),this);
|
||||
// Create the socket
|
||||
if (!m_socket.create(PF_INET,SOCK_STREAM)) {
|
||||
terminateSocket("failed to create socket");
|
||||
|
@ -2853,6 +3060,7 @@ void TcpListener::run()
|
|||
}
|
||||
XDebug(&__plugin,DebugAll,"Listener(%s) '%s:%d' start listening [%p]",
|
||||
c_str(),m_address.safe(),m_port,this);
|
||||
bool plain = m_sslContext.null();
|
||||
while (true) {
|
||||
if (Thread::check(false))
|
||||
break;
|
||||
|
@ -2862,21 +3070,26 @@ void TcpListener::run()
|
|||
if (sock) {
|
||||
DDebug(&__plugin,DebugAll,"Listener(%s) '%s:%d' got conn from '%s:%d' [%p]",
|
||||
c_str(),m_address.safe(),m_port,addr.host().c_str(),addr.port(),this);
|
||||
processed = m_engine && m_engine->acceptConn(sock,addr,m_type);
|
||||
if (plain)
|
||||
processed = m_engine && m_engine->acceptConn(sock,addr,m_type);
|
||||
else {
|
||||
Message m("socket.ssl");
|
||||
m.userData(new RefSocket(&sock));
|
||||
m.addParam("server",String::boolText(true));
|
||||
m.addParam("context",m_sslContext);
|
||||
if (Engine::dispatch(m))
|
||||
processed = m_engine && m_engine->acceptConn(sock,addr,m_type,true);
|
||||
else {
|
||||
Debug(&__plugin,DebugWarn,"Listener(%s) Failed to start SSL [%p]",
|
||||
c_str(),this);
|
||||
delete sock;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!processed)
|
||||
delete sock;
|
||||
}
|
||||
Thread::idle();
|
||||
#if 0
|
||||
if (processed) {
|
||||
if (m_sleepMs)
|
||||
Thread::msleep(m_sleepMs,false);
|
||||
}
|
||||
else if (m_sleepMsNone)
|
||||
Thread::msleep(m_sleepMsNone,false);
|
||||
else
|
||||
Thread::yield(false);
|
||||
#endif
|
||||
}
|
||||
terminateSocket();
|
||||
DDebug(&__plugin,DebugAll,"Listener(%s) '%s:%d' terminated [%p]",c_str(),
|
||||
|
@ -3296,21 +3509,30 @@ bool JBModule::buildListener(const String& name, NamedList& p)
|
|||
name.c_str(),stype);
|
||||
return false;
|
||||
}
|
||||
const char* context = 0;
|
||||
String* sport = p.getParam("port");
|
||||
int port = 0;
|
||||
if (!TelEngine::null(sport))
|
||||
port = sport->toInteger();
|
||||
else if (t == JBStream::c2s)
|
||||
port = XMPP_C2S_PORT;
|
||||
else if (t == JBStream::s2s)
|
||||
port = XMPP_S2S_PORT;
|
||||
if (t == JBStream::c2s) {
|
||||
context = p.getValue("sslcontext");
|
||||
if (TelEngine::null(sport) && TelEngine::null(context))
|
||||
port = XMPP_C2S_PORT;
|
||||
}
|
||||
if (!port) {
|
||||
Debug(this,DebugWarn,"Can't build listener='%s' with invalid port='%s'",
|
||||
name.c_str(),c_safe(sport));
|
||||
return false;
|
||||
}
|
||||
TcpListener* l = new TcpListener(name,s_jabber,t,
|
||||
p.getValue("address"),port,p.getIntValue("backlog",5));
|
||||
const char* addr = p.getValue("address");
|
||||
unsigned int backlog = p.getIntValue("backlog",5);
|
||||
TcpListener* l = 0;
|
||||
if (TelEngine::null(context))
|
||||
l = new TcpListener(name,s_jabber,t,addr,port,backlog);
|
||||
else
|
||||
l = new TcpListener(name,s_jabber,context,addr,port,backlog);
|
||||
if (l->startup())
|
||||
return true;
|
||||
Debug(this,DebugWarn,"Failed to start listener='%s' type='%s' addr='%s' port=%d",
|
||||
|
|
Loading…
Reference in New Issue