====== 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 [[https://tools.ietf.org/html/rfc3826|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 [[https://tools.ietf.org/html/rfc3414|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 '''' 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 [[https://tools.ietf.org/html/rfc3414|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 [[https://tools.ietf.org/html/rfc3414|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 [[https://tools.ietf.org/html/rfc3414|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 [[https://tools.ietf.org/html/rfc3414|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:
{{:contiki:snmpd13.zip|}}