Ignore:
Timestamp:
May 8, 2009, 2:56:11 PM (13 years ago)
Author:
charles
Message:

(trunk) support an X-Transmission-Session-Id header in the RPC server. Yesterday's approach of including the session_id in posted forms -- which is a typical approach -- isn't sufficient for Transmission, since it also allows remote access via JSON/RPC. (part 1 of 2. part 2 is kjg's web ui patch)

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/libtransmission/rpc-server.c

    r8356 r8358  
    4040#include "net.h"
    4141
     42/* session-id is used to make cross-site request forgery attacks difficult.
     43 * Don't disable this feature unless you really know what you're doing!
     44 * http://en.wikipedia.org/wiki/Cross-site_request_forgery
     45 * http://shiflett.org/articles/cross-site-request-forgeries
     46 * http://www.webappsec.org/lists/websecurity/archive/2008-04/msg00037.html */
     47#define REQUIRE_SESSION_ID
     48
    4249#define MY_NAME "RPC Server"
    4350#define MY_REALM "Transmission"
     
    6168    char *             whitelistStr;
    6269    tr_list *          whitelist;
     70
     71    char *             sessionId;
     72    time_t             sessionIdExpiresAt;
    6373
    6474#ifdef HAVE_ZLIB
     
    451461}
    452462
    453 static void
    454 handle_request( struct evhttp_request * req,
    455                 void *                  arg )
     463static char*
     464get_current_session_id( struct tr_rpc_server * server )
     465{
     466    const time_t now = time( NULL );
     467
     468    if( !server->sessionId || ( now >= server->sessionIdExpiresAt ) )
     469    {
     470        int i;
     471        const int n = 48;
     472        const char * pool = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
     473        const size_t pool_size = strlen( pool );
     474        char * buf = tr_new( char, n+1 );
     475
     476        for( i=0; i<n; ++i )
     477            buf[i] = pool[ tr_cryptoRandInt( pool_size ) ];
     478        buf[n] = '\0';
     479
     480        tr_free( server->sessionId );
     481        server->sessionId = buf;
     482        server->sessionIdExpiresAt = now + (60*60); /* expire in an hour */
     483    }
     484
     485    return server->sessionId;
     486}
     487
     488
     489static tr_bool
     490test_session_id( struct tr_rpc_server * server, struct evhttp_request * req )
     491{
     492    const char * ours = get_current_session_id( server );
     493    const char * theirs = evhttp_find_header( req->input_headers, TR_RPC_SESSION_ID_HEADER );
     494    const tr_bool success =  theirs && !strcmp( theirs, ours );
     495    return success;
     496}
     497
     498static void
     499handle_request( struct evhttp_request * req, void * arg )
    456500{
    457501    struct tr_rpc_server * server = arg;
     
    460504    {
    461505        const char * auth;
    462         char *       user = NULL;
    463         char *       pass = NULL;
     506        char * user = NULL;
     507        char * pass = NULL;
    464508
    465509        evhttp_add_header( req->output_headers, "Server", MY_REALM );
    466510
    467511        auth = evhttp_find_header( req->input_headers, "Authorization" );
    468 
    469512        if( auth && !strncasecmp( auth, "basic ", 6 ) )
    470513        {
     
    480523        if( !isAddressAllowed( server, req->remote_host ) )
    481524        {
    482             send_simple_response( req, 401,
     525            send_simple_response( req, 403,
    483526                "<p>Unauthorized IP Address.</p>"
    484527                "<p>Either disable the IP address whitelist or add your address to it.</p>"
     
    512555            handle_clutch( req, server );
    513556        }
     557#ifdef REQUIRE_SESSION_ID
     558        else if( !test_session_id( server, req ) )
     559        {
     560            const char * sessionId = get_current_session_id( server );
     561            char * tmp = tr_strdup_printf(
     562                "<p>Please add this header to your requests:</p>"
     563                "<p><code>%s: %s</code></p>"
     564                "<p>This requirement is to make "
     565                "<a href=\"http://en.wikipedia.org/wiki/Cross-site_request_forgery\">CSRF</a>"
     566                " attacks more difficult.</p>",
     567                TR_RPC_SESSION_ID_HEADER, sessionId );
     568            evhttp_add_header( req->output_headers, TR_RPC_SESSION_ID_HEADER, sessionId );
     569            send_simple_response( req, 409, tmp );
     570            tr_free( tmp );
     571        }
     572#endif
    514573        else if( !strncmp( req->uri, "/transmission/rpc", 17 ) )
    515574        {
Note: See TracChangeset for help on using the changeset viewer.