summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorkali kaneko (leap communications) <kali@leap.se>2020-01-24 22:34:09 -0600
committerkali kaneko (leap communications) <kali@leap.se>2020-01-24 22:35:54 -0600
commit1c9220e04016d035c3c688c315ceabe274f45dfc (patch)
tree796488fe592e117fd97781a018bc4b2060a0672d
parentd3b21e5adc27cbb472e688b7c602e3bd721dec31 (diff)
initial sip implementation
-rw-r--r--cmd/vpnweb/vpnweb.go4
-rw-r--r--go.mod2
-rw-r--r--go.sum4
-rw-r--r--pkg/auth/middleware.go8
-rw-r--r--pkg/auth/sip2/client.go74
-rw-r--r--pkg/auth/sip2/spec.go141
-rw-r--r--pkg/auth/sip2/telnet.go43
7 files changed, 270 insertions, 6 deletions
diff --git a/cmd/vpnweb/vpnweb.go b/cmd/vpnweb/vpnweb.go
index 2305d98..ff25e09 100644
--- a/cmd/vpnweb/vpnweb.go
+++ b/cmd/vpnweb/vpnweb.go
@@ -21,11 +21,11 @@ func main() {
/* protected routes */
/* TODO ----
- http.HandleFunc("/3/auth", auth.AuthMiddleware(opts.Auth))
http.HandleFunc("/3/refresh-token", auth.RefreshAuthMiddleware(opts.Auth))
*/
- http.Handle("/3/cert", auth.AuthMiddleware(opts.Auth, ch))
+ http.Handle("/3/cert", auth.RestrictedMiddleware(opts.Auth, ch))
+ http.Handle("/3/auth", auth.Authenticator(opts.Auth))
/* static files */
diff --git a/go.mod b/go.mod
index 11be102..55f4406 100644
--- a/go.mod
+++ b/go.mod
@@ -8,5 +8,7 @@ require (
github.com/dgrijalva/jwt-go v3.2.0+incompatible
github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab // indirect
github.com/gorilla/mux v1.7.3 // indirect
+ github.com/reiver/go-oi v1.0.0 // indirect
+ github.com/reiver/go-telnet v0.0.0-20180421082511-9ff0b2ab096e
github.com/urfave/negroni v1.0.0 // indirect
)
diff --git a/go.sum b/go.sum
index 7211e92..6f0e519 100644
--- a/go.sum
+++ b/go.sum
@@ -8,5 +8,9 @@ github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab h1:xveKWz2iauee
github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab/go.mod h1:/P9AEU963A2AYjv4d1V5eVL1CQbEJq6aCNHDDjibzu8=
github.com/gorilla/mux v1.7.3 h1:gnP5JzjVOuiZD07fKKToCAOjS0yOpj/qPETTXCCS6hw=
github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
+github.com/reiver/go-oi v1.0.0 h1:nvECWD7LF+vOs8leNGV/ww+F2iZKf3EYjYZ527turzM=
+github.com/reiver/go-oi v1.0.0/go.mod h1:RrDBct90BAhoDTxB1fenZwfykqeGvhI6LsNfStJoEkI=
+github.com/reiver/go-telnet v0.0.0-20180421082511-9ff0b2ab096e h1:quuzZLi72kkJjl+f5AQ93FMcadG19WkS7MO6TXFOSas=
+github.com/reiver/go-telnet v0.0.0-20180421082511-9ff0b2ab096e/go.mod h1:+5vNVvEWwEIx86DB9Ke/+a5wBI464eDRo3eF0LcfpWg=
github.com/urfave/negroni v1.0.0 h1:kIimOitoypq34K7TG7DUaJ9kq/N4Ofuwi1sjz0KipXc=
github.com/urfave/negroni v1.0.0/go.mod h1:Meg73S6kFm/4PpbYdq35yYWoCZ9mS/YSx+lKnmiohz4=
diff --git a/pkg/auth/middleware.go b/pkg/auth/middleware.go
index 5fe0ab7..a183c1b 100644
--- a/pkg/auth/middleware.go
+++ b/pkg/auth/middleware.go
@@ -11,14 +11,13 @@ import (
const anonAuth string = "anon"
const sipAuth string = "sip"
+/* FIXME -- get this from configuration variables */
var jwtSecret = []byte("somethingverysecret")
-func getHandler(ch web.CertHandler) func(w http.ResponseWriter, r *http.Request) {
- return ch.CertResponder
+func Authenticator(auth string) {
}
-//func AuthMiddleware(auth string, ch web.CertHandler) func(w http.ResponseWriter, r *http.Request) {
-func AuthMiddleware(auth string, ch web.CertHandler) http.Handler {
+func RestrictedMiddleware(auth string, ch web.CertHandler) http.Handler {
jwtMiddleware := jwtmiddleware.New(jwtmiddleware.Options{
ValidationKeyGetter: func(token *jwt.Token) (interface{}, error) {
@@ -38,5 +37,6 @@ func AuthMiddleware(auth string, ch web.CertHandler) http.Handler {
default:
log.Fatal("Unknown auth module: '", auth, "'. Should be one of: ", anonAuth, ", ", sipAuth, ".")
}
+ // should not get here
return nil
}
diff --git a/pkg/auth/sip2/client.go b/pkg/auth/sip2/client.go
new file mode 100644
index 0000000..fbdeded
--- /dev/null
+++ b/pkg/auth/sip2/client.go
@@ -0,0 +1,74 @@
+package sip2
+
+import (
+ "fmt"
+ "github.com/reiver/go-telnet"
+ "log"
+ "time"
+)
+
+const loginRequestTemplate string = "9300CN%s|CO%s|CP%s|"
+const statusRequestTemplate string = "23000%s %sAO%s|AA%s|AD%s|"
+
+type Client struct {
+ Host string
+ Port string
+ location string
+ conn *telnet.Conn
+ parser *Parser
+}
+
+func NewClient(host, port, location string) Client {
+ c := Client{host, port, location, nil, nil}
+ c.parser = getParser()
+ return c
+}
+
+func (c *Client) Connect() (bool, error) {
+ conn, err := telnet.DialTo(c.Host + ":" + c.Port)
+ if nil != err {
+ log.Println(log.Printf("error: %v", err))
+ return false, err
+ }
+ c.conn = conn
+ return true, nil
+}
+
+func (c *Client) Login(user, pass string) bool {
+ loginStr := fmt.Sprintf(loginRequestTemplate, user, pass, c.location)
+ if nil == c.conn {
+ fmt.Println("error! null connection")
+ }
+ telnetSend(c.conn, loginStr)
+ loginResp := telnetRead(c.conn)
+ msg := c.parseResponse(loginResp)
+ if value, ok := c.parser.getFixedFieldValue(msg, Ok); ok && value == TRUE {
+ return true
+ }
+ return false
+}
+
+func (c *Client) CheckCredentials(user, passwd string) bool {
+ currentTime := time.Now()
+ statusRequest := fmt.Sprintf(
+ statusRequestTemplate,
+ currentTime.Format("20060102"),
+ currentTime.Format("150102"),
+ c.location, user, passwd)
+ telnetSend(c.conn, statusRequest)
+
+ statusMsg := c.parseResponse(telnetRead(c.conn))
+ if value, ok := c.parser.getFieldValue(statusMsg, ValidPatron); ok && value == YES {
+ if value, ok := c.parser.getFieldValue(statusMsg, ValidPatronPassword); ok && value == YES {
+ return true
+ }
+ }
+
+ // TODO log whatever error we can find (AF, Screen Message, for instance)
+ return false
+}
+
+func (c *Client) parseResponse(txt string) *Message {
+ msg := c.parser.parseMessage(txt)
+ return msg
+}
diff --git a/pkg/auth/sip2/spec.go b/pkg/auth/sip2/spec.go
new file mode 100644
index 0000000..9c4ac48
--- /dev/null
+++ b/pkg/auth/sip2/spec.go
@@ -0,0 +1,141 @@
+package sip2
+
+import (
+ "log"
+ "strconv"
+ "strings"
+)
+
+type FixedFieldSpec struct {
+ length int
+ label string
+}
+
+type FixedField struct {
+ spec FixedFieldSpec
+ value string
+}
+
+type VariableFieldSpec struct {
+ id string
+ label string
+}
+
+type VariableField struct {
+ spec VariableFieldSpec
+ value string
+}
+
+type MessageSpec struct {
+ id int
+ label string
+ fields []FixedFieldSpec
+}
+
+type Message struct {
+ fields []VariableField
+ fixed_fields []FixedField
+ msg_txt string
+}
+
+type Parser struct {
+ getMessageSpecByCode func(int) MessageSpec
+ getVariableFieldByCode func(string) VariableFieldSpec
+ getFixedFieldValue func(*Message, string) (string, bool)
+ getFieldValue func(*Message, string) (string, bool)
+ parseMessage func(string) *Message
+}
+
+const (
+ YES string = "Y"
+ TRUE string = "1"
+ Ok string = "ok"
+ Language string = "language"
+ PatronStatus string = "patron status"
+ Date string = "transaction date"
+ PatronIdentifier string = "patron identifier"
+ PatronPassword string = "patron password"
+ PersonalName string = "personal name"
+ ScreenMessage string = "screen message"
+ InstitutionId string = "institution id"
+ ValidPatron string = "valid patron"
+ ValidPatronPassword string = "valid patron password"
+ LoginResponse string = "Login Response"
+ PatronStatusResponse string = "Patron Status Response"
+)
+
+func getParser() *Parser {
+
+ LanguageSpec := FixedFieldSpec{3, Language}
+ PatronStatusSpec := FixedFieldSpec{14, PatronStatus}
+ DateSpec := FixedFieldSpec{18, Date}
+ OkSpec := FixedFieldSpec{1, Ok}
+
+ msgByCodeMap := map[int]MessageSpec{
+ 94: MessageSpec{94, LoginResponse, []FixedFieldSpec{OkSpec}},
+ 24: MessageSpec{24, PatronStatusResponse, []FixedFieldSpec{PatronStatusSpec, LanguageSpec, DateSpec}},
+ }
+
+ variableFieldByCodeMap := map[string]VariableFieldSpec{
+ "AA": VariableFieldSpec{"AA", PatronIdentifier},
+ "AD": VariableFieldSpec{"AD", PatronPassword},
+ "AE": VariableFieldSpec{"AE", PersonalName},
+ "AF": VariableFieldSpec{"AF", ScreenMessage},
+ "AO": VariableFieldSpec{"AO", InstitutionId},
+ "BL": VariableFieldSpec{"BL", ValidPatron},
+ "CQ": VariableFieldSpec{"CQ", ValidPatronPassword},
+ }
+
+ parser := new(Parser)
+ parser.getMessageSpecByCode = func(code int) MessageSpec {
+ return msgByCodeMap[code]
+ }
+ parser.getVariableFieldByCode = func(code string) VariableFieldSpec {
+ return variableFieldByCodeMap[code]
+ }
+ parser.getFixedFieldValue = func(msg *Message, field string) (string, bool) {
+ for _, v := range msg.fixed_fields {
+ if v.spec.label == field {
+ return v.value, true
+ }
+ }
+ return "", false
+ }
+ parser.getFieldValue = func(msg *Message, field string) (string, bool) {
+ for _, v := range msg.fields {
+ if v.spec.label == field {
+ return v.value, true
+ }
+ }
+ return "", false
+ }
+
+ parser.parseMessage = func(msg string) *Message {
+ txt := msg[:len(msg)-len(terminator)]
+ code, err := strconv.Atoi(txt[:2])
+ if nil != err {
+ log.Println("Error parsing integer: %s", txt[:2])
+ }
+ spec := parser.getMessageSpecByCode(code)
+ txt = txt[2:]
+
+ message := new(Message)
+ for _, sp := range spec.fields {
+ value := txt[:sp.length]
+ txt = txt[sp.length:]
+ message.fixed_fields = append(message.fixed_fields, FixedField{sp, value})
+ }
+ if len(txt) == 0 {
+ return message
+ }
+ for _, part := range strings.Split(txt, "|") {
+ if len(part) > 0 {
+ part_spec := parser.getVariableFieldByCode(part[:2])
+ value := part[2:]
+ message.fields = append(message.fields, VariableField{part_spec, value})
+ }
+ }
+ return message
+ }
+ return parser
+}
diff --git a/pkg/auth/sip2/telnet.go b/pkg/auth/sip2/telnet.go
new file mode 100644
index 0000000..b5abd5f
--- /dev/null
+++ b/pkg/auth/sip2/telnet.go
@@ -0,0 +1,43 @@
+package sip2
+
+import (
+ "github.com/reiver/go-telnet"
+)
+
+// TODO depends on how terminator is configured -- take it from config file
+// const terminator string = "\r\n"
+const terminator string = "\r"
+
+func telnetRead(conn *telnet.Conn) (out string) {
+ var buffer [1]byte
+ recvData := buffer[:]
+ var n int
+ var err error
+
+ for {
+ n, err = conn.Read(recvData)
+ if n <= 0 || err != nil {
+ break
+ } else {
+ out += string(recvData)
+ }
+ if len(out) > 1 && out[len(out)-len(terminator):] == terminator {
+ break
+ }
+ }
+ return out
+}
+
+func telnetSend(conn *telnet.Conn, command string) {
+ var commandBuffer []byte
+ for _, char := range command {
+ commandBuffer = append(commandBuffer, byte(char))
+ }
+
+ var crlfBuffer [2]byte = [2]byte{'\r', '\n'}
+
+ crlf := crlfBuffer[:]
+
+ conn.Write(commandBuffer)
+ conn.Write(crlf)
+}