#include <clientapi.h> #include <options.h> #include <msgsupp.h> #include <errorlog.h> #include <rpc.h> #include <ticket.h> #include <client.h> #include <msgrpc.h> static ErrorId trustHelp = { ErrorOf( 0, 0, E_INFO, 0, 0), "\n" " trust -- Establish trust of an SSL connection\n" "\n" " p4 trust [ -l -y -n -d -f -r -i <fingerprint> ]\n" "\n" " Establish trust of an SSL connection. This client command manages\n" " the p4 trust file. This file contains fingerprints of the keys\n" " received on ssl connections. When an SSL connection is made, this\n" " file is examined to determine if the SSL connection has been used\n" " before and if the key is the same as a previously seen key for that\n" " connection. Establishing trust with a connection prevents undetected\n" " communication interception (man-in-the-middle) attacks.\n" "\n" " Most options are mutually exclusive. Only the -r and -f options\n" " can be combined with the others.\n" "\n" " The -l flag lists existing known fingerprints.\n" "\n" " Without options, this command will make a connection to a server\n" " and examine the key if present, if one cannot be found this command\n" " will show a fingerprint and ask if this connection should be trusted.\n" " If a fingerprint exists and does not match, an error that a possible\n" " security problems exists will be displayed.\n" "\n" " The -y flag will cause prompts to be automatically accepted.\n" "\n" " The -n flag will cause prompts to be automatically refused.\n" "\n" " The -d flag will remove an existing trusted fingerprint of a connection.\n" "\n" " The -f flag will force the replacement of a mismatched fingerprint.\n" "\n" " The -i flag will allow a specific fingerprint to be installed.\n" "\n" " The -r flag specifies that a replacement fingerprint is to be\n" " affected. Replacement fingerprints can be used in anticipation\n" " of a server replacing its key. If a replacement fingerprint\n" " exists for a connection and the primary fingerprint does not match\n" " while the replacement fingerprint does, the replacement fingerprint\n" " will replace the primary. This flag can be combined with -l, -i,\n" " or -d.\n" }; static ErrorId trustUsage = { ErrorOf( 0, 0, E_FAILED, 0, 0), "p4 [ -p port ] trust [ -y -n -d -l -f -r -i <fingerprint> ]\n" "\tOnly one of -y -n -d -l -i is allowed.\n" "\tUse p4 trust -h for detailed help.\n" }; static ErrorId trustNotSSL = { ErrorOf( 0, 0, E_INFO, 0, 0), "Only SSL connections require trust" }; int clientTrustHelp( Error *e ) { ClientUser cuser; e->Set( trustHelp ); cuser.Message( e ); e->Clear(); return 0; } inline void OutputBuffer( Client *c, const StrPtr &b ) { c->GetUi()->OutputText( b.Text(), b.Length() ); } static void InstallTrust( Client *client, StrPtr *peer, StrPtr &u, StrPtr &k, Error *e ) { StrRef r( client->GetTrustFile() ); Ticket f( &r ); f.ReplaceTicket( *peer, u, k, e ); } static void DeleteTrust( Client *client, StrPtr *peer, StrPtr &u, Error *e ) { StrRef r( client->GetTrustFile() ); Ticket f( &r ); f.DeleteTicket( *peer, u, e ); } static void ReportPeerKey( Client *client, StrPtr *peer, StrPtr *key ) { StrBuf buf; buf.Set( "The fingerprint of the server of your P4PORT setting\n" ); buf.Append( peer ); buf.Append( " is not known.\n" ); buf.Append( "That fingerprint is " ); buf.Append( key ); buf.Append( "\n" ); OutputBuffer( client, buf ); } void clientTrust( Client *client, Error *e ) { AssertLog.SetTag( "trust" ); Options opts; int argc = client->GetArgc(); StrPtr *argv = client->GetArgv(); int longOpts[] = { Options::InputValue, Options::Delete, Options::Status, Options::Yes, Options::No, Options::Force, Options::Replacement, 0 }; opts.ParseLong( argc, argv, "hyndflri:", longOpts, OPT_NONE, trustUsage, e ); if( e->Test() ) return; int yflag = opts[ 'y' ] != 0; int nflag = opts[ 'n' ] != 0; int fflag = opts[ 'f' ] != 0; int dflag = opts[ 'd' ] != 0; int lflag = opts[ 'l' ] != 0; int rflag = opts[ 'r' ] != 0; int hflag = opts[ 'h' ] != 0; int flags = yflag + nflag + dflag + lflag + hflag; StrPtr *iflag = opts[ 'i' ]; if( flags > ( iflag ? 0 : 1 ) ) // tricky! { e->Set( MsgSupp::TooMany ); e->Set( trustUsage ); return; } if( hflag ) { e->Set( trustHelp ); client->GetUi()->Message( e ); return; } StrPtr *peer; peer = client->GetPeerAddress( RAF_PORT ); StrRef port( client->GetPort() ); StrBuf peername( "'" ); peername.Append( &port ); peername.Append( "' (" ); peername.Append( peer ); peername.Append( ")" ); StrBuf fingerprint; client->GetPeerFingerprint( fingerprint ); if( !fingerprint.Length() ) { e->Set( trustNotSSL ); client->GetUi()->Message( e ); return; } StrRef u( rflag ? "++++++" : "**++**" ); if( lflag ) { // list operation is different StrRef r( client->GetTrustFile() ); Ticket f( &r ); StrBuf o; f.ListUser( u, o ); OutputBuffer( client, o ); return; } client->CheckKnownHost( e, client->GetTrustFile() ); int mismatch = e->CheckId( MsgRpc::HostKeyMismatch ); int unknown = e->CheckId( MsgRpc::HostKeyUnknown ); if( iflag ) { if( unknown ) { ReportPeerKey( client, &peername, &fingerprint ); e->Clear(); } else if( e->Test() ) { client->GetUi()->Message( e ); e->Clear(); } InstallTrust( client, peer, u, *opts[ 'i' ], e ); if( !e->Test() ) { StrBuf done; done.Set( "Added trust for P4PORT " ); done.Append( &peername ); done.Append( "\n" ); OutputBuffer( client, done ); } return; } if( !e->Test() ) { if( dflag ) { DeleteTrust( client, peer, u, e ); if( !e->Test() ) { StrBuf done; done.Set( "Removed trust for P4PORT " ); done.Append( &peername ); done.Append( "\n" ); OutputBuffer( client, done ); } } else { StrRef done( "Trust already established.\n"); OutputBuffer( client, done ); } return; } // missing or wrong if( unknown ) ReportPeerKey( client, &peername, &fingerprint ); else client->GetUi()->Message( e ); e->Clear(); if( dflag ) { // delete it! DeleteTrust( client, peer, u, e ); if( !e->Test() ) { StrBuf done; done.Set( "Removed trust for P4PORT " ); done.Append( &peername ); done.Append( "\n" ); OutputBuffer( client, done ); } return; } if( nflag ) { client->SetError(); return; } if( !fflag && mismatch ) { StrRef done( "Can't trust mismatched P4PORT key without the '-f' force option.\n" ); OutputBuffer( client, done ); client->SetError(); return; } if( !yflag ) { StrBuf res; client->GetUi()->Prompt( StrRef( "Are you sure you want to establish trust (yes/no)? " ), res, 0, e ); if( e->Test() || res != "y" && res != "yes" ) { client->SetError(); return; } } InstallTrust( client, peer, u, fingerprint, e ); if( e->Test() ) client->SetError(); else { StrBuf done; done.Set( "Added trust for P4PORT " ); done.Append( &peername ); done.Append( "\n" ); OutputBuffer( client, done ); } }