Coverage Report

Created: 2026-09-03 07:24

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/samba/source3/libsmb/samlogon_cache.c
Line
Count
Source
1
/*
2
   Unix SMB/CIFS implementation.
3
   Net_sam_logon info3 helpers
4
   Copyright (C) Alexander Bokovoy              2002.
5
   Copyright (C) Andrew Bartlett                2002.
6
   Copyright (C) Gerald Carter      2003.
7
   Copyright (C) Tim Potter     2003.
8
   Copyright (C) Guenther Deschner    2008.
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 "replace.h"
25
#include "samlogon_cache.h"
26
#include "system/filesys.h"
27
#include "system/time.h"
28
#include "lib/util/debug.h"
29
#include "lib/util/talloc_stack.h"
30
#include "lib/util/memory.h" /* for SAFE_FREE() */
31
#include "source3/lib/util_path.h"
32
#include "librpc/gen_ndr/ndr_krb5pac.h"
33
#include "../libcli/security/security.h"
34
#include "util_tdb.h"
35
#include "param/loadparm.h"
36
37
0
#define NETSAMLOGON_TDB "netsamlogon_cache.tdb"
38
39
static TDB_CONTEXT *netsamlogon_tdb = NULL;
40
41
/***********************************************************************
42
 open the tdb
43
 ***********************************************************************/
44
45
bool netsamlogon_cache_init(void)
46
0
{
47
0
  bool first_try = true;
48
0
  char *path = NULL;
49
0
  int ret;
50
0
  struct tdb_context *tdb;
51
52
0
  if (netsamlogon_tdb) {
53
0
    return true;
54
0
  }
55
56
0
  path = cache_path(talloc_tos(), NETSAMLOGON_TDB);
57
0
  if (path == NULL) {
58
0
    return false;
59
0
  }
60
0
again:
61
0
  tdb = tdb_open_log(path, 0, TDB_DEFAULT|TDB_INCOMPATIBLE_HASH,
62
0
         O_RDWR | O_CREAT, 0600);
63
0
  if (tdb == NULL) {
64
0
    DEBUG(0,("tdb_open_log('%s') - failed\n", path));
65
0
    goto clear;
66
0
  }
67
68
0
  ret = tdb_check(tdb, NULL, NULL);
69
0
  if (ret != 0) {
70
0
    tdb_close(tdb);
71
0
    DEBUG(0,("tdb_check('%s') - failed\n", path));
72
0
    goto clear;
73
0
  }
74
75
0
  netsamlogon_tdb = tdb;
76
0
  talloc_free(path);
77
0
  return true;
78
79
0
clear:
80
0
  if (!first_try) {
81
0
    talloc_free(path);
82
0
    return false;
83
0
  }
84
0
  first_try = false;
85
86
0
  DEBUG(0,("retry after truncate for '%s'\n", path));
87
0
  ret = truncate(path, 0);
88
0
  if (ret == -1) {
89
0
    DBG_ERR("truncate failed: %s\n", strerror(errno));
90
0
    talloc_free(path);
91
0
    return false;
92
0
  }
93
94
0
  goto again;
95
0
}
96
97
/***********************************************************************
98
 Clear the whole cache
99
***********************************************************************/
100
101
void netsamlogon_cache_flush(void)
102
0
{
103
0
  if (!netsamlogon_cache_init()) {
104
0
    DBG_ERR("cannot open %s for write!\n",
105
0
      NETSAMLOGON_TDB);
106
0
    return;
107
0
  }
108
109
0
  DBG_DEBUG("Wipe all!\n");
110
111
0
  tdb_wipe_all(netsamlogon_tdb);
112
0
}
113
114
/***********************************************************************
115
 Clear cache getpwnam and getgroups entries from the winbindd cache
116
***********************************************************************/
117
118
void netsamlogon_clear_cached_user(const struct dom_sid *user_sid)
119
0
{
120
0
  struct dom_sid_buf keystr;
121
122
0
  if (!netsamlogon_cache_init()) {
123
0
    DEBUG(0,("netsamlogon_clear_cached_user: cannot open "
124
0
      "%s for write!\n",
125
0
      NETSAMLOGON_TDB));
126
0
    return;
127
0
  }
128
129
  /* Prepare key as DOMAIN-SID/USER-RID string */
130
0
  dom_sid_str_buf(user_sid, &keystr);
131
132
0
  DBG_DEBUG("SID [%s]\n", keystr.buf);
133
134
0
  tdb_delete_bystring(netsamlogon_tdb, keystr.buf);
135
0
}
136
137
/***********************************************************************
138
 Store a netr_SamInfo3 structure in a tdb for later user
139
 username should be in UTF-8 format
140
***********************************************************************/
141
142
bool netsamlogon_cache_store(const char *username, struct netr_SamInfo3 *info3)
143
0
{
144
0
  uint8_t dummy = 0;
145
0
  TDB_DATA data = { .dptr = &dummy, .dsize = sizeof(dummy) };
146
0
  struct dom_sid_buf keystr;
147
0
  bool result = false;
148
0
  struct dom_sid  user_sid;
149
0
  TALLOC_CTX *tmp_ctx = talloc_stackframe();
150
0
  DATA_BLOB blob;
151
0
  enum ndr_err_code ndr_err;
152
0
  struct netsamlogoncache_entry r;
153
0
  int ret;
154
155
0
  if (!info3) {
156
0
    goto fail;
157
0
  }
158
159
0
  if (!netsamlogon_cache_init()) {
160
0
    D_WARNING("netsamlogon_cache_store: cannot open %s for write!\n",
161
0
      NETSAMLOGON_TDB);
162
0
    goto fail;
163
0
  }
164
165
  /*
166
   * First write a record with just the domain sid for
167
   * netsamlogon_cache_domain_known. Use TDB_INSERT to avoid
168
   * overwriting potentially other data. We're just interested
169
   * in the existence of that record.
170
   */
171
0
  dom_sid_str_buf(info3->base.domain_sid, &keystr);
172
173
0
  ret = tdb_store_bystring(netsamlogon_tdb, keystr.buf, data, TDB_INSERT);
174
175
0
  if ((ret == -1) && (tdb_error(netsamlogon_tdb) != TDB_ERR_EXISTS)) {
176
0
    D_WARNING("Could not store domain marker for %s: %s\n",
177
0
          keystr.buf, tdb_errorstr(netsamlogon_tdb));
178
0
    goto fail;
179
0
  }
180
181
0
  sid_compose(&user_sid, info3->base.domain_sid, info3->base.rid);
182
183
  /* Prepare key as DOMAIN-SID/USER-RID string */
184
0
  dom_sid_str_buf(&user_sid, &keystr);
185
186
0
  DBG_DEBUG("SID [%s]\n", keystr.buf);
187
188
  /* Prepare data */
189
190
0
  if (info3->base.full_name.string == NULL) {
191
0
    struct netr_SamInfo3 *cached_info3;
192
0
    const char *full_name = NULL;
193
194
0
    cached_info3 = netsamlogon_cache_get(tmp_ctx, &user_sid);
195
0
    if (cached_info3 != NULL) {
196
0
      full_name = cached_info3->base.full_name.string;
197
0
    }
198
199
0
    if (full_name != NULL) {
200
0
      info3->base.full_name.string = talloc_strdup(info3, full_name);
201
0
      if (info3->base.full_name.string == NULL) {
202
0
        goto fail;
203
0
      }
204
0
    }
205
0
  }
206
207
  /* only Samba fills in the username, not sure why NT doesn't */
208
  /* so we fill it in since winbindd_getpwnam() makes use of it */
209
210
0
  if (!info3->base.account_name.string) {
211
0
    info3->base.account_name.string = talloc_strdup(info3, username);
212
0
    if (info3->base.account_name.string == NULL) {
213
0
      goto fail;
214
0
    }
215
0
  }
216
217
0
  r.timestamp = time(NULL);
218
0
  r.info3 = *info3;
219
220
  /* avoid storing secret information */
221
0
  ZERO_STRUCT(r.info3.base.key);
222
0
  ZERO_STRUCT(r.info3.base.LMSessKey);
223
224
0
  if (DEBUGLEVEL >= 10) {
225
0
    NDR_PRINT_DEBUG(netsamlogoncache_entry, &r);
226
0
  }
227
228
0
  ndr_err = ndr_push_struct_blob(&blob, tmp_ctx, &r,
229
0
               (ndr_push_flags_fn_t)ndr_push_netsamlogoncache_entry);
230
0
  if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
231
0
    DBG_WARNING("failed to push entry to cache: %s\n",
232
0
          ndr_errstr(ndr_err));
233
0
    goto fail;
234
0
  }
235
236
0
  data.dsize = blob.length;
237
0
  data.dptr = blob.data;
238
239
0
  if (tdb_store_bystring(netsamlogon_tdb, keystr.buf, data, TDB_REPLACE) == 0) {
240
0
    result = true;
241
0
  }
242
243
0
fail:
244
0
  TALLOC_FREE(tmp_ctx);
245
0
  return result;
246
0
}
247
248
/***********************************************************************
249
 Retrieves a netr_SamInfo3 structure from a tdb.  Caller must
250
 free the user_info struct (talloced memory)
251
***********************************************************************/
252
253
struct netr_SamInfo3 *netsamlogon_cache_get(TALLOC_CTX *mem_ctx, const struct dom_sid *user_sid)
254
0
{
255
0
  struct netr_SamInfo3 *info3 = NULL;
256
0
  TDB_DATA data;
257
0
  struct dom_sid_buf keystr;
258
0
  enum ndr_err_code ndr_err;
259
0
  DATA_BLOB blob;
260
0
  struct netsamlogoncache_entry r;
261
0
  int ttl;
262
263
0
  if (!netsamlogon_cache_init()) {
264
0
    DEBUG(0,("netsamlogon_cache_get: cannot open %s for write!\n",
265
0
      NETSAMLOGON_TDB));
266
0
    return NULL;
267
0
  }
268
269
  /* Prepare key as DOMAIN-SID/USER-RID string */
270
0
  dom_sid_str_buf(user_sid, &keystr);
271
0
  DBG_DEBUG("SID [%s]\n", keystr.buf);
272
0
  data = tdb_fetch_bystring( netsamlogon_tdb, keystr.buf );
273
274
0
  if (!data.dptr) {
275
0
    D_DEBUG("tdb fetch for %s is empty\n", keystr.buf);
276
0
    return NULL;
277
0
  }
278
279
0
  info3 = talloc_zero(mem_ctx, struct netr_SamInfo3);
280
0
  if (!info3) {
281
0
    goto done;
282
0
  }
283
284
0
  blob = data_blob_const(data.dptr, data.dsize);
285
286
0
  ndr_err = ndr_pull_struct_blob_all(
287
0
    &blob, mem_ctx, &r,
288
0
    (ndr_pull_flags_fn_t)ndr_pull_netsamlogoncache_entry);
289
290
0
  if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
291
0
    D_WARNING("netsamlogon_cache_get: failed to pull entry from cache\n");
292
0
    tdb_delete_bystring(netsamlogon_tdb, keystr.buf);
293
0
    TALLOC_FREE(info3);
294
0
    goto done;
295
0
  }
296
297
0
  NDR_PRINT_DEBUG_LEVEL(DBGLVL_DEBUG, netsamlogoncache_entry, &r);
298
299
0
  ttl = lp_parm_int(-1, "netsamlogoncache", "timeout", 0);
300
301
0
  if (ttl > 0 && r.timestamp + ttl < time(NULL)) {
302
0
    DBG_DEBUG("netsamlogon cache entry expired\n");
303
0
    tdb_delete_bystring(netsamlogon_tdb, keystr.buf);
304
0
    TALLOC_FREE(info3);
305
0
    goto done;
306
0
  }
307
308
0
  info3 = (struct netr_SamInfo3 *)talloc_memdup(mem_ctx, &r.info3,
309
0
                  sizeof(r.info3));
310
311
0
 done:
312
0
  SAFE_FREE(data.dptr);
313
314
0
  return info3;
315
0
}
316
317
bool netsamlogon_cache_have(const struct dom_sid *sid)
318
0
{
319
0
  struct dom_sid_buf keystr;
320
0
  bool ok;
321
322
0
  if (!netsamlogon_cache_init()) {
323
0
    DBG_WARNING("Cannot open %s\n", NETSAMLOGON_TDB);
324
0
    return false;
325
0
  }
326
327
0
  dom_sid_str_buf(sid, &keystr);
328
329
0
  ok = tdb_exists(netsamlogon_tdb, string_term_tdb_data(keystr.buf));
330
0
  return ok;
331
0
}
332
333
struct netsamlog_cache_forall_state {
334
  TALLOC_CTX *mem_ctx;
335
  int (*cb)(const char *sid_str,
336
      time_t when_cached,
337
      struct netr_SamInfo3 *,
338
      void *private_data);
339
  void *private_data;
340
};
341
342
static int netsamlog_cache_traverse_cb(struct tdb_context *tdb,
343
               TDB_DATA key,
344
               TDB_DATA data,
345
               void *private_data)
346
0
{
347
0
  struct netsamlog_cache_forall_state *state =
348
0
    (struct netsamlog_cache_forall_state *)private_data;
349
0
  TALLOC_CTX *mem_ctx = NULL;
350
0
  DATA_BLOB blob;
351
0
  const char *sid_str = NULL;
352
0
  struct dom_sid sid;
353
0
  struct netsamlogoncache_entry r;
354
0
  enum ndr_err_code ndr_err;
355
0
  int ret;
356
0
  bool ok;
357
358
0
  if (key.dsize == 0) {
359
0
    return 0;
360
0
  }
361
0
  if (key.dptr[key.dsize - 1] != '\0') {
362
0
    return 0;
363
0
  }
364
0
  if (data.dptr == NULL) {
365
0
    return 0;
366
0
  }
367
0
  sid_str = (char *)key.dptr;
368
369
0
  ok = string_to_sid(&sid, sid_str);
370
0
  if (!ok) {
371
0
    DBG_ERR("String to SID failed for %s\n", sid_str);
372
0
    return -1;
373
0
  }
374
375
0
  if (sid.num_auths != 5) {
376
0
    return 0;
377
0
  }
378
379
0
  mem_ctx = talloc_new(state->mem_ctx);
380
0
  if (mem_ctx == NULL) {
381
0
    return -1;
382
0
  }
383
384
0
  blob = data_blob_const(data.dptr, data.dsize);
385
386
0
  ndr_err = ndr_pull_struct_blob(
387
0
    &blob, state->mem_ctx, &r,
388
0
    (ndr_pull_flags_fn_t)ndr_pull_netsamlogoncache_entry);
389
390
0
  if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
391
0
    DBG_ERR("failed to pull entry from cache\n");
392
0
    return -1;
393
0
  }
394
395
0
  ret = state->cb(sid_str, r.timestamp, &r.info3, state->private_data);
396
397
0
  TALLOC_FREE(mem_ctx);
398
0
  return ret;
399
0
}
400
401
int netsamlog_cache_for_all(int (*cb)(const char *sid_str,
402
              time_t when_cached,
403
              struct netr_SamInfo3 *,
404
              void *private_data),
405
          void *private_data)
406
0
{
407
0
  int ret;
408
0
  TALLOC_CTX *mem_ctx = NULL;
409
0
  struct netsamlog_cache_forall_state state;
410
411
0
  if (!netsamlogon_cache_init()) {
412
0
    DBG_ERR("Cannot open %s\n", NETSAMLOGON_TDB);
413
0
    return -1;
414
0
  }
415
416
0
  mem_ctx = talloc_init("netsamlog_cache_for_all");
417
0
  if (mem_ctx == NULL) {
418
0
    return -1;
419
0
  }
420
421
0
  state = (struct netsamlog_cache_forall_state) {
422
0
    .mem_ctx = mem_ctx,
423
0
    .cb = cb,
424
0
    .private_data = private_data,
425
0
  };
426
427
0
  ret = tdb_traverse_read(netsamlogon_tdb,
428
0
        netsamlog_cache_traverse_cb,
429
0
        &state);
430
431
  TALLOC_FREE(state.mem_ctx);
432
0
  return ret;
433
0
}