blob: b232dfdb155181f6ed36d5c800e1d917d91f8d6b [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 Hofmeyr518335e2017-10-06 03:20:14 +0200157int db_subscr_get_by_imsi(struct db_context *dbc, const char *imsi,
158 struct hlr_subscriber *subscr)
Harald Weltee687be52016-05-03 18:49:27 +0200159{
Neels Hofmeyr4bde9492017-10-06 03:09:34 +0200160 sqlite3_stmt *stmt = dbc->stmt[DB_STMT_SEL_BY_IMSI];
Maxadc66482017-02-20 11:23:20 +0100161 int rc;
Harald Weltee687be52016-05-03 18:49:27 +0200162
Neels Hofmeyrf3144592017-10-06 03:40:52 +0200163 if (!db_bind_text(stmt, NULL, imsi))
Max00b37152017-02-20 11:09:27 +0100164 return -EINVAL;
Harald Weltee687be52016-05-03 18:49:27 +0200165
166 /* execute the statement */
167 rc = sqlite3_step(stmt);
168 if (rc != SQLITE_ROW) {
169 LOGHLR(imsi, LOGL_ERROR, "Error executing SQL: %d\n", rc);
Maxadc66482017-02-20 11:23:20 +0100170 db_remove_reset(stmt);
171 return -ENOEXEC;
172 }
173
174 if (!subscr) {
175 db_remove_reset(stmt);
176 return 0;
Harald Weltee687be52016-05-03 18:49:27 +0200177 }
178
179 /* obtain the various columns */
180 subscr->id = sqlite3_column_int64(stmt, 0);
181 SL3_TXT(subscr->imsi, stmt, 1);
182 SL3_TXT(subscr->msisdn, stmt, 2);
Harald Welte99909272016-05-05 18:24:15 +0200183 /* FIXME: These should all be BLOBs as they might contain NUL */
Harald Weltee687be52016-05-03 18:49:27 +0200184 SL3_TXT(subscr->vlr_number, stmt, 3);
185 SL3_TXT(subscr->sgsn_number, stmt, 4);
186 SL3_TXT(subscr->sgsn_address, stmt, 5);
187 subscr->periodic_lu_timer = sqlite3_column_int(stmt, 6);
188 subscr->periodic_rau_tau_timer = sqlite3_column_int(stmt, 7);
189 subscr->nam_cs = sqlite3_column_int(stmt, 8);
190 subscr->nam_ps = sqlite3_column_int(stmt, 9);
191 subscr->lmsi = sqlite3_column_int(stmt, 10);
192 subscr->ms_purged_cs = sqlite3_column_int(stmt, 11);
193 subscr->ms_purged_ps = sqlite3_column_int(stmt, 12);
194
Max00b37152017-02-20 11:09:27 +0100195 db_remove_reset(stmt);
Harald Weltee687be52016-05-03 18:49:27 +0200196
Maxadc66482017-02-20 11:23:20 +0100197 return 0;
Harald Weltee687be52016-05-03 18:49:27 +0200198}
199
Max3ce36862017-02-20 11:18:04 +0100200int db_subscr_ps(struct db_context *dbc, const char *imsi, bool enable)
201{
202 sqlite3_stmt *stmt =
Neels Hofmeyr4bde9492017-10-06 03:09:34 +0200203 dbc->stmt[enable ? DB_STMT_SET_NAM_PS_BY_IMSI : DB_STMT_UNSET_NAM_PS_BY_IMSI];
Max3ce36862017-02-20 11:18:04 +0100204 int rc;
205
Neels Hofmeyrf3144592017-10-06 03:40:52 +0200206 if (!db_bind_text(stmt, NULL, imsi))
Max3ce36862017-02-20 11:18:04 +0100207 return -EINVAL;
208
209 rc = sqlite3_step(stmt); /* execute the statement */
210 if (rc != SQLITE_DONE) {
211 LOGHLR(imsi, LOGL_ERROR, "Error executing SQL: %d\n", rc);
Neels Hofmeyr743cf422017-03-16 20:38:44 +0100212 db_remove_reset(stmt);
213 return -ENOEXEC;
Max3ce36862017-02-20 11:18:04 +0100214 }
215
216 rc = sqlite3_changes(dbc->db); /* verify execution result */
217 if (rc != 1) {
218 LOGHLR(imsi, LOGL_ERROR, "SQL modified %d rows (expected 1)\n",
219 rc);
220 rc = -EINVAL;
221 }
222
223 db_remove_reset(stmt);
224 return rc;
225}
226
Harald Weltee687be52016-05-03 18:49:27 +0200227int db_subscr_lu(struct db_context *dbc,
228 const struct hlr_subscriber *subscr,
229 const char *vlr_or_sgsn_number, bool lu_is_ps)
230{
Neels Hofmeyr4bde9492017-10-06 03:09:34 +0200231 sqlite3_stmt *stmt = dbc->stmt[DB_STMT_UPD_VLR_BY_ID];
Harald Weltee687be52016-05-03 18:49:27 +0200232 const char *txt;
233 int rc, ret = 0;
234
235 if (lu_is_ps) {
Neels Hofmeyr4bde9492017-10-06 03:09:34 +0200236 stmt = dbc->stmt[DB_STMT_UPD_SGSN_BY_ID];
Harald Weltee687be52016-05-03 18:49:27 +0200237 txt = subscr->sgsn_number;
238 } else {
Neels Hofmeyr4bde9492017-10-06 03:09:34 +0200239 stmt = dbc->stmt[DB_STMT_UPD_VLR_BY_ID];
Harald Weltee687be52016-05-03 18:49:27 +0200240 txt = subscr->vlr_number;
241 }
242
243 rc = sqlite3_bind_int64(stmt, 1, subscr->id);
244 if (rc != SQLITE_OK) {
245 LOGP(DAUC, LOGL_ERROR, "Error binding ID: %d\n", rc);
Max00b37152017-02-20 11:09:27 +0100246 return -EINVAL;
Harald Weltee687be52016-05-03 18:49:27 +0200247 }
248
249 rc = sqlite3_bind_text(stmt, 2, txt, -1, SQLITE_STATIC);
250 if (rc != SQLITE_OK) {
251 LOGP(DAUC, LOGL_ERROR, "Error binding VLR/SGSN Number: %d\n", rc);
Max00b37152017-02-20 11:09:27 +0100252 ret = -EBADMSG;
Harald Weltee687be52016-05-03 18:49:27 +0200253 goto out;
254 }
255
256 /* execute the statement */
257 rc = sqlite3_step(stmt);
258 if (rc != SQLITE_DONE) {
259 LOGP(DAUC, LOGL_ERROR, "Error updating SQN: %d\n", rc);
Max00b37152017-02-20 11:09:27 +0100260 ret = -ENOEXEC;
Harald Weltee687be52016-05-03 18:49:27 +0200261 }
262out:
Max00b37152017-02-20 11:09:27 +0100263 db_remove_reset(stmt);
Harald Weltee687be52016-05-03 18:49:27 +0200264
265 return ret;
266}
Harald Welteb18f0e02016-05-05 21:03:03 +0200267
268int db_subscr_purge(struct db_context *dbc, const char *imsi, bool is_ps)
269{
Neels Hofmeyr4bde9492017-10-06 03:09:34 +0200270 sqlite3_stmt *stmt = dbc->stmt[DB_STMT_UPD_VLR_BY_ID];
Harald Welteb18f0e02016-05-05 21:03:03 +0200271 int rc, ret = 1;
272
273 if (is_ps)
Neels Hofmeyr4bde9492017-10-06 03:09:34 +0200274 stmt = dbc->stmt[DB_STMT_UPD_PURGE_PS_BY_IMSI];
Harald Welteb18f0e02016-05-05 21:03:03 +0200275 else
Neels Hofmeyr4bde9492017-10-06 03:09:34 +0200276 stmt = dbc->stmt[DB_STMT_UPD_PURGE_CS_BY_IMSI];
Harald Welteb18f0e02016-05-05 21:03:03 +0200277
Neels Hofmeyrf3144592017-10-06 03:40:52 +0200278 if (!db_bind_text(stmt, NULL, imsi))
Max00b37152017-02-20 11:09:27 +0100279 return -EINVAL;
Harald Welteb18f0e02016-05-05 21:03:03 +0200280
281 /* execute the statement */
282 rc = sqlite3_step(stmt);
283 if (rc != SQLITE_DONE) {
284 LOGP(DAUC, LOGL_ERROR, "Error setting Purged: %d\n", rc);
Max00b37152017-02-20 11:09:27 +0100285 ret = -ENOEXEC;
Harald Welteb18f0e02016-05-05 21:03:03 +0200286 }
287 /* FIXME: return 0 in case IMSI not known */
Max00b37152017-02-20 11:09:27 +0100288
289 db_remove_reset(stmt);
Harald Welteb18f0e02016-05-05 21:03:03 +0200290
291 return ret;
292}