Android アプリがシリアル ポート ノードにアクセスするときに解決する必要がある権限の問題

この記事は Le Xiaodi vs.同級生の寄稿を転載したものです。この記事では主にシステムの基礎となる認証を実装する方法について説明しています。誰にとっても役に立つと思います。

元のアドレス:

https://juejin.cn/post/7234795667478003770

アプリケーション層はシリアルポート機能を開発する際にドライバーノードを扱う必要があることが多く、新しいハードウェアデバイスを追加する場合、通常は上位層(フレームワーク、アプリ)がアクセスするために対応するノードが提供されます。

ノードへのアクセス方法

  • Java IO

  • adbシェルキャット

USB ノードを例にとると、そのパスは /sys/devices/platform/soc/a600000.ssusb/mode で、通常は Java 標準 IO ストリームを使用します。

public static final String NODE_PATH = "/sys/devices/platform/soc/a600000.ssusb/mode";

    public void readNode(View view) {
        String result = FileUtils.readFileByLines(NODE_PATH);
        tvShow.setText(result);
        Log.d(TAG, "node data:"+result);
    }

    public static String readFileByLines(String fileName) {
        BufferedReader bufferedReader = null;
        FileReader fileReader = null;
        try {
            File file = new File(fileName);
            if (!file.exists()) {
                return "文件不存在";
            }
            fileReader = new FileReader(file);
            bufferedReader = new BufferedReader(fileReader);
            String string;
            StringBuffer sb = new StringBuffer();
            while ((string = bufferedReader.readLine()) != null) {
                sb.append(string);
            }
            return sb.toString();
        } catch (IOException e) {
            e.printStackTrace();
            return e.getMessage();
        } finally {
            if (fileReader != null) {
                try {
                    fileReader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (bufferedReader != null) {
                try {
                    bufferedReader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

または、adb シェルに入り、対応するノード名を直接 cat してノード データにアクセスします。

8e597bc715e37ae435dca49c69f4c3fd.jpeg

権限の問題

ただし、直接アクセスすることは絶対に不可能であり、権限エラーが報告されます。

872f10256fce75ecfb7178b4fcce63ab.png

そして、基礎となるノードへの上位層のアクセスを実現する中核となるのは、権限の問題に対処することであり、これには主に次の 2 方向の権限処理が含まれます。

  • SEAndroidの権限

  • ノードファイル自体の読み取りおよび書き込み権限

まず、アプリはアプリケーション層に属し、ドライバーノードはカーネル層に属します。SEAndroid の制限により、アプリケーション層はカーネル層のドライバーノードファイルにアクセスできません。これをよく理解する必要があります。アプリケーション層がカーネルに簡単にアクセスできる場合、Android は安全ではないため、現時点では、サードパーティのアプリもカーネル ノード ファイルにアクセスできるように、SEAndroid の権限制限を変更する必要があります。

SEAndroidの紹介

この SEAndroid の権限の問題を解決する前に、まず SEAndroid とは何かを理解し、わかりやすい言葉で簡単に紹介します。

SEAndroid を理解する前に、まず SELinux について理解する必要があります。SELinux は、米国 NSA と SCC によって開発された Linux 用の拡張必須アクセス制御セキュリティ モジュールです。SEAndroid は、Android システムのセキュリティ管理に特別に使用される SELinux のカスタマイズされたバージョンに相当します。Android 4.4 より前では、プロセスの権限は現在のユーザーの権限と一致していました。つまり、root ユーザーとしてプロセスを開始すると、プロセスは root 権限を持ち、必要なことを何でも実行できます。このモードはDAC(随意アクセス制御)と呼ばれます。明らかに、DAC は緩すぎたので、SEAndroid が追加されました。何かを実行したいプロセスには、「セキュリティ ポリシー ファイル」でアクセス許可を与える必要があります。これが MAC (Mandatory Access Control) モードです。

実際、現在の Android システムは DAC モードと MAC モードの両方を使用しており、システムは最初に DAC チェックを実行し、これが失敗すると操作は直接失敗します。DAC チェックに合格したら、MAC チェックを実行します。

MAC セキュリティ ポリシー ファイルに関して、SELinux には、SELinux ポリシー言語と呼ばれる、記述するための一連のルールがあります。この言語の基本的なルールについては、こちらの記事「Android System SELinux (SEAndroid)」 (https://blog.csdn.net/tq501501/article/details/118214159)を参照してください。

権限の処理

権限の問題を解決するには、通常、最初に SELinux 検証をオフにし、同時にターゲット ノード ファイルの読み取りおよび書き込み権限を最大に設定し、実行します。

adb root
adb shell setenforce 0
adb shell chmod 777 /sys/devices/platform/soc/a600000.ssusb/mode

ノードに正常にアクセスできるかどうかを確認します。成功した場合は、実際にアクセス許可に問題があることを意味します。次に、logcat ログ (フレームワーク) と dmesg ログ (カーネル) をそれぞれ取得し、キーワード avc を検索して関連するエラー ログを取得します。許可に。

埋め込む

# 抓取logcat日志
adb logcat | findstr avc > E://logcat.txt
# 抓取dmesg日志
adb shell dmesg | findstr avc > E://dmesg.txt

取得したログ結果は以下の通りです

[  312.961648] type=1400 audit(1684478793.923:22): avc: denied { read } for comm=".jason.nodetest" name="soc" dev="sysfs" ino=10272 scontext=u:r:untrusted_app:s0:c66,c256,c522,c768 tcontext=u:object_r:sysfs:s0 tclass=dir permissive=0
[  312.962343] type=1400 audit(1684478793.923:23): avc: denied { read } for comm=".jason.nodetest" name="soc" dev="sysfs" ino=10272 scontext=u:r:untrusted_app:s0:c66,c256,c522,c768 tcontext=u:object_r:sysfs:s0 tclass=dir permissive=0
[  314.145472] type=1400 audit(1684478795.107:24): avc: denied { read } for comm=".jason.nodetest" name="mode" dev="sysfs" ino=29874 scontext=u:r:untrusted_app:s0:c66,c256,c522,c768 tcontext=u:object_r:vendor_sysfs_usb_device:s0 tclass=file permissive=0
[  314.886338] type=1400 audit(1684478795.847:25): avc: denied { write } for comm=".jason.nodetest" name="mode" dev="sysfs" ino=29874 scontext=u:r:untrusted_app:s0:c66,c256,c522,c768 tcontext=u:object_r:vendor_sysfs_usb_device:s0 tclass=file permissive=0
[  315.523664] type=1400 audit(1684478796.483:26): avc: denied { write } for comm=".jason.nodetest" name="mode" dev="sysfs" ino=29874 scontext=u:r:untrusted_app:s0:c66,c256,c522,c768 tcontext=u:object_r:vendor_sysfs_usb_device:s0 tclass=file permissive=0
[  315.945379] type=1400 audit(1684478796.907:27): avc: denied { write } for comm=".jason.nodetest" name="mode" dev="sysfs" ino=29874 scontext=u:r:untrusted_app:s0:c66,c256,c522,c768 tcontext=u:object_r:vendor_sysfs_usb_device:s0 tclass=file permissive=0
[  316.294456] type=1400 audit(1684478797.255:28): avc: denied { read } for comm=".jason.nodetest" name="mode" dev="sysfs" ino=29874 scontext=u:r:untrusted_app:s0:c66,c256,c522,c768 tcontext=u:object_r:vendor_sysfs_usb_device:s0 tclass=file permissive=0

ログの 1 つを詳しく分析してみましょう

[  314.145472] type=1400 audit(1684478795.107:24): avc: denied { read } for comm=".jason.nodetest" name="mode" dev="sysfs" ino=29874 scontext=u:r:untrusted_app:s0:c66,c256,c522,c768 tcontext=u:object_r:vendor_sysfs_usb_device:s0 tclass=file permissive=0
  • avc は、これが SELinux 関連のエラーであることを意味します

  • 拒否された { 読み取り } は読み取り権限がないことを示します

  • comm=".jason.nodetest" はどのプロセスがアクセスしているかを示します

  • name="mode" は対象ファイルを示します

  • dev="sysfs" は、.jason.nodetest プロセスによってアクセスされるターゲット ファイルのファイル タイプを示します。

  • scontext=u:r:untrusted_app はアクセス プロセスのタイプを示します。ここではサードパーティ アプリを示します。

  • tcontext=u:object_r:vendor_sysfs_usb_device:s0 は、ターゲット ファイルのスペース名を示します

つまり、サードパーティ アプリは、vendor_sysfs_usb_device 空間のモード ファイルにアクセスするには読み取り権限が必要です。

OK、エラーの原因がわかったので、それにアクセス許可を追加しましょう。

ステップ 1: genfs_contexts ファイル内でターゲット ノード ファイルの宣言を検索します。

genfscon sysfs /devices/soc/a800000.ssusb/a800000.dwc3/xhci-hcd.0.auto/usb1 u:object_r:vendor_sysfs_usb_device:s0
genfscon sysfs /devices/soc/a800000.ssusb/a800000.dwc3/xhci-hcd.0.auto/usb2 u:object_r:vendor_sysfs_usb_device:s0
genfscon sysfs /devices/platform/soc/a600000.ssusb/mode                 u:object_r:vendor_sysfs_usb_device:s0
# 目标节点
genfscon sysfs /devices/platform/soc/a800000.ssusb/mode                 u:object_r:vendor_sysfs_usb_device:s0

実際に、vendor_sysfs_usb_device スペースで定義されており、他のノードもこのスペースで定義されていることがわかりました。

ステップ 2: file.te ファイルで、vendor_sysfs_usb_device スペースのタイプ定義を見つけます。

type vendor_sysfs_usb_device, sysfs_type, fs_type;

タイプは sysfs と fs です

ステップ 3: Vendor_sysfs_usb_device スペースにアクセスするためのサードパーティ アプリの権限を untrusted_app_25.te ファイルに追加します。

allow untrusted_app vendor_sysfs_usb_device:file rw_file_perms;

この構文の書き方については、以前別記事「Qualcomm Android 11 Platform への GNSS の移植」 (https://juejin.cn/post/7225152932676927543) で触れましたので、以下に再掲します。

2f6a908d4b7ab3ff5ae8ed37d766dca9.png

これを覚えるのが難しい場合は、Linux が提供する Audit2allow ツールを使用して、自動的に分析および生成することもできます。

audit2allow -i dmesg.txt

3c92ff59019a89dacb8888c0da6bd96c.jpeg

生成された te コマンドを te ファイルに直接設定します。

設定後は、セキュリティ ポリシーが Neverallow と競合できないことを忘れないでください。Neverallow ルールを確認したところ、SEAndroid ではサードパーティ アプリが sysfs および fs タイプのファイルにアクセスできないと規定されていることがわかりました。

# Do not allow any write access to files in /sys
# 第三方app不允许访问sysfs类型文件
neverallow all_untrusted_apps sysfs_type:file { no_w_file_perms no_x_file_perms };

# Apps may never access the default sysfs label.
# 第三方app不允许访问sysfs类型文件
neverallow all_untrusted_apps sysfs:file no_rw_file_perms;

neverallow { all_untrusted_apps -mediaprovider } {
# 不允许访问fs类型文件
  fs_type
  -sdcard_type
  file_type
  -app_data_file            # The apps sandbox itself
  -privapp_data_file
  -app_exec_data_file       # stored within the app sandbox directory
  -media_rw_data_file       # Internal storage. Known that apps can
                            # leave artfacts here after uninstall.
  -user_profile_data_file   # Access to profile files
  userdebug_or_eng(`
    -method_trace_data_file # only on ro.debuggable=1
    -coredump_file          # userdebug/eng only
  ')
}:dir_file_class_set { create unlink };

これらの Neverallows を削除します。system/sepolicy/private/app_neverallows.te と system/sepolicy/prebuilts/api/30.0/private/app_neverallows.te を削除する必要があることに注意してください。これら 2 つのファイルは一貫している必要があり、そうでない場合はコンパイル エラーが発生します。報告。

この時点で、検証を完了できます。この時点では、ノードの読み取りには問題がないことがわかりますが、ノードにデータを書き込むときに権限の問題がまだ残っています。これは、ノード ファイル自体が読み取りしか行っていないためです。システム起動後にノードファイルに権限を付与する必要があるので、system/core/rootdir/init.rc に起動条件で書き込み権限を追加します。

chmod 0777 /sys/devices/platform/soc/a600000.ssusb/mode

このノード ファイルの所有者がrootであるため、コンパイルおよび実行時に権限エラーが報告されます。

init プロセスの所有者は system です。システム所有者が root 所有者のファイル権限を直接変更するだけでは不十分です。vendor_sysfs_usb_device へのアクセス権限を init プロセスに追加する必要があります。これを init.te ファイルに追加します。

allow init vendor_sysfs_usb_device:file setattr;

また、vendor_sysfs_usb_device を mlstrustedobject タイプとして定義します

type vendor_sysfs_usb_device, sysfs_type, fs_type, mlstrustedobject;

この時点で、再度コンパイルして検証すると、サードパーティ アプリがドライバー ノードを直接読み書きできるようになります。

読み取りノード

edbdeb8d8f58f735733aa36ad6cef1e7.png

書き込みノード

713d1126d13423121a13d9fab753225d.png

要約する

この記事は、Android 開発を行っている友人に、基礎となるハードウェアが上位層にアクセスできる必要があることと、権限の問題に対処する方法を理解させることができます。SELinux 関連の知識ポイントは非常に抽象的で、初心者にとっては理解するのが困難ですが、いくつかの関連する問題に対処すると、何が起こっているのかをすぐに理解できます。

より多くの知識を得る、または記事に貢献するには私をフォローしてください

c6d2a8404df76b9683733af1b2893682.jpeg

8fe9aa38c8e60279e47594a42ad10d53.jpeg

おすすめ

転載: blog.csdn.net/c6E5UlI1N/article/details/132632518