@@ -17,6 +17,7 @@ import (
1717 "os"
1818 "os/exec"
1919 "path/filepath"
20+ "reflect"
2021 "strconv"
2122 "strings"
2223 "sync"
@@ -33,9 +34,21 @@ import (
3334 gossh "golang.org/x/crypto/ssh"
3435)
3536
36- type contextKey string
37-
38- const giteaKeyID = contextKey ("gitea-key-id" )
37+ // The ssh auth overall works like this:
38+ // NewServerConn:
39+ // serverHandshake+serverAuthenticate:
40+ // PublicKeyCallback:
41+ // PublicKeyHandler (our code):
42+ // clear(ctx.Permissions) and set ctx.Permissions.giteaKeyID = keyID
43+ // pubKey.Verify
44+ // return ctx.Permissions // only reaches here, the pub key is really authenticated
45+ // set conn.Permissions from serverAuthenticate
46+ // sessionHandler(conn)
47+ //
48+ // Then sessionHandler should only use the "verified keyID" from the conn.
49+ // Otherwise, if a users provides 2 keys A and B, if A succeeds to authenticate, sessionHandler will see B's keyID
50+
51+ const giteaPermissionExtensionKeyID = "gitea-perm-ext-key-id"
3952
4053func getExitStatusFromError (err error ) int {
4154 if err == nil {
@@ -61,8 +74,26 @@ func getExitStatusFromError(err error) int {
6174 return waitStatus .ExitStatus ()
6275}
6376
77+ type sessionPartial struct {
78+ sync.Mutex
79+ gossh.Channel
80+ conn * gossh.ServerConn
81+ }
82+
83+ func ptr [T any ](intf any ) * T {
84+ // https://pkg.go.dev/unsafe#Pointer
85+ // (1) Conversion of a *T1 to Pointer to *T2.
86+ // Provided that T2 is no larger than T1 and that the two share an equivalent memory layout,
87+ // this conversion allows reinterpreting data of one type as data of another type.
88+ v := reflect .ValueOf (intf )
89+ p := v .UnsafePointer ()
90+ return (* T )(p )
91+ }
92+
6493func sessionHandler (session ssh.Session ) {
65- keyID := fmt .Sprintf ("%d" , session .Context ().Value (giteaKeyID ).(int64 ))
94+ // it can't use session.Permissions() because it only use the ctx one, so we must use the original ssh conn
95+ sshConn := ptr [sessionPartial ](session )
96+ keyID := sshConn .conn .Permissions .Extensions [giteaPermissionExtensionKeyID ]
6697
6798 command := session .RawCommand ()
6899
@@ -164,6 +195,12 @@ func sessionHandler(session ssh.Session) {
164195}
165196
166197func publicKeyHandler (ctx ssh.Context , key ssh.PublicKey ) bool {
198+ setPermExt := func (keyID int64 ) {
199+ ctx .Permissions ().Permissions .Extensions = map [string ]string {
200+ giteaPermissionExtensionKeyID : fmt .Sprint (keyID ),
201+ }
202+ }
203+
167204 if log .IsDebug () { // <- FingerprintSHA256 is kinda expensive so only calculate it if necessary
168205 log .Debug ("Handle Public Key: Fingerprint: %s from %s" , gossh .FingerprintSHA256 (key ), ctx .RemoteAddr ())
169206 }
@@ -238,8 +275,7 @@ func publicKeyHandler(ctx ssh.Context, key ssh.PublicKey) bool {
238275 if log .IsDebug () { // <- FingerprintSHA256 is kinda expensive so only calculate it if necessary
239276 log .Debug ("Successfully authenticated: %s Certificate Fingerprint: %s Principal: %s" , ctx .RemoteAddr (), gossh .FingerprintSHA256 (key ), principal )
240277 }
241- ctx .SetValue (giteaKeyID , pkey .ID )
242-
278+ setPermExt (pkey .ID )
243279 return true
244280 }
245281
@@ -266,8 +302,7 @@ func publicKeyHandler(ctx ssh.Context, key ssh.PublicKey) bool {
266302 if log .IsDebug () { // <- FingerprintSHA256 is kinda expensive so only calculate it if necessary
267303 log .Debug ("Successfully authenticated: %s Public Key Fingerprint: %s" , ctx .RemoteAddr (), gossh .FingerprintSHA256 (key ))
268304 }
269- ctx .SetValue (giteaKeyID , pkey .ID )
270-
305+ setPermExt (pkey .ID )
271306 return true
272307}
273308
0 commit comments