iOS7.0.6アップデート

iPhoneを使っている人ならもう知っているかもしれませんがiOS7.0.6が配信されました. いつものアップデートなら少しの間様子見…なんてものいいかもしれませんが今回は少し様子がちがうようです.

iPhoneに限らず最近の端末は通信内容を暗号化するのが主流です. どのような通信が行われているかをのぞき見られた場合極めて危険なことになります.

暗号通信をするには当然ながら暗号化ソフトが必要になってきます. これを普段意識する人はネットワークエンジニアなどインフラ周りの仕事をしている人くらいでしょう.しかし極めて重要なソフトであり,通信周りの心臓部といっても過言ではありません.

今回のバグは暗号化されているはずの通信内容が全く暗号化されていないというバグを修正するアップデートのようです. 信じがたい話ですが本当のようです.

今回ばかりは一分一秒でも早くアップデートしてください.

問題のソースコード

static
OSStatusSSLVerifySignedServerKeyExchange(SSLContext *ctx, bool isRsa, SSLBuffer, signedParams,                                 
uint8_t *signature, UInt16 signatureLen)
{
    OSStatus        err;
    SSLBuffer       hashOut, hashCtx, clientRandom, serverRandom;
    uint8_t         hashes[SSL_SHA1_DIGEST_LEN + SSL_MD5_DIGEST_LEN];
    SSLBuffer       signedHashes;
    uint8_t         *dataToSign;
    size_t          dataToSignLen;

    signedHashes.data = 0;    
    hashCtx.data = 0;    
    clientRandom.data = ctx->clientRandom;
    clientRandom.length = SSL_CLIENT_SRVR_RAND_SIZE;    
    serverRandom.data = ctx->serverRandom;    
    serverRandom.length = SSL_CLIENT_SRVR_RAND_SIZE;        

    if(isRsa) {
      /* skip this if signing with DSA */                
      dataToSign = hashes;                
      dataToSignLen = SSL_SHA1_DIGEST_LEN + SSL_MD5_DIGEST_LEN;                
      hashOut.data = hashes;                
      hashOut.length = SSL_MD5_DIGEST_LEN;                                
      if ((err = ReadyHash(&SSLHashMD5, &hashCtx)) != 0)
        goto fail;                
      if ((err = SSLHashMD5.update(&hashCtx, &clientRandom)) != 0)
        goto fail;                
      if ((err = SSLHashMD5.update(&hashCtx, &serverRandom)) != 0)
        goto fail;                
      if ((err = SSLHashMD5.update(&hashCtx, &signedParams)) != 0)
        goto fail;                
      if ((err = SSLHashMD5.final(&hashCtx, &hashOut)) != 0)
        gotofail;        
      } else {
        /* DSA, ECDSA - just use the SHA1 hash */                
        dataToSign = &hashes[SSL_MD5_DIGEST_LEN];                
        dataToSignLen = SSL_SHA1_DIGEST_LEN;        
      }        
      hashOut.data = hashes + SSL_MD5_DIGEST_LEN;    
      hashOut.length = SSL_SHA1_DIGEST_LEN;    
      if ((err = SSLFreeBuffer(&hashCtx)) != 0)        
        goto fail;    
      if ((err = ReadyHash(&SSLHashSHA1, &hashCtx)) != 0)        
        goto fail;    
      if ((err = SSLHashSHA1.update(&hashCtx, &clientRandom)) != 0)        
        goto fail;    
      if ((err = SSLHashSHA1.update(&hashCtx, &serverRandom)) != 0)        
        goto fail;    
      if ((err = SSLHashSHA1.update(&hashCtx, &signedParams)) != 0)        
        goto fail;        
        goto fail;    
      if ((err = SSLHashSHA1.final(&hashCtx, &hashOut)) != 0)        
        goto fail;        

      err = sslRawVerify(ctx, 
        ctx->peerPubKey, 
        dataToSign,                              /* plaintext */                       dataToSignLen,                   /* plaintext length */                       signature,                       
        signatureLen);        

        if(err) {                
          sslErrorLog("SSLDecodeSignedServerKeyExchange: sslRawVerify " "returned %d\n", (int)err);           
          goto fail;
        }
fail:    
  SSLFreeBuffer(&signedHashes);    
  SSLFreeBuffer(&hashCtx);    
  return err;
}

の50行目付近,

goto fail;
goto fail;

をくり返している部分にあります.二つ目のgoto fail;はタイプミスで挿入されてしまったもののようです. このコードのすぐ上の

if ((err = SSLHashSHA1.update(&hashCtx, &signedParams)) != 0)

は SSLHashSHA1.update(…)という関数を実行し,戻り値をOSStatusというenum型で返します. OSStatusは内部的には単なるint32型でその名の通りOSの状態を表します.
OSStatus == 0の場合エラーなしで実行されたことを表します.0でなかったらなんらかのエラーが起きたことを表します.
OSStatusについてはこちらがわかりやすいかと思います.
http://blog.hi-farm.net/2010/12/26/ios-osstatus%E3%81%AE%E7%B5%90%E6%9E%9C%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6/

さて,C/C++言語では以下の二つのif文は同じものとして扱われます.問題の文とよく見比べてください.

if (foobar) {
 hogehoge(); // foobarが真の場合実行される.
}
mogamoga(); // foobarとは関係なく実行される.この場合無条件に実行される.

if (foobar)
 hogehoge(); // foobarが真の場合実行される.
 mogamoga(); // foobarとは関係なく実行される.この場合無条件に実行される.

もうお分かりかと思います.おそらくはエラー処理のためのgoto failなのに必ずgoto fail;が実行されています.これは推測なのですがSSL通信が使えなかったら緊急避難的に平文通信をするという機構が備わっていてそれが逆にまずかったという感じでしょうか.

問題のソースコード全体 http://opensource.apple.com/source/Security/Security-55471/libsecurity_ssl/lib/sslKeyExchange.c