int isns_dsasig_sign(isns_security_t *ctx, isns_principal_t *peer, buf_t *pdu, struct isns_authblk *blk) { static unsigned char signature[1024]; unsigned int sig_len = sizeof(signature); EVP_MD_CTX *md_ctx; EVP_PKEY *pkey; const BIGNUM *priv_key = NULL; int err; if ((pkey = peer->is_key) == NULL) return 0; if (EVP_PKEY_base_id(pkey) != EVP_PKEY_DSA) { isns_debug_message( "Incompatible public key (spi=%s)\n", peer->is_name); return 0; } if (EVP_PKEY_size(pkey) > sizeof(signature)) { isns_error("isns_dsasig_sign: signature buffer too small\n"); return 0; } DSA_get0_key(EVP_PKEY_get0_DSA(pkey), NULL, &priv_key); if (priv_key == NULL) { isns_error("isns_dsasig_sign: oops, seems to be a public key\n"); return 0; } isns_debug_auth("Signing messages with spi=%s, DSA/%u\n", peer->is_name, EVP_PKEY_bits(pkey)); md_ctx = EVP_MD_CTX_new(); EVP_SignInit(md_ctx, EVP_dss1()); isns_message_digest(md_ctx, pdu, blk); err = EVP_SignFinal(md_ctx, signature, &sig_len, pkey); EVP_MD_CTX_free(md_ctx); if (err == 0) { isns_dsasig_report_errors("EVP_SignFinal failed", isns_error); return 0; } blk->iab_sig = signature; blk->iab_sig_len = sig_len; return 1; }
int isns_dsasig_verify(isns_security_t *ctx, isns_principal_t *peer, buf_t *pdu, const struct isns_authblk *blk) { EVP_MD_CTX *md_ctx; EVP_PKEY *pkey; int err; if ((pkey = peer->is_key) == NULL) return 0; if (EVP_PKEY_base_id(pkey) != EVP_PKEY_DSA) { isns_debug_message( "Incompatible public key (spi=%s)\n", peer->is_name); return 0; } md_ctx = EVP_MD_CTX_new(); EVP_VerifyInit(md_ctx, EVP_dss1()); isns_message_digest(md_ctx, pdu, blk); err = EVP_VerifyFinal(md_ctx, blk->iab_sig, blk->iab_sig_len, pkey); EVP_MD_CTX_free(md_ctx); if (err == 0) { isns_debug_auth("*** Incorrect signature ***\n"); return 0; } if (err < 0) { isns_dsasig_report_errors("EVP_VerifyFinal failed", isns_error); return 0; } isns_debug_message("Good signature from %s\n", peer->is_name?: "<server>"); return 1; }
/* * 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; }
/* * Process a deregistration * * Normally, you would expect that a deregistration removes the * object from the database, and that's the end of the story. * Unfortunately, someone added Discovery Domains to the protocol, * requiring _some_ information to survive as long as an object * is referenced by a discovery domain. Specifically, we need to * retain the relationship between key attributes (eg iscsi node * name) and the object index. * * Thus, deregistration consists of the following steps * - the object is removed from the database's global scope, * so that it's no longer visible to DB lookups. * * - the object is detached from its containing Network * Entity. * * - all attributes except the key attr(s) and the index * attribute are removed. */ int isns_process_deregistration(isns_server_t *srv, isns_simple_t *call, isns_simple_t **result) { isns_object_list_t objects = ISNS_OBJECT_LIST_INIT; isns_simple_t *reply = NULL; isns_db_t *db = srv->is_db; int status, dereg_status; unsigned int i; /* Get the objects to deregister */ status = isns_deregistration_get_objects(call, db, &objects); if (status != ISNS_SUCCESS) goto done; /* * 5.6.5.4 * * 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. */ /* * Implementation note: this can be implemented either by * explicitly checking the object's owner in isns_db_remove * (which is what we do right now), or by matching only * those objects that have the right owner anyway. * * The latter sounds like a better choice if the client * uses NIL attributes, because it limits the scope of * the operation; but then the RFC doesn't say whether * this kind of deregistration would be valid at all. */ /* Success: create a new simple message, and * send it in our reply. */ reply = __isns_create_deregistration(srv->is_source, NULL); if (reply == NULL) { status = ISNS_INTERNAL_ERROR; goto done; } dereg_status = ISNS_SUCCESS; for (i = 0; i < objects.iol_count; ++i) { isns_object_t *obj = objects.iol_data[i]; /* Policy: check that the client is permitted * to deregister this object */ if (!isns_policy_validate_object_access(call->is_policy, call->is_source, obj, call->is_function)) status = ISNS_SOURCE_UNAUTHORIZED; if (status == ISNS_SUCCESS) status = isns_db_remove(db, obj); if (status != ISNS_SUCCESS) { /* * 5.7.5.4 * * In the event of an error, this response message * contains the appropriate status code as well * as a list of objects from the original DevDereg * message that were not successfully deregistered * from the iSNS database. This list of objects * is contained in the Operating Attributes * of the DevDeregRsp message. Note that an * attempted deregistration of a non-existent * object does not constitute an isns_error, and * non-existent entries SHALL not be returned * in the DevDeregRsp message. */ /* * Implementation: right now this doesn't work * at all, because isns_msg_set_error will * discard the entire message except for the * status word. */ isns_debug_message("Failed to deregister object: %s (0x%04x)\n", isns_strerror(status), status); isns_object_extract_all(obj, &reply->is_operating_attrs); dereg_status = status; continue; } /* * 5.7.5.4 * If all Nodes and Portals associated with a Network * Entity are deregistered, then the Network Entity * SHALL also be removed. * [...] * If both the Portal and iSCSI Storage Node objects * associated with a Portal Group object are removed, * then that Portal Group object SHALL also be removed. * The Portal Group object SHALL remain registered * as long as either of its associated Portal or * iSCSI Storage Node objects remain registered. If a * deleted Storage Node or Portal object is subsequently * re-registered, then a relationship between the re- * registered object and an existing Portal or Storage * Node object registration, indicated by the PG object, * SHALL be restored. */ /* isns_db_remove takes care of removing dead entities, * and dead portal groups. */ } if (status == ISNS_SUCCESS) status = dereg_status; done: isns_object_list_destroy(&objects); *result = reply; return status; }