LCOV - code coverage report
Current view: top level - librpc/rpc - dcesrv_reply.c (source / functions) Hit Total Coverage
Test: coverage report for master 2f515e9b Lines: 131 155 84.5 %
Date: 2024-04-21 15:09:00 Functions: 6 6 100.0 %

          Line data    Source code
       1             : /*
       2             :    Unix SMB/CIFS implementation.
       3             : 
       4             :    server side dcerpc common code
       5             : 
       6             :    Copyright (C) Andrew Tridgell 2003-2010
       7             :    Copyright (C) Stefan (metze) Metzmacher 2004-2005
       8             : 
       9             :    This program is free software; you can redistribute it and/or modify
      10             :    it under the terms of the GNU General Public License as published by
      11             :    the Free Software Foundation; either version 3 of the License, or
      12             :    (at your option) any later version.
      13             : 
      14             :    This program is distributed in the hope that it will be useful,
      15             :    but WITHOUT ANY WARRANTY; without even the implied warranty of
      16             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      17             :    GNU General Public License for more details.
      18             : 
      19             :    You should have received a copy of the GNU General Public License
      20             :    along with this program.  If not, see <http://www.gnu.org/licenses/>.
      21             : */
      22             : 
      23             : #include "includes.h"
      24             : #include "librpc/rpc/dcesrv_core.h"
      25             : #include "librpc/rpc/dcesrv_core_proto.h"
      26             : #include "librpc/rpc/dcerpc_util.h"
      27             : #include "auth/gensec/gensec.h"
      28             : #include "lib/util/dlinklist.h"
      29             : #include "param/param.h"
      30             : 
      31             : /*
      32             :   move a call from an existing linked list to the specified list. This
      33             :   prevents bugs where we forget to remove the call from a previous
      34             :   list when moving it.
      35             :  */
      36      835179 : static void dcesrv_call_set_list(struct dcesrv_call_state *call,
      37             :                                  enum dcesrv_call_list list)
      38             : {
      39      835179 :         switch (call->list) {
      40        2445 :         case DCESRV_LIST_NONE:
      41        2445 :                 break;
      42           0 :         case DCESRV_LIST_CALL_LIST:
      43           0 :                 DLIST_REMOVE(call->conn->call_list, call);
      44           0 :                 break;
      45          46 :         case DCESRV_LIST_FRAGMENTED_CALL_LIST:
      46          46 :                 DLIST_REMOVE(call->conn->incoming_fragmented_call_list, call);
      47          46 :                 break;
      48      832342 :         case DCESRV_LIST_PENDING_CALL_LIST:
      49      832342 :                 DLIST_REMOVE(call->conn->pending_call_list, call);
      50      825775 :                 break;
      51             :         }
      52      835179 :         call->list = list;
      53      835179 :         switch (list) {
      54           0 :         case DCESRV_LIST_NONE:
      55           0 :                 break;
      56      835179 :         case DCESRV_LIST_CALL_LIST:
      57      835179 :                 DLIST_ADD_END(call->conn->call_list, call);
      58      828266 :                 break;
      59           0 :         case DCESRV_LIST_FRAGMENTED_CALL_LIST:
      60           0 :                 DLIST_ADD_END(call->conn->incoming_fragmented_call_list, call);
      61           0 :                 break;
      62           0 :         case DCESRV_LIST_PENDING_CALL_LIST:
      63           0 :                 DLIST_ADD_END(call->conn->pending_call_list, call);
      64           0 :                 break;
      65             :         }
      66      835179 : }
      67             : 
      68             : 
      69     1187813 : void dcesrv_init_hdr(struct ncacn_packet *pkt, bool bigendian)
      70             : {
      71     1187813 :         pkt->rpc_vers = 5;
      72     1187813 :         pkt->rpc_vers_minor = 0;
      73     1187813 :         if (bigendian) {
      74           0 :                 pkt->drep[0] = 0;
      75             :         } else {
      76     1187813 :                 pkt->drep[0] = DCERPC_DREP_LE;
      77             :         }
      78     1187813 :         pkt->drep[1] = 0;
      79     1187813 :         pkt->drep[2] = 0;
      80     1187813 :         pkt->drep[3] = 0;
      81     1187813 : }
      82             : 
      83             : 
      84             : /*
      85             :   return a dcerpc fault
      86             : */
      87        2853 : NTSTATUS dcesrv_fault_with_flags(struct dcesrv_call_state *call,
      88             :                                  uint32_t fault_code,
      89             :                                  uint8_t extra_flags)
      90             : {
      91         346 :         struct ncacn_packet pkt;
      92         346 :         struct data_blob_list_item *rep;
      93         346 :         NTSTATUS status;
      94             : 
      95        2853 :         if (call->conn->terminate != NULL) {
      96             :                 /*
      97             :                  * If we're already disconnecting
      98             :                  * we should just drop a possible
      99             :                  * response
     100             :                  */
     101           0 :                 talloc_free(call);
     102           0 :                 return NT_STATUS_OK;
     103             :         }
     104             : 
     105             :         /* setup a fault */
     106        2853 :         dcesrv_init_hdr(&pkt, lpcfg_rpc_big_endian(call->conn->dce_ctx->lp_ctx));
     107        2853 :         pkt.auth_length = 0;
     108        2853 :         pkt.call_id = call->pkt.call_id;
     109        2853 :         pkt.ptype = DCERPC_PKT_FAULT;
     110        2853 :         pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST | extra_flags;
     111        2853 :         pkt.u.fault.alloc_hint = 24;
     112        2853 :         if (call->context != NULL) {
     113        2711 :                 pkt.u.fault.context_id = call->context->context_id;
     114             :         } else {
     115         142 :                 pkt.u.fault.context_id = 0;
     116             :         }
     117        2853 :         pkt.u.fault.cancel_count = 0;
     118        2853 :         pkt.u.fault.flags = 0;
     119        2853 :         pkt.u.fault.status = fault_code;
     120        2853 :         pkt.u.fault.reserved = 0;
     121        2853 :         pkt.u.fault.error_and_verifier = data_blob_null;
     122             : 
     123        2853 :         rep = talloc_zero(call, struct data_blob_list_item);
     124        2853 :         if (!rep) {
     125           0 :                 return NT_STATUS_NO_MEMORY;
     126             :         }
     127             : 
     128        2853 :         status = dcerpc_ncacn_push_auth(&rep->blob, call, &pkt, NULL);
     129        2853 :         if (!NT_STATUS_IS_OK(status)) {
     130           0 :                 return status;
     131             :         }
     132             : 
     133        2853 :         dcerpc_set_frag_length(&rep->blob, rep->blob.length);
     134             : 
     135        2853 :         DLIST_ADD_END(call->replies, rep);
     136        2853 :         dcesrv_call_set_list(call, DCESRV_LIST_CALL_LIST);
     137             : 
     138        2853 :         if (call->conn->call_list && call->conn->call_list->replies) {
     139        2853 :                 if (call->conn->transport.report_output_data) {
     140        2853 :                         call->conn->transport.report_output_data(call->conn);
     141             :                 }
     142             :         }
     143             : 
     144        2853 :         return NT_STATUS_OK;
     145             : }
     146             : 
     147        2511 : NTSTATUS dcesrv_fault(struct dcesrv_call_state *call, uint32_t fault_code)
     148             : {
     149        2511 :         return dcesrv_fault_with_flags(call, fault_code, 0);
     150             : }
     151             : 
     152      832342 : _PUBLIC_ NTSTATUS dcesrv_reply(struct dcesrv_call_state *call)
     153             : {
     154        6567 :         struct ndr_push *push;
     155        6567 :         NTSTATUS status;
     156        6567 :         DATA_BLOB stub;
     157        6567 :         uint32_t total_length, chunk_size;
     158      832342 :         struct dcesrv_connection_context *context = call->context;
     159      832342 :         struct dcesrv_auth *auth = call->auth_state;
     160      832342 :         size_t sig_size = 0;
     161             : 
     162             :         /*
     163             :          * call the reply function,
     164             :          * it's mostly for debug messages
     165             :          * and dcesrv_fault() also checks for
     166             :          * (call->conn->terminate != NULL) internally.
     167             :          */
     168      832342 :         status = context->iface->reply(call, call, call->r);
     169      832342 :         if (!NT_STATUS_IS_OK(status)) {
     170          16 :                 return dcesrv_fault(call, call->fault_code);
     171             :         }
     172             : 
     173      832326 :         if (call->conn->terminate != NULL) {
     174             :                 /*
     175             :                  * If we're already disconnecting
     176             :                  * we should just drop a possible
     177             :                  * response
     178             :                  */
     179           0 :                 talloc_free(call);
     180           0 :                 return NT_STATUS_OK;
     181             :         }
     182             : 
     183             :         /* form the reply NDR */
     184      832326 :         push = ndr_push_init_ctx(call);
     185      832326 :         NT_STATUS_HAVE_NO_MEMORY(push);
     186             : 
     187             :         /* carry over the pointer count to the reply in case we are
     188             :            using full pointer. See NDR specification for full
     189             :            pointers */
     190      832326 :         push->ptr_count = call->ndr_pull->ptr_count;
     191             : 
     192      832326 :         if (lpcfg_rpc_big_endian(call->conn->dce_ctx->lp_ctx)) {
     193           0 :                 push->flags |= LIBNDR_FLAG_BIGENDIAN;
     194             :         }
     195             : 
     196      832326 :         if (context->ndr64) {
     197           0 :                 push->flags |= LIBNDR_FLAG_NDR64;
     198             :         }
     199             : 
     200      832326 :         status = context->iface->ndr_push(call, call, push, call->r);
     201      832326 :         if (!NT_STATUS_IS_OK(status)) {
     202           0 :                 return dcesrv_fault(call, call->fault_code);
     203             :         }
     204             : 
     205      832326 :         stub = ndr_push_blob(push);
     206             : 
     207      832326 :         dcesrv_save_ndr_fuzz_seed(stub,
     208             :                                   call,
     209             :                                   NDR_OUT);
     210             : 
     211      832326 :         total_length = stub.length;
     212             : 
     213             :         /* we can write a full max_recv_frag size, minus the dcerpc
     214             :            request header size */
     215      832326 :         chunk_size = call->conn->max_xmit_frag;
     216      832326 :         chunk_size -= DCERPC_REQUEST_LENGTH;
     217      832326 :         if (auth->auth_finished && auth->gensec_security != NULL) {
     218       96750 :                 size_t max_payload = chunk_size;
     219             : 
     220       96750 :                 max_payload -= DCERPC_AUTH_TRAILER_LENGTH;
     221       96750 :                 max_payload -= (max_payload % DCERPC_AUTH_PAD_ALIGNMENT);
     222             : 
     223       96750 :                 sig_size = gensec_sig_size(auth->gensec_security,
     224             :                                            max_payload);
     225       96750 :                 if (sig_size) {
     226       95366 :                         chunk_size -= DCERPC_AUTH_TRAILER_LENGTH;
     227       95366 :                         chunk_size -= sig_size;
     228             :                 }
     229             :         }
     230      832326 :         chunk_size -= (chunk_size % DCERPC_AUTH_PAD_ALIGNMENT);
     231             : 
     232        6649 :         do {
     233        6649 :                 uint32_t length;
     234        6649 :                 struct data_blob_list_item *rep;
     235        6649 :                 struct ncacn_packet pkt;
     236        6649 :                 bool ok;
     237             : 
     238     1123365 :                 rep = talloc_zero(call, struct data_blob_list_item);
     239     1123365 :                 NT_STATUS_HAVE_NO_MEMORY(rep);
     240             : 
     241     1123365 :                 length = MIN(chunk_size, stub.length);
     242             : 
     243             :                 /* form the dcerpc response packet */
     244     1123365 :                 dcesrv_init_hdr(&pkt,
     245     1123365 :                                 lpcfg_rpc_big_endian(call->conn->dce_ctx->lp_ctx));
     246     1123365 :                 pkt.auth_length = 0;
     247     1123365 :                 pkt.call_id = call->pkt.call_id;
     248     1123365 :                 pkt.ptype = DCERPC_PKT_RESPONSE;
     249     1123365 :                 pkt.pfc_flags = 0;
     250     1123365 :                 if (stub.length == total_length) {
     251      832326 :                         pkt.pfc_flags |= DCERPC_PFC_FLAG_FIRST;
     252             :                 }
     253     1123365 :                 if (length == stub.length) {
     254      832326 :                         pkt.pfc_flags |= DCERPC_PFC_FLAG_LAST;
     255             :                 }
     256     1123365 :                 pkt.u.response.alloc_hint = stub.length;
     257             :                 /*
     258             :                  * bug for bug, feature for feature...
     259             :                  *
     260             :                  * Windows truncates the context_id with & 0xFF,
     261             :                  * so we do.
     262             :                  */
     263     1123365 :                 pkt.u.response.context_id = context->context_id & 0xFF;
     264     1123365 :                 pkt.u.response.cancel_count = 0;
     265     1123365 :                 pkt.u.response.stub_and_verifier.data = stub.data;
     266     1123365 :                 pkt.u.response.stub_and_verifier.length = length;
     267             : 
     268     1123365 :                 ok = dcesrv_auth_pkt_push(call, &rep->blob, sig_size,
     269             :                                           DCERPC_RESPONSE_LENGTH,
     270             :                                           &pkt.u.response.stub_and_verifier,
     271             :                                           &pkt);
     272     1123365 :                 if (!ok) {
     273           0 :                         return dcesrv_fault(call, DCERPC_FAULT_OTHER);
     274             :                 }
     275             : 
     276     1123365 :                 dcerpc_set_frag_length(&rep->blob, rep->blob.length);
     277             : 
     278     1123365 :                 DLIST_ADD_END(call->replies, rep);
     279             : 
     280     1123365 :                 stub.data += length;
     281     1123365 :                 stub.length -= length;
     282     1123365 :         } while (stub.length != 0);
     283             : 
     284             :         /* move the call from the pending to the finished calls list */
     285      832326 :         dcesrv_call_set_list(call, DCESRV_LIST_CALL_LIST);
     286             : 
     287      832326 :         if (call->conn->call_list && call->conn->call_list->replies) {
     288      832326 :                 if (call->conn->transport.report_output_data) {
     289      832326 :                         call->conn->transport.report_output_data(call->conn);
     290             :                 }
     291             :         }
     292             : 
     293      832326 :         return NT_STATUS_OK;
     294             : }
     295             : 
     296       17671 : _PUBLIC_ void _dcesrv_async_reply(struct dcesrv_call_state *call,
     297             :                                   const char *func,
     298             :                                   const char *location)
     299             : {
     300       17671 :         struct dcesrv_connection *conn = call->conn;
     301        1290 :         NTSTATUS status;
     302             : 
     303       17671 :         status = dcesrv_reply(call);
     304       17671 :         if (!NT_STATUS_IS_OK(status)) {
     305           0 :                 D_ERR("%s: %s: dcesrv_async_reply() failed - %s\n",
     306             :                       func, location, nt_errstr(status));
     307           0 :                 dcesrv_terminate_connection(conn, nt_errstr(status));
     308             :         }
     309       17671 : }

Generated by: LCOV version 1.14