blob: 3dbc383b2a145ed04e72105c64268d027ff51c5d [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 Smithedc27ef2019-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 Hofmeyrd9b36062018-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 Hofmeyrd9b36062018-12-25 17:23:41 +010041 struct tm tm;
42 gmtime_r(t, &tm);
43 strftime(buf, bufsize, "%FT%T+00:00", &tm);
44 return buf;
Stefan Sperling5c14c9c2018-12-07 12:30:21 +010045}
46
Neels Hofmeyr07e16022019-11-20 02:36:35 +010047static void dump_last_lu_seen(struct vty *vty, const char *domain_label, time_t last_lu_seen)
48{
Oliver Smithedc27ef2019-11-25 13:28:23 +010049 uint32_t age;
Neels Hofmeyrd9b36062018-12-25 17:23:41 +010050 char datebuf[32];
Neels Hofmeyr07e16022019-11-20 02:36:35 +010051 if (!last_lu_seen)
52 return;
Neels Hofmeyrd9b36062018-12-25 17:23:41 +010053 vty_out(vty, " last LU seen on %s: %s", domain_label, get_datestr(&last_lu_seen, datebuf, sizeof(datebuf)));
Oliver Smithedc27ef2019-11-25 13:28:23 +010054 if (!timestamp_age(&last_lu_seen, &age))
55 vty_out(vty, " (invalid timestamp)%s", VTY_NEWLINE);
Neels Hofmeyr26b49052019-11-27 16:47:50 +010056 else {
57 vty_out(vty, " (");
58#define UNIT_AGO(UNITNAME, UNITVAL) \
59 if (age >= (UNITVAL)) { \
60 vty_out(vty, "%u%s", age / (UNITVAL), UNITNAME); \
61 age = age % (UNITVAL); \
62 }
63 UNIT_AGO("d", 60*60*24);
64 UNIT_AGO("h", 60*60);
65 UNIT_AGO("m", 60);
66 UNIT_AGO("s", 1);
67 vty_out(vty, " ago)%s", VTY_NEWLINE);
68#undef UNIT_AGO
69 }
Neels Hofmeyr07e16022019-11-20 02:36:35 +010070}
71
Neels Hofmeyr183e7002017-10-06 02:59:54 +020072static void subscr_dump_full_vty(struct vty *vty, struct hlr_subscriber *subscr)
73{
74 int rc;
75 struct osmo_sub_auth_data aud2g;
76 struct osmo_sub_auth_data aud3g;
77
78 vty_out(vty, " ID: %"PRIu64"%s", subscr->id, VTY_NEWLINE);
79
Neels Hofmeyr36bec872017-10-23 18:44:23 +020080 vty_out(vty, " IMSI: %s%s", *subscr->imsi ? subscr->imsi : "none", VTY_NEWLINE);
Neels Hofmeyr183e7002017-10-06 02:59:54 +020081 vty_out(vty, " MSISDN: %s%s", *subscr->msisdn ? subscr->msisdn : "none", VTY_NEWLINE);
Oliver Smith02078b72019-01-11 15:41:29 +010082
83 if (*subscr->imei) {
84 char checksum = osmo_luhn(subscr->imei, 14);
85 if (checksum == -EINVAL)
86 vty_out(vty, " IMEI: %s (INVALID LENGTH!)%s", subscr->imei, VTY_NEWLINE);
87 else
88 vty_out(vty, " IMEI: %s%c%s", subscr->imei, checksum, VTY_NEWLINE);
89 }
90
Neels Hofmeyr183e7002017-10-06 02:59:54 +020091 if (*subscr->vlr_number)
92 vty_out(vty, " VLR number: %s%s", subscr->vlr_number, VTY_NEWLINE);
93 if (*subscr->sgsn_number)
94 vty_out(vty, " SGSN number: %s%s", subscr->sgsn_number, VTY_NEWLINE);
95 if (*subscr->sgsn_address)
96 vty_out(vty, " SGSN address: %s%s", subscr->sgsn_address, VTY_NEWLINE);
97 if (subscr->periodic_lu_timer)
98 vty_out(vty, " Periodic LU timer: %u%s", subscr->periodic_lu_timer, VTY_NEWLINE);
99 if (subscr->periodic_rau_tau_timer)
100 vty_out(vty, " Periodic RAU/TAU timer: %u%s", subscr->periodic_rau_tau_timer, VTY_NEWLINE);
101 if (subscr->lmsi)
102 vty_out(vty, " LMSI: %x%s", subscr->lmsi, VTY_NEWLINE);
103 if (!subscr->nam_cs)
104 vty_out(vty, " CS disabled%s", VTY_NEWLINE);
105 if (subscr->ms_purged_cs)
106 vty_out(vty, " CS purged%s", VTY_NEWLINE);
107 if (!subscr->nam_ps)
108 vty_out(vty, " PS disabled%s", VTY_NEWLINE);
109 if (subscr->ms_purged_ps)
110 vty_out(vty, " PS purged%s", VTY_NEWLINE);
Neels Hofmeyr07e16022019-11-20 02:36:35 +0100111 dump_last_lu_seen(vty, "CS", subscr->last_lu_seen);
112 dump_last_lu_seen(vty, "PS", subscr->last_lu_seen_ps);
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200113
114 if (!*subscr->imsi)
115 return;
116
117 OSMO_ASSERT(g_hlr);
118 rc = db_get_auth_data(g_hlr->dbc, subscr->imsi, &aud2g, &aud3g, NULL);
119
Neels Hofmeyrbd1dca02017-11-23 15:25:30 +0100120 switch (rc) {
121 case 0:
122 break;
123 case -ENOENT:
124 case -ENOKEY:
125 aud2g.algo = OSMO_AUTH_ALG_NONE;
126 aud3g.algo = OSMO_AUTH_ALG_NONE;
127 break;
128 default:
129 vty_out(vty, "%% Error retrieving data from database (%d)%s", rc, VTY_NEWLINE);
130 return;
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200131 }
132
133 if (aud2g.type != OSMO_AUTH_TYPE_NONE && aud2g.type != OSMO_AUTH_TYPE_GSM) {
134 vty_out(vty, "%% Error: 2G auth data is not of type 'GSM'%s", VTY_NEWLINE);
135 aud2g = (struct osmo_sub_auth_data){};
136 }
137
138 if (aud3g.type != OSMO_AUTH_TYPE_NONE && aud3g.type != OSMO_AUTH_TYPE_UMTS) {
139 vty_out(vty, "%% Error: 3G auth data is not of type 'UMTS'%s", VTY_NEWLINE);
140 aud3g = (struct osmo_sub_auth_data){};
141 }
142
143 if (aud2g.algo != OSMO_AUTH_ALG_NONE && aud2g.type != OSMO_AUTH_TYPE_NONE) {
144 vty_out(vty, " 2G auth: %s%s",
145 osmo_auth_alg_name(aud2g.algo), VTY_NEWLINE);
146 vty_out(vty, " KI=%s%s",
147 hexdump_buf(aud2g.u.gsm.ki), VTY_NEWLINE);
148 }
149
150 if (aud3g.algo != OSMO_AUTH_ALG_NONE && aud3g.type != OSMO_AUTH_TYPE_NONE) {
151 vty_out(vty, " 3G auth: %s%s", osmo_auth_alg_name(aud3g.algo), VTY_NEWLINE);
152 vty_out(vty, " K=%s%s", hexdump_buf(aud3g.u.umts.k), VTY_NEWLINE);
153 vty_out(vty, " %s=%s%s", aud3g.u.umts.opc_is_op? "OP" : "OPC",
154 hexdump_buf(aud3g.u.umts.opc), VTY_NEWLINE);
155 vty_out(vty, " IND-bitlen=%u", aud3g.u.umts.ind_bitlen);
156 if (aud3g.u.umts.sqn)
157 vty_out(vty, " last-SQN=%"PRIu64, aud3g.u.umts.sqn);
158 vty_out(vty, VTY_NEWLINE);
159 }
160}
161
162static int get_subscr_by_argv(struct vty *vty, const char *type, const char *id, struct hlr_subscriber *subscr)
163{
Oliver Smith02078b72019-01-11 15:41:29 +0100164 char imei_buf[GSM23003_IMEI_NUM_DIGITS_NO_CHK+1];
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200165 int rc = -1;
166 if (strcmp(type, "imsi") == 0)
167 rc = db_subscr_get_by_imsi(g_hlr->dbc, id, subscr);
168 else if (strcmp(type, "msisdn") == 0)
169 rc = db_subscr_get_by_msisdn(g_hlr->dbc, id, subscr);
170 else if (strcmp(type, "id") == 0)
171 rc = db_subscr_get_by_id(g_hlr->dbc, atoll(id), subscr);
Oliver Smith02078b72019-01-11 15:41:29 +0100172 else if (strcmp(type, "imei") == 0) {
173 /* Verify IMEI with checksum digit */
174 if (osmo_imei_str_valid(id, true)) {
175 /* Cut the checksum off */
176 osmo_strlcpy(imei_buf, id, sizeof(imei_buf));
177 id = imei_buf;
178 vty_out(vty, "%% Checksum validated and stripped for search: imei = '%s'%s", id,
179 VTY_NEWLINE);
180 }
181 rc = db_subscr_get_by_imei(g_hlr->dbc, id, subscr);
182 }
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200183 if (rc)
184 vty_out(vty, "%% No subscriber for %s = '%s'%s",
185 type, id, VTY_NEWLINE);
186 return rc;
187}
188
189#define SUBSCR_CMD "subscriber "
190#define SUBSCR_CMD_HELP "Subscriber management commands\n"
191
Oliver Smith02078b72019-01-11 15:41:29 +0100192#define SUBSCR_ID "(imsi|msisdn|id|imei) IDENT"
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200193#define SUBSCR_ID_HELP \
194 "Identify subscriber by IMSI\n" \
195 "Identify subscriber by MSISDN (phone number)\n" \
196 "Identify subscriber by database ID\n" \
Oliver Smith02078b72019-01-11 15:41:29 +0100197 "Identify subscriber by IMEI\n" \
198 "IMSI/MSISDN/ID/IMEI of the subscriber\n"
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200199
Neels Hofmeyr8aa780b2018-12-02 18:52:49 +0100200#define SUBSCR SUBSCR_CMD SUBSCR_ID " "
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200201#define SUBSCR_HELP SUBSCR_CMD_HELP SUBSCR_ID_HELP
202
203#define SUBSCR_UPDATE SUBSCR "update "
204#define SUBSCR_UPDATE_HELP SUBSCR_HELP "Set or update subscriber data\n"
Neels Hofmeyra820ea12018-12-02 19:46:46 +0100205#define SUBSCR_MSISDN_HELP "Set MSISDN (phone number) of the subscriber\n"
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200206
207DEFUN(subscriber_show,
208 subscriber_show_cmd,
209 SUBSCR "show",
210 SUBSCR_HELP "Show subscriber information\n")
211{
212 struct hlr_subscriber subscr;
213 const char *id_type = argv[0];
214 const char *id = argv[1];
215
216 if (get_subscr_by_argv(vty, id_type, id, &subscr))
217 return CMD_WARNING;
218
219 subscr_dump_full_vty(vty, &subscr);
220 return CMD_SUCCESS;
221}
222
Neels Hofmeyr8aa780b2018-12-02 18:52:49 +0100223ALIAS(subscriber_show, show_subscriber_cmd,
224 "show " SUBSCR_CMD SUBSCR_ID,
225 SHOW_STR SUBSCR_CMD_HELP SUBSCR_ID_HELP);
226
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200227DEFUN(subscriber_create,
228 subscriber_create_cmd,
229 SUBSCR_CMD "imsi IDENT create",
230 SUBSCR_CMD_HELP
Vadim Yanitskiyf473c7b2018-07-30 14:29:39 +0700231 "Identify subscriber by IMSI\n"
232 "IMSI/MSISDN/ID of the subscriber\n"
233 "Create subscriber by IMSI\n")
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200234{
235 int rc;
236 struct hlr_subscriber subscr;
237 const char *imsi = argv[0];
238
239 if (!osmo_imsi_str_valid(imsi)) {
240 vty_out(vty, "%% Not a valid IMSI: %s%s", imsi, VTY_NEWLINE);
241 return CMD_WARNING;
242 }
243
Oliver Smithcd2af5e2019-03-06 13:17:39 +0100244 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 +0200245
246 if (rc) {
247 if (rc == -EEXIST)
248 vty_out(vty, "%% Subscriber already exists for IMSI = %s%s",
249 imsi, VTY_NEWLINE);
250 else
251 vty_out(vty, "%% Error (rc=%d): cannot create subscriber for IMSI = %s%s",
252 rc, imsi, VTY_NEWLINE);
253 return CMD_WARNING;
254 }
255
256 rc = db_subscr_get_by_imsi(g_hlr->dbc, imsi, &subscr);
257 vty_out(vty, "%% Created subscriber %s%s", imsi, VTY_NEWLINE);
258
259 subscr_dump_full_vty(vty, &subscr);
260
261 return CMD_SUCCESS;
262}
263
264DEFUN(subscriber_delete,
265 subscriber_delete_cmd,
266 SUBSCR "delete",
267 SUBSCR_HELP "Delete subscriber from database\n")
268{
269 struct hlr_subscriber subscr;
270 int rc;
271 const char *id_type = argv[0];
272 const char *id = argv[1];
273
274 /* Find out the IMSI regardless of which way the caller decided to
275 * identify the subscriber by. */
276 if (get_subscr_by_argv(vty, id_type, id, &subscr))
277 return CMD_WARNING;
278
279 rc = db_subscr_delete_by_id(g_hlr->dbc, subscr.id);
280 if (rc) {
281 vty_out(vty, "%% Error: Failed to remove subscriber for IMSI '%s'%s",
282 subscr.imsi, VTY_NEWLINE);
283 return CMD_WARNING;
284 }
285
286 vty_out(vty, "%% Deleted subscriber for IMSI '%s'%s", subscr.imsi, VTY_NEWLINE);
287 return CMD_SUCCESS;
288}
289
290DEFUN(subscriber_msisdn,
291 subscriber_msisdn_cmd,
Neels Hofmeyra820ea12018-12-02 19:46:46 +0100292 SUBSCR_UPDATE "msisdn (none|MSISDN)",
293 SUBSCR_UPDATE_HELP SUBSCR_MSISDN_HELP
294 "Remove MSISDN (phone number)\n"
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200295 "New MSISDN (phone number)\n")
296{
297 struct hlr_subscriber subscr;
298 const char *id_type = argv[0];
299 const char *id = argv[1];
300 const char *msisdn = argv[2];
301
Neels Hofmeyra820ea12018-12-02 19:46:46 +0100302 if (strcmp(msisdn, "none") == 0)
303 msisdn = NULL;
304 else {
305 if (strlen(msisdn) > sizeof(subscr.msisdn) - 1) {
306 vty_out(vty, "%% MSISDN is too long, max. %zu characters are allowed%s",
307 sizeof(subscr.msisdn)-1, VTY_NEWLINE);
308 return CMD_WARNING;
309 }
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200310
Neels Hofmeyra820ea12018-12-02 19:46:46 +0100311 if (!osmo_msisdn_str_valid(msisdn)) {
312 vty_out(vty, "%% MSISDN invalid: '%s'%s", msisdn, VTY_NEWLINE);
313 return CMD_WARNING;
314 }
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200315 }
316
317 if (get_subscr_by_argv(vty, id_type, id, &subscr))
318 return CMD_WARNING;
319
320 if (db_subscr_update_msisdn_by_imsi(g_hlr->dbc, subscr.imsi, msisdn)) {
321 vty_out(vty, "%% Error: cannot update MSISDN for subscriber IMSI='%s'%s",
322 subscr.imsi, VTY_NEWLINE);
323 return CMD_WARNING;
324 }
325
Neels Hofmeyra820ea12018-12-02 19:46:46 +0100326 if (msisdn) {
327 vty_out(vty, "%% Updated subscriber IMSI='%s' to MSISDN='%s'%s",
328 subscr.imsi, msisdn, VTY_NEWLINE);
Stefan Sperlingf1622522018-04-09 11:39:16 +0200329
Neels Hofmeyra820ea12018-12-02 19:46:46 +0100330 if (db_subscr_get_by_msisdn(g_hlr->dbc, msisdn, &subscr) == 0)
331 osmo_hlr_subscriber_update_notify(&subscr);
332 } else {
333 vty_out(vty, "%% Updated subscriber IMSI='%s': removed MSISDN%s",
334 subscr.imsi, VTY_NEWLINE);
335
Stefan Sperlingf1622522018-04-09 11:39:16 +0200336 osmo_hlr_subscriber_update_notify(&subscr);
Neels Hofmeyra820ea12018-12-02 19:46:46 +0100337 }
Stefan Sperlingf1622522018-04-09 11:39:16 +0200338
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200339 return CMD_SUCCESS;
340}
341
342static bool is_hexkey_valid(struct vty *vty, const char *label,
343 const char *hex_str, int minlen, int maxlen)
344{
345 if (osmo_is_hexstr(hex_str, minlen * 2, maxlen * 2, true))
346 return true;
347 vty_out(vty, "%% Invalid value for %s: '%s'%s", label, hex_str, VTY_NEWLINE);
348 return false;
349}
350
351#define AUTH_ALG_TYPES_2G "(comp128v1|comp128v2|comp128v3|xor)"
352#define AUTH_ALG_TYPES_2G_HELP \
353 "Use COMP128v1 algorithm\n" \
354 "Use COMP128v2 algorithm\n" \
355 "Use COMP128v3 algorithm\n" \
356 "Use XOR algorithm\n"
357
358#define AUTH_ALG_TYPES_3G "milenage"
359#define AUTH_ALG_TYPES_3G_HELP \
360 "Use Milenage algorithm\n"
361
362#define A38_XOR_MIN_KEY_LEN 12
363#define A38_XOR_MAX_KEY_LEN 16
364#define A38_COMP128_KEY_LEN 16
365
366#define MILENAGE_KEY_LEN 16
367
368static bool auth_algo_parse(const char *alg_str, enum osmo_auth_algo *algo,
369 int *minlen, int *maxlen)
370{
371 if (!strcasecmp(alg_str, "none")) {
372 *algo = OSMO_AUTH_ALG_NONE;
373 *minlen = *maxlen = 0;
374 } else if (!strcasecmp(alg_str, "comp128v1")) {
375 *algo = OSMO_AUTH_ALG_COMP128v1;
376 *minlen = *maxlen = A38_COMP128_KEY_LEN;
377 } else if (!strcasecmp(alg_str, "comp128v2")) {
378 *algo = OSMO_AUTH_ALG_COMP128v2;
379 *minlen = *maxlen = A38_COMP128_KEY_LEN;
380 } else if (!strcasecmp(alg_str, "comp128v3")) {
381 *algo = OSMO_AUTH_ALG_COMP128v3;
382 *minlen = *maxlen = A38_COMP128_KEY_LEN;
383 } else if (!strcasecmp(alg_str, "xor")) {
384 *algo = OSMO_AUTH_ALG_XOR;
385 *minlen = A38_XOR_MIN_KEY_LEN;
386 *maxlen = A38_XOR_MAX_KEY_LEN;
387 } else if (!strcasecmp(alg_str, "milenage")) {
388 *algo = OSMO_AUTH_ALG_MILENAGE;
389 *minlen = *maxlen = MILENAGE_KEY_LEN;
390 } else
391 return false;
392 return true;
393}
394
395DEFUN(subscriber_no_aud2g,
396 subscriber_no_aud2g_cmd,
397 SUBSCR_UPDATE "aud2g none",
398 SUBSCR_UPDATE_HELP
399 "Set 2G authentication data\n"
400 "Delete 2G authentication data\n")
401{
402 struct hlr_subscriber subscr;
403 int rc;
404 const char *id_type = argv[0];
405 const char *id = argv[1];
406 struct sub_auth_data_str aud = {
407 .type = OSMO_AUTH_TYPE_GSM,
408 .algo = OSMO_AUTH_ALG_NONE,
409 };
410
411 if (get_subscr_by_argv(vty, id_type, id, &subscr))
412 return CMD_WARNING;
413
414 rc = db_subscr_update_aud_by_id(g_hlr->dbc, subscr.id, &aud);
415
Harald Welte880a34d2018-03-01 21:32:01 +0100416 if (rc && rc != -ENOENT) {
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200417 vty_out(vty, "%% Error: cannot disable 2G auth data for IMSI='%s'%s",
418 subscr.imsi, VTY_NEWLINE);
419 return CMD_WARNING;
420 }
421 return CMD_SUCCESS;
422}
423
424DEFUN(subscriber_aud2g,
425 subscriber_aud2g_cmd,
426 SUBSCR_UPDATE "aud2g " AUTH_ALG_TYPES_2G " ki KI",
427 SUBSCR_UPDATE_HELP
428 "Set 2G authentication data\n"
429 AUTH_ALG_TYPES_2G_HELP
430 "Set Ki Encryption Key\n" "Ki as 32 hexadecimal characters\n")
431{
432 struct hlr_subscriber subscr;
433 int rc;
434 int minlen = 0;
435 int maxlen = 0;
436 const char *id_type = argv[0];
437 const char *id = argv[1];
438 const char *alg_type = argv[2];
439 const char *ki = argv[3];
440 struct sub_auth_data_str aud2g = {
441 .type = OSMO_AUTH_TYPE_GSM,
442 .u.gsm.ki = ki,
443 };
444
445 if (!auth_algo_parse(alg_type, &aud2g.algo, &minlen, &maxlen)) {
446 vty_out(vty, "%% Unknown auth algorithm: '%s'%s", alg_type, VTY_NEWLINE);
447 return CMD_WARNING;
448 }
449
450 if (!is_hexkey_valid(vty, "KI", aud2g.u.gsm.ki, minlen, maxlen))
451 return CMD_WARNING;
452
453 if (get_subscr_by_argv(vty, id_type, id, &subscr))
454 return CMD_WARNING;
455
456 rc = db_subscr_update_aud_by_id(g_hlr->dbc, subscr.id, &aud2g);
457
458 if (rc) {
459 vty_out(vty, "%% Error: cannot set 2G auth data for IMSI='%s'%s",
460 subscr.imsi, VTY_NEWLINE);
461 return CMD_WARNING;
462 }
463 return CMD_SUCCESS;
464}
465
466DEFUN(subscriber_no_aud3g,
467 subscriber_no_aud3g_cmd,
468 SUBSCR_UPDATE "aud3g none",
469 SUBSCR_UPDATE_HELP
470 "Set UMTS authentication data (3G, and 2G with UMTS AKA)\n"
471 "Delete 3G authentication data\n")
472{
473 struct hlr_subscriber subscr;
474 int rc;
475 const char *id_type = argv[0];
476 const char *id = argv[1];
477 struct sub_auth_data_str aud = {
478 .type = OSMO_AUTH_TYPE_UMTS,
479 .algo = OSMO_AUTH_ALG_NONE,
480 };
481
482 if (get_subscr_by_argv(vty, id_type, id, &subscr))
483 return CMD_WARNING;
484
485 rc = db_subscr_update_aud_by_id(g_hlr->dbc, subscr.id, &aud);
486
Harald Welte880a34d2018-03-01 21:32:01 +0100487 if (rc && rc != -ENOENT) {
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200488 vty_out(vty, "%% Error: cannot disable 3G auth data for IMSI='%s'%s",
489 subscr.imsi, VTY_NEWLINE);
490 return CMD_WARNING;
491 }
492 return CMD_SUCCESS;
493}
494
495DEFUN(subscriber_aud3g,
496 subscriber_aud3g_cmd,
497 SUBSCR_UPDATE "aud3g " AUTH_ALG_TYPES_3G
498 " k K"
499 " (op|opc) OP_C"
500 " [ind-bitlen] [<0-28>]",
501 SUBSCR_UPDATE_HELP
502 "Set UMTS authentication data (3G, and 2G with UMTS AKA)\n"
503 AUTH_ALG_TYPES_3G_HELP
504 "Set Encryption Key K\n" "K as 32 hexadecimal characters\n"
505 "Set OP key\n" "Set OPC key\n" "OP or OPC as 32 hexadecimal characters\n"
506 "Set IND bit length\n" "IND bit length value (default: 5)\n")
507{
508 struct hlr_subscriber subscr;
509 int minlen = 0;
510 int maxlen = 0;
511 int rc;
512 const char *id_type = argv[0];
513 const char *id = argv[1];
514 const char *alg_type = AUTH_ALG_TYPES_3G;
515 const char *k = argv[2];
516 bool opc_is_op = (strcasecmp("op", argv[3]) == 0);
517 const char *op_opc = argv[4];
518 int ind_bitlen = argc > 6? atoi(argv[6]) : 5;
519 struct sub_auth_data_str aud3g = {
520 .type = OSMO_AUTH_TYPE_UMTS,
521 .u.umts = {
522 .k = k,
523 .opc_is_op = opc_is_op,
524 .opc = op_opc,
525 .ind_bitlen = ind_bitlen,
526 },
527 };
528
529 if (!auth_algo_parse(alg_type, &aud3g.algo, &minlen, &maxlen)) {
530 vty_out(vty, "%% Unknown auth algorithm: '%s'%s", alg_type, VTY_NEWLINE);
531 return CMD_WARNING;
532 }
533
534 if (!is_hexkey_valid(vty, "K", aud3g.u.umts.k, minlen, maxlen))
535 return CMD_WARNING;
536
537 if (!is_hexkey_valid(vty, opc_is_op ? "OP" : "OPC", aud3g.u.umts.opc,
538 MILENAGE_KEY_LEN, MILENAGE_KEY_LEN))
539 return CMD_WARNING;
540
541 if (get_subscr_by_argv(vty, id_type, id, &subscr))
542 return CMD_WARNING;
543
544 rc = db_subscr_update_aud_by_id(g_hlr->dbc, subscr.id, &aud3g);
545
546 if (rc) {
547 vty_out(vty, "%% Error: cannot set 3G auth data for IMSI='%s'%s",
548 subscr.imsi, VTY_NEWLINE);
549 return CMD_WARNING;
550 }
551 return CMD_SUCCESS;
552}
553
Harald Welte6e237d32020-12-28 01:01:31 +0100554DEFUN(subscriber_aud3g_xor,
555 subscriber_aud3g_xor_cmd,
556 SUBSCR_UPDATE "aud3g xor k K"
557 " [ind-bitlen] [<0-28>]",
558 SUBSCR_UPDATE_HELP
559 "Set UMTS authentication data (3G, and 2G with UMTS AKA)\n"
560 "Use XOR algorithm\n"
561 "Set Encryption Key K\n" "K as 32 hexadecimal characters\n"
562 "Set IND bit length\n" "IND bit length value (default: 5)\n")
563{
564 struct hlr_subscriber subscr;
565 int minlen = 0;
566 int maxlen = 0;
567 int rc;
568 const char *id_type = argv[0];
569 const char *id = argv[1];
570 const char *k = argv[2];
571 int ind_bitlen = argc > 4? atoi(argv[4]) : 5;
572 struct sub_auth_data_str aud3g = {
573 .type = OSMO_AUTH_TYPE_UMTS,
574 .u.umts = {
575 .k = k,
576 .opc_is_op = 0,
577 .opc = "00000000000000000000000000000000",
578 .ind_bitlen = ind_bitlen,
579 },
580 };
581
582 if (!auth_algo_parse("xor", &aud3g.algo, &minlen, &maxlen)) {
583 vty_out(vty, "%% Unknown auth algorithm: '%s'%s", "xor", VTY_NEWLINE);
584 return CMD_WARNING;
585 }
586
587 if (!is_hexkey_valid(vty, "K", aud3g.u.umts.k, minlen, maxlen))
588 return CMD_WARNING;
589
590 if (get_subscr_by_argv(vty, id_type, id, &subscr))
591 return CMD_WARNING;
592
593 rc = db_subscr_update_aud_by_id(g_hlr->dbc, subscr.id, &aud3g);
594
595 if (rc) {
596 vty_out(vty, "%% Error: cannot set 3G auth data for IMSI='%s'%s",
597 subscr.imsi, VTY_NEWLINE);
598 return CMD_WARNING;
599 }
600 return CMD_SUCCESS;
601}
602
Oliver Smith02078b72019-01-11 15:41:29 +0100603DEFUN(subscriber_imei,
604 subscriber_imei_cmd,
605 SUBSCR_UPDATE "imei (none|IMEI)",
606 SUBSCR_UPDATE_HELP
607 "Set IMEI of the subscriber (normally populated from MSC, no need to set this manually)\n"
608 "Forget IMEI\n"
609 "Set IMEI (use for debug only!)\n")
610{
611 struct hlr_subscriber subscr;
612 const char *id_type = argv[0];
613 const char *id = argv[1];
614 const char *imei = argv[2];
615 char imei_buf[GSM23003_IMEI_NUM_DIGITS_NO_CHK+1];
616
617 if (strcmp(imei, "none") == 0)
618 imei = NULL;
619 else {
620 /* Verify IMEI with checksum digit */
621 if (osmo_imei_str_valid(imei, true)) {
622 /* Cut the checksum off */
623 osmo_strlcpy(imei_buf, imei, sizeof(imei_buf));
624 imei = imei_buf;
625 } else if (!osmo_imei_str_valid(imei, false)) {
626 vty_out(vty, "%% IMEI invalid: '%s'%s", imei, VTY_NEWLINE);
627 return CMD_WARNING;
628 }
629 }
630
631 if (get_subscr_by_argv(vty, id_type, id, &subscr))
632 return CMD_WARNING;
633
634 if (db_subscr_update_imei_by_imsi(g_hlr->dbc, subscr.imsi, imei)) {
635 vty_out(vty, "%% Error: cannot update IMEI for subscriber IMSI='%s'%s",
636 subscr.imsi, VTY_NEWLINE);
637 return CMD_WARNING;
638 }
639
640 if (imei)
641 vty_out(vty, "%% Updated subscriber IMSI='%s' to IMEI='%s'%s",
642 subscr.imsi, imei, VTY_NEWLINE);
643 else
644 vty_out(vty, "%% Updated subscriber IMSI='%s': removed IMEI%s",
645 subscr.imsi, VTY_NEWLINE);
646
647 return CMD_SUCCESS;
648}
649
Oliver Smith3b33b012019-07-15 10:35:22 +0200650DEFUN(subscriber_nam,
651 subscriber_nam_cmd,
652 SUBSCR_UPDATE "network-access-mode (none|cs|ps|cs+ps)",
653 SUBSCR_UPDATE_HELP
654 "Set Network Access Mode (NAM) of the subscriber\n"
655 "Do not allow access to circuit switched or packet switched services\n"
656 "Allow access to circuit switched services only\n"
657 "Allow access to packet switched services only\n"
658 "Allow access to both circuit and packet switched services\n")
659{
660 struct hlr_subscriber subscr;
661 const char *id_type = argv[0];
662 const char *id = argv[1];
663 bool nam_cs = strstr(argv[2], "cs");
664 bool nam_ps = strstr(argv[2], "ps");
665
666 if (get_subscr_by_argv(vty, id_type, id, &subscr))
667 return CMD_WARNING;
668
669 if (nam_cs != subscr.nam_cs)
670 hlr_subscr_nam(g_hlr, &subscr, nam_cs, 0);
671 if (nam_ps != subscr.nam_ps)
672 hlr_subscr_nam(g_hlr, &subscr, nam_ps, 1);
673
674 return CMD_SUCCESS;
675}
676
Oliver Smith02078b72019-01-11 15:41:29 +0100677
Harald Welted5807b82018-07-29 12:27:41 +0200678void hlr_vty_subscriber_init(void)
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200679{
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200680 install_element_ve(&subscriber_show_cmd);
Neels Hofmeyr8aa780b2018-12-02 18:52:49 +0100681 install_element_ve(&show_subscriber_cmd);
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200682 install_element(ENABLE_NODE, &subscriber_create_cmd);
683 install_element(ENABLE_NODE, &subscriber_delete_cmd);
684 install_element(ENABLE_NODE, &subscriber_msisdn_cmd);
685 install_element(ENABLE_NODE, &subscriber_no_aud2g_cmd);
686 install_element(ENABLE_NODE, &subscriber_aud2g_cmd);
687 install_element(ENABLE_NODE, &subscriber_no_aud3g_cmd);
688 install_element(ENABLE_NODE, &subscriber_aud3g_cmd);
Harald Welte6e237d32020-12-28 01:01:31 +0100689 install_element(ENABLE_NODE, &subscriber_aud3g_xor_cmd);
Oliver Smith02078b72019-01-11 15:41:29 +0100690 install_element(ENABLE_NODE, &subscriber_imei_cmd);
Oliver Smith3b33b012019-07-15 10:35:22 +0200691 install_element(ENABLE_NODE, &subscriber_nam_cmd);
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200692}