qtransportauth_qws.cpp

来自「QT 开发环境里面一个很重要的文件」· C++ 代码 · 共 1,368 行 · 第 1/3 页

CPP
1,368
字号
/******************************************************************************** Copyright (C) 1992-2006 Trolltech ASA. All rights reserved.**** This file is part of the QtGui module of the Qt Toolkit.**** This file may be used under the terms of the GNU General Public** License version 2.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 GNU** General Public Licensing requirements will be met:** http://www.trolltech.com/products/qt/opensource.html**** If you are unsure which license is appropriate for your use, please** review the following information:** http://www.trolltech.com/products/qt/licensing.html or contact the** sales department at sales@trolltech.com.**** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.******************************************************************************/#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 "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 <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>/*!  \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:  \code  QTransportAuth::Data *conData;  QTransportAuth *a = QTransportAuth::getInstance();  conData = a->QTransportconnectTransport(        QTransportAuth::Trusted | QTransportAuth::UnixStreamSock,        socketDescriptor );  \endcode  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:  \code  // mySocket can be any QIODevice subclass  AuthDevice *ad = a->recvBuf( d, mySocket );  // proxy in the auth device where the socket would have gone  connect( ad, SIGNAL(readyRead()), this, SLOT(mySocketReadyRead()));  \endcode  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:  \code  AuthDevice *ad = a->authBuf( d, mySocket );  ad->write( someData );  \endcode*/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 30const 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];}QTransportAuthPrivate::QTransportAuthPrivate()    : keyInitialised(false)    , keyChanged(false){}QTransportAuthPrivate::~QTransportAuthPrivate(){    freeCache();    while ( data.count() )        delete data.takeLast();}/*!  \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 Qtopia 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));    d->keyInitialised = true;    d->keyChanged = true;}/*!  Apply \a key as the process key for the currently running application.  First checks if a file exists in $QPEDIR/etc/rekey/ with the same name as  this app, and if it does, the key from that file is used instead.  Will try to use qApp->applicationName() to get the name of this app, if that  won't work (since the QApplication hasn't been set up yet) pass the name in  \a prog instead.*/void QTransportAuth::setProcessKey( const char *key, const char *prog ){    static QString qtPrefix;    if ( qtPrefix.isEmpty() )        qtPrefix = QLibraryInfo::location(QLibraryInfo::PrefixPath);    QString appName;    if ( prog )    {        appName = prog;    }    else    {        if ( qApp )            appName = qApp->applicationName();    }    if ( appName.isEmpty() )    {        qWarning( "QTransportAuth::setProcessKey() - No application name!" );        QTransportAuth::getInstance()->setProcessKey( key );        return;    }    QString rekey = qtPrefix + "/etc/rekey/" + appName;    QFile rekeyFile( rekey );    if ( rekeyFile.open( QIODevice::ReadOnly ))    {        int rekeyFd = rekeyFile.handle();        if ( flock( rekeyFd, LOCK_EX ) == -1 )            qWarning( "Couldn't lock %s : %s", qPrintable( rekey ), strerror( errno ));        unsigned int keyOffset = 0;        AuthCookie ac;        ::memcpy( &ac, key, QSXE_KEY_LEN+2 ); // keep progId        int rc = ::read( rekeyFd, &keyOffset, sizeof( keyOffset ));        if ( rc != sizeof( keyOffset ))            qWarning( "Error reading key offset: result %d ( expecting %d bytes )",                    rc, sizeof( keyOffset ));        rc = ::read( rekeyFd, &ac, QSXE_KEY_LEN );        if ( rc != QSXE_KEY_LEN )            qWarning( "Error reading key: result %d ( expecting %d bytes )",                rc, QSXE_KEY_LEN );        setProcessKey( (const char *)&ac );#ifdef QTRANSPORTAUTH_DEBUG        char displaybuf[1024];        hexstring( displaybuf, (const unsigned char *)&ac, QSXE_HEADER_LEN );        qDebug() << "new key" << displaybuf << "set from" << rekey << "for prog id" << ac.progId;#endif    }    else    {        setProcessKey( key );#ifdef QTRANSPORTAUTH_DEBUG        char displaybuf2[1024];        hexstring( displaybuf2, (const unsigned char *)key, QSXE_HEADER_LEN );        qDebug() << "existing key" << displaybuf2 << "set" << appName;#endif    }}/*!  Register \a pr as a policy handler object.  The object pointed to  by \a pr should have a slot as follows  \code      policyCheck( QTransportAuth::Data &, const QString & )  \endcode  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 ){    Q_D(QTransportAuth);    QPointer<QObject> guard = pr;    // relies on QPointer::operator ==(const T *o, const QPointer<T> &p)    // and QList::contains() which calls it    if ( d->policyReceivers.contains( guard ))        return;    d->policyReceivers.append(guard);    // not every policy receiver needs setup - no error if this fails    QMetaObject::invokeMethod( pr, "setupPolicyCheck" );    if ( d->buffers.count() > 0 )    {        QHash<QTransportAuth::Data*,QAuthDevice*>::iterator it = d->buffers.begin();        while ( it != d->buffers.end() )        {            connect( it.value(), SIGNAL(policyCheck(QTransportAuth::Data&,QString)),                pr, SLOT(policyCheck(QTransportAuth::Data&,QString)));            ++it;        }    }}/*!  Unregister the \a pr from being a policy handler.  No more policyCheck signals  are received by this object.*/void QTransportAuth::unregisterPolicyReceiver( QObject *pr ){    Q_D(QTransportAuth);    QPointer<QObject> guard = pr;    if ( !d->policyReceivers.contains( guard ))        return;    d->policyReceivers.removeAll(guard);    if ( d->buffers.count() > 0 )    {        QHash<QTransportAuth::Data*,QAuthDevice*>::iterator it = d->buffers.begin();        while ( it != d->buffers.end() )        {            it.value()->disconnect( pr );            ++it;        }    }    // 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.*/QTransportAuth::Data *QTransportAuth::connectTransport( unsigned char properties, int descriptor ){    Q_D(QTransportAuth);    Data *data = new Data(properties, descriptor);    data->status = Pending;    d->data.append(data);    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 qtopia 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;}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;

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?