06019e579ebbc555ec92097f19863cd93a6804b7
[samba.git] / source4 / kdc / pac-glue.c
1 /*
2    Unix SMB/CIFS implementation.
3
4    PAC Glue between Samba and the KDC
5
6    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005-2009
7    Copyright (C) Simo Sorce <idra@samba.org> 2010
8
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.
13
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.
18
19
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/>.
22 */
23
24 #include "includes.h"
25 #include "../libds/common/flags.h"
26 #include <ldb.h>
27 #include "auth/auth.h"
28 #include "auth/auth_sam_reply.h"
29 #include "system/kerberos.h"
30 #include "auth/kerberos/kerberos.h"
31 #include "kdc/samba_kdc.h"
32 #include "kdc/pac-glue.h"
33 #include "param/param.h"
34 #include "librpc/gen_ndr/ndr_krb5pac.h"
35 #include "libcli/security/security.h"
36 #include "dsdb/samdb/samdb.h"
37 #include "auth/kerberos/pac_utils.h"
38
39 static
40 NTSTATUS samba_get_logon_info_pac_blob(TALLOC_CTX *mem_ctx,
41                                        const struct auth_user_info_dc *info,
42                                        DATA_BLOB *pac_data)
43 {
44         struct netr_SamInfo3 *info3;
45         union PAC_INFO pac_info;
46         enum ndr_err_code ndr_err;
47         NTSTATUS nt_status;
48
49         ZERO_STRUCT(pac_info);
50
51         *pac_data = data_blob_null;
52
53         nt_status = auth_convert_user_info_dc_saminfo3(mem_ctx, info, &info3);
54         if (!NT_STATUS_IS_OK(nt_status)) {
55                 DEBUG(1, ("Getting Samba info failed: %s\n",
56                           nt_errstr(nt_status)));
57                 return nt_status;
58         }
59
60         pac_info.logon_info.info = talloc_zero(mem_ctx, struct PAC_LOGON_INFO);
61         if (!pac_info.logon_info.info) {
62                 return NT_STATUS_NO_MEMORY;
63         }
64
65         pac_info.logon_info.info->info3 = *info3;
66
67         ndr_err = ndr_push_union_blob(pac_data, mem_ctx, &pac_info,
68                                       PAC_TYPE_LOGON_INFO,
69                                       (ndr_push_flags_fn_t)ndr_push_PAC_INFO);
70         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
71                 nt_status = ndr_map_error2ntstatus(ndr_err);
72                 DEBUG(1, ("PAC_LOGON_INFO (presig) push failed: %s\n",
73                           nt_errstr(nt_status)));
74                 return nt_status;
75         }
76
77         return NT_STATUS_OK;
78 }
79
80 static
81 NTSTATUS samba_get_upn_info_pac_blob(TALLOC_CTX *mem_ctx,
82                                      const struct auth_user_info_dc *info,
83                                      DATA_BLOB *upn_data)
84 {
85         union PAC_INFO pac_upn;
86         enum ndr_err_code ndr_err;
87         NTSTATUS nt_status;
88
89         ZERO_STRUCT(pac_upn);
90
91         *upn_data = data_blob_null;
92
93         pac_upn.upn_dns_info.upn_name = info->info->user_principal_name;
94         pac_upn.upn_dns_info.dns_domain_name = strupper_talloc(mem_ctx,
95                                                 info->info->dns_domain_name);
96         if (pac_upn.upn_dns_info.dns_domain_name == NULL) {
97                 return NT_STATUS_NO_MEMORY;
98         }
99         if (info->info->user_principal_constructed) {
100                 pac_upn.upn_dns_info.flags |= PAC_UPN_DNS_FLAG_CONSTRUCTED;
101         }
102
103         ndr_err = ndr_push_union_blob(upn_data, mem_ctx, &pac_upn,
104                                       PAC_TYPE_UPN_DNS_INFO,
105                                       (ndr_push_flags_fn_t)ndr_push_PAC_INFO);
106         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
107                 nt_status = ndr_map_error2ntstatus(ndr_err);
108                 DEBUG(1, ("PAC UPN_DNS_INFO (presig) push failed: %s\n",
109                           nt_errstr(nt_status)));
110                 return nt_status;
111         }
112
113         return NT_STATUS_OK;
114 }
115
116 static
117 NTSTATUS samba_get_pac_attrs_blob(TALLOC_CTX *mem_ctx,
118                                   const krb5_boolean *pac_request,
119                                   DATA_BLOB *pac_attrs_data)
120 {
121         union PAC_INFO pac_attrs;
122         enum ndr_err_code ndr_err;
123         NTSTATUS nt_status;
124
125         ZERO_STRUCT(pac_attrs);
126
127         *pac_attrs_data = data_blob_null;
128
129         /* Set the length of the flags in bits. */
130         pac_attrs.attributes_info.flags_length = 2;
131
132         if (pac_request == NULL) {
133                 pac_attrs.attributes_info.flags
134                         |= PAC_ATTRIBUTE_FLAG_PAC_WAS_GIVEN_IMPLICITLY;
135         } else if (*pac_request) {
136                 pac_attrs.attributes_info.flags
137                         |= PAC_ATTRIBUTE_FLAG_PAC_WAS_REQUESTED;
138         }
139
140         ndr_err = ndr_push_union_blob(pac_attrs_data, mem_ctx, &pac_attrs,
141                                       PAC_TYPE_ATTRIBUTES_INFO,
142                                       (ndr_push_flags_fn_t)ndr_push_PAC_INFO);
143         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
144                 nt_status = ndr_map_error2ntstatus(ndr_err);
145                 DEBUG(1, ("PAC ATTRIBUTES_INFO (presig) push failed: %s\n",
146                           nt_errstr(nt_status)));
147                 return nt_status;
148         }
149
150         return NT_STATUS_OK;
151 }
152
153 static
154 NTSTATUS samba_get_cred_info_ndr_blob(TALLOC_CTX *mem_ctx,
155                                       const struct ldb_message *msg,
156                                       DATA_BLOB *cred_blob)
157 {
158         enum ndr_err_code ndr_err;
159         NTSTATUS nt_status;
160         struct samr_Password *lm_hash = NULL;
161         struct samr_Password *nt_hash = NULL;
162         struct PAC_CREDENTIAL_NTLM_SECPKG ntlm_secpkg = {
163                 .version = 0,
164         };
165         DATA_BLOB ntlm_blob = data_blob_null;
166         struct PAC_CREDENTIAL_SUPPLEMENTAL_SECPKG secpkgs[1] = {{
167                 .credential_size = 0,
168         }};
169         struct PAC_CREDENTIAL_DATA cred_data = {
170                 .credential_count = 0,
171         };
172         struct PAC_CREDENTIAL_DATA_NDR cred_ndr;
173
174         ZERO_STRUCT(cred_ndr);
175
176         *cred_blob = data_blob_null;
177
178         lm_hash = samdb_result_hash(mem_ctx, msg, "dBCSPwd");
179         if (lm_hash != NULL) {
180                 bool zero = all_zero(lm_hash->hash, 16);
181                 if (zero) {
182                         lm_hash = NULL;
183                 }
184         }
185         if (lm_hash != NULL) {
186                 DEBUG(5, ("Passing LM password hash through credentials set\n"));
187                 ntlm_secpkg.flags |= PAC_CREDENTIAL_NTLM_HAS_LM_HASH;
188                 ntlm_secpkg.lm_password = *lm_hash;
189                 ZERO_STRUCTP(lm_hash);
190                 TALLOC_FREE(lm_hash);
191         }
192
193         nt_hash = samdb_result_hash(mem_ctx, msg, "unicodePwd");
194         if (nt_hash != NULL) {
195                 bool zero = all_zero(nt_hash->hash, 16);
196                 if (zero) {
197                         nt_hash = NULL;
198                 }
199         }
200         if (nt_hash != NULL) {
201                 DEBUG(5, ("Passing LM password hash through credentials set\n"));
202                 ntlm_secpkg.flags |= PAC_CREDENTIAL_NTLM_HAS_NT_HASH;
203                 ntlm_secpkg.nt_password = *nt_hash;
204                 ZERO_STRUCTP(nt_hash);
205                 TALLOC_FREE(nt_hash);
206         }
207
208         if (ntlm_secpkg.flags == 0) {
209                 return NT_STATUS_OK;
210         }
211
212 #ifdef DEBUG_PASSWORD
213         if (DEBUGLVL(11)) {
214                 NDR_PRINT_DEBUG(PAC_CREDENTIAL_NTLM_SECPKG, &ntlm_secpkg);
215         }
216 #endif
217
218         ndr_err = ndr_push_struct_blob(&ntlm_blob, mem_ctx, &ntlm_secpkg,
219                         (ndr_push_flags_fn_t)ndr_push_PAC_CREDENTIAL_NTLM_SECPKG);
220         ZERO_STRUCT(ntlm_secpkg);
221         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
222                 nt_status = ndr_map_error2ntstatus(ndr_err);
223                 DEBUG(1, ("PAC_CREDENTIAL_NTLM_SECPKG (presig) push failed: %s\n",
224                           nt_errstr(nt_status)));
225                 return nt_status;
226         }
227
228         DEBUG(10, ("NTLM credential BLOB (len %zu) for user\n",
229                   ntlm_blob.length));
230         dump_data_pw("PAC_CREDENTIAL_NTLM_SECPKG",
231                      ntlm_blob.data, ntlm_blob.length);
232
233         secpkgs[0].package_name.string = discard_const_p(char, "NTLM");
234         secpkgs[0].credential_size = ntlm_blob.length;
235         secpkgs[0].credential = ntlm_blob.data;
236
237         cred_data.credential_count = ARRAY_SIZE(secpkgs);
238         cred_data.credentials = secpkgs;
239
240 #ifdef DEBUG_PASSWORD
241         if (DEBUGLVL(11)) {
242                 NDR_PRINT_DEBUG(PAC_CREDENTIAL_DATA, &cred_data);
243         }
244 #endif
245
246         cred_ndr.ctr.data = &cred_data;
247
248 #ifdef DEBUG_PASSWORD
249         if (DEBUGLVL(11)) {
250                 NDR_PRINT_DEBUG(PAC_CREDENTIAL_DATA_NDR, &cred_ndr);
251         }
252 #endif
253
254         ndr_err = ndr_push_struct_blob(cred_blob, mem_ctx, &cred_ndr,
255                         (ndr_push_flags_fn_t)ndr_push_PAC_CREDENTIAL_DATA_NDR);
256         data_blob_clear(&ntlm_blob);
257         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
258                 nt_status = ndr_map_error2ntstatus(ndr_err);
259                 DEBUG(1, ("PAC_CREDENTIAL_DATA_NDR (presig) push failed: %s\n",
260                           nt_errstr(nt_status)));
261                 return nt_status;
262         }
263
264         DEBUG(10, ("Created credential BLOB (len %zu) for user\n",
265                   cred_blob->length));
266         dump_data_pw("PAC_CREDENTIAL_DATA_NDR",
267                      cred_blob->data, cred_blob->length);
268
269         return NT_STATUS_OK;
270 }
271
272 #ifdef SAMBA4_USES_HEIMDAL
273 krb5_error_code samba_kdc_encrypt_pac_credentials(krb5_context context,
274                                                   const krb5_keyblock *pkreplykey,
275                                                   const DATA_BLOB *cred_ndr_blob,
276                                                   TALLOC_CTX *mem_ctx,
277                                                   DATA_BLOB *cred_info_blob)
278 {
279         krb5_crypto cred_crypto;
280         krb5_enctype cred_enctype;
281         krb5_data cred_ndr_crypt;
282         struct PAC_CREDENTIAL_INFO pac_cred_info = { .version = 0, };
283         krb5_error_code ret;
284         const char *krb5err;
285         enum ndr_err_code ndr_err;
286         NTSTATUS nt_status;
287
288         *cred_info_blob = data_blob_null;
289
290         ret = krb5_crypto_init(context, pkreplykey, ETYPE_NULL,
291                                &cred_crypto);
292         if (ret != 0) {
293                 krb5err = krb5_get_error_message(context, ret);
294                 DEBUG(1, ("Failed initializing cred data crypto: %s\n", krb5err));
295                 krb5_free_error_message(context, krb5err);
296                 return ret;
297         }
298
299         ret = krb5_crypto_getenctype(context, cred_crypto, &cred_enctype);
300         if (ret != 0) {
301                 DEBUG(1, ("Failed getting crypto type for key\n"));
302                 krb5_crypto_destroy(context, cred_crypto);
303                 return ret;
304         }
305
306         DEBUG(10, ("Plain cred_ndr_blob (len %zu)\n",
307                   cred_ndr_blob->length));
308         dump_data_pw("PAC_CREDENTIAL_DATA_NDR",
309                      cred_ndr_blob->data, cred_ndr_blob->length);
310
311         ret = krb5_encrypt(context, cred_crypto,
312                            KRB5_KU_OTHER_ENCRYPTED,
313                            cred_ndr_blob->data, cred_ndr_blob->length,
314                            &cred_ndr_crypt);
315         krb5_crypto_destroy(context, cred_crypto);
316         if (ret != 0) {
317                 krb5err = krb5_get_error_message(context, ret);
318                 DEBUG(1, ("Failed crypt of cred data: %s\n", krb5err));
319                 krb5_free_error_message(context, krb5err);
320                 return ret;
321         }
322
323         pac_cred_info.encryption_type = cred_enctype;
324         pac_cred_info.encrypted_data.length = cred_ndr_crypt.length;
325         pac_cred_info.encrypted_data.data = (uint8_t *)cred_ndr_crypt.data;
326
327         if (DEBUGLVL(10)) {
328                 NDR_PRINT_DEBUG(PAC_CREDENTIAL_INFO, &pac_cred_info);
329         }
330
331         ndr_err = ndr_push_struct_blob(cred_info_blob, mem_ctx, &pac_cred_info,
332                         (ndr_push_flags_fn_t)ndr_push_PAC_CREDENTIAL_INFO);
333         krb5_data_free(&cred_ndr_crypt);
334         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
335                 nt_status = ndr_map_error2ntstatus(ndr_err);
336                 DEBUG(1, ("PAC_CREDENTIAL_INFO (presig) push failed: %s\n",
337                           nt_errstr(nt_status)));
338                 return KRB5KDC_ERR_SVC_UNAVAILABLE;
339         }
340
341         DEBUG(10, ("Encrypted credential BLOB (len %zu) with alg %d\n",
342                   cred_info_blob->length, (int)pac_cred_info.encryption_type));
343         dump_data_pw("PAC_CREDENTIAL_INFO",
344                       cred_info_blob->data, cred_info_blob->length);
345
346         return 0;
347 }
348 #else /* SAMBA4_USES_HEIMDAL */
349 krb5_error_code samba_kdc_encrypt_pac_credentials(krb5_context context,
350                                                   const krb5_keyblock *pkreplykey,
351                                                   const DATA_BLOB *cred_ndr_blob,
352                                                   TALLOC_CTX *mem_ctx,
353                                                   DATA_BLOB *cred_info_blob)
354 {
355         krb5_key cred_key;
356         krb5_enctype cred_enctype;
357         struct PAC_CREDENTIAL_INFO pac_cred_info = { .version = 0, };
358         krb5_error_code code;
359         const char *krb5err;
360         enum ndr_err_code ndr_err;
361         NTSTATUS nt_status;
362         krb5_data cred_ndr_data;
363         krb5_enc_data cred_ndr_crypt;
364         size_t enc_len = 0;
365
366         *cred_info_blob = data_blob_null;
367
368         code = krb5_k_create_key(context,
369                                  pkreplykey,
370                                  &cred_key);
371         if (code != 0) {
372                 krb5err = krb5_get_error_message(context, code);
373                 DEBUG(1, ("Failed initializing cred data crypto: %s\n", krb5err));
374                 krb5_free_error_message(context, krb5err);
375                 return code;
376         }
377
378         cred_enctype = krb5_k_key_enctype(context, cred_key);
379
380         DEBUG(10, ("Plain cred_ndr_blob (len %zu)\n",
381                   cred_ndr_blob->length));
382         dump_data_pw("PAC_CREDENTIAL_DATA_NDR",
383                      cred_ndr_blob->data, cred_ndr_blob->length);
384
385         pac_cred_info.encryption_type = cred_enctype;
386
387         cred_ndr_data.magic = 0;
388         cred_ndr_data.data = (char *)cred_ndr_blob->data;
389         cred_ndr_data.length = cred_ndr_blob->length;
390
391         code = krb5_c_encrypt_length(context,
392                                      cred_enctype,
393                                      cred_ndr_data.length,
394                                      &enc_len);
395         if (code != 0) {
396                 krb5err = krb5_get_error_message(context, code);
397                 DEBUG(1, ("Failed initializing cred data crypto: %s\n", krb5err));
398                 krb5_free_error_message(context, krb5err);
399                 return code;
400         }
401
402         pac_cred_info.encrypted_data = data_blob_talloc_zero(mem_ctx, enc_len);
403         if (pac_cred_info.encrypted_data.data == NULL) {
404                 DBG_ERR("Out of memory\n");
405                 return ENOMEM;
406         }
407
408         cred_ndr_crypt.ciphertext.length = enc_len;
409         cred_ndr_crypt.ciphertext.data = (char *)pac_cred_info.encrypted_data.data;
410
411         code = krb5_k_encrypt(context,
412                               cred_key,
413                               KRB5_KU_OTHER_ENCRYPTED,
414                               NULL,
415                               &cred_ndr_data,
416                               &cred_ndr_crypt);
417         krb5_k_free_key(context, cred_key);
418         if (code != 0) {
419                 krb5err = krb5_get_error_message(context, code);
420                 DEBUG(1, ("Failed crypt of cred data: %s\n", krb5err));
421                 krb5_free_error_message(context, krb5err);
422                 return code;
423         }
424
425         if (DEBUGLVL(10)) {
426                 NDR_PRINT_DEBUG(PAC_CREDENTIAL_INFO, &pac_cred_info);
427         }
428
429         ndr_err = ndr_push_struct_blob(cred_info_blob, mem_ctx, &pac_cred_info,
430                         (ndr_push_flags_fn_t)ndr_push_PAC_CREDENTIAL_INFO);
431         TALLOC_FREE(pac_cred_info.encrypted_data.data);
432         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
433                 nt_status = ndr_map_error2ntstatus(ndr_err);
434                 DEBUG(1, ("PAC_CREDENTIAL_INFO (presig) push failed: %s\n",
435                           nt_errstr(nt_status)));
436                 return KRB5KDC_ERR_SVC_UNAVAILABLE;
437         }
438
439         DEBUG(10, ("Encrypted credential BLOB (len %zu) with alg %d\n",
440                   cred_info_blob->length, (int)pac_cred_info.encryption_type));
441         dump_data_pw("PAC_CREDENTIAL_INFO",
442                       cred_info_blob->data, cred_info_blob->length);
443
444         return 0;
445 }
446 #endif /* SAMBA4_USES_HEIMDAL */
447
448
449 krb5_error_code samba_make_krb5_pac(krb5_context context,
450                                     const DATA_BLOB *logon_blob,
451                                     const DATA_BLOB *cred_blob,
452                                     const DATA_BLOB *upn_blob,
453                                     const DATA_BLOB *pac_attrs_blob,
454                                     const DATA_BLOB *deleg_blob,
455                                     krb5_pac *pac)
456 {
457         krb5_data logon_data;
458         krb5_data cred_data;
459         krb5_data upn_data;
460         krb5_data pac_attrs_data;
461         krb5_data deleg_data;
462         krb5_error_code ret;
463 #ifdef SAMBA4_USES_HEIMDAL
464         krb5_data null_data = {
465                 .length = 0,
466                 .data = NULL,
467         };
468 #endif
469
470         /* The user account may be set not to want the PAC */
471         if (logon_blob == NULL) {
472                 return 0;
473         }
474
475         ret = smb_krb5_copy_data_contents(&logon_data,
476                                           logon_blob->data,
477                                           logon_blob->length);
478         if (ret != 0) {
479                 return ret;
480         }
481
482         ZERO_STRUCT(cred_data);
483         if (cred_blob != NULL) {
484                 ret = smb_krb5_copy_data_contents(&cred_data,
485                                                   cred_blob->data,
486                                                   cred_blob->length);
487                 if (ret != 0) {
488                         smb_krb5_free_data_contents(context, &logon_data);
489                         return ret;
490                 }
491         }
492
493         ZERO_STRUCT(upn_data);
494         if (upn_blob != NULL) {
495                 ret = smb_krb5_copy_data_contents(&upn_data,
496                                                   upn_blob->data,
497                                                   upn_blob->length);
498                 if (ret != 0) {
499                         smb_krb5_free_data_contents(context, &logon_data);
500                         smb_krb5_free_data_contents(context, &cred_data);
501                         return ret;
502                 }
503         }
504
505         ZERO_STRUCT(pac_attrs_data);
506         if (pac_attrs_blob != NULL) {
507                 ret = smb_krb5_copy_data_contents(&pac_attrs_data,
508                                                   pac_attrs_blob->data,
509                                                   pac_attrs_blob->length);
510                 if (ret != 0) {
511                         smb_krb5_free_data_contents(context, &logon_data);
512                         smb_krb5_free_data_contents(context, &cred_data);
513                         smb_krb5_free_data_contents(context, &upn_data);
514                         return ret;
515                 }
516         }
517
518         ZERO_STRUCT(deleg_data);
519         if (deleg_blob != NULL) {
520                 ret = smb_krb5_copy_data_contents(&deleg_data,
521                                                   deleg_blob->data,
522                                                   deleg_blob->length);
523                 if (ret != 0) {
524                         smb_krb5_free_data_contents(context, &logon_data);
525                         smb_krb5_free_data_contents(context, &cred_data);
526                         smb_krb5_free_data_contents(context, &upn_data);
527                         smb_krb5_free_data_contents(context, &pac_attrs_data);
528                         return ret;
529                 }
530         }
531
532         ret = krb5_pac_init(context, pac);
533         if (ret != 0) {
534                 smb_krb5_free_data_contents(context, &logon_data);
535                 smb_krb5_free_data_contents(context, &cred_data);
536                 smb_krb5_free_data_contents(context, &upn_data);
537                 smb_krb5_free_data_contents(context, &pac_attrs_data);
538                 smb_krb5_free_data_contents(context, &deleg_data);
539                 return ret;
540         }
541
542         ret = krb5_pac_add_buffer(context, *pac, PAC_TYPE_LOGON_INFO, &logon_data);
543         smb_krb5_free_data_contents(context, &logon_data);
544         if (ret != 0) {
545                 smb_krb5_free_data_contents(context, &cred_data);
546                 smb_krb5_free_data_contents(context, &upn_data);
547                 smb_krb5_free_data_contents(context, &pac_attrs_data);
548                 smb_krb5_free_data_contents(context, &deleg_data);
549                 return ret;
550         }
551
552         if (cred_blob != NULL) {
553                 ret = krb5_pac_add_buffer(context, *pac,
554                                           PAC_TYPE_CREDENTIAL_INFO,
555                                           &cred_data);
556                 smb_krb5_free_data_contents(context, &cred_data);
557                 if (ret != 0) {
558                         smb_krb5_free_data_contents(context, &upn_data);
559                         smb_krb5_free_data_contents(context, &pac_attrs_data);
560                         smb_krb5_free_data_contents(context, &deleg_data);
561                         return ret;
562                 }
563         }
564
565 #ifdef SAMBA4_USES_HEIMDAL
566         /*
567          * null_data will be filled by the generic KDC code in the caller
568          * here we just add it in order to have it before
569          * PAC_TYPE_UPN_DNS_INFO
570          *
571          * Not needed with MIT Kerberos - asn
572          */
573         ret = krb5_pac_add_buffer(context, *pac,
574                                   PAC_TYPE_LOGON_NAME,
575                                   &null_data);
576         if (ret != 0) {
577                 smb_krb5_free_data_contents(context, &upn_data);
578                 smb_krb5_free_data_contents(context, &pac_attrs_data);
579                 smb_krb5_free_data_contents(context, &deleg_data);
580                 return ret;
581         }
582 #endif
583
584         if (upn_blob != NULL) {
585                 ret = krb5_pac_add_buffer(context, *pac,
586                                           PAC_TYPE_UPN_DNS_INFO,
587                                           &upn_data);
588                 smb_krb5_free_data_contents(context, &upn_data);
589                 if (ret != 0) {
590                         smb_krb5_free_data_contents(context, &pac_attrs_data);
591                         smb_krb5_free_data_contents(context, &deleg_data);
592                         return ret;
593                 }
594         }
595
596         if (pac_attrs_blob != NULL) {
597                 ret = krb5_pac_add_buffer(context, *pac,
598                                           PAC_TYPE_ATTRIBUTES_INFO,
599                                           &pac_attrs_data);
600                 smb_krb5_free_data_contents(context, &pac_attrs_data);
601                 if (ret != 0) {
602                         smb_krb5_free_data_contents(context, &deleg_data);
603                         return ret;
604                 }
605         }
606
607         if (deleg_blob != NULL) {
608                 ret = krb5_pac_add_buffer(context, *pac,
609                                           PAC_TYPE_CONSTRAINED_DELEGATION,
610                                           &deleg_data);
611                 smb_krb5_free_data_contents(context, &deleg_data);
612                 if (ret != 0) {
613                         return ret;
614                 }
615         }
616
617         return ret;
618 }
619
620 bool samba_princ_needs_pac(struct samba_kdc_entry *skdc_entry)
621 {
622
623         uint32_t userAccountControl;
624
625         /* The service account may be set not to want the PAC */
626         userAccountControl = ldb_msg_find_attr_as_uint(skdc_entry->msg, "userAccountControl", 0);
627         if (userAccountControl & UF_NO_AUTH_DATA_REQUIRED) {
628                 return false;
629         }
630
631         return true;
632 }
633
634 int samba_client_requested_pac(krb5_context context,
635                                krb5_pac *pac,
636                                TALLOC_CTX *mem_ctx,
637                                bool *requested_pac)
638 {
639         enum ndr_err_code ndr_err;
640         krb5_data k5pac_attrs_in;
641         DATA_BLOB pac_attrs_in;
642         union PAC_INFO pac_attrs;
643         int ret;
644
645         *requested_pac = true;
646
647         ret = krb5_pac_get_buffer(context, *pac, PAC_TYPE_ATTRIBUTES_INFO,
648                                   &k5pac_attrs_in);
649         if (ret != 0) {
650                 return ret == ENOENT ? 0 : ret;
651         }
652
653         pac_attrs_in = data_blob_const(k5pac_attrs_in.data,
654                                        k5pac_attrs_in.length);
655
656         ndr_err = ndr_pull_union_blob(&pac_attrs_in, mem_ctx, &pac_attrs,
657                                       PAC_TYPE_ATTRIBUTES_INFO,
658                                       (ndr_pull_flags_fn_t)ndr_pull_PAC_INFO);
659         smb_krb5_free_data_contents(context, &k5pac_attrs_in);
660         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
661                 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
662                 DEBUG(0,("can't parse the PAC ATTRIBUTES_INFO: %s\n", nt_errstr(nt_status)));
663                 return EINVAL;
664         }
665
666         if (pac_attrs.attributes_info.flags & (PAC_ATTRIBUTE_FLAG_PAC_WAS_GIVEN_IMPLICITLY
667                                                | PAC_ATTRIBUTE_FLAG_PAC_WAS_REQUESTED)) {
668                 *requested_pac = true;
669         } else {
670                 *requested_pac = false;
671         }
672
673         return 0;
674 }
675
676 /* Was the krbtgt in this DB (ie, should we check the incoming signature) and was it an RODC */
677 int samba_krbtgt_is_in_db(struct samba_kdc_entry *p,
678                           bool *is_in_db,
679                           bool *is_untrusted)
680 {
681         NTSTATUS status;
682         int rodc_krbtgt_number, trust_direction;
683         uint32_t rid;
684
685         TALLOC_CTX *mem_ctx = talloc_new(NULL);
686         if (!mem_ctx) {
687                 return ENOMEM;
688         }
689
690         trust_direction = ldb_msg_find_attr_as_int(p->msg, "trustDirection", 0);
691
692         if (trust_direction != 0) {
693                 /* Domain trust - we cannot check the sig, but we trust it for a correct PAC
694
695                    This is exactly where we should flag for SID
696                    validation when we do inter-foreest trusts
697                  */
698                 talloc_free(mem_ctx);
699                 *is_untrusted = false;
700                 *is_in_db = false;
701                 return 0;
702         }
703
704         /* The lack of password controls etc applies to krbtgt by
705          * virtue of being that particular RID */
706         status = dom_sid_split_rid(NULL, samdb_result_dom_sid(mem_ctx, p->msg, "objectSid"), NULL, &rid);
707
708         if (!NT_STATUS_IS_OK(status)) {
709                 talloc_free(mem_ctx);
710                 return EINVAL;
711         }
712
713         rodc_krbtgt_number = ldb_msg_find_attr_as_int(p->msg, "msDS-SecondaryKrbTgtNumber", -1);
714
715         if (p->kdc_db_ctx->my_krbtgt_number == 0) {
716                 if (rid == DOMAIN_RID_KRBTGT) {
717                         *is_untrusted = false;
718                         *is_in_db = true;
719                         talloc_free(mem_ctx);
720                         return 0;
721                 } else if (rodc_krbtgt_number != -1) {
722                         *is_in_db = true;
723                         *is_untrusted = true;
724                         talloc_free(mem_ctx);
725                         return 0;
726                 }
727         } else if ((rid != DOMAIN_RID_KRBTGT) && (rodc_krbtgt_number == p->kdc_db_ctx->my_krbtgt_number)) {
728                 talloc_free(mem_ctx);
729                 *is_untrusted = false;
730                 *is_in_db = true;
731                 return 0;
732         } else if (rid == DOMAIN_RID_KRBTGT) {
733                 /* krbtgt viewed from an RODC */
734                 talloc_free(mem_ctx);
735                 *is_untrusted = false;
736                 *is_in_db = false;
737                 return 0;
738         }
739
740         /* Another RODC */
741         talloc_free(mem_ctx);
742         *is_untrusted = true;
743         *is_in_db = false;
744         return 0;
745 }
746
747 NTSTATUS samba_kdc_get_pac_blobs(TALLOC_CTX *mem_ctx,
748                                  struct samba_kdc_entry *p,
749                                  DATA_BLOB **_logon_info_blob,
750                                  DATA_BLOB **_cred_ndr_blob,
751                                  DATA_BLOB **_upn_info_blob,
752                                  DATA_BLOB **_pac_attrs_blob,
753                                  const krb5_boolean *pac_request)
754 {
755         struct auth_user_info_dc *user_info_dc;
756         DATA_BLOB *logon_blob = NULL;
757         DATA_BLOB *cred_blob = NULL;
758         DATA_BLOB *upn_blob = NULL;
759         DATA_BLOB *pac_attrs_blob = NULL;
760         NTSTATUS nt_status;
761
762         *_logon_info_blob = NULL;
763         if (_cred_ndr_blob != NULL) {
764                 *_cred_ndr_blob = NULL;
765         }
766         *_upn_info_blob = NULL;
767         if (_pac_attrs_blob != NULL) {
768                 *_pac_attrs_blob = NULL;
769         }
770
771         logon_blob = talloc_zero(mem_ctx, DATA_BLOB);
772         if (logon_blob == NULL) {
773                 return NT_STATUS_NO_MEMORY;
774         }
775
776         if (_cred_ndr_blob != NULL) {
777                 cred_blob = talloc_zero(mem_ctx, DATA_BLOB);
778                 if (cred_blob == NULL) {
779                         return NT_STATUS_NO_MEMORY;
780                 }
781         }
782
783         upn_blob = talloc_zero(mem_ctx, DATA_BLOB);
784         if (upn_blob == NULL) {
785                 return NT_STATUS_NO_MEMORY;
786         }
787
788         if (_pac_attrs_blob != NULL) {
789                 pac_attrs_blob = talloc_zero(mem_ctx, DATA_BLOB);
790                 if (pac_attrs_blob == NULL) {
791                         return NT_STATUS_NO_MEMORY;
792                 }
793         }
794
795         nt_status = authsam_make_user_info_dc(mem_ctx, p->kdc_db_ctx->samdb,
796                                              lpcfg_netbios_name(p->kdc_db_ctx->lp_ctx),
797                                              lpcfg_sam_name(p->kdc_db_ctx->lp_ctx),
798                                              lpcfg_sam_dnsname(p->kdc_db_ctx->lp_ctx),
799                                              p->realm_dn,
800                                              p->msg,
801                                              data_blob(NULL, 0),
802                                              data_blob(NULL, 0),
803                                              &user_info_dc);
804         if (!NT_STATUS_IS_OK(nt_status)) {
805                 DEBUG(0, ("Getting user info for PAC failed: %s\n",
806                           nt_errstr(nt_status)));
807                 return nt_status;
808         }
809
810         nt_status = samba_get_logon_info_pac_blob(logon_blob,
811                                                   user_info_dc,
812                                                   logon_blob);
813         if (!NT_STATUS_IS_OK(nt_status)) {
814                 DEBUG(0, ("Building PAC LOGON INFO failed: %s\n",
815                           nt_errstr(nt_status)));
816                 return nt_status;
817         }
818
819         if (cred_blob != NULL) {
820                 nt_status = samba_get_cred_info_ndr_blob(cred_blob,
821                                                          p->msg,
822                                                          cred_blob);
823                 if (!NT_STATUS_IS_OK(nt_status)) {
824                         DEBUG(0, ("Building PAC CRED INFO failed: %s\n",
825                                   nt_errstr(nt_status)));
826                         return nt_status;
827                 }
828         }
829
830         nt_status = samba_get_upn_info_pac_blob(upn_blob,
831                                                 user_info_dc,
832                                                 upn_blob);
833         if (!NT_STATUS_IS_OK(nt_status)) {
834                 DEBUG(0, ("Building PAC UPN INFO failed: %s\n",
835                           nt_errstr(nt_status)));
836                 return nt_status;
837         }
838
839         if (pac_attrs_blob != NULL) {
840                 nt_status = samba_get_pac_attrs_blob(pac_attrs_blob,
841                                                      pac_request,
842                                                      pac_attrs_blob);
843
844                 if (!NT_STATUS_IS_OK(nt_status)) {
845                         DEBUG(0, ("Building PAC ATTRIBUTES failed: %s\n",
846                                   nt_errstr(nt_status)));
847                         return nt_status;
848                 }
849         }
850
851         TALLOC_FREE(user_info_dc);
852         *_logon_info_blob = logon_blob;
853         if (_cred_ndr_blob != NULL) {
854                 *_cred_ndr_blob = cred_blob;
855         }
856         *_upn_info_blob = upn_blob;
857         if (_pac_attrs_blob != NULL) {
858                 *_pac_attrs_blob = pac_attrs_blob;
859         }
860         return NT_STATUS_OK;
861 }
862
863 NTSTATUS samba_kdc_get_pac_blob(TALLOC_CTX *mem_ctx,
864                                 struct samba_kdc_entry *p,
865                                 DATA_BLOB **_logon_info_blob)
866 {
867         NTSTATUS nt_status;
868         DATA_BLOB *upn_blob = NULL;
869
870         nt_status = samba_kdc_get_pac_blobs(mem_ctx, p,
871                                             _logon_info_blob,
872                                             NULL, /* cred_blob */
873                                             &upn_blob,
874                                             NULL,
875                                             NULL);
876         if (!NT_STATUS_IS_OK(nt_status)) {
877                 return nt_status;
878         }
879
880         TALLOC_FREE(upn_blob);
881         return NT_STATUS_OK;
882 }
883
884 NTSTATUS samba_kdc_update_pac_blob(TALLOC_CTX *mem_ctx,
885                                    krb5_context context,
886                                    struct ldb_context *samdb,
887                                    const krb5_pac pac, DATA_BLOB *pac_blob,
888                                    struct PAC_SIGNATURE_DATA *pac_srv_sig,
889                                    struct PAC_SIGNATURE_DATA *pac_kdc_sig)
890 {
891         struct auth_user_info_dc *user_info_dc;
892         krb5_error_code ret;
893         NTSTATUS nt_status;
894
895         ret = kerberos_pac_to_user_info_dc(mem_ctx, pac,
896                                            context, &user_info_dc, pac_srv_sig, pac_kdc_sig);
897         if (ret) {
898                 return NT_STATUS_UNSUCCESSFUL;
899         }
900
901         /*
902          * We need to expand group memberships within our local domain,
903          * as the token might be generated by a trusted domain.
904          */
905         nt_status = authsam_update_user_info_dc(mem_ctx,
906                                                 samdb,
907                                                 user_info_dc);
908         if (!NT_STATUS_IS_OK(nt_status)) {
909                 return nt_status;
910         }
911
912         nt_status = samba_get_logon_info_pac_blob(mem_ctx,
913                                                   user_info_dc, pac_blob);
914
915         return nt_status;
916 }
917
918 NTSTATUS samba_kdc_update_delegation_info_blob(TALLOC_CTX *mem_ctx,
919                                 krb5_context context,
920                                 const krb5_pac pac,
921                                 const krb5_principal server_principal,
922                                 const krb5_principal proxy_principal,
923                                 DATA_BLOB *new_blob)
924 {
925         krb5_data old_data;
926         DATA_BLOB old_blob;
927         krb5_error_code ret;
928         NTSTATUS nt_status;
929         enum ndr_err_code ndr_err;
930         union PAC_INFO info;
931         struct PAC_CONSTRAINED_DELEGATION _d;
932         struct PAC_CONSTRAINED_DELEGATION *d = NULL;
933         char *server = NULL;
934         char *proxy = NULL;
935         uint32_t i;
936         TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
937
938         if (tmp_ctx == NULL) {
939                 return NT_STATUS_NO_MEMORY;
940         }
941
942         ret = krb5_pac_get_buffer(context, pac, PAC_TYPE_CONSTRAINED_DELEGATION, &old_data);
943         if (ret == ENOENT) {
944                 ZERO_STRUCT(old_data);
945         } else if (ret) {
946                 talloc_free(tmp_ctx);
947                 return NT_STATUS_UNSUCCESSFUL;
948         }
949
950         old_blob.length = old_data.length;
951         old_blob.data = (uint8_t *)old_data.data;
952
953         ZERO_STRUCT(info);
954         if (old_blob.length > 0) {
955                 ndr_err = ndr_pull_union_blob(&old_blob, mem_ctx,
956                                 &info, PAC_TYPE_CONSTRAINED_DELEGATION,
957                                 (ndr_pull_flags_fn_t)ndr_pull_PAC_INFO);
958                 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
959                         smb_krb5_free_data_contents(context, &old_data);
960                         nt_status = ndr_map_error2ntstatus(ndr_err);
961                         DEBUG(0,("can't parse the PAC LOGON_INFO: %s\n", nt_errstr(nt_status)));
962                         talloc_free(tmp_ctx);
963                         return nt_status;
964                 }
965         } else {
966                 ZERO_STRUCT(_d);
967                 info.constrained_delegation.info = &_d;
968         }
969         smb_krb5_free_data_contents(context, &old_data);
970
971         ret = krb5_unparse_name_flags(context, server_principal,
972                                       KRB5_PRINCIPAL_UNPARSE_NO_REALM, &server);
973         if (ret) {
974                 talloc_free(tmp_ctx);
975                 return NT_STATUS_INTERNAL_ERROR;
976         }
977
978         ret = krb5_unparse_name(context, proxy_principal, &proxy);
979         if (ret) {
980                 SAFE_FREE(server);
981                 talloc_free(tmp_ctx);
982                 return NT_STATUS_INTERNAL_ERROR;
983         }
984
985         d = info.constrained_delegation.info;
986         i = d->num_transited_services;
987         d->proxy_target.string = server;
988         d->transited_services = talloc_realloc(mem_ctx, d->transited_services,
989                                                struct lsa_String, i + 1);
990         d->transited_services[i].string = proxy;
991         d->num_transited_services = i + 1;
992
993         ndr_err = ndr_push_union_blob(new_blob, mem_ctx,
994                                 &info, PAC_TYPE_CONSTRAINED_DELEGATION,
995                                 (ndr_push_flags_fn_t)ndr_push_PAC_INFO);
996         SAFE_FREE(server);
997         SAFE_FREE(proxy);
998         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
999                 smb_krb5_free_data_contents(context, &old_data);
1000                 nt_status = ndr_map_error2ntstatus(ndr_err);
1001                 DEBUG(0,("can't parse the PAC LOGON_INFO: %s\n", nt_errstr(nt_status)));
1002                 talloc_free(tmp_ctx);
1003                 return nt_status;
1004         }
1005
1006         talloc_free(tmp_ctx);
1007         return NT_STATUS_OK;
1008 }
1009
1010 /* function to map policy errors */
1011 krb5_error_code samba_kdc_map_policy_err(NTSTATUS nt_status)
1012 {
1013         krb5_error_code ret;
1014
1015         if (NT_STATUS_EQUAL(nt_status, NT_STATUS_PASSWORD_MUST_CHANGE))
1016                 ret = KRB5KDC_ERR_KEY_EXP;
1017         else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_PASSWORD_EXPIRED))
1018                 ret = KRB5KDC_ERR_KEY_EXP;
1019         else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_ACCOUNT_EXPIRED))
1020                 ret = KRB5KDC_ERR_CLIENT_REVOKED;
1021         else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_ACCOUNT_DISABLED))
1022                 ret = KRB5KDC_ERR_CLIENT_REVOKED;
1023         else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_INVALID_LOGON_HOURS))
1024                 ret = KRB5KDC_ERR_CLIENT_REVOKED;
1025         else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_ACCOUNT_LOCKED_OUT))
1026                 ret = KRB5KDC_ERR_CLIENT_REVOKED;
1027         else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_INVALID_WORKSTATION))
1028                 ret = KRB5KDC_ERR_POLICY;
1029         else
1030                 ret = KRB5KDC_ERR_POLICY;
1031
1032         return ret;
1033 }
1034
1035 /* Given a kdc entry, consult the account_ok routine in auth/auth_sam.c
1036  * for consistency */
1037 NTSTATUS samba_kdc_check_client_access(struct samba_kdc_entry *kdc_entry,
1038                                        const char *client_name,
1039                                        const char *workstation,
1040                                        bool password_change)
1041 {
1042         TALLOC_CTX *tmp_ctx;
1043         NTSTATUS nt_status;
1044
1045         tmp_ctx = talloc_named(NULL, 0, "samba_kdc_check_client_access");
1046         if (!tmp_ctx) {
1047                 return NT_STATUS_NO_MEMORY;
1048         }
1049
1050         /* we allow all kinds of trusts here */
1051         nt_status = authsam_account_ok(tmp_ctx,
1052                                        kdc_entry->kdc_db_ctx->samdb,
1053                                        MSV1_0_ALLOW_SERVER_TRUST_ACCOUNT |
1054                                        MSV1_0_ALLOW_WORKSTATION_TRUST_ACCOUNT,
1055                                        kdc_entry->realm_dn, kdc_entry->msg,
1056                                        workstation, client_name,
1057                                        true, password_change);
1058
1059         talloc_free(tmp_ctx);
1060         return nt_status;
1061 }
1062
1063 /* Does a parse and SID check, but no crypto. */
1064 krb5_error_code samba_kdc_validate_pac_blob(
1065                 krb5_context context,
1066                 struct samba_kdc_entry *client_skdc_entry,
1067                 const krb5_pac pac)
1068 {
1069         TALLOC_CTX *frame = talloc_stackframe();
1070         struct auth_user_info_dc *pac_user_info = NULL;
1071         struct dom_sid *client_sid = NULL;
1072         struct dom_sid pac_sid;
1073         krb5_error_code code;
1074         bool ok;
1075
1076         code = kerberos_pac_to_user_info_dc(frame,
1077                                             pac,
1078                                             context,
1079                                             &pac_user_info,
1080                                             NULL,
1081                                             NULL);
1082         if (code != 0) {
1083                 goto out;
1084         }
1085
1086         if (pac_user_info->num_sids == 0) {
1087                 code = EINVAL;
1088                 goto out;
1089         }
1090
1091         pac_sid = pac_user_info->sids[0];
1092         client_sid = samdb_result_dom_sid(frame,
1093                                           client_skdc_entry->msg,
1094                                           "objectSid");
1095
1096         ok = dom_sid_equal(&pac_sid, client_sid);
1097         if (!ok) {
1098                 struct dom_sid_buf buf1;
1099                 struct dom_sid_buf buf2;
1100
1101                 DBG_ERR("SID mismatch between PAC and looked up client: "
1102                         "PAC[%s] != CLI[%s]\n",
1103                         dom_sid_str_buf(&pac_sid, &buf1),
1104                         dom_sid_str_buf(client_sid, &buf2));
1105 #if defined(KRB5KDC_ERR_CLIENT_NAME_MISMATCH) /* MIT */
1106                         code = KRB5KDC_ERR_CLIENT_NAME_MISMATCH;
1107 #else /* Heimdal (where this is an enum) */
1108                         code = KRB5_KDC_ERR_CLIENT_NAME_MISMATCH;
1109 #endif
1110                 goto out;
1111         }
1112
1113         code = 0;
1114 out:
1115         TALLOC_FREE(frame);
1116         return code;
1117 }