auth.go 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279
  1. package sip
  2. import (
  3. "crypto/md5"
  4. "encoding/hex"
  5. "fmt"
  6. "regexp"
  7. "strings"
  8. )
  9. // currently only Digest and MD5
  10. type Authorization struct {
  11. realm string
  12. nonce string
  13. algorithm string
  14. username string
  15. password string
  16. uri string
  17. response string
  18. method string
  19. qop string
  20. nc string
  21. cnonce string
  22. other map[string]string
  23. }
  24. func AuthFromValue(value string) *Authorization {
  25. auth := &Authorization{
  26. algorithm: "MD5",
  27. other: make(map[string]string),
  28. }
  29. re := regexp.MustCompile(`([\w]+)="([^"]+)"`)
  30. matches := re.FindAllStringSubmatch(value, -1)
  31. for _, match := range matches {
  32. switch match[1] {
  33. case "realm":
  34. auth.realm = match[2]
  35. case "algorithm":
  36. auth.algorithm = match[2]
  37. case "nonce":
  38. auth.nonce = match[2]
  39. case "username":
  40. auth.username = match[2]
  41. case "uri":
  42. auth.uri = match[2]
  43. case "response":
  44. auth.response = match[2]
  45. case "qop":
  46. for _, v := range strings.Split(match[2], ",") {
  47. v = strings.Trim(v, " ")
  48. if v == "auth" || v == "auth-int" {
  49. auth.qop = "auth"
  50. break
  51. }
  52. }
  53. case "nc":
  54. auth.nc = match[2]
  55. case "cnonce":
  56. auth.cnonce = match[2]
  57. default:
  58. auth.other[match[1]] = match[2]
  59. }
  60. }
  61. return auth
  62. }
  63. func (auth *Authorization) Realm() string {
  64. return auth.realm
  65. }
  66. func (auth *Authorization) Nonce() string {
  67. return auth.nonce
  68. }
  69. func (auth *Authorization) Algorithm() string {
  70. return auth.algorithm
  71. }
  72. func (auth *Authorization) Username() string {
  73. return auth.username
  74. }
  75. func (auth *Authorization) SetUsername(username string) *Authorization {
  76. auth.username = username
  77. return auth
  78. }
  79. func (auth *Authorization) SetPassword(password string) *Authorization {
  80. auth.password = password
  81. return auth
  82. }
  83. func (auth *Authorization) Uri() string {
  84. return auth.uri
  85. }
  86. func (auth *Authorization) SetUri(uri string) *Authorization {
  87. auth.uri = uri
  88. return auth
  89. }
  90. func (auth *Authorization) SetMethod(method string) *Authorization {
  91. auth.method = method
  92. return auth
  93. }
  94. func (auth *Authorization) Response() string {
  95. return auth.response
  96. }
  97. func (auth *Authorization) SetResponse(response string) {
  98. auth.response = response
  99. }
  100. func (auth *Authorization) Qop() string {
  101. return auth.qop
  102. }
  103. func (auth *Authorization) SetQop(qop string) {
  104. auth.qop = qop
  105. }
  106. func (auth *Authorization) Nc() string {
  107. return auth.nc
  108. }
  109. func (auth *Authorization) SetNc(nc string) {
  110. auth.nc = nc
  111. }
  112. func (auth *Authorization) CNonce() string {
  113. return auth.cnonce
  114. }
  115. func (auth *Authorization) SetCNonce(cnonce string) {
  116. auth.cnonce = cnonce
  117. }
  118. func (auth *Authorization) CalcResponse() string {
  119. return calcResponse(
  120. auth.username,
  121. auth.realm,
  122. auth.password,
  123. auth.method,
  124. auth.uri,
  125. auth.nonce,
  126. auth.qop,
  127. auth.cnonce,
  128. auth.nc,
  129. )
  130. }
  131. func (auth *Authorization) String() string {
  132. if auth == nil {
  133. return "<nil>"
  134. }
  135. str := fmt.Sprintf(
  136. `Digest realm="%s",algorithm=%s,nonce="%s",username="%s",uri="%s",response="%s"`,
  137. auth.realm,
  138. auth.algorithm,
  139. auth.nonce,
  140. auth.username,
  141. auth.uri,
  142. auth.response,
  143. )
  144. if auth.qop == "auth" {
  145. str += fmt.Sprintf(`,qop=%s,nc=%s,cnonce="%s"`, auth.qop, auth.nc, auth.cnonce)
  146. }
  147. return str
  148. }
  149. // calculates Authorization response https://www.ietf.org/rfc/rfc2617.txt
  150. func calcResponse(username, realm, password, method, uri, nonce, qop, cnonce, nc string) string {
  151. calcA1 := func() string {
  152. encoder := md5.New()
  153. encoder.Write([]byte(username + ":" + realm + ":" + password))
  154. return hex.EncodeToString(encoder.Sum(nil))
  155. }
  156. calcA2 := func() string {
  157. encoder := md5.New()
  158. encoder.Write([]byte(method + ":" + uri))
  159. return hex.EncodeToString(encoder.Sum(nil))
  160. }
  161. encoder := md5.New()
  162. encoder.Write([]byte(calcA1() + ":" + nonce + ":"))
  163. if qop != "" {
  164. encoder.Write([]byte(nc + ":" + cnonce + ":" + qop + ":"))
  165. }
  166. encoder.Write([]byte(calcA2()))
  167. return hex.EncodeToString(encoder.Sum(nil))
  168. }
  169. func AuthorizeRequest(request Request, response Response, user, password MaybeString) error {
  170. if user == nil {
  171. return fmt.Errorf("authorize request: user is nil")
  172. }
  173. var authenticateHeaderName, authorizeHeaderName string
  174. if response.StatusCode() == 401 {
  175. // on 401 Unauthorized increase request seq num, add Authorization header and send once again
  176. authenticateHeaderName = "WWW-Authenticate"
  177. authorizeHeaderName = "Authorization"
  178. } else {
  179. // 407 Proxy authentication
  180. authenticateHeaderName = "Proxy-Authenticate"
  181. authorizeHeaderName = "Proxy-Authorization"
  182. }
  183. if hdrs := response.GetHeaders(authenticateHeaderName); len(hdrs) > 0 {
  184. authenticateHeader := hdrs[0].(*GenericHeader)
  185. auth := AuthFromValue(authenticateHeader.Contents).
  186. SetMethod(string(request.Method())).
  187. SetUri(request.Recipient().String()).
  188. SetUsername(user.String())
  189. if password != nil {
  190. auth.SetPassword(password.String())
  191. }
  192. if auth.Qop() == "auth" {
  193. auth.SetNc("00000001")
  194. encoder := md5.New()
  195. encoder.Write([]byte(user.String() + request.Recipient().String()))
  196. if password != nil {
  197. encoder.Write([]byte(password.String()))
  198. }
  199. auth.SetCNonce(hex.EncodeToString(encoder.Sum(nil)))
  200. }
  201. auth.SetResponse(auth.CalcResponse())
  202. if hdrs = request.GetHeaders(authorizeHeaderName); len(hdrs) > 0 {
  203. authorizationHeader := hdrs[0].Clone().(*GenericHeader)
  204. authorizationHeader.Contents = auth.String()
  205. request.ReplaceHeaders(authorizationHeader.Name(), []Header{authorizationHeader})
  206. } else {
  207. request.AppendHeader(&GenericHeader{
  208. HeaderName: authorizeHeaderName,
  209. Contents: auth.String(),
  210. })
  211. }
  212. } else {
  213. return fmt.Errorf("authorize request: header '%s' not found in response", authenticateHeaderName)
  214. }
  215. if viaHop, ok := request.ViaHop(); ok {
  216. viaHop.Params.Add("branch", String{Str: GenerateBranch()})
  217. }
  218. if cseq, ok := request.CSeq(); ok {
  219. cseq := cseq.Clone().(*CSeq)
  220. cseq.SeqNo++
  221. request.ReplaceHeaders(cseq.Name(), []Header{cseq})
  222. }
  223. return nil
  224. }
  225. type Authorizer interface {
  226. AuthorizeRequest(request Request, response Response) error
  227. }
  228. type DefaultAuthorizer struct {
  229. User MaybeString
  230. Password MaybeString
  231. }
  232. func (auth *DefaultAuthorizer) AuthorizeRequest(request Request, response Response) error {
  233. return AuthorizeRequest(request, response, auth.User, auth.Password)
  234. }