Coverage Report

Created: 2026-04-01 06:26

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/samba/libcli/http/gensec/generic.c
Line
Count
Source
1
/*
2
   Unix SMB/CIFS implementation.
3
4
   HTTP library - NTLM authentication mechanism gensec module
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 <tevent.h>
24
#include "lib/util/tevent_ntstatus.h"
25
#include "auth/auth.h"
26
#include "auth/gensec/gensec.h"
27
#include "auth/gensec/gensec_internal.h"
28
#include "lib/util/base64.h"
29
30
#undef strncasecmp
31
32
_PUBLIC_ NTSTATUS gensec_http_generic_init(TALLOC_CTX *);
33
34
struct gensec_http_generic_state {
35
  struct gensec_security *sub;
36
  DATA_BLOB prefix;
37
};
38
39
static NTSTATUS gensec_http_generic_client_start(struct gensec_security *gensec,
40
             const char *prefix_str,
41
             const char *mech_oid)
42
0
{
43
0
  NTSTATUS status;
44
0
  struct gensec_http_generic_state *state;
45
46
0
  state = talloc_zero(gensec, struct gensec_http_generic_state);
47
0
  if (state == NULL) {
48
0
    return NT_STATUS_NO_MEMORY;
49
0
  }
50
0
  gensec->private_data = state;
51
52
0
  state->prefix = data_blob_string_const(prefix_str);
53
54
0
  status = gensec_subcontext_start(state, gensec, &state->sub);
55
0
  if (!NT_STATUS_IS_OK(status)) {
56
0
    return status;
57
0
  }
58
59
0
  return gensec_start_mech_by_oid(state->sub, mech_oid);
60
0
}
61
62
static NTSTATUS gensec_http_ntlm_client_start(struct gensec_security *gensec)
63
0
{
64
0
  return gensec_http_generic_client_start(gensec, "NTLM",
65
0
            GENSEC_OID_NTLMSSP);
66
0
}
67
68
static NTSTATUS gensec_http_negotiate_client_start(struct gensec_security *gensec)
69
0
{
70
0
  return gensec_http_generic_client_start(gensec, "Negotiate",
71
0
            GENSEC_OID_SPNEGO);
72
0
}
73
74
struct gensec_http_generic_update_state {
75
  struct gensec_security *gensec;
76
  DATA_BLOB sub_in;
77
  NTSTATUS status;
78
  DATA_BLOB out;
79
};
80
81
static void gensec_http_generic_update_done(struct tevent_req *subreq);
82
83
static struct tevent_req *gensec_http_generic_update_send(TALLOC_CTX *mem_ctx,
84
                struct tevent_context *ev,
85
                struct gensec_security *gensec_ctx,
86
                const DATA_BLOB in)
87
0
{
88
0
  struct gensec_http_generic_state *http_generic =
89
0
    talloc_get_type_abort(gensec_ctx->private_data,
90
0
              struct gensec_http_generic_state);
91
0
  struct tevent_req *req = NULL;
92
0
  struct gensec_http_generic_update_state *state = NULL;
93
0
  struct tevent_req *subreq = NULL;
94
95
0
  req = tevent_req_create(mem_ctx, &state,
96
0
        struct gensec_http_generic_update_state);
97
0
  if (req == NULL) {
98
0
    return NULL;
99
0
  }
100
0
  state->gensec = gensec_ctx;
101
102
0
  if (in.length) {
103
0
    int cmp;
104
0
    DATA_BLOB b64b;
105
0
    size_t skip = 0;
106
107
0
    if (in.length < http_generic->prefix.length) {
108
0
      tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
109
0
      return tevent_req_post(req, ev);
110
0
    }
111
112
0
    cmp = strncasecmp((const char *)in.data,
113
0
          (const char *)http_generic->prefix.data,
114
0
          http_generic->prefix.length);
115
0
    if (cmp != 0) {
116
0
      tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
117
0
      return tevent_req_post(req, ev);
118
0
    }
119
120
0
    if (in.length == http_generic->prefix.length) {
121
      /*
122
       * We expect more data, but the
123
       * server just sent the prefix without
124
       * a space prefixing base64 data.
125
       *
126
       * It means the server rejects
127
       * the request with.
128
       */
129
0
      tevent_req_nterror(req, NT_STATUS_LOGON_FAILURE);
130
0
      return tevent_req_post(req, ev);
131
0
    }
132
133
0
    if (in.data[http_generic->prefix.length] != ' ') {
134
0
      tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
135
0
      return tevent_req_post(req, ev);
136
0
    }
137
0
    skip = http_generic->prefix.length + 1;
138
139
0
    b64b = data_blob_const(in.data + skip, in.length - skip);
140
0
    if (b64b.length != 0) {
141
0
      char *b64 = NULL;
142
143
      /*
144
       * ensure it's terminated with \0' before
145
       * passing to base64_decode_data_blob_talloc().
146
       */
147
0
      b64 = talloc_strndup(state, (const char *)b64b.data,
148
0
               b64b.length);
149
0
      if (tevent_req_nomem(b64, req)) {
150
0
        return tevent_req_post(req, ev);
151
0
      }
152
153
0
      state->sub_in = base64_decode_data_blob_talloc(state,
154
0
                       b64);
155
0
      TALLOC_FREE(b64);
156
0
      if (tevent_req_nomem(state->sub_in.data, req)) {
157
0
        return tevent_req_post(req, ev);
158
0
      }
159
0
    }
160
0
  }
161
162
0
  subreq = gensec_update_send(state, ev,
163
0
            http_generic->sub,
164
0
            state->sub_in);
165
0
  if (tevent_req_nomem(subreq, req)) {
166
0
    return tevent_req_post(req, ev);
167
0
  }
168
0
  tevent_req_set_callback(subreq, gensec_http_generic_update_done, req);
169
170
0
  return req;
171
0
}
172
173
static void gensec_http_generic_update_done(struct tevent_req *subreq)
174
0
{
175
0
  struct tevent_req *req =
176
0
    tevent_req_callback_data(subreq,
177
0
    struct tevent_req);
178
0
  struct gensec_http_generic_update_state *state =
179
0
    tevent_req_data(req,
180
0
    struct gensec_http_generic_update_state);
181
0
  struct gensec_http_generic_state *http_generic =
182
0
    talloc_get_type_abort(state->gensec->private_data,
183
0
    struct gensec_http_generic_state);
184
0
  NTSTATUS status;
185
0
  DATA_BLOB sub_out = data_blob_null;
186
0
  char *b64 = NULL;
187
0
  char *str = NULL;
188
0
  int prefix_length;
189
190
0
  status = gensec_update_recv(subreq, state, &sub_out);
191
0
  TALLOC_FREE(subreq);
192
0
  state->status = status;
193
0
  if (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
194
0
    status = NT_STATUS_OK;
195
0
  }
196
0
  if (tevent_req_nterror(req, status)) {
197
0
    return;
198
0
  }
199
200
0
  if (sub_out.length == 0) {
201
0
    tevent_req_done(req);
202
0
    return;
203
0
  }
204
205
0
  b64 = base64_encode_data_blob(state, sub_out);
206
0
  data_blob_free(&sub_out);
207
0
  if (tevent_req_nomem(b64, req)) {
208
0
    return;
209
0
  }
210
211
0
  prefix_length = http_generic->prefix.length;
212
0
  str = talloc_asprintf(state, "%*.*s %s", prefix_length, prefix_length,
213
0
            (const char *)http_generic->prefix.data, b64);
214
0
  TALLOC_FREE(b64);
215
0
  if (tevent_req_nomem(str, req)) {
216
0
    return;
217
0
  }
218
219
0
  state->out = data_blob_string_const(str);
220
0
  tevent_req_done(req);
221
0
}
222
223
static NTSTATUS gensec_http_generic_update_recv(struct tevent_req *req,
224
               TALLOC_CTX *out_mem_ctx,
225
               DATA_BLOB *out)
226
0
{
227
0
  struct gensec_http_generic_update_state *state =
228
0
    tevent_req_data(req,
229
0
    struct gensec_http_generic_update_state);
230
0
  NTSTATUS status;
231
232
0
  *out = data_blob_null;
233
234
0
  if (tevent_req_is_nterror(req, &status)) {
235
0
    tevent_req_received(req);
236
0
    return status;
237
0
  }
238
239
0
  *out = state->out;
240
0
  talloc_steal(out_mem_ctx, state->out.data);
241
0
  status = state->status;
242
0
  tevent_req_received(req);
243
0
  return status;
244
0
}
245
246
static const struct gensec_security_ops gensec_http_ntlm_security_ops = {
247
  .name           = "http_ntlm",
248
  .auth_type      = 0,
249
  .client_start   = gensec_http_ntlm_client_start,
250
  .update_send    = gensec_http_generic_update_send,
251
  .update_recv    = gensec_http_generic_update_recv,
252
  .enabled        = true,
253
  .priority       = GENSEC_EXTERNAL,
254
};
255
256
static const struct gensec_security_ops gensec_http_negotiate_security_ops = {
257
  .name           = "http_negotiate",
258
  .auth_type      = 0,
259
  .client_start   = gensec_http_negotiate_client_start,
260
  .update_send    = gensec_http_generic_update_send,
261
  .update_recv    = gensec_http_generic_update_recv,
262
  .enabled        = true,
263
  .priority       = GENSEC_EXTERNAL,
264
  .glue           = true,
265
};
266
267
_PUBLIC_ NTSTATUS gensec_http_generic_init(TALLOC_CTX *ctx)
268
0
{
269
0
  NTSTATUS status;
270
271
0
  status = gensec_register(ctx, &gensec_http_ntlm_security_ops);
272
0
  if (!NT_STATUS_IS_OK(status)) {
273
0
    DEBUG(0, ("Failed to register '%s' gensec backend!\n",
274
0
        gensec_http_ntlm_security_ops.name));
275
0
    return status;
276
0
  }
277
278
0
  status = gensec_register(ctx, &gensec_http_negotiate_security_ops);
279
0
  if (!NT_STATUS_IS_OK(status)) {
280
0
    DEBUG(0, ("Failed to register '%s' gensec backend!\n",
281
0
        gensec_http_negotiate_security_ops.name));
282
0
    return status;
283
0
  }
284
285
0
  return status;
286
0
}