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) }
// 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 }
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) } }
// 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 }
// 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 }
// 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()) }