blob: 5311c13640febc0a8d88def2523409afc2791d67 [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 Hofmeyrce172ef2018-12-26 01:49:53 +010030#include <osmocom/gsm/gsm_utils.h>
Neels Hofmeyr183e7002017-10-06 02:59:54 +020031
Neels Hofmeyr2f758032019-11-20 00:37:07 +010032#include <osmocom/hlr/hlr.h>
33#include <osmocom/hlr/db.h>
Oliver Smithf6d457e2019-11-25 13:28:23 +010034#include <osmocom/hlr/timestamp.h>
Neels Hofmeyr183e7002017-10-06 02:59:54 +020035
36struct vty;
37
38#define hexdump_buf(buf) osmo_hexdump_nospc((void*)buf, sizeof(buf))
39
Neels Hofmeyrcb88c342018-12-25 17:23:41 +010040static char *get_datestr(const time_t *t, char *buf, size_t bufsize)
Stefan Sperling5c14c9c2018-12-07 12:30:21 +010041{
Neels Hofmeyrcb88c342018-12-25 17:23:41 +010042 struct tm *tm;
Stefan Sperling5c14c9c2018-12-07 12:30:21 +010043
Neels Hofmeyrcb88c342018-12-25 17:23:41 +010044 tm = gmtime(t);
45 if (!tm)
46 return "UNKNOWN";
47
48 strftime(buf, bufsize, "%FT%T+00:00", tm);
49 return buf;
Stefan Sperling5c14c9c2018-12-07 12:30:21 +010050}
51
Neels Hofmeyr5bc457e2018-12-29 03:28:38 +010052static void dump_last_lu_seen(struct vty *vty, const char *domain_label, time_t last_lu_seen, const char *last_lu_rat)
Neels Hofmeyr07e16022019-11-20 02:36:35 +010053{
Oliver Smithf6d457e2019-11-25 13:28:23 +010054 uint32_t age;
Neels Hofmeyrcb88c342018-12-25 17:23:41 +010055 char datebuf[32];
Neels Hofmeyr07e16022019-11-20 02:36:35 +010056 if (!last_lu_seen)
57 return;
Neels Hofmeyrcb88c342018-12-25 17:23:41 +010058 vty_out(vty, " last LU seen on %s: %s", domain_label, get_datestr(&last_lu_seen, datebuf, sizeof(datebuf)));
Oliver Smithf6d457e2019-11-25 13:28:23 +010059 if (!timestamp_age(&last_lu_seen, &age))
Neels Hofmeyr5bc457e2018-12-29 03:28:38 +010060 vty_out(vty, " (invalid timestamp)");
Neels Hofmeyr011e7812019-11-27 16:47:50 +010061 else {
62 vty_out(vty, " (");
63#define UNIT_AGO(UNITNAME, UNITVAL) \
64 if (age >= (UNITVAL)) { \
65 vty_out(vty, "%u%s", age / (UNITVAL), UNITNAME); \
66 age = age % (UNITVAL); \
67 }
68 UNIT_AGO("d", 60*60*24);
69 UNIT_AGO("h", 60*60);
70 UNIT_AGO("m", 60);
71 UNIT_AGO("s", 1);
Neels Hofmeyr5bc457e2018-12-29 03:28:38 +010072 vty_out(vty, " ago)");
Neels Hofmeyr011e7812019-11-27 16:47:50 +010073#undef UNIT_AGO
74 }
Neels Hofmeyr5bc457e2018-12-29 03:28:38 +010075 if (last_lu_rat && *last_lu_rat != '\0')
76 vty_out(vty, " on %s", last_lu_rat);
77 vty_out(vty, "%s", VTY_NEWLINE);
Neels Hofmeyr07e16022019-11-20 02:36:35 +010078}
79
Neels Hofmeyr183e7002017-10-06 02:59:54 +020080static void subscr_dump_full_vty(struct vty *vty, struct hlr_subscriber *subscr)
81{
82 int rc;
Neels Hofmeyrce172ef2018-12-26 01:49:53 +010083 int i;
Neels Hofmeyr183e7002017-10-06 02:59:54 +020084 struct osmo_sub_auth_data aud2g;
85 struct osmo_sub_auth_data aud3g;
86
87 vty_out(vty, " ID: %"PRIu64"%s", subscr->id, VTY_NEWLINE);
88
Neels Hofmeyr36bec872017-10-23 18:44:23 +020089 vty_out(vty, " IMSI: %s%s", *subscr->imsi ? subscr->imsi : "none", VTY_NEWLINE);
Neels Hofmeyr183e7002017-10-06 02:59:54 +020090 vty_out(vty, " MSISDN: %s%s", *subscr->msisdn ? subscr->msisdn : "none", VTY_NEWLINE);
Oliver Smith02078b72019-01-11 15:41:29 +010091
92 if (*subscr->imei) {
93 char checksum = osmo_luhn(subscr->imei, 14);
94 if (checksum == -EINVAL)
95 vty_out(vty, " IMEI: %s (INVALID LENGTH!)%s", subscr->imei, VTY_NEWLINE);
96 else
97 vty_out(vty, " IMEI: %s%c%s", subscr->imei, checksum, VTY_NEWLINE);
98 }
99
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200100 if (*subscr->vlr_number)
101 vty_out(vty, " VLR number: %s%s", subscr->vlr_number, VTY_NEWLINE);
102 if (*subscr->sgsn_number)
103 vty_out(vty, " SGSN number: %s%s", subscr->sgsn_number, VTY_NEWLINE);
104 if (*subscr->sgsn_address)
105 vty_out(vty, " SGSN address: %s%s", subscr->sgsn_address, VTY_NEWLINE);
106 if (subscr->periodic_lu_timer)
107 vty_out(vty, " Periodic LU timer: %u%s", subscr->periodic_lu_timer, VTY_NEWLINE);
108 if (subscr->periodic_rau_tau_timer)
109 vty_out(vty, " Periodic RAU/TAU timer: %u%s", subscr->periodic_rau_tau_timer, VTY_NEWLINE);
110 if (subscr->lmsi)
111 vty_out(vty, " LMSI: %x%s", subscr->lmsi, VTY_NEWLINE);
112 if (!subscr->nam_cs)
113 vty_out(vty, " CS disabled%s", VTY_NEWLINE);
114 if (subscr->ms_purged_cs)
115 vty_out(vty, " CS purged%s", VTY_NEWLINE);
116 if (!subscr->nam_ps)
117 vty_out(vty, " PS disabled%s", VTY_NEWLINE);
118 if (subscr->ms_purged_ps)
119 vty_out(vty, " PS purged%s", VTY_NEWLINE);
Neels Hofmeyr5bc457e2018-12-29 03:28:38 +0100120 dump_last_lu_seen(vty, "CS", subscr->last_lu_seen, subscr->last_lu_rat_cs);
121 dump_last_lu_seen(vty, "PS", subscr->last_lu_seen_ps, subscr->last_lu_rat_ps);
Neels Hofmeyrce172ef2018-12-26 01:49:53 +0100122 for (i = OSMO_RAT_UNKNOWN + 1; i < ARRAY_SIZE(subscr->rat_types); i++) {
123 vty_out(vty, " %s: %s%s", osmo_rat_type_name(i), subscr->rat_types[i] ? "allowed" : "forbidden",
124 VTY_NEWLINE);
125 }
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200126
127 if (!*subscr->imsi)
128 return;
129
130 OSMO_ASSERT(g_hlr);
131 rc = db_get_auth_data(g_hlr->dbc, subscr->imsi, &aud2g, &aud3g, NULL);
132
Neels Hofmeyrbd1dca02017-11-23 15:25:30 +0100133 switch (rc) {
134 case 0:
135 break;
136 case -ENOENT:
137 case -ENOKEY:
138 aud2g.algo = OSMO_AUTH_ALG_NONE;
139 aud3g.algo = OSMO_AUTH_ALG_NONE;
140 break;
141 default:
142 vty_out(vty, "%% Error retrieving data from database (%d)%s", rc, VTY_NEWLINE);
143 return;
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200144 }
145
146 if (aud2g.type != OSMO_AUTH_TYPE_NONE && aud2g.type != OSMO_AUTH_TYPE_GSM) {
147 vty_out(vty, "%% Error: 2G auth data is not of type 'GSM'%s", VTY_NEWLINE);
148 aud2g = (struct osmo_sub_auth_data){};
149 }
150
151 if (aud3g.type != OSMO_AUTH_TYPE_NONE && aud3g.type != OSMO_AUTH_TYPE_UMTS) {
152 vty_out(vty, "%% Error: 3G auth data is not of type 'UMTS'%s", VTY_NEWLINE);
153 aud3g = (struct osmo_sub_auth_data){};
154 }
155
156 if (aud2g.algo != OSMO_AUTH_ALG_NONE && aud2g.type != OSMO_AUTH_TYPE_NONE) {
157 vty_out(vty, " 2G auth: %s%s",
158 osmo_auth_alg_name(aud2g.algo), VTY_NEWLINE);
159 vty_out(vty, " KI=%s%s",
160 hexdump_buf(aud2g.u.gsm.ki), VTY_NEWLINE);
161 }
162
163 if (aud3g.algo != OSMO_AUTH_ALG_NONE && aud3g.type != OSMO_AUTH_TYPE_NONE) {
164 vty_out(vty, " 3G auth: %s%s", osmo_auth_alg_name(aud3g.algo), VTY_NEWLINE);
165 vty_out(vty, " K=%s%s", hexdump_buf(aud3g.u.umts.k), VTY_NEWLINE);
166 vty_out(vty, " %s=%s%s", aud3g.u.umts.opc_is_op? "OP" : "OPC",
167 hexdump_buf(aud3g.u.umts.opc), VTY_NEWLINE);
168 vty_out(vty, " IND-bitlen=%u", aud3g.u.umts.ind_bitlen);
169 if (aud3g.u.umts.sqn)
170 vty_out(vty, " last-SQN=%"PRIu64, aud3g.u.umts.sqn);
171 vty_out(vty, VTY_NEWLINE);
172 }
173}
174
175static int get_subscr_by_argv(struct vty *vty, const char *type, const char *id, struct hlr_subscriber *subscr)
176{
Oliver Smith02078b72019-01-11 15:41:29 +0100177 char imei_buf[GSM23003_IMEI_NUM_DIGITS_NO_CHK+1];
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200178 int rc = -1;
179 if (strcmp(type, "imsi") == 0)
180 rc = db_subscr_get_by_imsi(g_hlr->dbc, id, subscr);
181 else if (strcmp(type, "msisdn") == 0)
182 rc = db_subscr_get_by_msisdn(g_hlr->dbc, id, subscr);
183 else if (strcmp(type, "id") == 0)
184 rc = db_subscr_get_by_id(g_hlr->dbc, atoll(id), subscr);
Oliver Smith02078b72019-01-11 15:41:29 +0100185 else if (strcmp(type, "imei") == 0) {
186 /* Verify IMEI with checksum digit */
187 if (osmo_imei_str_valid(id, true)) {
188 /* Cut the checksum off */
189 osmo_strlcpy(imei_buf, id, sizeof(imei_buf));
190 id = imei_buf;
191 vty_out(vty, "%% Checksum validated and stripped for search: imei = '%s'%s", id,
192 VTY_NEWLINE);
193 }
194 rc = db_subscr_get_by_imei(g_hlr->dbc, id, subscr);
195 }
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200196 if (rc)
197 vty_out(vty, "%% No subscriber for %s = '%s'%s",
198 type, id, VTY_NEWLINE);
199 return rc;
200}
201
202#define SUBSCR_CMD "subscriber "
203#define SUBSCR_CMD_HELP "Subscriber management commands\n"
204
Oliver Smith02078b72019-01-11 15:41:29 +0100205#define SUBSCR_ID "(imsi|msisdn|id|imei) IDENT"
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200206#define SUBSCR_ID_HELP \
207 "Identify subscriber by IMSI\n" \
208 "Identify subscriber by MSISDN (phone number)\n" \
209 "Identify subscriber by database ID\n" \
Oliver Smith02078b72019-01-11 15:41:29 +0100210 "Identify subscriber by IMEI\n" \
211 "IMSI/MSISDN/ID/IMEI of the subscriber\n"
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200212
Neels Hofmeyr8aa780b2018-12-02 18:52:49 +0100213#define SUBSCR SUBSCR_CMD SUBSCR_ID " "
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200214#define SUBSCR_HELP SUBSCR_CMD_HELP SUBSCR_ID_HELP
215
216#define SUBSCR_UPDATE SUBSCR "update "
217#define SUBSCR_UPDATE_HELP SUBSCR_HELP "Set or update subscriber data\n"
Neels Hofmeyra820ea12018-12-02 19:46:46 +0100218#define SUBSCR_MSISDN_HELP "Set MSISDN (phone number) of the subscriber\n"
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200219
220DEFUN(subscriber_show,
221 subscriber_show_cmd,
222 SUBSCR "show",
223 SUBSCR_HELP "Show subscriber information\n")
224{
225 struct hlr_subscriber subscr;
226 const char *id_type = argv[0];
227 const char *id = argv[1];
228
229 if (get_subscr_by_argv(vty, id_type, id, &subscr))
230 return CMD_WARNING;
231
232 subscr_dump_full_vty(vty, &subscr);
233 return CMD_SUCCESS;
234}
235
Neels Hofmeyr8aa780b2018-12-02 18:52:49 +0100236ALIAS(subscriber_show, show_subscriber_cmd,
237 "show " SUBSCR_CMD SUBSCR_ID,
238 SHOW_STR SUBSCR_CMD_HELP SUBSCR_ID_HELP);
239
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200240DEFUN(subscriber_create,
241 subscriber_create_cmd,
242 SUBSCR_CMD "imsi IDENT create",
243 SUBSCR_CMD_HELP
Vadim Yanitskiyf473c7b2018-07-30 14:29:39 +0700244 "Identify subscriber by IMSI\n"
245 "IMSI/MSISDN/ID of the subscriber\n"
246 "Create subscriber by IMSI\n")
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200247{
248 int rc;
249 struct hlr_subscriber subscr;
250 const char *imsi = argv[0];
251
252 if (!osmo_imsi_str_valid(imsi)) {
253 vty_out(vty, "%% Not a valid IMSI: %s%s", imsi, VTY_NEWLINE);
254 return CMD_WARNING;
255 }
256
Oliver Smithcd2af5e2019-03-06 13:17:39 +0100257 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 +0200258
259 if (rc) {
260 if (rc == -EEXIST)
261 vty_out(vty, "%% Subscriber already exists for IMSI = %s%s",
262 imsi, VTY_NEWLINE);
263 else
264 vty_out(vty, "%% Error (rc=%d): cannot create subscriber for IMSI = %s%s",
265 rc, imsi, VTY_NEWLINE);
266 return CMD_WARNING;
267 }
268
269 rc = db_subscr_get_by_imsi(g_hlr->dbc, imsi, &subscr);
270 vty_out(vty, "%% Created subscriber %s%s", imsi, VTY_NEWLINE);
271
272 subscr_dump_full_vty(vty, &subscr);
273
274 return CMD_SUCCESS;
275}
276
277DEFUN(subscriber_delete,
278 subscriber_delete_cmd,
279 SUBSCR "delete",
280 SUBSCR_HELP "Delete subscriber from database\n")
281{
282 struct hlr_subscriber subscr;
283 int rc;
284 const char *id_type = argv[0];
285 const char *id = argv[1];
286
287 /* Find out the IMSI regardless of which way the caller decided to
288 * identify the subscriber by. */
289 if (get_subscr_by_argv(vty, id_type, id, &subscr))
290 return CMD_WARNING;
291
292 rc = db_subscr_delete_by_id(g_hlr->dbc, subscr.id);
293 if (rc) {
294 vty_out(vty, "%% Error: Failed to remove subscriber for IMSI '%s'%s",
295 subscr.imsi, VTY_NEWLINE);
296 return CMD_WARNING;
297 }
298
299 vty_out(vty, "%% Deleted subscriber for IMSI '%s'%s", subscr.imsi, VTY_NEWLINE);
300 return CMD_SUCCESS;
301}
302
303DEFUN(subscriber_msisdn,
304 subscriber_msisdn_cmd,
Neels Hofmeyra820ea12018-12-02 19:46:46 +0100305 SUBSCR_UPDATE "msisdn (none|MSISDN)",
306 SUBSCR_UPDATE_HELP SUBSCR_MSISDN_HELP
307 "Remove MSISDN (phone number)\n"
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200308 "New MSISDN (phone number)\n")
309{
310 struct hlr_subscriber subscr;
311 const char *id_type = argv[0];
312 const char *id = argv[1];
313 const char *msisdn = argv[2];
314
Neels Hofmeyra820ea12018-12-02 19:46:46 +0100315 if (strcmp(msisdn, "none") == 0)
316 msisdn = NULL;
317 else {
318 if (strlen(msisdn) > sizeof(subscr.msisdn) - 1) {
319 vty_out(vty, "%% MSISDN is too long, max. %zu characters are allowed%s",
320 sizeof(subscr.msisdn)-1, VTY_NEWLINE);
321 return CMD_WARNING;
322 }
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200323
Neels Hofmeyra820ea12018-12-02 19:46:46 +0100324 if (!osmo_msisdn_str_valid(msisdn)) {
325 vty_out(vty, "%% MSISDN invalid: '%s'%s", msisdn, VTY_NEWLINE);
326 return CMD_WARNING;
327 }
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200328 }
329
330 if (get_subscr_by_argv(vty, id_type, id, &subscr))
331 return CMD_WARNING;
332
333 if (db_subscr_update_msisdn_by_imsi(g_hlr->dbc, subscr.imsi, msisdn)) {
334 vty_out(vty, "%% Error: cannot update MSISDN for subscriber IMSI='%s'%s",
335 subscr.imsi, VTY_NEWLINE);
336 return CMD_WARNING;
337 }
338
Neels Hofmeyra820ea12018-12-02 19:46:46 +0100339 if (msisdn) {
340 vty_out(vty, "%% Updated subscriber IMSI='%s' to MSISDN='%s'%s",
341 subscr.imsi, msisdn, VTY_NEWLINE);
Stefan Sperlingf1622522018-04-09 11:39:16 +0200342
Neels Hofmeyra820ea12018-12-02 19:46:46 +0100343 if (db_subscr_get_by_msisdn(g_hlr->dbc, msisdn, &subscr) == 0)
344 osmo_hlr_subscriber_update_notify(&subscr);
345 } else {
346 vty_out(vty, "%% Updated subscriber IMSI='%s': removed MSISDN%s",
347 subscr.imsi, VTY_NEWLINE);
348
Stefan Sperlingf1622522018-04-09 11:39:16 +0200349 osmo_hlr_subscriber_update_notify(&subscr);
Neels Hofmeyra820ea12018-12-02 19:46:46 +0100350 }
Stefan Sperlingf1622522018-04-09 11:39:16 +0200351
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200352 return CMD_SUCCESS;
353}
354
355static bool is_hexkey_valid(struct vty *vty, const char *label,
356 const char *hex_str, int minlen, int maxlen)
357{
358 if (osmo_is_hexstr(hex_str, minlen * 2, maxlen * 2, true))
359 return true;
360 vty_out(vty, "%% Invalid value for %s: '%s'%s", label, hex_str, VTY_NEWLINE);
361 return false;
362}
363
364#define AUTH_ALG_TYPES_2G "(comp128v1|comp128v2|comp128v3|xor)"
365#define AUTH_ALG_TYPES_2G_HELP \
366 "Use COMP128v1 algorithm\n" \
367 "Use COMP128v2 algorithm\n" \
368 "Use COMP128v3 algorithm\n" \
369 "Use XOR algorithm\n"
370
371#define AUTH_ALG_TYPES_3G "milenage"
372#define AUTH_ALG_TYPES_3G_HELP \
373 "Use Milenage algorithm\n"
374
375#define A38_XOR_MIN_KEY_LEN 12
376#define A38_XOR_MAX_KEY_LEN 16
377#define A38_COMP128_KEY_LEN 16
378
379#define MILENAGE_KEY_LEN 16
380
381static bool auth_algo_parse(const char *alg_str, enum osmo_auth_algo *algo,
382 int *minlen, int *maxlen)
383{
384 if (!strcasecmp(alg_str, "none")) {
385 *algo = OSMO_AUTH_ALG_NONE;
386 *minlen = *maxlen = 0;
387 } else if (!strcasecmp(alg_str, "comp128v1")) {
388 *algo = OSMO_AUTH_ALG_COMP128v1;
389 *minlen = *maxlen = A38_COMP128_KEY_LEN;
390 } else if (!strcasecmp(alg_str, "comp128v2")) {
391 *algo = OSMO_AUTH_ALG_COMP128v2;
392 *minlen = *maxlen = A38_COMP128_KEY_LEN;
393 } else if (!strcasecmp(alg_str, "comp128v3")) {
394 *algo = OSMO_AUTH_ALG_COMP128v3;
395 *minlen = *maxlen = A38_COMP128_KEY_LEN;
396 } else if (!strcasecmp(alg_str, "xor")) {
397 *algo = OSMO_AUTH_ALG_XOR;
398 *minlen = A38_XOR_MIN_KEY_LEN;
399 *maxlen = A38_XOR_MAX_KEY_LEN;
400 } else if (!strcasecmp(alg_str, "milenage")) {
401 *algo = OSMO_AUTH_ALG_MILENAGE;
402 *minlen = *maxlen = MILENAGE_KEY_LEN;
403 } else
404 return false;
405 return true;
406}
407
408DEFUN(subscriber_no_aud2g,
409 subscriber_no_aud2g_cmd,
410 SUBSCR_UPDATE "aud2g none",
411 SUBSCR_UPDATE_HELP
412 "Set 2G authentication data\n"
413 "Delete 2G authentication data\n")
414{
415 struct hlr_subscriber subscr;
416 int rc;
417 const char *id_type = argv[0];
418 const char *id = argv[1];
419 struct sub_auth_data_str aud = {
420 .type = OSMO_AUTH_TYPE_GSM,
421 .algo = OSMO_AUTH_ALG_NONE,
422 };
423
424 if (get_subscr_by_argv(vty, id_type, id, &subscr))
425 return CMD_WARNING;
426
427 rc = db_subscr_update_aud_by_id(g_hlr->dbc, subscr.id, &aud);
428
Harald Welte880a34d2018-03-01 21:32:01 +0100429 if (rc && rc != -ENOENT) {
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200430 vty_out(vty, "%% Error: cannot disable 2G auth data for IMSI='%s'%s",
431 subscr.imsi, VTY_NEWLINE);
432 return CMD_WARNING;
433 }
434 return CMD_SUCCESS;
435}
436
437DEFUN(subscriber_aud2g,
438 subscriber_aud2g_cmd,
439 SUBSCR_UPDATE "aud2g " AUTH_ALG_TYPES_2G " ki KI",
440 SUBSCR_UPDATE_HELP
441 "Set 2G authentication data\n"
442 AUTH_ALG_TYPES_2G_HELP
443 "Set Ki Encryption Key\n" "Ki as 32 hexadecimal characters\n")
444{
445 struct hlr_subscriber subscr;
446 int rc;
447 int minlen = 0;
448 int maxlen = 0;
449 const char *id_type = argv[0];
450 const char *id = argv[1];
451 const char *alg_type = argv[2];
452 const char *ki = argv[3];
453 struct sub_auth_data_str aud2g = {
454 .type = OSMO_AUTH_TYPE_GSM,
455 .u.gsm.ki = ki,
456 };
457
458 if (!auth_algo_parse(alg_type, &aud2g.algo, &minlen, &maxlen)) {
459 vty_out(vty, "%% Unknown auth algorithm: '%s'%s", alg_type, VTY_NEWLINE);
460 return CMD_WARNING;
461 }
462
463 if (!is_hexkey_valid(vty, "KI", aud2g.u.gsm.ki, minlen, maxlen))
464 return CMD_WARNING;
465
466 if (get_subscr_by_argv(vty, id_type, id, &subscr))
467 return CMD_WARNING;
468
469 rc = db_subscr_update_aud_by_id(g_hlr->dbc, subscr.id, &aud2g);
470
471 if (rc) {
472 vty_out(vty, "%% Error: cannot set 2G auth data for IMSI='%s'%s",
473 subscr.imsi, VTY_NEWLINE);
474 return CMD_WARNING;
475 }
476 return CMD_SUCCESS;
477}
478
479DEFUN(subscriber_no_aud3g,
480 subscriber_no_aud3g_cmd,
481 SUBSCR_UPDATE "aud3g none",
482 SUBSCR_UPDATE_HELP
483 "Set UMTS authentication data (3G, and 2G with UMTS AKA)\n"
484 "Delete 3G authentication data\n")
485{
486 struct hlr_subscriber subscr;
487 int rc;
488 const char *id_type = argv[0];
489 const char *id = argv[1];
490 struct sub_auth_data_str aud = {
491 .type = OSMO_AUTH_TYPE_UMTS,
492 .algo = OSMO_AUTH_ALG_NONE,
493 };
494
495 if (get_subscr_by_argv(vty, id_type, id, &subscr))
496 return CMD_WARNING;
497
498 rc = db_subscr_update_aud_by_id(g_hlr->dbc, subscr.id, &aud);
499
Harald Welte880a34d2018-03-01 21:32:01 +0100500 if (rc && rc != -ENOENT) {
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200501 vty_out(vty, "%% Error: cannot disable 3G auth data for IMSI='%s'%s",
502 subscr.imsi, VTY_NEWLINE);
503 return CMD_WARNING;
504 }
505 return CMD_SUCCESS;
506}
507
508DEFUN(subscriber_aud3g,
509 subscriber_aud3g_cmd,
510 SUBSCR_UPDATE "aud3g " AUTH_ALG_TYPES_3G
511 " k K"
512 " (op|opc) OP_C"
513 " [ind-bitlen] [<0-28>]",
514 SUBSCR_UPDATE_HELP
515 "Set UMTS authentication data (3G, and 2G with UMTS AKA)\n"
516 AUTH_ALG_TYPES_3G_HELP
517 "Set Encryption Key K\n" "K as 32 hexadecimal characters\n"
518 "Set OP key\n" "Set OPC key\n" "OP or OPC as 32 hexadecimal characters\n"
519 "Set IND bit length\n" "IND bit length value (default: 5)\n")
520{
521 struct hlr_subscriber subscr;
522 int minlen = 0;
523 int maxlen = 0;
524 int rc;
525 const char *id_type = argv[0];
526 const char *id = argv[1];
527 const char *alg_type = AUTH_ALG_TYPES_3G;
528 const char *k = argv[2];
529 bool opc_is_op = (strcasecmp("op", argv[3]) == 0);
530 const char *op_opc = argv[4];
531 int ind_bitlen = argc > 6? atoi(argv[6]) : 5;
532 struct sub_auth_data_str aud3g = {
533 .type = OSMO_AUTH_TYPE_UMTS,
534 .u.umts = {
535 .k = k,
536 .opc_is_op = opc_is_op,
537 .opc = op_opc,
538 .ind_bitlen = ind_bitlen,
539 },
540 };
541
542 if (!auth_algo_parse(alg_type, &aud3g.algo, &minlen, &maxlen)) {
543 vty_out(vty, "%% Unknown auth algorithm: '%s'%s", alg_type, VTY_NEWLINE);
544 return CMD_WARNING;
545 }
546
547 if (!is_hexkey_valid(vty, "K", aud3g.u.umts.k, minlen, maxlen))
548 return CMD_WARNING;
549
550 if (!is_hexkey_valid(vty, opc_is_op ? "OP" : "OPC", aud3g.u.umts.opc,
551 MILENAGE_KEY_LEN, MILENAGE_KEY_LEN))
552 return CMD_WARNING;
553
554 if (get_subscr_by_argv(vty, id_type, id, &subscr))
555 return CMD_WARNING;
556
557 rc = db_subscr_update_aud_by_id(g_hlr->dbc, subscr.id, &aud3g);
558
559 if (rc) {
560 vty_out(vty, "%% Error: cannot set 3G auth data for IMSI='%s'%s",
561 subscr.imsi, VTY_NEWLINE);
562 return CMD_WARNING;
563 }
564 return CMD_SUCCESS;
565}
566
Oliver Smith02078b72019-01-11 15:41:29 +0100567DEFUN(subscriber_imei,
568 subscriber_imei_cmd,
569 SUBSCR_UPDATE "imei (none|IMEI)",
570 SUBSCR_UPDATE_HELP
571 "Set IMEI of the subscriber (normally populated from MSC, no need to set this manually)\n"
572 "Forget IMEI\n"
573 "Set IMEI (use for debug only!)\n")
574{
575 struct hlr_subscriber subscr;
576 const char *id_type = argv[0];
577 const char *id = argv[1];
578 const char *imei = argv[2];
579 char imei_buf[GSM23003_IMEI_NUM_DIGITS_NO_CHK+1];
580
581 if (strcmp(imei, "none") == 0)
582 imei = NULL;
583 else {
584 /* Verify IMEI with checksum digit */
585 if (osmo_imei_str_valid(imei, true)) {
586 /* Cut the checksum off */
587 osmo_strlcpy(imei_buf, imei, sizeof(imei_buf));
588 imei = imei_buf;
589 } else if (!osmo_imei_str_valid(imei, false)) {
590 vty_out(vty, "%% IMEI invalid: '%s'%s", imei, VTY_NEWLINE);
591 return CMD_WARNING;
592 }
593 }
594
595 if (get_subscr_by_argv(vty, id_type, id, &subscr))
596 return CMD_WARNING;
597
598 if (db_subscr_update_imei_by_imsi(g_hlr->dbc, subscr.imsi, imei)) {
599 vty_out(vty, "%% Error: cannot update IMEI for subscriber IMSI='%s'%s",
600 subscr.imsi, VTY_NEWLINE);
601 return CMD_WARNING;
602 }
603
604 if (imei)
605 vty_out(vty, "%% Updated subscriber IMSI='%s' to IMEI='%s'%s",
606 subscr.imsi, imei, VTY_NEWLINE);
607 else
608 vty_out(vty, "%% Updated subscriber IMSI='%s': removed IMEI%s",
609 subscr.imsi, VTY_NEWLINE);
610
611 return CMD_SUCCESS;
612}
613
Oliver Smith3b33b012019-07-15 10:35:22 +0200614DEFUN(subscriber_nam,
615 subscriber_nam_cmd,
616 SUBSCR_UPDATE "network-access-mode (none|cs|ps|cs+ps)",
617 SUBSCR_UPDATE_HELP
618 "Set Network Access Mode (NAM) of the subscriber\n"
619 "Do not allow access to circuit switched or packet switched services\n"
620 "Allow access to circuit switched services only\n"
621 "Allow access to packet switched services only\n"
622 "Allow access to both circuit and packet switched services\n")
623{
624 struct hlr_subscriber subscr;
625 const char *id_type = argv[0];
626 const char *id = argv[1];
627 bool nam_cs = strstr(argv[2], "cs");
628 bool nam_ps = strstr(argv[2], "ps");
629
630 if (get_subscr_by_argv(vty, id_type, id, &subscr))
631 return CMD_WARNING;
632
633 if (nam_cs != subscr.nam_cs)
634 hlr_subscr_nam(g_hlr, &subscr, nam_cs, 0);
635 if (nam_ps != subscr.nam_ps)
636 hlr_subscr_nam(g_hlr, &subscr, nam_ps, 1);
637
638 return CMD_SUCCESS;
639}
640
Oliver Smith02078b72019-01-11 15:41:29 +0100641
Neels Hofmeyrce172ef2018-12-26 01:49:53 +0100642DEFUN(subscriber_rat,
643 subscriber_rat_cmd,
Piotr Krysika5ed6632019-08-22 11:18:15 +0200644 SUBSCR_UPDATE "rat (geran-a|utran-iu|eutran-sgs) (allowed|forbidden)",
Neels Hofmeyrce172ef2018-12-26 01:49:53 +0100645 SUBSCR_UPDATE_HELP
646 "Allow or forbid specific Radio Access Types\n"
647 "Set access to GERAN-A\n"
648 "Set access to UTRAN-Iu\n"
Piotr Krysika5ed6632019-08-22 11:18:15 +0200649 "Set access to EUTRAN-SGs\n"
Neels Hofmeyrce172ef2018-12-26 01:49:53 +0100650 "Allow access\n"
651 "Forbid access\n")
652{
653 struct hlr_subscriber subscr;
654 const char *id_type = argv[0];
655 const char *id = argv[1];
656 const char *rat_str = argv[2];
657 const char *allowed_forbidden = argv[3];
Harald Welte15031852019-08-21 20:01:31 +0200658 enum osmo_rat_type rat = OSMO_RAT_UNKNOWN;
Neels Hofmeyrce172ef2018-12-26 01:49:53 +0100659 bool allowed;
660 int rc;
661
662 if (strcmp(rat_str, "geran-a") == 0)
663 rat = OSMO_RAT_GERAN_A;
664 else if (strcmp(rat_str, "utran-iu") == 0)
665 rat = OSMO_RAT_UTRAN_IU;
Piotr Krysika5ed6632019-08-22 11:18:15 +0200666 else if (strcmp(rat_str, "eutran-sgs") == 0)
Harald Welte15031852019-08-21 20:01:31 +0200667 rat = OSMO_RAT_EUTRAN_SGS;
Neels Hofmeyrce172ef2018-12-26 01:49:53 +0100668
669 allowed = (strcmp(allowed_forbidden, "allowed") == 0);
670
671 if (get_subscr_by_argv(vty, id_type, id, &subscr))
672 return CMD_WARNING;
673
674 rc = hlr_subscr_rat_flag(g_hlr, &subscr, rat, allowed);
675
676 if (rc && rc != -ENOEXEC) {
677 vty_out(vty, "%% Error: cannot set %s to %s%s",
678 osmo_rat_type_name(rat), allowed ? "allowed" : "forbidden", VTY_NEWLINE);
679 return CMD_WARNING;
680 }
681 return CMD_SUCCESS;
682}
683
Harald Welted5807b82018-07-29 12:27:41 +0200684void hlr_vty_subscriber_init(void)
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200685{
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200686 install_element_ve(&subscriber_show_cmd);
Neels Hofmeyr8aa780b2018-12-02 18:52:49 +0100687 install_element_ve(&show_subscriber_cmd);
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200688 install_element(ENABLE_NODE, &subscriber_create_cmd);
689 install_element(ENABLE_NODE, &subscriber_delete_cmd);
690 install_element(ENABLE_NODE, &subscriber_msisdn_cmd);
691 install_element(ENABLE_NODE, &subscriber_no_aud2g_cmd);
692 install_element(ENABLE_NODE, &subscriber_aud2g_cmd);
693 install_element(ENABLE_NODE, &subscriber_no_aud3g_cmd);
694 install_element(ENABLE_NODE, &subscriber_aud3g_cmd);
Oliver Smith02078b72019-01-11 15:41:29 +0100695 install_element(ENABLE_NODE, &subscriber_imei_cmd);
Oliver Smith3b33b012019-07-15 10:35:22 +0200696 install_element(ENABLE_NODE, &subscriber_nam_cmd);
Neels Hofmeyrce172ef2018-12-26 01:49:53 +0100697 install_element(ENABLE_NODE, &subscriber_rat_cmd);
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200698}