diff -durN ocserv-0.12.6.orig/src/Makefile.in ocserv-0.12.6/src/Makefile.in --- ocserv-0.12.6.orig/src/Makefile.in 2020-02-24 23:02:55.238576086 +0000 +++ ocserv-0.12.6/src/Makefile.in 2020-02-25 03:00:23.572343594 +0000 @@ -235,7 +235,7 @@ ocpasswd_ocpasswd_DEPENDENCIES = ../gl/libgnu.a $(am__DEPENDENCIES_1) \ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ $(am__DEPENDENCIES_1) -am__ocserv_SOURCES_DIST = main.c main-auth.c worker-vpn.c \ +am__ocserv_SOURCES_DIST = main.c main-auth.c worker-vpn.c worker-svc.c \ worker-auth.c tlslib.c main-worker-cmd.c ip-lease.c ip-lease.h \ vhost.h main-proc.c vpn.h tlslib.h log.c tun.c tun.h \ config-kkdcp.c config.c worker-resume.c worker.h \ @@ -267,7 +267,7 @@ @ENABLE_COMPRESSION_TRUE@am__objects_7 = lzs.$(OBJEXT) @HAVE_GSSAPI_TRUE@am__objects_8 = kkdcp_asn1_tab.$(OBJEXT) am_ocserv_OBJECTS = main.$(OBJEXT) main-auth.$(OBJEXT) \ - worker-vpn.$(OBJEXT) worker-auth.$(OBJEXT) tlslib.$(OBJEXT) \ + worker-vpn.$(OBJEXT) worker-svc.$(OBJEXT) worker-auth.$(OBJEXT) tlslib.$(OBJEXT) \ main-worker-cmd.$(OBJEXT) ip-lease.$(OBJEXT) \ main-proc.$(OBJEXT) log.$(OBJEXT) tun.$(OBJEXT) \ config-kkdcp.$(OBJEXT) config.$(OBJEXT) \ @@ -367,7 +367,7 @@ ./$(DEPDIR)/worker-http-handlers.Po ./$(DEPDIR)/worker-http.Po \ ./$(DEPDIR)/worker-kkdcp.Po ./$(DEPDIR)/worker-misc.Po \ ./$(DEPDIR)/worker-privs.Po ./$(DEPDIR)/worker-proxyproto.Po \ - ./$(DEPDIR)/worker-resume.Po ./$(DEPDIR)/worker-vpn.Po \ + ./$(DEPDIR)/worker-resume.Po ./$(DEPDIR)/worker-vpn.Po ./$(DEPDIR)/worker-svc.Po \ acct/$(DEPDIR)/pam.Po acct/$(DEPDIR)/radius.Po \ auth/$(DEPDIR)/common.Po auth/$(DEPDIR)/gssapi.Po \ auth/$(DEPDIR)/pam.Po auth/$(DEPDIR)/plain.Po \ @@ -1193,7 +1193,7 @@ auth-unix.h ACCT_SOURCES = acct/radius.c acct/radius.h acct/pam.c acct/pam.h -ocserv_SOURCES = main.c main-auth.c worker-vpn.c worker-auth.c \ +ocserv_SOURCES = main.c main-auth.c worker-vpn.c worker-svc.c worker-auth.c \ tlslib.c main-worker-cmd.c ip-lease.c ip-lease.h vhost.h \ main-proc.c vpn.h tlslib.h log.c tun.c tun.h config-kkdcp.c \ config.c worker-resume.c worker.h sec-mod-resume.c main.h \ @@ -1679,6 +1679,7 @@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/worker-proxyproto.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/worker-resume.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/worker-vpn.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/worker-svc.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@acct/$(DEPDIR)/pam.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@acct/$(DEPDIR)/radius.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@auth/$(DEPDIR)/common.Po@am__quote@ # am--include-marker @@ -2308,6 +2309,7 @@ -rm -f ./$(DEPDIR)/worker-proxyproto.Po -rm -f ./$(DEPDIR)/worker-resume.Po -rm -f ./$(DEPDIR)/worker-vpn.Po + -rm -f ./$(DEPDIR)/worker-svc.Po -rm -f acct/$(DEPDIR)/pam.Po -rm -f acct/$(DEPDIR)/radius.Po -rm -f auth/$(DEPDIR)/common.Po @@ -2437,6 +2439,7 @@ -rm -f ./$(DEPDIR)/worker-proxyproto.Po -rm -f ./$(DEPDIR)/worker-resume.Po -rm -f ./$(DEPDIR)/worker-vpn.Po + -rm -f ./$(DEPDIR)/worker-svc.Po -rm -f acct/$(DEPDIR)/pam.Po -rm -f acct/$(DEPDIR)/radius.Po -rm -f auth/$(DEPDIR)/common.Po diff -durN ocserv-0.12.6.orig/src/worker.h ocserv-0.12.6/src/worker.h --- ocserv-0.12.6.orig/src/worker.h 2020-02-24 23:02:55.238576086 +0000 +++ ocserv-0.12.6/src/worker.h 2020-02-25 03:00:23.572343594 +0000 @@ -304,6 +304,8 @@ int get_cert_der_handler(worker_st * ws, unsigned http_ver); int get_ca_handler(worker_st * ws, unsigned http_ver); int get_ca_der_handler(worker_st * ws, unsigned http_ver); +int get_svc_handler(worker_st *server, unsigned http_ver); +int post_svc_handler(worker_st *server, unsigned http_ver); int response_404(worker_st *ws, unsigned http_ver); int get_empty_handler(worker_st *server, unsigned http_ver); diff -durN ocserv-0.12.6.orig/src/worker-http.c ocserv-0.12.6/src/worker-http.c --- ocserv-0.12.6.orig/src/worker-http.c 2020-02-24 23:02:55.238576086 +0000 +++ ocserv-0.12.6/src/worker-http.c 2020-02-25 03:00:23.572343594 +0000 @@ -74,6 +74,7 @@ LL("/+CSCOT+/", get_string_handler, NULL), LL("/logout", get_empty_handler, NULL), #endif + LL("/svc", get_svc_handler, post_svc_handler), {NULL, 0, 0, NULL, NULL} }; diff -durN ocserv-0.12.6.orig/src/worker-svc.c ocserv-0.12.6/src/worker-svc.c --- ocserv-0.12.6.orig/src/worker-svc.c 1970-01-01 00:00:00.000000000 +0000 +++ ocserv-0.12.6/src/worker-svc.c 2020-02-25 20:08:52.775969674 +0000 @@ -0,0 +1,425 @@ +/* + * Copyright (C) 2018 Gareth Palmer + * + * This file is part of ocserv. + * + * ocserv is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * ocserv is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "html.h" +#include +#include + + +static const char svc_login_body[] = + "\n" + "\n" + "SSL VPN Service\n" + "Please enter your username and password.\n" + "
\n" + "\n" + "\n" + "\n" + "\n" + "
\n" + "
\n"; + +static const char svc_success_body[] = + "\n" + "\n" + "\n"; + +int get_svc_handler(worker_st *ws, unsigned http_ver) +{ + int ret; + str_st str; + + str_init(&str, ws); + + oclog(ws, LOG_HTTP_DEBUG, "HTTP sending: 200 OK"); + cstp_cork(ws); + + ret = cstp_printf(ws, "HTTP/1.%u 200 OK\r\n", http_ver); + if (ret < 0) + return -1; + + ret = str_append_printf(&str, svc_login_body, ws->req.url); + if (ret < 0) + goto fail; + + ret = cstp_puts(ws, "Content-Type: text/xml\r\n"); + if (ret < 0) + goto fail; + + ret = cstp_printf(ws, "Content-Length: %u\r\n", (unsigned int)str.length); + if (ret < 0) + goto fail; + + ret = cstp_puts(ws, "Set-Cookie: webvpn=; expires=Thu, 01 Jan 1970 22:00:00 GMT; path=/; secure\r\n"); + if (ret < 0) + goto fail; + + ret = cstp_puts(ws, "Set-Cookie: webvpnc=; expires=Thu, 01 Jan 1970 22:00:00 GMT; path=/; secure\r\n"); + if (ret < 0) + goto fail; + + ret = cstp_puts(ws, "Set-Cookie: webvpnlogin=1; secure\r\n"); + if (ret < 0) + goto fail; + + ret = cstp_puts(ws, "X-Transcend-Version: 1\r\n"); + if (ret < 0) + goto fail; + + /* end of headers */ + ret = cstp_puts(ws, "\r\n"); + if (ret < 0) + goto fail; + + ret = cstp_send(ws, str.data, str.length); + if (ret < 0) + goto fail; + + ret = cstp_uncork(ws); + if (ret < 0) + goto fail; + + ret = 0; + +fail: + str_clear(&str); + if (ret < 0) + ret = -1; + + return ret; +} + +static int parse_post_body(worker_st *ws, char **username, char **password) +{ + char *field, *value, *delim; + int field_len, value_len; + + *username = NULL; + *password = NULL; + + if (ws->req.body_length == 0) + return -1; + + oclog(ws, LOG_HTTP_DEBUG, "POST body: '%.*s'", (int)ws->req.body_length, + ws->req.body); + + /* body should be "username=foo&password=bar&Login=Login" */ + field = ws->req.body; + do { + delim = field; + field_len = 0; + + while (*delim != 0) { + if (*delim == '=') + break; + delim++; + field_len++; + } + + if (*delim != '=') + break; + delim++; + + value = delim; + value_len = 0; + + while (*delim != 0) { + if (*delim == '&') + break; + delim++; + value_len++; + } + + if (strncasecmp(field, "username", field_len) == 0) { + *username = unescape_url(ws->req.body, + value, value_len, NULL); + } else if (strncasecmp(field, "password", field_len) == 0) { + *password = unescape_url(ws->req.body, + value, value_len, NULL); + } + + if (*delim != '&') + break; + delim++; + + field = delim; + } while (*field != 0); + + if (*username == NULL) { + talloc_free(*password); + oclog(ws, LOG_HTTP_DEBUG, "missing username in request"); + return -1; + } else if (*password == NULL) { + talloc_free(*username); + oclog(ws, LOG_HTTP_DEBUG, "missing password in request"); + return -1; + } + + return 0; +} + +static int recv_auth_reply(worker_st *ws, int sd) +{ + int ret; + SecAuthReplyMsg *rep = NULL; + PROTOBUF_ALLOCATOR(pa, ws); + + ret = recv_msg(ws, sd, CMD_SEC_AUTH_REPLY, + (void *)&rep, (unpack_func)sec_auth_reply_msg__unpack, + WSCONFIG(ws)->auth_timeout); + if (ret < 0) { + oclog(ws, LOG_ERR, "error receiving auth reply message"); + return ret; + } + + oclog(ws, LOG_DEBUG, "received auth reply message (value: %u)", + (unsigned)rep->reply); + + switch (rep->reply) { + case AUTH__REP__MSG: + if (rep->has_sid && rep->sid.len == sizeof(ws->sid)) { + memcpy(ws->sid, rep->sid.data, sizeof(ws->sid)); + ws->sid_set = 1; + } + + ret = ERR_AUTH_CONTINUE; + break; + case AUTH__REP__OK: + if (rep->user_name == NULL) { + ret = ERR_AUTH_FAIL; + break; + } + + strlcpy(ws->username, rep->user_name, sizeof(ws->username)); + + if (rep->has_sid && rep->sid.len == sizeof(ws->sid)) { + memcpy(ws->sid, rep->sid.data, sizeof(ws->sid)); + ws->sid_set = 1; + } + + if (rep->has_sid == 0 + || rep->sid.len != sizeof(ws->cookie) + || rep->dtls_session_id.len != sizeof(ws->session_id)) { + ret = ERR_AUTH_FAIL; + break; + } + + memcpy(ws->cookie, rep->sid.data, rep->sid.len); + ws->cookie_set = 1; + + memcpy(ws->session_id, rep->dtls_session_id.data, + rep->dtls_session_id.len); + + ret = ERR_SUCCESS; + break; + case AUTH__REP__FAILED: + default: + if (rep->reply != AUTH__REP__FAILED) + oclog(ws, LOG_ERR, "unexpected auth reply %u", + (unsigned)rep->reply); + ret = ERR_AUTH_FAIL; + break; + } + + sec_auth_reply_msg__free_unpacked(rep, &pa); + return ret; +} + +static int client_auth(worker_st *ws, char *username, char *password) +{ + int ret = -1, sd = -1; + char our_ip[MAX_IP_STR]; + SecAuthInitMsg init = SEC_AUTH_INIT_MSG__INIT; + SecAuthContMsg cont = SEC_AUTH_CONT_MSG__INIT; + + strlcpy(ws->username, username, sizeof(ws->username)); + human_addr2((struct sockaddr*)&ws->our_addr, ws->our_addr_len, + our_ip, sizeof(our_ip), 0); + + init.user_name = username; + init.auth_type |= AUTH_TYPE_USERNAME_PASS; + init.ip = ws->remote_ip_str; + init.our_ip = our_ip; + + if (ws->req.user_agent[0] != 0) + init.user_agent = ws->req.user_agent; + + sd = connect_to_secmod(ws); + if (sd == -1) { + oclog(ws, LOG_ERR, "failed connecting to sec mod"); + goto fail; + } + + ret = send_msg_to_secmod(ws, sd, CMD_SEC_AUTH_INIT, &init, + (pack_size_func)sec_auth_init_msg__get_packed_size, + (pack_func)sec_auth_init_msg__pack); + if (ret < 0) { + oclog(ws, LOG_ERR, "failed sending auth init message to sec mod"); + goto fail; + } + + ret = recv_auth_reply(ws, sd); + if (ret != ERR_AUTH_CONTINUE) { + oclog(ws, LOG_ERR, "not in auth continue state: %d", ret); + goto fail; + } + + close(sd); + + cont.ip = ws->remote_ip_str; + cont.password = password; + + if (ws->sid_set != 0) { + cont.sid.data = ws->sid; + cont.sid.len = sizeof(ws->sid); + } + + sd = connect_to_secmod(ws); + if (sd == -1) { + oclog(ws, LOG_ERR, "failed connecting to sec mod"); + goto fail; + } + + ret = send_msg_to_secmod(ws, sd, CMD_SEC_AUTH_CONT, &cont, + (pack_size_func)sec_auth_cont_msg__get_packed_size, + (pack_func)sec_auth_cont_msg__pack); + if (ret < 0) { + oclog(ws, LOG_ERR, "failed sending auth cont message to sec mod"); + goto fail; + } + + ret = recv_auth_reply(ws, sd); + if (ret < 0) { + oclog(ws, LOG_ERR, "failed authentication for '%s'", + ws->username); + goto fail; + } + +fail: + if (sd != -1) + close(sd); + + return ret; +} + +int post_svc_handler(worker_st *ws, unsigned http_ver) +{ + int ret = -1; + char *username = NULL, *password = NULL; + char cookie[BASE64_ENCODE_RAW_LENGTH(sizeof(ws->cookie)) + 1]; + + /* fail if username or password is missing */ + ret = parse_post_body(ws, &username, &password); + if (ret < 0) + return get_svc_handler(ws, http_ver); + + ret = client_auth(ws, username, password); + + talloc_free(username); + talloc_free(password); + + if (ret < 0) { + oclog(ws, LOG_HTTP_DEBUG, "HTTP sending: 401 Unauthorized"); + cstp_printf(ws, "HTTP/1.%d 401 Authentication failed\r\n" + "Content-Length: 0\r\n" + "\r\n", http_ver); + + cstp_fatal_close(ws, GNUTLS_A_ACCESS_DENIED); + exit_worker(ws); + return -1; + } + + ws->auth_state = S_AUTH_COOKIE; + oclog(ws, LOG_HTTP_DEBUG, "user '%s' obtained cookie", ws->username); + + oc_base64_encode((char *)ws->cookie, sizeof(ws->cookie), + cookie, sizeof(cookie)); + + /* reply */ + oclog(ws, LOG_HTTP_DEBUG, "HTTP sending: 200 OK"); + + cstp_cork(ws); + ret = cstp_printf(ws, "HTTP/1.%u 200 OK\r\n", http_ver); + if (ret < 0) + return -1; + + ret = cstp_puts(ws, "Connection: Keep-Alive\r\n"); + if (ret < 0) + return -1; + + ret = cstp_puts(ws, "Content-Type: text/xml\r\n"); + if (ret < 0) + return -1; + + ret = cstp_printf(ws, "Content-Length: %u\r\n", + (unsigned int)sizeof(svc_success_body) - 1); + if (ret < 0) + return -1; + + ret = cstp_printf(ws, "Set-Cookie: webvpn=%s; secure\r\n", cookie); + if (ret < 0) + return -1; + + ret = cstp_printf(ws, + "Set-Cookie: webvpnc=bu:/&p:t&iu:1/&sh:%s; path=/; secure\r\n", + WSPCONFIG(ws)->cert_hash); + if (ret < 0) + return -1; + + ret = cstp_puts(ws, "Set-Cookie: webvpnlogin=1; secure\r\n"); + if (ret < 0) + return -1; + + ret = cstp_puts(ws, "X-Transcend-Version: 1\r\n"); + if (ret < 0) + return -1; + + /* end of headers */ + ret = cstp_puts(ws, "\r\n"); + if (ret < 0) + return -1; + + ret = cstp_puts(ws, svc_success_body); + if (ret < 0) + return -1; + + ret = cstp_uncork(ws); + if (ret < 0) + return -1; + + return 0; +}