aboutsummaryrefslogtreecommitdiffstats
path: root/src/db_auc.c
blob: eae5070de5bc37390ff7e9d0e14a97d31b26949b (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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
/* (C) 2015 by Harald Welte <laforge@gnumonks.org>
 *
 * 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 <string.h>
#include <inttypes.h>
#include <errno.h>

#include <osmocom/core/utils.h>
#include <osmocom/crypt/auth.h>

#include <sqlite3.h>

#include "logging.h"
#include "db.h"
#include "auc.h"
#include "rand.h"

#define LOGAUC(imsi, level, fmt, args ...)	LOGP(DAUC, level, "IMSI='%s': " fmt, imsi, ## args)

/* update the SQN for a given subscriber ID */
int db_update_sqn(struct db_context *dbc, int64_t subscr_id, uint64_t new_sqn)
{
	sqlite3_stmt *stmt = dbc->stmt[DB_STMT_AUC_UPD_SQN];
	int rc;
	int ret = 0;

	if (!db_bind_int64(stmt, "$sqn", new_sqn))
		return -EIO;

	if (!db_bind_int64(stmt, "$subscriber_id", subscr_id))
		return -EIO;

	/* execute the statement */
	rc = sqlite3_step(stmt);
	if (rc != SQLITE_DONE) {
		LOGP(DAUC, LOGL_ERROR, "Cannot update SQN for subscriber ID=%"PRId64
		     ": SQL error: (%d) %s\n",
		     subscr_id, rc, sqlite3_errmsg(dbc->db));
		ret = -EIO;
		goto out;
	}

	/* verify execution result */
	rc = sqlite3_changes(dbc->db);
	if (!rc) {
		LOGP(DAUC, LOGL_ERROR, "Cannot update SQN for subscriber ID=%"PRId64
		     ": no auc_3g entry for such subscriber\n", subscr_id);
		ret = -ENOENT;
	} else if (rc != 1) {
		LOGP(DAUC, LOGL_ERROR, "Update SQN for subscriber ID=%"PRId64
		     ": SQL modified %d rows (expected 1)\n", subscr_id, rc);
		ret = -EIO;
	}

out:
	db_remove_reset(stmt);
	return ret;
}

/* obtain the authentication data for a given imsi
 * returns -1 in case of error, 0 for unknown IMSI, 1 for success */
int db_get_auth_data(struct db_context *dbc, const char *imsi,
		     struct osmo_sub_auth_data *aud2g,
		     struct osmo_sub_auth_data *aud3g,
		     int64_t *subscr_id)
{
	sqlite3_stmt *stmt = dbc->stmt[DB_STMT_AUC_BY_IMSI];
	int ret = 0;
	int rc;

	memset(aud2g, 0, sizeof(*aud2g));
	memset(aud3g, 0, sizeof(*aud3g));

	/* bind the IMSI value */
	rc = sqlite3_bind_text(stmt, 1, imsi, -1,
				SQLITE_STATIC);
	if (rc != SQLITE_OK) {
		LOGAUC(imsi, LOGL_ERROR, "Error binding IMSI: %d\n", rc);
		ret = -EIO;
		goto out;
	}

	/* execute the statement */
	rc = sqlite3_step(stmt);
	if (rc == SQLITE_DONE) {
		LOGAUC(imsi, LOGL_INFO, "No such subscriber\n");
		ret = -ENOENT;
		goto out;
	} else if (rc != SQLITE_ROW) {
		LOGAUC(imsi, LOGL_ERROR, "Error executing SQL: %d\n", rc);
		ret = -EIO;
		goto out;
	}

	/* as an optimization, we retrieve the subscriber ID, to ensure we can
	 * update the SQN later without having to go back via a JOIN with the
	 * subscriber table. */
	if (subscr_id)
		*subscr_id = sqlite3_column_int64(stmt, 0);

	/* obtain result values using sqlite3_column_*() */
	if (sqlite3_column_type(stmt, 1) == SQLITE_INTEGER) {
		/* we do have some 2G authentication data */
		const uint8_t *ki;

		aud2g->algo = sqlite3_column_int(stmt, 1);
		ki = sqlite3_column_text(stmt, 2);
#if 0
		if (sqlite3_column_bytes(stmt, 2) != sizeof(aud2g->u.gsm.ki)) {
			LOGAUC(imsi, LOGL_ERROR, "Error reading Ki: %d\n", rc);
			goto end_2g;
		}
#endif
		osmo_hexparse((void*)ki, (void*)&aud2g->u.gsm.ki, sizeof(aud2g->u.gsm.ki));
		aud2g->type = OSMO_AUTH_TYPE_GSM;
	} else
		LOGAUC(imsi, LOGL_DEBUG, "No 2G Auth Data\n");
//end_2g:
	if (sqlite3_column_type(stmt, 3) == SQLITE_INTEGER) {
		/* we do have some 3G authentication data */
		const uint8_t *k, *op, *opc;

		aud3g->algo = sqlite3_column_int(stmt, 3);
		k = sqlite3_column_text(stmt, 4);
		if (!k) {
			LOGAUC(imsi, LOGL_ERROR, "Error reading K: %d\n", rc);
			ret = -EIO;
			goto out;
		}
		osmo_hexparse((void*)k, (void*)&aud3g->u.umts.k, sizeof(aud3g->u.umts.k));
		/* UMTS Subscribers can have either OP or OPC */
		op = sqlite3_column_text(stmt, 5);
		if (!op) {
			opc = sqlite3_column_text(stmt, 6);
			if (!opc) {
				LOGAUC(imsi, LOGL_ERROR, "Error reading OPC: %d\n", rc);
				ret = -EIO;
				goto out;
			}
			osmo_hexparse((void*)opc, (void*)&aud3g->u.umts.opc,
					sizeof(aud3g->u.umts.opc));
			aud3g->u.umts.opc_is_op = 0;
		} else {
			osmo_hexparse((void*)op, (void*)&aud3g->u.umts.opc,
					sizeof(aud3g->u.umts.opc));
			aud3g->u.umts.opc_is_op = 1;
		}
		aud3g->u.umts.sqn = sqlite3_column_int64(stmt, 7);
		aud3g->u.umts.ind_bitlen = sqlite3_column_int(stmt, 8);
		/* FIXME: amf? */
		aud3g->type = OSMO_AUTH_TYPE_UMTS;
	} else
		LOGAUC(imsi, LOGL_DEBUG, "No 3G Auth Data\n");

	if (aud2g->type == 0 && aud3g->type == 0)
		ret = -ENOENT;

out:
	db_remove_reset(stmt);
	return ret;
}

/* return -1 in case of error, 0 for unknown imsi, positive for number
 * of vectors generated */
int db_get_auc(struct db_context *dbc, const char *imsi,
	       unsigned int auc_3g_ind, struct osmo_auth_vector *vec,
	       unsigned int num_vec, const uint8_t *rand_auts,
	       const uint8_t *auts)
{
	struct osmo_sub_auth_data aud2g, aud3g;
	int64_t subscr_id;
	int ret = 0;
	int rc;

	rc = db_get_auth_data(dbc, imsi, &aud2g, &aud3g, &subscr_id);
	if (rc)
		return rc;

	aud3g.u.umts.ind = auc_3g_ind;
	if (aud3g.type == OSMO_AUTH_TYPE_UMTS
	    && aud3g.u.umts.ind >= (1U << aud3g.u.umts.ind_bitlen)) {
		LOGAUC(imsi, LOGL_NOTICE, "3G auth: SQN's IND bitlen %u is"
		       " too small to hold an index of %u. Truncating. This"
		       " may cause numerous additional AUTS resyncing.\n",
		       aud3g.u.umts.ind_bitlen, aud3g.u.umts.ind);
		aud3g.u.umts.ind &= (1U << aud3g.u.umts.ind_bitlen) - 1;
	}

	LOGAUC(imsi, LOGL_DEBUG, "Calling to generate %u vectors\n", num_vec);
	rc = auc_compute_vectors(vec, num_vec, &aud2g, &aud3g, rand_auts, auts);
	if (rc < 0) {
		num_vec = 0;
		ret = -1;
	} else {
		num_vec = rc;
		ret = num_vec;
	}
	LOGAUC(imsi, LOGL_INFO, "Generated %u vectors\n", num_vec);

	/* Update SQN in database, as needed */
	if (aud3g.algo) {
		LOGAUC(imsi, LOGL_DEBUG, "Updating SQN=%" PRIu64 " in DB\n",
		       aud3g.u.umts.sqn);
		rc = db_update_sqn(dbc, subscr_id, aud3g.u.umts.sqn);
		/* don't tell caller we generated any triplets in case of
		 * update error */
		if (rc < 0) {
			LOGAUC(imsi, LOGL_ERROR, "Error updating SQN: %d\n", rc);
			num_vec = 0;
			ret = -1;
		}
	}

	return ret;
}