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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498
  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. "fmt"
  8. "strings"
  9. "time"
  10. "code.gitea.io/gitea/models/db"
  11. "code.gitea.io/gitea/models/login"
  12. "code.gitea.io/gitea/models/perm"
  13. user_model "code.gitea.io/gitea/models/user"
  14. "code.gitea.io/gitea/modules/log"
  15. "code.gitea.io/gitea/modules/timeutil"
  16. "code.gitea.io/gitea/modules/util"
  17. "golang.org/x/crypto/ssh"
  18. "xorm.io/builder"
  19. )
  20. // KeyType specifies the key type
  21. type KeyType int
  22. const (
  23. // KeyTypeUser specifies the user key
  24. KeyTypeUser = iota + 1
  25. // KeyTypeDeploy specifies the deploy key
  26. KeyTypeDeploy
  27. // KeyTypePrincipal specifies the authorized principal key
  28. KeyTypePrincipal
  29. )
  30. // PublicKey represents a user or deploy SSH public key.
  31. type PublicKey struct {
  32. ID int64 `xorm:"pk autoincr"`
  33. OwnerID int64 `xorm:"INDEX NOT NULL"`
  34. Name string `xorm:"NOT NULL"`
  35. Fingerprint string `xorm:"INDEX NOT NULL"`
  36. Content string `xorm:"TEXT NOT NULL"`
  37. Mode perm.AccessMode `xorm:"NOT NULL DEFAULT 2"`
  38. Type KeyType `xorm:"NOT NULL DEFAULT 1"`
  39. LoginSourceID int64 `xorm:"NOT NULL DEFAULT 0"`
  40. CreatedUnix timeutil.TimeStamp `xorm:"created"`
  41. UpdatedUnix timeutil.TimeStamp `xorm:"updated"`
  42. HasRecentActivity bool `xorm:"-"`
  43. HasUsed bool `xorm:"-"`
  44. }
  45. func init() {
  46. db.RegisterModel(new(PublicKey))
  47. }
  48. // AfterLoad is invoked from XORM after setting the values of all fields of this object.
  49. func (key *PublicKey) AfterLoad() {
  50. key.HasUsed = key.UpdatedUnix > key.CreatedUnix
  51. key.HasRecentActivity = key.UpdatedUnix.AddDuration(7*24*time.Hour) > timeutil.TimeStampNow()
  52. }
  53. // OmitEmail returns content of public key without email address.
  54. func (key *PublicKey) OmitEmail() string {
  55. return strings.Join(strings.Split(key.Content, " ")[:2], " ")
  56. }
  57. // AuthorizedString returns formatted public key string for authorized_keys file.
  58. //
  59. // TODO: Consider dropping this function
  60. func (key *PublicKey) AuthorizedString() string {
  61. return AuthorizedStringForKey(key)
  62. }
  63. func addKey(e db.Engine, key *PublicKey) (err error) {
  64. if len(key.Fingerprint) == 0 {
  65. key.Fingerprint, err = calcFingerprint(key.Content)
  66. if err != nil {
  67. return err
  68. }
  69. }
  70. // Save SSH key.
  71. if _, err = e.Insert(key); err != nil {
  72. return err
  73. }
  74. return appendAuthorizedKeysToFile(key)
  75. }
  76. // AddPublicKey adds new public key to database and authorized_keys file.
  77. func AddPublicKey(ownerID int64, name, content string, loginSourceID int64) (*PublicKey, error) {
  78. log.Trace(content)
  79. fingerprint, err := calcFingerprint(content)
  80. if err != nil {
  81. return nil, err
  82. }
  83. ctx, committer, err := db.TxContext()
  84. if err != nil {
  85. return nil, err
  86. }
  87. defer committer.Close()
  88. sess := db.GetEngine(ctx)
  89. if err := checkKeyFingerprint(sess, fingerprint); err != nil {
  90. return nil, err
  91. }
  92. // Key name of same user cannot be duplicated.
  93. has, err := sess.
  94. Where("owner_id = ? AND name = ?", ownerID, name).
  95. Get(new(PublicKey))
  96. if err != nil {
  97. return nil, err
  98. } else if has {
  99. return nil, ErrKeyNameAlreadyUsed{ownerID, name}
  100. }
  101. key := &PublicKey{
  102. OwnerID: ownerID,
  103. Name: name,
  104. Fingerprint: fingerprint,
  105. Content: content,
  106. Mode: perm.AccessModeWrite,
  107. Type: KeyTypeUser,
  108. LoginSourceID: loginSourceID,
  109. }
  110. if err = addKey(sess, key); err != nil {
  111. return nil, fmt.Errorf("addKey: %v", err)
  112. }
  113. return key, committer.Commit()
  114. }
  115. // GetPublicKeyByID returns public key by given ID.
  116. func GetPublicKeyByID(keyID int64) (*PublicKey, error) {
  117. key := new(PublicKey)
  118. has, err := db.GetEngine(db.DefaultContext).
  119. ID(keyID).
  120. Get(key)
  121. if err != nil {
  122. return nil, err
  123. } else if !has {
  124. return nil, ErrKeyNotExist{keyID}
  125. }
  126. return key, nil
  127. }
  128. func searchPublicKeyByContentWithEngine(e db.Engine, content string) (*PublicKey, error) {
  129. key := new(PublicKey)
  130. has, err := e.
  131. Where("content like ?", content+"%").
  132. Get(key)
  133. if err != nil {
  134. return nil, err
  135. } else if !has {
  136. return nil, ErrKeyNotExist{}
  137. }
  138. return key, nil
  139. }
  140. // SearchPublicKeyByContent searches content as prefix (leak e-mail part)
  141. // and returns public key found.
  142. func SearchPublicKeyByContent(content string) (*PublicKey, error) {
  143. return searchPublicKeyByContentWithEngine(db.GetEngine(db.DefaultContext), content)
  144. }
  145. func searchPublicKeyByContentExactWithEngine(e db.Engine, content string) (*PublicKey, error) {
  146. key := new(PublicKey)
  147. has, err := e.
  148. Where("content = ?", content).
  149. Get(key)
  150. if err != nil {
  151. return nil, err
  152. } else if !has {
  153. return nil, ErrKeyNotExist{}
  154. }
  155. return key, nil
  156. }
  157. // SearchPublicKeyByContentExact searches content
  158. // and returns public key found.
  159. func SearchPublicKeyByContentExact(content string) (*PublicKey, error) {
  160. return searchPublicKeyByContentExactWithEngine(db.GetEngine(db.DefaultContext), content)
  161. }
  162. // SearchPublicKey returns a list of public keys matching the provided arguments.
  163. func SearchPublicKey(uid int64, fingerprint string) ([]*PublicKey, error) {
  164. keys := make([]*PublicKey, 0, 5)
  165. cond := builder.NewCond()
  166. if uid != 0 {
  167. cond = cond.And(builder.Eq{"owner_id": uid})
  168. }
  169. if fingerprint != "" {
  170. cond = cond.And(builder.Eq{"fingerprint": fingerprint})
  171. }
  172. return keys, db.GetEngine(db.DefaultContext).Where(cond).Find(&keys)
  173. }
  174. // ListPublicKeys returns a list of public keys belongs to given user.
  175. func ListPublicKeys(uid int64, listOptions db.ListOptions) ([]*PublicKey, error) {
  176. sess := db.GetEngine(db.DefaultContext).Where("owner_id = ? AND type != ?", uid, KeyTypePrincipal)
  177. if listOptions.Page != 0 {
  178. sess = db.SetSessionPagination(sess, &listOptions)
  179. keys := make([]*PublicKey, 0, listOptions.PageSize)
  180. return keys, sess.Find(&keys)
  181. }
  182. keys := make([]*PublicKey, 0, 5)
  183. return keys, sess.Find(&keys)
  184. }
  185. // CountPublicKeys count public keys a user has
  186. func CountPublicKeys(userID int64) (int64, error) {
  187. sess := db.GetEngine(db.DefaultContext).Where("owner_id = ? AND type != ?", userID, KeyTypePrincipal)
  188. return sess.Count(&PublicKey{})
  189. }
  190. // ListPublicKeysBySource returns a list of synchronized public keys for a given user and login source.
  191. func ListPublicKeysBySource(uid, loginSourceID int64) ([]*PublicKey, error) {
  192. keys := make([]*PublicKey, 0, 5)
  193. return keys, db.GetEngine(db.DefaultContext).
  194. Where("owner_id = ? AND login_source_id = ?", uid, loginSourceID).
  195. Find(&keys)
  196. }
  197. // UpdatePublicKeyUpdated updates public key use time.
  198. func UpdatePublicKeyUpdated(id int64) error {
  199. // Check if key exists before update as affected rows count is unreliable
  200. // and will return 0 affected rows if two updates are made at the same time
  201. if cnt, err := db.GetEngine(db.DefaultContext).ID(id).Count(&PublicKey{}); err != nil {
  202. return err
  203. } else if cnt != 1 {
  204. return ErrKeyNotExist{id}
  205. }
  206. _, err := db.GetEngine(db.DefaultContext).ID(id).Cols("updated_unix").Update(&PublicKey{
  207. UpdatedUnix: timeutil.TimeStampNow(),
  208. })
  209. if err != nil {
  210. return err
  211. }
  212. return nil
  213. }
  214. // deletePublicKeys does the actual key deletion but does not update authorized_keys file.
  215. func deletePublicKeys(e db.Engine, keyIDs ...int64) error {
  216. if len(keyIDs) == 0 {
  217. return nil
  218. }
  219. _, err := e.In("id", keyIDs).Delete(new(PublicKey))
  220. return err
  221. }
  222. // PublicKeysAreExternallyManaged returns whether the provided KeyID represents an externally managed Key
  223. func PublicKeysAreExternallyManaged(keys []*PublicKey) ([]bool, error) {
  224. sources := make([]*login.Source, 0, 5)
  225. externals := make([]bool, len(keys))
  226. keyloop:
  227. for i, key := range keys {
  228. if key.LoginSourceID == 0 {
  229. externals[i] = false
  230. continue keyloop
  231. }
  232. var source *login.Source
  233. sourceloop:
  234. for _, s := range sources {
  235. if s.ID == key.LoginSourceID {
  236. source = s
  237. break sourceloop
  238. }
  239. }
  240. if source == nil {
  241. var err error
  242. source, err = login.GetSourceByID(key.LoginSourceID)
  243. if err != nil {
  244. if login.IsErrSourceNotExist(err) {
  245. externals[i] = false
  246. sources[i] = &login.Source{
  247. ID: key.LoginSourceID,
  248. }
  249. continue keyloop
  250. }
  251. return nil, err
  252. }
  253. }
  254. if sshKeyProvider, ok := source.Cfg.(login.SSHKeyProvider); ok && sshKeyProvider.ProvidesSSHKeys() {
  255. // Disable setting SSH keys for this user
  256. externals[i] = true
  257. }
  258. }
  259. return externals, nil
  260. }
  261. // PublicKeyIsExternallyManaged returns whether the provided KeyID represents an externally managed Key
  262. func PublicKeyIsExternallyManaged(id int64) (bool, error) {
  263. key, err := GetPublicKeyByID(id)
  264. if err != nil {
  265. return false, err
  266. }
  267. if key.LoginSourceID == 0 {
  268. return false, nil
  269. }
  270. source, err := login.GetSourceByID(key.LoginSourceID)
  271. if err != nil {
  272. if login.IsErrSourceNotExist(err) {
  273. return false, nil
  274. }
  275. return false, err
  276. }
  277. if sshKeyProvider, ok := source.Cfg.(login.SSHKeyProvider); ok && sshKeyProvider.ProvidesSSHKeys() {
  278. // Disable setting SSH keys for this user
  279. return true, nil
  280. }
  281. return false, nil
  282. }
  283. // DeletePublicKey deletes SSH key information both in database and authorized_keys file.
  284. func DeletePublicKey(doer *user_model.User, id int64) (err error) {
  285. key, err := GetPublicKeyByID(id)
  286. if err != nil {
  287. return err
  288. }
  289. // Check if user has access to delete this key.
  290. if !doer.IsAdmin && doer.ID != key.OwnerID {
  291. return ErrKeyAccessDenied{doer.ID, key.ID, "public"}
  292. }
  293. ctx, committer, err := db.TxContext()
  294. if err != nil {
  295. return err
  296. }
  297. defer committer.Close()
  298. if err = deletePublicKeys(db.GetEngine(ctx), id); err != nil {
  299. return err
  300. }
  301. if err = committer.Commit(); err != nil {
  302. return err
  303. }
  304. committer.Close()
  305. if key.Type == KeyTypePrincipal {
  306. return RewriteAllPrincipalKeys()
  307. }
  308. return RewriteAllPublicKeys()
  309. }
  310. // deleteKeysMarkedForDeletion returns true if ssh keys needs update
  311. func deleteKeysMarkedForDeletion(keys []string) (bool, error) {
  312. // Start session
  313. ctx, committer, err := db.TxContext()
  314. if err != nil {
  315. return false, err
  316. }
  317. defer committer.Close()
  318. sess := db.GetEngine(ctx)
  319. // Delete keys marked for deletion
  320. var sshKeysNeedUpdate bool
  321. for _, KeyToDelete := range keys {
  322. key, err := searchPublicKeyByContentWithEngine(sess, KeyToDelete)
  323. if err != nil {
  324. log.Error("SearchPublicKeyByContent: %v", err)
  325. continue
  326. }
  327. if err = deletePublicKeys(sess, key.ID); err != nil {
  328. log.Error("deletePublicKeys: %v", err)
  329. continue
  330. }
  331. sshKeysNeedUpdate = true
  332. }
  333. if err := committer.Commit(); err != nil {
  334. return false, err
  335. }
  336. return sshKeysNeedUpdate, nil
  337. }
  338. // AddPublicKeysBySource add a users public keys. Returns true if there are changes.
  339. func AddPublicKeysBySource(usr *user_model.User, s *login.Source, sshPublicKeys []string) bool {
  340. var sshKeysNeedUpdate bool
  341. for _, sshKey := range sshPublicKeys {
  342. var err error
  343. found := false
  344. keys := []byte(sshKey)
  345. loop:
  346. for len(keys) > 0 && err == nil {
  347. var out ssh.PublicKey
  348. // We ignore options as they are not relevant to Gitea
  349. out, _, _, keys, err = ssh.ParseAuthorizedKey(keys)
  350. if err != nil {
  351. break loop
  352. }
  353. found = true
  354. marshalled := string(ssh.MarshalAuthorizedKey(out))
  355. marshalled = marshalled[:len(marshalled)-1]
  356. sshKeyName := fmt.Sprintf("%s-%s", s.Name, ssh.FingerprintSHA256(out))
  357. if _, err := AddPublicKey(usr.ID, sshKeyName, marshalled, s.ID); err != nil {
  358. if IsErrKeyAlreadyExist(err) {
  359. log.Trace("AddPublicKeysBySource[%s]: Public SSH Key %s already exists for user", sshKeyName, usr.Name)
  360. } else {
  361. log.Error("AddPublicKeysBySource[%s]: Error adding Public SSH Key for user %s: %v", sshKeyName, usr.Name, err)
  362. }
  363. } else {
  364. log.Trace("AddPublicKeysBySource[%s]: Added Public SSH Key for user %s", sshKeyName, usr.Name)
  365. sshKeysNeedUpdate = true
  366. }
  367. }
  368. if !found && err != nil {
  369. log.Warn("AddPublicKeysBySource[%s]: Skipping invalid Public SSH Key for user %s: %v", s.Name, usr.Name, sshKey)
  370. }
  371. }
  372. return sshKeysNeedUpdate
  373. }
  374. // SynchronizePublicKeys updates a users public keys. Returns true if there are changes.
  375. func SynchronizePublicKeys(usr *user_model.User, s *login.Source, sshPublicKeys []string) bool {
  376. var sshKeysNeedUpdate bool
  377. log.Trace("synchronizePublicKeys[%s]: Handling Public SSH Key synchronization for user %s", s.Name, usr.Name)
  378. // Get Public Keys from DB with current LDAP source
  379. var giteaKeys []string
  380. keys, err := ListPublicKeysBySource(usr.ID, s.ID)
  381. if err != nil {
  382. log.Error("synchronizePublicKeys[%s]: Error listing Public SSH Keys for user %s: %v", s.Name, usr.Name, err)
  383. }
  384. for _, v := range keys {
  385. giteaKeys = append(giteaKeys, v.OmitEmail())
  386. }
  387. // Process the provided keys to remove duplicates and name part
  388. var providedKeys []string
  389. for _, v := range sshPublicKeys {
  390. sshKeySplit := strings.Split(v, " ")
  391. if len(sshKeySplit) > 1 {
  392. key := strings.Join(sshKeySplit[:2], " ")
  393. if !util.ExistsInSlice(key, providedKeys) {
  394. providedKeys = append(providedKeys, key)
  395. }
  396. }
  397. }
  398. // Check if Public Key sync is needed
  399. if util.IsEqualSlice(giteaKeys, providedKeys) {
  400. log.Trace("synchronizePublicKeys[%s]: Public Keys are already in sync for %s (Source:%v/DB:%v)", s.Name, usr.Name, len(providedKeys), len(giteaKeys))
  401. return false
  402. }
  403. log.Trace("synchronizePublicKeys[%s]: Public Key needs update for user %s (Source:%v/DB:%v)", s.Name, usr.Name, len(providedKeys), len(giteaKeys))
  404. // Add new Public SSH Keys that doesn't already exist in DB
  405. var newKeys []string
  406. for _, key := range providedKeys {
  407. if !util.ExistsInSlice(key, giteaKeys) {
  408. newKeys = append(newKeys, key)
  409. }
  410. }
  411. if AddPublicKeysBySource(usr, s, newKeys) {
  412. sshKeysNeedUpdate = true
  413. }
  414. // Mark keys from DB that no longer exist in the source for deletion
  415. var giteaKeysToDelete []string
  416. for _, giteaKey := range giteaKeys {
  417. if !util.ExistsInSlice(giteaKey, providedKeys) {
  418. log.Trace("synchronizePublicKeys[%s]: Marking Public SSH Key for deletion for user %s: %v", s.Name, usr.Name, giteaKey)
  419. giteaKeysToDelete = append(giteaKeysToDelete, giteaKey)
  420. }
  421. }
  422. // Delete keys from DB that no longer exist in the source
  423. needUpd, err := deleteKeysMarkedForDeletion(giteaKeysToDelete)
  424. if err != nil {
  425. log.Error("synchronizePublicKeys[%s]: Error deleting Public Keys marked for deletion for user %s: %v", s.Name, usr.Name, err)
  426. }
  427. if needUpd {
  428. sshKeysNeedUpdate = true
  429. }
  430. return sshKeysNeedUpdate
  431. }