Example #1
0
std::string CalculateShaPassHash(std::string& name, std::string& password)
{
    SHA1Hash sha;
    sha.Initialize();
    sha.UpdateData(name);
    sha.UpdateData(":");
    sha.UpdateData(password);
    sha.Finalize();

    std::string encoded;
    hexEncodeByteArray(sha.GetDigest(), sha.GetLength(), encoded);

    return encoded;
}
Example #2
0
std::string AccountMgr::CalculateShaPassHash(std::string& name, std::string& password) const
{
    Sha1Hash sha;
    sha.Initialize();
    sha.UpdateData(name);
    sha.UpdateData(":");
    sha.UpdateData(password);
    sha.Finalize();

    std::string encoded;
    hexEncodeByteArray(sha.GetDigest(), Sha1Hash::GetLength(), encoded);

    return encoded;
}
Example #3
0
/// Logon Challenge command handler
bool AuthSocket::_HandleLogonChallenge()
{
    DEBUG_LOG("Entering _HandleLogonChallenge");
    if (recv_len() < sizeof(sAuthLogonChallenge_C))
        return false;

    ///- Read the first 4 bytes (header) to get the length of the remaining of the packet
    std::vector<uint8> buf;
    buf.resize(4);

    recv((char *)&buf[0], 4);

    EndianConvert(*((uint16*)(buf[0])));
    uint16 remaining = ((sAuthLogonChallenge_C *)&buf[0])->size;
    DEBUG_LOG("[AuthChallenge] got header, body is %#04x bytes", remaining);

    if ((remaining < sizeof(sAuthLogonChallenge_C) - buf.size()) || (recv_len() < remaining))
        return false;

    //No big fear of memory outage (size is int16, i.e. < 65536)
    buf.resize(remaining + buf.size() + 1);
    buf[buf.size() - 1] = 0;
    sAuthLogonChallenge_C *ch = (sAuthLogonChallenge_C*)&buf[0];

    ///- Read the remaining of the packet
    recv((char *)&buf[4], remaining);
    DEBUG_LOG("[AuthChallenge] got full packet, %#04x bytes", ch->size);
    DEBUG_LOG("[AuthChallenge] name(%d): '%s'", ch->I_len, ch->I);

    // BigEndian code, nop in little endian case
    // size already converted
    EndianConvert(*((uint32*)(&ch->gamename[0])));
    EndianConvert(ch->build);
    EndianConvert(*((uint32*)(&ch->platform[0])));
    EndianConvert(*((uint32*)(&ch->os[0])));
    EndianConvert(*((uint32*)(&ch->country[0])));
    EndianConvert(ch->timezone_bias);
    EndianConvert(ch->ip);

    ByteBuffer pkt;

    _login = (const char*)ch->I;
    _build = ch->build;
    _os = (const char*)ch->os;

    if(_os.size() > 4)
        return false;

    ///- Normalize account name
    //utf8ToUpperOnlyLatin(_login); -- client already send account in expected form

    //Escape the user login to avoid further SQL injection
    //Memory will be freed on AuthSocket object destruction
    _safelogin = _login;
    LoginDatabase.escape_string(_safelogin);

    // Starting CMD_AUTH_LOGON_CHALLENGE
    AuthResult result = WOW_FAIL_UNKNOWN0;

    ///- Verify that this IP is not in the ip_banned table
    // No SQL injection possible (paste the IP address as passed by the socket)
    std::string address = get_remote_address();
    LoginDatabase.escape_string(address);
    QueryResult* qresult = LoginDatabase.PQuery("SELECT unbandate FROM ip_banned WHERE "
    //    permanent                    still banned
        "(unbandate = bandate OR unbandate > UNIX_TIMESTAMP()) AND ip = '%s'", address.c_str());

    if (qresult)
    {
        result = WOW_FAIL_BANNED;
        BASIC_LOG("[AuthChallenge] Banned ip %s tries to login!", get_remote_address().c_str());
        delete qresult;
    }
    else
    {
        ///- Get the account details from the account table
        // No SQL injection (escaped user name)

        //qresult = LoginDatabase.PQuery("SELECT sha_pass_hash,id,locked,last_ip,gmlevel,v,s FROM account WHERE username = '******'",_safelogin.c_str());
        qresult = LoginDatabase.PQuery("SELECT a.sha_pass_hash,a.id,a.locked,a.last_ip,aa.gmlevel,a.v,a.s FROM account a LEFT JOIN account_access aa ON (a.id = aa.id) WHERE username = '******'", _safelogin.c_str());

        if (qresult)
        {
            std::string rI = (*qresult)[0].GetCppString();
            uint32 accountId = (*qresult)[1].GetUInt32();
            uint8 locked = (*qresult)[2].GetUInt8();
            std::string lastIP = (*qresult)[3].GetString();
            uint8 secLevel = (*qresult)[4].GetUInt8();
            std::string databaseV = (*qresult)[5].GetCppString();
            std::string databaseS = (*qresult)[6].GetCppString();

            bool blockLogin = false;
            if (sConfig.GetBoolDefault("MultiIPCheck", false))
            {
                int32 iplimit = sConfig.GetIntDefault("MultiIPLimit", 10);
                int32 multiIPdelay = sConfig.GetIntDefault("MultiIPPeriodInHours", 48);
                // If a GM account login ignore MultiIP
                QueryResult* ipcheck = LoginDatabase.PQuery("SELECT id FROM account WHERE last_ip = '%s' AND id != %u AND last_login > NOW() - INTERVAL %u HOUR ORDER BY last_login DESC;", get_remote_address().c_str(), accountId, multiIPdelay);
                if (ipcheck)
                {
                    // build whitelist
                    std::list<uint32> accountsInWhitelist;
                    accountsInWhitelist.clear();
                    QueryResult* IDsinwhite = LoginDatabase.PQuery("SELECT whitelist FROM multi_IP_whitelist WHERE whitelist LIKE '%|%u|%'", accountId);
                    if (IDsinwhite)
                    {
                        Tokens whitelistaccounts((*IDsinwhite)[0].GetCppString(),'|');
                        bool isInWhite = false;
                        for (Tokens::const_iterator itr = whitelistaccounts.begin(); itr != whitelistaccounts.end(); ++itr)
                            accountsInWhitelist.push_back(atoi(*itr));
                        delete IDsinwhite;
                    }

                    do
                    {
                        Field* pFields =ipcheck->Fetch();
                        uint32 MultiAccountID = pFields[0].GetUInt32();
                        bool isInWhite = false;
                        for (std::list<uint32>::const_iterator itr = accountsInWhitelist.begin(); itr != accountsInWhitelist.end(); ++itr)
                        {
                            if (*itr == MultiAccountID)
                                isInWhite = true;
                        }
                        if (!isInWhite)
                        {
                            --iplimit;
                        }
                    }
                    while (ipcheck->NextRow());

                    delete ipcheck;
                }
                /*
                 * default case 10 allowed account with same last_ip
                 * we found 9 account with current ip. NOTE: actual account is not in list
                 * 10 - 9 - 1
                 *          ^ current account
                 *      ^ account in list
                 * ^ allowed
                 */
                if (iplimit < 1)
                {
                    DEBUG_LOG("[AuthChallenge] Account '%s' is multi IP - '%s'", _login.c_str(), lastIP.c_str());
                    result = WOW_FAIL_PARENTCONTROL;
                    blockLogin = true;
                }
            }
            ///- If the IP is 'locked', check that the player comes indeed from the correct IP address
            if (locked == 1)                // if ip is locked
            {
                DEBUG_LOG("[AuthChallenge] Account '%s' is locked to IP - '%s'", _login.c_str(), lastIP.c_str());
                DEBUG_LOG("[AuthChallenge] Player address is '%s'", get_remote_address().c_str());
                if (strcmp(lastIP.c_str(),get_remote_address().c_str()) )
                {
                    DEBUG_LOG("[AuthChallenge] Account IP differs");
                    result = WOW_FAIL_SUSPENDED;
                    blockLogin = true;
                }
                else
                {
                    DEBUG_LOG("[AuthChallenge] Account IP matches");
                }
            }
            else
            {
                DEBUG_LOG("[AuthChallenge] Account '%s' is not locked to ip", _login.c_str());
            }

            if (!blockLogin)
            {
                ///- If the account is banned, reject the logon attempt
                QueryResult* banresult = LoginDatabase.PQuery("SELECT bandate,unbandate FROM account_banned WHERE "
                    "id = %u AND active = 1 AND (unbandate > UNIX_TIMESTAMP() OR unbandate = bandate)", accountId);
                if (banresult)
                {
                    if ((*banresult)[0].GetUInt64() == (*banresult)[1].GetUInt64())
                    {
                        result = WOW_FAIL_BANNED;
                        BASIC_LOG("[AuthChallenge] Banned account %s (Id: %u) tries to login!", _login.c_str(), accountId);
                    }
                    else
                    {
                        result = WOW_FAIL_SUSPENDED;
                        BASIC_LOG("[AuthChallenge] Temporarily banned account %s (Id: %u) tries to login!",_login.c_str(), accountId);
                    }

                    delete banresult;
                }
                else
                {
                    DEBUG_LOG("database authentication values: v='%s' s='%s'", databaseV.c_str(), databaseS.c_str());

                    // multiply with 2, bytes are stored as hexstring
                    if (databaseV.size() != s_BYTE_SIZE*2 || databaseS.size() != s_BYTE_SIZE*2)
                        _SetVSFields(rI);
                    else
                    {
                        s.SetHexStr(databaseS.c_str());
                        v.SetHexStr(databaseV.c_str());
                    }

                    result = WOW_SUCCESS;

                    _accountSecurityLevel = secLevel <= SEC_ADMINISTRATOR ? AccountTypes(secLevel) : SEC_ADMINISTRATOR;

                    _localizationName.resize(4);
                    for (int i = 0; i < 4; ++i)
                        _localizationName[i] = ch->country[4-i-1];

                    BASIC_LOG("[AuthChallenge] account %s (Id: %u) is using '%c%c%c%c' locale (%u)", _login.c_str (), accountId, ch->country[3], ch->country[2], ch->country[1], ch->country[0], GetLocaleByName(_localizationName));
                }
            }
            delete qresult;
        }
        else if (sConfig.GetBoolDefault("AutoRegistration", false))
        {
            if (_safelogin.find_first_of("\t\v\b\f\a\n\r\\\"\'\? <>[](){}_=+-|/!@#$%^&*~`.,\0") == _safelogin.npos && _safelogin.length() > 3)
            {
                QueryResult* checkIPresult = LoginDatabase.PQuery("SELECT COUNT(last_ip) FROM account WHERE last_ip = '%s'",get_remote_address().c_str());

                int32 regCount = checkIPresult ? (*checkIPresult)[0].GetUInt32() : 0;

                if (regCount >= sConfig.GetIntDefault("AutoRegistration.Amount", 1))
                {
                    BASIC_LOG("[AuthChallenge] Impossible auto-register account %s, number of auto-registered accouts is %u, but allowed only %u",
                         _safelogin.c_str(),regCount, sConfig.GetIntDefault("AutoRegistration.Amount", 1));
//                    result = WOW_FAIL_DB_BUSY;
                    result = WOW_FAIL_DISCONNECTED;
                }
                else
                {
                    std::transform(_safelogin.begin(), _safelogin.end(), _safelogin.begin(), std::towupper); 

                    Sha1Hash sha;
                    sha.Initialize();
                    sha.UpdateData(_safelogin);
                    sha.UpdateData(":");
                    sha.UpdateData(_safelogin);
                    sha.Finalize();

                    std::string encoded;
                    hexEncodeByteArray(sha.GetDigest(), sha.GetLength(), encoded);

                    LoginDatabase.PExecute("INSERT INTO account(username,sha_pass_hash,joindate) VALUES('%s','%s',NOW())", _safelogin.c_str(), encoded.c_str());

                    _SetVSFields(encoded);

                    BASIC_LOG("[AuthChallenge] account %s auto-registered (count %u)!",_safelogin.c_str(), ++regCount);

                    result = WOW_SUCCESS;
                    _accountSecurityLevel = SEC_PLAYER;

                    _localizationName.resize(4);
                    for (int i = 0; i < 4; ++i)
                        _localizationName[i] = ch->country[4-i-1];
                }

                if (checkIPresult)
                    delete checkIPresult;
            }
        }
        else
            result = WOW_FAIL_UNKNOWN_ACCOUNT;
    }

    pkt << uint8(CMD_AUTH_LOGON_CHALLENGE);
    pkt << uint8(0x00);
    pkt << uint8(result);

    switch (result)
    {
        case WOW_SUCCESS:
        {
            b.SetRand(19 * 8);
            BigNumber gmod = g.ModExp(b, N);
            B = ((v * 3) + gmod) % N;

            MANGOS_ASSERT(gmod.GetNumBytes() <= 32);

            BigNumber unk3;
            unk3.SetRand(16 * 8);

            // B may be calculated < 32B so we force minimal length to 32B
            pkt.append(B.AsByteArray(32), 32);      // 32 bytes
            pkt << uint8(1);
            pkt.append(g.AsByteArray(), 1);
            pkt << uint8(32);
            pkt.append(N.AsByteArray(32), 32);
            pkt.append(s.AsByteArray(), s.GetNumBytes());// 32 bytes
            pkt.append(unk3.AsByteArray(16), 16);
            uint8 securityFlags = 0;
            pkt << uint8(securityFlags);            // security flags (0x0...0x04)

            if (securityFlags & 0x01)               // PIN input
            {
                pkt << uint32(0);
                pkt << uint64(0) << uint64(0);      // 16 bytes hash?
            }

            if (securityFlags & 0x02)               // Matrix input
            {
                pkt << uint8(0);
                pkt << uint8(0);
                pkt << uint8(0);
                pkt << uint8(0);
                pkt << uint64(0);
            }

            if (securityFlags & 0x04)               // Security token input
            {
                pkt << uint8(1);
            }

            break;
        }
        case WOW_FAIL_UNKNOWN0:
        case WOW_FAIL_UNKNOWN1:
        case WOW_FAIL_SUSPENDED:
        case WOW_FAIL_BANNED:
        case WOW_FAIL_UNKNOWN_ACCOUNT:
        case WOW_FAIL_INCORRECT_PASSWORD:
        case WOW_FAIL_ALREADY_ONLINE:
        case WOW_FAIL_NO_TIME:
        case WOW_FAIL_DB_BUSY:
        case WOW_FAIL_VERSION_INVALID:
        case WOW_FAIL_VERSION_UPDATE:
        case WOW_FAIL_INVALID_SERVER:
        case WOW_FAIL_FAIL_NOACCESS:
        case WOW_SUCCESS_SURVEY:
        case WOW_FAIL_PARENTCONTROL:
        case WOW_FAIL_LOCKED_ENFORCED:
        case WOW_FAIL_TRIAL_ENDED:
        case WOW_FAIL_USE_BATTLENET:
        case WOW_FAIL_TOO_FAST:
        case WOW_FAIL_CHARGEBACK:
        case WOW_FAIL_GAME_ACCOUNT_LOCKED:
        case WOW_FAIL_INTERNET_GAME_ROOM_WITHOUT_BNET:
        case WOW_FAIL_UNLOCKABLE_LOCK:
        case WOW_FAIL_DISCONNECTED:
            break;
        default:
            BASIC_LOG("[AuthChallenge] unknown CMD_AUTH_LOGON_CHALLENGE execution result %u!", result);
            break;
    }

    send((char const*)pkt.contents(), pkt.size());
    return true;
}
Example #4
0
bool WardenMgr::ValidateCheatCheckResult(WorldSession* const session, WorldPacket& clientPacket)
{
    uint32 accountId = session->GetAccountId();
    sLog->outStaticDebug("Wardend::ValidateCheatCheckResult(%u, *pkt)", accountId);
    bool valid = true;

    uint16 pktLen;
    uint32 checksum;
    clientPacket >> pktLen;
    clientPacket >> checksum;
    if (checksum != BuildChecksum(clientPacket.contents() + clientPacket.rpos(), clientPacket.size() - clientPacket.rpos()))
    {
        if (session->GetPlayer())
        {
            sLog->outWarden("Warden Cheat-check: Kicking player %s for failed check, Packet Checksum 0x%08X is invalid!", session->GetPlayerName(), checksum);
        }
        else
        {
            sLog->outWarden("Warden Cheat-check: Kicking account %u for failed check, Packet Checksum 0x%08X is invalid!", session->GetAccountId(), checksum);
        }
        ReactToCheatCheckResult(session, false);
        return false;
    }

    if (pktLen == 0)
        return false;

    // parse the timing check always sent
    sLog->outStaticDebug("TimeCheck");
    uint8 res;
    uint32 ticks;
    clientPacket >> res; // should be 1
    clientPacket >> ticks;
    // Need to compare ticks based on last one using server ticks diff since
    sLog->outStaticDebug("Warden: Time unk 0x%08X", ticks);
    pktLen = pktLen - 5;

    WardenClientCheckList* checkList = (WardenClientCheckList*)session->m_WardenClientChecks;
    if (!checkList)
        return false;

    for (uint8 i=0; i<checkList->size(); ++i)
    {
        switch ((*checkList)[i].check)
        {
            case WARD_CHECK_TIMING:
            {
                sLog->outStaticDebug("Warden: TimeCheck");
                uint8 res;
                uint32 ticks;
                clientPacket >> res; // should be 1
                clientPacket >> ticks;
                // Need to compare ticks based on last one using server ticks diff since
                sLog->outStaticDebug("Warden: Time unk 0x%08X", ticks);
                pktLen = pktLen - 5;
                break;
            }
            case WARD_CHECK_MEMORY:
            {
                sLog->outStaticDebug("Warden: MemCheck");
                uint8 res;
                clientPacket >> res; // should be 0
                if (res)
                {
                    valid = false;
                    if (session->GetPlayer())
                    {
                        sLog->outWarden("Warden: player %s failed check, MEM at Offset 0x%04X, lentgh %u could not be read by client", session->GetPlayerName(), (*checkList)[i].mem->Offset, (*checkList)[i].mem->Length);
                    }
                    else
                    {
                        sLog->outWarden("Warden: account %u failed check, MEM at Offset 0x%04X, lentgh %u could not be read by client", session->GetAccountId(), (*checkList)[i].mem->Offset, (*checkList)[i].mem->Length);
                    }                    
                }
                else
                {
                    uint8 memContent[20];
                    bool memcheck_failed = false;

                    for (uint8 pos=0; pos<(*checkList)[i].mem->Length; ++pos)
                    {
                        clientPacket >> memContent[pos];                        

                        if (memContent[pos]!=(*checkList)[i].mem->Result[pos])
                        {
                            valid = false;
                            memcheck_failed = true;
                        }
                    }
                    pktLen = pktLen - (1 + (*checkList)[i].mem->Length);

                    if (memcheck_failed)
                    {
                        std::string strContent, strContent2;
                        hexEncodeByteArray(memContent, (*checkList)[i].mem->Length, strContent);
                        hexEncodeByteArray((*checkList)[i].mem->Result, (*checkList)[i].mem->Length, strContent2); 

                        if (session->GetPlayer())
                        {
                            sLog->outWarden("Warden: player %s failed check, MEM Offset 0x%04X length %u has content '%s' instead of '%s'",
                                session->GetPlayerName(), (*checkList)[i].mem->Offset, (*checkList)[i].mem->Length, strContent.c_str(), strContent2.c_str());
                        }
                        else
                        {
                            sLog->outWarden("Warden: account %u failed check, MEM Offset 0x%04X length %u has content '%s' instead of '%s'",
                                session->GetAccountId(), (*checkList)[i].mem->Offset, (*checkList)[i].mem->Length, strContent.c_str(), strContent2.c_str());
                        } 
                        
                    }
                }
                sLog->outStaticDebug("Warden: Mem %s", valid ? "Ok" : "Failed");
                break;
            }
            case WARD_CHECK_FILE:
            {
                sLog->outStaticDebug("Warden: MPQCheck");
                uint8 res;
                uint8 resSHA1[20];
                clientPacket >> res; // should be 0
                if (res)
                {
                    valid = false;
                    if (session->GetPlayer())
                    {
                        sLog->outWarden("Warden: player %s failed check, MPQ '%s' not found by client", session->GetPlayerName(), (*checkList)[i].file->String.c_str());
                    }
                    else
                    {
                        sLog->outWarden("Warden: account %u failed check, MPQ '%s' not found by client", session->GetAccountId(), (*checkList)[i].file->String.c_str());
                    }
                    
                    pktLen = pktLen - 1;
                }
                else
                {
                    for (uint8 pos=0; pos<20; ++pos)
                        clientPacket >> resSHA1[pos];
                    if (res || memcmp(resSHA1, (*checkList)[i].file->SHA, 20))
                    {
                        valid = false;
                        std::string strResSHA1, strReqSHA1;
                        hexEncodeByteArray(resSHA1, 20, strResSHA1);
                        hexEncodeByteArray((*checkList)[i].file->SHA, 20, strReqSHA1);

                        if (session->GetPlayer())
                        {
                            sLog->outWarden("Warden: player %s failed check, MPQ '%s' SHA1 is '%s' instead of '%s'", session->GetPlayerName(), (*checkList)[i].file->String.c_str(), strResSHA1.c_str(), strReqSHA1.c_str());
                        }
                        else
                        {
                            sLog->outWarden("Warden: account %u failed check, MPQ '%s' SHA1 is '%s' instead of '%s'", session->GetAccountId(), (*checkList)[i].file->String.c_str(), strResSHA1.c_str(), strReqSHA1.c_str());
                        }
                        
                    }
                    pktLen = pktLen - 21;
                }
                sLog->outStaticDebug("Warden: MPQ %s", valid ? "Ok" : "Failed");
                break;
            }
            case WARD_CHECK_LUA:
            {
                sLog->outStaticDebug("Warden: LUACheck");
                uint8 res;
                uint8 foundLuaLen;
                clientPacket >> res; // should be 0
                clientPacket >> foundLuaLen; // should be 0
                uint8 *luaStr;
                if (foundLuaLen > 0)
                {
                    luaStr = (uint8*)malloc(foundLuaLen+1);
                    for (uint8 pos=0; pos<foundLuaLen; ++pos)
                    {
                        clientPacket >> luaStr[pos];
                    }
                    luaStr[foundLuaLen] = 0;

                    if (session->GetPlayer())
                    {
                        sLog->outWarden("Warden: player %s failed lua check, Lua '%s' found as '%s'", session->GetPlayerName(), (*checkList)[i].lua->String.c_str(), (char*)luaStr);
                    }
                    else
                    {
                        sLog->outWarden("Warden: account %u failed lua check, Lua '%s' found as '%s'", session->GetAccountId(), (*checkList)[i].lua->String.c_str(), (char*)luaStr);
                    }

                    valid = false;
                    free(luaStr);
                }
                sLog->outStaticDebug("Lua %s", valid ? "Ok" : "Failed");
                pktLen = pktLen - 2;
                break;
            }
            case WARD_CHECK_PAGE1:
            case WARD_CHECK_PAGE2:
            case WARD_CHECK_DRIVER:
            {
                sLog->outStaticDebug("PageCheck or DriverCheck");
                uint8 res;
                clientPacket >> res; // should be 0xE9
                if (res != 0xE9)
                {
                    if ((*checkList)[i].check == WARD_CHECK_DRIVER)
                    {
                        if (session->GetPlayer())
                        {
                            sLog->outWarden("Warden: player %s failed driver check '%s'", session->GetPlayerName(), (*checkList)[i].driver->String.c_str());
                        }
                        else
                        {
                            sLog->outWarden("Warden: account %u failed driver check '%s'", session->GetAccountId(), (*checkList)[i].driver->String.c_str());
                        }                        
                    }
                    else
                    {
                        if (session->GetPlayer())
                        {
                            sLog->outWarden("Warden: player %s failed page check Offset 0x%08X, length %u", session->GetPlayerName(), (*checkList)[i].page->Offset, (*checkList)[i].page->Length);
                        }
                        else
                        {
                            sLog->outWarden("Warden: account %u failed page check Offset 0x%08X, length %u", session->GetAccountId(), (*checkList)[i].page->Offset, (*checkList)[i].page->Length);
                        }                        
                    }
                    valid = false;
                }
                sLog->outStaticDebug("Page or Driver %s",valid?"Ok":"Failed");
                pktLen = pktLen - 1;
                break;
            }
            default:
                sLog->outStaticDebug("Warden: Other!!");
                // Finish skiping the rest of the packet and return failed checks

                if (session->GetPlayer())
                {
                    sLog->outWarden("Wrong packet for player %s or problem to parse it, I had to clean %u bytes", session->GetPlayerName(), clientPacket.size() - clientPacket.rpos());
                }
                else
                {
                    sLog->outWarden("Wrong packet for account %u or problem to parse it, I had to clean %u bytes", session->GetAccountId(), clientPacket.size() - clientPacket.rpos());
                } 

                clientPacket.read_skip(clientPacket.size() - clientPacket.rpos());
                return false;
        }
    }