Commit message: Fix kadm5/gssrpc XDR double free [CVE-2014-9421]

[MITKRB5-SA-2015-001] In auth_gssapi_unwrap_data(), do not free
partial deserialization results upon failure to deserialize.  This
responsibility belongs to the callers, svctcp_getargs() and
svcudp_getargs(); doing it in the unwrap function results in freeing
the results twice.

In xdr_krb5_tl_data() and xdr_krb5_principal(), null out the pointers
we are freeing, as other XDR functions such as xdr_bytes() and
xdr_string().

ticket: 8056 (new)
target_version: 1.13.1
tags: pullup
CVE: CVE-2014-9421
CWE: CWE-703
Vulnerability description: The auth_gssapi_unwrap_data function in lib/rpc/auth_gssapi_misc.c in MIT Kerberos 5 (aka krb5) through 1.11.5, 1.12.x through 1.12.2, and 1.13.x before 1.13.1 does not properly handle partial XDR deserialization, which allows remote authenticated users to cause a denial of service (use-after-free and double free, and daemon crash) or possibly execute arbitrary code via malformed XDR data, as demonstrated by data sent to kadmind.

Project: krb5
File: kadm_rpc_xdr.c

bool_t xdr_krb5_tl_data(XDR *xdrs, krb5_tl_data **tl_data_head)
{
     krb5_tl_data *tl, *tl2;
     bool_t more;
     unsigned int len;

     switch (xdrs->x_op) {
     case XDR_FREE:
	  tl = tl2 = *tl_data_head;
	  while (tl) {
	       tl2 = tl->tl_data_next;
	       free(tl->tl_data_contents);
 	       free(tl);
 	       tl = tl2;
 	  }
	  *tl_data_head = NULL;
 	  break;
 
      case XDR_ENCODE:
	  tl = *tl_data_head;
	  while (1) {
	       more = (tl != NULL);
	       if (!xdr_bool(xdrs, &more))
		    return FALSE;
	       if (tl == NULL)
		    break;
	       if (!xdr_krb5_int16(xdrs, &tl->tl_data_type))
		    return FALSE;
	       len = tl->tl_data_length;
	       if (!xdr_bytes(xdrs, (char **) &tl->tl_data_contents, &len, ~0))
		    return FALSE;
	       tl = tl->tl_data_next;
	  }
	  break;

     case XDR_DECODE:
	  tl = NULL;
	  while (1) {
	       if (!xdr_bool(xdrs, &more))
		    return FALSE;
	       if (more == FALSE)
		    break;
	       tl2 = (krb5_tl_data *) malloc(sizeof(krb5_tl_data));
	       if (tl2 == NULL)
		    return FALSE;
	       memset(tl2, 0, sizeof(krb5_tl_data));
	       if (!xdr_krb5_int16(xdrs, &tl2->tl_data_type))
		    return FALSE;
	       if (!xdr_bytes(xdrs, (char **)&tl2->tl_data_contents, &len, ~0))
		    return FALSE;
	       tl2->tl_data_length = len;

	       tl2->tl_data_next = tl;
	       tl = tl2;
	  }

	  *tl_data_head = tl;
	  break;
     }

     return TRUE;
}

Project: krb5
File: auth_gssapi_misc.c

bool_t auth_gssapi_unwrap_data(
     OM_uint32 *major,
     OM_uint32 *minor,
     gss_ctx_id_t context,
     uint32_t seq_num,
     XDR *in_xdrs,
     bool_t (*xdr_func)(),
     caddr_t xdr_ptr)
{
     gss_buffer_desc in_buf, out_buf;
     XDR temp_xdrs;
     uint32_t verf_seq_num;
     int conf, qop;
     unsigned int length;

     PRINTF(("gssapi_unwrap_data: starting\n"));

     *major = GSS_S_COMPLETE;
     *minor = 0; /* assumption */

     in_buf.value = NULL;
     out_buf.value = NULL;
     if (! xdr_bytes(in_xdrs, (char **) &in_buf.value,
		     &length, (unsigned int) -1)) {
	 PRINTF(("gssapi_unwrap_data: deserializing encrypted data failed\n"));
	 temp_xdrs.x_op = XDR_FREE;
	 (void)xdr_bytes(&temp_xdrs, (char **) &in_buf.value, &length,
			 (unsigned int) -1);
	 return FALSE;
     }
     in_buf.length = length;

     *major = gss_unseal(minor, context, &in_buf, &out_buf, &conf,
			 &qop);
     free(in_buf.value);
     if (*major != GSS_S_COMPLETE)
	  return FALSE;

     PRINTF(("gssapi_unwrap_data: %llu bytes data, %llu bytes sealed\n",
	     (unsigned long long)out_buf.length,
	     (unsigned long long)in_buf.length));

     xdrmem_create(&temp_xdrs, out_buf.value, out_buf.length, XDR_DECODE);

     /* deserialize the sequence number */
     if (! xdr_u_int32(&temp_xdrs, &verf_seq_num)) {
	  PRINTF(("gssapi_unwrap_data: deserializing verf_seq_num failed\n"));
	  gss_release_buffer(minor, &out_buf);
	  XDR_DESTROY(&temp_xdrs);
	  return FALSE;
     }
     if (verf_seq_num != seq_num) {
	  PRINTF(("gssapi_unwrap_data: seq %d specified, read %d\n",
		  seq_num, verf_seq_num));
	  gss_release_buffer(minor, &out_buf);
	  XDR_DESTROY(&temp_xdrs);
	  return FALSE;
     }
     PRINTF(("gssapi_unwrap_data: unwrap seq_num %d okay\n", verf_seq_num));

     /* deserialize the arguments into xdr_ptr */
      if (! (*xdr_func)(&temp_xdrs, xdr_ptr)) {
 	  PRINTF(("gssapi_unwrap_data: deserializing arguments failed\n"));
 	  gss_release_buffer(minor, &out_buf);
 	  XDR_DESTROY(&temp_xdrs);
 	  return FALSE;
      }

     PRINTF(("gssapi_unwrap_data: succeeding\n\n"));

     gss_release_buffer(minor, &out_buf);
     XDR_DESTROY(&temp_xdrs);
     return TRUE;
}