Open Source im professionellen Einsatz

Null-Zeiger-Fehler in Asterisks SIP-Code

Asterisk macht es möglich, eine umfangreiche Telefonanlage mit Sprachdiensten, Verzeichnisdiensten und Telefonkonferenzen auf einem Rechner zu betreiben. Die Software unterstützt zahlreiche VoIP-Protokolle, beispielsweise InterAsterisk eXchange (IAX), das Session Initiation Protocol (SIP) und H.323. In der Implementierung des SIP-Protokolls ist nun eine Sicherheitslücke entdeckt worden.

Der verantwortliche Programmierfehler befindet sich in der Datei "channels/sip/reqresp_parser.c". Die dortige Funktion "parse_uri_full()" ist dafür verantwortlich, einen URI in ihre Bestandteile zu zerlegen. Wie so oft sind solche Funktionen, die das Parsing von Benutzereingaben übernehmen, sehr anfällig für Sicherheitslecks, da der Benutzer einen beliebigen URI übergeben kann. Fängt die Software nicht alle Eventualitäten ab, kann das zu schwerwiegenden Problemen führen.

Die alte, fehlerhafte Version dieser Funktion sah so aus:

/*! \brief * parses a URI in its components.*/
int parse_uri_full(char *uri, const char *scheme, char **user, char **pass,
     char **domain, struct uriparams *params, char **headers,
     char **residue)
{
 char *userinfo = NULL;
 char *parameters = NULL;
 char *endparams = NULL;
 char *c = NULL;
 int error = 0;

 /* check for valid input */
 if (ast_strlen_zero(uri)) {
  return -1;
 }

...
}

Die User-, Pass-, Domain-, Headers- und Residue-Variablen werden von "parse_uri_full()" im weiteren Verlauf (wie auch die anderen Pass-by-Reference-Variablen) gesetzt. Dies geschieht allerdings nur dann, wenn keine Schwierigkeiten beim Verarbeiten des URI auftreten. Sobald es ein Problem mit dem Parsen gibt, liefert "parse_uri_full()" einen Fehlercode zurück, beispielsweise -1, wenn die Stringlänge 0 beträgt, siehe Codefragment).

Die Programmlogik gebietet also, dass "user", "pass", "domain", "headers" und "residue" nur dann nach einem "parse_uri_full()"-Aufruf vom aufrufenden Code verwendet werden dürfen, wenn die Funktion fehlerfrei ablief. Und hier liegt das Problem: Teile von Asterisk halten sich nicht daran, und sie verwenden einige dieser Variablen trotzdem. Dadurch kann es vorkommen, dass beispielsweise "user" auf "NULL" gesetzt ist, wenn "parse_uri_full()" zum Aufrufer zurückkehrt. Das ist kritisch, denn wird nun eine Operation auf "user" angewandt, kommt es unweigerlich zu einem typischen NULL-Zeigerfehler und das Programm stürzt ab.

Die einfachste Behebung dieses Problems besteht darin, einfach sicherzustellen, dass die fraglichen Variablen immer gesetzt werden. Die gepatchte und aktuelle Version des Codes sieht so aus:

/*! \brief * parses a URI in its components.*/
int parse_uri_full(char *uri, const char *scheme, char **user, char **pass,
     char **domain, struct uriparams *params, char **headers,
     char **residue)
{
 char *userinfo = NULL;
 char *parameters = NULL;
 char *endparams = NULL;
 char *c = NULL;
 int error = 0;

 /*
  * Initialize requested strings - some functions don't care if parse_uri fails
  * and will attempt to use string pointers passed into parse_uri even after a
  * parse_uri failure
  */
 if (user) {
  *user = "";
 }
 if (pass) {
  *pass = "";
 }
 if (domain) {
  *domain = "";
 }
 if (headers) {
  *headers = "";
 }
 if (residue) {
  *residue = "";
 }

 /* check for valid input */
 if (ast_strlen_zero(uri)) {
  return -1;
 }

Der Kommentar im Code erklärt noch einmal, warum der Fix eingeführt wurde: Die If-Abfragen müssen sein, da einige Aufrufe von "parse_uri_full()" auch nach negativem Ergebnis versuchen, die Zeiger auf die Variablen zu verwenden.

Betroffen sind die Asterisk-Versionen 1.8.x vor 1.8.4.2.

comments powered by Disqus

Stellenmarkt

Artikelserien und interessante Workshops aus dem Magazin können Sie hier als Bundle erwerben.