LCOV - code coverage report
Current view: top level - source4/libcli/smb2 - session.c (source / functions) Hit Total Coverage
Test: coverage report for abartlet/fix-coverage dd10fb34 Lines: 161 204 78.9 %
Date: 2021-09-23 10:06:22 Functions: 10 10 100.0 %

          Line data    Source code
       1             : /* 
       2             :    Unix SMB/CIFS implementation.
       3             : 
       4             :    SMB2 client session handling
       5             : 
       6             :    Copyright (C) Andrew Tridgell 2005
       7             :    
       8             :    This program is free software; you can redistribute it and/or modify
       9             :    it under the terms of the GNU General Public License as published by
      10             :    the Free Software Foundation; either version 3 of the License, or
      11             :    (at your option) any later version.
      12             :    
      13             :    This program is distributed in the hope that it will be useful,
      14             :    but WITHOUT ANY WARRANTY; without even the implied warranty of
      15             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      16             :    GNU General Public License for more details.
      17             :    
      18             :    You should have received a copy of the GNU General Public License
      19             :    along with this program.  If not, see <http://www.gnu.org/licenses/>.
      20             : */
      21             : 
      22             : #include "includes.h"
      23             : #include "system/network.h"
      24             : #include <tevent.h>
      25             : #include "lib/util/tevent_ntstatus.h"
      26             : #include "libcli/raw/libcliraw.h"
      27             : #include "libcli/smb2/smb2.h"
      28             : #include "libcli/smb2/smb2_calls.h"
      29             : #include "auth/gensec/gensec.h"
      30             : #include "auth/credentials/credentials.h"
      31             : #include "../libcli/smb/smbXcli_base.h"
      32             : 
      33             : /**
      34             :   initialise a smb2_session structure
      35             :  */
      36       13741 : struct smb2_session *smb2_session_init(struct smb2_transport *transport,
      37             :                                        struct gensec_settings *settings,
      38             :                                        TALLOC_CTX *parent_ctx)
      39             : {
      40             :         struct smb2_session *session;
      41             :         NTSTATUS status;
      42             : 
      43       13741 :         session = talloc_zero(parent_ctx, struct smb2_session);
      44       13741 :         if (!session) {
      45           0 :                 return NULL;
      46             :         }
      47       13741 :         session->transport = talloc_steal(session, transport);
      48             : 
      49       13741 :         session->smbXcli = smbXcli_session_create(session, transport->conn);
      50       13741 :         if (session->smbXcli == NULL) {
      51           0 :                 talloc_free(session);
      52           0 :                 return NULL;
      53             :         }
      54             : 
      55             :         /* prepare a gensec context for later use */
      56       13741 :         status = gensec_client_start(session, &session->gensec,
      57             :                                      settings);
      58       13741 :         if (!NT_STATUS_IS_OK(status)) {
      59           0 :                 talloc_free(session);
      60           0 :                 return NULL;
      61             :         }
      62             : 
      63       13741 :         gensec_want_feature(session->gensec, GENSEC_FEATURE_SESSION_KEY);
      64             : 
      65       13741 :         return session;
      66             : }
      67             : 
      68             : /*
      69             :  * Note: that the caller needs to keep 'transport' around as
      70             :  *       long as the returned session is active!
      71             :  */
      72        1890 : struct smb2_session *smb2_session_channel(struct smb2_transport *transport,
      73             :                                           struct gensec_settings *settings,
      74             :                                           TALLOC_CTX *parent_ctx,
      75             :                                           struct smb2_session *base_session)
      76             : {
      77             :         struct smb2_session *session;
      78             :         NTSTATUS status;
      79             : 
      80        1890 :         session = talloc_zero(parent_ctx, struct smb2_session);
      81        1890 :         if (!session) {
      82           0 :                 return NULL;
      83             :         }
      84        1890 :         session->transport = transport;
      85             : 
      86        1890 :         status = smb2cli_session_create_channel(session,
      87             :                                                 base_session->smbXcli,
      88             :                                                 transport->conn,
      89             :                                                 &session->smbXcli);
      90        1890 :         if (!NT_STATUS_IS_OK(status)) {
      91           0 :                 talloc_free(session);
      92           0 :                 return NULL;
      93             :         }
      94             : 
      95        1890 :         session->needs_bind = true;
      96             : 
      97             :         /* prepare a gensec context for later use */
      98        1890 :         status = gensec_client_start(session, &session->gensec,
      99             :                                      settings);
     100        1890 :         if (!NT_STATUS_IS_OK(status)) {
     101           0 :                 talloc_free(session);
     102           0 :                 return NULL;
     103             :         }
     104             : 
     105        1890 :         gensec_want_feature(session->gensec, GENSEC_FEATURE_SESSION_KEY);
     106             : 
     107        1890 :         return session;
     108             : }
     109             : 
     110             : struct smb2_session_setup_spnego_state {
     111             :         struct tevent_context *ev;
     112             :         struct smb2_session *session;
     113             :         struct cli_credentials *credentials;
     114             :         uint64_t previous_session_id;
     115             :         bool session_bind;
     116             :         bool reauth;
     117             :         NTSTATUS gensec_status;
     118             :         NTSTATUS remote_status;
     119             :         DATA_BLOB in_secblob;
     120             :         DATA_BLOB out_secblob;
     121             :         struct iovec *recv_iov;
     122             : };
     123             : 
     124             : static void smb2_session_setup_spnego_gensec_next(struct tevent_req *req);
     125             : static void smb2_session_setup_spnego_gensec_done(struct tevent_req *subreq);
     126             : static void smb2_session_setup_spnego_smb2_next(struct tevent_req *req);
     127             : static void smb2_session_setup_spnego_smb2_done(struct tevent_req *subreq);
     128             : static void smb2_session_setup_spnego_both_ready(struct tevent_req *req);
     129             : 
     130             : /*
     131             :   a composite function that does a full SPNEGO session setup
     132             :  */
     133       14639 : struct tevent_req *smb2_session_setup_spnego_send(
     134             :                                 TALLOC_CTX *mem_ctx,
     135             :                                 struct tevent_context *ev,
     136             :                                 struct smb2_session *session,
     137             :                                 struct cli_credentials *credentials,
     138             :                                 uint64_t previous_session_id)
     139             : {
     140       14639 :         struct smb2_transport *transport = session->transport;
     141             :         struct tevent_req *req;
     142             :         struct smb2_session_setup_spnego_state *state;
     143             :         uint64_t current_session_id;
     144             :         const char *chosen_oid;
     145             :         NTSTATUS status;
     146             :         const DATA_BLOB *server_gss_blob;
     147             :         struct timeval endtime;
     148             :         bool ok;
     149             : 
     150       14639 :         req = tevent_req_create(mem_ctx, &state,
     151             :                                 struct smb2_session_setup_spnego_state);
     152       14639 :         if (req == NULL) {
     153           0 :                 return NULL;
     154             :         }
     155       14639 :         state->ev = ev;
     156       14639 :         state->session = session;
     157       14639 :         state->credentials = credentials;
     158       14639 :         state->previous_session_id = previous_session_id;
     159       14639 :         state->gensec_status = NT_STATUS_MORE_PROCESSING_REQUIRED;
     160       14639 :         state->remote_status = NT_STATUS_MORE_PROCESSING_REQUIRED;
     161             : 
     162       14639 :         endtime = timeval_current_ofs(transport->options.request_timeout, 0);
     163             : 
     164       14639 :         ok = tevent_req_set_endtime(req, ev, endtime);
     165       14639 :         if (!ok) {
     166           0 :                 return tevent_req_post(req, ev);
     167             :         }
     168             : 
     169       14639 :         current_session_id = smb2cli_session_current_id(state->session->smbXcli);
     170       14639 :         if (state->session->needs_bind) {
     171        1522 :                 state->session_bind = true;
     172       13117 :         } else if (current_session_id != 0) {
     173         826 :                 state->reauth = true;
     174             :         }
     175       14639 :         server_gss_blob = smbXcli_conn_server_gss_blob(session->transport->conn);
     176       14639 :         if (server_gss_blob) {
     177       14639 :                 state->out_secblob = *server_gss_blob;
     178             :         }
     179             : 
     180       14639 :         status = gensec_set_credentials(session->gensec, credentials);
     181       14639 :         if (tevent_req_nterror(req, status)) {
     182           0 :                 return tevent_req_post(req, ev);
     183             :         }
     184             : 
     185       14639 :         status = gensec_set_target_hostname(session->gensec,
     186       14639 :                                             smbXcli_conn_remote_name(session->transport->conn));
     187       14639 :         if (tevent_req_nterror(req, status)) {
     188           0 :                 return tevent_req_post(req, ev);
     189             :         }
     190             : 
     191       14639 :         status = gensec_set_target_service(session->gensec, "cifs");
     192       14639 :         if (tevent_req_nterror(req, status)) {
     193           0 :                 return tevent_req_post(req, ev);
     194             :         }
     195             : 
     196       14639 :         if (state->out_secblob.length > 0) {
     197       14639 :                 chosen_oid = GENSEC_OID_SPNEGO;
     198       14639 :                 status = gensec_start_mech_by_oid(session->gensec, chosen_oid);
     199       14639 :                 if (!NT_STATUS_IS_OK(status)) {
     200          72 :                         DEBUG(1, ("Failed to start set GENSEC client mechanism %s: %s\n",
     201             :                                   gensec_get_name_by_oid(session->gensec,
     202             :                                                          chosen_oid),
     203             :                                   nt_errstr(status)));
     204          72 :                         state->out_secblob = data_blob_null;
     205          72 :                         chosen_oid = GENSEC_OID_NTLMSSP;
     206          72 :                         status = gensec_start_mech_by_oid(session->gensec,
     207             :                                                           chosen_oid);
     208          72 :                         if (!NT_STATUS_IS_OK(status)) {
     209           0 :                                 DEBUG(1, ("Failed to start set (fallback) GENSEC client mechanism %s: %s\n",
     210             :                                           gensec_get_name_by_oid(session->gensec,
     211             :                                                                  chosen_oid),
     212             :                                           nt_errstr(status)));
     213             :                         }
     214             :                 }
     215       14639 :                 if (tevent_req_nterror(req, status)) {
     216           0 :                         return tevent_req_post(req, ev);
     217             :                 }
     218             :         } else {
     219           0 :                 chosen_oid = GENSEC_OID_NTLMSSP;
     220           0 :                 status = gensec_start_mech_by_oid(session->gensec, chosen_oid);
     221           0 :                 if (!NT_STATUS_IS_OK(status)) {
     222           0 :                         DEBUG(1, ("Failed to start set GENSEC client mechanism %s: %s\n",
     223             :                                   gensec_get_name_by_oid(session->gensec,
     224             :                                                          chosen_oid),
     225             :                                   nt_errstr(status)));
     226             :                 }
     227           0 :                 if (tevent_req_nterror(req, status)) {
     228           0 :                         return tevent_req_post(req, ev);
     229             :                 }
     230             :         }
     231             : 
     232       14639 :         smb2_session_setup_spnego_gensec_next(req);
     233       14639 :         if (!tevent_req_is_in_progress(req)) {
     234           0 :                 return tevent_req_post(req, ev);
     235             :         }
     236             : 
     237       14249 :         return req;
     238             : }
     239             : 
     240       33049 : static void smb2_session_setup_spnego_gensec_next(struct tevent_req *req)
     241             : {
     242       26993 :         struct smb2_session_setup_spnego_state *state =
     243       33049 :                 tevent_req_data(req,
     244             :                 struct smb2_session_setup_spnego_state);
     245       33049 :         struct smb2_session *session = state->session;
     246       33049 :         struct tevent_req *subreq = NULL;
     247             : 
     248       33049 :         if (NT_STATUS_IS_OK(state->gensec_status)) {
     249           0 :                 tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
     250           0 :                 return;
     251             :         }
     252             : 
     253       33049 :         subreq = gensec_update_send(state, state->ev,
     254             :                                     session->gensec,
     255             :                                     state->out_secblob);
     256       33049 :         if (tevent_req_nomem(subreq, req)) {
     257           0 :                 return;
     258             :         }
     259       33049 :         tevent_req_set_callback(subreq,
     260             :                                 smb2_session_setup_spnego_gensec_done,
     261             :                                 req);
     262             : }
     263             : 
     264       33049 : static void smb2_session_setup_spnego_gensec_done(struct tevent_req *subreq)
     265             : {
     266       26993 :         struct tevent_req *req =
     267       33049 :                 tevent_req_callback_data(subreq,
     268             :                 struct tevent_req);
     269       26993 :         struct smb2_session_setup_spnego_state *state =
     270       33049 :                 tevent_req_data(req,
     271             :                 struct smb2_session_setup_spnego_state);
     272             :         NTSTATUS status;
     273             : 
     274       33049 :         status = gensec_update_recv(subreq, state,
     275             :                                     &state->in_secblob);
     276       33049 :         state->gensec_status = status;
     277       33049 :         state->out_secblob = data_blob_null;
     278       50554 :         if (!NT_STATUS_IS_OK(status) &&
     279       21031 :             !NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
     280          22 :                 tevent_req_nterror(req, status);
     281          22 :                 return;
     282             :         }
     283             : 
     284       41661 :         if (NT_STATUS_IS_OK(state->remote_status) &&
     285       11164 :             NT_STATUS_IS_OK(state->gensec_status)) {
     286       11554 :                 smb2_session_setup_spnego_both_ready(req);
     287       11554 :                 return;
     288             :         }
     289             : 
     290       21473 :         smb2_session_setup_spnego_smb2_next(req);
     291             : }
     292             : 
     293       21473 : static void smb2_session_setup_spnego_smb2_next(struct tevent_req *req)
     294             : {
     295       17947 :         struct smb2_session_setup_spnego_state *state =
     296       21473 :                 tevent_req_data(req,
     297             :                 struct smb2_session_setup_spnego_state);
     298       21473 :         struct smb2_session *session = state->session;
     299             :         uint32_t timeout_msec;
     300       21473 :         uint8_t in_flags = 0;
     301       21473 :         struct tevent_req *subreq = NULL;
     302             : 
     303       21473 :         if (NT_STATUS_IS_OK(state->remote_status)) {
     304           0 :                 tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
     305           0 :                 return;
     306             :         }
     307             : 
     308       21473 :         timeout_msec = session->transport->options.request_timeout * 1000;
     309             : 
     310       21473 :         if (state->session_bind) {
     311        2044 :                 in_flags |= SMB2_SESSION_FLAG_BINDING;
     312             :         }
     313             : 
     314       39028 :         subreq = smb2cli_session_setup_send(state, state->ev,
     315       21081 :                                             session->transport->conn,
     316             :                                             timeout_msec,
     317             :                                             session->smbXcli,
     318             :                                             in_flags,
     319             :                                             0, /* in_capabilities */
     320             :                                             0, /* in_channel */
     321             :                                             state->previous_session_id,
     322       21473 :                                             &state->in_secblob);
     323       21473 :         if (tevent_req_nomem(subreq, req)) {
     324           0 :                 return;
     325             :         }
     326       21473 :         tevent_req_set_callback(subreq,
     327             :                                 smb2_session_setup_spnego_smb2_done,
     328             :                                 req);
     329             : }
     330             : 
     331             : /*
     332             :   handle continuations of the spnego session setup
     333             : */
     334       21473 : static void smb2_session_setup_spnego_smb2_done(struct tevent_req *subreq)
     335             : {
     336       17947 :         struct tevent_req *req =
     337       21473 :                 tevent_req_callback_data(subreq,
     338             :                 struct tevent_req);
     339       17947 :         struct smb2_session_setup_spnego_state *state =
     340       21473 :                 tevent_req_data(req,
     341             :                 struct smb2_session_setup_spnego_state);
     342             :         NTSTATUS status;
     343             : 
     344       21473 :         status = smb2cli_session_setup_recv(subreq, state,
     345             :                                             &state->recv_iov,
     346             :                                             &state->out_secblob);
     347       21473 :         state->remote_status = status;
     348       21473 :         state->in_secblob = data_blob_null;
     349       30322 :         if (!NT_STATUS_IS_OK(status) &&
     350        9845 :             !NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
     351        2991 :                 tevent_req_nterror(req, status);
     352        2991 :                 return;
     353             :         }
     354             : 
     355       27578 :         if (NT_STATUS_IS_OK(state->remote_status) &&
     356       11626 :             NT_STATUS_IS_OK(state->gensec_status)) {
     357          72 :                 smb2_session_setup_spnego_both_ready(req);
     358          72 :                 return;
     359             :         }
     360             : 
     361       18410 :         smb2_session_setup_spnego_gensec_next(req);
     362             : }
     363             : 
     364       11626 : static void smb2_session_setup_spnego_both_ready(struct tevent_req *req)
     365             : {
     366        9096 :         struct smb2_session_setup_spnego_state *state =
     367       11626 :                 tevent_req_data(req,
     368             :                 struct smb2_session_setup_spnego_state);
     369       11626 :         struct smb2_session *session = state->session;
     370             :         NTSTATUS status;
     371             :         DATA_BLOB session_key;
     372             : 
     373       11626 :         if (state->out_secblob.length != 0) {
     374           0 :                 tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
     375           0 :                 return;
     376             :         }
     377             : 
     378       11626 :         if (state->in_secblob.length != 0) {
     379           0 :                 tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
     380           0 :                 return;
     381             :         }
     382             : 
     383       11626 :         if (state->reauth) {
     384         114 :                 tevent_req_done(req);
     385         114 :                 return;
     386             :         }
     387             : 
     388       11512 :         if (cli_credentials_is_anonymous(state->credentials)) {
     389             :                 /*
     390             :                  * Windows server does not set the
     391             :                  * SMB2_SESSION_FLAG_IS_GUEST nor
     392             :                  * SMB2_SESSION_FLAG_IS_NULL flag.
     393             :                  *
     394             :                  * This fix makes sure we do not try
     395             :                  * to verify a signature on the final
     396             :                  * session setup response.
     397             :                  */
     398         203 :                 tevent_req_done(req);
     399         203 :                 return;
     400             :         }
     401             : 
     402       11309 :         status = gensec_session_key(session->gensec, state,
     403             :                                     &session_key);
     404       11309 :         if (tevent_req_nterror(req, status)) {
     405           0 :                 return;
     406             :         }
     407             : 
     408       11309 :         if (state->session_bind) {
     409         808 :                 status = smb2cli_session_set_channel_key(session->smbXcli,
     410             :                                                          session_key,
     411         808 :                                                          state->recv_iov);
     412         808 :                 if (tevent_req_nterror(req, status)) {
     413           0 :                         return;
     414             :                 }
     415         808 :                 session->needs_bind = false;
     416             :         } else {
     417       10501 :                 status = smb2cli_session_set_session_key(session->smbXcli,
     418             :                                                          session_key,
     419       10501 :                                                          state->recv_iov);
     420       10501 :                 if (tevent_req_nterror(req, status)) {
     421           0 :                         return;
     422             :                 }
     423             :         }
     424       11309 :         tevent_req_done(req);
     425       11309 :         return;
     426             : }
     427             : 
     428             : /*
     429             :   receive a composite session setup reply
     430             : */
     431       14639 : NTSTATUS smb2_session_setup_spnego_recv(struct tevent_req *req)
     432             : {
     433       14639 :         return tevent_req_simple_recv_ntstatus(req);
     434             : }
     435             : 
     436             : /*
     437             :   sync version of smb2_session_setup_spnego
     438             : */
     439        3923 : NTSTATUS smb2_session_setup_spnego(struct smb2_session *session, 
     440             :                                    struct cli_credentials *credentials,
     441             :                                    uint64_t previous_session_id)
     442             : {
     443             :         struct tevent_req *subreq;
     444             :         NTSTATUS status;
     445             :         bool ok;
     446        3923 :         TALLOC_CTX *frame = talloc_stackframe();
     447        3923 :         struct tevent_context *ev = session->transport->ev;
     448             : 
     449        3923 :         if (frame == NULL) {
     450           0 :                 return NT_STATUS_NO_MEMORY;
     451             :         }
     452             : 
     453        3923 :         subreq = smb2_session_setup_spnego_send(frame, ev,
     454             :                                                 session, credentials,
     455             :                                                 previous_session_id);
     456        3923 :         if (subreq == NULL) {
     457           0 :                 TALLOC_FREE(frame);
     458           0 :                 return NT_STATUS_NO_MEMORY;
     459             :         }
     460             : 
     461        3923 :         ok = tevent_req_poll(subreq, ev);
     462        3923 :         if (!ok) {
     463           0 :                 status = map_nt_error_from_unix_common(errno);
     464           0 :                 TALLOC_FREE(frame);
     465           0 :                 return status;
     466             :         }
     467             : 
     468        3923 :         status = smb2_session_setup_spnego_recv(subreq);
     469        3923 :         TALLOC_FREE(subreq);
     470        3923 :         if (!NT_STATUS_IS_OK(status)) {
     471        2979 :                 TALLOC_FREE(frame);
     472        2979 :                 return status;
     473             :         }
     474             : 
     475         944 :         TALLOC_FREE(frame);
     476         944 :         return NT_STATUS_OK;
     477             : }

Generated by: LCOV version 1.13