From 486bb768f0f3745bf810795a05ee99bf821c4e87 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Thu, 5 Jun 2008 18:16:59 +0000 Subject: [PATCH] add authentication support to daemon & remote --- daemon/daemon.c | 84 ++++++++++++++++++++++++++++++------ daemon/remote.c | 14 +++++- daemon/transmission-daemon.1 | 7 +++ daemon/transmission-remote.1 | 6 +++ 4 files changed, 97 insertions(+), 14 deletions(-) diff --git a/daemon/daemon.c b/daemon/daemon.c index 8ee835287..6b68463bd 100644 --- a/daemon/daemon.c +++ b/daemon/daemon.c @@ -38,12 +38,18 @@ saveState( tr_handle * h ) { tr_benc d; const char * str; + char * username = tr_sessionGetRPCUsername( h ); + char * password = tr_sessionGetRPCPassword( h ); + char * auth = tr_strdup_printf( "%s:%s", username, password ); - tr_bencInitDict( &d, 12 ); + tr_bencInitDict( &d, 14 ); tr_bencDictAddStr( &d, "download-dir", tr_sessionGetDownloadDir( h ) ); tr_bencDictAddInt( &d, "peer-limit", tr_sessionGetPeerLimit( h ) ); tr_bencDictAddInt( &d, "pex-allowed", tr_sessionIsPexEnabled( h ) ); tr_bencDictAddInt( &d, "port", tr_sessionGetPeerPort( h ) ); + tr_bencDictAddStr( &d, "auth", auth ); + tr_bencDictAddInt( &d, "auth-required", + tr_sessionIsRPCPasswordEnabled( h ) ); tr_bencDictAddInt( &d, "port-forwarding-enabled", tr_sessionIsPortForwardingEnabled( h ) ); tr_bencDictAddStr( &d, "rpc-acl", tr_sessionGetRPCACL( h ) ); @@ -67,10 +73,28 @@ saveState( tr_handle * h ) tr_bencSaveFile( myConfigFilename, &d ); tr_bencFree( &d ); + tr_free( auth ); + tr_free( password ); + tr_free( username ); +} + +static int +parseAuth( const char * auth, char ** username, char ** password ) +{ + int err = 0; + const char * pch = strchr( auth, ':' ); + if( !pch ) + err = -1; + else { + *username = tr_strndup( auth, pch-auth ); + *password = tr_strdup( pch+1 ); + } + return err; } static void -session_init( const char * configDir, int rpc_port, const char * rpc_acl ) +session_init( const char * configDir, int rpc_port, + const char * acl, const char * auth, int noauth ) { tr_benc state; int have_state; @@ -84,10 +108,15 @@ session_init( const char * configDir, int rpc_port, const char * rpc_acl ) int64_t down_limited = FALSE; int encryption = TR_ENCRYPTION_PREFERRED; char downloadDir[MAX_PATH_LENGTH] = { '\0' }; - const char * rpc_acl_fallback = TR_DEFAULT_RPC_ACL; + const char * acl_fallback = TR_DEFAULT_RPC_ACL; int64_t rpc_port_fallback = TR_DEFAULT_RPC_PORT; + int64_t auth_required_fallback = 0; + const char * auth_fallback = NULL; tr_ctor * ctor; tr_torrent ** torrents; + int auth_required; + char * user = NULL; + char * pass = NULL; if(( have_state = !tr_bencLoadFile( myConfigFilename, &state ))) { @@ -100,7 +129,9 @@ session_init( const char * configDir, int rpc_port, const char * rpc_acl ) tr_bencDictFindInt( &state, "pex-allowed", &pex_enabled ); tr_bencDictFindInt( &state, "port", &peer_port ); tr_bencDictFindInt( &state, "port-forwarding-enabled", &fwd_enabled ); - tr_bencDictFindStr( &state, "rpc-acl", &rpc_acl_fallback ); + tr_bencDictFindStr( &state, "rpc-acl", &acl_fallback ); + tr_bencDictFindStr( &state, "auth", &auth_fallback ); + tr_bencDictFindInt( &state, "auth-required", &auth_required_fallback ); tr_bencDictFindInt( &state, "rpc-port", &rpc_port_fallback ); tr_bencDictFindInt( &state, "speed-limit-down", &down_limit ); tr_bencDictFindInt( &state, "speed-limit-down-enabled", &down_limited ); @@ -119,8 +150,23 @@ session_init( const char * configDir, int rpc_port, const char * rpc_acl ) getcwd( downloadDir, sizeof( downloadDir ) ); if( rpc_port < 1 ) rpc_port = rpc_port_fallback; - if( !rpc_acl || !*rpc_acl ) - rpc_acl = rpc_acl_fallback; + if( !acl || !*acl ) + acl = acl_fallback; + if( !auth || !*auth ) + auth = auth_fallback; + + if( auth && parseAuth( auth, &user, &pass ) ) { + tr_nerr( MY_NAME, "Unable to parse authentication string \"%s\"", auth ); + abort( ); + } + + if( noauth ) { + /* user has explicitly turned off authentication */ + user = NULL; + pass = NULL; + } + + auth_required = user || pass; /* start the session */ mySession = tr_sessionInitFull( configDir, "daemon", downloadDir, @@ -132,7 +178,11 @@ session_init( const char * configDir, int rpc_port, const char * rpc_acl ) TR_MSG_INF, 0, FALSE, /* is the blocklist enabled? */ TR_DEFAULT_PEER_SOCKET_TOS, - TRUE, rpc_port, rpc_acl ); + TRUE, rpc_port, acl, + auth_required, user, pass ); + + if( auth_required ) + tr_ninf( MY_NAME, "requiring authentication" ); /* load the torrents */ ctor = tr_ctorNew( mySession ); @@ -156,6 +206,7 @@ daemonUsage( void ) " -f --foreground Run in the foreground and log to stderr\n" " -g --config-dir Where to look for torrents and daemon-config.benc\n" " -h --help Display this message and exit\n" + " -t --auth : Username and password for authentication\n" " -p --port n Port to listen to for requests (Default: "TR_DEFAULT_RPC_PORT_STR")\n" "\n" MY_NAME" is a headless Transmission session\n" @@ -165,24 +216,31 @@ daemonUsage( void ) static void readargs( int argc, char ** argv, - int * nofork, int * port, char ** acl, - char ** configDir ) + int * nofork, int * port, + char ** configDir, + char ** acl, + char ** auth, + int * noauth ) { int opt; - char optstr[] = "a:fg:hp:"; + char optstr[] = "a:fg:hnp:t:u:w:"; struct option longopts[] = { { "acl", required_argument, NULL, 'a' }, { "foreground", no_argument, NULL, 'f' }, { "config-dir", required_argument, NULL, 'g' }, { "help", no_argument, NULL, 'h' }, + { "noauth", no_argument, NULL, 'n' }, { "port", required_argument, NULL, 'p' }, + { "auth", required_argument, NULL, 't' }, { NULL, 0, NULL, '\0' } }; while((( opt = getopt_long( argc, argv, optstr, longopts, NULL ))) != -1 ) { switch( opt ) { case 'a': *acl = tr_strdup( optarg ); break; case 'f': *nofork = 1; break; + case 'n': *noauth = 1; break; case 'g': *configDir = tr_strdup( optarg ); break; + case 't': *auth = tr_strdup( optarg ); break; case 'p': *port = atoi( optarg ); break; default: daemonUsage( ); break; } @@ -253,9 +311,11 @@ int main( int argc, char ** argv ) { int nofork = 0; + int noauth = 0; int port = TR_DEFAULT_RPC_PORT; char * configDir = NULL; char * acl = NULL; + char * auth = NULL; signal( SIGINT, gotsig ); signal( SIGQUIT, gotsig ); @@ -263,7 +323,7 @@ main( int argc, char ** argv ) signal( SIGPIPE, SIG_IGN ); signal( SIGHUP, SIG_IGN ); - readargs( argc, argv, &nofork, &port, &acl, &configDir ); + readargs( argc, argv, &nofork, &port, &configDir, &acl, &auth, &noauth ); if( configDir == NULL ) configDir = tr_strdup_printf( "%s-daemon", tr_getDefaultConfigDir() ); tr_buildPath( myConfigFilename, sizeof( myConfigFilename ), @@ -276,7 +336,7 @@ main( int argc, char ** argv ) } } - session_init( configDir, port, acl ); + session_init( configDir, port, acl, auth, noauth ); while( !closing ) sleep( 1 ); diff --git a/daemon/remote.c b/daemon/remote.c index c9131c0a7..a4bbe900d 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -65,6 +65,7 @@ showUsage( void ) " -s --start all Start all stopped torrents\n" " -S --stop Stop the torrent with the given ID\n" " -S --stop all Stop all running torrents\n" + " -t --auth : Username and password for authentication\n" " -u --upload-limit Max upload rate in KiB/s\n" " -U --upload-unlimited No upload rate limit\n" " -v --verify Verify the torrent's local data\n" ); @@ -86,6 +87,7 @@ numarg( const char * arg ) static char * reqs[256]; /* arbitrary max */ static int reqCount = 0; static int debug = 0; +static char * auth = NULL; static char* absolutify( char * buf, size_t len, const char * path ) @@ -114,7 +116,7 @@ static void readargs( int argc, char ** argv ) { int opt; - char optstr[] = "a:c:d:DeEf:ghlmMp:r:s:S:u:Uv:"; + char optstr[] = "a:c:d:DeEf:ghlmMp:r:s:S:t:u:Uv:"; const struct option longopts[] = { @@ -134,6 +136,7 @@ readargs( int argc, char ** argv ) { "remove", required_argument, NULL, 'r' }, { "start", required_argument, NULL, 's' }, { "stop", required_argument, NULL, 'S' }, + { "auth", required_argument, NULL, 't' }, { "upload-limit", required_argument, NULL, 'u' }, { "upload-unlimited", no_argument, NULL, 'U' }, { "verify", required_argument, NULL, 'v' }, @@ -154,6 +157,9 @@ readargs( int argc, char ** argv ) case 'g': debug = 1; addArg = FALSE; break; + case 't': auth = tr_strdup( optarg ); + addArg = FALSE; + break; case 'h': showUsage( ); addArg = FALSE; break; @@ -287,7 +293,7 @@ processResponse( const char * host, int port, (int)len, (int)len, (const char*) response ); if( tr_jsonParse( response, len, &top, NULL ) ) - tr_nerr( MY_NAME, "Unable to parse response" ); + tr_nerr( MY_NAME, "Unable to parse response \"%*.*s\"", (int)len, (int)len, (char*)response ); else { tr_benc *args, *list; @@ -346,6 +352,10 @@ processRequests( const char * host, int port, curl_easy_setopt( curl, CURLOPT_WRITEDATA, buf ); curl_easy_setopt( curl, CURLOPT_POST, 1 ); curl_easy_setopt( curl, CURLOPT_URL, url ); + if( auth ) { + curl_easy_setopt( curl, CURLOPT_USERPWD, auth ); + curl_easy_setopt( curl, CURLOPT_HTTPAUTH, CURLAUTH_ANY ); + } for( i=0; i