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

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