RFC 4976
Relay Extensions for the Message Sessions Relay Protocol (MSRP)
RFC 4976: Relay Extensions for MSRP ๐
Okay bestie, here's the deal ๐ This doc is from the Network Working Group and it's a Standards Track spec, so it's an official Internet standard, not just vibes. It's dated September 2007. The authors are C. Jennings (Cisco Systems, Inc.), R. Mahy (Plantronics), and A. B. Roach (Estacado Systems). It doesn't obsolete or update any other RFCs, according to its title block ๐ฆ
Status of This Memo โจ
This document is an Internet standards track protocol for the Internet community, and it's asking for discussion and suggestions for improvements. To find out where this protocol stands in standardization, check the current edition of the "Internet Official Protocol Standards" (STD 1) ๐ Also, distribution of this memo is unlimited, so share away ๐ธ
Abstract ๐ง
Okay so there are two ways to send instant messages. Page-mode messages just stand alone, not part of a Session Initiation Protocol (SIP) session. Session-mode messages get set up as part of a SIP session first ๐
Then there's the Message Session Relay Protocol (MSRP). It's for near real-time, peer-to-peer swapping of binary content with no middlemen, and it's meant to be set up with a separate rendezvous protocol like SIP ๐ฆ
This document adds the idea of message relay intermediaries to MSRP, and it spells out the extensions you need to actually use them. It's giving upgrade ๐ฅ
Table of Contents ๐
1. Introduction and Requirements ...................................3
2. Conventions and Definitions .....................................4
3. Protocol Overview ...............................................4
3.1. Authorization Overview ....................................11
4. New Protocol Elements ..........................................11
4.1. The AUTH Method ...........................................11
4.2. The Use-Path Header .......................................12
4.3. The HTTP Authentication "WWW-Authenticate" Header .........12
4.4. The HTTP Authentication "Authorization" Header ............12
4.5. The HTTP Authentication "Authentication-Info" Header ......12
4.6. Time-Related Headers ......................................12
5. Client Behavior ................................................13
5.1. Connecting to Relays Acting on Your Behalf ................13
5.2. Sending Requests ..........................................18
5.3. Receiving Requests ........................................18
5.4. Managing Connections ......................................18
6. Relay Behavior .................................................18
6.1. Handling Incoming Connections .............................18
6.2. Generic Request Behavior ..................................19
6.3. Receiving AUTH Requests ...................................19
6.4. Forwarding ................................................20
6.4.1. Forwarding SEND Requests ...........................21
6.4.2. Forwarding Non-SEND Requests .......................22
6.4.3. Handling Responses .................................22
6.5. Managing Connections ......................................23
7. Formal Syntax ..................................................23
8. Finding MSRP Relays ............................................24
9. Security Considerations ........................................25
9.1. Using HTTP Authentication .................................25
9.2. Using TLS .................................................26
9.3. Threat Model ..............................................27
9.4. Security Mechanism ........................................29
10. IANA Considerations ...........................................31
10.1. New MSRP Method ..........................................31
10.2. New MSRP Headers .........................................31
10.3. New MSRP Response Codes ..................................31
11. Example SDP with Multiple Hops ................................31
12. Acknowledgments ...............................................32
13. References ....................................................32
13.1. Normative References .....................................32
13.2. Informative References ...................................33
Appendix A. Implementation Considerations ........................34
1. Introduction and Requirements ๐
There are a bunch of situations where a separate protocol for bulk messaging is the move. The big one: you want a sequence of messages to be treated as a media session started with SIP [8], just like any other media type ๐ MSRP [11] is what carries a session of messages directly between two end systems, with no intermediaries. Messages can be arbitrarily large, and all the traffic goes over reliable, congestion-safe transports โจ
This document extends core MSRP with intermediaries called relays ๐ง With these extensions, MSRP clients can talk directly or through an arbitrary number of relays. Each client is responsible for figuring out which relays act on its behalf and giving them the right credentials. Clients that can take new TCP connections directly don't need any new functionality to work with these relays ๐ซถ
The goals of the MSRP relay extensions are:
- convey arbitrary binary MIME data without modification or transfer encoding
- continue to support client-to-client operation (no relay servers required)
- operate through an arbitrary number of relays for policy enforcement
- operate through relays under differing administrative control
- allow each client to control which relays are traversed on its behalf
- prevent unsolicited messages (spam), "open relays", and Denial of Service (DoS) amplification
- allow relays to use one or a small number of TCP or TLS [2] connections to carry messages for multiple sessions, recipients, and senders
- allow large messages to be sent over slow connections without causing head-of-line blocking problems
- allow transmissions of large messages to be interrupted and resumed in places where network connectivity is lost and later reestablished
- offer notification of message failure at any intermediary
- allow relays to delete state after a short amount of time
2. Conventions and Definitions ๐
Here's the key word rule ๐ The words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document get read the way RFC 2119 [9] describes. Those capitals are the whole point, so pay attention to them ๐
Before we go further, here are some definitions that matter for MSRP:
MSRP node: a host that implements the MSRP protocols as a client or a relay.
MSRP client: an MSRP node that is the initial sender or final target of messages and delivery status.
MSRP relay: an MSRP node that forwards messages and delivery status, and may provide policy enforcement. Relays can fragment and reassemble portions of messages ๐ฆ
Message: arbitrary MIME [13][14] content that one client wants to send to another. For this spec, that means a complete MIME body, not a piece of a complete message.
chunk: a portion of a complete message delivered in a SEND request.
end-to-end: delivery of data from the initiating client to the final target client.
hop: delivery of data between one MSRP node and an adjacent node.
3. Protocol Overview ๐
With this extension, MSRP now has both clients and relays ๐ Clients send messages to relays and/or other clients. Relays forward messages and message delivery status to clients and other relays. Clients that can open TCP connections to each other, with no policy restrictions in the way, can talk directly ๐ธ Clients stuck behind firewalls, or who need intermediaries for policy reasons, can use a relay. Each client is responsible for getting one or more relays to help with its side of the conversation ๐ซถ
Clients that use a relay do the following: first they open a TLS connection with a relay, then they authenticate, then they retrieve an msrps: URI from the relay. That URI is what the client gives its peers so they can send messages later. There are several steps here ๐ง
- Open TLS. The client opens a TLS connection to its first relay and checks that the name in the certificate matches the name of the relay it's trying to reach. That check follows the procedures in Section 9.2.
- Authenticate. After confirming it's connected to the right host, the client authenticates itself to the relay with an AUTH request that carries appropriate credentials.
- Get the URI. In a successful AUTH response, the relay gives back an
msrps:URI tied to the path back to the client. The client can then hand this URI to other clients for end-to-end message delivery โจ
When clients want to send a short message, they issue a SEND request containing the entire message. If any relays are needed, they go in the To-Path header. The leftmost URI in the To-Path header is the next hop for delivering a request or response. The rightmost URI in the To-Path header is the final target ๐
SEND requests have headers that say how they get acknowledged, both hop-by-hop and end-to-end. The default is that SEND messages are acknowledged hop-by-hop. Each relay that receives a SEND request acknowledges receipt before forwarding the content to the next relay or the final target. All other requests are acknowledged end-to-end ๐ฅ
Relays make the subtle semantics of the To-Path and From-Path headers way more important. In both requests and responses, the To-Path is the list of URIs that need to be visited to reach the final target of that request or response. The From-Path is the list of URIs that show how to get back to the original sender. These headers are different from the To and From headers in SIP, which do not "swap" from request to response. (Sometimes a request is sent to or from an intermediary directly, so keep that in mind ๐)
When a relay forwards a request, it removes its own address from the To-Path and inserts it as the first URI in the From-Path. For example, say the path from Alice to Bob goes through relays A and B. When B receives the request, the path headers look like this. (MSRP doesn't allow line folding. The \ in these examples marks a line continuation, just because this doc has a limited line length. The backslash and the extra CRLF aren't in the real request or response ๐)
To-Path: msrps://B.example.com/bbb;tcp \
msrps://Bob.example.com/bob;tcp
From-Path: msrps://A.example.com/aaa;tcp \
msrps://Alice.example.com/alice;tcp
After B forwards the request, the path headers look like this:
To-Path: msrps://Bob.example.com/bob;tcp
From-Path: msrps://B.example.com/bbb;tcp \
msrps://A.example.com/aaa;tcp \
msrps://Alice.example.com/alice;tcp
โจ That's part 1 of 8 โจ The next part picks up with the rest of Section 3 ๐ฆ
The sending of an acknowledgment for SEND requests is controlled by the Success-Report and Failure-Report headers, and it works exactly the same way as in base MSRP ๐
. When a relay gets a SEND request and Failure-Report is set to "yes", that means the previous hop is running a timer, so the relay has to send back a response to the request ๐ฐ๏ธ. If the final response conveys an error, the previous hop is the one responsible for building the error report and sending it back to the original sender of the message. The 200 response is basically the "got it, bestie" moment โจ. It tells the previous hop that it's no longer responsible for the request. If the relay knows it can't deliver the request and Failure-Report is set to anything other than "no", it sends a REPORT to tell the sender about the error ๐ญ. If Failure-Report is "yes", then once the relay finishes sending the request to the next hop, it starts a timer. If that timer runs out before a response comes back from the next hop, the relay assumes something went wrong and sends a REPORT to the sender. If Failure-Report isn't set to "yes", the relay doesn't need to run this timer at all ๐ง
The following example shows a typical MSRP session ๐. The AUTH requests get explained in a later section, but they're left in the example so the call flow is complete.
Alice a.example.org b.example.net Bob
| | | |
|::::::::::::::::::::>| connection opened |<::::::::::::::::::::|
|--- AUTH ----------->| |<-- AUTH ------------|
|<-- 200 OK-----------| |--- 200 OK---------->|
| | | |
.... time passes ....
| | | |
|--- SEND ----------->| | |
|<-- 200 OK ----------|:::::::::::::::::::>| (slow link) |
| |--- SEND ---------->| |
| |<-- 200 OK ---------|--- SEND ----------->|
| | | ....>|
| | | ..>|
| | |<-- 200 OK ----------|
| | |<-- REPORT ----------|
| |<-- REPORT ---------| |
|<-- REPORT ----------| | |
| | | |
The SEND and REPORT messages are shown below so we can see the To-Path and From-Path headers in action ๐. (Heads up: MSRP does not permit line folding. A \ in the examples shows a line continuation, only because this document has limited line length. Neither the backslash nor the extra CRLF is included in the actual request or response ๐.)
MSRP 6aef SEND
To-Path: msrps://a.example.org:9000/kjfjan;tcp \
msrps://b.example.net:9000/aeiug;tcp \
msrps://bob.example.net:8145/foo;tcp
From-Path: msrps://alice.example.org:7965/bar;tcp
Success-Report: yes
Byte-Range: 1-*/*
Message-ID: 87652
Content-Type: text/plain
Hi Bob, I'm about to send you file.mpeg
-------6aef$
MSRP 6aef 200 OK
To-Path: msrps://alice.example.org:7965/bar;tcp
From-Path: msrps://a.example.org:9000/kjfjan;tcp
Message-ID: 87652
-------6aef$
MSRP juh76 SEND
To-Path: msrps://b.example.net:9000/aeiug;tcp \
msrps://bob.example.net:8145/foo;tcp
From-Path: msrps://a.example.org:9000/kjfjan;tcp \
msrps://alice.example.org:7965/bar;tcp
Success-Report: yes
Message-ID: 87652
Byte-Range: 1-*/*
Content-Type: text/plain
Hi Bob, I'm about to send you file.mpeg
-------juh76$
MSRP juh76 200 OK
To-Path: msrps://a.example.org:9000/kjfjan;tcp
From-Path: msrps://b.example.net:9000/aeiug;tcp
Message-ID: 87652
-------juh76$
MSRP xght6 SEND
To-Path: msrps://bob.example.net:8145/foo;tcp
From-Path: msrps://b.example.net:9000/aeiug;tcp \
msrps://a.example.org:9000/kjfjan;tcp \
msrps://alice.example.org:7965/bar;tcp
Success-Report: yes
Message-ID: 87652
Byte-Range: 1-*/*
Content-Type: text/plain
Hi Bob, I'm about to send you file.mpeg
-------xght6$
MSRP xght6 200 OK
To-Path: msrps://b.example.net:9000/aeiug;tcp
From-Path: msrps://bob.example.net:8145/foo;tcp
Message-ID: 87652
MSRP yh67 REPORT
To-Path: msrps://b.example.net:9000/aeiug;tcp \
msrps://a.example.org:9000/kjfjan;tcp \
msrps://alice.example.org:7965/bar;tcp
From-Path: msrps://bob.example.net:8145/foo;tcp
Message-ID: 87652
Byte-Range: 1-39/39
Status: 000 200 OK
-------yh67$
MSRP yh67 REPORT
To-Path: msrps://a.example.org:9000/kjfjan;tcp \
msrps://alice.example.org:7965/bar;tcp
From-Path: msrps://b.example.net:9000/aeiug;tcp \
msrps://bob.example.net:8145/foo;tcp
Message-ID: 87652
Byte-Range: 1-39/39
Status: 000 200 OK
-------yh67$
MSRP yh67 REPORT
To-Path: msrps://alice.example.org:7965/bar;tcp
From-Path: msrps://a.example.org:9000/kjfjan;tcp \
msrps://b.example.net:9000/aeiug;tcp \
msrps://bob.example.net:8145/foo;tcp
Message-ID: 87652
Byte-Range: 1-39/39
Status: 000 200 OK
-------yh67$
When content is big, the client can split a message into smaller pieces, so each SEND request might carry only a portion of the complete message ๐ฆ. For example, when Alice sends Bob a 4-GB file called "file.mpeg", she sends several SEND requests, each with a portion of the complete message. Relays can repack message fragments en route ๐. As the individual parts of the complete message arrive at the final destination client, the receiving client can optionally send REPORT requests to show the delivery status.
MSRP nodes can send individual portions of a complete message in multiple SEND requests. As relays receive chunks, they can reassemble or re-fragment them, as long as they resend the resulting chunks in order ๐งฉ. (Receivers still need to be prepared for out-of-order chunks, though, so stay ready ๐.) If the sender has set the Success-Report header to "yes", then once a chunk or a complete message arrives at the destination client, the destination sends a REPORT request saying that a chunk arrived end-to-end ๐. That REPORT travels back along the reverse path of the SEND request. Here's the difference: SEND requests can be acknowledged along every hop, but REPORT requests are never acknowledged ๐ซถ.
The following example shows a message being re-chunked through two relays ๐ฆ:
Alice a.example.org b.example.net Bob
| | | |
|--- SEND 1-3 ------->| | |
|<-- 200 OK ----------| | (slow link) |
|--- SEND 4-7 ------->|--- SEND 1-5 ------>| |
|<-- 200 OK ----------|<-- 200 OK ---------|--- SEND 1-3 ------->|
|--- SEND 8-10 ------>|--- SEND 6-10 ----->| ....>|
|<-- 200 OK ----------|<-- 200 OK ---------| ..>|
| | |<-- 200 OK ----------|
| | |<-- REPORT 1-3 ------|
| |<-- REPORT 1-3 -----|--- SEND 4-7 ------->|
|<-- REPORT 1-3 ------| | ...>|
| | |<-- REPORT 4-7 ----->|
| |<-- REPORT 4-7 -----|--- SEND 8-10 ------>|
|<-- REPORT 4-7 ------| | ..>|
| | |<-- 200 OK ----------|
| |<-- REPORT done-----|<-- REPORT done -----|
|<-- REPORT done -----| | |
| | | |
Relays only keep transaction state for a short time for each chunk โฑ๏ธ. Delivery over each hop should take no more than 30 seconds after the last byte of data is sent. Client applications define their own implementation-dependent timers for end-to-end message delivery.
For client-to-client communication, the sender of a message typically opens a new TCP connection (with or without TLS) if one is needed ๐. Relays reuse existing connections first, but they can open new connections (typically to other relays) to deliver requests such as SEND or REPORT. Responses can only be sent over existing connections.
The relationship between MSRP and signaling protocols (such as SIP) is unchanged by this document, and it's exactly as described in [11]. An example of an SDP exchange for an MSRP session involving relays is shown in Section 11 ๐.
3.1. Authorization Overview โจ
A key element of this protocol is that it cannot introduce open relays, and you already know why that's a disaster: all the associated problems they create, including DoS attacks ๐จ. A message is only forwarded by a relay if it's either going to or coming from a client that has authenticated to the relay and been authorized for relaying messages on that particular session. That's why clients use an AUTH message to authenticate to a relay and get a URI they can use for forwarding messages ๐๏ธ.
If a client wants to use a relay, it sends an AUTH request to the relay. The client authenticates the relay using the relay's TLS certificate. The client uses HTTP Digest authentication [1] to authenticate to the relay. When the authentication succeeds, the relay returns a 200 response containing the URI that the client can use in the MSRP path for the relay ๐ธ.
A typical challenge response flow looks like this:
Alice a.example.org
| |
|::::::::::::::::::::>|
|--- AUTH ----------->|
|<- 401 Unauthorized -|
|--- AUTH ----------->|
|<-- 200 OK-----------|
| |
The URI the client should use is returned in the Use-Path header of the 200.
Okay, real talk: URIs returned to the client are basically secret tokens ๐คซ. They should only be shared with the other MSRP client in a session. For that reason, the client MUST NOT reuse the same URI for multiple sessions, and it needs to protect these URIs from eavesdropping ๐๐.
4. New Protocol Elements โจ
4.1. The AUTH Method ๐
AUTH requests are how clients create a handle they can use to receive incoming requests ๐ฌ. AUTH requests also carry the credentials used to authenticate a client, plus the authorization policy used to block Denial of Service attacks ๐ก๏ธ.
In response to an AUTH request, a successful response contains a Use-Path header with a list of URIs. The client can give these URIs to its peers so they can route responses back to the client ๐ฆ.
4.2. The Use-Path Header ๐ฆ
okay so the Use-Path header is a list of URIs that an MSRP relay hands back in response to a successful AUTH request ๐ The MSRP client that sent the AUTH request can use this list to receive MSRP requests, and it can also advertise the list, for example in a session description ๐ URIs in Use-Path MUST include a fully qualified domain name (as opposed to a numeric IP address) and an explicit port number. no cap, that's a strict one ๐
The URIs in Use-Path are in the same order that the authenticating client uses them in a To-Path header. If you want to know how to form To-Path headers and SDP [7] path attributes from the Use-Path info, that's all in Section 5.1 ๐ง
4.3. The HTTP Authentication "WWW-Authenticate" Header ๐ธ
the WWW-Authenticate header carries a challenge token for the HTTP Digest authentication procedure (from RFC 2617 [1]) ๐ how HTTP Digest authentication gets used in MSRP is explained in detail in Section 5.1 โจ
4.4. The HTTP Authentication "Authorization" Header ๐
the Authorization header holds the authentication credentials for HTTP Digest authentication (from RFC 2617 [1]) ๐๏ธ and yep, the details of how that works in MSRP are in Section 5.1 ๐ซถ
4.5. The HTTP Authentication "Authentication-Info" Header ๐
the Authentication-Info header contains future challenges for HTTP Digest authentication (from RFC 2617 [1]) ๐ฎ the MSRP usage is described in detail in Section 5.1, so go check it out ๐ฆ
4.6. Time-Related Headers โณ
the Expires header in a request gives a relative time after which the action implied by the request's method isn't of interest anymore โฐ so in a request, Expires tells you how long the sender wants the request to stay valid ๐ in a response, it tells you how long the responder thinks that info is still relevant ๐ and for an AUTH request specifically, an Expires header shows how long the provided URIs will be valid ๐๏ธ
the Min-Expires header holds the minimum duration a server will allow in an Expires header ๐ it only shows up in 423 "Interval Out-of-Bounds" responses, so it's very much a "you're out of range, bestie" kind of moment ๐ญ and the Max-Expires header is the opposite: it holds the maximum duration a server will allow in an Expires header ๐
5. Client Behavior ๐
5.1. Connecting to Relays Acting on Your Behalf โจ
okay so if you want a relay (or a whole list of relays) to handle stuff for you, you need to send an AUTH request to each relay that's gonna act on your behalf ๐ซถ like, an org could totally have an "intra-org" relay AND an "extra-org" relay, iykyk. The inner relay tunnels your AUTH requests out to the outer relay. So the client sends an AUTH to intra-org and gets back a path for forwarding through intra-org. Then it sends a second AUTH aimed at extra-org, but routed through intra-org. intra-org forwards it along, and extra-org hands back a path so messages from somewhere else can reach extra-org, then intra-org, and then finally you ๐ Every relay authenticates the client. The client authenticates the first relay, and each relay authenticates the next one. no cap, it's a whole chain of trust ๐
Clients can be configured (usually through discovery or manual provisioning) with the list of relays they need to use. They MUST be able to form a connection to the first relay and send an AUTH command so they can get a URI to put in a To-Path header. The client can authenticate its first relay by checking the relay's TLS certificate ๐ And the client MUST authenticate itself to each of its relays using HTTP Digest authentication [1] (see Section 9.1 for details). non-negotiable bestie ๐
The relay sends back a URI, or a list of URIs, in the "Use-Path" header of a success response. Each URI SHOULD be used for only one unique session ๐ฆ These URIs get used in two places: the path attribute you send in the SDP to set up the session, and the To-Path header on your outgoing requests. To build the To-Path header, you take the list from the Use-Path header (after the outermost authentication) and append the URIs from the path attribute in the peer's session description. To build the SDP path attribute for your peer, you reverse the Use-Path list (again, after the outermost authentication) and tack your own URI onto the end โจ
Here's the example, with the same names and everything:
Client "A" has to go through its own relays "B" and "C", and then through relays "D" and "E" in domain2 to reach "F". Client "A" authenticates with "B" and "C" and eventually gets a Use-Path header containing "B C". It reverses that list (so now it's "C B"), adds its own URI (now "C B A"), and hands that to "F" in a path SDP attribute. Client "F" sends its SDP path list "D E F", which "A" appends to the Use-Path list it got, "B C". The final To-Path header is "B C D E F" ๐ง
domain 1 domain 2
---------------- -----------------
client relays relays client
A ----- B -- C -------- D -- E ----- F
Use-Path returned by C: B C
path: attribute generated by A: C B A
path: attribute received from F: D E F
To-Path header generated by A: B C D E F
The first AUTH request a client sends to a relay usually won't have an Authorization header, because there's no challenge for it to answer yet ๐ So when a relay gets an AUTH request without an Authorization header, it MUST respond with a "401 Unauthorized" response that has a WWW-Authenticate header. That WWW-Authenticate header is built the way RFC 2617 [1] describes, with the restrictions and changes from Section 9.1. Which realm the MSRP relay picks in that challenge is up to its admin policy. Since one relay doesn't have multiple protection spaces in MSRP, it's totally reasonable to always just use the relay's hostname as the realm ๐ธ
When a 401 comes back, the client SHOULD grab the realm from the WWW-Authenticate header and retry the request, this time with an Authorization header holding the right credentials for that realm. That Authorization header is built the way RFC 2617 [1] describes, with the restrictions and changes from Section 9.1.
If you want to use more than one relay, you MUST send an AUTH request to each relay you plan to use. Say client A wants messages to flow from A to the first relay, R1, and then on to a second relay, R2. A does a normal AUTH with R1. Then it does an AUTH transaction with R2 that gets routed through R1. To make this AUTH message, A sets the To-Path to msrps://R1;tcp msrps://R2;tcp. R1 forwards the request onward to R2 ๐ฆ
When the client sends an AUTH request, it MAY add an Expires header to ask for an MSRP URI that's valid for no longer than the interval it gives (a whole number of seconds). The server puts an Expires header in a successful response to show how long its URI from the Use-Path will be valid. Also, each server can return its own independent expiration time, so don't assume they all match ๐
Quick note: MSRP doesn't allow line folding. The "" in the examples is just a line continuation because this document has line length limits ๐ Neither the backslash nor the extra CRLF is actually in the real request or response, so don't copy them.
Here's the full walkthrough. Alice opens a TLS connection to intra.example.com and sends an AUTH request to start the authentication process ๐
MSRP 49fh AUTH
To-Path: msrps://alice@intra.example.com;tcp
From-Path: msrps://alice.example.com:9892/98cjs;tcp
-------49fh$
Alice's relay challenges the AUTH request ๐
MSRP 49fh 401 Unauthorized
To-Path: msrps://alice.example.com:9892/98cjs;tcp
From-Path: msrps://alice@intra.example.com;tcp
WWW-Authenticate: Digest realm="intra.example.com", qop="auth", \
nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093"
-------49fh$
Alice answers the challenge ๐ฅ
MSRP 49fi AUTH
To-Path: msrps://alice@intra.example.com;tcp
From-Path: msrps://alice.example.com:9892/98cjs;tcp
Authorization: Digest username="Alice",
realm="intra.example.com", \
nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093", \
qop=auth, nc=00000001, cnonce="0a4f113b", \
response="6629fae49393a05397450978507c4ef1"
-------49fi$
Alice's relay confirms that Alice is an authorized user โจ As a matter of local policy, it includes an "Authentication-Info" header with a new nonce value so future AUTH requests go faster ๐
MSRP 49fi 200 OK
To-Path: msrps://alice.example.com:9892/98cjs;tcp
From-Path: msrps://alice@intra.example.com;tcp
Use-Path: msrps://intra.example.com:9000/jui787s2f;tcp
Authentication-Info: nextnonce="40f2e879449675f288476d772627370a",\
rspauth="7327570c586207eca2afae94fc20903d", \
cnonce="0a4f113b", nc=00000001, qop=auth
Expires: 900
-------49fi$
Now Alice sends an AUTH request to her "external" relay through her "internal" relay, using the URI she just got. This one gets challenged too ๐
MSRP mnbvw AUTH
To-Path: msrps://intra.example.com:9000/jui787s2f;tcp \
msrps://extra.example.com;tcp
From-Path: msrps://alice.example.com:9892/98cjs;tcp
-------mnbvw$
MSRP m2nbvw AUTH
To-Path: msrps://extra.example.com;tcp
From-Path: msrps://intra.example.com:9000/jui787s2f;tcp \
msrps://alice.example.com:9892/98cjs;tcp
-------m2nbvw$
MSRP m2nbvw 401 Unauthorized
To-Path: msrps://intra.example.com:9000/jui787s2f;tcp \
msrps://alice.example.com:9892/98cjs;tcp
From-Path: msrps://extra.example.com;tcp
WWW-Authenticate: Digest realm="extra.example.com", qop="auth", \
nonce="Uumu8cAV38FGsEF31VLevIbNXj9HWO"
-------m2nbvw$
MSRP mnbvw 401 Unauthorized
To-Path: msrps://alice.example.com:9892/98cjs;tcp
From-Path: msrps://intra.example.com:9000/jui787s2f;tcp \
msrps://extra.example.com;tcp
WWW-Authenticate: Digest realm="extra.example.com", qop="auth", \
nonce="Uumu8cAV38FGsEF31VLevIbNXj9HWO"
-------mnbvw$
Alice replies to that challenge with her credentials and gets authorized to use the "external" relay ๐
MSRP m3nbvx AUTH
To-Path: msrps://intra.example.com:9000/jui787s2f;tcp \
msrps://extra.example.com;tcp
From-Path: msrps://alice.example.com:9892/98cjs;tcp
Authorization: Digest username="Alice",
realm="extra.example.com", \
nonce="Uumu8cAV38FGsEF31VLevIbNXj9HWO", \
qop=auth, nc=00000001, cnonce="85a0dca8", \
response="cb06c4a77cd90918cd7914432032e0e6"
-------m3nbvx$
MSRP m4nbvx AUTH
To-Path: msrps://extra.example.com;tcp
From-Path: msrps://intra.example.com:9000/jui787s2f;tcp \
msrps://alice.example.com:9892/98cjs;tcp
Authorization: Digest username="Alice",
realm="extra.example.com", \
nonce="Uumu8cAV38FGsEF31VLevIbNXj9HWO", \
qop=auth, nc=00000001, cnonce="85a0dca8", \
response="cb06c4a77cd90918cd7914432032e0e6"
-------m4nbvx$
MSRP m4nbvx 200 OK
To-Path: msrps://intra.example.com:9000/jui787s2f;tcp \
msrps://alice.example.com:9892/98cjs;tcp
From-Path: msrps://extra.example.com;tcp
Use-Path: msrps://intra.example.com:9000/jui787s2f;tcp \
msrps://extra.example.com:9000/mywdEe1233;tcp
Authentication-Info: nextnonce="bz8V080GEA2sLyEDpITF2AZCq7gIkc", \
rspauth="72f109ed2755d7ed0d0a213ec653b3f2", \
cnonce="85a0dca8", nc=00000001, qop=auth
Expires: 1800
-------m4nbvx$
MSRP m3nbvx 200 OK
To-Path: msrps://alice.example.com:9892/98cjs;tcp
From-Path: msrps://intra.example.com:9000/jui787s2f;tcp \
msrps://extra.example.com;tcp
Use-Path: msrps://extra.example.com:9000/mywdEe1233;tcp \
msrps://extra.example.com:9000/mywdEe1233;tcp
Authentication-Info: nextnonce="bz8V080GEA2sLyEDpITF2AZCq7gIkc", \
rspauth="72f109ed2755d7ed0d0a213ec653b3f2", \
cnonce="85a0dca8", nc=00000001, qop=auth
Expires: 1800
-------m3nbvx$
5.2. Sending Requests ๐
Good news: building SEND and REPORT requests works exactly the same whether or not relays are involved ๐ The specific steps are in Section 7 of the core MSRP protocol.
As usual, once the next-hop URI is figured out, the client MUST find the right address, port, and transport to use, and then check whether there's already a suitable existing connection to that next-hop target. If there is, the client MUST send the request over the most suitable connection. What makes a connection "suitable" MAY depend on stuff like measured load and local policy, but in most simple implementations a connection counts as suitable if it exists and is active โจ
5.3. Receiving Requests ๐ฅ
Receiving requests is the same for clients whether or not relays are involved. Easy breezy ๐
5.4. Managing Connections ๐
Clients should open a connection whenever they want to deliver a request and no suitable connection exists. For connections to relays, the client should keep a connection up until no sessions have used it for a locally defined period of time. That defaults to 5 minutes for foreign relays and one hour for the client's own relays ๐
6. Relay Behavior โจ
6.1. Handling Incoming Connections โจ
okay so when a relay gets an incoming connection on a port set up for TLS, it sends a client CertificateRequest in the same record as its ServerHello ๐ if the TLS client shows up with a certificate, the server checks it and keeps going only if it's valid and rooted in a trusted authority ๐ซถ if the TLS client doesn't bring a certificate, the server assumes it's an MSRP endpoint and kicks off Digest authentication ๐
once a TCP or TLS channel is negotiated, the server waits up to 30 seconds for an MSRP request on it. no request in that time? it closes the connection ๐ and if no successful requests get sent during this probationary period, it closes the connection too. likewise, if several unsuccessful requests are sent during the probation period and none go through successfully, the server SHOULD close the connection ๐
6.2. Generic Request Behavior ๐ธ
upon getting a new request, relays first check that the request is valid ๐ then they look at the first URI in the To-Path header and remove it if it matches a URI that belongs to the relay โจ if the request isn't addressed to the relay at all, the relay immediately drops the corresponding connection that the request came in on. bye bye ๐
6.3. Receiving AUTH Requests ๐
when a relay receives an AUTH request, the first thing it does is authenticate and authorize the previous hop and the client at the far end ๐ if there are no other relays between this relay and the client, then these are the same thing.
when the previous hop is a relay, authentication is done with TLS using mutual authentication ๐ค if the TLS client presented a host certificate, the relay checks that the subjectAltName in the certificate of the TLS client matches the hostname in the first From-Path URI. if the TLS client doesn't provide a host certificate, the relay assumes the TLS client is an MSRP client and sends it a challenge ๐ซฃ
authorization is a matter of local policy at the relay ๐ง many relays will choose to authorize all relays that can be authenticated, possibly along with a blacklisting mechanism. relays meant to operate only within a limited federation may choose to authorize only those relays whose identity appears in a provisioned list. other authorization policies may also be applied ๐
when the previous hop is a client, the previous hop is the same as the identity of the client ๐ฆ the relay checks the credentials (username and password) provided by the client in the Authorization header, and checks if this client is allowed to use the relay. if the client is not authorized, the relay returns a 403 response ๐ซ if the client asked for a particular expiration time in an Expires header, the relay needs to check that the time is acceptable to it. if it isn't, the relay returns an error containing a Min-Expires or Max-Expires header, as appropriate โฐ
next, the relay generates an MSRP URI that allows messages to be forwarded to or from this previous hop ๐ if the previous hop was a relay authenticated by mutual TLS, then the URI MUST be valid to route across any connection the relay has to the previous hop relay. if the previous hop is a client, then the URI MUST only be valid to route across the same connection over which the AUTH request was received. if the client's connection is closed and then reopened, the URI MUST be invalidated. no sneaking back in on a new connection, periodt ๐
โโ๏ธ
if the AUTH request contains an Expires header, the relay MUST ensure that the URI is invalidated after the expiry time โณ the URI MUST contain at least 64 bits of cryptographically random material so that it isn't guessable by attackers ๐ฒ if a relay is asked to forward a message for which the URI is not valid, the relay MUST discard the message, and it MAY send a REPORT saying that the AUTH URI was bad ๐
a successful AUTH response returns a Use-Path header that contains an MSRP URI the client can use ๐ it also returns an Expires header that says how long the URI will be valid, expressed as a whole number of seconds โญ
if a relay receives several unsuccessful AUTH requests from a client that is directly connected to it via TLS, the relay SHOULD terminate the corresponding connection ๐ค similarly, if a relay forwards several failed AUTH requests to the same destination, and those requests came from a client directly connected to it via TLS, the relay SHOULD terminate the corresponding connection. a remote AUTH failure can be spotted by seeing an AUTH request with an Authorization header that triggers a 401 response without a "stale=TRUE" indication ๐ these preventive measures only apply to a connection between a relay and a client, though. a relay SHOULD NOT use excessive AUTH request failures as a reason to terminate a connection with another relay ๐ซถ relays are not the drama, bestie
6.4. Forwarding ๐
before any request is forwarded, the relay MUST check that the first URI in the To-Path header corresponds to a URI this relay has created and handed out in the Use-Path header of an AUTH request ๐ง next it verifies that either 1) the next hop is the next hop back toward the client that obtained this URI, or 2) the previous hop was the correct previous hop coming from the client that obtained this URI. checks, checks, checks ๐
since transact-id values are not allowed to conflict on a given connection, a relay will generally need to construct a new transact-id value for any request it forwards ๐
6.4.1. Forwarding SEND Requests ๐จ
if an incoming SEND request contains a Failure-Report header with a value of "yes", an MSRP relay that receives that SEND request MUST respond with a final response immediately โก a 200-class response means the message fragment was received successfully, but it does NOT mean the message has been forwarded on to the next hop, so don't get it twisted ๐ the final response to the SEND MUST be sent only to the previous hop, which could be an MSRP relay or the original sender of the SEND request ๐ฌ
if the Failure-Report header is "yes", then the relay MUST run a timer to detect if transmission to the next hop fails โฑ๏ธ the timer starts when the last byte of the message has been sent to the next hop. if after 30 seconds the next hop still hasn't sent any response, then the relay MUST construct a REPORT with a status code of 408, to indicate a timeout error happened while sending the message, and send that REPORT to the original sender of the message ๐
if the Failure-Report header is "yes" or "partial", and there's a problem processing the SEND request, or an error response is received for that SEND request, then the relay MUST respond with an appropriate error response in a REPORT back to the original source of the message ๐จ
the MSRP relay MAY further break up the message fragment received in the SEND request into smaller fragments, and forward them to the next hop in separate SEND requests โ๏ธ it MAY also combine message fragments received before or after this SEND request, and forward them out in a single SEND request to the next hop identified in the To-Path header ๐งฉ but the MSRP relay MUST NOT combine message fragments from SEND requests with different values in the Message-ID header. no mixing up different messages, iykyk ๐
โโ๏ธ
the MSRP relay MAY choose whether to further fragment the message, combine message fragments, or send the message as is, based on some administered policy, the network speed to the next hop, or any other mechanism ๐
if the MSRP relay knows the byte range it will transmit to the next hop, it SHOULD update the Byte-Range header in the SEND request appropriately ๐
before forwarding the SEND request to the next hop, the MSRP relay MUST inspect the first URI in the To-Path header. if it indicates this relay, the relay removes this URI from the To-Path header and inserts this URI in the From-Path header before any other URIs ๐ช if it does not indicate this relay, there has been an error in forwarding at a previous hop. in that case, the relay SHOULD discard the message and, if the Failure-Report header is set to "yes", the relay SHOULD generate a failure report ๐ฅฒ
6.4.2. Forwarding Non-SEND Requests ๐
an MSRP relay that receives any request other than a SEND request (including new methods unknown to the relay) first follows the validation and authorization rules for all requests โ
then the relay moves its URI from the beginning of the To-Path headers to the beginning of the From-Path header, and forwards the request on to the next hop. if it already has a connection to the next hop, it SHOULD use that connection and not form a new one ๐ if no suitable connection exists, the relay opens a new connection ๐
requests with an unknown method are forwarded as if they were REPORT requests ๐คทโโ๏ธ an MSRP node MAY be configured to block unknown methods for security reasons ๐ก๏ธ
6.4.3. Handling Responses ๐ฌ
relays receiving a response first verify that the first URI in the To-Path corresponds to itself. if not, the response SHOULD be dropped ๐๏ธ likewise, if the message cannot be parsed, the relay MUST drop the response. next, the relay figures out if there are additional URIs in the To-Path. (for responses to SEND requests there will be no additional URIs, whereas responses to AUTH requests have additional URIs directing the response back to the client ๐)
if the response matches an existing transaction, then that transaction is completed and any timers running on it can be removed โ๏ธ if the response is a non 200 response, and the original request was a SEND request that had a Failure-Report header with a value other than "no", then the relay MUST send a REPORT indicating the nature of the failure ๐งพ the response code the relay received is used to form the status line in the REPORT that the relay sends.
if there are additional URIs in the To-Path header, the relay MUST then move its URI from the To-Path header, insert its URI in front of any other URIs in the From-Path header, and forward the response to the next URI in the To-Path header ๐ช the relay sends the request over the best connection that corresponds to the next URI in the To-Path header. if that connection has closed, then the response is silently discarded ๐ซฅ
6.5. Managing Connections ๐งน
relays should keep connections open as long as possible ๐ฐ๏ธ if a connection hasn't been used in a significant time (more than one hour), it MAY be closed. if the relay runs out of resources and can no longer establish new connections, it SHOULD start closing existing connections ๐ฅน it MAY choose to close connections on a least recently used basis, so the ones nobody's touched in a while get the boot first ๐ข
7. Formal Syntax โจ
okay bestie, this part is the grammar ๐ค๐. The syntax is written in ABNF (Augmented Backus-Naur Form), as described in RFC 4234 [10]. It also pulls in everything from RFC 4975, so you get the whole base grammar too ๐.
; This ABNF imports all the definitions in the ABNF of RFC 4975.
header =/ Expires / Min-Expires / Max-Expires / Use-Path /
WWW-Authenticate / Authorization / Authentication-Info
; this adds to the rule in RFC 4975
mAUTH = %x41.55.54.48 ; AUTH in caps
method =/ mAUTH
; this adds to the rule in RFC 4975
WWW-Authenticate = "WWW-Authenticate:" SP "Digest" SP digest-param
*("," SP digest-param)
digest-param = ( realm / nonce / [ opaque ] / [ stale ] / [
algorithm ] / qop-options / [auth-param] )
realm = "realm=" realm-value
realm-value = quoted-string
auth-param = token "=" ( token / quoted-string )
nonce = "nonce=" nonce-value
nonce-value = quoted-string
opaque = "opaque=" quoted-string
stale = "stale=" ( "true" / "false" )
algorithm = "algorithm=" ( "MD5" / token )
qop-options = "qop=" DQUOTE qop-list DQUOTE
qop-list = qop-value *( "," qop-value )
qop-value = "auth" / token
Authorization = "Authorization:" SP credentials
credentials = "Digest" SP digest-response
*( "," SP digest-response)
digest-response = ( username / realm / nonce / response / [
algorithm ] / cnonce / [opaque] / message-qop /
[nonce-count] / [auth-param] )
username = "username=" username-value
username-value = quoted-string
message-qop = "qop=" qop-value
cnonce = "cnonce=" cnonce-value
cnonce-value = nonce-value
nonce-count = "nc=" nc-value
nc-value = 8LHEX
response = "response=" request-digest
request-digest = DQUOTE 32LHEX DQUOTE
LHEX = DIGIT / %x61-66 ;lowercase a-f
Authentication-Info = "Authentication-Info:" SP ainfo
*("," ainfo)
ainfo = nextnonce / message-qop
/ response-auth / cnonce
/ nonce-count
nextnonce = "nextnonce=" nonce-value
response-auth = "rspauth=" response-digest
response-digest = DQUOTE *LHEX DQUOTE
Expires = "Expires:" SP 1*DIGIT
Min-Expires = "Min-Expires:" SP 1*DIGIT
Max-Expires = "Max-Expires:" SP 1*DIGIT
Use-Path = "Use-Path:" SP MSRP-URI *(SP MSRP-URI)
8. Finding MSRP Relays ๐ฆ
so when an MSRP node is resolving an MSRP URI that has an explicit port number, it follows the rules in Section 6 of the MSRP base specification ๐ . MSRP URIs exchanged in SDP, and in To-Path and From-Path headers in non-AUTH requests, MUST have an explicit port number. (The only message in this spec that can get away with an MSRP URI without a port is the To-Path header in an AUTH request ๐.) Same deal for msrps: URIs: if the authority component has an IPv4 address or an IPv6 reference, a port number MUST be present.
Next up, the rules that help MSRP clients find relays more easily in AUTH requests โจ. If the authority component has a domain name and an explicit port number, look up an address record (A or AAAA) for that domain name. Then connect using TLS over the default transport (TCP) with the port you were given ๐.
If there's a domain name but no port number, do a DNS SRV [4] lookup for the '_msrps' service and '_tcp' transport at that domain name. Then follow the Service Record (SRV) selection algorithm defined in that spec to pick the entry ๐ง. (There's no '_msrp' service, since AUTH requests only ever go over TLS.) If no SRVs turn up, try an address lookup (A or AAAA) for the domain name instead. Connect using TLS over the default transport (TCP) with the default port number, which is 2855 ๐ธ. Also, AUTH requests MUST only be sent over a TLS-protected channel. No cap, that's a big one ๐ฅ.
Here's what an SRV lookup in the example.com domain might return:
;; in example.com. Pri Wght Port Target
_msrps._tcp IN SRV 0 1 9000 server1.example.com.
_msrps._tcp IN SRV 0 2 9000 server2.example.com.
If you're running a relay farm, it is RECOMMENDED that each member of the farm has an SRV entry ๐. And if any farm members have multiple IP addresses (say, one IPv4 and one IPv6), each of those addresses SHOULD be registered in DNS as its own A or AAAA record, all pointing at a single target ๐.
9. Security Considerations ๐
okay this is the serious section, so pay attention bestie ๐ญ. It goes over the security mechanisms you can use in MSRP first, then the threat model, and then a list of implementation requirements related to security ๐.
9.1. Using HTTP Authentication
AUTH requests MUST be authenticated ๐. The authentication mechanism in this spec uses HTTP Digest authentication. HTTP Digest authentication is done as described in RFC 2617 [1], with the following restrictions and changes ๐ :
- Clients MUST NOT attempt to use Basic authentication, and relays MUST NOT request or accept Basic authentication ๐ซ.
- The qop value of auth-int makes no sense for MSRP ๐. Integrity protection comes from TLS. So MSRP relays MUST NOT indicate a qop of auth-int in a challenge.
- The interaction between the MD5-sess algorithm and the nextnonce mechanism is underspecified in RFC 2617 [1]. Because of that, MSRP relays MUST NOT send challenges indicating the MD5-sess algorithm ๐.
- Clients SHOULD consider the protection space within a realm to be scoped to the authority portion of the URI, without caring about what's in the path portion of the URI โจ. So relays SHOULD NOT send the "domain" parameter on the
WWW-Authenticateheader, and clients MUST ignore it if it shows up anyway. - Clients and relays MUST include a qop parameter in all
WWW-AuthenticateandAuthorizationheaders. Watch out for this one: the qop value is quoted in aWWW-Authenticateheader, but it is NOT quoted in anAuthorizationheader or anAuthentication-Infoheader ๐. - Clients MUST send cnonce and nonce-count parameters in all
Authorizationheaders ๐. - The request-URI used when calculating H(A2) is the rightmost URI in the To-Path header ๐ง.
- Relays MUST include rspauth, cnonce, nc, and qop parameters in an
Authentication-Infoheader for all "200 OK" responses to an AUTH request ๐ซถ.
Note that the BNF in RFC 2617 has a bunch of errors ๐. In particular, the value of the uri parameter MUST be in quotes, and the parameters in the Authentication-Info header MUST be separated by commas. The BNF in this document is correct, and so are the examples in RFC 2617 [1] ๐
.
The nextnonce and nc parameters work as described in RFC 2617 [1], which gives guidance on how and when to use them. There's one small tweak to that guidance. If you're building relays, keep in mind that AUTH requests cannot be pipelined. That means there's no bad hit to throughput when relays use the nextnonce mechanism ๐.
See Section 5.1 for more on the procedures for client authentication ๐.
9.2. Using TLS
TLS is used to authenticate relays to senders, and to give integrity and confidentiality for the headers being transported ๐โจ. MSRP clients and relays MUST implement TLS. Clients MUST send the TLS ClientExtendedHello extended hello information for server name indication, as described in RFC 4366 [5]. A TLS cipher-suite of TLS_RSA_WITH_AES_128_CBC_SHA [6] MUST be supported (other cipher-suites MAY also be supported too ๐). A relay MUST act as a TLS server and present a certificate with its identity in the SubjectAltName, using the dnsName choice type. Relay-to-relay connections MUST use TLS with mutual authentication. And client-to-relay communications MUST use TLS for AUTH requests and responses ๐ซถ.
The SubjectAltName in the certificate from a relay MUST match the hostname part of the URI. The certificate MUST also be valid according to RFC 3280 [12], which includes having a date that's currently valid and being signed by an acceptable certification authority ๐. Once that's all checked out, the device that started the TLS connection can assume it has connected to the correct relay โ .
This document doesn't define procedures for mutual authentication between an MSRP client and an MSRP relay ๐ . Client authentication is handled by the AUTH method, using the procedures in Section 5.1 and Section 6.3. Other specs may define using TLS mutual authentication to authenticate users linked to MSRP clients. Unless you're operating under one of those other specs, MSRP clients SHOULD present an empty certificate list (if the MSRP relay asks for one), and MSRP relays SHOULD ignore any certificates the client presents ๐ธ.
This behavior is defined specifically to allow forward-compatibility with specifications that define the use of TLS for client authentication. It's giving future-proof ๐ฎ.
Note: when relays are involved in a session, TCP without TLS is only used when a user who doesn't use relays connects directly to the relay of a user who is using relays ๐. In that case, the client has no way to authenticate the relay other than using the URIs that form a shared secret, the same way those URIs are used when no relays are involved ๐.
9.3. Threat Model โจ
okay so this section is the threat model ๐ต๏ธโโ๏ธ basically it's about what could go wrong and the big picture of what needs to be in place to lock the protocol down. the next section gets into how the actual mechanism covers those needs ๐
so MSRP lets two peer-to-peer clients swap messages ๐ each peer can pick a set of relays to handle certain policy stuff for them, and that combined set of relays is called the route set ๐ง there also has to be a channel outside of MSRP that can securely set up the session and hand the route set to both clients. think out-of-band provisioning or an explicit rendezvous protocol like SIP ๐ literally no way around that one
trust is lowkey the main character here ๐ a client might trust a relay with routing and policy decisions but not with everything in the session. like, a relay that checks for viruses would probably need to see all the contents, duh ๐ฆ but a relay that helps get through the ISP's Network Address Translator (NAT) would NOT be trusted with the contents. it'd just be trusted to forward messages correctly ๐
clients implicitly trust the relays they send and receive through to honor the routing in those messages, within the limits of the MSRP protocol ๐ซถ they also need to trust that relays don't inject new messages on their behalf or modify messages going to or from them ๐ โโ๏ธ fun fact: some relays could make a client misroute a message by sneakily changing a Use-Path returned by a relay further down the chain ๐ญ BUT this isn't an extra threat, because those same relays could just misroute the message in the first place ๐ if a relay is trusted to route, it's reasonable to trust it not to mess with the Use-Path header. if it can't be trusted to route, then it just can't be used at all ๐ซ
under certain circumstances relays also need to trust other relays not to change info between them and the client they represent ๐ฅบ for example, say a client goes through Relay A to reach Relay B, and Relay B is logging the messages the client sends. Relay B might need to verify that the logged messages really came from the client and weren't modified or forged by Relay A ๐ต๏ธ the fix for that is having the client sign the message โ๏ธ
clients need to authenticate that the relay they're talking to is the one they trust ๐ and relays need to authenticate that the client is the one they're allowed to forward info to ๐ชช clients also need the option of making sure info between relay and client is integrity protected and confidential to everyone except the relays and clients ๐ธ to keep the options simple, traffic between relays is always integrity protected and encrypted, whether or not the client asks for it ๐ clients can't tell relays what strength of crypto to use between them. the only lever they have is picking relays that are administered to require an adequate level of security ๐ฏ
the system also needs to stop messages from being sent to relays that aren't supposed to see them ๐ง to keep relays from being abused in Denial of Service (DoS) attacks, relays never forward messages unless they have a trust relationship with either the sending or the receiving client. and they only forward a message if it's coming from or going to the client they have that trust relationship with ๐ก๏ธ if a relay trusts the destination client, it should not send the message anywhere except to that destination client ๐ฆ
some terms for this bit: SClient is the client sending a message and RClient is the client receiving it ๐ฌ SRelay is a relay the sender trusts and RRelay is a relay the receiver trusts. the message goes SClient to SRelay1 to SRelay2 to RRelay2 to RRelay1 to RClient ๐ค๏ธ bestie we're going on a journey ๐งโจ
9.4. Security Mechanism ๐
okay the confidentiality and privacy part ๐ฅน anything not in the route set is kept out by using TLS on all the transports ๐ relays always use TLS. clients can use unprotected TCP for peer-to-peer MSRP, but any time a client talks to its relay, it MUST use TLS ๐ non-negotiable ๐ โโ๏ธ
the relays authenticate to the clients using TLS, but they don't have to do mutual TLS ๐ค the rspauth parameter in the Authentication-Info header also gives limited authentication of relays the client isn't directly connected to โจ clients authenticate to relays using HTTP Digest authentication ๐ช and relays authenticate to each other using TLS mutual authentication ๐ซ
with Secure/Multipurpose Internet Mail Extensions (S/MIME) [3] encryption, clients can protect their actual message contents so relays can't see them ๐ end-to-end signing is also possible with S/MIME ๐๏ธ
the complicated part is making sure relays can't be tricked into sending messages somewhere they shouldn't go ๐ค the way this works is the client authenticates to the relay, and then the relay hands back a token ๐๏ธ messages carrying that token can be relayed if they come from the client that got the token or if they're headed toward that client ๐ง the tokens are the URIs the relay puts in the Use-Path header. they contain random material (defined in Section 6.3) so attackers can't guess them ๐ฒ the tokens need protecting so only elements in the route set, or elements at least one of the parties trusts, ever see them ๐
โ ๏ธ the big scary part: if some third party finds the token RRelay2 uses to forward messages to RClient, that third party can send as many messages as they want to RRelay2 and they'll all get forwarded to RClient. the third party can't make them go anywhere except RClient though, so the open relay problems are avoided ๐ and SRelay1 won't forward a message unless it has a valid token ๐
when SClient goes to get a token from SRelay2, the request is relayed through SRelay1 ๐ฌ SRelay2 authenticates that it's really SClient asking for the token, and then it generates a token that's only valid for forwarding messages to or from SRelay1 ๐ SRelay2 knows it's connected to SRelay1 because of the mutual TLS ๐ซถ
the tokens are carried in the resource portion of the MSRP URIs ๐งพ how long the tokens stay valid is negotiated using the Expire header in the AUTH request โณ clients need to renegotiate the tokens using a new offer/answer [15] exchange (for example, a SIP re-invite) before the tokens expire ๐ don't let them expire, bestie ๐ญ
this scheme relies on relays being trusted nodes acting on behalf of the users authenticated to them ๐ค there's no security mechanism stopping relays on the path from inserting forged messages, changing message contents, sending messages in a session to someone other than the specified party, or copying them to a third party ๐ซฃ however, the one-to-one binding between session identifiers and sessions helps limit the damage a rogue relay can do ๐ฆ it limits where forged or modified messages can go to the two parties in the session, and only for the duration of that session ๐ฐ๏ธ also, S/MIME encryption can be used to limit how useful redirected messages are ๐ and finally, clients can use S/MIME signatures to guarantee the authenticity of messages they send, which can make it possible in some cases to detect relay manipulation or forged messages ๐ต๏ธโโ๏ธโจ
clients aren't the only ones who need relays to behave ๐ฅบ if a relay doesn't have a direct TLS connection with the client it's acting for (i.e. there are one or more intervening relays), it's at the mercy of those intervening relays to transmit messages to and from the client accurately ๐ฌ if a stronger guarantee of the authentic origin of a message is needed (e.g. the relay is logging messages as part of a legal requirement), then the relay's users can be told by their administrators to use detached S/MIME signatures on all messages their client sends ๐ the relay can enforce that policy by returning a 415 response to any SEND requests using a top-level MIME type other than "multipart/signed" ๐ซ and relays like that may choose to make policy decisions, like terminating sessions and/or suspending user authorization, if those signatures don't match the message contents ๐
โโ๏ธ๐
10. IANA Considerations ๐
10.1. New MSRP Method ๐
this spec defines a brand-new MSRP method, AUTH ๐ it gets added to the Methods sub-registry under the MSRP Parameters registry. see Section 5.1 for all the details on AUTH ๐
10.2. New MSRP Headers ๐
this spec defines several new MSRP header fields ๐ they get added to the header-field sub-registry under the MSRP Parameters registry:
ExpiresMin-ExpiresMax-ExpiresUse-PathWWW-AuthenticateAuthorizationAuthentication-Info
10.3. New MSRP Response Codes ๐ฆ
this spec defines one new MSRP status code, which gets added to the Status-Code sub-registry under the MSRP Parameters registry ๐ง
the 401 response means an AUTH request had no credentials, an expired nonce value, or invalid credentials ๐ the response includes a "WWW-Authenticate" header with a challenge (among other fields). see Section 9.1 for more ๐ the default response phrase is "Unauthorized" ๐ช
11. Example SDP with Multiple Hops ๐งโโ๏ธ
okay this example is SO cute ๐ฅน it's an SDP that could show up in a SIP message to set up an MSRP session between Alice and Bob, where Bob uses a relay ๐ Alice makes an offer with a path to herself ๐
c=IN IP4 a.example.com
m=message 1234 TCP/MSRP *
a=accept-types: message/cpim text/plain text/html
a=path:msrp://a.example.com:1234/agic456;tcp
in this offer, Alice wants to receive MSRP messages at a.example.com ๐ she wants TCP as the transport for the session ๐ she can accept message/cpim, text/plain, and text/html bodies in SEND requests ๐ผ๏ธ and she doesn't need a relay to set up the session ๐
Bob's answer could look like this ๐ซถ
c=IN IP4 bob.example.com
m=message 1234 TCP/TLS/MSRP *
a=accept-types: message/cpim text/plain
a=path:msrps://relay.example.com:9000/hjdhfha;tcp \
msrps://bob.example.com:1234/fuige;tcp
Bob wants to receive MSRP messages at bob.example.com ๐ก he can accept only message/cpim and text/plain bodies in SEND requests, so he rejected the text/html content Alice offered ๐ โโ๏ธ and he wants to use a relay called relay.example.com for the session ๐ธ
12. Acknowledgments ๐ซถ
huge thanks to Avshalom Houri, Hisham Khartabil, Robert Sparks, Miguel Garcia, Hans Persson, and Orit Levin, who gave detailed proofreading and super helpful text ๐ also thanks to the following SIMPLE WG members for the spirited discussions on session mode: Chris Boulton, Ben Campbell, Juhee Garg, Paul Kyzivat, Allison Mankin, Aki Niemi, Pekka Pessi, Jon Peterson, Brian Rosen, Jonathan Rosenberg, and Dean Willis ๐โจ
13. References ๐
13.1. Normative References ๐
okay so these are the docs MSRP literally can't work without, bestie ๐ every one of them is required reading, no skipping!
[1] Franks, J., Hallam-Baker, P., Hostetler, J., Lawrence, S.,
Leach, P., Luotonen, A., and L. Stewart, "HTTP Authentication:
Basic and Digest Access Authentication", RFC 2617, June 1999.
[2] Dierks, T. and E. Rescorla, "The Transport Layer Security (TLS)
Protocol Version 1.1", RFC 4346, April 2006.
[3] Ramsdell, B., "Secure/Multipurpose Internet Mail Extensions
(S/MIME) Version 3.1 Message Specification", RFC 3851, July
2004.
[4] Gulbrandsen, A., Vixie, P., and L. Esibov, "A DNS RR for
specifying the location of services (DNS SRV)", RFC 2782,
February 2000.
[5] Blake-Wilson, S., Nystrom, M., Hopwood, D., Mikkelsen, J., and
T. Wright, "Transport Layer Security (TLS) Extensions", RFC
4366, April 2006.
[6] Chown, P., "Advanced Encryption Standard (AES) Ciphersuites for
Transport Layer Security (TLS)", RFC 3268, June 2002.
[7] Handley, M., Jacobson, V., and C. Perkins, "SDP: Session
Description Protocol", RFC 4566, July 2006.
[8] Rosenberg, J., Schulzrinne, H., Camarillo, G., Johnston, A.,
Peterson, J., Sparks, R., Handley, M., and E. Schooler, "SIP:
Session Initiation Protocol", RFC 3261, June 2002.
[9] Bradner, S., "Key words for use in RFCs to Indicate Requirement
Levels", BCP 14, RFC 2119, March 1997.
[10] Crocker, D. and P. Overell, "Augmented BNF for Syntax
Specifications: ABNF", RFC 4234, October 2005.
[11] Campbell, B., Ed., Mahy, R., Ed., and C. Jennings, Ed., "The
Message Session Relay Protocol (MSRP)", RFC 4975, September
2007.
[12] Housley, R., Polk, W., Ford, W., and D. Solo, "Internet X.509
Public Key Infrastructure Certificate and Certificate
Revocation List (CRL) Profile", RFC 3280, April 2002.
13.2. Informative References ๐ง
these ones are more like background reading. helpful, not mandatory, but lowkey worth a peek ๐ธ
[13] Freed, N. and N. Borenstein, "Multipurpose Internet Mail
Extensions (MIME) Part One: Format of Internet Message Bodies",
RFC 2045, November 1996.
[14] Freed, N. and N. Borenstein, "Multipurpose Internet Mail
Extensions (MIME) Part Two: Media Types", RFC 2046, November
1996.
[15] Rosenberg, J. and H. Schulzrinne, "An Offer/Answer Model with
Session Description Protocol (SDP)", RFC 3264, June 2002.
Appendix A. Implementation Considerations โจ
okay so this part is NOT required to make MSRP interoperable ๐ซถ it's just a juicy implementation discussion for the community. purely an implementation detail, no stress!
Quick heads up on one idea that got floated: having a relay hand back a base URI that the client uses to build more URIs. The catch is that third parties who once had a session with the client could then learn URIs the relay will keep using for forwarding even after that session is over. That basically spills the secret URIs and wrecks the security, so this approach is not used ๐
A different approach has the relays return a URI split into an index portion and a secret portion. The client encrypts its identifier and its own opaque data with the secret portion, then concatenates that with the index portion to make a bunch of valid URIs. When the relay gets one of these URIs, it could use the index to look up the right secret, decrypt the client portion, and check that it contains the client identifier. Then it can forward the request. The client doesn't need to send an AUTH request for each URI it uses. This is an implementation detail that is out of the scope of this document ๐ฆ
Now the fun one: forwarding in a farm without the relay saving any state! ๐ It's possible, and the rest of this section describes one way a relay might do it. When a relay starts up, it could pick a cryptographically random 128-bit password (K) and a 128-bit initialization vector (IV). If the relay is really a farm of servers sharing the same DNS name, every machine in the farm would need to share the same K. When an AUTH request comes in, the relay forms a string containing the expiry time of the URI, and whether the previous hop was mutual TLS authenticated or not. If it was, the string includes the name of the previous hop. If it wasn't, the string includes the identifier for the connection that received the AUTH request.
That string then gets padded by appending a byte with the value 0x80, then adding zero or more bytes with the value 0x00 until the string length is a multiple of 16 bytes long. A new random IV gets selected (it needs to change because it forms the salt), and the padded string is encrypted using AES-CBC with a key of K. The IV, the encrypted data, and an SPI (security parameter index) that changes each time K changes get base 64 encoded and form the resource portion of the request URI. The SPI lets the key be changed and lets the system know which K to use ๐ฅ
Later, when the relay receives this URI, it could decrypt it, check that the current time is before the expiry time, and check that the message is coming from or going to the connection or location specified in the URI. Integrity protection is not required here, because it is extremely unlikely that random data that was decrypted would turn into a valid location that matches the one the message was routing to or from. ๐ง
โ ๏ธ bestie, if you ever build something like this, be careful NOT to use a scheme like EBE. That kind of scheme would let portions of encrypted tokens get cut and pasted into other URIs, and that's a whole mess ๐ญ
Authors' Addresses ๐
Cullen Jennings
Cisco Systems, Inc.
170 West Tasman Dr.
MS: SJC-21/2
San Jose, CA 95134
USA
Phone: +1 408 421-9990
EMail: fluffy@cisco.com
Rohan Mahy
Plantronics
345 Encincal Street
Santa Cruz, CA 95060
USA
EMail: rohan@ekabal.com
Adam Roach
Estacado Systems
17210 Campbell Rd.
Suite 250
Dallas, TX 75252
USA
Phone: sip:adam@estacado.net
EMail: adam@estacado.net
Full Copyright Statement ๐
Copyright (C) The IETF Trust (2007).
This document is subject to the rights, licenses and restrictions contained in BCP 78, and except as set forth therein, the authors retain all their rights.
This document and the information contained herein are provided on an "AS IS" basis and THE CONTRIBUTOR, THE ORGANIZATION HE/SHE REPRESENTS OR IS SPONSORED BY (IF ANY), THE INTERNET SOCIETY, THE IETF TRUST AND THE INTERNET ENGINEERING TASK FORCE DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
Intellectual Property ๐
The IETF takes no position regarding the validity or scope of any Intellectual Property Rights or other rights that might be claimed to pertain to the implementation or use of the technology described in this document or the extent to which any license under such rights might or might not be available; nor does it represent that it has made any independent effort to identify any such rights. Information on the procedures with respect to rights in RFC documents can be found in BCP 78 and BCP 79.
Copies of IPR disclosures made to the IETF Secretariat and any assurances of licenses to be made available, or the result of an attempt made to obtain a general license or permission for the use of such proprietary rights by implementers or users of this specification can be obtained from the IETF on-line IPR repository at http://www.ietf.org/ipr.
The IETF invites any interested party to bring to its attention any copyrights, patents or patent applications, or other proprietary rights that may cover technology that may be required to implement this standard. Please address the information to the IETF at ietf-ipr@ietf.org.
๐ฆ where this page comes from
- ietf: rfc4976 2007-09
- ๐ girlified by claude-haiku-5-5 on : a language model's rewrite of the original, under its terms