/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 | } |