Example #1
0
int
SendReceiveBlockingLock(const unsigned int xid, struct cifsTconInfo *tcon,
	    struct smb_hdr *in_buf, struct smb_hdr *out_buf,
	    int *pbytes_returned)
{
	int rc = 0;
	int rstart = 0;
	unsigned int receive_len;
	struct mid_q_entry *midQ;
	struct cifsSesInfo *ses;

	if (tcon == NULL || tcon->ses == NULL) {
		cERROR(1, "Null smb session");
		return -EIO;
	}
	ses = tcon->ses;

	if (ses->server == NULL) {
		cERROR(1, "Null tcp session");
		return -EIO;
	}

	if (ses->server->tcpStatus == CifsExiting)
		return -ENOENT;

	/* Ensure that we do not send more than 50 overlapping requests
	   to the same server. We may make this configurable later or
	   use ses->maxReq */

	if (in_buf->smb_buf_length > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) {
		cERROR(1, "Illegal length, greater than maximum frame, %d",
			   in_buf->smb_buf_length);
		return -EIO;
	}

	rc = wait_for_free_request(ses, CIFS_BLOCKING_OP);
	if (rc)
		return rc;

	/* make sure that we sign in the same order that we send on this socket
	   and avoid races inside tcp sendmsg code that could cause corruption
	   of smb data */

	mutex_lock(&ses->server->srv_mutex);

	rc = allocate_mid(ses, in_buf, &midQ);
	if (rc) {
		mutex_unlock(&ses->server->srv_mutex);
		return rc;
	}

	rc = cifs_sign_smb(in_buf, ses->server, &midQ->sequence_number);
	if (rc) {
		DeleteMidQEntry(midQ);
		mutex_unlock(&ses->server->srv_mutex);
		return rc;
	}

	midQ->midState = MID_REQUEST_SUBMITTED;
#ifdef CONFIG_CIFS_STATS2
	atomic_inc(&ses->server->inSend);
#endif
	rc = smb_send(ses->server, in_buf, in_buf->smb_buf_length);
#ifdef CONFIG_CIFS_STATS2
	atomic_dec(&ses->server->inSend);
	midQ->when_sent = jiffies;
#endif
	mutex_unlock(&ses->server->srv_mutex);

	if (rc < 0) {
		DeleteMidQEntry(midQ);
		return rc;
	}

	/* Wait for a reply - allow signals to interrupt. */
	rc = wait_event_interruptible(ses->server->response_q,
		(!(midQ->midState == MID_REQUEST_SUBMITTED)) ||
		((ses->server->tcpStatus != CifsGood) &&
		 (ses->server->tcpStatus != CifsNew)));

	/* Were we interrupted by a signal ? */
	if ((rc == -ERESTARTSYS) &&
		(midQ->midState == MID_REQUEST_SUBMITTED) &&
		((ses->server->tcpStatus == CifsGood) ||
		 (ses->server->tcpStatus == CifsNew))) {

		if (in_buf->Command == SMB_COM_TRANSACTION2) {
			/* POSIX lock. We send a NT_CANCEL SMB to cause the
			   blocking lock to return. */

			rc = send_nt_cancel(tcon, in_buf, midQ);
			if (rc) {
				DeleteMidQEntry(midQ);
				return rc;
			}
		} else {
			/* Windows lock. We send a LOCKINGX_CANCEL_LOCK
			   to cause the blocking lock to return. */

			rc = send_lock_cancel(xid, tcon, in_buf, out_buf);

			/* If we get -ENOLCK back the lock may have
			   already been removed. Don't exit in this case. */
			if (rc && rc != -ENOLCK) {
				DeleteMidQEntry(midQ);
				return rc;
			}
		}

		/* Wait 5 seconds for the response. */
		if (wait_for_response(ses, midQ, 5 * HZ, 5 * HZ) == 0) {
			/* We got the response - restart system call. */
			rstart = 1;
		}
	}

	spin_lock(&GlobalMid_Lock);
	if (midQ->resp_buf) {
		spin_unlock(&GlobalMid_Lock);
		receive_len = midQ->resp_buf->smb_buf_length;
	} else {
		cERROR(1, "No response for cmd %d mid %d",
			  midQ->command, midQ->mid);
		if (midQ->midState == MID_REQUEST_SUBMITTED) {
			if (ses->server->tcpStatus == CifsExiting)
				rc = -EHOSTDOWN;
			else {
				ses->server->tcpStatus = CifsNeedReconnect;
				midQ->midState = MID_RETRY_NEEDED;
			}
		}

		if (rc != -EHOSTDOWN) {
			if (midQ->midState == MID_RETRY_NEEDED) {
				rc = -EAGAIN;
				cFYI(1, "marking request for retry");
			} else {
				rc = -EIO;
			}
		}
		spin_unlock(&GlobalMid_Lock);
		DeleteMidQEntry(midQ);
		return rc;
	}

	if (receive_len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE) {
		cERROR(1, "Frame too large received.  Length: %d  Xid: %d",
			receive_len, xid);
		rc = -EIO;
		goto out;
	}

	/* rcvd frame is ok */

	if ((out_buf == NULL) || (midQ->midState != MID_RESPONSE_RECEIVED)) {
		rc = -EIO;
		cERROR(1, "Bad MID state?");
		goto out;
	}

	out_buf->smb_buf_length = receive_len;
	memcpy((char *)out_buf + 4,
	       (char *)midQ->resp_buf + 4,
	       receive_len);

	dump_smb(out_buf, 92);
	/* convert the length into a more usable form */
	if ((receive_len > 24) &&
	    (ses->server->secMode & (SECMODE_SIGN_REQUIRED |
				     SECMODE_SIGN_ENABLED))) {
		rc = cifs_verify_signature(out_buf,
					   ses->server,
					   midQ->sequence_number+1);
		if (rc) {
			cERROR(1, "Unexpected SMB signature");
			/* BB FIXME add code to kill session */
		}
	}

	*pbytes_returned = out_buf->smb_buf_length;

	/* BB special case reconnect tid and uid here? */
	rc = map_smb_to_linux_error(out_buf, 0 /* no log */ );

	/* convert ByteCount if necessary */
	if (receive_len >= sizeof(struct smb_hdr) - 4
	    /* do not count RFC1001 header */  +
	    (2 * out_buf->WordCount) + 2 /* bcc */ )
		BCC(out_buf) = le16_to_cpu(BCC_LE(out_buf));

out:
	DeleteMidQEntry(midQ);
	if (rstart && rc == -EACCES)
		return -ERESTARTSYS;
	return rc;
}
Example #2
0
int
SendReceive(const unsigned int xid, struct cifsSesInfo *ses,
	    struct smb_hdr *in_buf, struct smb_hdr *out_buf,
	    int *pbytes_returned, const int long_op)
{
	int rc = 0;
	unsigned int receive_len;
	unsigned long timeout;
	struct mid_q_entry *midQ;

	if (ses == NULL) {
		cERROR(1, "Null smb session");
		return -EIO;
	}
	if (ses->server == NULL) {
		cERROR(1, "Null tcp session");
		return -EIO;
	}

	if (ses->server->tcpStatus == CifsExiting)
		return -ENOENT;

	/* Ensure that we do not send more than 50 overlapping requests
	   to the same server. We may make this configurable later or
	   use ses->maxReq */

	if (in_buf->smb_buf_length > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) {
		cERROR(1, "Illegal length, greater than maximum frame, %d",
			   in_buf->smb_buf_length);
		return -EIO;
	}

	rc = wait_for_free_request(ses, long_op);
	if (rc)
		return rc;

	/* make sure that we sign in the same order that we send on this socket
	   and avoid races inside tcp sendmsg code that could cause corruption
	   of smb data */

	mutex_lock(&ses->server->srv_mutex);

	rc = allocate_mid(ses, in_buf, &midQ);
	if (rc) {
		mutex_unlock(&ses->server->srv_mutex);
		/* Update # of requests on wire to server */
		atomic_dec(&ses->server->inFlight);
		wake_up(&ses->server->request_q);
		return rc;
	}

	rc = cifs_sign_smb(in_buf, ses->server, &midQ->sequence_number);
	if (rc) {
		mutex_unlock(&ses->server->srv_mutex);
		goto out;
	}

	midQ->midState = MID_REQUEST_SUBMITTED;
#ifdef CONFIG_CIFS_STATS2
	atomic_inc(&ses->server->inSend);
#endif
	rc = smb_send(ses->server, in_buf, in_buf->smb_buf_length);
#ifdef CONFIG_CIFS_STATS2
	atomic_dec(&ses->server->inSend);
	midQ->when_sent = jiffies;
#endif
	mutex_unlock(&ses->server->srv_mutex);

	if (rc < 0)
		goto out;

	if (long_op == CIFS_STD_OP)
		timeout = 15 * HZ;
	/* wait for 15 seconds or until woken up due to response arriving or
	   due to last connection to this server being unmounted */
	else if (long_op == CIFS_ASYNC_OP)
		goto out;
	else if (long_op == CIFS_VLONG_OP) /* writes past EOF can be slow */
		timeout = 180 * HZ;
	else if (long_op == CIFS_LONG_OP)
		timeout = 45 * HZ; /* should be greater than
			servers oplock break timeout (about 43 seconds) */
	else if (long_op == CIFS_BLOCKING_OP)
		timeout = 0x7FFFFFFF; /* large but no so large as to wrap */
	else {
		cERROR(1, "unknown timeout flag %d", long_op);
		rc = -EIO;
		goto out;
	}

	if (signal_pending(current)) {
		/* if signal pending do not hold up user for full smb timeout
		but we still give response a chance to complete */
		timeout = 2 * HZ;
	}

	/* No user interrupts in wait - wreaks havoc with performance */
	wait_for_response(ses, midQ, timeout, 10 * HZ);

	spin_lock(&GlobalMid_Lock);
	if (midQ->resp_buf == NULL) {
		cERROR(1, "No response for cmd %d mid %d",
			  midQ->command, midQ->mid);
		if (midQ->midState == MID_REQUEST_SUBMITTED) {
			if (ses->server->tcpStatus == CifsExiting)
				rc = -EHOSTDOWN;
			else {
				ses->server->tcpStatus = CifsNeedReconnect;
				midQ->midState = MID_RETRY_NEEDED;
			}
		}

		if (rc != -EHOSTDOWN) {
			if (midQ->midState == MID_RETRY_NEEDED) {
				rc = -EAGAIN;
				cFYI(1, "marking request for retry");
			} else {
				rc = -EIO;
			}
		}
		spin_unlock(&GlobalMid_Lock);
		DeleteMidQEntry(midQ);
		/* Update # of requests on wire to server */
		atomic_dec(&ses->server->inFlight);
		wake_up(&ses->server->request_q);
		return rc;
	}

	spin_unlock(&GlobalMid_Lock);
	receive_len = midQ->resp_buf->smb_buf_length;

	if (receive_len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE) {
		cERROR(1, "Frame too large received.  Length: %d  Xid: %d",
			receive_len, xid);
		rc = -EIO;
		goto out;
	}

	/* rcvd frame is ok */

	if (midQ->resp_buf && out_buf
	    && (midQ->midState == MID_RESPONSE_RECEIVED)) {
		out_buf->smb_buf_length = receive_len;
		memcpy((char *)out_buf + 4,
		       (char *)midQ->resp_buf + 4,
		       receive_len);

		dump_smb(out_buf, 92);
		/* convert the length into a more usable form */
		if ((receive_len > 24) &&
		    (ses->server->secMode & (SECMODE_SIGN_REQUIRED |
					     SECMODE_SIGN_ENABLED))) {
			rc = cifs_verify_signature(out_buf,
						ses->server,
						midQ->sequence_number+1);
			if (rc) {
				cERROR(1, "Unexpected SMB signature");
				/* BB FIXME add code to kill session */
			}
		}

		*pbytes_returned = out_buf->smb_buf_length;

		/* BB special case reconnect tid and uid here? */
		rc = map_smb_to_linux_error(out_buf, 0 /* no log */ );

		/* convert ByteCount if necessary */
		if (receive_len >= sizeof(struct smb_hdr) - 4
		    /* do not count RFC1001 header */  +
		    (2 * out_buf->WordCount) + 2 /* bcc */ )
			BCC(out_buf) = le16_to_cpu(BCC_LE(out_buf));
	} else {
		rc = -EIO;
		cERROR(1, "Bad MID state?");
	}

out:
	DeleteMidQEntry(midQ);
	atomic_dec(&ses->server->inFlight);
	wake_up(&ses->server->request_q);

	return rc;
}
Example #3
0
int
SendReceive2(const unsigned int xid, struct cifsSesInfo *ses, 
	     struct kvec *iov, int n_vec, int * pRespBufType /* ret */, 
	     const int long_op)
{
	int rc = 0;
	unsigned int receive_len;
	unsigned long timeout;
	struct mid_q_entry *midQ;
	struct smb_hdr *in_buf = iov[0].iov_base;
	
	*pRespBufType = CIFS_NO_BUFFER;  /* no response buf yet */

	if ((ses == NULL) || (ses->server == NULL)) {
		cifs_small_buf_release(in_buf);
		cERROR(1,("Null session"));
		return -EIO;
	}

	if(ses->server->tcpStatus == CifsExiting) {
		cifs_small_buf_release(in_buf);
		return -ENOENT;
	}

	/* Ensure that we do not send more than 50 overlapping requests 
	   to the same server. We may make this configurable later or
	   use ses->maxReq */

	rc = wait_for_free_request(ses, long_op);
	if (rc) {
		cifs_small_buf_release(in_buf);
		return rc;
	}

	/* make sure that we sign in the same order that we send on this socket 
	   and avoid races inside tcp sendmsg code that could cause corruption
	   of smb data */

	down(&ses->server->tcpSem); 

	rc = allocate_mid(ses, in_buf, &midQ);
	if (rc) {
		up(&ses->server->tcpSem);
		cifs_small_buf_release(in_buf);
		/* Update # of requests on wire to server */
		atomic_dec(&ses->server->inFlight); 
		wake_up(&ses->server->request_q);
		return rc;
	}

 	rc = cifs_sign_smb2(iov, n_vec, ses->server, &midQ->sequence_number);

	midQ->midState = MID_REQUEST_SUBMITTED;
#ifdef CONFIG_CIFS_STATS2
	atomic_inc(&ses->server->inSend);
#endif
	rc = smb_send2(ses->server->ssocket, iov, n_vec,
		      (struct sockaddr *) &(ses->server->addr.sockAddr));
#ifdef CONFIG_CIFS_STATS2
	atomic_dec(&ses->server->inSend);
	midQ->when_sent = jiffies;
#endif

	up(&ses->server->tcpSem);
	cifs_small_buf_release(in_buf);

	if(rc < 0)
		goto out;

	if (long_op == -1)
		goto out;
	else if (long_op == 2) /* writes past end of file can take loong time */
		timeout = 180 * HZ;
	else if (long_op == 1)
		timeout = 45 * HZ; /* should be greater than 
			servers oplock break timeout (about 43 seconds) */
	else
		timeout = 15 * HZ;

	/* wait for 15 seconds or until woken up due to response arriving or 
	   due to last connection to this server being unmounted */
	if (signal_pending(current)) {
		/* if signal pending do not hold up user for full smb timeout
		but we still give response a change to complete */
		timeout = 2 * HZ;
	}   

	/* No user interrupts in wait - wreaks havoc with performance */
	wait_for_response(ses, midQ, timeout, 10 * HZ);

	spin_lock(&GlobalMid_Lock);
	if (midQ->resp_buf) {
		spin_unlock(&GlobalMid_Lock);
		receive_len = midQ->resp_buf->smb_buf_length;
	} else {
		cERROR(1,("No response to cmd %d mid %d",
			midQ->command, midQ->mid));
		if(midQ->midState == MID_REQUEST_SUBMITTED) {
			if(ses->server->tcpStatus == CifsExiting)
				rc = -EHOSTDOWN;
			else {
				ses->server->tcpStatus = CifsNeedReconnect;
				midQ->midState = MID_RETRY_NEEDED;
			}
		}

		if (rc != -EHOSTDOWN) {
			if(midQ->midState == MID_RETRY_NEEDED) {
				rc = -EAGAIN;
				cFYI(1,("marking request for retry"));
			} else {
				rc = -EIO;
			}
		}
		spin_unlock(&GlobalMid_Lock);
		DeleteMidQEntry(midQ);
		/* Update # of requests on wire to server */
		atomic_dec(&ses->server->inFlight); 
		wake_up(&ses->server->request_q);
		return rc;
	}
  
	if (receive_len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE) {
		cERROR(1, ("Frame too large received.  Length: %d  Xid: %d",
			receive_len, xid));
		rc = -EIO;
	} else {		/* rcvd frame is ok */
		if (midQ->resp_buf && 
			(midQ->midState == MID_RESPONSE_RECEIVED)) {

			iov[0].iov_base = (char *)midQ->resp_buf;
			if(midQ->largeBuf)
				*pRespBufType = CIFS_LARGE_BUFFER;
			else
				*pRespBufType = CIFS_SMALL_BUFFER;
			iov[0].iov_len = receive_len + 4;

			dump_smb(midQ->resp_buf, 80);
			/* convert the length into a more usable form */
			if((receive_len > 24) &&
			   (ses->server->secMode & (SECMODE_SIGN_REQUIRED |
					SECMODE_SIGN_ENABLED))) {
				rc = cifs_verify_signature(midQ->resp_buf,
						ses->server->mac_signing_key,
						midQ->sequence_number+1);
				if(rc) {
					cERROR(1,("Unexpected SMB signature"));
					/* BB FIXME add code to kill session */
				}
			}

			/* BB special case reconnect tid and uid here? */
			/* BB special case Errbadpassword and pwdexpired here */
			rc = map_smb_to_linux_error(midQ->resp_buf);

			/* convert ByteCount if necessary */
			if (receive_len >=
			    sizeof (struct smb_hdr) -
			    4 /* do not count RFC1001 header */  +
			    (2 * midQ->resp_buf->WordCount) + 2 /* bcc */ )
				BCC(midQ->resp_buf) = 
					le16_to_cpu(BCC_LE(midQ->resp_buf));
			midQ->resp_buf = NULL;  /* mark it so will not be freed
						by DeleteMidQEntry */
		} else {
			rc = -EIO;
			cFYI(1,("Bad MID state?"));
		}
	}

out:

	DeleteMidQEntry(midQ);
	atomic_dec(&ses->server->inFlight); 
	wake_up(&ses->server->request_q);

	return rc;
}
Example #4
0
File: sess.c Project: 274914765/C
int
CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses, int first_time,
        const struct nls_table *nls_cp)
{
    int rc = 0;
    int wct;
    struct smb_hdr *smb_buf;
    char *bcc_ptr;
    char *str_area;
    SESSION_SETUP_ANDX *pSMB;
    __u32 capabilities;
    int count;
    int resp_buf_type;
    struct kvec iov[3];
    enum securityEnum type;
    __u16 action;
    int bytes_remaining;
    struct key *spnego_key = NULL;

    if (ses == NULL)
        return -EINVAL;

    type = ses->server->secType;

    cFYI(1, ("sess setup type %d", type));
    if (type == LANMAN) {
#ifndef CONFIG_CIFS_WEAK_PW_HASH
        /* LANMAN and plaintext are less secure and off by default.
        So we make this explicitly be turned on in kconfig (in the
        build) and turned on at runtime (changed from the default)
        in proc/fs/cifs or via mount parm.  Unfortunately this is
        needed for old Win (e.g. Win95), some obscure NAS and OS/2 */
        return -EOPNOTSUPP;
#endif
        wct = 10; /* lanman 2 style sessionsetup */
    } else if ((type == NTLM) || (type == NTLMv2)) {
        /* For NTLMv2 failures eventually may need to retry NTLM */
        wct = 13; /* old style NTLM sessionsetup */
    } else /* same size: negotiate or auth, NTLMSSP or extended security */
        wct = 12;

    rc = small_smb_init_no_tc(SMB_COM_SESSION_SETUP_ANDX, wct, ses,
                (void **)&smb_buf);
    if (rc)
        return rc;

    pSMB = (SESSION_SETUP_ANDX *)smb_buf;

    capabilities = cifs_ssetup_hdr(ses, pSMB);

    /* we will send the SMB in three pieces:
    a fixed length beginning part, an optional
    SPNEGO blob (which can be zero length), and a
    last part which will include the strings
    and rest of bcc area. This allows us to avoid
    a large buffer 17K allocation */
    iov[0].iov_base = (char *)pSMB;
    iov[0].iov_len = smb_buf->smb_buf_length + 4;

    /* setting this here allows the code at the end of the function
       to free the request buffer if there's an error */
    resp_buf_type = CIFS_SMALL_BUFFER;

    /* 2000 big enough to fit max user, domain, NOS name etc. */
    str_area = kmalloc(2000, GFP_KERNEL);
    if (str_area == NULL) {
        rc = -ENOMEM;
        goto ssetup_exit;
    }
    bcc_ptr = str_area;

    ses->flags &= ~CIFS_SES_LANMAN;

    iov[1].iov_base = NULL;
    iov[1].iov_len = 0;

    if (type == LANMAN) {
#ifdef CONFIG_CIFS_WEAK_PW_HASH
        char lnm_session_key[CIFS_SESS_KEY_SIZE];

        /* no capabilities flags in old lanman negotiation */

        pSMB->old_req.PasswordLength = cpu_to_le16(CIFS_SESS_KEY_SIZE);
        /* BB calculate hash with password */
        /* and copy into bcc */

        calc_lanman_hash(ses, lnm_session_key);
        ses->flags |= CIFS_SES_LANMAN;
        memcpy(bcc_ptr, (char *)lnm_session_key, CIFS_SESS_KEY_SIZE);
        bcc_ptr += CIFS_SESS_KEY_SIZE;

        /* can not sign if LANMAN negotiated so no need
        to calculate signing key? but what if server
        changed to do higher than lanman dialect and
        we reconnected would we ever calc signing_key? */

        cFYI(1, ("Negotiating LANMAN setting up strings"));
        /* Unicode not allowed for LANMAN dialects */
        ascii_ssetup_strings(&bcc_ptr, ses, nls_cp);
#endif
    } else if (type == NTLM) {
        char ntlm_session_key[CIFS_SESS_KEY_SIZE];

        pSMB->req_no_secext.Capabilities = cpu_to_le32(capabilities);
        pSMB->req_no_secext.CaseInsensitivePasswordLength =
            cpu_to_le16(CIFS_SESS_KEY_SIZE);
        pSMB->req_no_secext.CaseSensitivePasswordLength =
            cpu_to_le16(CIFS_SESS_KEY_SIZE);

        /* calculate session key */
        SMBNTencrypt(ses->password, ses->server->cryptKey,
                 ntlm_session_key);

        if (first_time) /* should this be moved into common code
                  with similar ntlmv2 path? */
            cifs_calculate_mac_key(&ses->server->mac_signing_key,
                ntlm_session_key, ses->password);
        /* copy session key */

        memcpy(bcc_ptr, (char *)ntlm_session_key, CIFS_SESS_KEY_SIZE);
        bcc_ptr += CIFS_SESS_KEY_SIZE;
        memcpy(bcc_ptr, (char *)ntlm_session_key, CIFS_SESS_KEY_SIZE);
        bcc_ptr += CIFS_SESS_KEY_SIZE;
        if (ses->capabilities & CAP_UNICODE) {
            /* unicode strings must be word aligned */
            if (iov[0].iov_len % 2) {
                *bcc_ptr = 0;
                bcc_ptr++;
            }
            unicode_ssetup_strings(&bcc_ptr, ses, nls_cp);
        } else
            ascii_ssetup_strings(&bcc_ptr, ses, nls_cp);
    } else if (type == NTLMv2) {
        char *v2_sess_key =
            kmalloc(sizeof(struct ntlmv2_resp), GFP_KERNEL);

        /* BB FIXME change all users of v2_sess_key to
           struct ntlmv2_resp */

        if (v2_sess_key == NULL) {
            rc = -ENOMEM;
            goto ssetup_exit;
        }

        pSMB->req_no_secext.Capabilities = cpu_to_le32(capabilities);

        /* LM2 password would be here if we supported it */
        pSMB->req_no_secext.CaseInsensitivePasswordLength = 0;
        /*    cpu_to_le16(LM2_SESS_KEY_SIZE); */

        pSMB->req_no_secext.CaseSensitivePasswordLength =
            cpu_to_le16(sizeof(struct ntlmv2_resp));

        /* calculate session key */
        setup_ntlmv2_rsp(ses, v2_sess_key, nls_cp);
        if (first_time) /* should this be moved into common code
                   with similar ntlmv2 path? */
        /*   cifs_calculate_ntlmv2_mac_key(ses->server->mac_signing_key,
                response BB FIXME, v2_sess_key); */

        /* copy session key */

    /*    memcpy(bcc_ptr, (char *)ntlm_session_key,LM2_SESS_KEY_SIZE);
        bcc_ptr += LM2_SESS_KEY_SIZE; */
        memcpy(bcc_ptr, (char *)v2_sess_key,
               sizeof(struct ntlmv2_resp));
        bcc_ptr += sizeof(struct ntlmv2_resp);
        kfree(v2_sess_key);
        if (ses->capabilities & CAP_UNICODE) {
            if (iov[0].iov_len % 2) {
                *bcc_ptr = 0;
                bcc_ptr++;
            }
            unicode_ssetup_strings(&bcc_ptr, ses, nls_cp);
        } else
            ascii_ssetup_strings(&bcc_ptr, ses, nls_cp);
    } else if (type == Kerberos) {
#ifdef CONFIG_CIFS_UPCALL
        struct cifs_spnego_msg *msg;
        spnego_key = cifs_get_spnego_key(ses);
        if (IS_ERR(spnego_key)) {
            rc = PTR_ERR(spnego_key);
            spnego_key = NULL;
            goto ssetup_exit;
        }

        msg = spnego_key->payload.data;
        /* bail out if key is too long */
        if (msg->sesskey_len >
            sizeof(ses->server->mac_signing_key.data.krb5)) {
            cERROR(1, ("Kerberos signing key too long (%u bytes)",
                msg->sesskey_len));
            rc = -EOVERFLOW;
            goto ssetup_exit;
        }
        if (first_time) {
            ses->server->mac_signing_key.len = msg->sesskey_len;
            memcpy(ses->server->mac_signing_key.data.krb5,
                msg->data, msg->sesskey_len);
        }
        pSMB->req.hdr.Flags2 |= SMBFLG2_EXT_SEC;
        capabilities |= CAP_EXTENDED_SECURITY;
        pSMB->req.Capabilities = cpu_to_le32(capabilities);
        iov[1].iov_base = msg->data + msg->sesskey_len;
        iov[1].iov_len = msg->secblob_len;
        pSMB->req.SecurityBlobLength = cpu_to_le16(iov[1].iov_len);

        if (ses->capabilities & CAP_UNICODE) {
            /* unicode strings must be word aligned */
            if ((iov[0].iov_len + iov[1].iov_len) % 2) {
                *bcc_ptr = 0;
                bcc_ptr++;
            }
            unicode_oslm_strings(&bcc_ptr, nls_cp);
            unicode_domain_string(&bcc_ptr, ses, nls_cp);
        } else
        /* BB: is this right? */
            ascii_ssetup_strings(&bcc_ptr, ses, nls_cp);
#else /* ! CONFIG_CIFS_UPCALL */
        cERROR(1, ("Kerberos negotiated but upcall support disabled!"));
        rc = -ENOSYS;
        goto ssetup_exit;
#endif /* CONFIG_CIFS_UPCALL */
    } else {
        cERROR(1, ("secType %d not supported!", type));
        rc = -ENOSYS;
        goto ssetup_exit;
    }

    iov[2].iov_base = str_area;
    iov[2].iov_len = (long) bcc_ptr - (long) str_area;

    count = iov[1].iov_len + iov[2].iov_len;
    smb_buf->smb_buf_length += count;

    BCC_LE(smb_buf) = cpu_to_le16(count);

    rc = SendReceive2(xid, ses, iov, 3 /* num_iovecs */, &resp_buf_type,
              CIFS_STD_OP /* not long */ | CIFS_LOG_ERROR);
    /* SMB request buf freed in SendReceive2 */

    cFYI(1, ("ssetup rc from sendrecv2 is %d", rc));
    if (rc)
        goto ssetup_exit;

    pSMB = (SESSION_SETUP_ANDX *)iov[0].iov_base;
    smb_buf = (struct smb_hdr *)iov[0].iov_base;

    if ((smb_buf->WordCount != 3) && (smb_buf->WordCount != 4)) {
        rc = -EIO;
        cERROR(1, ("bad word count %d", smb_buf->WordCount));
        goto ssetup_exit;
    }
    action = le16_to_cpu(pSMB->resp.Action);
    if (action & GUEST_LOGIN)
        cFYI(1, ("Guest login")); /* BB mark SesInfo struct? */
    ses->Suid = smb_buf->Uid;   /* UID left in wire format (le) */
    cFYI(1, ("UID = %d ", ses->Suid));
    /* response can have either 3 or 4 word count - Samba sends 3 */
    /* and lanman response is 3 */
    bytes_remaining = BCC(smb_buf);
    bcc_ptr = pByteArea(smb_buf);

    if (smb_buf->WordCount == 4) {
        __u16 blob_len;
        blob_len = le16_to_cpu(pSMB->resp.SecurityBlobLength);
        bcc_ptr += blob_len;
        if (blob_len > bytes_remaining) {
            cERROR(1, ("bad security blob length %d", blob_len));
            rc = -EINVAL;
            goto ssetup_exit;
        }
        bytes_remaining -= blob_len;
    }

    /* BB check if Unicode and decode strings */
    if (smb_buf->Flags2 & SMBFLG2_UNICODE)
        rc = decode_unicode_ssetup(&bcc_ptr, bytes_remaining,
                           ses, nls_cp);
    else
        rc = decode_ascii_ssetup(&bcc_ptr, bytes_remaining,
                     ses, nls_cp);

ssetup_exit:
    if (spnego_key)
        key_put(spnego_key);
    kfree(str_area);
    if (resp_buf_type == CIFS_SMALL_BUFFER) {
        cFYI(1, ("ssetup freeing small buf %p", iov[0].iov_base));
        cifs_small_buf_release(iov[0].iov_base);
    } else if (resp_buf_type == CIFS_LARGE_BUFFER)
        cifs_buf_release(iov[0].iov_base);

    return rc;
}
Example #5
0
unsigned int
smbCalcSize_LE(struct smb_hdr *ptr)
{
	return (sizeof(struct smb_hdr) + (2 * ptr->WordCount) +
		2 /* size of the bcc field */ + le16_to_cpu(BCC_LE(ptr)));
}