Coverage Report

Created: 2026-09-03 07:24

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/samba/source4/kdc/pac-blobs.c
Line
Count
Source
1
/*
2
   Unix SMB/CIFS implementation.
3
4
   PAC Glue between Samba and the KDC
5
6
   Copyright (C) Catalyst.Net Ltd 2023
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 "source4/kdc/pac-blobs.h"
23
24
#include "lib/util/debug.h"
25
#include "lib/util/samba_util.h"
26
27
static inline size_t *pac_blobs_get_index(struct pac_blobs *pac_blobs, size_t type)
28
0
{
29
  /* Ensure the type is valid. */
30
0
  SMB_ASSERT(type >= PAC_TYPE_BEGIN);
31
0
  SMB_ASSERT(type < PAC_TYPE_END);
32
33
0
  return &pac_blobs->type_index[type - PAC_TYPE_BEGIN];
34
0
}
35
36
static inline struct type_data *pac_blobs_get(struct pac_blobs *pac_blobs, size_t type)
37
0
{
38
0
  size_t index = *pac_blobs_get_index(pac_blobs, type);
39
0
  SMB_ASSERT(index < pac_blobs->num_types);
40
41
0
  return &pac_blobs->type_blobs[index];
42
0
}
43
44
krb5_error_code pac_blobs_from_krb5_pac(TALLOC_CTX *mem_ctx,
45
          krb5_context context,
46
          const krb5_const_pac pac,
47
          struct pac_blobs **pac_blobs)
48
0
{
49
0
  krb5_error_code code = 0;
50
0
  uint32_t *types = NULL;
51
0
  struct pac_blobs *blobs = NULL;
52
0
  size_t i;
53
54
0
  SMB_ASSERT(pac_blobs != NULL);
55
0
  *pac_blobs = NULL;
56
57
0
  blobs = talloc(mem_ctx, struct pac_blobs);
58
0
  if (blobs == NULL) {
59
0
    code = ENOMEM;
60
0
    goto out;
61
0
  }
62
63
0
  *blobs = (struct pac_blobs) {};
64
65
  /* Initialize the array indices. */
66
0
  for (i = 0; i < ARRAY_SIZE(blobs->type_index); ++i) {
67
0
    blobs->type_index[i] = SIZE_MAX;
68
0
  }
69
70
0
  code = krb5_pac_get_types(context, pac, &blobs->num_types, &types);
71
0
  if (code != 0) {
72
0
    DBG_ERR("krb5_pac_get_types failed\n");
73
0
    goto out;
74
0
  }
75
76
0
  blobs->type_blobs = talloc_array(blobs, struct type_data, blobs->num_types);
77
0
  if (blobs->type_blobs == NULL) {
78
0
    DBG_ERR("Out of memory\n");
79
0
    code = ENOMEM;
80
0
    goto out;
81
0
  }
82
83
0
  for (i = 0; i < blobs->num_types; ++i) {
84
0
    uint32_t type = types[i];
85
0
    size_t *type_index = NULL;
86
87
0
    blobs->type_blobs[i] = (struct type_data) {
88
0
      .type = type,
89
0
      .data = NULL,
90
0
    };
91
92
0
    switch (type) {
93
      /* PAC buffer types that we support. */
94
0
    case PAC_TYPE_LOGON_INFO:
95
0
    case PAC_TYPE_CREDENTIAL_INFO:
96
0
    case PAC_TYPE_SRV_CHECKSUM:
97
0
    case PAC_TYPE_KDC_CHECKSUM:
98
0
    case PAC_TYPE_LOGON_NAME:
99
0
    case PAC_TYPE_CONSTRAINED_DELEGATION:
100
0
    case PAC_TYPE_UPN_DNS_INFO:
101
0
    case PAC_TYPE_CLIENT_CLAIMS_INFO:
102
0
    case PAC_TYPE_DEVICE_INFO:
103
0
    case PAC_TYPE_DEVICE_CLAIMS_INFO:
104
0
    case PAC_TYPE_TICKET_CHECKSUM:
105
0
    case PAC_TYPE_ATTRIBUTES_INFO:
106
0
    case PAC_TYPE_REQUESTER_SID:
107
0
    case PAC_TYPE_FULL_CHECKSUM:
108
0
      type_index = pac_blobs_get_index(blobs, type);
109
0
      if (*type_index != SIZE_MAX) {
110
0
        DBG_WARNING("PAC buffer type[%"PRIu32"] twice\n", type);
111
0
        code = EINVAL;
112
0
        goto out;
113
0
      }
114
0
      *type_index = i;
115
116
0
      break;
117
0
    default:
118
0
      break;
119
0
    }
120
0
  }
121
122
0
  *pac_blobs = blobs;
123
0
  blobs = NULL;
124
125
0
out:
126
0
  SAFE_FREE(types);
127
0
  TALLOC_FREE(blobs);
128
0
  return code;
129
0
}
130
131
krb5_error_code _pac_blobs_ensure_exists(struct pac_blobs *pac_blobs,
132
           const uint32_t type,
133
           const char *name,
134
           const char *location,
135
           const char *function)
136
0
{
137
0
  if (*pac_blobs_get_index(pac_blobs, type) == SIZE_MAX) {
138
0
    DEBUGLF(DBGLVL_ERR, ("%s: %s missing\n", function, name), location, function);
139
0
    return EINVAL;
140
0
  }
141
142
0
  return 0;
143
0
}
144
145
krb5_error_code _pac_blobs_replace_existing(struct pac_blobs *pac_blobs,
146
              const uint32_t type,
147
              const char *name,
148
              const DATA_BLOB *blob,
149
              const char *location,
150
              const char *function)
151
0
{
152
0
  krb5_error_code code;
153
154
0
  code = _pac_blobs_ensure_exists(pac_blobs,
155
0
          type,
156
0
          name,
157
0
          location,
158
0
          function);
159
0
  if (code != 0) {
160
0
    return code;
161
0
  }
162
163
0
  pac_blobs_get(pac_blobs, type)->data = blob;
164
165
0
  return 0;
166
0
}
167
168
krb5_error_code pac_blobs_add_blob(struct pac_blobs *pac_blobs,
169
           const uint32_t type,
170
           const DATA_BLOB *blob)
171
0
{
172
0
  size_t *index = NULL;
173
174
0
  if (blob == NULL) {
175
0
    return 0;
176
0
  }
177
178
0
  index = pac_blobs_get_index(pac_blobs, type);
179
0
  if (*index == SIZE_MAX) {
180
0
    struct type_data *type_blobs = NULL;
181
182
0
    type_blobs = talloc_realloc(pac_blobs,
183
0
              pac_blobs->type_blobs,
184
0
              struct type_data,
185
0
              pac_blobs->num_types + 1);
186
0
    if (type_blobs == NULL) {
187
0
      DBG_ERR("Out of memory\n");
188
0
      return ENOMEM;
189
0
    }
190
191
0
    pac_blobs->type_blobs = type_blobs;
192
0
    *index = pac_blobs->num_types++;
193
0
  }
194
195
0
  *pac_blobs_get(pac_blobs, type) = (struct type_data) {
196
0
    .type = type,
197
0
    .data = blob,
198
0
  };
199
200
0
  return 0;
201
0
}
202
203
void pac_blobs_remove_blob(struct pac_blobs *pac_blobs,
204
         const uint32_t type)
205
0
{
206
0
  struct type_data *type_blobs = NULL;
207
0
  size_t found_index;
208
0
  size_t i;
209
210
  /* Get the index of this PAC buffer type. */
211
0
  found_index = *pac_blobs_get_index(pac_blobs, type);
212
0
  if (found_index == SIZE_MAX) {
213
    /* We don't have a PAC buffer of this type, so we're done. */
214
0
    return;
215
0
  }
216
217
  /* Since the PAC buffer is present, there will be at least one type in the array. */
218
0
  SMB_ASSERT(pac_blobs->num_types > 0);
219
220
  /* The index should be valid. */
221
0
  SMB_ASSERT(found_index < pac_blobs->num_types);
222
223
  /*
224
   * Even though a consistent ordering of PAC buffers is not to be relied
225
   * upon, we must still maintain the ordering we are given.
226
   */
227
0
  for (i = found_index; i < pac_blobs->num_types - 1; ++i) {
228
0
    size_t moved_type;
229
230
    /* Shift each following element backwards by one. */
231
0
    pac_blobs->type_blobs[i] = pac_blobs->type_blobs[i + 1];
232
233
    /* Mark the new position of the moved element in the index. */
234
0
    moved_type = pac_blobs->type_blobs[i].type;
235
0
    if (moved_type >= PAC_TYPE_BEGIN && moved_type < PAC_TYPE_END) {
236
0
      *pac_blobs_get_index(pac_blobs, moved_type) = i;
237
0
    }
238
0
  }
239
240
  /* Mark the removed element as no longer present. */
241
0
  *pac_blobs_get_index(pac_blobs, type) = SIZE_MAX;
242
243
  /* We do not free the removed data blob, as it may be statically allocated (e.g., a null blob). */
244
245
  /* Remove the last element from the array. */
246
0
  type_blobs = talloc_realloc(pac_blobs,
247
0
            pac_blobs->type_blobs,
248
0
            struct type_data,
249
0
            --pac_blobs->num_types);
250
0
  if (type_blobs != NULL) {
251
0
    pac_blobs->type_blobs = type_blobs;
252
0
  }
253
0
}