class Vines::Stream::SASL
Provides plain (username/password) and external (TLS certificate) SASL
authentication to client and server streams.
Constants
- EMPTY
Public Class Methods
# File lib/vines/stream/sasl.rb, line 11 def initialize(stream) @stream = stream end
Public Instance Methods
Authenticate server-to-server streams, comparing their domain to their SSL certificate.
http://xmpp.org/extensions/xep-0178.html#s2s
encoded - The Base64 encoded remote domain name String sent by the
server stream.
Returns true if the Base64 encoded domain matches the TLS certificate
presented earlier in stream negotiation.
Raises a SaslError
if authentication failed.
# File lib/vines/stream/sasl.rb, line 27 def external_auth(encoded) unless encoded == EMPTY authzid = decode64(encoded) matches_from = (authzid == @stream.remote_domain) raise SaslErrors::InvalidAuthzid unless matches_from end matches_from = @stream.cert_domain_matches?(@stream.remote_domain) matches_from or raise SaslErrors::NotAuthorized end
Authenticate client-to-server streams using a username and password.
encoded - The Base64 encoded jid and password String sent by the
client stream.
Returns the authenticated User
or raises SaslError
if authentication failed.
# File lib/vines/stream/sasl.rb, line 43 def plain_auth(encoded) jid, password = decode_credentials(encoded) user = authenticate(jid, password) user or raise SaslErrors::NotAuthorized end
Private Instance Methods
Storage
backends should not raise errors, but if an unexpected error occurs during authentication, convert it to a temporary-auth-failure.
jid - The user's jid String. password - The String password.
Returns the authenticated User
or nil if authentication failed.
Raises TemoraryAuthFailure if the storage system failed.
# File lib/vines/stream/sasl.rb, line 60 def authenticate(jid, password) log.info("Authenticating user: %s" % jid) @stream.storage.authenticate(jid, password).tap do |user| log.info("Authentication succeeded: %s" % user.jid) if user end rescue => e log.error("Failed to authenticate: #{e.to_s}") raise SaslErrors::TemporaryAuthFailure end
Decode the Base64 encoded string, raising an error for invalid data.
http://tools.ietf.org/html/rfc6120#section-13.9.1
encoded - The Base64 encoded String.
Returns a UTF-8 String.
# File lib/vines/stream/sasl.rb, line 118 def decode64(encoded) Base64.strict_decode64(encoded).tap do |decoded| decoded.force_encoding(Encoding::UTF_8) raise SaslErrors::IncorrectEncoding unless decoded.valid_encoding? end rescue raise SaslErrors::IncorrectEncoding end
Return the JID
and password decoded from the Base64 encoded SASL
PLAIN credentials formatted as authzid0authcid0password.
http://tools.ietf.org/html/rfc6120#section-6.3.8 http://tools.ietf.org/html/rfc4616
encoded - The Base64 encoded String from which to extract jid and password.
Returns an Array of jid String and password String.
# File lib/vines/stream/sasl.rb, line 79 def decode_credentials(encoded) authzid, node, password = decode64(encoded).split("\x00") raise SaslErrors::NotAuthorized if node.nil? || node.empty? || password.nil? || password.empty? jid = JID.new(node, @stream.domain) rescue (raise SaslErrors::NotAuthorized) validate_authzid!(authzid, jid) [jid, password] end
An optional SASL
authzid allows a user to authenticate with one user name and password and then have their connection authorized as a different ID (the authzid). We don't support that, so raise an error if the authzid is provided and different than the authcid.
Most clients don't send an authzid at all because it's optional and not widely supported. However, Strophe and Blather send a bare JID
, in compliance with RFC 6120, but Smack sends just the user name as the authzid. So, take care to handle non-compliant clients here.
http://tools.ietf.org/html/rfc6120#section-6.3.8
authzid - The authzid String (may be nil). jid - The username String.
Returns nothing.
# File lib/vines/stream/sasl.rb, line 103 def validate_authzid!(authzid, jid) return if authzid.nil? || authzid.empty? authzid.downcase! smack = authzid == jid.node compliant = authzid == jid.to_s raise SaslErrors::InvalidAuthzid unless compliant || smack end