blob: 3078577304ab5efd05a31f3844994c6c52a98736 [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
31#include "hlr.h"
32#include "db.h"
33
34struct vty;
35
36#define hexdump_buf(buf) osmo_hexdump_nospc((void*)buf, sizeof(buf))
37
Stefan Sperling5c14c9c2018-12-07 12:30:21 +010038static char *
39get_datestr(const time_t *t, char *datebuf)
40{
41 char *p, *s = ctime_r(t, datebuf);
42
43 /* Strip trailing newline. */
44 p = strchr(s, '\n');
45 if (p)
46 *p = '\0';
47 return s;
48}
49
Neels Hofmeyr183e7002017-10-06 02:59:54 +020050static void subscr_dump_full_vty(struct vty *vty, struct hlr_subscriber *subscr)
51{
52 int rc;
53 struct osmo_sub_auth_data aud2g;
54 struct osmo_sub_auth_data aud3g;
Stefan Sperling5c14c9c2018-12-07 12:30:21 +010055 char datebuf[26]; /* for ctime_r(3) */
Neels Hofmeyr183e7002017-10-06 02:59:54 +020056
57 vty_out(vty, " ID: %"PRIu64"%s", subscr->id, VTY_NEWLINE);
58
Neels Hofmeyr36bec872017-10-23 18:44:23 +020059 vty_out(vty, " IMSI: %s%s", *subscr->imsi ? subscr->imsi : "none", VTY_NEWLINE);
Neels Hofmeyr183e7002017-10-06 02:59:54 +020060 vty_out(vty, " MSISDN: %s%s", *subscr->msisdn ? subscr->msisdn : "none", VTY_NEWLINE);
Oliver Smith02078b72019-01-11 15:41:29 +010061
62 if (*subscr->imei) {
63 char checksum = osmo_luhn(subscr->imei, 14);
64 if (checksum == -EINVAL)
65 vty_out(vty, " IMEI: %s (INVALID LENGTH!)%s", subscr->imei, VTY_NEWLINE);
66 else
67 vty_out(vty, " IMEI: %s%c%s", subscr->imei, checksum, VTY_NEWLINE);
68 }
69
Neels Hofmeyr183e7002017-10-06 02:59:54 +020070 if (*subscr->vlr_number)
71 vty_out(vty, " VLR number: %s%s", subscr->vlr_number, VTY_NEWLINE);
72 if (*subscr->sgsn_number)
73 vty_out(vty, " SGSN number: %s%s", subscr->sgsn_number, VTY_NEWLINE);
74 if (*subscr->sgsn_address)
75 vty_out(vty, " SGSN address: %s%s", subscr->sgsn_address, VTY_NEWLINE);
76 if (subscr->periodic_lu_timer)
77 vty_out(vty, " Periodic LU timer: %u%s", subscr->periodic_lu_timer, VTY_NEWLINE);
78 if (subscr->periodic_rau_tau_timer)
79 vty_out(vty, " Periodic RAU/TAU timer: %u%s", subscr->periodic_rau_tau_timer, VTY_NEWLINE);
80 if (subscr->lmsi)
81 vty_out(vty, " LMSI: %x%s", subscr->lmsi, VTY_NEWLINE);
82 if (!subscr->nam_cs)
83 vty_out(vty, " CS disabled%s", VTY_NEWLINE);
84 if (subscr->ms_purged_cs)
85 vty_out(vty, " CS purged%s", VTY_NEWLINE);
86 if (!subscr->nam_ps)
87 vty_out(vty, " PS disabled%s", VTY_NEWLINE);
88 if (subscr->ms_purged_ps)
89 vty_out(vty, " PS purged%s", VTY_NEWLINE);
Stefan Sperling5c14c9c2018-12-07 12:30:21 +010090 if (subscr->last_lu_seen)
91 vty_out(vty, " last LU seen: %s UTC%s", get_datestr(&subscr->last_lu_seen, datebuf), VTY_NEWLINE);
Neels Hofmeyr183e7002017-10-06 02:59:54 +020092
93 if (!*subscr->imsi)
94 return;
95
96 OSMO_ASSERT(g_hlr);
97 rc = db_get_auth_data(g_hlr->dbc, subscr->imsi, &aud2g, &aud3g, NULL);
98
Neels Hofmeyrbd1dca02017-11-23 15:25:30 +010099 switch (rc) {
100 case 0:
101 break;
102 case -ENOENT:
103 case -ENOKEY:
104 aud2g.algo = OSMO_AUTH_ALG_NONE;
105 aud3g.algo = OSMO_AUTH_ALG_NONE;
106 break;
107 default:
108 vty_out(vty, "%% Error retrieving data from database (%d)%s", rc, VTY_NEWLINE);
109 return;
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200110 }
111
112 if (aud2g.type != OSMO_AUTH_TYPE_NONE && aud2g.type != OSMO_AUTH_TYPE_GSM) {
113 vty_out(vty, "%% Error: 2G auth data is not of type 'GSM'%s", VTY_NEWLINE);
114 aud2g = (struct osmo_sub_auth_data){};
115 }
116
117 if (aud3g.type != OSMO_AUTH_TYPE_NONE && aud3g.type != OSMO_AUTH_TYPE_UMTS) {
118 vty_out(vty, "%% Error: 3G auth data is not of type 'UMTS'%s", VTY_NEWLINE);
119 aud3g = (struct osmo_sub_auth_data){};
120 }
121
122 if (aud2g.algo != OSMO_AUTH_ALG_NONE && aud2g.type != OSMO_AUTH_TYPE_NONE) {
123 vty_out(vty, " 2G auth: %s%s",
124 osmo_auth_alg_name(aud2g.algo), VTY_NEWLINE);
125 vty_out(vty, " KI=%s%s",
126 hexdump_buf(aud2g.u.gsm.ki), VTY_NEWLINE);
127 }
128
129 if (aud3g.algo != OSMO_AUTH_ALG_NONE && aud3g.type != OSMO_AUTH_TYPE_NONE) {
130 vty_out(vty, " 3G auth: %s%s", osmo_auth_alg_name(aud3g.algo), VTY_NEWLINE);
131 vty_out(vty, " K=%s%s", hexdump_buf(aud3g.u.umts.k), VTY_NEWLINE);
132 vty_out(vty, " %s=%s%s", aud3g.u.umts.opc_is_op? "OP" : "OPC",
133 hexdump_buf(aud3g.u.umts.opc), VTY_NEWLINE);
134 vty_out(vty, " IND-bitlen=%u", aud3g.u.umts.ind_bitlen);
135 if (aud3g.u.umts.sqn)
136 vty_out(vty, " last-SQN=%"PRIu64, aud3g.u.umts.sqn);
137 vty_out(vty, VTY_NEWLINE);
138 }
139}
140
141static int get_subscr_by_argv(struct vty *vty, const char *type, const char *id, struct hlr_subscriber *subscr)
142{
Oliver Smith02078b72019-01-11 15:41:29 +0100143 char imei_buf[GSM23003_IMEI_NUM_DIGITS_NO_CHK+1];
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200144 int rc = -1;
145 if (strcmp(type, "imsi") == 0)
146 rc = db_subscr_get_by_imsi(g_hlr->dbc, id, subscr);
147 else if (strcmp(type, "msisdn") == 0)
148 rc = db_subscr_get_by_msisdn(g_hlr->dbc, id, subscr);
149 else if (strcmp(type, "id") == 0)
150 rc = db_subscr_get_by_id(g_hlr->dbc, atoll(id), subscr);
Oliver Smith02078b72019-01-11 15:41:29 +0100151 else if (strcmp(type, "imei") == 0) {
152 /* Verify IMEI with checksum digit */
153 if (osmo_imei_str_valid(id, true)) {
154 /* Cut the checksum off */
155 osmo_strlcpy(imei_buf, id, sizeof(imei_buf));
156 id = imei_buf;
157 vty_out(vty, "%% Checksum validated and stripped for search: imei = '%s'%s", id,
158 VTY_NEWLINE);
159 }
160 rc = db_subscr_get_by_imei(g_hlr->dbc, id, subscr);
161 }
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200162 if (rc)
163 vty_out(vty, "%% No subscriber for %s = '%s'%s",
164 type, id, VTY_NEWLINE);
165 return rc;
166}
167
168#define SUBSCR_CMD "subscriber "
169#define SUBSCR_CMD_HELP "Subscriber management commands\n"
170
Oliver Smith02078b72019-01-11 15:41:29 +0100171#define SUBSCR_ID "(imsi|msisdn|id|imei) IDENT"
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200172#define SUBSCR_ID_HELP \
173 "Identify subscriber by IMSI\n" \
174 "Identify subscriber by MSISDN (phone number)\n" \
175 "Identify subscriber by database ID\n" \
Oliver Smith02078b72019-01-11 15:41:29 +0100176 "Identify subscriber by IMEI\n" \
177 "IMSI/MSISDN/ID/IMEI of the subscriber\n"
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200178
Neels Hofmeyr8aa780b2018-12-02 18:52:49 +0100179#define SUBSCR SUBSCR_CMD SUBSCR_ID " "
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200180#define SUBSCR_HELP SUBSCR_CMD_HELP SUBSCR_ID_HELP
181
182#define SUBSCR_UPDATE SUBSCR "update "
183#define SUBSCR_UPDATE_HELP SUBSCR_HELP "Set or update subscriber data\n"
Neels Hofmeyra820ea12018-12-02 19:46:46 +0100184#define SUBSCR_MSISDN_HELP "Set MSISDN (phone number) of the subscriber\n"
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200185
186DEFUN(subscriber_show,
187 subscriber_show_cmd,
188 SUBSCR "show",
189 SUBSCR_HELP "Show subscriber information\n")
190{
191 struct hlr_subscriber subscr;
192 const char *id_type = argv[0];
193 const char *id = argv[1];
194
195 if (get_subscr_by_argv(vty, id_type, id, &subscr))
196 return CMD_WARNING;
197
198 subscr_dump_full_vty(vty, &subscr);
199 return CMD_SUCCESS;
200}
201
Neels Hofmeyr8aa780b2018-12-02 18:52:49 +0100202ALIAS(subscriber_show, show_subscriber_cmd,
203 "show " SUBSCR_CMD SUBSCR_ID,
204 SHOW_STR SUBSCR_CMD_HELP SUBSCR_ID_HELP);
205
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200206DEFUN(subscriber_create,
207 subscriber_create_cmd,
208 SUBSCR_CMD "imsi IDENT create",
209 SUBSCR_CMD_HELP
Vadim Yanitskiyf473c7b2018-07-30 14:29:39 +0700210 "Identify subscriber by IMSI\n"
211 "IMSI/MSISDN/ID of the subscriber\n"
212 "Create subscriber by IMSI\n")
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200213{
214 int rc;
215 struct hlr_subscriber subscr;
216 const char *imsi = argv[0];
217
218 if (!osmo_imsi_str_valid(imsi)) {
219 vty_out(vty, "%% Not a valid IMSI: %s%s", imsi, VTY_NEWLINE);
220 return CMD_WARNING;
221 }
222
Oliver Smithcd2af5e2019-03-06 13:17:39 +0100223 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 +0200224
225 if (rc) {
226 if (rc == -EEXIST)
227 vty_out(vty, "%% Subscriber already exists for IMSI = %s%s",
228 imsi, VTY_NEWLINE);
229 else
230 vty_out(vty, "%% Error (rc=%d): cannot create subscriber for IMSI = %s%s",
231 rc, imsi, VTY_NEWLINE);
232 return CMD_WARNING;
233 }
234
235 rc = db_subscr_get_by_imsi(g_hlr->dbc, imsi, &subscr);
236 vty_out(vty, "%% Created subscriber %s%s", imsi, VTY_NEWLINE);
237
238 subscr_dump_full_vty(vty, &subscr);
239
240 return CMD_SUCCESS;
241}
242
243DEFUN(subscriber_delete,
244 subscriber_delete_cmd,
245 SUBSCR "delete",
246 SUBSCR_HELP "Delete subscriber from database\n")
247{
248 struct hlr_subscriber subscr;
249 int rc;
250 const char *id_type = argv[0];
251 const char *id = argv[1];
252
253 /* Find out the IMSI regardless of which way the caller decided to
254 * identify the subscriber by. */
255 if (get_subscr_by_argv(vty, id_type, id, &subscr))
256 return CMD_WARNING;
257
258 rc = db_subscr_delete_by_id(g_hlr->dbc, subscr.id);
259 if (rc) {
260 vty_out(vty, "%% Error: Failed to remove subscriber for IMSI '%s'%s",
261 subscr.imsi, VTY_NEWLINE);
262 return CMD_WARNING;
263 }
264
265 vty_out(vty, "%% Deleted subscriber for IMSI '%s'%s", subscr.imsi, VTY_NEWLINE);
266 return CMD_SUCCESS;
267}
268
269DEFUN(subscriber_msisdn,
270 subscriber_msisdn_cmd,
Neels Hofmeyra820ea12018-12-02 19:46:46 +0100271 SUBSCR_UPDATE "msisdn (none|MSISDN)",
272 SUBSCR_UPDATE_HELP SUBSCR_MSISDN_HELP
273 "Remove MSISDN (phone number)\n"
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200274 "New MSISDN (phone number)\n")
275{
276 struct hlr_subscriber subscr;
277 const char *id_type = argv[0];
278 const char *id = argv[1];
279 const char *msisdn = argv[2];
280
Neels Hofmeyra820ea12018-12-02 19:46:46 +0100281 if (strcmp(msisdn, "none") == 0)
282 msisdn = NULL;
283 else {
284 if (strlen(msisdn) > sizeof(subscr.msisdn) - 1) {
285 vty_out(vty, "%% MSISDN is too long, max. %zu characters are allowed%s",
286 sizeof(subscr.msisdn)-1, VTY_NEWLINE);
287 return CMD_WARNING;
288 }
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200289
Neels Hofmeyra820ea12018-12-02 19:46:46 +0100290 if (!osmo_msisdn_str_valid(msisdn)) {
291 vty_out(vty, "%% MSISDN invalid: '%s'%s", msisdn, VTY_NEWLINE);
292 return CMD_WARNING;
293 }
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200294 }
295
296 if (get_subscr_by_argv(vty, id_type, id, &subscr))
297 return CMD_WARNING;
298
299 if (db_subscr_update_msisdn_by_imsi(g_hlr->dbc, subscr.imsi, msisdn)) {
300 vty_out(vty, "%% Error: cannot update MSISDN for subscriber IMSI='%s'%s",
301 subscr.imsi, VTY_NEWLINE);
302 return CMD_WARNING;
303 }
304
Neels Hofmeyra820ea12018-12-02 19:46:46 +0100305 if (msisdn) {
306 vty_out(vty, "%% Updated subscriber IMSI='%s' to MSISDN='%s'%s",
307 subscr.imsi, msisdn, VTY_NEWLINE);
Stefan Sperlingf1622522018-04-09 11:39:16 +0200308
Neels Hofmeyra820ea12018-12-02 19:46:46 +0100309 if (db_subscr_get_by_msisdn(g_hlr->dbc, msisdn, &subscr) == 0)
310 osmo_hlr_subscriber_update_notify(&subscr);
311 } else {
312 vty_out(vty, "%% Updated subscriber IMSI='%s': removed MSISDN%s",
313 subscr.imsi, VTY_NEWLINE);
314
Stefan Sperlingf1622522018-04-09 11:39:16 +0200315 osmo_hlr_subscriber_update_notify(&subscr);
Neels Hofmeyra820ea12018-12-02 19:46:46 +0100316 }
Stefan Sperlingf1622522018-04-09 11:39:16 +0200317
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200318 return CMD_SUCCESS;
319}
320
321static bool is_hexkey_valid(struct vty *vty, const char *label,
322 const char *hex_str, int minlen, int maxlen)
323{
324 if (osmo_is_hexstr(hex_str, minlen * 2, maxlen * 2, true))
325 return true;
326 vty_out(vty, "%% Invalid value for %s: '%s'%s", label, hex_str, VTY_NEWLINE);
327 return false;
328}
329
330#define AUTH_ALG_TYPES_2G "(comp128v1|comp128v2|comp128v3|xor)"
331#define AUTH_ALG_TYPES_2G_HELP \
332 "Use COMP128v1 algorithm\n" \
333 "Use COMP128v2 algorithm\n" \
334 "Use COMP128v3 algorithm\n" \
335 "Use XOR algorithm\n"
336
337#define AUTH_ALG_TYPES_3G "milenage"
338#define AUTH_ALG_TYPES_3G_HELP \
339 "Use Milenage algorithm\n"
340
341#define A38_XOR_MIN_KEY_LEN 12
342#define A38_XOR_MAX_KEY_LEN 16
343#define A38_COMP128_KEY_LEN 16
344
345#define MILENAGE_KEY_LEN 16
346
347static bool auth_algo_parse(const char *alg_str, enum osmo_auth_algo *algo,
348 int *minlen, int *maxlen)
349{
350 if (!strcasecmp(alg_str, "none")) {
351 *algo = OSMO_AUTH_ALG_NONE;
352 *minlen = *maxlen = 0;
353 } else if (!strcasecmp(alg_str, "comp128v1")) {
354 *algo = OSMO_AUTH_ALG_COMP128v1;
355 *minlen = *maxlen = A38_COMP128_KEY_LEN;
356 } else if (!strcasecmp(alg_str, "comp128v2")) {
357 *algo = OSMO_AUTH_ALG_COMP128v2;
358 *minlen = *maxlen = A38_COMP128_KEY_LEN;
359 } else if (!strcasecmp(alg_str, "comp128v3")) {
360 *algo = OSMO_AUTH_ALG_COMP128v3;
361 *minlen = *maxlen = A38_COMP128_KEY_LEN;
362 } else if (!strcasecmp(alg_str, "xor")) {
363 *algo = OSMO_AUTH_ALG_XOR;
364 *minlen = A38_XOR_MIN_KEY_LEN;
365 *maxlen = A38_XOR_MAX_KEY_LEN;
366 } else if (!strcasecmp(alg_str, "milenage")) {
367 *algo = OSMO_AUTH_ALG_MILENAGE;
368 *minlen = *maxlen = MILENAGE_KEY_LEN;
369 } else
370 return false;
371 return true;
372}
373
374DEFUN(subscriber_no_aud2g,
375 subscriber_no_aud2g_cmd,
376 SUBSCR_UPDATE "aud2g none",
377 SUBSCR_UPDATE_HELP
378 "Set 2G authentication data\n"
379 "Delete 2G authentication data\n")
380{
381 struct hlr_subscriber subscr;
382 int rc;
383 const char *id_type = argv[0];
384 const char *id = argv[1];
385 struct sub_auth_data_str aud = {
386 .type = OSMO_AUTH_TYPE_GSM,
387 .algo = OSMO_AUTH_ALG_NONE,
388 };
389
390 if (get_subscr_by_argv(vty, id_type, id, &subscr))
391 return CMD_WARNING;
392
393 rc = db_subscr_update_aud_by_id(g_hlr->dbc, subscr.id, &aud);
394
Harald Welte880a34d2018-03-01 21:32:01 +0100395 if (rc && rc != -ENOENT) {
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200396 vty_out(vty, "%% Error: cannot disable 2G auth data for IMSI='%s'%s",
397 subscr.imsi, VTY_NEWLINE);
398 return CMD_WARNING;
399 }
400 return CMD_SUCCESS;
401}
402
403DEFUN(subscriber_aud2g,
404 subscriber_aud2g_cmd,
405 SUBSCR_UPDATE "aud2g " AUTH_ALG_TYPES_2G " ki KI",
406 SUBSCR_UPDATE_HELP
407 "Set 2G authentication data\n"
408 AUTH_ALG_TYPES_2G_HELP
409 "Set Ki Encryption Key\n" "Ki as 32 hexadecimal characters\n")
410{
411 struct hlr_subscriber subscr;
412 int rc;
413 int minlen = 0;
414 int maxlen = 0;
415 const char *id_type = argv[0];
416 const char *id = argv[1];
417 const char *alg_type = argv[2];
418 const char *ki = argv[3];
419 struct sub_auth_data_str aud2g = {
420 .type = OSMO_AUTH_TYPE_GSM,
421 .u.gsm.ki = ki,
422 };
423
424 if (!auth_algo_parse(alg_type, &aud2g.algo, &minlen, &maxlen)) {
425 vty_out(vty, "%% Unknown auth algorithm: '%s'%s", alg_type, VTY_NEWLINE);
426 return CMD_WARNING;
427 }
428
429 if (!is_hexkey_valid(vty, "KI", aud2g.u.gsm.ki, minlen, maxlen))
430 return CMD_WARNING;
431
432 if (get_subscr_by_argv(vty, id_type, id, &subscr))
433 return CMD_WARNING;
434
435 rc = db_subscr_update_aud_by_id(g_hlr->dbc, subscr.id, &aud2g);
436
437 if (rc) {
438 vty_out(vty, "%% Error: cannot set 2G auth data for IMSI='%s'%s",
439 subscr.imsi, VTY_NEWLINE);
440 return CMD_WARNING;
441 }
442 return CMD_SUCCESS;
443}
444
445DEFUN(subscriber_no_aud3g,
446 subscriber_no_aud3g_cmd,
447 SUBSCR_UPDATE "aud3g none",
448 SUBSCR_UPDATE_HELP
449 "Set UMTS authentication data (3G, and 2G with UMTS AKA)\n"
450 "Delete 3G authentication data\n")
451{
452 struct hlr_subscriber subscr;
453 int rc;
454 const char *id_type = argv[0];
455 const char *id = argv[1];
456 struct sub_auth_data_str aud = {
457 .type = OSMO_AUTH_TYPE_UMTS,
458 .algo = OSMO_AUTH_ALG_NONE,
459 };
460
461 if (get_subscr_by_argv(vty, id_type, id, &subscr))
462 return CMD_WARNING;
463
464 rc = db_subscr_update_aud_by_id(g_hlr->dbc, subscr.id, &aud);
465
Harald Welte880a34d2018-03-01 21:32:01 +0100466 if (rc && rc != -ENOENT) {
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200467 vty_out(vty, "%% Error: cannot disable 3G auth data for IMSI='%s'%s",
468 subscr.imsi, VTY_NEWLINE);
469 return CMD_WARNING;
470 }
471 return CMD_SUCCESS;
472}
473
474DEFUN(subscriber_aud3g,
475 subscriber_aud3g_cmd,
476 SUBSCR_UPDATE "aud3g " AUTH_ALG_TYPES_3G
477 " k K"
478 " (op|opc) OP_C"
479 " [ind-bitlen] [<0-28>]",
480 SUBSCR_UPDATE_HELP
481 "Set UMTS authentication data (3G, and 2G with UMTS AKA)\n"
482 AUTH_ALG_TYPES_3G_HELP
483 "Set Encryption Key K\n" "K as 32 hexadecimal characters\n"
484 "Set OP key\n" "Set OPC key\n" "OP or OPC as 32 hexadecimal characters\n"
485 "Set IND bit length\n" "IND bit length value (default: 5)\n")
486{
487 struct hlr_subscriber subscr;
488 int minlen = 0;
489 int maxlen = 0;
490 int rc;
491 const char *id_type = argv[0];
492 const char *id = argv[1];
493 const char *alg_type = AUTH_ALG_TYPES_3G;
494 const char *k = argv[2];
495 bool opc_is_op = (strcasecmp("op", argv[3]) == 0);
496 const char *op_opc = argv[4];
497 int ind_bitlen = argc > 6? atoi(argv[6]) : 5;
498 struct sub_auth_data_str aud3g = {
499 .type = OSMO_AUTH_TYPE_UMTS,
500 .u.umts = {
501 .k = k,
502 .opc_is_op = opc_is_op,
503 .opc = op_opc,
504 .ind_bitlen = ind_bitlen,
505 },
506 };
507
508 if (!auth_algo_parse(alg_type, &aud3g.algo, &minlen, &maxlen)) {
509 vty_out(vty, "%% Unknown auth algorithm: '%s'%s", alg_type, VTY_NEWLINE);
510 return CMD_WARNING;
511 }
512
513 if (!is_hexkey_valid(vty, "K", aud3g.u.umts.k, minlen, maxlen))
514 return CMD_WARNING;
515
516 if (!is_hexkey_valid(vty, opc_is_op ? "OP" : "OPC", aud3g.u.umts.opc,
517 MILENAGE_KEY_LEN, MILENAGE_KEY_LEN))
518 return CMD_WARNING;
519
520 if (get_subscr_by_argv(vty, id_type, id, &subscr))
521 return CMD_WARNING;
522
523 rc = db_subscr_update_aud_by_id(g_hlr->dbc, subscr.id, &aud3g);
524
525 if (rc) {
526 vty_out(vty, "%% Error: cannot set 3G auth data for IMSI='%s'%s",
527 subscr.imsi, VTY_NEWLINE);
528 return CMD_WARNING;
529 }
530 return CMD_SUCCESS;
531}
532
Oliver Smith02078b72019-01-11 15:41:29 +0100533DEFUN(subscriber_imei,
534 subscriber_imei_cmd,
535 SUBSCR_UPDATE "imei (none|IMEI)",
536 SUBSCR_UPDATE_HELP
537 "Set IMEI of the subscriber (normally populated from MSC, no need to set this manually)\n"
538 "Forget IMEI\n"
539 "Set IMEI (use for debug only!)\n")
540{
541 struct hlr_subscriber subscr;
542 const char *id_type = argv[0];
543 const char *id = argv[1];
544 const char *imei = argv[2];
545 char imei_buf[GSM23003_IMEI_NUM_DIGITS_NO_CHK+1];
546
547 if (strcmp(imei, "none") == 0)
548 imei = NULL;
549 else {
550 /* Verify IMEI with checksum digit */
551 if (osmo_imei_str_valid(imei, true)) {
552 /* Cut the checksum off */
553 osmo_strlcpy(imei_buf, imei, sizeof(imei_buf));
554 imei = imei_buf;
555 } else if (!osmo_imei_str_valid(imei, false)) {
556 vty_out(vty, "%% IMEI invalid: '%s'%s", imei, VTY_NEWLINE);
557 return CMD_WARNING;
558 }
559 }
560
561 if (get_subscr_by_argv(vty, id_type, id, &subscr))
562 return CMD_WARNING;
563
564 if (db_subscr_update_imei_by_imsi(g_hlr->dbc, subscr.imsi, imei)) {
565 vty_out(vty, "%% Error: cannot update IMEI for subscriber IMSI='%s'%s",
566 subscr.imsi, VTY_NEWLINE);
567 return CMD_WARNING;
568 }
569
570 if (imei)
571 vty_out(vty, "%% Updated subscriber IMSI='%s' to IMEI='%s'%s",
572 subscr.imsi, imei, VTY_NEWLINE);
573 else
574 vty_out(vty, "%% Updated subscriber IMSI='%s': removed IMEI%s",
575 subscr.imsi, VTY_NEWLINE);
576
577 return CMD_SUCCESS;
578}
579
580
Harald Welted5807b82018-07-29 12:27:41 +0200581void hlr_vty_subscriber_init(void)
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200582{
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200583 install_element_ve(&subscriber_show_cmd);
Neels Hofmeyr8aa780b2018-12-02 18:52:49 +0100584 install_element_ve(&show_subscriber_cmd);
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200585 install_element(ENABLE_NODE, &subscriber_create_cmd);
586 install_element(ENABLE_NODE, &subscriber_delete_cmd);
587 install_element(ENABLE_NODE, &subscriber_msisdn_cmd);
588 install_element(ENABLE_NODE, &subscriber_no_aud2g_cmd);
589 install_element(ENABLE_NODE, &subscriber_aud2g_cmd);
590 install_element(ENABLE_NODE, &subscriber_no_aud3g_cmd);
591 install_element(ENABLE_NODE, &subscriber_aud3g_cmd);
Oliver Smith02078b72019-01-11 15:41:29 +0100592 install_element(ENABLE_NODE, &subscriber_imei_cmd);
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200593}