summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJeremy Allison <jra@samba.org>2007-02-20 06:22:20 +0000
committerGerald (Jerry) Carter <jerry@samba.org>2007-10-10 12:18:06 -0500
commitf77bdcf6c71a9d0274d96298b136300d5bb05bb1 (patch)
tree1234d128f0e1af6e970ecac41c17677cb6448da2
parent83dce235507cab7fef284e012982f5665aff55c4 (diff)
downloadsamba-f77bdcf6c71a9d0274d96298b136300d5bb05bb1.tar.gz
samba-f77bdcf6c71a9d0274d96298b136300d5bb05bb1.tar.bz2
samba-f77bdcf6c71a9d0274d96298b136300d5bb05bb1.zip
r21460: Fix for server-side processing of SPNEGO auth
fragmented into "max xmit" size security blob chunks. Bug #4400. Needs limits adding, and also a client-side version. Jeremy. (This used to be commit aa69f2481aafee5dccc3783b8a6e23ca4eb0dbfa)
-rw-r--r--source3/include/smb.h10
-rw-r--r--source3/libsmb/asn1.c8
-rw-r--r--source3/smbd/password.c1
-rw-r--r--source3/smbd/sesssetup.c226
4 files changed, 234 insertions, 11 deletions
diff --git a/source3/include/smb.h b/source3/include/smb.h
index cc03722c2b..cea7638d79 100644
--- a/source3/include/smb.h
+++ b/source3/include/smb.h
@@ -1719,6 +1719,15 @@ struct pwd_info {
fstring password;
};
+/* For split krb5 SPNEGO blobs. */
+struct pending_auth_data {
+ struct pending_auth_data *prev, *next;
+ uint16 vuid; /* Tag for this entry. */
+ uint16 smbpid; /* Alternate tag for this entry. */
+ size_t needed_len;
+ DATA_BLOB partial_data;
+};
+
typedef struct user_struct {
struct user_struct *next, *prev;
uint16 vuid; /* Tag for this entry. */
@@ -1748,7 +1757,6 @@ typedef struct user_struct {
struct auth_serversupplied_info *server_info;
struct auth_ntlmssp_state *auth_ntlmssp_state;
-
} user_struct;
struct unix_error_map {
diff --git a/source3/libsmb/asn1.c b/source3/libsmb/asn1.c
index 937e063c62..6ebe9ab62c 100644
--- a/source3/libsmb/asn1.c
+++ b/source3/libsmb/asn1.c
@@ -23,6 +23,14 @@
/* free an asn1 structure */
void asn1_free(ASN1_DATA *data)
{
+ struct nesting *nesting = data->nesting;
+
+ while (nesting) {
+ struct nesting *nnext = nesting->next;
+ free(nesting);
+ nesting = nnext;
+ };
+ data->nesting = NULL;
SAFE_FREE(data->data);
}
diff --git a/source3/smbd/password.c b/source3/smbd/password.c
index 10cb920237..bf4e9258ff 100644
--- a/source3/smbd/password.c
+++ b/source3/smbd/password.c
@@ -111,6 +111,7 @@ void invalidate_vuid(uint16 vuid)
SAFE_FREE(vuser->groups);
TALLOC_FREE(vuser->nt_user_token);
+
SAFE_FREE(vuser);
num_validated_vuids--;
}
diff --git a/source3/smbd/sesssetup.c b/source3/smbd/sesssetup.c
index 6c5e8f678f..9b9ae4c353 100644
--- a/source3/smbd/sesssetup.c
+++ b/source3/smbd/sesssetup.c
@@ -659,6 +659,183 @@ static int reply_spnego_auth(connection_struct *conn, char *inbuf, char *outbuf,
}
/****************************************************************************
+ List to store partial SPNEGO auth fragments.
+****************************************************************************/
+
+static struct pending_auth_data *pd_list;
+
+/****************************************************************************
+ Delete an entry on the list.
+****************************************************************************/
+
+static void delete_partial_auth(struct pending_auth_data *pad)
+{
+ DLIST_REMOVE(pd_list, pad);
+ data_blob_free(&pad->partial_data);
+ SAFE_FREE(pad);
+}
+
+/****************************************************************************
+ Search for a partial SPNEGO auth fragment matching an smbpid.
+****************************************************************************/
+
+static struct pending_auth_data *get_pending_auth_data(uint16 smbpid)
+{
+ struct pending_auth_data *pad;
+
+ for (pad = pd_list; pad; pad = pad->next) {
+ if (pad->smbpid == smbpid) {
+ break;
+ }
+ }
+ return pad;
+}
+
+/****************************************************************************
+ Check the size of an SPNEGO blob. If we need more return NT_STATUS_MORE_PROCESSING_REQUIRED,
+ else return NT_STATUS_OK.
+****************************************************************************/
+
+static NTSTATUS check_spnego_blob_complete(uint16 smbpid, uint16 vuid, DATA_BLOB *pblob)
+{
+ struct pending_auth_data *pad;
+ ASN1_DATA data;
+ size_t needed_len = 0;
+
+ /* Ensure we have some data. */
+ if (pblob->length == 0) {
+ /* Caller can cope. */
+ DEBUG(2,("check_spnego_blob_complete: zero blob length !\n"));
+ delete_partial_auth(pad);
+ return NT_STATUS_OK;
+ }
+
+ pad = get_pending_auth_data(smbpid);
+
+ /* Were we waiting for more data ? */
+ if (pad) {
+ DATA_BLOB tmp_blob;
+
+ /* Integer wrap paranoia.... */
+
+ if (pad->partial_data.length + pblob->length < pad->partial_data.length ||
+ pad->partial_data.length + pblob->length < pblob->length) {
+
+ DEBUG(2,("check_spnego_blob_complete: integer wrap "
+ "pad->partial_data.length = %u, "
+ "pblob->length = %u\n",
+ (unsigned int)pad->partial_data.length,
+ (unsigned int)pblob->length ));
+
+ delete_partial_auth(pad);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ DEBUG(10,("check_spnego_blob_complete: "
+ "pad->partial_data.length = %u, "
+ "pad->needed_len = %u, "
+ "pblob->length = %u,\n",
+ (unsigned int)pad->partial_data.length,
+ (unsigned int)pad->needed_len,
+ (unsigned int)pblob->length ));
+
+ tmp_blob = data_blob(NULL,
+ pad->partial_data.length + pblob->length);
+
+ /* Concatenate the two. */
+ memcpy(tmp_blob.data,
+ pad->partial_data.data,
+ pad->partial_data.length);
+ memcpy(tmp_blob.data + pad->partial_data.length,
+ pblob->data,
+ pblob->length);
+
+ /* Replace the partial data. */
+ data_blob_free(&pad->partial_data);
+ pad->partial_data = tmp_blob;
+ ZERO_STRUCT(tmp_blob);
+
+ /* Are we done ? */
+ if (pblob->length >= pad->needed_len) {
+ /* Yes, replace pblob. */
+ data_blob_free(pblob);
+ *pblob = pad->partial_data;
+ ZERO_STRUCT(pad->partial_data);
+ delete_partial_auth(pad);
+ return NT_STATUS_OK;
+ }
+
+ /* Still need more data. */
+ pad->needed_len -= pblob->length;
+ return NT_STATUS_MORE_PROCESSING_REQUIRED;
+ }
+
+ if ((pblob->data[0] != ASN1_APPLICATION(0)) &&
+ (pblob->data[0] != ASN1_CONTEXT(1))) {
+ /* Not something we can determine the
+ * length of.
+ */
+ return NT_STATUS_OK;
+ }
+
+ /* This is a new SPNEGO sessionsetup - see if
+ * the data given in this blob is enough.
+ */
+
+ asn1_load(&data, *pblob);
+ asn1_start_tag(&data, pblob->data[0]);
+ if (data.has_error || data.nesting == NULL) {
+ asn1_free(&data);
+ /* Let caller catch. */
+ return NT_STATUS_OK;
+ }
+
+ /* Integer wrap paranoia.... */
+
+ if (data.nesting->taglen + data.nesting->start < data.nesting->taglen ||
+ data.nesting->taglen + data.nesting->start < data.nesting->start) {
+
+ DEBUG(2,("check_spnego_blob_complete: integer wrap "
+ "data.nesting->taglen = %u, "
+ "data.nesting->start = %u\n",
+ (unsigned int)data.nesting->taglen,
+ (unsigned int)data.nesting->start ));
+
+ asn1_free(&data);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* Total length of the needed asn1 is the tag length
+ * plus the current offset. */
+
+ needed_len = data.nesting->taglen + data.nesting->start;
+ asn1_free(&data);
+
+ DEBUG(10,("check_spnego_blob_complete: needed_len = %u, "
+ "pblob->length = %u\n",
+ (unsigned int)needed_len,
+ (unsigned int)pblob->length ));
+
+ if (needed_len <= pblob->length) {
+ /* Nothing to do - blob is complete. */
+ return NT_STATUS_OK;
+ }
+
+ /* We must store this blob until complete. */
+ pad = SMB_MALLOC(sizeof(struct pending_auth_data));
+ if (!pad) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ pad->needed_len = needed_len - pblob->length;
+ pad->partial_data = data_blob(pblob->data, pblob->length);
+ pad->smbpid = smbpid;
+ pad->vuid = vuid;
+ DLIST_ADD(pd_list, pad);
+
+ return NT_STATUS_MORE_PROCESSING_REQUIRED;
+}
+
+/****************************************************************************
Reply to a session setup command.
conn POINTER CAN BE NULL HERE !
****************************************************************************/
@@ -677,6 +854,8 @@ static int reply_sesssetup_and_X_spnego(connection_struct *conn, char *inbuf,
enum remote_arch_types ra_type = get_remote_arch();
int vuid = SVAL(inbuf,smb_uid);
user_struct *vuser = NULL;
+ NTSTATUS status = NT_STATUS_OK;
+ uint16 smbpid = SVAL(inbuf,smb_pid);
DEBUG(3,("Doing spnego session setup\n"));
@@ -715,16 +894,28 @@ static int reply_sesssetup_and_X_spnego(connection_struct *conn, char *inbuf,
/* Windows 2003 doesn't set the native lanman string,
but does set primary domain which is a bug I think */
- if ( !strlen(native_lanman) )
+ if ( !strlen(native_lanman) ) {
ra_lanman_string( primary_domain );
- else
+ } else {
ra_lanman_string( native_lanman );
+ }
}
vuser = get_partial_auth_user_struct(vuid);
if (!vuser) {
+ struct pending_auth_data *pad = get_pending_auth_data(smbpid);
+ if (pad) {
+ DEBUG(10,("reply_sesssetup_and_X_spnego: found pending vuid %u\n",
+ (unsigned int)pad->vuid ));
+ vuid = pad->vuid;
+ vuser = get_partial_auth_user_struct(vuid);
+ }
+ }
+
+ if (!vuser) {
vuid = register_vuid(NULL, data_blob(NULL, 0), data_blob(NULL, 0), NULL);
if (vuid == UID_FIELD_INVALID ) {
+ data_blob_free(&blob1);
return ERROR_NT(nt_status_squash(NT_STATUS_INVALID_PARAMETER));
}
@@ -732,11 +923,27 @@ static int reply_sesssetup_and_X_spnego(connection_struct *conn, char *inbuf,
}
if (!vuser) {
+ data_blob_free(&blob1);
return ERROR_NT(nt_status_squash(NT_STATUS_INVALID_PARAMETER));
}
SSVAL(outbuf,smb_uid,vuid);
-
+
+ /* Large (greater than 4k) SPNEGO blobs are split into multiple
+ * sessionsetup requests as the Windows limit on the security blob
+ * field is 4k. Bug #4400. JRA.
+ */
+
+ status = check_spnego_blob_complete(smbpid, vuid, &blob1);
+ if (!NT_STATUS_IS_OK(status)) {
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ /* Real error - kill the intermediate vuid */
+ invalidate_vuid(vuid);
+ }
+ data_blob_free(&blob1);
+ return ERROR_NT(nt_status_squash(status));
+ }
+
if (blob1.data[0] == ASN1_APPLICATION(0)) {
/* its a negTokenTarg packet */
ret = reply_spnego_negotiate(conn, inbuf, outbuf, vuid, length, bufsize, blob1,
@@ -755,25 +962,24 @@ static int reply_sesssetup_and_X_spnego(connection_struct *conn, char *inbuf,
if (strncmp((char *)(blob1.data), "NTLMSSP", 7) == 0) {
DATA_BLOB chal;
- NTSTATUS nt_status;
if (!vuser->auth_ntlmssp_state) {
- nt_status = auth_ntlmssp_start(&vuser->auth_ntlmssp_state);
- if (!NT_STATUS_IS_OK(nt_status)) {
+ status = auth_ntlmssp_start(&vuser->auth_ntlmssp_state);
+ if (!NT_STATUS_IS_OK(status)) {
/* Kill the intermediate vuid */
invalidate_vuid(vuid);
-
- return ERROR_NT(nt_status_squash(nt_status));
+ data_blob_free(&blob1);
+ return ERROR_NT(nt_status_squash(status));
}
}
- nt_status = auth_ntlmssp_update(vuser->auth_ntlmssp_state,
+ status = auth_ntlmssp_update(vuser->auth_ntlmssp_state,
blob1, &chal);
data_blob_free(&blob1);
reply_spnego_ntlmssp(conn, inbuf, outbuf, vuid,
&vuser->auth_ntlmssp_state,
- &chal, nt_status, False);
+ &chal, status, False);
data_blob_free(&chal);
return -1;
}