blob: dfbf72f205372a66b1e14636f5395d67a87b639f [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>
30
Neels Hofmeyr2f758032019-11-20 00:37:07 +010031#include <osmocom/hlr/hlr.h>
32#include <osmocom/hlr/db.h>
Oliver Smithf6d457e2019-11-25 13:28:23 +010033#include <osmocom/hlr/timestamp.h>
Neels Hofmeyr183e7002017-10-06 02:59:54 +020034
35struct vty;
36
37#define hexdump_buf(buf) osmo_hexdump_nospc((void*)buf, sizeof(buf))
38
Neels Hofmeyrcb88c342018-12-25 17:23:41 +010039static char *get_datestr(const time_t *t, char *buf, size_t bufsize)
Stefan Sperling5c14c9c2018-12-07 12:30:21 +010040{
Neels Hofmeyrcb88c342018-12-25 17:23:41 +010041 struct tm *tm;
Stefan Sperling5c14c9c2018-12-07 12:30:21 +010042
Neels Hofmeyrcb88c342018-12-25 17:23:41 +010043 tm = gmtime(t);
44 if (!tm)
45 return "UNKNOWN";
46
47 strftime(buf, bufsize, "%FT%T+00:00", tm);
48 return buf;
Stefan Sperling5c14c9c2018-12-07 12:30:21 +010049}
50
Neels Hofmeyr07e16022019-11-20 02:36:35 +010051static void dump_last_lu_seen(struct vty *vty, const char *domain_label, time_t last_lu_seen)
52{
Oliver Smithf6d457e2019-11-25 13:28:23 +010053 uint32_t age;
Neels Hofmeyrcb88c342018-12-25 17:23:41 +010054 char datebuf[32];
Neels Hofmeyr07e16022019-11-20 02:36:35 +010055 if (!last_lu_seen)
56 return;
Neels Hofmeyrcb88c342018-12-25 17:23:41 +010057 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 +010058 if (!timestamp_age(&last_lu_seen, &age))
59 vty_out(vty, " (invalid timestamp)%s", VTY_NEWLINE);
Neels Hofmeyr011e7812019-11-27 16:47:50 +010060 else {
61 vty_out(vty, " (");
62#define UNIT_AGO(UNITNAME, UNITVAL) \
63 if (age >= (UNITVAL)) { \
64 vty_out(vty, "%u%s", age / (UNITVAL), UNITNAME); \
65 age = age % (UNITVAL); \
66 }
67 UNIT_AGO("d", 60*60*24);
68 UNIT_AGO("h", 60*60);
69 UNIT_AGO("m", 60);
70 UNIT_AGO("s", 1);
71 vty_out(vty, " ago)%s", VTY_NEWLINE);
72#undef UNIT_AGO
73 }
Neels Hofmeyr07e16022019-11-20 02:36:35 +010074}
75
Neels Hofmeyr183e7002017-10-06 02:59:54 +020076static void subscr_dump_full_vty(struct vty *vty, struct hlr_subscriber *subscr)
77{
78 int rc;
79 struct osmo_sub_auth_data aud2g;
80 struct osmo_sub_auth_data aud3g;
81
82 vty_out(vty, " ID: %"PRIu64"%s", subscr->id, VTY_NEWLINE);
83
Neels Hofmeyr36bec872017-10-23 18:44:23 +020084 vty_out(vty, " IMSI: %s%s", *subscr->imsi ? subscr->imsi : "none", VTY_NEWLINE);
Neels Hofmeyr183e7002017-10-06 02:59:54 +020085 vty_out(vty, " MSISDN: %s%s", *subscr->msisdn ? subscr->msisdn : "none", VTY_NEWLINE);
Oliver Smith02078b72019-01-11 15:41:29 +010086
87 if (*subscr->imei) {
88 char checksum = osmo_luhn(subscr->imei, 14);
89 if (checksum == -EINVAL)
90 vty_out(vty, " IMEI: %s (INVALID LENGTH!)%s", subscr->imei, VTY_NEWLINE);
91 else
92 vty_out(vty, " IMEI: %s%c%s", subscr->imei, checksum, VTY_NEWLINE);
93 }
94
Neels Hofmeyr183e7002017-10-06 02:59:54 +020095 if (*subscr->vlr_number)
96 vty_out(vty, " VLR number: %s%s", subscr->vlr_number, VTY_NEWLINE);
97 if (*subscr->sgsn_number)
98 vty_out(vty, " SGSN number: %s%s", subscr->sgsn_number, VTY_NEWLINE);
99 if (*subscr->sgsn_address)
100 vty_out(vty, " SGSN address: %s%s", subscr->sgsn_address, VTY_NEWLINE);
101 if (subscr->periodic_lu_timer)
102 vty_out(vty, " Periodic LU timer: %u%s", subscr->periodic_lu_timer, VTY_NEWLINE);
103 if (subscr->periodic_rau_tau_timer)
104 vty_out(vty, " Periodic RAU/TAU timer: %u%s", subscr->periodic_rau_tau_timer, VTY_NEWLINE);
105 if (subscr->lmsi)
106 vty_out(vty, " LMSI: %x%s", subscr->lmsi, VTY_NEWLINE);
107 if (!subscr->nam_cs)
108 vty_out(vty, " CS disabled%s", VTY_NEWLINE);
109 if (subscr->ms_purged_cs)
110 vty_out(vty, " CS purged%s", VTY_NEWLINE);
111 if (!subscr->nam_ps)
112 vty_out(vty, " PS disabled%s", VTY_NEWLINE);
113 if (subscr->ms_purged_ps)
114 vty_out(vty, " PS purged%s", VTY_NEWLINE);
Neels Hofmeyr07e16022019-11-20 02:36:35 +0100115 dump_last_lu_seen(vty, "CS", subscr->last_lu_seen);
116 dump_last_lu_seen(vty, "PS", subscr->last_lu_seen_ps);
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200117
118 if (!*subscr->imsi)
119 return;
120
121 OSMO_ASSERT(g_hlr);
122 rc = db_get_auth_data(g_hlr->dbc, subscr->imsi, &aud2g, &aud3g, NULL);
123
Neels Hofmeyrbd1dca02017-11-23 15:25:30 +0100124 switch (rc) {
125 case 0:
126 break;
127 case -ENOENT:
128 case -ENOKEY:
129 aud2g.algo = OSMO_AUTH_ALG_NONE;
130 aud3g.algo = OSMO_AUTH_ALG_NONE;
131 break;
132 default:
133 vty_out(vty, "%% Error retrieving data from database (%d)%s", rc, VTY_NEWLINE);
134 return;
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200135 }
136
137 if (aud2g.type != OSMO_AUTH_TYPE_NONE && aud2g.type != OSMO_AUTH_TYPE_GSM) {
138 vty_out(vty, "%% Error: 2G auth data is not of type 'GSM'%s", VTY_NEWLINE);
139 aud2g = (struct osmo_sub_auth_data){};
140 }
141
142 if (aud3g.type != OSMO_AUTH_TYPE_NONE && aud3g.type != OSMO_AUTH_TYPE_UMTS) {
143 vty_out(vty, "%% Error: 3G auth data is not of type 'UMTS'%s", VTY_NEWLINE);
144 aud3g = (struct osmo_sub_auth_data){};
145 }
146
147 if (aud2g.algo != OSMO_AUTH_ALG_NONE && aud2g.type != OSMO_AUTH_TYPE_NONE) {
148 vty_out(vty, " 2G auth: %s%s",
149 osmo_auth_alg_name(aud2g.algo), VTY_NEWLINE);
150 vty_out(vty, " KI=%s%s",
151 hexdump_buf(aud2g.u.gsm.ki), VTY_NEWLINE);
152 }
153
154 if (aud3g.algo != OSMO_AUTH_ALG_NONE && aud3g.type != OSMO_AUTH_TYPE_NONE) {
155 vty_out(vty, " 3G auth: %s%s", osmo_auth_alg_name(aud3g.algo), VTY_NEWLINE);
156 vty_out(vty, " K=%s%s", hexdump_buf(aud3g.u.umts.k), VTY_NEWLINE);
157 vty_out(vty, " %s=%s%s", aud3g.u.umts.opc_is_op? "OP" : "OPC",
158 hexdump_buf(aud3g.u.umts.opc), VTY_NEWLINE);
159 vty_out(vty, " IND-bitlen=%u", aud3g.u.umts.ind_bitlen);
160 if (aud3g.u.umts.sqn)
161 vty_out(vty, " last-SQN=%"PRIu64, aud3g.u.umts.sqn);
162 vty_out(vty, VTY_NEWLINE);
163 }
164}
165
166static int get_subscr_by_argv(struct vty *vty, const char *type, const char *id, struct hlr_subscriber *subscr)
167{
Oliver Smith02078b72019-01-11 15:41:29 +0100168 char imei_buf[GSM23003_IMEI_NUM_DIGITS_NO_CHK+1];
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200169 int rc = -1;
170 if (strcmp(type, "imsi") == 0)
171 rc = db_subscr_get_by_imsi(g_hlr->dbc, id, subscr);
172 else if (strcmp(type, "msisdn") == 0)
173 rc = db_subscr_get_by_msisdn(g_hlr->dbc, id, subscr);
174 else if (strcmp(type, "id") == 0)
175 rc = db_subscr_get_by_id(g_hlr->dbc, atoll(id), subscr);
Oliver Smith02078b72019-01-11 15:41:29 +0100176 else if (strcmp(type, "imei") == 0) {
177 /* Verify IMEI with checksum digit */
178 if (osmo_imei_str_valid(id, true)) {
179 /* Cut the checksum off */
180 osmo_strlcpy(imei_buf, id, sizeof(imei_buf));
181 id = imei_buf;
182 vty_out(vty, "%% Checksum validated and stripped for search: imei = '%s'%s", id,
183 VTY_NEWLINE);
184 }
185 rc = db_subscr_get_by_imei(g_hlr->dbc, id, subscr);
186 }
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200187 if (rc)
188 vty_out(vty, "%% No subscriber for %s = '%s'%s",
189 type, id, VTY_NEWLINE);
190 return rc;
191}
192
193#define SUBSCR_CMD "subscriber "
194#define SUBSCR_CMD_HELP "Subscriber management commands\n"
195
Oliver Smith02078b72019-01-11 15:41:29 +0100196#define SUBSCR_ID "(imsi|msisdn|id|imei) IDENT"
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200197#define SUBSCR_ID_HELP \
198 "Identify subscriber by IMSI\n" \
199 "Identify subscriber by MSISDN (phone number)\n" \
200 "Identify subscriber by database ID\n" \
Oliver Smith02078b72019-01-11 15:41:29 +0100201 "Identify subscriber by IMEI\n" \
202 "IMSI/MSISDN/ID/IMEI of the subscriber\n"
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200203
Neels Hofmeyr8aa780b2018-12-02 18:52:49 +0100204#define SUBSCR SUBSCR_CMD SUBSCR_ID " "
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200205#define SUBSCR_HELP SUBSCR_CMD_HELP SUBSCR_ID_HELP
206
207#define SUBSCR_UPDATE SUBSCR "update "
208#define SUBSCR_UPDATE_HELP SUBSCR_HELP "Set or update subscriber data\n"
Neels Hofmeyra820ea12018-12-02 19:46:46 +0100209#define SUBSCR_MSISDN_HELP "Set MSISDN (phone number) of the subscriber\n"
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200210
211DEFUN(subscriber_show,
212 subscriber_show_cmd,
213 SUBSCR "show",
214 SUBSCR_HELP "Show subscriber information\n")
215{
216 struct hlr_subscriber subscr;
217 const char *id_type = argv[0];
218 const char *id = argv[1];
219
220 if (get_subscr_by_argv(vty, id_type, id, &subscr))
221 return CMD_WARNING;
222
223 subscr_dump_full_vty(vty, &subscr);
224 return CMD_SUCCESS;
225}
226
Neels Hofmeyr8aa780b2018-12-02 18:52:49 +0100227ALIAS(subscriber_show, show_subscriber_cmd,
228 "show " SUBSCR_CMD SUBSCR_ID,
229 SHOW_STR SUBSCR_CMD_HELP SUBSCR_ID_HELP);
230
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200231DEFUN(subscriber_create,
232 subscriber_create_cmd,
233 SUBSCR_CMD "imsi IDENT create",
234 SUBSCR_CMD_HELP
Vadim Yanitskiyf473c7b2018-07-30 14:29:39 +0700235 "Identify subscriber by IMSI\n"
236 "IMSI/MSISDN/ID of the subscriber\n"
237 "Create subscriber by IMSI\n")
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200238{
239 int rc;
240 struct hlr_subscriber subscr;
241 const char *imsi = argv[0];
242
243 if (!osmo_imsi_str_valid(imsi)) {
244 vty_out(vty, "%% Not a valid IMSI: %s%s", imsi, VTY_NEWLINE);
245 return CMD_WARNING;
246 }
247
Oliver Smithcd2af5e2019-03-06 13:17:39 +0100248 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 +0200249
250 if (rc) {
251 if (rc == -EEXIST)
252 vty_out(vty, "%% Subscriber already exists for IMSI = %s%s",
253 imsi, VTY_NEWLINE);
254 else
255 vty_out(vty, "%% Error (rc=%d): cannot create subscriber for IMSI = %s%s",
256 rc, imsi, VTY_NEWLINE);
257 return CMD_WARNING;
258 }
259
260 rc = db_subscr_get_by_imsi(g_hlr->dbc, imsi, &subscr);
261 vty_out(vty, "%% Created subscriber %s%s", imsi, VTY_NEWLINE);
262
263 subscr_dump_full_vty(vty, &subscr);
264
265 return CMD_SUCCESS;
266}
267
268DEFUN(subscriber_delete,
269 subscriber_delete_cmd,
270 SUBSCR "delete",
271 SUBSCR_HELP "Delete subscriber from database\n")
272{
273 struct hlr_subscriber subscr;
274 int rc;
275 const char *id_type = argv[0];
276 const char *id = argv[1];
277
278 /* Find out the IMSI regardless of which way the caller decided to
279 * identify the subscriber by. */
280 if (get_subscr_by_argv(vty, id_type, id, &subscr))
281 return CMD_WARNING;
282
283 rc = db_subscr_delete_by_id(g_hlr->dbc, subscr.id);
284 if (rc) {
285 vty_out(vty, "%% Error: Failed to remove subscriber for IMSI '%s'%s",
286 subscr.imsi, VTY_NEWLINE);
287 return CMD_WARNING;
288 }
289
290 vty_out(vty, "%% Deleted subscriber for IMSI '%s'%s", subscr.imsi, VTY_NEWLINE);
291 return CMD_SUCCESS;
292}
293
294DEFUN(subscriber_msisdn,
295 subscriber_msisdn_cmd,
Neels Hofmeyra820ea12018-12-02 19:46:46 +0100296 SUBSCR_UPDATE "msisdn (none|MSISDN)",
297 SUBSCR_UPDATE_HELP SUBSCR_MSISDN_HELP
298 "Remove MSISDN (phone number)\n"
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200299 "New MSISDN (phone number)\n")
300{
301 struct hlr_subscriber subscr;
302 const char *id_type = argv[0];
303 const char *id = argv[1];
304 const char *msisdn = argv[2];
305
Neels Hofmeyra820ea12018-12-02 19:46:46 +0100306 if (strcmp(msisdn, "none") == 0)
307 msisdn = NULL;
308 else {
309 if (strlen(msisdn) > sizeof(subscr.msisdn) - 1) {
310 vty_out(vty, "%% MSISDN is too long, max. %zu characters are allowed%s",
311 sizeof(subscr.msisdn)-1, VTY_NEWLINE);
312 return CMD_WARNING;
313 }
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200314
Neels Hofmeyra820ea12018-12-02 19:46:46 +0100315 if (!osmo_msisdn_str_valid(msisdn)) {
316 vty_out(vty, "%% MSISDN invalid: '%s'%s", msisdn, VTY_NEWLINE);
317 return CMD_WARNING;
318 }
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200319 }
320
321 if (get_subscr_by_argv(vty, id_type, id, &subscr))
322 return CMD_WARNING;
323
324 if (db_subscr_update_msisdn_by_imsi(g_hlr->dbc, subscr.imsi, msisdn)) {
325 vty_out(vty, "%% Error: cannot update MSISDN for subscriber IMSI='%s'%s",
326 subscr.imsi, VTY_NEWLINE);
327 return CMD_WARNING;
328 }
329
Neels Hofmeyra820ea12018-12-02 19:46:46 +0100330 if (msisdn) {
331 vty_out(vty, "%% Updated subscriber IMSI='%s' to MSISDN='%s'%s",
332 subscr.imsi, msisdn, VTY_NEWLINE);
Stefan Sperlingf1622522018-04-09 11:39:16 +0200333
Neels Hofmeyra820ea12018-12-02 19:46:46 +0100334 if (db_subscr_get_by_msisdn(g_hlr->dbc, msisdn, &subscr) == 0)
335 osmo_hlr_subscriber_update_notify(&subscr);
336 } else {
337 vty_out(vty, "%% Updated subscriber IMSI='%s': removed MSISDN%s",
338 subscr.imsi, VTY_NEWLINE);
339
Stefan Sperlingf1622522018-04-09 11:39:16 +0200340 osmo_hlr_subscriber_update_notify(&subscr);
Neels Hofmeyra820ea12018-12-02 19:46:46 +0100341 }
Stefan Sperlingf1622522018-04-09 11:39:16 +0200342
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200343 return CMD_SUCCESS;
344}
345
346static bool is_hexkey_valid(struct vty *vty, const char *label,
347 const char *hex_str, int minlen, int maxlen)
348{
349 if (osmo_is_hexstr(hex_str, minlen * 2, maxlen * 2, true))
350 return true;
351 vty_out(vty, "%% Invalid value for %s: '%s'%s", label, hex_str, VTY_NEWLINE);
352 return false;
353}
354
355#define AUTH_ALG_TYPES_2G "(comp128v1|comp128v2|comp128v3|xor)"
356#define AUTH_ALG_TYPES_2G_HELP \
357 "Use COMP128v1 algorithm\n" \
358 "Use COMP128v2 algorithm\n" \
359 "Use COMP128v3 algorithm\n" \
360 "Use XOR algorithm\n"
361
362#define AUTH_ALG_TYPES_3G "milenage"
363#define AUTH_ALG_TYPES_3G_HELP \
364 "Use Milenage algorithm\n"
365
366#define A38_XOR_MIN_KEY_LEN 12
367#define A38_XOR_MAX_KEY_LEN 16
368#define A38_COMP128_KEY_LEN 16
369
370#define MILENAGE_KEY_LEN 16
371
372static bool auth_algo_parse(const char *alg_str, enum osmo_auth_algo *algo,
373 int *minlen, int *maxlen)
374{
375 if (!strcasecmp(alg_str, "none")) {
376 *algo = OSMO_AUTH_ALG_NONE;
377 *minlen = *maxlen = 0;
378 } else if (!strcasecmp(alg_str, "comp128v1")) {
379 *algo = OSMO_AUTH_ALG_COMP128v1;
380 *minlen = *maxlen = A38_COMP128_KEY_LEN;
381 } else if (!strcasecmp(alg_str, "comp128v2")) {
382 *algo = OSMO_AUTH_ALG_COMP128v2;
383 *minlen = *maxlen = A38_COMP128_KEY_LEN;
384 } else if (!strcasecmp(alg_str, "comp128v3")) {
385 *algo = OSMO_AUTH_ALG_COMP128v3;
386 *minlen = *maxlen = A38_COMP128_KEY_LEN;
387 } else if (!strcasecmp(alg_str, "xor")) {
388 *algo = OSMO_AUTH_ALG_XOR;
389 *minlen = A38_XOR_MIN_KEY_LEN;
390 *maxlen = A38_XOR_MAX_KEY_LEN;
391 } else if (!strcasecmp(alg_str, "milenage")) {
392 *algo = OSMO_AUTH_ALG_MILENAGE;
393 *minlen = *maxlen = MILENAGE_KEY_LEN;
394 } else
395 return false;
396 return true;
397}
398
399DEFUN(subscriber_no_aud2g,
400 subscriber_no_aud2g_cmd,
401 SUBSCR_UPDATE "aud2g none",
402 SUBSCR_UPDATE_HELP
403 "Set 2G authentication data\n"
404 "Delete 2G authentication data\n")
405{
406 struct hlr_subscriber subscr;
407 int rc;
408 const char *id_type = argv[0];
409 const char *id = argv[1];
410 struct sub_auth_data_str aud = {
411 .type = OSMO_AUTH_TYPE_GSM,
412 .algo = OSMO_AUTH_ALG_NONE,
413 };
414
415 if (get_subscr_by_argv(vty, id_type, id, &subscr))
416 return CMD_WARNING;
417
418 rc = db_subscr_update_aud_by_id(g_hlr->dbc, subscr.id, &aud);
419
Harald Welte880a34d2018-03-01 21:32:01 +0100420 if (rc && rc != -ENOENT) {
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200421 vty_out(vty, "%% Error: cannot disable 2G auth data for IMSI='%s'%s",
422 subscr.imsi, VTY_NEWLINE);
423 return CMD_WARNING;
424 }
425 return CMD_SUCCESS;
426}
427
428DEFUN(subscriber_aud2g,
429 subscriber_aud2g_cmd,
430 SUBSCR_UPDATE "aud2g " AUTH_ALG_TYPES_2G " ki KI",
431 SUBSCR_UPDATE_HELP
432 "Set 2G authentication data\n"
433 AUTH_ALG_TYPES_2G_HELP
434 "Set Ki Encryption Key\n" "Ki as 32 hexadecimal characters\n")
435{
436 struct hlr_subscriber subscr;
437 int rc;
438 int minlen = 0;
439 int maxlen = 0;
440 const char *id_type = argv[0];
441 const char *id = argv[1];
442 const char *alg_type = argv[2];
443 const char *ki = argv[3];
444 struct sub_auth_data_str aud2g = {
445 .type = OSMO_AUTH_TYPE_GSM,
446 .u.gsm.ki = ki,
447 };
448
449 if (!auth_algo_parse(alg_type, &aud2g.algo, &minlen, &maxlen)) {
450 vty_out(vty, "%% Unknown auth algorithm: '%s'%s", alg_type, VTY_NEWLINE);
451 return CMD_WARNING;
452 }
453
454 if (!is_hexkey_valid(vty, "KI", aud2g.u.gsm.ki, minlen, maxlen))
455 return CMD_WARNING;
456
457 if (get_subscr_by_argv(vty, id_type, id, &subscr))
458 return CMD_WARNING;
459
460 rc = db_subscr_update_aud_by_id(g_hlr->dbc, subscr.id, &aud2g);
461
462 if (rc) {
463 vty_out(vty, "%% Error: cannot set 2G auth data for IMSI='%s'%s",
464 subscr.imsi, VTY_NEWLINE);
465 return CMD_WARNING;
466 }
467 return CMD_SUCCESS;
468}
469
470DEFUN(subscriber_no_aud3g,
471 subscriber_no_aud3g_cmd,
472 SUBSCR_UPDATE "aud3g none",
473 SUBSCR_UPDATE_HELP
474 "Set UMTS authentication data (3G, and 2G with UMTS AKA)\n"
475 "Delete 3G authentication data\n")
476{
477 struct hlr_subscriber subscr;
478 int rc;
479 const char *id_type = argv[0];
480 const char *id = argv[1];
481 struct sub_auth_data_str aud = {
482 .type = OSMO_AUTH_TYPE_UMTS,
483 .algo = OSMO_AUTH_ALG_NONE,
484 };
485
486 if (get_subscr_by_argv(vty, id_type, id, &subscr))
487 return CMD_WARNING;
488
489 rc = db_subscr_update_aud_by_id(g_hlr->dbc, subscr.id, &aud);
490
Harald Welte880a34d2018-03-01 21:32:01 +0100491 if (rc && rc != -ENOENT) {
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200492 vty_out(vty, "%% Error: cannot disable 3G auth data for IMSI='%s'%s",
493 subscr.imsi, VTY_NEWLINE);
494 return CMD_WARNING;
495 }
496 return CMD_SUCCESS;
497}
498
499DEFUN(subscriber_aud3g,
500 subscriber_aud3g_cmd,
501 SUBSCR_UPDATE "aud3g " AUTH_ALG_TYPES_3G
502 " k K"
503 " (op|opc) OP_C"
504 " [ind-bitlen] [<0-28>]",
505 SUBSCR_UPDATE_HELP
506 "Set UMTS authentication data (3G, and 2G with UMTS AKA)\n"
507 AUTH_ALG_TYPES_3G_HELP
508 "Set Encryption Key K\n" "K as 32 hexadecimal characters\n"
509 "Set OP key\n" "Set OPC key\n" "OP or OPC as 32 hexadecimal characters\n"
510 "Set IND bit length\n" "IND bit length value (default: 5)\n")
511{
512 struct hlr_subscriber subscr;
513 int minlen = 0;
514 int maxlen = 0;
515 int rc;
516 const char *id_type = argv[0];
517 const char *id = argv[1];
518 const char *alg_type = AUTH_ALG_TYPES_3G;
519 const char *k = argv[2];
520 bool opc_is_op = (strcasecmp("op", argv[3]) == 0);
521 const char *op_opc = argv[4];
522 int ind_bitlen = argc > 6? atoi(argv[6]) : 5;
523 struct sub_auth_data_str aud3g = {
524 .type = OSMO_AUTH_TYPE_UMTS,
525 .u.umts = {
526 .k = k,
527 .opc_is_op = opc_is_op,
528 .opc = op_opc,
529 .ind_bitlen = ind_bitlen,
530 },
531 };
532
533 if (!auth_algo_parse(alg_type, &aud3g.algo, &minlen, &maxlen)) {
534 vty_out(vty, "%% Unknown auth algorithm: '%s'%s", alg_type, VTY_NEWLINE);
535 return CMD_WARNING;
536 }
537
538 if (!is_hexkey_valid(vty, "K", aud3g.u.umts.k, minlen, maxlen))
539 return CMD_WARNING;
540
541 if (!is_hexkey_valid(vty, opc_is_op ? "OP" : "OPC", aud3g.u.umts.opc,
542 MILENAGE_KEY_LEN, MILENAGE_KEY_LEN))
543 return CMD_WARNING;
544
545 if (get_subscr_by_argv(vty, id_type, id, &subscr))
546 return CMD_WARNING;
547
548 rc = db_subscr_update_aud_by_id(g_hlr->dbc, subscr.id, &aud3g);
549
550 if (rc) {
551 vty_out(vty, "%% Error: cannot set 3G auth data for IMSI='%s'%s",
552 subscr.imsi, VTY_NEWLINE);
553 return CMD_WARNING;
554 }
555 return CMD_SUCCESS;
556}
557
Oliver Smith02078b72019-01-11 15:41:29 +0100558DEFUN(subscriber_imei,
559 subscriber_imei_cmd,
560 SUBSCR_UPDATE "imei (none|IMEI)",
561 SUBSCR_UPDATE_HELP
562 "Set IMEI of the subscriber (normally populated from MSC, no need to set this manually)\n"
563 "Forget IMEI\n"
564 "Set IMEI (use for debug only!)\n")
565{
566 struct hlr_subscriber subscr;
567 const char *id_type = argv[0];
568 const char *id = argv[1];
569 const char *imei = argv[2];
570 char imei_buf[GSM23003_IMEI_NUM_DIGITS_NO_CHK+1];
571
572 if (strcmp(imei, "none") == 0)
573 imei = NULL;
574 else {
575 /* Verify IMEI with checksum digit */
576 if (osmo_imei_str_valid(imei, true)) {
577 /* Cut the checksum off */
578 osmo_strlcpy(imei_buf, imei, sizeof(imei_buf));
579 imei = imei_buf;
580 } else if (!osmo_imei_str_valid(imei, false)) {
581 vty_out(vty, "%% IMEI invalid: '%s'%s", imei, VTY_NEWLINE);
582 return CMD_WARNING;
583 }
584 }
585
586 if (get_subscr_by_argv(vty, id_type, id, &subscr))
587 return CMD_WARNING;
588
589 if (db_subscr_update_imei_by_imsi(g_hlr->dbc, subscr.imsi, imei)) {
590 vty_out(vty, "%% Error: cannot update IMEI for subscriber IMSI='%s'%s",
591 subscr.imsi, VTY_NEWLINE);
592 return CMD_WARNING;
593 }
594
595 if (imei)
596 vty_out(vty, "%% Updated subscriber IMSI='%s' to IMEI='%s'%s",
597 subscr.imsi, imei, VTY_NEWLINE);
598 else
599 vty_out(vty, "%% Updated subscriber IMSI='%s': removed IMEI%s",
600 subscr.imsi, VTY_NEWLINE);
601
602 return CMD_SUCCESS;
603}
604
Oliver Smith3b33b012019-07-15 10:35:22 +0200605DEFUN(subscriber_nam,
606 subscriber_nam_cmd,
607 SUBSCR_UPDATE "network-access-mode (none|cs|ps|cs+ps)",
608 SUBSCR_UPDATE_HELP
609 "Set Network Access Mode (NAM) of the subscriber\n"
610 "Do not allow access to circuit switched or packet switched services\n"
611 "Allow access to circuit switched services only\n"
612 "Allow access to packet switched services only\n"
613 "Allow access to both circuit and packet switched services\n")
614{
615 struct hlr_subscriber subscr;
616 const char *id_type = argv[0];
617 const char *id = argv[1];
618 bool nam_cs = strstr(argv[2], "cs");
619 bool nam_ps = strstr(argv[2], "ps");
620
621 if (get_subscr_by_argv(vty, id_type, id, &subscr))
622 return CMD_WARNING;
623
624 if (nam_cs != subscr.nam_cs)
625 hlr_subscr_nam(g_hlr, &subscr, nam_cs, 0);
626 if (nam_ps != subscr.nam_ps)
627 hlr_subscr_nam(g_hlr, &subscr, nam_ps, 1);
628
629 return CMD_SUCCESS;
630}
631
Oliver Smith02078b72019-01-11 15:41:29 +0100632
Harald Welted5807b82018-07-29 12:27:41 +0200633void hlr_vty_subscriber_init(void)
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200634{
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200635 install_element_ve(&subscriber_show_cmd);
Neels Hofmeyr8aa780b2018-12-02 18:52:49 +0100636 install_element_ve(&show_subscriber_cmd);
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200637 install_element(ENABLE_NODE, &subscriber_create_cmd);
638 install_element(ENABLE_NODE, &subscriber_delete_cmd);
639 install_element(ENABLE_NODE, &subscriber_msisdn_cmd);
640 install_element(ENABLE_NODE, &subscriber_no_aud2g_cmd);
641 install_element(ENABLE_NODE, &subscriber_aud2g_cmd);
642 install_element(ENABLE_NODE, &subscriber_no_aud3g_cmd);
643 install_element(ENABLE_NODE, &subscriber_aud3g_cmd);
Oliver Smith02078b72019-01-11 15:41:29 +0100644 install_element(ENABLE_NODE, &subscriber_imei_cmd);
Oliver Smith3b33b012019-07-15 10:35:22 +0200645 install_element(ENABLE_NODE, &subscriber_nam_cmd);
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200646}