10 Commits

Author SHA1 Message Date
lyc8503
7ff2625dd1 Merge pull request #30 from TommyIX/main
Add Basic TOTP Authentication
2023-01-28 17:21:20 +08:00
lyc8503
81319fde97 Update web_login.go 2023-01-28 17:19:38 +08:00
JHong
ff60f05a31 Add Basic TOTP Authentication
老哥你好,我是中山大学这边的,因为我们学校的easyconnect要求二步验证,所以我把手机2fa的功能简单的写了一下,交一个pull request给你w。我这边在中大sslvpn测试是正常的。
另外就是初次绑定的话我没做,需要用sxf自己的客户端绑定好验证器之后,才可以在这里用2fa登录。谢谢,祝好
2023-01-28 15:29:19 +08:00
lyc8503
7a1dc42607 supress error for RSA_ENCRYPT_EXP not found 2023-01-25 20:05:03 +08:00
lyc8503
4a365b628b fix older version sms code 2023-01-25 17:21:06 +08:00
lyc8503
a3121efaf2 Merge pull request #20 from i-Pear/main
[bugfix] use thread-local statistic vars
2023-01-25 16:53:15 +08:00
i.Pear
4097d0ae7a use thread-local statistic vars 2023-01-25 16:06:05 +08:00
lyc8503
1696b74763 Merge pull request #16 from i-Pear/main
fix unexpected EOF: Prevent client from being garbage collected
2023-01-25 13:08:53 +08:00
i.Pear
fd0217e446 Prevent client from being garbage collected 2023-01-25 04:19:40 +08:00
lyc8503
a7fa8c3656 fix sms code bug 2023-01-25 00:45:30 +08:00
4 changed files with 80 additions and 6 deletions

View File

@@ -56,6 +56,19 @@ func (client *EasyConnectClient) AuthSMSCode(code string) ([]byte, error) {
return client.LoginByTwfId(twfId)
}
func (client *EasyConnectClient) AuthTOTP(code string) ([]byte, error) {
if client.twfId == "" {
return nil, errors.New("TOTP Auth not required")
}
twfId, err := TOTPAuth(client.server, client.username, client.password, client.twfId, code)
if err != nil {
return nil, err
}
return client.LoginByTwfId(twfId)
}
func (client *EasyConnectClient) LoginByTwfId(twfId string) ([]byte, error) {
agentToken, err := ECAgentToken(client.server, twfId)
if err != nil {

View File

@@ -168,7 +168,7 @@ func BlockTXStream(server string, token *[48]byte, ipRev *[4]byte, ep *EasyConne
errCh := make(chan error)
ep.OnRecv = func(buf []byte) {
n, err = conn.Write(buf)
var n, err = conn.Write(buf)
if err != nil {
errCh <- err
return

View File

@@ -20,7 +20,8 @@ import (
utls "github.com/refraction-networking/utls"
)
var ERR_NEXT_AUTH_SMS = errors.New("SMS Code required.")
var ERR_NEXT_AUTH_SMS = errors.New("SMS Code required")
var ERR_NEXT_AUTH_TOTP = errors.New("Current user's TOTP bound")
func WebLogin(server string, username string, password string) (string, error) {
server = "https://" + server
@@ -50,7 +51,14 @@ func WebLogin(server string, username string, password string) (string, error) {
rsaKey := string(regexp.MustCompile(`<RSA_ENCRYPT_KEY>(.*)</RSA_ENCRYPT_KEY>`).FindSubmatch(buf[:n])[1])
log.Printf("RSA Key: %s", rsaKey)
rsaExp := string(regexp.MustCompile(`<RSA_ENCRYPT_EXP>(.*)</RSA_ENCRYPT_EXP>`).FindSubmatch(buf[:n])[1])
rsaExpMatch := regexp.MustCompile(`<RSA_ENCRYPT_EXP>(.*)</RSA_ENCRYPT_EXP>`).FindSubmatch(buf[:n])
rsaExp := ""
if rsaExpMatch != nil {
rsaExp = string(rsaExpMatch[1])
} else {
log.Printf("Warning: No RSA_ENCRYPT_EXP, using default.")
rsaExp = "65537"
}
log.Printf("RSA Exp: %s", rsaExp)
csrfMatch := regexp.MustCompile(`<CSRF_RAND_CODE>(.*)</CSRF_RAND_CODE>`).FindSubmatch(buf[:n])
@@ -104,7 +112,7 @@ func WebLogin(server string, username string, password string) (string, error) {
// log.Printf("First stage login response: %s", string(buf[:n]))
// SMS Code Process
if strings.Contains(string(buf[:n]), "<NextService>auth/sms</NextService>") {
if strings.Contains(string(buf[:n]), "<NextService>auth/sms</NextService>") || strings.Contains(string(buf[:n]), "<NextAuth>2</NextAuth>") {
log.Print("SMS code required.")
addr = server + "/por/login_sms.csp?apiversion=1"
@@ -121,7 +129,7 @@ func WebLogin(server string, username string, password string) (string, error) {
n, _ := resp.Body.Read(buf)
defer resp.Body.Close()
if !strings.Contains(string(buf[:n]), "验证码已发送到您的手机") {
if !strings.Contains(string(buf[:n]), "验证码已发送到您的手机") && !strings.Contains(string(buf[:n]), "<USER_PHONE>") {
debug.PrintStack()
return "", errors.New("unexpected sms resp: " + string(buf[:n]))
}
@@ -131,6 +139,12 @@ func WebLogin(server string, username string, password string) (string, error) {
return twfId, ERR_NEXT_AUTH_SMS
}
// TOTP Authnication Process (Edited by JHong)
if strings.Contains(string(buf[:n]), "<NextService>auth/token</NextService>") || strings.Contains(string(buf[:n]), "<NextServiceSubType>totp</NextServiceSubType>") {
log.Print("TOTP Authnication required.")
return twfId, ERR_NEXT_AUTH_TOTP
}
if strings.Contains(string(buf[:n]), "<NextAuth>-1</NextAuth>") || !strings.Contains(string(buf[:n]), "<NextAuth>") {
log.Print("No NextAuth found.")
} else {
@@ -191,6 +205,44 @@ func AuthSms(server string, username string, password string, twfId string, smsC
return twfId, nil
}
// JHong Implementing.......
func TOTPAuth(server string, username string, password string, twfId string, TOTPCode string) (string, error) {
c := &http.Client{
Transport: &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}}
buf := make([]byte, 40960)
addr := "https://" + server + "/por/login_token.csp"
log.Printf("TOTP token Request: " + addr)
form := url.Values{
"svpn_inputtoken": {TOTPCode},
}
req, err := http.NewRequest("POST", addr, strings.NewReader(form.Encode()))
req.Header.Set("Cookie", "TWFID="+twfId)
resp, err := c.Do(req)
if err != nil {
debug.PrintStack()
return "", err
}
n, _ := resp.Body.Read(buf)
defer resp.Body.Close()
if !strings.Contains(string(buf[:n]), "suc") {
debug.PrintStack()
return "", errors.New("TOTP token verification FAILED: " + string(buf[:n]))
}
twfId = string(regexp.MustCompile(`<TwfID>(.*)</TwfID>`).FindSubmatch(buf[:n])[1])
log.Print("TOTP verification SUCCESS")
return twfId, nil
}
func ECAgentToken(server string, twfId string) (string, error) {
dialConn, err := net.Dial("tcp", server)
defer dialConn.Close()

11
main.go
View File

@@ -5,12 +5,13 @@ import (
"flag"
"fmt"
"log"
"runtime"
)
func main() {
// CLI args
host, port, username, password, socksBind, twfId := "", 0, "", "", "", ""
flag.StringVar(&host, "server", "", "EasyConnect server address (e.g. vpn.nju.edu.cn)")
flag.StringVar(&host, "server", "", "EasyConnect server address (e.g. vpn.nju.edu.cn, sslvpn.sysu.edu.cn)")
flag.StringVar(&username, "username", "", "Your username")
flag.StringVar(&password, "password", "", "Your password")
flag.StringVar(&socksBind, "socks-bind", ":1080", "The addr socks5 server listens on (e.g. 0.0.0.0:1080)")
@@ -42,6 +43,12 @@ func main() {
fmt.Scan(&smsCode)
ip, err = client.AuthSMSCode(smsCode)
} else if err == core.ERR_NEXT_AUTH_TOTP {
fmt.Print(">>>Please enter your TOTP Auth code<<<:")
TOTPCode := ""
fmt.Scan(&TOTPCode)
ip, err = client.AuthTOTP(TOTPCode)
}
}
@@ -51,4 +58,6 @@ func main() {
log.Printf("Login success, your IP: %d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3])
client.ServeSocks5(socksBind, debugDump)
runtime.KeepAlive(client)
}