Autor Thema: Allgemeiner Ressourcen-Allocator  (Gelesen 86693 mal)

FlashBurn

  • Beiträge: 844
    • Profil anzeigen
Gespeichert
« Antwort #120 am: 21. November 2010, 11:16 »
Zitat von: erik
Der Kernel kann die Länge der Daten prüfen und auch ob die Pointer gültig sind.
Was genau meinst du mit die Länge der Daten? Bei mir ist die Länge festgelegt, muss also nicht überprüft werden und bei dir musst du halt soviel Daten "kopieren" wie halt gesendet werden sollen (willst du da eigentlich ne Obergrenze setzen?).
Was meinste du mit Gültigkeit der Pointer?

Man muss auf jeden Fall prüfen ob der Speicher der gesendet werden darf, überhaupt existiert und ob er vom User gelesen werden darf.

Da ist dann auch gleich ein Problem, worüber ich mir die letzten Tage den Kopf zerbrochen habe und keine vernünftige Lösung gefunden habe.

Nehmen wir mal an, das Programm besteht aus 2 Threads und jeder Thread läuft auf einer anderen CPU (und keine Segmente ;) ).
Der eine Thread will eine Nachricht versenden (oder irgendetwas anderes, was es erfordert das der Kernel auf Speicher im UserSpace zugreifen muss). Der Kernel wird also, bevor er irgendwelche Daten aus dem UserSpace liest, überprüfen ob der User überhaupt den Speicher lesen darf und ob dieser vorhanden/gemappt ist.
Die Überprüfung ist abgeschlossen und der Kernel liest seine Daten. In der Zwischenzeit will der andere Thread Speicher freigeben und zwar genau den Speicher den der Kernel zugreifen will.

Worauf ich hinaus will, wie verhindert man das ein Thread Speicher freigeben kann, wo der Kernel die Überprüfung schon abgeschlossen hat und auf diesen Speicher jetzt zurgreift?

erik.vikinger

  • Beiträge: 1 277
    • Profil anzeigen
Gespeichert
« Antwort #121 am: 21. November 2010, 13:11 »
Hallo,


Was genau meinst du mit die Länge der Daten? Bei mir ist die Länge festgelegt, muss also nicht überprüft werden
Okay, ich dachte Du übergibst die Länge der Daten als Parameter an den Syscall, ich bin davon aus gegangen das die Länge der Daten beim erstellen Deiner Ports festgelegt werden kann also jeder Port mit einer individuell festgelegten Message-Größe arbeitet, aber wenn es da ein globales Define gibt das immer und für alles gültig ist ist das natürlich überflüssig.

und bei dir musst du halt soviel Daten "kopieren" wie halt gesendet werden sollen (willst du da eigentlich ne Obergrenze setzen?).
Ich kopiere nicht, ich erstelle ein Alias-Segment im Prozess des Service. Beim erstellen von meinen Message-Targets darf der Service festlegen wie groß die Messages maximal sein dürfen die er empfangen möchte, getrennt für Kommando/Antwort und die Nutzdaten. Also der VFS kann sagen das die Kommandos die er empfangen möchte (und damit auch die Antworten die er zurückschicken kann) maximal z.B. 4kByte groß sind und für die Nutzdaten (die mit einem einzelnen read/write-Kommando übertragen werden) darf er z.B. 256MByte als Maximum angeben. Diese Maxima sind unabhängig vom Aligment, werden also auch bei unausgerichteten Daten exakt eingehalten.

Was meinste du mit Gültigkeit der Pointer?
Man muss auf jeden Fall prüfen ob der Speicher der gesendet werden darf, überhaupt existiert und ob er vom User gelesen werden darf.
Das wären gute Vorschläge. Ansonsten wäre noch zu nennen das der Speicher nicht ausführbar sein sollte. Bei meinen Segmenten hab ich es da recht einfach ich muss einfach prüfen ob der Pointer im Segment gültig ist und das Ende der Daten nicht über das Segment-Limit hinaus geht. Zusätzlich werde ich IPC das sich auf Code-Segmente bezieht verbieten.

....
Worauf ich hinaus will, wie verhindert man das ein Thread Speicher freigeben kann, wo der Kernel die Überprüfung schon abgeschlossen hat und auf diesen Speicher jetzt zurgreift?
Hm, da fällt mir jetzt auch nichts so spontan ein außer das der Kernel die Verwaltungsstruckturen für den Prozess immer locken muss wenn er daraus lesen oder darin modifizieren will. Ansonsten hat ein Micro-Kernel eigentlich nichts in den User-Daten verloren.


Ich meine, ich würde nen Message-Parser als switch-Verzweigung bauen und die anderen Paramter sind dann Parameter für die aufzurufenden Funktionen (mal sehr vereinfacht gesagt), da kann dem Parser erstmal nichts passieren.
So wollte ich das auch machen und kann da eigentlich auch erstmal keine grundlegenden Schwächen erkennen.


Grüße
Erik
Reality is that which, when you stop believing in it, doesn't go away.

FlashBurn

  • Beiträge: 844
    • Profil anzeigen
Gespeichert
« Antwort #122 am: 21. November 2010, 13:40 »
Zitat von: erik
Okay, ich dachte Du übergibst die Länge der Daten als Parameter an den Syscall, ich bin davon aus gegangen das die Länge der Daten beim erstellen Deiner Ports festgelegt werden kann also jeder Port mit einer individuell festgelegten Message-Größe arbeitet, aber wenn es da ein globales Define gibt das immer und für alles gültig ist ist das natürlich überflüssig.
Das mit der Größe pro Port ist natürlich auch eine Möglichkeit, aber selbst da brauche ich dann keinen Parameter für die Länger vom User.

Zitat von: erik
Ich kopiere nicht
Genau deswegen habe ich ja "kopieren" geschrieben ;)

Zitat von: erik
Hm, da fällt mir jetzt auch nichts so spontan ein außer das der Kernel die Verwaltungsstruckturen für den Prozess immer locken muss wenn er daraus lesen oder darin modifizieren will.
Das ist eigentlich die Idee! Ich habe ja noch ein "paar" freie Bits bei jeder Page zur Verfügung und die könnte man dafür nutzen.

Hat den "Nachteil" (mache ich eh oft) das ich die Ints während des Kopierens ausschalten muss, damit das ganze auch auf Single CPU Systemen funktioniert und andere CPUs nicht alzu lange warten müssen.

erik.vikinger

  • Beiträge: 1 277
    • Profil anzeigen
Gespeichert
« Antwort #123 am: 21. November 2010, 17:34 »
Hallo,


Zitat von: erik
Ich kopiere nicht
Genau deswegen habe ich ja "kopieren" geschrieben ;)
Aha, Du willst also ein bisschen stänkern oder Dich gar mit mir anlegen, ja? Wollen wir noch mal darüber diskutieren warum Dein IPC-Konzept kein Zero-Copy erlaubt und meines schon? :-D

Hat den "Nachteil" (mache ich eh oft) das ich die Ints während des Kopierens ausschalten muss
Möchtest Du vielleicht doch noch mal über einen nicht unterbrechbaren Kernel nachdenken? Wir können auch darüber gerne noch mal ausführlich diskutieren. ;)


@svenska:
Ich hab übrigens keinen richtigen Grund gefunden warum OpenSSL den VIA-RNG abgelehnt hat, außer der Aussage das es eher Aufgabe des Kernels ist die Hardware-Komponenten zu unterstützen und das man sich als User-Space-Applikation lieber auf die öffentlichen Interfaces (wie /dev/random) verlassen möchte.


Grüße
Erik
Reality is that which, when you stop believing in it, doesn't go away.

Svenska

  • Beiträge: 1 792
    • Profil anzeigen
Gespeichert
« Antwort #124 am: 22. November 2010, 13:04 »
Hallo,

Mit dem Nullbyte würdest du aus Angriffen NULL-Pointer-Angriffe machen, aber auch die hat man bisher erfolgreich ausgenutzt. (Darum werden im virtuellen Adressraum die ersten 4K (ARM) / 64K (x86) von Linux nicht genutzt, die BSDs machen das ähnlich.)
Ich fürchte auch da hast Du mich missverstanden, mit dem 0-Word als (zusätzliches) Magic-Cookie meinte ich das wenn man erwartet das ein 0-Wort auf dem Stack liegt das dann der Angreifer ein Problem hat weil er eben bei dem meisten Buffer-Overflows, die ja auf String-Funktionen beruhen, keine Chance hat weil die String-Funktionen ja eben ein 0-Byte als Abbruchkriterium nehmen.
Das meinte ich und finde es unschön... du verhinderst damit Angriffe durch strcpy() und Familie im Betriebssystem. Ist schön und gut, versteckt aber Bugs in den Anwendungen, die man lieber fixen sollte. Darum sollte zu Debugzwecken, also wenn man Fehler sucht, das Programm schön brav mit Anlauf auf die Schnauze fallen, damit der Fehler auffällt und behoben werden kann. Das Nullbyte versteckt den Fehler ja nur.

Mir wäre es am liebsten, wenn jeder Bug in einer Anwendung direkt zu einem Absturz führt und sich niemals irgendwo verschleiern lässt. Ist leider unmöglich.

Das kann ich mir einfach nicht vorstellen, wenn Du nicht genau weißt wo Deine Daten im Stack liegen dürfte es schwer fallen diese richtig zu adressieren oder Du musst die "nützlichen" Code-Schnipsel noch weiter einschränken indem nur zum Stack-Pointer relative Adressierung nutzbar ist. Aber spätestens bei ALR für den Code ist dann Schluss weil Du ja korrekte absolute Rücksprungadressen brauchst.
Was ist ALR? Google sagt mir auf Anhieb nichts und Wikipedia auch nicht. Solange du hinreichend viele Code-Schnipsel hast (und das sind minimal eben nicht viele), kannst du ja auch solange einschränken, bis du deine reale Adresse rausbekommen hast und davon ausgehend dann den Angriff stricken. Konkret kann ich das nicht umsetzen, aber stelle mir da genug Phantasie vor...

Genau das würde ich nicht machen, wenn es um das Debugging geht. Damit werden Fehler im Programm durch das Betriebssystem entschärft und das Programm hat dann auf anderen Betriebssystemen gravierende Sicherheitslücken.
Hä, wovon schreibst Du da eigentlich? Was hat das OS damit zu tun? Das Erzeugen und Prüfen von diesen Magic-Cookies macht doch üblicherweise der vom Compiler generierte User-Code im Funktions-Prolog/Epilog. Dadurch kann ein Programm doch keine zusätzlichen Sicherheitslücken bekommen bzw. vorhandene überdeckt werden.[/quote]Dann sind es eben die Compiler-Routinen, die den Fehler überdecken. In jedem Fall führt das dann, wenn man die Umgebung ändert (und kein Nullbyte mehr im Magic Cookie drin ist), direkt zu Sicherheitslücken, die man auch vorher hätte beheben können/sollen/müssen. :-)

Code im Kernel zu überschreiben ist auf verschiedenen Wegen möglich. Man kann als "root" /dev/mem oder /dev/kmem überschreiben, deswegen führt man Anwendungen nur im Notfall so aus.
Also wenn der Admin sein Passwort eingibt um ein Programm laufen zu lassen dann helfen eh nicht mehr viele Sicherheitsmaßnahmen, egal ob die in Software oder in Hardware implementiert sind.
Du brauchst normalerweise Root-Zugriff, um TCP-Ports unter 1024 verwenden zu dürfen... du brauchst normalerweise Root-Zugriff, um ein vollständiges Backup (inkl. /etc/shadow) des Systems anfertigen zu können. Du brauchst (noch) Root-Zugriff, um die grafische Oberfläche (X-Server) ausführen zu können. Alle diese Programme sind hackbar und damit potentielle Überschreiber von /dev/mem.

Sicher können viele Programme das Socket erzeugen und dann ihre Rechte gezielt ablegen und sich runterstufen lassen, aber ich glaube, das geht nicht immer und grundsätzlich ist das eine nicht zu vergessende Problematik. Die kann man nicht mit "tja, root ist halt selbst schuld" abhandeln. Glaube ich.

Alle die Adressen die ein Treiber seiner HW mitteilt müssen ja in festen Pages liegen und das muss der Treiber immer explizit anfordern (und auch wieder freigeben) und genau darauf soll der Kernel dann die Peripherie-Zugriff beschränken, ob das überhaupt praktikabel ist weiß ich noch nicht aber wenn nicht dann soll es damit zumindest möglich sein das der Kernel seine eigenen Speicherbereiche für die Peripherie verbietet.
Du müsstest in dem Moment direkt vor den physischen Adressleitungen zum RAM ein paar Gatter setzen, die dem System solche Zugriffsüberschreitungen mitteilen. Ich glaube nicht, dass das praktikabel ist - theoretisch ginge es aber, wenn man die MMU aus dem Prozessor herausnimmt und physischen zwischen Prozessor/Peripherie und RAM ansetzt. Das wurde ja auch gemacht (z.B. Motorola 68010 auf Sun2-Maschinen).

In Software fällt das komplett aus und in Hardware umgesetzt, ist es viel zu langsam. Zumindest, wenn man Paging benutzen möchte. Segmentierung finde ich ja gut, aber ohne Paging geht es meiner Meinung nach nicht (auch du implementierst ein Paging, weil reine Segmentierung doof ist).

Wegen dem Fixed-Size-IPC: ich will Flexible-Size-IPC und sehe da auch keine zusätzlichen Probleme. Warum denkt ihr das IPC überhaupt ein guter Angriffspunkt ist?
Weil das IPC so wunderbar zentral im System verankert ist und auf einem Mikrokernel so ziemlich der einzige Zugang direkt in den Ring 0 ist.

oder indem man den Inhalt so strickt, dass der Message-Parser amok läuft oder Dinge tut, die man als Designer so nicht vorgesehen hat.
Dagegen sollte ein sauber programmierter Service einigermaßen immun sein.
Bugs, Bugs, Bugs. ;-)

Gruß,
Svenska

FlashBurn

  • Beiträge: 844
    • Profil anzeigen
Gespeichert
« Antwort #125 am: 22. November 2010, 13:16 »
Zitat von: svenska
Weil das IPC so wunderbar zentral im System verankert ist und auf einem Mikrokernel so ziemlich der einzige Zugang direkt in den Ring 0 ist.
Wo ich immernoch nicht weiß, wo das Problem liegt?

erik.vikinger

  • Beiträge: 1 277
    • Profil anzeigen
Gespeichert
« Antwort #126 am: 22. November 2010, 15:11 »
Hallo,


Mit dem Nullbyte würdest du aus Angriffen NULL-Pointer-Angriffe machen, aber auch die hat man bisher erfolgreich ausgenutzt. (Darum werden im virtuellen Adressraum die ersten 4K (ARM) / 64K (x86) von Linux nicht genutzt, die BSDs machen das ähnlich.)
Ich fürchte auch da hast Du mich missverstanden, mit dem 0-Word als (zusätzliches) Magic-Cookie meinte ich das wenn man erwartet das ein 0-Wort auf dem Stack liegt das dann der Angreifer ein Problem hat weil er eben bei dem meisten Buffer-Overflows, die ja auf String-Funktionen beruhen, keine Chance hat weil die String-Funktionen ja eben ein 0-Byte als Abbruchkriterium nehmen.
Das meinte ich und finde es unschön... du verhinderst damit Angriffe durch strcpy() und Familie im Betriebssystem.
Ich glaube wir schreiben da immer noch aneinander vorbei. Das (zusätzliche) 0-Word-Magic-Cookie wird doch nicht im Betriebssystem gemacht sondern es wird von Code (den der Compiler extra einfügt) generiert und geprüft, so wie alle anderen Cookies auch, da gibt es absolut keine Abhängigkeit vom OS oder der benutzten CPU-Architektur. Die Funktion dieses 0-Word-Cookies ist genau so wie die anderer Cookies auch, nur eben das der Wert nicht irgendein geheimer Zufallswert ist sondern bekannt immer 0 (deswegen empfehle ich das auch nur als Zusatz-Magic-Cookie). Die Schutzfunktion bei diesem 0-Word-Magic-Cookie liegt eben darin das man es nicht mit den klassischen String-Funktionen auf dem Stack ablegen kann was bei einem Zufallswert, der kein 0-Byte enthält, ja durchaus möglich ist (falls es dem Angreifer gelingt diesen Zufallswert zu ermitteln oder zu erraten). Das ist einfach nur eine kleine zusätzliche Hürde weil somit die ganzen klassischen String-Funktionen als Einfallstor sicher ausfallen und die restliche Angriffsfläche eben kleiner wird.

Ist schön und gut, versteckt aber Bugs in den Anwendungen, die man lieber fixen sollte. ..... Das Nullbyte versteckt den Fehler ja nur.
Wieso sollte ein Magic-Cookie mit dem festen Wert 0 irgendwelche Fehler verstecken können? Ich kann da absolut nicht nachvollziehen was Du eigentlich damit meinst.

Mir wäre es am liebsten, wenn jeder Bug in einer Anwendung direkt zu einem Absturz führt und sich niemals irgendwo verschleiern lässt. Ist leider unmöglich.
Da stimme ich Dir voll zu, aber das ist eben unrealistisch. Obwohl es sich um digitale Technik handelt sind die Auswirkungen von Fehlern doch schon ziemlich fein unterteilt (zwischen "keine sichtbaren Auswirkungen" und "Total-Crash").

Was ist ALR?
Ich meinte die ganze Zeit ASLR, sorry. Ich denke das wenn die Qualität des ASLR gut ist (und auch der benutzte Zufall hoher Qualität ist) dürfte es kaum möglich sein eine brauchbare Einsprung-Adresse zu ermitteln die dann für den Angriff als Rücksprungadresse in die Stack-Angriffsdaten eingebaut werden kann.

Du brauchst normalerweise Root-Zugriff, um TCP-Ports unter 1024 verwenden zu dürfen
Also das betrachte ich als Design-Fehler, dafür sollte man eine extra Berechtigung einführen die nur bestimmte Programme wenn sie von bestimmten Usern ausgeführt werden bekommen können. Warum soll ein Web-Server (sicher das Einfallstor Nummer 1 auf den Servern) root-Rechte brauchen nur weil sein öffentlicher Port < 1024 ist?

du brauchst normalerweise Root-Zugriff, um ein vollständiges Backup (inkl. /etc/shadow) des Systems anfertigen zu können.
Das ist IMHO okay, das Backup-Programm ist nicht ständig aktiv (also das Zeitfenster für den Angreifer begrenzt) und hat auch keine öffentlichen Interfaces wie TCP-Ports usw. (der Angreifer muss also bereits Zugriff auf das System haben um da überhaupt ran zu kommen).

Du brauchst (noch) Root-Zugriff, um die grafische Oberfläche (X-Server) ausführen zu können.
Das ist natürlich genau so Quatsch wie bei dem Web-Server. Ein spezielles Recht zum zugreifen auf den GraKa-Treiber sollte völlig reichen.

Alle diese Programme sind hackbar und damit potentielle Überschreiber von /dev/mem.
Also alle Programme die in /sbin liegen sollten mit der höchsten Sicherheitsstufe vom Compiler generiert werden (da würde ich persönlich auch gerne ein paar Prozent Performanceverlust akzeptieren) und auch die OS-Mechanismen wie ASLR und NX sollten immer voll aktiv sein. Programme die mit root-Rechten laufen sind immer ein besonders interessantes Angriffsziel. Ich würde auch gerne so weit gehen das Programme die root-Rechte bekommen sollen eine spezielle Signatur benötigen damit sich root wenigstens sicher sein kann das diese Programme von niemanden auf der Platte modifiziert wurde.

Die kann man nicht mit "tja, root ist halt selbst schuld" abhandeln.
Das sehe ich ganz genau so, auch wenn ich schon der Meinung bin das man root sicher etwas mehr an Sorgfalt abverlangen kann.

Alle die Adressen die ein Treiber seiner HW mitteilt müssen ja in festen Pages liegen und das muss der Treiber immer explizit anfordern (und auch wieder freigeben) und genau darauf soll der Kernel dann die Peripherie-Zugriff beschränken, ob das überhaupt praktikabel ist weiß ich noch nicht aber wenn nicht dann soll es damit zumindest möglich sein das der Kernel seine eigenen Speicherbereiche für die Peripherie verbietet.
Du müsstest in dem Moment direkt vor den physischen Adressleitungen zum RAM ein paar Gatter setzen, die dem System solche Zugriffsüberschreitungen mitteilen.
Ich hatte an eine White-List im PCI(e)-Root-Complex gedacht die alle reinkommenden Zugriffe filtert und bei Verletzungen einen IRQ auslöst damit der Kernel weiß welche HW (bei PCIe gibt es ja Absenderadressen in den Paketen) an welche nicht freigeschaltete Adresse ran will. Das Problem ist das wenn diese White-List mehr als ca. 16 Einträge haben muss wird sie zu einer Bremse. Eine Black-List die nur die paar Bereiche des Kernel schützt (und dafür vielleicht mit 8 Einträgen gut auskommt) ist da ein akzeptabler Kompromiss (zumindest besser als gar nichts).

Weil das IPC so wunderbar zentral im System verankert ist und auf einem Mikrokernel so ziemlich der einzige Zugang direkt in den Ring 0 ist.
Hä, jeder Syscall liefert einen potentiellen Zugang zum Ring 0 (System-Mode). Was ist da an IPC so besonders? Das einzigste wo IPC von den anderen Syscalls abweicht ist das man damit Zugriff auf andere Prozesse bekommt aber das trifft auf TCP ja auch zu (auch wenn TCP auf nen Micro-Kernel nicht direkt über Syscalls gemacht wird).

Dagegen sollte ein sauber programmierter Service einigermaßen immun sein.
Bugs, Bugs, Bugs. ;-)
Da hilft nur testen, testen, testen! ;)



Grüße
Erik
Reality is that which, when you stop believing in it, doesn't go away.

Svenska

  • Beiträge: 1 792
    • Profil anzeigen
Gespeichert
« Antwort #127 am: 23. November 2010, 11:00 »
Hallo,

Ist schön und gut, versteckt aber Bugs in den Anwendungen, die man lieber fixen sollte. ..... Das Nullbyte versteckt den Fehler ja nur.
Wieso sollte ein Magic-Cookie mit dem festen Wert 0 irgendwelche Fehler verstecken können? Ich kann da absolut nicht nachvollziehen was Du eigentlich damit meinst.
Naja, Stringfunktionen kopieren im Zweifelsfall bis zu einem Nullbyte und damit eventuell über Puffergrenzen hinweg. Wenn du in der Quelle (Stack) jetzt ein neues, zusätzliches Nullbyte einfügst, dann wird die Stringfunktion nicht mehr über den Puffer hinauslaufen (bzw. maximal ein Byte zuviel kopieren, was meist egal ist), weil die Quelldaten ein Zwangsende verpasst kriegen. Damit verhinderst du Angriffe dieser Art konsequent.

Du brauchst normalerweise Root-Zugriff, um TCP-Ports unter 1024 verwenden zu dürfen
Also das betrachte ich als Design-Fehler, dafür sollte man eine extra Berechtigung einführen die nur bestimmte Programme wenn sie von bestimmten Usern ausgeführt werden bekommen können. Warum soll ein Web-Server (sicher das Einfallstor Nummer 1 auf den Servern) root-Rechte brauchen nur weil sein öffentlicher Port < 1024 ist?
Weil Ports unterhalb von 1024 privilegierte Ports sind und deren Funktionen von der IANA standardisiert wurden. Du kannst damit Clients auf anderen Rechnern stark verwirren, wenn du falsche Dienste anbietest. Außerdem könntest du sonst als User auf dem System einen Webserver auf Port 80 starten und so tun, als würdest du einen System-Service anbieten (z.B. ein DoS auf den Webserver mit Absturz als Folge, anschließend startest du einen eigenen Webserver auf Port 80) und dann Schadcode verbreiten - ohne, dass der Admin davon was merkt (er ist nicht immer anwesend) und ohne, dass eine Firewall davor schützen könnte.

du brauchst normalerweise Root-Zugriff, um ein vollständiges Backup (inkl. /etc/shadow) des Systems anfertigen zu können.
Das ist IMHO okay, das Backup-Programm ist nicht ständig aktiv (also das Zeitfenster für den Angreifer begrenzt) und hat auch keine öffentlichen Interfaces wie TCP-Ports usw. (der Angreifer muss also bereits Zugriff auf das System haben um da überhaupt ran zu kommen).
Das regelmäßige Backup-Programm wird aber automatisch mit Root-Rechten gestartet (z.B. von "cron"), dazu muss trotzdem ein Daemon mit Root-Rechten ununterbrochen laufen.

Du brauchst (noch) Root-Zugriff, um die grafische Oberfläche (X-Server) ausführen zu können.
Das ist natürlich genau so Quatsch wie bei dem Web-Server. Ein spezielles Recht zum zugreifen auf den GraKa-Treiber sollte völlig reichen.
Richtig, bzw. der Grafiktreiber wandert in den Kernel (Windows, Linux mit KMS) und das Problem ist beseitigt. Aber wenn du den Gedankengang weiterführst, dann erzeugst du Unmengen an Rechten, die alle brav konfiguriert werden müssen.

Schau dir SELinux an, das ist sicher, wenn es richtig konfiguriert ist. Dumm ist nur, dass es fast niemand konfigurieren kann und dadurch, dass du so enorm viele Einschränkungen machen musst, ist die Zeit zwischen "Installation des Systems" und "produktive Inbetriebnahme" sehr groß, was selbst für Firmen mit sicherheitskritischen Daten nicht mehr akzeptabel ist. Eine Woche (max. zwei) muss reichen und das kriegst du mit SELinux nicht hin, wenn du Webserver/Mailserver/LDAP-Server/... auch sicher konfigurieren möchtest.

Vorgefertigte Beispielregelungen sind immer freier als notwendig, da nicht auf den Anwendungsfall angepasst; Sicherheit ist eine Individualregelung. Je seltsamer dein System aufgebaut ist, desto schwieriger wird ein Angriff.

Alle diese Programme sind hackbar und damit potentielle Überschreiber von /dev/mem.
Also alle Programme die in /sbin liegen sollten mit der höchsten Sicherheitsstufe vom Compiler generiert werden (da würde ich persönlich auch gerne ein paar Prozent Performanceverlust akzeptieren) und auch die OS-Mechanismen wie ASLR und NX sollten immer voll aktiv sein.
Richtig, schließt auch /usr/sbin mit ein. Aber du hast setuid-root-Anwendungen auch in /bin (z.B. "ping") ...

Programme die mit root-Rechten laufen sind immer ein besonders interessantes Angriffsziel. Ich würde auch gerne so weit gehen das Programme die root-Rechte bekommen sollen eine spezielle Signatur benötigen damit sich root wenigstens sicher sein kann das diese Programme von niemanden auf der Platte modifiziert wurde.
Möglichst diese Signatur noch an das TPM des Mainboards gebunden?

Ich hatte an eine White-List im PCI(e)-Root-Complex gedacht die alle reinkommenden Zugriffe filtert und bei Verletzungen einen IRQ auslöst damit der Kernel weiß welche HW (bei PCIe gibt es ja Absenderadressen in den Paketen) an welche nicht freigeschaltete Adresse ran will. Das Problem ist das wenn diese White-List mehr als ca. 16 Einträge haben muss wird sie zu einer Bremse. Eine Black-List die nur die paar Bereiche des Kernel schützt (und dafür vielleicht mit 8 Einträgen gut auskommt) ist da ein akzeptabler Kompromiss (zumindest besser als gar nichts).
Also doch eine hardwareseitige schwarze oder weiße Liste zwischen RAM und den Busmastern (CPU, DMA, PCI/e). ;-) Das wirst du aber gefühlt nicht unbedingt in die PC-Architektur eingebaut kriegen, es ist teure und eine Bremse. Selbst die MMU ist eine Bremse - auch bei 1:1-Mapping mit den Pagetables im internen Cache. So eine Schaltung (im Prinzip ein CPLD-Gatter) bremst allein dadurch, dass die Taktraten zum RAM so hoch wie möglich sind. Wenn es dir wert ist, dann ist das natürlich eine optimale Lösung.

Weil das IPC so wunderbar zentral im System verankert ist und auf einem Mikrokernel so ziemlich der einzige Zugang direkt in den Ring 0 ist.
Hä, jeder Syscall liefert einen potentiellen Zugang zum Ring 0 (System-Mode). Was ist da an IPC so besonders? Das einzigste wo IPC von den anderen Syscalls abweicht ist das man damit Zugriff auf andere Prozesse bekommt aber das trifft auf TCP ja auch zu (auch wenn TCP auf nen Micro-Kernel nicht direkt über Syscalls gemacht wird).
Syscalls sind eine Form von IPC, nur dass das Ziel fest ist. Bei einem Mikrokernel (und die kleineren Varianten) können Syscalls auch direkt als IPC implementiert werden, wenn z.B. das Memory Management in einen eigenen Task ausgelagert wird (vgl. Minix). Wenn du TCP dort sinnvollerweise als Extra-Task implementierst, wird der Netzwerktreiber dann IPC mit dem TCP/IP-Stack machen. Greifst du das IPC an, hast du Zugriff auf beide Seiten (und damit DMA).

IPC ist nicht der einzige Angriffspunkt, aber ein sehr zentral gelegener und daher gut geeigneter. Darum muss man dort besonders aufpassen.

Dagegen sollte ein sauber programmierter Service einigermaßen immun sein.
Bugs, Bugs, Bugs. ;-)
Da hilft nur testen, testen, testen! ;)
Testen findet nur Fehler, die auch auftreten. ;-) Fuzzing-Attacken sind da schon hilfreicher, aber aufwändig (Bruteforce) und finden damit auch nicht immer alles. Auch Testsuites helfen, aber nicht universell.

Wobei die Lage nicht so schlimm ist, wie von mir beschrieben. Was geht, sind Programmiersprachen, die inhärent sicher (bzw. korrekt) sind. Aber Ada will man nicht programmieren, von der Performance rede ich lieber nicht.

Das ist jetzt aber definitiv OT gewesen.

Gruß,
Svenska

FlashBurn

  • Beiträge: 844
    • Profil anzeigen
Gespeichert
« Antwort #128 am: 23. November 2010, 11:10 »
Zitat von: svenska
Syscalls sind eine Form von IPC, nur dass das Ziel fest ist. Bei einem Mikrokernel (und die kleineren Varianten) können Syscalls auch direkt als IPC implementiert werden, wenn z.B. das Memory Management in einen eigenen Task ausgelagert wird (vgl. Minix). Wenn du TCP dort sinnvollerweise als Extra-Task implementierst, wird der Netzwerktreiber dann IPC mit dem TCP/IP-Stack machen. Greifst du das IPC an, hast du Zugriff auf beide Seiten (und damit DMA).
Dem kann man aber (und so ungefähr mache ich es auch) ganz einfach entgegentreten, es dürfen halt nicht alle auf die entsprechenden Ports zu greifen (entweder ein Bestimmer Task/Thread oder alle).
Somit kannst du da auch nichts angreifen, du bekommst einfach nen Errorcode zurück, das du keine Rechte hast da was hinzuschicken oder auszulesen.

Ich denke du stellst dir das zu einfach vor oder ich sehe bestimmte Angriffspunkte nicht.

Svenska

  • Beiträge: 1 792
    • Profil anzeigen
Gespeichert
« Antwort #129 am: 23. November 2010, 13:11 »
Zitat von: svenska
Greifst du das IPC an, hast du Zugriff auf beide Seiten (und damit DMA).
Dem kann man aber (und so ungefähr mache ich es auch) ganz einfach entgegentreten, es dürfen halt nicht alle auf die entsprechenden Ports zu greifen (entweder ein Bestimmer Task/Thread oder alle).
Somit kannst du da auch nichts angreifen, du bekommst einfach nen Errorcode zurück, das du keine Rechte hast da was hinzuschicken oder auszulesen.
Siehst du? Du hast drüber nachgedacht und mehr wollte ich nicht erreichen. ;-)

Ich denke du stellst dir das zu einfach vor oder ich sehe bestimmte Angriffspunkte nicht.
Wenn man eine zentrale Rechtevergabe für das IPC macht (und diese hinreichend fehlerfrei ist, das mal angenommen), fällt das Problem wie ein Kartenhaus zusammen.

Gruß,
Svenska

erik.vikinger

  • Beiträge: 1 277
    • Profil anzeigen
Gespeichert
« Antwort #130 am: 23. November 2010, 15:36 »
Hallo,

Wieso sollte ein Magic-Cookie mit dem festen Wert 0 irgendwelche Fehler verstecken können? Ich kann da absolut nicht nachvollziehen was Du eigentlich damit meinst.
Naja, Stringfunktionen kopieren im Zweifelsfall bis zu einem Nullbyte und damit eventuell über Puffergrenzen hinweg. Wenn du in der Quelle (Stack) jetzt ein neues, zusätzliches Nullbyte einfügst, dann wird die Stringfunktion nicht mehr über den Puffer hinauslaufen (bzw. maximal ein Byte zuviel kopieren, was meist egal ist), weil die Quelldaten ein Zwangsende verpasst kriegen. Damit verhinderst du Angriffe dieser Art konsequent.
Es ging mir eigentlich nicht um den Quell-Puffer sondern darum das der Angreifer nicht absichtlich ein 0-Byte in einen normalen Angriffs-String einbauen kann um damit dieses spezielle Magic-Cookie mit dem richtigen Wert zu überschreiben (bei einem Zufalls-Cookie wo kein 0-Byte drin ist geht das ja prinzipiell falls er das erraten oder ausspähen kann), das funktioniert einfach nicht mit den normalen String-Funktionen so dass das überschreiben dieses Magic-Cookies eben immer auffallen würde (weil der Angreifer ja eben kein 0-Byte ablegen kann). Ansonsten hast Du natürlich recht.

Das regelmäßige Backup-Programm wird aber automatisch mit Root-Rechten gestartet (z.B. von "cron"), dazu muss trotzdem ein Daemon mit Root-Rechten ununterbrochen laufen.
Da hast Du recht. cron ist so ein Prozess der ständig läuft und root-Rechte hat aber cron ist klein genug das er einigermaßen fehlerfrei sein sollte und außerdem hat cron auch keine öffentlich erreichbaren Interfaces (wie TCP o.ä.).

Das ist natürlich genau so Quatsch wie bei dem Web-Server. Ein spezielles Recht zum zugreifen auf den GraKa-Treiber sollte völlig reichen.
Richtig, bzw. der Grafiktreiber wandert in den Kernel (Windows, Linux mit KMS) und das Problem ist beseitigt.
Also gerade dadurch das der Grafiktreiber in den Kernel wandert kann ich absolut keinen Sicherheitsgewinn erkennen. Grafiktreiber sind enorm kurzlebig und zeitkritisch (jeder Hersteller will das neuste Feature seiner Chips auch bestmöglich anbieten) so das dort kaum auf Sicherheit u.ä. geachtet wird. Die besseren Treiber für die professionellen Workstations sind eben extra geprüft und zertifiziert und den normalen Treiber immer mindestens 6 Monate hinterher (aber das ist der typischen Klientel dieser Treiber egal).

Aber wenn du den Gedankengang weiterführst, dann erzeugst du Unmengen an Rechten, die alle brav konfiguriert werden müssen.
Das stimmt natürlich, hier ist es wichtig mit einem guten Augenmaß ran zu gehen. Sicherheit macht eben immer etwas Arbeit. Egal ob es um den Zugriff auf Ports < 1024 oder den Grafiktreiber geht (beides muss natürlich angemessen reglementiert werden), es ist immer Konfigurationsarbeit zu leisten. Für den Endanwender läuft das auf eine (nicht immer einfache) Kosten/Nutzen-Abwägung hinaus.

Möglichst diese Signatur noch an das TPM des Mainboards gebunden?
Nein, natürlich nicht! Schon allein der Gedanke ist völlig unpassend. Das sollte mit dem privaten Schlüssel von root erledigt werden und der ist nur über das Passwort (oder eine SmartCard oder ein US-Dongle oder ...) von root zugänglich.

Also doch eine hardwareseitige schwarze oder weiße Liste
Ja. Das ist das einzigste was man nicht einfach so umgehen kann, es sei denn man schafft es Kernel-Privilegien zu bekommen und diese Liste zu ändern aber wenn man eh schon soweit ist kommt man auch so an alles ran.

zwischen RAM und den Busmastern (CPU, DMA, PCI/e).
Nein, im Chipsatz. Also in der Verbindung zwischen CPUs und RAM auf der einen Seite und der Peripherie auf der anderen Seite. Es geht nicht jeder RAM-Zugriff durch diesen Filter sondern nur das was von der Peripherie kommt. DMA hab ich auf meiner Plattform nicht, würde ich aber eventuell der CPU-Seite zurechnen wenn der Kernel aufpasst das da nichts unerlaubtes rein kommt, ansonsten doch lieber auf die Peripherie-Seite.

Das wirst du aber gefühlt nicht unbedingt in die PC-Architektur eingebaut kriegen, es ist teure und eine Bremse.
Das sehe ich anders, auch bei der PC-Architektur gibt es eine zentrale Schnittstelle zwischen den CPUs mit ihrem RAM auf der einen Seite und der Peripherie auf der anderen Seite, eben der Chipsatz (dort wo der PCI(e)-Root-Complex sitzt). Selbst bei einer Single-Chip-Lösung muss dieser Filter einfach nur an der richtigen Stelle sitzen und schon ist der Performanceverlust nicht mehr existent. Bei reinkommenden Zugriffen von der Peripherie muss doch eh geschaut werden wo die hingehen und parallel dazu kann die Filterliste befragt werden, kostet nicht einen Takt extra nur ein klein wenig Logik (zumindest eine Black-List sollte sich simpel realisieren lassen).

Wenn es dir wert ist, dann ist das natürlich eine optimale Lösung.
Ja, das ist es mir wert.

Selbst die MMU ist eine Bremse - auch bei 1:1-Mapping mit den Pagetables im internen Cache.
Das ist einer der wesentlichen Gründe für meine Segmente.

IPC ist nicht der einzige Angriffspunkt, aber ein sehr zentral gelegener und daher gut geeigneter. Darum muss man dort besonders aufpassen.
Gut, IPC ist sehr wichtig und daher muss man dort besonders gut aufpassen, auch bzw. gerade bei der Konzeptentwicklung. Die anderen Syscalls sollte man aber auch nicht vernachlässigen. Es gab schon genug erfolgreiche Angriffe durch die (unbewachte) Hintertür.

hast du Zugriff auf beide Seiten (und damit DMA).
Nehmen wir an es würde einem Angreifer auf meiner Plattform gelingen sich zwischen einem Ethernet-Treiber und dem IP-Prozess zu drängeln (mal ganz egal wie das gehen soll), dann hat der Angreifer aber noch keinen wahlfreien Zugriff auf die Busmasterfunktionen des Ethernet-Controllers sondern kann sich nur Daten in seine eigenen Segmente schreiben lassen. Auch auf die Clients vom IP-Prozess hat der Angreifer keinen freien Zugriff weil die ja nur die Datenbereiche per IPC weitergeben in die sie auch Netzwerkdaten haben wollen. Ein Angreifer könnte also einen Man-in-the-Middle-Angriff auf die Netzwerkkommunikation starten (so als würde er einen Ethernet-Switch kontrollieren) aber einen Zugriff auf das System hat er damit noch lange nicht.

Da hilft nur testen, testen, testen! ;)
Testen findet nur Fehler, die auch auftreten. ;-) Fuzzing-Attacken sind da schon hilfreicher, aber aufwändig (Bruteforce) und finden damit auch nicht immer alles. Auch Testsuites helfen, aber nicht universell.
Ja, gutes Testen will gelernt sein. Aber was, außer testen, kann ein Entwickler sonst noch tun? Er muss nur genügend Test-Szenarien finden oder finden lassen (zur Not muss eben der Endanwender die Test-Szenarien finden ;) ).

Was geht, sind Programmiersprachen, die inhärent sicher (bzw. korrekt) sind. Aber Ada will man nicht programmieren, von der Performance rede ich lieber nicht.
Soweit ich weiß ist der Performanceverlust bei Ada unterhalb der Messbarkeitsgrenze. Das einzigste was C/C++ wirklich fehlt sind die "run time array bound checks" und die muss der Programmierer dann von Hand erledigen (die Stellen wo er das effizienter erledigen kann als der Compiler dürften sehr rar sein), wenn der Programmierer das nicht tut gibt es die allseits beliebten Buffer-Overflows.

Das ist jetzt aber definitiv OT gewesen.
Wir sind hier auf "Lowlevel-Coding" und Sicherheit ist immer eine Angelegenheit von allen Ebenen, auch der niedrigen Ebene.


es dürfen halt nicht alle auf die entsprechenden Ports zu greifen (entweder ein Bestimmer Task/Thread oder alle).
Das bedeutet aber das Du wirklich für jeden Client einen neuen Port aufmachen musst, oder Du kannst für jeden Port eine richtige Liste mit den erlaubten Prozess-IDs verwalten. Zum ersten connecten neuer Clients brauchst Du aber auch Ports an die jeder ran kommt. Das ganze ist dann so wie bei den TCP-Sockets, der Server erstellt einen TCP-Server-Socket an den jeder Client ran kann und für jede zustande kommende Verbindung gibt es dann einen extra Socket der nur eine einzelne Verbindung repräsentiert und damit nur von einem einzelnen Client benutzt werden kann.

Bei meinem Konzept habe ich mich dazu entschieden das grundsätzlich immer jeder Prozess auf einen IPC-Service zugreifen darf (falls er denn die nötige ID kennt), der Kernel macht da nie irgendwelche Einschränkungen. Die Rechteverwaltung bleibt somit bei den Services hängen aber z.B. der VFS muss das eh erledigen (eine bereits geöffnete Datei kann nicht einfach so noch mal geöffnet werden) so das ich da keinen allzu großen Nachteil sehe. Seht ihr das anders?


Grüße
Erik
Reality is that which, when you stop believing in it, doesn't go away.

FlashBurn

  • Beiträge: 844
    • Profil anzeigen
Gespeichert
« Antwort #131 am: 23. November 2010, 16:05 »
Zitat von: erik
Bei meinem Konzept habe ich mich dazu entschieden das grundsätzlich immer jeder Prozess auf einen IPC-Service zugreifen darf (falls er denn die nötige ID kennt), der Kernel macht da nie irgendwelche Einschränkungen. Die Rechteverwaltung bleibt somit bei den Services hängen aber z.B. der VFS muss das eh erledigen (eine bereits geöffnete Datei kann nicht einfach so noch mal geöffnet werden) so das ich da keinen allzu großen Nachteil sehe. Seht ihr das anders?
Wenn der Client also die ID des Ports vom VFS kennt (was er ja muss), kann er also das gesamte System lahmlegen, weil er von dem Port lesen kann?

Zitat von: erik
Das bedeutet aber das Du wirklich für jeden Client einen neuen Port aufmachen musst, oder Du kannst für jeden Port eine richtige Liste mit den erlaubten Prozess-IDs verwalten. Zum ersten connecten neuer Clients brauchst Du aber auch Ports an die jeder ran kommt. Das ganze ist dann so wie bei den TCP-Sockets, der Server erstellt einen TCP-Server-Socket an den jeder Client ran kann und für jede zustande kommende Verbindung gibt es dann einen extra Socket der nur eine einzelne Verbindung repräsentiert und damit nur von einem einzelnen Client benutzt werden kann.
Jap, genauso habe ich mir das vorgestellt, ein allgemein bekannter Port um die Kommunikation zu initialisieren und dann ein "privater" Port für die eigentliche Kommunikation.

Da wir das schonmal hatten, gibt es eigentlich eine Situation wo man über die gesamte Programmlaufzeit keinen Port zum lesen benötigt, aber eine Nachricht an einen Port schicken will/muss?

Ich frage deshalb, weil man bei mir zwingend einen Port braucht um überhaupt eine Nachricht an einen anderen Port zu schreiben.

erik.vikinger

  • Beiträge: 1 277
    • Profil anzeigen
Gespeichert
« Antwort #132 am: 23. November 2010, 17:18 »
Hallo,


Wenn der Client also die ID des Ports vom VFS kennt (was er ja muss), kann er also das gesamte System lahmlegen, weil er von dem Port lesen kann?
Hä, was meinst Du damit? Mein IPC soll etwas anders arbeiten, da kann man an so einen Port (der bei mir Message-Target heißt) einfach einen synchronen IPC-Vorgang (ein komplett atomar zusammenhängendes Spiel aus Anfrage und zugehöriger Antwort) schicken, das ist für den Sender blockierend. Wenn der Service es erlaubt können auch mehrere solcher Vorgänge parallel laufen (dann sind in dem Service-Prozess mehrere IPC-PopUp-Threads für dieses eine Message-Target aktiv), also mehrere Clients parallel Anfragen durchführen (das können mehrere unabhängige Prozesse sein aber auch mehrere Threads eines Prozess). Ein Service kann auch mehrere von diesen Message-Target aufmachen, die sind dann völlig unabhängig von einander, um darüber unterschiedliche Services anzubieten (oder auch unterschiedliche Instanzen eines Services).

Es soll auch Message-Targets für asynchrones IPC geben, da wird einfach nur eine Message geschickt und dafür ein PopUp-Thread im Service aufgemacht (der dann die Message verarbeitet), der Sender kann während dessen weiter arbeiten (also nicht blockierend da keine Antwort zurückkommt).

Als Sicherheit gibt der Kernel dem PopUp-Thread immer die Prozess-ID des Senders mit so das der Service weiß für welchen Client er eine Ressource geöffnet hat.

Jap, genauso habe ich mir das vorgestellt, ein allgemein bekannter Port um die Kommunikation zu initialisieren und dann ein "privater" Port für die eigentliche Kommunikation.
Dann wirst Du eine Menge Ports dafür aufmachen müssen.

Da wir das schonmal hatten, gibt es eigentlich eine Situation wo man über die gesamte Programmlaufzeit keinen Port zum lesen benötigt, aber eine Nachricht an einen Port schicken will/muss?
Ich frage deshalb, weil man bei mir zwingend einen Port braucht um überhaupt eine Nachricht an einen anderen Port zu schreiben.
Also bei meinem Konzept benötigt der Client kein eigenes Message-Target um an einen Service eine Anfrage zu schicken (egal ob synchrones oder asynchrones IPC), er benötigt nur die ID des gewünschten Message-Targets und macht damit die IPC-Syscalls. Ich kann mir ehrlich gesagt auch nicht vorstellen wozu das gut sein soll das der Client selber quasi zum Service wird. Bei mir wird es sicher viele simple Programme (die trotzdem deutlich über Hello-World hinausgehen, ich denke da z.B. an Compiler o.ä.) geben die kein einziges eigenes Message-Target aufmachen.
Was meist du eigentlich mit "Port zum lesen", also hat das lesen ein spezielle Bedeutung?


Grüße
Erik
Reality is that which, when you stop believing in it, doesn't go away.

Svenska

  • Beiträge: 1 792
    • Profil anzeigen
Gespeichert
« Antwort #133 am: 23. November 2010, 17:30 »
Hallo,

Das regelmäßige Backup-Programm wird aber automatisch mit Root-Rechten gestartet (z.B. von "cron"), dazu muss trotzdem ein Daemon mit Root-Rechten ununterbrochen laufen.
Da hast Du recht. cron ist so ein Prozess der ständig läuft und root-Rechte hat aber cron ist klein genug das er einigermaßen fehlerfrei sein sollte und außerdem hat cron auch keine öffentlich erreichbaren Interfaces (wie TCP o.ä.).
Was ist mit inetd? Ich weiß, ein Ausgeburt der Hölle, aber mir gefällt es. Der muss Prozesse unter bestimmten Nutzernamen ausführen können, also muss er root sein. Und er ist definitiv per TCP (UDP?) von außen erreichbar.

Wie auch immer, gewisse Services müssen root-Rechte haben, solange du nicht für jede noch so kleine Aktion eine eigene Permission baust. Wenn der Kernel endlich hinreichend sicher ist, wird man sich an genau diesen Services versuchen. ;-)

Grafiktreiber sind enorm kurzlebig und zeitkritisch (jeder Hersteller will das neuste Feature seiner Chips auch bestmöglich anbieten) so das dort kaum auf Sicherheit u.ä. geachtet wird. Die besseren Treiber für die professionellen Workstations sind eben extra geprüft und zertifiziert und den normalen Treiber immer mindestens 6 Monate hinterher (aber das ist der typischen Klientel dieser Treiber egal).
Naja, ich denke, dass es dabei hauptsächlich um Funktionskorrektheit geht, weniger um Sicherheit. Professionelle Workstations sollten ohnehin weit weg vom öffentlichen Internet sein (mindestens NAT, besser Proxy).

Der Grund für die Implementation im Kernel (bei Monolithen) ist Performance (Windows) und eine gemeinsame API als Basis für mehrere Treiber (Linux). Gleicher Grund, wie die Implementation von WLAN im Kernel, damit nicht jeder Treiber seinen eigenen WLAN-Stack mitbringen muss (wie bei Windows XP) und damit jeder Treiber, egal von wem, die gleichen Dinge unterstützen kann (WPA, HostAP).

Möglichst diese Signatur noch an das TPM des Mainboards gebunden?
Nein, natürlich nicht! Schon allein der Gedanke ist völlig unpassend. Das sollte mit dem privaten Schlüssel von root erledigt werden und der ist nur über das Passwort (oder eine SmartCard oder ein US-Dongle oder ...) von root zugänglich.
Gut, dann bindest du eben den Schlüssel an das TPM. Dafür ist es schließlich da: Als Ersatz für SmartCard-Authentifizierung, wenn du dem System trauen kannst, es also nicht einfach wegtragen kannst.

zwischen RAM und den Busmastern (CPU, DMA, PCI/e).
Nein, im Chipsatz. Also in der Verbindung zwischen CPUs und RAM auf der einen Seite und der Peripherie auf der anderen Seite. Es geht nicht jeder RAM-Zugriff durch diesen Filter sondern nur das was von der Peripherie kommt. DMA hab ich auf meiner Plattform nicht, würde ich aber eventuell der CPU-Seite zurechnen wenn der Kernel aufpasst das da nichts unerlaubtes rein kommt, ansonsten doch lieber auf die Peripherie-Seite.
Das ist aber inkonsequent. :-P Im Chipsatz liegt die RAM-Steuerung, also kannst du die Liste auch direkt vor den RAM setzen und mit mehreren Eingängen (CPU, DMA, PCI-Bus, Firewire-Bus [extra], ...) versehen, die jeweils eigene Einträge haben. Der Unterschied ist dann gering und wenn du paranoid genug bist, könntest du eine Adressraumliste für jeden einzelnen Task im System haben.

Je nachdem, wie schnell ein Neuladen der Liste relativ zur Zeitscheibe von Tasks dauert, könnte das sogar erträglich sein. Allerdings bezweifle ich, dass sowas des Rätsels Lösung ist. Das Prinzip ist ja dasselbe wie ein Watchdog und der wurde ja auch schon hinreichend ausgenutzt. (Der DoS-Exploit muss den Watchdog nur selbst streicheln, schon legt man die Services trotzdem unbegrenzt lange lahm.)

Das wirst du aber gefühlt nicht unbedingt in die PC-Architektur eingebaut kriegen, es ist teure und eine Bremse.
Das sehe ich anders, auch bei der PC-Architektur gibt es eine zentrale Schnittstelle zwischen den CPUs mit ihrem RAM auf der einen Seite und der Peripherie auf der anderen Seite, eben der Chipsatz (dort wo der PCI(e)-Root-Complex sitzt).
Du vergisst die Kompatiblität mit der Urzeit. Halte dich von der PC-Architektur fern, die ist stabil seit 1981! :-P

Prinzipiell möglich ist alles, aber umsetzbar ist es dann trotzdem nicht. Das können höchstens geschlossene Systeme a la Apple oder Spezialgeräte (embedded) machen, wo es angebracht ist.

IPC ist nicht der einzige Angriffspunkt, aber ein sehr zentral gelegener und daher gut geeigneter. Darum muss man dort besonders aufpassen.
Gut, IPC ist sehr wichtig und daher muss man dort besonders gut aufpassen, auch bzw. gerade bei der Konzeptentwicklung. Die anderen Syscalls sollte man aber auch nicht vernachlässigen. Es gab schon genug erfolgreiche Angriffe durch die (unbewachte) Hintertür.
Auf dem 27C3 (hoffentlich krieg ich noch ne Karte...) wird es einen Vortrag geben zu Angriffen durch Mischung von 32- und 64-Bit-Code in einer Anwendung und ungewöhnliches Verwenden von Syscalls in beiden Modi. Ähnlich kann man es ja auf ARM (mit ARM und Thumb-Code) oder MIPS (da gibt's jetzt auch einen kleineren Code) übertragen.

Nehmen wir an es würde einem Angreifer auf meiner Plattform gelingen sich zwischen einem Ethernet-Treiber und dem IP-Prozess zu drängeln (mal ganz egal wie das gehen soll), dann hat der Angreifer aber noch keinen wahlfreien Zugriff auf die Busmasterfunktionen des Ethernet-Controllers sondern kann sich nur Daten in seine eigenen Segmente schreiben lassen.
Richtig, weil du - und das meinte ich mit "komisches Zeug" - verschiedene Adressräume hast. Und zwar hardwareseitig, wenn du es wünschst. Bei Flatmem-Systemen gibt es ja nur einen und den kannst du aus Hardwaresicht nicht mit Paging erschlagen - eben weil DMA und so. Da braucht es dann die Fangschaltung von oben, die ist aber nicht in heutigen Systemen drin.

Was geht, sind Programmiersprachen, die inhärent sicher (bzw. korrekt) sind. Aber Ada will man nicht programmieren, von der Performance rede ich lieber nicht.
Soweit ich weiß ist der Performanceverlust bei Ada unterhalb der Messbarkeitsgrenze. Das einzigste was C/C++ wirklich fehlt sind die "run time array bound checks" und die muss der Programmierer dann von Hand erledigen (die Stellen wo er das effizienter erledigen kann als der Compiler dürften sehr rar sein), wenn der Programmierer das nicht tut gibt es die allseits beliebten Buffer-Overflows.
Njain. Bevor du in Ada anfangen kannst zu programmieren, bist du erstmal ziemlich lange damit beschäftigt, die Aufgabe und ihre Einzelteile und Abhängigkeiten zu beschreiben. Darum ist Ada ja auf Korrektheit prüfbar (im Gegensatz zu den meisten Programmiersprachen), aber der Programmierer muss dafür schon enormen Aufwand treiben und er darf nicht mit unscharfen Aufgabenstellungen beworfen werden. Run-Time-Boundary-Checks will man ja mit so Dingen wie Java (virtuellen maschinen) oder .NET (managed code) erschlagen, im Endeffekt hab ich heute ne Statistik gefunden, wonach irgendwas um die Hälfte aller Sicherheitslücken auf Buffer Overflow-Angriffe zurückgeht.

Wusstest du übrigens, dass das Template-System von C++ turing-vollständig ist?

Zitat von: erik
Bei meinem Konzept habe ich mich dazu entschieden das grundsätzlich immer jeder Prozess auf einen IPC-Service zugreifen darf (falls er denn die nötige ID kennt), der Kernel macht da nie irgendwelche Einschränkungen. Die Rechteverwaltung bleibt somit bei den Services hängen aber z.B. der VFS muss das eh erledigen (eine bereits geöffnete Datei kann nicht einfach so noch mal geöffnet werden) so das ich da keinen allzu großen Nachteil sehe. Seht ihr das anders?
Du trennst also nicht zwischen der Berechtigung des Zugriffs und der Berechtigung der Wunschaktion. Gibt es die Möglichkeit, die IPC-IDs zu bruteforcen? ;-)

Da wir das schonmal hatten, gibt es eigentlich eine Situation wo man über die gesamte Programmlaufzeit keinen Port zum lesen benötigt, aber eine Nachricht an einen Port schicken will/muss?
Ja, kann man durchaus konstruieren, z.B. wenn ich etwas in der Art eines Funkbroadcast verschicken möchte, wo es keine Bestätigung geben kann oder (aus Performancegründen) muss. Sowas wie "init", welches allen Prozessen mitteilt, dass das System jetzt herunterfährt/abschaltet oder meinetwegen auch UDP-Nachrichten.

Ich frage deshalb, weil man bei mir zwingend einen Port braucht um überhaupt eine Nachricht an einen anderen Port zu schreiben.
Damit sehe ich kein Problem. Für irgendwelche Aktionen brauchst du eigentlich immer ein Handle, das wäre bei dir der Port. Ist halt ne Designvorgabe, dass man keine "anonymen" IPC-Nachrichten schicken kann. Wenn du potentiell gefährlichen Prozessen verbietest, einen Port zu bekommen, dann verhinderst du effektiv die IPC-Kommunikation; für die Sicherheit ist das sicherlich förderlich.

Ich kann mir ehrlich gesagt auch nicht vorstellen wozu das gut sein soll das der Client selber quasi zum Service wird. Bei mir wird es sicher viele simple Programme (die trotzdem deutlich über Hello-World hinausgehen, ich denke da z.B. an Compiler o.ä.) geben die kein einziges eigenes Message-Target aufmachen.
Wenn du eine Syscall-API hast, die vollkommen asynchron und mit Callbacks arbeitet, wie z.B. das X11-Protokoll, dann ist jeder Client irgendwo ein Anbieter für so eine Callback-Struktur.

Gruß,
Svenska

FlashBurn

  • Beiträge: 844
    • Profil anzeigen
Gespeichert
« Antwort #134 am: 23. November 2010, 18:11 »
Zitat von: erik
Hä, was meinst Du damit? Mein IPC soll etwas anders arbeiten, da kann man an so einen Port (der bei mir Message-Target heißt) einfach einen synchronen IPC-Vorgang (ein komplett atomar zusammenhängendes Spiel aus Anfrage und zugehöriger Antwort) schicken, das ist für den Sender blockierend. Wenn der Service es erlaubt können auch mehrere solcher Vorgänge parallel laufen (dann sind in dem Service-Prozess mehrere IPC-PopUp-Threads für dieses eine Message-Target aktiv), also mehrere Clients parallel Anfragen durchführen (das können mehrere unabhängige Prozesse sein aber auch mehrere Threads eines Prozess). Ein Service kann auch mehrere von diesen Message-Target aufmachen, die sind dann völlig unabhängig von einander, um darüber unterschiedliche Services anzubieten (oder auch unterschiedliche Instanzen eines Services).
Ich vergesse immer das du ja synchrones IPC hast, also auf jeden Fall auf eine Antwort "gewartet" wird. Du hast aber sehr wohl eine Art Rechteüberprüfung, denn es ist ganz genau festgelegt wer eine Nachricht bekommt, das ist bei mir nicht so. Es kann bei mir Ports geben, die von allen Prozessen gelesen werden können (keine Ahnung ob das jemals benutzt wird, aber ich habe es eben).

Zitat von: erik
Also bei meinem Konzept benötigt der Client kein eigenes Message-Target um an einen Service eine Anfrage zu schicken (egal ob synchrones oder asynchrones IPC), er benötigt nur die ID des gewünschten Message-Targets und macht damit die IPC-Syscalls. Ich kann mir ehrlich gesagt auch nicht vorstellen wozu das gut sein soll das der Client selber quasi zum Service wird. Bei mir wird es sicher viele simple Programme (die trotzdem deutlich über Hello-World hinausgehen, ich denke da z.B. an Compiler o.ä.) geben die kein einziges eigenes Message-Target aufmachen.
Was meist du eigentlich mit "Port zum lesen", also hat das lesen ein spezielle Bedeutung?
Mein IPC System funktioniert doch ein wenig anders. Bei mir werden Nachrichten grundsätzlich an Ports verschickt. Deswegen braucht auch jeder Client einen Port wo er die Antwort auf eine Anfrage abholen kann (das ist mit Port lesen gemeint).
Und genau das wäre auch ein Angriffspunkt meines IPC Systems. Denn man könnte eine DoS Attacke auf jeden Clienten machen, weil der muss ja die Nachricht auf jeden Fall erstmal abholen um sie dann verwerfen zu können. Im Endeffekt verschiebe ich damit die Kontrolle ob jemand eine Nachricht an einen Port schicken darf, auf den Clienten (dieser kann ja anhand des Senders überprüfen ob die Nachricht von einer vernünftigen Quelle kommt).
Desweiteren sollte das auch kein Problem sein (höchstens aus Speichertechnischer Sicht). Denn Standardmäßig wird bei der Porterstellung auch eine max. Anzahl an Nachrichten im Port mit angegeben und wenn die erreicht ist, bekommt der Sender nen Errorcode das die Queue des Empfängers voll ist, aber eine Nachricht auf die ein "Port" wartet (also synchrones IPC) können immer in die Queue.

Ich denke das mein System auch ausreichend sicher sein sollte. Ich würde halt nen sehr niedrigen Wert Standardwert (Nachrichten in der Port-Queue) für Clienten wählen und somit würde da auch nicht alzu viel Speicher verschwendet werden.
Ansich könnte man sogar soweit gehen und sagen, das für nen Clienten ne Port-Queue-Länge von 1 reicht.

erik.vikinger

  • Beiträge: 1 277
    • Profil anzeigen
Gespeichert
« Antwort #135 am: 24. November 2010, 12:45 »
Hallo,


Wie auch immer, gewisse Services müssen root-Rechte haben, solange du nicht für jede noch so kleine Aktion eine eigene Permission baust. Wenn der Kernel endlich hinreichend sicher ist, wird man sich an genau diesen Services versuchen.
Ja, Du hast recht. Ein normal nutzbares System wird immer irgendwelche potentiellen Angriffsziele bieten, wenn man das nicht will darf man den Computer niemals anschalten. Wir alle wissen das es keine 100%-ige Sicherheit geben wird, alles was uns bleibt ist auf jeder Ebene unser bestes zu tun.

Der Grund für die Implementation im Kernel (bei Monolithen) ist Performance (Windows) und eine gemeinsame API als Basis für mehrere Treiber (Linux). Gleicher Grund, wie die Implementation von WLAN im Kernel, damit nicht jeder Treiber seinen eigenen WLAN-Stack mitbringen muss (wie bei Windows XP) und damit jeder Treiber, egal von wem, die gleichen Dinge unterstützen kann (WPA, HostAP).
Warum muss man für ein einheitliches Interface solche Dinge unbedingt in den Kernel verlegen? Auch unterschiedliche eigenständige Treiber sollten ein einheitliches Interface anbieten können. Sowas wie der WLAN-Stack könnte ein eigenständiger Service sein der allen WLAN-HW-Treibern zur Verfügung steht. Ich weiß ich denke zu sehr in Richtung Micro-Kernel.

Gut, dann bindest du eben den Schlüssel an das TPM. Dafür ist es schließlich da: Als Ersatz für SmartCard-Authentifizierung, wenn du dem System trauen kannst, es also nicht einfach wegtragen kannst.
Also sowas wie das TPM kommt für mich persönlich schon ganz grundsätzlich nicht in Frage, dieses TPM ist in meinen Augen absolut nicht vertrauenswürdig (jedenfalls nicht für den Endanwender). Also wenn dann würde ich auf jeden Fall eine SmartCard oder ein USB-Dongle bevorzugen, meinetwegen auch ein unter die Haut implantierter RF-ID-Chip oder sowas in der Art, aber diese Komponenten müssen für mich als Anwender absolut vertrauenswürdig sein und sollten nicht permanent mit dem System verbunden sein.

Im Chipsatz liegt die RAM-Steuerung, also kannst du die Liste auch direkt vor den RAM setzen und mit mehreren Eingängen (CPU, DMA, PCI-Bus, Firewire-Bus [extra], ...) versehen, die jeweils eigene Einträge haben.
Bei heutigen PC-Chipsätzen ist der RAM-Controller nicht mehr drin, so soll meine Plattform auch werden. Aber auch logisch sind die Peripherie-Geräte ja nicht direkt mit dem RAM verbunden, sondern gehen eigentlich immer erst über den PCI(e)-Root-Controller drüber. Ich hab sogar mal mit dem Gedanken gespielt dort ne Art MMU einzubauen die in sowas wie ner Paging-Tabelle (die aber keine Adressen sondern nur Bits für erlaubt/nicht erlaubt enthält) nachschaut ob der Zugriff genehmigt werden kann, wenn man das geschickt implementiert dürfte das ziemlich schnell und vor allem sehr flexibel sein.

wenn du paranoid genug bist, könntest du eine Adressraumliste für jeden einzelnen Task im System haben.
Das hab ich doch schon: meine Segmente. Keine (User-Mode-)Software kann da dran vorbei. Es geht mir bei der Liste im Chipsatz wirklich um die Hardware auf der anderen Seite vom Chipsatz.

auch bei der PC-Architektur gibt es eine zentrale Schnittstelle zwischen den CPUs mit ihrem RAM auf der einen Seite und der Peripherie auf der anderen Seite, eben der Chipsatz (dort wo der PCI(e)-Root-Complex sitzt).
Du vergisst die Kompatiblität mit der Urzeit. Halte dich von der PC-Architektur fern, die ist stabil seit 1981! :-P
Ich bin der Meinung das eine (default leere) Black-List im PCI(e)-Root-Controller die PC-Architektur weniger beeinflusst als die Einführung von HyperTransport oder QPI und da läuft auch noch ein uraltes MS-DOS drauf.

Auf dem 27C3 (hoffentlich krieg ich noch ne Karte...) wird es einen Vortrag geben zu Angriffen durch Mischung von 32- und 64-Bit-Code in einer Anwendung und ungewöhnliches Verwenden von Syscalls in beiden Modi. Ähnlich kann man es ja auf ARM (mit ARM und Thumb-Code) oder MIPS (da gibt's jetzt auch einen kleineren Code) übertragen.
Okay, das der 32Bit/64Bit-Mischbetrieb sicher nicht einfach zu implementieren ist im OS und dort ne Menge Fehlerpotential drin steckt ist klar aber was damit die Code-Größe zu tun hat kann ich mir echt nicht erklären. Den Syscall-Handlern kann doch völlig wurscht sein woher der Syscall kommt (das sollte auf die IRQ-Handler auch zutreffen), Hauptsache die Parameter usw. stimmen. Höchstens die Exception-Handler die den Code analysieren müssen um genau zu ermitteln was schief gelaufen ist sollten sich für den Code interessieren.

Nehmen wir an es würde einem Angreifer auf meiner Plattform gelingen sich zwischen einem Ethernet-Treiber und dem IP-Prozess zu drängeln (mal ganz egal wie das gehen soll), dann hat der Angreifer aber noch keinen wahlfreien Zugriff auf die Busmasterfunktionen des Ethernet-Controllers sondern kann sich nur Daten in seine eigenen Segmente schreiben lassen.
Richtig, weil du - und das meinte ich mit "komisches Zeug" - verschiedene Adressräume hast. Und zwar hardwareseitig, wenn du es wünschst. Bei Flatmem-Systemen gibt es ja nur einen und den kannst du aus Hardwaresicht nicht mit Paging erschlagen - eben weil DMA und so. Da braucht es dann die Fangschaltung von oben, die ist aber nicht in heutigen Systemen drin.
Auch auf einem Flat-Memory-System sollte ein Treiber nur die physischen Pages von seinem Gerät lesen/schreiben lassen können die er auch virtuell erreichen kann, der Syscall der ihm die physischen Adressen verrät (und die entsprechenden Pages im RAM festlockt) funktioniert ja nur mit gültigen virtuellen Adressen.

Bevor du in Ada anfangen kannst zu programmieren, bist du erstmal ziemlich lange damit beschäftigt, die Aufgabe und ihre Einzelteile und Abhängigkeiten zu beschreiben. Darum ist Ada ja auf Korrektheit prüfbar (im Gegensatz zu den meisten Programmiersprachen), aber der Programmierer muss dafür schon enormen Aufwand treiben und er darf nicht mit unscharfen Aufgabenstellungen beworfen werden.
In meiner idealisierten Traumwelt sollte jedes Projekt so ablaufen. ;)

Run-Time-Boundary-Checks will man ja mit so Dingen wie Java (virtuellen maschinen) oder .NET (managed code) erschlagen, im Endeffekt hab ich heute ne Statistik gefunden, wonach irgendwas um die Hälfte aller Sicherheitslücken auf Buffer Overflow-Angriffe zurückgeht.
Diese Statistik glaube ich gern, deswegen ist es IMHO ja so wichtig das da endlich mal effektive Maßnahmen ergriffen werden. Auch wenn das zum Schluss nur bedeutet das die bösen Leute sich auf etwas anderes stürzen.

Du trennst also nicht zwischen der Berechtigung des Zugriffs und der Berechtigung der Wunschaktion.
Doch. An den VFS (um mal ein gutes Beispiel zu nutzen) kann erstmal jeder seine Anfragen schicken, schließlich muss ja jeder Prozess in der Lage sein mit Dateien arbeiten zu können, aber ob die gewünschte Aktion auch tatsächlich durchgeführt wird entscheidet der VFS selber.

Gibt es die Möglichkeit, die IPC-IDs zu bruteforcen?
Ja. Siehst Du darin ein Problem? In einem klassischen Monolithen kann ja auch jeder Prozess erstmal so viele Syscalls aufrufen wie er will und prinzipiell damit alle CPU-Ressourcen belegen. Mir ist auch nicht bekannt das es ein OS gibt das da versucht zu reglementieren wie viele Syscalls ein Prozess machen darf, das erscheint mir auch nicht sehr sinnvoll. Als Strafe könnten meine Services ja bei unerlaubten/ungültigen Anfragen einfach ein delay(500) machen bevor der PopUp-Thread sich mit einem Fehler-Code für den Client beendet, auf Grund der blockierenden Natur des synchronen IPC würde das einen Prozess der eine DoS-Attake probiert schon mal ziemlich ausbremsen (ohne die guten Prozesse zu beeinträchtigen). Dieser böse Prozess könnte dann zwar ne riesige Menge an Threads erstellen um das auszugleichen aber da dürfte er dann ziemlich bald ins Visier vom OOM-Killer geraten. Da fällt mir ein das ich den Speicher den die PopUp-Threads benötigen (Stack und TLS) nicht auf das Konto des Services buchen sollte sondern auf das Konto des Client, damit auch wirklich der richtige Prozess bekanntschaft mit dem OOM-Killer macht. Meine Services sollen bestimmen können wie viele PopUp-Threads pro Message-Target (also pro angebotenem Service) maximal erstellt werden dürfen (weitere Anfragen werden gequeued) was dann doch eine gewisse Art von DoS-Attake ermöglicht, weil aber wieder der Client-Thread für jede Anfrage blockiert ist das nicht so einfach nutzbar. Ein Problem sind die asynchronen IPC-Services aber darüber läuft IMHO nichts kritisches.

Ist halt ne Designvorgabe, dass man keine "anonymen" IPC-Nachrichten schicken kann.
Diese Vorgabe gibt es bei mir auch, dem PopUp-Thread wird vom Kernel immer die Prozess-ID des Clients mitgegeben. Anonyme IPC-Kommunikation wäre ein echter Angriffspunkt und sollte daher nicht möglich sein.

Wenn du potentiell gefährlichen Prozessen verbietest, einen Port zu bekommen, dann verhinderst du effektiv die IPC-Kommunikation; für die Sicherheit ist das sicherlich förderlich.
Damit sind diese Prozesse auf einem Micro-Kernel aber von allem abgeschnitten und können eigentlich gar nichts tun, das erscheint mir irgendwie sinnlos.

Wenn du eine Syscall-API hast, die vollkommen asynchron und mit Callbacks arbeitet, wie z.B. das X11-Protokoll, dann ist jeder Client irgendwo ein Anbieter für so eine Callback-Struktur.
Das ist natürlich richtig, aber das betrifft zumindest nicht die Basics.


Ich vergesse immer das du ja synchrones IPC hast, also auf jeden Fall auf eine Antwort "gewartet" wird.
Also gerade das die Antwort immer automatisch den richtigen Client erreicht und der Client nichts spezielles dafür machen muss ist IMHO der wesentliche Vorteil von synchronem IPC. Genau deswegen möchte ich das auch unbedingt haben.

Du hast aber sehr wohl eine Art Rechteüberprüfung, denn es ist ganz genau festgelegt wer eine Nachricht bekommt, das ist bei mir nicht so.
Naja, indirekt schon. Bei mir sind Anfrage und Antwort bei einem synchronen IPC-Vorgang immer zusammenhängend, weder der Client noch der Service können da irgendetwas gegen tun. Es ist mir auch sehr wichtig das selbst der Client nicht durch den Service einfach so angegriffen werden kann, der Service darf nur mit genau dem Speicher des Client arbeiten den der Client auch explizit dafür anbietet sonst absolut nichts.

Und genau das wäre auch ein Angriffspunkt meines IPC Systems. Denn man könnte eine DoS Attacke auf jeden Clienten machen, weil der muss ja die Nachricht auf jeden Fall erstmal abholen um sie dann verwerfen zu können.
Eben. Asynchrones IPC ist IMHO eine leichte Beute für DoS-Attacken. Ich wette tyndur lahm zu legen ist extrem simpel (schon allein das LIFO-Prinzip der Signal-Handler macht sowas einfach zu einfach) und mit dem derzeitigen IPC-Konzept gibt es IMHO da absolut keine Möglichkeit was dagegen zu tun.

Im Endeffekt verschiebe ich damit die Kontrolle ob jemand eine Nachricht an einen Port schicken darf, auf den Clienten (dieser kann ja anhand des Senders überprüfen ob die Nachricht von einer vernünftigen Quelle kommt).
Mag sein der der Client diese Prüfung recht simpel erledigen kann, trotzdem bin ich wirklich nicht der Meinung das die Prüfung dort hin gehört. Wenn ich einen Service programmiere mache ich mir automatisch ein paar Gedanken über dessen Sicherheit (so sollte es zumindest sein) aber bei einem reinem Client (z.B. ein simples Hello-World) macht man sich üblicherweise keine Gedanken über sowas.

aber eine Nachricht auf die ein "Port" wartet (also synchrones IPC) können immer in die Queue.
Auch wenn diese bereits voll ist?


Grüße
Erik
Reality is that which, when you stop believing in it, doesn't go away.

FlashBurn

  • Beiträge: 844
    • Profil anzeigen
Gespeichert
« Antwort #136 am: 24. November 2010, 13:43 »
Zitat von: erik
Auch wenn diese bereits voll ist?
Deswegen das "immer"! Es wird nicht geprüft ob die Queue die Länge die max. erlaubt ist erreicht hat, sondern diese Nachricht wird immer an den Queue-Anfang gepackt und der Client wird aufgeweckt.

Was die DoS-Attacke betrifft, wenn du als Client eh immer nur Anfrage-Antwort Sachen per IPC machst, gibt es eigentlich keine Möglichkeit eine DoS-Attacke durchzuführen, weil der Client nie seine Port-Queue abfragt.

Problematisch wird es halt erst wenn du nen grafisches Progg hast. Denn der GUI-Server kann/muss dir ja Nachrichten schicken, das z.B. gerade eine Taste gedrückt wurde.
Dem würde ich so entgegenwirken, das man sagt, eine Nachricht von einem Port auf den der Client wartet darf immer in die Queue (ohne Rechteprüfung) und eine Nachricht auf die der Client nicht wartet, da werden Rechteüberprüfungen gemacht und da sage ich halt einfach, das nur Nachrichten vom GUI-Server kommen dürfen. Das sollte nicht angreifbar sein und einfach ist es auch.

Svenska

  • Beiträge: 1 792
    • Profil anzeigen
Gespeichert
« Antwort #137 am: 24. November 2010, 23:29 »
Hallo,

Der Grund für die Implementation im Kernel (bei Monolithen) ist Performance (Windows) und eine gemeinsame API als Basis für mehrere Treiber (Linux). Gleicher Grund, wie die Implementation von WLAN im Kernel, damit nicht jeder Treiber seinen eigenen WLAN-Stack mitbringen muss (wie bei Windows XP) und damit jeder Treiber, egal von wem, die gleichen Dinge unterstützen kann (WPA, HostAP).
Warum muss man für ein einheitliches Interface solche Dinge unbedingt in den Kernel verlegen? Auch unterschiedliche eigenständige Treiber sollten ein einheitliches Interface anbieten können. Sowas wie der WLAN-Stack könnte ein eigenständiger Service sein der allen WLAN-HW-Treibern zur Verfügung steht. Ich weiß ich denke zu sehr in Richtung Micro-Kernel.
Ja, ich vergleiche immer mit einem monolithischen Kernel. Und alles, was da drin ist, ist für mich "Kernel", egal ob es nun als eigenständiger Service implementiert ist oder nicht. Sieh das bitte bei mir immer als Prämisse. ;-)

Aber insbesondere bei Grafiktreibern sollte die Implementation aus Geschwindigkeitsgründen im Kernel selbst stattfinden - siehe Windows NT 3.5 gegen NT 4. Dort wurde es auch vom Userspace in den Kernel verfrachtet, weil es einfach zu langsam war.

Also sowas wie das TPM kommt für mich persönlich schon ganz grundsätzlich nicht in Frage, dieses TPM ist in meinen Augen absolut nicht vertrauenswürdig (jedenfalls nicht für den Endanwender).
TPMs sind von Grund auf als sichere Technologie implementiert. Du kannst denen schon vertrauen. Als Grundlage, mit der alles steht oder fällt, dient hierbei der Rechner selbst. Ich lehne das allerdings auch ab.

Also wenn dann würde ich auf jeden Fall eine SmartCard oder ein USB-Dongle bevorzugen, meinetwegen auch ein unter die Haut implantierter RF-ID-Chip oder sowas in der Art, aber diese Komponenten müssen für mich als Anwender absolut vertrauenswürdig sein und sollten nicht permanent mit dem System verbunden sein.
Wenn du dich gegen das System authentifizieren willst, muss der Schlüssel an das System gebunden sein. Willst du dich gegen den Menschen authentifizieren, muss der Schlüssel an den Menschen gebunden sein.

Ich bin der Meinung das eine (default leere) Black-List im PCI(e)-Root-Controller die PC-Architektur weniger beeinflusst als die Einführung von HyperTransport oder QPI und da läuft auch noch ein uraltes MS-DOS drauf.
Auch wieder wahr.

Den Syscall-Handlern kann doch völlig wurscht sein woher der Syscall kommt (das sollte auf die IRQ-Handler auch zutreffen), Hauptsache die Parameter usw. stimmen. Höchstens die Exception-Handler die den Code analysieren müssen um genau zu ermitteln was schief gelaufen ist sollten sich für den Code interessieren.
Das Problem sind "die Parameter usw. stimmen". Denn es kann (und wird) Unterschiede geben, ob der Syscall nun im 32- oder im 64-Bit-Modus aufgerufen wird. Möglicherweise sind das sogar zwei verschiedene Syscalls, die aus einem gemeinsamem Template erzeugt wurden. Allerdings hab ich dazu keine Ahnung mehr.

Auch auf einem Flat-Memory-System sollte ein Treiber nur die physischen Pages von seinem Gerät lesen/schreiben lassen können die er auch virtuell erreichen kann, der Syscall der ihm die physischen Adressen verrät (und die entsprechenden Pages im RAM festlockt) funktioniert ja nur mit gültigen virtuellen Adressen.
Das erfordert aber, dass selbst ein Busmaster keinen Direktzugriff auf den RAM bekommen kann (was ja der Sinn von DMA ist). Du brauchst halt diese hardwareseitige Fangschaltung.

Du trennst also nicht zwischen der Berechtigung des Zugriffs und der Berechtigung der Wunschaktion.
Doch. An den VFS (um mal ein gutes Beispiel zu nutzen) kann erstmal jeder seine Anfragen schicken, schließlich muss ja jeder Prozess in der Lage sein mit Dateien arbeiten zu können, aber ob die gewünschte Aktion auch tatsächlich durchgeführt wird entscheidet der VFS selber.
Achso, also trennst du nicht im IPC, sondern in den Services selbst. Woher weiß das VFS denn, ob ein Prozess eine bestimmte Aktion ausführen darf - die Information muss ja von irgendwo kommen?

Gibt es die Möglichkeit, die IPC-IDs zu bruteforcen?
Ja. Siehst Du darin ein Problem? In einem klassischen Monolithen kann ja auch jeder Prozess erstmal so viele Syscalls aufrufen wie er will und prinzipiell damit alle CPU-Ressourcen belegen.
Ganz simpel wäre die Lösung, dass ein Prozess nach jedem Syscall seine Zeitscheibe verliert. ;-)

Wenn du potentiell gefährlichen Prozessen verbietest, einen Port zu bekommen, dann verhinderst du effektiv die IPC-Kommunikation; für die Sicherheit ist das sicherlich förderlich.
Damit sind diese Prozesse auf einem Micro-Kernel aber von allem abgeschnitten und können eigentlich gar nichts tun, das erscheint mir irgendwie sinnlos.
Du kannst den Port ja zeitweise sperren, das kann die Applikation selbst tun ("ich weiß, dass ich hier keine Syscalls machen werde, also verbiete ich mir das selbst"). Ist genau das gleiche, wie wenn Anwendungen als root gestartet werden, ein Socket öffnen, und dann als User "nobody" weiterlaufen (Apache).

Gruß,
Svenska

kevin

  • Administrator
  • Beiträge: 2 767
    • Profil anzeigen
Gespeichert
« Antwort #138 am: 25. November 2010, 09:29 »
Auch auf einem Flat-Memory-System sollte ein Treiber nur die physischen Pages von seinem Gerät lesen/schreiben lassen können die er auch virtuell erreichen kann, der Syscall der ihm die physischen Adressen verrät (und die entsprechenden Pages im RAM festlockt) funktioniert ja nur mit gültigen virtuellen Adressen.
Das erfordert aber, dass selbst ein Busmaster keinen Direktzugriff auf den RAM bekommen kann (was ja der Sinn von DMA ist). Du brauchst halt diese hardwareseitige Fangschaltung.
Hm, nennen wir das Kind doch beim Namen: Deine "hardwareseitige Fangschaltung" ist eine IOMMU, oder? Wenn du dich auf sehr aktuelle Rechner beschränkst, kannst du das voraussetzen. Oder wenigstens dann einschalten, wenn eine vorhanden ist.

Zitat
Gibt es die Möglichkeit, die IPC-IDs zu bruteforcen?
Ja. Siehst Du darin ein Problem? In einem klassischen Monolithen kann ja auch jeder Prozess erstmal so viele Syscalls aufrufen wie er will und prinzipiell damit alle CPU-Ressourcen belegen.
Ganz simpel wäre die Lösung, dass ein Prozess nach jedem Syscall seine Zeitscheibe verliert. ;-)
Öhm, ja. Geschwindigkeit interessiert ja keinen. ;)
Thou shalt not follow the NULL pointer, for chaos and madness await thee at its end.

erik.vikinger

  • Beiträge: 1 277
    • Profil anzeigen
Gespeichert
« Antwort #139 am: 25. November 2010, 12:08 »
Hallo,


Ja, ich vergleiche immer mit einem monolithischen Kernel. Und alles, was da drin ist, ist für mich "Kernel", egal ob es nun als eigenständiger Service implementiert ist oder nicht. Sieh das bitte bei mir immer als Prämisse.
Okay, auf einen Micro-Kernel-OS übertragen bedeutet das eben Kernel + Personality. Aber selbst bei einem Monolithen besteht ein nutzbares OS aus deutlich mehr als nur dem nackten Kernel. Trotzdem bin ich nicht der Meinung das man Funktionalität nur für ein "einheitliches Interface" in den Kernel verschieben muss, sowas geht doch auch anders.

Aber insbesondere bei Grafiktreibern sollte die Implementation aus Geschwindigkeitsgründen im Kernel selbst stattfinden
Warum "sollte"? Sollte man nicht lieber ordentliches und schnelles IPC implementieren? Nur weil MS das nicht hinbekommen hat heißt es nicht dass das nicht geht.

TPMs sind von Grund auf als sichere Technologie implementiert.
Ja, für den Main-Board-Hersteller und seine Geschäftspartner aus der Content-Industrie vielleicht aber für mich als Benutzer und legaler Eigentümer des PCs ist das TPM in keinster weise Vertrauenswürdig. Ließ Dir dazu mal die Spezifikation durch, das TPM ist vom Konzept her schon nicht dazu gedacht das der Benutzer seinem PC vertrauen kann.

Du kannst denen schon vertrauen.
Dem würde ich nicht mal meine Sammlung an Kuchenrezepten anvertrauen.

... Ich lehne das allerdings auch ab.
Dann brauchen wir ja nicht weiter über dieses dämliche TPM diskutieren.

Das Problem sind "die Parameter usw. stimmen". Denn es kann (und wird) Unterschiede geben, ob der Syscall nun im 32- oder im 64-Bit-Modus aufgerufen wird. Möglicherweise sind das sogar zwei verschiedene Syscalls, die aus einem gemeinsamem Template erzeugt wurden. Allerdings hab ich dazu keine Ahnung mehr.
Über was schreiben wir da eigentlich? Über die Unterschiede zwischen dem 32Bit-Mode und dem 64Bit-Mode bei x86 oder über die unterschiedlichen OpCode-Größen bei ARM und MIPS (und etlichen anderen CPUs auch)? Für erstes sollte man meiner Meinung nach alle Syscalls doppelt haben, auch wenn die 32Bit-Versionen wohl nur eine Umsetzung auf 64Bit vornehmen (mal außer acht gelassen wie komplex so ne Umsetzung manchmal sein kann). Für zweites ist das für die Syscalls völlig egal weil die CPU ja immer noch im selben Modus ist (oder zumindest noch in einem Modus mit der selben Bit-Breite). Zumindest der ARM-Mode und der Thumb-Mode machen da keine Unterschiede, das ist dort eine Sache die eigentlich nur den Befehlsdecoder betrifft.

Auch auf einem Flat-Memory-System sollte ein Treiber nur die physischen Pages von seinem Gerät lesen/schreiben lassen können die er auch virtuell erreichen kann, der Syscall der ihm die physischen Adressen verrät (und die entsprechenden Pages im RAM festlockt) funktioniert ja nur mit gültigen virtuellen Adressen.
Das erfordert aber, dass selbst ein Busmaster keinen Direktzugriff auf den RAM bekommen kann (was ja der Sinn von DMA ist). Du brauchst halt diese hardwareseitige Fangschaltung.
Ich wollte eigentlich darauf hinaus das ein (fehlerfreier) Treiber seiner HW erstmal nur die physischen Adressen mitteilt die er selber von einem entsprechenden Syscall bekommen hat. Wenn im Treiber aber aktiver Schad-Code (oder ein Bug der sich von außen passend nutzen lässt) ist der dann anfängt den physischen Speicher (mit Hilfe seiner DMA-fähigen HW) zu analysieren dann ist eh alles zu spät, da könnte dann nur noch eine HW-Schaltung was helfen. Aus diesem Grund werde ich zumindest die Black-List-Version umsetzen damit wenigstens der Kernel selber intakt bleibt.
Eine richtige IOMMU ist natürlich auch was feines, kostet aber dann tatsächlich etwas Performance. Das Problem ist das diese IOMMU ja eigentlich sehr viele verschiedene Kontexte parallel beherrschen muss (für jedes Peripherie-Gerät einen eigenen) um auch wirklich die gewünschte Flexibilität zu erreichen und dafür ist dann wieder einiges an Speicher erforderlich (zusätzlich müssen diese Paging-Tables gepflegt werden). Wimre war die ursprüngliche Idee hinter der IOMMU die Benutzung von echten HW-Komponenten direkt aus einer VM heraus ohne dass das darin laufende OS (und auch die HW) überhaupt wissen muss das es in einer VM läuft.

Achso, also trennst du nicht im IPC, sondern in den Services selbst.
Richtig, sonst müsste ja der Kernel selber die Rechteverwaltung mitmachen und das finde ich bei einem Micro-Kernel eher unpraktisch. Mein Kernel soll nur ein paar ganz elementare Rechte kennen und benutzen, z.B. ob ein Prozess seinen Speicher im physischen RAM festpinnen darf um so die physische Adresse dieses Speichers zu erfahren (Treiber benötigen das) oder die Rechte bestimmte Syscalls zu benutzen.

Woher weiß das VFS denn, ob ein Prozess eine bestimmte Aktion ausführen darf - die Information muss ja von irgendwo kommen?
Das ist allerdings eine gute Frage, darüber hab ich mir noch nicht all zu viele Gedanken gemacht. Da ich eh für das Erstellen neuer Prozesse einen extra Service hab könnte dieser auch ne kleine Datenbank führen die zu jedem Prozess die zugehörigen Rechte + User usw. enthält, da muss ich mal in Ruhe drüber nachdenken.

Ganz simpel wäre die Lösung, dass ein Prozess nach jedem Syscall seine Zeitscheibe verliert.
Ich schließe mich hierzu taljeth's Meinung an, das wäre definitiv zu simpel.

Du kannst den Port ja zeitweise sperren, das kann die Applikation selbst tun ("ich weiß, dass ich hier keine Syscalls machen werde, also verbiete ich mir das selbst").
Also für Syscalls erscheint mir so ein (selbst auferlegtes) Verbot nicht geeignet, dann könnte das Programm ja nicht mal mehr ein Error-Log führen oder Speicher anfordern oder andere ganz banale Dinge erledigen. Wenn dann muss man sowas schon etwas feiner unterteilen (z.B. keine Netzwerk-Kommunikation also TCP/UDP/usw.).

Ist genau das gleiche, wie wenn Anwendungen als root gestartet werden, ein Socket öffnen, und dann als User "nobody" weiterlaufen (Apache).
Das funktioniert aber nur weil auch der User "nobody" immer noch verschiedene Dinge machen darf.


Grüße
Erik
Reality is that which, when you stop believing in it, doesn't go away.

 

Einloggen