import oap message parsing / encoding from openbsc.git; AGPL->GPL

In the process, also:
* Change the license from AGPLv3 to GPLv2-or-later;
* correct spelling of 'sysmocom' to lowercase;
* add '2016' to the copyright;
* rename to osmo_*;
* add API docs;
* add logging category DLOAP: define id and add to internal_cat;
* redirect all oap.c logging to DLOAP.

A unit test will follow in a subsequent patch, since it needs a minor tweak for
decoding of boolean values.

The related openbsc change-id is I2f06aaa6eb54eafa860cfed8e72e41d82ff1c4cf.

Tweaked-by: Neels Hofmeyr
Change-Id: If5099e60681a215e798b6675f21813f26769c253
This commit is contained in:
Harald Welte 2016-04-27 18:32:35 +02:00 committed by Neels Hofmeyr
parent 9795cf1b12
commit c0f0007292
9 changed files with 519 additions and 3 deletions

View File

@ -13,7 +13,7 @@ $(top_srcdir)/.version:
dist-hook:
echo $(VERSION) > $(distdir)/.tarball-version
EXTRA_DIST = git-version-gen .version
EXTRA_DIST = git-version-gen .version doc/osmocom-authn-protocol.txt
if HAVE_DOXYGEN

View File

@ -0,0 +1,250 @@
Osmocom Authentication Protocol (OAP)
1. General
The Osmocom Authentication Protocol employs mutual authentication to register a
client with a server over an IPA connection. Milenage is used as the
authentication algorithm, where client and server have a shared secret.
For example, an SGSN, as OAP client, may use its SGSN ID to register with a MAP
proxy, an OAP server.
1.1. Connection
The protocol expects that a reliable, ordered, packet boundaries preserving
connection is used (e.g. IPA over TCP).
1.2. Using IPA
By default, the following identifiers should be used:
- IPA protocol: 0xee (OSMO)
- IPA OSMO protocol extension: 0x06 (OAP)
2. Procedures
Ideal communication sequence:
Client Server
| |
| Register (ID) |
|----------------------------------->|
| |
| Challenge (RAND+AUTN) |
|<-----------------------------------|
| |
| Challenge Result (XRES) |
|----------------------------------->|
| |
| Register Result |
|<-----------------------------------|
Variation "test setup":
Client Server
| |
| Register (ID) |
|----------------------------------->|
| |
| Register Result |
|<-----------------------------------|
Variation "invalid sequence nr":
Client Server
| |
| Register (ID) |
|----------------------------------->|
| |
| Challenge (RAND+AUTN) |
|<-----------------------------------|
| |
| Sync Request (AUTS) |
|----------------------------------->|
| |
| Challenge (RAND'+AUTN') |
|<-----------------------------------|
| |
| Challenge Result (XRES) |
|----------------------------------->|
| |
| Register Result |
|<-----------------------------------|
2.1. Register
The client sends a REGISTER_REQ message containing an identifier number.
2.2. Challenge
The OAP server (optionally) sends back a CHALLENGE_REQ, containing random bytes
and a milenage authentication token generated from these random bytes, using a
shared secret, to authenticate itself to the OAP client. The server may omit
this challenge entirely, based on its configuration, and immediately reply with
a Register Result response. If the client cannot be registered (e.g. id is
invalid), the server sends a REGISTER_ERR response.
2.3. Challenge Result
When the client has received a Challenge, it may verify the server's
authenticity and validity of the sequence number (included in AUTN), and, if
valid, reply with a CHALLENGE_RES message. This shall contain an XRES
authentication token generated by milenage from the same random bytes received
from the server and the same shared secet. If the client decides to cancel the
registration (e.g. invalid AUTN), it shall not reply to the CHALLENGE_REQ; a
CHALLENGE_ERR message may be sent, but is not mandatory. For example, the
client may directly start with a new REGISTER_REQ message.
2.4. Sync Request
When the client has received a Challenge but sees an invalid sequence number
(embedded in AUTN, according to the milenage algorithm), the client may send a
SYNC_REQ message containing an AUTS synchronisation token.
2.5. Sync Result
If the server has received a valid Sync Request, it shall answer by directly
sending another Challenge (see 2.2.). If an invalid Sync Request is received,
the server shall reply with a REGISTER_ERR message.
2.6. Register Result
The server sends a REGISTER_RES message to indicate that registration has been
successful. If the server cannot register the client (e.g. invalid challenge
response), it shall send a REGISTER_ERR message.
3. Message Format
3.1. General
Every message is based on the following message format
IEI Info Element Type Pres. Format Length
Message type 4.2.1 M V 1
The receiver shall be able to receive IEs in any order. Unknown IEs shall be
ignored.
3.2.1. Register Request
Client -> Server
IEI Info Element Type Pres. Format Length
Message type 4.2.1 M V 1
30 Client ID big endian int (2 oct) M TLV 4
3.2.2. Register Error
Server -> Client
IEI Info Element Type Pres. Format Length
Message type 4.2.1 M V 1
02 Cause GMM cause, M TLV 3
04.08: 10.5.5.14
3.2.6. Register Result
Server -> Client
IEI Info Element Type Pres. Format Length
Message type 4.2.1 M V 1
3.2.3. Challenge
Server -> Client
IEI Info Element Type Pres. Format Length
Message type 4.2.1 M V 1
20 RAND octet string (16) M TLV 18
23 AUTN octet string (16) M TLV 18
3.2.4. Challenge Error
Client -> Server
IEI Info Element Type Pres. Format Length
Message type 4.2.1 M V 1
02 Cause GMM cause, M TLV 3
04.08: 10.5.5.14
3.2.5. Challenge Result
Client -> Server
IEI Info Element Type Pres. Format Length
Message type 4.2.1 M V 1
21 XRES octet string (8) M TLV 10
3.2.3. Sync Request
Client -> Server
IEI Info Element Type Pres. Format Length
Message type 4.2.1 M V 1
20 AUTS octet string (16) M TLV 18
3.2.4. Sync Error
Server -> Client
IEI Info Element Type Pres. Format Length
Message type 4.2.1 M V 1
02 Cause GMM cause, M TLV 3
04.08: 10.5.5.14
4. Information Elements
4.1. General
[...]
4.2.1. Message Type
+---------------------------------------------------+
| 8 7 6 5 4 3 2 1 |
| |
| 0 0 0 0 0 1 0 0 - Register Request |
| 0 0 0 0 0 1 0 1 - Register Error |
| 0 0 0 0 0 1 1 0 - Register Result |
| |
| 0 0 0 0 1 0 0 0 - Challenge Request |
| 0 0 0 0 1 0 0 1 - Challenge Error |
| 0 0 0 0 1 0 1 0 - Challenge Result |
| |
| 0 0 0 0 1 1 0 0 - Sync Request |
| 0 0 0 0 1 1 0 1 - Sync Error (not used) |
| 0 0 0 0 1 1 1 0 - Sync Result (not used) |
| |
+---------------------------------------------------+
4.2.2. IE Identifier (informational)
These are the standard values for the IEI.
+---------------------------------------------------------+
| IEI Info Element Type |
| |
| 0x02 Cause GMM cause, 04.08: 10.5.5.14 |
| 0x20 RAND octet string |
| 0x23 AUTN octet string |
| 0x24 XRES octet string |
| 0x25 AUTS octet string |
| 0x30 Client ID big endian int (2 octets) |
+---------------------------------------------------------+
4.2.3. Client ID
8 7 6 5 4 3 2 1
+-----------------------------------------------------+
| | Client ID IEI | octet 1
+-----------------------------------------------------+
| Length of Client ID IE contents (2) | octet 2
+-----------------------------------------------------+
| Client ID number, most significant byte | octet 3
+-----------------------------------------------------+
| Client ID number, least significant byte | octet 4
+-----------------------------------------------------+
The Client ID number shall be interpreted as an unsigned 16bit integer, where 0
indicates an invalid / unset ID.

View File

@ -82,6 +82,7 @@ nobase_include_HEADERS = \
osmocom/gsm/mncc.h \
osmocom/gsm/prim.h \
osmocom/gsm/l1sap.h \
osmocom/gsm/oap.h \
osmocom/gsm/protocol/gsm_03_40.h \
osmocom/gsm/protocol/gsm_03_41.h \
osmocom/gsm/protocol/gsm_04_08.h \

View File

@ -93,7 +93,8 @@ void logp(int subsys, const char *file, int line, int cont, const char *format,
#define DLGTP -9 /*!< GTP (GPRS Tunneling Protocol */
#define DLSTATS -10 /*!< Statistics */
#define DLGSUP -11 /*!< Generic Subscriber Update Protocol */
#define OSMO_NUM_DLIB 11 /*!< Number of logging sub-systems in libraries */
#define DLOAP -12 /*!< Osmocom Authentication Protocol */
#define OSMO_NUM_DLIB 12 /*!< Number of logging sub-systems in libraries */
/*! Configuration of singgle log category / sub-system */
struct log_category {

72
include/osmocom/gsm/oap.h Normal file
View File

@ -0,0 +1,72 @@
/* Osmocom Authentication Protocol message encoder/decoder */
/* (C) 2015-2016 by sysmocom s.f.m.c. GmbH
* All Rights Reserved
*
* Author: Neels Hofmeyr
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#pragma once
#include <stdint.h>
#include <osmocom/core/msgb.h>
#include <osmocom/gsm/protocol/gsm_04_08_gprs.h>
/*! \brief Information Element Identifiers for OAP IEs.
* They match osmo_gsup_iei (so far). */
enum osmo_oap_iei {
OAP_CAUSE_IE = 0x02,
OAP_RAND_IE = 0x20,
OAP_AUTN_IE = 0x23,
OAP_XRES_IE = 0x24,
OAP_AUTS_IE = 0x25,
OAP_CLIENT_ID_IE = 0x30,
};
/*! \brief OAP message types */
enum osmo_oap_message_type {
OAP_MSGT_REGISTER_REQUEST = 0b00000100,
OAP_MSGT_REGISTER_ERROR = 0b00000101,
OAP_MSGT_REGISTER_RESULT = 0b00000110,
OAP_MSGT_CHALLENGE_REQUEST = 0b00001000,
OAP_MSGT_CHALLENGE_ERROR = 0b00001001,
OAP_MSGT_CHALLENGE_RESULT = 0b00001010,
OAP_MSGT_SYNC_REQUEST = 0b00001100,
OAP_MSGT_SYNC_ERROR = 0b00001101,
OAP_MSGT_SYNC_RESULT = 0b00001110,
};
/*! \brief Parsed/decoded OAP protocol message */
struct osmo_oap_message {
enum osmo_oap_message_type message_type;
enum gsm48_gmm_cause cause;
uint16_t client_id;
int rand_present;
uint8_t rand[16];
int autn_present;
uint8_t autn[16];
int xres_present;
uint8_t xres[8];
int auts_present;
uint8_t auts[16];
};
int osmo_oap_decode(struct osmo_oap_message *oap_msg, const uint8_t *data,
size_t data_len);
void osmo_oap_encode(struct msgb *msg, const struct osmo_oap_message *oap_msg);

View File

@ -22,7 +22,7 @@ libgsmint_la_SOURCES = a5.c rxlev_stat.c tlv_parser.c comp128.c comp128v23.c \
auth_milenage.c milenage/aes-encblock.c gea.c \
milenage/aes-internal.c milenage/aes-internal-enc.c \
milenage/milenage.c gan.c ipa.c gsm0341.c apn.c \
gsup.c gprs_gea.c gsm0503_conv.c
gsup.c gprs_gea.c gsm0503_conv.c oap.c
libgsmint_la_LDFLAGS = -no-undefined
libgsmint_la_LIBADD = $(top_builddir)/src/libosmocore.la

View File

@ -356,5 +356,8 @@ osmo_apn_from_str;
osmo_gsup_encode;
osmo_gsup_decode;
osmo_oap_encode;
osmo_oap_decode;
local: *;
};

184
src/gsm/oap.c Normal file
View File

@ -0,0 +1,184 @@
/* Osmocom Authentication Protocol message encoder/decoder */
/* (C) 2015-2016 by sysmocom s.f.m.c. GmbH
* All Rights Reserved
*
* Author: Neels Hofmeyr
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <osmocom/core/utils.h>
#include <osmocom/core/logging.h>
#include <osmocom/core/msgb.h>
#include <osmocom/gsm/tlv.h>
#include <osmocom/gsm/oap.h>
#include <stdint.h>
/*! \brief Decode OAP message data.
* \param[out] oap_msg Parsed data is written to this instance.
* \param[in] data Pointer to the data buffer containing the OAP message.
* \param[in] data_len Length of the OAP message data.
* \returns 0 on success, a negative cause value on failure.
*/
int osmo_oap_decode(struct osmo_oap_message *oap_msg,
const uint8_t *const_data, size_t data_len)
{
int rc;
uint8_t tag;
/* the shift/match functions expect non-const pointers, but we'll
* either copy the data or cast pointers back to const before returning
* them
*/
uint8_t *data = (uint8_t *)const_data;
uint8_t *value;
size_t value_len;
memset(oap_msg, 0, sizeof(*oap_msg));
/* message type */
rc = osmo_shift_v_fixed(&data, &data_len, 1, &value);
if (rc < 0)
return -GMM_CAUSE_INV_MAND_INFO;
oap_msg->message_type = osmo_decode_big_endian(value, 1);
/* specific parts */
while (data_len > 0) {
enum osmo_oap_iei iei;
rc = osmo_shift_tlv(&data, &data_len, &tag, &value, &value_len);
if (rc < 0)
return -GMM_CAUSE_PROTO_ERR_UNSPEC;
iei = tag;
switch (iei) {
case OAP_CLIENT_ID_IE:
if (value_len != 2) {
LOGP(DLOAP, LOGL_NOTICE,
"OAP IE type client ID (%d) should be 2 octets, but has %d\n",
(int)iei, (int)value_len);
return -GMM_CAUSE_PROTO_ERR_UNSPEC;
}
oap_msg->client_id = osmo_decode_big_endian(value, value_len);
if (oap_msg->client_id == 0) {
LOGP(DLOAP, LOGL_NOTICE,
"OAP IE type client ID (%d): client ID must be nonzero.\n",
(int)iei);
return -GMM_CAUSE_PROTO_ERR_UNSPEC;
}
break;
case OAP_AUTN_IE:
if (value_len != sizeof(oap_msg->autn)) {
LOGP(DLOAP, LOGL_NOTICE,
"OAP IE type AUTN (%d) should be %d octets, but has %d\n",
(int)iei, (int)sizeof(oap_msg->autn), (int)value_len);
return -GMM_CAUSE_PROTO_ERR_UNSPEC;
}
memcpy(oap_msg->autn, value, value_len);
oap_msg->autn_present = value_len;
break;
case OAP_RAND_IE:
if (value_len != sizeof(oap_msg->rand)) {
LOGP(DLOAP, LOGL_NOTICE,
"OAP IE type RAND (%d) should be %d octets, but has %d\n",
(int)iei, (int)sizeof(oap_msg->rand), (int)value_len);
return -GMM_CAUSE_PROTO_ERR_UNSPEC;
}
memcpy(oap_msg->rand, value, value_len);
oap_msg->rand_present = value_len;
break;
case OAP_XRES_IE:
if (value_len != sizeof(oap_msg->xres)) {
LOGP(DLOAP, LOGL_NOTICE,
"OAP IE type XRES (%d) should be %d octets, but has %d\n",
(int)iei, (int)sizeof(oap_msg->xres), (int)value_len);
return -GMM_CAUSE_PROTO_ERR_UNSPEC;
}
memcpy(oap_msg->xres, value, value_len);
oap_msg->xres_present = value_len;
break;
case OAP_AUTS_IE:
if (value_len != sizeof(oap_msg->auts)) {
LOGP(DLOAP, LOGL_NOTICE,
"OAP IE type AUTS (%d) should be %d octets, but has %d\n",
(int)iei, (int)sizeof(oap_msg->auts), (int)value_len);
return -GMM_CAUSE_PROTO_ERR_UNSPEC;
}
memcpy(oap_msg->auts, value, value_len);
oap_msg->auts_present = value_len;
break;
case OAP_CAUSE_IE:
if (value_len > 1) {
LOGP(DLOAP, LOGL_ERROR,
"OAP cause may not exceed one octet, is %d", (int)value_len);
return -GMM_CAUSE_PROTO_ERR_UNSPEC;
}
oap_msg->cause = *value;
break;
default:
LOGP(DLOAP, LOGL_NOTICE,
"OAP IE type %d unknown\n", iei);
continue;
}
}
return 0;
}
/*! \brief Compose OAP message data.
* \param[out] msg OAP message data is appended to this message buffer.
* \param[in] oap_msg Elements to encode in the message data.
*/
void osmo_oap_encode(struct msgb *msg, const struct osmo_oap_message *oap_msg)
{
uint8_t u8;
/* generic part */
OSMO_ASSERT(oap_msg->message_type);
msgb_v_put(msg, (uint8_t)oap_msg->message_type);
/* specific parts */
if ((u8 = oap_msg->cause))
msgb_tlv_put(msg, OAP_CAUSE_IE, sizeof(u8), &u8);
if (oap_msg->client_id > 0)
msgb_tlv_put(msg, OAP_CLIENT_ID_IE, sizeof(oap_msg->client_id),
osmo_encode_big_endian(oap_msg->client_id,
sizeof(oap_msg->client_id)));
if (oap_msg->rand_present)
msgb_tlv_put(msg, OAP_RAND_IE, sizeof(oap_msg->rand), oap_msg->rand);
if (oap_msg->autn_present)
msgb_tlv_put(msg, OAP_AUTN_IE, sizeof(oap_msg->autn), oap_msg->autn);
if (oap_msg->auts_present)
msgb_tlv_put(msg, OAP_AUTS_IE, sizeof(oap_msg->auts), oap_msg->auts);
if (oap_msg->xres_present)
msgb_tlv_put(msg, OAP_XRES_IE, sizeof(oap_msg->xres), oap_msg->xres);
msg->l2h = msg->data;
}

View File

@ -128,6 +128,11 @@ static const struct log_info_cat internal_cat[OSMO_NUM_DLIB] = {
.description = "Generic Subscriber Update Protocol",
.enabled = 1, .loglevel = LOGL_NOTICE,
},
[INT2IDX(DLOAP)] = {
.name = "DLOAP",
.description = "Osmocom Authentication Protocol",
.enabled = 1, .loglevel = LOGL_NOTICE,
},
};
/*! \brief descriptive string for each log level */