2016-05-02 04:08:07 +00:00
package vault
import (
2018-01-08 18:31:38 +00:00
"context"
2016-09-29 04:01:28 +00:00
"fmt"
2016-05-02 04:08:07 +00:00
"strings"
"time"
"github.com/armon/go-metrics"
2016-05-16 20:11:33 +00:00
"github.com/hashicorp/go-multierror"
2018-03-02 17:18:39 +00:00
"github.com/hashicorp/vault/audit"
2017-02-16 20:15:02 +00:00
"github.com/hashicorp/vault/helper/consts"
2017-10-11 17:21:20 +00:00
"github.com/hashicorp/vault/helper/identity"
2016-09-29 19:03:47 +00:00
"github.com/hashicorp/vault/helper/jsonutil"
2016-08-08 21:42:25 +00:00
"github.com/hashicorp/vault/helper/policyutil"
2016-05-02 04:08:07 +00:00
"github.com/hashicorp/vault/helper/strutil"
2017-04-24 19:15:01 +00:00
"github.com/hashicorp/vault/helper/wrapping"
2016-05-02 04:08:07 +00:00
"github.com/hashicorp/vault/logical"
)
2017-10-23 18:59:37 +00:00
const (
replTimeout = 10 * time . Second
)
2016-05-02 04:08:07 +00:00
// HandleRequest is used to handle a new incoming request
func ( c * Core ) HandleRequest ( req * logical . Request ) ( resp * logical . Response , err error ) {
c . stateLock . RLock ( )
defer c . stateLock . RUnlock ( )
if c . sealed {
2017-02-16 20:15:02 +00:00
return nil , consts . ErrSealed
2016-05-02 04:08:07 +00:00
}
if c . standby {
2017-02-16 20:15:02 +00:00
return nil , consts . ErrStandby
2016-05-02 04:08:07 +00:00
}
2018-01-19 06:44:44 +00:00
ctx , cancel := context . WithCancel ( c . activeContext )
2018-01-08 18:31:38 +00:00
defer cancel ( )
2016-05-02 04:08:07 +00:00
// Allowing writing to a path ending in / makes it extremely difficult to
2017-09-15 13:02:29 +00:00
// understand user intent for the filesystem-like backends (kv,
2016-05-02 04:08:07 +00:00
// cubbyhole) -- did they want a key named foo/ or did they want to write
// to a directory foo/ with no (or forgotten) key, or...? It also affects
// lookup, because paths ending in / are considered prefixes by some
// backends. Basically, it's all just terrible, so don't allow it.
if strings . HasSuffix ( req . Path , "/" ) &&
( req . Operation == logical . UpdateOperation ||
req . Operation == logical . CreateOperation ) {
return logical . ErrorResponse ( "cannot write to a path ending in '/'" ) , nil
}
var auth * logical . Auth
if c . router . LoginPath ( req . Path ) {
2018-01-08 18:31:38 +00:00
resp , auth , err = c . handleLoginRequest ( ctx , req )
2016-05-02 04:08:07 +00:00
} else {
2018-01-08 18:31:38 +00:00
resp , auth , err = c . handleRequest ( ctx , req )
2016-05-02 04:08:07 +00:00
}
// Ensure we don't leak internal data
if resp != nil {
if resp . Secret != nil {
resp . Secret . InternalData = nil
}
if resp . Auth != nil {
resp . Auth . InternalData = nil
}
}
// We are wrapping if there is anything to wrap (not a nil response) and a
2016-09-29 04:01:28 +00:00
// TTL was specified for the token. Errors on a call should be returned to
// the caller, so wrapping is turned off if an error is hit and the error
// is logged to the audit log.
wrapping := resp != nil &&
err == nil &&
! resp . IsError ( ) &&
resp . WrapInfo != nil &&
2017-11-13 20:31:32 +00:00
resp . WrapInfo . TTL != 0 &&
resp . WrapInfo . Token == ""
2016-05-02 04:08:07 +00:00
2016-05-16 20:11:33 +00:00
if wrapping {
2018-01-08 18:31:38 +00:00
cubbyResp , cubbyErr := c . wrapInCubbyhole ( ctx , req , resp , auth )
2016-05-16 20:11:33 +00:00
// If not successful, returns either an error response from the
2016-09-29 04:01:28 +00:00
// cubbyhole backend or an error; if either is set, set resp and err to
// those and continue so that that's what we audit log. Otherwise
// finish the wrapping and audit log that.
if cubbyResp != nil || cubbyErr != nil {
resp = cubbyResp
err = cubbyErr
} else {
wrappingResp := & logical . Response {
WrapInfo : resp . WrapInfo ,
2017-06-05 14:52:43 +00:00
Warnings : resp . Warnings ,
2016-09-29 04:01:28 +00:00
}
resp = wrappingResp
2016-05-02 04:08:07 +00:00
}
}
2016-09-29 19:03:47 +00:00
auditResp := resp
// When unwrapping we want to log the actual response that will be written
// out. We still want to return the raw value to avoid automatic updating
// to any of it.
if req . Path == "sys/wrapping/unwrap" &&
resp != nil &&
resp . Data != nil &&
resp . Data [ logical . HTTPRawBody ] != nil {
// Decode the JSON
httpResp := & logical . HTTPResponse { }
err := jsonutil . DecodeJSON ( resp . Data [ logical . HTTPRawBody ] . ( [ ] byte ) , httpResp )
if err != nil {
c . logger . Error ( "core: failed to unmarshal wrapped HTTP response for audit logging" , "error" , err )
return nil , ErrInternalError
}
auditResp = logical . HTTPResponseToLogicalResponse ( httpResp )
}
2018-03-02 17:18:39 +00:00
var nonHMACReqDataKeys [ ] string
var nonHMACRespDataKeys [ ] string
entry := c . router . MatchingMountEntry ( req . Path )
if entry != nil {
// Get and set ignored HMAC'd value. Reset those back to empty afterwards.
if rawVals , ok := entry . synthesizedConfigCache . Load ( "audit_non_hmac_request_keys" ) ; ok {
nonHMACReqDataKeys = rawVals . ( [ ] string )
}
// Get and set ignored HMAC'd value. Reset those back to empty afterwards.
if auditResp != nil {
if rawVals , ok := entry . synthesizedConfigCache . Load ( "audit_non_hmac_response_keys" ) ; ok {
nonHMACRespDataKeys = rawVals . ( [ ] string )
}
}
}
2016-05-02 04:08:07 +00:00
// Create an audit trail of the response
2018-03-02 17:18:39 +00:00
logInput := & audit . LogInput {
Auth : auth ,
Request : req ,
Response : auditResp ,
OuterErr : err ,
NonHMACReqDataKeys : nonHMACReqDataKeys ,
NonHMACRespDataKeys : nonHMACRespDataKeys ,
}
if auditErr := c . auditBroker . LogResponse ( ctx , logInput , c . auditedHeaders ) ; auditErr != nil {
2016-08-19 20:45:17 +00:00
c . logger . Error ( "core: failed to audit response" , "request_path" , req . Path , "error" , auditErr )
2016-05-02 04:08:07 +00:00
return nil , ErrInternalError
}
return
}
2018-01-08 18:31:38 +00:00
func ( c * Core ) handleRequest ( ctx context . Context , req * logical . Request ) ( retResp * logical . Response , retAuth * logical . Auth , retErr error ) {
2016-05-02 04:08:07 +00:00
defer metrics . MeasureSince ( [ ] string { "core" , "handle_request" } , time . Now ( ) )
2018-03-02 17:18:39 +00:00
var nonHMACReqDataKeys [ ] string
entry := c . router . MatchingMountEntry ( req . Path )
if entry != nil {
// Get and set ignored HMAC'd value.
if rawVals , ok := entry . synthesizedConfigCache . Load ( "audit_non_hmac_request_keys" ) ; ok {
nonHMACReqDataKeys = rawVals . ( [ ] string )
}
}
2016-05-02 04:08:07 +00:00
// Validate the token
2018-01-08 18:31:38 +00:00
auth , te , ctErr := c . checkToken ( ctx , req , false )
2016-05-02 07:11:14 +00:00
// We run this logic first because we want to decrement the use count even in the case of an error
2016-05-02 04:08:07 +00:00
if te != nil {
2016-05-02 07:11:14 +00:00
// Attempt to use the token (decrement NumUses)
var err error
2018-01-19 06:44:44 +00:00
te , err = c . tokenStore . UseToken ( ctx , te )
2016-05-02 07:11:14 +00:00
if err != nil {
2016-08-19 20:45:17 +00:00
c . logger . Error ( "core: failed to use token" , "error" , err )
2016-05-16 20:11:33 +00:00
retErr = multierror . Append ( retErr , ErrInternalError )
return nil , nil , retErr
2016-05-02 07:11:14 +00:00
}
if te == nil {
// Token has been revoked by this point
2016-05-16 20:11:33 +00:00
retErr = multierror . Append ( retErr , logical . ErrPermissionDenied )
return nil , nil , retErr
2016-05-02 07:11:14 +00:00
}
if te . NumUses == - 1 {
// We defer a revocation until after logic has run, since this is a
// valid request (this is the token's final use). We pass the ID in
// directly just to be safe in case something else modifies te later.
defer func ( id string ) {
2018-01-19 06:44:44 +00:00
err = c . tokenStore . Revoke ( ctx , id )
2016-05-02 07:11:14 +00:00
if err != nil {
2016-08-19 20:45:17 +00:00
c . logger . Error ( "core: failed to revoke token" , "error" , err )
2016-05-02 07:11:14 +00:00
retResp = nil
retAuth = nil
2016-05-16 20:11:33 +00:00
retErr = multierror . Append ( retErr , ErrInternalError )
2016-05-02 07:11:14 +00:00
}
if retResp != nil && retResp . Secret != nil &&
// Some backends return a TTL even without a Lease ID
retResp . Secret . LeaseID != "" {
retResp = logical . ErrorResponse ( "Secret cannot be returned; token had one use left, so leased credentials were immediately revoked." )
return
}
} ( te . ID )
}
2016-05-02 04:08:07 +00:00
}
2016-05-02 07:11:14 +00:00
if ctErr != nil {
2016-05-02 04:08:07 +00:00
// If it is an internal error we return that, otherwise we
// return invalid request so that the status codes can be correct
2017-11-13 20:31:32 +00:00
errType := logical . ErrInvalidRequest
2016-05-02 07:11:14 +00:00
switch ctErr {
2016-05-02 04:08:07 +00:00
case ErrInternalError , logical . ErrPermissionDenied :
2016-05-02 07:11:14 +00:00
errType = ctErr
2016-05-02 04:08:07 +00:00
}
2018-03-02 17:18:39 +00:00
logInput := & audit . LogInput {
Auth : auth ,
Request : req ,
OuterErr : ctErr ,
NonHMACReqDataKeys : nonHMACReqDataKeys ,
}
if err := c . auditBroker . LogRequest ( ctx , logInput , c . auditedHeaders ) ; err != nil {
2016-08-19 20:45:17 +00:00
c . logger . Error ( "core: failed to audit request" , "path" , req . Path , "error" , err )
2016-05-02 04:08:07 +00:00
}
2016-05-16 20:11:33 +00:00
if errType != nil {
retErr = multierror . Append ( retErr , errType )
}
2017-08-15 20:44:16 +00:00
if ctErr == ErrInternalError {
return nil , auth , retErr
}
2017-06-05 22:04:31 +00:00
return logical . ErrorResponse ( ctErr . Error ( ) ) , auth , retErr
2016-05-02 04:08:07 +00:00
}
// Attach the display name
req . DisplayName = auth . DisplayName
// Create an audit trail of the request
2018-03-02 17:18:39 +00:00
logInput := & audit . LogInput {
Auth : auth ,
Request : req ,
NonHMACReqDataKeys : nonHMACReqDataKeys ,
}
if err := c . auditBroker . LogRequest ( ctx , logInput , c . auditedHeaders ) ; err != nil {
2016-08-19 20:45:17 +00:00
c . logger . Error ( "core: failed to audit request" , "path" , req . Path , "error" , err )
2016-05-16 20:11:33 +00:00
retErr = multierror . Append ( retErr , ErrInternalError )
return nil , auth , retErr
2016-05-02 04:08:07 +00:00
}
// Route the request
2018-01-08 18:31:38 +00:00
resp , routeErr := c . router . Route ( ctx , req )
2016-05-16 20:11:33 +00:00
if resp != nil {
2016-11-11 20:12:11 +00:00
// If wrapping is used, use the shortest between the request and response
var wrapTTL time . Duration
2017-08-02 22:28:58 +00:00
var wrapFormat , creationPath string
2017-11-09 17:47:42 +00:00
var sealWrap bool
2016-05-16 20:11:33 +00:00
2016-11-11 20:12:11 +00:00
// Ensure no wrap info information is set other than, possibly, the TTL
if resp . WrapInfo != nil {
if resp . WrapInfo . TTL > 0 {
wrapTTL = resp . WrapInfo . TTL
}
2017-01-04 21:44:03 +00:00
wrapFormat = resp . WrapInfo . Format
2017-08-02 22:28:58 +00:00
creationPath = resp . WrapInfo . CreationPath
2017-11-09 17:47:42 +00:00
sealWrap = resp . WrapInfo . SealWrap
2016-11-11 20:12:11 +00:00
resp . WrapInfo = nil
}
2017-01-04 21:44:03 +00:00
if req . WrapInfo != nil {
if req . WrapInfo . TTL > 0 {
switch {
case wrapTTL == 0 :
wrapTTL = req . WrapInfo . TTL
case req . WrapInfo . TTL < wrapTTL :
wrapTTL = req . WrapInfo . TTL
}
}
// If the wrap format hasn't been set by the response, set it to
// the request format
if req . WrapInfo . Format != "" && wrapFormat == "" {
wrapFormat = req . WrapInfo . Format
2016-11-11 20:12:11 +00:00
}
}
if wrapTTL > 0 {
2017-04-24 19:15:01 +00:00
resp . WrapInfo = & wrapping . ResponseWrapInfo {
2017-08-02 22:28:58 +00:00
TTL : wrapTTL ,
Format : wrapFormat ,
CreationPath : creationPath ,
2017-11-09 17:47:42 +00:00
SealWrap : sealWrap ,
2016-05-16 20:11:33 +00:00
}
}
}
2016-05-02 04:08:07 +00:00
// If there is a secret, we must register it with the expiration manager.
// We exclude renewal of a lease, since it does not need to be re-registered
2017-06-20 16:34:00 +00:00
if resp != nil && resp . Secret != nil && ! strings . HasPrefix ( req . Path , "sys/renew" ) &&
! strings . HasPrefix ( req . Path , "sys/leases/renew" ) {
2016-05-02 04:08:07 +00:00
// Get the SystemView for the mount
sysView := c . router . MatchingSystemView ( req . Path )
if sysView == nil {
2016-08-19 20:45:17 +00:00
c . logger . Error ( "core: unable to retrieve system view from router" )
2016-05-16 20:11:33 +00:00
retErr = multierror . Append ( retErr , ErrInternalError )
return nil , auth , retErr
2016-05-02 04:08:07 +00:00
}
// Apply the default lease if none given
if resp . Secret . TTL == 0 {
resp . Secret . TTL = sysView . DefaultLeaseTTL ( )
}
// Limit the lease duration
maxTTL := sysView . MaxLeaseTTL ( )
if resp . Secret . TTL > maxTTL {
resp . Secret . TTL = maxTTL
}
2017-09-15 13:02:29 +00:00
// KV mounts should return the TTL but not register
2016-05-02 04:08:07 +00:00
// for a lease as this provides a massive slowdown
registerLease := true
2018-03-21 19:04:27 +00:00
matchingMountEntry := c . router . MatchingMountEntry ( req . Path )
if matchingMountEntry == nil {
c . logger . Error ( "core: unable to retrieve kv mount entry from router" )
2016-05-16 20:11:33 +00:00
retErr = multierror . Append ( retErr , ErrInternalError )
return nil , auth , retErr
2016-05-02 04:08:07 +00:00
}
2018-03-21 19:04:27 +00:00
switch matchingMountEntry . Type {
case "kv" , "generic" :
// If we are kv type, first see if we are an older passthrough
// backend, and otherwise check the mount entry options.
matchingBackend := c . router . MatchingBackend ( req . Path )
if matchingBackend == nil {
c . logger . Error ( "core: unable to retrieve kv backend from router" )
retErr = multierror . Append ( retErr , ErrInternalError )
return nil , auth , retErr
}
if ptbe , ok := matchingBackend . ( * PassthroughBackend ) ; ok {
if ! ptbe . GeneratesLeases ( ) {
registerLease = false
resp . Secret . Renewable = false
}
} else if matchingMountEntry . Options == nil || matchingMountEntry . Options [ "leased_passthrough" ] != "true" {
registerLease = false
resp . Secret . Renewable = false
}
case "plugin" :
// If we are a plugin type and the plugin name is "kv" check the
// mount entry options.
if matchingMountEntry . Config . PluginName == "kv" && ( matchingMountEntry . Options == nil || matchingMountEntry . Options [ "leased_passthrough" ] != "true" ) {
2016-05-02 04:08:07 +00:00
registerLease = false
resp . Secret . Renewable = false
}
}
if registerLease {
leaseID , err := c . expiration . Register ( req , resp )
if err != nil {
2016-08-19 20:45:17 +00:00
c . logger . Error ( "core: failed to register lease" , "request_path" , req . Path , "error" , err )
2016-05-16 20:11:33 +00:00
retErr = multierror . Append ( retErr , ErrInternalError )
return nil , auth , retErr
2016-05-02 04:08:07 +00:00
}
resp . Secret . LeaseID = leaseID
}
}
2017-11-02 20:05:48 +00:00
// If the request was to renew a token, and if there are group aliases set
// in the auth object, then the group memberships should be refreshed
if strings . HasPrefix ( req . Path , "auth/token/renew" ) &&
resp != nil &&
resp . Auth != nil &&
resp . Auth . EntityID != "" &&
resp . Auth . GroupAliases != nil {
err := c . identityStore . refreshExternalGroupMembershipsByEntityID ( resp . Auth . EntityID , resp . Auth . GroupAliases )
if err != nil {
c . logger . Error ( "core: failed to refresh external group memberships" , "error" , err )
retErr = multierror . Append ( retErr , ErrInternalError )
return nil , auth , retErr
}
}
2016-05-02 04:08:07 +00:00
// Only the token store is allowed to return an auth block, for any
// other request this is an internal error. We exclude renewal of a token,
// since it does not need to be re-registered
if resp != nil && resp . Auth != nil && ! strings . HasPrefix ( req . Path , "auth/token/renew" ) {
if ! strings . HasPrefix ( req . Path , "auth/token/" ) {
2016-08-19 20:45:17 +00:00
c . logger . Error ( "core: unexpected Auth response for non-token backend" , "request_path" , req . Path )
2016-05-16 20:11:33 +00:00
retErr = multierror . Append ( retErr , ErrInternalError )
return nil , auth , retErr
2016-05-02 04:08:07 +00:00
}
// Register with the expiration manager. We use the token's actual path
// here because roles allow suffixes.
2018-01-19 06:44:44 +00:00
te , err := c . tokenStore . Lookup ( ctx , resp . Auth . ClientToken )
2016-05-02 04:08:07 +00:00
if err != nil {
2016-08-19 20:45:17 +00:00
c . logger . Error ( "core: failed to look up token" , "error" , err )
2016-05-16 20:11:33 +00:00
retErr = multierror . Append ( retErr , ErrInternalError )
2017-05-03 18:29:57 +00:00
return nil , auth , retErr
2016-05-02 04:08:07 +00:00
}
if err := c . expiration . RegisterAuth ( te . Path , resp . Auth ) ; err != nil {
2018-01-19 06:44:44 +00:00
c . tokenStore . Revoke ( ctx , te . ID )
2016-08-19 20:45:17 +00:00
c . logger . Error ( "core: failed to register token lease" , "request_path" , req . Path , "error" , err )
2016-05-16 20:11:33 +00:00
retErr = multierror . Append ( retErr , ErrInternalError )
return nil , auth , retErr
2016-05-02 04:08:07 +00:00
}
}
2016-09-29 04:01:28 +00:00
if resp != nil &&
req . Path == "cubbyhole/response" &&
len ( te . Policies ) == 1 &&
te . Policies [ 0 ] == responseWrappingPolicyName {
2016-09-29 21:44:15 +00:00
resp . AddWarning ( "Reading from 'cubbyhole/response' is deprecated. Please use sys/wrapping/unwrap to unwrap responses, as it provides additional security checks and other benefits." )
2016-09-29 04:01:28 +00:00
}
2016-05-02 04:08:07 +00:00
// Return the response and error
2017-02-17 04:09:39 +00:00
if routeErr != nil {
retErr = multierror . Append ( retErr , routeErr )
2016-05-16 20:11:33 +00:00
}
2017-11-02 20:05:48 +00:00
2016-05-16 20:11:33 +00:00
return resp , auth , retErr
2016-05-02 04:08:07 +00:00
}
// handleLoginRequest is used to handle a login request, which is an
// unauthenticated request to the backend.
2018-01-08 18:31:38 +00:00
func ( c * Core ) handleLoginRequest ( ctx context . Context , req * logical . Request ) ( retResp * logical . Response , retAuth * logical . Auth , retErr error ) {
2016-05-02 04:08:07 +00:00
defer metrics . MeasureSince ( [ ] string { "core" , "handle_login_request" } , time . Now ( ) )
2017-10-23 18:59:37 +00:00
req . Unauthenticated = true
2017-10-23 20:49:46 +00:00
2017-10-23 18:59:37 +00:00
var auth * logical . Auth
2016-05-02 04:08:07 +00:00
// Create an audit trail of the request, auth is not available on login requests
2017-10-23 18:59:37 +00:00
// Create an audit trail of the request. Attach auth if it was returned,
// e.g. if a token was provided.
2018-03-02 17:18:39 +00:00
logInput := & audit . LogInput {
Auth : auth ,
Request : req ,
}
if err := c . auditBroker . LogRequest ( ctx , logInput , c . auditedHeaders ) ; err != nil {
2016-08-19 20:45:17 +00:00
c . logger . Error ( "core: failed to audit request" , "path" , req . Path , "error" , err )
2016-05-02 04:08:07 +00:00
return nil , nil , ErrInternalError
}
2016-08-08 21:32:37 +00:00
// The token store uses authentication even when creating a new token,
// so it's handled in handleRequest. It should not be reached here.
if strings . HasPrefix ( req . Path , "auth/token/" ) {
2016-08-19 20:45:17 +00:00
c . logger . Error ( "core: unexpected login request for token backend" , "request_path" , req . Path )
2016-08-08 21:56:14 +00:00
return nil , nil , ErrInternalError
2016-08-08 21:32:37 +00:00
}
2016-05-02 04:08:07 +00:00
// Route the request
2018-01-08 18:31:38 +00:00
resp , routeErr := c . router . Route ( ctx , req )
2016-07-05 15:46:21 +00:00
if resp != nil {
2016-11-11 20:12:11 +00:00
// If wrapping is used, use the shortest between the request and response
var wrapTTL time . Duration
2017-08-02 22:28:58 +00:00
var wrapFormat , creationPath string
2017-11-09 17:47:42 +00:00
var sealWrap bool
2016-07-05 15:46:21 +00:00
2016-11-11 20:12:11 +00:00
// Ensure no wrap info information is set other than, possibly, the TTL
if resp . WrapInfo != nil {
if resp . WrapInfo . TTL > 0 {
wrapTTL = resp . WrapInfo . TTL
}
2017-01-04 21:44:03 +00:00
wrapFormat = resp . WrapInfo . Format
2017-08-02 22:28:58 +00:00
creationPath = resp . WrapInfo . CreationPath
2017-11-09 17:47:42 +00:00
sealWrap = resp . WrapInfo . SealWrap
2016-11-11 20:12:11 +00:00
resp . WrapInfo = nil
}
2017-01-04 21:44:03 +00:00
if req . WrapInfo != nil {
if req . WrapInfo . TTL > 0 {
switch {
case wrapTTL == 0 :
wrapTTL = req . WrapInfo . TTL
case req . WrapInfo . TTL < wrapTTL :
wrapTTL = req . WrapInfo . TTL
}
}
if req . WrapInfo . Format != "" && wrapFormat == "" {
wrapFormat = req . WrapInfo . Format
2016-11-11 20:12:11 +00:00
}
}
if wrapTTL > 0 {
2017-04-24 19:15:01 +00:00
resp . WrapInfo = & wrapping . ResponseWrapInfo {
2017-08-02 22:28:58 +00:00
TTL : wrapTTL ,
Format : wrapFormat ,
CreationPath : creationPath ,
2017-11-09 17:47:42 +00:00
SealWrap : sealWrap ,
2016-07-05 15:46:21 +00:00
}
}
}
2016-05-02 04:08:07 +00:00
// A login request should never return a secret!
if resp != nil && resp . Secret != nil {
2016-08-19 20:45:17 +00:00
c . logger . Error ( "core: unexpected Secret response for login path" , "request_path" , req . Path )
2016-05-02 04:08:07 +00:00
return nil , nil , ErrInternalError
}
// If the response generated an authentication, then generate the token
if resp != nil && resp . Auth != nil {
2017-10-11 17:21:20 +00:00
var entity * identity . Entity
2016-05-02 04:08:07 +00:00
auth = resp . Auth
2017-10-11 17:21:20 +00:00
if auth . Alias != nil {
// Overwrite the mount type and mount path in the alias
// information
auth . Alias . MountType = req . MountType
auth . Alias . MountAccessor = req . MountAccessor
if auth . Alias . Name == "" {
return nil , nil , fmt . Errorf ( "missing name in alias" )
}
var err error
2018-02-09 15:40:56 +00:00
// Fetch the entity for the alias, or create an entity if one
// doesn't exist.
entity , err = c . identityStore . CreateOrFetchEntity ( auth . Alias )
2017-10-11 17:21:20 +00:00
if err != nil {
return nil , nil , err
}
if entity == nil {
2018-02-09 15:40:56 +00:00
return nil , nil , fmt . Errorf ( "failed to create an entity for the authenticated alias" )
2017-10-11 17:21:20 +00:00
}
auth . EntityID = entity . ID
2017-11-02 20:05:48 +00:00
if auth . GroupAliases != nil {
err = c . identityStore . refreshExternalGroupMembershipsByEntityID ( auth . EntityID , auth . GroupAliases )
if err != nil {
return nil , nil , err
}
}
2017-10-11 17:21:20 +00:00
}
2016-08-08 21:32:37 +00:00
if strutil . StrListSubset ( auth . Policies , [ ] string { "root" } ) {
2017-09-13 01:48:52 +00:00
return logical . ErrorResponse ( "auth methods cannot create root tokens" ) , nil , logical . ErrInvalidRequest
2016-08-08 21:32:37 +00:00
}
2016-05-02 04:08:07 +00:00
// Determine the source of the login
source := c . router . MatchingMount ( req . Path )
source = strings . TrimPrefix ( source , credentialRoutePrefix )
source = strings . Replace ( source , "/" , "-" , - 1 )
// Prepend the source to the display name
auth . DisplayName = strings . TrimSuffix ( source + auth . DisplayName , "-" )
sysView := c . router . MatchingSystemView ( req . Path )
if sysView == nil {
2016-08-19 20:45:17 +00:00
c . logger . Error ( "core: unable to look up sys view for login path" , "request_path" , req . Path )
2016-05-02 04:08:07 +00:00
return nil , nil , ErrInternalError
}
2017-12-15 18:30:05 +00:00
// Start off with the sys default value, and update according to period/TTL
// from resp.Auth
tokenTTL := sysView . DefaultLeaseTTL ( )
2016-05-02 04:08:07 +00:00
2017-12-15 18:30:05 +00:00
switch {
case auth . Period > time . Duration ( 0 ) :
// Cap the period value to the sys max_ttl value. The auth backend should
// have checked for it on its login path, but we check here again for
// sanity.
if auth . Period > sysView . MaxLeaseTTL ( ) {
auth . Period = sysView . MaxLeaseTTL ( )
}
tokenTTL = auth . Period
case auth . TTL > time . Duration ( 0 ) :
// Cap the TTL value. The auth backend should have checked for it on its
// login path (e.g. a call to b.SanitizeTTL), but we check here again for
// sanity.
if auth . TTL > sysView . MaxLeaseTTL ( ) {
auth . TTL = sysView . MaxLeaseTTL ( )
}
tokenTTL = auth . TTL
2016-05-02 04:08:07 +00:00
}
// Generate a token
te := TokenEntry {
Path : req . Path ,
Policies : auth . Policies ,
Meta : auth . Metadata ,
DisplayName : auth . DisplayName ,
CreationTime : time . Now ( ) . Unix ( ) ,
2017-12-15 18:30:05 +00:00
TTL : tokenTTL ,
2017-03-03 14:31:20 +00:00
NumUses : auth . NumUses ,
2017-10-18 17:23:05 +00:00
EntityID : auth . EntityID ,
2016-05-02 04:08:07 +00:00
}
2016-08-08 21:42:25 +00:00
te . Policies = policyutil . SanitizePolicies ( te . Policies , true )
2016-08-08 21:32:37 +00:00
2016-09-29 04:01:28 +00:00
// Prevent internal policies from being assigned to tokens
for _ , policy := range te . Policies {
if strutil . StrListContains ( nonAssignablePolicies , policy ) {
return logical . ErrorResponse ( fmt . Sprintf ( "cannot assign policy %q" , policy ) ) , nil , logical . ErrInvalidRequest
}
}
2018-01-19 06:44:44 +00:00
if err := c . tokenStore . create ( ctx , & te ) ; err != nil {
2016-08-19 20:45:17 +00:00
c . logger . Error ( "core: failed to create token" , "error" , err )
2016-05-02 04:08:07 +00:00
return nil , auth , ErrInternalError
}
2017-12-15 18:30:05 +00:00
// Populate the client token, accessor, and TTL
2016-05-02 04:08:07 +00:00
auth . ClientToken = te . ID
auth . Accessor = te . Accessor
auth . Policies = te . Policies
2017-12-15 18:30:05 +00:00
auth . TTL = te . TTL
2016-05-02 04:08:07 +00:00
// Register with the expiration manager
if err := c . expiration . RegisterAuth ( te . Path , auth ) ; err != nil {
2018-01-19 06:44:44 +00:00
c . tokenStore . Revoke ( ctx , te . ID )
2016-08-19 20:45:17 +00:00
c . logger . Error ( "core: failed to register token lease" , "request_path" , req . Path , "error" , err )
2016-05-02 04:08:07 +00:00
return nil , auth , ErrInternalError
}
// Attach the display name, might be used by audit backends
req . DisplayName = auth . DisplayName
}
2017-02-17 04:09:39 +00:00
return resp , auth , routeErr
2016-05-02 04:08:07 +00:00
}