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