Coverage Report

Created: 2026-05-24 06:47

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/samba/auth/kerberos/gssapi_helper.c
Line
Count
Source
1
/*
2
   Unix SMB/CIFS implementation.
3
   GSSAPI helper functions
4
5
   Copyright (C) Stefan Metzmacher 2008,2015
6
7
   This program is free software; you can redistribute it and/or modify
8
   it under the terms of the GNU General Public License as published by
9
   the Free Software Foundation; either version 3 of the License, or
10
   (at your option) any later version.
11
12
   This program is distributed in the hope that it will be useful,
13
   but WITHOUT ANY WARRANTY; without even the implied warranty of
14
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
   GNU General Public License for more details.
16
17
   You should have received a copy of the GNU General Public License
18
   along with this program.  If not, see <http://www.gnu.org/licenses/>.
19
*/
20
21
#include "includes.h"
22
#include "system/gssapi.h"
23
#include "auth/kerberos/pac_utils.h"
24
#include "auth/kerberos/gssapi_helper.h"
25
26
#undef DBGC_CLASS
27
0
#define DBGC_CLASS DBGC_AUTH
28
29
size_t gssapi_get_sig_size(gss_ctx_id_t gssapi_context,
30
         const gss_OID mech,
31
         uint32_t gss_want_flags,
32
         size_t data_size)
33
0
{
34
0
  TALLOC_CTX *frame = talloc_stackframe();
35
0
  size_t sig_size = 0;
36
37
0
  if (gss_want_flags & GSS_C_CONF_FLAG) {
38
0
    OM_uint32 min_stat, maj_stat;
39
0
    bool want_sealing = true;
40
0
    int sealed = 0;
41
0
    gss_iov_buffer_desc iov[2];
42
43
0
    if (!(gss_want_flags & GSS_C_DCE_STYLE)) {
44
0
      TALLOC_FREE(frame);
45
0
      return 0;
46
0
    }
47
48
    /*
49
     * gss_wrap_iov_length() only needs the type and length
50
     */
51
0
    iov[0].type = GSS_IOV_BUFFER_TYPE_HEADER;
52
0
    iov[0].buffer.value = NULL;
53
0
    iov[0].buffer.length = 0;
54
0
    iov[1].type = GSS_IOV_BUFFER_TYPE_DATA;
55
0
    iov[1].buffer.value = NULL;
56
0
    iov[1].buffer.length = data_size;
57
58
0
    maj_stat = gss_wrap_iov_length(&min_stat,
59
0
                 gssapi_context,
60
0
                 want_sealing,
61
0
                 GSS_C_QOP_DEFAULT,
62
0
                 &sealed,
63
0
                 iov, ARRAY_SIZE(iov));
64
0
    if (maj_stat) {
65
0
      DEBUG(0, ("gss_wrap_iov_length failed with [%s]\n",
66
0
          gssapi_error_string(frame,
67
0
                  maj_stat,
68
0
                  min_stat,
69
0
                  mech)));
70
0
      TALLOC_FREE(frame);
71
0
      return 0;
72
0
    }
73
74
0
    sig_size = iov[0].buffer.length;
75
0
  } else if (gss_want_flags & GSS_C_INTEG_FLAG) {
76
0
    NTSTATUS status;
77
0
    uint32_t keytype;
78
79
0
    status = gssapi_get_session_key(frame,
80
0
            gssapi_context,
81
0
            NULL, &keytype);
82
0
    if (!NT_STATUS_IS_OK(status)) {
83
0
      TALLOC_FREE(frame);
84
0
      return 0;
85
0
    }
86
87
0
    switch (keytype) {
88
0
    case ENCTYPE_DES_CBC_MD5:
89
0
    case ENCTYPE_DES_CBC_CRC:
90
0
    case ENCTYPE_ARCFOUR_HMAC:
91
0
    case ENCTYPE_ARCFOUR_HMAC_EXP:
92
0
      sig_size = 37;
93
0
      break;
94
0
    default:
95
0
      sig_size = 28;
96
0
      break;
97
0
    }
98
0
  }
99
100
0
  TALLOC_FREE(frame);
101
0
  return sig_size;
102
0
}
103
104
NTSTATUS gssapi_seal_packet(gss_ctx_id_t gssapi_context,
105
          const gss_OID mech,
106
          bool hdr_signing, size_t sig_size,
107
          uint8_t *data, size_t length,
108
          const uint8_t *whole_pdu, size_t pdu_length,
109
          TALLOC_CTX *mem_ctx,
110
          DATA_BLOB *sig)
111
0
{
112
0
  OM_uint32 maj_stat, min_stat;
113
0
  gss_iov_buffer_desc iov[4];
114
0
  int req_seal = 1;
115
0
  int sealed = 0;
116
0
  const uint8_t *pre_sign_ptr = NULL;
117
0
  size_t pre_sign_len = 0;
118
0
  const uint8_t *post_sign_ptr = NULL;
119
0
  size_t post_sign_len = 0;
120
121
0
  if (hdr_signing) {
122
0
    const uint8_t *de = data + length;
123
0
    const uint8_t *we = whole_pdu + pdu_length;
124
125
0
    if (data < whole_pdu) {
126
0
      return NT_STATUS_INVALID_PARAMETER;
127
0
    }
128
129
0
    if (de > we) {
130
0
      return NT_STATUS_INVALID_PARAMETER;
131
0
    }
132
133
0
    pre_sign_len = data - whole_pdu;
134
0
    if (pre_sign_len > 0) {
135
0
      pre_sign_ptr = whole_pdu;
136
0
    }
137
0
    post_sign_len = we - de;
138
0
    if (post_sign_len > 0) {
139
0
      post_sign_ptr = de;
140
0
    }
141
0
  }
142
143
0
  sig->length = sig_size;
144
0
  if (sig->length == 0) {
145
0
    return NT_STATUS_ACCESS_DENIED;
146
0
  }
147
148
0
  sig->data = talloc_zero_array(mem_ctx, uint8_t, sig->length);
149
0
  if (sig->data == NULL) {
150
0
    return NT_STATUS_NO_MEMORY;
151
0
  }
152
153
0
  iov[0].type          = GSS_IOV_BUFFER_TYPE_HEADER;
154
0
  iov[0].buffer.length = sig->length;
155
0
  iov[0].buffer.value  = sig->data;
156
157
0
  if (pre_sign_ptr != NULL) {
158
0
    iov[1].type = GSS_IOV_BUFFER_TYPE_SIGN_ONLY;
159
0
    iov[1].buffer.length = pre_sign_len;
160
0
    iov[1].buffer.value = discard_const(pre_sign_ptr);
161
0
  } else {
162
0
    iov[1].type = GSS_IOV_BUFFER_TYPE_EMPTY;
163
0
    iov[1].buffer.length = 0;
164
0
    iov[1].buffer.value = NULL;
165
0
  }
166
167
  /* data is encrypted in place, which is ok */
168
0
  iov[2].type          = GSS_IOV_BUFFER_TYPE_DATA;
169
0
  iov[2].buffer.length = length;
170
0
  iov[2].buffer.value  = data;
171
172
0
  if (post_sign_ptr != NULL) {
173
0
    iov[3].type = GSS_IOV_BUFFER_TYPE_SIGN_ONLY;
174
0
    iov[3].buffer.length = post_sign_len;
175
0
    iov[3].buffer.value = discard_const(post_sign_ptr);
176
0
  } else {
177
0
    iov[3].type = GSS_IOV_BUFFER_TYPE_EMPTY;
178
0
    iov[3].buffer.length = 0;
179
0
    iov[3].buffer.value = NULL;
180
0
  }
181
182
0
  maj_stat = gss_wrap_iov(&min_stat,
183
0
        gssapi_context,
184
0
        req_seal,
185
0
        GSS_C_QOP_DEFAULT,
186
0
        &sealed,
187
0
        iov, ARRAY_SIZE(iov));
188
0
  if (GSS_ERROR(maj_stat)) {
189
0
    char *error_string = gssapi_error_string(mem_ctx,
190
0
               maj_stat,
191
0
               min_stat,
192
0
               mech);
193
0
    DEBUG(1, ("gss_wrap_iov failed: %s\n", error_string));
194
0
    talloc_free(error_string);
195
0
    data_blob_free(sig);
196
0
    return NT_STATUS_ACCESS_DENIED;
197
0
  }
198
199
0
  if (req_seal == 1 && sealed == 0) {
200
0
    DEBUG(0, ("gss_wrap_iov says data was not sealed!\n"));
201
0
    data_blob_free(sig);
202
0
    return NT_STATUS_ACCESS_DENIED;
203
0
  }
204
205
0
  dump_data_pw("gssapi_seal_packet: sig\n", sig->data, sig->length);
206
0
  dump_data_pw("gssapi_seal_packet: sealed\n", data, length);
207
208
0
  DEBUG(10, ("Sealed %d bytes, and got %d bytes header/signature.\n",
209
0
       (int)iov[2].buffer.length, (int)iov[0].buffer.length));
210
211
0
  return NT_STATUS_OK;
212
0
}
213
214
NTSTATUS gssapi_unseal_packet(gss_ctx_id_t gssapi_context,
215
            const gss_OID mech,
216
            bool hdr_signing,
217
            uint8_t *data, size_t length,
218
            const uint8_t *whole_pdu, size_t pdu_length,
219
            const DATA_BLOB *sig)
220
0
{
221
0
  OM_uint32 maj_stat, min_stat;
222
0
  gss_iov_buffer_desc iov[4];
223
0
  gss_qop_t qop_state;
224
0
  int sealed = 0;
225
0
  const uint8_t *pre_sign_ptr = NULL;
226
0
  size_t pre_sign_len = 0;
227
0
  const uint8_t *post_sign_ptr = NULL;
228
0
  size_t post_sign_len = 0;
229
230
0
  if (hdr_signing) {
231
0
    const uint8_t *de = data + length;
232
0
    const uint8_t *we = whole_pdu + pdu_length;
233
234
0
    if (data < whole_pdu) {
235
0
      return NT_STATUS_INVALID_PARAMETER;
236
0
    }
237
238
0
    if (de > we) {
239
0
      return NT_STATUS_INVALID_PARAMETER;
240
0
    }
241
242
0
    pre_sign_len = data - whole_pdu;
243
0
    if (pre_sign_len > 0) {
244
0
      pre_sign_ptr = whole_pdu;
245
0
    }
246
0
    post_sign_len = we - de;
247
0
    if (post_sign_len > 0) {
248
0
      post_sign_ptr = de;
249
0
    }
250
0
  }
251
252
0
  dump_data_pw("gssapi_unseal_packet: sig\n", sig->data, sig->length);
253
0
  dump_data_pw("gssapi_unseal_packet: sealed\n", data, length);
254
255
0
  iov[0].type          = GSS_IOV_BUFFER_TYPE_HEADER;
256
0
  iov[0].buffer.length = sig->length;
257
0
  iov[0].buffer.value  = sig->data;
258
259
0
  if (pre_sign_ptr != NULL) {
260
0
    iov[1].type = GSS_IOV_BUFFER_TYPE_SIGN_ONLY;
261
0
    iov[1].buffer.length = pre_sign_len;
262
0
    iov[1].buffer.value = discard_const(pre_sign_ptr);
263
0
  } else {
264
0
    iov[1].type = GSS_IOV_BUFFER_TYPE_EMPTY;
265
0
    iov[1].buffer.length = 0;
266
0
    iov[1].buffer.value = NULL;
267
0
  }
268
269
  /* data is encrypted in place, which is ok */
270
0
  iov[2].type          = GSS_IOV_BUFFER_TYPE_DATA;
271
0
  iov[2].buffer.length = length;
272
0
  iov[2].buffer.value  = data;
273
274
0
  if (post_sign_ptr != NULL) {
275
0
    iov[3].type = GSS_IOV_BUFFER_TYPE_SIGN_ONLY;
276
0
    iov[3].buffer.length = post_sign_len;
277
0
    iov[3].buffer.value = discard_const(post_sign_ptr);
278
0
  } else {
279
0
    iov[3].type = GSS_IOV_BUFFER_TYPE_EMPTY;
280
0
    iov[3].buffer.length = 0;
281
0
    iov[3].buffer.value = NULL;
282
0
  }
283
284
0
  maj_stat = gss_unwrap_iov(&min_stat,
285
0
          gssapi_context,
286
0
          &sealed,
287
0
          &qop_state,
288
0
          iov, ARRAY_SIZE(iov));
289
0
  if (GSS_ERROR(maj_stat)) {
290
0
    char *error_string = gssapi_error_string(NULL,
291
0
               maj_stat,
292
0
               min_stat,
293
0
               mech);
294
0
    DEBUG(1, ("gss_unwrap_iov failed: %s\n", error_string));
295
0
    talloc_free(error_string);
296
297
0
    return NT_STATUS_ACCESS_DENIED;
298
0
  }
299
300
0
  if (sealed == 0) {
301
0
    DEBUG(0, ("gss_unwrap_iov says data was not sealed!\n"));
302
0
    return NT_STATUS_ACCESS_DENIED;
303
0
  }
304
305
0
  DEBUG(10, ("Unsealed %d bytes, with %d bytes header/signature.\n",
306
0
       (int)iov[2].buffer.length, (int)iov[0].buffer.length));
307
308
0
  return NT_STATUS_OK;
309
0
}
310
311
NTSTATUS gssapi_sign_packet(gss_ctx_id_t gssapi_context,
312
          const gss_OID mech,
313
          bool hdr_signing,
314
          const uint8_t *data, size_t length,
315
          const uint8_t *whole_pdu, size_t pdu_length,
316
          TALLOC_CTX *mem_ctx,
317
          DATA_BLOB *sig)
318
0
{
319
0
  OM_uint32 maj_stat, min_stat;
320
0
  gss_buffer_desc input_token, output_token;
321
322
0
  if (hdr_signing) {
323
0
    input_token.length = pdu_length;
324
0
    input_token.value = discard_const_p(uint8_t *, whole_pdu);
325
0
  } else {
326
0
    input_token.length = length;
327
0
    input_token.value = discard_const_p(uint8_t *, data);
328
0
  }
329
330
0
  maj_stat = gss_get_mic(&min_stat,
331
0
             gssapi_context,
332
0
             GSS_C_QOP_DEFAULT,
333
0
             &input_token,
334
0
             &output_token);
335
0
  if (GSS_ERROR(maj_stat)) {
336
0
    char *error_string = gssapi_error_string(mem_ctx,
337
0
               maj_stat,
338
0
               min_stat,
339
0
               mech);
340
0
    DEBUG(1, ("GSS GetMic failed: %s\n", error_string));
341
0
    talloc_free(error_string);
342
0
    return NT_STATUS_ACCESS_DENIED;
343
0
  }
344
345
0
  *sig = data_blob_talloc(mem_ctx, (uint8_t *)output_token.value, output_token.length);
346
0
  gss_release_buffer(&min_stat, &output_token);
347
0
  if (sig->data == NULL) {
348
0
    return NT_STATUS_NO_MEMORY;
349
0
  }
350
351
0
  dump_data_pw("gssapi_sign_packet: sig\n", sig->data, sig->length);
352
353
0
  return NT_STATUS_OK;
354
0
}
355
356
NTSTATUS gssapi_check_packet(gss_ctx_id_t gssapi_context,
357
           const gss_OID mech,
358
           bool hdr_signing,
359
           const uint8_t *data, size_t length,
360
           const uint8_t *whole_pdu, size_t pdu_length,
361
           const DATA_BLOB *sig)
362
0
{
363
0
  OM_uint32 maj_stat, min_stat;
364
0
  gss_buffer_desc input_token;
365
0
  gss_buffer_desc input_message;
366
0
  gss_qop_t qop_state;
367
368
0
  dump_data_pw("gssapi_check_packet: sig\n", sig->data, sig->length);
369
370
0
  if (hdr_signing) {
371
0
    input_message.length = pdu_length;
372
0
    input_message.value = discard_const(whole_pdu);
373
0
  } else {
374
0
    input_message.length = length;
375
0
    input_message.value = discard_const(data);
376
0
  }
377
378
0
  input_token.length = sig->length;
379
0
  input_token.value = sig->data;
380
381
0
  maj_stat = gss_verify_mic(&min_stat,
382
0
          gssapi_context,
383
0
          &input_message,
384
0
          &input_token,
385
0
          &qop_state);
386
0
  if (GSS_ERROR(maj_stat)) {
387
0
    char *error_string = gssapi_error_string(NULL,
388
0
               maj_stat,
389
0
               min_stat,
390
0
               mech);
391
0
    DEBUG(1, ("GSS VerifyMic failed: %s\n", error_string));
392
0
    talloc_free(error_string);
393
394
0
    return NT_STATUS_ACCESS_DENIED;
395
0
  }
396
397
0
  return NT_STATUS_OK;
398
0
}