blob: 14a37f2c980bc78cdaa1b50eabb5a3186acf9ad5 [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
Stefan Sperling5c14c9c2018-12-07 12:30:21 +010039static char *
40get_datestr(const time_t *t, char *datebuf)
41{
42 char *p, *s = ctime_r(t, datebuf);
43
44 /* Strip trailing newline. */
45 p = strchr(s, '\n');
46 if (p)
47 *p = '\0';
48 return s;
49}
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 Hofmeyr07e16022019-11-20 02:36:35 +010054 char datebuf[26]; /* for ctime_r(3) */
55 if (!last_lu_seen)
56 return;
Oliver Smithf6d457e2019-11-25 13:28:23 +010057 vty_out(vty, " last LU seen on %s: %s UTC", domain_label, get_datestr(&last_lu_seen, datebuf));
58 if (!timestamp_age(&last_lu_seen, &age))
59 vty_out(vty, " (invalid timestamp)%s", VTY_NEWLINE);
60 else
61 vty_out(vty, " (%us ago)%s", age, VTY_NEWLINE);
Neels Hofmeyr07e16022019-11-20 02:36:35 +010062}
63
Neels Hofmeyr183e7002017-10-06 02:59:54 +020064static void subscr_dump_full_vty(struct vty *vty, struct hlr_subscriber *subscr)
65{
66 int rc;
67 struct osmo_sub_auth_data aud2g;
68 struct osmo_sub_auth_data aud3g;
69
70 vty_out(vty, " ID: %"PRIu64"%s", subscr->id, VTY_NEWLINE);
71
Neels Hofmeyr36bec872017-10-23 18:44:23 +020072 vty_out(vty, " IMSI: %s%s", *subscr->imsi ? subscr->imsi : "none", VTY_NEWLINE);
Neels Hofmeyr183e7002017-10-06 02:59:54 +020073 vty_out(vty, " MSISDN: %s%s", *subscr->msisdn ? subscr->msisdn : "none", VTY_NEWLINE);
Oliver Smith02078b72019-01-11 15:41:29 +010074
75 if (*subscr->imei) {
76 char checksum = osmo_luhn(subscr->imei, 14);
77 if (checksum == -EINVAL)
78 vty_out(vty, " IMEI: %s (INVALID LENGTH!)%s", subscr->imei, VTY_NEWLINE);
79 else
80 vty_out(vty, " IMEI: %s%c%s", subscr->imei, checksum, VTY_NEWLINE);
81 }
82
Neels Hofmeyr183e7002017-10-06 02:59:54 +020083 if (*subscr->vlr_number)
84 vty_out(vty, " VLR number: %s%s", subscr->vlr_number, VTY_NEWLINE);
85 if (*subscr->sgsn_number)
86 vty_out(vty, " SGSN number: %s%s", subscr->sgsn_number, VTY_NEWLINE);
87 if (*subscr->sgsn_address)
88 vty_out(vty, " SGSN address: %s%s", subscr->sgsn_address, VTY_NEWLINE);
89 if (subscr->periodic_lu_timer)
90 vty_out(vty, " Periodic LU timer: %u%s", subscr->periodic_lu_timer, VTY_NEWLINE);
91 if (subscr->periodic_rau_tau_timer)
92 vty_out(vty, " Periodic RAU/TAU timer: %u%s", subscr->periodic_rau_tau_timer, VTY_NEWLINE);
93 if (subscr->lmsi)
94 vty_out(vty, " LMSI: %x%s", subscr->lmsi, VTY_NEWLINE);
95 if (!subscr->nam_cs)
96 vty_out(vty, " CS disabled%s", VTY_NEWLINE);
97 if (subscr->ms_purged_cs)
98 vty_out(vty, " CS purged%s", VTY_NEWLINE);
99 if (!subscr->nam_ps)
100 vty_out(vty, " PS disabled%s", VTY_NEWLINE);
101 if (subscr->ms_purged_ps)
102 vty_out(vty, " PS purged%s", VTY_NEWLINE);
Neels Hofmeyr07e16022019-11-20 02:36:35 +0100103 dump_last_lu_seen(vty, "CS", subscr->last_lu_seen);
104 dump_last_lu_seen(vty, "PS", subscr->last_lu_seen_ps);
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200105
106 if (!*subscr->imsi)
107 return;
108
109 OSMO_ASSERT(g_hlr);
110 rc = db_get_auth_data(g_hlr->dbc, subscr->imsi, &aud2g, &aud3g, NULL);
111
Neels Hofmeyrbd1dca02017-11-23 15:25:30 +0100112 switch (rc) {
113 case 0:
114 break;
115 case -ENOENT:
116 case -ENOKEY:
117 aud2g.algo = OSMO_AUTH_ALG_NONE;
118 aud3g.algo = OSMO_AUTH_ALG_NONE;
119 break;
120 default:
121 vty_out(vty, "%% Error retrieving data from database (%d)%s", rc, VTY_NEWLINE);
122 return;
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200123 }
124
125 if (aud2g.type != OSMO_AUTH_TYPE_NONE && aud2g.type != OSMO_AUTH_TYPE_GSM) {
126 vty_out(vty, "%% Error: 2G auth data is not of type 'GSM'%s", VTY_NEWLINE);
127 aud2g = (struct osmo_sub_auth_data){};
128 }
129
130 if (aud3g.type != OSMO_AUTH_TYPE_NONE && aud3g.type != OSMO_AUTH_TYPE_UMTS) {
131 vty_out(vty, "%% Error: 3G auth data is not of type 'UMTS'%s", VTY_NEWLINE);
132 aud3g = (struct osmo_sub_auth_data){};
133 }
134
135 if (aud2g.algo != OSMO_AUTH_ALG_NONE && aud2g.type != OSMO_AUTH_TYPE_NONE) {
136 vty_out(vty, " 2G auth: %s%s",
137 osmo_auth_alg_name(aud2g.algo), VTY_NEWLINE);
138 vty_out(vty, " KI=%s%s",
139 hexdump_buf(aud2g.u.gsm.ki), VTY_NEWLINE);
140 }
141
142 if (aud3g.algo != OSMO_AUTH_ALG_NONE && aud3g.type != OSMO_AUTH_TYPE_NONE) {
143 vty_out(vty, " 3G auth: %s%s", osmo_auth_alg_name(aud3g.algo), VTY_NEWLINE);
144 vty_out(vty, " K=%s%s", hexdump_buf(aud3g.u.umts.k), VTY_NEWLINE);
145 vty_out(vty, " %s=%s%s", aud3g.u.umts.opc_is_op? "OP" : "OPC",
146 hexdump_buf(aud3g.u.umts.opc), VTY_NEWLINE);
147 vty_out(vty, " IND-bitlen=%u", aud3g.u.umts.ind_bitlen);
148 if (aud3g.u.umts.sqn)
149 vty_out(vty, " last-SQN=%"PRIu64, aud3g.u.umts.sqn);
150 vty_out(vty, VTY_NEWLINE);
151 }
152}
153
154static int get_subscr_by_argv(struct vty *vty, const char *type, const char *id, struct hlr_subscriber *subscr)
155{
Oliver Smith02078b72019-01-11 15:41:29 +0100156 char imei_buf[GSM23003_IMEI_NUM_DIGITS_NO_CHK+1];
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200157 int rc = -1;
158 if (strcmp(type, "imsi") == 0)
159 rc = db_subscr_get_by_imsi(g_hlr->dbc, id, subscr);
160 else if (strcmp(type, "msisdn") == 0)
161 rc = db_subscr_get_by_msisdn(g_hlr->dbc, id, subscr);
162 else if (strcmp(type, "id") == 0)
163 rc = db_subscr_get_by_id(g_hlr->dbc, atoll(id), subscr);
Oliver Smith02078b72019-01-11 15:41:29 +0100164 else if (strcmp(type, "imei") == 0) {
165 /* Verify IMEI with checksum digit */
166 if (osmo_imei_str_valid(id, true)) {
167 /* Cut the checksum off */
168 osmo_strlcpy(imei_buf, id, sizeof(imei_buf));
169 id = imei_buf;
170 vty_out(vty, "%% Checksum validated and stripped for search: imei = '%s'%s", id,
171 VTY_NEWLINE);
172 }
173 rc = db_subscr_get_by_imei(g_hlr->dbc, id, subscr);
174 }
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200175 if (rc)
176 vty_out(vty, "%% No subscriber for %s = '%s'%s",
177 type, id, VTY_NEWLINE);
178 return rc;
179}
180
181#define SUBSCR_CMD "subscriber "
182#define SUBSCR_CMD_HELP "Subscriber management commands\n"
183
Oliver Smith02078b72019-01-11 15:41:29 +0100184#define SUBSCR_ID "(imsi|msisdn|id|imei) IDENT"
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200185#define SUBSCR_ID_HELP \
186 "Identify subscriber by IMSI\n" \
187 "Identify subscriber by MSISDN (phone number)\n" \
188 "Identify subscriber by database ID\n" \
Oliver Smith02078b72019-01-11 15:41:29 +0100189 "Identify subscriber by IMEI\n" \
190 "IMSI/MSISDN/ID/IMEI of the subscriber\n"
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200191
Neels Hofmeyr8aa780b2018-12-02 18:52:49 +0100192#define SUBSCR SUBSCR_CMD SUBSCR_ID " "
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200193#define SUBSCR_HELP SUBSCR_CMD_HELP SUBSCR_ID_HELP
194
195#define SUBSCR_UPDATE SUBSCR "update "
196#define SUBSCR_UPDATE_HELP SUBSCR_HELP "Set or update subscriber data\n"
Neels Hofmeyra820ea12018-12-02 19:46:46 +0100197#define SUBSCR_MSISDN_HELP "Set MSISDN (phone number) of the subscriber\n"
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200198
199DEFUN(subscriber_show,
200 subscriber_show_cmd,
201 SUBSCR "show",
202 SUBSCR_HELP "Show subscriber information\n")
203{
204 struct hlr_subscriber subscr;
205 const char *id_type = argv[0];
206 const char *id = argv[1];
207
208 if (get_subscr_by_argv(vty, id_type, id, &subscr))
209 return CMD_WARNING;
210
211 subscr_dump_full_vty(vty, &subscr);
212 return CMD_SUCCESS;
213}
214
Neels Hofmeyr8aa780b2018-12-02 18:52:49 +0100215ALIAS(subscriber_show, show_subscriber_cmd,
216 "show " SUBSCR_CMD SUBSCR_ID,
217 SHOW_STR SUBSCR_CMD_HELP SUBSCR_ID_HELP);
218
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200219DEFUN(subscriber_create,
220 subscriber_create_cmd,
221 SUBSCR_CMD "imsi IDENT create",
222 SUBSCR_CMD_HELP
Vadim Yanitskiyf473c7b2018-07-30 14:29:39 +0700223 "Identify subscriber by IMSI\n"
224 "IMSI/MSISDN/ID of the subscriber\n"
225 "Create subscriber by IMSI\n")
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200226{
227 int rc;
228 struct hlr_subscriber subscr;
229 const char *imsi = argv[0];
230
231 if (!osmo_imsi_str_valid(imsi)) {
232 vty_out(vty, "%% Not a valid IMSI: %s%s", imsi, VTY_NEWLINE);
233 return CMD_WARNING;
234 }
235
Oliver Smithcd2af5e2019-03-06 13:17:39 +0100236 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 +0200237
238 if (rc) {
239 if (rc == -EEXIST)
240 vty_out(vty, "%% Subscriber already exists for IMSI = %s%s",
241 imsi, VTY_NEWLINE);
242 else
243 vty_out(vty, "%% Error (rc=%d): cannot create subscriber for IMSI = %s%s",
244 rc, imsi, VTY_NEWLINE);
245 return CMD_WARNING;
246 }
247
248 rc = db_subscr_get_by_imsi(g_hlr->dbc, imsi, &subscr);
249 vty_out(vty, "%% Created subscriber %s%s", imsi, VTY_NEWLINE);
250
251 subscr_dump_full_vty(vty, &subscr);
252
253 return CMD_SUCCESS;
254}
255
256DEFUN(subscriber_delete,
257 subscriber_delete_cmd,
258 SUBSCR "delete",
259 SUBSCR_HELP "Delete subscriber from database\n")
260{
261 struct hlr_subscriber subscr;
262 int rc;
263 const char *id_type = argv[0];
264 const char *id = argv[1];
265
266 /* Find out the IMSI regardless of which way the caller decided to
267 * identify the subscriber by. */
268 if (get_subscr_by_argv(vty, id_type, id, &subscr))
269 return CMD_WARNING;
270
271 rc = db_subscr_delete_by_id(g_hlr->dbc, subscr.id);
272 if (rc) {
273 vty_out(vty, "%% Error: Failed to remove subscriber for IMSI '%s'%s",
274 subscr.imsi, VTY_NEWLINE);
275 return CMD_WARNING;
276 }
277
278 vty_out(vty, "%% Deleted subscriber for IMSI '%s'%s", subscr.imsi, VTY_NEWLINE);
279 return CMD_SUCCESS;
280}
281
282DEFUN(subscriber_msisdn,
283 subscriber_msisdn_cmd,
Neels Hofmeyra820ea12018-12-02 19:46:46 +0100284 SUBSCR_UPDATE "msisdn (none|MSISDN)",
285 SUBSCR_UPDATE_HELP SUBSCR_MSISDN_HELP
286 "Remove MSISDN (phone number)\n"
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200287 "New MSISDN (phone number)\n")
288{
289 struct hlr_subscriber subscr;
290 const char *id_type = argv[0];
291 const char *id = argv[1];
292 const char *msisdn = argv[2];
293
Neels Hofmeyra820ea12018-12-02 19:46:46 +0100294 if (strcmp(msisdn, "none") == 0)
295 msisdn = NULL;
296 else {
297 if (strlen(msisdn) > sizeof(subscr.msisdn) - 1) {
298 vty_out(vty, "%% MSISDN is too long, max. %zu characters are allowed%s",
299 sizeof(subscr.msisdn)-1, VTY_NEWLINE);
300 return CMD_WARNING;
301 }
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200302
Neels Hofmeyra820ea12018-12-02 19:46:46 +0100303 if (!osmo_msisdn_str_valid(msisdn)) {
304 vty_out(vty, "%% MSISDN invalid: '%s'%s", msisdn, VTY_NEWLINE);
305 return CMD_WARNING;
306 }
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200307 }
308
309 if (get_subscr_by_argv(vty, id_type, id, &subscr))
310 return CMD_WARNING;
311
312 if (db_subscr_update_msisdn_by_imsi(g_hlr->dbc, subscr.imsi, msisdn)) {
313 vty_out(vty, "%% Error: cannot update MSISDN for subscriber IMSI='%s'%s",
314 subscr.imsi, VTY_NEWLINE);
315 return CMD_WARNING;
316 }
317
Neels Hofmeyra820ea12018-12-02 19:46:46 +0100318 if (msisdn) {
319 vty_out(vty, "%% Updated subscriber IMSI='%s' to MSISDN='%s'%s",
320 subscr.imsi, msisdn, VTY_NEWLINE);
Stefan Sperlingf1622522018-04-09 11:39:16 +0200321
Neels Hofmeyra820ea12018-12-02 19:46:46 +0100322 if (db_subscr_get_by_msisdn(g_hlr->dbc, msisdn, &subscr) == 0)
323 osmo_hlr_subscriber_update_notify(&subscr);
324 } else {
325 vty_out(vty, "%% Updated subscriber IMSI='%s': removed MSISDN%s",
326 subscr.imsi, VTY_NEWLINE);
327
Stefan Sperlingf1622522018-04-09 11:39:16 +0200328 osmo_hlr_subscriber_update_notify(&subscr);
Neels Hofmeyra820ea12018-12-02 19:46:46 +0100329 }
Stefan Sperlingf1622522018-04-09 11:39:16 +0200330
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200331 return CMD_SUCCESS;
332}
333
334static bool is_hexkey_valid(struct vty *vty, const char *label,
335 const char *hex_str, int minlen, int maxlen)
336{
337 if (osmo_is_hexstr(hex_str, minlen * 2, maxlen * 2, true))
338 return true;
339 vty_out(vty, "%% Invalid value for %s: '%s'%s", label, hex_str, VTY_NEWLINE);
340 return false;
341}
342
343#define AUTH_ALG_TYPES_2G "(comp128v1|comp128v2|comp128v3|xor)"
344#define AUTH_ALG_TYPES_2G_HELP \
345 "Use COMP128v1 algorithm\n" \
346 "Use COMP128v2 algorithm\n" \
347 "Use COMP128v3 algorithm\n" \
348 "Use XOR algorithm\n"
349
350#define AUTH_ALG_TYPES_3G "milenage"
351#define AUTH_ALG_TYPES_3G_HELP \
352 "Use Milenage algorithm\n"
353
354#define A38_XOR_MIN_KEY_LEN 12
355#define A38_XOR_MAX_KEY_LEN 16
356#define A38_COMP128_KEY_LEN 16
357
358#define MILENAGE_KEY_LEN 16
359
360static bool auth_algo_parse(const char *alg_str, enum osmo_auth_algo *algo,
361 int *minlen, int *maxlen)
362{
363 if (!strcasecmp(alg_str, "none")) {
364 *algo = OSMO_AUTH_ALG_NONE;
365 *minlen = *maxlen = 0;
366 } else if (!strcasecmp(alg_str, "comp128v1")) {
367 *algo = OSMO_AUTH_ALG_COMP128v1;
368 *minlen = *maxlen = A38_COMP128_KEY_LEN;
369 } else if (!strcasecmp(alg_str, "comp128v2")) {
370 *algo = OSMO_AUTH_ALG_COMP128v2;
371 *minlen = *maxlen = A38_COMP128_KEY_LEN;
372 } else if (!strcasecmp(alg_str, "comp128v3")) {
373 *algo = OSMO_AUTH_ALG_COMP128v3;
374 *minlen = *maxlen = A38_COMP128_KEY_LEN;
375 } else if (!strcasecmp(alg_str, "xor")) {
376 *algo = OSMO_AUTH_ALG_XOR;
377 *minlen = A38_XOR_MIN_KEY_LEN;
378 *maxlen = A38_XOR_MAX_KEY_LEN;
379 } else if (!strcasecmp(alg_str, "milenage")) {
380 *algo = OSMO_AUTH_ALG_MILENAGE;
381 *minlen = *maxlen = MILENAGE_KEY_LEN;
382 } else
383 return false;
384 return true;
385}
386
387DEFUN(subscriber_no_aud2g,
388 subscriber_no_aud2g_cmd,
389 SUBSCR_UPDATE "aud2g none",
390 SUBSCR_UPDATE_HELP
391 "Set 2G authentication data\n"
392 "Delete 2G authentication data\n")
393{
394 struct hlr_subscriber subscr;
395 int rc;
396 const char *id_type = argv[0];
397 const char *id = argv[1];
398 struct sub_auth_data_str aud = {
399 .type = OSMO_AUTH_TYPE_GSM,
400 .algo = OSMO_AUTH_ALG_NONE,
401 };
402
403 if (get_subscr_by_argv(vty, id_type, id, &subscr))
404 return CMD_WARNING;
405
406 rc = db_subscr_update_aud_by_id(g_hlr->dbc, subscr.id, &aud);
407
Harald Welte880a34d2018-03-01 21:32:01 +0100408 if (rc && rc != -ENOENT) {
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200409 vty_out(vty, "%% Error: cannot disable 2G auth data for IMSI='%s'%s",
410 subscr.imsi, VTY_NEWLINE);
411 return CMD_WARNING;
412 }
413 return CMD_SUCCESS;
414}
415
416DEFUN(subscriber_aud2g,
417 subscriber_aud2g_cmd,
418 SUBSCR_UPDATE "aud2g " AUTH_ALG_TYPES_2G " ki KI",
419 SUBSCR_UPDATE_HELP
420 "Set 2G authentication data\n"
421 AUTH_ALG_TYPES_2G_HELP
422 "Set Ki Encryption Key\n" "Ki as 32 hexadecimal characters\n")
423{
424 struct hlr_subscriber subscr;
425 int rc;
426 int minlen = 0;
427 int maxlen = 0;
428 const char *id_type = argv[0];
429 const char *id = argv[1];
430 const char *alg_type = argv[2];
431 const char *ki = argv[3];
432 struct sub_auth_data_str aud2g = {
433 .type = OSMO_AUTH_TYPE_GSM,
434 .u.gsm.ki = ki,
435 };
436
437 if (!auth_algo_parse(alg_type, &aud2g.algo, &minlen, &maxlen)) {
438 vty_out(vty, "%% Unknown auth algorithm: '%s'%s", alg_type, VTY_NEWLINE);
439 return CMD_WARNING;
440 }
441
442 if (!is_hexkey_valid(vty, "KI", aud2g.u.gsm.ki, minlen, maxlen))
443 return CMD_WARNING;
444
445 if (get_subscr_by_argv(vty, id_type, id, &subscr))
446 return CMD_WARNING;
447
448 rc = db_subscr_update_aud_by_id(g_hlr->dbc, subscr.id, &aud2g);
449
450 if (rc) {
451 vty_out(vty, "%% Error: cannot set 2G auth data for IMSI='%s'%s",
452 subscr.imsi, VTY_NEWLINE);
453 return CMD_WARNING;
454 }
455 return CMD_SUCCESS;
456}
457
458DEFUN(subscriber_no_aud3g,
459 subscriber_no_aud3g_cmd,
460 SUBSCR_UPDATE "aud3g none",
461 SUBSCR_UPDATE_HELP
462 "Set UMTS authentication data (3G, and 2G with UMTS AKA)\n"
463 "Delete 3G authentication data\n")
464{
465 struct hlr_subscriber subscr;
466 int rc;
467 const char *id_type = argv[0];
468 const char *id = argv[1];
469 struct sub_auth_data_str aud = {
470 .type = OSMO_AUTH_TYPE_UMTS,
471 .algo = OSMO_AUTH_ALG_NONE,
472 };
473
474 if (get_subscr_by_argv(vty, id_type, id, &subscr))
475 return CMD_WARNING;
476
477 rc = db_subscr_update_aud_by_id(g_hlr->dbc, subscr.id, &aud);
478
Harald Welte880a34d2018-03-01 21:32:01 +0100479 if (rc && rc != -ENOENT) {
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200480 vty_out(vty, "%% Error: cannot disable 3G auth data for IMSI='%s'%s",
481 subscr.imsi, VTY_NEWLINE);
482 return CMD_WARNING;
483 }
484 return CMD_SUCCESS;
485}
486
487DEFUN(subscriber_aud3g,
488 subscriber_aud3g_cmd,
489 SUBSCR_UPDATE "aud3g " AUTH_ALG_TYPES_3G
490 " k K"
491 " (op|opc) OP_C"
492 " [ind-bitlen] [<0-28>]",
493 SUBSCR_UPDATE_HELP
494 "Set UMTS authentication data (3G, and 2G with UMTS AKA)\n"
495 AUTH_ALG_TYPES_3G_HELP
496 "Set Encryption Key K\n" "K as 32 hexadecimal characters\n"
497 "Set OP key\n" "Set OPC key\n" "OP or OPC as 32 hexadecimal characters\n"
498 "Set IND bit length\n" "IND bit length value (default: 5)\n")
499{
500 struct hlr_subscriber subscr;
501 int minlen = 0;
502 int maxlen = 0;
503 int rc;
504 const char *id_type = argv[0];
505 const char *id = argv[1];
506 const char *alg_type = AUTH_ALG_TYPES_3G;
507 const char *k = argv[2];
508 bool opc_is_op = (strcasecmp("op", argv[3]) == 0);
509 const char *op_opc = argv[4];
510 int ind_bitlen = argc > 6? atoi(argv[6]) : 5;
511 struct sub_auth_data_str aud3g = {
512 .type = OSMO_AUTH_TYPE_UMTS,
513 .u.umts = {
514 .k = k,
515 .opc_is_op = opc_is_op,
516 .opc = op_opc,
517 .ind_bitlen = ind_bitlen,
518 },
519 };
520
521 if (!auth_algo_parse(alg_type, &aud3g.algo, &minlen, &maxlen)) {
522 vty_out(vty, "%% Unknown auth algorithm: '%s'%s", alg_type, VTY_NEWLINE);
523 return CMD_WARNING;
524 }
525
526 if (!is_hexkey_valid(vty, "K", aud3g.u.umts.k, minlen, maxlen))
527 return CMD_WARNING;
528
529 if (!is_hexkey_valid(vty, opc_is_op ? "OP" : "OPC", aud3g.u.umts.opc,
530 MILENAGE_KEY_LEN, MILENAGE_KEY_LEN))
531 return CMD_WARNING;
532
533 if (get_subscr_by_argv(vty, id_type, id, &subscr))
534 return CMD_WARNING;
535
536 rc = db_subscr_update_aud_by_id(g_hlr->dbc, subscr.id, &aud3g);
537
538 if (rc) {
539 vty_out(vty, "%% Error: cannot set 3G auth data for IMSI='%s'%s",
540 subscr.imsi, VTY_NEWLINE);
541 return CMD_WARNING;
542 }
543 return CMD_SUCCESS;
544}
545
Oliver Smith02078b72019-01-11 15:41:29 +0100546DEFUN(subscriber_imei,
547 subscriber_imei_cmd,
548 SUBSCR_UPDATE "imei (none|IMEI)",
549 SUBSCR_UPDATE_HELP
550 "Set IMEI of the subscriber (normally populated from MSC, no need to set this manually)\n"
551 "Forget IMEI\n"
552 "Set IMEI (use for debug only!)\n")
553{
554 struct hlr_subscriber subscr;
555 const char *id_type = argv[0];
556 const char *id = argv[1];
557 const char *imei = argv[2];
558 char imei_buf[GSM23003_IMEI_NUM_DIGITS_NO_CHK+1];
559
560 if (strcmp(imei, "none") == 0)
561 imei = NULL;
562 else {
563 /* Verify IMEI with checksum digit */
564 if (osmo_imei_str_valid(imei, true)) {
565 /* Cut the checksum off */
566 osmo_strlcpy(imei_buf, imei, sizeof(imei_buf));
567 imei = imei_buf;
568 } else if (!osmo_imei_str_valid(imei, false)) {
569 vty_out(vty, "%% IMEI invalid: '%s'%s", imei, VTY_NEWLINE);
570 return CMD_WARNING;
571 }
572 }
573
574 if (get_subscr_by_argv(vty, id_type, id, &subscr))
575 return CMD_WARNING;
576
577 if (db_subscr_update_imei_by_imsi(g_hlr->dbc, subscr.imsi, imei)) {
578 vty_out(vty, "%% Error: cannot update IMEI for subscriber IMSI='%s'%s",
579 subscr.imsi, VTY_NEWLINE);
580 return CMD_WARNING;
581 }
582
583 if (imei)
584 vty_out(vty, "%% Updated subscriber IMSI='%s' to IMEI='%s'%s",
585 subscr.imsi, imei, VTY_NEWLINE);
586 else
587 vty_out(vty, "%% Updated subscriber IMSI='%s': removed IMEI%s",
588 subscr.imsi, VTY_NEWLINE);
589
590 return CMD_SUCCESS;
591}
592
Oliver Smith3b33b012019-07-15 10:35:22 +0200593DEFUN(subscriber_nam,
594 subscriber_nam_cmd,
595 SUBSCR_UPDATE "network-access-mode (none|cs|ps|cs+ps)",
596 SUBSCR_UPDATE_HELP
597 "Set Network Access Mode (NAM) of the subscriber\n"
598 "Do not allow access to circuit switched or packet switched services\n"
599 "Allow access to circuit switched services only\n"
600 "Allow access to packet switched services only\n"
601 "Allow access to both circuit and packet switched services\n")
602{
603 struct hlr_subscriber subscr;
604 const char *id_type = argv[0];
605 const char *id = argv[1];
606 bool nam_cs = strstr(argv[2], "cs");
607 bool nam_ps = strstr(argv[2], "ps");
608
609 if (get_subscr_by_argv(vty, id_type, id, &subscr))
610 return CMD_WARNING;
611
612 if (nam_cs != subscr.nam_cs)
613 hlr_subscr_nam(g_hlr, &subscr, nam_cs, 0);
614 if (nam_ps != subscr.nam_ps)
615 hlr_subscr_nam(g_hlr, &subscr, nam_ps, 1);
616
617 return CMD_SUCCESS;
618}
619
Oliver Smith02078b72019-01-11 15:41:29 +0100620
Harald Welted5807b82018-07-29 12:27:41 +0200621void hlr_vty_subscriber_init(void)
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200622{
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200623 install_element_ve(&subscriber_show_cmd);
Neels Hofmeyr8aa780b2018-12-02 18:52:49 +0100624 install_element_ve(&show_subscriber_cmd);
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200625 install_element(ENABLE_NODE, &subscriber_create_cmd);
626 install_element(ENABLE_NODE, &subscriber_delete_cmd);
627 install_element(ENABLE_NODE, &subscriber_msisdn_cmd);
628 install_element(ENABLE_NODE, &subscriber_no_aud2g_cmd);
629 install_element(ENABLE_NODE, &subscriber_aud2g_cmd);
630 install_element(ENABLE_NODE, &subscriber_no_aud3g_cmd);
631 install_element(ENABLE_NODE, &subscriber_aud3g_cmd);
Oliver Smith02078b72019-01-11 15:41:29 +0100632 install_element(ENABLE_NODE, &subscriber_imei_cmd);
Oliver Smith3b33b012019-07-15 10:35:22 +0200633 install_element(ENABLE_NODE, &subscriber_nam_cmd);
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200634}