diff options
author | Rich Megginson <rmeggins@redhat.com> | 2009-08-20 12:59:08 -0600 |
---|---|---|
committer | Rich Megginson <rmeggins@redhat.com> | 2009-08-21 17:56:43 -0600 |
commit | 0d1f300d0e4e41d96f3022c5c80bfcb34507f5b5 (patch) | |
tree | e9f551386786af97cc6e3d7742348535f1cd2d62 | |
parent | 99b41607701765fd4c8c93a1183507db78d785fa (diff) | |
download | ds-0d1f300d0e4e41d96f3022c5c80bfcb34507f5b5.tar.gz ds-0d1f300d0e4e41d96f3022c5c80bfcb34507f5b5.tar.xz ds-0d1f300d0e4e41d96f3022c5c80bfcb34507f5b5.zip |
Retry SASL writes if buffer not fully sent
https://bugzilla.redhat.com/show_bug.cgi?id=518544
Resolves: bug 518544
Bug Description: large entries cause server SASL responses to fail
Reviewed by: nhosoi (Thanks!)
Branch: HEAD and 1.2
Fix Description: The SASL server code was broken when we switched over to
use NSPR I/O for the SASL IO layer. If the entire encrypted buffer could
not be sent to the client, the server was just failing. Instead, the server
must keep track of how many encrypted bytes were sent. If all of the
encrypted bytes could not be sent, we must return the appropriate error
to the caller to let them know the operation would block. The caller in
this case is the write_function() which does a poll() to see if the socket
is available for writing again, then will attempt the send again.
I also cleaned up usage of the various Debug macros.
Finally, I discovered that the sasl init code was calling config_get_localhost()
before that value could be set. In most cases, it is ok, because it will
fall back to the default hostname from the system. However, if for some
reason you want to use a different localhost, it will fail. Now it will be
set in the boostrap config code.
Platforms tested: RHEL5 x86_64
Flag Day: no
Doc impact: no
-rw-r--r-- | ldap/servers/slapd/config.c | 14 | ||||
-rw-r--r-- | ldap/servers/slapd/sasl_io.c | 82 |
2 files changed, 71 insertions, 25 deletions
diff --git a/ldap/servers/slapd/config.c b/ldap/servers/slapd/config.c index 62757572..da87a0a1 100644 --- a/ldap/servers/slapd/config.c +++ b/ldap/servers/slapd/config.c @@ -594,6 +594,20 @@ slapd_bootstrap_config(const char *configdir) } } + /* what is our localhost name */ + if (entry_has_attr_and_value(e, CONFIG_LOCALHOST_ATTRIBUTE, + val, sizeof(val))) + { + if (config_set_localhost( + CONFIG_LOCALHOST_ATTRIBUTE, val, errorbuf, + CONFIG_APPLY) != LDAP_SUCCESS) + { + LDAPDebug(LDAP_DEBUG_ANY, "%s: %s: %s\n", configfile, + CONFIG_LOCALHOST_ATTRIBUTE, errorbuf); + } + val[0] = 0; + } + if (e) slapi_entry_free(e); } diff --git a/ldap/servers/slapd/sasl_io.c b/ldap/servers/slapd/sasl_io.c index c6ecf4b3..4c695b1d 100644 --- a/ldap/servers/slapd/sasl_io.c +++ b/ldap/servers/slapd/sasl_io.c @@ -75,7 +75,9 @@ struct PRFilePrivate { PRBool send_encrypted; /* can only send encrypted data after the first read - that is, we cannot send back an encrypted response to the bind request that established the sasl io */ - + const char *send_buffer; /* encrypted buffer to send to client */ + unsigned int send_size; /* size of the encrypted buffer */ + unsigned int send_offset; /* number of bytes sent so far */ }; typedef PRFilePrivate sasl_io_private; @@ -269,10 +271,10 @@ sasl_io_read_packet(PRFileDesc *fd, PRIntn flags, PRIntervalTime timeout, PRInt3 Connection *c = sp->conn; size_t bytes_remaining_to_read = sp->encrypted_buffer_count - sp->encrypted_buffer_offset; - LDAPDebug( LDAP_DEBUG_CONNS, + LDAPDebug2Args( LDAP_DEBUG_CONNS, "sasl_io_read_packet: reading %d bytes for connection %" NSPRIu64 "\n", bytes_remaining_to_read, - c->c_connid, 0 ); + c->c_connid ); ret = PR_Recv(fd->lower, sp->encrypted_buffer + sp->encrypted_buffer_offset, bytes_remaining_to_read, flags, timeout); if (ret < 0) { *err = PR_GetError(); @@ -333,13 +335,13 @@ sasl_io_recv(PRFileDesc *fd, void *buf, PRInt32 len, PRIntn flags, { const char *output_buffer = NULL; unsigned int output_length = 0; - LDAPDebug( LDAP_DEBUG_CONNS, - "sasl_io_recv finished reading packet for connection %" NSPRIu64 "\n", c->c_connid, 0, 0 ); + LDAPDebug1Arg( LDAP_DEBUG_CONNS, + "sasl_io_recv finished reading packet for connection %" NSPRIu64 "\n", c->c_connid ); /* Now decode it */ ret = sasl_decode(c->c_sasl_conn,sp->encrypted_buffer,sp->encrypted_buffer_count,&output_buffer,&output_length); if (SASL_OK == ret) { - LDAPDebug( LDAP_DEBUG_CONNS, - "sasl_io_recv decoded packet length %d for connection %" NSPRIu64 "\n", output_length, c->c_connid, 0 ); + LDAPDebug2Args( LDAP_DEBUG_CONNS, + "sasl_io_recv decoded packet length %d for connection %" NSPRIu64 "\n", output_length, c->c_connid ); if (output_length) { sasl_io_resize_decrypted_buffer(sp,output_length); memcpy(sp->decrypted_buffer,output_buffer,output_length); @@ -350,8 +352,8 @@ sasl_io_recv(PRFileDesc *fd, void *buf, PRInt32 len, PRIntn flags, bytes_in_buffer = output_length; } } else { - LDAPDebug( LDAP_DEBUG_ANY, - "sasl_io_recv failed to decode packet for connection %" NSPRIu64 "\n", c->c_connid, 0, 0 ); + LDAPDebug1Arg( LDAP_DEBUG_ANY, + "sasl_io_recv failed to decode packet for connection %" NSPRIu64 "\n", c->c_connid ); PR_SetError(PR_IO_ERROR, 0); return PR_FAILURE; } @@ -368,8 +370,8 @@ sasl_io_recv(PRFileDesc *fd, void *buf, PRInt32 len, PRIntn flags, if (bytes_in_buffer == bytes_to_return) { sp->decrypted_buffer_offset = 0; sp->decrypted_buffer_count = 0; - LDAPDebug( LDAP_DEBUG_CONNS, - "sasl_io_recv all decrypted data returned for connection %" NSPRIu64 "\n", c->c_connid, 0, 0 ); + LDAPDebug1Arg( LDAP_DEBUG_CONNS, + "sasl_io_recv all decrypted data returned for connection %" NSPRIu64 "\n", c->c_connid ); } else { sp->decrypted_buffer_offset += bytes_to_return; LDAPDebug( LDAP_DEBUG_CONNS, @@ -387,6 +389,14 @@ sasl_io_recv(PRFileDesc *fd, void *buf, PRInt32 len, PRIntn flags, return ret; } +static void +reset_send_info(sasl_io_private *sp) +{ + sp->send_buffer = NULL; + sp->send_size = 0; + sp->send_offset = 0; +} + PRInt32 sasl_io_send(PRFileDesc *fd, const void *buf, PRInt32 amount, PRIntn flags, PRIntervalTime timeout) @@ -394,27 +404,49 @@ sasl_io_send(PRFileDesc *fd, const void *buf, PRInt32 amount, PRInt32 ret = 0; sasl_io_private *sp = sasl_get_io_private(fd); Connection *c = sp->conn; - const char *crypt_buffer = NULL; - unsigned crypt_buffer_size = 0; - LDAPDebug( LDAP_DEBUG_CONNS, - "sasl_io_send writing %d bytes\n", amount, 0, 0 ); + LDAPDebug1Arg( LDAP_DEBUG_CONNS, + "sasl_io_send writing %d bytes\n", amount ); if (sp->send_encrypted) { /* Get SASL to encrypt the buffer */ - ret = sasl_encode(c->c_sasl_conn, buf, amount, &crypt_buffer, &crypt_buffer_size); - LDAPDebug( LDAP_DEBUG_CONNS, - "sasl_io_send encoded as %d bytes\n", crypt_buffer_size, 0, 0 ); - ret = PR_Send(fd->lower, crypt_buffer, crypt_buffer_size, flags, timeout); + if (NULL == sp->send_buffer) { + ret = sasl_encode(c->c_sasl_conn, buf, amount, &sp->send_buffer, &sp->send_size); + if (ret != SASL_OK) { + const char *saslerr = sasl_errdetail(c->c_sasl_conn); + LDAPDebug2Args( LDAP_DEBUG_ANY, + "sasl_io_send could not encode %d bytes - sasl error %s\n", + amount, saslerr ? saslerr : "unknown" ); + reset_send_info(sp); + PR_SetError(PR_IO_ERROR, 0); + return PR_FAILURE; + } + LDAPDebug1Arg( LDAP_DEBUG_CONNS, + "sasl_io_send encoded as %d bytes\n", sp->send_size ); + sp->send_offset = 0; + } else if ((amount > 0) && (sp->send_offset >= sp->send_size)) { + /* something went wrong - we sent too many bytes */ + LDAPDebug2Args( LDAP_DEBUG_ANY, + "sasl_io_send - client requested to send %d bytes but we " + "already sent %d bytes\n", amount, (sp->send_offset >= sp->send_size)); + reset_send_info(sp); + PR_SetError(PR_BUFFER_OVERFLOW_ERROR, EMSGSIZE); + return PR_FAILURE; + } + ret = PR_Send(fd->lower, sp->send_buffer + sp->send_offset, + sp->send_size - sp->send_offset, flags, timeout); /* we need to return the amount of cleartext sent */ - if (ret == crypt_buffer_size) { + if (ret == (sp->send_size - sp->send_offset)) { ret = amount; /* sent amount of data requested by caller */ - } else if (ret > 0) { /* could not send the entire encrypted buffer - error */ - LDAPDebug( LDAP_DEBUG_CONNS, - "sasl_io_send error: only sent %d of %d encoded bytes\n", ret, crypt_buffer_size, 0 ); + reset_send_info(sp); /* done with this buffer, ready for next buffer */ + } else if (ret > 0) { /* could not send the entire encrypted buffer - tell caller we're blocked */ + LDAPDebug2Args( LDAP_DEBUG_CONNS, + "sasl_io_send error: only sent %d of %d encoded bytes\n", ret, + (sp->send_size - sp->send_offset) ); + sp->send_offset += ret; ret = PR_FAILURE; - PR_SetError(PR_IO_ERROR, 0); + PR_SetError(PR_WOULD_BLOCK_ERROR, 0); } - /* else - ret is error */ + /* else - ret is error - caller will handle */ } else { ret = PR_Send(fd->lower, buf, amount, flags, timeout); } |