package main import ( "bufio" "encoding/xml" "fmt" "net/http" "os" "strings" "golang.org/x/crypto/ssh/terminal" ) // The XML response returned by the WatchGuard server type Resp struct { Action string `xml:"action"` LogonStatus int `xml:"logon_status"` LogonId int `xml:"logon_id"` Error string `xml:"errStr"` Challenge string `xml:"chaStr"` } func main() { args := os.Args[1:] if len(args) != 1 { fmt.Fprintln(os.Stderr, "Usage: watchblob ") os.Exit(1) } host := args[0] username, password, err := readCredentials() if err != nil { fmt.Fprintf(os.Stderr, "Could not read credentials: %v\n", err) } fmt.Println("Requesting challenge from %s as user %s\n", host, username) challenge, err := triggerChallengeResponse(&host, &username, &password) if err != nil || challenge.LogonStatus != 4 { fmt.Fprintln(os.Stderr, "Did not receive challenge from server") fmt.Fprintf(os.Stderr, "Response: %v\nError: %v\n", challenge, err) os.Exit(1) } token := getToken(&challenge) err = logon(&host, &challenge, &token) if err != nil { fmt.Fprintf(os.Stderr, "Logon failed: %v\n", err) os.Exit(1) } fmt.Printf("Login succeeded, you may now (quickly) authenticate OpenVPN with %d as your password\n", token) } func readCredentials() (string, string, error) { fmt.Printf("Username: ") reader := bufio.NewReader(os.Stdin) username, err := reader.ReadString('\n') fmt.Printf("Password: ") password, err := terminal.ReadPassword(1) // If an error occured, I don't care about which one it is. return strings.TrimSpace(username), strings.TrimSpace(string(password)), err } func triggerChallengeResponse(host *string, username *string, password *string) (r Resp, err error) { return request(templateUrl(host, templateChallengeTriggerUri(username, password))) } func getToken(challenge *Resp) string { fmt.Println(challenge.Challenge) reader := bufio.NewReader(os.Stdin) token, _ := reader.ReadString('\n') return strings.TrimSpace(token) } func logon(host *string, challenge *Resp, token *string) (err error) { resp, err := request(templateUrl(host, templateResponseUri(challenge.LogonId, token))) if err != nil { return } if resp.LogonStatus != 1 { err = fmt.Errorf("Challenge/response authentication failed: %v", resp) } return } func request(url string) (r Resp, err error) { resp, err := http.Get(url) if err != nil { return } defer resp.Body.Close() decoder := xml.NewDecoder(resp.Body) err = decoder.Decode(&r) return }