/* * Check whether the client is permitted to access * or create an object of this type. * FIXME: Pass R/W permission bit */ int isns_policy_validate_object_type(const isns_policy_t *policy, isns_object_template_t *tmpl, unsigned int function) { uint32_t mask; int rv = 0; /* Control nodes get to do everything */ if (policy->ip_node_types & ISNS_ISCSI_CONTROL_MASK) goto accept; mask = ISNS_ACCESS_R(tmpl->iot_handle); if (!(policy->ip_object_types & mask)) goto reject; accept: rv = 1; reject: isns_debug_auth(":: policy %s operation %s on object type %s %s\n", policy->ip_name, isns_function_name(function), tmpl->iot_name, rv? "permitted" : "DENIED"); return rv; }
/* * Check whether the call is permitted. * This is particularly useful to prevent rogue * clients from messing with Discovery Domains. */ int isns_policy_validate_function(const isns_policy_t *policy, const isns_message_t *msg) { uint32_t function = msg->im_header.i_function; int rv = 0; if (function >= 32) { isns_debug_auth("Bad function code %08x\n", function); return 0; } if (!(policy->ip_functions & (1 << function))) goto reject; rv = 1; reject: isns_debug_auth(":: policy %s function %s (%04x) %s\n", policy->ip_name, isns_function_name(function), function, rv? "permitted" : "DENIED"); return rv; }
/* * Check whether the client is allowed to access * the given object in a particular way. */ static int __isns_policy_validate_object_access(const isns_policy_t *policy, const isns_source_t *source, const isns_object_t *obj, isns_object_template_t *tmpl, unsigned int function) { uint32_t mask, perm = ISNS_PERMISSION_WRITE; int rv = 0; /* Control nodes get to do everything */ if (policy->ip_node_types & ISNS_ISCSI_CONTROL_MASK) goto accept; if (function == ISNS_DEVICE_ATTRIBUTE_QUERY || function == ISNS_DEVICE_GET_NEXT) perm = ISNS_PERMISSION_READ; /* * 5.6.1. Source Attribute * * For messages that change the contents of the iSNS * database, the iSNS server MUST verify that the Source * Attribute identifies either a Control Node or a Storage * Node that is a part of the Network Entity containing * the added, deleted, or modified objects. * * Note: this statement makes sense for nodes, portals * etc, but not for discovery domains, which are not * part of any network entity (but the Control Node clause * above still applies). */ if (perm == ISNS_PERMISSION_WRITE && obj != NULL) { const isns_object_t *entity; entity = obj->ie_container; if (entity && entity != source->is_entity) goto refuse; /* You're not allowed to modify virtual objects */ if (obj->ie_rebuild) goto refuse; } /* Check whether the client is permitted to access such an object */ mask = ISNS_ACCESS(tmpl->iot_handle, perm); if (!(policy->ip_object_types & mask)) goto refuse; if (source->is_untrusted && (obj->ie_flags & ISNS_OBJECT_PRIVATE)) goto refuse; accept: rv = 1; refuse: if (obj) { isns_debug_auth(":: policy %s operation %s on object %08x (%s) %s\n", policy->ip_name, isns_function_name(function), obj->ie_index, tmpl->iot_name, rv? "permitted" : "DENIED"); } else { isns_debug_auth(":: policy %s operation %s on %s object %s\n", policy->ip_name, isns_function_name(function), tmpl->iot_name, rv? "permitted" : "DENIED"); } return rv; }
/* * Process an incoming message */ isns_message_t * isns_process_message(isns_server_t *srv, isns_message_t *msg) { struct isns_service_ops *ops = srv->is_ops; uint16_t function = msg->im_header.i_function; int status = ISNS_SUCCESS; isns_simple_t *call = NULL, *reply = NULL; isns_message_t *res_msg = NULL; isns_db_t *db = srv->is_db; status = isns_simple_decode(msg, &call); if (status) { isns_debug_message("Failed to decode %s request: %s\n", isns_function_name(msg->im_header.i_function), isns_strerror(status)); goto reply; } isns_simple_print(call, isns_debug_message); /* Set policy and privileges based on the * sender's identity. */ if (!(call->is_policy = isns_policy_bind(msg))) goto err_unauthorized; if (!isns_policy_validate_function(call->is_policy, msg)) goto err_unauthorized; /* Checks related to the message source. * Note - some messages do not use a source. */ if (call->is_source) { /* Validate the message source. This checks whether the client * is permitted to use this source node name. * Beware - not all messages include a source. */ if (!isns_policy_validate_source(call->is_policy, call->is_source)) goto err_unauthorized; /* This may fail if the source node isn't in the DB yet. */ isns_source_set_node(call->is_source, db); /* * 6.2.6. Registration Period * * The registration SHALL be removed from the iSNS database * if an iSNS Protocol message is not received from the * iSNS client before the registration period has expired. * Receipt of any iSNS Protocol message from the iSNS client * automatically refreshes the Entity Registration Period and * Entity Registration Timestamp. To prevent a registration * from expiring, the iSNS client should send an iSNS Protocol * message to the iSNS server at intervals shorter than the * registration period. Such a message can be as simple as a * query for one of its own attributes, using its associated * iSCSI Name or FC Port Name WWPN as the Source attribute. */ /* Thusly, we update the timestamps of all entities * registered by this source. */ isns_entity_touch(call->is_source->is_entity); } /* Handle the requested function. If the function vector is * NULL, silently discard the message. */ switch (function) { #define DO(rw, FUNCTION, __function) \ case FUNCTION: \ if (!ops->__function) \ goto no_reply; \ \ if (!isns_begin_##rw##_operation(srv, call, &status)) \ break; \ status = ops->__function(srv, call, &reply); \ isns_end_##rw##_operation(srv, call, &status); \ break DO(write, ISNS_DEVICE_ATTRIBUTE_REGISTER, process_registration); DO(read, ISNS_DEVICE_ATTRIBUTE_QUERY, process_query); DO(read, ISNS_DEVICE_GET_NEXT, process_getnext); DO(write, ISNS_DEVICE_DEREGISTER, process_deregistration); DO(write, ISNS_DD_REGISTER, process_dd_registration); DO(write, ISNS_DD_DEREGISTER, process_dd_deregistration); DO(read, ISNS_SCN_REGISTER, process_scn_registration); DO(read, ISNS_SCN_DEREGISTER, process_scn_deregistration); DO(read, ISNS_SCN_EVENT, process_scn_event); DO(read, ISNS_STATE_CHANGE_NOTIFICATION, process_scn); DO(read, ISNS_ENTITY_STATUS_INQUIRY, process_esi); DO(read, ISNS_HEARTBEAT, process_heartbeat); #undef DO default: isns_error("Function %s not supported\n", isns_function_name(function)); status = ISNS_MESSAGE_NOT_SUPPORTED; break; } reply: /* Commit any changes to the DB before we reply */ if (db) isns_db_sync(db); /* Send out SCN notifications */ isns_flush_events(); if (reply != NULL) { reply->is_function |= 0x8000; isns_simple_print(reply, isns_debug_message); /* Encode the whole thing */ status = isns_simple_encode_response(reply, msg, &res_msg); } /* No reply, or error when encoding it: * just send the error, nothing else. */ if (res_msg == NULL) { res_msg = isns_create_reply(msg); if (status == ISNS_SUCCESS) status = ISNS_INTERNAL_ERROR; } isns_debug_message("response status 0x%04x (%s)\n", status, isns_strerror(status)); if (status != ISNS_SUCCESS) isns_message_set_error(res_msg, status); no_reply: isns_simple_free(call); if (reply) isns_simple_free(reply); return res_msg; err_unauthorized: status = ISNS_SOURCE_UNAUTHORIZED; goto reply; }