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;
}