osmo-msc/openbsc/src/libmsc/auth.c

147 lines
3.8 KiB
C

/* Authentication related functions */
/*
* (C) 2010 by Sylvain Munaut <tnt@246tNt.com>
*
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <openbsc/db.h>
#include <openbsc/debug.h>
#include <openbsc/auth.h>
#include <openbsc/gsm_data.h>
#include <osmocom/gsm/comp128.h>
#include <openssl/rand.h>
#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)
{
int i, l = ainfo->a3a8_ki_len;
if ((l > A38_XOR_MAX_KEY_LEN) || (l < A38_XOR_MIN_KEY_LEN)) {
LOGP(DMM, LOGL_ERROR, "Invalid XOR key (len=%d) %s\n",
ainfo->a3a8_ki_len,
osmo_hexdump(ainfo->a3a8_ki, ainfo->a3a8_ki_len));
return -1;
}
for (i=0; i<4; i++)
atuple->sres[i] = atuple->rand[i] ^ ainfo->a3a8_ki[i];
for (i=4; i<12; i++)
atuple->kc[i-4] = atuple->rand[i] ^ ainfo->a3a8_ki[i];
return 0;
}
static int
_use_comp128_v1(struct gsm_auth_info *ainfo, struct gsm_auth_tuple *atuple)
{
if (ainfo->a3a8_ki_len != A38_COMP128_KEY_LEN) {
LOGP(DMM, LOGL_ERROR, "Invalid COMP128v1 key (len=%d) %s\n",
ainfo->a3a8_ki_len,
osmo_hexdump(ainfo->a3a8_ki, ainfo->a3a8_ki_len));
return -1;
}
comp128(ainfo->a3a8_ki, atuple->rand, atuple->sres, atuple->kc);
return 0;
}
/* Return values
* -1 -> Internal error
* 0 -> Not available
* 1 -> Tuple returned, need to do auth, then enable cipher
* 2 -> Tuple returned, need to enable cipher
*/
int auth_get_tuple_for_subscr(struct gsm_auth_tuple *atuple,
struct gsm_subscriber *subscr, int key_seq)
{
struct gsm_auth_info ainfo;
int rc;
/* Get subscriber info (if any) */
rc = db_get_authinfo_for_subscr(&ainfo, subscr);
if (rc < 0) {
LOGP(DMM, LOGL_NOTICE,
"No retrievable Ki for subscriber, skipping auth\n");
return rc == -ENOENT ? AUTH_NOT_AVAIL : -1;
}
/* 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) &&
(atuple->use_count < 3))
{
atuple->use_count++;
db_sync_lastauthtuple_for_subscr(atuple, subscr);
DEBUGP(DMM, "Auth tuple use < 3, just doing ciphering\n");
return AUTH_DO_CIPH;
}
/* Generate a new one */
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;
}
switch (ainfo.auth_algo) {
case AUTH_ALGO_NONE:
DEBUGP(DMM, "No authentication for subscriber\n");
return 0;
case AUTH_ALGO_XOR:
if (_use_xor(&ainfo, atuple))
return 0;
break;
case AUTH_ALGO_COMP128v1:
if (_use_comp128_v1(&ainfo, atuple))
return 0;
break;
default:
DEBUGP(DMM, "Unsupported auth type algo_id=%d\n",
ainfo.auth_algo);
return 0;
}
db_sync_lastauthtuple_for_subscr(atuple, subscr);
DEBUGP(DMM, "Need to do authentication and ciphering\n");
return AUTH_DO_AUTH_THEN_CIPH;
}