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

xdr_generic_ret(XDR *xdrs, generic_ret *objp)
{
	if (!xdr_ui_4(xdrs, &objp->api_version)) {
		return (FALSE);
	}
	if (!xdr_kadm5_ret_t(xdrs, &objp->code)) {
		return (FALSE);
	}

	return(TRUE);
}

Project: krb5
File: kadm_rpc_xdr.c

xdr_gstrings_arg(XDR *xdrs, gstrings_arg *objp)
{
	if (!xdr_ui_4(xdrs, &objp->api_version)) {
		return (FALSE);
	}
	if (!xdr_krb5_principal(xdrs, &objp->princ)) {
		return (FALSE);
	}

	return (TRUE);
}

Project: krb5
File: kadm_rpc_xdr.c

xdr_kadm5_policy_ent_rec(XDR *xdrs, kadm5_policy_ent_rec *objp)
{
	return _xdr_kadm5_policy_ent_rec(xdrs, objp, KADM5_API_VERSION_4);
}

Project: krb5
File: kadm_rpc_xdr.c

xdr_krb5_keyblock(XDR *xdrs, krb5_keyblock *objp)
{
   /* XXX This only works because free_keyblock assumes ->contents
      is allocated by malloc() */

   if(!xdr_krb5_enctype(xdrs, &objp->enctype))
      return FALSE;
   if(!xdr_bytes(xdrs, (char **) &objp->contents, (unsigned int *)
		 &objp->length, ~0))
      return FALSE;
   return TRUE;
}

Project: krb5
File: kadm_rpc_xdr.c

xdr_krb5_octet(XDR *xdrs, krb5_octet *objp)
{
   if (!xdr_u_char(xdrs, objp))
	return (FALSE);
   return (TRUE);
}

Project: krb5
File: kadm_rpc_xdr.c

xdr_setv4key_arg(XDR *xdrs, setv4key_arg *objp)
{
	unsigned int n_keys = 1;

	if (!xdr_ui_4(xdrs, &objp->api_version)) {
		return (FALSE);
	}
	if (!xdr_krb5_principal(xdrs, &objp->princ)) {
		return (FALSE);
	}
	if (!xdr_array(xdrs, (caddr_t *) &objp->keyblock,
		       &n_keys, ~0,
		       sizeof(krb5_keyblock), xdr_krb5_keyblock)) {
		return (FALSE);
	}
	return (TRUE);
}

Project: krb5
File: auth_gssapi_misc.c

void auth_gssapi_display_status(
     char *msg,
     OM_uint32 major,
     OM_uint32 minor)
{
     auth_gssapi_display_status_1(msg, major, GSS_C_GSS_CODE, 0);
     auth_gssapi_display_status_1(msg, minor, GSS_C_MECH_CODE, 0);
}

Project: krb5
File: auth_gssapi_misc.c

bool_t auth_gssapi_unseal_seq(
     gss_ctx_id_t context,
     gss_buffer_t in_buf,
     uint32_t *seq_num)
{
     gss_buffer_desc out_buf;
     OM_uint32 gssstat, minor_stat;
     uint32_t nl_seq_num;

     gssstat = gss_unseal(&minor_stat, context, in_buf, &out_buf,
			  NULL, NULL);
     if (gssstat != GSS_S_COMPLETE) {
	  PRINTF(("gssapi_unseal_seq: failed\n"));
	  AUTH_GSSAPI_DISPLAY_STATUS(("unsealing sequence number",
				      gssstat, minor_stat));
	  return FALSE;
     } else if (out_buf.length != sizeof(uint32_t)) {
	  PRINTF(("gssapi_unseal_seq: unseal gave %d bytes\n",
		  (int) out_buf.length));
	  gss_release_buffer(&minor_stat, &out_buf);
	  return FALSE;
     }

     nl_seq_num = *((uint32_t *) out_buf.value);
     *seq_num = (uint32_t) ntohl(nl_seq_num);
     gss_release_buffer(&minor_stat, &out_buf);

     return TRUE;
}