get RPC password protections working in libT and the gtk+ client. mac, daemon, and cli need to be synced.

This commit is contained in:
Charles Kerr 2008-06-05 16:23:03 +00:00
parent bd2ad5ca97
commit ab17f553c4
10 changed files with 285 additions and 70 deletions

View File

@ -428,7 +428,10 @@ main( int argc, char ** argv )
pref_int_get( PREF_KEY_PEER_SOCKET_TOS ),
pref_flag_get( PREF_KEY_RPC_ENABLED ),
pref_int_get( PREF_KEY_RPC_PORT ),
pref_string_get( PREF_KEY_RPC_ACL ) );
pref_string_get( PREF_KEY_RPC_ACL ),
pref_flag_get( PREF_KEY_RPC_PASSWORD_ENABLED ),
pref_string_get( PREF_KEY_RPC_USERNAME ),
pref_string_get( PREF_KEY_RPC_PASSWORD ) );
cbdata->core = tr_core_new( h );
/* create main window now to be a parent to any error dialogs */
@ -948,16 +951,22 @@ prefschanged( TrCore * core UNUSED, const char * key, gpointer data )
tr_sessionSetRPCACL( tr, s, &err );
g_free( s );
}
else if( !strcmp( key, PREF_KEY_RPC_USERNAME ) )
{
char * s = pref_string_get( key );
tr_sessionSetRPCUsername( tr, s );
g_free( s );
}
else if( !strcmp( key, PREF_KEY_RPC_PASSWORD ) )
{
char * s = pref_string_get( key );
/*FIXMEtr_sessionSetRPCACL( tr, s );*/
tr_sessionSetRPCPassword( tr, s );
g_free( s );
}
else if( !strcmp( key, PREF_KEY_RPC_PASSWORD_ENABLED ) )
{
const gboolean enabled = pref_flag_get( key );
/*FIXME tr_sessionSetRPCEnabled( tr, enabled );*/
tr_sessionSetRPCPasswordEnabled( tr, enabled );
}
}

View File

@ -35,8 +35,11 @@ void
tr_prefs_init_global( void )
{
int i;
char pw[12];
char pw[32];
const char * str;
const char * pool = "abcdefghijklmnopqrstuvwxyz"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"1234567890";
cf_check_older_configs( );
@ -101,13 +104,13 @@ tr_prefs_init_global( void )
pref_int_set_default ( PREF_KEY_RPC_PORT, TR_DEFAULT_RPC_PORT );
pref_string_set_default ( PREF_KEY_RPC_ACL, TR_DEFAULT_RPC_ACL );
for( i=0; i<8; ++i )
pw[i] = 'a' + tr_rand(26);
pw[8] = '\0';
for( i=0; i<16; ++i )
pw[i] = pool[ tr_rand( strlen( pool ) ) ];
pw[16] = '\0';
pref_string_set_default( PREF_KEY_RPC_USERNAME, "transmission" );
pref_string_set_default( PREF_KEY_RPC_PASSWORD, pw );
pref_flag_set_default ( PREF_KEY_RPC_PASSWORD_ENABLED, FALSE );
pref_save( NULL );
}
@ -615,10 +618,10 @@ struct remote_page
GtkTreeView * view;
GtkListStore * store;
GtkWidget * remove_button;
GtkWidget * pw_entry;
GSList * widgets;
GSList * auth_widgets;
GtkToggleButton * rpc_tb;
GtkToggleButton * pw_tb;
GtkToggleButton * auth_tb;
};
static void
@ -727,7 +730,7 @@ refreshRPCSensitivity( struct remote_page * page )
{
GSList * l;
const int rpc_active = gtk_toggle_button_get_active( page->rpc_tb );
const int pw_active = gtk_toggle_button_get_active( page->pw_tb );
const int auth_active = gtk_toggle_button_get_active( page->auth_tb );
GtkTreeSelection * sel = gtk_tree_view_get_selection( page->view );
const int have_addr = gtk_tree_selection_get_selected( sel, NULL, NULL );
const int n_rules = gtk_tree_model_iter_n_children(
@ -736,7 +739,8 @@ refreshRPCSensitivity( struct remote_page * page )
for( l=page->widgets; l!=NULL; l=l->next )
gtk_widget_set_sensitive( GTK_WIDGET( l->data ), rpc_active );
gtk_widget_set_sensitive( page->pw_entry, rpc_active && pw_active );
for( l=page->auth_widgets; l!=NULL; l=l->next )
gtk_widget_set_sensitive( GTK_WIDGET( l->data ), rpc_active && auth_active);
gtk_widget_set_sensitive( page->remove_button,
rpc_active && have_addr && n_rules>1 );
@ -748,11 +752,6 @@ onRPCToggled( GtkToggleButton * tb UNUSED, gpointer page )
refreshRPCSensitivity( page );
}
static void
onRPCPassToggled( GtkToggleButton * tb UNUSED, gpointer page )
{
refreshRPCSensitivity( page );
}
static void
onACLSelectionChanged( GtkTreeSelection * sel UNUSED, gpointer page )
{
refreshRPCSensitivity( page );
@ -765,8 +764,6 @@ remotePage( GObject * core )
int row = 0;
GtkWidget * t;
GtkWidget * w;
GtkWidget * w2;
GtkWidget * enabled_toggle;
struct remote_page * page = g_new0( struct remote_page, 1 );
page->core = TR_CORE( core );
@ -780,29 +777,37 @@ remotePage( GObject * core )
s = _( "A_llow requests from transmission-remote, Clutch, etc." );
w = new_check_button( s, PREF_KEY_RPC_ENABLED, core );
hig_workarea_add_wide_control( t, &row, w );
enabled_toggle = w;
page->rpc_tb = GTK_TOGGLE_BUTTON( w );
g_signal_connect( w, "clicked", G_CALLBACK(onRPCToggled), page );
/* password protection */
s = _( "Require _password:" );
/* require authentication */
s = _( "Require _authentication" );
w = new_check_button( s, PREF_KEY_RPC_PASSWORD_ENABLED, core );
g_signal_connect( w, "clicked", G_CALLBACK(onRPCPassToggled), page );
page->pw_tb = GTK_TOGGLE_BUTTON( w );
hig_workarea_add_wide_control( t, &row, w );
page->auth_tb = GTK_TOGGLE_BUTTON( w );
page->widgets = g_slist_append( page->widgets, w );
//toggles_set_sensitivity( w, enabled_toggle, NULL );
w2 = page->pw_entry = new_entry( PREF_KEY_RPC_PASSWORD, core );
//toggles_set_sensitivity( w2, enabled_toggle, w, NULL );
gtk_entry_set_visibility( GTK_ENTRY( w2 ), FALSE );
hig_workarea_add_row_w( t, &row, w, w2, NULL );
g_signal_connect( w, "clicked", G_CALLBACK(onRPCToggled), page );
/* username */
s = _( "_Username:" );
w = new_entry( PREF_KEY_RPC_USERNAME, core );
page->auth_widgets = g_slist_append( page->auth_widgets, w );
w = hig_workarea_add_row( t, &row, s, w, NULL );
page->auth_widgets = g_slist_append( page->auth_widgets, w );
/* password */
s = _( "_Password:" );
w = new_entry( PREF_KEY_RPC_PASSWORD, core );
gtk_entry_set_visibility( GTK_ENTRY( w ), FALSE );
page->auth_widgets = g_slist_append( page->auth_widgets, w );
w = hig_workarea_add_row( t, &row, s, w, NULL );
page->auth_widgets = g_slist_append( page->auth_widgets, w );
/* port */
w = new_spin_button( PREF_KEY_RPC_PORT, core, 0, 65535, 1 );
page->widgets = g_slist_append( page->widgets, w );
//toggles_set_sensitivity( w, enabled_toggle, NULL );
w = hig_workarea_add_row( t, &row, _( "Listening _port:" ), w, NULL );
page->widgets = g_slist_append( page->widgets, w );
//toggles_set_sensitivity( w, enabled_toggle, NULL );
/* access control list */
{
@ -821,7 +826,6 @@ remotePage( GObject * core )
w = gtk_tree_view_new_with_model( m );
page->widgets = g_slist_append( page->widgets, w );
//toggles_set_sensitivity( w, enabled_toggle, NULL );
v = page->view = GTK_TREE_VIEW( w );
gtk_tooltips_set_tip( tips, w,
_( "IP addresses may use wildcards, such as 192.168.*.*" ),
@ -849,7 +853,6 @@ remotePage( GObject * core )
w = hig_workarea_add_row( t, &row, s, w, NULL );
gtk_misc_set_alignment( GTK_MISC( w ), 0.0f, 0.1f );
page->widgets = g_slist_append( page->widgets, w );
//toggles_set_sensitivity( w, enabled_toggle, NULL );
g_free( val );
/* permission column */
@ -875,7 +878,6 @@ remotePage( GObject * core )
gtk_box_pack_start_defaults( GTK_BOX( h ), w );
w = gtk_button_new_from_stock( GTK_STOCK_ADD );
page->widgets = g_slist_append( page->widgets, w );
//toggles_set_sensitivity( w, enabled_toggle, NULL );
g_signal_connect( w, "clicked", G_CALLBACK(onAddACLClicked), page );
gtk_box_pack_start_defaults( GTK_BOX( h ), w );
w = gtk_hbox_new( FALSE, 0 );
@ -884,6 +886,7 @@ remotePage( GObject * core )
hig_workarea_add_wide_control( t, &row, w );
}
refreshRPCSensitivity( page );
hig_workarea_finish( t, &row );
return t;
}

View File

@ -59,6 +59,7 @@ GtkWidget * tr_prefs_dialog_new( GObject * core, GtkWindow * parent );
#define PREF_KEY_RPC_ACL "rpc-access-control-list"
#define PREF_KEY_RPC_PASSWORD_ENABLED "rpc-password-required"
#define PREF_KEY_RPC_PASSWORD "rpc-password"
#define PREF_KEY_RPC_USERNAME "rpc-username"
void tr_prefs_init_global( void );

View File

@ -57,7 +57,7 @@ SOFTWARE.
#include "ConvertUTF.h"
#if _MSC_VER >= 1400 /* Visual Studio 2005 and up */
# pragma warning(disable:4996) // unsecure sscanf
# pragma warning(disable:4996) /* unsecure sscanf */
#endif

View File

@ -105,13 +105,13 @@ tr_cpSizeWhenDone( const tr_completion * ccp )
for( i=0; i<info->pieceCount; ++i )
{
if( !info->pieces[i].dnd ) {
// we want the piece...
/* we want the piece... */
size += tr_torPieceCountBytes( tor, i );
} else if( tr_cpPieceIsComplete( cp, i ) ) {
// we have the piece...
/* we have the piece... */
size += tr_torPieceCountBytes( tor, i );
} else if( cp->completeBlocks[i] ) {
// we have part of the piece...
/* we have part of the piece... */
const tr_block_index_t b = tr_torPieceFirstBlock( tor, i );
const tr_block_index_t e = b + tr_torPieceCountBlocks( tor, i );
tr_block_index_t j;

View File

@ -16,7 +16,10 @@
#include <stdlib.h> /* strtol */
#include <string.h>
#include <unistd.h> /* unlink */
#include <libevent/event.h>
#include <shttpd/defs.h> /* edit_passwords */
#include <shttpd/shttpd.h>
#include "transmission.h"
@ -25,6 +28,7 @@
#include "utils.h"
#define MY_NAME "RPC Server"
#define MY_REALM "Transmission RPC Server"
#define BUSY_INTERVAL_MSEC 30
#define IDLE_INTERVAL_MSEC 100
@ -39,6 +43,9 @@ struct tr_rpc_server
struct evbuffer * in;
struct evbuffer * out;
struct event timer;
int isPasswordEnabled;
char * username;
char * password;
char * acl;
};
@ -119,6 +126,14 @@ rpcPulse( int socket UNUSED, short action UNUSED, void * vserver )
evtimer_add( &server->timer, &tv );
}
static void
getPasswordFile( tr_rpc_server * server, char * buf, int buflen )
{
tr_buildPath( buf, buflen, tr_sessionGetConfigDir( server->session ),
"htpasswd",
NULL );
}
static void
startServer( tr_rpc_server * server )
{
@ -127,18 +142,31 @@ startServer( tr_rpc_server * server )
if( !server->ctx )
{
char ports[128];
char passwd[MAX_PATH_LENGTH];
struct timeval tv = tr_timevalMsec( UNUSED_INTERVAL_MSEC );
getPasswordFile( server, passwd, sizeof( passwd ) );
if( !server->isPasswordEnabled )
unlink( passwd );
else
edit_passwords( passwd, MY_REALM, "user", "pass" );
server->ctx = shttpd_init( );
snprintf( ports, sizeof( ports ), "%d", server->port );
shttpd_register_uri( server->ctx, "/transmission", handle_rpc, server );
shttpd_set_option( server->ctx, "ports", ports );
shttpd_set_option( server->ctx, "dir_list", "0" );
shttpd_set_option( server->ctx, "root", "/dev/null" );
shttpd_set_option( server->ctx, "auth_realm", MY_REALM );
if( server->acl ) {
dbgmsg( "setting acl [%s]", server->acl );
shttpd_set_option( server->ctx, "acl", server->acl );
}
if( server->isPasswordEnabled ) {
char * buf = tr_strdup_printf( "/transmission=%s", passwd );
shttpd_set_option( server->ctx, "protect", buf );
tr_free( buf );
}
evtimer_set( &server->timer, rpcPulse, server );
evtimer_add( &server->timer, &tv );
@ -150,6 +178,10 @@ stopServer( tr_rpc_server * server )
{
if( server->ctx )
{
char passwd[MAX_PATH_LENGTH];
getPasswordFile( server, passwd, sizeof( passwd ) );
unlink( passwd );
evtimer_del( &server->timer );
shttpd_fini( server->ctx );
server->ctx = NULL;
@ -193,6 +225,10 @@ tr_rpcGetPort( const tr_rpc_server * server )
return server->port;
}
/****
***** ACL
****/
/*
* DELIM_CHARS, FOR_EACH_WORD_IN_LIST, isbyte, and testACL are from, or modified from,
* shttpd, written by Sergey Lyubka under this license:
@ -327,12 +363,88 @@ tr_rpcSetACL( tr_rpc_server * server,
return err;
}
const char*
char*
tr_rpcGetACL( const tr_rpc_server * server )
{
return server->acl ? server->acl : "";
return tr_strdup( server->acl ? server->acl : "" );
}
/****
***** PASSWORD
****/
void
tr_rpcSetUsername( tr_rpc_server * server,
const char * username )
{
const int isRunning = server->ctx != NULL;
if( isRunning )
stopServer( server );
tr_free( server->username );
server->username = tr_strdup( username );
dbgmsg( "setting our Username to [%s]", server->username );
if( isRunning )
startServer( server );
}
char*
tr_rpcGetUsername( const tr_rpc_server * server )
{
return tr_strdup( server->username ? server->username : "" );
}
void
tr_rpcSetPassword( tr_rpc_server * server,
const char * password )
{
const int isRunning = server->ctx != NULL;
if( isRunning )
stopServer( server );
tr_free( server->password );
server->password = tr_strdup( password );
dbgmsg( "setting our Password to [%s]", server->password );
if( isRunning )
startServer( server );
}
char*
tr_rpcGetPassword( const tr_rpc_server * server )
{
return tr_strdup( server->password ? server->password : "" );
}
void
tr_rpcSetPasswordEnabled( tr_rpc_server * server,
int isEnabled )
{
const int isRunning = server->ctx != NULL;
if( isRunning )
stopServer( server );
server->isPasswordEnabled = isEnabled;
dbgmsg( "setting 'password enabled' to %d", isEnabled );
if( isRunning )
startServer( server );
}
int
tr_rpcIsPasswordEnabled( const tr_rpc_server * server )
{
return server->isPasswordEnabled;
}
/****
***** LIFE CYCLE
****/
void
tr_rpcClose( tr_rpc_server ** ps )
{
@ -350,7 +462,10 @@ tr_rpc_server *
tr_rpcInit( tr_handle * session,
int isEnabled,
int port,
const char * acl )
const char * acl,
int isPasswordEnabled,
const char * username,
const char * password )
{
char * errmsg;
tr_rpc_server * s;
@ -369,6 +484,9 @@ tr_rpcInit( tr_handle * session,
s->in = evbuffer_new( );
s->out = evbuffer_new( );
s->acl = tr_strdup( acl );
s->username = tr_strdup( username );
s->password = tr_strdup( password );
s->isPasswordEnabled = isPasswordEnabled;
if( isEnabled )
startServer( s );

View File

@ -15,35 +15,53 @@
typedef struct tr_rpc_server tr_rpc_server;
tr_rpc_server * tr_rpcInit ( struct tr_handle * session,
int isEnabled,
int port,
const char * acl );
tr_rpc_server * tr_rpcInit ( struct tr_handle * session,
int isEnabled,
int port,
const char * acl,
int isPasswordEnabled,
const char * username,
const char * password );
void tr_rpcClose ( tr_rpc_server ** freeme );
void tr_rpcClose ( tr_rpc_server ** freeme );
void tr_rpcSetEnabled( tr_rpc_server * server,
int isEnabled );
void tr_rpcSetEnabled ( tr_rpc_server * server,
int isEnabled );
int tr_rpcIsEnabled ( const tr_rpc_server * server );
int tr_rpcIsEnabled ( const tr_rpc_server * server );
void tr_rpcSetPort ( tr_rpc_server * server,
int port );
void tr_rpcSetPort ( tr_rpc_server * server,
int port );
int tr_rpcGetPort ( const tr_rpc_server * server );
int tr_rpcGetPort ( const tr_rpc_server * server );
int tr_rpcSetTest ( const tr_rpc_server * server,
const char * acl,
char ** allocme_errmsg );
int tr_rpcSetTest ( const tr_rpc_server * server,
const char * acl,
char ** allocme_errmsg );
int tr_rpcTestACL ( const tr_rpc_server * server,
const char * acl,
char ** allocme_errmsg );
int tr_rpcTestACL ( const tr_rpc_server * server,
const char * acl,
char ** allocme_errmsg );
int tr_rpcSetACL ( tr_rpc_server * server,
const char * acl,
char ** allocme_errmsg );
int tr_rpcSetACL ( tr_rpc_server * server,
const char * acl,
char ** allocme_errmsg );
const char* tr_rpcGetACL ( const tr_rpc_server * server );
char* tr_rpcGetACL ( const tr_rpc_server * server );
void tr_rpcSetPassword ( tr_rpc_server * server,
const char * password );
char* tr_rpcGetPassword ( const tr_rpc_server * server );
void tr_rpcSetUsername ( tr_rpc_server * server,
const char * username );
char* tr_rpcGetUsername ( const tr_rpc_server * server );
void tr_rpcSetPasswordEnabled ( tr_rpc_server * server,
int isEnabled );
int tr_rpcIsPasswordEnabled ( const tr_rpc_server * session );
#endif

View File

@ -137,7 +137,10 @@ tr_sessionInitFull( const char * configDir,
int peerSocketTOS,
int rpcIsEnabled,
int rpcPort,
const char * rpcACL )
const char * rpcACL,
int rpcPasswordIsEnabled,
const char * rpcUsername,
const char * rpcPassword )
{
tr_handle * h;
char filename[MAX_PATH_LENGTH];
@ -199,7 +202,8 @@ tr_sessionInitFull( const char * configDir,
tr_statsInit( h );
h->web = tr_webInit( h );
h->rpcServer = tr_rpcInit( h, rpcIsEnabled, rpcPort, rpcACL );
h->rpcServer = tr_rpcInit( h, rpcIsEnabled, rpcPort, rpcACL,
rpcPasswordIsEnabled, rpcUsername, rpcPassword );
metainfoLookupRescan( h );
@ -229,7 +233,10 @@ tr_sessionInit( const char * configDir,
TR_DEFAULT_PEER_SOCKET_TOS,
TR_DEFAULT_RPC_ENABLED,
TR_DEFAULT_RPC_PORT,
TR_DEFAULT_RPC_ACL );
TR_DEFAULT_RPC_ACL,
FALSE,
"fnord",
"potzrebie" );
}
/***
@ -791,8 +798,44 @@ tr_sessionSetRPCACL( tr_handle * session,
return tr_rpcSetACL( session->rpcServer, acl, allocme_errmsg );
}
const char*
char*
tr_sessionGetRPCACL( const tr_session * session )
{
return tr_rpcGetACL( session->rpcServer );
}
void
tr_sessionSetRPCPassword( tr_handle * session, const char * password )
{
tr_rpcSetPassword( session->rpcServer, password );
}
char*
tr_sessionGetRPCPassword( const tr_handle * session )
{
return tr_rpcGetPassword( session->rpcServer );
}
void
tr_sessionSetRPCUsername( tr_handle * session, const char * username )
{
tr_rpcSetUsername( session->rpcServer, username );
}
char*
tr_sessionGetRPCUsername( const tr_handle * session )
{
return tr_rpcGetUsername( session->rpcServer );
}
void
tr_sessionSetRPCPasswordEnabled( tr_handle * session, int isEnabled )
{
tr_rpcSetPasswordEnabled( session->rpcServer, isEnabled );
}
int
tr_sessionIsRPCPasswordEnabled( const tr_handle * session )
{
return tr_rpcIsPasswordEnabled( session->rpcServer );
}

View File

@ -327,7 +327,6 @@ onTrackerResponse( tr_session * session,
success = TRUE;
if(( tr_bencDictFindStr( &benc, "failure reason", &str ))) {
// publishErrorMessageAndStop( t, str );
publishMessage( t, str, TR_TRACKER_ERROR );
success = FALSE;
}

View File

@ -229,7 +229,10 @@ tr_handle * tr_sessionInitFull( const char * configDir,
int peerSocketTOS,
int rpcIsEnabled,
int rpcPort,
const char * rpcAccessControlList );
const char * rpcAccessControlList,
int rpcPasswordIsEnabled,
const char * rpcUsername,
const char * rpcPassword );
/** @brief Shorter form of tr_sessionInitFull()
@deprecated Use tr_sessionInitFull() instead. */
@ -338,10 +341,31 @@ int tr_sessionSetRPCACL( tr_handle * session,
const char * acl,
char ** allocme_errmsg );
/** Returns the Access Control List for allowing/denying RPC requests.
/** @brief get the Access Control List for allowing/denying RPC requests.
@return a comma-separated string of ACL rules. tr_free() when done.
@see tr_sessionInitFull
@see tr_sessionSetRPCACL */
const char* tr_sessionGetRPCACL( const tr_handle * );
char* tr_sessionGetRPCACL( const tr_handle * );
void tr_sessionSetRPCPassword( tr_handle * session,
const char * password );
void tr_sessionSetRPCUsername( tr_handle * session,
const char * username );
/** @brief get the password used to restrict RPC requests.
@return the password string. tr_free() when done.
@see tr_sessionInitFull()
@see tr_sessionSetRPCPassword() */
char* tr_sessionGetRPCPassword( const tr_handle * session );
char* tr_sessionGetRPCUsername( const tr_handle * session );
void tr_sessionSetRPCPasswordEnabled( tr_handle * session,
int isEnabled );
int tr_sessionIsRPCPasswordEnabled( const tr_handle * session );
typedef enum
{