mirror of
https://github.com/yuezk/GlobalProtect-openconnect.git
synced 2025-05-20 07:26:58 -04:00
Compare commits
18 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
1e70dd088f | ||
|
2dc7f97ff0 | ||
|
6542d677a9 | ||
|
222fe26cea | ||
|
ed413ee029 | ||
|
516d685c9e | ||
|
54e2371022 | ||
|
511cc5ebd7 | ||
|
30bec40338 | ||
|
0bb9353a11 | ||
|
80134f5a2b | ||
|
57e20fe478 | ||
|
9317430968 | ||
|
f92b04e99a | ||
|
a1c63f8498 | ||
|
9460d498fc | ||
|
c2a6a436a5 | ||
|
c578e35178 |
1
.vscode/settings.json
vendored
1
.vscode/settings.json
vendored
@@ -11,6 +11,7 @@
|
|||||||
"distro",
|
"distro",
|
||||||
"dotenv",
|
"dotenv",
|
||||||
"dotenvy",
|
"dotenvy",
|
||||||
|
"dtls",
|
||||||
"getconfig",
|
"getconfig",
|
||||||
"globalprotect",
|
"globalprotect",
|
||||||
"globalprotectcallback",
|
"globalprotectcallback",
|
||||||
|
260
Cargo.lock
generated
260
Cargo.lock
generated
@@ -266,9 +266,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitflags"
|
name = "bitflags"
|
||||||
version = "2.4.1"
|
version = "2.6.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07"
|
checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "block"
|
name = "block"
|
||||||
@@ -285,6 +285,15 @@ dependencies = [
|
|||||||
"generic-array",
|
"generic-array",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "block2"
|
||||||
|
version = "0.5.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2c132eebf10f5cad5289222520a4a058514204aed6d791f1cf4fe8088b82d15f"
|
||||||
|
dependencies = [
|
||||||
|
"objc2",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "brotli"
|
name = "brotli"
|
||||||
version = "3.4.0"
|
version = "3.4.0"
|
||||||
@@ -525,7 +534,7 @@ dependencies = [
|
|||||||
"bitflags 1.3.2",
|
"bitflags 1.3.2",
|
||||||
"block",
|
"block",
|
||||||
"cocoa-foundation",
|
"cocoa-foundation",
|
||||||
"core-foundation",
|
"core-foundation 0.9.4",
|
||||||
"core-graphics",
|
"core-graphics",
|
||||||
"foreign-types",
|
"foreign-types",
|
||||||
"libc",
|
"libc",
|
||||||
@@ -540,7 +549,7 @@ checksum = "8c6234cbb2e4c785b456c0644748b1ac416dd045799740356f8363dfe00c93f7"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 1.3.2",
|
"bitflags 1.3.2",
|
||||||
"block",
|
"block",
|
||||||
"core-foundation",
|
"core-foundation 0.9.4",
|
||||||
"core-graphics-types",
|
"core-graphics-types",
|
||||||
"libc",
|
"libc",
|
||||||
"objc",
|
"objc",
|
||||||
@@ -570,7 +579,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "common"
|
name = "common"
|
||||||
version = "2.3.4"
|
version = "2.3.8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"is_executable",
|
"is_executable",
|
||||||
]
|
]
|
||||||
@@ -606,10 +615,20 @@ dependencies = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "core-foundation-sys"
|
name = "core-foundation"
|
||||||
version = "0.8.6"
|
version = "0.10.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f"
|
checksum = "b55271e5c8c478ad3f38ad24ef34923091e0548492a266d19b3c0b4d82574c63"
|
||||||
|
dependencies = [
|
||||||
|
"core-foundation-sys",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "core-foundation-sys"
|
||||||
|
version = "0.8.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "core-graphics"
|
name = "core-graphics"
|
||||||
@@ -618,7 +637,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "2581bbab3b8ffc6fcbd550bf46c355135d16e9ff2a6ea032ad6b9bf1d7efe4fb"
|
checksum = "2581bbab3b8ffc6fcbd550bf46c355135d16e9ff2a6ea032ad6b9bf1d7efe4fb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 1.3.2",
|
"bitflags 1.3.2",
|
||||||
"core-foundation",
|
"core-foundation 0.9.4",
|
||||||
"core-graphics-types",
|
"core-graphics-types",
|
||||||
"foreign-types",
|
"foreign-types",
|
||||||
"libc",
|
"libc",
|
||||||
@@ -631,7 +650,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf"
|
checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 1.3.2",
|
"bitflags 1.3.2",
|
||||||
"core-foundation",
|
"core-foundation 0.9.4",
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -1430,7 +1449,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gpapi"
|
name = "gpapi"
|
||||||
version = "2.3.4"
|
version = "2.3.8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"base64 0.21.5",
|
"base64 0.21.5",
|
||||||
@@ -1459,12 +1478,14 @@ dependencies = [
|
|||||||
"url",
|
"url",
|
||||||
"urlencoding",
|
"urlencoding",
|
||||||
"uzers",
|
"uzers",
|
||||||
|
"webbrowser",
|
||||||
|
"which",
|
||||||
"whoami",
|
"whoami",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gpauth"
|
name = "gpauth"
|
||||||
version = "2.3.4"
|
version = "2.3.8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"clap",
|
"clap",
|
||||||
@@ -1485,7 +1506,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gpclient"
|
name = "gpclient"
|
||||||
version = "2.3.4"
|
version = "2.3.8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"clap",
|
"clap",
|
||||||
@@ -1507,7 +1528,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gpgui-helper"
|
name = "gpgui-helper"
|
||||||
version = "2.3.4"
|
version = "2.3.8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"clap",
|
"clap",
|
||||||
@@ -1525,7 +1546,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gpservice"
|
name = "gpservice"
|
||||||
version = "2.3.4"
|
version = "2.3.8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"axum",
|
"axum",
|
||||||
@@ -1676,6 +1697,15 @@ version = "0.4.3"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "home"
|
||||||
|
version = "0.5.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5"
|
||||||
|
dependencies = [
|
||||||
|
"windows-sys 0.52.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "html-escape"
|
name = "html-escape"
|
||||||
version = "0.2.13"
|
version = "0.2.13"
|
||||||
@@ -2105,6 +2135,22 @@ dependencies = [
|
|||||||
"walkdir",
|
"walkdir",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "jni"
|
||||||
|
version = "0.21.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97"
|
||||||
|
dependencies = [
|
||||||
|
"cesu8",
|
||||||
|
"cfg-if",
|
||||||
|
"combine",
|
||||||
|
"jni-sys",
|
||||||
|
"log",
|
||||||
|
"thiserror",
|
||||||
|
"walkdir",
|
||||||
|
"windows-sys 0.45.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "jni-sys"
|
name = "jni-sys"
|
||||||
version = "0.3.0"
|
version = "0.3.0"
|
||||||
@@ -2153,9 +2199,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.151"
|
version = "0.2.155"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4"
|
checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libredox"
|
name = "libredox"
|
||||||
@@ -2163,7 +2209,7 @@ version = "0.0.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "85c833ca1e66078851dba29046874e38f08b2c883700aa29a03ddd3b23814ee8"
|
checksum = "85c833ca1e66078851dba29046874e38f08b2c883700aa29a03ddd3b23814ee8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.4.1",
|
"bitflags 2.6.0",
|
||||||
"libc",
|
"libc",
|
||||||
"redox_syscall",
|
"redox_syscall",
|
||||||
]
|
]
|
||||||
@@ -2416,6 +2462,12 @@ dependencies = [
|
|||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-conv"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num-integer"
|
name = "num-integer"
|
||||||
version = "0.1.45"
|
version = "0.1.45"
|
||||||
@@ -2487,6 +2539,40 @@ dependencies = [
|
|||||||
"objc_exception",
|
"objc_exception",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "objc-sys"
|
||||||
|
version = "0.3.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cdb91bdd390c7ce1a8607f35f3ca7151b65afc0ff5ff3b34fa350f7d7c7e4310"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "objc2"
|
||||||
|
version = "0.5.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "46a785d4eeff09c14c487497c162e92766fbb3e4059a71840cecc03d9a50b804"
|
||||||
|
dependencies = [
|
||||||
|
"objc-sys",
|
||||||
|
"objc2-encode",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "objc2-encode"
|
||||||
|
version = "4.0.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7891e71393cd1f227313c9379a26a584ff3d7e6e7159e988851f0934c993f0f8"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "objc2-foundation"
|
||||||
|
version = "0.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 2.6.0",
|
||||||
|
"block2",
|
||||||
|
"libc",
|
||||||
|
"objc2",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "objc_exception"
|
name = "objc_exception"
|
||||||
version = "0.1.2"
|
version = "0.1.2"
|
||||||
@@ -2539,7 +2625,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "openconnect"
|
name = "openconnect"
|
||||||
version = "2.3.4"
|
version = "2.3.8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cc",
|
"cc",
|
||||||
"common",
|
"common",
|
||||||
@@ -2548,11 +2634,11 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "openssl"
|
name = "openssl"
|
||||||
version = "0.10.62"
|
version = "0.10.66"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8cde4d2d9200ad5909f8dac647e29482e07c3a35de8a13fce7c9c7747ad9f671"
|
checksum = "9529f4786b70a3e8c61e11179af17ab6188ad8d0ded78c5529441ed39d4bd9c1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.4.1",
|
"bitflags 2.6.0",
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"foreign-types",
|
"foreign-types",
|
||||||
"libc",
|
"libc",
|
||||||
@@ -2589,9 +2675,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "openssl-sys"
|
name = "openssl-sys"
|
||||||
version = "0.9.98"
|
version = "0.9.103"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c1665caf8ab2dc9aef43d1c0023bd904633a6a05cb30b0ad59bec2ae986e57a7"
|
checksum = "7f9e8deee91df40a943c71b917e5874b951d32a802526c85721ce3b776c929d6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cc",
|
"cc",
|
||||||
"libc",
|
"libc",
|
||||||
@@ -3234,11 +3320,11 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustix"
|
name = "rustix"
|
||||||
version = "0.38.28"
|
version = "0.38.34"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "72e572a5e8ca657d7366229cdde4bd14c4eb5499a9573d4d366fe1b599daa316"
|
checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.4.1",
|
"bitflags 2.6.0",
|
||||||
"errno",
|
"errno",
|
||||||
"libc",
|
"libc",
|
||||||
"linux-raw-sys",
|
"linux-raw-sys",
|
||||||
@@ -3300,7 +3386,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de"
|
checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 1.3.2",
|
"bitflags 1.3.2",
|
||||||
"core-foundation",
|
"core-foundation 0.9.4",
|
||||||
"core-foundation-sys",
|
"core-foundation-sys",
|
||||||
"libc",
|
"libc",
|
||||||
"security-framework-sys",
|
"security-framework-sys",
|
||||||
@@ -3761,7 +3847,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7"
|
checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 1.3.2",
|
"bitflags 1.3.2",
|
||||||
"core-foundation",
|
"core-foundation 0.9.4",
|
||||||
"system-configuration-sys",
|
"system-configuration-sys",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -3811,7 +3897,7 @@ dependencies = [
|
|||||||
"cairo-rs",
|
"cairo-rs",
|
||||||
"cc",
|
"cc",
|
||||||
"cocoa",
|
"cocoa",
|
||||||
"core-foundation",
|
"core-foundation 0.9.4",
|
||||||
"core-graphics",
|
"core-graphics",
|
||||||
"crossbeam-channel",
|
"crossbeam-channel",
|
||||||
"dispatch",
|
"dispatch",
|
||||||
@@ -3826,7 +3912,7 @@ dependencies = [
|
|||||||
"gtk",
|
"gtk",
|
||||||
"image",
|
"image",
|
||||||
"instant",
|
"instant",
|
||||||
"jni",
|
"jni 0.20.0",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"libc",
|
"libc",
|
||||||
"log",
|
"log",
|
||||||
@@ -4133,12 +4219,13 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "time"
|
name = "time"
|
||||||
version = "0.3.31"
|
version = "0.3.36"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f657ba42c3f86e7680e53c8cd3af8abbe56b5491790b46e22e19c0d57463583e"
|
checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"deranged",
|
"deranged",
|
||||||
"itoa 1.0.10",
|
"itoa 1.0.10",
|
||||||
|
"num-conv",
|
||||||
"powerfmt",
|
"powerfmt",
|
||||||
"serde",
|
"serde",
|
||||||
"time-core",
|
"time-core",
|
||||||
@@ -4153,10 +4240,11 @@ checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "time-macros"
|
name = "time-macros"
|
||||||
version = "0.2.16"
|
version = "0.2.18"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "26197e33420244aeb70c3e8c78376ca46571bc4e701e4791c2cd9f57dcb3a43f"
|
checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"num-conv",
|
||||||
"time-core",
|
"time-core",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -4713,6 +4801,24 @@ dependencies = [
|
|||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "webbrowser"
|
||||||
|
version = "1.0.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2e5f07fb9bc8de2ddfe6b24a71a75430673fd679e568c48b52716cef1cfae923"
|
||||||
|
dependencies = [
|
||||||
|
"block2",
|
||||||
|
"core-foundation 0.10.0",
|
||||||
|
"home",
|
||||||
|
"jni 0.21.1",
|
||||||
|
"log",
|
||||||
|
"ndk-context",
|
||||||
|
"objc2",
|
||||||
|
"objc2-foundation",
|
||||||
|
"url",
|
||||||
|
"web-sys",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "webkit2gtk"
|
name = "webkit2gtk"
|
||||||
version = "0.18.2"
|
version = "0.18.2"
|
||||||
@@ -4798,6 +4904,18 @@ dependencies = [
|
|||||||
"windows-metadata",
|
"windows-metadata",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "which"
|
||||||
|
version = "6.0.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3d9c5ed668ee1f17edb3b627225343d210006a90bb1e3745ce1f30b1fb115075"
|
||||||
|
dependencies = [
|
||||||
|
"either",
|
||||||
|
"home",
|
||||||
|
"rustix",
|
||||||
|
"winsafe",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "whoami"
|
name = "whoami"
|
||||||
version = "1.5.1"
|
version = "1.5.1"
|
||||||
@@ -4898,6 +5016,15 @@ version = "0.39.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9ee5e275231f07c6e240d14f34e1b635bf1faa1c76c57cfd59a5cdb9848e4278"
|
checksum = "9ee5e275231f07c6e240d14f34e1b635bf1faa1c76c57cfd59a5cdb9848e4278"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-sys"
|
||||||
|
version = "0.45.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
|
||||||
|
dependencies = [
|
||||||
|
"windows-targets 0.42.2",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-sys"
|
name = "windows-sys"
|
||||||
version = "0.48.0"
|
version = "0.48.0"
|
||||||
@@ -4916,6 +5043,21 @@ dependencies = [
|
|||||||
"windows-targets 0.52.0",
|
"windows-targets 0.52.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-targets"
|
||||||
|
version = "0.42.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071"
|
||||||
|
dependencies = [
|
||||||
|
"windows_aarch64_gnullvm 0.42.2",
|
||||||
|
"windows_aarch64_msvc 0.42.2",
|
||||||
|
"windows_i686_gnu 0.42.2",
|
||||||
|
"windows_i686_msvc 0.42.2",
|
||||||
|
"windows_x86_64_gnu 0.42.2",
|
||||||
|
"windows_x86_64_gnullvm 0.42.2",
|
||||||
|
"windows_x86_64_msvc 0.42.2",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-targets"
|
name = "windows-targets"
|
||||||
version = "0.48.5"
|
version = "0.48.5"
|
||||||
@@ -4961,6 +5103,12 @@ dependencies = [
|
|||||||
"windows-targets 0.52.0",
|
"windows-targets 0.52.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_gnullvm"
|
||||||
|
version = "0.42.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_aarch64_gnullvm"
|
name = "windows_aarch64_gnullvm"
|
||||||
version = "0.48.5"
|
version = "0.48.5"
|
||||||
@@ -4979,6 +5127,12 @@ version = "0.39.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ec7711666096bd4096ffa835238905bb33fb87267910e154b18b44eaabb340f2"
|
checksum = "ec7711666096bd4096ffa835238905bb33fb87267910e154b18b44eaabb340f2"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_msvc"
|
||||||
|
version = "0.42.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_aarch64_msvc"
|
name = "windows_aarch64_msvc"
|
||||||
version = "0.48.5"
|
version = "0.48.5"
|
||||||
@@ -4997,6 +5151,12 @@ version = "0.39.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "763fc57100a5f7042e3057e7e8d9bdd7860d330070251a73d003563a3bb49e1b"
|
checksum = "763fc57100a5f7042e3057e7e8d9bdd7860d330070251a73d003563a3bb49e1b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_gnu"
|
||||||
|
version = "0.42.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_i686_gnu"
|
name = "windows_i686_gnu"
|
||||||
version = "0.48.5"
|
version = "0.48.5"
|
||||||
@@ -5015,6 +5175,12 @@ version = "0.39.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7bc7cbfe58828921e10a9f446fcaaf649204dcfe6c1ddd712c5eebae6bda1106"
|
checksum = "7bc7cbfe58828921e10a9f446fcaaf649204dcfe6c1ddd712c5eebae6bda1106"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_msvc"
|
||||||
|
version = "0.42.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_i686_msvc"
|
name = "windows_i686_msvc"
|
||||||
version = "0.48.5"
|
version = "0.48.5"
|
||||||
@@ -5033,6 +5199,12 @@ version = "0.39.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6868c165637d653ae1e8dc4d82c25d4f97dd6605eaa8d784b5c6e0ab2a252b65"
|
checksum = "6868c165637d653ae1e8dc4d82c25d4f97dd6605eaa8d784b5c6e0ab2a252b65"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnu"
|
||||||
|
version = "0.42.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_x86_64_gnu"
|
name = "windows_x86_64_gnu"
|
||||||
version = "0.48.5"
|
version = "0.48.5"
|
||||||
@@ -5045,6 +5217,12 @@ version = "0.52.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd"
|
checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnullvm"
|
||||||
|
version = "0.42.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_x86_64_gnullvm"
|
name = "windows_x86_64_gnullvm"
|
||||||
version = "0.48.5"
|
version = "0.48.5"
|
||||||
@@ -5063,6 +5241,12 @@ version = "0.39.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5e4d40883ae9cae962787ca76ba76390ffa29214667a111db9e0a1ad8377e809"
|
checksum = "5e4d40883ae9cae962787ca76ba76390ffa29214667a111db9e0a1ad8377e809"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_msvc"
|
||||||
|
version = "0.42.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_x86_64_msvc"
|
name = "windows_x86_64_msvc"
|
||||||
version = "0.48.5"
|
version = "0.48.5"
|
||||||
@@ -5104,6 +5288,12 @@ dependencies = [
|
|||||||
"windows-sys 0.48.0",
|
"windows-sys 0.48.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winsafe"
|
||||||
|
version = "0.0.19"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d135d17ab770252ad95e9a872d365cf3090e3be864a34ab46f48555993efc904"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wry"
|
name = "wry"
|
||||||
version = "0.24.7"
|
version = "0.24.7"
|
||||||
|
@@ -5,7 +5,7 @@ members = ["crates/*", "apps/gpclient", "apps/gpservice", "apps/gpauth", "apps/g
|
|||||||
|
|
||||||
[workspace.package]
|
[workspace.package]
|
||||||
rust-version = "1.70"
|
rust-version = "1.70"
|
||||||
version = "2.3.4"
|
version = "2.3.8"
|
||||||
authors = ["Kevin Yue <k3vinyue@gmail.com>"]
|
authors = ["Kevin Yue <k3vinyue@gmail.com>"]
|
||||||
homepage = "https://github.com/yuezk/GlobalProtect-openconnect"
|
homepage = "https://github.com/yuezk/GlobalProtect-openconnect"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
@@ -46,6 +46,7 @@ compile-time = "0.2"
|
|||||||
serde_urlencoded = "0.7"
|
serde_urlencoded = "0.7"
|
||||||
md5="0.7"
|
md5="0.7"
|
||||||
sha256="1"
|
sha256="1"
|
||||||
|
which="6"
|
||||||
|
|
||||||
# Tauri dependencies
|
# Tauri dependencies
|
||||||
tauri = { version = "1.5" }
|
tauri = { version = "1.5" }
|
||||||
|
12
README.md
12
README.md
@@ -44,12 +44,20 @@ Options:
|
|||||||
See 'gpclient help <command>' for more information on a specific command.
|
See 'gpclient help <command>' for more information on a specific command.
|
||||||
```
|
```
|
||||||
|
|
||||||
To use the default browser for authentication with the CLI version, you need to use the following command:
|
To use the external browser for authentication with the CLI version, you need to use the following command:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo -E gpclient connect --default-browser <portal>
|
sudo -E gpclient connect --browser default <portal>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Or you can try the following command if the above command does not work:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
gpauth <portal> --browser default 2>/dev/null | sudo gpclient connect <portal> --cookie-on-stdin
|
||||||
|
```
|
||||||
|
|
||||||
|
You can specify the browser with the `--browser <browser>` option, e.g., `--browser firefox`, `--browser chrome`, etc.
|
||||||
|
|
||||||
### GUI
|
### GUI
|
||||||
|
|
||||||
The GUI version is also available after you installed it. You can launch it from the application menu or run `gpclient launch-gui` in the terminal.
|
The GUI version is also available after you installed it. You can launch it from the application menu or run `gpclient launch-gui` in the terminal.
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "gpauth"
|
name = "gpauth"
|
||||||
|
authors.workspace = true
|
||||||
version.workspace = true
|
version.workspace = true
|
||||||
edition.workspace = true
|
edition.workspace = true
|
||||||
license.workspace = true
|
license.workspace = true
|
||||||
|
@@ -1,3 +1,5 @@
|
|||||||
|
use std::{env::temp_dir, fs, os::unix::fs::PermissionsExt};
|
||||||
|
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use gpapi::{
|
use gpapi::{
|
||||||
auth::{SamlAuthData, SamlAuthResult},
|
auth::{SamlAuthData, SamlAuthResult},
|
||||||
@@ -11,35 +13,69 @@ use log::{info, LevelFilter};
|
|||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
use tauri::{App, AppHandle, RunEvent};
|
use tauri::{App, AppHandle, RunEvent};
|
||||||
use tempfile::NamedTempFile;
|
use tempfile::NamedTempFile;
|
||||||
|
use tokio::{io::AsyncReadExt, net::TcpListener};
|
||||||
|
|
||||||
use crate::auth_window::{portal_prelogin, AuthWindow};
|
use crate::auth_window::{portal_prelogin, AuthWindow};
|
||||||
|
|
||||||
const VERSION: &str = concat!(env!("CARGO_PKG_VERSION"), " (", compile_time::date_str!(), ")");
|
const VERSION: &str = concat!(env!("CARGO_PKG_VERSION"), " (", compile_time::date_str!(), ")");
|
||||||
|
|
||||||
#[derive(Parser, Clone)]
|
#[derive(Parser, Clone)]
|
||||||
#[command(version = VERSION)]
|
#[command(
|
||||||
|
version = VERSION,
|
||||||
|
author,
|
||||||
|
about = "The authentication component for the GlobalProtect VPN client, supports the SSO authentication method.",
|
||||||
|
help_template = "\
|
||||||
|
{before-help}{name} {version}
|
||||||
|
{author}
|
||||||
|
|
||||||
|
{about}
|
||||||
|
|
||||||
|
{usage-heading} {usage}
|
||||||
|
|
||||||
|
{all-args}{after-help}
|
||||||
|
|
||||||
|
See 'gpauth -h' for more information.
|
||||||
|
"
|
||||||
|
)]
|
||||||
struct Cli {
|
struct Cli {
|
||||||
|
#[arg(help = "The portal server to authenticate")]
|
||||||
server: String,
|
server: String,
|
||||||
#[arg(long)]
|
|
||||||
|
#[arg(long, help = "Treating the server as a gateway")]
|
||||||
gateway: bool,
|
gateway: bool,
|
||||||
#[arg(long)]
|
|
||||||
|
#[arg(long, help = "The SAML authentication request")]
|
||||||
saml_request: Option<String>,
|
saml_request: Option<String>,
|
||||||
#[arg(long, default_value = GP_USER_AGENT)]
|
|
||||||
|
#[arg(long, default_value = GP_USER_AGENT, help = "The user agent to use")]
|
||||||
user_agent: String,
|
user_agent: String,
|
||||||
|
|
||||||
#[arg(long, default_value = "Linux")]
|
#[arg(long, default_value = "Linux")]
|
||||||
os: Os,
|
os: Os,
|
||||||
|
|
||||||
#[arg(long)]
|
#[arg(long)]
|
||||||
os_version: Option<String>,
|
os_version: Option<String>,
|
||||||
#[arg(long)]
|
|
||||||
|
#[arg(long, help = "The HiDPI mode, useful for high-resolution screens")]
|
||||||
hidpi: bool,
|
hidpi: bool,
|
||||||
#[arg(long)]
|
|
||||||
|
#[arg(long, help = "Get around the OpenSSL `unsafe legacy renegotiation` error")]
|
||||||
fix_openssl: bool,
|
fix_openssl: bool,
|
||||||
#[arg(long)]
|
|
||||||
|
#[arg(long, help = "Ignore TLS errors")]
|
||||||
ignore_tls_errors: bool,
|
ignore_tls_errors: bool,
|
||||||
#[arg(long)]
|
|
||||||
|
#[arg(long, help = "Clean the cache of the embedded browser")]
|
||||||
clean: bool,
|
clean: bool,
|
||||||
#[arg(long)]
|
|
||||||
|
#[arg(long, help = "Use the default browser for authentication")]
|
||||||
default_browser: bool,
|
default_browser: bool,
|
||||||
|
|
||||||
|
#[arg(
|
||||||
|
long,
|
||||||
|
help = "The browser to use for authentication, e.g., `default`, `firefox`, `chrome`, `chromium`, or the path to the browser executable"
|
||||||
|
)]
|
||||||
|
browser: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Cli {
|
impl Cli {
|
||||||
@@ -59,12 +95,28 @@ impl Cli {
|
|||||||
None => portal_prelogin(&self.server, &gp_params).await?,
|
None => portal_prelogin(&self.server, &gp_params).await?,
|
||||||
};
|
};
|
||||||
|
|
||||||
if self.default_browser {
|
let browser_auth = if let Some(browser) = &self.browser {
|
||||||
let browser_auth = BrowserAuthenticator::new(&saml_request);
|
Some(BrowserAuthenticator::new_with_browser(&saml_request, browser))
|
||||||
|
} else if self.default_browser {
|
||||||
|
Some(BrowserAuthenticator::new(&saml_request))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(browser_auth) = browser_auth {
|
||||||
browser_auth.authenticate()?;
|
browser_auth.authenticate()?;
|
||||||
|
|
||||||
info!("Please continue the authentication process in the default browser");
|
info!("Please continue the authentication process in the default browser");
|
||||||
|
|
||||||
|
let auth_result = match wait_auth_data().await {
|
||||||
|
Ok(auth_data) => SamlAuthResult::Success(auth_data),
|
||||||
|
Err(err) => SamlAuthResult::Failure(format!("{}", err)),
|
||||||
|
};
|
||||||
|
|
||||||
|
info!("Authentication completed");
|
||||||
|
|
||||||
|
println!("{}", json!(auth_result));
|
||||||
|
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -172,3 +224,35 @@ pub async fn run() {
|
|||||||
std::process::exit(1);
|
std::process::exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn wait_auth_data() -> anyhow::Result<SamlAuthData> {
|
||||||
|
// Start a local server to receive the browser authentication data
|
||||||
|
let listener = TcpListener::bind("127.0.0.1:0").await?;
|
||||||
|
let port = listener.local_addr()?.port();
|
||||||
|
let port_file = temp_dir().join("gpcallback.port");
|
||||||
|
|
||||||
|
// Write the port to a file
|
||||||
|
fs::write(&port_file, port.to_string())?;
|
||||||
|
fs::set_permissions(&port_file, fs::Permissions::from_mode(0o600))?;
|
||||||
|
|
||||||
|
// Remove the previous log file
|
||||||
|
let callback_log = temp_dir().join("gpcallback.log");
|
||||||
|
let _ = fs::remove_file(&callback_log);
|
||||||
|
|
||||||
|
info!("Listening authentication data on port {}", port);
|
||||||
|
info!(
|
||||||
|
"If it hangs, please check the logs at `{}` for more information",
|
||||||
|
callback_log.display()
|
||||||
|
);
|
||||||
|
let (mut socket, _) = listener.accept().await?;
|
||||||
|
|
||||||
|
info!("Received the browser authentication data from the socket");
|
||||||
|
let mut data = String::new();
|
||||||
|
socket.read_to_string(&mut data).await?;
|
||||||
|
|
||||||
|
// Remove the port file
|
||||||
|
fs::remove_file(&port_file)?;
|
||||||
|
|
||||||
|
let auth_data = SamlAuthData::from_gpcallback(&data)?;
|
||||||
|
Ok(auth_data)
|
||||||
|
}
|
||||||
|
@@ -1,3 +1,5 @@
|
|||||||
|
use std::{env::temp_dir, fs::File};
|
||||||
|
|
||||||
use clap::{Parser, Subcommand};
|
use clap::{Parser, Subcommand};
|
||||||
use gpapi::utils::openssl;
|
use gpapi::utils::openssl;
|
||||||
use log::{info, LevelFilter};
|
use log::{info, LevelFilter};
|
||||||
@@ -85,14 +87,29 @@ impl Cli {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init_logger() {
|
fn init_logger(command: &CliCommand) {
|
||||||
env_logger::builder().filter_level(LevelFilter::Info).init();
|
let mut builder = env_logger::builder();
|
||||||
|
builder.filter_level(LevelFilter::Info);
|
||||||
|
|
||||||
|
// Output the log messages to a file if the command is the auth callback
|
||||||
|
if let CliCommand::LaunchGui(args) = command {
|
||||||
|
let auth_data = args.auth_data.as_deref().unwrap_or_default();
|
||||||
|
if !auth_data.is_empty() {
|
||||||
|
if let Ok(log_file) = File::create(temp_dir().join("gpcallback.log")) {
|
||||||
|
let target = Box::new(log_file);
|
||||||
|
builder.target(env_logger::Target::Pipe(target));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.init();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn run() {
|
pub(crate) async fn run() {
|
||||||
let cli = Cli::parse();
|
let cli = Cli::parse();
|
||||||
|
|
||||||
init_logger();
|
init_logger(&cli.command);
|
||||||
|
|
||||||
info!("gpclient started: {}", VERSION);
|
info!("gpclient started: {}", VERSION);
|
||||||
|
|
||||||
if let Err(err) = cli.run().await {
|
if let Err(err) = cli.run().await {
|
||||||
|
@@ -1,8 +1,10 @@
|
|||||||
use std::{cell::RefCell, fs, sync::Arc};
|
use std::{cell::RefCell, fs, sync::Arc};
|
||||||
|
|
||||||
|
use anyhow::bail;
|
||||||
use clap::Args;
|
use clap::Args;
|
||||||
use common::vpn_utils::find_csd_wrapper;
|
use common::vpn_utils::find_csd_wrapper;
|
||||||
use gpapi::{
|
use gpapi::{
|
||||||
|
auth::SamlAuthResult,
|
||||||
clap::args::Os,
|
clap::args::Os,
|
||||||
credential::{Credential, PasswordCredential},
|
credential::{Credential, PasswordCredential},
|
||||||
error::PortalError,
|
error::PortalError,
|
||||||
@@ -19,9 +21,8 @@ use gpapi::{
|
|||||||
use inquire::{Password, PasswordDisplayMode, Select, Text};
|
use inquire::{Password, PasswordDisplayMode, Select, Text};
|
||||||
use log::info;
|
use log::info;
|
||||||
use openconnect::Vpn;
|
use openconnect::Vpn;
|
||||||
use tokio::{io::AsyncReadExt, net::TcpListener};
|
|
||||||
|
|
||||||
use crate::{cli::SharedArgs, GP_CLIENT_LOCK_FILE, GP_CLIENT_PORT_FILE};
|
use crate::{cli::SharedArgs, GP_CLIENT_LOCK_FILE};
|
||||||
|
|
||||||
#[derive(Args)]
|
#[derive(Args)]
|
||||||
pub(crate) struct ConnectArgs {
|
pub(crate) struct ConnectArgs {
|
||||||
@@ -37,6 +38,9 @@ pub(crate) struct ConnectArgs {
|
|||||||
#[arg(long, help = "Read the password from standard input")]
|
#[arg(long, help = "Read the password from standard input")]
|
||||||
passwd_on_stdin: bool,
|
passwd_on_stdin: bool,
|
||||||
|
|
||||||
|
#[arg(long, help = "Read the cookie from standard input")]
|
||||||
|
cookie_on_stdin: bool,
|
||||||
|
|
||||||
#[arg(long, short, help = "The VPNC script to use")]
|
#[arg(long, short, help = "The VPNC script to use")]
|
||||||
script: Option<String>,
|
script: Option<String>,
|
||||||
|
|
||||||
@@ -86,7 +90,10 @@ pub(crate) struct ConnectArgs {
|
|||||||
#[arg(long)]
|
#[arg(long)]
|
||||||
os_version: Option<String>,
|
os_version: Option<String>,
|
||||||
|
|
||||||
#[arg(long, help = "The HiDPI mode, useful for high resolution screens")]
|
#[arg(long, help = "Disable DTLS and ESP")]
|
||||||
|
no_dtls: bool,
|
||||||
|
|
||||||
|
#[arg(long, help = "The HiDPI mode, useful for high-resolution screens")]
|
||||||
hidpi: bool,
|
hidpi: bool,
|
||||||
|
|
||||||
#[arg(long, help = "Do not reuse the remembered authentication cookie")]
|
#[arg(long, help = "Do not reuse the remembered authentication cookie")]
|
||||||
@@ -94,6 +101,12 @@ pub(crate) struct ConnectArgs {
|
|||||||
|
|
||||||
#[arg(long, help = "Use the default browser to authenticate")]
|
#[arg(long, help = "Use the default browser to authenticate")]
|
||||||
default_browser: bool,
|
default_browser: bool,
|
||||||
|
|
||||||
|
#[arg(
|
||||||
|
long,
|
||||||
|
help = "Use the specified browser to authenticate, e.g., `default`, `firefox`, `chrome`, `chromium`, or the path to the browser executable"
|
||||||
|
)]
|
||||||
|
browser: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ConnectArgs {
|
impl ConnectArgs {
|
||||||
@@ -138,6 +151,10 @@ impl<'a> ConnectHandler<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn handle(&self) -> anyhow::Result<()> {
|
pub(crate) async fn handle(&self) -> anyhow::Result<()> {
|
||||||
|
if self.args.default_browser && self.args.browser.is_some() {
|
||||||
|
bail!("Cannot use `--default-browser` and `--browser` options at the same time");
|
||||||
|
}
|
||||||
|
|
||||||
self.latest_key_password.replace(self.args.key_password.clone());
|
self.latest_key_password.replace(self.args.key_password.clone());
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
@@ -281,9 +298,11 @@ impl<'a> ConnectHandler<'a> {
|
|||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let os = ClientOs::from(&self.args.os).to_openconnect_os().to_string();
|
||||||
let vpn = Vpn::builder(gateway, cookie)
|
let vpn = Vpn::builder(gateway, cookie)
|
||||||
.script(self.args.script.clone())
|
.script(self.args.script.clone())
|
||||||
.user_agent(self.args.user_agent.clone())
|
.user_agent(self.args.user_agent.clone())
|
||||||
|
.os(Some(os))
|
||||||
.certificate(self.args.certificate.clone())
|
.certificate(self.args.certificate.clone())
|
||||||
.sslkey(self.args.sslkey.clone())
|
.sslkey(self.args.sslkey.clone())
|
||||||
.key_password(self.latest_key_password.borrow().clone())
|
.key_password(self.latest_key_password.borrow().clone())
|
||||||
@@ -292,6 +311,7 @@ impl<'a> ConnectHandler<'a> {
|
|||||||
.reconnect_timeout(self.args.reconnect_timeout)
|
.reconnect_timeout(self.args.reconnect_timeout)
|
||||||
.mtu(mtu)
|
.mtu(mtu)
|
||||||
.disable_ipv6(self.args.disable_ipv6)
|
.disable_ipv6(self.args.disable_ipv6)
|
||||||
|
.no_dtls(self.args.no_dtls)
|
||||||
.build()?;
|
.build()?;
|
||||||
|
|
||||||
let vpn = Arc::new(vpn);
|
let vpn = Arc::new(vpn);
|
||||||
@@ -315,11 +335,20 @@ impl<'a> ConnectHandler<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn obtain_credential(&self, prelogin: &Prelogin, server: &str) -> anyhow::Result<Credential> {
|
async fn obtain_credential(&self, prelogin: &Prelogin, server: &str) -> anyhow::Result<Credential> {
|
||||||
|
if self.args.cookie_on_stdin {
|
||||||
|
return read_cookie_from_stdin();
|
||||||
|
}
|
||||||
|
|
||||||
let is_gateway = prelogin.is_gateway();
|
let is_gateway = prelogin.is_gateway();
|
||||||
|
|
||||||
match prelogin {
|
match prelogin {
|
||||||
Prelogin::Saml(prelogin) => {
|
Prelogin::Saml(prelogin) => {
|
||||||
let use_default_browser = prelogin.support_default_browser() && self.args.default_browser;
|
let use_default_browser = prelogin.support_default_browser() && self.args.default_browser;
|
||||||
|
let browser = if prelogin.support_default_browser() {
|
||||||
|
self.args.browser.as_deref()
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
let cred = SamlAuthLauncher::new(&self.args.server)
|
let cred = SamlAuthLauncher::new(&self.args.server)
|
||||||
.gateway(is_gateway)
|
.gateway(is_gateway)
|
||||||
@@ -332,21 +361,13 @@ impl<'a> ConnectHandler<'a> {
|
|||||||
.ignore_tls_errors(self.shared_args.ignore_tls_errors)
|
.ignore_tls_errors(self.shared_args.ignore_tls_errors)
|
||||||
.clean(self.args.clean)
|
.clean(self.args.clean)
|
||||||
.default_browser(use_default_browser)
|
.default_browser(use_default_browser)
|
||||||
|
.browser(browser)
|
||||||
.launch()
|
.launch()
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
if let Some(cred) = cred {
|
Ok(cred)
|
||||||
return Ok(cred);
|
|
||||||
}
|
|
||||||
|
|
||||||
if !use_default_browser {
|
|
||||||
// This should never happen
|
|
||||||
unreachable!("SAML authentication failed without using the default browser");
|
|
||||||
}
|
|
||||||
|
|
||||||
info!("Waiting for the browser authentication to complete...");
|
|
||||||
wait_credentials().await
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Prelogin::Standard(prelogin) => {
|
Prelogin::Standard(prelogin) => {
|
||||||
let prefix = if is_gateway { "Gateway" } else { "Portal" };
|
let prefix = if is_gateway { "Gateway" } else { "Portal" };
|
||||||
println!("{} ({}: {})", prelogin.auth_message(), prefix, server);
|
println!("{} ({}: {})", prelogin.auth_message(), prefix, server);
|
||||||
@@ -376,25 +397,17 @@ impl<'a> ConnectHandler<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn wait_credentials() -> anyhow::Result<Credential> {
|
fn read_cookie_from_stdin() -> anyhow::Result<Credential> {
|
||||||
// Start a local server to receive the browser authentication data
|
info!("Reading cookie from standard input");
|
||||||
let listener = TcpListener::bind("127.0.0.1:0").await?;
|
|
||||||
let port = listener.local_addr()?.port();
|
|
||||||
|
|
||||||
// Write the port to a file
|
let mut cookie = String::new();
|
||||||
fs::write(GP_CLIENT_PORT_FILE, port.to_string())?;
|
std::io::stdin().read_line(&mut cookie)?;
|
||||||
|
|
||||||
info!("Listening authentication data on port {}", port);
|
let Ok(auth_result) = serde_json::from_str::<SamlAuthResult>(cookie.trim_end()) else {
|
||||||
let (mut socket, _) = listener.accept().await?;
|
bail!("Failed to parse auth data")
|
||||||
|
};
|
||||||
|
|
||||||
info!("Received the browser authentication data from the socket");
|
Credential::try_from(auth_result)
|
||||||
let mut data = String::new();
|
|
||||||
socket.read_to_string(&mut data).await?;
|
|
||||||
|
|
||||||
// Remove the port file
|
|
||||||
fs::remove_file(GP_CLIENT_PORT_FILE)?;
|
|
||||||
|
|
||||||
Credential::from_gpcallback(&data)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_pid_file() {
|
fn write_pid_file() {
|
||||||
|
@@ -9,15 +9,13 @@ use gpapi::{
|
|||||||
use log::info;
|
use log::info;
|
||||||
use tokio::io::AsyncWriteExt;
|
use tokio::io::AsyncWriteExt;
|
||||||
|
|
||||||
use crate::GP_CLIENT_PORT_FILE;
|
|
||||||
|
|
||||||
#[derive(Args)]
|
#[derive(Args)]
|
||||||
pub(crate) struct LaunchGuiArgs {
|
pub(crate) struct LaunchGuiArgs {
|
||||||
#[arg(
|
#[arg(
|
||||||
required = false,
|
required = false,
|
||||||
help = "The authentication data, used for the default browser authentication"
|
help = "The authentication data, used for the default browser authentication"
|
||||||
)]
|
)]
|
||||||
auth_data: Option<String>,
|
pub auth_data: Option<String>,
|
||||||
#[arg(long, help = "Launch the GUI minimized")]
|
#[arg(long, help = "Launch the GUI minimized")]
|
||||||
minimized: bool,
|
minimized: bool,
|
||||||
}
|
}
|
||||||
@@ -40,6 +38,7 @@ impl<'a> LaunchGuiHandler<'a> {
|
|||||||
|
|
||||||
let auth_data = self.args.auth_data.as_deref().unwrap_or_default();
|
let auth_data = self.args.auth_data.as_deref().unwrap_or_default();
|
||||||
if !auth_data.is_empty() {
|
if !auth_data.is_empty() {
|
||||||
|
info!("Received auth callback data");
|
||||||
// Process the authentication data, its format is `globalprotectcallback:<data>`
|
// Process the authentication data, its format is `globalprotectcallback:<data>`
|
||||||
return feed_auth_data(auth_data).await;
|
return feed_auth_data(auth_data).await;
|
||||||
}
|
}
|
||||||
@@ -81,16 +80,26 @@ impl<'a> LaunchGuiHandler<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn feed_auth_data(auth_data: &str) -> anyhow::Result<()> {
|
async fn feed_auth_data(auth_data: &str) -> anyhow::Result<()> {
|
||||||
let _ = tokio::join!(feed_auth_data_gui(auth_data), feed_auth_data_cli(auth_data));
|
let (res_gui, res_cli) = tokio::join!(feed_auth_data_gui(auth_data), feed_auth_data_cli(auth_data));
|
||||||
|
if let Err(err) = res_gui {
|
||||||
|
info!("Failed to feed auth data to the GUI: {}", err);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Err(err) = res_cli {
|
||||||
|
info!("Failed to feed auth data to the CLI: {}", err);
|
||||||
|
}
|
||||||
|
|
||||||
// Cleanup the temporary file
|
// Cleanup the temporary file
|
||||||
let html_file = temp_dir().join("gpauth.html");
|
let html_file = temp_dir().join("gpauth.html");
|
||||||
let _ = std::fs::remove_file(html_file);
|
if let Err(err) = std::fs::remove_file(&html_file) {
|
||||||
|
info!("Failed to remove {}: {}", html_file.display(), err);
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn feed_auth_data_gui(auth_data: &str) -> anyhow::Result<()> {
|
async fn feed_auth_data_gui(auth_data: &str) -> anyhow::Result<()> {
|
||||||
|
info!("Feeding auth data to the GUI");
|
||||||
let service_endpoint = http_endpoint().await?;
|
let service_endpoint = http_endpoint().await?;
|
||||||
|
|
||||||
reqwest::Client::default()
|
reqwest::Client::default()
|
||||||
@@ -104,7 +113,10 @@ async fn feed_auth_data_gui(auth_data: &str) -> anyhow::Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn feed_auth_data_cli(auth_data: &str) -> anyhow::Result<()> {
|
async fn feed_auth_data_cli(auth_data: &str) -> anyhow::Result<()> {
|
||||||
let port = tokio::fs::read_to_string(GP_CLIENT_PORT_FILE).await?;
|
info!("Feeding auth data to the CLI");
|
||||||
|
|
||||||
|
let port_file = temp_dir().join("gpcallback.port");
|
||||||
|
let port = tokio::fs::read_to_string(port_file).await?;
|
||||||
let mut stream = tokio::net::TcpStream::connect(format!("127.0.0.1:{}", port.trim())).await?;
|
let mut stream = tokio::net::TcpStream::connect(format!("127.0.0.1:{}", port.trim())).await?;
|
||||||
|
|
||||||
stream.write_all(auth_data.as_bytes()).await?;
|
stream.write_all(auth_data.as_bytes()).await?;
|
||||||
@@ -124,7 +136,7 @@ async fn try_active_gui() -> anyhow::Result<()> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_log_file() -> anyhow::Result<PathBuf> {
|
fn get_log_file() -> anyhow::Result<PathBuf> {
|
||||||
let dirs = ProjectDirs::from("com.yuezk", "GlobalProtect-openconnect", "gpclient")
|
let dirs = ProjectDirs::from("com.yuezk", "GlobalProtect-openconnect", "gpclient")
|
||||||
.ok_or_else(|| anyhow::anyhow!("Failed to get project dirs"))?;
|
.ok_or_else(|| anyhow::anyhow!("Failed to get project dirs"))?;
|
||||||
|
|
||||||
|
@@ -4,7 +4,6 @@ mod disconnect;
|
|||||||
mod launch_gui;
|
mod launch_gui;
|
||||||
|
|
||||||
pub(crate) const GP_CLIENT_LOCK_FILE: &str = "/var/run/gpclient.lock";
|
pub(crate) const GP_CLIENT_LOCK_FILE: &str = "/var/run/gpclient.lock";
|
||||||
pub(crate) const GP_CLIENT_PORT_FILE: &str = "/var/run/gpclient.port";
|
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
|
@@ -9,28 +9,29 @@
|
|||||||
"tauri": "tauri"
|
"tauri": "tauri"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@emotion/react": "^11.11.1",
|
"@emotion/react": "^11.13.0",
|
||||||
"@emotion/styled": "^11.11.0",
|
"@emotion/styled": "^11.13.0",
|
||||||
"@mui/icons-material": "^5.14.18",
|
"@mui/icons-material": "^5.16.7",
|
||||||
"@mui/material": "^5.14.18",
|
"@mui/material": "^5.16.7",
|
||||||
"@tauri-apps/api": "^1.5.0",
|
"@tauri-apps/api": "^1.6.0",
|
||||||
"react": "^18.2.0",
|
"react": "^18.3.1",
|
||||||
"react-dom": "^18.2.0"
|
"react-dom": "^18.3.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@tauri-apps/cli": "^1.5.6",
|
"@tauri-apps/cli": "^1.6.0",
|
||||||
"@types/node": "^20.8.10",
|
"@types/node": "^20.14.15",
|
||||||
"@types/react": "^18.2.15",
|
"@types/react": "^18.3.3",
|
||||||
"@types/react-dom": "^18.2.7",
|
"@types/react-dom": "^18.3.0",
|
||||||
"@typescript-eslint/eslint-plugin": "^6.12.0",
|
"@typescript-eslint/eslint-plugin": "^6.21.0",
|
||||||
"@typescript-eslint/parser": "^6.12.0",
|
"@typescript-eslint/parser": "^6.21.0",
|
||||||
"@vitejs/plugin-react": "^4.0.3",
|
"@vitejs/plugin-react": "^4.3.1",
|
||||||
"eslint": "^8.54.0",
|
"eslint": "^8.57.0",
|
||||||
"eslint-config-prettier": "^9.0.0",
|
"eslint-config-prettier": "^9.1.0",
|
||||||
"eslint-plugin-react": "^7.33.2",
|
"eslint-plugin-react": "^7.35.0",
|
||||||
"eslint-plugin-react-hooks": "^4.6.0",
|
"eslint-plugin-react-hooks": "^4.6.2",
|
||||||
"prettier": "3.1.0",
|
"prettier": "3.1.0",
|
||||||
"typescript": "^5.0.2",
|
"typescript": "^5.5.4",
|
||||||
"vite": "^4.5.3"
|
"vite": "^4.5.3"
|
||||||
}
|
},
|
||||||
|
"packageManager": "pnpm@8.15.7"
|
||||||
}
|
}
|
||||||
|
2010
apps/gpgui-helper/pnpm-lock.yaml
generated
2010
apps/gpgui-helper/pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -47,6 +47,7 @@ impl VpnTaskContext {
|
|||||||
.reconnect_timeout(args.reconnect_timeout())
|
.reconnect_timeout(args.reconnect_timeout())
|
||||||
.mtu(args.mtu())
|
.mtu(args.mtu())
|
||||||
.disable_ipv6(args.disable_ipv6())
|
.disable_ipv6(args.disable_ipv6())
|
||||||
|
.no_dtls(args.no_dtls())
|
||||||
.build()
|
.build()
|
||||||
{
|
{
|
||||||
Ok(vpn) => vpn,
|
Ok(vpn) => vpn,
|
||||||
|
25
changelog.md
25
changelog.md
@@ -1,5 +1,30 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 2.3.8 - 2024-10-31
|
||||||
|
|
||||||
|
- GUI: support configure the external browser to use for authentication (fix [#423](https://github.com/yuezk/GlobalProtect-openconnect/issues/423))
|
||||||
|
- GUI: add option to remember the credential (fix [#420](https://github.com/yuezk/GlobalProtect-openconnect/issues/420))
|
||||||
|
- GUI: fix the credential not saved issue (fix [#420](https://github.com/yuezk/GlobalProtect-openconnect/issues/420))
|
||||||
|
- CLI: fix the default browser detection issue (fix [#416](https://github.com/yuezk/GlobalProtect-openconnect/issues/416))
|
||||||
|
|
||||||
|
## 2.3.7 - 2024-08-16
|
||||||
|
|
||||||
|
- Fix the Rust type inference regression [issue in 1.80](https://github.com/rust-lang/rust/issues/125319).
|
||||||
|
|
||||||
|
## 2.3.6 - 2024-08-15
|
||||||
|
|
||||||
|
- CLI: enhance the `gpauth` command to support external browser authentication
|
||||||
|
- CLI: add the `--cookie-on-stdin` option to support read the cookie from stdin
|
||||||
|
- CLI: support usage: `gpauth <portal> --browser <browser> 2>/dev/null | sudo gpclient connect <portal> --cookie-on-stdin`
|
||||||
|
- CLI: fix the `--browser <browser>` option not working
|
||||||
|
|
||||||
|
## 2.3.5 - 2024-08-14
|
||||||
|
|
||||||
|
- Support configure `no-dtls` option
|
||||||
|
- GUI: fix the tray icon disk usage issue (#398)
|
||||||
|
- CLI: support specify the browser with `--browser <browser>` option (#405, #407, #397)
|
||||||
|
- CLI: fix the `--os` option not working
|
||||||
|
|
||||||
## 2.3.4 - 2024-07-08
|
## 2.3.4 - 2024-07-08
|
||||||
|
|
||||||
- Support the Internal Host Detection (fix [#377](https://github.com/yuezk/GlobalProtect-openconnect/issues/377))
|
- Support the Internal Host Detection (fix [#377](https://github.com/yuezk/GlobalProtect-openconnect/issues/377))
|
||||||
|
@@ -30,12 +30,14 @@ uzers.workspace = true
|
|||||||
serde_urlencoded.workspace = true
|
serde_urlencoded.workspace = true
|
||||||
md5.workspace = true
|
md5.workspace = true
|
||||||
sha256.workspace = true
|
sha256.workspace = true
|
||||||
|
which.workspace = true
|
||||||
|
|
||||||
tauri = { workspace = true, optional = true }
|
tauri = { workspace = true, optional = true }
|
||||||
clap = { workspace = true, optional = true }
|
clap = { workspace = true, optional = true }
|
||||||
open = { version = "5", optional = true }
|
open = { version = "5", optional = true }
|
||||||
|
webbrowser = { version = "1", optional = true }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
tauri = ["dep:tauri"]
|
tauri = ["dep:tauri"]
|
||||||
clap = ["dep:clap"]
|
clap = ["dep:clap"]
|
||||||
browser-auth = ["dep:open"]
|
browser-auth = ["dep:open", "dep:webbrowser"]
|
||||||
|
@@ -85,7 +85,6 @@ impl SamlAuthData {
|
|||||||
return Ok(auth_data);
|
return Ok(auth_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
info!("Parsing SAML auth data...");
|
|
||||||
let auth_data = decode_to_string(auth_data).map_err(|e| {
|
let auth_data = decode_to_string(auth_data).map_err(|e| {
|
||||||
warn!("Failed to decode SAML auth data: {}", e);
|
warn!("Failed to decode SAML auth data: {}", e);
|
||||||
AuthDataParseError::Invalid
|
AuthDataParseError::Invalid
|
||||||
|
@@ -1,9 +1,10 @@
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use anyhow::bail;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use specta::Type;
|
use specta::Type;
|
||||||
|
|
||||||
use crate::auth::SamlAuthData;
|
use crate::auth::{SamlAuthData, SamlAuthResult};
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Type, Clone)]
|
#[derive(Debug, Serialize, Deserialize, Type, Clone)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
@@ -110,11 +111,11 @@ impl AuthCookieCredential {
|
|||||||
pub struct CachedCredential {
|
pub struct CachedCredential {
|
||||||
username: String,
|
username: String,
|
||||||
password: Option<String>,
|
password: Option<String>,
|
||||||
auth_cookie: AuthCookieCredential,
|
auth_cookie: Option<AuthCookieCredential>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CachedCredential {
|
impl CachedCredential {
|
||||||
pub fn new(username: String, password: Option<String>, auth_cookie: AuthCookieCredential) -> Self {
|
pub fn new(username: String, password: Option<String>, auth_cookie: Option<AuthCookieCredential>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
username,
|
username,
|
||||||
password,
|
password,
|
||||||
@@ -130,12 +131,12 @@ impl CachedCredential {
|
|||||||
self.password.as_deref()
|
self.password.as_deref()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn auth_cookie(&self) -> &AuthCookieCredential {
|
pub fn auth_cookie(&self) -> Option<&AuthCookieCredential> {
|
||||||
&self.auth_cookie
|
self.auth_cookie.as_ref()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_auth_cookie(&mut self, auth_cookie: AuthCookieCredential) {
|
pub fn set_auth_cookie(&mut self, auth_cookie: AuthCookieCredential) {
|
||||||
self.auth_cookie = auth_cookie;
|
self.auth_cookie = Some(auth_cookie);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_username(&mut self, username: String) {
|
pub fn set_username(&mut self, username: String) {
|
||||||
@@ -149,11 +150,7 @@ impl CachedCredential {
|
|||||||
|
|
||||||
impl From<PasswordCredential> for CachedCredential {
|
impl From<PasswordCredential> for CachedCredential {
|
||||||
fn from(value: PasswordCredential) -> Self {
|
fn from(value: PasswordCredential) -> Self {
|
||||||
Self::new(
|
Self::new(value.username().to_owned(), Some(value.password().to_owned()), None)
|
||||||
value.username().to_owned(),
|
|
||||||
Some(value.password().to_owned()),
|
|
||||||
AuthCookieCredential::new("", "", ""),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[derive(Debug, Serialize, Deserialize, Type, Clone)]
|
#[derive(Debug, Serialize, Deserialize, Type, Clone)]
|
||||||
@@ -200,8 +197,8 @@ impl Credential {
|
|||||||
Credential::Cached(cred) => (
|
Credential::Cached(cred) => (
|
||||||
cred.password(),
|
cred.password(),
|
||||||
None,
|
None,
|
||||||
Some(cred.auth_cookie.user_auth_cookie()),
|
cred.auth_cookie.as_ref().map(|c| c.user_auth_cookie()),
|
||||||
Some(cred.auth_cookie.prelogon_user_auth_cookie()),
|
cred.auth_cookie.as_ref().map(|c| c.prelogon_user_auth_cookie()),
|
||||||
None,
|
None,
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
@@ -230,6 +227,17 @@ impl From<SamlAuthData> for Credential {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl TryFrom<SamlAuthResult> for Credential {
|
||||||
|
type Error = anyhow::Error;
|
||||||
|
|
||||||
|
fn try_from(value: SamlAuthResult) -> anyhow::Result<Self> {
|
||||||
|
match value {
|
||||||
|
SamlAuthResult::Success(auth_data) => Ok(Self::from(auth_data)),
|
||||||
|
SamlAuthResult::Failure(err) => bail!(err),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<PasswordCredential> for Credential {
|
impl From<PasswordCredential> for Credential {
|
||||||
fn from(value: PasswordCredential) -> Self {
|
fn from(value: PasswordCredential) -> Self {
|
||||||
Self::Password(value)
|
Self::Password(value)
|
||||||
|
@@ -29,7 +29,7 @@ pub async fn gateway_login(gateway: &str, cred: &Credential, gp_params: &GpParam
|
|||||||
params.extend(extra_params);
|
params.extend(extra_params);
|
||||||
params.insert("server", &gateway);
|
params.insert("server", &gateway);
|
||||||
|
|
||||||
info!("Gateway login, user_agent: {}", gp_params.user_agent());
|
info!("Perform gateway login, user_agent: {}", gp_params.user_agent());
|
||||||
|
|
||||||
let res = client
|
let res = client
|
||||||
.post(&login_url)
|
.post(&login_url)
|
||||||
|
@@ -109,7 +109,7 @@ pub async fn retrieve_config(portal: &str, cred: &Credential, gp_params: &GpPara
|
|||||||
params.insert("server", &server);
|
params.insert("server", &server);
|
||||||
params.insert("host", &server);
|
params.insert("host", &server);
|
||||||
|
|
||||||
info!("Portal config, user_agent: {}", gp_params.user_agent());
|
info!("Retrieve the portal config, user_agent: {}", gp_params.user_agent());
|
||||||
|
|
||||||
let res = client
|
let res = client
|
||||||
.post(&url)
|
.post(&url)
|
||||||
@@ -153,7 +153,9 @@ pub async fn retrieve_config(portal: &str, cred: &Credential, gp_params: &GpPara
|
|||||||
});
|
});
|
||||||
|
|
||||||
let user_auth_cookie = root.descendant_text("portal-userauthcookie").unwrap_or_default();
|
let user_auth_cookie = root.descendant_text("portal-userauthcookie").unwrap_or_default();
|
||||||
let prelogon_user_auth_cookie = root.descendant_text("portal-prelogonuserauthcookie").unwrap_or_default();
|
let prelogon_user_auth_cookie = root
|
||||||
|
.descendant_text("portal-prelogonuserauthcookie")
|
||||||
|
.unwrap_or_default();
|
||||||
let config_digest = root.descendant_text("config-digest");
|
let config_digest = root.descendant_text("config-digest");
|
||||||
|
|
||||||
if gateways.is_empty() {
|
if gateways.is_empty() {
|
||||||
|
@@ -116,6 +116,8 @@ pub async fn prelogin(portal: &str, gp_params: &GpParams) -> anyhow::Result<Prel
|
|||||||
|
|
||||||
let client = Client::try_from(gp_params)?;
|
let client = Client::try_from(gp_params)?;
|
||||||
|
|
||||||
|
info!("Perform prelogin, user_agent: {}", gp_params.user_agent());
|
||||||
|
|
||||||
let res = client
|
let res = client
|
||||||
.post(&prelogin_url)
|
.post(&prelogin_url)
|
||||||
.form(¶ms)
|
.form(¶ms)
|
||||||
|
@@ -19,6 +19,7 @@ pub struct SamlAuthLauncher<'a> {
|
|||||||
ignore_tls_errors: bool,
|
ignore_tls_errors: bool,
|
||||||
clean: bool,
|
clean: bool,
|
||||||
default_browser: bool,
|
default_browser: bool,
|
||||||
|
browser: Option<&'a str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> SamlAuthLauncher<'a> {
|
impl<'a> SamlAuthLauncher<'a> {
|
||||||
@@ -35,6 +36,7 @@ impl<'a> SamlAuthLauncher<'a> {
|
|||||||
ignore_tls_errors: false,
|
ignore_tls_errors: false,
|
||||||
clean: false,
|
clean: false,
|
||||||
default_browser: false,
|
default_browser: false,
|
||||||
|
browser: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -88,8 +90,13 @@ impl<'a> SamlAuthLauncher<'a> {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn browser(mut self, browser: Option<&'a str>) -> Self {
|
||||||
|
self.browser = browser;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
/// Launch the authenticator binary as the current user or SUDO_USER if available.
|
/// Launch the authenticator binary as the current user or SUDO_USER if available.
|
||||||
pub async fn launch(self) -> anyhow::Result<Option<Credential>> {
|
pub async fn launch(self) -> anyhow::Result<Credential> {
|
||||||
let mut auth_cmd = Command::new(GP_AUTH_BINARY);
|
let mut auth_cmd = Command::new(GP_AUTH_BINARY);
|
||||||
auth_cmd.arg(self.server);
|
auth_cmd.arg(self.server);
|
||||||
|
|
||||||
@@ -133,6 +140,10 @@ impl<'a> SamlAuthLauncher<'a> {
|
|||||||
auth_cmd.arg("--default-browser");
|
auth_cmd.arg("--default-browser");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(browser) = self.browser {
|
||||||
|
auth_cmd.arg("--browser").arg(browser);
|
||||||
|
}
|
||||||
|
|
||||||
let mut non_root_cmd = auth_cmd.into_non_root()?;
|
let mut non_root_cmd = auth_cmd.into_non_root()?;
|
||||||
let output = non_root_cmd
|
let output = non_root_cmd
|
||||||
.kill_on_drop(true)
|
.kill_on_drop(true)
|
||||||
@@ -141,17 +152,10 @@ impl<'a> SamlAuthLauncher<'a> {
|
|||||||
.wait_with_output()
|
.wait_with_output()
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
if self.default_browser {
|
|
||||||
return Ok(None);
|
|
||||||
}
|
|
||||||
|
|
||||||
let Ok(auth_result) = serde_json::from_slice::<SamlAuthResult>(&output.stdout) else {
|
let Ok(auth_result) = serde_json::from_slice::<SamlAuthResult>(&output.stdout) else {
|
||||||
bail!("Failed to parse auth data")
|
bail!("Failed to parse auth data")
|
||||||
};
|
};
|
||||||
|
|
||||||
match auth_result {
|
Credential::try_from(auth_result)
|
||||||
SamlAuthResult::Success(auth_data) => Ok(Some(Credential::from(auth_data))),
|
|
||||||
SamlAuthResult::Failure(msg) => bail!(msg),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,20 +1,36 @@
|
|||||||
use std::{env::temp_dir, fs, io::Write, os::unix::fs::PermissionsExt};
|
use std::{borrow::Cow, env::temp_dir, fs, io::Write, os::unix::fs::PermissionsExt};
|
||||||
|
|
||||||
use anyhow::bail;
|
use anyhow::bail;
|
||||||
use log::warn;
|
use log::{info, warn};
|
||||||
|
|
||||||
pub struct BrowserAuthenticator<'a> {
|
pub struct BrowserAuthenticator<'a> {
|
||||||
auth_request: &'a str,
|
auth_request: &'a str,
|
||||||
|
browser: Option<&'a str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BrowserAuthenticator<'_> {
|
impl BrowserAuthenticator<'_> {
|
||||||
pub fn new(auth_request: &str) -> BrowserAuthenticator {
|
pub fn new(auth_request: &str) -> BrowserAuthenticator {
|
||||||
BrowserAuthenticator { auth_request }
|
BrowserAuthenticator {
|
||||||
|
auth_request,
|
||||||
|
browser: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_with_browser<'a>(auth_request: &'a str, browser: &'a str) -> BrowserAuthenticator<'a> {
|
||||||
|
let browser = browser.trim();
|
||||||
|
BrowserAuthenticator {
|
||||||
|
auth_request,
|
||||||
|
browser: if browser.is_empty() || browser == "default" {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(browser)
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn authenticate(&self) -> anyhow::Result<()> {
|
pub fn authenticate(&self) -> anyhow::Result<()> {
|
||||||
if self.auth_request.starts_with("http") {
|
let path = if self.auth_request.starts_with("http") {
|
||||||
open::that_detached(self.auth_request)?;
|
Cow::Borrowed(self.auth_request)
|
||||||
} else {
|
} else {
|
||||||
let html_file = temp_dir().join("gpauth.html");
|
let html_file = temp_dir().join("gpauth.html");
|
||||||
|
|
||||||
@@ -31,9 +47,31 @@ impl BrowserAuthenticator<'_> {
|
|||||||
file.set_permissions(fs::Permissions::from_mode(0o600))?;
|
file.set_permissions(fs::Permissions::from_mode(0o600))?;
|
||||||
file.write_all(self.auth_request.as_bytes())?;
|
file.write_all(self.auth_request.as_bytes())?;
|
||||||
|
|
||||||
open::that_detached(html_file)?;
|
Cow::Owned(html_file.to_string_lossy().to_string())
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(browser) = self.browser {
|
||||||
|
let app = find_browser_path(browser);
|
||||||
|
|
||||||
|
info!("Launching browser: {}", app);
|
||||||
|
open::with_detached(path.as_ref(), app)?;
|
||||||
|
} else {
|
||||||
|
info!("Launching the default browser...");
|
||||||
|
webbrowser::open(path.as_ref())?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn find_browser_path(browser: &str) -> String {
|
||||||
|
if browser == "chrome" {
|
||||||
|
which::which("google-chrome-stable")
|
||||||
|
.or_else(|_| which::which("google-chrome"))
|
||||||
|
.or_else(|_| which::which("chromium"))
|
||||||
|
.map(|path| path.to_string_lossy().to_string())
|
||||||
|
.unwrap_or_else(|_| browser.to_string())
|
||||||
|
} else {
|
||||||
|
browser.into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@@ -41,6 +41,7 @@ pub struct ConnectArgs {
|
|||||||
reconnect_timeout: u32,
|
reconnect_timeout: u32,
|
||||||
mtu: u32,
|
mtu: u32,
|
||||||
disable_ipv6: bool,
|
disable_ipv6: bool,
|
||||||
|
no_dtls: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ConnectArgs {
|
impl ConnectArgs {
|
||||||
@@ -58,6 +59,7 @@ impl ConnectArgs {
|
|||||||
reconnect_timeout: 300,
|
reconnect_timeout: 300,
|
||||||
mtu: 0,
|
mtu: 0,
|
||||||
disable_ipv6: false,
|
disable_ipv6: false,
|
||||||
|
no_dtls: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -108,6 +110,10 @@ impl ConnectArgs {
|
|||||||
pub fn disable_ipv6(&self) -> bool {
|
pub fn disable_ipv6(&self) -> bool {
|
||||||
self.disable_ipv6
|
self.disable_ipv6
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn no_dtls(&self) -> bool {
|
||||||
|
self.no_dtls
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, Type)]
|
#[derive(Debug, Deserialize, Serialize, Type)]
|
||||||
@@ -179,6 +185,11 @@ impl ConnectRequest {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn with_no_dtls(mut self, no_dtls: bool) -> Self {
|
||||||
|
self.args.no_dtls = no_dtls;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
pub fn gateway(&self) -> &Gateway {
|
pub fn gateway(&self) -> &Gateway {
|
||||||
self.info.gateway()
|
self.info.gateway()
|
||||||
}
|
}
|
||||||
|
@@ -24,6 +24,7 @@ pub(crate) struct ConnectOptions {
|
|||||||
pub reconnect_timeout: u32,
|
pub reconnect_timeout: u32,
|
||||||
pub mtu: u32,
|
pub mtu: u32,
|
||||||
pub disable_ipv6: u32,
|
pub disable_ipv6: u32,
|
||||||
|
pub no_dtls: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[link(name = "vpn")]
|
#[link(name = "vpn")]
|
||||||
|
@@ -63,6 +63,7 @@ int vpn_connect(const vpn_options *options, vpn_connected_callback callback)
|
|||||||
INFO("RECONNECT_TIMEOUT: %d", options->reconnect_timeout);
|
INFO("RECONNECT_TIMEOUT: %d", options->reconnect_timeout);
|
||||||
INFO("MTU: %d", options->mtu);
|
INFO("MTU: %d", options->mtu);
|
||||||
INFO("DISABLE_IPV6: %d", options->disable_ipv6);
|
INFO("DISABLE_IPV6: %d", options->disable_ipv6);
|
||||||
|
INFO("NO_DTLS: %d", options->no_dtls);
|
||||||
|
|
||||||
vpninfo = openconnect_vpninfo_new(options->user_agent, validate_peer_cert, NULL, NULL, print_progress, NULL);
|
vpninfo = openconnect_vpninfo_new(options->user_agent, validate_peer_cert, NULL, NULL, print_progress, NULL);
|
||||||
|
|
||||||
@@ -119,7 +120,7 @@ int vpn_connect(const vpn_options *options, vpn_connected_callback callback)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (openconnect_setup_dtls(vpninfo, 60) != 0) {
|
if (options->no_dtls || openconnect_setup_dtls(vpninfo, 60) != 0) {
|
||||||
openconnect_disable_dtls(vpninfo);
|
openconnect_disable_dtls(vpninfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -25,6 +25,7 @@ typedef struct vpn_options
|
|||||||
const int reconnect_timeout;
|
const int reconnect_timeout;
|
||||||
const int mtu;
|
const int mtu;
|
||||||
const int disable_ipv6;
|
const int disable_ipv6;
|
||||||
|
const int no_dtls;
|
||||||
} vpn_options;
|
} vpn_options;
|
||||||
|
|
||||||
int vpn_connect(const vpn_options *options, vpn_connected_callback callback);
|
int vpn_connect(const vpn_options *options, vpn_connected_callback callback);
|
||||||
|
@@ -28,6 +28,7 @@ pub struct Vpn {
|
|||||||
reconnect_timeout: u32,
|
reconnect_timeout: u32,
|
||||||
mtu: u32,
|
mtu: u32,
|
||||||
disable_ipv6: bool,
|
disable_ipv6: bool,
|
||||||
|
no_dtls: bool,
|
||||||
|
|
||||||
callback: OnConnectedCallback,
|
callback: OnConnectedCallback,
|
||||||
}
|
}
|
||||||
@@ -77,6 +78,7 @@ impl Vpn {
|
|||||||
reconnect_timeout: self.reconnect_timeout,
|
reconnect_timeout: self.reconnect_timeout,
|
||||||
mtu: self.mtu,
|
mtu: self.mtu,
|
||||||
disable_ipv6: self.disable_ipv6 as u32,
|
disable_ipv6: self.disable_ipv6 as u32,
|
||||||
|
no_dtls: self.no_dtls as u32,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -125,6 +127,7 @@ pub struct VpnBuilder {
|
|||||||
reconnect_timeout: u32,
|
reconnect_timeout: u32,
|
||||||
mtu: u32,
|
mtu: u32,
|
||||||
disable_ipv6: bool,
|
disable_ipv6: bool,
|
||||||
|
no_dtls: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VpnBuilder {
|
impl VpnBuilder {
|
||||||
@@ -147,6 +150,7 @@ impl VpnBuilder {
|
|||||||
reconnect_timeout: 300,
|
reconnect_timeout: 300,
|
||||||
mtu: 0,
|
mtu: 0,
|
||||||
disable_ipv6: false,
|
disable_ipv6: false,
|
||||||
|
no_dtls: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -205,6 +209,11 @@ impl VpnBuilder {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn no_dtls(mut self, no_dtls: bool) -> Self {
|
||||||
|
self.no_dtls = no_dtls;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
pub fn build(self) -> Result<Vpn, VpnError> {
|
pub fn build(self) -> Result<Vpn, VpnError> {
|
||||||
let script = match self.script {
|
let script = match self.script {
|
||||||
Some(script) => {
|
Some(script) => {
|
||||||
@@ -239,6 +248,7 @@ impl VpnBuilder {
|
|||||||
reconnect_timeout: self.reconnect_timeout,
|
reconnect_timeout: self.reconnect_timeout,
|
||||||
mtu: self.mtu,
|
mtu: self.mtu,
|
||||||
disable_ipv6: self.disable_ipv6,
|
disable_ipv6: self.disable_ipv6,
|
||||||
|
no_dtls: self.no_dtls,
|
||||||
|
|
||||||
callback: Default::default(),
|
callback: Default::default(),
|
||||||
})
|
})
|
||||||
|
@@ -42,6 +42,8 @@
|
|||||||
|
|
||||||
overrideMain = {...}: {
|
overrideMain = {...}: {
|
||||||
postPatch = ''
|
postPatch = ''
|
||||||
|
substituteInPlace crates/common/src/vpn_utils.rs \
|
||||||
|
--replace-fail /etc/vpnc/vpnc-script ${pkgs.vpnc-scripts}/bin/vpnc-script
|
||||||
substituteInPlace crates/gpapi/src/lib.rs \
|
substituteInPlace crates/gpapi/src/lib.rs \
|
||||||
--replace-fail /usr/bin/gpclient $out/bin/gpclient \
|
--replace-fail /usr/bin/gpclient $out/bin/gpclient \
|
||||||
--replace-fail /usr/bin/gpservice $out/bin/gpservice \
|
--replace-fail /usr/bin/gpservice $out/bin/gpservice \
|
||||||
|
Reference in New Issue
Block a user