1/*
2 * Copyright (c) 2013 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
5 *
6 * Portions Copyright (c) 2013 Apple Inc. All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 *
12 * 1. Redistributions of source code must retain the above copyright
13 *    notice, this list of conditions and the following disclaimer.
14 *
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 *
19 * 3. Neither the name of the Institute nor the names of its contributors
20 *    may be used to endorse or promote products derived from this software
21 *    without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 */
35
36#include "krb5_locl.h"
37#include <CoreFoundation/CoreFoundation.h>
38#include <CFNetwork/CFNetwork.h>
39
40static krb5_error_code
41requestToURL(krb5_context context,
42	     const char *stringurl,
43	     const krb5_data *outdata,
44	     krb5_data *retdata)
45{
46    CFMutableDataRef responseBytes = NULL;
47    CFReadStreamRef requestStream = NULL;
48    CFHTTPMessageRef message = NULL;
49    CFDataRef bodyData = NULL;
50    KDC_PROXY_MESSAGE msg;
51    CFIndex numBytesRead;
52    krb5_error_code ret;
53    CFURLRef url = NULL;
54    size_t size;
55
56    bodyData = CFDataCreateWithBytesNoCopy(NULL, outdata->data, outdata->length, kCFAllocatorNull);
57    if (bodyData == NULL) {
58	ret = ENOMEM;
59	goto out;
60    }
61
62    url = CFURLCreateWithBytes(NULL, (const UInt8 *)stringurl, strlen(stringurl), kCFStringEncodingUTF8, NULL);
63    if (url == NULL) {
64	ret = ENOMEM;
65	goto out;
66    }
67
68    message = CFHTTPMessageCreateRequest(kCFAllocatorDefault, CFSTR("POST"), url, kCFHTTPVersion1_1);
69    if (message == NULL) {
70	ret = ENOMEM;
71	goto out;
72    }
73    CFHTTPMessageSetBody(message, bodyData);
74    CFHTTPMessageSetHeaderFieldValue(message, CFSTR("Content-Type"), CFSTR("application/octet-stream"));
75
76    requestStream = CFReadStreamCreateForHTTPRequest(NULL, message);
77    if (requestStream == NULL) {
78	ret = ENOMEM;
79	goto out;
80    }
81
82    if (!CFReadStreamOpen(requestStream)) {
83	CFErrorRef error = CFReadStreamCopyError(requestStream);
84	ret = HEIM_ERR_EOF;
85	_krb5_set_cf_error_message(context, ret, error, "Failed to open kkdcp stream");
86	if (error)
87	    CFRelease(error);
88	goto out;
89    }
90
91    responseBytes = CFDataCreateMutable(NULL, 0);
92    numBytesRead = 0 ;
93    do {
94	UInt8 buf[1024];
95	numBytesRead = CFReadStreamRead(requestStream, buf, sizeof(buf));
96	if(numBytesRead > 0)
97	    CFDataAppendBytes(responseBytes, buf, numBytesRead);
98
99    } while(numBytesRead > 0);
100    if (numBytesRead < 0) {
101	CFErrorRef error = CFReadStreamCopyError(requestStream);
102	ret = HEIM_ERR_EOF;
103	_krb5_set_cf_error_message(context, ret, error, "Failed to reading kkdcp stream");
104	goto out;
105    }
106    CFReadStreamClose(requestStream);
107
108    ret = decode_KDC_PROXY_MESSAGE(CFDataGetBytePtr(responseBytes), CFDataGetLength(responseBytes), &msg, &size);
109    if (ret) {
110	krb5_set_error_message(context, ret, "failed to decode KDC_PROXY_MESSAGE");
111	goto out;
112    }
113
114    ret = krb5_data_copy(retdata, msg.kerb_message.data, msg.kerb_message.length);
115    free_KDC_PROXY_MESSAGE(&msg);
116    if (ret)
117	goto out;
118
119    ret = 0;
120 out:
121    if (ret)
122	_krb5_debug(context, 10, ret, "kkdcp to url (%s) failed", stringurl);
123
124    if (bodyData)
125	CFRelease(bodyData);
126    if (url)
127	CFRelease(url);
128    if (message)
129	CFRelease(message);
130    if (requestStream)
131	CFRelease(requestStream);
132    if (responseBytes)
133	CFRelease(responseBytes);
134
135    return ret;
136}
137
138krb5_error_code
139_krb5_kkdcp_request(krb5_context context,
140		    const char *realm,
141		    const char *url,
142		    const krb5_data *data,
143		    krb5_data *retdata)
144{
145    KDC_PROXY_MESSAGE msg;
146    krb5_data msgdata;
147    krb5_error_code ret;
148    size_t size;
149
150    memset(&msg, 0, sizeof(msg));
151
152    msg.kerb_message = *data;
153    msg.target_domain = (Realm *)&realm;
154    msg.dclocator_hint = NULL;
155
156    ASN1_MALLOC_ENCODE(KDC_PROXY_MESSAGE, msgdata.data, msgdata.length, &msg, &size, ret);
157    if (ret)
158	return ret;
159    heim_assert(msgdata.length == size, "internal asn1. encoder error");
160
161    ret = requestToURL(context, url, &msgdata, retdata);
162    krb5_data_free(&msgdata);
163
164    return ret;
165}
166