DUE TO SPAM, SIGN-UP IS DISABLED. Goto Selfserve wiki signup and request an account.
The Qpid Proton C transport code is responsible for receiving and sending all the protocol bytes into and out of the Proton-C engine. It encapsulates 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:
- SSL
- SASL
- AMQP
Loosely speaking, SSL is mostly associated with encrypting the connection, SASL with authenticating the connection, and of course AMQP carries the actual messaging protocol we're "really" interested in. However despite common knowledge, actually SSL and SSL can both do encryption and authentication 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 further later on.
In the current Proton codebase (0.8) the SSL layer is fairly comprehensively implemented (using the OpenSSL and Windows SChannel implementations) and allows access to both encryption and authentication (although SSL authentication is somewhat complex to set up and use).
Conversely the SASL layer is very rudimentary and essentially punts to the 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.8 SASL codebase requires the application to directly read the SASL protocol 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 ANONYMOUS, EXTERNAL, PLAIN, DIGEST-MD5, CRAM-MD5, GSSAPI etc.) and which is extensible 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 encryption with the transport by unifying the most commonly used concepts into the transport code and only requiring delving directly into the SSL/SASL APIs for more complex uses.
API Changes
From the authentication point of view the API flow is radically simpler:
Instead of the current situation where the application has to directly read and write AMQP SASL frames using pn_sasl_recv(), pn_sasl_send() etc. the meat of the protocol interchange happens behind the scenes and instead the application has to set up authentication parameters and then allow the protocol handling loop to handle SASL without application intervention.
At the client the parameters that can be set up include username/password (with pn_transport_set_user_password() ), if necessary the SASL mechanisms used can be affected to either force SASL to use the ANONYMOUS mechanism (pn_sasl_force_anonymous() ) or to exclude some mechanisms offered by the server (pn_sasl_exclude_mechs() ).
By default the server will adapt to whatever layers the client attempts to use to communicate. But the 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 mechanism or to exclude some mechanisms that might be installed on the system (by using pn_sasl_force_anonymous() or pn_sasl_exclude_mechs() ). The location and name of the configuration file used by the SASL implementation can be changed by using the pn_sasl_config_name() and pn_sasl_config_path() APIs.
SASL
The biggest functional changes happen in the SASL layer code where the API has largely changed, very little backwards compatibility has been kept, because it is fairly clear that very few (if any) people have been using the current API to implement their own SASL mechanisms. If this turns out to be untrue, then we could add some further measure of backwards compatibility as required.
Functions Removed
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 or server, they have been deprecated since functionality went into the transport code to specify whether it is an authentication client or server. Currently, 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 deprecated 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
void pn_sasl_mechanisms(pn_sasl_t *sasl, const char *mechanisms)
const char *pn_sasl_remote_mechanisms(pn_sasl_t *sasl)
These functions
void pn_sasl_plain(pn_sasl_t *sasl, const char *username, const char *password)
This
Functions Added
/** Retrieve the authenticated user** 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 was passed in to the* pn_transport_set_user_password() API.** If pn_sasl_outcome() returns a value other than PN_SASL_OK, then there will be no user to return.* The returned value is only reliable after the PN_TRANSPORT_AUTHENTICATED event has been received.** @param[in] sasl the sasl layer** @return* If the SASL layer was not negotiated then 0 is returned* If the ANONYMOUS mechanism is used then the user will be "anonymous"* Otherwise a string containing the user is returned.*/PN_EXTERN const char *pn_sasl_get_user(pn_sasl_t *sasl);/** Return the selected SASL mechanism** The returned value is only reliable after the PN_TRANSPORT_AUTHENTICATED event has been received.** @param[in] sasl the SASL layer** @return The authentication mechanism selected by the SASL layer*/PN_EXTERN const char *pn_sasl_get_mech(pn_sasl_t *sasl);/** SASL mechanism that are not to be considered for authentication** This can be used on either the client or the server to restrict the SASL mechanisms that may be used.** @param[in] sasl the SASL layer* @param[in] mechs space separated list of mechanisms that are not to be allowed for authentication*/PN_EXTERN void pn_sasl_exclude_mechs(pn_sasl_t *sasl, const char *mechs);/*** Set the sasl configuration name** This is used to construct the SASL configuration filename. In the current implementation* it ".conf" is added to the name and the file is looked for in the configuration directory.** If not set it will default to "proton-server" for a sasl server and "proton-client"* for a client.** @param[in] sasl the SASL layer* @param[in] name the configuration name*/PN_EXTERN void pn_sasl_config_name(pn_sasl_t *sasl, const char *name);/*** Set the sasl configuration path** This is used to tell SASL where to look for the configuration file.* In the current implementation it can be a colon separated list of directories.** The environment variable PN_SASL_CONFIG_PATH can also be used to set this path,* but if both methods are used then this pn_sasl_config_path() will take precedence.** If not set the underlying implementation default will be used.* for a client.** @param[in] sasl the SASL layer* @param[in] path the configuration path*/PN_EXTERN void pn_sasl_config_path(pn_sasl_t *sasl, const char *path);
Transport
Functions Added
/*** Set the authentication username and password for a client transport
*
* If not set then no authentication will be negotiated unless the client
* sasl layer is explicitly created (this would be for sometting like Kerberos
* where the credentials are implicit in the environment, or to explicitly use
* the ANONYMOUS SASL mechanism)
*
* @param[in] transport the transport
* @param[in] user the username
* @param[in] password the password corresponding to the username - this will be copied and zeroed out after use
*/
PN_EXTERN void pn_transport_set_user_password(pn_transport_t *transport, const char *user, const char* password);
/** Retrieve the authenticated user
*
* 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 was passed in to the
* pn_transport_set_user_password() API.
*
* The returned value is only reliable after the PN_TRANSPORT_AUTHENTICATED event has been received.
*
* @param[in] transport the transport
*
* @return
* If a the user is anonymous (either no SASL layer is negotiated or the SASL ANONYMOUS mechanism is used)
* then the user will be "anonymous"
* Otherwise a string containing the user is returned.
*/
PN_EXTERN const char *pn_transport_get_user(pn_transport_t *transport);
/**
* Set the remote FQDN
*
* @param[in] transport the transport
* @param[in] fqdn the remotes Fully qualified domain name
*/
PN_EXTERN void pn_transport_set_remote_hostname(pn_transport_t *transport, const char* fqdn);
/**
* Set whether a non authenticated transport connection is allowed
*
* There are several ways within the AMQP protocol suite to get unauthenticated connections:
* - Use no SASL layer (with either no TLS or TLS without client certificates)
* - Use an SASL layer but the ANONYMOUS mechanism
*
* The default if this option is not set is to allow unauthenticated connections.
*
* @param[in] transport the transport
* @param[in] required boolean is true when authenticated connections are required
*/
PN_EXTERN void pn_transport_require_auth(pn_transport_t *transport, bool required);
/**
* Set whether a non encrypted transport connection is allowed
*
* There are several ways within the AMQP protocol suite to get encrypted connections:
* - Use TLS/SSL
* - Use an SASL with a mechanism that supports saecurity layers
*
* The default if this option is not set is to allow unencrypted connections.
*
* @param[in] transport the transport
* @param[in] required boolean is true when encrypted connections are required
*/
PN_EXTERN void pn_transport_require_encryption(pn_transport_t *transport, bool required);
New Events
- PN_TRANSPORT_AUTHENTICATED
Authentication and Encryption Combinations
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 connection and likewise double authentication would be using both for authentication.
Classically in protocols where SASL was not optional the way to avoid double authentication was to use the EXTERNAL SASL mechanism. With AMQP, SASL is optional, so if SSL is used for client authentication the SASL layer could be entirely omitted and so using EXTERNAL is not necessary.
Similarly in protocols where the SASL layer is not optional the ANONYMOUS mechanism is used when client authentication is not required. With AMQP as the SASL layer can just be entirely omitted, and the SSL layer if present used only for encryption (and server authentication).
↓Auth\Encryp→ | None | SSL | SASL |
|---|---|---|---|
| None | 1. | Privacy only | |
| SSL | 2. | ||
| SASL | Auth only | 4. | 3. |
In this table "auth" means client authentication.
- Entries in pink aren't allowed/don't make any sense
- All other entries have some use
The numbered boxes are the combinations that are reasonably important:
- Anonymous, "public" connection
- Encrypted connection with client certificate
- Kerberos or DIGEST-MD5
- Usually SSL encryption with PLAIN authentication.
- All of the SSL encryption possibilities have some use and all could be used to authenticate the server for the client to avoid "man-in-the-middle" attacks.
- The right 2 columns correspond to an encrypted connection
- The bottom 2 rows correspond to an authenticated connection