Commit message: Verify decoded kadmin C strings [CVE-2015-8629]

In xdr_nullstring(), check that the decoded string is terminated with
a zero byte and does not contain any internal zero bytes.

CVE-2015-8629:

In all versions of MIT krb5, an authenticated attacker can cause
kadmind to read beyond the end of allocated memory by sending a string
without a terminating zero byte.  Information leakage may be possible
for an attacker with permission to modify the database.

    CVSSv2 Vector: AV:N/AC:H/Au:S/C:P/I:N/A:N/E:POC/RL:OF/RC:C

ticket: 8341 (new)
target_version: 1.14-next
target_version: 1.13-next
tags: pullup
CVE: CVE-2015-8629
CWE: CWE-119
Vulnerability description: The xdr_nullstring function in lib/kadm5/kadm_rpc_xdr.c in kadmind in MIT Kerberos 5 (aka krb5) before 1.13.4 and 1.14.x before 1.14.1 does not verify whether '\0' characters exist as expected, which allows remote authenticated users to obtain sensitive information or cause a denial of service (out-of-bounds read) via a crafted string.

Project: krb5
File: kadm_rpc_xdr.c

bool_t xdr_krb5_key_data_nocontents(XDR *xdrs, krb5_key_data *objp)
{
     /*
      * Note that this function intentionally DOES NOT tranfer key
      * length or contents!  xdr_krb5_key_data in adb_xdr.c does, but
      * that is only for use within the server-side library.
      */
     unsigned int tmp;

     if (xdrs->x_op == XDR_DECODE)
	  memset(objp, 0, sizeof(krb5_key_data));

     if (!xdr_krb5_int16(xdrs, &objp->key_data_ver)) {
	  return (FALSE);
     }
     if (!xdr_krb5_ui_2(xdrs, &objp->key_data_kvno)) {
	  return (FALSE);
     }
     if (!xdr_krb5_int16(xdrs, &objp->key_data_type[0])) {
	  return (FALSE);
     }
     if (objp->key_data_ver > 1) {
	  if (!xdr_krb5_int16(xdrs, &objp->key_data_type[1])) {
	       return (FALSE);
	  }
     }
     /*
      * kadm5_get_principal on the server side allocates and returns
      * key contents when asked.  Even though this function refuses to
      * transmit that data, it still has to *free* the data at the
      * appropriate time to avoid a memory leak.
      */
     if (xdrs->x_op == XDR_FREE) {
	  tmp = (unsigned int) objp->key_data_length[0];
	  if (!xdr_bytes(xdrs, (char **) &objp->key_data_contents[0],
			 &tmp, ~0))
	       return FALSE;

	  tmp = (unsigned int) objp->key_data_length[1];
	  if (!xdr_bytes(xdrs, (char **) &objp->key_data_contents[1],
			 &tmp, ~0))
	       return FALSE;
     }

     return (TRUE);
}

Project: krb5
File: kadm_rpc_xdr.c

xdr_krb5_kvno(XDR *xdrs, krb5_kvno *objp)
{
	return xdr_u_int(xdrs, objp);
}

Project: krb5
File: kadm_rpc_xdr.c

xdr_krb5_principal(XDR *xdrs, krb5_principal *objp)
{
    int	    ret;
    char	    *p = NULL;
    krb5_principal  pr = NULL;
    static krb5_context context = NULL;

    /* using a static context here is ugly, but should work
       ok, and the other solutions are even uglier */

    if (!context &&
	kadm5_init_krb5_context(&context))
       return(FALSE);

    switch(xdrs->x_op) {
    case XDR_ENCODE:
	if (*objp) {
	     if((ret = krb5_unparse_name(context, *objp, &p)) != 0)
		  return FALSE;
	}
	if(!xdr_nullstring(xdrs, &p))
	    return FALSE;
	if (p) free(p);
	break;
    case XDR_DECODE:
	if(!xdr_nullstring(xdrs, &p))
	    return FALSE;
	if (p) {
	     ret = krb5_parse_name(context, p, &pr);
	     if(ret != 0)
		  return FALSE;
	     *objp = pr;
	     free(p);
	} else
	     *objp = NULL;
	break;
    case XDR_FREE:
	if(*objp != NULL)
	    krb5_free_principal(context, *objp);
	*objp = NULL;
	break;
    }
    return TRUE;
}