rtsp server support digest auth

pull/185/head
ZSC714725 3 years ago
parent ad376bcc64
commit 1357fa5db7

@ -9,6 +9,7 @@
package rtsp
import (
"crypto/rand"
"encoding/base64"
"fmt"
"strings"
@ -32,6 +33,45 @@ type Auth struct {
Realm string
Nonce string
Algorithm string
Uri string
Response string
Opaque string
Stale string
}
func (a *Auth) ParseAuthorization(authStr string) (err error) {
switch {
case strings.HasPrefix(authStr, "Basic "):
a.Typ = AuthTypeDigest
authBase64Str := strings.TrimLeft(authStr, "Basic ")
authInfo, err := base64.StdEncoding.DecodeString(authBase64Str)
if err != nil {
return err
}
tmp := strings.Split(string(authInfo), ":")
if len(tmp) != 2 {
return fmt.Errorf("invalid Authorization:%s", authStr)
}
a.Username, a.Password = tmp[0], tmp[1]
case strings.HasPrefix(authStr, "Digest "):
a.Typ = AuthTypeDigest
authDigestStr := strings.TrimLeft(authStr, "Digest ")
a.Username = a.getV(authDigestStr, `username="`)
a.Realm = a.getV(authDigestStr, `realm="`)
a.Nonce = a.getV(authDigestStr, `nonce="`)
a.Uri = a.getV(authDigestStr, `uri="`)
a.Algorithm = a.getV(authDigestStr, `algorithm="`)
a.Response = a.getV(authDigestStr, `response="`)
a.Opaque = a.getV(authDigestStr, `opaque="`)
a.Stale = a.getV(authDigestStr, `stale="`)
}
return nil
}
func (a *Auth) FeedWwwAuthenticate(auths []string, username, password string) {
@ -94,6 +134,36 @@ func (a *Auth) MakeAuthorization(method, uri string) string {
return ""
}
func (a *Auth) MakeAuthenticate(method string) string {
switch method {
case AuthTypeBasic:
return fmt.Sprintf("%s realm=\"Lal Server\"")
case AuthTypeDigest:
return fmt.Sprintf("%s realm=\"Lal Server\", nonce=\"%s\"", method, a.nonce())
}
return ""
}
func (a *Auth) CheckAuthorization(method, username, password string) bool {
switch a.Typ {
case AuthTypeBasic:
if username == a.Username && password == a.Password {
return true
}
case AuthTypeDigest:
// The "response" field is computed as:
// md5(md5(<username>:<realm>:<password>):<nonce>:md5(<cmd>:<url>))
ha1 := nazamd5.Md5([]byte(fmt.Sprintf("%s:%s:%s", username, a.Realm, password)))
ha2 := nazamd5.Md5([]byte(fmt.Sprintf("%s:%s", method, a.Uri)))
response := nazamd5.Md5([]byte(fmt.Sprintf("%s:%s:%s", ha1, a.Nonce, ha2)))
if a.Response == response {
return true
}
}
return false
}
func (a *Auth) getV(s string, pre string) string {
b := strings.Index(s, pre)
if b == -1 {
@ -105,3 +175,13 @@ func (a *Auth) getV(s string, pre string) string {
}
return s[b+len(pre) : b+len(pre)+e]
}
func (a *Auth) nonce() string {
k := make([]byte, 32)
for bytes := 0; bytes < len(k); {
n, _ := rand.Read(k[bytes:])
bytes += n
}
return nazamd5.Md5(k)
}

@ -77,10 +77,10 @@ var ResponseTeardownTmpl = "RTSP/1.0 200 OK\r\n" +
"CSeq: %s\r\n" +
"\r\n"
var ResponseBasicAuthorizedTmpl = "RTSP/1.0 401 Unauthorized\r\n" +
var ResponseAuthorizedTmpl = "RTSP/1.0 401 Unauthorized\r\n" +
"CSeq: %s\r\n" +
"Date: %s\r\n" +
"WWW-Authenticate: %s realm=\"Lal Server\"\r\n" +
"WWW-Authenticate: %s\r\n" +
"\r\n"
func PackResponseOptions(cseq string) string {
@ -115,9 +115,9 @@ func PackResponseTeardown(cseq string) string {
return fmt.Sprintf(ResponseTeardownTmpl, cseq)
}
func PackResponseBasicAuthorized(cseq, authMethod string) string {
func PackResponseAuthorized(cseq, authenticate string) string {
date := time.Now().Format(time.RFC1123)
return fmt.Sprintf(ResponseBasicAuthorizedTmpl, cseq, date, authMethod)
return fmt.Sprintf(ResponseAuthorizedTmpl, cseq, date, authenticate)
}
// PackRequest @param body 可以为空

@ -10,7 +10,6 @@ package rtsp
import (
"bufio"
"encoding/base64"
"fmt"
"net"
"strings"
@ -55,18 +54,19 @@ type ServerCommandSession struct {
prevConnStat connection.Stat
staleStat *connection.Stat
stat base.StatSession
auth ServerAuthConfig
authConf ServerAuthConfig
auth Auth
pubSession *PubSession
subSession *SubSession
}
func NewServerCommandSession(observer IServerCommandSessionObserver, conn net.Conn, auth ServerAuthConfig) *ServerCommandSession {
func NewServerCommandSession(observer IServerCommandSessionObserver, conn net.Conn, authConf ServerAuthConfig) *ServerCommandSession {
uk := base.GenUkRtspServerCommandSession()
s := &ServerCommandSession{
uniqueKey: uk,
observer: observer,
auth: auth,
authConf: authConf,
conn: connection.New(conn, func(option *connection.Option) {
option.ReadBufSize = serverCommandSessionReadBufSize
option.WriteChanSize = serverCommandSessionWriteChanSize
@ -254,7 +254,7 @@ func (session *ServerCommandSession) handleAnnounce(requestCtx nazahttp.HttpReqM
func (session *ServerCommandSession) handleDescribe(requestCtx nazahttp.HttpReqMsgCtx) error {
Log.Infof("[%s] < R DESCRIBE", session.uniqueKey)
if session.auth.AuthEnable {
if session.authConf.AuthEnable {
// 鉴权处理
authresp, err := session.handleAuthorized(requestCtx)
if err != nil {
@ -291,35 +291,37 @@ func (session *ServerCommandSession) handleDescribe(requestCtx nazahttp.HttpReqM
func (session *ServerCommandSession) handleAuthorized(requestCtx nazahttp.HttpReqMsgCtx) (string, error) {
if requestCtx.Headers.Get(HeaderAuthorization) != "" {
authStr := requestCtx.Headers.Get(HeaderAuthorization)
if strings.Contains(authStr, AuthTypeBasic) {
// Basic 鉴权
authBase64Client := strings.TrimLeft(authStr, "Basic ")
authstr := fmt.Sprintf("%s:%s", session.auth.UserName, session.auth.PassWord)
authBase64Server := base64.StdEncoding.EncodeToString([]byte(authstr))
if authBase64Server == authBase64Client {
Log.Infof("[%s] Rtsp Basic auth success. uri=%s", session.uniqueKey, requestCtx.Uri)
} else {
// TODO(chef): [refactor] 错误放入base/error.go中 202205
err := fmt.Errorf("rtsp basic auth failed, auth:%s", authBase64Client)
return "", err
authorization := requestCtx.Headers.Get(HeaderAuthorization)
session.auth.ParseAuthorization(authorization)
// 解析出的鉴权方式需要与配置的鉴权方式一致,防止鉴权降级
if session.auth.Typ == AuthTypeBasic && session.authConf.AuthMethod == 0 ||
session.auth.Typ == AuthTypeDigest && session.authConf.AuthMethod == 1 {
if session.auth.CheckAuthorization(requestCtx.Method, session.authConf.UserName, session.authConf.PassWord) {
return "", nil
}
} else {
err := fmt.Errorf("unsupport, auth method:%d", session.auth.AuthMethod)
return "", err
}
// TODO(chef): [refactor] 错误放入base/error.go中 202205
err := fmt.Errorf("rtsp auth failed, auth:%s", authorization)
return "", err
} else {
if session.auth.AuthMethod == 0 {
resp := PackResponseBasicAuthorized(requestCtx.Headers.Get(HeaderCSeq), AuthTypeBasic)
if session.authConf.AuthMethod == 0 {
// Basic鉴权
authenticate := session.auth.MakeAuthenticate(AuthTypeBasic)
resp := PackResponseAuthorized(requestCtx.Headers.Get(HeaderCSeq), authenticate)
return resp, nil
} else if session.authConf.AuthMethod == 1 {
// Digest鉴权
authenticate := session.auth.MakeAuthenticate(AuthTypeDigest)
resp := PackResponseAuthorized(requestCtx.Headers.Get(HeaderCSeq), authenticate)
return resp, nil
} else {
err := fmt.Errorf("unsupport, auth method:%d", session.auth.AuthMethod)
// TODO(chef): [refactor] 错误放入base/error.go中 202205
err := fmt.Errorf("unsupport, auth method:%d", session.authConf.AuthMethod)
return "", err
}
}
return "", nil
}
// 一次SETUP对应一路流音频或视频

Loading…
Cancel
Save