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

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