I can offer some general suggestions although I don’t have a direct answer or resolution for your specific problem.
Bear in mind that I am not very familiar with the ST-provided HAL (my preference is to access peripheral registers directly using their CMSIS names and symbols); some of what I say here might need to be done a bit differently if you’re going to work through the HAL.
The general requirements to get an interrupt working for any STM32 peripheral are:
Set the interrupt-source-enable bit(s) in the configuration or interrupt-enable register(s) associated with the peripheral you are working with. It looks like you’ve done this.
Call NVIC_SetPriority(irq_number, priority) and NVIC_EnableIRQ(irq_number) to allow the core to process interrupt(s) from the peripheral in question. Figuring out what (irq_number) to use can be a bit difficult; I usually refer the the (startup_stm32xxxx.s) asm source file for this, and the CMSIS header file(s) for the specific target in use to find it. This step may already be getting done somewhere (maybe inside the HAL code) since you stated that you were able to get one character match interrupt.
You need to create a function that will serve as the interrupt handler (ISR) that is appropriately named; you can get some idea of what the name should be by looking at the interrupt vector table declaration in the system_stm32xxxx.s file. The asm .s file declares ‘weak’ stubs all of the STM32’s interrupts; you need to create a function with the same name in your own source to override (replace) the weak definition in the startup code.
Now, what I’d do next - and not sure how much the HAL does for you in this regard - would be to set the character match interrupt enable bit, and inside the ISR, after recognizing that a CM interrupt, I’d turn on the receive-not-empty interrupt enable, and process all data coming in after the match character until end-of-message (whatever that may be) is detected, then turn off the RXNE interrupt and set CME.
Another general rule of thumb for dealing with interrupts with the STM32: All of the peripherals I’ve worked with (which is quite a few, but certainly not all) usually have some sort of interrupt identification or interrupt flag register associated with them - this is the register you look at to determine which peripheral event(s) caused the interrupt to occur. It is absolutely necessary to get the flag(s) associated with the event(s) that are interrupt-enabled cleared before exiting the ISR. Conditions for clearing these flag(s) will vary. Oftentimes (but certainly not always), the flag is cleared by writing 1’s to the interrupt status register or the interrupt clear register (USARTx->ICR = USART_ICR_CMF in this specific case). You’ll have to look at the reference manual for specifics. Failing to clear the interrupt flag will result in the ISR being re-entered immediately after ISR exit.