LCOV - code coverage report
Current view: top level - libcli/http - http_auth.c (source / functions) Hit Total Coverage
Test: coverage report for master 2f515e9b Lines: 84 173 48.6 %
Date: 2024-04-21 15:09:00 Functions: 5 7 71.4 %

          Line data    Source code
       1             : /*
       2             :    Unix SMB/CIFS implementation.
       3             : 
       4             :    HTTP library
       5             : 
       6             :    Copyright (C) 2014 Samuel Cabrero <samuelcabrero@kernevil.me>
       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 "http.h"
      24             : #include "http_internal.h"
      25             : #include "lib/util/tevent_ntstatus.h"
      26             : #include "lib/param/param.h"
      27             : #include "tevent.h"
      28             : #include "auth/gensec/gensec.h"
      29             : #include "auth/credentials/credentials.h"
      30             : #include "lib/util/data_blob.h"
      31             : 
      32             : #undef strcasecmp
      33             : #undef strncasecmp
      34             : 
      35             : /**
      36             :  * Copy the request headers from src to dst
      37             :  */
      38          14 : static NTSTATUS http_copy_header(const struct http_request *src,
      39             :                                  struct http_request *dst)
      40             : {
      41           0 :         struct http_header *h;
      42             : 
      43          14 :         dst->type = src->type;
      44          14 :         dst->major = src->major;
      45          14 :         dst->minor = src->minor;
      46          14 :         dst->uri = talloc_strdup(dst, src->uri);
      47             : 
      48          42 :         for (h = src->headers; h != NULL; h = h->next) {
      49          28 :                 http_add_header(dst, &dst->headers, h->key, h->value);
      50             :         }
      51          14 :         dst->headers_size = src->headers_size;
      52             : 
      53          14 :         return NT_STATUS_OK;
      54             : }
      55             : 
      56             : /*
      57             :  * Retrieve the WWW-Authenticate header from server response based on the
      58             :  * authentication scheme being used.
      59             :  */
      60           0 : static NTSTATUS http_parse_auth_response(const DATA_BLOB prefix,
      61             :                                          struct http_request *auth_response,
      62             :                                          DATA_BLOB *in)
      63             : {
      64           0 :         struct http_header *h;
      65             : 
      66           0 :         for (h = auth_response->headers; h != NULL; h = h->next) {
      67           0 :                 int cmp;
      68             : 
      69           0 :                 cmp = strcasecmp(h->key, "WWW-Authenticate");
      70           0 :                 if (cmp != 0) {
      71           0 :                         continue;
      72             :                 }
      73             : 
      74           0 :                 cmp = strncasecmp(h->value,
      75           0 :                                   (const char *)prefix.data,
      76           0 :                                   prefix.length);
      77           0 :                 if (cmp != 0) {
      78           0 :                         continue;
      79             :                 }
      80             : 
      81           0 :                 *in = data_blob_string_const(h->value);
      82           0 :                 return NT_STATUS_OK;
      83             :         }
      84             : 
      85           0 :         return NT_STATUS_NOT_SUPPORTED;
      86             : }
      87             : 
      88             : struct http_auth_state {
      89             :         struct tevent_context *ev;
      90             : 
      91             :         struct http_conn *http_conn;
      92             : 
      93             :         enum http_auth_method auth;
      94             :         DATA_BLOB prefix;
      95             : 
      96             :         struct gensec_security *gensec_ctx;
      97             :         NTSTATUS gensec_status;
      98             : 
      99             :         const struct http_request *original_request;
     100             :         struct http_request *next_request;
     101             :         struct http_request *auth_response;
     102             : };
     103             : 
     104             : 
     105             : static void http_send_auth_request_gensec_done(struct tevent_req *subreq);
     106             : static void http_send_auth_request_http_req_done(struct tevent_req *subreq);
     107             : static void http_send_auth_request_http_rep_done(struct tevent_req *subreq);
     108             : 
     109          14 : struct tevent_req *http_send_auth_request_send(TALLOC_CTX *mem_ctx,
     110             :                                                struct tevent_context *ev,
     111             :                                                struct http_conn *http_conn,
     112             :                                                const struct http_request *original_request,
     113             :                                                struct cli_credentials *credentials,
     114             :                                                struct loadparm_context *lp_ctx,
     115             :                                                enum http_auth_method auth)
     116             : {
     117          14 :         struct tevent_req *req = NULL;
     118          14 :         struct http_auth_state *state = NULL;
     119          14 :         struct tevent_req *subreq = NULL;
     120          14 :         DATA_BLOB gensec_in = data_blob_null;
     121           0 :         NTSTATUS status;
     122          14 :         struct http_header *h = NULL;
     123          14 :         const char *mech_name = NULL;
     124             : 
     125          14 :         req = tevent_req_create(mem_ctx, &state, struct http_auth_state);
     126          14 :         if (req == NULL) {
     127           0 :                 return NULL;
     128             :         }
     129          14 :         state->ev = ev;
     130          14 :         state->http_conn = http_conn;
     131          14 :         state->auth = auth;
     132          14 :         state->original_request = original_request;
     133             : 
     134          14 :         status = gensec_init();
     135          14 :         if (tevent_req_nterror(req, status)) {
     136           0 :                 return tevent_req_post(req, ev);
     137             :         }
     138             : 
     139          14 :         status = gensec_client_start(state, &state->gensec_ctx,
     140             :                                      lpcfg_gensec_settings(state, lp_ctx));
     141          14 :         if (tevent_req_nterror(req, status)) {
     142           0 :                 return tevent_req_post(req, ev);
     143             :         }
     144             : 
     145          14 :         status = gensec_set_credentials(state->gensec_ctx, credentials);
     146          14 :         if (tevent_req_nterror(req, status)) {
     147           0 :                 return tevent_req_post(req, ev);
     148             :         }
     149             : 
     150          42 :         for (h = original_request->headers; h != NULL; h = h->next) {
     151           0 :                 int cmp;
     152             : 
     153          28 :                 cmp = strcasecmp(h->key, "Host");
     154          28 :                 if (cmp != 0) {
     155          28 :                         continue;
     156             :                 }
     157             : 
     158           0 :                 status = gensec_set_target_service(state->gensec_ctx, "http");
     159           0 :                 if (tevent_req_nterror(req, status)) {
     160           0 :                         return tevent_req_post(req, ev);
     161             :                 }
     162             : 
     163           0 :                 status = gensec_set_target_hostname(state->gensec_ctx, h->value);
     164           0 :                 if (tevent_req_nterror(req, status)) {
     165           0 :                         return tevent_req_post(req, ev);
     166             :                 }
     167           0 :                 break;
     168             :         }
     169             : 
     170          14 :         switch (state->auth) {
     171          14 :         case HTTP_AUTH_BASIC:
     172          14 :                 mech_name = "http_basic";
     173          14 :                 state->prefix = data_blob_string_const("Basic");
     174          14 :                 break;
     175           0 :         case HTTP_AUTH_NTLM:
     176           0 :                 mech_name = "http_ntlm";
     177           0 :                 state->prefix = data_blob_string_const("NTLM");
     178           0 :                 break;
     179           0 :         case HTTP_AUTH_NEGOTIATE:
     180           0 :                 mech_name = "http_negotiate";
     181           0 :                 state->prefix = data_blob_string_const("Negotiate");
     182           0 :                 break;
     183           0 :         default:
     184           0 :                 tevent_req_nterror(req, NT_STATUS_NOT_SUPPORTED);
     185           0 :                 return tevent_req_post(req, ev);
     186             :         }
     187             : 
     188          14 :         status = gensec_start_mech_by_name(state->gensec_ctx, mech_name);
     189          14 :         if (tevent_req_nterror(req, status)) {
     190           0 :                 return tevent_req_post(req, ev);
     191             :         }
     192             : 
     193          14 :         subreq = gensec_update_send(state, state->ev,
     194          14 :                                     state->gensec_ctx,
     195             :                                     gensec_in);
     196          14 :         if (tevent_req_nomem(subreq, req)) {
     197           0 :                 return tevent_req_post(req, ev);
     198             :         }
     199          14 :         tevent_req_set_callback(subreq, http_send_auth_request_gensec_done, req);
     200             : 
     201          14 :         return req;
     202             : }
     203             : 
     204          14 : static void http_send_auth_request_gensec_done(struct tevent_req *subreq)
     205             : {
     206           0 :         struct tevent_req *req =
     207          14 :                 tevent_req_callback_data(subreq,
     208             :                 struct tevent_req);
     209           0 :         struct http_auth_state *state =
     210          14 :                 tevent_req_data(req,
     211             :                 struct http_auth_state);
     212          14 :         DATA_BLOB gensec_out = data_blob_null;
     213           0 :         NTSTATUS status;
     214           0 :         int ret;
     215             : 
     216          14 :         TALLOC_FREE(state->auth_response);
     217             : 
     218          14 :         status = gensec_update_recv(subreq, state, &gensec_out);
     219          14 :         TALLOC_FREE(subreq);
     220          14 :         state->gensec_status = status;
     221          14 :         if (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
     222           0 :                 status = NT_STATUS_OK;
     223             :         }
     224          14 :         if (tevent_req_nterror(req, status)) {
     225           0 :                 return;
     226             :         }
     227             : 
     228          14 :         state->next_request = talloc_zero(state, struct http_request);
     229          14 :         if (tevent_req_nomem(state->next_request, req)) {
     230           0 :                 return;
     231             :         }
     232             : 
     233          14 :         status = http_copy_header(state->original_request, state->next_request);
     234          14 :         if (tevent_req_nterror(req, status)) {
     235           0 :                 return;
     236             :         }
     237             : 
     238          14 :         if (!NT_STATUS_IS_OK(state->gensec_status)) {
     239             :                 /*
     240             :                  * More preprocessing required before we
     241             :                  * can include the content.
     242             :                  */
     243           0 :                 ret = http_replace_header(state->next_request,
     244           0 :                                           &state->next_request->headers,
     245             :                                           "Content-Length", "0");
     246           0 :                 if (ret != 0) {
     247           0 :                         tevent_req_oom(req);
     248           0 :                         return;
     249             :                 }
     250             :         } else {
     251          14 :                 state->next_request->body = state->original_request->body;
     252             :         }
     253             : 
     254          14 :         if (gensec_out.length > 0) {
     255          14 :                 ret = http_add_header(state->next_request,
     256          14 :                                       &state->next_request->headers,
     257             :                                       "Authorization",
     258          14 :                                       (char *)gensec_out.data);
     259          14 :                 if (ret != 0) {
     260           0 :                         tevent_req_oom(req);
     261           0 :                         return;
     262             :                 }
     263          14 :                 data_blob_free(&gensec_out);
     264             :         }
     265             : 
     266          14 :         subreq = http_send_request_send(state, state->ev,
     267             :                                         state->http_conn,
     268             :                                         state->next_request);
     269          14 :         if (tevent_req_nomem(subreq, req)) {
     270           0 :                 return;
     271             :         }
     272          14 :         tevent_req_set_callback(subreq,
     273             :                                 http_send_auth_request_http_req_done,
     274             :                                 req);
     275             : }
     276             : 
     277          14 : static void http_send_auth_request_http_req_done(struct tevent_req *subreq)
     278             : {
     279           0 :         struct tevent_req *req =
     280          14 :                 tevent_req_callback_data(subreq,
     281             :                 struct tevent_req);
     282           0 :         struct http_auth_state *state =
     283          14 :                 tevent_req_data(req,
     284             :                 struct http_auth_state);
     285           0 :         NTSTATUS status;
     286             : 
     287          14 :         TALLOC_FREE(state->next_request);
     288             : 
     289          14 :         status = http_send_request_recv(subreq);
     290          14 :         TALLOC_FREE(subreq);
     291          14 :         if (tevent_req_nterror(req, status)) {
     292          14 :                 return;
     293             :         }
     294             : 
     295             :         /*
     296             :          * If no more processing required, it is done
     297             :          *
     298             :          * The caller will use http_read_response_send/recv
     299             :          * in order to get the high level response.
     300             :          */
     301          14 :         if (NT_STATUS_IS_OK(state->gensec_status)) {
     302          14 :                 tevent_req_done(req);
     303          14 :                 return;
     304             :         }
     305             : 
     306             :         /*
     307             :          * If more processing required, read the response from server
     308             :          *
     309             :          * We may get an empty RPCH Echo packet from the server
     310             :          * on the "RPC_OUT_DATA" path. We need to consume this
     311             :          * from the socket, but for now we just ignore the bytes.
     312             :          */
     313           0 :         subreq = http_read_response_send(state, state->ev,
     314             :                                          state->http_conn,
     315             :                                          UINT16_MAX);
     316           0 :         if (tevent_req_nomem(subreq, req)) {
     317           0 :                 return;
     318             :         }
     319           0 :         tevent_req_set_callback(subreq,
     320             :                                 http_send_auth_request_http_rep_done,
     321             :                                 req);
     322             : }
     323             : 
     324           0 : static void http_send_auth_request_http_rep_done(struct tevent_req *subreq)
     325             : {
     326           0 :         struct tevent_req *req =
     327           0 :                 tevent_req_callback_data(subreq,
     328             :                 struct tevent_req);
     329           0 :         struct http_auth_state *state =
     330           0 :                 tevent_req_data(req,
     331             :                 struct http_auth_state);
     332           0 :         DATA_BLOB gensec_in = data_blob_null;
     333           0 :         NTSTATUS status;
     334             : 
     335           0 :         status = http_read_response_recv(subreq, state,
     336             :                                          &state->auth_response);
     337           0 :         TALLOC_FREE(subreq);
     338           0 :         if (tevent_req_nterror(req, status)) {
     339           0 :                 return;
     340             :         }
     341             : 
     342             :         /*
     343             :          * We we asked for up to UINT16_MAX bytes of
     344             :          * content, we don't expect
     345             :          * state->auth_response->remaining_content_length
     346             :          * to be set.
     347             :          *
     348             :          * For now we just ignore any bytes in
     349             :          * state->auth_response->body.
     350             :          */
     351           0 :         if (state->auth_response->remaining_content_length != 0) {
     352           0 :                 tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
     353           0 :                 return;
     354             :         }
     355             : 
     356           0 :         status = http_parse_auth_response(state->prefix,
     357             :                                           state->auth_response,
     358             :                                           &gensec_in);
     359           0 :         if (tevent_req_nterror(req, status)) {
     360           0 :                 return;
     361             :         }
     362             : 
     363           0 :         subreq = gensec_update_send(state, state->ev,
     364             :                                     state->gensec_ctx,
     365             :                                     gensec_in);
     366           0 :         if (tevent_req_nomem(subreq, req)) {
     367           0 :                 return;
     368             :         }
     369           0 :         tevent_req_set_callback(subreq, http_send_auth_request_gensec_done, req);
     370             : }
     371             : 
     372          14 : NTSTATUS http_send_auth_request_recv(struct tevent_req *req)
     373             : {
     374           0 :         NTSTATUS status;
     375             : 
     376          14 :         if (tevent_req_is_nterror(req, &status)) {
     377           0 :                 tevent_req_received(req);
     378           0 :                 return status;
     379             :         }
     380          14 :         tevent_req_received(req);
     381             : 
     382          14 :         return NT_STATUS_OK;
     383             : }

Generated by: LCOV version 1.14