forked from go-ldap/ldap
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
https://github.com/go-ldap/ldap/pull/449
- Loading branch information
Власов Роман Сергеевич
committed
Dec 15, 2023
1 parent
cdbbc30
commit 9cf9ff5
Showing
4 changed files
with
452 additions
and
2 deletions.
There are no files selected for viewing
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
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
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,211 @@ | ||
package gssapi | ||
|
||
import ( | ||
"fmt" | ||
|
||
"github.com/jcmturner/gokrb5/v8/client" | ||
"github.com/jcmturner/gokrb5/v8/config" | ||
"github.com/jcmturner/gokrb5/v8/credentials" | ||
"github.com/jcmturner/gokrb5/v8/crypto" | ||
"github.com/jcmturner/gokrb5/v8/gssapi" | ||
"github.com/jcmturner/gokrb5/v8/iana/keyusage" | ||
"github.com/jcmturner/gokrb5/v8/keytab" | ||
"github.com/jcmturner/gokrb5/v8/messages" | ||
"github.com/jcmturner/gokrb5/v8/spnego" | ||
"github.com/jcmturner/gokrb5/v8/types" | ||
) | ||
|
||
// Client implements ldap.GSSAPIClient interface. | ||
type Client struct { | ||
*client.Client | ||
|
||
ekey types.EncryptionKey | ||
Subkey types.EncryptionKey | ||
} | ||
|
||
// NewClientWithKeytab creates a new client from a keytab credential. | ||
// Set the realm to empty string to use the default realm from config. | ||
func NewClientWithKeytab(username, realm, keytabPath, krb5confPath string, settings ...func(*client.Settings)) (*Client, error) { | ||
krb5conf, err := config.Load(krb5confPath) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
keytab, err := keytab.Load(keytabPath) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
client := client.NewWithKeytab(username, realm, keytab, krb5conf, settings...) | ||
|
||
return &Client{ | ||
Client: client, | ||
}, nil | ||
} | ||
|
||
// NewClientWithPassword creates a new client from a password credential. | ||
// Set the realm to empty string to use the default realm from config. | ||
func NewClientWithPassword(username, realm, password string, krb5confPath string, settings ...func(*client.Settings)) (*Client, error) { | ||
krb5conf, err := config.Load(krb5confPath) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
client := client.NewWithPassword(username, realm, password, krb5conf, settings...) | ||
|
||
return &Client{ | ||
Client: client, | ||
}, nil | ||
} | ||
|
||
// NewClientFromCCache creates a new client from a populated client cache. | ||
func NewClientFromCCache(ccachePath, krb5confPath string, settings ...func(*client.Settings)) (*Client, error) { | ||
krb5conf, err := config.Load(krb5confPath) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
ccache, err := credentials.LoadCCache(ccachePath) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
client, err := client.NewFromCCache(ccache, krb5conf, settings...) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
return &Client{ | ||
Client: client, | ||
}, nil | ||
} | ||
|
||
// Close deletes any established secure context and closes the client. | ||
func (client *Client) Close() error { | ||
client.Client.Destroy() | ||
return nil | ||
} | ||
|
||
// DeleteSecContext destroys any established secure context. | ||
func (client *Client) DeleteSecContext() error { | ||
client.ekey = types.EncryptionKey{} | ||
client.Subkey = types.EncryptionKey{} | ||
return nil | ||
} | ||
|
||
// InitSecContext initiates the establishment of a security context for | ||
// GSS-API between the client and server. | ||
// See RFC 4752 section 3.1. | ||
func (client *Client) InitSecContext(target string, input []byte) ([]byte, bool, error) { | ||
gssapiFlags := []int{gssapi.ContextFlagInteg, gssapi.ContextFlagConf, gssapi.ContextFlagMutual} | ||
|
||
switch input { | ||
case nil: | ||
tkt, ekey, err := client.Client.GetServiceTicket(target) | ||
if err != nil { | ||
return nil, false, err | ||
} | ||
client.ekey = ekey | ||
|
||
token, err := spnego.NewKRB5TokenAPREQ(client.Client, tkt, ekey, gssapiFlags, []int{}) | ||
if err != nil { | ||
return nil, false, err | ||
} | ||
|
||
output, err := token.Marshal() | ||
if err != nil { | ||
return nil, false, err | ||
} | ||
|
||
return output, true, nil | ||
|
||
default: | ||
var token spnego.KRB5Token | ||
|
||
err := token.Unmarshal(input) | ||
if err != nil { | ||
return nil, false, err | ||
} | ||
|
||
var completed bool | ||
|
||
if token.IsAPRep() { | ||
completed = true | ||
|
||
encpart, err := crypto.DecryptEncPart(token.APRep.EncPart, client.ekey, keyusage.AP_REP_ENCPART) | ||
if err != nil { | ||
return nil, false, err | ||
} | ||
|
||
part := &messages.EncAPRepPart{} | ||
|
||
if err = part.Unmarshal(encpart); err != nil { | ||
return nil, false, err | ||
} | ||
client.Subkey = part.Subkey | ||
} | ||
|
||
if token.IsKRBError() { | ||
return nil, !false, token.KRBError | ||
} | ||
|
||
return make([]byte, 0), !completed, nil | ||
} | ||
} | ||
|
||
// NegotiateSaslAuth performs the last step of the SASL handshake. | ||
// See RFC 4752 section 3.1. | ||
func (client *Client) NegotiateSaslAuth(input []byte, authzid string) ([]byte, error) { | ||
token := &gssapi.WrapToken{} | ||
err := token.Unmarshal(input, true) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
if (token.Flags & 0b1) == 0 { | ||
return nil, fmt.Errorf("got a Wrapped token that's not from the server") | ||
} | ||
|
||
key := client.ekey | ||
if (token.Flags & 0b100) != 0 { | ||
key = client.Subkey | ||
} | ||
|
||
_, err = token.Verify(key, keyusage.GSSAPI_ACCEPTOR_SEAL) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
pl := token.Payload | ||
if len(pl) != 4 { | ||
return nil, fmt.Errorf("server send bad final token for SASL GSSAPI Handshake") | ||
} | ||
|
||
// We never want a security layer | ||
b := [4]byte{0, 0, 0, 0} | ||
payload := append(b[:], []byte(authzid)...) | ||
|
||
encType, err := crypto.GetEtype(key.KeyType) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
token = &gssapi.WrapToken{ | ||
Flags: 0b100, | ||
EC: uint16(encType.GetHMACBitLength() / 8), | ||
RRC: 0, | ||
SndSeqNum: 1, | ||
Payload: payload, | ||
} | ||
|
||
if err := token.SetCheckSum(key, keyusage.GSSAPI_INITIATOR_SEAL); err != nil { | ||
return nil, err | ||
} | ||
|
||
output, err := token.Marshal() | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
return output, nil | ||
} |
Oops, something went wrong.