func respondLogical(w http.ResponseWriter, r *http.Request, req *logical.Request, dataOnly bool, resp *logical.Response) { var httpResp *logical.HTTPResponse var ret interface{} if resp != nil { if resp.Redirect != "" { // If we have a redirect, redirect! We use a 307 code // because we don't actually know if its permanent. http.Redirect(w, r, resp.Redirect, 307) return } // Check if this is a raw response if _, ok := resp.Data[logical.HTTPStatusCode]; ok { respondRaw(w, r, resp) return } if resp.WrapInfo != nil && resp.WrapInfo.Token != "" { httpResp = &logical.HTTPResponse{ WrapInfo: &logical.HTTPWrapInfo{ Token: resp.WrapInfo.Token, TTL: int(resp.WrapInfo.TTL.Seconds()), CreationTime: resp.WrapInfo.CreationTime.Format(time.RFC3339Nano), WrappedAccessor: resp.WrapInfo.WrappedAccessor, }, } } else { httpResp = logical.LogicalResponseToHTTPResponse(resp) httpResp.RequestID = req.ID } ret = httpResp if dataOnly { injector := logical.HTTPSysInjector{ Response: httpResp, } ret = injector } } // Respond respondOk(w, ret) return }
func (c *Core) wrapInCubbyhole(req *logical.Request, resp *logical.Response) (*logical.Response, error) { // Before wrapping, obey special rules for listing: if no entries are // found, 404. This prevents unwrapping only to find empty data. if req.Operation == logical.ListOperation { if resp == nil || len(resp.Data) == 0 { return nil, logical.ErrUnsupportedPath } keysRaw, ok := resp.Data["keys"] if !ok || keysRaw == nil { return nil, logical.ErrUnsupportedPath } keys, ok := keysRaw.([]string) if !ok { return nil, logical.ErrUnsupportedPath } if len(keys) == 0 { return nil, logical.ErrUnsupportedPath } } // If we are wrapping, the first part (performed in this functions) happens // before auditing so that resp.WrapInfo.Token can contain the HMAC'd // wrapping token ID in the audit logs, so that it can be determined from // the audit logs whether the token was ever actually used. creationTime := time.Now() te := TokenEntry{ Path: req.Path, Policies: []string{"response-wrapping"}, CreationTime: creationTime.Unix(), TTL: resp.WrapInfo.TTL, NumUses: 1, ExplicitMaxTTL: resp.WrapInfo.TTL, } if err := c.tokenStore.create(&te); err != nil { c.logger.Error("core: failed to create wrapping token", "error", err) return nil, ErrInternalError } resp.WrapInfo.Token = te.ID resp.WrapInfo.CreationTime = creationTime // This will only be non-nil if this response contains a token, so in that // case put the accessor in the wrap info. if resp.Auth != nil { resp.WrapInfo.WrappedAccessor = resp.Auth.Accessor } cubbyReq := &logical.Request{ Operation: logical.CreateOperation, Path: "cubbyhole/response", ClientToken: te.ID, } // During a rewrap, store the original response, don't wrap it again. if req.Path == "sys/wrapping/rewrap" { cubbyReq.Data = map[string]interface{}{ "response": resp.Data["response"], } } else { httpResponse := logical.LogicalResponseToHTTPResponse(resp) // Add the unique identifier of the original request to the response httpResponse.RequestID = req.ID // Because of the way that JSON encodes (likely just in Go) we actually get // mixed-up values for ints if we simply put this object in the response // and encode the whole thing; so instead we marshal it first, then store // the string response. This actually ends up making it easier on the // client side, too, as it becomes a straight read-string-pass-to-unmarshal // operation. marshaledResponse, err := json.Marshal(httpResponse) if err != nil { c.logger.Error("core: failed to marshal wrapped response", "error", err) return nil, ErrInternalError } cubbyReq.Data = map[string]interface{}{ "response": string(marshaledResponse), } } cubbyResp, err := c.router.Route(cubbyReq) if err != nil { // Revoke since it's not yet being tracked for expiration c.tokenStore.Revoke(te.ID) c.logger.Error("core: failed to store wrapped response information", "error", err) return nil, ErrInternalError } if cubbyResp != nil && cubbyResp.IsError() { c.tokenStore.Revoke(te.ID) c.logger.Error("core: failed to store wrapped response information", "error", cubbyResp.Data["error"]) return cubbyResp, nil } // Store info for lookup cubbyReq.Path = "cubbyhole/wrapinfo" cubbyReq.Data = map[string]interface{}{ "creation_ttl": resp.WrapInfo.TTL, "creation_time": creationTime, } cubbyResp, err = c.router.Route(cubbyReq) if err != nil { // Revoke since it's not yet being tracked for expiration c.tokenStore.Revoke(te.ID) c.logger.Error("core: failed to store wrapping information", "error", err) return nil, ErrInternalError } if cubbyResp != nil && cubbyResp.IsError() { c.tokenStore.Revoke(te.ID) c.logger.Error("core: failed to store wrapping information", "error", cubbyResp.Data["error"]) return cubbyResp, nil } auth := &logical.Auth{ ClientToken: te.ID, Policies: []string{"response-wrapping"}, LeaseOptions: logical.LeaseOptions{ TTL: te.TTL, Renewable: false, }, } // Register the wrapped token with the expiration manager if err := c.expiration.RegisterAuth(te.Path, auth); err != nil { // Revoke since it's not yet being tracked for expiration c.tokenStore.Revoke(te.ID) c.logger.Error("core: failed to register cubbyhole wrapping token lease", "request_path", req.Path, "error", err) return nil, ErrInternalError } return nil, nil }