RFC 4976

Relay Extensions for the Message Sessions Relay Protocol (MSRP)

๐Ÿ“… published September 2007, from ietf

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 ๐Ÿงš

  1. 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.
  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.
  3. 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 ๐Ÿฆ‹

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-Authenticate header, and clients MUST ignore it if it shows up anyway.
  • Clients and relays MUST include a qop parameter in all WWW-Authenticate and Authorization headers. Watch out for this one: the qop value is quoted in a WWW-Authenticate header, but it is NOT quoted in an Authorization header or an Authentication-Info header ๐Ÿ‘€.
  • Clients MUST send cnonce and nonce-count parameters in all Authorization headers ๐ŸŽ€.
  • 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-Info header 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:

  • Expires
  • Min-Expires
  • Max-Expires
  • Use-Path
  • WWW-Authenticate
  • Authorization
  • Authentication-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

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