You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

ssh_key.go 35KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273
  1. // Copyright 2014 The Gogs Authors. All rights reserved.
  2. // Copyright 2019 The Gitea Authors. All rights reserved.
  3. // Use of this source code is governed by a MIT-style
  4. // license that can be found in the LICENSE file.
  5. package models
  6. import (
  7. "bufio"
  8. "crypto/rsa"
  9. "crypto/x509"
  10. "encoding/asn1"
  11. "encoding/base64"
  12. "encoding/binary"
  13. "encoding/pem"
  14. "errors"
  15. "fmt"
  16. "io"
  17. "io/ioutil"
  18. "math/big"
  19. "os"
  20. "path/filepath"
  21. "strings"
  22. "sync"
  23. "time"
  24. "code.gitea.io/gitea/modules/log"
  25. "code.gitea.io/gitea/modules/process"
  26. "code.gitea.io/gitea/modules/setting"
  27. "code.gitea.io/gitea/modules/timeutil"
  28. "code.gitea.io/gitea/modules/util"
  29. "github.com/unknwon/com"
  30. "golang.org/x/crypto/ssh"
  31. "xorm.io/builder"
  32. "xorm.io/xorm"
  33. )
  34. const (
  35. tplCommentPrefix = `# gitea public key`
  36. tplCommand = "%s --config=%s serv key-%d"
  37. tplPublicKey = tplCommentPrefix + "\n" + `command=%s,no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty %s` + "\n"
  38. authorizedPrincipalsFile = "authorized_principals"
  39. )
  40. var sshOpLocker sync.Mutex
  41. // KeyType specifies the key type
  42. type KeyType int
  43. const (
  44. // KeyTypeUser specifies the user key
  45. KeyTypeUser = iota + 1
  46. // KeyTypeDeploy specifies the deploy key
  47. KeyTypeDeploy
  48. // KeyTypePrincipal specifies the authorized principal key
  49. KeyTypePrincipal
  50. )
  51. // PublicKey represents a user or deploy SSH public key.
  52. type PublicKey struct {
  53. ID int64 `xorm:"pk autoincr"`
  54. OwnerID int64 `xorm:"INDEX NOT NULL"`
  55. Name string `xorm:"NOT NULL"`
  56. Fingerprint string `xorm:"INDEX NOT NULL"`
  57. Content string `xorm:"TEXT NOT NULL"`
  58. Mode AccessMode `xorm:"NOT NULL DEFAULT 2"`
  59. Type KeyType `xorm:"NOT NULL DEFAULT 1"`
  60. LoginSourceID int64 `xorm:"NOT NULL DEFAULT 0"`
  61. CreatedUnix timeutil.TimeStamp `xorm:"created"`
  62. UpdatedUnix timeutil.TimeStamp `xorm:"updated"`
  63. HasRecentActivity bool `xorm:"-"`
  64. HasUsed bool `xorm:"-"`
  65. }
  66. // AfterLoad is invoked from XORM after setting the values of all fields of this object.
  67. func (key *PublicKey) AfterLoad() {
  68. key.HasUsed = key.UpdatedUnix > key.CreatedUnix
  69. key.HasRecentActivity = key.UpdatedUnix.AddDuration(7*24*time.Hour) > timeutil.TimeStampNow()
  70. }
  71. // OmitEmail returns content of public key without email address.
  72. func (key *PublicKey) OmitEmail() string {
  73. return strings.Join(strings.Split(key.Content, " ")[:2], " ")
  74. }
  75. // AuthorizedString returns formatted public key string for authorized_keys file.
  76. func (key *PublicKey) AuthorizedString() string {
  77. return fmt.Sprintf(tplPublicKey, util.ShellEscape(fmt.Sprintf(tplCommand, util.ShellEscape(setting.AppPath), util.ShellEscape(setting.CustomConf), key.ID)), key.Content)
  78. }
  79. func extractTypeFromBase64Key(key string) (string, error) {
  80. b, err := base64.StdEncoding.DecodeString(key)
  81. if err != nil || len(b) < 4 {
  82. return "", fmt.Errorf("invalid key format: %v", err)
  83. }
  84. keyLength := int(binary.BigEndian.Uint32(b))
  85. if len(b) < 4+keyLength {
  86. return "", fmt.Errorf("invalid key format: not enough length %d", keyLength)
  87. }
  88. return string(b[4 : 4+keyLength]), nil
  89. }
  90. const ssh2keyStart = "---- BEGIN SSH2 PUBLIC KEY ----"
  91. // parseKeyString parses any key string in OpenSSH or SSH2 format to clean OpenSSH string (RFC4253).
  92. func parseKeyString(content string) (string, error) {
  93. // remove whitespace at start and end
  94. content = strings.TrimSpace(content)
  95. var keyType, keyContent, keyComment string
  96. if strings.HasPrefix(content, ssh2keyStart) {
  97. // Parse SSH2 file format.
  98. // Transform all legal line endings to a single "\n".
  99. content = strings.NewReplacer("\r\n", "\n", "\r", "\n").Replace(content)
  100. lines := strings.Split(content, "\n")
  101. continuationLine := false
  102. for _, line := range lines {
  103. // Skip lines that:
  104. // 1) are a continuation of the previous line,
  105. // 2) contain ":" as that are comment lines
  106. // 3) contain "-" as that are begin and end tags
  107. if continuationLine || strings.ContainsAny(line, ":-") {
  108. continuationLine = strings.HasSuffix(line, "\\")
  109. } else {
  110. keyContent += line
  111. }
  112. }
  113. t, err := extractTypeFromBase64Key(keyContent)
  114. if err != nil {
  115. return "", fmt.Errorf("extractTypeFromBase64Key: %v", err)
  116. }
  117. keyType = t
  118. } else {
  119. if strings.Contains(content, "-----BEGIN") {
  120. // Convert PEM Keys to OpenSSH format
  121. // Transform all legal line endings to a single "\n".
  122. content = strings.NewReplacer("\r\n", "\n", "\r", "\n").Replace(content)
  123. block, _ := pem.Decode([]byte(content))
  124. if block == nil {
  125. return "", fmt.Errorf("failed to parse PEM block containing the public key")
  126. }
  127. pub, err := x509.ParsePKIXPublicKey(block.Bytes)
  128. if err != nil {
  129. var pk rsa.PublicKey
  130. _, err2 := asn1.Unmarshal(block.Bytes, &pk)
  131. if err2 != nil {
  132. return "", fmt.Errorf("failed to parse DER encoded public key as either PKIX or PEM RSA Key: %v %v", err, err2)
  133. }
  134. pub = &pk
  135. }
  136. sshKey, err := ssh.NewPublicKey(pub)
  137. if err != nil {
  138. return "", fmt.Errorf("unable to convert to ssh public key: %v", err)
  139. }
  140. content = string(ssh.MarshalAuthorizedKey(sshKey))
  141. }
  142. // Parse OpenSSH format.
  143. // Remove all newlines
  144. content = strings.NewReplacer("\r\n", "", "\n", "").Replace(content)
  145. parts := strings.SplitN(content, " ", 3)
  146. switch len(parts) {
  147. case 0:
  148. return "", errors.New("empty key")
  149. case 1:
  150. keyContent = parts[0]
  151. case 2:
  152. keyType = parts[0]
  153. keyContent = parts[1]
  154. default:
  155. keyType = parts[0]
  156. keyContent = parts[1]
  157. keyComment = parts[2]
  158. }
  159. // If keyType is not given, extract it from content. If given, validate it.
  160. t, err := extractTypeFromBase64Key(keyContent)
  161. if err != nil {
  162. return "", fmt.Errorf("extractTypeFromBase64Key: %v", err)
  163. }
  164. if len(keyType) == 0 {
  165. keyType = t
  166. } else if keyType != t {
  167. return "", fmt.Errorf("key type and content does not match: %s - %s", keyType, t)
  168. }
  169. }
  170. // Finally we need to check whether we can actually read the proposed key:
  171. _, _, _, _, err := ssh.ParseAuthorizedKey([]byte(keyType + " " + keyContent + " " + keyComment))
  172. if err != nil {
  173. return "", fmt.Errorf("invalid ssh public key: %v", err)
  174. }
  175. return keyType + " " + keyContent + " " + keyComment, nil
  176. }
  177. // writeTmpKeyFile writes key content to a temporary file
  178. // and returns the name of that file, along with any possible errors.
  179. func writeTmpKeyFile(content string) (string, error) {
  180. tmpFile, err := ioutil.TempFile(setting.SSH.KeyTestPath, "gitea_keytest")
  181. if err != nil {
  182. return "", fmt.Errorf("TempFile: %v", err)
  183. }
  184. defer tmpFile.Close()
  185. if _, err = tmpFile.WriteString(content); err != nil {
  186. return "", fmt.Errorf("WriteString: %v", err)
  187. }
  188. return tmpFile.Name(), nil
  189. }
  190. // SSHKeyGenParsePublicKey extracts key type and length using ssh-keygen.
  191. func SSHKeyGenParsePublicKey(key string) (string, int, error) {
  192. // The ssh-keygen in Windows does not print key type, so no need go further.
  193. if setting.IsWindows {
  194. return "", 0, nil
  195. }
  196. tmpName, err := writeTmpKeyFile(key)
  197. if err != nil {
  198. return "", 0, fmt.Errorf("writeTmpKeyFile: %v", err)
  199. }
  200. defer func() {
  201. if err := util.Remove(tmpName); err != nil {
  202. log.Warn("Unable to remove temporary key file: %s: Error: %v", tmpName, err)
  203. }
  204. }()
  205. stdout, stderr, err := process.GetManager().Exec("SSHKeyGenParsePublicKey", setting.SSH.KeygenPath, "-lf", tmpName)
  206. if err != nil {
  207. return "", 0, fmt.Errorf("fail to parse public key: %s - %s", err, stderr)
  208. }
  209. if strings.Contains(stdout, "is not a public key file") {
  210. return "", 0, ErrKeyUnableVerify{stdout}
  211. }
  212. fields := strings.Split(stdout, " ")
  213. if len(fields) < 4 {
  214. return "", 0, fmt.Errorf("invalid public key line: %s", stdout)
  215. }
  216. keyType := strings.Trim(fields[len(fields)-1], "()\r\n")
  217. return strings.ToLower(keyType), com.StrTo(fields[0]).MustInt(), nil
  218. }
  219. // SSHNativeParsePublicKey extracts the key type and length using the golang SSH library.
  220. func SSHNativeParsePublicKey(keyLine string) (string, int, error) {
  221. fields := strings.Fields(keyLine)
  222. if len(fields) < 2 {
  223. return "", 0, fmt.Errorf("not enough fields in public key line: %s", keyLine)
  224. }
  225. raw, err := base64.StdEncoding.DecodeString(fields[1])
  226. if err != nil {
  227. return "", 0, err
  228. }
  229. pkey, err := ssh.ParsePublicKey(raw)
  230. if err != nil {
  231. if strings.Contains(err.Error(), "ssh: unknown key algorithm") {
  232. return "", 0, ErrKeyUnableVerify{err.Error()}
  233. }
  234. return "", 0, fmt.Errorf("ParsePublicKey: %v", err)
  235. }
  236. // The ssh library can parse the key, so next we find out what key exactly we have.
  237. switch pkey.Type() {
  238. case ssh.KeyAlgoDSA:
  239. rawPub := struct {
  240. Name string
  241. P, Q, G, Y *big.Int
  242. }{}
  243. if err := ssh.Unmarshal(pkey.Marshal(), &rawPub); err != nil {
  244. return "", 0, err
  245. }
  246. // as per https://bugzilla.mindrot.org/show_bug.cgi?id=1647 we should never
  247. // see dsa keys != 1024 bit, but as it seems to work, we will not check here
  248. return "dsa", rawPub.P.BitLen(), nil // use P as per crypto/dsa/dsa.go (is L)
  249. case ssh.KeyAlgoRSA:
  250. rawPub := struct {
  251. Name string
  252. E *big.Int
  253. N *big.Int
  254. }{}
  255. if err := ssh.Unmarshal(pkey.Marshal(), &rawPub); err != nil {
  256. return "", 0, err
  257. }
  258. return "rsa", rawPub.N.BitLen(), nil // use N as per crypto/rsa/rsa.go (is bits)
  259. case ssh.KeyAlgoECDSA256:
  260. return "ecdsa", 256, nil
  261. case ssh.KeyAlgoECDSA384:
  262. return "ecdsa", 384, nil
  263. case ssh.KeyAlgoECDSA521:
  264. return "ecdsa", 521, nil
  265. case ssh.KeyAlgoED25519:
  266. return "ed25519", 256, nil
  267. }
  268. return "", 0, fmt.Errorf("unsupported key length detection for type: %s", pkey.Type())
  269. }
  270. // CheckPublicKeyString checks if the given public key string is recognized by SSH.
  271. // It returns the actual public key line on success.
  272. func CheckPublicKeyString(content string) (_ string, err error) {
  273. if setting.SSH.Disabled {
  274. return "", ErrSSHDisabled{}
  275. }
  276. content, err = parseKeyString(content)
  277. if err != nil {
  278. return "", err
  279. }
  280. content = strings.TrimRight(content, "\n\r")
  281. if strings.ContainsAny(content, "\n\r") {
  282. return "", errors.New("only a single line with a single key please")
  283. }
  284. // remove any unnecessary whitespace now
  285. content = strings.TrimSpace(content)
  286. if !setting.SSH.MinimumKeySizeCheck {
  287. return content, nil
  288. }
  289. var (
  290. fnName string
  291. keyType string
  292. length int
  293. )
  294. if setting.SSH.StartBuiltinServer {
  295. fnName = "SSHNativeParsePublicKey"
  296. keyType, length, err = SSHNativeParsePublicKey(content)
  297. } else {
  298. fnName = "SSHKeyGenParsePublicKey"
  299. keyType, length, err = SSHKeyGenParsePublicKey(content)
  300. }
  301. if err != nil {
  302. return "", fmt.Errorf("%s: %v", fnName, err)
  303. }
  304. log.Trace("Key info [native: %v]: %s-%d", setting.SSH.StartBuiltinServer, keyType, length)
  305. if minLen, found := setting.SSH.MinimumKeySizes[keyType]; found && length >= minLen {
  306. return content, nil
  307. } else if found && length < minLen {
  308. return "", fmt.Errorf("key length is not enough: got %d, needs %d", length, minLen)
  309. }
  310. return "", fmt.Errorf("key type is not allowed: %s", keyType)
  311. }
  312. // appendAuthorizedKeysToFile appends new SSH keys' content to authorized_keys file.
  313. func appendAuthorizedKeysToFile(keys ...*PublicKey) error {
  314. // Don't need to rewrite this file if builtin SSH server is enabled.
  315. if setting.SSH.StartBuiltinServer {
  316. return nil
  317. }
  318. sshOpLocker.Lock()
  319. defer sshOpLocker.Unlock()
  320. if setting.SSH.RootPath != "" {
  321. // First of ensure that the RootPath is present, and if not make it with 0700 permissions
  322. // This of course doesn't guarantee that this is the right directory for authorized_keys
  323. // but at least if it's supposed to be this directory and it doesn't exist and we're the
  324. // right user it will at least be created properly.
  325. err := os.MkdirAll(setting.SSH.RootPath, 0700)
  326. if err != nil {
  327. log.Error("Unable to MkdirAll(%s): %v", setting.SSH.RootPath, err)
  328. return err
  329. }
  330. }
  331. fPath := filepath.Join(setting.SSH.RootPath, "authorized_keys")
  332. f, err := os.OpenFile(fPath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0600)
  333. if err != nil {
  334. return err
  335. }
  336. defer f.Close()
  337. // Note: chmod command does not support in Windows.
  338. if !setting.IsWindows {
  339. fi, err := f.Stat()
  340. if err != nil {
  341. return err
  342. }
  343. // .ssh directory should have mode 700, and authorized_keys file should have mode 600.
  344. if fi.Mode().Perm() > 0600 {
  345. log.Error("authorized_keys file has unusual permission flags: %s - setting to -rw-------", fi.Mode().Perm().String())
  346. if err = f.Chmod(0600); err != nil {
  347. return err
  348. }
  349. }
  350. }
  351. for _, key := range keys {
  352. if key.Type == KeyTypePrincipal {
  353. continue
  354. }
  355. if _, err = f.WriteString(key.AuthorizedString()); err != nil {
  356. return err
  357. }
  358. }
  359. return nil
  360. }
  361. // checkKeyFingerprint only checks if key fingerprint has been used as public key,
  362. // it is OK to use same key as deploy key for multiple repositories/users.
  363. func checkKeyFingerprint(e Engine, fingerprint string) error {
  364. has, err := e.Get(&PublicKey{
  365. Fingerprint: fingerprint,
  366. })
  367. if err != nil {
  368. return err
  369. } else if has {
  370. return ErrKeyAlreadyExist{0, fingerprint, ""}
  371. }
  372. return nil
  373. }
  374. func calcFingerprintSSHKeygen(publicKeyContent string) (string, error) {
  375. // Calculate fingerprint.
  376. tmpPath, err := writeTmpKeyFile(publicKeyContent)
  377. if err != nil {
  378. return "", err
  379. }
  380. defer func() {
  381. if err := util.Remove(tmpPath); err != nil {
  382. log.Warn("Unable to remove temporary key file: %s: Error: %v", tmpPath, err)
  383. }
  384. }()
  385. stdout, stderr, err := process.GetManager().Exec("AddPublicKey", "ssh-keygen", "-lf", tmpPath)
  386. if err != nil {
  387. if strings.Contains(stderr, "is not a public key file") {
  388. return "", ErrKeyUnableVerify{stderr}
  389. }
  390. return "", fmt.Errorf("'ssh-keygen -lf %s' failed with error '%s': %s", tmpPath, err, stderr)
  391. } else if len(stdout) < 2 {
  392. return "", errors.New("not enough output for calculating fingerprint: " + stdout)
  393. }
  394. return strings.Split(stdout, " ")[1], nil
  395. }
  396. func calcFingerprintNative(publicKeyContent string) (string, error) {
  397. // Calculate fingerprint.
  398. pk, _, _, _, err := ssh.ParseAuthorizedKey([]byte(publicKeyContent))
  399. if err != nil {
  400. return "", err
  401. }
  402. return ssh.FingerprintSHA256(pk), nil
  403. }
  404. func calcFingerprint(publicKeyContent string) (string, error) {
  405. //Call the method based on configuration
  406. var (
  407. fnName, fp string
  408. err error
  409. )
  410. if setting.SSH.StartBuiltinServer {
  411. fnName = "calcFingerprintNative"
  412. fp, err = calcFingerprintNative(publicKeyContent)
  413. } else {
  414. fnName = "calcFingerprintSSHKeygen"
  415. fp, err = calcFingerprintSSHKeygen(publicKeyContent)
  416. }
  417. if err != nil {
  418. if IsErrKeyUnableVerify(err) {
  419. log.Info("%s", publicKeyContent)
  420. return "", err
  421. }
  422. return "", fmt.Errorf("%s: %v", fnName, err)
  423. }
  424. return fp, nil
  425. }
  426. func addKey(e Engine, key *PublicKey) (err error) {
  427. if len(key.Fingerprint) == 0 {
  428. key.Fingerprint, err = calcFingerprint(key.Content)
  429. if err != nil {
  430. return err
  431. }
  432. }
  433. // Save SSH key.
  434. if _, err = e.Insert(key); err != nil {
  435. return err
  436. }
  437. return appendAuthorizedKeysToFile(key)
  438. }
  439. // AddPublicKey adds new public key to database and authorized_keys file.
  440. func AddPublicKey(ownerID int64, name, content string, loginSourceID int64) (*PublicKey, error) {
  441. log.Trace(content)
  442. fingerprint, err := calcFingerprint(content)
  443. if err != nil {
  444. return nil, err
  445. }
  446. sess := x.NewSession()
  447. defer sess.Close()
  448. if err = sess.Begin(); err != nil {
  449. return nil, err
  450. }
  451. if err := checkKeyFingerprint(sess, fingerprint); err != nil {
  452. return nil, err
  453. }
  454. // Key name of same user cannot be duplicated.
  455. has, err := sess.
  456. Where("owner_id = ? AND name = ?", ownerID, name).
  457. Get(new(PublicKey))
  458. if err != nil {
  459. return nil, err
  460. } else if has {
  461. return nil, ErrKeyNameAlreadyUsed{ownerID, name}
  462. }
  463. key := &PublicKey{
  464. OwnerID: ownerID,
  465. Name: name,
  466. Fingerprint: fingerprint,
  467. Content: content,
  468. Mode: AccessModeWrite,
  469. Type: KeyTypeUser,
  470. LoginSourceID: loginSourceID,
  471. }
  472. if err = addKey(sess, key); err != nil {
  473. return nil, fmt.Errorf("addKey: %v", err)
  474. }
  475. return key, sess.Commit()
  476. }
  477. // GetPublicKeyByID returns public key by given ID.
  478. func GetPublicKeyByID(keyID int64) (*PublicKey, error) {
  479. key := new(PublicKey)
  480. has, err := x.
  481. ID(keyID).
  482. Get(key)
  483. if err != nil {
  484. return nil, err
  485. } else if !has {
  486. return nil, ErrKeyNotExist{keyID}
  487. }
  488. return key, nil
  489. }
  490. func searchPublicKeyByContentWithEngine(e Engine, content string) (*PublicKey, error) {
  491. key := new(PublicKey)
  492. has, err := e.
  493. Where("content like ?", content+"%").
  494. Get(key)
  495. if err != nil {
  496. return nil, err
  497. } else if !has {
  498. return nil, ErrKeyNotExist{}
  499. }
  500. return key, nil
  501. }
  502. // SearchPublicKeyByContent searches content as prefix (leak e-mail part)
  503. // and returns public key found.
  504. func SearchPublicKeyByContent(content string) (*PublicKey, error) {
  505. return searchPublicKeyByContentWithEngine(x, content)
  506. }
  507. func searchPublicKeyByContentExactWithEngine(e Engine, content string) (*PublicKey, error) {
  508. key := new(PublicKey)
  509. has, err := e.
  510. Where("content = ?", content).
  511. Get(key)
  512. if err != nil {
  513. return nil, err
  514. } else if !has {
  515. return nil, ErrKeyNotExist{}
  516. }
  517. return key, nil
  518. }
  519. // SearchPublicKeyByContentExact searches content
  520. // and returns public key found.
  521. func SearchPublicKeyByContentExact(content string) (*PublicKey, error) {
  522. return searchPublicKeyByContentExactWithEngine(x, content)
  523. }
  524. // SearchPublicKey returns a list of public keys matching the provided arguments.
  525. func SearchPublicKey(uid int64, fingerprint string) ([]*PublicKey, error) {
  526. keys := make([]*PublicKey, 0, 5)
  527. cond := builder.NewCond()
  528. if uid != 0 {
  529. cond = cond.And(builder.Eq{"owner_id": uid})
  530. }
  531. if fingerprint != "" {
  532. cond = cond.And(builder.Eq{"fingerprint": fingerprint})
  533. }
  534. return keys, x.Where(cond).Find(&keys)
  535. }
  536. // ListPublicKeys returns a list of public keys belongs to given user.
  537. func ListPublicKeys(uid int64, listOptions ListOptions) ([]*PublicKey, error) {
  538. sess := x.Where("owner_id = ? AND type != ?", uid, KeyTypePrincipal)
  539. if listOptions.Page != 0 {
  540. sess = listOptions.setSessionPagination(sess)
  541. keys := make([]*PublicKey, 0, listOptions.PageSize)
  542. return keys, sess.Find(&keys)
  543. }
  544. keys := make([]*PublicKey, 0, 5)
  545. return keys, sess.Find(&keys)
  546. }
  547. // ListPublicLdapSSHKeys returns a list of synchronized public ldap ssh keys belongs to given user and login source.
  548. func ListPublicLdapSSHKeys(uid int64, loginSourceID int64) ([]*PublicKey, error) {
  549. keys := make([]*PublicKey, 0, 5)
  550. return keys, x.
  551. Where("owner_id = ? AND login_source_id = ?", uid, loginSourceID).
  552. Find(&keys)
  553. }
  554. // UpdatePublicKeyUpdated updates public key use time.
  555. func UpdatePublicKeyUpdated(id int64) error {
  556. // Check if key exists before update as affected rows count is unreliable
  557. // and will return 0 affected rows if two updates are made at the same time
  558. if cnt, err := x.ID(id).Count(&PublicKey{}); err != nil {
  559. return err
  560. } else if cnt != 1 {
  561. return ErrKeyNotExist{id}
  562. }
  563. _, err := x.ID(id).Cols("updated_unix").Update(&PublicKey{
  564. UpdatedUnix: timeutil.TimeStampNow(),
  565. })
  566. if err != nil {
  567. return err
  568. }
  569. return nil
  570. }
  571. // deletePublicKeys does the actual key deletion but does not update authorized_keys file.
  572. func deletePublicKeys(e Engine, keyIDs ...int64) error {
  573. if len(keyIDs) == 0 {
  574. return nil
  575. }
  576. _, err := e.In("id", keyIDs).Delete(new(PublicKey))
  577. return err
  578. }
  579. // DeletePublicKey deletes SSH key information both in database and authorized_keys file.
  580. func DeletePublicKey(doer *User, id int64) (err error) {
  581. key, err := GetPublicKeyByID(id)
  582. if err != nil {
  583. return err
  584. }
  585. // Check if user has access to delete this key.
  586. if !doer.IsAdmin && doer.ID != key.OwnerID {
  587. return ErrKeyAccessDenied{doer.ID, key.ID, "public"}
  588. }
  589. sess := x.NewSession()
  590. defer sess.Close()
  591. if err = sess.Begin(); err != nil {
  592. return err
  593. }
  594. if err = deletePublicKeys(sess, id); err != nil {
  595. return err
  596. }
  597. if err = sess.Commit(); err != nil {
  598. return err
  599. }
  600. sess.Close()
  601. if key.Type == KeyTypePrincipal {
  602. return RewriteAllPrincipalKeys()
  603. }
  604. return RewriteAllPublicKeys()
  605. }
  606. // RewriteAllPublicKeys removes any authorized key and rewrite all keys from database again.
  607. // Note: x.Iterate does not get latest data after insert/delete, so we have to call this function
  608. // outside any session scope independently.
  609. func RewriteAllPublicKeys() error {
  610. return rewriteAllPublicKeys(x)
  611. }
  612. func rewriteAllPublicKeys(e Engine) error {
  613. //Don't rewrite key if internal server
  614. if setting.SSH.StartBuiltinServer || !setting.SSH.CreateAuthorizedKeysFile {
  615. return nil
  616. }
  617. sshOpLocker.Lock()
  618. defer sshOpLocker.Unlock()
  619. if setting.SSH.RootPath != "" {
  620. // First of ensure that the RootPath is present, and if not make it with 0700 permissions
  621. // This of course doesn't guarantee that this is the right directory for authorized_keys
  622. // but at least if it's supposed to be this directory and it doesn't exist and we're the
  623. // right user it will at least be created properly.
  624. err := os.MkdirAll(setting.SSH.RootPath, 0700)
  625. if err != nil {
  626. log.Error("Unable to MkdirAll(%s): %v", setting.SSH.RootPath, err)
  627. return err
  628. }
  629. }
  630. fPath := filepath.Join(setting.SSH.RootPath, "authorized_keys")
  631. tmpPath := fPath + ".tmp"
  632. t, err := os.OpenFile(tmpPath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600)
  633. if err != nil {
  634. return err
  635. }
  636. defer func() {
  637. t.Close()
  638. if err := util.Remove(tmpPath); err != nil {
  639. log.Warn("Unable to remove temporary authorized keys file: %s: Error: %v", tmpPath, err)
  640. }
  641. }()
  642. if setting.SSH.AuthorizedKeysBackup && com.IsExist(fPath) {
  643. bakPath := fmt.Sprintf("%s_%d.gitea_bak", fPath, time.Now().Unix())
  644. if err = com.Copy(fPath, bakPath); err != nil {
  645. return err
  646. }
  647. }
  648. if err := regeneratePublicKeys(e, t); err != nil {
  649. return err
  650. }
  651. t.Close()
  652. return os.Rename(tmpPath, fPath)
  653. }
  654. // RegeneratePublicKeys regenerates the authorized_keys file
  655. func RegeneratePublicKeys(t io.StringWriter) error {
  656. return regeneratePublicKeys(x, t)
  657. }
  658. func regeneratePublicKeys(e Engine, t io.StringWriter) error {
  659. if err := e.Where("type != ?", KeyTypePrincipal).Iterate(new(PublicKey), func(idx int, bean interface{}) (err error) {
  660. _, err = t.WriteString((bean.(*PublicKey)).AuthorizedString())
  661. return err
  662. }); err != nil {
  663. return err
  664. }
  665. fPath := filepath.Join(setting.SSH.RootPath, "authorized_keys")
  666. if com.IsExist(fPath) {
  667. f, err := os.Open(fPath)
  668. if err != nil {
  669. return err
  670. }
  671. scanner := bufio.NewScanner(f)
  672. for scanner.Scan() {
  673. line := scanner.Text()
  674. if strings.HasPrefix(line, tplCommentPrefix) {
  675. scanner.Scan()
  676. continue
  677. }
  678. _, err = t.WriteString(line + "\n")
  679. if err != nil {
  680. f.Close()
  681. return err
  682. }
  683. }
  684. f.Close()
  685. }
  686. return nil
  687. }
  688. // ________ .__ ____ __.
  689. // \______ \ ____ ______ | | ____ ___.__.| |/ _|____ ___.__.
  690. // | | \_/ __ \\____ \| | / _ < | || <_/ __ < | |
  691. // | ` \ ___/| |_> > |_( <_> )___ || | \ ___/\___ |
  692. // /_______ /\___ > __/|____/\____// ____||____|__ \___ > ____|
  693. // \/ \/|__| \/ \/ \/\/
  694. // DeployKey represents deploy key information and its relation with repository.
  695. type DeployKey struct {
  696. ID int64 `xorm:"pk autoincr"`
  697. KeyID int64 `xorm:"UNIQUE(s) INDEX"`
  698. RepoID int64 `xorm:"UNIQUE(s) INDEX"`
  699. Name string
  700. Fingerprint string
  701. Content string `xorm:"-"`
  702. Mode AccessMode `xorm:"NOT NULL DEFAULT 1"`
  703. CreatedUnix timeutil.TimeStamp `xorm:"created"`
  704. UpdatedUnix timeutil.TimeStamp `xorm:"updated"`
  705. HasRecentActivity bool `xorm:"-"`
  706. HasUsed bool `xorm:"-"`
  707. }
  708. // AfterLoad is invoked from XORM after setting the values of all fields of this object.
  709. func (key *DeployKey) AfterLoad() {
  710. key.HasUsed = key.UpdatedUnix > key.CreatedUnix
  711. key.HasRecentActivity = key.UpdatedUnix.AddDuration(7*24*time.Hour) > timeutil.TimeStampNow()
  712. }
  713. // GetContent gets associated public key content.
  714. func (key *DeployKey) GetContent() error {
  715. pkey, err := GetPublicKeyByID(key.KeyID)
  716. if err != nil {
  717. return err
  718. }
  719. key.Content = pkey.Content
  720. return nil
  721. }
  722. // IsReadOnly checks if the key can only be used for read operations
  723. func (key *DeployKey) IsReadOnly() bool {
  724. return key.Mode == AccessModeRead
  725. }
  726. func checkDeployKey(e Engine, keyID, repoID int64, name string) error {
  727. // Note: We want error detail, not just true or false here.
  728. has, err := e.
  729. Where("key_id = ? AND repo_id = ?", keyID, repoID).
  730. Get(new(DeployKey))
  731. if err != nil {
  732. return err
  733. } else if has {
  734. return ErrDeployKeyAlreadyExist{keyID, repoID}
  735. }
  736. has, err = e.
  737. Where("repo_id = ? AND name = ?", repoID, name).
  738. Get(new(DeployKey))
  739. if err != nil {
  740. return err
  741. } else if has {
  742. return ErrDeployKeyNameAlreadyUsed{repoID, name}
  743. }
  744. return nil
  745. }
  746. // addDeployKey adds new key-repo relation.
  747. func addDeployKey(e *xorm.Session, keyID, repoID int64, name, fingerprint string, mode AccessMode) (*DeployKey, error) {
  748. if err := checkDeployKey(e, keyID, repoID, name); err != nil {
  749. return nil, err
  750. }
  751. key := &DeployKey{
  752. KeyID: keyID,
  753. RepoID: repoID,
  754. Name: name,
  755. Fingerprint: fingerprint,
  756. Mode: mode,
  757. }
  758. _, err := e.Insert(key)
  759. return key, err
  760. }
  761. // HasDeployKey returns true if public key is a deploy key of given repository.
  762. func HasDeployKey(keyID, repoID int64) bool {
  763. has, _ := x.
  764. Where("key_id = ? AND repo_id = ?", keyID, repoID).
  765. Get(new(DeployKey))
  766. return has
  767. }
  768. // AddDeployKey add new deploy key to database and authorized_keys file.
  769. func AddDeployKey(repoID int64, name, content string, readOnly bool) (*DeployKey, error) {
  770. fingerprint, err := calcFingerprint(content)
  771. if err != nil {
  772. return nil, err
  773. }
  774. accessMode := AccessModeRead
  775. if !readOnly {
  776. accessMode = AccessModeWrite
  777. }
  778. sess := x.NewSession()
  779. defer sess.Close()
  780. if err = sess.Begin(); err != nil {
  781. return nil, err
  782. }
  783. pkey := &PublicKey{
  784. Fingerprint: fingerprint,
  785. }
  786. has, err := sess.Get(pkey)
  787. if err != nil {
  788. return nil, err
  789. }
  790. if has {
  791. if pkey.Type != KeyTypeDeploy {
  792. return nil, ErrKeyAlreadyExist{0, fingerprint, ""}
  793. }
  794. } else {
  795. // First time use this deploy key.
  796. pkey.Mode = accessMode
  797. pkey.Type = KeyTypeDeploy
  798. pkey.Content = content
  799. pkey.Name = name
  800. if err = addKey(sess, pkey); err != nil {
  801. return nil, fmt.Errorf("addKey: %v", err)
  802. }
  803. }
  804. key, err := addDeployKey(sess, pkey.ID, repoID, name, pkey.Fingerprint, accessMode)
  805. if err != nil {
  806. return nil, err
  807. }
  808. return key, sess.Commit()
  809. }
  810. // GetDeployKeyByID returns deploy key by given ID.
  811. func GetDeployKeyByID(id int64) (*DeployKey, error) {
  812. return getDeployKeyByID(x, id)
  813. }
  814. func getDeployKeyByID(e Engine, id int64) (*DeployKey, error) {
  815. key := new(DeployKey)
  816. has, err := e.ID(id).Get(key)
  817. if err != nil {
  818. return nil, err
  819. } else if !has {
  820. return nil, ErrDeployKeyNotExist{id, 0, 0}
  821. }
  822. return key, nil
  823. }
  824. // GetDeployKeyByRepo returns deploy key by given public key ID and repository ID.
  825. func GetDeployKeyByRepo(keyID, repoID int64) (*DeployKey, error) {
  826. return getDeployKeyByRepo(x, keyID, repoID)
  827. }
  828. func getDeployKeyByRepo(e Engine, keyID, repoID int64) (*DeployKey, error) {
  829. key := &DeployKey{
  830. KeyID: keyID,
  831. RepoID: repoID,
  832. }
  833. has, err := e.Get(key)
  834. if err != nil {
  835. return nil, err
  836. } else if !has {
  837. return nil, ErrDeployKeyNotExist{0, keyID, repoID}
  838. }
  839. return key, nil
  840. }
  841. // UpdateDeployKeyCols updates deploy key information in the specified columns.
  842. func UpdateDeployKeyCols(key *DeployKey, cols ...string) error {
  843. _, err := x.ID(key.ID).Cols(cols...).Update(key)
  844. return err
  845. }
  846. // UpdateDeployKey updates deploy key information.
  847. func UpdateDeployKey(key *DeployKey) error {
  848. _, err := x.ID(key.ID).AllCols().Update(key)
  849. return err
  850. }
  851. // DeleteDeployKey deletes deploy key from its repository authorized_keys file if needed.
  852. func DeleteDeployKey(doer *User, id int64) error {
  853. sess := x.NewSession()
  854. defer sess.Close()
  855. if err := sess.Begin(); err != nil {
  856. return err
  857. }
  858. if err := deleteDeployKey(sess, doer, id); err != nil {
  859. return err
  860. }
  861. return sess.Commit()
  862. }
  863. func deleteDeployKey(sess Engine, doer *User, id int64) error {
  864. key, err := getDeployKeyByID(sess, id)
  865. if err != nil {
  866. if IsErrDeployKeyNotExist(err) {
  867. return nil
  868. }
  869. return fmt.Errorf("GetDeployKeyByID: %v", err)
  870. }
  871. // Check if user has access to delete this key.
  872. if !doer.IsAdmin {
  873. repo, err := getRepositoryByID(sess, key.RepoID)
  874. if err != nil {
  875. return fmt.Errorf("GetRepositoryByID: %v", err)
  876. }
  877. has, err := isUserRepoAdmin(sess, repo, doer)
  878. if err != nil {
  879. return fmt.Errorf("GetUserRepoPermission: %v", err)
  880. } else if !has {
  881. return ErrKeyAccessDenied{doer.ID, key.ID, "deploy"}
  882. }
  883. }
  884. if _, err = sess.ID(key.ID).Delete(new(DeployKey)); err != nil {
  885. return fmt.Errorf("delete deploy key [%d]: %v", key.ID, err)
  886. }
  887. // Check if this is the last reference to same key content.
  888. has, err := sess.
  889. Where("key_id = ?", key.KeyID).
  890. Get(new(DeployKey))
  891. if err != nil {
  892. return err
  893. } else if !has {
  894. if err = deletePublicKeys(sess, key.KeyID); err != nil {
  895. return err
  896. }
  897. // after deleted the public keys, should rewrite the public keys file
  898. if err = rewriteAllPublicKeys(sess); err != nil {
  899. return err
  900. }
  901. }
  902. return nil
  903. }
  904. // ListDeployKeys returns all deploy keys by given repository ID.
  905. func ListDeployKeys(repoID int64, listOptions ListOptions) ([]*DeployKey, error) {
  906. return listDeployKeys(x, repoID, listOptions)
  907. }
  908. func listDeployKeys(e Engine, repoID int64, listOptions ListOptions) ([]*DeployKey, error) {
  909. sess := e.Where("repo_id = ?", repoID)
  910. if listOptions.Page != 0 {
  911. sess = listOptions.setSessionPagination(sess)
  912. keys := make([]*DeployKey, 0, listOptions.PageSize)
  913. return keys, sess.Find(&keys)
  914. }
  915. keys := make([]*DeployKey, 0, 5)
  916. return keys, sess.Find(&keys)
  917. }
  918. // SearchDeployKeys returns a list of deploy keys matching the provided arguments.
  919. func SearchDeployKeys(repoID int64, keyID int64, fingerprint string) ([]*DeployKey, error) {
  920. keys := make([]*DeployKey, 0, 5)
  921. cond := builder.NewCond()
  922. if repoID != 0 {
  923. cond = cond.And(builder.Eq{"repo_id": repoID})
  924. }
  925. if keyID != 0 {
  926. cond = cond.And(builder.Eq{"key_id": keyID})
  927. }
  928. if fingerprint != "" {
  929. cond = cond.And(builder.Eq{"fingerprint": fingerprint})
  930. }
  931. return keys, x.Where(cond).Find(&keys)
  932. }
  933. // __________ .__ .__ .__
  934. // \______ _______|__| ____ ____ |_____________ | | ______
  935. // | ___\_ __ | |/ \_/ ___\| \____ \__ \ | | / ___/
  936. // | | | | \| | | \ \___| | |_> / __ \| |__\___ \
  937. // |____| |__| |__|___| /\___ |__| __(____ |____/____ >
  938. // \/ \/ |__| \/ \/
  939. // AddPrincipalKey adds new principal to database and authorized_principals file.
  940. func AddPrincipalKey(ownerID int64, content string, loginSourceID int64) (*PublicKey, error) {
  941. sess := x.NewSession()
  942. defer sess.Close()
  943. if err := sess.Begin(); err != nil {
  944. return nil, err
  945. }
  946. // Principals cannot be duplicated.
  947. has, err := sess.
  948. Where("content = ? AND type = ?", content, KeyTypePrincipal).
  949. Get(new(PublicKey))
  950. if err != nil {
  951. return nil, err
  952. } else if has {
  953. return nil, ErrKeyAlreadyExist{0, "", content}
  954. }
  955. key := &PublicKey{
  956. OwnerID: ownerID,
  957. Name: content,
  958. Content: content,
  959. Mode: AccessModeWrite,
  960. Type: KeyTypePrincipal,
  961. LoginSourceID: loginSourceID,
  962. }
  963. if err = addPrincipalKey(sess, key); err != nil {
  964. return nil, fmt.Errorf("addKey: %v", err)
  965. }
  966. if err = sess.Commit(); err != nil {
  967. return nil, err
  968. }
  969. sess.Close()
  970. return key, RewriteAllPrincipalKeys()
  971. }
  972. func addPrincipalKey(e Engine, key *PublicKey) (err error) {
  973. // Save Key representing a principal.
  974. if _, err = e.Insert(key); err != nil {
  975. return err
  976. }
  977. return nil
  978. }
  979. // CheckPrincipalKeyString strips spaces and returns an error if the given principal contains newlines
  980. func CheckPrincipalKeyString(user *User, content string) (_ string, err error) {
  981. if setting.SSH.Disabled {
  982. return "", ErrSSHDisabled{}
  983. }
  984. content = strings.TrimSpace(content)
  985. if strings.ContainsAny(content, "\r\n") {
  986. return "", errors.New("only a single line with a single principal please")
  987. }
  988. // check all the allowed principals, email, username or anything
  989. // if any matches, return ok
  990. for _, v := range setting.SSH.AuthorizedPrincipalsAllow {
  991. switch v {
  992. case "anything":
  993. return content, nil
  994. case "email":
  995. emails, err := GetEmailAddresses(user.ID)
  996. if err != nil {
  997. return "", err
  998. }
  999. for _, email := range emails {
  1000. if !email.IsActivated {
  1001. continue
  1002. }
  1003. if content == email.Email {
  1004. return content, nil
  1005. }
  1006. }
  1007. case "username":
  1008. if content == user.Name {
  1009. return content, nil
  1010. }
  1011. }
  1012. }
  1013. return "", fmt.Errorf("didn't match allowed principals: %s", setting.SSH.AuthorizedPrincipalsAllow)
  1014. }
  1015. // RewriteAllPrincipalKeys removes any authorized principal and rewrite all keys from database again.
  1016. // Note: x.Iterate does not get latest data after insert/delete, so we have to call this function
  1017. // outside any session scope independently.
  1018. func RewriteAllPrincipalKeys() error {
  1019. return rewriteAllPrincipalKeys(x)
  1020. }
  1021. func rewriteAllPrincipalKeys(e Engine) error {
  1022. // Don't rewrite key if internal server
  1023. if setting.SSH.StartBuiltinServer || !setting.SSH.CreateAuthorizedPrincipalsFile {
  1024. return nil
  1025. }
  1026. sshOpLocker.Lock()
  1027. defer sshOpLocker.Unlock()
  1028. if setting.SSH.RootPath != "" {
  1029. // First of ensure that the RootPath is present, and if not make it with 0700 permissions
  1030. // This of course doesn't guarantee that this is the right directory for authorized_keys
  1031. // but at least if it's supposed to be this directory and it doesn't exist and we're the
  1032. // right user it will at least be created properly.
  1033. err := os.MkdirAll(setting.SSH.RootPath, 0700)
  1034. if err != nil {
  1035. log.Error("Unable to MkdirAll(%s): %v", setting.SSH.RootPath, err)
  1036. return err
  1037. }
  1038. }
  1039. fPath := filepath.Join(setting.SSH.RootPath, authorizedPrincipalsFile)
  1040. tmpPath := fPath + ".tmp"
  1041. t, err := os.OpenFile(tmpPath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600)
  1042. if err != nil {
  1043. return err
  1044. }
  1045. defer func() {
  1046. t.Close()
  1047. os.Remove(tmpPath)
  1048. }()
  1049. if setting.SSH.AuthorizedPrincipalsBackup && com.IsExist(fPath) {
  1050. bakPath := fmt.Sprintf("%s_%d.gitea_bak", fPath, time.Now().Unix())
  1051. if err = com.Copy(fPath, bakPath); err != nil {
  1052. return err
  1053. }
  1054. }
  1055. if err := regeneratePrincipalKeys(e, t); err != nil {
  1056. return err
  1057. }
  1058. t.Close()
  1059. return os.Rename(tmpPath, fPath)
  1060. }
  1061. // ListPrincipalKeys returns a list of principals belongs to given user.
  1062. func ListPrincipalKeys(uid int64, listOptions ListOptions) ([]*PublicKey, error) {
  1063. sess := x.Where("owner_id = ? AND type = ?", uid, KeyTypePrincipal)
  1064. if listOptions.Page != 0 {
  1065. sess = listOptions.setSessionPagination(sess)
  1066. keys := make([]*PublicKey, 0, listOptions.PageSize)
  1067. return keys, sess.Find(&keys)
  1068. }
  1069. keys := make([]*PublicKey, 0, 5)
  1070. return keys, sess.Find(&keys)
  1071. }
  1072. // RegeneratePrincipalKeys regenerates the authorized_principals file
  1073. func RegeneratePrincipalKeys(t io.StringWriter) error {
  1074. return regeneratePrincipalKeys(x, t)
  1075. }
  1076. func regeneratePrincipalKeys(e Engine, t io.StringWriter) error {
  1077. if err := e.Where("type = ?", KeyTypePrincipal).Iterate(new(PublicKey), func(idx int, bean interface{}) (err error) {
  1078. _, err = t.WriteString((bean.(*PublicKey)).AuthorizedString())
  1079. return err
  1080. }); err != nil {
  1081. return err
  1082. }
  1083. fPath := filepath.Join(setting.SSH.RootPath, authorizedPrincipalsFile)
  1084. if com.IsExist(fPath) {
  1085. f, err := os.Open(fPath)
  1086. if err != nil {
  1087. return err
  1088. }
  1089. scanner := bufio.NewScanner(f)
  1090. for scanner.Scan() {
  1091. line := scanner.Text()
  1092. if strings.HasPrefix(line, tplCommentPrefix) {
  1093. scanner.Scan()
  1094. continue
  1095. }
  1096. _, err = t.WriteString(line + "\n")
  1097. if err != nil {
  1098. f.Close()
  1099. return err
  1100. }
  1101. }
  1102. f.Close()
  1103. }
  1104. return nil
  1105. }