A detective story about RMCP + and OpenSSL, or how Wireshark helped defeat the incorrect argument in OpenIPMI

    Inside there will be a bit of C code, a few dumps of Wireshark and a bit of console commands.
    Given: several pieces of iron that should be interrogated via the IPMI interface from under GNU / Linux, and two of them that refused to do this.

    image

    IPMI (from the English Intelligent Platform Management Interface) is an intelligent platform management interface designed for offline monitoring and management of functions built directly into the hardware and firmware of server platforms. The key features of IPMI are monitoring, restoring management functions, logging and inventory, which are available independently of the processor, BIOS, and operating system. Platform management features may be available even if the system is turned off. (Wikipedia)
    “They refused to do it” - when trying to connect to them both through the standard ipmitool utility, and through my development using the libOpenIPMI library, the connection ended with a timeout error.
    OpenIPMI is an effort to create a full-function IPMI system to allow full access to all IPMI information on a server and to abstract it to a level that will make it easy to use. See the SourceForge page for the source code.
    (OpenIPMI)
    The first solution was found quickly:

    ipmitool -I lanplus -H 192.168.14.5 -U ADMIN -P ADMIN mc info

    Here, in addition to the standard IPMI connection details, the need to use the RMCP + protocol (included in the IPMI 2.0 specification) is clearly indicated.

    It would seem that with the OpenIPMI library everything should be simple too.

    Although the documentation for this library is very complicated: a large book (yes, a book in PDF format) called " A Gentle Introduction to IPMI " is offered as documentation . That is, it is impossible to read a brief HowTo or Readme, look at examples and start writing code, periodically glancing at the docks for reference, but worse than that: despite a detailed description of IPMI architecture and library functions, some elementary things are missing in this manual. For example, how to connect using RMCP +.

    A quick run through the library headers, we find what we need in the definitions and replace it in ipmi_ip_setup_con ()

    IPMI_AUTHTYPE_MD5 на IPMI_AUTHTYPE_RMCP_PLUS

    And here we are faced with the following problem: the timeout error really disappeared, however, the connection function began to throw an Incorrect argument error.

    There are no details what this error can mean, neither in the documentation book, nor in debugging messages, anywhere. It is clear that someone (either the library function itself or the remote device) swears at some argument, but it’s impossible to find out which one, and at what stage in general, to find out. A quick look at the sources suggests that the Incorrect argument (EINVAL constant) can be returned by different functions during the connection for very different reasons (there are a lot of branches and conditions).

    Two ways come to mind:

    1. make a debug-assembly of the library and learn step-by-step with a debugger what, where and when
    2. first, look at everything that happens from the outside, observing the packet exchange between the client (my application) and the server (hardware) and compare what is the difference between my implementation and ipmitool, and only then get into the code.

    Intuition and desire for adventure pushed to the second option and were not mistaken.

    We start wireshark, we configure the filter and we begin to study.

    image

    It can be seen that the client and server exchange request-answers, in the case of working with ipmitool the exchange continues successfully, and when using libOpenIPMI everything shuts up.

    Question: The incorrect argument event occurs somewhere in the bowels of the library, or does something not like the device itself?

    Compare the answers from the hardware:

    image
    (Successful response when using ipmitool)

    image
    (Not very successful response when using our application)

    As you can see, the response from the device is really different - in case of an error, the data block is only 7 bytes long.

    I tried to search the web for a normal description of the RMCP + protocol to understand what and how is encoded in this data, but it was inconclusive.

    The following question arose: what is the difference between the requests sent to the server, that in one case it answers normally, and in the second case something goes wrong?

    image

    image

    Having looked closely at the packets being sent, the difference was found, it remains only to find out what these different bytes mean.

    Rummaging through the sources of the connection establishment function, an
    algorithm for building the package was found in send_rmcpp_open_session :

    if ((int) lan->cparm.auth == IPMI_LANP_AUTHENTICATION_ALGORITHM_BMCPICK)
        data[11] = 0; /* Let the BMC pick */
    else {
        data[11] = 8;
        data[12] = lan->cparm.auth;
    }
    data[16] = 1; /* integrity algorithm */
    if ((int) lan->cparm.integ == IPMI_LANP_INTEGRITY_ALGORITHM_BMCPICK)
        data[19] = 0; /* Let the BMC pick */
    else {
        data[19] = 8;
        data[20] = lan->cparm.integ;
    }
    data[24] = 2; /* confidentiality algorithm */
    if ((int) lan->cparm.conf == IPMI_LANP_CONFIDENTIALITY_ALGORITHM_BMCPICK)
        data[27] = 0; /* Let the BMC pick */
    else {
        data[27] = 8;
        data[28] = lan->cparm.conf;
    }
    

    It was already becoming interesting. Bytes 0x08 in the package were clearly evident and said that "this is it."

    In the same source there were defined definitions of various authentication options and the like:

    #define     IPMI_LANP_AUTHENTICATION_ALGORITHM_BMCPICK   (~0)
    #define     IPMI_LANP_AUTHENTICATION_ALGORITHM_RAKP_NONE   0
    #define     IPMI_LANP_AUTHENTICATION_ALGORITHM_RAKP_HMAC_SHA1   1
    #define     IPMI_LANP_AUTHENTICATION_ALGORITHM_RAKP_HMAC_MD5   2
    

    This clearly led to the conclusion that ipmitool initiated a connection with SHA-1 authentication, and our application with libOpenIPMI for some reason tried to connect without protection at all, receiving an ignore response (apparently, the device really didn’t like insecure connections).

    After a little digging around in the source code, it became clear that OpenIPMI by default selects the most secure option for establishing a connection, but in our case, in the library’s opinion, the most secure option is without protection at all.

    Further study of the sources showed that authentication options are taken from the global auths [] array, where they are added by the ipmi_rmcpp_register_authentication () procedure, but the procedure itself ... where is it called? We search and find:

    #ifdef HAVE_OPENSSL
    ipmi_rmcpp_register_authentication
        (IPMI_LANP_AUTHENTICATION_ALGORITHM_RAKP_HMAC_MD5, NULL);
    ipmi_rmcpp_register_authentication
        (IPMI_LANP_AUTHENTICATION_ALGORITHM_RAKP_HMAC_SHA1, NULL);
    #endif

    there he is the answer.

    The standard libopenipmi0 package in Ubuntu and Debian is compiled without the OpenSSL support required for these functions. And at least someone would write a word about this nuance in the documentation!

    You can verify this by doing

    apt-get source libopenipmi0

    and looking at the debian / rules file and meeting there quite a clear line --without-openssl

    The solution is to rebuild the package as needed.

    sudo apt-get install devscripts build-essential fakeroot
    sudo apt-get build-dep libopenipmi0
    apt-get source libopenipmi0
    # заменяем в debian/rules:
    # --without-openssl  =>  --with-openssl
    sudo dpkg-buildpackage
    debuild -us -uc
    

    We install and verify that everything worked as it should.

    A little later ppa was found where a kind person rebuilds this lib for Ubuntu with support for OpenSSL.

    Hurrah. Happy end.

    Also popular now: