Skip to content

Commit

Permalink
Add self-service Bootstrap OTP generation.
Browse files Browse the repository at this point in the history
  • Loading branch information
rgooch committed Jun 12, 2020
1 parent c18a666 commit 7bd2fdb
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 7 deletions.
38 changes: 38 additions & 0 deletions cmd/keymasterd/2fa_bootstrapOTP.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,44 @@ func (state *RuntimeState) BootstrapOtpAuthHandler(w http.ResponseWriter,
}
}

func (state *RuntimeState) trySelfServiceGenerateBootstrapOTP(username string,
profile *userProfile) {
if !state.Config.Base.AllowSelfServiceBootstrapOTP ||
profile.UserHasRegistered2ndFactor ||
len(state.userBootstrapOtpHash(profile, false)) > 0 ||
state.emailManager == nil {
return
}
bootstrapOtpValue, err := genRandomString()
if err != nil {
state.logger.Printf("error generating Bootstrap OTP: %s", err)
return
}
duration := time.Minute * 5
bootstrapOtpHash := sha512.Sum512([]byte(bootstrapOtpValue))
bootstrapOTP := bootstrapOTPData{
ExpiresAt: time.Now().Add(duration),
Sha512Hash: bootstrapOtpHash[:],
}
profile.BootstrapOTP = bootstrapOTP
var fingerprint [4]byte
copy(fingerprint[:], bootstrapOtpHash[:4])
err = state.sendBootstrapOtpEmail(bootstrapOtpHash[:],
bootstrapOtpValue, duration, username, username)
if err != nil {
state.logger.Printf("error sending email: %s", err)
return
}
err = state.SaveUserProfile(username, profile)
if err != nil {
state.logger.Printf("error saving profile: %s", err)
return
}
state.logger.Debugf(0,
"generated bootstrap OTP by/for: %s, duration: %s, hash: %x\n",
duration, username, bootstrapOtpHash)
}

func (state *RuntimeState) userBootstrapOtpHash(profile *userProfile,
fromCache bool) []byte {
if len(profile.U2fAuthData) > 0 || len(profile.TOTPAuthData) > 0 {
Expand Down
19 changes: 12 additions & 7 deletions cmd/keymasterd/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -346,17 +346,19 @@ func convertToBindDN(username string, bind_pattern string) string {
return fmt.Sprintf(bind_pattern, username)
}

func checkUserPassword(username string, password string, config AppConfigFile, passwordChecker pwauth.PasswordAuthenticator, r *http.Request) (bool, error) {
func checkUserPassword(username string, password string, config AppConfigFile,
passwordChecker pwauth.PasswordAuthenticator,
r *http.Request) (bool, error) {
clientType := getClientType(r)
if passwordChecker != nil {
logger.Debugf(3, "checking auth with passwordChecker")
isLDAP := false
if len(config.Ldap.LDAPTargetURLs) > 0 {
isLDAP = true
}

start := time.Now()
valid, err := passwordChecker.PasswordAuthenticate(username, []byte(password))
valid, err := passwordChecker.PasswordAuthenticate(username,
[]byte(password))
if err != nil {
return false, err
}
Expand All @@ -368,18 +370,18 @@ func checkUserPassword(username string, password string, config AppConfigFile, p
if isOktaPwAuth {
metricLogExternalServiceDuration("okta-passwd", time.Since(start))
}
logger.Debugf(3, "pwdChaker output = %d", valid)
logger.Debugf(3, "pwdChecker output = %d", valid)
metricLogAuthOperation(clientType, "password", valid)
return valid, nil
}

if config.Base.HtpasswdFilename != "" {
logger.Debugf(3, "I have htpasswed filename")
buffer, err := ioutil.ReadFile(config.Base.HtpasswdFilename)
if err != nil {
return false, err
}
valid, err := authutil.CheckHtpasswdUserPassword(username, password, buffer)
valid, err := authutil.CheckHtpasswdUserPassword(username, password,
buffer)
if err != nil {
return false, err
}
Expand Down Expand Up @@ -412,7 +414,7 @@ func browserSupportsU2F(r *http.Request) bool {
if strings.Contains(r.UserAgent(), "Presto/") {
return true
}
//Once FF support reaches main we can remove these silly checks
// Once FF support reaches main we can remove these silly checks.
if strings.Contains(r.UserAgent(), "Firefox/57") ||
strings.Contains(r.UserAgent(), "Firefox/58") ||
strings.Contains(r.UserAgent(), "Firefox/59") ||
Expand Down Expand Up @@ -1064,6 +1066,9 @@ func (state *RuntimeState) loginHandler(w http.ResponseWriter,
"cannot load user profile")
return
}
if !fromCache {
state.trySelfServiceGenerateBootstrapOTP(username, profile)
}
userHasBootstrapOTP := len(state.userBootstrapOtpHash(profile,
fromCache)) > 0
// Compute the cert prefs
Expand Down
1 change: 1 addition & 0 deletions cmd/keymasterd/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ type baseConfig struct {
HideStandardLogin bool `yaml:"hide_standard_login"`
AllowedAuthBackendsForCerts []string `yaml:"allowed_auth_backends_for_certs"`
AllowedAuthBackendsForWebUI []string `yaml:"allowed_auth_backends_for_webui"`
AllowSelfServiceBootstrapOTP bool `yaml:"allow_self_service_bootstrap_otp"`
AdminUsers []string `yaml:"admin_users"`
AdminGroups []string `yaml:"admin_groups"`
PublicLogs bool `yaml:"public_logs"`
Expand Down

0 comments on commit 7bd2fdb

Please sign in to comment.