aboutsummaryrefslogtreecommitdiffstats
path: root/openbsc/src/auth.c
blob: f57b668efcb41bff219ac03a865badcd8ef2b74b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
/* 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 General Public License as published by
 * the Free Software Foundation; either version 2 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 */

#include <openbsc/db.h>
#include <openbsc/debug.h>
#include <openbsc/gsm_data.h>

#include <osmocore/comp128.h>

#include <stdlib.h>


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)) {
		DEBUGP(DMM, "Invalid XOR key (len=%d) %s",
			ainfo->a3a8_ki_len,
			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=8; 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) {
		DEBUGP(DMM, "Invalid COMP128v1 key (len=%d) %s",
			ainfo->a3a8_ki_len,
			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 i, rc;

	/* Get subscriber info (if any) */
	rc = db_get_authinfo_for_subscr(&ainfo, subscr);
	if (rc < 0) {
		DEBUGP(DMM, "No retrievable Ki for subscriber, skipping auth");
		return rc == -ENOENT ? 0 : -1;
	}

	/* If possible, re-use the last tuple and skip auth */
	rc = db_get_lastauthtuple_for_subscr(atuple, subscr);
	if ((rc == 0) &&
	    (atuple->key_seq != GSM_KEY_SEQ_INVAL) &&
	    (atuple->use_count < 3))
	{
		atuple->use_count++;
		db_sync_lastauthtuple_for_subscr(atuple, subscr);
		return 2;
	}

	/* Generate a new one */
	atuple->use_count = 1;
	atuple->key_seq = (atuple->key_seq + 1) % 7;
        for (i=0; i<sizeof(atuple->rand); i++)
                atuple->rand[i] = random() & 0xff;

	switch (ainfo.auth_algo) {
		case AUTH_ALGO_NONE:
			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);

	return 1;
}