Index: smbd/sesssetup.c =================================================================== --- smbd/sesssetup.c (revision 21443) +++ smbd/sesssetup.c (working copy) @@ -659,6 +659,105 @@ } /**************************************************************************** + Check the size of an SPNEGO blob. If we need more return NT_STATUS_MORE_PROCESSING_REQUIRED, + else return NT_STATUS_OK. +****************************************************************************/ + +NTSTATUS check_spnego_blob_complete(user_struct *vuser, DATA_BLOB *pblob) +{ + ASN1_DATA data; + size_t needed_len = 0; + + /* Ensure we have some data. */ + if (pblob->length == 0) { + /* Caller can cope. */ + return NT_STATUS_OK; + } + + /* Were we waiting for more data ? */ + if (vuser->pending_auth) { + struct pending_auth_data *pa = vuser->pending_auth; + DATA_BLOB tmp_blob = data_blob(NULL, + pa->partial_data.length + pblob->length); + + /* Concatenate the two. */ + memcpy(tmp_blob.data, + pa->partial_data.data, + pa->partial_data.length); + memcpy(tmp_blob.data + pa->partial_data.length, + pblob->data, + pblob->length); + + /* Replace the partial data. */ + data_blob_free(&pa->partial_data); + pa->partial_data = tmp_blob; + ZERO_STRUCT(tmp_blob); + + /* Are we done ? */ + if (pblob->length >= pa->needed_len) { + /* Yes, replace pblob. */ + data_blob_free(pblob); + *pblob = pa->partial_data; + ZERO_STRUCT(pa->partial_data); + SAFE_FREE(vuser->pending_auth); + return NT_STATUS_OK; + } + + /* Still need more data. */ + pa->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) { + 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); + + if (needed_len <= pblob->length) { + /* Nothing to do - blob is complete. */ + return NT_STATUS_OK; + } + + /* We must store this blob until complete. */ + vuser->pending_auth = SMB_MALLOC(sizeof(struct pending_auth_data)); + if (!vuser->pending_auth) { + return NT_STATUS_NO_MEMORY; + } + vuser->pending_auth->needed_len = needed_len; + vuser->pending_auth->partial_data = data_blob(pblob->data, pblob->length); + return NT_STATUS_MORE_PROCESSING_REQUIRED; +} + +/**************************************************************************** Reply to a session setup command. conn POINTER CAN BE NULL HERE ! ****************************************************************************/ @@ -677,6 +776,7 @@ enum remote_arch_types ra_type = get_remote_arch(); int vuid = SVAL(inbuf,smb_uid); user_struct *vuser = NULL; + NTSTATUS status = NT_STATUS_OK; DEBUG(3,("Doing spnego session setup\n")); @@ -736,7 +836,22 @@ } 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(vuser, &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 +870,24 @@ 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)); + 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; } Index: smbd/password.c =================================================================== --- smbd/password.c (revision 21443) +++ smbd/password.c (working copy) @@ -111,6 +111,12 @@ SAFE_FREE(vuser->groups); TALLOC_FREE(vuser->nt_user_token); + + if (vuser->pending_auth) { + data_blob_free(&vuser->pending_auth->partial_data); + SAFE_FREE(vuser->pending_auth); + } + SAFE_FREE(vuser); num_validated_vuids--; } Index: libsmb/asn1.c =================================================================== --- libsmb/asn1.c (revision 21443) +++ libsmb/asn1.c (working copy) @@ -23,6 +23,13 @@ /* 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; + }; SAFE_FREE(data->data); } Index: include/smb.h =================================================================== --- include/smb.h (revision 21443) +++ include/smb.h (working copy) @@ -1728,6 +1728,11 @@ fstring password; }; +struct pending_auth_data { + size_t needed_len; + DATA_BLOB partial_data; +}; + typedef struct user_struct { struct user_struct *next, *prev; uint16 vuid; /* Tag for this entry. */ @@ -1758,6 +1763,8 @@ struct auth_ntlmssp_state *auth_ntlmssp_state; + struct pending_auth_data *pending_auth; + } user_struct; struct unix_error_map {