2 # Unix SMB/CIFS implementation.
3 # Copyright (C) Stefan Metzmacher 2020
4 # Copyright (C) Catalyst.Net Ltd
6 # This program is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 3 of the License, or
9 # (at your option) any later version.
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
16 # You should have received a copy of the GNU General Public License
17 # along with this program. If not, see <http://www.gnu.org/licenses/>.
23 sys
.path
.insert(0, 'bin/python')
24 os
.environ
['PYTHONUNBUFFERED'] = '1'
26 from functools
import partial
28 from samba
import generate_random_password
29 from samba
.dcerpc
import krb5pac
30 from samba
.sd_utils
import SDUtils
32 from samba
.tests
.krb5
.kdc_base_test
import KDCBaseTest
33 from samba
.tests
.krb5
.rfc4120_constants
import (
39 KPASSWD_INITIAL_FLAG_NEEDED
,
47 global_asn1_print
= False
48 global_hexdump
= False
51 # Note: these tests do not pass on Windows, which returns different error codes
52 # to the ones we have chosen, and does not always return additional error data.
53 class KpasswdTests(KDCBaseTest
):
57 self
.do_asn1_print
= global_asn1_print
58 self
.do_hexdump
= global_hexdump
60 samdb
= self
.get_samdb()
62 # Get the old 'dSHeuristics' if it was set
63 dsheuristics
= samdb
.get_dsheuristics()
65 # Reset the 'dSHeuristics' as they were before
66 self
.addCleanup(samdb
.set_dsheuristics
, dsheuristics
)
68 # Set the 'dSHeuristics' to activate the correct 'userPassword'
70 samdb
.set_dsheuristics('000000001')
72 # Get the old 'minPwdAge'
73 minPwdAge
= samdb
.get_minPwdAge()
75 # Reset the 'minPwdAge' as it was before
76 self
.addCleanup(samdb
.set_minPwdAge
, minPwdAge
)
78 # Set it temporarily to '0'
79 samdb
.set_minPwdAge('0')
81 def _get_creds(self
, expired
=False):
83 'expired_password': expired
87 creds
= self
.get_cached_creds(account_type
=self
.AccountType
.USER
,
93 def get_ticket_lifetime(self
, ticket
):
94 enc_part
= ticket
.ticket_private
96 authtime
= enc_part
['authtime']
97 starttime
= enc_part
.get('starttime', authtime
)
98 endtime
= enc_part
['endtime']
100 starttime
= self
.get_EpochFromKerberosTime(starttime
)
101 endtime
= self
.get_EpochFromKerberosTime(endtime
)
103 return endtime
- starttime
105 # Test setting a password with kpasswd.
106 def test_kpasswd_set(self
):
107 # Create an account for testing.
108 creds
= self
._get
_creds
()
110 # Get an initial ticket to kpasswd.
111 ticket
= self
.get_tgt(creds
, sname
=self
.get_kpasswd_sname(),
114 expected_code
= KPASSWD_SUCCESS
115 expected_msg
= b
'Password changed'
118 new_password
= generate_random_password(32, 32)
119 self
.kpasswd_exchange(ticket
,
123 mode
=self
.KpasswdMode
.SET
)
125 # Test the newly set password.
126 creds
.update_password(new_password
)
127 self
.get_tgt(creds
, fresh
=True)
129 # Test changing a password with kpasswd.
130 def test_kpasswd_change(self
):
131 # Create an account for testing.
132 creds
= self
._get
_creds
()
134 # Get an initial ticket to kpasswd.
135 ticket
= self
.get_tgt(creds
, sname
=self
.get_kpasswd_sname(),
138 expected_code
= KPASSWD_SUCCESS
139 expected_msg
= b
'Password changed'
141 # Change the password.
142 new_password
= generate_random_password(32, 32)
143 self
.kpasswd_exchange(ticket
,
147 mode
=self
.KpasswdMode
.CHANGE
)
149 # Test the newly set password.
150 creds
.update_password(new_password
)
151 self
.get_tgt(creds
, fresh
=True)
153 # Test kpasswd without setting the canonicalize option.
154 def test_kpasswd_no_canonicalize(self
):
155 # Create an account for testing.
156 creds
= self
._get
_creds
()
158 sname
= self
.get_kpasswd_sname()
160 # Get an initial ticket to kpasswd.
161 ticket
= self
.get_tgt(creds
, sname
=sname
,
164 expected_code
= KPASSWD_SUCCESS
165 expected_msg
= b
'Password changed'
168 new_password
= generate_random_password(32, 32)
169 self
.kpasswd_exchange(ticket
,
173 mode
=self
.KpasswdMode
.SET
)
175 creds
.update_password(new_password
)
177 # Get an initial ticket to kpasswd.
178 ticket
= self
.get_tgt(creds
, sname
=sname
,
181 # Change the password.
182 new_password
= generate_random_password(32, 32)
183 self
.kpasswd_exchange(ticket
,
187 mode
=self
.KpasswdMode
.CHANGE
)
189 # Test kpasswd with the canonicalize option reset and a non-canonical
190 # (by conversion to title case) realm.
191 def test_kpasswd_no_canonicalize_realm_case(self
):
192 # Create an account for testing.
193 creds
= self
._get
_creds
()
195 sname
= self
.get_kpasswd_sname()
196 realm
= creds
.get_realm().capitalize() # We use a title-cased realm.
198 # Get an initial ticket to kpasswd.
199 ticket
= self
.get_tgt(creds
, sname
=sname
,
203 expected_code
= KPASSWD_SUCCESS
204 expected_msg
= b
'Password changed'
207 new_password
= generate_random_password(32, 32)
208 self
.kpasswd_exchange(ticket
,
212 mode
=self
.KpasswdMode
.SET
)
214 creds
.update_password(new_password
)
216 # Get an initial ticket to kpasswd.
217 ticket
= self
.get_tgt(creds
, sname
=sname
,
221 # Change the password.
222 new_password
= generate_random_password(32, 32)
223 self
.kpasswd_exchange(ticket
,
227 mode
=self
.KpasswdMode
.CHANGE
)
229 # Test kpasswd with the canonicalize option set.
230 def test_kpasswd_canonicalize(self
):
231 # Create an account for testing.
232 creds
= self
._get
_creds
()
234 # Get an initial ticket to kpasswd. We set the canonicalize flag here.
235 ticket
= self
.get_tgt(creds
, sname
=self
.get_kpasswd_sname(),
236 kdc_options
='canonicalize')
238 expected_code
= KPASSWD_SUCCESS
239 expected_msg
= b
'Password changed'
242 new_password
= generate_random_password(32, 32)
243 self
.kpasswd_exchange(ticket
,
247 mode
=self
.KpasswdMode
.SET
)
249 creds
.update_password(new_password
)
251 # Get an initial ticket to kpasswd. We set the canonicalize flag here.
252 ticket
= self
.get_tgt(creds
, sname
=self
.get_kpasswd_sname(),
253 kdc_options
='canonicalize')
255 # Change the password.
256 new_password
= generate_random_password(32, 32)
257 self
.kpasswd_exchange(ticket
,
261 mode
=self
.KpasswdMode
.CHANGE
)
263 # Test kpasswd with the canonicalize option set and a non-canonical (by
264 # conversion to title case) realm.
265 def test_kpasswd_canonicalize_realm_case(self
):
266 # Create an account for testing.
267 creds
= self
._get
_creds
()
269 sname
= self
.get_kpasswd_sname()
270 realm
= creds
.get_realm().capitalize() # We use a title-cased realm.
272 # Get an initial ticket to kpasswd. We set the canonicalize flag here.
273 ticket
= self
.get_tgt(creds
, sname
=sname
,
275 kdc_options
='canonicalize')
277 expected_code
= KPASSWD_SUCCESS
278 expected_msg
= b
'Password changed'
281 new_password
= generate_random_password(32, 32)
282 self
.kpasswd_exchange(ticket
,
286 mode
=self
.KpasswdMode
.SET
)
288 creds
.update_password(new_password
)
290 # Get an initial ticket to kpasswd. We set the canonicalize flag here.
291 ticket
= self
.get_tgt(creds
, sname
=sname
,
293 kdc_options
='canonicalize')
295 # Change the password.
296 new_password
= generate_random_password(32, 32)
297 self
.kpasswd_exchange(ticket
,
301 mode
=self
.KpasswdMode
.CHANGE
)
303 # Test kpasswd rejects a password that does not meet complexity
305 def test_kpasswd_too_weak(self
):
306 # Create an account for testing.
307 creds
= self
._get
_creds
()
309 # Get an initial ticket to kpasswd.
310 ticket
= self
.get_tgt(creds
, sname
=self
.get_kpasswd_sname(),
313 expected_code
= KPASSWD_SOFTERROR
314 expected_msg
= b
'Password does not meet complexity requirements'
317 new_password
= 'password'
318 self
.kpasswd_exchange(ticket
,
322 mode
=self
.KpasswdMode
.SET
)
324 # Change the password.
325 self
.kpasswd_exchange(ticket
,
329 mode
=self
.KpasswdMode
.CHANGE
)
331 # Test kpasswd rejects an empty new password.
332 def test_kpasswd_empty(self
):
333 # Create an account for testing.
334 creds
= self
._get
_creds
()
336 # Get an initial ticket to kpasswd.
337 ticket
= self
.get_tgt(creds
, sname
=self
.get_kpasswd_sname(),
340 expected_code
= KPASSWD_SOFTERROR
, KPASSWD_HARDERROR
341 expected_msg
= (b
'Password too short, password must be at least 7 '
343 b
'String conversion failed!')
347 self
.kpasswd_exchange(ticket
,
351 mode
=self
.KpasswdMode
.SET
)
353 expected_code
= KPASSWD_HARDERROR
354 expected_msg
= b
'String conversion failed!'
356 # Change the password.
357 self
.kpasswd_exchange(ticket
,
361 mode
=self
.KpasswdMode
.CHANGE
)
363 # Test kpasswd rejects a request that does not include a random sequence
365 def test_kpasswd_no_seq_number(self
):
366 # Create an account for testing.
367 creds
= self
._get
_creds
()
369 # Get an initial ticket to kpasswd.
370 ticket
= self
.get_tgt(creds
, sname
=self
.get_kpasswd_sname(),
373 expected_code
= KPASSWD_HARDERROR
374 expected_msg
= b
'gensec_unwrap failed - NT_STATUS_ACCESS_DENIED\n'
377 new_password
= generate_random_password(32, 32)
378 self
.kpasswd_exchange(ticket
,
382 mode
=self
.KpasswdMode
.SET
,
383 send_seq_number
=False)
385 # Change the password.
386 self
.kpasswd_exchange(ticket
,
390 mode
=self
.KpasswdMode
.CHANGE
,
391 send_seq_number
=False)
393 # Test kpasswd rejects a ticket issued by an RODC.
394 def test_kpasswd_from_rodc(self
):
395 # Create an account for testing.
396 creds
= self
._get
_creds
()
398 # Get an initial ticket to kpasswd.
399 ticket
= self
.get_tgt(creds
, sname
=self
.get_kpasswd_sname(),
402 # Have the ticket be issued by the RODC.
403 ticket
= self
.issued_by_rodc(ticket
)
405 expected_code
= KPASSWD_HARDERROR
406 expected_msg
= b
'gensec_update failed - NT_STATUS_LOGON_FAILURE\n'
409 new_password
= generate_random_password(32, 32)
410 self
.kpasswd_exchange(ticket
,
414 mode
=self
.KpasswdMode
.SET
)
416 # Change the password.
417 self
.kpasswd_exchange(ticket
,
421 mode
=self
.KpasswdMode
.CHANGE
)
423 # Test setting a password, specifying the principal of the target user.
424 def test_kpasswd_set_target_princ_only(self
):
425 # Create an account for testing.
426 creds
= self
._get
_creds
()
427 username
= creds
.get_username()
429 cname
= self
.PrincipalName_create(name_type
=NT_PRINCIPAL
,
430 names
=username
.split('/'))
432 # Get an initial ticket to kpasswd.
433 ticket
= self
.get_tgt(creds
, sname
=self
.get_kpasswd_sname(),
436 expected_code
= KPASSWD_MALFORMED
437 expected_msg
= (b
'Realm and principal must be both present, or '
439 b
'Failed to decode packet')
441 # Change the password.
442 new_password
= generate_random_password(32, 32)
443 self
.kpasswd_exchange(ticket
,
447 mode
=self
.KpasswdMode
.SET
,
450 # Test that kpasswd rejects a password set specifying only the realm of the
452 def test_kpasswd_set_target_realm_only(self
):
453 # Create an account for testing.
454 creds
= self
._get
_creds
()
456 # Get an initial ticket to kpasswd.
457 ticket
= self
.get_tgt(creds
, sname
=self
.get_kpasswd_sname(),
460 expected_code
= KPASSWD_MALFORMED
, KPASSWD_ACCESSDENIED
461 expected_msg
= (b
'Realm and principal must be both present, or '
463 b
'Failed to decode packet',
464 b
'No such user when changing password')
466 # Change the password.
467 new_password
= generate_random_password(32, 32)
468 self
.kpasswd_exchange(ticket
,
472 mode
=self
.KpasswdMode
.SET
,
473 target_realm
=creds
.get_realm())
475 # Show that a user cannot set a password, specifying both principal and
476 # realm of the target user, without having control access.
477 def test_kpasswd_set_target_princ_and_realm_no_access(self
):
478 # Create an account for testing.
479 creds
= self
._get
_creds
()
480 username
= creds
.get_username()
482 cname
= self
.PrincipalName_create(name_type
=NT_PRINCIPAL
,
483 names
=username
.split('/'))
485 # Get an initial ticket to kpasswd.
486 ticket
= self
.get_tgt(creds
, sname
=self
.get_kpasswd_sname(),
489 expected_code
= KPASSWD_ACCESSDENIED
490 expected_msg
= b
'Not permitted to change password'
492 # Change the password.
493 new_password
= generate_random_password(32, 32)
494 self
.kpasswd_exchange(ticket
,
498 mode
=self
.KpasswdMode
.SET
,
500 target_realm
=creds
.get_realm())
502 # Test setting a password, specifying both principal and realm of the
503 # target user, when the user has control access on their account.
504 def test_kpasswd_set_target_princ_and_realm_access(self
):
505 # Create an account for testing.
506 creds
= self
._get
_creds
()
507 username
= creds
.get_username()
508 tgt
= self
.get_tgt(creds
)
510 cname
= self
.PrincipalName_create(name_type
=NT_PRINCIPAL
,
511 names
=username
.split('/'))
513 samdb
= self
.get_samdb()
514 sd_utils
= SDUtils(samdb
)
516 user_dn
= creds
.get_dn()
517 user_sid
= creds
.get_sid()
519 # Give the user control access on their account.
520 ace
= f
'(A;;CR;;;{user_sid})'
521 sd_utils
.dacl_add_ace(user_dn
, ace
)
523 # Get a non-initial ticket to kpasswd. Since we have the right to
524 # change the account's password, we don't need an initial ticket.
525 krbtgt_creds
= self
.get_krbtgt_creds()
526 ticket
= self
.get_service_ticket(tgt
,
529 target_name
='changepw',
532 expected_code
= KPASSWD_SUCCESS
533 expected_msg
= b
'Password changed'
535 # Change the password.
536 new_password
= generate_random_password(32, 32)
537 self
.kpasswd_exchange(ticket
,
541 mode
=self
.KpasswdMode
.SET
,
543 target_realm
=creds
.get_realm())
545 # Test setting a password when the existing password has expired.
546 def test_kpasswd_set_expired_password(self
):
547 # Create an account for testing, with an expired password.
548 creds
= self
._get
_creds
(expired
=True)
550 # Get an initial ticket to kpasswd.
551 ticket
= self
.get_tgt(creds
, sname
=self
.get_kpasswd_sname(),
554 expected_code
= KPASSWD_SUCCESS
555 expected_msg
= b
'Password changed'
558 new_password
= generate_random_password(32, 32)
559 self
.kpasswd_exchange(ticket
,
563 mode
=self
.KpasswdMode
.SET
)
565 # Test changing a password when the existing password has expired.
566 def test_kpasswd_change_expired_password(self
):
567 # Create an account for testing, with an expired password.
568 creds
= self
._get
_creds
(expired
=True)
570 # Get an initial ticket to kpasswd.
571 ticket
= self
.get_tgt(creds
, sname
=self
.get_kpasswd_sname(),
574 expected_code
= KPASSWD_SUCCESS
575 expected_msg
= b
'Password changed'
577 # Change the password.
578 new_password
= generate_random_password(32, 32)
579 self
.kpasswd_exchange(ticket
,
583 mode
=self
.KpasswdMode
.CHANGE
)
585 # Check the lifetime of a kpasswd ticket is not more than two minutes.
586 def test_kpasswd_ticket_lifetime(self
):
587 # Create an account for testing.
588 creds
= self
._get
_creds
()
590 # Get an initial ticket to kpasswd.
591 ticket
= self
.get_tgt(creds
, sname
=self
.get_kpasswd_sname(),
594 # Check the lifetime of the ticket is equal to two minutes.
595 lifetime
= self
.get_ticket_lifetime(ticket
)
596 self
.assertEqual(2 * 60, lifetime
)
598 # Ensure we cannot perform a TGS-REQ with a kpasswd ticket.
599 def test_kpasswd_ticket_tgs(self
):
600 creds
= self
.get_client_creds()
602 # Get an initial ticket to kpasswd.
603 ticket
= self
.get_tgt(creds
, sname
=self
.get_kpasswd_sname(),
606 # Change the sname of the ticket to match that of a TGT.
607 realm
= creds
.get_realm()
608 krbtgt_sname
= self
.PrincipalName_create(name_type
=NT_SRV_INST
,
609 names
=['krbtgt', realm
])
610 ticket
.set_sname(krbtgt_sname
)
612 # Try to use that ticket to get a service ticket.
613 service_creds
= self
.get_service_creds()
615 # This fails due to missing REQUESTER_SID buffer.
616 self
._make
_tgs
_request
(creds
, service_creds
, ticket
,
617 expect_error
=(KDC_ERR_TGT_REVOKED
,
618 KDC_ERR_TKT_EXPIRED
))
620 # Ensure we cannot perform a TGS-REQ with a kpasswd ticket containing a
621 # requester SID and having a remaining lifetime of two minutes.
622 def test_kpasswd_ticket_requester_sid_tgs(self
):
623 creds
= self
.get_client_creds()
625 # Get an initial ticket to kpasswd.
626 ticket
= self
.get_tgt(creds
, sname
=self
.get_kpasswd_sname(),
629 # Change the sname of the ticket to match that of a TGT.
630 realm
= creds
.get_realm()
631 krbtgt_sname
= self
.PrincipalName_create(name_type
=NT_SRV_INST
,
632 names
=['krbtgt', realm
])
633 ticket
.set_sname(krbtgt_sname
)
635 # Modify the ticket to add a requester SID and give it two minutes to
637 ticket
= self
.modify_lifetime(ticket
,
639 requester_sid
=creds
.get_sid())
641 # Try to use that ticket to get a service ticket.
642 service_creds
= self
.get_service_creds()
644 # This fails due to the lifetime being too short.
645 self
._make
_tgs
_request
(creds
, service_creds
, ticket
,
646 expect_error
=KDC_ERR_TKT_EXPIRED
)
648 # Show we can perform a TGS-REQ with a kpasswd ticket containing a
649 # requester SID if the remaining lifetime exceeds two minutes.
650 def test_kpasswd_ticket_requester_sid_lifetime_tgs(self
):
651 creds
= self
.get_client_creds()
653 # Get an initial ticket to kpasswd.
654 ticket
= self
.get_tgt(creds
, sname
=self
.get_kpasswd_sname(),
657 # Change the sname of the ticket to match that of a TGT.
658 realm
= creds
.get_realm()
659 krbtgt_sname
= self
.PrincipalName_create(name_type
=NT_SRV_INST
,
660 names
=['krbtgt', realm
])
661 ticket
.set_sname(krbtgt_sname
)
663 # Modify the ticket to add a requester SID and give it two minutes and
664 # ten seconds to live.
665 ticket
= self
.modify_lifetime(ticket
,
666 lifetime
=2 * 60 + 10,
667 requester_sid
=creds
.get_sid())
669 # Try to use that ticket to get a service ticket.
670 service_creds
= self
.get_service_creds()
673 self
._make
_tgs
_request
(creds
, service_creds
, ticket
,
676 # Show that we cannot provide a TGT to kpasswd to change the password.
677 def test_kpasswd_tgt(self
):
678 # Create an account for testing, and get a TGT.
679 creds
= self
._get
_creds
()
680 tgt
= self
.get_tgt(creds
)
682 # Change the sname of the ticket to match that of kadmin/changepw.
683 tgt
.set_sname(self
.get_kpasswd_sname())
685 expected_code
= KPASSWD_AUTHERROR
686 expected_msg
= b
'A TGT may not be used as a ticket to kpasswd'
689 new_password
= generate_random_password(32, 32)
690 self
.kpasswd_exchange(tgt
,
694 mode
=self
.KpasswdMode
.SET
)
696 # Change the password.
697 self
.kpasswd_exchange(tgt
,
701 mode
=self
.KpasswdMode
.CHANGE
)
703 # Show that we cannot provide a TGT to kpasswd that was obtained with a
704 # single‐component principal.
705 def test_kpasswd_tgt_single_component_krbtgt(self
):
706 # Create an account for testing.
707 creds
= self
._get
_creds
()
709 # Create a single‐component principal of the form ‘krbtgt@REALM’.
710 sname
= self
.PrincipalName_create(name_type
=NT_PRINCIPAL
,
713 # Don’t request canonicalization.
714 kdc_options
= 'forwardable,renewable,renewable-ok'
717 tgt
= self
.get_tgt(creds
, sname
=sname
, kdc_options
=kdc_options
)
719 # Change the sname of the ticket to match that of kadmin/changepw.
720 tgt
.set_sname(self
.get_kpasswd_sname())
722 expected_code
= KPASSWD_AUTHERROR
723 expected_msg
= b
'A TGT may not be used as a ticket to kpasswd'
726 new_password
= generate_random_password(32, 32)
727 self
.kpasswd_exchange(tgt
,
731 mode
=self
.KpasswdMode
.SET
)
733 # Change the password.
734 self
.kpasswd_exchange(tgt
,
738 mode
=self
.KpasswdMode
.CHANGE
)
740 # Test that kpasswd rejects requests with a service ticket.
741 def test_kpasswd_non_initial(self
):
742 # Create an account for testing, and get a TGT.
743 creds
= self
._get
_creds
()
744 tgt
= self
.get_tgt(creds
)
746 # Get a non-initial ticket to kpasswd.
747 krbtgt_creds
= self
.get_krbtgt_creds()
748 ticket
= self
.get_service_ticket(tgt
,
751 target_name
='changepw',
754 expected_code
= KPASSWD_INITIAL_FLAG_NEEDED
755 expected_msg
= b
'Expected an initial ticket'
758 new_password
= generate_random_password(32, 32)
759 self
.kpasswd_exchange(ticket
,
763 mode
=self
.KpasswdMode
.SET
)
765 # Change the password.
766 self
.kpasswd_exchange(ticket
,
770 mode
=self
.KpasswdMode
.CHANGE
)
772 # Show that kpasswd accepts requests with a service ticket modified to set
773 # the 'initial' flag.
774 def test_kpasswd_initial(self
):
775 # Create an account for testing, and get a TGT.
776 creds
= self
._get
_creds
()
778 krbtgt_creds
= self
.get_krbtgt_creds()
780 # Get a service ticket, and modify it to set the 'initial' flag.
782 tgt
= self
.get_tgt(creds
, fresh
=True)
784 # Get a non-initial ticket to kpasswd.
785 ticket
= self
.get_service_ticket(tgt
,
788 target_name
='changepw',
792 set_initial_flag
= partial(self
.modify_ticket_flag
, flag
='initial',
795 checksum_keys
= self
.get_krbtgt_checksum_key()
796 return self
.modified_ticket(ticket
,
797 modify_fn
=set_initial_flag
,
798 checksum_keys
=checksum_keys
)
800 expected_code
= KPASSWD_SUCCESS
801 expected_msg
= b
'Password changed'
803 ticket
= get_ticket()
806 new_password
= generate_random_password(32, 32)
807 self
.kpasswd_exchange(ticket
,
811 mode
=self
.KpasswdMode
.SET
)
813 creds
.update_password(new_password
)
814 ticket
= get_ticket()
816 # Change the password.
817 new_password
= generate_random_password(32, 32)
818 self
.kpasswd_exchange(ticket
,
822 mode
=self
.KpasswdMode
.CHANGE
)
824 # Test that kpasswd rejects requests where the ticket is encrypted with a
825 # key other than the krbtgt's.
826 def test_kpasswd_wrong_key(self
):
827 # Create an account for testing.
828 creds
= self
._get
_creds
()
830 sname
= self
.get_kpasswd_sname()
832 # Get an initial ticket to kpasswd.
833 ticket
= self
.get_tgt(creds
, sname
=sname
,
836 # Get a key belonging to the Administrator account.
837 admin_creds
= self
.get_admin_creds()
838 admin_key
= self
.TicketDecryptionKey_from_creds(admin_creds
)
839 self
.assertIsNotNone(admin_key
.kvno
,
840 'a kvno is required to tell the DB '
841 'which key to look up.')
843 krb5pac
.PAC_TYPE_KDC_CHECKSUM
: admin_key
,
846 # Re-encrypt the ticket using the Administrator's key.
847 ticket
= self
.modified_ticket(ticket
,
848 new_ticket_key
=admin_key
,
849 checksum_keys
=checksum_keys
)
851 # Set the sname of the ticket to that of the Administrator account.
852 admin_sname
= self
.PrincipalName_create(name_type
=NT_PRINCIPAL
,
853 names
=['Administrator'])
854 ticket
.set_sname(admin_sname
)
856 expected_code
= KPASSWD_HARDERROR
857 expected_msg
= b
'gensec_update failed - NT_STATUS_LOGON_FAILURE\n'
860 new_password
= generate_random_password(32, 32)
861 self
.kpasswd_exchange(ticket
,
865 mode
=self
.KpasswdMode
.SET
)
867 # Change the password.
868 self
.kpasswd_exchange(ticket
,
872 mode
=self
.KpasswdMode
.CHANGE
)
874 def test_kpasswd_wrong_key_service(self
):
875 # Create an account for testing.
876 creds
= self
.get_cached_creds(account_type
=self
.AccountType
.COMPUTER
,
879 sname
= self
.get_kpasswd_sname()
881 # Get an initial ticket to kpasswd.
882 ticket
= self
.get_tgt(creds
, sname
=sname
,
885 # Get a key belonging to our account.
886 our_key
= self
.TicketDecryptionKey_from_creds(creds
)
887 self
.assertIsNotNone(our_key
.kvno
,
888 'a kvno is required to tell the DB '
889 'which key to look up.')
891 krb5pac
.PAC_TYPE_KDC_CHECKSUM
: our_key
,
894 # Re-encrypt the ticket using our key.
895 ticket
= self
.modified_ticket(ticket
,
896 new_ticket_key
=our_key
,
897 checksum_keys
=checksum_keys
)
899 # Set the sname of the ticket to that of our account.
900 username
= creds
.get_username()
901 sname
= self
.PrincipalName_create(name_type
=NT_PRINCIPAL
,
902 names
=username
.split('/'))
903 ticket
.set_sname(sname
)
905 expected_code
= KPASSWD_HARDERROR
906 expected_msg
= b
'gensec_update failed - NT_STATUS_LOGON_FAILURE\n'
909 new_password
= generate_random_password(32, 32)
910 self
.kpasswd_exchange(ticket
,
914 mode
=self
.KpasswdMode
.SET
)
916 # Change the password.
917 self
.kpasswd_exchange(ticket
,
921 mode
=self
.KpasswdMode
.CHANGE
)
923 # Test that kpasswd rejects requests where the ticket is encrypted with a
924 # key belonging to a server account other than the krbtgt.
925 def test_kpasswd_wrong_key_server(self
):
926 # Create an account for testing.
927 creds
= self
._get
_creds
()
929 sname
= self
.get_kpasswd_sname()
931 # Get an initial ticket to kpasswd.
932 ticket
= self
.get_tgt(creds
, sname
=sname
,
935 # Get a key belonging to the DC's account.
936 dc_creds
= self
.get_dc_creds()
937 dc_key
= self
.TicketDecryptionKey_from_creds(dc_creds
)
938 self
.assertIsNotNone(dc_key
.kvno
,
939 'a kvno is required to tell the DB '
940 'which key to look up.')
942 krb5pac
.PAC_TYPE_KDC_CHECKSUM
: dc_key
,
945 # Re-encrypt the ticket using the DC's key.
946 ticket
= self
.modified_ticket(ticket
,
947 new_ticket_key
=dc_key
,
948 checksum_keys
=checksum_keys
)
950 # Set the sname of the ticket to that of the DC's account.
951 dc_username
= dc_creds
.get_username()
952 dc_sname
= self
.PrincipalName_create(name_type
=NT_PRINCIPAL
,
953 names
=dc_username
.split('/'))
954 ticket
.set_sname(dc_sname
)
956 expected_code
= KPASSWD_HARDERROR
957 expected_msg
= b
'gensec_update failed - NT_STATUS_LOGON_FAILURE\n'
960 new_password
= generate_random_password(32, 32)
961 self
.kpasswd_exchange(ticket
,
965 mode
=self
.KpasswdMode
.SET
)
967 # Change the password.
968 self
.kpasswd_exchange(ticket
,
972 mode
=self
.KpasswdMode
.CHANGE
)
975 if __name__
== '__main__':
976 global_asn1_print
= False
977 global_hexdump
= False