blob: 02a1d335371c69436b038f1184382da4390a15bc [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>
Stefan Sperling5c14c9c2018-12-07 12:30:21 +010023#include <sys/types.h>
24#include <time.h>
Neels Hofmeyr183e7002017-10-06 02:59:54 +020025
26#include <osmocom/gsm/gsm23003.h>
27#include <osmocom/vty/vty.h>
28#include <osmocom/vty/command.h>
29#include <osmocom/core/utils.h>
Neels Hofmeyrf1949f72018-12-26 01:49:53 +010030#include <osmocom/gsm/gsm_utils.h>
Neels Hofmeyr183e7002017-10-06 02:59:54 +020031
32#include "hlr.h"
33#include "db.h"
34
35struct vty;
36
37#define hexdump_buf(buf) osmo_hexdump_nospc((void*)buf, sizeof(buf))
38
Neels Hofmeyrf302de92018-12-25 17:23:41 +010039static char *get_datestr(const time_t *t)
Stefan Sperling5c14c9c2018-12-07 12:30:21 +010040{
Neels Hofmeyrf302de92018-12-25 17:23:41 +010041 static char buf[32];
42 struct tm tm;
Stefan Sperling5c14c9c2018-12-07 12:30:21 +010043
Neels Hofmeyrf302de92018-12-25 17:23:41 +010044 tm = *gmtime(t);
45
46 strftime(buf, sizeof(buf), "%FT%T+00:00", &tm);
47 return buf;
Stefan Sperling5c14c9c2018-12-07 12:30:21 +010048}
49
Neels Hofmeyr183e7002017-10-06 02:59:54 +020050static void subscr_dump_full_vty(struct vty *vty, struct hlr_subscriber *subscr)
51{
52 int rc;
Neels Hofmeyrf1949f72018-12-26 01:49:53 +010053 int i;
Neels Hofmeyr183e7002017-10-06 02:59:54 +020054 struct osmo_sub_auth_data aud2g;
55 struct osmo_sub_auth_data aud3g;
56
57 vty_out(vty, " ID: %"PRIu64"%s", subscr->id, VTY_NEWLINE);
58
Neels Hofmeyr36bec872017-10-23 18:44:23 +020059 vty_out(vty, " IMSI: %s%s", *subscr->imsi ? subscr->imsi : "none", VTY_NEWLINE);
Neels Hofmeyr183e7002017-10-06 02:59:54 +020060 vty_out(vty, " MSISDN: %s%s", *subscr->msisdn ? subscr->msisdn : "none", VTY_NEWLINE);
Oliver Smith02078b72019-01-11 15:41:29 +010061
62 if (*subscr->imei) {
63 char checksum = osmo_luhn(subscr->imei, 14);
64 if (checksum == -EINVAL)
65 vty_out(vty, " IMEI: %s (INVALID LENGTH!)%s", subscr->imei, VTY_NEWLINE);
66 else
67 vty_out(vty, " IMEI: %s%c%s", subscr->imei, checksum, VTY_NEWLINE);
68 }
69
Neels Hofmeyr183e7002017-10-06 02:59:54 +020070 if (*subscr->vlr_number)
71 vty_out(vty, " VLR number: %s%s", subscr->vlr_number, VTY_NEWLINE);
72 if (*subscr->sgsn_number)
73 vty_out(vty, " SGSN number: %s%s", subscr->sgsn_number, VTY_NEWLINE);
74 if (*subscr->sgsn_address)
75 vty_out(vty, " SGSN address: %s%s", subscr->sgsn_address, VTY_NEWLINE);
76 if (subscr->periodic_lu_timer)
77 vty_out(vty, " Periodic LU timer: %u%s", subscr->periodic_lu_timer, VTY_NEWLINE);
78 if (subscr->periodic_rau_tau_timer)
79 vty_out(vty, " Periodic RAU/TAU timer: %u%s", subscr->periodic_rau_tau_timer, VTY_NEWLINE);
80 if (subscr->lmsi)
81 vty_out(vty, " LMSI: %x%s", subscr->lmsi, VTY_NEWLINE);
82 if (!subscr->nam_cs)
83 vty_out(vty, " CS disabled%s", VTY_NEWLINE);
84 if (subscr->ms_purged_cs)
85 vty_out(vty, " CS purged%s", VTY_NEWLINE);
86 if (!subscr->nam_ps)
87 vty_out(vty, " PS disabled%s", VTY_NEWLINE);
88 if (subscr->ms_purged_ps)
89 vty_out(vty, " PS purged%s", VTY_NEWLINE);
Stefan Sperling5c14c9c2018-12-07 12:30:21 +010090 if (subscr->last_lu_seen)
Neels Hofmeyrf302de92018-12-25 17:23:41 +010091 vty_out(vty, " last LU seen: %s%s", get_datestr(&subscr->last_lu_seen), VTY_NEWLINE);
Neels Hofmeyrb984a6c2018-12-29 03:28:38 +010092 if (subscr->last_lu_rat[0])
93 vty_out(vty, " last LU RAT: %s%s", subscr->last_lu_rat, VTY_NEWLINE);
Neels Hofmeyrf1949f72018-12-26 01:49:53 +010094 for (i = OSMO_RAT_UNKNOWN + 1; i < ARRAY_SIZE(subscr->rat_types); i++) {
95 vty_out(vty, " %s: %s%s", osmo_rat_type_name(i), subscr->rat_types[i] ? "allowed" : "forbidden",
96 VTY_NEWLINE);
97 }
98 if (subscr->ms_purged_cs)
99 vty_out(vty, " CS purged%s", VTY_NEWLINE);
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200100
101 if (!*subscr->imsi)
102 return;
103
104 OSMO_ASSERT(g_hlr);
105 rc = db_get_auth_data(g_hlr->dbc, subscr->imsi, &aud2g, &aud3g, NULL);
106
Neels Hofmeyrbd1dca02017-11-23 15:25:30 +0100107 switch (rc) {
108 case 0:
109 break;
110 case -ENOENT:
111 case -ENOKEY:
112 aud2g.algo = OSMO_AUTH_ALG_NONE;
113 aud3g.algo = OSMO_AUTH_ALG_NONE;
114 break;
115 default:
116 vty_out(vty, "%% Error retrieving data from database (%d)%s", rc, VTY_NEWLINE);
117 return;
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200118 }
119
120 if (aud2g.type != OSMO_AUTH_TYPE_NONE && aud2g.type != OSMO_AUTH_TYPE_GSM) {
121 vty_out(vty, "%% Error: 2G auth data is not of type 'GSM'%s", VTY_NEWLINE);
122 aud2g = (struct osmo_sub_auth_data){};
123 }
124
125 if (aud3g.type != OSMO_AUTH_TYPE_NONE && aud3g.type != OSMO_AUTH_TYPE_UMTS) {
126 vty_out(vty, "%% Error: 3G auth data is not of type 'UMTS'%s", VTY_NEWLINE);
127 aud3g = (struct osmo_sub_auth_data){};
128 }
129
130 if (aud2g.algo != OSMO_AUTH_ALG_NONE && aud2g.type != OSMO_AUTH_TYPE_NONE) {
131 vty_out(vty, " 2G auth: %s%s",
132 osmo_auth_alg_name(aud2g.algo), VTY_NEWLINE);
133 vty_out(vty, " KI=%s%s",
134 hexdump_buf(aud2g.u.gsm.ki), VTY_NEWLINE);
135 }
136
137 if (aud3g.algo != OSMO_AUTH_ALG_NONE && aud3g.type != OSMO_AUTH_TYPE_NONE) {
138 vty_out(vty, " 3G auth: %s%s", osmo_auth_alg_name(aud3g.algo), VTY_NEWLINE);
139 vty_out(vty, " K=%s%s", hexdump_buf(aud3g.u.umts.k), VTY_NEWLINE);
140 vty_out(vty, " %s=%s%s", aud3g.u.umts.opc_is_op? "OP" : "OPC",
141 hexdump_buf(aud3g.u.umts.opc), VTY_NEWLINE);
142 vty_out(vty, " IND-bitlen=%u", aud3g.u.umts.ind_bitlen);
143 if (aud3g.u.umts.sqn)
144 vty_out(vty, " last-SQN=%"PRIu64, aud3g.u.umts.sqn);
145 vty_out(vty, VTY_NEWLINE);
146 }
147}
148
149static int get_subscr_by_argv(struct vty *vty, const char *type, const char *id, struct hlr_subscriber *subscr)
150{
Oliver Smith02078b72019-01-11 15:41:29 +0100151 char imei_buf[GSM23003_IMEI_NUM_DIGITS_NO_CHK+1];
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200152 int rc = -1;
153 if (strcmp(type, "imsi") == 0)
154 rc = db_subscr_get_by_imsi(g_hlr->dbc, id, subscr);
155 else if (strcmp(type, "msisdn") == 0)
156 rc = db_subscr_get_by_msisdn(g_hlr->dbc, id, subscr);
157 else if (strcmp(type, "id") == 0)
158 rc = db_subscr_get_by_id(g_hlr->dbc, atoll(id), subscr);
Oliver Smith02078b72019-01-11 15:41:29 +0100159 else if (strcmp(type, "imei") == 0) {
160 /* Verify IMEI with checksum digit */
161 if (osmo_imei_str_valid(id, true)) {
162 /* Cut the checksum off */
163 osmo_strlcpy(imei_buf, id, sizeof(imei_buf));
164 id = imei_buf;
165 vty_out(vty, "%% Checksum validated and stripped for search: imei = '%s'%s", id,
166 VTY_NEWLINE);
167 }
168 rc = db_subscr_get_by_imei(g_hlr->dbc, id, subscr);
169 }
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200170 if (rc)
171 vty_out(vty, "%% No subscriber for %s = '%s'%s",
172 type, id, VTY_NEWLINE);
173 return rc;
174}
175
176#define SUBSCR_CMD "subscriber "
177#define SUBSCR_CMD_HELP "Subscriber management commands\n"
178
Oliver Smith02078b72019-01-11 15:41:29 +0100179#define SUBSCR_ID "(imsi|msisdn|id|imei) IDENT"
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200180#define SUBSCR_ID_HELP \
181 "Identify subscriber by IMSI\n" \
182 "Identify subscriber by MSISDN (phone number)\n" \
183 "Identify subscriber by database ID\n" \
Oliver Smith02078b72019-01-11 15:41:29 +0100184 "Identify subscriber by IMEI\n" \
185 "IMSI/MSISDN/ID/IMEI of the subscriber\n"
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200186
Neels Hofmeyr8aa780b2018-12-02 18:52:49 +0100187#define SUBSCR SUBSCR_CMD SUBSCR_ID " "
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200188#define SUBSCR_HELP SUBSCR_CMD_HELP SUBSCR_ID_HELP
189
190#define SUBSCR_UPDATE SUBSCR "update "
191#define SUBSCR_UPDATE_HELP SUBSCR_HELP "Set or update subscriber data\n"
Neels Hofmeyra820ea12018-12-02 19:46:46 +0100192#define SUBSCR_MSISDN_HELP "Set MSISDN (phone number) of the subscriber\n"
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200193
194DEFUN(subscriber_show,
195 subscriber_show_cmd,
196 SUBSCR "show",
197 SUBSCR_HELP "Show subscriber information\n")
198{
199 struct hlr_subscriber subscr;
200 const char *id_type = argv[0];
201 const char *id = argv[1];
202
203 if (get_subscr_by_argv(vty, id_type, id, &subscr))
204 return CMD_WARNING;
205
206 subscr_dump_full_vty(vty, &subscr);
207 return CMD_SUCCESS;
208}
209
Neels Hofmeyr8aa780b2018-12-02 18:52:49 +0100210ALIAS(subscriber_show, show_subscriber_cmd,
211 "show " SUBSCR_CMD SUBSCR_ID,
212 SHOW_STR SUBSCR_CMD_HELP SUBSCR_ID_HELP);
213
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200214DEFUN(subscriber_create,
215 subscriber_create_cmd,
216 SUBSCR_CMD "imsi IDENT create",
217 SUBSCR_CMD_HELP
Vadim Yanitskiyf473c7b2018-07-30 14:29:39 +0700218 "Identify subscriber by IMSI\n"
219 "IMSI/MSISDN/ID of the subscriber\n"
220 "Create subscriber by IMSI\n")
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200221{
222 int rc;
223 struct hlr_subscriber subscr;
224 const char *imsi = argv[0];
225
226 if (!osmo_imsi_str_valid(imsi)) {
227 vty_out(vty, "%% Not a valid IMSI: %s%s", imsi, VTY_NEWLINE);
228 return CMD_WARNING;
229 }
230
Oliver Smithcd2af5e2019-03-06 13:17:39 +0100231 rc = db_subscr_create(g_hlr->dbc, imsi, DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS);
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200232
233 if (rc) {
234 if (rc == -EEXIST)
235 vty_out(vty, "%% Subscriber already exists for IMSI = %s%s",
236 imsi, VTY_NEWLINE);
237 else
238 vty_out(vty, "%% Error (rc=%d): cannot create subscriber for IMSI = %s%s",
239 rc, imsi, VTY_NEWLINE);
240 return CMD_WARNING;
241 }
242
243 rc = db_subscr_get_by_imsi(g_hlr->dbc, imsi, &subscr);
244 vty_out(vty, "%% Created subscriber %s%s", imsi, VTY_NEWLINE);
245
246 subscr_dump_full_vty(vty, &subscr);
247
248 return CMD_SUCCESS;
249}
250
251DEFUN(subscriber_delete,
252 subscriber_delete_cmd,
253 SUBSCR "delete",
254 SUBSCR_HELP "Delete subscriber from database\n")
255{
256 struct hlr_subscriber subscr;
257 int rc;
258 const char *id_type = argv[0];
259 const char *id = argv[1];
260
261 /* Find out the IMSI regardless of which way the caller decided to
262 * identify the subscriber by. */
263 if (get_subscr_by_argv(vty, id_type, id, &subscr))
264 return CMD_WARNING;
265
266 rc = db_subscr_delete_by_id(g_hlr->dbc, subscr.id);
267 if (rc) {
268 vty_out(vty, "%% Error: Failed to remove subscriber for IMSI '%s'%s",
269 subscr.imsi, VTY_NEWLINE);
270 return CMD_WARNING;
271 }
272
273 vty_out(vty, "%% Deleted subscriber for IMSI '%s'%s", subscr.imsi, VTY_NEWLINE);
274 return CMD_SUCCESS;
275}
276
277DEFUN(subscriber_msisdn,
278 subscriber_msisdn_cmd,
Neels Hofmeyra820ea12018-12-02 19:46:46 +0100279 SUBSCR_UPDATE "msisdn (none|MSISDN)",
280 SUBSCR_UPDATE_HELP SUBSCR_MSISDN_HELP
281 "Remove MSISDN (phone number)\n"
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200282 "New MSISDN (phone number)\n")
283{
284 struct hlr_subscriber subscr;
285 const char *id_type = argv[0];
286 const char *id = argv[1];
287 const char *msisdn = argv[2];
288
Neels Hofmeyra820ea12018-12-02 19:46:46 +0100289 if (strcmp(msisdn, "none") == 0)
290 msisdn = NULL;
291 else {
292 if (strlen(msisdn) > sizeof(subscr.msisdn) - 1) {
293 vty_out(vty, "%% MSISDN is too long, max. %zu characters are allowed%s",
294 sizeof(subscr.msisdn)-1, VTY_NEWLINE);
295 return CMD_WARNING;
296 }
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200297
Neels Hofmeyra820ea12018-12-02 19:46:46 +0100298 if (!osmo_msisdn_str_valid(msisdn)) {
299 vty_out(vty, "%% MSISDN invalid: '%s'%s", msisdn, VTY_NEWLINE);
300 return CMD_WARNING;
301 }
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200302 }
303
304 if (get_subscr_by_argv(vty, id_type, id, &subscr))
305 return CMD_WARNING;
306
307 if (db_subscr_update_msisdn_by_imsi(g_hlr->dbc, subscr.imsi, msisdn)) {
308 vty_out(vty, "%% Error: cannot update MSISDN for subscriber IMSI='%s'%s",
309 subscr.imsi, VTY_NEWLINE);
310 return CMD_WARNING;
311 }
312
Neels Hofmeyra820ea12018-12-02 19:46:46 +0100313 if (msisdn) {
314 vty_out(vty, "%% Updated subscriber IMSI='%s' to MSISDN='%s'%s",
315 subscr.imsi, msisdn, VTY_NEWLINE);
Stefan Sperlingf1622522018-04-09 11:39:16 +0200316
Neels Hofmeyra820ea12018-12-02 19:46:46 +0100317 if (db_subscr_get_by_msisdn(g_hlr->dbc, msisdn, &subscr) == 0)
318 osmo_hlr_subscriber_update_notify(&subscr);
319 } else {
320 vty_out(vty, "%% Updated subscriber IMSI='%s': removed MSISDN%s",
321 subscr.imsi, VTY_NEWLINE);
322
Stefan Sperlingf1622522018-04-09 11:39:16 +0200323 osmo_hlr_subscriber_update_notify(&subscr);
Neels Hofmeyra820ea12018-12-02 19:46:46 +0100324 }
Stefan Sperlingf1622522018-04-09 11:39:16 +0200325
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200326 return CMD_SUCCESS;
327}
328
329static bool is_hexkey_valid(struct vty *vty, const char *label,
330 const char *hex_str, int minlen, int maxlen)
331{
332 if (osmo_is_hexstr(hex_str, minlen * 2, maxlen * 2, true))
333 return true;
334 vty_out(vty, "%% Invalid value for %s: '%s'%s", label, hex_str, VTY_NEWLINE);
335 return false;
336}
337
338#define AUTH_ALG_TYPES_2G "(comp128v1|comp128v2|comp128v3|xor)"
339#define AUTH_ALG_TYPES_2G_HELP \
340 "Use COMP128v1 algorithm\n" \
341 "Use COMP128v2 algorithm\n" \
342 "Use COMP128v3 algorithm\n" \
343 "Use XOR algorithm\n"
344
345#define AUTH_ALG_TYPES_3G "milenage"
346#define AUTH_ALG_TYPES_3G_HELP \
347 "Use Milenage algorithm\n"
348
349#define A38_XOR_MIN_KEY_LEN 12
350#define A38_XOR_MAX_KEY_LEN 16
351#define A38_COMP128_KEY_LEN 16
352
353#define MILENAGE_KEY_LEN 16
354
355static bool auth_algo_parse(const char *alg_str, enum osmo_auth_algo *algo,
356 int *minlen, int *maxlen)
357{
358 if (!strcasecmp(alg_str, "none")) {
359 *algo = OSMO_AUTH_ALG_NONE;
360 *minlen = *maxlen = 0;
361 } else if (!strcasecmp(alg_str, "comp128v1")) {
362 *algo = OSMO_AUTH_ALG_COMP128v1;
363 *minlen = *maxlen = A38_COMP128_KEY_LEN;
364 } else if (!strcasecmp(alg_str, "comp128v2")) {
365 *algo = OSMO_AUTH_ALG_COMP128v2;
366 *minlen = *maxlen = A38_COMP128_KEY_LEN;
367 } else if (!strcasecmp(alg_str, "comp128v3")) {
368 *algo = OSMO_AUTH_ALG_COMP128v3;
369 *minlen = *maxlen = A38_COMP128_KEY_LEN;
370 } else if (!strcasecmp(alg_str, "xor")) {
371 *algo = OSMO_AUTH_ALG_XOR;
372 *minlen = A38_XOR_MIN_KEY_LEN;
373 *maxlen = A38_XOR_MAX_KEY_LEN;
374 } else if (!strcasecmp(alg_str, "milenage")) {
375 *algo = OSMO_AUTH_ALG_MILENAGE;
376 *minlen = *maxlen = MILENAGE_KEY_LEN;
377 } else
378 return false;
379 return true;
380}
381
382DEFUN(subscriber_no_aud2g,
383 subscriber_no_aud2g_cmd,
384 SUBSCR_UPDATE "aud2g none",
385 SUBSCR_UPDATE_HELP
386 "Set 2G authentication data\n"
387 "Delete 2G authentication data\n")
388{
389 struct hlr_subscriber subscr;
390 int rc;
391 const char *id_type = argv[0];
392 const char *id = argv[1];
393 struct sub_auth_data_str aud = {
394 .type = OSMO_AUTH_TYPE_GSM,
395 .algo = OSMO_AUTH_ALG_NONE,
396 };
397
398 if (get_subscr_by_argv(vty, id_type, id, &subscr))
399 return CMD_WARNING;
400
401 rc = db_subscr_update_aud_by_id(g_hlr->dbc, subscr.id, &aud);
402
Harald Welte880a34d2018-03-01 21:32:01 +0100403 if (rc && rc != -ENOENT) {
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200404 vty_out(vty, "%% Error: cannot disable 2G auth data for IMSI='%s'%s",
405 subscr.imsi, VTY_NEWLINE);
406 return CMD_WARNING;
407 }
408 return CMD_SUCCESS;
409}
410
411DEFUN(subscriber_aud2g,
412 subscriber_aud2g_cmd,
413 SUBSCR_UPDATE "aud2g " AUTH_ALG_TYPES_2G " ki KI",
414 SUBSCR_UPDATE_HELP
415 "Set 2G authentication data\n"
416 AUTH_ALG_TYPES_2G_HELP
417 "Set Ki Encryption Key\n" "Ki as 32 hexadecimal characters\n")
418{
419 struct hlr_subscriber subscr;
420 int rc;
421 int minlen = 0;
422 int maxlen = 0;
423 const char *id_type = argv[0];
424 const char *id = argv[1];
425 const char *alg_type = argv[2];
426 const char *ki = argv[3];
427 struct sub_auth_data_str aud2g = {
428 .type = OSMO_AUTH_TYPE_GSM,
429 .u.gsm.ki = ki,
430 };
431
432 if (!auth_algo_parse(alg_type, &aud2g.algo, &minlen, &maxlen)) {
433 vty_out(vty, "%% Unknown auth algorithm: '%s'%s", alg_type, VTY_NEWLINE);
434 return CMD_WARNING;
435 }
436
437 if (!is_hexkey_valid(vty, "KI", aud2g.u.gsm.ki, minlen, maxlen))
438 return CMD_WARNING;
439
440 if (get_subscr_by_argv(vty, id_type, id, &subscr))
441 return CMD_WARNING;
442
443 rc = db_subscr_update_aud_by_id(g_hlr->dbc, subscr.id, &aud2g);
444
445 if (rc) {
446 vty_out(vty, "%% Error: cannot set 2G auth data for IMSI='%s'%s",
447 subscr.imsi, VTY_NEWLINE);
448 return CMD_WARNING;
449 }
450 return CMD_SUCCESS;
451}
452
453DEFUN(subscriber_no_aud3g,
454 subscriber_no_aud3g_cmd,
455 SUBSCR_UPDATE "aud3g none",
456 SUBSCR_UPDATE_HELP
457 "Set UMTS authentication data (3G, and 2G with UMTS AKA)\n"
458 "Delete 3G authentication data\n")
459{
460 struct hlr_subscriber subscr;
461 int rc;
462 const char *id_type = argv[0];
463 const char *id = argv[1];
464 struct sub_auth_data_str aud = {
465 .type = OSMO_AUTH_TYPE_UMTS,
466 .algo = OSMO_AUTH_ALG_NONE,
467 };
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, &aud);
473
Harald Welte880a34d2018-03-01 21:32:01 +0100474 if (rc && rc != -ENOENT) {
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200475 vty_out(vty, "%% Error: cannot disable 3G auth data for IMSI='%s'%s",
476 subscr.imsi, VTY_NEWLINE);
477 return CMD_WARNING;
478 }
479 return CMD_SUCCESS;
480}
481
482DEFUN(subscriber_aud3g,
483 subscriber_aud3g_cmd,
484 SUBSCR_UPDATE "aud3g " AUTH_ALG_TYPES_3G
485 " k K"
486 " (op|opc) OP_C"
487 " [ind-bitlen] [<0-28>]",
488 SUBSCR_UPDATE_HELP
489 "Set UMTS authentication data (3G, and 2G with UMTS AKA)\n"
490 AUTH_ALG_TYPES_3G_HELP
491 "Set Encryption Key K\n" "K as 32 hexadecimal characters\n"
492 "Set OP key\n" "Set OPC key\n" "OP or OPC as 32 hexadecimal characters\n"
493 "Set IND bit length\n" "IND bit length value (default: 5)\n")
494{
495 struct hlr_subscriber subscr;
496 int minlen = 0;
497 int maxlen = 0;
498 int rc;
499 const char *id_type = argv[0];
500 const char *id = argv[1];
501 const char *alg_type = AUTH_ALG_TYPES_3G;
502 const char *k = argv[2];
503 bool opc_is_op = (strcasecmp("op", argv[3]) == 0);
504 const char *op_opc = argv[4];
505 int ind_bitlen = argc > 6? atoi(argv[6]) : 5;
506 struct sub_auth_data_str aud3g = {
507 .type = OSMO_AUTH_TYPE_UMTS,
508 .u.umts = {
509 .k = k,
510 .opc_is_op = opc_is_op,
511 .opc = op_opc,
512 .ind_bitlen = ind_bitlen,
513 },
514 };
515
516 if (!auth_algo_parse(alg_type, &aud3g.algo, &minlen, &maxlen)) {
517 vty_out(vty, "%% Unknown auth algorithm: '%s'%s", alg_type, VTY_NEWLINE);
518 return CMD_WARNING;
519 }
520
521 if (!is_hexkey_valid(vty, "K", aud3g.u.umts.k, minlen, maxlen))
522 return CMD_WARNING;
523
524 if (!is_hexkey_valid(vty, opc_is_op ? "OP" : "OPC", aud3g.u.umts.opc,
525 MILENAGE_KEY_LEN, MILENAGE_KEY_LEN))
526 return CMD_WARNING;
527
528 if (get_subscr_by_argv(vty, id_type, id, &subscr))
529 return CMD_WARNING;
530
531 rc = db_subscr_update_aud_by_id(g_hlr->dbc, subscr.id, &aud3g);
532
533 if (rc) {
534 vty_out(vty, "%% Error: cannot set 3G auth data for IMSI='%s'%s",
535 subscr.imsi, VTY_NEWLINE);
536 return CMD_WARNING;
537 }
538 return CMD_SUCCESS;
539}
540
Oliver Smith02078b72019-01-11 15:41:29 +0100541DEFUN(subscriber_imei,
542 subscriber_imei_cmd,
543 SUBSCR_UPDATE "imei (none|IMEI)",
544 SUBSCR_UPDATE_HELP
545 "Set IMEI of the subscriber (normally populated from MSC, no need to set this manually)\n"
546 "Forget IMEI\n"
547 "Set IMEI (use for debug only!)\n")
548{
549 struct hlr_subscriber subscr;
550 const char *id_type = argv[0];
551 const char *id = argv[1];
552 const char *imei = argv[2];
553 char imei_buf[GSM23003_IMEI_NUM_DIGITS_NO_CHK+1];
554
555 if (strcmp(imei, "none") == 0)
556 imei = NULL;
557 else {
558 /* Verify IMEI with checksum digit */
559 if (osmo_imei_str_valid(imei, true)) {
560 /* Cut the checksum off */
561 osmo_strlcpy(imei_buf, imei, sizeof(imei_buf));
562 imei = imei_buf;
563 } else if (!osmo_imei_str_valid(imei, false)) {
564 vty_out(vty, "%% IMEI invalid: '%s'%s", imei, VTY_NEWLINE);
565 return CMD_WARNING;
566 }
567 }
568
569 if (get_subscr_by_argv(vty, id_type, id, &subscr))
570 return CMD_WARNING;
571
572 if (db_subscr_update_imei_by_imsi(g_hlr->dbc, subscr.imsi, imei)) {
573 vty_out(vty, "%% Error: cannot update IMEI for subscriber IMSI='%s'%s",
574 subscr.imsi, VTY_NEWLINE);
575 return CMD_WARNING;
576 }
577
578 if (imei)
579 vty_out(vty, "%% Updated subscriber IMSI='%s' to IMEI='%s'%s",
580 subscr.imsi, imei, VTY_NEWLINE);
581 else
582 vty_out(vty, "%% Updated subscriber IMSI='%s': removed IMEI%s",
583 subscr.imsi, VTY_NEWLINE);
584
585 return CMD_SUCCESS;
586}
587
Oliver Smith3b33b012019-07-15 10:35:22 +0200588DEFUN(subscriber_nam,
589 subscriber_nam_cmd,
590 SUBSCR_UPDATE "network-access-mode (none|cs|ps|cs+ps)",
591 SUBSCR_UPDATE_HELP
592 "Set Network Access Mode (NAM) of the subscriber\n"
593 "Do not allow access to circuit switched or packet switched services\n"
594 "Allow access to circuit switched services only\n"
595 "Allow access to packet switched services only\n"
596 "Allow access to both circuit and packet switched services\n")
597{
598 struct hlr_subscriber subscr;
599 const char *id_type = argv[0];
600 const char *id = argv[1];
601 bool nam_cs = strstr(argv[2], "cs");
602 bool nam_ps = strstr(argv[2], "ps");
603
604 if (get_subscr_by_argv(vty, id_type, id, &subscr))
605 return CMD_WARNING;
606
607 if (nam_cs != subscr.nam_cs)
608 hlr_subscr_nam(g_hlr, &subscr, nam_cs, 0);
609 if (nam_ps != subscr.nam_ps)
610 hlr_subscr_nam(g_hlr, &subscr, nam_ps, 1);
611
612 return CMD_SUCCESS;
613}
614
Oliver Smith02078b72019-01-11 15:41:29 +0100615
Neels Hofmeyrf1949f72018-12-26 01:49:53 +0100616DEFUN(subscriber_rat,
617 subscriber_rat_cmd,
Piotr Krysik0b497962019-08-22 11:18:15 +0200618 SUBSCR_UPDATE "rat (geran-a|utran-iu|eutran-sgs) (allowed|forbidden)",
Neels Hofmeyrf1949f72018-12-26 01:49:53 +0100619 SUBSCR_UPDATE_HELP
620 "Allow or forbid specific Radio Access Types\n"
621 "Set access to GERAN-A\n"
622 "Set access to UTRAN-Iu\n"
Piotr Krysik0b497962019-08-22 11:18:15 +0200623 "Set access to EUTRAN-SGs\n"
Neels Hofmeyrf1949f72018-12-26 01:49:53 +0100624 "Allow access\n"
625 "Forbid access\n")
626{
627 struct hlr_subscriber subscr;
628 const char *id_type = argv[0];
629 const char *id = argv[1];
630 const char *rat_str = argv[2];
631 const char *allowed_forbidden = argv[3];
Harald Welte15f88102019-08-21 20:01:31 +0200632 enum osmo_rat_type rat = OSMO_RAT_UNKNOWN;
Neels Hofmeyrf1949f72018-12-26 01:49:53 +0100633 bool allowed;
634 int rc;
635
636 if (strcmp(rat_str, "geran-a") == 0)
637 rat = OSMO_RAT_GERAN_A;
638 else if (strcmp(rat_str, "utran-iu") == 0)
639 rat = OSMO_RAT_UTRAN_IU;
Piotr Krysik0b497962019-08-22 11:18:15 +0200640 else if (strcmp(rat_str, "eutran-sgs") == 0)
Harald Welte15f88102019-08-21 20:01:31 +0200641 rat = OSMO_RAT_EUTRAN_SGS;
Neels Hofmeyrf1949f72018-12-26 01:49:53 +0100642
643 allowed = (strcmp(allowed_forbidden, "allowed") == 0);
644
645 if (get_subscr_by_argv(vty, id_type, id, &subscr))
646 return CMD_WARNING;
647
648 rc = hlr_subscr_rat_flag(g_hlr, &subscr, rat, allowed);
649
650 if (rc && rc != -ENOEXEC) {
651 vty_out(vty, "%% Error: cannot set %s to %s%s",
652 osmo_rat_type_name(rat), allowed ? "allowed" : "forbidden", VTY_NEWLINE);
653 return CMD_WARNING;
654 }
655 return CMD_SUCCESS;
656}
657
Harald Welted5807b82018-07-29 12:27:41 +0200658void hlr_vty_subscriber_init(void)
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200659{
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200660 install_element_ve(&subscriber_show_cmd);
Neels Hofmeyr8aa780b2018-12-02 18:52:49 +0100661 install_element_ve(&show_subscriber_cmd);
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200662 install_element(ENABLE_NODE, &subscriber_create_cmd);
663 install_element(ENABLE_NODE, &subscriber_delete_cmd);
664 install_element(ENABLE_NODE, &subscriber_msisdn_cmd);
665 install_element(ENABLE_NODE, &subscriber_no_aud2g_cmd);
666 install_element(ENABLE_NODE, &subscriber_aud2g_cmd);
667 install_element(ENABLE_NODE, &subscriber_no_aud3g_cmd);
668 install_element(ENABLE_NODE, &subscriber_aud3g_cmd);
Oliver Smith02078b72019-01-11 15:41:29 +0100669 install_element(ENABLE_NODE, &subscriber_imei_cmd);
Oliver Smith3b33b012019-07-15 10:35:22 +0200670 install_element(ENABLE_NODE, &subscriber_nam_cmd);
Neels Hofmeyrf1949f72018-12-26 01:49:53 +0100671 install_element(ENABLE_NODE, &subscriber_rat_cmd);
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200672}