SGSN: use unique AUTH REQ reference

The A&C reference number specified in 3GPP TS 24.008 ยง 10.5.5.19
identifies particular request sent by network with the related response
sent by MS. The value transparently copied from request to response by
MS: the spec do not specify what exactly should be in there so we use
rand() to decrease chance for collisions.

Note: variable named 'rand' clashes with standard function rand() so it
was renamed.

Change-Id: I3638821a9b4a0532b28dbbb50faa30c4082579f6
Related: OS#1582
diff --git a/openbsc/include/openbsc/gprs_sgsn.h b/openbsc/include/openbsc/gprs_sgsn.h
index 0e574d8..9604084 100644
--- a/openbsc/include/openbsc/gprs_sgsn.h
+++ b/openbsc/include/openbsc/gprs_sgsn.h
@@ -158,6 +158,8 @@
 	/* Iu: CK, IK, KSI */
 	/* CKSN */
 	enum gprs_ciph_algo	ciph_algo;
+	/* Auth & Ciphering Request reference from 3GPP TS 24.008 § 10.5.5.19: */
+	uint8_t ac_ref_nr_used;
 
 	struct {
 		uint8_t	len;
diff --git a/openbsc/src/gprs/gprs_gmm.c b/openbsc/src/gprs/gprs_gmm.c
index 5db69dd..7f10b07 100644
--- a/openbsc/src/gprs/gprs_gmm.c
+++ b/openbsc/src/gprs/gprs_gmm.c
@@ -31,6 +31,8 @@
 #include <arpa/inet.h>
 #include <netdb.h>
 
+#include <openssl/rand.h>
+
 #include <openbsc/db.h>
 #include <osmocom/core/msgb.h>
 #include <osmocom/gsm/tlv.h>
@@ -417,16 +419,16 @@
 }
 
 /* Section 9.4.9: Authentication and Ciphering Request */
-static int gsm48_tx_gmm_auth_ciph_req(struct sgsn_mm_ctx *mm, uint8_t *rand,
+static int gsm48_tx_gmm_auth_ciph_req(struct sgsn_mm_ctx *mm, uint8_t *rnd,
 				      uint8_t key_seq, uint8_t algo)
 {
 	struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 AUTH CIPH REQ");
 	struct gsm48_hdr *gh;
 	struct gsm48_auth_ciph_req *acreq;
-	uint8_t *m_rand, *m_cksn;
+	uint8_t *m_rand, *m_cksn, rbyte;
 
 	LOGMMCTXP(LOGL_INFO, mm, "<- GPRS AUTH AND CIPHERING REQ (rand = %s)\n",
-		osmo_hexdump(rand, 16));
+		osmo_hexdump(rnd, 16));
 
 	mmctx2msgid(msg, mm);
 
@@ -438,13 +440,20 @@
 	acreq->ciph_alg = algo & 0xf;
 	acreq->imeisv_req = 0x1;
 	acreq->force_stby = 0x0;
-	acreq->ac_ref_nr = 0x0;	/* FIXME: increment this? */
+	/* 3GPP TS 24.008 § 10.5.5.19: */
+	if (RAND_bytes(&rbyte, 1) != 1) {
+		LOGP(DMM, LOGL_NOTICE, "RAND_bytes failed for A&C ref, falling "
+		     "back to rand()\n");
+		acreq->ac_ref_nr = rand();
+	} else
+		acreq->ac_ref_nr = rbyte;
+	mm->ac_ref_nr_used = acreq->ac_ref_nr;
 
 	/* Only if authentication is requested we need to set RAND + CKSN */
-	if (rand) {
+	if (rnd) {
 		m_rand = msgb_put(msg, 16+1);
 		m_rand[0] = GSM48_IE_GMM_AUTH_RAND;
-		memcpy(m_rand+1, rand, 16);
+		memcpy(m_rand + 1, rnd, 16);
 
 		m_cksn = msgb_put(msg, 1);
 		m_cksn[0] = (GSM48_IE_GMM_CIPH_CKSN << 4) | (key_seq & 0x07);
@@ -490,14 +499,19 @@
 		return 0;
 	}
 
+	if (acr->ac_ref_nr != ctx->ac_ref_nr_used) {
+		LOGMMCTXP(LOGL_NOTICE, ctx, "Reference mismatch for Auth & Ciph"
+			  " Response: %u received, %u expected\n",
+			  acr->ac_ref_nr, ctx->ac_ref_nr_used);
+		return 0;
+	}
+
 	/* Stop T3360 */
 	mmctx_timer_stop(ctx, 3360);
 
 	tlv_parse(&tp, &gsm48_gmm_att_tlvdef, acr->data,
 			(msg->data + msg->len) - acr->data, 0, 0);
 
-	/* FIXME: compare ac_ref? */
-
 	if (!TLVP_PRESENT(&tp, GSM48_IE_GMM_AUTH_SRES) ||
 	    !TLVP_PRESENT(&tp, GSM48_IE_GMM_IMEISV)) {
 		/* TODO: missing mandatory IE, return STATUS or REJ? */
diff --git a/openbsc/src/gprs/sgsn_main.c b/openbsc/src/gprs/sgsn_main.c
index c852840..4475136 100644
--- a/openbsc/src/gprs/sgsn_main.c
+++ b/openbsc/src/gprs/sgsn_main.c
@@ -21,6 +21,7 @@
 
 #include <unistd.h>
 #include <stdio.h>
+#include <time.h>
 #include <stdlib.h>
 #include <string.h>
 #include <getopt.h>
@@ -306,6 +307,7 @@
 	struct gsm_network dummy_network;
 	int rc;
 
+	srand(time(NULL));
 	tall_bsc_ctx = talloc_named_const(NULL, 0, "osmo_sgsn");
 	tall_msgb_ctx = talloc_named_const(tall_bsc_ctx, 0, "msgb");
 
diff --git a/openbsc/tests/sgsn/Makefile.am b/openbsc/tests/sgsn/Makefile.am
index e3cec50..b6036c7 100644
--- a/openbsc/tests/sgsn/Makefile.am
+++ b/openbsc/tests/sgsn/Makefile.am
@@ -7,6 +7,7 @@
 
 sgsn_test_SOURCES = sgsn_test.c
 sgsn_test_LDFLAGS = \
+	-Wl,--wrap=RAND_bytes \
 	-Wl,--wrap=sgsn_update_subscriber_data \
 	-Wl,--wrap=gprs_subscr_request_update_location \
 	-Wl,--wrap=gprs_subscr_request_auth_info \
diff --git a/openbsc/tests/sgsn/sgsn_test.c b/openbsc/tests/sgsn/sgsn_test.c
index d568807..e8e05f6 100644
--- a/openbsc/tests/sgsn/sgsn_test.c
+++ b/openbsc/tests/sgsn/sgsn_test.c
@@ -101,6 +101,25 @@
 	return 0;
 }
 
+/* override, requires '-Wl,--wrap=RAND_bytes' */
+int __real_RAND_bytes(unsigned char *buf, int num);
+int mock_RAND_bytes(unsigned char *buf, int num);
+int (*RAND_bytes_cb)(unsigned char *, int) =
+  &mock_RAND_bytes;
+
+int __wrap_RAND_bytes(unsigned char *buf, int num)
+{
+	return (*RAND_bytes_cb)(buf, num);
+}
+/* make results of A&C ref predictable */
+int mock_RAND_bytes(unsigned char *buf, int num)
+{
+	if (num > 1)
+		return __real_RAND_bytes(buf, num);
+	buf[0] = 0;
+	return 1;
+}
+
 /* override, requires '-Wl,--wrap=sgsn_update_subscriber_data' */
 void __real_sgsn_update_subscriber_data(struct sgsn_mm_ctx *);
 void (*update_subscriber_data_cb)(struct sgsn_mm_ctx *) =