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

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