web-dev-qa-db-ja.com

C内からシェルコマンドを呼び出すことは良い考えですか?

Cプログラムから呼び出したいUNIXシェルコマンド(_udevadm info -q path -n /dev/ttyUSB2_)があります。おそらく1週間ほどの苦労で、自分で再実装することができましたが、それはしたくありません。

私がpopen("my_command", "r");を呼び出すだけで広く受け入れられているのでしょうか、それとも許容できないセキュリティの問題を引き起こし、互換性の問題を転送するのでしょうか?このようなことをするのは私には間違っていると感じますが、なぜそれが悪いのかを指で言えません。

50
johnny_boy

特に悪くはありませんが、注意点がいくつかあります。

  1. ソリューションはどの程度移植可能ですか?選択したバイナリはどこでも同じように動作し、同じ形式で結果を出力しますか? LANGなどの設定で異なる出力になりますか?
  2. これはあなたのプロセスにどれだけ余分な負荷を加えますか?バイナリをフォークすると、ライブラリ呼び出し(一般的に言えば)を実行するよりも負荷が大きくなり、より多くのリソースが必要になります。これはあなたのシナリオで受け入れられますか?
  3. セキュリティ上の問題はありますか?誰かがあなたの選んだバイナリを別のものに置き換え、その後悪質な行為を行うことができますか?バイナリにユーザー指定の引数を使用していますか、それらは;rm -rf /(例)(一部のAPIでは、コマンドラインで引数を指定するよりも安全に引数を指定できることに注意してください)

バイナリ出力が解析しやすい(必要な場合-終了コードが必要なだけかもしれません)ときに、私が予測できる既知の環境にいるときに、バイナリを実行して満足し、私もそれを行う必要はありません。しばしば。

お気づきのように、もう1つの問題は、バイナリの機能を複製するためにどれだけの作業が必要かということです。活用できるライブラリを使用していますか?

59
Brian Agnew

潜在的なベクターを導入したら、インジェクションの脆弱性から保護するために細心の注意を払います。それは今、あなたの心の最前線にありますが、後でttyUSB0-3を選択する機能が必要になる場合があります。その場合、そのリストは他の場所で使用されるため、単一責任の原則に従うために因数分解され、顧客はリストに任意のデバイスを配置する必要があり、開発者がthatを変更すると、リストが最終的に安全でない方法で使用されることがわかりません。

言い換えれば、あなたが知っている最も不注意な開発者が、コードの無関係に見える部分に危険な変更を加えるかのようにコードを記述します。

第二に、CLIツールの出力は、ドキュメントで明確にマークされていない限り、一般に安定したインターフェイスとは見なされません。自分でトラブルシューティングできるスクリプトを実行しても大丈夫かもしれませんが、顧客に展開するものについてはそうではありません。

3番目に、ソフトウェアから値を簡単に抽出する方法が必要な場合、他の誰かがそれを望んでいる可能性があります。すでにあなたが望むことをしているライブラリを探してください。 libudevはすでにシステムにインストールされています:

#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;
}

そのライブラリには他にも便利な機能があります。私の推測では、これが必要な場合は、関連する機能の一部も役立つかもしれません。

37
Karl Bielefeldt

udevadmを呼び出す特定のケースでは、udevをライブラリとしてプルし、適切な関数呼び出しを代わりに実行できると思いますか?

たとえば、「情報」モードで起動したときにudevadm自体が何を行っているかを確認できます。 https://github.com/gentoo/eudev/blob/master/src/udev/udevadm-info.c とudevadmが行っていることに関して、equiv呼び出しを行います。

これは Brian Agnewの優れた答え -に列挙されている多くのマイナス面のトレードオフを回避します。たとえば、特定のパスに存在するバイナリに依存せず、フォークの費用を回避します.

16
Adam Krouskop

あなたの質問は森の答えを要求しているようで、ここの答えは木の答えのように思えたので、私はあなたに森の答えを与えると思いました。

これがCプログラムの作成方法であることは非常にまれです。それは常にシェルスクリプトが書かれる方法であり、時にはPython、PerlまたはRubyプログラムが書かれる方法です。

人々は通常、システムライブラリを簡単に使用し、OSシステムコールへの低レベルのアクセスを指示し、速度を上げるためにCで書き込みます。また、Cは書くのが難しい言語なので、人々がそれらを必要としない場合は、Cを使用しません。また、Cプログラムは通常、共有ライブラリと構成ファイルにのみ依存していると予想されます。

サブプロセスへのシェルアウトは特に高速ではなく、低レベルのシステム機能へのきめ細かい制御されたアクセスを必要とせず、外部実行可能ファイルへのおそらく驚くべき依存性を導入するため、一般的ではありませんCプログラムで。

いくつかの追加の懸念があります。人々が言及するセキュリティと移植性の懸念は完全に有効です。もちろん、それらはシェルスクリプトにも同様に有効ですが、人々はシェルスクリプトにこの種の問題があることを期待しています。しかし、Cプログラムには通常、この種のセキュリティ上の懸念があるとは予想されていないため、より危険です。

しかし、私の意見では、最大の懸念はpopenがプログラムの他の部分と対話する方法に関係しています。 popenは、子プロセスを作成し、その出力を読み取り、その終了ステータスを収集する必要があります。その間、そのプロセスのstderrはプログラムと同じstderrに接続されるため、混乱を招く可能性があり、そのstdinはプログラムと同じになるため、他の興味深い問題が発生する可能性があります。これは、popenに渡す文字列に</dev/null 2>/dev/nullを含めることで解決できます。これは、シェルによって解釈されるためです。

そしてpopenは子プロセスを作成します。シグナル処理やフォークプロセスを自分で行うと、奇妙なSIGCHLDシグナルが発生する可能性があります。 waitへの呼び出しは、popenと奇妙に相互作用し、奇妙な競合状態を引き起こす可能性があります。

もちろん、セキュリティと移植性の問題があります。シェルスクリプトや、システム上の他の実行可能ファイルを起動するためのものです。また、プログラムを使用している人は、popenに渡す文字列にシェルメタ文字を取得できないことに注意する必要があります。その文字列は、sh -c <string from popen as a single argument>を使用してshに直接渡されるためです。

しかし、Cプログラムがpopenを使用しているのがおかしいのは、そのためだとは思いません。奇妙な理由は、Cは通常低レベルの言語であり、popenは低レベルの言語ではないためです。また、popenを使用すると、プログラムに設計上の制約が課せられるため、プログラムの標準入出力と奇妙に相互作用し、独自のプロセス管理や信号処理を行うのが面倒になります。また、Cプログラムは通常、外部の実行可能ファイルに依存しているとは想定されていないためです。

7
Omnifarious

プログラムはハッキングなどの影響を受ける可能性があります。この種の活動から身を守る1つの方法は、現在のマシン環境のコピーを作成し、chrootと呼ばれるシステムを使用してプログラムを実行することです。

プログラムの観点からは、通常の環境での実行、セキュリティの観点から、誰かがプログラムを破った場合、コピーを作成したときに指定した要素にしかアクセスできません。

このような設定は、chroot jailと呼ばれ、詳細は chroot jail を参照してください。

通常、公的にアクセス可能なサーバーなどの設定に使用されます。

0
Dave