blob: 8df4bcb4d4c5df67e7d06a3d940e93d126f6f05e [file] [log] [blame]
Harald Weltee687be52016-05-03 18:49:27 +02001/* (C) 2015 by Harald Welte <laforge@gnumonks.org>
2 *
3 * All Rights Reserved
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU Affero General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU Affero General Public License for more details.
14 *
15 * You should have received a copy of the GNU Affero General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 *
18 */
19
20#include <string.h>
Max00b37152017-02-20 11:09:27 +010021#include <errno.h>
Neels Hofmeyrf7c3e6e2017-10-09 17:55:16 +020022#include <inttypes.h>
Harald Weltee687be52016-05-03 18:49:27 +020023
24#include <osmocom/core/utils.h>
25#include <osmocom/crypt/auth.h>
Neels Hofmeyrf7c3e6e2017-10-09 17:55:16 +020026#include <osmocom/gsm/gsm23003.h>
Harald Weltee687be52016-05-03 18:49:27 +020027
28#include <sqlite3.h>
29
30#include "logging.h"
31#include "db.h"
32
Neels Hofmeyr40aa61c2017-10-09 17:56:04 +020033#define LOGHLR(imsi, level, fmt, args ...) LOGP(DAUC, level, "IMSI='%s': " fmt, imsi, ## args)
Harald Weltee687be52016-05-03 18:49:27 +020034
Neels Hofmeyr1e31d182017-10-10 23:20:09 +020035#define SL3_TXT(x, stmt, idx) \
36 do { \
37 const char *_txt = (const char *) sqlite3_column_text(stmt, idx);\
38 if (_txt) \
39 strncpy(x, _txt, sizeof(x)); \
40 x[sizeof(x)-1] = '\0'; \
41 } while (0)
Harald Weltee687be52016-05-03 18:49:27 +020042
Neels Hofmeyrf7c3e6e2017-10-09 17:55:16 +020043int db_subscr_create(struct db_context *dbc, const char *imsi)
44{
45 sqlite3_stmt *stmt;
46 int rc;
47
48 if (!osmo_imsi_str_valid(imsi)) {
49 LOGP(DAUC, LOGL_ERROR, "Cannot create subscriber: invalid IMSI: '%s'\n",
50 imsi);
51 return -EINVAL;
52 }
53
54 stmt = dbc->stmt[DB_STMT_SUBSCR_CREATE];
55
56 if (!db_bind_text(stmt, "$imsi", imsi))
57 return -EIO;
58
59 /* execute the statement */
60 rc = sqlite3_step(stmt);
61 db_remove_reset(stmt);
62 if (rc != SQLITE_DONE) {
63 LOGHLR(imsi, LOGL_ERROR, "Cannot create subscriber: SQL error: (%d) %s\n",
64 rc, sqlite3_errmsg(dbc->db));
65 return -EIO;
66 }
67
68 return 0;
69}
70
71int db_subscr_delete_by_id(struct db_context *dbc, int64_t subscr_id)
72{
73 int rc;
74 int ret = 0;
75
76 sqlite3_stmt *stmt = dbc->stmt[DB_STMT_DEL_BY_ID];
77
78 if (!db_bind_int64(stmt, "$subscriber_id", subscr_id))
79 return -EIO;
80
81 /* execute the statement */
82 rc = sqlite3_step(stmt);
83 if (rc != SQLITE_DONE) {
84 LOGP(DAUC, LOGL_ERROR,
85 "Cannot delete subscriber ID=%"PRId64": SQL error: (%d) %s\n",
86 subscr_id, rc, sqlite3_errmsg(dbc->db));
87 db_remove_reset(stmt);
88 return -EIO;
89 }
90
91 /* verify execution result */
92 rc = sqlite3_changes(dbc->db);
93 if (!rc) {
94 LOGP(DAUC, LOGL_ERROR, "Cannot delete: no such subscriber: ID=%"PRId64"\n",
95 subscr_id);
96 ret = -ENOENT;
97 } else if (rc != 1) {
98 LOGP(DAUC, LOGL_ERROR, "Delete subscriber ID=%"PRId64
99 ": SQL modified %d rows (expected 1)\n", subscr_id, rc);
100 ret = -EIO;
101 }
102
103 /* FIXME: also remove authentication data from auc_2g and auc_3g */
104
105 db_remove_reset(stmt);
106 return ret;
107}
108
109int db_subscr_update_msisdn_by_imsi(struct db_context *dbc, const char *imsi,
110 const char *msisdn)
111{
112 int rc;
113 int ret = 0;
114
115 if (!osmo_msisdn_str_valid(msisdn)) {
116 LOGHLR(imsi, LOGL_ERROR,
117 "Cannot update subscriber: invalid MSISDN: '%s'\n",
118 msisdn);
119 return -EINVAL;
120 }
121
122 sqlite3_stmt *stmt = dbc->stmt[DB_STMT_SET_MSISDN_BY_IMSI];
123
124 if (!db_bind_text(stmt, "$imsi", imsi))
125 return -EIO;
126 if (!db_bind_text(stmt, "$msisdn", msisdn))
127 return -EIO;
128
129 /* execute the statement */
130 rc = sqlite3_step(stmt);
131 if (rc != SQLITE_DONE) {
132 LOGHLR(imsi, LOGL_ERROR,
133 "Cannot update subscriber's MSISDN: SQL error: (%d) %s\n",
134 rc, sqlite3_errmsg(dbc->db));
135 ret = -EIO;
136 goto out;
137 }
138
139 /* verify execution result */
140 rc = sqlite3_changes(dbc->db);
141 if (!rc) {
142 LOGP(DAUC, LOGL_ERROR, "Cannot update MSISDN: no such subscriber: IMSI='%s'\n",
143 imsi);
144 ret = -ENOENT;
145 goto out;
146 } else if (rc != 1) {
147 LOGHLR(imsi, LOGL_ERROR, "Update MSISDN: SQL modified %d rows (expected 1)\n", rc);
148 ret = -EIO;
149 }
150
151out:
152 db_remove_reset(stmt);
153 return ret;
154
155}
156
Neels Hofmeyr9c2bbc82017-10-09 17:30:32 +0200157/* Common code for db_subscr_get_by_*() functions. */
158static int db_sel(struct db_context *dbc, sqlite3_stmt *stmt, struct hlr_subscriber *subscr,
159 const char **err)
Harald Weltee687be52016-05-03 18:49:27 +0200160{
Maxadc66482017-02-20 11:23:20 +0100161 int rc;
Neels Hofmeyr9c2bbc82017-10-09 17:30:32 +0200162 int ret = 0;
Harald Weltee687be52016-05-03 18:49:27 +0200163
164 /* execute the statement */
165 rc = sqlite3_step(stmt);
Neels Hofmeyr9c2bbc82017-10-09 17:30:32 +0200166 if (rc == SQLITE_DONE) {
167 ret = -ENOENT;
168 goto out;
169 }
Harald Weltee687be52016-05-03 18:49:27 +0200170 if (rc != SQLITE_ROW) {
Neels Hofmeyr9c2bbc82017-10-09 17:30:32 +0200171 ret = -EIO;
172 goto out;
Maxadc66482017-02-20 11:23:20 +0100173 }
174
Neels Hofmeyr9c2bbc82017-10-09 17:30:32 +0200175 if (!subscr)
176 goto out;
Harald Weltee687be52016-05-03 18:49:27 +0200177
178 /* obtain the various columns */
179 subscr->id = sqlite3_column_int64(stmt, 0);
180 SL3_TXT(subscr->imsi, stmt, 1);
181 SL3_TXT(subscr->msisdn, stmt, 2);
Harald Welte99909272016-05-05 18:24:15 +0200182 /* FIXME: These should all be BLOBs as they might contain NUL */
Harald Weltee687be52016-05-03 18:49:27 +0200183 SL3_TXT(subscr->vlr_number, stmt, 3);
184 SL3_TXT(subscr->sgsn_number, stmt, 4);
185 SL3_TXT(subscr->sgsn_address, stmt, 5);
186 subscr->periodic_lu_timer = sqlite3_column_int(stmt, 6);
187 subscr->periodic_rau_tau_timer = sqlite3_column_int(stmt, 7);
188 subscr->nam_cs = sqlite3_column_int(stmt, 8);
189 subscr->nam_ps = sqlite3_column_int(stmt, 9);
190 subscr->lmsi = sqlite3_column_int(stmt, 10);
191 subscr->ms_purged_cs = sqlite3_column_int(stmt, 11);
192 subscr->ms_purged_ps = sqlite3_column_int(stmt, 12);
193
Neels Hofmeyr9c2bbc82017-10-09 17:30:32 +0200194out:
Max00b37152017-02-20 11:09:27 +0100195 db_remove_reset(stmt);
Harald Weltee687be52016-05-03 18:49:27 +0200196
Neels Hofmeyr9c2bbc82017-10-09 17:30:32 +0200197 switch (ret) {
198 case 0:
199 *err = NULL;
200 break;
201 case -ENOENT:
202 *err = "No such subscriber";
203 break;
204 default:
205 *err = sqlite3_errmsg(dbc->db);
206 break;
207 }
208 return ret;
209}
210
211int db_subscr_get_by_imsi(struct db_context *dbc, const char *imsi,
212 struct hlr_subscriber *subscr)
213{
214 sqlite3_stmt *stmt = dbc->stmt[DB_STMT_SEL_BY_IMSI];
215 const char *err;
216 int rc;
217
218 if (!db_bind_text(stmt, NULL, imsi))
219 return -EIO;
220
221 rc = db_sel(dbc, stmt, subscr, &err);
222 if (rc)
223 LOGP(DAUC, LOGL_ERROR, "Cannot read subscriber from db: IMSI='%s': %s\n",
224 imsi, err);
225 return rc;
226}
227
228int db_subscr_get_by_msisdn(struct db_context *dbc, const char *msisdn,
229 struct hlr_subscriber *subscr)
230{
231 sqlite3_stmt *stmt = dbc->stmt[DB_STMT_SEL_BY_MSISDN];
232 const char *err;
233 int rc;
234
235 if (!db_bind_text(stmt, NULL, msisdn))
236 return -EIO;
237
238 rc = db_sel(dbc, stmt, subscr, &err);
239 if (rc)
240 LOGP(DAUC, LOGL_ERROR, "Cannot read subscriber from db: MSISDN='%s': %s\n",
241 msisdn, err);
242 return rc;
243}
244
245int db_subscr_get_by_id(struct db_context *dbc, int64_t id,
246 struct hlr_subscriber *subscr)
247{
248 sqlite3_stmt *stmt = dbc->stmt[DB_STMT_SEL_BY_ID];
249 const char *err;
250 int rc;
251
252 if (!db_bind_int64(stmt, NULL, id))
253 return -EIO;
254
255 rc = db_sel(dbc, stmt, subscr, &err);
256 if (rc)
257 LOGP(DAUC, LOGL_ERROR, "Cannot read subscriber from db: ID=%"PRId64": %s\n",
258 id, err);
259 return rc;
Harald Weltee687be52016-05-03 18:49:27 +0200260}
261
Max3ce36862017-02-20 11:18:04 +0100262int db_subscr_ps(struct db_context *dbc, const char *imsi, bool enable)
263{
264 sqlite3_stmt *stmt =
Neels Hofmeyr4bde9492017-10-06 03:09:34 +0200265 dbc->stmt[enable ? DB_STMT_SET_NAM_PS_BY_IMSI : DB_STMT_UNSET_NAM_PS_BY_IMSI];
Max3ce36862017-02-20 11:18:04 +0100266 int rc;
267
Neels Hofmeyrf3144592017-10-06 03:40:52 +0200268 if (!db_bind_text(stmt, NULL, imsi))
Max3ce36862017-02-20 11:18:04 +0100269 return -EINVAL;
270
271 rc = sqlite3_step(stmt); /* execute the statement */
272 if (rc != SQLITE_DONE) {
273 LOGHLR(imsi, LOGL_ERROR, "Error executing SQL: %d\n", rc);
Neels Hofmeyr743cf422017-03-16 20:38:44 +0100274 db_remove_reset(stmt);
275 return -ENOEXEC;
Max3ce36862017-02-20 11:18:04 +0100276 }
277
278 rc = sqlite3_changes(dbc->db); /* verify execution result */
279 if (rc != 1) {
280 LOGHLR(imsi, LOGL_ERROR, "SQL modified %d rows (expected 1)\n",
281 rc);
282 rc = -EINVAL;
283 }
284
285 db_remove_reset(stmt);
286 return rc;
287}
288
Harald Weltee687be52016-05-03 18:49:27 +0200289int db_subscr_lu(struct db_context *dbc,
290 const struct hlr_subscriber *subscr,
291 const char *vlr_or_sgsn_number, bool lu_is_ps)
292{
Neels Hofmeyr4bde9492017-10-06 03:09:34 +0200293 sqlite3_stmt *stmt = dbc->stmt[DB_STMT_UPD_VLR_BY_ID];
Harald Weltee687be52016-05-03 18:49:27 +0200294 const char *txt;
295 int rc, ret = 0;
296
297 if (lu_is_ps) {
Neels Hofmeyr4bde9492017-10-06 03:09:34 +0200298 stmt = dbc->stmt[DB_STMT_UPD_SGSN_BY_ID];
Harald Weltee687be52016-05-03 18:49:27 +0200299 txt = subscr->sgsn_number;
300 } else {
Neels Hofmeyr4bde9492017-10-06 03:09:34 +0200301 stmt = dbc->stmt[DB_STMT_UPD_VLR_BY_ID];
Harald Weltee687be52016-05-03 18:49:27 +0200302 txt = subscr->vlr_number;
303 }
304
305 rc = sqlite3_bind_int64(stmt, 1, subscr->id);
306 if (rc != SQLITE_OK) {
307 LOGP(DAUC, LOGL_ERROR, "Error binding ID: %d\n", rc);
Max00b37152017-02-20 11:09:27 +0100308 return -EINVAL;
Harald Weltee687be52016-05-03 18:49:27 +0200309 }
310
311 rc = sqlite3_bind_text(stmt, 2, txt, -1, SQLITE_STATIC);
312 if (rc != SQLITE_OK) {
313 LOGP(DAUC, LOGL_ERROR, "Error binding VLR/SGSN Number: %d\n", rc);
Max00b37152017-02-20 11:09:27 +0100314 ret = -EBADMSG;
Harald Weltee687be52016-05-03 18:49:27 +0200315 goto out;
316 }
317
318 /* execute the statement */
319 rc = sqlite3_step(stmt);
320 if (rc != SQLITE_DONE) {
321 LOGP(DAUC, LOGL_ERROR, "Error updating SQN: %d\n", rc);
Max00b37152017-02-20 11:09:27 +0100322 ret = -ENOEXEC;
Harald Weltee687be52016-05-03 18:49:27 +0200323 }
324out:
Max00b37152017-02-20 11:09:27 +0100325 db_remove_reset(stmt);
Harald Weltee687be52016-05-03 18:49:27 +0200326
327 return ret;
328}
Harald Welteb18f0e02016-05-05 21:03:03 +0200329
330int db_subscr_purge(struct db_context *dbc, const char *imsi, bool is_ps)
331{
Neels Hofmeyr4bde9492017-10-06 03:09:34 +0200332 sqlite3_stmt *stmt = dbc->stmt[DB_STMT_UPD_VLR_BY_ID];
Harald Welteb18f0e02016-05-05 21:03:03 +0200333 int rc, ret = 1;
334
335 if (is_ps)
Neels Hofmeyr4bde9492017-10-06 03:09:34 +0200336 stmt = dbc->stmt[DB_STMT_UPD_PURGE_PS_BY_IMSI];
Harald Welteb18f0e02016-05-05 21:03:03 +0200337 else
Neels Hofmeyr4bde9492017-10-06 03:09:34 +0200338 stmt = dbc->stmt[DB_STMT_UPD_PURGE_CS_BY_IMSI];
Harald Welteb18f0e02016-05-05 21:03:03 +0200339
Neels Hofmeyrf3144592017-10-06 03:40:52 +0200340 if (!db_bind_text(stmt, NULL, imsi))
Max00b37152017-02-20 11:09:27 +0100341 return -EINVAL;
Harald Welteb18f0e02016-05-05 21:03:03 +0200342
343 /* execute the statement */
344 rc = sqlite3_step(stmt);
345 if (rc != SQLITE_DONE) {
346 LOGP(DAUC, LOGL_ERROR, "Error setting Purged: %d\n", rc);
Max00b37152017-02-20 11:09:27 +0100347 ret = -ENOEXEC;
Harald Welteb18f0e02016-05-05 21:03:03 +0200348 }
349 /* FIXME: return 0 in case IMSI not known */
Max00b37152017-02-20 11:09:27 +0100350
351 db_remove_reset(stmt);
Harald Welteb18f0e02016-05-05 21:03:03 +0200352
353 return ret;
354}