mirror of
https://github.com/bluenviron/mediamtx
synced 2024-12-14 10:45:08 +00:00
397c58a882
* Add argon2 credential hash support * update README, tests and documentation --------- Co-authored-by: aler9 <46489434+aler9@users.noreply.github.com>
127 lines
3.2 KiB
Go
127 lines
3.2 KiB
Go
package core
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
|
|
"github.com/bluenviron/gortsplib/v4/pkg/auth"
|
|
"github.com/bluenviron/gortsplib/v4/pkg/headers"
|
|
"github.com/google/uuid"
|
|
|
|
"github.com/bluenviron/mediamtx/internal/conf"
|
|
"github.com/bluenviron/mediamtx/internal/defs"
|
|
)
|
|
|
|
func doExternalAuthentication(
|
|
ur string,
|
|
accessRequest defs.PathAccessRequest,
|
|
) error {
|
|
enc, _ := json.Marshal(struct {
|
|
IP string `json:"ip"`
|
|
User string `json:"user"`
|
|
Password string `json:"password"`
|
|
Path string `json:"path"`
|
|
Protocol string `json:"protocol"`
|
|
ID *uuid.UUID `json:"id"`
|
|
Action string `json:"action"`
|
|
Query string `json:"query"`
|
|
}{
|
|
IP: accessRequest.IP.String(),
|
|
User: accessRequest.User,
|
|
Password: accessRequest.Pass,
|
|
Path: accessRequest.Name,
|
|
Protocol: string(accessRequest.Proto),
|
|
ID: accessRequest.ID,
|
|
Action: func() string {
|
|
if accessRequest.Publish {
|
|
return "publish"
|
|
}
|
|
return "read"
|
|
}(),
|
|
Query: accessRequest.Query,
|
|
})
|
|
res, err := http.Post(ur, "application/json", bytes.NewReader(enc))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer res.Body.Close()
|
|
|
|
if res.StatusCode < 200 || res.StatusCode > 299 {
|
|
if resBody, err := io.ReadAll(res.Body); err == nil && len(resBody) != 0 {
|
|
return fmt.Errorf("server replied with code %d: %s", res.StatusCode, string(resBody))
|
|
}
|
|
return fmt.Errorf("server replied with code %d", res.StatusCode)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func doAuthentication(
|
|
externalAuthenticationURL string,
|
|
rtspAuthMethods conf.AuthMethods,
|
|
pathConf *conf.Path,
|
|
accessRequest defs.PathAccessRequest,
|
|
) error {
|
|
var rtspAuth headers.Authorization
|
|
if accessRequest.RTSPRequest != nil {
|
|
err := rtspAuth.Unmarshal(accessRequest.RTSPRequest.Header["Authorization"])
|
|
if err == nil && rtspAuth.Method == headers.AuthBasic {
|
|
accessRequest.User = rtspAuth.BasicUser
|
|
accessRequest.Pass = rtspAuth.BasicPass
|
|
}
|
|
}
|
|
|
|
if externalAuthenticationURL != "" {
|
|
err := doExternalAuthentication(
|
|
externalAuthenticationURL,
|
|
accessRequest,
|
|
)
|
|
if err != nil {
|
|
return defs.AuthenticationError{Message: fmt.Sprintf("external authentication failed: %s", err)}
|
|
}
|
|
}
|
|
|
|
var pathIPs conf.IPsOrCIDRs
|
|
var pathUser conf.Credential
|
|
var pathPass conf.Credential
|
|
|
|
if accessRequest.Publish {
|
|
pathIPs = pathConf.PublishIPs
|
|
pathUser = pathConf.PublishUser
|
|
pathPass = pathConf.PublishPass
|
|
} else {
|
|
pathIPs = pathConf.ReadIPs
|
|
pathUser = pathConf.ReadUser
|
|
pathPass = pathConf.ReadPass
|
|
}
|
|
|
|
if pathIPs != nil {
|
|
if !ipEqualOrInRange(accessRequest.IP, pathIPs) {
|
|
return defs.AuthenticationError{Message: fmt.Sprintf("IP %s not allowed", accessRequest.IP)}
|
|
}
|
|
}
|
|
|
|
if !pathUser.IsEmpty() {
|
|
if accessRequest.RTSPRequest != nil && rtspAuth.Method == headers.AuthDigest {
|
|
err := auth.Validate(
|
|
accessRequest.RTSPRequest,
|
|
pathUser.GetValue(),
|
|
pathPass.GetValue(),
|
|
accessRequest.RTSPBaseURL,
|
|
rtspAuthMethods,
|
|
"IPCAM",
|
|
accessRequest.RTSPNonce)
|
|
if err != nil {
|
|
return defs.AuthenticationError{Message: err.Error()}
|
|
}
|
|
} else if !pathUser.Check(accessRequest.User) || !pathPass.Check(accessRequest.Pass) {
|
|
return defs.AuthenticationError{Message: "invalid credentials"}
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|