ssh_gss.go raw

   1  // Copyright 2011 The Go Authors. All rights reserved.
   2  // Use of this source code is governed by a BSD-style
   3  // license that can be found in the LICENSE file.
   4  
   5  package ssh
   6  
   7  import (
   8  	"encoding/asn1"
   9  	"errors"
  10  )
  11  
  12  var krb5OID []byte
  13  
  14  func init() {
  15  	krb5OID, _ = asn1.Marshal(krb5Mesh)
  16  }
  17  
  18  // GSSAPIClient provides the API to plug-in GSSAPI authentication for client logins.
  19  type GSSAPIClient interface {
  20  	// InitSecContext initiates the establishment of a security context for GSS-API between the
  21  	// ssh client and ssh server. Initially the token parameter should be specified as nil.
  22  	// The routine may return a outputToken which should be transferred to
  23  	// the ssh server, where the ssh server will present it to
  24  	// AcceptSecContext. If no token need be sent, InitSecContext will indicate this by setting
  25  	// needContinue to false. To complete the context
  26  	// establishment, one or more reply tokens may be required from the ssh
  27  	// server;if so, InitSecContext will return a needContinue which is true.
  28  	// In this case, InitSecContext should be called again when the
  29  	// reply token is received from the ssh server, passing the reply
  30  	// token to InitSecContext via the token parameters.
  31  	// See RFC 2743 section 2.2.1 and RFC 4462 section 3.4.
  32  	InitSecContext(target string, token []byte, isGSSDelegCreds bool) (outputToken []byte, needContinue bool, err error)
  33  	// GetMIC generates a cryptographic MIC for the SSH2 message, and places
  34  	// the MIC in a token for transfer to the ssh server.
  35  	// The contents of the MIC field are obtained by calling GSS_GetMIC()
  36  	// over the following, using the GSS-API context that was just
  37  	// established:
  38  	//  string    session identifier
  39  	//  byte      SSH_MSG_USERAUTH_REQUEST
  40  	//  string    user name
  41  	//  string    service
  42  	//  string    "gssapi-with-mic"
  43  	// See RFC 2743 section 2.3.1 and RFC 4462 3.5.
  44  	GetMIC(micFiled []byte) ([]byte, error)
  45  	// Whenever possible, it should be possible for
  46  	// DeleteSecContext() calls to be successfully processed even
  47  	// if other calls cannot succeed, thereby enabling context-related
  48  	// resources to be released.
  49  	// In addition to deleting established security contexts,
  50  	// gss_delete_sec_context must also be able to delete "half-built"
  51  	// security contexts resulting from an incomplete sequence of
  52  	// InitSecContext()/AcceptSecContext() calls.
  53  	// See RFC 2743 section 2.2.3.
  54  	DeleteSecContext() error
  55  }
  56  
  57  // GSSAPIServer provides the API to plug in GSSAPI authentication for server logins.
  58  type GSSAPIServer interface {
  59  	// AcceptSecContext allows a remotely initiated security context between the application
  60  	// and a remote peer to be established by the ssh client. The routine may return a
  61  	// outputToken which should be transferred to the ssh client,
  62  	// where the ssh client will present it to InitSecContext.
  63  	// If no token need be sent, AcceptSecContext will indicate this
  64  	// by setting the needContinue to false. To
  65  	// complete the context establishment, one or more reply tokens may be
  66  	// required from the ssh client. if so, AcceptSecContext
  67  	// will return a needContinue which is true, in which case it
  68  	// should be called again when the reply token is received from the ssh
  69  	// client, passing the token to AcceptSecContext via the
  70  	// token parameters.
  71  	// The srcName return value is the authenticated username.
  72  	// See RFC 2743 section 2.2.2 and RFC 4462 section 3.4.
  73  	AcceptSecContext(token []byte) (outputToken []byte, srcName string, needContinue bool, err error)
  74  	// VerifyMIC verifies that a cryptographic MIC, contained in the token parameter,
  75  	// fits the supplied message is received from the ssh client.
  76  	// See RFC 2743 section 2.3.2.
  77  	VerifyMIC(micField []byte, micToken []byte) error
  78  	// Whenever possible, it should be possible for
  79  	// DeleteSecContext() calls to be successfully processed even
  80  	// if other calls cannot succeed, thereby enabling context-related
  81  	// resources to be released.
  82  	// In addition to deleting established security contexts,
  83  	// gss_delete_sec_context must also be able to delete "half-built"
  84  	// security contexts resulting from an incomplete sequence of
  85  	// InitSecContext()/AcceptSecContext() calls.
  86  	// See RFC 2743 section 2.2.3.
  87  	DeleteSecContext() error
  88  }
  89  
  90  var (
  91  	// OpenSSH supports Kerberos V5 mechanism only for GSS-API authentication,
  92  	// so we also support the krb5 mechanism only.
  93  	// See RFC 1964 section 1.
  94  	krb5Mesh = asn1.ObjectIdentifier{1, 2, 840, 113554, 1, 2, 2}
  95  )
  96  
  97  // The GSS-API authentication method is initiated when the client sends an SSH_MSG_USERAUTH_REQUEST
  98  // See RFC 4462 section 3.2.
  99  type userAuthRequestGSSAPI struct {
 100  	N    uint32
 101  	OIDS []asn1.ObjectIdentifier
 102  }
 103  
 104  func parseGSSAPIPayload(payload []byte) (*userAuthRequestGSSAPI, error) {
 105  	n, rest, ok := parseUint32(payload)
 106  	if !ok {
 107  		return nil, errors.New("parse uint32 failed")
 108  	}
 109  	// Each ASN.1 encoded OID must have a minimum
 110  	// of 2 bytes; 64 maximum mechanisms is an
 111  	// arbitrary, but reasonable ceiling.
 112  	const maxMechs = 64
 113  	if n > maxMechs || int(n)*2 > len(rest) {
 114  		return nil, errors.New("invalid mechanism count")
 115  	}
 116  	s := &userAuthRequestGSSAPI{
 117  		N:    n,
 118  		OIDS: make([]asn1.ObjectIdentifier, n),
 119  	}
 120  	for i := 0; i < int(n); i++ {
 121  		var (
 122  			desiredMech []byte
 123  			err         error
 124  		)
 125  		desiredMech, rest, ok = parseString(rest)
 126  		if !ok {
 127  			return nil, errors.New("parse string failed")
 128  		}
 129  		if rest, err = asn1.Unmarshal(desiredMech, &s.OIDS[i]); err != nil {
 130  			return nil, err
 131  		}
 132  	}
 133  	return s, nil
 134  }
 135  
 136  // See RFC 4462 section 3.6.
 137  func buildMIC(sessionID string, username string, service string, authMethod string) []byte {
 138  	out := make([]byte, 0, 0)
 139  	out = appendString(out, sessionID)
 140  	out = append(out, msgUserAuthRequest)
 141  	out = appendString(out, username)
 142  	out = appendString(out, service)
 143  	out = appendString(out, authMethod)
 144  	return out
 145  }
 146