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 26KB

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