| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747 |
- package ims
- import (
- "bufio"
- "context"
- "crypto/rand"
- "encoding/binary"
- "encoding/hex"
- "errors"
- "fmt"
- "net"
- "sort"
- "strconv"
- "strings"
- )
- type SecurityMode string
- const (
- // SecurityRequired is the production default. A 401 response without a
- // supported ipsec-3gpp Security-Server offer fails closed.
- SecurityRequired SecurityMode = "required"
- // SecurityOptional advertises ipsec-3gpp but permits a carrier that
- // explicitly omits Security-Server to continue on the tunnel in plain SIP.
- SecurityOptional SecurityMode = "optional"
- // SecurityDisabled is intended for controlled interoperability testing.
- SecurityDisabled SecurityMode = "disabled"
- )
- var (
- ErrIPSecAgreementRequired = errors.New("ims: a supported ipsec-3gpp security agreement is required")
- ErrIPSecInstall = errors.New("ims: install ipsec-3gpp security associations")
- )
- // IPSecSAConfig is the complete, evidence-derived 3GPP transport-mode SA set.
- // The two UE SPIs identify inbound SAs; the two P-CSCF SPIs identify outbound
- // SAs. EncryptionKey and IntegrityKey must be discarded after Install returns.
- type IPSecSAConfig struct {
- LocalIP net.IP
- RemoteIP net.IP
- UEClientSPI uint32
- UEServerSPI uint32
- PCSCFClientSPI uint32
- PCSCFServerSPI uint32
- UEClientPort int
- UEServerPort int
- PCSCFClientPort int
- PCSCFServerPort int
- EncryptionKey []byte
- IntegrityKey []byte
- }
- type IPSecSAHandle interface {
- Close(context.Context) error
- }
- type IPSecSAInstaller interface {
- Install(context.Context, IPSecSAConfig) (IPSecSAHandle, error)
- }
- type securityProposal struct {
- spiClient uint32
- spiServer uint32
- portClient int
- portServer int
- }
- func newSecurityProposal(localIP net.IP, configuredClientPort int, configuredServerPort int) (securityProposal, error) {
- spiClient, err := randomSPI(0)
- if err != nil {
- return securityProposal{}, err
- }
- spiServer, err := randomSPI(spiClient)
- if err != nil {
- return securityProposal{}, err
- }
- portClient := configuredClientPort
- if portClient == 0 {
- portClient, err = availableProtectedPort(localIP, 0)
- if err != nil {
- return securityProposal{}, err
- }
- }
- portServer := configuredServerPort
- if portServer == 0 {
- portServer, err = availableProtectedPort(localIP, portClient)
- if err != nil {
- return securityProposal{}, err
- }
- }
- if !validProtectedPort(portClient) || !validProtectedPort(portServer) || portClient == portServer {
- return securityProposal{}, errors.New("ims: protected UE ports must be distinct non-standard SIP ports")
- }
- return securityProposal{
- spiClient: spiClient,
- spiServer: spiServer,
- portClient: portClient,
- portServer: portServer,
- }, nil
- }
- func (proposal securityProposal) headerValue() string {
- return fmt.Sprintf(
- "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",
- proposal.spiClient,
- proposal.spiServer,
- proposal.portClient,
- proposal.portServer,
- )
- }
- func randomSPI(exclude uint32) (uint32, error) {
- for attempts := 0; attempts < 16; attempts++ {
- var value [4]byte
- if _, err := rand.Read(value[:]); err != nil {
- return 0, fmt.Errorf("ims: create protected SPI: %w", err)
- }
- spi := binary.BigEndian.Uint32(value[:])
- if spi >= 256 && spi != exclude {
- return spi, nil
- }
- }
- return 0, errors.New("ims: could not allocate a protected SPI")
- }
- func availableProtectedPort(localIP net.IP, exclude int) (int, error) {
- for attempts := 0; attempts < 32; attempts++ {
- var value [2]byte
- if _, err := rand.Read(value[:]); err != nil {
- return 0, fmt.Errorf("ims: create protected port: %w", err)
- }
- port := 20000 + int(binary.BigEndian.Uint16(value[:]))%44000
- if port == exclude || !validProtectedPort(port) {
- continue
- }
- address := &net.TCPAddr{IP: append(net.IP(nil), localIP...), Port: port}
- listener, err := net.ListenTCP("tcp", address)
- if err != nil {
- continue
- }
- _ = listener.Close()
- packet, err := net.ListenUDP("udp", &net.UDPAddr{IP: append(net.IP(nil), localIP...), Port: port})
- if err != nil {
- continue
- }
- _ = packet.Close()
- return port, nil
- }
- return 0, errors.New("ims: no protected local port is available")
- }
- func validProtectedPort(port int) bool {
- return port > 1024 && port <= 65535 && port != 5060 && port != 5061
- }
- type securityMechanism struct {
- raw string
- name string
- algorithm string
- protocol string
- mode string
- encryption string
- spiClient uint32
- spiServer uint32
- portClient int
- portServer int
- preference int
- }
- type securityAgreement struct {
- selected securityMechanism
- verifyValue string
- }
- func parseSecurityAgreement(values []string, proposal securityProposal) (securityAgreement, error) {
- items := splitHeaderValues(values)
- if len(items) == 0 {
- return securityAgreement{}, ErrIPSecAgreementRequired
- }
- candidates := make([]securityMechanism, 0, len(items))
- for _, item := range items {
- mechanism, err := parseSecurityMechanism(item)
- if err != nil {
- name := strings.ToLower(strings.TrimSpace(strings.SplitN(item, ";", 2)[0]))
- if name == "ipsec-3gpp" {
- return securityAgreement{}, fmt.Errorf(
- "ims: malformed ipsec-3gpp Security-Server: %w",
- err,
- )
- }
- continue
- }
- if !strings.EqualFold(mechanism.name, "ipsec-3gpp") ||
- !strings.EqualFold(mechanism.algorithm, "hmac-sha-1-96") ||
- !strings.EqualFold(mechanism.protocol, "esp") ||
- !strings.EqualFold(mechanism.mode, "trans") ||
- !strings.EqualFold(mechanism.encryption, "aes-cbc") {
- continue
- }
- if mechanism.spiClient == 0 || mechanism.spiServer == 0 ||
- mechanism.spiClient == mechanism.spiServer ||
- mechanism.spiClient == proposal.spiClient ||
- mechanism.spiClient == proposal.spiServer ||
- mechanism.spiServer == proposal.spiClient ||
- mechanism.spiServer == proposal.spiServer ||
- !validProtectedPort(mechanism.portClient) ||
- !validProtectedPort(mechanism.portServer) ||
- mechanism.portClient == mechanism.portServer {
- continue
- }
- candidates = append(candidates, mechanism)
- }
- if len(candidates) == 0 {
- return securityAgreement{}, ErrIPSecAgreementRequired
- }
- sort.SliceStable(candidates, func(left int, right int) bool {
- return candidates[left].preference > candidates[right].preference
- })
- return securityAgreement{
- selected: candidates[0],
- verifyValue: strings.Join(items, ", "),
- }, nil
- }
- func parseSecurityMechanism(value string) (securityMechanism, error) {
- parts := strings.Split(value, ";")
- if len(parts) == 0 {
- return securityMechanism{}, errors.New("ims: empty Security-Server mechanism")
- }
- mechanism := securityMechanism{
- raw: strings.TrimSpace(value),
- name: strings.ToLower(strings.TrimSpace(parts[0])),
- protocol: "esp",
- mode: "trans",
- encryption: "null",
- }
- parameters := make(map[string]string)
- for _, raw := range parts[1:] {
- key, parameterValue, found := strings.Cut(strings.TrimSpace(raw), "=")
- if !found {
- return securityMechanism{}, errors.New("ims: malformed Security-Server parameter")
- }
- key = strings.ToLower(strings.TrimSpace(key))
- parameterValue = strings.Trim(strings.TrimSpace(parameterValue), `"`)
- if key == "" || parameterValue == "" {
- return securityMechanism{}, errors.New("ims: empty Security-Server parameter")
- }
- if _, duplicate := parameters[key]; duplicate {
- return securityMechanism{}, errors.New("ims: duplicate Security-Server parameter")
- }
- parameters[key] = parameterValue
- }
- mechanism.algorithm = strings.ToLower(parameters["alg"])
- if value := parameters["prot"]; value != "" {
- mechanism.protocol = strings.ToLower(value)
- }
- if value := parameters["mod"]; value != "" {
- mechanism.mode = strings.ToLower(value)
- }
- if value := parameters["ealg"]; value != "" {
- mechanism.encryption = strings.ToLower(value)
- }
- var err error
- if mechanism.spiClient, err = decimalUint32(parameters["spi-c"]); err != nil {
- return securityMechanism{}, err
- }
- if mechanism.spiServer, err = decimalUint32(parameters["spi-s"]); err != nil {
- return securityMechanism{}, err
- }
- if mechanism.portClient, err = decimalPort(parameters["port-c"]); err != nil {
- return securityMechanism{}, err
- }
- if mechanism.portServer, err = decimalPort(parameters["port-s"]); err != nil {
- return securityMechanism{}, err
- }
- mechanism.preference, err = preferenceValue(parameters["q"])
- if err != nil {
- return securityMechanism{}, err
- }
- return mechanism, nil
- }
- func decimalUint32(value string) (uint32, error) {
- if value == "" || len(value) > 10 {
- return 0, errors.New("ims: invalid Security-Server SPI")
- }
- parsed, err := strconv.ParseUint(value, 10, 32)
- if err != nil {
- return 0, errors.New("ims: invalid Security-Server SPI")
- }
- return uint32(parsed), nil
- }
- func decimalPort(value string) (int, error) {
- parsed, err := strconv.Atoi(value)
- if err != nil || parsed < 1 || parsed > 65535 {
- return 0, errors.New("ims: invalid Security-Server port")
- }
- return parsed, nil
- }
- func preferenceValue(value string) (int, error) {
- if value == "" {
- return 0, nil
- }
- whole, fraction, found := strings.Cut(value, ".")
- if whole != "0" && whole != "1" {
- return 0, errors.New("ims: invalid Security-Server preference")
- }
- if !found {
- if whole == "1" {
- return 1000, nil
- }
- return 0, nil
- }
- if len(fraction) > 3 {
- return 0, errors.New("ims: invalid Security-Server preference")
- }
- for len(fraction) < 3 {
- fraction += "0"
- }
- numeric, err := strconv.Atoi(fraction)
- if err != nil || (whole == "1" && numeric != 0) {
- return 0, errors.New("ims: invalid Security-Server preference")
- }
- if whole == "1" {
- return 1000, nil
- }
- return numeric, nil
- }
- func expandIPSecKeys(ck []byte, ik []byte) (encryption []byte, integrity []byte, err error) {
- if len(ck) != 16 || len(ik) != 16 {
- return nil, nil, errors.New("ims: AKA did not return 16-byte CK and IK")
- }
- encryption = append([]byte(nil), ck...)
- integrity = make([]byte, 20)
- copy(integrity, ik)
- return encryption, integrity, nil
- }
- type xfrmOperation struct {
- description string
- arguments []string
- }
- func buildXFRMInstallPlan(config IPSecSAConfig) ([]xfrmOperation, error) {
- if err := validateIPSecSAConfig(config); err != nil {
- return nil, err
- }
- var operations []xfrmOperation
- states := []struct {
- description string
- source net.IP
- destination net.IP
- spi uint32
- reqid uint32
- }{
- {"outbound UE-client to P-CSCF-server state", config.LocalIP, config.RemoteIP, config.PCSCFServerSPI, clientPairReqID(config)},
- {"inbound P-CSCF-server to UE-client state", config.RemoteIP, config.LocalIP, config.UEClientSPI, clientPairReqID(config)},
- {"inbound P-CSCF-client to UE-server state", config.RemoteIP, config.LocalIP, config.UEServerSPI, serverPairReqID(config)},
- {"outbound UE-server to P-CSCF-client state", config.LocalIP, config.RemoteIP, config.PCSCFClientSPI, serverPairReqID(config)},
- }
- for _, state := range states {
- operations = append(operations, xfrmOperation{
- description: state.description,
- arguments: []string{
- "xfrm", "state", "add",
- "src", state.source.String(),
- "dst", state.destination.String(),
- "proto", "esp",
- "spi", fmt.Sprintf("0x%08x", state.spi),
- "reqid", strconv.FormatUint(uint64(state.reqid), 10),
- "mode", "transport",
- "replay-window", "32",
- "auth-trunc", "hmac(sha1)", "0x" + hex.EncodeToString(config.IntegrityKey), "96",
- "enc", "cbc(aes)", "0x" + hex.EncodeToString(config.EncryptionKey),
- },
- })
- }
- for _, flow := range xfrmFlows(config) {
- for _, protocol := range flow.protocols {
- operations = append(operations, xfrmOperation{
- description: flow.description + " " + protocol + " policy",
- arguments: []string{
- flow.family,
- "xfrm", "policy", "add",
- "src", flow.sourcePrefix,
- "dst", flow.destinationPrefix,
- "proto", protocol,
- "sport", strconv.Itoa(flow.sourcePort),
- "dport", strconv.Itoa(flow.destinationPort),
- "dir", flow.direction,
- "priority", "100",
- "tmpl",
- "src", flow.templateSource.String(),
- "dst", flow.templateDestination.String(),
- "proto", "esp",
- "spi", fmt.Sprintf("0x%08x", flow.spi),
- "reqid", strconv.FormatUint(uint64(flow.reqid), 10),
- "mode", "transport",
- "level", "required",
- },
- })
- }
- }
- return operations, nil
- }
- func buildXFRMCleanupPlan(config IPSecSAConfig) []xfrmOperation {
- var operations []xfrmOperation
- flows := xfrmFlows(config)
- for flowIndex := len(flows) - 1; flowIndex >= 0; flowIndex-- {
- flow := flows[flowIndex]
- for protocolIndex := len(flow.protocols) - 1; protocolIndex >= 0; protocolIndex-- {
- protocol := flow.protocols[protocolIndex]
- operations = append(operations, xfrmOperation{
- description: "delete " + flow.description + " " + protocol + " policy",
- arguments: []string{
- flow.family,
- "xfrm", "policy", "delete",
- "src", flow.sourcePrefix,
- "dst", flow.destinationPrefix,
- "proto", protocol,
- "sport", strconv.Itoa(flow.sourcePort),
- "dport", strconv.Itoa(flow.destinationPort),
- "dir", flow.direction,
- },
- })
- }
- }
- states := []struct {
- source net.IP
- destination net.IP
- spi uint32
- }{
- {config.LocalIP, config.RemoteIP, config.PCSCFClientSPI},
- {config.RemoteIP, config.LocalIP, config.UEServerSPI},
- {config.RemoteIP, config.LocalIP, config.UEClientSPI},
- {config.LocalIP, config.RemoteIP, config.PCSCFServerSPI},
- }
- for _, state := range states {
- operations = append(operations, xfrmOperation{
- description: "delete ipsec-3gpp state",
- arguments: []string{
- "xfrm", "state", "delete",
- "src", state.source.String(),
- "dst", state.destination.String(),
- "proto", "esp",
- "spi", fmt.Sprintf("0x%08x", state.spi),
- },
- })
- }
- return operations
- }
- type xfrmFlow struct {
- description string
- family string
- sourcePrefix string
- destinationPrefix string
- sourcePort int
- destinationPort int
- direction string
- templateSource net.IP
- templateDestination net.IP
- spi uint32
- reqid uint32
- protocols []string
- }
- func xfrmFlows(config IPSecSAConfig) []xfrmFlow {
- family := "-4"
- prefix := "/32"
- if config.LocalIP.To4() == nil {
- family = "-6"
- prefix = "/128"
- }
- localPrefix := config.LocalIP.String() + prefix
- remotePrefix := config.RemoteIP.String() + prefix
- return []xfrmFlow{
- {
- description: "UE-client to P-CSCF-server", family: family,
- sourcePrefix: localPrefix, destinationPrefix: remotePrefix,
- sourcePort: config.UEClientPort, destinationPort: config.PCSCFServerPort,
- direction: "out", templateSource: config.LocalIP, templateDestination: config.RemoteIP,
- spi: config.PCSCFServerSPI, reqid: clientPairReqID(config),
- protocols: []string{"tcp", "udp"},
- },
- {
- description: "P-CSCF-server to UE-client", family: family,
- sourcePrefix: remotePrefix, destinationPrefix: localPrefix,
- sourcePort: config.PCSCFServerPort, destinationPort: config.UEClientPort,
- direction: "in", templateSource: config.RemoteIP, templateDestination: config.LocalIP,
- spi: config.UEClientSPI, reqid: clientPairReqID(config),
- protocols: []string{"tcp"},
- },
- {
- description: "P-CSCF-client to UE-server", family: family,
- sourcePrefix: remotePrefix, destinationPrefix: localPrefix,
- sourcePort: config.PCSCFClientPort, destinationPort: config.UEServerPort,
- direction: "in", templateSource: config.RemoteIP, templateDestination: config.LocalIP,
- spi: config.UEServerSPI, reqid: serverPairReqID(config),
- protocols: []string{"tcp", "udp"},
- },
- {
- description: "UE-server to P-CSCF-client", family: family,
- sourcePrefix: localPrefix, destinationPrefix: remotePrefix,
- sourcePort: config.UEServerPort, destinationPort: config.PCSCFClientPort,
- direction: "out", templateSource: config.LocalIP, templateDestination: config.RemoteIP,
- spi: config.PCSCFClientSPI, reqid: serverPairReqID(config),
- protocols: []string{"tcp"},
- },
- }
- }
- func clientPairReqID(config IPSecSAConfig) uint32 {
- reqid := (config.UEClientSPI ^ config.PCSCFServerSPI) & 0x7fffffff
- if reqid == 0 {
- return 1
- }
- return reqid
- }
- func serverPairReqID(config IPSecSAConfig) uint32 {
- reqid := (config.UEServerSPI ^ config.PCSCFClientSPI) & 0x7fffffff
- if reqid == 0 {
- reqid = 2
- }
- if reqid == clientPairReqID(config) {
- reqid ^= 0x40000000
- if reqid == 0 {
- reqid = 2
- }
- }
- return reqid
- }
- func validateIPSecSAConfig(config IPSecSAConfig) error {
- local := config.LocalIP
- remote := config.RemoteIP
- if local == nil || remote == nil || local.IsUnspecified() || remote.IsUnspecified() ||
- (local.To4() == nil) != (remote.To4() == nil) {
- return errors.New("ims: ipsec-3gpp endpoints are invalid or use different IP families")
- }
- spis := []uint32{
- config.UEClientSPI, config.UEServerSPI, config.PCSCFClientSPI, config.PCSCFServerSPI,
- }
- seen := make(map[uint32]struct{}, len(spis))
- for _, spi := range spis {
- if spi == 0 {
- return errors.New("ims: ipsec-3gpp SPI is zero")
- }
- if _, duplicate := seen[spi]; duplicate {
- return errors.New("ims: ipsec-3gpp SPIs must be unique")
- }
- seen[spi] = struct{}{}
- }
- ports := []int{
- config.UEClientPort, config.UEServerPort, config.PCSCFClientPort, config.PCSCFServerPort,
- }
- for _, port := range ports {
- if !validProtectedPort(port) {
- return errors.New("ims: ipsec-3gpp protected port is invalid")
- }
- }
- if config.UEClientPort == config.UEServerPort ||
- config.PCSCFClientPort == config.PCSCFServerPort {
- return errors.New("ims: client and server protected ports must differ")
- }
- if len(config.EncryptionKey) != 16 || len(config.IntegrityKey) != 20 {
- return errors.New("ims: ipsec-3gpp key length is invalid")
- }
- return nil
- }
- func cloneIPSecSAConfig(config IPSecSAConfig) IPSecSAConfig {
- config.LocalIP = append(net.IP(nil), config.LocalIP...)
- config.RemoteIP = append(net.IP(nil), config.RemoteIP...)
- config.EncryptionKey = append([]byte(nil), config.EncryptionKey...)
- config.IntegrityKey = append([]byte(nil), config.IntegrityKey...)
- return config
- }
- func zeroBytes(value []byte) {
- for index := range value {
- value[index] = 0
- }
- }
- func (session *Session) securityOffered() bool {
- return session.provider.config.SecurityMode != SecurityDisabled && !session.securityDeclined
- }
- func (session *Session) securityFromResponse(response *sipResponse) (securityAgreement, bool, error) {
- if !session.securityOffered() {
- return securityAgreement{}, false, nil
- }
- values := response.values("Security-Server")
- if len(splitHeaderValues(values)) == 0 {
- if session.provider.config.SecurityMode == SecurityRequired {
- return securityAgreement{}, false, ErrIPSecAgreementRequired
- }
- session.declineSecurity()
- return securityAgreement{}, false, nil
- }
- agreement, err := parseSecurityAgreement(values, session.securityProposal)
- if err != nil {
- return securityAgreement{}, false, err
- }
- return agreement, true, nil
- }
- func (session *Session) declineSecurity() {
- session.securityDeclined = true
- session.endpoint = session.initialEndpoint
- if session.protectedTCP != nil {
- _ = session.protectedTCP.Close()
- session.protectedTCP = nil
- }
- if session.protectedUDP != nil {
- _ = session.protectedUDP.Close()
- session.protectedUDP = nil
- }
- session.securityProposal = securityProposal{}
- }
- func (session *Session) activateIPSec(
- ctx context.Context,
- agreement securityAgreement,
- ck []byte,
- ik []byte,
- ) error {
- if session.securityActive {
- return errors.New("ims: ipsec-3gpp is already active")
- }
- if !session.securityOffered() {
- return ErrIPSecAgreementRequired
- }
- encryptionKey, integrityKey, err := expandIPSecKeys(ck, ik)
- if err != nil {
- return err
- }
- defer zeroBytes(encryptionKey)
- defer zeroBytes(integrityKey)
- localIP := addressIP(session.conn.LocalAddr())
- remoteIP := addressIP(session.conn.RemoteAddr())
- if localIP == nil || remoteIP == nil {
- return errors.New("ims: protected SIP endpoints are unavailable")
- }
- selected := agreement.selected
- config := IPSecSAConfig{
- LocalIP: localIP,
- RemoteIP: remoteIP,
- UEClientSPI: session.securityProposal.spiClient,
- UEServerSPI: session.securityProposal.spiServer,
- PCSCFClientSPI: selected.spiClient,
- PCSCFServerSPI: selected.spiServer,
- UEClientPort: session.securityProposal.portClient,
- UEServerPort: session.securityProposal.portServer,
- PCSCFClientPort: selected.portClient,
- PCSCFServerPort: selected.portServer,
- EncryptionKey: encryptionKey,
- IntegrityKey: integrityKey,
- }
- handle, err := session.provider.installer.Install(ctx, config)
- if err != nil {
- return fmt.Errorf("%w: %v", ErrIPSecInstall, err)
- }
- if handle == nil {
- return fmt.Errorf("%w: installer returned no handle", ErrIPSecInstall)
- }
- remoteAddress := net.JoinHostPort(remoteIP.String(), strconv.Itoa(selected.portServer))
- _ = session.conn.Close()
- connection, dialErr := dialSIP(
- ctx,
- session.transport,
- localIP.String(),
- session.securityProposal.portClient,
- remoteAddress,
- )
- if dialErr != nil {
- cleanupErr := handle.Close(context.Background())
- if cleanupErr != nil {
- return errors.Join(
- fmt.Errorf("ims: connect protected P-CSCF: %w", dialErr),
- fmt.Errorf("ims: roll back ipsec-3gpp: %w", cleanupErr),
- )
- }
- return fmt.Errorf("ims: connect protected P-CSCF: %w", dialErr)
- }
- session.conn = connection
- if session.transport == "tcp" {
- session.reader = bufio.NewReader(connection)
- } else {
- session.reader = nil
- }
- session.endpoint.port = selected.portServer
- session.securityAgreement = agreement
- session.securityActive = true
- session.ipsecHandle = handle
- return nil
- }
- func (session *Session) contactAddress() string {
- if session.securityOffered() {
- host := addressHost(session.conn.LocalAddr())
- return net.JoinHostPort(host, strconv.Itoa(session.securityProposal.portServer))
- }
- return session.conn.LocalAddr().String()
- }
- func (session *Session) emptyDigestAuthorization() string {
- uri := "sip:" + session.identity.domain
- return "Digest " + strings.Join([]string{
- `username="` + quoteDigest(session.identity.private) + `"`,
- `realm="` + quoteDigest(session.identity.domain) + `"`,
- `nonce=""`,
- `uri="` + quoteDigest(uri) + `"`,
- `response=""`,
- "algorithm=AKAv1-MD5",
- "integrity-protected=no",
- }, ", ")
- }
- func (session *Session) validProtectedUDPSource(remote *net.UDPAddr) bool {
- if remote == nil || !session.securityActive {
- return false
- }
- expectedIP := addressIP(session.conn.RemoteAddr())
- return expectedIP != nil &&
- expectedIP.Equal(remote.IP) &&
- remote.Port == session.securityAgreement.selected.portClient
- }
- func (session *Session) effectiveSecurityMode() string {
- if session.securityActive {
- return "ipsec-3gpp"
- }
- return "none"
- }
|