Security/Reviews/Gaia/Voicemail

From MozillaWiki
< Security‎ | Reviews‎ | Gaia
Jump to: navigation, search

REVIEW IN PROGRESS

High Level Architecture

Infrastructure

Voicemail number on the SIM Card

The SIM optionally contains the voicemail number and display name. This is usually preprogrammed by the telco. In the code this is called the ICC MBDN, the Integrated Card Circuit MailBox Display Number.

Voicemail messages from the Mobile Network

When someone leaves you a voicemail message, the mobile network will send a special SMS message to the phone with the Message Waiting Indicator bit set. (Can these messages also be invisible? Ie, they only trigger the voicemail notification handler but do not show up as a an actual text message?)

Voicemail WebAPI

To support voicemail in end-user (certified) applications, we have introduced a navigator.mozVoicemail API. This API is very basic and gives read-only access to the voicemail number and the last voicemail notification that was received through the mobile network. The mozVoicemail instance also generates onStatusChanged events, which are emitted when a new voicemail notification is received from the network.

See http://dxr.mozilla.org/mozilla-central/source/dom/voicemail/nsIDOMMozVoicemail.idl

RIL Daemon

The RIL Daemon (Radio Interface Layer) is the first to receive a voicemail notification message from the mobile network. The RIL forwards these messages to it's listeners. One of those listeners is Chrome, which runs the ril_worker.js.

Chrome

The ril_worker.js in Chrome listens to incoming RIL messages. Those messages also include incoming SMS (and thus Voicemail notifications). It is where they are parsed and then pushed through several other Chrome components to eventually end up in Gaia.

Message Flow from Network to Content

RIL Daemon -> Chrome RIL

As explained earlier, Voicemail related status messages arrive from the network through the RIL as a special type of SMS message.

For GSM, the (JavaScript) RIL handles these messages in RIL[UNSOLICITED_RESPONSE_NEW_SMS] where it will call GsmPduHelper.processReceivedSms which will decode the incoming message into a high level JavaScript object.

For CDMA, the (JavaScript) RIL handles these messages in RIL[UNSOLICITED_RESPONSE_CDMA_NEW_SMS] where it will call CdmaPDUHelper.processReceivedSms() which will decode the incoming message into a high level JavaScript object.

For GSM the relevant parsing code can be found in GsmPDUHelper.readProtocolIndicator, GsmPDUHelper.readDataCodingScheme and GsmPDUHelper.readUserDataHeader.

For CDMA the relevant code can be found in CdmaPDUHelper. readMessage and CdmaPDUHelper.decodeUserDataHeader.

All the functions mentioned above deal with parsing MWI data from incoming messages, which stands for Message Waiting Indicator.

When the (JavaScript) RIL has successfully parsed an incoming message, it calls RIL._processSmsMultipart, (It always calls *Multipart even if the SMS is single part), which will eventually call sendChromeMessage() with a message.rilMessageType of "sms-received" and the message data set to the received and parsed SMS which will include the MWI data.

The RIL.sendChromeMessage() is basically a wrapper for postMessage() so it is a way for the RIL code to simply pass the message on to whatever next party is interested.

At this point, the message has been taken from the RIL Daemon as a raw encoded message, parsed and then broadcast to interested components in Chrome as a high level JavaScript object through postMessage.

Chrome RIL -> Chrome WorkerMessenger

When the RIL has parsed an incoming SMS message, it uses postEvent() to broadcast it with message.rilMessageType set to "sms-received".

The WorkerMessenger is one of the components that has registered itself as a 'RIL Worker' to receive postMessage()s from the RIL. It catches messages in it's WorkerMessenger.onmessage message handler.

(The WorkerMessenger is wrapped by a ChromeWorker which is then registered with gSystemWorkerManager.registerRilWorker - The gSystemWorkerManager is an instance of nsISystemWorkerManager, which is implemented by SystemWorkerManager which also also the RegisterRilWorker implementation.)

Chrome WorkerMessenger -> Chrome RadioInterfaceLayer

The WorkerMessenger then looks at the type of the incoming message and in case of an SMS message it passes it on to RadioInterfaceLayer.handleUnsolicitedWorkerMessage (unsolicited really means 'incoming' here). There it is is finally handled by RadioInterfaceLayer.handleSmsReceived.

Chrome RadioInterfaceLayer -> Content

In RadioIntefaceLayer.handleSmsReceived, the incoming SMS message is looked at. If the message has MWI data set then the gMessageManager.sendVoicemailMessage is called, which sends a message to all content processes that have registered to receive voicemail messages.

The message is sent to the content processes in gMessageManager._sendTargetMessage which loops over all registered targets (which are instances of nsIMessageListener) and calls sendAsyncMessage on them.

At this point the message has been delivered to interested content processes.

(The content process registered itself by calling RegisterVoicemailMsg() in the Voicemail constructor).

Message flows from Content to Chrome

The RadioInterfaceLayer code in Chrome listens to the RIL:RegisterVoicemailMsg from Content.

This message is handled in gMessageManager.receiveMessage() where first is checked if the caller has the right permission ("voicemail") for voicemail related messages and then the caller is added to a list of targets for the voicemail topic.

Voicemail WebAPI

Calls to navigator.mozVoicemail() are handled by Navigator::GetMozVoicemail() which implements nsIDOMVoicemail.

The first time this API is used, a Voicemail instance is created through NS_NewVoicemail and made available through navigator.mozVoicemail. Although this sounds like an object that would represent a Voicemail, it really is just the Voicemail API and interface to other parts that deal with Voicemail messages.

The Voicemail API is really basic. It has some attributes and it can emit onStatusChanged events.

It allows you to retrieve the following three attributes:

  • status - the current nsIDOMMozVoicemailStatus which contains the last received voicemail status (you have new voicemail, 3 messages, you can call 123 to listen to them)
  • number - the voicemail number stored in the SIM card
  • displayName - the voicemail number name stored in the SIM card

The Voicemail also has a provider and listeners.

The provider is the RILContentHelper which is really just proxying calls from Content to the RadioInterfaceLayer in Chrome where the actual implementation lives.

The listeners objects living in the Content process that are interested in receiving mozVoicemail onStatuschanged events.

The message flow that triggers the onStatusChanged event is such that when the RadioInterfaceLayer receives a voicemail status message, it will call gMessageManager.sendVoicemailMessage which then delivers that event to all (Content) listeners. The Voicemail instance registered for this event. Each Voicemail instance is a VoicemailListener and such it's Voicemail:NotifyStatusChanged will be called, which then dispatches an event to the listeners in the content process. One of those (content) listeners is the system app, which uses the event to show a user notification.

User Interface

Voicemail is integrated in the following parts of Gaia

  • The System contains a listener for mozVoicemail's statuschanged message, which is used to trigger on incoming voicemail notifications.
  • The Dialer (in the communications app) has a voicemail button that starts a call to the configured voicemail service.
  • The Settings can be used to see and configure the voicemail number
  • The Emergency Call (in the system app) has a button to make a voicemail call

System

The system is the main entry point for incoming voicemail notification messages.

All code is https://github.com/mozilla-b2g/gaia/blob/master/apps/system/js/voicemail.js

It registers for these messages in init() and handles them in handleEvent(). When it receives an incoming Voicemail status message it uses the NotificationScreen API to add a new notification to the notification tray.

If the incoming voicemail status message indicates that no more voicemail messages are available, the user notification is removed from the tray.

If the network provided a message for the voicemail status change, that is displayed in the user notification. Otherwise a Gaia provided message is displayed, which also contains the number of messages available if provided.

The text of the user notification is set to 'Call $NUMBER' if we know the number to dial for the voicemail service. Otherwise it is left empty.

When you tap on the user notification, a call to mozTelephony.dial() is triggered to dial the voicemail number.

Dialer

The dialer is aware of Voicemail in two ways:

It shows a Voicemail button on the keypad, which will dial the voicemail number.

When it recognizes a number in the call log as the Voicemail number, it will not show the actual number but instead the string 'Voicemail'.

Settings

The settings app will show the configured voicemail number and will also allow you to change it.

Emergency Call

The Emergency Call screen also has a dialer pad with a Voicemail button. This also simply triggers a call to mozTelephony.dial().

User Interaction

Users interact with Voicemail in the following ways:

  • Users can tap on user notifications to start a call to the voicemail service
  • Users can tap on the Voicemail button on the dial pad in the dialer or the emergency dialer
  • Users can see and change the voicemail number in the phone settings

The first two actions are simple and do not allow for any user provided input.

The voicemail number in the settings can be changed by the user.

Threat Discussion

Malformed SIM data

Threat When the phone needs the voicemail phone number, it first tries to get it from the SIM card. Is it possible to change that data on the SIM (on another device maybe) and to put malicious payload in it?

Analysis It is not clear wether this number can be changed by a user with a SIM programmer or another phone.

Theat Is the data taken from the SIM card properly validated before used and sanitized before used?

Analysis No, the number is taken as-is as offered by the SIM card. However, anywhere where this number is used, it is properly escaped before being displayed or used in content. But, for completeness, it might be worth it to apply a strict 'phone number' filter on it when it is loaded from the SIM card

Malformed network data

Threat The voicemail status messages are parsed from an incoming SMS message. Can sending a malformed packet crash, confuse or hang the SMS parser?

Analysis We have fuzzing tests (peach pits) for the message parser. We can probably improve these by making more SMS specific tests.

Threat Can voicemail status messages be spoofed? It is easy to spoof and send SMS text messages to phones through many available services. Do those services also allow to send non-text messages? (Might be outside the scope of this review, although it means that we cannot trust that data and we need to do strict validation on it)

Analysis Very likely but outside of the scope of this review. There is likely also nothing that can be done about this on the client side.

Threat The voicemail status message contains an optional displayable text string to be shown to the user. This string is provided by the telco. Is it possible to put HTML in this message to be executed when the message is displayed?

Analysis The text is displayed in the notification is ultimately put in the content by NotificationScreen.addNotification which uses textContent for both title and message. So even if injected content would be put in the voicemail message, it would show up escaped in the content.

Bad User Input

Threat In the settings app the user can provide an (alternative) voicemail number. Is the number properly validated and/or can it be used to trigger malicious actions?

Analysis The input field in the settings has a type="tel" attribute. This means that the input is validated and should only accept things that look like phone numbers. See apps/settings/index.html