github.com/gophish/gophish@v0.12.2-0.20230915144530-8e7929441393/models/models.go (about)

     1  package models
     2  
     3  import (
     4  	"crypto/rand"
     5  	"crypto/tls"
     6  	"crypto/x509"
     7  	"fmt"
     8  	"io"
     9  	"io/ioutil"
    10  	"os"
    11  	"time"
    12  
    13  	"bitbucket.org/liamstask/goose/lib/goose"
    14  
    15  	mysql "github.com/go-sql-driver/mysql"
    16  	"github.com/gophish/gophish/auth"
    17  	"github.com/gophish/gophish/config"
    18  
    19  	log "github.com/gophish/gophish/logger"
    20  	"github.com/jinzhu/gorm"
    21  	_ "github.com/mattn/go-sqlite3" // Blank import needed to import sqlite3
    22  )
    23  
    24  var db *gorm.DB
    25  var conf *config.Config
    26  
    27  const MaxDatabaseConnectionAttempts int = 10
    28  
    29  // DefaultAdminUsername is the default username for the administrative user
    30  const DefaultAdminUsername = "admin"
    31  
    32  // InitialAdminPassword is the environment variable that specifies which
    33  // password to use for the initial root login instead of generating one
    34  // randomly
    35  const InitialAdminPassword = "GOPHISH_INITIAL_ADMIN_PASSWORD"
    36  
    37  // InitialAdminApiToken is the environment variable that specifies the
    38  // API token to seed the initial root login instead of generating one
    39  // randomly
    40  const InitialAdminApiToken = "GOPHISH_INITIAL_ADMIN_API_TOKEN"
    41  
    42  const (
    43  	CampaignInProgress string = "In progress"
    44  	CampaignQueued     string = "Queued"
    45  	CampaignCreated    string = "Created"
    46  	CampaignEmailsSent string = "Emails Sent"
    47  	CampaignComplete   string = "Completed"
    48  	EventSent          string = "Email Sent"
    49  	EventSendingError  string = "Error Sending Email"
    50  	EventOpened        string = "Email Opened"
    51  	EventClicked       string = "Clicked Link"
    52  	EventDataSubmit    string = "Submitted Data"
    53  	EventReported      string = "Email Reported"
    54  	EventProxyRequest  string = "Proxied request"
    55  	StatusSuccess      string = "Success"
    56  	StatusQueued       string = "Queued"
    57  	StatusSending      string = "Sending"
    58  	StatusUnknown      string = "Unknown"
    59  	StatusScheduled    string = "Scheduled"
    60  	StatusRetry        string = "Retrying"
    61  	Error              string = "Error"
    62  )
    63  
    64  // Flash is used to hold flash information for use in templates.
    65  type Flash struct {
    66  	Type    string
    67  	Message string
    68  }
    69  
    70  // Response contains the attributes found in an API response
    71  type Response struct {
    72  	Message string      `json:"message"`
    73  	Success bool        `json:"success"`
    74  	Data    interface{} `json:"data"`
    75  }
    76  
    77  // Copy of auth.GenerateSecureKey to prevent cyclic import with auth library
    78  func generateSecureKey() string {
    79  	k := make([]byte, 32)
    80  	io.ReadFull(rand.Reader, k)
    81  	return fmt.Sprintf("%x", k)
    82  }
    83  
    84  func chooseDBDriver(name, openStr string) goose.DBDriver {
    85  	d := goose.DBDriver{Name: name, OpenStr: openStr}
    86  
    87  	switch name {
    88  	case "mysql":
    89  		d.Import = "github.com/go-sql-driver/mysql"
    90  		d.Dialect = &goose.MySqlDialect{}
    91  
    92  	// Default database is sqlite3
    93  	default:
    94  		d.Import = "github.com/mattn/go-sqlite3"
    95  		d.Dialect = &goose.Sqlite3Dialect{}
    96  	}
    97  
    98  	return d
    99  }
   100  
   101  func createTemporaryPassword(u *User) error {
   102  	var temporaryPassword string
   103  	if envPassword := os.Getenv(InitialAdminPassword); envPassword != "" {
   104  		temporaryPassword = envPassword
   105  	} else {
   106  		// This will result in a 16 character password which could be viewed as an
   107  		// inconvenience, but it should be ok for now.
   108  		temporaryPassword = auth.GenerateSecureKey(auth.MinPasswordLength)
   109  	}
   110  	hash, err := auth.GeneratePasswordHash(temporaryPassword)
   111  	if err != nil {
   112  		return err
   113  	}
   114  	u.Hash = hash
   115  	// Anytime a temporary password is created, we will force the user
   116  	// to change their password
   117  	u.PasswordChangeRequired = true
   118  	err = db.Save(u).Error
   119  	if err != nil {
   120  		return err
   121  	}
   122  	log.Infof("Please login with the username admin and the password %s", temporaryPassword)
   123  	return nil
   124  }
   125  
   126  // Setup initializes the database and runs any needed migrations.
   127  //
   128  // First, it establishes a connection to the database, then runs any migrations
   129  // newer than the version the database is on.
   130  //
   131  // Once the database is up-to-date, we create an admin user (if needed) that
   132  // has a randomly generated API key and password.
   133  func Setup(c *config.Config) error {
   134  	// Setup the package-scoped config
   135  	conf = c
   136  	// Setup the goose configuration
   137  	migrateConf := &goose.DBConf{
   138  		MigrationsDir: conf.MigrationsPath,
   139  		Env:           "production",
   140  		Driver:        chooseDBDriver(conf.DBName, conf.DBPath),
   141  	}
   142  	// Get the latest possible migration
   143  	latest, err := goose.GetMostRecentDBVersion(migrateConf.MigrationsDir)
   144  	if err != nil {
   145  		log.Error(err)
   146  		return err
   147  	}
   148  
   149  	// Register certificates for tls encrypted db connections
   150  	if conf.DBSSLCaPath != "" {
   151  		switch conf.DBName {
   152  		case "mysql":
   153  			rootCertPool := x509.NewCertPool()
   154  			pem, err := ioutil.ReadFile(conf.DBSSLCaPath)
   155  			if err != nil {
   156  				log.Error(err)
   157  				return err
   158  			}
   159  			if ok := rootCertPool.AppendCertsFromPEM(pem); !ok {
   160  				log.Error("Failed to append PEM.")
   161  				return err
   162  			}
   163  			mysql.RegisterTLSConfig("ssl_ca", &tls.Config{
   164  				RootCAs: rootCertPool,
   165  			})
   166  			// Default database is sqlite3, which supports no tls, as connection
   167  			// is file based
   168  		default:
   169  		}
   170  	}
   171  
   172  	// Open our database connection
   173  	i := 0
   174  	for {
   175  		db, err = gorm.Open(conf.DBName, conf.DBPath)
   176  		if err == nil {
   177  			break
   178  		}
   179  		if err != nil && i >= MaxDatabaseConnectionAttempts {
   180  			log.Error(err)
   181  			return err
   182  		}
   183  		i += 1
   184  		log.Warn("waiting for database to be up...")
   185  		time.Sleep(5 * time.Second)
   186  	}
   187  	db.LogMode(false)
   188  	db.SetLogger(log.Logger)
   189  	db.DB().SetMaxOpenConns(1)
   190  	if err != nil {
   191  		log.Error(err)
   192  		return err
   193  	}
   194  	// Migrate up to the latest version
   195  	err = goose.RunMigrationsOnDb(migrateConf, migrateConf.MigrationsDir, latest, db.DB())
   196  	if err != nil {
   197  		log.Error(err)
   198  		return err
   199  	}
   200  	// Create the admin user if it doesn't exist
   201  	var userCount int64
   202  	var adminUser User
   203  	db.Model(&User{}).Count(&userCount)
   204  	adminRole, err := GetRoleBySlug(RoleAdmin)
   205  	if err != nil {
   206  		log.Error(err)
   207  		return err
   208  	}
   209  	if userCount == 0 {
   210  		adminUser := User{
   211  			Username:               DefaultAdminUsername,
   212  			Role:                   adminRole,
   213  			RoleID:                 adminRole.ID,
   214  			PasswordChangeRequired: true,
   215  		}
   216  
   217  		if envToken := os.Getenv(InitialAdminApiToken); envToken != "" {
   218  			adminUser.ApiKey = envToken
   219  		} else {
   220  			adminUser.ApiKey = auth.GenerateSecureKey(auth.APIKeyLength)
   221  		}
   222  
   223  		err = db.Save(&adminUser).Error
   224  		if err != nil {
   225  			log.Error(err)
   226  			return err
   227  		}
   228  	}
   229  	// If this is the first time the user is installing Gophish, then we will
   230  	// generate a temporary password for the admin user.
   231  	//
   232  	// We do this here instead of in the block above where the admin is created
   233  	// since there's the chance the user executes Gophish and has some kind of
   234  	// error, then tries restarting it. If they didn't grab the password out of
   235  	// the logs, then they would have lost it.
   236  	//
   237  	// By doing the temporary password here, we will regenerate that temporary
   238  	// password until the user is able to reset the admin password.
   239  	if adminUser.Username == "" {
   240  		adminUser, err = GetUserByUsername(DefaultAdminUsername)
   241  		if err != nil {
   242  			log.Error(err)
   243  			return err
   244  		}
   245  	}
   246  	if adminUser.PasswordChangeRequired {
   247  		err = createTemporaryPassword(&adminUser)
   248  		if err != nil {
   249  			log.Error(err)
   250  			return err
   251  		}
   252  	}
   253  	return nil
   254  }