Beispiel #1
0
func addKey(e Engine, key *PublicKey) (err error) {
	// Calculate fingerprint.
	tmpPath := strings.Replace(path.Join(os.TempDir(), fmt.Sprintf("%d", time.Now().Nanosecond()),
		"id_rsa.pub"), "\\", "/", -1)
	os.MkdirAll(path.Dir(tmpPath), os.ModePerm)
	if err = ioutil.WriteFile(tmpPath, []byte(key.Content), 0644); err != nil {
		return err
	}
	stdout, stderr, err := process.Exec("AddPublicKey", "ssh-keygen", "-lf", tmpPath)
	if err != nil {
		return fmt.Errorf("'ssh-keygen -lf %s' failed with error '%s': %s", tmpPath, err, stderr)
	} else if len(stdout) < 2 {
		return errors.New("not enough output for calculating fingerprint: " + stdout)
	}
	key.Fingerprint = strings.Split(stdout, " ")[1]

	// Save SSH key.
	if _, err = e.Insert(key); err != nil {
		return err
	}

	// Don't need to rewrite this file if builtin SSH server is enabled.
	if setting.SSH.StartBuiltinServer {
		return nil
	}
	return appendAuthorizedKeysToFile(key)
}
Beispiel #2
0
// SSHKeyGenParsePublicKey extracts key type and length using ssh-keygen.
func SSHKeyGenParsePublicKey(key string) (string, int, error) {
	// The ssh-keygen in Windows does not print key type, so no need go further.
	if setting.IsWindows {
		return "", 0, nil
	}

	tmpName, err := writeTmpKeyFile(key)
	if err != nil {
		return "", 0, fmt.Errorf("writeTmpKeyFile: %v", err)
	}
	defer os.Remove(tmpName)

	stdout, stderr, err := process.Exec("SSHKeyGenParsePublicKey", setting.SSH.KeygenPath, "-lf", tmpName)
	if err != nil {
		return "", 0, fmt.Errorf("fail to parse public key: %s - %s", err, stderr)
	}
	if strings.Contains(stdout, "is not a public key file") {
		return "", 0, ErrKeyUnableVerify{stdout}
	}

	fields := strings.Split(stdout, " ")
	if len(fields) < 4 {
		return "", 0, fmt.Errorf("invalid public key line: %s", stdout)
	}

	keyType := strings.Trim(fields[len(fields)-1], "()\r\n")
	return strings.ToLower(keyType), com.StrTo(fields[0]).MustInt(), nil
}
Beispiel #3
0
func NewRepoContext() {
	zip.Verbose = false

	// Check Git installation.
	if _, err := exec.LookPath("git"); err != nil {
		log.Fatal(4, "Fail to test 'git' command: %v (forgotten install?)", err)
	}

	// Check Git version.
	ver, err := git.GetVersion()
	if err != nil {
		log.Fatal(4, "Fail to get Git version: %v", err)
	}

	reqVer, err := git.ParseVersion("1.7.1")
	if err != nil {
		log.Fatal(4, "Fail to parse required Git version: %v", err)
	}
	if ver.LessThan(reqVer) {
		log.Fatal(4, "Gogs requires Git version greater or equal to 1.7.1")
	}

	// Git requires setting user.name and user.email in order to commit changes.
	for configKey, defaultValue := range map[string]string{"user.name": "Gogs", "user.email": "*****@*****.**"} {
		if stdout, stderr, err := process.Exec("NewRepoContext(get setting)", "git", "config", "--get", configKey); err != nil || strings.TrimSpace(stdout) == "" {
			// ExitError indicates this config is not set
			if _, ok := err.(*exec.ExitError); ok || strings.TrimSpace(stdout) == "" {
				if _, stderr, gerr := process.Exec("NewRepoContext(set "+configKey+")", "git", "config", "--global", configKey, defaultValue); gerr != nil {
					log.Fatal(4, "Fail to set git %s(%s): %s", configKey, gerr, stderr)
				}
				log.Info("Git config %s set to %s", configKey, defaultValue)
			} else {
				log.Fatal(4, "Fail to get git %s(%s): %s", configKey, err, stderr)
			}
		}
	}

	// Set git some configurations.
	if _, stderr, err := process.Exec("NewRepoContext(git config --global core.quotepath false)",
		"git", "config", "--global", "core.quotepath", "false"); err != nil {
		log.Fatal(4, "Fail to execute 'git config --global core.quotepath false': %s", stderr)
	}

}
Beispiel #4
0
// CheckPublicKeyString checks if the given public key string is recognized by SSH.
func CheckPublicKeyString(content string) (bool, error) {
	content = strings.TrimRight(content, "\n\r")
	if strings.ContainsAny(content, "\n\r") {
		return false, errors.New("only a single line with a single key please")
	}

	// write the key to a file…
	tmpFile, err := ioutil.TempFile(os.TempDir(), "keytest")
	if err != nil {
		return false, err
	}
	tmpPath := tmpFile.Name()
	defer os.Remove(tmpPath)
	tmpFile.WriteString(content)
	tmpFile.Close()

	// Check if ssh-keygen recognizes its contents.
	stdout, stderr, err := process.Exec("CheckPublicKeyString", "ssh-keygen", "-l", "-f", tmpPath)
	if err != nil {
		return false, errors.New("ssh-keygen -l -f: " + stderr)
	} else if len(stdout) < 2 {
		return false, errors.New("ssh-keygen returned not enough output to evaluate the key: " + stdout)
	}

	// The ssh-keygen in Windows does not print key type, so no need go further.
	if setting.IsWindows {
		return true, nil
	}

	fmt.Println(stdout)
	sshKeygenOutput := strings.Split(stdout, " ")
	if len(sshKeygenOutput) < 4 {
		return false, ErrKeyUnableVerify
	}

	// Check if key type and key size match.
	if !setting.Service.DisableMinimumKeySizeCheck {
		keySize := com.StrTo(sshKeygenOutput[0]).MustInt()
		if keySize == 0 {
			return false, errors.New("cannot get key size of the given key")
		}
		keyType := strings.TrimSpace(sshKeygenOutput[len(sshKeygenOutput)-1])
		if minimumKeySize := minimumKeySizes[keyType]; minimumKeySize == 0 {
			return false, errors.New("sorry, unrecognized public key type")
		} else if keySize < minimumKeySize {
			return false, fmt.Errorf("the minimum accepted size of a public key %s is %d", keyType, minimumKeySize)
		}
	}

	return true, nil
}
Beispiel #5
0
// AddPublicKey adds new public key to database and authorized_keys file.
func AddPublicKey(key *PublicKey) (err error) {
	has, err := x.Get(key)
	if err != nil {
		return err
	} else if has {
		return ErrKeyAlreadyExist
	}

	// Calculate fingerprint.
	tmpPath := strings.Replace(path.Join(os.TempDir(), fmt.Sprintf("%d", time.Now().Nanosecond()),
		"id_rsa.pub"), "\\", "/", -1)
	os.MkdirAll(path.Dir(tmpPath), os.ModePerm)
	if err = ioutil.WriteFile(tmpPath, []byte(key.Content), os.ModePerm); err != nil {
		return err
	}
	stdout, stderr, err := process.Exec("AddPublicKey", "ssh-keygen", "-l", "-f", tmpPath)
	if err != nil {
		return errors.New("ssh-keygen -l -f: " + stderr)
	} else if len(stdout) < 2 {
		return errors.New("not enough output for calculating fingerprint: " + stdout)
	}
	key.Fingerprint = strings.Split(stdout, " ")[1]
	if has, err := x.Get(&PublicKey{Fingerprint: key.Fingerprint}); err == nil && has {
		return ErrKeyAlreadyExist
	}

	// Save SSH key.
	if _, err = x.Insert(key); err != nil {
		return err
	} else if err = saveAuthorizedKeyFile(key); err != nil {
		// Roll back.
		if _, err2 := x.Delete(key); err2 != nil {
			return err2
		}
		return err
	}

	return nil
}
Beispiel #6
0
// InitRepository initializes README and .gitignore if needed.
func initRepository(e Engine, repoPath string, u *User, repo *Repository, initReadme bool, repoLang, license string) error {
	// Init bare new repository.
	os.MkdirAll(repoPath, os.ModePerm)
	_, stderr, err := process.ExecDir(-1, repoPath,
		fmt.Sprintf("initRepository(git init --bare): %s", repoPath),
		"git", "init", "--bare")
	if err != nil {
		return errors.New("git init --bare: " + stderr)
	}

	if err := createUpdateHook(repoPath); err != nil {
		return err
	}

	// Initialize repository according to user's choice.
	fileName := map[string]string{}
	if initReadme {
		fileName["readme"] = "README.md"
	}
	if repoLang != "" {
		fileName["gitign"] = ".gitignore"
	}
	if license != "" {
		fileName["license"] = "LICENSE"
	}

	// Clone to temprory path and do the init commit.
	tmpDir := filepath.Join(os.TempDir(), com.ToStr(time.Now().Nanosecond()))
	os.MkdirAll(tmpDir, os.ModePerm)

	_, stderr, err = process.Exec(
		fmt.Sprintf("initRepository(git clone): %s", repoPath),
		"git", "clone", repoPath, tmpDir)
	if err != nil {
		return errors.New("git clone: " + stderr)
	}

	// README
	if initReadme {
		defaultReadme := repo.Name + "\n" + strings.Repeat("=",
			utf8.RuneCountInString(repo.Name)) + "\n\n" + repo.Description
		if err := ioutil.WriteFile(filepath.Join(tmpDir, fileName["readme"]),
			[]byte(defaultReadme), 0644); err != nil {
			return err
		}
	}

	// FIXME: following two can be merged.

	// .gitignore
	// Copy custom file when available.
	customPath := path.Join(setting.CustomPath, "conf/gitignore", repoLang)
	targetPath := path.Join(tmpDir, fileName["gitign"])
	if com.IsFile(customPath) {
		if err := com.Copy(customPath, targetPath); err != nil {
			return fmt.Errorf("copy gitignore: %v", err)
		}
	} else if com.IsSliceContainsStr(Gitignores, repoLang) {
		if err = ioutil.WriteFile(targetPath,
			bindata.MustAsset(path.Join("conf/gitignore", repoLang)), os.ModePerm); err != nil {
			return fmt.Errorf("generate gitignore: %v", err)
		}
	} else {
		delete(fileName, "gitign")
	}

	// LICENSE
	customPath = path.Join(setting.CustomPath, "conf/license", license)
	targetPath = path.Join(tmpDir, fileName["license"])
	if com.IsFile(customPath) {
		if err = com.Copy(customPath, targetPath); err != nil {
			return fmt.Errorf("copy license: %v", err)
		}
	} else if com.IsSliceContainsStr(Licenses, license) {
		if err = ioutil.WriteFile(targetPath,
			bindata.MustAsset(path.Join("conf/license", license)), os.ModePerm); err != nil {
			return fmt.Errorf("generate license: %v", err)
		}
	} else {
		delete(fileName, "license")
	}

	if len(fileName) == 0 {
		// Re-fetch the repository from database before updating it (else it would
		// override changes that were done earlier with sql)
		if repo, err = getRepositoryById(e, repo.Id); err != nil {
			return err
		}
		repo.IsBare = true
		repo.DefaultBranch = "master"
		return updateRepository(e, repo, false)
	}

	// Apply changes and commit.
	return initRepoCommit(tmpDir, u.NewGitSig())
}