aboutsummaryrefslogtreecommitdiffstats
path: root/openbsc
diff options
context:
space:
mode:
authorNeels Hofmeyr <nhofmeyr@sysmocom.de>2016-03-31 16:14:13 +0200
committerNeels Hofmeyr <nhofmeyr@sysmocom.de>2016-03-31 16:14:13 +0200
commitb70dfa610d89786aa72a4adb4ffc1ba27a5f8af4 (patch)
tree46b8ad0e9463c83b3a36344dc0ae050adc6ed358 /openbsc
parent4b940126a3132ac6d6da4194229f2b8cb642c2ec (diff)
parentcf1302e4cb4875816615a23e5d7e2e9f7bcb5bca (diff)
Merge branch 'master' into sysmocom/iu
Conflicts: openbsc/src/libmsc/auth.c openbsc/src/libmsc/gsm_04_08.c openbsc/src/osmo-bsc/osmo_bsc_vty.c openbsc/tests/Makefile.am
Diffstat (limited to 'openbsc')
-rw-r--r--openbsc/.gitignore1
-rw-r--r--openbsc/configure.ac1
-rw-r--r--openbsc/include/openbsc/auth.h9
-rw-r--r--openbsc/src/gprs/gprs_gmm.c2
-rw-r--r--openbsc/src/libmgcp/mgcp_transcode.c8
-rw-r--r--openbsc/src/libmsc/auth.c33
-rw-r--r--openbsc/src/osmo-bsc/osmo_bsc_vty.c4
-rw-r--r--openbsc/src/utils/meas_db.c13
-rw-r--r--openbsc/tests/Makefile.am2
-rw-r--r--openbsc/tests/mm_auth/Makefile.am21
-rw-r--r--openbsc/tests/mm_auth/mm_auth_test.c340
-rw-r--r--openbsc/tests/mm_auth/mm_auth_test.ok40
-rw-r--r--openbsc/tests/testsuite.at7
13 files changed, 455 insertions, 26 deletions
diff --git a/openbsc/.gitignore b/openbsc/.gitignore
index 14f98bc9d..209e83e2d 100644
--- a/openbsc/.gitignore
+++ b/openbsc/.gitignore
@@ -81,6 +81,7 @@ tests/sgsn/sgsn_test
tests/subscr/subscr_test
tests/oap/oap_test
tests/gtphub/gtphub_test
+tests/mm_auth/mm_auth_test
tests/atconfig
tests/atlocal
diff --git a/openbsc/configure.ac b/openbsc/configure.ac
index 9d739392a..52886e43e 100644
--- a/openbsc/configure.ac
+++ b/openbsc/configure.ac
@@ -222,6 +222,7 @@ AC_OUTPUT(
tests/subscr/Makefile
tests/oap/Makefile
tests/gtphub/Makefile
+ tests/mm_auth/Makefile
doc/Makefile
doc/examples/Makefile
Makefile)
diff --git a/openbsc/include/openbsc/auth.h b/openbsc/include/openbsc/auth.h
index d41d1419b..61811316b 100644
--- a/openbsc/include/openbsc/auth.h
+++ b/openbsc/include/openbsc/auth.h
@@ -1,16 +1,25 @@
#ifndef _AUTH_H
#define _AUTH_H
+#include <osmocom/core/utils.h>
+
struct gsm_auth_tuple;
struct gsm_subscriber;
enum auth_action {
+ AUTH_ERROR = -1, /* Internal error */
AUTH_NOT_AVAIL = 0, /* No auth tuple available */
AUTH_DO_AUTH_THEN_CIPH = 1, /* Firsth authenticate, then cipher */
AUTH_DO_CIPH = 2, /* Only ciphering */
AUTH_DO_AUTH = 3, /* Only authentication, no ciphering */
};
+extern const struct value_string auth_action_names[];
+static inline const char *auth_action_str(enum auth_action a)
+{
+ return get_value_string(auth_action_names, a);
+}
+
int auth_get_tuple_for_subscr(struct gsm_auth_tuple *atuple,
struct gsm_subscriber *subscr, int key_seq);
diff --git a/openbsc/src/gprs/gprs_gmm.c b/openbsc/src/gprs/gprs_gmm.c
index 630810aaa..6ece0e9ab 100644
--- a/openbsc/src/gprs/gprs_gmm.c
+++ b/openbsc/src/gprs/gprs_gmm.c
@@ -2400,7 +2400,7 @@ int gsm0408_gprs_rcvmsg_iu(struct msgb *msg, struct gprs_ra_id *ra_id,
uint16_t *sai)
{
struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_gmmh(msg);
- uint8_t pdisc = gh->proto_discr & 0x0f;
+ uint8_t pdisc = gsm48_hdr_pdisc(gh);
struct sgsn_mm_ctx *mmctx;
int rc = -EINVAL;
diff --git a/openbsc/src/libmgcp/mgcp_transcode.c b/openbsc/src/libmgcp/mgcp_transcode.c
index c994d3291..f31e7aefb 100644
--- a/openbsc/src/libmgcp/mgcp_transcode.c
+++ b/openbsc/src/libmgcp/mgcp_transcode.c
@@ -330,7 +330,7 @@ static int decode_audio(struct mgcp_process_rtp_state *state,
while (*nbytes >= state->src_frame_size) {
if (state->sample_cnt + state->src_samples_per_frame > ARRAY_SIZE(state->samples)) {
LOGP(DMGCP, LOGL_ERROR,
- "Sample buffer too small: %d > %d.\n",
+ "Sample buffer too small: %zu > %zu.\n",
state->sample_cnt + state->src_samples_per_frame,
ARRAY_SIZE(state->samples));
return -ENOSPC;
@@ -388,7 +388,7 @@ static int encode_audio(struct mgcp_process_rtp_state *state,
/* Not even one frame fits into the buffer */
LOGP(DMGCP, LOGL_INFO,
- "Encoding (RTP) buffer too small: %d > %d.\n",
+ "Encoding (RTP) buffer too small: %zu > %zu.\n",
nbytes + state->dst_frame_size, buf_size);
return -ENOSPC;
}
@@ -540,7 +540,7 @@ int mgcp_transcoding_process_rtp(struct mgcp_endpoint *endp,
* instead if the delta is small enough.
*/
LOGP(DMGCP, LOGL_NOTICE,
- "0x%x dropping sample buffer due delta=%d sample_cnt=%d\n",
+ "0x%x dropping sample buffer due delta=%d sample_cnt=%zu\n",
ENDPOINT_NUMBER(endp), delta, state->sample_cnt);
state->sample_cnt = 0;
state->next_time = ts_no;
@@ -569,7 +569,7 @@ int mgcp_transcoding_process_rtp(struct mgcp_endpoint *endp,
if (nbytes > 0)
LOGP(DMGCP, LOGL_NOTICE,
- "Skipped audio frame in RTP packet: %d octets\n",
+ "Skipped audio frame in RTP packet: %zu octets\n",
nbytes);
} else
ts_no = state->next_time;
diff --git a/openbsc/src/libmsc/auth.c b/openbsc/src/libmsc/auth.c
index 3d036950f..2d42c2dfe 100644
--- a/openbsc/src/libmsc/auth.c
+++ b/openbsc/src/libmsc/auth.c
@@ -31,6 +31,15 @@
#include <stdlib.h>
+const struct value_string auth_action_names[] = {
+#define AUTH_ACTION_STR(X) { X, #X }
+ AUTH_ACTION_STR(AUTH_ERROR),
+ AUTH_ACTION_STR(AUTH_NOT_AVAIL),
+ AUTH_ACTION_STR(AUTH_DO_AUTH_THEN_CIPH),
+ AUTH_ACTION_STR(AUTH_DO_CIPH),
+ AUTH_ACTION_STR(AUTH_DO_AUTH),
+#undef AUTH_ACTION_STR
+};
static int
_use_xor(struct gsm_auth_info *ainfo, struct gsm_auth_tuple *atuple)
@@ -82,13 +91,14 @@ int auth_get_tuple_for_subscr(struct gsm_auth_tuple *atuple,
/* Get subscriber info (if any) */
rc = db_get_authinfo_for_subscr(&ainfo, subscr);
if (rc < 0) {
- return rc == -ENOENT ? AUTH_NOT_AVAIL : -1;
+ return rc == -ENOENT ? AUTH_NOT_AVAIL : AUTH_ERROR;
}
/* If possible, re-use the last tuple and skip auth */
rc = db_get_lastauthtuple_for_subscr(atuple, subscr);
if ((rc == 0) &&
(key_seq != GSM_KEY_SEQ_INVAL) &&
+ (key_seq == atuple->key_seq) &&
(atuple->use_count < 3))
{
atuple->use_count++;
@@ -98,35 +108,44 @@ int auth_get_tuple_for_subscr(struct gsm_auth_tuple *atuple,
}
/* Generate a new one */
+ if (rc != 0) {
+ /* If db_get_lastauthtuple_for_subscr() returned nothing, make
+ * sure the atuple memory is initialized to zero and thus start
+ * off with key_seq = 0. */
+ memset(atuple, 0, sizeof(*atuple));
+ } else {
+ /* If db_get_lastauthtuple_for_subscr() returned a previous
+ * tuple, use the next key_seq. */
+ atuple->key_seq = (atuple->key_seq + 1) % 7;
+ }
atuple->use_count = 1;
- atuple->key_seq = (atuple->key_seq + 1) % 7;
if (RAND_bytes(atuple->rand, sizeof(atuple->rand)) != 1) {
LOGP(DMM, LOGL_NOTICE, "RAND_bytes failed, can't generate new auth tuple\n");
- return -1;
+ return AUTH_ERROR;
}
switch (ainfo.auth_algo) {
case AUTH_ALGO_NONE:
DEBUGP(DMM, "No authentication for subscriber\n");
- return 0;
+ return AUTH_NOT_AVAIL;
case AUTH_ALGO_XOR:
if (_use_xor(&ainfo, atuple))
/* non-zero return value means failure */
- return 0;
+ return AUTH_NOT_AVAIL;
break;
case AUTH_ALGO_COMP128v1:
if (_use_comp128_v1(&ainfo, atuple))
/* non-zero return value means failure */
- return 0;
+ return AUTH_NOT_AVAIL;
break;
default:
DEBUGP(DMM, "Unsupported auth type algo_id=%d\n",
ainfo.auth_algo);
- return 0;
+ return AUTH_NOT_AVAIL;
}
db_sync_lastauthtuple_for_subscr(atuple, subscr);
diff --git a/openbsc/src/osmo-bsc/osmo_bsc_vty.c b/openbsc/src/osmo-bsc/osmo_bsc_vty.c
index c8f0621dc..e623c9c10 100644
--- a/openbsc/src/osmo-bsc/osmo_bsc_vty.c
+++ b/openbsc/src/osmo-bsc/osmo_bsc_vty.c
@@ -43,7 +43,7 @@ static struct osmo_bsc_data *osmo_bsc_data(struct vty *vty)
static struct osmo_msc_data *osmo_msc_data(struct vty *vty)
{
- return osmo_msc_data_find(bsc_gsmnet, (long int) vty->index);
+ return vty->index;
}
static struct cmd_node bsc_node = {
@@ -70,7 +70,7 @@ DEFUN(cfg_net_msc, cfg_net_msc_cmd,
return CMD_WARNING;
}
- vty->index = (void *)(long int)index;
+ vty->index = msc;
vty->node = MSC_NODE;
return CMD_SUCCESS;
}
diff --git a/openbsc/src/utils/meas_db.c b/openbsc/src/utils/meas_db.c
index 6c7e7ae6e..a3b694e6b 100644
--- a/openbsc/src/utils/meas_db.c
+++ b/openbsc/src/utils/meas_db.c
@@ -314,7 +314,6 @@ err_io:
void meas_db_close(struct meas_db_state *st)
{
- int retries;
if (sqlite3_finalize(st->stmt_ins_mr) != SQLITE_OK)
fprintf(stderr, "DB insert measurement report finalize error: %s\n",
sqlite3_errmsg(st->db));
@@ -324,16 +323,8 @@ void meas_db_close(struct meas_db_state *st)
if (sqlite3_finalize(st->stmt_upd_mr) != SQLITE_OK)
fprintf(stderr, "DB update measurement report finalize error: %s\n",
sqlite3_errmsg(st->db));
- retries = 0;
- while (1) {
- if (sqlite3_close(st->db) == SQLITE_OK)
- break;
- if ((++retries) >= 3) {
- fprintf(stderr, "Unable to close DB, abandoning.\n");
- break;
- }
- sleep(1);
- }
+ if (sqlite3_close(st->db) != SQLITE_OK)
+ fprintf(stderr, "Unable to close DB, abandoning.\n");
talloc_free(st);
diff --git a/openbsc/tests/Makefile.am b/openbsc/tests/Makefile.am
index 6409867e2..75d5081e8 100644
--- a/openbsc/tests/Makefile.am
+++ b/openbsc/tests/Makefile.am
@@ -1,4 +1,4 @@
-SUBDIRS = libiudummy gsm0408 db channel mgcp gprs abis gbproxy trau subscr
+SUBDIRS = libiudummy gsm0408 db channel mgcp gprs abis gbproxy trau subscr mm_auth
if BUILD_NAT
SUBDIRS += bsc-nat bsc-nat-trie
diff --git a/openbsc/tests/mm_auth/Makefile.am b/openbsc/tests/mm_auth/Makefile.am
new file mode 100644
index 000000000..516df0007
--- /dev/null
+++ b/openbsc/tests/mm_auth/Makefile.am
@@ -0,0 +1,21 @@
+AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include
+AM_CFLAGS=-Wall \
+ $(LIBOSMOCORE_CFLAGS) \
+ $(LIBOSMOGSM_CFLAGS) \
+ $(LIBCRYPTO_CFLAGS)
+
+noinst_PROGRAMS = mm_auth_test
+
+EXTRA_DIST = mm_auth_test.ok
+
+mm_auth_test_SOURCES = mm_auth_test.c
+
+mm_auth_test_LDFLAGS = \
+ -Wl,--wrap=db_get_authinfo_for_subscr \
+ -Wl,--wrap=db_get_lastauthtuple_for_subscr \
+ -Wl,--wrap=db_sync_lastauthtuple_for_subscr
+
+mm_auth_test_LDADD = $(top_builddir)/src/libmsc/libmsc.a \
+ $(top_builddir)/src/libcommon/libcommon.a \
+ $(LIBOSMOCORE_LIBS) \
+ $(LIBOSMOGSM_LIBS)
diff --git a/openbsc/tests/mm_auth/mm_auth_test.c b/openbsc/tests/mm_auth/mm_auth_test.c
new file mode 100644
index 000000000..34d96f187
--- /dev/null
+++ b/openbsc/tests/mm_auth/mm_auth_test.c
@@ -0,0 +1,340 @@
+#include <stdbool.h>
+
+#include <osmocom/core/application.h>
+#include <osmocom/core/logging.h>
+
+#include <openbsc/debug.h>
+#include <openbsc/gsm_data.h>
+#include <openbsc/gsm_subscriber.h>
+#include <openbsc/auth.h>
+
+#define min(A,B) ((A)>(B)? (B) : (A))
+
+static char *auth_tuple_str(struct gsm_auth_tuple *atuple)
+{
+ static char buf[256];
+ char *pos = buf;
+ int len = sizeof(buf);
+ int l;
+
+#define print2buf(FMT, args...) do {\
+ l = snprintf(pos, len, FMT, ## args); \
+ pos += l;\
+ len -= l;\
+ } while (0)
+
+ print2buf("gsm_auth_tuple {\n");
+ print2buf(" .use_count = %d\n", atuple->use_count);
+ print2buf(" .key_seq = %d\n", atuple->key_seq);
+ print2buf(" .rand = %s\n", osmo_hexdump(atuple->rand, sizeof(atuple->rand)));
+ print2buf(" .sres = %s\n", osmo_hexdump(atuple->sres, sizeof(atuple->sres)));
+ print2buf(" .kc = %s\n", osmo_hexdump(atuple->kc, sizeof(atuple->kc)));
+ print2buf("}\n");
+#undef print2buf
+
+ return buf;
+}
+
+static bool auth_tuple_is(struct gsm_auth_tuple *atuple,
+ const char *expect_str)
+{
+ int l, l1, l2;
+ int i;
+ char *tuple_str = auth_tuple_str(atuple);
+ bool same = (strcmp(expect_str, tuple_str) == 0);
+ if (!same) {
+ l1 = strlen(expect_str);
+ l2 = strlen(tuple_str);
+ printf("Expected %d:\n%s\nGot %d:\n%s\n",
+ l1, expect_str, l2, tuple_str);
+ l = min(l1, l2);
+ for (i = 0; i < l; i++) {
+ if (expect_str[i] != tuple_str[i]) {
+ printf("Difference at pos %d"
+ " (%c 0x%0x != %c 0x%0x)\n",
+ i, expect_str[i], expect_str[i],
+ tuple_str[i], tuple_str[i]);
+ break;
+ }
+ }
+ }
+ return same;
+}
+
+/* override, requires '-Wl,--wrap=db_get_authinfo_for_subscr' */
+int __real_db_get_authinfo_for_subscr(struct gsm_auth_info *ainfo,
+ struct gsm_subscriber *subscr);
+
+int test_get_authinfo_rc = 0;
+struct gsm_auth_info test_auth_info = {0};
+struct gsm_auth_info default_auth_info = {
+ .auth_algo = AUTH_ALGO_COMP128v1,
+ .a3a8_ki_len = 16,
+ .a3a8_ki = { 0 }
+};
+
+int __wrap_db_get_authinfo_for_subscr(struct gsm_auth_info *ainfo,
+ struct gsm_subscriber *subscr)
+{
+ *ainfo = test_auth_info;
+ printf("wrapped: db_get_authinfo_for_subscr(): rc = %d\n", test_get_authinfo_rc);
+ return test_get_authinfo_rc;
+}
+
+/* override, requires '-Wl,--wrap=db_get_lastauthtuple_for_subscr' */
+int __real_db_get_lastauthtuple_for_subscr(struct gsm_auth_tuple *atuple,
+ struct gsm_subscriber *subscr);
+
+int test_get_lastauthtuple_rc = 0;
+struct gsm_auth_tuple test_last_auth_tuple = { 0 };
+struct gsm_auth_tuple default_auth_tuple = { 0 };
+
+int __wrap_db_get_lastauthtuple_for_subscr(struct gsm_auth_tuple *atuple,
+ struct gsm_subscriber *subscr)
+{
+ *atuple = test_last_auth_tuple;
+ printf("wrapped: db_get_lastauthtuple_for_subscr(): rc = %d\n", test_get_lastauthtuple_rc);
+ return test_get_lastauthtuple_rc;
+}
+
+/* override, requires '-Wl,--wrap=db_sync_lastauthtuple_for_subscr' */
+int __real_db_sync_lastauthtuple_for_subscr(struct gsm_auth_tuple *atuple,
+ struct gsm_subscriber *subscr);
+int test_sync_lastauthtuple_rc = 0;
+int __wrap_db_sync_lastauthtuple_for_subscr(struct gsm_auth_tuple *atuple,
+ struct gsm_subscriber *subscr)
+{
+ test_last_auth_tuple = *atuple;
+ printf("wrapped: db_sync_lastauthtuple_for_subscr(): rc = %d\n", test_sync_lastauthtuple_rc);
+ return test_sync_lastauthtuple_rc;
+}
+
+int auth_get_tuple_for_subscr_verbose(struct gsm_auth_tuple *atuple,
+ struct gsm_subscriber *subscr,
+ int key_seq)
+{
+ int auth_action;
+ auth_action = auth_get_tuple_for_subscr(atuple, subscr, key_seq);
+ printf("auth_get_tuple_for_subscr(key_seq=%d) --> auth_action == %s\n",
+ key_seq, auth_action_str(auth_action));
+ return auth_action;
+}
+
+/* override libssl RAND_bytes() to get testable crypto results */
+int RAND_bytes(uint8_t *rand, int len)
+{
+ memset(rand, 23, len);
+ return 1;
+}
+
+static void test_error()
+{
+ int auth_action;
+
+ struct gsm_auth_tuple atuple = {0};
+ struct gsm_subscriber subscr = {0};
+ int key_seq = 0;
+
+ printf("\n* test_error()\n");
+
+ /* any error (except -ENOENT) */
+ test_get_authinfo_rc = -EIO;
+ auth_action = auth_get_tuple_for_subscr_verbose(&atuple, &subscr,
+ key_seq);
+ OSMO_ASSERT(auth_action == AUTH_ERROR);
+}
+
+static void test_auth_not_avail()
+{
+ int auth_action;
+
+ struct gsm_auth_tuple atuple = {0};
+ struct gsm_subscriber subscr = {0};
+ int key_seq = 0;
+
+ printf("\n* test_auth_not_avail()\n");
+
+ /* no entry */
+ test_get_authinfo_rc = -ENOENT;
+ auth_action = auth_get_tuple_for_subscr_verbose(&atuple, &subscr,
+ key_seq);
+ OSMO_ASSERT(auth_action == AUTH_NOT_AVAIL);
+}
+
+static void test_auth_then_ciph1()
+{
+ int auth_action;
+
+ struct gsm_auth_tuple atuple = {0};
+ struct gsm_subscriber subscr = {0};
+ int key_seq;
+
+ printf("\n* test_auth_then_ciph1()\n");
+
+ /* Ki entry, but no auth tuple negotiated yet */
+ test_auth_info = default_auth_info;
+ test_last_auth_tuple = default_auth_tuple;
+ test_get_authinfo_rc = 0;
+ test_get_lastauthtuple_rc = -ENOENT;
+ key_seq = 0;
+ auth_action = auth_get_tuple_for_subscr_verbose(&atuple, &subscr,
+ key_seq);
+ OSMO_ASSERT(auth_action == AUTH_DO_AUTH_THEN_CIPH);
+ OSMO_ASSERT(auth_tuple_is(&atuple,
+ "gsm_auth_tuple {\n"
+ " .use_count = 1\n"
+ " .key_seq = 0\n"
+ " .rand = 17 17 17 17 17 17 17 17 17 17 17 17 17 17 17 17 \n"
+ " .sres = a1 ab c6 90 \n"
+ " .kc = 0f 27 ed f3 ac 97 ac 00 \n"
+ "}\n"
+ ));
+
+ /* With a different last saved key_seq stored in the out-arg of
+ * db_get_lastauthtuple_for_subscr() by coincidence, expect absolutely
+ * the same as above. */
+ test_auth_info = default_auth_info;
+ test_last_auth_tuple = default_auth_tuple;
+ test_last_auth_tuple.key_seq = 3;
+ test_get_authinfo_rc = 0;
+ test_get_lastauthtuple_rc = -ENOENT;
+ key_seq = 0;
+ auth_action = auth_get_tuple_for_subscr_verbose(&atuple, &subscr,
+ key_seq);
+ OSMO_ASSERT(auth_action == AUTH_DO_AUTH_THEN_CIPH);
+ OSMO_ASSERT(auth_tuple_is(&atuple,
+ "gsm_auth_tuple {\n"
+ " .use_count = 1\n"
+ " .key_seq = 0\n"
+ " .rand = 17 17 17 17 17 17 17 17 17 17 17 17 17 17 17 17 \n"
+ " .sres = a1 ab c6 90 \n"
+ " .kc = 0f 27 ed f3 ac 97 ac 00 \n"
+ "}\n"
+ ));
+}
+
+static void test_auth_then_ciph2()
+{
+ int auth_action;
+
+ struct gsm_auth_tuple atuple = {0};
+ struct gsm_subscriber subscr = {0};
+ int key_seq;
+
+ printf("\n* test_auth_then_ciph2()\n");
+
+ /* Ki entry, auth tuple negotiated, but invalid incoming key_seq */
+ test_auth_info = default_auth_info;
+ test_last_auth_tuple = default_auth_tuple;
+ test_last_auth_tuple.key_seq = 2;
+ test_get_authinfo_rc = 0;
+ test_get_lastauthtuple_rc = 0;
+ key_seq = GSM_KEY_SEQ_INVAL;
+ auth_action = auth_get_tuple_for_subscr_verbose(&atuple, &subscr,
+ key_seq);
+ OSMO_ASSERT(auth_action == AUTH_DO_AUTH_THEN_CIPH);
+ OSMO_ASSERT(auth_tuple_is(&atuple,
+ "gsm_auth_tuple {\n"
+ " .use_count = 1\n"
+ " .key_seq = 3\n"
+ " .rand = 17 17 17 17 17 17 17 17 17 17 17 17 17 17 17 17 \n"
+ " .sres = a1 ab c6 90 \n"
+ " .kc = 0f 27 ed f3 ac 97 ac 00 \n"
+ "}\n"
+ ));
+
+ /* Change the last saved key_seq, expect last_auth_tuple.key_seq + 1 */
+ test_auth_info = default_auth_info;
+ test_last_auth_tuple = default_auth_tuple;
+ test_last_auth_tuple.key_seq = 3;
+ test_get_authinfo_rc = 0;
+ test_get_lastauthtuple_rc = 0;
+ key_seq = GSM_KEY_SEQ_INVAL;
+ auth_action = auth_get_tuple_for_subscr_verbose(&atuple, &subscr,
+ key_seq);
+ OSMO_ASSERT(auth_action == AUTH_DO_AUTH_THEN_CIPH);
+ OSMO_ASSERT(auth_tuple_is(&atuple,
+ "gsm_auth_tuple {\n"
+ " .use_count = 1\n"
+ " .key_seq = 4\n"
+ " .rand = 17 17 17 17 17 17 17 17 17 17 17 17 17 17 17 17 \n"
+ " .sres = a1 ab c6 90 \n"
+ " .kc = 0f 27 ed f3 ac 97 ac 00 \n"
+ "}\n"
+ ));
+}
+
+static void test_auth_reuse()
+{
+ int auth_action;
+ struct gsm_auth_tuple atuple = {0};
+ struct gsm_subscriber subscr = {0};
+ int key_seq;
+
+ printf("\n* test_auth_reuse()\n");
+
+ /* Ki entry, auth tuple negotiated, valid+matching incoming key_seq */
+ test_auth_info = default_auth_info;
+ test_last_auth_tuple = default_auth_tuple;
+ test_last_auth_tuple.key_seq = key_seq = 3;
+ test_last_auth_tuple.use_count = 1;
+ test_get_authinfo_rc = 0;
+ test_get_lastauthtuple_rc = 0;
+ auth_action = auth_get_tuple_for_subscr_verbose(&atuple, &subscr,
+ key_seq);
+ OSMO_ASSERT(auth_action == AUTH_DO_CIPH);
+ OSMO_ASSERT(auth_tuple_is(&atuple,
+ "gsm_auth_tuple {\n"
+ " .use_count = 2\n"
+ " .key_seq = 3\n"
+ " .rand = 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n"
+ " .sres = 00 00 00 00 \n"
+ " .kc = 00 00 00 00 00 00 00 00 \n"
+ "}\n"
+ ));
+}
+
+static void test_auth_reuse_key_seq_mismatch()
+{
+ int auth_action;
+ struct gsm_auth_tuple atuple = {0};
+ struct gsm_subscriber subscr = {0};
+ int key_seq;
+
+ printf("\n* test_auth_reuse_key_seq_mismatch()\n");
+
+ /* Ki entry, auth tuple negotiated, valid+matching incoming key_seq */
+ test_auth_info = default_auth_info;
+ test_last_auth_tuple = default_auth_tuple;
+ test_last_auth_tuple.key_seq = 3;
+ key_seq = 4;
+ test_last_auth_tuple.use_count = 1;
+ test_get_authinfo_rc = 0;
+ test_get_lastauthtuple_rc = 0;
+ auth_action = auth_get_tuple_for_subscr_verbose(&atuple, &subscr,
+ key_seq);
+ OSMO_ASSERT(auth_action == AUTH_DO_AUTH_THEN_CIPH);
+ OSMO_ASSERT(auth_tuple_is(&atuple,
+ "gsm_auth_tuple {\n"
+ " .use_count = 1\n"
+ " .key_seq = 4\n"
+ " .rand = 17 17 17 17 17 17 17 17 17 17 17 17 17 17 17 17 \n"
+ " .sres = a1 ab c6 90 \n"
+ " .kc = 0f 27 ed f3 ac 97 ac 00 \n"
+ "}\n"
+ ));
+}
+
+int main(void)
+{
+ osmo_init_logging(&log_info);
+ log_set_log_level(osmo_stderr_target, LOGL_INFO);
+
+ test_error();
+ test_auth_not_avail();
+ test_auth_then_ciph1();
+ test_auth_then_ciph2();
+ test_auth_reuse();
+ test_auth_reuse_key_seq_mismatch();
+ return 0;
+}
diff --git a/openbsc/tests/mm_auth/mm_auth_test.ok b/openbsc/tests/mm_auth/mm_auth_test.ok
new file mode 100644
index 000000000..6c49f97b7
--- /dev/null
+++ b/openbsc/tests/mm_auth/mm_auth_test.ok
@@ -0,0 +1,40 @@
+
+* test_error()
+wrapped: db_get_authinfo_for_subscr(): rc = -5
+auth_get_tuple_for_subscr(key_seq=0) --> auth_action == AUTH_ERROR
+
+* test_auth_not_avail()
+wrapped: db_get_authinfo_for_subscr(): rc = -2
+auth_get_tuple_for_subscr(key_seq=0) --> auth_action == AUTH_NOT_AVAIL
+
+* test_auth_then_ciph1()
+wrapped: db_get_authinfo_for_subscr(): rc = 0
+wrapped: db_get_lastauthtuple_for_subscr(): rc = -2
+wrapped: db_sync_lastauthtuple_for_subscr(): rc = 0
+auth_get_tuple_for_subscr(key_seq=0) --> auth_action == AUTH_DO_AUTH_THEN_CIPH
+wrapped: db_get_authinfo_for_subscr(): rc = 0
+wrapped: db_get_lastauthtuple_for_subscr(): rc = -2
+wrapped: db_sync_lastauthtuple_for_subscr(): rc = 0
+auth_get_tuple_for_subscr(key_seq=0) --> auth_action == AUTH_DO_AUTH_THEN_CIPH
+
+* test_auth_then_ciph2()
+wrapped: db_get_authinfo_for_subscr(): rc = 0
+wrapped: db_get_lastauthtuple_for_subscr(): rc = 0
+wrapped: db_sync_lastauthtuple_for_subscr(): rc = 0
+auth_get_tuple_for_subscr(key_seq=7) --> auth_action == AUTH_DO_AUTH_THEN_CIPH
+wrapped: db_get_authinfo_for_subscr(): rc = 0
+wrapped: db_get_lastauthtuple_for_subscr(): rc = 0
+wrapped: db_sync_lastauthtuple_for_subscr(): rc = 0
+auth_get_tuple_for_subscr(key_seq=7) --> auth_action == AUTH_DO_AUTH_THEN_CIPH
+
+* test_auth_reuse()
+wrapped: db_get_authinfo_for_subscr(): rc = 0
+wrapped: db_get_lastauthtuple_for_subscr(): rc = 0
+wrapped: db_sync_lastauthtuple_for_subscr(): rc = 0
+auth_get_tuple_for_subscr(key_seq=3) --> auth_action == AUTH_DO_CIPH
+
+* test_auth_reuse_key_seq_mismatch()
+wrapped: db_get_authinfo_for_subscr(): rc = 0
+wrapped: db_get_lastauthtuple_for_subscr(): rc = 0
+wrapped: db_sync_lastauthtuple_for_subscr(): rc = 0
+auth_get_tuple_for_subscr(key_seq=4) --> auth_action == AUTH_DO_AUTH_THEN_CIPH
diff --git a/openbsc/tests/testsuite.at b/openbsc/tests/testsuite.at
index 6a1c77f54..dab956888 100644
--- a/openbsc/tests/testsuite.at
+++ b/openbsc/tests/testsuite.at
@@ -117,3 +117,10 @@ AT_CHECK([test "$enable_gtphub_test" != no || exit 77])
cat $abs_srcdir/gtphub/gtphub_test.ok > expout
AT_CHECK([$abs_top_builddir/tests/gtphub/gtphub_test], [], [expout], [ignore])
AT_CLEANUP
+
+AT_SETUP([mm_auth])
+AT_KEYWORDS([mm_auth])
+cat $abs_srcdir/mm_auth/mm_auth_test.ok > expout
+AT_CHECK([$abs_top_builddir/tests/mm_auth/mm_auth_test], [], [expout], [ignore])
+AT_CLEANUP
+