Skip to content

Latest commit

 

History

History
190 lines (147 loc) · 7.16 KB

AccessControl.md

File metadata and controls

190 lines (147 loc) · 7.16 KB

SRT Access Control Guidelines

Motivation

One type of information that can be interchanged when a connection is being established in SRT is "Stream ID", which can be used in a caller-listener connection layout. This is a string of maximum 512 characters set on the caller side. It can be retrieved at the listener side on the newly accepted socket through a socket option (see SRTO_STREAMID in API.md).

As of SRT version 1.3.3 a callback can be registered on the listener socket for an application to make decisions on incoming caller connections. This callback, among others, is provided with the value of Stream ID from the incoming connection. Based on this information, the application can accept or reject the connection, select the desired data stream, or set an appropriate passphrase for the connection.

Purpose

The Stream ID value can be used as free-form, but there is a recommended convention so that all SRT users speak the same language. The intent of the convention is to:

  • promote readability and consistency among free-form names
  • interpret some typical data in the key-value style

Character Encoding

The Stream ID uses UTF-8 encoding.

General Syntax

This recommended syntax starts with the characters known as an executable specification in POSIX:

#!

The next two characters are:

  • : - this marks the YAML format, the only one currently used
  • The content format, which is either:
    • : - the comma-separated keys with no nesting
    • { - like above, but nesting is allowed and must end with }

(Nesting means that you can have multiple level brace-enclosed parts inside.)

The form of the key-value pair is:

  • key1=value1,key2=value2...

Standard Keys

Beside the general syntax, there are several top-level keys treated as standard keys. Other keys can be used as needed.

The following keys are standard:

  • u: User Name, or authorization name, that is expected to control which password should be used for the connection. The application should interpret it to distinguish which user should be used by the listener party to set up the password.
  • r: Resource Name identifies the name of the resource and facilitates selection should the listener party be able to serve multiple resources.
  • h: Host Name identifies the hostname of the resource. For example, to request a stream with the URI somehost.com/videos/querry.php?vid=366 the hostname field should have “somehost.com”, and the resource name can have “videos/querry.php?vid=366” or simply "366". Note that this is still a key to be specified explicitly. Support tools that apply simplifications and URI extraction are expected to insert only the host portion of the URI here.
  • s: Session ID is a temporary resource identifier negotiated with the server, used just for verification. This is a one-shot identifier, invalidated after the first use. The expected usage is when details for the resource and authorization are negotiated over a separate connection first, and then the session ID is used here alone.
  • t: Type specifies the purpose of the connection. Several standard types are defined, but users may extend the use:
    • stream (default, if not specified): for exchanging the user-specified payload for an application-defined purpose
    • file: for transmitting a file, where r is the filename
    • auth: for exchanging sensible data. The r value states its purpose. No specific possible values for that are known so far (FUTURE USE]
  • m: Mode expected for this connection:
    • request (default): the caller wants to receive the stream
    • publish: the caller wants to send the stream data
    • bidirectional: bidirectional data exchange is expected

Note that m is not required in the case where you don't use streamid to distinguish authorization or resources, and your caller is expected to send the data. This is only for cases where the listener can handle various purposes of the connection and is therefore required to know what the caller is attempting to do.

Examples:

#!::u=admin,r=bluesbrothers1_hi

This specifies the username and the resource name of the stream to be served to the caller.

#!::u=johnny,t=file,m=publish,r=results.csv

This specifies that the file is expected to be transmitted from the caller to the listener and its name is results.csv.

Example

An example of Stream ID functionality and the listener callback can be found under tests/test_listen_callback.cpp.

A listener can register a callback to be called in the middle of accepting a new socket connection:

srt_listen(server_sock, 5);
srt_listen_callback(server_sock, &SrtTestListenCallback, NULL);

A callback function has to be implemented by the upstream application. In the example below, the function tries to interpret the Stream ID value first according to the Access Control guidelines and to extract the username from the u key. Otherwise it falls back to a free-form specified username. Depending on the user, it sets the appropriate password for the expected connection so that it can be rejected if the password isn't correct. If the user isn't found in the database (passwd map) the function itself rejects the connection. Note that this can be done by both returning -1 and by throwing an exception.

int SrtTestListenCallback(void* opaq, SRTSOCKET ns, int hsversion,
    const struct sockaddr* peeraddr, const char* streamid)
{
    using namespace std;

    // opaq is used to pass some further chained callbacks

    // To reject a connection attempt, return -1.

    static const map<string, string> passwd {
        {"admin", "thelocalmanager"},
        {"user", "verylongpassword"}
    };

    // Try the "standard interpretation" with username at key u
    string username;

    static const char stdhdr [] = "#!::";
    uint32_t* pattern = (uint32_t*)stdhdr;
    bool found = -1;

    // Extract a username from the StreamID:
    if (strlen(streamid) > 4 && *(uint32_t*)streamid == *pattern)
    {
        vector<string> items;
        Split(streamid+4, ',', back_inserter(items));
        for (auto& i: items)
        {
            vector<string> kv;
            Split(i, '=', back_inserter(kv));
            if (kv.size() == 2 && kv[0] == "u")
            {
                username = kv[1];
                found = true;
            }
        }

        if (!found)
        {
            cerr << "TEST: USER NOT FOUND, returning false.\n";
            return -1;
        }
    }
    else
    {
        // By default the whole streamid is username
        username = streamid;
    }

    // When the username of the client is known, the passphrase can be set
    // on the socket being accepted (SRTSOCKET ns).
    // The remaining part of the SRT handshaking process will check the
    // passphrase of the client and accept or reject the connection.

    // When not found, it will throw an exception
    cerr << "TEST: Accessing user '" << username << "', might throw if not found\n";
    string exp_pw = passwd.at(username);

    cerr << "TEST: Setting password '" << exp_pw << "' as per user '" << username << "'\n";
    srt_setsockflag(ns, SRTO_PASSPHRASE, exp_pw.c_str(), exp_pw.size());
    return 0;
}