diff -durN ocserv-1.1.6.orig/src/Makefile.am ocserv-1.1.6/src/Makefile.am --- ocserv-1.1.6.orig/src/Makefile.am 2022-02-27 23:34:34.653439020 +1300 +++ ocserv-1.1.6/src/Makefile.am 2022-03-01 21:54:16.144350358 +1300 @@ -75,7 +75,7 @@ html.c html.h http-heads.h worker.c worker.h worker-auth.c \ worker-bandwidth.c worker-bandwidth.h worker-http.c worker-http-handlers.c \ worker-kkdcp.c worker-misc.c worker-privs.c worker-proxyproto.c \ - worker-resume.c worker-vpn.c + worker-resume.c worker-vpn.c worker-svc.c ocserv_worker_LDADD = $(CORE_LDADD) diff -durN ocserv-1.1.6.orig/src/Makefile.in ocserv-1.1.6/src/Makefile.in --- ocserv-1.1.6.orig/src/Makefile.in 2022-02-27 23:34:34.653439020 +1300 +++ ocserv-1.1.6/src/Makefile.in 2022-03-01 21:54:16.148350129 +1300 @@ -323,7 +323,7 @@ worker-bandwidth.c worker-bandwidth.h worker-http.c \ worker-http-handlers.c worker-kkdcp.c worker-misc.c \ worker-privs.c worker-proxyproto.c worker-resume.c \ - worker-vpn.c worker-latency.c worker-latency.h + worker-vpn.c worker-svc.c worker-latency.c worker-latency.h @LOCAL_HTTP_PARSER_TRUE@am__objects_11 = http-parser/ocserv_worker-http_parser.$(OBJEXT) @ENABLE_COMPRESSION_TRUE@am__objects_12 = ocserv_worker-lzs.$(OBJEXT) @HAVE_GSSAPI_TRUE@am__objects_13 = \ @@ -358,7 +358,8 @@ ocserv_worker-worker-privs.$(OBJEXT) \ ocserv_worker-worker-proxyproto.$(OBJEXT) \ ocserv_worker-worker-resume.$(OBJEXT) \ - ocserv_worker-worker-vpn.$(OBJEXT) $(am__objects_15) + ocserv_worker-worker-vpn.$(OBJEXT) \ + ocserv_worker-worker-svc.$(OBJEXT) $(am__objects_15) ocserv_worker_OBJECTS = $(am_ocserv_worker_OBJECTS) @ENABLE_LATENCY_SUPPORT_TRUE@am__DEPENDENCIES_7 = \ @ENABLE_LATENCY_SUPPORT_TRUE@ $(am__DEPENDENCIES_1) @@ -446,6 +447,7 @@ ./$(DEPDIR)/ocserv_worker-worker-privs.Po \ ./$(DEPDIR)/ocserv_worker-worker-proxyproto.Po \ ./$(DEPDIR)/ocserv_worker-worker-resume.Po \ + ./$(DEPDIR)/ocserv_worker-worker-svc.Po \ ./$(DEPDIR)/ocserv_worker-worker-vpn.Po \ ./$(DEPDIR)/ocserv_worker-worker.Po ./$(DEPDIR)/proc-search.Po \ ./$(DEPDIR)/route-add.Po ./$(DEPDIR)/sec-mod-auth.Po \ @@ -1333,7 +1335,7 @@ worker.c worker.h worker-auth.c worker-bandwidth.c \ worker-bandwidth.h worker-http.c worker-http-handlers.c \ worker-kkdcp.c worker-misc.c worker-privs.c \ - worker-proxyproto.c worker-resume.c worker-vpn.c \ + worker-proxyproto.c worker-resume.c worker-vpn.c worker-svc.c \ $(am__append_8) ocserv_worker_LDADD = $(CORE_LDADD) $(am__append_9) noinst_LIBRARIES = libipc.a libcommon.a libccan.a $(am__append_17) \ @@ -1825,6 +1827,7 @@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ocserv_worker-worker-privs.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ocserv_worker-worker-proxyproto.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ocserv_worker-worker-resume.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ocserv_worker-worker-svc.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ocserv_worker-worker-vpn.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ocserv_worker-worker.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/proc-search.Po@am__quote@ # am--include-marker @@ -2766,6 +2769,20 @@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ocserv_worker_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ocserv_worker-worker-vpn.obj `if test -f 'worker-vpn.c'; then $(CYGPATH_W) 'worker-vpn.c'; else $(CYGPATH_W) '$(srcdir)/worker-vpn.c'; fi` +ocserv_worker-worker-svc.o: worker-svc.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ocserv_worker_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ocserv_worker-worker-svc.o -MD -MP -MF $(DEPDIR)/ocserv_worker-worker-svc.Tpo -c -o ocserv_worker-worker-svc.o `test -f 'worker-svc.c' || echo '$(srcdir)/'`worker-svc.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ocserv_worker-worker-svc.Tpo $(DEPDIR)/ocserv_worker-worker-svc.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='worker-svc.c' object='ocserv_worker-worker-svc.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ocserv_worker_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ocserv_worker-worker-svc.o `test -f 'worker-svc.c' || echo '$(srcdir)/'`worker-svc.c + +ocserv_worker-worker-svc.obj: worker-svc.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ocserv_worker_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ocserv_worker-worker-svc.obj -MD -MP -MF $(DEPDIR)/ocserv_worker-worker-svc.Tpo -c -o ocserv_worker-worker-svc.obj `if test -f 'worker-svc.c'; then $(CYGPATH_W) 'worker-svc.c'; else $(CYGPATH_W) '$(srcdir)/worker-svc.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ocserv_worker-worker-svc.Tpo $(DEPDIR)/ocserv_worker-worker-svc.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='worker-svc.c' object='ocserv_worker-worker-svc.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ocserv_worker_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ocserv_worker-worker-svc.obj `if test -f 'worker-svc.c'; then $(CYGPATH_W) 'worker-svc.c'; else $(CYGPATH_W) '$(srcdir)/worker-svc.c'; fi` + ocserv_worker-worker-latency.o: worker-latency.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ocserv_worker_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ocserv_worker-worker-latency.o -MD -MP -MF $(DEPDIR)/ocserv_worker-worker-latency.Tpo -c -o ocserv_worker-worker-latency.o `test -f 'worker-latency.c' || echo '$(srcdir)/'`worker-latency.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ocserv_worker-worker-latency.Tpo $(DEPDIR)/ocserv_worker-worker-latency.Po @@ -2989,6 +3006,7 @@ -rm -f ./$(DEPDIR)/ocserv_worker-worker-privs.Po -rm -f ./$(DEPDIR)/ocserv_worker-worker-proxyproto.Po -rm -f ./$(DEPDIR)/ocserv_worker-worker-resume.Po + -rm -f ./$(DEPDIR)/ocserv_worker-worker-svc.Po -rm -f ./$(DEPDIR)/ocserv_worker-worker-vpn.Po -rm -f ./$(DEPDIR)/ocserv_worker-worker.Po -rm -f ./$(DEPDIR)/proc-search.Po @@ -3149,6 +3167,7 @@ -rm -f ./$(DEPDIR)/ocserv_worker-worker-privs.Po -rm -f ./$(DEPDIR)/ocserv_worker-worker-proxyproto.Po -rm -f ./$(DEPDIR)/ocserv_worker-worker-resume.Po + -rm -f ./$(DEPDIR)/ocserv_worker-worker-svc.Po -rm -f ./$(DEPDIR)/ocserv_worker-worker-vpn.Po -rm -f ./$(DEPDIR)/ocserv_worker-worker.Po -rm -f ./$(DEPDIR)/proc-search.Po diff -durN ocserv-1.1.6.orig/src/worker.h ocserv-1.1.6/src/worker.h --- ocserv-1.1.6.orig/src/worker.h 2022-02-27 23:34:34.653439020 +1300 +++ ocserv-1.1.6/src/worker.h 2022-03-01 21:54:16.148350129 +1300 @@ -91,7 +91,8 @@ AGENT_UNKNOWN, AGENT_OPENCONNECT_V3, AGENT_OPENCONNECT, - AGENT_ANYCONNECT + AGENT_ANYCONNECT, + AGENT_SVC_IPPHONE }; typedef int (*decompress_fn)(void* dst, int maxDstSize, const void* src, int src_size); @@ -337,6 +338,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-1.1.6.orig/src/worker-http.c ocserv-1.1.6/src/worker-http.c --- ocserv-1.1.6.orig/src/worker-http.c 2022-02-27 23:34:34.653439020 +1300 +++ ocserv-1.1.6/src/worker-http.c 2022-03-01 21:54:16.148350129 +1300 @@ -75,6 +75,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} }; @@ -419,6 +420,9 @@ } else if (strncasecmp(req->user_agent, "AnyConnect", 10) == 0) { oclog(ws, LOG_DEBUG, "Detected Cisco AnyConnect"); req->user_agent_type = AGENT_ANYCONNECT; + } else if (strncasecmp(req->user_agent, "Cisco SVC IPPhone Client", 24) == 0) { + oclog(ws, LOG_DEBUG, "Detected Cisco SVC IPPhone Client"); + req->user_agent_type = AGENT_SVC_IPPHONE; } else { oclog(ws, LOG_DEBUG, "Unknown client (%s)", req->user_agent); } diff -durN ocserv-1.1.6.orig/src/worker-svc.c ocserv-1.1.6/src/worker-svc.c --- ocserv-1.1.6.orig/src/worker-svc.c 1970-01-01 12:00:00.000000000 +1200 +++ ocserv-1.1.6/src/worker-svc.c 2022-09-02 23:01:46.889506645 +1200 @@ -0,0 +1,536 @@ +/* + * Copyright (C) 2021 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 **password) +{ + char *username, *field, *value, *delim; + int field_len, value_len; + + *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); + strlcpy(ws->username, username, sizeof(ws->username)); + talloc_free(username); + } else if (strncasecmp(field, "password", field_len) == 0) { + talloc_free(*password); + *password = unescape_url(ws->req.body, + value, value_len, NULL); + } + + if (*delim != '&') + break; + delim++; + + field = delim; + } while (*field != 0); + + if (ws->username[0] == '\0') { + talloc_free(*password); + oclog(ws, LOG_HTTP_DEBUG, "missing username in request"); + return -1; + } else if (*password == NULL) { + oclog(ws, LOG_HTTP_DEBUG, "missing password in request"); + return -1; + } + + return 0; +} + +static int get_cert_info(worker_st *ws) +{ + const gnutls_datum_t *raw; + unsigned int ncerts; + gnutls_x509_crt_t cert; + char cert_username[MAX_USERNAME_SIZE], cert_group[MAX_GROUPNAME_SIZE]; + size_t cert_username_len, cert_group_len; + int ret = -1; + + if (ws->cert_auth_ok == 0) + return -1; + + raw = gnutls_certificate_get_peers(ws->session, &ncerts); + if (raw == NULL) + return -1; + + ret = gnutls_x509_crt_init(&cert); + if (ret < 0) { + oclog(ws, LOG_ERR, "certificate init error: %s", + gnutls_strerror(ret)); + goto fail; + } + + ret = gnutls_x509_crt_import(cert, raw, GNUTLS_X509_FMT_DER); + if (ret < 0) { + oclog(ws, LOG_ERR, "certificate import error: %s", + gnutls_strerror(ret)); + goto fail; + } + + cert_username_len = sizeof(cert_username); + ret = gnutls_x509_crt_get_dn_by_oid(cert, WSCONFIG(ws)->cert_user_oid, 0, + 0, cert_username, &cert_username_len); + if (ret < 0) { + if (ret == GNUTLS_E_SHORT_MEMORY_BUFFER) + oclog(ws, LOG_ERR, + "certificate's username exceed the maximum buffer size (%u)", + (unsigned)sizeof(cert_username)); + else if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) + oclog(ws, LOG_ERR, "the certificate's DN does not contain OID %s; " + "cannot determine username", WSCONFIG(ws)->cert_user_oid); + else + oclog(ws, LOG_ERR, "cannot obtain username from certificate: %s", + gnutls_strerror(ret)); + + goto fail; + } + + strlcpy(ws->cert_username, cert_username, sizeof(ws->cert_username)); + + if (WSCONFIG(ws)->cert_group_oid) { + cert_group_len = sizeof(cert_group); + ret = gnutls_x509_crt_get_dn_by_oid(cert, WSCONFIG(ws)->cert_group_oid, 0, + 0, cert_group, &cert_group_len); + if (ret < 0) { + if (ret == GNUTLS_E_SHORT_MEMORY_BUFFER) + oclog(ws, LOG_ERR, + "certificate's group exceed the maximum buffer size (%u)", + (unsigned)sizeof(cert_group)); + else if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) + oclog(ws, LOG_ERR, "the certificate's DN does not contain OID %s; " + "cannot determine group", WSCONFIG(ws)->cert_group_oid); + else + oclog(ws, LOG_ERR, "cannot obtain group from certificate: %s", + gnutls_strerror(ret)); + + goto fail; + } + + ws->cert_groups = talloc_realloc(ws, ws->cert_groups, char *, 1); + if (ws->cert_groups == NULL) { + oclog(ws, LOG_ERR, "cannot allocate memory for cert groups"); + goto fail; + } + + ws->cert_groups[0] = talloc_size(ws->cert_groups, cert_group_len + 1); + if (ws->cert_groups[0] == NULL) { + oclog(ws, LOG_ERR, "cannot allocate memory for cert group"); + goto fail; + } + + strlcpy(ws->cert_groups[0], cert_group, cert_group_len); + ws->cert_groups_size = 1; + } + +fail: + gnutls_x509_crt_deinit(cert); + return ret; +} + +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 *password) +{ + int ret = -1, sd = -1; + SecAuthInitMsg init = SEC_AUTH_INIT_MSG__INIT; + SecAuthContMsg cont = SEC_AUTH_CONT_MSG__INIT; + + if (ws->selected_auth->type & AUTH_TYPE_USERNAME_PASS) { + ws->groupname[0] = '\0'; + init.user_name = ws->username; + init.auth_type |= AUTH_TYPE_USERNAME_PASS; + } else if (ws->selected_auth->type & AUTH_TYPE_CERTIFICATE) { + init.tls_auth_ok = ws->cert_auth_ok; + init.cert_user_name = ws->cert_username; + init.cert_group_names = ws->cert_groups; + init.n_cert_group_names = ws->cert_groups_size; + init.auth_type |= AUTH_TYPE_CERTIFICATE; + } + init.vhost = ws->vhost->name; + init.ip = ws->remote_ip_str; + init.our_ip = ws->our_ip_str; + init.session_start_time = ws->session_start_time; + init.hmac.data = (uint8_t*)ws->sec_auth_init_hmac; + init.hmac.len = sizeof(ws->sec_auth_init_hmac); + + if (ws->req.user_agent[0] != 0) + init.user_agent = ws->req.user_agent; + + if (ws->req.devtype[0] != 0) + init.device_type = ws->req.devtype; + + if (ws->req.devplatform[0] != 0) + init.device_platform = ws->req.devplatform; + + 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; + } + + ws->auth_state = S_AUTH_INIT; + + 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; + if (ws->selected_auth->type & AUTH_TYPE_USERNAME_PASS) + 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 != ERR_SUCCESS) { + 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) +{ + char *password = NULL; + int ret = -1; + char cookie[BASE64_ENCODE_RAW_LENGTH(sizeof(ws->cookie)) + 1]; + + if (ws->selected_auth->type & AUTH_TYPE_USERNAME_PASS) { + /* fail if username or password is missing */ + ret = parse_post_body(ws, &password); + if (ret < 0) + return get_svc_handler(ws, http_ver); + } else if (ws->selected_auth->type & AUTH_TYPE_CERTIFICATE) { + ret = get_cert_info(ws); + } + + if (ret >= 0) + ret = client_auth(ws, password); + 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; +}