// Copyright 2011 The Go Authors. All rights reserved.// Use of this source code is governed by a BSD-style// license that can be found in the LICENSE file.package sshimport ()// The Permissions type holds fine-grained permissions that are// specific to a user or a specific authentication method for a user.// The Permissions value for a successful authentication attempt is// available in ServerConn, so it can be used to pass information from// the user-authentication phase to the application layer.typePermissionsstruct {// CriticalOptions indicate restrictions to the default // permissions, and are typically used in conjunction with // user certificates. The standard for SSH certificates // defines "force-command" (only allow the given command to // execute) and "source-address" (only allow connections from // the given address). The SSH package currently only enforces // the "source-address" critical option. It is up to server // implementations to enforce other critical options, such as // "force-command", by checking them after the SSH handshake // is successful. In general, SSH servers should reject // connections that specify critical options that are unknown // or not supported. CriticalOptions map[string]string// Extensions are extra functionality that the server may // offer on authenticated connections. Lack of support for an // extension does not preclude authenticating a user. Common // extensions are "permit-agent-forwarding", // "permit-X11-forwarding". The Go SSH library currently does // not act on any extension, and it is up to server // implementations to honor them. Extensions can be used to // pass data from the authentication callbacks to the server // application layer. Extensions map[string]string// ExtraData allows to store user defined data. ExtraData map[any]any}typeGSSAPIWithMICConfigstruct {// 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. AllowLogin func(conn ConnMetadata, srcName string) (*Permissions, error)// Server must be set. It's the implementation // of the GSSAPIServer interface. See GSSAPIServer interface for details. Server GSSAPIServer}// SendAuthBanner implements [ServerPreAuthConn].func ( *connection) ( string) error {return .transport.writePacket(Marshal(&userAuthBannerMsg{Message: , }))}func (*connection) () {}// ServerPreAuthConn is the interface available on an incoming server// connection before authentication has completed.typeServerPreAuthConninterface { unexportedMethodForFutureProofing() // permits growing ServerPreAuthConn safely later, ala testing.TBConnMetadata// SendAuthBanner sends a banner message to the client. // It returns an error once the authentication phase has ended.SendAuthBanner(string) error}// ServerConfig holds server specific configuration data.typeServerConfigstruct {// Config contains configuration shared between client and server.Config// PublicKeyAuthAlgorithms specifies the supported client public key // authentication algorithms. Note that this should not include certificate // types since those use the underlying algorithm. This list is sent to the // client if it supports the server-sig-algs extension. Order is irrelevant. // If unspecified then a default set of algorithms is used. PublicKeyAuthAlgorithms []string hostKeys []Signer// NoClientAuth is true if clients are allowed to connect without // authenticating. // To determine NoClientAuth at runtime, set NoClientAuth to true // and the optional NoClientAuthCallback to a non-nil value. NoClientAuth bool// NoClientAuthCallback, if non-nil, is called when a user // attempts to authenticate with auth method "none". // NoClientAuth must also be set to true for this be used, or // this func is unused. NoClientAuthCallback func(ConnMetadata) (*Permissions, error)// MaxAuthTries specifies the maximum number of authentication attempts // permitted per connection. If set to a negative number, the number of // attempts are unlimited. If set to zero, the number of attempts are limited // to 6. MaxAuthTries int// PasswordCallback, if non-nil, is called when a user // attempts to authenticate using a password. PasswordCallback func(conn ConnMetadata, password []byte) (*Permissions, error)// PublicKeyCallback, if non-nil, is called when a client // offers a public key for authentication. It must return a nil error // if the given public key can be used to authenticate the // given user. For example, see CertChecker.Authenticate. A // call to this function does not guarantee that the key // offered is in fact used to authenticate. To record any data // depending on the public key, store it inside a // Permissions.Extensions entry. PublicKeyCallback func(conn ConnMetadata, key PublicKey) (*Permissions, error)// VerifiedPublicKeyCallback, if non-nil, is called after a client // successfully confirms having control over a key that was previously // approved by PublicKeyCallback. The permissions object passed to the // callback is the one returned by PublicKeyCallback for the given public // key and its ownership is transferred to the callback. The returned // Permissions object can be the same object, optionally modified, or a // completely new object. If VerifiedPublicKeyCallback is non-nil, // PublicKeyCallback is not allowed to return a PartialSuccessError, which // can instead be returned by VerifiedPublicKeyCallback. // // VerifiedPublicKeyCallback does not affect which authentication methods // are included in the list of methods that can be attempted by the client. VerifiedPublicKeyCallback func(conn ConnMetadata, key PublicKey, permissions *Permissions, signatureAlgorithm string) (*Permissions, error)// KeyboardInteractiveCallback, if non-nil, is called when // keyboard-interactive authentication is selected (RFC // 4256). The client object's Challenge function should be // used to query the user. The callback may offer multiple // Challenge rounds. To avoid information leaks, the client // should be presented a challenge even if the user is // unknown. KeyboardInteractiveCallback func(conn ConnMetadata, client KeyboardInteractiveChallenge) (*Permissions, error)// AuthLogCallback, if non-nil, is called to log all authentication // attempts. AuthLogCallback func(conn ConnMetadata, method string, err error)// PreAuthConnCallback, if non-nil, is called upon receiving a new connection // before any authentication has started. The provided ServerPreAuthConn // can be used at any time before authentication is complete, including // after this callback has returned. PreAuthConnCallback func(ServerPreAuthConn)// ServerVersion is the version identification string to announce in // the public handshake. // If empty, a reasonable default is used. // Note that RFC 4253 section 4.2 requires that this string start with // "SSH-2.0-". ServerVersion string// BannerCallback, if present, is called and the return string is sent to // the client after key exchange completed but before authentication. BannerCallback func(conn ConnMetadata) string// GSSAPIWithMICConfig includes gssapi server and callback, which if both non-nil, is used // when gssapi-with-mic authentication is selected (RFC 4462 section 3). GSSAPIWithMICConfig *GSSAPIWithMICConfig}// AddHostKey adds a private key as a host key. If an existing host// key exists with the same public key format, it is replaced. Each server// config must have at least one host key.func ( *ServerConfig) ( Signer) {for , := range .hostKeys {if .PublicKey().Type() == .PublicKey().Type() { .hostKeys[] = return } } .hostKeys = append(.hostKeys, )}// cachedPubKey contains the results of querying whether a public key is// acceptable for a user. This is a FIFO cache.type cachedPubKey struct { user string pubKeyData []byte result error perms *Permissions}// maxCachedPubKeys is the number of cache entries we store.//// Due to consistent misuse of the PublicKeyCallback API, we have reduced this// to 1, such that the only key in the cache is the most recently seen one. This// forces the behavior that the last call to PublicKeyCallback will always be// with the key that is used for authentication.const maxCachedPubKeys = 1// pubKeyCache caches tests for public keys. Since SSH clients// will query whether a public key is acceptable before attempting to// authenticate with it, we end up with duplicate queries for public// key validity. The cache only applies to a single ServerConn.type pubKeyCache struct { keys []cachedPubKey}// get returns the result for a given user/algo/key tuple.func ( *pubKeyCache) ( string, []byte) (cachedPubKey, bool) {for , := range .keys {if .user == && bytes.Equal(.pubKeyData, ) {return , true } }returncachedPubKey{}, false}// add adds the given tuple to the cache.func ( *pubKeyCache) ( cachedPubKey) {iflen(.keys) >= maxCachedPubKeys { .keys = .keys[1:] } .keys = append(.keys, )}// ServerConn is an authenticated SSH connection, as seen from the// servertypeServerConnstruct {Conn// If the succeeding authentication callback returned a // non-nil Permissions pointer, it is stored here. Permissions *Permissions}// NewServerConn starts a new SSH server with c as the underlying// transport. It starts with a handshake and, if the handshake is// unsuccessful, it closes the connection and returns an error. The// Request and NewChannel channels must be serviced, or the connection// will hang.//// The returned error may be of type *ServerAuthError for// authentication errors.func ( net.Conn, *ServerConfig) (*ServerConn, <-chanNewChannel, <-chan *Request, error) { := * .SetDefaults()if .MaxAuthTries == 0 { .MaxAuthTries = 6 }iflen(.PublicKeyAuthAlgorithms) == 0 { .PublicKeyAuthAlgorithms = defaultPubKeyAuthAlgos } else {for , := range .PublicKeyAuthAlgorithms {if !slices.Contains(SupportedAlgorithms().PublicKeyAuths, ) && !slices.Contains(InsecureAlgorithms().PublicKeyAuths, ) { .Close()returnnil, nil, nil, fmt.Errorf("ssh: unsupported public key authentication algorithm %s", ) } } } := &connection{sshConn: sshConn{conn: }, } , := .serverHandshake(&)if != nil { .Close()returnnil, nil, nil, }return &ServerConn{, }, .mux.incomingChannels, .mux.incomingRequests, nil}// signAndMarshal signs the data with the appropriate algorithm,// and serializes the result in SSH wire format. algo is the negotiate// algorithm and may be a certificate type.func signAndMarshal( AlgorithmSigner, io.Reader, []byte, string) ([]byte, error) { , := .SignWithAlgorithm(, , underlyingAlgo())if != nil {returnnil, }returnMarshal(), nil}// handshake performs key exchange and user authentication.func ( *connection) ( *ServerConfig) (*Permissions, error) {iflen(.hostKeys) == 0 {returnnil, errors.New("ssh: server has no host keys") }if !.NoClientAuth && .PasswordCallback == nil && .PublicKeyCallback == nil && .KeyboardInteractiveCallback == nil && (.GSSAPIWithMICConfig == nil || .GSSAPIWithMICConfig.AllowLogin == nil || .GSSAPIWithMICConfig.Server == nil) {returnnil, errors.New("ssh: no authentication methods configured but NoClientAuth is also false") }if .ServerVersion != "" { .serverVersion = []byte(.ServerVersion) } else { .serverVersion = []byte(packageVersion) }varerror .clientVersion, = exchangeVersions(.sshConn.conn, .serverVersion)if != nil {returnnil, } := newTransport(.sshConn.conn, .Rand, false/* not client */) .transport = newServerTransport(, .clientVersion, .serverVersion, )if := .transport.waitSession(); != nil {returnnil, }// We just did the key change, so the session ID is established. .sessionID = .transport.getSessionID() .algorithms = .transport.getAlgorithms()var []byteif , = .transport.readPacket(); != nil {returnnil, }varserviceRequestMsgif = Unmarshal(, &); != nil {returnnil, }if .Service != serviceUserAuth {returnnil, errors.New("ssh: requested service '" + .Service + "' before authenticating") } := serviceAcceptMsg{Service: serviceUserAuth, }if := .transport.writePacket(Marshal(&)); != nil {returnnil, } , := .serverAuthenticate()if != nil {returnnil, } .mux = newMux(.transport)return , }func checkSourceAddress( net.Addr, string) error {if == nil {returnerrors.New("ssh: no address known for client, but source-address match required") } , := .(*net.TCPAddr)if ! {returnfmt.Errorf("ssh: remote address %v is not an TCP address when checking source-address match", ) }for , := rangestrings.Split(, ",") {if := net.ParseIP(); != nil {if .Equal(.IP) {returnnil } } else { , , := net.ParseCIDR()if != nil {returnfmt.Errorf("ssh: error parsing source-address restriction %q: %v", , ) }if .Contains(.IP) {returnnil } } }returnfmt.Errorf("ssh: remote address %v is not allowed because of source-address restriction", )}func gssExchangeToken( *GSSAPIWithMICConfig, []byte, *connection, []byte, userAuthRequestMsg) ( error, *Permissions, error) { := .Serverdefer .DeleteSecContext()varstringfor {var ( []bytebool ) , , , = .AcceptSecContext()if != nil {return , nil, nil }iflen() != 0 {if := .transport.writePacket(Marshal(&userAuthGSSAPIToken{Token: , })); != nil {returnnil, nil, } }if ! {break } , := .transport.readPacket()if != nil {returnnil, nil, } := &userAuthGSSAPIToken{}if := Unmarshal(, ); != nil {returnnil, nil, } = .Token } , := .transport.readPacket()if != nil {returnnil, nil, } := &userAuthGSSAPIMIC{}if := Unmarshal(, ); != nil {returnnil, nil, } := buildMIC(string(), .User, .Service, .Method)if := .VerifyMIC(, .MIC); != nil {return , nil, nil } , = .AllowLogin(, )return , , nil}// isAlgoCompatible checks if the signature format is compatible with the// selected algorithm taking into account edge cases that occur with old// clients.func isAlgoCompatible(, string) bool {// Compatibility for old clients. // // For certificate authentication with OpenSSH 7.2-7.7 signature format can // be rsa-sha2-256 or rsa-sha2-512 for the algorithm // ssh-rsa-cert-v01@openssh.com. // // With gpg-agent < 2.2.6 the algorithm can be rsa-sha2-256 or rsa-sha2-512 // for signature format ssh-rsa.ifisRSA() && isRSA() {returntrue }// Standard case: the underlying algorithm must match the signature format.returnunderlyingAlgo() == }// ServerAuthError represents server authentication errors and is// sometimes returned by NewServerConn. It appends any authentication// errors that may occur, and is returned if all of the authentication// methods provided by the user failed to authenticate.typeServerAuthErrorstruct {// Errors contains authentication errors returned by the authentication // callback methods. The first entry is typically ErrNoAuth. Errors []error}func ( ServerAuthError) () string {var []stringfor , := range .Errors { = append(, .Error()) }return"[" + strings.Join(, ", ") + "]"}// ServerAuthCallbacks defines server-side authentication callbacks.typeServerAuthCallbacksstruct {// PasswordCallback behaves like [ServerConfig.PasswordCallback]. PasswordCallback func(conn ConnMetadata, password []byte) (*Permissions, error)// PublicKeyCallback behaves like [ServerConfig.PublicKeyCallback]. PublicKeyCallback func(conn ConnMetadata, key PublicKey) (*Permissions, error)// KeyboardInteractiveCallback behaves like [ServerConfig.KeyboardInteractiveCallback]. KeyboardInteractiveCallback func(conn ConnMetadata, client KeyboardInteractiveChallenge) (*Permissions, error)// GSSAPIWithMICConfig behaves like [ServerConfig.GSSAPIWithMICConfig]. GSSAPIWithMICConfig *GSSAPIWithMICConfig}// PartialSuccessError can be returned by any of the [ServerConfig]// authentication callbacks to indicate to the client that authentication has// partially succeeded, but further steps are required.typePartialSuccessErrorstruct {// Next defines the authentication callbacks to apply to further steps. The // available methods communicated to the client are based on the non-nil // ServerAuthCallbacks fields. Next ServerAuthCallbacks}func ( *PartialSuccessError) () string {return"ssh: authenticated with partial success"}// ErrNoAuth is the error value returned if no// authentication method has been passed yet. This happens as a normal// part of the authentication loop, since the client first tries// 'none' authentication to discover available methods.// It is returned in ServerAuthError.Errors from NewServerConn.varErrNoAuth = errors.New("ssh: no auth passed yet")// BannerError is an error that can be returned by authentication handlers in// ServerConfig to send a banner message to the client.typeBannerErrorstruct { Err error Message string}func ( *BannerError) () error {return .Err}func ( *BannerError) () string {if .Err == nil {return .Message }return .Err.Error()}func ( *connection) ( *ServerConfig) (*Permissions, error) {if .PreAuthConnCallback != nil { .PreAuthConnCallback() } := .transport.getSessionID()varpubKeyCachevar *Permissions := 0 := 0var []errorvarbool := false// Set the initial authentication callbacks from the config. They can be // changed if a PartialSuccessError is returned. := ServerAuthCallbacks{PasswordCallback: .PasswordCallback,PublicKeyCallback: .PublicKeyCallback,KeyboardInteractiveCallback: .KeyboardInteractiveCallback,GSSAPIWithMICConfig: .GSSAPIWithMICConfig, }:for {if >= .MaxAuthTries && .MaxAuthTries > 0 { := &disconnectMsg{Reason: 2,Message: "too many authentication failures", }if := .transport.writePacket(Marshal()); != nil {returnnil, } = append(, )returnnil, &ServerAuthError{Errors: } }varuserAuthRequestMsgif , := .transport.readPacket(); != nil {if == io.EOF {returnnil, &ServerAuthError{Errors: } }returnnil, } elseif = Unmarshal(, &); != nil {returnnil, }if .Service != serviceSSH {returnnil, errors.New("ssh: client attempted to negotiate for unknown service: " + .Service) }if .user != .User && {returnnil, fmt.Errorf("ssh: client changed the user after a partial success authentication, previous user %q, current user %q", .user, .User) } .user = .Userif ! && .BannerCallback != nil { = trueif := .BannerCallback(); != "" {if := .SendAuthBanner(); != nil {returnnil, } } } = nil := ErrNoAuthswitch .Method {case"none": ++// We don't allow none authentication after a partial success // response.if .NoClientAuth && ! {if .NoClientAuthCallback != nil { , = .NoClientAuthCallback() } else { = nil } }case"password":if .PasswordCallback == nil { = errors.New("ssh: password auth not configured")break } := .Payloadiflen() < 1 || [0] != 0 {returnnil, parseError(msgUserAuthRequest) } = [1:] , , := parseString()if ! || len() > 0 {returnnil, parseError(msgUserAuthRequest) } , = .PasswordCallback(, )case"keyboard-interactive":if .KeyboardInteractiveCallback == nil { = errors.New("ssh: keyboard-interactive auth not configured")break } := &sshClientKeyboardInteractive{} , = .KeyboardInteractiveCallback(, .Challenge)case"publickey":if .PublicKeyCallback == nil { = errors.New("ssh: publickey auth not configured")break } := .Payloadiflen() < 1 {returnnil, parseError(msgUserAuthRequest) } := [0] == 0 = [1:] , , := parseString()if ! {returnnil, parseError(msgUserAuthRequest) } := string()if !slices.Contains(.PublicKeyAuthAlgorithms, underlyingAlgo()) { = fmt.Errorf("ssh: algorithm %q not accepted", )break } , , := parseString()if ! {returnnil, parseError(msgUserAuthRequest) } , := ParsePublicKey()if != nil {returnnil, } , := .get(.user, )if ! { .user = .user .pubKeyData = .perms, .result = .PublicKeyCallback(, ) , := .result.(*PartialSuccessError)if && .VerifiedPublicKeyCallback != nil {returnnil, errors.New("ssh: invalid library usage: PublicKeyCallback must not return partial success when VerifiedPublicKeyCallback is defined") }if (.result == nil || ) && .perms != nil && .perms.CriticalOptions != nil && .perms.CriticalOptions[sourceAddressCriticalOption] != "" {if := checkSourceAddress( .RemoteAddr(), .perms.CriticalOptions[sourceAddressCriticalOption]); != nil { .result = } } .add() }if {// The client can query if the given public key // would be okay.iflen() > 0 {returnnil, parseError(msgUserAuthRequest) } , := .result.(*PartialSuccessError)if .result == nil || { := userAuthPubKeyOkMsg{Algo: ,PubKey: , }if = .transport.writePacket(Marshal(&)); != nil {returnnil, }continue } = .result } else { , , := parseSignature()if ! || len() > 0 {returnnil, parseError(msgUserAuthRequest) }// Ensure the declared public key algo is compatible with the // decoded one. This check will ensure we don't accept e.g. // ssh-rsa-cert-v01@openssh.com algorithm with ssh-rsa public // key type. The algorithm and public key type must be // consistent: both must be certificate algorithms, or neither.if !slices.Contains(algorithmsForKeyFormat(.Type()), ) { = fmt.Errorf("ssh: public key type %q not compatible with selected algorithm %q", .Type(), )break }// Ensure the public key algo and signature algo // are supported. Compare the private key // algorithm name that corresponds to algo with // sig.Format. This is usually the same, but // for certs, the names differ.if !slices.Contains(.PublicKeyAuthAlgorithms, .Format) { = fmt.Errorf("ssh: algorithm %q not accepted", .Format)break }if !isAlgoCompatible(, .Format) { = fmt.Errorf("ssh: signature %q not compatible with selected algorithm %q", .Format, )break } := buildDataSignedForAuth(, , , )if := .Verify(, ); != nil {returnnil, } = .result = .permsif == nil && .VerifiedPublicKeyCallback != nil {// Only call VerifiedPublicKeyCallback after the key has been accepted // and successfully verified. If authErr is non-nil, the key is not // considered verified and the callback must not run. , = .VerifiedPublicKeyCallback(, , , ) } }case"gssapi-with-mic":if .GSSAPIWithMICConfig == nil { = errors.New("ssh: gssapi-with-mic auth not configured")break } := .GSSAPIWithMICConfig , := parseGSSAPIPayload(.Payload)if != nil {returnnil, parseError(msgUserAuthRequest) }// OpenSSH supports Kerberos V5 mechanism only for GSS-API authentication.if .N == 0 { = fmt.Errorf("ssh: Mechanism negotiation is not supported")break }varuint32 := falsefor = 0; < .N; ++ {if .OIDS[].Equal(krb5Mesh) { = truebreak } }if ! { = fmt.Errorf("ssh: GSSAPI authentication must use the Kerberos V5 mechanism")break }// Initial server response, see RFC 4462 section 3.3.if := .transport.writePacket(Marshal(&userAuthGSSAPIResponse{SupportMech: krb5OID, })); != nil {returnnil, }// Exchange token, see RFC 4462 section 3.4. , := .transport.readPacket()if != nil {returnnil, } := &userAuthGSSAPIToken{}if := Unmarshal(, ); != nil {returnnil, } , , = gssExchangeToken(, .Token, , , )if != nil {returnnil, }default: = fmt.Errorf("ssh: unknown method %q", .Method) } = append(, )if .AuthLogCallback != nil { .AuthLogCallback(, .Method, ) }var *BannerErroriferrors.As(, &) {if .Message != "" {if := .SendAuthBanner(.Message); != nil {returnnil, } } }if == nil {break }varuserAuthFailureMsgif , := .(*PartialSuccessError); {// After a partial success error we don't allow changing the user // name and execute the NoClientAuthCallback. = true// In case a partial success is returned, the server may send // a new set of authentication methods. = .Next// Reset pubkey cache, as the new PublicKeyCallback might // accept a different set of public keys. = pubKeyCache{}// Send back a partial success message to the user. .PartialSuccess = true } else {// Allow initial attempt of 'none' without penalty.if > 0 || .Method != "none" || != 1 { ++ }if .MaxAuthTries > 0 && >= .MaxAuthTries {// If we have hit the max attempts, don't bother sending the // final SSH_MSG_USERAUTH_FAILURE message, since there are // no more authentication methods which can be attempted, // and this message may cause the client to re-attempt // authentication while we send the disconnect message. // Continue, and trigger the disconnect at the start of // the loop. // // The SSH specification is somewhat confusing about this, // RFC 4252 Section 5.1 requires each authentication failure // be responded to with a respective SSH_MSG_USERAUTH_FAILURE // message, but Section 4 says the server should disconnect // after some number of attempts, but it isn't explicit which // message should take precedence (i.e. should there be a failure // message than a disconnect message, or if we are going to // disconnect, should we only send that message.) // // Either way, OpenSSH disconnects immediately after the last // failed authentication attempt, and given they are typically // considered the golden implementation it seems reasonable // to match that behavior.continue } }if .PasswordCallback != nil { .Methods = append(.Methods, "password") }if .PublicKeyCallback != nil { .Methods = append(.Methods, "publickey") }if .KeyboardInteractiveCallback != nil { .Methods = append(.Methods, "keyboard-interactive") }if .GSSAPIWithMICConfig != nil && .GSSAPIWithMICConfig.Server != nil && .GSSAPIWithMICConfig.AllowLogin != nil { .Methods = append(.Methods, "gssapi-with-mic") }iflen(.Methods) == 0 {returnnil, errors.New("ssh: no authentication methods available") }if := .transport.writePacket(Marshal(&)); != nil {returnnil, } }if := .transport.writePacket([]byte{msgUserAuthSuccess}); != nil {returnnil, }return , nil}// sshClientKeyboardInteractive implements a ClientKeyboardInteractive by// asking the client on the other side of a ServerConn.type sshClientKeyboardInteractive struct { *connection}func ( *sshClientKeyboardInteractive) (, string, []string, []bool) ( []string, error) {iflen() != len() {returnnil, errors.New("ssh: echos and questions must have equal length") }var []bytefor := range { = appendString(, []) = appendBool(, []) }if := .transport.writePacket(Marshal(&userAuthInfoRequestMsg{Name: ,Instruction: ,NumPrompts: uint32(len()),Prompts: , })); != nil {returnnil, } , := .transport.readPacket()if != nil {returnnil, }if [0] != msgUserAuthInfoResponse {returnnil, unexpectedMessageError(msgUserAuthInfoResponse, [0]) } = [1:] , , := parseUint32()if ! || int() != len() {returnnil, parseError(msgUserAuthInfoResponse) }for := uint32(0); < ; ++ { , , := parseString()if ! {returnnil, parseError(msgUserAuthInfoResponse) } = append(, string()) = }iflen() != 0 {returnnil, errors.New("ssh: junk at end of message") }return , nil}
The pages are generated with Goldsv0.8.2. (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu.
PR and bug reports are welcome and can be submitted to the issue list.
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds.