diff options
author | Simon McVittie <simon.mcvittie@collabora.co.uk> | 2010-11-19 17:34:28 +0000 |
---|---|---|
committer | Simon McVittie <simon.mcvittie@collabora.co.uk> | 2010-11-23 19:42:53 +0000 |
commit | 24b8eba2452f2d881ab99dc366be0e7c1ba6b0d3 (patch) | |
tree | b09bcba71356b6f31909ef191790114f17320087 | |
parent | 74d160243d6ba936fe7d3449e3e91a3abc5985ed (diff) |
Update SASL code to draft 2
This isn't fully correct yet, but it compiles and passes the existing
tests (with suitable updates).
-rw-r--r-- | extensions/Channel_Interface_SASL_Authentication.xml | 661 | ||||
-rw-r--r-- | extensions/Channel_Interface_Sasl_Authentication.xml | 231 | ||||
-rw-r--r-- | extensions/Channel_Type_Server_Authentication.xml | 143 | ||||
-rw-r--r-- | extensions/Makefile.am | 2 | ||||
-rw-r--r-- | extensions/all.xml | 2 | ||||
-rw-r--r-- | src/server-sasl-channel.c | 438 | ||||
-rw-r--r-- | tests/twisted/constants.py | 15 | ||||
-rw-r--r-- | tests/twisted/sasl/abort.py | 32 | ||||
-rw-r--r-- | tests/twisted/sasl/complex.py | 32 | ||||
-rw-r--r-- | tests/twisted/sasl/jabber_auth.py | 27 | ||||
-rw-r--r-- | tests/twisted/sasl/plain.py | 71 | ||||
-rw-r--r-- | tests/twisted/sasl/saslutil.py | 43 |
12 files changed, 1099 insertions, 598 deletions
diff --git a/extensions/Channel_Interface_SASL_Authentication.xml b/extensions/Channel_Interface_SASL_Authentication.xml new file mode 100644 index 00000000..bb25a6b9 --- /dev/null +++ b/extensions/Channel_Interface_SASL_Authentication.xml @@ -0,0 +1,661 @@ +<?xml version="1.0" ?> +<node name="/Channel_Interface_SASL_Authentication" + xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0"> + <tp:copyright> Copyright © 2010 Collabora Limited </tp:copyright> + <tp:license xmlns="http://www.w3.org/1999/xhtml"> + <p>This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version.</p> + +<p>This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details.</p> + +<p>You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.</p> + </tp:license> + <interface name="org.freedesktop.Telepathy.Channel.Interface.SASLAuthentication.DRAFT2" + tp:causes-havoc="experimental"> + <tp:added version="0.21.UNRELEASED">(draft 2)</tp:added> + <tp:requires interface="org.freedesktop.Telepathy.Channel"/> + <tp:docstring xmlns="http://www.w3.org/1999/xhtml"> + <p>A channel interface for SASL authentication, + as defined by + <a href="http://tools.ietf.org/html/rfc4422">RFC 4422</a>. + When this interface appears on a <tp:dbus-ref + namespace="ofdT.Channel.Type">ServerAuthentication.DRAFT2</tp:dbus-ref> + channel, it represents authentication with the server. In future, + it could also be used to authenticate with secondary services, + or even to authenticate end-to-end connections with contacts.</p> + + <p>In any protocol that requires a password, the connection manager can + use this channel to let a user interface carry out a simple SASL-like + handshake with it, as a way to get the user's credentials + interactively. This can be used to connect to protocols that may + require a password, without requiring that the password is saved in + the <tp:dbus-ref + namespace="ofdT">Account.Parameters</tp:dbus-ref>.</p> + + <p>In some protocols, such as XMPP, authentication with the server + is also carried out using SASL. In these protocols, a channel with this + interface can provide a simple 1:1 mapping of the SASL negotiations + taking place in the protocol, allowing more advanced clients to + perform authentication via SASL mechanisms not known to the + connection manager.</p> + + <tp:rationale> + <p>By providing SASL directly when the protocol supports it, we can + use mechanisms like Kerberos or Google's <code>X-GOOGLE-TOKEN</code> + without specific support in the connection manager.</p> + </tp:rationale> + + <p>For channels managed by a + <tp:dbus-ref namespace="ofdT">ChannelDispatcher</tp:dbus-ref>, + only the channel's <tp:dbus-ref + namespace="ofdT.Client">Handler</tp:dbus-ref> may call the + methods on this interface. Other clients MAY observe the + authentication process by watching its signals and properties.</p> + + <tp:rationale> + <p>There can only be one Handler, which is a good fit for SASL's + 1-1 conversation between a client and a server.</p> + </tp:rationale> + </tp:docstring> + + <tp:simple-type name="SASL_Mechanism" type="s" + array-name="SASL_Mechanism_List"> + <tp:docstring xmlns="http://www.w3.org/1999/xhtml"> + <p>A SASL mechanism, as defined by + <a href="http://tools.ietf.org/html/rfc4422">RFC 4422</a> + and registered in + <a href="http://www.iana.org/assignments/sasl-mechanisms">the + IANA registry of SASL mechanisms</a>, or an unregistered + SASL mechanism such as <code>X-GOOGLE-TOKEN</code> used in the + same contexts.</p> + + <p>As a special case, pseudo-mechanisms starting with + <code>X-TELEPATHY-</code> are defined by this specification. + Use of these pseudo-mechanisms indicates that the user's credentials + are to be passed to the connection manager, which will then use + them for authentication with the service, either by implementing + the client side of some SASL mechanisms itself or by using a + non-SASL protocol. The only such pseudo-mechanism currently + defined is <code>X-TELEPATHY-PASSWORD</code>.</p> + + <p>The <code>X-TELEPATHY-PASSWORD</code> mechanism is extremely + simple:</p> + + <ul> + <li>The client MUST call + <tp:member-ref>StartMechanismWithData</tp:member-ref>, with + Initial_Data set to the password encoded in UTF-8. + For simplicity, calling <tp:member-ref>StartMechanism</tp:member-ref> + followed by calling <tp:member-ref>Respond</tp:member-ref> is not + allowed in this mechanism.</li> + + <li>The connection manager uses the password, together with + authentication details from the Connection parameters, to + authenticate itself to the server.</li> + + <li>When the connection manager finishes its attempt to authenticate + to the server, the channel's state changes to + either SASL_Status_Server_Succeeded or + SASL_Status_Server_Failed as appropriate.</li> + </ul> + </tp:docstring> + </tp:simple-type> + + <property name="AvailableMechanisms" + tp:name-for-bindings="Available_Mechanisms" + type="as" tp:type="SASL_Mechanism[]" + access="read" tp:immutable="yes"> + <tp:docstring xmlns="http://www.w3.org/1999/xhtml"> + <p>The SASL mechanisms as offered by the server, plus any + pseudo-SASL mechanisms supported by the connection manager for + credentials transfer. For instance, in a protocol that + natively uses SASL (like XMPP), this might be + <code>[ "X-TELEPATHY-PASSWORD", "PLAIN", "DIGEST-MD5", + "SCRAM-SHA-1" ]</code>.</p> + + <p>To make it possible to implement a very simple password-querying + user interface without knowledge of any particular SASL mechanism, + implementations of this interface MUST implement the + pseudo-mechanism <code>X-TELEPATHY-PASSWORD</code>.</p> + </tp:docstring> + </property> + + <property name="HasInitialData" tp:name-for-bindings="Has_Initial_Data" + type="b" access="read" tp:immutable="yes"> + <tp:docstring> + If true, <tp:member-ref>StartMechanismWithData</tp:member-ref> + can be expected to work (this is the case in most, but not all, + protocols). If false, <tp:member-ref>StartMechanism</tp:member-ref> + must be used instead. + </tp:docstring> + </property> + + <property name="CanTryAgain" tp:name-for-bindings="Can_Try_Again" + type="b" access="read" tp:immutable="yes"> + <tp:docstring xmlns="http://www.w3.org/1999/xhtml"> + <p>If true, <tp:member-ref>StartMechanism</tp:member-ref> and (if + supported) <tp:member-ref>StartMechanismWithData</tp:member-ref> + can be expected to work when in one of the Failed states. If + false, the only thing you can do after failure is to close the + channel.</p> + + <tp:rationale> + <p>Retrying isn't required to work, although some protocols and + implementations allow it.</p> + </tp:rationale> + </tp:docstring> + </property> + + <property name="Encrypted" + tp:name-for-bindings="Encrypted" type="b" + access="read" tp:immutable="yes"> + <tp:docstring xmlns="http://www.w3.org/1999/xhtml"> + <p>True if this authentication process occurs over an encrypted + connection. This <strong>does not</strong> imply that steps have + been taken to avoid man-in-the-middle attacks.</p> + + <tp:rationale> + <p>For future support for <a + href="http://tools.ietf.org/html/rfc5056">RFC 5056 Channel + Binding</a> it is desirable to be able to use some SASL + mechanisms over an encrypted connection to an unverified peer, + which can prove that it is the desired destination during + the SASL negotiation.</p> + </tp:rationale> + + <p>Clients MAY use the combination of this property and + <tp:member-ref>Verified</tp:member-ref> to decide whether the + <code>PLAIN</code> mechanism is acceptable, for instance.</p> + </tp:docstring> + </property> + + <property name="Verified" + tp:name-for-bindings="Verified" type="b" + access="read" tp:immutable="yes"> + <tp:docstring xmlns="http://www.w3.org/1999/xhtml"> + <p>True if this authentication process occurs over a connection + that is protected against tampering, and has been verified to + be with the desired destination: for instance, one where TLS + was previously negotiated, and the TLS certificate has been + verified against a configured certificate authority or + accepted by the user.</p> + + <p>Clients MAY use the combination of this property and + <tp:member-ref>Encrypted</tp:member-ref> to decide whether the + <code>PLAIN</code> mechanism is acceptable, for instance.</p> + </tp:docstring> + </property> + + <property type="u" tp:type="SASL_Status" access="read" + name="SASLStatus" tp:name-for-bindings="SASL_Status"> + <tp:docstring xmlns="http://www.w3.org/1999/xhtml"> + The current status of this channel. + Change notification is via the + <tp:member-ref>SASLStatusChanged</tp:member-ref> signal. + </tp:docstring> + </property> + + <property type="s" tp:type="DBus_Error_Name" access="read" + name="SASLError" tp:name-for-bindings="SASL_Error"> + <tp:docstring xmlns="http://www.w3.org/1999/xhtml"> + <p>The reason for the <tp:member-ref>SASLStatus</tp:member-ref>, or + an empty string if the state is neither + Server_Failed nor Client_Failed.</p> + + <p>In particular, an ordinary authentication failure (as would + be produced for an incorrect password) SHOULD be represented by + <tp:error-ref>AuthenticationFailed</tp:error-ref>, + cancellation by the user's request SHOULD be represented + by <tp:error-ref>Cancelled</tp:error-ref>, and + cancellation by a local process due to inconsistent or invalid + challenges from the server SHOULD be represented by + <tp:error-ref>ServiceConfused</tp:error-ref>.</p> + + <p>If this interface appears on a <tp:dbus-ref + namespace="ofdT.Channel.Type">ServerAuthentication.DRAFT2</tp:dbus-ref> + channel, and connection to the server fails with an authentication + failure, this error code SHOULD be copied into the + <tp:dbus-ref + namespace="ofdT">Connection.ConnectionError</tp:dbus-ref> + signal.</p> + </tp:docstring> + </property> + + <property name="SASLErrorDetails" + tp:name-for-bindings="SASL_Error_Details" + access="read" type="a{sv}" tp:type="String_Variant_Map"> + <tp:docstring xmlns="http://www.w3.org/1999/xhtml"> + <p>If <tp:member-ref>SASLError</tp:member-ref> is non-empty, + any additional information about the last + disconnection; otherwise, the empty map. The keys and values are + the same as for the second argument of + <tp:dbus-ref + namespace="ofdT">Connection.ConnectionError</tp:dbus-ref>.</p> + + <p>If this interface appears on a <tp:dbus-ref + namespace="ofdT.Channel.Type">ServerAuthentication.DRAFT2</tp:dbus-ref> + channel, and connection to the server fails with an authentication + failure, these details SHOULD be copied into the + <tp:dbus-ref + namespace="ofdT">Connection.ConnectionError</tp:dbus-ref> + signal.</p> + </tp:docstring> + </property> + + <property name="AuthorizationIdentity" + tp:name-for-bindings="Authorization_Identity" + type="s" access="read" tp:immutable="yes"> + <tp:docstring xmlns="http://www.w3.org/1999/xhtml"> + <p>The identity for which authorization is being attempted, + typically the 'account' from the <tp:dbus-ref + namespace="ofdT.ConnectionManager">RequestConnection</tp:dbus-ref> + parameters, normalized and formatted according to the + conventions used for SASL in this protocol.</p> + + <tp:rationale> + <p>The normalization used for SASL might not be the same + normalization used elsewhere: for instance, in a protocol + with email-like identifiers such as XMPP or SIP, the user + "juliet@example.com" might have to authenticate to the + example.com server via SASL PLAIN as "juliet".</p> + </tp:rationale> + + <p>This is usually achieved by using the authorization identity for + authentication, but an advanced Handler could offer the option + to authenticate under a different identity.</p> + + <p>The terminology used here is that the authorization identity + is who you want to act as, and the authentication identity is + used to prove that you may do so. For instance, if Juliet is + authorized to access a role account, "sysadmin@example.com", + and act on its behalf, it might be possible to authenticate + as "juliet@example.com" with her own password, but request to + be authorized as "sysadmin@example.com" instead of her own + account. See + <a href="http://tools.ietf.org/html/rfc4422#section-3.4.1">RFC + 4422 §3.4.1</a> for more details.</p> + + <tp:rationale> + <p>In SASL the authorization identity is normally guessed from + the authentication identity, but the information available + to the connection manager is the identity for which + authorization is required, such as the desired JID in XMPP, + so that's what we signal to UIs; it's up to the UI to + choose whether to authenticate as the authorization identity + or some other identity.</p> + + <p>As a concrete example, the "sysadmin" XMPP account mentioned + above would have <code>{ 'account': 'sysadmin@example.com' }</code> + in its Parameters, and this property would also be + 'sysadmin@example.com'. A simple Handler would + merely prompt for sysadmin@example.com's password, + and use that JID as both the authorization and authentication + identity, which might result in SASL PLAIN authentication with the + initial response + '\000sysadmin@example.com\000root'.</p> + + <p>A more advanced Handler might also ask for an authentication + identity, defaulting to 'sysadmin@example.com'; if Juliet + provided authentication identity 'juliet@example.com' and + password 'romeo', the Handler might perform SASL PLAIN + authentication using the initial response + 'sysadmin@example.com\000juliet@example.com\000romeo'.</p> + </tp:rationale> + </tp:docstring> + </property> + + <property name="DefaultRealm" tp:name-for-bindings="Default_Realm" + type="s" access="read" tp:immutable="yes"> + <tp:docstring xmlns="http://www.w3.org/1999/xhtml"> + <p>The default realm (as defined in + <a href="http://tools.ietf.org/html/rfc2831#section-2.1.1">RFC + 2831</a>) to use for authentication, if the server does not + supply one.</p> + + <tp:rationale> + <p>The server is not required to provide a realm; if it doesn't, + the client is expected to ask the user or provide a sensible + default, typically the requested DNS name of the server. + In some implementations of <code>DIGEST-MD5</code>, the + server does not specify a realm, but expects that the client + will choose a particular default, and authentication will + fail if the client's default is different. Connection + managers for protocols where this occurs are more easily able + to work around these implementations than a generic client + would be.</p> + </tp:rationale> + </tp:docstring> + </property> + + <property name="SASLContext" tp:name-for-bindings="SASL_Context" + type="a{sv}" tp:type="String_Variant_Map" access="read" + tp:immutable="yes"> + <tp:docstring xmlns="http://www.w3.org/1999/xhtml"> + <p>Additional protocol- or mechanism-specific context for the + authentication. Only one well-known key is currently defined:</p> + + <dl> + <dt>jabber-stream-id (string)</dt> + <dd>The <code>id</code> attribute of the XMPP <code>stream</code> + element, as used in <a + href="http://xmpp.org/extensions/xep-0078.html">the digest + mechanism historically used in Jabber</a>.</dd> + </dl> + </tp:docstring> + </property> + + <method name="StartMechanism" tp:name-for-bindings="Start_Mechanism"> + <arg direction="in" name="Mechanism" type="s" tp:type="SASL_Mechanism"> + <tp:docstring> + The chosen mechanism. + </tp:docstring> + </arg> + <tp:docstring xmlns="http://www.w3.org/1999/xhtml"> + <p>Start an authentication try using <var>Mechanism</var>, without + sending initial data (an "initial response" as defined in RFC + 4422).</p> + + <tp:rationale> + <p>This method is appropriate for mechanisms where the client + cannot send anything until it receives a challenge from the + server, such as <code>DIGEST-MD5</code>.</p> + </tp:rationale> + </tp:docstring> + + <tp:possible-errors> + <tp:error name="org.freedesktop.Telepathy.Error.NotAvailable"> + <tp:docstring> + The channel is not in a state where starting authentication makes + sense (i.e. SASL_Status_Not_Started, or (if + <tp:member-ref>CanTryAgain</tp:member-ref> is true) + SASL_Status_Server_Failed or + SASL_Status_Client_Failed). You should call + <tp:member-ref>AbortSASL</tp:member-ref> and wait for + SASL_Status_Client_Failed before starting another attempt. + </tp:docstring> + </tp:error> + <tp:error name="org.freedesktop.Telepathy.Error.NetworkError"/> + <tp:error name="org.freedesktop.Telepathy.Error.NotImplemented"> + <tp:docstring> + The server or connection manager doesn't implement the given + SASL mechanism. Choose a SASL mechanism from + <tp:member-ref>AvailableMechanisms</tp:member-ref>, or abort + authentication if none of them are suitable. + </tp:docstring> + </tp:error> + </tp:possible-errors> + </method> + + <method name="StartMechanismWithData" + tp:name-for-bindings="Start_Mechanism_With_Data"> + <arg direction="in" name="Mechanism" type="s" tp:type="SASL_Mechanism"> + <tp:docstring> + The chosen mechanism. + </tp:docstring> + </arg> + <arg direction="in" name="Initial_Data" type="ay"> + <tp:docstring> + Initial data (an "initial response" in RFC 4422's terminology) to send + with the mechanism. + </tp:docstring> + </arg> + <tp:docstring xmlns="http://www.w3.org/1999/xhtml"> + <p>Start an authentication try using <var>Mechanism</var>, and send + <var>Initial_Data</var> as the "initial response" defined in + <a href="http://tools.ietf.org/html/rfc4422#section-3.3">RFC 4422 + §3.3</a>.</p> + + <p>If the underlying protocol does not make it possible to send + initial data, this method will fail.</p> + + <tp:rationale> + <p>This method is appropriate for mechanisms where the client may + send data first, such as <code>PLAIN</code>.</p> + + <p>Having two methods allows any mechanism where it makes a difference + to distinguish between the absence of an initial response + (<tp:member-ref>StartMechanism</tp:member-ref>) and a zero-byte + initial response (StartMechanismWithData, with Initial_Data + empty).</p> + </tp:rationale> + </tp:docstring> + + <tp:possible-errors> + <tp:error name="org.freedesktop.Telepathy.Error.NotAvailable"> + <tp:docstring> + The channel is not in a state where starting authentication makes + sense (i.e. SASL_Status_Not_Started, or (if + <tp:member-ref>CanTryAgain</tp:member-ref> is true) + SASL_Status_Server_Failed or + SASL_Status_Client_Failed). You should call + <tp:member-ref>AbortSASL</tp:member-ref> and wait for + SASL_Status_Client_Failed before starting another attempt. + </tp:docstring> + </tp:error> + <tp:error name="org.freedesktop.Telepathy.Error.NetworkError"/> + <tp:error name="org.freedesktop.Telepathy.Error.NotImplemented"> + <tp:docstring> + The server or connection manager doesn't implement the given + SASL mechanism (choose one from + <tp:member-ref>AvailableMechanisms</tp:member-ref>, or abort + authentication if none of them are suitable), or doesn't allow + initial data to be sent (as indicated by + <tp:member-ref>HasInitialData</tp:member-ref>; call + <tp:member-ref>StartMechanism</tp:member-ref> instead). + </tp:docstring> + </tp:error> + </tp:possible-errors> + </method> + + <method name="Respond" tp:name-for-bindings="Respond"> + <arg direction="in" name="Response_Data" type="ay"> + <tp:docstring> + The response data. + </tp:docstring> + </arg> + <tp:docstring xmlns="http://www.w3.org/1999/xhtml"> + <p>Send a response to the the last challenge received via + <tp:member-ref>NewChallenge</tp:member-ref>.</p> + </tp:docstring> + + <tp:possible-errors> + <tp:error name="org.freedesktop.Telepathy.Error.NotAvailable"> + <tp:docstring> + Either the state is not In_Progress, or no challenge has been + received yet, or you have already responded to the last challenge. + </tp:docstring> + </tp:error> + <tp:error name="org.freedesktop.Telepathy.Error.NetworkError"/> + </tp:possible-errors> + </method> + + <method name="AcceptSASL" tp:name-for-bindings="Accept_SASL"> + <tp:docstring xmlns="http://www.w3.org/1999/xhtml"> + <p>If the channel's status is SASL_Status_Server_Succeeded, + this method confirms successful authentication and advances + the status of the channel to SASL_Status_Succeeded.</p> + + <p>If the channel's status is SASL_Status_In_Progress, calling this + method indicates that the last + <tp:member-ref>NewChallenge</tp:member-ref> signal was in fact + additional data sent after a successful SASL negotiation, and + declares that from the client's point of view, authentication + was successful. This advances the state of the channel to + SASL_Status_Client_Accepted.</p> + + <p>In mechanisms where the server authenticates itself to the client, + calling this method indicates that the client considers this to have + been successful. In the case of <tp:dbus-ref + namespace="ofdT.Channel.Type">ServerAuthentication.DRAFT2</tp:dbus-ref> + channels, this means that the connection manager MAY continue to + connect, and MAY advance the <tp:dbus-ref + namespace="ofdT">Connection.Status</tp:dbus-ref> to Connected.</p> + </tp:docstring> + + <tp:possible-errors> + <tp:error name="org.freedesktop.Telepathy.Error.NotAvailable"> + <tp:docstring> + Either the state is neither In_Progress nor Server_Succeeded, or no + challenge has been received yet, or you have already responded to + the last challenge. + </tp:docstring> + </tp:error> + <tp:error name="org.freedesktop.Telepathy.Error.NetworkError"/> + </tp:possible-errors> + </method> + + <method name="AbortSASL" tp:name-for-bindings="Abort_SASL"> + <arg direction="in" name="Reason" type="u" tp:type="SASL_Abort_Reason"> + <tp:docstring> + Reason for abort. + </tp:docstring> + </arg> + <arg direction="in" name="Debug_Message" type="s"> + <tp:docstring> + Debug message for abort. + </tp:docstring> + </arg> + <tp:docstring xmlns="http://www.w3.org/1999/xhtml"> + <p>Abort the current authentication try.</p> + + <p>If the current status is SASL_Status_Server_Failed or + SASL_Status_Client_Failed, this method returns successfully, but has + no further effect. Otherwise, it changes the channel's state to + SASL_Status_Client_Failed, with an appropriate error name and + reason code.</p> + </tp:docstring> + </method> + + <signal name="SASLStatusChanged" tp:name-for-bindings="SASL_Status_Changed"> + <tp:docstring> + Emitted when the status of the channel changes. + </tp:docstring> + <arg type="u" tp:type="SASL_Status" name="Status"> + <tp:docstring> + The new value of <tp:member-ref>SASLStatus</tp:member-ref>. + </tp:docstring> + </arg> + <arg type="s" tp:type="DBus_Error_Name" name="Reason"> + <tp:docstring> + The new value of <tp:member-ref>SASLError</tp:member-ref>. + </tp:docstring> + </arg> + <arg type="a{sv}" tp:type="String_Variant_Map" name="Details"> + <tp:docstring> + The new value of <tp:member-ref>SASLErrorDetails</tp:member-ref>. + </tp:docstring> + </arg> + </signal> + + <signal name="NewChallenge" tp:name-for-bindings="New_Challenge"> + <tp:docstring xmlns="http://www.w3.org/1999/xhtml"> + <p>Emitted when a new challenge is received from the server, or when + a message indicating successful authentication and containing + additional data is received from the server.</p> + + <p>When the channel's handler is ready to proceed, it should respond + to the challenge by calling <tp:member-ref>Respond</tp:member-ref>, + or respond to the additional data by calling + <tp:member-ref>AcceptSASL</tp:member-ref>. Alternatively, it may call + <tp:member-ref>AbortSASL</tp:member-ref> to abort authentication.</p> + </tp:docstring> + <arg name="Challenge_Data" type="ay"> + <tp:docstring> + The challenge data or additional data from the server. + </tp:docstring> + </arg> + </signal> + + <tp:enum name="SASL_Abort_Reason" type="u"> + <tp:docstring xmlns="http://www.w3.org/1999/xhtml"> + <p>A reason why SASL authentication was aborted by the client.</p> + </tp:docstring> + + <tp:enumvalue suffix="Invalid_Challenge" value="0"> + <tp:docstring> + The server sent an invalid challenge or data. + </tp:docstring> + </tp:enumvalue> + <tp:enumvalue suffix="User_Abort" value="1"> + <tp:docstring> + The user aborted the authentication. + </tp:docstring> + </tp:enumvalue> + </tp:enum> + + <tp:enum name="SASL_Status" type="u"> + <tp:enumvalue suffix="Not_Started" value="0"> + <tp:docstring> + The initial state. The Handler SHOULD either + call <tp:member-ref>AbortSASL</tp:member-ref>, or connect to the + <tp:member-ref>NewChallenge</tp:member-ref> signal then call + <tp:member-ref>StartMechanism</tp:member-ref> or + <tp:member-ref>StartMechanismWithData</tp:member-ref>. + </tp:docstring> + </tp:enumvalue> + <tp:enumvalue suffix="In_Progress" value="1"> + <tp:docstring> + The challenge/response exchange is in progress. The Handler SHOULD + call either <tp:member-ref>Respond</tp:member-ref> or + <tp:member-ref>AcceptSASL</tp:member-ref> exactly once per emission + of <tp:member-ref>NewChallenge</tp:member-ref>, or call + <tp:member-ref>AbortSASL</tp:member-ref> at any time. + </tp:docstring> + </tp:enumvalue> + <tp:enumvalue suffix="Server_Succeeded" value="2"> + <tp:docstring> + The server has indicated successful authentication, and the + connection manager is waiting for confirmation from the Handler. + The Handler must call either <tp:member-ref>AcceptSASL</tp:member-ref> or + <tp:member-ref>AbortSASL</tp:member-ref> to indicate whether it + considers authentication to have been successful. + </tp:docstring> + </tp:enumvalue> + <tp:enumvalue suffix="Client_Accepted" value="3"> + <tp:docstring> + The Handler has indicated successful authentication, and the + connection manager is waiting for confirmation from the server. + The state will progress to either Succeeded or Server_Failed when + confirmation is received. + </tp:docstring> + </tp:enumvalue> + <tp:enumvalue suffix="Succeeded" value="4"> + <tp:docstring> + Everyone is happy (the server sent success, and the client has called + <tp:member-ref>AcceptSASL</tp:member-ref>). Connection to the server + will proceed as soon as this state is reached. The Handler SHOULD + call <tp:dbus-ref + namespace="org.freedesktop.Telepathy.Channel">Close</tp:dbus-ref> + to close the channel. + </tp:docstring> + </tp:enumvalue> + <tp:enumvalue suffix="Server_Failed" value="5"> + <tp:docstring> + The server has indicated an authentication failure. + If <tp:member-ref>CanTryAgain</tp:member-ref> is true, + the client may try to authenticate again, by calling + <tp:member-ref>StartMechanism</tp:member-ref> or + <tp:member-ref>StartMechanismWithData</tp:member-ref> again. + Otherwise, it should give up completely, by calling <tp:dbus-ref + namespace="org.freedesktop.Telepathy.Channel">Close</tp:dbus-ref> + on the channel. + </tp:docstring> + </tp:enumvalue> + <tp:enumvalue suffix="Client_Failed" value="6"> + <tp:docstring> + The client has indicated an authentication failure. The + possible actions are the same as for Server_Failed. + </tp:docstring> + </tp:enumvalue> + </tp:enum> + + </interface> +</node> +<!-- vim:set sw=2 sts=2 et ft=xml: --> diff --git a/extensions/Channel_Interface_Sasl_Authentication.xml b/extensions/Channel_Interface_Sasl_Authentication.xml deleted file mode 100644 index 6ae4e1da..00000000 --- a/extensions/Channel_Interface_Sasl_Authentication.xml +++ /dev/null @@ -1,231 +0,0 @@ -<?xml version="1.0" ?> -<node name="/Channel_Interface_Sasl_Authentication" - xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0"> - <tp:copyright> Copyright © 2010 Collabora Limited </tp:copyright> - <tp:license xmlns="http://www.w3.org/1999/xhtml"> - <p>This library is free software; you can redistribute it and/or -modify it under the terms of the GNU Lesser General Public -License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version.</p> - -<p>This library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -Lesser General Public License for more details.</p> - -<p>You should have received a copy of the GNU Lesser General Public -License along with this library; if not, write to the Free Software -Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.</p> - </tp:license> - <interface name="org.freedesktop.Telepathy.Channel.Interface.SaslAuthentication.DRAFT" tp:causes-havoc="experimental"> - <tp:requires interface="org.freedesktop.Telepathy.Channel"/> - <tp:requires interface="org.freedesktop.Telepathy.Channel.Type.Server.Authentication.DRAFT"/> - <tp:docstring xmlns="http://www.w3.org/1999/xhtml"> - A channel interface for SASL authentication. - </tp:docstring> - - - <property name="AvailableMechanisms" - tp:name-for-bindings="Available_Mechanisms" - type="as" access="read"> - <tp:docstring> - Example: [ "PLAIN", "DIGEST-MD5", "SCRAM-SHA-1" ] - The SASL mechanisms as offered by the server. - </tp:docstring> - </property> - - <property name="Secure" - tp:name-for-bindings="Secure" type="b" access="read"> - <tp:docstring> - Is this exchange secure (ie. TLS)? Good to know if you need to take - a calculated risk with plaintext. - </tp:docstring> - </property> - - <property name="CurrentChallenge" tp:name-for-bindings="Current_Challenge" - type="ay" access="read"> - <tp:docstring> - The current challenge from the server. change notification via - <tp:member-ref>NewChallenge</tp:member-ref>. The handler either needs - to respond by calling <tp:member-ref>Respond</tp:member-ref> - (if it needs to send reply data), <tp:member-ref>Accept</tp:member-ref> - (If the challenge contained final data) or - <tp:member-ref>Abort</tp:member-ref> (in case of errors). - </tp:docstring> - </property> - - <property name="CurrentState" tp:name-for-bindings="Current_State" - type="(uss)" tp:type="Sasl_State" access="read"> - <tp:docstring> - The current state of the authentication. - Change notification via <tp:member-ref>StateChanged</tp:member-ref> - signal. - </tp:docstring> - </property> - - <method name="StartMechanism" tp:name-for-bindings="Start_Mechanism"> - <arg direction="in" name="Mechanism" type="s"> - <tp:docstring> - The chosen mechanism. - </tp:docstring> - </arg> - <arg direction="in" name="InitialData" type="ay"> - <tp:docstring> - Initial data to send with the mechanism. - </tp:docstring> - </arg> - <tp:docstring> - Start an authentication try using Mechanism. If the choosen SASL - mechanism is client-first then the first data must be passed in - InitialData, otherwise InitialData must be an empty array. - </tp:docstring> - </method> - - <method name="Respond" tp:name-for-bindings="Respond"> - <arg direction="in" name="Response_Data" type="ay"> - <tp:docstring> - The response data. - </tp:docstring> - </arg> - <tp:docstring> - Our response to the CurrentChallenge if required. - </tp:docstring> - </method> - - <method name="Accept" tp:name-for-bindings="Accept"> - <tp:docstring> - Handler accepts the authentication as finished. Can be called - whenever the Handler considered the authentication process to - be (successfully) finished from its part. - </tp:docstring> - </method> - - <method name="Abort" tp:name-for-bindings="Abort"> - <arg direction="in" name="Reason" type="u" tp:type="Abort_Reason"> - <tp:docstring> - Reason for abort. - </tp:docstring> - </arg> - <arg direction="in" name="Debug_Message" type="s"> - <tp:docstring> - Debug message for abort. - </tp:docstring> - </arg> - <tp:docstring> - Abort the current authentication try. - </tp:docstring> - </method> - - <signal name="StateChanged" tp:name-for-bindings="State_Changed"> - <tp:docstring> - Notifies of <tp:member-ref>CurrentState</tp:member-ref> changing - </tp:docstring> - <arg type="u" tp:type="Sasl_Status" name="Status"> - <tp:docstring> - The status of the state. - </tp:docstring> - </arg> - <arg type="s" tp:type="DBus_Error_Name" name="Reason"> - <tp:docstring> - The reason for the state. - </tp:docstring> - </arg> - <arg type="s" name="DebugMessage"> - <tp:docstring> - A non-localized debug message. - </tp:docstring> - </arg> - </signal> - - <signal name="NewChallenge" tp:name-for-bindings="New_Challenge"> - <tp:docstring> - Recieved a new challenge from the server. - </tp:docstring> - <arg name="ChallengeData" type="ay"> - <tp:docstring> - The challenge data from the server. - </tp:docstring> - </arg> - </signal> - - <tp:enum name="Abort_Reason" type="u"> - <tp:enumvalue suffix="Invalid_Challenge" value="0"> - <tp:docstring> - Server sent an invalid challenge or data. - </tp:docstring> - </tp:enumvalue> - <tp:enumvalue suffix="User_Abort" value="1"> - <tp:docstring> - User aborted the authentication. - </tp:docstring> - </tp:enumvalue> - </tp:enum> - - <tp:enum name="Sasl_Status" type="u"> - <tp:enumvalue suffix="Not_Started" value="0"> - <tp:docstring> - Need to call <tp:member-ref>StartMechanism</tp:member-ref> to start. - </tp:docstring> - </tp:enumvalue> - <tp:enumvalue suffix="In_Progress" value="1"> - <tp:docstring> - Challenge/Response cycle in progress - </tp:docstring> - </tp:enumvalue> - <tp:enumvalue suffix="Server_Succeeded" value="2"> - <tp:docstring> - Server indicated successful authentication, handler needs to - Accept or Abort. - </tp:docstring> - </tp:enumvalue> - <tp:enumvalue suffix="Client_Accepted" value="3"> - <tp:docstring> - Handler indicates that from its perspective the - authentication has successfully finished. - </tp:docstring> - </tp:enumvalue> - <tp:enumvalue suffix="Succeeded" value="4"> - <tp:docstring> - Everyone is happy (server sent success, client sent Accept), up to the handler to close the channel. - </tp:docstring> - </tp:enumvalue> - <tp:enumvalue suffix="Server_Failed" value="5"> - <tp:docstring> - Server indicated an authentication failure, - Authentication can be restarted by calling - StartMechanism again or completely aborted by Closing - the channel. - </tp:docstring> - </tp:enumvalue> - <tp:enumvalue suffix="Client_Failed" value="6"> - <tp:docstring> - Client indicated an authentication failure, - Authentication can be restarted by calling - StartMechanism again or completely aborted by Closing - the channel. - </tp:docstring> - </tp:enumvalue> - </tp:enum> - - <tp:struct name="Sasl_State"> - <tp:member type="u" tp:type="Sasl_Status" name="Status"> - <tp:docstring> - The status of the state. - </tp:docstring> - </tp:member> - - <tp:member type="s" tp:type="DBus_Error_Name" name="Reason"> - <tp:docstring> - The reason for the state. - </tp:docstring> - </tp:member> - - <tp:member type="s" name="DebugMessage"> - <tp:docstring> - A non-localized debug message. - </tp:docstring> - </tp:member> - </tp:struct> - </interface> -</node> -<!-- vim:set sw=2 sts=2 et ft=xml: --> diff --git a/extensions/Channel_Type_Server_Authentication.xml b/extensions/Channel_Type_Server_Authentication.xml index bc9ca150..ae5fd061 100644 --- a/extensions/Channel_Type_Server_Authentication.xml +++ b/extensions/Channel_Type_Server_Authentication.xml @@ -16,77 +16,96 @@ Lesser General Public License for more details.</p> License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.</p> </tp:license> - <interface name="org.freedesktop.Telepathy.Channel.Type.ServerAuthentication.DRAFT" tp:causes-havoc="experimental"> + <interface name="org.freedesktop.Telepathy.Channel.Type.ServerAuthentication.DRAFT2" + tp:causes-havoc="experimental"> + <tp:added version="0.21.UNRELEASED">(draft 2)</tp:added> <tp:requires interface="org.freedesktop.Telepathy.Channel"/> <tp:docstring xmlns="http://www.w3.org/1999/xhtml"> - An interface for SASL authentication. - </tp:docstring> + <p>The type for a channel representing an authentication step with the + server. The actual authentication functionality is implemented by + the additional interface named in + <tp:member-ref>AuthenticationMethod</tp:member-ref>, + such as <tp:dbus-ref namespace="ofdT" + >Channel.Interface.SASLAuthentication.DRAFT2</tp:dbus-ref>.</p> - <property name="AuthenticationInformation" - tp:name-for-bindings="Authentication_Information" - type="a{sv}" access="read"> - <tp:docstring> - Dictionary of information given by the CM which can be used by the - handler for authentication. - </tp:docstring> - </property> + <p>Future authentication steps also supported by this channel type might + include solving a captcha and/or agreeing to an EULA or terms-of-use + document; each of these would be represented by a channel with this + type, but a different + <tp:member-ref>AuthenticationMethod</tp:member-ref>.</p> - <property name="AuthenticationMethod" - tp:name-for-bindings="Authentication_Method" - type="u" tp:type="Authentication_Type" access="read"> - <tp:docstring> - This property defines the Method used for the current - authentication step. The method also defines which Interfaces - the channel implements. For exmaple if for the SASL method the - SaslAuthentication interface needs to be implemented. - </tp:docstring> - </property> + <p>Channels of this type SHOULD be signalled and dispatched while the + <tp:dbus-ref namespace="ofdT">Connection</tp:dbus-ref> owning them is in + the CONNECTING state. Normally, only one channel of this type will + exist on a given Connection; if there is more than one, the handler + must complete authentication with each of them in turn.</p> - <tp:enum name="Authentication_Type" type="u"> - <tp:enumvalue suffix="Sasl" value="0"> - <tp:docstring> - SASL authentication. - </tp:docstring> - </tp:enumvalue> - <tp:enumvalue suffix="Captcha" value="1"> - <tp:docstring> - Captcha authentication. - </tp:docstring> - </tp:enumvalue> - </tp:enum> + <p>Channels of this type cannot be requested with methods such as + <tp:dbus-ref + namespace="ofdT.Connection.Interface.Requests">CreateChannel</tp:dbus-ref>. + They always have <tp:dbus-ref + namespace="ofdT.Channel">Requested</tp:dbus-ref> = False, + <tp:dbus-ref + namespace="ofdT.Channel">TargetHandleType</tp:dbus-ref> = None + and <tp:dbus-ref + namespace="org.freedesktop.Telepathy.Channel">TargetHandle</tp:dbus-ref> + = 0.</p> - <tp:mapping name="AuthDetails" array-name="AuthDetails_List"> - <tp:docstring> - An extensible map representing details provided by the server for - authentication. - </tp:docstring> + <p>The Connection MUST NOT proceed with connection, or signal + <tp:dbus-ref namespace="ofdT.Connection">StatusChanged</tp:dbus-ref> + to the CONNECTED state, until each channel of this type has either + been accepted as having a positive result (for instance, on SASL + channels this is done with the <tp:dbus-ref + namespace="ofdT.Channel.Interface.SASLAuthentication.DRAFT2" + >AcceptSASL</tp:dbus-ref> method), or closed with the <tp:dbus-ref + namespace="ofdT.Channel">Close</tp:dbus-ref> method.</p> + + <tp:rationale> + <p>ServerAuthentication channels normally represent the client + authenticating itself to the server, but can also be used for the + server to authenticate itself to the client (i.e. prove that it is + in fact the desired server and not an imposter). Until the + authentication handler has confirmed this, connection should not + continue.</p> + </tp:rationale> - <tp:member type="s" name="Key"> - <tp:docstring xmlns="http://www.w3.org/1999/xhtml"> - <p> - Well-known keys: - <dl> - <dt>username</dt> - <dd> - string, Username to authenticate with if needed - </dd> - <dt>realm</dt> - <dd> - string, Realm to use for authentication if needed - </dd> - <dt>session-id</dt> - <dd> - XMPP session id as needed for the legacy jabber digest method. - </dd> - </dl> - </p> - </tp:docstring> - </tp:member> + <p>If a channel of this type is closed with the <tp:dbus-ref + namespace="ofdT.Channel">Close</tp:dbus-ref> method before + authentication has succeeded, this indicates that the Handler has + given up its attempts to authenticate or that no Handler is + available.</p> - <tp:member name="Value" type="v"> - </tp:member> - </tp:mapping> + <p>If this occurs, the connection manager MAY attempt to continue + connection (for instance, performing SASL authentication by using any + credentials passed to <tp:dbus-ref + namespace="ofdT.ConnectionManager">RequestConnection</tp:dbus-ref>, + for instance from the <tp:dbus-ref + namespace="ofdT">Account.Parameters</tp:dbus-ref>). If this fails + or has already been tried, the <tp:dbus-ref + namespace="ofdT">Connection</tp:dbus-ref> will + disconnect.</p> + + <tp:rationale> + <p>In particular, the <tp:dbus-ref + namespace="ofdT">ChannelDispatcher</tp:dbus-ref> will close the + channel if it cannot find a handler.</p> + </tp:rationale> + </tp:docstring> + + <property name="AuthenticationMethod" + tp:name-for-bindings="Authentication_Method" + type="s" tp:type="DBus_Interface" access="read"> + <tp:docstring xmlns="http://www.w3.org/1999/xhtml"> + <p>This property defines the method used for the authentication step + represented by this channel, which MUST be one of this channel's + <tp:dbus-ref namespace="ofdT.Channel">Interfaces</tp:dbus-ref>.</p> + + <p>The initially-defined interface that can be used here is + <tp:dbus-ref namespace="ofdT" + >Channel.Interface.SASLAuthentication.DRAFT2</tp:dbus-ref>.</p> + </tp:docstring> + </property> </interface> </node> diff --git a/extensions/Makefile.am b/extensions/Makefile.am index e00442db..f217b668 100644 --- a/extensions/Makefile.am +++ b/extensions/Makefile.am @@ -17,7 +17,7 @@ EXTRA_DIST = \ Gabble_Plugin_Test.xml \ OLPC_Activity_Properties.xml \ OLPC_Buddy_Info.xml \ - Channel_Interface_Sasl_Authentication.xml \ + Channel_Interface_SASL_Authentication.xml \ Channel_Type_Server_Authentication.xml noinst_LTLIBRARIES = libgabble-extensions.la diff --git a/extensions/all.xml b/extensions/all.xml index 680ba7b3..00d3f02c 100644 --- a/extensions/all.xml +++ b/extensions/all.xml @@ -53,7 +53,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA</p> <xi:include href="Channel_Type_Call.xml" /> <xi:include href="Channel_Type_Server_Authentication.xml" /> -<xi:include href="Channel_Interface_Sasl_Authentication.xml" /> +<xi:include href="Channel_Interface_SASL_Authentication.xml" /> <tp:generic-types> <tp:external-type name="Contact_Handle" type="u" diff --git a/src/server-sasl-channel.c b/src/server-sasl-channel.c index 5a73c53b..e808826a 100644 --- a/src/server-sasl-channel.c +++ b/src/server-sasl-channel.c @@ -145,15 +145,19 @@ enum PROP_CHANNEL_PROPERTIES, /* server authentication channel */ - PROP_AUTH_INFO, PROP_AUTH_METHOD, /* sasl authentication channel */ - PROP_CURRENT_STATE, - PROP_MECHANISM, - PROP_CHALLENGE, PROP_AVAILABLE_MECHANISMS, + PROP_HAS_INITIAL_DATA, + PROP_CAN_TRY_AGAIN, PROP_SECURE, + PROP_SASL_STATUS, + PROP_SASL_ERROR, + PROP_SASL_ERROR_DETAILS, + PROP_AUTHORIZATION_IDENTITY, + PROP_DEFAULT_REALM, + PROP_SASL_CONTEXT, LAST_PROPERTY, }; @@ -169,15 +173,17 @@ struct _GabbleServerSaslChannelPrivate GabbleConnection *conn; gboolean closed; - /* Server Authentication Iface*/ - GHashTable *auth_info; - - /* SASL Auth Iface */ + /* Immutable SASL properties */ + gchar *authorization_identity; + gchar *default_realm; GPtrArray *available_mechanisms; - GValueArray *current_state; - GArray *challenge; - gchar *mechanism; gboolean secure; + GHashTable *sasl_context; + + /* Mutable SASL properties */ + GabbleSASLStatus sasl_status; + gchar *sasl_error; + GHashTable *sasl_error_details; GSimpleAsyncResult *result; }; @@ -185,39 +191,20 @@ struct _GabbleServerSaslChannelPrivate static void gabble_server_sasl_channel_init (GabbleServerSaslChannel *self) { - self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, + GabbleServerSaslChannelPrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GABBLE_TYPE_SERVER_SASL_CHANNEL, GabbleServerSaslChannelPrivate); -} - -static GObject * -gabble_server_sasl_channel_constructor (GType type, - guint n_props, - GObjectConstructParam *props) -{ - GObject *obj; - GabbleServerSaslChannel *self; - GabbleServerSaslChannelPrivate *priv; - - obj = G_OBJECT_CLASS (gabble_server_sasl_channel_parent_class)-> - constructor (type, n_props, props); - self = GABBLE_SERVER_SASL_CHANNEL (obj); - priv = self->priv; + self->priv = priv; priv->closed = TRUE; - priv->challenge = g_array_new (FALSE, FALSE, sizeof (gchar)); - priv->available_mechanisms = g_ptr_array_new_with_free_func ( (GDestroyNotify) g_free); + priv->sasl_context = tp_asv_new (NULL, NULL); - priv->current_state = tp_value_array_build (3, - G_TYPE_UINT, GABBLE_SASL_STATUS_NOT_STARTED, - G_TYPE_STRING, "", - G_TYPE_STRING, "", - G_TYPE_INVALID); - - return obj; + priv->sasl_status = GABBLE_SASL_STATUS_NOT_STARTED; + priv->sasl_error = NULL; + priv->sasl_error_details = tp_asv_new (NULL, NULL); } static void @@ -278,26 +265,40 @@ gabble_server_sasl_channel_get_property (GObject *object, TP_IFACE_CHANNEL, "Requested", TP_IFACE_CHANNEL, "Interfaces", GABBLE_IFACE_CHANNEL_TYPE_SERVER_AUTHENTICATION, - "AuthenticationInformation", - GABBLE_IFACE_CHANNEL_TYPE_SERVER_AUTHENTICATION, - "AuthenticationMethod", + "AuthenticationMethod", + GABBLE_IFACE_CHANNEL_INTERFACE_SASL_AUTHENTICATION, + "AvailableMechanisms", + GABBLE_IFACE_CHANNEL_INTERFACE_SASL_AUTHENTICATION, + "HasInitialData", GABBLE_IFACE_CHANNEL_INTERFACE_SASL_AUTHENTICATION, - "AvailableMechanisms", + "CanTryAgain", GABBLE_IFACE_CHANNEL_INTERFACE_SASL_AUTHENTICATION, - "Secure", + "Encrypted", + GABBLE_IFACE_CHANNEL_INTERFACE_SASL_AUTHENTICATION, + "Verified", + GABBLE_IFACE_CHANNEL_INTERFACE_SASL_AUTHENTICATION, + "AuthorizationIdentity", + GABBLE_IFACE_CHANNEL_INTERFACE_SASL_AUTHENTICATION, + "DefaultRealm", + GABBLE_IFACE_CHANNEL_INTERFACE_SASL_AUTHENTICATION, + "SASLContext", NULL)); break; - case PROP_CURRENT_STATE: - g_value_set_boxed (value, priv->current_state); + case PROP_SASL_STATUS: + g_value_set_uint (value, priv->sasl_status); break; - case PROP_AUTH_INFO: - g_value_set_boxed (value, priv->auth_info); + case PROP_SASL_ERROR: + g_value_set_string (value, priv->sasl_error); break; - case PROP_AUTH_METHOD: - g_value_set_uint (value, GABBLE_AUTHENTICATION_TYPE_SASL); + case PROP_SASL_ERROR_DETAILS: + g_value_set_boxed (value, priv->sasl_error_details); break; - case PROP_MECHANISM: - g_value_set_string (value, priv->mechanism); + case PROP_SASL_CONTEXT: + g_value_set_boxed (value, priv->sasl_context); + break; + case PROP_AUTH_METHOD: + g_value_set_static_string (value, + GABBLE_IFACE_CHANNEL_INTERFACE_SASL_AUTHENTICATION); break; case PROP_AVAILABLE_MECHANISMS: g_value_set_boxed (value, @@ -306,8 +307,19 @@ gabble_server_sasl_channel_get_property (GObject *object, case PROP_SECURE: g_value_set_boolean (value, chan->priv->secure); break; - case PROP_CHALLENGE: - g_value_set_boxed (value, chan->priv->challenge); + case PROP_CAN_TRY_AGAIN: + /* Wocky can't retry SASL authentication (although XMPP can) */ + g_value_set_boolean (value, FALSE); + break; + case PROP_HAS_INITIAL_DATA: + /* Yes, XMPP has "initial data" in its SASL */ + g_value_set_boolean (value, TRUE); + break; + case PROP_AUTHORIZATION_IDENTITY: + g_value_set_string (value, priv->authorization_identity); + break; + case PROP_DEFAULT_REALM: + g_value_set_string (value, priv->default_realm); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); @@ -355,18 +367,18 @@ gabble_server_sasl_channel_dispose (GObject *object) priv->dispose_has_run = TRUE; gabble_server_sasl_channel_close (self); + g_assert (priv->result == NULL); - g_free (priv->mechanism); - + /* FIXME: from here down should really be in finalize since no object refs + * are involved */ g_free (priv->object_path); + g_free (priv->authorization_identity); + g_free (priv->default_realm); + g_ptr_array_unref (priv->available_mechanisms); + g_hash_table_unref (priv->sasl_context); - g_ptr_array_free (priv->available_mechanisms, TRUE); - - g_value_array_free (priv->current_state); - - g_array_free (priv->challenge, TRUE); - - tp_clear_pointer (&priv->auth_info, g_hash_table_unref); + g_free (priv->sasl_error); + g_hash_table_unref (priv->sasl_error_details); if (G_OBJECT_CLASS (gabble_server_sasl_channel_parent_class)->dispose) G_OBJECT_CLASS (gabble_server_sasl_channel_parent_class)->dispose (object); @@ -388,16 +400,24 @@ gabble_server_sasl_channel_class_init (GabbleServerSaslChannelClass *klass) }; static TpDBusPropertiesMixinPropImpl server_auth_props[] = { - { "AuthenticationInformation", "auth-info", NULL }, { "AuthenticationMethod", "auth-method", NULL }, { NULL } }; static TpDBusPropertiesMixinPropImpl sasl_auth_props[] = { - { "CurrentState", "current-state", NULL }, { "AvailableMechanisms", "available-mechanisms", NULL }, - { "Secure", "secure", NULL }, - { "CurrentChallenge", "challenge", NULL }, + { "HasInitialData", "has-initial-data", NULL }, + { "CanTryAgain", "can-try-again", NULL }, + { "SASLStatus", "sasl-status", NULL }, + { "SASLError", "sasl-error", NULL }, + { "SASLErrorDetails", "sasl-error-details", NULL }, + { "AuthorizationIdentity", "authorization-identity", NULL }, + { "DefaultRealm", "default-realm", NULL }, + { "SASLContext", "sasl-context", NULL }, + /* For the moment we only have a unified "secure" property, which + * implies we're both encrypted and verified */ + { "Encrypted", "secure", NULL }, + { "Verified", "secure", NULL }, { NULL } }; @@ -427,7 +447,6 @@ gabble_server_sasl_channel_class_init (GabbleServerSaslChannelClass *klass) g_type_class_add_private (klass, sizeof (GabbleServerSaslChannelPrivate)); - object_class->constructor = gabble_server_sasl_channel_constructor; object_class->get_property = gabble_server_sasl_channel_get_property; object_class->set_property = gabble_server_sasl_channel_set_property; object_class->dispose = gabble_server_sasl_channel_dispose; @@ -501,34 +520,55 @@ gabble_server_sasl_channel_class_init (GabbleServerSaslChannelClass *klass) g_object_class_install_property (object_class, PROP_INITIATOR_ID, param_spec); - param_spec = g_param_spec_boxed ("auth-info", - "Authentication information", - "Details used for authentication purposes.", - GABBLE_HASH_TYPE_AUTHDETAILS, + param_spec = g_param_spec_boxed ("sasl-context", "SASLContext", + "Extra context for doing SASL", + TP_HASH_TYPE_STRING_VARIANT_MAP, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); - g_object_class_install_property (object_class, PROP_AUTH_INFO, + g_object_class_install_property (object_class, PROP_SASL_CONTEXT, param_spec); - param_spec = g_param_spec_uint ("auth-method", + param_spec = g_param_spec_string ("auth-method", "Authentication method", - "Method of authentication like SASL or Captcha.", - 0, NUM_GABBLE_AUTHENTICATION_TYPES, GABBLE_AUTHENTICATION_TYPE_SASL, + "Method of authentication (D-Bus interface)", + GABBLE_IFACE_CHANNEL_INTERFACE_SASL_AUTHENTICATION, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_AUTH_METHOD, param_spec); - param_spec = g_param_spec_boxed ("current-state", - "Current state", - "The state of the current SASL authentication.", - GABBLE_STRUCT_TYPE_SASL_STATE, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); - g_object_class_install_property (object_class, PROP_CURRENT_STATE, param_spec); + param_spec = g_param_spec_string ("authorization-identity", + "AuthorizationIdentity", + "Identity for which we wish to be authorized", + NULL, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + g_object_class_install_property (object_class, PROP_AUTHORIZATION_IDENTITY, + param_spec); + + param_spec = g_param_spec_string ("default-realm", + "DefaultRealm", + "Default realm if the server does not supply one", + NULL, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + g_object_class_install_property (object_class, PROP_DEFAULT_REALM, + param_spec); - param_spec = g_param_spec_string ("mechanism", "Mechanism", - "Mechanism used for this negotiation.", - NULL, + param_spec = g_param_spec_uint ("sasl-status", "SASLStatus", + "Status of this channel", + /* FIXME: fix the pluralization in the spec */ + 0, NUM_GABBLE_SASL_STATUSS, 0, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); - g_object_class_install_property (object_class, PROP_MECHANISM, param_spec); + g_object_class_install_property (object_class, PROP_SASL_STATUS, + param_spec); + + param_spec = g_param_spec_string ("sasl-error", "SASLError", + "D-Bus error name", + "", G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + g_object_class_install_property (object_class, PROP_SASL_ERROR, + param_spec); + + param_spec = g_param_spec_boxed ("sasl-error-details", "SASLErrorDetails", + "Extra details of a SASL error", + TP_HASH_TYPE_STRING_VARIANT_MAP, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + g_object_class_install_property (object_class, PROP_SASL_ERROR_DETAILS, + param_spec); param_spec = g_param_spec_boxed ("available-mechanisms", "Available authentication mechanisms", @@ -538,22 +578,26 @@ gabble_server_sasl_channel_class_init (GabbleServerSaslChannelClass *klass) g_object_class_install_property (object_class, PROP_AVAILABLE_MECHANISMS, param_spec); + param_spec = g_param_spec_boolean ("can-try-again", "CanTryAgain", + "True if failed SASL can be retried without reconnecting", + FALSE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + g_object_class_install_property (object_class, PROP_CAN_TRY_AGAIN, + param_spec); + + param_spec = g_param_spec_boolean ("has-initial-data", "HasInitialData", + "True if SASL has initial data", + TRUE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + g_object_class_install_property (object_class, PROP_HAS_INITIAL_DATA, + param_spec); + param_spec = g_param_spec_boolean ("secure", "Is secure", - "Is this channel secure (encrypted)?", + "Is this channel secure (encrypted and verified)?", FALSE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_SECURE, param_spec); - param_spec = g_param_spec_boxed ("challenge", - "Challenge", - "The latest challenge", - DBUS_TYPE_G_UCHAR_ARRAY, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); - g_object_class_install_property (object_class, PROP_CHALLENGE, - param_spec); - klass->dbus_props_class.interfaces = prop_interfaces; tp_dbus_properties_mixin_class_init (object_class, G_STRUCT_OFFSET (GabbleServerSaslChannelClass, dbus_props_class)); @@ -561,42 +605,24 @@ gabble_server_sasl_channel_class_init (GabbleServerSaslChannelClass *klass) static void change_current_state (GabbleServerSaslChannel *self, - GabbleSaslStatus status, + GabbleSASLStatus status, const gchar *dbus_error, - const gchar *message) + const gchar *debug_message) { - GabbleServerSaslChannelPrivate *priv = self->priv; - GValue *value; - gboolean status_changed; - - value = g_value_array_get_nth (priv->current_state, 0); - status_changed = (status != g_value_get_uint (value)); - g_value_set_uint (value, status); - - if (dbus_error != NULL) - { - value = g_value_array_get_nth (priv->current_state, 1); - g_value_set_string (value, dbus_error); - } - - if (message != NULL) - { - value = g_value_array_get_nth (priv->current_state, 2); - g_value_set_string (value, message); - } + self->priv->sasl_status = status; - if (status_changed) - { - const gchar *current_message; - const gchar *current_dbus_error; - GabbleSaslStatus current_status; + g_free (self->priv->sasl_error); + self->priv->sasl_error = g_strdup (dbus_error); - tp_value_array_unpack (priv->current_state, 3, ¤t_status, - ¤t_dbus_error, ¤t_message); + g_hash_table_remove_all (self->priv->sasl_error_details); + if (debug_message != NULL) + tp_asv_set_string (self->priv->sasl_error_details, "debug-message", + debug_message); - gabble_svc_channel_interface_sasl_authentication_emit_state_changed ( - self, current_status, current_dbus_error, current_message); - } + gabble_svc_channel_interface_sasl_authentication_emit_sasl_status_changed ( + self, self->priv->sasl_status, + self->priv->sasl_error, + self->priv->sasl_error_details); } /** @@ -641,7 +667,7 @@ channel_iface_init (gpointer g_iface, } /** - * Sasl Authentication Channel Interface + * SASL Authentication Channel Interface */ static void @@ -656,19 +682,20 @@ gabble_server_sasl_channel_raise_not_available (DBusGMethodInvocation *context, g_error_free (error); } +/* When called from start_mechanism, initial_data can be NULL. When called + * from D-Bus as StartMechanismWithData, it can't. */ static void -gabble_server_sasl_channel_start_mechanism ( - GabbleSvcChannelInterfaceSaslAuthentication *mechanisms_chooser, +gabble_server_sasl_channel_start_mechanism_with_data ( + GabbleSvcChannelInterfaceSASLAuthentication *iface, const gchar *in_Mechanism, const GArray *in_InitialData, DBusGMethodInvocation *context) { - GabbleServerSaslChannel *self = GABBLE_SERVER_SASL_CHANNEL ( - mechanisms_chooser); + GabbleServerSaslChannel *self = GABBLE_SERVER_SASL_CHANNEL (iface); GabbleServerSaslChannelPrivate *priv = self->priv; WockyAuthRegistryStartData *start_data; GSimpleAsyncResult *r = priv->result; - GString *initial_data; + GString *initial_data = NULL; guint i; gboolean mechanism_available = FALSE; DEBUG (""); @@ -697,8 +724,17 @@ gabble_server_sasl_channel_start_mechanism ( { priv->result = NULL; - initial_data = g_string_new_len (in_InitialData->data, - in_InitialData->len); + if (in_InitialData != NULL) + { + initial_data = g_string_new_len (in_InitialData->data, + in_InitialData->len); + } + else if (g_str_has_prefix (in_Mechanism, "X-WOCKY-JABBER-")) + { + /* FIXME: wocky-jabber-auth asserts there is an initial response, + * and will crash otherwise */ + initial_data = g_string_sized_new (0); + } start_data = wocky_auth_registry_start_data_new (in_Mechanism, initial_data); @@ -720,8 +756,18 @@ gabble_server_sasl_channel_start_mechanism ( } static void +gabble_server_sasl_channel_start_mechanism ( + GabbleSvcChannelInterfaceSASLAuthentication *iface, + const gchar *mech, + DBusGMethodInvocation *context) +{ + gabble_server_sasl_channel_start_mechanism_with_data (iface, mech, NULL, + context); +} + +static void gabble_server_sasl_channel_respond ( - GabbleSvcChannelInterfaceSaslAuthentication *channel, + GabbleSvcChannelInterfaceSASLAuthentication *channel, const GArray *in_Response_Data, DBusGMethodInvocation *context) { @@ -758,18 +804,16 @@ gabble_server_sasl_channel_respond ( } static void -gabble_server_sasl_channel_accept ( - GabbleSvcChannelInterfaceSaslAuthentication *channel, +gabble_server_sasl_channel_accept_sasl ( + GabbleSvcChannelInterfaceSASLAuthentication *channel, DBusGMethodInvocation *context) { GabbleServerSaslChannel *self = GABBLE_SERVER_SASL_CHANNEL (channel); - GabbleSaslStatus current_status = g_value_get_uint (g_value_array_get_nth ( - self->priv->current_state, 0)); GSimpleAsyncResult *r = self->priv->result; const gchar *message = NULL; - switch (current_status) + switch (self->priv->sasl_status) { case GABBLE_SASL_STATUS_NOT_STARTED: message = "Authentication has not yet begun."; @@ -815,64 +859,71 @@ gabble_server_sasl_channel_accept ( g_object_unref (r); } - gabble_svc_channel_interface_sasl_authentication_return_from_accept ( + gabble_svc_channel_interface_sasl_authentication_return_from_accept_sasl ( context); } static void -gabble_server_sasl_channel_abort ( - GabbleSvcChannelInterfaceSaslAuthentication *channel, +gabble_server_sasl_channel_abort_sasl ( + GabbleSvcChannelInterfaceSASLAuthentication *channel, guint in_Reason, const gchar *in_Debug_Message, DBusGMethodInvocation *context) { GabbleServerSaslChannel *self = GABBLE_SERVER_SASL_CHANNEL (channel); GSimpleAsyncResult *r = self->priv->result; - GabbleSaslStatus current_status = g_value_get_uint (g_value_array_get_nth ( - self->priv->current_state, 0)); guint code; const gchar *dbus_error; - if (current_status == GABBLE_SASL_STATUS_SUCCEEDED || - current_status == GABBLE_SASL_STATUS_CLIENT_ACCEPTED) - { - gabble_server_sasl_channel_raise_not_available (context, - "Authentication has already succeeded."); - - return; - } - - switch (in_Reason) + switch (self->priv->sasl_status) { - case GABBLE_ABORT_REASON_INVALID_CHALLENGE: - code = WOCKY_AUTH_ERROR_INVALID_REPLY; - dbus_error = TP_ERROR_STR_AUTHENTICATION_FAILED; + case GABBLE_SASL_STATUS_SERVER_FAILED: + case GABBLE_SASL_STATUS_CLIENT_FAILED: + /* no effect */ break; - case GABBLE_ABORT_REASON_USER_ABORT: - code = WOCKY_AUTH_ERROR_FAILURE; - dbus_error = TP_ERROR_STR_CANCELLED; - break; + case GABBLE_SASL_STATUS_SUCCEEDED: + case GABBLE_SASL_STATUS_CLIENT_ACCEPTED: + gabble_server_sasl_channel_raise_not_available (context, + "Authentication has already succeeded."); + return; default: - g_assert_not_reached (); + switch (in_Reason) + { + case GABBLE_SASL_ABORT_REASON_INVALID_CHALLENGE: + code = WOCKY_AUTH_ERROR_INVALID_REPLY; + /* FIXME: should be ServiceConfused, when it lands in tp-glib */ + dbus_error = TP_ERROR_STR_AUTHENTICATION_FAILED; + break; + + case GABBLE_SASL_ABORT_REASON_USER_ABORT: + code = WOCKY_AUTH_ERROR_FAILURE; + dbus_error = TP_ERROR_STR_CANCELLED; + break; + + default: + /* FIXME: should not abort on D-Bus input! */ + g_assert_not_reached (); + } + + if (r != NULL) + { + self->priv->result = NULL; + + g_simple_async_result_set_error (r, WOCKY_AUTH_ERROR, code, + "Authentication aborted: %s", in_Debug_Message); + + g_simple_async_result_complete_in_idle (r); + g_object_unref (r); + } + + change_current_state (self, GABBLE_SASL_STATUS_CLIENT_FAILED, + dbus_error, in_Debug_Message); } - if (r != NULL) - { - self->priv->result = NULL; - - g_simple_async_result_set_error (r, WOCKY_AUTH_ERROR, code, - "Authentication aborted: %s", in_Debug_Message); - - g_simple_async_result_complete_in_idle (r); - g_object_unref (r); - } - - change_current_state (self, GABBLE_SASL_STATUS_CLIENT_FAILED, dbus_error, - in_Debug_Message); - - gabble_svc_channel_interface_sasl_authentication_return_from_abort (context); + gabble_svc_channel_interface_sasl_authentication_return_from_abort_sasl ( + context); } static void @@ -883,9 +934,10 @@ sasl_auth_iface_init (gpointer klass, gabble_svc_channel_interface_sasl_authentication_implement_##x ( \ klass, gabble_server_sasl_channel_##x) IMPLEMENT (start_mechanism); + IMPLEMENT (start_mechanism_with_data); IMPLEMENT (respond); - IMPLEMENT (accept); - IMPLEMENT (abort); + IMPLEMENT (accept_sasl); + IMPLEMENT (abort_sasl); #undef IMPLEMENT } @@ -922,14 +974,15 @@ gabble_server_sasl_channel_start_auth_async_func ( g_ptr_array_set_size (priv->available_mechanisms, 0); - if (priv->auth_info != NULL) - g_hash_table_unref (priv->auth_info); + g_free (priv->authorization_identity); + priv->authorization_identity = g_strdup (username); - priv->auth_info = tp_asv_new ( - "username", G_TYPE_STRING, username, - "realm", G_TYPE_STRING, server, - "session-id", G_TYPE_STRING, session_id, - NULL); + g_free (priv->default_realm); + priv->default_realm = g_strdup (server); + + g_hash_table_remove_all (priv->sasl_context); + g_hash_table_insert (priv->sasl_context, "jabber-stream-id", + tp_g_value_slice_new_string (session_id)); priv->result = g_simple_async_result_new (G_OBJECT (self), callback, user_data, gabble_server_sasl_channel_start_auth_async_func); @@ -939,7 +992,7 @@ gabble_server_sasl_channel_start_auth_async_func ( g_ptr_array_add (priv->available_mechanisms, NULL); - priv->object_path = g_strdup_printf ("%s/SaslChannel", + priv->object_path = g_strdup_printf ("%s/SASLChannel", conn->object_path); priv->secure = is_secure_channel; @@ -993,23 +1046,22 @@ gabble_server_sasl_channel_challenge_async_func ( if (!priv->closed) { + GArray *challenge_ay; + g_assert (priv->result == NULL); priv->result = g_simple_async_result_new (G_OBJECT (self), callback, user_data, gabble_server_sasl_channel_challenge_async_func); - g_array_free (priv->challenge, TRUE); - - priv->challenge = g_array_sized_new (FALSE, FALSE, sizeof (gchar), + challenge_ay = g_array_sized_new (FALSE, FALSE, sizeof (gchar), challenge_data->len); - - g_array_append_vals (priv->challenge, challenge_data->str, + g_array_append_vals (challenge_ay, challenge_data->str, challenge_data->len); change_current_state (self, GABBLE_SASL_STATUS_IN_PROGRESS, NULL, NULL); gabble_svc_channel_interface_sasl_authentication_emit_new_challenge ( - self, priv->challenge); + self, challenge_ay); } else { @@ -1052,8 +1104,6 @@ gabble_server_sasl_channel_success_async_func ( if (!priv->closed) { - GabbleSaslStatus current_status = g_value_get_uint ( - g_value_array_get_nth (priv->current_state, 0)); GSimpleAsyncResult *r = g_simple_async_result_new (G_OBJECT (self), callback, user_data, gabble_server_sasl_channel_success_async_func); @@ -1062,7 +1112,7 @@ gabble_server_sasl_channel_success_async_func ( g_assert (priv->result == NULL); - if (current_status != GABBLE_SASL_STATUS_CLIENT_ACCEPTED) + if (self->priv->sasl_status != GABBLE_SASL_STATUS_CLIENT_ACCEPTED) { priv->result = r; change_current_state (self, GABBLE_SASL_STATUS_SERVER_SUCCEEDED, diff --git a/tests/twisted/constants.py b/tests/twisted/constants.py index 1d2edf84..f799101c 100644 --- a/tests/twisted/constants.py +++ b/tests/twisted/constants.py @@ -24,7 +24,7 @@ CHANNEL_IFACE_MEDIA_SIGNALLING = CHANNEL + ".Interface.MediaSignalling" CHANNEL_IFACE_MESSAGES = CHANNEL + ".Interface.Messages" CHANNEL_IFACE_PASSWORD = CHANNEL + ".Interface.Password" CHANNEL_IFACE_TUBE = CHANNEL + ".Interface.Tube" -CHANNEL_IFACE_SASL_AUTH = CHANNEL + ".Interface.SaslAuthentication.DRAFT" +CHANNEL_IFACE_SASL_AUTH = CHANNEL + ".Interface.SASLAuthentication.DRAFT2" CHANNEL_IFACE_CONFERENCE = CHANNEL + '.Interface.Conference' CHANNEL_TYPE_CALL = CHANNEL + ".Type.Call.DRAFT" @@ -38,7 +38,7 @@ CHANNEL_TYPE_STREAMED_MEDIA = CHANNEL + ".Type.StreamedMedia" CHANNEL_TYPE_TEXT = CHANNEL + ".Type.Text" CHANNEL_TYPE_FILE_TRANSFER = CHANNEL + ".Type.FileTransfer" CHANNEL_TYPE_SERVER_AUTHENTICATION = \ - CHANNEL + ".Type.ServerAuthentication.DRAFT" + CHANNEL + ".Type.ServerAuthentication.DRAFT2" CHANNEL_TYPE_SERVER_TLS_CONNECTION = \ CHANNEL + ".Type.ServerTLSConnection" @@ -350,10 +350,6 @@ CONTACT_INFO_FLAG_CAN_SET = 1 CONTACT_INFO_FLAG_PUSH = 2 CONTACT_INFO_FIELD_FLAG_PARAMETERS_MANDATORY = 1 -# Channel_Type_ServerAuthentication -AUTH_TYPE_SASL = 0 -AUTH_TYPE_CAPTCHA = 1 - # Channel_Interface_SaslAuthentication SASL_STATUS_NOT_STARTED = 0 SASL_STATUS_IN_PROGRESS = 1 @@ -367,8 +363,13 @@ SASL_ABORT_REASON_INVALID_CHALLENGE = 0 SASL_ABORT_REASON_USER_ABORT = 1 AUTH_METHOD = CHANNEL_TYPE_SERVER_AUTHENTICATION + ".AuthenticationMethod" -AUTH_INFO = CHANNEL_TYPE_SERVER_AUTHENTICATION + ".AuthenticationInformation" SASL_AVAILABLE_MECHANISMS = CHANNEL_IFACE_SASL_AUTH + ".AvailableMechanisms" +SASL_STATUS = CHANNEL_IFACE_SASL_AUTH + ".SASLStatus" +SASL_ERROR = CHANNEL_IFACE_SASL_AUTH + ".SASLError" +SASL_ERROR_DETAILS = CHANNEL_IFACE_SASL_AUTH + ".SASLErrorDetails" +SASL_CONTEXT = CHANNEL_IFACE_SASL_AUTH + ".SASLContext" +SASL_AUTHORIZATION_IDENTITY = CHANNEL_IFACE_SASL_AUTH + ".AuthorizationIdentity" +SASL_DEFAULT_REALM = CHANNEL_IFACE_SASL_AUTH + ".DefaultRealm" # Channel_Type_ServerTLSConnection TLS_CERT_PATH = CHANNEL_TYPE_SERVER_TLS_CONNECTION + ".ServerCertificate" diff --git a/tests/twisted/sasl/abort.py b/tests/twisted/sasl/abort.py index 51c38c8e..0f1615af 100644 --- a/tests/twisted/sasl/abort.py +++ b/tests/twisted/sasl/abort.py @@ -21,25 +21,25 @@ def test_abort_early(q, bus, conn, stream): pass def test_abort_mid(q, bus, conn, stream): - chan, auth_info, avail_mechs = connect_and_get_sasl_channel(q, bus, conn) + chan, props = connect_and_get_sasl_channel(q, bus, conn) - chan.SaslAuthentication.StartMechanism("ABORT-TEST", EXCHANGE[0][1]) + chan.SASLAuthentication.StartMechanismWithData("ABORT-TEST", EXCHANGE[0][1]) - q.expect('dbus-signal', signal='StateChanged', + q.expect('dbus-signal', signal='SASLStatusChanged', interface=cs.CHANNEL_IFACE_SASL_AUTH, - args=[cs.SASL_STATUS_IN_PROGRESS, '', '']) + args=[cs.SASL_STATUS_IN_PROGRESS, '', {}]) abort_auth(q, chan, cs.SASL_ABORT_REASON_INVALID_CHALLENGE, "wrong data from server") def test_disconnect_mid(q, bus, conn, stream): - chan, auth_info, avail_mechs = connect_and_get_sasl_channel(q, bus, conn) + chan, props = connect_and_get_sasl_channel(q, bus, conn) - chan.SaslAuthentication.StartMechanism("ABORT-TEST", EXCHANGE[0][1]) + chan.SASLAuthentication.StartMechanismWithData("ABORT-TEST", EXCHANGE[0][1]) - q.expect('dbus-signal', signal='StateChanged', + q.expect('dbus-signal', signal='SASLStatusChanged', interface=cs.CHANNEL_IFACE_SASL_AUTH, - args=[cs.SASL_STATUS_IN_PROGRESS, '', '']) + args=[cs.SASL_STATUS_IN_PROGRESS, '', {}]) call_async(q, conn, 'Disconnect') q.expect_many(EventPattern('dbus-signal', signal='StatusChanged', @@ -48,26 +48,26 @@ def test_disconnect_mid(q, bus, conn, stream): EventPattern('dbus-return', method='Disconnect')) def test_abort_connected(q, bus, conn, stream): - chan, auth_info, avail_mechs = connect_and_get_sasl_channel(q, bus, conn) + chan, props = connect_and_get_sasl_channel(q, bus, conn) - chan.SaslAuthentication.StartMechanism( + chan.SASLAuthentication.StartMechanismWithData( 'PLAIN', '\0' + JID.split('@')[0] + '\0' + PASSWORD) - q.expect('dbus-signal', signal='StateChanged', + q.expect('dbus-signal', signal='SASLStatusChanged', interface=cs.CHANNEL_IFACE_SASL_AUTH, - args=[cs.SASL_STATUS_SERVER_SUCCEEDED, '', '']) + args=[cs.SASL_STATUS_SERVER_SUCCEEDED, '', {}]) - chan.SaslAuthentication.Accept() + chan.SASLAuthentication.AcceptSASL() - q.expect('dbus-signal', signal='StateChanged', + q.expect('dbus-signal', signal='SASLStatusChanged', interface=cs.CHANNEL_IFACE_SASL_AUTH, - args=[cs.SASL_STATUS_SUCCEEDED, '', '']) + args=[cs.SASL_STATUS_SUCCEEDED, '', {}]) e = q.expect('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_CONNECTED, cs.CSR_REQUESTED]) try: - chan.SaslAuthentication.Abort(cs.SASL_ABORT_REASON_USER_ABORT, + chan.SASLAuthentication.AbortSASL(cs.SASL_ABORT_REASON_USER_ABORT, "aborting too late") except dbus.DBusException: pass diff --git a/tests/twisted/sasl/complex.py b/tests/twisted/sasl/complex.py index eea0f8b0..fe66f85f 100644 --- a/tests/twisted/sasl/complex.py +++ b/tests/twisted/sasl/complex.py @@ -3,7 +3,7 @@ Test the server sasl channel with the PLAIN mechanism """ import dbus -from servicetest import EventPattern, assertEquals +from servicetest import EventPattern, assertEquals, assertSameSets from gabbletest import exec_test import constants as cs from saslutil import SaslComplexAuthenticator, connect_and_get_sasl_channel @@ -17,12 +17,12 @@ EXCHANGE = [("", "Hi dear"), MECHANISMS = ["PLAIN", "DIGEST-MD5", "POLITE-TEST"] def test_complex_success(q, bus, conn, stream): - chan, auth_info, avail_mechs = connect_and_get_sasl_channel(q, bus, conn) + chan, props = connect_and_get_sasl_channel(q, bus, conn) - assertEquals(MECHANISMS, avail_mechs) + assertSameSets(MECHANISMS, props.get(cs.SASL_AVAILABLE_MECHANISMS)) try: - chan.SaslAuthentication.StartMechanism("FOO", "") + chan.SASLAuthentication.StartMechanismWithData("FOO", "") except dbus.DBusException: pass else: @@ -34,11 +34,11 @@ def test_complex_success(q, bus, conn, stream): else: initial_data = "" - chan.SaslAuthentication.StartMechanism("POLITE-TEST", initial_data) + chan.SASLAuthentication.StartMechanismWithData("POLITE-TEST", initial_data) - q.expect('dbus-signal', signal='StateChanged', + q.expect('dbus-signal', signal='SASLStatusChanged', interface=cs.CHANNEL_IFACE_SASL_AUTH, - args=[cs.SASL_STATUS_IN_PROGRESS, '', '']) + args=[cs.SASL_STATUS_IN_PROGRESS, '', {}]) for i, pair in enumerate(EXCHANGE[1:]): challenge, response = pair @@ -46,23 +46,17 @@ def test_complex_success(q, bus, conn, stream): interface=cs.CHANNEL_IFACE_SASL_AUTH, args=[challenge or 'None']) - challenge_prop = ''.join( - map(chr, chan.Properties.Get( - cs.CHANNEL_IFACE_SASL_AUTH, "CurrentChallenge"))) - - assertEquals(challenge, challenge_prop) - if i == len(EXCHANGE) - 2: - chan.SaslAuthentication.Accept() - q.expect('dbus-signal', signal='StateChanged', + chan.SASLAuthentication.AcceptSASL() + q.expect('dbus-signal', signal='SASLStatusChanged', interface=cs.CHANNEL_IFACE_SASL_AUTH, - args=[cs.SASL_STATUS_CLIENT_ACCEPTED, '', '']) + args=[cs.SASL_STATUS_CLIENT_ACCEPTED, '', {}]) else: - chan.SaslAuthentication.Respond(response) + chan.SASLAuthentication.Respond(response) - q.expect('dbus-signal', signal='StateChanged', + q.expect('dbus-signal', signal='SASLStatusChanged', interface=cs.CHANNEL_IFACE_SASL_AUTH, - args=[cs.SASL_STATUS_SUCCEEDED, '', '']) + args=[cs.SASL_STATUS_SUCCEEDED, '', {}]) q.expect('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_CONNECTED, cs.CSR_REQUESTED]) diff --git a/tests/twisted/sasl/jabber_auth.py b/tests/twisted/sasl/jabber_auth.py index e83e6c89..9dc56d95 100644 --- a/tests/twisted/sasl/jabber_auth.py +++ b/tests/twisted/sasl/jabber_auth.py @@ -9,7 +9,7 @@ import hashlib import dbus -from servicetest import EventPattern, assertEquals +from servicetest import EventPattern, assertEquals, assertSameSets from gabbletest import exec_test, JabberXmlStream, JabberAuthenticator import constants as cs from saslutil import SaslPlainAuthenticator, connect_and_get_sasl_channel, \ @@ -19,26 +19,29 @@ JID = "test@localhost" PASSWORD = "pass" def test_jabber_pass_success(q, bus, conn, stream): - chan, auth_info, avail_mechs = connect_and_get_sasl_channel(q, bus, conn) + chan, props = connect_and_get_sasl_channel(q, bus, conn) - assertEquals ( - avail_mechs, ['X-WOCKY-JABBER-PASSWORD', 'X-WOCKY-JABBER-DIGEST']) + assertSameSets(['X-WOCKY-JABBER-PASSWORD', 'X-WOCKY-JABBER-DIGEST'], + props.get(cs.SASL_AVAILABLE_MECHANISMS)) - assert auth_info.has_key('session-id') + context = props.get(cs.SASL_CONTEXT) - digest = hashlib.sha1(auth_info['session-id'] + PASSWORD).hexdigest() + assert context.has_key('jabber-stream-id') - chan.SaslAuthentication.StartMechanism('X-WOCKY-JABBER-DIGEST', digest) + digest = hashlib.sha1(context['jabber-stream-id'] + PASSWORD).hexdigest() - q.expect('dbus-signal', signal='StateChanged', + chan.SASLAuthentication.StartMechanismWithData('X-WOCKY-JABBER-DIGEST', + digest) + + q.expect('dbus-signal', signal='SASLStatusChanged', interface=cs.CHANNEL_IFACE_SASL_AUTH, - args=[cs.SASL_STATUS_SERVER_SUCCEEDED, '', '']) + args=[cs.SASL_STATUS_SERVER_SUCCEEDED, '', {}]) - chan.SaslAuthentication.Accept() + chan.SASLAuthentication.AcceptSASL() - q.expect('dbus-signal', signal='StateChanged', + q.expect('dbus-signal', signal='SASLStatusChanged', interface=cs.CHANNEL_IFACE_SASL_AUTH, - args=[cs.SASL_STATUS_SUCCEEDED, '', '']) + args=[cs.SASL_STATUS_SUCCEEDED, '', {}]) e = q.expect('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_CONNECTED, cs.CSR_REQUESTED]) diff --git a/tests/twisted/sasl/plain.py b/tests/twisted/sasl/plain.py index 9b7d0de2..15adf59e 100644 --- a/tests/twisted/sasl/plain.py +++ b/tests/twisted/sasl/plain.py @@ -9,7 +9,7 @@ from base64 import b64decode import dbus -from servicetest import EventPattern, assertEquals +from servicetest import EventPattern, assertEquals, assertContains from gabbletest import exec_test import constants as cs from saslutil import SaslPlainAuthenticator, connect_and_get_sasl_channel, \ @@ -19,59 +19,62 @@ JID = "test@example.org" PASSWORD = "pass" def test_plain_success(q, bus, conn, stream): - chan, auth_info, avail_mechs = connect_and_get_sasl_channel(q, bus, conn) + chan, props = connect_and_get_sasl_channel(q, bus, conn) - assertEquals('@'.join((auth_info['username'], auth_info['realm'])), JID) + # FIXME: is this realistic? I'd expect this to be the JID + assertEquals('test', props.get(cs.SASL_AUTHORIZATION_IDENTITY)) - chan.SaslAuthentication.StartMechanism( + # On some servers we can't do DIGEST auth without this information. + assertEquals('example.org', props.get(cs.SASL_DEFAULT_REALM)) + + chan.SASLAuthentication.StartMechanismWithData( 'PLAIN', '\0' + JID.split('@')[0] + '\0' + PASSWORD) - q.expect('dbus-signal', signal='StateChanged', + q.expect('dbus-signal', signal='SASLStatusChanged', interface=cs.CHANNEL_IFACE_SASL_AUTH, - args=[cs.SASL_STATUS_SERVER_SUCCEEDED, '', '']) + args=[cs.SASL_STATUS_SERVER_SUCCEEDED, '', {}]) - chan.SaslAuthentication.Accept() + chan.SASLAuthentication.AcceptSASL() - q.expect('dbus-signal', signal='StateChanged', + q.expect('dbus-signal', signal='SASLStatusChanged', interface=cs.CHANNEL_IFACE_SASL_AUTH, - args=[cs.SASL_STATUS_SUCCEEDED, '', '']) + args=[cs.SASL_STATUS_SUCCEEDED, '', {}]) e = q.expect('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_CONNECTED, cs.CSR_REQUESTED]) def test_plain_no_account(q, bus, conn, stream): - chan, auth_info, avail_mechs = connect_and_get_sasl_channel(q, bus, conn) + chan, props = connect_and_get_sasl_channel(q, bus, conn) - chan.SaslAuthentication.StartMechanism( + chan.SASLAuthentication.StartMechanismWithData( 'PLAIN', '\0' + JID.split('@')[0] + '\0' + PASSWORD) - q.expect('dbus-signal', signal='StateChanged', + q.expect('dbus-signal', signal='SASLStatusChanged', interface=cs.CHANNEL_IFACE_SASL_AUTH, - args=[cs.SASL_STATUS_SERVER_SUCCEEDED, '', '']) + args=[cs.SASL_STATUS_SERVER_SUCCEEDED, '', {}]) - chan.SaslAuthentication.Accept() + chan.SASLAuthentication.AcceptSASL() - q.expect('dbus-signal', signal='StateChanged', + q.expect('dbus-signal', signal='SASLStatusChanged', interface=cs.CHANNEL_IFACE_SASL_AUTH, - args=[cs.SASL_STATUS_SUCCEEDED, '', '']) + args=[cs.SASL_STATUS_SUCCEEDED, '', {}]) e = q.expect('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_CONNECTED, cs.CSR_REQUESTED]) def test_plain_fail(q, bus, conn, stream): - chan, auth_info, avail_mechs = connect_and_get_sasl_channel(q, bus, conn) - - assertEquals('@'.join((auth_info['username'], auth_info['realm'])), JID) + chan, props = connect_and_get_sasl_channel(q, bus, conn) - chan.SaslAuthentication.StartMechanism( + chan.SASLAuthentication.StartMechanismWithData( 'PLAIN', '\0' + JID.split('@')[0] + '\0' + 'wrong') - e = q.expect('dbus-signal', signal='StateChanged', + e = q.expect('dbus-signal', signal='SASLStatusChanged', interface=cs.CHANNEL_IFACE_SASL_AUTH) assertEquals(e.args[:2], [cs.SASL_STATUS_SERVER_FAILED, cs.AUTHENTICATION_FAILED]) + assertContains('debug-message', e.args[2]) e = q.expect('dbus-signal', signal='ConnectionError') assertEquals(cs.AUTHENTICATION_FAILED, e.args[0]) @@ -79,48 +82,46 @@ def test_plain_fail(q, bus, conn, stream): args=[cs.CONN_STATUS_DISCONNECTED, cs.CSR_AUTHENTICATION_FAILED]) def test_plain_abort(q, bus, conn, stream): - chan, auth_info, avail_mechs = connect_and_get_sasl_channel(q, bus, conn) - - assertEquals('@'.join((auth_info['username'], auth_info['realm'])), JID) + chan, props = connect_and_get_sasl_channel(q, bus, conn) - chan.SaslAuthentication.StartMechanism( + chan.SASLAuthentication.StartMechanismWithData( 'PLAIN', '\0' + JID.split('@')[0] + '\0' + PASSWORD) - q.expect('dbus-signal', signal='StateChanged', + q.expect('dbus-signal', signal='SASLStatusChanged', interface=cs.CHANNEL_IFACE_SASL_AUTH, - args=[cs.SASL_STATUS_SERVER_SUCCEEDED, '', '']) + args=[cs.SASL_STATUS_SERVER_SUCCEEDED, '', {}]) abort_auth(q, chan, cs.SASL_ABORT_REASON_INVALID_CHALLENGE, "Something is fishy") def test_bad_usage(q, bus, conn, stream): - chan, auth_info, avail_mechs = connect_and_get_sasl_channel(q, bus, conn) + chan, props = connect_and_get_sasl_channel(q, bus, conn) try: - chan.SaslAuthentication.Respond("This is uncalled for"); + chan.SASLAuthentication.Respond("This is uncalled for"); except dbus.DBusException, e: assertEquals (e.get_dbus_name(), cs.NOT_AVAILABLE) else: raise AssertionError, \ "Calling Respond() before StartMechanism() should raise an error." - chan.SaslAuthentication.StartMechanism( + chan.SASLAuthentication.StartMechanismWithData( 'PLAIN', '\0' + JID.split('@')[0] + '\0' + PASSWORD) try: - chan.SaslAuthentication.StartMechanism('PLAIN', "foo") + chan.SASLAuthentication.StartMechanismWithData('PLAIN', "foo") except dbus.DBusException, e: assertEquals (e.get_dbus_name(), cs.NOT_AVAILABLE) else: raise AssertionError, \ - "Calling StartMechanism() twice should raise an error." + "Calling StartMechanismWithData() twice should raise an error." - q.expect('dbus-signal', signal='StateChanged', + q.expect('dbus-signal', signal='SASLStatusChanged', interface=cs.CHANNEL_IFACE_SASL_AUTH, - args=[cs.SASL_STATUS_SERVER_SUCCEEDED, '', '']) + args=[cs.SASL_STATUS_SERVER_SUCCEEDED, '', {}]) try: - chan.SaslAuthentication.Respond("Responding after success"); + chan.SASLAuthentication.Respond("Responding after success"); except dbus.DBusException, e: assertEquals (e.get_dbus_name(), cs.NOT_AVAILABLE) else: diff --git a/tests/twisted/sasl/saslutil.py b/tests/twisted/sasl/saslutil.py index ad037b8d..a6e05d93 100644 --- a/tests/twisted/sasl/saslutil.py +++ b/tests/twisted/sasl/saslutil.py @@ -4,12 +4,12 @@ from base64 import b64decode, b64encode from twisted.words.xish import domish import constants as cs import ns -from servicetest import ProxyWrapper, EventPattern, assertEquals +from servicetest import ProxyWrapper, EventPattern, assertEquals, assertLength class SaslChannelWrapper(ProxyWrapper): def __init__(self, object, default=cs.CHANNEL, interfaces={ "ServerAuthentication" : cs.CHANNEL_TYPE_SERVER_AUTHENTICATION, - "SaslAuthentication" : cs.CHANNEL_IFACE_SASL_AUTH}): + "SASLAuthentication" : cs.CHANNEL_IFACE_SASL_AUTH}): ProxyWrapper.__init__(self, object, default, interfaces) class SaslComplexAuthenticator(XmppAuthenticator): @@ -121,41 +121,44 @@ def connect_and_get_sasl_channel(q, bus, conn): args=[cs.CONN_STATUS_CONNECTING, cs.CSR_REQUESTED]) old_signal, new_signal = q.expect_many( - EventPattern('dbus-signal', signal='NewChannel'), - EventPattern('dbus-signal', signal='NewChannels')) + EventPattern('dbus-signal', signal='NewChannel', + predicate=lambda e: + e.args[1] == cs.CHANNEL_TYPE_SERVER_AUTHENTICATION), + EventPattern('dbus-signal', signal='NewChannels', + predicate=lambda e: + e.args[0][0][1].get(cs.CHANNEL_TYPE) == + cs.CHANNEL_TYPE_SERVER_AUTHENTICATION), + ) path, type, handle_type, handle, suppress_handler = old_signal.args chan = SaslChannelWrapper(bus.get_object(conn.bus_name, path)) + assertLength(1, new_signal.args[0]) + assertEquals(path, new_signal.args[0][0][0]) + props = new_signal.args[0][0][1] - auth_info = {} - avail_mechs = [] - for obj_path, props in new_signal.args[0]: - if props[cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_SERVER_AUTHENTICATION: - assertEquals (props[cs.AUTH_METHOD], cs.AUTH_TYPE_SASL) - info = props[cs.AUTH_INFO] - mechs = props[cs.SASL_AVAILABLE_MECHANISMS] - return chan, info, mechs - - raise AssertionError, "SASL channel not created." + assertEquals(cs.CHANNEL_IFACE_SASL_AUTH, props.get(cs.AUTH_METHOD)) + return chan, props def abort_auth(q, chan, reason, message): reason_err_map = { cs.SASL_ABORT_REASON_USER_ABORT : cs.CANCELLED, cs.SASL_ABORT_REASON_INVALID_CHALLENGE : cs.AUTHENTICATION_FAILED} - chan.SaslAuthentication.Abort(reason, message) + chan.SASLAuthentication.AbortSASL(reason, message) - q.expect_many( + ssc, _, _ = q.expect_many( EventPattern( - 'dbus-signal', signal='StateChanged', + 'dbus-signal', signal='SASLStatusChanged', interface=cs.CHANNEL_IFACE_SASL_AUTH, - args=[cs.SASL_STATUS_CLIENT_FAILED, - reason_err_map[reason], - message]), + predicate=lambda e:e.args[0] == cs.SASL_STATUS_CLIENT_FAILED), EventPattern('dbus-signal', signal='ConnectionError', predicate=lambda e: e.args[0] == reason_err_map[reason]), EventPattern( 'dbus-signal', signal="StatusChanged", args=[cs.CONN_STATUS_DISCONNECTED, cs.CSR_AUTHENTICATION_FAILED])) + + assertEquals(cs.SASL_STATUS_CLIENT_FAILED, ssc.args[0]) + assertEquals(reason_err_map[reason], ssc.args[1]) + assertEquals(message, ssc.args[2].get('debug-message')), |