1 /*
2 * Copyright (C) 2015 Red Hat
3 *
4 * This file is part of ocserv.
5 *
6 * ocserv is free software: you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * ocserv is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include <config.h>
21
22 #include <stdlib.h>
23 #include <stdarg.h>
24 #include <stdio.h>
25 #include <string.h>
26
27 #include <vpn.h>
28 #include <worker.h>
29 #include "common.h"
30
31 #ifdef HAVE_GSSAPI
32
der_decode(const uint8_t * der,unsigned der_size,uint8_t * out,unsigned * out_size,char * realm,unsigned realm_size,int * error)33 int der_decode(const uint8_t *der, unsigned der_size, uint8_t *out, unsigned *out_size,
34 char *realm, unsigned realm_size, int *error)
35 {
36 int ret, len;
37 ASN1_TYPE c2 = ASN1_TYPE_EMPTY;
38
39 ret = asn1_create_element(_kkdcp_pkix1_asn, "KKDCP.KDC-PROXY-MESSAGE", &c2);
40 if (ret != ASN1_SUCCESS) {
41 *error = ret;
42 return -1;
43 }
44
45 ret = asn1_der_decoding(&c2, der, der_size, NULL);
46 if (ret != ASN1_SUCCESS) {
47 *error = ret;
48 ret = -1;
49 goto cleanup;
50 }
51
52 len = *out_size;
53 ret = asn1_read_value(c2, "kerb-message", out, &len);
54 if (ret != ASN1_SUCCESS) {
55 *error = ret;
56 ret = -1;
57 goto cleanup;
58 }
59 *out_size = len;
60
61 len = realm_size;
62 ret = asn1_read_value(c2, "target-domain", realm, &len);
63 if (ret != ASN1_SUCCESS) {
64 /* no realm was given */
65 realm[0] = 0;
66 }
67
68 ret = 0;
69 cleanup:
70 asn1_delete_structure(&c2);
71 return ret;
72
73 }
74
der_encode_inplace(uint8_t * raw,unsigned * raw_size,unsigned max_size,int * error)75 int der_encode_inplace(uint8_t *raw, unsigned *raw_size, unsigned max_size, int *error)
76 {
77 int ret, len;
78 ASN1_TYPE c2 = ASN1_TYPE_EMPTY;
79
80 ret = asn1_create_element(_kkdcp_pkix1_asn, "KKDCP.KDC-PROXY-MESSAGE", &c2);
81 if (ret != ASN1_SUCCESS) {
82 *error = ret;
83 return -1;
84 }
85
86 ret = asn1_write_value(c2, "kerb-message", raw, *raw_size);
87 if (ret != ASN1_SUCCESS) {
88 *error = ret;
89 ret = -1;
90 goto cleanup;
91 }
92
93 asn1_write_value(c2, "target-domain", NULL, 0);
94 asn1_write_value(c2, "dclocator-hint", NULL, 0);
95
96 len = max_size;
97
98 ret = asn1_der_coding(c2, "", raw, &len, NULL);
99 if (ret != ASN1_SUCCESS) {
100 *error = ret;
101 ret = -1;
102 goto cleanup;
103 }
104 *raw_size = len;
105
106 ret = 0;
107 cleanup:
108 asn1_delete_structure(&c2);
109 return ret;
110
111 }
112
113 /* max UDP size */
114 #define KKDCP_READ_TIMEOUT 20
115 #define BUF_SIZE 64*1024
post_kkdcp_handler(worker_st * ws,unsigned http_ver)116 int post_kkdcp_handler(worker_st *ws, unsigned http_ver)
117 {
118 int ret, e, fd = -1;
119 struct http_req_st *req = &ws->req;
120 unsigned i, length;
121 kkdcp_st *kkdcp = NULL;
122 uint8_t *buf;
123 uint32_t mlength;
124 char realm[128] = "";
125 const char *reason = "Unknown";
126 kkdcp_realm_st *kr;
127
128 oclog(ws, LOG_INFO, "Processing KKDCP request");
129
130 for (i=0;i<WSCONFIG(ws)->kkdcp_size;i++) {
131 if (WSCONFIG(ws)->kkdcp[i].url && strcmp(WSCONFIG(ws)->kkdcp[i].url, req->url) == 0) {
132 kkdcp = &WSCONFIG(ws)->kkdcp[i];
133 break;
134 }
135 }
136
137 if (kkdcp == NULL) {
138 oclog(ws, LOG_HTTP_DEBUG, "could not figure kkdcp handler for %s", req->url);
139 return -1;
140 }
141
142 if (req->body_length == 0) {
143 oclog(ws, LOG_HTTP_DEBUG, "empty body length for kkdcp handler %s", req->url);
144 return -1;
145 }
146
147 ws_add_score_to_ip(ws, WSCONFIG(ws)->ban_points_kkdcp, 0, 0);
148 oclog(ws, LOG_HTTP_DEBUG, "HTTP processing kkdcp framed request: %u bytes", (unsigned)req->body_length);
149
150 length = BUF_SIZE;
151 buf = talloc_size(ws, length);
152 if (buf == NULL) {
153 oclog(ws, LOG_ERR, "kkdcp: memory error");
154 return -1;
155 }
156
157 ret = der_decode((uint8_t*)req->body, req->body_length, buf, &length, realm, sizeof(realm), &e);
158 if (ret < 0) {
159 oclog(ws, LOG_ERR, "kkdcp: DER decoding error: %s", asn1_strerror(e));
160 reason = "kkdcp: DER decoding error";
161 goto fail;
162 }
163
164 kr = &kkdcp->realms[0];
165 if (realm[0] != 0 && kkdcp->realms_size > 1) {
166 oclog(ws, LOG_DEBUG, "kkdcp: client asked for '%s'", realm);
167
168 for (i=0;i<kkdcp->realms_size;i++) {
169 if (strcmp(kkdcp->realms[i].realm, realm) == 0) {
170 kr = &kkdcp->realms[i];
171 break;
172 }
173 }
174 }
175
176 fd = socket(kr->ai_family, kr->ai_socktype, kr->ai_protocol);
177 if (fd == -1) {
178 e = errno;
179 oclog(ws, LOG_ERR, "kkdcp: socket error: %s", strerror(e));
180 reason = "kkdcp: socket error";
181 goto fail;
182 }
183
184 ret = connect(fd, (struct sockaddr*)&kr->addr, kr->addr_len);
185 if (ret == -1) {
186 e = errno;
187 oclog(ws, LOG_ERR, "kkdcp: connect error: %s", strerror(e));
188 reason = "kkdcp: error connecting to server";
189 goto fail;
190 }
191
192 oclog(ws, LOG_HTTP_DEBUG, "HTTP sending kkdcp request: %u bytes", (unsigned)length);
193 ret = send(fd, buf, length, 0);
194 if (ret != length) {
195 if (ret == -1) {
196 e = errno;
197 oclog(ws, LOG_ERR, "kkdcp: send error: %s", strerror(e));
198 } else {
199 oclog(ws, LOG_ERR, "kkdcp: send error: only %d were sent", ret);
200 }
201 reason = "kkdcp: error sending to server";
202 goto fail;
203 }
204
205 if (kr->ai_socktype == SOCK_DGRAM) {
206 ret = recv(fd, buf, BUF_SIZE, 0);
207 if (ret == -1) {
208 e = errno;
209 oclog(ws, LOG_ERR, "kkdcp: recv error: %s", strerror(e));
210 reason = "kkdcp: error receiving from server";
211 goto fail;
212 }
213
214 length = ret;
215 } else {
216 ret = recv(fd, buf, 4, 0);
217 if (ret < 4) {
218 e = errno;
219 oclog(ws, LOG_ERR, "kkdcp: recv error: %s", strerror(e));
220 reason = "kkdcp: error receiving from server";
221 goto fail;
222 }
223
224 memcpy(&mlength, buf, 4);
225 mlength = ntohl(mlength);
226 if (mlength >= BUF_SIZE-4) {
227 oclog(ws, LOG_ERR, "kkdcp: too long message (%d bytes)", (int)mlength);
228 reason = "kkdcp: error receiving from server";
229 goto fail;
230 }
231
232 ret = force_read_timeout(fd, buf+4, mlength, KKDCP_READ_TIMEOUT);
233 if (ret == -1) {
234 e = errno;
235 oclog(ws, LOG_ERR, "kkdcp: recv error: %s", strerror(e));
236 reason = "kkdcp: error receiving from server";
237 goto fail;
238 }
239 length = ret + 4;
240 }
241
242 oclog(ws, LOG_HTTP_DEBUG, "HTTP processing kkdcp reply: %u bytes", (unsigned)length);
243
244 cstp_cork(ws);
245 ret = cstp_printf(ws, "HTTP/1.%u 200 OK\r\n", http_ver);
246 if (ret < 0) {
247 goto fail;
248 }
249
250 ret =
251 cstp_puts(ws, "Content-Type: application/kerberos\r\n");
252 if (ret < 0) {
253 goto fail;
254 }
255
256 ret = der_encode_inplace(buf, &length, BUF_SIZE, &e);
257 if (ret < 0) {
258 oclog(ws, LOG_ERR, "kkdcp: DER encoding error: %s", asn1_strerror(e));
259 reason = "kkdcp: DER encoding error";
260 goto fail;
261 }
262
263 oclog(ws, LOG_HTTP_DEBUG, "HTTP sending kkdcp framed reply: %u bytes", (unsigned)length);
264 ret =
265 cstp_printf(ws, "Content-Length: %u\r\n",
266 (unsigned int)length);
267 if (ret < 0) {
268 goto fail;
269 }
270
271 ret = cstp_puts(ws, "Connection: Keep-Alive\r\n");
272 if (ret < 0) {
273 goto fail;
274 }
275
276 ret = add_owasp_headers(ws);
277 if (ret < 0) {
278 goto fail;
279 }
280
281 ret = cstp_puts(ws, "\r\n");
282 if (ret < 0) {
283 goto fail;
284 }
285
286 ret = cstp_send(ws, buf, length);
287 if (ret < 0) {
288 goto fail;
289 }
290
291 ret = cstp_uncork(ws);
292 if (ret < 0) {
293 goto fail;
294 }
295
296 ret = 0;
297 goto cleanup;
298 fail:
299 (void)cstp_printf(ws,
300 "HTTP/1.%u 502 %s\r\n\r\n",
301 http_ver, reason);
302 ret = -1;
303
304 cleanup:
305 talloc_free(buf);
306 if (fd != -1)
307 close(fd);
308 return ret;
309 }
310
311 #else
312
post_kkdcp_handler(worker_st * ws,unsigned http_ver)313 int post_kkdcp_handler(worker_st *ws, unsigned http_ver)
314 {
315 return -1;
316 }
317 #endif
318