summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRich Megginson <rmeggins@redhat.com>2009-08-20 12:59:08 -0600
committerRich Megginson <rmeggins@redhat.com>2009-08-21 17:56:43 -0600
commit0d1f300d0e4e41d96f3022c5c80bfcb34507f5b5 (patch)
treee9f551386786af97cc6e3d7742348535f1cd2d62
parent99b41607701765fd4c8c93a1183507db78d785fa (diff)
downloadds-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.c14
-rw-r--r--ldap/servers/slapd/sasl_io.c82
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);
}