diff --git a/flake.nix b/flake.nix index 6378faa..828f296 100644 --- a/flake.nix +++ b/flake.nix @@ -10,6 +10,9 @@ pname = "softether-5"; version = "5.02.5187"; in { - packages.${system}.default = pkgs.callPackage ./package.nix { inherit pname version; }; + packages.${system} = { + default = pkgs.callPackage ./package.nix { inherit pname version; }; + sha0 = pkgs.callPackage ./sha0 {}; + }; }; } diff --git a/package.nix b/package.nix index 5d4bec9..cc65897 100644 --- a/package.nix +++ b/package.nix @@ -3,6 +3,7 @@ lib, stdenv, fetchFromGitHub, + debug ? false, pname ? "softether", logDir ? "/var/log/${pname}", pidDir ? "/run/${pname}", @@ -22,6 +23,8 @@ stdenv.mkDerivation (finalAttrs: { fetchSubmodules = true; }; + patches = [ ./patches/ipb-profile-key.patch ./patches/tap-name-no-prefix.patch ./patches/prevent-dmesg-call.patch ./patches/simplify_l2tp_auth.patch ]; + nativeBuildInputs = with pkgs; [ cmake pkg-config @@ -39,7 +42,7 @@ stdenv.mkDerivation (finalAttrs: { cmakeFlags = [ "-DSE_PIDDIR=${pidDir}" "-DSE_LOGDIR=${logDir}" "-DSE_DBDIR=${dbDir}" "-DCMAKE_INSTALL_SYSTEMD_UNITDIR=" - "-DCMAKE_BUILD_TYPE=Debug" + (lib.optionalString debug "-DCMAKE_BUILD_TYPE=Debug") "-DCMAKE_INSTALL_PREFIX=${placeholder "out"}" "-DCMAKE_INSTALL_LIBDIR=lib" "-DCMAKE_INSTALL_INCLUDEDIR=include" diff --git a/patches/ipb-profile-key.patch b/patches/ipb-profile-key.patch new file mode 100644 index 0000000..f4e987e --- /dev/null +++ b/patches/ipb-profile-key.patch @@ -0,0 +1,112 @@ +diff --git a/src/Cedar/Proto_OpenVPN.c b/src/Cedar/Proto_OpenVPN.c +index 9143d46..9e2d9d8 100644 +--- a/src/Cedar/Proto_OpenVPN.c ++++ b/src/Cedar/Proto_OpenVPN.c +@@ -823,7 +823,8 @@ void OvsProcessRecvControlPacket(OPENVPN_SERVER *s, OPENVPN_SESSION *se, OPENVPN + s->Dh = DhNewFromBits(s->Cedar->DhParamBits); + } + +- c->SslPipe = NewSslPipeEx(true, s->Cedar->ServerX, s->Cedar->ServerK, s->Dh, true, &c->ClientCert); ++ // disable automatic cert auth, use profileKey based auth instead ++ c->SslPipe = NewSslPipeEx(true, s->Cedar->ServerX, s->Cedar->ServerK, s->Dh, false, NULL); + if (c->SslPipe == NULL) + { + return; +@@ -1477,6 +1478,36 @@ void OvsWriteStringToBuf(BUF *b, char *str, UINT max_size) + Free(tmp); + } + ++// Extract the value of profile-key as UV_TOKEN from the openvpn peer info ++char *ExtractProfileKeyAsUvTokenFromPeerInfo(char *peerInfo) ++{ ++ if (peerInfo == NULL) return NULL; ++ ++ TOKEN_LIST *tokens = ParseTokenWithoutNullStr(peerInfo, "\r\n"); ++ if (tokens == NULL) return NULL; ++ ++ char *result = NULL; ++ ++ for (UINT i = 0; i < tokens->NumTokens; i++) ++ { ++ char *line = tokens->Token[i]; ++ Trim(line); ++ char key[1536], value[1536]; // same length allocated to peerInfo ++ ++ if (GetKeyAndValue(line, key, sizeof(key), value, sizeof(value), "=")) ++ { ++ if (StrCmpi(key, "UV_TOKEN") == 0) ++ { ++ result = CopyStr(value); ++ break; ++ } ++ } ++ } ++ ++ FreeToken(tokens); ++ return result; ++} ++ + // Parse the KEY_METHOD2 + UINT OvsParseKeyMethod2(OPENVPN_KEY_METHOD_2 *ret, UCHAR *data, UINT size, bool client_mode) + { +@@ -1527,6 +1558,16 @@ UINT OvsParseKeyMethod2(OPENVPN_KEY_METHOD_2 *ret, UCHAR *data, UINT size, bool + } + } + ++ if(IsEmptyStr(ret->Username)) ++ { ++ // keep it the same as softether protocol, which also sends entire profilekey as username and a password with any unimportant value ++ char *ProfileKey = ExtractProfileKeyAsUvTokenFromPeerInfo(ret->PeerInfo); ++ StrCpy(ret->Username, sizeof(ret->Username), ProfileKey); ++ ret->Password[0] = '\0'; // empty string ++ ++ Free(ProfileKey); ++ } ++ + FreeBuf(b); + + return read_size; +diff --git a/src/Cedar/Proto_OpenVPN.h b/src/Cedar/Proto_OpenVPN.h +index 0ca10f0..a0ad37b 100644 +--- a/src/Cedar/Proto_OpenVPN.h ++++ b/src/Cedar/Proto_OpenVPN.h +@@ -255,6 +255,7 @@ void OvsSendControlPacketEx(OPENVPN_CHANNEL *c, UCHAR opcode, UCHAR *data, UINT + void OvsSendControlPacketWithAutoSplit(OPENVPN_CHANNEL *c, UCHAR opcode, UCHAR *data, UINT data_size); + void OvsFreeControlPacket(OPENVPN_CONTROL_PACKET *p); + void OvsDeleteFromSendingControlPacketList(OPENVPN_CHANNEL *c, UINT num_acks, UINT *acks); ++char *ExtractProfileKeyAsUvTokenFromPeerInfo(char *str); + UINT OvsParseKeyMethod2(OPENVPN_KEY_METHOD_2 *ret, UCHAR *data, UINT size, bool client_mode); + bool OvsReadStringFromBuf(BUF *b, char *str, UINT str_size); + void OvsSetupSessionParameters(OPENVPN_SERVER *s, OPENVPN_SESSION *se, OPENVPN_CHANNEL *c, OPENVPN_KEY_METHOD_2 *data); +diff --git a/src/Cedar/Protocol.c b/src/Cedar/Protocol.c +index a0da6c7..d0c9838 100644 +--- a/src/Cedar/Protocol.c ++++ b/src/Cedar/Protocol.c +@@ -2861,7 +2861,13 @@ bool ServerAccept(CONNECTION *c) + } + + // Create a Session +- StrLower(username); ++ if (admin_mode == false && StrLen(username) == 6) ++ { ++ //concat password to username so it's the combined profileKey ++ //this is for recognizing L2TP clients ++ StrCat(username, sizeof(username), plain_password); ++ } ++ + s = NewServerSessionEx(c->Cedar, c, hub, username, policy, c->IsInProc, + (c->IsInProc && IsZero(assigned_ipc_mac_address, 6) == false) ? assigned_ipc_mac_address : NULL); + +diff --git a/src/Cedar/Session.c b/src/Cedar/Session.c +index e0bb58a..09e77b3 100644 +--- a/src/Cedar/Session.c ++++ b/src/Cedar/Session.c +@@ -2186,7 +2186,7 @@ SESSION *NewServerSessionEx(CEDAR *cedar, CONNECTION *c, HUB *h, char *username, + StrCpy(hub_name_upper, sizeof(hub_name_upper), h->Name); + StrUpper(hub_name_upper); + StrCpy(user_name_upper, sizeof(user_name_upper), username); +- StrUpper(user_name_upper); ++ // do not uppercase the profileKey + + if ((StrCmpi(username, ADMINISTRATOR_USERNAME) != 0) && (StrCmpi(username, BRIDGE_USER_NAME) != 0) || (cedar->Server == NULL || cedar->Server->ServerType == SERVER_TYPE_STANDALONE)) + { diff --git a/patches/prevent-dmesg-call.patch b/patches/prevent-dmesg-call.patch new file mode 100644 index 0000000..3572d29 --- /dev/null +++ b/patches/prevent-dmesg-call.patch @@ -0,0 +1,12 @@ +diff --git a/src/Mayaqua/Unix.c b/src/Mayaqua/Unix.c +index 0c3778d..657dea4 100644 +--- a/src/Mayaqua/Unix.c ++++ b/src/Mayaqua/Unix.c +@@ -345,6 +345,7 @@ bool UnixIsInVmMain() + + bool UnixIsInVm() + { ++ return false; + static bool is_in_vm_flag = false; + static bool is_in_vm_ret = false; + diff --git a/patches/simplify_l2tp_auth.patch b/patches/simplify_l2tp_auth.patch new file mode 100644 index 0000000..d871b75 --- /dev/null +++ b/patches/simplify_l2tp_auth.patch @@ -0,0 +1,60 @@ +diff --git a/src/Cedar/Proto_PPP.c b/src/Cedar/Proto_PPP.c +index e9908e0..120a65b 100644 +--- a/src/Cedar/Proto_PPP.c ++++ b/src/Cedar/Proto_PPP.c +@@ -320,12 +320,12 @@ void PPPThread(THREAD *thread, void *param) + + if (p->PPPStatus == PPP_STATUS_CONNECTED && authReqSent == false) + { +- // EAP code ++ // PAP code + PPP_LCP *c = NewPPPLCP(PPP_LCP_CODE_REQ, 0); +- USHORT eap_code = Endian16(PPP_LCP_AUTH_EAP); ++ USHORT pap_code = Endian16(PPP_LCP_AUTH_PAP); + +- Debug("Request EAP\n"); +- Add(c->OptionList, NewPPPOption(PPP_LCP_OPTION_AUTH, &eap_code, sizeof(eap_code))); ++ Debug("Request PAP\n"); ++ Add(c->OptionList, NewPPPOption(PPP_LCP_OPTION_AUTH, &pap_code, sizeof(pap_code))); + if (PPPSendAndRetransmitRequest(p, PPP_PROTOCOL_LCP, c) == false) + { + PPPSetStatus(p, PPP_STATUS_FAIL); +@@ -1871,12 +1871,6 @@ bool PPPProcessPAPRequestPacket(PPP_SESSION *p, PPP_PACKET *pp) + + p->AuthOk = true; + } +- else +- { +- PPPSetStatus(p, PPP_STATUS_FAIL); +- WHERE; +- return false; +- } + } + } + } +diff --git a/src/Cedar/Proto_PPP.h b/src/Cedar/Proto_PPP.h +index 1278fbc..20500cc 100644 +--- a/src/Cedar/Proto_PPP.h ++++ b/src/Cedar/Proto_PPP.h +@@ -32,18 +32,18 @@ + #define PPP_CODE_IS_REQUEST(protocol, c) ((((protocol) == PPP_PROTOCOL_LCP || (protocol) == PPP_PROTOCOL_IPCP || (protocol) == PPP_PROTOCOL_IPV6CP) && PPP_LCP_CODE_IS_REQUEST(c)) || (((protocol) == PPP_PROTOCOL_PAP) && PPP_PAP_CODE_IS_REQUEST(c)) || (((protocol) == PPP_PROTOCOL_CHAP) && PPP_CHAP_CODE_IS_REQUEST(c)) || (((protocol) == PPP_PROTOCOL_EAP) && PPP_EAP_CODE_IS_REQUEST(c))) + #define PPP_CODE_IS_WITH_OPTION_LIST(protocol, c) ((((protocol) == PPP_PROTOCOL_LCP || (protocol) == PPP_PROTOCOL_IPCP || (protocol) == PPP_PROTOCOL_IPV6CP) && PPP_LCP_CODE_IS_WITH_OPTION_LIST(c)) || false) + +-#define PPP_IS_SUPPORTED_PROTOCOL(p) ((p) == PPP_PROTOCOL_LCP || (p) == PPP_PROTOCOL_PAP || (p) == PPP_PROTOCOL_CHAP || (p) == PPP_PROTOCOL_IPCP || (p) == PPP_PROTOCOL_IPV6CP || (p) == PPP_PROTOCOL_IP || (p) == PPP_PROTOCOL_IPV6 || (p) == PPP_PROTOCOL_EAP ) ++#define PPP_IS_SUPPORTED_PROTOCOL(p) ((p) == PPP_PROTOCOL_LCP || (p) == PPP_PROTOCOL_PAP || (p) == PPP_PROTOCOL_IPCP || (p) == PPP_PROTOCOL_IP) + + #define PPP_STATUS_IS_UNAVAILABLE(c) ((c) == PPP_STATUS_FAIL || (c) == PPP_STATUS_AUTH_FAIL || (c) == PPP_STATUS_CLOSING || (c) == PPP_STATUS_CLOSING_WAIT || (c) == PPP_STATUS_CLOSED) + + //// Constants + + // Time-out value +-#define PPP_PACKET_RECV_TIMEOUT (15 * 1000) // Timeout until the next packet is received (3/4 of default policy) ++#define PPP_PACKET_RECV_TIMEOUT (90 * 1000) // Timeout until the next packet is received (3/4 of default policy) + #define PPP_PACKET_RESEND_INTERVAL (3 * 1000) // Retransmission interval of the last packet + #define PPP_TERMINATE_TIMEOUT 2000 // Timeout value to complete disconnection after requesting to disconnect in the PPP + #define PPP_ECHO_SEND_INTERVAL 4792 // Transmission interval of PPP Echo Request +-#define PPP_DATA_TIMEOUT (20 * 1000) // Communication time-out (from default policy) ++#define PPP_DATA_TIMEOUT (120 * 1000) // Communication time-out (from default policy) + + // MRU + #define PPP_MRU_DEFAULT 1500 // Default value diff --git a/patches/tap-name-no-prefix.patch b/patches/tap-name-no-prefix.patch new file mode 100644 index 0000000..17fe0f0 --- /dev/null +++ b/patches/tap-name-no-prefix.patch @@ -0,0 +1,13 @@ +diff --git a/src/Cedar/VLanUnix.c b/src/Cedar/VLanUnix.c +index 784bd74..127d15e 100644 +--- a/src/Cedar/VLanUnix.c ++++ b/src/Cedar/VLanUnix.c +@@ -339,7 +339,7 @@ void GenerateTunName(char *name, char *prefix, char *tun_name, size_t tun_name_l + StrCpy(instance_name_lower, sizeof(instance_name_lower), name); + Trim(instance_name_lower); + StrLower(instance_name_lower); +- Format(tun_name, tun_name_len, "%s_%s", prefix, instance_name_lower); ++ Format(tun_name, tun_name_len, "%s", instance_name_lower); + + tun_name[15] = 0; + } diff --git a/sha0/default.nix b/sha0/default.nix new file mode 100644 index 0000000..5570057 --- /dev/null +++ b/sha0/default.nix @@ -0,0 +1,20 @@ +{ pkgs, stdenv, ... }: +stdenv.mkDerivation { + pname = "sha0"; + version = "0.1.0"; + + src = ./.; + + buildPhase = '' + cc -O2 -o sha0 sha0.c main.c + ''; + + installPhase = '' + mkdir -p $out/bin + cp sha0 $out/bin/ + ''; + + meta = with pkgs.lib; { + description = "SHA-0 hashing CLI tool"; + }; +} \ No newline at end of file diff --git a/sha0/main.c b/sha0/main.c new file mode 100644 index 0000000..d4bc99f --- /dev/null +++ b/sha0/main.c @@ -0,0 +1,25 @@ +#include +#include +#include "types.h" + +// Declare the function from sha0.c +const UCHAR* MY_SHA0_hash(const void* data, int len, UCHAR* digest); + +#define SHA0_DIGEST_SIZE 20 + +int main(int argc, char **argv) { + if (argc != 2) { + fprintf(stderr, "Usage: %s \n", argv[0]); + return 1; + } + + UCHAR digest[SHA0_DIGEST_SIZE]; + MY_SHA0_hash((const UCHAR *)argv[1], strlen(argv[1]), digest); + + for (int i = 0; i < SHA0_DIGEST_SIZE; i++) { + printf("%02x", digest[i]); + } + printf("\n"); + + return 0; +} \ No newline at end of file diff --git a/sha0/sha0.c b/sha0/sha0.c new file mode 100644 index 0000000..37a0ff7 --- /dev/null +++ b/sha0/sha0.c @@ -0,0 +1,148 @@ +// Copied from softether's src/Cedar/Encrypt.c + +// define custom types as defined in SoftEther's code +#include "types.h" + +// rest is copied as is + +///////////////////////// +// SHA0 implementation // +///////////////////////// + +// Source codes from: +// https://android.googlesource.com/platform/system/core/+/81df1cc77722000f8d0025c1ab00ced123aa573c/libmincrypt/sha.c +// https://android.googlesource.com/platform/system/core/+/81df1cc77722000f8d0025c1ab00ced123aa573c/include/mincrypt/hash-internal.h +// https://android.googlesource.com/platform/system/core/+/81df1cc77722000f8d0025c1ab00ced123aa573c/include/mincrypt/sha.h + +/* + * Copyright 2013 The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Google Inc. nor the names of its contributors may + * be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Google Inc. ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL Google Inc. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#define rol(bits, value) (((value) << (bits)) | ((value) >> (32 - (bits)))) + +typedef struct MY_SHA0_CTX { +// const HASH_VTAB * f; + UINT64 count; + UCHAR buf[64]; + UINT state[8]; // upto SHA2 +} MY_SHA0_CTX; + +#define MY_SHA0_DIGEST_SIZE 20 + +static void MY_SHA0_Transform(MY_SHA0_CTX* ctx) { + UINT W[80]; + UINT A, B, C, D, E; + UCHAR* p = ctx->buf; + int t; + for(t = 0; t < 16; ++t) { + UINT tmp = *p++ << 24; + tmp |= *p++ << 16; + tmp |= *p++ << 8; + tmp |= *p++; + W[t] = tmp; + } + for(; t < 80; t++) { + //W[t] = rol(1,W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16]); + W[t] = (1,W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16]); + } + A = ctx->state[0]; + B = ctx->state[1]; + C = ctx->state[2]; + D = ctx->state[3]; + E = ctx->state[4]; + for(t = 0; t < 80; t++) { + UINT tmp = rol(5,A) + E + W[t]; + if (t < 20) + tmp += (D^(B&(C^D))) + 0x5A827999; + else if ( t < 40) + tmp += (B^C^D) + 0x6ED9EBA1; + else if ( t < 60) + tmp += ((B&C)|(D&(B|C))) + 0x8F1BBCDC; + else + tmp += (B^C^D) + 0xCA62C1D6; + E = D; + D = C; + C = rol(30,B); + B = A; + A = tmp; + } + ctx->state[0] += A; + ctx->state[1] += B; + ctx->state[2] += C; + ctx->state[3] += D; + ctx->state[4] += E; +} +void MY_SHA0_init(MY_SHA0_CTX* ctx) { + //ctx->f = &SHA_VTAB; + ctx->state[0] = 0x67452301; + ctx->state[1] = 0xEFCDAB89; + ctx->state[2] = 0x98BADCFE; + ctx->state[3] = 0x10325476; + ctx->state[4] = 0xC3D2E1F0; + ctx->count = 0; +} +void MY_SHA0_update(MY_SHA0_CTX* ctx, const void* data, int len) { + int i = (int) (ctx->count & 63); + const UCHAR* p = (const UCHAR*)data; + ctx->count += len; + while (len--) { + ctx->buf[i++] = *p++; + if (i == 64) { + MY_SHA0_Transform(ctx); + i = 0; + } + } +} +const UCHAR* MY_SHA0_final(MY_SHA0_CTX* ctx) { + UCHAR *p = ctx->buf; + UINT64 cnt = ctx->count * 8; + int i; + MY_SHA0_update(ctx, (UCHAR*)"\x80", 1); + while ((ctx->count & 63) != 56) { + MY_SHA0_update(ctx, (UCHAR*)"\0", 1); + } + for (i = 0; i < 8; ++i) { + UCHAR tmp = (UCHAR) (cnt >> ((7 - i) * 8)); + MY_SHA0_update(ctx, &tmp, 1); + } + for (i = 0; i < 5; i++) { + UINT tmp = ctx->state[i]; + *p++ = tmp >> 24; + *p++ = tmp >> 16; + *p++ = tmp >> 8; + *p++ = tmp >> 0; + } + return ctx->buf; +} +/* Convenience function */ +const UCHAR* MY_SHA0_hash(const void* data, int len, UCHAR* digest) { + MY_SHA0_CTX ctx; + MY_SHA0_init(&ctx); + MY_SHA0_update(&ctx, data, len); + memcpy(digest, MY_SHA0_final(&ctx), MY_SHA0_DIGEST_SIZE); + return digest; +} \ No newline at end of file diff --git a/sha0/types.h b/sha0/types.h new file mode 100644 index 0000000..4c0523b --- /dev/null +++ b/sha0/types.h @@ -0,0 +1,8 @@ +#ifndef TYPES_H +#define TYPES_H + +typedef unsigned long long UINT64; +typedef unsigned char UCHAR; +typedef unsigned int UINT; + +#endif