blob: 9555e288e3a9d24047e23259b1417dd7350f3ba0 [file] [log] [blame]
Max372868b2017-03-02 12:12:00 +01001/* OsmoHLR Control Interface implementation */
2
Harald Weltea854b482023-05-30 17:27:32 +02003/* (C) 2017-2023 sysmocom s.f.m.c. GmbH <info@sysmocom.de>
Max372868b2017-03-02 12:12:00 +01004 * All Rights Reserved
5 *
6 * Author: Max Suraev <msuraev@sysmocom.de>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU Affero General Public License as published by
10 * the Free Software Foundation; either version 3 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU Affero General Public License for more details.
17 *
18 * You should have received a copy of the GNU Affero General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 *
21 */
22
23#include <stdbool.h>
Neels Hofmeyr446eb0f2017-10-17 01:58:24 +020024#include <errno.h>
25#include <inttypes.h>
26#include <string.h>
Max372868b2017-03-02 12:12:00 +010027
Neels Hofmeyr446eb0f2017-10-17 01:58:24 +020028#include <osmocom/gsm/gsm23003.h>
Max372868b2017-03-02 12:12:00 +010029#include <osmocom/ctrl/ports.h>
30
Neels Hofmeyr2f758032019-11-20 00:37:07 +010031#include <osmocom/hlr/hlr.h>
32#include <osmocom/hlr/ctrl.h>
33#include <osmocom/hlr/db.h>
Pau Espin Pedrol777860d2022-06-20 18:02:26 +020034#include <osmocom/hlr/hlr_vty.h>
Max372868b2017-03-02 12:12:00 +010035
Neels Hofmeyr446eb0f2017-10-17 01:58:24 +020036#define SEL_BY "by-"
37#define SEL_BY_IMSI SEL_BY "imsi-"
38#define SEL_BY_MSISDN SEL_BY "msisdn-"
39#define SEL_BY_ID SEL_BY "id-"
40
Pau Espin Pedrol1d0a0302022-06-20 16:48:45 +020041extern bool auth_algo_parse(const char *alg_str, enum osmo_auth_algo *algo,
Harald Welte626f5eb2023-05-30 18:28:15 +020042 int *minlen, int *maxlen, int *minlen_opc, int *maxlen_opc);
Pau Espin Pedrol1d0a0302022-06-20 16:48:45 +020043
Neels Hofmeyr446eb0f2017-10-17 01:58:24 +020044#define hexdump_buf(buf) osmo_hexdump_nospc((void*)buf, sizeof(buf))
45
46static bool startswith(const char *str, const char *start)
47{
48 return strncmp(str, start, strlen(start)) == 0;
49}
50
51static int _get_subscriber(struct db_context *dbc,
52 const char *by_selector,
53 struct hlr_subscriber *subscr)
54{
55 const char *val;
56 if (startswith(by_selector, SEL_BY_IMSI)) {
57 val = by_selector + strlen(SEL_BY_IMSI);
58 if (!osmo_imsi_str_valid(val))
59 return -EINVAL;
60 return db_subscr_get_by_imsi(dbc, val, subscr);
61 }
62 if (startswith(by_selector, SEL_BY_MSISDN)) {
63 val = by_selector + strlen(SEL_BY_MSISDN);
64 if (!osmo_msisdn_str_valid(val))
65 return -EINVAL;
66 return db_subscr_get_by_msisdn(dbc, val, subscr);
67 }
68 if (startswith(by_selector, SEL_BY_ID)) {
69 int64_t id;
70 char *endptr;
71 val = by_selector + strlen(SEL_BY_ID);
72 if (*val == '+')
73 return -EINVAL;
74 errno = 0;
75 id = strtoll(val, &endptr, 10);
76 if (errno || *endptr)
77 return -EINVAL;
78 return db_subscr_get_by_id(dbc, id, subscr);
79 }
80 return -ENOTSUP;
81}
82
83static bool get_subscriber(struct db_context *dbc,
84 const char *by_selector,
85 struct hlr_subscriber *subscr,
86 struct ctrl_cmd *cmd)
87{
88 int rc = _get_subscriber(dbc, by_selector, subscr);
89 switch (rc) {
90 case 0:
91 return true;
92 case -ENOTSUP:
93 cmd->reply = "Not a known subscriber 'by-xxx-' selector.";
94 return false;
95 case -EINVAL:
96 cmd->reply = "Invalid value part of 'by-xxx-value' selector.";
97 return false;
98 case -ENOENT:
99 cmd->reply = "No such subscriber.";
100 return false;
101 default:
Thorsten Alteholzb07f33d2019-07-16 21:21:36 +0200102 cmd->reply = "An unknown error has occurred during get_subscriber().";
Neels Hofmeyr446eb0f2017-10-17 01:58:24 +0200103 return false;
104 }
105}
106
107/* Optimization: if a subscriber operation is requested by-imsi, just return
108 * the IMSI right back. */
109static const char *get_subscriber_imsi(struct db_context *dbc,
110 const char *by_selector,
111 struct ctrl_cmd *cmd)
112{
113 static struct hlr_subscriber subscr;
114
115 if (startswith(by_selector, SEL_BY_IMSI))
116 return by_selector + strlen(SEL_BY_IMSI);
117 if (!get_subscriber(dbc, by_selector, &subscr, cmd))
118 return NULL;
119 return subscr.imsi;
120}
121
122/* printf fmt and arg to completely omit a string if it is empty. */
123#define FMT_S "%s%s%s%s"
124#define ARG_S(name, val) \
125 (val) && *(val) ? "\n" : "", \
126 (val) && *(val) ? name : "", \
127 (val) && *(val) ? "\t" : "", \
128 (val) && *(val) ? (val) : "" \
129
130/* printf fmt and arg to completely omit bool of given value. */
131#define FMT_BOOL "%s"
132#define ARG_BOOL(name, val) \
133 val ? "\n" name "\t1" : "\n" name "\t0"
134
135static void print_subscr_info(struct ctrl_cmd *cmd,
136 struct hlr_subscriber *subscr)
137{
138 ctrl_cmd_reply_printf(cmd,
Stefan Sperling705b61b2018-12-07 12:44:50 +0100139 "\nid\t%" PRIu64
Neels Hofmeyr446eb0f2017-10-17 01:58:24 +0200140 FMT_S
141 FMT_S
142 FMT_BOOL
143 FMT_BOOL
144 FMT_S
145 FMT_S
146 FMT_S
147 FMT_BOOL
148 FMT_BOOL
149 "\nperiodic_lu_timer\t%u"
150 "\nperiodic_rau_tau_timer\t%u"
151 "\nlmsi\t%08x"
152 ,
153 subscr->id,
154 ARG_S("imsi", subscr->imsi),
155 ARG_S("msisdn", subscr->msisdn),
156 ARG_BOOL("nam_cs", subscr->nam_cs),
157 ARG_BOOL("nam_ps", subscr->nam_ps),
158 ARG_S("vlr_number", subscr->vlr_number),
159 ARG_S("sgsn_number", subscr->sgsn_number),
160 ARG_S("sgsn_address", subscr->sgsn_address),
161 ARG_BOOL("ms_purged_cs", subscr->ms_purged_cs),
162 ARG_BOOL("ms_purged_ps", subscr->ms_purged_ps),
163 subscr->periodic_lu_timer,
164 subscr->periodic_rau_tau_timer,
165 subscr->lmsi
166 );
167}
168
Harald Weltea854b482023-05-30 17:27:32 +0200169static void print_subscr_info_aud2g(struct ctrl_cmd *cmd, struct osmo_sub_auth_data2 *aud)
Neels Hofmeyr446eb0f2017-10-17 01:58:24 +0200170{
171 if (aud->algo == OSMO_AUTH_ALG_NONE)
172 return;
173 ctrl_cmd_reply_printf(cmd,
174 "\naud2g.algo\t%s"
175 "\naud2g.ki\t%s"
176 ,
177 osmo_auth_alg_name(aud->algo),
178 hexdump_buf(aud->u.gsm.ki));
179}
180
Harald Weltea854b482023-05-30 17:27:32 +0200181static void print_subscr_info_aud3g(struct ctrl_cmd *cmd, struct osmo_sub_auth_data2 *aud)
Neels Hofmeyr446eb0f2017-10-17 01:58:24 +0200182{
183 if (aud->algo == OSMO_AUTH_ALG_NONE)
184 return;
185 ctrl_cmd_reply_printf(cmd,
186 "\naud3g.algo\t%s"
187 "\naud3g.k\t%s"
188 ,
189 osmo_auth_alg_name(aud->algo),
Harald Weltea854b482023-05-30 17:27:32 +0200190 osmo_hexdump_nospc(aud->u.umts.k, aud->u.umts.k_len));
Neels Hofmeyr446eb0f2017-10-17 01:58:24 +0200191 /* hexdump uses a static string buffer, hence only one hexdump per
192 * printf(). */
193 ctrl_cmd_reply_printf(cmd,
194 "\naud3g.%s\t%s"
195 "\naud3g.ind_bitlen\t%u"
Stefan Sperling705b61b2018-12-07 12:44:50 +0100196 "\naud3g.sqn\t%" PRIu64
Neels Hofmeyr446eb0f2017-10-17 01:58:24 +0200197 ,
198 aud->u.umts.opc_is_op? "op" : "opc",
Harald Weltea854b482023-05-30 17:27:32 +0200199 osmo_hexdump_nospc(aud->u.umts.opc, aud->u.umts.opc_len),
Neels Hofmeyr446eb0f2017-10-17 01:58:24 +0200200 aud->u.umts.ind_bitlen,
201 aud->u.umts.sqn);
202}
203
Pau Espin Pedrold63ec882022-06-17 17:54:51 +0200204CTRL_CMD_DEFINE_WO_NOVRF(subscr_create, "create");
205static int set_subscr_create(struct ctrl_cmd *cmd, void *data)
206{
207 struct hlr_subscriber subscr;
208 struct hlr *hlr = data;
209 const char *imsi = cmd->value;
210 int rc;
211
212 if (!osmo_imsi_str_valid(imsi)) {
213 cmd->reply = "Invalid IMSI value.";
214 return CTRL_CMD_ERROR;
215 }
216
217 /* Create the subscriber in the DB */
218 rc = db_subscr_create(g_hlr->dbc, imsi, DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS);
219 if (rc) {
220 if (rc == -EEXIST)
221 cmd->reply = "Subscriber already exists.";
222 else
223 cmd->reply = "Cannot create subscriber.";
224 return CTRL_CMD_ERROR;
225 }
226
227 LOGP(DCTRL, LOGL_INFO, "Created subscriber IMSI='%s'\n",
228 imsi);
229
230 /* Retrieve data of newly created subscriber: */
231 rc = db_subscr_get_by_imsi(hlr->dbc, imsi, &subscr);
232 if (rc < 0) {
233 cmd->reply = "Failed retrieving ID of newly created subscriber.";
234 return CTRL_CMD_ERROR;
235 }
236
237 cmd->reply = talloc_asprintf(cmd, "%" PRIu64, subscr.id);
238 return CTRL_CMD_REPLY;
239}
240
Pau Espin Pedrol3ca9a1f2022-06-20 15:38:21 +0200241CTRL_CMD_DEFINE_WO_NOVRF(subscr_delete, "delete");
242static int set_subscr_delete(struct ctrl_cmd *cmd, void *data)
243{
244 struct hlr_subscriber subscr;
245 struct hlr *hlr = data;
246 const char *imsi = cmd->value;
247 int rc;
248
249 if (!osmo_imsi_str_valid(imsi)) {
250 cmd->reply = "Invalid IMSI value.";
251 return CTRL_CMD_ERROR;
252 }
253
254 /* Retrieve data of newly created subscriber: */
255 rc = db_subscr_get_by_imsi(hlr->dbc, imsi, &subscr);
256 if (rc < 0) {
257 cmd->reply = "Subscriber doesn't exist.";
258 return CTRL_CMD_ERROR;
259 }
260
261 /* Create the subscriber in the DB */
262 rc = db_subscr_delete_by_id(g_hlr->dbc, subscr.id);
263 if (rc) {
264 cmd->reply = "Cannot delete subscriber.";
265 return CTRL_CMD_ERROR;
266 }
267
268 LOGP(DCTRL, LOGL_INFO, "Deleted subscriber IMSI='%s'\n",
269 imsi);
270
271 cmd->reply = talloc_asprintf(cmd, "%" PRIu64, subscr.id);
272 return CTRL_CMD_REPLY;
273}
274
Neels Hofmeyr446eb0f2017-10-17 01:58:24 +0200275CTRL_CMD_DEFINE_RO(subscr_info, "info");
276static int get_subscr_info(struct ctrl_cmd *cmd, void *data)
Max9cacb6f2017-02-20 17:22:56 +0100277{
Neels Hofmeyr00b1d432017-10-17 01:43:48 +0200278 struct hlr_subscriber subscr;
Neels Hofmeyr446eb0f2017-10-17 01:58:24 +0200279 struct hlr *hlr = data;
280 const char *by_selector = cmd->node;
Max9cacb6f2017-02-20 17:22:56 +0100281
Neels Hofmeyr446eb0f2017-10-17 01:58:24 +0200282 if (!get_subscriber(hlr->dbc, by_selector, &subscr, cmd))
283 return CTRL_CMD_ERROR;
284
285 print_subscr_info(cmd, &subscr);
286
287 return CTRL_CMD_REPLY;
288}
289
290CTRL_CMD_DEFINE_RO(subscr_info_aud, "info-aud");
291static int get_subscr_info_aud(struct ctrl_cmd *cmd, void *data)
292{
293 const char *imsi;
Harald Weltea854b482023-05-30 17:27:32 +0200294 struct osmo_sub_auth_data2 aud2g;
295 struct osmo_sub_auth_data2 aud3g;
Neels Hofmeyr446eb0f2017-10-17 01:58:24 +0200296 struct hlr *hlr = data;
297 const char *by_selector = cmd->node;
298 int rc;
299
300 imsi = get_subscriber_imsi(hlr->dbc, by_selector, cmd);
301 if (!imsi)
302 return CTRL_CMD_ERROR;
303
304 rc = db_get_auth_data(hlr->dbc, imsi, &aud2g, &aud3g, NULL);
305
Neels Hofmeyrbd1dca02017-11-23 15:25:30 +0100306 switch (rc) {
307 case 0:
308 break;
309 case -ENOENT:
310 case -ENOKEY:
Neels Hofmeyr446eb0f2017-10-17 01:58:24 +0200311 /* No auth data found, tell the print*() functions about it. */
312 aud2g.algo = OSMO_AUTH_ALG_NONE;
313 aud3g.algo = OSMO_AUTH_ALG_NONE;
Neels Hofmeyrbd1dca02017-11-23 15:25:30 +0100314 break;
315 default:
Neels Hofmeyr446eb0f2017-10-17 01:58:24 +0200316 cmd->reply = "Error retrieving authentication data.";
Max9cacb6f2017-02-20 17:22:56 +0100317 return CTRL_CMD_ERROR;
318 }
319
Neels Hofmeyr446eb0f2017-10-17 01:58:24 +0200320 print_subscr_info_aud2g(cmd, &aud2g);
321 print_subscr_info_aud3g(cmd, &aud3g);
322
323 return CTRL_CMD_REPLY;
324}
325
326CTRL_CMD_DEFINE_RO(subscr_info_all, "info-all");
327static int get_subscr_info_all(struct ctrl_cmd *cmd, void *data)
328{
329 struct hlr_subscriber subscr;
Harald Weltea854b482023-05-30 17:27:32 +0200330 struct osmo_sub_auth_data2 aud2g;
331 struct osmo_sub_auth_data2 aud3g;
Neels Hofmeyr446eb0f2017-10-17 01:58:24 +0200332 struct hlr *hlr = data;
333 const char *by_selector = cmd->node;
334 int rc;
335
336 if (!get_subscriber(hlr->dbc, by_selector, &subscr, cmd))
337 return CTRL_CMD_ERROR;
338
339 rc = db_get_auth_data(hlr->dbc, subscr.imsi, &aud2g, &aud3g, NULL);
340
Neels Hofmeyrbd1dca02017-11-23 15:25:30 +0100341 switch (rc) {
342 case 0:
343 break;
344 case -ENOENT:
345 case -ENOKEY:
Neels Hofmeyr446eb0f2017-10-17 01:58:24 +0200346 /* No auth data found, tell the print*() functions about it. */
347 aud2g.algo = OSMO_AUTH_ALG_NONE;
348 aud3g.algo = OSMO_AUTH_ALG_NONE;
Neels Hofmeyrbd1dca02017-11-23 15:25:30 +0100349 break;
350 default:
Neels Hofmeyr446eb0f2017-10-17 01:58:24 +0200351 cmd->reply = "Error retrieving authentication data.";
Max9cacb6f2017-02-20 17:22:56 +0100352 return CTRL_CMD_ERROR;
353 }
354
Neels Hofmeyr446eb0f2017-10-17 01:58:24 +0200355 print_subscr_info(cmd, &subscr);
356 print_subscr_info_aud2g(cmd, &aud2g);
357 print_subscr_info_aud3g(cmd, &aud3g);
358
359 return CTRL_CMD_REPLY;
360}
361
362static int verify_subscr_cs_ps_enabled(struct ctrl_cmd *cmd, const char *value, void *data)
363{
364 if (!value || !*value
365 || (strcmp(value, "0") && strcmp(value, "1")))
366 return 1;
367 return 0;
368}
369
370static int get_subscr_cs_ps_enabled(struct ctrl_cmd *cmd, void *data,
371 bool is_ps)
372{
373 struct hlr_subscriber subscr;
374 struct hlr *hlr = data;
375 const char *by_selector = cmd->node;
376
377 if (!get_subscriber(hlr->dbc, by_selector, &subscr, cmd))
378 return CTRL_CMD_ERROR;
379
380 cmd->reply = (is_ps ? subscr.nam_ps : subscr.nam_cs)
381 ? "1" : "0";
382 return CTRL_CMD_REPLY;
383}
384
385static int set_subscr_cs_ps_enabled(struct ctrl_cmd *cmd, void *data,
386 bool is_ps)
387{
388 const char *imsi;
389 struct hlr *hlr = data;
390 const char *by_selector = cmd->node;
391
392 imsi = get_subscriber_imsi(hlr->dbc, by_selector, cmd);
393 if (!imsi)
394 return CTRL_CMD_ERROR;
395 if (db_subscr_nam(hlr->dbc, imsi, strcmp(cmd->value, "1") == 0, is_ps))
396 return CTRL_CMD_ERROR;
Max9cacb6f2017-02-20 17:22:56 +0100397 cmd->reply = "OK";
Max9cacb6f2017-02-20 17:22:56 +0100398 return CTRL_CMD_REPLY;
399}
400
Neels Hofmeyr446eb0f2017-10-17 01:58:24 +0200401CTRL_CMD_DEFINE(subscr_ps_enabled, "ps-enabled");
402static int verify_subscr_ps_enabled(struct ctrl_cmd *cmd, const char *value, void *data)
Max9cacb6f2017-02-20 17:22:56 +0100403{
Neels Hofmeyr446eb0f2017-10-17 01:58:24 +0200404 return verify_subscr_cs_ps_enabled(cmd, value, data);
405}
406static int get_subscr_ps_enabled(struct ctrl_cmd *cmd, void *data)
407{
408 return get_subscr_cs_ps_enabled(cmd, data, true);
409}
410static int set_subscr_ps_enabled(struct ctrl_cmd *cmd, void *data)
411{
412 return set_subscr_cs_ps_enabled(cmd, data, true);
Max9cacb6f2017-02-20 17:22:56 +0100413}
414
Neels Hofmeyr446eb0f2017-10-17 01:58:24 +0200415CTRL_CMD_DEFINE(subscr_cs_enabled, "cs-enabled");
416static int verify_subscr_cs_enabled(struct ctrl_cmd *cmd, const char *value, void *data)
Max9cacb6f2017-02-20 17:22:56 +0100417{
Neels Hofmeyr446eb0f2017-10-17 01:58:24 +0200418 return verify_subscr_cs_ps_enabled(cmd, value, data);
Max9cacb6f2017-02-20 17:22:56 +0100419}
Neels Hofmeyr446eb0f2017-10-17 01:58:24 +0200420static int get_subscr_cs_enabled(struct ctrl_cmd *cmd, void *data)
Max372868b2017-03-02 12:12:00 +0100421{
Neels Hofmeyr446eb0f2017-10-17 01:58:24 +0200422 return get_subscr_cs_ps_enabled(cmd, data, false);
423}
424static int set_subscr_cs_enabled(struct ctrl_cmd *cmd, void *data)
425{
426 return set_subscr_cs_ps_enabled(cmd, data, false);
Max372868b2017-03-02 12:12:00 +0100427}
428
Mychaela N. Falconiabe8bcd32023-12-17 20:25:41 +0000429CTRL_CMD_DEFINE_RO(subscr_imsi, "imsi");
430static int get_subscr_imsi(struct ctrl_cmd *cmd, void *data)
431{
432 struct hlr_subscriber subscr;
433 struct hlr *hlr = data;
434 const char *by_selector = cmd->node;
435
436 if (!get_subscriber(hlr->dbc, by_selector, &subscr, cmd))
437 return CTRL_CMD_ERROR;
438
439 cmd->reply = talloc_strdup(cmd, subscr.imsi);
440 return CTRL_CMD_REPLY;
441}
442
Pau Espin Pedrol140dffd2022-06-17 19:04:23 +0200443CTRL_CMD_DEFINE(subscr_msisdn, "msisdn");
444static int verify_subscr_msisdn(struct ctrl_cmd *cmd, const char *value, void *data)
445{
446 struct hlr_subscriber subscr;
447 if (!value)
448 return 1;
449 if (strlen(value) > sizeof(subscr.msisdn) - 1)
450 return 1;
451 if (strcmp(value, "none") != 0 && !osmo_msisdn_str_valid(value))
452 return 1;
453 return 0;
454}
455static int get_subscr_msisdn(struct ctrl_cmd *cmd, void *data)
456{
457 struct hlr_subscriber subscr;
458 struct hlr *hlr = data;
459 const char *by_selector = cmd->node;
460
461 if (!get_subscriber(hlr->dbc, by_selector, &subscr, cmd))
462 return CTRL_CMD_ERROR;
463
464 if (strlen(subscr.msisdn) == 0)
465 snprintf(subscr.msisdn, sizeof(subscr.msisdn), "none");
466
467 cmd->reply = talloc_asprintf(cmd, "%s", subscr.msisdn);
468 return CTRL_CMD_REPLY;
469}
470static int set_subscr_msisdn(struct ctrl_cmd *cmd, void *data)
471{
472 struct hlr_subscriber subscr;
473 struct hlr *hlr = data;
474 const char *by_selector = cmd->node;
475 const char *msisdn;
476
477 if (!get_subscriber(hlr->dbc, by_selector, &subscr, cmd))
478 return CTRL_CMD_ERROR;
479
480 if (strcmp(cmd->value, "none") == 0)
481 msisdn = NULL;
482 else
483 msisdn = cmd->value;
484
485 if (db_subscr_update_msisdn_by_imsi(g_hlr->dbc, subscr.imsi, msisdn)) {
486 cmd->reply = "Update MSISDN failed";
487 return CTRL_CMD_ERROR;
488 }
489
490 cmd->reply = "OK";
491 return CTRL_CMD_REPLY;
492}
493
Pau Espin Pedrol1d0a0302022-06-20 16:48:45 +0200494/* value format: <algo[,KI]> */
495CTRL_CMD_DEFINE(subscr_aud2g, "aud2g");
496static int verify_subscr_aud2g(struct ctrl_cmd *cmd, const char *value, void *data)
497{
498 if (!value)
499 return 1;
500 if (strcasecmp(value, "none") != 0 && !strchr(value, ','))
501 return 1;
502 return 0;
503}
504static int get_subscr_aud2g(struct ctrl_cmd *cmd, void *data)
505{
506 struct hlr_subscriber subscr;
507 struct hlr *hlr = data;
508 const char *by_selector = cmd->node;
Harald Weltea854b482023-05-30 17:27:32 +0200509 struct osmo_sub_auth_data2 aud2g;
510 struct osmo_sub_auth_data2 aud3g_unused;
Pau Espin Pedrol1d0a0302022-06-20 16:48:45 +0200511 int rc;
512
513 if (!get_subscriber(hlr->dbc, by_selector, &subscr, cmd))
514 return CTRL_CMD_ERROR;
515
516 rc = db_get_auth_data(hlr->dbc, subscr.imsi, &aud2g, &aud3g_unused, NULL);
517 switch (rc) {
518 case 0:
519 break;
520 case -ENOENT:
521 case -ENOKEY:
522 aud2g.algo = OSMO_AUTH_ALG_NONE;
523 break;
524 default:
525 cmd->reply = "Error retrieving data from database.";
526 return CTRL_CMD_ERROR;
527 }
528
529 if (aud2g.algo == OSMO_AUTH_ALG_NONE) {
530 cmd->reply = "none";
531 return CTRL_CMD_REPLY;
532 }
533
534 cmd->reply = talloc_asprintf(cmd, "%s,%s", osmo_auth_alg_name(aud2g.algo),
535 hexdump_buf(aud2g.u.gsm.ki));
536 return CTRL_CMD_REPLY;
537}
538static int set_subscr_aud2g(struct ctrl_cmd *cmd, void *data)
539{
540 struct hlr_subscriber subscr;
541 struct hlr *hlr = data;
542 const char *by_selector = cmd->node;
543 char *tmp = NULL, *tok, *saveptr;
544 int minlen = 0;
545 int maxlen = 0;
546 struct sub_auth_data_str aud2g = {
547 .type = OSMO_AUTH_TYPE_GSM
548 };
549
550 if (!get_subscriber(hlr->dbc, by_selector, &subscr, cmd))
551 return CTRL_CMD_ERROR;
552
553 tmp = talloc_strdup(cmd, cmd->value);
554 if (!tmp) {
555 cmd->reply = "OOM";
556 return CTRL_CMD_ERROR;
557 }
558
559 /* Parse alg_type: */
560 tok = strtok_r(tmp, ",", &saveptr);
561 if (!tok) {
562 cmd->reply = "Invalid format";
563 return CTRL_CMD_ERROR;
564 }
565 if (strcmp(tok, "none") == 0) {
566 aud2g.algo = OSMO_AUTH_ALG_NONE;
Harald Welte626f5eb2023-05-30 18:28:15 +0200567 } else if (!auth_algo_parse(tok, &aud2g.algo, &minlen, &maxlen, NULL, NULL)) {
Pau Espin Pedrol1d0a0302022-06-20 16:48:45 +0200568 cmd->reply = "Unknown auth algorithm.";
569 return CTRL_CMD_ERROR;
570 }
571
572 if (aud2g.algo != OSMO_AUTH_ALG_NONE) {
573 tok = strtok_r(NULL, "\0", &saveptr);
574 if (!tok) {
575 cmd->reply = "Invalid format.";
576 return CTRL_CMD_ERROR;
577 }
578 aud2g.u.gsm.ki = tok;
579 if (!osmo_is_hexstr(aud2g.u.gsm.ki, minlen * 2, maxlen * 2, true)) {
580 cmd->reply = "Invalid KI.";
581 return CTRL_CMD_ERROR;
582 }
583 }
584
585 if (db_subscr_update_aud_by_id(g_hlr->dbc, subscr.id, &aud2g)) {
586 cmd->reply = "Update aud2g failed.";
587 return CTRL_CMD_ERROR;
588 }
589
590 cmd->reply = "OK";
591 return CTRL_CMD_REPLY;
592}
593
Pau Espin Pedrol777860d2022-06-20 18:02:26 +0200594/* value format: <algo[,KI,(op|opc),OP_C[,ind_bitlen]]> */
595CTRL_CMD_DEFINE(subscr_aud3g, "aud3g");
596static int verify_subscr_aud3g(struct ctrl_cmd *cmd, const char *value, void *data)
597{
598 if (!value)
599 return 1;
600 if (strcasecmp(value, "none") != 0 && !strchr(value, ','))
601 return 1;
602 return 0;
603}
604static int get_subscr_aud3g(struct ctrl_cmd *cmd, void *data)
605{
606 struct hlr_subscriber subscr;
607 struct hlr *hlr = data;
608 const char *by_selector = cmd->node;
Harald Weltea854b482023-05-30 17:27:32 +0200609 struct osmo_sub_auth_data2 aud2g_unused;
610 struct osmo_sub_auth_data2 aud3g;
Pau Espin Pedrol777860d2022-06-20 18:02:26 +0200611 int rc;
612
613 if (!get_subscriber(hlr->dbc, by_selector, &subscr, cmd))
614 return CTRL_CMD_ERROR;
615
616 rc = db_get_auth_data(hlr->dbc, subscr.imsi, &aud2g_unused, &aud3g, NULL);
617 switch (rc) {
618 case 0:
619 break;
620 case -ENOENT:
621 case -ENOKEY:
622 aud3g.algo = OSMO_AUTH_ALG_NONE;
623 break;
624 default:
625 cmd->reply = "Error retrieving data from database.";
626 return CTRL_CMD_ERROR;
627 }
628
629 if (aud3g.algo == OSMO_AUTH_ALG_NONE) {
630 cmd->reply = "none";
631 return CTRL_CMD_REPLY;
632 }
633
634 cmd->reply = talloc_asprintf(cmd, "%s,%s,%s,%s,%u", osmo_auth_alg_name(aud3g.algo),
Harald Weltea854b482023-05-30 17:27:32 +0200635 osmo_hexdump_nospc_c(cmd, aud3g.u.umts.k, aud3g.u.umts.k_len),
Pau Espin Pedrol777860d2022-06-20 18:02:26 +0200636 aud3g.u.umts.opc_is_op ? "OP" : "OPC",
Harald Weltea854b482023-05-30 17:27:32 +0200637 osmo_hexdump_nospc_c(cmd, aud3g.u.umts.opc, aud3g.u.umts.opc_len),
Pau Espin Pedrol777860d2022-06-20 18:02:26 +0200638 aud3g.u.umts.ind_bitlen);
639 return CTRL_CMD_REPLY;
640}
641static int set_subscr_aud3g(struct ctrl_cmd *cmd, void *data)
642{
643 struct hlr_subscriber subscr;
644 struct hlr *hlr = data;
645 const char *by_selector = cmd->node;
646 char *tmp = NULL, *tok, *saveptr;
Harald Welte626f5eb2023-05-30 18:28:15 +0200647 int minlen = 0, minlen_opc = 0;
648 int maxlen = 0, maxlen_opc = 0;
Pau Espin Pedrol777860d2022-06-20 18:02:26 +0200649 struct sub_auth_data_str aud3g = {
650 .type = OSMO_AUTH_TYPE_UMTS,
651 .u.umts = {
652 .ind_bitlen = 5,
653 },
654 };
655 bool ind_bitlen_present;
656
657 if (!get_subscriber(hlr->dbc, by_selector, &subscr, cmd))
658 return CTRL_CMD_ERROR;
659
660 tmp = talloc_strdup(cmd, cmd->value);
661 if (!tmp) {
662 cmd->reply = "OOM";
663 return CTRL_CMD_ERROR;
664 }
665
666 /* Parse alg_type: */
667 tok = strtok_r(tmp, ",", &saveptr);
668 if (!tok) {
669 cmd->reply = "Invalid format.";
670 return CTRL_CMD_ERROR;
671 }
672 if (strcmp(tok, "none") == 0) {
673 aud3g.algo = OSMO_AUTH_ALG_NONE;
Harald Welte626f5eb2023-05-30 18:28:15 +0200674 } else if (!auth_algo_parse(tok, &aud3g.algo, &minlen, &maxlen, &minlen_opc, &maxlen_opc)) {
Pau Espin Pedrol777860d2022-06-20 18:02:26 +0200675 cmd->reply = "Unknown auth algorithm.";
676 return CTRL_CMD_ERROR;
677 }
678
679 if (aud3g.algo != OSMO_AUTH_ALG_NONE) {
680 /* Parse K */
681 tok = strtok_r(NULL, ",", &saveptr);
682 if (!tok) {
683 cmd->reply = "Invalid format.";
684 return CTRL_CMD_ERROR;
685 }
686 aud3g.u.umts.k = tok;
687 if (!osmo_is_hexstr(aud3g.u.umts.k, minlen * 2, maxlen * 2, true)) {
688 cmd->reply = "Invalid KI.";
689 return CTRL_CMD_ERROR;
690 }
691
692 /* Parse OP/OPC choice */
693 tok = strtok_r(NULL, ",", &saveptr);
694 if (!tok) {
695 cmd->reply = "Invalid format.";
696 return CTRL_CMD_ERROR;
697 }
698 if (strcasecmp(tok, "op") == 0) {
699 aud3g.u.umts.opc_is_op = true;
700 } else if (strcasecmp(tok, "opc") == 0) {
701 aud3g.u.umts.opc_is_op = false;
702 } else {
703 cmd->reply = "Invalid format.";
704 return CTRL_CMD_ERROR;
705 }
706
707 /* Parse OP/OPC value */
708 ind_bitlen_present = !!strchr(saveptr, ',');
709 tok = strtok_r(NULL, ind_bitlen_present ? "," : "\0", &saveptr);
710 if (!tok) {
711 cmd->reply = "Invalid format.";
712 return CTRL_CMD_ERROR;
713 }
714
715 aud3g.u.umts.opc = tok;
Harald Welte626f5eb2023-05-30 18:28:15 +0200716 if (!osmo_is_hexstr(aud3g.u.umts.opc, minlen_opc * 2, maxlen_opc * 2, true)) {
Pau Espin Pedrol777860d2022-06-20 18:02:26 +0200717 cmd->reply = talloc_asprintf(cmd, "Invalid OP/OPC.");
718 return CTRL_CMD_ERROR;
719 }
720
721 if (ind_bitlen_present) {
722 /* Parse bitlen_ind */
723 tok = strtok_r(NULL, "\0", &saveptr);
724 if (!tok || tok[0] == '\0') {
725 cmd->reply = "Invalid format.";
726 return CTRL_CMD_ERROR;
727 }
728 aud3g.u.umts.ind_bitlen = atoi(tok);
729 }
730 }
731
732 if (db_subscr_update_aud_by_id(g_hlr->dbc, subscr.id, &aud3g)) {
733 cmd->reply = "Update aud3g failed.";
734 return CTRL_CMD_ERROR;
735 }
736
737 cmd->reply = "OK";
738 return CTRL_CMD_REPLY;
739}
740
Neels Hofmeyr446eb0f2017-10-17 01:58:24 +0200741static int hlr_ctrl_node_lookup(void *data, vector vline, int *node_type,
742 void **node_data, int *i)
743{
744 const char *token = vector_slot(vline, *i);
745
746 switch (*node_type) {
747 case CTRL_NODE_ROOT:
748 if (strcmp(token, "subscriber") != 0)
749 return 0;
750 *node_data = NULL;
751 *node_type = CTRL_NODE_SUBSCR;
752 break;
753 case CTRL_NODE_SUBSCR:
754 if (!startswith(token, "by-"))
755 return 0;
756 *node_data = (void*)token;
757 *node_type = CTRL_NODE_SUBSCR_BY;
758 break;
759 default:
760 return 0;
761 }
762
763 return 1;
764}
765
Harald Welte7a476532022-11-03 11:38:41 +0100766static int hlr_ctrl_cmds_install(void)
Pau Espin Pedrolb4f25a02022-06-17 15:53:28 +0200767{
768 int rc = 0;
769
Pau Espin Pedrold63ec882022-06-17 17:54:51 +0200770 rc |= ctrl_cmd_install(CTRL_NODE_SUBSCR, &cmd_subscr_create);
Pau Espin Pedrol3ca9a1f2022-06-20 15:38:21 +0200771 rc |= ctrl_cmd_install(CTRL_NODE_SUBSCR, &cmd_subscr_delete);
Pau Espin Pedrold63ec882022-06-17 17:54:51 +0200772
Pau Espin Pedrolb4f25a02022-06-17 15:53:28 +0200773 rc |= ctrl_cmd_install(CTRL_NODE_SUBSCR_BY, &cmd_subscr_info);
774 rc |= ctrl_cmd_install(CTRL_NODE_SUBSCR_BY, &cmd_subscr_info_aud);
775 rc |= ctrl_cmd_install(CTRL_NODE_SUBSCR_BY, &cmd_subscr_info_all);
776 rc |= ctrl_cmd_install(CTRL_NODE_SUBSCR_BY, &cmd_subscr_ps_enabled);
777 rc |= ctrl_cmd_install(CTRL_NODE_SUBSCR_BY, &cmd_subscr_cs_enabled);
Mychaela N. Falconiabe8bcd32023-12-17 20:25:41 +0000778 rc |= ctrl_cmd_install(CTRL_NODE_SUBSCR_BY, &cmd_subscr_imsi);
Pau Espin Pedrol140dffd2022-06-17 19:04:23 +0200779 rc |= ctrl_cmd_install(CTRL_NODE_SUBSCR_BY, &cmd_subscr_msisdn);
Pau Espin Pedrol1d0a0302022-06-20 16:48:45 +0200780 rc |= ctrl_cmd_install(CTRL_NODE_SUBSCR_BY, &cmd_subscr_aud2g);
Pau Espin Pedrol777860d2022-06-20 18:02:26 +0200781 rc |= ctrl_cmd_install(CTRL_NODE_SUBSCR_BY, &cmd_subscr_aud3g);
Pau Espin Pedrolb4f25a02022-06-17 15:53:28 +0200782
783 return rc;
784}
785
Neels Hofmeyr234f9cb2017-10-24 17:23:04 +0200786struct ctrl_handle *hlr_controlif_setup(struct hlr *hlr)
Max372868b2017-03-02 12:12:00 +0100787{
788 int rc;
Max6263cf32022-12-17 20:59:00 +0300789 struct ctrl_handle *hdl = ctrl_interface_setup2(hlr, OSMO_CTRL_PORT_HLR, hlr_ctrl_node_lookup,
790 _LAST_CTRL_NODE_HLR);
Max372868b2017-03-02 12:12:00 +0100791 if (!hdl)
792 return NULL;
793
794 rc = hlr_ctrl_cmds_install();
795 if (rc) /* FIXME: close control interface? */
796 return NULL;
797
798 return hdl;
799}