Ist es eine gute Idee, Shell-Befehle aus C heraus aufzurufen?

50

Es gibt einen Unix-Shell-Befehl ( udevadm info -q path -n /dev/ttyUSB2), den ich aus einem C-Programm aufrufen möchte. Nach ungefähr einer Woche Kampf könnte ich es wieder selbst implementieren, aber ich möchte das nicht tun.

Ist es allgemein anerkannt, dass ich nur anrufe popen("my_command", "r");, oder führt dies zu inakzeptablen Sicherheitsproblemen und leitet Kompatibilitätsprobleme weiter? Es fühlt sich für mich falsch an, so etwas zu tun, aber ich kann nicht sagen, warum es schlecht wäre.

johnny_boy
quelle
8
Eine zu berücksichtigende Sache sind Injektionsattacken.
MetaFight
8
Ich dachte hauptsächlich an den Fall, in dem Sie Benutzereingaben als Shell-Befehlsargumente angegeben haben.
MetaFight
8
Könnte jemand eine böswillige udevadmausführbare Datei in dasselbe Verzeichnis wie Ihr Programm stellen und sie zur Eskalation von Berechtigungen verwenden? Ich weiß nicht viel über Unix-Sicherheit, aber wird Ihr Programm mit ausgeführt setuid root?
Andrew Piliser
7
@AndrewPiliser Wenn sich das aktuelle Verzeichnis nicht im befindet PATH, muss es mit "no" ausgeführt werden ./udevadm. IIRC Windows würde jedoch ausführbare Dateien mit demselben Verzeichnis ausführen.
Izkata
4
Wenn immer möglich, rufen Sie das gewünschte Programm direkt auf, ohne die Shell zu durchlaufen.
CodesInChaos

Antworten:

58

Es ist nicht besonders schlimm, aber es gibt einige Einschränkungen.

  1. Wie portabel wird Ihre Lösung sein? Funktioniert Ihre gewählte Binärdatei überall gleich, gibt die Ergebnisse im gleichen Format usw. aus? Wird es bei Einstellungen von LANGetc. anders ausgegeben ?
  2. Wie viel zusätzliche Belastung bedeutet dies für Ihren Prozess? Das Verzweigen einer Binärdatei führt zu einer viel höheren Belastung und erfordert mehr Ressourcen als das Ausführen von Bibliotheksaufrufen (im Allgemeinen). Ist das in Ihrem Szenario akzeptabel?
  3. Gibt es Sicherheitsprobleme? Kann jemand Ihre gewählte Binärdatei durch eine andere ersetzen und danach schändliche Taten vollbringen? Verwenden Sie vom Benutzer bereitgestellte Argumente für Ihre Binärdatei und können diese beispielsweise bereitstellen? ;rm -rf /(Beachten Sie, dass Sie mit einigen APIs Argumente sicherer angeben können, als sie nur über die Befehlszeile bereitzustellen. )

Ich bin im Allgemeinen froh, Binärdateien auszuführen, wenn ich mich in einer bekannten Umgebung befinde, die ich vorhersagen kann, wenn die Binärausgabe einfach zu analysieren ist (falls erforderlich - Sie benötigen möglicherweise nur einen Exit-Code), und ich muss dies auch nicht tun häufig.

Wie Sie bereits bemerkt haben, ist das andere Problem, wie viel Arbeit es kostet, die Funktionen der Binärdatei zu replizieren. Verwendet es eine Bibliothek, die Sie auch nutzen können?

Brian Agnew
quelle
13
Forking a binary results in a lot more load and requires more resources than executing library calls (generally speaking). Wenn dies unter Windows wäre, würde ich zustimmen. Das OP gibt jedoch Unix an, bei dem das Forken so kostengünstig ist, dass es schwer zu sagen ist, ob das dynamische Laden einer Bibliothek schlechter ist als das Forken.
Wallyk
1
Normalerweise würde ich erwarten, dass eine Bibliothek einmal geladen wird , während die Binärdatei möglicherweise mehrmals ausgeführt wird. Wie immer hängt es von den Nutzungsszenarien ab, muss aber berücksichtigt werden (falls später entlassen)
Brian Agnew
3
Es gibt kein „Forking“ unter Windows, so das Zitat hat Unix-spezifisch.
Cody Grey
5
NT-Kernel unterstützt Forking, obwohl es nicht über Win32 verfügbar gemacht wird. Wie auch immer, ich würde glauben, dass die Kosten für die Ausführung eines externen Programms mehr von der execals von der kommen würden fork. Laden von Abschnitten, Verknüpfen von freigegebenen Objekten und Ausführen von Init-Code für jeden. Und dann müssen alle Daten in Text konvertiert werden, um auf der anderen Seite analysiert zu werden, sowohl für Eingaben als auch für Ausgaben.
Spectras
8
Um fair zu sein, udevadm info -q path -n /dev/ttyUSB2wird es auf Windows sowieso nicht funktionieren, daher ist die ganze Diskussion über das Gabeln ein bisschen theoretisch.
MSalters
37

Wenn Sie einen potenziellen Vektor eingeführt haben, ist äußerste Vorsicht geboten, um sich vor Anfälligkeiten durch Injektionen zu schützen. Es steht jetzt im Vordergrund Ihres Denkvermögens, aber später müssen Sie möglicherweise die Möglichkeit zur Auswahl haben. Diese ttyUSB0-3Liste wird dann an anderen Stellen verwendet, damit sie nach dem Prinzip der einheitlichen Verantwortung ausgeklammert wird. Dann muss ein Kunde eine Anforderung stellen Ein beliebiges Gerät in der Liste, und der Entwickler, der diese Änderung vornimmt, hat keine Ahnung, dass die Liste möglicherweise auf unsichere Weise verwendet wird.

Mit anderen Worten, Code, als ob der sorgloseste Entwickler, den Sie kennen, eine unsichere Änderung an einem scheinbar nicht verwandten Teil des Codes vornimmt.

Zweitens wird die Ausgabe von CLI-Tools im Allgemeinen nicht als stabile Schnittstelle betrachtet, es sei denn, die Dokumentation kennzeichnet sie ausdrücklich als solche. Sie können sich möglicherweise auf sie verlassen, wenn Sie ein Skript ausführen, das Sie selbst beheben können, jedoch nicht auf etwas, das Sie für einen Kunden bereitstellen.

Drittens, wenn Sie auf einfache Weise einen Wert aus Ihrer Software extrahieren möchten, ist die Wahrscheinlichkeit groß, dass es auch jemand anderes möchte. Suchen Sie nach einer Bibliothek, die bereits das tut, was Sie wollen. libudevwar bereits auf meinem System installiert:

#include <libudev.h>
#include <sys/stat.h>
#include <stdio.h>

int main(int argc, char* argv[]) {
    struct stat statbuf;

    if (stat("dev/ttyUSB2", &statbuf) < 0)
        return -1;
    struct udev* udev = udev_new();
    struct udev_device *dev = udev_device_new_from_devnum(udev, 'c', statbuf.st_rdev);

    printf("%s\n", udev_device_get_devpath(dev));

    udev_device_unref(dev);
    udev_unref(udev);
    return 0;
}

In dieser Bibliothek gibt es weitere nützliche Funktionen. Ich vermute, wenn Sie dies benötigen, könnten einige der zugehörigen Funktionen auch nützlich sein.

Karl Bielefeldt
quelle
2
DANKESCHÖN. Ich habe so lange nach libudev gesucht. Ich konnte es einfach nicht finden!
Johnny_boy
2
Ich verstehe nicht, wie "Injection Vulnerabilities" ein Problem sind, es sei denn, das Programm von OP wird aufgerufen, wenn ein anderer Benutzer die Kontrolle über seine Umgebung hat cgi und ssh forcierter Befehl). Es gibt keinen Grund, warum normale Programme gegen den Benutzer, unter dem sie ausgeführt werden, abgesichert werden müssen.
R ..
2
@R ..: Die "Injection Vulnerability" ist, wenn Sie die verallgemeinern ttyUSB2und verwenden sprintf(buf, "udevadm info -q path -n /dev/%s", argv[1]). Nun , da erscheint ziemlich sicher, aber was ist, wenn argv[1]ist "nul || echo OOPS"?
MSalters
3
@R ..: Sie brauchen nur mehr Vorstellungskraft. Zuerst ist es eine feste Zeichenfolge, dann ein Argument des Benutzers, dann ein Argument eines anderen vom Benutzer ausgeführten Programms, das sie jedoch nicht verstehen. Dann ist es ein Argument, das ihr Browser von einer Webseite extrahiert hat. Wenn es weiter bergab geht, muss irgendwann jemand die Verantwortung für die Bereinigung der Eingabe übernehmen, und Karl sagt, dass es äußerst sorgfältig ist, diesen Punkt zu identifizieren, wo immer er auch herkommt.
Steve Jessop
6
Wenn das Vorsorgeprinzip tatsächlich lautete: "Nehmen Sie an, der unvorsichtigste Entwickler, den Sie kennen, führt eine unsichere Änderung durch", und ich musste jetzt Code schreiben, um zu gewährleisten, dass kein Exploit ausgeführt werden kann, dann konnte ich persönlich nicht aufstehen. Die nachlässigsten Entwickler, die ich kenne, lassen Machievelli so aussehen, als würde er es nicht einmal versuchen .
Steve Jessop
16

udevadmIch würde vermuten, dass Sie in Ihrem speziellen Fall, in dem Sie aufrufen möchten, udevalternativ eine Bibliothek aufrufen und die entsprechenden Funktionsaufrufe ausführen könnten .

Sie können sich beispielsweise ansehen, was udevadm selbst tut, wenn Sie im "info" -Modus aufrufen: https://github.com/gentoo/eudev/blob/master/src/udev/udevadm-info.c und gleichwertige Anrufe tätigen was diese udevadm macht.

Dies würde viele der in Brian Agnews hervorragender Antwort aufgezählten Nachteile vermeiden - z. B. sich nicht auf die Binärdatei zu verlassen, die auf einem bestimmten Pfad vorhanden ist, die Kosten für das Gabeln zu vermeiden usw.

Adam Krouskop
quelle
Danke, ich habe genau dieses Repo gefunden und es dann ein bisschen durchgesehen.
Johnny_boy
1
… Und libudev ist nicht besonders schwer zu bedienen. Die Fehlerbehandlung ist etwas mangelhaft, aber das Auflisten, Abfragen und Überwachen von APIs ist ziemlich einfach. Der Kampf, vor dem Sie Angst haben, kann weniger als zwei Stunden dauern, wenn Sie es versuchen.
Spectras
7

Ihre Frage schien nach einer Waldantwort zu verlangen, und die Antworten hier scheinen wie Baumantworten zu sein. Ich dachte, ich würde Ihnen eine Waldantwort geben.

So werden C-Programme sehr selten geschrieben. Es ist immer so, wie Shell-Skripte geschrieben werden und manchmal, wie Python-, Perl- oder Ruby-Programme geschrieben werden.

Die Benutzer schreiben in der Regel in C, um die Systembibliotheken einfach zu verwenden, auf OS-Systemaufrufe zuzugreifen und die Geschwindigkeit zu erhöhen. Und C ist eine schwierige Sprache, in der man schreiben kann. Wenn die Leute diese Dinge nicht brauchen, dann benutzen sie kein C. Außerdem wird von C-Programmen normalerweise nur erwartet, dass sie von gemeinsam genutzten Bibliotheken und Konfigurationsdateien abhängen.

Das Ausschalten eines Unterprozesses ist nicht besonders schnell und erfordert keinen feinkörnigen und kontrollierten Zugriff auf Systemeinrichtungen auf niedriger Ebene. Daher ist die Abhängigkeit von einer externen ausführbaren Datei möglicherweise überraschend in C-Programmen.

Es gibt einige zusätzliche Bedenken. Die Sicherheits- und Portabilitätsbedenken, die die Leute erwähnen, sind vollständig gültig. Sie sind natürlich auch für Shell-Skripte gültig, aber die Leute erwarten solche Probleme in Shell-Skripten. Es wird jedoch normalerweise nicht erwartet, dass C-Programme diese Art von Sicherheitsbedenken haben, was sie gefährlicher macht.

Aber meiner Meinung nach haben die größten Bedenken mit der Art popenund Weise zu tun, wie Sie mit dem Rest Ihres Programms interagieren. popenmuss einen untergeordneten Prozess erstellen, dessen Ausgabe lesen und dessen Beendigungsstatus erfassen. In der Zwischenzeit wird dieser Prozess 'stderr mit demselben stderr wie Ihr Programm verbunden, was zu verwirrenden Ausgaben führen kann, und sein stdin entspricht dem Ihres Programms, was zu anderen interessanten Problemen führen kann. Sie können das lösen, indem </dev/null 2>/dev/nullSie den String, an den Sie übergeben, einschließen , popenda er von der Shell interpretiert wird.

Und popenerstellt einen untergeordneten Prozess. Wenn Sie selbst etwas mit Signalverarbeitung oder Gabelungsprozessen anfangen, erhalten Sie möglicherweise seltsame SIGCHLDSignale. Ihre Aufrufe zu waitinteragieren möglicherweise seltsam mit popenund schaffen möglicherweise seltsame Rennbedingungen.

Die Sicherheits- und Portabilitätsbedenken sind natürlich da. Wie für Shell-Skripte oder alles, was andere ausführbare Dateien auf dem System startet. Und Sie müssen darauf achten, dass Benutzer Ihres Programms keine Shell-Metazeichen in die Zeichenfolge einfügen können, in die Sie übergehen, popenda diese Zeichenfolge direkt shmit übergeben wird sh -c <string from popen as a single argument>.

Aber ich denke nicht, dass sie der Grund dafür sind, warum es seltsam ist, ein C-Programm zu verwenden popen. Der Grund, warum es seltsam ist, ist, dass C in der Regel eine niedrige Sprache und popenkeine niedrige Sprache ist. Und weil die Verwendung von popenBereichen das Design Ihres Programms einschränkt, weil es seltsam mit den Standardeingaben und -ausgaben Ihres Programms interagiert und es Ihnen schwer macht, Ihr eigenes Prozessmanagement oder Ihre eigene Signalverarbeitung durchzuführen. Und weil von C-Programmen normalerweise keine Abhängigkeiten von externen ausführbaren Dateien erwartet werden.

Alleswissend
quelle
0

Ihr Programm kann Hacking usw. ausgesetzt sein. Eine Möglichkeit, sich vor dieser Art von Aktivität zu schützen, besteht darin, eine Kopie Ihrer aktuellen Maschinenumgebung zu erstellen und Ihr Programm mit einem System namens chroot auszuführen.

Aus Sicht Ihres Programms wird es unter normalen Sicherheitsaspekten ausgeführt. Wenn jemand Ihr Programm beschädigt, hat es nur Zugriff auf die Elemente, die Sie beim Erstellen der Kopie angegeben haben.

Ein solches Setup wird als Chroot-Gefängnis bezeichnet. Weitere Informationen finden Sie unter Chroot-Gefängnis .

Es wird normalerweise zum Einrichten von öffentlich zugänglichen Servern usw. verwendet.

Dave
quelle
3
Das OP schreibt ein Programm, das andere Benutzer ausführen können, ohne mit nicht vertrauenswürdigen Binärdateien oder Quellen auf seinem eigenen System zu spielen.
Peter Cordes