src/gui/embedded/qtransportauth_qws.cpp
changeset 0 1918ee327afb
child 4 3b1da2848fc7
equal deleted inserted replaced
-1:000000000000 0:1918ee327afb
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
       
     4 ** All rights reserved.
       
     5 ** Contact: Nokia Corporation (qt-info@nokia.com)
       
     6 **
       
     7 ** This file is part of the QtGui module of the Qt Toolkit.
       
     8 **
       
     9 ** $QT_BEGIN_LICENSE:LGPL$
       
    10 ** No Commercial Usage
       
    11 ** This file contains pre-release code and may not be distributed.
       
    12 ** You may use this file in accordance with the terms and conditions
       
    13 ** contained in the Technology Preview License Agreement accompanying
       
    14 ** this package.
       
    15 **
       
    16 ** GNU Lesser General Public License Usage
       
    17 ** Alternatively, this file may be used under the terms of the GNU Lesser
       
    18 ** General Public License version 2.1 as published by the Free Software
       
    19 ** Foundation and appearing in the file LICENSE.LGPL included in the
       
    20 ** packaging of this file.  Please review the following information to
       
    21 ** ensure the GNU Lesser General Public License version 2.1 requirements
       
    22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
       
    23 **
       
    24 ** In addition, as a special exception, Nokia gives you certain additional
       
    25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
       
    26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
       
    27 **
       
    28 ** If you have questions regarding the use of this file, please contact
       
    29 ** Nokia at qt-info@nokia.com.
       
    30 **
       
    31 **
       
    32 **
       
    33 **
       
    34 **
       
    35 **
       
    36 **
       
    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 */
       
    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 
       
   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 
       
   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 
       
   174 SxeRegistryLocker::~SxeRegistryLocker()
       
   175 {
       
   176     if ( m_success )
       
   177         QMetaObject::invokeMethod( m_reg, "unlockManifest" );
       
   178 }
       
   179 
       
   180 
       
   181 QTransportAuthPrivate::QTransportAuthPrivate()
       
   182     : keyInitialised(false)
       
   183     , m_packageRegistry( 0 )
       
   184 {
       
   185 }
       
   186 
       
   187 QTransportAuthPrivate::~QTransportAuthPrivate()
       
   188 {
       
   189 }
       
   190 
       
   191 /*!
       
   192   \internal
       
   193   Construct a new QTransportAuth
       
   194 */
       
   195 QTransportAuth::QTransportAuth() : QObject(*new QTransportAuthPrivate)
       
   196 {
       
   197     // qDebug( "creating transport auth" );
       
   198 }
       
   199 
       
   200 /*!
       
   201   \internal
       
   202   Destructor
       
   203 */
       
   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 */
       
   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 */
       
   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 */
       
   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 */
       
   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 */
       
   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 */
       
   302 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 */
       
   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 */
       
   338 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 */
       
   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 */
       
   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 */
       
   372 void QTransportAuth::setKeyFilePath( const QString &path )
       
   373 {
       
   374     Q_D(QTransportAuth);
       
   375     d->m_keyFilePath = path;
       
   376 }
       
   377 
       
   378 QString QTransportAuth::keyFilePath() const
       
   379 {
       
   380     Q_D(const QTransportAuth);
       
   381     return d->m_keyFilePath;
       
   382 }
       
   383 
       
   384 void QTransportAuth::setLogFilePath( const QString &path )
       
   385 {
       
   386     Q_D(QTransportAuth);
       
   387     d->m_logFilePath = path;
       
   388 }
       
   389 
       
   390 QString QTransportAuth::logFilePath() const
       
   391 {
       
   392     Q_D(const QTransportAuth);
       
   393     return d->m_logFilePath;
       
   394 }
       
   395 
       
   396 void QTransportAuth::setPackageRegistry( QObject *registry )
       
   397 {
       
   398     Q_D(QTransportAuth);
       
   399     d->m_packageRegistry = registry;
       
   400 }
       
   401 
       
   402 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 */
       
   432 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 authorisation 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 */
       
   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 authorisation 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 */
       
   487 QAuthDevice *QTransportAuth::authBuf( QTransportAuth::Data *data, QIODevice *iod )
       
   488 {
       
   489     return new QAuthDevice( iod, data, QAuthDevice::Send );
       
   490 }
       
   491 
       
   492 const unsigned char *QTransportAuth::getClientKey( unsigned char progId )
       
   493 {
       
   494     Q_D(QTransportAuth);
       
   495     return d->getClientKey( progId );
       
   496 }
       
   497 
       
   498 void QTransportAuth::invalidateClientKeyCache()
       
   499 {
       
   500     Q_D(QTransportAuth);
       
   501     d->invalidateClientKeyCache();
       
   502 }
       
   503 
       
   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 */
       
   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 
       
   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 
       
   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 */
       
   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 
       
   765 void QTransportAuthPrivate::invalidateClientKeyCache()
       
   766 {
       
   767     keyfileMutex.lock();
       
   768     keyCache.clear();
       
   769     keyfileMutex.unlock();
       
   770 }
       
   771 
       
   772 ////////////////////////////////////////////////////////////////////////
       
   773 ////
       
   774 ////  RequestAnalyzer definition
       
   775 ////
       
   776 
       
   777 
       
   778 RequestAnalyzer::RequestAnalyzer()
       
   779     : moreData( false )
       
   780     , dataSize( 0 )
       
   781 {
       
   782 }
       
   783 
       
   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 */
       
   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 */
       
   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 
       
   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 */
       
   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 
       
   913 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 */
       
   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 */
       
  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 */
       
  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 */
       
  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 */
       
  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 
       
  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 */
       
  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 */
       
  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 */
       
  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 
       
  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 
       
  1500 FAREnforcer::FAREnforcer():authAttempts()
       
  1501 {
       
  1502     QDateTime nullDateTime = QDateTime();
       
  1503     for (int i = 0; i < minutelyRate; i++ )
       
  1504         authAttempts << nullDateTime;
       
  1505 }
       
  1506 
       
  1507 
       
  1508 FAREnforcer *FAREnforcer::getInstance()
       
  1509 {
       
  1510     static FAREnforcer theInstance;
       
  1511     return &theInstance;
       
  1512 }
       
  1513 
       
  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 
       
  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