vlr: implement fallback to no-auth

When the HLR fails to return auth info and authentication and ciphering
are configured to be optional, fall back to no-auth.

This patch concludes a series of preparatory patches and implements the
actual functional change.

Related: OS#4830
Change-Id: I5feda196fa481dd8a46b0e4721c64b7c6600f0d1
diff --git a/src/libvlr/vlr_access_req_fsm.c b/src/libvlr/vlr_access_req_fsm.c
index 2c9009e..629625e 100644
--- a/src/libvlr/vlr_access_req_fsm.c
+++ b/src/libvlr/vlr_access_req_fsm.c
@@ -40,6 +40,7 @@
 	OSMO_VALUE_STRING(PR_ARQ_E_START),
 	OSMO_VALUE_STRING(PR_ARQ_E_ID_IMSI),
 	OSMO_VALUE_STRING(PR_ARQ_E_AUTH_RES),
+	OSMO_VALUE_STRING(PR_ARQ_E_AUTH_NO_INFO),
 	OSMO_VALUE_STRING(PR_ARQ_E_AUTH_FAILURE),
 	OSMO_VALUE_STRING(PR_ARQ_E_CIPH_RES),
 	OSMO_VALUE_STRING(PR_ARQ_E_UPD_LOC_RES),
@@ -289,7 +290,10 @@
 
 	LOGPFSM(fi, "%s()\n", __func__);
 
-	if (!is_cmc_smc_to_be_attempted(par)) {
+	/* Continue with ciphering, if enabled.
+	 * If auth/ciph is optional and the HLR returned no auth info, continue without ciphering. */
+	if (!is_cmc_smc_to_be_attempted(par)
+	    || (vsub->sec_ctx == VLR_SEC_CTX_NONE && !par->is_ciphering_required)) {
 		_proc_arq_vlr_node2_post_ciph(fi);
 		return;
 	}
@@ -345,7 +349,7 @@
 					0, 0);
 		vsub->auth_fsm = auth_fsm_start(vsub, fi,
 						PR_ARQ_E_AUTH_RES,
-						PR_ARQ_E_AUTH_FAILURE,
+						PR_ARQ_E_AUTH_NO_INFO,
 						PR_ARQ_E_AUTH_FAILURE,
 						par->is_r99,
 						par->is_utran);
@@ -439,6 +443,7 @@
 static void proc_arq_vlr_fn_w_auth(struct osmo_fsm_inst *fi,
 				   uint32_t event, void *data)
 {
+	struct proc_arq_priv *par = fi->priv;
 	enum gsm48_reject_value *cause = data;
 
 	switch (event) {
@@ -448,7 +453,19 @@
 		return;
 
 	case PR_ARQ_E_AUTH_FAILURE:
-		proc_arq_fsm_done(fi, cause? *cause : GSM48_REJECT_NETWORK_FAILURE);
+		proc_arq_fsm_done(fi, cause ? *cause : GSM48_REJECT_NETWORK_FAILURE);
+		return;
+
+	case PR_ARQ_E_AUTH_NO_INFO:
+		/* HLR returned no auth info for the subscriber. Continue only if authentication is optional. */
+		if (par->authentication_required) {
+			proc_arq_fsm_done(fi, cause ? *cause : GSM48_REJECT_NETWORK_FAILURE);
+			return;
+		}
+		LOGPFSML(fi, LOGL_INFO,
+			 "Attaching subscriber without auth (auth is optional, and no auth info received from HLR)\n");
+		/* Node 2 */
+		_proc_arq_vlr_node2(fi);
 		return;
 
 	default:
@@ -559,6 +576,7 @@
 	[PR_ARQ_S_WAIT_AUTH] = {
 		.name = OSMO_STRINGIFY(PR_ARQ_S_WAIT_AUTH),
 		.in_event_mask = S(PR_ARQ_E_AUTH_RES) |
+				 S(PR_ARQ_E_AUTH_NO_INFO) |
 				 S(PR_ARQ_E_AUTH_FAILURE),
 		.out_state_mask = S(PR_ARQ_S_DONE) |
 				  S(PR_ARQ_S_WAIT_CIPH) |
diff --git a/src/libvlr/vlr_lu_fsm.c b/src/libvlr/vlr_lu_fsm.c
index a65421c..7500c86 100644
--- a/src/libvlr/vlr_lu_fsm.c
+++ b/src/libvlr/vlr_lu_fsm.c
@@ -643,6 +643,7 @@
 	OSMO_VALUE_STRING(VLR_ULA_E_SEND_ID_ACK),
 	OSMO_VALUE_STRING(VLR_ULA_E_SEND_ID_NACK),
 	OSMO_VALUE_STRING(VLR_ULA_E_AUTH_SUCCESS),
+	OSMO_VALUE_STRING(VLR_ULA_E_AUTH_NO_INFO),
 	OSMO_VALUE_STRING(VLR_ULA_E_AUTH_FAILURE),
 	OSMO_VALUE_STRING(VLR_ULA_E_CIPH_RES),
 	OSMO_VALUE_STRING(VLR_ULA_E_ID_IMSI),
@@ -861,7 +862,10 @@
 
 	OSMO_ASSERT(vsub);
 
-	if (!is_cmc_smc_to_be_attempted(lfp)) {
+	/* Continue with ciphering, if enabled.
+	 * If auth/ciph is optional and the HLR returned no auth info, continue without ciphering. */
+	if (!is_cmc_smc_to_be_attempted(lfp)
+	    || (vsub->sec_ctx == VLR_SEC_CTX_NONE && !lfp->is_ciphering_required)) {
 		vlr_loc_upd_post_ciph(fi);
 		return;
 	}
@@ -913,7 +917,7 @@
 		vsub->auth_fsm = auth_fsm_start(lfp->vsub,
 						fi,
 						VLR_ULA_E_AUTH_SUCCESS,
-						VLR_ULA_E_AUTH_FAILURE,
+						VLR_ULA_E_AUTH_NO_INFO,
 						VLR_ULA_E_AUTH_FAILURE,
 						lfp->is_r99,
 						lfp->is_utran);
@@ -1156,7 +1160,18 @@
 		return;
 
 	case VLR_ULA_E_AUTH_FAILURE:
-		lu_fsm_failure(fi, res? *res : GSM48_REJECT_NETWORK_FAILURE);
+		lu_fsm_failure(fi, res ? *res : GSM48_REJECT_NETWORK_FAILURE);
+		return;
+
+	case VLR_ULA_E_AUTH_NO_INFO:
+		/* HLR returned no auth info for the subscriber. Continue only if authentication is optional. */
+		if (lfp->authentication_required || lfp->is_ciphering_required) {
+			lu_fsm_failure(fi, res ? *res : GSM48_REJECT_NETWORK_FAILURE);
+			return;
+		}
+		LOGPFSML(fi, LOGL_INFO,
+			 "Attaching subscriber without auth (auth is optional, and no auth info received from HLR)\n");
+		vlr_loc_upd_post_auth(fi);
 		return;
 
 	default:
@@ -1377,6 +1392,7 @@
 	},
 	[VLR_ULA_S_WAIT_AUTH] = {
 		.in_event_mask = S(VLR_ULA_E_AUTH_SUCCESS) |
+				 S(VLR_ULA_E_AUTH_NO_INFO) |
 				 S(VLR_ULA_E_AUTH_FAILURE),
 		.out_state_mask = S(VLR_ULA_S_WAIT_CIPH) |
 				  S(VLR_ULA_S_WAIT_LU_COMPL) |