void
Fiid_obj_clear_field(fiid_obj_t obj, char *field)
{
  int8_t rv;

  assert(fiid_obj_valid(obj) && field);

  if ((rv = fiid_obj_clear_field(obj, field)) < 0)
    ierr_exit("Fiid_obj_clear_field: %s", strerror(errno)); 
}
int
ipmiconsole_check_rakp_4_integrity_check_value (ipmiconsole_ctx_t c, ipmiconsole_packet_type_t p)
{
    uint8_t managed_system_guid[IPMI_MANAGED_SYSTEM_GUID_LENGTH];
    int managed_system_guid_len;
    uint32_t managed_system_session_id;
    uint8_t authentication_algorithm = 0;
    uint64_t val;
    int rv;

    assert (c);
    assert (c->magic == IPMICONSOLE_CTX_MAGIC);
    assert (p == IPMICONSOLE_PACKET_TYPE_RAKP_MESSAGE_4);

    /* IPMI Workaround
     *
     * Intel IPMI 2.0 implementations respond with the integrity check
     * value based on the integrity algorithm rather than the
     * authentication algorithm.
     */
    if (c->config.workaround_flags & IPMICONSOLE_WORKAROUND_INTEL_2_0_SESSION)
    {
        if (c->config.integrity_algorithm == IPMI_INTEGRITY_ALGORITHM_NONE)
            authentication_algorithm = IPMI_AUTHENTICATION_ALGORITHM_RAKP_NONE;
        else if (c->config.integrity_algorithm == IPMI_INTEGRITY_ALGORITHM_HMAC_SHA1_96)
            authentication_algorithm = IPMI_AUTHENTICATION_ALGORITHM_RAKP_HMAC_SHA1;
        else if (c->config.integrity_algorithm == IPMI_INTEGRITY_ALGORITHM_HMAC_MD5_128)
            authentication_algorithm = IPMI_AUTHENTICATION_ALGORITHM_RAKP_HMAC_MD5;
        else if (c->config.integrity_algorithm == IPMI_INTEGRITY_ALGORITHM_MD5_128)
        {
            /* achu: I have thus far been unable to reverse engineer this
             * corner case.  Since we cannot provide a reasonable two
             * part authentication, we're going to error out.
             */
            IPMICONSOLE_CTX_DEBUG (c, ("Intel Non-Compliance: Cannot Reverse Engineer"));
            ipmiconsole_ctx_set_errnum (c, IPMICONSOLE_ERR_BMC_ERROR);
            return (0);
        }
    }
    else
        authentication_algorithm = c->config.authentication_algorithm;

    if (FIID_OBJ_GET (c->connection.obj_open_session_response,
                      "managed_system_session_id",
                      &val) < 0)
    {
        IPMICONSOLE_CTX_DEBUG (c, ("FIID_OBJ_GET: 'managed_system_session_id': %s",
                                   fiid_obj_errormsg (c->connection.obj_open_session_response)));
        ipmiconsole_ctx_set_errnum (c, IPMICONSOLE_ERR_INTERNAL_ERROR);
        return (-1);
    }
    managed_system_session_id = val;

    if ((managed_system_guid_len = fiid_obj_get_data (c->connection.obj_rakp_message_2,
                                   "managed_system_guid",
                                   managed_system_guid,
                                   IPMI_MANAGED_SYSTEM_RANDOM_NUMBER_LENGTH)) < 0)
    {
        IPMICONSOLE_CTX_DEBUG (c, ("fiid_obj_get_data: 'managed_system_guid': %s",
                                   fiid_obj_errormsg (c->connection.obj_rakp_message_2)));
        ipmiconsole_ctx_set_errnum (c, IPMICONSOLE_ERR_INTERNAL_ERROR);
        return (-1);
    }

    if (managed_system_guid_len != IPMI_MANAGED_SYSTEM_GUID_LENGTH)
    {
        IPMICONSOLE_CTX_DEBUG (c, ("fiid_obj_get_data: invalid managed system guid length: %d",
                                   managed_system_guid_len));
        ipmiconsole_ctx_set_errnum (c, IPMICONSOLE_ERR_INTERNAL_ERROR);
        return (-1);
    }

    /* IPMI Workaround (achu)
     *
     * Discovered on Supermicro X8DTG, Supermicro X8DTU, Intel
     * S5500WBV/Penguin Relion 700
     *
     * For whatever reason, with cipher suite 0, the RAKP 4 response
     * returns with an Integrity Check Value when it should be empty.
     */

    if (c->config.workaround_flags & IPMICONSOLE_WORKAROUND_NON_EMPTY_INTEGRITY_CHECK_VALUE
            && !c->config.cipher_suite_id)
    {
        if (fiid_obj_clear_field (c->connection.obj_rakp_message_4,
                                  "integrity_check_value") < 0)
        {
            IPMICONSOLE_CTX_DEBUG (c, ("fiid_obj_clear_field: 'integrity_check_value': %s",
                                       fiid_obj_errormsg (c->connection.obj_rakp_message_4)));
            ipmiconsole_ctx_set_errnum (c, IPMICONSOLE_ERR_INTERNAL_ERROR);
            return (-1);
        }
    }

    if ((rv = ipmi_rmcpplus_check_rakp_4_integrity_check_value (authentication_algorithm,
              c->session.sik_key_ptr,
              c->session.sik_key_len,
              c->session.remote_console_random_number,
              IPMI_REMOTE_CONSOLE_RANDOM_NUMBER_LENGTH,
              managed_system_session_id,
              managed_system_guid,
              managed_system_guid_len,
              c->connection.obj_rakp_message_4)) < 0)
    {
        IPMICONSOLE_CTX_DEBUG (c, ("ipmi_rmcpplus_check_rakp_4_integrity_check_value: p = %d; %s", p, strerror (errno)));
        ipmiconsole_ctx_set_errnum (c, IPMICONSOLE_ERR_INTERNAL_ERROR);
        return (-1);
    }

    if (!rv)
        IPMICONSOLE_CTX_DEBUG (c, ("rakp 4 integrity check value check failed; p = %d", p));

    return (rv);
}
int
ipmiconsole_check_rakp_2_key_exchange_authentication_code (ipmiconsole_ctx_t c, ipmiconsole_packet_type_t p)
{
    uint8_t managed_system_random_number[IPMI_MANAGED_SYSTEM_RANDOM_NUMBER_LENGTH];
    int managed_system_random_number_len;
    uint8_t managed_system_guid[IPMI_MANAGED_SYSTEM_GUID_LENGTH];
    int managed_system_guid_len;
    char username_buf[IPMI_MAX_USER_NAME_LENGTH+1];
    char *username;
    unsigned int username_len;
    char *password;
    unsigned int password_len;
    uint32_t managed_system_session_id;
    uint64_t val;
    int rv;

    assert (c);
    assert (c->magic == IPMICONSOLE_CTX_MAGIC);
    assert (p == IPMICONSOLE_PACKET_TYPE_RAKP_MESSAGE_2);

    /* IPMI Workaround
     *
     * Intel IPMI 2.0 implementations pad their usernames.
     */
    if (c->config.workaround_flags & IPMICONSOLE_WORKAROUND_INTEL_2_0_SESSION)
    {
        memset (username_buf, '\0', IPMI_MAX_USER_NAME_LENGTH+1);
        if (strlen (c->config.username))
            strcpy (username_buf, c->config.username);
        username = username_buf;
        username_len = IPMI_MAX_USER_NAME_LENGTH;
    }
    else
    {
        if (strlen (c->config.username))
            username = c->config.username;
        else
            username = NULL;
        username_len = (username) ? strlen (username) : 0;
    }

    /* IPMI Workaround
     *
     * Supermicro IPMI 2.0 implementations may have invalid payload lengths
     * on the RAKP response packet.
     */
    if (c->config.workaround_flags & IPMICONSOLE_WORKAROUND_SUPERMICRO_2_0_SESSION)
    {
        uint8_t keybuf[IPMICONSOLE_PACKET_BUFLEN];
        int keybuf_len;

        if ((keybuf_len = fiid_obj_get_data (c->connection.obj_rakp_message_2,
                                             "key_exchange_authentication_code",
                                             keybuf,
                                             IPMICONSOLE_PACKET_BUFLEN)) < 0)
        {
            IPMICONSOLE_CTX_DEBUG (c, ("fiid_obj_get_data: 'key_exchange_authentication_code': %s",
                                       fiid_obj_errormsg (c->connection.obj_rakp_message_2)));
            ipmiconsole_ctx_set_errnum (c, IPMICONSOLE_ERR_INTERNAL_ERROR);
            return (-1);
        }

        if (c->config.authentication_algorithm == IPMI_AUTHENTICATION_ALGORITHM_RAKP_NONE
                && keybuf_len == 1)
        {
            if (fiid_obj_clear_field (c->connection.obj_rakp_message_2,
                                      "key_exchange_authentication_code") < 0)
            {
                IPMICONSOLE_CTX_DEBUG (c, ("fiid_obj_clear_field: 'key_exchange_authentication_code': %s",
                                           fiid_obj_errormsg (c->connection.obj_rakp_message_2)));
                ipmiconsole_ctx_set_errnum (c, IPMICONSOLE_ERR_INTERNAL_ERROR);
                return (-1);
            }
        }
        else if (c->config.authentication_algorithm == IPMI_AUTHENTICATION_ALGORITHM_RAKP_HMAC_SHA1
                 && keybuf_len == (IPMI_HMAC_SHA1_DIGEST_LENGTH + 1))
        {
            if (fiid_obj_set_data (c->connection.obj_rakp_message_2,
                                   "key_exchange_authentication_code",
                                   keybuf,
                                   IPMI_HMAC_SHA1_DIGEST_LENGTH) < 0)
            {
                IPMICONSOLE_CTX_DEBUG (c, ("fiid_obj_set_data: 'key_exchange_authentication_code': %s",
                                           fiid_obj_errormsg (c->connection.obj_rakp_message_2)));
                ipmiconsole_ctx_set_errnum (c, IPMICONSOLE_ERR_INTERNAL_ERROR);
                return (-1);
            }
        }
        else if (c->config.authentication_algorithm == IPMI_AUTHENTICATION_ALGORITHM_RAKP_HMAC_MD5
                 && keybuf_len == (IPMI_HMAC_MD5_DIGEST_LENGTH + 1))
        {
            if (fiid_obj_set_data (c->connection.obj_rakp_message_2,
                                   "key_exchange_authentication_code",
                                   keybuf,
                                   IPMI_HMAC_MD5_DIGEST_LENGTH) < 0)
            {
                IPMICONSOLE_CTX_DEBUG (c, ("fiid_obj_set_data: 'key_exchange_authentication_code': %s",
                                           fiid_obj_errormsg (c->connection.obj_rakp_message_2)));
                ipmiconsole_ctx_set_errnum (c, IPMICONSOLE_ERR_INTERNAL_ERROR);
                return (-1);
            }
        }
    }

    if (strlen (c->config.password))
        password = c->config.password;
    else
        password = NULL;
    password_len = (password) ? strlen (password) : 0;

    /* IPMI Workaround
     *
     * Intel IPMI 2.0 implementations improperly calculate HMAC-MD5-128 hashes
     * when the passwords are > 16 bytes long.  The BMCs probably assume
     * all keys are <= 16 bytes in length.  So we have to adjust.
     */
    if (c->config.workaround_flags & IPMICONSOLE_WORKAROUND_INTEL_2_0_SESSION
            && c->config.authentication_algorithm == IPMI_AUTHENTICATION_ALGORITHM_RAKP_HMAC_MD5
            && password_len > IPMI_1_5_MAX_PASSWORD_LENGTH)
        password_len = IPMI_1_5_MAX_PASSWORD_LENGTH;

    /* IPMI Workaround
     *
     * Discovered on Sun Fire 4100.
     *
     * The key exchange authentication code is the wrong length.  We
     * need to shorten it.
     */
    if (c->config.workaround_flags & IPMICONSOLE_WORKAROUND_SUN_2_0_SESSION
            && c->config.authentication_algorithm == IPMI_AUTHENTICATION_ALGORITHM_RAKP_HMAC_SHA1)
    {
        uint8_t buf[IPMI_MAX_KEY_EXCHANGE_AUTHENTICATION_CODE_LENGTH];
        int buf_len;

        /* trace, but do not return potential error */

        if ((buf_len = fiid_obj_get_data (c->connection.obj_rakp_message_2,
                                          "key_exchange_authentication_code",
                                          buf,
                                          IPMI_MAX_KEY_EXCHANGE_AUTHENTICATION_CODE_LENGTH)) < 0)
            IPMICONSOLE_CTX_DEBUG (c, ("fiid_obj_get_data: 'key_exchange_authentication_code': %s",
                                       fiid_obj_errormsg (c->connection.obj_rakp_message_2)));

        if (buf_len == (IPMI_HMAC_SHA1_DIGEST_LENGTH + 1))
        {
            /* trace, but do not return potential error */

            if (fiid_obj_clear_field (c->connection.obj_rakp_message_2,
                                      "key_exchange_authentication_code") < 0)
                IPMICONSOLE_CTX_DEBUG (c, ("fiid_obj_clear_field: 'key_exchange_authentication_code': %s",
                                           fiid_obj_errormsg (c->connection.obj_rakp_message_2)));

            if (fiid_obj_set_data (c->connection.obj_rakp_message_2,
                                   "key_exchange_authentication_code",
                                   buf,
                                   IPMI_HMAC_SHA1_DIGEST_LENGTH) < 0)
                IPMICONSOLE_CTX_DEBUG (c, ("fiid_obj_set_data: 'key_exchange_authentication_code': %s",
                                           fiid_obj_errormsg (c->connection.obj_rakp_message_2)));
        }
    }


    if (FIID_OBJ_GET (c->connection.obj_open_session_response,
                      "managed_system_session_id",
                      &val) < 0)
    {
        IPMICONSOLE_CTX_DEBUG (c, ("FIID_OBJ_GET: 'managed_system_session_id': %s",
                                   fiid_obj_errormsg (c->connection.obj_open_session_response)));
        ipmiconsole_ctx_set_errnum (c, IPMICONSOLE_ERR_INTERNAL_ERROR);
        return (-1);
    }
    managed_system_session_id = val;

    if ((managed_system_random_number_len = fiid_obj_get_data (c->connection.obj_rakp_message_2,
                                            "managed_system_random_number",
                                            managed_system_random_number,
                                            IPMI_MANAGED_SYSTEM_RANDOM_NUMBER_LENGTH)) < 0)
    {
        IPMICONSOLE_CTX_DEBUG (c, ("fiid_obj_get_data: 'managed_system_random_number': %s",
                                   fiid_obj_errormsg (c->connection.obj_rakp_message_2)));
        ipmiconsole_ctx_set_errnum (c, IPMICONSOLE_ERR_INTERNAL_ERROR);
        return (-1);
    }

    if (managed_system_random_number_len != IPMI_MANAGED_SYSTEM_RANDOM_NUMBER_LENGTH)
    {
        IPMICONSOLE_CTX_DEBUG (c, ("fiid_obj_get_data: invalid managed system random number length: %d",
                                   managed_system_random_number_len));
        ipmiconsole_ctx_set_errnum (c, IPMICONSOLE_ERR_INTERNAL_ERROR);
        return (-1);
    }

    if ((managed_system_guid_len = fiid_obj_get_data (c->connection.obj_rakp_message_2,
                                   "managed_system_guid",
                                   managed_system_guid,
                                   IPMI_MANAGED_SYSTEM_GUID_LENGTH)) < 0)
    {
        IPMICONSOLE_CTX_DEBUG (c, ("fiid_obj_get_data: 'managed_system_guid': %s",
                                   fiid_obj_errormsg (c->connection.obj_rakp_message_2)));
        ipmiconsole_ctx_set_errnum (c, IPMICONSOLE_ERR_INTERNAL_ERROR);
        return (-1);
    }

    if (managed_system_guid_len != IPMI_MANAGED_SYSTEM_GUID_LENGTH)
    {
        IPMICONSOLE_CTX_DEBUG (c, ("fiid_obj_get_data: invalid managed system guid length: %d",
                                   managed_system_guid_len));
        ipmiconsole_ctx_set_errnum (c, IPMICONSOLE_ERR_INTERNAL_ERROR);
        return (-1);
    }

    if ((rv = ipmi_rmcpplus_check_rakp_2_key_exchange_authentication_code (c->config.authentication_algorithm,
              password,
              password_len,
              c->session.remote_console_session_id,
              managed_system_session_id,
              c->session.remote_console_random_number,
              IPMI_REMOTE_CONSOLE_RANDOM_NUMBER_LENGTH,
              managed_system_random_number,
              managed_system_random_number_len,
              managed_system_guid,
              managed_system_guid_len,
              c->session.name_only_lookup,
              c->config.privilege_level,
              username,
              username_len,
              c->connection.obj_rakp_message_2)) < 0)
    {
        IPMICONSOLE_CTX_DEBUG (c, ("ipmi_rmcpplus_check_rakp_2_key_exchange_authentication_code: p = %d; %s", p, strerror (errno)));
        ipmiconsole_ctx_set_errnum (c, IPMICONSOLE_ERR_INTERNAL_ERROR);
        return (-1);
    }

    if (!rv)
        IPMICONSOLE_CTX_DEBUG (c, ("rakp 2 key exchanged authentication code check failed; p = %d", p));

    return (rv);
}