Skip to content

Commit b04ae5e

Browse files
Hendrik EckardtMeneDev
authored andcommitted
Add support for multiple slots
Rewrite TLV handling to return a list instead of map. This way, the name tags can be inspected and the correct one can be picked. The desired slot name is passed as an (optional) argument. If the argument is omitted, the first slot is used.
1 parent e11a602 commit b04ae5e

File tree

8 files changed

+62
-21
lines changed

8 files changed

+62
-21
lines changed

cmd/yubi-oath-vpn/options_linux.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,6 @@ package main
22

33
type Options struct {
44
ConnectionName string `required:"yes" short:"c" long:"connection" description:"The name of the connection as shown by 'nmcli c show'"`
5+
SlotName string `required:"no" short:"s" long:"slot" description:"The name of the YubiKey slot to use (typically of the form user@example.com)"`
56
ShowVersion bool `required:"no" short:"v" long:"version" description:"Show version and exit"`
67
}

cmd/yubi-oath-vpn/options_windows.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,6 @@ package main
22

33
type Options struct {
44
ConnectionName string `required:"yes" short:"c" long:"connection" description:"The name of the OpenVPN connection without extension'"`
5+
SlotName string `required:"no" short:"s" long:"slot" description:"The name of the YubiKey slot to use (typically of the form user@example.com)"`
56
ShowVersion bool `required:"no" short:"v" long:"version" description:"Show version and exit"`
67
}

cmd/yubi-oath-vpn/yubi-oath-vpn.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ func main() {
8686
if applicableYubiKey(key) {
8787
connectedToTun, _ := isConnectedToTun()
8888
if !connectedToTun {
89-
controller.ConnectWith(key, opts.ConnectionName)
89+
controller.ConnectWith(key, opts.ConnectionName, opts.SlotName)
9090
} else {
9191
log.Printf("Connected TUN device found, not trying to connect")
9292
}

gui2/gui_controller.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ type ConnectionParameters struct {
1818
}
1919

2020
type GuiController interface {
21-
ConnectWith(key yubikey.YubiKey, connectionId string)
21+
ConnectWith(key yubikey.YubiKey, connectionId string, slotName string)
2222
InitializeConnection() chan ConnectionParameters
2323
ConnectionResult(events netctrl.ConnectionAttemptResult)
2424
SetLatestVersion(release githubreleasemon.Release)
@@ -33,6 +33,7 @@ type guiController struct {
3333
yubiKey yubikey.YubiKey
3434
initializeConnectionChan chan ConnectionParameters
3535
connectionId string
36+
slotName string
3637
cancelCurrentConnection context.CancelFunc
3738
}
3839

@@ -102,9 +103,9 @@ func GuiControllerNew(ctx context.Context, title string) (GuiController, error)
102103
return controller, nil
103104
}
104105

105-
func (ctrl *guiController) ConnectWith(key yubikey.YubiKey, connectionId string) {
106+
func (ctrl *guiController) ConnectWith(key yubikey.YubiKey, connectionId string, slotName string) {
106107
println("ConnectWith")
107-
ctrl.sendEvent(evKeyInserted, key, connectionId)
108+
ctrl.sendEvent(evKeyInserted, key, connectionId, slotName)
108109
go func() {
109110
<-key.Context().Done()
110111

gui2/states.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ func eventString(e *fsm.Event, idx int) string {
8888
func (ctrl *guiController) enterPrepare(e *fsm.Event) {
8989
key := key(e, 0)
9090
connectionId := eventString(e, 1)
91+
slotName := eventString(e, 2)
9192
//key.RequiresPassword()
9293
// TODO password required?
9394
glib.IdleAdd(func() {
@@ -96,6 +97,7 @@ func (ctrl *guiController) enterPrepare(e *fsm.Event) {
9697

9798
ctrl.yubiKey = key
9899
ctrl.connectionId = connectionId
100+
ctrl.slotName = slotName
99101
ctrl.sendEvent(evPasswordRequired, key, connectionId)
100102
}
101103
func (ctrl *guiController) leavePrepare(e *fsm.Event) {
@@ -139,7 +141,7 @@ func (ctrl *guiController) enterConnecting(e *fsm.Event) {
139141
ctrl.gtkGui.HideError()
140142

141143
password := e.Args[0].(string)
142-
code, err := ctrl.yubiKey.GetCodeWithPassword(password)
144+
code, err := ctrl.yubiKey.GetCodeWithPassword(password, ctrl.slotName)
143145

144146
println("code", code)
145147
println("err", err)

yubierror/errors.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ const (
77
ErrorChkWrong YubiKeyError = iota
88
ErrorWrongPassword YubiKeyError = iota
99
ErrorUserCancled YubiKeyError = iota
10+
ErrorSlotNotFound YubiKeyError = iota
1011
)
1112

1213
func (e YubiKeyError) Error() string {
@@ -17,6 +18,8 @@ func (e YubiKeyError) Error() string {
1718
return "Wrong YubiKey password"
1819
case ErrorUserCancled:
1920
return "User canceled"
21+
case ErrorSlotNotFound:
22+
return "No slot with the specified name was found"
2023
}
2124
return "unknown error"
2225
}

yubikey/scard/scard_yubikey.go

Lines changed: 48 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ func (key *scardYubiKey) Context() context.Context {
5757
return key.ctx
5858
}
5959

60-
func (key *scardYubiKey) GetCodeWithPassword(pwd string) (string, error) {
60+
func (key *scardYubiKey) GetCodeWithPassword(pwd string, slotName string) (string, error) {
6161

6262
card := key.card
6363

@@ -93,16 +93,18 @@ func (key *scardYubiKey) GetCodeWithPassword(pwd string) (string, error) {
9393
return "", err
9494
}
9595

96-
tlvs, err := key.parseTlvs(resp_oath)
96+
tlvsList, err := key.parseTlvs(resp_oath)
9797
if err != nil {
9898
return "", err
9999
}
100+
tlvs := tlvsToMap(tlvsList)
100101

101102
OATH_TAG_NAME := byte(0x71)
102103
OATH_TAG_CHALLENGE := byte(0x74)
103104
OATH_TAG_ALGORITHM := byte(0x7b)
104105
OATH_TAG_VERSION := byte(0x79)
105106
OATH_TAG_RESPONSE := byte(0x75)
107+
OATH_TAG_TRUNCATED_RESPONSE := byte(0x76)
106108

107109
name := binary.BigEndian.Uint64(tlvs[OATH_TAG_NAME].value)
108110

@@ -144,16 +146,17 @@ func (key *scardYubiKey) GetCodeWithPassword(pwd string) (string, error) {
144146
return "", err
145147
}
146148

147-
verify_tlvs, err := key.parseTlvs(verify_resp)
149+
verifyTlvsList, err := key.parseTlvs(verify_resp)
148150
if err != nil {
149151
return "", err
150152
}
153+
verifyTlvs := tlvsToMap(verifyTlvsList)
151154

152-
println(verify_tlvs)
155+
println(verifyTlvs)
153156
fmt.Printf("verification: % 0x\n", verification)
154-
fmt.Printf("verification: % 0x\n", verify_tlvs[OATH_TAG_RESPONSE].value)
157+
fmt.Printf("verification: % 0x\n", verifyTlvs[OATH_TAG_RESPONSE].value)
155158

156-
if !reflect.DeepEqual(verification, verify_tlvs[OATH_TAG_RESPONSE].value) {
159+
if !reflect.DeepEqual(verification, verifyTlvs[OATH_TAG_RESPONSE].value) {
157160
panic("Verification failed")
158161
}
159162

@@ -172,20 +175,40 @@ func (key *scardYubiKey) GetCodeWithPassword(pwd string) (string, error) {
172175
}
173176
fmt.Printf("% 0x\n", rsp_5)
174177

175-
creds_tlvs, err := key.parseTlvs(rsp_5)
178+
credsTlvs, err := key.parseTlvs(rsp_5)
176179
if err != nil {
177180
return "", err
178181
}
179182

180-
TRUNCATED_RESPONSE := byte(0x76)
183+
foundSlot := false
184+
var strCode string
185+
for _, tlv := range credsTlvs {
186+
if tlv.tag == OATH_TAG_NAME {
187+
keySlotName := string(tlv.value)
188+
189+
if slotName == "" || keySlotName == slotName {
190+
foundSlot = true
191+
fmt.Printf("slot %s matched\n", keySlotName)
192+
} else {
193+
fmt.Printf("found non-matching slot %s\n", keySlotName)
194+
}
195+
}
196+
197+
if foundSlot && tlv.tag == OATH_TAG_TRUNCATED_RESPONSE {
198+
fmt.Printf("code is in: % 0x\n", tlv.value)
181199

182-
fmt.Printf("code is in: % 0x\n", creds_tlvs[TRUNCATED_RESPONSE].value)
200+
code := parseTruncated(tlv.value[1:])
183201

184-
code := parseTruncated(creds_tlvs[TRUNCATED_RESPONSE].value[1:])
202+
fmt.Printf("code: %06d\n", code)
203+
strCode = fmt.Sprintf("%06d", code)
185204

186-
fmt.Printf("code: %06d\n", code)
205+
break
206+
}
207+
}
187208

188-
strCode := fmt.Sprintf("%06d", code)
209+
if !foundSlot {
210+
return "", yubierror.ErrorSlotNotFound
211+
}
189212

190213
return strCode, err
191214
}
@@ -260,8 +283,8 @@ type Tlv struct {
260283
value []byte
261284
}
262285

263-
func (self *scardYubiKey) parseTlvs(response []byte) (map[byte]Tlv, error) {
264-
tlvs := make(map[byte]Tlv)
286+
func (self *scardYubiKey) parseTlvs(response []byte) ([]Tlv, error) {
287+
var tlvs []Tlv
265288
for len(response) > 0 {
266289
tag := response[0]
267290
ln := uint64(response[1])
@@ -284,12 +307,22 @@ func (self *scardYubiKey) parseTlvs(response []byte) (map[byte]Tlv, error) {
284307
value: value,
285308
}
286309

287-
tlvs[tag] = tlv
310+
tlvs = append(tlvs, tlv)
288311
}
289312

290313
return tlvs, nil
291314
}
292315

316+
func tlvsToMap(tlvs []Tlv) map[byte]Tlv {
317+
result := make(map[byte]Tlv)
318+
319+
for _, tlv := range tlvs {
320+
result[tlv.tag] = tlv
321+
}
322+
323+
return result
324+
}
325+
293326
func (self Tlv) buffer() []byte {
294327
res := make([]byte, 1)
295328
res[0] = self.tag

yubikey/yubikey.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,5 @@ import "context"
44

55
type YubiKey interface {
66
Context() context.Context
7-
GetCodeWithPassword(password string) (string, error)
7+
GetCodeWithPassword(password string, slotName string) (string, error)
88
}

0 commit comments

Comments
 (0)