HowTo: 3rdparty Apps Anwendungsberechtigung und SynoToken

QTip

Super-Moderator
Teammitglied
Mitglied seit
04. Sep 2008
Beiträge
2.341
Punkte für Reaktionen
6
Punkte
84
Vorgeschichte
Wieder einmal packte mich mein Forscherdrang um die letzten Geheimnisse des DSM ans Licht zu bringen. :D Bedingt durch eines meiner aktuellen Projekte (Notification messanger), in diesem ich eine eigene Benutzerberechtrigung integrierte, begab ich mich auf die Suche, unter welchen Kriterien bzw. welcher Konfiguration ein App unter den Anwendungsberechtigungen erscheint.
Da nun auch die von Synology separat angebotenen Applikationen teilweise in dem Dialog der Anwendungsberechtigungen zu sehen waren, untersuchte ich diese ein wenig näher.

Mit dem Eintrag "grantPrivilege" in der config des App's wird die Grundlage geschaffen, damit das App in der Liste auswählbar ist. Der DSM durchsucht nämlich alle App-Verzeichnisse nach den config Dateien. Wird der EIntrag "grantPrivilege" gefunden, stellt die Systemsteuerung das entsprechende App in der Liste dar. Erste Versuche zeigten den erhofften Erfolg, allerdings war da noch eine Kleinigkeit, für die ich eine Lösung finden musste.

Synology hat diese Funktion nicht für App's vorgesehen, die per type "legacy" oder "url" gestartet werden. Denn eine App mit einer dieser beiden type's, welche in einem neuen Fenster aufgerufen wird, wird immer per URL aufgerufen. Und dort wird einfach keine Prüfung auf Berechtigung des Benutzers durchgeführt. Jeder angemeldete Benutzer der die URL kennt, kann das App direkt öffnen. Ich schreibe mit Absicht "angemeldete Benutzer", da trotz Allem noch eine Prüfung diesbezüglich durchgeführt wird.

Nach ein wenig Suche bin ich in der Datei initdata.cgi fündig geworden. Die initdata.cgi gibt die Information über die Berechtigung des Benutzer aus, man muss sie nur noch extrahieren und auswerten.

Damit das Ganze auch funktioniert, muss man unbedingt die Variante mit der config benutzen. Die application.cfg ist veraltet und nur noch aus Kompatibilitätsgründen vorhanden. Über die Vorteile der config habe ich schon im Wiki geschrieben - Aufbau der Datei 'config'

Default ist nur der "admin" berechtigt, andere Benutzer (auch Administratoren) müssen erst explizit zugewiesen werden.

Als Wert für den Eintrag "grantPrivilege" stehen die folgenden 4 zur Verfügung, meist wird "local" benutzt.
localnur für lokale Benutzer kann diese App ausgewählt werden
domainnur für domain Benutzer kann diese App ausgewählt werden
ldapnur für LDAP Benutzer kann diese App ausgewählt werden
allfür alle Benutzer kann diese App ausgewählt werden


Von der Theorie nun zur Praxis...

Ich habe 2(3) Skripte erarbeitet, die diese Aufgabe erledigen sollen, in Perl / Javascript / PHP. (siehe Anhänge)

Perl
Einfach den folgenden Code möglichst am Anfang der normalerweise aufzurufenden Datei einfügen und die Datei aus dem Anhang in ein Verzeichnis packen (Pfade anpassen nicht vergessen). Wichtig ist, das ihr den internen Anwendungsnamen wisst. Da eh die config benutzt werden muss, steht oben der komplette Name. Er fängt für gewöhnlich mit SYNO.SDS._ThirdParty..App. <Text> an, hinter dem Punkt folgt dann der frei wählbare Text. Er kann natürlich auch anders lauten und muss nicht mit SYNO.SDS._ThirdParty.App. beginnen, einfach mal nachsehen. Dieser komplette Name muss in der aufgerufenen Funktion übergeben werden.
Rich (BBCode):
require "include/check_appprivilege.pl";
my ($synotoken,$synouser,$is_admin) = check_privilege('<Name_der_Anwendung>');

Javascript
Die Javascript Variante eignet sich für HTML- und PHP-Skripte. Die Prüfung ob der Benutzer angemeldet ist muss zuvor stattfinden, da dies nicht im Javascript-Code durchgeführt wird.
Rich (BBCode):
var check = check_appprivilege('<Name_der_Anwendung>')
Für eine reine Javascript-Anwendung kann auch das im "3rd-Party Apps Developer Guide" von Synology auf Seite 87 erwähnte dsmtoken.cgi benutzt werden.

PHP
Es existiert nun auch ein Stück Code für PHP (Dank an mrsandman), das Script kommt nun ohne Javascript-Teil aus.
PHP:
require('check_appprivilege.php');
list($synotoken, $synouser, $is_admin) = check_privilege('<Name_der_Anwendung>');
Was machen die Skripte
Zuerst wird überprüft, ob ein SynoToken existiert und bei Bedarf der Url angehangen. Danach wird die initdata.cgi bzw. ab DSM 6.0 die synowebapi aufgerufen und ausgewertet. Im Anschluss folgt die Suche nach der Applikation im Abschnitt "AppPrivilege". Die 3 gesammelten Werte werden dann als Rückgabewerte der Funktion ausgegeben.

Update 20.07.2013
Für den DSM 4.3 beta habe ich das Perl-Skript um das SynoToken erweitert. Das Skript ist nun eine Funktion und man erhält als Rückgabe den SynoToken, den Usernamen und ob dieser der Administratoren Gruppe angehört.
Growler, Notification forwarder und iPKGui sind bereits mit der neuen Variante ausgestattet.
Die Javascript Variante ist nun ebenfalls eine Funktion, allerdings kann sie bis jetzt nur bei deaktiviertem Sicherheitsfeature benutzt werden. Als Rückgabe erhält man ein Array mit dem Usernamen und ob dieser der Administratoren Gruppe angehört.

Update 26.01.2014
In DSM 5 existiert die Datei in /tmp nicht mehr, deshalb musste ein anderer Weg her, um an das SynoToken zu kommen. Wie schon im letzten "3rd-Party Apps Developer Guide" von Synology erwähnt, liefert login.cgi die notwendige Information, wenn ein Benutzer im DSM angemeldet ist (Dank an mrsandman für den Schubs in die richtige Richtung).

Update 01.09.2015
Scripte wurde geringfügig überarbeitet, kleinere Fehler behoben.

Update 07.03.2016
Anpassungen für DSM 6.0 rc
Variante in plain PHP und Perl, Javascript Variante vorerst entfernt

Update 18.09.2016
Auf Anregung von Tosoboso wird nun vor Durchführung in beiden Scripten ein Backup der Environmentvariable "QUERY_STRING" angelegt und am Ende wiederhergestellt.

Wie immer ist Alles was ihr auf eurer DS anstellt euer Ding, ich übernehme keine Haftung.
 

Anhänge

  • check_appprivilege51.zip
    2,4 KB · Aufrufe: 39
Zuletzt bearbeitet:

QTip

Super-Moderator
Teammitglied
Mitglied seit
04. Sep 2008
Beiträge
2.341
Punkte für Reaktionen
6
Punkte
84
Der nächste Schritt - SynoToken

Mit Erscheinen von DSM 4.3 beta hat sich Synology im Bereich 3rdparty Anwendungen mal wieder einen Fauxpas geleistet. 3rdparty Anwendungen, welche sich an das von Synology herausgegebene Dokument "Synology DiskStation Manager 3rd-Party Apps Developer Guide" im Abschnitt "Integrate with DSM Web Authentication" auf Seite 46 halten, können nun Dank einer neu eingebauten Sicherheitsoption nicht mehr gestartet werden. Man erhält nur die lapidare Meldung "Es tut uns Leid, die von Ihnen gesuchte Seite konnte nicht gefunden werden."

Was ist hier passiert
Eine neue Sicherheitsoption mit der Bezeichnung "Schutz gegen Cross-Site-Request-Forgery-Attacken verbessern" verhindert den Start von "böswilligen" Anwendungen, welche sich nicht mit einem entsprechendem "Token" identifizieren. An der Idee ist ja nichts auszusetzen und auch löblich, dass Synology sich Gedanken über die Sicherheit macht, nur bleiben bei der Implementation wieder einmal viele 3rdparty-Anwendungen der Community auf der Strecke. Das dies nicht das erste Mal ist, dürfte jedem einigermaßen aktiv lesenden Forumuser bekannt sein - ich erinnere nur an DSM 3.1, als sich 3rdparty-Anwendungen nur noch in einem extra Fenster öffnen liessen.

Meiner Meinung nach ist der Fehler aber der, dass diese Option schon wieder voreingestellt war und die Seiteneffekte nicht publiziert wurden.

Was kann man tun:
Da Synology bis heute nicht sein offizielles Dokument aktualisiert hat, bleiben nur folgende Lösungen:
  • die Option deaktivieren, bis Synology sein offizielles Dokument aktualisiert hat
  • keine Software benutzen, welche die interne DSM-Authentifizierung benutzen (betrifft hauptsächlich die Benutzer)
  • der Anleitung oben folgen und seine Anwendungen anpassen
Wie wirds gemacht:
Beim Login wird eine eindeutige ID erzeugt, welche unter anderem im Cookie vorzufinden ist. Diese ID wird zusammen mit einem Token (Synology nennt ihn SynoToken) intern gesichert.
Beispiel:
eqlGPcsFHJWnD=eqHF4ZIPZcgnA
Wir nun eine Anwendung aufgerufen, hängt der DSM zuvor den Token mit dem QuerySchlüssel SynoToken=<token> an die URL. Kann man sehen, indem man z.B. die Synology-Hilfe in einem neuen Fenster aufruft. Bei jedem weiteren Zugriff innerhalb der Anwendung zum DSM muss nun der Token mitgeliefert werden, dies wird meist per Header mit dem Namen "X-SynoToken" durchgeführt.
Der eigentliche Schutz besteht nun darin, dass jede Anfrage an den Server nun nur noch mit dem Token funktioniert und ansonsten abgewiesen wird. Dies betrifft ebenfalls die Abfrage auf Authentifizierung über authenticate.cgi; es muss in jedem Fall der SynoToken existieren.

Wird nun eine 3rdparty Anwendung der Community geöffnet, fehlt dieser SynoToken in der URL und die weitere Ausführung/der Start wird abgebrochen.
Ich hätte mir von Synology zumindest eine Art Trustcenter gewünscht, in diesem man selbst bestimmen kann, welche Anwendung gestartet werden darf bzw. Zugriff auf den DSM bekommt und welche nicht. Wenn man sich das allerdings wieder vor Augen führt ist dies ja im Grunde überflüssig, denn der Anwender selbst hat sich diese Anwendung installiert...ein Teufelskreis ;)

Für oder besser gegen vermeintliche Angreifer macht diese Funktion bestimmt Sinn, doch bitte verschont unsere 3rdparty-Anwendungen mit diesen Diskriminierungen.
 
Zuletzt bearbeitet:

Tommes

Benutzer
Mitglied seit
26. Okt 2009
Beiträge
7.924
Punkte für Reaktionen
144
Punkte
209
Hey QTip!

Ich habe grade versucht, dein HowTo in Ultimate Backup zu implementieren, was augenscheinlich zwar funktioniert hat, jedoch war es mir nicht möglich die Funktion check_privilege() zu inkludieren und auszuführen. Selbst als ich die Funktion direkt in meine index.php kopiert und aufgerufen hatte, wollte es nicht funktionieren. Daraufhin habe ich die Bereiche, die die Funktion zu einer Funktion machen mal entfernt...

Rich (BBCode):
function check_privilege($appname) {
..
.
return array($synotoken,$synouser,$is_admin);
}

... und die Variable $appname mit dem Anwendungsnamen gefüllt und siehe da, es funktioniert. Ich kann also weder die Funktion als solches noch das inkludieren der check_appprivilege.php erfolgreich umsetzten. Hast du dafür eine Erklärung?

Und noch eine Frage: Wenn ich in der Browser-Adressleiste den Pfad zu Ultimate Backup angebe (http://meineIP:5000/webman/3rdparty.... ) dann erhalte ich "nur" eine weiße Seite. Sollte hier nicht irgendeine Fehlermeldung erscheinen ( HTTP 1.1 404) ??

Ich bin mir jetzt unsicher, ob ich alles richtig durchgeführt habe, auch wenn es augenscheinlich funktioniert. Oder habe ich vielleicht irgendwas übersehen, vergessen oder verkackt?

Tommes
 

QTip

Super-Moderator
Teammitglied
Mitglied seit
04. Sep 2008
Beiträge
2.341
Punkte für Reaktionen
6
Punkte
84
Hallo Tommes,

Der Aufruf für die PHP Variante sieht folgendermaßen aus, ich vermute du hast nicht an die Auswertung der 3 Rückgabevariablen gedacht.
PHP:
require('check_appprivilege.php');
list($synotoken, $synouser, $is_admin) = check_privilege(<appname>);
if ($synouser == '') exit(); // Abfrage ob User bekannt
if (!$is_admin) exit(); // Abfrage ob User der Admingruppe angehört

Die obigen beiden If-Abfragen müssen nicht beide verwendet werden, wenn man nur auf einen validen User testen möchte kann die letzte Abfrage weggelassen werden.

Du solltest für POST und GET die Variable SynoToken anhängen, z.B. so
PHP:
my $token = ($synotoken ? "?SynoToken=".$synotoken : "");
und dann "deine-url....".$token;
Für POST kann man die Variable auch als 'X-SYNO-TOKEN' in das Environment hängen.
Möchte man die Einstellungen aus der Systemsteuerung - Berechtigungen verarbeiten, dann müssen in der 'config' Datei noch die Properties "advanceGrantPrivilege" : true, "configablePrivilege" : true und 'grantPrivilege' mindestens auf 'local' gesetzt werden (siehe auch http://www.synology-wiki.de/index.php/Aufbau_der_Datei_'config')

Da Script wertet nur aus und übergibt die Rückgabewerte, wie das Script im Anschluss reagiert ist dir überlassen. Ein simples 'exit' sollte eine Fehlermeldung "Es tut uns Leid, die von Ihnen gesuchte Seite konnte nicht gefunden werden" ausspucken.
 
Zuletzt bearbeitet:

Tommes

Benutzer
Mitglied seit
26. Okt 2009
Beiträge
7.924
Punkte für Reaktionen
144
Punkte
209
QTip, was soll ich sagen außer... DANKE!

Ohne deine Hilfe hätte ich das wohl nicht hinbekommen bzw. mir niemalsnie die passenden Zeilen für den Funktionsaufruf aus den Fingern saugen können. Ich stehe nämlich noch ziemlich weit unten auf der Treppe zum php-Olymp, um es mal so auszudrücken.

Die Implementierung des Syno-Tokens funktioniert jetzt. Mit den Anwendungsberechtigungen muß ich noch ein wenig experimentieren um bewerten zu können, was für Ultimate Backup am sinnvollsten erscheint.

Du solltest für POST und GET die Variable SynoToken anhängen...
Es geht aber auch ohne *g* Ich muß mal schauen, wo und wie ich das noch überlall unterbingen kann/muß/sollte!

Ein simples 'exit' sollte eine Fehlermeldung "Es tut uns Leid, die von Ihnen gesuchte Seite konnte nicht gefunden werden" ausspucken.
Hm, das tut es leider irgnedwie nicht, daher habe ich einfach exit() mit exit('Es tut uns Leid, die von Ihnen gesuchte Seite konnte nicht gefunden werden') gefüllt. Nicht schön, aber immerhin etwas.

Ich bastel noch ein wenig rum und schaue was ich damit noch alles so anstellen kann. Bis hierhin erstmal recht herzlichen Dank für deine Hilfe.

Tommes
 
Zuletzt bearbeitet:

QTip

Super-Moderator
Teammitglied
Mitglied seit
04. Sep 2008
Beiträge
2.341
Punkte für Reaktionen
6
Punkte
84
zu SynoToken mit POST und GET: Hast du auch unter Systemsteuerung - Sicherheit die Option "Schutz gegen Cross-Site-Request-Forgery-Attacken verbessern" aktiviert und dich anschließend vom DSM ab- und wieder angemeldet? Nur dann wird ein SynoToken erzeugt und verwendet.
 

Tommes

Benutzer
Mitglied seit
26. Okt 2009
Beiträge
7.924
Punkte für Reaktionen
144
Punkte
209
Ja, ich habe den "Schutz gegen Cross-Site-Request-Forgery-Attacken verbessern" aktiviert und ja, der Token wird mir aktuell auch bei allen GET und POST Anfragen angehangen, aber... ich verstehe den Sinn trotzdem irgendwie noch nicht ganz. Denn ich kann die Seiten auch genausogut ohne den angegangenen Token erreichen. Daher stellt sich mir die Frage, wie oder wo wird der Token denn gegengeprüft? Oder muss ich dazu noch irgendeine Verknüpfung in meine PHP-Scripte einbauen, damit das Ganze auch greift?

Ich bin mir auch noch nicht ganz sicher, ob ich den Token richtig eingebunden habe. Bei <a href... hab ich z.B.
Code:
<a href="index.php<?php echo $token; ?>">Startseite</a>

... und bei Formularen dann...

Code:
$_SERVER['PHP_SELF'].$token

... eingesetzt. Ist das so richtig?

Weiterhin wird beim Aufruf von Ultimate Backup erstmal kein Token an die Index.php übergeben. Wo stelle ich ein, das beim Start von Ulimate Backup ein Token erzeugt wird.

Sorry für meine vielleicht dummen Fragen, aber ich stehe da noch ein wenig auf dem Schlauch.

Tommes
 

Tommes

Benutzer
Mitglied seit
26. Okt 2009
Beiträge
7.924
Punkte für Reaktionen
144
Punkte
209
Kann es vielleicht sein, das der Token nicht greift, weil ich Init_3rdParty als Motor für unser SPK verwende? Ich arbeite ja ausschließlich mit .php Dateien und habe keine CGI oder Perl-Scripte am Start. Liegt vielleicht hier der Hund begraben?

Tommes
 

QTip

Super-Moderator
Teammitglied
Mitglied seit
04. Sep 2008
Beiträge
2.341
Punkte für Reaktionen
6
Punkte
84
Für den Start der Anwendung muss du Nichts weiter hinzufügen, das macht die eingebunde Funktion in den folgenden Zeilen:
PHP:
$login = shell_exec("/usr/syno/synoman/webman/login.cgi");  // anmelden, gibt bei angemeldetem User diverse Variablen, u.a. SynoToken, zurück
preg_match('/\"SynoToken\"\s*?:\s*?\"(.*)\"/',$login,$synotoken);  // SynoToken auslesen...
$synotoken = trim($synotoken[1]);  // und bereinigen
$tmpenv = getenv('QUERY_STRING');  // aktuellen QueryString sichern (wird am Ende des Scripts wiederhergestellt)
putenv('QUERY_STRING=SynoToken='.$synotoken);  // SynoToken dem aktuellen QueryString anhängen
Du benötigst die Variante
PHP:
my $token = ($synotoken ? "?SynoToken=".$synotoken : "");
und dann "deine-url....".$token;
, wenn du z.B. zum Sichern oder einem Upload (Verbindung mit dem Backend) ein weiteres Script benutzt, welches auch direkt über die Browserzeile aufrufbar ist.
Ich platziere am Anfang jedes Scriptes die "kleine Abfrage"
PHP:
$synouser = shell_exec("/usr/syno/synoman/webman/modules/authenticate.cgi");
if ($synouser == '') exit;
Wenn dann beim Aufruf der HTTP-Fehler 500 oder 403 erscheint, dann bin ich mit diesem Script nicht angemeldet bzw. der SynoToken fehlt.
 

Tommes

Benutzer
Mitglied seit
26. Okt 2009
Beiträge
7.924
Punkte für Reaktionen
144
Punkte
209
Alles klar. Dann sag ich bis hierin erstmal Danke für deine Hilfe und Ausführungen.

Das Berechtigungssystem läuft so wie es soll. Ich kann Benutzern und Gruppen den Zugriff auf Ultimate Backup erlauben oder entziehen. Das ist erstmal das wichtigste für mich gewesen, damit kein "unerlaubter" Zugriff auf das SPK erfolgen kann. Das mit dem Token bleibt mir aber weitehin ein Rätsel, welches wir hier aber nicht mehr vertiefen müssen. Ich hab den Token jetzt einfach mal eingebaut, jedoch sehe ich den Nutzen noch nicht wirklich, da alles auch ohne diesen läuft. Vielleicht stolper ich ja irgendwann über den Stein der Erkenntis. Wie dem auch sei...

Danke nochmal,

Tommes
 

QTip

Super-Moderator
Teammitglied
Mitglied seit
04. Sep 2008
Beiträge
2.341
Punkte für Reaktionen
6
Punkte
84
Wie schon geschrieben, ein separates Einbinden des Token ist nur in den von mir erläuterten Fällen notwendig.
 

PsychoHH

Benutzer
Mitglied seit
03. Jul 2013
Beiträge
2.967
Punkte für Reaktionen
1
Punkte
78
Hallo QTip,


ich wollte mal Fragen ob du auch eine Möglichkeit hast dies über die Shell/CGI zu machen?

Wenn ich mir z.B. den Token in eine Variable hole, kann ich den Token auch ausgeben lassen.
token=$(/usr/syno/synoman/webman/login.cgi)


Beim neu laden der Seite geht aber nichts mehr.

Dabei ist es egal ob ich den token an die cgi hänge oder nicht.
Sobald ich die Variable auskommentiere klappt wieder alles.
 

QTip

Super-Moderator
Teammitglied
Mitglied seit
04. Sep 2008
Beiträge
2.341
Punkte für Reaktionen
6
Punkte
84
Was genau meinst du mit "Beim neu laden der Seite geht aber nichts mehr", was geht dann nicht mehr? Ein konkretes Beispiel wäre schön.
Also CGI's benutze ich ja selber, dort wird der Token vom aufrufenden Script gesetzt, da die CGI's bei mir hauptsächlich per AJAX (Javascript) aufgerufen werden.
 

PsychoHH

Benutzer
Mitglied seit
03. Jul 2013
Beiträge
2.967
Punkte für Reaktionen
1
Punkte
78
Also irgendwie scheint es wohl an post zu liegen.
Mit get geht es.

read -n $CONTENT_LENGTH QUERY_STRING_POST

Hab dort alles probiert aber keine Ahnung wieso es wohl bei Post nicht klappt.
 

voodoo44

Benutzer
Mitglied seit
27. Jan 2012
Beiträge
26
Punkte für Reaktionen
1
Punkte
3
Das scheint unter DSM7 (evtl. mache ich aber auch was falsch) so nicht mehr zu funktionieren.

PHP:
$login = shell_exec("/usr/syno/synoman/webman/login.cgi");
returned "null", ein Aufruf des CGI-Files per CLI funktioniert, liefert ein
{
"reason" : "error_cantlogin",
"result" : "error",
"success" : false
}

Das hängt an den Berechtigungen, weil ja jetzt jeder Benutzer im Grunde sein Script selbst ausführt, die login.cgi aber root gehört mit Berechtigungen 550.

Berechtigungen ändern oder login.cgi + authenticate.cgi kopieren geht.... halte ich aber nicht für sinnvoll. Hast du da ggf. ne Idee?
 
Zuletzt bearbeitet:

Tosoboso

Benutzer
Mitglied seit
27. Aug 2012
Beiträge
1.221
Punkte für Reaktionen
45
Punkte
74
Hallo zusammen,
Das Scheitern der 3rd Party Authentication unter DSM-7 liegt an den Zugriffsrechten: /usr/syno/synoman/webman/login.cgi & authenticate.cgi, sowie /usr/syno/bin/synowebapi können nur von root und system Group und eben nicht von users ausgeführt werden.
Es gibt aktuell zwei Möglichkeiten:
1) 'brute force backdoor': im privilege File des Pakets: "join-groupname": "system" setzen, dann ist man wieder im Spiel.

2) 'Good Citicen workaround': Unter Verwendung einer non-privileged Gruppe muss man login.cgi & authenticate.cgi in die eigene UI Area kopieren, dann gehört diese dem Paket User und es funktioniert wieder. Siehe auch hier: https://www.synology-forum.de/threads/wann-koennen-wir-dsm-7-0-erwarten.97322/page-32#post-891036
Das ist m. E. Design-Fehler von Synology und wir sollten das gemeinsam Melden als Bug in der Beta Melden. Denn wenn die Dateien read/execute all hätten, dann funktioniert's und es ist widersinnig bei JS client-seitig kann man/jeder die Dateien aufrufen (da Backend dann root ist)..
Siehe auch: https://global.download.synology.co.../DSM/7.0/enu/DSM_Developer_Guide_7_0_Beta.pdf Seite: 85 da wird angepriesen, dass man mit C, damit auch Perl, authenticate.cgi Aufrufen kann; was ja nicht mehr geht ohne Root Rechte..

Zu 2) Eine unter DSM-6 /7 "funktionierende" Perl syno-cgi sieht dann so aus, wobei im ui/modules Verzeichnis login.cgi, authenticate.cgi, synowebapi erwartet werden.
Wichtig ist, dass die Dateien vom jeweiligen DMS 6 / 7 Build stammen müssen; sonst scheitert das Laden der Libs.
Code:
#**************************************************************************#
#  syno_cgi.pl                                                             #
#  Description: Script to get the query parameters and permissions of      #
#  active user for the calling perl 3rd party application.    #
#  Replaces the need for CGI.pm no longer shipped in DSM-6.   #
#  my $p = param ('foo') provides qs-parameter same as CGI.pm #
#  my ($a,$u,$t) = usr_priv() provides user sys privileges.  #
#  $a=in admin grp, $u=name of active user, $t=syno token.    #
#  Ensure to put syno's login.cgi, authenticate.cgi, synowebapi to modules.   #
#  Author:      TosoBoso & QTip's input; German Synology Support Forum.    #
#  Copyright:   2017-2020 by TosoBoso                                      #
#  License:     GNU GPLv3 (see LICENSE)                                    #
#  ------------------------------------------------------------------- --  #
#  Version:     0.4 - 09/22/2020                                           #
#**************************************************************************#
use File::Basename;
my $rpInitialised = 0; # to have requestParams only loaded once
my $reqParams; # html query string captured from cgi

sub parse_qs { # parsing the quesry string provided via get or post
    my $queryString = shift; # query string to parse from get or post
    my @pairs = split(/&/, $queryString);
    foreach $pair (@pairs) {
        my ($key, $value) = split(/=/, $pair);
        $value =~ tr/+/ /; # trim away +
        $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;
        $value =~ s/\r\n/\n/g; # windows fix
        $value =~ s/\r/\n/g; # mac fix
        $reqParams->{lc($key)}=$value; # normalize to lower case
    }
    $rpInitialised = 1;
}
sub param { # html-get / post parameters as alternative to cgi.pm not always shiped on syno
    my $rp = shift; # the get request parameter
    return '' if ( !$rp || !$ENV{'REQUEST_METHOD'} ); # exit if no request set
    # parse once url params from query string this will catch anything after the "?"
    if( ($ENV{'REQUEST_METHOD'} eq 'GET') && $ENV{'QUERY_STRING'} && !$rpInitialised) {
        parse_qs($ENV{'QUERY_STRING'});
    }    
    if( ($ENV{'REQUEST_METHOD'} eq 'POST') && !$rpInitialised) {
        my $formData; # data from form via post
        read(STDIN, $formData, $ENV{'CONTENT_LENGTH'});
        parse_qs($formData);
    }
    return $reqParams->{$rp};
}
sub get_usr_token { # get loged on user and synotoken, use own syno-modules to address low privilege
    my $user = '';
    my $token = '';
    # save http_get environment and restore later to get syno-cgi working for token and user
    my $tmpenv = $ENV{'QUERY_STRING'};
    my $tmpreq = $ENV{'REQUEST_METHOD'};
    $ENV{'QUERY_STRING'}="";
    $ENV{'REQUEST_METHOD'} = 'GET';
    my $synoModules = "/usr/syno/synoman/webman/modules"; # default dir will only work as root
    # if exist switch to synomodules shipped with package with permission for non privileged user
    $synoModules = dirname( $ENV{'SCRIPT_FILENAME'} ) . '/modules' if ($ENV{'SCRIPT_FILENAME'} && -f dirname( $ENV{'SCRIPT_FILENAME'} ) . '/modules/login.cgi');
    # get the synotoken to verify login if non root using login.cgi in our root with our acls
    if (open (IN,"$synoModules/login.cgi|")) {
        while(<IN>) {
            if (/SynoToken/) { ($token)=/SynoToken" *: *"([^"]+)"/; }
        }
        close(IN);
    }
    else {
        $token = 'no-permission login.cgi';
    }
    if ( $token ne '' && $token ne 'no-permission login.cgi' ) { # no token no query respectively in cmd-line mode
        $ENV{'QUERY_STRING'}="SynoToken=$token";
        $ENV{'X-SYNO-TOKEN'} = $token;
        # get the user loged into gui if non root using login.cgi in our root with our acls
        if (open (IN,"$synoModules/authenticate.cgi|")) {
            $user=<IN>;
            chop($user);
            close(IN);
        }
    }
    $ENV{QUERY_STRING} = $tmpenv;
    $ENV{'REQUEST_METHOD'} = $tmpreq;
    return ($user,$token);
}
sub usr_priv { # legacy simple way: admin privilege on system level via id -G = 101
    my $isAdmin = 0;
    my ($user,$token) = get_usr_token();
    # verify if active user is part of administrators group on syno: 101
    if ($user && open (IN,"id -G ".$user." |")) {
        my $groupIds=<IN>;
        chop($groupIds) if $groupIds;
        $isAdmin = 1 if index($groupIds, "101") != -1;
        close(IN);
    }    
    return ($isAdmin,$user,$token);
}
sub app_priv { # new way: admin privilege on syno app level
    my $appName = shift;
    my $appPrivilege = 0;
    my $isAdmin = 0;
    my $rawData = '';
    use JSON::XS;
    use Data::Dumper;
    my $synoModules = "/usr/syno/bin"; # default dir will only work as root
    # if exist switch to synomodules shipped with package with permission for non privileged user
    $synoModules = dirname( $ENV{'SCRIPT_FILENAME'} ) . '/modules' if ($ENV{'SCRIPT_FILENAME'} && -f dirname( $ENV{'SCRIPT_FILENAME'} ) . '/modules/synowebapi');
    my ($user,$token) = get_usr_token();
    # verify user allowed admin on application level, with low privilege use our own modules path
    $rawData = `$synoModules/synowebapi --exec api=SYNO.Core.Desktop.Initdata method=get version=1 runner=$user`;
    return (0,'','') unless ($rawData); # exit if empty to avoid JSON exception
    $initdata = JSON::XS->new->decode($rawData);
    $appPrivilege = (defined $initdata->{'data'}->{'AppPrivilege'}->{$appName}) ? 1 : 0;
    $isAdmin = (defined $initdata->{'data'}->{'Session'}->{'is_admin'} && $initdata->{'data'}->{'Session'}->{'is_admin'} == 1) ? 1 : 0;
    # if application not found or user not admin, return empty string
    return (0,'','') unless ($appPrivilege || $isAdmin);
    return ($isAdmin,$user,$token);
}
# return true for included libraries
1
-TosoBoso
 

Tosoboso

Benutzer
Mitglied seit
27. Aug 2012
Beiträge
1.221
Punkte für Reaktionen
45
Punkte
74
Hallo zusammen,
es ist vollbracht: ein DSM-7 Paket mit Docker und neuer syno_cgi.pl https://www.cphub.net/?p=jitsimeet (arch=x86_64).
Ich habe einige nette Hacks mit Docker vollziehen müssen, dass alles Funktioniert. Der Weg zu einem DSM-/ Paket jenseits von PHP ist steinig...
Damit dem Paket die authenticate.cgi, login.cgi, synowebapi passen, Kopiere ich z.B. Diese in den ui/modules Ordner rüber und zwar mit Prüfung bei jedem Start, ob sich was binär geändert hat. Das Kopieren erfolgt mit einem Docker Hilfs-Container, der das System-Verzeichnis /usr/syno/bin mounted.

Docker ist nun mall root.. Also wie ein 3rd Party Entwickler das Alles hinbekommen soll ohne Hilfe, Reverse Engineering und Energie, ist mir schleierhaft.

PS: möchte sich jemand mit mir austauchen über die Verwendung der sogenannten Helpers, womit man Softlinks etc. Anlegen kann, ohne root Rechte?
Einiges habe ich schon am Laufen, aber das automatische Anlegen eines Reverse Proxy mit URL klappt noch nicht und ist auch nicht dokumentiert.
-TosoBoso
 

Tosoboso

Benutzer
Mitglied seit
27. Aug 2012
Beiträge
1.221
Punkte für Reaktionen
45
Punkte
74
Das Scheitern der 3rd Party Authentication unter DSM-7 liegt an den Zugriffsrechten: /usr/syno/synoman/webman/login.cgi & authenticate.cgi, sowie /usr/syno/bin/synowebapi können nur von root und system Group und eben nicht von users ausgeführt werden.
Es gibt aktuell zwei Möglichkeiten:
1) 'brute force backdoor': im privilege File des Pakets: "join-groupname": "system" setzen, dann ist man wieder im Spiel.
2) 'Good Citicen workaround': Unter Verwendung einer non-privileged Gruppe muss man login.cgi & authenticate.cgi in die eigene UI Area kopieren, dann gehört diese dem Paket User und es funktioniert wieder.
Hallo zusammen,
ich habe einen "Proof of Concept" gebaut, für den Fall 1), um euch eine Vorlage zu geben für "join-groupname": "system" im conf/privilege File:
Code:
{
    "defaults":{
        "run-as": "package"
    },
    "username": "DummySpk7",  <= euer Paket-Name, der als User angelegt wird, unter dem das Paket läuft 
    "join-groupname": "system" <= damit seid Ihr wieder im Rennen für Authentication, Zugriff auf System-Files, z.B. /var/log/messages etc.
}
Das DummySpk7 ist ein voll funktionsfähiges Skelett-Paket, das eine min UI mit Perl und ExtJS implementiert und Authentication via syno_cgi.pl nutzt. Es sollte eine gute DSM-7 Vorlage sein für allen "Nicht-Docker" Pakete, wo man meine oben beschriebenen Hacks nicht anwenden kann.
Dank der Gruppe system hat man Zugriff auf Synology autheticate.cgi und noch viel mehr :). Es sollten auch die alten Skripte von QTip funktionieren.

Ich hab auch mal für Spass versucht ein bestehendes SPK, LogAnalysis von Tommes: https://www.cphub.net/?p=loganalysis auf DSM-7 zu "pimpen" mit Hilfe einer conf/privilege wie oben und angepasster INFO. Die Installation hatte auch funktioniert auf DSM-7, aber in den Skripten waren noch Inkompatibilitäten, d.h. die Shell und Perl Scripte müssten noch angepasst werden, aber im Prinzip funktioniert das als Portierungs-Option.
@Tommes : du kannst mich ja per IM kontaktieren, dann schick ich dir den Versuch. Alternativ kannst du dir das Dummy Paket als Anregung nehmen.
@QTip: Growler: https://www.cphub.net/?p=growler kann damit m.E. auf DSM-7 gehoben werden. Du verwendets ja vergleichbare Komponenten mit extjs, authenticate etc., wie ich. Nur mit synodsmnotify wirst du noch etwas Arbeit haben; das habe ich noch nicht geknackt und suche eine Lösung: https://www.synoforum.com/threads/dsm7-synodsmnotify-behaviour-how-to-use-i18n-format.5696/

Glück auf an alle 3rd Party Entwickler. Lasst euch nicht entmutigen mit den DSM-7 Fallstricken und versucht eure Pakete zu Portieren..
-TosoBoso
 

Anhänge

  • DummySpk7.zip
    13,7 KB · Aufrufe: 4

Tommes

Benutzer
Mitglied seit
26. Okt 2009
Beiträge
7.924
Punkte für Reaktionen
144
Punkte
209
Ich hab auch mal für Spass versucht ein bestehendes SPK, LogAnalysis von Tommes ... zu "pimpen" ... Die Installation hatte auch funktioniert ... die Shell und Perl Scripte müssten noch angepasst werden, aber im Prinzip funktioniert das als Portierungs-Option.

@Tommes : du kannst mich ja per IM kontaktieren, dann schick ich dir den Versuch. Alternativ kannst du dir das Dummy Paket als Anregung nehmen.

Ganz ehrlich? Ich bin jetzt erstmal ziemlich geplättet das du ausgerechnet eins meiner SPK‘s zum Testen herangezogen hast. Ganz zu schweigen von der Tatsache, das du eine Möglichkeit gefunden hast, DSM 7 zu überlisten. Ich weiß garnicht wie tief ich mich jetzt vor dir verneigen soll, aber mein Dank ist dir auf jeden Fall sicher. Darauf kannst du einen lassen.

Mein Problem besteht jedoch aktuell darin, das ich mich noch gar nicht groß mit DSM 7 auseinandergesetzt habe und das ich grad auch gar keine Maschine für die DSM 7 Beta zur freien Verfügung habe um das alles mal zu probieren.

Glück auf an alle 3rd Party Entwickler. Lasst euch nicht entmutigen mit den DSM-7 Fallstricken und versucht eure Pakete zu Portieren..

Tja, eigentlich hatte ich mich schon mit dem Gedanken abgefunden, meine 3rd Party Developer Karriere an den Nagel zu hängen und es gut sein zu lassen. Denn auch wenn ich Synologys Strategie ein Stück weit nachvollziehen kann, so bin ich doch auch ziemlich enttäuscht darüber, welche Steine Synology einem da ständig in den Weg legt. Aber gut...

Sobald ich hier ein DSM 7 zum rumspielen habe, werde ich mir dein DummySPK aber auf jeden Fall mal vorknöpfen und mich daran versuchen. Auch würde ich mich ziemlich darüber freuen, wenn du mir das modifizierte LogAnalysis zukommen lassen könntest. Du bekommst also in kürze ein PM von mir...

Bis hierhin schon mal ein ganz dickes Dankeschön meinerseits und meinen vollen Respekt.

Tommes
 

Tosoboso

Benutzer
Mitglied seit
27. Aug 2012
Beiträge
1.221
Punkte für Reaktionen
45
Punkte
74
..Mein Problem besteht jedoch aktuell darin, das ich mich noch gar nicht groß mit DSM 7 auseinandergesetzt habe und das ich grad auch gar keine Maschine für die DSM 7 Beta zur freien Verfügung habe um das alles mal zu probieren.
Sobald ich hier ein DSM 7 zum rumspielen habe, werde ich mir dein DummySPK aber auf jeden Fall mal vorknöpfen und mich daran versuchen. Auch würde ich mich ziemlich darüber freuen, wenn du mir das modifizierte LogAnalysis zukommen lassen könntest. Du bekommst also in kürze ein PM von mir...
Man braucht kein DSM-7 man kann sich die low privileges auch unter DSM-6 antun, man braucht nur die conf/privileges Anlegen (siehe oben).
Du kannst also die DummySPK7, oder die mod LogAnalysis, die ich dir geschickt habe und in INF die Zeile kommentieren: os_min_ver="7.0-4000" und firmware tag auf 6 setzen und schon ist es ein DSM-6 Paket..
-TosoBoso
 
  AdBlocker gefunden!

Du bist nicht hier, um Support für Adblocker zu erhalten, denn dein Adblocker funktioniert bereits ;-)

Klar machen Adblocker einen guten Job, aber sie blockieren auch nützliche Funktionen.

Das Forum wird mit einem hohen technischen, zeitlichen und finanziellen Aufwand kostenfrei zur Verfügung gestellt. Wir zeigen keine offensive oder Themen fremde Werbung. Bitte unterstütze dieses Forum, in dem du deinen Adblocker für diese Seite deaktivierst.