Ejemplo n.º 1
0
static enum proto_parse_status parse_rpc_call(struct cursor *cursor, struct rpc_proto_info *info)
{
    CHECK(28);
    info->u.call_msg.rpc_version = cursor_read_u32n(cursor);
    if (info->u.call_msg.rpc_version != 2) {
        SLOG(LOG_DEBUG, "Rpc version should be 2, got %"PRIu32, info->u.call_msg.rpc_version);
        return PROTO_PARSE_ERR;
    }
    info->u.call_msg.program = cursor_read_u32n(cursor);
    info->u.call_msg.program_version = cursor_read_u32n(cursor);
    info->u.call_msg.procedure = cursor_read_u32n(cursor);
    RPC_CHECK_AUTH(credential);
    RPC_CHECK_AUTH(auth);
    return PROTO_OK;
}
Ejemplo n.º 2
0
enum proto_parse_status cursor_read_fixed_int_n(struct cursor *cursor, uint_least64_t *out_res, unsigned len)
{
    uint_least64_t res;
    if (cursor->cap_len < len) return PROTO_TOO_SHORT;
    switch (len) {
        case 0:
            res = 0;
            break;
        case 1:
            res = cursor_read_u8(cursor);
            break;
        case 2:
            res = cursor_read_u16n(cursor);
            break;
        case 3:
            res = cursor_read_u24n(cursor);
            break;
        case 4:
            res = cursor_read_u32n(cursor);
            break;
        case 8:
            res = cursor_read_u64n(cursor);
            break;
        default:
            SLOG(LOG_DEBUG, "Can't read a %d bytes long number", len);
            return PROTO_PARSE_ERR;
    }
    if (out_res) *out_res = res;
    return PROTO_OK;
}
Ejemplo n.º 3
0
/* Read a message header, return type and msg length, and advance the cursor to the msg payload.
 * if type is NULL that means no type are read from the cursor.
 * return PROTO_TOO_SHORT if the msg content is not available. */
static enum proto_parse_status cursor_read_msg(struct cursor *cursor, uint8_t *type, size_t *len_)
{
    SLOG(LOG_DEBUG, "Reading new message");
    unsigned rollback = 0;

    if (type) { // read type first
        CHECK_LEN(cursor, 1, rollback);
        *type = cursor_read_u8(cursor);
        rollback++;
        SLOG(LOG_DEBUG, "... of type %u ('%c')", (unsigned)*type, *type);
    }

    // read length
    CHECK_LEN(cursor, 4, rollback);
    size_t len = cursor_read_u32n(cursor);
    rollback += 4;
    if (len < 4) return PROTO_PARSE_ERR;    // as length includes itself
    len -= 4;
    SLOG(LOG_DEBUG, "... of length %zu", len);

    if (len_) *len_ = len;

    // read payload
    CHECK_LEN(cursor, len, rollback);

    return PROTO_OK;
}
Ejemplo n.º 4
0
static enum proto_parse_status pg_parse_startup(struct pgsql_parser *pg_parser, struct sql_proto_info *info, unsigned way, uint8_t const *payload, size_t cap_len, size_t unused_ wire_len, struct timeval const *now, size_t tot_cap_len, uint8_t const *tot_packet)
{
    info->msg_type = SQL_STARTUP;

    struct cursor cursor;
    cursor_ctor(&cursor, payload, cap_len);
    uint8_t type;
    size_t len;
    enum proto_parse_status status = cursor_read_msg(&cursor, &type, &len);
    if (status != PROTO_OK) return status;

    /* In this phase, we expect to see from the client the pwd message,
     * and from the server the authentication request. */
    if (info->is_query) {   // password message
        if (type != 'p') return PROTO_PARSE_ERR;
        char *passwd;
        status = cursor_read_string(&cursor, &passwd, len);
        if (status == PROTO_PARSE_ERR) return status;
        if (status == PROTO_TOO_SHORT) {    // in case of GSSAPI or SSPI authentication then the "string" is in fact arbitrary bytes
            passwd = "GSSAPI/SSPI";
        }
        info->set_values |= SQL_PASSWD;
        snprintf(info->u.startup.passwd, sizeof(info->u.startup.passwd), "%s", passwd);
    } else {    // Authentication request
        SLOG(LOG_DEBUG, "Authentification response from server with type %c", type);
        if (len < 4) return PROTO_PARSE_ERR;
        if (type == 'E') {
            status = pg_parse_error(info, &cursor, len);
            if (status != PROTO_OK) return status;
        } else if (type == 'R' ) {
            // We don't care about the auth method, we just want to know when auth is complete
            uint32_t auth_type = cursor_read_u32n(&cursor);
            if (auth_type == 0) {   // AuthenticationOK
                pg_parser->phase = QUERY;   // we don't wait for the ReadyForQuery msg since we are not interrested in following messages
                info->set_values |= SQL_REQUEST_STATUS;
                info->request_status = SQL_REQUEST_COMPLETE;
            }
        } else {
            SLOG(LOG_DEBUG, "Unknown startup message with type %c", type);
            return PROTO_PARSE_ERR;
        }
    }

    // Discard the rest of the packet
    return proto_parse(NULL, &info->info, way, NULL, 0, 0, now, tot_cap_len, tot_packet);
}
Ejemplo n.º 5
0
static void cursor_check(void)
{
    struct cursor cursor;

    static uint8_t const data[] = { 1, 2 };
    cursor_ctor(&cursor, data, sizeof(data));
    assert(cursor_peek_u8(&cursor, 0) == 0x01U);
    assert(cursor_peek_u8(&cursor, 1) == 0x02U);
    assert(cursor_read_u8(&cursor) == 0x01U);
    assert(cursor_read_u8(&cursor) == 0x02U);

    static uint16_t const data16[] = { 0x0102, 0x0304 };
    cursor_ctor(&cursor, (uint8_t *)data16, sizeof(data16));
    assert(cursor_peek_u16le(&cursor, 0) == 0x0102U);
    assert(cursor_peek_u16le(&cursor, 2) == 0x0304U);
    assert(cursor_read_u16(&cursor) == 0x0102U);
    assert(cursor_read_u16(&cursor) == 0x0304U);

    static uint32_t const data32[] = { 0x01020304U, 0x05060708U };
    cursor_ctor(&cursor, (uint8_t *)data32, sizeof(data32));
    assert(cursor_peek_u32le(&cursor, 0) == 0x01020304U);
    assert(cursor_peek_u32le(&cursor, 4) == 0x05060708U);
    assert(cursor_read_u32(&cursor) == 0x01020304U);
    assert(cursor_read_u32(&cursor) == 0x05060708U);

    static uint64_t const data64[] = { 0x0102030405060708ULL };
    cursor_ctor(&cursor, (uint8_t *)data64, sizeof(data64));
    assert(cursor_peek_u64le(&cursor, 0) == 0x0102030405060708ULL);
    assert(cursor_read_u64(&cursor) == 0x0102030405060708ULL);

    static uint8_t const datan[] = { 1, 2, 3, 4 };
    cursor_ctor(&cursor, datan, sizeof(datan));
    assert(cursor_peek_u32n(&cursor, 0) == 0x01020304);
    assert(cursor_read_u32n(&cursor) == 0x01020304);

    cursor_ctor(&cursor, datan, sizeof(datan));
    assert(cursor_peek_u16n(&cursor, 0) == 0x0102);
    assert(cursor_read_u16n(&cursor) == 0x0102);
}
Ejemplo n.º 6
0
static enum proto_parse_status rpc_parse(struct parser *parser, struct proto_info *parent, unsigned unused_ way,
        uint8_t const *packet, size_t cap_len, size_t wire_len, struct timeval const unused_ *now,
        size_t unused_ tot_cap_len, uint8_t const unused_ *tot_packet)
{
    struct cursor cursor;
    cursor_ctor(&cursor, packet, cap_len);
    if (wire_len < 12) return PROTO_PARSE_ERR;
    if (cap_len < 12) return PROTO_TOO_SHORT;

    ASSIGN_INFO_OPT(tcp, parent);

    struct rpc_proto_info info;
    proto_info_ctor(&info.info, parser, parent, wire_len, 0);

    if (tcp) cursor_drop(&cursor, 4); // Drop fragment header
    cursor_drop(&cursor, 4);
    info.msg_type = cursor_read_u32n(&cursor);
    enum proto_parse_status status = PROTO_OK;
    switch (info.msg_type) {
    case RPC_CALL:
        if (wire_len < 40) return PROTO_PARSE_ERR;
        status = parse_rpc_call(&cursor, &info);
        break;
    case RPC_REPLY:
        status = parse_rpc_reply(&cursor, &info);
        break;
    default:
        return PROTO_PARSE_ERR;
    }
    SLOG(LOG_DEBUG, "Parsed rpc status %s, %s", proto_parse_status_2_str(status), rpc_info_2_str(&info.info));
    if (status == PROTO_OK) {
        // TODO We can have a nfs payload
        (void)proto_parse(NULL, &info.info, way, NULL, 0, 0, now, tot_cap_len, tot_packet);
    }
    return status;
}
Ejemplo n.º 7
0
static enum proto_parse_status pg_parse_init(struct pgsql_parser *pg_parser, struct sql_proto_info *info, unsigned way, uint8_t const *payload, size_t cap_len, size_t wire_len, struct timeval const *now, size_t tot_cap_len, uint8_t const *tot_packet)
{
    info->msg_type = SQL_STARTUP;

    /* NONE phase is when we haven't seen the startup message yet.
     * In this phase, we expect to see from the client a startup message,
     * and from the server nothing but an answer to an SSL request. */
    if (info->is_query) {
        struct cursor cursor;
        cursor_ctor(&cursor, payload, cap_len);

        // Startup message comes without a type tag
        size_t len;
        enum proto_parse_status status = cursor_read_msg(&cursor, NULL, &len);
        if (status != PROTO_OK) return status;
        SLOG(LOG_DEBUG, "Msg of length %zu", len);
        if (len < 4) return PROTO_PARSE_ERR;
        uint32_t msg = cursor_read_u32n(&cursor);
        if (msg == 80877103) {  // magic value for SSL request
            SLOG(LOG_DEBUG, "Msg is an SSL request");
            info->set_values |= SQL_SSL_REQUEST;
            info->u.startup.ssl_request = SQL_SSL_REQUESTED;
        } else if (msg == 196608) { // version number, here 00 03 00 00 (ie. 3.0), which is parsed here
            SLOG(LOG_DEBUG, "Msg is a startup message for v3.0");
            info->version_maj = 3;
            info->version_min = 0;
            info->set_values |= SQL_VERSION;
            // fine, now parse all the strings that follow
            do {
                char *name, *value;
                status = cursor_read_string(&cursor, &name, len);
                if (status != PROTO_OK) return status;
                if (name[0] == '\0') break;
                status = cursor_read_string(&cursor, &value, len);
                if (status != PROTO_OK) return status;
                if (0 == strcmp(name, "user")) {
                    info->set_values |= SQL_USER;
                    snprintf(info->u.startup.user, sizeof(info->u.startup.user), "%s", value);
                } else if (0 == strcmp(name, "database")) {
                    info->set_values |= SQL_DBNAME;
                    snprintf(info->u.startup.dbname, sizeof(info->u.startup.dbname), "%s", value);
                }
            } while (1);
            // and enter "startup phase" untill the server is ready for query
            pg_parser->phase = STARTUP;
        } else {
            SLOG(LOG_DEBUG, "Unknown message");
            return PROTO_PARSE_ERR;
        }
    } else {    // reply (to an SSL request)
        if (wire_len != 1 || cap_len < 1) return PROTO_TOO_SHORT;
        info->set_values |= SQL_SSL_REQUEST;
        if (payload[0] == 'S') {
            info->u.startup.ssl_request = SQL_SSL_GRANTED;  // We will get parse errors from now on :-<
        } else if (payload[0] == 'N') {
            info->u.startup.ssl_request = SQL_SSL_REFUSED;
        } else {
            return PROTO_PARSE_ERR;
        }
    }

    return proto_parse(NULL, &info->info, way, NULL, 0, 0, now, tot_cap_len, tot_packet);
}