Skip to content

Commit fd03142

Browse files
committed
Merge remote-tracking branch 'remotes/origin/jake/new-seccontext-interface' into dev
Move to a cleaner seccontext/acceptcontext interface. This change makes the context estalishment loop simpler and is more akin to the Java interface.
2 parents ad62b31 + 3fbf2ad commit fd03142

File tree

7 files changed

+525
-17
lines changed

7 files changed

+525
-17
lines changed

examples/go/go.mod

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
module examples
2+
3+
go 1.22.4
4+
5+
replace github.com/golang-auth/go-gssapi/v3 => ../../v3
6+
7+
require github.com/golang-auth/go-gssapi/v3 v3.0.0-alpha
8+
9+
require github.com/golang-auth/go-gssapi-c v0.0.0-20240828194135-955ba90d4511

examples/go/go.sum

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
2+
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
3+
github.com/golang-auth/go-gssapi-c v0.0.0-20240827133603-e7af9f04586a h1:qdMspd9EVKyHD4PqzYpCDpWaBwdm4oBY1u631biS/3U=
4+
github.com/golang-auth/go-gssapi-c v0.0.0-20240827133603-e7af9f04586a/go.mod h1:7+YbBfLmM3gMF6DoCfjZFQBx1SXj1Uru6Y2tl77nhJ8=
5+
github.com/golang-auth/go-gssapi-c v0.0.0-20240828194135-955ba90d4511 h1:k9cgAxS+AYKwAN7/moi03LK3EjTFUKeMRh9Cu2j4/D0=
6+
github.com/golang-auth/go-gssapi-c v0.0.0-20240828194135-955ba90d4511/go.mod h1:rb9NLAgRMfr732Kvm1mOH5J6eIx/WULl8rAFNXSzGqY=
7+
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
8+
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
9+
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
10+
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
11+
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
12+
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

examples/go/gss-client/gss-client.go

Lines changed: 277 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,277 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
package main
3+
4+
import (
5+
"bytes"
6+
"encoding/asn1"
7+
"encoding/binary"
8+
"encoding/hex"
9+
"flag"
10+
"fmt"
11+
"log"
12+
"net"
13+
"os"
14+
"strconv"
15+
"strings"
16+
"time"
17+
18+
_ "github.com/golang-auth/go-gssapi-c"
19+
"github.com/golang-auth/go-gssapi/v3"
20+
)
21+
22+
var _debug bool
23+
24+
var provider string = "GSSAPI-C"
25+
var gss = gssapi.NewProvider(provider)
26+
27+
func main() {
28+
port := flag.Int("port", 1234, "remote port to connect to")
29+
mech := flag.String("mech", "", "use specific mech OID")
30+
deleg := flag.Bool("d", false, "request delegation")
31+
file := flag.Bool("f", false, "use file")
32+
confReq := flag.Bool("seal", false, "seal (encrypt) the message")
33+
mutual := flag.Bool("mutual", false, "request mutual authentication")
34+
flag.BoolVar(&_debug, "debug", false, "enable debugging")
35+
flag.Parse()
36+
37+
if flag.NArg() != 3 {
38+
log.Fatalf("Usage: %s [-port <int>] [-mech <OID>] [-d] [-f] [-seal] [-mutual] [-debug] host service msg\n", os.Args[0])
39+
}
40+
41+
host := flag.Arg(0)
42+
service := flag.Arg(1)
43+
msg := flag.Arg(2)
44+
45+
// Connect to the host
46+
addr := fmt.Sprintf("%s:%d", host, *port)
47+
conn, err := net.Dial("tcp", addr)
48+
if err != nil {
49+
log.Fatal(err)
50+
}
51+
52+
debug("Connected to %s", addr)
53+
54+
var flags gssapi.ContextFlag
55+
if *mutual {
56+
flags |= gssapi.ContextFlagMutual
57+
}
58+
if *deleg {
59+
flags |= gssapi.ContextFlagDeleg
60+
}
61+
62+
opts := []gssapi.InitSecContextOption{
63+
gssapi.WithInitiatorFlags(flags),
64+
}
65+
if *mech != "" {
66+
oid, err := parseOid(*mech)
67+
if err != nil {
68+
log.Fatal(err)
69+
}
70+
gssMech, err := gssapi.MechFromOid(oid)
71+
if err != nil {
72+
log.Fatal(err)
73+
}
74+
opts = append(opts, gssapi.WithInitatorMech(gssMech))
75+
}
76+
77+
serviceName, err := gss.ImportName(service, gssapi.GSS_NT_HOSTBASED_SERVICE)
78+
if err != nil {
79+
log.Fatal(err)
80+
}
81+
defer serviceName.Release()
82+
83+
debug("Requested flags: %s", flags)
84+
85+
secctx, outToken, err := gss.InitSecContext(serviceName, opts...)
86+
if err != nil {
87+
log.Fatal(err)
88+
}
89+
90+
defer secctx.Delete()
91+
92+
if sendErr := sendToken(conn, outToken); sendErr != nil {
93+
log.Fatal(err)
94+
}
95+
debug("Sent context token (%d bytes):", len(outToken))
96+
debug("%s", formatToken(outToken))
97+
98+
for secctx.ContinueNeeded() {
99+
100+
inToken, err := recvToken(conn)
101+
if err != nil {
102+
log.Fatal(err)
103+
}
104+
debug("Read context token (%d bytes:", len(inToken))
105+
debug("%s", formatToken(inToken))
106+
107+
outToken, err = secctx.Continue(inToken)
108+
109+
if len(outToken) > 0 {
110+
if err := sendToken(conn, outToken); err != nil {
111+
log.Fatal(err)
112+
}
113+
debug("Sent context token (%d bytes):", len(outToken))
114+
debug("%s", formatToken(outToken))
115+
116+
}
117+
118+
if err != nil {
119+
log.Fatal(err)
120+
}
121+
}
122+
123+
info, err := secctx.Inquire()
124+
if err != nil {
125+
log.Fatal(err)
126+
}
127+
printContextInfo(info)
128+
129+
ntypes, err := gss.InquireNamesForMech(info.Mech)
130+
if err != nil {
131+
log.Fatal(err)
132+
}
133+
134+
debug("Name types supported by mech:")
135+
for _, nt := range ntypes {
136+
debug(" %-30s (%s)", nt, nt.OidString())
137+
}
138+
139+
var msgBuf []byte
140+
if *file {
141+
msgBuf, err = os.ReadFile(msg)
142+
if err != nil {
143+
log.Fatal(err)
144+
}
145+
} else {
146+
msgBuf = []byte(msg)
147+
}
148+
149+
// Wrap the message
150+
outMsg, hasConf, err := secctx.Wrap(msgBuf, *confReq, 0)
151+
if err != nil {
152+
log.Fatal(err)
153+
}
154+
155+
if !hasConf {
156+
debug("Warning! Message not encrypted.")
157+
}
158+
159+
if err = sendToken(conn, outMsg); err != nil {
160+
log.Fatal(err)
161+
}
162+
debug("Sent wrap message (%d bytes):\n%s", len(outMsg), formatToken(outMsg))
163+
164+
// Receive a wrapped token from the server
165+
msgMIC, err := recvToken(conn)
166+
if err != nil {
167+
log.Fatal(err)
168+
}
169+
debug("Received MIC message (%d bytes):\n%s", len(msgMIC), formatToken(msgMIC))
170+
171+
if _, err = secctx.VerifyMIC(msgBuf, msgMIC); err != nil {
172+
log.Fatal(err)
173+
}
174+
175+
fmt.Println("Successfully verified message signature (MIC) from server")
176+
177+
}
178+
179+
func printContextInfo(info *gssapi.SecContextInfo) {
180+
local := "remotely initiated"
181+
open := "closed"
182+
183+
if info.LocallyInitiated {
184+
local = "locally initiated"
185+
}
186+
if info.FullyEstablished {
187+
open = "open"
188+
}
189+
190+
debug("Context flags: %s", info.Flags)
191+
debug("\"%s\" to \"%s\", expires: %s, %s, %s",
192+
info.InitiatorName, info.AcceptorName,
193+
info.ExpiresAt.Round(time.Second),
194+
local,
195+
open)
196+
197+
debug("Name type of source is %s (%s)", info.AcceptorNameType, info.AcceptorNameType.OidString())
198+
debug("Name type of destination is %s (%s)", info.InitiatorNameType, info.InitiatorNameType.OidString())
199+
debug("Mechanism: %s (%s)", info.Mech, info.Mech.OidString())
200+
}
201+
202+
func parseOid(s string) (gssapi.Oid, error) {
203+
// split string into components
204+
elms := strings.Split(s, ".")
205+
206+
oid := make(asn1.ObjectIdentifier, len(elms))
207+
208+
for i, elm := range elms {
209+
j, err := strconv.ParseUint(elm, 10, 32)
210+
if err != nil {
211+
return nil, fmt.Errorf("non-number in OID: %w", err)
212+
}
213+
214+
oid[i] = int(j)
215+
}
216+
217+
enc, err := asn1.Marshal(oid)
218+
if err != nil {
219+
return nil, fmt.Errorf("parsing OID: %w", err)
220+
}
221+
222+
return enc[2:], nil
223+
}
224+
225+
func sendToken(conn net.Conn, token []byte) error {
226+
szBuff := make([]byte, 4)
227+
binary.BigEndian.PutUint32(szBuff, uint32(len(token)))
228+
_, err := conn.Write(szBuff)
229+
if err != nil {
230+
return err
231+
}
232+
233+
_, err = conn.Write(token)
234+
if err != nil {
235+
return err
236+
}
237+
238+
return nil
239+
}
240+
241+
func formatToken(tok []byte) string {
242+
b := &strings.Builder{}
243+
244+
bd := hex.Dumper(b)
245+
defer bd.Close()
246+
247+
bd.Write(tok)
248+
return b.String()
249+
}
250+
251+
func recvToken(conn net.Conn) (token []byte, err error) {
252+
szBuff := make([]byte, 4)
253+
_, err = conn.Read(szBuff)
254+
if err != nil {
255+
return
256+
}
257+
258+
buf := bytes.NewBuffer(szBuff)
259+
var tokenSize uint32
260+
binary.Read(buf, binary.BigEndian, &tokenSize)
261+
262+
token = make([]byte, tokenSize)
263+
_, err = conn.Read(token)
264+
if err != nil {
265+
return
266+
}
267+
268+
return
269+
}
270+
271+
func debug(format string, args ...interface{}) {
272+
if !_debug {
273+
return
274+
}
275+
276+
fmt.Printf(format+"\n", args...)
277+
}

0 commit comments

Comments
 (0)