Weblog
28. August 2007: RFC-Umwege
Um die Optionen einen RFC-Verbindung auszuwerten gibt es einen schönen Funktionsbaustein namens RFCDES2RFCDISPLAY. Dieser löst den Connect-String aus Tabelle RFCDES auf und bietet einem die Informationen in Struktur RFCDISPLAY zur weiteren Verarbeitung an. Leider musste ich jedoch feststellen, dass sowohl die Load-Balancing Informationen der RFC-Verbindung selbst, als auch die der dazugehörigen A-RFC Einstellungen nicht korrekt übergeben werden.
Da ich weder im SDN noch in den sonstigen weiten des Internets fündig geworden bin, möchte ich auf diese Weise meinen Work-Around mit dem Rest der Welt teilen:
Form get_rfcoptions using value(dest) type RFCDES-RFCDEST
changing options type RFCDISPLAY.
* Temporary variables
Data: rfcdest like rfcdes,
Begin of it_rfcopt occurs 32,
token like rfcdes-rfcoptions,
End of it_rfcopt.
* Get RFC-Destination
Select single * from RFCDES into rfcdest where RFCDEST = dest.
* Get Details
Call function 'RFCDES2RFCDISPLAY'
exporting import_rfcdes = rfcdest
importing export_rfcdisplay = options.
* Check Load-Balancing (not properly checked in Function-Module)
Split rfcdest-rfcoptions at ',' into table it_rfcopt.
Loop at it_rfcopt where token = 'X=LB=ON'.
options-rfclbflag = true.
EndLoop.
* Check A-RFC Options (not checked in Function-Module)
Select * from RFCCHECK where rfcdest = dest.
If RFCCHECK-ARFCACTIVE = true.
options-rfcarfcopt = true.
EndIf.
EndSelect.
EndForm.
27. März 2007: Trackback?
Nachdem dieses verschlafene Blog es geschafft hat die Aufmerksamkeit eines Spammers auf sich zu ziehen, habe ich die Funktion nunmehr abgeschafft. Schade drum, aber ~1750 Spam-Trackbacks in drei bis vier Tagen waren mir einfach zuviel.
6. Dezember 2006: Passwörter-Raten
Als Administrator weist man ab und an seine Anwender darauf hin komplexere Passwörter zu nutzen, die aus einer zufälligen Reihenfolge von Buchstaben und Zahlen bestehen. Innerhalb eines SAP-System pflegt man denn auch einige Passwortregeln in Bezug auf deren Länge oder einiger Wörter und Wortteile, die nicht in Passwörtern vorkommen dürfen. Und man selbst geht natürlich mit leuchtendem Beispiel voran und verwendet ausschließlich kryptische Passwörter.
Dumm nur, wenn man dann die Passwortliste für technische Hintergrund-User verliert. Da diese User meist für die Anbindung anderer Systeme genutzt werden, kann man auch deren Passwort nicht einfach wieder zurücksetzen. Grund genug also, sich einmal nähergehend mit der Passwortverschlüsselung von SAP zu beschäftigen.
Zunächst einmal das Wichtigste: Passwörter werden innerhalb eines SAP-Systems mit einem nicht weiter bekannten Algorhytmus im Kernel selbst verschlüsselt. Dabei wird der Username mit in die Berechnung einbezogen, wodurch ein anderer User mit demselben Passwort einen anderen Hash-String bekäme. Der so verschlüsselte Hash-String wird anschließend in Tabelle USR02 zu einem User abgelegt.
Wie die Passwörter genau verschlüsselt werden ist nicht bekannt. Allerdings können wir uns zunutze machen, dass die SID eines Systems nicht mit in die Berechnung eingeschlossen wird. Dadurch können wir auf einem anderen System, auf dem der User nicht existiert, einen Report starten, der die möglichen Passwort-Kombinationen durchtestest. Der Report benutzt einen Funktionsbaustein, der einen User mit einem gegebenen Namen und Passwort anlegt. Nach der Anlage kann nun der Hash-Wert ausgelesen und mit einem anderen Wert verglichen werden. Ist der Hash-Wert derselbe, hat man das Passwort gefunden:
parameters: user like usr02-bname,
pw_hash like usr02-bcode.
data: charset type table of c with default key initial size 26,
char1 like line of charset,
char2 like line of charset,
char3 like line of charset,
char4 like line of charset,
char5 like line of charset,
char6 like line of charset,
char7 like line of charset,
char8 like line of charset,
pass(8),
logondata type USLOGOND.
CALL FUNCTION 'SUSR_USER_CREATE'
EXPORTING
USER_NAME = user
USER_LOGONDATA = logondata
PASSWORD = ''
EXCEPTIONS
USER_NAME_ALREADY_EXISTS = 1
USER_LOCKED_BY_ANOTHER_ADMIN = 2
PASSWORD_NOT_ALLOWED = 3
ERROR_IN_LOCK_MANAGEMENT = 4
ERROR_WRITING_TO_DB = 5
INTERNAL_ERROR = 6
OTHERS = 7.
case SY-SUBRC.
when 0.
break 1.
when 1.
MESSAGE e000(fb) with 'User existiert bereits!'.
when others.
MESSAGE e000(fb) with 'User konnte nicht angelegt werden.'.
endcase.
append 'A' to charset.
append 'B' to charset.
append 'C' to charset.
append 'D' to charset.
append 'E' to charset.
append 'F' to charset.
append 'G' to charset.
append 'H' to charset.
append 'I' to charset.
append 'J' to charset.
append 'K' to charset.
append 'L' to charset.
append 'M' to charset.
append 'N' to charset.
append 'O' to charset.
append 'P' to charset.
append 'Q' to charset.
append 'R' to charset.
append 'S' to charset.
append 'T' to charset.
append 'U' to charset.
append 'V' to charset.
append 'W' to charset.
append 'X' to charset.
append 'Y' to charset.
append 'Z' to charset.
loop at charset into char1.
loop at charset into char2.
loop at charset into char3.
loop at charset into char4.
loop at charset into char5.
loop at charset into char6.
loop at charset into char7.
loop at charset into char8.
concatenate char1 char2 char3 char4
char5 char6 char7 char8 into pass.
CALL FUNCTION 'SUSR_USER_CHANGE'
EXPORTING
USER_NAME = user
PASSWORD = pass
EXCEPTIONS
USER_NAME_NOT_EXISTS = 1
PASSWORD_NOT_ALLOWED = 2
INTERNAL_ERROR = 3
OTHERS = 4.
IF SY-SUBRC <> 0.
exit.
ENDIF.
CALL FUNCTION 'SUSR_USER_LOGONDATA_GET'
EXPORTING
USER_NAME = user
IMPORTING
USER_LOGONDATA = logondata.
if pw_hash EQ logondata-BCODE.
write pass.
break 9.
endif.
endloop.
endloop.
endloop.
endloop.
endloop.
endloop.
endloop.
endloop.
data ret_table type table of BAPIRET2.
CALL FUNCTION 'BAPI_USER_DELETE'
EXPORTING
USERNAME = user
TABLES
RETURN = ret_table.
Die Laufzeiten bei diesem Verfahren sind jedoch immens; auch wenn der Report selbst, der hier nur als Proof-of-Concept verstanden werden will, durchaus noch einiges Optimierungspotential besitzt. Die Laufzeit steigt jedoch expotentiell zur Anzahl der verwendeten Zeichen. Die möglichen Kombinationen lassen sich einfach berechnen:
<Anzahl Zeichen>^<Passwortlänge>
In einem Test habe ich ein bekanntes Passwort bei einer Passwortlänge von acht Zeichen und sieben verschiedenen Zeichen in fünf bis zehn Minuten finden können.
7. November 2006: Serialisierte Instanzen
Abwärtskompatibilität ist eine tolle Sache. Sie verspricht, dass man die Entwicklungen auf der momentanen Version auch in Zukunft noch nutzen kann und dadurch nicht laufend mit der Wartung seiner alten Programme beschäftigt ist.
Was ich jedoch gestern erfahren musste, hat immer noch einen faden Beigeschmack. Es ging darum, dass eine Webseite mit komplexen adhoc-Berechnungen die zugrunde liegenden Instanzen eines Objektes in einer Datenbank-Tabelle zwischenspeichern sollte. Nichts leichter als das:
$so = serialize($o);
Das Zurückholen in den Applikationskontext zu einem späteren Zeitpunkt ging ebenso leicht:
$o = unserialize($so);
In meiner Test-Umgebung (PHP 4.3.3) hat das alles wunderbar funktioniert. Toll, dachte ich, dass das so einfach geht. Doch zu früh gefreut. In der Produktiv-Umgebung (PHP 4.4.4) fehlten den so wieder ins Leben zurück gerufenen Instanzen auf einmal die Methoden, was neben unschönen Fehlermeldungen auch zu einem Ausfall der Anwendung geführt hat. Laut PHP-Dokumentation soll man, um genau dies zu vermeiden, eine kleine Korrektur vornehmen:
$so = unserialize(implode("", $so));
Leider führte auch das nicht zum gewünschten Ergebnis. Blieb also nur der Biss in den sauren Apfel und ein entsprechend ausschweifender Work-Around (die Daten werden jetzt in einer eigenen Cache-Tabelle zwischengespeichert).

Kommentare