blob: 8079d10a6e8d5350098297eddcd3d7151c31c35f [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 Smithc992d852019-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 Hofmeyr43d5cfc2018-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 Hofmeyr43d5cfc2018-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 Smithc992d852019-11-25 13:28:23 +010049 uint32_t age;
Neels Hofmeyr43d5cfc2018-12-25 17:23:41 +010050 char datebuf[32];
Neels Hofmeyr07e16022019-11-20 02:36:35 +010051 if (!last_lu_seen)
52 return;
Neels Hofmeyr43d5cfc2018-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 Smithc992d852019-11-25 13:28:23 +010054 if (!timestamp_age(&last_lu_seen, &age))
55 vty_out(vty, " (invalid timestamp)%s", VTY_NEWLINE);
56 else
57 vty_out(vty, " (%us ago)%s", age, VTY_NEWLINE);
Neels Hofmeyr07e16022019-11-20 02:36:35 +010058}
59
Neels Hofmeyr183e7002017-10-06 02:59:54 +020060static void subscr_dump_full_vty(struct vty *vty, struct hlr_subscriber *subscr)
61{
62 int rc;
63 struct osmo_sub_auth_data aud2g;
64 struct osmo_sub_auth_data aud3g;
65
66 vty_out(vty, " ID: %"PRIu64"%s", subscr->id, VTY_NEWLINE);
67
Neels Hofmeyr36bec872017-10-23 18:44:23 +020068 vty_out(vty, " IMSI: %s%s", *subscr->imsi ? subscr->imsi : "none", VTY_NEWLINE);
Neels Hofmeyr183e7002017-10-06 02:59:54 +020069 vty_out(vty, " MSISDN: %s%s", *subscr->msisdn ? subscr->msisdn : "none", VTY_NEWLINE);
Oliver Smith02078b72019-01-11 15:41:29 +010070
71 if (*subscr->imei) {
72 char checksum = osmo_luhn(subscr->imei, 14);
73 if (checksum == -EINVAL)
74 vty_out(vty, " IMEI: %s (INVALID LENGTH!)%s", subscr->imei, VTY_NEWLINE);
75 else
76 vty_out(vty, " IMEI: %s%c%s", subscr->imei, checksum, VTY_NEWLINE);
77 }
78
Neels Hofmeyr183e7002017-10-06 02:59:54 +020079 if (*subscr->vlr_number)
80 vty_out(vty, " VLR number: %s%s", subscr->vlr_number, VTY_NEWLINE);
81 if (*subscr->sgsn_number)
82 vty_out(vty, " SGSN number: %s%s", subscr->sgsn_number, VTY_NEWLINE);
83 if (*subscr->sgsn_address)
84 vty_out(vty, " SGSN address: %s%s", subscr->sgsn_address, VTY_NEWLINE);
85 if (subscr->periodic_lu_timer)
86 vty_out(vty, " Periodic LU timer: %u%s", subscr->periodic_lu_timer, VTY_NEWLINE);
87 if (subscr->periodic_rau_tau_timer)
88 vty_out(vty, " Periodic RAU/TAU timer: %u%s", subscr->periodic_rau_tau_timer, VTY_NEWLINE);
89 if (subscr->lmsi)
90 vty_out(vty, " LMSI: %x%s", subscr->lmsi, VTY_NEWLINE);
91 if (!subscr->nam_cs)
92 vty_out(vty, " CS disabled%s", VTY_NEWLINE);
93 if (subscr->ms_purged_cs)
94 vty_out(vty, " CS purged%s", VTY_NEWLINE);
95 if (!subscr->nam_ps)
96 vty_out(vty, " PS disabled%s", VTY_NEWLINE);
97 if (subscr->ms_purged_ps)
98 vty_out(vty, " PS purged%s", VTY_NEWLINE);
Neels Hofmeyr07e16022019-11-20 02:36:35 +010099 dump_last_lu_seen(vty, "CS", subscr->last_lu_seen);
100 dump_last_lu_seen(vty, "PS", subscr->last_lu_seen_ps);
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200101
102 if (!*subscr->imsi)
103 return;
104
105 OSMO_ASSERT(g_hlr);
106 rc = db_get_auth_data(g_hlr->dbc, subscr->imsi, &aud2g, &aud3g, NULL);
107
Neels Hofmeyrbd1dca02017-11-23 15:25:30 +0100108 switch (rc) {
109 case 0:
110 break;
111 case -ENOENT:
112 case -ENOKEY:
113 aud2g.algo = OSMO_AUTH_ALG_NONE;
114 aud3g.algo = OSMO_AUTH_ALG_NONE;
115 break;
116 default:
117 vty_out(vty, "%% Error retrieving data from database (%d)%s", rc, VTY_NEWLINE);
118 return;
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200119 }
120
121 if (aud2g.type != OSMO_AUTH_TYPE_NONE && aud2g.type != OSMO_AUTH_TYPE_GSM) {
122 vty_out(vty, "%% Error: 2G auth data is not of type 'GSM'%s", VTY_NEWLINE);
123 aud2g = (struct osmo_sub_auth_data){};
124 }
125
126 if (aud3g.type != OSMO_AUTH_TYPE_NONE && aud3g.type != OSMO_AUTH_TYPE_UMTS) {
127 vty_out(vty, "%% Error: 3G auth data is not of type 'UMTS'%s", VTY_NEWLINE);
128 aud3g = (struct osmo_sub_auth_data){};
129 }
130
131 if (aud2g.algo != OSMO_AUTH_ALG_NONE && aud2g.type != OSMO_AUTH_TYPE_NONE) {
132 vty_out(vty, " 2G auth: %s%s",
133 osmo_auth_alg_name(aud2g.algo), VTY_NEWLINE);
134 vty_out(vty, " KI=%s%s",
135 hexdump_buf(aud2g.u.gsm.ki), VTY_NEWLINE);
136 }
137
138 if (aud3g.algo != OSMO_AUTH_ALG_NONE && aud3g.type != OSMO_AUTH_TYPE_NONE) {
139 vty_out(vty, " 3G auth: %s%s", osmo_auth_alg_name(aud3g.algo), VTY_NEWLINE);
140 vty_out(vty, " K=%s%s", hexdump_buf(aud3g.u.umts.k), VTY_NEWLINE);
141 vty_out(vty, " %s=%s%s", aud3g.u.umts.opc_is_op? "OP" : "OPC",
142 hexdump_buf(aud3g.u.umts.opc), VTY_NEWLINE);
143 vty_out(vty, " IND-bitlen=%u", aud3g.u.umts.ind_bitlen);
144 if (aud3g.u.umts.sqn)
145 vty_out(vty, " last-SQN=%"PRIu64, aud3g.u.umts.sqn);
146 vty_out(vty, VTY_NEWLINE);
147 }
148}
149
150static int get_subscr_by_argv(struct vty *vty, const char *type, const char *id, struct hlr_subscriber *subscr)
151{
Oliver Smith02078b72019-01-11 15:41:29 +0100152 char imei_buf[GSM23003_IMEI_NUM_DIGITS_NO_CHK+1];
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200153 int rc = -1;
154 if (strcmp(type, "imsi") == 0)
155 rc = db_subscr_get_by_imsi(g_hlr->dbc, id, subscr);
156 else if (strcmp(type, "msisdn") == 0)
157 rc = db_subscr_get_by_msisdn(g_hlr->dbc, id, subscr);
158 else if (strcmp(type, "id") == 0)
159 rc = db_subscr_get_by_id(g_hlr->dbc, atoll(id), subscr);
Oliver Smith02078b72019-01-11 15:41:29 +0100160 else if (strcmp(type, "imei") == 0) {
161 /* Verify IMEI with checksum digit */
162 if (osmo_imei_str_valid(id, true)) {
163 /* Cut the checksum off */
164 osmo_strlcpy(imei_buf, id, sizeof(imei_buf));
165 id = imei_buf;
166 vty_out(vty, "%% Checksum validated and stripped for search: imei = '%s'%s", id,
167 VTY_NEWLINE);
168 }
169 rc = db_subscr_get_by_imei(g_hlr->dbc, id, subscr);
170 }
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200171 if (rc)
172 vty_out(vty, "%% No subscriber for %s = '%s'%s",
173 type, id, VTY_NEWLINE);
174 return rc;
175}
176
177#define SUBSCR_CMD "subscriber "
178#define SUBSCR_CMD_HELP "Subscriber management commands\n"
179
Oliver Smith02078b72019-01-11 15:41:29 +0100180#define SUBSCR_ID "(imsi|msisdn|id|imei) IDENT"
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200181#define SUBSCR_ID_HELP \
182 "Identify subscriber by IMSI\n" \
183 "Identify subscriber by MSISDN (phone number)\n" \
184 "Identify subscriber by database ID\n" \
Oliver Smith02078b72019-01-11 15:41:29 +0100185 "Identify subscriber by IMEI\n" \
186 "IMSI/MSISDN/ID/IMEI of the subscriber\n"
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200187
Neels Hofmeyr8aa780b2018-12-02 18:52:49 +0100188#define SUBSCR SUBSCR_CMD SUBSCR_ID " "
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200189#define SUBSCR_HELP SUBSCR_CMD_HELP SUBSCR_ID_HELP
190
191#define SUBSCR_UPDATE SUBSCR "update "
192#define SUBSCR_UPDATE_HELP SUBSCR_HELP "Set or update subscriber data\n"
Neels Hofmeyra820ea12018-12-02 19:46:46 +0100193#define SUBSCR_MSISDN_HELP "Set MSISDN (phone number) of the subscriber\n"
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200194
195DEFUN(subscriber_show,
196 subscriber_show_cmd,
197 SUBSCR "show",
198 SUBSCR_HELP "Show subscriber information\n")
199{
200 struct hlr_subscriber subscr;
201 const char *id_type = argv[0];
202 const char *id = argv[1];
203
204 if (get_subscr_by_argv(vty, id_type, id, &subscr))
205 return CMD_WARNING;
206
207 subscr_dump_full_vty(vty, &subscr);
208 return CMD_SUCCESS;
209}
210
Neels Hofmeyr8aa780b2018-12-02 18:52:49 +0100211ALIAS(subscriber_show, show_subscriber_cmd,
212 "show " SUBSCR_CMD SUBSCR_ID,
213 SHOW_STR SUBSCR_CMD_HELP SUBSCR_ID_HELP);
214
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200215DEFUN(subscriber_create,
216 subscriber_create_cmd,
217 SUBSCR_CMD "imsi IDENT create",
218 SUBSCR_CMD_HELP
Vadim Yanitskiyf473c7b2018-07-30 14:29:39 +0700219 "Identify subscriber by IMSI\n"
220 "IMSI/MSISDN/ID of the subscriber\n"
221 "Create subscriber by IMSI\n")
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200222{
223 int rc;
224 struct hlr_subscriber subscr;
225 const char *imsi = argv[0];
226
227 if (!osmo_imsi_str_valid(imsi)) {
228 vty_out(vty, "%% Not a valid IMSI: %s%s", imsi, VTY_NEWLINE);
229 return CMD_WARNING;
230 }
231
Oliver Smithcd2af5e2019-03-06 13:17:39 +0100232 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 +0200233
234 if (rc) {
235 if (rc == -EEXIST)
236 vty_out(vty, "%% Subscriber already exists for IMSI = %s%s",
237 imsi, VTY_NEWLINE);
238 else
239 vty_out(vty, "%% Error (rc=%d): cannot create subscriber for IMSI = %s%s",
240 rc, imsi, VTY_NEWLINE);
241 return CMD_WARNING;
242 }
243
244 rc = db_subscr_get_by_imsi(g_hlr->dbc, imsi, &subscr);
245 vty_out(vty, "%% Created subscriber %s%s", imsi, VTY_NEWLINE);
246
247 subscr_dump_full_vty(vty, &subscr);
248
249 return CMD_SUCCESS;
250}
251
252DEFUN(subscriber_delete,
253 subscriber_delete_cmd,
254 SUBSCR "delete",
255 SUBSCR_HELP "Delete subscriber from database\n")
256{
257 struct hlr_subscriber subscr;
258 int rc;
259 const char *id_type = argv[0];
260 const char *id = argv[1];
261
262 /* Find out the IMSI regardless of which way the caller decided to
263 * identify the subscriber by. */
264 if (get_subscr_by_argv(vty, id_type, id, &subscr))
265 return CMD_WARNING;
266
267 rc = db_subscr_delete_by_id(g_hlr->dbc, subscr.id);
268 if (rc) {
269 vty_out(vty, "%% Error: Failed to remove subscriber for IMSI '%s'%s",
270 subscr.imsi, VTY_NEWLINE);
271 return CMD_WARNING;
272 }
273
274 vty_out(vty, "%% Deleted subscriber for IMSI '%s'%s", subscr.imsi, VTY_NEWLINE);
275 return CMD_SUCCESS;
276}
277
278DEFUN(subscriber_msisdn,
279 subscriber_msisdn_cmd,
Neels Hofmeyra820ea12018-12-02 19:46:46 +0100280 SUBSCR_UPDATE "msisdn (none|MSISDN)",
281 SUBSCR_UPDATE_HELP SUBSCR_MSISDN_HELP
282 "Remove MSISDN (phone number)\n"
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200283 "New MSISDN (phone number)\n")
284{
285 struct hlr_subscriber subscr;
286 const char *id_type = argv[0];
287 const char *id = argv[1];
288 const char *msisdn = argv[2];
289
Neels Hofmeyra820ea12018-12-02 19:46:46 +0100290 if (strcmp(msisdn, "none") == 0)
291 msisdn = NULL;
292 else {
293 if (strlen(msisdn) > sizeof(subscr.msisdn) - 1) {
294 vty_out(vty, "%% MSISDN is too long, max. %zu characters are allowed%s",
295 sizeof(subscr.msisdn)-1, VTY_NEWLINE);
296 return CMD_WARNING;
297 }
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200298
Neels Hofmeyra820ea12018-12-02 19:46:46 +0100299 if (!osmo_msisdn_str_valid(msisdn)) {
300 vty_out(vty, "%% MSISDN invalid: '%s'%s", msisdn, VTY_NEWLINE);
301 return CMD_WARNING;
302 }
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200303 }
304
305 if (get_subscr_by_argv(vty, id_type, id, &subscr))
306 return CMD_WARNING;
307
308 if (db_subscr_update_msisdn_by_imsi(g_hlr->dbc, subscr.imsi, msisdn)) {
309 vty_out(vty, "%% Error: cannot update MSISDN for subscriber IMSI='%s'%s",
310 subscr.imsi, VTY_NEWLINE);
311 return CMD_WARNING;
312 }
313
Neels Hofmeyra820ea12018-12-02 19:46:46 +0100314 if (msisdn) {
315 vty_out(vty, "%% Updated subscriber IMSI='%s' to MSISDN='%s'%s",
316 subscr.imsi, msisdn, VTY_NEWLINE);
Stefan Sperlingf1622522018-04-09 11:39:16 +0200317
Neels Hofmeyra820ea12018-12-02 19:46:46 +0100318 if (db_subscr_get_by_msisdn(g_hlr->dbc, msisdn, &subscr) == 0)
319 osmo_hlr_subscriber_update_notify(&subscr);
320 } else {
321 vty_out(vty, "%% Updated subscriber IMSI='%s': removed MSISDN%s",
322 subscr.imsi, VTY_NEWLINE);
323
Stefan Sperlingf1622522018-04-09 11:39:16 +0200324 osmo_hlr_subscriber_update_notify(&subscr);
Neels Hofmeyra820ea12018-12-02 19:46:46 +0100325 }
Stefan Sperlingf1622522018-04-09 11:39:16 +0200326
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200327 return CMD_SUCCESS;
328}
329
330static bool is_hexkey_valid(struct vty *vty, const char *label,
331 const char *hex_str, int minlen, int maxlen)
332{
333 if (osmo_is_hexstr(hex_str, minlen * 2, maxlen * 2, true))
334 return true;
335 vty_out(vty, "%% Invalid value for %s: '%s'%s", label, hex_str, VTY_NEWLINE);
336 return false;
337}
338
339#define AUTH_ALG_TYPES_2G "(comp128v1|comp128v2|comp128v3|xor)"
340#define AUTH_ALG_TYPES_2G_HELP \
341 "Use COMP128v1 algorithm\n" \
342 "Use COMP128v2 algorithm\n" \
343 "Use COMP128v3 algorithm\n" \
344 "Use XOR algorithm\n"
345
346#define AUTH_ALG_TYPES_3G "milenage"
347#define AUTH_ALG_TYPES_3G_HELP \
348 "Use Milenage algorithm\n"
349
350#define A38_XOR_MIN_KEY_LEN 12
351#define A38_XOR_MAX_KEY_LEN 16
352#define A38_COMP128_KEY_LEN 16
353
354#define MILENAGE_KEY_LEN 16
355
356static bool auth_algo_parse(const char *alg_str, enum osmo_auth_algo *algo,
357 int *minlen, int *maxlen)
358{
359 if (!strcasecmp(alg_str, "none")) {
360 *algo = OSMO_AUTH_ALG_NONE;
361 *minlen = *maxlen = 0;
362 } else if (!strcasecmp(alg_str, "comp128v1")) {
363 *algo = OSMO_AUTH_ALG_COMP128v1;
364 *minlen = *maxlen = A38_COMP128_KEY_LEN;
365 } else if (!strcasecmp(alg_str, "comp128v2")) {
366 *algo = OSMO_AUTH_ALG_COMP128v2;
367 *minlen = *maxlen = A38_COMP128_KEY_LEN;
368 } else if (!strcasecmp(alg_str, "comp128v3")) {
369 *algo = OSMO_AUTH_ALG_COMP128v3;
370 *minlen = *maxlen = A38_COMP128_KEY_LEN;
371 } else if (!strcasecmp(alg_str, "xor")) {
372 *algo = OSMO_AUTH_ALG_XOR;
373 *minlen = A38_XOR_MIN_KEY_LEN;
374 *maxlen = A38_XOR_MAX_KEY_LEN;
375 } else if (!strcasecmp(alg_str, "milenage")) {
376 *algo = OSMO_AUTH_ALG_MILENAGE;
377 *minlen = *maxlen = MILENAGE_KEY_LEN;
378 } else
379 return false;
380 return true;
381}
382
383DEFUN(subscriber_no_aud2g,
384 subscriber_no_aud2g_cmd,
385 SUBSCR_UPDATE "aud2g none",
386 SUBSCR_UPDATE_HELP
387 "Set 2G authentication data\n"
388 "Delete 2G authentication data\n")
389{
390 struct hlr_subscriber subscr;
391 int rc;
392 const char *id_type = argv[0];
393 const char *id = argv[1];
394 struct sub_auth_data_str aud = {
395 .type = OSMO_AUTH_TYPE_GSM,
396 .algo = OSMO_AUTH_ALG_NONE,
397 };
398
399 if (get_subscr_by_argv(vty, id_type, id, &subscr))
400 return CMD_WARNING;
401
402 rc = db_subscr_update_aud_by_id(g_hlr->dbc, subscr.id, &aud);
403
Harald Welte880a34d2018-03-01 21:32:01 +0100404 if (rc && rc != -ENOENT) {
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200405 vty_out(vty, "%% Error: cannot disable 2G auth data for IMSI='%s'%s",
406 subscr.imsi, VTY_NEWLINE);
407 return CMD_WARNING;
408 }
409 return CMD_SUCCESS;
410}
411
412DEFUN(subscriber_aud2g,
413 subscriber_aud2g_cmd,
414 SUBSCR_UPDATE "aud2g " AUTH_ALG_TYPES_2G " ki KI",
415 SUBSCR_UPDATE_HELP
416 "Set 2G authentication data\n"
417 AUTH_ALG_TYPES_2G_HELP
418 "Set Ki Encryption Key\n" "Ki as 32 hexadecimal characters\n")
419{
420 struct hlr_subscriber subscr;
421 int rc;
422 int minlen = 0;
423 int maxlen = 0;
424 const char *id_type = argv[0];
425 const char *id = argv[1];
426 const char *alg_type = argv[2];
427 const char *ki = argv[3];
428 struct sub_auth_data_str aud2g = {
429 .type = OSMO_AUTH_TYPE_GSM,
430 .u.gsm.ki = ki,
431 };
432
433 if (!auth_algo_parse(alg_type, &aud2g.algo, &minlen, &maxlen)) {
434 vty_out(vty, "%% Unknown auth algorithm: '%s'%s", alg_type, VTY_NEWLINE);
435 return CMD_WARNING;
436 }
437
438 if (!is_hexkey_valid(vty, "KI", aud2g.u.gsm.ki, minlen, maxlen))
439 return CMD_WARNING;
440
441 if (get_subscr_by_argv(vty, id_type, id, &subscr))
442 return CMD_WARNING;
443
444 rc = db_subscr_update_aud_by_id(g_hlr->dbc, subscr.id, &aud2g);
445
446 if (rc) {
447 vty_out(vty, "%% Error: cannot set 2G auth data for IMSI='%s'%s",
448 subscr.imsi, VTY_NEWLINE);
449 return CMD_WARNING;
450 }
451 return CMD_SUCCESS;
452}
453
454DEFUN(subscriber_no_aud3g,
455 subscriber_no_aud3g_cmd,
456 SUBSCR_UPDATE "aud3g none",
457 SUBSCR_UPDATE_HELP
458 "Set UMTS authentication data (3G, and 2G with UMTS AKA)\n"
459 "Delete 3G authentication data\n")
460{
461 struct hlr_subscriber subscr;
462 int rc;
463 const char *id_type = argv[0];
464 const char *id = argv[1];
465 struct sub_auth_data_str aud = {
466 .type = OSMO_AUTH_TYPE_UMTS,
467 .algo = OSMO_AUTH_ALG_NONE,
468 };
469
470 if (get_subscr_by_argv(vty, id_type, id, &subscr))
471 return CMD_WARNING;
472
473 rc = db_subscr_update_aud_by_id(g_hlr->dbc, subscr.id, &aud);
474
Harald Welte880a34d2018-03-01 21:32:01 +0100475 if (rc && rc != -ENOENT) {
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200476 vty_out(vty, "%% Error: cannot disable 3G auth data for IMSI='%s'%s",
477 subscr.imsi, VTY_NEWLINE);
478 return CMD_WARNING;
479 }
480 return CMD_SUCCESS;
481}
482
483DEFUN(subscriber_aud3g,
484 subscriber_aud3g_cmd,
485 SUBSCR_UPDATE "aud3g " AUTH_ALG_TYPES_3G
486 " k K"
487 " (op|opc) OP_C"
488 " [ind-bitlen] [<0-28>]",
489 SUBSCR_UPDATE_HELP
490 "Set UMTS authentication data (3G, and 2G with UMTS AKA)\n"
491 AUTH_ALG_TYPES_3G_HELP
492 "Set Encryption Key K\n" "K as 32 hexadecimal characters\n"
493 "Set OP key\n" "Set OPC key\n" "OP or OPC as 32 hexadecimal characters\n"
494 "Set IND bit length\n" "IND bit length value (default: 5)\n")
495{
496 struct hlr_subscriber subscr;
497 int minlen = 0;
498 int maxlen = 0;
499 int rc;
500 const char *id_type = argv[0];
501 const char *id = argv[1];
502 const char *alg_type = AUTH_ALG_TYPES_3G;
503 const char *k = argv[2];
504 bool opc_is_op = (strcasecmp("op", argv[3]) == 0);
505 const char *op_opc = argv[4];
506 int ind_bitlen = argc > 6? atoi(argv[6]) : 5;
507 struct sub_auth_data_str aud3g = {
508 .type = OSMO_AUTH_TYPE_UMTS,
509 .u.umts = {
510 .k = k,
511 .opc_is_op = opc_is_op,
512 .opc = op_opc,
513 .ind_bitlen = ind_bitlen,
514 },
515 };
516
517 if (!auth_algo_parse(alg_type, &aud3g.algo, &minlen, &maxlen)) {
518 vty_out(vty, "%% Unknown auth algorithm: '%s'%s", alg_type, VTY_NEWLINE);
519 return CMD_WARNING;
520 }
521
522 if (!is_hexkey_valid(vty, "K", aud3g.u.umts.k, minlen, maxlen))
523 return CMD_WARNING;
524
525 if (!is_hexkey_valid(vty, opc_is_op ? "OP" : "OPC", aud3g.u.umts.opc,
526 MILENAGE_KEY_LEN, MILENAGE_KEY_LEN))
527 return CMD_WARNING;
528
529 if (get_subscr_by_argv(vty, id_type, id, &subscr))
530 return CMD_WARNING;
531
532 rc = db_subscr_update_aud_by_id(g_hlr->dbc, subscr.id, &aud3g);
533
534 if (rc) {
535 vty_out(vty, "%% Error: cannot set 3G auth data for IMSI='%s'%s",
536 subscr.imsi, VTY_NEWLINE);
537 return CMD_WARNING;
538 }
539 return CMD_SUCCESS;
540}
541
Oliver Smith02078b72019-01-11 15:41:29 +0100542DEFUN(subscriber_imei,
543 subscriber_imei_cmd,
544 SUBSCR_UPDATE "imei (none|IMEI)",
545 SUBSCR_UPDATE_HELP
546 "Set IMEI of the subscriber (normally populated from MSC, no need to set this manually)\n"
547 "Forget IMEI\n"
548 "Set IMEI (use for debug only!)\n")
549{
550 struct hlr_subscriber subscr;
551 const char *id_type = argv[0];
552 const char *id = argv[1];
553 const char *imei = argv[2];
554 char imei_buf[GSM23003_IMEI_NUM_DIGITS_NO_CHK+1];
555
556 if (strcmp(imei, "none") == 0)
557 imei = NULL;
558 else {
559 /* Verify IMEI with checksum digit */
560 if (osmo_imei_str_valid(imei, true)) {
561 /* Cut the checksum off */
562 osmo_strlcpy(imei_buf, imei, sizeof(imei_buf));
563 imei = imei_buf;
564 } else if (!osmo_imei_str_valid(imei, false)) {
565 vty_out(vty, "%% IMEI invalid: '%s'%s", imei, VTY_NEWLINE);
566 return CMD_WARNING;
567 }
568 }
569
570 if (get_subscr_by_argv(vty, id_type, id, &subscr))
571 return CMD_WARNING;
572
573 if (db_subscr_update_imei_by_imsi(g_hlr->dbc, subscr.imsi, imei)) {
574 vty_out(vty, "%% Error: cannot update IMEI for subscriber IMSI='%s'%s",
575 subscr.imsi, VTY_NEWLINE);
576 return CMD_WARNING;
577 }
578
579 if (imei)
580 vty_out(vty, "%% Updated subscriber IMSI='%s' to IMEI='%s'%s",
581 subscr.imsi, imei, VTY_NEWLINE);
582 else
583 vty_out(vty, "%% Updated subscriber IMSI='%s': removed IMEI%s",
584 subscr.imsi, VTY_NEWLINE);
585
586 return CMD_SUCCESS;
587}
588
Oliver Smith3b33b012019-07-15 10:35:22 +0200589DEFUN(subscriber_nam,
590 subscriber_nam_cmd,
591 SUBSCR_UPDATE "network-access-mode (none|cs|ps|cs+ps)",
592 SUBSCR_UPDATE_HELP
593 "Set Network Access Mode (NAM) of the subscriber\n"
594 "Do not allow access to circuit switched or packet switched services\n"
595 "Allow access to circuit switched services only\n"
596 "Allow access to packet switched services only\n"
597 "Allow access to both circuit and packet switched services\n")
598{
599 struct hlr_subscriber subscr;
600 const char *id_type = argv[0];
601 const char *id = argv[1];
602 bool nam_cs = strstr(argv[2], "cs");
603 bool nam_ps = strstr(argv[2], "ps");
604
605 if (get_subscr_by_argv(vty, id_type, id, &subscr))
606 return CMD_WARNING;
607
608 if (nam_cs != subscr.nam_cs)
609 hlr_subscr_nam(g_hlr, &subscr, nam_cs, 0);
610 if (nam_ps != subscr.nam_ps)
611 hlr_subscr_nam(g_hlr, &subscr, nam_ps, 1);
612
613 return CMD_SUCCESS;
614}
615
Oliver Smith02078b72019-01-11 15:41:29 +0100616
Harald Welted5807b82018-07-29 12:27:41 +0200617void hlr_vty_subscriber_init(void)
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200618{
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200619 install_element_ve(&subscriber_show_cmd);
Neels Hofmeyr8aa780b2018-12-02 18:52:49 +0100620 install_element_ve(&show_subscriber_cmd);
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200621 install_element(ENABLE_NODE, &subscriber_create_cmd);
622 install_element(ENABLE_NODE, &subscriber_delete_cmd);
623 install_element(ENABLE_NODE, &subscriber_msisdn_cmd);
624 install_element(ENABLE_NODE, &subscriber_no_aud2g_cmd);
625 install_element(ENABLE_NODE, &subscriber_aud2g_cmd);
626 install_element(ENABLE_NODE, &subscriber_no_aud3g_cmd);
627 install_element(ENABLE_NODE, &subscriber_aud3g_cmd);
Oliver Smith02078b72019-01-11 15:41:29 +0100628 install_element(ENABLE_NODE, &subscriber_imei_cmd);
Oliver Smith3b33b012019-07-15 10:35:22 +0200629 install_element(ENABLE_NODE, &subscriber_nam_cmd);
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200630}