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 }