s3: Fix an uninitialized variable read

Found by Laurent Gaffie <laurent.gaffie@gmail.com>

Thanks for that,

Volker

Fix bug #7254 (An uninitialized variable read could cause an smbd crash).

static void reply_sesssetup_and_X_spnego(struct smb_request *req)
{
	const uint8 *p;
	DATA_BLOB blob1;
	size_t bufrem;
	char *tmp;
	const char *native_os;
	const char *native_lanman;
	const char *primary_domain;
	const char *p2;
	uint16 data_blob_len = SVAL(req->vwv+7, 0);
	enum remote_arch_types ra_type = get_remote_arch();
	int vuid = req->vuid;
	user_struct *vuser = NULL;
	NTSTATUS status = NT_STATUS_OK;
	uint16 smbpid = req->smbpid;
	struct smbd_server_connection *sconn = smbd_server_conn;

	DEBUG(3,("Doing spnego session setup\n"));

	if (global_client_caps == 0) {
		global_client_caps = IVAL(req->vwv+10, 0);

		if (!(global_client_caps & CAP_STATUS32)) {
			remove_from_common_flags2(FLAGS2_32_BIT_ERROR_CODES);
		}

	}

	p = req->buf;

	if (data_blob_len == 0) {
		/* an invalid request */
		reply_nterror(req, nt_status_squash(NT_STATUS_LOGON_FAILURE));
		return;
	}

	bufrem = smbreq_bufrem(req, p);
	/* pull the spnego blob */
	blob1 = data_blob(p, MIN(bufrem, data_blob_len));

#if 0
        file_save("negotiate.dat", blob1.data, blob1.length);
 #endif

       p2 = (char *)req->buf + data_blob_len;

        p2 += srvstr_pull_req_talloc(talloc_tos(), req, &tmp, p2,
                                     STR_TERMINATE);
	native_os = tmp ? tmp : "";

	p2 += srvstr_pull_req_talloc(talloc_tos(), req, &tmp, p2,
				     STR_TERMINATE);
	native_lanman = tmp ? tmp : "";

	p2 += srvstr_pull_req_talloc(talloc_tos(), req, &tmp, p2,
				     STR_TERMINATE);
	primary_domain = tmp ? tmp : "";

	DEBUG(3,("NativeOS=[%s] NativeLanMan=[%s] PrimaryDomain=[%s]\n",
		native_os, native_lanman, primary_domain));

	if ( ra_type == RA_WIN2K ) {
		/* Vista sets neither the OS or lanman strings */

		if ( !strlen(native_os) && !strlen(native_lanman) )
			set_remote_arch(RA_VISTA);

		/* Windows 2003 doesn't set the native lanman string,
		   but does set primary domain which is a bug I think */

		if ( !strlen(native_lanman) ) {
			ra_lanman_string( primary_domain );
		} else {
			ra_lanman_string( native_lanman );
		}
	}

	/* Did we get a valid vuid ? */
	if (!is_partial_auth_vuid(sconn, vuid)) {
		/* No, then try and see if this is an intermediate sessionsetup
		 * for a large SPNEGO packet. */
		struct pending_auth_data *pad;
		pad = get_pending_auth_data(sconn, smbpid);
		if (pad) {
			DEBUG(10,("reply_sesssetup_and_X_spnego: found "
				"pending vuid %u\n",
				(unsigned int)pad->vuid ));
			vuid = pad->vuid;
		}
	}

	/* Do we have a valid vuid now ? */
	if (!is_partial_auth_vuid(sconn, vuid)) {
		/* No, start a new authentication setup. */
		vuid = register_initial_vuid(sconn);
		if (vuid == UID_FIELD_INVALID) {
			data_blob_free(&blob1);
			reply_nterror(req, nt_status_squash(
					      NT_STATUS_INVALID_PARAMETER));
			return;
		}
	}

	vuser = get_partial_auth_user_struct(sconn, vuid);
	/* This MUST be valid. */
	if (!vuser) {
		smb_panic("reply_sesssetup_and_X_spnego: invalid vuid.");
	}

	/* Large (greater than 4k) SPNEGO blobs are split into multiple
	 * sessionsetup requests as the Windows limit on the security blob
	 * field is 4k. Bug #4400. JRA.
	 */

	status = check_spnego_blob_complete(sconn, smbpid, vuid, &blob1);
	if (!NT_STATUS_IS_OK(status)) {
		if (!NT_STATUS_EQUAL(status,
				NT_STATUS_MORE_PROCESSING_REQUIRED)) {
			/* Real error - kill the intermediate vuid */
			invalidate_vuid(sconn, vuid);
		}
		data_blob_free(&blob1);
		reply_nterror(req, nt_status_squash(status));
		return;
	}

	if (blob1.data[0] == ASN1_APPLICATION(0)) {

		/* its a negTokenTarg packet */

		reply_spnego_negotiate(req, vuid, blob1,
				       &vuser->auth_ntlmssp_state);
		data_blob_free(&blob1);
		return;
	}

	if (blob1.data[0] == ASN1_CONTEXT(1)) {

		/* its a auth packet */

		reply_spnego_auth(req, vuid, blob1,
				  &vuser->auth_ntlmssp_state);
		data_blob_free(&blob1);
		return;
	}

	if (strncmp((char *)(blob1.data), "NTLMSSP", 7) == 0) {
		DATA_BLOB chal;

		if (!vuser->auth_ntlmssp_state) {
			status = auth_ntlmssp_start(&vuser->auth_ntlmssp_state);
			if (!NT_STATUS_IS_OK(status)) {
				/* Kill the intermediate vuid */
				invalidate_vuid(sconn, vuid);
				data_blob_free(&blob1);
				reply_nterror(req, nt_status_squash(status));
				return;
			}
		}

		status = auth_ntlmssp_update(vuser->auth_ntlmssp_state,
						blob1, &chal);

		data_blob_free(&blob1);

		reply_spnego_ntlmssp(req, vuid,
				     &vuser->auth_ntlmssp_state,
				     &chal, status, OID_NTLMSSP, false);
		data_blob_free(&chal);
		return;
	}

	/* what sort of packet is this? */
	DEBUG(1,("Unknown packet in reply_sesssetup_and_X_spnego\n"));

	data_blob_free(&blob1);

	reply_nterror(req, nt_status_squash(NT_STATUS_LOGON_FAILURE));
}

The reply_sesssetup_and_X_spnego function in sesssetup.c in smbd in Samba before 3.4.8 and 3.5.x before 3.5.2 allows remote attackers to trigger an out-of-bounds read, and cause a denial of service (process crash), via a \xff\xff security blob length in a Session Setup AndX request.


static void reply_sesssetup_and_X_spnego(struct smb_request *req)
{
	const uint8 *p;
	DATA_BLOB blob1;
	size_t bufrem;
	char *tmp;
	const char *native_os;
	const char *native_lanman;
	const char *primary_domain;
	const char *p2;
	uint16 data_blob_len = SVAL(req->vwv+7, 0);
	enum remote_arch_types ra_type = get_remote_arch();
	int vuid = req->vuid;
	user_struct *vuser = NULL;
	NTSTATUS status = NT_STATUS_OK;
	uint16 smbpid = req->smbpid;
	struct smbd_server_connection *sconn = smbd_server_conn;

	DEBUG(3,("Doing spnego session setup\n"));

	if (global_client_caps == 0) {
		global_client_caps = IVAL(req->vwv+10, 0);

		if (!(global_client_caps & CAP_STATUS32)) {
			remove_from_common_flags2(FLAGS2_32_BIT_ERROR_CODES);
		}

	}

	p = req->buf;

	if (data_blob_len == 0) {
		/* an invalid request */
		reply_nterror(req, nt_status_squash(NT_STATUS_LOGON_FAILURE));
		return;
	}

	bufrem = smbreq_bufrem(req, p);
	/* pull the spnego blob */
	blob1 = data_blob(p, MIN(bufrem, data_blob_len));

#if 0
        file_save("negotiate.dat", blob1.data, blob1.length);
 #endif

       p2 = (char *)req->buf + blob1.length;

        p2 += srvstr_pull_req_talloc(talloc_tos(), req, &tmp, p2,
                                     STR_TERMINATE);
	native_os = tmp ? tmp : "";

	p2 += srvstr_pull_req_talloc(talloc_tos(), req, &tmp, p2,
				     STR_TERMINATE);
	native_lanman = tmp ? tmp : "";

	p2 += srvstr_pull_req_talloc(talloc_tos(), req, &tmp, p2,
				     STR_TERMINATE);
	primary_domain = tmp ? tmp : "";

	DEBUG(3,("NativeOS=[%s] NativeLanMan=[%s] PrimaryDomain=[%s]\n",
		native_os, native_lanman, primary_domain));

	if ( ra_type == RA_WIN2K ) {
		/* Vista sets neither the OS or lanman strings */

		if ( !strlen(native_os) && !strlen(native_lanman) )
			set_remote_arch(RA_VISTA);

		/* Windows 2003 doesn't set the native lanman string,
		   but does set primary domain which is a bug I think */

		if ( !strlen(native_lanman) ) {
			ra_lanman_string( primary_domain );
		} else {
			ra_lanman_string( native_lanman );
		}
	}

	/* Did we get a valid vuid ? */
	if (!is_partial_auth_vuid(sconn, vuid)) {
		/* No, then try and see if this is an intermediate sessionsetup
		 * for a large SPNEGO packet. */
		struct pending_auth_data *pad;
		pad = get_pending_auth_data(sconn, smbpid);
		if (pad) {
			DEBUG(10,("reply_sesssetup_and_X_spnego: found "
				"pending vuid %u\n",
				(unsigned int)pad->vuid ));
			vuid = pad->vuid;
		}
	}

	/* Do we have a valid vuid now ? */
	if (!is_partial_auth_vuid(sconn, vuid)) {
		/* No, start a new authentication setup. */
		vuid = register_initial_vuid(sconn);
		if (vuid == UID_FIELD_INVALID) {
			data_blob_free(&blob1);
			reply_nterror(req, nt_status_squash(
					      NT_STATUS_INVALID_PARAMETER));
			return;
		}
	}

	vuser = get_partial_auth_user_struct(sconn, vuid);
	/* This MUST be valid. */
	if (!vuser) {
		smb_panic("reply_sesssetup_and_X_spnego: invalid vuid.");
	}

	/* Large (greater than 4k) SPNEGO blobs are split into multiple
	 * sessionsetup requests as the Windows limit on the security blob
	 * field is 4k. Bug #4400. JRA.
	 */

	status = check_spnego_blob_complete(sconn, smbpid, vuid, &blob1);
	if (!NT_STATUS_IS_OK(status)) {
		if (!NT_STATUS_EQUAL(status,
				NT_STATUS_MORE_PROCESSING_REQUIRED)) {
			/* Real error - kill the intermediate vuid */
			invalidate_vuid(sconn, vuid);
		}
		data_blob_free(&blob1);
		reply_nterror(req, nt_status_squash(status));
		return;
	}

	if (blob1.data[0] == ASN1_APPLICATION(0)) {

		/* its a negTokenTarg packet */

		reply_spnego_negotiate(req, vuid, blob1,
				       &vuser->auth_ntlmssp_state);
		data_blob_free(&blob1);
		return;
	}

	if (blob1.data[0] == ASN1_CONTEXT(1)) {

		/* its a auth packet */

		reply_spnego_auth(req, vuid, blob1,
				  &vuser->auth_ntlmssp_state);
		data_blob_free(&blob1);
		return;
	}

	if (strncmp((char *)(blob1.data), "NTLMSSP", 7) == 0) {
		DATA_BLOB chal;

		if (!vuser->auth_ntlmssp_state) {
			status = auth_ntlmssp_start(&vuser->auth_ntlmssp_state);
			if (!NT_STATUS_IS_OK(status)) {
				/* Kill the intermediate vuid */
				invalidate_vuid(sconn, vuid);
				data_blob_free(&blob1);
				reply_nterror(req, nt_status_squash(status));
				return;
			}
		}

		status = auth_ntlmssp_update(vuser->auth_ntlmssp_state,
						blob1, &chal);

		data_blob_free(&blob1);

		reply_spnego_ntlmssp(req, vuid,
				     &vuser->auth_ntlmssp_state,
				     &chal, status, OID_NTLMSSP, false);
		data_blob_free(&chal);
		return;
	}

	/* what sort of packet is this? */
	DEBUG(1,("Unknown packet in reply_sesssetup_and_X_spnego\n"));

	data_blob_free(&blob1);

	reply_nterror(req, nt_status_squash(NT_STATUS_LOGON_FAILURE));
}


_asn1_extract_der_octet (asn1_node node, const unsigned char *der,
			 int der_len, unsigned flags)
{
  int len2, len3;
  int counter, counter_end;
  int result;

  len2 = asn1_get_length_der (der, der_len, &len3);
  if (len2 < -1)
    return ASN1_DER_ERROR;

  counter = len3 + 1;
   DECR_LEN(der_len, len3);

   if (len2 == -1)
    counter_end = der_len - 2;
   else
     counter_end = der_len;

   while (counter < counter_end)
     {
       DECR_LEN(der_len, 1);

      if (len2 >= 0)
	{
	  DECR_LEN(der_len, len2+len3);
	  _asn1_append_value (node, der + counter + len3, len2);
	}
      else
	{			/* indefinite */
	  DECR_LEN(der_len, len3);
	  result =
	    _asn1_extract_der_octet (node, der + counter + len3,
				     der_len, flags);
	  if (result != ASN1_SUCCESS)
	    return result;
	  len2 = 0;
	}

      counter += len2 + len3 + 1;
    }

  return ASN1_SUCCESS;

cleanup:
  return result;
}

The _asn1_extract_der_octet function in lib/decoding.c in GNU Libtasn1 before 4.8, when used without the ASN1_DECODE_FLAG_STRICT_DER flag, allows remote attackers to cause a denial of service (infinite recursion) via a crafted certificate.


_asn1_extract_der_octet (asn1_node node, const unsigned char *der,
			 int der_len, unsigned flags)
{
  int len2, len3;
  int counter, counter_end;
  int result;

  len2 = asn1_get_length_der (der, der_len, &len3);
  if (len2 < -1)
    return ASN1_DER_ERROR;

  counter = len3 + 1;
   DECR_LEN(der_len, len3);

   if (len2 == -1)
    {
      if (der_len < 2)
        return ASN1_DER_ERROR;
      counter_end = der_len - 2;
    }
   else
     counter_end = der_len;

  if (counter_end < counter)
    return ASN1_DER_ERROR;

   while (counter < counter_end)
     {
       DECR_LEN(der_len, 1);

      if (len2 >= 0)
	{
	  DECR_LEN(der_len, len2+len3);
	  _asn1_append_value (node, der + counter + len3, len2);
	}
      else
	{			/* indefinite */
	  DECR_LEN(der_len, len3);
	  result =
	    _asn1_extract_der_octet (node, der + counter + len3,
				     der_len, flags);
	  if (result != ASN1_SUCCESS)
	    return result;
	  len2 = 0;
	}

      counter += len2 + len3 + 1;
    }

  return ASN1_SUCCESS;

cleanup:
  return result;
}


load(ImlibImage * im, ImlibProgressFunction progress, char progress_granularity,
     char immediate_load)
{
   static const int    intoffset[] = { 0, 4, 2, 1 };
   static const int    intjump[] = { 8, 8, 4, 2 };
   int                 rc;
   DATA32             *ptr;
   GifFileType        *gif;
   GifRowType         *rows;
   GifRecordType       rec;
   ColorMapObject     *cmap;
   int                 i, j, done, bg, r, g, b, w = 0, h = 0;
   float               per = 0.0, per_inc;
   int                 last_per = 0, last_y = 0;
   int                 transp;
   int                 fd;

   done = 0;
   rows = NULL;
   transp = -1;

   /* if immediate_load is 1, then dont delay image laoding as below, or */
   /* already data in this image - dont load it again */
   if (im->data)
      return 0;

   fd = open(im->real_file, O_RDONLY);
   if (fd < 0)
      return 0;

#if GIFLIB_MAJOR >= 5
   gif = DGifOpenFileHandle(fd, NULL);
#else
   gif = DGifOpenFileHandle(fd);
#endif
   if (!gif)
     {
        close(fd);
        return 0;
     }

   rc = 0;                      /* Failure */

   do
     {
        if (DGifGetRecordType(gif, &rec) == GIF_ERROR)
          {
             /* PrintGifError(); */
             rec = TERMINATE_RECORD_TYPE;
          }
        if ((rec == IMAGE_DESC_RECORD_TYPE) && (!done))
          {
             if (DGifGetImageDesc(gif) == GIF_ERROR)
               {
                  /* PrintGifError(); */
                  rec = TERMINATE_RECORD_TYPE;
                  break;
               }
             w = gif->Image.Width;
             h = gif->Image.Height;
             if (!IMAGE_DIMENSIONS_OK(w, h))
                goto quit2;

             rows = calloc(h, sizeof(GifRowType *));
             if (!rows)
                goto quit2;

             for (i = 0; i < h; i++)
               {
                  rows[i] = calloc(w, sizeof(GifPixelType));
                  if (!rows[i])
                     goto quit;
               }

             if (gif->Image.Interlace)
               {
                  for (i = 0; i < 4; i++)
                    {
                       for (j = intoffset[i]; j < h; j += intjump[i])
                         {
                            DGifGetLine(gif, rows[j], w);
                         }
                    }
               }
             else
               {
                  for (i = 0; i < h; i++)
                    {
                       DGifGetLine(gif, rows[i], w);
                    }
               }
             done = 1;
          }
        else if (rec == EXTENSION_RECORD_TYPE)
          {
             int                 ext_code;
             GifByteType        *ext;

             ext = NULL;
             DGifGetExtension(gif, &ext_code, &ext);
             while (ext)
               {
                  if ((ext_code == 0xf9) && (ext[1] & 1) && (transp < 0))
                    {
                       transp = (int)ext[4];
                    }
                  ext = NULL;
                  DGifGetExtensionNext(gif, &ext);
               }
          }
     }
   while (rec != TERMINATE_RECORD_TYPE);

   if (transp >= 0)
     {
        SET_FLAG(im->flags, F_HAS_ALPHA);
     }
   else
     {
        UNSET_FLAG(im->flags, F_HAS_ALPHA);
     }
   if (!rows)
     {
        goto quit2;
     }

   /* set the format string member to the lower-case full extension */
   /* name for the format - so example names would be: */
   /* "png", "jpeg", "tiff", "ppm", "pgm", "pbm", "gif", "xpm" ... */
   im->w = w;
   im->h = h;
   if (!im->format)
      im->format = strdup("gif");

    if (im->loader || immediate_load || progress)
      {
         bg = gif->SBackGroundColor;
         cmap = (gif->Image.ColorMap ? gif->Image.ColorMap : gif->SColorMap);
         im->data = (DATA32 *) malloc(sizeof(DATA32) * w * h);
         if (!im->data)
            goto quit;
                    {
                       r = cmap->Colors[bg].Red;
                       g = cmap->Colors[bg].Green;
                       b = cmap->Colors[bg].Blue;
                       *ptr++ = 0x00ffffff & ((r << 16) | (g << 8) | b);
                    }
                  else
                    {
                       r = cmap->Colors[rows[i][j]].Red;
                       g = cmap->Colors[rows[i][j]].Green;
                       b = cmap->Colors[rows[i][j]].Blue;
                       *ptr++ = (0xff << 24) | (r << 16) | (g << 8) | b;
           {
              for (j = 0; j < w; j++)
                {
                  if (rows[i][j] == transp)
                    {
                       r = cmap->Colors[bg].Red;
                       g = cmap->Colors[bg].Green;
                       b = cmap->Colors[bg].Blue;
                       *ptr++ = 0x00ffffff & ((r << 16) | (g << 8) | b);
                    }
                  else
                    {
                       r = cmap->Colors[rows[i][j]].Red;
                       g = cmap->Colors[rows[i][j]].Green;
                       b = cmap->Colors[rows[i][j]].Blue;
                       *ptr++ = (0xff << 24) | (r << 16) | (g << 8) | b;
                    }
                   per += per_inc;
                   if (progress && (((int)per) != last_per)
                       && (((int)per) % progress_granularity == 0))
                         {
                            rc = 2;
                            goto quit;
                         }
                       last_y = i;
                    }
               }
          }

      finish:
        if (progress)
           progress(im, 100, 0, last_y, w, h);
     }

   rc = 1;                      /* Success */

 quit:
   for (i = 0; i < h; i++)
      free(rows[i]);
   free(rows);

 quit2:
#if GIFLIB_MAJOR > 5 || (GIFLIB_MAJOR == 5 && GIFLIB_MINOR >= 1)
   DGifCloseFile(gif, NULL);
#else
   DGifCloseFile(gif);
#endif

   return rc;
}

The GIF loader in imlib2 before 1.4.9 allows remote attackers to cause a denial of service (application crash) or obtain sensitive information via a crafted image, which triggers an out-of-bounds read.


load(ImlibImage * im, ImlibProgressFunction progress, char progress_granularity,
     char immediate_load)
{
   static const int    intoffset[] = { 0, 4, 2, 1 };
   static const int    intjump[] = { 8, 8, 4, 2 };
   int                 rc;
   DATA32             *ptr;
   GifFileType        *gif;
   GifRowType         *rows;
   GifRecordType       rec;
   ColorMapObject     *cmap;
   int                 i, j, done, bg, r, g, b, w = 0, h = 0;
   float               per = 0.0, per_inc;
   int                 last_per = 0, last_y = 0;
   int                 transp;
   int                 fd;

   done = 0;
   rows = NULL;
   transp = -1;

   /* if immediate_load is 1, then dont delay image laoding as below, or */
   /* already data in this image - dont load it again */
   if (im->data)
      return 0;

   fd = open(im->real_file, O_RDONLY);
   if (fd < 0)
      return 0;

#if GIFLIB_MAJOR >= 5
   gif = DGifOpenFileHandle(fd, NULL);
#else
   gif = DGifOpenFileHandle(fd);
#endif
   if (!gif)
     {
        close(fd);
        return 0;
     }

   rc = 0;                      /* Failure */

   do
     {
        if (DGifGetRecordType(gif, &rec) == GIF_ERROR)
          {
             /* PrintGifError(); */
             rec = TERMINATE_RECORD_TYPE;
          }
        if ((rec == IMAGE_DESC_RECORD_TYPE) && (!done))
          {
             if (DGifGetImageDesc(gif) == GIF_ERROR)
               {
                  /* PrintGifError(); */
                  rec = TERMINATE_RECORD_TYPE;
                  break;
               }
             w = gif->Image.Width;
             h = gif->Image.Height;
             if (!IMAGE_DIMENSIONS_OK(w, h))
                goto quit2;

             rows = calloc(h, sizeof(GifRowType *));
             if (!rows)
                goto quit2;

             for (i = 0; i < h; i++)
               {
                  rows[i] = calloc(w, sizeof(GifPixelType));
                  if (!rows[i])
                     goto quit;
               }

             if (gif->Image.Interlace)
               {
                  for (i = 0; i < 4; i++)
                    {
                       for (j = intoffset[i]; j < h; j += intjump[i])
                         {
                            DGifGetLine(gif, rows[j], w);
                         }
                    }
               }
             else
               {
                  for (i = 0; i < h; i++)
                    {
                       DGifGetLine(gif, rows[i], w);
                    }
               }
             done = 1;
          }
        else if (rec == EXTENSION_RECORD_TYPE)
          {
             int                 ext_code;
             GifByteType        *ext;

             ext = NULL;
             DGifGetExtension(gif, &ext_code, &ext);
             while (ext)
               {
                  if ((ext_code == 0xf9) && (ext[1] & 1) && (transp < 0))
                    {
                       transp = (int)ext[4];
                    }
                  ext = NULL;
                  DGifGetExtensionNext(gif, &ext);
               }
          }
     }
   while (rec != TERMINATE_RECORD_TYPE);

   if (transp >= 0)
     {
        SET_FLAG(im->flags, F_HAS_ALPHA);
     }
   else
     {
        UNSET_FLAG(im->flags, F_HAS_ALPHA);
     }
   if (!rows)
     {
        goto quit2;
     }

   /* set the format string member to the lower-case full extension */
   /* name for the format - so example names would be: */
   /* "png", "jpeg", "tiff", "ppm", "pgm", "pbm", "gif", "xpm" ... */
   im->w = w;
   im->h = h;
   if (!im->format)
      im->format = strdup("gif");

    if (im->loader || immediate_load || progress)
      {
        DATA32 colormap[256];

         bg = gif->SBackGroundColor;
         cmap = (gif->Image.ColorMap ? gif->Image.ColorMap : gif->SColorMap);
        memset (colormap, 0, sizeof(colormap));
        if (cmap != NULL)
           {
              for (i = cmap->ColorCount > 256 ? 256 : cmap->ColorCount; i-- > 0;)
                 {
                    r = cmap->Colors[i].Red;
                    g = cmap->Colors[i].Green;
                    b = cmap->Colors[i].Blue;
                    colormap[i] = (0xff << 24) | (r << 16) | (g << 8) | b;
                 }
              /* if bg > cmap->ColorCount, it is transparent black already */
              if (transp >= 0 && transp < 256)
                 colormap[transp] = bg >= 0 && bg < 256 ? colormap[bg] & 0x00ffffff : 0x00000000;
           }
         im->data = (DATA32 *) malloc(sizeof(DATA32) * w * h);
         if (!im->data)
            goto quit;
                    {
                       r = cmap->Colors[bg].Red;
                       g = cmap->Colors[bg].Green;
                       b = cmap->Colors[bg].Blue;
                       *ptr++ = 0x00ffffff & ((r << 16) | (g << 8) | b);
                    }
                  else
                    {
                       r = cmap->Colors[rows[i][j]].Red;
                       g = cmap->Colors[rows[i][j]].Green;
                       b = cmap->Colors[rows[i][j]].Blue;
                       *ptr++ = (0xff << 24) | (r << 16) | (g << 8) | b;
           {
              for (j = 0; j < w; j++)
                {
                  *ptr++ = colormap[rows[i][j]];
                   per += per_inc;
                   if (progress && (((int)per) != last_per)
                       && (((int)per) % progress_granularity == 0))
                         {
                            rc = 2;
                            goto quit;
                         }
                       last_y = i;
                    }
               }
          }

      finish:
        if (progress)
           progress(im, 100, 0, last_y, w, h);
     }

   rc = 1;                      /* Success */

 quit:
   for (i = 0; i < h; i++)
      free(rows[i]);
   free(rows);

 quit2:
#if GIFLIB_MAJOR > 5 || (GIFLIB_MAJOR == 5 && GIFLIB_MINOR >= 1)
   DGifCloseFile(gif, NULL);
#else
   DGifCloseFile(gif);
#endif

   return rc;
}


__imlib_MergeUpdate(ImlibUpdate * u, int w, int h, int hgapmax)
{
   ImlibUpdate        *nu = NULL, *uu;
   struct _tile       *t;
   int                 tw, th, x, y, i;
   int                *gaps = NULL;

   /* if theres no rects to process.. return NULL */
   if (!u)
      return NULL;
   tw = w >> TB;
   if (w & TM)
      tw++;
   th = h >> TB;
   if (h & TM)
      th++;
   t = malloc(tw * th * sizeof(struct _tile));
   /* fill in tiles to be all not used */
   for (i = 0, y = 0; y < th; y++)
     {
        for (x = 0; x < tw; x++)
           t[i++].used = T_UNUSED;
     }
   /* fill in all tiles */
   for (uu = u; uu; uu = uu->next)
     {
        CLIP(uu->x, uu->y, uu->w, uu->h, 0, 0, w, h);
        for (y = uu->y >> TB; y <= ((uu->y + uu->h - 1) >> TB); y++)
          {
             for (x = uu->x >> TB; x <= ((uu->x + uu->w - 1) >> TB); x++)
                T(x, y).used = T_USED;
          }
     }
   /* scan each line - if > hgapmax gaps between tiles, then fill smallest */
   gaps = malloc(tw * sizeof(int));
   for (y = 0; y < th; y++)
     {
        int                 hgaps = 0, start = -1, min;
        char                have = 1, gap = 0;

        for (x = 0; x < tw; x++)
           gaps[x] = 0;
        for (x = 0; x < tw; x++)
          {
             if ((have) && (T(x, y).used == T_UNUSED))
               {
                  start = x;
                  gap = 1;
                  have = 0;
               }
             else if ((!have) && (gap) && (T(x, y).used & T_USED))
               {
                  gap = 0;
                  hgaps++;
                  have = 1;
                  gaps[start] = x - start;
               }
             else if (T(x, y).used & T_USED)
                have = 1;
          }
        while (hgaps > hgapmax)
          {
             start = -1;
             min = tw;

             for (x = 0; x < tw; x++)
               {
                  if ((gaps[x] > 0) && (gaps[x] < min))
                    {
                       start = x;
                       min = gaps[x];
                    }
               }
             if (start >= 0)
               {
                  gaps[start] = 0;
                  for (x = start;
                       T(x, y).used == T_UNUSED; T(x++, y).used = T_USED);
                  hgaps--;
               }
          }
     }
   free(gaps);
   /* coalesce tiles into larger blocks and make new rect list */
   for (y = 0; y < th; y++)
     {
        for (x = 0; x < tw; x++)
          {
             if (T(x, y).used & T_USED)
               {
                   int                 xx, yy, ww, hh, ok, xww;

                   for (xx = x + 1, ww = 1;
                       (T(xx, y).used & T_USED) && (xx < tw); xx++, ww++);
                   xww = x + ww;
                   for (yy = y + 1, hh = 1, ok = 1;
                        (yy < th) && (ok); yy++, hh++)
                    {
                       for (xx = x; xx < xww; xx++)
                         {
                            if (!(T(xx, yy).used & T_USED))
                              {
                                 ok = 0;
                                 hh--;
                                 break;
                              }
                         }
                    }
                  for (yy = y; yy < (y + hh); yy++)
                    {
                       for (xx = x; xx < xww; xx++)
                          T(xx, yy).used = T_UNUSED;
                    }
                  nu = __imlib_AddUpdate(nu, (x << TB), (y << TB),
                                         (ww << TB), (hh << TB));
               }
          }
     }
   free(t);
   __imlib_FreeUpdates(u);
   return nu;
}

Off-by-one error in the __imlib_MergeUpdate function in lib/updates.c in imlib2 before 1.4.9 allows remote attackers to cause a denial of service (out-of-bounds read and application crash) via crafted coordinates.


__imlib_MergeUpdate(ImlibUpdate * u, int w, int h, int hgapmax)
{
   ImlibUpdate        *nu = NULL, *uu;
   struct _tile       *t;
   int                 tw, th, x, y, i;
   int                *gaps = NULL;

   /* if theres no rects to process.. return NULL */
   if (!u)
      return NULL;
   tw = w >> TB;
   if (w & TM)
      tw++;
   th = h >> TB;
   if (h & TM)
      th++;
   t = malloc(tw * th * sizeof(struct _tile));
   /* fill in tiles to be all not used */
   for (i = 0, y = 0; y < th; y++)
     {
        for (x = 0; x < tw; x++)
           t[i++].used = T_UNUSED;
     }
   /* fill in all tiles */
   for (uu = u; uu; uu = uu->next)
     {
        CLIP(uu->x, uu->y, uu->w, uu->h, 0, 0, w, h);
        for (y = uu->y >> TB; y <= ((uu->y + uu->h - 1) >> TB); y++)
          {
             for (x = uu->x >> TB; x <= ((uu->x + uu->w - 1) >> TB); x++)
                T(x, y).used = T_USED;
          }
     }
   /* scan each line - if > hgapmax gaps between tiles, then fill smallest */
   gaps = malloc(tw * sizeof(int));
   for (y = 0; y < th; y++)
     {
        int                 hgaps = 0, start = -1, min;
        char                have = 1, gap = 0;

        for (x = 0; x < tw; x++)
           gaps[x] = 0;
        for (x = 0; x < tw; x++)
          {
             if ((have) && (T(x, y).used == T_UNUSED))
               {
                  start = x;
                  gap = 1;
                  have = 0;
               }
             else if ((!have) && (gap) && (T(x, y).used & T_USED))
               {
                  gap = 0;
                  hgaps++;
                  have = 1;
                  gaps[start] = x - start;
               }
             else if (T(x, y).used & T_USED)
                have = 1;
          }
        while (hgaps > hgapmax)
          {
             start = -1;
             min = tw;

             for (x = 0; x < tw; x++)
               {
                  if ((gaps[x] > 0) && (gaps[x] < min))
                    {
                       start = x;
                       min = gaps[x];
                    }
               }
             if (start >= 0)
               {
                  gaps[start] = 0;
                  for (x = start;
                       T(x, y).used == T_UNUSED; T(x++, y).used = T_USED);
                  hgaps--;
               }
          }
     }
   free(gaps);
   /* coalesce tiles into larger blocks and make new rect list */
   for (y = 0; y < th; y++)
     {
        for (x = 0; x < tw; x++)
          {
             if (T(x, y).used & T_USED)
               {
                   int                 xx, yy, ww, hh, ok, xww;

                   for (xx = x + 1, ww = 1;
                       (xx < tw) && (T(xx, y).used & T_USED); xx++, ww++);
                   xww = x + ww;
                   for (yy = y + 1, hh = 1, ok = 1;
                        (yy < th) && (ok); yy++, hh++)
                    {
                       for (xx = x; xx < xww; xx++)
                         {
                            if (!(T(xx, yy).used & T_USED))
                              {
                                 ok = 0;
                                 hh--;
                                 break;
                              }
                         }
                    }
                  for (yy = y; yy < (y + hh); yy++)
                    {
                       for (xx = x; xx < xww; xx++)
                          T(xx, yy).used = T_UNUSED;
                    }
                  nu = __imlib_AddUpdate(nu, (x << TB), (y << TB),
                                         (ww << TB), (hh << TB));
               }
          }
     }
   free(t);
   __imlib_FreeUpdates(u);
   return nu;
}


_gnutls_ciphertext2compressed (gnutls_session_t session,
                               opaque * compress_data,
                               int compress_size,
                               gnutls_datum_t ciphertext, uint8_t type,
                               record_parameters_st * params)
{
  uint8_t MAC[MAX_HASH_SIZE];
  uint16_t c_length;
  uint8_t pad;
  int length;
  uint16_t blocksize;
  int ret, i, pad_failed = 0;
  opaque preamble[PREAMBLE_SIZE];
  int preamble_size;
  int ver = gnutls_protocol_get_version (session);
  int hash_size = _gnutls_hash_get_algo_len (params->mac_algorithm);

  blocksize = gnutls_cipher_get_block_size (params->cipher_algorithm);

  /* actual decryption (inplace)
   */
  switch (_gnutls_cipher_is_block (params->cipher_algorithm))
    {
    case CIPHER_STREAM:
      if ((ret =
           _gnutls_cipher_decrypt (&params->read.cipher_state,
                                   ciphertext.data, ciphertext.size)) < 0)
        {
          gnutls_assert ();
          return ret;
        }

      length = ciphertext.size - hash_size;

      break;
    case CIPHER_BLOCK:
      if ((ciphertext.size < blocksize) || (ciphertext.size % blocksize != 0))
        {
          gnutls_assert ();
          return GNUTLS_E_DECRYPTION_FAILED;
        }

      if ((ret =
           _gnutls_cipher_decrypt (&params->read.cipher_state,
                                   ciphertext.data, ciphertext.size)) < 0)
        {
          gnutls_assert ();
          return ret;
        }

      /* ignore the IV in TLS 1.1.
       */
      if (_gnutls_version_has_explicit_iv
          (session->security_parameters.version))
         {
           ciphertext.size -= blocksize;
           ciphertext.data += blocksize;
          if (ciphertext.size == 0)
            {
              gnutls_assert ();
              return GNUTLS_E_DECRYPTION_FAILED;
            }
         }

       pad = ciphertext.data[ciphertext.size - 1] + 1;   /* pad */

       if ((int) pad > (int) ciphertext.size - hash_size)
      if ((int) pad > (int) ciphertext.size - hash_size)
        {
          gnutls_assert ();
          _gnutls_record_log
            ("REC[%p]: Short record length %d > %d - %d (under attack?)\n",
             session, pad, ciphertext.size, hash_size);
          /* We do not fail here. We check below for the
           * the pad_failed. If zero means success.
           */
          pad_failed = GNUTLS_E_DECRYPTION_FAILED;
        }

      length = ciphertext.size - hash_size - pad;

      /* Check the pading bytes (TLS 1.x)
       */
      if (_gnutls_version_has_variable_padding (ver) && pad_failed == 0)
        for (i = 2; i < pad; i++)
          {
            if (ciphertext.data[ciphertext.size - i] !=
                ciphertext.data[ciphertext.size - 1])
              pad_failed = GNUTLS_E_DECRYPTION_FAILED;
          }
      break;
    default:
      gnutls_assert ();
      return GNUTLS_E_INTERNAL_ERROR;
    }

  if (length < 0)
    length = 0;
  c_length = _gnutls_conv_uint16 ((uint16_t) length);

  /* Pass the type, version, length and compressed through
   * MAC.
   */
  if (params->mac_algorithm != GNUTLS_MAC_NULL)
    {
      digest_hd_st td;

      ret = mac_init (&td, params->mac_algorithm,
                      params->read.mac_secret.data,
                      params->read.mac_secret.size, ver);

      if (ret < 0)
        {
          gnutls_assert ();
          return GNUTLS_E_INTERNAL_ERROR;
        }

      preamble_size =
        make_preamble (UINT64DATA
                       (params->read.sequence_number), type,
                       c_length, ver, preamble);
      mac_hash (&td, preamble, preamble_size, ver);
      if (length > 0)
        mac_hash (&td, ciphertext.data, length, ver);

      mac_deinit (&td, MAC, ver);
    }

  /* This one was introduced to avoid a timing attack against the TLS
   * 1.0 protocol.
   */
  if (pad_failed != 0)
    {
      gnutls_assert ();
      return pad_failed;
    }

  /* HMAC was not the same.
   */
  if (memcmp (MAC, &ciphertext.data[length], hash_size) != 0)
    {
      gnutls_assert ();
      return GNUTLS_E_DECRYPTION_FAILED;
    }

  /* copy the decrypted stuff to compress_data.
   */
  if (compress_size < length)
    {
      gnutls_assert ();
      return GNUTLS_E_DECOMPRESSION_FAILED;
    }
  memcpy (compress_data, ciphertext.data, length);

  return length;
}

gnutls_cipher.c in libgnutls in GnuTLS before 2.12.17 and 3.x before 3.0.15 does not properly handle data encrypted with a block cipher, which allows remote attackers to cause a denial of service (heap memory corruption and application crash) via a crafted record, as demonstrated by a crafted GenericBlockCipher structure.


_gnutls_ciphertext2compressed (gnutls_session_t session,
                               opaque * compress_data,
                               int compress_size,
                               gnutls_datum_t ciphertext, uint8_t type,
                               record_parameters_st * params)
{
  uint8_t MAC[MAX_HASH_SIZE];
  uint16_t c_length;
  uint8_t pad;
  int length;
  uint16_t blocksize;
  int ret, i, pad_failed = 0;
  opaque preamble[PREAMBLE_SIZE];
  int preamble_size;
  int ver = gnutls_protocol_get_version (session);
  int hash_size = _gnutls_hash_get_algo_len (params->mac_algorithm);

  blocksize = gnutls_cipher_get_block_size (params->cipher_algorithm);

  /* actual decryption (inplace)
   */
  switch (_gnutls_cipher_is_block (params->cipher_algorithm))
    {
    case CIPHER_STREAM:
      if ((ret =
           _gnutls_cipher_decrypt (&params->read.cipher_state,
                                   ciphertext.data, ciphertext.size)) < 0)
        {
          gnutls_assert ();
          return ret;
        }

      length = ciphertext.size - hash_size;

      break;
    case CIPHER_BLOCK:
      if ((ciphertext.size < blocksize) || (ciphertext.size % blocksize != 0))
        {
          gnutls_assert ();
          return GNUTLS_E_DECRYPTION_FAILED;
        }

      if ((ret =
           _gnutls_cipher_decrypt (&params->read.cipher_state,
                                   ciphertext.data, ciphertext.size)) < 0)
        {
          gnutls_assert ();
          return ret;
        }

      /* ignore the IV in TLS 1.1.
       */
      if (_gnutls_version_has_explicit_iv
          (session->security_parameters.version))
         {
           ciphertext.size -= blocksize;
           ciphertext.data += blocksize;
         }

      if (ciphertext.size < hash_size)
        {
          gnutls_assert ();
          return GNUTLS_E_DECRYPTION_FAILED;
        }
       pad = ciphertext.data[ciphertext.size - 1] + 1;   /* pad */

       if ((int) pad > (int) ciphertext.size - hash_size)
      if ((int) pad > (int) ciphertext.size - hash_size)
        {
          gnutls_assert ();
          _gnutls_record_log
            ("REC[%p]: Short record length %d > %d - %d (under attack?)\n",
             session, pad, ciphertext.size, hash_size);
          /* We do not fail here. We check below for the
           * the pad_failed. If zero means success.
           */
          pad_failed = GNUTLS_E_DECRYPTION_FAILED;
        }

      length = ciphertext.size - hash_size - pad;

      /* Check the pading bytes (TLS 1.x)
       */
      if (_gnutls_version_has_variable_padding (ver) && pad_failed == 0)
        for (i = 2; i < pad; i++)
          {
            if (ciphertext.data[ciphertext.size - i] !=
                ciphertext.data[ciphertext.size - 1])
              pad_failed = GNUTLS_E_DECRYPTION_FAILED;
          }
      break;
    default:
      gnutls_assert ();
      return GNUTLS_E_INTERNAL_ERROR;
    }

  if (length < 0)
    length = 0;
  c_length = _gnutls_conv_uint16 ((uint16_t) length);

  /* Pass the type, version, length and compressed through
   * MAC.
   */
  if (params->mac_algorithm != GNUTLS_MAC_NULL)
    {
      digest_hd_st td;

      ret = mac_init (&td, params->mac_algorithm,
                      params->read.mac_secret.data,
                      params->read.mac_secret.size, ver);

      if (ret < 0)
        {
          gnutls_assert ();
          return GNUTLS_E_INTERNAL_ERROR;
        }

      preamble_size =
        make_preamble (UINT64DATA
                       (params->read.sequence_number), type,
                       c_length, ver, preamble);
      mac_hash (&td, preamble, preamble_size, ver);
      if (length > 0)
        mac_hash (&td, ciphertext.data, length, ver);

      mac_deinit (&td, MAC, ver);
    }

  /* This one was introduced to avoid a timing attack against the TLS
   * 1.0 protocol.
   */
  if (pad_failed != 0)
    {
      gnutls_assert ();
      return pad_failed;
    }

  /* HMAC was not the same.
   */
  if (memcmp (MAC, &ciphertext.data[length], hash_size) != 0)
    {
      gnutls_assert ();
      return GNUTLS_E_DECRYPTION_FAILED;
    }

  /* copy the decrypted stuff to compress_data.
   */
  if (compress_size < length)
    {
      gnutls_assert ();
      return GNUTLS_E_DECOMPRESSION_FAILED;
    }
  memcpy (compress_data, ciphertext.data, length);

  return length;
}


Fix a padding oracle in PKCS7_dataDecode and CMS_decrypt_set1_pkey

An attack is simple, if the first CMS_recipientInfo is valid but the
second CMS_recipientInfo is chosen ciphertext. If the second
recipientInfo decodes to PKCS #1 v1.5 form plaintext, the correct
encryption key will be replaced by garbage, and the message cannot be
decoded, but if the RSA decryption fails, the correct encryption key is
used and the recipient will not notice the attack.

As a work around for this potential attack the length of the decrypted
key must be equal to the cipher default key length, in case the
certifiate is not given and all recipientInfo are tried out.

The old behaviour can be re-enabled in the CMS code by setting the
CMS_DEBUG_DECRYPT flag.

Reviewed-by: Matt Caswell <matt@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/9777)

(cherry picked from commit 5840ed0cd1e6487d247efbc1a04136a41d7b3a37)

int CMS_decrypt(CMS_ContentInfo *cms, EVP_PKEY *pk, X509 *cert,
                BIO *dcont, BIO *out, unsigned int flags)
{
    int r;
    BIO *cont;
    if (OBJ_obj2nid(CMS_get0_type(cms)) != NID_pkcs7_enveloped) {
        CMSerr(CMS_F_CMS_DECRYPT, CMS_R_TYPE_NOT_ENVELOPED_DATA);
        return 0;
    }
    if (!dcont && !check_content(cms))
        return 0;
    if (flags & CMS_DEBUG_DECRYPT)
         cms->d.envelopedData->encryptedContentInfo->debug = 1;
     else
         cms->d.envelopedData->encryptedContentInfo->debug = 0;
     if (!pk && !cert && !dcont && !out)
         return 1;
     if (pk && !CMS_decrypt_set1_pkey(cms, pk, cert))
    r = cms_copy_content(out, cont, flags);
    do_free_upto(cont, dcont);
    return r;
}

In situations where an attacker receives automated notification of the success or failure of a decryption attempt an attacker, after sending a very large number of messages to be decrypted, can recover a CMS/PKCS7 transported encryption key or decrypt any RSA encrypted message that was encrypted with the public RSA key, using a Bleichenbacher padding oracle attack. Applications are not affected if they use a certificate together with the private RSA key to the CMS_decrypt or PKCS7_decrypt functions to select the correct recipient info to decrypt. Fixed in OpenSSL 1.1.1d (Affected 1.1.1-1.1.1c). Fixed in OpenSSL 1.1.0l (Affected 1.1.0-1.1.0k). Fixed in OpenSSL 1.0.2t (Affected 1.0.2-1.0.2s).


int CMS_decrypt(CMS_ContentInfo *cms, EVP_PKEY *pk, X509 *cert,
                BIO *dcont, BIO *out, unsigned int flags)
{
    int r;
    BIO *cont;
    if (OBJ_obj2nid(CMS_get0_type(cms)) != NID_pkcs7_enveloped) {
        CMSerr(CMS_F_CMS_DECRYPT, CMS_R_TYPE_NOT_ENVELOPED_DATA);
        return 0;
    }
    if (!dcont && !check_content(cms))
        return 0;
    if (flags & CMS_DEBUG_DECRYPT)
         cms->d.envelopedData->encryptedContentInfo->debug = 1;
     else
         cms->d.envelopedData->encryptedContentInfo->debug = 0;
    if (!cert)
        cms->d.envelopedData->encryptedContentInfo->havenocert = 1;
    else
        cms->d.envelopedData->encryptedContentInfo->havenocert = 0;
     if (!pk && !cert && !dcont && !out)
         return 1;
     if (pk && !CMS_decrypt_set1_pkey(cms, pk, cert))
    r = cms_copy_content(out, cont, flags);
    do_free_upto(cont, dcont);
    return r;
}


_gnutls_ciphertext2compressed (gnutls_session_t session,
			       opaque * compress_data,
			       int compress_size,
			       gnutls_datum_t ciphertext, uint8_t type)
{
  uint8_t MAC[MAX_HASH_SIZE];
  uint16_t c_length;
  uint8_t pad;
  int length;
  digest_hd_st td;
  uint16_t blocksize;
  int ret, i, pad_failed = 0;
  uint8_t major, minor;
  gnutls_protocol_t ver;
  int hash_size =
    _gnutls_hash_get_algo_len (session->security_parameters.
			       read_mac_algorithm);

  ver = gnutls_protocol_get_version (session);
  minor = _gnutls_version_get_minor (ver);
  major = _gnutls_version_get_major (ver);

  blocksize = _gnutls_cipher_get_block_size (session->security_parameters.
					     read_bulk_cipher_algorithm);

  /* initialize MAC
   */
  ret = mac_init (&td, session->security_parameters.read_mac_algorithm,
		 session->connection_state.read_mac_secret.data,
		 session->connection_state.read_mac_secret.size, ver);

  if (ret < 0
      && session->security_parameters.read_mac_algorithm != GNUTLS_MAC_NULL)
    {
      gnutls_assert ();
       return GNUTLS_E_INTERNAL_ERROR;
     }

   /* actual decryption (inplace)
    */
	{
	  gnutls_assert ();
	  return ret;
	}

      length = ciphertext.size - hash_size;

      break;
    case CIPHER_BLOCK:
      if ((ciphertext.size < blocksize) || (ciphertext.size % blocksize != 0))
	{
	  gnutls_assert ();
	  return GNUTLS_E_DECRYPTION_FAILED;
	}

      if ((ret = _gnutls_cipher_decrypt (&session->connection_state.
					 read_cipher_state,
					 ciphertext.data,
					 ciphertext.size)) < 0)
	{
	  gnutls_assert ();
	  return ret;
	}

      /* ignore the IV in TLS 1.1.
       */
      if (session->security_parameters.version >= GNUTLS_TLS1_1)
	{
	  ciphertext.size -= blocksize;
	  ciphertext.data += blocksize;

	  if (ciphertext.size == 0)
	    {
	      gnutls_assert ();
	      return GNUTLS_E_DECRYPTION_FAILED;
	    }
	}

      pad = ciphertext.data[ciphertext.size - 1] + 1;	/* pad */

      length = ciphertext.size - hash_size - pad;

      if (pad > ciphertext.size - hash_size)
	{
	  gnutls_assert ();

       pad = ciphertext.data[ciphertext.size - 1] + 1;  /* pad */

      length = ciphertext.size - hash_size - pad;
      if (pad > ciphertext.size - hash_size)
        {
          gnutls_assert ();
          /* We do not fail here. We check below for the
       */
      if (ver >= GNUTLS_TLS1 && pad_failed == 0)
          pad_failed = GNUTLS_E_DECRYPTION_FAILED;
        }

       /* Check the pading bytes (TLS 1.x)
        */
       if (ver >= GNUTLS_TLS1 && pad_failed == 0)
      gnutls_assert ();
      return GNUTLS_E_INTERNAL_ERROR;
    }

  if (length < 0)
    length = 0;
  c_length = _gnutls_conv_uint16 ((uint16_t) length);

  /* Pass the type, version, length and compressed through
   * MAC.
   */
  if (session->security_parameters.read_mac_algorithm != GNUTLS_MAC_NULL)
    {
      _gnutls_hmac (&td,
		    UINT64DATA (session->connection_state.
				read_sequence_number), 8);

      _gnutls_hmac (&td, &type, 1);
      if (ver >= GNUTLS_TLS1)
	{			/* TLS 1.x */
	  _gnutls_hmac (&td, &major, 1);
	  _gnutls_hmac (&td, &minor, 1);
	}
      _gnutls_hmac (&td, &c_length, 2);

      if (length > 0)
	_gnutls_hmac (&td, ciphertext.data, length);

      mac_deinit (&td, MAC, ver);
    }

  /* This one was introduced to avoid a timing attack against the TLS
   * 1.0 protocol.
   */
  if (pad_failed != 0)
    return pad_failed;

  /* HMAC was not the same.
   */
  if (memcmp (MAC, &ciphertext.data[length], hash_size) != 0)
    {
      gnutls_assert ();
      return GNUTLS_E_DECRYPTION_FAILED;
    }

  /* copy the decrypted stuff to compress_data.
   */
  if (compress_size < length)
    {
      gnutls_assert ();
      return GNUTLS_E_DECOMPRESSION_FAILED;
    }
  memcpy (compress_data, ciphertext.data, length);

  return length;
}

Integer signedness error in the _gnutls_ciphertext2compressed function in lib/gnutls_cipher.c in libgnutls in GnuTLS before 2.2.4 allows remote attackers to cause a denial of service (buffer over-read and crash) via a certain integer value in the Random field in an encrypted Client Hello message within a TLS record with an invalid Record Length, which leads to an invalid cipher padding length, aka GNUTLS-SA-2008-1-3.


_gnutls_ciphertext2compressed (gnutls_session_t session,
			       opaque * compress_data,
			       int compress_size,
			       gnutls_datum_t ciphertext, uint8_t type)
{
  uint8_t MAC[MAX_HASH_SIZE];
  uint16_t c_length;
  uint8_t pad;
  int length;
  digest_hd_st td;
  uint16_t blocksize;
  int ret, i, pad_failed = 0;
  uint8_t major, minor;
  gnutls_protocol_t ver;
  int hash_size =
    _gnutls_hash_get_algo_len (session->security_parameters.
			       read_mac_algorithm);

  ver = gnutls_protocol_get_version (session);
  minor = _gnutls_version_get_minor (ver);
  major = _gnutls_version_get_major (ver);

  blocksize = _gnutls_cipher_get_block_size (session->security_parameters.
					     read_bulk_cipher_algorithm);

  /* initialize MAC
   */
  ret = mac_init (&td, session->security_parameters.read_mac_algorithm,
		 session->connection_state.read_mac_secret.data,
		 session->connection_state.read_mac_secret.size, ver);

  if (ret < 0
      && session->security_parameters.read_mac_algorithm != GNUTLS_MAC_NULL)
    {
      gnutls_assert ();
       return GNUTLS_E_INTERNAL_ERROR;
     }

  if (ciphertext.size < (unsigned) blocksize + hash_size)
    {
      _gnutls_record_log
       ("REC[%x]: Short record length %d < %d + %d (under attack?)\n",
        session, ciphertext.size, blocksize, hash_size);
      gnutls_assert ();
      return GNUTLS_E_DECRYPTION_FAILED;
    }

   /* actual decryption (inplace)
    */
	{
	  gnutls_assert ();
	  return ret;
	}

      length = ciphertext.size - hash_size;

      break;
    case CIPHER_BLOCK:
      if ((ciphertext.size < blocksize) || (ciphertext.size % blocksize != 0))
	{
	  gnutls_assert ();
	  return GNUTLS_E_DECRYPTION_FAILED;
	}

      if ((ret = _gnutls_cipher_decrypt (&session->connection_state.
					 read_cipher_state,
					 ciphertext.data,
					 ciphertext.size)) < 0)
	{
	  gnutls_assert ();
	  return ret;
	}

      /* ignore the IV in TLS 1.1.
       */
      if (session->security_parameters.version >= GNUTLS_TLS1_1)
	{
	  ciphertext.size -= blocksize;
	  ciphertext.data += blocksize;

	  if (ciphertext.size == 0)
	    {
	      gnutls_assert ();
	      return GNUTLS_E_DECRYPTION_FAILED;
	    }
	}

      pad = ciphertext.data[ciphertext.size - 1] + 1;	/* pad */

      length = ciphertext.size - hash_size - pad;

      if (pad > ciphertext.size - hash_size)
	{
	  gnutls_assert ();

       pad = ciphertext.data[ciphertext.size - 1] + 1;  /* pad */

      if ((int)pad > (int)ciphertext.size - hash_size)
        {
          gnutls_assert ();
          /* We do not fail here. We check below for the
       */
      if (ver >= GNUTLS_TLS1 && pad_failed == 0)
          pad_failed = GNUTLS_E_DECRYPTION_FAILED;
        }

      length = ciphertext.size - hash_size - pad;

       /* Check the pading bytes (TLS 1.x)
        */
       if (ver >= GNUTLS_TLS1 && pad_failed == 0)
      gnutls_assert ();
      return GNUTLS_E_INTERNAL_ERROR;
    }

  if (length < 0)
    length = 0;
  c_length = _gnutls_conv_uint16 ((uint16_t) length);

  /* Pass the type, version, length and compressed through
   * MAC.
   */
  if (session->security_parameters.read_mac_algorithm != GNUTLS_MAC_NULL)
    {
      _gnutls_hmac (&td,
		    UINT64DATA (session->connection_state.
				read_sequence_number), 8);

      _gnutls_hmac (&td, &type, 1);
      if (ver >= GNUTLS_TLS1)
	{			/* TLS 1.x */
	  _gnutls_hmac (&td, &major, 1);
	  _gnutls_hmac (&td, &minor, 1);
	}
      _gnutls_hmac (&td, &c_length, 2);

      if (length > 0)
	_gnutls_hmac (&td, ciphertext.data, length);

      mac_deinit (&td, MAC, ver);
    }

  /* This one was introduced to avoid a timing attack against the TLS
   * 1.0 protocol.
   */
  if (pad_failed != 0)
    return pad_failed;

  /* HMAC was not the same.
   */
  if (memcmp (MAC, &ciphertext.data[length], hash_size) != 0)
    {
      gnutls_assert ();
      return GNUTLS_E_DECRYPTION_FAILED;
    }

  /* copy the decrypted stuff to compress_data.
   */
  if (compress_size < length)
    {
      gnutls_assert ();
      return GNUTLS_E_DECOMPRESSION_FAILED;
    }
  memcpy (compress_data, ciphertext.data, length);

  return length;
}


static bool on_accept(private_stroke_socket_t *this, stream_t *stream)
{
	stroke_msg_t *msg;
	uint16_t len;
	FILE *out;

	/* read length */
	if (!stream->read_all(stream, &len, sizeof(len)))
	{
		if (errno != EWOULDBLOCK)
		{
			DBG1(DBG_CFG, "reading length of stroke message failed: %s",
				 strerror(errno));
                }
                return FALSE;
        }

        /* read message (we need an additional byte to terminate the buffer) */
        msg = malloc(len + 1);
			DBG1(DBG_CFG, "reading stroke message failed: %s", strerror(errno));
		}

In stroke_socket.c in strongSwan before 5.6.3, a missing packet length check could allow a buffer underflow, which may lead to resource exhaustion and denial of service while reading from the socket.


static bool on_accept(private_stroke_socket_t *this, stream_t *stream)
{
	stroke_msg_t *msg;
	uint16_t len;
	FILE *out;

	/* read length */
	if (!stream->read_all(stream, &len, sizeof(len)))
	{
		if (errno != EWOULDBLOCK)
		{
			DBG1(DBG_CFG, "reading length of stroke message failed: %s",
				 strerror(errno));
                }
                return FALSE;
        }
       if (len < offsetof(stroke_msg_t, buffer))
       {
               DBG1(DBG_CFG, "invalid stroke message length %d", len);
               return FALSE;
       }

        /* read message (we need an additional byte to terminate the buffer) */
        msg = malloc(len + 1);
			DBG1(DBG_CFG, "reading stroke message failed: %s", strerror(errno));
		}


Fix for session tickets memory leak.

CVE-2014-3567

Reviewed-by: Rich Salz <rsalz@openssl.org>
Reviewed-by: Matt Caswell <matt@openssl.org>
(cherry picked from commit 5dc6070a03779cd524f0e67f76c945cb0ac38320)

static int tls_decrypt_ticket(SSL *s, const unsigned char *etick, int eticklen,
				const unsigned char *sess_id, int sesslen,
				SSL_SESSION **psess)
	{
	SSL_SESSION *sess;
	unsigned char *sdec;
	const unsigned char *p;
	int slen, mlen, renew_ticket = 0;
	unsigned char tick_hmac[EVP_MAX_MD_SIZE];
	HMAC_CTX hctx;
	EVP_CIPHER_CTX ctx;
	SSL_CTX *tctx = s->initial_ctx;
	/* Need at least keyname + iv + some encrypted data */
	if (eticklen < 48)
		return 2;
	/* Initialize session ticket encryption and HMAC contexts */
	HMAC_CTX_init(&hctx);
	EVP_CIPHER_CTX_init(&ctx);
	if (tctx->tlsext_ticket_key_cb)
		{
		unsigned char *nctick = (unsigned char *)etick;
		int rv = tctx->tlsext_ticket_key_cb(s, nctick, nctick + 16,
							&ctx, &hctx, 0);
		if (rv < 0)
			return -1;
		if (rv == 0)
			return 2;
		if (rv == 2)
			renew_ticket = 1;
		}
	else
		{
		/* Check key name matches */
		if (memcmp(etick, tctx->tlsext_tick_key_name, 16))
			return 2;
		HMAC_Init_ex(&hctx, tctx->tlsext_tick_hmac_key, 16,
					tlsext_tick_md(), NULL);
		EVP_DecryptInit_ex(&ctx, EVP_aes_128_cbc(), NULL,
				tctx->tlsext_tick_aes_key, etick + 16);
		}
	/* Attempt to process session ticket, first conduct sanity and
	 * integrity checks on ticket.
	 */
	mlen = HMAC_size(&hctx);
	if (mlen < 0)
		{
		EVP_CIPHER_CTX_cleanup(&ctx);
		return -1;
		}
	eticklen -= mlen;
	/* Check HMAC of encrypted ticket */
	HMAC_Update(&hctx, etick, eticklen);
        HMAC_Final(&hctx, tick_hmac, NULL);
        HMAC_CTX_cleanup(&hctx);
        if (CRYPTO_memcmp(tick_hmac, etick + eticklen, mlen))
                return 2;
        /* Attempt to decrypt session data */
        /* Move p after IV to start of encrypted ticket, update length */
        p = etick + 16 + EVP_CIPHER_CTX_iv_length(&ctx);
		{
		EVP_CIPHER_CTX_cleanup(&ctx);
		return -1;
		}
	EVP_DecryptUpdate(&ctx, sdec, &slen, p, eticklen);
	if (EVP_DecryptFinal(&ctx, sdec + slen, &mlen) <= 0)
		{
		EVP_CIPHER_CTX_cleanup(&ctx);
		OPENSSL_free(sdec);
		return 2;
		}
	slen += mlen;
	EVP_CIPHER_CTX_cleanup(&ctx);
	p = sdec;

	sess = d2i_SSL_SESSION(NULL, &p, slen);
	OPENSSL_free(sdec);
	if (sess)
		{
		/* The session ID, if non-empty, is used by some clients to
		 * detect that the ticket has been accepted. So we copy it to
		 * the session structure. If it is empty set length to zero
		 * as required by standard.
		 */
		if (sesslen)
			memcpy(sess->session_id, sess_id, sesslen);
		sess->session_id_length = sesslen;
		*psess = sess;
		if (renew_ticket)
			return 4;
		else
			return 3;
		}
        ERR_clear_error();
	/* For session parse failure, indicate that we need to send a new
	 * ticket. */
	return 2;
	}

Memory leak in the tls_decrypt_ticket function in t1_lib.c in OpenSSL before 0.9.8zc, 1.0.0 before 1.0.0o, and 1.0.1 before 1.0.1j allows remote attackers to cause a denial of service (memory consumption) via a crafted session ticket that triggers an integrity-check failure.


static int tls_decrypt_ticket(SSL *s, const unsigned char *etick, int eticklen,
				const unsigned char *sess_id, int sesslen,
				SSL_SESSION **psess)
	{
	SSL_SESSION *sess;
	unsigned char *sdec;
	const unsigned char *p;
	int slen, mlen, renew_ticket = 0;
	unsigned char tick_hmac[EVP_MAX_MD_SIZE];
	HMAC_CTX hctx;
	EVP_CIPHER_CTX ctx;
	SSL_CTX *tctx = s->initial_ctx;
	/* Need at least keyname + iv + some encrypted data */
	if (eticklen < 48)
		return 2;
	/* Initialize session ticket encryption and HMAC contexts */
	HMAC_CTX_init(&hctx);
	EVP_CIPHER_CTX_init(&ctx);
	if (tctx->tlsext_ticket_key_cb)
		{
		unsigned char *nctick = (unsigned char *)etick;
		int rv = tctx->tlsext_ticket_key_cb(s, nctick, nctick + 16,
							&ctx, &hctx, 0);
		if (rv < 0)
			return -1;
		if (rv == 0)
			return 2;
		if (rv == 2)
			renew_ticket = 1;
		}
	else
		{
		/* Check key name matches */
		if (memcmp(etick, tctx->tlsext_tick_key_name, 16))
			return 2;
		HMAC_Init_ex(&hctx, tctx->tlsext_tick_hmac_key, 16,
					tlsext_tick_md(), NULL);
		EVP_DecryptInit_ex(&ctx, EVP_aes_128_cbc(), NULL,
				tctx->tlsext_tick_aes_key, etick + 16);
		}
	/* Attempt to process session ticket, first conduct sanity and
	 * integrity checks on ticket.
	 */
	mlen = HMAC_size(&hctx);
	if (mlen < 0)
		{
		EVP_CIPHER_CTX_cleanup(&ctx);
		return -1;
		}
	eticklen -= mlen;
	/* Check HMAC of encrypted ticket */
	HMAC_Update(&hctx, etick, eticklen);
        HMAC_Final(&hctx, tick_hmac, NULL);
        HMAC_CTX_cleanup(&hctx);
        if (CRYPTO_memcmp(tick_hmac, etick + eticklen, mlen))
               {
               EVP_CIPHER_CTX_cleanup(&ctx);
                return 2;
               }
        /* Attempt to decrypt session data */
        /* Move p after IV to start of encrypted ticket, update length */
        p = etick + 16 + EVP_CIPHER_CTX_iv_length(&ctx);
		{
		EVP_CIPHER_CTX_cleanup(&ctx);
		return -1;
		}
	EVP_DecryptUpdate(&ctx, sdec, &slen, p, eticklen);
	if (EVP_DecryptFinal(&ctx, sdec + slen, &mlen) <= 0)
		{
		EVP_CIPHER_CTX_cleanup(&ctx);
		OPENSSL_free(sdec);
		return 2;
		}
	slen += mlen;
	EVP_CIPHER_CTX_cleanup(&ctx);
	p = sdec;

	sess = d2i_SSL_SESSION(NULL, &p, slen);
	OPENSSL_free(sdec);
	if (sess)
		{
		/* The session ID, if non-empty, is used by some clients to
		 * detect that the ticket has been accepted. So we copy it to
		 * the session structure. If it is empty set length to zero
		 * as required by standard.
		 */
		if (sesslen)
			memcpy(sess->session_id, sess_id, sesslen);
		sess->session_id_length = sesslen;
		*psess = sess;
		if (renew_ticket)
			return 4;
		else
			return 3;
		}
        ERR_clear_error();
	/* For session parse failure, indicate that we need to send a new
	 * ticket. */
	return 2;
	}


status_handler (void *opaque, int fd)
{
  struct io_cb_data *data = (struct io_cb_data *) opaque;
  engine_gpgsm_t gpgsm = (engine_gpgsm_t) data->handler_value;
  gpgme_error_t err = 0;
  char *line;
  size_t linelen;

  do
    {
      err = assuan_read_line (gpgsm->assuan_ctx, &line, &linelen);
      if (err)
	{
	  /* Try our best to terminate the connection friendly.  */
	  /*	  assuan_write_line (gpgsm->assuan_ctx, "BYE"); */
          TRACE3 (DEBUG_CTX, "gpgme:status_handler", gpgsm,
		  "fd 0x%x: error from assuan (%d) getting status line : %s",
                  fd, err, gpg_strerror (err));
	}
      else if (linelen >= 3
	       && line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
	       && (line[3] == '\0' || line[3] == ' '))
	{
	  if (line[3] == ' ')
	    err = atoi (&line[4]);
	  if (! err)
	    err = gpg_error (GPG_ERR_GENERAL);
          TRACE2 (DEBUG_CTX, "gpgme:status_handler", gpgsm,
		  "fd 0x%x: ERR line - mapped to: %s",
                  fd, err ? gpg_strerror (err) : "ok");
	  /* Try our best to terminate the connection friendly.  */
	  /*	  assuan_write_line (gpgsm->assuan_ctx, "BYE"); */
	}
      else if (linelen >= 2
	       && line[0] == 'O' && line[1] == 'K'
	       && (line[2] == '\0' || line[2] == ' '))
	{
	  if (gpgsm->status.fnc)
	    err = gpgsm->status.fnc (gpgsm->status.fnc_value,
				     GPGME_STATUS_EOF, "");

	  if (!err && gpgsm->colon.fnc && gpgsm->colon.any)
            {
              /* We must tell a colon function about the EOF. We do
                 this only when we have seen any data lines.  Note
                 that this inlined use of colon data lines will
                 eventually be changed into using a regular data
                 channel. */
              gpgsm->colon.any = 0;
              err = gpgsm->colon.fnc (gpgsm->colon.fnc_value, NULL);
            }
          TRACE2 (DEBUG_CTX, "gpgme:status_handler", gpgsm,
		  "fd 0x%x: OK line - final status: %s",
                  fd, err ? gpg_strerror (err) : "ok");
	  _gpgme_io_close (gpgsm->status_cb.fd);
	  return err;
	}
      else if (linelen > 2
	       && line[0] == 'D' && line[1] == ' '
	       && gpgsm->colon.fnc)
        {
	  /* We are using the colon handler even for plain inline data
             - strange name for that function but for historic reasons
             we keep it.  */
          /* FIXME We can't use this for binary data because we
             assume this is a string.  For the current usage of colon
             output it is correct.  */
          char *src = line + 2;
	  char *end = line + linelen;
	  char *dst;
          char **aline = &gpgsm->colon.attic.line;
	  int *alinelen = &gpgsm->colon.attic.linelen;

	  if (gpgsm->colon.attic.linesize < *alinelen + linelen + 1)
	    {
	      char *newline = realloc (*aline, *alinelen + linelen + 1);
	      if (!newline)
		err = gpg_error_from_syserror ();
              else
                {
                  *aline = newline;
                 gpgsm->colon.attic.linesize += linelen + 1;
                }
            }
          if (!err)
	    {
	      dst = *aline + *alinelen;

	      while (!err && src < end)
		{
		  if (*src == '%' && src + 2 < end)
		    {
		      /* Handle escaped characters.  */
		      ++src;
		      *dst = _gpgme_hextobyte (src);
		      (*alinelen)++;
		      src += 2;
		    }
		  else
		    {
		      *dst = *src++;
		      (*alinelen)++;
		    }

		  if (*dst == '\n')
		    {
		      /* Terminate the pending line, pass it to the colon
			 handler and reset it.  */

		      gpgsm->colon.any = 1;
		      if (*alinelen > 1 && *(dst - 1) == '\r')
			dst--;
		      *dst = '\0';

		      /* FIXME How should we handle the return code?  */
		      err = gpgsm->colon.fnc (gpgsm->colon.fnc_value, *aline);
		      if (!err)
			{
			  dst = *aline;
			  *alinelen = 0;
			}
		    }
		  else
		    dst++;
		}
	    }
          TRACE2 (DEBUG_CTX, "gpgme:status_handler", gpgsm,
		  "fd 0x%x: D line; final status: %s",
                  fd, err? gpg_strerror (err):"ok");
        }
      else if (linelen > 2
	       && line[0] == 'D' && line[1] == ' '
	       && gpgsm->inline_data)
        {
          char *src = line + 2;
	  char *end = line + linelen;
	  char *dst = src;
          gpgme_ssize_t nwritten;

          linelen = 0;
          while (src < end)
            {
              if (*src == '%' && src + 2 < end)
                {
                  /* Handle escaped characters.  */
                  ++src;
                  *dst++ = _gpgme_hextobyte (src);
                  src += 2;
                }
              else
                *dst++ = *src++;

              linelen++;
            }

          src = line + 2;
          while (linelen > 0)
            {
              nwritten = gpgme_data_write (gpgsm->inline_data, src, linelen);
              if (!nwritten || (nwritten < 0 && errno != EINTR)
                  || nwritten > linelen)
                {
                  err = gpg_error_from_syserror ();
                  break;
                }
              src += nwritten;
              linelen -= nwritten;
            }

          TRACE2 (DEBUG_CTX, "gpgme:status_handler", gpgsm,
		  "fd 0x%x: D inlinedata; final status: %s",
                  fd, err? gpg_strerror (err):"ok");
        }
      else if (linelen > 2
	       && line[0] == 'S' && line[1] == ' ')
	{
	  char *rest;
	  gpgme_status_code_t r;

	  rest = strchr (line + 2, ' ');
	  if (!rest)
	    rest = line + linelen; /* set to an empty string */
	  else
	    *(rest++) = 0;

	  r = _gpgme_parse_status (line + 2);

	  if (r >= 0)
	    {
	      if (gpgsm->status.fnc)
		err = gpgsm->status.fnc (gpgsm->status.fnc_value, r, rest);
	    }
	  else
	    fprintf (stderr, "[UNKNOWN STATUS]%s %s", line + 2, rest);
          TRACE3 (DEBUG_CTX, "gpgme:status_handler", gpgsm,
		  "fd 0x%x: S line (%s) - final status: %s",
                  fd, line+2, err? gpg_strerror (err):"ok");
	}
      else if (linelen >= 7
               && line[0] == 'I' && line[1] == 'N' && line[2] == 'Q'
               && line[3] == 'U' && line[4] == 'I' && line[5] == 'R'
               && line[6] == 'E'
               && (line[7] == '\0' || line[7] == ' '))
        {
          char *keyword = line+7;

          while (*keyword == ' ')
            keyword++;;
          default_inq_cb (gpgsm, keyword);
          assuan_write_line (gpgsm->assuan_ctx, "END");
        }

    }
  while (!err && assuan_pending_line (gpgsm->assuan_ctx));

  return err;
}

Multiple heap-based buffer overflows in the status_handler function in (1) engine-gpgsm.c and (2) engine-uiserver.c in GPGME before 1.5.1 allow remote attackers to cause a denial of service (crash) and possibly execute arbitrary code via vectors related to "different line lengths in a specific order."


status_handler (void *opaque, int fd)
{
  struct io_cb_data *data = (struct io_cb_data *) opaque;
  engine_gpgsm_t gpgsm = (engine_gpgsm_t) data->handler_value;
  gpgme_error_t err = 0;
  char *line;
  size_t linelen;

  do
    {
      err = assuan_read_line (gpgsm->assuan_ctx, &line, &linelen);
      if (err)
	{
	  /* Try our best to terminate the connection friendly.  */
	  /*	  assuan_write_line (gpgsm->assuan_ctx, "BYE"); */
          TRACE3 (DEBUG_CTX, "gpgme:status_handler", gpgsm,
		  "fd 0x%x: error from assuan (%d) getting status line : %s",
                  fd, err, gpg_strerror (err));
	}
      else if (linelen >= 3
	       && line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
	       && (line[3] == '\0' || line[3] == ' '))
	{
	  if (line[3] == ' ')
	    err = atoi (&line[4]);
	  if (! err)
	    err = gpg_error (GPG_ERR_GENERAL);
          TRACE2 (DEBUG_CTX, "gpgme:status_handler", gpgsm,
		  "fd 0x%x: ERR line - mapped to: %s",
                  fd, err ? gpg_strerror (err) : "ok");
	  /* Try our best to terminate the connection friendly.  */
	  /*	  assuan_write_line (gpgsm->assuan_ctx, "BYE"); */
	}
      else if (linelen >= 2
	       && line[0] == 'O' && line[1] == 'K'
	       && (line[2] == '\0' || line[2] == ' '))
	{
	  if (gpgsm->status.fnc)
	    err = gpgsm->status.fnc (gpgsm->status.fnc_value,
				     GPGME_STATUS_EOF, "");

	  if (!err && gpgsm->colon.fnc && gpgsm->colon.any)
            {
              /* We must tell a colon function about the EOF. We do
                 this only when we have seen any data lines.  Note
                 that this inlined use of colon data lines will
                 eventually be changed into using a regular data
                 channel. */
              gpgsm->colon.any = 0;
              err = gpgsm->colon.fnc (gpgsm->colon.fnc_value, NULL);
            }
          TRACE2 (DEBUG_CTX, "gpgme:status_handler", gpgsm,
		  "fd 0x%x: OK line - final status: %s",
                  fd, err ? gpg_strerror (err) : "ok");
	  _gpgme_io_close (gpgsm->status_cb.fd);
	  return err;
	}
      else if (linelen > 2
	       && line[0] == 'D' && line[1] == ' '
	       && gpgsm->colon.fnc)
        {
	  /* We are using the colon handler even for plain inline data
             - strange name for that function but for historic reasons
             we keep it.  */
          /* FIXME We can't use this for binary data because we
             assume this is a string.  For the current usage of colon
             output it is correct.  */
          char *src = line + 2;
	  char *end = line + linelen;
	  char *dst;
          char **aline = &gpgsm->colon.attic.line;
	  int *alinelen = &gpgsm->colon.attic.linelen;

	  if (gpgsm->colon.attic.linesize < *alinelen + linelen + 1)
	    {
	      char *newline = realloc (*aline, *alinelen + linelen + 1);
	      if (!newline)
		err = gpg_error_from_syserror ();
              else
                {
                  *aline = newline;
                 gpgsm->colon.attic.linesize = *alinelen + linelen + 1;
                }
            }
          if (!err)
	    {
	      dst = *aline + *alinelen;

	      while (!err && src < end)
		{
		  if (*src == '%' && src + 2 < end)
		    {
		      /* Handle escaped characters.  */
		      ++src;
		      *dst = _gpgme_hextobyte (src);
		      (*alinelen)++;
		      src += 2;
		    }
		  else
		    {
		      *dst = *src++;
		      (*alinelen)++;
		    }

		  if (*dst == '\n')
		    {
		      /* Terminate the pending line, pass it to the colon
			 handler and reset it.  */

		      gpgsm->colon.any = 1;
		      if (*alinelen > 1 && *(dst - 1) == '\r')
			dst--;
		      *dst = '\0';

		      /* FIXME How should we handle the return code?  */
		      err = gpgsm->colon.fnc (gpgsm->colon.fnc_value, *aline);
		      if (!err)
			{
			  dst = *aline;
			  *alinelen = 0;
			}
		    }
		  else
		    dst++;
		}
	    }
          TRACE2 (DEBUG_CTX, "gpgme:status_handler", gpgsm,
		  "fd 0x%x: D line; final status: %s",
                  fd, err? gpg_strerror (err):"ok");
        }
      else if (linelen > 2
	       && line[0] == 'D' && line[1] == ' '
	       && gpgsm->inline_data)
        {
          char *src = line + 2;
	  char *end = line + linelen;
	  char *dst = src;
          gpgme_ssize_t nwritten;

          linelen = 0;
          while (src < end)
            {
              if (*src == '%' && src + 2 < end)
                {
                  /* Handle escaped characters.  */
                  ++src;
                  *dst++ = _gpgme_hextobyte (src);
                  src += 2;
                }
              else
                *dst++ = *src++;

              linelen++;
            }

          src = line + 2;
          while (linelen > 0)
            {
              nwritten = gpgme_data_write (gpgsm->inline_data, src, linelen);
              if (!nwritten || (nwritten < 0 && errno != EINTR)
                  || nwritten > linelen)
                {
                  err = gpg_error_from_syserror ();
                  break;
                }
              src += nwritten;
              linelen -= nwritten;
            }

          TRACE2 (DEBUG_CTX, "gpgme:status_handler", gpgsm,
		  "fd 0x%x: D inlinedata; final status: %s",
                  fd, err? gpg_strerror (err):"ok");
        }
      else if (linelen > 2
	       && line[0] == 'S' && line[1] == ' ')
	{
	  char *rest;
	  gpgme_status_code_t r;

	  rest = strchr (line + 2, ' ');
	  if (!rest)
	    rest = line + linelen; /* set to an empty string */
	  else
	    *(rest++) = 0;

	  r = _gpgme_parse_status (line + 2);

	  if (r >= 0)
	    {
	      if (gpgsm->status.fnc)
		err = gpgsm->status.fnc (gpgsm->status.fnc_value, r, rest);
	    }
	  else
	    fprintf (stderr, "[UNKNOWN STATUS]%s %s", line + 2, rest);
          TRACE3 (DEBUG_CTX, "gpgme:status_handler", gpgsm,
		  "fd 0x%x: S line (%s) - final status: %s",
                  fd, line+2, err? gpg_strerror (err):"ok");
	}
      else if (linelen >= 7
               && line[0] == 'I' && line[1] == 'N' && line[2] == 'Q'
               && line[3] == 'U' && line[4] == 'I' && line[5] == 'R'
               && line[6] == 'E'
               && (line[7] == '\0' || line[7] == ' '))
        {
          char *keyword = line+7;

          while (*keyword == ' ')
            keyword++;;
          default_inq_cb (gpgsm, keyword);
          assuan_write_line (gpgsm->assuan_ctx, "END");
        }

    }
  while (!err && assuan_pending_line (gpgsm->assuan_ctx));

  return err;
}


status_handler (void *opaque, int fd)
{
  struct io_cb_data *data = (struct io_cb_data *) opaque;
  engine_uiserver_t uiserver = (engine_uiserver_t) data->handler_value;
  gpgme_error_t err = 0;
  char *line;
  size_t linelen;

  do
    {
      err = assuan_read_line (uiserver->assuan_ctx, &line, &linelen);
      if (err)
	{
	  /* Try our best to terminate the connection friendly.  */
	  /*	  assuan_write_line (uiserver->assuan_ctx, "BYE"); */
          TRACE3 (DEBUG_CTX, "gpgme:status_handler", uiserver,
		  "fd 0x%x: error from assuan (%d) getting status line : %s",
                  fd, err, gpg_strerror (err));
	}
      else if (linelen >= 3
	       && line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
	       && (line[3] == '\0' || line[3] == ' '))
	{
	  if (line[3] == ' ')
	    err = atoi (&line[4]);
	  if (! err)
	    err = gpg_error (GPG_ERR_GENERAL);
          TRACE2 (DEBUG_CTX, "gpgme:status_handler", uiserver,
		  "fd 0x%x: ERR line - mapped to: %s",
                  fd, err ? gpg_strerror (err) : "ok");
	  /* Try our best to terminate the connection friendly.  */
	  /*	  assuan_write_line (uiserver->assuan_ctx, "BYE"); */
	}
      else if (linelen >= 2
	       && line[0] == 'O' && line[1] == 'K'
	       && (line[2] == '\0' || line[2] == ' '))
	{
	  if (uiserver->status.fnc)
	    err = uiserver->status.fnc (uiserver->status.fnc_value,
				     GPGME_STATUS_EOF, "");

	  if (!err && uiserver->colon.fnc && uiserver->colon.any)
            {
              /* We must tell a colon function about the EOF. We do
                 this only when we have seen any data lines.  Note
                 that this inlined use of colon data lines will
                 eventually be changed into using a regular data
                 channel. */
              uiserver->colon.any = 0;
              err = uiserver->colon.fnc (uiserver->colon.fnc_value, NULL);
            }
          TRACE2 (DEBUG_CTX, "gpgme:status_handler", uiserver,
		  "fd 0x%x: OK line - final status: %s",
                  fd, err ? gpg_strerror (err) : "ok");
	  _gpgme_io_close (uiserver->status_cb.fd);
	  return err;
	}
      else if (linelen > 2
	       && line[0] == 'D' && line[1] == ' '
	       && uiserver->colon.fnc)
        {
	  /* We are using the colon handler even for plain inline data
             - strange name for that function but for historic reasons
             we keep it.  */
          /* FIXME We can't use this for binary data because we
             assume this is a string.  For the current usage of colon
             output it is correct.  */
          char *src = line + 2;
	  char *end = line + linelen;
	  char *dst;
          char **aline = &uiserver->colon.attic.line;
	  int *alinelen = &uiserver->colon.attic.linelen;

	  if (uiserver->colon.attic.linesize < *alinelen + linelen + 1)
	    {
	      char *newline = realloc (*aline, *alinelen + linelen + 1);
	      if (!newline)
		err = gpg_error_from_syserror ();
              else
                {
                  *aline = newline;
                 uiserver->colon.attic.linesize += linelen + 1;
                }
            }
          if (!err)
	    {
	      dst = *aline + *alinelen;

	      while (!err && src < end)
		{
		  if (*src == '%' && src + 2 < end)
		    {
		      /* Handle escaped characters.  */
		      ++src;
		      *dst = _gpgme_hextobyte (src);
		      (*alinelen)++;
		      src += 2;
		    }
		  else
		    {
		      *dst = *src++;
		      (*alinelen)++;
		    }

		  if (*dst == '\n')
		    {
		      /* Terminate the pending line, pass it to the colon
			 handler and reset it.  */

		      uiserver->colon.any = 1;
		      if (*alinelen > 1 && *(dst - 1) == '\r')
			dst--;
		      *dst = '\0';

		      /* FIXME How should we handle the return code?  */
		      err = uiserver->colon.fnc (uiserver->colon.fnc_value, *aline);
		      if (!err)
			{
			  dst = *aline;
			  *alinelen = 0;
			}
		    }
		  else
		    dst++;
		}
	    }
          TRACE2 (DEBUG_CTX, "gpgme:status_handler", uiserver,
		  "fd 0x%x: D line; final status: %s",
                  fd, err? gpg_strerror (err):"ok");
        }
      else if (linelen > 2
	       && line[0] == 'D' && line[1] == ' '
	       && uiserver->inline_data)
        {
          char *src = line + 2;
	  char *end = line + linelen;
	  char *dst = src;
          gpgme_ssize_t nwritten;

          linelen = 0;
          while (src < end)
            {
              if (*src == '%' && src + 2 < end)
                {
                  /* Handle escaped characters.  */
                  ++src;
                  *dst++ = _gpgme_hextobyte (src);
                  src += 2;
                }
              else
                *dst++ = *src++;

              linelen++;
            }

          src = line + 2;
          while (linelen > 0)
            {
              nwritten = gpgme_data_write (uiserver->inline_data, src, linelen);
              if (!nwritten || (nwritten < 0 && errno != EINTR)
                  || nwritten > linelen)
                {
                  err = gpg_error_from_syserror ();
                  break;
                }
              src += nwritten;
              linelen -= nwritten;
            }

          TRACE2 (DEBUG_CTX, "gpgme:status_handler", uiserver,
		  "fd 0x%x: D inlinedata; final status: %s",
                  fd, err? gpg_strerror (err):"ok");
        }
      else if (linelen > 2
	       && line[0] == 'S' && line[1] == ' ')
	{
	  char *rest;
	  gpgme_status_code_t r;

	  rest = strchr (line + 2, ' ');
	  if (!rest)
	    rest = line + linelen; /* set to an empty string */
	  else
	    *(rest++) = 0;

	  r = _gpgme_parse_status (line + 2);

	  if (r >= 0)
	    {
	      if (uiserver->status.fnc)
		err = uiserver->status.fnc (uiserver->status.fnc_value, r, rest);
	    }
	  else
	    fprintf (stderr, "[UNKNOWN STATUS]%s %s", line + 2, rest);
          TRACE3 (DEBUG_CTX, "gpgme:status_handler", uiserver,
		  "fd 0x%x: S line (%s) - final status: %s",
                  fd, line+2, err? gpg_strerror (err):"ok");
	}
      else if (linelen >= 7
               && line[0] == 'I' && line[1] == 'N' && line[2] == 'Q'
               && line[3] == 'U' && line[4] == 'I' && line[5] == 'R'
               && line[6] == 'E'
               && (line[7] == '\0' || line[7] == ' '))
        {
          char *keyword = line+7;

          while (*keyword == ' ')
            keyword++;;
          default_inq_cb (uiserver, keyword);
          assuan_write_line (uiserver->assuan_ctx, "END");
        }

    }
  while (!err && assuan_pending_line (uiserver->assuan_ctx));

  return err;
}


status_handler (void *opaque, int fd)
{
  struct io_cb_data *data = (struct io_cb_data *) opaque;
  engine_uiserver_t uiserver = (engine_uiserver_t) data->handler_value;
  gpgme_error_t err = 0;
  char *line;
  size_t linelen;

  do
    {
      err = assuan_read_line (uiserver->assuan_ctx, &line, &linelen);
      if (err)
	{
	  /* Try our best to terminate the connection friendly.  */
	  /*	  assuan_write_line (uiserver->assuan_ctx, "BYE"); */
          TRACE3 (DEBUG_CTX, "gpgme:status_handler", uiserver,
		  "fd 0x%x: error from assuan (%d) getting status line : %s",
                  fd, err, gpg_strerror (err));
	}
      else if (linelen >= 3
	       && line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
	       && (line[3] == '\0' || line[3] == ' '))
	{
	  if (line[3] == ' ')
	    err = atoi (&line[4]);
	  if (! err)
	    err = gpg_error (GPG_ERR_GENERAL);
          TRACE2 (DEBUG_CTX, "gpgme:status_handler", uiserver,
		  "fd 0x%x: ERR line - mapped to: %s",
                  fd, err ? gpg_strerror (err) : "ok");
	  /* Try our best to terminate the connection friendly.  */
	  /*	  assuan_write_line (uiserver->assuan_ctx, "BYE"); */
	}
      else if (linelen >= 2
	       && line[0] == 'O' && line[1] == 'K'
	       && (line[2] == '\0' || line[2] == ' '))
	{
	  if (uiserver->status.fnc)
	    err = uiserver->status.fnc (uiserver->status.fnc_value,
				     GPGME_STATUS_EOF, "");

	  if (!err && uiserver->colon.fnc && uiserver->colon.any)
            {
              /* We must tell a colon function about the EOF. We do
                 this only when we have seen any data lines.  Note
                 that this inlined use of colon data lines will
                 eventually be changed into using a regular data
                 channel. */
              uiserver->colon.any = 0;
              err = uiserver->colon.fnc (uiserver->colon.fnc_value, NULL);
            }
          TRACE2 (DEBUG_CTX, "gpgme:status_handler", uiserver,
		  "fd 0x%x: OK line - final status: %s",
                  fd, err ? gpg_strerror (err) : "ok");
	  _gpgme_io_close (uiserver->status_cb.fd);
	  return err;
	}
      else if (linelen > 2
	       && line[0] == 'D' && line[1] == ' '
	       && uiserver->colon.fnc)
        {
	  /* We are using the colon handler even for plain inline data
             - strange name for that function but for historic reasons
             we keep it.  */
          /* FIXME We can't use this for binary data because we
             assume this is a string.  For the current usage of colon
             output it is correct.  */
          char *src = line + 2;
	  char *end = line + linelen;
	  char *dst;
          char **aline = &uiserver->colon.attic.line;
	  int *alinelen = &uiserver->colon.attic.linelen;

	  if (uiserver->colon.attic.linesize < *alinelen + linelen + 1)
	    {
	      char *newline = realloc (*aline, *alinelen + linelen + 1);
	      if (!newline)
		err = gpg_error_from_syserror ();
              else
                {
                  *aline = newline;
                 uiserver->colon.attic.linesize = *alinelen + linelen + 1;
                }
            }
          if (!err)
	    {
	      dst = *aline + *alinelen;

	      while (!err && src < end)
		{
		  if (*src == '%' && src + 2 < end)
		    {
		      /* Handle escaped characters.  */
		      ++src;
		      *dst = _gpgme_hextobyte (src);
		      (*alinelen)++;
		      src += 2;
		    }
		  else
		    {
		      *dst = *src++;
		      (*alinelen)++;
		    }

		  if (*dst == '\n')
		    {
		      /* Terminate the pending line, pass it to the colon
			 handler and reset it.  */

		      uiserver->colon.any = 1;
		      if (*alinelen > 1 && *(dst - 1) == '\r')
			dst--;
		      *dst = '\0';

		      /* FIXME How should we handle the return code?  */
		      err = uiserver->colon.fnc (uiserver->colon.fnc_value, *aline);
		      if (!err)
			{
			  dst = *aline;
			  *alinelen = 0;
			}
		    }
		  else
		    dst++;
		}
	    }
          TRACE2 (DEBUG_CTX, "gpgme:status_handler", uiserver,
		  "fd 0x%x: D line; final status: %s",
                  fd, err? gpg_strerror (err):"ok");
        }
      else if (linelen > 2
	       && line[0] == 'D' && line[1] == ' '
	       && uiserver->inline_data)
        {
          char *src = line + 2;
	  char *end = line + linelen;
	  char *dst = src;
          gpgme_ssize_t nwritten;

          linelen = 0;
          while (src < end)
            {
              if (*src == '%' && src + 2 < end)
                {
                  /* Handle escaped characters.  */
                  ++src;
                  *dst++ = _gpgme_hextobyte (src);
                  src += 2;
                }
              else
                *dst++ = *src++;

              linelen++;
            }

          src = line + 2;
          while (linelen > 0)
            {
              nwritten = gpgme_data_write (uiserver->inline_data, src, linelen);
              if (!nwritten || (nwritten < 0 && errno != EINTR)
                  || nwritten > linelen)
                {
                  err = gpg_error_from_syserror ();
                  break;
                }
              src += nwritten;
              linelen -= nwritten;
            }

          TRACE2 (DEBUG_CTX, "gpgme:status_handler", uiserver,
		  "fd 0x%x: D inlinedata; final status: %s",
                  fd, err? gpg_strerror (err):"ok");
        }
      else if (linelen > 2
	       && line[0] == 'S' && line[1] == ' ')
	{
	  char *rest;
	  gpgme_status_code_t r;

	  rest = strchr (line + 2, ' ');
	  if (!rest)
	    rest = line + linelen; /* set to an empty string */
	  else
	    *(rest++) = 0;

	  r = _gpgme_parse_status (line + 2);

	  if (r >= 0)
	    {
	      if (uiserver->status.fnc)
		err = uiserver->status.fnc (uiserver->status.fnc_value, r, rest);
	    }
	  else
	    fprintf (stderr, "[UNKNOWN STATUS]%s %s", line + 2, rest);
          TRACE3 (DEBUG_CTX, "gpgme:status_handler", uiserver,
		  "fd 0x%x: S line (%s) - final status: %s",
                  fd, line+2, err? gpg_strerror (err):"ok");
	}
      else if (linelen >= 7
               && line[0] == 'I' && line[1] == 'N' && line[2] == 'Q'
               && line[3] == 'U' && line[4] == 'I' && line[5] == 'R'
               && line[6] == 'E'
               && (line[7] == '\0' || line[7] == ' '))
        {
          char *keyword = line+7;

          while (*keyword == ' ')
            keyword++;;
          default_inq_cb (uiserver, keyword);
          assuan_write_line (uiserver->assuan_ctx, "END");
        }

    }
  while (!err && assuan_pending_line (uiserver->assuan_ctx));

  return err;
}


Fix race condition in ssl_parse_serverhello_tlsext

CVE-2014-3509
Reviewed-by: Tim Hudson <tjh@openssl.org>
Reviewed-by: Dr. Stephen Henson <steve@openssl.org>

static int ssl_scan_serverhello_tlsext(SSL *s, unsigned char **p, unsigned char *d, int n, int *al)
	{
	unsigned short length;
	unsigned short type;
	unsigned short size;
	unsigned char *data = *p;
	int tlsext_servername = 0;
	int renegotiate_seen = 0;

#ifndef OPENSSL_NO_NEXTPROTONEG
	s->s3->next_proto_neg_seen = 0;
#endif

	if (s->s3->alpn_selected)
		{
		OPENSSL_free(s->s3->alpn_selected);
		s->s3->alpn_selected = NULL;
		}

#ifndef OPENSSL_NO_HEARTBEATS
	s->tlsext_heartbeat &= ~(SSL_TLSEXT_HB_ENABLED |
	                       SSL_TLSEXT_HB_DONT_SEND_REQUESTS);
#endif

#ifdef TLSEXT_TYPE_encrypt_then_mac
	s->s3->flags &= ~TLS1_FLAGS_ENCRYPT_THEN_MAC;
#endif

	if (data >= (d+n-2))
		goto ri_check;

	n2s(data,length);
	if (data+length != d+n)
		{
		*al = SSL_AD_DECODE_ERROR;
		return 0;
		}

	while(data <= (d+n-4))
		{
		n2s(data,type);
		n2s(data,size);

		if (data+size > (d+n))
	   		goto ri_check;

		if (s->tlsext_debug_cb)
			s->tlsext_debug_cb(s, 1, type, data, size,
						s->tlsext_debug_arg);

		if (type == TLSEXT_TYPE_server_name)
			{
			if (s->tlsext_hostname == NULL || size > 0)
				{
				*al = TLS1_AD_UNRECOGNIZED_NAME;
				return 0;
				}
			tlsext_servername = 1;
			}

#ifndef OPENSSL_NO_EC
		else if (type == TLSEXT_TYPE_ec_point_formats)
			{
			unsigned char *sdata = data;
			int ecpointformatlist_length = *(sdata++);

			if (ecpointformatlist_length != size - 1)
				{
                                *al = TLS1_AD_DECODE_ERROR;
                                return 0;
                                }
                       s->session->tlsext_ecpointformatlist_length = 0;
                       if (s->session->tlsext_ecpointformatlist != NULL) OPENSSL_free(s->session->tlsext_ecpointformatlist);
                       if ((s->session->tlsext_ecpointformatlist = OPENSSL_malloc(ecpointformatlist_length)) == NULL)
                                {
                               *al = TLS1_AD_INTERNAL_ERROR;
                               return 0;
                                }
                       s->session->tlsext_ecpointformatlist_length = ecpointformatlist_length;
                       memcpy(s->session->tlsext_ecpointformatlist, sdata, ecpointformatlist_length);
 #if 0
                        fprintf(stderr,"ssl_parse_serverhello_tlsext s->session->tlsext_ecpointformatlist ");
                        sdata = s->session->tlsext_ecpointformatlist;
#endif
			}
#endif /* OPENSSL_NO_EC */

		else if (type == TLSEXT_TYPE_session_ticket)
			{
			if (s->tls_session_ticket_ext_cb &&
			    !s->tls_session_ticket_ext_cb(s, data, size, s->tls_session_ticket_ext_cb_arg))
				{
				*al = TLS1_AD_INTERNAL_ERROR;
				return 0;
				}
			if (!tls_use_ticket(s) || (size > 0))
				{
				*al = TLS1_AD_UNSUPPORTED_EXTENSION;
				return 0;
				}
			s->tlsext_ticket_expected = 1;
			}
#ifdef TLSEXT_TYPE_opaque_prf_input
		else if (type == TLSEXT_TYPE_opaque_prf_input)
			{
			unsigned char *sdata = data;

			if (size < 2)
				{
				*al = SSL_AD_DECODE_ERROR;
				return 0;
				}
			n2s(sdata, s->s3->server_opaque_prf_input_len);
			if (s->s3->server_opaque_prf_input_len != size - 2)
				{
				*al = SSL_AD_DECODE_ERROR;
				return 0;
				}

			if (s->s3->server_opaque_prf_input != NULL) /* shouldn't really happen */
				OPENSSL_free(s->s3->server_opaque_prf_input);
			if (s->s3->server_opaque_prf_input_len == 0)
				s->s3->server_opaque_prf_input = OPENSSL_malloc(1); /* dummy byte just to get non-NULL */
			else
				s->s3->server_opaque_prf_input = BUF_memdup(sdata, s->s3->server_opaque_prf_input_len);

			if (s->s3->server_opaque_prf_input == NULL)
				{
				*al = TLS1_AD_INTERNAL_ERROR;
				return 0;
				}
			}
#endif
		else if (type == TLSEXT_TYPE_status_request)
			{
			/* MUST be empty and only sent if we've requested
			 * a status request message.
			 */
			if ((s->tlsext_status_type == -1) || (size > 0))
				{
				*al = TLS1_AD_UNSUPPORTED_EXTENSION;
				return 0;
				}
			/* Set flag to expect CertificateStatus message */
			s->tlsext_status_expected = 1;
			}
#ifndef OPENSSL_NO_NEXTPROTONEG
		else if (type == TLSEXT_TYPE_next_proto_neg &&
			 s->s3->tmp.finish_md_len == 0)
			{
			unsigned char *selected;
			unsigned char selected_len;

			/* We must have requested it. */
			if (s->ctx->next_proto_select_cb == NULL)
				{
				*al = TLS1_AD_UNSUPPORTED_EXTENSION;
				return 0;
				}
			/* The data must be valid */
			if (!ssl_next_proto_validate(data, size))
				{
				*al = TLS1_AD_DECODE_ERROR;
				return 0;
				}
			if (s->ctx->next_proto_select_cb(s, &selected, &selected_len, data, size, s->ctx->next_proto_select_cb_arg) != SSL_TLSEXT_ERR_OK)
				{
				*al = TLS1_AD_INTERNAL_ERROR;
				return 0;
				}
			s->next_proto_negotiated = OPENSSL_malloc(selected_len);
			if (!s->next_proto_negotiated)
				{
				*al = TLS1_AD_INTERNAL_ERROR;
				return 0;
				}
			memcpy(s->next_proto_negotiated, selected, selected_len);
			s->next_proto_negotiated_len = selected_len;
			s->s3->next_proto_neg_seen = 1;
			}
#endif

		else if (type == TLSEXT_TYPE_application_layer_protocol_negotiation)
			{
			unsigned len;

			/* We must have requested it. */
			if (s->alpn_client_proto_list == NULL)
				{
				*al = TLS1_AD_UNSUPPORTED_EXTENSION;
				return 0;
				}
			if (size < 4)
				{
				*al = TLS1_AD_DECODE_ERROR;
				return 0;
				}
			/* The extension data consists of:
			 *   uint16 list_length
			 *   uint8 proto_length;
			 *   uint8 proto[proto_length]; */
			len = data[0];
			len <<= 8;
			len |= data[1];
			if (len != (unsigned) size - 2)
				{
				*al = TLS1_AD_DECODE_ERROR;
				return 0;
				}
			len = data[2];
			if (len != (unsigned) size - 3)
				{
				*al = TLS1_AD_DECODE_ERROR;
				return 0;
				}
			if (s->s3->alpn_selected)
				OPENSSL_free(s->s3->alpn_selected);
			s->s3->alpn_selected = OPENSSL_malloc(len);
			if (!s->s3->alpn_selected)
				{
				*al = TLS1_AD_INTERNAL_ERROR;
				return 0;
				}
			memcpy(s->s3->alpn_selected, data + 3, len);
			s->s3->alpn_selected_len = len;
			}

		else if (type == TLSEXT_TYPE_renegotiate)
			{
			if(!ssl_parse_serverhello_renegotiate_ext(s, data, size, al))
				return 0;
			renegotiate_seen = 1;
			}
#ifndef OPENSSL_NO_HEARTBEATS
		else if (type == TLSEXT_TYPE_heartbeat)
			{
			switch(data[0])
				{
				case 0x01:	/* Server allows us to send HB requests */
							s->tlsext_heartbeat |= SSL_TLSEXT_HB_ENABLED;
							break;
				case 0x02:	/* Server doesn't accept HB requests */
							s->tlsext_heartbeat |= SSL_TLSEXT_HB_ENABLED;
							s->tlsext_heartbeat |= SSL_TLSEXT_HB_DONT_SEND_REQUESTS;
							break;
				default:	*al = SSL_AD_ILLEGAL_PARAMETER;
							return 0;
				}
			}
#endif
		else if (type == TLSEXT_TYPE_use_srtp)
                        {
                        if(ssl_parse_serverhello_use_srtp_ext(s, data, size,
							      al))
                                return 0;
                        }
		/* If this extension type was not otherwise handled, but
		 * matches a custom_cli_ext_record, then send it to the c
		 * callback */
		else if (s->ctx->custom_cli_ext_records_count)
			{
			size_t i;
			custom_cli_ext_record* record;

			for (i = 0; i < s->ctx->custom_cli_ext_records_count; i++)
				{
				record = &s->ctx->custom_cli_ext_records[i];
				if (record->ext_type == type)
					{
					if (record->fn2 && !record->fn2(s, type, data, size, al, record->arg))
						return 0;
					break;
					}
				}
			}
#ifdef TLSEXT_TYPE_encrypt_then_mac
		else if (type == TLSEXT_TYPE_encrypt_then_mac)
			{
			/* Ignore if inappropriate ciphersuite */
			if (s->s3->tmp.new_cipher->algorithm_mac != SSL_AEAD)
				s->s3->flags |= TLS1_FLAGS_ENCRYPT_THEN_MAC;
			}
#endif

		data += size;
		}

	if (data != d+n)
		{
		*al = SSL_AD_DECODE_ERROR;
		return 0;
		}

	if (!s->hit && tlsext_servername == 1)
		{
 		if (s->tlsext_hostname)
			{
			if (s->session->tlsext_hostname == NULL)
				{
				s->session->tlsext_hostname = BUF_strdup(s->tlsext_hostname);
				if (!s->session->tlsext_hostname)
					{
					*al = SSL_AD_UNRECOGNIZED_NAME;
					return 0;
					}
				}
			else
				{
				*al = SSL_AD_DECODE_ERROR;
				return 0;
				}
			}
		}

	*p = data;

	ri_check:

	/* Determine if we need to see RI. Strictly speaking if we want to
	 * avoid an attack we should *always* see RI even on initial server
	 * hello because the client doesn't see any renegotiation during an
	 * attack. However this would mean we could not connect to any server
	 * which doesn't support RI so for the immediate future tolerate RI
	 * absence on initial connect only.
	 */
	if (!renegotiate_seen
		&& !(s->options & SSL_OP_LEGACY_SERVER_CONNECT)
		&& !(s->options & SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION))
		{
		*al = SSL_AD_HANDSHAKE_FAILURE;
		SSLerr(SSL_F_SSL_SCAN_SERVERHELLO_TLSEXT,
				SSL_R_UNSAFE_LEGACY_RENEGOTIATION_DISABLED);
		return 0;
		}

	return 1;
	}

Race condition in the ssl_parse_serverhello_tlsext function in t1_lib.c in OpenSSL 1.0.0 before 1.0.0n and 1.0.1 before 1.0.1i, when multithreading and session resumption are used, allows remote SSL servers to cause a denial of service (memory overwrite and client application crash) or possibly have unspecified other impact by sending Elliptic Curve (EC) Supported Point Formats Extension data.