From 2df2039eafdf217965231c040490bda5353067d0 Mon Sep 17 00:00:00 2001 From: David Lawrence Date: Tue, 14 Mar 2000 03:54:13 +0000 Subject: [PATCH] message signature verification --- lib/omapi/message.c | 197 +++++++++++++++++++++++++++++++++++++++----- 1 file changed, 175 insertions(+), 22 deletions(-) diff --git a/lib/omapi/message.c b/lib/omapi/message.c index 61f578f780..f373294c96 100644 --- a/lib/omapi/message.c +++ b/lib/omapi/message.c @@ -131,13 +131,14 @@ omapi_message_send(omapi_object_t *message, omapi_object_t *protocol) { * For this function, at least, generic objects have fully spelled * names and special type objects have short names. * XXXDCL It would be good to be more consistent about this throughout - * the code. + * the omapi library code. */ omapi_protocol_t *p; omapi_connection_t *c; omapi_message_t *m; omapi_object_t *connection; - isc_result_t result; + int authlen = 0; + isc_result_t result = ISC_R_SUCCESS; REQUIRE(message != NULL && message->type == omapi_type_message); /* @@ -161,12 +162,23 @@ omapi_message_send(omapi_object_t *message, omapi_object_t *protocol) { m = (omapi_message_t *)message; - /* XXXTL Write the authenticator length */ - result = omapi_connection_putuint32(connection, 0); + if (p->key != NULL) { + result = dst_sign(DST_SIGMODE_INIT, p->key, &p->dstctx, + NULL, NULL); + + if (result == ISC_R_SUCCESS) + result = dst_sig_size(p->key, &authlen); + + p->dst_update = ISC_TRUE; + } + if (result == ISC_R_SUCCESS) /* XXXTL Write the ID of the authentication key we're using. */ result = omapi_connection_putuint32(connection, 0); + if (result == ISC_R_SUCCESS) + result = omapi_connection_putuint32(connection, authlen); + if (result == ISC_R_SUCCESS) /* * Write the opcode. @@ -228,9 +240,22 @@ omapi_message_send(omapi_object_t *message, omapi_object_t *protocol) { */ result = omapi_connection_putuint16(connection, 0); - if (result == ISC_R_SUCCESS) - /* XXXTL Write the authenticator... */ - (void)0; + if (result == ISC_R_SUCCESS && p->key != NULL) { + isc_region_t r; + + isc_buffer_clear(p->signature_out); + + result = dst_sign(DST_SIGMODE_FINAL, p->key, &p->dstctx, + NULL, p->signature_out); + + isc_buffer_region(p->signature_out, &r); + + p->dst_update = ISC_FALSE; + + if (result == ISC_R_SUCCESS) + result = omapi_connection_putmem(connection, + r.base, r.length); + } /* * Prime the bytes_needed for the server's reply message. @@ -306,13 +331,29 @@ message_process(omapi_object_t *mo, omapi_object_t *po) { omapi_message_t *message, *m; omapi_object_t *object = NULL; omapi_objecttype_t *type = NULL; + omapi_protocol_t *protocol; + omapi_connection_t *connection; omapi_value_t *tv = NULL; unsigned long create, update, exclusive; isc_result_t result, waitstatus; REQUIRE(mo != NULL && mo->type == omapi_type_message); + REQUIRE(po != NULL); message = (omapi_message_t *)mo; + protocol = (omapi_protocol_t *)po; + + INSIST(po->outer != NULL && po->outer->type == omapi_type_connection); + + /* + * Note that the checking of connection->is_client throughout this + * function pretty much means that peer-to-peer transactions can't + * happen over a single connection. It is not clear, yet, whether that + * is such a bad thing, but the original design document didn't + * specify that particular operations were only valid on the client + * or on the server. + */ + connection = (omapi_connection_t *)po->outer; if (message->rid != 0) { for (m = registered_messages; m != NULL; m = m->next) @@ -328,8 +369,60 @@ message_process(omapi_object_t *mo, omapi_object_t *po) { } else m = NULL; + if (protocol->key != NULL) { + if (protocol->verify_result == ISC_R_SUCCESS) + protocol->verify_result = + dst_verify(DST_SIGMODE_FINAL, protocol->key, + &protocol->dstctx, NULL, + &protocol->signature_in); + + if (protocol->verify_result != ISC_R_SUCCESS) { + if (connection->is_client) { + INSIST(m != NULL); + result = omapi_object_setstring(mo, "message", + "failed to verify signature"); + if (result == ISC_R_SUCCESS) + (void)omapi_object_getvalue(mo, + "message", + &tv); + + object_signal((omapi_object_t *)m, "status", + protocol->verify_result, tv); + + if (tv != NULL) + omapi_value_dereference(&tv); + + /* + * This keeps the connection from being blown + * away, although it seems fairly reasonable + * to force a disconnect. + */ + return (ISC_R_SUCCESS); + + } else + /* + * XXXDCL Should the key be stricken? + * The curious thing about the way this + * is currently set up is that the status + * message won't verify on the client if + * the secret was wrong ... so rather than + * getting processed in OMAPI_OP_STATUS + * below, it will be handled by this ``if'' + * statement on the client. + */ + return (send_status(po, + protocol->verify_result, + message->id, + "failed to verify " + "signature")); + } + } + switch (message->op) { - case OMAPI_OP_OPEN: + case OMAPI_OP_OPEN: + if (connection->is_client) + return (ISC_R_UNEXPECTED); + if (m != NULL) { return (send_status(po, OMAPI_R_INVALIDARG, message->id, @@ -396,6 +489,14 @@ message_process(omapi_object_t *mo, omapi_object_t *po) { return (send_status(po, result, message->id, isc_result_totext(result))); + /* + * All messages except for the first attempt to set + * the dst key used by the protocol must be signed. + */ + if (type != omapi_type_protocol && protocol->key == NULL) + return (send_status(po, ISC_R_NOPERM, message->id, + "unauthorized access")); + /* * If we weren't given a type, look the object up with * the handle. @@ -409,16 +510,34 @@ message_process(omapi_object_t *mo, omapi_object_t *po) { goto refresh; } - /* - * If the type doesn't provide a lookup method, we can't - * look up the object. Ditto if no lookup key is provided. - */ if (message->object == NULL) - return (send_status(po, ISC_R_NOTFOUND, - message->id, + return (send_status(po, ISC_R_NOTFOUND, message->id, "no lookup key specified")); - result = object_methodlookup(type, &object, message->object); + /* + * This is pretty hackish, a special case for an attempt + * to open the protocol object. It was done because + * under the current design of OMAPI, there just isn't + * a good way to set the authentication values. The + * connection object and protocol object are the only + * things that hold state on the server throughout the life + * of a particular connection, and the original design + * for lookup methods does not provide a way to identify + * the current protocol or connection object. + * + * To minimize the hackishness, at least the rest of + * the manipulation of the protocol object is done through + * the normal object interfaces, rather than having a + * a special block do the work directly. Small consolation. + */ + if (type == omapi_type_protocol) { + OBJECT_REF(&object, po); + result = ISC_R_SUCCESS; + + } else + result = object_methodlookup(type, &object, + message->object); + if (result == ISC_R_NOTIMPLEMENTED) return (send_status(po, result, message->id, "unsearchable object type")); @@ -477,19 +596,32 @@ message_process(omapi_object_t *mo, omapi_object_t *po) { */ goto send; - case OMAPI_OP_REFRESH: - refresh: + case OMAPI_OP_REFRESH: + if (connection->is_client) + return (ISC_R_UNEXPECTED); + + if (protocol->key == NULL) + return (send_status(po, ISC_R_NOPERM, message->id, + "unauthorized access")); + + refresh: result = handle_lookup(&object, message->h); if (result != ISC_R_SUCCESS) return (send_status(po, result, message->id, "no matching handle")); - send: + send: result = send_update(po, message->id, object); OBJECT_DEREF(&object); return (result); - case OMAPI_OP_UPDATE: + case OMAPI_OP_UPDATE: + if (! connection->is_client) + return (send_status(po, OMAPI_R_INVALIDARG, + message->id, + "OMAPI_OP_UPDATE is not a " + "valid server operation")); + if (m->object != NULL) OBJECT_REF(&object, m->object); @@ -528,11 +660,17 @@ message_process(omapi_object_t *mo, omapi_object_t *po) { return (result); - case OMAPI_OP_NOTIFY: + case OMAPI_OP_NOTIFY: return (send_status(po, ISC_R_NOTIMPLEMENTED, message->id, "notify not implemented yet")); - case OMAPI_OP_STATUS: + case OMAPI_OP_STATUS: + if (! connection->is_client) + return (send_status(po, OMAPI_R_INVALIDARG, + message->id, + "OMAPI_OP_STATUS is not a " + "valid server operation")); + /* * The return status of a request. */ @@ -563,7 +701,14 @@ message_process(omapi_object_t *mo, omapi_object_t *po) { */ return (ISC_R_SUCCESS); - case OMAPI_OP_DELETE: + case OMAPI_OP_DELETE: + if (connection->is_client) + return (ISC_R_UNEXPECTED); + + if (protocol->key == NULL) + return (send_status(po, ISC_R_NOPERM, message->id, + "unauthorized delete")); + result = handle_lookup(&object, message->h); if (result != ISC_R_SUCCESS) return (send_status(po, result, message->id, @@ -579,6 +724,7 @@ message_process(omapi_object_t *mo, omapi_object_t *po) { return (send_status(po, result, message->id, NULL)); } + return (ISC_R_NOTIMPLEMENTED); } @@ -597,6 +743,7 @@ message_setvalue(omapi_object_t *h, omapi_string_t *name, omapi_data_t *value) /* * Can set authenticator, but the value must be typed data. + * XXXDCL (no longer meaningful) */ if (omapi_string_strcmp(name, "authenticator") == 0) { if (m->authenticator != NULL) @@ -746,6 +893,12 @@ message_signalhandler(omapi_object_t *handle, const char *name, va_list ap) { message = (omapi_message_t *)handle; + /* + * XXXDCL It would make the client side a bit cleaner if when "status" + * is signalled, it sets both "waitresult" and "waittext" (or some + * such) in the OMAPI_OBJECT_PREAMBLE of both the message and + * the notify_object or regular object. + */ if (strcmp(name, "status") == 0 && (message->object != NULL || message->notify_object != NULL)) { if (message->notify_object != NULL)