1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 The Qt Company Ltd.
4 ** Contact: http://www.qt.io/licensing/
5 **
6 ** This file is part of the QtGui module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see http://www.qt.io/terms-conditions. For further
15 ** information use the contact form at http://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 or version 3 as published by the Free
20 ** Software Foundation and appearing in the file LICENSE.LGPLv21 and
21 ** LICENSE.LGPLv3 included in the packaging of this file. Please review the
22 ** following information to ensure the GNU Lesser General Public License
23 ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
24 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 **
26 ** As a special exception, The Qt Company gives you certain additional
27 ** rights. These rights are described in The Qt Company LGPL Exception
28 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 **
30 ** GNU General Public License Usage
31 ** Alternatively, this file may be used under the terms of the GNU
32 ** General Public License version 3.0 as published by the Free Software
33 ** Foundation and appearing in the file LICENSE.GPL included in the
34 ** packaging of this file.  Please review the following information to
35 ** ensure the GNU General Public License version 3.0 requirements will be
36 ** met: http://www.gnu.org/copyleft/gpl.html.
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41 
42 #include "qtransportauth_qws.h"
43 #include "qtransportauth_qws_p.h"
44 
45 #ifndef QT_NO_SXE
46 
47 #include "../../3rdparty/md5/md5.h"
48 #include "../../3rdparty/md5/md5.cpp"
49 #include "qwsutils_qws.h"
50 #include "qwssocket_qws.h"
51 #include "qwscommand_qws_p.h"
52 #include "qwindowsystem_qws.h"
53 #include "qbuffer.h"
54 #include "qthread.h"
55 #include "qabstractsocket.h"
56 #include "qlibraryinfo.h"
57 #include "qfile.h"
58 #include "qdebug.h"
59 #include <private/qcore_unix_p.h> // overrides QT_OPEN
60 
61 #include <syslog.h>
62 #include <unistd.h>
63 #include <fcntl.h>
64 #include <sys/stat.h>
65 #include <sys/types.h>
66 #include <sys/socket.h>
67 #include <sys/file.h>
68 #include <stdio.h>
69 #include <stdlib.h>
70 #include <errno.h>
71 #include <time.h>
72 
73 #include <QtCore/qcache.h>
74 
75 #define BUF_SIZE 512
76 
77 QT_BEGIN_NAMESPACE
78 
79 /*!
80   \internal
81   memset for security purposes, guaranteed not to be optimized away
82   http://www.faqs.org/docs/Linux-HOWTO/Secure-Programs-HOWTO.html
83 */
guaranteed_memset(void * v,int c,size_t n)84 Q_GUI_EXPORT void *guaranteed_memset(void *v,int c,size_t n)
85 {
86     volatile char *p = (char *)v; while (n--) *p++=c; return v;
87 }
88 
89 /*!
90   \class QTransportAuth
91   \internal
92 
93   \brief Authenticate a message transport.
94 
95   For performance reasons, message authentication is tied to an individual
96   message transport instance.  For example in connection oriented transports
97   the authentication cookie can be cached against the connection avoiding
98   the overhead of authentication on every message.
99 
100   For each process there is one instance of the QTransportAuth object.
101   For server processes it can determine the \link secure-exe-environ.html SXE
102   Program Identity \endlink and provide access to policy data to determine if
103   the message should be forwarded for action.  If not actioned, the message
104   may be treated as being from a flawed or malicious process.
105 
106   Retrieve the instance with the getInstance() method.  The constructor is
107   disabled and instances of QTransportAuth should never be constructed by
108   calling classes.
109 
110   To make the Authentication easier to use a proxied QIODevice is provided
111   which uses an internal QBuffer.
112 
113   In the server code first get a pointer to a QTransportAuth::Data object
114   using the connectTransport() method:
115 
116   \snippet doc/src/snippets/code/src_gui_embedded_qtransportauth_qws.cpp 0
117 
118   Here it is asserted that the transport is trusted.  See the assumptions
119   listed in the \link secure-exe-environ.html SXE documentation \endlink
120 
121   Then proxy in the authentication device:
122 
123   \snippet doc/src/snippets/code/src_gui_embedded_qtransportauth_qws.cpp 1
124 
125   In the client code it is similar.  Use the connectTransport() method
126   just the same then proxy in the authentication device instead of the
127   socket in write calls:
128 
129   \snippet doc/src/snippets/code/src_gui_embedded_qtransportauth_qws.cpp 2
130 */
131 
132 static int hmac_md5(
133         unsigned char*  text,         /* pointer to data stream */
134         int             text_length,  /* length of data stream */
135         const unsigned char*  key,    /* pointer to authentication key */
136         int             key_length,   /* length of authentication key */
137         unsigned char * digest        /* caller digest to be filled in */
138         );
139 
140 
141 
142 #define KEY_CACHE_SIZE 30
143 
144 const char * const errorStrings[] = {
145     "pending identity verification",
146     "message too small to carry auth data",
147     "cache miss on connection oriented transport",
148     "no magic bytes on message",
149     "key not found for prog id",
150     "authorization key match failed",
151     "key out of date"
152 };
153 
errorString(const Data & d)154 const char *QTransportAuth::errorString( const Data &d )
155 {
156     if (( d.status & ErrMask ) == Success )
157         return "success";
158     int e = d.status & ErrMask;
159     if ( e > OutOfDate )
160         return "unknown";
161     return errorStrings[e];
162 }
163 
SxeRegistryLocker(QObject * reg)164 SxeRegistryLocker::SxeRegistryLocker( QObject *reg )
165     : m_success( false )
166     , m_reg( 0 )
167 {
168     if ( reg )
169         if ( !QMetaObject::invokeMethod( reg, "lockManifest", Q_RETURN_ARG(bool, m_success) ))
170             m_success = false;
171     m_reg = reg;
172 }
173 
~SxeRegistryLocker()174 SxeRegistryLocker::~SxeRegistryLocker()
175 {
176     if ( m_success )
177         QMetaObject::invokeMethod( m_reg, "unlockManifest" );
178 }
179 
180 
QTransportAuthPrivate()181 QTransportAuthPrivate::QTransportAuthPrivate()
182     : keyInitialised(false)
183     , m_packageRegistry( 0 )
184 {
185 }
186 
~QTransportAuthPrivate()187 QTransportAuthPrivate::~QTransportAuthPrivate()
188 {
189 }
190 
191 /*!
192   \internal
193   Construct a new QTransportAuth
194 */
QTransportAuth()195 QTransportAuth::QTransportAuth() : QObject(*new QTransportAuthPrivate)
196 {
197     // qDebug( "creating transport auth" );
198 }
199 
200 /*!
201   \internal
202   Destructor
203 */
~QTransportAuth()204 QTransportAuth::~QTransportAuth()
205 {
206     // qDebug( "deleting transport auth" );
207 }
208 
209 /*!
210   Set the process key for this currently running Qt Extended process to
211   the \a authdata.  \a authdata should be sizeof(struct AuthCookie)
212   in length and contain the key and program id.  Use this method
213   when setting or changing the SXE identity of the current program.
214 */
setProcessKey(const char * authdata)215 void QTransportAuth::setProcessKey( const char *authdata )
216 {
217     Q_D(QTransportAuth);
218     ::memcpy(&d->authKey, authdata, sizeof(struct AuthCookie));
219     QFile proc_key( QLatin1String("/proc/self/lids_key") );
220     // where proc key exists use that instead
221     if ( proc_key.open( QIODevice::ReadOnly ))
222     {
223         qint64 kb = proc_key.read( (char*)&d->authKey.key, QSXE_KEY_LEN );
224 #ifdef QTRANSPORTAUTH_DEBUG
225         qDebug( "Using %li bytes of /proc/%i/lids_key\n", (long int)kb, getpid() );
226 #else
227         Q_UNUSED( kb );
228 #endif
229     }
230     d->keyInitialised = true;
231 }
232 
233 
234 /*!
235   Apply \a key as the process key for the currently running application.
236 
237   \a prog is current ignored
238 
239   Deprecated function
240 */
setProcessKey(const char * key,const char * prog)241 void QTransportAuth::setProcessKey( const char *key, const char *prog )
242 {
243     Q_UNUSED(prog);
244     setProcessKey( key );
245 #ifdef QTRANSPORTAUTH_DEBUG
246     char displaybuf[QSXE_KEY_LEN*2+1];
247     hexstring( displaybuf, (const unsigned char *)key, QSXE_KEY_LEN );
248     qDebug() << "key" << displaybuf << "set";
249 #endif
250 }
251 
252 /*!
253   Register \a pr as a policy handler object.  The object pointed to
254   by \a pr should have a slot as follows
255   \snippet doc/src/snippets/code/src_gui_embedded_qtransportauth_qws.cpp 3
256   All requests received by this server will then generate a call to
257   this slot, and may be processed for policy compliance.
258 */
registerPolicyReceiver(QObject * pr)259 void QTransportAuth::registerPolicyReceiver( QObject *pr )
260 {
261     // not every policy receiver needs setup - no error if this fails
262     QMetaObject::invokeMethod( pr, "setupPolicyCheck" );
263 
264     connect( this, SIGNAL(policyCheck(QTransportAuth::Data&,QString)),
265              pr, SLOT(policyCheck(QTransportAuth::Data&,QString)), Qt::DirectConnection );
266 }
267 
268 /*!
269   Unregister the \a pr from being a policy handler.  No more policyCheck signals
270   are received by this object.
271 */
unregisterPolicyReceiver(QObject * pr)272 void QTransportAuth::unregisterPolicyReceiver( QObject *pr )
273 {
274     disconnect( pr );
275     // not every policy receiver needs tear down - no error if this fails
276     QMetaObject::invokeMethod( pr, "teardownPolicyCheck" );
277 }
278 
279 /*!
280   Record a new transport connection with \a properties and \a descriptor.
281 
282   The calling code is responsible for destroying the returned data when the
283   tranport connection is closed.
284 */
connectTransport(unsigned char properties,int descriptor)285 QTransportAuth::Data *QTransportAuth::connectTransport( unsigned char properties, int descriptor )
286 {
287     Data *data = new Data(properties, descriptor);
288     data->status = Pending;
289     return data;
290 }
291 
292 /*!
293   Is the transport trusted.  This is true iff data written into the
294   transport medium cannot be intercepted or modified by another process.
295   This is for example true for Unix Domain Sockets, but not for shared
296   memory or UDP sockets.
297 
298   There is of course an underlying assumption that the kernel implementing
299   the transport is sound, ie it cannot be compromised by writing to
300   /dev/kmem or loading untrusted modules
301 */
trusted() const302 inline bool QTransportAuth::Data::trusted() const
303 {
304     return (bool)(properties & Trusted);
305 }
306 
307 /*!
308   Assert that the transport is trusted.
309 
310   For example with respect to shared memory, if it is ensured that no untrusted
311   root processes are running, and that unix permissions have been set such that
312   any untrusted non-root processes do not have access rights, then a shared
313   memory transport could be asserted to be trusted.
314 
315   \sa trusted()
316 */
setTrusted(bool t)317 inline void QTransportAuth::Data::setTrusted( bool t )
318 {
319     properties = t ? properties | Trusted : properties & ~Trusted;
320 }
321 
322 /*!
323   Is the transport connection oriented.  This is true iff once a connection
324   has been accepted, and state established, then further messages over the
325   transport are guaranteed to have come from the original connecting entity.
326   This is for example true for Unix Domain Sockets, but not
327   for shared memory or UDP sockets.
328 
329   By extension if the transport is not trusted() then it should not be
330   assumed to be connection oriented, since spoofed connection information
331   could be created.  For example if we assume the TCP/IP transport is
332   trusted, it can be treated as connection oriented; but this is only the
333   case if intervening routers are trusted.
334 
335   Connection oriented transports have authorization cached against the
336   connection, and thus authorization is only done at connect time.
337 */
connection() const338 inline bool QTransportAuth::Data::connection() const
339 {
340     return (bool)(properties & Connection);
341 }
342 
343 /*!
344   Assert that the transport is connection oriented.
345 
346   \sa connection()
347 */
setConnection(bool t)348 inline void QTransportAuth::Data::setConnection( bool t )
349 {
350     properties = t ? properties | Connection : properties & ~Connection;
351 }
352 
353 /*!
354   Return a pointer to the instance of this process's QTransportAuth object
355 */
getInstance()356 QTransportAuth *QTransportAuth::getInstance()
357 {
358     static QTransportAuth theInstance;
359 
360     return &theInstance;
361 }
362 
363 /*!
364   Set the full path to the key file
365 
366   Since this is normally relative to Qtopia::qpeDir() this needs to be
367   set within the Qt Extended framework.
368 
369   The keyfile should be protected by file permissions or by MAC rules
370   such that it can only be read/written by the "qpe" server process
371 */
setKeyFilePath(const QString & path)372 void QTransportAuth::setKeyFilePath( const QString &path )
373 {
374     Q_D(QTransportAuth);
375     d->m_keyFilePath = path;
376 }
377 
keyFilePath() const378 QString QTransportAuth::keyFilePath() const
379 {
380     Q_D(const QTransportAuth);
381     return d->m_keyFilePath;
382 }
383 
setLogFilePath(const QString & path)384 void QTransportAuth::setLogFilePath( const QString &path )
385 {
386     Q_D(QTransportAuth);
387     d->m_logFilePath = path;
388 }
389 
logFilePath() const390 QString QTransportAuth::logFilePath() const
391 {
392     Q_D(const QTransportAuth);
393     return d->m_logFilePath;
394 }
395 
setPackageRegistry(QObject * registry)396 void QTransportAuth::setPackageRegistry( QObject *registry )
397 {
398     Q_D(QTransportAuth);
399     d->m_packageRegistry = registry;
400 }
401 
isDiscoveryMode() const402 bool QTransportAuth::isDiscoveryMode() const
403 {
404 #if defined(SXE_DISCOVERY)
405     static bool checked = false;
406     static bool yesItIs = false;
407 
408     if ( checked ) return yesItIs;
409 
410     yesItIs = ( getenv( "SXE_DISCOVERY_MODE" ) != 0 );
411     if ( yesItIs )
412     {
413         qWarning("SXE Discovery mode on, ALLOWING ALL requests and logging to %s",
414                  qPrintable(logFilePath()));
415         QFile::remove( logFilePath() );
416     }
417     checked = true;
418     return yesItIs;
419 #else
420     return false;
421 #endif
422 }
423 
424 /*!
425   \internal
426   Return the authorizer device mapped to this client.  Note that this
427   could probably all be void* instead of QWSClient* for generality.
428   Until the need for that rears its head its QWSClient* to save the casts.
429 
430   #### OK the need has arrived, but the public API is frozen.
431 */
passThroughByClient(QWSClient * client) const432 QIODevice *QTransportAuth::passThroughByClient( QWSClient *client ) const
433 {
434     Q_D(const QTransportAuth);
435 
436     if ( client == 0 ) return 0;
437     if ( d->buffersByClient.contains( client ))
438     {
439         return d->buffersByClient[client];
440     }
441     // qWarning( "buffer not found for client %p", client );
442     return 0;
443 }
444 
445 /*!
446   \internal
447   Return a QIODevice pointer (to an internal QBuffer) which can be used
448   to receive data after authorization on transport \a d.
449 
450   The return QIODevice will act as a pass-through.
451 
452   The data will be consumed from \a iod and forwarded on to the returned
453   QIODevice which can be connected to readyRead() signal handlers in
454   place of the original QIODevice \a iod.
455 
456   This will be called in the server process to handle incoming
457   authenticated requests.
458 
459   The returned QIODevice will take ownership of \a data which will be deleted
460   when the QIODevice is delected.
461 
462   \sa setTargetDevice()
463 */
recvBuf(QTransportAuth::Data * data,QIODevice * iod)464 QAuthDevice *QTransportAuth::recvBuf( QTransportAuth::Data *data, QIODevice *iod )
465 {
466     return new QAuthDevice( iod, data, QAuthDevice::Receive );
467 }
468 
469 /*!
470   Return a QIODevice pointer (to an internal QBuffer) which can be used
471   to write data onto, for authorization on transport \a d.
472 
473   The return QIODevice will act as a pass-through.
474 
475   The data written to the return QIODevice will be forwarded on to the
476   returned QIODevice.  In the case of a QTcpSocket, this will cause it
477   to send out the data with the authentication information on it.
478 
479   This will be called in the client process to generate outgoing
480   authenticated requests.
481 
482   The returned QIODevice will take ownership of \a data which will be deleted
483   when the QIODevice is delected.
484 
485   \sa setTargetDevice()
486 */
authBuf(QTransportAuth::Data * data,QIODevice * iod)487 QAuthDevice *QTransportAuth::authBuf( QTransportAuth::Data *data, QIODevice *iod )
488 {
489     return new QAuthDevice( iod, data, QAuthDevice::Send );
490 }
491 
getClientKey(unsigned char progId)492 const unsigned char *QTransportAuth::getClientKey( unsigned char progId )
493 {
494     Q_D(QTransportAuth);
495     return d->getClientKey( progId );
496 }
497 
invalidateClientKeyCache()498 void QTransportAuth::invalidateClientKeyCache()
499 {
500     Q_D(QTransportAuth);
501     d->invalidateClientKeyCache();
502 }
503 
getKeyFileMutex()504 QMutex *QTransportAuth::getKeyFileMutex()
505 {
506     Q_D(QTransportAuth);
507     return &d->keyfileMutex;
508 }
509 
510 /*
511    \internal
512    Respond to the destroyed(QObject*) signal of the QAuthDevice's
513    client object and remove it from the buffersByClient lookup hash.
514 */
bufferDestroyed(QObject * cli)515 void QTransportAuth::bufferDestroyed( QObject *cli )
516 {
517     Q_D(QTransportAuth);
518     if ( cli == NULL ) return;
519 
520     if ( d->buffersByClient.contains( cli ))
521     {
522         d->buffersByClient.remove( cli );
523         // qDebug( "@@@@@@@ client %p removed @@@@@@@@@", cli );
524     }
525     // qDebug( "           client count %d", d->buffersByClient.count() );
526 }
527 
authorizeRequest(QTransportAuth::Data & d,const QString & request)528 bool QTransportAuth::authorizeRequest( QTransportAuth::Data &d, const QString &request )
529 {
530     bool isAuthorized = true;
531 
532     if ( !request.isEmpty() && request != QLatin1String("Unknown") )
533     {
534         d.status &= QTransportAuth::ErrMask;  // clear the status
535         emit policyCheck( d, request );
536         isAuthorized = (( d.status & QTransportAuth::StatusMask ) == QTransportAuth::Allow );
537     }
538 #if defined(SXE_DISCOVERY)
539     if (isDiscoveryMode()) {
540 #ifndef QT_NO_TEXTSTREAM
541         if (!logFilePath().isEmpty()) {
542             QFile log( logFilePath() );
543             if (!log.open(QIODevice::WriteOnly | QIODevice::Append)) {
544                 qWarning("Could not write to log in discovery mode: %s",
545                          qPrintable(logFilePath()));
546             } else {
547                 QTextStream ts( &log );
548                 ts << d.progId << '\t' << ( isAuthorized ? "Allow" : "Deny" ) << '\t' << request << endl;
549             }
550         }
551 #endif
552         isAuthorized = true;
553     }
554 #endif
555     if ( !isAuthorized )
556     {
557         qWarning( "%s - denied: for Program Id %u [PID %d]"
558                 , qPrintable(request), d.progId, d.processId );
559 
560         char linkTarget[BUF_SIZE]="";
561         char exeLink[BUF_SIZE]="";
562         char cmdlinePath[BUF_SIZE]="";
563         char cmdline[BUF_SIZE]="";
564 
565         //get executable from /proc/pid/exe
566         snprintf( exeLink, BUF_SIZE, "/proc/%d/exe", d.processId );
567         if ( -1 == ::readlink( exeLink, linkTarget, BUF_SIZE - 1 ) )
568         {
569             qWarning( "SXE:- Error encountered in retrieving executable link target from /proc/%u/exe : %s",
570                 d.processId, strerror(errno) );
571             snprintf( linkTarget, BUF_SIZE, "%s", linkTarget );
572         }
573 
574         //get cmdline from proc/pid/cmdline
575         snprintf( cmdlinePath, BUF_SIZE, "/proc/%d/cmdline", d.processId );
576         int  cmdlineFd = QT_OPEN( cmdlinePath, O_RDONLY );
577         if ( cmdlineFd == -1 )
578         {
579             qWarning( "SXE:- Error encountered in opening /proc/%u/cmdline: %s",
580                 d.processId, strerror(errno) );
581             snprintf( cmdline, BUF_SIZE, "%s", "Unknown" );
582         }
583         else
584         {
585             if ( -1 == QT_READ(cmdlineFd, cmdline, BUF_SIZE - 1 ) )
586             {
587                 qWarning( "SXE:- Error encountered in reading /proc/%u/cmdline : %s",
588                     d.processId, strerror(errno) );
589                 snprintf( cmdline, BUF_SIZE, "%s", "Unknown" );
590             }
591             QT_CLOSE( cmdlineFd );
592         }
593 
594         syslog( LOG_ERR | LOG_LOCAL6, "%s // PID:%u // ProgId:%u // Exe:%s // Request:%s // Cmdline:%s",
595                 "<SXE Breach>", d.processId, d.progId, linkTarget, qPrintable(request), cmdline);
596     }
597 
598     return isAuthorized;
599 }
600 
__fileOpen(QFile * f)601 inline bool __fileOpen( QFile *f )
602 {
603 #ifdef QTRANSPORTAUTH_DEBUG
604     if ( f->open( QIODevice::ReadOnly ))
605     {
606         qDebug( "Opened file: %s\n", qPrintable( f->fileName() ));
607         return true;
608     }
609     else
610     {
611         qWarning( "Could not open file: %s\n", qPrintable( f->fileName() ));
612         return false;
613     }
614 #else
615     return ( f->open( QIODevice::ReadOnly ));
616 #endif
617 }
618 
619 /*!
620   \internal
621   Find client keys for the \a progId.  If it is cached should be very
622   fast, otherwise requires a read of the secret key file
623 
624   In the success case a pointer to the keys is returned.  The pointer is
625   to storage allocated for the internal cache and must be used asap.
626 
627   The list returned is a sequence of one or more keys which match the
628   progId.  There is no separator, each 16 byte sequence represents a key.
629   The sequence is followed by two iterations of the SXE magic
630   bytes,eg  0xBA, 0xD4, 0xD4, 0xBA, 0xBA, 0xD4, 0xD4, 0xBA
631 
632   NULL is returned in the following cases:
633   \list
634     \o the keyfiles could not be accessed - error condition
635     \o there was no key for the supplied program id - key auth failed
636   \endlist
637 
638   Note that for the keyfiles, there is multi-thread and multi-process
639   concurrency issues: they can be read by the qpe process when
640   QTransportAuth calls getClientKey to verify a request, and they can be
641   read or written by the packagemanager when updating package data.
642 
643   To protect against this, the keyfileMutex & SxeRegistryLocker is used.
644 
645   The sxe_installer tool can also update inode and device numbers in
646   the manifest file, but this only occurs outside of normal operation,
647   so qpe and packagemanager are never running when this occurs.
648 */
getClientKey(unsigned char progId)649 const unsigned char *QTransportAuthPrivate::getClientKey(unsigned char progId)
650 {
651     int manifestMatchCount = 0;
652     struct IdBlock mr;
653     int total_size = 0;
654     char *result = 0;
655     char *result_ptr;
656     int keysFound = 0;
657     bool foundKey;
658     int keysRead = 0;
659     struct usr_key_entry keys_list[128];
660 
661     if ( keyCache.contains( progId ))
662         return (const unsigned char *)keyCache[progId];
663 
664     SxeRegistryLocker rlock( m_packageRegistry );
665 
666     // ### Qt 4.3: this is hacky - see documentation for setKeyFilePath
667     QString manifestPath = m_keyFilePath + QLatin1String("/manifest");
668     QString actualKeyPath = QLatin1String("/proc/lids/keys");
669     bool noFailOnKeyMissing = true;
670     if ( !QFile::exists( actualKeyPath )) {
671         actualKeyPath = m_keyFilePath + QLatin1String( "/" QSXE_KEYFILE );
672     }
673     QFile kf( actualKeyPath );
674     QFile mn( manifestPath );
675     if ( !__fileOpen( &mn ))
676         goto key_not_found;
677     // first find how much storage is needed
678     while ( mn.read( (char*)&mr, sizeof(struct IdBlock)) > 0 )
679         if ( mr.progId == progId )
680             manifestMatchCount++;
681     if ( manifestMatchCount == 0 )
682         goto key_not_found;
683     if ( !__fileOpen( &kf ))
684     {
685         noFailOnKeyMissing = false;
686         goto key_not_found;
687     }
688     total_size = 2 * QSXE_MAGIC_BYTES + manifestMatchCount * QSXE_KEY_LEN;
689     result = (char*)malloc( total_size );
690     Q_CHECK_PTR( result );
691     mn.seek( 0 );
692     result_ptr = result;
693     /* reading whole key array in is much more efficient, 99% case is this loop only
694        executes once, should not have more than 128 keyed items */
695     while (( keysRead = kf.read( (char*)keys_list, sizeof(struct usr_key_entry)*128 )) > 0 )
696     {
697         /* qDebug("PID %d: getClientKey() - read %d bytes = %d keys from %s", getpid(), keysRead,
698                 keysRead/sizeof(struct usr_key_entry), qPrintable(actualKeyPath)); */
699         keysRead /= sizeof(struct usr_key_entry);
700         while ( mn.read( (char*)&mr, sizeof(struct IdBlock)) > 0 )
701         {
702             if ( mr.progId == progId )
703             {
704                 foundKey = false;
705                 for ( int i = 0; i < keysRead; ++i )
706                 {
707                     /* if ( i == 0 )
708                         qDebug() << "         pid" << getpid() << "looking for device"  << (dev_t)mr.device << "inode" << (ino_t)mr.inode;
709                     qDebug() << "         pid" << getpid() << "trying device" <<  keys_list[i].dev << "inode" <<  keys_list[i].ino; */
710                     if ( keys_list[i].ino == (ino_t)mr.inode && keys_list[i].dev == (dev_t)mr.device )
711                     {
712                         memcpy( result_ptr, keys_list[i].key, QSXE_KEY_LEN );
713                         result_ptr += QSXE_KEY_LEN;
714                         foundKey = true;
715                         break;
716                     }
717                 }
718                 if ( foundKey )
719                 {
720                     keysFound++;
721                     if ( keysFound == manifestMatchCount )
722                         break;
723                 }
724             }
725         }
726     }
727     if ( result_ptr == result ) // nothing found!
728         goto key_not_found;
729     // 2 x magic bytes sentinel at end of sequence
730     for ( int i = 0; i < 2; ++i )
731         for ( int j = 0; j < QSXE_MAGIC_BYTES; ++j )
732             *result_ptr++ = magic[j];
733     keyCache.insert( progId, result, total_size / 10 );
734     /* qDebug( "PID %d : Found %d client keys for prog %u", getpid(), keysFound, progId ); */
735     goto success_out;
736 
737 key_not_found:
738     if ( noFailOnKeyMissing )  // return an "empty" set of keys in this case
739     {
740         if ( result == 0 )
741         {
742             result = (char*)malloc( 2 * QSXE_MAGIC_BYTES );
743             Q_CHECK_PTR( result );
744         }
745         result_ptr = result;
746         for ( int i = 0; i < 2; ++i )
747             for ( int j = 0; j < QSXE_MAGIC_BYTES; ++j )
748                 *result_ptr++ = magic[j];
749         return (unsigned char *)result;
750     }
751     qWarning( "PID %d : Not found client key for prog %u", getpid(), progId );
752     if ( result )
753     {
754         free( result );
755         result = 0;
756     }
757 success_out:
758     if ( mn.isOpen() )
759         mn.close();
760     if ( kf.isOpen() )
761         kf.close();
762     return (unsigned char *)result;
763 }
764 
invalidateClientKeyCache()765 void QTransportAuthPrivate::invalidateClientKeyCache()
766 {
767     keyfileMutex.lock();
768     keyCache.clear();
769     keyfileMutex.unlock();
770 }
771 
772 ////////////////////////////////////////////////////////////////////////
773 ////
774 ////  RequestAnalyzer definition
775 ////
776 
777 
RequestAnalyzer()778 RequestAnalyzer::RequestAnalyzer()
779     : moreData( false )
780     , dataSize( 0 )
781 {
782 }
783 
~RequestAnalyzer()784 RequestAnalyzer::~RequestAnalyzer()
785 {
786 }
787 
788 /*!
789   Analzye the data in the\a msgQueue according to some protocol
790   and produce a request string for policy analysis.
791 
792   If enough data is in the queue for analysis of a complete message,
793   return a non-null string, and set a flag so requireMoreData() will
794   return false; otherwise return a null string and requireMoreData()
795   return true.
796 
797   The amount of bytes analyzed is then available via bytesAnalyzed().
798 
799   A null string is also returned in the case where the message was
800   corrupt and could not be analyzed.  In this case requireMoreData()
801   returns false.
802 
803 Note: this method will modify the msgQueue and pull off the data
804   deemed to be corrupt, in the case of corrupt data.
805 
806   In all other cases the msgQueue is left alone.  The calling code
807   should then pull off the analyzed data.  Use bytesAnalzyed() to
808   find how much data to pull off the queue.
809 */
analyze(QByteArray * msgQueue)810 QString RequestAnalyzer::analyze( QByteArray *msgQueue )
811 {
812 #ifdef Q_WS_QWS
813     dataSize = 0;
814     moreData = false;
815     QBuffer cmdBuf( msgQueue );
816     cmdBuf.open( QIODevice::ReadOnly | QIODevice::Unbuffered );
817     QWSCommand::Type command_type = (QWSCommand::Type)(qws_read_uint( &cmdBuf ));
818     QWSCommand *command = QWSCommand::factory(command_type);
819     // if NULL, factory will have already printed warning for bogus
820     // command_type just purge the bad stuff and attempt to recover
821     if ( command == NULL )
822     {
823         *msgQueue = msgQueue->mid( sizeof(int) );
824         return QString();
825     }
826     QString request = QLatin1String(qws_getCommandTypeString(command_type));
827 #ifndef QT_NO_COP
828     if ( !command->read( &cmdBuf ))
829     {
830         // not all command arrived yet - come back later
831         delete command;
832         moreData = true;
833         return QString();
834     }
835     if ( command_type == QWSCommand::QCopSend )
836     {
837         QWSQCopSendCommand *sendCommand = static_cast<QWSQCopSendCommand*>(command);
838         request += QString::fromLatin1("/QCop/%1/%2").arg( sendCommand->channel ).arg( sendCommand->message );
839     }
840     if ( command_type == QWSCommand::QCopRegisterChannel )
841     {
842         QWSQCopRegisterChannelCommand *registerCommand = static_cast<QWSQCopRegisterChannelCommand*>(command);
843         request += QString::fromLatin1("/QCop/RegisterChannel/%1").arg( registerCommand->channel );
844     }
845 #endif
846     dataSize = QWS_PROTOCOL_ITEM_SIZE( *command );
847     delete command;
848     return request;
849 #else
850     Q_UNUSED(msgQueue);
851     return QString();
852 #endif
853 }
854 
855 ////////////////////////////////////////////////////////////////////////
856 ////
857 ////  AuthDevice definition
858 ////
859 
860 /*!
861   Constructs a new auth device for the transport \a data and I/O device \a parent.
862 
863   Incoming or outgoing data will be authenticated according to the auth direction \a dir.
864 
865   The auth device will take ownership of the transport \a data and delete it when the device
866   is destroyed.
867 */
QAuthDevice(QIODevice * parent,QTransportAuth::Data * data,AuthDirection dir)868 QAuthDevice::QAuthDevice( QIODevice *parent, QTransportAuth::Data *data, AuthDirection dir )
869     : QIODevice( parent )
870     , d( data )
871     , way( dir )
872     , m_target( parent )
873     , m_client( 0 )
874     , m_bytesAvailable( 0 )
875     , m_skipWritten( 0 )
876     , analyzer( 0 )
877 {
878     if ( dir == Receive ) // server side
879     {
880         connect( m_target, SIGNAL(readyRead()),
881                 this, SLOT(recvReadyRead()));
882     } else {
883         connect( m_target, SIGNAL(readyRead()),
884                 this, SIGNAL(readyRead()));
885     }
886     connect( m_target, SIGNAL(bytesWritten(qint64)),
887             this, SLOT(targetBytesWritten(qint64)) );
888     open( QIODevice::ReadWrite | QIODevice::Unbuffered );
889 }
890 
~QAuthDevice()891 QAuthDevice::~QAuthDevice()
892 {
893     if ( analyzer )
894         delete analyzer;
895     delete d;
896 }
897 
898 /*!
899   \internal
900   Store a pointer to the related device or instance which this
901   authorizer is proxying for
902 */
setClient(QObject * cli)903 void QAuthDevice::setClient( QObject *cli )
904 {
905     m_client = cli;
906     QTransportAuth::getInstance()->d_func()->buffersByClient[cli] = this;
907     QObject::connect( cli, SIGNAL(destroyed(QObject*)),
908             QTransportAuth::getInstance(), SLOT(bufferDestroyed(QObject*)) );
909     // qDebug( "@@@@@@@@@@@@ client set %p @@@@@@@@@", cli );
910     // qDebug( "           client count %d", QTransportAuth::getInstance()->d_func()->buffersByClient.count() );
911 }
912 
client() const913 QObject *QAuthDevice::client() const
914 {
915     return m_client;
916 }
917 
918 /*
919   \fn void QAuthDevice::authViolation(QTransportAuth::Data &)
920 
921   This signal is emitted if an authorization failure is generated, as
922   described in checkAuth();
923 
924   \sa checkAuth()
925 */
926 
927 
928 /*
929   \fn void QAuthDevice::policyCheck(QTransportAuth::Data &transport, const QString &request )
930 
931   This signal is emitted when a transport successfully delivers a request
932   and gives the opportunity to either deny or accept the request.
933 
934   This signal must be connected in the same thread, ie it cannot be queued.
935 
936   As soon as all handlers connected to this signal are processed the Allow or
937   Deny state on the \a transport is checked, and the request is allowed or denied
938   accordingly.
939 
940   \sa checkAuth()
941 */
942 
943 /*!
944   \internal
945   Reimplement QIODevice writeData method.
946 
947   For client end, when the device is written to the incoming data is
948   processed and an authentication header calculated.  This is pushed
949   into the target device, followed by the actual incoming data (the
950   payload).
951 
952   For server end, it is a fatal error to write to the device.
953 */
writeData(const char * data,qint64 len)954 qint64 QAuthDevice::writeData(const char *data, qint64 len)
955 {
956     if ( way == Receive )  // server
957         return m_target->write( data, len );
958     // client
959 #ifdef QTRANSPORTAUTH_DEBUG
960     char displaybuf[1024];
961 #endif
962     char header[QSXE_HEADER_LEN];
963     ::memset( header, 0, QSXE_HEADER_LEN );
964     qint64 bytes = 0;
965     if ( QTransportAuth::getInstance()->authToMessage( *d, header, data, len ))
966     {
967         m_target->write( header, QSXE_HEADER_LEN );
968 #ifdef QTRANSPORTAUTH_DEBUG
969         hexstring( displaybuf, (const unsigned char *)header, QSXE_HEADER_LEN );
970         qDebug( "%d QAuthDevice::writeData - CLIENT: Header written: %s", getpid(), displaybuf );
971 #endif
972         m_skipWritten += QSXE_HEADER_LEN;
973     }
974     m_target->write( data, len );
975     bytes += len;
976 #ifdef QTRANSPORTAUTH_DEBUG
977     int bytesToDisplay = bytes;
978     const unsigned char *dataptr = (const unsigned char *)data;
979     while ( bytesToDisplay > 0 )
980     {
981         int amt = bytes < 500 ? bytes : 500;
982         hexstring( displaybuf, dataptr, amt );
983         qDebug( "%d QAuthDevice::writeData - CLIENT: %s", getpid(), bytes > 0 ? displaybuf : "(null)" );
984         dataptr += 500;
985         bytesToDisplay -= 500;
986     }
987 #endif
988     if ( m_target->inherits( "QAbstractSocket" ))
989         static_cast<QAbstractSocket*>(m_target)->flush();
990     return bytes;
991 }
992 
993 /*!
994   Reimplement from QIODevice
995 
996   Read data out of the internal message queue, reduce the queue by the amount
997   read.  Note that the amount available is only ever the size of a command
998   (although a command can be very big) since we need to check at command
999   boundaries for new authentication headers.
1000 */
readData(char * data,qint64 maxSize)1001 qint64 QAuthDevice::readData( char *data, qint64 maxSize )
1002 {
1003     if ( way == Send )  // client
1004         return m_target->read( data, maxSize );
1005     if ( msgQueue.size() == 0 )
1006         return 0;
1007 #ifdef QTRANSPORTAUTH_DEBUG
1008     char displaybuf[1024];
1009     hexstring( displaybuf, reinterpret_cast<const unsigned char *>(msgQueue.constData()),
1010             msgQueue.size() > 500 ? 500 : msgQueue.size() );
1011     qDebug() << getpid() << "QAuthDevice::readData() buffered/requested/avail"
1012             << msgQueue.size() << maxSize << m_bytesAvailable << displaybuf;
1013 #endif
1014     Q_ASSERT( m_bytesAvailable <= msgQueue.size() );
1015     qint64 bytes = ( maxSize > m_bytesAvailable ) ? m_bytesAvailable : maxSize;
1016     ::memcpy( data, msgQueue.constData(), bytes );
1017     msgQueue = msgQueue.mid( bytes );
1018     m_bytesAvailable -= bytes;
1019     return bytes;
1020 }
1021 
1022 /*!
1023   \internal
1024   Receive readyRead signal from the target recv device.  In response
1025   authorize the data, and write results out to the recvBuf() device
1026   for processing by the application.  Trigger the readyRead signal.
1027 
1028   Authorizing involves first checking the transport is valid, ie the
1029   handshake has either already been done and is cached on a trusted
1030   transport, or was valid with this message; then second passing the
1031   string representation of the service request up to any policyReceivers
1032 
1033   If either of these fail, the message is denied.  In discovery mode
1034   denied messages are allowed, but the message is logged.
1035 */
recvReadyRead()1036 void QAuthDevice::recvReadyRead()
1037 {
1038     qint64 bytes = m_target->bytesAvailable();
1039     if ( bytes <= 0 ) return;
1040     open( QIODevice::ReadWrite | QIODevice::Unbuffered );
1041     QUnixSocket *usock = static_cast<QUnixSocket*>(m_target);
1042     QUnixSocketMessage msg = usock->read();
1043     msgQueue.append( msg.bytes() );
1044     d->processId = msg.processId();
1045     // if "fragmented" packet 1/2 way through start of a command, ie
1046     // in the QWS msg type, cant do anything, come back later when
1047     // there's more of the packet
1048     if ( msgQueue.size() < (int)sizeof(int) )
1049     {
1050         // qDebug() << "returning: msg size too small" << msgQueue.size();
1051         return;
1052     }
1053 #ifdef QTRANSPORTAUTH_DEBUG
1054     char displaybuf[1024];
1055     hexstring( displaybuf, reinterpret_cast<const unsigned char *>(msgQueue.constData()),
1056             msgQueue.size() > 500 ? 500 : msgQueue.size() );
1057     qDebug( "%d ***** SERVER read %lli bytes - msg %s", getpid(), bytes, displaybuf );
1058 #endif
1059 
1060     bool bufHasMessages = msgQueue.size() >= (int)sizeof(int);
1061     while ( bufHasMessages )
1062     {
1063         unsigned char saveStatus = d->status;
1064         if (( d->status & QTransportAuth::ErrMask ) == QTransportAuth::NoSuchKey )
1065         {
1066             QTransportAuth::getInstance()->authorizeRequest( *d, QLatin1String("NoSuchKey") );
1067             break;
1068         }
1069         if ( !QTransportAuth::getInstance()->authFromMessage( *d, msgQueue, msgQueue.size() ))
1070         {
1071             // not all arrived yet?  come back later
1072             if (( d->status & QTransportAuth::ErrMask ) == QTransportAuth::TooSmall )
1073             {
1074                 d->status = saveStatus;
1075                 return;
1076             }
1077         }
1078         if (( d->status & QTransportAuth::ErrMask ) == QTransportAuth::NoMagic )
1079         {
1080             // no msg auth header, don't change the success status for connections
1081             if ( d->connection() )
1082                 d->status = saveStatus;
1083         }
1084         else
1085         {
1086             // msg auth header detected and auth determined, remove hdr
1087             msgQueue = msgQueue.mid( QSXE_HEADER_LEN );
1088         }
1089         if ( !authorizeMessage() )
1090             break;
1091         bufHasMessages = msgQueue.size() >= (int)sizeof(int);
1092     }
1093 }
1094 
1095 /**
1096   \internal
1097   Handle bytesWritten signals from the underlying target device.
1098   We adjust the target's value for bytes that are part of auth packets.
1099 */
targetBytesWritten(qint64 bytes)1100 void QAuthDevice::targetBytesWritten( qint64 bytes )
1101 {
1102     if ( m_skipWritten >= bytes ) {
1103         m_skipWritten -= bytes;
1104         bytes = 0;
1105     } else if ( m_skipWritten > 0 ) {
1106         bytes -= m_skipWritten;
1107         m_skipWritten = 0;
1108     }
1109     if ( bytes > 0 ) {
1110         emit bytesWritten( bytes );
1111     }
1112 }
1113 
1114 /**
1115   \internal
1116   Pre-process the message to determine what QWS command it is.  This
1117   information is used as the "request" for the purposes of authorization.
1118 
1119   The request and other data on the connection (id, PID, etc.) are forwarded
1120   to all policy listeners by emitting a signal.
1121 
1122   The signal must be processed synchronously because on return the allow/deny
1123   status is used immediately to either drop or continue processing the message.
1124 */
authorizeMessage()1125 bool QAuthDevice::authorizeMessage()
1126 {
1127     if ( analyzer == NULL )
1128         analyzer = new RequestAnalyzer();
1129     QString request = (*analyzer)( &msgQueue );
1130     if ( analyzer->requireMoreData() )
1131         return false;
1132     bool isAuthorized = true;
1133 
1134     if ( !request.isEmpty() && request != QLatin1String("Unknown") )
1135     {
1136         isAuthorized = QTransportAuth::getInstance()->authorizeRequest( *d, request );
1137     }
1138 
1139     bool moreToProcess = ( msgQueue.size() - analyzer->bytesAnalyzed() ) > (int)sizeof(int);
1140     if ( isAuthorized )
1141     {
1142 #ifdef QTRANSPORTAUTH_DEBUG
1143         qDebug() << getpid() << "SERVER authorized: releasing" << analyzer->bytesAnalyzed() << "byte command" << request;
1144 #endif
1145         m_bytesAvailable = analyzer->bytesAnalyzed();
1146         emit QIODevice::readyRead();
1147         return moreToProcess;
1148     }
1149     else
1150     {
1151         msgQueue = msgQueue.mid( analyzer->bytesAnalyzed() );
1152     }
1153 
1154     return true;
1155 }
1156 
setRequestAnalyzer(RequestAnalyzer * ra)1157 void QAuthDevice::setRequestAnalyzer( RequestAnalyzer *ra )
1158 {
1159     Q_ASSERT( ra );
1160     if ( analyzer )
1161         delete analyzer;
1162     analyzer = ra;
1163 }
1164 
1165 /*!
1166   \internal
1167    Add authentication header to the beginning of a message
1168 
1169    Note that the per-process auth cookie is used.  This key should be rewritten in
1170    the binary image of the executable at install time to make it unique.
1171 
1172    For this to be secure some mechanism (eg MAC kernel or other
1173    permissions) must prevent other processes from reading the key.
1174 
1175    The buffer must have AUTH_SPACE(0) bytes spare at the beginning for the
1176    authentication header to be added.
1177 
1178    Returns true if header successfully added.  Will fail if the
1179    per-process key has not yet been set with setProcessKey()
1180 */
authToMessage(QTransportAuth::Data & d,char * hdr,const char * msg,int msgLen)1181 bool QTransportAuth::authToMessage( QTransportAuth::Data &d, char *hdr, const char *msg, int msgLen )
1182 {
1183     // qDebug( "authToMessage(): prog id %u", d.progId );
1184     // only authorize connection oriented transports once, unless key has changed
1185     if ( d.connection() && ((d.status & QTransportAuth::ErrMask) != QTransportAuth::Pending) &&
1186         d_func()->authKey.progId == d.progId )
1187         return false;
1188     d.progId = d_func()->authKey.progId;
1189     // If Unix socket credentials are being used the key wont be set
1190     if ( !d_func()->keyInitialised )
1191         return false;
1192     unsigned char digest[QSXE_KEY_LEN];
1193     char *msgPtr = hdr;
1194     // magic always goes on the beginning
1195     for ( int m = 0; m < QSXE_MAGIC_BYTES; ++m )
1196         *msgPtr++ = magic[m];
1197     hdr[ QSXE_LEN_IDX ] = (unsigned char)msgLen;
1198     if ( !d.trusted())
1199     {
1200         // Use HMAC
1201         int rc = hmac_md5( (unsigned char *)msg, msgLen, d_func()->authKey.key, QSXE_KEY_LEN, digest );
1202         if ( rc == -1 )
1203             return false;
1204         memcpy( hdr + QSXE_KEY_IDX, digest, QSXE_KEY_LEN );
1205     }
1206     else
1207     {
1208         memcpy( hdr + QSXE_KEY_IDX, d_func()->authKey.key, QSXE_KEY_LEN );
1209     }
1210 
1211     hdr[ QSXE_PROG_IDX ] = d_func()->authKey.progId;
1212 
1213 #ifdef QTRANSPORTAUTH_DEBUG
1214     char keydisplay[QSXE_KEY_LEN*2+1];
1215     hexstring( keydisplay, d_func()->authKey.key, QSXE_KEY_LEN );
1216 
1217     qDebug( "%d CLIENT Auth to message %s against prog id %u and key %s\n",
1218             getpid(), msg, d_func()->authKey.progId, keydisplay );
1219 #endif
1220 
1221     // TODO implement sequence to prevent replay attack, not required
1222     // for trusted transports
1223     hdr[ QSXE_SEQ_IDX ] = 1;  // dummy sequence
1224 
1225     d.status = ( d.status & QTransportAuth::StatusMask ) | QTransportAuth::Success;
1226     return true;
1227 }
1228 
1229 
1230 /*!
1231   Check authorization on the \a msg, which must be of size \a msgLen,
1232   for the transport \a d.
1233 
1234   If able to determine authorization, return the program identity of
1235   the message source in the reference \a progId, and return true.
1236 
1237   Otherwise return false.
1238 
1239   If data is being received on a socket, it may be that more data is yet
1240   needed before authentication can proceed.
1241 
1242   Also the message may not be an authenticated at all.
1243 
1244   In these cases the method returns false to indicate authorization could
1245   not be determined:
1246   \list
1247     \i The message is too small to carry the authentication data
1248        (status TooSmall is set on the \a d transport )
1249     \i The 4 magic bytes are missing from the message start
1250        (status NoMagic is set on the \a d transport )
1251     \i The message is too small to carry the auth + claimed payload
1252        (status TooSmall is set on the \a d transport )
1253   \endlist
1254 
1255   If however the authentication header (preceded by the magic bytes) and
1256   any authenticated payload is received the method will determine the
1257   authentication status, and return true.
1258 
1259   In the following cases as well as returning true it will also emit
1260   an authViolation():
1261   \list
1262     \i If the program id claimed by the message is not found in the key file
1263        (status NoSuchKey is set on the \a d transport )
1264     \i The authentication token failed against the claimed program id:
1265         \list
1266             \i in the case of trusted transports, the secret did not match
1267             \i in the case of untrusted transports the HMAC code did not match
1268         \endlist
1269        (status FailMatch is set on the \a d transport )
1270     \endlist
1271 
1272   In these cases the authViolation( QTransportAuth::Data d ) signal is emitted
1273   and the error string can be obtained from the status like this:
1274   \snippet doc/src/snippets/code/src_gui_embedded_qtransportauth_qws.cpp 4
1275 */
authFromMessage(QTransportAuth::Data & d,const char * msg,int msgLen)1276 bool QTransportAuth::authFromMessage( QTransportAuth::Data &d, const char *msg, int msgLen )
1277 {
1278     if ( msgLen < QSXE_MAGIC_BYTES )
1279     {
1280         d.status = ( d.status & QTransportAuth::StatusMask ) | QTransportAuth::TooSmall;
1281         return false;
1282     }
1283     // if no magic bytes, exit straight away
1284     int m;
1285     const unsigned char *mptr = reinterpret_cast<const unsigned char *>(msg);
1286     for ( m = 0; m < QSXE_MAGIC_BYTES; ++m )
1287     {
1288         if ( *mptr++ != magic[m] )
1289         {
1290             d.status = ( d.status & QTransportAuth::StatusMask ) | QTransportAuth::NoMagic;
1291             return false;
1292         }
1293     }
1294 
1295     if ( msgLen < AUTH_SPACE(1) )
1296     {
1297         d.status = ( d.status & QTransportAuth::StatusMask ) | QTransportAuth::TooSmall;
1298         return false;
1299     }
1300 
1301     // At this point we know the header is at least long enough to contain valid auth
1302     // data, however the data may be spoofed.  If it is not verified then the status will
1303     // be set to uncertified so the spoofed data will not be relied on.  However we want to
1304     // know the program id which is being reported (even if it might be spoofed) for
1305     // policy debugging purposes.  So set it here, rather than after verification.
1306     d.progId = msg[QSXE_PROG_IDX];
1307 
1308 #ifdef QTRANSPORTAUTH_DEBUG
1309     char authhdr[QSXE_HEADER_LEN*2+1];
1310     hexstring( authhdr, reinterpret_cast<const unsigned char *>(msg), QSXE_HEADER_LEN );
1311     qDebug( "%d SERVER authFromMessage(): message header is %s",
1312             getpid(), authhdr );
1313 #endif
1314 
1315     unsigned char authLen = (unsigned char)(msg[ QSXE_LEN_IDX ]);
1316 
1317     if ( msgLen < AUTH_SPACE(authLen) )
1318     {
1319         d.status = ( d.status & QTransportAuth::StatusMask ) | QTransportAuth::TooSmall;
1320         return false;
1321     }
1322 
1323     bool isCached = d_func()->keyCache.contains( d.progId );
1324     const unsigned char *clientKey = d_func()->getClientKey( d.progId );
1325     if ( clientKey == NULL )
1326     {
1327         d.status = ( d.status & QTransportAuth::StatusMask ) | QTransportAuth::NoSuchKey;
1328         return false;
1329     }
1330 
1331 #ifdef QTRANSPORTAUTH_DEBUG
1332     char keydisplay[QSXE_KEY_LEN*2+1];
1333     hexstring( keydisplay, clientKey, QSXE_KEY_LEN );
1334     qDebug( "\t\tauthFromMessage(): message %s against prog id %u and key %s\n",
1335             AUTH_DATA(msg), ((unsigned int)d.progId), keydisplay );
1336 #endif
1337 
1338     const unsigned char *auth_tok;
1339     unsigned char digest[QSXE_KEY_LEN];
1340     bool multi_tok = false;
1341 
1342     bool need_to_recheck=false;
1343     do
1344     {
1345         if ( !d.trusted())
1346         {
1347             hmac_md5( AUTH_DATA(msg), authLen, clientKey, QSXE_KEY_LEN, digest );
1348             auth_tok = digest;
1349         }
1350         else
1351         {
1352             auth_tok = clientKey;
1353             multi_tok = true;  // 1 or more keys are in the clientKey
1354         }
1355         while( true )
1356         {
1357             if ( memcmp( auth_tok, magic, QSXE_MAGIC_BYTES ) == 0
1358                     && memcmp( auth_tok + QSXE_MAGIC_BYTES, magic, QSXE_MAGIC_BYTES ) == 0 )
1359                 break;
1360             if ( memcmp( msg + QSXE_KEY_IDX, auth_tok, QSXE_KEY_LEN ) == 0 )
1361             {
1362                 d.status = ( d.status & QTransportAuth::StatusMask ) | QTransportAuth::Success;
1363                 return true;
1364             }
1365             if ( !multi_tok )
1366                 break;
1367             auth_tok += QSXE_KEY_LEN;
1368         }
1369         //the keys cached on d.progId may not contain the binary key because the cache entry was made
1370         //before the binary had first started, must search for client key again.
1371         if ( isCached )
1372         {
1373             d_func()->keyCache.remove(d.progId);
1374             isCached = false;
1375 
1376 #ifdef QTRANSPORTAUTH_DEBUG
1377             qDebug() << "QTransportAuth::authFromMessage(): key not found in set of keys cached"
1378                      << "against prog Id =" << d.progId << ". Re-obtaining client key. ";
1379 #endif
1380             clientKey = d_func()->getClientKey( d.progId );
1381             if ( clientKey == NULL )
1382             {
1383                 d.status = ( d.status & QTransportAuth::StatusMask ) | QTransportAuth::NoSuchKey;
1384                 return false;
1385             }
1386             need_to_recheck = true;
1387         }
1388         else
1389         {
1390             need_to_recheck = false;
1391         }
1392     } while( need_to_recheck );
1393 
1394     d.status = ( d.status & QTransportAuth::StatusMask ) | QTransportAuth::FailMatch;
1395     qWarning() << "QTransportAuth::authFromMessage():failed authentication";
1396     FAREnforcer::getInstance()->logAuthAttempt( QDateTime::currentDateTime() );
1397     emit authViolation( d );
1398     return false;
1399 }
1400 
1401 
1402 #ifdef QTRANSPORTAUTH_DEBUG
1403 /*!
1404   sprintf into hex - dest \a buf, src \a key, \a key_len is length of key.
1405 
1406   The target buf should be [ key_len * 2 + 1 ] in size
1407 */
hexstring(char * buf,const unsigned char * key,size_t key_len)1408 void hexstring( char *buf, const unsigned char* key, size_t key_len )
1409 {
1410     unsigned int i, p;
1411     for ( i = 0, p = 0; i < key_len; i++, p+=2 )
1412     {
1413         unsigned char lo_nibble = key[i] & 0x0f;
1414         unsigned char hi_nibble = key[i] >> 4;
1415         buf[p] = (int)hi_nibble > 9 ? hi_nibble-10 + 'A' : hi_nibble + '0';
1416         buf[p+1] = (int)lo_nibble > 9 ? lo_nibble-10 + 'A' : lo_nibble + '0';
1417     }
1418     buf[p] = '\0';
1419 }
1420 #endif
1421 
1422 /*
1423   HMAC MD5 as listed in RFC 2104
1424 
1425   This code is taken from:
1426 
1427       http://www.faqs.org/rfcs/rfc2104.html
1428 
1429   with the allowance for keys other than length 16 removed, but otherwise
1430   a straight cut-and-paste.
1431 
1432   The HMAC_MD5 transform looks like:
1433 
1434   \snippet doc/src/snippets/code/src.gui.embedded.qtransportauth_qws.cpp 5
1435 
1436   \list
1437     \i where K is an n byte key
1438     \i ipad is the byte 0x36 repeated 64 times
1439     \i opad is the byte 0x5c repeated 64 times
1440     \i and text is the data being protected
1441   \endlist
1442 
1443   Hardware is available with accelerated implementations of HMAC-MD5 and
1444   HMAC-SHA1.  Where this hardware is available, this routine should be
1445   replaced with a call into the accelerated version.
1446 */
1447 
hmac_md5(unsigned char * text,int text_length,const unsigned char * key,int key_length,unsigned char * digest)1448 static int hmac_md5(
1449         unsigned char*  text,         /* pointer to data stream */
1450         int             text_length,  /* length of data stream */
1451         const unsigned char*  key,    /* pointer to authentication key */
1452         int             key_length,   /* length of authentication key */
1453         unsigned char * digest        /* caller digest to be filled in */
1454         )
1455 {
1456         MD5Context context;
1457         unsigned char k_ipad[65];    /* inner padding - * key XORd with ipad */
1458         unsigned char k_opad[65];    /* outer padding - * key XORd with opad */
1459         int i;
1460 
1461         /* in this implementation key_length == 16 */
1462         if ( key_length != 16 )
1463         {
1464             fprintf( stderr, "Key length was %d - must be 16 bytes", key_length );
1465             return 0;
1466         }
1467 
1468         /* start out by storing key in pads */
1469         memset( k_ipad, 0, sizeof k_ipad );
1470         memset( k_opad, 0, sizeof k_opad );
1471         memcpy( k_ipad, key, key_length );
1472         memcpy( k_opad, key, key_length );
1473 
1474         /* XOR key with ipad and opad values */
1475         for (i=0; i<64; i++) {
1476                 k_ipad[i] ^= 0x36;
1477                 k_opad[i] ^= 0x5c;
1478         }
1479 
1480         /* perform inner MD5 */
1481         MD5Init(&context);                   /* init context for 1st pass */
1482         MD5Update(&context, k_ipad, 64);     /* start with inner pad */
1483         MD5Update(&context, text, text_length); /* then text of datagram */
1484         MD5Final(&context, digest);          /* finish up 1st pass */
1485 
1486         /* perform outer MD5 */
1487         MD5Init(&context);                   /* init context for 2nd pass */
1488         MD5Update(&context, k_opad, 64);     /* start with outer pad */
1489         MD5Update(&context, digest, 16);     /* then results of 1st * hash */
1490         MD5Final(&context, digest);          /* finish up 2nd pass */
1491         return 1;
1492 }
1493 
1494 
1495 const int FAREnforcer::minutelyRate = 4; //allowed number of false authentication attempts per minute
1496 const QString FAREnforcer::FARMessage = QLatin1String("FAR_Exceeded");
1497 const QString FAREnforcer::SxeTag = QLatin1String("<SXE Breach>");
1498 const int FAREnforcer::minute = 60;
1499 
FAREnforcer()1500 FAREnforcer::FAREnforcer():authAttempts()
1501 {
1502     QDateTime nullDateTime = QDateTime();
1503     for (int i = 0; i < minutelyRate; i++ )
1504         authAttempts << nullDateTime;
1505 }
1506 
1507 
getInstance()1508 FAREnforcer *FAREnforcer::getInstance()
1509 {
1510     static FAREnforcer theInstance;
1511     return &theInstance;
1512 }
1513 
logAuthAttempt(QDateTime time)1514 void FAREnforcer::logAuthAttempt( QDateTime time )
1515 {
1516     QDateTime dt =  authAttempts.takeFirst();
1517 
1518     authAttempts.append( time );
1519     if ( dt.secsTo( authAttempts.last() ) <= minute )
1520     {
1521 #if defined(SXE_DISCOVERY)
1522         if ( QTransportAuth::getInstance()->isDiscoveryMode() ) {
1523             static QBasicAtomicInt reported = Q_BASIC_ATOMIC_INITIALIZER(0);
1524             if ( reported.testAndSetRelaxed(0,1) ) {
1525 #ifndef QT_NO_TEXTSTREAM
1526                 QString logFilePath = QTransportAuth::getInstance()->logFilePath();
1527                 if ( !logFilePath.isEmpty() ) {
1528                       QFile log( logFilePath );
1529                     if ( !log.open(QIODevice::WriteOnly | QIODevice::Append) ) {
1530                         qWarning("Could not write to log in discovery mode: %s",
1531                                  qPrintable(logFilePath) );
1532                     } else {
1533                         QTextStream ts( &log );
1534                         ts << "\t\tWarning: False Authentication Rate of " <<  minutelyRate << "\n"
1535                            << "\t\tserver connections/authentications per minute has been exceeded,\n"
1536                            << "\t\tno further warnings will be issued\n";
1537                     }
1538                 }
1539             }
1540 #endif
1541             reset();
1542             return;
1543         }
1544 #endif
1545         syslog( LOG_ERR | LOG_LOCAL6, "%s %s",
1546                 qPrintable( FAREnforcer::SxeTag ),
1547                 qPrintable( FAREnforcer::FARMessage ) );
1548         reset();
1549     }
1550 }
1551 
reset()1552 void FAREnforcer::reset()
1553 {
1554     QDateTime nullDateTime = QDateTime();
1555     for (int i = 0; i < minutelyRate; i++ )
1556         authAttempts[i] = nullDateTime;
1557 }
1558 
1559 QT_END_NAMESPACE
1560 
1561 #include "moc_qtransportauth_qws_p.cpp"
1562 
1563 #endif // QT_NO_SXE
1564