blob: ddbaf261e234e19cee705cb2d538337d75891510 [file] [log] [blame]
Neels Hofmeyr183e7002017-10-06 02:59:54 +02001/* OsmoHLR subscriber management VTY implementation */
2/* (C) 2017 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
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#include <stdlib.h>
20#include <inttypes.h>
21#include <string.h>
22#include <errno.h>
23
24#include <osmocom/gsm/gsm23003.h>
25#include <osmocom/vty/vty.h>
26#include <osmocom/vty/command.h>
27#include <osmocom/core/utils.h>
28
29#include "hlr.h"
30#include "db.h"
31
32struct vty;
33
34#define hexdump_buf(buf) osmo_hexdump_nospc((void*)buf, sizeof(buf))
35
Neels Hofmeyr183e7002017-10-06 02:59:54 +020036static void subscr_dump_full_vty(struct vty *vty, struct hlr_subscriber *subscr)
37{
38 int rc;
39 struct osmo_sub_auth_data aud2g;
40 struct osmo_sub_auth_data aud3g;
41
42 vty_out(vty, " ID: %"PRIu64"%s", subscr->id, VTY_NEWLINE);
43
Neels Hofmeyr36bec872017-10-23 18:44:23 +020044 vty_out(vty, " IMSI: %s%s", *subscr->imsi ? subscr->imsi : "none", VTY_NEWLINE);
Neels Hofmeyr183e7002017-10-06 02:59:54 +020045 vty_out(vty, " MSISDN: %s%s", *subscr->msisdn ? subscr->msisdn : "none", VTY_NEWLINE);
46 if (*subscr->vlr_number)
47 vty_out(vty, " VLR number: %s%s", subscr->vlr_number, VTY_NEWLINE);
48 if (*subscr->sgsn_number)
49 vty_out(vty, " SGSN number: %s%s", subscr->sgsn_number, VTY_NEWLINE);
50 if (*subscr->sgsn_address)
51 vty_out(vty, " SGSN address: %s%s", subscr->sgsn_address, VTY_NEWLINE);
52 if (subscr->periodic_lu_timer)
53 vty_out(vty, " Periodic LU timer: %u%s", subscr->periodic_lu_timer, VTY_NEWLINE);
54 if (subscr->periodic_rau_tau_timer)
55 vty_out(vty, " Periodic RAU/TAU timer: %u%s", subscr->periodic_rau_tau_timer, VTY_NEWLINE);
56 if (subscr->lmsi)
57 vty_out(vty, " LMSI: %x%s", subscr->lmsi, VTY_NEWLINE);
58 if (!subscr->nam_cs)
59 vty_out(vty, " CS disabled%s", VTY_NEWLINE);
60 if (subscr->ms_purged_cs)
61 vty_out(vty, " CS purged%s", VTY_NEWLINE);
62 if (!subscr->nam_ps)
63 vty_out(vty, " PS disabled%s", VTY_NEWLINE);
64 if (subscr->ms_purged_ps)
65 vty_out(vty, " PS purged%s", VTY_NEWLINE);
66
67 if (!*subscr->imsi)
68 return;
69
70 OSMO_ASSERT(g_hlr);
71 rc = db_get_auth_data(g_hlr->dbc, subscr->imsi, &aud2g, &aud3g, NULL);
72
Neels Hofmeyrbd1dca02017-11-23 15:25:30 +010073 switch (rc) {
74 case 0:
75 break;
76 case -ENOENT:
77 case -ENOKEY:
78 aud2g.algo = OSMO_AUTH_ALG_NONE;
79 aud3g.algo = OSMO_AUTH_ALG_NONE;
80 break;
81 default:
82 vty_out(vty, "%% Error retrieving data from database (%d)%s", rc, VTY_NEWLINE);
83 return;
Neels Hofmeyr183e7002017-10-06 02:59:54 +020084 }
85
86 if (aud2g.type != OSMO_AUTH_TYPE_NONE && aud2g.type != OSMO_AUTH_TYPE_GSM) {
87 vty_out(vty, "%% Error: 2G auth data is not of type 'GSM'%s", VTY_NEWLINE);
88 aud2g = (struct osmo_sub_auth_data){};
89 }
90
91 if (aud3g.type != OSMO_AUTH_TYPE_NONE && aud3g.type != OSMO_AUTH_TYPE_UMTS) {
92 vty_out(vty, "%% Error: 3G auth data is not of type 'UMTS'%s", VTY_NEWLINE);
93 aud3g = (struct osmo_sub_auth_data){};
94 }
95
96 if (aud2g.algo != OSMO_AUTH_ALG_NONE && aud2g.type != OSMO_AUTH_TYPE_NONE) {
97 vty_out(vty, " 2G auth: %s%s",
98 osmo_auth_alg_name(aud2g.algo), VTY_NEWLINE);
99 vty_out(vty, " KI=%s%s",
100 hexdump_buf(aud2g.u.gsm.ki), VTY_NEWLINE);
101 }
102
103 if (aud3g.algo != OSMO_AUTH_ALG_NONE && aud3g.type != OSMO_AUTH_TYPE_NONE) {
104 vty_out(vty, " 3G auth: %s%s", osmo_auth_alg_name(aud3g.algo), VTY_NEWLINE);
105 vty_out(vty, " K=%s%s", hexdump_buf(aud3g.u.umts.k), VTY_NEWLINE);
106 vty_out(vty, " %s=%s%s", aud3g.u.umts.opc_is_op? "OP" : "OPC",
107 hexdump_buf(aud3g.u.umts.opc), VTY_NEWLINE);
108 vty_out(vty, " IND-bitlen=%u", aud3g.u.umts.ind_bitlen);
109 if (aud3g.u.umts.sqn)
110 vty_out(vty, " last-SQN=%"PRIu64, aud3g.u.umts.sqn);
111 vty_out(vty, VTY_NEWLINE);
112 }
113}
114
115static int get_subscr_by_argv(struct vty *vty, const char *type, const char *id, struct hlr_subscriber *subscr)
116{
117 int rc = -1;
118 if (strcmp(type, "imsi") == 0)
119 rc = db_subscr_get_by_imsi(g_hlr->dbc, id, subscr);
120 else if (strcmp(type, "msisdn") == 0)
121 rc = db_subscr_get_by_msisdn(g_hlr->dbc, id, subscr);
122 else if (strcmp(type, "id") == 0)
123 rc = db_subscr_get_by_id(g_hlr->dbc, atoll(id), subscr);
124 if (rc)
125 vty_out(vty, "%% No subscriber for %s = '%s'%s",
126 type, id, VTY_NEWLINE);
127 return rc;
128}
129
130#define SUBSCR_CMD "subscriber "
131#define SUBSCR_CMD_HELP "Subscriber management commands\n"
132
Neels Hofmeyr8aa780b2018-12-02 18:52:49 +0100133#define SUBSCR_ID "(imsi|msisdn|id) IDENT"
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200134#define SUBSCR_ID_HELP \
135 "Identify subscriber by IMSI\n" \
136 "Identify subscriber by MSISDN (phone number)\n" \
137 "Identify subscriber by database ID\n" \
138 "IMSI/MSISDN/ID of the subscriber\n"
139
Neels Hofmeyr8aa780b2018-12-02 18:52:49 +0100140#define SUBSCR SUBSCR_CMD SUBSCR_ID " "
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200141#define SUBSCR_HELP SUBSCR_CMD_HELP SUBSCR_ID_HELP
142
143#define SUBSCR_UPDATE SUBSCR "update "
144#define SUBSCR_UPDATE_HELP SUBSCR_HELP "Set or update subscriber data\n"
145
146DEFUN(subscriber_show,
147 subscriber_show_cmd,
148 SUBSCR "show",
149 SUBSCR_HELP "Show subscriber information\n")
150{
151 struct hlr_subscriber subscr;
152 const char *id_type = argv[0];
153 const char *id = argv[1];
154
155 if (get_subscr_by_argv(vty, id_type, id, &subscr))
156 return CMD_WARNING;
157
158 subscr_dump_full_vty(vty, &subscr);
159 return CMD_SUCCESS;
160}
161
Neels Hofmeyr8aa780b2018-12-02 18:52:49 +0100162ALIAS(subscriber_show, show_subscriber_cmd,
163 "show " SUBSCR_CMD SUBSCR_ID,
164 SHOW_STR SUBSCR_CMD_HELP SUBSCR_ID_HELP);
165
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200166DEFUN(subscriber_create,
167 subscriber_create_cmd,
168 SUBSCR_CMD "imsi IDENT create",
169 SUBSCR_CMD_HELP
Vadim Yanitskiyf473c7b2018-07-30 14:29:39 +0700170 "Identify subscriber by IMSI\n"
171 "IMSI/MSISDN/ID of the subscriber\n"
172 "Create subscriber by IMSI\n")
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200173{
174 int rc;
175 struct hlr_subscriber subscr;
176 const char *imsi = argv[0];
177
178 if (!osmo_imsi_str_valid(imsi)) {
179 vty_out(vty, "%% Not a valid IMSI: %s%s", imsi, VTY_NEWLINE);
180 return CMD_WARNING;
181 }
182
183 rc = db_subscr_create(g_hlr->dbc, imsi);
184
185 if (rc) {
186 if (rc == -EEXIST)
187 vty_out(vty, "%% Subscriber already exists for IMSI = %s%s",
188 imsi, VTY_NEWLINE);
189 else
190 vty_out(vty, "%% Error (rc=%d): cannot create subscriber for IMSI = %s%s",
191 rc, imsi, VTY_NEWLINE);
192 return CMD_WARNING;
193 }
194
195 rc = db_subscr_get_by_imsi(g_hlr->dbc, imsi, &subscr);
196 vty_out(vty, "%% Created subscriber %s%s", imsi, VTY_NEWLINE);
197
198 subscr_dump_full_vty(vty, &subscr);
199
200 return CMD_SUCCESS;
201}
202
203DEFUN(subscriber_delete,
204 subscriber_delete_cmd,
205 SUBSCR "delete",
206 SUBSCR_HELP "Delete subscriber from database\n")
207{
208 struct hlr_subscriber subscr;
209 int rc;
210 const char *id_type = argv[0];
211 const char *id = argv[1];
212
213 /* Find out the IMSI regardless of which way the caller decided to
214 * identify the subscriber by. */
215 if (get_subscr_by_argv(vty, id_type, id, &subscr))
216 return CMD_WARNING;
217
218 rc = db_subscr_delete_by_id(g_hlr->dbc, subscr.id);
219 if (rc) {
220 vty_out(vty, "%% Error: Failed to remove subscriber for IMSI '%s'%s",
221 subscr.imsi, VTY_NEWLINE);
222 return CMD_WARNING;
223 }
224
225 vty_out(vty, "%% Deleted subscriber for IMSI '%s'%s", subscr.imsi, VTY_NEWLINE);
226 return CMD_SUCCESS;
227}
228
229DEFUN(subscriber_msisdn,
230 subscriber_msisdn_cmd,
231 SUBSCR_UPDATE "msisdn MSISDN",
232 SUBSCR_UPDATE_HELP
233 "Set MSISDN (phone number) of the subscriber\n"
234 "New MSISDN (phone number)\n")
235{
236 struct hlr_subscriber subscr;
237 const char *id_type = argv[0];
238 const char *id = argv[1];
239 const char *msisdn = argv[2];
240
241 if (strlen(msisdn) > sizeof(subscr.msisdn) - 1) {
242 vty_out(vty, "%% MSISDN is too long, max. %zu characters are allowed%s",
243 sizeof(subscr.msisdn)-1, VTY_NEWLINE);
244 return CMD_WARNING;
245 }
246
247 if (!osmo_msisdn_str_valid(msisdn)) {
248 vty_out(vty, "%% MSISDN invalid: '%s'%s", msisdn, VTY_NEWLINE);
249 return CMD_WARNING;
250 }
251
252 if (get_subscr_by_argv(vty, id_type, id, &subscr))
253 return CMD_WARNING;
254
255 if (db_subscr_update_msisdn_by_imsi(g_hlr->dbc, subscr.imsi, msisdn)) {
256 vty_out(vty, "%% Error: cannot update MSISDN for subscriber IMSI='%s'%s",
257 subscr.imsi, VTY_NEWLINE);
258 return CMD_WARNING;
259 }
260
261 vty_out(vty, "%% Updated subscriber IMSI='%s' to MSISDN='%s'%s",
262 subscr.imsi, msisdn, VTY_NEWLINE);
Stefan Sperlingf1622522018-04-09 11:39:16 +0200263
264 if (db_subscr_get_by_msisdn(g_hlr->dbc, msisdn, &subscr) == 0)
265 osmo_hlr_subscriber_update_notify(&subscr);
266
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200267 return CMD_SUCCESS;
268}
269
270static bool is_hexkey_valid(struct vty *vty, const char *label,
271 const char *hex_str, int minlen, int maxlen)
272{
273 if (osmo_is_hexstr(hex_str, minlen * 2, maxlen * 2, true))
274 return true;
275 vty_out(vty, "%% Invalid value for %s: '%s'%s", label, hex_str, VTY_NEWLINE);
276 return false;
277}
278
279#define AUTH_ALG_TYPES_2G "(comp128v1|comp128v2|comp128v3|xor)"
280#define AUTH_ALG_TYPES_2G_HELP \
281 "Use COMP128v1 algorithm\n" \
282 "Use COMP128v2 algorithm\n" \
283 "Use COMP128v3 algorithm\n" \
284 "Use XOR algorithm\n"
285
286#define AUTH_ALG_TYPES_3G "milenage"
287#define AUTH_ALG_TYPES_3G_HELP \
288 "Use Milenage algorithm\n"
289
290#define A38_XOR_MIN_KEY_LEN 12
291#define A38_XOR_MAX_KEY_LEN 16
292#define A38_COMP128_KEY_LEN 16
293
294#define MILENAGE_KEY_LEN 16
295
296static bool auth_algo_parse(const char *alg_str, enum osmo_auth_algo *algo,
297 int *minlen, int *maxlen)
298{
299 if (!strcasecmp(alg_str, "none")) {
300 *algo = OSMO_AUTH_ALG_NONE;
301 *minlen = *maxlen = 0;
302 } else if (!strcasecmp(alg_str, "comp128v1")) {
303 *algo = OSMO_AUTH_ALG_COMP128v1;
304 *minlen = *maxlen = A38_COMP128_KEY_LEN;
305 } else if (!strcasecmp(alg_str, "comp128v2")) {
306 *algo = OSMO_AUTH_ALG_COMP128v2;
307 *minlen = *maxlen = A38_COMP128_KEY_LEN;
308 } else if (!strcasecmp(alg_str, "comp128v3")) {
309 *algo = OSMO_AUTH_ALG_COMP128v3;
310 *minlen = *maxlen = A38_COMP128_KEY_LEN;
311 } else if (!strcasecmp(alg_str, "xor")) {
312 *algo = OSMO_AUTH_ALG_XOR;
313 *minlen = A38_XOR_MIN_KEY_LEN;
314 *maxlen = A38_XOR_MAX_KEY_LEN;
315 } else if (!strcasecmp(alg_str, "milenage")) {
316 *algo = OSMO_AUTH_ALG_MILENAGE;
317 *minlen = *maxlen = MILENAGE_KEY_LEN;
318 } else
319 return false;
320 return true;
321}
322
323DEFUN(subscriber_no_aud2g,
324 subscriber_no_aud2g_cmd,
325 SUBSCR_UPDATE "aud2g none",
326 SUBSCR_UPDATE_HELP
327 "Set 2G authentication data\n"
328 "Delete 2G authentication data\n")
329{
330 struct hlr_subscriber subscr;
331 int rc;
332 const char *id_type = argv[0];
333 const char *id = argv[1];
334 struct sub_auth_data_str aud = {
335 .type = OSMO_AUTH_TYPE_GSM,
336 .algo = OSMO_AUTH_ALG_NONE,
337 };
338
339 if (get_subscr_by_argv(vty, id_type, id, &subscr))
340 return CMD_WARNING;
341
342 rc = db_subscr_update_aud_by_id(g_hlr->dbc, subscr.id, &aud);
343
Harald Welte880a34d2018-03-01 21:32:01 +0100344 if (rc && rc != -ENOENT) {
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200345 vty_out(vty, "%% Error: cannot disable 2G auth data for IMSI='%s'%s",
346 subscr.imsi, VTY_NEWLINE);
347 return CMD_WARNING;
348 }
349 return CMD_SUCCESS;
350}
351
352DEFUN(subscriber_aud2g,
353 subscriber_aud2g_cmd,
354 SUBSCR_UPDATE "aud2g " AUTH_ALG_TYPES_2G " ki KI",
355 SUBSCR_UPDATE_HELP
356 "Set 2G authentication data\n"
357 AUTH_ALG_TYPES_2G_HELP
358 "Set Ki Encryption Key\n" "Ki as 32 hexadecimal characters\n")
359{
360 struct hlr_subscriber subscr;
361 int rc;
362 int minlen = 0;
363 int maxlen = 0;
364 const char *id_type = argv[0];
365 const char *id = argv[1];
366 const char *alg_type = argv[2];
367 const char *ki = argv[3];
368 struct sub_auth_data_str aud2g = {
369 .type = OSMO_AUTH_TYPE_GSM,
370 .u.gsm.ki = ki,
371 };
372
373 if (!auth_algo_parse(alg_type, &aud2g.algo, &minlen, &maxlen)) {
374 vty_out(vty, "%% Unknown auth algorithm: '%s'%s", alg_type, VTY_NEWLINE);
375 return CMD_WARNING;
376 }
377
378 if (!is_hexkey_valid(vty, "KI", aud2g.u.gsm.ki, minlen, maxlen))
379 return CMD_WARNING;
380
381 if (get_subscr_by_argv(vty, id_type, id, &subscr))
382 return CMD_WARNING;
383
384 rc = db_subscr_update_aud_by_id(g_hlr->dbc, subscr.id, &aud2g);
385
386 if (rc) {
387 vty_out(vty, "%% Error: cannot set 2G auth data for IMSI='%s'%s",
388 subscr.imsi, VTY_NEWLINE);
389 return CMD_WARNING;
390 }
391 return CMD_SUCCESS;
392}
393
394DEFUN(subscriber_no_aud3g,
395 subscriber_no_aud3g_cmd,
396 SUBSCR_UPDATE "aud3g none",
397 SUBSCR_UPDATE_HELP
398 "Set UMTS authentication data (3G, and 2G with UMTS AKA)\n"
399 "Delete 3G authentication data\n")
400{
401 struct hlr_subscriber subscr;
402 int rc;
403 const char *id_type = argv[0];
404 const char *id = argv[1];
405 struct sub_auth_data_str aud = {
406 .type = OSMO_AUTH_TYPE_UMTS,
407 .algo = OSMO_AUTH_ALG_NONE,
408 };
409
410 if (get_subscr_by_argv(vty, id_type, id, &subscr))
411 return CMD_WARNING;
412
413 rc = db_subscr_update_aud_by_id(g_hlr->dbc, subscr.id, &aud);
414
Harald Welte880a34d2018-03-01 21:32:01 +0100415 if (rc && rc != -ENOENT) {
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200416 vty_out(vty, "%% Error: cannot disable 3G auth data for IMSI='%s'%s",
417 subscr.imsi, VTY_NEWLINE);
418 return CMD_WARNING;
419 }
420 return CMD_SUCCESS;
421}
422
423DEFUN(subscriber_aud3g,
424 subscriber_aud3g_cmd,
425 SUBSCR_UPDATE "aud3g " AUTH_ALG_TYPES_3G
426 " k K"
427 " (op|opc) OP_C"
428 " [ind-bitlen] [<0-28>]",
429 SUBSCR_UPDATE_HELP
430 "Set UMTS authentication data (3G, and 2G with UMTS AKA)\n"
431 AUTH_ALG_TYPES_3G_HELP
432 "Set Encryption Key K\n" "K as 32 hexadecimal characters\n"
433 "Set OP key\n" "Set OPC key\n" "OP or OPC as 32 hexadecimal characters\n"
434 "Set IND bit length\n" "IND bit length value (default: 5)\n")
435{
436 struct hlr_subscriber subscr;
437 int minlen = 0;
438 int maxlen = 0;
439 int rc;
440 const char *id_type = argv[0];
441 const char *id = argv[1];
442 const char *alg_type = AUTH_ALG_TYPES_3G;
443 const char *k = argv[2];
444 bool opc_is_op = (strcasecmp("op", argv[3]) == 0);
445 const char *op_opc = argv[4];
446 int ind_bitlen = argc > 6? atoi(argv[6]) : 5;
447 struct sub_auth_data_str aud3g = {
448 .type = OSMO_AUTH_TYPE_UMTS,
449 .u.umts = {
450 .k = k,
451 .opc_is_op = opc_is_op,
452 .opc = op_opc,
453 .ind_bitlen = ind_bitlen,
454 },
455 };
456
457 if (!auth_algo_parse(alg_type, &aud3g.algo, &minlen, &maxlen)) {
458 vty_out(vty, "%% Unknown auth algorithm: '%s'%s", alg_type, VTY_NEWLINE);
459 return CMD_WARNING;
460 }
461
462 if (!is_hexkey_valid(vty, "K", aud3g.u.umts.k, minlen, maxlen))
463 return CMD_WARNING;
464
465 if (!is_hexkey_valid(vty, opc_is_op ? "OP" : "OPC", aud3g.u.umts.opc,
466 MILENAGE_KEY_LEN, MILENAGE_KEY_LEN))
467 return CMD_WARNING;
468
469 if (get_subscr_by_argv(vty, id_type, id, &subscr))
470 return CMD_WARNING;
471
472 rc = db_subscr_update_aud_by_id(g_hlr->dbc, subscr.id, &aud3g);
473
474 if (rc) {
475 vty_out(vty, "%% Error: cannot set 3G auth data for IMSI='%s'%s",
476 subscr.imsi, VTY_NEWLINE);
477 return CMD_WARNING;
478 }
479 return CMD_SUCCESS;
480}
481
Harald Welted5807b82018-07-29 12:27:41 +0200482void hlr_vty_subscriber_init(void)
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200483{
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200484 install_element_ve(&subscriber_show_cmd);
Neels Hofmeyr8aa780b2018-12-02 18:52:49 +0100485 install_element_ve(&show_subscriber_cmd);
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200486 install_element(ENABLE_NODE, &subscriber_create_cmd);
487 install_element(ENABLE_NODE, &subscriber_delete_cmd);
488 install_element(ENABLE_NODE, &subscriber_msisdn_cmd);
489 install_element(ENABLE_NODE, &subscriber_no_aud2g_cmd);
490 install_element(ENABLE_NODE, &subscriber_aud2g_cmd);
491 install_element(ENABLE_NODE, &subscriber_no_aud3g_cmd);
492 install_element(ENABLE_NODE, &subscriber_aud3g_cmd);
493}