/* * Copyright (c) 2009 Secure Endpoints Inc. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include #include #include #include #include "ms-wkssvc.h" #include "ms-srvsvc.h" #include "afsd.h" #include "cm_btree.h" #include "cm_nls.h" #include "msrpc.h" static void MSRPC_AllocBuffer(msrpc_buffer * buf, unsigned int size); static void MSRPC_ReallocBuffer(msrpc_buffer * buf, unsigned int size); static void MSRPC_FreeBuffer(msrpc_buffer * buf); static void MSRPC_SetCmUser(cm_user_t * userp); /*! \brief Interface list This is the list of interfaces that are exported through this RPC module. The *_ifspec variable is defined the _s.c file that is generated by MIDL. We have to use an extra level of indirection because the actual RPC_SERVER_INTERFACE structure is not exported in the generated code. */ RPC_SERVER_INTERFACE **_Interfaces[] = { (RPC_SERVER_INTERFACE **) &wkssvc_v1_0_s_ifspec, (RPC_SERVER_INTERFACE **) &srvsvc_v3_0_s_ifspec, }; const int n_Interfaces = sizeof(_Interfaces)/sizeof(_Interfaces[0]); /* Serialization */ #define RPC_ALIGNMENT 8 #define ALIGNTO8(x) (((((x) - 1)/8) + 1) * 8) typedef struct fcursor { BYTE * pos; BYTE * end; } fcursor; #define INITC(pfc, buf, len) (pfc)->pos = (BYTE *)(buf); (pfc)->end = (pfc)->pos + (len) #define SPC_LEFT(pfc) ((unsigned int)((pfc)->end - (pfc)->pos)) #define IS_SPC(pfc, n) (SPC_LEFT(pfc) >= (n)) __inline BYTE READB(fcursor * pfc) { BYTE b = *pfc->pos; pfc->pos++; return b; } #define READW(pfc) (((WORD)READB(pfc)) | (((WORD)READB(pfc)) << 8)) #define READDW(pfc) (((DWORD)READW(pfc)) | (((DWORD)READW(pfc)) << 16)) #define O_READS(pfc, s) do { memcpy(s, (pfc)->pos, sizeof(*s)); (pfc)->pos += sizeof(*s); } while (0) #define O_READA(pfc, a) do { memcpy(&a, (pfc)->pos, sizeof(a)); (pfc)->pos += sizeof(a); } while (0) #define O_READSZ(pfc, s, sz) do { memcpy(s, (pfc)->pos, sz); (pfc)->pos += sz; } while (0) #define O_RALIGN4(pfc) do { while(((size_t) (pfc)->pos) & 0x3) READB(pfc); } while (0) #define O_RALIGN8(pfc) do { while(((size_t) (pfc)->pos) & 0x7) READB(pfc); } while (0) __inline void WRITEB(fcursor * pfc, BYTE b) { *pfc->pos = b; pfc->pos++; } #define WRITEW(pfc, w) (WRITEB(pfc, ((w) & 0xff)), WRITEB(pfc, (((w) >> 8) & 0xff))) #define WRITEDW(pfc, dw) (WRITEW(pfc, ((dw) & 0xffff)), WRITEW(pfc, (((dw) >> 16) & 0xffff))) #define O_WRITES(pfc, s) do { memcpy((pfc)->pos, s, sizeof(*s)); (pfc)->pos += sizeof(*s); } while (0) #define O_WRITEA(pfc, a) do { memcpy((pfc)->pos, &a, sizeof(a)); (pfc)->pos += sizeof(a); } while (0) #define O_WRITESZ(pfc, s, sz) do { memcpy((pfc)->pos, s, sz); (pfc)->pos += sz; } while (0) #define O_WALIGN4(pfc) do { while(((size_t) (pfc)->pos) & 0x3) WRITEB(pfc, 0); } while (0) #define O_WALIGN8(pfc) do { while(((size_t) (pfc)->pos) & 0x7) WRITEB(pfc, 0); } while (0) #define iread_u_int8(pfc) READB(pfc) #define iread_byte(pfc) READB(pfc) #define iread_u_int16(pfc) READW(pfc) #define iread_u_int32(pfc) READDW(pfc) #define write_u_int8(pfc, i) WRITEB(pfc, i) #define write_byte(pfc, i) WRITEB(pfc, i) #define write_u_int16(pfc, i) WRITEW(pfc, i) #define write_u_int32(pfc, i) WRITEDW(pfc, i) /*! \brief Supported RPC protocol version numbers */ RPC_VERSION msrpc_supported_protocol_versions[] = { { 5, 0 }, /* 5.0 */ { 5, 1 } /* 5.1 */ }; const int n_msrpc_supported_protocol_versions = sizeof(msrpc_supported_protocol_versions)/sizeof(msrpc_supported_protocol_versions[0]); #define IS_PROTOCOL_VER_SUPPORTED(_maj, _min) ((_maj) == 5 && ((_min) == 0 || (_min) == 1)) /*! \brief Number of bytes of envelope overhead for an RPC response PDU */ #define RPC_RESPONSE_ENVELOPE_OVERHEAD 24 static int read_E_CommonHeader(fcursor *pc, E_CommonHeader * pr) { if (IS_SPC(pc, 16)) { pr->rpc_vers = iread_u_int8(pc); pr->rpc_vers_minor = iread_u_int8(pc); pr->PTYPE = iread_u_int8(pc); pr->pfc_flags = iread_u_int8(pc); O_READA(pc, pr->packed_drep); pr->frag_length = iread_u_int16(pc); pr->auth_length = iread_u_int16(pc); pr->call_id = iread_u_int32(pc); return 0; } else { return CM_ERROR_BADFORMAT; } } static int write_E_CommonHeader(fcursor * pc, msrpc_conn * conn, msrpc_call * call, PDU_TYPE pdu_type) { INITC(pc, call->out.buf_data, call->out.buf_alloc); call->out.pdu_type = pdu_type; if (IS_SPC(pc, 16)) { write_u_int8(pc, conn->rpc_vers); /* protocol version */ write_u_int8(pc, conn->rpc_vers_minor); /* minor version */ write_u_int8(pc, pdu_type); write_u_int8(pc, PFC_FIRST_FRAG|PFC_LAST_FRAG); write_byte(pc, 0x10); /* Data representation */ write_byte(pc, 0); write_byte(pc, 0); write_byte(pc, 0); write_u_int16(pc, 0); /* Fragment ength, to be filled in later */ write_u_int16(pc, 0); /* Length of auth data. Always 0 since we don't do authentication for RPC calls.*/ write_u_int32(pc, call->call_id); return 0; } else { return CM_ERROR_BUFFERTOOSMALL; } } #define USE_CURRENT_BUFFER_LENGTH (-1) static int closeMessage(fcursor * pc, msrpc_call * call, ptrdiff_t len) { unsigned short slen; if (len == USE_CURRENT_BUFFER_LENGTH) len = pc->pos - call->out.buf_data; if (len >= USHRT_MAX) return CM_ERROR_TOOBIG; slen = (unsigned short)len; pc->pos = &call->out.buf_data[8]; write_u_int16(pc, slen); call->out.buf_length = slen; return 0; } static int read_UUID(fcursor * pc, UUID * uuid) { if (IS_SPC(pc, sizeof(*uuid))) { O_READS(pc, uuid); return 0; } else { return CM_ERROR_BADFORMAT; } } static int write_UUID(fcursor * pc, const UUID * uuid) { if (IS_SPC(pc, sizeof(*uuid))) { O_WRITES(pc, uuid); return 0; } else { return CM_ERROR_BUFFERTOOSMALL; } } static int read_RPC_VERSION(fcursor * pc, RPC_VERSION * ver) { if (IS_SPC(pc, 4)) { ver->MajorVersion = iread_u_int16(pc); ver->MinorVersion = iread_u_int16(pc); return 0; } else { return CM_ERROR_BADFORMAT; } } static int write_RPC_VERSION(fcursor * pc, const RPC_VERSION * ver) { if (IS_SPC(pc, 4)) { write_u_int16(pc, ver->MajorVersion); write_u_int16(pc, ver->MinorVersion); return 0; } else { return CM_ERROR_BUFFERTOOSMALL; } } static int read_RPC_SYNTAX_IDENTIFIER(fcursor * pc, RPC_SYNTAX_IDENTIFIER * psi) { int code; code = read_UUID(pc, &psi->SyntaxGUID); if (code) return code; code = read_RPC_VERSION(pc, &psi->SyntaxVersion); return code; } static int write_RPC_SYNTAX_IDENTIFIER(fcursor * pc, const RPC_SYNTAX_IDENTIFIER * psi) { int code; code = write_UUID(pc, &psi->SyntaxGUID); if (code) return code; code = write_RPC_VERSION(pc, &psi->SyntaxVersion); return code; } static int is_equal_RPC_SYNTAX_IDENTIFIER(const RPC_SYNTAX_IDENTIFIER * s1, const RPC_SYNTAX_IDENTIFIER * s2) { return IsEqualGUID(&s1->SyntaxGUID, &s2->SyntaxGUID) && s1->SyntaxVersion.MajorVersion == s2->SyntaxVersion.MajorVersion && s2->SyntaxVersion.MinorVersion == s2->SyntaxVersion.MinorVersion; } #define FAULT_PDU_PACKET_SIZE 32 /*! \brief Write a fault PDU as the output of this call */ static int write_fault_PDU(msrpc_conn * conn, msrpc_call * call, u_int32 status, u_int8 flags) { fcursor o; MSRPC_AllocBuffer(&call->out, FAULT_PDU_PACKET_SIZE); write_E_CommonHeader(&o, conn, call, PDU_TYPE_FAULT); write_u_int32(&o, 16); /* allocation hint */ write_u_int16(&o, call->context_id); /* presentation context ID */ write_u_int8(&o, 0); /* cancel count */ write_u_int8(&o, 0); /* reserved */ write_u_int32(&o, status); /* status code */ O_WALIGN8(&o); /* alignment */ closeMessage(&o, call, USE_CURRENT_BUFFER_LENGTH); if (flags) { call->out.buf_data[3] |= flags; } call->status = MSRPC_CALL_DISPATCHED; return 0; } #define BIND_NAK_PDU_PACKET_SIZE 18 /*! \brief Write a bind_nak PDU as the output of this call */ static int write_bind_nak_PDU(msrpc_conn * conn, msrpc_call * call, BIND_REJECT_REASON reject_reason) { fcursor o; unsigned buf_size = BIND_NAK_PDU_PACKET_SIZE; if (reject_reason == BIND_REJ_PROTOCOL_VERSION_NOT_SUPPORTED) buf_size += n_msrpc_supported_protocol_versions * 2 + 1; MSRPC_AllocBuffer(&call->out, buf_size); write_E_CommonHeader(&o, conn, call, PDU_TYPE_BIND_NAK); write_u_int16(&o, reject_reason); if (reject_reason == BIND_REJ_PROTOCOL_VERSION_NOT_SUPPORTED) { int i; write_u_int8(&o, n_msrpc_supported_protocol_versions); for (i=0; i < n_msrpc_supported_protocol_versions; i++) { write_u_int8(&o, (u_int8) msrpc_supported_protocol_versions[i].MajorVersion); write_u_int8(&o, (u_int8) msrpc_supported_protocol_versions[i].MinorVersion); } } closeMessage(&o, call, USE_CURRENT_BUFFER_LENGTH); call->status = MSRPC_CALL_DISPATCHED; return 0; } static int handle_ConnBind(msrpc_conn * conn, msrpc_call * call, fcursor * in, fcursor * out) { int code = 0; if (!IS_SPC(in, 12)) return CM_ERROR_BADFORMAT; conn->max_xmit_frag = iread_u_int16(in); conn->max_recv_frag = iread_u_int16(in); conn->assoc_group_id = iread_u_int32(in); conn->rpc_vers = call->in_header->rpc_vers; conn->rpc_vers_minor = call->in_header->rpc_vers_minor; if (conn->assoc_group_id == 0) conn->assoc_group_id = 1; write_E_CommonHeader(out, conn, call, PDU_TYPE_BIND_ACK); write_u_int16(out, conn->max_xmit_frag); write_u_int16(out, conn->max_recv_frag); write_u_int32(out, conn->assoc_group_id); if (conn->secondary_address) { int len = (int)strlen(conn->secondary_address) + 1; write_u_int16(out, len); O_WRITESZ(out, conn->secondary_address, len); } else { write_u_int16(out, 0); } O_WALIGN4(out); /* presentation context negotiation */ { int n_context_elem; int i; n_context_elem = iread_u_int8(in); iread_u_int8(in); /* skip over reserved */ iread_u_int16(in); /* skip over reserved */ write_u_int8(out, n_context_elem); write_u_int8(out, 0); /* pad */ write_u_int16(out, 0); /* pad */ for (i=0; i < n_context_elem; i++) { int n_transfer_syn; RPC_SYNTAX_IDENTIFIER abstract_syntax; RPC_SYNTAX_IDENTIFIER transfer_syntax; RPC_SERVER_INTERFACE *interf = NULL; int j, k; if (!IS_SPC(in, 4 + 16)) return CM_ERROR_BADFORMAT; call->context_id = iread_u_int16(in); n_transfer_syn = iread_u_int8(in); iread_u_int8(in); /* skip */ code = read_RPC_SYNTAX_IDENTIFIER(in, &abstract_syntax); if (code) return code; for (j=0; j < n_Interfaces; j++) { interf = *_Interfaces[j]; if (is_equal_RPC_SYNTAX_IDENTIFIER(&abstract_syntax, &interf->InterfaceId)) break; } if (j < n_Interfaces) { for (k=0; k < n_transfer_syn; k++) { code = read_RPC_SYNTAX_IDENTIFIER(in, &transfer_syntax); if (code) return code; if (is_equal_RPC_SYNTAX_IDENTIFIER(&transfer_syntax, &interf->TransferSyntax)) break; } } if (j < n_Interfaces && k < n_transfer_syn) { /* accepted */ write_u_int16(out, 0); write_u_int16(out, 0); write_RPC_SYNTAX_IDENTIFIER(out, &transfer_syntax); conn->interface = interf; } else { int reason; if (j >= n_Interfaces) reason = 1; /* abstract syntax not supported */ else reason = 2; /* transfer syntax not supported */ write_u_int16(out, 2); /* provider rejection */ write_u_int16(out, reason); ZeroMemory(&transfer_syntax, sizeof(transfer_syntax)); write_RPC_SYNTAX_IDENTIFIER(out, &transfer_syntax); } } } return closeMessage(out, call, USE_CURRENT_BUFFER_LENGTH); } static int handle_ConnRequest(msrpc_conn * conn, msrpc_call * call, fcursor * in, fcursor * out) { int code = 0; int alloc_hint; unsigned int op_num; RPC_STATUS _Status = RPC_S_OK; int len; if (!IS_SPC(in, 8)) return CM_ERROR_BADFORMAT; alloc_hint = iread_u_int32(in); call->context_id = iread_u_int16(in); op_num = iread_u_int16(in); if (call->in_header->pfc_flags & PFC_OBJECT_UUID) { UUID uuid; code = read_UUID(in, &uuid); if (code) return code; } O_RALIGN8(in); if (conn->interface == NULL) { /* if there was no interface selected, we can't go ahead with this request. */ return write_fault_PDU(conn, call, RPC_S_NO_BINDINGS, PFC_DID_NOT_EXECUTE); } len = SPC_LEFT(in); if (op_num < 0 || op_num >= conn->interface->DispatchTable->DispatchTableCount || conn->interface->DispatchTable->DispatchTable[op_num] == NULL) { return write_fault_PDU(conn, call, RPC_S_PROCNUM_OUT_OF_RANGE, PFC_DID_NOT_EXECUTE); } call->msg.BufferLength = len; call->msg.ProcNum = op_num; call->msg.TransferSyntax = &conn->interface->TransferSyntax; call->msg.RpcInterfaceInformation = conn->interface; _Status = I_RpcGetBuffer(&call->msg); if ( _Status || call->msg.Buffer == NULL) { return write_fault_PDU(conn, call, RPC_S_SERVER_OUT_OF_MEMORY, PFC_DID_NOT_EXECUTE); } memcpy(call->msg.Buffer, in->pos, len); MSRPC_SetCmUser(call->cm_userp); RpcTryExcept { (*conn->interface->DispatchTable->DispatchTable[op_num])(&call->msg); } RpcExcept ( 1 ) { code = CM_ERROR_UNKNOWN; } RpcEndExcept; MSRPC_SetCmUser(NULL); if (code == 0) { call->out.pdu_type = PDU_TYPE_RESPONSE; MSRPC_AllocBuffer(&call->out, call->msg.BufferLength); call->out.buf_length = call->msg.BufferLength; memcpy(call->out.buf_data, call->msg.Buffer, call->out.buf_length); } else { code = write_fault_PDU(conn, call, RPC_S_CALL_FAILED, 0); } return code; } static int merge_fragment_Bind(msrpc_call * call, E_CommonHeader * ph, msrpc_call * ncall, E_CommonHeader *pnh) { if (ph->auth_length + (unsigned int) pnh->auth_length > _UI16_MAX || ph->frag_length + (unsigned int) pnh->auth_length > _UI16_MAX || pnh->auth_length + 16 > pnh->frag_length) return CM_ERROR_TOOBIG; MSRPC_ReallocBuffer(&call->in, call->in.buf_length + pnh->auth_length); if (call->in.buf_data == NULL) return CM_ERROR_TOOBIG; memcpy(call->in.buf_data + ph->frag_length, ncall->in.buf_data + (pnh->frag_length - pnh->auth_length), pnh->auth_length); call->in.buf_length += pnh->auth_length; ph->frag_length += pnh->auth_length; ph->auth_length += pnh->auth_length; if ((pnh->pfc_flags & PFC_LAST_FRAG) == PFC_LAST_FRAG) { ph->pfc_flags |= PFC_LAST_FRAG; } return 0; } static int merge_fragment_Request(msrpc_call * call, E_CommonHeader * ph, msrpc_call * ncall, E_CommonHeader * pnh) { int new_auth_length = pnh->auth_length; int new_stub_length = (int)pnh->frag_length - (pnh->auth_length + 24); if (pnh->pfc_flags & PFC_OBJECT_UUID) new_stub_length -= 16; if (ph->auth_length + new_auth_length > _UI16_MAX || ph->frag_length + new_stub_length + new_auth_length > _UI16_MAX || pnh->auth_length + 24 > pnh->frag_length) return CM_ERROR_TOOBIG; MSRPC_ReallocBuffer(&call->in, call->in.buf_length + new_auth_length + new_stub_length); if (call->in.buf_data == NULL) return CM_ERROR_TOOBIG; if(ph->auth_length) memmove(call->in.buf_data + (ph->frag_length - ph->auth_length), call->in.buf_data + (ph->frag_length - ph->auth_length) + new_stub_length, ph->auth_length); memcpy(call->in.buf_data + (ph->frag_length - ph->auth_length), ncall->in.buf_data + (pnh->frag_length - (new_auth_length + new_stub_length)), new_stub_length); memcpy(call->in.buf_data + ph->frag_length + new_stub_length, ncall->in.buf_data + (pnh->frag_length - pnh->auth_length), pnh->auth_length); call->in.buf_length += new_auth_length + new_stub_length; ph->frag_length += new_auth_length + new_stub_length; ph->auth_length += new_auth_length; if ((pnh->pfc_flags & PFC_LAST_FRAG) == PFC_LAST_FRAG) { ph->pfc_flags |= PFC_LAST_FRAG; } return 0; } static int assemble_message(msrpc_conn * conn) { int code = 0; msrpc_call * call; msrpc_call * ncall; E_CommonHeader h; fcursor c; call = conn->head; if (call == NULL) return CM_ERROR_INVAL; INITC(&c, call->in.buf_data, call->in.buf_length); code = read_E_CommonHeader(&c, &h); if (code) return code; if ((h.pfc_flags & (PFC_FIRST_FRAG | PFC_LAST_FRAG)) == (PFC_FIRST_FRAG | PFC_LAST_FRAG)) return 0; if ((h.pfc_flags & PFC_FIRST_FRAG) != PFC_FIRST_FRAG) return CM_ERROR_INVAL; for (ncall = call->next; ncall; ncall = ncall->next) { fcursor nc; E_CommonHeader nh; INITC(&nc, ncall->in.buf_data, ncall->in.buf_length); code = read_E_CommonHeader(&nc, &nh); if (nh.call_id != h.call_id) continue; if ((nh.pfc_flags & PFC_FIRST_FRAG) != 0) continue; switch(h.PTYPE) { case PDU_TYPE_BIND: case PDU_TYPE_ALTER_CONTEXT: code = merge_fragment_Bind(call, &h, ncall, &nh); break; case PDU_TYPE_REQUEST: code = merge_fragment_Request(call, &h, ncall, &nh); break; default: code = CM_ERROR_INVAL; } ncall->status = MSRPC_CALL_SKIP; if (nh.pfc_flags & PFC_LAST_FRAG) { break; } } c.pos = &call->in.buf_data[3]; write_u_int8(&c, h.pfc_flags); write_u_int16(&c, h.frag_length); write_u_int16(&c, h.auth_length); return code; } static int dispatch_call(msrpc_conn * conn) { int code = 0; fcursor c; fcursor o; E_CommonHeader h; msrpc_call * call; call = conn->head; if (call == NULL || call->status != MSRPC_CALL_MSGRECEIVED) return CM_ERROR_INVAL; MSRPC_AllocBuffer(&call->out, DEF_RPC_MSG_SIZE); retry_buffer: INITC(&c, call->in.buf_data, call->in.buf_length); code = read_E_CommonHeader(&c, &h); if (code) return code; if (!IS_PROTOCOL_VER_SUPPORTED(h.rpc_vers, h.rpc_vers_minor)) { if (h.PTYPE == PDU_TYPE_BIND) { return write_bind_nak_PDU(conn, call, BIND_REJ_PROTOCOL_VERSION_NOT_SUPPORTED); } else { /* we shouldn't get here, but just in case */ return write_fault_PDU(conn, call, RPC_S_INVALID_ARG, PFC_DID_NOT_EXECUTE); } } if (h.packed_drep[0] != 0x10 || h.packed_drep[1] != 0 || h.packed_drep[2] != 0 || h.packed_drep[3] != 0) { if (h.PTYPE == PDU_TYPE_BIND) { return write_bind_nak_PDU(conn, call, BIND_REJ_REASON_NOT_SPECIFIED); } else { return write_fault_PDU(conn, call, RPC_S_INVALID_ARG, PFC_DID_NOT_EXECUTE); } } if (h.frag_length > call->in.buf_length) { return CM_ERROR_INVAL; } if ((h.pfc_flags & (PFC_FIRST_FRAG | PFC_LAST_FRAG)) != (PFC_FIRST_FRAG | PFC_LAST_FRAG)) { code = assemble_message(conn); if (code == 0) goto retry_buffer; else return write_fault_PDU(conn, call, RPC_S_INVALID_ARG, PFC_DID_NOT_EXECUTE); } if (h.auth_length != 0) { return write_fault_PDU(conn, call, RPC_S_BINDING_HAS_NO_AUTH, PFC_DID_NOT_EXECUTE); } call->call_id = h.call_id; call->in.pdu_type = h.PTYPE; INITC(&o, call->out.buf_data, call->out.buf_alloc); call->in_header = &h; code = CM_ERROR_INVAL; switch (h.PTYPE) { case PDU_TYPE_REQUEST: code = handle_ConnRequest(conn, call, &c, &o); break; case PDU_TYPE_BIND: code = handle_ConnBind(conn, call, &c, &o); break; case PDU_TYPE_ALTER_CONTEXT: code = write_fault_PDU(conn, call, RPC_S_CALL_FAILED, PFC_DID_NOT_EXECUTE); break; case PDU_TYPE_SHUTDOWN: code = write_fault_PDU(conn, call, RPC_S_CALL_FAILED, PFC_DID_NOT_EXECUTE); break; case PDU_TYPE_CO_CANCEL: code = write_fault_PDU(conn, call, RPC_S_CALL_FAILED, PFC_DID_NOT_EXECUTE); break; case PDU_TYPE_ORPHANED: code = write_fault_PDU(conn, call, RPC_S_CALL_FAILED, PFC_DID_NOT_EXECUTE); break; default: code = write_fault_PDU(conn, call, RPC_S_CALL_FAILED, PFC_DID_NOT_EXECUTE); } call->in_header = NULL; call->status = MSRPC_CALL_DISPATCHED; return code; } static void MSRPC_AllocBuffer(msrpc_buffer * buf, unsigned int size) { if (buf->buf_alloc < size) { if (buf->buf_data) _aligned_free(buf->buf_data); buf->buf_alloc = ALIGNTO8(size); buf->buf_data = _aligned_malloc(buf->buf_alloc, RPC_ALIGNMENT); buf->buf_length = 0; buf->buf_pos = 0; } } static void MSRPC_FreeBuffer(msrpc_buffer * buf) { if (buf->buf_data) { _aligned_free(buf->buf_data); buf->buf_data = NULL; buf->buf_alloc = 0; buf->buf_length = 0; buf->buf_pos = 0; } } static void MSRPC_ReallocBuffer(msrpc_buffer * buf, unsigned int size) { if (buf->buf_alloc < size) { BYTE * new_buf; buf->buf_alloc = ALIGNTO8(size); new_buf = _aligned_realloc(buf->buf_data, buf->buf_alloc, RPC_ALIGNMENT); if (new_buf != NULL) { buf->buf_data = new_buf; } else { MSRPC_FreeBuffer(buf); } } } static msrpc_call * MSRPC_NewCall(void) { msrpc_call * c; c = calloc(sizeof(*c), 1); c->msg.Handle = NULL; c->msg.DataRepresentation = 0x00000010; c->msg.Buffer = NULL; c->msg.BufferLength = 0; c->msg.TransferSyntax = NULL; return c; } static int MSRPC_PutCall(msrpc_conn * conn, msrpc_call * pcall) { if (conn->tail) { conn->tail->next = pcall; pcall->prev = conn->tail; conn->tail = pcall; pcall->next = NULL; } else { conn->head = conn->tail = pcall; pcall->next = pcall->prev = NULL; } return 0; } static int MSRPC_DequeueCall(msrpc_conn * conn, msrpc_call * call) { if (call->prev) call->prev->next = call->next; if (call->next) call->next->prev = call->prev; if (conn->head && conn->head == call) conn->head = call->next; if (conn->tail && conn->tail == call) conn->tail = call->prev; call->next = call->prev = NULL; return 0; } static int MSRPC_GetCall(msrpc_conn * conn, msrpc_call ** ppcall) { if (conn->head) { *ppcall = conn->head; conn->head = conn->head->next; if (conn->head) conn->head->prev = NULL; (*ppcall)->next = (*ppcall)->prev = NULL; if (conn->tail == *ppcall) conn->tail = NULL; return 0; } else { *ppcall = NULL; return 1; } } static int MSRPC_FreeCall(msrpc_call * call) { I_RpcFreeBuffer(&call->msg); MSRPC_FreeBuffer(&call->in); MSRPC_FreeBuffer(&call->out); if (call->cm_userp) { cm_ReleaseUser(call->cm_userp); call->cm_userp = NULL; } ZeroMemory(call, sizeof(*call)); free (call); return 0; } /*! \brief Initialize a msrpc_conn \param [out] conn Connection to initialize \param [in] secondary_address An optional secondary address for the query. Typically for RPCs to wellknown endpoints such as SRVSVC or WKSSVC, this is a named pipe address (e.g.: \PIPE\srvsvc ). \return Always returns 0. */ int MSRPC_InitConn(msrpc_conn * conn, const char * secondary_address) { ZeroMemory(conn, sizeof(*conn)); if (secondary_address) { conn->secondary_address = strdup(secondary_address); } return 0; } /*! \brief Free a msrcp_conn structure contents This doesn't free the msrpc_conn object, but frees the contents. */ int MSRPC_FreeConn(msrpc_conn * conn) { msrpc_call * call = NULL; while(!MSRPC_GetCall(conn, &call)) { MSRPC_FreeCall(call); } if (conn->secondary_address) free(conn->secondary_address); ZeroMemory(conn, sizeof(*conn)); return 0; } /*! \brief Write an RPC message into the RPC module This function should be used by message mode RPC transports to submit a single message. if successful, the message will be queued for dispatch. The RPC will not be executed until MSRPC_PrepareRead() or MSRPC_ReadMessageLength() or MSRPC_ReadMessage() is called. */ int MSRPC_WriteMessage(msrpc_conn * conn, BYTE * buffer, unsigned int len, cm_user_t * userp) { msrpc_call * call; if (len == 0) return CM_ERROR_INVAL; if (len > MAX_RPC_MSG_SIZE) return CM_ERROR_BUFFERTOOSMALL; call = MSRPC_NewCall(); if (!call) return ENOMEM; MSRPC_AllocBuffer(&call->in, len); memcpy(call->in.buf_data, buffer, len); call->in.buf_pos = len; call->in.buf_length = call->in.buf_pos; call->status = MSRPC_CALL_MSGRECEIVED; if (userp) { call->cm_userp = userp; cm_HoldUser(userp); } MSRPC_PutCall(conn, call); return 0; } /*! \brief Prepare to read the next message from the RPC output queue Each RPC message received is queued until the response is required. MSRPC_PrepareRead() will, if necessary, dispatch the RPC request and prepare the output buffer to be sent to the caller. */ int MSRPC_PrepareRead(msrpc_conn * conn) { msrpc_call * call; int code = 0; while (conn->head) { call = conn->head; if (call->status == MSRPC_CALL_SKIP || call->status == MSRPC_CALL_MSGSENT || (call->status == MSRPC_CALL_DISPATCHED && call->out.buf_length == 0)) { MSRPC_DequeueCall(conn, call); MSRPC_FreeCall(call); continue; } if (call->status == MSRPC_CALL_MSGRECEIVED) { code = dispatch_call(conn); /* If the dispatch failed, then we discard the message. If the error was handled and a response packet was created, dispatch_call() would return 0. */ if (code) { MSRPC_DequeueCall(conn, call); MSRPC_FreeCall(call); } continue; } break; /* the status wasn't SKIP or MSGRECEIVED, or we successfully dispatched the message. */ } if (conn->head == NULL) return CM_ERROR_BADFD; if (conn->head->status == MSRPC_CALL_DISPATCHED || conn->head->status == MSRPC_CALL_MSGSENDING) return 0; else return CM_ERROR_BADFD; } /*! \brief Return the size of the buffer for the next RPC message Given the maximum size of the buffer, returns the actual size of the data that will be copied. If the return value is 0, there is no data to be sent to the caller either because there was no RPC message queued or the queued message could not be dispatched. */ int MSRPC_ReadMessageLength(msrpc_conn * conn, unsigned int max_len) { int code; msrpc_call *call; max_len = min(max_len, conn->max_recv_frag); code = MSRPC_PrepareRead(conn); if (code) return 0; call = conn->head; if (call && (call->status == MSRPC_CALL_DISPATCHED || call->status == MSRPC_CALL_MSGSENDING)) { switch(call->out.pdu_type) { case PDU_TYPE_RESPONSE: { /* This calculation should mirror what MSRPC_ReadMessage() does below. */ int fragment_size = conn->max_recv_frag - RPC_RESPONSE_ENVELOPE_OVERHEAD; unsigned int len; len = min(max_len, conn->max_recv_frag); if (call->out.buf_pos % fragment_size == 0) { if (len < RPC_RESPONSE_ENVELOPE_OVERHEAD) return 0; len = min(len, RPC_RESPONSE_ENVELOPE_OVERHEAD + call->out.buf_length - call->out.buf_pos); } else { unsigned int to_copy; to_copy = min(fragment_size - (call->out.buf_pos % fragment_size), call->out.buf_length - call->out.buf_pos); len = min(to_copy, len); } return len; } case PDU_TYPE_BIND_ACK: case PDU_TYPE_FAULT: case PDU_TYPE_BIND_NAK: { int len = min(call->out.buf_length - call->out.buf_pos, max_len); return len; } } return 0; } else { return 0; } } /*! \brief Read the next message from the RPC queue Copies the next chunk of data from the response buffer to the buffer indicated by \a buffer and \a len. \retval 0 The data was copied to the buffer. There is either no more data to send or the existence of more data is implicit in the data already sent. I.e. fragment flags were set appropriately to indicate more data and we are at a fragment boundary. \retval CM_ERROR_RPC_MOREDATA The data was copied to the buffer and there is more data to be sent. \retval Else something went wrong. The data was not copied to the buffer successfully. */ int MSRPC_ReadMessage(msrpc_conn * conn, BYTE *buffer, unsigned int len) { int code; msrpc_call * call; code = MSRPC_PrepareRead(conn); if (code) return code; if (conn->head && (conn->head->status == MSRPC_CALL_DISPATCHED || conn->head->status == MSRPC_CALL_MSGSENDING)) { call = conn->head; switch(call->out.pdu_type) { case PDU_TYPE_RESPONSE: { BYTE * o_buffer = call->out.buf_data; unsigned int o_alloc = call->out.buf_alloc; unsigned int o_pos = call->out.buf_pos; unsigned int o_len = call->out.buf_length; unsigned int start_pos = o_pos; unsigned int fragment_size; fcursor o; /* If call->out.buf_pos falls on a fragment boundary: Assume that we are starting a new fragment and deal accordingly. Else Assume we are continuing a fragment. */ fragment_size = conn->max_recv_frag - RPC_RESPONSE_ENVELOPE_OVERHEAD; /* Don't send more than the max_recv_frag */ len = min(len, conn->max_recv_frag); /* Switch the supplied user buffer with the output buffer */ call->out.buf_data = buffer; call->out.buf_length = 0; call->out.buf_alloc = len; call->out.buf_pos = 0; if (o_pos % fragment_size == 0) { /* New fragment: We need to write the packet headers and fragment bits here. The length of the buffer should be at least big enough to hold the entire envelope. We don't handle the case were the envelope itself is too big. */ if (len < RPC_RESPONSE_ENVELOPE_OVERHEAD) { return CM_ERROR_INVAL; } code = write_E_CommonHeader(&o, conn, call, PDU_TYPE_RESPONSE); if (code) return code; write_u_int32(&o, o_len); /* allocation hint */ write_u_int16(&o, call->context_id); /* presentation context ID */ write_u_int8(&o, 0); /* cancel count */ write_u_int8(&o, 0); /* reserved */ { int to_copy = min(SPC_LEFT(&o), o_len - o_pos); int rc; O_WRITESZ(&o, (o_buffer + o_pos), to_copy); /* the fragment size of the message is the minimum of: - max_recv_frag for the connection - envelope size + (full message size - start offset) */ rc = closeMessage(&o, call, min(conn->max_recv_frag, RPC_RESPONSE_ENVELOPE_OVERHEAD + o_len - start_pos)); if (rc) return rc; o_pos += to_copy; /* If there is more data to be sent and we are not at a fragment boundary after the last send, we return CM_ERROR_RPC_MOREDATA. Having more fragments to send is not a case we indicate via a return value. */ if (o_pos < o_len && o_pos < start_pos + fragment_size) code = CM_ERROR_RPC_MOREDATA; } if (start_pos + fragment_size < o_len) buffer[3] &= ~PFC_LAST_FRAG; if (start_pos != 0) buffer[3] &= ~PFC_FIRST_FRAG; } else { /* Continuation: The packet header should have already been sent in a previous block. */ unsigned int to_copy; INITC(&o, call->out.buf_data, call->out.buf_alloc); to_copy = min(fragment_size - (o_pos % fragment_size), o_len - o_pos); to_copy = min(to_copy, SPC_LEFT(&o)); O_WRITESZ(&o, (o_buffer + o_pos), to_copy); o_pos += to_copy; if (o_pos < o_len && (o_pos % fragment_size) != 0) code = CM_ERROR_RPC_MOREDATA; } call->out.buf_data = o_buffer; call->out.buf_alloc = o_alloc; call->out.buf_pos = o_pos; call->out.buf_length = o_len; if (call->out.buf_pos < call->out.buf_length) { call->status = MSRPC_CALL_MSGSENDING; } else { call->status = MSRPC_CALL_MSGSENT; MSRPC_DequeueCall(conn, call); MSRPC_FreeCall(call); } } return code; case PDU_TYPE_BIND_ACK: case PDU_TYPE_FAULT: case PDU_TYPE_BIND_NAK: { unsigned int to_send; /* These types of packets are already fully formed. We don't need to wrap them in an envelope. They also don't fragment. */ to_send = call->out.buf_length - call->out.buf_pos; if (len < to_send) code = CM_ERROR_RPC_MOREDATA; else len = to_send; if (len > 0) { memcpy(buffer, call->out.buf_data + call->out.buf_pos, len); } call->out.buf_pos += len; if (call->out.buf_pos < call->out.buf_length) { call->status = MSRPC_CALL_MSGSENDING; } else { call->status = MSRPC_CALL_MSGSENT; MSRPC_DequeueCall(conn, call); MSRPC_FreeCall(call); } } return code; } return CM_ERROR_INVAL; } else { return 0; } } pthread_once_t msrpc_user_slot_init = PTHREAD_ONCE_INIT; DWORD msrpc_tls_user_slot = 0; static void msrpc_user_slot_init_func(void) { msrpc_tls_user_slot = TlsAlloc(); } cm_user_t * MSRPC_GetCmUser(void) { pthread_once(&msrpc_user_slot_init, msrpc_user_slot_init_func); return (cm_user_t *) TlsGetValue(msrpc_tls_user_slot); } static void MSRPC_SetCmUser(cm_user_t * userp) { pthread_once(&msrpc_user_slot_init, msrpc_user_slot_init_func); TlsSetValue(msrpc_tls_user_slot, userp); } typedef struct I_RpcAllocatedBuffer { DWORD magic; /* == _RPCALLOCATEDBUFFER_MAGIC */ #define _RPCALLOCATEDBUFFER_MAGIC 0xa110ca73 struct I_RpcAllocatedBuffer * next; /* Data goes here */ } I_RpcAllocatedBuffer; #define I_RPCALLOCATEDBUFFER_FROM_PTR(v) (&(((I_RpcAllocatedBuffer *) (v))[-1])) RPC_STATUS RPC_ENTRY I_RpcGetBuffer (IN OUT RPC_MESSAGE __RPC_FAR * Message) { I_RpcAllocatedBuffer * newBuffer = I_RpcAllocate(Message->BufferLength + sizeof(I_RpcAllocatedBuffer)); I_RpcAllocatedBuffer * oldBuffer = NULL; if (Message->Buffer) { oldBuffer = I_RPCALLOCATEDBUFFER_FROM_PTR(Message->Buffer); } if (newBuffer) { newBuffer->magic = _RPCALLOCATEDBUFFER_MAGIC; newBuffer->next = oldBuffer; Message->Buffer = &newBuffer[1]; } else { return ERROR_OUTOFMEMORY; } return RPC_S_OK; } RPC_STATUS RPC_ENTRY I_RpcFreeBuffer (IN OUT RPC_MESSAGE __RPC_FAR * Message) { if (Message->Buffer) { I_RpcAllocatedBuffer * buffer; buffer = I_RPCALLOCATEDBUFFER_FROM_PTR(Message->Buffer); while (buffer) { I_RpcAllocatedBuffer * bnext; bnext = buffer->next; I_RpcFree(buffer); buffer = bnext; } Message->Buffer = NULL; } return RPC_S_OK; } unsigned char * RPC_ENTRY NdrServerInitializeNew( PRPC_MESSAGE pRpcMsg, PMIDL_STUB_MESSAGE pStubMsg, PMIDL_STUB_DESC pStubDesc ) { ZeroMemory(pStubMsg, sizeof(*pStubMsg)); pStubMsg->RpcMsg = pRpcMsg; pStubMsg->Buffer = pRpcMsg->Buffer; pStubMsg->BufferStart = pRpcMsg->Buffer; pStubMsg->BufferEnd = pStubMsg->Buffer + pRpcMsg->BufferLength; pStubMsg->BufferLength = pRpcMsg->BufferLength; pStubMsg->MemorySize = pRpcMsg->BufferLength; pStubMsg->pfnAllocate = pStubDesc->pfnAllocate; pStubMsg->pfnFree = pStubDesc->pfnFree; pStubMsg->StubDesc = pStubDesc; pStubMsg->dwDestContext = MSHCTX_LOCAL; return NULL; } void MSRPC_Init(void) { RPC_SRVSVC_Init(); } void MSRPC_Shutdown(void) { RPC_SRVSVC_Shutdown(); } int MSRPC_IsWellKnownService(const clientchar_t * lastNamep) { return cm_ClientStrCmpIA(lastNamep, _C("\\srvsvc")) == 0 || cm_ClientStrCmpIA(lastNamep, _C("\\wkssvc")) == 0 || cm_ClientStrCmpIA(lastNamep, _C("\\spoolss")) == 0 || cm_ClientStrCmpIA(lastNamep, _C("\\winreg")) == 0 || cm_ClientStrCmpIA(lastNamep, _C("\\lsass")) == 0 || cm_ClientStrCmpIA(lastNamep, _C("\\lsarpc")) == 0 || cm_ClientStrCmpIA(lastNamep, _C("\\samr")) == 0 || cm_ClientStrCmpIA(lastNamep, _C("\\netlogon")) == 0 || cm_ClientStrCmpIA(lastNamep, _C("\\ntsvcs")) == 0 || cm_ClientStrCmpIA(lastNamep, _C("\\eventlog")) == 0 || cm_ClientStrCmpIA(lastNamep, _C("\\svcctl")) == 0 || cm_ClientStrCmpIA(lastNamep, _C("\\browse")) == 0 || cm_ClientStrCmpIA(lastNamep, _C("\\msgsvc")) == 0 || cm_ClientStrCmpIA(lastNamep, _C("\\w32time")) == 0 || cm_ClientStrCmpIA(lastNamep, _C("ipc$")) == 0; }