open-vault/helper/mfa/duo/duo.go

150 lines
4.1 KiB
Go
Raw Normal View History

2015-07-31 00:16:53 +00:00
// Package duo provides a Duo MFA handler to authenticate users
// with Duo. This handler is registered as the "duo" type in
// mfa_config.
package duo
import (
"context"
"fmt"
"net/url"
"github.com/duosecurity/duo_api_golang/authapi"
"github.com/hashicorp/vault/logical"
"github.com/hashicorp/vault/logical/framework"
)
2015-07-31 00:16:53 +00:00
// DuoPaths returns path functions to configure Duo.
func DuoPaths() []*framework.Path {
return []*framework.Path{
pathDuoConfig(),
pathDuoAccess(),
}
}
2015-07-31 00:16:53 +00:00
// DuoRootPaths returns the paths that are used to configure Duo.
func DuoRootPaths() []string {
2016-08-19 20:48:32 +00:00
return []string{
"duo/access",
"duo/config",
}
}
2015-07-31 00:16:53 +00:00
// DuoHandler interacts with the Duo Auth API to authenticate a user
// login request. If successful, the original response from the login
// backend is returned.
func DuoHandler(ctx context.Context, req *logical.Request, d *framework.FieldData, resp *logical.Response) (
*logical.Response, error) {
duoConfig, err := GetDuoConfig(ctx, req)
2015-07-28 01:05:06 +00:00
if err != nil || duoConfig == nil {
return logical.ErrorResponse("Could not load Duo configuration"), nil
}
duoAuthClient, err := GetDuoAuthClient(ctx, req, duoConfig)
if err != nil {
return logical.ErrorResponse(err.Error()), nil
}
username, ok := resp.Auth.Metadata["username"]
if !ok {
return logical.ErrorResponse("Could not read username for MFA"), nil
}
2015-07-28 18:55:46 +00:00
var request *duoAuthRequest = &duoAuthRequest{}
request.successResp = resp
request.username = username
request.method = d.Get("method").(string)
request.passcode = d.Get("passcode").(string)
request.ipAddr = req.Connection.RemoteAddr
return duoHandler(duoConfig, duoAuthClient, request)
}
2015-07-28 01:05:06 +00:00
2015-07-28 18:55:46 +00:00
type duoAuthRequest struct {
successResp *logical.Response
2016-08-19 20:48:32 +00:00
username string
method string
passcode string
ipAddr string
2015-07-28 01:05:06 +00:00
}
2015-07-28 18:55:46 +00:00
func duoHandler(duoConfig *DuoConfig, duoAuthClient AuthClient, request *duoAuthRequest) (
*logical.Response, error) {
2015-07-28 18:55:46 +00:00
duoUser := fmt.Sprintf(duoConfig.UsernameFormat, request.username)
2015-07-28 01:05:06 +00:00
preauth, err := duoAuthClient.Preauth(
authapi.PreauthUsername(duoUser),
2015-07-28 18:55:46 +00:00
authapi.PreauthIpAddr(request.ipAddr),
)
2015-07-28 18:55:46 +00:00
if err != nil || preauth == nil {
return logical.ErrorResponse("Could not call Duo preauth"), nil
}
if preauth.StatResult.Stat != "OK" {
2015-07-28 03:38:49 +00:00
errorMsg := "Could not look up Duo user information"
if preauth.StatResult.Message != nil {
errorMsg = errorMsg + ": " + *preauth.StatResult.Message
}
if preauth.StatResult.Message_Detail != nil {
errorMsg = errorMsg + " (" + *preauth.StatResult.Message_Detail + ")"
}
return logical.ErrorResponse(errorMsg), nil
}
switch preauth.Response.Result {
case "allow":
2015-07-28 18:55:46 +00:00
return request.successResp, err
case "deny":
return logical.ErrorResponse(preauth.Response.Status_Msg), nil
case "enroll":
return logical.ErrorResponse(fmt.Sprintf("%s (%s)",
preauth.Response.Status_Msg,
preauth.Response.Enroll_Portal_Url)), nil
case "auth":
break
2015-07-28 18:55:46 +00:00
default:
return logical.ErrorResponse(fmt.Sprintf("Invalid Duo preauth response: %s",
preauth.Response.Result)), nil
}
2015-07-28 01:05:06 +00:00
options := []func(*url.Values){authapi.AuthUsername(duoUser)}
2015-07-28 18:55:46 +00:00
if request.method == "" {
request.method = "auto"
}
2016-12-19 20:37:44 +00:00
if request.method == "auto" || request.method == "push" {
if duoConfig.PushInfo != "" {
options = append(options, authapi.AuthPushinfo(duoConfig.PushInfo))
}
}
2015-07-28 18:55:46 +00:00
if request.passcode != "" {
request.method = "passcode"
options = append(options, authapi.AuthPasscode(request.passcode))
} else {
options = append(options, authapi.AuthDevice("auto"))
}
2015-07-28 18:55:46 +00:00
result, err := duoAuthClient.Auth(request.method, options...)
2015-07-28 18:55:46 +00:00
if err != nil || result == nil {
return logical.ErrorResponse("Could not call Duo auth"), nil
}
if result.StatResult.Stat != "OK" {
2015-07-28 03:38:49 +00:00
errorMsg := "Could not authenticate Duo user"
if result.StatResult.Message != nil {
errorMsg = errorMsg + ": " + *result.StatResult.Message
}
if result.StatResult.Message_Detail != nil {
errorMsg = errorMsg + " (" + *result.StatResult.Message_Detail + ")"
}
return logical.ErrorResponse(errorMsg), nil
}
if result.Response.Result != "allow" {
return logical.ErrorResponse(result.Response.Status_Msg), nil
}
2015-07-28 18:55:46 +00:00
return request.successResp, nil
}