Skip to content
This repository has been archived by the owner on May 18, 2021. It is now read-only.

WIP Add TLS key log to okta and duo libs, use in add #133

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 14 additions & 4 deletions cmd/add.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,18 @@ func add(cmd *cobra.Command, args []string) error {
// to centralize the MFA config logic
var dummyProfiles lib.Profiles
updateMfaConfig(cmd, dummyProfiles, "", &mfaConfig)
var oktaClient *lib.OktaClient
if oktaClient, err = lib.NewOktaClient(creds, "", "", mfaConfig); err != nil {
log.Debugf("Failed to initialize client: %s", err)
return ErrFailedToValidateCredentials
}
keyLogFile := addTLSKeyLog(oktaClient)

if keyLogFile != nil {
defer keyLogFile.Close()
}

if err := creds.Validate(mfaConfig); err != nil {
if err := creds.ValidateWithClient(oktaClient); err != nil {
log.Debugf("Failed to validate credentials: %s", err)
return ErrFailedToValidateCredentials
}
Expand All @@ -98,9 +108,9 @@ func add(cmd *cobra.Command, args []string) error {
}

item := keyring.Item{
Key: "okta-creds",
Data: encoded,
Label: "okta credentials",
Key: "okta-creds",
Data: encoded,
Label: "okta credentials",
KeychainNotTrustApplication: false,
}

Expand Down
25 changes: 25 additions & 0 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package cmd
import (
"errors"
"fmt"
"io"
"os"

"github.com/99designs/keyring"
Expand All @@ -21,6 +22,12 @@ var (
ErrFailedToValidateCredentials = errors.New("Failed to validate credentials")
)

// if non-zero, will log TLS keys to this file
var UseTLSKeyLogFile = ""

// SSL to be consistent with other producers, like Firefox and Chrome
const TLSKeyLogFileEnv = "SSLKEYLOGFILE"
nickatsegment marked this conversation as resolved.
Show resolved Hide resolved

const (
// keep expected behavior pre-u2f with duo push
DefaultMFADuoDevice = "phone1"
Expand Down Expand Up @@ -92,6 +99,24 @@ func prerun(cmd *cobra.Command, args []string) {
}
}

// addTLSKeyLog opens a TLS keylog and add its to oktaClient. Its return value
// should be closed by the caller (if it isn't nil)
func addTLSKeyLog(oktaClient *lib.OktaClient) (w io.WriteCloser) {
if UseTLSKeyLogFile != "" {
file := os.Getenv(TLSKeyLogFileEnv)
if file != "" {
w, err := os.OpenFile(file, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
if err != nil {
log.Debugf("Failed to open TLS key log file %s: %s", file, err)
} else {
log.Infof("SECURITY WARNING: logging TLS keys to %s", file)
oktaClient.TLSKeyLogWriter = w
}
}
}
return w
}

func postrun(cmd *cobra.Command, args []string) {
if analyticsEnabled && analyticsClient != nil {
analyticsClient.Close()
Expand Down
43 changes: 31 additions & 12 deletions lib/duo.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@ package lib
import (
"bufio"
"bytes"
"crypto/tls"
"encoding/json"
"errors"
"fmt"
"io"
"net/http"
"os"
"strings"
Expand All @@ -28,6 +30,9 @@ type DuoClient struct {
Callback string
Device string
StateToken string
// Insecure! If set to non-nil, a TLS key log will be written here
// See https://golang.org/pkg/crypto/tls/#example_Config_keyLogWriter
TLSKeyLogWriter io.Writer
}

type StatusResp struct {
Expand Down Expand Up @@ -66,6 +71,23 @@ func NewDuoClient(host, signature, callback string) *DuoClient {
}
}

func (d *DuoClient) httpClient() *http.Client {
var httpClient http.Client
if d.TLSKeyLogWriter != nil {
log.Info("SECURITY WARNING: logging TLS keys for Duo")
httpClient = http.Client{
Transport: &http.Transport{
TLSClientConfig: &tls.Config{
KeyLogWriter: d.TLSKeyLogWriter,
},
},
}
} else {
httpClient = http.Client{}
}
return &httpClient
}

type FacetResponse struct {
TrustedFacets []struct {
Ids []string `json:"ids"`
Expand All @@ -79,14 +101,12 @@ type FacetResponse struct {
// U2F Signing Request returns some trusted urls that we need to lookup
func (d *DuoClient) getTrustedFacet(appId string) (facetResponse *FacetResponse, err error) {

client := &http.Client{}

req, err := http.NewRequest("GET", appId, nil)
if err != nil {
return
}

res, err := client.Do(req)
res, err := d.httpClient().Do(req)
if err != nil {
return
}
Expand Down Expand Up @@ -248,10 +268,9 @@ func (d *DuoClient) DoAuth(tx string, inputSid string, inputCertsURL string) (si
d.Host, tx,
)

client := &http.Client{
CheckRedirect: func(req *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse
},
client := d.httpClient()
client.CheckRedirect = func(req *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse
}

data := uniformResourceLocator.Values{}
Expand Down Expand Up @@ -308,7 +327,7 @@ func (d *DuoClient) DoU2FPromptFinish(sid string, sessionID string, resp *u2fhos

promptUrl := "https://" + d.Host + "/frame/prompt"

client := &http.Client{}
client := d.httpClient()

var respData = ResponseData{
SessionID: sessionID,
Expand Down Expand Up @@ -373,7 +392,7 @@ func (d *DuoClient) DoPrompt(sid string) (txid string, err error) {

url := "https://" + d.Host + "/frame/prompt"

client := &http.Client{}
client := d.httpClient()

// Pick between device you want to use -- the flow are bit different depending on
// whether you want to use a token or a phone of some sort
Expand Down Expand Up @@ -433,7 +452,7 @@ func (d *DuoClient) DoStatus(txid, sid string) (auth string, status StatusResp,

url := "https://" + d.Host + "/frame/status"

client := &http.Client{}
client := d.httpClient()

statusData := "sid=" + sid + "&txid=" + txid
req, err = http.NewRequest("POST", url, bytes.NewReader([]byte(statusData)))
Expand Down Expand Up @@ -469,7 +488,7 @@ func (d *DuoClient) DoStatus(txid, sid string) (auth string, status StatusResp,
}

func (d *DuoClient) DoRedirect(url string, sid string) (string, error) {
client := http.Client{}
client := d.httpClient()
statusData := "sid=" + sid
url = "https://" + d.Host + url
req, err := http.NewRequest("POST", url, bytes.NewReader([]byte(statusData)))
Expand Down Expand Up @@ -512,7 +531,7 @@ func (d *DuoClient) DoCallback(auth string) (err error) {

sigResp := auth + ":" + app

client := &http.Client{}
client := d.httpClient()

callbackData := "stateToken=" + d.StateToken + "&sig_response=" + sigResp
req, err = http.NewRequest("POST", d.Callback, bytes.NewReader([]byte(callbackData)))
Expand Down
32 changes: 24 additions & 8 deletions lib/okta.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@ package lib

import (
"bytes"
"crypto/tls"
"encoding/json"
"errors"
"fmt"
"io"
"io/ioutil"
"net/http"
"net/http/cookiejar"
Expand Down Expand Up @@ -51,6 +53,10 @@ type OktaClient struct {
BaseURL *url.URL
Domain string
MFAConfig MFAConfig
// Insecure! If set to non-nil, a TLS key log will be written here
// See https://golang.org/pkg/crypto/tls/#example_Config_keyLogWriter
// TLSKeyLogWriter propagates to DuoClient
TLSKeyLogWriter io.Writer
}

type MFAConfig struct {
Expand Down Expand Up @@ -78,7 +84,10 @@ func (c *OktaCreds) Validate(mfaConfig MFAConfig) error {
if err != nil {
return err
}
return c.ValidateWithClient(o)
}

func (c *OktaCreds) ValidateWithClient(o *OktaClient) error {
if err := o.AuthenticateUser(); err != nil {
return err
}
Expand Down Expand Up @@ -338,11 +347,12 @@ func (o *OktaClient) postChallenge(payload []byte, oktaFactorProvider string, ok
// Contact the Duo to initiate Push notification
if f.Embedded.Verification.Host != "" {
o.DuoClient = &DuoClient{
Host: f.Embedded.Verification.Host,
Signature: f.Embedded.Verification.Signature,
Callback: f.Embedded.Verification.Links.Complete.Href,
Device: o.MFAConfig.DuoDevice,
StateToken: o.UserAuth.StateToken,
Host: f.Embedded.Verification.Host,
Signature: f.Embedded.Verification.Signature,
Callback: f.Embedded.Verification.Links.Complete.Href,
Device: o.MFAConfig.DuoDevice,
StateToken: o.UserAuth.StateToken,
TLSKeyLogWriter: o.TLSKeyLogWriter,
}

log.Debugf("Host:%s\nSignature:%s\nStateToken:%s\n",
Expand Down Expand Up @@ -472,9 +482,15 @@ func (o *OktaClient) Get(method string, path string, data []byte, recv interface
header = http.Header{}
}

var tlsClientConfig tls.Config
if o.TLSKeyLogWriter != nil {
log.Info("SECURITY WARNING: logging TLS keys for Okta")
tlsClientConfig.KeyLogWriter = o.TLSKeyLogWriter
}
transCfg := &http.Transport{
Proxy: http.ProxyFromEnvironment,
TLSHandshakeTimeout: Timeout,
TLSClientConfig: &tlsClientConfig,
}
client = http.Client{
Transport: transCfg,
Expand Down Expand Up @@ -561,9 +577,9 @@ func (p *OktaProvider) Retrieve() (sts.Credentials, string, error) {
}

newCookieItem := keyring.Item{
Key: p.OktaSessionCookieKey,
Data: []byte(newSessionCookie),
Label: "okta session cookie",
Key: p.OktaSessionCookieKey,
Data: []byte(newSessionCookie),
Label: "okta session cookie",
KeychainNotTrustApplication: false,
}

Expand Down