Inhaltsverzeichnis

Nötige Änderungen innerhalb des User Based Security Models

Leider sind die Implementierungen für die 64Bit Integer Variable zur Generierung des IV und für die Sicherheitsvariable msgAuthoritativeEngineBoots nicht mit den zugehörigen RFCs konform. Außerdem fehlt in der original Contiki SNMP Implementierung die Überprüfung des Benutzersicherheitslevels und die Überprüfung der Variablen msgAuthoritativeEngineTime wurde nicht korrekt umgesetzt.

64Bit Integer Variable zur Generierung des IV

Laut RFC3826 muss die 64 Bit Integer Variable beim Bootvorgang pseudo zufällig generiert werden. In der momentanen Implementierung ist diese Variable durch zwei 32 Bit Integer u32t privacyLow und u32t privacyHigh in der Datei snmpd-conf.c mit festen Werten definiert. Um für diese Variablen nun zufällige Werte beim Bootvorgang zu generieren, wird die Funktion random() der AVR-GCC Library genutzt. Diese wird zuerst mit einem Startwert (Seed) durch die Funktion srandom() gesetzt. Zur Generierung eines Startwerts wird die Funktion u32t get_seed() genutzt. Die Funktion u32t get_seed(), generiert aus dem Datenspeicher, genauer gesagt aus dem SRAM Bereich des Datenspeichers in dem sich der Heap und der Stack befindet, einen 32 Bit Anfangswert. Da dies beim Start geschieht, ist der Inhalt des SRAMs zufällig. Der Beginn des Heap Speichers wird über die Variable heap_start, welche im zugehörigen Linker Skript definiert ist, markiert. Das Ende des SRAMS wird über das Makro RAMEND (avr/io.h) gekennzeichnet. Es wird nun der gesamte Speicherbereich zwischen heap_start und RAMEND stückweise immer wieder mit einer XOR Verknüpfung durchwandert und der Variable seed zugewiesen. Die Funktion random() wird nun nacheinander den beiden Variablen u32t privacyLow und u32t privacyHigh zugewiesen, wodurch diese nun pseudozufällig sind.

→Die Variablen sowie die dazugehörigen get-Funktionen wurden von der Datei snmpd-conf.c bzw. snmpd-conf.h in die Dateien snmpd.c bzw. snmpd.h verschoben, da diese beim Programmstart generiert werden müssen.

Funktion: get_seed():

u32t get_seed ( )
{
u32t seed = 0 ;
u32t p = ( u32t ) (RAMEND+1) ;
extern u32t __heap_start ;
#if PDEBUG
printf ( "RAMEND:  %X\n__heap_start :  %X" ,RAMEND, &__heap_start );
#endif
while (p >= &__heap_start + 1)
seed ^=  *(--p);
return seed;
}

→ Der vollständige Quellcode mit allen Änderungen, kann am Ende dieser Seite heruntergeladen werden.

msgAuthoritativeEngineBoots

Die Variable msgAuthoritativeEngineBoots muss laut RFC3414 innerhalb einer nicht flüchtigen Variable gespeichert werden und enthält die Anzahl der Bootvorgänge seit der Installation des SNMP Agenten. Sie wird innerhalb des USM msgAuthoritativeEngineBoots zur Generierung des Initialisierungsvektors sowie innerhalb des Timliness Moduls verwendet. In der ursprünglichen Implementierung von Contiki SNMP wird diese Variable einfach immer mit dem Wert Null verwendet. Dies stellt natürlich ein erhebliches Sicherheitsrisko dar. Es wird deshalb eine 32Bit unsigned Integer Variable innerhalb des EEPROM, also im nicht flüchtigen Speicher, des ATmega1281 angelegt. Um auf den EEPROM zuzugreifen, muss die Datei <avr/eeprom.h> mit einbezogen werden. Damit nun bei jedem Neustart des SNMP Agenten msgAuthoritativeEngineBoots inkrementiert wird, kommt die Funktion incMsgAuthoritativeEngineBoots(), siehe folgenden Quellcode, zum Einsatz.

u8t incMsgAuthoritativeEngineBoots()
{
	/*Increments the Value of MsgAuthoritativeEngineBoots when booting.*/
	/*Checks if the maximum value of 2147483647 (RFC3414) is reached.*/
	if((eeprom_read_dword(&MsgAuthoritativeEngineBoots))<2147483647)
	{
		eeprom_update_dword(&MsgAuthoritativeEngineBoots, (eeprom_read_dword(&MsgAuthoritativeEngineBoots)+1));
	}
	else
	{
#if PDEBUG
		printf("Maximum Number of MsgAuthoritativeEngineBoots reached, please reconfigure all secret values and reinstall SNMP Agent\n");
#endif
	}
#if PDEBUG
	printf("MsgAuthoritativeEngineBoots = %lu\n",eeprom_read_dword(&MsgAuthoritativeEngineBoots));
#endif
 
	return 0;
}

Sie wird beim Starten des snmpd_process in der Datei snmpd.c aufgerufen und erhöht die Variable um Eins. Außerdem wurde die Ausgabe der Funktion getSysUpTime(), welche die Zeit für die Variable snmpEngineTime liefert, wie in RFC3414 festgelegt, auf einen maximalen Wert von 2147482647 begrenzt. Ist der Wert erreicht, so wird die Variable seconds (Zeit in Sekunden seit Systemstart) auf Null gesetzt und msgAuthoritativeEngineBoots über die Funktion incMsgAuthoritativeEngineBoots() um Eins erhöht. Des Weiteren fordert RFC3414 bei Erreichen eines Wertes von 2147483647 für msgAuthoritativeEngineBoots eine Sperrung des Zugriffs auf den SNMP Agenten. Bei einem Zugriff muss als Fehler eine notInTimeWindow Fehlermeldung zurückgesendet werden. Dies wurde innerhalb der Datei usm.c realisiert.

→ Der vollständige Quellcode mit allen Änderungen, kann am Ende dieser Seite heruntergeladen werden.

Überprüfung des Benutzersicherheitslevels

Laut RFC3414 muss vor dem Verarbeiten einer eingehenden SNMP Nachricht überprüft werden ob das Sicherheitslevel der eingehenden Nachricht mit dem Sicherheitslevel des dazugehörigen Benutzernamens übereinstimmt. Ist dies nicht der Fall, so soll die Nachricht verworfen werden und eine Meldung mit dem Inhalt unsupportedSecurityLevel an den Sender zurückgesendet werden. Die original Contiki SNMP Implementierung erfüllt diese Bedingung nicht. Das USM Sicherheitsmodul antwortet immer mit dem Sicherheitslevel, das in der eingehenden Nachricht angegeben ist. Lediglich der Benutzername wird überprüft. Um dieses Problem zu lösen, wurden einige kleine Änderung innerhalb der Datei usm.c vorgenommen. Zuerst musste ein Objekt für die Fehlermeldung unsupportedSecurityLevel angelegt werden.

/** \brief The total number of packets received by the SNMP
 *         engine which were dropped because they got an
 *         unsupportedSecurityLevel for the user specified
 *		   in snmpd-conf.c.
 */
u8t usmStatsUnsupportedSecurityLevel_array[] = {0x2b, 0x06, 0x01, 0x06, 0x03, 0x0f, 0x01, 0x01, 0x01};
ptr_t usmStatsUnsupportedSecurityLevel = {usmStatsUnsupportedSecurityLevel_array, 9};
u32t usmStatsUnsupportedSecurityLevelCounter;

Nach diesem Schritt wird innerhalb der Funktion zur Bearbeitung eingehender Nachrichten vor dem Aufruf des Privacy bzw. des Authentication Moduls eine Überprüfung des Sicherheitslevels durchgeführt, sofern dieses innerhalb des Agenten aktiviert wurde.

#if ENABLE_AUTH
	if (!(request->msgFlags & FLAG_AUTH))
	{
	#if PDEBUG
		printf("USM Modul: Error! User needs Authentication\n");
	#endif
		TRY(report(request, &usmStatsUnsupportedSecurityLevel, &usmStatsUnsupportedSecurityLevelCounter));
        return ERR_USM;
	}
#endif

Entspricht das Sicherheitslevel der Nachricht nicht dem des dazugehörigen Benutzernamens, so wird mithilfe der Funktion report() eine Antwortnachricht mit dem Fehlercode unsupportedSecurityLevel an den Absender geschickt. Für das Privacy Modul wurde diese Funktion nach dem gleichen Prinzip umgesetzt.

→ Der vollständige Quellcode mit allen Änderungen, kann am Ende dieser Seite heruntergeladen werden.

Korrektur zur Überprüfung der msgAuthoritativeEngineTime des Timliness Moduls

Innerhalb des USM Moduls befindet sich das Timeliness Modul, dieses dient zur Überprüfung der Authentizität der Nachricht. Dazu muss laut RFC3414 die ankommende msgAuthoritativeEngineTime mit der lokalen AuthoritativeEngineTime des Agenten verglichen werden. Der Unterschied zwischen beiden Zeiten darf 150 Sekunden nicht überschreiten. Dies wurde im original Contiki SNMP mit der folgenden if-Abfrage umgesetzt.

if(request->msgAuthor i tat iveEngineBoots != getMsgAuthoritativeEngineBoots()|| abs(request->msgAuthoritativeEngineTime - getSysUpTime() ) < TIME_WINDOW) {
            TRY( report ( request, &usmStatsNotInTimeWindows, &usmStatsNotInTimeWindowsCounter ) ) ;
            return ERR_USM;
}

Es wird also eine Absolutwertbildung der Subtraktion der msgAuthoritativeEngineTime der ankommenden Nachricht und der Funktion getSysUpTime() durchgeführt. Der Wert der Variablen msgAuthoritativeEngineTime der eingehenden Nachricht ist in Sekunden angegeben. Und die Funktion getSysUpTime() liefert die Zeit seit dem letzten Systemstart in Millisekunden zurück. Dies führt natürlich grundsätzlich zu einem Problem, sodass dieser Wert scheinbar immer größer als 150 Sekunden ist. Dies hat zur Folge, dass die Überprüfung innerhalb der if-Abfrage also immer null ist. Es wird also immer angenommen dass der Wert von msgAuthoritativeEngineTime der eingehenden Nachricht innerhalb des Zeitfensters liegt. Dies wurde durch die folgende Quellcodeänderung korrigiert.

if (request->msgAuthoritativeEngineBoots != getMsgAuthoritativeEngineBoots() || abs(request->msgAuthoritativeEngineTime -(getSysUpTime()/100)) > TIME_WINDOW)

Möglichkeit zur Versendung authentifizierter Reports (RFC konformer Discovery Prozess)

Da laut RFC3414 muss innerhalb des Discovery Vorgangs der zweite Report authentifiziert sein. Innerhalb der Funktion s8t report(message_v3_t* request, ptr_t* oid, u32t* counter) muss hierzu eine Möglichkeit geschaffen werden.

In der originalen Implementierung ist die Versendung authentifizierter Reports nicht vorgesehen. Je nach verwendeten SNMP Manager kann dies jedoch dazu führen dass kein Zugriff auf den Agenten möglich ist. Die meisten Manager kürzen den Discovery Prozess ab und entnehmen die Werte für msgAuthoritativeEngineBoots, sowie msgAuthoritativeEngineTime dem ersten nicht authentifizierten Report, welcher jedoch nach RFC ausschließlich zur Synchronisierung der msgAuthoritativeEngineID bestimmt ist. Hält sich eine SNMPv3 Manager Implementierung strikt an den vorgegebenen Ablauf aus RFC3414, so ist wie oben beschrieben kein Zugriff auf den Agenten möglich. Um dieses Problem zu beheben, genügt die folgende Änderung innerhalb der Datei usm.c:

static s8t report(message_v3_t* request, ptr_t* oid, u32t* counter) {
    (*counter)++;
    if (!(request->msgFlags & FLAG_REPORTABLE)) {
        /* if the reportable flag is not set, then don't send a Report PDU */
        return FAILURE;
    }
    // release variable bindings from the PDU
    free_varbinds(&request->pdu);
	printf("Sending Report....\n");
 
 
    //request->msgFlags = 0;
 
    /*sz*/
    /*Added this lines to perform authentication for reports which need it*/
    /*Especially for the SNMP Discovery Process, see RFC3414, Sec.4*/
    if(request->msgFlags & FLAG_AUTH)
    {
		request->msgFlags = FLAG_AUTH;
		printf("Report set to Auth\n");
	}
    /*sz*/
 
    request->pdu.response_type = BER_TYPE_SNMP_REPORT;
 
    request->pdu.varbind_first_ptr = varbind_list_append(0);
    if (!request->pdu.varbind_first_ptr) {
        return FAILURE;
    }
    request->pdu.varbind_first_ptr->varbind.oid_ptr = oid;
    request->pdu.varbind_first_ptr->varbind.value_type = BER_TYPE_COUNTER;
    request->pdu.varbind_first_ptr->varbind.value.u_value = *counter;
    return 0;
}

Download

Hier kann der komplette Quellcode des geänderten Contiki SNMP heruntergeladen werden: snmpd13.zip