3131#include < QLocalServer>
3232#include < QtNetwork>
3333#include < QDebug>
34+ #include < QDir>
35+ #include < QRandomGenerator>
36+ #include < QFile>
3437
3538#include " ipc.h"
3639#include " utils.h"
3740
41+ // Max size of an IPC command in bytes. Payment URIs are short; anything larger
42+ // is treated as invalid to avoid unbounded reads from local clients.
43+ static const int IPC_MAX_CMD_SIZE = 4096 ;
44+
3845// Start listening for incoming IPC commands on UDS (Unix) or named pipe (Windows)
3946void IPC::bind (){
4047 QString path = QString (this ->m_socketFile .absoluteFilePath ());
41- qDebug () << path;
48+
49+ // Generate a fresh shared secret so only processes that know it (i.e. other
50+ // instances started by the same user) can submit commands to this server.
51+ if (!writeTokenFile ()) {
52+ qWarning () << " IPC: unable to write token file, commands will be rejected" ;
53+ m_token.clear ();
54+ }
55+ qDebug () << " IPC socket:" << path;
4256
4357 this ->m_server = new QLocalServer (this );
4458 this ->m_server ->setSocketOptions (QLocalServer::UserAccessOption);
@@ -72,22 +86,35 @@ void IPC::bind(){
7286// when queued, false if sent to another instance, at which point we can
7387// kill the current process.
7488bool IPC::saveCommand (QString cmdString){
75- qDebug () << QString (" saveCommand called: %1" ).arg (cmdString);
89+ if (cmdString.length () > IPC_MAX_CMD_SIZE ) {
90+ qWarning () << " saveCommand: command too large, ignoring" ;
91+ return true ;
92+ }
93+
94+ // The server only accepts commands that carry the shared token.
95+ QString token;
96+ if (!readTokenFile (token) || token.isEmpty ()) {
97+ qWarning () << " saveCommand: no IPC token available, queueing command" ;
98+ this ->SetQueuedCmd (cmdString);
99+ return true ;
100+ }
76101
77102 QLocalSocket ls;
78103 QByteArray buffer;
79- buffer = buffer.append (cmdString.toUtf8 ());
104+ buffer.append (token.toUtf8 ());
105+ buffer.append (' \n ' );
106+ buffer.append (cmdString.toUtf8 ());
80107 QString socketFilePath = this ->socketFile ().filePath ();
81108
82109 ls.connectToServer (socketFilePath, QIODevice::WriteOnly);
83110 if (ls.waitForConnected (1000 )){
84111 ls.write (buffer);
85112 if (!ls.waitForBytesWritten (1000 )){
86- qDebug () << QString (" Could not send command \" %1 \" over IPC %2 : \" %3 \" " ).arg (cmdString, socketFilePath, ls.errorString ());
113+ qDebug () << QString (" Could not send command over IPC %1 : \" %2 \" " ).arg (socketFilePath, ls.errorString ());
87114 return false ;
88115 }
89116
90- qDebug () << QString ( " Sent command \" %1 \" over IPC \" %2 \" " ). arg (cmdString, socketFilePath) ;
117+ qDebug () << " Sent command over IPC" << socketFilePath;
91118 return false ;
92119 }
93120
@@ -109,9 +136,32 @@ void IPC::handleConnection(){
109136 clientConnection, &QLocalSocket::deleteLater);
110137
111138 clientConnection->waitForReadyRead (2 );
112- QString cmdString = QString (clientConnection->readAll ());
113- qDebug () << cmdString;
139+ QByteArray data = clientConnection->readAll ();
114140
141+ // Reject oversized or empty payloads.
142+ if (data.isEmpty () || data.size () > IPC_MAX_CMD_SIZE + 1 + 64 ) {
143+ clientConnection->close ();
144+ delete clientConnection;
145+ return ;
146+ }
147+
148+ // The payload must start with the shared token followed by a newline.
149+ int sep = data.indexOf (' \n ' );
150+ if (sep <= 0 ) {
151+ clientConnection->close ();
152+ delete clientConnection;
153+ return ;
154+ }
155+
156+ QString receivedToken = QString::fromUtf8 (data.left (sep));
157+ if (receivedToken != m_token) {
158+ qWarning () << " IPC: rejecting command with invalid token" ;
159+ clientConnection->close ();
160+ delete clientConnection;
161+ return ;
162+ }
163+
164+ QString cmdString = QString::fromUtf8 (data.mid (sep + 1 ));
115165 this ->parseCommand (cmdString);
116166
117167 clientConnection->close ();
@@ -131,3 +181,34 @@ void IPC::parseCommand(QString cmdString){
131181void IPC::emitUriHandler (QString uriString){
132182 emit uriHandler (uriString);
133183}
184+
185+ bool IPC::writeTokenFile ()
186+ {
187+ // 32 random bytes, hex-encoded (64 chars). The token is regenerated on
188+ // every bind() so an attacker cannot predict it across runs.
189+ QByteArray random;
190+ for (int i = 0 ; i < 8 ; ++i)
191+ random.append (QRandomGenerator::system ()->generate ());
192+ m_token = QString::fromLatin1 (random.toHex ());
193+
194+ QFile f (m_tokenFile.absoluteFilePath ());
195+ if (!f.open (QIODevice::WriteOnly | QIODevice::Truncate))
196+ return false ;
197+ f.write (m_token.toUtf8 ());
198+ f.close ();
199+ #ifdef Q_OS_UNIX
200+ QFile::setPermissions (m_tokenFile.absoluteFilePath (),
201+ QFile::ReadOwner | QFile::WriteOwner);
202+ #endif
203+ return true ;
204+ }
205+
206+ bool IPC::readTokenFile (QString &token) const
207+ {
208+ QFile f (m_tokenFile.absoluteFilePath ());
209+ if (!f.open (QIODevice::ReadOnly))
210+ return false ;
211+ token = QString::fromUtf8 (f.readAll ()).trimmed ();
212+ f.close ();
213+ return !token.isEmpty ();
214+ }
0 commit comments