2 Unix SMB/CIFS implementation.
4 Create and parse the krb5 PAC
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
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.
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.
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/>.
27 #include "system/kerberos.h"
28 #include "auth/auth.h"
29 #include "auth/kerberos/kerberos.h"
30 #include "librpc/gen_ndr/ndr_krb5pac.h"
32 #include "auth/auth_sam_reply.h"
33 #include "auth/kerberos/kerberos_util.h"
34 #include "auth/kerberos/pac_utils.h"
36 _PUBLIC_ NTSTATUS
kerberos_pac_logon_info(TALLOC_CTX
*mem_ctx
,
39 const krb5_keyblock
*krbtgt_keyblock
,
40 const krb5_keyblock
*service_keyblock
,
41 krb5_const_principal client_principal
,
43 struct PAC_LOGON_INFO
**logon_info
)
46 struct PAC_DATA
*pac_data
;
48 nt_status
= kerberos_decode_pac(mem_ctx
,
56 if (!NT_STATUS_IS_OK(nt_status
)) {
61 for (i
=0; i
< pac_data
->num_buffers
; i
++) {
62 if (pac_data
->buffers
[i
].type
!= PAC_TYPE_LOGON_INFO
) {
65 *logon_info
= pac_data
->buffers
[i
].info
->logon_info
.info
;
68 return NT_STATUS_INVALID_PARAMETER
;
73 static krb5_error_code
make_pac_checksum(TALLOC_CTX
*mem_ctx
,
75 struct PAC_SIGNATURE_DATA
*sig
,
77 const krb5_keyblock
*keyblock
)
84 ret
= krb5_crypto_init(context
,
89 DEBUG(0,("krb5_crypto_init() failed: %s\n",
90 smb_get_krb5_error_message(context
, ret
, mem_ctx
)));
93 ret
= krb5_create_checksum(context
,
101 DEBUG(2, ("PAC Verification failed: %s\n",
102 smb_get_krb5_error_message(context
, ret
, mem_ctx
)));
105 krb5_crypto_destroy(context
, crypto
);
111 sig
->type
= cksum
.cksumtype
;
112 sig
->signature
= data_blob_talloc(mem_ctx
, cksum
.checksum
.data
, cksum
.checksum
.length
);
113 free_Checksum(&cksum
);
118 krb5_error_code
kerberos_encode_pac(TALLOC_CTX
*mem_ctx
,
119 struct PAC_DATA
*pac_data
,
120 krb5_context context
,
121 const krb5_keyblock
*krbtgt_keyblock
,
122 const krb5_keyblock
*service_keyblock
,
127 enum ndr_err_code ndr_err
;
128 DATA_BLOB zero_blob
= data_blob(NULL
, 0);
129 DATA_BLOB tmp_blob
= data_blob(NULL
, 0);
130 struct PAC_SIGNATURE_DATA
*kdc_checksum
= NULL
;
131 struct PAC_SIGNATURE_DATA
*srv_checksum
= NULL
;
134 /* First, just get the keytypes filled in (and lengths right, eventually) */
135 for (i
=0; i
< pac_data
->num_buffers
; i
++) {
136 if (pac_data
->buffers
[i
].type
!= PAC_TYPE_KDC_CHECKSUM
) {
139 kdc_checksum
= &pac_data
->buffers
[i
].info
->kdc_cksum
,
140 ret
= make_pac_checksum(mem_ctx
, &zero_blob
,
142 context
, krbtgt_keyblock
);
144 DEBUG(2, ("making krbtgt PAC checksum failed: %s\n",
145 smb_get_krb5_error_message(context
, ret
, mem_ctx
)));
146 talloc_free(pac_data
);
151 for (i
=0; i
< pac_data
->num_buffers
; i
++) {
152 if (pac_data
->buffers
[i
].type
!= PAC_TYPE_SRV_CHECKSUM
) {
155 srv_checksum
= &pac_data
->buffers
[i
].info
->srv_cksum
;
156 ret
= make_pac_checksum(mem_ctx
, &zero_blob
,
158 context
, service_keyblock
);
160 DEBUG(2, ("making service PAC checksum failed: %s\n",
161 smb_get_krb5_error_message(context
, ret
, mem_ctx
)));
162 talloc_free(pac_data
);
168 DEBUG(2, ("Invalid PAC constructed for signing, no KDC checksum present!"));
172 DEBUG(2, ("Invalid PAC constructed for signing, no SRV checksum present!"));
176 /* But wipe out the actual signatures */
177 memset(kdc_checksum
->signature
.data
, '\0', kdc_checksum
->signature
.length
);
178 memset(srv_checksum
->signature
.data
, '\0', srv_checksum
->signature
.length
);
180 ndr_err
= ndr_push_struct_blob(&tmp_blob
, mem_ctx
,
182 (ndr_push_flags_fn_t
)ndr_push_PAC_DATA
);
183 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
184 nt_status
= ndr_map_error2ntstatus(ndr_err
);
185 DEBUG(1, ("PAC (presig) push failed: %s\n", nt_errstr(nt_status
)));
186 talloc_free(pac_data
);
190 /* Then sign the result of the previous push, where the sig was zero'ed out */
191 ret
= make_pac_checksum(mem_ctx
, &tmp_blob
, srv_checksum
,
192 context
, service_keyblock
);
194 /* Then sign Server checksum */
195 ret
= make_pac_checksum(mem_ctx
, &srv_checksum
->signature
, kdc_checksum
, context
, krbtgt_keyblock
);
197 DEBUG(2, ("making krbtgt PAC checksum failed: %s\n",
198 smb_get_krb5_error_message(context
, ret
, mem_ctx
)));
199 talloc_free(pac_data
);
203 /* And push it out again, this time to the world. This relies on determanistic pointer values */
204 ndr_err
= ndr_push_struct_blob(&tmp_blob
, mem_ctx
,
206 (ndr_push_flags_fn_t
)ndr_push_PAC_DATA
);
207 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
208 nt_status
= ndr_map_error2ntstatus(ndr_err
);
209 DEBUG(1, ("PAC (final) push failed: %s\n", nt_errstr(nt_status
)));
210 talloc_free(pac_data
);
220 krb5_error_code
kerberos_create_pac(TALLOC_CTX
*mem_ctx
,
221 struct auth_user_info_dc
*user_info_dc
,
222 krb5_context context
,
223 const krb5_keyblock
*krbtgt_keyblock
,
224 const krb5_keyblock
*service_keyblock
,
225 krb5_principal client_principal
,
231 struct PAC_DATA
*pac_data
= talloc(mem_ctx
, struct PAC_DATA
);
232 struct netr_SamInfo3
*sam3
;
233 union PAC_INFO
*u_LOGON_INFO
;
234 struct PAC_LOGON_INFO
*LOGON_INFO
;
235 union PAC_INFO
*u_LOGON_NAME
;
236 struct PAC_LOGON_NAME
*LOGON_NAME
;
237 union PAC_INFO
*u_KDC_CHECKSUM
;
238 union PAC_INFO
*u_SRV_CHECKSUM
;
243 PAC_BUF_LOGON_INFO
= 0,
244 PAC_BUF_LOGON_NAME
= 1,
245 PAC_BUF_SRV_CHECKSUM
= 2,
246 PAC_BUF_KDC_CHECKSUM
= 3,
247 PAC_BUF_NUM_BUFFERS
= 4
254 pac_data
->num_buffers
= PAC_BUF_NUM_BUFFERS
;
255 pac_data
->version
= 0;
257 pac_data
->buffers
= talloc_array(pac_data
,
259 pac_data
->num_buffers
);
260 if (!pac_data
->buffers
) {
261 talloc_free(pac_data
);
266 u_LOGON_INFO
= talloc_zero(pac_data
->buffers
, union PAC_INFO
);
268 talloc_free(pac_data
);
271 pac_data
->buffers
[PAC_BUF_LOGON_INFO
].type
= PAC_TYPE_LOGON_INFO
;
272 pac_data
->buffers
[PAC_BUF_LOGON_INFO
].info
= u_LOGON_INFO
;
275 u_LOGON_NAME
= talloc_zero(pac_data
->buffers
, union PAC_INFO
);
277 talloc_free(pac_data
);
280 pac_data
->buffers
[PAC_BUF_LOGON_NAME
].type
= PAC_TYPE_LOGON_NAME
;
281 pac_data
->buffers
[PAC_BUF_LOGON_NAME
].info
= u_LOGON_NAME
;
282 LOGON_NAME
= &u_LOGON_NAME
->logon_name
;
285 u_SRV_CHECKSUM
= talloc_zero(pac_data
->buffers
, union PAC_INFO
);
286 if (!u_SRV_CHECKSUM
) {
287 talloc_free(pac_data
);
290 pac_data
->buffers
[PAC_BUF_SRV_CHECKSUM
].type
= PAC_TYPE_SRV_CHECKSUM
;
291 pac_data
->buffers
[PAC_BUF_SRV_CHECKSUM
].info
= u_SRV_CHECKSUM
;
294 u_KDC_CHECKSUM
= talloc_zero(pac_data
->buffers
, union PAC_INFO
);
295 if (!u_KDC_CHECKSUM
) {
296 talloc_free(pac_data
);
299 pac_data
->buffers
[PAC_BUF_KDC_CHECKSUM
].type
= PAC_TYPE_KDC_CHECKSUM
;
300 pac_data
->buffers
[PAC_BUF_KDC_CHECKSUM
].info
= u_KDC_CHECKSUM
;
302 /* now the real work begins... */
304 LOGON_INFO
= talloc_zero(u_LOGON_INFO
, struct PAC_LOGON_INFO
);
306 talloc_free(pac_data
);
309 nt_status
= auth_convert_user_info_dc_saminfo3(LOGON_INFO
, user_info_dc
, &sam3
);
310 if (!NT_STATUS_IS_OK(nt_status
)) {
311 DEBUG(1, ("Getting Samba info failed: %s\n", nt_errstr(nt_status
)));
312 talloc_free(pac_data
);
316 u_LOGON_INFO
->logon_info
.info
= LOGON_INFO
;
317 LOGON_INFO
->info3
= *sam3
;
319 ret
= krb5_unparse_name_flags(context
, client_principal
,
320 KRB5_PRINCIPAL_UNPARSE_NO_REALM
, &name
);
324 LOGON_NAME
->account_name
= talloc_strdup(LOGON_NAME
, name
);
327 this logon_time field is absolutely critical. This is what
328 caused all our PAC troubles :-)
330 unix_to_nt_time(&LOGON_NAME
->logon_time
, tgs_authtime
);
332 ret
= kerberos_encode_pac(mem_ctx
,
338 talloc_free(pac_data
);
342 krb5_error_code
kerberos_pac_to_user_info_dc(TALLOC_CTX
*mem_ctx
,
344 krb5_context context
,
345 struct auth_user_info_dc
**user_info_dc
,
346 struct PAC_SIGNATURE_DATA
*pac_srv_sig
,
347 struct PAC_SIGNATURE_DATA
*pac_kdc_sig
)
350 enum ndr_err_code ndr_err
;
353 DATA_BLOB pac_logon_info_in
, pac_srv_checksum_in
, pac_kdc_checksum_in
;
354 krb5_data k5pac_logon_info_in
, k5pac_srv_checksum_in
, k5pac_kdc_checksum_in
;
357 struct auth_user_info_dc
*user_info_dc_out
;
359 TALLOC_CTX
*tmp_ctx
= talloc_new(mem_ctx
);
365 ret
= krb5_pac_get_buffer(context
, pac
, PAC_TYPE_LOGON_INFO
, &k5pac_logon_info_in
);
367 talloc_free(tmp_ctx
);
371 pac_logon_info_in
= data_blob_const(k5pac_logon_info_in
.data
, k5pac_logon_info_in
.length
);
373 ndr_err
= ndr_pull_union_blob(&pac_logon_info_in
, tmp_ctx
, &info
,
375 (ndr_pull_flags_fn_t
)ndr_pull_PAC_INFO
);
376 krb5_data_free(&k5pac_logon_info_in
);
377 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
) || !info
.logon_info
.info
) {
378 nt_status
= ndr_map_error2ntstatus(ndr_err
);
379 DEBUG(0,("can't parse the PAC LOGON_INFO: %s\n", nt_errstr(nt_status
)));
380 talloc_free(tmp_ctx
);
384 /* Pull this right into the normal auth sysstem structures */
385 nt_status
= make_user_info_dc_pac(mem_ctx
,
386 info
.logon_info
.info
,
388 if (!NT_STATUS_IS_OK(nt_status
)) {
389 talloc_free(tmp_ctx
);
394 ret
= krb5_pac_get_buffer(context
, pac
, PAC_TYPE_SRV_CHECKSUM
, &k5pac_srv_checksum_in
);
396 talloc_free(tmp_ctx
);
400 pac_srv_checksum_in
= data_blob_const(k5pac_srv_checksum_in
.data
, k5pac_srv_checksum_in
.length
);
402 ndr_err
= ndr_pull_struct_blob(&pac_srv_checksum_in
, pac_srv_sig
,
404 (ndr_pull_flags_fn_t
)ndr_pull_PAC_SIGNATURE_DATA
);
405 krb5_data_free(&k5pac_srv_checksum_in
);
406 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
407 nt_status
= ndr_map_error2ntstatus(ndr_err
);
408 DEBUG(0,("can't parse the KDC signature: %s\n",
409 nt_errstr(nt_status
)));
415 ret
= krb5_pac_get_buffer(context
, pac
, PAC_TYPE_KDC_CHECKSUM
, &k5pac_kdc_checksum_in
);
417 talloc_free(tmp_ctx
);
421 pac_kdc_checksum_in
= data_blob_const(k5pac_kdc_checksum_in
.data
, k5pac_kdc_checksum_in
.length
);
423 ndr_err
= ndr_pull_struct_blob(&pac_kdc_checksum_in
, pac_kdc_sig
,
425 (ndr_pull_flags_fn_t
)ndr_pull_PAC_SIGNATURE_DATA
);
426 krb5_data_free(&k5pac_kdc_checksum_in
);
427 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
428 nt_status
= ndr_map_error2ntstatus(ndr_err
);
429 DEBUG(0,("can't parse the KDC signature: %s\n",
430 nt_errstr(nt_status
)));
434 *user_info_dc
= user_info_dc_out
;
440 NTSTATUS
kerberos_pac_blob_to_user_info_dc(TALLOC_CTX
*mem_ctx
,
442 krb5_context context
,
443 struct auth_user_info_dc
**user_info_dc
,
444 struct PAC_SIGNATURE_DATA
*pac_srv_sig
,
445 struct PAC_SIGNATURE_DATA
*pac_kdc_sig
)
449 ret
= krb5_pac_parse(context
,
450 pac_blob
.data
, pac_blob
.length
,
453 return map_nt_error_from_unix_common(ret
);
457 ret
= kerberos_pac_to_user_info_dc(mem_ctx
, pac
, context
, user_info_dc
, pac_srv_sig
, pac_kdc_sig
);
458 krb5_pac_free(context
, pac
);
460 return map_nt_error_from_unix_common(ret
);