Skip to content

Commit

Permalink
Merge branch 'master' into wiki_revisions
Browse files Browse the repository at this point in the history
  • Loading branch information
Cherrg committed Jul 7, 2019
2 parents 3331335 + fcda2d5 commit 9208fd3
Show file tree
Hide file tree
Showing 27 changed files with 3,094 additions and 30 deletions.
4 changes: 4 additions & 0 deletions custom/conf/app.ini.sample
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,8 @@ MIN_PASSWORD_LENGTH = 6
IMPORT_LOCAL_PATHS = false
; Set to true to prevent all users (including admin) from creating custom git hooks
DISABLE_GIT_HOOKS = false
; Password Hash algorithm, either "pbkdf2", "argon2", "scrypt" or "bcrypt"
PASSWORD_HASH_ALGO = pbkdf2

[openid]
;
Expand Down Expand Up @@ -668,6 +670,8 @@ SCHEDULE = @every 24h
UPDATE_EXISTING = true

[git]
; The path of git executable. If empty, Gitea searches through the PATH environment.
PATH =
; Disables highlight of added and removed changes
DISABLE_DIFF_HIGHLIGHT = false
; Max number of lines allowed in a single file in diff view
Expand Down
2 changes: 2 additions & 0 deletions docs/content/doc/advanced/config-cheat-sheet.en-us.md
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,7 @@ Values containing `#` or `;` must be quoted using `` ` `` or `"""`.
- `IMPORT_LOCAL_PATHS`: **false**: Set to `false` to prevent all users (including admin) from importing local path on server.
- `INTERNAL_TOKEN`: **\<random at every install if no uri set\>**: Secret used to validate communication within Gitea binary.
- `INTERNAL_TOKEN_URI`: **<empty>**: Instead of defining internal token in the configuration, this configuration option can be used to give Gitea a path to a file that contains the internal token (example value: `file:/etc/gitea/internal_token`)
- `PASSWORD_HASH_ALGO`: **pbkdf2**: The hash algorithm to use \[pbkdf2, argon2, scrypt, bcrypt\].

## OpenID (`openid`)

Expand Down Expand Up @@ -408,6 +409,7 @@ NB: You must `REDIRECT_MACARON_LOG` and have `DISABLE_ROUTER_LOG` set to `false`

## Git (`git`)

- `PATH`: **""**: The path of git executable. If empty, Gitea searches through the PATH environment.
- `MAX_GIT_DIFF_LINES`: **100**: Max number of lines allowed of a single file in diff view.
- `MAX_GIT_DIFF_LINE_CHARACTERS`: **5000**: Max character count per line highlighted in diff view.
- `MAX_GIT_DIFF_FILES`: **100**: Max number of files shown in diff view.
Expand Down
10 changes: 10 additions & 0 deletions models/login_source.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"code.gitea.io/gitea/modules/auth/oauth2"
"code.gitea.io/gitea/modules/auth/pam"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
)

Expand Down Expand Up @@ -665,6 +666,15 @@ func UserSignIn(username, password string) (*User, error) {
switch user.LoginType {
case LoginNoType, LoginPlain, LoginOAuth2:
if user.IsPasswordSet() && user.ValidatePassword(password) {

// Update password hash if server password hash algorithm have changed
if user.PasswdHashAlgo != setting.PasswordHashAlgo {
user.HashPassword(password)
if err := UpdateUserCols(user, "passwd", "passwd_hash_algo"); err != nil {
return nil, err
}
}

// WARN: DON'T check user.IsActive, that will be checked on reqSign so that
// user could be hint to resend confirm email.
if user.ProhibitLogin {
Expand Down
48 changes: 41 additions & 7 deletions models/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,10 @@ import (

"github.com/Unknwon/com"
"github.com/go-xorm/xorm"
"golang.org/x/crypto/argon2"
"golang.org/x/crypto/bcrypt"
"golang.org/x/crypto/pbkdf2"
"golang.org/x/crypto/scrypt"
"golang.org/x/crypto/ssh"
"xorm.io/builder"
"xorm.io/core"
Expand All @@ -50,6 +53,13 @@ const (
UserTypeOrganization
)

const (
algoBcrypt = "bcrypt"
algoScrypt = "scrypt"
algoArgon2 = "argon2"
algoPbkdf2 = "pbkdf2"
)

const syncExternalUsers = "sync_external_users"

var (
Expand Down Expand Up @@ -82,6 +92,7 @@ type User struct {
Email string `xorm:"NOT NULL"`
KeepEmailPrivate bool
Passwd string `xorm:"NOT NULL"`
PasswdHashAlgo string `xorm:"NOT NULL DEFAULT 'pbkdf2'"`

// MustChangePassword is an attribute that determines if a user
// is to change his/her password after registration.
Expand Down Expand Up @@ -430,25 +441,48 @@ func (u *User) NewGitSig() *git.Signature {
}
}

func hashPassword(passwd, salt string) string {
tempPasswd := pbkdf2.Key([]byte(passwd), []byte(salt), 10000, 50, sha256.New)
func hashPassword(passwd, salt, algo string) string {
var tempPasswd []byte

switch algo {
case algoBcrypt:
tempPasswd, _ = bcrypt.GenerateFromPassword([]byte(passwd), bcrypt.DefaultCost)
return string(tempPasswd)
case algoScrypt:
tempPasswd, _ = scrypt.Key([]byte(passwd), []byte(salt), 65536, 16, 2, 50)
case algoArgon2:
tempPasswd = argon2.IDKey([]byte(passwd), []byte(salt), 2, 65536, 8, 50)
case algoPbkdf2:
fallthrough
default:
tempPasswd = pbkdf2.Key([]byte(passwd), []byte(salt), 10000, 50, sha256.New)
}

return fmt.Sprintf("%x", tempPasswd)
}

// HashPassword hashes a password using PBKDF.
// HashPassword hashes a password using the algorithm defined in the config value of PASSWORD_HASH_ALGO.
func (u *User) HashPassword(passwd string) {
u.Passwd = hashPassword(passwd, u.Salt)
u.PasswdHashAlgo = setting.PasswordHashAlgo
u.Passwd = hashPassword(passwd, u.Salt, setting.PasswordHashAlgo)
}

// ValidatePassword checks if given password matches the one belongs to the user.
func (u *User) ValidatePassword(passwd string) bool {
tempHash := hashPassword(passwd, u.Salt)
return subtle.ConstantTimeCompare([]byte(u.Passwd), []byte(tempHash)) == 1
tempHash := hashPassword(passwd, u.Salt, u.PasswdHashAlgo)

if u.PasswdHashAlgo != algoBcrypt && subtle.ConstantTimeCompare([]byte(u.Passwd), []byte(tempHash)) == 1 {
return true
}
if u.PasswdHashAlgo == algoBcrypt && bcrypt.CompareHashAndPassword([]byte(u.Passwd), []byte(passwd)) == nil {
return true
}
return false
}

// IsPasswordSet checks if the password is set or left empty
func (u *User) IsPasswordSet() bool {
return !u.ValidatePassword("")
return len(u.Passwd) > 0
}

// UploadAvatar saves custom avatar for user.
Expand Down
38 changes: 23 additions & 15 deletions models/user_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -147,21 +147,29 @@ func TestHashPasswordDeterministic(t *testing.T) {
b := make([]byte, 16)
rand.Read(b)
u := &User{Salt: string(b)}
for i := 0; i < 50; i++ {
// generate a random password
rand.Read(b)
pass := string(b)

// save the current password in the user - hash it and store the result
u.HashPassword(pass)
r1 := u.Passwd

// run again
u.HashPassword(pass)
r2 := u.Passwd

// assert equal (given the same salt+pass, the same result is produced)
assert.Equal(t, r1, r2)
algos := []string{"pbkdf2", "argon2", "scrypt", "bcrypt"}
for j := 0; j < len(algos); j++ {
u.PasswdHashAlgo = algos[j]
for i := 0; i < 50; i++ {
// generate a random password
rand.Read(b)
pass := string(b)

// save the current password in the user - hash it and store the result
u.HashPassword(pass)
r1 := u.Passwd

// run again
u.HashPassword(pass)
r2 := u.Passwd

// assert equal (given the same salt+pass, the same result is produced) except bcrypt
if u.PasswdHashAlgo == "bcrypt" {
assert.NotEqual(t, r1, r2)
} else {
assert.Equal(t, r1, r2)
}
}
}
}

Expand Down
15 changes: 11 additions & 4 deletions modules/git/git.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,20 +77,27 @@ func BinVersion() (string, error) {
return gitVersion, nil
}

func init() {
// SetExecutablePath changes the path of git executable and checks the file permission and version.
func SetExecutablePath(path string) error {
// If path is empty, we use the default value of GitExecutable "git" to search for the location of git.
if path != "" {
GitExecutable = path
}
absPath, err := exec.LookPath(GitExecutable)
if err != nil {
panic(fmt.Sprintf("Git not found: %v", err))
return fmt.Errorf("Git not found: %v", err)
}
GitExecutable = absPath

gitVersion, err := BinVersion()
if err != nil {
panic(fmt.Sprintf("Git version missing: %v", err))
return fmt.Errorf("Git version missing: %v", err)
}
if version.Compare(gitVersion, GitVersionRequired, "<") {
panic(fmt.Sprintf("Git version not supported. Requires version > %v", GitVersionRequired))
return fmt.Errorf("Git version not supported. Requires version > %v", GitVersionRequired)
}

return nil
}

// Init initializes git module
Expand Down
4 changes: 4 additions & 0 deletions modules/setting/git.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
var (
// Git settings
Git = struct {
Path string
DisableDiffHighlight bool
MaxGitDiffLines int
MaxGitDiffLineCharacters int
Expand Down Expand Up @@ -59,6 +60,9 @@ func newGit() {
if err := Cfg.Section("git").MapTo(&Git); err != nil {
log.Fatal("Failed to map Git settings: %v", err)
}
if err := git.SetExecutablePath(Git.Path); err != nil {
log.Fatal("Failed to initialize Git settings", err)
}
git.DefaultCommandExecutionTimeout = time.Duration(Git.Timeout.Default) * time.Second

binVersion, err := git.BinVersion()
Expand Down
2 changes: 2 additions & 0 deletions modules/setting/setting.go
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@ var (
MinPasswordLength int
ImportLocalPaths bool
DisableGitHooks bool
PasswordHashAlgo string

// Database settings
UseSQLite3 bool
Expand Down Expand Up @@ -779,6 +780,7 @@ func NewContext() {
MinPasswordLength = sec.Key("MIN_PASSWORD_LENGTH").MustInt(6)
ImportLocalPaths = sec.Key("IMPORT_LOCAL_PATHS").MustBool(false)
DisableGitHooks = sec.Key("DISABLE_GIT_HOOKS").MustBool(false)
PasswordHashAlgo = sec.Key("PASSWORD_HASH_ALGO").MustString("pbkdf2")
InternalToken = loadInternalToken(sec)
IterateBufferSize = Cfg.Section("database").Key("ITERATE_BUFFER_SIZE").MustInt(50)
LogSQL = Cfg.Section("database").Key("LOG_SQL").MustBool(true)
Expand Down
Loading

0 comments on commit 9208fd3

Please sign in to comment.