Browse Source

fix authenticating with vlc and login prompt

pull/31/head
aler9 6 years ago
parent
commit
4c6f929c02
  1. 2
      go.mod
  2. 4
      go.sum
  3. 26
      main.go
  4. 106
      server-client.go
  5. 12
      server-udpl.go

2
go.mod

@ -5,7 +5,7 @@ go 1.13
require ( require (
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 // indirect github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 // indirect
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d // indirect github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d // indirect
github.com/aler9/gortsplib v0.0.0-20200709205811-04b0a6eabe37 github.com/aler9/gortsplib v0.0.0-20200710091324-fb7d7b008e68
github.com/stretchr/testify v1.4.0 github.com/stretchr/testify v1.4.0
gopkg.in/alecthomas/kingpin.v2 v2.2.6 gopkg.in/alecthomas/kingpin.v2 v2.2.6
gopkg.in/yaml.v2 v2.2.2 gopkg.in/yaml.v2 v2.2.2

4
go.sum

@ -2,8 +2,8 @@ github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafo
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d h1:UQZhZ2O0vMHr2cI+DC1Mbh0TJxzA3RcLoMsFw+aXw7E= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d h1:UQZhZ2O0vMHr2cI+DC1Mbh0TJxzA3RcLoMsFw+aXw7E=
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
github.com/aler9/gortsplib v0.0.0-20200709205811-04b0a6eabe37 h1:O92YFUvPXejUg08pDIQTI8MydvHZBnCnSsOMxP2Q828= github.com/aler9/gortsplib v0.0.0-20200710091324-fb7d7b008e68 h1:apyYugiG/luHl0Xyc2xJtGkL2HbF1umxjCNP2sX9iyw=
github.com/aler9/gortsplib v0.0.0-20200709205811-04b0a6eabe37/go.mod h1:sL64nUkmrTVhlT/GCaxRXyI2Xk7m8XSdw5Uv8xKGPdc= github.com/aler9/gortsplib v0.0.0-20200710091324-fb7d7b008e68/go.mod h1:sL64nUkmrTVhlT/GCaxRXyI2Xk7m8XSdw5Uv8xKGPdc=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=

26
main.go

@ -192,16 +192,16 @@ type programEventTerminate struct{}
func (programEventTerminate) isProgramEvent() {} func (programEventTerminate) isProgramEvent() {}
type ConfPath struct { type ConfPath struct {
Source string `yaml:"source"` Source string `yaml:"source"`
SourceProtocol string `yaml:"sourceProtocol"` SourceProtocol string `yaml:"sourceProtocol"`
PublishUser string `yaml:"publishUser"` PublishUser string `yaml:"publishUser"`
PublishPass string `yaml:"publishPass"` PublishPass string `yaml:"publishPass"`
PublishIps []string `yaml:"publishIps"` PublishIps []string `yaml:"publishIps"`
publishIps []interface{} publishIpsParsed []interface{}
ReadUser string `yaml:"readUser"` ReadUser string `yaml:"readUser"`
ReadPass string `yaml:"readPass"` ReadPass string `yaml:"readPass"`
ReadIps []string `yaml:"readIps"` ReadIps []string `yaml:"readIps"`
readIps []interface{} readIpsParsed []interface{}
} }
type conf struct { type conf struct {
@ -366,7 +366,7 @@ func newProgram(sargs []string, stdin io.Reader) (*program, error) {
return nil, fmt.Errorf("publish password must be alphanumeric") return nil, fmt.Errorf("publish password must be alphanumeric")
} }
} }
pconf.publishIps, err = parseIpCidrList(pconf.PublishIps) pconf.publishIpsParsed, err = parseIpCidrList(pconf.PublishIps)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -387,7 +387,7 @@ func newProgram(sargs []string, stdin io.Reader) (*program, error) {
if pconf.ReadUser != "" && pconf.ReadPass == "" || pconf.ReadUser == "" && pconf.ReadPass != "" { if pconf.ReadUser != "" && pconf.ReadPass == "" || pconf.ReadUser == "" && pconf.ReadPass != "" {
return nil, fmt.Errorf("read username and password must be both filled") return nil, fmt.Errorf("read username and password must be both filled")
} }
pconf.readIps, err = parseIpCidrList(pconf.ReadIps) pconf.readIpsParsed, err = parseIpCidrList(pconf.ReadIps)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -482,7 +482,7 @@ outer:
// close all other clients that share the same path // close all other clients that share the same path
if pub.publisherIsReady() { if pub.publisherIsReady() {
for oc := range p.clients { for oc := range p.clients {
if oc.path == evt.client.path { if oc != evt.client && oc.path == evt.client.path {
go oc.close() go oc.close()
} }
} }

106
server-client.go

@ -71,8 +71,10 @@ type serverClient struct {
conn *gortsplib.ConnServer conn *gortsplib.ConnServer
state clientState state clientState
path string path string
publishAuth *gortsplib.AuthServer authUser string
readAuth *gortsplib.AuthServer authPass string
authHelper *gortsplib.AuthServer
authFailures int
streamSdpText []byte // filled only if publisher streamSdpText []byte // filled only if publisher
streamSdpParsed *sdp.Message // filled only if publisher streamSdpParsed *sdp.Message // filled only if publisher
streamProtocol streamProtocol streamProtocol streamProtocol
@ -86,8 +88,8 @@ type serverClient struct {
writeBuf2 []byte writeBuf2 []byte
writeCurBuf bool writeCurBuf bool
writec chan *gortsplib.InterleavedFrame writeChan chan *gortsplib.InterleavedFrame
done chan struct{} done chan struct{}
} }
func newServerClient(p *program, nconn net.Conn) *serverClient { func newServerClient(p *program, nconn net.Conn) *serverClient {
@ -103,7 +105,7 @@ func newServerClient(p *program, nconn net.Conn) *serverClient {
readBuf2: make([]byte, 0, 512*1024), readBuf2: make([]byte, 0, 512*1024),
writeBuf1: make([]byte, 2048), writeBuf1: make([]byte, 2048),
writeBuf2: make([]byte, 2048), writeBuf2: make([]byte, 2048),
writec: make(chan *gortsplib.InterleavedFrame), writeChan: make(chan *gortsplib.InterleavedFrame),
done: make(chan struct{}), done: make(chan struct{}),
} }
@ -164,7 +166,7 @@ func (c *serverClient) run() {
} }
go func() { go func() {
for range c.writec { for range c.writeChan {
} }
}() }()
@ -182,9 +184,10 @@ func (c *serverClient) run() {
c.p.events <- programEventClientClose{done, c} c.p.events <- programEventClientClose{done, c}
<-done <-done
close(c.writec) close(c.writeChan)
c.conn.NetConn().Close() // close socket in case it has not been closed yet
close(c.done) close(c.done) // close() never blocks
} }
func (c *serverClient) close() { func (c *serverClient) close() {
@ -204,7 +207,7 @@ func (c *serverClient) writeFrame(channel uint8, inbuf []byte) {
copy(buf, inbuf) copy(buf, inbuf)
c.writeCurBuf = !c.writeCurBuf c.writeCurBuf = !c.writeCurBuf
c.writec <- &gortsplib.InterleavedFrame{ c.writeChan <- &gortsplib.InterleavedFrame{
Channel: channel, Channel: channel,
Content: buf, Content: buf,
} }
@ -224,70 +227,101 @@ func (c *serverClient) writeResError(req *gortsplib.Request, code gortsplib.Stat
}) })
} }
func (c *serverClient) findConfForPath(path string) *ConfPath {
if pconf, ok := c.p.conf.Paths[path]; ok {
return pconf
}
if pconf, ok := c.p.conf.Paths["all"]; ok {
return pconf
}
return nil
}
var errAuthCritical = errors.New("auth critical") var errAuthCritical = errors.New("auth critical")
var errAuthNotCritical = errors.New("auth not critical") var errAuthNotCritical = errors.New("auth not critical")
func (c *serverClient) validateAuth(req *gortsplib.Request, user string, pass string, auth **gortsplib.AuthServer, ips []interface{}) error { func (c *serverClient) authenticate(ips []interface{}, user string, pass string, req *gortsplib.Request) error {
// validate ip
err := func() error { err := func() error {
if ips == nil { if ips == nil {
return nil return nil
} }
connIp := c.conn.NetConn().LocalAddr().(*net.TCPAddr).IP ip := c.ip()
for _, item := range ips { for _, item := range ips {
switch titem := item.(type) { switch titem := item.(type) {
case net.IP: case net.IP:
if titem.Equal(connIp) { if titem.Equal(ip) {
return nil return nil
} }
case *net.IPNet: case *net.IPNet:
if titem.Contains(connIp) { if titem.Contains(ip) {
return nil return nil
} }
} }
} }
c.log("ERR: ip '%s' not allowed", connIp) c.log("ERR: ip '%s' not allowed", ip)
return errAuthCritical return errAuthCritical
}() }()
if err != nil { if err != nil {
return err return err
} }
// validate credentials
err = func() error { err = func() error {
if user == "" { if user == "" {
return nil return nil
} }
initialRequest := false // reset authHelper every time the credentials change
if *auth == nil { if c.authHelper == nil || c.authUser != user || c.authPass != pass {
initialRequest = true c.authUser = user
*auth = gortsplib.NewAuthServer(user, pass, nil) c.authPass = pass
c.authHelper = gortsplib.NewAuthServer(user, pass, nil)
} }
err := (*auth).ValidateHeader(req.Header["Authorization"], req.Method, req.Url) err := c.authHelper.ValidateHeader(req.Header["Authorization"], req.Method, req.Url)
if err != nil { if err != nil {
if !initialRequest { c.authFailures += 1
// vlc with login prompt sends 4 requests:
// 1) without credentials
// 2) with password but without the username
// 3) without credentials
// 4) with password and username
// hence we must allow up to 3 failures
var retErr error
if c.authFailures > 3 {
c.log("ERR: unauthorized: %s", err) c.log("ERR: unauthorized: %s", err)
retErr = errAuthCritical
} else if c.authFailures > 1 {
c.log("WARN: unauthorized: %s", err)
retErr = errAuthNotCritical
} else {
retErr = errAuthNotCritical
} }
c.conn.WriteResponse(&gortsplib.Response{ c.conn.WriteResponse(&gortsplib.Response{
StatusCode: gortsplib.StatusUnauthorized, StatusCode: gortsplib.StatusUnauthorized,
Header: gortsplib.Header{ Header: gortsplib.Header{
"CSeq": req.Header["CSeq"], "CSeq": req.Header["CSeq"],
"WWW-Authenticate": (*auth).GenerateHeader(), "WWW-Authenticate": c.authHelper.GenerateHeader(),
}, },
}) })
if !initialRequest { return retErr
return errAuthCritical
}
return errAuthNotCritical
} }
// reset authFailures after a successful login
c.authFailures = 0
return nil return nil
}() }()
if err != nil { if err != nil {
@ -297,18 +331,6 @@ func (c *serverClient) validateAuth(req *gortsplib.Request, user string, pass st
return nil return nil
} }
func (c *serverClient) findConfForPath(path string) *ConfPath {
if pconf, ok := c.p.conf.Paths[path]; ok {
return pconf
}
if pconf, ok := c.p.conf.Paths["all"]; ok {
return pconf
}
return nil
}
func (c *serverClient) handleRequest(req *gortsplib.Request) bool { func (c *serverClient) handleRequest(req *gortsplib.Request) bool {
c.log(string(req.Method)) c.log(string(req.Method))
@ -370,7 +392,7 @@ func (c *serverClient) handleRequest(req *gortsplib.Request) bool {
return false return false
} }
err := c.validateAuth(req, pconf.ReadUser, pconf.ReadPass, &c.readAuth, pconf.readIps) err := c.authenticate(pconf.readIpsParsed, pconf.ReadUser, pconf.ReadPass, req)
if err != nil { if err != nil {
if err == errAuthCritical { if err == errAuthCritical {
return false return false
@ -411,7 +433,7 @@ func (c *serverClient) handleRequest(req *gortsplib.Request) bool {
return false return false
} }
err := c.validateAuth(req, pconf.PublishUser, pconf.PublishPass, &c.publishAuth, pconf.publishIps) err := c.authenticate(pconf.publishIpsParsed, pconf.PublishUser, pconf.PublishPass, req)
if err != nil { if err != nil {
if err == errAuthCritical { if err == errAuthCritical {
return false return false
@ -484,7 +506,7 @@ func (c *serverClient) handleRequest(req *gortsplib.Request) bool {
return false return false
} }
err := c.validateAuth(req, pconf.ReadUser, pconf.ReadPass, &c.readAuth, pconf.readIps) err := c.authenticate(pconf.readIpsParsed, pconf.ReadUser, pconf.ReadPass, req)
if err != nil { if err != nil {
if err == errAuthCritical { if err == errAuthCritical {
return false return false
@ -772,7 +794,7 @@ func (c *serverClient) handleRequest(req *gortsplib.Request) bool {
if c.streamProtocol == _STREAM_PROTOCOL_TCP { if c.streamProtocol == _STREAM_PROTOCOL_TCP {
// write RTP frames sequentially // write RTP frames sequentially
go func() { go func() {
for frame := range c.writec { for frame := range c.writeChan {
c.conn.WriteInterleavedFrame(frame) c.conn.WriteInterleavedFrame(frame)
} }
}() }()

12
server-udpl.go

@ -21,8 +21,8 @@ type serverUdpListener struct {
writeBuf2 []byte writeBuf2 []byte
writeCurBuf bool writeCurBuf bool
writec chan *udpWrite writeChan chan *udpWrite
done chan struct{} done chan struct{}
} }
func newServerUdpListener(p *program, port int, trackFlowType trackFlowType) (*serverUdpListener, error) { func newServerUdpListener(p *program, port int, trackFlowType trackFlowType) (*serverUdpListener, error) {
@ -41,7 +41,7 @@ func newServerUdpListener(p *program, port int, trackFlowType trackFlowType) (*s
readBuf2: make([]byte, 2048), readBuf2: make([]byte, 2048),
writeBuf1: make([]byte, 2048), writeBuf1: make([]byte, 2048),
writeBuf2: make([]byte, 2048), writeBuf2: make([]byte, 2048),
writec: make(chan *udpWrite), writeChan: make(chan *udpWrite),
done: make(chan struct{}), done: make(chan struct{}),
} }
@ -61,7 +61,7 @@ func (l *serverUdpListener) log(format string, args ...interface{}) {
func (l *serverUdpListener) run() { func (l *serverUdpListener) run() {
go func() { go func() {
for w := range l.writec { for w := range l.writeChan {
l.nconn.SetWriteDeadline(time.Now().Add(l.p.conf.WriteTimeout)) l.nconn.SetWriteDeadline(time.Now().Add(l.p.conf.WriteTimeout))
l.nconn.WriteTo(w.buf, w.addr) l.nconn.WriteTo(w.buf, w.addr)
} }
@ -88,7 +88,7 @@ func (l *serverUdpListener) run() {
} }
} }
close(l.writec) close(l.writeChan)
close(l.done) close(l.done)
} }
@ -110,7 +110,7 @@ func (l *serverUdpListener) write(addr *net.UDPAddr, inbuf []byte) {
copy(buf, inbuf) copy(buf, inbuf)
l.writeCurBuf = !l.writeCurBuf l.writeCurBuf = !l.writeCurBuf
l.writec <- &udpWrite{ l.writeChan <- &udpWrite{
addr: addr, addr: addr,
buf: buf, buf: buf,
} }

Loading…
Cancel
Save