From 773d277802a92cb4de2163de4d880d90e0c923c0 Mon Sep 17 00:00:00 2001 From: Nikos Mavrogiannopoulos Date: Mon, 16 Feb 2015 10:08:34 +0100 Subject: [PATCH] kkdcp: perform the proper encoding and decoding on exchanged data --- configure.ac | 4 +- doc/sample.config | 9 ++-- src/Makefile.am | 12 +++-- src/config.c | 8 ++++ src/kkdcp.asn | 13 +++++ src/kkdcp_asn1_tab.c | 18 +++++++ src/main.c | 18 +++++++ src/ocserv-args.def | 7 +++ src/worker-kkdcp.c | 112 +++++++++++++++++++++++++++++++++++++++++-- src/worker.h | 6 +++ 10 files changed, 194 insertions(+), 13 deletions(-) create mode 100644 src/kkdcp.asn create mode 100644 src/kkdcp_asn1_tab.c diff --git a/configure.ac b/configure.ac index f7c5f8fc..8d5457f2 100644 --- a/configure.ac +++ b/configure.ac @@ -402,13 +402,15 @@ if test "$test_for_gssapi" = yes;then PKG_CHECK_MODULES([LIBKRB5], [krb5-gssapi], [ enable_gssapi=yes AC_DEFINE([HAVE_GSSAPI], [], [GSSAPI was found]) +PKG_CHECK_MODULES(LIBTASN1, [libtasn1 >= 3.9]) ], [ AC_MSG_WARN([[ *** -*** gssapi not found. Will disable compression support. +*** gssapi not found. Will disable gssapi (and Kerberos) support. *** ]]) ]) + fi dnl needed in the included PCL diff --git a/doc/sample.config b/doc/sample.config index a20fc098..142f74fd 100644 --- a/doc/sample.config +++ b/doc/sample.config @@ -414,10 +414,11 @@ no-route = 192.168.5.0/255.255.255.0 #proxy-url = http://example.com/%{U}/ # This option allows you to specify a URL location where a client can -# post, and the message will be forwarded to the provided server. Any -# reply will be provided as body into POST reply. -#url-fw = /kerberos udp@127.0.0.1:88 application/kerberos -#url-fw = /kerberos1 tcp@127.0.0.1:88 application/kerberos +# post using MS-KKDCP, and the message will be forwarded to the provided +# KDC server. That is a translation URL between HTTP and Kerberos. +# This option is available if ocserv is compiled with GSSAPI support. +#kkdcp = /kerberos udp@127.0.0.1:88 application/kerberos +#kkdcp = /kerberos-tcp tcp@127.0.0.1:88 application/kerberos # # The following options are for (experimental) AnyConnect client diff --git a/src/Makefile.am b/src/Makefile.am index 8175b8f6..e8f91715 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -6,11 +6,11 @@ AM_CPPFLAGS = -I$(srcdir)/../gl/ -I$(builddir)/../gl/ \ $(LIBPROTOBUF_C_CFLAGS) $(LIBLZ4_CFLAGS) \ $(LIBNL3_CFLAGS) $(LIBREADLINE_CFLAGS) \ $(LIBTALLOC_CFLAGS) $(LIBDBUS_CFLAGS) \ - $(LIBKRB5_CFLAGS) + $(LIBKRB5_CFLAGS) $(LIBTASN1_CFLAGS) BUILT_SOURCES = ocpasswd-args.c ocpasswd-args.h \ ocserv-args.c ocserv-args.h ipc.pb-c.c ipc.pb-c.h \ - ctl.pb-c.c ctl.pb-c.h http-heads.h + ctl.pb-c.c ctl.pb-c.h http-heads.h kkdcp_asn1_tab.c #AM_CPPFLAGS += -DDEBUG_LEAKS @@ -82,7 +82,7 @@ ocserv_SOURCES = main.c main-auth.c worker-vpn.c worker-auth.c tlslib.c \ vasprintf.c vasprintf.h lzs.c lzs.h \ proc-search.c proc-search.h http-heads.h \ str.c str.h gettime.h $(CCAN_SOURCES) $(HTTP_PARSER_SOURCES) \ - $(PROTOBUF_SOURCES) + $(PROTOBUF_SOURCES) kkdcp.asn kkdcp_asn1_tab.c ocserv_SOURCES += ipc.pb-c.h ipc.pb-c.c ctl.pb-c.c ctl.pb-c.h @@ -91,7 +91,8 @@ ocserv_LDADD = ../gl/libgnu.a $(NEEDED_LIBOPTS) libcmd-ocserv.a ocserv_LDADD += $(LIBGNUTLS_LIBS) $(PAM_LIBS) $(LIBUTIL) \ $(LIBSECCOMP) $(LIBWRAP) $(LIBCRYPT) $(NEEDED_HTTP_PARSER_LIBS) \ $(LIBPROTOBUF_C_LIBS) $(LIBSYSTEMD) $(LIBTALLOC_LIBS) \ - $(FREERADIUS_CLIENT_LIBS) $(LIBLZ4_LIBS) $(LIBKRB5_LIBS) + $(FREERADIUS_CLIENT_LIBS) $(LIBLZ4_LIBS) $(LIBKRB5_LIBS) \ + $(LIBTASN1_LIBS) if PCL @@ -142,3 +143,6 @@ ctl.pb-c.h: ctl.pb-c.c http-heads.h: $(srcdir)/http-heads.gperf -gperf --global-table -t $^ > $@-tmp && mv $@-tmp $@ + +kkdcp_asn1_tab.c: $(srcdir)/kkdcp.asn + -asn1Parser $^ $@ diff --git a/src/config.c b/src/config.c index b6c81247..98a0b9f0 100644 --- a/src/config.c +++ b/src/config.c @@ -95,7 +95,9 @@ static struct cfg_options available_options[] = { { .name = "connect-script", .type = OPTION_STRING, .mandatory = 0 }, { .name = "disconnect-script", .type = OPTION_STRING, .mandatory = 0 }, { .name = "pid-file", .type = OPTION_STRING, .mandatory = 0 }, +#ifdef HAVE_GSSAPI { .name = "kkdcp", .type = OPTION_STRING, .mandatory = 0 }, +#endif { .name = "socket-file", .type = OPTION_STRING, .mandatory = 1 }, { .name = "listen-clear-file", .type = OPTION_STRING, .mandatory = 0 }, { .name = "occtl-socket-file", .type = OPTION_STRING, .mandatory = 0 }, @@ -525,6 +527,7 @@ static void figure_auth_funcs(struct cfg_st *config, char **auth, unsigned auth_ talloc_free(auth); } +#ifdef HAVE_GSSAPI static void parse_kkdcp(struct cfg_st *config, char **urlfw, unsigned urlfw_size) { unsigned i; @@ -593,6 +596,7 @@ static void parse_kkdcp(struct cfg_st *config, char **urlfw, unsigned urlfw_size config->kkdcp_size = urlfw_size; } +#endif static void parse_cfg_file(const char* file, struct cfg_st *config, unsigned reload) { @@ -656,11 +660,13 @@ unsigned urlfw_size = 0; READ_TF("listen-host-is-dyndns", config->is_dyndns, 0); READ_STRING("listen-clear-file", config->unix_conn_file); +#ifdef HAVE_GSSAPI READ_MULTI_LINE("kkdcp", urlfw, urlfw_size); if (urlfw_size > 0) { parse_kkdcp(config, urlfw, urlfw_size); talloc_free(urlfw); } +#endif READ_NUMERIC("tcp-port", config->port); READ_NUMERIC("udp-port", config->udp_port); @@ -1074,9 +1080,11 @@ unsigned i; DEL(config->disconnect_script); DEL(config->proxy_url); +#ifdef HAVE_GSSAPI for (i=0;ikkdcp_size;i++) DEL(config->kkdcp[i].url); DEL(config->kkdcp); +#endif DEL(config->network.ipv4); DEL(config->network.ipv4_netmask); diff --git a/src/kkdcp.asn b/src/kkdcp.asn new file mode 100644 index 00000000..75b585ba --- /dev/null +++ b/src/kkdcp.asn @@ -0,0 +1,13 @@ +KKDCP { } + +DEFINITIONS EXPLICIT TAGS ::= + +BEGIN + +KDC-PROXY-MESSAGE ::= SEQUENCE { + kerb-message [0] OCTET STRING, + target-domain [1] ANY OPTIONAL, + dclocator-hint [2] INTEGER OPTIONAL +} + +END diff --git a/src/kkdcp_asn1_tab.c b/src/kkdcp_asn1_tab.c new file mode 100644 index 00000000..9d9d6930 --- /dev/null +++ b/src/kkdcp_asn1_tab.c @@ -0,0 +1,18 @@ +#if HAVE_CONFIG_H +# include "config.h" +#endif + +#include + +const asn1_static_node kkdcp_asn1_tab[] = { + { "KKDCP", 536872976, NULL }, + { NULL, 1073741836, NULL }, + { "KDC-PROXY-MESSAGE", 536870917, NULL }, + { "kerb-message", 1610620935, NULL }, + { NULL, 2056, "0"}, + { "target-domain", 1610637325, NULL }, + { NULL, 2056, "1"}, + { "dclocator-hint", 536895491, NULL }, + { NULL, 2056, "2"}, + { NULL, 0, NULL } +}; diff --git a/src/main.c b/src/main.c index 24058ecd..fd206c1f 100644 --- a/src/main.c +++ b/src/main.c @@ -61,6 +61,13 @@ #include #include +#ifdef HAVE_GSSAPI +# include + +extern const ASN1_ARRAY_TYPE kkdcp_asn1_tab[]; +ASN1_TYPE _kkdcp_pkix1_asn = ASN1_TYPE_EMPTY; +#endif + int syslog_open = 0; static unsigned int terminate = 0; static unsigned int reload_conf = 0; @@ -1054,6 +1061,17 @@ int main(int argc, char** argv) exit(1); } +#ifdef HAVE_GSSAPI + /* Initialize kkdcp structures */ + if (s->config->kkdcp) { + ret = asn1_array2tree(kkdcp_asn1_tab, &_kkdcp_pkix1_asn, NULL); + if (ret != ASN1_SUCCESS) { + fprintf(stderr, "KKDCP ASN.1 initialization error\n"); + exit(1); + } + } +#endif + sigprocmask(SIG_BLOCK, &blockset, &sig_default_set); alarm(MAINTAINANCE_TIME(s)); diff --git a/src/ocserv-args.def b/src/ocserv-args.def index 439d72c9..ec9faf72 100644 --- a/src/ocserv-args.def +++ b/src/ocserv-args.def @@ -493,6 +493,13 @@ no-route = 192.168.5.0/255.255.255.0 #proxy-url = http://example.com/ #proxy-url = http://example.com/%{U}/ +# This option allows you to specify a URL location where a client can +# post using MS-KKDCP, and the message will be forwarded to the provided +# KDC server. That is a translation URL between HTTP and Kerberos. +# This option is available if ocserv is compiled with GSSAPI support. +#kkdcp = /kerberos udp@127.0.0.1:88 application/kerberos +#kkdcp = /kerberos-tcp tcp@127.0.0.1:88 application/kerberos + # # The following options are for (experimental) AnyConnect client # compatibility. diff --git a/src/worker-kkdcp.c b/src/worker-kkdcp.c index 87560158..cceeda9d 100644 --- a/src/worker-kkdcp.c +++ b/src/worker-kkdcp.c @@ -27,13 +27,87 @@ #include #include +#ifdef HAVE_GSSAPI + +int der_decode(const uint8_t *der, unsigned der_size, uint8_t *out, unsigned *out_size, int *error) +{ + int ret, len; + ASN1_TYPE c2 = ASN1_TYPE_EMPTY; + + ret = asn1_create_element(_kkdcp_pkix1_asn, "KDC-PROXY-MESSAGE", &c2); + if (ret != ASN1_SUCCESS) { + *error = ret; + return -1; + } + + ret = asn1_der_decoding(&c2, der, der_size, NULL); + if (ret != ASN1_SUCCESS) { + *error = ret; + ret = -1; + goto cleanup; + } + + len = *out_size; + ret = asn1_read_value(c2, "kerb-message", out, &len); + if (ret != ASN1_SUCCESS) { + *error = ret; + ret = -1; + goto cleanup; + } + *out_size = len; + + ret = 0; + cleanup: + asn1_delete_structure(&c2); + return ret; + +} + +int der_encode_inplace(uint8_t *raw, unsigned *raw_size, unsigned max_size, int *error) +{ + int ret, len; + ASN1_TYPE c2 = ASN1_TYPE_EMPTY; + + ret = asn1_create_element(_kkdcp_pkix1_asn, "KDC-PROXY-MESSAGE", &c2); + if (ret != ASN1_SUCCESS) { + *error = ret; + return -1; + } + + ret = asn1_write_value(c2, "kerb-message", raw, *raw_size); + if (ret != ASN1_SUCCESS) { + *error = ret; + ret = -1; + goto cleanup; + } + + asn1_write_value(c2, "target-domain", NULL, 0); + asn1_write_value(c2, "dclocator-hint", NULL, 0); + + len = max_size; + ret = asn1_der_coding(c2, "", raw, &len, NULL); + if (ret != ASN1_SUCCESS) { + *error = ret; + ret = -1; + goto cleanup; + } + *raw_size = len; + + ret = 0; + cleanup: + asn1_delete_structure(&c2); + return ret; + +} + +#define BUF_SIZE 16*1024 int post_kkdcp_handler(worker_st *ws, unsigned http_ver) { int ret, e, fd; struct http_req_st *req = &ws->req; unsigned i, length; kkdcp_st *handler = NULL; - char buf[16*1024]; + uint8_t *buf = NULL; const char *reason = "Unknown"; for (i=0;iconfig->kkdcp_size;i++) { @@ -71,8 +145,23 @@ int post_kkdcp_handler(worker_st *ws, unsigned http_ver) goto fail; } - ret = send(fd, req->body, req->body_length, 0); - if (ret != req->body_length) { + length = BUF_SIZE; + buf = talloc_size(ws, length); + if (buf == NULL) { + oclog(ws, LOG_INFO, "kkdcp: memory error"); + reason = "Memory error"; + goto fail; + } + + ret = der_decode((uint8_t*)req->body, req->body_length, buf, &length, &e); + if (ret <= 0) { + oclog(ws, LOG_INFO, "kkdcp: DER decoding error: %s", asn1_strerror(e)); + reason = "DER decoding error"; + goto fail; + } + + ret = send(fd, buf, length, 0); + if (ret != length) { if (ret == -1) { e = errno; oclog(ws, LOG_INFO, "kkdcp: send error: %s", strerror(e)); @@ -106,6 +195,13 @@ int post_kkdcp_handler(worker_st *ws, unsigned http_ver) } } + ret = der_encode_inplace(buf, &length, BUF_SIZE, &e); + if (ret < 0) { + oclog(ws, LOG_INFO, "kkdcp: DER encoding error: %s", asn1_strerror(e)); + reason = "DER encoding error"; + goto fail; + } + ret = cstp_printf(ws, "Content-Length: %u\r\n", (unsigned int)length); @@ -142,7 +238,15 @@ int post_kkdcp_handler(worker_st *ws, unsigned http_ver) ret = -1; cleanup: + talloc_free(buf); close(fd); return ret; - } + +#else + +int post_kkdcp_handler(worker_st *ws, unsigned http_ver) +{ + return -1; +} +#endif diff --git a/src/worker.h b/src/worker.h index 35cdea89..2f3986e7 100644 --- a/src/worker.h +++ b/src/worker.h @@ -103,6 +103,12 @@ typedef struct dtls_ciphersuite_st { unsigned gnutls_version; } dtls_ciphersuite_st; +#ifdef HAVE_GSSAPI +# include +/* main has initialized that for us */ +extern ASN1_TYPE _kkdcp_pkix1_asn; +#endif + struct http_req_st { char url[256];