diff --git a/doc/jsonrpc.md b/doc/jsonrpc.md index 84dc27c6d..4115d3670 100644 --- a/doc/jsonrpc.md +++ b/doc/jsonrpc.md @@ -1919,6 +1919,93 @@ Example response: } ~~~ +## add_iscsi_auth_group method {#rpc_add_iscsi_auth_group} + +Add an authentication group for CHAP authentication. + +### Parameters + +Name | Optional | Type | Description +--------------------------- | -------- | --------| ----------- +tag | Required | number | Authentication group tag (unique, integer > 0) +secrets | Optional | array | Array of @ref rpc_add_iscsi_auth_group_secret objects + +### secret {#rpc_add_iscsi_auth_group_secret} + +Name | Optional | Type | Description +--------------------------- | ---------| --------| ----------- +user | Required | string | Unidirectional CHAP name +secret | Required | string | Unidirectional CHAP secret +muser | Optional | string | Bidirectional CHAP name +msecret | Optional | string | Bidirectional CHAP secret + +### Example + +Example request: + +~~~ +{ + "params": { + "secrets": [ + { + "muser": "mu1", + "secret": "s1", + "user": "u1", + "msecret": "ms1" + } + ], + "tag": 2 + }, + "jsonrpc": "2.0", + "method": "add_iscsi_auth_group", + "id": 1 +} +~~~ + +Example response: +~~~ +{ + "jsonrpc": "2.0", + "id": 1, + "result": true +} +~~~ + +## delete_iscsi_auth_group method {#rpc_delete_iscsi_auth_group} + +Delete an existing authentication group for CHAP authentication. + +### Parameters + +Name | Optional | Type | Description +--------------------------- | -------- | --------| ----------- +tag | Required | number | Authentication group tag (unique, integer > 0) + +### Example + +Example request: + +~~~ +{ + "params": { + "tag": 2 + }, + "jsonrpc": "2.0", + "method": "delete_iscsi_auth_group", + "id": 1 +} +~~~ + +Example response: + +~~~ +{ + "jsonrpc": "2.0", + "id": 1, + "result": true +} +~~~ + ## get_initiator_groups method {#rpc_get_initiator_groups} Show information about all available initiator groups. diff --git a/lib/iscsi/iscsi.h b/lib/iscsi/iscsi.h index 88b4cf301..0c7bfa10b 100644 --- a/lib/iscsi/iscsi.h +++ b/lib/iscsi/iscsi.h @@ -401,6 +401,12 @@ int spdk_iscsi_set_discovery_auth(bool disable_chap, bool require_chap, bool mutual_chap, int32_t chap_group); int spdk_iscsi_chap_get_authinfo(struct iscsi_chap_auth *auth, const char *authuser, int ag_tag); +int spdk_iscsi_add_auth_group(int32_t tag, struct spdk_iscsi_auth_group **_group); +struct spdk_iscsi_auth_group *spdk_iscsi_find_auth_group_by_tag(int32_t tag); +void spdk_iscsi_delete_auth_group(struct spdk_iscsi_auth_group *group); +int spdk_iscsi_auth_group_add_secret(struct spdk_iscsi_auth_group *group, + const char *user, const char *secret, + const char *muser, const char *msecret); void spdk_iscsi_send_nopin(struct spdk_iscsi_conn *conn); void spdk_iscsi_task_response(struct spdk_iscsi_conn *conn, diff --git a/lib/iscsi/iscsi_rpc.c b/lib/iscsi/iscsi_rpc.c index 15428265f..b396eaa2d 100644 --- a/lib/iscsi/iscsi_rpc.c +++ b/lib/iscsi/iscsi_rpc.c @@ -1086,7 +1086,7 @@ spdk_rpc_set_iscsi_target_node_auth(struct spdk_jsonrpc_request *request, req.mutual_chap, req.chap_group); if (rc < 0) { spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, - "Invalid combination of CHAP params"); + "Invalid combination of auth params"); free_rpc_target_auth(&req); return; } @@ -1174,3 +1174,191 @@ spdk_rpc_set_iscsi_discovery_auth(struct spdk_jsonrpc_request *request, spdk_jsonrpc_end_result(request, w); } SPDK_RPC_REGISTER("set_iscsi_discovery_auth", spdk_rpc_set_iscsi_discovery_auth, SPDK_RPC_RUNTIME) + + +#define MAX_AUTH_SECRETS 64 + +struct rpc_auth_secret { + char *user; + char *secret; + char *muser; + char *msecret; +}; + +static void +free_rpc_auth_secret(struct rpc_auth_secret *_secret) +{ + free(_secret->user); + free(_secret->secret); + free(_secret->muser); + free(_secret->msecret); +} + +static const struct spdk_json_object_decoder rpc_auth_secret_decoders[] = { + {"user", offsetof(struct rpc_auth_secret, user), spdk_json_decode_string}, + {"secret", offsetof(struct rpc_auth_secret, secret), spdk_json_decode_string}, + {"muser", offsetof(struct rpc_auth_secret, muser), spdk_json_decode_string, true}, + {"msecret", offsetof(struct rpc_auth_secret, msecret), spdk_json_decode_string, true}, +}; + +static int +decode_rpc_auth_secret(const struct spdk_json_val *val, void *out) +{ + struct rpc_auth_secret *_secret = out; + + return spdk_json_decode_object(val, rpc_auth_secret_decoders, + SPDK_COUNTOF(rpc_auth_secret_decoders), _secret); +} + +struct rpc_auth_secrets { + size_t num_secret; + struct rpc_auth_secret secrets[MAX_AUTH_SECRETS]; +}; + +static void +free_rpc_auth_secrets(struct rpc_auth_secrets *secrets) +{ + size_t i; + + for (i = 0; i < secrets->num_secret; i++) { + free_rpc_auth_secret(&secrets->secrets[i]); + } +} + +static int +decode_rpc_auth_secrets(const struct spdk_json_val *val, void *out) +{ + struct rpc_auth_secrets *secrets = out; + + return spdk_json_decode_array(val, decode_rpc_auth_secret, secrets->secrets, + MAX_AUTH_SECRETS, &secrets->num_secret, + sizeof(struct rpc_auth_secret)); +} + +struct rpc_auth_group { + int32_t tag; + struct rpc_auth_secrets secrets; +}; + +static void +free_rpc_auth_group(struct rpc_auth_group *group) +{ + free_rpc_auth_secrets(&group->secrets); +} + +static const struct spdk_json_object_decoder rpc_auth_group_decoders[] = { + {"tag", offsetof(struct rpc_auth_group, tag), spdk_json_decode_int32}, + {"secrets", offsetof(struct rpc_auth_group, secrets), decode_rpc_auth_secrets, true}, +}; + +static void +spdk_rpc_add_iscsi_auth_group(struct spdk_jsonrpc_request *request, + const struct spdk_json_val *params) +{ + struct rpc_auth_group req = {}; + struct rpc_auth_secret *_secret; + struct spdk_json_write_ctx *w; + struct spdk_iscsi_auth_group *group = NULL; + int rc; + size_t i; + + if (spdk_json_decode_object(params, rpc_auth_group_decoders, + SPDK_COUNTOF(rpc_auth_group_decoders), &req)) { + SPDK_ERRLOG("spdk_json_decode_object failed\n"); + spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, + "Invalid parameters"); + free_rpc_auth_group(&req); + return; + } + + pthread_mutex_lock(&g_spdk_iscsi.mutex); + + rc = spdk_iscsi_add_auth_group(req.tag, &group); + if (rc != 0) { + pthread_mutex_unlock(&g_spdk_iscsi.mutex); + + spdk_jsonrpc_send_error_response_fmt(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, + "Could not add auth group (%d), %s", + req.tag, spdk_strerror(-rc)); + free_rpc_auth_group(&req); + return; + } + + for (i = 0; i < req.secrets.num_secret; i++) { + _secret = &req.secrets.secrets[i]; + rc = spdk_iscsi_auth_group_add_secret(group, _secret->user, _secret->secret, + _secret->muser, _secret->msecret); + if (rc != 0) { + spdk_iscsi_delete_auth_group(group); + pthread_mutex_unlock(&g_spdk_iscsi.mutex); + + spdk_jsonrpc_send_error_response_fmt(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, + "Could not add secret to auth group (%d), %s", + req.tag, spdk_strerror(-rc)); + free_rpc_auth_group(&req); + return; + } + } + + pthread_mutex_unlock(&g_spdk_iscsi.mutex); + + free_rpc_auth_group(&req); + + w = spdk_jsonrpc_begin_result(request); + if (w == NULL) { + return; + } + + spdk_json_write_bool(w, true); + spdk_jsonrpc_end_result(request, w); +} +SPDK_RPC_REGISTER("add_iscsi_auth_group", spdk_rpc_add_iscsi_auth_group, SPDK_RPC_RUNTIME) + +struct rpc_delete_auth_group { + int32_t tag; +}; + +static const struct spdk_json_object_decoder rpc_delete_auth_group_decoders[] = { + {"tag", offsetof(struct rpc_delete_auth_group, tag), spdk_json_decode_int32}, +}; + +static void +spdk_rpc_delete_iscsi_auth_group(struct spdk_jsonrpc_request *request, + const struct spdk_json_val *params) +{ + struct rpc_delete_auth_group req = {}; + struct spdk_json_write_ctx *w; + struct spdk_iscsi_auth_group *group; + + if (spdk_json_decode_object(params, rpc_delete_auth_group_decoders, + SPDK_COUNTOF(rpc_delete_auth_group_decoders), &req)) { + SPDK_ERRLOG("spdk_json_decode_object failed\n"); + spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, + "Invalid parameters"); + return; + } + + pthread_mutex_lock(&g_spdk_iscsi.mutex); + + group = spdk_iscsi_find_auth_group_by_tag(req.tag); + if (group == NULL) { + pthread_mutex_unlock(&g_spdk_iscsi.mutex); + + spdk_jsonrpc_send_error_response_fmt(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, + "Could not find auth group (%d)", req.tag); + return; + } + + spdk_iscsi_delete_auth_group(group); + + pthread_mutex_unlock(&g_spdk_iscsi.mutex); + + w = spdk_jsonrpc_begin_result(request); + if (w == NULL) { + return; + } + + spdk_json_write_bool(w, true); + spdk_jsonrpc_end_result(request, w); +} +SPDK_RPC_REGISTER("delete_iscsi_auth_group", spdk_rpc_delete_iscsi_auth_group, SPDK_RPC_RUNTIME) diff --git a/lib/iscsi/iscsi_subsystem.c b/lib/iscsi/iscsi_subsystem.c index 4eb82da1d..044e9fabf 100644 --- a/lib/iscsi/iscsi_subsystem.c +++ b/lib/iscsi/iscsi_subsystem.c @@ -801,7 +801,7 @@ spdk_iscsi_set_discovery_auth(bool disable_chap, bool require_chap, bool mutual_ return 0; } -static int +int spdk_iscsi_auth_group_add_secret(struct spdk_iscsi_auth_group *group, const char *user, const char *secret, const char *muser, const char *msecret) @@ -874,7 +874,7 @@ spdk_iscsi_auth_group_add_secret(struct spdk_iscsi_auth_group *group, return 0; } -static int +int spdk_iscsi_add_auth_group(int32_t tag, struct spdk_iscsi_auth_group **_group) { struct spdk_iscsi_auth_group *group; @@ -901,7 +901,7 @@ spdk_iscsi_add_auth_group(int32_t tag, struct spdk_iscsi_auth_group **_group) return 0; } -static void +void spdk_iscsi_delete_auth_group(struct spdk_iscsi_auth_group *group) { struct spdk_iscsi_auth_secret *_secret, *tmp; @@ -915,6 +915,20 @@ spdk_iscsi_delete_auth_group(struct spdk_iscsi_auth_group *group) free(group); } +struct spdk_iscsi_auth_group * +spdk_iscsi_find_auth_group_by_tag(int32_t tag) +{ + struct spdk_iscsi_auth_group *group; + + TAILQ_FOREACH(group, &g_spdk_iscsi.auth_group_head, tailq) { + if (group->tag == tag) { + return group; + } + } + + return NULL; +} + static void spdk_iscsi_auth_groups_destroy(void) { @@ -1050,8 +1064,12 @@ spdk_iscsi_chap_get_authinfo(struct iscsi_chap_auth *auth, const char *authuser, memset(auth->msecret, 0, sizeof(auth->msecret)); } + pthread_mutex_lock(&g_spdk_iscsi.mutex); + _secret = spdk_iscsi_find_auth_secret(authuser, ag_tag); if (_secret == NULL) { + pthread_mutex_unlock(&g_spdk_iscsi.mutex); + SPDK_ERRLOG("CHAP secret is not found: user:%s, tag:%d\n", authuser, ag_tag); return -ENOENT; @@ -1065,6 +1083,7 @@ spdk_iscsi_chap_get_authinfo(struct iscsi_chap_auth *auth, const char *authuser, memcpy(auth->msecret, _secret->msecret, sizeof(auth->msecret)); } + pthread_mutex_unlock(&g_spdk_iscsi.mutex); return 0; } diff --git a/scripts/rpc.py b/scripts/rpc.py index c331e8225..26f5eecbd 100755 --- a/scripts/rpc.py +++ b/scripts/rpc.py @@ -551,6 +551,28 @@ if __name__ == "__main__": *** Authentication group must be precreated ***""", type=int) p.set_defaults(func=set_iscsi_discovery_auth) + def add_iscsi_auth_group(args): + secrets = None + if args.secrets: + secrets = [dict(u.split(":") for u in a.split(" ")) for a in args.secrets.split(",")] + + rpc.iscsi.add_iscsi_auth_group(args.client, tag=args.tag, secrets=secrets) + + p = subparsers.add_parser('add_iscsi_auth_group', help='Add authentication group for CHAP authentication.') + p.add_argument('tag', help='Authentication group tag (unique, integer > 0).', type=int) + p.add_argument('-c', '--secrets', help="""Comma-separated list of CHAP secrets + enclosed in quotes. +Format: 'user:u1 secret:s1 muser:mu1 msecret:ms1,user:u2 secret:s2 muser:mu2 msecret:ms2'""", required=False) + p.set_defaults(func=add_iscsi_auth_group) + + @call_cmd + def delete_iscsi_auth_group(args): + rpc.iscsi.delete_iscsi_auth_group(args.client, tag=args.tag) + + p = subparsers.add_parser('delete_iscsi_auth_group', help='Delete an authentication group.') + p.add_argument('tag', help='Authentication group tag', type=int) + p.set_defaults(func=delete_iscsi_auth_group) + @call_cmd def get_portal_groups(args): print_dict(rpc.iscsi.get_portal_groups(args.client)) diff --git a/scripts/rpc/iscsi.py b/scripts/rpc/iscsi.py index b4042671a..b725ca0e2 100755 --- a/scripts/rpc/iscsi.py +++ b/scripts/rpc/iscsi.py @@ -253,6 +253,36 @@ def set_iscsi_target_node_auth( return client.call('set_iscsi_target_node_auth', params) +def add_iscsi_auth_group(client, tag, secrets=None): + """Add authentication group for CHAP authentication. + + Args: + tag: Authentication group tag (unique, integer > 0). + secrets: Array of secrets objects (optional). + + Returns: + True or False + """ + params = {'tag': tag} + + if secrets: + params['secrets'] = secrets + return client.call('add_iscsi_auth_group', params) + + +def delete_iscsi_auth_group(client, tag): + """Delete an authentication group. + + Args: + tag: Authentication group tag (unique, integer > 0) + + Returns: + True or False + """ + params = {'tag': tag} + return client.call('delete_iscsi_auth_group', params) + + def delete_pg_ig_maps(client, pg_ig_maps, name): """Delete PG-IG maps from the target node.