ネットワークセキュリティサービスライブラリの確認



Network Security Services(NSS)は、セキュアなクライアントおよびサーバーアプリケーションのクロスプラットフォーム開発をサポートするように設計された一連のライブラリです。 このライブラリは、FirefoxおよびChromeブラウザーの暗号化に使用されます。最近、証明書の署名の検証に脆弱性が見つかったため、このプロジェクトも検討することにしました。



脆弱性に関する詳細。



ソースコードは、次のコマンドによって取得されました。 ライブラリはWindowsコンソールからアセンブルされているため、検証に特別なユーティリティPVS-Studio Standaloneを使用しました。これについては記事「 PVS-StudioがWindows上のビルドシステムとコンパイラをサポートするようになりました。 簡単ですぐに使用できます



検証結果



V547式 'dtype!= 2 || dtype!= 3 'は常に真です。 ここでは、おそらく「&&」演算子を使用する必要があります。 crlgen.c 1172

static SECStatus crlgen_setNextDataFn_field(...., unsigned short dtype) { .... if (dtype != CRLGEN_TYPE_DIGIT || //<== dtype != CRLGEN_TYPE_DIGIT_RANGE) { //<== crlgen_PrintError(crlGenData->parsedLineNum, "range value should have " "numeric or numeric range values.\n"); return SECFailure; } .... }
      
      





論理加算の真理値表から、少なくとも1つのオペランドがユニットである場合(この場合のように)、条件は常に真になります。



V567未定義の動作。 'j'変数は、シーケンスポイント間で2回使用されている間に変更されます。 pk11slot.c 1934

 PK11SlotList* PK11_GetAllTokens(....) { .... #if defined( XP_WIN32 ) waste[ j & 0xf] = j++; #endif .... }
      
      





変数 'j'は、同じ継承ポイントで2回使用されます。 その結果、そのような式の結果を予測することは不可能です。 詳細については、診断V567の説明を参照してください。



V575 NULLポインターが「fclose」関数に渡されます。 最初の引数を調べます。 certcgi.c 608

 static int get_serial_number(Pair *data) { FILE *serialFile; .... serialFile = fopen(filename, "r"); if (serialFile != NULL) { .... } else { fclose(serialFile); //<== .... } .... }
      
      





この場合、ファイルへのポインターがゼロであれば、ファイルを閉じる必要はありません。 しかし、それを閉じると、トラブルにつながる可能性があります。 正確に何が起こるかは、そのようなイベントのインストール済みハンドラーによって異なります(_set_invalid_parameter_handler()などを参照)。



V576形式が正しくありません 。 'fprintf'関数を呼び出すときに、異なる数の実引数が予期されます。 予想:3.現在:7. pp.c 34

 static void Usage(char *progName) { .... fprintf(stderr, "%-14s (Use either the long type name or " "the shortcut.)\n", "", SEC_CT_CERTIFICATE_ID, SEC_CT_PKCS7, SEC_CT_CRL, SEC_CT_NAME); .... }
      
      





書式指定子の数は、fprintf()に渡される引数の数と一致しません。



V595 nullptrに対して検証される前に、「buf」ポインターが使用されました。 行を確認してください:1709、1710。prtime.c 1709

 PR_IMPLEMENT(PRUint32) PR_FormatTime(....) { .... rv = strftime(buf, buflen, fmt, ap); if (!rv && buf && buflen > 0) { buf[0] = '\0'; } return rv; }
      
      





'buf'ポインタはゼロと等しいかどうかチェックされます。つまり、strftime()関数にNULLポインタを渡すと、前の行でエラーが発生する可能性があります。



V597コンパイラーは、「memset」関数呼び出しを削除できます。これは、「hashed_secret」バッファーをフラッシュするために使用されます。 RtlSecureZeroMemory()関数を使用して、プライベートデータを消去する必要があります。 alghmac.c 87

 #define PORT_Memset memset SECStatus HMAC_Init( HMACContext * cx, const SECHashObject *hash_obj, const unsigned char *secret, unsigned int secret_len, PRBool isFIPS) { .... PORT_Memset(hashed_secret, 0, sizeof hashed_secret); //<== if (cx->hash != NULL) cx->hashobj->destroy(cx->hash, PR_TRUE); return SECFailure; }
      
      





機密情報の処理を担当するコードの最も危険な場所。 'hashed_secret'配列は 'memset'関数を呼び出した後は使用されないため、コンパイラは最適化のために関数呼び出しを削除でき、配列は計画どおりにゼロにリセットされません。



これらはおそらく、見つかったすべての中で最も深刻なエラーです。



多くの場合、警告V597は依然として誤解されています。 問題の本質を明らかにする追加資料を提供します。 そのような場所すべてのリスト:

V610未定義の動作。 シフト演算子 '<<を確認してください。 左のオペランド '-1L'は負です。 inflate.c 1475

 long ZEXPORT inflateMark(strm) z_streamp strm; { struct inflate_state FAR *state; if (strm == Z_NULL || strm->state == Z_NULL) return -1L << 16; state = (struct inflate_state FAR *)strm->state; .... }
      
      





C ++ 11言語標準によれば、負の数をシフトすると未定義の動作が発生します。



別の同様の場所:

V555式「emLen-reservedLen-inputLen> 0」は、「emLen-reservedLen!= InputLen」として機能します。 rsapkcs.c 708

 #define PORT_Memset memset static SECStatus eme_oaep_encode(unsigned char * em, unsigned int emLen, const unsigned char * input, unsigned int inputLen, HASH_HashType hashAlg, HASH_HashType maskHashAlg, const unsigned char * label, unsigned int labelLen, const unsigned char * seed, unsigned int seedLen) { .... /* Step 2.b - Generate PS */ if (emLen - reservedLen - inputLen > 0) { PORT_Memset(em + 1 + (hash->length * 2), 0x00, emLen - reservedLen - inputLen); } .... }
      
      





正しい値とゼロ値に加えて、符号なしの数値の差の結果は、負の数値を符号なしの型に変換した結果、非常に大きな数値になる可能性もあります。 このフラグメントでは、誤ったジャイアント値がテスト条件を満たし、「memset」関数が膨大な量のメモリをゼロにしようとします。



ただし、このようなオーバーフローはまったく発生しない可能性があります。式で使用される変数の制限値を判断することは困難です。 しかし、チェックはいずれにしても非常に信頼性が低いです。



V677標準の「BYTE」型のカスタム宣言。 システムヘッダーファイル#include <WinDef.h>を使用する必要があります。 des.h 15

 typedef unsigned char BYTE;
      
      





最後に、システムファイルで既に定義されている型の宣言に関する簡単なメモ。



同様の場所:

もちろん、これは間違いではありません。 しかし、なぜそうするのですか?



おわりに



最近、個人データのセキュリティが特に注目されています。 ハッキングツールのような防御は人によって書かれ、人は間違いを犯しがちであることを忘れないでください。



静的分析を定期的に使用することで、より便利なタスクを解決するために多くの時間を節約できます。 また、コードの品質管理は、たとえば、新しいサービスを使用して、C / C ++コードの定期的な監査など、他のサービスに移行できます



この記事は英語です。



英語を話す聴衆とこの記事を共有したい場合は、翻訳へのリンクを使用してください:Svyatoslav Razmyslov。 ネットワークセキュリティサービスライブラリの分析



記事を読んで質問がありますか?
多くの場合、記事には同じ質問が寄せられます。 ここでそれらに対する回答を収集しました: PVS-StudioおよびCppCatバージョン2014に関する記事の読者からの質問への回答 。 リストをご覧ください。




All Articles