v2 was an accidental re-post of the same patches, this time for real:
Changes to first version:
* Use struct value_string[] instead of switch{} for auth_action_str(). * Tweak first and last patch's log message.
Neels Hofmeyr (7): Add MM Auth test; add auth_action_str() function MM Auth test: add two tests for AUTH_THEN_CIPH MM Auth test: add test to re-use existing auth MM Auth: introduce AUTH_ERROR constant. MM Auth: return AUTH_NOT_AVAIL instead of hardcoded zero Fix MM Auth: disallow key_seq mismatch Fix MM Auth: zero-initialize auth tuple before first use
openbsc/.gitignore | 1 + openbsc/configure.ac | 1 + openbsc/include/openbsc/auth.h | 9 + openbsc/src/libmsc/auth.c | 33 +++- openbsc/tests/Makefile.am | 2 +- openbsc/tests/mm_auth/Makefile.am | 21 +++ openbsc/tests/mm_auth/mm_auth_test.c | 340 ++++++++++++++++++++++++++++++++++ openbsc/tests/mm_auth/mm_auth_test.ok | 40 ++++ openbsc/tests/testsuite.at | 7 + 9 files changed, 446 insertions(+), 8 deletions(-) create mode 100644 openbsc/tests/mm_auth/Makefile.am create mode 100644 openbsc/tests/mm_auth/mm_auth_test.c create mode 100644 openbsc/tests/mm_auth/mm_auth_test.ok
Add basic MM Authentication test setup, with fake DB access and RAND_bytes().
So far implement simple tests for IO error during DB access and missing auth entry.
To print the auth action during tests, add struct auth_action_names and auth_action_str() inline function in auth.[hc]. --- openbsc/.gitignore | 1 + openbsc/configure.ac | 1 + openbsc/include/openbsc/auth.h | 8 +++ openbsc/src/libmsc/auth.c | 9 +++ openbsc/tests/Makefile.am | 2 +- openbsc/tests/mm_auth/Makefile.am | 21 ++++++ openbsc/tests/mm_auth/mm_auth_test.c | 119 ++++++++++++++++++++++++++++++++++ openbsc/tests/mm_auth/mm_auth_test.ok | 8 +++ openbsc/tests/testsuite.at | 7 ++ 9 files changed, 175 insertions(+), 1 deletion(-) create mode 100644 openbsc/tests/mm_auth/Makefile.am create mode 100644 openbsc/tests/mm_auth/mm_auth_test.c create mode 100644 openbsc/tests/mm_auth/mm_auth_test.ok
diff --git a/openbsc/.gitignore b/openbsc/.gitignore index 55f4a31..28fdcc8 100644 --- a/openbsc/.gitignore +++ b/openbsc/.gitignore @@ -80,6 +80,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 24dbc30..60601fe 100644 --- a/openbsc/configure.ac +++ b/openbsc/configure.ac @@ -216,6 +216,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 d41d141..90495bb 100644 --- a/openbsc/include/openbsc/auth.h +++ b/openbsc/include/openbsc/auth.h @@ -1,6 +1,8 @@ #ifndef _AUTH_H #define _AUTH_H
+#include <osmocom/core/utils.h> + struct gsm_auth_tuple; struct gsm_subscriber;
@@ -11,6 +13,12 @@ enum auth_action { 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/libmsc/auth.c b/openbsc/src/libmsc/auth.c index 65a9b03..8512316 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 } + { -1, "(internal error)" }, /* soon to be fixed with an enum val */ + 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) diff --git a/openbsc/tests/Makefile.am b/openbsc/tests/Makefile.am index 04b8e34..09298a3 100644 --- a/openbsc/tests/Makefile.am +++ b/openbsc/tests/Makefile.am @@ -1,4 +1,4 @@ -SUBDIRS = gsm0408 db channel mgcp gprs abis gbproxy trau subscr +SUBDIRS = 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 0000000..516df00 --- /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 0000000..d8e4475 --- /dev/null +++ b/openbsc/tests/mm_auth/mm_auth_test.c @@ -0,0 +1,119 @@ +#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> + +/* 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 == -1); +} + +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); +} + +int main(void) +{ + osmo_init_logging(&log_info); + log_set_log_level(osmo_stderr_target, LOGL_INFO); + + test_error(); + test_auth_not_avail(); + 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 0000000..5efb3de --- /dev/null +++ b/openbsc/tests/mm_auth/mm_auth_test.ok @@ -0,0 +1,8 @@ + +* test_error() +wrapped: db_get_authinfo_for_subscr(): rc = -5 +auth_get_tuple_for_subscr(key_seq=0) --> auth_action == (internal 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 diff --git a/openbsc/tests/testsuite.at b/openbsc/tests/testsuite.at index 6a1c77f..dab9568 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 +
On Wed, Mar 30, 2016 at 11:22:24AM +0200, Neels Hofmeyr wrote:
Add basic MM Authentication test setup, with fake DB access and RAND_bytes().
I'm sorry, but it doesn't seem to apply to current master ?
On Wed, Mar 30, 2016 at 02:21:52PM +0200, Harald Welte wrote:
On Wed, Mar 30, 2016 at 11:22:24AM +0200, Neels Hofmeyr wrote:
Add basic MM Authentication test setup, with fake DB access and RAND_bytes().
I'm sorry, but it doesn't seem to apply to current master ?
I get a warning about a (super dangerous!) blank line at EOF, which is in openbsc/tests/testuite.at, otherwise it applies:
▶ git am /tmp/m Applying: Add MM Auth test; add auth_action_str() function /n/s/osmo/git/openbsc/.git/rebase-apply/patch:276: new blank line at EOF. + warning: 1 line adds whitespace errors. Applying: MM Auth test: add two tests for AUTH_THEN_CIPH Applying: MM Auth test: add test to re-use existing auth Applying: MM Auth: introduce AUTH_ERROR constant. Applying: MM Auth: return AUTH_NOT_AVAIL instead of hardcoded zero Applying: Fix MM Auth: disallow key_seq mismatch Applying: Fix MM Auth: zero-initialize auth tuple before first use
I can re-post without that blank line if you like. Let me know...
~Neels
Test two situations for AUTH_DO_AUTH_THEN_CIPH: - when no auth tuple is available - when the key sequence from LU is marked invalid
Add convenience auth tuple comparison function using stringification. --- openbsc/tests/mm_auth/mm_auth_test.c | 136 ++++++++++++++++++++++++++++++++++ openbsc/tests/mm_auth/mm_auth_test.ok | 16 ++++ 2 files changed, 152 insertions(+)
diff --git a/openbsc/tests/mm_auth/mm_auth_test.c b/openbsc/tests/mm_auth/mm_auth_test.c index d8e4475..c0b8da4 100644 --- a/openbsc/tests/mm_auth/mm_auth_test.c +++ b/openbsc/tests/mm_auth/mm_auth_test.c @@ -8,6 +8,59 @@ #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); @@ -108,6 +161,87 @@ static void test_auth_not_avail() 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 = 1\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" + )); +} + int main(void) { osmo_init_logging(&log_info); @@ -115,5 +249,7 @@ int main(void)
test_error(); test_auth_not_avail(); + test_auth_then_ciph1(); + test_auth_then_ciph2(); return 0; } diff --git a/openbsc/tests/mm_auth/mm_auth_test.ok b/openbsc/tests/mm_auth/mm_auth_test.ok index 5efb3de..52feb36 100644 --- a/openbsc/tests/mm_auth/mm_auth_test.ok +++ b/openbsc/tests/mm_auth/mm_auth_test.ok @@ -6,3 +6,19 @@ auth_get_tuple_for_subscr(key_seq=0) --> auth_action == (internal 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 + +* 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
--- openbsc/tests/mm_auth/mm_auth_test.c | 31 +++++++++++++++++++++++++++++++ openbsc/tests/mm_auth/mm_auth_test.ok | 6 ++++++ 2 files changed, 37 insertions(+)
diff --git a/openbsc/tests/mm_auth/mm_auth_test.c b/openbsc/tests/mm_auth/mm_auth_test.c index c0b8da4..e541898 100644 --- a/openbsc/tests/mm_auth/mm_auth_test.c +++ b/openbsc/tests/mm_auth/mm_auth_test.c @@ -242,6 +242,36 @@ static void test_auth_then_ciph2() )); }
+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" + )); +} + int main(void) { osmo_init_logging(&log_info); @@ -251,5 +281,6 @@ int main(void) test_auth_not_avail(); test_auth_then_ciph1(); test_auth_then_ciph2(); + test_auth_reuse(); return 0; } diff --git a/openbsc/tests/mm_auth/mm_auth_test.ok b/openbsc/tests/mm_auth/mm_auth_test.ok index 52feb36..cc0e769 100644 --- a/openbsc/tests/mm_auth/mm_auth_test.ok +++ b/openbsc/tests/mm_auth/mm_auth_test.ok @@ -22,3 +22,9 @@ 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
Instead of using hardcoded -1 for errors, include -1 in the enum auth_action type; apply its use.
In the mm_auth test, the string output changes from '(internal error)' to 'AUTH_ERROR', since now the proper enum value is used in auth_action_names[]. --- openbsc/include/openbsc/auth.h | 1 + openbsc/src/libmsc/auth.c | 6 +++--- openbsc/tests/mm_auth/mm_auth_test.c | 2 +- openbsc/tests/mm_auth/mm_auth_test.ok | 2 +- 4 files changed, 6 insertions(+), 5 deletions(-)
diff --git a/openbsc/include/openbsc/auth.h b/openbsc/include/openbsc/auth.h index 90495bb..6181131 100644 --- a/openbsc/include/openbsc/auth.h +++ b/openbsc/include/openbsc/auth.h @@ -7,6 +7,7 @@ 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 */ diff --git a/openbsc/src/libmsc/auth.c b/openbsc/src/libmsc/auth.c index 8512316..99e3a24 100644 --- a/openbsc/src/libmsc/auth.c +++ b/openbsc/src/libmsc/auth.c @@ -33,7 +33,7 @@
const struct value_string auth_action_names[] = { #define AUTH_ACTION_STR(X) { X, #X } - { -1, "(internal error)" }, /* soon to be fixed with an enum val */ + 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), @@ -93,7 +93,7 @@ int auth_get_tuple_for_subscr(struct gsm_auth_tuple *atuple, if (rc < 0) { LOGP(DMM, LOGL_NOTICE, "No retrievable Ki for subscriber, skipping auth\n"); - 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 */ @@ -114,7 +114,7 @@ int auth_get_tuple_for_subscr(struct gsm_auth_tuple *atuple,
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) { diff --git a/openbsc/tests/mm_auth/mm_auth_test.c b/openbsc/tests/mm_auth/mm_auth_test.c index e541898..1d65984 100644 --- a/openbsc/tests/mm_auth/mm_auth_test.c +++ b/openbsc/tests/mm_auth/mm_auth_test.c @@ -141,7 +141,7 @@ static void test_error() test_get_authinfo_rc = -EIO; auth_action = auth_get_tuple_for_subscr_verbose(&atuple, &subscr, key_seq); - OSMO_ASSERT(auth_action == -1); + OSMO_ASSERT(auth_action == AUTH_ERROR); }
static void test_auth_not_avail() diff --git a/openbsc/tests/mm_auth/mm_auth_test.ok b/openbsc/tests/mm_auth/mm_auth_test.ok index cc0e769..7dedadc 100644 --- a/openbsc/tests/mm_auth/mm_auth_test.ok +++ b/openbsc/tests/mm_auth/mm_auth_test.ok @@ -1,7 +1,7 @@
* test_error() wrapped: db_get_authinfo_for_subscr(): rc = -5 -auth_get_tuple_for_subscr(key_seq=0) --> auth_action == (internal error) +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_NOT_AVAIL == 0, so this is no functional change. --- openbsc/src/libmsc/auth.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/openbsc/src/libmsc/auth.c b/openbsc/src/libmsc/auth.c index 99e3a24..4ce1839 100644 --- a/openbsc/src/libmsc/auth.c +++ b/openbsc/src/libmsc/auth.c @@ -120,22 +120,22 @@ int auth_get_tuple_for_subscr(struct gsm_auth_tuple *atuple, 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)) - return 0; + return AUTH_NOT_AVAIL; break;
case AUTH_ALGO_COMP128v1: if (_use_comp128_v1(&ainfo, atuple)) - 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);
In auth_get_tuple_for_subscr(), add missing condition to match incoming key_seq with stored key_seq, so that re-authentication is requested for mismatching key_seqs.
Add test for this issue. --- openbsc/src/libmsc/auth.c | 1 + openbsc/tests/mm_auth/mm_auth_test.c | 32 ++++++++++++++++++++++++++++++++ openbsc/tests/mm_auth/mm_auth_test.ok | 6 ++++++ 3 files changed, 39 insertions(+)
diff --git a/openbsc/src/libmsc/auth.c b/openbsc/src/libmsc/auth.c index 4ce1839..ca39d01 100644 --- a/openbsc/src/libmsc/auth.c +++ b/openbsc/src/libmsc/auth.c @@ -100,6 +100,7 @@ int auth_get_tuple_for_subscr(struct gsm_auth_tuple *atuple, 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++; diff --git a/openbsc/tests/mm_auth/mm_auth_test.c b/openbsc/tests/mm_auth/mm_auth_test.c index 1d65984..2b45861 100644 --- a/openbsc/tests/mm_auth/mm_auth_test.c +++ b/openbsc/tests/mm_auth/mm_auth_test.c @@ -272,6 +272,37 @@ static void test_auth_reuse() )); }
+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); @@ -282,5 +313,6 @@ int main(void) 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 index 7dedadc..9d89bfb 100644 --- a/openbsc/tests/mm_auth/mm_auth_test.ok +++ b/openbsc/tests/mm_auth/mm_auth_test.ok @@ -28,3 +28,9 @@ 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
Make sure a new auth tuple is initialized after db_get_lastauthtuple_for_subscr() returns an error, i.e. if no tuple is present for the subscriber yet.
Before this patch, the first key_seq depended on the typically uninitialized value that was present in auth tuple's key_seq upon calling auth_get_tuple_for_subscr().
The very first key_seq used for a new subscriber will now always be 0. Before, it used to be mostly 1 ("(0 + 1) % 7"), but depended on whether the key_seq was indeed initialized with 0, actually by random. --- openbsc/src/libmsc/auth.c | 11 ++++++++++- openbsc/tests/mm_auth/mm_auth_test.c | 24 +++++++++++++++++++++++- openbsc/tests/mm_auth/mm_auth_test.ok | 4 ++++ 3 files changed, 37 insertions(+), 2 deletions(-)
diff --git a/openbsc/src/libmsc/auth.c b/openbsc/src/libmsc/auth.c index ca39d01..f30d56d 100644 --- a/openbsc/src/libmsc/auth.c +++ b/openbsc/src/libmsc/auth.c @@ -110,8 +110,17 @@ 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"); diff --git a/openbsc/tests/mm_auth/mm_auth_test.c b/openbsc/tests/mm_auth/mm_auth_test.c index 2b45861..34d96f1 100644 --- a/openbsc/tests/mm_auth/mm_auth_test.c +++ b/openbsc/tests/mm_auth/mm_auth_test.c @@ -183,7 +183,29 @@ static void test_auth_then_ciph1() OSMO_ASSERT(auth_tuple_is(&atuple, "gsm_auth_tuple {\n" " .use_count = 1\n" - " .key_seq = 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" diff --git a/openbsc/tests/mm_auth/mm_auth_test.ok b/openbsc/tests/mm_auth/mm_auth_test.ok index 9d89bfb..6c49f97 100644 --- a/openbsc/tests/mm_auth/mm_auth_test.ok +++ b/openbsc/tests/mm_auth/mm_auth_test.ok @@ -12,6 +12,10 @@ 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