Coverage Report

Created: 2024-06-18 06:03

/src/gss-ntlmssp/src/gss_auth.c
Line
Count
Source (jump to first uncovered line)
1
/* Copyright (C) 2014 GSS-NTLMSSP contributors, see COPYING for License */
2
3
#include <errno.h>
4
#include <string.h>
5
#include "gss_ntlmssp.h"
6
7
8
uint32_t gssntlm_cli_auth(uint32_t *minor_status,
9
                          struct gssntlm_ctx *ctx,
10
                          struct gssntlm_cred *cred,
11
                          struct ntlm_buffer *target_info,
12
                          uint32_t in_flags,
13
                          gss_channel_bindings_t input_chan_bindings)
14
0
{
15
0
    struct ntlm_buffer nt_chal_resp = { 0 };
16
0
    struct ntlm_buffer lm_chal_resp = { 0 };
17
0
    struct ntlm_buffer client_target_info = { 0 };
18
0
    struct ntlm_key key_exchange_key = { .length = 16 };
19
0
    struct ntlm_key encrypted_random_session_key = { .length = 16 };
20
0
    struct ntlm_buffer enc_sess_key = { 0 };
21
0
    struct ntlm_buffer auth_mic = { NULL, 16 };
22
0
    uint8_t micbuf[16];
23
0
    struct ntlm_buffer mic = { micbuf, 16 };
24
0
    bool add_mic = false;
25
0
    bool key_exch;
26
0
    uint32_t retmaj;
27
0
    uint32_t retmin;
28
29
0
    switch (cred->type) {
30
31
0
    case GSSNTLM_CRED_USER:
32
33
0
        if (ctx->gss_flags & GSS_C_ANON_FLAG) {
34
            /* Anonymous auth, empty responses */
35
0
            memset(&nt_chal_resp, 0, sizeof(nt_chal_resp));
36
0
            lm_chal_resp.data = malloc(1);
37
0
            if (!lm_chal_resp.data) {
38
0
                set_GSSERR(ENOMEM);
39
0
                goto done;
40
0
            }
41
0
            lm_chal_resp.data[0] = 0;
42
0
            lm_chal_resp.length = 1;
43
44
0
        } else if (gssntlm_sec_v2_ok(ctx)) {
45
46
            /* ### NTLMv2 ### */
47
0
            uint8_t client_chal[8];
48
0
            struct ntlm_buffer cli_chal = { client_chal, 8 };
49
0
            struct ntlm_key ntlmv2_key = { .length = 16 };
50
0
            struct ntlm_buffer nt_proof = { 0 };
51
0
            struct ntlm_buffer cb = { 0 };
52
0
            uint64_t srv_time = 0;
53
54
0
            if (target_info->length == 0 &&
55
0
                input_chan_bindings != GSS_C_NO_CHANNEL_BINDINGS) {
56
0
                set_GSSERRS(ERR_NOBINDINGS, GSS_S_BAD_BINDINGS);
57
0
                goto done;
58
0
            }
59
60
0
            if (target_info->length > 0) {
61
0
                bool *add_mic_ptr = NULL;
62
0
                bool protect;
63
64
0
                if (input_chan_bindings != GSS_C_NO_CHANNEL_BINDINGS) {
65
0
                    if (input_chan_bindings->initiator_addrtype != 0 ||
66
0
                        input_chan_bindings->initiator_address.length != 0 ||
67
0
                        input_chan_bindings->acceptor_addrtype != 0 ||
68
0
                        input_chan_bindings->acceptor_address.length != 0 ||
69
0
                        input_chan_bindings->application_data.length == 0) {
70
0
                        set_GSSERRS(ERR_BADARG, GSS_S_BAD_BINDINGS);
71
0
                        goto done;
72
0
                    }
73
0
                    cb.length = input_chan_bindings->application_data.length;
74
0
                    cb.data = input_chan_bindings->application_data.value;
75
0
                }
76
77
0
                protect = in_flags & (NTLMSSP_NEGOTIATE_SIGN
78
0
                                      | NTLMSSP_NEGOTIATE_SEAL);
79
0
                if (protect) {
80
0
                    if (ctx->int_flags & NTLMSSP_CTX_FLAG_SPNEGO_CAN_MIC) {
81
0
                        add_mic_ptr = &add_mic;
82
0
                    }
83
0
                }
84
85
0
                retmin = ntlm_process_target_info(
86
0
                                            ctx->ntlm, protect, target_info,
87
0
                                            ctx->target_name.data.server.spn,
88
0
                                            &cb, &client_target_info,
89
0
                                            &srv_time, add_mic_ptr);
90
0
                if (retmin) {
91
0
                    set_GSSERR(retmin);
92
0
                    goto done;
93
0
                }
94
95
0
                if (srv_time != 0) {
96
0
                    long int tdiff;
97
0
                    tdiff = ntlm_timestamp_now() - srv_time;
98
0
                    if ((tdiff / 10000000) > MAX_CHALRESP_LIFETIME) {
99
0
                        set_GSSERRS(ERR_TIMESKEW, GSS_S_CONTEXT_EXPIRED);
100
0
                        goto done;
101
0
                    }
102
0
                }
103
0
            }
104
105
            /* Random client challenge */
106
0
            retmin = RAND_BUFFER(&cli_chal);
107
0
            if (retmin) {
108
0
                set_GSSERR(retmin);
109
0
                goto done;
110
0
            }
111
112
            /* NTLMv2 Key */
113
0
            retmin = NTOWFv2(ctx->ntlm, &cred->cred.user.nt_hash,
114
0
                             cred->cred.user.user.data.user.name,
115
0
                             cred->cred.user.user.data.user.domain,
116
0
                             &ntlmv2_key);
117
0
            if (retmin) {
118
0
                set_GSSERR(retmin);
119
0
                goto done;
120
0
            }
121
122
            /* NTLMv2 Response */
123
0
            retmin = ntlmv2_compute_nt_response(&ntlmv2_key,
124
0
                                                ctx->server_chal, client_chal,
125
0
                                                srv_time, &client_target_info,
126
0
                                                &nt_chal_resp);
127
0
            if (retmin) {
128
0
                set_GSSERR(retmin);
129
0
                goto done;
130
0
            }
131
132
0
            if (target_info->length == 0) {
133
                /* LMv2 Response
134
                 * (only sent if challenge response has no target_info) */
135
0
                retmin = ntlmv2_compute_lm_response(&ntlmv2_key,
136
0
                                                    ctx->server_chal,
137
0
                                                    client_chal,
138
0
                                                    &lm_chal_resp);
139
0
                if (retmin) {
140
0
                    set_GSSERR(retmin);
141
0
                    goto done;
142
0
                }
143
0
            }
144
145
            /* The NT proof is the first 16 bytes */
146
0
            nt_proof.data = nt_chal_resp.data;
147
0
            nt_proof.length = 16;
148
149
            /* The Session Base Key */
150
            /* In NTLMv2 the Key Exchange Key is the Session Base Key */
151
0
            retmin = ntlmv2_session_base_key(&ntlmv2_key, &nt_proof,
152
0
                                             &key_exchange_key);
153
0
            if (retmin) {
154
0
                set_GSSERR(retmin);
155
0
                goto done;
156
0
            }
157
0
        } else {
158
            /* ### NTLMv1 ### */
159
0
            uint8_t client_chal[8];
160
0
            struct ntlm_buffer cli_chal = { client_chal, 8 };
161
0
            struct ntlm_key session_base_key = { .length = 16 };
162
0
            bool NoLMResponseNTLMv1 = !gssntlm_sec_lm_ok(ctx);
163
0
            bool ext_sec;
164
165
0
            nt_chal_resp.length = 24;
166
0
            nt_chal_resp.data = calloc(1, nt_chal_resp.length);
167
0
            lm_chal_resp.length = 24;
168
0
            lm_chal_resp.data = calloc(1, lm_chal_resp.length);
169
0
            if (!nt_chal_resp.data || !lm_chal_resp.data) {
170
0
                set_GSSERR(ENOMEM);
171
0
                goto done;
172
0
            }
173
174
            /* Random client challenge */
175
0
            retmin = RAND_BUFFER(&cli_chal);
176
0
            if (retmin) {
177
0
                set_GSSERR(retmin);
178
0
                goto done;
179
0
            }
180
181
0
            ext_sec = (in_flags & NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY);
182
183
0
            retmin = ntlm_compute_nt_response(&cred->cred.user.nt_hash,
184
0
                                              ext_sec, ctx->server_chal,
185
0
                                              client_chal, &nt_chal_resp);
186
0
            if (retmin) {
187
0
                set_GSSERR(retmin);
188
0
                goto done;
189
0
            }
190
191
0
            if (!ext_sec && NoLMResponseNTLMv1) {
192
0
                memcpy(lm_chal_resp.data, nt_chal_resp.data, 24);
193
0
            } else {
194
0
                retmin = ntlm_compute_lm_response(&cred->cred.user.lm_hash,
195
0
                                                  ext_sec, ctx->server_chal,
196
0
                                                  client_chal, &lm_chal_resp);
197
0
                if (retmin) {
198
0
                    set_GSSERR(retmin);
199
0
                    goto done;
200
0
                }
201
0
            }
202
203
0
            retmin = ntlm_session_base_key(&cred->cred.user.nt_hash,
204
0
                                           &session_base_key);
205
0
            if (retmin) {
206
0
                set_GSSERR(retmin);
207
0
                goto done;
208
0
            }
209
210
0
            retmin = KXKEY(ctx->ntlm, ext_sec,
211
0
                           (in_flags & NTLMSSP_NEGOTIATE_LM_KEY),
212
0
                           (in_flags & NTLMSSP_REQUEST_NON_NT_SESSION_KEY),
213
0
                           ctx->server_chal, &cred->cred.user.lm_hash,
214
0
                           &session_base_key, &lm_chal_resp,
215
0
                           &key_exchange_key);
216
0
            if (retmin) {
217
0
                set_GSSERR(retmin);
218
0
                goto done;
219
0
            }
220
0
        }
221
222
0
        key_exch = (in_flags & NTLMSSP_NEGOTIATE_KEY_EXCH);
223
224
0
        retmin = ntlm_exported_session_key(&key_exchange_key, key_exch,
225
0
                                           &ctx->exported_session_key);
226
0
        if (retmin) {
227
0
            set_GSSERR(retmin);
228
0
            goto done;
229
0
        }
230
231
0
        if (key_exch) {
232
0
            retmin = ntlm_encrypted_session_key(&key_exchange_key,
233
0
                                                &ctx->exported_session_key,
234
0
                                                &encrypted_random_session_key);
235
0
            if (retmin) {
236
0
                set_GSSERR(retmin);
237
0
                goto done;
238
0
            }
239
0
        }
240
241
        /* in_flags all verified, assign as current flags */
242
0
        ctx->neg_flags |= in_flags;
243
244
0
        enc_sess_key.data = encrypted_random_session_key.data;
245
0
        enc_sess_key.length = encrypted_random_session_key.length;
246
247
0
        retmin = ntlm_encode_auth_msg(ctx->ntlm, ctx->neg_flags,
248
0
                                      &lm_chal_resp,  &nt_chal_resp,
249
0
                                      cred->cred.user.user.data.user.domain,
250
0
                                      cred->cred.user.user.data.user.name,
251
0
                                      ctx->workstation, &enc_sess_key,
252
0
                                      add_mic ? &auth_mic : NULL,
253
0
                                      &ctx->auth_msg);
254
0
        if (retmin) {
255
0
            set_GSSERR(retmin);
256
0
            goto done;
257
0
        }
258
259
        /* Now we need to calculate the MIC, because the MIC is part of the
260
         * message it protects, ntlm_encode_auth_msg() always add a zeroeth
261
         * buffer, however it returns in data_mic the pointer to the actual
262
         * area in the auth_msg that points at the mic, so we can backfill */
263
0
        if (add_mic) {
264
0
            retmin = ntlm_mic(&ctx->exported_session_key, &ctx->nego_msg,
265
0
                              &ctx->chal_msg, &ctx->auth_msg, &mic);
266
0
            if (retmin) {
267
0
                set_GSSERR(retmin);
268
0
                goto done;
269
0
            }
270
            /* now that we have the mic, copy it into the auth message */
271
0
            memcpy(auth_mic.data, mic.data, 16);
272
273
            /* Make sure SPNEGO gets to know it has to add mechlistMIC too */
274
0
            ctx->int_flags |= NTLMSSP_CTX_FLAG_AUTH_WITH_MIC;
275
0
        }
276
277
0
        set_GSSERRS(0, GSS_S_COMPLETE);
278
0
        break;
279
280
0
    case GSSNTLM_CRED_EXTERNAL:
281
0
        retmin = external_cli_auth(ctx, cred, in_flags, input_chan_bindings);
282
0
        if (retmin) {
283
0
            set_GSSERR(retmin);
284
0
            goto done;
285
0
        }
286
0
        set_GSSERRS(0, GSS_S_COMPLETE);
287
0
        break;
288
289
0
    default:
290
0
        set_GSSERR(ERR_NOUSRCRED);
291
0
    }
292
293
0
done:
294
0
    ntlm_free_buffer_data(&client_target_info);
295
0
    ntlm_free_buffer_data(&nt_chal_resp);
296
0
    ntlm_free_buffer_data(&lm_chal_resp);
297
298
0
    return GSSERR();
299
0
}
300
301
302
bool is_ntlm_v1(struct ntlm_buffer *nt_chal_resp)
303
0
{
304
0
    return (nt_chal_resp->length == 24);
305
0
}
306
307
308
uint32_t gssntlm_srv_auth(uint32_t *minor_status,
309
                          struct gssntlm_ctx *ctx,
310
                          struct gssntlm_cred *cred,
311
                          struct ntlm_buffer *nt_chal_resp,
312
                          struct ntlm_buffer *lm_chal_resp,
313
                          struct ntlm_key *key_exchange_key)
314
0
{
315
0
    struct ntlm_key session_base_key = { .length = 16 };
316
0
    struct ntlm_key ntlmv2_key = { .length = 16 };
317
0
    struct ntlm_buffer nt_proof = { 0 };
318
0
    uint32_t retmaj, retmin;
319
0
    const char *domstr;
320
0
    bool ntlm_v1;
321
0
    bool ext_sec;
322
0
    int retries;
323
324
0
    if (key_exchange_key->length != 16) {
325
0
        return GSSERRS(ERR_KEYLEN, GSS_S_FAILURE);
326
0
    }
327
328
0
    ntlm_v1 = is_ntlm_v1(nt_chal_resp);
329
330
0
    if (ntlm_v1 && !gssntlm_sec_lm_ok(ctx) && !gssntlm_sec_ntlm_ok(ctx)) {
331
0
        return GSSERRS(ERR_NONTLMV1, GSS_S_FAILURE);
332
0
    }
333
334
0
    ext_sec = (ctx->neg_flags & NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY);
335
336
0
    switch (cred->type) {
337
338
0
    case GSSNTLM_CRED_USER:
339
0
        if (ntlm_v1) {
340
0
            uint8_t client_chal[8] = { 0 };
341
342
0
            if (ext_sec) {
343
0
                memcpy(client_chal, lm_chal_resp->data, 8);
344
0
            }
345
346
0
            retmin = ntlm_verify_nt_response(nt_chal_resp,
347
0
                                             &cred->cred.user.nt_hash,
348
0
                                             ext_sec, ctx->server_chal,
349
0
                                             client_chal);
350
0
            if (retmin && gssntlm_sec_lm_ok(ctx)) {
351
0
                retmin = ntlm_verify_lm_response(lm_chal_resp,
352
0
                                                 &cred->cred.user.lm_hash,
353
0
                                                 ext_sec, ctx->server_chal,
354
0
                                                 client_chal);
355
0
            }
356
357
0
        } else for (retries = 2; retries > 0; retries--) {
358
359
0
            if (retries == 2) {
360
0
                domstr = cred->cred.user.user.data.user.domain;
361
0
            } else {
362
0
                domstr = NULL;
363
0
            }
364
365
            /* NTLMv2 Key */
366
0
            retmin = NTOWFv2(ctx->ntlm, &cred->cred.user.nt_hash,
367
0
                             cred->cred.user.user.data.user.name,
368
0
                             domstr, &ntlmv2_key);
369
0
            if (retmin) {
370
0
                set_GSSERR(retmin);
371
0
                goto done;
372
0
            }
373
374
            /* NTLMv2 Response */
375
0
            retmin = ntlmv2_verify_nt_response(nt_chal_resp,
376
0
                                               &ntlmv2_key,
377
0
                                               ctx->server_chal);
378
0
            if (retmin && gssntlm_sec_lm_ok(ctx)) {
379
                /* LMv2 Response */
380
0
                retmin = ntlmv2_verify_lm_response(lm_chal_resp,
381
0
                                                   &ntlmv2_key,
382
0
                                                   ctx->server_chal);
383
0
            }
384
0
            if (retmin == 0) break;
385
0
        }
386
387
0
        if (retmin) {
388
0
            set_GSSERR(retmin);
389
0
            goto done;
390
0
        }
391
392
0
        if (ntlm_v1) {
393
0
            retmin = ntlm_session_base_key(&cred->cred.user.nt_hash,
394
0
                                           &session_base_key);
395
0
            if (retmin) {
396
0
                set_GSSERR(retmin);
397
0
                goto done;
398
0
            }
399
0
            break;
400
0
        }
401
402
        /* The NT proof is the first 16 bytes */
403
0
        nt_proof.data = nt_chal_resp->data;
404
0
        nt_proof.length = 16;
405
406
        /* The Session Base Key */
407
        /* In NTLMv2 the Key Exchange Key is the Session Base Key */
408
0
        retmin = ntlmv2_session_base_key(&ntlmv2_key, &nt_proof,
409
0
                                         &session_base_key);
410
0
        if (retmin) {
411
0
            set_GSSERR(retmin);
412
0
            goto done;
413
0
        }
414
0
        break;
415
416
0
    case GSSNTLM_CRED_EXTERNAL:
417
0
        retmin = external_srv_auth(ctx, cred, nt_chal_resp, lm_chal_resp,
418
0
                                   &session_base_key);
419
0
        if (retmin) {
420
0
            set_GSSERR(retmin);
421
0
            goto done;
422
0
        }
423
0
        break;
424
425
0
    default:
426
0
        set_GSSERR(ERR_NOUSRCRED);
427
0
        goto done;
428
0
    }
429
430
0
    if (ntlm_v1) {
431
0
        retmin = KXKEY(ctx->ntlm, ext_sec,
432
0
                       (ctx->neg_flags & NTLMSSP_NEGOTIATE_LM_KEY),
433
0
                       (ctx->neg_flags & NTLMSSP_REQUEST_NON_NT_SESSION_KEY),
434
0
                       ctx->server_chal, &cred->cred.user.lm_hash,
435
0
                       &session_base_key, lm_chal_resp,
436
0
                       key_exchange_key);
437
0
        if (retmin) {
438
0
            set_GSSERR(retmin);
439
0
            goto done;
440
0
        }
441
0
    } else {
442
0
        memcpy(key_exchange_key->data,
443
0
               session_base_key.data, session_base_key.length);
444
0
    }
445
446
0
    set_GSSERRS(0, GSS_S_COMPLETE);
447
448
0
done:
449
0
    return GSSERR();
450
0
}