Example #1
0
NTSTATUS smbXsrv_open_update(struct smbXsrv_open *op)
{
    struct smbXsrv_open_table *table = op->table;
    NTSTATUS status;
    uint8_t key_buf[SMBXSRV_OPEN_GLOBAL_TDB_KEY_SIZE];
    TDB_DATA key;

    if (op->global->db_rec != NULL) {
        DEBUG(0, ("smbXsrv_open_update(0x%08x): "
                  "Called with db_rec != NULL'\n",
                  op->global->open_global_id));
        return NT_STATUS_INTERNAL_ERROR;
    }

    key = smbXsrv_open_global_id_to_key(op->global->open_global_id,
                                        key_buf);

    op->global->db_rec = dbwrap_fetch_locked(table->global.db_ctx,
                         op->global, key);
    if (op->global->db_rec == NULL) {
        DEBUG(0, ("smbXsrv_open_update(0x%08x): "
                  "Failed to lock global key '%s'\n",
                  op->global->open_global_id,
                  hex_encode_talloc(talloc_tos(), key.dptr,
                                    key.dsize)));
        return NT_STATUS_INTERNAL_DB_ERROR;
    }

    status = smbXsrv_open_global_store(op->global);
    if (!NT_STATUS_IS_OK(status)) {
        DEBUG(0,("smbXsrv_open_update: "
                 "global_id (0x%08x) store failed - %s\n",
                 op->global->open_global_id,
                 nt_errstr(status)));
        return status;
    }

    if (CHECK_DEBUGLVL(10)) {
        struct smbXsrv_openB open_blob;

        ZERO_STRUCT(open_blob);
        open_blob.version = SMBXSRV_VERSION_0;
        open_blob.info.info0 = op;

        DEBUG(10,("smbXsrv_open_update: global_id (0x%08x) stored\n",
                  op->global->open_global_id));
        NDR_PRINT_DEBUG(smbXsrv_openB, &open_blob);
    }

    return NT_STATUS_OK;
}
Example #2
0
static NTSTATUS smbXsrv_open_global_lookup(struct smbXsrv_open_table *table,
        uint32_t open_global_id,
        TALLOC_CTX *mem_ctx,
        struct smbXsrv_open_global0 **_global)
{
    TDB_DATA key;
    uint8_t key_buf[SMBXSRV_OPEN_GLOBAL_TDB_KEY_SIZE];
    struct db_record *global_rec = NULL;
    bool is_free = false;

    *_global = NULL;

    if (table->global.db_ctx == NULL) {
        return NT_STATUS_INTERNAL_ERROR;
    }

    key = smbXsrv_open_global_id_to_key(open_global_id, key_buf);

    global_rec = dbwrap_fetch_locked(table->global.db_ctx, mem_ctx, key);
    if (global_rec == NULL) {
        DEBUG(0, ("smbXsrv_open_global_lookup(0x%08x): "
                  "Failed to lock global key '%s'\n",
                  open_global_id,
                  hex_encode_talloc(talloc_tos(), key.dptr,
                                    key.dsize)));
        return NT_STATUS_INTERNAL_DB_ERROR;
    }

    smbXsrv_open_global_verify_record(global_rec,
                                      &is_free,
                                      NULL,
                                      mem_ctx,
                                      _global);
    if (is_free) {
        talloc_free(global_rec);
        return NT_STATUS_OBJECT_NAME_NOT_FOUND;
    }

    (*_global)->db_rec = talloc_move(*_global, &global_rec);

    talloc_set_destructor(*_global, smbXsrv_open_global_destructor);

    return NT_STATUS_OK;
}
Example #3
0
static struct db_record *smbXsrv_open_global_fetch_locked(
			struct db_context *db,
			uint32_t id,
			TALLOC_CTX *mem_ctx)
{
	TDB_DATA key;
	uint8_t key_buf[SMBXSRV_OPEN_GLOBAL_TDB_KEY_SIZE];
	struct db_record *rec = NULL;

	key = smbXsrv_open_global_id_to_key(id, key_buf);

	rec = dbwrap_fetch_locked(db, mem_ctx, key);

	if (rec == NULL) {
		DBG_DEBUG("Failed to lock global id 0x%08x, key '%s'\n", id,
			  hex_encode_talloc(talloc_tos(), key.dptr, key.dsize));
	}

	return rec;
}
Example #4
0
NTSTATUS smbXsrv_open_close(struct smbXsrv_open *op, NTTIME now)
{
    struct smbXsrv_open_table *table;
    struct db_record *local_rec = NULL;
    struct db_record *global_rec = NULL;
    NTSTATUS status;
    NTSTATUS error = NT_STATUS_OK;

    if (op->table == NULL) {
        return NT_STATUS_OK;
    }

    table = op->table;
    op->table = NULL;

    op->status = NT_STATUS_FILE_CLOSED;
    op->global->disconnect_time = now;
    server_id_set_disconnected(&op->global->server_id);

    global_rec = op->global->db_rec;
    op->global->db_rec = NULL;
    if (global_rec == NULL) {
        uint8_t key_buf[SMBXSRV_OPEN_GLOBAL_TDB_KEY_SIZE];
        TDB_DATA key;

        key = smbXsrv_open_global_id_to_key(
                  op->global->open_global_id,
                  key_buf);

        global_rec = dbwrap_fetch_locked(table->global.db_ctx,
                                         op->global, key);
        if (global_rec == NULL) {
            DEBUG(0, ("smbXsrv_open_close(0x%08x): "
                      "Failed to lock global key '%s'\n",
                      op->global->open_global_id,
                      hex_encode_talloc(global_rec, key.dptr,
                                        key.dsize)));
            error = NT_STATUS_INTERNAL_ERROR;
        }
    }

    if (global_rec != NULL && op->global->durable) {
        /*
         * If it is a durable open we need to update the global part
         * instead of deleting it
         */
        op->global->db_rec = global_rec;
        status = smbXsrv_open_global_store(op->global);
        if (NT_STATUS_IS_OK(status)) {
            /*
             * smbXsrv_open_global_store does the free
             * of op->global->db_rec
             */
            global_rec = NULL;
        }
        if (!NT_STATUS_IS_OK(status)) {
            DEBUG(0,("smbXsrv_open_close(0x%08x)"
                     "smbXsrv_open_global_store() failed - %s\n",
                     op->global->open_global_id,
                     nt_errstr(status)));
            error = status;
        }

        if (NT_STATUS_IS_OK(status) && CHECK_DEBUGLVL(10)) {
            struct smbXsrv_openB open_blob;

            ZERO_STRUCT(open_blob);
            open_blob.version = SMBXSRV_VERSION_0;
            open_blob.info.info0 = op;

            DEBUG(10,("smbXsrv_open_close(0x%08x): "
                      "stored disconnect\n",
                      op->global->open_global_id));
            NDR_PRINT_DEBUG(smbXsrv_openB, &open_blob);
        }
    }

    if (global_rec != NULL) {
        status = dbwrap_record_delete(global_rec);
        if (!NT_STATUS_IS_OK(status)) {
            TDB_DATA key = dbwrap_record_get_key(global_rec);

            DEBUG(0, ("smbXsrv_open_close(0x%08x): "
                      "failed to delete global key '%s': %s\n",
                      op->global->open_global_id,
                      hex_encode_talloc(global_rec, key.dptr,
                                        key.dsize),
                      nt_errstr(status)));
            error = status;
        }
    }
    TALLOC_FREE(global_rec);

    local_rec = op->db_rec;
    if (local_rec == NULL) {
        uint8_t key_buf[SMBXSRV_OPEN_LOCAL_TDB_KEY_SIZE];
        TDB_DATA key;

        key = smbXsrv_open_local_id_to_key(op->local_id, key_buf);

        local_rec = dbwrap_fetch_locked(table->local.db_ctx,
                                        op, key);
        if (local_rec == NULL) {
            DEBUG(0, ("smbXsrv_open_close(0x%08x): "
                      "Failed to lock local key '%s'\n",
                      op->global->open_global_id,
                      hex_encode_talloc(local_rec, key.dptr,
                                        key.dsize)));
            error = NT_STATUS_INTERNAL_ERROR;
        }
    }

    if (local_rec != NULL) {
        status = dbwrap_record_delete(local_rec);
        if (!NT_STATUS_IS_OK(status)) {
            TDB_DATA key = dbwrap_record_get_key(local_rec);

            DEBUG(0, ("smbXsrv_open_close(0x%08x): "
                      "failed to delete local key '%s': %s\n",
                      op->global->open_global_id,
                      hex_encode_talloc(local_rec, key.dptr,
                                        key.dsize),
                      nt_errstr(status)));
            error = status;
        }
        table->local.num_opens -= 1;
    }
    if (op->db_rec == NULL) {
        TALLOC_FREE(local_rec);
    }
    op->db_rec = NULL;

    if (op->compat) {
        op->compat->op = NULL;
        file_free(NULL, op->compat);
        op->compat = NULL;
    }

    return error;
}
Example #5
0
static NTSTATUS smbXsrv_open_local_allocate_id(struct db_context *db,
        uint32_t lowest_id,
        uint32_t highest_id,
        TALLOC_CTX *mem_ctx,
        struct db_record **_rec,
        uint32_t *_id)
{
    struct smbXsrv_open_local_allocate_state state = {
        .lowest_id = lowest_id,
        .highest_id = highest_id,
        .last_id = 0,
        .useable_id = lowest_id,
        .status = NT_STATUS_INTERNAL_ERROR,
    };
    uint32_t i;
    uint32_t range;
    NTSTATUS status;
    int count = 0;

    *_rec = NULL;
    *_id = 0;

    if (lowest_id > highest_id) {
        return NT_STATUS_INSUFFICIENT_RESOURCES;
    }

    /*
     * first we try randomly
     */
    range = (highest_id - lowest_id) + 1;

    for (i = 0; i < (range / 2); i++) {
        uint32_t id;
        uint8_t key_buf[SMBXSRV_OPEN_LOCAL_TDB_KEY_SIZE];
        TDB_DATA key;
        TDB_DATA val;
        struct db_record *rec = NULL;

        id = generate_random() % range;
        id += lowest_id;

        if (id < lowest_id) {
            id = lowest_id;
        }
        if (id > highest_id) {
            id = highest_id;
        }

        key = smbXsrv_open_local_id_to_key(id, key_buf);

        rec = dbwrap_fetch_locked(db, mem_ctx, key);
        if (rec == NULL) {
            return NT_STATUS_INSUFFICIENT_RESOURCES;
        }

        val = dbwrap_record_get_value(rec);
        if (val.dsize != 0) {
            TALLOC_FREE(rec);
            continue;
        }

        *_rec = rec;
        *_id = id;
        return NT_STATUS_OK;
    }

    /*
     * if the range is almost full,
     * we traverse the whole table
     * (this relies on sorted behavior of dbwrap_rbt)
     */
    status = dbwrap_traverse_read(db, smbXsrv_open_local_allocate_traverse,
                                  &state, &count);
    if (NT_STATUS_IS_OK(status)) {
        if (NT_STATUS_IS_OK(state.status)) {
            return NT_STATUS_INTERNAL_ERROR;
        }

        if (!NT_STATUS_EQUAL(state.status, NT_STATUS_INTERNAL_ERROR)) {
            return state.status;
        }

        if (state.useable_id <= state.highest_id) {
            state.status = NT_STATUS_OK;
        } else {
            return NT_STATUS_INSUFFICIENT_RESOURCES;
        }
    } else if (!NT_STATUS_EQUAL(status, NT_STATUS_INTERNAL_DB_CORRUPTION)) {
        /*
         * Here we really expect NT_STATUS_INTERNAL_DB_CORRUPTION!
         *
         * If we get anything else it is an error, because it
         * means we did not manage to find a free slot in
         * the db.
         */
        return NT_STATUS_INSUFFICIENT_RESOURCES;
    }

    if (NT_STATUS_IS_OK(state.status)) {
        uint32_t id;
        uint8_t key_buf[SMBXSRV_OPEN_LOCAL_TDB_KEY_SIZE];
        TDB_DATA key;
        TDB_DATA val;
        struct db_record *rec = NULL;

        id = state.useable_id;

        key = smbXsrv_open_local_id_to_key(id, key_buf);

        rec = dbwrap_fetch_locked(db, mem_ctx, key);
        if (rec == NULL) {
            return NT_STATUS_INSUFFICIENT_RESOURCES;
        }

        val = dbwrap_record_get_value(rec);
        if (val.dsize != 0) {
            TALLOC_FREE(rec);
            return NT_STATUS_INTERNAL_DB_CORRUPTION;
        }

        *_rec = rec;
        *_id = id;
        return NT_STATUS_OK;
    }

    return state.status;
}

struct smbXsrv_open_local_fetch_state {
    struct smbXsrv_open *op;
    NTSTATUS status;
};

static void smbXsrv_open_local_fetch_parser(TDB_DATA key, TDB_DATA data,
        void *private_data)
{
    struct smbXsrv_open_local_fetch_state *state =
        (struct smbXsrv_open_local_fetch_state *)private_data;
    void *ptr;

    if (data.dsize != sizeof(ptr)) {
        state->status = NT_STATUS_INTERNAL_DB_ERROR;
        return;
    }

    memcpy(&ptr, data.dptr, data.dsize);
    state->op = talloc_get_type_abort(ptr, struct smbXsrv_open);
    state->status = NT_STATUS_OK;
}

static NTSTATUS smbXsrv_open_local_lookup(struct smbXsrv_open_table *table,
        uint32_t open_local_id,
        uint32_t open_global_id,
        NTTIME now,
        struct smbXsrv_open **_open)
{
    struct smbXsrv_open_local_fetch_state state = {
        .op = NULL,
        .status = NT_STATUS_INTERNAL_ERROR,
    };
    uint8_t key_buf[SMBXSRV_OPEN_LOCAL_TDB_KEY_SIZE];
    TDB_DATA key;
    NTSTATUS status;

    *_open = NULL;

    if (open_local_id == 0) {
        return NT_STATUS_FILE_CLOSED;
    }

    if (table == NULL) {
        /* this might happen before the end of negprot */
        return NT_STATUS_FILE_CLOSED;
    }

    if (table->local.db_ctx == NULL) {
        return NT_STATUS_INTERNAL_ERROR;
    }

    key = smbXsrv_open_local_id_to_key(open_local_id, key_buf);

    status = dbwrap_parse_record(table->local.db_ctx, key,
                                 smbXsrv_open_local_fetch_parser,
                                 &state);
    if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
        return NT_STATUS_FILE_CLOSED;
    } else if (!NT_STATUS_IS_OK(status)) {
        return status;
    }
    if (!NT_STATUS_IS_OK(state.status)) {
        return state.status;
    }

    if (NT_STATUS_EQUAL(state.op->status, NT_STATUS_FILE_CLOSED)) {
        return NT_STATUS_FILE_CLOSED;
    }

    if (open_global_id == 0) {
        /* make the global check a no-op for SMB1 */
        open_global_id = state.op->global->open_global_id;
    }

    if (state.op->global->open_global_id != open_global_id) {
        return NT_STATUS_FILE_CLOSED;
    }

    if (now != 0) {
        state.op->idle_time = now;
    }

    *_open = state.op;
    return state.op->status;
}

static int smbXsrv_open_global_destructor(struct smbXsrv_open_global0 *global)
{
    return 0;
}

static void smbXsrv_open_global_verify_record(struct db_record *db_rec,
        bool *is_free,
        bool *was_free,
        TALLOC_CTX *mem_ctx,
        struct smbXsrv_open_global0 **_g);

static NTSTATUS smbXsrv_open_global_allocate(struct db_context *db,
        TALLOC_CTX *mem_ctx,
        struct smbXsrv_open_global0 **_global)
{
    uint32_t i;
    struct smbXsrv_open_global0 *global = NULL;
    uint32_t last_free = 0;
    const uint32_t min_tries = 3;

    *_global = NULL;

    global = talloc_zero(mem_ctx, struct smbXsrv_open_global0);
    if (global == NULL) {
        return NT_STATUS_NO_MEMORY;
    }
    talloc_set_destructor(global, smbXsrv_open_global_destructor);

    /*
     * Here we just randomly try the whole 32-bit space
     *
     * We use just 32-bit, because we want to reuse the
     * ID for SRVSVC.
     */
    for (i = 0; i < UINT32_MAX; i++) {
        bool is_free = false;
        bool was_free = false;
        uint32_t id;
        uint8_t key_buf[SMBXSRV_OPEN_GLOBAL_TDB_KEY_SIZE];
        TDB_DATA key;

        if (i >= min_tries && last_free != 0) {
            id = last_free;
        } else {
            id = generate_random();
        }
        if (id == 0) {
            id++;
        }
        if (id == UINT32_MAX) {
            id--;
        }

        key = smbXsrv_open_global_id_to_key(id, key_buf);

        global->db_rec = dbwrap_fetch_locked(db, mem_ctx, key);
        if (global->db_rec == NULL) {
            talloc_free(global);
            return NT_STATUS_INSUFFICIENT_RESOURCES;
        }

        smbXsrv_open_global_verify_record(global->db_rec,
                                          &is_free,
                                          &was_free,
                                          NULL, NULL);

        if (!is_free) {
            TALLOC_FREE(global->db_rec);
            continue;
        }

        if (!was_free && i < min_tries) {
            /*
             * The session_id is free now,
             * but was not free before.
             *
             * This happens if a smbd crashed
             * and did not cleanup the record.
             *
             * If this is one of our first tries,
             * then we try to find a real free one.
             */
            if (last_free == 0) {
                last_free = id;
            }
            TALLOC_FREE(global->db_rec);
            continue;
        }

        global->open_global_id = id;

        *_global = global;
        return NT_STATUS_OK;
    }

    /* should not be reached */
    talloc_free(global);
    return NT_STATUS_INTERNAL_ERROR;
}
Example #6
0
NTSTATUS smbXsrv_open_cleanup(uint64_t persistent_id)
{
    NTSTATUS status = NT_STATUS_OK;
    TALLOC_CTX *frame = talloc_stackframe();
    struct smbXsrv_open_global0 *op = NULL;
    uint8_t key_buf[SMBXSRV_OPEN_GLOBAL_TDB_KEY_SIZE];
    TDB_DATA key;
    TDB_DATA val;
    struct db_record *rec;
    bool delete_open = false;
    uint32_t global_id = persistent_id & UINT32_MAX;

    key = smbXsrv_open_global_id_to_key(global_id, key_buf);
    rec = dbwrap_fetch_locked(smbXsrv_open_global_db_ctx, frame, key);
    if (rec == NULL) {
        status = NT_STATUS_NOT_FOUND;
        DEBUG(1, ("smbXsrv_open_cleanup[global: 0x%08x] "
                  "failed to fetch record from %s - %s\n",
                  global_id, dbwrap_name(smbXsrv_open_global_db_ctx),
                  nt_errstr(status)));
        goto done;
    }

    val = dbwrap_record_get_value(rec);
    if (val.dsize == 0) {
        DEBUG(10, ("smbXsrv_open_cleanup[global: 0x%08x] "
                   "empty record in %s, skipping...\n",
                   global_id, dbwrap_name(smbXsrv_open_global_db_ctx)));
        goto done;
    }

    status = smbXsrv_open_global_parse_record(talloc_tos(), rec, &op);
    if (!NT_STATUS_IS_OK(status)) {
        DEBUG(1, ("smbXsrv_open_cleanup[global: 0x%08x] "
                  "failed to read record: %s\n",
                  global_id, nt_errstr(status)));
        goto done;
    }

    if (server_id_is_disconnected(&op->server_id)) {
        struct timeval now, disconnect_time;
        int64_t tdiff;
        now = timeval_current();
        nttime_to_timeval(&disconnect_time, op->disconnect_time);
        tdiff = usec_time_diff(&now, &disconnect_time);
        delete_open = (tdiff >= 1000*op->durable_timeout_msec);

        DEBUG(10, ("smbXsrv_open_cleanup[global: 0x%08x] "
                   "disconnected at [%s] %us ago with "
                   "timeout of %us -%s reached\n",
                   global_id,
                   nt_time_string(frame, op->disconnect_time),
                   (unsigned)(tdiff/1000000),
                   op->durable_timeout_msec / 1000,
                   delete_open ? "" : " not"));
    } else if (!serverid_exists(&op->server_id)) {
        DEBUG(10, ("smbXsrv_open_cleanup[global: 0x%08x] "
                   "server[%s] does not exist\n",
                   global_id, server_id_str(frame, &op->server_id)));
        delete_open = true;
    }

    if (!delete_open) {
        goto done;
    }

    status = dbwrap_record_delete(rec);
    if (!NT_STATUS_IS_OK(status)) {
        DEBUG(1, ("smbXsrv_open_cleanup[global: 0x%08x] "
                  "failed to delete record"
                  "from %s: %s\n", global_id,
                  dbwrap_name(smbXsrv_open_global_db_ctx),
                  nt_errstr(status)));
        goto done;
    }

    DEBUG(10, ("smbXsrv_open_cleanup[global: 0x%08x] "
               "delete record from %s\n",
               global_id,
               dbwrap_name(smbXsrv_open_global_db_ctx)));

done:
    talloc_free(frame);
    return status;
}