diff --git a/src/Cedar/Proto.c b/src/Cedar/Proto.c index 4b17d02c..2bb33c91 100644 --- a/src/Cedar/Proto.c +++ b/src/Cedar/Proto.c @@ -2,6 +2,21 @@ #include "Proto_OpenVPN.h" +int ProtoOptionCompare(void *p1, void *p2) +{ + PROTO_OPTION *option_1, *option_2; + + if (p1 == NULL || p2 == NULL) + { + return (p1 == NULL && p2 == NULL ? 0 : (p1 == NULL ? -1 : 1)); + } + + option_1 = *(PROTO_OPTION **)p1; + option_2 = *(PROTO_OPTION **)p2; + + return StrCmpi(option_1->Name, option_2->Name); +} + int ProtoContainerCompare(void *p1, void *p2) { PROTO_CONTAINER *container_1, *container_2; @@ -113,6 +128,35 @@ UINT ProtoSessionHash(void *p) return ret; } +bool ProtoEnabled(const PROTO *proto, const char *name) +{ + PROTO_OPTION *option, tmp_o; + PROTO_CONTAINER *container, tmp_c; + + if (proto == NULL || name == NULL) + { + return false; + } + + tmp_c.Name = name; + + container = Search(proto->Containers, &tmp_c); + if (container == NULL) + { + return false; + } + + tmp_o.Name = PROTO_OPTION_TOGGLE_NAME; + + option = Search(container->Options, &tmp_o); + if (option == NULL || option->Type != PROTO_OPTION_BOOL) + { + return false; + } + + return option->Bool; +} + PROTO *ProtoNew(CEDAR *cedar) { PROTO *proto; @@ -170,7 +214,9 @@ void ProtoDelete(PROTO *proto) PROTO_CONTAINER *ProtoContainerNew(const PROTO_IMPL *impl) { UINT i; + PROTO_OPTION *option; PROTO_CONTAINER *container; + const PROTO_OPTION *impl_options; if (impl == NULL) { @@ -179,8 +225,43 @@ PROTO_CONTAINER *ProtoContainerNew(const PROTO_IMPL *impl) container = Malloc(sizeof(PROTO_CONTAINER)); container->Name = impl->Name(); + container->Options = NewList(ProtoOptionCompare); container->Impl = impl; + option = ZeroMalloc(sizeof(PROTO_OPTION)); + option->Name = PROTO_OPTION_TOGGLE_NAME; + option->Type = PROTO_OPTION_BOOL; + option->Bool = true; + + Add(container->Options, option); + + impl_options = impl->Options(); + + for (i = 0; impl_options[i].Name != NULL; ++i) + { + const PROTO_OPTION *impl_option = &impl_options[i]; + + option = ZeroMalloc(sizeof(PROTO_OPTION)); + option->Name = impl_option->Name; + option->Type = impl_option->Type; + + switch (impl_option->Type) + { + case PROTO_OPTION_BOOL: + option->Bool = impl_option->Bool; + break; + case PROTO_OPTION_STRING: + option->String = CopyStr(impl_option->String); + break; + default: + Debug("ProtoContainerNew(): unhandled option type %u!\n", impl_option->Type); + Free(option); + continue; + } + + Add(container->Options, option); + } + Debug("ProtoContainerNew(): %s\n", container->Name); return container; @@ -188,11 +269,28 @@ PROTO_CONTAINER *ProtoContainerNew(const PROTO_IMPL *impl) void ProtoContainerDelete(PROTO_CONTAINER *container) { + UINT i; + LIST *options; + if (container == NULL) { return; } + options = container->Options; + + for (i = 0; i < LIST_NUM(options); ++i) + { + PROTO_OPTION *option = LIST_DATA(options, i); + if (option->Type == PROTO_OPTION_STRING) + { + Free(option->String); + } + + Free(option); + } + + ReleaseList(options); Free(container); } @@ -229,6 +327,7 @@ const PROTO_CONTAINER *ProtoDetect(const PROTO *proto, const PROTO_MODE mode, co PROTO_SESSION *ProtoNewSession(PROTO *proto, const PROTO_CONTAINER *container, const IP *src_ip, const USHORT src_port, const IP *dst_ip, const USHORT dst_port) { + LIST *options; PROTO_SESSION *session; const PROTO_IMPL *impl; @@ -237,16 +336,20 @@ PROTO_SESSION *ProtoNewSession(PROTO *proto, const PROTO_CONTAINER *container, c return NULL; } + options = container->Options; impl = container->Impl; session = ZeroMalloc(sizeof(PROTO_SESSION)); session->SockEvent = NewSockEvent(); session->InterruptManager = NewInterruptManager(); - if (impl->Init != NULL && impl->Init(&session->Param, proto->Cedar, session->InterruptManager, session->SockEvent, NULL, NULL) == false) + LockList(options); + + if (impl->Init != NULL && impl->Init(&session->Param, container->Options, proto->Cedar, session->InterruptManager, session->SockEvent, NULL, NULL) == false) { Debug("ProtoNewSession(): failed to initialize %s\n", container->Name); + UnlockList(options); ReleaseSockEvent(session->SockEvent); FreeInterruptManager(session->InterruptManager); Free(session); @@ -254,6 +357,8 @@ PROTO_SESSION *ProtoNewSession(PROTO *proto, const PROTO_CONTAINER *container, c return NULL; } + UnlockList(options); + session->Proto = proto; session->Impl = impl; @@ -350,6 +455,7 @@ bool ProtoHandleConnection(PROTO *proto, SOCK *sock, const char *protocol) { const PROTO_CONTAINER *container = NULL; + LIST *options; if (protocol != NULL) { @@ -381,18 +487,26 @@ bool ProtoHandleConnection(PROTO *proto, SOCK *sock, const char *protocol) return false; } + options = container->Options; impl = container->Impl; im = NewInterruptManager(); se = NewSockEvent(); - if (impl->Init != NULL && impl->Init(&impl_data, proto->Cedar, im, se, sock->CipherName, sock->RemoteHostname) == false) + LockList(options); + + if (impl->Init != NULL && impl->Init(&impl_data, options, proto->Cedar, im, se, sock->CipherName, sock->RemoteHostname) == false) { Debug("ProtoHandleConnection(): failed to initialize %s\n", container->Name); + + UnlockList(options); FreeInterruptManager(im); ReleaseSockEvent(se); + return false; } + + UnlockList(options); } SetTimeout(sock, TIMEOUT_INFINITE); diff --git a/src/Cedar/Proto.h b/src/Cedar/Proto.h index 79c6814d..29397d06 100644 --- a/src/Cedar/Proto.h +++ b/src/Cedar/Proto.h @@ -1,6 +1,8 @@ #ifndef PROTO_H #define PROTO_H +#define PROTO_OPTION_TOGGLE_NAME "Enabled" + // OpenVPN sends 2 bytes, thus this is the buffer size. // If another protocol requires more bytes to be detected, the buffer size must be increased. #define PROTO_CHECK_BUFFER_SIZE 2 @@ -9,11 +11,18 @@ typedef enum PROTO_MODE { - PROTO_MODE_UNKNOWN = 0, - PROTO_MODE_TCP = 1, - PROTO_MODE_UDP = 2 + PROTO_MODE_UNKNOWN, + PROTO_MODE_TCP, + PROTO_MODE_UDP } PROTO_MODE; +typedef enum PROTO_OPTION_VALUE +{ + PROTO_OPTION_UNKNOWN, + PROTO_OPTION_STRING, + PROTO_OPTION_BOOL +} PROTO_OPTION_VALUE; + typedef struct PROTO { CEDAR *Cedar; @@ -22,10 +31,22 @@ typedef struct PROTO UDPLISTENER *UdpListener; } PROTO; +typedef struct PROTO_OPTION +{ + char *Name; + PROTO_OPTION_VALUE Type; + union + { + bool Bool; + char *String; + }; +} PROTO_OPTION; + typedef struct PROTO_IMPL { const char *(*Name)(); - bool (*Init)(void **param, CEDAR *cedar, INTERRUPT_MANAGER *im, SOCK_EVENT *se, const char *cipher, const char *hostname); + const PROTO_OPTION *(*Options)(); + bool (*Init)(void **param, const LIST *options, CEDAR *cedar, INTERRUPT_MANAGER *im, SOCK_EVENT *se, const char *cipher, const char *hostname); void (*Free)(void *param); bool (*IsPacketForMe)(const PROTO_MODE mode, const UCHAR *data, const UINT size); bool (*ProcessData)(void *param, TCP_RAW_DATA *in, FIFO *out); @@ -35,6 +56,7 @@ typedef struct PROTO_IMPL typedef struct PROTO_CONTAINER { const char *Name; + LIST *Options; const PROTO_IMPL *Impl; } PROTO_CONTAINER; @@ -56,11 +78,14 @@ typedef struct PROTO_SESSION volatile bool Halt; } PROTO_SESSION; +int ProtoOptionCompare(void *p1, void *p2); int ProtoContainerCompare(void *p1, void *p2); int ProtoSessionCompare(void *p1, void *p2); UINT ProtoSessionHash(void *p); +bool ProtoEnabled(const PROTO *proto, const char *name); + PROTO *ProtoNew(CEDAR *cedar); void ProtoDelete(PROTO *proto); diff --git a/src/Cedar/Proto_OpenVPN.c b/src/Cedar/Proto_OpenVPN.c index 54d40707..f03a9c09 100644 --- a/src/Cedar/Proto_OpenVPN.c +++ b/src/Cedar/Proto_OpenVPN.c @@ -19,6 +19,7 @@ const PROTO_IMPL *OvsGetProtoImpl() static const PROTO_IMPL impl = { OvsName, + OvsOptions, OvsInit, OvsFree, OvsIsPacketForMe, @@ -34,9 +35,23 @@ const char *OvsName() return "OpenVPN"; } -bool OvsInit(void **param, CEDAR *cedar, INTERRUPT_MANAGER *im, SOCK_EVENT *se, const char *cipher, const char *hostname) +const PROTO_OPTION *OvsOptions() { - if (param == NULL || cedar == NULL || im == NULL || se == NULL) + static const PROTO_OPTION options[] = + { + { .Name = "DefaultClientOption", .Type = PROTO_OPTION_STRING, .String = "dev-type tun,link-mtu 1500,tun-mtu 1500,cipher AES-128-CBC,auth SHA1,keysize 128,key-method 2,tls-client" }, + { .Name = "Obfuscation", .Type = PROTO_OPTION_BOOL, .Bool = false }, + { .Name = "ObfuscationMask", .Type = PROTO_OPTION_STRING, .String = ""}, + { .Name = "PushDummyIPv4AddressOnL2Mode", .Type = PROTO_OPTION_BOOL, .Bool = true }, + { .Name = NULL, .Type = PROTO_OPTION_UNKNOWN } + }; + + return options; +} + +bool OvsInit(void **param, const LIST *options, CEDAR *cedar, INTERRUPT_MANAGER *im, SOCK_EVENT *se, const char *cipher, const char *hostname) +{ + if (param == NULL || options == NULL || cedar == NULL || im == NULL || se == NULL) { return false; } diff --git a/src/Cedar/Proto_OpenVPN.h b/src/Cedar/Proto_OpenVPN.h index 8bea7f32..c468d587 100644 --- a/src/Cedar/Proto_OpenVPN.h +++ b/src/Cedar/Proto_OpenVPN.h @@ -210,7 +210,8 @@ struct OPENVPN_SERVER //// Function prototype const PROTO_IMPL *OvsGetProtoImpl(); const char *OvsName(); -bool OvsInit(void **param, CEDAR *cedar, INTERRUPT_MANAGER *im, SOCK_EVENT *se, const char *cipher, const char *hostname); +const PROTO_OPTION *OvsOptions(); +bool OvsInit(void **param, const LIST *options, CEDAR *cedar, INTERRUPT_MANAGER *im, SOCK_EVENT *se, const char *cipher, const char *hostname); void OvsFree(void *param); bool OvsIsPacketForMe(const PROTO_MODE mode, const UCHAR *data, const UINT size); bool OvsProcessData(void *param, TCP_RAW_DATA *in, FIFO *out); diff --git a/src/Cedar/Proto_SSTP.c b/src/Cedar/Proto_SSTP.c index cb06ec74..5391e36c 100644 --- a/src/Cedar/Proto_SSTP.c +++ b/src/Cedar/Proto_SSTP.c @@ -21,6 +21,7 @@ const PROTO_IMPL *SstpGetProtoImpl() static const PROTO_IMPL impl = { SstpName, + SstpOptions, SstpInit, SstpFree, NULL, @@ -36,9 +37,19 @@ const char *SstpName() return "SSTP"; } -bool SstpInit(void **param, struct CEDAR *cedar, INTERRUPT_MANAGER *im, SOCK_EVENT *se, const char *cipher, const char *hostname) +const PROTO_OPTION *SstpOptions() { - if (param == NULL || cedar == NULL || im == NULL || se == NULL) + static const PROTO_OPTION options[] = + { + { .Name = NULL, .Type = PROTO_OPTION_UNKNOWN } + }; + + return options; +} + +bool SstpInit(void **param, const LIST *options, CEDAR *cedar, INTERRUPT_MANAGER *im, SOCK_EVENT *se, const char *cipher, const char *hostname) +{ + if (param == NULL || options == NULL || cedar == NULL || im == NULL || se == NULL) { return false; } diff --git a/src/Cedar/Proto_SSTP.h b/src/Cedar/Proto_SSTP.h index bb090c0b..44e167c2 100644 --- a/src/Cedar/Proto_SSTP.h +++ b/src/Cedar/Proto_SSTP.h @@ -123,8 +123,9 @@ struct SSTP_SERVER //// Function prototype const PROTO_IMPL *SstpGetProtoImpl(); +const PROTO_OPTION *SstpOptions(); const char *SstpName(); -bool SstpInit(void **param, struct CEDAR *cedar, INTERRUPT_MANAGER *im, SOCK_EVENT *se, const char *cipher, const char *hostname); +bool SstpInit(void **param, const LIST *options, CEDAR *cedar, INTERRUPT_MANAGER *im, SOCK_EVENT *se, const char *cipher, const char *hostname); void SstpFree(void *param); bool SstpProcessData(void *param, TCP_RAW_DATA *in, FIFO *out);