qtransportauth_qws.cpp

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

CPP
1,368
字号
    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( "%li ***** 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 ( !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;    QTransportAuth *auth = QTransportAuth::getInstance();    if ( !request.isEmpty() && request != "Unknown" )    {        d->status &= QTransportAuth::ErrMask;  // clear the status        emit policyCheck( *d, request );        isAuthorized = (( d->status & QTransportAuth::StatusMask ) == QTransportAuth::Allow );    }#if defined(SXE_DISCOVERY)    if (auth->isDiscoveryMode()) {#ifndef QT_NO_TEXTSTREAM        if (!auth->logFilePath().isEmpty()) {            QFile log( auth->logFilePath() );            if (!log.open(QIODevice::WriteOnly | QIODevice::Append)) {                qWarning("Could not write to log in discovery mode: %s",                         qPrintable(auth->logFilePath()));            } else {                QTextStream ts( &log );                ts << d->progId << '\t' << ( isAuthorized ? "Allow" : "Deny" ) << '\t' << request << endl;            }        }#endif        isAuthorized = true;    }#endif    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    {        qWarning( "%s - denied: for Program Id %u [PID %lu]"#if defined(SXE_DISCOVERY)                "(to turn on discovery mode, export SXE_DISCOVERY_MODE=1)"#endif                , qPrintable(request), d->progId, d->processId );        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 QAuthDevice::authToMessage( QTransportAuth::Data &d, char *hdr, const char *msg, int msgLen ){    // qDebug( "authToMessage(): prog id %u", d.progId );    QTransportAuth *a = QTransportAuth::getInstance();    // only authorize connection oriented transports once, unless key has changed    if ( !a->d_func()->keyChanged && d.connection() &&            (( d.status & QTransportAuth::ErrMask ) != QTransportAuth::Pending ))        return false;    a->d_func()->keyChanged = false;    // If Unix socket credentials are being used the key wont be set    if ( ! a->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, a->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, a->d_func()->authKey.key, QSXE_KEY_LEN );    }    hdr[ QSXE_PROG_IDX ] = a->d_func()->authKey.progId;#ifdef QTRANSPORTAUTH_DEBUG    char keydisplay[QSXE_KEY_LEN*2+1];    hexstring( keydisplay, a->d_func()->authKey.key, QSXE_KEY_LEN );    qDebug( "%li CLIENT Auth to message %s against prog id %u and key %s\n",            getpid(), msg, a->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:  \code      QTransportAuth::Result r = d.status & QTransportAuth::ErrMask;      qWarning( "error: %s", QTransportAuth::errorStrings[r] );  \endcode*/bool QAuthDevice::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;        }    }    QTransportAuth *a = QTransportAuth::getInstance();    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( "%li 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;    }    unsigned char progbuf = (unsigned char)(msg[ QSXE_PROG_IDX ]);    const unsigned char *clientKey = a->d_func()->getClientKey( progbuf );    if ( clientKey == NULL )    {        d.status = ( d.status & QTransportAuth::StatusMask ) | QTransportAuth::NoSuchKey;        return false;    }    AuthRecord *ar = (AuthRecord *)clientKey;#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)(msg[ QSXE_PROG_IDX ])), keydisplay );#endif    const unsigned char *auth_tok;    unsigned char digest[QSXE_KEY_LEN];    if ( !d.trusted())    {        hmac_md5( AUTH_DATA(msg), authLen, clientKey, QSXE_KEY_LEN, digest );        auth_tok = digest;    }    else    {        auth_tok = clientKey;    }    mptr = reinterpret_cast<const unsigned char *>( msg + QSXE_KEY_IDX );    for ( m = 0; m < QSXE_KEY_LEN; ++m )    {        if ( *mptr++ != *auth_tok++ )        {            d.status = ( d.status & QTransportAuth::StatusMask ) | QTransportAuth::FailMatch;            emit authViolation( d );            return false;        }    }    time_t now = time(0);    if ( ar->change_time + QSXE_KEY_PERIOD < now )    {        d.status = ( d.status & QTransportAuth::StatusMask ) | QTransportAuth::OutOfDate;#ifdef QTRANSPORTAUTH_DEBUG        qDebug( "authFromMessage() - key out of date" );#endif        return false;    }    // TODO - provide sequence number check against replay attack    // Note that this is only reqd for promiscuous transports (not UDS)    d.status = ( d.status & QTransportAuth::StatusMask ) | QTransportAuth::Success;    return true;}#ifdef QTRANSPORTAUTH_DEBUG/*!  In order to printf in hex, need to break the key up into unsigned ints  so the %x format can be used.  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:  \code      MD5(K XOR opad, MD5(K XOR ipad, text))  \endcode  \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(digest, &context);          /* 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(digest, &context);          /* finish up 2nd pass */        return 1;}#include "moc_qtransportauth_qws_p.cpp"#endif // QT_NO_SXE

⌨️ 快捷键说明

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