open-vault/builtin/logical/mssql/secret_creds.go

183 lines
4.4 KiB
Go
Raw Normal View History

2016-03-03 14:19:17 +00:00
package mssql
import (
"context"
"database/sql"
2016-03-03 14:19:17 +00:00
"fmt"
"github.com/hashicorp/vault/logical"
"github.com/hashicorp/vault/logical/framework"
)
const SecretCredsType = "creds"
func secretCreds(b *backend) *framework.Secret {
return &framework.Secret{
Type: SecretCredsType,
Fields: map[string]*framework.FieldSchema{
"username": &framework.FieldSchema{
Type: framework.TypeString,
Description: "Username",
},
"password": &framework.FieldSchema{
Type: framework.TypeString,
Description: "Password",
},
},
Renew: b.secretCredsRenew,
Revoke: b.secretCredsRevoke,
}
}
func (b *backend) secretCredsRenew(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
2016-03-03 14:19:17 +00:00
// Get the lease information
leaseConfig, err := b.LeaseConfig(req.Storage)
2016-03-03 14:19:17 +00:00
if err != nil {
return nil, err
}
if leaseConfig == nil {
leaseConfig = &configLease{}
2016-03-03 14:19:17 +00:00
}
f := framework.LeaseExtend(leaseConfig.TTL, leaseConfig.TTLMax, b.System())
return f(ctx, req, d)
2016-03-03 14:19:17 +00:00
}
func (b *backend) secretCredsRevoke(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
2016-03-03 14:19:17 +00:00
// Get the username from the internal data
usernameRaw, ok := req.Secret.InternalData["username"]
if !ok {
return nil, fmt.Errorf("secret is missing username internal data")
}
username, ok := usernameRaw.(string)
// Get our connection
db, err := b.DB(req.Storage)
if err != nil {
return nil, err
}
// First disable server login
disableStmt, err := db.Prepare(fmt.Sprintf("ALTER LOGIN [%s] DISABLE;", username))
2016-03-03 14:19:17 +00:00
if err != nil {
return nil, err
}
defer disableStmt.Close()
if _, err := disableStmt.Exec(); err != nil {
2016-03-03 14:19:17 +00:00
return nil, err
}
// Query for sessions for the login so that we can kill any outstanding
// sessions. There cannot be any active sessions before we drop the logins
// This isn't done in a transaction because even if we fail along the way,
// we want to remove as much access as possible
sessionStmt, err := db.Prepare(fmt.Sprintf(
2016-03-03 14:19:17 +00:00
"SELECT session_id FROM sys.dm_exec_sessions WHERE login_name = '%s';", username))
if err != nil {
return nil, err
}
defer sessionStmt.Close()
2016-03-03 14:19:17 +00:00
sessionRows, err := sessionStmt.Query()
2016-03-03 14:19:17 +00:00
if err != nil {
return nil, err
}
defer sessionRows.Close()
2016-03-03 14:19:17 +00:00
var revokeStmts []string
for sessionRows.Next() {
2016-03-03 14:19:17 +00:00
var sessionID int
err = sessionRows.Scan(&sessionID)
2016-03-03 14:19:17 +00:00
if err != nil {
return nil, err
2016-03-03 14:19:17 +00:00
}
revokeStmts = append(revokeStmts, fmt.Sprintf("KILL %d;", sessionID))
}
// Query for database users using undocumented stored procedure for now since
// it is the easiest way to get this information;
// we need to drop the database users before we can drop the login and the role
// This isn't done in a transaction because even if we fail along the way,
// we want to remove as much access as possible
stmt, err := db.Prepare(fmt.Sprintf("EXEC master.dbo.sp_msloginmappings '%s';", username))
2016-03-03 14:19:17 +00:00
if err != nil {
return nil, err
}
defer stmt.Close()
rows, err := stmt.Query()
2016-03-03 14:19:17 +00:00
if err != nil {
return nil, err
}
defer rows.Close()
for rows.Next() {
var loginName, dbName, qUsername string
var aliasName sql.NullString
2016-03-03 14:19:17 +00:00
err = rows.Scan(&loginName, &dbName, &qUsername, &aliasName)
if err != nil {
return nil, err
2016-03-03 14:19:17 +00:00
}
revokeStmts = append(revokeStmts, fmt.Sprintf(dropUserSQL, dbName, username, username))
2016-03-03 14:19:17 +00:00
}
// we do not stop on error, as we want to remove as
// many permissions as possible right now
var lastStmtError error
for _, query := range revokeStmts {
stmt, err := db.Prepare(query)
if err != nil {
lastStmtError = err
continue
}
2016-06-29 21:39:47 +00:00
defer stmt.Close()
2016-03-03 14:19:17 +00:00
_, err = stmt.Exec()
if err != nil {
lastStmtError = err
}
}
// can't drop if not all database users are dropped
if rows.Err() != nil {
return nil, fmt.Errorf("cound not generate sql statements for all rows: %s", rows.Err())
2016-03-03 14:19:17 +00:00
}
if lastStmtError != nil {
return nil, fmt.Errorf("could not perform all sql statements: %s", lastStmtError)
2016-03-03 14:19:17 +00:00
}
// Drop this login
stmt, err = db.Prepare(fmt.Sprintf(dropLoginSQL, username, username))
if err != nil {
return nil, err
}
defer stmt.Close()
if _, err := stmt.Exec(); err != nil {
return nil, err
}
return nil, nil
}
const dropUserSQL = `
USE [%s]
IF EXISTS
(SELECT name
FROM sys.database_principals
WHERE name = N'%s')
BEGIN
DROP USER [%s]
END
`
2016-03-03 14:19:17 +00:00
const dropLoginSQL = `
IF EXISTS
(SELECT name
FROM master.sys.server_principals
WHERE name = N'%s')
2016-03-03 14:19:17 +00:00
BEGIN
DROP LOGIN [%s]
2016-03-03 14:19:17 +00:00
END
`