security.go 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747
  1. package ims
  2. import (
  3. "bufio"
  4. "context"
  5. "crypto/rand"
  6. "encoding/binary"
  7. "encoding/hex"
  8. "errors"
  9. "fmt"
  10. "net"
  11. "sort"
  12. "strconv"
  13. "strings"
  14. )
  15. type SecurityMode string
  16. const (
  17. // SecurityRequired is the production default. A 401 response without a
  18. // supported ipsec-3gpp Security-Server offer fails closed.
  19. SecurityRequired SecurityMode = "required"
  20. // SecurityOptional advertises ipsec-3gpp but permits a carrier that
  21. // explicitly omits Security-Server to continue on the tunnel in plain SIP.
  22. SecurityOptional SecurityMode = "optional"
  23. // SecurityDisabled is intended for controlled interoperability testing.
  24. SecurityDisabled SecurityMode = "disabled"
  25. )
  26. var (
  27. ErrIPSecAgreementRequired = errors.New("ims: a supported ipsec-3gpp security agreement is required")
  28. ErrIPSecInstall = errors.New("ims: install ipsec-3gpp security associations")
  29. )
  30. // IPSecSAConfig is the complete, evidence-derived 3GPP transport-mode SA set.
  31. // The two UE SPIs identify inbound SAs; the two P-CSCF SPIs identify outbound
  32. // SAs. EncryptionKey and IntegrityKey must be discarded after Install returns.
  33. type IPSecSAConfig struct {
  34. LocalIP net.IP
  35. RemoteIP net.IP
  36. UEClientSPI uint32
  37. UEServerSPI uint32
  38. PCSCFClientSPI uint32
  39. PCSCFServerSPI uint32
  40. UEClientPort int
  41. UEServerPort int
  42. PCSCFClientPort int
  43. PCSCFServerPort int
  44. EncryptionKey []byte
  45. IntegrityKey []byte
  46. }
  47. type IPSecSAHandle interface {
  48. Close(context.Context) error
  49. }
  50. type IPSecSAInstaller interface {
  51. Install(context.Context, IPSecSAConfig) (IPSecSAHandle, error)
  52. }
  53. type securityProposal struct {
  54. spiClient uint32
  55. spiServer uint32
  56. portClient int
  57. portServer int
  58. }
  59. func newSecurityProposal(localIP net.IP, configuredClientPort int, configuredServerPort int) (securityProposal, error) {
  60. spiClient, err := randomSPI(0)
  61. if err != nil {
  62. return securityProposal{}, err
  63. }
  64. spiServer, err := randomSPI(spiClient)
  65. if err != nil {
  66. return securityProposal{}, err
  67. }
  68. portClient := configuredClientPort
  69. if portClient == 0 {
  70. portClient, err = availableProtectedPort(localIP, 0)
  71. if err != nil {
  72. return securityProposal{}, err
  73. }
  74. }
  75. portServer := configuredServerPort
  76. if portServer == 0 {
  77. portServer, err = availableProtectedPort(localIP, portClient)
  78. if err != nil {
  79. return securityProposal{}, err
  80. }
  81. }
  82. if !validProtectedPort(portClient) || !validProtectedPort(portServer) || portClient == portServer {
  83. return securityProposal{}, errors.New("ims: protected UE ports must be distinct non-standard SIP ports")
  84. }
  85. return securityProposal{
  86. spiClient: spiClient,
  87. spiServer: spiServer,
  88. portClient: portClient,
  89. portServer: portServer,
  90. }, nil
  91. }
  92. func (proposal securityProposal) headerValue() string {
  93. return fmt.Sprintf(
  94. "ipsec-3gpp;q=1.000;alg=hmac-sha-1-96;prot=esp;mod=trans;ealg=aes-cbc;spi-c=%010d;spi-s=%010d;port-c=%d;port-s=%d",
  95. proposal.spiClient,
  96. proposal.spiServer,
  97. proposal.portClient,
  98. proposal.portServer,
  99. )
  100. }
  101. func randomSPI(exclude uint32) (uint32, error) {
  102. for attempts := 0; attempts < 16; attempts++ {
  103. var value [4]byte
  104. if _, err := rand.Read(value[:]); err != nil {
  105. return 0, fmt.Errorf("ims: create protected SPI: %w", err)
  106. }
  107. spi := binary.BigEndian.Uint32(value[:])
  108. if spi >= 256 && spi != exclude {
  109. return spi, nil
  110. }
  111. }
  112. return 0, errors.New("ims: could not allocate a protected SPI")
  113. }
  114. func availableProtectedPort(localIP net.IP, exclude int) (int, error) {
  115. for attempts := 0; attempts < 32; attempts++ {
  116. var value [2]byte
  117. if _, err := rand.Read(value[:]); err != nil {
  118. return 0, fmt.Errorf("ims: create protected port: %w", err)
  119. }
  120. port := 20000 + int(binary.BigEndian.Uint16(value[:]))%44000
  121. if port == exclude || !validProtectedPort(port) {
  122. continue
  123. }
  124. address := &net.TCPAddr{IP: append(net.IP(nil), localIP...), Port: port}
  125. listener, err := net.ListenTCP("tcp", address)
  126. if err != nil {
  127. continue
  128. }
  129. _ = listener.Close()
  130. packet, err := net.ListenUDP("udp", &net.UDPAddr{IP: append(net.IP(nil), localIP...), Port: port})
  131. if err != nil {
  132. continue
  133. }
  134. _ = packet.Close()
  135. return port, nil
  136. }
  137. return 0, errors.New("ims: no protected local port is available")
  138. }
  139. func validProtectedPort(port int) bool {
  140. return port > 1024 && port <= 65535 && port != 5060 && port != 5061
  141. }
  142. type securityMechanism struct {
  143. raw string
  144. name string
  145. algorithm string
  146. protocol string
  147. mode string
  148. encryption string
  149. spiClient uint32
  150. spiServer uint32
  151. portClient int
  152. portServer int
  153. preference int
  154. }
  155. type securityAgreement struct {
  156. selected securityMechanism
  157. verifyValue string
  158. }
  159. func parseSecurityAgreement(values []string, proposal securityProposal) (securityAgreement, error) {
  160. items := splitHeaderValues(values)
  161. if len(items) == 0 {
  162. return securityAgreement{}, ErrIPSecAgreementRequired
  163. }
  164. candidates := make([]securityMechanism, 0, len(items))
  165. for _, item := range items {
  166. mechanism, err := parseSecurityMechanism(item)
  167. if err != nil {
  168. name := strings.ToLower(strings.TrimSpace(strings.SplitN(item, ";", 2)[0]))
  169. if name == "ipsec-3gpp" {
  170. return securityAgreement{}, fmt.Errorf(
  171. "ims: malformed ipsec-3gpp Security-Server: %w",
  172. err,
  173. )
  174. }
  175. continue
  176. }
  177. if !strings.EqualFold(mechanism.name, "ipsec-3gpp") ||
  178. !strings.EqualFold(mechanism.algorithm, "hmac-sha-1-96") ||
  179. !strings.EqualFold(mechanism.protocol, "esp") ||
  180. !strings.EqualFold(mechanism.mode, "trans") ||
  181. !strings.EqualFold(mechanism.encryption, "aes-cbc") {
  182. continue
  183. }
  184. if mechanism.spiClient == 0 || mechanism.spiServer == 0 ||
  185. mechanism.spiClient == mechanism.spiServer ||
  186. mechanism.spiClient == proposal.spiClient ||
  187. mechanism.spiClient == proposal.spiServer ||
  188. mechanism.spiServer == proposal.spiClient ||
  189. mechanism.spiServer == proposal.spiServer ||
  190. !validProtectedPort(mechanism.portClient) ||
  191. !validProtectedPort(mechanism.portServer) ||
  192. mechanism.portClient == mechanism.portServer {
  193. continue
  194. }
  195. candidates = append(candidates, mechanism)
  196. }
  197. if len(candidates) == 0 {
  198. return securityAgreement{}, ErrIPSecAgreementRequired
  199. }
  200. sort.SliceStable(candidates, func(left int, right int) bool {
  201. return candidates[left].preference > candidates[right].preference
  202. })
  203. return securityAgreement{
  204. selected: candidates[0],
  205. verifyValue: strings.Join(items, ", "),
  206. }, nil
  207. }
  208. func parseSecurityMechanism(value string) (securityMechanism, error) {
  209. parts := strings.Split(value, ";")
  210. if len(parts) == 0 {
  211. return securityMechanism{}, errors.New("ims: empty Security-Server mechanism")
  212. }
  213. mechanism := securityMechanism{
  214. raw: strings.TrimSpace(value),
  215. name: strings.ToLower(strings.TrimSpace(parts[0])),
  216. protocol: "esp",
  217. mode: "trans",
  218. encryption: "null",
  219. }
  220. parameters := make(map[string]string)
  221. for _, raw := range parts[1:] {
  222. key, parameterValue, found := strings.Cut(strings.TrimSpace(raw), "=")
  223. if !found {
  224. return securityMechanism{}, errors.New("ims: malformed Security-Server parameter")
  225. }
  226. key = strings.ToLower(strings.TrimSpace(key))
  227. parameterValue = strings.Trim(strings.TrimSpace(parameterValue), `"`)
  228. if key == "" || parameterValue == "" {
  229. return securityMechanism{}, errors.New("ims: empty Security-Server parameter")
  230. }
  231. if _, duplicate := parameters[key]; duplicate {
  232. return securityMechanism{}, errors.New("ims: duplicate Security-Server parameter")
  233. }
  234. parameters[key] = parameterValue
  235. }
  236. mechanism.algorithm = strings.ToLower(parameters["alg"])
  237. if value := parameters["prot"]; value != "" {
  238. mechanism.protocol = strings.ToLower(value)
  239. }
  240. if value := parameters["mod"]; value != "" {
  241. mechanism.mode = strings.ToLower(value)
  242. }
  243. if value := parameters["ealg"]; value != "" {
  244. mechanism.encryption = strings.ToLower(value)
  245. }
  246. var err error
  247. if mechanism.spiClient, err = decimalUint32(parameters["spi-c"]); err != nil {
  248. return securityMechanism{}, err
  249. }
  250. if mechanism.spiServer, err = decimalUint32(parameters["spi-s"]); err != nil {
  251. return securityMechanism{}, err
  252. }
  253. if mechanism.portClient, err = decimalPort(parameters["port-c"]); err != nil {
  254. return securityMechanism{}, err
  255. }
  256. if mechanism.portServer, err = decimalPort(parameters["port-s"]); err != nil {
  257. return securityMechanism{}, err
  258. }
  259. mechanism.preference, err = preferenceValue(parameters["q"])
  260. if err != nil {
  261. return securityMechanism{}, err
  262. }
  263. return mechanism, nil
  264. }
  265. func decimalUint32(value string) (uint32, error) {
  266. if value == "" || len(value) > 10 {
  267. return 0, errors.New("ims: invalid Security-Server SPI")
  268. }
  269. parsed, err := strconv.ParseUint(value, 10, 32)
  270. if err != nil {
  271. return 0, errors.New("ims: invalid Security-Server SPI")
  272. }
  273. return uint32(parsed), nil
  274. }
  275. func decimalPort(value string) (int, error) {
  276. parsed, err := strconv.Atoi(value)
  277. if err != nil || parsed < 1 || parsed > 65535 {
  278. return 0, errors.New("ims: invalid Security-Server port")
  279. }
  280. return parsed, nil
  281. }
  282. func preferenceValue(value string) (int, error) {
  283. if value == "" {
  284. return 0, nil
  285. }
  286. whole, fraction, found := strings.Cut(value, ".")
  287. if whole != "0" && whole != "1" {
  288. return 0, errors.New("ims: invalid Security-Server preference")
  289. }
  290. if !found {
  291. if whole == "1" {
  292. return 1000, nil
  293. }
  294. return 0, nil
  295. }
  296. if len(fraction) > 3 {
  297. return 0, errors.New("ims: invalid Security-Server preference")
  298. }
  299. for len(fraction) < 3 {
  300. fraction += "0"
  301. }
  302. numeric, err := strconv.Atoi(fraction)
  303. if err != nil || (whole == "1" && numeric != 0) {
  304. return 0, errors.New("ims: invalid Security-Server preference")
  305. }
  306. if whole == "1" {
  307. return 1000, nil
  308. }
  309. return numeric, nil
  310. }
  311. func expandIPSecKeys(ck []byte, ik []byte) (encryption []byte, integrity []byte, err error) {
  312. if len(ck) != 16 || len(ik) != 16 {
  313. return nil, nil, errors.New("ims: AKA did not return 16-byte CK and IK")
  314. }
  315. encryption = append([]byte(nil), ck...)
  316. integrity = make([]byte, 20)
  317. copy(integrity, ik)
  318. return encryption, integrity, nil
  319. }
  320. type xfrmOperation struct {
  321. description string
  322. arguments []string
  323. }
  324. func buildXFRMInstallPlan(config IPSecSAConfig) ([]xfrmOperation, error) {
  325. if err := validateIPSecSAConfig(config); err != nil {
  326. return nil, err
  327. }
  328. var operations []xfrmOperation
  329. states := []struct {
  330. description string
  331. source net.IP
  332. destination net.IP
  333. spi uint32
  334. reqid uint32
  335. }{
  336. {"outbound UE-client to P-CSCF-server state", config.LocalIP, config.RemoteIP, config.PCSCFServerSPI, clientPairReqID(config)},
  337. {"inbound P-CSCF-server to UE-client state", config.RemoteIP, config.LocalIP, config.UEClientSPI, clientPairReqID(config)},
  338. {"inbound P-CSCF-client to UE-server state", config.RemoteIP, config.LocalIP, config.UEServerSPI, serverPairReqID(config)},
  339. {"outbound UE-server to P-CSCF-client state", config.LocalIP, config.RemoteIP, config.PCSCFClientSPI, serverPairReqID(config)},
  340. }
  341. for _, state := range states {
  342. operations = append(operations, xfrmOperation{
  343. description: state.description,
  344. arguments: []string{
  345. "xfrm", "state", "add",
  346. "src", state.source.String(),
  347. "dst", state.destination.String(),
  348. "proto", "esp",
  349. "spi", fmt.Sprintf("0x%08x", state.spi),
  350. "reqid", strconv.FormatUint(uint64(state.reqid), 10),
  351. "mode", "transport",
  352. "replay-window", "32",
  353. "auth-trunc", "hmac(sha1)", "0x" + hex.EncodeToString(config.IntegrityKey), "96",
  354. "enc", "cbc(aes)", "0x" + hex.EncodeToString(config.EncryptionKey),
  355. },
  356. })
  357. }
  358. for _, flow := range xfrmFlows(config) {
  359. for _, protocol := range flow.protocols {
  360. operations = append(operations, xfrmOperation{
  361. description: flow.description + " " + protocol + " policy",
  362. arguments: []string{
  363. flow.family,
  364. "xfrm", "policy", "add",
  365. "src", flow.sourcePrefix,
  366. "dst", flow.destinationPrefix,
  367. "proto", protocol,
  368. "sport", strconv.Itoa(flow.sourcePort),
  369. "dport", strconv.Itoa(flow.destinationPort),
  370. "dir", flow.direction,
  371. "priority", "100",
  372. "tmpl",
  373. "src", flow.templateSource.String(),
  374. "dst", flow.templateDestination.String(),
  375. "proto", "esp",
  376. "spi", fmt.Sprintf("0x%08x", flow.spi),
  377. "reqid", strconv.FormatUint(uint64(flow.reqid), 10),
  378. "mode", "transport",
  379. "level", "required",
  380. },
  381. })
  382. }
  383. }
  384. return operations, nil
  385. }
  386. func buildXFRMCleanupPlan(config IPSecSAConfig) []xfrmOperation {
  387. var operations []xfrmOperation
  388. flows := xfrmFlows(config)
  389. for flowIndex := len(flows) - 1; flowIndex >= 0; flowIndex-- {
  390. flow := flows[flowIndex]
  391. for protocolIndex := len(flow.protocols) - 1; protocolIndex >= 0; protocolIndex-- {
  392. protocol := flow.protocols[protocolIndex]
  393. operations = append(operations, xfrmOperation{
  394. description: "delete " + flow.description + " " + protocol + " policy",
  395. arguments: []string{
  396. flow.family,
  397. "xfrm", "policy", "delete",
  398. "src", flow.sourcePrefix,
  399. "dst", flow.destinationPrefix,
  400. "proto", protocol,
  401. "sport", strconv.Itoa(flow.sourcePort),
  402. "dport", strconv.Itoa(flow.destinationPort),
  403. "dir", flow.direction,
  404. },
  405. })
  406. }
  407. }
  408. states := []struct {
  409. source net.IP
  410. destination net.IP
  411. spi uint32
  412. }{
  413. {config.LocalIP, config.RemoteIP, config.PCSCFClientSPI},
  414. {config.RemoteIP, config.LocalIP, config.UEServerSPI},
  415. {config.RemoteIP, config.LocalIP, config.UEClientSPI},
  416. {config.LocalIP, config.RemoteIP, config.PCSCFServerSPI},
  417. }
  418. for _, state := range states {
  419. operations = append(operations, xfrmOperation{
  420. description: "delete ipsec-3gpp state",
  421. arguments: []string{
  422. "xfrm", "state", "delete",
  423. "src", state.source.String(),
  424. "dst", state.destination.String(),
  425. "proto", "esp",
  426. "spi", fmt.Sprintf("0x%08x", state.spi),
  427. },
  428. })
  429. }
  430. return operations
  431. }
  432. type xfrmFlow struct {
  433. description string
  434. family string
  435. sourcePrefix string
  436. destinationPrefix string
  437. sourcePort int
  438. destinationPort int
  439. direction string
  440. templateSource net.IP
  441. templateDestination net.IP
  442. spi uint32
  443. reqid uint32
  444. protocols []string
  445. }
  446. func xfrmFlows(config IPSecSAConfig) []xfrmFlow {
  447. family := "-4"
  448. prefix := "/32"
  449. if config.LocalIP.To4() == nil {
  450. family = "-6"
  451. prefix = "/128"
  452. }
  453. localPrefix := config.LocalIP.String() + prefix
  454. remotePrefix := config.RemoteIP.String() + prefix
  455. return []xfrmFlow{
  456. {
  457. description: "UE-client to P-CSCF-server", family: family,
  458. sourcePrefix: localPrefix, destinationPrefix: remotePrefix,
  459. sourcePort: config.UEClientPort, destinationPort: config.PCSCFServerPort,
  460. direction: "out", templateSource: config.LocalIP, templateDestination: config.RemoteIP,
  461. spi: config.PCSCFServerSPI, reqid: clientPairReqID(config),
  462. protocols: []string{"tcp", "udp"},
  463. },
  464. {
  465. description: "P-CSCF-server to UE-client", family: family,
  466. sourcePrefix: remotePrefix, destinationPrefix: localPrefix,
  467. sourcePort: config.PCSCFServerPort, destinationPort: config.UEClientPort,
  468. direction: "in", templateSource: config.RemoteIP, templateDestination: config.LocalIP,
  469. spi: config.UEClientSPI, reqid: clientPairReqID(config),
  470. protocols: []string{"tcp"},
  471. },
  472. {
  473. description: "P-CSCF-client to UE-server", family: family,
  474. sourcePrefix: remotePrefix, destinationPrefix: localPrefix,
  475. sourcePort: config.PCSCFClientPort, destinationPort: config.UEServerPort,
  476. direction: "in", templateSource: config.RemoteIP, templateDestination: config.LocalIP,
  477. spi: config.UEServerSPI, reqid: serverPairReqID(config),
  478. protocols: []string{"tcp", "udp"},
  479. },
  480. {
  481. description: "UE-server to P-CSCF-client", family: family,
  482. sourcePrefix: localPrefix, destinationPrefix: remotePrefix,
  483. sourcePort: config.UEServerPort, destinationPort: config.PCSCFClientPort,
  484. direction: "out", templateSource: config.LocalIP, templateDestination: config.RemoteIP,
  485. spi: config.PCSCFClientSPI, reqid: serverPairReqID(config),
  486. protocols: []string{"tcp"},
  487. },
  488. }
  489. }
  490. func clientPairReqID(config IPSecSAConfig) uint32 {
  491. reqid := (config.UEClientSPI ^ config.PCSCFServerSPI) & 0x7fffffff
  492. if reqid == 0 {
  493. return 1
  494. }
  495. return reqid
  496. }
  497. func serverPairReqID(config IPSecSAConfig) uint32 {
  498. reqid := (config.UEServerSPI ^ config.PCSCFClientSPI) & 0x7fffffff
  499. if reqid == 0 {
  500. reqid = 2
  501. }
  502. if reqid == clientPairReqID(config) {
  503. reqid ^= 0x40000000
  504. if reqid == 0 {
  505. reqid = 2
  506. }
  507. }
  508. return reqid
  509. }
  510. func validateIPSecSAConfig(config IPSecSAConfig) error {
  511. local := config.LocalIP
  512. remote := config.RemoteIP
  513. if local == nil || remote == nil || local.IsUnspecified() || remote.IsUnspecified() ||
  514. (local.To4() == nil) != (remote.To4() == nil) {
  515. return errors.New("ims: ipsec-3gpp endpoints are invalid or use different IP families")
  516. }
  517. spis := []uint32{
  518. config.UEClientSPI, config.UEServerSPI, config.PCSCFClientSPI, config.PCSCFServerSPI,
  519. }
  520. seen := make(map[uint32]struct{}, len(spis))
  521. for _, spi := range spis {
  522. if spi == 0 {
  523. return errors.New("ims: ipsec-3gpp SPI is zero")
  524. }
  525. if _, duplicate := seen[spi]; duplicate {
  526. return errors.New("ims: ipsec-3gpp SPIs must be unique")
  527. }
  528. seen[spi] = struct{}{}
  529. }
  530. ports := []int{
  531. config.UEClientPort, config.UEServerPort, config.PCSCFClientPort, config.PCSCFServerPort,
  532. }
  533. for _, port := range ports {
  534. if !validProtectedPort(port) {
  535. return errors.New("ims: ipsec-3gpp protected port is invalid")
  536. }
  537. }
  538. if config.UEClientPort == config.UEServerPort ||
  539. config.PCSCFClientPort == config.PCSCFServerPort {
  540. return errors.New("ims: client and server protected ports must differ")
  541. }
  542. if len(config.EncryptionKey) != 16 || len(config.IntegrityKey) != 20 {
  543. return errors.New("ims: ipsec-3gpp key length is invalid")
  544. }
  545. return nil
  546. }
  547. func cloneIPSecSAConfig(config IPSecSAConfig) IPSecSAConfig {
  548. config.LocalIP = append(net.IP(nil), config.LocalIP...)
  549. config.RemoteIP = append(net.IP(nil), config.RemoteIP...)
  550. config.EncryptionKey = append([]byte(nil), config.EncryptionKey...)
  551. config.IntegrityKey = append([]byte(nil), config.IntegrityKey...)
  552. return config
  553. }
  554. func zeroBytes(value []byte) {
  555. for index := range value {
  556. value[index] = 0
  557. }
  558. }
  559. func (session *Session) securityOffered() bool {
  560. return session.provider.config.SecurityMode != SecurityDisabled && !session.securityDeclined
  561. }
  562. func (session *Session) securityFromResponse(response *sipResponse) (securityAgreement, bool, error) {
  563. if !session.securityOffered() {
  564. return securityAgreement{}, false, nil
  565. }
  566. values := response.values("Security-Server")
  567. if len(splitHeaderValues(values)) == 0 {
  568. if session.provider.config.SecurityMode == SecurityRequired {
  569. return securityAgreement{}, false, ErrIPSecAgreementRequired
  570. }
  571. session.declineSecurity()
  572. return securityAgreement{}, false, nil
  573. }
  574. agreement, err := parseSecurityAgreement(values, session.securityProposal)
  575. if err != nil {
  576. return securityAgreement{}, false, err
  577. }
  578. return agreement, true, nil
  579. }
  580. func (session *Session) declineSecurity() {
  581. session.securityDeclined = true
  582. session.endpoint = session.initialEndpoint
  583. if session.protectedTCP != nil {
  584. _ = session.protectedTCP.Close()
  585. session.protectedTCP = nil
  586. }
  587. if session.protectedUDP != nil {
  588. _ = session.protectedUDP.Close()
  589. session.protectedUDP = nil
  590. }
  591. session.securityProposal = securityProposal{}
  592. }
  593. func (session *Session) activateIPSec(
  594. ctx context.Context,
  595. agreement securityAgreement,
  596. ck []byte,
  597. ik []byte,
  598. ) error {
  599. if session.securityActive {
  600. return errors.New("ims: ipsec-3gpp is already active")
  601. }
  602. if !session.securityOffered() {
  603. return ErrIPSecAgreementRequired
  604. }
  605. encryptionKey, integrityKey, err := expandIPSecKeys(ck, ik)
  606. if err != nil {
  607. return err
  608. }
  609. defer zeroBytes(encryptionKey)
  610. defer zeroBytes(integrityKey)
  611. localIP := addressIP(session.conn.LocalAddr())
  612. remoteIP := addressIP(session.conn.RemoteAddr())
  613. if localIP == nil || remoteIP == nil {
  614. return errors.New("ims: protected SIP endpoints are unavailable")
  615. }
  616. selected := agreement.selected
  617. config := IPSecSAConfig{
  618. LocalIP: localIP,
  619. RemoteIP: remoteIP,
  620. UEClientSPI: session.securityProposal.spiClient,
  621. UEServerSPI: session.securityProposal.spiServer,
  622. PCSCFClientSPI: selected.spiClient,
  623. PCSCFServerSPI: selected.spiServer,
  624. UEClientPort: session.securityProposal.portClient,
  625. UEServerPort: session.securityProposal.portServer,
  626. PCSCFClientPort: selected.portClient,
  627. PCSCFServerPort: selected.portServer,
  628. EncryptionKey: encryptionKey,
  629. IntegrityKey: integrityKey,
  630. }
  631. handle, err := session.provider.installer.Install(ctx, config)
  632. if err != nil {
  633. return fmt.Errorf("%w: %v", ErrIPSecInstall, err)
  634. }
  635. if handle == nil {
  636. return fmt.Errorf("%w: installer returned no handle", ErrIPSecInstall)
  637. }
  638. remoteAddress := net.JoinHostPort(remoteIP.String(), strconv.Itoa(selected.portServer))
  639. _ = session.conn.Close()
  640. connection, dialErr := dialSIP(
  641. ctx,
  642. session.transport,
  643. localIP.String(),
  644. session.securityProposal.portClient,
  645. remoteAddress,
  646. )
  647. if dialErr != nil {
  648. cleanupErr := handle.Close(context.Background())
  649. if cleanupErr != nil {
  650. return errors.Join(
  651. fmt.Errorf("ims: connect protected P-CSCF: %w", dialErr),
  652. fmt.Errorf("ims: roll back ipsec-3gpp: %w", cleanupErr),
  653. )
  654. }
  655. return fmt.Errorf("ims: connect protected P-CSCF: %w", dialErr)
  656. }
  657. session.conn = connection
  658. if session.transport == "tcp" {
  659. session.reader = bufio.NewReader(connection)
  660. } else {
  661. session.reader = nil
  662. }
  663. session.endpoint.port = selected.portServer
  664. session.securityAgreement = agreement
  665. session.securityActive = true
  666. session.ipsecHandle = handle
  667. return nil
  668. }
  669. func (session *Session) contactAddress() string {
  670. if session.securityOffered() {
  671. host := addressHost(session.conn.LocalAddr())
  672. return net.JoinHostPort(host, strconv.Itoa(session.securityProposal.portServer))
  673. }
  674. return session.conn.LocalAddr().String()
  675. }
  676. func (session *Session) emptyDigestAuthorization() string {
  677. uri := "sip:" + session.identity.domain
  678. return "Digest " + strings.Join([]string{
  679. `username="` + quoteDigest(session.identity.private) + `"`,
  680. `realm="` + quoteDigest(session.identity.domain) + `"`,
  681. `nonce=""`,
  682. `uri="` + quoteDigest(uri) + `"`,
  683. `response=""`,
  684. "algorithm=AKAv1-MD5",
  685. "integrity-protected=no",
  686. }, ", ")
  687. }
  688. func (session *Session) validProtectedUDPSource(remote *net.UDPAddr) bool {
  689. if remote == nil || !session.securityActive {
  690. return false
  691. }
  692. expectedIP := addressIP(session.conn.RemoteAddr())
  693. return expectedIP != nil &&
  694. expectedIP.Equal(remote.IP) &&
  695. remote.Port == session.securityAgreement.selected.portClient
  696. }
  697. func (session *Session) effectiveSecurityMode() string {
  698. if session.securityActive {
  699. return "ipsec-3gpp"
  700. }
  701. return "none"
  702. }