/src/samba/source4/auth/kerberos/kerberos_pac.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | Unix SMB/CIFS implementation. |
3 | | |
4 | | Create and parse the krb5 PAC |
5 | | |
6 | | Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004-2005,2008 |
7 | | Copyright (C) Andrew Tridgell 2001 |
8 | | Copyright (C) Luke Howard 2002-2003 |
9 | | Copyright (C) Stefan Metzmacher 2004-2005 |
10 | | |
11 | | This program is free software; you can redistribute it and/or modify |
12 | | it under the terms of the GNU General Public License as published by |
13 | | the Free Software Foundation; either version 3 of the License, or |
14 | | (at your option) any later version. |
15 | | |
16 | | This program is distributed in the hope that it will be useful, |
17 | | but WITHOUT ANY WARRANTY; without even the implied warranty of |
18 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
19 | | GNU General Public License for more details. |
20 | | |
21 | | |
22 | | You should have received a copy of the GNU General Public License |
23 | | along with this program. If not, see <http://www.gnu.org/licenses/>. |
24 | | */ |
25 | | |
26 | | #include "includes.h" |
27 | | #include "system/kerberos.h" |
28 | | #include "auth/auth.h" |
29 | | #include "auth/kerberos/kerberos.h" |
30 | | #include "librpc/gen_ndr/ndr_krb5pac.h" |
31 | | #include <ldb.h> |
32 | | #include "auth/auth_sam_reply.h" |
33 | | #include "auth/credentials/credentials.h" |
34 | | #include "auth/kerberos/kerberos_util.h" |
35 | | #include "auth/kerberos/pac_utils.h" |
36 | | |
37 | | krb5_error_code kerberos_encode_pac(TALLOC_CTX *mem_ctx, |
38 | | struct PAC_DATA *pac_data, |
39 | | krb5_context context, |
40 | | const krb5_keyblock *krbtgt_keyblock, |
41 | | const krb5_keyblock *service_keyblock, |
42 | | DATA_BLOB *pac) |
43 | 0 | { |
44 | 0 | NTSTATUS nt_status; |
45 | 0 | krb5_error_code ret; |
46 | 0 | enum ndr_err_code ndr_err; |
47 | 0 | DATA_BLOB zero_blob = data_blob(NULL, 0); |
48 | 0 | DATA_BLOB tmp_blob = data_blob(NULL, 0); |
49 | 0 | struct PAC_SIGNATURE_DATA *kdc_checksum = NULL; |
50 | 0 | struct PAC_SIGNATURE_DATA *srv_checksum = NULL; |
51 | 0 | uint32_t i; |
52 | | |
53 | | /* First, just get the keytypes filled in (and lengths right, eventually) */ |
54 | 0 | for (i=0; i < pac_data->num_buffers; i++) { |
55 | 0 | if (pac_data->buffers[i].type != PAC_TYPE_KDC_CHECKSUM) { |
56 | 0 | continue; |
57 | 0 | } |
58 | 0 | kdc_checksum = &pac_data->buffers[i].info->kdc_cksum, |
59 | 0 | ret = smb_krb5_make_pac_checksum(mem_ctx, |
60 | 0 | &zero_blob, |
61 | 0 | context, |
62 | 0 | krbtgt_keyblock, |
63 | 0 | &kdc_checksum->type, |
64 | 0 | &kdc_checksum->signature); |
65 | 0 | if (ret) { |
66 | 0 | DEBUG(2, ("making krbtgt PAC checksum failed: %s\n", |
67 | 0 | smb_get_krb5_error_message(context, ret, mem_ctx))); |
68 | 0 | talloc_free(pac_data); |
69 | 0 | return ret; |
70 | 0 | } |
71 | 0 | } |
72 | | |
73 | 0 | for (i=0; i < pac_data->num_buffers; i++) { |
74 | 0 | if (pac_data->buffers[i].type != PAC_TYPE_SRV_CHECKSUM) { |
75 | 0 | continue; |
76 | 0 | } |
77 | 0 | srv_checksum = &pac_data->buffers[i].info->srv_cksum; |
78 | 0 | ret = smb_krb5_make_pac_checksum(mem_ctx, |
79 | 0 | &zero_blob, |
80 | 0 | context, |
81 | 0 | service_keyblock, |
82 | 0 | &srv_checksum->type, |
83 | 0 | &srv_checksum->signature); |
84 | 0 | if (ret) { |
85 | 0 | DEBUG(2, ("making service PAC checksum failed: %s\n", |
86 | 0 | smb_get_krb5_error_message(context, ret, mem_ctx))); |
87 | 0 | talloc_free(pac_data); |
88 | 0 | return ret; |
89 | 0 | } |
90 | 0 | } |
91 | | |
92 | 0 | if (!kdc_checksum) { |
93 | 0 | DEBUG(2, ("Invalid PAC constructed for signing, no KDC checksum present!\n")); |
94 | 0 | return EINVAL; |
95 | 0 | } |
96 | 0 | if (!srv_checksum) { |
97 | 0 | DEBUG(2, ("Invalid PAC constructed for signing, no SRV checksum present!\n")); |
98 | 0 | return EINVAL; |
99 | 0 | } |
100 | | |
101 | | /* But wipe out the actual signatures */ |
102 | 0 | memset(kdc_checksum->signature.data, '\0', kdc_checksum->signature.length); |
103 | 0 | memset(srv_checksum->signature.data, '\0', srv_checksum->signature.length); |
104 | |
|
105 | 0 | ndr_err = ndr_push_struct_blob(&tmp_blob, mem_ctx, |
106 | 0 | pac_data, |
107 | 0 | (ndr_push_flags_fn_t)ndr_push_PAC_DATA); |
108 | 0 | if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { |
109 | 0 | nt_status = ndr_map_error2ntstatus(ndr_err); |
110 | 0 | DEBUG(1, ("PAC (presig) push failed: %s\n", nt_errstr(nt_status))); |
111 | 0 | talloc_free(pac_data); |
112 | 0 | return EINVAL; |
113 | 0 | } |
114 | | |
115 | | /* Then sign the result of the previous push, where the sig was zero'ed out */ |
116 | 0 | ret = smb_krb5_make_pac_checksum(mem_ctx, |
117 | 0 | &tmp_blob, |
118 | 0 | context, |
119 | 0 | service_keyblock, |
120 | 0 | &srv_checksum->type, |
121 | 0 | &srv_checksum->signature); |
122 | |
|
123 | 0 | if (ret) { |
124 | 0 | DBG_WARNING("making krbtgt PAC srv_checksum failed: %s\n", |
125 | 0 | smb_get_krb5_error_message(context, ret, mem_ctx)); |
126 | 0 | talloc_free(pac_data); |
127 | 0 | return ret; |
128 | 0 | } |
129 | | |
130 | | /* Then sign Server checksum */ |
131 | 0 | ret = smb_krb5_make_pac_checksum(mem_ctx, |
132 | 0 | &srv_checksum->signature, |
133 | 0 | context, |
134 | 0 | krbtgt_keyblock, |
135 | 0 | &kdc_checksum->type, |
136 | 0 | &kdc_checksum->signature); |
137 | 0 | if (ret) { |
138 | 0 | DBG_WARNING("making krbtgt PAC kdc_checksum failed: %s\n", |
139 | 0 | smb_get_krb5_error_message(context, ret, mem_ctx)); |
140 | 0 | talloc_free(pac_data); |
141 | 0 | return ret; |
142 | 0 | } |
143 | | |
144 | | /* And push it out again, this time to the world. This relies on deterministic pointer values */ |
145 | 0 | ndr_err = ndr_push_struct_blob(&tmp_blob, mem_ctx, |
146 | 0 | pac_data, |
147 | 0 | (ndr_push_flags_fn_t)ndr_push_PAC_DATA); |
148 | 0 | if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { |
149 | 0 | nt_status = ndr_map_error2ntstatus(ndr_err); |
150 | 0 | DEBUG(1, ("PAC (final) push failed: %s\n", nt_errstr(nt_status))); |
151 | 0 | talloc_free(pac_data); |
152 | 0 | return EINVAL; |
153 | 0 | } |
154 | | |
155 | 0 | *pac = tmp_blob; |
156 | |
|
157 | 0 | return ret; |
158 | 0 | } |
159 | | |
160 | | |
161 | | krb5_error_code kerberos_create_pac(TALLOC_CTX *mem_ctx, |
162 | | struct auth_user_info_dc *user_info_dc, |
163 | | krb5_context context, |
164 | | const krb5_keyblock *krbtgt_keyblock, |
165 | | const krb5_keyblock *service_keyblock, |
166 | | krb5_principal client_principal, |
167 | | time_t tgs_authtime, |
168 | | DATA_BLOB *pac) |
169 | 0 | { |
170 | 0 | NTSTATUS nt_status; |
171 | 0 | krb5_error_code ret; |
172 | 0 | struct PAC_DATA *pac_data = talloc(mem_ctx, struct PAC_DATA); |
173 | 0 | struct netr_SamInfo3 *sam3; |
174 | 0 | union PAC_INFO *u_LOGON_INFO; |
175 | 0 | struct PAC_LOGON_INFO *LOGON_INFO; |
176 | 0 | union PAC_INFO *u_LOGON_NAME; |
177 | 0 | struct PAC_LOGON_NAME *LOGON_NAME; |
178 | 0 | union PAC_INFO *u_KDC_CHECKSUM; |
179 | 0 | union PAC_INFO *u_SRV_CHECKSUM; |
180 | |
|
181 | 0 | char *name; |
182 | |
|
183 | 0 | enum { |
184 | 0 | PAC_BUF_LOGON_INFO = 0, |
185 | 0 | PAC_BUF_LOGON_NAME = 1, |
186 | 0 | PAC_BUF_SRV_CHECKSUM = 2, |
187 | 0 | PAC_BUF_KDC_CHECKSUM = 3, |
188 | 0 | PAC_BUF_NUM_BUFFERS = 4 |
189 | 0 | }; |
190 | |
|
191 | 0 | if (!pac_data) { |
192 | 0 | return ENOMEM; |
193 | 0 | } |
194 | | |
195 | 0 | pac_data->num_buffers = PAC_BUF_NUM_BUFFERS; |
196 | 0 | pac_data->version = 0; |
197 | |
|
198 | 0 | pac_data->buffers = talloc_array(pac_data, |
199 | 0 | struct PAC_BUFFER, |
200 | 0 | pac_data->num_buffers); |
201 | 0 | if (!pac_data->buffers) { |
202 | 0 | talloc_free(pac_data); |
203 | 0 | return ENOMEM; |
204 | 0 | } |
205 | | |
206 | | /* LOGON_INFO */ |
207 | 0 | u_LOGON_INFO = talloc_zero(pac_data->buffers, union PAC_INFO); |
208 | 0 | if (!u_LOGON_INFO) { |
209 | 0 | talloc_free(pac_data); |
210 | 0 | return ENOMEM; |
211 | 0 | } |
212 | 0 | pac_data->buffers[PAC_BUF_LOGON_INFO].type = PAC_TYPE_LOGON_INFO; |
213 | 0 | pac_data->buffers[PAC_BUF_LOGON_INFO].info = u_LOGON_INFO; |
214 | | |
215 | | /* LOGON_NAME */ |
216 | 0 | u_LOGON_NAME = talloc_zero(pac_data->buffers, union PAC_INFO); |
217 | 0 | if (!u_LOGON_NAME) { |
218 | 0 | talloc_free(pac_data); |
219 | 0 | return ENOMEM; |
220 | 0 | } |
221 | 0 | pac_data->buffers[PAC_BUF_LOGON_NAME].type = PAC_TYPE_LOGON_NAME; |
222 | 0 | pac_data->buffers[PAC_BUF_LOGON_NAME].info = u_LOGON_NAME; |
223 | 0 | LOGON_NAME = &u_LOGON_NAME->logon_name; |
224 | | |
225 | | /* SRV_CHECKSUM */ |
226 | 0 | u_SRV_CHECKSUM = talloc_zero(pac_data->buffers, union PAC_INFO); |
227 | 0 | if (!u_SRV_CHECKSUM) { |
228 | 0 | talloc_free(pac_data); |
229 | 0 | return ENOMEM; |
230 | 0 | } |
231 | 0 | pac_data->buffers[PAC_BUF_SRV_CHECKSUM].type = PAC_TYPE_SRV_CHECKSUM; |
232 | 0 | pac_data->buffers[PAC_BUF_SRV_CHECKSUM].info = u_SRV_CHECKSUM; |
233 | | |
234 | | /* KDC_CHECKSUM */ |
235 | 0 | u_KDC_CHECKSUM = talloc_zero(pac_data->buffers, union PAC_INFO); |
236 | 0 | if (!u_KDC_CHECKSUM) { |
237 | 0 | talloc_free(pac_data); |
238 | 0 | return ENOMEM; |
239 | 0 | } |
240 | 0 | pac_data->buffers[PAC_BUF_KDC_CHECKSUM].type = PAC_TYPE_KDC_CHECKSUM; |
241 | 0 | pac_data->buffers[PAC_BUF_KDC_CHECKSUM].info = u_KDC_CHECKSUM; |
242 | | |
243 | | /* now the real work begins... */ |
244 | |
|
245 | 0 | LOGON_INFO = talloc_zero(u_LOGON_INFO, struct PAC_LOGON_INFO); |
246 | 0 | if (!LOGON_INFO) { |
247 | 0 | talloc_free(pac_data); |
248 | 0 | return ENOMEM; |
249 | 0 | } |
250 | 0 | nt_status = auth_convert_user_info_dc_saminfo3(LOGON_INFO, user_info_dc, |
251 | 0 | AUTH_INCLUDE_RESOURCE_GROUPS, |
252 | 0 | &sam3, NULL); |
253 | 0 | if (!NT_STATUS_IS_OK(nt_status)) { |
254 | 0 | DEBUG(1, ("Getting Samba info failed: %s\n", nt_errstr(nt_status))); |
255 | 0 | talloc_free(pac_data); |
256 | 0 | return EINVAL; |
257 | 0 | } |
258 | | |
259 | 0 | u_LOGON_INFO->logon_info.info = LOGON_INFO; |
260 | 0 | LOGON_INFO->info3 = *sam3; |
261 | |
|
262 | 0 | ret = krb5_unparse_name_flags(context, client_principal, |
263 | 0 | KRB5_PRINCIPAL_UNPARSE_NO_REALM | |
264 | 0 | KRB5_PRINCIPAL_UNPARSE_DISPLAY, |
265 | 0 | &name); |
266 | 0 | if (ret) { |
267 | 0 | return ret; |
268 | 0 | } |
269 | 0 | LOGON_NAME->account_name = talloc_strdup(LOGON_NAME, name); |
270 | 0 | free(name); |
271 | | /* |
272 | | this logon_time field is absolutely critical. This is what |
273 | | caused all our PAC troubles :-) |
274 | | */ |
275 | 0 | unix_to_nt_time(&LOGON_NAME->logon_time, tgs_authtime); |
276 | |
|
277 | 0 | ret = kerberos_encode_pac(mem_ctx, |
278 | 0 | pac_data, |
279 | 0 | context, |
280 | 0 | krbtgt_keyblock, |
281 | 0 | service_keyblock, |
282 | 0 | pac); |
283 | 0 | talloc_free(pac_data); |
284 | 0 | return ret; |
285 | 0 | } |
286 | | |
287 | | static krb5_error_code kerberos_pac_buffer_present(krb5_context context, |
288 | | const krb5_const_pac pac, |
289 | | uint32_t type) |
290 | 0 | { |
291 | 0 | #ifdef SAMBA4_USES_HEIMDAL |
292 | 0 | return krb5_pac_get_buffer(context, pac, type, NULL); |
293 | | #else /* MIT */ |
294 | | krb5_error_code ret; |
295 | | krb5_data data; |
296 | | |
297 | | /* |
298 | | * MIT won't let us pass NULL for the data parameter, so we are forced |
299 | | * to allocate a new buffer and then immediately free it. |
300 | | */ |
301 | | ret = krb5_pac_get_buffer(context, pac, type, &data); |
302 | | if (ret == 0) { |
303 | | krb5_free_data_contents(context, &data); |
304 | | } |
305 | | return ret; |
306 | | #endif /* SAMBA4_USES_HEIMDAL */ |
307 | 0 | } |
308 | | |
309 | | krb5_error_code kerberos_pac_to_user_info_dc(TALLOC_CTX *mem_ctx, |
310 | | krb5_const_pac pac, |
311 | | krb5_context context, |
312 | | struct auth_user_info_dc **user_info_dc, |
313 | | const enum auth_group_inclusion group_inclusion, |
314 | | struct PAC_SIGNATURE_DATA *pac_srv_sig, |
315 | | struct PAC_SIGNATURE_DATA *pac_kdc_sig, |
316 | | struct PAC_DOMAIN_GROUP_MEMBERSHIP **resource_groups) |
317 | 0 | { |
318 | 0 | NTSTATUS nt_status; |
319 | 0 | enum ndr_err_code ndr_err; |
320 | 0 | krb5_error_code ret; |
321 | |
|
322 | 0 | DATA_BLOB pac_logon_info_in, pac_srv_checksum_in, pac_kdc_checksum_in; |
323 | 0 | krb5_data k5pac_logon_info_in, k5pac_srv_checksum_in, k5pac_kdc_checksum_in; |
324 | 0 | DATA_BLOB pac_upn_dns_info_in; |
325 | 0 | krb5_data k5pac_upn_dns_info_in; |
326 | |
|
327 | 0 | union PAC_INFO info; |
328 | 0 | union PAC_INFO _upn_dns_info; |
329 | 0 | struct PAC_UPN_DNS_INFO *upn_dns_info = NULL; |
330 | 0 | struct auth_user_info_dc *user_info_dc_out; |
331 | 0 | const struct PAC_DOMAIN_GROUP_MEMBERSHIP *resource_groups_in = NULL; |
332 | 0 | struct PAC_DOMAIN_GROUP_MEMBERSHIP *resource_groups_out = NULL; |
333 | |
|
334 | 0 | TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx); |
335 | |
|
336 | 0 | if (!tmp_ctx) { |
337 | 0 | return ENOMEM; |
338 | 0 | } |
339 | | |
340 | 0 | if (pac == NULL) { |
341 | 0 | talloc_free(tmp_ctx); |
342 | 0 | return EINVAL; |
343 | 0 | } |
344 | | |
345 | 0 | ret = krb5_pac_get_buffer(context, pac, PAC_TYPE_LOGON_INFO, &k5pac_logon_info_in); |
346 | 0 | if (ret != 0) { |
347 | 0 | talloc_free(tmp_ctx); |
348 | 0 | return EINVAL; |
349 | 0 | } |
350 | | |
351 | 0 | pac_logon_info_in = data_blob_const(k5pac_logon_info_in.data, k5pac_logon_info_in.length); |
352 | |
|
353 | 0 | ndr_err = ndr_pull_union_blob(&pac_logon_info_in, tmp_ctx, &info, |
354 | 0 | PAC_TYPE_LOGON_INFO, |
355 | 0 | (ndr_pull_flags_fn_t)ndr_pull_PAC_INFO); |
356 | 0 | smb_krb5_free_data_contents(context, &k5pac_logon_info_in); |
357 | 0 | if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { |
358 | 0 | nt_status = ndr_map_error2ntstatus(ndr_err); |
359 | 0 | DEBUG(0,("can't parse the PAC LOGON_INFO: %s\n", nt_errstr(nt_status))); |
360 | 0 | talloc_free(tmp_ctx); |
361 | 0 | return EINVAL; |
362 | 0 | } |
363 | 0 | if (info.logon_info.info == NULL) { |
364 | 0 | DEBUG(0,("can't parse the PAC LOGON_INFO: missing info pointer\n")); |
365 | 0 | talloc_free(tmp_ctx); |
366 | 0 | return EINVAL; |
367 | 0 | } |
368 | | |
369 | 0 | ret = krb5_pac_get_buffer(context, pac, PAC_TYPE_UPN_DNS_INFO, |
370 | 0 | &k5pac_upn_dns_info_in); |
371 | 0 | if (ret == ENOENT) { |
372 | 0 | ZERO_STRUCT(k5pac_upn_dns_info_in); |
373 | 0 | ret = 0; |
374 | 0 | } |
375 | 0 | if (ret != 0) { |
376 | 0 | talloc_free(tmp_ctx); |
377 | 0 | return EINVAL; |
378 | 0 | } |
379 | | |
380 | 0 | pac_upn_dns_info_in = data_blob_const(k5pac_upn_dns_info_in.data, |
381 | 0 | k5pac_upn_dns_info_in.length); |
382 | |
|
383 | 0 | if (pac_upn_dns_info_in.length != 0) { |
384 | 0 | ndr_err = ndr_pull_union_blob(&pac_upn_dns_info_in, tmp_ctx, |
385 | 0 | &_upn_dns_info, |
386 | 0 | PAC_TYPE_UPN_DNS_INFO, |
387 | 0 | (ndr_pull_flags_fn_t)ndr_pull_PAC_INFO); |
388 | 0 | smb_krb5_free_data_contents(context, &k5pac_upn_dns_info_in); |
389 | 0 | if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { |
390 | 0 | nt_status = ndr_map_error2ntstatus(ndr_err); |
391 | 0 | DEBUG(0,("can't parse the PAC UPN_DNS_INFO: %s\n", |
392 | 0 | nt_errstr(nt_status))); |
393 | 0 | talloc_free(tmp_ctx); |
394 | 0 | return EINVAL; |
395 | 0 | } |
396 | 0 | upn_dns_info = &_upn_dns_info.upn_dns_info; |
397 | 0 | } |
398 | | |
399 | | /* Pull this right into the normal auth system structures */ |
400 | 0 | nt_status = make_user_info_dc_pac(tmp_ctx, |
401 | 0 | info.logon_info.info, |
402 | 0 | upn_dns_info, |
403 | 0 | group_inclusion, |
404 | 0 | &user_info_dc_out); |
405 | 0 | if (!NT_STATUS_IS_OK(nt_status)) { |
406 | 0 | DBG_ERR("make_user_info_dc_pac() failed - %s\n", |
407 | 0 | nt_errstr(nt_status)); |
408 | 0 | NDR_PRINT_DEBUG(PAC_LOGON_INFO, info.logon_info.info); |
409 | 0 | if (upn_dns_info != NULL) { |
410 | 0 | NDR_PRINT_DEBUG(PAC_UPN_DNS_INFO, upn_dns_info); |
411 | 0 | } |
412 | 0 | talloc_free(tmp_ctx); |
413 | 0 | return EINVAL; |
414 | 0 | } |
415 | | |
416 | 0 | if (pac_srv_sig) { |
417 | 0 | ret = krb5_pac_get_buffer(context, pac, PAC_TYPE_SRV_CHECKSUM, &k5pac_srv_checksum_in); |
418 | 0 | if (ret != 0) { |
419 | 0 | talloc_free(tmp_ctx); |
420 | 0 | return ret; |
421 | 0 | } |
422 | | |
423 | 0 | pac_srv_checksum_in = data_blob_const(k5pac_srv_checksum_in.data, k5pac_srv_checksum_in.length); |
424 | |
|
425 | 0 | ndr_err = ndr_pull_struct_blob(&pac_srv_checksum_in, pac_srv_sig, |
426 | 0 | pac_srv_sig, |
427 | 0 | (ndr_pull_flags_fn_t)ndr_pull_PAC_SIGNATURE_DATA); |
428 | 0 | smb_krb5_free_data_contents(context, &k5pac_srv_checksum_in); |
429 | 0 | if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { |
430 | 0 | nt_status = ndr_map_error2ntstatus(ndr_err); |
431 | 0 | DEBUG(0,("can't parse the server signature: %s\n", |
432 | 0 | nt_errstr(nt_status))); |
433 | 0 | return EINVAL; |
434 | 0 | } |
435 | 0 | } |
436 | | |
437 | 0 | if (pac_kdc_sig) { |
438 | 0 | ret = krb5_pac_get_buffer(context, pac, PAC_TYPE_KDC_CHECKSUM, &k5pac_kdc_checksum_in); |
439 | 0 | if (ret != 0) { |
440 | 0 | talloc_free(tmp_ctx); |
441 | 0 | return ret; |
442 | 0 | } |
443 | | |
444 | 0 | pac_kdc_checksum_in = data_blob_const(k5pac_kdc_checksum_in.data, k5pac_kdc_checksum_in.length); |
445 | |
|
446 | 0 | ndr_err = ndr_pull_struct_blob(&pac_kdc_checksum_in, pac_kdc_sig, |
447 | 0 | pac_kdc_sig, |
448 | 0 | (ndr_pull_flags_fn_t)ndr_pull_PAC_SIGNATURE_DATA); |
449 | 0 | smb_krb5_free_data_contents(context, &k5pac_kdc_checksum_in); |
450 | 0 | if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { |
451 | 0 | nt_status = ndr_map_error2ntstatus(ndr_err); |
452 | 0 | DEBUG(0,("can't parse the KDC signature: %s\n", |
453 | 0 | nt_errstr(nt_status))); |
454 | 0 | return EINVAL; |
455 | 0 | } |
456 | 0 | } |
457 | | |
458 | | /* |
459 | | * Based on the presence of a REQUESTER_SID PAC buffer, ascertain |
460 | | * whether the ticket is a TGT. This helps the KDC and kpasswd service |
461 | | * ensure they do not accept tickets meant for the other. |
462 | | * |
463 | | * This heuristic will fail for older Samba versions and Windows prior |
464 | | * to Nov. 2021 updates, which lack support for the REQUESTER_SID PAC |
465 | | * buffer. |
466 | | */ |
467 | 0 | ret = kerberos_pac_buffer_present(context, pac, PAC_TYPE_REQUESTER_SID); |
468 | 0 | if (ret == ENOENT) { |
469 | | /* This probably isn't a TGT. */ |
470 | 0 | user_info_dc_out->ticket_type = TICKET_TYPE_NON_TGT; |
471 | 0 | } else if (ret != 0) { |
472 | 0 | talloc_free(tmp_ctx); |
473 | 0 | return ret; |
474 | 0 | } else { |
475 | | /* This probably is a TGT. */ |
476 | 0 | user_info_dc_out->ticket_type = TICKET_TYPE_TGT; |
477 | 0 | } |
478 | | |
479 | | /* |
480 | | * If we have resource groups and the caller wants them returned, we |
481 | | * oblige. |
482 | | */ |
483 | 0 | resource_groups_in = &info.logon_info.info->resource_groups; |
484 | 0 | if (resource_groups != NULL && resource_groups_in->groups.count != 0) { |
485 | 0 | resource_groups_out = talloc(tmp_ctx, struct PAC_DOMAIN_GROUP_MEMBERSHIP); |
486 | 0 | if (resource_groups_out == NULL) { |
487 | 0 | talloc_free(tmp_ctx); |
488 | 0 | return ENOMEM; |
489 | 0 | } |
490 | | |
491 | 0 | *resource_groups_out = (struct PAC_DOMAIN_GROUP_MEMBERSHIP) { |
492 | 0 | .domain_sid = talloc_steal(resource_groups_out, resource_groups_in->domain_sid), |
493 | 0 | .groups = { |
494 | 0 | .count = resource_groups_in->groups.count, |
495 | 0 | .rids = talloc_steal(resource_groups_out, resource_groups_in->groups.rids), |
496 | 0 | }, |
497 | 0 | }; |
498 | 0 | } |
499 | | |
500 | 0 | *user_info_dc = talloc_steal(mem_ctx, user_info_dc_out); |
501 | 0 | if (resource_groups_out != NULL) { |
502 | 0 | *resource_groups = talloc_steal(mem_ctx, resource_groups_out); |
503 | 0 | } |
504 | |
|
505 | 0 | return 0; |
506 | 0 | } |
507 | | |
508 | | |
509 | | NTSTATUS kerberos_pac_blob_to_user_info_dc(TALLOC_CTX *mem_ctx, |
510 | | DATA_BLOB pac_blob, |
511 | | krb5_context context, |
512 | | struct auth_user_info_dc **user_info_dc, |
513 | | struct PAC_SIGNATURE_DATA *pac_srv_sig, |
514 | | struct PAC_SIGNATURE_DATA *pac_kdc_sig) |
515 | 0 | { |
516 | 0 | krb5_error_code ret; |
517 | 0 | krb5_pac pac; |
518 | 0 | ret = krb5_pac_parse(context, |
519 | 0 | pac_blob.data, pac_blob.length, |
520 | 0 | &pac); |
521 | 0 | if (ret) { |
522 | 0 | return map_nt_error_from_unix_common(ret); |
523 | 0 | } |
524 | | |
525 | | |
526 | 0 | ret = kerberos_pac_to_user_info_dc(mem_ctx, |
527 | 0 | pac, |
528 | 0 | context, |
529 | 0 | user_info_dc, |
530 | 0 | AUTH_INCLUDE_RESOURCE_GROUPS, |
531 | 0 | pac_srv_sig, |
532 | 0 | pac_kdc_sig, |
533 | 0 | NULL); |
534 | 0 | krb5_pac_free(context, pac); |
535 | 0 | if (ret) { |
536 | 0 | return map_nt_error_from_unix_common(ret); |
537 | 0 | } |
538 | 0 | return NT_STATUS_OK; |
539 | 0 | } |