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

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