cli_pipe.c

来自「samba-3.0.22.tar.gz 编译smb服务器的源码」· C语言 代码 · 共 2,128 行 · 第 1/5 页

C
2,128
字号
/*  *  Unix SMB/CIFS implementation. *  RPC Pipe client / server routines *  Largely rewritten by Jeremy Allison		    2005. *   *  This program is free software; you can redistribute it and/or modify *  it under the terms of the GNU General Public License as published by *  the Free Software Foundation; either version 2 of the License, or *  (at your option) any later version. *   *  This program is distributed in the hope that it will be useful, *  but WITHOUT ANY WARRANTY; without even the implied warranty of *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the *  GNU General Public License for more details. *   *  You should have received a copy of the GNU General Public License *  along with this program; if not, write to the Free Software *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */#include "includes.h"#undef DBGC_CLASS#define DBGC_CLASS DBGC_RPC_CLIextern struct pipe_id_info pipe_names[];/******************************************************************** Map internal value to wire value. ********************************************************************/static int map_pipe_auth_type_to_rpc_auth_type(enum pipe_auth_type auth_type){	switch (auth_type) {	case PIPE_AUTH_TYPE_NONE:		return RPC_ANONYMOUS_AUTH_TYPE;	case PIPE_AUTH_TYPE_NTLMSSP:		return RPC_NTLMSSP_AUTH_TYPE;	case PIPE_AUTH_TYPE_SPNEGO_NTLMSSP:	case PIPE_AUTH_TYPE_SPNEGO_KRB5:		return RPC_SPNEGO_AUTH_TYPE;	case PIPE_AUTH_TYPE_SCHANNEL:		return RPC_SCHANNEL_AUTH_TYPE;	case PIPE_AUTH_TYPE_KRB5:		return RPC_KRB5_AUTH_TYPE;	default:		DEBUG(0,("map_pipe_auth_type_to_rpc_type: unknown pipe "			"auth type %u\n",			(unsigned int)auth_type ));		break;	}	return -1;}/******************************************************************** Rpc pipe call id. ********************************************************************/static uint32 get_rpc_call_id(void){	static uint32 call_id = 0;	return ++call_id;}/******************************************************************* Use SMBreadX to get rest of one fragment's worth of rpc data. Will expand the current_pdu struct to the correct size. ********************************************************************/static NTSTATUS rpc_read(struct rpc_pipe_client *cli,			prs_struct *current_pdu,			uint32 data_to_read,			uint32 *current_pdu_offset){	size_t size = (size_t)cli->max_recv_frag;	uint32 stream_offset = 0;	ssize_t num_read;	char *pdata;	ssize_t extra_data_size = ((ssize_t)*current_pdu_offset) + ((ssize_t)data_to_read) - (ssize_t)prs_data_size(current_pdu);	DEBUG(5,("rpc_read: data_to_read: %u current_pdu offset: %u extra_data_size: %d\n",		(unsigned int)data_to_read, (unsigned int)*current_pdu_offset, (int)extra_data_size ));	/*	 * Grow the buffer if needed to accommodate the data to be read.	 */	if (extra_data_size > 0) {		if(!prs_force_grow(current_pdu, (uint32)extra_data_size)) {			DEBUG(0,("rpc_read: Failed to grow parse struct by %d bytes.\n", (int)extra_data_size ));			return NT_STATUS_NO_MEMORY;		}		DEBUG(5,("rpc_read: grew buffer by %d bytes to %u\n", (int)extra_data_size, prs_data_size(current_pdu) ));	}	pdata = prs_data_p(current_pdu) + *current_pdu_offset;	do {		/* read data using SMBreadX */		if (size > (size_t)data_to_read) {			size = (size_t)data_to_read;		}		num_read = cli_read(cli->cli, cli->fnum, pdata,					 (off_t)stream_offset, size);		DEBUG(5,("rpc_read: num_read = %d, read offset: %u, to read: %u\n",			(int)num_read, (unsigned int)stream_offset, (unsigned int)data_to_read));        	/*	         * A dos error of ERRDOS/ERRmoredata is not an error.		 */		if (cli_is_dos_error(cli->cli)) {			uint32 ecode;			uint8 eclass;			cli_dos_error(cli->cli, &eclass, &ecode);			if (eclass != ERRDOS && ecode != ERRmoredata) {				DEBUG(0,("rpc_read: DOS Error %d/%u (%s) in cli_read on pipe %s\n",					eclass, (unsigned int)ecode,					cli_errstr(cli->cli),					cli->pipe_name ));				return dos_to_ntstatus(eclass, ecode);			}		}        	/*	         * Likewise for NT_STATUS_BUFFER_TOO_SMALL		 */		if (cli_is_nt_error(cli->cli)) {			if (!NT_STATUS_EQUAL(cli_nt_error(cli->cli), NT_STATUS_BUFFER_TOO_SMALL)) {				DEBUG(0,("rpc_read: Error (%s) in cli_read on pipe %s\n",					nt_errstr(cli_nt_error(cli->cli)),					cli->pipe_name ));				return cli_nt_error(cli->cli);			}		}		if (num_read == -1) {			DEBUG(0,("rpc_read: Error - cli_read on pipe %s returned -1\n",				cli->pipe_name ));			return cli_get_nt_error(cli->cli);		}		data_to_read -= num_read;		stream_offset += num_read;		pdata += num_read;	} while (num_read > 0 && data_to_read > 0);	/* && err == (0x80000000 | STATUS_BUFFER_OVERFLOW)); */	/*	 * Update the current offset into current_pdu by the amount read.	 */	*current_pdu_offset += stream_offset;	return NT_STATUS_OK;}/**************************************************************************** Try and get a PDU's worth of data from current_pdu. If not, then read more from the wire. ****************************************************************************/static NTSTATUS cli_pipe_get_current_pdu(struct rpc_pipe_client *cli, RPC_HDR *prhdr, prs_struct *current_pdu){	NTSTATUS ret = NT_STATUS_OK;	uint32 current_pdu_len = prs_data_size(current_pdu);	/* Ensure we have at least RPC_HEADER_LEN worth of data to parse. */	if (current_pdu_len < RPC_HEADER_LEN) {		/* rpc_read expands the current_pdu struct as neccessary. */		ret = rpc_read(cli, current_pdu, RPC_HEADER_LEN - current_pdu_len, &current_pdu_len);		if (!NT_STATUS_IS_OK(ret)) {			return ret;		}	}	/* This next call sets the endian bit correctly in current_pdu. */	/* We will propagate this to rbuf later. */	if(!smb_io_rpc_hdr("rpc_hdr   ", prhdr, current_pdu, 0)) {		DEBUG(0,("cli_pipe_get_current_pdu: Failed to unmarshall RPC_HDR.\n"));		return NT_STATUS_BUFFER_TOO_SMALL;	}	/* Ensure we have frag_len bytes of data. */	if (current_pdu_len < prhdr->frag_len) {		/* rpc_read expands the current_pdu struct as neccessary. */		ret = rpc_read(cli, current_pdu, (uint32)prhdr->frag_len - current_pdu_len, &current_pdu_len);		if (!NT_STATUS_IS_OK(ret)) {			return ret;		}	}	if (current_pdu_len < prhdr->frag_len) {		return NT_STATUS_BUFFER_TOO_SMALL;	}	return NT_STATUS_OK;}/**************************************************************************** NTLMSSP specific sign/seal. Virtually identical to rpc_server/srv_pipe.c:api_pipe_ntlmssp_auth_process. In fact I should probably abstract these into identical pieces of code... JRA. ****************************************************************************/static NTSTATUS cli_pipe_verify_ntlmssp(struct rpc_pipe_client *cli, RPC_HDR *prhdr,				prs_struct *current_pdu,				uint8 *p_ss_padding_len){	RPC_HDR_AUTH auth_info;	uint32 save_offset = prs_offset(current_pdu);	uint32 auth_len = prhdr->auth_len;	NTLMSSP_STATE *ntlmssp_state = cli->auth.a_u.ntlmssp_state;	unsigned char *data = NULL;	size_t data_len;	unsigned char *full_packet_data = NULL;	size_t full_packet_data_len;	DATA_BLOB auth_blob;	NTSTATUS status;	if (cli->auth.auth_level == PIPE_AUTH_LEVEL_NONE || cli->auth.auth_level == PIPE_AUTH_LEVEL_CONNECT) {		return NT_STATUS_OK;	}	if (!ntlmssp_state) {		return NT_STATUS_INVALID_PARAMETER;	}	/* Ensure there's enough data for an authenticated response. */	if ((auth_len > RPC_MAX_SIGN_SIZE) ||			(RPC_HEADER_LEN + RPC_HDR_RESP_LEN + RPC_HDR_AUTH_LEN + auth_len > prhdr->frag_len)) {		DEBUG(0,("cli_pipe_verify_ntlmssp: auth_len %u is too large.\n",			(unsigned int)auth_len ));		return NT_STATUS_BUFFER_TOO_SMALL;	}	/*	 * We need the full packet data + length (minus auth stuff) as well as the packet data + length	 * after the RPC header.	 * We need to pass in the full packet (minus auth len) to the NTLMSSP sign and check seal	 * functions as NTLMv2 checks the rpc headers also.	 */	data = (unsigned char *)(prs_data_p(current_pdu) + RPC_HEADER_LEN + RPC_HDR_RESP_LEN);	data_len = (size_t)(prhdr->frag_len - RPC_HEADER_LEN - RPC_HDR_RESP_LEN - RPC_HDR_AUTH_LEN - auth_len);	full_packet_data = (unsigned char *)prs_data_p(current_pdu);	full_packet_data_len = prhdr->frag_len - auth_len;	/* Pull the auth header and the following data into a blob. */	if(!prs_set_offset(current_pdu, RPC_HEADER_LEN + RPC_HDR_RESP_LEN + data_len)) {		DEBUG(0,("cli_pipe_verify_ntlmssp: cannot move offset to %u.\n",			(unsigned int)RPC_HEADER_LEN + (unsigned int)RPC_HDR_RESP_LEN + (unsigned int)data_len ));		return NT_STATUS_BUFFER_TOO_SMALL;	}	if(!smb_io_rpc_hdr_auth("hdr_auth", &auth_info, current_pdu, 0)) {		DEBUG(0,("cli_pipe_verify_ntlmssp: failed to unmarshall RPC_HDR_AUTH.\n"));		return NT_STATUS_BUFFER_TOO_SMALL;	}	auth_blob.data = (unsigned char *)prs_data_p(current_pdu) + prs_offset(current_pdu);	auth_blob.length = auth_len;	switch (cli->auth.auth_level) {		case PIPE_AUTH_LEVEL_PRIVACY:			/* Data is encrypted. */			status = ntlmssp_unseal_packet(ntlmssp_state,							data, data_len,							full_packet_data,							full_packet_data_len,							&auth_blob);			if (!NT_STATUS_IS_OK(status)) {				DEBUG(0,("cli_pipe_verify_ntlmssp: failed to unseal "					"packet from remote machine %s on pipe %s "					"fnum 0x%x. Error was %s.\n",					cli->cli->desthost,					cli->pipe_name,					(unsigned int)cli->fnum,					nt_errstr(status) ));				return status;			}			break;		case PIPE_AUTH_LEVEL_INTEGRITY:			/* Data is signed. */			status = ntlmssp_check_packet(ntlmssp_state,							data, data_len,							full_packet_data,							full_packet_data_len,							&auth_blob);			if (!NT_STATUS_IS_OK(status)) {				DEBUG(0,("cli_pipe_verify_ntlmssp: check signing failed on "					"packet from remote machine %s on pipe %s "					"fnum 0x%x. Error was %s.\n",					cli->cli->desthost,					cli->pipe_name,					(unsigned int)cli->fnum,					nt_errstr(status) ));				return status;			}			break;		default:			DEBUG(0,("cli_pipe_verify_ntlmssp: unknown internal auth level %d\n",				cli->auth.auth_level ));			return NT_STATUS_INVALID_INFO_CLASS;	}	/*	 * Return the current pointer to the data offset.	 */	if(!prs_set_offset(current_pdu, save_offset)) {		DEBUG(0,("api_pipe_auth_process: failed to set offset back to %u\n",			(unsigned int)save_offset ));		return NT_STATUS_BUFFER_TOO_SMALL;	}	/*	 * Remember the padding length. We must remove it from the real data	 * stream once the sign/seal is done.	 */	*p_ss_padding_len = auth_info.auth_pad_len;	return NT_STATUS_OK;}/**************************************************************************** schannel specific sign/seal. ****************************************************************************/static NTSTATUS cli_pipe_verify_schannel(struct rpc_pipe_client *cli, RPC_HDR *prhdr,				prs_struct *current_pdu,				uint8 *p_ss_padding_len){	RPC_HDR_AUTH auth_info;	RPC_AUTH_SCHANNEL_CHK schannel_chk;	uint32 auth_len = prhdr->auth_len;	uint32 save_offset = prs_offset(current_pdu);	struct schannel_auth_struct *schannel_auth = cli->auth.a_u.schannel_auth;	uint32 data_len;	if (cli->auth.auth_level == PIPE_AUTH_LEVEL_NONE || cli->auth.auth_level == PIPE_AUTH_LEVEL_CONNECT) {		return NT_STATUS_OK;	}	if (auth_len != RPC_AUTH_SCHANNEL_SIGN_OR_SEAL_CHK_LEN) {		DEBUG(0,("cli_pipe_verify_schannel: auth_len %u.\n", (unsigned int)auth_len ));		return NT_STATUS_INVALID_PARAMETER;	}	if (!schannel_auth) {		return NT_STATUS_INVALID_PARAMETER;	}	/* Ensure there's enough data for an authenticated response. */	if ((auth_len > RPC_MAX_SIGN_SIZE) ||			(RPC_HEADER_LEN + RPC_HDR_RESP_LEN + RPC_HDR_AUTH_LEN + auth_len > prhdr->frag_len)) {		DEBUG(0,("cli_pipe_verify_schannel: auth_len %u is too large.\n",			(unsigned int)auth_len ));		return NT_STATUS_INVALID_PARAMETER;	}	data_len = prhdr->frag_len - RPC_HEADER_LEN - RPC_HDR_RESP_LEN - RPC_HDR_AUTH_LEN - auth_len;	if(!prs_set_offset(current_pdu, RPC_HEADER_LEN + RPC_HDR_RESP_LEN + data_len)) {		DEBUG(0,("cli_pipe_verify_schannel: cannot move offset to %u.\n",			(unsigned int)RPC_HEADER_LEN + RPC_HDR_RESP_LEN + data_len ));		return NT_STATUS_BUFFER_TOO_SMALL;	}                                                                                                                             	if(!smb_io_rpc_hdr_auth("hdr_auth", &auth_info, current_pdu, 0)) {		DEBUG(0,("cli_pipe_verify_schannel: failed to unmarshall RPC_HDR_AUTH.\n"));		return NT_STATUS_BUFFER_TOO_SMALL;	}	if (auth_info.auth_type != RPC_SCHANNEL_AUTH_TYPE) {		DEBUG(0,("cli_pipe_verify_schannel: Invalid auth info %d on schannel\n",			auth_info.auth_type));		return NT_STATUS_BUFFER_TOO_SMALL;	}	if(!smb_io_rpc_auth_schannel_chk("", RPC_AUTH_SCHANNEL_SIGN_OR_SEAL_CHK_LEN,				&schannel_chk, current_pdu, 0)) {		DEBUG(0,("cli_pipe_verify_schannel: failed to unmarshal RPC_AUTH_SCHANNEL_CHK.\n"));		return NT_STATUS_BUFFER_TOO_SMALL;	}	if (!schannel_decode(schannel_auth,			cli->auth.auth_level,			SENDER_IS_ACCEPTOR,			&schannel_chk,			prs_data_p(current_pdu)+RPC_HEADER_LEN+RPC_HDR_RESP_LEN,			data_len)) {		DEBUG(3,("cli_pipe_verify_schannel: failed to decode PDU "				"Connection to remote machine %s "				"pipe %s fnum 0x%x.\n",				cli->cli->desthost,				cli->pipe_name,				(unsigned int)cli->fnum ));		return NT_STATUS_INVALID_PARAMETER;	}	/* The sequence number gets incremented on both send and receive. */	schannel_auth->seq_num++;	/*	 * Return the current pointer to the data offset.	 */	if(!prs_set_offset(current_pdu, save_offset)) {		DEBUG(0,("api_pipe_auth_process: failed to set offset back to %u\n",			(unsigned int)save_offset ));		return NT_STATUS_BUFFER_TOO_SMALL;	}	/*	 * Remember the padding length. We must remove it from the real data	 * stream once the sign/seal is done.	 */

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?