Coverage Report

Created: 2025-07-23 07:04

/src/samba/libcli/auth/schannel_state_tdb.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
   Unix SMB/CIFS implementation.
3
4
   module to store/fetch session keys for the schannel server
5
6
   Copyright (C) Andrew Tridgell 2004
7
   Copyright (C) Andrew Bartlett <abartlet@samba.org> 2006-2009
8
   Copyright (C) Guenther Deschner 2009
9
10
   This program is free software; you can redistribute it and/or modify
11
   it under the terms of the GNU General Public License as published by
12
   the Free Software Foundation; either version 3 of the License, or
13
   (at your option) any later version.
14
15
   This program is distributed in the hope that it will be useful,
16
   but WITHOUT ANY WARRANTY; without even the implied warranty of
17
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18
   GNU General Public License for more details.
19
20
   You should have received a copy of the GNU General Public License
21
   along with this program.  If not, see <http://www.gnu.org/licenses/>.
22
*/
23
24
#include "includes.h"
25
#include "system/filesys.h"
26
#include "../lib/tdb/include/tdb.h"
27
#include "../lib/util/util_tdb.h"
28
#include "../lib/param/param.h"
29
#include "../libcli/auth/schannel.h"
30
#include "../librpc/gen_ndr/ndr_schannel.h"
31
#include "lib/dbwrap/dbwrap.h"
32
33
0
#define SECRETS_SCHANNEL_STATE "SECRETS/SCHANNEL"
34
35
/******************************************************************************
36
 Open or create the schannel session store tdb.  Non-static so it can
37
 be called from parent processes to correctly handle TDB_CLEAR_IF_FIRST
38
*******************************************************************************/
39
40
struct db_context *open_schannel_session_store(TALLOC_CTX *mem_ctx,
41
                 struct loadparm_context *lp_ctx)
42
0
{
43
0
  struct db_context *db_sc = NULL;
44
0
  char *fname = lpcfg_private_db_path(mem_ctx, lp_ctx, "schannel_store");
45
0
  int hash_size, tdb_flags;
46
47
0
  if (!fname) {
48
0
    return NULL;
49
0
  }
50
51
0
  hash_size = lpcfg_tdb_hash_size(lp_ctx, fname);
52
0
  tdb_flags = lpcfg_tdb_flags(lp_ctx, TDB_CLEAR_IF_FIRST|TDB_NOSYNC);
53
54
0
  db_sc = dbwrap_local_open(
55
0
    mem_ctx,
56
0
    fname,
57
0
    hash_size,
58
0
    tdb_flags,
59
0
    O_RDWR|O_CREAT,
60
0
    0600,
61
0
    DBWRAP_LOCK_ORDER_NONE,
62
0
    DBWRAP_FLAG_NONE);
63
64
0
  if (!db_sc) {
65
0
    DEBUG(0,("open_schannel_session_store: Failed to open %s - %s\n",
66
0
       fname, strerror(errno)));
67
0
    TALLOC_FREE(fname);
68
0
    return NULL;
69
0
  }
70
71
0
  TALLOC_FREE(fname);
72
73
0
  return db_sc;
74
0
}
75
76
/********************************************************************
77
 ********************************************************************/
78
79
static
80
NTSTATUS schannel_store_session_key_tdb(struct db_context *db_sc,
81
          TALLOC_CTX *mem_ctx,
82
          struct netlogon_creds_CredentialState *creds)
83
0
{
84
0
  enum ndr_err_code ndr_err;
85
0
  DATA_BLOB blob;
86
0
  TDB_DATA value;
87
0
  char *keystr;
88
0
  char *name_upper;
89
0
  NTSTATUS status;
90
91
0
  if (strlen(creds->computer_name) > 15) {
92
    /*
93
     * We may want to check for a completely
94
     * valid netbios name.
95
     */
96
0
    return STATUS_BUFFER_OVERFLOW;
97
0
  }
98
99
0
  name_upper = strupper_talloc(mem_ctx, creds->computer_name);
100
0
  if (!name_upper) {
101
0
    return NT_STATUS_NO_MEMORY;
102
0
  }
103
104
0
  keystr = talloc_asprintf(mem_ctx, "%s/%s",
105
0
         SECRETS_SCHANNEL_STATE, name_upper);
106
0
  TALLOC_FREE(name_upper);
107
0
  if (!keystr) {
108
0
    return NT_STATUS_NO_MEMORY;
109
0
  }
110
111
0
  ndr_err = ndr_push_struct_blob(&blob, mem_ctx, creds,
112
0
      (ndr_push_flags_fn_t)ndr_push_netlogon_creds_CredentialState);
113
0
  if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
114
0
    talloc_free(keystr);
115
0
    return ndr_map_error2ntstatus(ndr_err);
116
0
  }
117
118
0
  value.dptr = blob.data;
119
0
  value.dsize = blob.length;
120
121
0
  status = dbwrap_store_bystring(db_sc, keystr, value, TDB_REPLACE);
122
0
  if (!NT_STATUS_IS_OK(status)) {
123
0
    DEBUG(0,("Unable to add %s to session key db - %s\n",
124
0
       keystr, nt_errstr(status)));
125
0
    talloc_free(keystr);
126
0
    return status;
127
0
  }
128
129
0
  DEBUG(3,("schannel_store_session_key_tdb: stored schannel info with key %s\n",
130
0
    keystr));
131
132
0
  if (DEBUGLEVEL >= 10) {
133
0
    NDR_PRINT_DEBUG(netlogon_creds_CredentialState, creds);
134
0
  }
135
136
0
  talloc_free(keystr);
137
138
0
  return NT_STATUS_OK;
139
0
}
140
141
/********************************************************************
142
 ********************************************************************/
143
144
static
145
NTSTATUS schannel_fetch_session_key_tdb(struct db_context *db_sc,
146
          TALLOC_CTX *mem_ctx,
147
          const char *computer_name,
148
          struct netlogon_creds_CredentialState **pcreds)
149
0
{
150
0
  NTSTATUS status;
151
0
  TDB_DATA value;
152
0
  enum ndr_err_code ndr_err;
153
0
  DATA_BLOB blob;
154
0
  struct netlogon_creds_CredentialState *creds = NULL;
155
0
  char *keystr = NULL;
156
0
  char *name_upper;
157
158
0
  *pcreds = NULL;
159
160
0
  name_upper = strupper_talloc(mem_ctx, computer_name);
161
0
  if (!name_upper) {
162
0
    return NT_STATUS_NO_MEMORY;
163
0
  }
164
165
0
  keystr = talloc_asprintf(mem_ctx, "%s/%s",
166
0
         SECRETS_SCHANNEL_STATE, name_upper);
167
0
  TALLOC_FREE(name_upper);
168
0
  if (!keystr) {
169
0
    return NT_STATUS_NO_MEMORY;
170
0
  }
171
172
0
  status = dbwrap_fetch_bystring(db_sc, keystr, keystr, &value);
173
0
  if (!NT_STATUS_IS_OK(status)) {
174
0
    DEBUG(10,("schannel_fetch_session_key_tdb: Failed to find entry with key %s\n",
175
0
      keystr ));
176
0
    goto done;
177
0
  }
178
179
0
  creds = talloc_zero(mem_ctx, struct netlogon_creds_CredentialState);
180
0
  if (!creds) {
181
0
    status = NT_STATUS_NO_MEMORY;
182
0
    goto done;
183
0
  }
184
185
0
  blob = data_blob_const(value.dptr, value.dsize);
186
187
0
  ndr_err = ndr_pull_struct_blob(&blob, creds, creds,
188
0
      (ndr_pull_flags_fn_t)ndr_pull_netlogon_creds_CredentialState);
189
0
  if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
190
0
    status = ndr_map_error2ntstatus(ndr_err);
191
0
    goto done;
192
0
  }
193
194
0
  if (DEBUGLEVEL >= 10) {
195
0
    NDR_PRINT_DEBUG(netlogon_creds_CredentialState, creds);
196
0
  }
197
198
0
  DEBUG(3,("schannel_fetch_session_key_tdb: restored schannel info key %s\n",
199
0
    keystr));
200
201
0
  status = NT_STATUS_OK;
202
203
0
 done:
204
205
0
  talloc_free(keystr);
206
207
0
  if (!NT_STATUS_IS_OK(status)) {
208
0
    talloc_free(creds);
209
0
    return status;
210
0
  }
211
212
0
  *pcreds = creds;
213
214
0
  return NT_STATUS_OK;
215
0
}
216
217
/******************************************************************************
218
 Wrapper around schannel_fetch_session_key_tdb()
219
 Note we must be root here.
220
*******************************************************************************/
221
222
NTSTATUS schannel_get_creds_state(TALLOC_CTX *mem_ctx,
223
          struct loadparm_context *lp_ctx,
224
          const char *computer_name,
225
          struct netlogon_creds_CredentialState **_creds)
226
0
{
227
0
  TALLOC_CTX *tmpctx;
228
0
  struct db_context *db_sc;
229
0
  struct netlogon_creds_CredentialState *creds;
230
0
  NTSTATUS status;
231
232
0
  tmpctx = talloc_named(mem_ctx, 0, "schannel_get_creds_state");
233
0
  if (!tmpctx) {
234
0
    return NT_STATUS_NO_MEMORY;
235
0
  }
236
237
0
  db_sc = open_schannel_session_store(tmpctx, lp_ctx);
238
0
  if (!db_sc) {
239
0
    TALLOC_FREE(tmpctx);
240
0
    return NT_STATUS_ACCESS_DENIED;
241
0
  }
242
243
0
  status = schannel_fetch_session_key_tdb(db_sc, tmpctx,
244
0
            computer_name, &creds);
245
0
  if (NT_STATUS_IS_OK(status)) {
246
0
    *_creds = talloc_steal(mem_ctx, creds);
247
0
    if (!*_creds) {
248
0
      status = NT_STATUS_NO_MEMORY;
249
0
    }
250
0
  }
251
252
0
  talloc_free(tmpctx);
253
0
  return status;
254
0
}
255
256
/******************************************************************************
257
 Wrapper around schannel_store_session_key_tdb()
258
 Note we must be root here.
259
*******************************************************************************/
260
261
NTSTATUS schannel_save_creds_state(TALLOC_CTX *mem_ctx,
262
           struct loadparm_context *lp_ctx,
263
           struct netlogon_creds_CredentialState *creds)
264
0
{
265
0
  TALLOC_CTX *tmpctx;
266
0
  struct db_context *db_sc;
267
0
  NTSTATUS status;
268
269
0
  tmpctx = talloc_named(mem_ctx, 0, "schannel_save_creds_state");
270
0
  if (!tmpctx) {
271
0
    return NT_STATUS_NO_MEMORY;
272
0
  }
273
274
0
  db_sc = open_schannel_session_store(tmpctx, lp_ctx);
275
0
  if (!db_sc) {
276
0
    status = NT_STATUS_ACCESS_DENIED;
277
0
    goto fail;
278
0
  }
279
280
0
  status = schannel_store_session_key_tdb(db_sc, tmpctx, creds);
281
282
0
fail:
283
0
  talloc_free(tmpctx);
284
0
  return status;
285
0
}
286
287
288
/*
289
 * Create a very lossy hash of the computer name.
290
 *
291
 * The idea here is to compress the computer name into small space so
292
 * that malicious clients cannot fill the database with junk, as only a
293
 * maximum of 16k of entries are possible.
294
 *
295
 * Collisions are certainly possible, and the design behaves in the
296
 * same way as when the hostname is reused, but clients that use the
297
 * same connection do not go via the cache, and the cache only needs
298
 * to function between the ReqChallenge and ServerAuthenticate
299
 * packets.
300
 */
301
static void hash_computer_name(const char *computer_name,
302
             char keystr[16])
303
0
{
304
0
  unsigned int hash;
305
0
  TDB_DATA computer_tdb_data = {
306
0
    .dptr = (uint8_t *)discard_const_p(char, computer_name),
307
0
    .dsize = strlen(computer_name)
308
0
  };
309
0
  hash = tdb_jenkins_hash(&computer_tdb_data);
310
311
  /* we are using 14 bits of the digest to index our connections, so
312
     that we use at most 16,384 buckets.*/
313
0
  snprintf(keystr, 15, "CHALLENGE/%x%x", hash & 0xFF,
314
0
     (hash & 0xFF00 >> 8) & 0x3f);
315
0
  return;
316
0
}
317
318
319
static
320
NTSTATUS schannel_store_challenge_tdb(struct db_context *db_sc,
321
              TALLOC_CTX *mem_ctx,
322
              const struct netr_Credential *client_challenge,
323
              const struct netr_Credential *server_challenge,
324
              const char *computer_name)
325
0
{
326
0
  enum ndr_err_code ndr_err;
327
0
  DATA_BLOB blob;
328
0
  TDB_DATA value;
329
0
  char *name_upper = NULL;
330
0
  NTSTATUS status;
331
0
  char keystr[16] = { 0, };
332
0
  struct netlogon_cache_entry cache_entry;
333
334
0
  if (strlen(computer_name) > 255) {
335
    /*
336
     * We don't make this a limit at 15 chars as Samba has
337
     * a test showing this can be longer :-(
338
     */
339
0
    return STATUS_BUFFER_OVERFLOW;
340
0
  }
341
342
0
  name_upper = strupper_talloc(mem_ctx, computer_name);
343
0
  if (name_upper == NULL) {
344
0
    return NT_STATUS_NO_MEMORY;
345
0
  }
346
347
0
  hash_computer_name(name_upper, keystr);
348
349
0
  cache_entry.computer_name = name_upper;
350
0
  cache_entry.client_challenge = *client_challenge;
351
0
  cache_entry.server_challenge = *server_challenge;
352
353
0
  ndr_err = ndr_push_struct_blob(&blob, mem_ctx, &cache_entry,
354
0
             (ndr_push_flags_fn_t)ndr_push_netlogon_cache_entry);
355
0
  if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
356
0
    return NT_STATUS_UNSUCCESSFUL;
357
0
  }
358
359
0
  value.dptr = blob.data;
360
0
  value.dsize = blob.length;
361
362
0
  status = dbwrap_store_bystring(db_sc, keystr, value, TDB_REPLACE);
363
0
  if (!NT_STATUS_IS_OK(status)) {
364
0
    DEBUG(0,("%s: failed to stored challenge info for '%s' "
365
0
       "with key %s - %s\n",
366
0
       __func__, cache_entry.computer_name, keystr,
367
0
       nt_errstr(status)));
368
0
    return status;
369
0
  }
370
371
0
  DEBUG(3,("%s: stored challenge info for '%s' with key %s\n",
372
0
    __func__, cache_entry.computer_name, keystr));
373
374
0
  if (DEBUGLEVEL >= 10) {
375
0
    NDR_PRINT_DEBUG(netlogon_cache_entry, &cache_entry);
376
0
  }
377
378
0
  return NT_STATUS_OK;
379
0
}
380
381
/********************************************************************
382
 Fetch a single challenge from the TDB.
383
 ********************************************************************/
384
385
static
386
NTSTATUS schannel_fetch_challenge_tdb(struct db_context *db_sc,
387
              TALLOC_CTX *mem_ctx,
388
              struct netr_Credential *client_challenge,
389
              struct netr_Credential *server_challenge,
390
              const char *computer_name)
391
0
{
392
0
  NTSTATUS status;
393
0
  TDB_DATA value;
394
0
  enum ndr_err_code ndr_err;
395
0
  DATA_BLOB blob;
396
0
  char keystr[16] = { 0, };
397
0
  struct netlogon_cache_entry cache_entry;
398
0
  char *name_upper = NULL;
399
400
0
  name_upper = strupper_talloc(mem_ctx, computer_name);
401
0
  if (name_upper == NULL) {
402
0
    return NT_STATUS_NO_MEMORY;
403
0
  }
404
405
0
  hash_computer_name(name_upper, keystr);
406
407
0
  status = dbwrap_fetch_bystring(db_sc, mem_ctx, keystr, &value);
408
0
  if (!NT_STATUS_IS_OK(status)) {
409
0
    DEBUG(3,("%s: Failed to find entry for %s with key %s - %s\n",
410
0
      __func__, name_upper, keystr, nt_errstr(status)));
411
0
    goto done;
412
0
  }
413
414
0
  blob = data_blob_const(value.dptr, value.dsize);
415
416
0
  ndr_err = ndr_pull_struct_blob_all(&blob, mem_ctx, &cache_entry,
417
0
             (ndr_pull_flags_fn_t)ndr_pull_netlogon_cache_entry);
418
0
  if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
419
0
    status = ndr_map_error2ntstatus(ndr_err);
420
0
    DEBUG(3,("%s: Failed to parse entry for %s with key %s - %s\n",
421
0
      __func__, name_upper, keystr, nt_errstr(status)));
422
0
    goto done;
423
0
  }
424
425
0
  if (DEBUGLEVEL >= 10) {
426
0
    NDR_PRINT_DEBUG(netlogon_cache_entry, &cache_entry);
427
0
  }
428
429
0
  if (strcmp(cache_entry.computer_name, name_upper) != 0) {
430
0
    status = NT_STATUS_NOT_FOUND;
431
432
0
    DEBUG(1, ("%s: HASH COLLISION with key %s ! "
433
0
        "Wanted to fetch record for %s but got %s.\n",
434
0
        __func__, keystr, name_upper,
435
0
        cache_entry.computer_name));
436
0
  } else {
437
438
0
    DEBUG(3,("%s: restored key %s for %s\n",
439
0
       __func__, keystr, cache_entry.computer_name));
440
441
0
    *client_challenge = cache_entry.client_challenge;
442
0
    *server_challenge = cache_entry.server_challenge;
443
0
  }
444
0
 done:
445
446
0
  if (!NT_STATUS_IS_OK(status)) {
447
0
    return status;
448
0
  }
449
450
0
  return NT_STATUS_OK;
451
0
}
452
453
/******************************************************************************
454
 Wrapper around schannel_fetch_challenge_tdb()
455
 Note we must be root here.
456
457
*******************************************************************************/
458
459
NTSTATUS schannel_get_challenge(struct loadparm_context *lp_ctx,
460
        struct netr_Credential *client_challenge,
461
        struct netr_Credential *server_challenge,
462
        const char *computer_name)
463
0
{
464
0
  TALLOC_CTX *frame = talloc_stackframe();
465
0
  struct db_context *db_sc;
466
0
  NTSTATUS status;
467
468
0
  db_sc = open_schannel_session_store(frame, lp_ctx);
469
0
  if (!db_sc) {
470
0
    TALLOC_FREE(frame);
471
0
    return NT_STATUS_ACCESS_DENIED;
472
0
  }
473
474
0
  status = schannel_fetch_challenge_tdb(db_sc, frame,
475
0
                client_challenge,
476
0
                server_challenge,
477
0
                computer_name);
478
0
  TALLOC_FREE(frame);
479
0
  return status;
480
0
}
481
482
/******************************************************************************
483
 Wrapper around dbwrap_delete_bystring()
484
 Note we must be root here.
485
486
 This allows the challenge to be removed from the TDB, which should be
487
 as soon as the TDB or in-memory copy it is used, to avoid reuse.
488
*******************************************************************************/
489
490
NTSTATUS schannel_delete_challenge(struct loadparm_context *lp_ctx,
491
           const char *computer_name)
492
0
{
493
0
  TALLOC_CTX *frame = talloc_stackframe();
494
0
  struct db_context *db_sc;
495
0
  char *name_upper;
496
0
  char keystr[16] = { 0, };
497
498
0
  db_sc = open_schannel_session_store(frame, lp_ctx);
499
0
  if (!db_sc) {
500
0
    TALLOC_FREE(frame);
501
0
    return NT_STATUS_ACCESS_DENIED;
502
0
  }
503
504
0
  name_upper = strupper_talloc(frame, computer_name);
505
0
  if (!name_upper) {
506
0
    TALLOC_FREE(frame);
507
0
    return NT_STATUS_NO_MEMORY;
508
0
  }
509
510
0
  hash_computer_name(name_upper, keystr);
511
512
  /* Now delete it, we do not want to permit fetch of this twice */
513
0
  dbwrap_delete_bystring(db_sc, keystr);
514
515
0
  TALLOC_FREE(frame);
516
0
  return NT_STATUS_OK;
517
0
}
518
519
/******************************************************************************
520
 Wrapper around schannel_store_session_key_tdb()
521
 Note we must be root here.
522
*******************************************************************************/
523
524
NTSTATUS schannel_save_challenge(struct loadparm_context *lp_ctx,
525
         const struct netr_Credential *client_challenge,
526
         const struct netr_Credential *server_challenge,
527
         const char *computer_name)
528
0
{
529
0
  TALLOC_CTX *frame = talloc_stackframe();
530
0
  struct db_context *db_sc;
531
0
  NTSTATUS status;
532
533
0
  db_sc = open_schannel_session_store(frame, lp_ctx);
534
0
  if (!db_sc) {
535
0
    TALLOC_FREE(frame);
536
0
    return NT_STATUS_ACCESS_DENIED;
537
0
  }
538
539
0
  status = schannel_store_challenge_tdb(db_sc, frame,
540
0
                client_challenge,
541
0
                server_challenge,
542
0
                computer_name);
543
544
0
  TALLOC_FREE(frame);
545
0
  return status;
546
0
}
547
548
/********************************************************************
549
 Validate an incoming authenticator against the credentials for the
550
 remote machine stored in the schannel database.
551
552
 The credentials are (re)read and from the schannel database, and
553
 written back after the calculations are performed.
554
555
 If the creds_out parameter is not NULL returns the credentials.
556
 ********************************************************************/
557
558
NTSTATUS schannel_check_creds_state(TALLOC_CTX *mem_ctx,
559
            struct loadparm_context *lp_ctx,
560
            const char *computer_name,
561
            struct netr_Authenticator *received_authenticator,
562
            struct netr_Authenticator *return_authenticator,
563
            enum dcerpc_AuthType auth_type,
564
            enum dcerpc_AuthLevel auth_level,
565
            NTSTATUS (*access_check_cb)(struct netlogon_creds_CredentialState *creds,
566
                NTSTATUS step_status,
567
                bool *store,
568
                void *access_check_private),
569
            void *access_check_private,
570
            struct netlogon_creds_CredentialState **creds_out)
571
0
{
572
0
  TALLOC_CTX *tmpctx;
573
0
  struct db_context *db_sc;
574
0
  struct netlogon_creds_CredentialState *creds;
575
0
  NTSTATUS status;
576
0
  char *name_upper = NULL;
577
0
  char *keystr = NULL;
578
0
  struct db_record *record;
579
0
  TDB_DATA key;
580
0
  bool store = true;
581
582
0
  if (creds_out != NULL) {
583
0
    *creds_out = NULL;
584
0
  }
585
586
0
  tmpctx = talloc_named(mem_ctx, 0, "schannel_check_creds_state");
587
0
  if (!tmpctx) {
588
0
    return NT_STATUS_NO_MEMORY;
589
0
  }
590
591
0
  name_upper = strupper_talloc(tmpctx, computer_name);
592
0
  if (!name_upper) {
593
0
    status = NT_STATUS_NO_MEMORY;
594
0
    goto done;
595
0
  }
596
597
0
  keystr = talloc_asprintf(tmpctx, "%s/%s",
598
0
         SECRETS_SCHANNEL_STATE, name_upper);
599
0
  if (!keystr) {
600
0
    status = NT_STATUS_NO_MEMORY;
601
0
    goto done;
602
0
  }
603
604
0
  key = string_term_tdb_data(keystr);
605
606
0
  db_sc = open_schannel_session_store(tmpctx, lp_ctx);
607
0
  if (!db_sc) {
608
0
    status = NT_STATUS_ACCESS_DENIED;
609
0
    goto done;
610
0
  }
611
612
0
  record = dbwrap_fetch_locked(db_sc, tmpctx, key);
613
0
  if (!record) {
614
0
    status = NT_STATUS_INTERNAL_DB_CORRUPTION;
615
0
    goto done;
616
0
  }
617
618
  /* Because this is a shared structure (even across
619
   * disconnects) we must update the database every time we
620
   * update the structure */
621
622
0
  status = schannel_fetch_session_key_tdb(db_sc, tmpctx,
623
0
            computer_name, &creds);
624
0
  if (!NT_STATUS_IS_OK(status)) {
625
0
    goto done;
626
0
  }
627
628
0
  status = netlogon_creds_server_step_check(creds,
629
0
              received_authenticator,
630
0
              return_authenticator,
631
0
              auth_type,
632
0
              auth_level);
633
0
  if (access_check_cb != NULL) {
634
0
    NTSTATUS step_status = status;
635
0
    status = access_check_cb(creds,
636
0
           step_status,
637
0
           &store,
638
0
           access_check_private);
639
0
  }
640
0
  if (!NT_STATUS_IS_OK(status)) {
641
0
    goto done;
642
0
  }
643
644
0
  if (store) {
645
0
    status = schannel_store_session_key_tdb(db_sc, tmpctx, creds);
646
0
    if (!NT_STATUS_IS_OK(status)) {
647
0
      goto done;
648
0
    }
649
0
  }
650
651
0
  if (creds_out) {
652
0
    *creds_out = talloc_steal(mem_ctx, creds);
653
0
    if (!*creds_out) {
654
0
      status = NT_STATUS_NO_MEMORY;
655
0
      goto done;
656
0
    }
657
0
  }
658
659
0
  status = NT_STATUS_OK;
660
661
0
done:
662
0
  talloc_free(tmpctx);
663
0
  return status;
664
0
}
665