Compare commits
10 Commits
4817d5ed0d
...
a0c13fba99
Author | SHA1 | Date |
---|---|---|
Alexander Couzens | a0c13fba99 | |
Alexander Couzens | 9852101624 | |
Alexander Couzens | 8074d68e57 | |
Alexander Couzens | d776ea007c | |
Alexander Couzens | 40974e78dd | |
Alexander Couzens | 46a01090bb | |
Alexander Couzens | 6d8bcb834a | |
Alexander Couzens | 1c92c4d83e | |
Alexander Couzens | e7e6a51fb1 | |
Alexander Couzens | dfa0f7daf5 |
694
README.md
694
README.md
|
@ -1,693 +1,9 @@
|
|||
# strongSwan Configuration #
|
||||
# strongSwan osmo-epdg component
|
||||
|
||||
## Overview ##
|
||||
- strongswan + a osmo-epdg plugins
|
||||
|
||||
strongSwan is an OpenSource IPsec-based VPN solution.
|
||||
## example configuration
|
||||
|
||||
This document is just a short introduction of the strongSwan **swanctl** command
|
||||
which uses the modern [**vici**](src/libcharon/plugins/vici/README.md) *Versatile
|
||||
IKE Configuration Interface*. The deprecated **ipsec** command using the legacy
|
||||
**stroke** configuration interface is described [**here**](README_LEGACY.md).
|
||||
For more detailed information consult the man pages and
|
||||
[**our wiki**](https://wiki.strongswan.org).
|
||||
See ./osmo-epdg for a full example configuration (both UE and ePDG).
|
||||
|
||||
|
||||
## Quickstart ##
|
||||
|
||||
Certificates for users, hosts and gateways are issued by a fictitious
|
||||
strongSwan CA. In our example scenarios the CA certificate `strongswanCert.pem`
|
||||
must be present on all VPN endpoints in order to be able to authenticate the
|
||||
peers. For your particular VPN application you can either use certificates from
|
||||
any third-party CA or generate the needed private keys and certificates yourself
|
||||
with the strongSwan **pki** tool, the use of which will be explained in one of
|
||||
the sections following below.
|
||||
|
||||
|
||||
### Site-to-Site Case ###
|
||||
|
||||
In this scenario two security gateways _moon_ and _sun_ will connect the
|
||||
two subnets _moon-net_ and _sun-net_ with each other through a VPN tunnel
|
||||
set up between the two gateways:
|
||||
|
||||
10.1.0.0/16 -- | 192.168.0.1 | === | 192.168.0.2 | -- 10.2.0.0/16
|
||||
moon-net moon sun sun-net
|
||||
|
||||
Configuration on gateway _moon_:
|
||||
|
||||
/etc/swanctl/x509ca/strongswanCert.pem
|
||||
/etc/swanctl/x509/moonCert.pem
|
||||
/etc/swanctl/private/moonKey.pem
|
||||
|
||||
/etc/swanctl/swanctl.conf:
|
||||
|
||||
connections {
|
||||
net-net {
|
||||
remote_addrs = 192.168.0.2
|
||||
|
||||
local {
|
||||
auth = pubkey
|
||||
certs = moonCert.pem
|
||||
}
|
||||
remote {
|
||||
auth = pubkey
|
||||
id = "C=CH, O=strongSwan, CN=sun.strongswan.org"
|
||||
}
|
||||
children {
|
||||
net-net {
|
||||
local_ts = 10.1.0.0/16
|
||||
remote_ts = 10.2.0.0/16
|
||||
start_action = trap
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Configuration on gateway _sun_:
|
||||
|
||||
/etc/swanctl/x509ca/strongswanCert.pem
|
||||
/etc/swanctl/x509/sunCert.pem
|
||||
/etc/swanctl/private/sunKey.pem
|
||||
|
||||
/etc/swanctl/swanctl.conf:
|
||||
|
||||
connections {
|
||||
net-net {
|
||||
remote_addrs = 192.168.0.1
|
||||
|
||||
local {
|
||||
auth = pubkey
|
||||
certs = sunCert.pem
|
||||
}
|
||||
remote {
|
||||
auth = pubkey
|
||||
id = "C=CH, O=strongSwan, CN=moon.strongswan.org"
|
||||
}
|
||||
children {
|
||||
net-net {
|
||||
local_ts = 10.2.0.0/16
|
||||
remote_ts = 10.1.0.0/16
|
||||
start_action = trap
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
The local and remote identities used in this scenario are the
|
||||
*subjectDistinguishedNames* contained in the end entity certificates.
|
||||
The certificates and private keys are loaded into the **charon** daemon with
|
||||
the command
|
||||
|
||||
swanctl --load-creds
|
||||
|
||||
whereas
|
||||
|
||||
swanctl --load-conns
|
||||
|
||||
loads the connections defined in `swanctl.conf`. With `start_action = trap` the
|
||||
IPsec connection is automatically set up with the first plaintext payload IP
|
||||
packet wanting to go through the tunnel.
|
||||
|
||||
### Host-to-Host Case ###
|
||||
|
||||
This is a setup between two single hosts which don't have a subnet behind
|
||||
them. Although IPsec transport mode would be sufficient for host-to-host
|
||||
connections we will use the default IPsec tunnel mode.
|
||||
|
||||
| 192.168.0.1 | === | 192.168.0.2 |
|
||||
moon sun
|
||||
|
||||
Configuration on host _moon_:
|
||||
|
||||
/etc/swanctl/x509ca/strongswanCert.pem
|
||||
/etc/swanctl/x509/moonCert.pem
|
||||
/etc/swanctl/private/moonKey.pem
|
||||
|
||||
/etc/swanctl/swanctl.conf:
|
||||
|
||||
connections {
|
||||
host-host {
|
||||
remote_addrs = 192.168.0.2
|
||||
|
||||
local {
|
||||
auth=pubkey
|
||||
certs = moonCert.pem
|
||||
}
|
||||
remote {
|
||||
auth = pubkey
|
||||
id = "C=CH, O=strongSwan, CN=sun.strongswan.org"
|
||||
}
|
||||
children {
|
||||
net-net {
|
||||
start_action = trap
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Configuration on host _sun_:
|
||||
|
||||
/etc/swanctl/x509ca/strongswanCert.pem
|
||||
/etc/swanctl/x509/sunCert.pem
|
||||
/etc/swanctl/private/sunKey.pem
|
||||
|
||||
/etc/swanctl/swanctl.conf:
|
||||
|
||||
connections {
|
||||
host-host {
|
||||
remote_addrs = 192.168.0.1
|
||||
|
||||
local {
|
||||
auth = pubkey
|
||||
certs = sunCert.pem
|
||||
}
|
||||
remote {
|
||||
auth = pubkey
|
||||
id = "C=CH, O=strongSwan, CN=moon.strongswan.org"
|
||||
}
|
||||
children {
|
||||
host-host {
|
||||
start_action = trap
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
### Roadwarrior Case ###
|
||||
|
||||
This is a very common case where a strongSwan gateway serves an arbitrary
|
||||
number of remote VPN clients usually having dynamic IP addresses.
|
||||
|
||||
10.1.0.0/16 -- | 192.168.0.1 | === | x.x.x.x |
|
||||
moon-net moon carol
|
||||
|
||||
Configuration on gateway _moon_:
|
||||
|
||||
/etc/swanctl/x509ca/strongswanCert.pem
|
||||
/etc/swanctl/x509/moonCert.pem
|
||||
/etc/swanctl/private/moonKey.pem
|
||||
|
||||
/etc/swanctl/swanctl.conf:
|
||||
|
||||
connections {
|
||||
rw {
|
||||
local {
|
||||
auth = pubkey
|
||||
certs = moonCert.pem
|
||||
id = moon.strongswan.org
|
||||
}
|
||||
remote {
|
||||
auth = pubkey
|
||||
}
|
||||
children {
|
||||
net-net {
|
||||
local_ts = 10.1.0.0/16
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Configuration on roadwarrior _carol_:
|
||||
|
||||
/etc/swanctl/x509ca/strongswanCert.pem
|
||||
/etc/swanctl/x509/carolCert.pem
|
||||
/etc/swanctl/private/carolKey.pem
|
||||
|
||||
/etc/swanctl/swanctl.conf:
|
||||
|
||||
connections {
|
||||
home {
|
||||
remote_addrs = moon.strongswan.org
|
||||
|
||||
local {
|
||||
auth = pubkey
|
||||
certs = carolCert.pem
|
||||
id = carol@strongswan.org
|
||||
}
|
||||
remote {
|
||||
auth = pubkey
|
||||
id = moon.strongswan.org
|
||||
}
|
||||
children {
|
||||
home {
|
||||
local_ts = 10.1.0.0/16
|
||||
start_action = start
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
For `remote_addrs` the hostname `moon.strongswan.org` was chosen which will be
|
||||
resolved by DNS at runtime into the corresponding IP destination address.
|
||||
In this scenario the identity of the roadwarrior `carol` is the email address
|
||||
`carol@strongswan.org` which must be included as a *subjectAlternativeName* in
|
||||
the roadwarrior certificate `carolCert.pem`.
|
||||
|
||||
|
||||
### Roadwarrior Case with Virtual IP ###
|
||||
|
||||
Roadwarriors usually have dynamic IP addresses assigned by the ISP they are
|
||||
currently attached to. In order to simplify the routing from _moon-net_ back
|
||||
to the remote access client _carol_ it would be desirable if the roadwarrior had
|
||||
an inner IP address chosen from a pre-defined pool.
|
||||
|
||||
10.1.0.0/16 -- | 192.168.0.1 | === | x.x.x.x | -- 10.3.0.1
|
||||
moon-net moon carol virtual IP
|
||||
|
||||
In our example the virtual IP address is chosen from the address pool
|
||||
`10.3.0.0/16` which can be configured by adding the section
|
||||
|
||||
pools {
|
||||
rw_pool {
|
||||
addrs = 10.3.0.0/16
|
||||
}
|
||||
}
|
||||
|
||||
to the gateway's `swanctl.conf` from where they are loaded into the **charon**
|
||||
daemon using the command
|
||||
|
||||
swanctl --load-pools
|
||||
|
||||
To request an IP address from this pool a roadwarrior can use IKEv1 mode config
|
||||
or IKEv2 configuration payloads. The configuration for both is the same
|
||||
|
||||
vips = 0.0.0.0
|
||||
|
||||
Configuration on gateway _moon_:
|
||||
|
||||
/etc/swanctl/x509ca/strongswanCert.pem
|
||||
/etc/swanctl/x509/moonCert.pem
|
||||
/etc/swanctl/private/moonKey.pem
|
||||
|
||||
/etc/swanctl/swanctl.conf:
|
||||
|
||||
connections {
|
||||
rw {
|
||||
pools = rw_pool
|
||||
|
||||
local {
|
||||
auth = pubkey
|
||||
certs = moonCert.pem
|
||||
id = moon.strongswan.org
|
||||
}
|
||||
remote {
|
||||
auth = pubkey
|
||||
}
|
||||
children {
|
||||
net-net {
|
||||
local_ts = 10.1.0.0/16
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pools {
|
||||
rw_pool {
|
||||
addrs = 10.30.0.0/16
|
||||
}
|
||||
}
|
||||
|
||||
Configuration on roadwarrior _carol_:
|
||||
|
||||
/etc/swanctl/x509ca/strongswanCert.pem
|
||||
/etc/swanctl/x509/carolCert.pem
|
||||
/etc/swanctl/private/carolKey.pem
|
||||
|
||||
/etc/swanctl/swanctl.conf:
|
||||
|
||||
connections {
|
||||
home {
|
||||
remote_addrs = moon.strongswan.org
|
||||
vips = 0.0.0.0
|
||||
|
||||
local {
|
||||
auth = pubkey
|
||||
certs = carolCert.pem
|
||||
id = carol@strongswan.org
|
||||
}
|
||||
remote {
|
||||
auth = pubkey
|
||||
id = moon.strongswan.org
|
||||
}
|
||||
children {
|
||||
home {
|
||||
local_ts = 10.1.0.0/16
|
||||
start_action = start
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
### Roadwarrior Case with EAP Authentication ###
|
||||
|
||||
This is a very common case where a strongSwan gateway serves an arbitrary
|
||||
number of remote VPN clients which authenticate themselves via a password
|
||||
based *Extended Authentication Protocol* as e.g. *EAP-MD5* or *EAP-MSCHAPv2*.
|
||||
|
||||
10.1.0.0/16 -- | 192.168.0.1 | === | x.x.x.x |
|
||||
moon-net moon carol
|
||||
|
||||
Configuration on gateway _moon_:
|
||||
|
||||
/etc/swanctl/x509ca/strongswanCert.pem
|
||||
/etc/swanctl/x509/moonCert.pem
|
||||
/etc/swanctl/private/moonKey.pem
|
||||
|
||||
/etc/swanctl/swanctl.conf:
|
||||
|
||||
connections {
|
||||
rw {
|
||||
local {
|
||||
auth = pubkey
|
||||
certs = moonCert.pem
|
||||
id = moon.strongswan.org
|
||||
}
|
||||
remote {
|
||||
auth = eap-md5
|
||||
}
|
||||
children {
|
||||
net-net {
|
||||
local_ts = 10.1.0.0/16
|
||||
}
|
||||
}
|
||||
send_certreq = no
|
||||
}
|
||||
}
|
||||
|
||||
The `swanctl.conf` file additionally contains a `secrets` section defining all
|
||||
client credentials
|
||||
|
||||
secrets {
|
||||
eap-carol {
|
||||
id = carol@strongswan.org
|
||||
secret = Ar3etTnp
|
||||
}
|
||||
eap-dave {
|
||||
id = dave@strongswan.org
|
||||
secret = W7R0g3do
|
||||
}
|
||||
}
|
||||
|
||||
Configuration on roadwarrior _carol_:
|
||||
|
||||
/etc/swanctl/x509ca/strongswanCert.pem
|
||||
|
||||
/etc/swanctl/swanctl.conf:
|
||||
|
||||
connections {
|
||||
home {
|
||||
remote_addrs = moon.strongswan.org
|
||||
|
||||
local {
|
||||
auth = eap
|
||||
id = carol@strongswan.org
|
||||
}
|
||||
remote {
|
||||
auth = pubkey
|
||||
id = moon.strongswan.org
|
||||
}
|
||||
children {
|
||||
home {
|
||||
local_ts = 10.1.0.0/16
|
||||
start_action = start
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
secrets {
|
||||
eap-carol {
|
||||
id = carol@strongswan.org
|
||||
secret = Ar3etTnp
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
### Roadwarrior Case with EAP Identity ###
|
||||
|
||||
Often a client EAP identity is exchanged via EAP which differs from the
|
||||
external IKEv2 identity. In this example the IKEv2 identity defaults to
|
||||
the IPv4 address of the client.
|
||||
|
||||
10.1.0.0/16 -- | 192.168.0.1 | === | x.x.x.x |
|
||||
moon-net moon carol
|
||||
|
||||
Configuration on gateway _moon_:
|
||||
|
||||
/etc/swanctl/x509ca/strongswanCert.pem
|
||||
/etc/swanctl/x509/moonCert.pem
|
||||
/etc/swanctl/private/moonKey.pem
|
||||
|
||||
/etc/swanctl/swanctl.conf:
|
||||
|
||||
connections {
|
||||
rw {
|
||||
local {
|
||||
auth = pubkey
|
||||
certs = moonCert.pem
|
||||
id = moon.strongswan.org
|
||||
}
|
||||
remote {
|
||||
auth = eap-md5
|
||||
eap_id = %any
|
||||
}
|
||||
children {
|
||||
net-net {
|
||||
local_ts = 10.1.0.0/16
|
||||
}
|
||||
}
|
||||
send_certreq = no
|
||||
}
|
||||
}
|
||||
|
||||
secrets {
|
||||
eap-carol {
|
||||
id = carol
|
||||
secret = Ar3etTnp
|
||||
}
|
||||
eap-dave {
|
||||
id = dave
|
||||
secret = W7R0g3do
|
||||
}
|
||||
}
|
||||
|
||||
Configuration on roadwarrior _carol_:
|
||||
|
||||
/etc/swanctl/x509ca/strongswanCert.pem
|
||||
|
||||
/etc/swanctl/swanctl.conf:
|
||||
|
||||
connections {
|
||||
home {
|
||||
remote_addrs = moon.strongswan.org
|
||||
|
||||
local {
|
||||
auth = eap
|
||||
eap_id = carol
|
||||
}
|
||||
remote {
|
||||
auth = pubkey
|
||||
id = moon.strongswan.org
|
||||
}
|
||||
children {
|
||||
home {
|
||||
local_ts = 10.1.0.0/16
|
||||
start_action = start
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
secrets {
|
||||
eap-carol {
|
||||
id = carol
|
||||
secret = Ar3etTnp
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
## Generating Certificates and CRLs ##
|
||||
|
||||
This section is not a full-blown tutorial on how to use the strongSwan **pki**
|
||||
tool. It just lists a few points that are relevant if you want to generate your
|
||||
own certificates and CRLs for use with strongSwan.
|
||||
|
||||
|
||||
### Generating a CA Certificate ###
|
||||
|
||||
The pki statement
|
||||
|
||||
pki --gen --type ed25519 --outform pem > strongswanKey.pem
|
||||
|
||||
generates an elliptic Edwards-Curve key with a cryptographic strength of 128
|
||||
bits. The corresponding public key is packed into a self-signed CA certificate
|
||||
with a lifetime of 10 years (3652 days)
|
||||
|
||||
pki --self --ca --lifetime 3652 --in strongswanKey.pem \
|
||||
--dn "C=CH, O=strongSwan, CN=strongSwan Root CA" \
|
||||
--outform pem > strongswanCert.pem
|
||||
|
||||
which can be listed with the command
|
||||
|
||||
pki --print --in strongswanCert.pem
|
||||
|
||||
subject: "C=CH, O=strongSwan, CN=strongSwan Root CA"
|
||||
issuer: "C=CH, O=strongSwan, CN=strongSwan Root CA"
|
||||
validity: not before May 18 08:32:06 2017, ok
|
||||
not after May 18 08:32:06 2027, ok (expires in 3651 days)
|
||||
serial: 57:e0:6b:3a:9a:eb:c6:e0
|
||||
flags: CA CRLSign self-signed
|
||||
subjkeyId: 2b:95:14:5b:c3:22:87:de:d1:42:91:88:63:b3:d5:c1:92:7a:0f:5d
|
||||
pubkey: ED25519 256 bits
|
||||
keyid: a7:e1:6a:3f:e7:6f:08:9d:89:ec:23:92:a9:a1:14:3c:78:a8:7a:f7
|
||||
subjkey: 2b:95:14:5b:c3:22:87:de:d1:42:91:88:63:b3:d5:c1:92:7a:0f:5d
|
||||
|
||||
If you prefer the CA private key and X.509 certificate to be in binary DER format
|
||||
then just omit the `--outform pem` option. The directory `/etc/swanctl/x509ca`
|
||||
contains all required CA certificates either in binary DER or in Base64 PEM
|
||||
format. Irrespective of the file suffix the correct format will be determined
|
||||
by strongSwan automagically.
|
||||
|
||||
|
||||
### Generating a Host or User End Entity Certificate ###
|
||||
|
||||
Again we are using the command
|
||||
|
||||
pki --gen --type ed25519 --outform pem > moonKey.pem
|
||||
|
||||
to generate an Ed25519 private key for the host `moon`. Alternatively you could
|
||||
type
|
||||
|
||||
pki --gen --type rsa --size 3072 > moonKey.der
|
||||
|
||||
to generate a traditional 3072 bit RSA key and store it in binary DER format.
|
||||
As an alternative a **TPM 2.0** *Trusted Platform Module* available on every
|
||||
recent Intel platform could be used as a virtual smartcard to securely store an
|
||||
RSA or ECDSA private key. For details, refer to the TPM 2.0
|
||||
[HOWTO](https://wiki.strongswan.org/projects/strongswan/wiki/TpmPlugin).
|
||||
|
||||
In a next step the command
|
||||
|
||||
pki --req --type priv --in moonKey.pem \
|
||||
--dn "C=CH, O=strongswan, CN=moon.strongswan.org" \
|
||||
--san moon.strongswan.org --outform pem > moonReq.pem
|
||||
|
||||
creates a PKCS#10 certificate request that has to be signed by the CA.
|
||||
Through the [multiple] use of the `--san` parameter any number of desired
|
||||
*subjectAlternativeNames* can be added to the request. These can be of the
|
||||
form
|
||||
|
||||
--san sun.strongswan.org # fully qualified host name
|
||||
--san carol@strongswan.org # RFC822 user email address
|
||||
--san 192.168.0.1 # IPv4 address
|
||||
--san fec0::1 # IPv6 address
|
||||
|
||||
Based on the certificate request the CA issues a signed end entity certificate
|
||||
with the following command
|
||||
|
||||
pki --issue --cacert strongswanCert.pem --cakey strongswanKey.pem \
|
||||
--type pkcs10 --in moonReq.pem --serial 01 --lifetime 1826 \
|
||||
--outform pem > moonCert.pem
|
||||
|
||||
If the `--serial` parameter with a hexadecimal argument is omitted then a random
|
||||
serial number is generated. Some third party VPN clients require that a VPN
|
||||
gateway certificate contains the *TLS Server Authentication* Extended Key Usage
|
||||
(EKU) flag which can be included with the following option
|
||||
|
||||
--flag serverAuth
|
||||
|
||||
If you want to use the dynamic CRL fetching feature described in one of the
|
||||
following sections then you may include one or several *crlDistributionPoints*
|
||||
in your end entity certificates using the `--crl` parameter
|
||||
|
||||
--crl http://crl.strongswan.org/strongswan.crl
|
||||
--crl "ldap://ldap.strongswan.org/cn=strongSwan Root CA, o=strongSwan,c=CH?certificateRevocationList"
|
||||
|
||||
The issued host certificate can be listed with
|
||||
|
||||
pki --print --in moonCert.pem
|
||||
|
||||
subject: "C=CH, O=strongSwan, CN=moon.strongswan.org"
|
||||
issuer: "C=CH, O=strongSwan, CN=strongSwan Root CA"
|
||||
validity: not before May 19 10:28:19 2017, ok
|
||||
not after May 19 10:28:19 2022, ok (expires in 1825 days)
|
||||
serial: 01
|
||||
altNames: moon.strongswan.org
|
||||
flags: serverAuth
|
||||
CRL URIs: http://crl.strongswan.org/strongswan.crl
|
||||
authkeyId: 2b:95:14:5b:c3:22:87:de:d1:42:91:88:63:b3:d5:c1:92:7a:0f:5d
|
||||
subjkeyId: 60:9d:de:30:a6:ca:b9:8e:87:bb:33:23:61:19:18:b8:c4:7e:23:8f
|
||||
pubkey: ED25519 256 bits
|
||||
keyid: 39:1b:b3:c2:34:72:1a:01:08:40:ce:97:75:b8:be:ce:24:30:26:29
|
||||
subjkey: 60:9d:de:30:a6:ca:b9:8e:87:bb:33:23:61:19:18:b8:c4:7e:23:8f
|
||||
|
||||
Usually, a Windows, OSX, Android or iOS based VPN client needs its private key,
|
||||
its host or user certificate and the CA certificate. The most convenient way
|
||||
to load this information is to put everything into a PKCS#12 container:
|
||||
|
||||
openssl pkcs12 -export -inkey carolKey.pem \
|
||||
-in carolCert.pem -name "carol" \
|
||||
-certfile strongswanCert.pem -caname "strongSwan Root CA" \
|
||||
-out carolCert.p12
|
||||
|
||||
The strongSwan **pki** tool currently is not able to create PKCS#12 containers
|
||||
so that **openssl** must be used.
|
||||
|
||||
|
||||
### Generating a CRL ###
|
||||
|
||||
An empty CRL that is signed by the CA can be generated with the command
|
||||
|
||||
pki --signcrl --cacert strongswanCert.pem --cakey strongswanKey.pem \
|
||||
--lifetime 30 > strongswan.crl
|
||||
|
||||
If you omit the `--lifetime` option then the default value of 15 days is used.
|
||||
CRLs can either be uploaded to a HTTP or LDAP server or put in binary DER or
|
||||
Base64 PEM format into the `/etc/swanctl/x509crl` directory from where they are
|
||||
loaded into the **charon** daemon with the command
|
||||
|
||||
swanctl --load-creds
|
||||
|
||||
|
||||
### Revoking a Certificate ###
|
||||
|
||||
A specific end entity certificate is revoked with the command
|
||||
|
||||
pki --signcrl --cacert strongswanCert.pem --cakey strongswanKey.pem \
|
||||
--lifetime 30 --lastcrl strongswan.crl \
|
||||
--reason key-compromise --cert moonCert.pem > new.crl
|
||||
|
||||
Instead of the certificate file (in our example moonCert.pem), the serial number
|
||||
of the certificate to be revoked can be indicated using the `--serial`
|
||||
parameter. The `pki --signcrl --help` command documents all possible revocation
|
||||
reasons but the `--reason` parameter can also be omitted. The content of the new
|
||||
CRL file can be listed with the command
|
||||
|
||||
pki --print --type crl --in new.crl
|
||||
|
||||
issuer: "C=CH, O=strongSwan, CN=strongSwan Root CA"
|
||||
update: this on May 19 11:13:01 2017, ok
|
||||
next on Jun 18 11:13:01 2017, ok (expires in 29 days)
|
||||
serial: 02
|
||||
authKeyId: 2b:95:14:5b:c3:22:87:de:d1:42:91:88:63:b3:d5:c1:92:7a:0f:5d
|
||||
1 revoked certificate:
|
||||
01: May 19 11:13:01 2017, key compromise
|
||||
|
||||
|
||||
### Local Caching of CRLs ###
|
||||
|
||||
The `strongswan.conf` option
|
||||
|
||||
charon {
|
||||
cache_crls = yes
|
||||
}
|
||||
|
||||
activates the local caching of CRLs that were dynamically fetched from an
|
||||
HTTP or LDAP server. Cached copies are stored in `/etc/swanctl/x509crl` using a
|
||||
unique filename formed from the issuer's *subjectKeyIdentifier* and the
|
||||
suffix `.crl`.
|
||||
|
||||
With the cached copy the CRL is immediately available after startup. When the
|
||||
local copy has become stale, an updated CRL is automatically fetched from one of
|
||||
the defined CRL distribution points during the next IKEv2 authentication.
|
||||
The ePDG must use the address 192.168.0.2
|
||||
|
|
|
@ -212,6 +212,7 @@ ARG_ENABL_SET([eap-peap], [enable EAP PEAP authentication module.])
|
|||
ARG_ENABL_SET([eap-tnc], [enable EAP TNC trusted network connect module.])
|
||||
ARG_ENABL_SET([eap-dynamic], [enable dynamic EAP proxy module.])
|
||||
ARG_ENABL_SET([eap-radius], [enable RADIUS proxy authentication module.])
|
||||
ARG_ENABL_SET([osmo-epdg], [enable osmo-epdg plugin.])
|
||||
ARG_ENABL_SET([ext-auth], [enable plugin calling an external authorization script.])
|
||||
ARG_ENABL_SET([ipseckey], [enable IPSECKEY authentication plugin.])
|
||||
ARG_ENABL_SET([keychain], [enables OS X Keychain Services credential set.])
|
||||
|
@ -431,6 +432,11 @@ if test x$eap_aka_3gpp2 = xtrue; then
|
|||
gmp=true;
|
||||
fi
|
||||
|
||||
if test x$osmo_epdg = xtrue; then
|
||||
simaka=true;
|
||||
eap_aka=true;
|
||||
fi
|
||||
|
||||
if test x$eap_aka = xtrue; then
|
||||
fips_prf=true;
|
||||
simaka=true;
|
||||
|
@ -1513,6 +1519,7 @@ ADD_PLUGIN([kernel-pfkey], [c charon starter nm cmd])
|
|||
ADD_PLUGIN([kernel-pfroute], [c charon starter nm cmd])
|
||||
ADD_PLUGIN([kernel-netlink], [c charon starter nm cmd])
|
||||
ADD_PLUGIN([resolve], [c charon cmd])
|
||||
ADD_PLUGIN([osmo-epdg], [c charon])
|
||||
ADD_PLUGIN([save-keys], [c])
|
||||
ADD_PLUGIN([socket-default], [c charon nm cmd])
|
||||
ADD_PLUGIN([socket-dynamic], [c charon cmd])
|
||||
|
@ -1720,6 +1727,7 @@ AM_CONDITIONAL(USE_EAP_PEAP, test x$eap_peap = xtrue)
|
|||
AM_CONDITIONAL(USE_EAP_TNC, test x$eap_tnc = xtrue)
|
||||
AM_CONDITIONAL(USE_EAP_DYNAMIC, test x$eap_dynamic = xtrue)
|
||||
AM_CONDITIONAL(USE_EAP_RADIUS, test x$eap_radius = xtrue)
|
||||
AM_CONDITIONAL(USE_OSMO_EPDG, test x$osmo_epdg = xtrue)
|
||||
AM_CONDITIONAL(USE_XAUTH_GENERIC, test x$xauth_generic = xtrue)
|
||||
AM_CONDITIONAL(USE_XAUTH_EAP, test x$xauth_eap = xtrue)
|
||||
AM_CONDITIONAL(USE_XAUTH_PAM, test x$xauth_pam = xtrue)
|
||||
|
@ -2005,6 +2013,7 @@ AC_CONFIG_FILES([
|
|||
src/libcharon/plugins/eap_peap/Makefile
|
||||
src/libcharon/plugins/eap_tnc/Makefile
|
||||
src/libcharon/plugins/eap_radius/Makefile
|
||||
src/libcharon/plugins/osmo_epdg/Makefile
|
||||
src/libcharon/plugins/xauth_generic/Makefile
|
||||
src/libcharon/plugins/xauth_eap/Makefile
|
||||
src/libcharon/plugins/xauth_pam/Makefile
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
-----BEGIN PRIVATE KEY-----
|
||||
MC4CAQAwBQYDK2VwBCIEIG8/fPyaM21uZp+R2ykAKpC2/yviDXL7MzGJzoZtVc6r
|
||||
-----END PRIVATE KEY-----
|
|
@ -0,0 +1,36 @@
|
|||
connections {
|
||||
rw {
|
||||
local_addrs = 192.168.0.2
|
||||
pools = rw_pool
|
||||
|
||||
local {
|
||||
auth = pubkey
|
||||
certs = epdgCert.pem
|
||||
id = epdg
|
||||
}
|
||||
remote {
|
||||
auth = eap-aka
|
||||
}
|
||||
children {
|
||||
net {
|
||||
local_ts = 172.16.24.0/24
|
||||
|
||||
updown = /usr/lib/ipsec/_updown iptables
|
||||
esp_proposals = default
|
||||
}
|
||||
}
|
||||
version = 2
|
||||
# proposals = null-md5-prfmd5-null-ecp192
|
||||
# proposals = AES_CBC_128-HMAC_SHA1_96-PRF_HMAC_SHA1-MODP_2048
|
||||
}
|
||||
}
|
||||
|
||||
secrets {
|
||||
}
|
||||
|
||||
pools {
|
||||
rw_pool {
|
||||
addrs = 172.20.0.0/24
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIBWTCCAQugAwIBAgIBATAFBgMrZXAwPzELMAkGA1UEBhMCQ0gxEzARBgNVBAoT
|
||||
CnN0cm9uZ1N3YW4xGzAZBgNVBAMTEnN0cm9uZ1N3YW4gUm9vdCBDQTAeFw0yMzAx
|
||||
MjUwMjI0MzBaFw0yODAxMjUwMjI0MzBaMDExCzAJBgNVBAYTAkNIMRMwEQYDVQQK
|
||||
EwpzdHJvbmdzd2FuMQ0wCwYDVQQDEwRlcGRnMCowBQYDK2VwAyEACh6O6jF06dqu
|
||||
GBo3TGPm0kpqTxwBWKhLtqptpjnLIuyjOjA4MB8GA1UdIwQYMBaAFJVHEuUMeHiS
|
||||
I+xjtCGE2ha+u8zlMBUGA1UdEQQOMAyHBMCoAAKCBGVwZGcwBQYDK2VwA0EAk+75
|
||||
9+1Vvvrq7d2KmBF0rJ7yT0+kEOn9bArUvXFTkswCGxbvknLPKuJ+80nK2JDRhBi5
|
||||
fBlIuS1GJHlSntX7Dw==
|
||||
-----END CERTIFICATE-----
|
|
@ -0,0 +1,10 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIBdjCCASigAwIBAgIIBLX5AduBn1YwBQYDK2VwMD8xCzAJBgNVBAYTAkNIMRMw
|
||||
EQYDVQQKEwpzdHJvbmdTd2FuMRswGQYDVQQDExJzdHJvbmdTd2FuIFJvb3QgQ0Ew
|
||||
HhcNMjMwMTI1MDIyMTM2WhcNMzMwMTI0MDIyMTM2WjA/MQswCQYDVQQGEwJDSDET
|
||||
MBEGA1UEChMKc3Ryb25nU3dhbjEbMBkGA1UEAxMSc3Ryb25nU3dhbiBSb290IENB
|
||||
MCowBQYDK2VwAyEAC/rxt1BbzUYNLSvpMGAWXlYowpdfq16HvI45Nno4DRejQjBA
|
||||
MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBSVRxLl
|
||||
DHh4kiPsY7QhhNoWvrvM5TAFBgMrZXADQQDk6neTZfsA1PJKejYt2oW3dsZJmYwS
|
||||
Yez/nEA8Jvyzmu4aQ0q6VahRXRSqESbTh5Z2wyVLEfsW9CyMUI/w8eEI
|
||||
-----END CERTIFICATE-----
|
|
@ -0,0 +1,34 @@
|
|||
connections {
|
||||
home {
|
||||
local_addrs = 192.168.0.1
|
||||
remote_addrs = 192.168.0.2
|
||||
vips = 0.0.0.0
|
||||
|
||||
local {
|
||||
auth = eap-aka
|
||||
id = 0999421234567890@wlan.mnc999.mcc42.3gppnetwork.org
|
||||
}
|
||||
remote {
|
||||
auth = pubkey
|
||||
id = epdg
|
||||
}
|
||||
children {
|
||||
home {
|
||||
remote_ts = 172.16.24.0/24
|
||||
updown = /usr/lib/ipsec/_updown iptables
|
||||
# esp_proposals = aes128gcm128-x25519
|
||||
}
|
||||
}
|
||||
version = 2
|
||||
# proposals = null-md5-prfmd5-null-ecp192
|
||||
}
|
||||
}
|
||||
|
||||
secrets {
|
||||
eap {
|
||||
id = 0999421234567890@wlan.mnc999.mcc42.3gppnetwork.org
|
||||
# 16 bytes key 0xff, 16 byte opc 0xee
|
||||
secret = 0x1111111111111111111111111111111122222222222222222222222222222222
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIBdjCCASigAwIBAgIIBLX5AduBn1YwBQYDK2VwMD8xCzAJBgNVBAYTAkNIMRMw
|
||||
EQYDVQQKEwpzdHJvbmdTd2FuMRswGQYDVQQDExJzdHJvbmdTd2FuIFJvb3QgQ0Ew
|
||||
HhcNMjMwMTI1MDIyMTM2WhcNMzMwMTI0MDIyMTM2WjA/MQswCQYDVQQGEwJDSDET
|
||||
MBEGA1UEChMKc3Ryb25nU3dhbjEbMBkGA1UEAxMSc3Ryb25nU3dhbiBSb290IENB
|
||||
MCowBQYDK2VwAyEAC/rxt1BbzUYNLSvpMGAWXlYowpdfq16HvI45Nno4DRejQjBA
|
||||
MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBSVRxLl
|
||||
DHh4kiPsY7QhhNoWvrvM5TAFBgMrZXADQQDk6neTZfsA1PJKejYt2oW3dsZJmYwS
|
||||
Yez/nEA8Jvyzmu4aQ0q6VahRXRSqESbTh5Z2wyVLEfsW9CyMUI/w8eEI
|
||||
-----END CERTIFICATE-----
|
|
@ -467,6 +467,13 @@ if MONOLITHIC
|
|||
endif
|
||||
endif
|
||||
|
||||
if USE_OSMO_EPDG
|
||||
SUBDIRS += plugins/osmo_epdg
|
||||
if MONOLITHIC
|
||||
libcharon_la_LIBADD += plugins/osmo_epdg/libstrongswan-osmo-epdg.la
|
||||
endif
|
||||
endif
|
||||
|
||||
if USE_TLS
|
||||
if MONOLITHIC
|
||||
# otherwise this library is linked to eap_tls
|
||||
|
|
|
@ -949,6 +949,46 @@ METHOD(bus_t, ike_reestablish_post, void,
|
|||
this->mutex->unlock(this->mutex);
|
||||
}
|
||||
|
||||
METHOD(bus_t, eap_authorize, bool,
|
||||
private_bus_t *this, identification_t *id, bool final)
|
||||
{
|
||||
enumerator_t *enumerator;
|
||||
ike_sa_t *ike_sa;
|
||||
entry_t *entry;
|
||||
bool keep, success = TRUE;
|
||||
|
||||
ike_sa = this->thread_sa->get(this->thread_sa);
|
||||
|
||||
this->mutex->lock(this->mutex);
|
||||
enumerator = this->listeners->create_enumerator(this->listeners);
|
||||
while (enumerator->enumerate(enumerator, &entry))
|
||||
{
|
||||
if (entry->calling || !entry->listener->eap_authorize)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
entry->calling++;
|
||||
keep = entry->listener->eap_authorize(entry->listener, ike_sa, id,
|
||||
final, &success);
|
||||
entry->calling--;
|
||||
if (!keep)
|
||||
{
|
||||
unregister_listener(this, entry, enumerator);
|
||||
}
|
||||
if (!success)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
enumerator->destroy(enumerator);
|
||||
this->mutex->unlock(this->mutex);
|
||||
if (!success)
|
||||
{
|
||||
alert(this, ALERT_EAP_AUTHORIZATION_FAILED);
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
METHOD(bus_t, authorize, bool,
|
||||
private_bus_t *this, bool final)
|
||||
{
|
||||
|
@ -1154,6 +1194,7 @@ bus_t *bus_create()
|
|||
.child_rekey = _child_rekey,
|
||||
.children_migrate = _children_migrate,
|
||||
.authorize = _authorize,
|
||||
.eap_authorize = _eap_authorize,
|
||||
.narrow = _narrow,
|
||||
.assign_vips = _assign_vips,
|
||||
.handle_vips = _handle_vips,
|
||||
|
|
|
@ -157,6 +157,8 @@ enum alert_t {
|
|||
ALERT_CERT_EXCEEDED_PATH_LEN,
|
||||
/** Certificate rejected; other policy violation, certificate_t */
|
||||
ALERT_CERT_POLICY_VIOLATION,
|
||||
/** an eap_authorize() hook failed, no argument */
|
||||
ALERT_EAP_AUTHORIZATION_FAILED,
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -332,6 +334,14 @@ struct bus_t {
|
|||
*/
|
||||
bool (*authorize)(bus_t *this, bool final);
|
||||
|
||||
/**
|
||||
* EAP authorization hook.
|
||||
*
|
||||
* @param final TRUE if this is the final invocation
|
||||
* @return TRUE to establish IKE_SA, FALSE to send AUTH_FAILED
|
||||
*/
|
||||
bool (*eap_authorize)(bus_t *this, identification_t *id, bool final);
|
||||
|
||||
/**
|
||||
* CHILD_SA traffic selector narrowing hook.
|
||||
*
|
||||
|
|
|
@ -238,6 +238,21 @@ struct listener_t {
|
|||
bool (*children_migrate)(listener_t *this, ike_sa_t *ike_sa,
|
||||
ike_sa_id_t *new, uint32_t unique);
|
||||
|
||||
/**
|
||||
* Hook called to invoke additional eap authorization rules.
|
||||
*
|
||||
* An eap authorization hook gets invoked only before sending the EAP_SUCCESS.
|
||||
*
|
||||
* @param ike_sa IKE_SA to authorize
|
||||
* @param id EAP identification
|
||||
* @param final TRUE if this is the final hook invocation
|
||||
* @param success set to TRUE to complete IKE_SA, FALSE abort
|
||||
* @return TRUE to stay registered, FALSE to unregister
|
||||
*/
|
||||
bool (*eap_authorize)(listener_t *this, ike_sa_t *ike_sa,
|
||||
identification_t *id,
|
||||
bool final, bool *success);
|
||||
|
||||
/**
|
||||
* Hook called to invoke additional authorization rules.
|
||||
*
|
||||
|
|
|
@ -116,6 +116,11 @@ struct private_eap_aka_server_t {
|
|||
* Did the client send a synchronize request?
|
||||
*/
|
||||
bool synchronized;
|
||||
|
||||
/**
|
||||
* Auth data contains porperty on EAP_SUCCESS.
|
||||
*/
|
||||
auth_cfg_t *auth;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -430,6 +435,8 @@ static status_t process_challenge(private_eap_aka_server_t *this,
|
|||
DBG1(DBG_IKE, "received RES does not match XRES");
|
||||
return FAILED;
|
||||
}
|
||||
|
||||
this->auth->add(this->auth, AUTH_RULE_EAP_IDENTITY, this->permanent->clone(this->permanent));
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
|
@ -643,6 +650,12 @@ METHOD(eap_method_t, get_type, eap_type_t,
|
|||
return EAP_AKA;
|
||||
}
|
||||
|
||||
METHOD(eap_method_t, get_auth, auth_cfg_t*,
|
||||
private_eap_aka_server_t *this)
|
||||
{
|
||||
return this->auth;
|
||||
}
|
||||
|
||||
METHOD(eap_method_t, get_msk, status_t,
|
||||
private_eap_aka_server_t *this, chunk_t *msk)
|
||||
{
|
||||
|
@ -677,6 +690,7 @@ METHOD(eap_method_t, destroy, void,
|
|||
{
|
||||
this->crypto->destroy(this->crypto);
|
||||
this->permanent->destroy(this->permanent);
|
||||
this->auth->destroy(this->auth);
|
||||
DESTROY_IF(this->pseudonym);
|
||||
DESTROY_IF(this->reauth);
|
||||
free(this->xres.ptr);
|
||||
|
@ -703,12 +717,14 @@ eap_aka_server_t *eap_aka_server_create(identification_t *server,
|
|||
.get_type = _get_type,
|
||||
.is_mutual = _is_mutual,
|
||||
.get_msk = _get_msk,
|
||||
.get_auth = _get_auth,
|
||||
.get_identifier = _get_identifier,
|
||||
.set_identifier = _set_identifier,
|
||||
.destroy = _destroy,
|
||||
},
|
||||
},
|
||||
.crypto = simaka_crypto_create(EAP_AKA),
|
||||
.auth = auth_cfg_create(),
|
||||
.mgr = lib->get(lib, "aka-manager"),
|
||||
);
|
||||
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
AM_CPPFLAGS = \
|
||||
-I$(top_srcdir)/src/libstrongswan \
|
||||
-I$(top_srcdir)/src/libcharon \
|
||||
-I$(top_srcdir)/src/libsimaka
|
||||
|
||||
AM_CFLAGS = \
|
||||
$(PLUGIN_CFLAGS)
|
||||
|
||||
libstrongswan_osmo_epdg_la_LDFLAGS = -module -avoid-version
|
||||
libstrongswan_osmo_epdg_la_LIBADD = -lgmp -losmogsm
|
||||
|
||||
if MONOLITHIC
|
||||
noinst_LTLIBRARIES = libstrongswan-osmo-epdg.la
|
||||
else
|
||||
plugin_LTLIBRARIES = libstrongswan-osmo-epdg.la
|
||||
libstrongswan_osmo_epdg_la_LIBADD += $(top_builddir)/src/libsimaka/libsimaka.la
|
||||
endif
|
||||
|
||||
libstrongswan_osmo_epdg_la_SOURCES = \
|
||||
osmo_epdg_plugin.h osmo_epdg_plugin.c \
|
||||
osmo_epdg_provider.h osmo_epdg_provider.c \
|
||||
osmo_epdg_listener.h osmo_epdg_listener.c \
|
||||
gsup_client.h gsup_client.c \
|
||||
ipa_client.h ipa_client.c \
|
||||
osmo_epdg_utils.h osmo_epdg_utils.c \
|
||||
osmo_epdg_db.h osmo_epdg_db.c
|
|
@ -0,0 +1,558 @@
|
|||
/*
|
||||
* Copyright (C) 2023 sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* Author: Alexander Couzens <acouzens@sysmocom.de>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
/* TODO: check license */
|
||||
|
||||
#include <collections/enumerator.h>
|
||||
#include <collections/linked_list.h>
|
||||
#include <collections/blocking_queue.h>
|
||||
#include <processing/jobs/callback_job.h>
|
||||
#include <threading/mutex.h>
|
||||
#include <threading/thread.h>
|
||||
#include <threading/condvar.h>
|
||||
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/gsm/gsup.h>
|
||||
#include <osmocom/gsm/protocol/ipaccess.h>
|
||||
|
||||
#include <library.h>
|
||||
|
||||
#include "ipa_client.h"
|
||||
#include "gsup_client.h"
|
||||
#include "osmo_epdg_utils.h"
|
||||
|
||||
typedef struct gsup_request_t gsup_request_t;
|
||||
struct gsup_request_t {
|
||||
/**
|
||||
* Mutex used to synchronize access to the condvar
|
||||
*/
|
||||
mutex_t *mutex;
|
||||
|
||||
/**
|
||||
* Condvar used to wait for a response
|
||||
*/
|
||||
condvar_t *condvar;
|
||||
|
||||
struct msgb *msg;
|
||||
enum osmo_gsup_message_type msg_type;
|
||||
osmo_epdg_gsup_response_t *resp;
|
||||
};
|
||||
|
||||
typedef struct private_osmo_epdg_gsup_client_t private_osmo_epdg_gsup_client_t;
|
||||
struct private_osmo_epdg_gsup_client_t {
|
||||
/**
|
||||
* Public osmo_epdg_gsup_client_t
|
||||
*/
|
||||
osmo_epdg_gsup_client_t public;
|
||||
|
||||
osmo_epdg_ipa_client_t *ipa;
|
||||
|
||||
/**
|
||||
* List of all pending requests
|
||||
*/
|
||||
blocking_queue_t *pending;
|
||||
|
||||
/**
|
||||
* Current request which isn't part of linked list
|
||||
*/
|
||||
gsup_request_t *current_request;
|
||||
|
||||
/**
|
||||
* Mutex to protect current_request
|
||||
*/
|
||||
mutex_t *mutex;
|
||||
|
||||
char *uri;
|
||||
|
||||
stream_t *stream;
|
||||
};
|
||||
|
||||
static gsup_request_t *gsup_request_create(enum osmo_gsup_message_type msg_type, struct msgb *msg)
|
||||
{
|
||||
gsup_request_t *req = calloc(1, sizeof(gsup_request_t));
|
||||
if (!req)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
req->mutex = mutex_create(MUTEX_TYPE_DEFAULT);
|
||||
req->condvar = condvar_create(CONDVAR_TYPE_DEFAULT);
|
||||
req->msg_type = msg_type;
|
||||
req->msg = msg;
|
||||
|
||||
return req;
|
||||
}
|
||||
|
||||
static void gsup_request_destroy(private_osmo_epdg_gsup_client_t *this, gsup_request_t *req)
|
||||
{
|
||||
if (!req)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (req->mutex)
|
||||
{
|
||||
req->mutex->destroy(req->mutex);
|
||||
}
|
||||
|
||||
if (req->condvar)
|
||||
{
|
||||
req->condvar->destroy(req->condvar);
|
||||
}
|
||||
|
||||
if (req->msg)
|
||||
{
|
||||
free(req->msg);
|
||||
}
|
||||
|
||||
if (req->resp)
|
||||
{
|
||||
free(req->resp);
|
||||
}
|
||||
free(req);
|
||||
}
|
||||
|
||||
static struct msgb *encode_to_msgb(struct osmo_gsup_message *gsup_msg)
|
||||
{
|
||||
chunk_t msg_chunk;
|
||||
struct msgb *msg;
|
||||
int ret;
|
||||
|
||||
msg_chunk = chunk_alloc(4000);
|
||||
if (msg_chunk.ptr == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
msg = chunk_to_msgb(&msg_chunk);
|
||||
if (!msg)
|
||||
{
|
||||
goto free_msg;
|
||||
}
|
||||
|
||||
/* reserve headroom */
|
||||
msgb_reserve(msg, 64);
|
||||
ret = osmo_gsup_encode(msg, gsup_msg);
|
||||
if (ret)
|
||||
{
|
||||
DBG1(DBG_NET, "GSUP: couldn't encode gsup message %d.", ret);
|
||||
goto free_msg;
|
||||
}
|
||||
|
||||
return msg;
|
||||
|
||||
free_msg:
|
||||
chunk_free(&msg_chunk);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* enqueue a message/request to be send out and wait for the response.
|
||||
*
|
||||
* when exiting enqueue, it must be guaranteed the req isn't referenced by anything
|
||||
* @param timeout_ms A timeout in ms
|
||||
* @return TRUE is the request timed out.
|
||||
*/
|
||||
static bool enqueue(private_osmo_epdg_gsup_client_t *this, gsup_request_t *req, u_int timeout_ms)
|
||||
{
|
||||
bool ret = FALSE;
|
||||
|
||||
DBG1(DBG_NET, "Enqueuing message. Waiting %d ms for an answer", timeout_ms);
|
||||
req->mutex->lock(req->mutex);
|
||||
this->pending->enqueue(this->pending, req);
|
||||
ret = req->condvar->timed_wait(req->condvar, req->mutex, timeout_ms);
|
||||
if (ret)
|
||||
{
|
||||
void *found = this->pending->remove(this->pending, req);
|
||||
if (!found)
|
||||
{
|
||||
this->mutex->lock(this->mutex);
|
||||
if (this->current_request == req)
|
||||
{
|
||||
this->current_request = NULL;
|
||||
}
|
||||
this->mutex->unlock(this->mutex);
|
||||
}
|
||||
DBG1(DBG_NET, "Message timedout!");
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
METHOD(osmo_epdg_gsup_client_t, tunnel_request, osmo_epdg_gsup_response_t*,
|
||||
private_osmo_epdg_gsup_client_t *this, char *imsi, char *apn)
|
||||
{
|
||||
struct osmo_gsup_message gsup_msg = {0};
|
||||
struct osmo_gsup_pdp_info *pdp;
|
||||
struct msgb *msg;
|
||||
bool timedout;
|
||||
|
||||
DBG1(DBG_NET, "Tunnel Request Request for %s", imsi);
|
||||
gsup_msg.message_type = OSMO_GSUP_MSGT_EPDG_TUNNEL_REQUEST;
|
||||
gsup_msg.current_rat_type = OSMO_RAT_EUTRAN_SGS;
|
||||
if (!imsi || strlen(imsi) == 0)
|
||||
{
|
||||
/* TODO: inval imsi! */
|
||||
return NULL;
|
||||
}
|
||||
strncpy(gsup_msg.imsi, imsi, sizeof(gsup_msg.imsi))
|
||||
|
||||
if (apn && strlen(apn) > 0)
|
||||
{
|
||||
gsup_msg->num_pdp_infos = 1;
|
||||
pdp = gsup_msg.pdp_infos[0];
|
||||
pdp->context_id = 1;
|
||||
pdp->have_info = 1;
|
||||
pdp->apn_enc = apn;
|
||||
pdp->apn_enc_len = strlen(apn);
|
||||
}
|
||||
|
||||
msg = encode_to_msgb(&gsup_msg);
|
||||
if (!msg)
|
||||
{
|
||||
DBG1(DBG_NET, "Couldn't alloc/encode gsup message.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
gsup_request_t *req = gsup_request_create(OSMO_GSUP_MSGT_EPDG_TUNNEL_REQUEST, msg);
|
||||
osmo_epdg_gsup_response_t *resp = NULL;
|
||||
timedout = enqueue(this, req, 5000);
|
||||
if (timedout)
|
||||
{
|
||||
gsup_request_destroy(this, req);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
resp = req->resp;
|
||||
req->resp = NULL;
|
||||
gsup_request_destroy(this, req);
|
||||
return ret;
|
||||
}
|
||||
|
||||
METHOD(osmo_epdg_gsup_client_t, send_auth_request, osmo_epdg_gsup_response_t*,
|
||||
private_osmo_epdg_gsup_client_t *this, char *imsi, uint8_t cn_domain, chunk_t *auts, chunk_t *auts_rand)
|
||||
{
|
||||
struct osmo_gsup_message gsup_msg = {0};
|
||||
struct msgb *msg;
|
||||
bool timedout;
|
||||
|
||||
DBG1(DBG_NET, "Send Auth Request for %s", imsi);
|
||||
gsup_msg.message_type = OSMO_GSUP_MSGT_SEND_AUTH_INFO_REQUEST;
|
||||
gsup_msg.num_auth_vectors = 1;
|
||||
gsup_msg.current_rat_type = OSMO_RAT_EUTRAN_SGS;
|
||||
|
||||
if (!imsi || strlen(imsi) == 0)
|
||||
{
|
||||
/* TODO: inval imsi! */
|
||||
return NULL;
|
||||
}
|
||||
strncpy(gsup_msg.imsi, imsi, sizeof(gsup_msg.imsi));
|
||||
|
||||
switch (cn_domain)
|
||||
{
|
||||
case 0:
|
||||
/* empty cn_domain */
|
||||
break;
|
||||
case OSMO_GSUP_CN_DOMAIN_PS:
|
||||
case OSMO_GSUP_CN_DOMAIN_CS:
|
||||
gsup_msg.cn_domain = cn_domain;
|
||||
break;
|
||||
default:
|
||||
DBG1(DBG_NET, "GSUP: SAIR: Ignoring invalid cn_domain message.");
|
||||
break;
|
||||
}
|
||||
|
||||
if (auts && auts->ptr && auts->len != 0)
|
||||
{
|
||||
if (auts->len != 14)
|
||||
{
|
||||
/* TODO: inval auts */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
gsup_msg.auts = auts->ptr;
|
||||
}
|
||||
|
||||
/* TODO check for other sizes */
|
||||
if (auts_rand && auts_rand->ptr && auts_rand->len != 0)
|
||||
{
|
||||
if (auts_rand->len != 16)
|
||||
{
|
||||
/* TODO: inval auts */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
gsup_msg.rand = auts_rand->ptr;
|
||||
}
|
||||
|
||||
msg = encode_to_msgb(&gsup_msg);
|
||||
if (!msg)
|
||||
{
|
||||
DBG1(DBG_NET, "Couldn't alloc/encode gsup message.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
gsup_request_t *req = gsup_request_create(OSMO_GSUP_MSGT_SEND_AUTH_INFO_REQUEST, msg);
|
||||
osmo_epdg_gsup_response_t *resp = NULL;
|
||||
timedout = enqueue(this, req, 5000);
|
||||
if (timedout)
|
||||
{
|
||||
gsup_request_destroy(this, req);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
resp = req->resp;
|
||||
req->resp = NULL;
|
||||
gsup_request_destroy(this, req);
|
||||
|
||||
return resp;
|
||||
}
|
||||
|
||||
METHOD(osmo_epdg_gsup_client_t, update_location, osmo_epdg_gsup_response_t *,
|
||||
private_osmo_epdg_gsup_client_t *this, char *imsi, uint8_t cn_domain)
|
||||
{
|
||||
struct osmo_gsup_message gsup_msg = {0};
|
||||
struct msgb *msg;
|
||||
bool timedout;
|
||||
|
||||
gsup_msg.message_type = OSMO_GSUP_MSGT_UPDATE_LOCATION_REQUEST;
|
||||
gsup_msg.current_rat_type = OSMO_RAT_EUTRAN_SGS;
|
||||
|
||||
if (!imsi || strlen(imsi) == 0)
|
||||
{
|
||||
DBG1(DBG_NET, "GSUP: ULR: Invalid IMSI!");
|
||||
return NULL;
|
||||
}
|
||||
strncpy(gsup_msg.imsi, imsi, sizeof(gsup_msg.imsi));
|
||||
|
||||
switch (cn_domain)
|
||||
{
|
||||
case 0:
|
||||
/* empty cn_domain */
|
||||
break;
|
||||
case OSMO_GSUP_CN_DOMAIN_PS:
|
||||
case OSMO_GSUP_CN_DOMAIN_CS:
|
||||
gsup_msg.cn_domain = cn_domain;
|
||||
break;
|
||||
default:
|
||||
DBG1(DBG_NET, "GSUP: ULR: Ignoring invalid cn_domain message.");
|
||||
break;
|
||||
}
|
||||
|
||||
msg = encode_to_msgb(&gsup_msg);
|
||||
if (!msg)
|
||||
{
|
||||
DBG1(DBG_NET, "GSUP: ULR: Couldn't alloc/encode gsup message.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
gsup_request_t *req = gsup_request_create(OSMO_GSUP_MSGT_UPDATE_LOCATION_REQUEST, msg);
|
||||
osmo_epdg_gsup_response_t *resp = NULL;
|
||||
timedout = enqueue(this, req, 5000);
|
||||
if (timedout)
|
||||
{
|
||||
gsup_request_destroy(this, req);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
resp = req->resp;
|
||||
req->resp = NULL;
|
||||
gsup_request_destroy(this, req);
|
||||
|
||||
return resp;
|
||||
}
|
||||
|
||||
METHOD(osmo_epdg_gsup_client_t, destroy, void,
|
||||
private_osmo_epdg_gsup_client_t *this)
|
||||
{
|
||||
free(this->uri);
|
||||
free(this);
|
||||
}
|
||||
|
||||
void tx_insert_data_result(private_osmo_epdg_gsup_client_t *this, char *imsi, uint8_t cn_domain)
|
||||
{
|
||||
struct osmo_gsup_message gsup_msg = {0};
|
||||
struct msgb *msg;
|
||||
|
||||
gsup_msg.message_type = OSMO_GSUP_MSGT_INSERT_DATA_RESULT;
|
||||
if (!imsi || strlen(imsi) == 0)
|
||||
{
|
||||
DBG1(DBG_NET, "GSUP: ULR: Invalid IMSI!");
|
||||
}
|
||||
strncpy(gsup_msg.imsi, imsi, sizeof(gsup_msg.imsi));
|
||||
|
||||
switch (cn_domain)
|
||||
{
|
||||
case 0:
|
||||
/* empty cn_domain */
|
||||
break;
|
||||
case OSMO_GSUP_CN_DOMAIN_PS:
|
||||
case OSMO_GSUP_CN_DOMAIN_CS:
|
||||
gsup_msg.cn_domain = cn_domain;
|
||||
break;
|
||||
default:
|
||||
DBG1(DBG_NET, "GSUP: ULR: Ignoring invalid cn_domain message.");
|
||||
break;
|
||||
}
|
||||
|
||||
msg = encode_to_msgb(&gsup_msg);
|
||||
if (!msg)
|
||||
{
|
||||
DBG1(DBG_NET, "GSUP: ULR: Couldn't alloc/encode gsup message.");
|
||||
}
|
||||
this->ipa->send(this->ipa, IPAC_PROTO_EXT_GSUP, msg);
|
||||
}
|
||||
|
||||
static void signal_request(gsup_request_t *req, osmo_epdg_gsup_response_t *resp)
|
||||
{
|
||||
req->mutex->lock(req->mutex);
|
||||
req->resp = resp;
|
||||
req->condvar->signal(req->condvar);
|
||||
req->mutex->unlock(req->mutex);
|
||||
}
|
||||
|
||||
static bool on_recv_pdu(void *data, osmo_epdg_ipa_client_t *client, struct msgb *pdu)
|
||||
{
|
||||
private_osmo_epdg_gsup_client_t *this = data;
|
||||
osmo_epdg_gsup_response_t *resp;
|
||||
int ret;
|
||||
|
||||
resp = calloc(1, sizeof(*resp));
|
||||
if (!resp)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
ret = osmo_gsup_decode(msgb_l2(pdu), msgb_l2len(pdu), &resp->gsup);
|
||||
if (ret) {
|
||||
// TODO: failed to decode response. Close connection
|
||||
goto out;
|
||||
}
|
||||
|
||||
switch (resp->gsup.message_type)
|
||||
{
|
||||
case OSMO_GSUP_MSGT_INSERT_DATA_REQUEST:
|
||||
tx_insert_data_result(this, resp->gsup.imsi, resp->gsup.cn_domain);
|
||||
goto out;
|
||||
case OSMO_GSUP_MSGT_SEND_AUTH_INFO_ERROR:
|
||||
case OSMO_GSUP_MSGT_SEND_AUTH_INFO_RESULT:
|
||||
case OSMO_GSUP_MSGT_UPDATE_LOCATION_ERROR:
|
||||
case OSMO_GSUP_MSGT_UPDATE_LOCATION_RESULT:
|
||||
this->mutex->lock(this->mutex);
|
||||
if ((this->current_request->msg_type & 0xfffffffc) != (resp->gsup.message_type & 0xfffffffc))
|
||||
{
|
||||
/* Request, Result, Error, Other are encoded in the last 2 bits */
|
||||
DBG1(DBG_NET, "GSUP: received non matching Result. Requested %s but received %s",
|
||||
osmo_gsup_message_type_name(this->current_request->msg_type),
|
||||
osmo_gsup_message_type_name(resp->gsup.message_type));
|
||||
goto out;
|
||||
}
|
||||
if (!this->current_request)
|
||||
{
|
||||
DBG2(DBG_NET, "GSUP: received response when no request waiting %02x. This might came too late.", resp->gsup.message_type);
|
||||
this->mutex->unlock(this->mutex);
|
||||
goto out;
|
||||
}
|
||||
signal_request(this->current_request, resp);
|
||||
this->current_request = NULL;
|
||||
this->mutex->unlock(this->mutex);
|
||||
break;
|
||||
default:
|
||||
DBG1(DBG_NET, "GSUP received unknown message type %02x", resp->gsup.message_type);
|
||||
goto out;
|
||||
}
|
||||
free(pdu);
|
||||
return TRUE;
|
||||
|
||||
out:
|
||||
free(resp);
|
||||
free(pdu);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static int disconnect_gsup(private_osmo_epdg_gsup_client_t *this)
|
||||
{
|
||||
this->ipa->disconnect(this->ipa);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* TODO: worker thread which sends out enqueue'd message ! */
|
||||
static job_requeue_t queue_worker(private_osmo_epdg_gsup_client_t *this)
|
||||
{
|
||||
int ret;
|
||||
gsup_request_t *req;
|
||||
this->mutex->lock(this->mutex);
|
||||
if (this->current_request)
|
||||
{
|
||||
/* TODO: should we join the signal? */
|
||||
this->mutex->unlock(this->mutex);
|
||||
return JOB_REQUEUE_FAIR;
|
||||
}
|
||||
this->mutex->unlock(this->mutex);
|
||||
|
||||
/* TODO: replace pending with a thread safe queue, but non-blocking */
|
||||
req = this->pending->dequeue(this->pending);
|
||||
|
||||
this->mutex->lock(this->mutex);
|
||||
if (this->current_request)
|
||||
{
|
||||
/* TODO: how could this happen? */
|
||||
this->mutex->unlock(this->mutex);
|
||||
signal_request(req, NULL);
|
||||
return JOB_REQUEUE_FAIR;
|
||||
}
|
||||
this->current_request = req;
|
||||
this->mutex->unlock(this->mutex);
|
||||
|
||||
ret = this->ipa->send(this->ipa, IPAC_PROTO_EXT_GSUP, req->msg);
|
||||
req->msg = NULL;
|
||||
if (ret < 0)
|
||||
{
|
||||
/* TODO: disconnect & reconnect, but request is lost for now */
|
||||
/* TODO: wake up request */
|
||||
signal_request(req, NULL);
|
||||
}
|
||||
return JOB_REQUEUE_FAIR;
|
||||
}
|
||||
|
||||
osmo_epdg_gsup_client_t *osmo_epdg_gsup_client_create(char *uri)
|
||||
{
|
||||
private_osmo_epdg_gsup_client_t *this;
|
||||
DBG1(DBG_NET, "Starting osmo-epdg");
|
||||
|
||||
INIT(this,
|
||||
.public = {
|
||||
.send_auth_request = _send_auth_request,
|
||||
.update_location = _update_location,
|
||||
.tunnel_request = _tunnel_request,
|
||||
.destroy = _destroy,
|
||||
},
|
||||
.uri = strdup(uri),
|
||||
.pending = blocking_queue_create(),
|
||||
.current_request = NULL,
|
||||
.mutex = mutex_create(MUTEX_TYPE_DEFAULT),
|
||||
.ipa = osmo_epdg_ipa_client_create(uri),
|
||||
);
|
||||
this->ipa->on_recv(this->ipa, IPAC_PROTO_EXT_GSUP, on_recv_pdu, this);
|
||||
/* I would more like to have either an internal event which unblocks the queue. */
|
||||
/* src/libipsec/ipsec_event_relay.c */
|
||||
lib->processor->queue_job(lib->processor,
|
||||
(job_t*)callback_job_create_with_prio((callback_job_cb_t)queue_worker,
|
||||
this, NULL, (callback_job_cancel_t)return_false, JOB_PRIO_CRITICAL));
|
||||
return &this->public;
|
||||
}
|
|
@ -0,0 +1,85 @@
|
|||
/*
|
||||
* Copyright (C) 2023 sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* Author: Alexander Couzens <acouzens@sysmocom.de>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
// TODO: check license
|
||||
|
||||
/**
|
||||
* @defgroup osmo_epdg_gsup_client osmo_epdg_gsup_client
|
||||
* @{ @ingroup eap_simaka_sql
|
||||
*/
|
||||
|
||||
#ifndef OSMO_EPDG_GSUP_CLIENT_H_
|
||||
#define OSMO_EPDG_GSUP_CLIENT_H_
|
||||
|
||||
#include <utils/chunk.h>
|
||||
|
||||
#include <osmocom/gsm/gsup.h>
|
||||
|
||||
struct osmo_epdg_gsup_response_t {
|
||||
struct osmo_gsup_message gsup;
|
||||
};
|
||||
typedef struct osmo_epdg_gsup_response_t osmo_epdg_gsup_response_t;
|
||||
|
||||
typedef struct osmo_epdg_gsup_client_t osmo_epdg_gsup_client_t;
|
||||
|
||||
/**
|
||||
* foo
|
||||
*/
|
||||
struct osmo_epdg_gsup_client_t {
|
||||
/**
|
||||
* Send Authentication Info Request
|
||||
*
|
||||
* @param imsi IMSI encoded as human-readable string (IMSI MAX = 15)
|
||||
* @param auts (optional)
|
||||
* @param auts_rand (optional)
|
||||
* @return NULL or the osmo_epdg_gsup_response_t
|
||||
*/
|
||||
osmo_epdg_gsup_response_t *(*send_auth_request)(osmo_epdg_gsup_client_t *this,
|
||||
char *imsi, uint8_t cn_domain, chunk_t *auts, chunk_t *auts_rand);
|
||||
|
||||
/**
|
||||
* Update Location Request
|
||||
*
|
||||
* @return NULL or the osmo_gsup_message
|
||||
*/
|
||||
osmo_epdg_gsup_response_t *(*update_location)(osmo_epdg_gsup_client_t *this,
|
||||
char *imsi,
|
||||
uint8_t cn_domain);
|
||||
|
||||
/**
|
||||
* Tunnel Request
|
||||
*
|
||||
* @return NULL or the osmo_gsup_message
|
||||
*/
|
||||
osmo_epdg_gsup_response_t *(*tunnel_request)(osmo_epdg_gsup_client_t *this,
|
||||
char *imsi, char *apn);
|
||||
|
||||
/**
|
||||
* Destroy a osmo_epdg_gsup_client_t.
|
||||
*/
|
||||
void (*destroy)(osmo_epdg_gsup_client_t *this);
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a osmo_epdg_gsup_client instance.
|
||||
*
|
||||
* @param address the address of the gsup server */
|
||||
osmo_epdg_gsup_client_t *osmo_epdg_gsup_client_create(char *addr);
|
||||
|
||||
#endif /** OSMO_EPDG_GSUP_CLIENT_H_ @}*/
|
||||
|
|
@ -0,0 +1,398 @@
|
|||
/*
|
||||
* Copyright (C) 2023 sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* Author: Alexander Couzens <acouzens@sysmocom.de>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
// TODO: check license
|
||||
|
||||
#include <collections/enumerator.h>
|
||||
#include <collections/linked_list.h>
|
||||
#include <collections/blocking_queue.h>
|
||||
#include <threading/mutex.h>
|
||||
#include <threading/thread.h>
|
||||
#include <threading/condvar.h>
|
||||
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/gsm/protocol/ipaccess.h>
|
||||
#include <osmocom/gsm/ipa.h>
|
||||
|
||||
#include <library.h>
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#include "ipa_client.h"
|
||||
#include "osmo_epdg_utils.h"
|
||||
|
||||
typedef struct private_osmo_epdg_ipa_client_t private_osmo_epdg_ipa_client_t;
|
||||
struct private_osmo_epdg_ipa_client_t {
|
||||
/**
|
||||
* Public osmo_epdg_ipa_client_t
|
||||
*/
|
||||
osmo_epdg_ipa_client_t public;
|
||||
|
||||
char *uri;
|
||||
|
||||
stream_t *stream;
|
||||
|
||||
ipa_cb_t osmo_cb;
|
||||
void *osmo_cb_data;
|
||||
};
|
||||
|
||||
METHOD(osmo_epdg_ipa_client_t, on_error, int,
|
||||
private_osmo_epdg_ipa_client_t *this, uint8_t osmo_proto, ipa_cb_t cb, void *data)
|
||||
{
|
||||
/* TODO: on error ? */
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
METHOD(osmo_epdg_ipa_client_t, on_recv, int,
|
||||
private_osmo_epdg_ipa_client_t *this, uint8_t osmo_proto, ipa_cb_t cb, void *data)
|
||||
{
|
||||
/* TODO: protect it by mutex?! */
|
||||
this->osmo_cb = NULL;
|
||||
this->osmo_cb_data = data;
|
||||
this->osmo_cb = cb;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
METHOD(osmo_epdg_ipa_client_t, destroy, void,
|
||||
private_osmo_epdg_ipa_client_t *this)
|
||||
{
|
||||
free(this->uri);
|
||||
free(this);
|
||||
}
|
||||
|
||||
METHOD(osmo_epdg_ipa_client_t, disconnect, int,
|
||||
private_osmo_epdg_ipa_client_t *this)
|
||||
{
|
||||
this->stream->destroy(this->stream);
|
||||
this->stream = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
METHOD(osmo_epdg_ipa_client_t, send_pdu, ssize_t,
|
||||
private_osmo_epdg_ipa_client_t *this, uint8_t osmo_proto, struct msgb *msg)
|
||||
{
|
||||
struct ipaccess_head *head;
|
||||
int len;
|
||||
head = (struct ipaccess_head *) msgb_push(msg, sizeof(struct ipaccess_head) + 1);
|
||||
head->proto = IPAC_PROTO_OSMO;
|
||||
head->len = htons(msgb_length(msg) - (sizeof(struct ipaccess_head)));
|
||||
head->data[0] = osmo_proto;
|
||||
len = msgb_length(msg);
|
||||
if (!this->stream->write_all(this->stream, msgb_data(msg), msgb_length(msg)))
|
||||
{
|
||||
// TODO: write error
|
||||
free(msg);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
free(msg);
|
||||
return len;
|
||||
}
|
||||
|
||||
static bool read_error(private_osmo_epdg_ipa_client_t *this, int read_err)
|
||||
{
|
||||
DBG1(DBG_NET, "IPA client failed to read with %d. Disconnecting", read_err);
|
||||
this->public.disconnect(&this->public);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* send a simple IPA PDU with the base protocol */
|
||||
static void ipa_pdu_base_send_simple(private_osmo_epdg_ipa_client_t *this, uint8_t msg_type)
|
||||
{
|
||||
struct ipaccess_head *head = calloc(1, sizeof(*head) + 1);
|
||||
head->proto = IPAC_PROTO_IPACCESS;
|
||||
head->len = htons(1);
|
||||
head->data[0] = msg_type;
|
||||
|
||||
if (!this->stream->write_all(this->stream, head, sizeof(*head) + 1))
|
||||
{
|
||||
/* TODO: write error */
|
||||
}
|
||||
|
||||
free(head);
|
||||
return;
|
||||
}
|
||||
|
||||
static inline void ipa_tag_put_str(struct msgb *resp, uint8_t tag, const char *value)
|
||||
{
|
||||
char *buf;
|
||||
ssize_t len = strlen(value);
|
||||
|
||||
msgb_put_u16(resp, len + 1);
|
||||
msgb_put_u8(resp, tag);
|
||||
buf = msgb_put(resp, len);
|
||||
memcpy(buf, value, len);
|
||||
}
|
||||
|
||||
static void ipa_resp_tag_encode(private_osmo_epdg_ipa_client_t *this, struct msgb *resp, uint8_t tag)
|
||||
{
|
||||
switch (tag)
|
||||
{
|
||||
case IPAC_IDTAG_SERNR:
|
||||
case IPAC_IDTAG_UNITNAME:
|
||||
ipa_tag_put_str(resp, tag, "SWAN-00-00-00-00-00-00");
|
||||
break;
|
||||
case IPAC_IDTAG_LOCATION1:
|
||||
case IPAC_IDTAG_LOCATION2:
|
||||
case IPAC_IDTAG_EQUIPVERS:
|
||||
case IPAC_IDTAG_SWVERSION:
|
||||
ipa_tag_put_str(resp, tag, "00:00:00:00:00:00");
|
||||
break;
|
||||
case IPAC_IDTAG_UNIT:
|
||||
ipa_tag_put_str(resp, tag, "0/0/0");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void protocol_error(private_osmo_epdg_ipa_client_t *this, char *error_msg)
|
||||
{
|
||||
/* TODO: protocol error */
|
||||
DBG1(DBG_NET, error_msg);
|
||||
return;
|
||||
}
|
||||
/* send a IPA PDU (base protocol) ID Response message */
|
||||
static void ipa_pdu_base_send_id_resp(private_osmo_epdg_ipa_client_t *this, struct msgb *req)
|
||||
{
|
||||
struct msgb *resp;
|
||||
struct ipaccess_head *resp_head;
|
||||
|
||||
chunk_t resp_pdu = chunk_alloc(IPA_ALLOC_SIZE);
|
||||
if (!resp_pdu.ptr)
|
||||
{
|
||||
/* TODO: alloc err */
|
||||
return;
|
||||
}
|
||||
|
||||
resp = chunk_to_msgb(&resp_pdu);
|
||||
|
||||
/* remove the ipaccess header so we can use msg_pull on the message */
|
||||
msgb_pull(req, sizeof(struct ipaccess_head));
|
||||
if (msgb_length(req) < 1)
|
||||
{
|
||||
protocol_error(this, "Invalid IPA ID Request message.");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* prepare our response message */
|
||||
msgb_reserve(resp, 128);
|
||||
resp_head = (struct ipaccess_head *) msgb_put(resp, sizeof(struct ipaccess_head));
|
||||
resp->l1h = (void *)resp_head;
|
||||
resp->l2h = resp->tail;
|
||||
resp_head->proto = IPAC_PROTO_IPACCESS;
|
||||
msgb_put_u8(resp, IPAC_MSGT_ID_RESP);
|
||||
/* remove IPA message type */
|
||||
msgb_pull_u8(req);
|
||||
|
||||
/* ID Request contains: a list of [0x1, tag] */
|
||||
while (msgb_length(req) >= 2)
|
||||
{
|
||||
uint8_t len = msgb_pull_u8(req);
|
||||
if (len != 1)
|
||||
{
|
||||
if (msgb_length(req) < len)
|
||||
{
|
||||
protocol_error(this, "Invalid IPA ID Request message");
|
||||
goto out;
|
||||
}
|
||||
/* ignoring this requested LValue */
|
||||
DBG1(DBG_NET, "IPA ignoring IPA ID Request tag with size != 1");
|
||||
msgb_pull(req, len);
|
||||
continue;
|
||||
}
|
||||
uint8_t tag = msgb_pull_u8(req);
|
||||
ipa_resp_tag_encode(this, resp, tag);
|
||||
}
|
||||
resp_head->len = htons(msgb_l2len(resp));
|
||||
if (!this->stream->write_all(this->stream, msgb_l1(resp), msgb_l1len(resp)))
|
||||
{
|
||||
// TODO: write error
|
||||
return;
|
||||
}
|
||||
|
||||
ipa_pdu_base_send_simple(this, IPAC_MSGT_ID_ACK);
|
||||
out:
|
||||
chunk_free(&resp_pdu);
|
||||
return;
|
||||
}
|
||||
|
||||
static void on_recv_ipa_pdu(private_osmo_epdg_ipa_client_t *this, struct msgb *pdu)
|
||||
{
|
||||
if (msgb_length(pdu) < sizeof(struct ipaccess_head) + 1)
|
||||
{
|
||||
/* TODO: invalid package */
|
||||
return;
|
||||
}
|
||||
|
||||
struct ipaccess_head *head = (struct ipaccess_head *) msgb_data(pdu);
|
||||
uint8_t msg_type = head->data[0];
|
||||
switch (msg_type)
|
||||
{
|
||||
case IPAC_MSGT_PING:
|
||||
ipa_pdu_base_send_simple(this, IPAC_MSGT_PONG);
|
||||
break;
|
||||
case IPAC_MSGT_PONG:
|
||||
/* ignore. We don't implement an own PING/PONG timer */
|
||||
break;
|
||||
case IPAC_MSGT_ID_GET:
|
||||
ipa_pdu_base_send_id_resp(this, pdu);
|
||||
break;
|
||||
case IPAC_MSGT_ID_ACK:
|
||||
/* ignore. An ACK means everything the ID got accepted */
|
||||
break;
|
||||
default:
|
||||
DBG1(DBG_NET, "IPA client Received an unknown IPA PDU %02x", msg_type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
CALLBACK(on_stream_read, bool,
|
||||
private_osmo_epdg_ipa_client_t *this, stream_t *stream)
|
||||
{
|
||||
uint16_t len;
|
||||
ssize_t hlen;
|
||||
chunk_t req_chunk;
|
||||
struct ipaccess_head head;
|
||||
struct msgb *req;
|
||||
|
||||
DBG2(DBG_NET, "on stream read!");
|
||||
hlen = stream->read(stream, &head, sizeof(head), FALSE);
|
||||
if (hlen <= 0)
|
||||
{
|
||||
if (errno == EWOULDBLOCK)
|
||||
{
|
||||
DBG2(DBG_NET, "on stream read EWOULDBLOCK!");
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
DBG2(DBG_NET, "on stream errno not EWOULDBLOCK %d!", hlen);
|
||||
return read_error(this, errno);
|
||||
}
|
||||
DBG2(DBG_NET, "on stream hlen %d!", hlen);
|
||||
if (hlen < sizeof(head))
|
||||
{
|
||||
if (!stream->read_all(stream, ((void*)&head) + hlen, sizeof(head) - hlen))
|
||||
{
|
||||
return read_error(this, errno);
|
||||
}
|
||||
}
|
||||
len = ntohs(head.len);
|
||||
if ((len + sizeof(head)) > IPA_ALLOC_SIZE)
|
||||
{
|
||||
/* TODO: pkg too big */
|
||||
return read_error(this, EINVAL);
|
||||
}
|
||||
|
||||
req_chunk = chunk_alloc(IPA_ALLOC_SIZE + sizeof(struct msgb));
|
||||
if (!req_chunk.ptr)
|
||||
{
|
||||
/* TODO: -ENOMEM; */
|
||||
return FALSE;
|
||||
}
|
||||
req = chunk_to_msgb(&req_chunk);
|
||||
memcpy(msgb_put(req, sizeof(head)), &head, sizeof(head));
|
||||
/* TODO: either wait here with a timeout or don't care on this stream read */
|
||||
if (!stream->read_all(stream, msgb_put(req, len), len))
|
||||
{
|
||||
chunk_free(&req_chunk);
|
||||
return read_error(this, errno);
|
||||
}
|
||||
|
||||
switch (head.proto)
|
||||
{
|
||||
case IPAC_PROTO_IPACCESS:
|
||||
on_recv_ipa_pdu(this, req);
|
||||
break;
|
||||
case IPAC_PROTO_OSMO:
|
||||
/* will take care of the response */
|
||||
if (msgb_length(req) < sizeof(struct ipaccess_head) + 1)
|
||||
{
|
||||
/* TODO: inval pdu */
|
||||
chunk_free(&req_chunk);
|
||||
break;
|
||||
}
|
||||
req->l1h = req->head;
|
||||
req->l2h = req->l1h + sizeof(struct ipaccess_head) + 1;
|
||||
DBG2(DBG_NET, "IPA client: pushing osmo pdu");
|
||||
if (this->osmo_cb)
|
||||
{
|
||||
this->osmo_cb(this->osmo_cb_data, &this->public, req);
|
||||
}
|
||||
else
|
||||
{
|
||||
chunk_free(&req_chunk);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
DBG1(DBG_NET, "IPA client: ignoring unknown proto %02x", head.proto);
|
||||
chunk_free(&req_chunk);
|
||||
break;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static int connect_ipa(private_osmo_epdg_ipa_client_t *this)
|
||||
{
|
||||
|
||||
DBG1(DBG_NET, "IPA client connecting to %s", this->uri);
|
||||
if (this->stream != NULL)
|
||||
{
|
||||
DBG1(DBG_NET, "closing old ipa conncetion %s", this->uri);
|
||||
this->public.disconnect(&this->public);
|
||||
}
|
||||
|
||||
this->stream = lib->streams->connect(lib->streams, this->uri);
|
||||
if (!this->stream)
|
||||
{
|
||||
/* TODO: failed to connect */
|
||||
/* TODO: re-schedule the connect */
|
||||
DBG1(DBG_NET, "failed to connect the ipa %s", this->uri);
|
||||
return -EINVAL;
|
||||
}
|
||||
DBG1(DBG_NET, "IPA client connected");
|
||||
|
||||
/* TODO: check if we need this
|
||||
* ensure we get the first bytes after the connect
|
||||
* on_stream_read(this, this->stream); */
|
||||
|
||||
/* TODO: can a race happen here when data arrives between those 2 calls? */
|
||||
this->stream->on_read(this->stream, on_stream_read, this);
|
||||
on_stream_read(this, this->stream);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
osmo_epdg_ipa_client_t *osmo_epdg_ipa_client_create(char *uri)
|
||||
{
|
||||
private_osmo_epdg_ipa_client_t *this;
|
||||
|
||||
INIT(this,
|
||||
.public = {
|
||||
.on_recv = _on_recv,
|
||||
.on_error = _on_error,
|
||||
.send = _send_pdu,
|
||||
.disconnect = _disconnect,
|
||||
.destroy = _destroy,
|
||||
},
|
||||
.uri = strdup(uri),
|
||||
.stream = NULL,
|
||||
);
|
||||
connect_ipa(this);
|
||||
return &this->public;
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
* Copyright (C) 2023 sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* Author: Alexander Couzens <acouzens@sysmocom.de>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
// TODO: check license
|
||||
|
||||
/**
|
||||
* @defgroup osmo_epdg_ipa_client osmo_epdg_ipa_client
|
||||
* @{ @ingroup eap_simaka_sql
|
||||
*/
|
||||
|
||||
#ifndef OSMO_EPDG_IPA_CLIENT_H_
|
||||
#define OSMO_EPDG_IPA_CLIENT_H_
|
||||
|
||||
#include <utils/chunk.h>
|
||||
|
||||
struct msgb;
|
||||
typedef struct osmo_epdg_ipa_client_t osmo_epdg_ipa_client_t;
|
||||
typedef bool (*ipa_cb_t)(void *data, osmo_epdg_ipa_client_t *client, struct msgb *pdu);
|
||||
|
||||
/**
|
||||
* IP Access Protocol
|
||||
*/
|
||||
struct osmo_epdg_ipa_client_t {
|
||||
/**
|
||||
* Register a callback to invoke when a IPA PDU of proto arrived.
|
||||
*
|
||||
* Only called when a full PDU has arrived.
|
||||
*
|
||||
* @param cb callback function, NULL to unregister
|
||||
* @param data data to pass to callback
|
||||
*/
|
||||
int (*on_recv)(osmo_epdg_ipa_client_t *this, uint8_t osmo_proto, ipa_cb_t cb, void *data);
|
||||
|
||||
/**
|
||||
* Send a PDU over the IPA connection
|
||||
*
|
||||
* @param proto define the protocol
|
||||
* @param buf data buffer to write
|
||||
* @param len number of bytes to write
|
||||
* @return number of bytes written, -1 on error
|
||||
*/
|
||||
ssize_t (*send)(osmo_epdg_ipa_client_t *this, uint8_t osmo_proto, struct msgb *msg);
|
||||
|
||||
int (*on_error)(osmo_epdg_ipa_client_t *this, uint8_t osmo_proto, ipa_cb_t cb, void *data);
|
||||
|
||||
// TODO: unsure if we need this int (*connect)(osmo_epdg_ipa_client_t *this);
|
||||
int (*disconnect)(osmo_epdg_ipa_client_t *this);
|
||||
/**
|
||||
* Destroy a osmo_epdg_ipa_client_t.
|
||||
*/
|
||||
void (*destroy)(osmo_epdg_ipa_client_t *this);
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a osmo_epdg_ipa_client instance.
|
||||
*
|
||||
* @param address the address of the gsup server */
|
||||
osmo_epdg_ipa_client_t *osmo_epdg_ipa_client_create(char *addr);
|
||||
|
||||
#endif /** OSMO_EPDG_IPA_CLIENT_H_ @}*/
|
||||
|
|
@ -0,0 +1,129 @@
|
|||
/*
|
||||
* Copyright (C) 2023 sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* Author: Alexander Couzens <acouzens@sysmocom.de>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <daemon.h>
|
||||
#include <plugins/plugin.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "osmo_epdg_plugin.h"
|
||||
#include "osmo_epdg_db.h"
|
||||
#include "osmo_epdg_utils.h"
|
||||
|
||||
typedef struct private_osmo_epdg_db_t private_osmo_epdg_db_t;
|
||||
|
||||
/**
|
||||
* Private data of an osmo_epdg_db_t object.
|
||||
*/
|
||||
struct private_osmo_epdg_db_t {
|
||||
/**
|
||||
* Public osmo_epdg_db_t interface.
|
||||
*/
|
||||
osmo_epdg_db_t public;
|
||||
|
||||
/**
|
||||
* GSUP client
|
||||
*/
|
||||
osmo_epdg_gsup_client_t *gsup;
|
||||
|
||||
/**
|
||||
* subscriber hash by ID
|
||||
*/
|
||||
hashtable_t *subscribers;
|
||||
|
||||
/**
|
||||
* subscriber hash by imsi (how to handle multiple?)
|
||||
*/
|
||||
hashtable_t *subscribers_imsi;
|
||||
|
||||
/**
|
||||
* subscriber by ike_sa
|
||||
*/
|
||||
hashtable_t *subscribers_ike_sa_t;
|
||||
};
|
||||
|
||||
METHOD(osmo_epdg_db_t, create_subscriber_imsi, osmo_epdg_ue_t *,
|
||||
private_osmo_epdg_db_t *this, ike_sa_t *ike_sa,
|
||||
char *imsi)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
METHOD(osmo_epdg_db_t, get_subscriber_imsi, osmo_epdg_ue_t *,
|
||||
private_osmo_epdg_db_t *this, char *imsi, int offset)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
METHOD(osmo_epdg_db_t, get_subscriber_id, osmo_epdg_ue_t *,
|
||||
private_osmo_epdg_db_t *this, uint32_t id)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
METHOD(osmo_epdg_db_t, get_subscriber_ike, osmo_epdg_ue_t *,
|
||||
private_osmo_epdg_db_t *this, ike_sa_t *ike_sa)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
METHOD(osmo_epdg_db_t, destroy_subscriber_id, void,
|
||||
private_osmo_epdg_db_t *this, uint32_t id)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
METHOD(osmo_epdg_db_t, destroy_subscriber_ike, void,
|
||||
private_osmo_epdg_db_t *this, ike_sa_t *ike_sa)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
METHOD(osmo_epdg_db_t, destroy_subscriber, void,
|
||||
private_osmo_epdg_db_t *this)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
METHOD(osmo_epdg_db_t, destroy, void,
|
||||
private_osmo_epdg_db_t *this)
|
||||
{
|
||||
free(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* See header
|
||||
*/
|
||||
osmo_epdg_db_t *osmo_epdg_db_create(osmo_epdg_gsup_client_t *gsup)
|
||||
{
|
||||
private_osmo_epdg_db_t *this;
|
||||
|
||||
INIT(this,
|
||||
.public = {
|
||||
.create_subscriber = _create_subscriber_imsi,
|
||||
.get_subscriber_id = _get_subscriber_id,
|
||||
.get_subscriber_imsi = _get_subscriber_imsi,
|
||||
.get_subscriber_ike = _get_subscriber_ike,
|
||||
.destroy_subscriber_ike = _destroy_subscriber_ike,
|
||||
.destroy_subscriber_id = _destroy_subscriber_id,
|
||||
.destroy_subscriber = _destroy_subscriber,
|
||||
.destroy = _destroy,
|
||||
},
|
||||
);
|
||||
|
||||
return &this->public;
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
/*
|
||||
* Copyright (C) 2023 sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* Author: Alexander Couzens <acouzens@sysmocom.de>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
// TODO: check license
|
||||
|
||||
/**
|
||||
* @defgroup osmo_epdg_db osmo_epdg_db
|
||||
* @{ @ingroup osmo_epdg
|
||||
*/
|
||||
|
||||
#ifndef OSMO_EPDG_LISTENER_H_
|
||||
#define OSMO_EPDG_LISTENER_H_
|
||||
|
||||
#include <bus/listeners/listener.h>
|
||||
#include "gsup_client.h"
|
||||
|
||||
typedef struct osmo_epdg_db_t osmo_epdg_db_t;
|
||||
|
||||
/**
|
||||
* SIM listener implementation using a set of AKA functions.
|
||||
*/
|
||||
struct osmo_epdg_db_t {
|
||||
/**
|
||||
* Create new subscriber by imsi, before sending authentication
|
||||
*/
|
||||
osmo_epdg_ue_t *(*create_subscriber)(osmo_epdg_db_t *this, ike_sa_t *ike_sa, char *imsi);
|
||||
|
||||
/**
|
||||
* Get subscriber by imsi, there might be multiple UE by this IMSI
|
||||
*/
|
||||
osmo_epdg_ue_t *(*get_subscriber_imsi)(osmo_epdg_db_t *this, char *imsi, int offset);
|
||||
|
||||
/**
|
||||
* Get subscriber by ike
|
||||
*/
|
||||
osmo_epdg_ue_t *(*get_subscriber_ike)(osmo_epdg_db_t *this, ike_sa_t *ike_sa);
|
||||
|
||||
/**
|
||||
* Get subscriber by id
|
||||
*/
|
||||
osmo_epdg_ue_t *(*get_subscriber_id)(osmo_epdg_db_t *this, uint32_t id);
|
||||
|
||||
/**
|
||||
* Destroy subscriber by imsi
|
||||
*/
|
||||
void (*destroy_subscriber_ike)(osmo_epdg_db_t *this, ike_sa_t *ike_sa);
|
||||
|
||||
/**
|
||||
* Destroy subscriber by imsi
|
||||
*/
|
||||
void (*destroy_subscriber_id)(osmo_epdg_db_t *this, uint32_t id);
|
||||
|
||||
/**
|
||||
* Destroy subscriber by object
|
||||
*/
|
||||
void (*destroy_subscriber)(osmo_epdg_db_t *this, osmo_epdg_ue_t *ue);
|
||||
|
||||
/**
|
||||
* Destroy a osmo_epdg_db_t.
|
||||
*/
|
||||
void (*destroy)(osmo_epdg_db_t *this);
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a osmo_epdg_db instance.
|
||||
*/
|
||||
osmo_epdg_db_t *osmo_epdg_db_create(osmo_epdg_gsup_client_t *gsup);
|
||||
|
||||
#endif /** OSMO_EPDG_LISTENER_H_ @}*/
|
|
@ -0,0 +1,176 @@
|
|||
/*
|
||||
* Copyright (C) 2023 sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* Author: Alexander Couzens <acouzens@sysmocom.de>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
// TODO: check license
|
||||
|
||||
#include <daemon.h>
|
||||
#include <plugins/plugin.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <osmocom/gsm/apn.h>
|
||||
|
||||
#include "osmo_epdg_plugin.h"
|
||||
#include "osmo_epdg_listener.h"
|
||||
#include "osmo_epdg_utils.h"
|
||||
|
||||
typedef struct private_osmo_epdg_listener_t private_osmo_epdg_listener_t;
|
||||
|
||||
/**
|
||||
* Private data of an osmo_epdg_listener_t object.
|
||||
*/
|
||||
struct private_osmo_epdg_listener_t {
|
||||
/**
|
||||
* Public osmo_epdg_listener_t interface.
|
||||
*/
|
||||
osmo_epdg_listener_t public;
|
||||
|
||||
osmo_epdg_gsup_client_t *gsup;
|
||||
};
|
||||
|
||||
METHOD(listener_t, eap_authorize, bool,
|
||||
private_osmo_epdg_listener_t *this, ike_sa_t *ike_sa,
|
||||
identification_t *id, bool final, bool *success)
|
||||
{
|
||||
char imsi[16] = {0};
|
||||
|
||||
if (!id)
|
||||
{
|
||||
DBG1(DBG_NET, "epdg: authorize: no id given. Failing.");
|
||||
goto err;
|
||||
}
|
||||
if (get_imsi(id, imsi, sizeof(imsi) - 1))
|
||||
{
|
||||
DBG1(DBG_NET, "epdg: authorize: Can't find IMSI in EAP identity.");
|
||||
goto err;
|
||||
}
|
||||
|
||||
osmo_epdg_gsup_response_t *resp = this->gsup->update_location(this->gsup, imsi, OSMO_GSUP_CN_DOMAIN_PS);
|
||||
if (!resp)
|
||||
{
|
||||
DBG1(DBG_NET, "epdg: GSUP: couldn't send Update Location.");
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* TODO: validate if this APN is allowed! */
|
||||
|
||||
if (resp->gsup.message_type != OSMO_GSUP_MSGT_UPDATE_LOCATION_RESULT)
|
||||
{
|
||||
DBG1(DBG_NET, "epdg_listener: Update Location Error! Cause: %02x", resp->gsup.cause);
|
||||
goto err;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
|
||||
err:
|
||||
*success = FALSE;
|
||||
/* keep still subscribed */
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
METHOD(listener_t, authorize, bool,
|
||||
private_osmo_epdg_listener_t *this, ike_sa_t *ike_sa,
|
||||
bool final, bool *success)
|
||||
{
|
||||
int ret;
|
||||
identification_t* imsi_id;
|
||||
char apn[APN_MAXLEN];
|
||||
char imsi[16] = {0};
|
||||
DBG1(DBG_NET, "Authorized: uniq 0x%08x, name %s final: %d, eap: %d!",
|
||||
ike_sa->get_unique_id(ike_sa),
|
||||
ike_sa->get_name(ike_sa),
|
||||
final,
|
||||
ike_sa->has_condition(ike_sa, COND_EAP_AUTHENTICATED));
|
||||
|
||||
if (!final)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
imsi_id = ike_sa->get_other_id(ike_sa);
|
||||
if (!imsi_id)
|
||||
{
|
||||
DBG1(DBG_NET, "epdg: authorize: Can't get EAP identity.");
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (get_imsi(imsi_id, imsi, sizeof(imsi) - 1))
|
||||
{
|
||||
DBG1(DBG_NET, "epdg: authorize: Can't find IMSI in EAP identity.");
|
||||
goto err;
|
||||
}
|
||||
|
||||
apn[0] = 0;
|
||||
ret = get_apn(ike_sa, apn, APN_MAXLEN);
|
||||
if (!ret && ret != -EINVAL)
|
||||
{
|
||||
DBG1(DBG_NET, "epdg_listener: Tunnel Request: Couldn't get APN!");
|
||||
goto err;
|
||||
}
|
||||
|
||||
osmo_epdg_gsup_response_t *resp = this->gsup->tunnel_request(this->gsup, imsi, apn, strlen(apn));
|
||||
if (!resp)
|
||||
{
|
||||
DBG1(DBG_NET, "epdg_listener: Tunnel Request: GSUP: couldn't send.");
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (resp->gsup.message_type == OSMO_GSUP_MSGT_EPDG_TUNNEL_ERROR)
|
||||
{
|
||||
DBG1(DBG_NET, "epdg_listener: Tunnel Error! Cause: %02x", resp->gsup.cause);
|
||||
goto err;
|
||||
}
|
||||
else if (resp->gsup.message_type != OSMO_GSUP_MSGT_EPDG_TUNNEL_RESULT)
|
||||
{
|
||||
DBG1(DBG_NET, "epdg_listener: Tunnel Response: unexpected message type: %02x", resp->gsup.message_type);
|
||||
goto err;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
err:
|
||||
*success = FALSE;
|
||||
/* keep still subscribed */
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
METHOD(osmo_epdg_listener_t, destroy, void,
|
||||
private_osmo_epdg_listener_t *this)
|
||||
{
|
||||
free(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* See header
|
||||
*/
|
||||
osmo_epdg_listener_t *osmo_epdg_listener_create(osmo_epdg_gsup_client_t *gsup)
|
||||
{
|
||||
private_osmo_epdg_listener_t *this;
|
||||
|
||||
INIT(this,
|
||||
.public = {
|
||||
.listener = {
|
||||
.authorize = _authorize,
|
||||
.eap_authorize = _eap_authorize,
|
||||
},
|
||||
.destroy = _destroy,
|
||||
},
|
||||
.gsup = gsup,
|
||||
);
|
||||
|
||||
return &this->public;
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* Copyright (C) 2023 sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* Author: Alexander Couzens <acouzens@sysmocom.de>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
// TODO: check license
|
||||
|
||||
/**
|
||||
* @defgroup osmo_epdg_listener osmo_epdg_listener
|
||||
* @{ @ingroup osmo_epdg
|
||||
*/
|
||||
|
||||
#ifndef OSMO_EPDG_LISTENER_H_
|
||||
#define OSMO_EPDG_LISTENER_H_
|
||||
|
||||
#include <bus/listeners/listener.h>
|
||||
#include "gsup_client.h"
|
||||
|
||||
typedef struct osmo_epdg_listener_t osmo_epdg_listener_t;
|
||||
|
||||
/**
|
||||
* SIM listener implementation using a set of AKA functions.
|
||||
*/
|
||||
struct osmo_epdg_listener_t {
|
||||
|
||||
/**
|
||||
* Implements listener_t interface.
|
||||
*/
|
||||
listener_t listener;
|
||||
|
||||
/**
|
||||
* Destroy a osmo_epdg_listener_t.
|
||||
*/
|
||||
void (*destroy)(osmo_epdg_listener_t *this);
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a osmo_epdg_listener instance.
|
||||
*/
|
||||
osmo_epdg_listener_t *osmo_epdg_listener_create(osmo_epdg_gsup_client_t *gsup);
|
||||
|
||||
#endif /** OSMO_EPDG_LISTENER_H_ @}*/
|
|
@ -0,0 +1,120 @@
|
|||
/*
|
||||
* Copyright (C) 2023 sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* Author: Alexander Couzens <acouzens@sysmocom.de>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
// TODO: check license
|
||||
|
||||
#include <daemon.h>
|
||||
#include <plugins/plugin.h>
|
||||
|
||||
#include "osmo_epdg_plugin.h"
|
||||
#include "osmo_epdg_provider.h"
|
||||
#include "osmo_epdg_listener.h"
|
||||
|
||||
typedef struct private_osmo_epdg_t private_osmo_epdg_t;
|
||||
|
||||
/**
|
||||
* Private data of an eap_osmo_epdg_t object.
|
||||
*/
|
||||
struct private_osmo_epdg_t {
|
||||
/**
|
||||
* Public osmo_epdg_plugin_t interface.
|
||||
*/
|
||||
osmo_epdg_plugin_t public;
|
||||
|
||||
/**
|
||||
* SIM AKA provider
|
||||
*/
|
||||
osmo_epdg_provider_t *provider;
|
||||
|
||||
osmo_epdg_listener_t *listener;
|
||||
};
|
||||
|
||||
METHOD(plugin_t, get_name, char*,
|
||||
private_osmo_epdg_t *this)
|
||||
{
|
||||
return "osmo-epdg";
|
||||
}
|
||||
|
||||
static bool register_functions(private_osmo_epdg_t *this,
|
||||
plugin_feature_t *feature, bool reg, void *data)
|
||||
{
|
||||
if (reg)
|
||||
{
|
||||
osmo_epdg_gsup_client_t *gsup = osmo_epdg_gsup_client_create("tcp://127.0.0.1:4222");
|
||||
this->provider = osmo_epdg_provider_create(gsup);
|
||||
this->listener = osmo_epdg_listener_create(gsup);
|
||||
charon->bus->add_listener(charon->bus, &this->listener->listener);
|
||||
return TRUE;
|
||||
}
|
||||
if (this->listener)
|
||||
{
|
||||
charon->bus->remove_listener(charon->bus, &this->listener->listener);
|
||||
}
|
||||
this->provider->destroy(this->provider);
|
||||
this->provider = NULL;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback providing our provider to register
|
||||
*/
|
||||
static simaka_provider_t* get_provider(private_osmo_epdg_t *this)
|
||||
{
|
||||
return &this->provider->provider;
|
||||
}
|
||||
|
||||
METHOD(plugin_t, get_features, int,
|
||||
private_osmo_epdg_t *this, plugin_feature_t *features[])
|
||||
{
|
||||
static plugin_feature_t f[] = {
|
||||
PLUGIN_CALLBACK((void*)register_functions, NULL),
|
||||
PLUGIN_PROVIDE(CUSTOM, "osmo-epdg"),
|
||||
PLUGIN_CALLBACK(simaka_manager_register, get_provider),
|
||||
PLUGIN_PROVIDE(CUSTOM, "aka-provider"),
|
||||
PLUGIN_DEPENDS(CUSTOM, "aka-manager"),
|
||||
};
|
||||
*features = f;
|
||||
return countof(f);
|
||||
}
|
||||
|
||||
METHOD(plugin_t, destroy, void,
|
||||
private_osmo_epdg_t *this)
|
||||
{
|
||||
free(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* See header
|
||||
*/
|
||||
plugin_t *osmo_epdg_plugin_create()
|
||||
{
|
||||
private_osmo_epdg_t *this;
|
||||
|
||||
INIT(this,
|
||||
.public = {
|
||||
.plugin = {
|
||||
.get_name = _get_name,
|
||||
.get_features = _get_features,
|
||||
.destroy = _destroy,
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
return &this->public.plugin;
|
||||
}
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* Copyright (C) 2023 sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* Author: Alexander Couzens <acouzens@sysmocom.de>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
// TODO: check license
|
||||
|
||||
/**
|
||||
* @defgroup eap_osmo_epdg eap_aka_3gpp2
|
||||
* @ingroup cplugins
|
||||
*
|
||||
* @defgroup eap_osmo_epdg_plugin eap_aka_3gpp2_plugin
|
||||
* @{ @ingroup eap_osmo_epdg
|
||||
*/
|
||||
|
||||
#ifndef OSMO_EPDG_PLUGIN_H_
|
||||
#define OSMO_EPDG_PLUGIN_H_
|
||||
|
||||
#include <plugins/plugin.h>
|
||||
|
||||
typedef struct osmo_epdg_plugin_t osmo_epdg_plugin_t;
|
||||
|
||||
struct osmo_epdg_plugin_t {
|
||||
|
||||
/**
|
||||
* implements plugin interface
|
||||
*/
|
||||
plugin_t plugin;
|
||||
};
|
||||
|
||||
#endif /** OSMO_EPDG_PLUGIN_H_ @}*/
|
|
@ -0,0 +1,153 @@
|
|||
/*
|
||||
* Copyright (C) 2023 sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* Author: Alexander Couzens <acouzens@sysmocom.de>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "osmo_epdg_provider.h"
|
||||
#include "osmo_epdg_utils.h"
|
||||
#include "gsup_client.h"
|
||||
|
||||
#include <daemon.h>
|
||||
#include <credentials/keys/shared_key.h>
|
||||
|
||||
#define AKA_SQN_LEN 6
|
||||
#define AKA_K_LEN 16
|
||||
#define AKA_OPC_LEN 16
|
||||
#define AKA_MAC_LEN 8
|
||||
#define AKA_AK_LEN 6
|
||||
#define AKA_AMF_LEN 2
|
||||
#define AKA_RES_LEN 8
|
||||
|
||||
typedef struct private_osmo_epdg_provider_t private_osmo_epdg_provider_t;
|
||||
|
||||
/**
|
||||
* Private data of an osmo_epdg_provider_t object.
|
||||
*/
|
||||
struct private_osmo_epdg_provider_t {
|
||||
|
||||
/**
|
||||
* Public osmo_epdg_provider_t interface.
|
||||
*/
|
||||
osmo_epdg_provider_t public;
|
||||
|
||||
osmo_epdg_gsup_client_t *gsup;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get a shared key K from the credential database
|
||||
*/
|
||||
bool osmo_epdg_get_k(identification_t *id, char k[AKA_K_LEN])
|
||||
{
|
||||
shared_key_t *shared;
|
||||
chunk_t key;
|
||||
|
||||
shared = lib->credmgr->get_shared(lib->credmgr, SHARED_EAP, id, NULL);
|
||||
if (shared == NULL)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
key = shared->get_key(shared);
|
||||
memset(k, '\0', AKA_K_LEN);
|
||||
memcpy(k, key.ptr, min(key.len, AKA_K_LEN));
|
||||
shared->destroy(shared);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
METHOD(simaka_provider_t, get_quintuplet, bool,
|
||||
private_osmo_epdg_provider_t *this, identification_t *id,
|
||||
char rand[AKA_RAND_LEN], char xres[AKA_RES_MAX], int *xres_len,
|
||||
char ck[AKA_CK_LEN], char ik[AKA_IK_LEN], char autn[AKA_AUTN_LEN])
|
||||
{
|
||||
char imsi[17] = {0};
|
||||
if (get_imsi(id, imsi, sizeof(imsi) - 1))
|
||||
{
|
||||
DBG1(DBG_NET, "epdg: get_quintuplet: Can't find IMSI in EAP identity.");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* TODO: check before if this is a null terminated string */
|
||||
osmo_epdg_gsup_response_t *resp = this->gsup->send_auth_request(
|
||||
this->gsup, imsi, OSMO_GSUP_CN_DOMAIN_PS, NULL, NULL);
|
||||
if (!resp)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (resp->gsup.message_type != OSMO_GSUP_MSGT_SEND_AUTH_INFO_RESULT)
|
||||
{
|
||||
DBG1(DBG_NET, "epdg_provider: SendAuthInfo Error! Cause: %02x", resp->gsup.cause);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
struct osmo_auth_vector *auth = &resp->gsup.auth_vectors[0];
|
||||
if (resp->gsup.num_auth_vectors == 0)
|
||||
{
|
||||
/* TODO: invalid auth data received */
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
memcpy(rand, auth->rand, AKA_RAND_LEN);
|
||||
memcpy(ck, auth->ck, AKA_CK_LEN);
|
||||
memcpy(ik, auth->ik, AKA_IK_LEN);
|
||||
memcpy(autn, auth->autn, AKA_AUTN_LEN);
|
||||
memcpy(xres, auth->res, auth->res_len);
|
||||
*xres_len = auth->res_len;
|
||||
|
||||
free(resp);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
METHOD(simaka_provider_t, resync, bool,
|
||||
private_osmo_epdg_provider_t *this, identification_t *id,
|
||||
char rand[AKA_RAND_LEN], char auts[AKA_AUTS_LEN])
|
||||
{
|
||||
/* TODO: invalid auth data received */
|
||||
/* prepare and fill up the struct */
|
||||
/* send pdu blocking */
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
METHOD(osmo_epdg_provider_t, destroy, void,
|
||||
private_osmo_epdg_provider_t *this)
|
||||
{
|
||||
free(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* See header
|
||||
*/
|
||||
osmo_epdg_provider_t *osmo_epdg_provider_create(osmo_epdg_gsup_client_t *gsup)
|
||||
{
|
||||
private_osmo_epdg_provider_t *this;
|
||||
|
||||
INIT(this,
|
||||
.public = {
|
||||
.provider = {
|
||||
.get_triplet = (void*)return_false,
|
||||
.get_quintuplet = _get_quintuplet,
|
||||
.resync = _resync,
|
||||
.is_pseudonym = (void*)return_null,
|
||||
.gen_pseudonym = (void*)return_null,
|
||||
.is_reauth = (void*)return_null,
|
||||
.gen_reauth = (void*)return_null,
|
||||
},
|
||||
.destroy = _destroy,
|
||||
},
|
||||
.gsup = gsup,
|
||||
);
|
||||
|
||||
return &this->public;
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* Copyright (C) 2023 sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* Author: Alexander Couzens <acouzens@sysmocom.de>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
// TODO: check license
|
||||
|
||||
/**
|
||||
* @defgroup osmo_epdg_listener osmo_epdg_listener
|
||||
* @{ @ingroup osmo_epdg
|
||||
*/
|
||||
|
||||
#ifndef OSMO_EPDG_PROVIDER_H_
|
||||
#define OSMO_EPDG_PROVIDER_H_
|
||||
|
||||
#include <simaka_provider.h>
|
||||
#include "gsup_client.h"
|
||||
|
||||
typedef struct osmo_epdg_provider_t osmo_epdg_provider_t;
|
||||
|
||||
/**
|
||||
* SIM provider implementation using a set of AKA functions.
|
||||
*/
|
||||
struct osmo_epdg_provider_t {
|
||||
|
||||
/**
|
||||
* Implements simaka_provider_t interface.
|
||||
*/
|
||||
simaka_provider_t provider;
|
||||
|
||||
/**
|
||||
* Destroy a osmo_epdg_provider_t.
|
||||
*/
|
||||
void (*destroy)(osmo_epdg_provider_t *this);
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a osmo_epdg_provider instance.
|
||||
*/
|
||||
osmo_epdg_provider_t *osmo_epdg_provider_create(osmo_epdg_gsup_client_t *gsup);
|
||||
|
||||
#endif /** OSMO_EPDG_PROVIDER_H_ @}*/
|
|
@ -0,0 +1,88 @@
|
|||
/*
|
||||
* Copyright (C) 2023 sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* Author: Alexander Couzens <acouzens@sysmocom.de>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <sa/ike_sa.h>
|
||||
#include <utils/utils.h>
|
||||
#include <utils/debug.h>
|
||||
|
||||
#include "osmo_epdg_utils.h"
|
||||
|
||||
struct msgb *chunk_to_msgb(chunk_t *chunk)
|
||||
{
|
||||
struct msgb *msg;
|
||||
if (chunk->len < sizeof(*msg))
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
msg = (struct msgb *) chunk->ptr;
|
||||
memset(msg, 0x00, sizeof(*msg));
|
||||
msg->data_len = chunk->len - sizeof(*msg);
|
||||
msg->len = 0;
|
||||
msg->data = msg->_data;
|
||||
msg->head = msg->_data;
|
||||
msg->tail = msg->_data;
|
||||
return msg;
|
||||
}
|
||||
|
||||
int get_imsi(identification_t *id, char *imsi, size_t imsi_len)
|
||||
{
|
||||
chunk_t nai = id->get_encoding(id);
|
||||
/* TODO: maybe use regex? */
|
||||
/* 099942123456789@mnc042.mcc999.3gpp... */
|
||||
if (nai.len < 17)
|
||||
{
|
||||
DBG1(DBG_NET, "epdg: Invalid NAI %s.", nai);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (nai.ptr[0] != '0')
|
||||
{
|
||||
DBG1(DBG_NET, "epdg: Invalid NAI. Only support IMSI (starting with 0). %s.",
|
||||
nai);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
strncpy(imsi, nai.ptr + 1, min(15, imsi_len));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int get_apn(ike_sa_t *sa, char *apn, size_t apn_len)
|
||||
{
|
||||
identification_t* apn_id;
|
||||
chunk_t apn_chunk;
|
||||
|
||||
apn_id = sa->get_my_id(sa);
|
||||
if (!apn_id)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
apn_chunk = apn_id->get_encoding(apn_id);
|
||||
if (apn_chunk.len >= apn_len)
|
||||
{
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
memcpy(apn, apn_chunk.ptr, apn_chunk.len);
|
||||
apn[apn_chunk_len] = 0;
|
||||
return -1;
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* Copyright (C) 2023 sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* Author: Alexander Couzens <acouzens@sysmocom.de>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <sa/ike_sa.h>
|
||||
#include <utils/chunk.h>
|
||||
#include <utils/identification.h>
|
||||
|
||||
|
||||
#define IPA_ALLOC_SIZE 1200
|
||||
|
||||
enum ue_state {
|
||||
/* Initial */
|
||||
UE_UNAUTHENTICATED,
|
||||
/* Authenticated */
|
||||
UE_AUTHENTICATED,
|
||||
/* Wait for GSUP Update Location Request */
|
||||
UE_WAIT_LOCATION_UPDATE,
|
||||
/* Wait for GSUP Tunnel Request */
|
||||
UE_WAIT_TUNNEL,
|
||||
/* Everything ready, data can flow */
|
||||
UE_CONNECTED,
|
||||
/* Notify the osmo-epdg about destruction, wait for an answer */
|
||||
UE_DISCONNECTING,
|
||||
UE_DESTROYED,
|
||||
};
|
||||
|
||||
/* TODO: how to clean up/garbage collect */
|
||||
struct osmo_epdg_ue {
|
||||
/* increasing uniq id */
|
||||
uint32_t id;
|
||||
/* imsi should be uniq, need protected against fake UE */
|
||||
char *imsi;
|
||||
enum ue_state state;
|
||||
|
||||
/* TODO: missing strongswan session pointer */
|
||||
ike_sa_t *ike_sa;
|
||||
};
|
||||
typedef struct osmo_epdg_ue osmo_epdg_ue_t;
|
||||
|
||||
struct msgb *chunk_to_msgb(chunk_t *chunk);
|
||||
int get_imsi(identification_t *id, char *imsi, size_t imsi_len);
|
||||
int get_apn(ike_sa_t *sa, char *apn, size_t apn_len);
|
|
@ -240,6 +240,32 @@ static void replace_eap_identity(private_eap_authenticator_t *this)
|
|||
cfg->add(cfg, AUTH_RULE_EAP_IDENTITY, eap_identity);
|
||||
}
|
||||
|
||||
static identification_t *get_identification(private_eap_authenticator_t *this)
|
||||
{
|
||||
identification_t *id;
|
||||
auth_cfg_t *auth = NULL;
|
||||
|
||||
if (this->eap_identity)
|
||||
{
|
||||
return this->eap_identity;
|
||||
}
|
||||
|
||||
if (this->method->get_auth)
|
||||
{
|
||||
auth = this->method->get_auth(this->method);
|
||||
if (auth)
|
||||
{
|
||||
id = auth->get(auth, AUTH_RULE_EAP_IDENTITY);
|
||||
if (id)
|
||||
{
|
||||
return id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle EAP exchange as server
|
||||
*/
|
||||
|
@ -320,6 +346,14 @@ static eap_payload_t* server_process_eap(private_eap_authenticator_t *this,
|
|||
DBG1(DBG_IKE, "EAP method %N succeeded, %sMSK established",
|
||||
eap_type_names, type, this->msk.ptr ? "" : "no ");
|
||||
}
|
||||
|
||||
if (!charon->bus->eap_authorize(charon->bus, get_identification(this), TRUE))
|
||||
{
|
||||
DBG1(DBG_IKE, "eap_authorization hook forbids EAP_SUCCESS, canceling");
|
||||
this->eap_complete = FALSE;
|
||||
return eap_payload_create_code(EAP_FAILURE, in->get_identifier(in));
|
||||
}
|
||||
|
||||
this->ike_sa->set_condition(this->ike_sa, COND_EAP_AUTHENTICATED,
|
||||
TRUE);
|
||||
this->eap_complete = TRUE;
|
||||
|
|
|
@ -51,6 +51,20 @@ struct private_blocking_queue_t {
|
|||
|
||||
};
|
||||
|
||||
METHOD(blocking_queue_t, remove_, void*,
|
||||
private_blocking_queue_t *this, void *item)
|
||||
{
|
||||
int removed = 0;
|
||||
this->mutex->lock(this->mutex);
|
||||
removed = this->list->remove(this->list, item, NULL);
|
||||
this->mutex->unlock(this->mutex);
|
||||
|
||||
if (removed)
|
||||
return item;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
METHOD(blocking_queue_t, enqueue, void,
|
||||
private_blocking_queue_t *this, void *item)
|
||||
{
|
||||
|
@ -115,6 +129,7 @@ blocking_queue_t *blocking_queue_create()
|
|||
.public = {
|
||||
.enqueue = _enqueue,
|
||||
.dequeue = _dequeue,
|
||||
.remove = _remove_,
|
||||
.destroy = _destroy,
|
||||
.destroy_offset = _destroy_offset,
|
||||
.destroy_function = _destroy_function,
|
||||
|
|
|
@ -49,6 +49,14 @@ struct blocking_queue_t {
|
|||
*/
|
||||
void *(*dequeue)(blocking_queue_t *this);
|
||||
|
||||
/**
|
||||
* Removes a specific item from the queue.
|
||||
*
|
||||
* @param item item to be removed from the queue
|
||||
* @return item if item was on the queue. Otherwise NULL`
|
||||
*/
|
||||
void *(*remove)(blocking_queue_t *this, void *item);
|
||||
|
||||
/**
|
||||
* Destroys a blocking_queue_t object.
|
||||
*
|
||||
|
|
Loading…
Reference in New Issue