2 Unix SMB/CIFS implementation.
4 PAC Glue between Samba and the KDC
6 Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005-2009
7 Copyright (C) Simo Sorce <idra@samba.org> 2010
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 3 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
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/>.
25 #include "../libds/common/flags.h"
27 #include "auth/auth.h"
28 #include "auth/auth_sam_reply.h"
29 #include "kdc/kdc-glue.h"
30 #include "kdc/pac-glue.h"
31 #include "param/param.h"
32 #include "librpc/gen_ndr/ndr_krb5pac.h"
35 NTSTATUS
samba_get_logon_info_pac_blob(TALLOC_CTX
*mem_ctx
,
36 struct auth_user_info_dc
*info
,
39 struct netr_SamInfo3
*info3
;
40 union PAC_INFO pac_info
;
41 enum ndr_err_code ndr_err
;
44 ZERO_STRUCT(pac_info
);
46 nt_status
= auth_convert_user_info_dc_saminfo3(mem_ctx
, info
, &info3
);
47 if (!NT_STATUS_IS_OK(nt_status
)) {
48 DEBUG(1, ("Getting Samba info failed: %s\n",
49 nt_errstr(nt_status
)));
53 pac_info
.logon_info
.info
= talloc_zero(mem_ctx
, struct PAC_LOGON_INFO
);
54 if (!pac_info
.logon_info
.info
) {
55 return NT_STATUS_NO_MEMORY
;
58 pac_info
.logon_info
.info
->info3
= *info3
;
60 ndr_err
= ndr_push_union_blob(pac_data
, mem_ctx
, &pac_info
,
62 (ndr_push_flags_fn_t
)ndr_push_PAC_INFO
);
63 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
64 nt_status
= ndr_map_error2ntstatus(ndr_err
);
65 DEBUG(1, ("PAC (presig) push failed: %s\n",
66 nt_errstr(nt_status
)));
73 krb5_error_code
samba_make_krb5_pac(krb5_context context
,
75 DATA_BLOB
*deleg_blob
,
82 /* The user account may be set not to want the PAC */
87 ret
= krb5_data_copy(&pac_data
, pac_blob
->data
, pac_blob
->length
);
92 ZERO_STRUCT(deleg_data
);
94 ret
= krb5_data_copy(&deleg_data
,
98 krb5_data_free(&pac_data
);
103 ret
= krb5_pac_init(context
, pac
);
105 krb5_data_free(&pac_data
);
106 krb5_data_free(&deleg_data
);
110 ret
= krb5_pac_add_buffer(context
, *pac
, PAC_TYPE_LOGON_INFO
, &pac_data
);
111 krb5_data_free(&pac_data
);
113 krb5_data_free(&deleg_data
);
118 ret
= krb5_pac_add_buffer(context
, *pac
,
119 PAC_TYPE_CONSTRAINED_DELEGATION
,
121 krb5_data_free(&deleg_data
);
130 bool samba_princ_needs_pac(struct hdb_entry_ex
*princ
)
133 struct samba_kdc_entry
*p
= talloc_get_type(princ
->ctx
, struct samba_kdc_entry
);
134 uint32_t userAccountControl
;
137 /* The service account may be set not to want the PAC */
138 userAccountControl
= ldb_msg_find_attr_as_uint(p
->msg
, "userAccountControl", 0);
139 if (userAccountControl
& UF_NO_AUTH_DATA_REQUIRED
) {
146 /* Was the krbtgt an RODC (and we are not) */
147 bool samba_krbtgt_was_untrusted_rodc(struct hdb_entry_ex
*princ
)
150 struct samba_kdc_entry
*p
= talloc_get_type(princ
->ctx
, struct samba_kdc_entry
);
151 int rodc_krbtgt_number
;
153 /* Determine if this was printed by an RODC */
154 rodc_krbtgt_number
= ldb_msg_find_attr_as_int(p
->msg
, "msDS-SecondaryKrbTgtNumber", -1);
155 if (rodc_krbtgt_number
== -1) {
157 } else if (rodc_krbtgt_number
!= p
->kdc_db_ctx
->my_krbtgt_number
) {
164 NTSTATUS
samba_kdc_get_pac_blob(TALLOC_CTX
*mem_ctx
,
165 struct hdb_entry_ex
*client
,
166 DATA_BLOB
**_pac_blob
)
168 struct samba_kdc_entry
*p
= talloc_get_type(client
->ctx
, struct samba_kdc_entry
);
169 struct auth_user_info_dc
*user_info_dc
;
173 /* The user account may be set not to want the PAC */
174 if ( ! samba_princ_needs_pac(client
)) {
179 pac_blob
= talloc_zero(mem_ctx
, DATA_BLOB
);
181 return NT_STATUS_NO_MEMORY
;
184 nt_status
= authsam_make_user_info_dc(mem_ctx
, p
->kdc_db_ctx
->samdb
,
185 lpcfg_netbios_name(p
->kdc_db_ctx
->lp_ctx
),
186 lpcfg_sam_name(p
->kdc_db_ctx
->lp_ctx
),
192 if (!NT_STATUS_IS_OK(nt_status
)) {
193 DEBUG(0, ("Getting user info for PAC failed: %s\n",
194 nt_errstr(nt_status
)));
198 nt_status
= samba_get_logon_info_pac_blob(mem_ctx
, user_info_dc
, pac_blob
);
199 if (!NT_STATUS_IS_OK(nt_status
)) {
200 DEBUG(0, ("Building PAC failed: %s\n",
201 nt_errstr(nt_status
)));
205 *_pac_blob
= pac_blob
;
209 NTSTATUS
samba_kdc_update_pac_blob(TALLOC_CTX
*mem_ctx
,
210 krb5_context context
,
211 const krb5_pac pac
, DATA_BLOB
*pac_blob
)
213 struct auth_user_info_dc
*user_info_dc
;
217 ret
= kerberos_pac_to_user_info_dc(mem_ctx
, pac
,
218 context
, &user_info_dc
, NULL
, NULL
);
220 return NT_STATUS_UNSUCCESSFUL
;
223 nt_status
= samba_get_logon_info_pac_blob(mem_ctx
,
224 user_info_dc
, pac_blob
);
229 NTSTATUS
samba_kdc_update_delegation_info_blob(TALLOC_CTX
*mem_ctx
,
230 krb5_context context
,
232 const krb5_principal server_principal
,
233 const krb5_principal proxy_principal
,
240 enum ndr_err_code ndr_err
;
242 struct PAC_CONSTRAINED_DELEGATION _d
;
243 struct PAC_CONSTRAINED_DELEGATION
*d
= NULL
;
247 TALLOC_CTX
*tmp_ctx
= talloc_new(mem_ctx
);
249 if (tmp_ctx
== NULL
) {
250 return NT_STATUS_NO_MEMORY
;
253 ret
= krb5_pac_get_buffer(context
, pac
, PAC_TYPE_CONSTRAINED_DELEGATION
, &old_data
);
255 ZERO_STRUCT(old_data
);
257 talloc_free(tmp_ctx
);
258 return NT_STATUS_UNSUCCESSFUL
;
261 old_blob
.length
= old_data
.length
;
262 old_blob
.data
= (uint8_t *)old_data
.data
;
265 if (old_blob
.length
> 0) {
266 ndr_err
= ndr_pull_union_blob(&old_blob
, mem_ctx
,
267 &info
, PAC_TYPE_CONSTRAINED_DELEGATION
,
268 (ndr_pull_flags_fn_t
)ndr_pull_PAC_INFO
);
269 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
270 krb5_data_free(&old_data
);
271 nt_status
= ndr_map_error2ntstatus(ndr_err
);
272 DEBUG(0,("can't parse the PAC LOGON_INFO: %s\n", nt_errstr(nt_status
)));
273 talloc_free(tmp_ctx
);
278 info
.constrained_delegation
.info
= &_d
;
280 krb5_data_free(&old_data
);
282 ret
= krb5_unparse_name(context
, server_principal
, &server
);
284 talloc_free(tmp_ctx
);
285 return NT_STATUS_INTERNAL_ERROR
;
288 ret
= krb5_unparse_name_flags(context
, proxy_principal
,
289 KRB5_PRINCIPAL_UNPARSE_NO_REALM
, &proxy
);
292 talloc_free(tmp_ctx
);
293 return NT_STATUS_INTERNAL_ERROR
;
296 d
= info
.constrained_delegation
.info
;
297 i
= d
->num_transited_services
;
298 d
->proxy_target
.string
= server
;
299 d
->transited_services
= talloc_realloc(mem_ctx
, d
->transited_services
,
300 struct lsa_String
, i
+ 1);
301 d
->transited_services
[i
].string
= proxy
;
302 d
->num_transited_services
= i
+ 1;
304 ndr_err
= ndr_push_union_blob(new_blob
, mem_ctx
,
305 &info
, PAC_TYPE_CONSTRAINED_DELEGATION
,
306 (ndr_push_flags_fn_t
)ndr_push_PAC_INFO
);
309 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
310 krb5_data_free(&old_data
);
311 nt_status
= ndr_map_error2ntstatus(ndr_err
);
312 DEBUG(0,("can't parse the PAC LOGON_INFO: %s\n", nt_errstr(nt_status
)));
313 talloc_free(tmp_ctx
);
317 talloc_free(tmp_ctx
);
321 /* this function allocates 'data' using malloc.
322 * The caller is responsible for freeing it */
323 void samba_kdc_build_edata_reply(NTSTATUS nt_status
, DATA_BLOB
*e_data
)
328 krb5_error_code ret
= 0;
333 pa
.padata_type
= KRB5_PADATA_PW_SALT
;
334 pa
.padata_value
.length
= 12;
335 pa
.padata_value
.data
= malloc(pa
.padata_value
.length
);
336 if (!pa
.padata_value
.data
) {
342 SIVAL(pa
.padata_value
.data
, 0, NT_STATUS_V(nt_status
));
343 SIVAL(pa
.padata_value
.data
, 4, 0);
344 SIVAL(pa
.padata_value
.data
, 8, 1);
346 ASN1_MALLOC_ENCODE(PA_DATA
, buf
, len
, &pa
, &len
, ret
);
347 free(pa
.padata_value
.data
);
350 e_data
->length
= len
;
355 /* function to map policy errors */
356 krb5_error_code
samba_kdc_map_policy_err(NTSTATUS nt_status
)
360 if (NT_STATUS_EQUAL(nt_status
, NT_STATUS_PASSWORD_MUST_CHANGE
))
361 ret
= KRB5KDC_ERR_KEY_EXPIRED
;
362 else if (NT_STATUS_EQUAL(nt_status
, NT_STATUS_PASSWORD_EXPIRED
))
363 ret
= KRB5KDC_ERR_KEY_EXPIRED
;
364 else if (NT_STATUS_EQUAL(nt_status
, NT_STATUS_ACCOUNT_EXPIRED
))
365 ret
= KRB5KDC_ERR_CLIENT_REVOKED
;
366 else if (NT_STATUS_EQUAL(nt_status
, NT_STATUS_ACCOUNT_DISABLED
))
367 ret
= KRB5KDC_ERR_CLIENT_REVOKED
;
368 else if (NT_STATUS_EQUAL(nt_status
, NT_STATUS_INVALID_LOGON_HOURS
))
369 ret
= KRB5KDC_ERR_CLIENT_REVOKED
;
370 else if (NT_STATUS_EQUAL(nt_status
, NT_STATUS_ACCOUNT_LOCKED_OUT
))
371 ret
= KRB5KDC_ERR_CLIENT_REVOKED
;
372 else if (NT_STATUS_EQUAL(nt_status
, NT_STATUS_INVALID_WORKSTATION
))
373 ret
= KRB5KDC_ERR_POLICY
;
375 ret
= KRB5KDC_ERR_POLICY
;
380 /* Given a kdc entry, consult the account_ok routine in auth/auth_sam.c
382 NTSTATUS
samba_kdc_check_client_access(struct samba_kdc_entry
*kdc_entry
,
383 const char *client_name
,
384 const char *workstation
,
385 bool password_change
)
390 tmp_ctx
= talloc_named(NULL
, 0, "samba_kdc_check_client_access");
392 return NT_STATUS_NO_MEMORY
;
395 /* we allow all kinds of trusts here */
396 nt_status
= authsam_account_ok(tmp_ctx
,
397 kdc_entry
->kdc_db_ctx
->samdb
,
398 MSV1_0_ALLOW_SERVER_TRUST_ACCOUNT
|
399 MSV1_0_ALLOW_WORKSTATION_TRUST_ACCOUNT
,
400 kdc_entry
->realm_dn
, kdc_entry
->msg
,
401 workstation
, client_name
,
402 true, password_change
);
404 talloc_free(tmp_ctx
);