1 /*
2  * Copyright (C) 2013-2018 Nikos Mavrogiannopoulos
3  * Copyright (C) 2015 Red Hat
4  *
5  * This file is part of ocserv.
6  *
7  * ocserv is free software: you can redistribute it and/or modify it
8  * under the terms of the GNU General Public License as published by
9  * the Free Software Foundation, either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * ocserv is distributed in the hope that it will be useful, but
13  * WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
19  */
20 
21 #include <config.h>
22 
23 #include <gnutls/gnutls.h>
24 #include <gnutls/crypto.h>
25 #include <gnutls/x509.h>
26 #include <errno.h>
27 #include <stdlib.h>
28 #include <stdarg.h>
29 #include <stdio.h>
30 #include <string.h>
31 #include <sys/types.h>
32 #include <sys/stat.h>
33 #include <fcntl.h>
34 #include <unistd.h>
35 #include <limits.h>
36 
37 #include <vpn.h>
38 #include <worker.h>
39 #include <tlslib.h>
40 
41 #define HTML_404 "<html><body><h1>404 Not Found</h1></body></html>\r\n"
42 
response_404(worker_st * ws,unsigned http_ver)43 int response_404(worker_st *ws, unsigned http_ver)
44 {
45 	if (cstp_printf(ws, "HTTP/1.%u 404 Not found\r\n", http_ver) < 0 ||
46 	    cstp_printf(ws, "Content-length: %u\r\n", (unsigned)(sizeof(HTML_404) - 1)) < 0 ||
47 	    cstp_puts  (ws, "Connection: close\r\n\r\n") < 0 ||
48 	    cstp_puts  (ws, HTML_404) < 0)
49 		return -1;
50 	return 0;
51 }
52 
send_headers(worker_st * ws,unsigned http_ver,const char * content_type,unsigned content_length)53 static int send_headers(worker_st *ws, unsigned http_ver, const char *content_type,
54 			unsigned content_length)
55 {
56 	if (cstp_printf(ws, "HTTP/1.%u 200 OK\r\n", http_ver) < 0 ||
57 	    cstp_puts  (ws, "Connection: Keep-Alive\r\n") < 0 ||
58 	    cstp_printf(ws, "Content-Type: %s\r\n", content_type) < 0 ||
59 	    cstp_puts  (ws, "X-Transcend-Version: 1\r\n") < 0 ||
60 	    cstp_printf(ws, "Content-Length: %u\r\n", content_length) < 0 ||
61 		add_owasp_headers(ws) < 0 ||
62 	    cstp_puts  (ws, "\r\n") < 0)
63 		return -1;
64 	return 0;
65 }
66 
send_data(worker_st * ws,unsigned http_ver,const char * content_type,const char * data,int content_length)67 static int send_data(worker_st *ws, unsigned http_ver, const char *content_type,
68 		       const char *data, int content_length)
69 {
70 	/* don't bother uncorking on error - the connection will be closed anyway */
71 	cstp_cork(ws);
72 	if (send_headers(ws, http_ver, content_type, content_length) < 0 ||
73 	    cstp_send(ws, data, content_length) < 0 ||
74 	    cstp_uncork(ws) < 0)
75 		return -1;
76 	return 0;
77 }
78 
get_cert_handler(worker_st * ws,unsigned http_ver)79 int get_cert_handler(worker_st * ws, unsigned http_ver)
80 {
81 	if (ws->conn_type != SOCK_TYPE_UNIX) { /* we have TLS */
82 		const gnutls_datum_t *certs;
83 		gnutls_datum_t out = {NULL, 0};
84 		int ret;
85 
86 		oclog(ws, LOG_DEBUG, "requested server certificate");
87 
88 		certs = gnutls_certificate_get_ours(ws->session);
89 		if (certs == NULL) {
90 			return -1;
91 		}
92 
93 		ret = gnutls_pem_base64_encode_alloc("CERTIFICATE", &certs[0], &out);
94 		if (ret < 0)
95 			return -1;
96 
97 		ret = send_data(ws, http_ver, "application/x-pem-file", (char*)out.data, out.size);
98 		gnutls_free(out.data);
99 
100 		return ret;
101 	} else {
102 		return -1;
103 	}
104 }
get_cert_der_handler(worker_st * ws,unsigned http_ver)105 int get_cert_der_handler(worker_st * ws, unsigned http_ver)
106 {
107 	if (ws->conn_type != SOCK_TYPE_UNIX) { /* we have TLS */
108 		const gnutls_datum_t *certs;
109 
110 		oclog(ws, LOG_DEBUG, "requested raw server certificate");
111 
112 		certs = gnutls_certificate_get_ours(ws->session);
113 		if (certs == NULL) {
114 			return -1;
115 		}
116 
117 		return send_data(ws, http_ver, "application/pkix-cert", (char*)certs[0].data, certs[0].size);
118 	} else {
119 		return -1;
120 	}
121 }
122 
123 
124 static
ca_handler(worker_st * ws,unsigned http_ver,unsigned der)125 int ca_handler(worker_st * ws, unsigned http_ver, unsigned der)
126 {
127 	if (ws->conn_type != SOCK_TYPE_UNIX) { /* we have TLS */
128 		const gnutls_datum_t *certs;
129 		gnutls_datum_t out = {NULL, 0}, tmpca;
130 		unsigned i;
131 		int ret;
132 		gnutls_x509_crt_t issuer = NULL, crt = NULL;
133 
134 		oclog(ws, LOG_DEBUG, "requested server CA");
135 
136 		certs = gnutls_certificate_get_ours(ws->session);
137 		if (certs == NULL) {
138 			oclog(ws, LOG_DEBUG, "could not obtain our cert");
139 			return -1;
140 		}
141 
142 		ret = gnutls_x509_crt_init(&crt);
143 		if (ret < 0) {
144 			oclog(ws, LOG_DEBUG, "could not initialize cert");
145 			return -1;
146 		}
147 
148 		ret = gnutls_x509_crt_init(&issuer);
149 		if (ret < 0) {
150 			oclog(ws, LOG_DEBUG, "could not initialize cert");
151 			ret = -1;
152 			goto cleanup;
153 		}
154 
155 		ret = gnutls_x509_crt_import(crt, &certs[0], GNUTLS_X509_FMT_DER);
156 		if (ret < 0) {
157 			ret = -1;
158 			oclog(ws, LOG_DEBUG, "could not import our cert");
159 			goto cleanup;
160 		}
161 
162 		for (i=0;i<8;i++) {
163 			ret = gnutls_certificate_get_crt_raw(WSCREDS(ws)->xcred, i, 1, &tmpca);
164 			if (ret < 0) {
165 				goto cleanup;
166 			}
167 
168 			ret = gnutls_x509_crt_import(issuer, &tmpca, GNUTLS_X509_FMT_DER);
169 			if (ret < 0) {
170 				ret = -1;
171 				oclog(ws, LOG_DEBUG, "could not import issuer cert");
172 				goto cleanup;
173 			}
174 
175 			ret = gnutls_x509_crt_check_issuer(crt, issuer);
176 			if (ret != 0) {
177 				ret = gnutls_x509_crt_export2(issuer, der?GNUTLS_X509_FMT_DER:GNUTLS_X509_FMT_PEM, &out);
178 				if (ret < 0) {
179 					ret = -1;
180 					oclog(ws, LOG_DEBUG, "could not export issuer of cert");
181 					goto cleanup;
182 				}
183 				break;
184 			}
185 
186 			gnutls_x509_crt_deinit(issuer);
187 			issuer = NULL;
188 		}
189 
190 		ret = send_data(ws, http_ver, "application/pkix-cert", (char*)out.data, out.size);
191 
192  cleanup:
193 		if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
194 			oclog(ws, LOG_DEBUG, "could not get CA; does the server cert list contain the CA certificate?");
195 			ret = -1;
196 		}
197 
198 		if (crt)
199 			gnutls_x509_crt_deinit(crt);
200 		if (issuer)
201 			gnutls_x509_crt_deinit(issuer);
202 		gnutls_free(out.data);
203 
204 		return ret;
205 	} else {
206 		return -1;
207 	}
208 }
209 
get_ca_handler(worker_st * ws,unsigned http_ver)210 int get_ca_handler(worker_st * ws, unsigned http_ver)
211 {
212 	return ca_handler(ws, http_ver, 0);
213 }
214 
get_ca_der_handler(worker_st * ws,unsigned http_ver)215 int get_ca_der_handler(worker_st * ws, unsigned http_ver)
216 {
217 	return ca_handler(ws, http_ver, 1);
218 }
219 
220 #ifdef ANYCONNECT_CLIENT_COMPAT
get_config_handler(worker_st * ws,unsigned http_ver)221 int get_config_handler(worker_st *ws, unsigned http_ver)
222 {
223 	int ret;
224 	struct stat st;
225 
226 	oclog(ws, LOG_HTTP_DEBUG, "requested config: %s", ws->req.url);
227 
228 	cookie_authenticate_or_exit(ws);
229 
230 	if (ws->user_config->xml_config_file == NULL) {
231 		oclog(ws, LOG_INFO, "requested config but no config file is set");
232 		response_404(ws, http_ver);
233 		return -1;
234 	}
235 
236 	ret = stat(ws->user_config->xml_config_file, &st);
237 	if (ret == -1) {
238 		oclog(ws, LOG_INFO, "cannot load config file '%s'", ws->user_config->xml_config_file);
239 		response_404(ws, http_ver);
240 		return -1;
241 	}
242 
243 	cstp_cork(ws);
244 	if (send_headers(ws, http_ver, "text/xml", (unsigned)st.st_size) < 0 ||
245 	    cstp_uncork(ws) < 0)
246 		return -1;
247 
248 	ret = cstp_send_file(ws, ws->user_config->xml_config_file);
249 	if (ret < 0) {
250 		oclog(ws, LOG_ERR, "error sending file '%s': %s", ws->user_config->xml_config_file, gnutls_strerror(ret));
251 		return -1;
252 	}
253 
254 	return 0;
255 }
256 
257 #define VPN_VERSION "0,0,0000\n"
258 #define XML_START "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<vpn rev=\"1.0\">\n</vpn>\n"
259 
get_string_handler(worker_st * ws,unsigned http_ver)260 int get_string_handler(worker_st *ws, unsigned http_ver)
261 {
262 	oclog(ws, LOG_HTTP_DEBUG, "requested fixed string: %s", ws->req.url);
263 	if (!strcmp(ws->req.url, "/1/binaries/update.txt")) {
264 		return send_data(ws, http_ver, "text/xml", VPN_VERSION,
265 				   sizeof(VPN_VERSION) - 1);
266 	} else {
267 		return send_data(ws, http_ver, "text/xml", XML_START,
268 				   sizeof(XML_START) - 1);
269 	}
270 }
271 
272 #define SH_SCRIPT "#!/bin/sh\n\n" \
273 	"exit 0"
274 
get_dl_handler(worker_st * ws,unsigned http_ver)275 int get_dl_handler(worker_st *ws, unsigned http_ver)
276 {
277 	oclog(ws, LOG_HTTP_DEBUG, "requested downloader: %s", ws->req.url);
278 	return send_data(ws, http_ver, "application/x-shellscript", SH_SCRIPT,
279 			   sizeof(SH_SCRIPT) - 1);
280 }
281 
282 #define EMPTY_MSG "<html></html>\n"
283 
get_empty_handler(worker_st * ws,unsigned http_ver)284 int get_empty_handler(worker_st *ws, unsigned http_ver)
285 {
286 	return send_data(ws, http_ver, "text/html", EMPTY_MSG,
287 			   sizeof(EMPTY_MSG) - 1);
288 }
289 
290 #endif
291 
292