From ada62fde3b8450b8e761ab3172e63fe934838223 Mon Sep 17 00:00:00 2001 From: Travis Keep Date: Wed, 27 Sep 2017 16:31:26 -0700 Subject: [PATCH 1/3] u2f -> twofa --- lib/client/{u2f => twofa}/api.go | 5 ++--- lib/client/{u2f/u2f.go => twofa/twofa.go} | 2 +- lib/client/{u2f/u2f_test.go => twofa/twofa_test.go} | 2 +- 3 files changed, 4 insertions(+), 5 deletions(-) rename lib/client/{u2f => twofa}/api.go (90%) rename lib/client/{u2f/u2f.go => twofa/twofa.go} (99%) rename lib/client/{u2f/u2f_test.go => twofa/twofa_test.go} (99%) diff --git a/lib/client/u2f/api.go b/lib/client/twofa/api.go similarity index 90% rename from lib/client/u2f/api.go rename to lib/client/twofa/api.go index 8ec4643..d850fc2 100644 --- a/lib/client/u2f/api.go +++ b/lib/client/twofa/api.go @@ -1,6 +1,5 @@ -// Package u2f contains routines for doing 2 factor authentication and getting -//a short lived certificate. -package u2f +// Package twofa contains routines for getting short lived certificate. +package twofa import ( "crypto" diff --git a/lib/client/u2f/u2f.go b/lib/client/twofa/twofa.go similarity index 99% rename from lib/client/u2f/u2f.go rename to lib/client/twofa/twofa.go index 03d6e5b..ac9b88e 100644 --- a/lib/client/u2f/u2f.go +++ b/lib/client/twofa/twofa.go @@ -1,4 +1,4 @@ -package u2f +package twofa import ( "bufio" diff --git a/lib/client/u2f/u2f_test.go b/lib/client/twofa/twofa_test.go similarity index 99% rename from lib/client/u2f/u2f_test.go rename to lib/client/twofa/twofa_test.go index 0b6074a..da465a5 100644 --- a/lib/client/u2f/u2f_test.go +++ b/lib/client/twofa/twofa_test.go @@ -1,4 +1,4 @@ -package u2f +package twofa import ( "crypto/tls" From 63bf07f3d10d2d9858fe29a65c09a3aa3c0b7020 Mon Sep 17 00:00:00 2001 From: Travis Keep Date: Wed, 27 Sep 2017 17:03:44 -0700 Subject: [PATCH 2/3] Break out u2f package from 2fa package. --- lib/client/twofa/api.go | 6 - lib/client/twofa/twofa.go | 200 +--------------------------------- lib/client/twofa/u2f/api.go | 22 ++++ lib/client/twofa/u2f/u2f.go | 211 ++++++++++++++++++++++++++++++++++++ 4 files changed, 235 insertions(+), 204 deletions(-) create mode 100644 lib/client/twofa/u2f/api.go create mode 100644 lib/client/twofa/u2f/u2f.go diff --git a/lib/client/twofa/api.go b/lib/client/twofa/api.go index d850fc2..7b58efb 100644 --- a/lib/client/twofa/api.go +++ b/lib/client/twofa/api.go @@ -18,12 +18,6 @@ var ( noVIPAccess = flag.Bool("noVIPAccess", false, "Don't use VIPAccess as second factor") ) -// CheckU2FDevices checks the U2F devices and terminates the application by -// calling Fatal on the passed logger if the U2F devices cannot be read. -func CheckU2FDevices(logger log.Logger) { - checkU2FDevices(logger) -} - // GetCertFromTargetUrls gets a signed cert from the given target URLs. func GetCertFromTargetUrls( signer crypto.Signer, diff --git a/lib/client/twofa/twofa.go b/lib/client/twofa/twofa.go index ac9b88e..da03c27 100644 --- a/lib/client/twofa/twofa.go +++ b/lib/client/twofa/twofa.go @@ -4,22 +4,19 @@ import ( "bufio" "bytes" "crypto" - "crypto/sha256" "crypto/tls" - "encoding/base64" "encoding/json" "encoding/pem" "github.com/Symantec/Dominator/lib/log" // client side (interface with hardware) "github.com/flynn/u2f/u2fhid" - "github.com/flynn/u2f/u2ftoken" // server side: "crypto/x509" "errors" "fmt" + "github.com/Symantec/keymaster/lib/client/twofa/u2f" "github.com/Symantec/keymaster/lib/client/util" "github.com/Symantec/keymaster/lib/webapi/v0/proto" - "github.com/tstranex/u2f" "golang.org/x/crypto/ssh" "io" "io/ioutil" @@ -29,7 +26,6 @@ import ( "os" "strconv" "strings" - "time" ) const clientDataAuthenticationTypeValue = "navigator.id.getAssertion" @@ -96,198 +92,6 @@ func doCertRequest(client *http.Client, authCookies []*http.Cookie, url, filedat } -func checkU2FDevices(logger log.Logger) { - // TODO: move this to initialization code, ans pass the device list to this function? - // or maybe pass the token?... - devices, err := u2fhid.Devices() - if err != nil { - logger.Fatal(err) - } - if len(devices) == 0 { - logger.Fatal("no U2F tokens found") - } - - // TODO: transform this into an iteration over all found devices - for _, d := range devices { - //d := devices[0] - logger.Printf("manufacturer = %q, product = %q, vid = 0x%04x, pid = 0x%04x", d.Manufacturer, d.Product, d.ProductID, d.VendorID) - - dev, err := u2fhid.Open(d) - if err != nil { - logger.Fatal(err) - } - defer dev.Close() - } - -} - -func doU2FAuthenticate( - client *http.Client, - authCookies []*http.Cookie, - baseURL string, - logger log.DebugLogger) error { - logger.Printf("top of doU2fAuthenticate") - url := baseURL + "/u2f/SignRequest" - signRequest, err := http.NewRequest("GET", url, nil) - if err != nil { - logger.Fatal(err) - } - // Add the login cookies - for _, cookie := range authCookies { - signRequest.AddCookie(cookie) - } - logger.Debugf(0, "Authcookies: %+v", authCookies) - - signRequestResp, err := client.Do(signRequest) // Client.Get(targetUrl) - if err != nil { - logger.Printf("Failure to sign request req %s", err) - return err - } - logger.Debugf(0, "Get url request did not failed %+v", signRequestResp) - - // Dont defer the body response Close ... as we need to close it explicitly - // in the body of the function so that we can reuse the connection - if signRequestResp.StatusCode != 200 { - signRequestResp.Body.Close() - logger.Printf("got error from call %s, url='%s'\n", signRequestResp.Status, url) - err = errors.New("failed respose from sign request") - return err - } - - var webSignRequest u2f.WebSignRequest - if err := json.NewDecoder(signRequestResp.Body).Decode(&webSignRequest); err != nil { - //http.Error(w, "invalid response: "+err.Error(), http.StatusBadRequest) - // return - logger.Fatal(err) - } - io.Copy(ioutil.Discard, signRequestResp.Body) - signRequestResp.Body.Close() - - // TODO: move this to initialization code, ans pass the device list to this function? - // or maybe pass the token?... - devices, err := u2fhid.Devices() - if err != nil { - logger.Fatal(err) - return err - } - if len(devices) == 0 { - err = errors.New("no U2F tokens found") - logger.Println(err) - return err - } - - // TODO: transform this into an iteration over all found devices - d := devices[0] - logger.Printf("manufacturer = %q, product = %q, vid = 0x%04x, pid = 0x%04x", d.Manufacturer, d.Product, d.ProductID, d.VendorID) - - dev, err := u2fhid.Open(d) - if err != nil { - logger.Fatal(err) - } - defer dev.Close() - t := u2ftoken.NewToken(dev) - - version, err := t.Version() - if err != nil { - logger.Fatal(err) - } - // TODO: Maybe use Debugf()? - logger.Println("version:", version) - - /////// - tokenAuthenticationClientData := u2f.ClientData{Typ: clientDataAuthenticationTypeValue, Challenge: webSignRequest.Challenge, Origin: webSignRequest.AppID} - tokenAuthenticationBuf := new(bytes.Buffer) - err = json.NewEncoder(tokenAuthenticationBuf).Encode(tokenAuthenticationClientData) - if err != nil { - logger.Fatal(err) - } - reqSignChallenge := sha256.Sum256(tokenAuthenticationBuf.Bytes()) - - challenge := make([]byte, 32) - app := make([]byte, 32) - - challenge = reqSignChallenge[:] - reqSingApp := sha256.Sum256([]byte(webSignRequest.AppID)) - app = reqSingApp[:] - - // We find out what key is associated to the currently inserted device. - keyIsKnown := false - var req u2ftoken.AuthenticateRequest - var keyHandle []byte - for _, registeredKey := range webSignRequest.RegisteredKeys { - decodedHandle, err := base64.RawURLEncoding.DecodeString(registeredKey.KeyHandle) - if err != nil { - logger.Fatal(err) - } - keyHandle = decodedHandle - - req = u2ftoken.AuthenticateRequest{ - Challenge: challenge, - Application: app, - KeyHandle: keyHandle, - } - - //logger.Printf("%+v", req) - if err := t.CheckAuthenticate(req); err == nil { - keyIsKnown = true - break - } - } - if !keyIsKnown { - err = errors.New("key is not known") - return err - } - - // Now we ask the token to sign/authenticate - logger.Println("authenticating, provide user presence") - var rawBytes []byte - for { - res, err := t.Authenticate(req) - if err == u2ftoken.ErrPresenceRequired { - time.Sleep(200 * time.Millisecond) - continue - } else if err != nil { - logger.Fatal(err) - } - rawBytes = res.RawResponse - logger.Printf("counter = %d, signature = %x", res.Counter, res.Signature) - break - } - - // now we do the last request - var signRequestResponse u2f.SignResponse - signRequestResponse.KeyHandle = base64.RawURLEncoding.EncodeToString(keyHandle) - signRequestResponse.SignatureData = base64.RawURLEncoding.EncodeToString(rawBytes) - signRequestResponse.ClientData = base64.RawURLEncoding.EncodeToString(tokenAuthenticationBuf.Bytes()) - - // - webSignRequestBuf := &bytes.Buffer{} - err = json.NewEncoder(webSignRequestBuf).Encode(signRequestResponse) - if err != nil { - logger.Fatal(err) - } - - url = baseURL + "/u2f/SignResponse" - webSignRequest2, err := http.NewRequest("POST", url, webSignRequestBuf) - // Add the login cookies - for _, cookie := range authCookies { - webSignRequest2.AddCookie(cookie) - } - signRequestResp2, err := client.Do(webSignRequest2) // Client.Get(targetUrl) - if err != nil { - logger.Printf("Failure to sign request req %s", err) - return err - } - - defer signRequestResp2.Body.Close() - if signRequestResp2.StatusCode != 200 { - logger.Printf("got error from call %s, url='%s'\n", signRequestResp2.Status, url) - return err - } - io.Copy(ioutil.Discard, signRequestResp2.Body) - return nil -} - func doVIPAuthenticate( client *http.Client, authCookies []*http.Cookie, @@ -441,7 +245,7 @@ func getCertsFromServer( } if len(devices) > 0 { - err = doU2FAuthenticate( + err = u2f.DoU2FAuthenticate( client, loginResp.Cookies(), baseUrl, logger) if err != nil { diff --git a/lib/client/twofa/u2f/api.go b/lib/client/twofa/u2f/api.go new file mode 100644 index 0000000..6b127c7 --- /dev/null +++ b/lib/client/twofa/u2f/api.go @@ -0,0 +1,22 @@ +// Package twofa contains routines for getting short lived certificate. +package u2f + +import ( + "github.com/Symantec/Dominator/lib/log" + "net/http" +) + +// CheckU2FDevices checks the U2F devices and terminates the application by +// calling Fatal on the passed logger if the U2F devices cannot be read. +func CheckU2FDevices(logger log.Logger) { + checkU2FDevices(logger) +} + +// DoU2FAuthenticate does U2F authentication +func DoU2FAuthenticate( + client *http.Client, + authCookies []*http.Cookie, + baseURL string, + logger log.DebugLogger) error { + return doU2FAuthenticate(client, authCookies, baseURL, logger) +} diff --git a/lib/client/twofa/u2f/u2f.go b/lib/client/twofa/u2f/u2f.go new file mode 100644 index 0000000..4d30039 --- /dev/null +++ b/lib/client/twofa/u2f/u2f.go @@ -0,0 +1,211 @@ +package u2f + +import ( + "bytes" + "crypto/sha256" + "encoding/base64" + "encoding/json" + "errors" + "github.com/Symantec/Dominator/lib/log" + "github.com/flynn/u2f/u2fhid" + "github.com/flynn/u2f/u2ftoken" + "github.com/tstranex/u2f" + "io" + "io/ioutil" + "net/http" + "time" +) + +const clientDataAuthenticationTypeValue = "navigator.id.getAssertion" + +func checkU2FDevices(logger log.Logger) { + // TODO: move this to initialization code, ans pass the device list to this function? + // or maybe pass the token?... + devices, err := u2fhid.Devices() + if err != nil { + logger.Fatal(err) + } + if len(devices) == 0 { + logger.Fatal("no U2F tokens found") + } + + // TODO: transform this into an iteration over all found devices + for _, d := range devices { + //d := devices[0] + logger.Printf("manufacturer = %q, product = %q, vid = 0x%04x, pid = 0x%04x", d.Manufacturer, d.Product, d.ProductID, d.VendorID) + + dev, err := u2fhid.Open(d) + if err != nil { + logger.Fatal(err) + } + defer dev.Close() + } + +} + +func doU2FAuthenticate( + client *http.Client, + authCookies []*http.Cookie, + baseURL string, + logger log.DebugLogger) error { + logger.Printf("top of doU2fAuthenticate") + url := baseURL + "/u2f/SignRequest" + signRequest, err := http.NewRequest("GET", url, nil) + if err != nil { + logger.Fatal(err) + } + // Add the login cookies + for _, cookie := range authCookies { + signRequest.AddCookie(cookie) + } + logger.Debugf(0, "Authcookies: %+v", authCookies) + + signRequestResp, err := client.Do(signRequest) // Client.Get(targetUrl) + if err != nil { + logger.Printf("Failure to sign request req %s", err) + return err + } + logger.Debugf(0, "Get url request did not failed %+v", signRequestResp) + + // Dont defer the body response Close ... as we need to close it explicitly + // in the body of the function so that we can reuse the connection + if signRequestResp.StatusCode != 200 { + signRequestResp.Body.Close() + logger.Printf("got error from call %s, url='%s'\n", signRequestResp.Status, url) + err = errors.New("failed respose from sign request") + return err + } + + var webSignRequest u2f.WebSignRequest + if err := json.NewDecoder(signRequestResp.Body).Decode(&webSignRequest); err != nil { + //http.Error(w, "invalid response: "+err.Error(), http.StatusBadRequest) + // return + logger.Fatal(err) + } + io.Copy(ioutil.Discard, signRequestResp.Body) + signRequestResp.Body.Close() + + // TODO: move this to initialization code, ans pass the device list to this function? + // or maybe pass the token?... + devices, err := u2fhid.Devices() + if err != nil { + logger.Fatal(err) + return err + } + if len(devices) == 0 { + err = errors.New("no U2F tokens found") + logger.Println(err) + return err + } + + // TODO: transform this into an iteration over all found devices + d := devices[0] + logger.Printf("manufacturer = %q, product = %q, vid = 0x%04x, pid = 0x%04x", d.Manufacturer, d.Product, d.ProductID, d.VendorID) + + dev, err := u2fhid.Open(d) + if err != nil { + logger.Fatal(err) + } + defer dev.Close() + t := u2ftoken.NewToken(dev) + + version, err := t.Version() + if err != nil { + logger.Fatal(err) + } + // TODO: Maybe use Debugf()? + logger.Println("version:", version) + + /////// + tokenAuthenticationClientData := u2f.ClientData{Typ: clientDataAuthenticationTypeValue, Challenge: webSignRequest.Challenge, Origin: webSignRequest.AppID} + tokenAuthenticationBuf := new(bytes.Buffer) + err = json.NewEncoder(tokenAuthenticationBuf).Encode(tokenAuthenticationClientData) + if err != nil { + logger.Fatal(err) + } + reqSignChallenge := sha256.Sum256(tokenAuthenticationBuf.Bytes()) + + challenge := make([]byte, 32) + app := make([]byte, 32) + + challenge = reqSignChallenge[:] + reqSingApp := sha256.Sum256([]byte(webSignRequest.AppID)) + app = reqSingApp[:] + + // We find out what key is associated to the currently inserted device. + keyIsKnown := false + var req u2ftoken.AuthenticateRequest + var keyHandle []byte + for _, registeredKey := range webSignRequest.RegisteredKeys { + decodedHandle, err := base64.RawURLEncoding.DecodeString(registeredKey.KeyHandle) + if err != nil { + logger.Fatal(err) + } + keyHandle = decodedHandle + + req = u2ftoken.AuthenticateRequest{ + Challenge: challenge, + Application: app, + KeyHandle: keyHandle, + } + + //logger.Printf("%+v", req) + if err := t.CheckAuthenticate(req); err == nil { + keyIsKnown = true + break + } + } + if !keyIsKnown { + err = errors.New("key is not known") + return err + } + + // Now we ask the token to sign/authenticate + logger.Println("authenticating, provide user presence") + var rawBytes []byte + for { + res, err := t.Authenticate(req) + if err == u2ftoken.ErrPresenceRequired { + time.Sleep(200 * time.Millisecond) + continue + } else if err != nil { + logger.Fatal(err) + } + rawBytes = res.RawResponse + logger.Printf("counter = %d, signature = %x", res.Counter, res.Signature) + break + } + + // now we do the last request + var signRequestResponse u2f.SignResponse + signRequestResponse.KeyHandle = base64.RawURLEncoding.EncodeToString(keyHandle) + signRequestResponse.SignatureData = base64.RawURLEncoding.EncodeToString(rawBytes) + signRequestResponse.ClientData = base64.RawURLEncoding.EncodeToString(tokenAuthenticationBuf.Bytes()) + + // + webSignRequestBuf := &bytes.Buffer{} + err = json.NewEncoder(webSignRequestBuf).Encode(signRequestResponse) + if err != nil { + logger.Fatal(err) + } + + url = baseURL + "/u2f/SignResponse" + webSignRequest2, err := http.NewRequest("POST", url, webSignRequestBuf) + // Add the login cookies + for _, cookie := range authCookies { + webSignRequest2.AddCookie(cookie) + } + signRequestResp2, err := client.Do(webSignRequest2) // Client.Get(targetUrl) + if err != nil { + logger.Printf("Failure to sign request req %s", err) + return err + } + + defer signRequestResp2.Body.Close() + if signRequestResp2.StatusCode != 200 { + logger.Printf("got error from call %s, url='%s'\n", signRequestResp2.Status, url) + return err + } + io.Copy(ioutil.Discard, signRequestResp2.Body) + return nil +} From 507166d911f9fe8af555784082ff33b9a9f4599f Mon Sep 17 00:00:00 2001 From: Travis Keep Date: Wed, 27 Sep 2017 17:38:28 -0700 Subject: [PATCH 3/3] Break out vip package from twofa package. --- cmd/keymaster/main.go | 7 ++-- lib/client/twofa/twofa.go | 68 +------------------------------ lib/client/twofa/vip/api.go | 16 ++++++++ lib/client/twofa/vip/vip.go | 79 +++++++++++++++++++++++++++++++++++++ 4 files changed, 101 insertions(+), 69 deletions(-) create mode 100644 lib/client/twofa/vip/api.go create mode 100644 lib/client/twofa/vip/vip.go diff --git a/cmd/keymaster/main.go b/cmd/keymaster/main.go index 0898524..e24dc91 100644 --- a/cmd/keymaster/main.go +++ b/cmd/keymaster/main.go @@ -8,7 +8,8 @@ import ( "github.com/Symantec/Dominator/lib/log/cmdlogger" "github.com/Symantec/keymaster/lib/client/config" - "github.com/Symantec/keymaster/lib/client/u2f" + "github.com/Symantec/keymaster/lib/client/twofa" + "github.com/Symantec/keymaster/lib/client/twofa/u2f" "github.com/Symantec/keymaster/lib/client/util" "io/ioutil" @@ -136,7 +137,7 @@ func main() { logger.Fatal(err) } - sshCert, x509Cert, err := u2f.GetCertFromTargetUrls( + sshCert, x509Cert, err := twofa.GetCertFromTargetUrls( signer, userName, password, @@ -189,7 +190,7 @@ func main() { logger.Printf("Success") if _, ok := os.LookupEnv("SSH_AUTH_SOCK"); ok { // TODO(rgooch): Parse certificate to get actual lifetime. - lifetime := fmt.Sprintf("%ds", uint64((*u2f.Duration).Seconds())) + lifetime := fmt.Sprintf("%ds", uint64((*twofa.Duration).Seconds())) cmd := exec.Command("ssh-add", "-t", lifetime, privateKeyPath) cmd.Run() } diff --git a/lib/client/twofa/twofa.go b/lib/client/twofa/twofa.go index da03c27..d0a1713 100644 --- a/lib/client/twofa/twofa.go +++ b/lib/client/twofa/twofa.go @@ -1,7 +1,6 @@ package twofa import ( - "bufio" "bytes" "crypto" "crypto/tls" @@ -15,6 +14,7 @@ import ( "errors" "fmt" "github.com/Symantec/keymaster/lib/client/twofa/u2f" + "github.com/Symantec/keymaster/lib/client/twofa/vip" "github.com/Symantec/keymaster/lib/client/util" "github.com/Symantec/keymaster/lib/webapi/v0/proto" "golang.org/x/crypto/ssh" @@ -23,7 +23,6 @@ import ( "mime/multipart" "net/http" "net/url" - "os" "strconv" "strings" ) @@ -92,69 +91,6 @@ func doCertRequest(client *http.Client, authCookies []*http.Cookie, url, filedat } -func doVIPAuthenticate( - client *http.Client, - authCookies []*http.Cookie, - baseURL string, - logger log.DebugLogger) error { - logger.Printf("top of doVIPAuthenticate") - - // Read VIP token from client - - reader := bufio.NewReader(os.Stdin) - fmt.Print("Enter VIP/OTP code: ") - otpText, err := reader.ReadString('\n') - otpText = strings.TrimSpace(otpText) - //fmt.Println(codeText) - logger.Debugf(1, "codeText: '%s'", otpText) - - // TODO: add some client side validation that the codeText is actually a six digit - // integer - - VIPLoginURL := baseURL + "/api/v0/vipAuth" - - form := url.Values{} - form.Add("OTP", otpText) - //form.Add("password", string(password[:])) - req, err := http.NewRequest("POST", VIPLoginURL, strings.NewReader(form.Encode())) - - // Add the login cookies - for _, cookie := range authCookies { - req.AddCookie(cookie) - } - logger.Debugf(0, "Authcookies: %+v", authCookies) - - req.Header.Add("Content-Length", strconv.Itoa(len(form.Encode()))) - req.Header.Add("Content-Type", "application/x-www-form-urlencoded") - req.Header.Add("Accept", "application/json") - - loginResp, err := client.Do(req) //client.Get(targetUrl) - if err != nil { - logger.Printf("got error from req") - logger.Println(err) - // TODO: differentiate between 400 and 500 errors - // is OK to fail.. try next - return err - } - defer loginResp.Body.Close() - if loginResp.StatusCode != 200 { - logger.Printf("got error from login call %s", loginResp.Status) - return err - } - - loginJSONResponse := proto.LoginResponse{} - //body := jsonrr.Result().Body - err = json.NewDecoder(loginResp.Body).Decode(&loginJSONResponse) - if err != nil { - return err - } - io.Copy(ioutil.Discard, loginResp.Body) - - logger.Debugf(1, "This the login response=%v\n", loginJSONResponse) - - return nil -} - func getCertsFromServer( signer crypto.Signer, userName string, @@ -256,7 +192,7 @@ func getCertsFromServer( } if allowVIP && !successful2fa { - err = doVIPAuthenticate( + err = vip.DoVIPAuthenticate( client, loginResp.Cookies(), baseUrl, logger) if err != nil { diff --git a/lib/client/twofa/vip/api.go b/lib/client/twofa/vip/api.go new file mode 100644 index 0000000..80ad57d --- /dev/null +++ b/lib/client/twofa/vip/api.go @@ -0,0 +1,16 @@ +// Package vip does two factor authentication with Symantec VIP +package vip + +import ( + "github.com/Symantec/Dominator/lib/log" + "net/http" +) + +// DoVIPAuthenticate performs two factor authentication with Symantec VIP +func DoVIPAuthenticate( + client *http.Client, + authCookies []*http.Cookie, + baseURL string, + logger log.DebugLogger) error { + return doVIPAuthenticate(client, authCookies, baseURL, logger) +} diff --git a/lib/client/twofa/vip/vip.go b/lib/client/twofa/vip/vip.go new file mode 100644 index 0000000..d4d0036 --- /dev/null +++ b/lib/client/twofa/vip/vip.go @@ -0,0 +1,79 @@ +package vip + +import ( + "bufio" + "encoding/json" + "fmt" + "github.com/Symantec/Dominator/lib/log" + "github.com/Symantec/keymaster/lib/webapi/v0/proto" + "io" + "io/ioutil" + "net/http" + "net/url" + "os" + "strconv" + "strings" +) + +func doVIPAuthenticate( + client *http.Client, + authCookies []*http.Cookie, + baseURL string, + logger log.DebugLogger) error { + logger.Printf("top of doVIPAuthenticate") + + // Read VIP token from client + + reader := bufio.NewReader(os.Stdin) + fmt.Print("Enter VIP/OTP code: ") + otpText, err := reader.ReadString('\n') + otpText = strings.TrimSpace(otpText) + //fmt.Println(codeText) + logger.Debugf(1, "codeText: '%s'", otpText) + + // TODO: add some client side validation that the codeText is actually a six digit + // integer + + VIPLoginURL := baseURL + "/api/v0/vipAuth" + + form := url.Values{} + form.Add("OTP", otpText) + //form.Add("password", string(password[:])) + req, err := http.NewRequest("POST", VIPLoginURL, strings.NewReader(form.Encode())) + + // Add the login cookies + for _, cookie := range authCookies { + req.AddCookie(cookie) + } + logger.Debugf(0, "Authcookies: %+v", authCookies) + + req.Header.Add("Content-Length", strconv.Itoa(len(form.Encode()))) + req.Header.Add("Content-Type", "application/x-www-form-urlencoded") + req.Header.Add("Accept", "application/json") + + loginResp, err := client.Do(req) //client.Get(targetUrl) + if err != nil { + logger.Printf("got error from req") + logger.Println(err) + // TODO: differentiate between 400 and 500 errors + // is OK to fail.. try next + return err + } + defer loginResp.Body.Close() + if loginResp.StatusCode != 200 { + logger.Printf("got error from login call %s", loginResp.Status) + return err + } + + loginJSONResponse := proto.LoginResponse{} + //body := jsonrr.Result().Body + err = json.NewDecoder(loginResp.Body).Decode(&loginJSONResponse) + if err != nil { + return err + } + io.Copy(ioutil.Discard, loginResp.Body) + + logger.Debugf(1, "This the login response=%v\n", loginJSONResponse) + + return nil +}