Spezifikation der Semantik einer generischen
Transcrição
Spezifikation der Semantik einer generischen
T ECHNISCHE U NIVERSITÄT M ÜNCHEN FAKULTÄT FÜR I NFORMATIK Diplomarbeit Spezifikation der Semantik einer generischen Autorisierungskomponente als Basis für eine verbesserte Implementierung Thomas Nietsch Abgabedatum: 31. August 2004 Aufgabensteller: Prof. Dr. Florian Matthes Betreuer: Dipl.-Ing. Kathrin Lehmann Prof. Dr. Johannes Siedersleben∗ ∗ sd&m AG, München Ich versichere, dass ich diese Diplomarbeit selbständig verfasst und nur die angegebenen Quellen und Hilfsmittel verwendet habe. München, den 31. August 2004 ................................... Kurzfassung Autorisierungskomponenten erbringen Basisdienste für die Kontrolle von Zugriffen auf schützenswerte Ressourcen. Ausgehend von einem fertigen Produkt entwirft und spezifiziert die vorliegende Arbeit eine generische Autorisierungskomponente anhand ihrer Schnittstellen. Die Komponente bietet Dienste zur Authentifizierung und Sitzungsverwaltung an, zur Administration von Benutzerdaten und Zugriffsrechten und zur Abfrage der festgelegten Rechte. Lehrstuhl für Software Engineering betrieblicher Informationssysteme Inhaltsverzeichnis 1. Einführung 1.1. Thematische Einordnung . 1.1.1. IT-Sicherheit . . . 1.1.2. Design by Contract 1.2. Motivation und Ziel . . . . 1.3. Fahrplan . . . . . . . . . . 1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2. Softwaretechnische Grundlagen 2.1. Quasar . . . . . . . . . . . . . . . . 2.2. Spezifikation von Schnittstellen . . 2.2.1. Die Schnittstelle als Vertrag 2.2.2. Spezifikationselemente . . . 2.2.3. Spezifikationstechniken . . 2.2.4. QSL . . . . . . . . . . . . . 1 1 2 2 3 5 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3. Grundlagen einer Sicherheitsarchitektur 3.1. Authentifizierung . . . . . . . . . . . . . . . . . . . 3.2. Autorisierung und Zugriffskontrolle . . . . . . . . . 3.2.1. Grundlegende Autorisierungsmechanismen 3.2.2. Implizite Rechte . . . . . . . . . . . . . . . 3.2.3. Negative Rechte und Konsistenz . . . . . . . 3.2.4. Zugriffskontrollstrategien . . . . . . . . . . 3.3. Vertrauens(un)würdige Software . . . . . . . . . . 3.4. JAAS – Informationssicherheit in der Java-Welt . . 3.4.1. Sandkastenspiele . . . . . . . . . . . . . . . 3.4.2. Codezentrierte Autorisierung . . . . . . . . 3.4.3. Know your principals . . . . . . . . . . . . . 5 7 7 8 10 11 15 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15 16 17 18 19 20 22 25 25 26 27 v Inhaltsverzeichnis 4. QuasarAuthorization 29 4.1. Beschreibung der Komponente . . . . . . 4.1.1. Funktionsumfang . . . . . . . . . . 4.1.2. Datenmodell und Außensicht . . . 4.1.3. Berechtigungs-Cache . . . . . . . . 4.2. Verbesserungspotential . . . . . . . . . . . 4.2.1. Architektur und Implementierung . 4.2.2. Spezifikation . . . . . . . . . . . . 4.3. Fazit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5. BasicAuthorization 5.1. Funktionsumfang . . . . . 5.2. Datenmodell . . . . . . . 5.3. Schnittstellen . . . . . . . 5.3.1. Operativer Betrieb 5.3.2. Instrumentierung . 5.3.3. Administration . . 29 29 30 35 36 36 38 39 41 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6. AdvancedAuthorization 6.1. Funktionsumfang . . . . . . . 6.2. Datenmodell . . . . . . . . . 6.3. Schnittstellen . . . . . . . . . 6.3.1. Technische Umgebung 6.3.2. Operativer Betrieb . . 6.3.3. Instrumentierung . . . 6.3.4. Administration . . . . 6.4. Potential und Grenzen . . . . 41 41 42 44 45 46 49 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49 49 52 52 53 54 62 64 7. Zusammenfassung und Ausblick 67 A. Glossar 69 B. QSL-Spezifikationen 73 B.1. Entitätstypen der Basic-/AdvancedAuthorization . . . . . . . . . . . . . B.2. Schnittstellen der BasicAuthorization . . . . . . . . . . . . . . . . . . . B.3. Schnittstellen der AdvancedAuthorization . . . . . . . . . . . . . . . . 73 75 81 Literaturverzeichnis 95 Index 99 vi Abbildungen und Tabellen Abbildungsverzeichnis 3.1. Ordnung auf Zugriffsarten . . . . . . . . . . . . . . . . . . . . . . . . . 3.2. Fundamentale Zugriffskontrollfunktionen . . . . . . . . . . . . . . . . 3.3. Sicherheitsarchitektur . . . . . . . . . . . . . . . . . . . . . . . . . . . 18 23 24 4.1. QuasarAuthorization: Datenmodell (Teil 1) . . . . . . . . . . . . . . . . . . 4.2. QuasarAuthorization: Datenmodell (Teil 2) . . . . . . . . . . . . . . . . . . 4.3. QuasarAuthorization: Außensicht . . . . . . . . . . . . . . . . . . . . . . . 31 31 34 5.1. BasicAuthorization: Datenmodell . . . . . . . . . . . . . . . . . . . . . . . 5.2. BasicAuthorization: Außensicht . . . . . . . . . . . . . . . . . . . . . . . . 5.3. BasicAuthorization: Beispielkomposition . . . . . . . . . . . . . . . . . . . 42 43 43 6.1. 6.2. 6.3. 6.4. 6.5. 6.6. 6.7. . . . . . . . 50 52 55 55 57 57 60 3.1. Zugriffsmatrix . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17 A.1. Gegenüberstellung der Synonyme . . . . . . . . . . . . . . . . . . . . . 72 Datenmodell . . . . . . . . . . . . . . Außensicht . . . . . . . . . . . . . . . Login-Sequenz (allgemein) . . . . . . . . . . . . . . . . . Konfiguration für klassische Passwort-Authentifizierung . . Login-Sequenz mit klassischer Passwort-Authentifizierung Konfiguration für JAAS-basierte Authentifizierung . . . . . Beispiel zur Linearisierung . . . . . . . . . . . . . . . . . . AdvancedAuthorization: AdvancedAuthorization: . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Tabellenverzeichnis vii Verzeichnis der Quelltexte 2.1. 2.2. 2.3. 2.4. 5.1. 5.2. 5.3. 5.4. 5.5. 6.1. 6.2. 6.3. 6.4. 6.5. 6.6. 6.7. B.1. B.2. B.3. B.4. B.5. B.6. B.7. B.8. B.9. B.10. B.11. B.12. B.13. B.14. Grundgerüst einer QSL-Schnittstellenspezifikation . . . . . . Allgemeine QSL-Methodenspezifikation . . . . . . . . . . . . QSL-Methodenspezifikation (Beispiel) . . . . . . . . . . . . . QSL-Testfall (Beispiel) . . . . . . . . . . . . . . . . . . . . . . BasicAuthorization: Authentication.authenticate . . . . . . BasicAuthorization: AuthorizationCheck.mayPerform . . . . . BasicAuthorization: Auszug aus ResourceDefinition . . . . . . BasicAuthorization: Implementierung von ResourceDefinition BasicAuthorization: Auszug aus UserAdmin . . . . . . . . . . . . AdvancedAuthorization: Auszug aus Assignment . . . . . . . . . AdvancedAuthorization: Auszug aus SessionManagement . . . . AdvancedAuthorization: Auszug aus Authentication . . . . . . AdvancedAuthorization: Auszug aus ResourceDefinition . . . . AdvancedAuthorization: Auszug aus Policy . . . . . . . . . . . . AdvancedAuthorization: Auszug aus PositiveOverridePolicy . AdvancedAuthorization: Auszug aus UserAdmin . . . . . . . . . . AuthorizationEntity . . . . . . . . . . . . . . . . . . . . . AbstractUser . . . . . . . . . . . . . . . . . . . . . . . . . . User . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Group . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . AbstractRight . . . . . . . . . . . . . . . . . . . . . . . . . Right . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Role . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Resource . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Operation . . . . . . . . . . . . . . . . . . . . . . . . . . . . Assignment . . . . . . . . . . . . . . . . . . . . . . . . . . . SessionTicket . . . . . . . . . . . . . . . . . . . . . . . . . BasicAuthorization: Authentication . . . . . . . . . . . . . . . BasicAuthorization: AuthorizationCheck . . . . . . . . . . . . BasicAuthorization: ResourceDefinition . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11 13 13 14 44 45 45 47 48 50 53 55 58 59 61 63 73 73 73 73 74 74 74 74 74 74 75 75 75 76 ix Verzeichnis der Quelltexte B.15. B.16. B.17. B.18. B.19. B.20. B.21. B.22. B.23. B.24. B.25. B.26. x UserAdmin . . . . . . . . . . AuthorizationAdmin . . . . AdvancedAuthorization: Repository . . . . . . . . AdvancedAuthorization: Logging . . . . . . . . . . AdvancedAuthorization: SessionManagement . . . AdvancedAuthorization: AuthorizationCheck . . AdvancedAuthorization: Authentication . . . . . AdvancedAuthorization: ResourceDefinition . . AdvancedAuthorization: Policy . . . . . . . . . . AdvancedAuthorization: PositiveOverridePolicy AdvancedAuthorization: UserAdmin . . . . . . . . AdvancedAuthorization: AuthorizationAdmin . . BasicAuthorization: BasicAuthorization: . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76 79 81 82 83 83 84 84 86 86 87 91 »Hey, I don’t believe that any system is totally secure!« WarGames (Metro-Goldwyn-Mayer, 1983) 1. Einführung Betriebliche Informationssysteme bilden das Kerngeschäft von sd&m (software design & management). Das Software- und Beratungshaus entwickelt und überarbeitet solche Systeme im Projektgeschäft für Unternehmen und Behörden. Bei jedem größeren System gibt es Teile, die man für den Kunden maßschneidert, und andere, für die man besser auf (fast) fertige Software zurückgreift. Wer Programmcode wiederverwendet, bringt ein Projekt (zumindest theoretisch) schneller und günstiger ans Ziel. sd&m Research – die Forschungs- und Entwicklungsabteilung von sd&m – hat mehrere wiederverwendbare Standardkomponenten für Informationssysteme entwickelt. Eine davon – die Autorisierungskomponente QuasarAuthorization – ist der Ausgangspunkt dieser Arbeit. 1.1. Thematische Einordnung IT-Sicherheit ist nicht nur in der Informatik ein Schlüsselbegriff, und wer verlässliche Software bauen will, ist gut beraten, Design by Contract zu betreiben. Beides sind Schlagworte, die den Inhalt der vorliegenden Arbeit betreffen. 1.1.1. IT-Sicherheit Sicherheit in der Informationstechnik, kurz IT-Sicherheit, ist auf den ersten Blick ein unübersichtliches Thema. Eine grundlegende Unterscheidung (nach [Eck03]) ist jene zwischen Funktionssicherheit (safety) und Informationssicherheit (security). Ein System arbeitet funktionssicher, wenn es jederzeit tut, was es soll. Wenn die Bordelektronik eines Autos ohne Anlass den Airbag auslöst, so ist das ein Beispiel für ein System, das seine Benutzer in Gefahr bringt, weil es nicht tut, was es soll. Ein System arbeitet informationssicher, wenn es keine unerlaubten Zugriffe auf schützenswerte Informationen zulässt. Systematische Zugriffskontrolle ist ein Weg zu diesem Ziel. In umfangreichen Systemen ist es sinnvoll, die dafür notwendigen Vorkehrungen in einer eigenen Komponente zusammenzufassen. Man spricht dann von einer Autorisierungskomponente. 1 1. Einführung 1.1.2. Design by Contract Ein IT-System, das undefinierte Zustände annehmen kann, ist potentiell anfällig für unerlaubte Zugriffe. Funktionssicherheit ist daher eine Voraussetzung für Informationssicherheit. Wenn Funktionssicherheit heißt, dass Software tut, was sie soll, dann stellt sich die Frage, wie man den Soll-Zustand festlegt. Idealerweise verfasst man dazu ein Dokument mit einer präzisen Leistungsbeschreibung: die Spezifikation. Design by Contract bedeutet, Software auf der Grundlage von Spezifikationen zu entwerfen. Analog zu einem Vertrag (contract) regelt die Spezifikation das Verhältnis zwischen zwei Vertragspartnern: Der eine erbringt eine (Rechen-)Leistung, der andere nimmt diese Leistung in Anspruch. Ein guter Vertrag lässt möglichst wenig Raum für Missverständnisse. 1.2. Motivation und Ziel Die QuasarAuthorization ist eine mächtige, vielseitig einsetzbare Autorisierungskomponente. Allerdings ist sie kompliziert in der Handhabung: Das mitgelieferte HelloWorld-Beispiel erstreckt sich über rund 500 Codezeilen. Der Einsatz der Komponente fordert von den beteiligten Entwicklern einen hohen Einarbeitungsaufwand, der sich nur bei großen Projekten mit langer Laufzeit lohnt. Verbesserungswürdig ist auch die Spezifikation der QuasarAuthorization. Sie besteht nur aus informellen Beschreibungen und ist unvollständig. So erlaubt die Komponente die Definition beliebig komplizierter (auch widersprüchlicher) Zugriffsrechte; die Auswertungslogik ist jedoch nicht spezifiziert, sondern allein von der Implementierung abhängig. Die wenigsten Informationssysteme entstehen heute auf der grünen Wiese; in der Regel integriert man neue Systeme in eine bestehende (Sicherheits-)Infrastruktur. Die QuasarAuthorization verfügt jedoch nur über rudimentäre Möglichkeiten der Integration mit anderen Produkten. Das Ziel dieser Arbeit ist der Entwurf einer neuen Autorisierungskomponente mit der QuasarAuthorization als Ausgangspunkt; dabei soll die Handhabung gegenüber der existierenden Komponente vereinfacht werden. Der Entwurf beschränkt sich auf die von außen sichtbaren Schnittstellen (die Außensicht) der Komponente. Die Schnittstellen sind präzise zu spezifizieren. Die Auswertungslogik ist als Bestandteil der Schnittstellensemantik in der Spezifikation zu berücksichtigen. Außerdem sollen Integrationsmöglichkeiten des Entwurfs erörtert werden. Er ist hierzu mit dem Produkt JAAS (Java Authentication and Authorization Service) in Bezug zu bringen. 2 1.3. Fahrplan 1.3. Fahrplan Es folgen zwei Grundlagenkapitel: In Softwaretechnische Grundlagen klären wir den Begriff Quasar und stellen die verwendete Spezifikationstechnik vor. In Grundlagen einer Sicherheitsarchitektur geht es um den Einsatzzweck von Autorisierungskomponenten: Authentifizierung und Autorisierung als Fundamente der Zugriffskontrolle und damit für sichere Systeme. Hier wird auch JAAS vorgestellt. Kapitel 4 ist der QuasarAuthorization gewidmet. Kapitel 5 enthält den Entwurf einer Autorisierungskomponente mit eingeschränktem Funktionsumfang, die wir BasicAuthorization nennen. Das ist ein Etappenziel auf dem Weg zum eigentlichen Entwurf, den wir im darauf folgenden Kapitel vorstellen: AdvancedAuthorization. Anhang A ist ein Glossar der zentralen Begriffe und aller verwendeten Abkürzungen. Anhang B enthält die Schnittstellenspezifikationen der Komponentenentwürfe. 3 »A verbal contract isn’t worth the paper it’s written on.« (Samuel Goldwyn) 2. Softwaretechnische Grundlagen Die vorliegende Arbeit basiert auf einer Komponente namens QuasarAuthorization. Doch was ist eigentlich Quasar? Der folgende Abschnitt fasst das kurz zusammen. Danach führen wir in das Thema Spezifikation ein und stellen die für unsere Komponentenentwürfe verwendete Spezifikationssprache vor. 2.1. Quasar Quasar steht für Qualitätssoftwarearchitektur und definiert den gedanklichen und begrifflichen Rahmen für die Architektur der sd&m-Softwaresysteme. Quasar formuliert universell gültige Regeln und Baumuster für Software im Allgemeinen und für betriebliche Informationssysteme im Besonderen. Letztere sind meistens groß, teuer und langlebig.1 Vor allem aus der Langlebigkeit der Systeme (oft misst man die Lebensdauer in Jahrzehnten) ergeben sich hohe Anforderungen an ihre Wartbarkeit und Erweiterbarkeit – und damit an ihre Architektur. Das Referenzwerk zu Quasar ist [Sie04]. Die darin aufgestellten Regeln basieren auf zwei Prinzipien, die seit langem zu den Tugenden des Software-Engineering zählen, in der Praxis aber selten konsequent umgesetzt werden: • Trennung von Zuständigkeiten (separation of concerns): Jeder Baustein des Gesamtsystems hat einen definierten Aufgabenbereich.2 • Denken in Komponenten und Schnittstellen: Man untergliedert das Gesamtsystem in überschaubare Bausteine (Komponenten), die ausschließlich über wohldefinierte Kanäle (Schnittstellen) kommunizieren. Komponenten eignen sich zur Komposition, dem Zusammenfügen mehrerer Komponenten zu einer größeren Komponente. David Parnas beschreibt bereits in [Par72] die Anwendung dieser Entwurfsprinzipien anhand eines Beispielsystems und fasst zusammen: 1 2 vgl. [DMT04, Teil 1] Aus der UNIX-Welt stammt die ähnliche Philosophie one job, one tool: Jedes Werkzeug erledigt nur eine Aufgabe, aber die sehr gut. 5 2. Softwaretechnische Grundlagen „Every module [...] is characterized by its knowledge of a design decision which it hides from all others. Its interface [...] was chosen to reveal as little as possible about its inner workings.“ Die Trennung von Zuständigkeiten bestimmt die Aufgabenverteilung zwischen den Komponenten (Parnas nennt sie modules), und Schnittstellen (interfaces) abstrahieren von deren Implementierung. Wie legt man die Zuständigkeiten der Komponenten fest? Man bringt Software, die unterschiedlichen Zwecken dient und sich voraussichtlich unterschiedlich schnell ändern wird, in verschiedenen Komponenten unter. Jeder Entwickler sollte seine Komponente möglichst unabhängig von anderen Komponenten entwickeln können, und die nachträgliche Änderung einer Entwurfsentscheidung sollte Veränderungen an möglichst wenigen Komponenten nach sich ziehen (knowledge of a design decision). Das ist das Ziel. Als Weg dorthin schlägt Quasar die Bestimmung von Softwarekategorien vor, die die verschiedenen Aufgabenbereiche des Systems benennen. Nach welchen Kriterien unterscheidet man Softwarekategorien? Ein Kriterium, das sich bei jedem System anwenden lässt, ist die Abhängigkeit der Software von der Anwendungslogik und von den technischen Mitteln, mit denen die Anwendung implementiert wird. Daraus ergeben sich die Standardkategorien (oder Blutgruppen): A-Software ist allein durch die Anwendung bestimmt, T-Software allein durch die Technik. 0-Software ist unabhängig von Anwendung und Technik (z. B. String- oder Behälterklassen). AT-Software vermischt Anwendung und Technik und ist deshalb unerwünscht – doch ganz ohne AT geht es nicht: Software, die Objekte zwischen der A- und T-Welt transformiert, ist zwangsläufig AT-Software. Quasar ordnet Software in die Kategorie R (für Repräsentation) ein, wenn sie nur solchen Transformationen dient. Einer A-Komponente (Komponente, die ausschließlich aus A-Software besteht) ist es egal, ob eine Oracle- oder eine IBM-Datenbank die Datenhaltung besorgt; einer T-Komponente ist die Mehrwertsteuer gleichgültig. Die Einteilung in A und T ist grobkörnig, deshalb schlägt Quasar vor, anwendungsspezifische Softwarekategorien einzuführen: Lagerhaltung und Personalverwaltung fallen beide in die Kategorie A, doch es wäre nicht sinnvoll, sie in einer Komponente zu vereinen. Besser ist es, die Kategorien Lager und Personal zu definieren und dafür getrennte Komponenten vorzusehen. Aus Kategorien lässt sich eine Hierarchie aufbauen: Spezifischere Kategorien verfeinern allgemeinere. Die Kategorie 0 ist die allgemeinste und damit die Basis jeder Kategorienhierarchie. Bei einfachen Komponenten (das sind Komponenten, die nicht durch Komposition entstanden sind) strebt man an, dass sie nur aus Software einer einzigen Kategorie bestehen. Zusammengesetzte Komponenten dürfen zwar Software verschiedener Ka- 6 2.2. Spezifikation von Schnittstellen tegorien enthalten (schließlich kann man auch das Gesamtsystem wiederum als Komponente auffassen) doch man sortiert die verschiedenartige Software in getrennten Subkomponenten. Neben den Grundprinzipien beschreibt Quasar eine Standardarchitektur für Informationssysteme, die detaillierter ist als das Drei-Schichten-Modell aus Datenhaltung, Anwendungs- und Präsentationsschicht. Auch hier gibt es eine A-Architektur aus AKomponenten und eine T-Architektur, die von der technischen Infrastruktur (Hardware, Betriebssystem, Middleware) abstrahiert. Manche Elemente der T-Architektur eignen sich gut zur Wiederverwendung, weil sie in jedem Informationssystem vorkommen, z. B. Datenhaltung (Persistenz), Transaktionsverwaltung, grafische Benutzeroberfläche (GUI3 ). Quasar definiert für diese Elemente Standardschnittstellen. sd&m Research hat einige Standardkomponenten entwickelt, die diese Schnittstellen implementieren. Eine davon ist die Persistenzkomponente QuasarPersistence – sie wird auf der Webseite [OQ] als open source vertrieben – eine andere ist die Autorisierungskomponente QuasarAuthorization. Sie ist Gegenstand von Kapitel 4. 2.2. Spezifikation von Schnittstellen Das Denken in Komponenten und Schnittstellen ist ein Fundament von Quasar. Schnittstellen definieren Dienste, die von Komponenten erbracht oder in Anspruch genommen werden. Beim Entwurf eines Softwaresystems kommt den Schnittstellen eine bedeutende Rolle zu: Sie sind die tragenden Wände des Gebäudes. Komponenten unterliegen einem Lebenszyklus, Veränderungen der Implementierung sind nicht ungewöhnlich. Dagegen wird eine Schnittstelle im Idealfall einmal definiert und danach nicht mehr geändert. Die Spezifikation des Systems stützt sich deshalb überwiegend auf seine Schnittstellen. Die folgenden Unterabschnitte orientieren sich am Schnittstellenkonzept von Quasar, insbesondere an der in [Sie04, Kapitel 6] vorgestellten Methode zur Schnittstellenspezifikation. 2.2.1. Die Schnittstelle als Vertrag Schnittstellen verbinden Komponenten mit Komponenten, Systeme mit Systemen oder – im Fall von Benutzerschnittstellen – Systeme mit Menschen. Im Folgenden beschränken wir uns auf Programmschnittstellen. Sie bestehen aus maschinenlesbaren Methoden- bzw. Funktionsdeklarationen. 3 Graphical User Interface 7 2. Softwaretechnische Grundlagen Verbindet eine Schnittstelle zwei Komponenten, treten beide in verschiedenen Rollen auf: • Der Exporteur erbringt den Dienst, er exportiert die Schnittstelle und implementiert ihre Methoden. • Der Importeur nimmt den Dienst in Anspruch, er importiert die Schnittstelle und ruft ihre Methoden auf. Jede Komponente erbringt irgendeinen Dienst und exportiert deshalb mindestens eine Schnittstelle. Die Gesamtheit der von einer Komponente exportierten und importierten Schnittstellen bildet die Außensicht der Komponente. Die Konfiguration legt fest, welche Importeure durch welche Schnittstellen mit welchen Exporteuren zu einer Komposition verbunden werden. Wenn zwei Menschen eine Ware oder Dienstleistung austauschen, schließen sie einen Vertrag, der die Rechte und Pflichten der Vertragspartner festschreibt. Wenn zwei Softwarekomponenten verbunden werden (um eine Dienstleistung auszutauschen), übernimmt die Schnittstelle die Rolle des Vertrages. Diese Sichtweise liegt der Entwurfstechnik Design by Contract zugrunde (siehe [Mey97]). Man beschreibt dabei Komponenten nur anhand der Außensicht. Die Schnittstellenspezifikation ist ein verbindlicher Vertrag (contract), und weder Importeur noch Exporteur dürfen Annahmen machen, die über den Vertrag hinausgehen. Damit weiß der Exporteur, was er zu tun hat, und der Importeur weiß, worauf er sich verlassen kann. Die Abhängigkeiten zwischen Komponenten beschränken sich auf die Schnittstellen und bleiben damit überschaubar. Design by Contract ist ein Schlüsselkonzept zur Entwicklung verlässlicher Software. 2.2.2. Spezifikationselemente Die Spezifikation einer Schnittstelle macht Aussagen über deren Syntax und Semantik. Die Syntax ist einfach zu beschreiben: Sie besteht aus den Signaturen4 aller Methoden der Schnittstelle, formuliert in der jeweiligen Programmiersprache. Interessanter ist die Semantik: Was bewirken Methodenaufrufe, welche Aufrufe sind unter welchen Umständen erlaubt? Was bedeuten die Rückgabewerte? Die Semantik von Schnittstellen kann man mit verschiedenen Ausdrucksmitteln erfassen: • Vor- und Nachbedingungen für Methoden: Vorbedingungen (preconditions) bilden die Gebrauchsanleitung für den Importeur. Er darf die Methode nur aufrufen, wenn all ihre Vorbedingungen erfüllt sind. Nachbedingungen (postcondi4 8 Methodenname, Parametertypen und Rückgabetyp 2.2. Spezifikation von Schnittstellen tions) beschreiben, was ein Methodenaufruf bewirkt. Der Exporteur sorgt dafür, dass am Ende des Aufrufs alle Nachbedingungen der Methode erfüllt sind. • Invarianten sind Bedingungen, die immer gelten. Jede Invariante ist zugleich Vor- und Nachbedingung jeder Methode der Schnittstelle. • Ein Zustandsmodell teilt den Zustandsraum einer Schnittstelle in wenige Äquivalenzklassen auf. Das erleichtert die Formulierung von Vor- und Nachbedingungen und Invarianten. Beispiel Liste: Der Zustandsraum einer Liste ist unendlich groß, doch ein Zustandsmodell, das nur zwischen leeren und gefüllten Listen unterscheidet, kommt mit zwei Zuständen aus. • Testfälle sind beispielhafte Abfolgen von Methodenaufrufen mitsamt den erwarteten Ergebnissen. • Fehler, die bei einem Methodenaufruf auftreten können, sind Bestandteil der Spezifikation. Quasar unterscheidet zwischen Fehlern und Ausnahmen. Ein Fehler ist auf der Abstraktionsebene der Schnittstelle ein sinnvolles Ergebnis, eine Ausnahme nicht. Beispiel Bankkonto: Wenn ein Überweisungsvorgang scheitert, weil der Kontostand zu niedrig ist, dann ist das ein Fehler. Scheitert die Überweisung, weil der Zentralcomputer der Bank unerreichbar ist, handelt es sich um eine Ausnahme. Im Gegensatz zu Fehlern sind Ausnahmen implementierungsabhängig, und der Aufrufer hat normalerweise keine Möglichkeit, sie zu verhindern. Quasar propagiert die Spezifikation in einer heilen Welt – Ausnahmen zählen nicht dazu. • Angaben zur Konstanz: Wenn der Aufruf einer Methode den Zustand5 der Komponente nicht verändert, schreibt man das in die Spezifikation dieser Methode. Das Gleiche gilt für Methodenparameter, die beim Aufruf konstant bleiben. Methoden, die den Komponentenzustand unverändert lassen, heißen Abfragen, zustandsändernde Methoden heißen Kommandos. • Angaben zur Wiederholbarkeit tragen ebenfalls zum besseren Verständnis der Schnittstellensemantik bei. Dabei geht es um die Frage: Führen wiederholte Methodenaufrufe mit unveränderten Parametern zu unveränderten Resultaten? Quasar unterscheidet hier drei Fälle: URR (unrestricted repeatable read) garantiert uneingeschränkte Wiederholbar- keit: Die Methode liefert mit denselben Parametern immer dasselbe Ergebnis, verhält sich also wie eine mathematische Funktion. 5 Hier ist der an den Schnittstellen der Komponente beobachtbare Zustand gemeint. Nur ein Aufruf, der das Ergebnis irgendeines nachfolgenden Aufrufs beeinflusst, ändert den beobachtbaren Zustand. 9 2. Softwaretechnische Grundlagen RRR (restricted repeatable read) schränkt die Wiederholbarkeit ein: Zwei Me- thodenaufrufe mit denselben Parametern liefern nur dann dasselbe Ergebnis, wenn niemand zwischenzeitlich den Zustand der Komponente ändert (durch Aufruf eines Kommandos). Dies ist der häufigste Fall. NRR (non-repeatable read) bedeutet, dass die Methode keine Wiederholbarkeit garantiert: Jeder Aufruf kann ein anderes Ergebnis liefern (Beispiel: Zufallsgenerator). 2.2.3. Spezifikationstechniken Wir haben aufgezählt, was in die Spezifikation einer Schnittstelle gehört, welche Vertragspunkte zu berücksichtigen sind. Es bleibt die Frage, wie man den Vertrag aufschreibt. Es gibt drei verschiedene Techniken: • Weit verbreitet sind informelle Beschreibungen, wie man sie z. B. aus Java-Programmdokumentationen (Javadoc) kennt. Dabei erläutert man die Semantik einer Schnittstelle oder Klasse ausschließlich in natürlicher Sprache. Die Qualität einer solchen Spezifikation hängt ganz von der Disziplin des Verfassers ab. Ein genereller Nachteil besteht in der Mehrdeutigkeit natürlicher Sprache. • Formale Spezifikationsmethoden bedienen sich präziser Spezifikationssprachen mit Ausdrucksmitteln aus der Logik und Mengenlehre. Beispiele sind die ZNotation (siehe [Z]) und die Vienna Development Method (VDM) mit ihrer VDM Specification Language (siehe [VDM]). Formale Spezifikationen sind maschinell verifizierbar. Sie sind jedoch aufwendig zu erstellen und für den menschlichen Leser nur schwer verständlich. • Programmiernahe Beschreibungssprachen verwenden ähnliche Konstrukte und Datenstrukturen wie Programmiersprachen. Sie kommen damit dem Programmierer entgegen, der die Spezifikation umsetzen soll. Als Beispiele seien hier die Sprachen JML6 , OCL7 und Eiffel8 genannt. JML ist eine Erweiterung der Programmiersprache Java, JML-Spezifikationen werden in Java-Quelltext eingebettet. OCL lehnt sich an keine bestimmte Programmiersprache an. Ein Sonderfall ist Eiffel: Das ist eine Programmiersprache, die von Haus aus Spezifikationselemente (Vor- und Nachbedingungen, Invarianten) enthält, also zugleich Spezifikationssprache ist. 6 Java Modeling Language; siehe [JML] Object Constraint Language; Teil des UML-Standards (Unified Modeling Language) 8 siehe [Mey97] 7 10 2.2. Spezifikation von Schnittstellen 2.2.4. QSL In dieser Arbeit spezifizieren wir Schnittstellen mit Hilfe der programmiernahen Beschreibungssprache Quasar Specification Language (QSL). Die Notation wird in [Sie04] eingeführt, wir fassen an dieser Stelle die wichtigsten Merkmale zusammen. QSL-Spezifikationen enthalten Elemente der Zielsprache, in unseren Komponentenentwürfen ist das Java. Jede QSL-Spezifikation hat den in Quelltext 2.1 dargestellten Aufbau (die meisten Quelltextbeispiele dieses Abschnitts stammen aus [Sie04, Abschnitt 6.5]). Quelltext 2.1: Grundgerüst einer Schnittstellenspezifikation in QSL interface MyInterface extends YourInterface , HerInterface uses H i s I n t e r f a c e , OurInterface variables . . . states . . . basicQueries firstBasicQuery secondBasicQuery ... derivedQueries firstDerivedQuery secondDerivedQuery ... commands firstCommand secondCommand ... invariants firstInvariant secondInvariant ... testcases firstTestCase secondTestCase ... 11 2. Softwaretechnische Grundlagen Die erste Zeile gleicht dem Beginn einer Java-Interface-Definition: Nach dem interface-Schlüsselwort folgt der Name der Schnittstelle; falls andere Schnittstellen erweitert werden, zählt man deren Namen nach extends auf. In der uses-Klausel stehen alle Klassen und Schnittstellen, die als Parameter- und Resultattypen der Methoden vorkommen, mit Ausnahme von 0-Software (in Java sind das die Typen aus den Paketen java.lang und java.util). In der variables-Klausel deklariert man globale Variablen, auf die man in Vor- und Nachbedingungen, Invarianten und Testfällen zurückgreift. Sie stehen in der gesamten Spezifikation mit einem impliziten Allquantor zur Verfügung, d. h. ohne explizite Einschränkung bezieht sich jede Aussage über eine solche Variable auf alle Variablen desselben Typs. Die Notation orientiert sich an Java: variables L i s t r s maybenull , xs ; I t e r a t o r i t ; Object x , y , z ; Variablen dürfen nur den Wert null annehmen, wenn dies durch Angabe des Schlüsselworts maybenull ausdrücklich gestattet ist. In der states-Klausel definiert man ein oder mehrere Zustandsmodelle. Ein Beispiel mit zwei Zustandsmodellen: states switch = { undefined , on , o f f } l e v e l = { undefined , low , high } Der Zugriff auf die Zustandsmodelle erfolgt durch Ausdrücke nach dem Muster switch(x) == on (hier ist x die Variable, deren Zustand abgefragt wird). Existiert nur ein Zustandsmodell, bekommt es keinen Namen, die Abfrage des Zustands erfolgt in diesem Fall mit state(x). Die Klauseln basicQueries, derivedQueries und commands enthalten die Spezifikationen der Methoden, wobei man jede Methode genau einer der drei Klauseln zuordnet. Kommandos ordnet man unter commands ein, Abfragen listet man unter basicQueries (einfache Abfragen) bzw. derivedQueries (abgeleitete Abfragen) auf. Die Unterscheidung zwischen einfachen und abgeleiteten Abfragen erfolgt gemäß [MM02]: Abgeleitete Abfragen lassen sich auf einfache Abfragen zurückführen. Man spezifiziert also das Resultat einer abgeleiteten Abfrage, indem man sich auf die Resultate einfacher Abfragen bezieht. Der Grund ist Arbeitserleichterung: Wenn man die Semantik eines Kommandos spezifiziert, genügt es, seine Auswirkungen auf die einfachen Abfragen zu beschreiben. Damit sind implizit die Auswirkungen auf die abgeleiteten Abfragen ebenfalls erfasst. Quelltext 2.2 zeigt den allgemeinen Aufbau von Methodenbeschreibungen in QSL. Vorbedingungen leitet man mit pre ein, Nachbedingungen mit post. error-Klauseln beschreiben, welche Fehler (keine Ausnahmen) unter welchen Bedingungen auftreten können. repeatability bezieht sich auf die Wiederholbarkeit der Methode. Hier steht entweder URR, NRR oder gar nichts (dann gilt RRR). Informelle Beschreibungen 12 2.2. Spezifikation von Schnittstellen Quelltext 2.2: Allgemeiner Aufbau einer Methodenspezifikation in QSL pre firstPrecondition pre secondPrecondition someType someMethod( firstParameter , secondParameter) r e pe a t ab i l it y // i n f o r m e l l e Beschreibung post firstPostcondition post secondPostcondition error someErrorCondition => f i r s t E r r o r error secondError // i n f o r m e l l e F e h l e r b e s c h r e i b u n g bringt man in Zeilenkommentaren unter (von // bis zum Zeilenende; QSL sieht keine Blockkommentare vor). Die Methodensignatur notiert man in der Zielsprache, wobei man die formalen Parameter zusätzlich mit maybenull markiert, falls sie den Wert null annehmen dürfen. Parameter, die durch den Methodenaufruf nicht verändert werden, kennzeichnet man mit const. Quelltext 2.3 zeigt die QSL-Beschreibung einer konkreten Methode. Quelltext 2.3: Beispiel einer QSL-Methodenspezifikation pre range > 0 i n t random( i n t range) NRR // l i e f e r t e i n e Z u f a l l s z a h l . post 0 <= r e s u l t < range Zurück zum Grundgerüst (Quelltext 2.1): In der invariants-Klausel beschreibt man die Invarianten der Schnittstelle. Die Angabe von Invariantennamen ist optional: invariants schulden () <= dispo () symmetry x . equals ( x ) // unbenannte I n v a r i a n t e // I n v a r i a n t e namens ' symmetry ' Unter testcases beschreibt man Testfälle. Jeder Testfall hat einen Namen, optional definiert man lokale Variablen, Vorbedingungen (pre) und Nachbedingungen (post). Quelltext 2.4 zeigt einen Beispiel-Testfall aus der Spezifikation einer Liste. Vor- und Nachbedingungen (von Methoden und Testfällen) sowie Invarianten formuliert man grundsätzlich in der Zielsprache. Zur Verbesserung der Ausdrucksstärke bzw. Lesbarkeit sieht QSL zusätzlich folgende Konstrukte und Schlüsselwörter vor: • Allquantor und Existenzquantor: forall, exists • ∈-Relation der Mengenlehre (anwendbar auf Behälter, Arrays): in • Logische Operatoren für UND, ODER und Negation: and, or, not 13 2. Softwaretechnische Grundlagen Quelltext 2.4: Beispiel eines QSL-Testfalls addAndRemove L i s t<Object> xs ; pre xs . s i z e () xs . add( x ) xs . s i z e () xs . add( x ) xs . s i z e () xs . remove( y ) xs . s i z e () xs . indexOf ( x ) xs . indexOf ( y ) xs . contains ( x ) xs . contains ( y ) xs . remove( x ) xs . s i z e () xs . indexOf ( x ) xs . remove( x ) post xs . s i z e () // l o k a l e V a r i a b l e == 0 −> 1 −> −> −> −> −> −> −> −> −> −> −> == 2 false 2 0 −1 true false true 1 0 true 0 • Implikation und Äquivalenz: =>, <=> • Zugriff auf den Wert von x vor Durchführung der Methode (in post- und errorKlauseln): ’x • Zugriff auf das Methodenresultat (in post-Klauseln): result • Typisierte Behälter: List<String> ls (Die Liste ls enthält nur Strings.) 14 3. Grundlagen einer Sicherheitsarchitektur »You can’t even travel around your own microcircuits without permission from that Master Control creep.« Tron (Walt Disney Pictures, 1982) In jedem IT-System gibt es Subjekte (Prozesse, menschliche Benutzer etc.), die etwas tun, und Objekte (Bits, Datenbanktabellen, Dateien etc.), mit denen etwas getan wird. Informationssicherheit ist dann hergestellt, wenn die Subjekte nur das tun können, was sie dürfen. Die Begriffe Authentifizierung, Autorisierung und Zugriffskontrolle fassen die hierfür notwendigen Maßnahmen zusammen. Wir erörtern diese Begriffe in den folgenden Abschnitten. Desweiteren gehen wir in diesem Kapitel der Frage nach, was es mit vertrauenswürdiger Software auf sich hat, und stellen mit JAAS1 ein etabliertes Produkt vor, das Softwareingenieuren Hilfsmittel für die Java-basierte Implementierung einer Sicherheitsarchitektur an die Hand gibt. 3.1. Authentifizierung Jedes Subjekt hat eine Identität. Es ist die Aufgabe der Authentifizierung2 , die Identität eines Subjekts nachzuweisen. Manchmal ist das trivial – das Betriebssystem erkennt einen lokal laufenden Prozess einfach anhand seiner fest zugeordneten Nummer. Läuft der Prozess jedoch auf einem entfernten Rechner, oder handelt es sich beim Subjekt um einen Menschen, der vor einem Terminal sitzt, liegt der Fall komplizierter. Authentifizierung basiert auf charakterisierenden Eigenschaften der Subjekte. Zu diesen Eigenschaften zählt neben einer eindeutigen Kennung (Prozessnummer, Benutzername etc.) in den meisten Fällen ein Geheimnis, das nur dem echten Subjekt bekannt ist, also ein Passwort – oder allgemeiner: ein Schlüssel in Form einer möglichst langen Zeichenkette. Die Authentifizierung mittels Passwort läuft im einfachsten Fall so ab, dass das zu authentifizierende Subjekt (nachfolgend Prüfling genannt) dem Authentifizierenden (Prüfer) sein Passwort mitteilt, woraufhin dieser es mit einer hinterlegten Kopie 1 2 Java Authentication and Authorization Service alternative Begriffe: Authentifikation, Authentisierung 15 3. Grundlagen einer Sicherheitsarchitektur vergleicht. Fortgeschrittenere Methoden erlauben es, einen Prüfling zu authentifizieren, ohne dass dieser sein Geheimnis vollständig preisgibt. Sogenannte ChallengeResponse-Verfahren basieren auf einer Art Frage-Antwort-Spiel, bei dem der Prüfer dem Prüfling Aufgaben stellt, die dieser nur mit Kenntnis des Geheimnisses korrekt lösen kann. Bei kryptographischen Verfahren (z. B. Fiat-Shamir-Authentifizierung, siehe [FS87, FFS87]) bestehen diese Aufgaben in der Regel darin, Zufallszahlen unter Einbeziehung einer geheimen Zeichenkette zu verschlüsseln. Neben Geheimnissen kann man bei menschlichen Benutzern auch biometrische Merkmale wie z. B. Fingerabdrücke als charakterisierende Eigenschaften für die Authentifizierung verwenden. Ein Fingerabdruckscanner errechnet aus Messdaten eine Zeichenkette, die ebenso wie ein geheimer Schlüssel nur sehr schwer zu fälschen ist. Ein IT-System kann die Identität von Subjekten selbst prüfen, es kann diese Aufgabe aber auch an andere Systeme delegieren, indem es sich versichern lässt, dass ein bestimmtes Subjekt bereits authentifiziert ist. Das setzt natürlich voraus, dass die beteiligten Systeme einander vertrauen, und ist die Grundlage für Single-Sign-On: Ein Benutzer eines verteilten Systems authentifiziert sich nur an einem der vernetzten Systeme, kann aber auch auf andere Systeme zugreifen, ohne sich dort erneut anzumelden. Die Delegation der Authentifizierung geschieht im Hintergrund, der Benutzer sieht davon nichts. Kerberos (siehe [Ker]) ist ein Authentifizierungsprotokoll, das Single-Sign-On auf der Ebene lokaler bzw. unternehmensweiter Netzwerke ermöglicht. Microsoft Passport (siehe [MSP]) und das Liberty-Alliance-Project (siehe [Lib]) definieren Protokolle für Single-Sign-On-Verfahren zwischen verschiedenen Diensteanbietern im World Wide Web. 3.2. Autorisierung und Zugriffskontrolle Durch Authentifizierung kann man sich auf die Identität der Subjekte verlassen – das allein reicht aber nicht aus für eine wirksame Kontrolle der Objektzugriffe. Man braucht Regeln, die festlegen, was welche Subjekte mit welchen Objekten tun dürfen. Alles, was mit dem Formulieren und Abfragen solcher Regeln zu tun hat, fassen wir unter Autorisierung3 zusammen. Die eigentliche Zugriffskontrolle besteht darin, die Einhaltung der Regeln durchzusetzen. Auch hier gibt es triviale Fälle. So kennt ein Betriebssystem für den Einbenutzerbetrieb (z. B. MS-DOS) nur eine Regel: Jeder hat die gleichen Rechte. Das macht Authentifizierung überflüssig, denn es findet überhaupt keine Zugriffskontrolle statt. 3 Der Begriff Autorisierung wird in der Literatur nicht einheitlich verwendet. Manche meinen damit nur die Vergabe, andere nur die Prüfung von Zugriffsrechten. Oft bezeichnet man auch ein einzelnes Zugriffsrecht als Autorisierung oder Autorisation. 16 3.2. Autorisierung und Zugriffskontrolle 3.2.1. Grundlegende Autorisierungsmechanismen Wir haben es in einem IT-System mit einer Menge von Subjekten (S) und einer Menge von Objekten (O) zu tun.4 Subjekte können in vielfältiger Weise auf Objekte zugreifen, womit als dritte Menge die Zugriffsarten (A) im Spiel sind. Typische Zugriffsarten sind z. B. lesen, schreiben oder ausführen. [RBKW91] definiert auf diesen Mengen eine Funktion f : S × O × A → {True, False} die jedes Tripel (s, o, a) mit s ∈ S, o ∈ O und a ∈ A auf einen Wahrheitswert abbildet. Ein solches Tripel repräsentiert einen Zugriff, und die Funktion f stellt fest, ob dieser Zugriff erlaubt ist. f liefert also die Antwort auf die Frage „Darf der das?“ Beispielsweise wäre f (Heidi, file1.txt, lesen) = True so zu interpretieren: „Heidi darf die Datei file1.txt lesen“. Es gibt verschiedene Möglichkeiten, diese Funktion zu implementieren. Ein offensichtlicher Ansatz wäre, alle Tripel zu speichern, für die f zu True auswerten soll. So arbeitet das Modell der Zugriffsmatrix5 . Für jede mögliche Kombination aus Subjekt und Objekt existiert in der Matrix eine Zelle, in der die erlaubten Zugriffsarten abgelegt sind. Tabelle 3.1 zeigt dafür ein Beispiel. Tabelle 3.1.: Ausschnitt einer Zugriffsmatrix Heidi Otto Uwe .. . file1.txt file2.exe lesen, schreiben ausführen ausführen lesen file3.txt ··· lesen lesen, schreiben Zugriffsmatrizen sind meist dünn besetzt, daher realisiert man sie gemeinhin durch Listen, die einzelne Zeilen oder Spalten der Matrix repräsentieren – je nachdem, ob man subjekt- oder objektbezogen vorgeht: • Beim subjektbezogenen Ansatz verknüpft man je eine Liste mit einem Subjekt. Diese Liste heißt Zugriffsausweis oder capability. Der Zugriffsausweis von Otto enthält – bezogen auf das Beispielszenario – die Einträge (file2.exe, ausführen) und (file3.txt, lesen). • Beim objektbezogenen Ansatz verknüpft man je eine Liste mit einem Objekt. Man spricht dann von einer Zugriffskontrollliste oder ACL (Access Control List). 4 5 S und O sind nicht zwangsläufig disjunkt. auch bekannt als Lampson-Matrix (nach Butler Lampson, [Lam71]) 17 3. Grundlagen einer Sicherheitsarchitektur Die ACL der Datei file1.txt enthält die Einträge (Heidi, (lesen, schreiben)) und (Uwe, lesen). 3.2.2. Implizite Rechte Die Zugriffsmatrix und davon abgeleitete Modelle machen alle Rechtezuordnungen explizit. Das ermöglicht eine sehr effiziente Auswertung, setzt in der Praxis aber überschaubare und möglichst statische Mengen von Subjekten und Objekten voraus, damit die Administration der Zugriffsrechte handhabbar bleibt. Man umgeht diese Einschränkung, indem man Rechte auch implizit vergibt, d. h. man speichert nur wenige (s, o, a)-Tripel explizit und leitet weitere Rechte durch Anwendung von Regeln ab. Sind Subjekte bzw. Objekte hierarchisch organisiert, ergeben sich Implikationsregeln oft ganz natürlich. Benutzer organisiert man normalerweise in Gruppen. Eine Gruppe besteht aus einer Menge von Benutzern und eventuell weiteren Gruppen. Beispiele für Implikationsregeln: • Ein Benutzer erlangt bestimmte Rechte durch Mitgliedschaft in einer Gruppe. • In einem Verzeichnisbaum schließt das Zugriffsrecht auf ein Verzeichnis entsprechende Rechte bezüglich aller enthaltenen Dateien und Unterverzeichnisse ein. Auch die Menge der Zugriffsarten kann geordnet sein. „Wer schreiben darf, darf auch lesen“ kommt häufig vor, gilt aber nicht immer: Auf ein Bankkonto darf jeder schreiben, indem er Geld einzahlt, deswegen darf er aber noch lange nicht den Kontostand abfragen.6 Abbildung 3.1 zeigt ein Beispiel mit vier Zugriffsarten. 6 Dieser Widerspruch lässt sich lösen, indem man neben der Zugriffsart schreiben die Zugriffsart einzahlen definiert. W G Abbildung 3.1.: Darstellung einer (partiellen) Ordnung auf Zugriffsarten write read generate read definition (entnommen aus [RBKW91]). Das Beispiel bezieht sich auf eine objektorientierte Datenbank. Wer schreiben darf (W), darf auch lesen (R) und – falls das Schreibrecht für eine Klasse gilt – neue Objekte erzeugen (G). Aus allen drei Zugriffsarten folgt das Recht zum Lesen der Klassendefinition (RD). R RD W : R : G : RD : 18 3.2. Autorisierung und Zugriffskontrolle 3.2.3. Negative Rechte und Konsistenz In den bisherigen Beispielen nahmen wir stillschweigend an, dass das Erlaubnisprinzip gilt: Alles ist verboten, was nicht (explizit oder implizit) erlaubt ist. Dieses lässt sich auch umkehren: Beim Verbotsprinzip formuliert man nur negative Zugriffsrechte (Verbote) und erlaubt alles andere (vgl. [Die03, Teil 3]). In Kombination mit impliziter Rechtevergabe ist es manchmal wünschenswert, sowohl positive als auch negative Zugriffsrechte parallel vergeben zu können. Man denke an ein Verzeichnis, das 100 Dateien enthält, von denen Otto 99 lesen darf. Statt 99 explizite Leserechte zu erteilen, ist es für den Administrator komfortabler, ein positives Recht für das Verzeichnis und ein negatives Recht für jene Datei darin zu erteilen, die Otto nicht lesen darf. Sobald positive und negative Rechte im Spiel sind, stellt sich die Frage, ob die Rechtefestlegungen widerspruchsfrei (konsistent) sind. Angenommen, Heidi ist Mitglied in Gruppe1 und Gruppe2 . Gruppe1 hat ein positives Leserecht für file4.txt, Gruppe2 dagegen ein negatives. Darf Heidi die Datei nun lesen oder nicht? Abhängig von den Implikationsregeln und den Beziehungsgeflechten bei Subjekten, Objekten und Zugriffsarten besteht unter Umständen ein beträchtliches Konfliktpotential. Damit die Funktion f von Seite 17 deterministisch bleibt, sorgt man entweder dafür, dass erst gar keine inkonsistenten Zugriffsrechte auftreten, oder man gibt Regeln für die Auflösung aller möglichen Widersprüche an. Im ersten Fall ist die Einhaltung der Konsistenz bei jeder neuen Rechtevergabe zu überwachen, im zweiten Fall sind alle Rechtezuordnungen per Definition konsistent (die Autorisierungskomponente QuasarAuthorization und unser Entwurf AdvancedAuthorization folgen diesem Ansatz). Im vorletzten Beispiel (Ottos 99 Dateien) haben wir bereits eine einfache Konfliktlösungsregel angewandt: Explizite Rechte überschreiben implizite. Das erscheint hier vernünftig, ist aber nicht verallgemeinerbar – und ob Heidi file4.txt lesen darf, beantwortet diese Regel auch nicht: Heidis widersprüchliche Rechte ergeben sich beide implizit durch Gruppenzugehörigkeit.7 Eine minimalistische Kombination aus positiver, negativer und impliziter Rechtevergabe kann Autorisierungsaufgaben vereinfachen, wohingegen das Ausschöpfen aller Freiheitsgrade den Rechtezustand eines Systems unüberschaubar macht. Das optimale Autorisierungsmodell gibt es (noch) nicht. Wie [Mar03] feststellt, „wird dem Problem der Konsistenz der [...] vergebenen Rechte in der Literatur bisher wenig Aufmerksamkeit geschenkt“. 7 Eine mögliche Lösung für das Heidi-Szenario wäre, Prioritäten auf Gruppenzugehörigkeiten zu definieren. Eine andere Lösung bietet die Regel: Negative Rechte überschreiben positive (oder umgekehrt). 19 3. Grundlagen einer Sicherheitsarchitektur 3.2.4. Zugriffskontrollstrategien [Eck03] unterscheidet drei Strategien der Zugriffskontrolle: DAC DAC steht für Discretionary Access Control, auf deutsch benutzerbestimmbare Zugriffskontrolle. Bei dieser Strategie hat jedes Objekt einen Eigentümer, der festlegt, wer auf welche Weise darauf zugreifen darf. Die Autorisierung erfolgt also dezentral. Im Falle der ACL-basierten Rechtefestlegung hat allein der Eigentümer die Hoheit über die ACLs seiner Objekte. SQL8 bietet Benutzern mit dem GRANT-Befehl die Möglichkeit, anderen Benutzern Zugriffsrechte auf ihre Datenbanktabellen zu gewähren. MAC Bei der systembestimmten Zugriffskontrolle oder MAC (Mandatory Access Control) bestimmen systemweite Vorgaben, wem was erlaubt ist. Das bekannteste Modell, das dieser Strategie folgt, ist das Bell-LaPadula-Modell (ausführlich beschrieben in [Eck03, S. 204ff]). Es basiert auf Sicherheitsklassen bzw. Vertraulichkeitsstufen, wie sie vor allem in militärischen Einrichtungen Anwendung finden. Jedes Objekt wird dabei entweder als unklassifiziert, vertraulich, geheim oder streng geheim eingestuft. Die Subjekte sind analog klassifiziert. Jedes Subjekt darf nur Objekte lesen, die seiner eigenen oder einer niedrigeren Vertraulichkeitsstufe angehören. Für den Schreibzugriff gilt die inverse Regel: Das Subjekt darf nur auf Objekte zugreifen, die der gleichen oder einer höheren Vertraulichkeitsstufe angehören. Auf diese Weise findet vertikaler Informationsfluss (also zwischen verschiedenen Sicherheitsklassen) nur von unten nach oben statt. MAC ist von Haus aus eine zentralistische Strategie, allerdings ist eine Kombination mit DAC möglich, wobei im Konfliktfall die restriktivere Regel Vorrang hat. RBAC Mit dem Anspruch, eine vollwertige Alternative zu den traditionellen DAC- und MACAnsätzen zu schaffen, wurde die rollenbasierte Zugriffskontrollstrategie in den neunziger Jahren unter dem Namen RBAC (Role-Based Access Control) formalisiert. Die Autorisierungskomponenten(entwürfe), die wir in den folgenden Kapiteln behandeln, verfolgen einen rollenbasierten Ansatz, weshalb wir RBAC an dieser Stelle etwas ausführlicher vorstellen. 8 Structured Query Language (eine Abfragesprache für relationale Datenbanken) 20 3.2. Autorisierung und Zugriffskontrolle Rollenkonzepte im weiteren Sinne gibt es schon lange – man denke z. B. an das Gruppenkonzept in UNIX-Betriebssystemen – doch erst mit RBAC setzte sich eine einheitliche Terminologie durch. Die US-amerikanische Normungsbehörde NIST (National Institute of Standards and Technology) schlägt RBAC als offiziellen Standard vor (siehe [FSG+ 01]). Wir führen zwei neue Begriffe ein: • Eine Berechtigung ist ein Tupel (o, a) mit o ∈ O und a ∈ A. Sie verknüpft ein Objekt mit einer Zugriffsart. Im Unterschied zum (s, o, a)-Tripel aus Abschnitt 3.2.1 beschreibt eine Berechtigung einen allgemeinen Zugriff unabhängig vom Subjekt. • Eine Rolle umfasst eine Menge von Berechtigungen. Beim hierarchischen RBACModell können Rollen auch ineinander verschachtelt sein, wodurch sich eine Ordnung auf der Menge der Rollen ergibt. Eine Rolle enthält dann mindestens alle Berechtigungen der ihr untergeordneten Rollen. Der so definierte Rollenbegriff (als Menge von Rechten) unterscheidet sich deutlich vom Begriff der Gruppe (als Menge von Subjekten).9 Bei RBAC ordnet man einem Subjekt eine oder mehrere Rollen zu, anstatt ihm direkte Zugriffsrechte auf Objekte zu erteilen. Unter der Voraussetzung, dass die Menge der Rollen kleiner ist und sich seltener ändert als die Mengen der Subjekte und Objekte, ermöglicht RBAC damit eine vereinfachte und übersichtliche Administration von Zugriffsrechten. Durch die Zuordnung einer Rolle zu einem Subjekt werden die Berechtigungen der Rolle zu (positiven) Zugriffsrechten des Subjekts – eine Vergabe negativer Rechte sieht das Modell nicht vor. Gemäß dem Prinzip der minimalen Rechte – auch need-to-know- oder least-privilegePrinzip genannt – darf ein Subjekt nur über jene Zugriffsrechte verfügen, die es zur Erfüllung seiner Aufgaben braucht. So minimiert man die Gefahr, dass jemand – absichtlich oder versehentlich – Unsinn anstellt. Bei der Definition von Rollen orientiert man sich an diesem Prinzip, indem man nur Berechtigungen eines begrenzten Aufgabengebiets zusammenfasst. Einen Schritt weiter geht das Konzept der Sitzung (session). Eine Sitzung existiert nur begrenzte Zeit – normalerweise von der Anmeldung eines Subjekts am System bis zu seiner Abmeldung – und enthält die während der Sitzungsdauer aktiven Rollen. Diese bilden eine Teilmenge aller Rollen, die dem Subjekt zugeordnet wurden. Wenn man diese Teilmenge genau auf jene Aufgabe zuschneidet, die während der Sitzung zu erledigen ist, erfüllt man das Prinzip der minimalen Rechte optimal. 9 Manche Autoren verwenden Gruppe und Rolle synonym, vgl. [SST97, S. 409]. 21 3. Grundlagen einer Sicherheitsarchitektur RBAC formuliert auch Randbedingungen (constraints) für eine statische und dynamische Aufgabentrennung (static/dynamic separation of duty). Diese Randbedingungen sind Relationen auf der Rollenmenge, die besagen, welche Rollen sich gegenseitig ausschließen. Statische Aufgabentrennung bedeutet, dass einander ausschließende Rollen generell nicht demselben Subjekt zugeordnet werden dürfen. Bei dynamischer Aufgabentrennung ist zwar die Zuordnung solcher Rollen erlaubt, das Subjekt darf aber während einer Sitzung nur in der einen oder der anderen Rolle aktiv sein. Als Beispiel stelle man sich eine studentische Hilfskraft vor, die an einem Lehrstuhl Klausuren korrigiert. Sie darf natürlich keine Klausuren korrigieren, die sie selbst mitgeschrieben hat, und deshalb nicht gleichzeitig in den Rollen Prüfungskandidat und Korrektor auftreten. 3.3. Vertrauens(un)würdige Software Authentifizierung und Autorisierung sind das Fundament einer Sicherheitsarchitektur. In Analogie zum Staatsapparat bilden sie die Legislative, die gesetzgebende Gewalt. Wir wissen damit, wer in einem System auf welche Weise agieren darf. Da wir uns nicht darauf verlassen, dass alle Subjekte sich freiwillig an die Regeln halten, fehlt noch ein entscheidender Schritt zur wirksamen Zugriffskontrolle: die Durchsetzung der festgelegten Rechte. Beim Staat ist dafür die Exekutive (ausführende Gewalt) verantwortlich. In der ISO-Norm 10181-3 (Security frameworks for open systems: Access control framework, [ISO]) findet sich eine einfache grafische Darstellung dieses Sachverhalts (siehe Abbildung 3.2). Die Norm benennt zwei getrennte Funktionen: • Die Access Control Decision Function (ADF) entscheidet, ob ein bestimmter Zugriff erlaubt ist, entspricht also unserer Funktion f von Seite 17. • Die Access Control Enforcement Function (AEF) nimmt Zugriffswünsche entgegen und konsultiert die ADF. Je nachdem, ob die Entscheidung der ADF positiv oder negativ ausfällt, ist es die Aufgabe der AEF, Zugriffe zu gestatten oder zu unterbinden. Die Implementierung der AEF ist eine anspruchsvolle Aufgabe (die wir in dieser Arbeit nur am Rande behandeln), und es leuchtet ein, dass man nicht sämtliche Zugriffe, die irgendwo im System stattfinden, durch eine AEF schleusen kann. Hier hilft die Unterscheidung zwischen Teilsystemen, denen man vertraut, und solchen, denen man misstraut. Bei einem vertrauenswürdigen System verlässt man sich darauf, dass es vor sicherheitskritischen Zugriffen die ADF von sich aus um Erlaubnis fragt. Die 22 3.3. Vertrauens(un)würdige Software Initiator Submit Access Request Present Access Request AEF Decision Request Target Decision ADF Abbildung 3.2.: Illustration fundamentaler Zugriffskontrollfunktionen aus [ISO]. Der Initiator ist das anfragende Subjekt, das Target ist das Objekt, auf das der Initiator zugreifen will. Zugriffe aus nicht vertrauenswürdigen Systemen sind streng zu überwachen. Man konzentriert sie nach Möglichkeit auf eine oder wenige Schnittstellen. [Dre02] stellt eine Sicherheitsarchitektur vor, die auf dem eben skizzierten Prinzip basiert. Abbildung 3.3 vermittelt einen Überblick dieser Architektur. Die Kernaussagen sind: • Der Vertrauensbereich (das trusted system, bestehend aus trusted software) ist abgegrenzt vom Misstrauensbereich der non-trusted software. • Ein zentraler Baustein wickelt alle Objektzugriffe ab. Im Bild ist das der Zugriffsmanager. • Nur trusted software darf den Zugriffsmanager direkt aufrufen. • Die trusted software sorgt selbst dafür, dass ihre Zugriffe mit den Rechtefestlegungen übereinstimmen. Sie konsultiert dazu die ADF (im Bild repräsentiert durch den Baustein Rechteprüfung). • Eine Prüfkomponente implementiert die AEF. Sie benutzt ihrerseits die ADF. Alle Zugriffe durch non-trusted software laufen über die Prüfkomponente. Das Architekturbild zeigt auch, welche Funktionen sich zur Auslagerung in eine Autorisierungskomponente eignen, nämlich • die Authentifizierung und • die Autorisierung, welche unterteilt ist in – die Administration der Zugriffsrechte und – die Rechteprüfung (ADF). 23 3. Grundlagen einer Sicherheitsarchitektur TRUSTED SYSTEM Login am System evtl. Ticket TRUSTED SOFTWARE evtl. Zugriffsausweis einmalig Ist der das? (Kennung, Schlüssel) Prüfschnittstelle Darf der das? (wer, wo, was, Ticket) ZugriffsManager Ja: Ticket, Nein: Ja, nein NON-TRUSTED SOFTWARE Prüfkomponente Ist der das? (Kennung, Schlüssel) Darf der das? (wer, wo, was) Authentifizierung Rechteprüfung Schnittstelle Administration Aufruf Autorisierungskomponente Administrator Autorisierungskomponente Abbildung 3.3.: Sicherheitsarchitektur aus [Dre02]. Die Prüfkomponente implementiert die AEF, die Rechteprüfung implementiert die ADF. Am Zugriffsmanager laufen alle Zugriffe auf Objekte zusammen. 24 3.4. JAAS – Informationssicherheit in der Java-Welt 3.4. JAAS – Informationssicherheit in der Java-Welt Der Java Authentication and Authorization Service (JAAS) ist seit der Version 1.4 in der J2SE10 enthalten. JAAS erweitert die ursprünglich codezentrierte Sicherheitsarchitektur von Java um benutzerbezogene Zugriffsrechte. Was das genau bedeutet, erläutern die folgenden Unterabschnitte. [Oak01] vermittelt einen umfassenden Einblick in die Sicherheitsarchitekur der Java-Plattform. Für Referenzdokumente und Programmieranleitungen zu JAAS ist die Webseite [JAAa] ein guter Einstiegspunkt. 3.4.1. Sandkastenspiele Java-Anwendungen laufen nicht direkt auf dem Prozessor, sondern in einem Interpreter – der Java Virtual Machine (JVM). Das ermöglicht in erster Linie die Entwicklung plattformunabhängiger Software, hat aber auch eine sicherheitsrelevante Dimension: Da die JVM selbst ein Softwaresystem ist, kann man sie mit Zugriffskontrollmechanismen ausstatten, die leistungsfähiger sind als jene, die die darunterliegende Hardware (Prozessor, Speicherverwaltung) von sich aus anbietet. Die Designer der Java-Plattform haben genau das getan. Die zentrale Metapher der Java-Sicherheitsarchitektur ist die Sandbox (Sandkasten). Das ist ein abgegrenzter Bereich, in dem sich non-trusted software austoben kann, ohne Schaden anzurichten.11 Der Prüfkomponente aus Abbildung 3.3 entspricht bei der JVM der Security Manager12 . Er wacht über die Zugriffe der Software, die in der Sandbox läuft. Der Security Manager der ersten Java-Version war recht einfach gestrickt, das entsprechende Sicherheitsmodell war schwarz-weiß: Anwendungen durften entweder alles, oder sie wurden in die Sandbox gesperrt, wo sie pauschal mit sehr restriktiven Zugriffsrechten auf Systemressourcen (Dateisystem, Netwerkverbindungen etc.) ausgestattet wurden. In der Standardeinstellung kamen alle Applets13 in die Sandbox, während lokaler Code keinen Einschränkungen unterlag. 10 Java 2 Platform, Standard Edition Eine treffendere Metapher wäre Gummizelle. 12 In neueren Java-Versionen delegiert der Security Manager seine Aufgaben an eine weitere Komponente, den Access Controller. Wir unterlassen im Folgenden diese Unterscheidung. 13 Ein Applet ist ein Java-Programm, das man üblicherweise aus dem Internet lädt und direkt im WebBrowser ausführt. 11 25 3. Grundlagen einer Sicherheitsarchitektur 3.4.2. Codezentrierte Autorisierung In jüngeren Java-Versionen wurde die Sandbox durchlässiger. Ab Java 1.2 kann man differenzierte Zugriffsrechte vergeben – doch an wen und wofür? Das Subjekt der Autorisierung ist hier der auszuführende Code, man vergibt also Rechte an Klassendateien (Dateien mit der Endung .class, die Java-Bytecode enthalten). Für die Rechtevergabe sind dabei nur zwei Eigenschaften relevant: • der Herkunftsort des Codes: Diesen benennt ein URL14 , zum Beispiel http://www.sdm.de/sample/ oder file:/C:/sample.jar. • der Unterzeichner des Codes: Das ist eine Person oder Institution, die durch Anbringen ihrer digitalen Signatur (Unterschrift) bezeugt, dass der Code vertrauenswürdig ist. Die Aussagekraft steht und fällt natürlich mit der Vertrauenswürdigkeit des Unterzeichners. Die Objekte der Autorisierung sind Permission-Objekte. Eine Permission ist charakterisiert durch ihren Typ (eine Unterklasse von java.security.Permission) und evtl. einen Namen. Der Security Manager gibt mittels der Methode checkPermission Auskunft, ob der laufende Code bestimmte Permissions hat; so benutzt man ihn als ADF. Er wirkt auch als AEF und setzt Zugriffsrechte durch – das gilt aber nicht für alle Permissions, es gibt nämlich zwei Arten: • Einige Permission-Klassen sind fester Bestandteil der Java-API. Wir nennen sie im Folgenden technische Permissions. Sie haben für den Security Manager eine definierte Semantik. Code, der in der Sandbox läuft, braucht diese Permissions, wenn er auf Systemressourcen zugreift. Der Security Manager wirkt hier als AEF. Beispiel: Der Zugriff auf eine Datei erfordert eine Permission vom Typ java.io.FilePermission, die den gleichen Namen trägt wie die Datei. • Anwendungsentwickler können beliebige Permission-Typen definieren. Permissions dieser Art nennen wir fachliche Permissions. Der Security Manager wirkt hier nur als ADF. Man kann ihn also fragen, ob ein bestimmtes Codestück die KundenPermission mit dem Namen Eva Meier besitzt. Die Verknüpfung mit dem zu schützenden Objekt (Frau Meiers Kundendaten) programmiert der Entwickler selbst. Die Zugriffsarten der Autorisierung heißen bei Java Actions. Die Menge der möglichen Actions hängt vom Permission-Typ ab und kann auch leer sein. Zu der erwähnten FilePermission gehören die Actions read, write, delete und execute. 14 Uniform Resource Locator 26 3.4. JAAS – Informationssicherheit in der Java-Welt Actions sind als Attribute von Permissions realisiert. Ein Permission-Objekt mit genau einer Action entspricht einer Berechtigung im Sinne von Abschnitt 3.2.4. Die Administration der Zugriffsrechte erfolgt durch Anlegen bzw. Ändern eines Policy-Objekts – in der Standardimplementierung ist das eine Textdatei, in die man die Zugriffsrechte in einer vorgegebenen Syntax einträgt. 3.4.3. Know your principals Bei der codezentrierten Autorisierung hängen die Zugriffsrechte ab von der Herkunft und dem Unterzeichner des auszuführenden Codes. Das hat den Zweck, das System vor bösartigen Programmen aus nicht vertrauenswürdigen Quellen zu schützen. Wer aber schützt das System vor seinen Benutzern? Wer schützt die Benutzer voreinander? JAAS erweitert die beschriebenen Autorisierungsmöglichkeiten um den Benutzerkontext: Die Zugriffsrechte hängen nicht nur davon ab, welcher Code ausgeführt wird, sondern auch davon, wer ihn ausführt. Benutzer heißen bei JAAS Principals. Ein Principal ist charakterisiert durch seinen Typ (eine Implementierung des Interface java.security.Principal) und einen Namen. Damit ist das Subjekt im Sinne der Frage „Darf der das?“ die Kombination aus Herkunftsort, Unterzeichner und Principal – also ein recht komplexes Gebilde. Wenn man nur mit vertrauenswürdigem Code arbeitet, spricht aber nichts dagegen, codebezogene Eigenschaften zu ignorieren und Zugriffsrechte allein an Principals zu vergeben. Das Ergebnis wäre eine benutzerzentrierte Autorisierung. Objekte und Zugriffsarten sind die gleichen wie ohne JAAS. An der Autorisierung ändert JAAS also nur wenig. Eine Frage ist allerdings noch offen: Wie kommen die Principals in das System? JAAS stellt für diesen Zweck Authentifizierungsschnittstellen bereit. Wie in Abschnitt 3.1 dargelegt, sind die Möglichkeiten, einen Benutzer zu authentifizieren, nicht auf die Anmeldung mit Name und Passwort beschränkt. JAAS greift daher auf Pluggable Authentication Modules (PAM) zurück. Das PAM-Konzept ist von der Open Software Foundation standardisiert (siehe [PAM95]) und in vielen UNIX-artigen Betriebssystemen implementiert. Die Idee besteht darin, dass die Anwendung die Authentifizierung an separate Komponenten delegiert, so dass es ihr egal sein kann, ob Benutzer sich per Passwort, Kerberos oder Fingerabdruckscanner anmelden. Die Authentifizierungskomponenten sind per Konfiguration austauschbar, ohne dass ein Neukompilieren der Anwendung nötig wäre. Bei JAAS heißen diese Komponenten Login Modules. Jedes Login Module ist selbst dafür verantwortlich, sich die für die Authentifizierung notwendigen Informationen zu besorgen. Dazu kann es z. B. einen Bildschirmdialog zum Abfragen von Name und 27 3. Grundlagen einer Sicherheitsarchitektur Passwort starten. Ein anderes Beispiel: Das Login Module delegiert die eigentliche Authentifizierung an das Betriebssystem, indem es auf die Kennung des angemeldeten Benutzers zurückgreift.15 Jedes Login Module liefert pro Authentifizierungsvorgang einen oder mehrere Principals. Die Semantik der Principals bestimmt das Login Module. Beispiel: Ein Principal steht für den Benutzernamen, weitere Principals stehen für die Gruppen, denen der Benutzer angehört. Der Security Manager verlässt sich darauf, dass Login Modules nur korrekt authentifizierte Principals liefern. Login Modules sind also trusted software. 15 Die Firma Sun bietet lauffähige Login Modules dieser Art für die Betriebssysteme Solaris und Windows NT kostenlos zum Download an (siehe [JAAb]). 28 »I’m what you guys call a User.« Tron (Walt Disney Pictures, 1982) 4. QuasarAuthorization Ein Ziel dieser Diplomarbeit ist der Entwurf einer Autorisierungskomponente, die vielseitig einsetzbar und dabei einfach zu bedienen ist. Als Ausgangspunkt dient die QuasarAuthorization, eine Eigenentwicklung von sd&m. Sie ist in Java programmiert. Wir beschreiben im Folgenden den Ist-Zustand der Komponente und zählen verbesserungswürdige Merkmale auf. 4.1. Beschreibung der Komponente Die QuasarAuthorization ist eine generische Autorisierungskomponente; generisch deshalb, weil sie keine Annahmen über die Autorisierungsobjekte macht und deshalb mit verschiedensten Anwendungen zusammenarbeiten kann. Die Subjekte der Autorisierung nennen wir von nun an generell Benutzer, egal ob es sich um Personen handelt oder um non-trusted software. (Logische) Objekte der Autorisierung nennen wir Ressourcen, um sie von (technischen) Java-Objekten zu unterscheiden, und Zugriffsarten bezeichnen wir als Operationen. Die QuasarAuthorization referenziert ihre Entitäten (Benutzer, Ressourcen etc.) anhand eindeutiger Namen. 4.1.1. Funktionsumfang Die Komponente bietet folgende Dienste an: • Benutzeradministration: Pflege der Benutzerdaten (Login-Name, Vor- und Nachname etc.) und Einordnung der Benutzer in eine Gruppenhierarchie • Administration von Zugriffsrechten: Definieren von Berechtigungen und Rollen und ihre Zuordnung zu Benutzern und Gruppen • Authentifizierung der Benutzer anhand von Name und Passwort • Rechteprüfung: Realisierung der ADF (siehe Abschnitt 3.3) Die QuasarAuthorization unterstützt positive und negative Rechtevergabe. Die Berechtigungen kann man mit Randbedingungen versehen, die im Zuge der Rechteprüfung ausgewertet werden. 29 4. QuasarAuthorization 4.1.2. Datenmodell und Außensicht Wir stellen das Datenmodell der QuasarAuthorization vor, sowie die Schnittstellen, die sie exportiert und importiert. Die Abbildungen 4.1 und 4.2 zeigen das Datenmodell in Form von UML-Klassendiagrammen (Attribute und Methoden sind nicht aufgeführt). Das erste Diagramm zeigt den administrierbaren Teil des Modells; er besteht aus den Entitätstypen und Beziehungen, deren Ausprägungen man zur Laufzeit anlegen, ändern und löschen kann. Das zweite Diagramm ergänzt das erste; es stellt den Bezug zu den wichtigsten Initialisierungs- und Konfigurationsschnittstellen her. In den Diagrammen sind die einzelnen Elemente so benannt wie im Programmcode und der Dokumentation der Komponente (siehe [JH03a, JH03b]). Beim Gebrauch deutscher Fachbegriffe weichen wir zum Teil von der Dokumentation ab1 , um für die drei Komponenten(entwürfe) dieser Arbeit eine einheitliche Terminologie zu schaffen. Im Anhang findet sich mit Tabelle A.1 eine Gegenüberstellung der Begriffe, die in verschiedenen Kontexten mit gleicher oder ähnlicher Bedeutung verwendet werden. Die Entitätstypen und Schnittstellen im Einzelnen: • Users repräsentieren Benutzer des Systems. Die wichtigsten Attribute eines Benutzers sind der eindeutige (Login-)Name und das Passwort; daneben gibt es typische Benutzerattribute wie Vor- und Nachname und Locale-Informationen (Sprache, Nationalität). Zusätzlich kann man anwendungsspezifische Eigenschaften definieren. • UserGroups repräsentieren benannte Gruppen von Benutzern. Durch Schachtelung erhält man eine Gruppenhierarchie; so lässt sich z. B. die Organisationsstruktur eines Unternehmens abbilden. Jeder Benutzer kann beliebig vielen Gruppen angehören, jede Gruppe kann höchstens einer anderen Gruppe untergeordnet sein. • Sowohl Benutzern als auch Gruppen kann man Zugriffsrechte zuordnen; der abstrakte Typ Accessor repräsentiert diese gemeinsame Eigenschaft. • Permissions entsprechen Berechtigungen im Sinne von Abschnitt 3.2.4, erweitert um Randbedingungen. Jede Permission hat einen eindeutigen Namen. Sie verknüpft eine Ressource mit einer Operation und beliebig vielen Randbedingungen. • PermissionGroups sind benannte Mengen von Berechtigungen, sie repräsentieren also Rollen. PermissionGroups sind nicht schachtelbar. 1 Beispiel: Die Dokumentation der QuasarAuthorization bezeichnet Ressourcen als Schutzobjekte. 30 4.1. Beschreibung der Komponente granted «i nterface» * Accessor revoked * «interface» * PermissionGroup * * contai ns * «i nterface» «interface» «interface» UserGroup User Permission * * 0..1 * * * depends on 0..1 * * contai ns contai ns 1 Operation 1 * «interface» «i nterface» AccessibleObject AccessRestriction 1 * RestrictionParameter Abbildung 4.1.: Datenmodell der QuasarAuthorization (administrierbarer Teil) «i nterface» «i nterface» AccessibleObject AccessRestriction Operation * 1..* * 1 * RestrictionParameter appli es to * 1 1 * «i nterface» «i nterface» AccessibleObjectType AccessRestrictionType 1 1 * defaul t param eter(s) * 1 «i nterface» «i nterface» AccessController AccessRestrictionController Abbildung 4.2.: Datenmodell, erweitert um die anwendungsseitig zu implementierenden Schnittstellen 31 4. QuasarAuthorization • AccessibleObjects repräsentieren die durch die QuasarAuthorization zu überwachenden Ressourcen. Sie bestehen nur aus einem symbolischen Namen. • Operations stehen für die Operationen (Zugriffsarten) der Autorisierung. Operationsnamen sind anwendungsspezifisch definierbar; typische Grundoperationen wie READ, WRITE und EXECUTE sind vordefiniert. • AccessRestrictions sind Randbedingungen, die man Berechtigungen zuordnet, um deren Gültigkeit einzuschränken. Eine gewährte Berechtigung ist nur dann tatsächlich wirksam, wenn die Rechteprüfung alle zugeordneten Randbedingungen als erfüllt erachtet. • Wenn Randbedingungen Parameter haben, speichert man diese in Instanzen der Klasse RestrictionParameter; sie enthält Parametername, -typ und -wert als Attribute. Besteht z. B. eine Randbedingung darin, dass das Recht, eine Überweisung zu tätigen, nur bis zu einem bestimmten Geldbetrag gilt, dann ist dieser Höchstbetrag der Parameter. Die im Diagramm mit depends on bezeichnete Beziehung des Permission-Typs zu sich selbst ist ein Spezialfall einer Randbedingung: Man kann damit die Wirksamkeit einer gewährten Berechtigung davon abhängig machen, ob dem Benutzer eine bestimmte andere Berechtigung ebenfalls gewährt wurde. So weit die administrierbaren Elemente; Abbildung 4.2 zeigt daneben vier angeforderte Schnittstellen2 : Die Autorisierungskomponente definiert und importiert diese Schnittstellen, und sie fordert von demjenigen, der die Komponente benutzen will, dass er die Implementierungen liefert. Angeforderte Schnittstellen implementiert man im Allgemeinen durch Adapter; ein Adapter bildet vorhandene Software auf die (maßgeschneiderte) Schnittstelle ab.3 Die folgenden vier angeforderten Schnittstellen entkoppeln die QuasarAuthorization von der Anwendung: • Jede Ressource ist einem AccessibleObjectType zugeordnet. Der Name ist etwas irreführend; die Schnittstelle birgt weit mehr Funktionalität als ein Entitätstyp oder Datentyp. Der AccessibleObjectType kapselt die Semantik seiner Ressourcen; er erfüllt folgende Aufgaben: – Er bildet technische Objekte auf Ressourcen ab. Diese Funktion kommt bei der Rechteprüfung zum Einsatz, denn geprüft werden immer technische Objekte, während die Autorisierungskomponente nur Ressourcen 2 3 Synonyme: Stützschnittstellen, Anforderungsschnittstellen Quasar empfiehlt die Verwendung von angeforderten Schnittstellen und Adaptern beim SoftwareEntwurf, um Importeure und Exporteure optimal zu entkoppeln. 32 4.1. Beschreibung der Komponente (logische Objekte) verwaltet. Im Allgemeinen bilden verschiedene AccessibleObjectTypes ein und dasselbe technische Objekt auf verschiedene Ressourcen ab. – Er definiert, welche Operationen auf seine Ressourcen grundsätzlich angewandt werden können. – Er definiert, welche Operationen sich untereinander implizieren (vgl. Abschnitt 3.2.2 über implizite Rechte) und realisiert damit einen Teil der Auswertungslogik. – Er verknüpft seine Ressourcen mit einem AccessController. • Die Schnittstelle AccessController kapselt den größten Teil der Auswertungslogik; die Implementierung (die von der Anwendung beizusteuern ist) bestimmt maßgeblich das Ergebnis der Rechteprüfung. Im Lieferumfang der QuasarAuthorization ist eine Standardimplementierung namens SimpleAccessController enthalten, die als Ausgangspunkt für anwendungsspezifische Implementierungen dienen kann. • Jede Randbedingung ist einem AccessRestrictionType zugeordnet. Er definiert Anzahl und Typen der Parameter seiner Randbedingungen, und er führt auf gegebenen Parametermengen Konformitätsprüfungen durch. Außerdem verknüpft er seine Randbedingungen mit einem AccessRestrictionController. • Die Schnittstelle AccessRestrictionController übernimmt jenen Teil der Rechteprüfung, der feststellt, ob eine gegebene Randbedingung erfüllt ist. Auch hierfür sind einige Standardimplementierungen im Lieferumfang der Komponente enthalten; eine davon – der UpperLimitRestrictionController – ist auf das obige Beispiel anwendbar (Überweisung mit beschränktem Geldbetrag). Das Ergebnis, das ein AccessController ermittelt, kann von einem oder mehreren Objekten des Typs AccessRestrictionController abhängen (das ist in Abbildung 4.2 durch einen Abhängigkeitspfeil angedeutet). Die QuasarAuthorization importiert noch weitere Schnittstellen, und sie exportiert natürlich auch welche. Das Komponentendiagramm in Abbildung 4.3 vermittelt einen Überblick der Außensicht. Über die beiden exportierten Schnittstellen links im Bild bietet die Komponente ihre Kerndienste an: • Die Schnittstelle AuthenticationManager ist der Zugang zum Authentifizierungsdienst. Die Methode authenticate erwartet den Login-Namen und das 33 4. QuasarAuthorization Accessi bl eObj ectT ype AccessControll er AccessRestrictionT ype AccessRestri cti onControll er Authenticati onM anager Authorizati onM anager QuasarAuthorization Authenti cati onAdm i ni strati onM anager Authori zati onAdm i nistrati onM anager Authenticati onStorage Authorizati onStorage UpdateEnvi ronm ent UserDataSource UserAuthenti cati on Abbildung 4.3.: Außensicht der QuasarAuthorization (UML-2.0-Notation) Passwort des Benutzers als Argumente; im Erfolgsfall liefert sie ein UserTicket – eine Art Ausweis, der bei Rechteprüfungen vorzulegen ist. Auch Passwortänderungen nimmt man über diese Schnittstelle vor. • Der AuthorizationManager ist der Zugang zur Rechteprüfung. Seine zentrale Methode ist: boolean isAccessPermitted ( S t r i n g accessibleObjectTypeName , Operation operation , Object object , UserTicket t i c k e t , Object parameter ) Die Aufrufparameter beschreiben detailliert den Zugriffswunsch: Man gibt an, gemäß welchem AccessibleObjectType die Auswertung erfolgen soll und welche Operation auf welches (technische) Objekt durch welchen Benutzer (repräsentiert durch ticket) auszuführen ist. Das parameter-Argument ist optional, es wird ggf. für die Bewertung von Randbedingungen herangezogen. Weitere Methoden dienen zur Registrierung von Implementierungen der angeforderten Schnittstellen AccessibleObjectType, AccessRestrictionType, AccessController und AccessRestrictionController. Die Registrierung erfolgt im Zuge der Initialisierung der QuasarAuthorization. Die Komponente exportiert zwei weitere Schnittstellen (rechts im Bild): • Über den AuthenticationAdministrationManager administriert man Benutzerdaten und einen Teil der Zugriffsrechte. Es gibt Methoden zum Anlegen, Ändern und Löschen von User- und UserGroup-Objekten. Außerdem definiert 34 4.1. Beschreibung der Komponente man hier die Mitgliedschaft von Benutzern in Gruppen bzw. von Gruppen in anderen Gruppen (Gruppenhierarchie). Die Zugriffsrechte legt man fest, indem man vordefinierte Rollen mit Benutzern und Gruppen verknüpft, wobei die granted-Beziehung (siehe Abbildung 4.1) in positiven Rechten resultiert und die revoked-Beziehung in negativen. • Über den AuthorizationAdministrationManager definiert man Rollen und Berechtigungen sowie Randbedingungen und deren Parameter. Im oberen und unteren Teil der Abbildung 4.3 sind die angeforderten Schnittstellen der Komponente dargestellt. Die oberen Schnittstellen wurden bereits im Zusammenhang mit dem Datenmodell erläutert, jetzt widmen wir uns den verbleibenden: • AuthenticationStorage, AuthorizationStorage und UpdateEnvironment betreffen die Datenhaltung der QuasarAuthorization. Man implementiert diese Schnittstellen durch einen Persistenz-Adapter, der die Anbindung an ein Datenbanksystem (oder einen sonstigen Datenspeicher) realisiert. Über AuthenticationStorage speichert und lädt die Autorisierungskomponente ihre Userund UserGroup-Objekte, über AuthorizationStorage alle anderen Entitäten des Datenmodells. Die QuasarAuthorization hält ihre Daten für den laufenden Betrieb in einem Hauptspeicher-Cache. UpdateEnvironment kommt bei der Aktualisierung dieses Caches zum Einsatz. • UserDataSource und UserAuthentication ermöglichen die Anbindung externer Systeme. UserDataSource dient dem Import von Benutzern und Gruppen aus einer externen Datenquelle (z. B. einem LDAP4 -Verzeichnis). Falls die QuasarAuthorization Authentifizierungen nicht selbst durchführen soll, stellt UserAuthentication die Verbindung zu einem externen Authentifizierungsdienst her. Die Methoden entsprechen denen des AuthenticationManagers; wenn sich ein Benutzer dort anmeldet, werden Login-Name und Passwort einfach an UserAuthentication weitergereicht. 4.1.3. Berechtigungs-Cache Die vorliegende Arbeit konzentriert sich auf die Außensicht von Autorisierungskomponenten. Die Architektur der QuasarAuthorization trennt aber nicht strikt zwischen Außen- und Innensicht und macht es notwendig, sich auch mit einem Implementierungsaspekt zu befassen: dem Berechtigungs-Cache5 . 4 5 Lightweight Directory Access Protocol (ein Protokoll für den Zugriff auf Verzeichnisdienste) com.sdm.quasar.authorization.implementation.PermissionEvaluationCache 35 4. QuasarAuthorization Dieser Cache ist zunächst einmal ein Zwischenspeicher für Benutzer- und Berechtigungsdaten. Er reduziert die Zugriffe der Komponente auf die Persistenz. Darüber hinaus kommt beim Befüllen des Berechtigungs-Caches bereits Auswertungslogik zum Einsatz: Beziehungen zwischen Benutzern und Berechtigungen, die eigentlich nur indirekt auf dem Weg über Gruppen und/oder Rollen existieren, werden aufgelöst und direkt im Cache abgelegt. Implementierungen der Schnittstelle AccessController arbeiten mit dem vorverarbeiteten Cache-Inhalt. Dieses Vorgehen hat Vor- und Nachteile. Es macht Rechteprüfungen effizienter, weil die Gruppenhierarchie zum Zeitpunkt der Prüfung nicht mehr traversiert wird. Allerdings enthält der Cache weniger Informationen als das persistente Datenmodell, dessen Mächtigkeit daher nicht vollständig ausgenutzt werden kann. Außerdem werden Änderungen an den Benutzer-/Berechtigungsdaten erst wirksam, nachdem der Berechtigungs-Cache neu befüllt wurde. Bei einer Datenmenge realistischer Größe liegt der Zeitaufwand hierfür in der Größenordnung von mehreren Minuten. 4.2. Verbesserungspotential Die QuasarAuthorization hat ihren Ursprung im Projektgeschäft von sd&m. Auch Weiterentwicklungen wurden bisher überwiegend von Projekten veranlasst, die Lösungen für ihre individuellen Autorisierungsanforderungen verlangten. Herausgekommen ist eine Komponente, die (anders als ihr Name vermuten lässt) nicht der reinen QuasarLehre entspricht. Wir nehmen an dieser Stelle den Faden aus dem Abschnitt Motivation und Ziel des Einführungskapitels wieder auf: Die Komponente weist architektonische Schwächen auf, sie ist unpräzise spezifiziert, und es fehlt ein klarer Bezug zu verwandten Produkten. Wo liegen Ansatzpunkte für Verbesserungen? 4.2.1. Architektur und Implementierung Das Hello-World-Programm6 , das als Beispiel für die Initialisierung und den Betrieb der QuasarAuthorization dient, wirkt mit seinen rund 500 Codezeilen7 eher abschreckend. Ein Grund für diesen Umfang ist, dass die Komponente durch Aufrufe im Programmtext konfiguriert wird, statt durch externe Konfigurationsdateien. Im Rahmen der Initialisierung wird auf technischer Seite die Verbindung zur Persistenz hergestellt und der Berechtigungs-Cache erzeugt. Auf fachlicher Seite werden AccessibleObjectTypes, AccessRestrictionTypes und die zugehörigen Controller-Objekte 6 7 com.sdm.quasar.authorization.sample.AuthorizationSample (Version vom 19.05.2004) Kommentare und Leerzeilen eingeschlossen 36 4.2. Verbesserungspotential registriert. Der vermeintlich simple Anstoß einer Rechteprüfung erfordert im Beispielprogramm mehrere Methodenaufrufe: Anstatt die Methode isAccessPermitted des AuthorizationManagers zu benutzen, bedient man sich hier direkt eines AccessController-Objekts, an das man über den Umweg des AccessibleObjectType gelangt.8 Doch auch der sauberere Weg über den AuthorizationManager ist aus Sicht des Anwendungsprogrammierers komplizierter als nötig. Ideal wäre eine Prüfung, die nur den Benutzer, die Ressource und die Operation als Argumente benötigt. Doch bei der QuasarAuthorization ist bei jeder Rechteprüfung zusätzlich ein AccessibleObjectType anzugeben. Gemäß dem Prinzip der Trennung von Zuständigkeiten sollte man sich bei der Aufgabenverteilung zwischen den Schnittstellen einer Komponente an den potentiellen Aufrufern orientieren. Die Schnittstelle stellt im Idealfall dem Importeur genau die Funktionalität zur Verfügung, die er zur Erfüllung seiner Aufgaben braucht. Bei der Schnittstelle AuthorizationManager sind Zuständigkeiten vermengt: Sie enthält die Methode zur Rechteprüfung, die von sehr vielen Programmierern sehr oft benutzt wird; sie enthält aber auch Registrierungsmethoden, die nur für die Initialisierung der Komponente gebraucht werden. Das Datenmodell (Abbildungen 4.1 und 4.2) ermöglicht recht komplexe Beziehungsgeflechte, was unvermeidbar ist, wenn man viele verschiedene Anwendungsszenarien unterstützen will. Trotzdem bleiben unschöne Einschränkungen: Eine Gruppe darf nicht mehreren anderen Gruppen angehören. Rollen sind nicht schachtelbar. AccessRestrictions sind zwar im Grunde ein universeller Erweiterungsmechanismus – doch man kann sie nur auf der Ebene der Berechtigungen definieren; was sich damit z. B. nicht realisieren lässt, ist der wechselseitige Ausschluss von Rollenzuordnungen (separation of duty aus dem RBAC-Standard). Die QuasarAuthorization verfügt auch nicht über ein Sitzungskonzept – Benutzer haben keine Möglichkeit, nur Teilmengen ihrer Rechte zu aktivieren (Prinzip der minimalen Rechte). Der Authentifizierungsdienst der QuasarAuthorization ist auf die Abfrage von Login-Name und Passwort des Benutzers festgelegt (ebenso die externe Authentifizierung über UserAuthentication). Diese Entwurfsentscheidung schränkt die Anwendungsmöglichkeiten ein: Die Einbindung von biometrischen oder kryptographischen Mechanismen ist nicht ohne weiteres möglich, dasselbe gilt für Single-Sign-On. Der anzuwendende Authentifizierungsmechanismus ist im Grunde ein Implementierungsaspekt, die Schnittstelle sollte darüber keine Annahmen machen. Hier können wir von JAAS und seinem Authentifizierungsdienst lernen. Zur Erinnerung: JAAS ist 8 Die Dokumentation zum AuthorizationManager empfiehlt dieses Vorgehen bei hohen Performanzanforderungen. 37 4. QuasarAuthorization von konkreten Authentifizierungsmechanismen unabhängig, weil diese in Login Modules ausgelagert sind. Die QuasarAuthorization wendet das gleiche Prinzip bereits bei der Rechteprüfung an, indem es diese Aufgabe an den AccessController delegiert. Das UserTicket, das einen erfolgreich authentifizierten Benutzer im System repräsentiert, taugt nur für den Einsatz innerhalb des trusted system, denn es verfügt über keinerlei Schutz gegen Manipulation. Außerdem ist die Gültigkeitsdauer nicht beschränkt: Ist der Benutzer erst einmal angemeldet, kann er beliebig lange im System agieren. Bei der Vorstellung des Berechtigungs-Caches haben wir bereits die von ihm verursachten Einschränkungen erwähnt. Bezüglich des großen Rechenaufwands für Befüllung bzw. Aktualisierung des Caches sollte man überdenken, ob es dabei wirklich notwendig ist, jedesmal die Berechtigungen aller Benutzer zu berechnen. Im Allgemeinen ist zu einem gegebenen Zeitpunkt nur eine überschaubare Teilmenge der Benutzer am System angemeldet. Wir schlagen daher vor, die individuellen Berechtigungen eines Benutzers erst zum Login-Zeitpunkt zu berechnen und im Cache abzulegen. Alternativ könnte man diese Berechtigungen auch direkt im UserTicket-Objekt speichern; es wäre dann ein Zugriffsausweis (capability) im Sinne von Abschnitt 3.2.1. 4.2.2. Spezifikation Die Semantik der Schnittstellen ist im Quelltext in Form von Javadoc-Kommentaren beschrieben, und zwar informell in englischer Sprache. Es ist nicht möglich, nur anhand dieser Kommentare beispielsweise einen anwendungsspezifischen AccessController zu programmieren. Der Entwickler ist hier auf den vorhandenen SimpleAccessController angewiesen, den er quasi als Bauanleitung benutzt. Das ist aber nicht Design by Contract (eher Design by Example). Die Methode isAccessPermitted liefert nur true oder false, der Benutzer hat das Recht zum Zugriff, oder er hat es nicht. Die Frage nach dem warum beantwortet die Komponente nicht. Um zu erfahren, in welcher Weise Änderungen der Benutzerund Berechtigungsdaten das Ergebnis der Rechteprüfung beeinflussen, sind Kenntnisse über die Implikations- und Konfliktlösungsregeln nötig – doch diese Regeln sind nirgendwo formal spezifiziert. Sie liegen nur als Programmcode vor, und dieser ist auch noch auf mehrere Stellen verteilt: auf die Klasse PermissionEvaluationCache (Berechtigungs-Cache) und auf alle Klassen, die AccessibleObjectType, AccessController und AccessRestrictionController implementieren. 38 4.3. Fazit 4.3. Fazit Die aktuelle Version der QuasarAuthorization kam bisher in vier sd&m-Projekten zum Einsatz.9 Dabei handelt es sich durchweg um mittlere bis große Projekte mit Aufwänden zwischen 14 und 50 Bearbeiterjahren. Es gibt keine Aufzeichnungen darüber, wie viele Projekte den Einsatz der QuasarAuthorization zwar in Betracht gezogen haben, sich dann aber doch für eine Eigenentwicklung entschieden. Die dargelegten Fakten (und der hohe firmeninterne Bekanntheitsgrad der Quasar-Komponenten) lassen aber vermuten, dass dieser Fall schon mehrere Male eingetreten ist. Vor allem die Architekten und Entwickler in kleinen Projekten dürfte der hohe Konfigurationsaufwand (Controller- und Type-Objekte) abschrecken. Im Projektgeschäft ist reines Design by Contract anhand präziser Schnittstellenspezifikationen (noch) nicht allgemein üblich. Das Anfertigen der Spezifikation ist ein Kostenfaktor, und bei Komponenten, die von einer statischen Menge von Entwicklern gebaut und gewartet werden, lohnt sich ein hoher Aufwand hierfür in der Tat nicht. Für Standardkomponenten jedoch – mit ihren hohen Anforderungen bezüglich Verlässlichkeit, Wiederverwendbarkeit und Wartbarkeit – zahlt sich eine präzise Spezifikation in jedem Fall aus. In den folgenden Kapiteln entwerfen wir eine präzise spezifizierte Autorisierungskomponente, die einfach zu konfigurieren und zu bedienen ist, ohne dabei gegenüber der existierenden Komponente zu viel an Funktionalität einzubüßen. Wo es sinnvoll erscheint, heben wir bestehende Beschränkungen der Flexibilität auf (z. B. beim Authentifizierungsdienst). Wir betreiben dabei kein reengineering mit nachträglichem Spezifizieren der QuasarAuthorization; die Ergebnisse der vorliegenden Arbeit sind vielmehr als Studie zu verstehen, wie eine generische Autorisierungskomponente für betriebliche Anwendungen prinzipiell aussehen kann. 9 ein Projekt für das Bayerische Geologische Landesamt und eines für den Reiseveranstalter Thomas Cook AG; außerdem zwei Projekte, die wegen Vertraulichkeitsvereinbarungen zwischen sd&m und den Kunden nicht näher genannt werden 39 »You believe in the Users?« Tron (Walt Disney Pictures, 1982) 5. BasicAuthorization Wir entwerfen unsere Autorisierungskomponente in zwei Stufen. Die erste nennen wir BasicAuthorization. An ihr erproben wir unter anderem die Tragfähigkeit der noch jungen Sprache QSL. 5.1. Funktionsumfang Die BasicAuthorization bietet grundsätzlich die gleichen Dienste an wie die QuasarAuthorization, also • Benutzeradministration, • Administration von Zugriffsrechten, • Authentifizierung und • Rechteprüfung. Die Einschränkungen liegen im Detail: Diese Komponente1 unterstützt nur positive Rechtevergabe und kennt keine Randbedingungen. Außerdem definieren die Schnittstellen eine starre Auswertungslogik. 5.2. Datenmodell Abbildung 5.1 illustriert das Datenmodell der BasicAuthorization. Für fast alle Entitätstypen gibt es Pendants bei der existierenden Komponente; teils verwenden wir andere Namen: Die Gruppe nennen wir Group (statt UserGroup), die Berechtigung Right (statt Permission), für die Ressource steht Resource (statt AccessibleObject) und für die Rolle Role (statt PermissionGroup). Der AbstractUser übernimmt den Part des Accessor, der neue Typ AbstractRight fasst Berechtigungen und Rollen zusammen. Wir identifizieren alle Entitäten anhand symbolischer Namen. Hierzu definieren wir als formale Basis den Typ AuthorizationEntity, von dem alle anderen Typen erben 1 Der Einfachheit halber schreiben wir Komponente statt Komponentenentwurf. 41 5. BasicAuthorization «interface» * AbstractUser «i nterface» has * AbstractRight * * contains contai ns * * «i nterface» «i nterface» «i nterface» «interface» Group User Right Role * * 1 1 «interface» «i nterface» Operation Resource * appl i es to * Abbildung 5.1.: Datenmodell der BasicAuthorization (im Diagramm nicht dargestellt). Er definiert eine Methode namens getName, die den Namen der Entität liefert und eine equals-Methode, die diesen Namen zum Unterscheidungskriterium macht. Für die QSL-Spezifikation der Entitätstypen verweisen wir auf Anhang B.1. Mehrere Beschränkungen des alten Modells hebt der Entwurf auf: Man kann jetzt eine Hierarchie von Rollen definieren, und auch Gruppen sind beliebig schachtelbar. Möglich wird das durch Anwendung des Composite-Pattern (nach [GHJV95]) an zwei Stellen: Jede Gruppe enthält beliebige viele AbstractUser-Objekte (das können sowohl Benutzer als auch Gruppen sein), und jede Rolle enthält beliebig viele Exemplare des Typs AbstractRight (Berechtigungen und Rollen). Die Rechtefestlegung geschieht auf der Ebene von AbstractUser und AbstractRight, woraus insbesondere folgt, dass Berechtigungen nicht nur als Bestandteil von Rollen, sondern auch direkt einem AbstractUser zugeordnet werden können. Das steht zwar nicht im Einklang mit dem RBAC-Prinzip, doch die BasicAuthorization eignet sich damit auch zur Umsetzung einfacher, nicht-rollenbasierter Berechtigungskonzepte. 5.3. Schnittstellen Die Außensicht der BasicAuthorization besteht aus lediglich fünf Programmschnittstellen, Abbildung 5.2 partitioniert sie in die drei logischen Schnittstellen Operativer Betrieb, Instrumentierung und Administration. Technische Aspekte wie die Datenhaltung bleiben unberücksichtigt. Bei der Aufteilung der Funktionalität auf die einzelnen 42 5.3. Schnittstellen Instrum enti erung ResourceDefiniti on Operativer Betri eb Authenti cation Adm i ni strati on BasicAuthorization Authori zati onCheck UserAdm i n Authori zati onAdm i n Abbildung 5.2.: Außensicht der BasicAuthorization M yApplication M yResourceDefinition Operati ver Betrieb Instrum enti erung Administration GUI Adm inistration BasicAuthorization Abbildung 5.3.: Die BasicAuthorization in einer Beispielkomposition. Die Komponente My- ResourceDefinition implementiert die Instrumentierungsschnittstelle und ist Bestandteil der Anwendung (MyApplication). Für die Administration kommt hier eine generische Benutzeroberfläche (Administration GUI) zum Einsatz. 43 5. BasicAuthorization Schnittstellen lehnen wir uns an die QuasarAuthorization an, legen jedoch größeren Wert auf die klare Trennung von Zuständigkeiten. Abbildung 5.3 illustriert, wie man die BasicAuthorization mit der Anwendung verbindet. Die folgenden Unterabschnitte enthalten Auszüge der Schnittstellenspezifikationen. Die komplette QSL-Spezifikation der Außensicht findet sich in Anhang B.2. 5.3.1. Operativer Betrieb Die Schnittstellen für den operativen Betrieb repräsentieren die beiden Kerndienste der Komponente: Authentifizierung und Rechteprüfung. Die Authentication-Schnittstelle hat nur die Methode authenticate: Quelltext 5.1: Authentication.authenticate User authenticate ( const String username , const String password) // a u t h e n t i c a t e s the user ' username ' by means o f ' password ' and r e t u r n s // an a p p r o p r i a t e User−o b j e c t on s u c c e s s . post r e s u l t . getName ( ) . equals (username) error a u t h e n t i c a t i o n F a i l e d // There i s no user r e g i s t e r e d by t h i s name , or the password i s // i n c o r r e c t . Benutzer melden sich also wie bei der QuasarAuthorization mittels Name und Passwort am System an. Als Resultat übernimmt hier eine Referenz auf das User-Objekt die Aufgabe des UserTicket. Das Scheitern der Authentifizierung wird durch einen Fehler signalisiert. Diese Schnittstelle sieht keine Funktion zur Passwortänderung vor; das fällt unter die Zuständigkeit der Administration. Von allen Schnittstellen hat AuthorizationCheck die meisten Importeure und wird bei weitem am häufigsten aufgerufen, weshalb eine möglichst einfache Bedienbarkeit anzustreben ist. Für den Großteil der Programmierer in einem Projekt ist diese Schnittstelle das einzige, was sie von der Autorisierungskomponente zu sehen bekommen. AuthorizationCheck dient der Rechteprüfung, die einzige Methode heißt mayPerform (siehe Quelltext 5.2). Sie ist das Pendant zur Methode isAccessPermitted des AuthorizationManager der QuasarAuthorization. Allerdings benötigt mayPerform keinen AccessibleObjectType als Argument. Der Anwendungsprogrammierer, der die Rechteprüfung anstößt, übergibt nur das technische Objekt des Zugriffs, die gewünschte Operation und den Benutzer; er braucht keine tiefer gehenden Kenntnisse über das Berechtigungskonzept. Zur Auswahl des passenden AccessibleObjectType wären solche Kenntnisse sehr wohl nötig. Die Spezifikation von mayPerform überlässt dem Aufrufer die Entscheidung darüber, was zu tun ist, wenn das übergebene technische Objekt auf keine bekannte 44 5.3. Schnittstellen Ressource abbildbar ist. In so einem Fall wird der Fehler unmanagedObject zurückgegeben. Quelltext 5.2: AuthorizationCheck.mayPerform boolean mayPerform( const Object resource , const Operation operation , const User user ) // r e t u r n s t r u e i f ' user ' i s permitted to perform ' operation ' on // ' r e s o u r c e ' and f a l s e o t h e r w i s e . error unmanagedObject // ' r e s o u r c e ' i s not under a c c e s s c o n t r o l . 5.3.2. Instrumentierung Unter dem Begriff Instrumentierung verstehen wir die fachliche Konfiguration der Komponente, also alle Maßnahmen zur Anpassung an den Anwendungskontext. Bei der BasicAuthorization ist die gesamte Instrumentierung auf die angeforderte Schnittstelle ResourceDefinition konzentriert, ihre Implementierung liegt in der Verantwortung der Anwendung. Die zwei wichtigsten Methoden sind getResource und getOperations (siehe Quelltext 5.3). getResource bildet technische Objekte auf Ressourcen ab, und getOperations legt fest, welche Operationen auf welche Ressourcen anwendbar sind. In dieser Hinsicht ähnelt ResourceDefinition dem AccessibleObjectType der QuasarAuthorization. Doch es gibt einen wesentlichen Unterschied: Im Falle der QuasarAuthorization registriert man beliebig viele AccessibleObjectType-Objekte, und bei jeder Rechteprüfung ist eines davon auszuwählen. Dagegen sieht ResourceDefinition genau eine Implementierung vor, die man während der Initialisierung festlegt – danach befasst sich kein Entwickler mehr damit. Quelltext 5.3: Auszug aus ResourceDefinition Resource getResource ( const Object object ) // maps a g e n e r a l o b j e c t to a r e s o u r c e e n t i t y . I f no mapping can be // determined , n u l l i s returned . // . . . post . . . pre getResource ( o b j e c t ) != n u l l List<Operation> getOperations ( Object object ) // r e t u r n s a l l a p p r o p r i a t e o p e r a t i o n s f o r ' o b j e c t ' . post r e s u l t != n u l l post . . . 45 5. BasicAuthorization Die für den Entwurf gewählte Technik ist natürlich weniger flexibel, wir setzen hier eine 1:n-Beziehung zwischen Ressourcen und technischen Objekten voraus (bei der QuasarAuthorization ist n:m möglich). Jedoch können wir nur durch dieses Vorgehen die Rechteprüfung durch AuthorizationCheck.mayPerform so einfach gestalten. Wie stellt sich die Instrumentierung in der Praxis dar? Zur Veranschaulichung entwerfen wir ein simples Szenario: Mitarbeiter der Personalabteilung (im Folgenden Personaler genannt) arbeiten mit personenbezogenen Daten der Angestellten des Unternehmens. Mit Hilfe der Autorisierungskomponente ist ein Berechtigungskonzept zu realisieren, das die Zugriffsmöglichkeiten der Personaler auf die Daten einschränkt. Bei den Personaldaten (das sind die Ressourcen) ist nur grobkörnig zwischen normalen und leitenden Angestellten zu unterscheiden. Lediglich ein Teil der Personaler soll Zugriff auf die Daten leitender Angestellter erhalten. Wir definieren zwei Ressourcen namens employee und executive, die die normalen und leitenden Angestellten repräsentieren. Auf alle Personaldaten wird entweder lesend oder schreibend zugegriffen, also definieren wir für beide Ressourcen einheitlich die Operationen read und write. Quelltext 5.4 zeigt eine beispielhafte Java-Implementierung der ResourceDefinition für diese Anwendung. Das Kriterium, nach dem sich normale von leitenden Angestellten unterscheiden, ist ein typisches Beispiel für Anwendungswissen; hier wird anhand des Jahresgehaltes differenziert (Programmzeile 21). 5.3.3. Administration Die Administrationsschnittstellen UserAdmin und AuthorizationAdmin enthalten Methoden zum Anlegen, Ändern und Löschen der Entitäten. Über UserAdmin administriert man die Benutzerdaten, die Gruppenhierarchie und die Zuordnung von Berechtigungen bzw. Rollen zu Benutzern bzw. Gruppen. Über AuthorizationAdmin definiert man die Berechtigungen und Rollen. Die Aufgabenverteilung zwischen UserAdmin und AuthorizationAdmin ist also mit der Aufteilung zwischen AuthenticationAdministrationManager und AuthorizationAdministrationManager bei der QuasarAuthorization vergleichbar. Sie ist sinnvoll in der Hinsicht, dass die Dienste von UserAdmin häufiger in Anspruch genommen werden als die von AuthorizationAdmin; bei betrieblichen Anwendungen ändert sich auf Seiten der Benutzer und ihrer individuellen Privilegien häufiger etwas als bei den Berechtigungs- und Rollendefinitionen, die eher statischer Natur sind. Beide Schnittstellen haben deshalb potentiell einen unterschiedlichen Nutzerkreis. Das Interessanteste an den Administrationsschnittstellen ist die Auswertungslogik: Sie ist hier als Bestandteil der Schnittstellensemantik spezifiziert. Die Auswertungslogik kommt zwar beim Vorgang der Rechteprüfung zur Anwendung, ihre Spezifikation wäre aber bei der Schnittstelle AuthorizationCheck fehl am Platz; die meisten Auf- 46 5.3. Schnittstellen Quelltext 5.4: Beispielimplementierung von ResourceDefinition (Java) 2 import java . u t i l . * ; // . . . e v t l . w e i t e r e import−Anweisungen . . . 4 public class MyResourceDefinition implements ResourceDefinition { public public public public 6 8 static static static static final final final final Resource EMPLOYEE = new MyResource( " employee " ) ; Resource EXECUTIVE = new MyResource( " executive " ) ; Operation READ = new MyOperation ( " read " ) ; Operation WRITE = new MyOperation ( " write " ) ; 10 private L i s t operations , dummyList ; 12 public MyResourceDefinition () { operations = new A r r a y L i s t ( 2 ) ; operations . add(READ) ; operations . add(WRITE) ; dummyList = new A r r a y L i s t ( 0 ) ; } 14 16 18 private boolean i s E x e c u t i v e ( S t a f f s t a f f ) { return s t a f f . getSalary () > 100000; } 20 22 public Resource getResource ( Object o b j e c t ) { i f ( o b j e c t instanceof S t a f f ) { i f ( i s E x e c u t i v e (( S t a f f ) o b j e c t ) ) return EXECUTIVE ; else return EMPLOYEE; } else return null ; } 24 26 28 30 32 public L i s t getOperations ( Object o b j e c t ) { i f ( o b j e c t instanceof S t a f f ) { return operations ; } else return dummyList ; } 34 36 38 40 // . . . w e i t e r e Methoden . . . 42 } 47 5. BasicAuthorization rufer von mayPerform wollen nur wissen, ob ein Zugriffsrecht gewährt ist, aber nicht, warum. Vor dem Gros der Anwendungsprogrammierer verbergen wir die Details des Berechtigungskonzepts. Der Administrator (oder der Entwickler eines Administrationswerkzeugs) ist aber auf die Kenntnis der Auswertungslogik angewiesen. UserAdmin erweitert AuthorizationCheck, damit der Administrator mayPerform zum Test der Rechtezuordnungen verwenden kann. Der Großteil der Auswertungslogik ist bei UserAdmin spezifiziert, und zwar in den Invarianten aus Quelltext 5.5. Quelltext 5.5: Auszug aus UserAdmin invariants ancestor in getClosure ( aUser ) <=> ( ancestor . equals ( aUser ) or e x i s t s g in getParents ( aUser ) : ancestor in getClosure (g) ) mayPerform( resource , operation , u) <=> ( operation in rDef . getOperations ( resource ) and ( e x i s t s aUser , aRight , r : aUser in getClosure (u) and aRight in getRights ( aUser ) and aRight in admin . getClosure ( r ) and admin . getResource ( r ) . equals ( rDef . getResource ( resource )) and admin . getOperation ( r ) . equals ( operation ) ) ) Die erste Invariante definiert mit der Methode getClosure die Hülle von AbstractUser. Die Hülle eines Benutzers besteht aus ihm selbst und allen Gruppen, denen er direkt oder indirekt angehört. Die Spezifikation von AuthorizationAdmin definiert einen analogen Hüllenbegriff für AbstractRight: Die Hülle einer Berechtigung besteht aus ihr selbst und allen Rollen, denen sie direkt oder indirekt angehört. Auf dieser Basis lässt sich die Semantik der Rechteprüfung recht knapp formulieren (siehe zweite Invariante): Benutzer u hat genau dann das Recht, die Operation auf die Ressource anzuwenden, wenn irgendeinem aUser ∈ Hülle(u) eine passende Berechtigung r oder ein Element aRight ∈ Hülle(r) zugeordnet ist. Man beachte, dass diese Spezifikation die Auswertungslogik rein deklarativ beschreibt; sie gibt weder Algorithmus noch Datenstruktur vor, lässt also ausreichende Freiheiten für eine effiziente Implementierung der Rechteprüfung. 48 »Speak friend and enter.« The Lord of the Rings (J.R.R. Tolkien) 6. AdvancedAuthorization Die BasicAuthorization ist einfacher zu konfigurieren und anzuwenden als die QuasarAuthorization. Ihr Einsatzfeld ist allerdings begrenzt, da die Rechtevergabe allein auf dem Erlaubnisprinzip beruht (vgl. Abschnitt 3.2.3). Die zweite Stufe des Entwurfs – wir nennen sie AdvancedAuthorization1 – bricht diese Beschränkung auf. Außerdem realisiert sie ein flexibleres Authentifizierungskonzept. 6.1. Funktionsumfang Auch diese Komponente bietet die Dienste • Benutzeradministration, • Administration von Zugriffsrechten und • Rechteprüfung an. Die Authentifizierung zählt dagegen nicht zu den Kerndiensten, sondern ist ausgelagert. Ersetzt wird sie durch einen Dienst zur • Sitzungsverwaltung, der Login und Logout abwickelt. Was genau beim Login geschieht (insbesondere, ob und wie authentifiziert wird) definiert die Instrumentierung. Im Gegensatz zur QuasarAuthorization unterstützt diese Komponente die selektive Aktivierung von Rollen – ein wichtiger Bestandteil des RBAC-Standards. Während der Sitzung eines Benutzers sind also nicht zwangsläufig alle seine Zugriffsrechte aktiv. Das ermöglicht die Durchsetzung des Prinzips der minimalen Rechte. Was die Autorisierung betrifft, so unterstützt die AdvancedAuthorization sowohl positive als auch negative Rechtevergabe. 6.2. Datenmodell Im Datenmodell (Abbildung 6.1) findet man alle Elemente des Modells der BasicAuthorization wieder. Die auffälligste Neuerung ist der Entitätstyp Assignment. Er 1 Für advanced existieren mehrere Übersetzungen, sie reichen von erweitert bis hochentwickelt. 49 6. AdvancedAuthorization Assignment posi ti ve: boolean m andatory: bool ean «interface» * «i nterface» AbstractUser * * {ordered} * AbstractRight contains contai ns * {ordered} * «i nterface» «i nterface» «interface» «i nterface» Group User Right Role * * 1 «i nterface» Resource 1 i m pl i es positi ve * «i nterface» * Operation * i m pl i es negative * i m pl i es * * * * appl i es to Abbildung 6.1.: Datenmodell der AdvancedAuthorization repräsentiert die einzelnen Rechtezuordnungen. Im UML-Diagramm ist er als Assoziationsklasse dargestellt, die die Beziehung zwischen Benutzern/Gruppen und Berechtigungen/Rollen näher beschreibt. Aus Spezifikationssicht ist er (ebenso wie alle anderen Typen) eine Schnittstelle (siehe Quelltext 6.1). Berechtigungen und Rollen sind neutrale Zugriffsdefinitionen, erst bei der Zuordnung zu Benutzern und Gruppen werden sie zu Erlaubnissen (isPositive() == true) oder zu Verboten (isPositive() == false). Der Wert von isMandatory() bestimmt, ob es sich um eine zwingend erforderliche (mandatory) Zuordnung handelt, oder ob sie während einer Sitzung inaktiv sein darf. Durch Setzen dieses Attributs kann man verhindern, dass ein Benutzer die ihn betreffenden Verbote einfach Quelltext 6.1: Auszug aus Assignment AbstractRight getRight () // r e t u r n s the a s s i g n e d r i g h t or r o l e . ... boolean isMandatory () ... boolean i s P o s i t i v e () 50 6.2. Datenmodell abschaltet.2 Bei diesem Datenmodell bestehen gegenüber der BasicAuthorization auch Unterschiede seitens einiger Assoziationen: In der Gruppenhierarchie haben die Mitgliedschaftsbeziehungen eine definierte Reihenfolge, und zwar aus Sicht der Mitglieder. Die Menge der Gruppen, denen ein gegebener Benutzer angehört, ist also geordnet. Dasselbe gilt für die Beziehungen zwischen einer Untergruppe und ihren übergeordneten Gruppen. Die einem gegebenen Benutzer bzw. einer Gruppe zugeordneten Berechtigungen und Rollen sind ebenfalls geordnet. Das Ordnen dieser Beziehungen erzeugt Prioritäten und ist eine notwendige (keine hinreichende!) Voraussetzung dafür, dass die Rechteprüfung auch im Falle mehrdeutiger Rechtefestlegungen zu eindeutigen Ergebnissen kommt.3 Übrigens verwaltet auch die QuasarAuthorization Reihenfolgen auf den Gruppenmitgliedschaften und Rollenzuordnungen – mit dem feinen Unterschied, dass grantedund revoked-Zuordnungen dort getrennte Mengen sind (vgl. Abbildung 4.1). Eine systemweite Einstellung legt pauschal fest, ob im Konfliktfall positive oder negative Rechte Vorzug genießen. Dagegen erlaubt die AdvancedAuthorization, die Rechte unabhängig davon zu priorisieren, ob sie positiv oder negativ sind. Im Datenmodell verfügt jetzt der Operation-Typ über zwei Beziehungen zu sich selbst. implies positive verknüpft Operationen miteinander, die sich im Falle einer positiven Rechtezuordnung implizieren. Standardbeispiel: Wer schreiben darf, darf auch lesen. Die Beziehung implies negative betrifft den Fall der negativen Rechtezuordnung. Standardbeispiel: Wer nicht lesen darf, darf auch nicht schreiben. Die Regeln für den positiven und negativen Fall sind nicht zwangsläufig invers. Die Ordnung auf den Operationen ist Sache der Instrumentierung (die auch festlegt, welche Operationen es überhaupt gibt). Das Konzept der implizierenden Operationen realisiert auch die QuasarAuthorization; völlig neu ist dagegen das Merkmal, welches die implies-Beziehung des Typs Resource andeutet: Ressourcen können jetzt auch untereinander in Beziehung stehen. Somit lassen sich Zugriffsrechte auf hierarchisch strukturierten Ressourcenmengen effizient definieren. Ein anschauliches Beispiel für eine Implikationsregel auf Ressourcen haben wir bereits in Abschnitt 3.2.2 genannt: 2 Warum definieren wir Verbote nicht generell als zwingend? – Manchen Beschränkungen unterwirft man sich freiwillig. Beispiel: Ein Benutzer verbietet sich selbst den Schreibzugriff auf die eigenen Dateien, um sie vor versehentlichem Löschen zu schützen. Zum Ändern einer Datei deaktiviert er vorübergehend das Verbot. 3 Warum sind die Berechtigungen innerhalb der Rollen nicht geordnet? – Die Ordnung dient der Auflösung von Konflikten. Zwischen den Berechtigungen einer Rolle sind Konflikte aber von vornherein ausgeschlossen: Jede positiv/negativ zugeordnete Rolle enthält nur positive/negative Berechtigungen und Rollen. 51 6. AdvancedAuthorization In einem Verzeichnisbaum schließt das Zugriffsrecht auf ein Verzeichnis entsprechende Rechte bezüglich aller enthaltenen Dateien und Unterverzeichnisse ein. Die Autorisierungskomponente verwaltet die Beziehungen zwischen den Ressourcen nicht selbst; bezogen auf das Beispiel weiß sie also nicht, welche Datei in welchem Verzeichnis liegt. Sie bekommt die nötigen Informationen über die Instrumentierung. In Abschnitt 6.3.3 untersuchen wir das genauer. 6.3. Schnittstellen Die Außensicht der AdvancedAuthorization ist in Abbildung 6.2 illustriert und in Anhang B.3 spezifiziert. Sie enthält neben den drei logischen Schnittstellen der BasicAuthorization eine vierte: Technische Umgebung. Instrum entierung Authenti cati on ResourceDefi ni ti on Pol i cy Operati ver Betri eb Adm i ni strati on Adv ancedAuthorization Sessi onM anagem ent Authorizati onCheck UserAdm i n Authori zati onAdm i n Reposi tory Logging T echni sche Um gebung Abbildung 6.2.: Außensicht der AdvancedAuthorization 6.3.1. Technische Umgebung Die angeforderte Schnittstelle Repository verbindet die Autorisierungskomponente mit der Persistenz. Die erste Stufe des Entwurfs machte die gesamte Datenhaltung zum Implementierungsgeheimnis – jetzt tragen wir der Tatsache Rechnung, dass man die Komponente für den praktischen Einsatz nicht immer mitsamt Datenbanksystem 52 6.3. Schnittstellen ausliefern kann. Repository definiert eine Reihe von get- und set-Methoden zum Laden und Speichern der Entitäten und ihrer Beziehungen untereinander. Mit der angeforderten Schnittstelle Logging führen wir die Protokollierung von Login-/Logout-Vorgängen und Zugriffen ein. Beispiel: Nachdem die Rechteprüfung ein positives Resultat geliefert hat, ruft die Komponente die Methode accessGranted der Logging-Schnittstelle auf. Als Argumente übermittelt sie die Eckdaten des Zugriffs: Zeitpunkt, Benutzer, Ressource, Operation. Der Adapter, der Logging implementiert, schreibt dann z. B. einen neuen Eintrag in eine Log-Datei, oder er delegiert den Aufruf an die systemweite Ereignisverwaltung – je nach vorhandener Infrastruktur. Die Protokolldaten kann man zur Beweissicherung heranziehen (Wer hat als Letzter auf diese Daten zugegriffen?) oder zur Suche nach Fehlern in den Rechtefestlegungen. 6.3.2. Operativer Betrieb Die neue Schnittstelle SessionManagement dient der Sitzungsverwaltung, Quelltext 6.2 zeigt die drei wesentlichen Methoden. Quelltext 6.2: Auszug aus SessionManagement boolean isValid ( SessionTicket t i c k e t ) NRR ... SessionTicket login () NRR ... void logout ( const SessionTicket t i c k e t ) // i n v a l i d a t e s ' t i c k e t ' . post not i s V a l i d ( t i c k e t ) login wird ohne Parameter aufgerufen und liefert im Erfolgsfall ein Objekt vom Typ SessionTicket. Es repräsentiert die soeben eröffnete Sitzung und ist bei Rechteprüfungen als Argument zu übergeben. Mit dem Ticket sind Informationen über den angemeldeten Benutzer und seine in dieser Sitzung aktiven Rechte verknüpft. Natürlich entsteht so ein Ticket nicht aus dem Nichts – was beim Login-Vorgang hinter den Kulissen abläuft, erläutern wir im Zusammenhang mit der Instrumentierung (siehe nächster Abschnitt). Jedes SessionTicket ist entweder gültig oder ungültig; das ist der wesentliche Unterschied zum UserTicket der QuasarAuthorization (das ist immer gültig). Ob ein Ticket gültig ist, erfragt man von SessionManagement über die Methode isValid. Welche Umstände machen ein SessionTicket ungültig? Auf jeden Fall der Aufruf von logout (dies beendet die Sitzung), doch die Spezifikation lässt Raum für weitere Bedingungen. Einige Beispiele: 53 6. AdvancedAuthorization • Das Ticket hat ein Verfallsdatum. Nach Ablauf einer bestimmten Zeitspanne ist es ungültig. • Das Ticket ist mit einer Prüfsumme ausgestattet. Schlägt der Integritätstest fehl, ist es ungültig. • Ein virtueller Not-Aus-Schalter macht pauschal alle Tickets ungültig (zur Schadensbegrenzung, wenn z. B. ein Einbruch in das Unternehmensnetzwerk erkannt wurde). Was letztendlich über die Ticket-Gültigkeit entscheidet, bleibt das Implementierungsgeheimnis von SessionManagement und SessionTicket. Die Spezifikation der Schnittstelle AuthorizationCheck unterscheidet sich geringfügig von der alten Fassung: mayPerform ruft man jetzt mit einem SessionTicketParameter statt einem User auf, und ein ungültiges Ticket führt zu einem invalidTicket-Fehler. 6.3.3. Instrumentierung Die AdvancedAuthorization fordert neben der ResourceDefinition zwei weitere Instrumentierungsschnittstellen an. Die folgenden Abschnitte behandeln nacheinander die Schnittstellen Authentication, ResourceDefinition und Policy. Ausgelagerte Authentifizierung Wir wenden uns jetzt den Vorgängen zu, die sich beim Login-Vorgang hinter den Kulissen abspielen. Die Autorisierungskomponente delegiert die Authentifizierung des Benutzers und das Auswählen seiner aktiven Rechte an die Schnittstelle Authentication (Quelltext 6.3). Das Sequenzdiagramm in Abbildung 6.3 zeigt das allgemeine Login-Protokoll. Den Anstoß gibt der Aufruf der login-Methode an der Schnittstelle SessionManagement. Die Komponente fordert daraufhin mit getAuthenticatedUser das fertig authentifizierte User-Objekt von Authentication an. Erst jetzt weiß die Komponente, welcher Benutzer sich anmeldet; sie erzeugt eine Liste mit Rechtezuordnungen dieses Benutzers und lässt eine Teilmenge davon als aktiv markieren (getActiveRights). Damit hat die Autorisierungskomponente alle Informationen, die sie braucht, um ein SessionTicket zu generieren. Wir demonstrieren nun, wie man mit der AdvancedAuthorization die herkömmliche Passwort-Authentifizierung nachbildet. Dazu skizzieren wir eine kleine Erweiterungskomponente namens PasswordLogin, die gemäß Abbildung 6.4 mit der AdvancedAuthorization verbunden wird. Die Schnittstelle BasicAuthentication 54 6.3. Schnittstellen Quelltext 6.3: Auszug aus Authentication boolean [] getActiveRights ( const User user , const List<Assignment> assignedRights ) // r e t u r n s an array o f f l a g s , one f o r each element o f ' a s s i g n e d R i g h t s ' . // . . . post r e s u l t . length == assignedRights . s i z e () post f o r a l l i : 0 <= i < r e s u l t . length : assignedRights . get ( i ) . isMandatory () => r e s u l t [ i ] == true User getAuthenticatedUser () // . . . :Adv ancedAuthorization «interface» :Authentication Initi ator l ogi n() getAuthenti catedUser() getActi veRi ghts(user,assi gnedRi ghts) sessi onT i cket Abbildung 6.3.: Allgemeiner Ablauf eines Login-Vorgangs «i nterface» BasicAuthentication authenticate(Stri ng, String) : Sessi onTi cket «real ize» Passw ordLogin Authenti cati on Adv ancedAuthorization BasicAuthenti cation UserAdm in Initiator «reali ze» Sessi onM anagem ent «interface» BasicAuthenticationAdmin setPassw ord(User, Stri ng) : void Abbildung 6.4.: Konfiguration für klassische Passwort-Authentifizierung 55 6. AdvancedAuthorization nimmt nach Art der Komponente BasicAuthorization die Benutzeranmeldung per Name und Passwort entgegen. PasswordLogin prüft die Anfrage und startet im Erfolgsfall eine Sitzung der AdvancedAuthorization, was die bekannten Rückfragen nach sich zieht. Abbildung 6.5 zeigt den zugehörigen Ablauf. Mit dem Aufruf existAbstractUser (Schnittstelle UserAdmin) prüft PasswordLogin zunächst, ob der Benutzer überhaupt (noch) registriert ist. Ansonsten entspricht der Ablauf dem Schema von Abbildung 6.3, allerdings mit der Besonderheit, dass PasswordLogin (als Exporteur der Schnittstelle Authentication) während des Aufrufs der Autorisierungskomponente Rückrufe von ihr erhält.4 Dieses Beispiel geht davon aus, dass beim Login pauschal alle Rechtezuordnungen des Benutzers aktiviert werden (analog zur BasicAuthorization). getActiveRights markiert also sämtliche angebotenen Listeneinträge. Über die Schnittstelle BasicAuthenticationAdmin kann man die Benutzerpasswörter anlegen und ändern. Die AdvancedAuthorization weiß nichts von Passwörtern, die Komponente PasswordLogin ist allein für deren Verwaltung verantwortlich. Dazu legt sie im einfachsten Fall die Name-Passwort-Paare in einer Tabelle ab. Eine andere Möglichkeit: PasswordLogin speichert die Passwörter in der Autorisierungskomponente – deren Schnittstelle UserAdmin bietet nämlich die Möglichkeit, beliebige Benutzerattribute als Schlüssel-Wert-Paare abzulegen. Die Technik, Authentifizierungsvorgänge an externe Komponenten zu delegieren, imitiert das PAM-Konzept von JAAS. Die Frage drängt sich auf, ob man Login Modules, die für JAAS entwickelt wurden, auch für unsere Autorisierungskomponente nutzbar machen kann. Im Prinzip spricht nichts dagegen; die einzige Schwierigkeit besteht darin, die JAAS-Principals auf unser Benutzerschema abzubilden. In Abschnitt 3.4.3 wiesen wir darauf hin, dass die Semantik der Principals variieren kann, je nachdem, welches Login Module die Principal-Objekte erzeugt. Im Grunde repräsentiert jeder Principal nur eine Teilidentität einer Person. Wenn sich z. B. Frau Mustermann heute als mustermann und morgen als administrator an ihrem PC anmeldet, so sind das zwei verschiedene Teilidentitäten derselben Person. Auch die Benutzer, die die AdvancedAuthorization verwaltet, sind nur Teilidentitäten, weil Frau Mustermann auch hier unter verschiedenen Namen registriert sein könnte. Ein Adapter zwischen JAAS und der Autorisierungskomponente braucht also Zugriff auf Wissen über die Benutzerorganisation beider Welten. Das Konfigurationsschema in Abbildung 6.6 zeigt, wie man bei der AdvancedAuthorization die Authentifizierung mit JAAS-Mitteln bewerkstelligt. Der JAASAdapter stellt die für unsere Komponente maßgeschneiderten Dienste bereit. Die Verknüpfung 4 Die Komponente PasswordLogin hat sicherzustellen, dass die Rückrufe sie in einem konsistenten Zustand antreffen (vgl. [Sie04, Abschnitt 6.7]). 56 6.3. Schnittstellen :Passw ordLogin :Adv ancedAuthorization Initi ator authenti cate(usernam e,password) exi stAbstractUser(user) l ogin() getAuthenticatedUser() getActiveRights(user,assi gnedRi ghts) sessionT icket sessionT i cket Abbildung 6.5.: Ablauf eines Login-Vorgangs mit klassischer Passwort-Authentifizierung Java Security / JAAS «i nterface» LoginContext LoginM odule «i nterface» Subj ect * 1 * * Principal IdentityM anagem ent JAASAdapter Authenticati on M yIdentityM anagement «real ize» «i nterface» Adv ancedAuthorization IdentityM anagement mapPri nci palsToUser(Li st) : User Initiator Sessi onM anagem ent Abbildung 6.6.: Konfiguration für JAAS-basierte Authentifizierung 57 6. AdvancedAuthorization der verschiedenen Teilidentitäten delegiert er an die Schnittstelle IdentityManagement. Deren Methode mapPrincipalsToUser bildet die Principals der JAAS-Welt auf einen Benutzer ab, mit dem die Autorisierungskomponente arbeiten kann. Die Implementierung (MyIdentityManagement) braucht natürlich Kenntnisse über beide Welten, was im Diagramm die Abhängigkeitspfeile zu Principal und AdvancedAuthorization andeuten. Die Klassen und Schnittstellen im oberen Teil des Bildes steuert die Java-Plattform bei. Subject und LoginContext bedürfen hier noch einer Erklärung. Subject ist ein Behälter für Principals. Dieser Behälter wird beim Login-Vorgang gefüllt. LoginContext realisiert bei JAAS die Sitzungsverwaltung mit Login und Logout. Nach dem Login stellt der LoginContext das Subject bereit, das die authentifizierten Principals enthält. Außerdem entkoppelt der LoginContext die Anwendung vom LoginModule. Hüllen auf Ressourcen und Operationen Bei der BasicAuthorization führten wir den Hüllenbegriff für AbstractUser und AbstractRight ein. Bei der AdvancedAuthorization definieren wir zusätzliche Hüllen, nämlich auf Ressourcen und auf Operationen. Dazu erweitern wir die Schnittstelle ResourceDefinition um die in Quelltext 6.4 aufgezählten Methoden. Zur Erinnerung: Die Hülle eines Benutzers besteht aus allen Gruppen, denen er direkt oder indirekt als Mitglied zugeordnet ist. Die Hülle einer Ressource stellen wir uns so ähnlich vor, aber da wir die Struktur der Ressourcenmenge nicht kennen, brauchen wir eine andere Definition. Wir definieren die Ressourcenhülle auf eine Weise, die die Rechteprüfung erleichtert: Die Hülle der Ressource r besteht aus allen Ressourcen r0 , für die folgendes gilt: Jede Berechtigung, die auf r0 definiert ist, gilt auch für r. Im Falle des Verzeichnisbaum-Beispiels besteht die Hülle einer Datei aus der Datei selbst und allen Verzeichnissen, die auf dem Pfad von der Datei bis zum Wurzelverzeichnis liegen. Eine Berechtigung, die für das Wurzelverzeichnis definiert wurde, gilt demnach für den ganzen Baum. Quelltext 6.4: Auszug aus ResourceDefinition List<Resource> getClosure ( const Resource resource ) ... List<Operation> getPermissiveClosure ( const Operation operation ) ... List<Operation> getProhibitiveClosure ( const Operation operation ) 58 6.3. Schnittstellen Die Methode getClosure liefert die Hülle der übergebenen Ressource in Form einer Liste. Die Methoden getPermissiveClosure und getProhibitiveClosure liefern die erlaubende bzw. verbietende Hülle der übergebenen Operation: Die erlaubende (verbietende) Hülle der Operation o besteht aus allen Operationen o0 , für die folgendes gilt: Jede Berechtigung, die o0 erlaubt (verbietet), erlaubt (verbietet) auch o. Nehmen wir als Beispiel noch einmal an, dass es nur die Operationen lesen und schreiben gibt, dass die Schreiberlaubnis die Leseerlaubnis einschließt, und dass das Leseverbot zum Schreibverbot führt. Die erlaubende Hülle der Operation lesen besteht dann aus lesen und schreiben. Die verbietende Hülle von lesen besteht nur aus lesen allein (Schreibverbot führt nicht zu Leseverbot). Linearisierung der Rechte Die Schnittstelle Policy definiert zwei Methoden, deren Verhalten das Ergebnis der Rechteprüfung entscheidend beeinflusst (siehe Quelltext 6.5). Quelltext 6.5: Auszug aus Policy pre uAdmin . e x i s t A b s t r a c t U s e r ( user ) List<Assignment> getLinearization ( const AbstractUser user ) ... error c o n f l i c t s W i t h P o l i c y // . . . boolean mayPerformByDefault () ... Das Resultat der Methode mayPerformByDefault bestimmt, zu welchem Ergebnis die Rechteprüfung gelangt, wenn weder ein explizit noch implizit festgelegtes Recht auf den Zugriffswunsch anwendbar ist. Beim Erlaubnisprinzip liefert man in so einem Fall false (was nicht erlaubt ist, ist verboten), beim Verbotsprinzip true (was nicht verboten ist, ist erlaubt). Die Methode getLinearization erzeugt eine geordnete Liste aller Berechtigungen und Rollen, die dem gegebenen AbstractUser explizit und implizit zugeordnet sind. Diese Liste (und den Vorgang ihrer Erzeugung) nennen wir Linearisierung. Ihre Reihenfolge hängt von der Strategie ab, nach der man den Beziehungsgraphen traversiert. Wir betrachten als Beispiel den Graphen in Abbildung 6.7. Die Gruppe A2 sei mit einer Erlaubnis (+) verknüpft, die Gruppe B1 mit einem Verbot (-). Wenn man nun, ausgehend vom Benutzer, auf dem Graphen eine Tiefensuche ausführt, werden die Gruppen-Knoten in folgender Reihenfolge besucht: 59 6. AdvancedAuthorization + A2 B2 A1 B1 - Priorität (0 = höchste) 0 1 2 3 Gruppe Mitglied in Abbildung 6.7.: Beispiel zur Linearisierung • A1, A2, B2, B1 A2 wird vor B1 besucht, die Erlaubnis befindet sich in der Linearisierung also vor dem Verbot. Eine Breitensuche auf demselben Graphen ergibt diese Sequenz: • A1, B1, A2, B2 Hier kommt B1 vor A2, das Verbot steht in der Liste vor der Erlaubnis. Die Policy-Schnittstelle definiert keine konkrete Linearisierungsstrategie, denn das wäre gleichbedeutend mit dem Festverdrahten der Auswertungslogik. Man kann aber beliebig viele Schnittstellen spezifizieren, die Policy erweitern. So lässt sich ein Satz von Standardstrategien für die Linearisierung bereitstellen. Mit der Schnittstelle PositiveOverridePolicy (Quelltext 6.6) geben wir ein Beispiel für dieses Vorgehen. Diese Strategie ist sehr einfach: Positive Rechte haben generell Vorrang vor negativen. Die Methode getLinearization braucht dafür nur sicherzustellen, dass alle positiven Rechtezuordnungen am Anfang der Ergebnisliste stehen – das besagt die Nachbedingung der Methodenspezifikation. Die Invariante bestimmt, welche Elemente überhaupt in der Liste enthalten sind: Es sind alle Rechte, die zu Elementen der Hülle des Benutzers gehören. Die inverse Strategie, bei der alle negativen Rechte Vorrang haben – lässt sich analog definieren; hier stellt man statt der positiven Rechte die negativen an den Listenanfang. Aus dem negativen Vorrang folgt ein sehr restriktives Autorisierungsmodell, und der positive Vorrang erlaubt im Allgemeinen zu viel. Auf der Suche nach differenzierteren Linearisierungsstrategien machen wir einen Abstecher zu einem anderen Gebiet der Informatik, das sich mit einer ähnlichen Fragestellung befasst. 60 6.3. Schnittstellen Quelltext 6.6: Auszug aus PositiveOverridePolicy basicQueries pre uAdmin . e x i s t A b s t r a c t U s e r ( user ) List<Assignment> getLinearization ( const AbstractUser user ) ... post f o r a l l aPos , aNeg in r e s u l t : aPos . i s P o s i t i v e () and not aNeg . i s P o s i t i v e () => r e s u l t . indexOf ( aPos ) < r e s u l t . indexOf (aNeg) ... invariants // . . . a in g e t L i n e a r i z a t i o n ( aUser ) <=> ( a in uAdmin . getAssignments ( aUser ) or e x i s t s g in uAdmin . getParents ( aUser ) : a in g e t L i n e a r i z a t i o n (g) ) Objektorientierte Programmiersprachen, die eine Klassenhierarchie mit Mehrfachvererbung unterstützen, brauchen eine Linearisierungsstrategie zur dynamischen Methodenbindung. Die Mehrfachvererbung führt dazu, dass jede Unterklasse beliebig viele Oberklassen hat, von denen sie verschiedene Implementierungen der gleichen Methode erben kann. Bei einer Methode, die von der Unterklasse nicht überschrieben wird, ist nicht eindeutig, welche der geerbten Implementierungen zur Laufzeit an die Methodendeklaration gebunden wird. Die Implementierungen werden transitiv in der Klassenhierarchie vererbt, und so ergibt sich eine ähnliche Situation wie bei der Gruppenhierarchie unserer Autorisierungskomponente: Für jede Klasse ist die Menge der direkten und indirekten Vererbungsbeziehungen sinnvoll zu priorisieren. Sinnvoll bedeutet hier, dass der Programmierer nicht mit unerwartetem Vererbungsverhalten überrascht werden sollte (Prinzip der geringsten Überraschung – principle of least surprise). Die Programmiersprache Python arbeitet mit Mehrfachvererbung. Die Linearisierungsstrategie nennt man dort Method Resolution Order (MRO). Die MRO funktioniert nach dem C3-Algorithmus.5 Dieser Algorithmus wird in [BCH+ 96] vorgestellt, seine Anwendung in der Python-MRO ist in [Sim03] anschaulich beschrieben. Zwei Bedingungen bilden die Voraussetzung für eine überraschungsarme Linearisierung: • Die Beibehaltung lokaler Prioritäten (local precedence order): In der Linearisierung der Klasse c hat c selbst die höchste Priorität, und für die direkten Ober5 gilt für Python ab Version 2.3 61 6. AdvancedAuthorization klassen von c gilt, dass sie in der Linearisierung von c in der gleichen Reihenfolge auftreten, in der sie c zugeordnet sind. • Das Monotoniekriterium (monotonicity): Wenn in der Linearisierung von c die Klasse c1 Vorrang vor der Klasse c2 hat, dann hat c1 auch in den Linearisierungen aller Unterklassen von c Vorrang vor c2 . Es gibt Klassenkonstellationen, bei denen die gleichzeitige Einhaltung beider Bedingungen nicht erfüllbar ist. Wir erläutern dies anhand Abbildung 6.7, indem wir die local precedence order und das Monotoniekriterium auf unsere Gruppenhierarchie übertragen. Unter diesen Umständen entspricht der dargestellte Graph einer ungültigen Konstellation. Der Grund: In der Linearisierung von A1 kommt A2 vor B2, und in der Linearisierung von B1 kommt B2 vor A2 – das schreibt die local precedence order vor. Die beiden Listen lassen sich aber nicht verschmelzen, ohne das Monotoniekriterium zu verletzen. Die gleichzeitige Mitgliedschaft des Benutzers in den Gruppen A1 und B1 wäre unter diesen Bedingungen nicht zulässig. Die Spezifikation unserer Policy-Schnittstelle sieht ausdrücklich vor, dass der Aufruf der Methode getLinearization auch Scheitern kann (siehe error-Klausel). Die Schnittstelle eignet sich also grundsätzlich für eine Implementierung nach Art des C3-Algorithmus. 6.3.4. Administration Bei der BasicAuthorization war fast die gesamte Auswertungslogik bei den Administrationsschnittstellen spezifiziert. Bei der AdvancedAuthorization ist zwar ein wesentlicher Teil der Logik (die Linearisierung der Rechte) über die Policy-Schnittstelle ausgelagert, jedoch ist auch hier die Schnittstelle UserAdmin der Ort, an dem die eigentliche Rechteprüfung spezifiziert wird. Quelltext 6.7 enthält jene Teile von UserAdmin, die das Verhalten der Methode mayPerform der Schnittstelle AuthorizationCheck definieren (UserAdmin erbt wieder von AuthorizationCheck). Die Methoden isHeadApplicable und listGrantsAccess sind Hilfsmethoden; wir führen sie ein, um die Spezifikation übersichtlicher zu machen.6 Das Resultat der Rechteprüfung wird wie folgt ermittelt: • mayPerform prüft zunächst, ob die Operation überhaupt auf die Ressource anwendbar ist (siehe erste Invariante). Ist das der Fall, wird die Entscheidung an listGrantsAccess delegiert. 6 Die Hilfsmethoden sind eigentlich abstrakt, weil sie nicht zur Implementierung vorgesehen sind – QSL kennt aber keine abstrakten Methoden. 62 6.3. Schnittstellen Quelltext 6.7: Auszug aus UserAdmin basicQueries ... boolean isHeadApplicable ( const List<Assignment> l i s t , const Resource resource , const Operation operation ) ... boolean listGrantsAccess ( const Resource resource , const Operation operation , const List<Assignment> l i s t ) // . . . post l i s t . isEmpty () => r e s u l t == p o l i c y . mayPerformByDefault () post not isHeadApplicable ( l i s t , resource , operation ) => r e s u l t == l i s t G r a n t s A c c e s s ( resource , operation , l i s t . s u b L i s t (1 , l i s t . s i z e ( ) ) ) post isHeadApplicable ( l i s t , resource , operation ) and l i s t . get ( 0 ) . i s P o s i t i v e () => r e s u l t == true post isHeadApplicable ( l i s t , resource , operation ) and l i s t . get ( 0 ) . isNegative () => r e s u l t == f a l s e invariants ... mayPerform( object , op , s T i c k e t ) <=> ( op in rDef . getOperations ( o b j e c t ) and l i s t G r a n t s A c c e s s ( rDef . getResource ( o b j e c t ) , op , g e t C a p a b i l i t y L i s t ( s T i c k e t )) ) isHeadApplicable ( a L i s t , res , op) <=> ( e x i s t s r in aAdmin . getClosure ( a L i s t . get ( 0 ) . getRight ( ) ) : aAdmin . getResource ( r ) in rDef . getClosure ( r e s ) and ( a L i s t . get ( 0 ) . i s P o s i t i v e () and aAdmin . getOperation ( r ) in rDef . getPermissiveClosure (op) or a L i s t . get ( 0 ) . isNegative () and aAdmin . getOperation ( r ) in rDef . g e t P r o h i b i t i v e C l o s u r e (op) ) ) 63 6. AdvancedAuthorization • listGrantsAccess arbeitet auf der Linearisierung aller in der Sitzung aktiven Rechte. Die Methode durchsucht die Linearisierungsliste von vorne nach hinten – dieser Vorgang ist als rekursiver Aufruf mit immer kürzer werdenden Teillisten spezifiziert. Die Rekursion bricht ab, – wenn das Ende der Liste erreicht wird (dann enthielt sie kein anwendbares Recht) oder – wenn eine Rechtezuordnung gefunden wird, die auf die Ressource und die Operation anwendbar ist. Wenn kein anwendbares Recht gefunden wurde, entscheidet mayPerformByDefault, ob der Zugriff gewährt wird. Wenn ein anwendbares Recht gefunden wurde, hängt die Entscheidung nur mehr davon ab, ob das Recht eine Erlaubnis oder ein Verbot darstellt. • isHeadApplicable (siehe zweite Invariante) stellt fest, ob das aktuelle Element der Liste (bei rekursiver Abarbeitung ist das der Listenkopf) auf die Ressource und die Operation anwendbar ist. Dazu betrachten wir die Hülle der Berechtigung/Rolle, die Hülle der Ressource und – die erlaubende Hülle der Operation (falls wir ein positives Recht untersuchen) oder – die verbietende Hülle der Operation (falls wir ein negatives Recht untersuchen). Die Berechtigung/Rolle ist anwendbar, wenn die Schnittmenge der drei betrachteten Hüllen nicht leer ist. Bei der Rechteprüfung durchsuchen wir also die linearisierte Liste aller aktiven Rechte so lange, bis wir auf eine Berechtigung oder Rolle stoßen, die irgendwie auf den Zugriffswunsch passt. 6.4. Potential und Grenzen Alle betrachteten Komponenten – die QuasarAuthorization und die beiden Entwürfe – realisieren eine zentralistische Rechteverwaltung. Ressourcen haben keine Eigentümer, die autonom entscheiden, wer wie auf ihre Objekte zugreifen darf. Zugriffsrechte legt man ausschließlich über die Administrationsschnittstellen fest, und die sind für den Gebrauch durch privilegiertes Personal bestimmt. 64 6.4. Potential und Grenzen Trotzdem ist eine Delegation des Rechts zur Rechtevergabe nicht völlig ausgeschlossen. Als generische Autorisierungskomponente kann die AdvancedAuthorization beliebige Ressourcen verwalten – einschließlich sich selbst. Denkbar wäre eine Konfiguration mit zwei Instanzen der Autorisierungskomponente. Die erste verwaltet die eigentlichen Ressourcen: Personaldaten, Verzeichnisbäume oder etwas in der Art. Die zweite Instanz überwacht die Zugriffe auf die erste. Sie verwaltet die Administrationsschnittstellen bzw. deren Methoden als Ressourcen. So lässt sich eine Rechteverwaltung mit einer Hierarchie von Administratoren aufbauen (Abteilungsadministrator, Niederlassungsadministrator etc.). Die Unterstützung von Randbedingungen ist ein Leistungsmerkmal, das der AdvancedAuthorization im Vergleich zur QuasarAuthorization noch fehlt. Randbedingungen hätten den Entwurf in zweifacher Hinsicht komplizierter gemacht. Man hätte eine weitere Instrumentierungsschnittstelle zur Definition der Semantik der Randbedingungen benötigt (ähnlich den AccessRestrictionTypes bei der QuasarAuthorization). Außerdem hätte die Annahme nicht mehr getragen, dass alle Berechtigungen, die das gleiche Ressourcen-Operations-Paar betreffen, äquivalent sind (eine Annahme, die die Rechteprüfung vereinfacht). Im Übrigen lassen sich statische Randbedingungen über die Instrumentierung realisieren, indem man dasselbe technische Objekt – abhängig von der Erfüllung der Randbedingung – auf verschiedene Ressourcen abbildet. Die Beispielimplementierung MyResourceDefinition (Quelltext 5.4) funktioniert im Grunde nach diesem Prinzip. 65 ». . . what’s past is prologue . . . « The Tempest (William Shakespeare) 7. Zusammenfassung und Ausblick Die AdvancedAuthorization – so sie denn einmal implementiert wird – ist eine ernst zu nehmende Alternative zur QuasarAuthorization. Sie ist einfacher zu konfigurieren und anzuwenden, denn ihre Schnittstellen sind weniger komplex, besser spezifiziert, und vermischte Verantwortlichkeiten wurden abgeschafft. Die größte Vereinfachung betrifft die Rechteprüfung (mayPerform), die mit drei elementaren Parametern und ohne zusätzliches Anwendungswissen auskommt. Die Vorgehensweise, den Entwurf der neuen Komponente in zwei Stufen anzugehen, erwies sich als günstig. In der ersten Stufe (BasicAuthorization) konnten wir uns auf das Wesentliche konzentrieren: klare Untergliederung der Außensicht; Entwicklung des Hüllenkonzepts als Grundlage einer elegant spezifizierbaren Rechteprüfung. Das schuf ein solides Fundament für die zweite Stufe (AdvancedAuthorization): Neue Schnittstellen fügten sich sinnvoll in die Außensicht ein, das Hüllenkonzept übertrugen wir von den Dimensionen Benutzer und Rechte auf die Dimensionen Ressourcen und Operationen. Die Auswertungslogik ist teils per Spezifikation festgelegt und teils variabel. Der variable Anteil konzentriert sich auf eine einzige Schnittstelle und betrifft die Linearisierung einer partiell geordneten Menge von Rechten. Der Authentifizierungsdienst von JAAS stand Pate bei der Konzeption einer neuen, flexibleren Sitzungsverwaltung. Die BasicAuthorization ist nicht nur eine Vorstufe zur AdvancedAuthorization, sie ist als voll funktionsfähige Autorisierungskomponente angelegt. Für den Einsatz in Projekten, die ein einfaches Autorisierungskonzept mit kleiner Ressourcenmenge und rein additiver Rechtevergabe benötigen, ist die BasicAuthorization gegenüber der AdvancedAuthorization vorzuziehen. Eine Autorisierungskomponente, die die parallele Vergabe positiver und negativer Rechte über eine tiefe Benutzerhierarchie hinweg ermöglicht, sollte eigentlich über eine Diagnoseschnittstelle verfügen, die zur Analyse subtiler Konflikte in den Rechtefestlegungen herangezogen werden kann. Wie sollte so eine Schnittstelle aussehen? Welche Informationen sind für den Administrator hilfreich? Kann man diese Schnittstelle überhaupt generisch spezifizieren, ohne Kenntnis der konkreten Auswertungslogik (oder Linearisierungsstrategie)? – Das sind interessante Fragen, an denen weitere Forschungsarbeiten ansetzen können. 67 A. Glossar AbstractRight Entitätstyp, der eine Berechtigung oder eine Rolle repräsentiert AbstractUser Entitätstyp, der einen Benutzer oder eine Gruppe repräsentiert AccessibleObject Entitätstyp, der eine Ressource repräsentiert Accessor Entitätstyp, der einen Benutzer oder eine Gruppe repräsentiert AccessRestriction Entitätstyp, der eine Randbedingung einer Berechtigung repräsentiert ACL Access Control List; Zugriffskontrollliste ADF Access Control Decision Function; ISO-Normbegriff für die Rechteprüfung Administration (von Zugriffsrechten) Rechtefestlegung als Teilaufgabe der Autorisie- rung AEF Access Control Enforcement Function; ISO-Normbegriff, bezeichnet die Durch- setzung der Zugriffsrechte gemäß der Rechteprüfung Angeforderte Schnittstelle Schnittstelle, die die importierende Komponente selbst definiert (Synonyme: Stützschnittstelle, Anforderungsschnittstelle) API Application Programming Interface Assignment Entitätstyp, der eine Rechtezuordnung an Benutzer oder Gruppen repräsentiert Authentifizierung Überprüfung der Identität eines Benutzers Autorisierung Rechteverwaltung als Basis der Zugriffskontrolle; besteht aus Adminis- tration und Rechteprüfung Benutzer Subjekt eines Zugriffs Berechtigung Eine Berechtigung verknüpft eine bestimmte Operation mit einer be- stimmten Ressource. Aus der Zuordnung von Berechtigungen zu Benutzern ergeben sich die wirksamen Zugriffsrechte. 69 A. Glossar DAC Discretionary Access Control; benutzerbestimmbare Zugriffskontrolle Group Entitätstyp, der eine Gruppe repräsentiert Gruppe Eine Gruppe fasst Benutzer und/oder weitere Gruppen zusammen. GUI Graphical User Interface Instrumentierung Maßnahmen zur fachlichen Konfiguration einer Komponente ISO International Organization for Standardization J2SE Java 2 Platform, Standard Edition JAAS Java Authentication and Authorization Service JML Java Modeling Language JVM Java Virtual Machine LDAP Lightweight Directory Access Protocol; Protokoll für den Zugriff auf Verzeich- nisdienste MAC Mandatory Access Control; systembestimmte Zugriffskontrolle MRO Method Resolution Order; Strategie der dynamischen Methodenbindung bei der Programmiersprache Python NIST National Institute of Standards and Technology; Normungsbehörde in den USA NRR non-repeatable read (Wiederholbarkeitseigenschaft von Methoden) OCL Object Constraint Language (Bestandteil der UML) Operation Zugriffsart (z. B. lesen, schreiben, ausführen) Operation Entitätstyp, der eine Operation repräsentiert Permission Entitätstyp, der eine Berechtigung repräsentiert PermissionGroup Entitätstyp, der eine Rolle repräsentiert QSL Quasar Specification Language; eine halbformale Sprache für die Spezifikation von Schnittstellen Quasar Qualitätssoftwarearchitektur 70 RBAC Role-Based Access Control; rollenbasierte Zugriffskontrolle Rechteprüfung Abfrage festgelegter Zugriffsrechte als Teilaufgabe der Autorisierung Resource Entitätstyp, der eine Ressource repräsentiert Ressource Objekt eines Zugriffs RestrictionParameter Entitätstyp, der einen Parameter einer AccessRestriction repräsentiert Right Entitätstyp, der eine Berechtigung repräsentiert Role Entitätstyp, der eine Rolle repräsentiert Rolle Eine Rolle fasst Berechtigungen und/oder weitere Rollen zusammen. RRR restricted repeatable read (Wiederholbarkeitseigenschaft von Methoden) SessionTicket Entitätstyp, der einen authentifizierten Benutzer bzw. dessen Sitzung repräsentiert Sitzung Eine Sitzung existiert vom Zeitpunkt der Anmeldung des Benutzers am Sys- tem bis zu seiner Abmeldung. Während der Sitzung ist unter Umständen nur eine Teilmenge der möglichen Zugriffsrechte aktiv. SQL Structured Query Language; Abfragesprache für relationale Datenbanken UML Unified Modeling Language URL Uniform Resource Locator URR unrestricted repeatable read (Wiederholbarkeitseigenschaft von Methoden) User Entitätstyp, der einen Benutzer repräsentiert UserGroup Entitätstyp, der eine Gruppe repräsentiert VDM Vienna Development Method; eine formale Spezifikationsmethode Z eine formale Spezifikationssprache Zugriffskontrolle Maßnahmen, die sicherstellen, dass niemand unerlaubt auf Ressour- cen zugreift. 71 A. Glossar Resource AbstractUser Group User Entitätstypen der Basic-/AdvancedAuthorization Operation AccessibleObjectType AccessibleObject Accessor UserGroup User Entitätstypen und Schnittstellen der QuasarAuthorization Berechtigung Operation Schutzobjekttyp Schutzobjekt Benutzergruppe Benutzer Begriffe aus der Dokumentation der QuasarAuthorization Permission Action Permission Principal Principal JAAS-Begriffe Tabelle A.1.: Gegenüberstellung der Synonyme Operation Permission Subjekt, Benutzer Gruppe Right Berechtigungsgruppe Objekt, Ressource Zugriffsart, Operation Berechtigung PermissionGroup Randbedingung Role AccessRestriction Randbedingungsparameter Rolle RestrictionParameter 72 B. QSL-Spezifikationen Nachfolgend sind die Spezifikationen aller Entitätstypen und Schnittstellen der Komponentenentwürfe abgedruckt. Die Anordnung der Methoden innerhalb der basicQueries-, derivedQueries- und commands-Sektionen erfolgt alphabetisch nach dem Methodennamen. Dieser Arbeit liegt eine CD bei, die die Spezifikationsdokumente als Textdateien enthält. B.1. Entitätstypen der Basic-/AdvancedAuthorization interface A u t h o r i z a t i o n E n t i t y 2 basicQueries 4 6 boolean equals ( const Object object ) post r e s u l t == ( o b j e c t i n s t a n c e o f A u t h o r i z a t i o n E n t i t y and t h i s . getName ( ) . equalsIgnoreCase ( o b j e c t . getName ( ) ) ) 8 10 S t r i n g getName() post r e s u l t != n u l l interface AbstractUser extends A u t h o r i z a t i o n E n t i t y interface User extends AbstractUser 2 basicQueries 4 6 boolean equals ( const Object object ) post r e s u l t == true => o b j e c t i n s t a n c e o f User // s t r e n g t h e n e d p o s t c o n d i t i o n interface Group extends AbstractUser 2 basicQueries 4 6 boolean equals ( const Object object ) post r e s u l t == true => o b j e c t i n s t a n c e o f Group // s t r e n g t h e n e d p o s t c o n d i t i o n 73 B. QSL-Spezifikationen interface AbstractRight extends A u t h o r i z a t i o n E n t i t y interface Right extends AbstractRight 2 basicQueries 4 boolean equals ( const Object object ) post r e s u l t == true => o b j e c t i n s t a n c e o f Right // s t r e n g t h e n e d p o s t c o n d i t i o n 6 interface Role extends AbstractRight 2 basicQueries 4 boolean equals ( const Object object ) post r e s u l t == true => o b j e c t i n s t a n c e o f Role // s t r e n g t h e n e d p o s t c o n d i t i o n 6 interface Resource extends A u t h o r i z a t i o n E n t i t y 2 basicQueries 4 boolean equals ( const Object object ) post r e s u l t == true => o b j e c t i n s t a n c e o f Resource // s t r e n g t h e n e d p o s t c o n d i t i o n 6 interface Operation extends A u t h o r i z a t i o n E n t i t y 2 basicQueries 4 boolean equals ( const Object object ) post r e s u l t == true => o b j e c t i n s t a n c e o f Operation // s t r e n g t h e n e d p o s t c o n d i t i o n 6 interface Assignment 2 uses AbstractRight 4 basicQueries 6 boolean equals ( Object object ) post r e s u l t == ( o b j e c t i n s t a n c e o f Assignment and t h i s . getRight ( ) . equals ( o b j e c t . getRight ( ) ) and t h i s . i s P o s i t i v e () == o b j e c t . i s P o s i t i v e () and t h i s . isMandatory () == o b j e c t . isMandatory () ) 8 10 12 74 B.2. Schnittstellen der BasicAuthorization 14 16 18 20 22 24 AbstractRight getRight () // r e t u r n s the a s s i g n e d r i g h t or r o l e . post r e s u l t != n u l l boolean isMandatory () // t r u e : The a s s i g n e d r i g h t or r o l e must always be a c t i v e in s e s s i o n s . // f a l s e : The user may l e a v e the a s s i g n e d r i g h t or r o l e i n a c t i v e . boolean i s P o s i t i v e () // t r u e : The a s s i g n e d r i g h t or r o l e a c t s p e r m i s s i v e . // f a l s e : The a s s i g n e d r i g h t or r o l e a c t s p r o h i b i t i v e . interface SessionTicket 2 uses User 4 basicQueries 6 8 User getUser () // r e t u r n s the user to whom t h i s t i c k e t be l on g s . post r e s u l t != n u l l B.2. Schnittstellen der BasicAuthorization Operativer Betrieb interface Authentication 2 uses User 4 basicQueries 6 8 10 12 User authenticate ( const String username , const String password) // a u t h e n t i c a t e s the user ' username ' by means o f ' password ' and r e t u r n s // an a p p r o p r i a t e User−o b j e c t on s u c c e s s . post r e s u l t . getName ( ) . equals (username) error a u t h e n t i c a t i o n F a i l e d // There i s no user r e g i s t e r e d by t h i s name , or the password i s // i n c o r r e c t . interface AuthorizationCheck 2 uses Operation , User 4 basicQueries 6 75 B. QSL-Spezifikationen boolean mayPerform( const Object resource , const Operation operation , const User user ) // r e t u r n s t r u e i f ' user ' i s permitted to perform ' operation ' on // ' r e s o u r c e ' and f a l s e o t h e r w i s e . error unmanagedObject // ' r e s o u r c e ' i s not under a c c e s s c o n t r o l . 8 10 12 Instrumentierung interface ResourceDefinition 2 uses Operation , Resource 4 basicQueries 6 boolean existResource ( const Resource resource ) // I s ' r e s o u r c e ' v a l i d ? 8 pre existResource ( resource ) List<Operation> getOperations (Resource resource ) // r e t u r n s a l l a p p r o p r i a t e o p e r a t i o n s f o r ' r e s o u r c e ' . post r e s u l t != n u l l 10 12 14 Resource getResource ( const Object object ) // maps a g e n e r a l o b j e c t to a r e s o u r c e e n t i t y . I f no mapping can be // determined , n u l l i s returned . // I f s e v e r a l o b j e c t s are mapped to the same r e s o u r c e , they are t r e a t e d // s i m i l a r l y in terms o f a c c e s s c o n t r o l . // Every r e s o u r c e has an i d e n t i f y i n g name . C l a s s names , o b j e c t IDs or // a p p l i c a t i o n l e v e l keys such as account numbers are t y p i c a l examples // o f r e s o u r c e names . The c h o i c e o f the name e v e n t u a l l y a f f e c t s the // g r a n u l a r i t y o f a c c e s s c o n t r o l measures . post r e s u l t != n u l l => existResource ( r e s u l t ) 16 18 20 22 24 derivedQueries 26 pre getResource ( o b j e c t ) != n u l l List<Operation> getOperations ( Object object ) // r e t u r n s a l l a p p r o p r i a t e o p e r a t i o n s f o r ' o b j e c t ' . post r e s u l t != n u l l post r e s u l t . equals ( getOperations ( getResource ( o b j e c t ) ) ) 28 30 32 Administration interface UserAdmin extends AuthorizationCheck 2 76 B.2. Schnittstellen der BasicAuthorization uses AbstractRight , AbstractUser , Group , User 4 6 8 10 variables AbstractUser ancestor , aUser ; AbstractRight aRight ; Right r ; Group g ; User u ; Object resource ; Operation operation ; Authentication aut ; AuthorizationAdmin admin ; ResourceDefinition rDef ; 12 basicQueries 14 16 18 20 22 24 26 28 30 32 34 boolean existAbstractUser ( const AbstractUser user ) // I s ' user ' a v a l i d user or group? pre e x i s t A b s t r a c t U s e r ( user ) List<Group> getParents ( const AbstractUser user ) // r e t u r n s a l l groups o f which ' user ' i s an immediate member . post r e s u l t != n u l l post f o r a l l g in r e s u l t : e x i s t A b s t r a c t U s e r (g) pre e x i s t A b s t r a c t U s e r ( user ) List<AbstractRight> getRights ( const AbstractUser user ) // r e tu r n a l l r i g h t s and r o l e s immediately a s s i g n e d to ' user ' . post r e s u l t != n u l l post f o r a l l aRight in r e s u l t : admin . e x i s t A b s t r a c t R i g h t ( aRight ) derivedQueries pre e x i s t A b s t r a c t U s e r ( user ) List<AbstractUser> getClosure ( const AbstractUser user ) post user in r e s u l t // r e t u r n s the c l o s u r e o f ' user ' . ( s e e i n v a r i a n t s ) 36 commands 38 40 42 44 46 48 pre e x i s t A b s t r a c t U s e r ( user ) pre group not in getParents ( user ) pre user not in getClosure ( group ) // p r e v e n t c y c l e s void addUser( const AbstractUser user , Group group) // makes ' user ' an immediate member o f ' group ' . post group in getParents ( user ) pre e x i s t A b s t r a c t U s e r ( user ) void changePassword( const User user , const String password) // a s s i g n s a new password to ' user ' . post aut . a u th en t ic a te ( user . getName ( ) , password ) . equals ( user ) 50 77 B. QSL-Spezifikationen Group createGroup ( const String groupname) // d e f i n e s a new group . post existAbstractUser ( result ) post r e s u l t . getName ( ) . equals (groupname) error duplicateName // group e x i s t s already 52 54 56 User createUser ( const String username , const String password) // r e g i s t e r s a new user . post existAbstractUser ( result ) post r e s u l t . getName ( ) . equals (username) post aut . a ut h en t icate (username , password ) . equals ( r e s u l t ) error duplicateName // user e x i s t s already 58 60 62 pre e x i s t A b s t r a c t U s e r ( group ) void deleteGroup (Group group) // d e l e t e s the group ( but not the u s e r s and subgroups i t c o n t a i n s ) . post not e x i s t A b s t r a c t U s e r ( group ) 64 66 68 pre e x i s t A b s t r a c t U s e r ( user ) void deleteUser (User user ) // d e l e t e s the user from the system . post not e x i s t A b s t r a c t U s e r ( user ) 70 72 pre admin . e x i s t A b s t r a c t R i g h t ( r i g h t ) pre e x i s t A b s t r a c t U s e r ( user ) pre r i g h t not in getRights ( user ) void grant ( const AbstractRight right , const AbstractUser user ) // e x p l i c i t l y g ra n ts ' r i g h t ' to ' user ' . post r i g h t in getRights ( user ) 74 76 78 80 pre group in getParents ( user ) void removeUser( const AbstractUser user , Group group) // removes ' user ' from ' group ' . post group not in getParents ( user ) 82 84 pre admin . e x i s t A b s t r a c t R i g h t ( r i g h t ) pre e x i s t A b s t r a c t U s e r ( user ) pre r i g h t in getRights ( user ) void withdraw( const AbstractRight right , const AbstractUser user ) // r e v e r s e s the e f f e c t o f ' grant ( r i g h t , user ) ' . post r i g h t not in getRights ( user ) 86 88 90 92 invariants 94 // The c l o s u r e o f an a b s t r a c t user c o n s i s t s o f i t s e l f and a l l groups // c o n t a i n i n g at l e a s t one element o f i t s c l o s u r e . ancestor in getClosure ( aUser ) <=> ( ancestor . equals ( aUser ) or 96 98 78 B.2. Schnittstellen der BasicAuthorization e x i s t s g in getParents ( aUser ) : ancestor in getClosure (g) 100 ) 102 // A use r may perform an op eration on a r e s o u r c e , i f a matching r i g h t // ( or an element o f the c l o s u r e o f a matching r i g h t ) i s a s s i g n e d to an // element o f the user ' s c l o s u r e . mayPerform( resource , operation , u) <=> ( operation in rDef . getOperations ( resource ) and ( e x i s t s aUser , aRight , r : aUser in getClosure (u) and aRight in getRights ( aUser ) and aRight in admin . getClosure ( r ) and admin . getResource ( r ) . equals ( rDef . getResource ( resource )) and admin . getOperation ( r ) . equals ( operation ) ) ) 104 106 108 110 112 114 116 testcases 118 120 unsuccessfulChecks Object managed , unmanaged ; pre rDef . getResource (unmanaged) == n u l l pre rDef . getResource (managed) != n u l l pre operation not in rDef . getOperations (managed) mayPerform(unmanaged , operation , u) −> error unmanagedObject mayPerform(managed , operation , u) −> f a l s e 122 124 126 interface AuthorizationAdmin 2 uses AbstractRight , Role , Right 4 6 variables AbstractRight ancestor , aRight ; Role r ; ResourceDefinition rDef ; 8 basicQueries 10 12 14 16 boolean existAbstractRight ( const AbstractRight right ) // I s ' r i g h t ' a v a l i d r i g h t or r o l e ? pre existAbstractRight ( right ) Operation getOperation ( const Right right ) // r e t u r n s the o p e ra t io n belonging to ' r i g h t ' . post r e s u l t != n u l l 18 pre existAbstractRight ( right ) 79 B. QSL-Spezifikationen List<Role> getParents ( const AbstractRight right ) // r e t u r n s a l l r o l e s o f which ' r i g h t ' i s an immediate member . post r e s u l t != n u l l post f o r a l l r in r e s u l t : e x i s t A b s t r a c t R i g h t ( r ) 20 22 24 pre existAbstractRight ( right ) Resource getResource ( const Right right ) // r e t u r n s the r e s o u r c e belonging to ' r i g h t ' . post r e s u l t != n u l l 26 28 derivedQueries 30 pre existAbstractRight ( right ) List<AbstractRight> getClosure ( const AbstractRight right ) post r i g h t in r e s u l t // r e t u r n s the c l o s u r e o f ' r i g h t ' . ( s e e i n v a r i a n t ) 32 34 36 commands 38 pre existAbstractRight ( right ) pre r o l e not in getParents ( r i g h t ) pre r i g h t not in getClosure ( r o l e ) // p r e v e n t c y c l e s void addRight( const AbstractRight right , Role role ) // makes ' r i g h t ' an immediate member o f ' r o l e ' . post r o l e in getParents ( r i g h t ) 40 42 44 pre rDef . existResource ( resource ) pre operation in rDef . getOperations ( resource ) Right createRight ( const String rightname , const Resource resource , const Operation operation ) // d e f i n e s a new r i g h t . post existAbstractRight ( result ) post r e s u l t . getName ( ) . equals ( rightname ) post getResource ( r e s u l t ) . equals ( resource ) post getOperation ( r e s u l t ) . equals ( operation ) error duplicateName // r i g h t e x i s t s already 46 48 50 52 54 56 Role createRole ( const String rolename) // d e f i n e s a new r o l e . post existAbstractRight ( result ) post r e s u l t . getName ( ) . equals ( rolename ) error duplicateName // r o l e e x i s t s already 58 60 62 pre existAbstractRight ( right ) void deleteRight ( Right right ) // d e l e t e s the r i g h t . post not e x i s t A b s t r a c t R i g h t ( r i g h t ) 64 66 80 B.3. Schnittstellen der AdvancedAuthorization 68 70 pre existAbstractRight ( role ) void deleteRole (Role role ) // d e l e t e s the r o l e ( but not the r i g h t s and s u b r o l e s i t c o n t a i n s ) . post not e x i s t A b s t r a c t R i g h t ( r o l e ) 72 74 76 78 80 82 84 pre r o l e in getParents ( r i g h t ) void removeRight( const AbstractRight right , Role role ) // removes ' r i g h t ' from ' r o l e ' . post r o l e not in getParents ( r i g h t ) invariants // The c l o s u r e o f an a b s t r a c t r i g h t c o n s i s t s o f i t s e l f and a l l r o l e s // c o n t a i n i n g at l e a s t one element o f i t s c l o s u r e . ancestor in getClosure ( aRight ) <=> ( ancestor . equals ( aRight ) or e x i s t s r in getParents ( aRight ) : ancestor in getClosure ( r ) ) B.3. Schnittstellen der AdvancedAuthorization Technische Umgebung interface Repository 2 4 uses AbstractUser , User , Group , AbstractRight , Right , Role , Assignment 6 basicQueries 8 // methods f o r the loading o f p e r s i s t e n t e n t i t i e s 10 List<Group> getAllGroups () 12 List<Right> getAllRights () 14 List<Role> getAllRoles () 16 List<User> getAllUsers () 18 List<Assignment> getAssignments ( const AbstractUser user ) 20 List<AbstractRight> getMembers( const Role role ) 22 List<Group> getParents ( const AbstractUser user ) 81 B. QSL-Spezifikationen 24 Map getProperties ( const AbstractRight right ) 26 Map getProperties ( const AbstractUser user ) 28 30 commands 32 // methods f o r the s t o r i n g o f p e r s i s t e n t e n t i t i e s 34 void setAllGroups ( const List<Group> groups) post getAllGroups ( ) . equals ( groups ) 36 void setAllRights ( const List<Right> rights ) post g e t A l l R i g h t s ( ) . equals ( r i g h t s ) 38 40 void setAllRoles ( const List<Role> roles ) post g e t A l l R o l e s ( ) . equals ( r o l e s ) 42 void setAllUsers ( const List<User> users ) post g e t A l l U s e r s ( ) . equals ( users ) 44 46 void setAssignments ( const AbstractUser user , const List<Assignment> assignments ) post getAssignments ( user ) . equals ( assignments ) 48 50 void setMembers( const Role role , const List<AbstractRight> members) post getMembers( r o l e ) . equals (members) 52 void setParents ( const AbstractUser user , const List<Group> parents ) post getParents ( user ) . equals ( parents ) 54 56 void setProperties ( AbstractUser user , const Map properties ) post g e t P r o p e r t i e s ( user ) . equals ( p r o p e r t i e s ) 58 void setProperties ( AbstractRight right , const Map properties ) post g e t P r o p e r t i e s ( r i g h t ) . equals ( p r o p e r t i e s ) 60 interface Logging 2 commands 4 void accessDenied ( const Date timestamp , const SessionTicket ticket , const Resource resource , const Operation operation ) // l o g s an a u t h o r i z a t i o n check with a n e g a t i v e r e s u l t . 6 8 void accessGranted ( const Date timestamp , const SessionTicket ticket , 82 B.3. Schnittstellen der AdvancedAuthorization 10 const Resource resource , const Operation operation ) // l o g s an a u t h o r i z a t i o n check with a p o s i t i v e r e s u l t . 12 14 16 18 20 void login ( const Date timestamp , const SessionTicket t i c k e t ) // l o g s the beginning o f a s e s s i o n . void loginFailed ( const Date timestamp , const String message) // l o g s a f a i l e d attempt to s t a r t a s e s s i o n . An a r b i t r a r y ' message ' may // d e s c r i b e the reason f o r the f a i l u r e . void logout ( const Date timestamp , const SessionTicket t i c k e t ) // l o g s the ending o f a s e s s i o n . Operativer Betrieb interface SessionManagement 2 uses SessionTicket 4 variables SessionTicket t ; 6 basicQueries 8 10 12 boolean isValid ( SessionTicket t i c k e t ) NRR // r e t u r n s t r u e i f ' t i c k e t ' i s v a l i d or f a l s e o t h e r w i s e . SessionTicket login () NRR // r e t u r n s a S e s s i o n T i c k e t or n u l l i f no s e s s i o n could be e s t a b l i s h e d . 14 commands 16 18 void logout ( const SessionTicket t i c k e t ) // i n v a l i d a t e s ' t i c k e t ' . post not i s V a l i d ( t i c k e t ) 20 22 void logoutAll ( const SessionTicket t i c k e t ) // i n v a l i d a t e s a l l t i c k e t s belonging to the user o f ' t i c k e t ' . post t . getUser ( ) . equals ( t i c k e t . getUser ( ) ) => not i s V a l i d ( t ) interface AuthorizationCheck 2 uses Operation , User 4 variables SessionManagement sm; 6 basicQueries 8 83 B. QSL-Spezifikationen boolean mayPerform( const Object resource , const Operation operation , const SessionTicket t i c k e t ) // r e t u r n s t r u e i f the user a s s o c i a t e d with ' t i c k e t ' i s permitted to // perform ' operation ' on ' r e s o u r c e ' and f a l s e o t h e r w i s e . error not sm . i s V a l i d ( t i c k e t ) => i n v a l i d T i c k e t error unmanagedObject // ' r e s o u r c e ' i s not under a c c e s s c o n t r o l . 10 12 14 Instrumentierung interface Authentication 2 uses Assignment , User 4 variables i n t i ; 6 basicQueries 8 boolean [] getActiveRights ( const User user , const List<Assignment> assignedRights ) // r e t u r n s an array o f f l a g s , one f o r each element o f ' a s s i g n e d R i g h t s ' . // For any ' r e s u l t [ i ] ' s e t to true , ' a s s i g n e d R i g h t s . g e t ( i ) ' i s supposed // to be a c t i v e during ' user ' s next s e s s i o n . // At l e a s t t h o s e assignments marked as mandatory have to be a c t i v e . post r e s u l t . length == assignedRights . s i z e () post f o r a l l i : 0 <= i < r e s u l t . length : assignedRights . get ( i ) . isMandatory () => r e s u l t [ i ] == true 10 12 14 16 18 User getAuthenticatedUser () // r e t u r n s an a u t h e n t i c a t e d user or n u l l i f no user could be // a u t h e n t i c a t e d . 20 interface ResourceDefinition 2 uses Operation , Resource 4 basicQueries 6 boolean existResource ( const Resource resource ) // I s ' r e s o u r c e ' v a l i d ? 8 pre existResource ( resource ) List<Resource> getClosure ( const Resource resource ) post resource in r e s u l t // r e t u r n s the c l o s u r e o f ' r e s o u r c e ' . // The r e s u l t c o n s i s t s o f a l l r e s o u r c e s whose a c c e s s r i g h t s hold f o r // ' r e s o u r c e ' . I f a user i s ( not ) permitted to perform a c e r t a i n 10 12 14 84 B.3. Schnittstellen der AdvancedAuthorization 16 18 20 22 24 26 // // // // // // // o p er a ti o n on an element o f the c l o s u r e , the user i s ( not ) permitted to perform t h i s o p e ra t io n on ' r e s o u r c e ' as w e l l . The c l o s u r e i s ordered . In c a s e o f a c o n f l i c t between p o s i t i v e and n e g a t i v e r i g h t assignments within the c l o s u r e , the foremost r e s o u r c e in the l i s t t a k e s p r e c e d e n c e . Usually , i f the r e s o u r c e s o f a system are h i e r a r c h i c a l l y organized as a t r e e , the c l o s u r e r e p r e s e n t s a path within t h i s t r e e . pre existResource ( resource ) List<Operation> getOperations ( const Resource resource ) // r e t u r n s a l l a p p r o p r i a t e o p e r a t i o n s f o r ' r e s o u r c e ' . post r e s u l t != n u l l 28 30 32 34 36 38 40 List<Operation> getPermissiveClosure ( const Operation operation ) // r e t u r n s a l l o p e r a t i o n s t hat are e q u i v a l e n t to ' operation ' as f a r as // p o s i t i v e a c c e s s r i g h t s are concerned . // I f a user i s permitted to perform an operation contained in the // c l o s u r e , the user i s permitted to perform ' operation ' as w e l l . post operation in r e s u l t List<Operation> getProhibitiveClosure ( const Operation operation ) // r e t u r n s a l l o p e r a t i o n s t hat are e q u i v a l e n t to ' operation ' as f a r as // n e g a t i v e a c c e s s r i g h t s are concerned . // I f a user i s not permitted to perform an operation contained in the // c l o s u r e , the user i s not permitted to perform ' operation ' as w e l l . post operation in r e s u l t 42 44 46 48 50 52 54 56 58 60 62 Resource getResource ( const Object object ) // maps a g e n e r a l o b j e c t to a r e s o u r c e e n t i t y . I f no mapping can be // determined , n u l l i s returned . // I f s e v e r a l o b j e c t s are mapped to the same r e s o u r c e , they are t r e a t e d // s i m i l a r l y in terms o f a c c e s s c o n t r o l . // Every r e s o u r c e has an i d e n t i f y i n g name . C l a s s names , o b j e c t IDs or // a p p l i c a t i o n l e v e l keys such as account numbers are t y p i c a l examples // o f r e s o u r c e names . The c h o i c e o f the name e v e n t u a l l y a f f e c t s the // g r a n u l a r i t y o f a c c e s s c o n t r o l measures . post r e s u l t != n u l l => existResource ( r e s u l t ) derivedQueries pre getResource ( o b j e c t ) != n u l l List<Resource> getClosure ( const Object object ) // r e t u r n s the r e s o u r c e c l o s u r e o f ' o b j e c t ' . // ( s e e ' g e t C l o s u r e ( Resource ) ' ) post r e s u l t . equals ( getClosure ( getResource ( o b j e c t ) ) ) pre getResource ( o b j e c t ) != n u l l List<Operation> getOperations ( Object object ) 85 B. QSL-Spezifikationen // r e t u r n s a l l a p p r o p r i a t e o p e r a t i o n s f o r ' o b j e c t ' . post r e s u l t . equals ( getOperations ( getResource ( o b j e c t ) ) ) 64 interface P o l i c y 2 uses AbstractUser , Assignment 4 variables UserAdmin uAdmin ; 6 basicQueries 8 pre uAdmin . e x i s t A b s t r a c t U s e r ( user ) List<Assignment> getLinearization ( const AbstractUser user ) // r e t u r n s the l i n e a r i z a t i o n ( t o t a l o r d e r i n g ) o f a l l a s s i g n e d r i g h t s o f // ' user ' , taking i n t o account d i r e c t and i n d i r e c t a s s o c i a t i o n s . // The l i s t i s ordered with descending p r i o r i t y . // S u b i n t e r f a c e s may s p e c i f y the l i n e a r i z a t i o n r u l e s . post r e s u l t != n u l l error c o n f l i c t s W i t h P o l i c y // ' user ' s a s s i g n e d r i g h t s are not // c o n s i s t e n t in terms o f the p o l i c y . 10 12 14 16 18 boolean mayPerformByDefault () // t r u e : A c c e s s i s granted u n l e s s a n e g a t i v e r i g h t i s a s s i g n e d . // f a l s e : A c c e s s i s denied u n l e s s a p o s i t i v e r i g h t i s a s s i g n e d . 20 interface P o s i t i v e O v e r r i d e P o l i c y extends P o l i c y 2 uses AbstractUser , Assignment 4 6 variables AbstractUser aUser ; Assignment a , aPos , aNeg ; Group g ; UserAdmin uAdmin ; 8 basicQueries pre uAdmin . e x i s t A b s t r a c t U s e r ( user ) List<Assignment> getLinearization ( const AbstractUser user ) // r e t u r n s the l i n e a r i z a t i o n ( t o t a l o r d e r i n g ) o f a l l a s s i g n e d r i g h t s o f // ' user ' , a s s u r i n g that a l l p o s i t i v e assignments p r e c e d e a l l n e g a t i v e // assignments . post r e s u l t != n u l l post f o r a l l aPos , aNeg in r e s u l t : aPos . i s P o s i t i v e () and not aNeg . i s P o s i t i v e () => r e s u l t . indexOf ( aPos ) < r e s u l t . indexOf (aNeg) 10 12 14 16 18 boolean mayPerformByDefault () // A c c e s s i s denied u n l e s s a p o s i t i v e r i g h t i s a s s i g n e d . post r e s u l t == f a l s e 20 22 86 B.3. Schnittstellen der AdvancedAuthorization 24 26 28 30 32 invariants // D e f i n e the c o n t e n t s o f the l i n e a r i z a t i o n l i s t : I t i s the union o f the // d i r e c t l y a s s i g n e d r i g h t s and o f a l l l i s t s belonging to elements o f // the c l o s u r e o f the user /group ( i t s d i r e c t and i n d i r e c t parents ) . a in g e t L i n e a r i z a t i o n ( aUser ) <=> ( a in uAdmin . getAssignments ( aUser ) or e x i s t s g in uAdmin . getParents ( aUser ) : a in g e t L i n e a r i z a t i o n (g) ) Administration interface UserAdmin extends AuthorizationCheck 2 4 6 8 10 12 14 16 uses AbstractRight , AbstractUser , Assignment , Group , User , Resource , Operation , SessionTicket variables AbstractUser ancestor , aUser ; Assignment a ; Right r ; Group g ; Object o b j e c t ; Operation op ; Resource r e s ; AuthorizationAdmin aAdmin ; Policy policy ; ResourceDefinition rDef ; SessionTicket s T i c k e t ; L i s t<Assignment> a L i s t ; 18 basicQueries 20 22 24 boolean existAbstractUser ( const AbstractUser user ) // I s ' user ' a v a l i d user or group? List<Assignment> getAssignments ( const AbstractUser user ) // r e t u r n s a l l immediate r i g h t assignments o f ' user ' . 26 28 30 32 34 List<Assignment> getCapabilityList ( const SessionTicket t i c k e t ) // r e t u r n s ( in l i n e a r i z e d form ) a l l r i g h t assignments that are a c t i v e // in the s e s s i o n r e p r e s e n t e d by ' t i c k e t ' . post r e s u l t != n u l l pre e x i s t A b s t r a c t U s e r ( user ) List<Group> getParents ( const AbstractUser user ) // r e t u r n s a l l groups o f which ' user ' i s an immediate member . post r e s u l t != n u l l 87 B. QSL-Spezifikationen 36 post 38 pre e x i s t A b s t r a c t U s e r ( user ) Map getProperties ( const AbstractUser user ) // r e t u r n s the p r o p e r t i e s a s s o c i a t e d with ' user ' or n u l l i f // no p r o p e r t i e s . 40 f o r a l l g in r e s u l t : e x i s t A b s t r a c t U s e r (g) ' user ' has 42 pre not l i s t . isEmpty () pre aAdmin . e x i s t A b s t r a c t R i g h t ( l i s t . get ( 0 ) . getRight ( ) ) pre rDef . existResource ( resource ) pre operation in rDef . getOperations ( resource ) boolean isHeadApplicable ( const List<Assignment> l i s t , const Resource resource , const Operation operation ) // h e l p e r method f o r s p e c i f y i n g ' l i s t G r a n t s A c c e s s ' ; r e t u r n s t r u e i f the // head ( f i r s t element ) o f ' l i s t ' concerns ' r e s o u r c e ' and ' operation ' // ( or elements o f t h e i r c l o s u r e s ) and f a l s e o t h e r w i s e . 44 46 48 50 52 pre rDef . existResource ( resource ) pre operation in rDef . getOperations ( resource ) boolean listGrantsAccess ( const Resource resource , const Operation operation , const List<Assignment> l i s t ) // h e l p e r method f o r s p e c i f y i n g ' mayPerform ' ( s e e i n v a r i a n t s ) ; // ' l i s t ' i s searched u n t i l an a p p l i c a b l e element i s found . I f none i s // found , the d e f a u l t d e c i s i o n i s returned . post l i s t . isEmpty () => r e s u l t == p o l i c y . mayPerformByDefault () post not isHeadApplicable ( l i s t , resource , operation ) => r e s u l t == l i s t G r a n t s A c c e s s ( resource , operation , l i s t . s u b L i s t (1 , l i s t . s i z e ( ) ) ) post isHeadApplicable ( l i s t , resource , operation ) and l i s t . get ( 0 ) . i s P o s i t i v e () => r e s u l t == true post isHeadApplicable ( l i s t , resource , operation ) and l i s t . get ( 0 ) . isNegative () => r e s u l t == f a l s e 54 56 58 60 62 64 66 68 70 derivedQueries 72 pre e x i s t A b s t r a c t U s e r ( user ) List<AbstractUser> getClosure ( const AbstractUser user ) post user in r e s u l t // r e t u r n s the c l o s u r e o f ' user ' . ( s e e i n v a r i a n t s ) 74 76 commands 78 pre pre pre pre 80 82 88 e x i s t A b s t r a c t U s e r ( user ) group not in getParents ( user ) user not in getClosure ( group ) // p r e v e n t c y c l e s 0 <= index <= getParents ( user ) . s i z e () B.3. Schnittstellen der AdvancedAuthorization 84 86 88 90 void addUser( const AbstractUser user , Group group , i n t index ) // makes ' user ' an immediate member o f ' group ' . ' group ' i s i n s e r t e d in // the l i s t o f ' user ' s pa r e nts at p o s i t i o n ' index ' , p r e s e r v i n g the // order o f e x i s t i n g elements . post getParents ( user ) . get ( index ) . equals ( group ) post getParents ( user ) . s i z e () == 1 + ' getParents ( user ) . s i z e () error c o n f l i c t s W i t h P o l i c y // The a d d i t i o n o f ' user ' to ' group ' does // not comply with the a u t h o r i z a t i o n p o l i c y . 92 94 96 98 100 102 104 106 108 110 pre pre pre e x i s t A b s t r a c t U s e r ( user ) aAdmin . e x i s t A b s t r a c t R i g h t ( r i g h t ) not e x i s t s a : a in getAssignments ( user ) and a . getRight ( ) . equals ( r i g h t ) pre 0 <= index <= getAssignments ( user ) . s i z e () Assignment assignRight ( const AbstractUser user , const AbstractRight right , boolean positive , boolean mandatory , i n t index ) // a s s i g n s ' r i g h t ' to ' user ' e i t h e r as a p o s i t i v e or a n e g a t i v e r i g h t // and d e f i n e s , i f t h i s assignment i s mandatory in e v e r y s e s s i o n that // ' user ' i n i t i a t e s . The r e s u l t i s i n s e r t e d in the l i s t o f ' user ' s // assignments , p r e s e r v i n g the order o f e x i s t i n g elements . post r e s u l t . getRight ( ) . equals ( r i g h t ) post r e s u l t . i s P o s i t i v e == p o s i t i v e post r e s u l t . isMandatory == mandatory post r e s u l t . equals ( getAssignments ( user ) . get ( index )) post getAssignments ( user ) . s i z e () == 1 + ' getAssignments ( user ) . s i z e () error c o n f l i c t s W i t h P o l i c y // This r i g h t assignment does not comply // with the a u t h o r i z a t i o n p o l i c y . 112 114 116 Group createGroup ( const String groupname) // d e f i n e s a new group . post existAbstractUser ( result ) post r e s u l t . getName ( ) . equals (groupname) error duplicateName // group e x i s t s already 118 120 122 User createUser ( const String username) // r e g i s t e r s a new user . post existAbstractUser ( result ) post r e s u l t . getName ( ) . equals (username) error duplicateName // user e x i s t s already 124 126 128 130 pre e x i s t A b s t r a c t U s e r ( group ) void deleteGroup (Group group) // d e l e t e s the group ( but not the u s e r s and subgroups i t c o n t a i n s ) . post not e x i s t A b s t r a c t U s e r ( group ) pre e x i s t A b s t r a c t U s e r ( user ) void deleteUser (User user ) 89 B. QSL-Spezifikationen // d e l e t e s the user from the system . post not e x i s t A b s t r a c t U s e r ( user ) 132 134 void removeAssignment( const AbstractUser user , i n t index ) // removes the element at p o s i t i o n ' index ' from the l i s t o f ' user ' s // assignments . post ' getAssignments ( user ) . get ( index ) not in getAssignments ( user ) post getAssignments ( user ) . s i z e () == ' getAssignments ( user ) . s i z e () − 1 136 138 140 pre group in getParents ( user ) pre 0 <= index < getParents ( user ) . s i z e () void removeUser( const AbstractUser user , Group group) // removes ' user ' from ' group ' . post group not in getParents ( user ) post getParents ( user ) . s i z e () == ' getParents ( user ) . s i z e () − 1 142 144 146 pre e x i s t A b s t r a c t U s e r ( user ) void setProperties ( AbstractUser user , const Map properties ) // a s s o c i a t e s some a r b i t r a r y p r o p e r t i e s with ' user ' . post g e t P r o p e r t i e s ( user ) . equals ( p r o p e r t i e s ) 148 150 152 invariants 154 // The c l o s u r e o f an a b s t r a c t user c o n s i s t s o f i t s e l f and a l l groups // c o n t a i n i n g at l e a s t one element o f i t s c l o s u r e . ancestor in getClosure ( aUser ) <=> ( ancestor . equals ( aUser ) or e x i s t s g in getParents ( aUser ) : ancestor in getClosure (g) ) 156 158 160 // A user ' s e f f e c t i v e r i g h t s depend on her ( l i n e a r i z e d ) c a p a b i l i t y l i s t . mayPerform( object , op , s T i c k e t ) <=> ( op in rDef . getOperations ( o b j e c t ) and l i s t G r a n t s A c c e s s ( rDef . getResource ( o b j e c t ) , op , g e t C a p a b i l i t y L i s t ( s T i c k e t )) ) 162 164 166 168 // The f i r s t element o f an assignment l i s t i s a p p l i c a b l e on a given // r e s o u r c e and operation , i f t h e r e e x i s t s an i n t e r s e c t i o n between // the c l o s u r e o f the a s s i g n e d r i g h t / r o l e and the c l o s u r e s o f the // r e s o u r c e and o p e ration . isHeadApplicable ( a L i s t , res , op) <=> ( e x i s t s r in aAdmin . getClosure ( a L i s t . get ( 0 ) . getRight ( ) ) : aAdmin . getResource ( r ) in rDef . getClosure ( r e s ) and ( a L i s t . get ( 0 ) . i s P o s i t i v e () and aAdmin . getOperation ( r ) in rDef . getPermissiveClosure (op) 170 172 174 176 178 90 B.3. Schnittstellen der AdvancedAuthorization or a L i s t . get ( 0 ) . isNegative () and aAdmin . getOperation ( r ) in rDef . g e t P r o h i b i t i v e C l o s u r e (op) 180 182 ) 184 ) 186 testcases 188 190 unsuccessfulChecks Object managed , unmanaged ; Operation rightOp , wrongOp ; SessionManagement sm; 192 194 196 198 200 pre rDef . getResource (unmanaged) == n u l l pre rDef . getResource (managed) != n u l l pre rightOp in rDef . getOperations (managed) pre wrongOp not in rDef . getOperations (managed) pre sm . i s V a l i d ( s T i c k e t ) mayPerform(unmanaged , rightOp , s T i c k e t ) −> error unmanagedObject mayPerform(managed , wrongOp , s T i c k e t ) −> f a l s e sm . logout ( s T i c k e t ) mayPerform(managed , rightOp , s T i c k e t ) −> error i n v a l i d T i c k e t interface AuthorizationAdmin 2 uses AbstractRight , Role , Right 4 6 variables AbstractRight aRight , descendant , member; ResourceDefinition rDef ; 8 basicQueries 10 boolean existAbstractRight ( const AbstractRight right ) // I s ' r i g h t ' a v a l i d r i g h t or r o l e ? 12 14 16 pre existAbstractRight ( role ) List<AbstractRight> getMembers( const Role role ) // r e t u r n s a l l immediate members ( r i g h t s and s u b r o l e s ) o f ' r o l e ' . post r e s u l t != n u l l post f o r a l l aRight in r e s u l t : e x i s t A b s t r a c t R i g h t ( aRight ) 18 20 22 24 pre existAbstractRight ( right ) Operation getOperation ( const Right right ) // r e t u r n s the o p e ra t io n belonging to ' r i g h t ' . post r e s u l t != n u l l pre e x i s t A b s t r a c t R i g h t ( r i g h t ) Map getProperties ( const AbstractRight right ) 91 B. QSL-Spezifikationen // r e t u r n s the p r o p e r t i e s a s s o c i a t e d with ' r i g h t ' or n u l l i f // no p r o p e r t i e s . 26 ' r i g h t ' has 28 pre e x i s t A b s t r a c t R i g h t ( r i g h t ) Resource getResource ( const Right right ) // r e t u r n s the r e s o u r c e belonging to ' r i g h t ' . post r e s u l t != n u l l 30 32 derivedQueries 34 pre existAbstractRight ( right ) List<AbstractRight> getClosure ( const AbstractRight right ) post r i g h t in r e s u l t // r e t u r n s the c l o s u r e o f ' r i g h t ' . ( s e e i n v a r i a n t ) 36 38 40 commands 42 pre existAbstractRight ( right ) pre r i g h t not in getMembers( r o l e ) pre r o l e not in getClosure ( r i g h t ) // p r e v e n t c y c l e s void addRight( const AbstractRight right , Role role ) // makes ' r i g h t ' an immediate member o f ' r o l e ' . post r i g h t in getMembers( r o l e ) 44 46 48 pre rDef . existResource ( resource ) pre operation in rDef . getOperations ( resource ) Right createRight ( const String rightname , const Resource resource , const Operation operation ) // d e f i n e s a new r i g h t . post existAbstractRight ( result ) post r e s u l t . getName ( ) . equals ( rightname ) post getResource ( r e s u l t ) . equals ( resource ) post getOperation ( r e s u l t ) . equals ( operation ) error duplicateName // r i g h t e x i s t s already 50 52 54 56 58 60 Role createRole ( const String rolename) // d e f i n e s a new r o l e . post existAbstractRight ( result ) post r e s u l t . getName ( ) . equals ( rolename ) error duplicateName // r o l e e x i s t s already 62 64 66 pre existAbstractRight ( right ) void deleteRight ( Right right ) // d e l e t e s the r i g h t . post not e x i s t A b s t r a c t R i g h t ( r i g h t ) 68 70 pre existAbstractRight ( role ) void deleteRole (Role role ) 72 92 B.3. Schnittstellen der AdvancedAuthorization 74 // d e l e t e s the r o l e ( but not the r i g h t s and s u b r o l e s i t c o n t a i n s ) . post not e x i s t A b s t r a c t R i g h t ( r o l e ) 76 78 80 82 84 pre r i g h t in getMembers( r o l e ) void removeRight( const AbstractRight right , Role role ) // removes ' r i g h t ' from ' r o l e ' . post r i g h t not in getMembers( r o l e ) pre existAbstractRight ( right ) void setProperties ( AbstractRight right , const Map properties ) // a s s o c i a t e s some a r b i t r a r y p r o p e r t i e s with ' r i g h t ' . post g e t P r o p e r t i e s ( r i g h t ) . equals ( p r o p e r t i e s ) 86 invariants 88 90 92 94 // The c l o s u r e o f an a b s t r a c t r i g h t c o n s i s t s o f i t s e l f and − i f i t ' s a // r o l e − o f a l l elements o f the c l o s u r e s o f i t s members . descendant in getClosure ( aRight ) <=> ( descendant . equals ( aRight ) or e x i s t s member in getMembers( aRight ) : descendant in getClosure (member) ) 93 Literaturverzeichnis [And01] A NDERSON, Ross J.: Security Engineering : A Guide to Building Dependable Distributed Systems. New York : Wiley, 2001. – ISBN 0–471–38922–6 [BCH+ 96] B ARRETT, Kim ; C ASSELS, Bob ; H AAHR, Paul ; M OON, David A. ; P LAYFORD, Keith ; W ITHINGTON, P. T.: A Monotonic Superclass Linearization for Dylan. In: Proceedings of the 11th ACM SIGPLAN conference on Object-oriented programming, systems, languages, and applications, ACM Press, 1996. – URL http://www.webcom.com/haahr/dylan/ linearization-oopsla96.html. – ISBN 0–89791–788–X, S. 69–82 [BM93] B ERTINO, Elisa ; M ARTINO, Lorenzo: Object-Oriented Database Systems : Concepts and Architectures. Wokingham : Addison-Wesley, 1993. – ISBN 0–201–62439–7 [Dah04] D AHM, Peter: Quasar Authorization : Anforderungen. Januar 2004. – sd&m AG (unveröffentlicht) [Die03] D IERSTEIN, Rüdiger: Grundlagen der IT-Sicherheit. Vorlesungsunterlagen, Technische Universität München, Fakultät für Informatik. WS 2002/03. – URL http://wwwbayer.in.tum.de/lehre/WS2002/ITS-dierstein/ [DMT04] D ENERT, Ernst ; M ATTHES, Florian ; TAUBNER, Dirk. Betriebliche Informationssysteme und ihre Rolle im Unternehmen. Vorlesungsunterlagen, Technische Universität München, Fakultät für Informatik. WS 2003/04 [Dre02] D REHER, Yvonne: Analyse und Entwurf von Berechtigungskomponenten für betriebliche Informationssysteme, Fachhochschule Konstanz, Diplomarbeit, 2002 [Eck03] E CKERT, Claudia: IT-Sicherheit : Konzepte – Verfahren – Protokolle. 2., überarb. und erw. Aufl. München : Oldenbourg, 2003. – ISBN 3–486– 27205–5 95 Literaturverzeichnis [FFS87] F EIGE, Uriel ; F IAT, Amos ; S HAMIR, Adi: Zero Knowledge Proofs of Identity. In: Proceedings of the nineteenth annual ACM conference on Theory of computing, ACM Press, 1987. – ISBN 0–89791–221–7, S. 210–217 [FS87] F IAT, Amos ; S HAMIR, Adi: How to Prove Yourself : Practical Solutions to Identification and Signature Problems. In: Advances in Cryptology – CRYPTO ’86 (Proceedings), Lecture Notes in Computer Science Bd. 263. Berlin : Springer, 1987. – ISSN 0302–9743, S. 186–194 [FSG+ 01] F ERRAIOLO, David F. ; S ANDHU, Ravi ; G AVRILA, Serban ; KUHN, D. R. ; C HANDRAMOULI, Ramaswamy: Proposed NIST Standard for Role-Based Access Control. In: ACM Transactions on Information and System Security 4 (2001), Nr. 3, S. 224–274. – ISSN 1094–9224 [FSW81] F ERNANDEZ, Eduardo B. ; S UMMERS, Rita C. ; W OOD, Christopher: Database Security and Integrity. Reading, MA : Addison-Wesley, 1981. – ISBN 0–201–14467–0 [GHJV95] G AMMA, Erich ; H ELM, Richard ; J OHNSON, Ralph ; V LISSIDES, John: Design Patterns : Elements of Reusable Object-Oriented Software. Reading, MA : Addison-Wesley, 1995. – ISBN 0–201–63361–2 [ISO] NORM ISO/IEC 10181-3:1996. Information technology – Open Systems Interconnection – Security frameworks for open systems : Access control framework [JAAa] JAAS Dokumentation (Sun). – URL http://java.sun.com/products/ jaas/reference/ [JAAb] JAAS Downloads (Sun). – URL http://java.sun.com/products/jaas/ index-10.html [JH03a] J EROMIN, Holger ; H AFT, Martin: Design Rationale : Quasar AuthorizationManager. November 2003. – Version 5.0 – sd&m AG (unveröffentlicht) [JH03b] J EROMIN, Holger ; H AFT, Martin: Nutzungskonzept : Quasar Authorization-Manager. November 2003. – Version 4.0 – sd&m AG (unveröffentlicht) [JML] The Java Modeling Language (JML). – URL http://www.jmlspecs.org/ [JSS97] JAJODIA, Sushil ; S AMARATI, Pierangela ; S UBRAHMANIAN, V. S.: A Logical Language for Expressing Authorizations. In: Proceedings of the 1997 IEEE Symposium on Security and Privacy, IEEE Computer Society, 1997, S. 31– 42 96 Literaturverzeichnis [Ker] Frequently Asked Questions about Kerberos. – URL http://www.cmf.nrl. navy.mil/CCS/people/kenh/kerberos-faq.html [Lam71] L AMPSON, Butler W.: Protection. In: Proceedings of the fifth Princeton Symposium on Information Sciences and Systems, Princeton University, 1971, S. 437–443 [Lib] Liberty Alliance Project. – URL http://www.projectliberty.org/ [Mar03] M AREK, Detlef: Sprachbasierte Konstruktion sicherer Systeme, Technische Universität München, Diss., 2003 [Mat04] M ATTHES, Florian. Software Engineering betrieblicher Anwendungen. Vorlesungsunterlagen, Technische Universität München, Fakultät für Informatik. WS 2003/04 [Mey97] M EYER, Bertrand: Object-Oriented Software Construction. 2nd edition. Upper Saddle River, NJ : Prentice Hall, 1997. – ISBN 0–13–629155–4 [MM02] M ITCHELL, Richard ; M C K IM, Jim: Design by Contract, by Example. Indianapolis, IN : Addison-Wesley, 2002. – ISBN 0–201–63460–0 [MSP] Microsoft Passport. – URL http://www.microsoft.com/net/services/ passport/ [Oak01] O AKS, Scott: Java Security. 2nd edition. Sebastopol, CA : O’Reilly, 2001. – ISBN 0–596–00157–6 [OQ] OpenQuasar. – URL http://www.openquasar.de/ [PAM95] Open Software Foundation: RFC 86.0 : Unified Login with Pluggable Authentication Modules (PAM). 1995. – URL http://www.opengroup.org/ tech/rfc/mirror-rfc/rfc86.0.txt [Par72] PARNAS, David L.: On the criteria To Be Used in Decomposing Systems into Modules. In: Communications of the ACM 15 (1972), Nr. 12, S. 1053–1058. – ISSN 0001–0782 [PCND04] PARK, Joon S. ; C OSTELLO, Keith P. ; N EVEN, Teresa M. ; D IOSOMITO, Josh A.: A Composite RBAC Approach for Large, Complex Organizations. In: Proceedings of the ninth ACM symposium on Access control models and technologies, ACM Press, 2004. – ISBN 1–58113–872–5, S. 163–172 97 Literaturverzeichnis [RBKW91] R ABITTI, Fausto ; B ERTINO, Elisa ; K IM, Won ; W OELK, Darrell: A Model of Authorization for Next-Generation Database Systems. In: ACM Transactions on Database Systems 16 (1991), Nr. 1, S. 88–131. – ISSN 0362–5915 [SD92] S HEN, HongHai ; D EWAN, Prasun: Access Control for Collaborative Environments. In: Proceedings of the 1992 ACM Conference on ComputerSupported Cooperative Work, ACM Press, 1992. – ISBN 0–89791–542–9, S. 51–58 [SGM02] S ZYPERSKI, Clemens ; G RUNTZ, Dominik ; M URER, Stephan: Component Software : Beyond Object-Oriented Programming. 2nd edition. London : Addison-Wesley, 2002. – ISBN 0–201–74572–0 [Sie02] S IEDERSLEBEN, Johannes (Hrsg.): Softwaretechnik : Praxiswissen für Software-Ingenieure. 2., überarb. und aktualisierte Aufl. München : Hanser, 2002. – ISBN 3–446–21843–2 [Sie04] S IEDERSLEBEN, Johannes: Moderne Software-Architektur : Umsichtig planen, robust bauen mit Quasar. Heidelberg : dpunkt, 2004. – ISBN 3– 89864–292–5 [Sik97] S IKKEL, Klaas: A Group-based Authorization Model for ComputerSupported Cooperative Work / GMD. Sankt Augustin, März 1997. – Arbeitspapier der GMD 1055. URL http://wwwhome.cs.utwente.nl/ ~sikkel/papers/ps/arbeitspapier1055.ps.gz [Sim03] S IMIONATO, Michele: The Python 2.3 Method Resolution Order. 2003. – URL http://www.python.org/2.3/mro.html(Aktualisierungsdatum: 17.11.2003) [SST97] S AAKE, Gunter ; S CHMITT, Ingo ; T ÜRKER, Can: Objektdatenbanken : Konzepte, Sprachen, Architekturen. Bonn : International Thomson Publishing, 1997. – ISBN 3–8266–0258–7 [Swo02] S WOBODA, Joachim. Cryptography and System Security / Kryptologie. Vorlesungsunterlagen, Technische Universität München, Fakultät für Elektrotechnik und Informationstechnik. SS 2002 [VDM] Information on VDM. – URL http://www.csr.ncl.ac.uk/vdm/ [Z] The Z notation. – URL http://www.zuser.org/z/ 98 Index ’x (QSL), 14 <=> (QSL), 14 => (QSL), 14 AbstractUser, 41, 42, 48, 58, 59, 69, 72 Abfrage, 9 AbstractRight, 41, 42, 48, 58, 69 Access Control Decision Function, siehe ADF Access Control Enforcement Function, siehe AEF Access Control List, siehe ACL AccessibleObject, 32, 41, 69, 72 Accessor, 30, 41, 69, 72 AccessRestriction, 32, 37, 69, 71, 72 ACL, 17, 18, 20, 69 Adapter, 32 ADF, 22–24, 26, 29, 69 Administration, 18, 21, 23, 27, 29, 41– 44, 46, 49, 62, 64, 65, 69, 76, 87 AdvancedAuthorization, 3, 19, 49–52, 54, 56, 58, 62, 65, 67, 72, 73, 81 AEF, 22–24, 26, 69 and (QSL), 13 Anforderungsschnittstelle, siehe Schnittstelle, angeforderte API, 26, 69 Assignment, 49, 50, 69 Außensicht, 2, 8 Ausnahme, 9, 12 Authentifizierung, 3, 15, 16, 22, 23, 27–29, 33, 35, 37–39, 41, 44, 49, 54–57, 67, 69 Autorisierung, 3, 15, 16, 19, 20, 22, 23, 27, 29, 32, 36, 49, 69, 71 -skomponente, 1–3, 7, 20, 23, 29, 32, 35, 39, 41, 44, 46, 52, 54, 56, 58, 61, 65, 67 BasicAuthorization, 3, 41–45, 49, 51, 52, 56, 58, 62, 67, 75 basicQueries (QSL), 12, 73 Bell-LaPadula-Modell, 20 Benutzer, 7, 15, 16, 18, 20, 27–30, 32, 34–38, 41, 42, 44, 46, 48–51, 53, 54, 56, 58–60, 62, 67, 69– 72 Benutzerschnittstelle, 7 Berechtigung, 21, 27, 29, 30, 32, 35– 38, 41, 42, 46, 48, 50, 51, 58, 59, 64, 65, 69–72 Berechtigungs-Cache, 35, 36, 38 Biometrie, 16 Blutgruppe, siehe Softwarekategorie 99 Index C3-Algorithmus, 61 Capability, 17, 38 Challenge-Response-Verfahren, 16 commands (QSL), 12, 73 Composite-Pattern, 42 const (QSL), 13 DAC, 20, 70 derivedQueries (QSL), 12, 73 Design by Contract, 1, 2, 8, 38, 39 digitale Signatur, 26 Discretionary Access Control, siehe DAC JAAS, 2, 3, 15, 25, 27, 37, 56–58, 67, 70, 72 Java Authentication and Authorization Service, siehe JAAS Java Virtual Machine, siehe JVM Javadoc, 10 JML, 10, 70 JVM, 25, 70 Fehler, 9, 12 forall (QSL), 13 Funktionssicherheit, 1, 2 Kerberos, 16 Kommando, 9 Komponente, 1–3, 5–8, 29, 30, 32–34, 36, 37, 39, 41, 43–45, 49, 53, 54, 56, 64, 67, 69, 70 Autorisierungs-, 1–3, 7, 20, 23, 29, 32, 35, 39, 41, 44, 46, 52, 54, 56, 58, 61, 65, 67 einfache, 6 Sub-, 7 zusammengesetzte, 6 Komposition, 5, 6, 8 Konfiguration, 8, 27, 45, 55, 57, 70 Konstanz, 9 Group, 41, 70, 72 Gruppe, 18, 28–30, 35–37, 41, 42, 46, 48, 50, 51, 58, 59, 61, 62, 69– 72 Lampson-Matrix, siehe Zugriffsmatrix Liberty Alliance, 16 Linearisierung, 59–62, 64, 67 Login Module, 27, 28, 38, 56 Importeur, 8, 32, 37, 44 in (QSL), 13 Informationssicherheit, 1, 2, 15 Instrumentierung, 42, 43, 45, 53, 54, 65, 70, 76, 84 interface (QSL), 12 Invariante, 9, 10, 12, 13, 48 invariants (QSL), 13 IT-Sicherheit, 1 MAC, 20, 70 Mandatory Access Control, siehe MAC maybenull (QSL), 12, 13 Microsoft Passport, 16 MRO, 61, 70 Eiffel, 10 Erlaubnisprinzip, 19 error (QSL), 12, 14, 62 exists (QSL), 13 Exporteur, 8, 9, 32 extends (QSL), 12 J2SE, 25, 70 100 Nachbedingung, 8–10, 12, 13 NIST, 21, 70 not (QSL), 13 NRR, 10, 70 NRR (QSL), 12 Index OCL, 10, 70 Operation, 29, 30, 32–34, 37, 44–46, 48, 51, 53, 58, 59, 62, 64, 65, 67, 69, 70, 72 Operation, 32, 51, 70, 72 or (QSL), 13 PAM, 27, 56 Permission, 30, 32, 41, 70, 72 PermissionGroup, 30, 41, 70, 72 Pluggable Authentication Modules, siehe PAM post (QSL), 12–14 pre (QSL), 12, 13 Principal, 27, 28, 56, 72 Programmschnittstelle, 7 Python, 61, 70 QSL, 11–14, 41, 42, 44, 62, 70, 73 Qualitätssoftwarearchitektur, siehe Quasar Quasar, 3, 5–7, 9, 32, 36, 39, 70 Quasar Specification Language, siehe QSL QuasarAuthorization, 1–3, 5, 7, 19, 29–39, 41, 44–46, 49, 51, 53, 64, 65, 67, 72 RBAC, 20–22, 37, 42, 49, 71 Rechteprüfung, 23, 24, 29, 32–34, 36– 38, 41, 44–46, 48, 49, 51, 53, 58, 59, 62, 64, 65, 67, 69, 71 Resource, 41, 51, 71, 72 Ressource, 29, 30, 32, 33, 37, 41, 45, 46, 48, 51–53, 58, 59, 62, 64, 65, 67, 69, 71, 72 RestrictionParameter, 32, 71, 72 result (QSL), 14 Right, 41, 71, 72 Role, 41, 71, 72 Role-Based Access Control, siehe RBAC Rolle, 21, 29, 30, 35–37, 41, 42, 46, 48, 50, 51, 59, 64, 69–72 RRR, 10, 12, 71 Safety, 1 Sandbox, 25, 26 Schnittstelle, 2, 3, 5–13, 23, 30–39, 41, 42, 44, 46, 52–54, 56, 58–60, 62, 67, 69, 70, 72, 73, 75, 81 angeforderte, 32, 34, 35, 45, 52, 53, 69 Benutzer-, 7 Programm-, 7 Standard-, 7 Security, 1 Security Manager, 25, 26, 28 Session, siehe Sitzung SessionTicket, 53, 54, 71 Single-Sign-On, 16, 37 Sitzung, 21, 22, 49, 53, 58, 67, 71 Softwarekategorie, 6, 7 Spezifikation, 2, 3, 5, 7–13, 38, 39, 42, 44, 48, 50, 54, 62, 67, 70, 73 Stützschnittstelle, siehe Schnittstelle, angeforderte Standardschnittstelle, 7 states (QSL), 12 testcases (QSL), 13 Testfall, 9, 12–14 UML, 10, 30, 34, 50, 70, 71 URR, 9, 71 URR (QSL), 12 User, 30, 34, 35, 44, 54, 71, 72 UserGroup, 30, 34, 35, 41, 71, 72 uses (QSL), 12 variables (QSL), 12 101 Index VDM, 10, 71 Verbotsprinzip, 19 Vorbedingung, 8–10, 12, 13 Wiederholbarkeit, 9, 12 Z, 71 Zugriffs -ausweis, 17, 38 -kontrolle, 1, 3, 15, 16, 20, 22, 69– 71 -kontrollliste, 17 -matrix, 17 Zustandsmodell, 9 102