Ejemplo n.º 1
0
 protected function _authenticate($command, $xml)
 {
     //response switch
     switch ($command) {
         case self::AUTH_TYPE_STREAM:
             // Connection initialized (or after authentication). Not much to do here...
             if (isset($xml['stream:stream'][0]['#']['stream:features'])) {
                 // we already got all info we need
                 $features = $xml['stream:stream'][0]['#'];
             } else {
                 $features = $this->wait();
             }
             $second_time = isset($this->_streamId);
             $this->_streamId = $xml['stream:stream'][0]['@']['id'];
             if ($second_time) {
                 // If we are here for the second time after TLS, we need to continue logging in
                 //if there are no features
                 if (!sizeof($features)) {
                     //throw exception
                     Eden_Jabber_Error::i(Eden_Jabber_Error::NO_FEATURES)->trigger();
                 }
                 return $this->_response($features);
             }
             //we are on the first step
             $this->_negotiation = self::AUTH_STARTED;
             // go on with authentication?
             if (isset($features['stream:features'][0]['#']['mechanisms']) || $this->_negotiation == self::AUTH_PROCEED) {
                 return $this->_response($features);
             }
             break;
         case self::AUTH_TYPE_FEATURES:
             // Resource binding after successful authentication
             if ($this->_negotiation == self::AUTH_SUCCESS) {
                 // session required?
                 $this->_session = isset($xml['stream:features'][0]['#']['session']);
                 $this->send("<iq type='set' id='bind_1'><bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'>" . "<resource>" . htmlspecialchars($this->_resource) . '</resource></bind></iq>');
                 return $this->_response($this->wait());
             }
             // Let's use TLS if SSL is not enabled and we can actually use it
             if (!$this->_ssl && $this->_tls && $this->_canUseSSL() && isset($xml['stream:features'][0]['#']['starttls'])) {
                 $this->send("<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>\n");
                 return $this->_response($this->wait());
             }
             // Does the server support SASL authentication?
             // I hope so, because we do (and no other method).
             if (isset($xml['stream:features'][0]['#']['mechanisms'][0]['@']['xmlns']) && $xml['stream:features'][0]['#']['mechanisms'][0]['@']['xmlns'] == 'urn:ietf:params:xml:ns:xmpp-sasl') {
                 // Now decide on method
                 $methods = array();
                 foreach ($xml['stream:features'][0]['#']['mechanisms'][0]['#']['mechanism'] as $value) {
                     $methods[] = $value['#'];
                 }
                 // we prefer DIGEST-MD5
                 // we don't want to use plain authentication (neither does the server usually) if no encryption is in place
                 // http://www.xmpp.org/extensions/attic/jep-0078-1.7.html
                 // The plaintext mechanism SHOULD NOT be used unless the underlying stream is encrypted (using SSL or TLS)
                 // and the client has verified that the server certificate is signed by a trusted certificate authority.
                 if (in_array('DIGEST-MD5', $methods)) {
                     $this->send("<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' mechanism='DIGEST-MD5'/>");
                 } else {
                     if (in_array('PLAIN', $methods) && ($this->_ssl || $this->_negotiation == self::AUTH_PROCEED)) {
                         $this->send("<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' mechanism='PLAIN'>" . base64_encode(chr(0) . $this->_user . '@' . $this->_domain . chr(0) . $this->_pass) . '</auth>');
                     } else {
                         if (in_array('ANONYMOUS', $methods)) {
                             $this->send("<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' mechanism='ANONYMOUS'/>");
                         } else {
                             // not good...
                             //disconnect
                             $this->disconnect();
                             //throw an exception
                             Eden_Jabber_Error::i(Eden_Jabber_Error::NO_AUTH_METHOD)->trigger();
                         }
                     }
                 }
                 return $this->_response($this->wait());
             }
             // ok, this is it. bye.
             //disconnect
             $this->disconnect();
             //throw an exception
             Eden_Jabber_Error::i(Eden_Jabber_Error::NO_SASL)->trigger();
             break;
         case self::AUTH_TYPE_CHALLENGE:
             // continue with authentication...a challenge literally -_-
             $this->_negotiation = self::AUTH_CHALLENGE;
             $decoded = base64_decode($xml['challenge'][0]['#']);
             $decoded = $this->_parseData($decoded);
             if (!isset($decoded['digest-uri'])) {
                 $decoded['digest-uri'] = 'xmpp/' . $this->_host;
             }
             // better generate a cnonce, maybe it's needed
             $decoded['cnonce'] = base64_encode(md5(uniqid(mt_rand(), true)));
             // second challenge?
             if (isset($decoded['rspauth'])) {
                 $this->send("<response xmlns='urn:ietf:params:xml:ns:xmpp-sasl'/>");
             } else {
                 // Make sure we only use 'auth' for qop (relevant for $this->_encryptPass())
                 // If the <response> is choking up on the changed parameter we may need to adjust _encryptPass() directly
                 if (isset($decoded['qop']) && $decoded['qop'] != 'auth' && strpos($decoded['qop'], 'auth') !== false) {
                     $decoded['qop'] = 'auth';
                 }
                 $response = array('username' => $this->_user, 'response' => $this->_encryptPass(array_merge($decoded, array('nc' => '00000001'))), 'charset' => 'utf-8', 'nc' => '00000001', 'qop' => 'auth');
                 // only auth being supported
                 foreach (array('nonce', 'digest-uri', 'realm', 'cnonce') as $key) {
                     if (isset($decoded[$key])) {
                         $response[$key] = $decoded[$key];
                     }
                 }
                 $this->send("<response xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>" . base64_encode($this->_implodeData($response)) . '</response>');
             }
             return $this->_response($this->wait());
         case self::AUTH_TYPE_FAILURE:
             $this->_negotiation = self::AUTH_FAILIURE;
             $this->trigger('failiure');
             //disconnect
             $this->disconnect();
             //throw an exception
             Eden_Jabber_Error::i(Eden_Jabber_Error::SERVER_FAILED)->trigger();
         case self::AUTH_TYPE_PROCEED:
             // continue switching to TLS
             $meta = stream_get_meta_data($this->_connection);
             socket_set_blocking($this->_connection, 1);
             if (!stream_socket_enable_crypto($this->_connection, true, STREAM_CRYPTO_METHOD_TLS_CLIENT)) {
                 //'Error: TLS mode change failed.'
                 Eden_Jabber_Error::i(Eden_Jabber_Error::SERVER_FAILED)->trigger();
             }
             socket_set_blocking($this->_connection, $meta['blocked']);
             $this->_negotiation = self::AUTH_PROCEED;
             // new stream
             $this->send("<?xml version='1.0' encoding='UTF-8' ?" . ">\n");
             $this->send("<stream:stream to='" . $this->_host . "' xmlns='jabber:client' " . "xmlns:stream='http://etherx.jabber.org/streams' version='1.0'>\n");
             return $this->_response($this->wait());
         case self::AUTH_TYPE_SUCCESS:
             // Yay, authentication successful.
             $this->send("<stream:stream to='" . $this->_host . "' xmlns='jabber:client' " . "xmlns:stream='http://etherx.jabber.org/streams' version='1.0'>\n");
             $this->_negotiation = self::AUTH_SUCCESS;
             // we have to wait for another response
             return $this->_response($this->wait());
     }
     return $this;
 }
Ejemplo n.º 2
0
 protected function _authenticate($command, $xml)
 {
     switch ($command) {
         case self::AUTH_TYPE_STREAM:
             if (isset($xml['stream:stream'][0]['#']['stream:features'])) {
                 $features = $xml['stream:stream'][0]['#'];
             } else {
                 $features = $this->wait();
             }
             $second_time = isset($this->_streamId);
             $this->_streamId = $xml['stream:stream'][0]['@']['id'];
             if ($second_time) {
                 if (!sizeof($features)) {
                     Eden_Jabber_Error::i(Eden_Jabber_Error::NO_FEATURES)->trigger();
                 }
                 return $this->_response($features);
             }
             $this->_negotiation = self::AUTH_STARTED;
             if (isset($features['stream:features'][0]['#']['mechanisms']) || $this->_negotiation == self::AUTH_PROCEED) {
                 return $this->_response($features);
             }
             break;
         case self::AUTH_TYPE_FEATURES:
             if ($this->_negotiation == self::AUTH_SUCCESS) {
                 $this->_session = isset($xml['stream:features'][0]['#']['session']);
                 $this->send("<iq type='set' id='bind_1'><bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'>" . "<resource>" . htmlspecialchars($this->_resource) . '</resource></bind></iq>');
                 return $this->_response($this->wait());
             }
             if (!$this->_ssl && $this->_tls && $this->_canUseSSL() && isset($xml['stream:features'][0]['#']['starttls'])) {
                 $this->send("<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>\n");
                 return $this->_response($this->wait());
             }
             if (isset($xml['stream:features'][0]['#']['mechanisms'][0]['@']['xmlns']) && $xml['stream:features'][0]['#']['mechanisms'][0]['@']['xmlns'] == 'urn:ietf:params:xml:ns:xmpp-sasl') {
                 $methods = array();
                 foreach ($xml['stream:features'][0]['#']['mechanisms'][0]['#']['mechanism'] as $value) {
                     $methods[] = $value['#'];
                 }
                 if (in_array('DIGEST-MD5', $methods)) {
                     $this->send("<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' mechanism='DIGEST-MD5'/>");
                 } else {
                     if (in_array('PLAIN', $methods) && ($this->_ssl || $this->_negotiation == self::AUTH_PROCEED)) {
                         $this->send("<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' mechanism='PLAIN'>" . base64_encode(chr(0) . $this->_user . '@' . $this->_domain . chr(0) . $this->_pass) . '</auth>');
                     } else {
                         if (in_array('ANONYMOUS', $methods)) {
                             $this->send("<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' mechanism='ANONYMOUS'/>");
                         } else {
                             $this->disconnect();
                             Eden_Jabber_Error::i(Eden_Jabber_Error::NO_AUTH_METHOD)->trigger();
                         }
                     }
                 }
                 return $this->_response($this->wait());
             }
             $this->disconnect();
             Eden_Jabber_Error::i(Eden_Jabber_Error::NO_SASL)->trigger();
             break;
         case self::AUTH_TYPE_CHALLENGE:
             $this->_negotiation = self::AUTH_CHALLENGE;
             $decoded = base64_decode($xml['challenge'][0]['#']);
             $decoded = $this->_parseData($decoded);
             if (!isset($decoded['digest-uri'])) {
                 $decoded['digest-uri'] = 'xmpp/' . $this->_host;
             }
             $decoded['cnonce'] = base64_encode(md5(uniqid(mt_rand(), true)));
             if (isset($decoded['rspauth'])) {
                 $this->send("<response xmlns='urn:ietf:params:xml:ns:xmpp-sasl'/>");
             } else {
                 if (isset($decoded['qop']) && $decoded['qop'] != 'auth' && strpos($decoded['qop'], 'auth') !== false) {
                     $decoded['qop'] = 'auth';
                 }
                 $response = array('username' => $this->_user, 'response' => $this->_encryptPass(array_merge($decoded, array('nc' => '00000001'))), 'charset' => 'utf-8', 'nc' => '00000001', 'qop' => 'auth');
                 foreach (array('nonce', 'digest-uri', 'realm', 'cnonce') as $key) {
                     if (isset($decoded[$key])) {
                         $response[$key] = $decoded[$key];
                     }
                 }
                 $this->send("<response xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>" . base64_encode($this->_implodeData($response)) . '</response>');
             }
             return $this->_response($this->wait());
         case self::AUTH_TYPE_FAILURE:
             $this->_negotiation = self::AUTH_FAILIURE;
             $this->trigger('failiure');
             $this->disconnect();
             Eden_Jabber_Error::i(Eden_Jabber_Error::SERVER_FAILED)->trigger();
         case self::AUTH_TYPE_PROCEED:
             $meta = stream_get_meta_data($this->_connection);
             socket_set_blocking($this->_connection, 1);
             if (!stream_socket_enable_crypto($this->_connection, true, STREAM_CRYPTO_METHOD_TLS_CLIENT)) {
                 Eden_Jabber_Error::i(Eden_Jabber_Error::SERVER_FAILED)->trigger();
             }
             socket_set_blocking($this->_connection, $meta['blocked']);
             $this->_negotiation = self::AUTH_PROCEED;
             $this->send("<?xml version='1.0' encoding='UTF-8' ?" . ">\n");
             $this->send("<stream:stream to='" . $this->_host . "' xmlns='jabber:client' " . "xmlns:stream='http://etherx.jabber.org/streams' version='1.0'>\n");
             return $this->_response($this->wait());
         case self::AUTH_TYPE_SUCCESS:
             $this->send("<stream:stream to='" . $this->_host . "' xmlns='jabber:client' " . "xmlns:stream='http://etherx.jabber.org/streams' version='1.0'>\n");
             $this->_negotiation = self::AUTH_SUCCESS;
             return $this->_response($this->wait());
     }
     return $this;
 }