s4:kdc: Add NTSTATUS e-data to KDC reply
[samba.git] / source4 / kdc / wdc-samba4.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 "kdc/kdc-glue.h"
26 #include "kdc/db-glue.h"
27 #include "kdc/pac-glue.h"
28 #include "sdb.h"
29 #include "sdb_hdb.h"
30 #include "librpc/gen_ndr/auth.h"
31 #include <krb5_locl.h>
32 #include "lib/replace/system/filesys.h"
33
34 #undef DBGC_CLASS
35 #define DBGC_CLASS DBGC_KERBEROS
36
37 static bool samba_wdc_is_s4u2self_req(astgs_request_t r)
38 {
39         krb5_kdc_configuration *config = kdc_request_get_config((kdc_request_t)r);
40         const KDC_REQ *req = kdc_request_get_req(r);
41         const PA_DATA *pa_for_user = NULL;
42
43         if (req->msg_type != krb_tgs_req) {
44                 return false;
45         }
46
47         if (config->enable_fast && req->padata != NULL) {
48                 const PA_DATA *pa_fx_fast = NULL;
49                 int idx = 0;
50
51                 pa_fx_fast = krb5_find_padata(req->padata->val,
52                                               req->padata->len,
53                                               KRB5_PADATA_FX_FAST,
54                                               &idx);
55                 if (pa_fx_fast != NULL) {
56                         /*
57                          * We're in the outer request
58                          * with KRB5_PADATA_FX_FAST
59                          * if fast is enabled we'll
60                          * process the s4u2self
61                          * request only in the
62                          * inner request.
63                          */
64                         return false;
65                 }
66         }
67
68         if (req->padata != NULL) {
69                 int idx = 0;
70
71                 pa_for_user = krb5_find_padata(req->padata->val,
72                                                req->padata->len,
73                                                KRB5_PADATA_FOR_USER,
74                                                &idx);
75         }
76
77         if (pa_for_user != NULL) {
78                 return true;
79         }
80
81         return false;
82 }
83
84 /*
85  * Given the right private pointer from hdb_samba4,
86  * get a PAC from the attached ldb messages.
87  *
88  * For PKINIT we also get pk_reply_key and can add PAC_CREDENTIAL_INFO.
89  */
90 static krb5_error_code samba_wdc_get_pac(void *priv,
91                                          astgs_request_t r,
92                                          hdb_entry *client,
93                                          hdb_entry *server,
94                                          const krb5_keyblock *pk_reply_key,
95                                          uint64_t pac_attributes,
96                                          krb5_pac *pac)
97 {
98         krb5_context context = kdc_request_get_context((kdc_request_t)r);
99         TALLOC_CTX *mem_ctx;
100         DATA_BLOB *logon_blob = NULL;
101         DATA_BLOB *cred_ndr = NULL;
102         DATA_BLOB **cred_ndr_ptr = NULL;
103         DATA_BLOB _cred_blob = data_blob_null;
104         DATA_BLOB *cred_blob = NULL;
105         DATA_BLOB *upn_blob = NULL;
106         DATA_BLOB *pac_attrs_blob = NULL;
107         DATA_BLOB *requester_sid_blob = NULL;
108         DATA_BLOB *client_claims_blob = NULL;
109         krb5_error_code ret;
110         NTSTATUS nt_status;
111         struct samba_kdc_entry *skdc_entry =
112                 talloc_get_type_abort(client->context,
113                 struct samba_kdc_entry);
114         const struct samba_kdc_entry *server_entry =
115                 talloc_get_type_abort(server->context,
116                 struct samba_kdc_entry);
117         bool is_krbtgt = krb5_principal_is_krbtgt(context, server->principal);
118         /* Only include resource groups in a service ticket. */
119         enum auth_group_inclusion group_inclusion;
120         bool is_s4u2self = samba_wdc_is_s4u2self_req(r);
121         enum samba_asserted_identity asserted_identity =
122                 (is_s4u2self) ?
123                         SAMBA_ASSERTED_IDENTITY_SERVICE :
124                         SAMBA_ASSERTED_IDENTITY_AUTHENTICATION_AUTHORITY;
125         const enum samba_claims_valid claims_valid = SAMBA_CLAIMS_VALID_INCLUDE;
126         const enum samba_compounded_auth compounded_auth = SAMBA_COMPOUNDED_AUTH_EXCLUDE;
127
128         struct auth_user_info_dc *user_info_dc = NULL;
129
130         /* Only include resource groups in a service ticket. */
131         if (is_krbtgt) {
132                 group_inclusion = AUTH_EXCLUDE_RESOURCE_GROUPS;
133         } else if (server_entry->supported_enctypes & KERB_ENCTYPE_RESOURCE_SID_COMPRESSION_DISABLED) {
134                 group_inclusion = AUTH_INCLUDE_RESOURCE_GROUPS;
135         } else {
136                 group_inclusion = AUTH_INCLUDE_RESOURCE_GROUPS_COMPRESSED;
137         }
138
139         mem_ctx = talloc_named(client->context, 0, "samba_wdc_get_pac context");
140         if (!mem_ctx) {
141                 return ENOMEM;
142         }
143
144         if (pk_reply_key != NULL) {
145                 cred_ndr_ptr = &cred_ndr;
146         }
147
148         nt_status = samba_kdc_get_user_info_dc(mem_ctx,
149                                                skdc_entry,
150                                                asserted_identity,
151                                                claims_valid,
152                                                compounded_auth,
153                                                &user_info_dc);
154         if (!NT_STATUS_IS_OK(nt_status)) {
155                 talloc_free(mem_ctx);
156                 return EINVAL;
157         }
158
159         nt_status = samba_kdc_get_logon_info_blob(mem_ctx,
160                                                   user_info_dc,
161                                                   group_inclusion,
162                                                   &logon_blob);
163         if (!NT_STATUS_IS_OK(nt_status)) {
164                 talloc_free(mem_ctx);
165                 return EINVAL;
166         }
167
168         if (cred_ndr_ptr != NULL) {
169                 nt_status = samba_kdc_get_cred_ndr_blob(mem_ctx,
170                                                         skdc_entry,
171                                                         cred_ndr_ptr);
172                 if (!NT_STATUS_IS_OK(nt_status)) {
173                         talloc_free(mem_ctx);
174                         return EINVAL;
175                 }
176         }
177
178         nt_status = samba_kdc_get_upn_info_blob(mem_ctx,
179                                                 user_info_dc,
180                                                 &upn_blob);
181         if (!NT_STATUS_IS_OK(nt_status)) {
182                 talloc_free(mem_ctx);
183                 return EINVAL;
184         }
185
186         if (is_krbtgt) {
187                 nt_status = samba_kdc_get_pac_attrs_blob(mem_ctx,
188                                                          pac_attributes,
189                                                          &pac_attrs_blob);
190                 if (!NT_STATUS_IS_OK(nt_status)) {
191                         talloc_free(mem_ctx);
192                         return EINVAL;
193                 }
194
195                 nt_status = samba_kdc_get_requester_sid_blob(mem_ctx,
196                                                              user_info_dc,
197                                                              &requester_sid_blob);
198                 if (!NT_STATUS_IS_OK(nt_status)) {
199                         talloc_free(mem_ctx);
200                         return EINVAL;
201                 }
202         }
203
204         nt_status = samba_kdc_get_claims_blob(mem_ctx,
205                                               skdc_entry,
206                                               &client_claims_blob);
207         if (!NT_STATUS_IS_OK(nt_status)) {
208                 talloc_free(mem_ctx);
209                 return EINVAL;
210         }
211
212         if (pk_reply_key != NULL && cred_ndr != NULL) {
213                 ret = samba_kdc_encrypt_pac_credentials(context,
214                                                         pk_reply_key,
215                                                         cred_ndr,
216                                                         mem_ctx,
217                                                         &_cred_blob);
218                 if (ret != 0) {
219                         talloc_free(mem_ctx);
220                         return ret;
221                 }
222                 cred_blob = &_cred_blob;
223         }
224
225         ret = krb5_pac_init(context, pac);
226         if (ret != 0) {
227                 talloc_free(mem_ctx);
228                 return ret;
229         }
230
231         ret = samba_make_krb5_pac(context, logon_blob, cred_blob,
232                                   upn_blob, pac_attrs_blob,
233                                   requester_sid_blob, NULL,
234                                   client_claims_blob, NULL, NULL,
235                                   *pac);
236
237         talloc_free(mem_ctx);
238         return ret;
239 }
240
241 static krb5_error_code samba_wdc_verify_pac2(astgs_request_t r,
242                                              const krb5_principal delegated_proxy_principal,
243                                              const hdb_entry *client,
244                                              const hdb_entry *server,
245                                              const hdb_entry *krbtgt,
246                                              const krb5_pac pac,
247                                              krb5_cksumtype ctype,
248                                              const hdb_entry *device,
249                                              krb5_const_pac *device_pac,
250                                              krb5_boolean *is_trusted_out)
251 {
252         krb5_context context = kdc_request_get_context((kdc_request_t)r);
253         struct samba_kdc_entry *client_skdc_entry = NULL;
254         struct samba_kdc_entry *device_skdc_entry = NULL;
255         struct samba_kdc_entry *krbtgt_skdc_entry =
256                 talloc_get_type_abort(krbtgt->context, struct samba_kdc_entry);
257         TALLOC_CTX *mem_ctx = NULL;
258         krb5_error_code ret;
259         bool is_s4u2self = samba_wdc_is_s4u2self_req(r);
260         bool is_in_db = false;
261         bool is_trusted = false;
262         uint32_t flags = 0;
263
264         mem_ctx = talloc_named(NULL, 0, "samba_wdc_verify_pac2 context");
265         if (mem_ctx == NULL) {
266                 return ENOMEM;
267         }
268
269         if (client != NULL) {
270                 client_skdc_entry = talloc_get_type_abort(client->context,
271                                                           struct samba_kdc_entry);
272         }
273
274         if (device != NULL) {
275                 device_skdc_entry = talloc_get_type_abort(device->context,
276                                                           struct samba_kdc_entry);
277         }
278
279         /*
280          * If the krbtgt was generated by an RODC, and we are not that
281          * RODC, then we need to regenerate the PAC - we can't trust
282          * it, and confirm that the RODC was permitted to print this ticket
283          *
284          * Becasue of the samba_kdc_validate_pac_blob() step we can be
285          * sure that the record in 'client' matches the SID in the
286          * original PAC.
287          */
288         ret = samba_krbtgt_is_in_db(krbtgt_skdc_entry, &is_in_db, &is_trusted);
289         if (ret != 0) {
290                 goto out;
291         }
292
293         if (is_s4u2self) {
294                 flags |= SAMBA_KDC_FLAG_PROTOCOL_TRANSITION;
295         }
296
297         if (delegated_proxy_principal != NULL) {
298                 krb5_enctype etype;
299                 Key *key = NULL;
300
301                 if (!is_in_db) {
302                         /*
303                          * The RODC-issued PAC was signed by a KDC entry that we
304                          * don't have a key for. The server signature is not
305                          * trustworthy, since it could have been created by the
306                          * server we got the ticket from. We must not proceed as
307                          * otherwise the ticket signature is unchecked.
308                          */
309                         ret = HDB_ERR_NOT_FOUND_HERE;
310                         goto out;
311                 }
312
313                 /* Fetch the correct key depending on the checksum type. */
314                 if (ctype == CKSUMTYPE_HMAC_MD5) {
315                         etype = ENCTYPE_ARCFOUR_HMAC;
316                 } else {
317                         ret = krb5_cksumtype_to_enctype(context,
318                                                         ctype,
319                                                         &etype);
320                         if (ret != 0) {
321                                 goto out;
322                         }
323                 }
324                 ret = hdb_enctype2key(context, krbtgt, NULL, etype, &key);
325                 if (ret != 0) {
326                         goto out;
327                 }
328
329                 /* Check the KDC, whole-PAC and ticket signatures. */
330                 ret = krb5_pac_verify(context,
331                                       pac,
332                                       0,
333                                       NULL,
334                                       NULL,
335                                       &key->key);
336                 if (ret != 0) {
337                         DEBUG(1, ("PAC KDC signature failed to verify\n"));
338                         goto out;
339                 }
340
341                 flags |= SAMBA_KDC_FLAG_CONSTRAINED_DELEGATION;
342         }
343
344         if (is_trusted) {
345                 flags |= SAMBA_KDC_FLAG_KRBTGT_IS_TRUSTED;
346         }
347
348         if (is_in_db) {
349                 flags |= SAMBA_KDC_FLAG_KRBTGT_IN_DB;
350         }
351
352         ret = samba_kdc_verify_pac(mem_ctx,
353                                    context,
354                                    flags,
355                                    client_skdc_entry,
356                                    krbtgt_skdc_entry,
357                                    device_skdc_entry,
358                                    device_pac,
359                                    pac);
360         if (ret != 0) {
361                 goto out;
362         }
363
364         if (is_trusted_out != NULL) {
365                 *is_trusted_out = is_trusted;
366         }
367
368 out:
369         talloc_free(mem_ctx);
370         return ret;
371 }
372
373 /* Resign (and reform, including possibly new groups) a PAC */
374
375 static krb5_error_code samba_wdc_reget_pac(void *priv, astgs_request_t r,
376                                            const krb5_principal _client_principal,
377                                            const krb5_principal delegated_proxy_principal,
378                                            hdb_entry *client,
379                                            hdb_entry *server,
380                                            hdb_entry *krbtgt,
381                                            krb5_pac *pac)
382 {
383         krb5_context context = kdc_request_get_context((kdc_request_t)r);
384         const hdb_entry *device = kdc_request_get_explicit_armor_client(r);
385         const krb5_const_pac device_pac = kdc_request_get_explicit_armor_pac(r);
386         struct samba_kdc_entry *client_skdc_entry = NULL;
387         struct samba_kdc_entry *device_skdc_entry = NULL;
388         const struct samba_kdc_entry *server_skdc_entry =
389                 talloc_get_type_abort(server->context, struct samba_kdc_entry);
390         const struct samba_kdc_entry *krbtgt_skdc_entry =
391                 talloc_get_type_abort(krbtgt->context, struct samba_kdc_entry);
392         TALLOC_CTX *mem_ctx = NULL;
393         krb5_pac new_pac = NULL;
394         krb5_error_code ret;
395         uint32_t flags = 0;
396
397         mem_ctx = talloc_named(NULL, 0, "samba_wdc_reget_pac context");
398         if (mem_ctx == NULL) {
399                 return ENOMEM;
400         }
401
402         if (client != NULL) {
403                 client_skdc_entry = talloc_get_type_abort(client->context,
404                                                           struct samba_kdc_entry);
405         }
406
407         if (device != NULL) {
408                 device_skdc_entry = talloc_get_type_abort(device->context,
409                                                           struct samba_kdc_entry);
410         }
411
412         ret = krb5_pac_init(context, &new_pac);
413         if (ret != 0) {
414                 new_pac = NULL;
415                 goto out;
416         }
417
418         if (krb5_pac_is_trusted(*pac)) {
419                 flags |= SAMBA_KDC_FLAG_KRBTGT_IS_TRUSTED;
420         }
421         if (device_pac != NULL && krb5_pac_is_trusted(device_pac)) {
422                 flags |= SAMBA_KDC_FLAG_DEVICE_KRBTGT_IS_TRUSTED;
423         }
424
425         ret = samba_kdc_update_pac(mem_ctx,
426                                    context,
427                                    krbtgt_skdc_entry->kdc_db_ctx->samdb,
428                                    flags,
429                                    client_skdc_entry,
430                                    server->principal,
431                                    server_skdc_entry,
432                                    delegated_proxy_principal,
433                                    device_skdc_entry,
434                                    device_pac,
435                                    *pac,
436                                    new_pac);
437         if (ret != 0) {
438                 krb5_pac_free(context, new_pac);
439                 if (ret == ENOATTR) {
440                         krb5_pac_free(context, *pac);
441                         *pac = NULL;
442                         ret = 0;
443                 }
444                 goto out;
445         }
446
447         /* Replace the pac */
448         krb5_pac_free(context, *pac);
449         *pac = new_pac;
450
451 out:
452         talloc_free(mem_ctx);
453         return ret;
454 }
455
456 /* Verify a PAC's SID and signatures */
457
458 static krb5_error_code samba_wdc_verify_pac(void *priv, astgs_request_t r,
459                                             const krb5_principal client_principal,
460                                             const krb5_principal delegated_proxy_principal,
461                                             hdb_entry *client,
462                                             hdb_entry *server,
463                                             hdb_entry *krbtgt,
464                                             krb5_pac pac,
465                                             krb5_boolean *is_trusted)
466 {
467         krb5_context context = kdc_request_get_context((kdc_request_t)r);
468         krb5_kdc_configuration *config = kdc_request_get_config((kdc_request_t)r);
469         struct samba_kdc_entry *krbtgt_skdc_entry =
470                 talloc_get_type_abort(krbtgt->context,
471                                       struct samba_kdc_entry);
472         krb5_error_code ret;
473         krb5_cksumtype ctype = CKSUMTYPE_NONE;
474         hdb_entry signing_krbtgt_hdb;
475         const hdb_entry *explicit_armor_client =
476                 kdc_request_get_explicit_armor_client(r);
477         krb5_const_pac explicit_armor_pac =
478                 kdc_request_get_explicit_armor_pac(r);
479
480         if (delegated_proxy_principal) {
481                 uint16_t rodc_id;
482                 unsigned int my_krbtgt_number;
483
484                 /*
485                  * We're using delegated_proxy_principal for the moment to
486                  * indicate cases where the ticket was encrypted with the server
487                  * key, and not a krbtgt key. This cannot be trusted, so we need
488                  * to find a krbtgt key that signs the PAC in order to trust the
489                  * ticket.
490                  *
491                  * The krbtgt passed in to this function refers to the krbtgt
492                  * used to decrypt the ticket of the server requesting
493                  * S4U2Proxy.
494                  *
495                  * When we implement service ticket renewal, we need to check
496                  * the PAC, and this will need to be updated.
497                  */
498                 ret = krb5_pac_get_kdc_checksum_info(context,
499                                                      pac,
500                                                      &ctype,
501                                                      &rodc_id);
502                 if (ret != 0) {
503                         DEBUG(1, ("Failed to get PAC checksum info\n"));
504                         return ret;
505                 }
506
507                 /*
508                  * We need to check the KDC and ticket signatures, fetching the
509                  * correct key based on the enctype.
510                  */
511
512                 my_krbtgt_number = krbtgt_skdc_entry->kdc_db_ctx->my_krbtgt_number;
513
514                 if (my_krbtgt_number != 0) {
515                         /*
516                          * If we are an RODC, and we are not the KDC that signed
517                          * the evidence ticket, then we need to proxy the
518                          * request.
519                          */
520                         if (rodc_id != my_krbtgt_number) {
521                                 return HDB_ERR_NOT_FOUND_HERE;
522                         }
523                 } else {
524                         /*
525                          * If we are a DC, the ticket may have been signed by a
526                          * different KDC than the one that issued the header
527                          * ticket.
528                          */
529                         if (rodc_id != krbtgt->kvno >> 16) {
530                                 struct sdb_entry signing_krbtgt_sdb;
531
532                                 /*
533                                  * If we didn't sign the ticket, then return an
534                                  * error.
535                                  */
536                                 if (rodc_id != 0) {
537                                         return KRB5KRB_AP_ERR_MODIFIED;
538                                 }
539
540                                 /*
541                                  * Fetch our key from the database. To support
542                                  * key rollover, we're going to need to try
543                                  * multiple keys by trial and error. For now,
544                                  * krbtgt keys aren't assumed to change.
545                                  */
546                                 ret = samba_kdc_fetch(context,
547                                                       krbtgt_skdc_entry->kdc_db_ctx,
548                                                       krbtgt->principal,
549                                                       SDB_F_GET_KRBTGT | SDB_F_CANON,
550                                                       0,
551                                                       &signing_krbtgt_sdb);
552                                 if (ret != 0) {
553                                         return ret;
554                                 }
555
556                                 ret = sdb_entry_to_hdb_entry(context,
557                                                              &signing_krbtgt_sdb,
558                                                              &signing_krbtgt_hdb);
559                                 sdb_entry_free(&signing_krbtgt_sdb);
560                                 if (ret != 0) {
561                                         return ret;
562                                 }
563
564                                 /*
565                                  * Replace the krbtgt entry with our own entry
566                                  * for further processing.
567                                  */
568                                 krbtgt = &signing_krbtgt_hdb;
569                         }
570                 }
571         } else if (!krbtgt_skdc_entry->is_trust) {
572                 /*
573                  * We expect to have received a TGT, so check that we haven't
574                  * been given a kpasswd ticket instead. We don't need to do this
575                  * check for an incoming trust, as they use a different secret
576                  * and can't be confused with a normal TGT.
577                  */
578                 krb5_ticket *tgt = kdc_request_get_ticket(r);
579
580                 struct timeval now = krb5_kdc_get_time();
581
582                 /*
583                  * Check if the ticket is in the last two minutes of its
584                  * life.
585                  */
586                 KerberosTime lifetime = rk_time_sub(tgt->ticket.endtime, now.tv_sec);
587                 if (lifetime <= CHANGEPW_LIFETIME) {
588                         /*
589                          * This ticket has at most two minutes left to live. It
590                          * may be a kpasswd ticket rather than a TGT, so don't
591                          * accept it.
592                          */
593                         kdc_audit_addreason((kdc_request_t)r,
594                                             "Ticket is not a ticket-granting ticket");
595                         return KRB5KRB_AP_ERR_TKT_EXPIRED;
596                 }
597         }
598
599         ret = samba_wdc_verify_pac2(r,
600                                     delegated_proxy_principal,
601                                     client,
602                                     server,
603                                     krbtgt,
604                                     pac,
605                                     ctype,
606                                     explicit_armor_client,
607                                     &explicit_armor_pac,
608                                     is_trusted);
609
610         if (krbtgt == &signing_krbtgt_hdb) {
611                 hdb_free_entry(context, config->db[0], &signing_krbtgt_hdb);
612         }
613
614         return ret;
615 }
616
617 static char *get_netbios_name(TALLOC_CTX *mem_ctx, HostAddresses *addrs)
618 {
619         char *nb_name = NULL;
620         size_t len;
621         unsigned int i;
622
623         for (i = 0; addrs && i < addrs->len; i++) {
624                 if (addrs->val[i].addr_type != KRB5_ADDRESS_NETBIOS) {
625                         continue;
626                 }
627                 len = MIN(addrs->val[i].address.length, 15);
628                 nb_name = talloc_strndup(mem_ctx,
629                                          addrs->val[i].address.data, len);
630                 if (nb_name) {
631                         break;
632                 }
633         }
634
635         if ((nb_name == NULL) || (nb_name[0] == '\0')) {
636                 return NULL;
637         }
638
639         /* Strip space padding */
640         for (len = strlen(nb_name) - 1;
641              (len > 0) && (nb_name[len] == ' ');
642              --len) {
643                 nb_name[len] = '\0';
644         }
645
646         return nb_name;
647 }
648
649 /* this function allocates 'data' using malloc.
650  * The caller is responsible for freeing it */
651 static void samba_kdc_build_edata_reply(NTSTATUS nt_status, krb5_data *e_data)
652 {
653         e_data->data = malloc(12);
654         if (e_data->data == NULL) {
655                 e_data->length = 0;
656                 e_data->data = NULL;
657                 return;
658         }
659         e_data->length = 12;
660
661         SIVAL(e_data->data, 0, NT_STATUS_V(nt_status));
662         SIVAL(e_data->data, 4, 0);
663         SIVAL(e_data->data, 8, 1);
664
665         return;
666 }
667
668 static krb5_error_code samba_wdc_check_client_access(void *priv,
669                                                      astgs_request_t r)
670 {
671         struct samba_kdc_entry *kdc_entry;
672         bool password_change;
673         char *workstation;
674         NTSTATUS nt_status;
675
676
677         kdc_entry = talloc_get_type(kdc_request_get_client(r)->context, struct samba_kdc_entry);
678         password_change = (kdc_request_get_server(r) && kdc_request_get_server(r)->flags.change_pw);
679         workstation = get_netbios_name((TALLOC_CTX *)kdc_request_get_client(r)->context,
680                                        kdc_request_get_req(r)->req_body.addresses);
681
682         nt_status = samba_kdc_check_client_access(kdc_entry,
683                                                   kdc_request_get_cname((kdc_request_t)r),
684                                                   workstation,
685                                                   password_change);
686
687         if (!NT_STATUS_IS_OK(nt_status)) {
688                 krb5_error_code ret;
689                 krb5_error_code ret2;
690
691                 if (NT_STATUS_EQUAL(nt_status, NT_STATUS_NO_MEMORY)) {
692                         return ENOMEM;
693                 }
694
695                 ret = samba_kdc_map_policy_err(nt_status);
696
697                 /*
698                  * Add the NTSTATUS to the request so we can return it in the
699                  * ‘e-data’ field later.
700                  */
701                 ret2 = hdb_samba4_set_ntstatus(r, nt_status, ret);
702                 if (ret2) {
703                         ret = ret2;
704                 }
705
706                 if (kdc_request_get_rep(r)->padata) {
707                         krb5_data kd;
708
709                         samba_kdc_build_edata_reply(nt_status, &kd);
710                         ret2 = krb5_padata_add(kdc_request_get_context((kdc_request_t)r), kdc_request_get_rep(r)->padata,
711                                                KRB5_PADATA_PW_SALT,
712                                                kd.data, kd.length);
713                         if (ret2) {
714                                 /*
715                                  * So we do not leak the allocated
716                                  * memory on kd in the error case
717                                  */
718                                 krb5_data_free(&kd);
719
720                                 ret = ret2;
721                         }
722                 }
723
724                 return ret;
725         }
726
727         /* Now do the standard Heimdal check */
728         return KRB5_PLUGIN_NO_HANDLE;
729 }
730
731 /* this function allocates 'data' using malloc.
732  * The caller is responsible for freeing it */
733 static krb5_error_code samba_kdc_build_supported_etypes(uint32_t supported_etypes,
734                                                         krb5_data *e_data)
735 {
736         e_data->data = malloc(4);
737         if (e_data->data == NULL) {
738                 return ENOMEM;
739         }
740         e_data->length = 4;
741
742         PUSH_LE_U32(e_data->data, 0, supported_etypes);
743
744         return 0;
745 }
746
747 static krb5_error_code samba_wdc_finalize_reply(void *priv,
748                                                 astgs_request_t r)
749 {
750         struct samba_kdc_entry *server_kdc_entry;
751         uint32_t supported_enctypes;
752
753         server_kdc_entry = talloc_get_type(kdc_request_get_server(r)->context, struct samba_kdc_entry);
754
755         /*
756          * If the canonicalize flag is set, add PA-SUPPORTED-ENCTYPES padata
757          * type to indicate what encryption types the server supports.
758          */
759         supported_enctypes = server_kdc_entry->supported_enctypes;
760         if (kdc_request_get_req(r)->req_body.kdc_options.canonicalize && supported_enctypes != 0) {
761                 krb5_error_code ret;
762
763                 PA_DATA md;
764
765                 ret = samba_kdc_build_supported_etypes(supported_enctypes, &md.padata_value);
766                 if (ret != 0) {
767                         return ret;
768                 }
769
770                 md.padata_type = KRB5_PADATA_SUPPORTED_ETYPES;
771
772                 ret = kdc_request_add_encrypted_padata(r, &md);
773                 if (ret != 0) {
774                         /*
775                          * So we do not leak the allocated
776                          * memory on kd in the error case
777                          */
778                         krb5_data_free(&md.padata_value);
779                 }
780         }
781
782         return 0;
783 }
784
785 static krb5_error_code samba_wdc_plugin_init(krb5_context context, void **ptr)
786 {
787         *ptr = NULL;
788         return 0;
789 }
790
791 static void samba_wdc_plugin_fini(void *ptr)
792 {
793         return;
794 }
795
796 static krb5_error_code samba_wdc_referral_policy(void *priv,
797                                                  astgs_request_t r)
798 {
799         return kdc_request_get_error_code((kdc_request_t)r);
800 }
801
802 struct krb5plugin_kdc_ftable kdc_plugin_table = {
803         .minor_version = KRB5_PLUGIN_KDC_VERSION_11,
804         .init = samba_wdc_plugin_init,
805         .fini = samba_wdc_plugin_fini,
806         .pac_verify = samba_wdc_verify_pac,
807         .pac_update = samba_wdc_reget_pac,
808         .client_access = samba_wdc_check_client_access,
809         .finalize_reply = samba_wdc_finalize_reply,
810         .pac_generate = samba_wdc_get_pac,
811         .referral_policy = samba_wdc_referral_policy,
812 };
813
814