LCOV - code coverage report
Current view: top level - source3/librpc/rpc - dcerpc_helpers.c (source / functions) Hit Total Coverage
Test: coverage report for master 2f515e9b Lines: 157 217 72.4 %
Date: 2024-04-21 15:09:00 Functions: 7 7 100.0 %

          Line data    Source code
       1             : /*
       2             :  *  DCERPC Helper routines
       3             :  *  Günther Deschner <gd@samba.org> 2010.
       4             :  *  Simo Sorce <idra@samba.org> 2010.
       5             :  *
       6             :  *  This program is free software; you can redistribute it and/or modify
       7             :  *  it under the terms of the GNU General Public License as published by
       8             :  *  the Free Software Foundation; either version 3 of the License, or
       9             :  *  (at your option) any later version.
      10             :  *
      11             :  *  This program is distributed in the hope that it will be useful,
      12             :  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
      13             :  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      14             :  *  GNU General Public License for more details.
      15             :  *
      16             :  *  You should have received a copy of the GNU General Public License
      17             :  *  along with this program; if not, see <http://www.gnu.org/licenses/>.
      18             :  */
      19             : 
      20             : 
      21             : #include "includes.h"
      22             : #include "librpc/rpc/dcerpc.h"
      23             : #include "librpc/rpc/dcerpc_util.h"
      24             : #include "librpc/gen_ndr/ndr_dcerpc.h"
      25             : #include "librpc/crypto/gse.h"
      26             : #include "auth/gensec/gensec.h"
      27             : 
      28             : #undef DBGC_CLASS
      29             : #define DBGC_CLASS DBGC_RPC_PARSE
      30             : 
      31             : /**
      32             : * @brief NDR Encodes a ncacn_packet
      33             : *
      34             : * @param mem_ctx        The memory context the blob will be allocated on
      35             : * @param ptype          The DCERPC packet type
      36             : * @param pfc_flags      The DCERPC PFC Flags
      37             : * @param auth_length    The length of the trailing auth blob
      38             : * @param call_id        The call ID
      39             : * @param u              The payload of the packet
      40             : * @param blob [out]     The encoded blob if successful
      41             : *
      42             : * @return an NTSTATUS error code
      43             : */
      44      588446 : NTSTATUS dcerpc_push_ncacn_packet(TALLOC_CTX *mem_ctx,
      45             :                                   enum dcerpc_pkt_type ptype,
      46             :                                   uint8_t pfc_flags,
      47             :                                   uint16_t auth_length,
      48             :                                   uint32_t call_id,
      49             :                                   union dcerpc_payload *u,
      50             :                                   DATA_BLOB *blob)
      51             : {
      52           0 :         struct ncacn_packet r;
      53           0 :         enum ndr_err_code ndr_err;
      54             : 
      55      588446 :         r.rpc_vers              = 5;
      56      588446 :         r.rpc_vers_minor        = 0;
      57      588446 :         r.ptype                 = ptype;
      58      588446 :         r.pfc_flags             = pfc_flags;
      59      588446 :         r.drep[0]               = DCERPC_DREP_LE;
      60      588446 :         r.drep[1]               = 0;
      61      588446 :         r.drep[2]               = 0;
      62      588446 :         r.drep[3]               = 0;
      63      588446 :         r.auth_length           = auth_length;
      64      588446 :         r.call_id               = call_id;
      65      588446 :         r.u                     = *u;
      66             : 
      67      588446 :         ndr_err = ndr_push_struct_blob(blob, mem_ctx, &r,
      68             :                 (ndr_push_flags_fn_t)ndr_push_ncacn_packet);
      69      588446 :         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
      70           0 :                 return ndr_map_error2ntstatus(ndr_err);
      71             :         }
      72             : 
      73      588446 :         dcerpc_set_frag_length(blob, blob->length);
      74             : 
      75             : 
      76      588446 :         if (DEBUGLEVEL >= 10) {
      77             :                 /* set frag len for print function */
      78          98 :                 r.frag_length = blob->length;
      79          98 :                 NDR_PRINT_DEBUG(ncacn_packet, &r);
      80             :         }
      81             : 
      82      588446 :         return NT_STATUS_OK;
      83             : }
      84             : 
      85             : /**
      86             : * @brief NDR Encodes a dcerpc_auth structure
      87             : *
      88             : * @param mem_ctx          The memory context the blob will be allocated on
      89             : * @param auth_type        The DCERPC Authentication Type
      90             : * @param auth_level       The DCERPC Authentication Level
      91             : * @param auth_pad_length  The padding added to the packet this blob will be
      92             : *                          appended to.
      93             : * @param auth_context_id  The context id
      94             : * @param credentials      The authentication credentials blob (signature)
      95             : * @param blob [out]       The encoded blob if successful
      96             : *
      97             : * @return a NTSTATUS error code
      98             : */
      99        1075 : NTSTATUS dcerpc_push_dcerpc_auth(TALLOC_CTX *mem_ctx,
     100             :                                  enum dcerpc_AuthType auth_type,
     101             :                                  enum dcerpc_AuthLevel auth_level,
     102             :                                  uint8_t auth_pad_length,
     103             :                                  uint32_t auth_context_id,
     104             :                                  const DATA_BLOB *credentials,
     105             :                                  DATA_BLOB *blob)
     106             : {
     107           0 :         struct dcerpc_auth r;
     108           0 :         enum ndr_err_code ndr_err;
     109             : 
     110        1075 :         r.auth_type             = auth_type;
     111        1075 :         r.auth_level            = auth_level;
     112        1075 :         r.auth_pad_length       = auth_pad_length;
     113        1075 :         r.auth_reserved         = 0;
     114        1075 :         r.auth_context_id       = auth_context_id;
     115        1075 :         r.credentials           = *credentials;
     116             : 
     117        1075 :         ndr_err = ndr_push_struct_blob(blob, mem_ctx, &r,
     118             :                 (ndr_push_flags_fn_t)ndr_push_dcerpc_auth);
     119        1075 :         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
     120           0 :                 return ndr_map_error2ntstatus(ndr_err);
     121             :         }
     122             : 
     123        1075 :         if (DEBUGLEVEL >= 10) {
     124           0 :                 NDR_PRINT_DEBUG(dcerpc_auth, &r);
     125             :         }
     126             : 
     127        1075 :         return NT_STATUS_OK;
     128             : }
     129             : 
     130             : /**
     131             : * @brief Calculate how much data we can in a packet, including calculating
     132             : *        auth token and pad lengths.
     133             : *
     134             : * @param auth           The pipe_auth_data structure for this pipe.
     135             : * @param header_len     The length of the packet header
     136             : * @param data_left      The data left in the send buffer
     137             : * @param max_xmit_frag  The max fragment size.
     138             : * @param data_to_send   [out] The max data we will send in the pdu
     139             : * @param frag_len       [out] The total length of the fragment
     140             : * @param auth_len       [out] The length of the auth trailer
     141             : * @param pad_len        [out] The padding to be applied
     142             : *
     143             : * @return A NT Error status code.
     144             : */
     145      547245 : NTSTATUS dcerpc_guess_sizes(struct pipe_auth_data *auth,
     146             :                             size_t header_len, size_t data_left,
     147             :                             size_t max_xmit_frag,
     148             :                             size_t *data_to_send, size_t *frag_len,
     149             :                             size_t *auth_len, size_t *pad_len)
     150             : {
     151           0 :         size_t max_len;
     152           0 :         size_t mod_len;
     153           0 :         struct gensec_security *gensec_security;
     154             : 
     155             :         /* no auth token cases first */
     156      547245 :         switch (auth->auth_level) {
     157      546879 :         case DCERPC_AUTH_LEVEL_NONE:
     158             :         case DCERPC_AUTH_LEVEL_CONNECT:
     159      546879 :                 max_len = max_xmit_frag - header_len;
     160      546879 :                 *data_to_send = MIN(max_len, data_left);
     161      546879 :                 *pad_len = 0;
     162      546879 :                 *auth_len = 0;
     163      546879 :                 *frag_len = header_len + *data_to_send;
     164      546879 :                 return NT_STATUS_OK;
     165             : 
     166         223 :         case DCERPC_AUTH_LEVEL_PRIVACY:
     167         223 :                 break;
     168             : 
     169          89 :         case DCERPC_AUTH_LEVEL_INTEGRITY:
     170          89 :                 break;
     171             : 
     172          54 :         case DCERPC_AUTH_LEVEL_PACKET:
     173          54 :                 break;
     174             : 
     175           0 :         default:
     176           0 :                 return NT_STATUS_INVALID_PARAMETER;
     177             :         }
     178             : 
     179             : 
     180             :         /* Sign/seal case, calculate auth and pad lengths */
     181             : 
     182         366 :         max_len = max_xmit_frag - header_len - DCERPC_AUTH_TRAILER_LENGTH;
     183             : 
     184             :         /* Treat the same for all authenticated rpc requests. */
     185         366 :         switch (auth->auth_type) {
     186         366 :         case DCERPC_AUTH_TYPE_SPNEGO:
     187             :         case DCERPC_AUTH_TYPE_NTLMSSP:
     188             :         case DCERPC_AUTH_TYPE_KRB5:
     189             :         case DCERPC_AUTH_TYPE_SCHANNEL:
     190         366 :                 gensec_security = auth->auth_ctx;
     191         366 :                 mod_len = (max_len % DCERPC_AUTH_PAD_ALIGNMENT);
     192         366 :                 *auth_len = gensec_sig_size(gensec_security, max_len - mod_len);
     193         366 :                 if (*auth_len == 0) {
     194           0 :                         return NT_STATUS_INTERNAL_ERROR;
     195             :                 }
     196         366 :                 break;
     197           0 :         default:
     198           0 :                 return NT_STATUS_INVALID_PARAMETER;
     199             :         }
     200             : 
     201         366 :         max_len -= *auth_len;
     202         366 :         mod_len = (max_len % DCERPC_AUTH_PAD_ALIGNMENT);
     203         366 :         max_len -= mod_len;
     204             : 
     205         366 :         *data_to_send = MIN(max_len, data_left);
     206             : 
     207         366 :         *pad_len = DCERPC_AUTH_PAD_LENGTH(*data_to_send);
     208             : 
     209         366 :         *frag_len = header_len + *data_to_send + *pad_len
     210         366 :                         + DCERPC_AUTH_TRAILER_LENGTH + *auth_len;
     211             : 
     212         366 :         return NT_STATUS_OK;
     213             : }
     214             : 
     215             : /*******************************************************************
     216             :  Create and add the NTLMSSP sign/seal auth data.
     217             :  ********************************************************************/
     218             : 
     219         366 : static NTSTATUS add_generic_auth_footer(struct gensec_security *gensec_security,
     220             :                                         enum dcerpc_AuthLevel auth_level,
     221             :                                         DATA_BLOB *rpc_out)
     222             : {
     223         366 :         uint16_t data_and_pad_len = rpc_out->length
     224             :                                         - DCERPC_RESPONSE_LENGTH
     225         366 :                                         - DCERPC_AUTH_TRAILER_LENGTH;
     226           0 :         DATA_BLOB auth_blob;
     227           0 :         NTSTATUS status;
     228             : 
     229         366 :         if (!gensec_security) {
     230           0 :                 return NT_STATUS_INVALID_PARAMETER;
     231             :         }
     232             : 
     233         366 :         switch (auth_level) {
     234         223 :         case DCERPC_AUTH_LEVEL_PRIVACY:
     235             :                 /* Data portion is encrypted. */
     236         223 :                 status = gensec_seal_packet(gensec_security,
     237         223 :                                             rpc_out->data,
     238         223 :                                             rpc_out->data
     239             :                                             + DCERPC_RESPONSE_LENGTH,
     240             :                                             data_and_pad_len,
     241         223 :                                             rpc_out->data,
     242             :                                             rpc_out->length,
     243             :                                             &auth_blob);
     244         223 :                 if (!NT_STATUS_IS_OK(status)) {
     245           0 :                         return status;
     246             :                 }
     247         223 :                 break;
     248             : 
     249         143 :         case DCERPC_AUTH_LEVEL_INTEGRITY:
     250             :         case DCERPC_AUTH_LEVEL_PACKET:
     251             :                 /* Data is signed. */
     252         143 :                 status = gensec_sign_packet(gensec_security,
     253         143 :                                             rpc_out->data,
     254         143 :                                             rpc_out->data
     255         143 :                                             + DCERPC_RESPONSE_LENGTH,
     256             :                                             data_and_pad_len,
     257         143 :                                             rpc_out->data,
     258             :                                             rpc_out->length,
     259             :                                             &auth_blob);
     260         143 :                 if (!NT_STATUS_IS_OK(status)) {
     261           0 :                         return status;
     262             :                 }
     263         143 :                 break;
     264             : 
     265           0 :         default:
     266             :                 /* Can't happen. */
     267           0 :                 smb_panic("bad auth level");
     268             :                 /* Notreached. */
     269             :                 return NT_STATUS_INVALID_PARAMETER;
     270             :         }
     271             : 
     272             :         /* Finally attach the blob. */
     273         366 :         if (!data_blob_append(NULL, rpc_out,
     274         366 :                                 auth_blob.data, auth_blob.length)) {
     275           0 :                 DEBUG(0, ("Failed to add %u bytes auth blob.\n",
     276             :                           (unsigned int)auth_blob.length));
     277           0 :                 return NT_STATUS_NO_MEMORY;
     278             :         }
     279         366 :         data_blob_free(&auth_blob);
     280             : 
     281         366 :         return NT_STATUS_OK;
     282             : }
     283             : 
     284             : /*******************************************************************
     285             :  Check/unseal the NTLMSSP auth data. (Unseal in place).
     286             :  ********************************************************************/
     287             : 
     288         366 : static NTSTATUS get_generic_auth_footer(struct gensec_security *gensec_security,
     289             :                                         enum dcerpc_AuthLevel auth_level,
     290             :                                         DATA_BLOB *data, DATA_BLOB *full_pkt,
     291             :                                         DATA_BLOB *auth_token)
     292             : {
     293         366 :         if (gensec_security == NULL) {
     294           0 :                 return NT_STATUS_INVALID_PARAMETER;
     295             :         }
     296             : 
     297         366 :         switch (auth_level) {
     298         223 :         case DCERPC_AUTH_LEVEL_PRIVACY:
     299             :                 /* Data portion is encrypted. */
     300         223 :                 return gensec_unseal_packet(gensec_security,
     301             :                                             data->data,
     302             :                                             data->length,
     303         223 :                                             full_pkt->data,
     304             :                                             full_pkt->length,
     305             :                                             auth_token);
     306             : 
     307         143 :         case DCERPC_AUTH_LEVEL_INTEGRITY:
     308             :         case DCERPC_AUTH_LEVEL_PACKET:
     309             :                 /* Data is signed. */
     310         143 :                 return gensec_check_packet(gensec_security,
     311         143 :                                            data->data,
     312             :                                            data->length,
     313         143 :                                            full_pkt->data,
     314             :                                            full_pkt->length,
     315             :                                            auth_token);
     316             : 
     317           0 :         default:
     318           0 :                 return NT_STATUS_INVALID_PARAMETER;
     319             :         }
     320             : }
     321             : 
     322             : /**
     323             : * @brief   Append an auth footer according to what is the current mechanism
     324             : *
     325             : * @param auth           The pipe_auth_data associated with the connection
     326             : * @param pad_len        The padding used in the packet
     327             : * @param rpc_out        Packet blob up to and including the auth header
     328             : *
     329             : * @return A NTSTATUS error code.
     330             : */
     331         366 : NTSTATUS dcerpc_add_auth_footer(struct pipe_auth_data *auth,
     332             :                                 size_t pad_len, DATA_BLOB *rpc_out)
     333             : {
     334           0 :         struct gensec_security *gensec_security;
     335         366 :         const char pad[DCERPC_AUTH_PAD_ALIGNMENT] = { 0, };
     336           0 :         DATA_BLOB auth_info;
     337           0 :         DATA_BLOB auth_blob;
     338           0 :         NTSTATUS status;
     339             : 
     340         366 :         if (auth->auth_type == DCERPC_AUTH_TYPE_NONE) {
     341           0 :                 return NT_STATUS_OK;
     342             :         }
     343             : 
     344         366 :         if (pad_len) {
     345         190 :                 SMB_ASSERT(pad_len <= ARRAY_SIZE(pad));
     346             : 
     347             :                 /* Copy the sign/seal padding data. */
     348         190 :                 if (!data_blob_append(NULL, rpc_out, pad, pad_len)) {
     349           0 :                         return NT_STATUS_NO_MEMORY;
     350             :                 }
     351             :         }
     352             : 
     353             :         /* marshall the dcerpc_auth with an actually empty auth_blob.
     354             :          * This is needed because the ntmlssp signature includes the
     355             :          * auth header. We will append the actual blob later. */
     356         366 :         auth_blob = data_blob_null;
     357         366 :         status = dcerpc_push_dcerpc_auth(rpc_out->data,
     358             :                                          auth->auth_type,
     359             :                                          auth->auth_level,
     360             :                                          pad_len,
     361             :                                          auth->auth_context_id,
     362             :                                          &auth_blob,
     363             :                                          &auth_info);
     364         366 :         if (!NT_STATUS_IS_OK(status)) {
     365           0 :                 return status;
     366             :         }
     367             : 
     368             :         /* append the header */
     369         366 :         if (!data_blob_append(NULL, rpc_out,
     370         366 :                                 auth_info.data, auth_info.length)) {
     371           0 :                 DEBUG(0, ("Failed to add %u bytes auth blob.\n",
     372             :                           (unsigned int)auth_info.length));
     373           0 :                 return NT_STATUS_NO_MEMORY;
     374             :         }
     375         366 :         data_blob_free(&auth_info);
     376             : 
     377             :         /* Generate any auth sign/seal and add the auth footer. */
     378         366 :         switch (auth->auth_type) {
     379           0 :         case DCERPC_AUTH_TYPE_NONE:
     380           0 :                 status = NT_STATUS_OK;
     381           0 :                 break;
     382         366 :         default:
     383         366 :                 gensec_security = auth->auth_ctx;
     384         366 :                 status = add_generic_auth_footer(gensec_security,
     385             :                                                  auth->auth_level,
     386             :                                                  rpc_out);
     387         366 :                 break;
     388             :         }
     389             : 
     390         366 :         return status;
     391             : }
     392             : 
     393             : /**
     394             : * @brief Check authentication for request/response packets
     395             : *
     396             : * @param auth           The auth data for the connection
     397             : * @param pkt            The actual ncacn_packet
     398             : * @param pkt_trailer [in][out]  The stub_and_verifier part of the packet,
     399             : *                       the auth_trailer and padding will be removed.
     400             : * @param header_size    The header size
     401             : * @param raw_pkt        The whole raw packet data blob
     402             : *
     403             : * @return A NTSTATUS error code
     404             : */
     405      547409 : NTSTATUS dcerpc_check_auth(struct pipe_auth_data *auth,
     406             :                            struct ncacn_packet *pkt,
     407             :                            DATA_BLOB *pkt_trailer,
     408             :                            uint8_t header_size,
     409             :                            DATA_BLOB *raw_pkt)
     410             : {
     411           0 :         struct gensec_security *gensec_security;
     412           0 :         NTSTATUS status;
     413           0 :         struct dcerpc_auth auth_info;
     414           0 :         uint32_t auth_length;
     415           0 :         DATA_BLOB full_pkt;
     416           0 :         DATA_BLOB data;
     417             : 
     418             :         /*
     419             :          * These check should be done in the caller.
     420             :          */
     421      547409 :         SMB_ASSERT(raw_pkt->length == pkt->frag_length);
     422      547409 :         SMB_ASSERT(header_size <= pkt->frag_length);
     423      547409 :         SMB_ASSERT(pkt_trailer->length < pkt->frag_length);
     424      547409 :         SMB_ASSERT((pkt_trailer->length + header_size) <= pkt->frag_length);
     425             : 
     426      547409 :         switch (auth->auth_level) {
     427         223 :         case DCERPC_AUTH_LEVEL_PRIVACY:
     428         223 :                 DEBUG(10, ("Requested Privacy.\n"));
     429         223 :                 break;
     430             : 
     431          89 :         case DCERPC_AUTH_LEVEL_INTEGRITY:
     432          89 :                 DEBUG(10, ("Requested Integrity.\n"));
     433          89 :                 break;
     434             : 
     435          54 :         case DCERPC_AUTH_LEVEL_PACKET:
     436          54 :                 DEBUG(10, ("Requested packet.\n"));
     437          54 :                 break;
     438             : 
     439         136 :         case DCERPC_AUTH_LEVEL_CONNECT:
     440         136 :                 if (pkt->auth_length != 0) {
     441           0 :                         break;
     442             :                 }
     443      547043 :                 return NT_STATUS_OK;
     444             : 
     445      546907 :         case DCERPC_AUTH_LEVEL_NONE:
     446      546907 :                 if (pkt->auth_length != 0) {
     447           0 :                         DEBUG(3, ("Got non-zero auth len on non "
     448             :                                   "authenticated connection!\n"));
     449           0 :                         return NT_STATUS_INVALID_PARAMETER;
     450             :                 }
     451      546907 :                 return NT_STATUS_OK;
     452             : 
     453           0 :         default:
     454           0 :                 DEBUG(3, ("Unimplemented Auth Level %d\n",
     455             :                           auth->auth_level));
     456           0 :                 return NT_STATUS_INVALID_PARAMETER;
     457             :         }
     458             : 
     459         366 :         if (pkt->auth_length == 0) {
     460           0 :                 return NT_STATUS_INVALID_PARAMETER;
     461             :         }
     462             : 
     463         366 :         status = dcerpc_pull_auth_trailer(pkt, pkt, pkt_trailer,
     464             :                                           &auth_info, &auth_length, false);
     465         366 :         if (!NT_STATUS_IS_OK(status)) {
     466           0 :                 return status;
     467             :         }
     468             : 
     469         366 :         if (auth_info.auth_type != auth->auth_type) {
     470           0 :                 return NT_STATUS_INVALID_PARAMETER;
     471             :         }
     472             : 
     473         366 :         if (auth_info.auth_level != auth->auth_level) {
     474           0 :                 return NT_STATUS_INVALID_PARAMETER;
     475             :         }
     476             : 
     477         366 :         if (auth_info.auth_context_id != auth->auth_context_id) {
     478           0 :                 return NT_STATUS_INVALID_PARAMETER;
     479             :         }
     480             : 
     481         366 :         pkt_trailer->length -= auth_length;
     482         366 :         data = data_blob_const(raw_pkt->data + header_size,
     483             :                                pkt_trailer->length);
     484         366 :         full_pkt = data_blob_const(raw_pkt->data, raw_pkt->length);
     485         366 :         full_pkt.length -= auth_info.credentials.length;
     486             : 
     487         366 :         switch (auth->auth_type) {
     488           0 :         case DCERPC_AUTH_TYPE_NONE:
     489           0 :                 return NT_STATUS_OK;
     490             : 
     491         366 :         default:
     492         366 :                 DEBUG(10, ("GENSEC auth\n"));
     493             : 
     494         366 :                 gensec_security = auth->auth_ctx;
     495         366 :                 status = get_generic_auth_footer(gensec_security,
     496             :                                                  auth->auth_level,
     497             :                                                  &data, &full_pkt,
     498             :                                                  &auth_info.credentials);
     499         366 :                 if (!NT_STATUS_IS_OK(status)) {
     500           0 :                         return status;
     501             :                 }
     502         366 :                 break;
     503             :         }
     504             : 
     505             :         /* TODO: remove later
     506             :          * this is still needed because in the server code the
     507             :          * pkt_trailer actually has a copy of the raw data, and they
     508             :          * are still both used in later calls */
     509         366 :         if (auth->auth_level == DCERPC_AUTH_LEVEL_PRIVACY) {
     510         223 :                 if (pkt_trailer->length != data.length) {
     511           0 :                         return NT_STATUS_INVALID_PARAMETER;
     512             :                 }
     513         223 :                 memcpy(pkt_trailer->data, data.data, data.length);
     514             :         }
     515             : 
     516         366 :         pkt_trailer->length -= auth_info.auth_pad_length;
     517         366 :         data_blob_free(&auth_info.credentials);
     518         366 :         return NT_STATUS_OK;
     519             : }
     520             : 

Generated by: LCOV version 1.14