Date: Tue, 19 Mar 2024 05:45:05 +0000 (UTC) Message-ID: <391430631.54379.1710827105003@cwiki-he-fi.apache.org> Subject: Exported From Confluence MIME-Version: 1.0 Content-Type: multipart/related; boundary="----=_Part_54378_2143125301.1710827105003" ------=_Part_54378_2143125301.1710827105003 Content-Type: text/html; charset=UTF-8 Content-Transfer-Encoding: quoted-printable Content-Location: file:///C:/exported.html
The Qpid Proton C transport code is responsible for receiving an= d sending all the protocol bytes into and out of the Proton-C engine. It en= capsulates all the protocol processing. Effectively turning the wire level = protocol into more abstract AMQP model state changes and vice versa turning= AMQP model state changes into wire level protocol to send.
The transport code is divided into 3 layers that divide up the protocol = processing:
Loosely speaking, SSL is mostly associated with encrypting the connectio= n, SASL with authenticating the connection, and of course AMQP carries the = actual messaging protocol we're "really" interested in. However despite com= mon knowledge, actually SSL and SASL can both do encryption and authenticat= ion of the connection. It's important to note that the SSL and SASL layers = are optional in the sense that if authentication and/or encryption are not = required then they need not be used - This will be covered in more detail l= ater on.
In the current Proton codebase (0.8) the SSL layer is fairly comprehensi= vely implemented (using the OpenSSL and Windows SChannel implementations) a= nd allows access to both encryption and authentication (although SSL authen= tication is somewhat complex to set up and use).
Conversely the SASL layer is very rudimentary and essentially punts to t= he application to handle the SASL protocol interchange. The SASL code does = have some built in capability to use the ANONYMOUS mechanism and some code = to simplify implementing the PLAIN SASL protocol exchange. Using the Proton= -C 0.9 SASL codebase requires the application to directly read the SASL pro= tocol bytes from the peer process them and send raw protocol bytes back to = the peer.
I have been re-implementing the SASL layer using the Cyrus-SASL library = which supports many SASL mechanisms via a range of plugins (including ANONY= MOUS, EXTERNAL, PLAIN, DIGEST-MD5, CRAM-MD5, GSSAPI etc.) and which is exte= nsible to new SASL mechanisms by writing new mechanism plugins.
This work has led me re-evaluate some of the transport API as a whole as= well as more specifically the SASL API which more directly needs reworking= because of these changes.
Specifically I'd like to somewhat simplify using authentication and encr= yption with the transport by unifying the most commonly used concepts into = the transport code and only requiring delving directly into the SSL/SASL AP= Is for more complex uses.
From the authentication point of view the API flow is much simpler than = before:
Instead of the Proton-C 0.9 situation where the application has to direc=
tly read and write AMQP SASL frames using pn_sasl_recv()
,
At the client the parameters that can be set up include username/passwor=
d (with pn_connection_set_user/pn_connection_set_password()
),=
if necessary the SASL mechanisms used can be set (pn_sasl_allowed_me=
chs()
).
By default the server will adapt to whatever layers the client uses to c=
ommunicate. But the server application can specify that the connection must=
be encrypted or authenticated (using pn_transport_require_encryption=
(
) and pn_transport_require_auth()
). Also if required =
the server application can force the SASL layer to use the ANONYMOUS mechan=
ism or to exclude some mechanisms that might be installed on the system (by=
using pn_sasl_force_anonymous()
or pn_sasl_exclude_mech=
s()
). The location and name of the configuration file used by the S=
ASL implementation can be changed by using the pn_sasl_config_name()<=
/code> and
pn_sasl_config_path()
APIs.
The outcome of the authentication process at both server and client side=
s is indicated by a transport event. In the case of authentication failure =
it will be a PN_TRANSPORT_ERROR
event, and the case of success=
it will a PN_TRANSPORT_AUTHENTICATED event. Once the authentication event =
has been received the server can discover who it is talking to by using the=
pn_transport_get_user()
API. If required the mechanism used c=
an be recovered using pn_sasl_get_mech()
.
Note that on the server you can also use the PN_CONNECTION_REMOTE_OPEN e= vent to signal that authentication has succeeded if you don't need to deal = with authentication by itself.
The biggest functional changes happen in the SASL layer code where the A= PI has largely changed, very little backwards compatibility has been kept, = because it is fairly clear that very few (if any) people have been using th= e current API to implement their own SASL mechanisms.
If this turns out to be untrue, then we could add some further measure o= f backwards compatibility as required.
void pn_sasl_client(pn_sasl_t *sasl)
void pn_sasl_server(pn_sasl_t *sasl)
These functions were used to specify the SASL layer as either a client o= r server, they have been deprecated since functionality went into the trans= port code to specify whether it is an authentication client or server. Curr= ently, on creation, the SASL layer will determine from the transport = whether it should be a server or a client. As there is a good deal of SASL = churn in these current changes this is a good opportunity to remove these d= eprecated APIs.
pn_sasl_state_t pn_sasl_state(pn_sasl_t *sasl)
size_t pn_sasl_pending(pn_sasl_t *sasl)
ssize_t pn_sasl_recv(pn_sasl_t *sasl, char *bytes, size_t size=
)
ssize_t pn_sasl_send(pn_sasl_t *sasl, const char *bytes, size_=
t size)
These functions are the ones which support the client directly reading a= nd writing the SASL protocol frames. They are not needed as this functional= ity has been removed and superseded by internal functionality.
void pn_sasl_mechanisms(pn_sasl_t *sasl, const char *mechanism=
s)
const char *pn_sasl_remote_mechanisms(pn_sasl_t *sasl)
<=
/p>
These functionality here has been replaced by pn_sasl_exclude_mech=
s()
and pn_sasl_get_mech()
.
void pn_sasl_plain(pn_sasl_t *sasl, const char *username, cons=
t char *password)
This function is a helper function that constructs and sends a SASL PLAI=
N mechanism frame. As directly sending the frames is no longer the responsi=
bility of the application it is not needed, however it is broadly replaced =
with pn_transport_set_user_password()
which allows the client =
to set the authentication username and password.
void pn_sasl_allow_skip(pn_sasl_t *sasl, bool allow)
This function is replaced with
although the sense is =
reversed. By default anonymous connections are allowed. This is not really =
a change as previously there was no API guard against the SASL ANONYMOUS me=
chanism which is no better authenticated than not using SASL at all.pn_transport_require_auth(pn_=
transport_t *transport, bool required)
const char *pn_sasl_get_user(pn_sasl_t *sasl)
This is u=
sually used at the the server end to find the name of the authenticated use=
r. On the client it will merely return whatever user was passed in to pn_transport_set_user_password()
.PN_TRANSPORT_AUTHENTICA=
TED
event has been received. If the SASL layer was not negotiated th=
en 0 is returned If the ANONYMOUS mechanism is used then the user will be "=
anonymous" Otherwise a string containing the authenticated user is returned=
.const char *pn_sasl_get_mech(pn_sasl_t *sasl)
PN_TRANSPORT_AUTHENTICATED
event has been received.void pn_sasl_allowed_mechs(pn_sasl_t *sasl, const char *mechs)
Specify SASL mechanisms that are to be considered for authenticatio=
n. This can be used on either the client or the server to restrict the SASL=
mechanisms that may be used.void pn_sasl_config_name(pn_sasl_t *sasl, const char *name)
void pn_sasl_config_path(pn_sasl_t *sasl, const char *path)
const char *pn_transport_get_user(pn_transport_t *transport)=
code>
This is usually used at the the server end to find the name of =
the authenticated user, On the client it will merely return whatever user w=
as passed in to pn_connection_set_user()
.
The returned v= alue is only reliable after the PN_TRANSPORT_AUTHENTICATED event has been r= eceived.
If a the user is anonymous (either no SASL layer is negotiat= ed or the SASL ANONYMOUS mechanism is used) then the user will be "anonymou= s", otherwise a string containing the user is returned.
void pn_transport_require_auth(pn_transport_t *transport=
, bool required)
Set whether a non authenticated transp= ort connection is allowed.
There are several ways within the AMQP pro= tocol suite to get unauthenticated connections:
Use no SASL layer (with either no SSL or SSL without client certific= ates)
Use an SASL layer but the ANONYMOUS mechanism
The default if this option is not set is = to allow unauthenticated connections.
bool pn_transport_is_authenticated(pn_transport_t *transport)
Returns whether the transport is authenticated.
This property may not be stable until after the PN_CONNECTION_REMOTE_OPEN e=
vent is received.
void pn_transport_require_encryption(pn_transport_t *transport, b=
ool required)
Set whether a non encrypted transport connection is=
allowed.bool pn_transport_is_encrypted(pn_transport_t *transport)
<=
br>
Returns whether the transport is encrypted.void pn_connection_set_user(pn_connection_t *connection,=
const char *user)
Set the authentication username for = a client transport
If not set then no authentication will be negotiat= ed unless the client sasl layer is explicitly created (this would be for so= mething like Kerberos where the credentials are implicit in the environment= , or to explicitly use the ANONYMOUS SASL mechanism).
void pn_connection_set_password(pn_connection_t *connect=
ion, const char* password)
Set the authentication passw= ord for a client transport. Note that there is no way to retrieve the passw= ord from the API as them implementation is supposed to use it and then zero= it out of memory as soon as it is finished with it.
pn_connection_get_=
user(pn_connection_t *connection)const char *
Get the authentication username for a client transport.
PN_TRANSPORT_ERROR event.
This table summarises the possible combinations of SSL and SASL use. The= implicit assumption of the table is that there is no point in doing double= encryption or double authentication so this is not captured in the table. = Double encryption would be using both SSL and SASL to encrypt the connectio= n and likewise double authentication would be using both for authentication= .
Classically in protocols where SASL was not optional the way to avoid do= uble authentication was to use the EXTERNAL SASL mechanism. With AMQP, SASL= is optional, so if SSL is used for client authentication the SASL layer co= uld be entirely omitted and so using EXTERNAL is not necessary.
Similarly in protocols where the SASL layer is not optional the ANONYMOU= S mechanism is used when client authentication is not required. With AMQP a= s the SASL layer can just be entirely omitted, and the SSL layer if present= used only for encryption (and server authentication).
=E2=86=93Auth\Encryp=E2=86=92 |
None | SSL | SASL |
---|---|---|---|
None | = 1. | Privacy only | &nbs= p; |
SSL | &nbs= p; | = 2. | &nbs= p; |
SASL | Auth only | = 4. | = 3. |
In this table "auth" means client authentication.
The numbered boxes are the combinations that are reasonably important:= p>
A substantial portion of this work is complete and ready to be included = for the 0.10 Proton release (subject to code review of course). You can fol= low along with my changes by looking in my Github Proton repository:
htt= ps://github.com/astitcher/qpid-proton/commits/PROTON-334
The items of work that will remain to be completed are:
pn_transport_requir=
e_encryption()
is not implemented.