support arbitrary SASL mechanisms

This commit is contained in:
lumi 2017-02-24 17:16:51 +01:00
parent 2722e1ebf3
commit 26d2710c1f
4 changed files with 42 additions and 15 deletions

View file

@ -4,15 +4,17 @@ use xmpp::jid::Jid;
use xmpp::client::ClientBuilder; use xmpp::client::ClientBuilder;
use xmpp::plugins::messaging::{MessagingPlugin, MessageEvent}; use xmpp::plugins::messaging::{MessagingPlugin, MessageEvent};
use xmpp::plugins::presence::{PresencePlugin, Show}; use xmpp::plugins::presence::{PresencePlugin, Show};
use xmpp::sasl::mechanisms::Plain;
use std::env; use std::env;
fn main() { fn main() {
let jid: Jid = env::var("JID").unwrap().parse().unwrap(); let jid: Jid = env::var("JID").unwrap().parse().unwrap();
let mut client = ClientBuilder::new(jid).connect().unwrap(); let mut client = ClientBuilder::new(jid.clone()).connect().unwrap();
client.register_plugin(MessagingPlugin::new()); client.register_plugin(MessagingPlugin::new());
client.register_plugin(PresencePlugin::new()); client.register_plugin(PresencePlugin::new());
client.connect_plain(&env::var("PASS").unwrap()).unwrap(); let pass = env::var("PASS").unwrap();
client.connect(&mut Plain::new(jid.node.clone().expect("JID requires a node"), pass)).unwrap();
client.plugin::<PresencePlugin>().set_presence(Show::Available, None).unwrap(); client.plugin::<PresencePlugin>().set_presence(Show::Available, None).unwrap();
loop { loop {
let event = client.next_event().unwrap(); let event = client.next_event().unwrap();

View file

@ -6,7 +6,6 @@ use plugin::{Plugin, PluginProxyBinding};
use event::AbstractEvent; use event::AbstractEvent;
use connection::{Connection, C2S}; use connection::{Connection, C2S};
use sasl::SaslMechanism; use sasl::SaslMechanism;
use sasl::mechanisms::Plain as SaslPlain;
use base64; use base64;
@ -122,8 +121,8 @@ impl Client {
Ok(()) Ok(())
} }
/// Connects using SASL plain authentication. /// Connects using the specified SASL mechanism.
pub fn connect_plain(&mut self, password: &str) -> Result<(), Error> { pub fn connect<S: SaslMechanism>(&mut self, mechanism: &mut S) -> Result<(), Error> {
// TODO: this is very ugly // TODO: this is very ugly
loop { loop {
let e = self.transport.read_event().unwrap(); let e = self.transport.read_event().unwrap();
@ -150,19 +149,36 @@ impl Client {
self.transport.write_element(&elem)?; self.transport.write_element(&elem)?;
} }
else { else {
let name = self.jid.node.clone().expect("JID has no node"); let auth = mechanism.initial();
let mut plain = SaslPlain::new(name, password.to_owned()); let mut elem = Element::builder("auth")
let auth = plain.initial(); .ns(ns::SASL)
let elem = Element::builder("auth") .attr("mechanism", "PLAIN")
.text(base64::encode(&auth)) .build();
.ns(ns::SASL) if !auth.is_empty() {
.attr("mechanism", "PLAIN") elem.append_text_node(base64::encode(&auth));
.build(); }
self.transport.write_element(&elem)?; self.transport.write_element(&elem)?;
did_sasl = true;
} }
} }
else if n.is("challenge", ns::SASL) {
let text = n.text();
let challenge = if text == "" {
Vec::new()
}
else {
base64::decode(&text)?
};
let response = mechanism.response(&challenge);
let mut elem = Element::builder("response")
.ns(ns::SASL)
.build();
if !response.is_empty() {
elem.append_text_node(base64::encode(&response));
}
self.transport.write_element(&elem)?;
}
else if n.is("success", ns::SASL) { else if n.is("success", ns::SASL) {
did_sasl = true;
self.transport.reset_stream(); self.transport.reset_stream();
C2S::init(&mut self.transport, &self.jid.domain, "after_sasl")?; C2S::init(&mut self.transport, &self.jid.domain, "after_sasl")?;
loop { loop {

View file

@ -12,6 +12,8 @@ use xml::writer::Error as EmitterError;
use minidom::Error as MinidomError; use minidom::Error as MinidomError;
use base64::Base64Error;
/// An error which wraps a bunch of errors from different crates and the stdlib. /// An error which wraps a bunch of errors from different crates and the stdlib.
#[derive(Debug)] #[derive(Debug)]
pub enum Error { pub enum Error {
@ -21,6 +23,7 @@ pub enum Error {
HandshakeError(HandshakeError<TcpStream>), HandshakeError(HandshakeError<TcpStream>),
OpenSslErrorStack(ErrorStack), OpenSslErrorStack(ErrorStack),
MinidomError(MinidomError), MinidomError(MinidomError),
Base64Error(Base64Error),
StreamError, StreamError,
EndOfDocument, EndOfDocument,
} }
@ -60,3 +63,9 @@ impl From<MinidomError> for Error {
Error::MinidomError(err) Error::MinidomError(err)
} }
} }
impl From<Base64Error> for Error {
fn from(err: Base64Error) -> Error {
Error::Base64Error(err)
}
}

View file

@ -10,7 +10,7 @@ pub trait SaslMechanism {
} }
/// Creates a response to the SASL challenge. /// Creates a response to the SASL challenge.
fn respond(&mut self, _challenge: &[u8]) -> Vec<u8> { fn response(&mut self, _challenge: &[u8]) -> Vec<u8> {
Vec::new() Vec::new()
} }
} }