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