MI_Boolean HttpClient_DecryptData()

in Unix/http/httpclientauth.c [1013:1327]


MI_Boolean HttpClient_DecryptData(_In_ HttpClient_SR_SocketData * handler, _Out_ HttpClientResponseHeader * pHeaders, _Out_ Page ** pData)
{
    OM_uint32 maj_stat = 0;
    OM_uint32 min_stat = 0;
    gss_buffer_desc input_buffer = { 0 };
    gss_buffer_desc output_buffer = { 0 };
    Page *page = NULL;
    char *scanlimit = NULL;
    char *scanp = NULL;
    char *segp = NULL;
    char *linep = NULL;
    char *linelimit = NULL;
    int flags = (int)handler->negoFlags&(GSS_C_INTEG_FLAG|GSS_C_CONF_FLAG);
    uint32_t sig_len = 0;
    //uint32_t sig_flags = 0;

    int original_content_length = 0;
    char original_content_type_save[1024] = { 0 };  // Longest possible content type?
    char original_encoding_save[64] = { 0 };

    char *original_content_type = NULL;
    char *original_encoding = NULL;

    MI_Boolean done = FALSE;
    static const char ENCRYPTED_SEGMENT[] = "Encrypted Boundary";
    static const size_t ENCRYPTED_SEGMENT_LEN = MI_COUNT(ENCRYPTED_SEGMENT) - 1;

    static const char ORIGINAL_CONTENT[] = "OriginalContent:";
    static const size_t ORIGINAL_CONTENT_LEN = MI_COUNT(ORIGINAL_CONTENT) - 1;

    static const char TYPE_FIELD[] = "type=";
    static const size_t TYPE_FIELD_LEN = MI_COUNT(TYPE_FIELD) - 1;

    static const char CHARSET_FIELD[] = "charset=";
    static const size_t CHARSET_FIELD_LEN = MI_COUNT(CHARSET_FIELD) - 1;

    static const char LENGTH_FIELD[] = "length=";
    static const size_t LENGTH_FIELD_LEN = MI_COUNT(LENGTH_FIELD) - 1;

    static const char CONTENT_TYPE[] = "Content-Type";
    static const size_t CONTENT_TYPE_LEN = MI_COUNT(CONTENT_TYPE) - 1;

    static const char CONTENT_LENGTH[] = "Content-Length";
    static const size_t CONTENT_LENGTH_LEN = MI_COUNT(CONTENT_LENGTH) - 1;

    static const char OCTET_STREAM[] = "application/octet-stream";
    static const size_t OCTET_STREAM_LEN = MI_COUNT(OCTET_STREAM) - 1;

    static const char APPLICATION_SPNEGO[] = "application/HTTP-SPNEGO-session-encrypted";   // 2do: compare to header protocol
    static const size_t APPLICATION_SPNEGO_LEN = MI_COUNT(APPLICATION_SPNEGO) - 1;

    static const char APPLICATION_KERBEROS[] = "application/HTTP-Kerberos-session-encrypted";
    static const size_t APPLICATION_KERBEROS_LEN = MI_COUNT(APPLICATION_KERBEROS) - 1;

    static const char MULTIPART_ENCRYPTED[] = "multipart/encrypted";
    static const size_t MULTIPART_ENCRYPTED_LEN = MI_COUNT(MULTIPART_ENCRYPTED) - 1;

    const char *content_type = NULL;
    const char *content_len_str = NULL;

    //char *char_set = NULL;

    if (!pHeaders)
    {
        trace_HTTP_CryptInvalidArg(__FUNCTION__, "pHeaders == NULL");
        return FALSE;
    }

    int i = 0;
    for (i = 0; i < pHeaders->sizeHeaders; i++ )
    {
        if (strncasecmp(pHeaders->headers[i].name, CONTENT_TYPE, CONTENT_TYPE_LEN) == 0)
        {
            content_type = pHeaders->headers[i].value;
        }
        else if (strncasecmp(pHeaders->headers[i].name, CONTENT_LENGTH, CONTENT_LENGTH_LEN) == 0)
        {
            content_len_str = pHeaders->headers[i].value;
        }
    }

    if (!content_type)
    {
       // Not encrypted
       //
       return FALSE;
    }

    if (!(strncasecmp(content_type, MULTIPART_ENCRYPTED, MULTIPART_ENCRYPTED_LEN) == 0))
    {
        // Then its not encrypted. our job is done

        return TRUE;
    }

    if (!handler->authContext)
    {
        trace_HTTP_CryptInvalidArg(__FUNCTION__, "context == NULL");
        return FALSE;
    }

    if (!pData)
    {
        trace_HTTP_CryptInvalidArg(__FUNCTION__, "pdata == NULL");
        return FALSE;
    }

    page = *pData;
    input_buffer.length = page->u.s.size;
    input_buffer.value = (void *)(page + 1);

    // Check the data for the original size and content type, and the start of the encrypted data

    scanp = (char *)(page + 1) + 1;
    scanlimit = ((char *)page) + page->u.s.size;
    segp = scanp;
    linep = scanp;
    while (scanp < scanlimit && !done)
    {
        if ('-' == scanp[0] && '-' == scanp[-1])
        {
            // Start of a segment. But which one?
            segp = ++scanp;
        }

        if (Strncasecmp(segp, ENCRYPTED_SEGMENT, ENCRYPTED_SEGMENT_LEN) == 0)
        {
            // Skip the boundary
            while (!('\n' == scanp[0] && '\r' == scanp[-1]) && scanp < scanlimit) scanp++;
            scanp++;            // ski[p the final \n

            // Which line
            while (!('\n' == scanp[0] && '\r' == scanp[-1]) && scanp < scanlimit && !done)
            {
                // Skip to the end of the line

                linep = scanp;

                if (Strncasecmp(linep, CONTENT_TYPE, CONTENT_TYPE_LEN) == 0)
                {
                    // Content-Type: application/HTTP-SPNEGO-session-encrypted | Content-Type: application/octet-stream

                    // Scan to the end of the line
                    while (!('\n' == scanp[0] && '\r' == scanp[-1]) && scanp < scanlimit && !done)
                    {
                        scanp++;
                    }

                    linelimit = scanp - 1;

                    linep += CONTENT_TYPE_LEN;
                    while (isspace(*linep) && linep < linelimit)
                    {
                        linep++;
                    }

                    if (':' == *linep && linep < linelimit)
                    {
                        linep++;
                    }

                    while (isspace(*linep) && linep < linelimit)
                    {
                        linep++;
                    }

                    if (Strncasecmp(linep, OCTET_STREAM, OCTET_STREAM_LEN) == 0)
                    {

                        sig_len = *(uint32_t *) (linelimit + 2);
                        sig_len = ByteSwapToWindows32(sig_len);

                        input_buffer.length = original_content_length+sig_len;
                        input_buffer.value = linelimit + 2  // skip crlf
                                                       + 4; // skip signature len

                        done = TRUE;
                        break;
                    }
                    else if (Strncasecmp(linep, APPLICATION_SPNEGO, APPLICATION_SPNEGO_LEN) == 0)
                    {
                        // Should be application/HTTP-SPNEGO-session-encrypted for spnego

                    }
                    else if (Strncasecmp(linep, APPLICATION_KERBEROS, APPLICATION_KERBEROS_LEN) == 0)
                    {
                        // Should be application/HTTP-Kerberos-session-encrypted for kerberos

                    }
                    else
                    {
                        // Bogus.
                    }
                    scanp = linelimit + 2;
                }
                else if (Strncasecmp(linep, ORIGINAL_CONTENT, ORIGINAL_CONTENT_LEN) == 0)
                {
                    while (!('\n' == scanp[0] && '\r' == scanp[-1]) && scanp < scanlimit && !done)
                        scanp++;

                    linelimit = scanp - 1;

                    linep += ORIGINAL_CONTENT_LEN;
                    do
                    {

                        while ((isspace(*linep) || ';' == *linep) && linep < linelimit)
                            linep++;
                        if (Strncasecmp(linep, LENGTH_FIELD, LENGTH_FIELD_LEN) == 0)
                        {
                            linep += LENGTH_FIELD_LEN;
                            original_content_length = atoi(linep);
                            while (';' != *linep && *linep && linep < linelimit)
                                linep++;
                            linep++;
                        }
                        else if (Strncasecmp(linep, TYPE_FIELD, TYPE_FIELD_LEN) == 0)
                        {
                            linep += TYPE_FIELD_LEN;
                            original_content_type = linep;
                            while (';' != *linep && *linep && linep < linelimit)
                            {
                                linep++;
                            }
                            *linep++ = '\0';
                            memcpy(original_content_type_save, original_content_type, linep - original_content_type);
                            original_content_type = original_content_type_save;
                        }
                        else if (Strncasecmp(linep, CHARSET_FIELD, CHARSET_FIELD_LEN) == 0)
                        {
                            linep += CHARSET_FIELD_LEN;
                            original_encoding = linep;
                            while (';' != *linep && linep < linelimit)
                                linep++;
                            *linep++ = '\0';
                            memcpy(original_encoding_save, original_encoding, linep - original_encoding);
                            original_encoding = original_encoding_save;
                        }

                    }
                    while (linep < linelimit);
                    scanp = linelimit + 2;
                }
                else if (*scanp == '-' && scanp[1] == '-')
                {    

                    // Start of new segment

                    break;
                }    
                else
                {
                    // Bogus

                    while (!('\n' == scanp[0] && '\r' == scanp[-1]) && *scanp != '-' && scanp < scanlimit && !done)
                        scanp++;
                    scanp++;
                }
            }

        }
        ++scanp;
    }

    if (!done)
    {
        return FALSE;
    }
    // Alloc the new data page based on the original content size

    maj_stat = (*_g_gssClientState.Gss_Unwrap)(&min_stat, (gss_ctx_id_t) handler->authContext, &input_buffer, &output_buffer, &flags, NULL);
    if (GSS_S_COMPLETE != maj_stat)
    {
        _ReportError(handler, "gss_unwrap", maj_stat, min_stat);
        return FALSE;
    }
    // Here is where we replace the pData page, replace the headers on content-type and content size

    // We can just copy the data into the buffer directly, since the decrypted data is guaranteed
    // to be smaller than the encrypted data plus header

    page->u.s.size = output_buffer.length;
    memcpy(page + 1, output_buffer.value, output_buffer.length);

    char *buffer_p = (char *)(page + 1) + output_buffer.length;

    // We know we have the additional room in the page because the string was in the page already
    memcpy(buffer_p, original_content_type, strlen(original_content_type) + 1);
    original_content_type = buffer_p;

    buffer_p += strlen(original_content_type) + 1;  // Include the null
    memcpy(buffer_p, original_encoding, strlen(original_encoding) + 1);

    (*_g_gssClientState.Gss_Release_Buffer)(&min_stat, &output_buffer);

    // In oroder for this to work, we must leave the original page allocated, and leave it to the 
    // caller to free the original data and recvBuffer

    for (i = 0; i < pHeaders->sizeHeaders; i++ )
    {
        char **pvalue = NULL;

        pvalue = (char**)(&pHeaders->headers[i].value);
        if (strncasecmp(pHeaders->headers[i].name, CONTENT_TYPE, CONTENT_TYPE_LEN) == 0)
        {
            *pvalue = (char*)original_content_type;
        }
        else if (strncasecmp(pHeaders->headers[i].name, CONTENT_LENGTH, CONTENT_LENGTH_LEN) == 0)
        {
            *pvalue = (char*)content_len_str;
        }
    }

    return MI_TRUE;
}