2014-01-09 06:42:05 +00:00
|
|
|
package models
|
2013-12-12 06:27:43 +00:00
|
|
|
|
2014-03-25 03:31:33 +00:00
|
|
|
import (
|
2016-01-13 02:46:17 +00:00
|
|
|
"crypto/rand"
|
2019-06-04 03:04:54 +00:00
|
|
|
"crypto/tls"
|
|
|
|
"crypto/x509"
|
2019-12-16 02:27:21 +00:00
|
|
|
"fmt"
|
|
|
|
"io"
|
2019-06-04 03:04:54 +00:00
|
|
|
"io/ioutil"
|
2020-06-25 13:31:28 +00:00
|
|
|
"os"
|
2019-12-16 02:27:21 +00:00
|
|
|
"time"
|
2014-01-13 03:36:26 +00:00
|
|
|
|
2016-01-19 03:13:32 +00:00
|
|
|
"bitbucket.org/liamstask/goose/lib/goose"
|
|
|
|
|
2019-06-04 03:04:54 +00:00
|
|
|
mysql "github.com/go-sql-driver/mysql"
|
2020-06-20 03:03:51 +00:00
|
|
|
"github.com/gophish/gophish/auth"
|
2016-01-10 17:03:17 +00:00
|
|
|
"github.com/gophish/gophish/config"
|
2020-06-20 03:03:51 +00:00
|
|
|
|
2018-05-04 00:07:41 +00:00
|
|
|
log "github.com/gophish/gophish/logger"
|
2016-01-13 02:46:17 +00:00
|
|
|
"github.com/jinzhu/gorm"
|
2024-01-02 21:23:28 +00:00
|
|
|
_ "modernc.org/sqlite" // Blank import needed to import sqlite3
|
2014-03-25 03:31:33 +00:00
|
|
|
)
|
2014-01-13 03:36:26 +00:00
|
|
|
|
2016-03-09 04:37:55 +00:00
|
|
|
var db *gorm.DB
|
2018-12-15 21:42:32 +00:00
|
|
|
var conf *config.Config
|
2015-02-07 02:24:10 +00:00
|
|
|
|
2019-03-28 03:48:31 +00:00
|
|
|
const MaxDatabaseConnectionAttempts int = 10
|
|
|
|
|
2020-06-20 03:03:51 +00:00
|
|
|
// DefaultAdminUsername is the default username for the administrative user
|
|
|
|
const DefaultAdminUsername = "admin"
|
|
|
|
|
2020-06-25 13:31:28 +00:00
|
|
|
// InitialAdminPassword is the environment variable that specifies which
|
|
|
|
// password to use for the initial root login instead of generating one
|
|
|
|
// randomly
|
|
|
|
const InitialAdminPassword = "GOPHISH_INITIAL_ADMIN_PASSWORD"
|
|
|
|
|
2020-07-02 03:06:31 +00:00
|
|
|
// InitialAdminApiToken is the environment variable that specifies the
|
|
|
|
// API token to seed the initial root login instead of generating one
|
|
|
|
// randomly
|
|
|
|
const InitialAdminApiToken = "GOPHISH_INITIAL_ADMIN_API_TOKEN"
|
|
|
|
|
2014-03-28 04:31:51 +00:00
|
|
|
const (
|
2018-12-16 03:38:51 +00:00
|
|
|
CampaignInProgress string = "In progress"
|
|
|
|
CampaignQueued string = "Queued"
|
|
|
|
CampaignCreated string = "Created"
|
|
|
|
CampaignEmailsSent string = "Emails Sent"
|
|
|
|
CampaignComplete string = "Completed"
|
|
|
|
EventSent string = "Email Sent"
|
|
|
|
EventSendingError string = "Error Sending Email"
|
|
|
|
EventOpened string = "Email Opened"
|
|
|
|
EventClicked string = "Clicked Link"
|
|
|
|
EventDataSubmit string = "Submitted Data"
|
|
|
|
EventReported string = "Email Reported"
|
|
|
|
EventProxyRequest string = "Proxied request"
|
|
|
|
StatusSuccess string = "Success"
|
|
|
|
StatusQueued string = "Queued"
|
|
|
|
StatusSending string = "Sending"
|
|
|
|
StatusUnknown string = "Unknown"
|
|
|
|
StatusScheduled string = "Scheduled"
|
|
|
|
StatusRetry string = "Retrying"
|
|
|
|
Error string = "Error"
|
2014-03-28 04:31:51 +00:00
|
|
|
)
|
|
|
|
|
2014-03-26 19:50:16 +00:00
|
|
|
// Flash is used to hold flash information for use in templates.
|
|
|
|
type Flash struct {
|
|
|
|
Type string
|
|
|
|
Message string
|
|
|
|
}
|
|
|
|
|
2015-02-07 02:24:10 +00:00
|
|
|
// Response contains the attributes found in an API response
|
2014-06-02 03:30:23 +00:00
|
|
|
type Response struct {
|
2014-06-02 04:38:21 +00:00
|
|
|
Message string `json:"message"`
|
|
|
|
Success bool `json:"success"`
|
|
|
|
Data interface{} `json:"data"`
|
2014-06-02 03:30:23 +00:00
|
|
|
}
|
|
|
|
|
2016-01-13 02:46:17 +00:00
|
|
|
// Copy of auth.GenerateSecureKey to prevent cyclic import with auth library
|
|
|
|
func generateSecureKey() string {
|
|
|
|
k := make([]byte, 32)
|
|
|
|
io.ReadFull(rand.Reader, k)
|
|
|
|
return fmt.Sprintf("%x", k)
|
|
|
|
}
|
|
|
|
|
2016-11-19 16:37:22 +00:00
|
|
|
func chooseDBDriver(name, openStr string) goose.DBDriver {
|
|
|
|
d := goose.DBDriver{Name: name, OpenStr: openStr}
|
|
|
|
|
|
|
|
switch name {
|
|
|
|
case "mysql":
|
|
|
|
d.Import = "github.com/go-sql-driver/mysql"
|
|
|
|
d.Dialect = &goose.MySqlDialect{}
|
|
|
|
|
|
|
|
// Default database is sqlite3
|
|
|
|
default:
|
2024-01-02 21:23:28 +00:00
|
|
|
d.Import = "modernc.org/sqlite"
|
2016-11-19 16:37:22 +00:00
|
|
|
d.Dialect = &goose.Sqlite3Dialect{}
|
|
|
|
}
|
|
|
|
|
|
|
|
return d
|
|
|
|
}
|
|
|
|
|
2020-06-20 03:03:51 +00:00
|
|
|
func createTemporaryPassword(u *User) error {
|
2020-06-25 13:31:28 +00:00
|
|
|
var temporaryPassword string
|
|
|
|
if envPassword := os.Getenv(InitialAdminPassword); envPassword != "" {
|
|
|
|
temporaryPassword = envPassword
|
|
|
|
} else {
|
|
|
|
// This will result in a 16 character password which could be viewed as an
|
|
|
|
// inconvenience, but it should be ok for now.
|
|
|
|
temporaryPassword = auth.GenerateSecureKey(auth.MinPasswordLength)
|
|
|
|
}
|
2020-06-20 03:03:51 +00:00
|
|
|
hash, err := auth.GeneratePasswordHash(temporaryPassword)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
u.Hash = hash
|
|
|
|
// Anytime a temporary password is created, we will force the user
|
|
|
|
// to change their password
|
|
|
|
u.PasswordChangeRequired = true
|
|
|
|
err = db.Save(u).Error
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
log.Infof("Please login with the username admin and the password %s", temporaryPassword)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Setup initializes the database and runs any needed migrations.
|
|
|
|
//
|
|
|
|
// First, it establishes a connection to the database, then runs any migrations
|
|
|
|
// newer than the version the database is on.
|
|
|
|
//
|
|
|
|
// Once the database is up-to-date, we create an admin user (if needed) that
|
|
|
|
// has a randomly generated API key and password.
|
2018-12-15 21:42:32 +00:00
|
|
|
func Setup(c *config.Config) error {
|
|
|
|
// Setup the package-scoped config
|
|
|
|
conf = c
|
2016-01-19 03:13:32 +00:00
|
|
|
// Setup the goose configuration
|
|
|
|
migrateConf := &goose.DBConf{
|
2018-12-15 21:42:32 +00:00
|
|
|
MigrationsDir: conf.MigrationsPath,
|
2016-01-19 03:13:32 +00:00
|
|
|
Env: "production",
|
2018-12-15 21:42:32 +00:00
|
|
|
Driver: chooseDBDriver(conf.DBName, conf.DBPath),
|
2016-01-19 03:13:32 +00:00
|
|
|
}
|
|
|
|
// Get the latest possible migration
|
|
|
|
latest, err := goose.GetMostRecentDBVersion(migrateConf.MigrationsDir)
|
|
|
|
if err != nil {
|
2018-05-04 00:07:41 +00:00
|
|
|
log.Error(err)
|
2016-01-19 03:13:32 +00:00
|
|
|
return err
|
|
|
|
}
|
2019-06-04 03:04:54 +00:00
|
|
|
|
|
|
|
// Register certificates for tls encrypted db connections
|
|
|
|
if conf.DBSSLCaPath != "" {
|
|
|
|
switch conf.DBName {
|
|
|
|
case "mysql":
|
|
|
|
rootCertPool := x509.NewCertPool()
|
|
|
|
pem, err := ioutil.ReadFile(conf.DBSSLCaPath)
|
|
|
|
if err != nil {
|
|
|
|
log.Error(err)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if ok := rootCertPool.AppendCertsFromPEM(pem); !ok {
|
|
|
|
log.Error("Failed to append PEM.")
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
mysql.RegisterTLSConfig("ssl_ca", &tls.Config{
|
|
|
|
RootCAs: rootCertPool,
|
|
|
|
})
|
|
|
|
// Default database is sqlite3, which supports no tls, as connection
|
|
|
|
// is file based
|
|
|
|
default:
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-01-19 03:13:32 +00:00
|
|
|
// Open our database connection
|
2019-03-28 03:48:31 +00:00
|
|
|
i := 0
|
|
|
|
for {
|
|
|
|
db, err = gorm.Open(conf.DBName, conf.DBPath)
|
|
|
|
if err == nil {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
if err != nil && i >= MaxDatabaseConnectionAttempts {
|
|
|
|
log.Error(err)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
i += 1
|
|
|
|
log.Warn("waiting for database to be up...")
|
|
|
|
time.Sleep(5 * time.Second)
|
|
|
|
}
|
2014-03-28 04:31:51 +00:00
|
|
|
db.LogMode(false)
|
2018-05-04 00:07:41 +00:00
|
|
|
db.SetLogger(log.Logger)
|
2016-08-03 04:28:22 +00:00
|
|
|
db.DB().SetMaxOpenConns(1)
|
2014-03-26 04:53:51 +00:00
|
|
|
if err != nil {
|
2018-05-04 00:07:41 +00:00
|
|
|
log.Error(err)
|
2014-03-26 19:50:16 +00:00
|
|
|
return err
|
2014-03-26 04:53:51 +00:00
|
|
|
}
|
2016-01-19 03:13:32 +00:00
|
|
|
// Migrate up to the latest version
|
|
|
|
err = goose.RunMigrationsOnDb(migrateConf, migrateConf.MigrationsDir, latest, db.DB())
|
|
|
|
if err != nil {
|
2018-05-04 00:07:41 +00:00
|
|
|
log.Error(err)
|
2016-01-19 03:13:32 +00:00
|
|
|
return err
|
|
|
|
}
|
2018-02-09 01:04:59 +00:00
|
|
|
// Create the admin user if it doesn't exist
|
|
|
|
var userCount int64
|
2020-06-20 03:03:51 +00:00
|
|
|
var adminUser User
|
2018-02-09 01:04:59 +00:00
|
|
|
db.Model(&User{}).Count(&userCount)
|
2019-02-20 02:33:50 +00:00
|
|
|
adminRole, err := GetRoleBySlug(RoleAdmin)
|
|
|
|
if err != nil {
|
|
|
|
log.Error(err)
|
|
|
|
return err
|
|
|
|
}
|
2018-02-09 01:04:59 +00:00
|
|
|
if userCount == 0 {
|
2020-06-20 03:03:51 +00:00
|
|
|
adminUser := User{
|
|
|
|
Username: DefaultAdminUsername,
|
|
|
|
Role: adminRole,
|
|
|
|
RoleID: adminRole.ID,
|
|
|
|
PasswordChangeRequired: true,
|
2014-03-26 04:53:51 +00:00
|
|
|
}
|
2020-07-02 03:06:31 +00:00
|
|
|
|
|
|
|
if envToken := os.Getenv(InitialAdminApiToken); envToken != "" {
|
|
|
|
adminUser.ApiKey = envToken
|
|
|
|
} else {
|
|
|
|
adminUser.ApiKey = auth.GenerateSecureKey(auth.APIKeyLength)
|
|
|
|
}
|
|
|
|
|
2020-06-20 03:03:51 +00:00
|
|
|
err = db.Save(&adminUser).Error
|
|
|
|
if err != nil {
|
|
|
|
log.Error(err)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// If this is the first time the user is installing Gophish, then we will
|
|
|
|
// generate a temporary password for the admin user.
|
|
|
|
//
|
|
|
|
// We do this here instead of in the block above where the admin is created
|
|
|
|
// since there's the chance the user executes Gophish and has some kind of
|
|
|
|
// error, then tries restarting it. If they didn't grab the password out of
|
|
|
|
// the logs, then they would have lost it.
|
|
|
|
//
|
|
|
|
// By doing the temporary password here, we will regenerate that temporary
|
|
|
|
// password until the user is able to reset the admin password.
|
|
|
|
if adminUser.Username == "" {
|
|
|
|
adminUser, err = GetUserByUsername(DefaultAdminUsername)
|
|
|
|
if err != nil {
|
|
|
|
log.Error(err)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if adminUser.PasswordChangeRequired {
|
|
|
|
err = createTemporaryPassword(&adminUser)
|
2014-03-26 04:53:51 +00:00
|
|
|
if err != nil {
|
2018-05-04 00:07:41 +00:00
|
|
|
log.Error(err)
|
2016-01-19 03:13:32 +00:00
|
|
|
return err
|
2014-03-26 04:53:51 +00:00
|
|
|
}
|
2014-03-25 03:31:33 +00:00
|
|
|
}
|
2014-03-25 03:38:59 +00:00
|
|
|
return nil
|
2013-12-12 07:00:22 +00:00
|
|
|
}
|