summaryrefslogtreecommitdiffstats
path: root/src/gui/embedded/qtransportauth_qws.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/gui/embedded/qtransportauth_qws.cpp')
-rw-r--r--src/gui/embedded/qtransportauth_qws.cpp1563
1 files changed, 0 insertions, 1563 deletions
diff --git a/src/gui/embedded/qtransportauth_qws.cpp b/src/gui/embedded/qtransportauth_qws.cpp
deleted file mode 100644
index f05699f340..0000000000
--- a/src/gui/embedded/qtransportauth_qws.cpp
+++ /dev/null
@@ -1,1563 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
-** All rights reserved.
-** Contact: Nokia Corporation (qt-info@nokia.com)
-**
-** This file is part of the QtGui module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** GNU Lesser General Public License Usage
-** This file may be used under the terms of the GNU Lesser General Public
-** License version 2.1 as published by the Free Software Foundation and
-** appearing in the file LICENSE.LGPL included in the packaging of this
-** file. Please review the following information to ensure the GNU Lesser
-** General Public License version 2.1 requirements will be met:
-** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
-**
-** In addition, as a special exception, Nokia gives you certain additional
-** rights. These rights are described in the Nokia Qt LGPL Exception
-** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU General
-** Public License version 3.0 as published by the Free Software Foundation
-** and appearing in the file LICENSE.GPL included in the packaging of this
-** file. Please review the following information to ensure the GNU General
-** Public License version 3.0 requirements will be met:
-** http://www.gnu.org/copyleft/gpl.html.
-**
-** Other Usage
-** Alternatively, this file may be used in accordance with the terms and
-** conditions contained in a signed written agreement between you and Nokia.
-**
-**
-**
-**
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "qtransportauth_qws.h"
-#include "qtransportauth_qws_p.h"
-
-#ifndef QT_NO_SXE
-
-#include "../../3rdparty/md5/md5.h"
-#include "../../3rdparty/md5/md5.cpp"
-#include "qwsutils_qws.h"
-#include "qwssocket_qws.h"
-#include "qwscommand_qws_p.h"
-#include "qwindowsystem_qws.h"
-#include "qbuffer.h"
-#include "qthread.h"
-#include "qabstractsocket.h"
-#include "qlibraryinfo.h"
-#include "qfile.h"
-#include "qdebug.h"
-#include <private/qcore_unix_p.h> // overrides QT_OPEN
-
-#include <syslog.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <sys/file.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <errno.h>
-#include <time.h>
-
-#include <QtCore/qcache.h>
-
-#define BUF_SIZE 512
-
-QT_BEGIN_NAMESPACE
-
-/*!
- \internal
- memset for security purposes, guaranteed not to be optimized away
- http://www.faqs.org/docs/Linux-HOWTO/Secure-Programs-HOWTO.html
-*/
-Q_GUI_EXPORT void *guaranteed_memset(void *v,int c,size_t n)
-{
- volatile char *p = (char *)v; while (n--) *p++=c; return v;
-}
-
-/*!
- \class QTransportAuth
- \internal
-
- \brief Authenticate a message transport.
-
- For performance reasons, message authentication is tied to an individual
- message transport instance. For example in connection oriented transports
- the authentication cookie can be cached against the connection avoiding
- the overhead of authentication on every message.
-
- For each process there is one instance of the QTransportAuth object.
- For server processes it can determine the \link secure-exe-environ.html SXE
- Program Identity \endlink and provide access to policy data to determine if
- the message should be forwarded for action. If not actioned, the message
- may be treated as being from a flawed or malicious process.
-
- Retrieve the instance with the getInstance() method. The constructor is
- disabled and instances of QTransportAuth should never be constructed by
- calling classes.
-
- To make the Authentication easier to use a proxied QIODevice is provided
- which uses an internal QBuffer.
-
- In the server code first get a pointer to a QTransportAuth::Data object
- using the connectTransport() method:
-
- \snippet doc/src/snippets/code/src_gui_embedded_qtransportauth_qws.cpp 0
-
- Here it is asserted that the transport is trusted. See the assumptions
- listed in the \link secure-exe-environ.html SXE documentation \endlink
-
- Then proxy in the authentication device:
-
- \snippet doc/src/snippets/code/src_gui_embedded_qtransportauth_qws.cpp 1
-
- In the client code it is similar. Use the connectTransport() method
- just the same then proxy in the authentication device instead of the
- socket in write calls:
-
- \snippet doc/src/snippets/code/src_gui_embedded_qtransportauth_qws.cpp 2
-*/
-
-static int hmac_md5(
- unsigned char* text, /* pointer to data stream */
- int text_length, /* length of data stream */
- const unsigned char* key, /* pointer to authentication key */
- int key_length, /* length of authentication key */
- unsigned char * digest /* caller digest to be filled in */
- );
-
-
-
-#define KEY_CACHE_SIZE 30
-
-const char * const errorStrings[] = {
- "pending identity verification",
- "message too small to carry auth data",
- "cache miss on connection oriented transport",
- "no magic bytes on message",
- "key not found for prog id",
- "authorization key match failed",
- "key out of date"
-};
-
-const char *QTransportAuth::errorString( const Data &d )
-{
- if (( d.status & ErrMask ) == Success )
- return "success";
- int e = d.status & ErrMask;
- if ( e > OutOfDate )
- return "unknown";
- return errorStrings[e];
-}
-
-SxeRegistryLocker::SxeRegistryLocker( QObject *reg )
- : m_success( false )
- , m_reg( 0 )
-{
- if ( reg )
- if ( !QMetaObject::invokeMethod( reg, "lockManifest", Q_RETURN_ARG(bool, m_success) ))
- m_success = false;
- m_reg = reg;
-}
-
-SxeRegistryLocker::~SxeRegistryLocker()
-{
- if ( m_success )
- QMetaObject::invokeMethod( m_reg, "unlockManifest" );
-}
-
-
-QTransportAuthPrivate::QTransportAuthPrivate()
- : keyInitialised(false)
- , m_packageRegistry( 0 )
-{
-}
-
-QTransportAuthPrivate::~QTransportAuthPrivate()
-{
-}
-
-/*!
- \internal
- Construct a new QTransportAuth
-*/
-QTransportAuth::QTransportAuth() : QObject(*new QTransportAuthPrivate)
-{
- // qDebug( "creating transport auth" );
-}
-
-/*!
- \internal
- Destructor
-*/
-QTransportAuth::~QTransportAuth()
-{
- // qDebug( "deleting transport auth" );
-}
-
-/*!
- Set the process key for this currently running Qt Extended process to
- the \a authdata. \a authdata should be sizeof(struct AuthCookie)
- in length and contain the key and program id. Use this method
- when setting or changing the SXE identity of the current program.
-*/
-void QTransportAuth::setProcessKey( const char *authdata )
-{
- Q_D(QTransportAuth);
- ::memcpy(&d->authKey, authdata, sizeof(struct AuthCookie));
- QFile proc_key( QLatin1String("/proc/self/lids_key") );
- // where proc key exists use that instead
- if ( proc_key.open( QIODevice::ReadOnly ))
- {
- qint64 kb = proc_key.read( (char*)&d->authKey.key, QSXE_KEY_LEN );
-#ifdef QTRANSPORTAUTH_DEBUG
- qDebug( "Using %li bytes of /proc/%i/lids_key\n", (long int)kb, getpid() );
-#else
- Q_UNUSED( kb );
-#endif
- }
- d->keyInitialised = true;
-}
-
-
-/*!
- Apply \a key as the process key for the currently running application.
-
- \a prog is current ignored
-
- Deprecated function
-*/
-void QTransportAuth::setProcessKey( const char *key, const char *prog )
-{
- Q_UNUSED(prog);
- setProcessKey( key );
-#ifdef QTRANSPORTAUTH_DEBUG
- char displaybuf[QSXE_KEY_LEN*2+1];
- hexstring( displaybuf, (const unsigned char *)key, QSXE_KEY_LEN );
- qDebug() << "key" << displaybuf << "set";
-#endif
-}
-
-/*!
- Register \a pr as a policy handler object. The object pointed to
- by \a pr should have a slot as follows
- \snippet doc/src/snippets/code/src_gui_embedded_qtransportauth_qws.cpp 3
- All requests received by this server will then generate a call to
- this slot, and may be processed for policy compliance.
-*/
-void QTransportAuth::registerPolicyReceiver( QObject *pr )
-{
- // not every policy receiver needs setup - no error if this fails
- QMetaObject::invokeMethod( pr, "setupPolicyCheck" );
-
- connect( this, SIGNAL(policyCheck(QTransportAuth::Data&,QString)),
- pr, SLOT(policyCheck(QTransportAuth::Data&,QString)), Qt::DirectConnection );
-}
-
-/*!
- Unregister the \a pr from being a policy handler. No more policyCheck signals
- are received by this object.
-*/
-void QTransportAuth::unregisterPolicyReceiver( QObject *pr )
-{
- disconnect( pr );
- // not every policy receiver needs tear down - no error if this fails
- QMetaObject::invokeMethod( pr, "teardownPolicyCheck" );
-}
-
-/*!
- Record a new transport connection with \a properties and \a descriptor.
-
- The calling code is responsible for destroying the returned data when the
- tranport connection is closed.
-*/
-QTransportAuth::Data *QTransportAuth::connectTransport( unsigned char properties, int descriptor )
-{
- Data *data = new Data(properties, descriptor);
- data->status = Pending;
- return data;
-}
-
-/*!
- Is the transport trusted. This is true iff data written into the
- transport medium cannot be intercepted or modified by another process.
- This is for example true for Unix Domain Sockets, but not for shared
- memory or UDP sockets.
-
- There is of course an underlying assumption that the kernel implementing
- the transport is sound, ie it cannot be compromised by writing to
- /dev/kmem or loading untrusted modules
-*/
-inline bool QTransportAuth::Data::trusted() const
-{
- return (bool)(properties & Trusted);
-}
-
-/*!
- Assert that the transport is trusted.
-
- For example with respect to shared memory, if it is ensured that no untrusted
- root processes are running, and that unix permissions have been set such that
- any untrusted non-root processes do not have access rights, then a shared
- memory transport could be asserted to be trusted.
-
- \sa trusted()
-*/
-inline void QTransportAuth::Data::setTrusted( bool t )
-{
- properties = t ? properties | Trusted : properties & ~Trusted;
-}
-
-/*!
- Is the transport connection oriented. This is true iff once a connection
- has been accepted, and state established, then further messages over the
- transport are guaranteed to have come from the original connecting entity.
- This is for example true for Unix Domain Sockets, but not
- for shared memory or UDP sockets.
-
- By extension if the transport is not trusted() then it should not be
- assumed to be connection oriented, since spoofed connection information
- could be created. For example if we assume the TCP/IP transport is
- trusted, it can be treated as connection oriented; but this is only the
- case if intervening routers are trusted.
-
- Connection oriented transports have authorization cached against the
- connection, and thus authorization is only done at connect time.
-*/
-inline bool QTransportAuth::Data::connection() const
-{
- return (bool)(properties & Connection);
-}
-
-/*!
- Assert that the transport is connection oriented.
-
- \sa connection()
-*/
-inline void QTransportAuth::Data::setConnection( bool t )
-{
- properties = t ? properties | Connection : properties & ~Connection;
-}
-
-/*!
- Return a pointer to the instance of this process's QTransportAuth object
-*/
-QTransportAuth *QTransportAuth::getInstance()
-{
- static QTransportAuth theInstance;
-
- return &theInstance;
-}
-
-/*!
- Set the full path to the key file
-
- Since this is normally relative to Qtopia::qpeDir() this needs to be
- set within the Qt Extended framework.
-
- The keyfile should be protected by file permissions or by MAC rules
- such that it can only be read/written by the "qpe" server process
-*/
-void QTransportAuth::setKeyFilePath( const QString &path )
-{
- Q_D(QTransportAuth);
- d->m_keyFilePath = path;
-}
-
-QString QTransportAuth::keyFilePath() const
-{
- Q_D(const QTransportAuth);
- return d->m_keyFilePath;
-}
-
-void QTransportAuth::setLogFilePath( const QString &path )
-{
- Q_D(QTransportAuth);
- d->m_logFilePath = path;
-}
-
-QString QTransportAuth::logFilePath() const
-{
- Q_D(const QTransportAuth);
- return d->m_logFilePath;
-}
-
-void QTransportAuth::setPackageRegistry( QObject *registry )
-{
- Q_D(QTransportAuth);
- d->m_packageRegistry = registry;
-}
-
-bool QTransportAuth::isDiscoveryMode() const
-{
-#if defined(SXE_DISCOVERY)
- static bool checked = false;
- static bool yesItIs = false;
-
- if ( checked ) return yesItIs;
-
- yesItIs = ( getenv( "SXE_DISCOVERY_MODE" ) != 0 );
- if ( yesItIs )
- {
- qWarning("SXE Discovery mode on, ALLOWING ALL requests and logging to %s",
- qPrintable(logFilePath()));
- QFile::remove( logFilePath() );
- }
- checked = true;
- return yesItIs;
-#else
- return false;
-#endif
-}
-
-/*!
- \internal
- Return the authorizer device mapped to this client. Note that this
- could probably all be void* instead of QWSClient* for generality.
- Until the need for that rears its head its QWSClient* to save the casts.
-
- #### OK the need has arrived, but the public API is frozen.
-*/
-QIODevice *QTransportAuth::passThroughByClient( QWSClient *client ) const
-{
- Q_D(const QTransportAuth);
-
- if ( client == 0 ) return 0;
- if ( d->buffersByClient.contains( client ))
- {
- return d->buffersByClient[client];
- }
- // qWarning( "buffer not found for client %p", client );
- return 0;
-}
-
-/*!
- \internal
- Return a QIODevice pointer (to an internal QBuffer) which can be used
- to receive data after authorization on transport \a d.
-
- The return QIODevice will act as a pass-through.
-
- The data will be consumed from \a iod and forwarded on to the returned
- QIODevice which can be connected to readyRead() signal handlers in
- place of the original QIODevice \a iod.
-
- This will be called in the server process to handle incoming
- authenticated requests.
-
- The returned QIODevice will take ownership of \a data which will be deleted
- when the QIODevice is delected.
-
- \sa setTargetDevice()
-*/
-QAuthDevice *QTransportAuth::recvBuf( QTransportAuth::Data *data, QIODevice *iod )
-{
- return new QAuthDevice( iod, data, QAuthDevice::Receive );
-}
-
-/*!
- Return a QIODevice pointer (to an internal QBuffer) which can be used
- to write data onto, for authorization on transport \a d.
-
- The return QIODevice will act as a pass-through.
-
- The data written to the return QIODevice will be forwarded on to the
- returned QIODevice. In the case of a QTcpSocket, this will cause it
- to send out the data with the authentication information on it.
-
- This will be called in the client process to generate outgoing
- authenticated requests.
-
- The returned QIODevice will take ownership of \a data which will be deleted
- when the QIODevice is delected.
-
- \sa setTargetDevice()
-*/
-QAuthDevice *QTransportAuth::authBuf( QTransportAuth::Data *data, QIODevice *iod )
-{
- return new QAuthDevice( iod, data, QAuthDevice::Send );
-}
-
-const unsigned char *QTransportAuth::getClientKey( unsigned char progId )
-{
- Q_D(QTransportAuth);
- return d->getClientKey( progId );
-}
-
-void QTransportAuth::invalidateClientKeyCache()
-{
- Q_D(QTransportAuth);
- d->invalidateClientKeyCache();
-}
-
-QMutex *QTransportAuth::getKeyFileMutex()
-{
- Q_D(QTransportAuth);
- return &d->keyfileMutex;
-}
-
-/*
- \internal
- Respond to the destroyed(QObject*) signal of the QAuthDevice's
- client object and remove it from the buffersByClient lookup hash.
-*/
-void QTransportAuth::bufferDestroyed( QObject *cli )
-{
- Q_D(QTransportAuth);
- if ( cli == NULL ) return;
-
- if ( d->buffersByClient.contains( cli ))
- {
- d->buffersByClient.remove( cli );
- // qDebug( "@@@@@@@ client %p removed @@@@@@@@@", cli );
- }
- // qDebug( " client count %d", d->buffersByClient.count() );
-}
-
-bool QTransportAuth::authorizeRequest( QTransportAuth::Data &d, const QString &request )
-{
- bool isAuthorized = true;
-
- if ( !request.isEmpty() && request != QLatin1String("Unknown") )
- {
- d.status &= QTransportAuth::ErrMask; // clear the status
- emit policyCheck( d, request );
- isAuthorized = (( d.status & QTransportAuth::StatusMask ) == QTransportAuth::Allow );
- }
-#if defined(SXE_DISCOVERY)
- if (isDiscoveryMode()) {
-#ifndef QT_NO_TEXTSTREAM
- if (!logFilePath().isEmpty()) {
- QFile log( logFilePath() );
- if (!log.open(QIODevice::WriteOnly | QIODevice::Append)) {
- qWarning("Could not write to log in discovery mode: %s",
- qPrintable(logFilePath()));
- } else {
- QTextStream ts( &log );
- ts << d.progId << '\t' << ( isAuthorized ? "Allow" : "Deny" ) << '\t' << request << endl;
- }
- }
-#endif
- isAuthorized = true;
- }
-#endif
- if ( !isAuthorized )
- {
- qWarning( "%s - denied: for Program Id %u [PID %d]"
- , qPrintable(request), d.progId, d.processId );
-
- char linkTarget[BUF_SIZE]="";
- char exeLink[BUF_SIZE]="";
- char cmdlinePath[BUF_SIZE]="";
- char cmdline[BUF_SIZE]="";
-
- //get executable from /proc/pid/exe
- snprintf( exeLink, BUF_SIZE, "/proc/%d/exe", d.processId );
- if ( -1 == ::readlink( exeLink, linkTarget, BUF_SIZE - 1 ) )
- {
- qWarning( "SXE:- Error encountered in retrieving executable link target from /proc/%u/exe : %s",
- d.processId, strerror(errno) );
- snprintf( linkTarget, BUF_SIZE, "%s", linkTarget );
- }
-
- //get cmdline from proc/pid/cmdline
- snprintf( cmdlinePath, BUF_SIZE, "/proc/%d/cmdline", d.processId );
- int cmdlineFd = QT_OPEN( cmdlinePath, O_RDONLY );
- if ( cmdlineFd == -1 )
- {
- qWarning( "SXE:- Error encountered in opening /proc/%u/cmdline: %s",
- d.processId, strerror(errno) );
- snprintf( cmdline, BUF_SIZE, "%s", "Unknown" );
- }
- else
- {
- if ( -1 == QT_READ(cmdlineFd, cmdline, BUF_SIZE - 1 ) )
- {
- qWarning( "SXE:- Error encountered in reading /proc/%u/cmdline : %s",
- d.processId, strerror(errno) );
- snprintf( cmdline, BUF_SIZE, "%s", "Unknown" );
- }
- QT_CLOSE( cmdlineFd );
- }
-
- syslog( LOG_ERR | LOG_LOCAL6, "%s // PID:%u // ProgId:%u // Exe:%s // Request:%s // Cmdline:%s",
- "<SXE Breach>", d.processId, d.progId, linkTarget, qPrintable(request), cmdline);
- }
-
- return isAuthorized;
-}
-
-inline bool __fileOpen( QFile *f )
-{
-#ifdef QTRANSPORTAUTH_DEBUG
- if ( f->open( QIODevice::ReadOnly ))
- {
- qDebug( "Opened file: %s\n", qPrintable( f->fileName() ));
- return true;
- }
- else
- {
- qWarning( "Could not open file: %s\n", qPrintable( f->fileName() ));
- return false;
- }
-#else
- return ( f->open( QIODevice::ReadOnly ));
-#endif
-}
-
-/*!
- \internal
- Find client keys for the \a progId. If it is cached should be very
- fast, otherwise requires a read of the secret key file
-
- In the success case a pointer to the keys is returned. The pointer is
- to storage allocated for the internal cache and must be used asap.
-
- The list returned is a sequence of one or more keys which match the
- progId. There is no separator, each 16 byte sequence represents a key.
- The sequence is followed by two iterations of the SXE magic
- bytes,eg 0xBA, 0xD4, 0xD4, 0xBA, 0xBA, 0xD4, 0xD4, 0xBA
-
- NULL is returned in the following cases:
- \list
- \o the keyfiles could not be accessed - error condition
- \o there was no key for the supplied program id - key auth failed
- \endlist
-
- Note that for the keyfiles, there is multi-thread and multi-process
- concurrency issues: they can be read by the qpe process when
- QTransportAuth calls getClientKey to verify a request, and they can be
- read or written by the packagemanager when updating package data.
-
- To protect against this, the keyfileMutex & SxeRegistryLocker is used.
-
- The sxe_installer tool can also update inode and device numbers in
- the manifest file, but this only occurs outside of normal operation,
- so qpe and packagemanager are never running when this occurs.
-*/
-const unsigned char *QTransportAuthPrivate::getClientKey(unsigned char progId)
-{
- int manifestMatchCount = 0;
- struct IdBlock mr;
- int total_size = 0;
- char *result = 0;
- char *result_ptr;
- int keysFound = 0;
- bool foundKey;
- int keysRead = 0;
- struct usr_key_entry keys_list[128];
-
- if ( keyCache.contains( progId ))
- return (const unsigned char *)keyCache[progId];
-
- SxeRegistryLocker rlock( m_packageRegistry );
-
- // ### Qt 4.3: this is hacky - see documentation for setKeyFilePath
- QString manifestPath = m_keyFilePath + QLatin1String("/manifest");
- QString actualKeyPath = QLatin1String("/proc/lids/keys");
- bool noFailOnKeyMissing = true;
- if ( !QFile::exists( actualKeyPath )) {
- actualKeyPath = m_keyFilePath + QLatin1String( "/" QSXE_KEYFILE );
- }
- QFile kf( actualKeyPath );
- QFile mn( manifestPath );
- if ( !__fileOpen( &mn ))
- goto key_not_found;
- // first find how much storage is needed
- while ( mn.read( (char*)&mr, sizeof(struct IdBlock)) > 0 )
- if ( mr.progId == progId )
- manifestMatchCount++;
- if ( manifestMatchCount == 0 )
- goto key_not_found;
- if ( !__fileOpen( &kf ))
- {
- noFailOnKeyMissing = false;
- goto key_not_found;
- }
- total_size = 2 * QSXE_MAGIC_BYTES + manifestMatchCount * QSXE_KEY_LEN;
- result = (char*)malloc( total_size );
- Q_CHECK_PTR( result );
- mn.seek( 0 );
- result_ptr = result;
- /* reading whole key array in is much more efficient, 99% case is this loop only
- executes once, should not have more than 128 keyed items */
- while (( keysRead = kf.read( (char*)keys_list, sizeof(struct usr_key_entry)*128 )) > 0 )
- {
- /* qDebug("PID %d: getClientKey() - read %d bytes = %d keys from %s", getpid(), keysRead,
- keysRead/sizeof(struct usr_key_entry), qPrintable(actualKeyPath)); */
- keysRead /= sizeof(struct usr_key_entry);
- while ( mn.read( (char*)&mr, sizeof(struct IdBlock)) > 0 )
- {
- if ( mr.progId == progId )
- {
- foundKey = false;
- for ( int i = 0; i < keysRead; ++i )
- {
- /* if ( i == 0 )
- qDebug() << " pid" << getpid() << "looking for device" << (dev_t)mr.device << "inode" << (ino_t)mr.inode;
- qDebug() << " pid" << getpid() << "trying device" << keys_list[i].dev << "inode" << keys_list[i].ino; */
- if ( keys_list[i].ino == (ino_t)mr.inode && keys_list[i].dev == (dev_t)mr.device )
- {
- memcpy( result_ptr, keys_list[i].key, QSXE_KEY_LEN );
- result_ptr += QSXE_KEY_LEN;
- foundKey = true;
- break;
- }
- }
- if ( foundKey )
- {
- keysFound++;
- if ( keysFound == manifestMatchCount )
- break;
- }
- }
- }
- }
- if ( result_ptr == result ) // nothing found!
- goto key_not_found;
- // 2 x magic bytes sentinel at end of sequence
- for ( int i = 0; i < 2; ++i )
- for ( int j = 0; j < QSXE_MAGIC_BYTES; ++j )
- *result_ptr++ = magic[j];
- keyCache.insert( progId, result, total_size / 10 );
- /* qDebug( "PID %d : Found %d client keys for prog %u", getpid(), keysFound, progId ); */
- goto success_out;
-
-key_not_found:
- if ( noFailOnKeyMissing ) // return an "empty" set of keys in this case
- {
- if ( result == 0 )
- {
- result = (char*)malloc( 2 * QSXE_MAGIC_BYTES );
- Q_CHECK_PTR( result );
- }
- result_ptr = result;
- for ( int i = 0; i < 2; ++i )
- for ( int j = 0; j < QSXE_MAGIC_BYTES; ++j )
- *result_ptr++ = magic[j];
- return (unsigned char *)result;
- }
- qWarning( "PID %d : Not found client key for prog %u", getpid(), progId );
- if ( result )
- {
- free( result );
- result = 0;
- }
-success_out:
- if ( mn.isOpen() )
- mn.close();
- if ( kf.isOpen() )
- kf.close();
- return (unsigned char *)result;
-}
-
-void QTransportAuthPrivate::invalidateClientKeyCache()
-{
- keyfileMutex.lock();
- keyCache.clear();
- keyfileMutex.unlock();
-}
-
-////////////////////////////////////////////////////////////////////////
-////
-//// RequestAnalyzer definition
-////
-
-
-RequestAnalyzer::RequestAnalyzer()
- : moreData( false )
- , dataSize( 0 )
-{
-}
-
-RequestAnalyzer::~RequestAnalyzer()
-{
-}
-
-/*!
- Analzye the data in the\a msgQueue according to some protocol
- and produce a request string for policy analysis.
-
- If enough data is in the queue for analysis of a complete message,
- return a non-null string, and set a flag so requireMoreData() will
- return false; otherwise return a null string and requireMoreData()
- return true.
-
- The amount of bytes analyzed is then available via bytesAnalyzed().
-
- A null string is also returned in the case where the message was
- corrupt and could not be analyzed. In this case requireMoreData()
- returns false.
-
-Note: this method will modify the msgQueue and pull off the data
- deemed to be corrupt, in the case of corrupt data.
-
- In all other cases the msgQueue is left alone. The calling code
- should then pull off the analyzed data. Use bytesAnalzyed() to
- find how much data to pull off the queue.
-*/
-QString RequestAnalyzer::analyze( QByteArray *msgQueue )
-{
-#ifdef Q_WS_QWS
- dataSize = 0;
- moreData = false;
- QBuffer cmdBuf( msgQueue );
- cmdBuf.open( QIODevice::ReadOnly | QIODevice::Unbuffered );
- QWSCommand::Type command_type = (QWSCommand::Type)(qws_read_uint( &cmdBuf ));
- QWSCommand *command = QWSCommand::factory(command_type);
- // if NULL, factory will have already printed warning for bogus
- // command_type just purge the bad stuff and attempt to recover
- if ( command == NULL )
- {
- *msgQueue = msgQueue->mid( sizeof(int) );
- return QString();
- }
- QString request = QLatin1String(qws_getCommandTypeString(command_type));
-#ifndef QT_NO_COP
- if ( !command->read( &cmdBuf ))
- {
- // not all command arrived yet - come back later
- delete command;
- moreData = true;
- return QString();
- }
- if ( command_type == QWSCommand::QCopSend )
- {
- QWSQCopSendCommand *sendCommand = static_cast<QWSQCopSendCommand*>(command);
- request += QString::fromLatin1("/QCop/%1/%2").arg( sendCommand->channel ).arg( sendCommand->message );
- }
- if ( command_type == QWSCommand::QCopRegisterChannel )
- {
- QWSQCopRegisterChannelCommand *registerCommand = static_cast<QWSQCopRegisterChannelCommand*>(command);
- request += QString::fromLatin1("/QCop/RegisterChannel/%1").arg( registerCommand->channel );
- }
-#endif
- dataSize = QWS_PROTOCOL_ITEM_SIZE( *command );
- delete command;
- return request;
-#else
- Q_UNUSED(msgQueue);
- return QString();
-#endif
-}
-
-////////////////////////////////////////////////////////////////////////
-////
-//// AuthDevice definition
-////
-
-/*!
- Constructs a new auth device for the transport \a data and I/O device \a parent.
-
- Incoming or outgoing data will be authenticated according to the auth direction \a dir.
-
- The auth device will take ownership of the transport \a data and delete it when the device
- is destroyed.
-*/
-QAuthDevice::QAuthDevice( QIODevice *parent, QTransportAuth::Data *data, AuthDirection dir )
- : QIODevice( parent )
- , d( data )
- , way( dir )
- , m_target( parent )
- , m_client( 0 )
- , m_bytesAvailable( 0 )
- , m_skipWritten( 0 )
- , analyzer( 0 )
-{
- if ( dir == Receive ) // server side
- {
- connect( m_target, SIGNAL(readyRead()),
- this, SLOT(recvReadyRead()));
- } else {
- connect( m_target, SIGNAL(readyRead()),
- this, SIGNAL(readyRead()));
- }
- connect( m_target, SIGNAL(bytesWritten(qint64)),
- this, SLOT(targetBytesWritten(qint64)) );
- open( QIODevice::ReadWrite | QIODevice::Unbuffered );
-}
-
-QAuthDevice::~QAuthDevice()
-{
- if ( analyzer )
- delete analyzer;
- delete d;
-}
-
-/*!
- \internal
- Store a pointer to the related device or instance which this
- authorizer is proxying for
-*/
-void QAuthDevice::setClient( QObject *cli )
-{
- m_client = cli;
- QTransportAuth::getInstance()->d_func()->buffersByClient[cli] = this;
- QObject::connect( cli, SIGNAL(destroyed(QObject*)),
- QTransportAuth::getInstance(), SLOT(bufferDestroyed(QObject*)) );
- // qDebug( "@@@@@@@@@@@@ client set %p @@@@@@@@@", cli );
- // qDebug( " client count %d", QTransportAuth::getInstance()->d_func()->buffersByClient.count() );
-}
-
-QObject *QAuthDevice::client() const
-{
- return m_client;
-}
-
-/*
- \fn void QAuthDevice::authViolation(QTransportAuth::Data &)
-
- This signal is emitted if an authorization failure is generated, as
- described in checkAuth();
-
- \sa checkAuth()
-*/
-
-
-/*
- \fn void QAuthDevice::policyCheck(QTransportAuth::Data &transport, const QString &request )
-
- This signal is emitted when a transport successfully delivers a request
- and gives the opportunity to either deny or accept the request.
-
- This signal must be connected in the same thread, ie it cannot be queued.
-
- As soon as all handlers connected to this signal are processed the Allow or
- Deny state on the \a transport is checked, and the request is allowed or denied
- accordingly.
-
- \sa checkAuth()
-*/
-
-/*!
- \internal
- Reimplement QIODevice writeData method.
-
- For client end, when the device is written to the incoming data is
- processed and an authentication header calculated. This is pushed
- into the target device, followed by the actual incoming data (the
- payload).
-
- For server end, it is a fatal error to write to the device.
-*/
-qint64 QAuthDevice::writeData(const char *data, qint64 len)
-{
- if ( way == Receive ) // server
- return m_target->write( data, len );
- // client
-#ifdef QTRANSPORTAUTH_DEBUG
- char displaybuf[1024];
-#endif
- char header[QSXE_HEADER_LEN];
- ::memset( header, 0, QSXE_HEADER_LEN );
- qint64 bytes = 0;
- if ( QTransportAuth::getInstance()->authToMessage( *d, header, data, len ))
- {
- m_target->write( header, QSXE_HEADER_LEN );
-#ifdef QTRANSPORTAUTH_DEBUG
- hexstring( displaybuf, (const unsigned char *)header, QSXE_HEADER_LEN );
- qDebug( "%d QAuthDevice::writeData - CLIENT: Header written: %s", getpid(), displaybuf );
-#endif
- m_skipWritten += QSXE_HEADER_LEN;
- }
- m_target->write( data, len );
- bytes += len;
-#ifdef QTRANSPORTAUTH_DEBUG
- int bytesToDisplay = bytes;
- const unsigned char *dataptr = (const unsigned char *)data;
- while ( bytesToDisplay > 0 )
- {
- int amt = bytes < 500 ? bytes : 500;
- hexstring( displaybuf, dataptr, amt );
- qDebug( "%d QAuthDevice::writeData - CLIENT: %s", getpid(), bytes > 0 ? displaybuf : "(null)" );
- dataptr += 500;
- bytesToDisplay -= 500;
- }
-#endif
- if ( m_target->inherits( "QAbstractSocket" ))
- static_cast<QAbstractSocket*>(m_target)->flush();
- return bytes;
-}
-
-/*!
- Reimplement from QIODevice
-
- Read data out of the internal message queue, reduce the queue by the amount
- read. Note that the amount available is only ever the size of a command
- (although a command can be very big) since we need to check at command
- boundaries for new authentication headers.
-*/
-qint64 QAuthDevice::readData( char *data, qint64 maxSize )
-{
- if ( way == Send ) // client
- return m_target->read( data, maxSize );
- if ( msgQueue.size() == 0 )
- return 0;
-#ifdef QTRANSPORTAUTH_DEBUG
- char displaybuf[1024];
- hexstring( displaybuf, reinterpret_cast<const unsigned char *>(msgQueue.constData()),
- msgQueue.size() > 500 ? 500 : msgQueue.size() );
- qDebug() << getpid() << "QAuthDevice::readData() buffered/requested/avail"
- << msgQueue.size() << maxSize << m_bytesAvailable << displaybuf;
-#endif
- Q_ASSERT( m_bytesAvailable <= msgQueue.size() );
- qint64 bytes = ( maxSize > m_bytesAvailable ) ? m_bytesAvailable : maxSize;
- ::memcpy( data, msgQueue.constData(), bytes );
- msgQueue = msgQueue.mid( bytes );
- m_bytesAvailable -= bytes;
- return bytes;
-}
-
-/*!
- \internal
- Receive readyRead signal from the target recv device. In response
- authorize the data, and write results out to the recvBuf() device
- for processing by the application. Trigger the readyRead signal.
-
- Authorizing involves first checking the transport is valid, ie the
- handshake has either already been done and is cached on a trusted
- transport, or was valid with this message; then second passing the
- string representation of the service request up to any policyReceivers
-
- If either of these fail, the message is denied. In discovery mode
- denied messages are allowed, but the message is logged.
-*/
-void QAuthDevice::recvReadyRead()
-{
- qint64 bytes = m_target->bytesAvailable();
- if ( bytes <= 0 ) return;
- open( QIODevice::ReadWrite | QIODevice::Unbuffered );
- QUnixSocket *usock = static_cast<QUnixSocket*>(m_target);
- QUnixSocketMessage msg = usock->read();
- msgQueue.append( msg.bytes() );
- d->processId = msg.processId();
- // if "fragmented" packet 1/2 way through start of a command, ie
- // in the QWS msg type, cant do anything, come back later when
- // there's more of the packet
- if ( msgQueue.size() < (int)sizeof(int) )
- {
- // qDebug() << "returning: msg size too small" << msgQueue.size();
- return;
- }
-#ifdef QTRANSPORTAUTH_DEBUG
- char displaybuf[1024];
- hexstring( displaybuf, reinterpret_cast<const unsigned char *>(msgQueue.constData()),
- msgQueue.size() > 500 ? 500 : msgQueue.size() );
- qDebug( "%d ***** SERVER read %lli bytes - msg %s", getpid(), bytes, displaybuf );
-#endif
-
- bool bufHasMessages = msgQueue.size() >= (int)sizeof(int);
- while ( bufHasMessages )
- {
- unsigned char saveStatus = d->status;
- if (( d->status & QTransportAuth::ErrMask ) == QTransportAuth::NoSuchKey )
- {
- QTransportAuth::getInstance()->authorizeRequest( *d, QLatin1String("NoSuchKey") );
- break;
- }
- if ( !QTransportAuth::getInstance()->authFromMessage( *d, msgQueue, msgQueue.size() ))
- {
- // not all arrived yet? come back later
- if (( d->status & QTransportAuth::ErrMask ) == QTransportAuth::TooSmall )
- {
- d->status = saveStatus;
- return;
- }
- }
- if (( d->status & QTransportAuth::ErrMask ) == QTransportAuth::NoMagic )
- {
- // no msg auth header, don't change the success status for connections
- if ( d->connection() )
- d->status = saveStatus;
- }
- else
- {
- // msg auth header detected and auth determined, remove hdr
- msgQueue = msgQueue.mid( QSXE_HEADER_LEN );
- }
- if ( !authorizeMessage() )
- break;
- bufHasMessages = msgQueue.size() >= (int)sizeof(int);
- }
-}
-
-/**
- \internal
- Handle bytesWritten signals from the underlying target device.
- We adjust the target's value for bytes that are part of auth packets.
-*/
-void QAuthDevice::targetBytesWritten( qint64 bytes )
-{
- if ( m_skipWritten >= bytes ) {
- m_skipWritten -= bytes;
- bytes = 0;
- } else if ( m_skipWritten > 0 ) {
- bytes -= m_skipWritten;
- m_skipWritten = 0;
- }
- if ( bytes > 0 ) {
- emit bytesWritten( bytes );
- }
-}
-
-/**
- \internal
- Pre-process the message to determine what QWS command it is. This
- information is used as the "request" for the purposes of authorization.
-
- The request and other data on the connection (id, PID, etc.) are forwarded
- to all policy listeners by emitting a signal.
-
- The signal must be processed synchronously because on return the allow/deny
- status is used immediately to either drop or continue processing the message.
-*/
-bool QAuthDevice::authorizeMessage()
-{
- if ( analyzer == NULL )
- analyzer = new RequestAnalyzer();
- QString request = (*analyzer)( &msgQueue );
- if ( analyzer->requireMoreData() )
- return false;
- bool isAuthorized = true;
-
- if ( !request.isEmpty() && request != QLatin1String("Unknown") )
- {
- isAuthorized = QTransportAuth::getInstance()->authorizeRequest( *d, request );
- }
-
- bool moreToProcess = ( msgQueue.size() - analyzer->bytesAnalyzed() ) > (int)sizeof(int);
- if ( isAuthorized )
- {
-#ifdef QTRANSPORTAUTH_DEBUG
- qDebug() << getpid() << "SERVER authorized: releasing" << analyzer->bytesAnalyzed() << "byte command" << request;
-#endif
- m_bytesAvailable = analyzer->bytesAnalyzed();
- emit QIODevice::readyRead();
- return moreToProcess;
- }
- else
- {
- msgQueue = msgQueue.mid( analyzer->bytesAnalyzed() );
- }
-
- return true;
-}
-
-void QAuthDevice::setRequestAnalyzer( RequestAnalyzer *ra )
-{
- Q_ASSERT( ra );
- if ( analyzer )
- delete analyzer;
- analyzer = ra;
-}
-
-/*!
- \internal
- Add authentication header to the beginning of a message
-
- Note that the per-process auth cookie is used. This key should be rewritten in
- the binary image of the executable at install time to make it unique.
-
- For this to be secure some mechanism (eg MAC kernel or other
- permissions) must prevent other processes from reading the key.
-
- The buffer must have AUTH_SPACE(0) bytes spare at the beginning for the
- authentication header to be added.
-
- Returns true if header successfully added. Will fail if the
- per-process key has not yet been set with setProcessKey()
-*/
-bool QTransportAuth::authToMessage( QTransportAuth::Data &d, char *hdr, const char *msg, int msgLen )
-{
- // qDebug( "authToMessage(): prog id %u", d.progId );
- // only authorize connection oriented transports once, unless key has changed
- if ( d.connection() && ((d.status & QTransportAuth::ErrMask) != QTransportAuth::Pending) &&
- d_func()->authKey.progId == d.progId )
- return false;
- d.progId = d_func()->authKey.progId;
- // If Unix socket credentials are being used the key wont be set
- if ( !d_func()->keyInitialised )
- return false;
- unsigned char digest[QSXE_KEY_LEN];
- char *msgPtr = hdr;
- // magic always goes on the beginning
- for ( int m = 0; m < QSXE_MAGIC_BYTES; ++m )
- *msgPtr++ = magic[m];
- hdr[ QSXE_LEN_IDX ] = (unsigned char)msgLen;
- if ( !d.trusted())
- {
- // Use HMAC
- int rc = hmac_md5( (unsigned char *)msg, msgLen, d_func()->authKey.key, QSXE_KEY_LEN, digest );
- if ( rc == -1 )
- return false;
- memcpy( hdr + QSXE_KEY_IDX, digest, QSXE_KEY_LEN );
- }
- else
- {
- memcpy( hdr + QSXE_KEY_IDX, d_func()->authKey.key, QSXE_KEY_LEN );
- }
-
- hdr[ QSXE_PROG_IDX ] = d_func()->authKey.progId;
-
-#ifdef QTRANSPORTAUTH_DEBUG
- char keydisplay[QSXE_KEY_LEN*2+1];
- hexstring( keydisplay, d_func()->authKey.key, QSXE_KEY_LEN );
-
- qDebug( "%d CLIENT Auth to message %s against prog id %u and key %s\n",
- getpid(), msg, d_func()->authKey.progId, keydisplay );
-#endif
-
- // TODO implement sequence to prevent replay attack, not required
- // for trusted transports
- hdr[ QSXE_SEQ_IDX ] = 1; // dummy sequence
-
- d.status = ( d.status & QTransportAuth::StatusMask ) | QTransportAuth::Success;
- return true;
-}
-
-
-/*!
- Check authorization on the \a msg, which must be of size \a msgLen,
- for the transport \a d.
-
- If able to determine authorization, return the program identity of
- the message source in the reference \a progId, and return true.
-
- Otherwise return false.
-
- If data is being received on a socket, it may be that more data is yet
- needed before authentication can proceed.
-
- Also the message may not be an authenticated at all.
-
- In these cases the method returns false to indicate authorization could
- not be determined:
- \list
- \i The message is too small to carry the authentication data
- (status TooSmall is set on the \a d transport )
- \i The 4 magic bytes are missing from the message start
- (status NoMagic is set on the \a d transport )
- \i The message is too small to carry the auth + claimed payload
- (status TooSmall is set on the \a d transport )
- \endlist
-
- If however the authentication header (preceded by the magic bytes) and
- any authenticated payload is received the method will determine the
- authentication status, and return true.
-
- In the following cases as well as returning true it will also emit
- an authViolation():
- \list
- \i If the program id claimed by the message is not found in the key file
- (status NoSuchKey is set on the \a d transport )
- \i The authentication token failed against the claimed program id:
- \list
- \i in the case of trusted transports, the secret did not match
- \i in the case of untrusted transports the HMAC code did not match
- \endlist
- (status FailMatch is set on the \a d transport )
- \endlist
-
- In these cases the authViolation( QTransportAuth::Data d ) signal is emitted
- and the error string can be obtained from the status like this:
- \snippet doc/src/snippets/code/src_gui_embedded_qtransportauth_qws.cpp 4
-*/
-bool QTransportAuth::authFromMessage( QTransportAuth::Data &d, const char *msg, int msgLen )
-{
- if ( msgLen < QSXE_MAGIC_BYTES )
- {
- d.status = ( d.status & QTransportAuth::StatusMask ) | QTransportAuth::TooSmall;
- return false;
- }
- // if no magic bytes, exit straight away
- int m;
- const unsigned char *mptr = reinterpret_cast<const unsigned char *>(msg);
- for ( m = 0; m < QSXE_MAGIC_BYTES; ++m )
- {
- if ( *mptr++ != magic[m] )
- {
- d.status = ( d.status & QTransportAuth::StatusMask ) | QTransportAuth::NoMagic;
- return false;
- }
- }
-
- if ( msgLen < AUTH_SPACE(1) )
- {
- d.status = ( d.status & QTransportAuth::StatusMask ) | QTransportAuth::TooSmall;
- return false;
- }
-
- // At this point we know the header is at least long enough to contain valid auth
- // data, however the data may be spoofed. If it is not verified then the status will
- // be set to uncertified so the spoofed data will not be relied on. However we want to
- // know the program id which is being reported (even if it might be spoofed) for
- // policy debugging purposes. So set it here, rather than after verification.
- d.progId = msg[QSXE_PROG_IDX];
-
-#ifdef QTRANSPORTAUTH_DEBUG
- char authhdr[QSXE_HEADER_LEN*2+1];
- hexstring( authhdr, reinterpret_cast<const unsigned char *>(msg), QSXE_HEADER_LEN );
- qDebug( "%d SERVER authFromMessage(): message header is %s",
- getpid(), authhdr );
-#endif
-
- unsigned char authLen = (unsigned char)(msg[ QSXE_LEN_IDX ]);
-
- if ( msgLen < AUTH_SPACE(authLen) )
- {
- d.status = ( d.status & QTransportAuth::StatusMask ) | QTransportAuth::TooSmall;
- return false;
- }
-
- bool isCached = d_func()->keyCache.contains( d.progId );
- const unsigned char *clientKey = d_func()->getClientKey( d.progId );
- if ( clientKey == NULL )
- {
- d.status = ( d.status & QTransportAuth::StatusMask ) | QTransportAuth::NoSuchKey;
- return false;
- }
-
-#ifdef QTRANSPORTAUTH_DEBUG
- char keydisplay[QSXE_KEY_LEN*2+1];
- hexstring( keydisplay, clientKey, QSXE_KEY_LEN );
- qDebug( "\t\tauthFromMessage(): message %s against prog id %u and key %s\n",
- AUTH_DATA(msg), ((unsigned int)d.progId), keydisplay );
-#endif
-
- const unsigned char *auth_tok;
- unsigned char digest[QSXE_KEY_LEN];
- bool multi_tok = false;
-
- bool need_to_recheck=false;
- do
- {
- if ( !d.trusted())
- {
- hmac_md5( AUTH_DATA(msg), authLen, clientKey, QSXE_KEY_LEN, digest );
- auth_tok = digest;
- }
- else
- {
- auth_tok = clientKey;
- multi_tok = true; // 1 or more keys are in the clientKey
- }
- while( true )
- {
- if ( memcmp( auth_tok, magic, QSXE_MAGIC_BYTES ) == 0
- && memcmp( auth_tok + QSXE_MAGIC_BYTES, magic, QSXE_MAGIC_BYTES ) == 0 )
- break;
- if ( memcmp( msg + QSXE_KEY_IDX, auth_tok, QSXE_KEY_LEN ) == 0 )
- {
- d.status = ( d.status & QTransportAuth::StatusMask ) | QTransportAuth::Success;
- return true;
- }
- if ( !multi_tok )
- break;
- auth_tok += QSXE_KEY_LEN;
- }
- //the keys cached on d.progId may not contain the binary key because the cache entry was made
- //before the binary had first started, must search for client key again.
- if ( isCached )
- {
- d_func()->keyCache.remove(d.progId);
- isCached = false;
-
-#ifdef QTRANSPORTAUTH_DEBUG
- qDebug() << "QTransportAuth::authFromMessage(): key not found in set of keys cached"
- << "against prog Id =" << d.progId << ". Re-obtaining client key. ";
-#endif
- clientKey = d_func()->getClientKey( d.progId );
- if ( clientKey == NULL )
- {
- d.status = ( d.status & QTransportAuth::StatusMask ) | QTransportAuth::NoSuchKey;
- return false;
- }
- need_to_recheck = true;
- }
- else
- {
- need_to_recheck = false;
- }
- } while( need_to_recheck );
-
- d.status = ( d.status & QTransportAuth::StatusMask ) | QTransportAuth::FailMatch;
- qWarning() << "QTransportAuth::authFromMessage():failed authentication";
- FAREnforcer::getInstance()->logAuthAttempt( QDateTime::currentDateTime() );
- emit authViolation( d );
- return false;
-}
-
-
-#ifdef QTRANSPORTAUTH_DEBUG
-/*!
- sprintf into hex - dest \a buf, src \a key, \a key_len is length of key.
-
- The target buf should be [ key_len * 2 + 1 ] in size
-*/
-void hexstring( char *buf, const unsigned char* key, size_t key_len )
-{
- unsigned int i, p;
- for ( i = 0, p = 0; i < key_len; i++, p+=2 )
- {
- unsigned char lo_nibble = key[i] & 0x0f;
- unsigned char hi_nibble = key[i] >> 4;
- buf[p] = (int)hi_nibble > 9 ? hi_nibble-10 + 'A' : hi_nibble + '0';
- buf[p+1] = (int)lo_nibble > 9 ? lo_nibble-10 + 'A' : lo_nibble + '0';
- }
- buf[p] = '\0';
-}
-#endif
-
-/*
- HMAC MD5 as listed in RFC 2104
-
- This code is taken from:
-
- http://www.faqs.org/rfcs/rfc2104.html
-
- with the allowance for keys other than length 16 removed, but otherwise
- a straight cut-and-paste.
-
- The HMAC_MD5 transform looks like:
-
- \snippet doc/src/snippets/code/src.gui.embedded.qtransportauth_qws.cpp 5
-
- \list
- \i where K is an n byte key
- \i ipad is the byte 0x36 repeated 64 times
- \i opad is the byte 0x5c repeated 64 times
- \i and text is the data being protected
- \endlist
-
- Hardware is available with accelerated implementations of HMAC-MD5 and
- HMAC-SHA1. Where this hardware is available, this routine should be
- replaced with a call into the accelerated version.
-*/
-
-static int hmac_md5(
- unsigned char* text, /* pointer to data stream */
- int text_length, /* length of data stream */
- const unsigned char* key, /* pointer to authentication key */
- int key_length, /* length of authentication key */
- unsigned char * digest /* caller digest to be filled in */
- )
-{
- MD5Context context;
- unsigned char k_ipad[65]; /* inner padding - * key XORd with ipad */
- unsigned char k_opad[65]; /* outer padding - * key XORd with opad */
- int i;
-
- /* in this implementation key_length == 16 */
- if ( key_length != 16 )
- {
- fprintf( stderr, "Key length was %d - must be 16 bytes", key_length );
- return 0;
- }
-
- /* start out by storing key in pads */
- memset( k_ipad, 0, sizeof k_ipad );
- memset( k_opad, 0, sizeof k_opad );
- memcpy( k_ipad, key, key_length );
- memcpy( k_opad, key, key_length );
-
- /* XOR key with ipad and opad values */
- for (i=0; i<64; i++) {
- k_ipad[i] ^= 0x36;
- k_opad[i] ^= 0x5c;
- }
-
- /* perform inner MD5 */
- MD5Init(&context); /* init context for 1st pass */
- MD5Update(&context, k_ipad, 64); /* start with inner pad */
- MD5Update(&context, text, text_length); /* then text of datagram */
- MD5Final(&context, digest); /* finish up 1st pass */
-
- /* perform outer MD5 */
- MD5Init(&context); /* init context for 2nd pass */
- MD5Update(&context, k_opad, 64); /* start with outer pad */
- MD5Update(&context, digest, 16); /* then results of 1st * hash */
- MD5Final(&context, digest); /* finish up 2nd pass */
- return 1;
-}
-
-
-const int FAREnforcer::minutelyRate = 4; //allowed number of false authentication attempts per minute
-const QString FAREnforcer::FARMessage = QLatin1String("FAR_Exceeded");
-const QString FAREnforcer::SxeTag = QLatin1String("<SXE Breach>");
-const int FAREnforcer::minute = 60;
-
-FAREnforcer::FAREnforcer():authAttempts()
-{
- QDateTime nullDateTime = QDateTime();
- for (int i = 0; i < minutelyRate; i++ )
- authAttempts << nullDateTime;
-}
-
-
-FAREnforcer *FAREnforcer::getInstance()
-{
- static FAREnforcer theInstance;
- return &theInstance;
-}
-
-void FAREnforcer::logAuthAttempt( QDateTime time )
-{
- QDateTime dt = authAttempts.takeFirst();
-
- authAttempts.append( time );
- if ( dt.secsTo( authAttempts.last() ) <= minute )
- {
-#if defined(SXE_DISCOVERY)
- if ( QTransportAuth::getInstance()->isDiscoveryMode() ) {
- static QBasicAtomicInt reported = Q_BASIC_ATOMIC_INITIALIZER(0);
- if ( reported.testAndSetRelaxed(0,1) ) {
-#ifndef QT_NO_TEXTSTREAM
- QString logFilePath = QTransportAuth::getInstance()->logFilePath();
- if ( !logFilePath.isEmpty() ) {
- QFile log( logFilePath );
- if ( !log.open(QIODevice::WriteOnly | QIODevice::Append) ) {
- qWarning("Could not write to log in discovery mode: %s",
- qPrintable(logFilePath) );
- } else {
- QTextStream ts( &log );
- ts << "\t\tWarning: False Authentication Rate of " << minutelyRate << "\n"
- << "\t\tserver connections/authentications per minute has been exceeded,\n"
- << "\t\tno further warnings will be issued\n";
- }
- }
- }
-#endif
- reset();
- return;
- }
-#endif
- syslog( LOG_ERR | LOG_LOCAL6, "%s %s",
- qPrintable( FAREnforcer::SxeTag ),
- qPrintable( FAREnforcer::FARMessage ) );
- reset();
- }
-}
-
-void FAREnforcer::reset()
-{
- QDateTime nullDateTime = QDateTime();
- for (int i = 0; i < minutelyRate; i++ )
- authAttempts[i] = nullDateTime;
-}
-
-QT_END_NAMESPACE
-
-#include "moc_qtransportauth_qws_p.cpp"
-
-#endif // QT_NO_SXE