Commit message: None
CVE: CVE-2017-6414
CWE: CWE-772
Vulnerability description: Memory leak in the vcard_apdu_new function in card_7816.c in libcacard before 2.5.3 allows local guest OS users to cause a denial of service (host memory consumption) via vectors related to allocating a new APDU object.

Project: spice

vcard7816_file_system_process_apdu(VCard *card, VCardAPDU *apdu,
                                   VCardResponse **response)
{
    /* TODO: if we want to support a virtual file system card, we do it here.
     * It would probably be a pkcs #15 card type */
    *response = vcard_make_response(
                    VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED);
    return VCARD_DONE;
}

Project: spice

vcard_apdu_set_class(VCardAPDU *apdu) {
    apdu->a_channel = 0;
    apdu->a_secure_messaging = 0;
    apdu->a_type = apdu->a_cla & 0xf0;
    apdu->a_gen_type = VCARD_7816_ISO;

    /* parse the class  tables 8 & 9 of the 7816-4 Part 4 spec */
    switch (apdu->a_type) {
        /* we only support the basic types */
    case 0x00:
    case 0x80:
    case 0x90:
    case 0xa0:
        apdu->a_channel = apdu->a_cla & 3;
        apdu->a_secure_messaging = apdu->a_cla & 0xe;
        break;
    case 0xb0:
    case 0xc0:
        break;

    case 0x10:
    case 0x20:
    case 0x30:
    case 0x40:
    case 0x50:
    case 0x60:
    case 0x70:
        /* Reserved for future use */
        apdu->a_gen_type = VCARD_7816_RFU;
        break;
    case 0xd0:
    case 0xe0:
    case 0xf0:
    default:
        apdu->a_gen_type =
            (apdu->a_cla == 0xff) ? VCARD_7816_PTS : VCARD_7816_PROPRIETARY;
        break;
    }
    return VCARD7816_STATUS_SUCCESS;
}

Project: spice

vcard_apdu_set_length(VCardAPDU *apdu)
{
    int L, Le;

    /* process according to table 5 of the 7816-4 Part 4 spec.
     * variable names match the variables in the spec */
    L = apdu->a_len-4; /* fixed APDU header */
    apdu->a_Lc = 0;
    apdu->a_Le = 0;
    apdu->a_body = NULL;
    switch (L) {
    case 0:
        /* 1 minimal apdu */
        return VCARD7816_STATUS_SUCCESS;
    case 1:
        /* 2S only return values apdu */
        /*   zero maps to 256 here */
        apdu->a_Le = apdu->a_header->ah_Le ?
                         apdu->a_header->ah_Le : 256;
        return VCARD7816_STATUS_SUCCESS;
    default:
        /* if the ah_Le byte is zero and we have more than
         * 1 byte in the header, then we must be using extended Le and Lc.
         * process the extended now. */
        if (apdu->a_header->ah_Le == 0) {
            if (L < 3) {
                /* coding error, need at least 3 bytes */
                return VCARD7816_STATUS_ERROR_WRONG_LENGTH;
            }
            /* calculate the first extended value. Could be either Le or Lc */
            Le = (apdu->a_header->ah_body[0] << 8)
                | apdu->a_header->ah_body[1];
            if (L == 3) {
                /* 2E extended, return data only */
                /*   zero maps to 65536 */
                apdu->a_Le = Le ? Le : 65536;
                return VCARD7816_STATUS_SUCCESS;
            }
            if (Le == 0) {
                /* reserved for future use, probably for next time we need
                 * to extend the lengths */
                return VCARD7816_STATUS_ERROR_WRONG_LENGTH;
            }
            /* we know that the first extended value is Lc now */
            apdu->a_Lc = Le;
            apdu->a_body = &apdu->a_header->ah_body[2];
            if (L == Le+3) {
                /* 3E extended, only body parameters */
                return VCARD7816_STATUS_SUCCESS;
            }
            if (L == Le+5) {
                /* 4E extended, parameters and return data */
                Le = (apdu->a_data[apdu->a_len-2] << 8)
                    | apdu->a_data[apdu->a_len-1];
                apdu->a_Le = Le ? Le : 65536;
                return VCARD7816_STATUS_SUCCESS;
            }
            return VCARD7816_STATUS_ERROR_WRONG_LENGTH;
        }
        /* not extended */
        apdu->a_Lc = apdu->a_header->ah_Le;
        apdu->a_body = &apdu->a_header->ah_body[0];
        if (L ==  apdu->a_Lc + 1) {
            /* 3S only body parameters */
            return VCARD7816_STATUS_SUCCESS;
        }
        if (L ==  apdu->a_Lc + 2) {
            /* 4S parameters and return data */
            Le = apdu->a_data[apdu->a_len-1];
            apdu->a_Le = Le ?  Le : 256;
            return VCARD7816_STATUS_SUCCESS;
        }
        break;
    }
    return VCARD7816_STATUS_ERROR_WRONG_LENGTH;
}

Project: spice

vcard_make_response(vcard_7816_status_t status)
{
    VCardResponse *response;

    switch (status) {
    /* known 7816 response codes */
    case VCARD7816_STATUS_SUCCESS:
        return VCARD_RESPONSE_GET_STATIC(
                    VCARD7816_STATUS_SUCCESS);
    case VCARD7816_STATUS_WARNING:
        return VCARD_RESPONSE_GET_STATIC(
                    VCARD7816_STATUS_WARNING);
    case VCARD7816_STATUS_WARNING_RET_CORUPT:
        return VCARD_RESPONSE_GET_STATIC(
                    VCARD7816_STATUS_WARNING_RET_CORUPT);
    case VCARD7816_STATUS_WARNING_BUF_END_BEFORE_LE:
        return VCARD_RESPONSE_GET_STATIC(
                    VCARD7816_STATUS_WARNING_BUF_END_BEFORE_LE);
    case VCARD7816_STATUS_WARNING_INVALID_FILE_SELECTED:
        return VCARD_RESPONSE_GET_STATIC(
                    VCARD7816_STATUS_WARNING_INVALID_FILE_SELECTED);
    case VCARD7816_STATUS_WARNING_FCI_FORMAT_INVALID:
        return VCARD_RESPONSE_GET_STATIC(
                    VCARD7816_STATUS_WARNING_FCI_FORMAT_INVALID);
    case VCARD7816_STATUS_WARNING_CHANGE:
        return VCARD_RESPONSE_GET_STATIC(
                    VCARD7816_STATUS_WARNING_CHANGE);
    case VCARD7816_STATUS_WARNING_FILE_FILLED:
        return VCARD_RESPONSE_GET_STATIC(
                    VCARD7816_STATUS_WARNING_FILE_FILLED);
    case VCARD7816_STATUS_EXC_ERROR:
        return VCARD_RESPONSE_GET_STATIC(
                    VCARD7816_STATUS_EXC_ERROR);
    case VCARD7816_STATUS_EXC_ERROR_CHANGE:
        return VCARD_RESPONSE_GET_STATIC(
                    VCARD7816_STATUS_EXC_ERROR_CHANGE);
    case VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE:
        return VCARD_RESPONSE_GET_STATIC(
                    VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE);
    case VCARD7816_STATUS_ERROR_WRONG_LENGTH:
        return VCARD_RESPONSE_GET_STATIC(
                    VCARD7816_STATUS_ERROR_WRONG_LENGTH);
    case VCARD7816_STATUS_ERROR_CLA_NOT_SUPPORTED:
        return VCARD_RESPONSE_GET_STATIC(
                    VCARD7816_STATUS_ERROR_CLA_NOT_SUPPORTED);
    case VCARD7816_STATUS_ERROR_CHANNEL_NOT_SUPPORTED:
        return VCARD_RESPONSE_GET_STATIC(
                    VCARD7816_STATUS_ERROR_CHANNEL_NOT_SUPPORTED);
    case VCARD7816_STATUS_ERROR_SECURE_NOT_SUPPORTED:
        return VCARD_RESPONSE_GET_STATIC(
                    VCARD7816_STATUS_ERROR_SECURE_NOT_SUPPORTED);
    case VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED:
        return VCARD_RESPONSE_GET_STATIC(
                    VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED);
    case VCARD7816_STATUS_ERROR_COMMAND_INCOMPATIBLE_WITH_FILE:
        return VCARD_RESPONSE_GET_STATIC(
                    VCARD7816_STATUS_ERROR_COMMAND_INCOMPATIBLE_WITH_FILE);
    case VCARD7816_STATUS_ERROR_SECURITY_NOT_SATISFIED:
        return VCARD_RESPONSE_GET_STATIC(
                    VCARD7816_STATUS_ERROR_SECURITY_NOT_SATISFIED);
    case VCARD7816_STATUS_ERROR_AUTHENTICATION_BLOCKED:
        return VCARD_RESPONSE_GET_STATIC(
                    VCARD7816_STATUS_ERROR_AUTHENTICATION_BLOCKED);
    case VCARD7816_STATUS_ERROR_DATA_INVALID:
        return VCARD_RESPONSE_GET_STATIC(
                    VCARD7816_STATUS_ERROR_DATA_INVALID);
    case VCARD7816_STATUS_ERROR_CONDITION_NOT_SATISFIED:
        return VCARD_RESPONSE_GET_STATIC(
                    VCARD7816_STATUS_ERROR_CONDITION_NOT_SATISFIED);
    case VCARD7816_STATUS_ERROR_DATA_NO_EF:
        return VCARD_RESPONSE_GET_STATIC(
                    VCARD7816_STATUS_ERROR_DATA_NO_EF);
    case VCARD7816_STATUS_ERROR_SM_OBJECT_MISSING:
        return VCARD_RESPONSE_GET_STATIC(
                    VCARD7816_STATUS_ERROR_SM_OBJECT_MISSING);
    case VCARD7816_STATUS_ERROR_SM_OBJECT_INCORRECT:
        return VCARD_RESPONSE_GET_STATIC(
                    VCARD7816_STATUS_ERROR_SM_OBJECT_INCORRECT);
    case VCARD7816_STATUS_ERROR_WRONG_PARAMETERS:
        return VCARD_RESPONSE_GET_STATIC(
                    VCARD7816_STATUS_ERROR_WRONG_PARAMETERS);
    case VCARD7816_STATUS_ERROR_WRONG_PARAMETERS_IN_DATA:
        return VCARD_RESPONSE_GET_STATIC(
                    VCARD7816_STATUS_ERROR_WRONG_PARAMETERS_IN_DATA);
    case VCARD7816_STATUS_ERROR_FUNCTION_NOT_SUPPORTED:
        return VCARD_RESPONSE_GET_STATIC(
                    VCARD7816_STATUS_ERROR_FUNCTION_NOT_SUPPORTED);
    case VCARD7816_STATUS_ERROR_FILE_NOT_FOUND:
        return VCARD_RESPONSE_GET_STATIC(
                    VCARD7816_STATUS_ERROR_FILE_NOT_FOUND);
    case VCARD7816_STATUS_ERROR_RECORD_NOT_FOUND:
        return VCARD_RESPONSE_GET_STATIC(
                    VCARD7816_STATUS_ERROR_RECORD_NOT_FOUND);
    case VCARD7816_STATUS_ERROR_NO_SPACE_FOR_FILE:
        return VCARD_RESPONSE_GET_STATIC(
                    VCARD7816_STATUS_ERROR_NO_SPACE_FOR_FILE);
    case VCARD7816_STATUS_ERROR_LC_TLV_INCONSISTENT:
        return VCARD_RESPONSE_GET_STATIC(
                    VCARD7816_STATUS_ERROR_LC_TLV_INCONSISTENT);
    case VCARD7816_STATUS_ERROR_P1_P2_INCORRECT:
        return VCARD_RESPONSE_GET_STATIC(
                    VCARD7816_STATUS_ERROR_P1_P2_INCORRECT);
    case VCARD7816_STATUS_ERROR_LC_P1_P2_INCONSISTENT:
        return VCARD_RESPONSE_GET_STATIC(
                    VCARD7816_STATUS_ERROR_LC_P1_P2_INCONSISTENT);
    case VCARD7816_STATUS_ERROR_DATA_NOT_FOUND:
        return VCARD_RESPONSE_GET_STATIC(
                    VCARD7816_STATUS_ERROR_DATA_NOT_FOUND);
    case VCARD7816_STATUS_ERROR_WRONG_PARAMETERS_2:
        return VCARD_RESPONSE_GET_STATIC(
                    VCARD7816_STATUS_ERROR_WRONG_PARAMETERS_2);
    case VCARD7816_STATUS_ERROR_INS_CODE_INVALID:
        return VCARD_RESPONSE_GET_STATIC(
                    VCARD7816_STATUS_ERROR_INS_CODE_INVALID);
    case VCARD7816_STATUS_ERROR_CLA_INVALID:
        return VCARD_RESPONSE_GET_STATIC(
                    VCARD7816_STATUS_ERROR_CLA_INVALID);
    case VCARD7816_STATUS_ERROR_GENERAL:
        return VCARD_RESPONSE_GET_STATIC(
                    VCARD7816_STATUS_ERROR_GENERAL);
    default:
        /* we don't know this status code, create a response buffer to
         * hold it */
        response = vcard_response_new_status(status);
        if (response == NULL) {
            /* couldn't allocate the buffer, return memmory error */
            return VCARD_RESPONSE_GET_STATIC(
                        VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE);
        }
        return response;
    }
}

Project: spice

vcard_process_apdu(VCard *card, VCardAPDU *apdu, VCardResponse **response)
{
    VCardStatus status;
    VCardBufferResponse *buffer_response;

    /* first handle any PTS commands, which aren't really APDU's */
    if (apdu->a_type == VCARD_7816_PTS) {
        /* the PTS responses aren't really responses either */
        *response = vcard_response_new_data(apdu->a_data, apdu->a_len);
        /* PTS responses have no status bytes */
        (*response)->b_total_len = (*response)->b_len;
        return VCARD_DONE;
    }
    buffer_response = vcard_get_buffer_response(card);
    if (buffer_response && apdu->a_ins != VCARD7816_INS_GET_RESPONSE) {
        /* clear out buffer_response, do not return an error */
        vcard_set_buffer_response(card, NULL);
        vcard_buffer_response_delete(buffer_response);
    }

    status = vcard_process_applet_apdu(card, apdu, response);
    if (status != VCARD_NEXT) {
        return status;
    }
    switch (vcard_get_type(card)) {
    case VCARD_FILE_SYSTEM:
        return vcard7816_file_system_process_apdu(card, apdu, response);
    case VCARD_VM:
        return vcard7816_vm_process_apdu(card, apdu, response);
    case VCARD_DIRECT:
        /* if we are type direct, then the applet should handle everything */
        assert(!"VCARD_DIRECT: applet failure");
        break;
    default:
        g_warn_if_reached();
    }
    *response =
        vcard_make_response(VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED);
    return VCARD_DONE;
}

Project: spice

vcard_response_delete(VCardResponse *response)
{
    if (response == NULL) {
        return;
    }
    switch (response->b_type) {
    case VCARD_MALLOC:
        /* everything was malloc'ed */
        g_free(response->b_data);
        g_free(response);
        break;
    case VCARD_MALLOC_DATA:
        /* only the data buffer was malloc'ed */
        g_free(response->b_data);
        break;
    case VCARD_MALLOC_STRUCT:
        /* only the structure was malloc'ed */
        g_free(response);
        break;
    case VCARD_STATIC:
        break;
    default:
        g_warn_if_reached();
    }
}

Project: spice

vcard_response_new(VCard *card, unsigned char *buf,
                   int len, int Le, vcard_7816_status_t status)
{
    VCardResponse *new_response;

    if (len > Le) {
        return vcard_init_buffer_response(card, buf, len);
    }
    new_response = vcard_response_new_data(buf, len);
    if (new_response == NULL) {
        return NULL;
    }
    vcard_response_set_status(new_response, status);
    return new_response;
}

Project: spice

vcard_response_new_bytes(VCard *card, unsigned char *buf, int len, int Le,
                         unsigned char sw1, unsigned char sw2)
{
    VCardResponse *new_response;

    if (len > Le) {
        return vcard_init_buffer_response(card, buf, len);
    }
    new_response = vcard_response_new_data(buf, len);
    if (new_response == NULL) {
        return NULL;
    }
    vcard_response_set_status_bytes(new_response, sw1, sw2);
    return new_response;
}

Project: spice

vcard_response_new_data(unsigned char *buf, int len)
{
    VCardResponse *new_response;

    new_response = g_new(VCardResponse, 1);
    new_response->b_data = g_malloc(len + 2);
    memcpy(new_response->b_data, buf, len);
    new_response->b_total_len = len+2;
    new_response->b_len = len;
    new_response->b_type = VCARD_MALLOC;
    return new_response;
}

Project: spice

vcard_response_new_status(vcard_7816_status_t status)
{
    VCardResponse *new_response;

    new_response = g_new(VCardResponse, 1);
    new_response->b_data = &new_response->b_sw1;
    new_response->b_len = 0;
    new_response->b_total_len = 2;
    new_response->b_type = VCARD_MALLOC_STRUCT;
    vcard_response_set_status(new_response, status);
    return new_response;
}

Project: spice

vcard_response_set_status(VCardResponse *response, vcard_7816_status_t status)
{
    unsigned char sw1, sw2;
    response->b_status = status; /* make sure the status and swX representations
                                  * are consistent */
    sw1 = (status >> 8) & 0xff;
    sw2 = status & 0xff;
    response->b_sw1 = sw1;
    response->b_sw2 = sw2;
    response->b_data[response->b_len] = sw1;
    response->b_data[response->b_len+1] = sw2;
}

Project: spice

vcard_response_set_status_bytes(VCardResponse *response,
                               unsigned char sw1, unsigned char sw2)
{
    response->b_status = sw1 << 8 | sw2;
    response->b_sw1 = sw1;
    response->b_sw2 = sw2;
    response->b_data[response->b_len] = sw1;
    response->b_data[response->b_len+1] = sw2;
}