mirror of
https://github.com/fhmq/hmq.git
synced 2026-09-01 23:34:52 +00:00
Plugins support (#46)
* modify * update * add acl * add feature * update dockerfile * add deploy * update * update * plugins * plugins * update * update * update * fixed * remove * fixed * add log * update * fixed * update * fix config * add http api * add http api * resp * add config for work chan * update * fixed * update * disable trace * fixed * change acl * fixed * fixed res * dd * dd * ddd * dd * update * fixed * update * add * fixed * update key * add log * update * format * update * update auth * update * update readme * added * update * fixed * fixed * fix * upade * update * update
This commit is contained in:
+432
@@ -0,0 +1,432 @@
|
||||
package messages
|
||||
|
||||
// Reference: https://www.ietf.org/rfc/rfc4120.txt
|
||||
// Section: 5.4.1
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"fmt"
|
||||
"math"
|
||||
"math/big"
|
||||
"time"
|
||||
|
||||
"github.com/jcmturner/gofork/encoding/asn1"
|
||||
"gopkg.in/jcmturner/gokrb5.v7/asn1tools"
|
||||
"gopkg.in/jcmturner/gokrb5.v7/config"
|
||||
"gopkg.in/jcmturner/gokrb5.v7/crypto"
|
||||
"gopkg.in/jcmturner/gokrb5.v7/iana"
|
||||
"gopkg.in/jcmturner/gokrb5.v7/iana/asnAppTag"
|
||||
"gopkg.in/jcmturner/gokrb5.v7/iana/flags"
|
||||
"gopkg.in/jcmturner/gokrb5.v7/iana/keyusage"
|
||||
"gopkg.in/jcmturner/gokrb5.v7/iana/msgtype"
|
||||
"gopkg.in/jcmturner/gokrb5.v7/iana/nametype"
|
||||
"gopkg.in/jcmturner/gokrb5.v7/iana/patype"
|
||||
"gopkg.in/jcmturner/gokrb5.v7/krberror"
|
||||
"gopkg.in/jcmturner/gokrb5.v7/types"
|
||||
)
|
||||
|
||||
type marshalKDCReq struct {
|
||||
PVNO int `asn1:"explicit,tag:1"`
|
||||
MsgType int `asn1:"explicit,tag:2"`
|
||||
PAData types.PADataSequence `asn1:"explicit,optional,tag:3"`
|
||||
ReqBody asn1.RawValue `asn1:"explicit,tag:4"`
|
||||
}
|
||||
|
||||
// KDCReqFields represents the KRB_KDC_REQ fields.
|
||||
type KDCReqFields struct {
|
||||
PVNO int
|
||||
MsgType int
|
||||
PAData types.PADataSequence
|
||||
ReqBody KDCReqBody
|
||||
Renewal bool
|
||||
}
|
||||
|
||||
// ASReq implements RFC 4120 KRB_AS_REQ: https://tools.ietf.org/html/rfc4120#section-5.4.1.
|
||||
type ASReq struct {
|
||||
KDCReqFields
|
||||
}
|
||||
|
||||
// TGSReq implements RFC 4120 KRB_TGS_REQ: https://tools.ietf.org/html/rfc4120#section-5.4.1.
|
||||
type TGSReq struct {
|
||||
KDCReqFields
|
||||
}
|
||||
|
||||
type marshalKDCReqBody struct {
|
||||
KDCOptions asn1.BitString `asn1:"explicit,tag:0"`
|
||||
CName types.PrincipalName `asn1:"explicit,optional,tag:1"`
|
||||
Realm string `asn1:"generalstring,explicit,tag:2"`
|
||||
SName types.PrincipalName `asn1:"explicit,optional,tag:3"`
|
||||
From time.Time `asn1:"generalized,explicit,optional,tag:4"`
|
||||
Till time.Time `asn1:"generalized,explicit,tag:5"`
|
||||
RTime time.Time `asn1:"generalized,explicit,optional,tag:6"`
|
||||
Nonce int `asn1:"explicit,tag:7"`
|
||||
EType []int32 `asn1:"explicit,tag:8"`
|
||||
Addresses []types.HostAddress `asn1:"explicit,optional,tag:9"`
|
||||
EncAuthData types.EncryptedData `asn1:"explicit,optional,tag:10"`
|
||||
// Ticket needs to be a raw value as it is wrapped in an APPLICATION tag
|
||||
AdditionalTickets asn1.RawValue `asn1:"explicit,optional,tag:11"`
|
||||
}
|
||||
|
||||
// KDCReqBody implements the KRB_KDC_REQ request body.
|
||||
type KDCReqBody struct {
|
||||
KDCOptions asn1.BitString `asn1:"explicit,tag:0"`
|
||||
CName types.PrincipalName `asn1:"explicit,optional,tag:1"`
|
||||
Realm string `asn1:"generalstring,explicit,tag:2"`
|
||||
SName types.PrincipalName `asn1:"explicit,optional,tag:3"`
|
||||
From time.Time `asn1:"generalized,explicit,optional,tag:4"`
|
||||
Till time.Time `asn1:"generalized,explicit,tag:5"`
|
||||
RTime time.Time `asn1:"generalized,explicit,optional,tag:6"`
|
||||
Nonce int `asn1:"explicit,tag:7"`
|
||||
EType []int32 `asn1:"explicit,tag:8"`
|
||||
Addresses []types.HostAddress `asn1:"explicit,optional,tag:9"`
|
||||
EncAuthData types.EncryptedData `asn1:"explicit,optional,tag:10"`
|
||||
AdditionalTickets []Ticket `asn1:"explicit,optional,tag:11"`
|
||||
}
|
||||
|
||||
// NewASReqForTGT generates a new KRB_AS_REQ struct for a TGT request.
|
||||
func NewASReqForTGT(realm string, c *config.Config, cname types.PrincipalName) (ASReq, error) {
|
||||
sname := types.PrincipalName{
|
||||
NameType: nametype.KRB_NT_SRV_INST,
|
||||
NameString: []string{"krbtgt", realm},
|
||||
}
|
||||
return NewASReq(realm, c, cname, sname)
|
||||
}
|
||||
|
||||
// NewASReqForChgPasswd generates a new KRB_AS_REQ struct for a change password request.
|
||||
func NewASReqForChgPasswd(realm string, c *config.Config, cname types.PrincipalName) (ASReq, error) {
|
||||
sname := types.PrincipalName{
|
||||
NameType: nametype.KRB_NT_PRINCIPAL,
|
||||
NameString: []string{"kadmin", "changepw"},
|
||||
}
|
||||
return NewASReq(realm, c, cname, sname)
|
||||
}
|
||||
|
||||
// NewASReq generates a new KRB_AS_REQ struct for a given SNAME.
|
||||
func NewASReq(realm string, c *config.Config, cname, sname types.PrincipalName) (ASReq, error) {
|
||||
nonce, err := rand.Int(rand.Reader, big.NewInt(math.MaxInt32))
|
||||
if err != nil {
|
||||
return ASReq{}, err
|
||||
}
|
||||
t := time.Now().UTC()
|
||||
// Copy the default options to make this thread safe
|
||||
kopts := types.NewKrbFlags()
|
||||
copy(kopts.Bytes, c.LibDefaults.KDCDefaultOptions.Bytes)
|
||||
kopts.BitLength = c.LibDefaults.KDCDefaultOptions.BitLength
|
||||
a := ASReq{
|
||||
KDCReqFields{
|
||||
PVNO: iana.PVNO,
|
||||
MsgType: msgtype.KRB_AS_REQ,
|
||||
PAData: types.PADataSequence{},
|
||||
ReqBody: KDCReqBody{
|
||||
KDCOptions: kopts,
|
||||
Realm: realm,
|
||||
CName: cname,
|
||||
SName: sname,
|
||||
Till: t.Add(c.LibDefaults.TicketLifetime),
|
||||
Nonce: int(nonce.Int64()),
|
||||
EType: c.LibDefaults.DefaultTktEnctypeIDs,
|
||||
},
|
||||
},
|
||||
}
|
||||
if c.LibDefaults.Forwardable {
|
||||
types.SetFlag(&a.ReqBody.KDCOptions, flags.Forwardable)
|
||||
}
|
||||
if c.LibDefaults.Canonicalize {
|
||||
types.SetFlag(&a.ReqBody.KDCOptions, flags.Canonicalize)
|
||||
}
|
||||
if c.LibDefaults.Proxiable {
|
||||
types.SetFlag(&a.ReqBody.KDCOptions, flags.Proxiable)
|
||||
}
|
||||
if c.LibDefaults.RenewLifetime != 0 {
|
||||
types.SetFlag(&a.ReqBody.KDCOptions, flags.Renewable)
|
||||
a.ReqBody.RTime = t.Add(c.LibDefaults.RenewLifetime)
|
||||
a.ReqBody.RTime = t.Add(time.Duration(48) * time.Hour)
|
||||
}
|
||||
if !c.LibDefaults.NoAddresses {
|
||||
ha, err := types.LocalHostAddresses()
|
||||
if err != nil {
|
||||
return a, fmt.Errorf("could not get local addresses: %v", err)
|
||||
}
|
||||
ha = append(ha, types.HostAddressesFromNetIPs(c.LibDefaults.ExtraAddresses)...)
|
||||
a.ReqBody.Addresses = ha
|
||||
}
|
||||
return a, nil
|
||||
}
|
||||
|
||||
// NewTGSReq generates a new KRB_TGS_REQ struct.
|
||||
func NewTGSReq(cname types.PrincipalName, kdcRealm string, c *config.Config, tgt Ticket, sessionKey types.EncryptionKey, sname types.PrincipalName, renewal bool) (TGSReq, error) {
|
||||
a, err := tgsReq(cname, sname, kdcRealm, renewal, c)
|
||||
if err != nil {
|
||||
return a, err
|
||||
}
|
||||
err = a.setPAData(tgt, sessionKey)
|
||||
return a, err
|
||||
}
|
||||
|
||||
// NewUser2UserTGSReq returns a TGS-REQ suitable for user-to-user authentication (https://tools.ietf.org/html/rfc4120#section-3.7)
|
||||
func NewUser2UserTGSReq(cname types.PrincipalName, kdcRealm string, c *config.Config, clientTGT Ticket, sessionKey types.EncryptionKey, sname types.PrincipalName, renewal bool, verifyingTGT Ticket) (TGSReq, error) {
|
||||
a, err := tgsReq(cname, sname, kdcRealm, renewal, c)
|
||||
if err != nil {
|
||||
return a, err
|
||||
}
|
||||
a.ReqBody.AdditionalTickets = []Ticket{verifyingTGT}
|
||||
types.SetFlag(&a.ReqBody.KDCOptions, flags.EncTktInSkey)
|
||||
err = a.setPAData(clientTGT, sessionKey)
|
||||
return a, err
|
||||
}
|
||||
|
||||
// tgsReq populates the fields for a TGS_REQ
|
||||
func tgsReq(cname, sname types.PrincipalName, kdcRealm string, renewal bool, c *config.Config) (TGSReq, error) {
|
||||
nonce, err := rand.Int(rand.Reader, big.NewInt(math.MaxInt32))
|
||||
if err != nil {
|
||||
return TGSReq{}, err
|
||||
}
|
||||
t := time.Now().UTC()
|
||||
k := KDCReqFields{
|
||||
PVNO: iana.PVNO,
|
||||
MsgType: msgtype.KRB_TGS_REQ,
|
||||
ReqBody: KDCReqBody{
|
||||
KDCOptions: types.NewKrbFlags(),
|
||||
Realm: kdcRealm,
|
||||
CName: cname, // Add the CName to make validation of the reply easier
|
||||
SName: sname,
|
||||
Till: t.Add(c.LibDefaults.TicketLifetime),
|
||||
Nonce: int(nonce.Int64()),
|
||||
EType: c.LibDefaults.DefaultTGSEnctypeIDs,
|
||||
},
|
||||
Renewal: renewal,
|
||||
}
|
||||
if c.LibDefaults.Forwardable {
|
||||
types.SetFlag(&k.ReqBody.KDCOptions, flags.Forwardable)
|
||||
}
|
||||
if c.LibDefaults.Canonicalize {
|
||||
types.SetFlag(&k.ReqBody.KDCOptions, flags.Canonicalize)
|
||||
}
|
||||
if c.LibDefaults.Proxiable {
|
||||
types.SetFlag(&k.ReqBody.KDCOptions, flags.Proxiable)
|
||||
}
|
||||
if c.LibDefaults.RenewLifetime > time.Duration(0) {
|
||||
types.SetFlag(&k.ReqBody.KDCOptions, flags.Renewable)
|
||||
k.ReqBody.RTime = t.Add(c.LibDefaults.RenewLifetime)
|
||||
}
|
||||
if !c.LibDefaults.NoAddresses {
|
||||
ha, err := types.LocalHostAddresses()
|
||||
if err != nil {
|
||||
return TGSReq{}, fmt.Errorf("could not get local addresses: %v", err)
|
||||
}
|
||||
ha = append(ha, types.HostAddressesFromNetIPs(c.LibDefaults.ExtraAddresses)...)
|
||||
k.ReqBody.Addresses = ha
|
||||
}
|
||||
if renewal {
|
||||
types.SetFlag(&k.ReqBody.KDCOptions, flags.Renew)
|
||||
types.SetFlag(&k.ReqBody.KDCOptions, flags.Renewable)
|
||||
}
|
||||
return TGSReq{
|
||||
k,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (k *TGSReq) setPAData(tgt Ticket, sessionKey types.EncryptionKey) error {
|
||||
// Marshal the request and calculate checksum
|
||||
b, err := k.ReqBody.Marshal()
|
||||
if err != nil {
|
||||
return krberror.Errorf(err, krberror.EncodingError, "error marshaling TGS_REQ body")
|
||||
}
|
||||
etype, err := crypto.GetEtype(sessionKey.KeyType)
|
||||
if err != nil {
|
||||
return krberror.Errorf(err, krberror.EncryptingError, "error getting etype to encrypt authenticator")
|
||||
}
|
||||
cb, err := etype.GetChecksumHash(sessionKey.KeyValue, b, keyusage.TGS_REQ_PA_TGS_REQ_AP_REQ_AUTHENTICATOR_CHKSUM)
|
||||
if err != nil {
|
||||
return krberror.Errorf(err, krberror.ChksumError, "error getting etype checksum hash")
|
||||
}
|
||||
|
||||
// Form PAData for TGS_REQ
|
||||
// Create authenticator
|
||||
auth, err := types.NewAuthenticator(tgt.Realm, k.ReqBody.CName)
|
||||
if err != nil {
|
||||
return krberror.Errorf(err, krberror.KRBMsgError, "error generating new authenticator")
|
||||
}
|
||||
auth.Cksum = types.Checksum{
|
||||
CksumType: etype.GetHashID(),
|
||||
Checksum: cb,
|
||||
}
|
||||
// Create AP_REQ
|
||||
apReq, err := NewAPReq(tgt, sessionKey, auth)
|
||||
if err != nil {
|
||||
return krberror.Errorf(err, krberror.KRBMsgError, "error generating new AP_REQ")
|
||||
}
|
||||
apb, err := apReq.Marshal()
|
||||
if err != nil {
|
||||
return krberror.Errorf(err, krberror.EncodingError, "error marshaling AP_REQ for pre-authentication data")
|
||||
}
|
||||
k.PAData = types.PADataSequence{
|
||||
types.PAData{
|
||||
PADataType: patype.PA_TGS_REQ,
|
||||
PADataValue: apb,
|
||||
},
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Unmarshal bytes b into the ASReq struct.
|
||||
func (k *ASReq) Unmarshal(b []byte) error {
|
||||
var m marshalKDCReq
|
||||
_, err := asn1.UnmarshalWithParams(b, &m, fmt.Sprintf("application,explicit,tag:%v", asnAppTag.ASREQ))
|
||||
if err != nil {
|
||||
return krberror.Errorf(err, krberror.EncodingError, "error unmarshaling AS_REQ")
|
||||
}
|
||||
expectedMsgType := msgtype.KRB_AS_REQ
|
||||
if m.MsgType != expectedMsgType {
|
||||
return krberror.NewErrorf(krberror.KRBMsgError, "message ID does not indicate a AS_REQ. Expected: %v; Actual: %v", expectedMsgType, m.MsgType)
|
||||
}
|
||||
var reqb KDCReqBody
|
||||
err = reqb.Unmarshal(m.ReqBody.Bytes)
|
||||
if err != nil {
|
||||
return krberror.Errorf(err, krberror.EncodingError, "error processing AS_REQ body")
|
||||
}
|
||||
k.MsgType = m.MsgType
|
||||
k.PAData = m.PAData
|
||||
k.PVNO = m.PVNO
|
||||
k.ReqBody = reqb
|
||||
return nil
|
||||
}
|
||||
|
||||
// Unmarshal bytes b into the TGSReq struct.
|
||||
func (k *TGSReq) Unmarshal(b []byte) error {
|
||||
var m marshalKDCReq
|
||||
_, err := asn1.UnmarshalWithParams(b, &m, fmt.Sprintf("application,explicit,tag:%v", asnAppTag.TGSREQ))
|
||||
if err != nil {
|
||||
return krberror.Errorf(err, krberror.EncodingError, "error unmarshaling TGS_REQ")
|
||||
}
|
||||
expectedMsgType := msgtype.KRB_TGS_REQ
|
||||
if m.MsgType != expectedMsgType {
|
||||
return krberror.NewErrorf(krberror.KRBMsgError, "message ID does not indicate a TGS_REQ. Expected: %v; Actual: %v", expectedMsgType, m.MsgType)
|
||||
}
|
||||
var reqb KDCReqBody
|
||||
err = reqb.Unmarshal(m.ReqBody.Bytes)
|
||||
if err != nil {
|
||||
return krberror.Errorf(err, krberror.EncodingError, "error processing TGS_REQ body")
|
||||
}
|
||||
k.MsgType = m.MsgType
|
||||
k.PAData = m.PAData
|
||||
k.PVNO = m.PVNO
|
||||
k.ReqBody = reqb
|
||||
return nil
|
||||
}
|
||||
|
||||
// Unmarshal bytes b into the KRB_KDC_REQ body struct.
|
||||
func (k *KDCReqBody) Unmarshal(b []byte) error {
|
||||
var m marshalKDCReqBody
|
||||
_, err := asn1.Unmarshal(b, &m)
|
||||
if err != nil {
|
||||
return krberror.Errorf(err, krberror.EncodingError, "error unmarshaling KDC_REQ body")
|
||||
}
|
||||
k.KDCOptions = m.KDCOptions
|
||||
if len(k.KDCOptions.Bytes) < 4 {
|
||||
tb := make([]byte, 4-len(k.KDCOptions.Bytes))
|
||||
k.KDCOptions.Bytes = append(tb, k.KDCOptions.Bytes...)
|
||||
k.KDCOptions.BitLength = len(k.KDCOptions.Bytes) * 8
|
||||
}
|
||||
k.CName = m.CName
|
||||
k.Realm = m.Realm
|
||||
k.SName = m.SName
|
||||
k.From = m.From
|
||||
k.Till = m.Till
|
||||
k.RTime = m.RTime
|
||||
k.Nonce = m.Nonce
|
||||
k.EType = m.EType
|
||||
k.Addresses = m.Addresses
|
||||
k.EncAuthData = m.EncAuthData
|
||||
if len(m.AdditionalTickets.Bytes) > 0 {
|
||||
k.AdditionalTickets, err = unmarshalTicketsSequence(m.AdditionalTickets)
|
||||
if err != nil {
|
||||
return krberror.Errorf(err, krberror.EncodingError, "error unmarshaling additional tickets")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Marshal ASReq struct.
|
||||
func (k *ASReq) Marshal() ([]byte, error) {
|
||||
m := marshalKDCReq{
|
||||
PVNO: k.PVNO,
|
||||
MsgType: k.MsgType,
|
||||
PAData: k.PAData,
|
||||
}
|
||||
b, err := k.ReqBody.Marshal()
|
||||
if err != nil {
|
||||
var mk []byte
|
||||
return mk, err
|
||||
}
|
||||
m.ReqBody = asn1.RawValue{
|
||||
Class: asn1.ClassContextSpecific,
|
||||
IsCompound: true,
|
||||
Tag: 4,
|
||||
Bytes: b,
|
||||
}
|
||||
mk, err := asn1.Marshal(m)
|
||||
if err != nil {
|
||||
return mk, krberror.Errorf(err, krberror.EncodingError, "error marshaling AS_REQ")
|
||||
}
|
||||
mk = asn1tools.AddASNAppTag(mk, asnAppTag.ASREQ)
|
||||
return mk, nil
|
||||
}
|
||||
|
||||
// Marshal TGSReq struct.
|
||||
func (k *TGSReq) Marshal() ([]byte, error) {
|
||||
m := marshalKDCReq{
|
||||
PVNO: k.PVNO,
|
||||
MsgType: k.MsgType,
|
||||
PAData: k.PAData,
|
||||
}
|
||||
b, err := k.ReqBody.Marshal()
|
||||
if err != nil {
|
||||
var mk []byte
|
||||
return mk, err
|
||||
}
|
||||
m.ReqBody = asn1.RawValue{
|
||||
Class: asn1.ClassContextSpecific,
|
||||
IsCompound: true,
|
||||
Tag: 4,
|
||||
Bytes: b,
|
||||
}
|
||||
mk, err := asn1.Marshal(m)
|
||||
if err != nil {
|
||||
return mk, krberror.Errorf(err, krberror.EncodingError, "error marshaling AS_REQ")
|
||||
}
|
||||
mk = asn1tools.AddASNAppTag(mk, asnAppTag.TGSREQ)
|
||||
return mk, nil
|
||||
}
|
||||
|
||||
// Marshal KRB_KDC_REQ body struct.
|
||||
func (k *KDCReqBody) Marshal() ([]byte, error) {
|
||||
var b []byte
|
||||
m := marshalKDCReqBody{
|
||||
KDCOptions: k.KDCOptions,
|
||||
CName: k.CName,
|
||||
Realm: k.Realm,
|
||||
SName: k.SName,
|
||||
From: k.From,
|
||||
Till: k.Till,
|
||||
RTime: k.RTime,
|
||||
Nonce: k.Nonce,
|
||||
EType: k.EType,
|
||||
Addresses: k.Addresses,
|
||||
EncAuthData: k.EncAuthData,
|
||||
}
|
||||
rawtkts, err := MarshalTicketSequence(k.AdditionalTickets)
|
||||
if err != nil {
|
||||
return b, krberror.Errorf(err, krberror.EncodingError, "error in marshaling KDC request body additional tickets")
|
||||
}
|
||||
//The asn1.rawValue needs the tag setting on it for where it is in the KDCReqBody
|
||||
rawtkts.Tag = 11
|
||||
if len(rawtkts.Bytes) > 0 {
|
||||
m.AdditionalTickets = rawtkts
|
||||
}
|
||||
b, err = asn1.Marshal(m)
|
||||
if err != nil {
|
||||
return b, krberror.Errorf(err, krberror.EncodingError, "error in marshaling KDC request body")
|
||||
}
|
||||
return b, nil
|
||||
}
|
||||
Reference in New Issue
Block a user