Created
January 24, 2021 00:02
-
-
Save 9072997/d9596aacccb63c77e15ecdcd2565617b to your computer and use it in GitHub Desktop.
Re-produce what I think is a bug in golang.org/x/crypto/ssh
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"fmt" | |
"log" | |
"net" | |
"time" | |
"golang.org/x/crypto/ssh" | |
) | |
const HostKey = ` | |
-----BEGIN OPENSSH PRIVATE KEY----- | |
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 | |
-----END OPENSSH PRIVATE KEY----- | |
` | |
// AllowLogin, must be set, is called when gssapi-with-mic | |
// authentication is selected (RFC 4462 section 3). The srcName is from the | |
// results of the GSS-API authentication. The format is username@DOMAIN. | |
// GSSAPI just guarantees to the server who the user is, but not if they can log in, and with what permissions. | |
// This callback is called after the user identity is established with GSSAPI to decide if the user can login with | |
// which permissions. If the user is allowed to login, it should return a nil error. | |
func AllowLogin( | |
conn ssh.ConnMetadata, srcName string, | |
) ( | |
*ssh.Permissions, error, | |
) { | |
fmt.Printf("AllowLogin(__, %s)\n", srcName) | |
fmt.Println(conn) | |
return nil, nil | |
} | |
func server() { | |
sshConfig := &ssh.ServerConfig{ | |
GSSAPIWithMICConfig: &ssh.GSSAPIWithMICConfig{ | |
AllowLogin: AllowLogin, | |
Server: &GSSAPIServer{}, | |
}, | |
} | |
// use host key from config file | |
hostKeyData := []byte(HostKey) | |
hostKey, err := ssh.ParsePrivateKey(hostKeyData) | |
if err != nil { | |
panic(err) | |
} | |
sshConfig.AddHostKey(hostKey) | |
// start a TCP server on the IP and port from the config | |
tcpListener, err := net.ListenTCP("tcp", &net.TCPAddr{ | |
IP: nil, | |
Port: 2222, | |
}) | |
if err != nil { | |
panic(err) | |
} | |
// accept connection | |
tcpConn, err := tcpListener.Accept() | |
if err != nil { | |
panic(err) | |
} | |
// upgrade the tcp connection to an ssh connection | |
sshConn, _, _, err := ssh.NewServerConn(tcpConn, sshConfig) | |
if err != nil { | |
panic(err) | |
} | |
log.Printf("New connection from %s", sshConn.RemoteAddr()) | |
// normally we would do something with the connection here, but this is | |
// just a demo | |
time.Sleep(10 * time.Second) | |
} | |
func client() { | |
sshConn, err := ssh.Dial( | |
"tcp", | |
"localhost:2222", | |
&ssh.ClientConfig{ | |
User: "UsernameSuppliedByClient", | |
Auth: []ssh.AuthMethod{ | |
ssh.GSSAPIWithMICAuthMethod(&GSSAPIClient{}, "HostnameSuppliedByClient"), | |
}, | |
HostKeyCallback: ssh.InsecureIgnoreHostKey(), | |
}, | |
) | |
if err != nil { | |
panic(err) | |
} | |
time.Sleep(10 * time.Second) | |
sshConn.Close() | |
} | |
func main() { | |
go server() | |
time.Sleep(time.Second) | |
client() | |
} | |
//////////////////////////////////////////////////////////////////////////// | |
type GSSAPIServer struct { | |
counter byte | |
} | |
// AcceptSecContext allows a remotely initiated security context between the application | |
// and a remote peer to be established by the ssh client. The routine may return a | |
// outputToken which should be transferred to the ssh client, | |
// where the ssh client will present it to InitSecContext. | |
// If no token need be sent, AcceptSecContext will indicate this | |
// by setting the needContinue to false. To | |
// complete the context establishment, one or more reply tokens may be | |
// required from the ssh client. if so, AcceptSecContext | |
// will return a needContinue which is true, in which case it | |
// should be called again when the reply token is received from the ssh | |
// client, passing the token to AcceptSecContext via the | |
// token parameters. | |
// The srcName return value is the authenticated username. | |
// See RFC 2743 section 2.2.2 and RFC 4462 section 3.4. | |
func (g *GSSAPIServer) AcceptSecContext( | |
token []byte, | |
) ( | |
outputToken []byte, srcName string, needContinue bool, err error, | |
) { | |
g.counter++ | |
outputToken = []byte{token[0] - 50, g.counter} | |
srcName = "UsernameDeterminedByServer" | |
needContinue = true | |
err = nil | |
fmt.Printf("SERVER: AcceptSecContext(%v): %v, %s, %t, %v\n", | |
token, outputToken, srcName, needContinue, err, | |
) | |
return | |
} | |
// VerifyMIC verifies that a cryptographic MIC, contained in the token parameter, | |
// fits the supplied message is received from the ssh client. | |
// See RFC 2743 section 2.3.2. | |
func (g *GSSAPIServer) VerifyMIC(micField []byte, micToken []byte) error { | |
fmt.Printf("SERVER: VerifyMIC(%v, %v)\n", micField, micToken) | |
return nil | |
} | |
// Whenever possible, it should be possible for | |
// DeleteSecContext() calls to be successfully processed even | |
// if other calls cannot succeed, thereby enabling context-related | |
// resources to be released. | |
// In addition to deleting established security contexts, | |
// gss_delete_sec_context must also be able to delete "half-built" | |
// security contexts resulting from an incomplete sequence of | |
// InitSecContext()/AcceptSecContext() calls. | |
// See RFC 2743 section 2.2.3. | |
func (g *GSSAPIServer) DeleteSecContext() error { | |
fmt.Println("SERVER: DeleteSecContext()") | |
return nil | |
} | |
//////////////////////////////////////////////////////////////////////////// | |
type GSSAPIClient struct { | |
counter byte | |
} | |
// InitSecContext initiates the establishment of a security context for GSS-API between the | |
// ssh client and ssh server. Initially the token parameter should be specified as nil. | |
// The routine may return a outputToken which should be transferred to | |
// the ssh server, where the ssh server will present it to | |
// AcceptSecContext. If no token need be sent, InitSecContext will indicate this by setting | |
// needContinue to false. To complete the context | |
// establishment, one or more reply tokens may be required from the ssh | |
// server;if so, InitSecContext will return a needContinue which is true. | |
// In this case, InitSecContext should be called again when the | |
// reply token is received from the ssh server, passing the reply | |
// token to InitSecContext via the token parameters. | |
// See RFC 2743 section 2.2.1 and RFC 4462 section 3.4. | |
func (g *GSSAPIClient) InitSecContext( | |
target string, token []byte, isGSSDelegCreds bool, | |
) ( | |
outputToken []byte, needContinue bool, err error, | |
) { | |
if token == nil { | |
g.counter = 100 | |
outputToken = []byte{g.counter} | |
needContinue = true | |
err = nil | |
} else { | |
g.counter++ | |
if g.counter == 110 { | |
outputToken = nil | |
needContinue = false | |
err = nil | |
} else { | |
outputToken = []byte{g.counter} | |
needContinue = true | |
err = nil | |
} | |
} | |
fmt.Printf("CLIENT: InitSecContext(%s, %v, %t): %v %t %v\n", | |
target, token, isGSSDelegCreds, outputToken, needContinue, err, | |
) | |
return | |
} | |
// GetMIC generates a cryptographic MIC for the SSH2 message, and places | |
// the MIC in a token for transfer to the ssh server. | |
// The contents of the MIC field are obtained by calling GSS_GetMIC() | |
// over the following, using the GSS-API context that was just | |
// established: | |
// string session identifier | |
// byte SSH_MSG_USERAUTH_REQUEST | |
// string user name | |
// string service | |
// string "gssapi-with-mic" | |
// See RFC 2743 section 2.3.1 and RFC 4462 3.5. | |
func (g *GSSAPIClient) GetMIC(micFiled []byte) ([]byte, error) { | |
fmt.Printf("CLIENT: GetMIC(%v)\n", micFiled) | |
return []byte{90, 72, 99, 7}, nil | |
} | |
// Whenever possible, it should be possible for | |
// DeleteSecContext() calls to be successfully processed even | |
// if other calls cannot succeed, thereby enabling context-related | |
// resources to be released. | |
// In addition to deleting established security contexts, | |
// gss_delete_sec_context must also be able to delete "half-built" | |
// security contexts resulting from an incomplete sequence of | |
// InitSecContext()/AcceptSecContext() calls. | |
// See RFC 2743 section 2.2.3. | |
func (g *GSSAPIClient) DeleteSecContext() error { | |
fmt.Println("CLIENT: DeleteSecContext()") | |
return nil | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment