blob: 4e4afebaa47e030bb19f17d90ab9bf0ac8ee0df1 [file] [log] [blame]
Max372868b2017-03-02 12:12:00 +01001/* OsmoHLR Control Interface implementation */
2
3/* (C) 2017 sysmocom s.f.m.c. GmbH <info@sysmocom.de>
4 * 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,
42 int *minlen, int *maxlen);
43
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
169static void print_subscr_info_aud2g(struct ctrl_cmd *cmd, struct osmo_sub_auth_data *aud)
170{
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
181static void print_subscr_info_aud3g(struct ctrl_cmd *cmd, struct osmo_sub_auth_data *aud)
182{
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),
190 hexdump_buf(aud->u.umts.k));
191 /* 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",
199 hexdump_buf(aud->u.umts.opc),
200 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;
294 struct osmo_sub_auth_data aud2g;
295 struct osmo_sub_auth_data aud3g;
296 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;
330 struct osmo_sub_auth_data aud2g;
331 struct osmo_sub_auth_data aud3g;
332 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
Pau Espin Pedrol140dffd2022-06-17 19:04:23 +0200429CTRL_CMD_DEFINE(subscr_msisdn, "msisdn");
430static int verify_subscr_msisdn(struct ctrl_cmd *cmd, const char *value, void *data)
431{
432 struct hlr_subscriber subscr;
433 if (!value)
434 return 1;
435 if (strlen(value) > sizeof(subscr.msisdn) - 1)
436 return 1;
437 if (strcmp(value, "none") != 0 && !osmo_msisdn_str_valid(value))
438 return 1;
439 return 0;
440}
441static int get_subscr_msisdn(struct ctrl_cmd *cmd, void *data)
442{
443 struct hlr_subscriber subscr;
444 struct hlr *hlr = data;
445 const char *by_selector = cmd->node;
446
447 if (!get_subscriber(hlr->dbc, by_selector, &subscr, cmd))
448 return CTRL_CMD_ERROR;
449
450 if (strlen(subscr.msisdn) == 0)
451 snprintf(subscr.msisdn, sizeof(subscr.msisdn), "none");
452
453 cmd->reply = talloc_asprintf(cmd, "%s", subscr.msisdn);
454 return CTRL_CMD_REPLY;
455}
456static int set_subscr_msisdn(struct ctrl_cmd *cmd, void *data)
457{
458 struct hlr_subscriber subscr;
459 struct hlr *hlr = data;
460 const char *by_selector = cmd->node;
461 const char *msisdn;
462
463 if (!get_subscriber(hlr->dbc, by_selector, &subscr, cmd))
464 return CTRL_CMD_ERROR;
465
466 if (strcmp(cmd->value, "none") == 0)
467 msisdn = NULL;
468 else
469 msisdn = cmd->value;
470
471 if (db_subscr_update_msisdn_by_imsi(g_hlr->dbc, subscr.imsi, msisdn)) {
472 cmd->reply = "Update MSISDN failed";
473 return CTRL_CMD_ERROR;
474 }
475
476 cmd->reply = "OK";
477 return CTRL_CMD_REPLY;
478}
479
Pau Espin Pedrol1d0a0302022-06-20 16:48:45 +0200480/* value format: <algo[,KI]> */
481CTRL_CMD_DEFINE(subscr_aud2g, "aud2g");
482static int verify_subscr_aud2g(struct ctrl_cmd *cmd, const char *value, void *data)
483{
484 if (!value)
485 return 1;
486 if (strcasecmp(value, "none") != 0 && !strchr(value, ','))
487 return 1;
488 return 0;
489}
490static int get_subscr_aud2g(struct ctrl_cmd *cmd, void *data)
491{
492 struct hlr_subscriber subscr;
493 struct hlr *hlr = data;
494 const char *by_selector = cmd->node;
495 struct osmo_sub_auth_data aud2g;
496 struct osmo_sub_auth_data aud3g_unused;
497 int rc;
498
499 if (!get_subscriber(hlr->dbc, by_selector, &subscr, cmd))
500 return CTRL_CMD_ERROR;
501
502 rc = db_get_auth_data(hlr->dbc, subscr.imsi, &aud2g, &aud3g_unused, NULL);
503 switch (rc) {
504 case 0:
505 break;
506 case -ENOENT:
507 case -ENOKEY:
508 aud2g.algo = OSMO_AUTH_ALG_NONE;
509 break;
510 default:
511 cmd->reply = "Error retrieving data from database.";
512 return CTRL_CMD_ERROR;
513 }
514
515 if (aud2g.algo == OSMO_AUTH_ALG_NONE) {
516 cmd->reply = "none";
517 return CTRL_CMD_REPLY;
518 }
519
520 cmd->reply = talloc_asprintf(cmd, "%s,%s", osmo_auth_alg_name(aud2g.algo),
521 hexdump_buf(aud2g.u.gsm.ki));
522 return CTRL_CMD_REPLY;
523}
524static int set_subscr_aud2g(struct ctrl_cmd *cmd, void *data)
525{
526 struct hlr_subscriber subscr;
527 struct hlr *hlr = data;
528 const char *by_selector = cmd->node;
529 char *tmp = NULL, *tok, *saveptr;
530 int minlen = 0;
531 int maxlen = 0;
532 struct sub_auth_data_str aud2g = {
533 .type = OSMO_AUTH_TYPE_GSM
534 };
535
536 if (!get_subscriber(hlr->dbc, by_selector, &subscr, cmd))
537 return CTRL_CMD_ERROR;
538
539 tmp = talloc_strdup(cmd, cmd->value);
540 if (!tmp) {
541 cmd->reply = "OOM";
542 return CTRL_CMD_ERROR;
543 }
544
545 /* Parse alg_type: */
546 tok = strtok_r(tmp, ",", &saveptr);
547 if (!tok) {
548 cmd->reply = "Invalid format";
549 return CTRL_CMD_ERROR;
550 }
551 if (strcmp(tok, "none") == 0) {
552 aud2g.algo = OSMO_AUTH_ALG_NONE;
553 } else if (!auth_algo_parse(tok, &aud2g.algo, &minlen, &maxlen)) {
554 cmd->reply = "Unknown auth algorithm.";
555 return CTRL_CMD_ERROR;
556 }
557
558 if (aud2g.algo != OSMO_AUTH_ALG_NONE) {
559 tok = strtok_r(NULL, "\0", &saveptr);
560 if (!tok) {
561 cmd->reply = "Invalid format.";
562 return CTRL_CMD_ERROR;
563 }
564 aud2g.u.gsm.ki = tok;
565 if (!osmo_is_hexstr(aud2g.u.gsm.ki, minlen * 2, maxlen * 2, true)) {
566 cmd->reply = "Invalid KI.";
567 return CTRL_CMD_ERROR;
568 }
569 }
570
571 if (db_subscr_update_aud_by_id(g_hlr->dbc, subscr.id, &aud2g)) {
572 cmd->reply = "Update aud2g failed.";
573 return CTRL_CMD_ERROR;
574 }
575
576 cmd->reply = "OK";
577 return CTRL_CMD_REPLY;
578}
579
Pau Espin Pedrol777860d2022-06-20 18:02:26 +0200580/* value format: <algo[,KI,(op|opc),OP_C[,ind_bitlen]]> */
581CTRL_CMD_DEFINE(subscr_aud3g, "aud3g");
582static int verify_subscr_aud3g(struct ctrl_cmd *cmd, const char *value, void *data)
583{
584 if (!value)
585 return 1;
586 if (strcasecmp(value, "none") != 0 && !strchr(value, ','))
587 return 1;
588 return 0;
589}
590static int get_subscr_aud3g(struct ctrl_cmd *cmd, void *data)
591{
592 struct hlr_subscriber subscr;
593 struct hlr *hlr = data;
594 const char *by_selector = cmd->node;
595 struct osmo_sub_auth_data aud2g_unused;
596 struct osmo_sub_auth_data aud3g;
597 int rc;
598
599 if (!get_subscriber(hlr->dbc, by_selector, &subscr, cmd))
600 return CTRL_CMD_ERROR;
601
602 rc = db_get_auth_data(hlr->dbc, subscr.imsi, &aud2g_unused, &aud3g, NULL);
603 switch (rc) {
604 case 0:
605 break;
606 case -ENOENT:
607 case -ENOKEY:
608 aud3g.algo = OSMO_AUTH_ALG_NONE;
609 break;
610 default:
611 cmd->reply = "Error retrieving data from database.";
612 return CTRL_CMD_ERROR;
613 }
614
615 if (aud3g.algo == OSMO_AUTH_ALG_NONE) {
616 cmd->reply = "none";
617 return CTRL_CMD_REPLY;
618 }
619
620 cmd->reply = talloc_asprintf(cmd, "%s,%s,%s,%s,%u", osmo_auth_alg_name(aud3g.algo),
621 osmo_hexdump_nospc_c(cmd, aud3g.u.umts.k, sizeof(aud3g.u.umts.k)),
622 aud3g.u.umts.opc_is_op ? "OP" : "OPC",
623 osmo_hexdump_nospc_c(cmd, aud3g.u.umts.opc, sizeof(aud3g.u.umts.opc)),
624 aud3g.u.umts.ind_bitlen);
625 return CTRL_CMD_REPLY;
626}
627static int set_subscr_aud3g(struct ctrl_cmd *cmd, void *data)
628{
629 struct hlr_subscriber subscr;
630 struct hlr *hlr = data;
631 const char *by_selector = cmd->node;
632 char *tmp = NULL, *tok, *saveptr;
633 int minlen = 0;
634 int maxlen = 0;
635 struct sub_auth_data_str aud3g = {
636 .type = OSMO_AUTH_TYPE_UMTS,
637 .u.umts = {
638 .ind_bitlen = 5,
639 },
640 };
641 bool ind_bitlen_present;
642
643 if (!get_subscriber(hlr->dbc, by_selector, &subscr, cmd))
644 return CTRL_CMD_ERROR;
645
646 tmp = talloc_strdup(cmd, cmd->value);
647 if (!tmp) {
648 cmd->reply = "OOM";
649 return CTRL_CMD_ERROR;
650 }
651
652 /* Parse alg_type: */
653 tok = strtok_r(tmp, ",", &saveptr);
654 if (!tok) {
655 cmd->reply = "Invalid format.";
656 return CTRL_CMD_ERROR;
657 }
658 if (strcmp(tok, "none") == 0) {
659 aud3g.algo = OSMO_AUTH_ALG_NONE;
660 } else if (!auth_algo_parse(tok, &aud3g.algo, &minlen, &maxlen)) {
661 cmd->reply = "Unknown auth algorithm.";
662 return CTRL_CMD_ERROR;
663 }
664
665 if (aud3g.algo != OSMO_AUTH_ALG_NONE) {
666 /* Parse K */
667 tok = strtok_r(NULL, ",", &saveptr);
668 if (!tok) {
669 cmd->reply = "Invalid format.";
670 return CTRL_CMD_ERROR;
671 }
672 aud3g.u.umts.k = tok;
673 if (!osmo_is_hexstr(aud3g.u.umts.k, minlen * 2, maxlen * 2, true)) {
674 cmd->reply = "Invalid KI.";
675 return CTRL_CMD_ERROR;
676 }
677
678 /* Parse OP/OPC choice */
679 tok = strtok_r(NULL, ",", &saveptr);
680 if (!tok) {
681 cmd->reply = "Invalid format.";
682 return CTRL_CMD_ERROR;
683 }
684 if (strcasecmp(tok, "op") == 0) {
685 aud3g.u.umts.opc_is_op = true;
686 } else if (strcasecmp(tok, "opc") == 0) {
687 aud3g.u.umts.opc_is_op = false;
688 } else {
689 cmd->reply = "Invalid format.";
690 return CTRL_CMD_ERROR;
691 }
692
693 /* Parse OP/OPC value */
694 ind_bitlen_present = !!strchr(saveptr, ',');
695 tok = strtok_r(NULL, ind_bitlen_present ? "," : "\0", &saveptr);
696 if (!tok) {
697 cmd->reply = "Invalid format.";
698 return CTRL_CMD_ERROR;
699 }
700
701 aud3g.u.umts.opc = tok;
702 if (!osmo_is_hexstr(aud3g.u.umts.opc, MILENAGE_KEY_LEN * 2, MILENAGE_KEY_LEN * 2, true)) {
703 cmd->reply = talloc_asprintf(cmd, "Invalid OP/OPC.");
704 return CTRL_CMD_ERROR;
705 }
706
707 if (ind_bitlen_present) {
708 /* Parse bitlen_ind */
709 tok = strtok_r(NULL, "\0", &saveptr);
710 if (!tok || tok[0] == '\0') {
711 cmd->reply = "Invalid format.";
712 return CTRL_CMD_ERROR;
713 }
714 aud3g.u.umts.ind_bitlen = atoi(tok);
715 }
716 }
717
718 if (db_subscr_update_aud_by_id(g_hlr->dbc, subscr.id, &aud3g)) {
719 cmd->reply = "Update aud3g failed.";
720 return CTRL_CMD_ERROR;
721 }
722
723 cmd->reply = "OK";
724 return CTRL_CMD_REPLY;
725}
726
Neels Hofmeyr446eb0f2017-10-17 01:58:24 +0200727static int hlr_ctrl_node_lookup(void *data, vector vline, int *node_type,
728 void **node_data, int *i)
729{
730 const char *token = vector_slot(vline, *i);
731
732 switch (*node_type) {
733 case CTRL_NODE_ROOT:
734 if (strcmp(token, "subscriber") != 0)
735 return 0;
736 *node_data = NULL;
737 *node_type = CTRL_NODE_SUBSCR;
738 break;
739 case CTRL_NODE_SUBSCR:
740 if (!startswith(token, "by-"))
741 return 0;
742 *node_data = (void*)token;
743 *node_type = CTRL_NODE_SUBSCR_BY;
744 break;
745 default:
746 return 0;
747 }
748
749 return 1;
750}
751
Harald Welte7a476532022-11-03 11:38:41 +0100752static int hlr_ctrl_cmds_install(void)
Pau Espin Pedrolb4f25a02022-06-17 15:53:28 +0200753{
754 int rc = 0;
755
Pau Espin Pedrold63ec882022-06-17 17:54:51 +0200756 rc |= ctrl_cmd_install(CTRL_NODE_SUBSCR, &cmd_subscr_create);
Pau Espin Pedrol3ca9a1f2022-06-20 15:38:21 +0200757 rc |= ctrl_cmd_install(CTRL_NODE_SUBSCR, &cmd_subscr_delete);
Pau Espin Pedrold63ec882022-06-17 17:54:51 +0200758
Pau Espin Pedrolb4f25a02022-06-17 15:53:28 +0200759 rc |= ctrl_cmd_install(CTRL_NODE_SUBSCR_BY, &cmd_subscr_info);
760 rc |= ctrl_cmd_install(CTRL_NODE_SUBSCR_BY, &cmd_subscr_info_aud);
761 rc |= ctrl_cmd_install(CTRL_NODE_SUBSCR_BY, &cmd_subscr_info_all);
762 rc |= ctrl_cmd_install(CTRL_NODE_SUBSCR_BY, &cmd_subscr_ps_enabled);
763 rc |= ctrl_cmd_install(CTRL_NODE_SUBSCR_BY, &cmd_subscr_cs_enabled);
Pau Espin Pedrol140dffd2022-06-17 19:04:23 +0200764 rc |= ctrl_cmd_install(CTRL_NODE_SUBSCR_BY, &cmd_subscr_msisdn);
Pau Espin Pedrol1d0a0302022-06-20 16:48:45 +0200765 rc |= ctrl_cmd_install(CTRL_NODE_SUBSCR_BY, &cmd_subscr_aud2g);
Pau Espin Pedrol777860d2022-06-20 18:02:26 +0200766 rc |= ctrl_cmd_install(CTRL_NODE_SUBSCR_BY, &cmd_subscr_aud3g);
Pau Espin Pedrolb4f25a02022-06-17 15:53:28 +0200767
768 return rc;
769}
770
Neels Hofmeyr234f9cb2017-10-24 17:23:04 +0200771struct ctrl_handle *hlr_controlif_setup(struct hlr *hlr)
Max372868b2017-03-02 12:12:00 +0100772{
773 int rc;
Max6263cf32022-12-17 20:59:00 +0300774 struct ctrl_handle *hdl = ctrl_interface_setup2(hlr, OSMO_CTRL_PORT_HLR, hlr_ctrl_node_lookup,
775 _LAST_CTRL_NODE_HLR);
Max372868b2017-03-02 12:12:00 +0100776 if (!hdl)
777 return NULL;
778
779 rc = hlr_ctrl_cmds_install();
780 if (rc) /* FIXME: close control interface? */
781 return NULL;
782
783 return hdl;
784}