diff --git a/iscsi-scst/README b/iscsi-scst/README index ad4f72dff..4517ab358 100644 --- a/iscsi-scst/README +++ b/iscsi-scst/README @@ -153,6 +153,20 @@ is /sys/kernel/scst_tgt/targets/iscsi. It has the following entries: "AccessControl" attribute, which allows to enable iSNS access control. Empty by default. + - allowed_portal[num] - optional attribute, which specifies, on which + portals (target's IP addresses) this target will be available. If not + specified (default) the target will be available on all all portals. + As soon as at least one allowed_portal specified, the target will be + accessible for initiators only on the specified portals. There might + be any number of the allowed_portal attributes. The portals + specification in the allowed_portal attributes can be a simple + DOS-type patterns, containing '*' and '?' symbols. '*' means match + all any symbols, '?' means match only any single symbol. For + instance, "10.170.77.2" will match "10.170.7?.*". Additionally, you + can use negative sign '!' to revert the value of the pattern. For + instance, "10.170.67.2" will match "!10.170.7?.*". See examples + below. + - enabled - using this attribute you can enable or disable iSCSI-SCST accept new connections. It allows to finish configuring global iSCSI-SCST attributes before it starts accepting new connections. 0 @@ -161,6 +175,15 @@ is /sys/kernel/scst_tgt/targets/iscsi. It has the following entries: - open_state - read-only attribute, which allows to see if the user space part of iSCSI-SCST connected to the kernel part. + - per_portal_acl - if set, makes iSCSI-SCST work in the per-portal + access control mode. In this mode iSCSI-SCST registers all initiators + in SCST core as "initiator_name#portal_IP_address" pattern, like + "iqn.2006-10.net.vlnb:ini#10.170.77.2" for initiator + iqn.2006-10.net.vlnb connected through portal 10.170.77.2. This mode + allows to make particular initiators be able to use only particular + portals on the target and don't see/be able to connect through + others. See below for more details. + - trace_level - allows to enable and disable various tracing facilities. See content of this file for help how to use it. @@ -482,6 +505,7 @@ both iSCSI-SCST targets will look like: | | | | |-- device -> ../../../../../devices/cdrom | | | | `-- read_only | | | `-- mgmt +| | |-- per_portal_acl | | |-- rel_tgt_id | | |-- sessions | | | `-- iqn.2005-03.org.open-iscsi:cacdcd2520 @@ -542,6 +566,7 @@ both iSCSI-SCST targets will look like: | | | | |-- device -> ../../../../../devices/nullio | | | | `-- read_only | | | `-- mgmt +| | |-- per_portal_acl | | |-- rel_tgt_id | | |-- sessions | | | `-- iqn.2005-03.org.open-iscsi:cacdcd2520 @@ -575,6 +600,92 @@ both iSCSI-SCST targets will look like: `-- version +Advanced initiators access control +---------------------------------- + +ISCSI-SCST allows you to optionally control visibility and accessibility +of your target and its portals (IP addresses) to remote initiators. This +control includes both the target's portals SendTargets discovery as well +as regular LUNs access. + +This facility supersedes the obsolete initiators.[allow,deny] method, +which is going to be removed in one of the future versions. + +This facility is available only in the sysfs build of iSCSI-SCST. + +By default, all portals are available for the initiators. + +1. If you want to enable/disable one or more target's portals for all +initiators, you should define one ore more allowed_portal attributes. +For example: + +echo 'add_target_attribute iqn.2006-10.net.vlnb:tgt allowed_portal 10.170.77.2' >/sys/kernel/scst_tgt/targets/iscsi/mgmt + +will enable only portal 10.170.77.2 and disable all other portals + +echo 'add_target_attribute iqn.2006-10.net.vlnb:tgt allowed_portal 10.170.77.2' >/sys/kernel/scst_tgt/targets/iscsi/mgmt +echo 'add_target_attribute iqn.2006-10.net.vlnb:tgt allowed_portal 10.170.75.2' >/sys/kernel/scst_tgt/targets/iscsi/mgmt + +will enable only portals 10.170.77.2 and 10.170.75.2 and disable all +other portals. + +echo 'add_target_attribute iqn.2006-10.net.vlnb:tgt allowed_portal 10.170.7?.2' >/sys/kernel/scst_tgt/targets/iscsi/mgmt + +will enable only portals 10.170.7x.2 and disable all other portals. + +echo 'add_target_attribute iqn.2006-10.net.vlnb:tgt allowed_portal !*' >/sys/kernel/scst_tgt/targets/iscsi/mgmt + +will disable all portals. + +2. If you want to want to allow only only specific set of initiators be +able to connect to your target, you should don't add any default LUNs +for the target and create for allowed initiators a security group to +which they will be assigned. + +For example, we want initiator iqn.2005-03.org.vlnb:cacdcd2520 and only +it be able to access target iqn.2006-10.net.vlnb:tgt: + +echo 'add_target iqn.2006-10.net.vlnb:tgt' >/sys/kernel/scst_tgt/targets/iscsi/mgmt +echo 'create allowed_ini' >/sys/kernel/scst_tgt/targets/iscsi/iqn.2006-10.net.vlnb:tgt/ini_groups/mgmt +echo 'add dev1 0' >/sys/kernel/scst_tgt/targets/iscsi/iqn.2006-10.net.vlnb:tgt/ini_groups/allowed_ini/luns/mgmt +echo 'add iqn.2005-03.org.vlnb:cacdcd2520' >/sys/kernel/scst_tgt/targets/iscsi/iqn.2006-10.net.vlnb:tgt/ini_groups/allowed_ini/initiators/mgmt +echo 1 >/sys/kernel/scst_tgt/targets/iscsi/iqn.2006-10.net.vlnb:tgt/enabled + +Since there will be no default LUNs for the target, all initiators other +than iqn.2005-03.org.vlnb:cacdcd2520 will be blocked from accessing it. + +Alternatively, you can create an empty security group and filter out in +it all initiators except the allowed one: + +echo 'add_target iqn.2006-10.net.vlnb:tgt' >/sys/kernel/scst_tgt/targets/iscsi/mgmt +echo 'add dev1 0' >/sys/kernel/scst_tgt/targets/iscsi/iqn.2006-10.net.vlnb:tgt/luns/mgmt +echo 'create denied_inis' >/sys/kernel/scst_tgt/targets/iscsi/iqn.2006-10.net.vlnb:tgt/ini_groups/mgmt +echo 'add !iqn.2005-03.org.vlnb:cacdcd2520' >/sys/kernel/scst_tgt/targets/iscsi/iqn.2006-10.net.vlnb:tgt/ini_groups/denied_inis/initiators/mgmt +echo 1 >/sys/kernel/scst_tgt/targets/iscsi/iqn.2006-10.net.vlnb:tgt/enabled + +3. If you want to enable/disable one or more target's portals for +particular initiators, you should set per_portal_acl attribute to 1 and +specify SCST access control to those initiators. If an SCST security +group doesn't have any LUNs, all the initiator, which should be assigned +to it, will not see this target and/or its portal. For example: + +(We assume that an empty group "BLOCKING_GROUP" is already created by for +target iqn.2006-10.net.vlnb:tgt by command (see above for more information): +"echo 'create BLOCKING_GROUP' >/sys/kernel/scst_tgt/targets/iscsi/iqn.2006-10.net.vlnb:tgt/ini_groups/mgmt) + +echo 'add iqn.2005-03.org.vlnb:cacdcd2520#10.170.77.2' >/sys/kernel/scst_tgt/targets/iscsi/iqn.2006-10.net.vlnb:tgt/ini_groups/BLOCKING_GROUP/initiators/mgmt + +will block access of initiator iqn.2005-03.org.vlnb:cacdcd2520 to +target iqn.2006-10.net.vlnb:tgt portal 10.170.77.2. + +Another example: + +echo 'add iqn.2005-03.org.vlnb:cacdcd2520*' >/sys/kernel/scst_tgt/targets/iscsi/iqn.2006-10.net.vlnb:tgt/ini_groups/BLOCKING_GROUP/initiators/mgmt + +will block access of initiator iqn.2005-03.org.vlnb:cacdcd2520 to +all target iqn.2006-10.net.vlnb:tgt portals. + + Troubleshooting --------------- diff --git a/iscsi-scst/README_in-tree b/iscsi-scst/README_in-tree index d51594888..1e44f158a 100644 --- a/iscsi-scst/README_in-tree +++ b/iscsi-scst/README_in-tree @@ -50,6 +50,20 @@ is /sys/kernel/scst_tgt/targets/iscsi. It has the following entries: "AccessControl" attribute, which allows to enable iSNS access control. Empty by default. + - allowed_portal[num] - optional attribute, which specifies, on which + portals (target's IP addresses) this target will be available. If not + specified (default) the target will be available on all all portals. + As soon as at least one allowed_portal specified, the target will be + accessible for initiators only on the specified portals. There might + be any number of the allowed_portal attributes. The portals + specification in the allowed_portal attributes can be a simple + DOS-type patterns, containing '*' and '?' symbols. '*' means match + all any symbols, '?' means match only any single symbol. For + instance, "10.170.77.2" will match "10.170.7?.*". Additionally, you + can use negative sign '!' to revert the value of the pattern. For + instance, "10.170.67.2" will match "!10.170.7?.*". See examples + below. + - enabled - using this attribute you can enable or disable iSCSI-SCST accept new connections. It allows to finish configuring global iSCSI-SCST attributes before it starts accepting new connections. 0 @@ -58,6 +72,15 @@ is /sys/kernel/scst_tgt/targets/iscsi. It has the following entries: - open_state - read-only attribute, which allows to see if the user space part of iSCSI-SCST connected to the kernel part. + - per_portal_acl - if set, makes iSCSI-SCST work in the per-portal + access control mode. In this mode iSCSI-SCST registers all initiators + in SCST core as "initiator_name#portal_IP_address" pattern, like + "iqn.2006-10.net.vlnb:ini#10.170.77.2" for initiator + iqn.2006-10.net.vlnb connected through portal 10.170.77.2. This mode + allows to make particular initiators be able to use only particular + portals on the target and don't see/be able to connect through + others. See below for more details. + - trace_level - allows to enable and disable various tracing facilities. See content of this file for help how to use it. @@ -379,6 +402,7 @@ both iSCSI-SCST targets will look like: | | | | |-- device -> ../../../../../devices/cdrom | | | | `-- read_only | | | `-- mgmt +| | |-- per_portal_acl | | |-- rel_tgt_id | | |-- sessions | | | `-- iqn.2005-03.org.open-iscsi:cacdcd2520 @@ -439,6 +463,7 @@ both iSCSI-SCST targets will look like: | | | | |-- device -> ../../../../../devices/nullio | | | | `-- read_only | | | `-- mgmt +| | |-- per_portal_acl | | |-- rel_tgt_id | | |-- sessions | | | `-- iqn.2005-03.org.open-iscsi:cacdcd2520 @@ -472,6 +497,92 @@ both iSCSI-SCST targets will look like: `-- version +Advanced initiators access control +---------------------------------- + +ISCSI-SCST allows you to optionally control visibility and accessibility +of your target and its portals (IP addresses) to remote initiators. This +control includes both the target's portals SendTargets discovery as well +as regular LUNs access. + +This facility supersedes the obsolete initiators.[allow,deny] method, +which is going to be removed in one of the future versions. + +This facility is available only in the sysfs build of iSCSI-SCST. + +By default, all portals are available for the initiators. + +1. If you want to enable/disable one or more target's portals for all +initiators, you should define one ore more allowed_portal attributes. +For example: + +echo 'add_target_attribute iqn.2006-10.net.vlnb:tgt allowed_portal 10.170.77.2' >/sys/kernel/scst_tgt/targets/iscsi/mgmt + +will enable only portal 10.170.77.2 and disable all other portals + +echo 'add_target_attribute iqn.2006-10.net.vlnb:tgt allowed_portal 10.170.77.2' >/sys/kernel/scst_tgt/targets/iscsi/mgmt +echo 'add_target_attribute iqn.2006-10.net.vlnb:tgt allowed_portal 10.170.75.2' >/sys/kernel/scst_tgt/targets/iscsi/mgmt + +will enable only portals 10.170.77.2 and 10.170.75.2 and disable all +other portals. + +echo 'add_target_attribute iqn.2006-10.net.vlnb:tgt allowed_portal 10.170.7?.2' >/sys/kernel/scst_tgt/targets/iscsi/mgmt + +will enable only portals 10.170.7x.2 and disable all other portals. + +echo 'add_target_attribute iqn.2006-10.net.vlnb:tgt allowed_portal !*' >/sys/kernel/scst_tgt/targets/iscsi/mgmt + +will disable all portals. + +2. If you want to want to allow only only specific set of initiators be +able to connect to your target, you should don't add any default LUNs +for the target and create for allowed initiators a security group to +which they will be assigned. + +For example, we want initiator iqn.2005-03.org.vlnb:cacdcd2520 and only +it be able to access target iqn.2006-10.net.vlnb:tgt: + +echo 'add_target iqn.2006-10.net.vlnb:tgt' >/sys/kernel/scst_tgt/targets/iscsi/mgmt +echo 'create allowed_ini' >/sys/kernel/scst_tgt/targets/iscsi/iqn.2006-10.net.vlnb:tgt/ini_groups/mgmt +echo 'add dev1 0' >/sys/kernel/scst_tgt/targets/iscsi/iqn.2006-10.net.vlnb:tgt/ini_groups/allowed_ini/luns/mgmt +echo 'add iqn.2005-03.org.vlnb:cacdcd2520' >/sys/kernel/scst_tgt/targets/iscsi/iqn.2006-10.net.vlnb:tgt/ini_groups/allowed_ini/initiators/mgmt +echo 1 >/sys/kernel/scst_tgt/targets/iscsi/iqn.2006-10.net.vlnb:tgt/enabled + +Since there will be no default LUNs for the target, all initiators other +than iqn.2005-03.org.vlnb:cacdcd2520 will be blocked from accessing it. + +Alternatively, you can create an empty security group and filter out in +it all initiators except the allowed one: + +echo 'add_target iqn.2006-10.net.vlnb:tgt' >/sys/kernel/scst_tgt/targets/iscsi/mgmt +echo 'add dev1 0' >/sys/kernel/scst_tgt/targets/iscsi/iqn.2006-10.net.vlnb:tgt/luns/mgmt +echo 'create denied_inis' >/sys/kernel/scst_tgt/targets/iscsi/iqn.2006-10.net.vlnb:tgt/ini_groups/mgmt +echo 'add !iqn.2005-03.org.vlnb:cacdcd2520' >/sys/kernel/scst_tgt/targets/iscsi/iqn.2006-10.net.vlnb:tgt/ini_groups/denied_inis/initiators/mgmt +echo 1 >/sys/kernel/scst_tgt/targets/iscsi/iqn.2006-10.net.vlnb:tgt/enabled + +3. If you want to enable/disable one or more target's portals for +particular initiators, you should set per_portal_acl attribute to 1 and +specify SCST access control to those initiators. If an SCST security +group doesn't have any LUNs, all the initiator, which should be assigned +to it, will not see this target and/or its portal. For example: + +(We assume that an empty group "BLOCKING_GROUP" is already created by for +target iqn.2006-10.net.vlnb:tgt by command (see above for more information): +"echo 'create BLOCKING_GROUP' >/sys/kernel/scst_tgt/targets/iscsi/iqn.2006-10.net.vlnb:tgt/ini_groups/mgmt) + +echo 'add iqn.2005-03.org.vlnb:cacdcd2520#10.170.77.2' >/sys/kernel/scst_tgt/targets/iscsi/iqn.2006-10.net.vlnb:tgt/ini_groups/BLOCKING_GROUP/initiators/mgmt + +will block access of initiator iqn.2005-03.org.vlnb:cacdcd2520 to +target iqn.2006-10.net.vlnb:tgt portal 10.170.77.2. + +Another example: + +echo 'add iqn.2005-03.org.vlnb:cacdcd2520*' >/sys/kernel/scst_tgt/targets/iscsi/iqn.2006-10.net.vlnb:tgt/ini_groups/BLOCKING_GROUP/initiators/mgmt + +will block access of initiator iqn.2005-03.org.vlnb:cacdcd2520 to +all target iqn.2006-10.net.vlnb:tgt portals. + + Troubleshooting --------------- diff --git a/iscsi-scst/include/iscsi_scst.h b/iscsi-scst/include/iscsi_scst.h index df9422d8c..3f119e649 100644 --- a/iscsi-scst/include/iscsi_scst.h +++ b/iscsi-scst/include/iscsi_scst.h @@ -26,6 +26,11 @@ /* The maximum length of 223 bytes in the RFC. */ #define ISCSI_NAME_LEN 256 +#define ISCSI_PORTAL_LEN 64 + +/* Full name is iSCSI name + connected portal */ +#define ISCSI_FULL_NAME_LEN (ISCSI_NAME_LEN + ISCSI_PORTAL_LEN) + #define ISCSI_LISTEN_PORT 3260 #define SCSI_ID_LEN 24 @@ -88,6 +93,7 @@ struct iscsi_kern_session_info { #ifdef CONFIG_SCST_PROC char user_name[ISCSI_NAME_LEN]; #endif + char full_initiator_name[ISCSI_FULL_NAME_LEN]; u32 exp_cmd_sn; s32 session_params[session_key_last]; s32 target_params[target_key_last]; @@ -171,6 +177,11 @@ struct iscsi_kern_attr_info { struct iscsi_kern_attr attr; }; +struct iscsi_kern_initiator_info { + u32 tid; + char full_initiator_name[ISCSI_FULL_NAME_LEN]; +}; + #define DEFAULT_NR_QUEUED_CMNDS 32 #define MIN_NR_QUEUED_CMNDS 1 #define MAX_NR_QUEUED_CMNDS 256 @@ -201,6 +212,8 @@ struct iscsi_kern_attr_info { #define MGMT_CMD_CALLBACK _IOW('s', 11, struct iscsi_kern_mgmt_cmd_res_info) #endif +#define ISCSI_INITIATOR_ALLOWED _IOW('s', 12, struct iscsi_kern_initiator_info) + static inline int iscsi_is_key_internal(int key) { switch (key) { diff --git a/iscsi-scst/kernel/config.c b/iscsi-scst/kernel/config.c index dc0d01ade..0d2b03ea3 100644 --- a/iscsi-scst/kernel/config.c +++ b/iscsi-scst/kernel/config.c @@ -414,6 +414,7 @@ static int add_session(void __user *ptr) #ifdef CONFIG_SCST_PROC info->user_name[sizeof(info->user_name)-1] = '\0'; #endif + info->full_initiator_name[sizeof(info->full_initiator_name)-1] = '\0'; target = target_lookup_by_id(info->tid); if (target == NULL) { @@ -523,6 +524,39 @@ out: return err; } +/* target_mgmt_mutex supposed to be locked */ +static int iscsi_initiator_allowed(void __user *ptr) +{ + int err = 0, rc; + struct iscsi_kern_initiator_info cinfo; + struct iscsi_target *target; + + TRACE_ENTRY(); + + rc = copy_from_user(&cinfo, ptr, sizeof(cinfo)); + if (rc != 0) { + PRINT_ERROR("Failed to copy %d user's bytes", rc); + err = -EFAULT; + goto out; + } + + cinfo.full_initiator_name[sizeof(cinfo.full_initiator_name)-1] = '\0'; + + target = target_lookup_by_id(cinfo.tid); + if (target == NULL) { + PRINT_ERROR("Target %d not found", cinfo.tid); + err = -ENOENT; + goto out; + } + + err = scst_initiator_has_luns(target->scst_tgt, + cinfo.full_initiator_name); + +out: + TRACE_EXIT_RES(err); + return err; +} + #ifndef CONFIG_SCST_PROC /* target_mgmt_mutex supposed to be locked */ @@ -1055,6 +1089,10 @@ static long ioctl(struct file *file, unsigned int cmd, unsigned long arg) break; #endif + case ISCSI_INITIATOR_ALLOWED: + err = iscsi_initiator_allowed((void __user *)arg); + break; + case ADD_SESSION: err = add_session((void __user *)arg); break; diff --git a/iscsi-scst/kernel/iscsi.c b/iscsi-scst/kernel/iscsi.c index 0a7030ee5..dac5d69de 100644 --- a/iscsi-scst/kernel/iscsi.c +++ b/iscsi-scst/kernel/iscsi.c @@ -3574,7 +3574,9 @@ static struct scst_trace_log iscsi_local_trace_tbl[] = { " echo \"add_target_attribute target_name IncomingUser name password\" >mgmt\n" \ " echo \"del_target_attribute target_name IncomingUser name\" >mgmt\n" \ " echo \"add_target_attribute target_name OutgoingUser name password\" >mgmt\n" \ - " echo \"del_target_attribute target_name OutgoingUser name\" >mgmt\n" + " echo \"del_target_attribute target_name OutgoingUser name\" >mgmt\n" \ + " echo \"add_target_attribute target_name allowed_portal portal_mask\" >mgmt\n" \ + " echo \"del_target_attribute target_name allowed_portal portal_mask\" >mgmt\n" struct scst_tgt_template iscsi_template = { .name = "iscsi", diff --git a/iscsi-scst/kernel/session.c b/iscsi-scst/kernel/session.c index e186f0caf..436df90f8 100644 --- a/iscsi-scst/kernel/session.c +++ b/iscsi-scst/kernel/session.c @@ -66,7 +66,7 @@ static int iscsi_session_alloc(struct iscsi_target *target, else sprintf(name, "%s", info->initiator_name); #else - name = info->initiator_name; + name = info->full_initiator_name; #endif INIT_LIST_HEAD(&session->conn_list); diff --git a/iscsi-scst/usr/chap.c b/iscsi-scst/usr/chap.c index f4ddc0e2c..b86163660 100644 --- a/iscsi-scst/usr/chap.c +++ b/iscsi-scst/usr/chap.c @@ -494,7 +494,7 @@ static int chap_target_auth_create_response(struct connection *conn) u8 *challenge = NULL, *digest = NULL; int encoding_format, response_len; int challenge_len = 0, digest_len = 0, retval = 0; - struct iscsi_user *user; + struct iscsi_attr *user; if (!(value = text_key_find(conn, "CHAP_I"))) { /* Initiator doesn't want target auth!? */ @@ -588,12 +588,12 @@ static int chap_target_auth_create_response(struct connection *conn) switch (conn->auth.chap.digest_alg) { case CHAP_DIGEST_ALG_MD5: - chap_calc_digest_md5(chap_id, user->password, strlen(user->password), - challenge, challenge_len, digest); + chap_calc_digest_md5(chap_id, ISCSI_USER_PASS(user), + strlen(ISCSI_USER_PASS(user)), challenge, challenge_len, digest); break; case CHAP_DIGEST_ALG_SHA1: - chap_calc_digest_sha1(chap_id, user->password, strlen(user->password), - challenge, challenge_len, digest); + chap_calc_digest_sha1(chap_id, ISCSI_USER_PASS(user), + strlen(ISCSI_USER_PASS(user)), challenge, challenge_len, digest); break; default: retval = CHAP_TARGET_ERROR; @@ -602,7 +602,7 @@ static int chap_target_auth_create_response(struct connection *conn) memset(response, 0x0, response_len); chap_encode_string(digest, digest_len, response, encoding_format); - text_key_add(conn, "CHAP_N", user->name); + text_key_add(conn, "CHAP_N", ISCSI_USER_NAME(user)); text_key_add(conn, "CHAP_R", response); conn->state = STATE_SECURITY_DONE; diff --git a/iscsi-scst/usr/config.c b/iscsi-scst/usr/config.c index 030e7a49e..e7920189e 100644 --- a/iscsi-scst/usr/config.c +++ b/iscsi-scst/usr/config.c @@ -35,7 +35,10 @@ #define BUFSIZE 4096 #define CONFIG_FILE "/etc/iscsi-scstd.conf" -/* Index must match ISCSI_USER_DIR_*!! */ +/* + * Index must match ISCSI_USER_DIR_* and ISCSI_USER_DIR_OUTGOING must + * be the last to confirm expectations of __config_account_add()!! + */ struct iscsi_key user_keys[] = { {"IncomingUser",}, {"OutgoingUser",}, @@ -45,6 +48,187 @@ struct iscsi_key user_keys[] = { static struct __qelem discovery_users_in = LIST_HEAD_INIT(discovery_users_in); static struct __qelem discovery_users_out = LIST_HEAD_INIT(discovery_users_out); +struct iscsi_attr *iscsi_attr_lookup_by_sysfs_name( + struct __qelem *attrs_list, const char *sysfs_name) +{ + struct iscsi_attr *attr; + + list_for_each_entry(attr, attrs_list, ulist) { + if (!strcmp(attr->sysfs_name, sysfs_name)) + return attr; + } + + return NULL; +} + +struct iscsi_attr *iscsi_attr_lookup_by_key( + struct __qelem *attrs_list, const char *key) +{ + struct iscsi_attr *attr; + + list_for_each_entry(attr, attrs_list, ulist) { + if (!strcmp(attr->attr_key, key)) + return attr; + } + + return NULL; +} + +static void __iscsi_attr_destroy(struct iscsi_attr *attr, int del) +{ + if (!attr) + return; + + if (del) + list_del(&attr->ulist); + + free((void *)attr->attr_key); + free((void *)attr->attr_value); + free(attr); + return; +} + +void iscsi_attr_destroy(struct iscsi_attr *attr) +{ + __iscsi_attr_destroy(attr, 1); +} + +void iscsi_attrs_free(struct __qelem *attrs_list) +{ + struct iscsi_attr *attr, *t; + + list_for_each_entry_safe(attr, t, attrs_list, ulist) { + iscsi_attr_destroy(attr); + } + + return; +} + +int iscsi_attr_create(int attr_size, struct __qelem *attrs_list, + const char *sysfs_name_tmpl, const char *key, const char *val, + u32 mode, struct iscsi_attr **res_attr) +{ + int res = 0; + struct iscsi_attr *attr; + int num = 0; + + if (iscsi_attr_lookup_by_key(attrs_list, key) != NULL) { + log_error("Attribute %s already exists", key); + res = -EINVAL; + goto out; + } + + attr = malloc(attr_size); + if (attr == NULL) + goto out; + + memset(attr, 0, attr_size); + + attr->attr_key = strdup(key); + if (attr->attr_key == NULL) { + log_error("Unable to duplicate attr_key %s", key); + res = -ENOMEM; + goto out_free; + } + + if ((val != NULL) && (*val != '\0')) { + attr->attr_value = strdup(val); + if (attr->attr_value == NULL) { + log_error("Unable to duplicate attr_value %s", val); + res = -ENOMEM; + goto out_free; + } + } + + attr->sysfs_mode = mode; + + if (sysfs_name_tmpl == NULL) + goto add; + + if (sysfs_name_tmpl != NULL) { + strlcpy(attr->sysfs_name, sysfs_name_tmpl, sizeof(attr->sysfs_name)); + if (iscsi_attr_lookup_by_sysfs_name(attrs_list, attr->sysfs_name) == NULL) + goto add; + } + + while (1) { + if (num == 0) + num++; + snprintf(attr->sysfs_name, sizeof(attr->sysfs_name), + "%s%d", sysfs_name_tmpl, num); + if (iscsi_attr_lookup_by_sysfs_name(attrs_list, attr->sysfs_name) == NULL) + break; + num++; + } + +add: + list_add_tail(attr, attrs_list); + + *res_attr = attr; + +out: + return res; + +out_free: + __iscsi_attr_destroy(attr, 0); + goto out; +} + +int iscsi_attr_replace(struct __qelem *attrs_list, const char *sysfs_name, + char *raw_value) +{ + int res = 0; + struct iscsi_attr *attr, *a; + char *key, *val, *new_key, *new_val; + + key = config_sep_string(&raw_value); + val = config_sep_string(&raw_value); + + attr = iscsi_attr_lookup_by_sysfs_name(attrs_list, sysfs_name); + if (attr == NULL) { + log_error("Unknown parameter %s\n", sysfs_name); + res = -EINVAL; + goto out; + } + + a = iscsi_attr_lookup_by_key(attrs_list, key); + + list_add_tail(attr, attrs_list); + + if ((a != NULL) && (a != attr)) { + log_error("Attr %s (sysfs_name %s) already exists\n", key, + a->sysfs_name); + res = -EEXIST; + goto out; + } + + new_key = strdup(key); + if (new_key == NULL) { + log_error("Unable to duplicate attr_key %s", key); + res = -ENOMEM; + goto out; + } + + if ((val != NULL) && (*val != '\0')) { + new_val = strdup(val); + if (new_val == NULL) { + log_error("Unable to duplicate attr_value %s", val); + res = -ENOMEM; + goto out; + } + } else + new_val = NULL; + + free((void *)attr->attr_key); + attr->attr_key = new_key; + + free((void *)attr->attr_value); + attr->attr_value = new_val; + +out: + return res; +} + static struct __qelem *account_list_get(struct target *target, int dir) { struct __qelem *list = NULL; @@ -117,24 +301,17 @@ int accounts_empty(u32 tid, int dir) return list_empty(list); } -static struct iscsi_user *__account_lookup_by_name(struct target *target, +static struct iscsi_attr *__account_lookup_by_name(struct target *target, int dir, const char *name) { struct __qelem *list; - struct iscsi_user *user = NULL; list = account_list_get(target, dir); - list_for_each_entry(user, list, ulist) { - if (!strcmp(user->name, name)) - return user; - } - - return NULL; + return iscsi_attr_lookup_by_key(list, name); } - -static struct iscsi_user *account_lookup_by_name(u32 tid, int dir, const char *name) +static struct iscsi_attr *account_lookup_by_name(u32 tid, int dir, const char *name) { struct target *target; @@ -148,11 +325,11 @@ static struct iscsi_user *account_lookup_by_name(u32 tid, int dir, const char *n return __account_lookup_by_name(target, dir, name); } -struct iscsi_user *account_get_first(u32 tid, int dir) +struct iscsi_attr *account_get_first(u32 tid, int dir) { struct target *target; struct __qelem *list; - struct iscsi_user *user = NULL; + struct iscsi_attr *user; if (tid) { target = target_find_by_id(tid); @@ -170,30 +347,24 @@ struct iscsi_user *account_get_first(u32 tid, int dir) return NULL; } -struct iscsi_user *account_lookup_by_sysfs_name(struct target *target, +struct iscsi_attr *account_lookup_by_sysfs_name(struct target *target, int dir, const char *sysfs_name) { struct __qelem *list; - struct iscsi_user *user = NULL; list = account_list_get(target, dir); - list_for_each_entry(user, list, ulist) { - if (!strcmp(user->sysfs_name, sysfs_name)) - return user; - } - - return NULL; + return iscsi_attr_lookup_by_sysfs_name(list, sysfs_name); } int config_account_query(u32 tid, int dir, const char *name, char *pass) { - struct iscsi_user *user; + struct iscsi_attr *user; if (!(user = account_lookup_by_name(tid, dir, name))) return -ENOENT; - strlcpy(pass, user->password, ISCSI_NAME_LEN); + strlcpy(pass, ISCSI_USER_PASS(user), ISCSI_NAME_LEN); return 0; } @@ -203,7 +374,7 @@ int config_account_list(u32 tid, int dir, u32 *cnt, u32 *overflow, { struct target *target; struct __qelem *list; - struct iscsi_user *user; + struct iscsi_attr *user; *cnt = *overflow = 0; @@ -221,7 +392,7 @@ int config_account_list(u32 tid, int dir, u32 *cnt, u32 *overflow, list_for_each_entry(user, list, ulist) { if (buf_sz >= ISCSI_NAME_LEN) { - strlcpy(buf, user->name, ISCSI_NAME_LEN); + strlcpy(buf, ISCSI_USER_NAME(user), ISCSI_NAME_LEN); buf_sz -= ISCSI_NAME_LEN; buf += ISCSI_NAME_LEN; *cnt += 1; @@ -232,33 +403,23 @@ int config_account_list(u32 tid, int dir, u32 *cnt, u32 *overflow, return 0; } -static void account_destroy(struct iscsi_user *user, int del) +static void account_destroy(struct iscsi_attr *user) { - if (!user) - return; - if (del) - list_del(&user->ulist); - free((void *)user->name); - free((void *)user->password); - free(user); + iscsi_attr_destroy(user); return; } void accounts_free(struct __qelem *accounts_list) { - struct iscsi_user *user, *t; - - list_for_each_entry_safe(user, t, accounts_list, ulist) { - account_destroy(user, 1); - } - + iscsi_attrs_free(accounts_list); return; } int config_account_del(u32 tid, int dir, char *name, u32 cookie) { - struct iscsi_user *user; + struct iscsi_attr *user; int res = 0; + struct target *target; if (!name) { log_error("%s", "Name expected"); @@ -266,7 +427,17 @@ int config_account_del(u32 tid, int dir, char *name, u32 cookie) goto out; } - user = account_lookup_by_name(tid, dir, name); + if (tid) { + target = target_find_by_id(tid); + if (target == NULL) { + log_error("Target %d not found", tid); + res = -ESRCH; + goto out; + } + } else + target = NULL; + + user = __account_lookup_by_name(target, dir, name); if (user == NULL) { log_error("User %s not found", name); res = -ENOENT; @@ -274,133 +445,42 @@ int config_account_del(u32 tid, int dir, char *name, u32 cookie) } #ifndef CONFIG_SCST_PROC - res = kernel_user_del(user, cookie); + res = kernel_user_del(target, user, cookie); if (res != 0) goto out; #endif - account_destroy(user, 1); + account_destroy(user); out: return res; } -static struct iscsi_user *account_create(struct target *target, int direction, - const char *sysfs_name, const char *name, const char *pass) +static int account_create(struct __qelem *list, const char *sysfs_name, + const char *name, const char *pass, struct iscsi_attr **res_user) { - struct iscsi_user *user; - - if (!(user = malloc(sizeof(*user)))) - return NULL; - - memset(user, 0, sizeof(*user)); - INIT_LIST_HEAD(&user->ulist); - user->target = target; - user->direction = direction; - - if (!(user->name = strdup(name)) || - !(user->password = strdup(pass))) { - log_error("Unable to duplicate name (%s) or password (%s)", - name, pass); - goto out_destroy; - } - - if (direction == ISCSI_USER_DIR_INCOMING) { - int inc_user_num = 0; - - if (sysfs_name != NULL) { - strlcpy(user->sysfs_name, sysfs_name, sizeof(user->sysfs_name)); - if (account_lookup_by_sysfs_name(target, direction, sysfs_name) == NULL) - goto out; - } - - while (1) { - if (inc_user_num == 0) - snprintf(user->sysfs_name, sizeof(user->sysfs_name), - "IncomingUser"); - else - snprintf(user->sysfs_name, sizeof(user->sysfs_name), - "IncomingUser%d", inc_user_num); - if (account_lookup_by_sysfs_name(target, direction, user->sysfs_name) == NULL) - break; - inc_user_num++; - } - } else - snprintf(user->sysfs_name, sizeof(user->sysfs_name), - "OutgoingUser"); - -out: - return user; - -out_destroy: - account_destroy(user, 0); - user = NULL; - goto out; + return iscsi_attr_create(sizeof(struct iscsi_attr), list, sysfs_name, + name, pass, 0600, res_user); } int account_replace(struct target *target, int direction, const char *sysfs_name, char *value) { - int res = 0; - struct iscsi_user *user, *user1; - char *name, *pass, *n; struct __qelem *list; - name = config_sep_string(&value); - pass = config_sep_string(&value); - - n = config_sep_string(&value); - if (*n != '\0') { - log_error("Unexpected parameter value %s\n", n); - res = -EINVAL; - goto out; - } - - user = account_lookup_by_sysfs_name(target, direction, sysfs_name); - if (user == NULL) { - log_error("Unknown parameter %s\n", sysfs_name); - res = -EINVAL; - goto out; - } - - user1 = __account_lookup_by_name(target, direction, name); - if ((user1 != NULL) && (user1 != user)) { - log_error("User %s already exists\n", name); - res = -EEXIST; - goto out; - } - list = account_list_get(target, direction); - list_del(&user->ulist); - - user1 = account_create(target, direction, sysfs_name, name, pass); - if (user1 == NULL) { - res = -ENOMEM; - goto out_add; - } - - list_add_tail(user1, list); - - account_destroy(user, 0); - -out: - return res; - -out_add: - list_add_tail(user, list); - goto out; + return iscsi_attr_replace(list, sysfs_name, value); } int __config_account_add(struct target *target, int dir, char *name, char *pass, char *sysfs_name, int send_to_kern, u32 cookie) { int err = 0; - struct iscsi_user *user; + struct iscsi_attr *user; struct __qelem *list; - int del = 0; - if (!name || !pass) { + if (!name || !pass || (*name == '\0') || (*pass == '\0')) { log_error("%s", "Name or password is NULL"); err = -EINVAL; goto out; @@ -414,42 +494,41 @@ int __config_account_add(struct target *target, int dir, char *name, goto out; } - user = account_create(target, dir, sysfs_name, name, pass); - if (user == NULL) { - err = -ENOMEM; + if (dir > ISCSI_USER_DIR_OUTGOING) { + log_error("Wrong direction %d\n", dir); + err = -EINVAL; goto out; } - if (__account_lookup_by_name(target, dir, name) != NULL) { - log_error("User %s already exists for target %s (direction %s)", - name, target ? target->name : "discovery", - (dir == ISCSI_USER_DIR_OUTGOING) ? "outgoing" : "incoming"); - err = -EEXIST; - goto out_destroy; - } + if (sysfs_name == NULL) + sysfs_name = (dir == ISCSI_USER_DIR_OUTGOING) ? + user_keys[ISCSI_USER_DIR_OUTGOING].name : + user_keys[ISCSI_USER_DIR_INCOMING].name; list = account_list_get(target, dir); + if (dir == ISCSI_USER_DIR_OUTGOING) { - struct iscsi_user *old; + struct iscsi_attr *old; list_for_each_entry(old, list, ulist) { log_warning("Only one outgoing %s account is " "supported. Replacing the old one.\n", target ? "target" : "discovery"); - account_destroy(old, 1); + account_destroy(old); break; } } - log_debug(1, "User %s added to target %s (direction %s)", user->name, + err = account_create(list, sysfs_name, name, pass, &user); + if (err != 0) + goto out; + + log_debug(1, "User %s added to target %s (direction %s)", ISCSI_USER_NAME(user), target ? target->name : "discovery", (dir == ISCSI_USER_DIR_OUTGOING) ? "outgoing" : "incoming"); - list_add_tail(user, list); - del = 1; - #ifndef CONFIG_SCST_PROC - if (send_to_kern) { - err = kernel_user_add(user, cookie); + if (send_to_kern && (sysfs_name != NULL)) { + err = kernel_user_add(target, user, cookie); if (err != 0) goto out_destroy; } @@ -458,8 +537,10 @@ int __config_account_add(struct target *target, int dir, char *name, out: return err; +#ifndef CONFIG_SCST_PROC out_destroy: - account_destroy(user, del); +#endif + account_destroy(user); goto out; } @@ -659,13 +740,13 @@ static int initiator_match(u32 tid, int fd, char *filename) return err; } -int config_initiator_access(u32 tid, int fd) +int config_initiator_access_allowed(u32 tid, int fd) { if (initiator_match(tid, fd, "/etc/initiators.deny") && !initiator_match(tid, fd, "/etc/initiators.allow")) - return -EPERM; - else return 0; + else + return 1; } /* diff --git a/iscsi-scst/usr/conn.c b/iscsi-scst/usr/conn.c index cd039ab09..94b5ce632 100644 --- a/iscsi-scst/usr/conn.c +++ b/iscsi-scst/usr/conn.c @@ -54,6 +54,7 @@ void conn_free(struct connection *conn) { list_del(&conn->clist); free(conn->initiator); + free(conn->target_portal); free(conn->user); free(conn); return; diff --git a/iscsi-scst/usr/ctldev.c b/iscsi-scst/usr/ctldev.c index 39b7369fb..c7f8d68bf 100644 --- a/iscsi-scst/usr/ctldev.c +++ b/iscsi-scst/usr/ctldev.c @@ -112,7 +112,8 @@ int kernel_target_create(struct target *target, u32 *tid, u32 cookie) { int err, i, j; struct iscsi_kern_target_info info; - struct iscsi_user *user; + struct iscsi_attr *user; + struct iscsi_attr *portal; struct iscsi_kern_attr *kern_attrs; memset(&info, 0, sizeof(info)); @@ -120,13 +121,15 @@ int kernel_target_create(struct target *target, u32 *tid, u32 cookie) info.tid = (tid != NULL) ? *tid : 0; info.cookie = cookie; + info.attrs_num = 1; + for (j = 0; j < session_key_last; j++) { if (session_keys[j].show_in_sysfs) - info.attrs_num++;; + info.attrs_num++; } for (j = 0; j < target_key_last; j++) { if (target_keys[j].show_in_sysfs) - info.attrs_num++;; + info.attrs_num++; } list_for_each_entry(user, &target->target_in_accounts, ulist) { info.attrs_num++; @@ -134,6 +137,9 @@ int kernel_target_create(struct target *target, u32 *tid, u32 cookie) list_for_each_entry(user, &target->target_out_accounts, ulist) { info.attrs_num++; } + list_for_each_entry(portal, &target->allowed_portals, ulist) { + info.attrs_num++; + } kern_attrs = calloc(info.attrs_num, sizeof(*kern_attrs)); if (kern_attrs == NULL) { @@ -143,6 +149,12 @@ int kernel_target_create(struct target *target, u32 *tid, u32 cookie) info.attrs_ptr = (unsigned long)kern_attrs; i = 0; + + kern_attrs[i].mode = 0644; + strlcpy(kern_attrs[i].name, ISCSI_PER_PORTAL_ACL, + sizeof(ISCSI_PER_PORTAL_ACL)); + i++; + for (j = 0; j < session_key_last; j++) { if (!session_keys[j].show_in_sysfs) continue; @@ -160,17 +172,23 @@ int kernel_target_create(struct target *target, u32 *tid, u32 cookie) i++; } list_for_each_entry(user, &target->target_in_accounts, ulist) { - kern_attrs[i].mode = 0600; + kern_attrs[i].mode = user->sysfs_mode; strlcpy(kern_attrs[i].name, user->sysfs_name, sizeof(kern_attrs[i].name)); i++; } list_for_each_entry(user, &target->target_out_accounts, ulist) { - kern_attrs[i].mode = 0600; + kern_attrs[i].mode = user->sysfs_mode; strlcpy(kern_attrs[i].name, user->sysfs_name, sizeof(kern_attrs[i].name)); i++; } + list_for_each_entry(portal, &target->allowed_portals, ulist) { + kern_attrs[i].mode = portal->sysfs_mode; + strlcpy(kern_attrs[i].name, portal->sysfs_name, + sizeof(kern_attrs[i].name)); + i++; + } log_debug(1, "Adding target %s (attrs_num %d)", target->name, info.attrs_num); @@ -252,46 +270,36 @@ int kernel_attr_del(struct target *target, const char *name, u32 cookie) return res; } -static int __kernel_user_cmd(struct iscsi_user *user, u32 cookie, unsigned int cmd) +int kernel_user_add(struct target *target, struct iscsi_attr *user, u32 cookie) { - struct iscsi_kern_attr_info info; - int res; - - memset(&info, 0, sizeof(info)); - if (user->target != NULL) - info.tid = user->target->tid; - info.cookie = cookie; - - info.attr.mode = 0600; - strlcpy(info.attr.name, user->sysfs_name, sizeof(info.attr.name)); - - res = ioctl(ctrl_fd, cmd, &info); - if (res < 0) - res = -errno; - - return res; + return kernel_attr_add(target, user->sysfs_name, 0600, cookie); } -int kernel_user_add(struct iscsi_user *user, u32 cookie) +int kernel_user_del(struct target *target, struct iscsi_attr *user, u32 cookie) { - int res; - res = __kernel_user_cmd(user, cookie, ISCSI_ATTR_ADD); - if (res != 0) - log_error("Can't add user %s (%d)\n", user->name, res); - return res; -} - -int kernel_user_del(struct iscsi_user *user, u32 cookie) -{ - int res; - res = __kernel_user_cmd(user, cookie, ISCSI_ATTR_DEL); - if (res != 0) - log_error("Can't del user %s (%d)\n", user->name, res); - return res; + return kernel_attr_del(target, user->sysfs_name, cookie); } #endif /* CONFIG_SCST_PROC */ +int kernel_initiator_allowed(u32 tid, const char *full_initiator_name) +{ + int err; + struct iscsi_kern_initiator_info info; + + memset(&info, 0, sizeof(info)); + info.tid = tid; + strlcpy(info.full_initiator_name, full_initiator_name, sizeof(info.full_initiator_name)); + + if ((err = ioctl(ctrl_fd, ISCSI_INITIATOR_ALLOWED, &info)) < 0) { + err = -errno; + log_error("Can't find out initiator %s permissions (errno %d, " + "tid %u", full_initiator_name, errno, tid); + } + + return err; +} + int kernel_conn_destroy(u32 tid, u64 sid, u32 cid) { int err; @@ -385,6 +393,13 @@ int kernel_session_create(struct connection *conn) int res, i; struct target *target; + target = target_find_by_id(conn->tid); + if (target == NULL) { + log_error("Target %d not found", conn->tid); + res = -EINVAL; + goto out; + } + memset(&info, 0, sizeof(info)); info.tid = conn->tid; @@ -399,12 +414,9 @@ int kernel_session_create(struct connection *conn) info.user_name[0] = '\0'; #endif - target = target_find_by_id(conn->tid); - if (target == NULL) { - log_error("Target %d not found", conn->tid); - res = -EINVAL; - goto out; - } + iscsi_make_full_initiator_name(target->per_portal_acl, + conn->sess->initiator, conn->target_portal, + info.full_initiator_name, sizeof(info.full_initiator_name)); for (i = 0; i < session_key_last; i++) info.session_params[i] = conn->session_params[i].val; diff --git a/iscsi-scst/usr/event.c b/iscsi-scst/usr/event.c index 23448179c..12acd38bc 100644 --- a/iscsi-scst/usr/event.c +++ b/iscsi-scst/usr/event.c @@ -93,6 +93,25 @@ static int nl_read(int fd, void *data, int len) #ifndef CONFIG_SCST_PROC +static int strncasecmp_numwild(const char *name, const char *mask) +{ + int err = -EINVAL; + + if (!strncasecmp(name, mask, strlen(name))) { + int j; + if (strlen(name) > strlen(mask)) + goto out; + for (j = strlen(name); j < strlen(mask); j++) { + if (!isdigit(mask[j])) + goto out; + } + err = 0; + } + +out: + return err; +} + static int send_mgmt_cmd_res(u32 tid, u32 cookie, u32 req_cmd, int result, const char *res_str) { @@ -214,15 +233,73 @@ static int handle_add_user(struct target *target, int dir, char *sysfs_name, return res; } -static int handle_add_param(struct target *target, char *p, u32 cookie) +static int __handle_add_attr(struct target *target, struct __qelem *attrs_list, + const char *sysfs_name_tmpl, char *p, int single_param_only, u32 cookie) +{ + int res; + const char *name = p; + const char *key, *val; + struct iscsi_attr *attr; + + key = config_sep_string(&p); + val = config_sep_string(&p); + + if (target == NULL) { + log_error("Target expected for attr %s", name); + res = -EINVAL; + goto out; + } + + if ((key == NULL) || (*key == '\0')) { + log_error("Value expected for attr %s", name); + res = -EINVAL; + goto out; + } + + if (val != NULL) + if (*val == '\0') + val = NULL; + + if (single_param_only) { + if (val != NULL) { + log_error("Only one value expected for attr %s", key); + res = -EINVAL; + goto out; + } + } + + res = iscsi_attr_create(sizeof(*attr), attrs_list, sysfs_name_tmpl, key, + val, 0644, &attr); + if (res != 0) { + log_error("Unknown portal %s", key); + goto out; + } + + res = kernel_attr_add(target, attr->sysfs_name, attr->sysfs_mode, cookie); + if (res != 0) + goto out_free; + +out: + return res; + +out_free: + iscsi_attr_destroy(attr); + goto out; +} + +static int handle_add_attr(struct target *target, char *p, u32 cookie) { int res, dir; char *pp; pp = config_sep_string(&p); + dir = params_index_by_name_numwild(pp, user_keys); if (dir >= 0) res = handle_add_user(target, dir, pp, p, cookie); + else if (strncasecmp_numwild(ISCSI_ALLOWED_PORTAL_NAME, pp) == 0) + res = __handle_add_attr(target, &target->allowed_portals, + ISCSI_ALLOWED_PORTAL_NAME, p, 1, cookie); else { log_error("Syntax error at %s", pp); res = -EINVAL; @@ -246,15 +323,51 @@ static int handle_del_user(struct target *target, int dir, char *p, u32 cookie) return res; } -static int handle_del_param(struct target *target, char *p, u32 cookie) +static int __handle_del_attr(struct target *target, struct __qelem *attrs_list, + char *p, u32 cookie) +{ + int res; + const char *key; + struct iscsi_attr *attr; + + key = config_sep_string(&p); + + if (target == NULL) { + log_error("Target expected for attr %s", p); + res = -EINVAL; + goto out; + } + + attr = iscsi_attr_lookup_by_key(attrs_list, key); + if (attr == NULL) { + log_error("Unknown portal %s", key); + res = -EINVAL; + goto out; + } + + res = kernel_attr_del(target, attr->sysfs_name, cookie); + if (res != 0) + goto out; + + iscsi_attr_destroy(attr); + +out: + return res; +} + +static int handle_del_attr(struct target *target, char *p, u32 cookie) { int res, dir; char *pp; pp = config_sep_string(&p); + dir = params_index_by_name_numwild(pp, user_keys); if (dir >= 0) res = handle_del_user(target, dir, p, cookie); + else if (strncasecmp_numwild(ISCSI_ALLOWED_PORTAL_NAME, pp) == 0) + res = __handle_del_attr(target, &target->allowed_portals, + p, cookie); else { log_error("Syntax error at %s", pp); res = -EINVAL; @@ -307,7 +420,7 @@ static int handle_e_mgmt_cmd(int fd, const struct iscsi_kern_event *event) p = buf; pp = config_sep_string(&p); if (strcasecmp("add_attribute", pp) == 0) { - res = handle_add_param(NULL, p, event->cookie); + res = handle_add_attr(NULL, p, event->cookie); } else if (strcasecmp("add_target_attribute", pp) == 0) { struct target *target; pp = config_sep_string(&p); @@ -317,10 +430,10 @@ static int handle_e_mgmt_cmd(int fd, const struct iscsi_kern_event *event) res = -ENOENT; goto out_free; } - res = handle_add_param(target, p, event->cookie); + res = handle_add_attr(target, p, event->cookie); } else if (strcasecmp("del_attribute", pp) == 0) { - res = handle_del_param(NULL, p, event->cookie); - } else if (strcasecmp("del_target_parameter", pp) == 0) { + res = handle_del_attr(NULL, p, event->cookie); + } else if (strcasecmp("del_target_attribute", pp) == 0) { struct target *target; pp = config_sep_string(&p); target = target_find_by_name(pp); @@ -329,7 +442,7 @@ static int handle_e_mgmt_cmd(int fd, const struct iscsi_kern_event *event) res = -ENOENT; goto out_free; } - res = handle_del_param(target, p, event->cookie); + res = handle_del_attr(target, p, event->cookie); } else { log_error("Syntax error at %s", pp); res = -EINVAL; @@ -422,7 +535,7 @@ static int handle_e_get_attr_value(int fd, const struct iscsi_kern_event *event) if (target->session_params[idx] != session_keys[idx].local_def) add_key_mark(res_str, sizeof(res_str), 1); } else if (!((idx = params_index_by_name_numwild(pp, user_keys)) < 0)) { - struct iscsi_user *user; + struct iscsi_attr *user; user = account_lookup_by_sysfs_name(target, idx, pp); if (user == NULL) { @@ -431,8 +544,26 @@ static int handle_e_get_attr_value(int fd, const struct iscsi_kern_event *event) goto out_free; } - snprintf(res_str, sizeof(res_str), "%s %s\n", user->name, - user->password); + snprintf(res_str, sizeof(res_str), "%s %s\n", ISCSI_USER_NAME(user), + ISCSI_USER_PASS(user)); + add_key_mark(res_str, sizeof(res_str), 0); + } else if (strncasecmp_numwild(ISCSI_ALLOWED_PORTAL_NAME, pp) == 0) { + struct iscsi_attr *portal; + + if (target == NULL) { + log_error("Target expected for attr %s", pp); + res = -EINVAL; + goto out_free; + } + + portal = iscsi_attr_lookup_by_sysfs_name(&target->allowed_portals, pp); + if (portal == NULL) { + log_error("Unknown portal attribute %s", pp); + res = -EINVAL; + goto out_free; + } + + snprintf(res_str, sizeof(res_str), "%s\n", portal->attr_key); add_key_mark(res_str, sizeof(res_str), 0); } else if (strcasecmp(ISCSI_ENABLED_ATTR_NAME, pp) == 0) { if (target != NULL) { @@ -441,9 +572,17 @@ static int handle_e_get_attr_value(int fd, const struct iscsi_kern_event *event) res = -EINVAL; goto out_free; } - snprintf(res_str, sizeof(res_str), "%d\n", iscsi_enabled); - } else if (strcasecmp(ISCSI_ISNS_SERVER_PARAM_NAME, pp) == 0) { + } else if (strcasecmp(ISCSI_PER_PORTAL_ACL, pp) == 0) { + if (target == NULL) { + log_error("Target expected for attr %s", pp); + res = -EINVAL; + goto out_free; + } + snprintf(res_str, sizeof(res_str), "%d\n", target->per_portal_acl); + if (target->per_portal_acl) + add_key_mark(res_str, sizeof(res_str), 0); + }else if (strcasecmp(ISCSI_ISNS_SERVER_PARAM_NAME, pp) == 0) { if (target != NULL) { log_error("Not NULL target %s for global attribute %s", target->name, pp); @@ -611,7 +750,7 @@ static int handle_e_set_attr_value(int fd, const struct iscsi_kern_event *event) target->session_params[idx] = val; } else if (!((idx = params_index_by_name_numwild(pp, user_keys)) < 0)) { - struct iscsi_user *user; + struct iscsi_attr *user; user = account_lookup_by_sysfs_name(target, idx, pp); if (user == NULL) { @@ -623,6 +762,25 @@ static int handle_e_set_attr_value(int fd, const struct iscsi_kern_event *event) res = account_replace(target, idx, pp, p); if (res != 0) goto out_free; + } else if (strncasecmp_numwild(ISCSI_ALLOWED_PORTAL_NAME, pp) == 0) { + struct iscsi_attr *portal; + + if (target == NULL) { + log_error("Target expected for attr %s", pp); + res = -EINVAL; + goto out_free; + } + + portal = iscsi_attr_lookup_by_sysfs_name(&target->allowed_portals, pp); + if (portal == NULL) { + log_error("Unknown portal attribute %s", pp); + res = -EINVAL; + goto out_free; + } + + res = iscsi_attr_replace(&target->allowed_portals, pp, p); + if (res != 0) + goto out_free; } else if (strcasecmp(ISCSI_ENABLED_ATTR_NAME, pp) == 0) { if (target != NULL) { log_error("Not NULL target %s for global attribute %s", @@ -630,7 +788,6 @@ static int handle_e_set_attr_value(int fd, const struct iscsi_kern_event *event) res = -EINVAL; goto out_free; } - pp = config_sep_string(&p); if (strcmp(pp, "1") == 0) iscsi_enabled = 1; @@ -641,7 +798,23 @@ static int handle_e_set_attr_value(int fd, const struct iscsi_kern_event *event) res = -EINVAL; goto out_free; } - } else if (strcasecmp(ISCSI_ISNS_SERVER_PARAM_NAME, pp) == 0) { + } else if (strcasecmp(ISCSI_PER_PORTAL_ACL, pp) == 0) { + if (target == NULL) { + log_error("Target expected for attr %s", pp); + res = -EINVAL; + goto out_free; + } + pp = config_sep_string(&p); + if (strcmp(pp, "1") == 0) + target->per_portal_acl = 1; + else if (strcmp(pp, "0") == 0) + target->per_portal_acl = 0; + else { + log_error("Unknown value %s", pp); + res = -EINVAL; + goto out_free; + } + }else if (strcasecmp(ISCSI_ISNS_SERVER_PARAM_NAME, pp) == 0) { if (target != NULL) { log_error("Not NULL target %s for global attribute %s", target->name, pp); diff --git a/iscsi-scst/usr/iscsi_scstd.c b/iscsi-scst/usr/iscsi_scstd.c index 66e9d158d..b341bedb3 100644 --- a/iscsi-scst/usr/iscsi_scstd.c +++ b/iscsi-scst/usr/iscsi_scstd.c @@ -39,24 +39,11 @@ #include "iscsid.h" #include "iscsi_adm.h" -#define LISTEN_MAX 8 -#define INCOMING_MAX 256 - -enum { - POLL_LISTEN, - POLL_IPC = POLL_LISTEN + LISTEN_MAX, - POLL_NL, - POLL_ISNS, - POLL_SCN_LISTEN, - POLL_SCN, - POLL_INCOMING, - POLL_MAX = POLL_INCOMING + INCOMING_MAX, -}; - static char* server_address; uint16_t server_port = ISCSI_LISTEN_PORT; -static struct pollfd poll_array[POLL_MAX]; +struct pollfd poll_array[POLL_MAX]; + static struct connection *incoming[INCOMING_MAX]; static int incoming_cnt; int ctrl_fd, ipc_fd, nl_fd; @@ -137,11 +124,19 @@ static void sock_set_keepalive(int sock, int timeout) } } +const char *get_EAI_error_str(int error) +{ + if (error == EAI_SYSTEM) + return strerror(errno); + else + return gai_strerror(error); +} + static void create_listen_socket(struct pollfd *array) { struct addrinfo hints, *res, *res0; char servname[64]; - int i, sock, opt; + int i, sock, opt, rc; memset(servname, 0, sizeof(servname)); snprintf(servname, sizeof(servname), "%d", server_port); @@ -150,8 +145,10 @@ static void create_listen_socket(struct pollfd *array) hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_PASSIVE; - if (getaddrinfo(server_address, servname, &hints, &res0)) { - log_error("Unable to get address info (%s)!", strerror(errno)); + rc = getaddrinfo(server_address, servname, &hints, &res0); + if (rc != 0) { + log_error("Unable to get address info (%s)!", + get_EAI_error_str(rc)); exit(1); } @@ -212,11 +209,12 @@ static void accept_connection(int listen) struct sockaddr_in sin; struct sockaddr_in6 sin6; } from, to; - char portal[50]; /* for full IP6 address + port */ socklen_t namesize; struct pollfd *pollfd; struct connection *conn; int fd, i, rc; + char initiator_addr[ISCSI_PORTAL_LEN], initiator_port[NI_MAXSERV]; + char target_portal[ISCSI_PORTAL_LEN], target_portal_port[NI_MAXSERV]; namesize = sizeof(from); if ((fd = accept(listen, &from.sa, &namesize)) < 0) { @@ -241,42 +239,32 @@ static void accept_connection(int listen) namesize = sizeof(to); rc = getsockname(fd, &to.sa, &namesize); - if (rc == 0) { - if (from.sa.sa_family == AF_INET) { - struct sockaddr_in *in = &to.sin; - rc = snprintf(portal, sizeof(portal), "%s:%hu", - inet_ntoa(in->sin_addr), ntohs(in->sin_port)); - } else if (from.sa.sa_family == AF_INET6) { - struct sockaddr_in6 *in6 = &to.sin6; - rc = snprintf(portal, sizeof(portal), "%x:%x:%x:%x:%x:%x:%x:%x.%hu", - in6->sin6_addr.s6_addr16[7], in6->sin6_addr.s6_addr16[6], - in6->sin6_addr.s6_addr16[5], in6->sin6_addr.s6_addr16[4], - in6->sin6_addr.s6_addr16[3], in6->sin6_addr.s6_addr16[2], - in6->sin6_addr.s6_addr16[1], in6->sin6_addr.s6_addr16[0], - ntohs(in6->sin6_port)); - } - if (rc >= sizeof(portal)) - log_error("portal too small %zu (needed %d)", sizeof(portal), rc); - } else { - portal[0] = '\0'; + if (rc != 0) { perror("getsockname() failed"); goto out_close; } - if (from.sa.sa_family == AF_INET) { - struct sockaddr_in *in = &from.sin; - log_info("Connect from %s:%hu to %s", inet_ntoa(in->sin_addr), - ntohs(in->sin_port), portal); - } else if (from.sa.sa_family == AF_INET6) { - struct sockaddr_in6 *in6 = &from.sin6; - log_info("Connect from %x:%x:%x:%x:%x:%x:%x:%x.%hu to %s", - in6->sin6_addr.s6_addr16[7], in6->sin6_addr.s6_addr16[6], - in6->sin6_addr.s6_addr16[5], in6->sin6_addr.s6_addr16[4], - in6->sin6_addr.s6_addr16[3], in6->sin6_addr.s6_addr16[2], - in6->sin6_addr.s6_addr16[1], in6->sin6_addr.s6_addr16[0], - ntohs(in6->sin6_port), portal); + rc = getnameinfo(&to.sa, sizeof(to), target_portal, sizeof(target_portal), + target_portal_port, sizeof(target_portal_port), + NI_NUMERICHOST | NI_NUMERICSERV); + if (rc != 0) { + log_error("Target portal getnameinfo() failed: %s!", + get_EAI_error_str(rc)); + goto out_close; } + rc = getnameinfo(&from.sa, sizeof(from), initiator_addr, + sizeof(initiator_addr), initiator_port, + sizeof(initiator_port), NI_NUMERICHOST | NI_NUMERICSERV); + if (rc != 0) { + log_error("Initiator getnameinfo() failed: %s!", + get_EAI_error_str(rc)); + goto out_close; + } + + log_info("Connect from %s:%s to %s:%s", initiator_addr, initiator_port, + target_portal, target_portal_port); + if (conn_blocked) { log_warning("Connection refused due to blocking\n"); goto out_close; @@ -297,6 +285,12 @@ static void accept_connection(int listen) } conn->fd = fd; + conn->target_portal = strdup(target_portal); + if (conn->target_portal == NULL) { + log_error("Unable to duplicate target portal %s", target_portal); + goto out_free; + } + incoming[i] = conn; conn_read_pdu(conn); @@ -313,6 +307,9 @@ static void accept_connection(int listen) out: return; +out_free: + conn_free(conn); + out_close: close(fd); goto out; diff --git a/iscsi-scst/usr/iscsid.c b/iscsi-scst/usr/iscsid.c index 1a14e6e3f..2d6eb40d6 100644 --- a/iscsi-scst/usr/iscsid.c +++ b/iscsi-scst/usr/iscsid.c @@ -551,8 +551,10 @@ static void login_start(struct connection *conn) conn->tid = target->tid; - if (config_initiator_access(conn->tid, conn->fd) || - isns_scn_access(conn->tid, conn->fd, name)) { + if (!config_initiator_access_allowed(conn->tid, conn->fd) || + !target_portal_allowed(target, conn->target_portal, + conn->initiator) || + !isns_scn_access_allowed(conn->tid, name)) { log_info("Initiator %s not allowed to connect to " "target %s", name, target_name); login_rsp_ini_err(conn, ISCSI_STATUS_TGT_NOT_FOUND); @@ -863,8 +865,9 @@ tgt_no_mem: return; } -static void text_scan_text(struct connection *conn) +static int text_scan_text(struct connection *conn) { + int res = 0; char *key, *value, *data; int datasize; @@ -873,65 +876,34 @@ static void text_scan_text(struct connection *conn) while ((key = next_key(&data, &datasize, &value))) { if (!strcmp(key, "SendTargets")) { - struct sockaddr_storage ss; - socklen_t slen, blen; - char *p, *addr, buf[NI_MAXHOST + 128]; - int suppress_ip6 = 0; - - if (value[0] == 0) - continue; - - p = addr = buf + 1; - buf[0] = '['; - blen = sizeof(buf) - 1; - - slen = sizeof(ss); - - getpeername(conn->fd, (struct sockaddr *) &ss, &slen); - if (ss.ss_family == AF_INET) - suppress_ip6 = 1; - else { - getnameinfo((struct sockaddr *) &ss, slen, p, - blen, NULL, 0, NI_NUMERICHOST); - - if (strstr(p, "::ffff:") == p) - suppress_ip6 = 1; // ipv4-mapped ipv6 ? + if (value[0] == '\0') + value = conn->sess->target->name; + else if (strcasecmp(value, "All") == 0) { + if (conn->session_type != SESSION_DISCOVERY) { + log_error("SendTargets=All allowed only in " + "Discovery session, rejecting " + "(initiator %s)", conn->initiator); + cmnd_reject(conn, ISCSI_REASON_PROTOCOL_ERROR); + res = -EINVAL; + goto out; + } } - getsockname(conn->fd, (struct sockaddr *) &ss, &slen); - slen = sizeof(ss); - - getnameinfo((struct sockaddr *) &ss, slen, p, blen, - NULL, 0, NI_NUMERICHOST); - - if (ss.ss_family == AF_INET6 && suppress_ip6) { - if (strstr(p, "::ffff:") != p) { - log_error("%s, %s:%d.", __FILE__, - __func__, __LINE__); - suppress_ip6 = 0; - } else - addr += 7; - } - - p += strlen(p); - - if (ss.ss_family == AF_INET6 && !suppress_ip6) { - *p++ = ']'; - addr = buf; - } - - sprintf(p, ":%d,1", server_port); - target_list_build(conn, addr, + target_list_build(conn, strcmp(value, "All") ? value : NULL); } else text_key_add(conn, key, "NotUnderstood"); } + +out: + return res; } static void cmnd_exec_text(struct connection *conn) { struct iscsi_text_req_hdr *req = (struct iscsi_text_req_hdr *)&conn->req.bhs; struct iscsi_text_rsp_hdr *rsp = (struct iscsi_text_rsp_hdr *)&conn->rsp.bhs; + int rc; memset(rsp, 0, BHS_SIZE); @@ -945,7 +917,9 @@ static void cmnd_exec_text(struct connection *conn) if (req->ttt == ISCSI_RESERVED_TAG) { conn_free_rsp_buf_list(conn); - text_scan_text(conn); + rc = text_scan_text(conn); + if (rc != 0) + goto out; if (!list_empty(&conn->rsp_buf_list) && !list_length_is_one(&conn->rsp_buf_list)) conn->ttt = get_next_ttt(conn); @@ -971,6 +945,9 @@ static void cmnd_exec_text(struct connection *conn) rsp->stat_sn = cpu_to_be32(conn->stat_sn++); rsp->exp_cmd_sn = cpu_to_be32(conn->exp_cmd_sn); rsp->max_cmd_sn = cpu_to_be32(conn->exp_cmd_sn + 1); + +out: + return; } static void cmnd_exec_logout(struct connection *conn) diff --git a/iscsi-scst/usr/iscsid.h b/iscsi-scst/usr/iscsid.h index 0621fc958..e8501f546 100644 --- a/iscsi-scst/usr/iscsid.h +++ b/iscsi-scst/usr/iscsid.h @@ -19,6 +19,7 @@ #include #include +#include #include #include "types.h" @@ -80,6 +81,7 @@ struct connection { struct iscsi_param session_params[session_key_last]; char *initiator; + char *target_portal; char *user; union iscsi_sid sid; u16 cid; @@ -153,18 +155,17 @@ struct connection { */ #define INCOMING_BUFSIZE 8192 -struct iscsi_user { +#define ISCSI_USER_DIR_INCOMING 0 +#define ISCSI_USER_DIR_OUTGOING 1 + +#define ISCSI_USER_NAME(attr) (attr)->attr_key +#define ISCSI_USER_PASS(attr) (attr)->attr_value + +struct iscsi_attr { struct __qelem ulist; - - struct target *target; - -#define ISCSI_USER_DIR_INCOMING 0 -#define ISCSI_USER_DIR_OUTGOING 1 - int direction; - - const char *name; - const char *password; - + const char *attr_key; + const char *attr_value; + u32 sysfs_mode; char sysfs_name[64]; }; @@ -174,6 +175,7 @@ struct target { struct __qelem sessions_list; unsigned int tgt_enabled:1; + unsigned int per_portal_acl:1; unsigned int target_params[target_key_last]; unsigned int session_params[session_key_last]; @@ -186,12 +188,30 @@ struct target { struct __qelem target_in_accounts; struct __qelem target_out_accounts; + struct __qelem allowed_portals; + struct __qelem isns_head; }; extern int ctrl_fd; extern int conn_blocked; +#define LISTEN_MAX 8 +#define INCOMING_MAX 256 + +enum { + POLL_LISTEN, + POLL_IPC = POLL_LISTEN + LISTEN_MAX, + POLL_NL, + POLL_ISNS, + POLL_SCN_LISTEN, + POLL_SCN, + POLL_INCOMING, + POLL_MAX = POLL_INCOMING + INCOMING_MAX, +}; + +extern struct pollfd poll_array[POLL_MAX]; + /* chap.c */ extern int cmnd_exec_auth_chap(struct connection *conn); @@ -208,6 +228,7 @@ extern void conn_free_rsp_buf_list(struct connection *conn); extern uint16_t server_port; extern struct iscsi_init_params iscsi_init_params; extern void isns_set_fd(int isns, int scn_listen, int scn); +extern const char *get_EAI_error_str(int error); /* iscsid.c */ extern int iscsi_enabled; @@ -253,8 +274,13 @@ extern int target_add(struct target *target, u32 *tid, u32 cookie); extern int target_del(u32 tid, u32 cookie); extern u32 target_find_id_by_name(const char *name); extern struct target *target_find_by_name(const char *name); -struct target *target_find_by_id(u32); -extern void target_list_build(struct connection *, char *, char *); +extern struct target *target_find_by_id(u32); +extern void target_list_build(struct connection *, char *); +extern int target_portal_allowed(struct target *target, + const char *target_portal, const char *initiator_name); +extern const char *iscsi_make_full_initiator_name(int per_portal_acl, + const char *initiator_name, const char *target_portal, + char *buf, int size); /* message.c */ extern int iscsi_adm_request_listen(void); @@ -268,12 +294,15 @@ extern int kernel_params_set(u32 tid, u64 sid, int type, u32 partial, extern int kernel_target_create(struct target *target, u32 *tid, u32 cookie); extern int kernel_target_destroy(u32 tid, u32 cookie); #ifndef CONFIG_SCST_PROC -extern int kernel_user_add(struct iscsi_user *user, u32 cookie); -extern int kernel_user_del(struct iscsi_user *user, u32 cookie); +extern int kernel_user_add(struct target *target, struct iscsi_attr *attr, + u32 cookie); +extern int kernel_user_del(struct target *target, struct iscsi_attr *attr, + u32 cookie); extern int kernel_attr_add(struct target *target, const char *name, u32 mode, u32 cookie); extern int kernel_attr_del(struct target *target, const char *name, u32 cookie); #endif +extern int kernel_initiator_allowed(u32 tid, const char *initiator_name); extern int kernel_session_create(struct connection *conn); extern int kernel_session_destroy(u32 tid, u64 sid); extern int kernel_conn_create(u32 tid, u64 sid, u32 cid, u32 stat_sn, u32 exp_stat_sn, @@ -301,14 +330,25 @@ extern int config_account_del(u32 tid, int dir, char *name, u32 cookie); extern int config_params_get(u32 tid, u64 sid, int type, struct iscsi_param *params); extern int config_params_set(u32 tid, u64 sid, int type, u32 partial, struct iscsi_param *params); -extern int config_initiator_access(u32 tid, int fd); +extern int config_initiator_access_allowed(u32 tid, int fd); extern int accounts_empty(u32 tid, int dir); -extern struct iscsi_user *account_get_first(u32 tid, int dir); -extern struct iscsi_user *account_lookup_by_sysfs_name(struct target *target, +extern struct iscsi_attr *account_get_first(u32 tid, int dir); +extern struct iscsi_attr *account_lookup_by_sysfs_name(struct target *target, int dir, const char *sysfs_name); extern int account_replace(struct target *target, int direction, const char *sysfs_name, char *value); extern void accounts_free(struct __qelem *accounts_list); +extern struct iscsi_attr *iscsi_attr_lookup_by_sysfs_name( + struct __qelem *attrs_list, const char *sysfs_name); +extern struct iscsi_attr *iscsi_attr_lookup_by_key( + struct __qelem *attrs_list, const char *key); +extern void iscsi_attrs_free(struct __qelem *attrs_list); +extern int iscsi_attr_create(int attr_size, struct __qelem *attrs_list, + const char *sysfs_name_tmpl, const char *key, const char *val, + u32 mode, struct iscsi_attr **res_attr); +extern void iscsi_attr_destroy(struct iscsi_attr *attr); +extern int iscsi_attr_replace(struct __qelem *attrs_list, const char *sysfs_name, + char *raw_value); /* isns.c */ extern char *isns_server; @@ -317,7 +357,7 @@ extern int isns_timeout; extern int isns_init(void); extern int isns_handle(int is_timeout); extern int isns_scn_handle(int accept); -extern int isns_scn_access(u32 tid, int fd, char *name); +extern int isns_scn_access_allowed(u32 tid, char *name); extern int isns_target_register(char *name); extern int isns_target_deregister(char *name); extern void isns_exit(void); diff --git a/iscsi-scst/usr/isns.c b/iscsi-scst/usr/isns.c index a70028739..9e29fc5e7 100644 --- a/iscsi-scst/usr/isns.c +++ b/iscsi-scst/usr/isns.c @@ -69,22 +69,22 @@ static char eid[ISCSI_NAME_LEN]; static uint8_t ip[16]; /* SCST iSCSI supports only one portal */ static struct sockaddr_storage ss; -int isns_scn_access(uint32_t tid, int fd, char *name) +int isns_scn_access_allowed(uint32_t tid, char *name) { struct isns_initiator *ini; struct target *target = target_find_by_id(tid); if ((isns_server == NULL) || !isns_access_control) - return 0; + return 1; if (!target) - return -ENOENT; + return 0; list_for_each_entry(ini, &target->isns_head, ilist) { if (!strcmp(ini->name, name)) - return 0; + return 1; } - return -EPERM; + return 0; } static int isns_get_ip(int fd) @@ -100,14 +100,14 @@ static int isns_get_ip(int fd) err = getsockname(fd, &lss.sa, &slen); if (err) { - log_error("getsockname error: %s!", gai_strerror(err)); + log_error("getsockname error: %s!", strerror(err)); return err; } err = getnameinfo(&lss.sa, sizeof(lss), eid, sizeof(eid), NULL, 0, 0); if (err) { - log_error("getaddrinfo error: %s!", gai_strerror(err)); + log_error("getnameinfo error: %s!", get_EAI_error_str(err)); return err; } @@ -941,7 +941,7 @@ int isns_init(void) hints.ai_socktype = SOCK_STREAM; err = getaddrinfo(isns_server, (char *)&port, &hints, &res); if (err) { - log_error("getaddrinfo error: %s, %s", gai_strerror(err), + log_error("getaddrinfo error: %s, %s", get_EAI_error_str(err), isns_server); goto out; } diff --git a/iscsi-scst/usr/param.h b/iscsi-scst/usr/param.h index 4c3d3fb56..6723ace33 100644 --- a/iscsi-scst/usr/param.h +++ b/iscsi-scst/usr/param.h @@ -16,10 +16,11 @@ #ifndef PARAMS_H #define PARAMS_H -#define ISCSI_ISNS_SERVER_PARAM_NAME "iSNSServer" -#define ISCSI_ISNS_ACCESS_CONTROL_PARAM_NAME "iSNSAccessControl" - -#define ISCSI_ENABLED_ATTR_NAME "enabled" +#define ISCSI_ISNS_SERVER_PARAM_NAME "iSNSServer" +#define ISCSI_ISNS_ACCESS_CONTROL_PARAM_NAME "iSNSAccessControl" +#define ISCSI_ENABLED_ATTR_NAME "enabled" +#define ISCSI_ALLOWED_PORTAL_NAME "allowed_portal" +#define ISCSI_PER_PORTAL_ACL "per_portal_acl" struct iscsi_key; diff --git a/iscsi-scst/usr/target.c b/iscsi-scst/usr/target.c index 683e69af5..e989d2f82 100644 --- a/iscsi-scst/usr/target.c +++ b/iscsi-scst/usr/target.c @@ -21,25 +21,266 @@ #include #include #include +#include +#include +#include +#include +#include +#include #include "iscsid.h" struct __qelem targets_list = LIST_HEAD_INIT(targets_list); -void target_list_build(struct connection *conn, char *addr, char *name) +const char *iscsi_make_full_initiator_name(int per_portal_acl, + const char *initiator_name, const char *target_portal, + char *buf, int size) +{ + if (per_portal_acl) + snprintf(buf, size, "%s#%s", initiator_name, + target_portal); + else + snprintf(buf, size, "%s", initiator_name); + + return buf; +} + +/* + * Written by Jack Handy - jakkhandy@hotmail.com + * Taken by Gennadiy Nerubayev from + * http://www.codeproject.com/KB/string/wildcmp.aspx. No license attached + * to it, and it's posted on a free site; assumed to be free for use. + * + * Added the negative sign support - VLNB + * + * Also see comment for wildcmp(). + * + * SCST core also has a copy of this code, so fixing a bug here, don't forget + * to fix the copy too! + */ +static int __wildcmp(const char *wild, const char *string, int recursion_level) +{ + const char *cp = NULL, *mp = NULL; + + while ((*string) && (*wild != '*')) { + if ((*wild == '!') && (recursion_level == 0)) + return !__wildcmp(++wild, string, ++recursion_level); + + if ((*wild != *string) && (*wild != '?')) + return 0; + + wild++; + string++; + } + + while (*string) { + if ((*wild == '!') && (recursion_level == 0)) + return !__wildcmp(++wild, string, ++recursion_level); + + if (*wild == '*') { + if (!*++wild) + return 1; + + mp = wild; + cp = string+1; + } else if ((*wild == *string) || (*wild == '?')) { + wild++; + string++; + } else { + wild = mp; + string = cp++; + } + } + + while (*wild == '*') + wild++; + + return !*wild; +} + +/* + * Returns true if string "string" matches pattern "wild", false otherwise. + * Pattern is a regular DOS-type pattern, containing '*' and '?' symbols. + * '*' means match all any symbols, '?' means match only any single symbol. + * + * For instance: + * if (wildcmp("bl?h.*", "blah.jpg")) { + * // match + * } else { + * // no match + * } + * + * Also it supports boolean inversion sign '!', which does boolean inversion of + * the value of the rest of the string. Only one '!' allowed in the pattern, + * other '!' are treated as regular symbols. For instance: + * if (wildcmp("bl!?h.*", "blah.jpg")) { + * // no match + * } else { + * // match + * } + * + * Also see comment for __wildcmp(). + */ +static int wildcmp(const char *wild, const char *string) +{ + return __wildcmp(wild, string, 0); +} + +int target_portal_allowed(struct target *target, + const char *target_portal, const char *initiator_name) +{ + int res; + char full_initiator_name[ISCSI_FULL_NAME_LEN]; + + if (!list_empty(&target->allowed_portals)) { + struct iscsi_attr *attr; + + res = 0; + list_for_each_entry(attr, &target->allowed_portals, ulist) { + if (wildcmp(attr->attr_key, target_portal)) { + res = 1; + break; + } + } + if (res == 0) + goto out; + } + + res = kernel_initiator_allowed(target->tid, + iscsi_make_full_initiator_name(target->per_portal_acl, + initiator_name, target_portal, + full_initiator_name, sizeof(full_initiator_name))); + if (res < 0) + res = 0; /* false */ + +out: + return res; +} + +static int is_addr_loopback(char *addr) +{ + struct in_addr ia; + struct in6_addr ia6; + + if (inet_pton(AF_INET, addr, &ia) == 1) + return !strncmp(addr, "127.", 4); + + if (inet_pton(AF_INET6, addr, &ia6) == 1) + return IN6_IS_ADDR_LOOPBACK(&ia6); + + return 0; +} + +static int is_addr_unspecified(char *addr) +{ + struct in_addr ia; + struct in6_addr ia6; + + if (inet_pton(AF_INET, addr, &ia) == 1) + return (ia.s_addr == 0); + + if (inet_pton(AF_INET6, addr, &ia6) == 1) + return IN6_IS_ADDR_UNSPECIFIED(&ia6); + + return 0; +} + +static void target_print_addr(struct connection *conn, char *addr, int family) +{ + char taddr[NI_MAXHOST + NI_MAXSERV + 5]; + + snprintf(taddr, sizeof(taddr), + (family == AF_INET) ? "%s:%d,1" : "[%s]:%d,1", + addr, server_port); + + text_key_add(conn, "TargetAddress", taddr); +} + +static void target_list_build_ifaddrs(struct connection *conn, + struct target *target, char *exclude_addr, int family) +{ + struct ifaddrs *ifaddr, *ifa; + char if_addr[NI_MAXHOST]; + + getifaddrs(&ifaddr); + + for (ifa = ifaddr; ifa; ifa = ifa->ifa_next) { + if (!ifa->ifa_addr) + continue; + + int sa_family = ifa->ifa_addr->sa_family; + + if (sa_family == family) { + if (getnameinfo(ifa->ifa_addr, (family == AF_INET) ? + sizeof(struct sockaddr_in) : + sizeof(struct sockaddr_in6), + if_addr, sizeof(if_addr), + NULL, 0, NI_NUMERICHOST)) + continue; + + if (strcmp(exclude_addr, if_addr) && + !is_addr_loopback(if_addr) && + target_portal_allowed(target, if_addr, conn->initiator)) + target_print_addr(conn, if_addr, family); + } + } + + freeifaddrs(ifaddr); + return; +} + +void target_list_build(struct connection *conn, char *target_name) { struct target *target; + struct sockaddr_storage ss1, ss2; + socklen_t slen = sizeof(struct sockaddr_storage); + char portal[NI_MAXHOST]; + int family, i; + + if (getsockname(conn->fd, (struct sockaddr *) &ss1, &slen)) { + log_error("getsockname failed: %m"); + return; + } + family = ss1.ss_family; list_for_each_entry(target, &targets_list, tlist) { - if (name && strcmp(target->name, name)) + if (target_name && strcmp(target->name, target_name)) continue; - if (config_initiator_access(target->tid, conn->fd) || - isns_scn_access(target->tid, conn->fd, conn->initiator)) + + if (!isns_scn_access_allowed(target->tid, conn->initiator) || + !config_initiator_access_allowed(target->tid, conn->fd) || + !target_portal_allowed(target, conn->target_portal, + conn->initiator)) continue; text_key_add(conn, "TargetName", target->name); - text_key_add(conn, "TargetAddress", addr); + target_print_addr(conn, conn->target_portal, family); + + for (i = 0; i < LISTEN_MAX && poll_array[i].fd; i++) { + slen = sizeof(struct sockaddr_storage); + + if (getsockname(poll_array[i].fd, + (struct sockaddr *) &ss2, &slen)) + continue; + + if (getnameinfo((struct sockaddr *) &ss2, slen, portal, + sizeof(portal), NULL, 0, NI_NUMERICHOST)) + continue; + + if (ss2.ss_family != family) + continue; + + if (is_addr_unspecified(portal)) + target_list_build_ifaddrs(conn, target, + conn->target_portal, family); + else if (strcmp(conn->target_portal, portal) && + !is_addr_loopback(portal) && + target_portal_allowed(target, portal, + conn->initiator)) + target_print_addr(conn, portal, family); + } } + return; } u32 target_find_id_by_name(const char *name) @@ -116,6 +357,8 @@ void target_free(struct target *target) accounts_free(&target->target_in_accounts); accounts_free(&target->target_out_accounts); + iscsi_attrs_free(&target->allowed_portals); + free(target); return; } @@ -146,6 +389,7 @@ int target_create(const char *name, struct target **out_target) INIT_LIST_HEAD(&target->sessions_list); INIT_LIST_HEAD(&target->target_in_accounts); INIT_LIST_HEAD(&target->target_out_accounts); + INIT_LIST_HEAD(&target->allowed_portals); INIT_LIST_HEAD(&target->isns_head); *out_target = target; diff --git a/scst/README b/scst/README index 817fa3ff3..2bef1a3c1 100644 --- a/scst/README +++ b/scst/README @@ -495,10 +495,12 @@ following files and directories under /proc/scsi_tgt: for group "GROUP_NAME" - "add NAME" to /proc/scsi_tgt/groups/GROUP_NAME/names adds name "NAME" to group - "GROUP_NAME". For NAME you can use simple DOS-type patterns, containing - '*' and '?' symbols. '*' means match all any symbols, '?' means - match only any single symbol. For instance, "blah.xxx" will match - "bl?h.*". + "GROUP_NAME". For NAME you can use simple DOS-type patterns, + containing '*' and '?' symbols. '*' means match all any symbols, '?' + means match only any single symbol. For instance, "blah.xxx" will + match "bl?h.*". Additionally, you can use negative sign '!' to + revert the value of the pattern. For instance, "ah.xxx" will match + "!bl?h.*". - "del NAME" to /proc/scsi_tgt/groups/GROUP_NAME/names deletes name "NAME" from group "GROUP_NAME" @@ -874,7 +876,9 @@ looking inside this file. For "add" and "del" commands INITIATOR_NAME can be a simple DOS-type patterns, containing '*' and '?' symbols. '*' means match all any symbols, '?' means match only any single symbol. For instance, -"blah.xxx" will match "bl?h.*". +"blah.xxx" will match "bl?h.*". Additionally, you can use negative sign +'!' to revert the value of the pattern. For instance, "ah.xxx" will +match "!bl?h.*". Each "luns" subdirectory contains the list of already defined LUNs for this group as well as file "mgmt". Content of this file as well as list diff --git a/scst/README_in-tree b/scst/README_in-tree index fd1c64e97..5460b040c 100644 --- a/scst/README_in-tree +++ b/scst/README_in-tree @@ -556,7 +556,9 @@ looking inside this file. For "add" and "del" commands INITIATOR_NAME can be a simple DOS-type patterns, containing '*' and '?' symbols. '*' means match all any symbols, '?' means match only any single symbol. For instance, -"blah.xxx" will match "bl?h.*". +"blah.xxx" will match "bl?h.*". Additionally, you can use negative sign +'!' to revert the value of the pattern. For instance, "ah.xxx" will +match "!bl?h.*". Each "luns" subdirectory contains the list of already defined LUNs for this group as well as file "mgmt". Content of this file as well as list diff --git a/scst/include/scst.h b/scst/include/scst.h index 73cd2cab7..d300c4449 100644 --- a/scst/include/scst.h +++ b/scst/include/scst.h @@ -2293,6 +2293,8 @@ static inline int scst_register_virtual_dev_driver( void scst_unregister_virtual_dev_driver(struct scst_dev_type *dev_type); +bool scst_initiator_has_luns(struct scst_tgt *tgt, const char *initiator_name); + struct scst_cmd *scst_rx_cmd(struct scst_session *sess, const uint8_t *lun, int lun_len, const uint8_t *cdb, int cdb_len, int atomic); diff --git a/scst/src/scst_lib.c b/scst/src/scst_lib.c index ca887f745..164244eeb 100644 --- a/scst/src/scst_lib.c +++ b/scst/src/scst_lib.c @@ -1635,6 +1635,9 @@ next: old_acg->acg_name, acg->acg_name); list_move_tail(&sess->acg_sess_list_entry, &acg->acg_sess_list); + scst_recreate_sess_luns_link(sess); + /* Ignore possible error, since we can't do anything on it */ + if (luns_changed) { scst_report_luns_changed_sess(sess); diff --git a/scst/src/scst_priv.h b/scst/src/scst_priv.h index 596cfa019..760bf91b6 100644 --- a/scst/src/scst_priv.h +++ b/scst/src/scst_priv.h @@ -465,6 +465,10 @@ static inline void scst_sess_sysfs_put(struct scst_session *sess) { scst_release_session(sess); } +static inline int scst_recreate_sess_luns_link(struct scst_session *sess) +{ + return 0; +} static inline int scst_create_sgv_sysfs(struct sgv_pool *pool) { @@ -521,6 +525,7 @@ void scst_tgt_sysfs_prepare_put(struct scst_tgt *tgt); void scst_tgt_sysfs_put(struct scst_tgt *tgt); int scst_create_sess_sysfs(struct scst_session *sess); void scst_sess_sysfs_put(struct scst_session *sess); +int scst_recreate_sess_luns_link(struct scst_session *sess); int scst_create_sgv_sysfs(struct sgv_pool *pool); void scst_sgv_sysfs_put(struct sgv_pool *pool); int scst_create_devt_sysfs(struct scst_dev_type *devt); diff --git a/scst/src/scst_sysfs.c b/scst/src/scst_sysfs.c index 9b3398727..5dce7d14b 100644 --- a/scst/src/scst_sysfs.c +++ b/scst/src/scst_sysfs.c @@ -1393,6 +1393,30 @@ static struct kobj_type scst_session_ktype = { .default_attrs = scst_session_attrs, }; +static int scst_create_sess_luns_link(struct scst_session *sess) +{ + int retval; + + if (sess->acg == sess->tgt->default_acg) + retval = sysfs_create_link(&sess->sess_kobj, + sess->tgt->tgt_luns_kobj, "luns"); + else + retval = sysfs_create_link(&sess->sess_kobj, + sess->acg->luns_kobj, "luns"); + + if (retval != 0) + PRINT_ERROR("Can't create luns link for initiator %s", + sess->initiator_name); + + return retval; +} + +int scst_recreate_sess_luns_link(struct scst_session *sess) +{ + sysfs_remove_link(&sess->sess_kobj, "luns"); + return scst_create_sess_luns_link(sess); +} + /* scst_mutex supposed to be locked */ int scst_create_sess_sysfs(struct scst_session *sess) { @@ -1463,12 +1487,7 @@ restart: } } - if (sess->acg == sess->tgt->default_acg) - retval = sysfs_create_link(&sess->sess_kobj, - sess->tgt->tgt_luns_kobj, "luns"); - else - retval = sysfs_create_link(&sess->sess_kobj, - sess->acg->luns_kobj, "luns"); + retval = scst_create_sess_luns_link(sess); out_free: if (name != sess->initiator_name) diff --git a/scst/src/scst_targ.c b/scst/src/scst_targ.c index a05391599..a88938c2b 100644 --- a/scst/src/scst_targ.c +++ b/scst/src/scst_targ.c @@ -5585,27 +5585,26 @@ out_free: EXPORT_SYMBOL(scst_rx_mgmt_fn); /* - * Returns true if string "string" matches pattern "wild", false otherwise. - * Pattern is a regular DOS-type pattern, containing '*' and '?' symbols. - * '*' means match all any symbols, '?' means match only any single symbol. - * - * For instance: - * if (wildcmp("bl?h.*", "blah.jpg")) { - * // match - * } else { - * // no match - * } - * * Written by Jack Handy - jakkhandy@hotmail.com * Taken by Gennadiy Nerubayev from * http://www.codeproject.com/KB/string/wildcmp.aspx. No license attached * to it, and it's posted on a free site; assumed to be free for use. + * + * Added the negative sign support - VLNB + * + * Also see comment for wildcmp(). + * + * User space part of iSCSI-SCST also has a copy of this code, so fixing a bug + * here, don't forget to fix the copy too! */ -static bool wildcmp(const char *wild, const char *string) +static bool __wildcmp(const char *wild, const char *string, int recursion_level) { const char *cp = NULL, *mp = NULL; while ((*string) && (*wild != '*')) { + if ((*wild == '!') && (recursion_level == 0)) + return !__wildcmp(++wild, string, ++recursion_level); + if ((*wild != *string) && (*wild != '?')) return false; @@ -5614,6 +5613,9 @@ static bool wildcmp(const char *wild, const char *string) } while (*string) { + if ((*wild == '!') && (recursion_level == 0)) + return !__wildcmp(++wild, string, ++recursion_level); + if (*wild == '*') { if (!*++wild) return true; @@ -5635,6 +5637,34 @@ static bool wildcmp(const char *wild, const char *string) return !*wild; } +/* + * Returns true if string "string" matches pattern "wild", false otherwise. + * Pattern is a regular DOS-type pattern, containing '*' and '?' symbols. + * '*' means match all any symbols, '?' means match only any single symbol. + * + * For instance: + * if (wildcmp("bl?h.*", "blah.jpg")) { + * // match + * } else { + * // no match + * } + * + * Also it supports boolean inversion sign '!', which does boolean inversion of + * the value of the rest of the string. Only one '!' allowed in the pattern, + * other '!' are treated as regular symbols. For instance: + * if (wildcmp("bl!?h.*", "blah.jpg")) { + * // no match + * } else { + * // match + * } + * + * Also see comment for __wildcmp(). + */ +static bool wildcmp(const char *wild, const char *string) +{ + return __wildcmp(wild, string, 0); +} + #ifdef CONFIG_SCST_PROC /* scst_mutex supposed to be held */ @@ -5715,29 +5745,62 @@ out: #endif /* CONFIG_SCST_PROC */ /* Must be called under scst_mutex */ -struct scst_acg *scst_find_acg(const struct scst_session *sess) +static struct scst_acg *__scst_find_acg(struct scst_tgt *tgt, + const char *initiator_name) { struct scst_acg *acg = NULL; TRACE_ENTRY(); #ifdef CONFIG_SCST_PROC - if (sess->initiator_name) - acg = scst_find_acg_by_name_wild(sess->initiator_name); - if ((acg == NULL) && (sess->tgt->default_group_name != NULL)) - acg = scst_find_acg_by_name(sess->tgt->default_group_name); + if (initiator_name) + acg = scst_find_acg_by_name_wild(initiator_name); + if ((acg == NULL) && (tgt->default_group_name != NULL)) + acg = scst_find_acg_by_name(tgt->default_group_name); if (acg == NULL) acg = scst_default_acg; #else - acg = scst_find_tgt_acg_by_name_wild(sess->tgt, sess->initiator_name); + acg = scst_find_tgt_acg_by_name_wild(tgt, initiator_name); if (acg == NULL) - acg = sess->tgt->default_acg; + acg = tgt->default_acg; #endif TRACE_EXIT_HRES((unsigned long)acg); return acg; } +/* Must be called under scst_mutex */ +struct scst_acg *scst_find_acg(const struct scst_session *sess) +{ + return __scst_find_acg(sess->tgt, sess->initiator_name); +} + +/** + * scst_initiator_has_luns() - check if this initiator will see any LUNs + * + * Checks if this initiator will see any LUNs upon connect to this target. + * Returns true if yes and false otherwise. + */ +bool scst_initiator_has_luns(struct scst_tgt *tgt, const char *initiator_name) +{ + bool res; + struct scst_acg *acg; + + TRACE_ENTRY(); + + mutex_lock(&scst_mutex); + + acg = __scst_find_acg(tgt, initiator_name); + + res = !list_empty(&acg->acg_dev_list); + + mutex_unlock(&scst_mutex); + + TRACE_EXIT_RES(res); + return res; +} +EXPORT_SYMBOL(scst_initiator_has_luns); + static int scst_init_session(struct scst_session *sess) { int res = 0, rc;