Benutzer Datenverwaltung auf Basis von OpenLDAP 2.4

Seit dem Wochenende tüftle ich an der „Users DB“. Zwischenzeitlich habe ich mich dazu entschlossen hierfür einen LDAP Server einzurichten, denn die meißten Zugriffe sind lesender Natur. Des weiteren kommt mir die die objektorientierte Ablage der Daten entgegen. Was noch dafür spricht, ich kannte mich mit LDAP vor einiger Zeit noch recht gut damit aus und da hier das ein oder andere hängen blieb geht es sicherlich recht schnell. 🙂

LDAP Schema Definitionen

Leider gibt es kein LDAP Schema für RFID Tags (im speziellen die verwendete DESFire EV1), daher habe ich nun selber eines entwickelt was die bisher meißte Zeit in anspruch nahm. Zum glück hatte ich vor einigen Jahren eine eigene OID bei IANA beantragt (siehe: Cybcon Industries) und habe nun für die Michael Oberdorf IT-Consulting (unter diesem Label wird das System  entwickelt) eine eigene unter Nummer definiert.

Zunächst habe ich das RFID DESFire Schema und das Schema für das System zusammen definiert, aber da das DESFire Schema sehr umfangreich wurde, habe ich dieses nun getrennt. Die verwendeten OID Räume sind wie folgt definiert:

1.3.6.1.4.1     15432     3     -     Namensraum von Michael Oberdorf IT-Consulting
1.3.6.1.4.1     15432     3     1     OITC RFID Tag representation (DESFire EV1 specific):
                                      Attribute: 1.3.6.1.4.1.15432.3.1.1.n
                                      Objektklasse: 1.3.6.1.4.1.15432.3.1.2.n
1.3.6.1.4.1     15432     3     2     OITC Access Control System:
                                      Attribute: 1.3.6.1.4.1.15432.3.2.1.n
                                      Objektklasse: 1.3.6.1.4.1.15432.3.2.2.n

Der aktuelle Stand ist hier zu finden:

OpenLDAP Installation und Konfiguration

Hier war ich faul und habe die Debian Luxus Methode gewählt. Ist ja auch nicht ganz so zickig wie ein OpenBSD. 🙂

apt-get install ldap-client ldap-server ldap-utils libldap-2.4-2 libsasl2-modules libsasl2-modules-otp libsasl2-modules-ldap libsasl2-modules-gssapi-heimdal

Danach findet sich ein standard OpenLDAP auf dem System:

  • Konfiguration: /etc/ldap
  • OpenLDAP Backend Module: /usr/lib/ldap
  • OpenLDAP Datenbank: /var/lib/ldap
  • OpenLDAP schreibt sein Log im Standard via LOCAL4 ins Syslog: /var/log/syslog

Vorteil dieser Methode:

  • Das configuration Backend (cn=config) wird bereits angelegt
  • Das Root Objekt existiert
  • Man kann sich nach der Installation sofort auf die Datenbank verbinden

Nachteil dieser Methode:

  • Es fehlt die Datei „/etc/ldap/slapd.conf“
  • der Zugriff auf „cn=config“ ist nicht möglich da die Berechtigung fehlt (geht nur über einen schmutzigen Workaround)
  • Schema- und Konfigurationsänderungen sind recht mühselig (das ist wohl aber ein persönliches Empfinden)

Daher bin ich nun so vorgegangen dass ich das Konfigurations Backen auf Basis der aktuell eingestellten Werte in eine Standard slapd.conf Datei übertragen habe. Diese Datei wurde dann um das neu erstellte Schema erweitert. In dieser Datei sind zudem eine vernünftige Berechtigungsstruktur hinzugekommen, die notwendige Indizierung der Attribute (hier war im Standard tatsächlich nur die objectClass indiziert), das montoring Backend wurde konfiguration sowie die ein oder anderen Tuningparameter für die DBs hinterlegt.
Um das Ganze abzurunden, habe ich auch für die Sleepycat DB ein wenig die Konfiguration optimiert. Diese DB_CONFIG Datei muss im übrigen im Datenbankverzeichnis abgelegt werden. darin habe ich unter anderem das Transaktions Logging aktiviert.

Hier die OpenLDAP Konfiguration:

Die Daten für die Datenbank kann man im LDIF format vorbereiten.
Die Datenbank wird dann wie folgt initialisiert:

/etc/init.d/slapd stop
slapadd -f /etc/ldap/slapd.conf -l /etc/ldap/dit.ldif
chown -R openldap:openldap /var/lib/ldap/slapd-001

Wer möchte, kann dann das Konfigurationsbackend wieder wie folgt anlegen:

rm -rf /etc/ldap/slapd.d/*
slaptest -f /etc/ldap/slapd.conf -F /etc/ldap/slapd.d
chown -R openldap:openldap /etc/ldap/slapd.d

ich hab mir das gespart.
Am Ende den Server dann wieder starten:

/etc/init.d/slapd start

Nun muss sich noch herausstellen ob die Verbindung (vorallem der Verbindungsaufbau) schnell genug ist für die Benutzervalidierung. Aber hierzu muss ich das ACS noch um die LDAP Funktionalität erweitern. eine passende Java Lib habe ich mir jedenfalls noch nicht herausgesucht.

 

Initialisierung von Karten (2nd try)

Im Moment bin ich an der Erstellung des „Card Management Systems“.
Das Thema mit der DESFire Kommunikation hat mich nun wieder eingeholt. Daher habe ich wieder angefangen nach den DESfire spezifischen APDUs zu suchen.

Um an die NXP Dokumentation „MIFARE DESFire – Implementation hints and examples,document number: 094532″ heranzukommen, habe ich nun auch einen Account im NXP Docstore beantragt. Mal sehen ob die sich melden, denn zu meinem eröffneten Ticket bei NXP.xom gab es in den letzten 3 Monaten keine Reaktion (selbst nach Rückfrage).

Den logischen Ablauf stelle ich mir wie folgt vor:

  1. RFID tag formatieren
  2. PICC Applikation formatieren
  3. Keys ablegen
  4. Kommunikation mit RFID tag verschlüsseln
  5. PICC Applikation konfigurieren (z.B. Random UID)
  6. Applikation anlegen und Konfigurieren
  7. Kommunikation Applikation mit Keys sichern
  8. File anlegen
  9. Kommunikation File mit Keys sichern
  10. Initiale Daten in File schreiben

Kaufsoftware oder selber machen

Die Frage stellt sich mir auch wieder. Es gibt einiges an Kaufsoftware mit der man die Karten konfigurieren kann. [1][2] Allerdings sind diese relativ teuer. Der ChipMan kostet (mit DESFire Funktionalität) immerhin 798 Euro (Stand November 2014). Die Vollversion sogar 1598 Euro. Nichts desto trotz frage ich derzeit auch die Preise für BadgeMaker an.

Parallel hierzu versuche ich natürlich mit der Feig SDK weiter zu kommen. Leider ist das SDK nicht wirklich gut Dokumentiert. Hauptinformationsquelle ist das Tutorial Dokument das allerdings nicht vollständig ist.
Am Montag habe ich den tagHandler für MIFARE DESfire entdeckt, mal sehen ob ich hier weiter komme. Ich habe gemerkt das mit dem Protokoll wissen (APDUs) das Feig SDK etwas besser zu durchschauen ist. Daher auch der erneute Versuch an die MIFARE Doku zu kommen. Meine Internet-Recherche hat allerdings wieder ein paar interessante Quellen aufgetan (siehe [3] bis [6]).
Wenn ich nicht weiterkomme muss ich wohl wieder mal den technischen Support der Feig bemühen mir zu helfen.

Links:

[1] MP-Sys – ChipMan: http://www.mpsys.de/
[2] ScreenCheck BV – BadgeMaker (mit Encode add-on) http://en.badgemaker.info/
[3] Ridrix’s Blog – APDU commands: https://ridrix.wordpress.com/tag/mifare-desfire/
[4] NXP – MIFARE DESFire as Type 4 Tag: http://www.nxp.com/documents/application_note/AN11004.pdf
[5] Jérémie Laval’s page – A Philips Datasheet: http://neteril.org/files/M075031_desfire.pdf
[6] SmartCard Networking Forum – DESFIRE Specification: http://www.scnf.org.uk/smartstore/LASSeO%20docs/DESFIRE%20Specification%20V1%200.pdf

MIFARE DESFire EV1 via pcsc-perl

Der RFID Chip machte und macht mir noch immer einige Sorgen.

Die Kommunikation erfolgt via sogenannter APDU Kommandos. Das sind 8 byte HEX Strings. [2]
Es gibt wohl nach ISO Standard irgendwelche standard Befehle und es gibt zusätzlich einen DESFire EV1 proprietären Befehlssatz.

Die ISO Befehle sind leider nur rudimentär beschrieben. [2] Jedenfalls habe ich nichts adequates gefunden. Schlimmer noch, die DESFire EV1 Befehle sind gar nicht öffentlich zu bekommen.

Es soll eine Dokumentation von NXP geben die sich „MIFARE DESFire – Implementation hints and examples,document number: 094532″ nennt. In diversen Foren ist davon die Rede dass man die nur nach Unterzeichnung einer NDA bekommt.
Um irgendwie weiter zu kommen habe ich nun einen Account bei NXP.com erstellt und am 1.6. eine Anfrage beim Support erstellt (diese ist Stand heute noch nicht beantwortet worden). Wir harren also der Dinge.

Nichts desto trotz ist es mir nach einiger Recherche gelungen die Basisdaten auszulesen aus einer nicht Verschlüsselten blanko RFID Karte. Also welche Grundlegende Konfiguration diese hat, die UID und ein paar Infos zum Betriebssystem und der Hardware. Hier ein Beispiel (der Quellcode ist NICHT vollständig, es sind nur ein paar Ausschnitte des gesammten Programms):

[..]
# PC/SC Handling
use Chipcard::PCSC;                # to connect to card reader
use Chipcard::PCSC::Card;          # to read smart card
[..]
#---------------------------------------------------------------------#
# sendAPDU
# description: send APDU command and receive data
# input: object (hCard), string (APDU comand)
# return: string (APDU response)
#---------------------------------------------------------------------#
sub sendAPDU
  {
  my $card = shift;
  my $APDU_in = shift;

  my $SendData = Chipcard::PCSC::ascii_to_array($APDU_in);
  my $RecvData = $card->Transmit($SendData);
  if ($RecvData)
    {
    my $APDU_out = Chipcard::PCSC::array_to_ascii($RecvData);
    return ($APDU_out);
    }
  else
    {
    return(undef);
    }
  }
#---------------------------------------------------------------------#
# sendAPDUwithSW
# description: send APDU command and receive data and status word
# input: object (hCard), string (APDU comand)
# return: string (APDU response), string (APDU status word)
#---------------------------------------------------------------------#
sub sendAPDUwithSW
  {
  my $card = shift;
  my $APDU_in = shift;

  my $SendData = Chipcard::PCSC::ascii_to_array($APDU_in);
  my $RecvData = $card->Transmit($SendData);
  my $sw_r = pop(@{$RecvData});
  my $sw_l = pop(@{$RecvData});
  my $sw = [$sw_l, $sw_r];

  my $APDU_out = Chipcard::PCSC::array_to_ascii($RecvData);
  my $SW = Chipcard::PCSC::array_to_ascii($sw);

  return ($APDU_out, $SW);
  }
#---------------------------------------------------------------------#
# getPICCData
# description: get the Cards manufacturing data
# input: object (hCard)
# return: hash table reference
#   hardware_vendor_id  => Vendor ID in hex
#   hardware_type       => Hardware Type
#   hardware_subtype    => Hardware Subtype
#   hardware_version    => Hardware Version Number
#   hardware_storage    => Storage size in bytes
#   hardware_protocol   => Hardware protocol
#   software_vendor_id  => Vendor ID in hex
#   software_type       => Software Type
#   software_subtype    => Software Subtype
#   software_version    => Software Version Number
#   software_storage    => Storage size in bytes
#   software_protocol   => Storage protocol
#   batch_number        => Batch Number
#   calendar_week       => Calendar week of production
#   production_year     => Year of production
#---------------------------------------------------------------------#
sub getPICCData
  {
  my $card = shift;

  my $response='';
  my $PICC = {};

  # get Hardware data
  $response = sendAPDU($card, '60');
  if (!$response) { return(undef); }
  # AF 04 01 01 01 00 1A 05
  $PICC->{'hardware_vendor_id'} = substr($response, 3, 2);
  $PICC->{'hardware_type'} = substr($response, 6, 2);
  $PICC->{'hardware_subtype'} = substr($response, 9, 2);
  my $HWVerMaj = substr($response, 12, 2);
  my $HWVerMin = substr($response, 15, 2);
  $PICC->{'hardware_version'} = hex($HWVerMaj).'.'.hex($HWVerMin);
  my $HWStorage = substr($response, 18, 2);
  #$PICC->{'hardware_storage'} = hex($HWStorage);
  $PICC->{'hardware_storage'} = $HWStorage;
  $PICC->{'hardware_protocol'} = substr($response, 21, 2);

  # get Software data
  $response = sendAPDU($card, 'AF');
  if (!$response) { return(undef); }
  $PICC->{'software_vendor_id'} = substr($response, 3, 2);
  $PICC->{'software_type'} = substr($response, 6, 2);
  $PICC->{'software_subtype'} = substr($response, 9, 2);
  my $SWVerMaj = substr($response, 12, 2);
  my $SWVerMin = substr($response, 15, 2);
  $PICC->{'software_version'} = hex($SWVerMaj).'.'.hex($SWVerMin);
  my $SWStorage = substr($response, 18, 2);
  #$PICC->{'software_storage'} = hex($SWStorage);
  $PICC->{'software_storage'} = $SWStorage;
  $PICC->{'software_protocol'} = substr($response, 21, 2);

  # get production data
  $response = sendAPDU($card, 'AF');
  if (!$response) { return(undef); }
  $PICC->{'batch_number'} = substr($response, 24, 14);
  my $ProdCW = substr($response, 39, 2);
  $PICC->{'calendar_week'} = $ProdCW;
  my $ProdYR = substr($response, 42, 2);
  $PICC->{'production_year'} = $ProdYR + 2000;

  return($PICC);
  }
[..]

my $hContext = new Chipcard::PCSC($Chipcard::PCSC::SCARD_SCOPE_SYSTEM, 0);
my @ReadersList = $hContext->ListReaders ();
my $ReadersName = $ReadersList[0];
$readers_states[0]={ 'reader_name' => $ReadersName };
# ignore first event
my @StatusResult = $hContext->GetStatusChange(\@readers_states);
foreach my $reader (@readers_states) { $reader->{'current_state'} = $reader->{'event_state'}; }

[..]

# Initialize  Card Object to read data from card
my $hCard = new Chipcard::PCSC::Card($hContext,$ReadersName,$Chipcard::PCSC::SCARD_SHARE_EXCLUSIVE,$Chipcard::PCSC::SCARD_PROTOCOL_T0);

# get UID from Card
my ($CARD_UID, $sw) = sendAPDUwithSW($hCard, 'FF CA 00 00 00');
print DATETIMESTRING().'   APDU state: '.Chipcard::PCSC::Card::ISO7816Error($sw).' ('.$sw.')'."\n";
print DATETIMESTRING().'   Card UID: '.$CARD_UID."\n";
[..]
my $piccData = getPICCData($hCard);
[..]
my $piccApp = getPICCApp($hCard);
[..]
$hCard->Disconnect($Chipcard::PCSC::SCARD_EJECT_CARD);
$hCard = undef;

Der nächste Schritt ist, eine Authentisierung hinzubekommen. Zum Verfahren findet man einige Beiträge im Netz – allerdings nie so richtig vollständig. [3a][3b][3c][4][5a][5b][6]

Die Desfire unterstützt als Verschlüsselungs Algorithmus DES, 3-DES und AES. Leider konnte ich keines der Beispiele im Netz mit Hilfe der Per Cipher Engins nachprogrammieren. Es scheint hier zu generellen Implementationsproblemen zu kommen. Egal was ich anwende CBC / noPadding via DES, 3-DES oder AES, nichts scheint die selben Ergebnisse zu reproduzieren wie in der Beispielen im Internet.

Ja, ich konnte das Licht im Büro ein und Ausschalten (hat meinen 5 Jährigen schwer beeindruckt) aber es ist mir dann doch zu unsicher nur mit der UID einer Karte etwas freizuschalten. Daher habe ich das Thema mit Perl zu Seite gelegt und angefangen Java zu lernen.

Ein paar Tage später konnte ich immerhin schon 3 der 5 auffindbaren Beispiele reproduzieren.

Die Idee aus Perl heraus ein Java zu triggern hatte ich tatsächlich kurz gehabt aber dann doch wieder verworfen. Wie schon gesagt, ich habe auch null Info von NXP.com bis heute.

Mahl ehrlich, das ist wie wen jemanden einen USB Stick verkauft der nicht vom Betriebsystem erkannt wird und man sagt dem Käufer auch nicht wie er ihn zum laufen bekommt, der der Treiber ist geheim. Meines Erachtens ziemlicher Schwachsinn.

Links:

[1] WikiPedia: Application Protocol Data Unit (APDU)
[2] CardWerk: ISO 7816 Smart Card Standard
[3a] http://stackoverflow.com/questions/14319321/how-can-i-do-native-authentication-in-desfire-ev1
[3b] http://stackoverflow.com/questions/14117025/des-send-and-receive-modes-for-desfire-authentication
[3c] https://n3vrax.wordpress.com/2011/07/23/des-algorithm-java-implementation/
[4] https://ridrix.wordpress.com/2009/09/19/mifare-desfire-communication-example/#comment-87
[5a] http://database.developer-works.com/article/15751843/Mifare+DESfire+card+Authentication!!!!!
[5b] https://community.oracle.com/thread/1751843
[6] http://stackoverflow.com/questions/21257442/mifare-desfire-ev1-authentication-using-aes