blob: ac3c730d8b9aa6cb785f43c12eef08bcfe2ea3ef [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
Neels Hofmeyre8ccd502017-10-06 04:10:06 +0200262/* Enable or disable PS or CS for a subscriber.
263 * For the subscriber with the given imsi, set nam_ps (when is_ps == true) or
264 * nam_cs (when is_ps == false) to nam_val in the database.
265 * Returns 0 on success, -ENOENT when the given IMSI does not exist, -EINVAL if
266 * the SQL statement could not be composed, -ENOEXEC if running the SQL
267 * statement failed, -EIO if the amount of rows modified is unexpected.
268 */
269int db_subscr_nam(struct db_context *dbc, const char *imsi, bool nam_val, bool is_ps)
Max3ce36862017-02-20 11:18:04 +0100270{
Neels Hofmeyre8ccd502017-10-06 04:10:06 +0200271 sqlite3_stmt *stmt;
Max3ce36862017-02-20 11:18:04 +0100272 int rc;
Neels Hofmeyre8ccd502017-10-06 04:10:06 +0200273 int ret = 0;
Max3ce36862017-02-20 11:18:04 +0100274
Neels Hofmeyre8ccd502017-10-06 04:10:06 +0200275 stmt = dbc->stmt[is_ps ? DB_STMT_UPD_NAM_PS_BY_IMSI
276 : DB_STMT_UPD_NAM_CS_BY_IMSI];
Max3ce36862017-02-20 11:18:04 +0100277
Neels Hofmeyre8ccd502017-10-06 04:10:06 +0200278 if (!db_bind_text(stmt, "$imsi", imsi))
279 return -EIO;
280 if (!db_bind_int(stmt, "$val", nam_val ? 1 : 0))
281 return -EIO;
282
283 /* execute the statement */
284 rc = sqlite3_step(stmt);
Max3ce36862017-02-20 11:18:04 +0100285 if (rc != SQLITE_DONE) {
Neels Hofmeyre8ccd502017-10-06 04:10:06 +0200286 LOGHLR(imsi, LOGL_ERROR, "%s %s: SQL error: %s\n",
287 nam_val ? "enable" : "disable",
288 is_ps ? "PS" : "CS",
289 sqlite3_errmsg(dbc->db));
290 ret = -EIO;
291 goto out;
Max3ce36862017-02-20 11:18:04 +0100292 }
293
Neels Hofmeyre8ccd502017-10-06 04:10:06 +0200294 /* verify execution result */
295 rc = sqlite3_changes(dbc->db);
296 if (!rc) {
297 LOGP(DAUC, LOGL_ERROR, "Cannot %s %s: no such subscriber: IMSI='%s'\n",
298 nam_val ? "enable" : "disable",
299 is_ps ? "PS" : "CS",
300 imsi);
301 ret = -ENOENT;
302 goto out;
303 } else if (rc != 1) {
304 LOGHLR(imsi, LOGL_ERROR, "%s %s: SQL modified %d rows (expected 1)\n",
305 nam_val ? "enable" : "disable",
306 is_ps ? "PS" : "CS",
Max3ce36862017-02-20 11:18:04 +0100307 rc);
Neels Hofmeyre8ccd502017-10-06 04:10:06 +0200308 ret = -EIO;
Max3ce36862017-02-20 11:18:04 +0100309 }
310
Neels Hofmeyre8ccd502017-10-06 04:10:06 +0200311out:
Max3ce36862017-02-20 11:18:04 +0100312 db_remove_reset(stmt);
Neels Hofmeyre8ccd502017-10-06 04:10:06 +0200313 return ret;
Max3ce36862017-02-20 11:18:04 +0100314}
315
Neels Hofmeyrdd783052017-10-09 17:36:08 +0200316int db_subscr_lu(struct db_context *dbc, int64_t subscr_id,
317 const char *vlr_or_sgsn_number, bool is_ps)
Harald Weltee687be52016-05-03 18:49:27 +0200318{
Neels Hofmeyrdd783052017-10-09 17:36:08 +0200319 sqlite3_stmt *stmt;
Harald Weltee687be52016-05-03 18:49:27 +0200320 int rc, ret = 0;
321
Neels Hofmeyrdd783052017-10-09 17:36:08 +0200322 stmt = dbc->stmt[is_ps ? DB_STMT_UPD_SGSN_BY_ID
323 : DB_STMT_UPD_VLR_BY_ID];
Harald Weltee687be52016-05-03 18:49:27 +0200324
Neels Hofmeyrdd783052017-10-09 17:36:08 +0200325 if (!db_bind_int64(stmt, "$subscriber_id", subscr_id))
326 return -EIO;
Harald Weltee687be52016-05-03 18:49:27 +0200327
Neels Hofmeyrdd783052017-10-09 17:36:08 +0200328 if (!db_bind_text(stmt, "$number", vlr_or_sgsn_number))
329 return -EIO;
Harald Weltee687be52016-05-03 18:49:27 +0200330
331 /* execute the statement */
332 rc = sqlite3_step(stmt);
333 if (rc != SQLITE_DONE) {
Neels Hofmeyrdd783052017-10-09 17:36:08 +0200334 LOGP(DAUC, LOGL_ERROR, "Update %s number for subscriber ID=%"PRId64": SQL Error: %s\n",
335 is_ps? "SGSN" : "VLR", subscr_id, sqlite3_errmsg(dbc->db));
336 ret = -EIO;
337 goto out;
Harald Weltee687be52016-05-03 18:49:27 +0200338 }
Neels Hofmeyrdd783052017-10-09 17:36:08 +0200339
340 /* verify execution result */
341 rc = sqlite3_changes(dbc->db);
342 if (!rc) {
343 LOGP(DAUC, LOGL_ERROR, "Cannot update %s number for subscriber ID=%"PRId64
344 ": no such subscriber\n",
345 is_ps? "SGSN" : "VLR", subscr_id);
346 ret = -ENOENT;
347 } else if (rc != 1) {
348 LOGP(DAUC, LOGL_ERROR, "Update %s number for subscriber ID=%"PRId64
349 ": SQL modified %d rows (expected 1)\n",
350 is_ps? "SGSN" : "VLR", subscr_id, rc);
351 ret = -EIO;
352 }
353
Harald Weltee687be52016-05-03 18:49:27 +0200354out:
Max00b37152017-02-20 11:09:27 +0100355 db_remove_reset(stmt);
Harald Weltee687be52016-05-03 18:49:27 +0200356 return ret;
357}
Harald Welteb18f0e02016-05-05 21:03:03 +0200358
359int db_subscr_purge(struct db_context *dbc, const char *imsi, bool is_ps)
360{
Neels Hofmeyr4bde9492017-10-06 03:09:34 +0200361 sqlite3_stmt *stmt = dbc->stmt[DB_STMT_UPD_VLR_BY_ID];
Harald Welteb18f0e02016-05-05 21:03:03 +0200362 int rc, ret = 1;
363
364 if (is_ps)
Neels Hofmeyr4bde9492017-10-06 03:09:34 +0200365 stmt = dbc->stmt[DB_STMT_UPD_PURGE_PS_BY_IMSI];
Harald Welteb18f0e02016-05-05 21:03:03 +0200366 else
Neels Hofmeyr4bde9492017-10-06 03:09:34 +0200367 stmt = dbc->stmt[DB_STMT_UPD_PURGE_CS_BY_IMSI];
Harald Welteb18f0e02016-05-05 21:03:03 +0200368
Neels Hofmeyrf3144592017-10-06 03:40:52 +0200369 if (!db_bind_text(stmt, NULL, imsi))
Max00b37152017-02-20 11:09:27 +0100370 return -EINVAL;
Harald Welteb18f0e02016-05-05 21:03:03 +0200371
372 /* execute the statement */
373 rc = sqlite3_step(stmt);
374 if (rc != SQLITE_DONE) {
375 LOGP(DAUC, LOGL_ERROR, "Error setting Purged: %d\n", rc);
Max00b37152017-02-20 11:09:27 +0100376 ret = -ENOEXEC;
Harald Welteb18f0e02016-05-05 21:03:03 +0200377 }
378 /* FIXME: return 0 in case IMSI not known */
Max00b37152017-02-20 11:09:27 +0100379
380 db_remove_reset(stmt);
Harald Welteb18f0e02016-05-05 21:03:03 +0200381
382 return ret;
383}