Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
42d0bb0805 | ||
|
|
024b410710 | ||
|
|
96ea6f94c8 | ||
|
|
20c03a3e89 | ||
|
|
bb24703a18 | ||
|
|
67a9d537e3 | ||
|
|
f2fc367fb0 | ||
|
|
e8c410ae5f | ||
|
|
6ae52a2fb4 | ||
|
|
f33a0e8e71 | ||
|
|
7a5a10b6bf | ||
|
|
3b38efe758 | ||
|
|
cf84a22228 | ||
|
|
c7da7e0dc9 | ||
|
|
f5c7237204 | ||
|
|
ed813fe6f0 | ||
|
|
21c6ccc982 | ||
|
|
d0684a7ecf | ||
|
|
5cc7941f30 | ||
|
|
f37c2eb878 | ||
|
|
1fc63e2b51 | ||
|
|
84751a26a9 | ||
|
|
8046023fc4 | ||
|
|
ee96c129a6 | ||
|
|
81401e6c01 | ||
|
|
e83d949c7f | ||
|
|
ffba69648a | ||
|
|
aadb8051d4 | ||
|
|
7b5f3d7971 | ||
|
|
fc02904340 | ||
|
|
7f9c890c04 | ||
|
|
531bd05de9 | ||
|
|
38b83c2b93 | ||
|
|
f03a96a7b9 | ||
|
|
0e4fbc3b0d | ||
|
|
2ddca5d5d5 | ||
|
|
fdd471d55c | ||
|
|
9f88188204 | ||
|
|
d1a485a435 | ||
|
|
6eb6a92930 | ||
|
|
de940802bb | ||
|
|
85a853ba93 | ||
|
|
8b703150a4 | ||
|
|
051d61fdc3 | ||
|
|
ab47696fa6 | ||
|
|
eeade50933 | ||
|
|
6c53a22a2c | ||
|
|
4ea1ad6aca | ||
|
|
7ab1bcb2d3 | ||
|
|
1b278de4ab | ||
|
|
efa5719363 | ||
|
|
06c02f5f2c | ||
|
|
a8e75bbb77 | ||
|
|
d635d70110 | ||
|
|
6fe268af5b | ||
|
|
2f902efd0e | ||
|
|
90b22ff888 | ||
|
|
52ea4ad46e | ||
|
|
48ff7da5cb | ||
|
|
9e364910f5 | ||
|
|
60c127d197 | ||
|
|
4521fe5f91 | ||
|
|
58154be74f | ||
|
|
467ec04f93 | ||
|
|
c8e3fdeb00 | ||
|
|
4d51446bd3 | ||
|
|
09e66dc936 | ||
|
|
9de053ef02 | ||
|
|
ebccaac3cf | ||
|
|
584d0cecbc | ||
|
|
5b3735b09d | ||
|
|
4a61184da6 | ||
|
|
f77c0a573c | ||
|
|
ea37593786 | ||
|
|
407408e9a8 | ||
|
|
c250b1a7e4 | ||
|
|
b3789876b8 | ||
|
|
69c6a817ce | ||
|
|
1223016ef2 | ||
|
|
5368120805 | ||
|
|
84eed2a67f | ||
|
|
145e33174d | ||
|
|
fd0f432fec | ||
|
|
9e861693fc | ||
|
|
6cc0a0b9a8 | ||
|
|
c325f9c045 | ||
|
|
902c8b0bae | ||
|
|
e386e22046 | ||
|
|
b6131b5737 | ||
|
|
08f1e6f2fd | ||
|
|
ab5465639d | ||
|
|
71d886f071 | ||
|
|
3608b2b63d | ||
|
|
295ac6385c | ||
|
|
6fc35156d9 | ||
|
|
9dc57ef77e | ||
|
|
47920e78c8 | ||
|
|
544652aae4 | ||
|
|
f225b3df17 | ||
|
|
b34525c76e | ||
|
|
7d54a6daaf | ||
|
|
6af8577d58 | ||
|
|
c861ce5a16 | ||
|
|
6b6c1879c5 | ||
|
|
879a937ddd | ||
|
|
bcd8d2d90a | ||
|
|
ea0bf4020c | ||
|
|
b9ec2fb0a9 | ||
|
|
31fda79a0e | ||
|
|
c6f2d0212b | ||
|
|
b63103c88a | ||
|
|
1ec875c6fe | ||
|
|
f8798b3e16 | ||
|
|
87ecca0f18 | ||
|
|
c6dd21f04e | ||
|
|
31d66bac64 | ||
|
|
6b894f28b5 | ||
|
|
36dbc6b025 | ||
|
|
06a8791b9b | ||
|
|
b89aa91977 | ||
|
|
0708f603e2 | ||
|
|
d0c489e282 | ||
|
|
93622005ba | ||
|
|
7ad3b489f6 | ||
|
|
a99091adc0 | ||
|
|
82c0b19acc | ||
|
|
d9efa728c5 | ||
|
|
f658a264f9 | ||
|
|
e3234708ac | ||
|
|
73d52a6135 | ||
|
|
34f28f06db | ||
|
|
e6078c639c | ||
|
|
cba3094c9a | ||
|
|
bb8dd0bfcd | ||
|
|
9e1e22d46b | ||
|
|
6c6b9354b5 | ||
|
|
cce8dc4da8 | ||
|
|
f9f96ac4a8 | ||
|
|
f78d25fc97 | ||
|
|
dda223668a | ||
|
|
d16c34997f | ||
|
|
6b0f6ff19e | ||
|
|
182dca38c2 | ||
|
|
8084b94c17 | ||
|
|
6294b93e14 | ||
|
|
cbe1873591 | ||
|
|
1df2abbd5f | ||
|
|
fc7c2cb624 | ||
|
|
ca6604b58b | ||
|
|
5d3c6b761b | ||
|
|
a9bb811296 | ||
|
|
38d0e74b6d | ||
|
|
6cc668ffeb | ||
|
|
a57b665be0 | ||
|
|
adf95dcf86 | ||
|
|
64861a0cf9 | ||
|
|
f9a1d74f5e | ||
|
|
557f0a5639 | ||
|
|
e431ce0ce7 | ||
|
|
23b224d7ad | ||
|
|
1d2897e3d7 | ||
|
|
c3d228d4a3 | ||
|
|
ca776b6a9c | ||
|
|
bf5c9892e9 | ||
|
|
05f1bf9e2c | ||
|
|
04ac64a4ed | ||
|
|
d8a4bde2c6 | ||
|
|
e60072fe89 | ||
|
|
ee6ff01b46 | ||
|
|
9ad27c954e | ||
|
|
558a7de8b7 | ||
|
|
f4a1696a3b | ||
|
|
f2befb4944 | ||
|
|
7b2ac6a293 | ||
|
|
99922e9963 | ||
|
|
86aae371fa | ||
|
|
78d86ba060 | ||
|
|
d9afa62814 | ||
|
|
282b551533 | ||
|
|
1ce532d5a8 | ||
|
|
2877cae09f | ||
|
|
c6aae2042d | ||
|
|
da9f2294e6 | ||
|
|
f4504e1e91 | ||
|
|
bc3085cd01 | ||
|
|
049a56d603 | ||
|
|
f7a56eb35c | ||
|
|
23d64942e1 | ||
|
|
20b01b09f2 | ||
|
|
cd1a36f233 | ||
|
|
f18db3abd7 | ||
|
|
655b13193a | ||
|
|
2959bec776 | ||
|
|
b4da6896d6 | ||
|
|
cb073530fa | ||
|
|
633ea8ba62 | ||
|
|
da8ab4b54f | ||
|
|
150050eda3 | ||
|
|
3766fb9863 | ||
|
|
33af154b7f | ||
|
|
404fe1018e | ||
|
|
dc3e902b0b | ||
|
|
835929353d | ||
|
|
e5bb0f392a | ||
|
|
4ab602a61f | ||
|
|
e2cab8e900 | ||
|
|
5c91cb2f47 | ||
|
|
5b18239951 | ||
|
|
b29f2e2307 | ||
|
|
2750637544 | ||
|
|
05f8eaea63 | ||
|
|
8b48c5f906 | ||
|
|
908ef6a179 | ||
|
|
dfb5099e7f | ||
|
|
bea9614b12 | ||
|
|
d6aaa626a6 | ||
|
|
f3a3d29117 | ||
|
|
6a59110947 | ||
|
|
b9552bcb71 | ||
|
|
591bbee847 | ||
|
|
9e31e70434 | ||
|
|
c3cb63e582 | ||
|
|
abc48e285e | ||
|
|
41bc6fc382 | ||
|
|
bd04dc28c8 | ||
|
|
7f849956df | ||
|
|
9c470d10a5 | ||
|
|
677d308066 | ||
|
|
ed977a6e82 | ||
|
|
89d945fe6f | ||
|
|
f8dcd78873 | ||
|
|
305fcc25b4 | ||
|
|
4cc6b9cd17 | ||
|
|
2d0a088f93 | ||
|
|
d4ffc53b2a | ||
|
|
75afdfea5d | ||
|
|
9802e852cb | ||
|
|
6cd04875de | ||
|
|
d002a4d7c9 | ||
|
|
22875ea2c6 | ||
|
|
a45a7a20d9 | ||
|
|
e6014a5c19 | ||
|
|
5adc07ae55 | ||
|
|
e399232a78 | ||
|
|
c740cb84ba | ||
|
|
3645543670 | ||
|
|
9b456101a3 | ||
|
|
717ae87a75 | ||
|
|
a5d38ca171 | ||
|
|
2874823c12 | ||
|
|
49c090b976 | ||
|
|
2c0fe1dd92 | ||
|
|
e5a9e46138 | ||
|
|
2a95a05f9e | ||
|
|
54fb1173f9 | ||
|
|
ce811db6be | ||
|
|
e4420624ff | ||
|
|
8ef3d6ce5c | ||
|
|
a44cb09713 | ||
|
|
86b073a7f0 | ||
|
|
13778d88ca | ||
|
|
11877d5903 | ||
|
|
bf1110bda1 | ||
|
|
8d7bd6c6ab | ||
|
|
7f485ba7dc | ||
|
|
020af153db | ||
|
|
8981ede1a2 | ||
|
|
c3bc938650 | ||
|
|
62c44c9e69 | ||
|
|
69ae9febfb | ||
|
|
00f8ddbfd2 | ||
|
|
821259f069 | ||
|
|
a177121af6 | ||
|
|
d1e09c68af | ||
|
|
48846e98e5 | ||
|
|
dc2ab91681 | ||
|
|
6d3d191825 | ||
|
|
0007876f42 | ||
|
|
6cd5a0847a | ||
|
|
2a1d454c74 | ||
|
|
79b08556a4 | ||
|
|
23ead1dc23 | ||
|
|
8a6361dc75 | ||
|
|
1a16130a9a | ||
|
|
3fd5d57ed6 | ||
|
|
25b58e6dea | ||
|
|
9abb50eb1e | ||
|
|
53951ca860 | ||
|
|
d61d1b059c | ||
|
|
5028b391f1 | ||
|
|
5deeee31c0 | ||
|
|
5c8614157d | ||
|
|
7ee2d7d359 | ||
|
|
6bc9e9d06e | ||
|
|
60fe752da4 | ||
|
|
5e17c134aa | ||
|
|
519685823b | ||
|
|
d97d62dead | ||
|
|
f00b52ce6d | ||
|
|
d659d21b43 | ||
|
|
c63fe925fb | ||
|
|
07e5637269 | ||
|
|
caccd6c693 | ||
|
|
56a2bc5775 | ||
|
|
ed6fa6029d | ||
|
|
d50624dce9 | ||
|
|
9d49f403c2 | ||
|
|
1202d0149b | ||
|
|
44349cfa76 | ||
|
|
5270d58a04 | ||
|
|
a8a923033b | ||
|
|
b6af3a43c7 | ||
|
|
62454574a1 | ||
|
|
fe005dd388 | ||
|
|
430054db41 | ||
|
|
e459e5b8bc | ||
|
|
f9075f9f77 | ||
|
|
b22605ee54 | ||
|
|
48cfe6848c | ||
|
|
fe223cde95 | ||
|
|
843f4a7458 | ||
|
|
1a2cd7f408 | ||
|
|
6aebfe74fb | ||
|
|
8b70ee1ea8 | ||
|
|
4af05dde48 | ||
|
|
496e3ace85 | ||
|
|
7ddf7711f3 | ||
|
|
32721c4132 | ||
|
|
f248b22ef4 | ||
|
|
dade02d5bb | ||
|
|
6aa3c3b628 | ||
|
|
f123b01d81 | ||
|
|
d355761f23 | ||
|
|
2bc247d831 | ||
|
|
953c7cc076 | ||
|
|
45fa43540f | ||
|
|
8f25b2a74a | ||
|
|
0654fb3737 | ||
|
|
317e7061d0 | ||
|
|
ed52142c40 | ||
|
|
8d081ce894 | ||
|
|
1251c15fae |
No files matched your search
@@ -120,7 +120,7 @@ extraction:
|
||||
- lxml
|
||||
- gssapi
|
||||
- netaddr
|
||||
- netifaces
|
||||
- ifaddr
|
||||
- polib
|
||||
- requests
|
||||
- python-augeas
|
||||
|
||||
@@ -10,5 +10,5 @@ ipaserver == @VERSION@
|
||||
ipatests == @VERSION@
|
||||
|
||||
# keep pylint version in sync with current Fedora release
|
||||
# F37 has 2.15.5
|
||||
pylint ~= 2.15.5
|
||||
# F39 has 3.0.25
|
||||
pylint ~= 3.0.2
|
||||
@@ -29,7 +29,7 @@ aci: (targetfilter = "(objectclass=ipaca)")(version 3.0;acl "permission:System:
|
||||
dn: cn=cas,cn=ca,dc=ipa,dc=example
|
||||
aci: (targetattr = "cn || description")(targetfilter = "(objectclass=ipaca)")(version 3.0;acl "permission:System: Modify CA";allow (write) groupdn = "ldap:///cn=System: Modify CA,cn=permissions,cn=pbac,dc=ipa,dc=example";)
|
||||
dn: cn=cas,cn=ca,dc=ipa,dc=example
|
||||
aci: (targetattr = "cn || createtimestamp || description || entryusn || ipacaid || ipacaissuerdn || ipacarandomserialnumberversion || ipacasubjectdn || modifytimestamp || objectclass")(targetfilter = "(objectclass=ipaca)")(version 3.0;acl "permission:System: Read CAs";allow (compare,read,search) userdn = "ldap:///all";)
|
||||
aci: (targetattr = "cn || createtimestamp || description || entryusn || ipacahsmconfiguration || ipacaid || ipacaissuerdn || ipacarandomserialnumberversion || ipacasubjectdn || modifytimestamp || objectclass")(targetfilter = "(objectclass=ipaca)")(version 3.0;acl "permission:System: Read CAs";allow (compare,read,search) userdn = "ldap:///all";)
|
||||
dn: cn=caacls,cn=ca,dc=ipa,dc=example
|
||||
aci: (targetfilter = "(objectclass=ipacaacl)")(version 3.0;acl "permission:System: Add CA ACL";allow (add) groupdn = "ldap:///cn=System: Add CA ACL,cn=permissions,cn=pbac,dc=ipa,dc=example";)
|
||||
dn: cn=caacls,cn=ca,dc=ipa,dc=example
|
||||
@@ -147,7 +147,7 @@ aci: (targetattr = "usercertificate")(targetfilter = "(objectclass=ipahost)")(ve
|
||||
dn: cn=computers,cn=accounts,dc=ipa,dc=example
|
||||
aci: (targetattr = "userpassword")(targetfilter = "(objectclass=ipahost)")(version 3.0;acl "permission:System: Manage Host Enrollment Password";allow (write) groupdn = "ldap:///cn=System: Manage Host Enrollment Password,cn=permissions,cn=pbac,dc=ipa,dc=example";)
|
||||
dn: cn=computers,cn=accounts,dc=ipa,dc=example
|
||||
aci: (targetattr = "krblastpwdchange || krbprincipalkey")(targetfilter = "(&(!(memberOf=cn=ipaservers,cn=hostgroups,cn=accounts,dc=ipa,dc=example))(objectclass=ipahost))")(version 3.0;acl "permission:System: Manage Host Keytab";allow (write) groupdn = "ldap:///cn=System: Manage Host Keytab,cn=permissions,cn=pbac,dc=ipa,dc=example";)
|
||||
aci: (targetattr = "ipaprotectedoperation;write_keys || krblastpwdchange || krbprincipalkey")(targetfilter = "(&(!(memberOf=cn=ipaservers,cn=hostgroups,cn=accounts,dc=ipa,dc=example))(objectclass=ipahost))")(version 3.0;acl "permission:System: Manage Host Keytab";allow (write) groupdn = "ldap:///cn=System: Manage Host Keytab,cn=permissions,cn=pbac,dc=ipa,dc=example";)
|
||||
dn: cn=computers,cn=accounts,dc=ipa,dc=example
|
||||
aci: (targetattr = "createtimestamp || entryusn || ipaallowedtoperform;read_keys || ipaallowedtoperform;write_keys || modifytimestamp || objectclass")(targetfilter = "(objectclass=ipahost)")(version 3.0;acl "permission:System: Manage Host Keytab Permissions";allow (compare,read,search,write) groupdn = "ldap:///cn=System: Manage Host Keytab Permissions,cn=permissions,cn=pbac,dc=ipa,dc=example";)
|
||||
dn: cn=computers,cn=accounts,dc=ipa,dc=example
|
||||
@@ -374,6 +374,14 @@ dn: cn=sudorules,cn=sudo,dc=ipa,dc=example
|
||||
aci: (targetattr = "cmdcategory || cn || createtimestamp || description || entryusn || externalhost || externaluser || hostcategory || hostmask || ipaenabledflag || ipasudoopt || ipasudorunas || ipasudorunasextgroup || ipasudorunasextuser || ipasudorunasextusergroup || ipasudorunasgroup || ipasudorunasgroupcategory || ipasudorunasusercategory || ipauniqueid || member || memberallowcmd || memberdenycmd || memberhost || memberuser || modifytimestamp || objectclass || sudonotafter || sudonotbefore || sudoorder || usercategory")(targetfilter = "(objectclass=ipasudorule)")(version 3.0;acl "permission:System: Read Sudo Rules";allow (compare,read,search) userdn = "ldap:///all";)
|
||||
dn: dc=ipa,dc=example
|
||||
aci: (targetattr = "cn || createtimestamp || description || entryusn || modifytimestamp || objectclass || ou || sudocommand || sudohost || sudonotafter || sudonotbefore || sudooption || sudoorder || sudorunas || sudorunasgroup || sudorunasuser || sudouser")(target = "ldap:///ou=sudoers,dc=ipa,dc=example")(version 3.0;acl "permission:System: Read Sudoers compat tree";allow (compare,read,search) userdn = "ldap:///anyone";)
|
||||
dn: cn=topology,cn=ipa,cn=etc,dc=ipa,dc=example
|
||||
aci: (targetfilter = "(|(objectclass=iparepltopoconf)(objectclass=iparepltoposegment))")(version 3.0;acl "permission:System: Add Topology Segments";allow (add) groupdn = "ldap:///cn=System: Add Topology Segments,cn=permissions,cn=pbac,dc=ipa,dc=example";)
|
||||
dn: cn=topology,cn=ipa,cn=etc,dc=ipa,dc=example
|
||||
aci: (targetattr = "iparepltoposegmentdirection || iparepltoposegmentleftnode || iparepltoposegmentrightnode || nsds5replicastripattrs || nsds5replicatedattributelist || nsds5replicatedattributelisttotal")(targetfilter = "(|(objectclass=iparepltopoconf)(objectclass=iparepltoposegment))")(version 3.0;acl "permission:System: Modify Topology Segments";allow (write) groupdn = "ldap:///cn=System: Modify Topology Segments,cn=permissions,cn=pbac,dc=ipa,dc=example";)
|
||||
dn: cn=topology,cn=ipa,cn=etc,dc=ipa,dc=example
|
||||
aci: (targetattr = "cn || createtimestamp || entryusn || iparepltopoconfroot || iparepltoposegmentdirection || iparepltoposegmentleftnode || iparepltoposegmentrightnode || iparepltoposegmentstatus || modifytimestamp || nsds5replicastripattrs || nsds5replicatedattributelist || nsds5replicatedattributelisttotal || objectclass")(targetfilter = "(|(objectclass=iparepltopoconf)(objectclass=iparepltoposegment))")(version 3.0;acl "permission:System: Read Topology Segments";allow (compare,read,search) groupdn = "ldap:///cn=System: Read Topology Segments,cn=permissions,cn=pbac,dc=ipa,dc=example";)
|
||||
dn: cn=topology,cn=ipa,cn=etc,dc=ipa,dc=example
|
||||
aci: (targetfilter = "(|(objectclass=iparepltopoconf)(objectclass=iparepltoposegment))")(version 3.0;acl "permission:System: Remove Topology Segments";allow (delete) groupdn = "ldap:///cn=System: Remove Topology Segments,cn=permissions,cn=pbac,dc=ipa,dc=example";)
|
||||
dn: cn=trusts,dc=ipa,dc=example
|
||||
aci: (targetattr = "cn || createtimestamp || entryusn || ipantadditionalsuffixes || ipantflatname || ipantsecurityidentifier || ipantsidblacklistincoming || ipantsidblacklistoutgoing || ipanttrustdirection || ipanttrusteddomainsid || ipanttrustpartner || modifytimestamp || objectclass")(version 3.0;acl "permission:System: Read Trust Information";allow (compare,read,search) userdn = "ldap:///all";)
|
||||
dn: cn=trusts,dc=ipa,dc=example
|
||||
|
||||
@@ -451,8 +451,9 @@ output: Entry('result')
|
||||
output: Output('summary', type=[<type 'unicode'>, <type 'NoneType'>])
|
||||
output: PrimaryKey('value')
|
||||
command: batch/1
|
||||
args: 1,1,2
|
||||
args: 1,2,2
|
||||
arg: Dict('methods*')
|
||||
option: Str('keeponly*')
|
||||
option: Str('version?')
|
||||
output: Output('count', type=[<type 'int'>])
|
||||
output: Output('results', type=[<type 'list'>, <type 'tuple'>])
|
||||
@@ -1082,7 +1083,7 @@ option: Flag('all', autofill=True, cli_name='all', default=False)
|
||||
option: Str('ca_renewal_master_server?', autofill=False)
|
||||
option: Str('delattr*', cli_name='delattr')
|
||||
option: Flag('enable_sid?', autofill=True, default=False)
|
||||
option: StrEnum('ipaconfigstring*', autofill=False, cli_name='ipaconfigstring', values=[u'AllowNThash', u'KDC:Disable Last Success', u'KDC:Disable Lockout', u'KDC:Disable Default Preauth for SPNs'])
|
||||
option: StrEnum('ipaconfigstring*', autofill=False, cli_name='ipaconfigstring', values=[u'AllowNThash', u'KDC:Disable Last Success', u'KDC:Disable Lockout', u'KDC:Disable Default Preauth for SPNs', u'EnforceLDAPOTP'])
|
||||
option: Str('ipadefaultemaildomain?', autofill=False, cli_name='emaildomain')
|
||||
option: Str('ipadefaultloginshell?', autofill=False, cli_name='defaultshell')
|
||||
option: Str('ipadefaultprimarygroup?', autofill=False, cli_name='defaultgroup')
|
||||
|
||||
@@ -4,6 +4,7 @@ The following people have contributed to the FreeIPA project.
|
||||
(Listed in alphabetical order within category)
|
||||
|
||||
Developers:
|
||||
007hacky007
|
||||
Abhijeet
|
||||
Abhijeet Kasurde
|
||||
Adam Misnyovszki
|
||||
@@ -16,6 +17,7 @@ Developers:
|
||||
Alexander Bokovoy
|
||||
Alexander Koksharov
|
||||
Alexander Scheel
|
||||
Alexandra Nikandrova
|
||||
Alexandre Mulatinho
|
||||
Alexey Slaykovsky
|
||||
Alexey Tikhonov
|
||||
@@ -126,6 +128,7 @@ Developers:
|
||||
Martin Bašti
|
||||
Martin Košek
|
||||
Martin Nagy
|
||||
Masahiro Matsuya
|
||||
Matt Rogers
|
||||
Matthew Davis
|
||||
mbhalodi
|
||||
@@ -219,12 +222,14 @@ Developers:
|
||||
Vit Mojzis
|
||||
W. Michael Petullo
|
||||
Weblate
|
||||
Weblate Translation Memory
|
||||
William Brown
|
||||
William Jon McCann
|
||||
Xiao-Long Chen
|
||||
Yuri Chornoivan
|
||||
Zdenek Pytela
|
||||
zdover
|
||||
zoedong
|
||||
김인수
|
||||
|
||||
Documentation:
|
||||
|
||||
@@ -36,6 +36,8 @@ SUBDIRS = \
|
||||
po \
|
||||
pypi \
|
||||
selinux \
|
||||
selinux/nfast \
|
||||
selinux/luna \
|
||||
$(PYTHON_SUBDIRS) \
|
||||
$(SERVER_SUBDIRS) \
|
||||
$(NULL)
|
||||
|
||||
+5
-5
@@ -20,8 +20,8 @@
|
||||
# -> "1.0.0" #
|
||||
########################################################
|
||||
define(IPA_VERSION_MAJOR, 4)
|
||||
define(IPA_VERSION_MINOR, 11)
|
||||
define(IPA_VERSION_RELEASE, 0)
|
||||
define(IPA_VERSION_MINOR, 12)
|
||||
define(IPA_VERSION_RELEASE, 5)
|
||||
|
||||
########################################################
|
||||
# For 'pre' releases the version will be #
|
||||
@@ -49,7 +49,7 @@ define(IPA_VERSION_PRE_RELEASE, )
|
||||
# This option works only with GNU m4: #
|
||||
# it requires esyscmd m4 macro. #
|
||||
########################################################
|
||||
define(IPA_VERSION_IS_GIT_SNAPSHOT, yes)
|
||||
define(IPA_VERSION_IS_GIT_SNAPSHOT, no)
|
||||
|
||||
########################################################
|
||||
# git development branch: #
|
||||
@@ -86,8 +86,8 @@ define(IPA_DATA_VERSION, 20100614120000)
|
||||
# #
|
||||
########################################################
|
||||
define(IPA_API_VERSION_MAJOR, 2)
|
||||
# Last change: add passkey support
|
||||
define(IPA_API_VERSION_MINOR, 253)
|
||||
# Last change: add keeponly option to batch command
|
||||
define(IPA_API_VERSION_MINOR, 254)
|
||||
|
||||
########################################################
|
||||
# Following values are auto-generated from values above
|
||||
|
||||
@@ -266,6 +266,7 @@ static int ldap_sasl_interact(LDAP *ld, unsigned flags, void *priv_data, void *s
|
||||
error_message(krberr), krberr);
|
||||
in->result = NULL;
|
||||
in->len = 0;
|
||||
krb5_free_context(krbctx);
|
||||
ret = LDAP_LOCAL_ERROR;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -29,6 +29,11 @@
|
||||
#include "ipa-client-common.h"
|
||||
#include "config.h"
|
||||
|
||||
int remove_principal(krb5_context context, krb5_keytab ktid,
|
||||
const char *principal, int debug);
|
||||
int remove_realm(krb5_context context, krb5_keytab ktid,
|
||||
const char *realm, int debug);
|
||||
|
||||
#define KERBEROS_ERROR 1
|
||||
#define OOM_ERROR 2
|
||||
#define KEYTAB_ERROR 3
|
||||
@@ -171,7 +176,8 @@ remove_realm(krb5_context context, krb5_keytab ktid, const char *realm, int debu
|
||||
}
|
||||
|
||||
done:
|
||||
|
||||
krb5_kt_end_seq_get(context, ktid, &kt_cursor);
|
||||
krb5_free_unparsed_name(context, entry_princ_s);
|
||||
return rval;
|
||||
}
|
||||
|
||||
|
||||
+52
-1
@@ -132,6 +132,56 @@ c \- compare\p
|
||||
W \- self\-write\p
|
||||
O \- self\-obliterate
|
||||
|
||||
.SH "AUDIT AND LOGGING"
|
||||
|
||||
The IPA API logs audit messages to systemd journal about each command executed
|
||||
through IPA API on the IPA server. These messages can be found by grepping
|
||||
systemd journal with \fBjournalctl -g IPA.API\fR command. The message includes
|
||||
following information:
|
||||
|
||||
May 21 11:31:33 master1.ipa1.test /usr/bin/ipa[247422]: [IPA.API] [autobind]: user_del: SUCCESS [ldap2_140328582446688] {"uid": ["foobar"], "continue": false, "version": "2.253"}
|
||||
|
||||
.TP
|
||||
\fB/usr/bin/ipa[247422]\fR
|
||||
executable name and PID (`/mod_wsgi` for HTTP end-point)
|
||||
.TP
|
||||
\fB[IPA.API]\fR
|
||||
marker to allow searches with \fBjournalctl -g IPA.API\R
|
||||
.TP
|
||||
\fBusername@REALM\fR
|
||||
authenticated Kerberos principal or \fB[autobind]\fR marker for LDAP-based operations done as root
|
||||
.TP
|
||||
\fBuser_del\fR
|
||||
name of the command executed
|
||||
.TP
|
||||
\fBSUCCESS\fR
|
||||
result of execution: \fBSUCCESS\fR or an exception name
|
||||
.TP
|
||||
\fB[ldap2_140328582446688]\fR
|
||||
LDAP backend connection instance identifier. The identifier will be the same for all
|
||||
operations performed under the same request. This allows to identify operations
|
||||
which were executed using the same LDAP connection. For API operations that
|
||||
didn't result in LDAP access, there will be \fB[no_connection_id]\fR marker.
|
||||
.TP
|
||||
\fB{"uid": ["foobar"], "continue": false, "version": "2.253"}\fR
|
||||
a list of arguments and options passed to the IPA API command, provided in JSON
|
||||
format. Credentials are filtered out.
|
||||
|
||||
.LP
|
||||
All explicitly requested operations logged. Internal operations, initiated as
|
||||
part of execution of the explicitly requested IPA API calls, aren't logged. For
|
||||
HTTP end-point operations will be logged as performed by the '/mod_wsgi'
|
||||
executable binary. Remaining details can be inspected through the systemd
|
||||
journal as journald records execution context. See systemd.journal\-fields(7)
|
||||
for details.
|
||||
|
||||
The details of the individual logged messages can be explained with the help of
|
||||
'\fBjournalctl -x\fR' command, while full set of logged properties can be
|
||||
retrieved with '\fBjournalctl -o json-pretty\fR'. See journalctl(1) for details
|
||||
on the systemd journal viewer.
|
||||
|
||||
For the sample message above, an explanation could be requested with '\fBjournalctl -x -g ldap2_140328582446688\fR' where LDAP backend connection instance identifier can be used to uniquely fetch that individual message.
|
||||
|
||||
.SH "EXAMPLES"
|
||||
.TP
|
||||
\fBipa help commands\fR
|
||||
@@ -206,4 +256,5 @@ IPA default configuration file.
|
||||
ipa\-client\-install(1), ipa\-compat\-manage(1), ipactl(1), ipa\-dns\-install(1),
|
||||
ipa\-getcert(1), ipa\-getkeytab(1), ipa\-join(1), ipa\-ldap\-updater(1),
|
||||
ipa\-nis\-manage(1), ipa\-replica\-install(1), ipa\-replica\-manage(1), ipa\-replica\-prepare(1),
|
||||
ipa\-rmkeytab(1), ipa\-server\-certinstall(2), ipa\-server\-install(1), ipa\-server\-upgrade(1)
|
||||
ipa\-rmkeytab(1), ipa\-server\-certinstall(2), ipa\-server\-install(1), ipa\-server\-upgrade(1),
|
||||
systemd.journal\-fields(7), journalctl(1)
|
||||
@@ -4,7 +4,13 @@ PubkeyAuthentication yes
|
||||
${ENABLEPROXY}GlobalKnownHostsFile $KNOWNHOSTS
|
||||
${VERIFYHOSTKEYDNS}VerifyHostKeyDNS yes
|
||||
|
||||
# use sss_ssh_knownhosts if available
|
||||
# assumes that if a user does not have shell (/sbin/nologin),
|
||||
# this will return nonzero exit code and proxy command will be ignored
|
||||
${ENABLEPROXY}Match exec true
|
||||
${ENABLEKNOWNHOSTS}Match exec true
|
||||
${ENABLEKNOWNHOSTS} KnownHostsCommand $KNOWNHOSTSCOMMAND %H
|
||||
|
||||
# assumes that if a user does not have shell (/sbin/nologin),
|
||||
# this will return nonzero exit code and proxy command will be ignored
|
||||
${ENABLEPROXY}Match exec true
|
||||
${ENABLEPROXY} ProxyCommand $KNOWNHOSTSPROXY -p %p %h
|
||||
@@ -267,6 +267,13 @@ AC_ARG_WITH([systemdtmpfilesdir],
|
||||
[systemdtmpfilesdir=$($PKG_CONFIG --define-variable=prefix='${prefix}' --variable=tmpfilesdir systemd)])
|
||||
AC_SUBST([systemdtmpfilesdir])
|
||||
|
||||
AC_ARG_WITH([systemdcatalogdir],
|
||||
AS_HELP_STRING([--with-systemdcatalogdir=DIR],
|
||||
[Directory for systemd journal catalog files]),
|
||||
[systemdcatalogdir=$with_systemdcatalogdir],
|
||||
[systemdcatalogdir=$($PKG_CONFIG --define-variable=prefix='${prefix}' --variable=catalogdir systemd)])
|
||||
AC_SUBST([systemdcatalogdir])
|
||||
|
||||
dnl ---------------------------------------------------------------------------
|
||||
dnl - Server-only configuration
|
||||
dnl ---------------------------------------------------------------------------
|
||||
@@ -688,6 +695,8 @@ AC_CONFIG_FILES([
|
||||
po/Makefile.in
|
||||
po/Makefile.hack
|
||||
selinux/Makefile
|
||||
selinux/nfast/Makefile
|
||||
selinux/luna/Makefile
|
||||
util/Makefile
|
||||
])
|
||||
|
||||
|
||||
@@ -116,7 +116,7 @@ ipa_kdb_tests_LDADD = \
|
||||
$(top_builddir)/util/libutil.la \
|
||||
-lkdb5 \
|
||||
-lsss_idmap \
|
||||
-lsamba-security-samba4 \
|
||||
-l$(SAMBA_SECURITY_LIBS)\
|
||||
-lsamba-errors \
|
||||
$(NULL)
|
||||
|
||||
|
||||
@@ -12,9 +12,7 @@ much more easily managed.
|
||||
|
||||
The grouping mechanism has been built so that lookup is highly optimized
|
||||
and is basically reduced to a single search that uses the derefernce
|
||||
control. Speed is very important in this case because KDC operations
|
||||
time out very quickly and unless we add a caching layer in ipa-kdb we
|
||||
must keep the number of searches down to avoid client timeouts.
|
||||
control.
|
||||
|
||||
The grouping mechanism is very simple a groupOfPrincipals object is
|
||||
introduced, this Auxiliary class have a single optional attribute called
|
||||
@@ -112,8 +110,7 @@ kinit -kt /etc/httpd/conf/ipa.keytab HTTP/ipaserver.example.com
|
||||
kvno -U admin HTTP/ipaserver.example.com
|
||||
|
||||
# Perform S4U2Proxy
|
||||
kvno -k /etc/httpd/conf/ipa.keytab -U admin -P HTTP/ipaserver.example.com
|
||||
ldap/ipaserver.example.com
|
||||
kvno -U admin -P ldap/ipaserver.example.com
|
||||
|
||||
|
||||
If this works it means you successfully impersonated the admin user with
|
||||
@@ -125,6 +122,18 @@ modprinc -ok_to_auth_as_delegate HTTP/ipaserver.example.com
|
||||
Simo.
|
||||
|
||||
|
||||
If IPA is compiled with krb5 1.20 and newer (KDB DAL >= 9), then the
|
||||
behavior of S4U2Self changes: S4U2Self TGS-REQs produce forwardable
|
||||
tickets for all requesters, except if the requester principal is set as
|
||||
the proxy (impersonating service) in at least one `servicedelegation`
|
||||
rule. In this case, even if the rule has no target, the KDC will
|
||||
response to S4U2Self requests with a non-forwardable ticket. Hence,
|
||||
granting the `ok_to_auth_as_delegate` permission to the proxy service
|
||||
remains the only way for this service to obtain the evidence ticket
|
||||
required for general constrained delegation requests if this ticket is
|
||||
not provided by the client.
|
||||
|
||||
|
||||
[1]
|
||||
Note that here I use the term proxy in a different way than it is used in
|
||||
the krb interfaces. It may seem a bit confusing but I think people will
|
||||
|
||||
@@ -494,7 +494,7 @@ init_with_password(const char *name, const char *password)
|
||||
|
||||
done:
|
||||
if (service_creds != GSS_C_NO_CREDENTIAL)
|
||||
gss_release_cred(&min, &client_creds);
|
||||
gss_release_cred(&min, &service_creds);
|
||||
|
||||
if (client_creds != GSS_C_NO_CREDENTIAL)
|
||||
gss_release_cred(&min, &client_creds);
|
||||
|
||||
+79
-19
@@ -26,6 +26,7 @@
|
||||
#include "ipa_kdb.h"
|
||||
#include "ipa_krb5.h"
|
||||
#include "ipa_hostname.h"
|
||||
#include <kadm5/admin.h>
|
||||
|
||||
#define IPADB_GLOBAL_CONFIG_CACHE_TIME 60
|
||||
|
||||
@@ -194,6 +195,9 @@ done:
|
||||
return base;
|
||||
}
|
||||
|
||||
/* In this table all _AUTH_PASSWORD entries will be
|
||||
* expanded to include _AUTH_HARDENED in ipadb_parse_user_auth()
|
||||
* which means there is no need to explicitly add it here */
|
||||
static const struct {
|
||||
const char *name;
|
||||
enum ipadb_user_auth flag;
|
||||
@@ -207,6 +211,19 @@ static const struct {
|
||||
{ "idp", IPADB_USER_AUTH_IDP },
|
||||
{ "passkey", IPADB_USER_AUTH_PASSKEY },
|
||||
{ }
|
||||
},
|
||||
objclass_table[] = {
|
||||
{ "ipaservice", IPADB_USER_AUTH_PASSWORD },
|
||||
{ "ipahost", IPADB_USER_AUTH_PASSWORD },
|
||||
{ }
|
||||
},
|
||||
princname_table[] = {
|
||||
{ KRB5_TGS_NAME, IPADB_USER_AUTH_PASSWORD },
|
||||
{ KRB5_KDB_M_NAME, IPADB_USER_AUTH_PASSWORD },
|
||||
{ KADM5_ADMIN_SERVICE, IPADB_USER_AUTH_PASSWORD },
|
||||
{ KADM5_CHANGEPW_SERVICE, IPADB_USER_AUTH_PASSWORD },
|
||||
{ KADM5_HIST_PRINCIPAL, IPADB_USER_AUTH_PASSWORD },
|
||||
{ }
|
||||
};
|
||||
|
||||
void ipadb_parse_user_auth(LDAP *lcontext, LDAPMessage *le,
|
||||
@@ -217,16 +234,48 @@ void ipadb_parse_user_auth(LDAP *lcontext, LDAPMessage *le,
|
||||
|
||||
*userauth = IPADB_USER_AUTH_NONE;
|
||||
vals = ldap_get_values_len(lcontext, le, IPA_USER_AUTH_TYPE);
|
||||
if (!vals)
|
||||
return;
|
||||
if (!vals) {
|
||||
/* if there is no explicit ipaUserAuthType set, use objectclass */
|
||||
vals = ldap_get_values_len(lcontext, le, "objectclass");
|
||||
if (!vals)
|
||||
return;
|
||||
|
||||
for (i = 0; vals[i]; i++) {
|
||||
for (j = 0; userauth_table[j].name; j++) {
|
||||
if (strcasecmp(vals[i]->bv_val, userauth_table[j].name) == 0) {
|
||||
*userauth |= userauth_table[j].flag;
|
||||
break;
|
||||
for (i = 0; vals[i]; i++) {
|
||||
for (j = 0; objclass_table[j].name; j++) {
|
||||
if (strcasecmp(vals[i]->bv_val, objclass_table[j].name) == 0) {
|
||||
*userauth |= objclass_table[j].flag;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (i = 0; vals[i]; i++) {
|
||||
for (j = 0; userauth_table[j].name; j++) {
|
||||
if (strcasecmp(vals[i]->bv_val, userauth_table[j].name) == 0) {
|
||||
*userauth |= userauth_table[j].flag;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* If neither ipaUserAuthType nor objectClass were definitive,
|
||||
* check the krbPrincipalName to see if it is krbtgt/ or K/M one */
|
||||
if (*userauth == IPADB_USER_AUTH_NONE) {
|
||||
ldap_value_free_len(vals);
|
||||
vals = ldap_get_values_len(lcontext, le, "krbprincipalname");
|
||||
if (!vals)
|
||||
return;
|
||||
for (i = 0; vals[i]; i++) {
|
||||
for (j = 0; princname_table[j].name; j++) {
|
||||
if (strncmp(vals[i]->bv_val, princname_table[j].name,
|
||||
strlen(princname_table[j].name)) == 0) {
|
||||
*userauth |= princname_table[j].flag;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
/* If password auth is enabled, enable hardened policy too. */
|
||||
if (*userauth & IPADB_USER_AUTH_PASSWORD) {
|
||||
@@ -400,6 +449,7 @@ int ipadb_get_connection(struct ipadb_context *ipactx)
|
||||
struct timeval tv = { 5, 0 };
|
||||
LDAPMessage *res = NULL;
|
||||
LDAPMessage *first;
|
||||
const char *stmsg;
|
||||
int ret;
|
||||
int v3;
|
||||
|
||||
@@ -479,13 +529,14 @@ int ipadb_get_connection(struct ipadb_context *ipactx)
|
||||
}
|
||||
|
||||
/* get adtrust options using default refresh interval */
|
||||
ret = ipadb_reinit_mspac(ipactx, false);
|
||||
if (ret && ret != ENOENT) {
|
||||
/* TODO: log that there is an issue with adtrust settings */
|
||||
if (ipactx->lcontext == NULL) {
|
||||
/* for some reason ldap connection was reset in ipadb_reinit_mspac
|
||||
* and is no longer established => failure of ipadb_get_connection
|
||||
*/
|
||||
ret = ipadb_reinit_mspac(ipactx, false, &stmsg);
|
||||
if (ret) {
|
||||
if (stmsg) {
|
||||
krb5_klog_syslog(LOG_WARNING, "MS-PAC generator: %s", stmsg);
|
||||
}
|
||||
/* Initialization of the MS-PAC generator is an optional dependency.
|
||||
* Fail only if the connection was lost. */
|
||||
if (!ipactx->lcontext) {
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
@@ -495,18 +546,27 @@ int ipadb_get_connection(struct ipadb_context *ipactx)
|
||||
done:
|
||||
ldap_msgfree(res);
|
||||
|
||||
/* LDAP context should never be null on success, but keep this test out of
|
||||
* security to make sure we do not return an invalid context. */
|
||||
if (ret == 0 && !ipactx->lcontext) {
|
||||
krb5_klog_syslog(LOG_WARNING, "Internal malfunction: LDAP connection "
|
||||
"process resulted in an invalid context "
|
||||
"(please report this incident)");
|
||||
ret = LDAP_SERVER_DOWN;
|
||||
}
|
||||
|
||||
if (ret) {
|
||||
/* Cleanup LDAP context if connection failed. */
|
||||
if (ipactx->lcontext) {
|
||||
ldap_unbind_ext_s(ipactx->lcontext, NULL, NULL);
|
||||
ipactx->lcontext = NULL;
|
||||
}
|
||||
if (ret == LDAP_SERVER_DOWN) {
|
||||
return ETIMEDOUT;
|
||||
}
|
||||
return EIO;
|
||||
|
||||
/* Replace LDAP error code by POSIX error code. */
|
||||
ret = ret == LDAP_SERVER_DOWN ? ETIMEDOUT : EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static krb5_principal ipadb_create_local_tgs(krb5_context kcontext,
|
||||
|
||||
@@ -94,6 +94,34 @@
|
||||
#define IPA_KRB_AUTHZ_DATA_ATTR "ipaKrbAuthzData"
|
||||
#define IPA_USER_AUTH_TYPE "ipaUserAuthType"
|
||||
|
||||
/* Virtual managed ticket flags like "-allow_tix", are always controlled by the
|
||||
* "nsAccountLock" attribute, such flags should never be set in the database.
|
||||
* The following expression combine all of them, and is used to filter them
|
||||
* out. */
|
||||
#define IPA_KDB_TKTFLAGS_VIRTUAL_MANAGED_ALL (KRB5_KDB_DISALLOW_ALL_TIX)
|
||||
|
||||
/* Virtual static ticket flags are hard-coded in the KDB driver. */
|
||||
/* Virtual static mandatory flags are set systematically and implicitly for all
|
||||
* principals. They are filtered out from database ticket flags updates.
|
||||
* (However, "KRB5_KDB_REQUIRES_PRE_AUTH" can still be unset by the
|
||||
* "KDC:Disable Default Preauth for SPNs" global setting) */
|
||||
#define IPA_KDB_TKTFLAGS_VIRTUAL_STATIC_MANDATORY (KRB5_KDB_REQUIRES_PRE_AUTH)
|
||||
/* Virtual static default ticket flags are implicitly set for user and non-user
|
||||
* (SPN) principals, and not stored in the database.
|
||||
* (Except if the "IPA_KDB_STRATTR_FINAL_TKTFLAGS" string attribute is "true"
|
||||
* the principal) */
|
||||
/* Virtual static default user ticket flags are set for users only. The
|
||||
* "-allow_svr" flag is set to protect them from CVE-2024-3183. */
|
||||
#define IPA_KDB_TKTFLAGS_VIRTUAL_STATIC_DEFAULTS_USER (KRB5_KDB_DISALLOW_SVR)
|
||||
#define IPA_KDB_TKTFLAGS_VIRTUAL_STATIC_DEFAULTS_SPN (0)
|
||||
|
||||
/* If this string attribute is set to "true", then only the virtual managed and
|
||||
* virtual static mandatory ticket flags are applied and filtered out from
|
||||
* database read and write operations for the concerned user principal.
|
||||
* Configurable principal ticket flags are applied, but not the configurable
|
||||
* global ticket policy flags. */
|
||||
#define IPA_KDB_STRATTR_FINAL_USER_TKTFLAGS "final_user_tkt_flags"
|
||||
|
||||
struct ipadb_mspac;
|
||||
struct dom_sid;
|
||||
|
||||
@@ -187,6 +215,21 @@ struct ipadb_e_data {
|
||||
struct dom_sid *sid;
|
||||
};
|
||||
|
||||
inline static krb5_error_code
|
||||
ipadb_get_edata(krb5_db_entry *entry, struct ipadb_e_data **ied)
|
||||
{
|
||||
struct ipadb_e_data *in_ied;
|
||||
|
||||
in_ied = (struct ipadb_e_data *)entry->e_data;
|
||||
if (!in_ied || in_ied->magic != IPA_E_DATA_MAGIC)
|
||||
return EINVAL;
|
||||
|
||||
if (ied)
|
||||
*ied = in_ied;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct ipadb_context *ipadb_get_context(krb5_context kcontext);
|
||||
int ipadb_get_connection(struct ipadb_context *ipactx);
|
||||
|
||||
@@ -371,7 +414,9 @@ krb5_error_code ipadb_v9_issue_pac(krb5_context context, unsigned int flags,
|
||||
krb5_data ***auth_indicators);
|
||||
#endif
|
||||
|
||||
krb5_error_code ipadb_reinit_mspac(struct ipadb_context *ipactx, bool force_reinit);
|
||||
krb5_error_code ipadb_reinit_mspac(struct ipadb_context *ipactx,
|
||||
bool force_reinit,
|
||||
const char **stmsg);
|
||||
|
||||
void ipadb_mspac_struct_free(struct ipadb_mspac **mspac);
|
||||
krb5_error_code ipadb_check_transited_realms(krb5_context kcontext,
|
||||
@@ -386,6 +431,14 @@ krb5_error_code ipadb_is_princ_from_trusted_realm(krb5_context kcontext,
|
||||
const char *test_realm, size_t size,
|
||||
char **trusted_realm);
|
||||
|
||||
/* Check the ticket provided in a TGS-REQ. In some situations, the ticket is
|
||||
* expected to contain a PAC. If it is not the case, or if the function is
|
||||
* enable to decode an authorization-data element, it fails.
|
||||
* Any failure should result in the TGS-REQ to be rejected. */
|
||||
krb5_error_code ipadb_enforce_pac(krb5_context kcontext,
|
||||
const krb5_ticket *ticket,
|
||||
const char **status);
|
||||
|
||||
/* DELEGATION CHECKS */
|
||||
|
||||
krb5_error_code ipadb_check_allowed_to_delegate(krb5_context kcontext,
|
||||
@@ -430,3 +483,4 @@ int ipadb_string_to_sid(const char *str, struct dom_sid *sid);
|
||||
void alloc_sid(struct dom_sid **sid);
|
||||
void free_sid(struct dom_sid **sid);
|
||||
bool dom_sid_check(const struct dom_sid *sid1, const struct dom_sid *sid2, bool exact_check);
|
||||
bool ipadb_is_tgs_princ(krb5_context kcontext, krb5_const_principal princ);
|
||||
@@ -767,3 +767,21 @@ krb5_error_code ipadb_multibase_search(struct ipadb_context *ipactx,
|
||||
return ipadb_simple_ldap_to_kerr(ret);
|
||||
}
|
||||
|
||||
bool
|
||||
ipadb_is_tgs_princ(krb5_context kcontext, krb5_const_principal princ)
|
||||
{
|
||||
krb5_data *primary;
|
||||
size_t l_tgs_name;
|
||||
|
||||
if (2 != krb5_princ_size(kcontext, princ))
|
||||
return false;
|
||||
|
||||
primary = krb5_princ_component(kcontext, princ, 0);
|
||||
|
||||
l_tgs_name = strlen(KRB5_TGS_NAME);
|
||||
|
||||
if (l_tgs_name != primary->length)
|
||||
return false;
|
||||
|
||||
return 0 == memcmp(primary->data, KRB5_TGS_NAME, l_tgs_name);
|
||||
}
|
||||
@@ -99,120 +99,110 @@ static bool ipadb_match_member(char *princ, LDAPDerefRes *dres)
|
||||
return false;
|
||||
}
|
||||
|
||||
static krb5_error_code ipadb_match_acl(krb5_context kcontext,
|
||||
LDAPMessage *results,
|
||||
krb5_const_principal client,
|
||||
krb5_const_principal target)
|
||||
#if KRB5_KDB_DAL_MAJOR_VERSION >= 9
|
||||
static krb5_error_code
|
||||
ipadb_has_acl(krb5_context kcontext, LDAPMessage *ldap_acl, bool *res)
|
||||
{
|
||||
struct ipadb_context *ipactx;
|
||||
krb5_error_code kerr;
|
||||
LDAPMessage *lentry;
|
||||
LDAPDerefRes *deref_results;
|
||||
LDAPDerefRes *dres;
|
||||
char *client_princ = NULL;
|
||||
char *target_princ = NULL;
|
||||
bool client_missing;
|
||||
bool client_found;
|
||||
bool target_found;
|
||||
bool is_constraint_delegation = false;
|
||||
size_t nrules = 0;
|
||||
int ret;
|
||||
bool in_res = false;
|
||||
krb5_error_code kerr = 0;
|
||||
|
||||
ipactx = ipadb_get_context(kcontext);
|
||||
if (!ipactx) {
|
||||
if (!ipactx)
|
||||
return KRB5_KDB_DBNOTINITED;
|
||||
|
||||
switch (ldap_count_entries(ipactx->lcontext, ldap_acl)) {
|
||||
case 0:
|
||||
break;
|
||||
case -1:
|
||||
kerr = EINVAL;
|
||||
goto end;
|
||||
default:
|
||||
in_res = true;
|
||||
goto end;
|
||||
}
|
||||
|
||||
if ((client != NULL) && (target != NULL)) {
|
||||
kerr = krb5_unparse_name(kcontext, client, &client_princ);
|
||||
if (kerr != 0) {
|
||||
goto done;
|
||||
}
|
||||
kerr = krb5_unparse_name(kcontext, target, &target_princ);
|
||||
if (kerr != 0) {
|
||||
goto done;
|
||||
}
|
||||
} else {
|
||||
is_constraint_delegation = true;
|
||||
}
|
||||
end:
|
||||
if (res)
|
||||
*res = in_res;
|
||||
|
||||
lentry = ldap_first_entry(ipactx->lcontext, results);
|
||||
if (!lentry) {
|
||||
kerr = ENOENT;
|
||||
goto done;
|
||||
}
|
||||
return kerr;
|
||||
}
|
||||
#endif
|
||||
|
||||
static krb5_error_code
|
||||
ipadb_match_acl(krb5_context kcontext, LDAPMessage *ldap_acl,
|
||||
krb5_const_principal client, krb5_const_principal target)
|
||||
{
|
||||
struct ipadb_context *ipactx;
|
||||
LDAPMessage *rule;
|
||||
LDAPDerefRes *acis, *aci;
|
||||
char *client_princ = NULL, *target_princ= NULL;
|
||||
bool client_missing, client_found, target_found;
|
||||
int lerr;
|
||||
krb5_error_code kerr;
|
||||
|
||||
ipactx = ipadb_get_context(kcontext);
|
||||
if (!ipactx)
|
||||
return KRB5_KDB_DBNOTINITED;
|
||||
|
||||
kerr = krb5_unparse_name(kcontext, client, &client_princ);
|
||||
if (kerr)
|
||||
goto end;
|
||||
|
||||
kerr = krb5_unparse_name(kcontext, target, &target_princ);
|
||||
if (kerr)
|
||||
goto end;
|
||||
|
||||
/* the default is that we fail */
|
||||
kerr = ENOENT;
|
||||
kerr = KRB5KDC_ERR_BADOPTION;
|
||||
|
||||
while (lentry) {
|
||||
for (rule = ldap_first_entry(ipactx->lcontext, ldap_acl);
|
||||
rule;
|
||||
rule = ldap_next_entry(ipactx->lcontext, rule))
|
||||
{
|
||||
/* both client and target must be found in the same ACI */
|
||||
client_missing = true;
|
||||
client_found = false;
|
||||
target_found = false;
|
||||
|
||||
ret = ipadb_ldap_deref_results(ipactx->lcontext, lentry,
|
||||
&deref_results);
|
||||
switch (ret) {
|
||||
lerr = ipadb_ldap_deref_results(ipactx->lcontext, rule, &acis);
|
||||
switch (lerr) {
|
||||
case 0:
|
||||
for (dres = deref_results; dres; dres = dres->next) {
|
||||
nrules++;
|
||||
if (is_constraint_delegation) {
|
||||
/*
|
||||
Microsoft revised the S4U2Proxy rules for forwardable
|
||||
tickets. All S4U2Proxy operations require forwardable
|
||||
evidence tickets, but S4U2Self should issue a
|
||||
forwardable ticket if the requesting service has no
|
||||
ok-to-auth-as-delegate bit but also no constrained
|
||||
delegation privileges for traditional S4U2Proxy.
|
||||
Implement these rules, extending the
|
||||
check_allowed_to_delegate() DAL method so that the KDC
|
||||
can ask if a principal has any delegation privileges.
|
||||
|
||||
Since target principal is NULL and client principal is
|
||||
NULL in this case, we simply calculate number of rules associated
|
||||
with the server principal to decide whether to deny forwardable bit
|
||||
*/
|
||||
continue;
|
||||
}
|
||||
if (client_found == false &&
|
||||
strcasecmp(dres->derefAttr, "ipaAllowToImpersonate") == 0) {
|
||||
for (aci = acis; aci; aci = aci->next) {
|
||||
if (!client_found &&
|
||||
0 == strcasecmp(aci->derefAttr, "ipaAllowToImpersonate"))
|
||||
{
|
||||
/* NOTE: client_missing is used to signal that the
|
||||
* attribute was completely missing. This signals that
|
||||
* ANY client is allowed to be impersonated.
|
||||
* This logic is valid only for clients, not for targets */
|
||||
client_missing = false;
|
||||
client_found = ipadb_match_member(client_princ, dres);
|
||||
client_found = ipadb_match_member(client_princ, aci);
|
||||
}
|
||||
if (target_found == false &&
|
||||
strcasecmp(dres->derefAttr, "ipaAllowedTarget") == 0) {
|
||||
target_found = ipadb_match_member(target_princ, dres);
|
||||
if (!target_found &&
|
||||
0 == strcasecmp(aci->derefAttr, "ipaAllowedTarget"))
|
||||
{
|
||||
target_found = ipadb_match_member(target_princ, aci);
|
||||
}
|
||||
}
|
||||
|
||||
ldap_derefresponse_free(deref_results);
|
||||
ldap_derefresponse_free(acis);
|
||||
break;
|
||||
case ENOENT:
|
||||
break;
|
||||
default:
|
||||
kerr = ret;
|
||||
goto done;
|
||||
kerr = lerr;
|
||||
goto end;
|
||||
}
|
||||
|
||||
if ((client_found == true || client_missing == true) &&
|
||||
target_found == true) {
|
||||
if ((client_found || client_missing) && target_found) {
|
||||
kerr = 0;
|
||||
goto done;
|
||||
goto end;
|
||||
}
|
||||
|
||||
lentry = ldap_next_entry(ipactx->lcontext, lentry);
|
||||
}
|
||||
|
||||
if (nrules > 0) {
|
||||
kerr = 0;
|
||||
}
|
||||
|
||||
done:
|
||||
end:
|
||||
krb5_free_unparsed_name(kcontext, client_princ);
|
||||
krb5_free_unparsed_name(kcontext, target_princ);
|
||||
return kerr;
|
||||
@@ -231,7 +221,7 @@ krb5_error_code ipadb_check_allowed_to_delegate(krb5_context kcontext,
|
||||
char *srv_principal = NULL;
|
||||
krb5_db_entry *proxy_entry = NULL;
|
||||
struct ipadb_e_data *ied_server, *ied_proxy;
|
||||
LDAPMessage *res = NULL;
|
||||
LDAPMessage *ldap_gcd_acl = NULL;
|
||||
|
||||
if (proxy != NULL) {
|
||||
/* Handle the case where server == proxy, this is allowed in S4U */
|
||||
@@ -269,27 +259,54 @@ krb5_error_code ipadb_check_allowed_to_delegate(krb5_context kcontext,
|
||||
goto done;
|
||||
}
|
||||
|
||||
kerr = ipadb_get_delegation_acl(kcontext, srv_principal, &res);
|
||||
/* Load general constrained delegation rules */
|
||||
kerr = ipadb_get_delegation_acl(kcontext, srv_principal, &ldap_gcd_acl);
|
||||
if (kerr) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
kerr = ipadb_match_acl(kcontext, res, client, proxy);
|
||||
if (kerr) {
|
||||
goto done;
|
||||
#if KRB5_KDB_DAL_MAJOR_VERSION >= 9
|
||||
/*
|
||||
* Microsoft revised the S4U2Proxy rules for forwardable tickets. All
|
||||
* S4U2Proxy operations require forwardable evidence tickets, but
|
||||
* S4U2Self should issue a forwardable ticket if the requesting service
|
||||
* has no ok-to-auth-as-delegate bit but also no constrained delegation
|
||||
* privileges for traditional S4U2Proxy. Implement these rules,
|
||||
* extending the check_allowed_to_delegate() DAL method so that the KDC
|
||||
* can ask if a principal has any delegation privileges.
|
||||
*
|
||||
* If target service principal is NULL, and the impersonating service has
|
||||
* at least one GCD rule, then succeed.
|
||||
*/
|
||||
if (!proxy) {
|
||||
bool has_gcd_rules;
|
||||
|
||||
kerr = ipadb_has_acl(kcontext, ldap_gcd_acl, &has_gcd_rules);
|
||||
if (!kerr)
|
||||
kerr = has_gcd_rules ? 0 : KRB5KDC_ERR_BADOPTION;
|
||||
} else if (client) {
|
||||
#else
|
||||
if (client && proxy) {
|
||||
#endif
|
||||
kerr = ipadb_match_acl(kcontext, ldap_gcd_acl, client, proxy);
|
||||
} else {
|
||||
/* client and/or proxy is missing */
|
||||
kerr = KRB5KDC_ERR_BADOPTION;
|
||||
}
|
||||
if (kerr)
|
||||
goto done;
|
||||
|
||||
done:
|
||||
if (kerr) {
|
||||
#if KRB5_KDB_DAL_MAJOR_VERSION < 9
|
||||
kerr = KRB5KDC_ERR_POLICY;
|
||||
#else
|
||||
#if KRB5_KDB_DAL_MAJOR_VERSION >= 9
|
||||
kerr = KRB5KDC_ERR_BADOPTION;
|
||||
#else
|
||||
kerr = KRB5KDC_ERR_POLICY;
|
||||
#endif
|
||||
}
|
||||
ipadb_free_principal(kcontext, proxy_entry);
|
||||
krb5_free_unparsed_name(kcontext, srv_principal);
|
||||
ldap_msgfree(res);
|
||||
ldap_msgfree(ldap_gcd_acl);
|
||||
return kerr;
|
||||
}
|
||||
|
||||
|
||||
@@ -119,11 +119,8 @@ ipa_kdcpolicy_check_as(krb5_context context, krb5_kdcpolicy_moddata moddata,
|
||||
pol_limits = &(ied->pol_limits[IPADB_USER_AUTH_IDX_RADIUS]);
|
||||
} else if (strcmp(auth_indicator, "pkinit") == 0) {
|
||||
valid_auth_indicators++;
|
||||
if (!(ua & IPADB_USER_AUTH_PKINIT)) {
|
||||
*status = "PKINIT pre-authentication not allowed for this user.";
|
||||
kerr = KRB5KDC_ERR_POLICY;
|
||||
goto done;
|
||||
}
|
||||
/* allow PKINIT unconditionally -- it has passed already at this
|
||||
* point so some certificate was useful, only apply the limits */
|
||||
pol_limits = &(ied->pol_limits[IPADB_USER_AUTH_IDX_PKINIT]);
|
||||
} else if (strcmp(auth_indicator, "hardened") == 0) {
|
||||
valid_auth_indicators++;
|
||||
@@ -198,7 +195,7 @@ ipa_kdcpolicy_check_tgs(krb5_context context, krb5_kdcpolicy_moddata moddata,
|
||||
*lifetime_out = 0;
|
||||
*renew_lifetime_out = 0;
|
||||
|
||||
return 0;
|
||||
return ipadb_enforce_pac(context, ticket, status);
|
||||
}
|
||||
|
||||
krb5_error_code kdcpolicy_ipakdb_initvt(krb5_context context,
|
||||
|
||||
+302
-170
@@ -496,8 +496,16 @@ static krb5_error_code ipadb_fill_info3(struct ipadb_context *ipactx,
|
||||
ret = ipadb_ldap_attr_to_str(ipactx->lcontext, lentry,
|
||||
"krbCanonicalName", &strres);
|
||||
if (ret) {
|
||||
/* krbCanonicalName is mandatory for services */
|
||||
return ret;
|
||||
/* krbCanonicalName is mandatory for services but IPA services
|
||||
* created before commit e6ff83e (FreeIPA 4.4.0, ~2016) had no
|
||||
* normalization to set krbCanonicalName; services created after
|
||||
* that version were upgraded to do have krbCanonicalName.
|
||||
*
|
||||
* Accept krbPrincipalName alone since they have no alias either */
|
||||
ret = ipadb_ldap_attr_to_str(ipactx->lcontext, lentry,
|
||||
"krbPrincipalName", &strres);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = krb5_parse_name(ipactx->kcontext, strres, &princ);
|
||||
@@ -785,16 +793,16 @@ static krb5_error_code ipadb_fill_info3(struct ipadb_context *ipactx,
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (!ipactx->mspac) {
|
||||
/* can't give a PAC without server NetBIOS name or primary group RID */
|
||||
return ENOENT;
|
||||
}
|
||||
|
||||
if (info3->base.primary_gid == 0) {
|
||||
if (is_host || is_service) {
|
||||
info3->base.primary_gid = 515; /* Well known RID for domain computers group */
|
||||
} else {
|
||||
if (ipactx->mspac->fallback_rid) {
|
||||
info3->base.primary_gid = ipactx->mspac->fallback_rid;
|
||||
} else {
|
||||
/* can't give a pack without a primary group rid */
|
||||
return ENOENT;
|
||||
}
|
||||
info3->base.primary_gid = ipactx->mspac->fallback_rid;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -804,26 +812,16 @@ static krb5_error_code ipadb_fill_info3(struct ipadb_context *ipactx,
|
||||
/* always zero out, not used for Krb, only NTLM */
|
||||
memset(&info3->base.key, '\0', sizeof(info3->base.key));
|
||||
|
||||
if (ipactx->mspac->flat_server_name) {
|
||||
info3->base.logon_server.string =
|
||||
talloc_strdup(memctx, ipactx->mspac->flat_server_name);
|
||||
if (!info3->base.logon_server.string) {
|
||||
return ENOMEM;
|
||||
}
|
||||
} else {
|
||||
/* can't give a pack without Server NetBIOS Name :-| */
|
||||
return ENOENT;
|
||||
info3->base.logon_server.string =
|
||||
talloc_strdup(memctx, ipactx->mspac->flat_server_name);
|
||||
if (!info3->base.logon_server.string) {
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
if (ipactx->mspac->flat_domain_name) {
|
||||
info3->base.logon_domain.string =
|
||||
talloc_strdup(memctx, ipactx->mspac->flat_domain_name);
|
||||
if (!info3->base.logon_domain.string) {
|
||||
return ENOMEM;
|
||||
}
|
||||
} else {
|
||||
/* can't give a pack without Domain NetBIOS Name :-| */
|
||||
return ENOENT;
|
||||
info3->base.logon_domain.string =
|
||||
talloc_strdup(memctx, ipactx->mspac->flat_domain_name);
|
||||
if (!info3->base.logon_domain.string) {
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
if (is_host || is_service) {
|
||||
@@ -1036,6 +1034,11 @@ krb5_error_code ipadb_get_pac(krb5_context kcontext,
|
||||
return KRB5_KDB_DBNOTINITED;
|
||||
}
|
||||
|
||||
/* Check if PAC generator is initialized */
|
||||
if (!ipactx->mspac) {
|
||||
return ENOENT;
|
||||
}
|
||||
|
||||
ied = (struct ipadb_e_data *)client->e_data;
|
||||
if (ied->magic != IPA_E_DATA_MAGIC) {
|
||||
return EINVAL;
|
||||
@@ -1618,14 +1621,14 @@ static struct ipadb_adtrusts *get_domain_from_realm(krb5_context context,
|
||||
{
|
||||
struct ipadb_context *ipactx;
|
||||
struct ipadb_adtrusts *domain;
|
||||
int i;
|
||||
size_t i;
|
||||
|
||||
ipactx = ipadb_get_context(context);
|
||||
if (!ipactx) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (ipactx->mspac == NULL) {
|
||||
if (!ipactx->mspac) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -1647,6 +1650,7 @@ static struct ipadb_adtrusts *get_domain_from_realm_update(krb5_context context,
|
||||
{
|
||||
struct ipadb_context *ipactx;
|
||||
struct ipadb_adtrusts *domain;
|
||||
const char *stmsg = NULL;
|
||||
krb5_error_code kerr;
|
||||
|
||||
ipactx = ipadb_get_context(context);
|
||||
@@ -1655,8 +1659,10 @@ static struct ipadb_adtrusts *get_domain_from_realm_update(krb5_context context,
|
||||
}
|
||||
|
||||
/* re-init MS-PAC info using default update interval */
|
||||
kerr = ipadb_reinit_mspac(ipactx, false);
|
||||
kerr = ipadb_reinit_mspac(ipactx, false, &stmsg);
|
||||
if (kerr != 0) {
|
||||
if (stmsg)
|
||||
krb5_klog_syslog(LOG_WARNING, "MS-PAC generator: %s", stmsg);
|
||||
return NULL;
|
||||
}
|
||||
domain = get_domain_from_realm(context, realm);
|
||||
@@ -1709,6 +1715,7 @@ static krb5_error_code check_logon_info_consistent(krb5_context context,
|
||||
struct ipadb_e_data *ied = NULL;
|
||||
int flags = 0;
|
||||
struct dom_sid client_sid;
|
||||
const char *stmsg = NULL;
|
||||
#ifdef KRB5_KDB_FLAG_ALIAS_OK
|
||||
flags = KRB5_KDB_FLAG_ALIAS_OK;
|
||||
#endif
|
||||
@@ -1722,10 +1729,14 @@ static krb5_error_code check_logon_info_consistent(krb5_context context,
|
||||
* check that our own view on the PAC details is up to date */
|
||||
if (ipactx->mspac->domsid.num_auths == 0) {
|
||||
/* Force re-init of KDB's view on our domain */
|
||||
kerr = ipadb_reinit_mspac(ipactx, true);
|
||||
kerr = ipadb_reinit_mspac(ipactx, true, &stmsg);
|
||||
if (kerr != 0) {
|
||||
krb5_klog_syslog(LOG_ERR,
|
||||
"PAC issue: unable to update realm's view on PAC info");
|
||||
if (stmsg) {
|
||||
krb5_klog_syslog(LOG_ERR, "MS-PAC generator: %s", stmsg);
|
||||
} else {
|
||||
krb5_klog_syslog(LOG_ERR, "PAC issue: unable to update " \
|
||||
"realm's view on PAC info");
|
||||
}
|
||||
return KRB5KDC_ERR_POLICY;
|
||||
}
|
||||
}
|
||||
@@ -1738,7 +1749,7 @@ static krb5_error_code check_logon_info_consistent(krb5_context context,
|
||||
if (is_s4u && (ipactx->mspac->trusts != NULL)) {
|
||||
/* Iterate through list of trusts and check if this SID belongs to
|
||||
* one of the domains we trust */
|
||||
for(int i = 0 ; i < ipactx->mspac->num_trusts ; i++) {
|
||||
for(size_t i = 0 ; i < ipactx->mspac->num_trusts ; i++) {
|
||||
result = dom_sid_check(&ipactx->mspac->trusts[i].domsid,
|
||||
info->info->info3.base.domain_sid, true);
|
||||
if (result) {
|
||||
@@ -1850,11 +1861,11 @@ krb5_error_code filter_logon_info(krb5_context context,
|
||||
struct ipadb_mspac *mspac_ctx = ipactx->mspac;
|
||||
result = FALSE;
|
||||
/* Didn't match but perhaps the original PAC was issued by a child domain's DC? */
|
||||
for (k = 0; k < mspac_ctx->num_trusts; k++) {
|
||||
result = dom_sid_check(&mspac_ctx->trusts[k].domsid,
|
||||
for (size_t m = 0; m < mspac_ctx->num_trusts; m++) {
|
||||
result = dom_sid_check(&mspac_ctx->trusts[m].domsid,
|
||||
info->info->info3.base.domain_sid, true);
|
||||
if (result) {
|
||||
domain = &mspac_ctx->trusts[k];
|
||||
domain = &mspac_ctx->trusts[m];
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -2083,10 +2094,10 @@ static krb5_error_code ipadb_check_logon_info(krb5_context context,
|
||||
return KRB5_KDB_DBNOTINITED;
|
||||
}
|
||||
/* In S4U case we might be dealing with the PAC issued by the trusted domain */
|
||||
if ((ipactx->mspac->trusts != NULL)) {
|
||||
if (ipactx->mspac->trusts) {
|
||||
/* Iterate through list of trusts and check if this SID belongs to
|
||||
* one of the domains we trust */
|
||||
for(int i = 0 ; i < ipactx->mspac->num_trusts ; i++) {
|
||||
for(size_t i = 0 ; i < ipactx->mspac->num_trusts ; i++) {
|
||||
result = dom_sid_check(&ipactx->mspac->trusts[i].domsid,
|
||||
&client_sid, false);
|
||||
if (result) {
|
||||
@@ -2316,6 +2327,7 @@ krb5_error_code ipadb_common_verify_pac(krb5_context context,
|
||||
size_t i;
|
||||
struct dom_sid *requester_sid = NULL;
|
||||
struct dom_sid req_sid;
|
||||
TALLOC_CTX *tmpctx = NULL;
|
||||
|
||||
if (signing_krbtgt != NULL &&
|
||||
ipadb_is_cross_realm_krbtgt(signing_krbtgt->princ)) {
|
||||
@@ -2371,6 +2383,12 @@ krb5_error_code ipadb_common_verify_pac(krb5_context context,
|
||||
goto done;
|
||||
}
|
||||
|
||||
tmpctx = talloc_new(NULL);
|
||||
if (tmpctx == NULL) {
|
||||
kerr = ENOMEM;
|
||||
goto done;
|
||||
}
|
||||
|
||||
for (i = 0; i < num_buffers; i++) {
|
||||
if (types[i] == KRB5_PAC_SERVER_CHECKSUM ||
|
||||
types[i] == KRB5_PAC_PRIVSVR_CHECKSUM ||
|
||||
@@ -2398,32 +2416,21 @@ krb5_error_code ipadb_common_verify_pac(krb5_context context,
|
||||
DATA_BLOB pac_attrs_data;
|
||||
krb5_boolean pac_requested;
|
||||
|
||||
TALLOC_CTX *tmpctx = talloc_new(NULL);
|
||||
if (tmpctx == NULL) {
|
||||
kerr = ENOMEM;
|
||||
goto done;
|
||||
}
|
||||
|
||||
kerr = ipadb_client_requested_pac(context, old_pac, tmpctx, &pac_requested);
|
||||
if (kerr != 0) {
|
||||
talloc_free(tmpctx);
|
||||
if (kerr)
|
||||
goto done;
|
||||
}
|
||||
|
||||
kerr = ipadb_get_pac_attrs_blob(tmpctx, &pac_requested, &pac_attrs_data);
|
||||
if (kerr) {
|
||||
talloc_free(tmpctx);
|
||||
if (kerr)
|
||||
goto done;
|
||||
}
|
||||
|
||||
data.magic = KV5M_DATA;
|
||||
data.data = (char *)pac_attrs_data.data;
|
||||
data.length = pac_attrs_data.length;
|
||||
|
||||
kerr = krb5_pac_add_buffer(context, new_pac, PAC_TYPE_ATTRIBUTES_INFO, &data);
|
||||
if (kerr) {
|
||||
talloc_free(tmpctx);
|
||||
if (kerr)
|
||||
goto done;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
@@ -2470,6 +2477,8 @@ done:
|
||||
if (kerr != 0 && (new_pac != *pac)) {
|
||||
krb5_pac_free(context, new_pac);
|
||||
}
|
||||
if (tmpctx)
|
||||
talloc_free(tmpctx);
|
||||
krb5_free_data_contents(context, &pac_blob);
|
||||
free(types);
|
||||
return kerr;
|
||||
@@ -2628,7 +2637,7 @@ static char *get_server_netbios_name(struct ipadb_context *ipactx)
|
||||
|
||||
void ipadb_mspac_struct_free(struct ipadb_mspac **mspac)
|
||||
{
|
||||
int i, j;
|
||||
size_t i, j;
|
||||
|
||||
if (!*mspac) return;
|
||||
|
||||
@@ -2783,7 +2792,8 @@ ipadb_mspac_get_trusted_domains(struct ipadb_context *ipactx)
|
||||
LDAPDN dn = NULL;
|
||||
char **sid_blocklist_incoming = NULL;
|
||||
char **sid_blocklist_outgoing = NULL;
|
||||
int ret, n, i;
|
||||
size_t i, n;
|
||||
int ret;
|
||||
|
||||
ret = asprintf(&base, "cn=ad,cn=trusts,%s", ipactx->base);
|
||||
if (ret == -1) {
|
||||
@@ -2868,7 +2878,7 @@ ipadb_mspac_get_trusted_domains(struct ipadb_context *ipactx)
|
||||
|
||||
t[n].upn_suffixes_len = NULL;
|
||||
if (t[n].upn_suffixes != NULL) {
|
||||
int len = 0;
|
||||
size_t len = 0;
|
||||
|
||||
for (; t[n].upn_suffixes[len] != NULL; len++);
|
||||
|
||||
@@ -2983,108 +2993,115 @@ done:
|
||||
return ret;
|
||||
}
|
||||
|
||||
krb5_error_code ipadb_reinit_mspac(struct ipadb_context *ipactx, bool force_reinit)
|
||||
krb5_error_code
|
||||
ipadb_reinit_mspac(struct ipadb_context *ipactx, bool force_reinit,
|
||||
const char **stmsg)
|
||||
{
|
||||
char *dom_attrs[] = { "ipaNTFlatName",
|
||||
"ipaNTFallbackPrimaryGroup",
|
||||
"ipaNTSecurityIdentifier",
|
||||
NULL };
|
||||
char *grp_attrs[] = { "ipaNTSecurityIdentifier", NULL };
|
||||
krb5_error_code kerr;
|
||||
LDAPMessage *result = NULL;
|
||||
LDAPMessage *lentry;
|
||||
struct dom_sid gsid;
|
||||
char *resstr;
|
||||
int ret;
|
||||
struct dom_sid gsid, domsid;
|
||||
char *resstr = NULL;
|
||||
char *flat_domain_name = NULL;
|
||||
char *flat_server_name = NULL;
|
||||
char *fallback_group = NULL;
|
||||
uint32_t fallback_rid;
|
||||
time_t now;
|
||||
const char *in_stmsg = NULL;
|
||||
int err;
|
||||
krb5_error_code trust_kerr = 0;
|
||||
|
||||
|
||||
/* Do not update the mspac struct more than once a minute. This would
|
||||
* avoid heavy load on the directory server if there are lots of requests
|
||||
* from domains which we do not trust. */
|
||||
now = time(NULL);
|
||||
|
||||
if (ipactx->mspac != NULL &&
|
||||
(force_reinit == false) &&
|
||||
(now > ipactx->mspac->last_update) &&
|
||||
(now - ipactx->mspac->last_update) < 60) {
|
||||
return 0;
|
||||
}
|
||||
if (ipactx->mspac) {
|
||||
if (!force_reinit &&
|
||||
(now > ipactx->mspac->last_update) &&
|
||||
(now - ipactx->mspac->last_update) < 60) {
|
||||
/* SKIP */
|
||||
err = 0;
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (ipactx->mspac && ipactx->mspac->num_trusts == 0) {
|
||||
/* Check if there is any trust configured. If not, just return
|
||||
* and do not re-initialize the MS-PAC structure. */
|
||||
kerr = ipadb_mspac_check_trusted_domains(ipactx);
|
||||
if (kerr == KRB5_KDB_NOENTRY) {
|
||||
kerr = 0;
|
||||
goto done;
|
||||
} else if (kerr != 0) {
|
||||
goto done;
|
||||
if (ipactx->mspac->num_trusts == 0) {
|
||||
/* Check if there is any trust configured. If not, just return
|
||||
* and do not re-initialize the MS-PAC structure. */
|
||||
err = ipadb_mspac_check_trusted_domains(ipactx);
|
||||
if (err) {
|
||||
if (err == KRB5_KDB_NOENTRY) {
|
||||
/* SKIP */
|
||||
err = 0;
|
||||
} else {
|
||||
in_stmsg = "Failed to fetch trusted domains information";
|
||||
}
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* clean up in case we had old values around */
|
||||
ipadb_mspac_struct_free(&ipactx->mspac);
|
||||
|
||||
ipactx->mspac = calloc(1, sizeof(struct ipadb_mspac));
|
||||
if (!ipactx->mspac) {
|
||||
kerr = ENOMEM;
|
||||
goto done;
|
||||
}
|
||||
|
||||
ipactx->mspac->last_update = now;
|
||||
|
||||
kerr = ipadb_simple_search(ipactx, ipactx->base, LDAP_SCOPE_SUBTREE,
|
||||
"(objectclass=ipaNTDomainAttrs)", dom_attrs,
|
||||
&result);
|
||||
if (kerr == KRB5_KDB_NOENTRY) {
|
||||
return ENOENT;
|
||||
} else if (kerr != 0) {
|
||||
return EIO;
|
||||
err = ipadb_simple_search(ipactx, ipactx->base, LDAP_SCOPE_SUBTREE,
|
||||
"(objectclass=ipaNTDomainAttrs)", dom_attrs,
|
||||
&result);
|
||||
if (err == KRB5_KDB_NOENTRY) {
|
||||
err = ENOENT;
|
||||
in_stmsg = "Local domain NT attributes not configured";
|
||||
goto end;
|
||||
} else if (err) {
|
||||
err = EIO;
|
||||
in_stmsg = "Failed to fetch local domain NT attributes";
|
||||
goto end;
|
||||
}
|
||||
|
||||
lentry = ldap_first_entry(ipactx->lcontext, result);
|
||||
if (!lentry) {
|
||||
kerr = ENOENT;
|
||||
goto done;
|
||||
err = ENOENT;
|
||||
in_stmsg = "Local domain NT attributes not configured";
|
||||
goto end;
|
||||
}
|
||||
|
||||
ret = ipadb_ldap_attr_to_str(ipactx->lcontext, lentry,
|
||||
"ipaNTFlatName",
|
||||
&ipactx->mspac->flat_domain_name);
|
||||
if (ret) {
|
||||
kerr = ret;
|
||||
goto done;
|
||||
err = ipadb_ldap_attr_to_str(ipactx->lcontext, lentry, "ipaNTFlatName",
|
||||
&flat_domain_name);
|
||||
if (err) {
|
||||
in_stmsg = "Local domain NT flat name not configured";
|
||||
goto end;
|
||||
}
|
||||
|
||||
ret = ipadb_ldap_attr_to_str(ipactx->lcontext, lentry,
|
||||
"ipaNTSecurityIdentifier",
|
||||
&resstr);
|
||||
if (ret) {
|
||||
kerr = ret;
|
||||
goto done;
|
||||
err = ipadb_ldap_attr_to_str(ipactx->lcontext, lentry,
|
||||
"ipaNTSecurityIdentifier", &resstr);
|
||||
if (err) {
|
||||
in_stmsg = "Local domain SID not configured";
|
||||
goto end;
|
||||
}
|
||||
|
||||
ret = ipadb_string_to_sid(resstr, &ipactx->mspac->domsid);
|
||||
if (ret) {
|
||||
kerr = ret;
|
||||
free(resstr);
|
||||
goto done;
|
||||
err = ipadb_string_to_sid(resstr, &domsid);
|
||||
if (err) {
|
||||
in_stmsg = "Malformed local domain SID";
|
||||
goto end;
|
||||
}
|
||||
|
||||
free(resstr);
|
||||
resstr = NULL;
|
||||
|
||||
free(ipactx->mspac->flat_server_name);
|
||||
ipactx->mspac->flat_server_name = get_server_netbios_name(ipactx);
|
||||
if (!ipactx->mspac->flat_server_name) {
|
||||
kerr = ENOMEM;
|
||||
goto done;
|
||||
flat_server_name = get_server_netbios_name(ipactx);
|
||||
if (!flat_server_name) {
|
||||
err = ENOMEM;
|
||||
goto end;
|
||||
}
|
||||
|
||||
ret = ipadb_ldap_attr_to_str(ipactx->lcontext, lentry,
|
||||
"ipaNTFallbackPrimaryGroup",
|
||||
&ipactx->mspac->fallback_group);
|
||||
if (ret && ret != ENOENT) {
|
||||
kerr = ret;
|
||||
goto done;
|
||||
err = ipadb_ldap_attr_to_str(ipactx->lcontext, lentry,
|
||||
"ipaNTFallbackPrimaryGroup", &fallback_group);
|
||||
if (err) {
|
||||
in_stmsg = (err == ENOENT)
|
||||
? "Local fallback primary group not configured"
|
||||
: "Failed to fetch local fallback primary group";
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* result and lentry not valid any more from here on */
|
||||
@@ -3092,53 +3109,81 @@ krb5_error_code ipadb_reinit_mspac(struct ipadb_context *ipactx, bool force_rein
|
||||
result = NULL;
|
||||
lentry = NULL;
|
||||
|
||||
if (ret != ENOENT) {
|
||||
kerr = ipadb_simple_search(ipactx, ipactx->mspac->fallback_group,
|
||||
LDAP_SCOPE_BASE,
|
||||
"(objectclass=posixGroup)",
|
||||
grp_attrs, &result);
|
||||
if (kerr && kerr != KRB5_KDB_NOENTRY) {
|
||||
kerr = ret;
|
||||
goto done;
|
||||
}
|
||||
|
||||
lentry = ldap_first_entry(ipactx->lcontext, result);
|
||||
if (!lentry) {
|
||||
kerr = ENOENT;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (kerr == 0) {
|
||||
ret = ipadb_ldap_attr_to_str(ipactx->lcontext, lentry,
|
||||
"ipaNTSecurityIdentifier",
|
||||
&resstr);
|
||||
if (ret && ret != ENOENT) {
|
||||
kerr = ret;
|
||||
goto done;
|
||||
}
|
||||
if (ret == 0) {
|
||||
ret = ipadb_string_to_sid(resstr, &gsid);
|
||||
if (ret) {
|
||||
free(resstr);
|
||||
kerr = ret;
|
||||
goto done;
|
||||
}
|
||||
ret = sid_split_rid(&gsid, &ipactx->mspac->fallback_rid);
|
||||
if (ret) {
|
||||
free(resstr);
|
||||
kerr = ret;
|
||||
goto done;
|
||||
}
|
||||
free(resstr);
|
||||
}
|
||||
}
|
||||
err = ipadb_simple_search(ipactx, fallback_group, LDAP_SCOPE_BASE,
|
||||
"(objectclass=posixGroup)", grp_attrs, &result);
|
||||
if (err) {
|
||||
in_stmsg = (err == KRB5_KDB_NOENTRY)
|
||||
? "Local fallback primary group has no POSIX definition"
|
||||
: "Failed to fetch SID of POSIX group mapped as local fallback " \
|
||||
"primary group";
|
||||
goto end;
|
||||
}
|
||||
|
||||
kerr = ipadb_mspac_get_trusted_domains(ipactx);
|
||||
lentry = ldap_first_entry(ipactx->lcontext, result);
|
||||
if (!lentry) {
|
||||
err = ENOENT;
|
||||
goto end;
|
||||
}
|
||||
|
||||
done:
|
||||
err = ipadb_ldap_attr_to_str(ipactx->lcontext, lentry,
|
||||
"ipaNTSecurityIdentifier", &resstr);
|
||||
if (err) {
|
||||
in_stmsg = (err == ENOENT)
|
||||
? "The POSIX group set as fallback primary group has no SID " \
|
||||
"configured"
|
||||
: "Failed to fetch SID of POSIX group set as local fallback " \
|
||||
"primary group";
|
||||
goto end;
|
||||
}
|
||||
|
||||
err = ipadb_string_to_sid(resstr, &gsid);
|
||||
if (err) {
|
||||
in_stmsg = "Malformed SID of POSIX group set as local fallback " \
|
||||
"primary group";
|
||||
goto end;
|
||||
}
|
||||
|
||||
err = sid_split_rid(&gsid, &fallback_rid);
|
||||
if (err) {
|
||||
in_stmsg = "Malformed SID of POSIX group mapped as local fallback " \
|
||||
"primary group";
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* clean up in case we had old values around */
|
||||
ipadb_mspac_struct_free(&ipactx->mspac);
|
||||
|
||||
ipactx->mspac = calloc(1, sizeof(struct ipadb_mspac));
|
||||
if (!ipactx->mspac) {
|
||||
err = ENOMEM;
|
||||
goto end;
|
||||
}
|
||||
|
||||
ipactx->mspac->last_update = now;
|
||||
ipactx->mspac->flat_domain_name = flat_domain_name;
|
||||
ipactx->mspac->flat_server_name = flat_server_name;
|
||||
ipactx->mspac->domsid = domsid;
|
||||
ipactx->mspac->fallback_group = fallback_group;
|
||||
ipactx->mspac->fallback_rid = fallback_rid;
|
||||
|
||||
trust_kerr = ipadb_mspac_get_trusted_domains(ipactx);
|
||||
if (trust_kerr)
|
||||
in_stmsg = "Failed to assemble trusted domains information";
|
||||
|
||||
end:
|
||||
if (stmsg)
|
||||
*stmsg = in_stmsg;
|
||||
|
||||
if (resstr) free(resstr);
|
||||
ldap_msgfree(result);
|
||||
return kerr;
|
||||
|
||||
if (err) {
|
||||
if (flat_domain_name) free(flat_domain_name);
|
||||
if (flat_server_name) free(flat_server_name);
|
||||
if (fallback_group) free(fallback_group);
|
||||
}
|
||||
|
||||
return err ? (krb5_error_code)err : trust_kerr;
|
||||
}
|
||||
|
||||
krb5_error_code ipadb_check_transited_realms(krb5_context kcontext,
|
||||
@@ -3148,11 +3193,11 @@ krb5_error_code ipadb_check_transited_realms(krb5_context kcontext,
|
||||
{
|
||||
struct ipadb_context *ipactx;
|
||||
bool has_transited_contents, has_client_realm, has_server_realm;
|
||||
int i;
|
||||
size_t i;
|
||||
krb5_error_code ret;
|
||||
|
||||
ipactx = ipadb_get_context(kcontext);
|
||||
if (!ipactx || !ipactx->mspac) {
|
||||
if (!ipactx) {
|
||||
return KRB5_KDB_DBNOTINITED;
|
||||
}
|
||||
|
||||
@@ -3214,7 +3259,7 @@ krb5_error_code ipadb_is_princ_from_trusted_realm(krb5_context kcontext,
|
||||
char **trusted_realm)
|
||||
{
|
||||
struct ipadb_context *ipactx;
|
||||
int i, j, length;
|
||||
size_t i, j, length;
|
||||
const char *name;
|
||||
bool result = false;
|
||||
|
||||
@@ -3301,3 +3346,90 @@ krb5_error_code ipadb_is_princ_from_trusted_realm(krb5_context kcontext,
|
||||
|
||||
return KRB5_KDB_NOENTRY;
|
||||
}
|
||||
|
||||
static krb5_error_code
|
||||
check_for_pac(krb5_context kcontext, krb5_authdata **authdata, bool *pac_present)
|
||||
{
|
||||
krb5_error_code kerr = ENOENT;
|
||||
size_t i, j;
|
||||
krb5_authdata **ifrel = NULL;
|
||||
|
||||
for (i = 0; authdata && authdata[i]; ++i) {
|
||||
if (authdata[i]->ad_type != KRB5_AUTHDATA_IF_RELEVANT) {
|
||||
continue;
|
||||
}
|
||||
|
||||
kerr = krb5_decode_authdata_container(kcontext,
|
||||
KRB5_AUTHDATA_IF_RELEVANT,
|
||||
authdata[i], &ifrel);
|
||||
if (kerr) {
|
||||
goto end;
|
||||
}
|
||||
|
||||
for (j = 0; ifrel[j]; ++j) {
|
||||
if (ifrel[j]->ad_type == KRB5_AUTHDATA_WIN2K_PAC) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (ifrel[j]) {
|
||||
break;
|
||||
}
|
||||
|
||||
krb5_free_authdata(kcontext, ifrel);
|
||||
ifrel = NULL;
|
||||
}
|
||||
|
||||
*pac_present = ifrel;
|
||||
kerr = 0;
|
||||
|
||||
end:
|
||||
krb5_free_authdata(kcontext, ifrel);
|
||||
return kerr;
|
||||
}
|
||||
|
||||
krb5_error_code
|
||||
ipadb_enforce_pac(krb5_context kcontext, const krb5_ticket *ticket,
|
||||
const char **status)
|
||||
{
|
||||
struct ipadb_context *ipactx;
|
||||
bool pac_present;
|
||||
krb5_error_code kerr;
|
||||
|
||||
/* Filter TGTs only */
|
||||
if (!ipadb_is_tgs_princ(kcontext, ticket->server)) {
|
||||
kerr = 0;
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* Get IPA context */
|
||||
ipactx = ipadb_get_context(kcontext);
|
||||
if (!ipactx) {
|
||||
kerr = KRB5_KDB_DBNOTINITED;
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* If local TGT but PAC generator not initialized, skip PAC enforcement */
|
||||
if (krb5_realm_compare(kcontext, ipactx->local_tgs, ticket->server) &&
|
||||
!ipactx->mspac)
|
||||
{
|
||||
krb5_klog_syslog(LOG_WARNING, "MS-PAC not available. This makes "
|
||||
"FreeIPA vulnerable to privilege escalation exploit "
|
||||
"(CVE-2025-7493). Please generate SIDs to enable PAC "
|
||||
"support.");
|
||||
kerr = 0;
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* Search for the PAC, fail if it cannot be found */
|
||||
kerr = check_for_pac(kcontext, ticket->enc_part2->authorization_data,
|
||||
&pac_present);
|
||||
if (kerr) {
|
||||
*status = "PAC_ENFORCEMENT_CANNOT_DECODE_TGT_AUTHDATA";
|
||||
} else if (!pac_present) {
|
||||
kerr = ENOENT;
|
||||
*status = "PAC_ENFORCEMENT_TGT_WITHOUT_PAC";
|
||||
}
|
||||
|
||||
end:
|
||||
return kerr;
|
||||
}
|
||||
@@ -31,7 +31,7 @@ struct ipadb_mspac {
|
||||
char *fallback_group;
|
||||
uint32_t fallback_rid;
|
||||
|
||||
int num_trusts;
|
||||
size_t num_trusts;
|
||||
struct ipadb_adtrusts *trusts;
|
||||
time_t last_update;
|
||||
};
|
||||
|
||||
@@ -233,6 +233,7 @@ krb5_error_code ipadb_sign_authdata(krb5_context context,
|
||||
krb5_db_entry *client_entry = NULL;
|
||||
krb5_boolean is_equal;
|
||||
bool force_reinit_mspac = false;
|
||||
const char *stmsg = NULL;
|
||||
|
||||
|
||||
is_as_req = ((flags & KRB5_KDB_FLAG_CLIENT_REFERRALS_ONLY) != 0);
|
||||
@@ -309,7 +310,9 @@ krb5_error_code ipadb_sign_authdata(krb5_context context,
|
||||
force_reinit_mspac = true;
|
||||
}
|
||||
|
||||
(void)ipadb_reinit_mspac(ipactx, force_reinit_mspac);
|
||||
kerr = ipadb_reinit_mspac(ipactx, force_reinit_mspac, &stmsg);
|
||||
if (kerr && stmsg)
|
||||
krb5_klog_syslog(LOG_WARNING, "MS-PAC generator: %s", stmsg);
|
||||
|
||||
kerr = ipadb_get_pac(context, flags, client, server, NULL, authtime, &pac);
|
||||
if (kerr != 0 && kerr != ENOENT) {
|
||||
|
||||
@@ -46,6 +46,7 @@ ipadb_v9_issue_pac(krb5_context context, unsigned int flags,
|
||||
bool with_pad;
|
||||
krb5_error_code kerr = 0;
|
||||
bool is_as_req = flags & CLIENT_REFERRALS_FLAGS;
|
||||
const char *stmsg = NULL;
|
||||
|
||||
if (is_as_req) {
|
||||
get_authz_data_types(context, client, &with_pac, &with_pad);
|
||||
@@ -110,12 +111,19 @@ ipadb_v9_issue_pac(krb5_context context, unsigned int flags,
|
||||
force_reinit_mspac = TRUE;
|
||||
}
|
||||
}
|
||||
(void)ipadb_reinit_mspac(ipactx, force_reinit_mspac);
|
||||
|
||||
/* MS-PAC needs proper configuration and if it is missing, we simply skip issuing one */
|
||||
if (ipactx->mspac->flat_server_name == NULL) {
|
||||
/* MS-PAC generator has to be initalized */
|
||||
kerr = ipadb_reinit_mspac(ipactx, force_reinit_mspac, &stmsg);
|
||||
if (kerr && stmsg)
|
||||
krb5_klog_syslog(LOG_ERR, "MS-PAC generator: %s", stmsg);
|
||||
|
||||
/* Continue even if initilization of PAC generator failed.
|
||||
* It may caused by the trust objects part only. */
|
||||
|
||||
/* At least the core part of the PAC generator is required. */
|
||||
if (!ipactx->mspac)
|
||||
return KRB5_PLUGIN_OP_NOTSUPP;
|
||||
}
|
||||
|
||||
kerr = ipadb_get_pac(context, flags,
|
||||
client, server, replaced_reply_key,
|
||||
authtime, &new_pac);
|
||||
|
||||
@@ -188,25 +188,6 @@ done:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool
|
||||
is_tgs_princ(krb5_context kcontext, krb5_const_principal princ)
|
||||
{
|
||||
krb5_data *primary;
|
||||
size_t l_tgs_name;
|
||||
|
||||
if (2 != krb5_princ_size(kcontext, princ))
|
||||
return false;
|
||||
|
||||
primary = krb5_princ_component(kcontext, princ, 0);
|
||||
|
||||
l_tgs_name = strlen(KRB5_TGS_NAME);
|
||||
|
||||
if (l_tgs_name != primary->length)
|
||||
return false;
|
||||
|
||||
return 0 == memcmp(primary->data, KRB5_TGS_NAME, l_tgs_name);
|
||||
}
|
||||
|
||||
static krb5_error_code
|
||||
cmp_local_tgs_princ(krb5_context kcontext, const char *local_realm,
|
||||
krb5_const_principal princ, bool *result)
|
||||
@@ -765,9 +746,12 @@ static krb5_error_code ipadb_parse_ldap_entry(krb5_context kcontext,
|
||||
"krbTicketFlags", &result);
|
||||
if (ret == 0) {
|
||||
entry->attributes = result;
|
||||
} else {
|
||||
*polmask |= TKTFLAGS_BIT;
|
||||
}
|
||||
/* Since principal, global policy, and virtual ticket flags are combined,
|
||||
* they must always be resolved, except if we are in IPA setup mode (because
|
||||
* ticket policies and virtual ticket flags are irrelevant in this case). */
|
||||
if (!ipactx->override_restrictions)
|
||||
*polmask |= TKTFLAGS_BIT;
|
||||
|
||||
ret = ipadb_ldap_attr_to_int(lcontext, lentry,
|
||||
"krbMaxTicketLife", &result);
|
||||
@@ -971,7 +955,12 @@ static krb5_error_code ipadb_parse_ldap_entry(krb5_context kcontext,
|
||||
goto done;
|
||||
}
|
||||
if (ret == 0) {
|
||||
ied->ipa_user = true;
|
||||
if (1 == krb5_princ_size(kcontext, entry->princ)) {
|
||||
/* A principal must be a POSIX account AND have only one element to
|
||||
* be considered a user (this is to filter out CIFS principals). */
|
||||
ied->ipa_user = true;
|
||||
}
|
||||
|
||||
ret = ipadb_ldap_attr_to_str(lcontext, lentry,
|
||||
"uid", &uidstring);
|
||||
if (ret != 0 && ret != ENOENT) {
|
||||
@@ -1339,23 +1328,150 @@ done:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static krb5_flags maybe_require_preauth(struct ipadb_context *ipactx,
|
||||
krb5_db_entry *entry)
|
||||
static krb5_error_code
|
||||
are_final_tktflags(struct ipadb_context *ipactx, krb5_db_entry *entry,
|
||||
bool *final_tktflags)
|
||||
{
|
||||
const struct ipadb_global_config *config;
|
||||
krb5_error_code kerr;
|
||||
struct ipadb_e_data *ied;
|
||||
char *str = NULL;
|
||||
bool in_final_tktflags = false;
|
||||
|
||||
config = ipadb_get_global_config(ipactx);
|
||||
if (config && config->disable_preauth_for_spns) {
|
||||
ied = (struct ipadb_e_data *)entry->e_data;
|
||||
if (ied && ied->ipa_user != true) {
|
||||
/* not a user, assume SPN */
|
||||
return 0;
|
||||
}
|
||||
kerr = ipadb_get_edata(entry, &ied);
|
||||
if (kerr)
|
||||
goto end;
|
||||
|
||||
if (!ied->ipa_user) {
|
||||
kerr = 0;
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* By default require preauth for all principals */
|
||||
return KRB5_KDB_REQUIRES_PRE_AUTH;
|
||||
kerr = krb5_dbe_get_string(ipactx->kcontext, entry,
|
||||
IPA_KDB_STRATTR_FINAL_USER_TKTFLAGS, &str);
|
||||
if (kerr)
|
||||
goto end;
|
||||
|
||||
in_final_tktflags = str && ipa_krb5_parse_bool(str);
|
||||
|
||||
end:
|
||||
if (final_tktflags)
|
||||
*final_tktflags = in_final_tktflags;
|
||||
|
||||
krb5_dbe_free_string(ipactx->kcontext, str);
|
||||
return kerr;
|
||||
}
|
||||
|
||||
static krb5_error_code
|
||||
add_virtual_static_tktflags(struct ipadb_context *ipactx, krb5_db_entry *entry,
|
||||
krb5_flags *tktflags)
|
||||
{
|
||||
krb5_error_code kerr;
|
||||
krb5_flags vsflg;
|
||||
bool final_tktflags;
|
||||
const struct ipadb_global_config *gcfg;
|
||||
struct ipadb_e_data *ied;
|
||||
|
||||
vsflg = IPA_KDB_TKTFLAGS_VIRTUAL_STATIC_MANDATORY;
|
||||
|
||||
kerr = ipadb_get_edata(entry, &ied);
|
||||
if (kerr)
|
||||
goto end;
|
||||
|
||||
kerr = are_final_tktflags(ipactx, entry, &final_tktflags);
|
||||
if (kerr)
|
||||
goto end;
|
||||
|
||||
/* In practice, principal ticket flags cannot be final for SPNs. */
|
||||
if (!final_tktflags)
|
||||
vsflg |= ied->ipa_user ? IPA_KDB_TKTFLAGS_VIRTUAL_STATIC_DEFAULTS_USER
|
||||
: IPA_KDB_TKTFLAGS_VIRTUAL_STATIC_DEFAULTS_SPN;
|
||||
|
||||
if (!ied->ipa_user) {
|
||||
gcfg = ipadb_get_global_config(ipactx);
|
||||
if (gcfg && gcfg->disable_preauth_for_spns)
|
||||
vsflg &= ~KRB5_KDB_REQUIRES_PRE_AUTH;
|
||||
}
|
||||
|
||||
if (tktflags)
|
||||
*tktflags |= vsflg;
|
||||
|
||||
end:
|
||||
return kerr;
|
||||
}
|
||||
|
||||
static krb5_error_code
|
||||
get_virtual_static_tktflags_mask(struct ipadb_context *ipactx,
|
||||
krb5_db_entry *entry, krb5_flags *mask)
|
||||
{
|
||||
krb5_error_code kerr;
|
||||
krb5_flags flags = IPA_KDB_TKTFLAGS_VIRTUAL_MANAGED_ALL;
|
||||
|
||||
kerr = add_virtual_static_tktflags(ipactx, entry, &flags);
|
||||
if (kerr)
|
||||
goto end;
|
||||
|
||||
if (mask)
|
||||
*mask = ~flags;
|
||||
|
||||
kerr = 0;
|
||||
|
||||
end:
|
||||
return kerr;
|
||||
}
|
||||
|
||||
/* Add ticket flags from the global ticket policy if it exists, otherwise
|
||||
* succeed. If the global ticket policy is set, the "exists" parameter is set to
|
||||
* true. */
|
||||
static krb5_error_code
|
||||
add_global_ticket_policy_flags(struct ipadb_context *ipactx,
|
||||
bool *gtpol_exists, krb5_flags *tktflags)
|
||||
{
|
||||
krb5_error_code kerr;
|
||||
char *policy_dn;
|
||||
char *tktflags_attr[] = { "krbticketflags", NULL };
|
||||
LDAPMessage *res = NULL, *first;
|
||||
int ec, ldap_tktflags;
|
||||
bool in_gtpol_exists = false;
|
||||
|
||||
ec = asprintf(&policy_dn, "cn=%s,cn=kerberos,%s", ipactx->realm,
|
||||
ipactx->base);
|
||||
if (-1 == ec) {
|
||||
kerr = ENOMEM;
|
||||
goto end;
|
||||
}
|
||||
|
||||
kerr = ipadb_simple_search(ipactx, policy_dn, LDAP_SCOPE_BASE,
|
||||
"(objectclass=krbticketpolicyaux)",
|
||||
tktflags_attr, &res);
|
||||
if (kerr) {
|
||||
if (KRB5_KDB_NOENTRY == kerr)
|
||||
kerr = 0;
|
||||
goto end;
|
||||
}
|
||||
|
||||
first = ldap_first_entry(ipactx->lcontext, res);
|
||||
if (!first) {
|
||||
kerr = 0;
|
||||
goto end;
|
||||
}
|
||||
|
||||
in_gtpol_exists = true;
|
||||
|
||||
ec = ipadb_ldap_attr_to_int(ipactx->lcontext, first, "krbticketflags",
|
||||
&ldap_tktflags);
|
||||
if (0 == ec && tktflags) {
|
||||
*tktflags |= (krb5_flags)ldap_tktflags;
|
||||
}
|
||||
|
||||
kerr = 0;
|
||||
|
||||
end:
|
||||
if (gtpol_exists)
|
||||
*gtpol_exists = in_gtpol_exists;
|
||||
|
||||
ldap_msgfree(res);
|
||||
free(policy_dn);
|
||||
return kerr;
|
||||
}
|
||||
|
||||
static krb5_error_code ipadb_fetch_tktpolicy(krb5_context kcontext,
|
||||
@@ -1368,6 +1484,7 @@ static krb5_error_code ipadb_fetch_tktpolicy(krb5_context kcontext,
|
||||
char *policy_dn = NULL;
|
||||
LDAPMessage *res = NULL;
|
||||
LDAPMessage *first;
|
||||
bool final_tktflags, has_local_tktpolicy = true;
|
||||
int result;
|
||||
int ret;
|
||||
|
||||
@@ -1376,12 +1493,18 @@ static krb5_error_code ipadb_fetch_tktpolicy(krb5_context kcontext,
|
||||
return KRB5_KDB_DBNOTINITED;
|
||||
}
|
||||
|
||||
kerr = are_final_tktflags(ipactx, entry, &final_tktflags);
|
||||
if (kerr)
|
||||
goto done;
|
||||
|
||||
ret = ipadb_ldap_attr_to_str(ipactx->lcontext, lentry,
|
||||
"krbticketpolicyreference", &policy_dn);
|
||||
switch (ret) {
|
||||
case 0:
|
||||
break;
|
||||
case ENOENT:
|
||||
/* If no principal ticket policy, fallback to the global one. */
|
||||
has_local_tktpolicy = false;
|
||||
ret = asprintf(&policy_dn, "cn=%s,cn=kerberos,%s",
|
||||
ipactx->realm, ipactx->base);
|
||||
if (ret == -1) {
|
||||
@@ -1425,12 +1548,13 @@ static krb5_error_code ipadb_fetch_tktpolicy(krb5_context kcontext,
|
||||
}
|
||||
}
|
||||
if (polmask & TKTFLAGS_BIT) {
|
||||
ret = ipadb_ldap_attr_to_int(ipactx->lcontext, first,
|
||||
"krbticketflags", &result);
|
||||
if (ret == 0) {
|
||||
entry->attributes |= result;
|
||||
} else {
|
||||
entry->attributes |= maybe_require_preauth(ipactx, entry);
|
||||
/* If global ticket policy is being applied, set flags only if
|
||||
* user principal ticket flags are not final. */
|
||||
if (has_local_tktpolicy || !final_tktflags) {
|
||||
ret = ipadb_ldap_attr_to_int(ipactx->lcontext, first,
|
||||
"krbticketflags", &result);
|
||||
if (ret == 0)
|
||||
entry->attributes |= result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1454,13 +1578,27 @@ static krb5_error_code ipadb_fetch_tktpolicy(krb5_context kcontext,
|
||||
if (polmask & MAXRENEWABLEAGE_BIT) {
|
||||
entry->max_renewable_life = 604800;
|
||||
}
|
||||
if (polmask & TKTFLAGS_BIT) {
|
||||
entry->attributes |= maybe_require_preauth(ipactx, entry);
|
||||
}
|
||||
|
||||
kerr = 0;
|
||||
}
|
||||
|
||||
if (polmask & TKTFLAGS_BIT) {
|
||||
/* If the principal ticket flags were applied, then flags from the
|
||||
* global ticket policy has to be applied atop of them if user principal
|
||||
* ticket flags are not final. */
|
||||
if (has_local_tktpolicy && !final_tktflags) {
|
||||
kerr = add_global_ticket_policy_flags(ipactx, NULL,
|
||||
&entry->attributes);
|
||||
if (kerr)
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Virtual static ticket flags are set regardless of database content */
|
||||
kerr = add_virtual_static_tktflags(ipactx, entry, &entry->attributes);
|
||||
if (kerr)
|
||||
goto done;
|
||||
}
|
||||
|
||||
done:
|
||||
ldap_msgfree(res);
|
||||
free(policy_dn);
|
||||
@@ -1598,6 +1736,7 @@ static krb5_error_code dbget_alias(krb5_context kcontext,
|
||||
-1,
|
||||
};
|
||||
size_t i = 0;
|
||||
const char *stmsg = NULL;
|
||||
|
||||
/* For TGS-REQ server principal lookup, KDC asks with KRB5_KDB_FLAG_REFERRAL_OK
|
||||
* and client usually asks for an KRB5_NT_PRINCIPAL type principal. */
|
||||
@@ -1685,8 +1824,11 @@ static krb5_error_code dbget_alias(krb5_context kcontext,
|
||||
if (kerr == KRB5_KDB_NOENTRY) {
|
||||
/* If no trusted realm found, refresh trusted domain data and try again
|
||||
* because it might be a freshly added trust to AD */
|
||||
kerr = ipadb_reinit_mspac(ipactx, false);
|
||||
kerr = ipadb_reinit_mspac(ipactx, false, &stmsg);
|
||||
if (kerr != 0) {
|
||||
if (stmsg)
|
||||
krb5_klog_syslog(LOG_WARNING, "MS-PAC generator: %s",
|
||||
stmsg);
|
||||
kerr = KRB5_KDB_NOENTRY;
|
||||
goto done;
|
||||
}
|
||||
@@ -1825,7 +1967,7 @@ krb5_error_code ipadb_get_principal(krb5_context kcontext,
|
||||
return kerr;
|
||||
|
||||
/* If TGS principal, some virtual attributes may be added */
|
||||
if (is_tgs_princ(kcontext, (*entry)->princ)) {
|
||||
if (ipadb_is_tgs_princ(kcontext, (*entry)->princ)) {
|
||||
kerr = cmp_local_tgs_princ(kcontext, ipactx->realm, (*entry)->princ,
|
||||
&is_local_tgs_princ);
|
||||
if (kerr)
|
||||
@@ -1839,6 +1981,9 @@ krb5_error_code ipadb_get_principal(krb5_context kcontext,
|
||||
kerr = krb5_dbe_set_string(kcontext, *entry,
|
||||
KRB5_KDB_SK_PAC_PRIVSVR_ENCTYPE,
|
||||
"aes256-sha1");
|
||||
if (kerr)
|
||||
return kerr;
|
||||
|
||||
}
|
||||
|
||||
/* We should have been initialized at this point already */
|
||||
@@ -1902,6 +2047,7 @@ void ipadb_free_principal(krb5_context kcontext, krb5_db_entry *entry)
|
||||
for (i = 0; (acl_list != NULL) && (acl_list[i] != NULL); i++) {
|
||||
free(acl_list[i]);
|
||||
}
|
||||
free(acl_list);
|
||||
}
|
||||
free(prev->tl_data_contents);
|
||||
free(prev);
|
||||
@@ -2041,6 +2187,36 @@ static void ipadb_mods_free_tip(struct ipadb_mods *imods)
|
||||
imods->tip--;
|
||||
}
|
||||
|
||||
/* Use LDAP REPLACE operation to remove an attribute.
|
||||
* Contrary to the DELETE operation, it will not fail if the attribute does not
|
||||
* exist. */
|
||||
static krb5_error_code
|
||||
ipadb_ldap_replace_remove(struct ipadb_mods *imods, char *attribute)
|
||||
{
|
||||
krb5_error_code kerr;
|
||||
LDAPMod *m = NULL;
|
||||
|
||||
kerr = ipadb_mods_new(imods, &m);
|
||||
if (kerr)
|
||||
return kerr;
|
||||
|
||||
m->mod_op = LDAP_MOD_REPLACE;
|
||||
m->mod_type = strdup(attribute);
|
||||
if (!m->mod_type) {
|
||||
kerr = ENOMEM;
|
||||
goto end;
|
||||
}
|
||||
|
||||
m->mod_values = NULL;
|
||||
|
||||
kerr = 0;
|
||||
|
||||
end:
|
||||
if (kerr)
|
||||
ipadb_mods_free_tip(imods);
|
||||
return kerr;
|
||||
}
|
||||
|
||||
static krb5_error_code ipadb_get_ldap_mod_str(struct ipadb_mods *imods,
|
||||
char *attribute, char *value,
|
||||
int mod_op)
|
||||
@@ -2457,6 +2633,93 @@ static krb5_error_code ipadb_get_ldap_mod_auth_ind(krb5_context kcontext,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static krb5_error_code
|
||||
update_tktflags(krb5_context kcontext, struct ipadb_mods *imods,
|
||||
krb5_db_entry *entry, int mod_op)
|
||||
{
|
||||
krb5_error_code kerr;
|
||||
struct ipadb_context *ipactx;
|
||||
struct ipadb_e_data *ied;
|
||||
bool final_tktflags;
|
||||
krb5_flags tktflags_mask;
|
||||
int tktflags;
|
||||
|
||||
ipactx = ipadb_get_context(kcontext);
|
||||
if (!ipactx) {
|
||||
kerr = KRB5_KDB_DBNOTINITED;
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (ipactx->override_restrictions) {
|
||||
/* In IPA setup mode, IPA edata might not be available. In this mode,
|
||||
* ticket flags are written as they are provided. */
|
||||
tktflags = (int)entry->attributes;
|
||||
} else {
|
||||
kerr = ipadb_get_edata(entry, &ied);
|
||||
if (kerr)
|
||||
goto end;
|
||||
|
||||
kerr = get_virtual_static_tktflags_mask(ipactx, entry, &tktflags_mask);
|
||||
if (kerr)
|
||||
goto end;
|
||||
|
||||
kerr = are_final_tktflags(ipactx, entry, &final_tktflags);
|
||||
if (kerr)
|
||||
goto end;
|
||||
|
||||
/* Flags from the global ticket policy are filtered out only if the user
|
||||
* principal flags are not final. */
|
||||
if (!final_tktflags) {
|
||||
krb5_flags gbl_tktflags = 0;
|
||||
|
||||
kerr = add_global_ticket_policy_flags(ipactx, NULL, &gbl_tktflags);
|
||||
if (kerr)
|
||||
goto end;
|
||||
|
||||
tktflags_mask &= ~gbl_tktflags;
|
||||
}
|
||||
|
||||
tktflags = (int)(entry->attributes & tktflags_mask);
|
||||
|
||||
if (LDAP_MOD_REPLACE == mod_op && ied && !ied->has_tktpolaux) {
|
||||
if (0 == tktflags) {
|
||||
/* No point initializing principal ticket policy if there are no
|
||||
* flags left after filtering out virtual and global ticket
|
||||
* policy ones. */
|
||||
kerr = 0;
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* if the object does not have the krbTicketPolicyAux class
|
||||
* we need to add it or this will fail, only for modifications.
|
||||
* We always add this objectclass by default when doing an add
|
||||
* from scratch. */
|
||||
kerr = ipadb_get_ldap_mod_str(imods, "objectclass",
|
||||
"krbTicketPolicyAux", LDAP_MOD_ADD);
|
||||
if (kerr)
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
|
||||
if (tktflags != 0) {
|
||||
kerr = ipadb_get_ldap_mod_int(imods, "krbTicketFlags", tktflags,
|
||||
mod_op);
|
||||
if (kerr)
|
||||
goto end;
|
||||
} else if (LDAP_MOD_REPLACE == mod_op) {
|
||||
/* If the principal is not being created, and there are no custom ticket
|
||||
* flags to be set, remove the "krbTicketFlags" attribute. */
|
||||
kerr = ipadb_ldap_replace_remove(imods, "krbTicketFlags");
|
||||
if (kerr)
|
||||
goto end;
|
||||
}
|
||||
|
||||
kerr = 0;
|
||||
|
||||
end:
|
||||
return kerr;
|
||||
}
|
||||
|
||||
static krb5_error_code ipadb_entry_to_mods(krb5_context kcontext,
|
||||
struct ipadb_mods *imods,
|
||||
krb5_db_entry *entry,
|
||||
@@ -2532,36 +2795,9 @@ static krb5_error_code ipadb_entry_to_mods(krb5_context kcontext,
|
||||
|
||||
/* KADM5_ATTRIBUTES */
|
||||
if (entry->mask & KMASK_ATTRIBUTES) {
|
||||
/* if the object does not have the krbTicketPolicyAux class
|
||||
* we need to add it or this will fail, only for modifications.
|
||||
* We always add this objectclass by default when doing an add
|
||||
* from scratch. */
|
||||
if ((mod_op == LDAP_MOD_REPLACE) && entry->e_data) {
|
||||
struct ipadb_e_data *ied;
|
||||
|
||||
ied = (struct ipadb_e_data *)entry->e_data;
|
||||
if (ied->magic != IPA_E_DATA_MAGIC) {
|
||||
kerr = EINVAL;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (!ied->has_tktpolaux) {
|
||||
kerr = ipadb_get_ldap_mod_str(imods, "objectclass",
|
||||
"krbTicketPolicyAux",
|
||||
LDAP_MOD_ADD);
|
||||
if (kerr) {
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
kerr = ipadb_get_ldap_mod_int(imods,
|
||||
"krbTicketFlags",
|
||||
(int)entry->attributes,
|
||||
mod_op);
|
||||
if (kerr) {
|
||||
kerr = update_tktflags(kcontext, imods, entry, mod_op);
|
||||
if (kerr)
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
/* KADM5_MAX_LIFE */
|
||||
|
||||
@@ -79,6 +79,7 @@ static void on_bind_readable(verto_ctx *vctx, verto_ev *ev)
|
||||
struct otpd_queue_item *item = NULL;
|
||||
int i, rslt;
|
||||
(void)vctx;
|
||||
int kerr = 0;
|
||||
|
||||
rslt = ldap_result(verto_get_private(ev), LDAP_RES_ANY, 0, NULL, &results);
|
||||
if (rslt != LDAP_RES_BIND) {
|
||||
@@ -118,6 +119,7 @@ static void on_bind_readable(verto_ctx *vctx, verto_ev *ev)
|
||||
krad_code_name2num("Access-Accept"),
|
||||
NULL, item->req, &item->rsp);
|
||||
if (i != 0) {
|
||||
kerr = 1;
|
||||
errstr = krb5_get_error_message(ctx.kctx, i);
|
||||
goto error;
|
||||
}
|
||||
@@ -127,6 +129,10 @@ error:
|
||||
otpd_log_req(item->req, "bind end: %s",
|
||||
item->rsp != NULL ? "success" : errstr);
|
||||
|
||||
if (kerr) {
|
||||
krb5_free_error_message(ctx.kctx, errstr);
|
||||
}
|
||||
|
||||
ldap_msgfree(results);
|
||||
otpd_queue_push(&ctx.stdio.responses, item);
|
||||
verto_set_flags(ctx.stdio.writer, VERTO_EV_FLAG_PERSIST |
|
||||
|
||||
@@ -43,10 +43,14 @@ static void forward_cb(krb5_error_code retval, const krad_packet *request,
|
||||
NULL, item->req, &item->rsp);
|
||||
}
|
||||
|
||||
otpd_log_req(item->req, "forward end: %s",
|
||||
retval == 0
|
||||
? krad_code_num2name(code)
|
||||
: krb5_get_error_message(ctx.kctx, retval));
|
||||
if (retval == 0) {
|
||||
otpd_log_req(item->req, "forward end: %s", krad_code_num2name(code));
|
||||
} else {
|
||||
const char *err_msg = krb5_get_error_message(ctx.kctx, retval);
|
||||
otpd_log_req(item->req, "forward end: %s",
|
||||
krb5_get_error_message(ctx.kctx, retval));
|
||||
krb5_free_error_message(ctx.kctx, err_msg);
|
||||
}
|
||||
|
||||
otpd_queue_push(&ctx.stdio.responses, item);
|
||||
verto_set_flags(ctx.stdio.writer, VERTO_EV_FLAG_PERSIST |
|
||||
@@ -117,8 +121,10 @@ krb5_error_code otpd_forward(struct otpd_queue_item **item)
|
||||
*item = NULL;
|
||||
|
||||
error:
|
||||
if (retval != 0)
|
||||
otpd_log_req((*item)->req, "forward end: %s",
|
||||
krb5_get_error_message(ctx.kctx, retval));
|
||||
if (retval != 0) {
|
||||
const char *err_msg = krb5_get_error_message(ctx.kctx, retval);
|
||||
otpd_log_req((*item)->req, "forward end: %s", err_msg);
|
||||
krb5_free_error_message(ctx.kctx, err_msg);
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
+14
-10
@@ -31,6 +31,7 @@
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/random.h>
|
||||
#include <sys/uio.h>
|
||||
|
||||
#include "internal.h"
|
||||
|
||||
@@ -93,6 +94,7 @@ static void oauth2_on_child_writable(verto_ctx *vctx, verto_ev *ev)
|
||||
(void)vctx; /* Unused */
|
||||
ssize_t io;
|
||||
struct child_ctx *child_ctx;
|
||||
struct iovec iov[3];
|
||||
|
||||
child_ctx = verto_get_private(ev);
|
||||
if (child_ctx == NULL) {
|
||||
@@ -102,15 +104,18 @@ static void oauth2_on_child_writable(verto_ctx *vctx, verto_ev *ev)
|
||||
}
|
||||
|
||||
if (child_ctx->oauth2_state == OAUTH2_GET_DEVICE_CODE) {
|
||||
/* no input needed */
|
||||
verto_del(ev);
|
||||
return;
|
||||
io = write(verto_get_fd(ev), child_ctx->item->idp.ipaidpClientSecret,
|
||||
strlen(child_ctx->item->idp.ipaidpClientSecret));
|
||||
} else {
|
||||
iov[0].iov_base = child_ctx->item->idp.ipaidpClientSecret;
|
||||
iov[0].iov_len = strlen(child_ctx->item->idp.ipaidpClientSecret);
|
||||
iov[1].iov_base = "\n";
|
||||
iov[1].iov_len = 1;
|
||||
iov[2].iov_base = child_ctx->saved_item->oauth2.device_code_reply;
|
||||
iov[2].iov_len = strlen(child_ctx->saved_item->oauth2.device_code_reply);
|
||||
|
||||
io = writev(verto_get_fd(ev), iov, 3);
|
||||
}
|
||||
|
||||
|
||||
io = write(verto_get_fd(ev),
|
||||
child_ctx->saved_item->oauth2.device_code_reply,
|
||||
strlen(child_ctx->saved_item->oauth2.device_code_reply));
|
||||
otpd_queue_item_free(child_ctx->saved_item);
|
||||
|
||||
if (io < 0) {
|
||||
@@ -429,8 +434,7 @@ int oauth2(struct otpd_queue_item **item, enum oauth2_state oauth2_state)
|
||||
args[args_idx++] = (*item)->idp.ipaidpClientID;
|
||||
|
||||
if ((*item)->idp.ipaidpClientSecret) {
|
||||
args[args_idx++] = "--client-secret";
|
||||
args[args_idx++] = (*item)->idp.ipaidpClientSecret;
|
||||
args[args_idx++] = "--client-secret-stdin";
|
||||
}
|
||||
|
||||
if ((*item)->idp.ipaidpScope) {
|
||||
|
||||
@@ -2165,17 +2165,17 @@ static bool handle_cross_realm_princs(struct ipasam_private *ipasam_state,
|
||||
|
||||
princ_l = talloc_asprintf(tmp_ctx, "krbtgt/%s@%s",
|
||||
remote_realm, ipasam_state->realm);
|
||||
princ_l_tdo = talloc_asprintf(tmp_ctx, "%s$@%s",
|
||||
l_tdo_alias = talloc_asprintf(tmp_ctx, "%s$@%s",
|
||||
flat_name, ipasam_state->realm);
|
||||
l_tdo_alias = talloc_asprintf(tmp_ctx, "krbtgt/%s@%s",
|
||||
princ_l_tdo = talloc_asprintf(tmp_ctx, "krbtgt/%s@%s",
|
||||
flat_name, ipasam_state->realm);
|
||||
|
||||
princ_r = talloc_asprintf(tmp_ctx, "krbtgt/%s@%s",
|
||||
ipasam_state->realm, remote_realm);
|
||||
princ_r_tdo = talloc_asprintf(tmp_ctx, "%s$@%s",
|
||||
r_tdo_alias = talloc_asprintf(tmp_ctx, "%s$@%s",
|
||||
ipasam_state->flat_name, remote_realm);
|
||||
|
||||
r_tdo_alias = talloc_asprintf(tmp_ctx, "krbtgt/%s@%s",
|
||||
princ_r_tdo = talloc_asprintf(tmp_ctx, "krbtgt/%s@%s",
|
||||
ipasam_state->flat_name, remote_realm);
|
||||
|
||||
if (trusted_dn == NULL || princ_l == NULL || princ_l_tdo == NULL ||
|
||||
@@ -2212,7 +2212,7 @@ static bool handle_cross_realm_princs(struct ipasam_private *ipasam_state,
|
||||
* only used to retrieve trusted domain credentials by
|
||||
* AD Trust Agents across the IPA topology */
|
||||
failed += !set_krb_princ(ipasam_state, tmp_ctx,
|
||||
r_tdo_alias, princ_r_tdo,
|
||||
princ_r_tdo, r_tdo_alias,
|
||||
pwd_incoming, trusted_dn,
|
||||
(KRB_PRINC_CREATE_DISABLED |
|
||||
KRB_PRINC_CREATE_AGENT_PERMISSION));
|
||||
@@ -2232,8 +2232,9 @@ static bool handle_cross_realm_princs(struct ipasam_private *ipasam_state,
|
||||
pwd_outgoing, trusted_dn,
|
||||
KRB_PRINC_CREATE_DEFAULT);
|
||||
|
||||
/* Second: <REMOTE FLAT NAME>$@<OUR REALM>, enabled by default
|
||||
/* Second: <krbtgt/REMOTE FLAT NAME>@<OUR REALM>, enabled by default
|
||||
* as it is used for a remote DC to authenticate against IPA Samba
|
||||
* Uses <REMOTE FLAT NAME$>@<OUR REALM> as an alias.
|
||||
*
|
||||
* A local account for the outbound trust must have
|
||||
* POSIX and SMB identities associated with our domain but we associate
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
* Program may make changes or additions to the list of Approved
|
||||
* Interfaces.
|
||||
*
|
||||
* Copyright (C) 2010 Red Hat, Inc.
|
||||
* Copyright (C) 2010-2023 Red Hat, Inc.
|
||||
* All rights reserved.
|
||||
* END COPYRIGHT BLOCK **/
|
||||
|
||||
@@ -67,6 +67,10 @@
|
||||
"[file %s, line %d]: " fmt, \
|
||||
__FILE__, __LINE__, ##__VA_ARGS__)
|
||||
|
||||
#define LOG_PWDPOLICY(fmt, ...) \
|
||||
slapi_log_error(SLAPI_LOG_PWDPOLICY, log_func, fmt, ##__VA_ARGS__)
|
||||
|
||||
/* "Trace" logging is very expensive and should be avoided/replaced. TBD */
|
||||
#define LOG_TRACE(fmt, ...) \
|
||||
slapi_log_error(SLAPI_LOG_TRACE, log_func, fmt, ##__VA_ARGS__)
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
* Program may make changes or additions to the list of Approved
|
||||
* Interfaces.
|
||||
*
|
||||
* Copyright (C) 2005 Red Hat, Inc.
|
||||
* Copyright (C) 2005-2023 Red Hat, Inc.
|
||||
* All rights reserved.
|
||||
* END COPYRIGHT BLOCK **/
|
||||
|
||||
@@ -132,7 +132,8 @@ ipa_join(Slapi_PBlock *pb)
|
||||
Slapi_DN *sdn;
|
||||
Slapi_Backend *be;
|
||||
Slapi_Entry **es = NULL;
|
||||
int rc=0, ret=0, res, i;
|
||||
int rc=0, ret=0, res;
|
||||
size_t i;
|
||||
int is_root=0;
|
||||
char *krbLastPwdChange = NULL;
|
||||
char *fqdn = NULL;
|
||||
@@ -204,7 +205,7 @@ ipa_join(Slapi_PBlock *pb)
|
||||
|
||||
/* if there is none or more than one, freak out */
|
||||
if (i != 1) {
|
||||
LOG_TRACE("Too many entries, or entry no found (%d)", i);
|
||||
LOG_TRACE("Too many entries, or entry no found (%lu)\n", i);
|
||||
if (i == 0)
|
||||
errMesg = "Host not found.\n";
|
||||
else
|
||||
@@ -217,7 +218,7 @@ ipa_join(Slapi_PBlock *pb)
|
||||
/* Is this host already enrolled? */
|
||||
krbLastPwdChange = slapi_entry_attr_get_charptr(targetEntry, "krbLastPwdChange");
|
||||
if (NULL != krbLastPwdChange) {
|
||||
LOG_TRACE("Host already enrolled");
|
||||
LOG_TRACE("Host already enrolled\n");
|
||||
errMesg = "Host already enrolled.\n";
|
||||
rc = LDAP_OPERATIONS_ERROR;
|
||||
goto free_and_return;
|
||||
@@ -313,8 +314,8 @@ done:
|
||||
ret = slapi_pblock_set(pb, SLAPI_EXT_OP_RET_OID, JOIN_OID);
|
||||
if (!ret) ret = slapi_pblock_set(pb, SLAPI_EXT_OP_RET_VALUE, &retbval);
|
||||
if (ret) {
|
||||
errMesg = "Could not set return values";
|
||||
LOG("%s\n", errMesg);
|
||||
errMesg = "Could not set return values\n";
|
||||
LOG("%s", errMesg);
|
||||
rc = SLAPI_PLUGIN_EXTENDED_SENT_RESULT;
|
||||
}
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
* Program may make changes or additions to the list of Approved
|
||||
* Interfaces.
|
||||
*
|
||||
* Copyright (C) 2022 Red Hat, Inc.
|
||||
* Copyright (C) 2022-2023 Red Hat, Inc.
|
||||
* All rights reserved.
|
||||
* END COPYRIGHT BLOCK **/
|
||||
|
||||
@@ -447,7 +447,7 @@ static int ipagraceperiod_preop(Slapi_PBlock *pb)
|
||||
LOG_TRACE("grace limit disabled, skipping\n");
|
||||
goto done;
|
||||
} else if (grace_limit < -1) {
|
||||
LOG_FATAL("Invalid passwordGraceLimit value %d\n", grace_limit);
|
||||
LOG_FATAL("Invalid passwordGraceLimit value %ld\n", grace_limit);
|
||||
return LDAP_OPERATIONS_ERROR;
|
||||
}
|
||||
|
||||
@@ -480,7 +480,7 @@ static int ipagraceperiod_preop(Slapi_PBlock *pb)
|
||||
slapi_pwpolicy_make_response_control(pb, -1, grace_limit - grace_user_time , -1);
|
||||
}
|
||||
} else if (grace_user_time >= grace_limit) {
|
||||
LOG_TRACE("%s password is expired and out of grace limit\n", dn);
|
||||
LOG_PWDPOLICY("%s password is expired and out of grace limit\n", dn);
|
||||
errstr = "Password is expired.\n";
|
||||
ret = LDAP_INVALID_CREDENTIALS;
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
* Program may make changes or additions to the list of Approved
|
||||
* Interfaces.
|
||||
*
|
||||
* Copyright (C) 2010 Red Hat, Inc.
|
||||
* Copyright (C) 2010-2023 Red Hat, Inc.
|
||||
* All rights reserved.
|
||||
* END COPYRIGHT BLOCK **/
|
||||
|
||||
@@ -823,13 +823,15 @@ static int ipalockout_preop(Slapi_PBlock *pb)
|
||||
if (failedcount >= max_fail) {
|
||||
if (lockout_duration == 0) {
|
||||
errstr = "Entry permanently locked.\n";
|
||||
LOG_PWDPOLICY("Entry '%s' is permanently locked.\n", dn);
|
||||
ret = LDAP_UNWILLING_TO_PERFORM;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (time_now < last_failed + lockout_duration) {
|
||||
/* Too many failures */
|
||||
LOG_TRACE("Too many failed logins. %lu out of %d\n", failedcount, max_fail);
|
||||
LOG_PWDPOLICY("Too many failed logins for '%s'. %lu out of %d\n",
|
||||
dn, failedcount, max_fail);
|
||||
errstr = "Too many failed logins.\n";
|
||||
ret = LDAP_UNWILLING_TO_PERFORM;
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
* Program may make changes or additions to the list of Approved
|
||||
* Interfaces.
|
||||
*
|
||||
* Copyright (C) 2010 Red Hat, Inc.
|
||||
* Copyright (C) 2010-2023 Red Hat, Inc.
|
||||
* All rights reserved.
|
||||
* END COPYRIGHT BLOCK **/
|
||||
|
||||
@@ -350,7 +350,6 @@ ipamodrdn_load_plugin_config(void)
|
||||
{
|
||||
int status = EOK;
|
||||
int result;
|
||||
int i;
|
||||
Slapi_PBlock *search_pb;
|
||||
Slapi_Entry **entries = NULL;
|
||||
|
||||
@@ -379,7 +378,7 @@ ipamodrdn_load_plugin_config(void)
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
for (i = 0; (entries[i] != NULL); i++) {
|
||||
for (size_t i = 0; (entries[i] != NULL); i++) {
|
||||
/* We don't care about the status here because we may have
|
||||
* some invalid config entries, but we just want to continue
|
||||
* looking for valid ones. */
|
||||
@@ -680,7 +679,8 @@ ipamodrdn_change_attr(struct configEntry *cfgentry,
|
||||
slapi_modify_internal_pb(mod_pb);
|
||||
slapi_pblock_get(mod_pb, SLAPI_PLUGIN_INTOP_RESULT, &ret);
|
||||
if (ret != LDAP_SUCCESS) {
|
||||
LOG_FATAL("Failed to change attribute with error %d\n", ret);
|
||||
LOG_FATAL("Failed to change attribute '%s' in '%s' with error %d\n",
|
||||
cfgentry->tattr, targetdn, ret);
|
||||
ret = EFAIL;
|
||||
}
|
||||
ret = EOK;
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
* Authors:
|
||||
* Nathaniel McCallum <npmccallum@redhat.com>
|
||||
*
|
||||
* Copyright (C) 2014 Red Hat, Inc.
|
||||
* Copyright (C) 2014-2023 Red Hat, Inc.
|
||||
* All rights reserved.
|
||||
* END COPYRIGHT BLOCK **/
|
||||
|
||||
@@ -295,14 +295,16 @@ preop_mod(Slapi_PBlock *pb)
|
||||
}
|
||||
|
||||
if (!simulate(mods, attr, cpre, &cpost) && repl == 0) {
|
||||
msg = slapi_ch_smprintf("Invalid operation sequence on %s", attr);
|
||||
msg = slapi_ch_smprintf("Invalid operation sequence on %s (%s)",
|
||||
attr, slapi_entry_get_dn_const(epre));
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (cpost < cpre) {
|
||||
if (repl == 0) {
|
||||
msg = slapi_ch_smprintf("Will not %s %s",
|
||||
cpost == COUNTER_UNSET ? "delete" : "decrement", attr);
|
||||
msg = slapi_ch_smprintf("Will not %s %s (%s)",
|
||||
cpost == COUNTER_UNSET ? "delete" : "decrement",
|
||||
attr, slapi_entry_get_dn_const(epre));
|
||||
goto error;
|
||||
}
|
||||
|
||||
@@ -321,6 +323,9 @@ preop_mod(Slapi_PBlock *pb)
|
||||
|
||||
error:
|
||||
rc = LDAP_UNWILLING_TO_PERFORM;
|
||||
if (msg) {
|
||||
LOG("%s - error %d\n", msg, rc);
|
||||
}
|
||||
slapi_send_ldap_result(pb, rc, NULL, msg, 0, NULL);
|
||||
if (slapi_pblock_set(pb, SLAPI_RESULT_CODE, &rc)) {
|
||||
LOG_FATAL("slapi_pblock_set failed!\n");
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
* Authors:
|
||||
* Nathaniel McCallum <npmccallum@redhat.com>
|
||||
*
|
||||
* Copyright (C) 2014 Red Hat, Inc.
|
||||
* Copyright (C) 2014-2023 Red Hat, Inc.
|
||||
* All rights reserved.
|
||||
* END COPYRIGHT BLOCK **/
|
||||
|
||||
@@ -41,6 +41,8 @@
|
||||
|
||||
#include <slapi-plugin.h>
|
||||
|
||||
#define IPA_PLUGIN_NAME "ipa-otp-counter"
|
||||
|
||||
long long
|
||||
ldapmod_get_value(const LDAPMod *mod, long long def);
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
* Authors:
|
||||
* Nathaniel McCallum <npmccallum@redhat.com>
|
||||
*
|
||||
* Copyright (C) 2013 Red Hat, Inc.
|
||||
* Copyright (C) 2013-2023 Red Hat, Inc.
|
||||
* All rights reserved.
|
||||
* END COPYRIGHT BLOCK **/
|
||||
|
||||
@@ -46,7 +46,7 @@
|
||||
|
||||
#include "util.h"
|
||||
|
||||
#define PLUGIN_NAME "ipa-otp-lasttoken"
|
||||
#define IPA_PLUGIN_NAME "ipa-otp-lasttoken"
|
||||
#define OTP_CONTAINER "cn=otp,%s"
|
||||
|
||||
static struct otp_config *otp_config;
|
||||
@@ -191,9 +191,14 @@ static inline int send_error(Slapi_PBlock *pb, int rc, const char *errstr)
|
||||
|
||||
static int preop_del(Slapi_PBlock *pb)
|
||||
{
|
||||
char *dn = NULL;
|
||||
|
||||
if (is_allowed(pb, NULL))
|
||||
return 0;
|
||||
|
||||
slapi_pblock_get(pb, SLAPI_TARGET_DN, &dn);
|
||||
LOG("Can't delete last active token (%s)", dn);
|
||||
|
||||
return send_error(pb, LDAP_UNWILLING_TO_PERFORM,
|
||||
"Can't delete last active token");
|
||||
}
|
||||
@@ -221,10 +226,12 @@ static int preop_mod(Slapi_PBlock *pb)
|
||||
return 0;
|
||||
|
||||
/* If a protected attribute is modified, deny. */
|
||||
for (int i = 0; mods != NULL && mods[i] != NULL; i++) {
|
||||
for (int j = 0; errors[j].attr != NULL; j++) {
|
||||
if (strcasecmp(mods[i]->mod_type, errors[j].attr) == 0)
|
||||
for (size_t i = 0; mods != NULL && mods[i] != NULL; i++) {
|
||||
for (size_t j = 0; errors[j].attr != NULL; j++) {
|
||||
if (strcasecmp(mods[i]->mod_type, errors[j].attr) == 0) {
|
||||
LOG("%s (%s)", errors[j].msg, slapi_entry_get_dn_const(entry));
|
||||
return send_error(pb, LDAP_UNWILLING_TO_PERFORM, errors[j].msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -284,7 +291,7 @@ static int ipa_otp_lasttoken_start(Slapi_PBlock *pb)
|
||||
int ipa_otp_lasttoken_init(Slapi_PBlock *pb)
|
||||
{
|
||||
static const Slapi_PluginDesc preop_desc = {
|
||||
PLUGIN_NAME,
|
||||
IPA_PLUGIN_NAME,
|
||||
"FreeIPA",
|
||||
"FreeIPA/1.0",
|
||||
"Protect the user's last active token"
|
||||
@@ -297,14 +304,14 @@ int ipa_otp_lasttoken_init(Slapi_PBlock *pb)
|
||||
ret |= slapi_pblock_set(pb, SLAPI_PLUGIN_VERSION, SLAPI_PLUGIN_VERSION_01);
|
||||
ret |= slapi_pblock_set(pb, SLAPI_PLUGIN_DESCRIPTION, (void *) &preop_desc);
|
||||
ret |= slapi_register_plugin("betxnpreoperation", 1, __func__, preop_init,
|
||||
PLUGIN_NAME " betxnpreoperation", NULL,
|
||||
IPA_PLUGIN_NAME " betxnpreoperation", NULL,
|
||||
ipa_otp_lasttoken_plugin_id);
|
||||
ret |= slapi_register_plugin("postoperation", 1, __func__, postop_init,
|
||||
PLUGIN_NAME " postoperation", NULL,
|
||||
IPA_PLUGIN_NAME " postoperation", NULL,
|
||||
ipa_otp_lasttoken_plugin_id);
|
||||
ret |= slapi_register_plugin("internalpostoperation", 1, __func__,
|
||||
intpostop_init,
|
||||
PLUGIN_NAME " internalpostoperation", NULL,
|
||||
IPA_PLUGIN_NAME " internalpostoperation", NULL,
|
||||
ipa_otp_lasttoken_plugin_id);
|
||||
ret |= slapi_pblock_set(pb, SLAPI_PLUGIN_START_FN,
|
||||
(void *)ipa_otp_lasttoken_start);
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
* Authors:
|
||||
* Simo Sorce <ssorce@redhat.com>
|
||||
*
|
||||
* Copyright (C) 2007-2010 Red Hat, Inc.
|
||||
* Copyright (C) 2007-2023 Red Hat, Inc.
|
||||
* All rights reserved.
|
||||
* END COPYRIGHT BLOCK **/
|
||||
|
||||
@@ -81,7 +81,9 @@ static struct ipapwd_krbcfg *ipapwd_getConfig(void)
|
||||
char **encsalts;
|
||||
char **tmparray;
|
||||
char *tmpstr;
|
||||
int i, ret;
|
||||
int ret;
|
||||
size_t i;
|
||||
bool fips_enabled = false;
|
||||
|
||||
config = calloc(1, sizeof(struct ipapwd_krbcfg));
|
||||
if (!config) {
|
||||
@@ -240,28 +242,35 @@ static struct ipapwd_krbcfg *ipapwd_getConfig(void)
|
||||
config->allow_nt_hash = false;
|
||||
if (ipapwd_fips_enabled()) {
|
||||
LOG("FIPS mode is enabled, NT hashes are not allowed.\n");
|
||||
fips_enabled = true;
|
||||
}
|
||||
|
||||
sdn = slapi_sdn_new_dn_byval(ipa_etc_config_dn);
|
||||
ret = ipapwd_getEntry(sdn, &config_entry, NULL);
|
||||
slapi_sdn_free(&sdn);
|
||||
if (ret != LDAP_SUCCESS) {
|
||||
LOG_FATAL("No config Entry?\n");
|
||||
goto free_and_error;
|
||||
} else {
|
||||
sdn = slapi_sdn_new_dn_byval(ipa_etc_config_dn);
|
||||
ret = ipapwd_getEntry(sdn, &config_entry, NULL);
|
||||
slapi_sdn_free(&sdn);
|
||||
if (ret != LDAP_SUCCESS) {
|
||||
LOG_FATAL("No config Entry?\n");
|
||||
goto free_and_error;
|
||||
} else {
|
||||
tmparray = slapi_entry_attr_get_charray(config_entry,
|
||||
"ipaConfigString");
|
||||
for (i = 0; tmparray && tmparray[i]; i++) {
|
||||
tmparray = slapi_entry_attr_get_charray(config_entry,
|
||||
"ipaConfigString");
|
||||
for (i = 0; tmparray && tmparray[i]; i++) {
|
||||
if (strcasecmp(tmparray[i], "EnforceLDAPOTP") == 0) {
|
||||
config->enforce_ldap_otp = true;
|
||||
continue;
|
||||
}
|
||||
if (!fips_enabled) {
|
||||
if (strcasecmp(tmparray[i], "AllowNThash") == 0) {
|
||||
config->allow_nt_hash = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (tmparray) slapi_ch_array_free(tmparray);
|
||||
}
|
||||
|
||||
slapi_entry_free(config_entry);
|
||||
if (tmparray) slapi_ch_array_free(tmparray);
|
||||
}
|
||||
|
||||
slapi_entry_free(config_entry);
|
||||
|
||||
return config;
|
||||
|
||||
free_and_error:
|
||||
@@ -327,7 +336,8 @@ int ipapwd_getPolicy(const char *dn,
|
||||
"ipaPwdUserCheck", NULL};
|
||||
Slapi_Entry **es = NULL;
|
||||
Slapi_Entry *pe = NULL;
|
||||
int ret, res, scope, i;
|
||||
int ret, res, scope;
|
||||
size_t i;
|
||||
int buffer_flags=0;
|
||||
Slapi_ValueSet* results = NULL;
|
||||
char *actual_type_name = NULL;
|
||||
@@ -545,7 +555,7 @@ int ipapwd_gen_checks(Slapi_PBlock *pb, char **errMesg,
|
||||
}
|
||||
sdn = slapi_sdn_new_dn_byref(dn);
|
||||
if (!sdn) {
|
||||
LOG_FATAL("Unable to convert dn to sdn %s", dn ? dn : "<NULL>");
|
||||
LOG_FATAL("Unable to convert dn to sdn %s\n", dn ? dn : "<NULL>");
|
||||
*errMesg = "Internal Error";
|
||||
rc = LDAP_OPERATIONS_ERROR;
|
||||
goto done;
|
||||
@@ -564,11 +574,18 @@ int ipapwd_gen_checks(Slapi_PBlock *pb, char **errMesg,
|
||||
/* get the kerberos context and master key */
|
||||
*config = ipapwd_getConfig();
|
||||
if (NULL == *config) {
|
||||
LOG_FATAL("Error Retrieving Master Key");
|
||||
LOG_FATAL("Error Retrieving Master Key\n");
|
||||
*errMesg = "Fatal Internal Error";
|
||||
rc = LDAP_OPERATIONS_ERROR;
|
||||
}
|
||||
|
||||
/* do not return the master key if asked */
|
||||
if (check_flags & IPAPWD_CHECK_ONLY_CONFIG) {
|
||||
free((*config)->kmkey->contents);
|
||||
free((*config)->kmkey);
|
||||
(*config)->kmkey = NULL;
|
||||
}
|
||||
|
||||
done:
|
||||
return rc;
|
||||
}
|
||||
@@ -594,7 +611,7 @@ int ipapwd_CheckPolicy(struct ipapwd_data *data)
|
||||
/* Find the entry with the password policy */
|
||||
ret = ipapwd_getPolicy(data->dn, data->target, &pol);
|
||||
if (ret) {
|
||||
LOG_TRACE("No password policy, use defaults");
|
||||
LOG_TRACE("No password policy, use defaults\n");
|
||||
}
|
||||
break;
|
||||
case IPA_CHANGETYPE_ADMIN:
|
||||
@@ -620,14 +637,14 @@ int ipapwd_CheckPolicy(struct ipapwd_data *data)
|
||||
*/
|
||||
ret = ipapwd_getPolicy(data->dn, data->target, &tmppol);
|
||||
if (ret) {
|
||||
LOG_TRACE("No password policy, use defaults");
|
||||
LOG_TRACE("No password policy, use defaults\n");
|
||||
} else {
|
||||
pol.max_pwd_life = tmppol.max_pwd_life;
|
||||
pol.history_length = tmppol.history_length;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
LOG_TRACE("Unknown password change type, use defaults");
|
||||
LOG_TRACE("Unknown password change type, use defaults\n");
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -860,7 +877,7 @@ int ipapwd_SetPassword(struct ipapwd_krbcfg *krbcfg,
|
||||
case IPA_CHANGETYPE_DSMGR:
|
||||
case IPA_CHANGETYPE_ADMIN:
|
||||
/* Mark as administratively reset which will unlock acct */
|
||||
ret = ipapwd_setdate(data->target, smods,
|
||||
ret = ipapwd_setdate(data->target, smods,
|
||||
"krbLastAdminUnlock",
|
||||
data->timeNow, false);
|
||||
if (ret != LDAP_SUCCESS)
|
||||
@@ -951,7 +968,7 @@ Slapi_Value **ipapwd_setPasswordHistory(Slapi_Mods *smods,
|
||||
char **new_pwd_history = NULL;
|
||||
int n = 0;
|
||||
int ret;
|
||||
int i;
|
||||
size_t i;
|
||||
|
||||
pwd_history = slapi_entry_attr_get_charray(data->target,
|
||||
"passwordHistory");
|
||||
@@ -1083,10 +1100,9 @@ int ipapwd_set_extradata(const char *dn,
|
||||
void ipapwd_free_slapi_value_array(Slapi_Value ***svals)
|
||||
{
|
||||
Slapi_Value **sv = *svals;
|
||||
int i;
|
||||
|
||||
if (sv) {
|
||||
for (i = 0; sv[i]; i++) {
|
||||
for (size_t i = 0; sv[i]; i++) {
|
||||
slapi_value_free(&sv[i]);
|
||||
}
|
||||
}
|
||||
@@ -1102,8 +1118,10 @@ void free_ipapwd_krbcfg(struct ipapwd_krbcfg **cfg)
|
||||
|
||||
krb5_free_default_realm(c->krbctx, c->realm);
|
||||
krb5_free_context(c->krbctx);
|
||||
free(c->kmkey->contents);
|
||||
free(c->kmkey);
|
||||
if (c->kmkey) {
|
||||
free(c->kmkey->contents);
|
||||
free(c->kmkey);
|
||||
}
|
||||
free(c->supp_encsalts);
|
||||
free(c->pref_encsalts);
|
||||
slapi_ch_array_free(c->passsync_mgrs);
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
* Authors:
|
||||
* Simo Sorce <ssorce@redhat.com>
|
||||
*
|
||||
* Copyright (C) 2007-2010 Red Hat, Inc.
|
||||
* Copyright (C) 2007-2023 Red Hat, Inc.
|
||||
* All rights reserved.
|
||||
* END COPYRIGHT BLOCK **/
|
||||
|
||||
@@ -231,7 +231,7 @@ int ipapwd_gen_hashes(struct ipapwd_krbcfg *krbcfg,
|
||||
|
||||
if (!*svals) {
|
||||
/* errMesg should have been set in encrypt_encode_key() */
|
||||
LOG_FATAL("key encryption/encoding failed\n");
|
||||
LOG_FATAL("key encryption/encoding failed (%s)\n", *errMesg);
|
||||
rc = LDAP_OPERATIONS_ERROR;
|
||||
goto done;
|
||||
}
|
||||
@@ -267,6 +267,7 @@ int ipapwd_gen_hashes(struct ipapwd_krbcfg *krbcfg,
|
||||
}
|
||||
(*ntvals)[0] = slapi_value_new();
|
||||
if (slapi_value_set((*ntvals)[0], nt_key, 16) == NULL) {
|
||||
LOG("Failed to set value for nt_key");
|
||||
rc = LDAP_OPERATIONS_ERROR;
|
||||
goto done;
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
* Authors:
|
||||
* Simo Sorce <ssorce@redhat.com>
|
||||
*
|
||||
* Copyright (C) 2007-2010 Red Hat, Inc.
|
||||
* Copyright (C) 2007-2023 Red Hat, Inc.
|
||||
* All rights reserved.
|
||||
* END COPYRIGHT BLOCK **/
|
||||
|
||||
@@ -108,7 +108,7 @@ static void filter_keys(struct ipapwd_krbcfg *krbcfg,
|
||||
struct ipapwd_keyset *kset,
|
||||
bool allow_nthash)
|
||||
{
|
||||
int i, j;
|
||||
size_t i, j;
|
||||
|
||||
for (i = 0; i < kset->num_keys; i++) {
|
||||
for (j = 0; j < krbcfg->num_supp_encsalts; j++) {
|
||||
@@ -151,11 +151,11 @@ static void filter_enctypes(struct ipapwd_krbcfg *krbcfg,
|
||||
bool allow_nthash)
|
||||
{
|
||||
/* first filter for duplicates */
|
||||
for (int i = 0; i + 1 < *num_kenctypes; i++) {
|
||||
for (int j = i + 1; j < *num_kenctypes; j++) {
|
||||
for (size_t i = 0; i + 1 < *num_kenctypes; i++) {
|
||||
for (size_t j = i + 1; j < *num_kenctypes; j++) {
|
||||
if (kenctypes[i].ks_enctype == kenctypes[j].ks_enctype) {
|
||||
/* duplicate, filter out */
|
||||
for (int k = j; k + 1 < *num_kenctypes; k++) {
|
||||
for (size_t k = j; k + 1 < *num_kenctypes; k++) {
|
||||
kenctypes[k].ks_enctype = kenctypes[k + 1].ks_enctype;
|
||||
kenctypes[k].ks_salttype = kenctypes[k + 1].ks_salttype;
|
||||
}
|
||||
@@ -166,8 +166,8 @@ static void filter_enctypes(struct ipapwd_krbcfg *krbcfg,
|
||||
}
|
||||
|
||||
/* then filter for supported */
|
||||
for (int i = 0; i < *num_kenctypes; i++) {
|
||||
int j;
|
||||
for (size_t i = 0; i < *num_kenctypes; i++) {
|
||||
size_t j;
|
||||
|
||||
/* Check if supported */
|
||||
for (j = 0; j < krbcfg->num_supp_encsalts; j++) {
|
||||
@@ -184,7 +184,7 @@ static void filter_enctypes(struct ipapwd_krbcfg *krbcfg,
|
||||
}
|
||||
if (j == krbcfg->num_supp_encsalts) {
|
||||
/* Unsupported, filter out */
|
||||
for (int k = i; k + 1 < *num_kenctypes; k++) {
|
||||
for (size_t k = i; k + 1 < *num_kenctypes; k++) {
|
||||
kenctypes[k].ks_enctype = kenctypes[k + 1].ks_enctype;
|
||||
kenctypes[k].ks_salttype = kenctypes[k + 1].ks_salttype;
|
||||
}
|
||||
@@ -344,6 +344,8 @@ parse_req_done:
|
||||
|
||||
rc = ipapwd_check_max_pwd_len(strlen(newPasswd), &errMesg);
|
||||
if (rc) {
|
||||
LOG_PWDPOLICY("Failed to set password credentials for '%s': %s\n",
|
||||
bindDN, errMesg);
|
||||
goto free_and_return;
|
||||
}
|
||||
|
||||
@@ -456,7 +458,7 @@ parse_req_done:
|
||||
char *cur_pw;
|
||||
|
||||
if (oldPasswd == NULL || *oldPasswd == '\0') {
|
||||
LOG_FATAL("Old password was not provided!\n");
|
||||
LOG_FATAL("Old password was not provided for '%s'!\n", dn);
|
||||
rc = LDAP_INVALID_CREDENTIALS;
|
||||
goto free_and_return;
|
||||
}
|
||||
@@ -466,7 +468,7 @@ parse_req_done:
|
||||
cur_pw = slapi_entry_attr_get_charptr(targetEntry,
|
||||
"userPassword");
|
||||
if (!cur_pw) {
|
||||
LOG_FATAL("User has no current password?\n");
|
||||
LOG_FATAL("User '%s' does not have a current password?\n", dn);
|
||||
rc = LDAP_UNWILLING_TO_PERFORM;
|
||||
goto free_and_return;
|
||||
}
|
||||
@@ -485,7 +487,7 @@ parse_req_done:
|
||||
slapi_value_free(&pw);
|
||||
|
||||
if (ret != 0) {
|
||||
LOG_TRACE("Invalid password!\n");
|
||||
LOG_TRACE("Invalid password for '%s'!\n", dn);
|
||||
rc = LDAP_INVALID_CREDENTIALS;
|
||||
goto free_and_return;
|
||||
}
|
||||
@@ -579,11 +581,9 @@ parse_req_done:
|
||||
/* special cases */
|
||||
if ((strcasecmp(dn, bindDN) != 0) &&
|
||||
(strcasecmp(ipa_changepw_principal_dn, bindDN) != 0)) {
|
||||
int i;
|
||||
|
||||
pwdata.changetype = IPA_CHANGETYPE_ADMIN;
|
||||
|
||||
for (i = 0; i < krbcfg->num_passsync_mgrs; i++) {
|
||||
for (size_t i = 0; i < krbcfg->num_passsync_mgrs; i++) {
|
||||
if (strcasecmp(krbcfg->passsync_mgrs[i], bindDN) == 0) {
|
||||
pwdata.changetype = IPA_CHANGETYPE_DSMGR;
|
||||
break;
|
||||
@@ -606,6 +606,8 @@ parse_req_done:
|
||||
errMesg = ipapwd_error2string(ret);
|
||||
ret = ipapwd_to_ldap_pwpolicy_error(ret);
|
||||
slapi_pwpolicy_make_response_control(pb, -1, -1, ret);
|
||||
LOG_PWDPOLICY("Failed to set password credentials for"
|
||||
" '%s': %s\n", dn, errMesg);
|
||||
rc = LDAP_CONSTRAINT_VIOLATION;
|
||||
goto free_and_return;
|
||||
}
|
||||
@@ -666,7 +668,7 @@ free_and_return:
|
||||
if (targetEntry) slapi_entry_free(targetEntry);
|
||||
if (ber) ber_free(ber, 1);
|
||||
|
||||
LOG("%s", errMesg ? errMesg : "success");
|
||||
LOG("%s\n", errMesg ? errMesg : "success");
|
||||
slapi_send_ldap_result(pb, rc, NULL, errMesg, 0, NULL);
|
||||
|
||||
return SLAPI_PLUGIN_EXTENDED_SENT_RESULT;
|
||||
@@ -732,7 +734,8 @@ static Slapi_Entry *get_entry_by_principal(const char *principal)
|
||||
"krbCanonicalName",
|
||||
"enrolledBy", NULL };
|
||||
Slapi_Entry **es = NULL;
|
||||
int res, ret, i;
|
||||
int res, ret;
|
||||
size_t i;
|
||||
Slapi_Entry *entry = NULL;
|
||||
|
||||
/* Find ancestor base DN */
|
||||
@@ -774,7 +777,7 @@ static Slapi_Entry *get_entry_by_principal(const char *principal)
|
||||
|
||||
/* if there is none or more than one, freak out */
|
||||
if (i != 1) {
|
||||
LOG_TRACE("Too many entries, or entry no found (%d)", i);
|
||||
LOG_TRACE("Too many entries, or entry no found (%ld)\n", i);
|
||||
goto free_and_return;
|
||||
}
|
||||
entry = slapi_entry_dup(es[0]);
|
||||
@@ -809,7 +812,7 @@ static bool is_allowed_to_access_attr(Slapi_PBlock *pb, char *bindDN,
|
||||
*/
|
||||
be = get_realm_backend();
|
||||
if (!be) {
|
||||
LOG_FATAL("Could not fetch REALM backend!");
|
||||
LOG_FATAL("Could not fetch REALM backend!\n");
|
||||
return false;
|
||||
}
|
||||
if (slapi_pblock_set(pb, SLAPI_BACKEND, be)) {
|
||||
@@ -868,7 +871,8 @@ static void remove_user_password(Slapi_Mods *smods,
|
||||
if ((NULL != pw) && (NULL == krbLastPwdChange)) {
|
||||
slapi_mods_add_mod_values(smods, LDAP_MOD_DELETE,
|
||||
"userPassword", NULL);
|
||||
LOG_TRACE("Removing userPassword from host entry\n");
|
||||
LOG_TRACE("Removing userPassword from host entry '%s'\n",
|
||||
slapi_entry_get_dn_const(targetEntry));
|
||||
}
|
||||
}
|
||||
if (krbLastPwdChange) slapi_ch_free_string(&krbLastPwdChange);
|
||||
@@ -891,8 +895,9 @@ static int store_new_keys(Slapi_Entry *target, char *svcname, char *bind_dn,
|
||||
rc = set_krbLastPwdChange(smods, time_now);
|
||||
if (rc) {
|
||||
rc = LDAP_OPERATIONS_ERROR;
|
||||
LOG_FATAL("Failed to set krbLastPwdChange");
|
||||
err_msg = "Internal error while storing keytab data\n";
|
||||
LOG_FATAL("Failed to set krbLastPwdChange for target '%s'\n",
|
||||
slapi_entry_get_dn_const(target));
|
||||
err_msg = "Internal error while storing keytab data";
|
||||
goto done;
|
||||
}
|
||||
|
||||
@@ -905,8 +910,9 @@ static int store_new_keys(Slapi_Entry *target, char *svcname, char *bind_dn,
|
||||
rc = ipapwd_apply_mods(slapi_entry_get_dn_const(target), smods);
|
||||
if (rc != LDAP_SUCCESS) {
|
||||
rc = LDAP_OPERATIONS_ERROR;
|
||||
LOG_FATAL("Failed to apply mods");
|
||||
err_msg = "Internal error while saving keys\n";
|
||||
LOG_FATAL("Failed to apply mods to target '%s'\n",
|
||||
slapi_entry_get_dn_const(target));
|
||||
err_msg = "Internal error while saving keys";
|
||||
goto done;
|
||||
}
|
||||
|
||||
@@ -914,8 +920,9 @@ static int store_new_keys(Slapi_Entry *target, char *svcname, char *bind_dn,
|
||||
svcname, time_now);
|
||||
if (rc != LDAP_SUCCESS) {
|
||||
rc = LDAP_OPERATIONS_ERROR;
|
||||
LOG_FATAL("Failed to set extradata");
|
||||
err_msg = "Internal error while saving keytab extradata\n";
|
||||
LOG_FATAL("Failed to set extradata for target '%s'\n",
|
||||
slapi_entry_get_dn_const(target));
|
||||
err_msg = "Internal error while saving keytab extradata";
|
||||
goto done;
|
||||
}
|
||||
|
||||
@@ -1003,7 +1010,7 @@ static int decode_setkeytab_request(krb5_context krbctx,
|
||||
kset->mkvno = mkvno;
|
||||
|
||||
rtag = ber_peek_tag(ber, &tlen);
|
||||
for (int i = 0; rtag == LBER_SEQUENCE; i++) {
|
||||
for (size_t i = 0; rtag == LBER_SEQUENCE; i++) {
|
||||
krb5_key_data *newset;
|
||||
ber_tag_t ctag;
|
||||
ber_int_t type;
|
||||
@@ -1181,29 +1188,29 @@ static int encode_setkeytab_reply(struct ipapwd_keyset *kset,
|
||||
rc = ber_printf(ber, "{i{", (ber_int_t)kset->keys[0].key_data_kvno);
|
||||
if (rc == -1) {
|
||||
rc = LDAP_OPERATIONS_ERROR;
|
||||
LOG_FATAL("Failed to ber_printf the kvno");
|
||||
LOG_FATAL("Failed to ber_printf the kvno\n");
|
||||
goto done;
|
||||
}
|
||||
|
||||
for (int i = 0; i < kset->num_keys; i++) {
|
||||
for (size_t i = 0; i < kset->num_keys; i++) {
|
||||
rc = ber_printf(ber, "{i}", (ber_int_t)kset->keys[i].key_data_type[0]);
|
||||
if (rc == -1) {
|
||||
rc = LDAP_OPERATIONS_ERROR;
|
||||
LOG_FATAL("Failed to ber_printf the enctype");
|
||||
LOG_FATAL("Failed to ber_printf the enctype\n");
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
rc = ber_printf(ber, "}}");
|
||||
if (rc == -1) {
|
||||
rc = LDAP_OPERATIONS_ERROR;
|
||||
LOG_FATAL("Failed to ber_printf the termination");
|
||||
LOG_FATAL("Failed to ber_printf the termination\n");
|
||||
goto done;
|
||||
}
|
||||
|
||||
rc = ber_flatten(ber, &bvp);
|
||||
if (rc == -1) {
|
||||
rc = LDAP_OPERATIONS_ERROR;
|
||||
LOG_FATAL("Failed to ber_flatten the buffer");
|
||||
LOG_FATAL("Failed to ber_flatten the buffer\n");
|
||||
goto done;
|
||||
}
|
||||
|
||||
@@ -1306,7 +1313,7 @@ static int ipapwd_setkeytab(Slapi_PBlock *pb, struct ipapwd_krbcfg *krbcfg)
|
||||
|
||||
/* get next kvno for entry (will be 1 if this is new) and fix keyset */
|
||||
kvno = ipapwd_get_cur_kvno(targetEntry) + 1;
|
||||
for (int i = 0; i < kset->num_keys; i++) {
|
||||
for (size_t i = 0; i < kset->num_keys; i++) {
|
||||
kset->keys[i].key_data_kvno = kvno;
|
||||
}
|
||||
|
||||
@@ -1352,7 +1359,7 @@ static int ipapwd_setkeytab(Slapi_PBlock *pb, struct ipapwd_krbcfg *krbcfg)
|
||||
|
||||
rc = encode_setkeytab_reply(kset, &bvp);
|
||||
if (rc) {
|
||||
errMesg = "Internal Error.\n";
|
||||
errMesg = "Internal Error.";
|
||||
goto free_and_return;
|
||||
}
|
||||
|
||||
@@ -1372,7 +1379,7 @@ free_and_return:
|
||||
if (targetEntry) slapi_entry_free(targetEntry);
|
||||
|
||||
if (svals) {
|
||||
for (int i = 0; svals[i]; i++) {
|
||||
for (size_t i = 0; svals[i]; i++) {
|
||||
slapi_value_free(&svals[i]);
|
||||
}
|
||||
free(svals);
|
||||
@@ -1382,7 +1389,7 @@ free_and_return:
|
||||
|
||||
if (rc == LDAP_SUCCESS)
|
||||
errMesg = NULL;
|
||||
LOG("%s", errMesg ? errMesg : "success");
|
||||
LOG("%s\n", errMesg ? errMesg : "success");
|
||||
slapi_send_ldap_result(pb, rc, NULL, errMesg, 0, NULL);
|
||||
|
||||
return SLAPI_PLUGIN_EXTENDED_SENT_RESULT;
|
||||
@@ -1403,7 +1410,6 @@ static int decode_getkeytab_request(struct berval *extop, bool *wantold,
|
||||
krb5_key_salt_tuple *enctypes = NULL;
|
||||
bool newkt;
|
||||
bool ret;
|
||||
int i;
|
||||
|
||||
ret = ipaasn1_dec_getkt(extop->bv_val, extop->bv_len, &newkt,
|
||||
&svcname, &password, &etypes, &numtypes);
|
||||
@@ -1423,7 +1429,7 @@ static int decode_getkeytab_request(struct berval *extop, bool *wantold,
|
||||
goto done;
|
||||
}
|
||||
|
||||
for (i = 0; i < numtypes; i++) {
|
||||
for (size_t i = 0; i < numtypes; i++) {
|
||||
enctypes[i].ks_enctype = etypes[i];
|
||||
enctypes[i].ks_salttype = KRB5_KDB_SALTTYPE_NORMAL;
|
||||
}
|
||||
@@ -1466,7 +1472,7 @@ static int encode_getkeytab_reply(krb5_context krbctx,
|
||||
/* uses last key kvno */
|
||||
kvno = keys[num_keys-1].key_data_kvno;
|
||||
|
||||
for (int i = 0; i < num_keys; i++) {
|
||||
for (size_t i = 0; i < num_keys; i++) {
|
||||
krb5_enc_data cipher = { 0 };
|
||||
krb5_data plain = { 0 };
|
||||
krb5_int16 plen;
|
||||
@@ -1516,7 +1522,7 @@ static int encode_getkeytab_reply(krb5_context krbctx,
|
||||
rc = LDAP_SUCCESS;
|
||||
|
||||
done:
|
||||
for (int i = 0; i < ksc.nkeys; i ++) {
|
||||
for (size_t i = 0; i < ksc.nkeys; i++) {
|
||||
free(ksc.ksdata[i].key.contents);
|
||||
}
|
||||
if (rc != LDAP_SUCCESS) {
|
||||
@@ -1632,7 +1638,7 @@ static int ipapwd_getkeytab(Slapi_PBlock *pb, struct ipapwd_krbcfg *krbcfg)
|
||||
* this operation. */
|
||||
if (bind_dn == NULL || *bind_dn == '\0') {
|
||||
/* Refuse the operation because they're bound anonymously */
|
||||
err_msg = "Anonymous Binds are not allowed.\n";
|
||||
err_msg = "Anonymous Binds are not allowed.";
|
||||
rc = LDAP_INSUFFICIENT_ACCESS;
|
||||
goto free_and_return;
|
||||
}
|
||||
@@ -1648,7 +1654,7 @@ static int ipapwd_getkeytab(Slapi_PBlock *pb, struct ipapwd_krbcfg *krbcfg)
|
||||
slapi_pblock_get(pb, SLAPI_EXT_OP_REQ_VALUE, &extop_value);
|
||||
if (!extop_value) {
|
||||
LOG_FATAL("Failed to retrieve extended op value from pblock\n");
|
||||
err_msg = "Failed to retrieve extended operation value\n";
|
||||
err_msg = "Failed to retrieve extended operation value";
|
||||
rc = LDAP_OPERATIONS_ERROR;
|
||||
goto free_and_return;
|
||||
}
|
||||
@@ -1674,7 +1680,7 @@ static int ipapwd_getkeytab(Slapi_PBlock *pb, struct ipapwd_krbcfg *krbcfg)
|
||||
/* get Entry by krbPrincipalName */
|
||||
target_entry = get_entry_by_principal(service_name);
|
||||
if (!target_entry) {
|
||||
err_msg = "PrincipalName not found.\n";
|
||||
err_msg = "PrincipalName not found.";
|
||||
rc = LDAP_NO_SUCH_OBJECT;
|
||||
goto free_and_return;
|
||||
}
|
||||
@@ -1690,7 +1696,7 @@ static int ipapwd_getkeytab(Slapi_PBlock *pb, struct ipapwd_krbcfg *krbcfg)
|
||||
if (!acl_ok) {
|
||||
LOG_FATAL("Not allowed to retrieve keytab on [%s] as user [%s]!\n",
|
||||
service_name, bind_dn);
|
||||
err_msg = "Insufficient access rights\n";
|
||||
err_msg = "Insufficient access rights";
|
||||
rc = LDAP_INSUFFICIENT_ACCESS;
|
||||
goto free_and_return;
|
||||
}
|
||||
@@ -1701,6 +1707,8 @@ static int ipapwd_getkeytab(Slapi_PBlock *pb, struct ipapwd_krbcfg *krbcfg)
|
||||
/* if password was passed-in, check its length */
|
||||
rc = ipapwd_check_max_pwd_len(strlen(password), &err_msg);
|
||||
if (rc) {
|
||||
LOG_PWDPOLICY("Failed to set password credentials for '%s': %s\n",
|
||||
bind_dn, err_msg);
|
||||
goto free_and_return;
|
||||
}
|
||||
}
|
||||
@@ -1712,7 +1720,7 @@ static int ipapwd_getkeytab(Slapi_PBlock *pb, struct ipapwd_krbcfg *krbcfg)
|
||||
if (!acl_ok) {
|
||||
LOG_FATAL("Not allowed to set keytab on [%s]!\n",
|
||||
service_name);
|
||||
err_msg = "Insufficient access rights\n";
|
||||
err_msg = "Insufficient access rights";
|
||||
rc = LDAP_INSUFFICIENT_ACCESS;
|
||||
goto free_and_return;
|
||||
}
|
||||
@@ -1745,7 +1753,7 @@ static int ipapwd_getkeytab(Slapi_PBlock *pb, struct ipapwd_krbcfg *krbcfg)
|
||||
if (!svals) {
|
||||
rc = LDAP_OPERATIONS_ERROR;
|
||||
LOG_FATAL("encrypt_encode_keys failed!\n");
|
||||
err_msg = "Internal error while encrypting keys\n";
|
||||
err_msg = "Internal error while encrypting keys";
|
||||
goto free_and_return;
|
||||
}
|
||||
|
||||
@@ -1765,7 +1773,7 @@ static int ipapwd_getkeytab(Slapi_PBlock *pb, struct ipapwd_krbcfg *krbcfg)
|
||||
rc = encode_getkeytab_reply(krbctx, krbcfg->kmkey, mkvno,
|
||||
keys, num_keys, &bvp);
|
||||
if (rc != LDAP_SUCCESS) {
|
||||
err_msg = "Internal Error.\n";
|
||||
err_msg = "Internal Error.";
|
||||
goto free_and_return;
|
||||
}
|
||||
|
||||
@@ -1776,7 +1784,7 @@ static int ipapwd_getkeytab(Slapi_PBlock *pb, struct ipapwd_krbcfg *krbcfg)
|
||||
|
||||
free_and_return:
|
||||
if (rc == LDAP_SUCCESS) err_msg = NULL;
|
||||
LOG("%s", err_msg ? err_msg : "success");
|
||||
LOG("%s\n", err_msg ? err_msg : "success");
|
||||
slapi_send_ldap_result(pb, rc, NULL, err_msg, 0, NULL);
|
||||
|
||||
/* Free anything that we allocated above */
|
||||
@@ -1787,7 +1795,7 @@ free_and_return:
|
||||
if (target_entry) slapi_entry_free(target_entry);
|
||||
if (keys) ipa_krb5_free_key_data(keys, num_keys);
|
||||
if (svals) {
|
||||
for (int i = 0; svals[i]; i++) {
|
||||
for (size_t i = 0; svals[i]; i++) {
|
||||
slapi_value_free(&svals[i]);
|
||||
}
|
||||
free(svals);
|
||||
@@ -2031,7 +2039,7 @@ int ipapwd_init( Slapi_PBlock *pb )
|
||||
"ipapwd_post_init_betxn", ipapwd_post_init_betxn,
|
||||
"IPA pwd post ops betxn", NULL,
|
||||
ipapwd_plugin_id);
|
||||
}
|
||||
}
|
||||
|
||||
slapi_register_plugin("preoperation", 1,
|
||||
"ipapwd_pre_init", ipapwd_pre_init,
|
||||
|
||||
@@ -70,6 +70,7 @@
|
||||
|
||||
#define IPAPWD_CHECK_CONN_SECURE 0x00000001
|
||||
#define IPAPWD_CHECK_DN 0x00000002
|
||||
#define IPAPWD_CHECK_ONLY_CONFIG 0x00000004
|
||||
|
||||
#define IPA_CHANGETYPE_NORMAL 0
|
||||
#define IPA_CHANGETYPE_ADMIN 1
|
||||
@@ -109,6 +110,7 @@ struct ipapwd_krbcfg {
|
||||
char **passsync_mgrs;
|
||||
int num_passsync_mgrs;
|
||||
bool allow_nt_hash;
|
||||
bool enforce_ldap_otp;
|
||||
};
|
||||
|
||||
int ipapwd_entry_checks(Slapi_PBlock *pb, struct slapi_entry *e,
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
* Authors:
|
||||
* Simo Sorce <ssorce@redhat.com>
|
||||
*
|
||||
* Copyright (C) 2007-2010 Red Hat, Inc.
|
||||
* Copyright (C) 2007-2023 Red Hat, Inc.
|
||||
* All rights reserved.
|
||||
* END COPYRIGHT BLOCK **/
|
||||
|
||||
@@ -248,6 +248,13 @@ static int ipapwd_pre_add(Slapi_PBlock *pb)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Get target DN */
|
||||
ret = slapi_pblock_get(pb, SLAPI_TARGET_SDN, &sdn);
|
||||
if (ret) {
|
||||
rc = LDAP_OPERATIONS_ERROR;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Ok this is interesting,
|
||||
* Check this is a clear text password, or refuse operation */
|
||||
if ('{' == userpw[0]) {
|
||||
@@ -280,6 +287,8 @@ static int ipapwd_pre_add(Slapi_PBlock *pb)
|
||||
} else {
|
||||
rc = ipapwd_check_max_pwd_len(strlen(userpw_clear), &errMesg);
|
||||
if (rc) {
|
||||
LOG_PWDPOLICY("Failed to set password credentials for '%s': %s\n",
|
||||
slapi_sdn_get_dn(sdn), errMesg);
|
||||
goto done;
|
||||
}
|
||||
userpw = slapi_ch_strdup(userpw_clear);
|
||||
@@ -329,13 +338,6 @@ static int ipapwd_pre_add(Slapi_PBlock *pb)
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Get target DN */
|
||||
ret = slapi_pblock_get(pb, SLAPI_TARGET_SDN, &sdn);
|
||||
if (ret) {
|
||||
rc = LDAP_OPERATIONS_ERROR;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* time to get the operation handler */
|
||||
ret = slapi_pblock_get(pb, SLAPI_OPERATION, &op);
|
||||
if (ret != 0) {
|
||||
@@ -359,7 +361,6 @@ static int ipapwd_pre_add(Slapi_PBlock *pb)
|
||||
pwdop->pwdata.changetype = IPA_CHANGETYPE_DSMGR;
|
||||
} else {
|
||||
char *binddn;
|
||||
int i;
|
||||
|
||||
pwdop->pwdata.changetype = IPA_CHANGETYPE_ADMIN;
|
||||
|
||||
@@ -367,7 +368,7 @@ static int ipapwd_pre_add(Slapi_PBlock *pb)
|
||||
slapi_pblock_get(pb, SLAPI_CONN_DN, &binddn);
|
||||
|
||||
/* if it is a passsync manager we also need to skip resets */
|
||||
for (i = 0; i < krbcfg->num_passsync_mgrs; i++) {
|
||||
for (size_t i = 0; i < krbcfg->num_passsync_mgrs; i++) {
|
||||
if (strcasecmp(krbcfg->passsync_mgrs[i], binddn) == 0) {
|
||||
pwdop->pwdata.changetype = IPA_CHANGETYPE_DSMGR;
|
||||
break;
|
||||
@@ -385,6 +386,8 @@ static int ipapwd_pre_add(Slapi_PBlock *pb)
|
||||
if ((pwdop->pwdata.changetype != IPA_CHANGETYPE_DSMGR) &&
|
||||
(ret != 0) ) {
|
||||
errMesg = ipapwd_error2string(ret);
|
||||
LOG_PWDPOLICY("Failed to add password credentials for '%s': %s\n",
|
||||
slapi_sdn_get_dn(sdn), errMesg);
|
||||
rc = LDAP_CONSTRAINT_VIOLATION;
|
||||
goto done;
|
||||
}
|
||||
@@ -507,6 +510,13 @@ static int ipapwd_pre_mod(Slapi_PBlock *pb)
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Get target DN */
|
||||
ret = slapi_pblock_get(pb, SLAPI_TARGET_SDN, &sdn);
|
||||
if (ret) {
|
||||
rc = LDAP_OPERATIONS_ERROR;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* grab the mods - we'll put them back later with
|
||||
* our modifications appended
|
||||
*/
|
||||
@@ -568,6 +578,8 @@ static int ipapwd_pre_mod(Slapi_PBlock *pb)
|
||||
|
||||
rc = ipapwd_check_max_pwd_len(bv->bv_len, &errMesg);
|
||||
if (rc) {
|
||||
LOG_PWDPOLICY("Failed to set password credentials for '%s': %s\n",
|
||||
slapi_sdn_get_dn(sdn), errMesg);
|
||||
goto done;
|
||||
}
|
||||
slapi_ch_free_string(&unhashedpw);
|
||||
@@ -591,14 +603,6 @@ static int ipapwd_pre_mod(Slapi_PBlock *pb)
|
||||
|
||||
/* OK we have something interesting here, start checking for
|
||||
* pre-requisites */
|
||||
|
||||
/* Get target DN */
|
||||
ret = slapi_pblock_get(pb, SLAPI_TARGET_SDN, &sdn);
|
||||
if (ret) {
|
||||
rc = LDAP_OPERATIONS_ERROR;
|
||||
goto done;
|
||||
}
|
||||
|
||||
tmp_sdn = slapi_sdn_dup(sdn);
|
||||
if (tmp_sdn) {
|
||||
/* xxxPAR: Ideally SLAPI_MODIFY_EXISTING_ENTRY should be
|
||||
@@ -795,6 +799,8 @@ static int ipapwd_pre_mod(Slapi_PBlock *pb)
|
||||
const char *userpw_clear = &userpw[strlen("{CLEAR}")];
|
||||
rc = ipapwd_check_max_pwd_len(strlen(userpw_clear), &errMesg);
|
||||
if (rc) {
|
||||
LOG_PWDPOLICY("Failed to set password credentials for '%s': %s\n",
|
||||
slapi_sdn_get_dn(sdn), errMesg);
|
||||
goto done;
|
||||
}
|
||||
unhashedpw = slapi_ch_strdup(userpw_clear);
|
||||
@@ -806,9 +812,8 @@ static int ipapwd_pre_mod(Slapi_PBlock *pb)
|
||||
slapi_ch_free_string(&userpw);
|
||||
|
||||
} else if (slapi_is_encoded(userpw)) {
|
||||
|
||||
LOG("Pre-Encoded passwords are not valid\n");
|
||||
errMesg = "Pre-Encoded passwords are not valid\n";
|
||||
errMesg = "Pre-Encoded passwords are not valid";
|
||||
LOG("%s (%s)\n", errMesg, slapi_sdn_get_dn(sdn));
|
||||
rc = LDAP_CONSTRAINT_VIOLATION;
|
||||
goto done;
|
||||
}
|
||||
@@ -843,7 +848,6 @@ static int ipapwd_pre_mod(Slapi_PBlock *pb)
|
||||
} else {
|
||||
char *binddn;
|
||||
Slapi_DN *bdn, *tdn;
|
||||
int i;
|
||||
|
||||
/* Check Bind DN */
|
||||
slapi_pblock_get(pb, SLAPI_CONN_DN, &binddn);
|
||||
@@ -857,18 +861,16 @@ static int ipapwd_pre_mod(Slapi_PBlock *pb)
|
||||
pwdop->pwdata.changetype = IPA_CHANGETYPE_ADMIN;
|
||||
|
||||
/* if it is a passsync manager we also need to skip resets */
|
||||
for (i = 0; i < krbcfg->num_passsync_mgrs; i++) {
|
||||
for (size_t i = 0; i < krbcfg->num_passsync_mgrs; i++) {
|
||||
if (strcasecmp(krbcfg->passsync_mgrs[i], binddn) == 0) {
|
||||
pwdop->pwdata.changetype = IPA_CHANGETYPE_DSMGR;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
slapi_sdn_free(&bdn);
|
||||
slapi_sdn_free(&tdn);
|
||||
|
||||
}
|
||||
|
||||
pwdop->pwdata.dn = slapi_ch_strdup(slapi_sdn_get_dn(sdn));
|
||||
@@ -884,6 +886,8 @@ static int ipapwd_pre_mod(Slapi_PBlock *pb)
|
||||
if ((pwdop->pwdata.changetype != IPA_CHANGETYPE_DSMGR) &&
|
||||
(ret != 0)) {
|
||||
errMesg = ipapwd_error2string(ret);
|
||||
LOG_PWDPOLICY("Check Password Policy failed for (%s) - %s/n",
|
||||
pwdop->pwdata.dn, errMesg);
|
||||
rc = LDAP_CONSTRAINT_VIOLATION;
|
||||
goto done;
|
||||
}
|
||||
@@ -976,7 +980,6 @@ static int ipapwd_regen_nthash(Slapi_PBlock *pb, Slapi_Mods *smods,
|
||||
int num_keys;
|
||||
int mkvno;
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
ret = slapi_entry_attr_find(entry, "ipaNTHash", &attr);
|
||||
if (ret == 0) {
|
||||
@@ -1008,7 +1011,7 @@ static int ipapwd_regen_nthash(Slapi_PBlock *pb, Slapi_Mods *smods,
|
||||
|
||||
ret = LDAP_UNWILLING_TO_PERFORM;
|
||||
|
||||
for (i = 0; i < num_keys; i++) {
|
||||
for (size_t i = 0; i < num_keys; i++) {
|
||||
char nthash[16];
|
||||
krb5_enc_data cipher;
|
||||
krb5_data plain;
|
||||
@@ -1209,13 +1212,20 @@ done:
|
||||
* value at the end. This leaves only the password in creds for later
|
||||
* validation.
|
||||
*/
|
||||
typedef enum {
|
||||
OTP_IS_NOT_REQUIRED = 0,
|
||||
OTP_IS_REQUIRED_EXPLICITLY,
|
||||
OTP_IS_REQUIRED_IMPLICITLY
|
||||
} otp_req_enum;
|
||||
static bool ipapwd_pre_bind_otp(const char *bind_dn, Slapi_Entry *entry,
|
||||
struct berval *creds, bool otpreq)
|
||||
struct berval *creds, otp_req_enum otpreq,
|
||||
bool *notokens)
|
||||
{
|
||||
uint32_t auth_types;
|
||||
|
||||
/* Get the configured authentication types. */
|
||||
auth_types = otp_config_auth_types(otp_config, entry);
|
||||
*notokens = false;
|
||||
|
||||
/*
|
||||
* IMPORTANT SECTION!
|
||||
@@ -1245,7 +1255,11 @@ static bool ipapwd_pre_bind_otp(const char *bind_dn, Slapi_Entry *entry,
|
||||
/* With no tokens, succeed if tokens aren't required. */
|
||||
if (tokens[0] == NULL) {
|
||||
otp_token_free_array(tokens);
|
||||
return !otpreq;
|
||||
*notokens = true;
|
||||
if (otpreq != OTP_IS_NOT_REQUIRED)
|
||||
/* DENY: OTP is required, either explicitly or implicitly */
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (otp_token_validate_berval(tokens, creds, NULL)) {
|
||||
@@ -1256,7 +1270,8 @@ static bool ipapwd_pre_bind_otp(const char *bind_dn, Slapi_Entry *entry,
|
||||
otp_token_free_array(tokens);
|
||||
}
|
||||
|
||||
return (auth_types & OTP_CONFIG_AUTH_TYPE_PASSWORD) && !otpreq;
|
||||
return (auth_types & OTP_CONFIG_AUTH_TYPE_PASSWORD) &&
|
||||
(otpreq == OTP_IS_NOT_REQUIRED);
|
||||
}
|
||||
|
||||
static int ipapwd_authenticate(const char *dn, Slapi_Entry *entry,
|
||||
@@ -1411,6 +1426,11 @@ done:
|
||||
}
|
||||
|
||||
|
||||
#ifdef USE_OP_NOTE_MFA_AUTH
|
||||
/* defined in ldap/servers/slapd/pblock.c in 389-ds but not exposed via slapi-plugin.h */
|
||||
extern void slapi_pblock_set_flag_operation_notes(Slapi_PBlock *pb, uint32_t opflag);
|
||||
#endif
|
||||
|
||||
/* PRE BIND Operation
|
||||
*
|
||||
* Used for:
|
||||
@@ -1428,6 +1448,7 @@ static int ipapwd_pre_bind(Slapi_PBlock *pb)
|
||||
"krbPasswordExpiration", "krblastpwchange",
|
||||
NULL
|
||||
};
|
||||
struct ipapwd_krbcfg *krbcfg = NULL;
|
||||
struct berval *credentials = NULL;
|
||||
Slapi_Entry *entry = NULL;
|
||||
Slapi_DN *target_sdn = NULL;
|
||||
@@ -1443,6 +1464,7 @@ static int ipapwd_pre_bind(Slapi_PBlock *pb)
|
||||
struct tm expire_tm;
|
||||
int rc = LDAP_INVALID_CREDENTIALS;
|
||||
char *errMesg = NULL;
|
||||
bool notokens = false;
|
||||
|
||||
/* get BIND parameters */
|
||||
ret |= slapi_pblock_get(pb, SLAPI_BIND_TARGET_SDN, &target_sdn);
|
||||
@@ -1501,9 +1523,28 @@ static int ipapwd_pre_bind(Slapi_PBlock *pb)
|
||||
|
||||
/* Try to do OTP first. */
|
||||
syncreq = otpctrl_present(pb, OTP_SYNC_REQUEST_OID);
|
||||
otpreq = otpctrl_present(pb, OTP_REQUIRED_OID);
|
||||
if (!syncreq && !ipapwd_pre_bind_otp(dn, entry, credentials, otpreq))
|
||||
goto invalid_creds;
|
||||
otpreq = otpctrl_present(pb, OTP_REQUIRED_OID) ?
|
||||
OTP_IS_REQUIRED_EXPLICITLY : OTP_IS_NOT_REQUIRED;
|
||||
if (!syncreq && (otpreq == OTP_IS_NOT_REQUIRED)) {
|
||||
ret = ipapwd_gen_checks(pb, &errMesg, &krbcfg, IPAPWD_CHECK_ONLY_CONFIG);
|
||||
if (ret != 0) {
|
||||
LOG_FATAL("ipapwd_gen_checks failed!?\n");
|
||||
slapi_entry_free(entry);
|
||||
slapi_sdn_free(&sdn);
|
||||
return 0;
|
||||
}
|
||||
if (krbcfg->enforce_ldap_otp) {
|
||||
otpreq = OTP_IS_REQUIRED_IMPLICITLY;
|
||||
}
|
||||
}
|
||||
if (!syncreq && !ipapwd_pre_bind_otp(dn, entry,
|
||||
credentials, otpreq, ¬okens)) {
|
||||
/* We got here because ipapwd_pre_bind_otp() returned false,
|
||||
* it means that either token verification failed or
|
||||
* a rule for empty tokens failed current policy. */
|
||||
if (!(notokens || (otpreq == OTP_IS_NOT_REQUIRED)))
|
||||
goto invalid_creds;
|
||||
}
|
||||
|
||||
/* Ensure that there is a password. */
|
||||
if (credentials->bv_len == 0) {
|
||||
@@ -1511,6 +1552,8 @@ static int ipapwd_pre_bind(Slapi_PBlock *pb)
|
||||
} else {
|
||||
rc = ipapwd_check_max_pwd_len(credentials->bv_len, &errMesg);
|
||||
if (rc) {
|
||||
LOG_PWDPOLICY("Failed to set password credentials for '%s': %s\n",
|
||||
slapi_sdn_get_dn(sdn), errMesg);
|
||||
goto invalid_creds;
|
||||
}
|
||||
}
|
||||
@@ -1533,11 +1576,23 @@ static int ipapwd_pre_bind(Slapi_PBlock *pb)
|
||||
/* Attempt to write out kerberos keys for the user. */
|
||||
ipapwd_write_krb_keys(pb, discard_const(dn), entry, credentials);
|
||||
|
||||
#ifdef USE_OP_NOTE_MFA_AUTH
|
||||
/* If it was a successful authentication with OTP required, mark it
|
||||
* for access log to notice multi-factor authentication has happened
|
||||
* https://www.port389.org/docs/389ds/design/mfa-operation-note-design.html
|
||||
*/
|
||||
if (!syncreq &&
|
||||
((otpreq != OTP_IS_NOT_REQUIRED) && !notokens)) {
|
||||
slapi_pblock_set_flag_operation_notes(pb, SLAPI_OP_NOTE_MFA_AUTH);
|
||||
}
|
||||
#endif
|
||||
|
||||
slapi_entry_free(entry);
|
||||
slapi_sdn_free(&sdn);
|
||||
return 0;
|
||||
|
||||
invalid_creds:
|
||||
free_ipapwd_krbcfg(&krbcfg);
|
||||
slapi_entry_free(entry);
|
||||
slapi_sdn_free(&sdn);
|
||||
slapi_send_ldap_result(pb, rc, NULL, errMesg, 0, NULL);
|
||||
|
||||
@@ -45,6 +45,8 @@
|
||||
#define UID_NUMBER "uidnumber"
|
||||
#define GID_NUMBER "gidnumber"
|
||||
#define IPA_SID "ipantsecurityidentifier"
|
||||
#define IPA_UNIQUEID "ipauniqueid"
|
||||
#define IPA_UNIQUEID_AUTOGENERATE "autogenerate"
|
||||
#define DOM_ATTRS_FILTER OBJECTCLASS"=ipantdomainattrs"
|
||||
#define DOMAIN_ID_RANGE_FILTER OBJECTCLASS"=ipadomainidrange"
|
||||
#define POSIX_ACCOUNT "posixaccount"
|
||||
@@ -104,3 +106,6 @@ int find_sid_for_ldap_entry(struct slapi_entry *entry,
|
||||
const char *base_dn,
|
||||
const char *dom_sid,
|
||||
struct range_info **ranges);
|
||||
|
||||
int sidgen_task_init(Slapi_PBlock *pb);
|
||||
int ipa_sidgen_init(Slapi_PBlock *pb);
|
||||
@@ -454,6 +454,7 @@ int find_sid_for_ldap_entry(struct slapi_entry *entry,
|
||||
uint32_t id;
|
||||
char *sid = NULL;
|
||||
char **objectclasses = NULL;
|
||||
char *uniqueid = NULL;
|
||||
Slapi_PBlock *mod_pb = NULL;
|
||||
Slapi_Mods *smods = NULL;
|
||||
int result;
|
||||
@@ -479,8 +480,18 @@ int find_sid_for_ldap_entry(struct slapi_entry *entry,
|
||||
goto done;
|
||||
}
|
||||
|
||||
uniqueid = slapi_entry_attr_get_charptr(entry, IPA_UNIQUEID);
|
||||
if (uniqueid != NULL &&
|
||||
strncmp(IPA_UNIQUEID_AUTOGENERATE, uniqueid,
|
||||
sizeof(IPA_UNIQUEID_AUTOGENERATE)) == 0) {
|
||||
LOG("Staged entry [%s] does not have Posix IDs, nothing to do.\n",
|
||||
dn_str);
|
||||
ret = 0;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (uid_number >= UINT32_MAX || gid_number >= UINT32_MAX) {
|
||||
LOG_FATAL("ID value too large.\n");
|
||||
LOG_FATAL("ID value too large on entry [%s].\n", dn_str);
|
||||
ret = LDAP_CONSTRAINT_VIOLATION;
|
||||
goto done;
|
||||
}
|
||||
@@ -497,7 +508,7 @@ int find_sid_for_ldap_entry(struct slapi_entry *entry,
|
||||
&has_posix_group,
|
||||
&has_ipa_id_object);
|
||||
if (ret != 0) {
|
||||
LOG_FATAL("Cannot determine objectclasses.\n");
|
||||
LOG_FATAL("Cannot determine objectclasses on entry [%s].\n", dn_str);
|
||||
goto done;
|
||||
}
|
||||
|
||||
@@ -511,15 +522,16 @@ int find_sid_for_ldap_entry(struct slapi_entry *entry,
|
||||
id = (uid_number != 0) ? uid_number : gid_number;
|
||||
objectclass_to_add = NULL;
|
||||
} else {
|
||||
LOG_FATAL("Inconsistent objectclasses and attributes, nothing to do.\n");
|
||||
LOG_FATAL("Inconsistent objectclasses and attributes on entry "
|
||||
"[%s], nothing to do.\n", dn_str);
|
||||
ret = 0;
|
||||
goto done;
|
||||
}
|
||||
|
||||
ret = find_sid_for_id(id, plugin_id, base_dn, dom_sid, ranges, &sid);
|
||||
if (ret != 0) {
|
||||
LOG_FATAL("Cannot convert Posix ID [%lu] into an unused SID.\n",
|
||||
(unsigned long) id);
|
||||
LOG_FATAL("Cannot convert Posix ID [%lu] into an unused SID on "
|
||||
"entry [%s].\n", (unsigned long) id, dn_str);
|
||||
goto done;
|
||||
}
|
||||
|
||||
@@ -554,6 +566,7 @@ int find_sid_for_ldap_entry(struct slapi_entry *entry,
|
||||
}
|
||||
|
||||
done:
|
||||
slapi_ch_free_string(&uniqueid);
|
||||
slapi_ch_free_string(&sid);
|
||||
slapi_pblock_destroy(mod_pb);
|
||||
slapi_mods_free(&smods);
|
||||
|
||||
@@ -89,7 +89,7 @@ static void free_pblock(void *arg)
|
||||
static int do_work(struct worker_ctx *worker_ctx)
|
||||
{
|
||||
Slapi_PBlock *pb;
|
||||
int ret;
|
||||
int ret, failures = 0;
|
||||
size_t c;
|
||||
char *filter = NULL;
|
||||
char *attrs[] = { OBJECTCLASS, UID_NUMBER, GID_NUMBER, NULL };
|
||||
@@ -151,8 +151,7 @@ static int do_work(struct worker_ctx *worker_ctx)
|
||||
worker_ctx->base_dn, worker_ctx->dom_sid,
|
||||
worker_ctx->ranges);
|
||||
if (ret != 0) {
|
||||
LOG_FATAL("Cannot add SID to existing entry.\n");
|
||||
goto done;
|
||||
failures++;
|
||||
}
|
||||
|
||||
if (worker_ctx->delay != 0) {
|
||||
@@ -162,6 +161,12 @@ static int do_work(struct worker_ctx *worker_ctx)
|
||||
}
|
||||
};
|
||||
|
||||
ret = failures;
|
||||
if (ret > 0) {
|
||||
LOG_FATAL("Finished with %d failures, please check the log.\n",
|
||||
failures);
|
||||
}
|
||||
|
||||
done:
|
||||
slapi_ch_free_string(&filter);
|
||||
pthread_cleanup_pop(1);
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
* Program may make changes or additions to the list of Approved
|
||||
* Interfaces.
|
||||
*
|
||||
* Copyright (C) 2010 Red Hat, Inc.
|
||||
* Copyright (C) 2010-2023 Red Hat, Inc.
|
||||
* All rights reserved.
|
||||
* END COPYRIGHT BLOCK **/
|
||||
|
||||
@@ -1185,7 +1185,7 @@ static int ipauuid_pre_op(Slapi_PBlock *pb, int modtype)
|
||||
* enforce is enabled. */
|
||||
errstr = slapi_ch_smprintf("Only the Directory Manager "
|
||||
"can set arbitrary values "
|
||||
"for %s\n", cfgentry->attr);
|
||||
"for %s", cfgentry->attr);
|
||||
ret = LDAP_INSUFFICIENT_ACCESS;
|
||||
goto done;
|
||||
}
|
||||
@@ -1221,7 +1221,7 @@ done:
|
||||
}
|
||||
|
||||
if (ret) {
|
||||
LOG("operation failure [%d]\n", ret);
|
||||
LOG("operation failure [%d] - %s\n", ret, errstr);
|
||||
slapi_send_ldap_result(pb, ret, NULL, errstr, 0, NULL);
|
||||
slapi_ch_free((void **)&errstr);
|
||||
ret = EFAIL;
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
# from the environment for the first two.
|
||||
PYTHON ?= python3
|
||||
VENVDIR = ./.venv
|
||||
SPHINXOPTS ?= -W --keep-going -j auto
|
||||
SPHINXOPTS ?= -W --keep-going -j 1
|
||||
SPHINXBUILD ?= PATH=$(VENVDIR)/bin:$$PATH sphinx-build
|
||||
SOURCEDIR = .
|
||||
BUILDDIR = _build
|
||||
|
||||
@@ -63,7 +63,7 @@ else:
|
||||
This will connect to LDAP directly if we are running our script in server, or
|
||||
use a RPC client if we are running it from a FreeIPA client.
|
||||
|
||||
After we have initialized the API and stablished a connection, we are ready to
|
||||
After we have initialized the API and established a connection, we are ready to
|
||||
issue commands.
|
||||
|
||||
## Running commands
|
||||
@@ -278,4 +278,4 @@ for i in range(100):
|
||||
'params' : [args, kw]
|
||||
})
|
||||
ret = api.Command.batch(*batch_args)
|
||||
```
|
||||
```
|
||||
@@ -8,6 +8,7 @@ Make multiple ipa calls via one remote procedure call
|
||||
|methods|:ref:`Dict<Dict>`|False
|
||||
|
||||
### Options
|
||||
* keeponly : :ref:`Str<Str>`
|
||||
* version : :ref:`Str<Str>`
|
||||
|
||||
### Output
|
||||
|
||||
@@ -27,7 +27,7 @@ No arguments.
|
||||
* ipauserobjectclasses : :ref:`Str<Str>`
|
||||
* ipapwdexpadvnotify : :ref:`Int<Int>`
|
||||
* ipaconfigstring : :ref:`StrEnum<StrEnum>`
|
||||
* Values: ('AllowNThash', 'KDC:Disable Last Success', 'KDC:Disable Lockout', 'KDC:Disable Default Preauth for SPNs')
|
||||
* Values: ('AllowNThash', 'KDC:Disable Last Success', 'KDC:Disable Lockout', 'KDC:Disable Default Preauth for SPNs', 'EnforceLDAPOTP')
|
||||
* ipaselinuxusermaporder : :ref:`Str<Str>`
|
||||
* ipaselinuxusermapdefault : :ref:`Str<Str>`
|
||||
* ipakrbauthzdata : :ref:`StrEnum<StrEnum>`
|
||||
|
||||
@@ -0,0 +1,262 @@
|
||||
# Audit IPA API operations
|
||||
|
||||
## Overview
|
||||
|
||||
IPA servers present an API to perform various actions that change the state of
|
||||
the deployment. These actions include, among others, modifications of user and
|
||||
group databases, add or remove information about hosts and Kerberos services,
|
||||
HBAC and SUDO rules, and many other action types.
|
||||
|
||||
It is possible to observe IPA API actions through the web server logs. Each IPA
|
||||
server logs their API calls in `/var/log/httpd/error_log` log file, as part of
|
||||
standard Apache webserver logs. However, IPA command line utilities, when run
|
||||
as `root` on IPA server, operate directly against LDAP database and results of
|
||||
these operations only present in LDAP logs.
|
||||
|
||||
Thus, current IPA API implementation does not provide a unified method to
|
||||
collect logs to audit API operations. The goal of this change is to make sure
|
||||
IPA API backend logs externally-initiated operations, regardless how these
|
||||
operations were invoked.
|
||||
|
||||
## Use Cases
|
||||
|
||||
- As an administrator, I'd like to collect details on who and when has called
|
||||
a particular IPA API on a particular IPA server. This information needs to be
|
||||
easily queried and it should be possible to correlate it across all servers
|
||||
in the IPA deployment.
|
||||
|
||||
- As an administrator, I'd like to make sure operations performed as part of
|
||||
IPA administrative utilities are audited as well.
|
||||
|
||||
## How to Use
|
||||
|
||||
`journalctl` tool can be used to query and filter through the IPA
|
||||
API audit details. Each logged entry will be tagged with an 'IPA.API' label and
|
||||
thus can easily be queried:
|
||||
|
||||
```
|
||||
# journalctl -g IPA.API
|
||||
...
|
||||
```
|
||||
|
||||
`journalctl -g` allows to search through the content of the log entries'
|
||||
messages. Each found entry has associated metadata which can be retrieved in a
|
||||
different format, depending on other options to `journalctl` invocation.
|
||||
|
||||
## Design
|
||||
|
||||
Audit of IPA API operations should happen independently. Operations should be
|
||||
logged whenever they happen.
|
||||
|
||||
IPA deployment heavily relies on a working systemd setup. systemd provides
|
||||
logging facilities in the form of a system journal. systemd journal allows
|
||||
centralized collection of the journals from individual systems, forward secure
|
||||
sealing of the forwarded data, and rich metadata associated with the log
|
||||
entries. Thus, it already provides an infrastructure to allow secure
|
||||
centralized collection of actions performed through IPA API on IPA servers.
|
||||
|
||||
From administrator's point of view, when IPA API operations logged through the
|
||||
systemd journal, standard journal commands can be used to retrieve and
|
||||
manipulate logged entries.
|
||||
|
||||
## Implementation
|
||||
|
||||
All IPA API calls end up in `Command.__do_call()` internal method. This method
|
||||
prepares execution of the command and runs it. After the command was performed,
|
||||
the output is formatted. If operations require forwarding the request to a
|
||||
remote IPA server, this will be performed automatically. As a result,
|
||||
`Command.__do_call()` is executed by both IPA client and IPA server components.
|
||||
It is possible to derive a context of operations through IPA API environment,
|
||||
using `api.env.in_server` boolean.
|
||||
|
||||
systemd journal provides a simple Python binding, `systemd.journal`, that
|
||||
allows structured logging of the messages against a `journald` daemon running
|
||||
on the system. If such operation is performed in the server context, system
|
||||
journal will be updated.
|
||||
|
||||
To aid with identification of these messages, an application name is replaced
|
||||
with IPA.API and the actual name from api.env.script is made a part of the
|
||||
logged message. The actual application script name is available as part of the
|
||||
journal metadata anyway. Additionally, a `MESSAGE_ID` property is set to IPA
|
||||
API-specific application UID, generated with Python's
|
||||
`uuid.uuid3(uuid.NAMESPACE_DNS, 'IPA.API')` function call. This value is
|
||||
available in IPA constants (`ipalib/constants.py`) as `SD_IPA_API_MESSAGE_ID`.
|
||||
The value of the constant is `6d70f1b493df36478bc3499257cd3b17`.
|
||||
|
||||
If no Kerberos authentication was used but rather LDAPI autobind was in use,
|
||||
the name of the authenticated principal will be replaced with `[autobind]`
|
||||
text.
|
||||
|
||||
Messages sent with syslog `NOTICE` priority.
|
||||
|
||||
An example session looks like the following output:
|
||||
|
||||
```
|
||||
# ipa -e in_server=True console
|
||||
(Custom IPA interactive Python console)
|
||||
api: IPA API object
|
||||
pp: pretty printer
|
||||
>>> api.Command.user_del('foobar')
|
||||
{'result': {'failed': []}, 'value': ['foobar'], 'messages': [{'type':
|
||||
'warning', 'name': 'VersionMissing', 'message': "API Version number was not
|
||||
sent, forward compatibility not guaranteed. Assuming server's API version,
|
||||
2.253", 'code': 13001, 'data': {'server_version': '2.253'}}], 'summary':
|
||||
'Deleted user "foobar"'}
|
||||
>>> ^D
|
||||
now exiting InteractiveConsole...
|
||||
|
||||
# journalctl -g IPA.API
|
||||
May 21 11:31:33 master1.ipa1.test /usr/bin/ipa[247422]: [IPA.API] [autobind]: user_del: SUCCESS [ldap2_140328582446688] {"uid": ["foobar"], "continue": false, "version": "2.253"}
|
||||
```
|
||||
|
||||
All operations triggered through IPA API logged, including locally initiated,
|
||||
as can be seen in the output above. For httpd end-point operations will be
|
||||
logged as requested by the `/mod_wsgi` binary:
|
||||
|
||||
```
|
||||
May 21 11:35:19 master1.ipa1.test /mod_wsgi[247035]: [IPA.API] admin@IPA1.TEST: ping: SUCCESS [ldap2_139910420944784] {"version": "2.253"}
|
||||
```
|
||||
|
||||
The message includes following fields:
|
||||
- executable name and PID (`/mod_wsgi` for HTTP end-point)
|
||||
- `[IPA.API]` marker to allow searches with `journalctl -g IPA.API`
|
||||
- authenticated Kerberos principal or `[autobind]` marker for LDAPI-based access as root
|
||||
- name of the command executed
|
||||
- result of execution: `SUCCESS` or an exception name
|
||||
- LDAP backend instance identifier. The identifier will be the same for all operations performed under the same request. This allows to identify operations which were executed as a part of the same API request instance. For API operations that didn't result in LDAP access, there will be `[no_connection_id]` marker.
|
||||
- finally, a list of arguments and options passed to the command is provided in JSON format
|
||||
|
||||
If an API call results in multiple operations triggered by the internal
|
||||
implementation of the API command, only the external operation is recorded.
|
||||
This means, for example, that a `user_del` API call will only be recorded as a
|
||||
`user_del` command and not a sequence of a `user_find`, `otptoken_find`,
|
||||
`subid_find`, and corresponding deletion commands which `user_del`
|
||||
implementation is using.
|
||||
|
||||
IPA supplies a message catalog to systemd journal which allows to explain
|
||||
content of the audited message and provide references to corresponding IPA API
|
||||
documentation. This feature is triggered by `journalctl -x` systemd journal
|
||||
command.
|
||||
|
||||
Full journal entry looks like the one below and can be obtained with `journalctl -o json-pretty` command:
|
||||
|
||||
```
|
||||
{
|
||||
"PRIORITY" : "5",
|
||||
"_HOSTNAME" : "master1.ipa1.test",
|
||||
"__SEQNUM" : "608971",
|
||||
"_COMM" : "ipa",
|
||||
"_AUDIT_LOGINUID" : "0",
|
||||
"CODE_FUNC" : "__audit_to_journal",
|
||||
"_TRANSPORT" : "journal",
|
||||
"__SEQNUM_ID" : "aa96317d3ab84c16b5f131922414af11",
|
||||
"_CAP_EFFECTIVE" : "1ffffffffff",
|
||||
"_MACHINE_ID" : "5582ad1e90354a2e82710afb4cd4477f",
|
||||
"_RUNTIME_SCOPE" : "system",
|
||||
"MESSAGE" : "[IPA.API] [autobind]: user_del: SUCCESS [ldap2_140155643874720] {\"uid\": [\"zuser3\"], \"continue\": false, \"version\": \"2.253\"}",
|
||||
"CODE_LINE" : "495",
|
||||
"__REALTIME_TIMESTAMP" : "1716360895014405",
|
||||
"__MONOTONIC_TIMESTAMP" : "5952405665424",
|
||||
"_SYSTEMD_OWNER_UID" : "0",
|
||||
"_SYSTEMD_UNIT" : "session-30.scope",
|
||||
"_SYSTEMD_CGROUP" : "/user.slice/user-0.slice/session-30.scope",
|
||||
"CODE_FILE" : "/usr/lib/python3.12/site-packages/ipalib/frontend.py",
|
||||
"_SYSTEMD_SESSION" : "30",
|
||||
"_SYSTEMD_INVOCATION_ID" : "d166c864dba04b478d01658aa180d50d",
|
||||
"_PID" : "255232",
|
||||
"IPA_API_PARAMS" : "{\"uid\": [\"zuser3\"], \"continue\": false, \"version\": \"2.253\"}",
|
||||
"MESSAGE_ID" : "6d70f1b493df36478bc3499257cd3b17",
|
||||
"IPA_API_ACTOR" : "[autobind]",
|
||||
"_SYSTEMD_SLICE" : "user-0.slice",
|
||||
"IPA_API_COMMAND" : "user_del",
|
||||
"_BOOT_ID" : "cce41ab07f404ced8676400eb01bf220",
|
||||
"__CURSOR" : "s=aa96317d3ab84c16b5f131922414af11;i=94acb;b=cce41ab07f404ced8676400eb01bf220;m=569e7067690;t=6190569742605;x=265dd38af30f934c",
|
||||
"_AUDIT_SESSION" : "30",
|
||||
"SYSLOG_IDENTIFIER" : "/usr/bin/ipa",
|
||||
"_UID" : "0",
|
||||
"_SYSTEMD_USER_SLICE" : "-.slice",
|
||||
"_SOURCE_REALTIME_TIMESTAMP" : "1716360895014364",
|
||||
"_SELINUX_CONTEXT" : "unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023",
|
||||
"_CMDLINE" : "/usr/bin/python3 -I /usr/bin/ipa -e in_server=True console",
|
||||
"_EXE" : "/usr/bin/python3.12",
|
||||
"_GID" : "0",
|
||||
"IPA_API_RESULT" : "SUCCESS"
|
||||
}
|
||||
```
|
||||
|
||||
An explanation for this audit message can be generated with `journalctl -x` command:
|
||||
|
||||
```
|
||||
# journalctl -x -g ldap2_140155643874720
|
||||
May 22 06:54:55 master1.ipa1.test /usr/bin/ipa[255232]: [🡕] [IPA.API] [autobind]: user_del: SUCCESS [ldap2_140155643874720] {"uid": ["zuser3"], "continue": false, "version": "2.253"}
|
||||
░░ Subject: IPA API command was executed and result of its execution was audited
|
||||
░░ Defined-by: FreeIPA
|
||||
░░ Support: https://lists.fedorahosted.org/archives/list/freeipa-users@lists.fedorahosted.org/
|
||||
░░ Documentation: man:ipa(1)
|
||||
░░ Documentation: https://freeipa.readthedocs.io/en/latest/api/index.html
|
||||
░░ Documentation: https://freeipa.readthedocs.io/en/latest/api/user_del.html
|
||||
|
||||
░░ FreeIPA provides an extensive API that allows to manage all aspects of IPA deployments.
|
||||
|
||||
░░ The following information about the API command executed is available:
|
||||
|
||||
░░ [IPA.API] [autobind]: user_del: SUCCESS [ldap2_140155643874720] {"uid": ["zuser3"], "continue": false, "version": "2.253"}
|
||||
|
||||
░░ The command was executed by '/usr/bin/ipa' utility. If the utility name
|
||||
░░ is '/mod_wsgi`, then this API command came from a remote source through the IPA
|
||||
░░ API end-point.
|
||||
|
||||
░░ The message includes following fields:
|
||||
|
||||
░░ - executable name and PID ('/mod_wsgi' for HTTP end-point; in this case it
|
||||
░░ was '/usr/bin/ipa' command)
|
||||
|
||||
░░ - '[IPA.API]' marker to allow searches with 'journalctl -g IPA.API'
|
||||
|
||||
░░ - authenticated Kerberos principal or '[autobind]' marker for LDAPI-based
|
||||
░░ access as root. In this case it was '[autobind]'
|
||||
|
||||
░░ - name of the command executed, in this case 'user_del'
|
||||
|
||||
░░ - result of execution: `SUCCESS` or an exception name. In this case it was
|
||||
░░ 'SUCCESS'
|
||||
|
||||
░░ - LDAP backend instance identifier. The identifier will be the same for all
|
||||
░░ operations performed under the same request. This allows to identify operations
|
||||
░░ which were executed as a part of the same API request instance. For API
|
||||
░░ operations that didn't result in LDAP access, there will be
|
||||
░░ '[no_connection_id]' marker.
|
||||
|
||||
░░ - finally, a list of arguments and options passed to the command is provided
|
||||
░░ in JSON format.
|
||||
|
||||
░░ ---------
|
||||
░░ The following list of arguments and options were passed to the command
|
||||
░░ 'user_del' by the '[autobind]' actor:
|
||||
░░
|
||||
░░ {"uid": ["zuser3"], "continue": false, "version": "2.253"}
|
||||
░░ ---------
|
||||
|
||||
░░ A detailed information about FreeIPA API can be found at upstream documentation API reference:
|
||||
░░ https://freeipa.readthedocs.io/en/latest/api/index.html
|
||||
|
||||
░░ For details on the IPA API command 'user_del' see
|
||||
░░ https://freeipa.readthedocs.io/en/latest/api/user_del.html
|
||||
```
|
||||
|
||||
## Feature Management
|
||||
|
||||
There is no separate management of the IPA API audit logging. Logging is always
|
||||
active on IPA server.
|
||||
|
||||
systemd journal has own mechanisms to control rates of messages coming from the
|
||||
services. The details can be found in the man page `journald.conf(5)`.
|
||||
|
||||
## Upgrade
|
||||
|
||||
There is no impact on upgrade. Once new IPA API code installed, any new
|
||||
application using it will start issuing log entries to the journald.
|
||||
|
||||
## Test plan
|
||||
|
||||
Test of IPA API audit logging can be done by observing systemd journal.
|
||||
+21
-3
@@ -43,6 +43,20 @@ There are a few basic rules:
|
||||
|
||||
### Installation
|
||||
|
||||
|
||||
#### SELinux
|
||||
|
||||
The two supported hardware HSMs require additional SELinux permissions
|
||||
so that IPA and certmonger have access to the tokens. There is a
|
||||
separate module for each one: {free}ipa-selinux-nfast and
|
||||
{free}ipa-selinux-luna. These are NOT installed by default and
|
||||
the user must install the appropriate one manually.
|
||||
|
||||
During HSM validation early in the installation a check is made to
|
||||
ensure that the correct module is installed but this is a best
|
||||
effort and will not cause the installation to fail if the module
|
||||
is not available.
|
||||
|
||||
#### CA
|
||||
|
||||
The token name, module name and shared library must be provided to the
|
||||
@@ -57,6 +71,10 @@ are generated and stored in the HSM.
|
||||
| --token-name | NSS name for the token |
|
||||
| --library-path | Path to PKCS#11 shared library |
|
||||
| --token-password | Password for the token |
|
||||
| --token-password-file | File containing the token password |
|
||||
|
||||
If neither --token-password nor --token-password-file are provided
|
||||
then the password will be obtained interactively.
|
||||
|
||||
This information will be stored in new schema so that replicas can auto-detect when an HSM is configured.
|
||||
|
||||
@@ -64,7 +82,7 @@ ipa-ca-install will accept the same options.
|
||||
|
||||
```
|
||||
attributeTypes: (
|
||||
2.16.840.1.113730.3.8.21.1.TBD
|
||||
2.16.840.1.113730.3.8.21.1.10
|
||||
NAME 'ipaCaHSMConfiguration'
|
||||
DESC 'HSM Configuration'
|
||||
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
|
||||
@@ -78,11 +96,11 @@ This attribute will be semi-colon delimited and contain the HSM information need
|
||||
|
||||
token-name;library-path
|
||||
|
||||
The token password will be prompted by ipa-replica-install or passed on the cli.
|
||||
On a replica installation the token password will be prompted by ipa-replica-install or passed using the cli options.
|
||||
|
||||
The presence of this attribute is enough to indicate that an HSM is present in the installation and the options will automatically be used for additional servers and/or services. The password will not be stored and the user must provide them on the cli. Whenever a replica, replica CA, KRA or replica KRA is added this attribute will be examined to determine whether an HSM is available or not, and what the options are.
|
||||
|
||||
A user can override library-path on the command-line in case it is in a different location or architecture. A different token name would mean a different token and they cannot be mixed.
|
||||
A user can override library-path on the command-line in case it is in a different location or architecture. A different token name would mean a different token and they cannot be mixed. If not provided on the command-line then the stored value will be used.
|
||||
|
||||
The NSS module name will be the basepath of the library minus .so*.
|
||||
|
||||
|
||||
@@ -0,0 +1,411 @@
|
||||
# Identity Mapping
|
||||
|
||||
In FreeIPA deployments user and group objects get their POSIX identities (IDs)
|
||||
assigned and managed in an automated way. This document describes how
|
||||
identity mapping is performed and enforced in FreeIPA deployments.
|
||||
|
||||
## Overview
|
||||
|
||||
FreeIPA deployment has three major identity mapping elements:
|
||||
- automated identifier assignment through LDAP
|
||||
- automated identifier assignment through SSSD
|
||||
- identity information consistency enforcement in LDAP and Kerberos KDC
|
||||
|
||||
POSIX identities are allocated to objects that need to be visible at run-time
|
||||
in POSIX environment. These are typically POSIX users and groups but there are
|
||||
some exceptions to these objects, to accomodate specialized use cases.
|
||||
|
||||
In POSIX environment user and group identities are properties of several
|
||||
different objects. When login session is initiated, the login process is
|
||||
started as root (ID 0) and during login flow at some point a switch to a
|
||||
certain user ID is performed. User's primary group is associated with a process
|
||||
and information about other groups a user is a member of is populated as
|
||||
'secondary groups' of the process. During lifetime of the process these lists
|
||||
are typically left intact and cannot be modified unless a process possesses
|
||||
certain administrative capabilities.
|
||||
|
||||
Both user and group IDs in POSIX environment are represented by unsigned
|
||||
integers. In Linux environment user and group ID spaces are separate and fit
|
||||
into 32-bit unsigned integer since 2001. Due to historical reasons, some of
|
||||
file formats might not support more than 16- or 15-bit unsigned integer spaces
|
||||
for UID and GID values. FreeIPA environment assumes operations in a
|
||||
contemporary environment, with 32-bit user and group IDs.
|
||||
|
||||
[Linux Standard Base](https://en.wikipedia.org/wiki/Linux_Standard_Base) Core
|
||||
Specification defines certain reserved ID ranges. Some Linux distributions or
|
||||
projects go beyond that and define own ID ranges. To avoid conflicts with these
|
||||
ranges, FreeIPA defines its own default ID ranges above 16-bit space.
|
||||
|
||||
For interoperability purposes a mapping between POSIX and non-POSIX identities
|
||||
has to be established. The only supported non-POSIX identities for which there
|
||||
exists a well defined mapping mechanism are identities represented with the
|
||||
help of Security IDentifiers (SIDs) from Active Directory.
|
||||
|
||||
## Identity ranges
|
||||
|
||||
Each POSIX identity issued by the FreeIPA server is generated within a certain
|
||||
identity range. ID ranges serve several purposes: they help multiple
|
||||
FreeIPA components to coordinate identity allocation and convey details of the
|
||||
purpose of the allocated identity ranges.
|
||||
|
||||
ID ranges are stored in LDAP, under `cn=ranges,cn=etc,$BASEDN` subtree. LDAP
|
||||
objects for ID ranges are based on the `ipaidrange` objectclass. Depending on
|
||||
the type of the range, other objectclasses might be added to express additional
|
||||
attributes.
|
||||
|
||||
As of FreeIPA 4.11, there are following ID range categories in FreeIPA:
|
||||
|
||||
- local FreeIPA POSIX ID range (`ipa-local` ID range type)
|
||||
- POSIX ID range for local subordinate identities (`ipa-ad-trust`)
|
||||
- trusted domain ID range using automated allocation based on SID of an object (`ipa-ad-trust`)
|
||||
- trusted domain ID range using explicit allocation for POSIX identities (`ipa-ad-trust-posix`)
|
||||
|
||||
These four categories expressed using three ID range types for technical reasons.
|
||||
|
||||
More ID ranges can be added through the lifetime of the deployment to
|
||||
accomodate administrative needs. The main requirement for these additional
|
||||
ranges is to not overlap with existing ranges. Range consistency is controlled
|
||||
with the help of a specialized plugin to 389-ds LDAP server, `ipa-range-check`.
|
||||
When a new range conflicts with existing configuration, its acceptance will be
|
||||
refused and an LDAP error `LDAP_CONSTRAINT_VIOLATION` will be returned. This
|
||||
ensures all ranges are in consistent state.
|
||||
|
||||
Trusted domain ID ranges have information about the domain SID of the
|
||||
associated domain stored directly in the range LDAP object. For the local
|
||||
domain ID ranges this information is stored elsewhere because only a single
|
||||
organizational domain (as opposed to DNS domains) can be present in IPA
|
||||
deployment.
|
||||
|
||||
Information about SID and other parameters of the local domain is stored in the
|
||||
LDAP object `cn=<domain>,cn=ad,cn=etc,$SUFFIX`. This information is used for ID
|
||||
mapping enforcement purposes by SID generation plugin and Kerberos KDC driver.
|
||||
|
||||
Information about SID and other parameters of a trusted domain is stored in the
|
||||
LDAP object `cn=<domain>,cn=ad,cn=trusts,$SUFFIX`. It is used by the Kerberos
|
||||
KDC driver when processing Kerberos requests.
|
||||
|
||||
### Local FreeIPA POSIX ID range
|
||||
|
||||
Each FreeIPA deployment has its own primary (or local) POSIX ID range. The
|
||||
range is chosen during an initial server deployment and cannot be changed.
|
||||
Initial `admin` user and associated groups (e.g. `admins`) are all allocated
|
||||
from this range. The range size defaults to 200000 identities but both starting
|
||||
point and the size of the range can be modified with `ipa-server-install`
|
||||
options.
|
||||
|
||||
The primary ID range corresponds to the DNA range also created during the
|
||||
initial IPA server deployment. When replica is deployed, this ID range already
|
||||
exists in the topology and is visible to the replica. However, a corresponding
|
||||
slice of the DNA range will not be created on the replica until the DNA plugin
|
||||
on the replica is not asked to allocate an ID. See `Automated Identifier Assignment Through LDAP`_
|
||||
section for details how DNA ranges are used.
|
||||
|
||||
### POSIX ID range for local subordinate identities
|
||||
|
||||
Subordinate identities are a Linux Kernel feature to grant a user additional
|
||||
user and group ID ranges. Amongst others, the feature can be used by container
|
||||
runtime engines to implement rootless containers. Traditionally subordinate ID
|
||||
ranges are configured in `/etc/subuid` and `/etc/subgid`. More details about
|
||||
subordinate ranges can be found in ["Subordinate IDs"](subordinate-ids.html)
|
||||
design page.
|
||||
|
||||
Due to a technical use of a trusted domain type to represent ID range of the
|
||||
local subordinate identities, the ID range has a domain SID associated with it.
|
||||
The SID has a special structure: `S-1-5-21-738065-838566-$DOMAIN_HASH`.
|
||||
`S-1-5-21` is the well-known SID prefix for domain SIDs. `738065-838566`
|
||||
is the representation of the string `IPA-SUB`. `DOMAIN_HASH` is the
|
||||
[MURMUR-3](https://en.wikipedia.org/wiki/MurmurHash) hash of the domain name
|
||||
for key `0xdeadbeef`. SSSD rejects SIDs unless they are prefixed with
|
||||
`S-1-5-21`. This SID is never used for any SID generation.
|
||||
|
||||
### Trusted domain ID range using automated allocation based on SID of an object
|
||||
|
||||
Active Directory environment does not use itself any POSIX identities because
|
||||
they have no meaning within the context of Windows operating system. Each
|
||||
object in Active Directory has associated security identifier (SID). SIDs stay
|
||||
the same for the lifetime of the object and never re-used by other objects. SID
|
||||
can be used to map an object in Active Directory to POSIX environment.
|
||||
|
||||
There are several methods to map SIDs to POSIX IDs. FreeIPA relies on an
|
||||
algorithmic method provided by SSSD. The algorithm is described in
|
||||
[sssd-ad(5) manual page](https://manpages.org/sssd-ad/5) in the section
|
||||
"Mapping algorithm".
|
||||
|
||||
On start-up, SSSD looks up all ID ranges from the active IPA server and uses
|
||||
information about trusted domains to map between SIDs and POSIX IDs. This
|
||||
avoids static allocation of identities in LDAP. Instead, ID ranges in
|
||||
FreeIPA serve as fences, to prevent other allocators from using these ranges
|
||||
for static allocation.
|
||||
|
||||
For each trusted domain, a separate ID range is created since every trusted
|
||||
domain has unique domain SID. Each ID range will have own POSIX ID range
|
||||
allocation. These allocations can be created automatically during `ipa
|
||||
trust-add` operation or ID ranges can be pre-created in advance with `ipa
|
||||
idrange-add` operation. The latter is useful when there is a need to explicitly
|
||||
define specific POSIX ID range boundaries as `ipa trust-add` command only
|
||||
allows to define a new range's size.
|
||||
|
||||
|
||||
### Trusted domain ID range using explicit allocation for POSIX identities
|
||||
|
||||
In case there is already existing static mapping of POSIX identities in Active
|
||||
Directory, one can explicitly define ID range assocated with the trust to
|
||||
Active Directory to handle explicit allocation. In this case SSSD will not use
|
||||
algorithmic method but instead will look up identities from LDAP entry (e.g.
|
||||
`uidNumber` and `gidNumber` attributes).
|
||||
|
||||
In case of explicit allocation, ID range associated with the trusted forest
|
||||
Active Directory root domain must have ID range type of `ipa-ad-trust-posix`,
|
||||
as well as all other domains visible through the trust link.
|
||||
|
||||
## ID range allocation
|
||||
|
||||
During deployment FreeIPA takes a random ID range slice from `(1,10000)` and
|
||||
multiplies the base offset by 200000. This base becomes a starting point for
|
||||
the deployment's ID range.
|
||||
|
||||
Both starting point and size of the range can be overridden by an administrator
|
||||
who deploys the initial server. The minimal value for the initial ID range
|
||||
start cannot be less than `UID_MAX` or `GID_MAX` from `/etc/login.defs`. This
|
||||
range is then used by all systems enrolled into the domain managed by this
|
||||
initial server.
|
||||
|
||||
Thus, a default range that FreeIPA chooses the deployment ID range from is
|
||||
200000...2000000000. If this is a single domain and amount of users in it is
|
||||
not going to be above 200K, then FreeIPA administrators may never need to
|
||||
allocate another ID range within 200000...2000000000 and that original ID range
|
||||
stays fixed forever.
|
||||
|
||||
There are two other use cases:
|
||||
|
||||
- trusting another IPA or AD deployment, and
|
||||
|
||||
- migrating pre-existing deployment not based on IPA.
|
||||
|
||||
When there are multiple organizational domains around and they need to
|
||||
interoperate (establish trust between them), additional ID ranges get added on
|
||||
the consuming side to represent trusted domain(s). For a trusted domain that is
|
||||
an Active Directory, there would be two possibilities, managed within SSSD and
|
||||
IPA:
|
||||
|
||||
- an ID range is implicit, defined through a murmurhash3 hash of the domain
|
||||
SID,
|
||||
|
||||
- an ID range is explicit, defined by the admin as UID/GID values stored in AD
|
||||
LDAP user entries for each user. The explicitly defined ID ranges most
|
||||
likely are part of a legacy setup and most likely are within `UID_MAX` and
|
||||
`GID_MAX` values from `/etc/login.defs`. There are exceptions, of course.
|
||||
|
||||
An implicit ID range derivation by SSSD is described in `sssd-ad(5)`, section 'ID
|
||||
Mapping'. Samba has own way to derive similar ID ranges based on different
|
||||
properties of the domain SID, handled by individual `idmap` modules but
|
||||
conceptually it is similar: a rule is chosen to map those properties to POSIX
|
||||
IDs and a map is maintained, algorithmically or based on a stored mapping in
|
||||
Samba's own ID database.
|
||||
|
||||
With Active Directory environments all domain objects have their own unique
|
||||
identifier, in the form of a SID. RID value of the SID is never reused when
|
||||
objects get deleted and added. This means that for a company with an Active
|
||||
Directory domain of ~20-25 years and large churn of employees over that time it
|
||||
is not uncommon to see RIDs above 200K. For such deployments an ID range
|
||||
SSSD and IPA would need to synthesize on Linux side would have size larger than
|
||||
200K.
|
||||
|
||||
Thus ID range landscape is 'dynamic'. At a given time SSSD on the client will
|
||||
be able to see all the ID ranges defined within the domain they enrolled into.
|
||||
When a new trust is established and a new ID range for that trusted domain is
|
||||
added, SSSD on the client will see this new ID range and will be able to derive
|
||||
user/group IDs automatically for them. Most likely there are no existing files
|
||||
on the client with ownership for these IDs yet -- except networking file
|
||||
systems but for those to be usable at least one system should be able to create
|
||||
those files with those POSIX IDs on a compatible client.
|
||||
|
||||
Existing ranges never change, LDAP plugins provided by FreeIPA prevent
|
||||
modifying existing ranges in IPA or delete them if there are users/groups
|
||||
with IDs assigned from those ID ranges.
|
||||
|
||||
## Automated identifier assignment through LDAP
|
||||
|
||||
When algorithmic ID range is used, SSSD only maps existing users and groups
|
||||
coming from trusted Active Directory domains and those so based on their SID
|
||||
values. For all other range types allocation of POSIX IDs is done in advance,
|
||||
typically when user or group is created or moved from staged/preserved state.
|
||||
|
||||
This automated allocation is done with the help of [Distributed Numeric
|
||||
Assignment (DNA) plugin](https://access.redhat.com/documentation/en-us/red_hat_directory_server/11/html/administration_guide/dna)
|
||||
in 389-ds LDAP server. DNA plugin has its own identity ranges and provides a
|
||||
generic mechanism to assign identities to various objects across the whole LDAP
|
||||
topology. During installation of the original IPA server a DNA range is created
|
||||
to match the primary IPA ID range. When new replicas added to IPA topology,
|
||||
their DNA plugins become aware of the overal DNA range and may ask for a
|
||||
sub-set of it for own needs. This sub-allocation only happens when DNA plugin
|
||||
needs a range to allocate an ID. For example, when a user or a group is created
|
||||
on that replica.
|
||||
|
||||
Since DNA ranges only get splitted and not extended automatically, IPA ID
|
||||
ranges aren't tracking changes in DNA ranges. However, if somebody adds new DNA
|
||||
ranges outside of the original primary IPA ID range, then a corresponding IPA
|
||||
ID range needs to be created manually. This affects a lot of legacy deployments
|
||||
where no real control over use of POSIX IDs through static allocation was done.
|
||||
|
||||
Another automatic ID assignment happens for SIDs. For each object in LDAP that
|
||||
has POSIX attributes and `ipaNTUserAttrs` or `ipaNTGroupAttrs` object classes
|
||||
upon creation a SID is generated by the `ipa-sidgen` 389-ds plugin. The SID is
|
||||
stored in `ipaNTSecurityIdentifier` attribute. There is also another plugin,
|
||||
`ipa-sidgen-task`, which handles a task of generating of SIDs for existing
|
||||
objects. Both plugins issue error messages in the 389-ds error log in case they
|
||||
were unable to map POSIX ID to a SID.
|
||||
|
||||
## ID mapping adjustments using ID overrides
|
||||
|
||||
On top of automatically or algorithmically allocated POSIX IDs, administrators
|
||||
have possiblity to adjust POSIX IDs locally for users and groups coming from
|
||||
trusted Active Directory domains. The same mechanism can also be used for
|
||||
adjustments of POSIX IDs on individual IPA clients (or groups of IPA clients)
|
||||
for legacy compatibility. These mechanisms should not be used for IPA users and
|
||||
groups in general case because they might break certain assumptions in SSSD and
|
||||
Kerberos KDC driver.
|
||||
|
||||
For users and groups from trusted Active Directory domains, ID overrides in
|
||||
"Default Trust View" are used by SSSD on IPA server to override any ID
|
||||
retrieved from the trusted domain controler side or generated algorithmically.
|
||||
This mechanism allows to migrate pre-defined POSIX IDs from Active Directory by
|
||||
creating ID overrides for individual users and groups.
|
||||
|
||||
From SSSD point of view, these ID overrides represent `uidNumber` and
|
||||
`gidNumber` attributes as if they were specified in the original LDAP object
|
||||
entry.
|
||||
|
||||
IPA users and groups cannot have ID overrides in the "Default Trust View".
|
||||
Their POSIX IDs have to be present in their own LDAP object entries.
|
||||
|
||||
## Security IDentifiers
|
||||
|
||||
While primary goal of FreeIPA is to serve identities for POSIX environment, a
|
||||
large effort is put into making interoperability with other identity management
|
||||
systems, out of which Active Directory is the most important one. Active
|
||||
Directory is using a different method to control access to their resources than
|
||||
POSIX environment and this method extends from identity management to
|
||||
authorization mechanisms. It is also has visible effects on various protocols
|
||||
through which FreeIPA interoperability with Active Directory is achieved.
|
||||
|
||||
One of the corner stones of Active Directory security model is the [security
|
||||
identifier (SID)](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-dtyp/78eb9013-1c3a-4970-ad1f-2b1dad588a25)
|
||||
assignment to each object in Active Directory that can be used for security
|
||||
evaluation. Objects grouped by Active Directory domains, each domain having a
|
||||
domain SID and all objects within the same domain have their SIDs starting with
|
||||
the SID of the domain. The relative identifier (RID) is the unique identity of
|
||||
an object within the domain scope. A full object SID, thus, is a combination of
|
||||
the `<domain-SID>-<object RID>`.
|
||||
|
||||
Since objects are unique within the domain, RIDs can be reused in different
|
||||
domains. There are common, so-called ['well-known'
|
||||
identifiers](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-dtyp/81d92bba-d22b-4a8c-908a-554ab29148ab),
|
||||
which express the same functional objects in different domains. These
|
||||
well-known objects have the same RID values but still be distinct: Objects from
|
||||
`domain1.test` and `domain2.test` with the same RID values will be different.
|
||||
For example, domain administrator user `Administrator` has RID value 500.
|
||||
`Administrator` user from `domain1.test` would be different from
|
||||
`Administrator` user from `domain2.test`, even though their individual RIDs
|
||||
(500) would be the same. If `domain1.test` has SID `S-1-5-21-123-45-6789` and
|
||||
`domain2.test` has SID `S-1-5-21-54-321-6789` then `Administrator` user from
|
||||
`domain1.test` would have SID `S-1-5-21-123-45-6789-500` and `Administrator`
|
||||
user from `domain2.test` would have SID `S-1-5-21-54-321-6789-500`.
|
||||
|
||||
Internally Active Directory does only use user and group names to perform
|
||||
translation to a SID. Once SID of the object who authenticated the connection
|
||||
is established, names don't matter anymore. For each authenticated object
|
||||
within a specific connection a special security token is built which contains
|
||||
this object's membership information in form of SIDs of groups it belongs to.
|
||||
Access control lists attached to Active Directory objects contain SIDs as well,
|
||||
making access control evaluation straightforward: security token SID details
|
||||
evaluated against access control lists to grant or deny access.
|
||||
|
||||
This model is loosely similar to how processes in POSIX environment get
|
||||
evaluated when accessing resources. Each POSIX process has user identity it is
|
||||
running under, with a set of primary and secondary groups associated with the
|
||||
identity as part of the process description. The difference is that POSIX UID
|
||||
and GID values exist only on a single machine and a care to avoid conflicts
|
||||
between multiple systems should be taken. In Active Directory model the SIDs
|
||||
are already bearing domain-specific information, hence allowing to distinguish
|
||||
objects belonging to different systems. The latter means for both networking
|
||||
and local resources such as file systems a single access control list mechanism
|
||||
can be used to address both remote and local identities.
|
||||
|
||||
Since SIDs do not exist in POSIX environment, mapping of objects from trusted
|
||||
Active Directory domains to POSIX environment within IPA deployment is based on
|
||||
other properties. In particular, a fully-qualified user or group name is used
|
||||
to represent the user (or group): a user Administrator from `domain1.test`
|
||||
would have a fully-qualified name `Administrator@domain1.test`, different from
|
||||
the user Administrator of the domain `domain2.test` which would have a
|
||||
fully-qualified name of `Administrator@domain2.test`.
|
||||
|
||||
Above description uses so-called user principal name (UPN) notation to describe
|
||||
user names. In this notation a user name corresponds to a (case-insensitive)
|
||||
Kerberos principal name. Since each Active Directory domain is a Kerberos
|
||||
realm, this allows to establish mapping between identities on POSIX and Active
|
||||
Directory levels through Kerberos authorization mechanism.
|
||||
|
||||
Active Directory has extended Kerberos protocol by adding a privilege
|
||||
attributes certificate (PAC) information to the Kerberos ticket issued by
|
||||
Active Directory domain controllers. The PAC part contains the security token
|
||||
details of the authenticated object represented by the Kerberos principal.
|
||||
These details are protected by a set of security checksums that prevent
|
||||
external modifications to the PAC content. This information is detailed in the
|
||||
[MS-PAC] and [MS-KILE] specifications Microsoft has published and Open Source
|
||||
projects such as MIT Kerberos, Heimdal Kerberos, Samba, and FreeIPA have since
|
||||
implemented.
|
||||
|
||||
## Enforcement of ID mapping
|
||||
|
||||
For trusted domains using Kerberos protocol to establish trust, a duty to
|
||||
validate information falls onto Kerberos KDCs of each realm. FreeIPA KDCs
|
||||
re-assess content of the presented Kerberos tickets and validate PAC issued by
|
||||
a trusted domain's domain controller. This validation includes checks to make
|
||||
sure only SIDs from trusted domains can be present there and a trusted domain's
|
||||
KDC cannot inject SIDs that belong to IPA domain.
|
||||
|
||||
The latter is needed because both sides of the trust possess a key that
|
||||
represents a cross-realm service principal, `krbtgt/DOMAIN1.TEST@DOMAIN2.TEST`.
|
||||
This key allows KDC from `DOMAIN1.TEST` realm to issue a service ticket that a
|
||||
client from `DOMAIN1.TEST` can present to a KDC from `DOMAIN2.TEST` to request
|
||||
a service ticket for a service from `DOMAIN2.TEST`. If a KDC from
|
||||
`DOMAIN1.TEST` goes rogue and decides to inject SIDs of groups from
|
||||
`DOMAIN2.TEST` into a security token of the user from `DOMAIN1.TEST`, nothing
|
||||
can stop it. Thus, a KDC from `DOMAIN2.TEST` must validate that a token encoded
|
||||
in the PAC buffer of the Kerberos ticket coming from the `DOMAIN1.TEST` is
|
||||
'sane'.
|
||||
|
||||
Sanity checks performed by the KDC in FreeIPA include multiple steps. For each
|
||||
trusted domain FreeIPA records a list of SIDs that must be filtered out
|
||||
unconditionally. This list is based on the section ['4.1.2.2 SID Filtering and
|
||||
Claims Transformation'](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-pac/55fc19f2-55ba-4251-8a6a-103dd7c66280)
|
||||
of the [MS-PAC] specification. The list can be extended for each specific
|
||||
domain using `ipa trust-mod` command for both incoming
|
||||
(`--sid-blacklist-incoming`) and outgoing (`--sid-blacklist-outgoing`)
|
||||
directions.
|
||||
|
||||
Additionally, KDC verifies that a PAC issued by a trusted domain's KDC does not
|
||||
contain SIDs from IPA domain. In such case the ticket issuance will be rejected.
|
||||
|
||||
For each ticket issued, KDC adds information about the requestor's SID
|
||||
in a separate PAC buffer. During processing of the consequent requests KDC does
|
||||
validate that a requestor SID is the same as the SID of the identity of the
|
||||
security token in PAC buffer. This information is then cross-verified against
|
||||
the list of known trusted domains to avoid cases of impersonation exploited
|
||||
through [CVE-2020-25721].
|
||||
|
||||
As a consequence of these checks, FreeIPA Kerberos KDC only issues initial
|
||||
Kerberos tickets for principals which have SID assigned. The SID assignment in
|
||||
FreeIPA is tied to presence of an ID range that covers both `uidNumber` and
|
||||
`gidNumber` of the LDAP object representing the principal. If there is no such
|
||||
range can be found, SID will not be issued and a Kerberos principal associated
|
||||
with this LDAP object will be used for authentication. Thus, SID and POSIX
|
||||
attributes are tied together.
|
||||
|
||||
|
||||
[MS-KILE]: https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-kile/2a32282e-dd48-4ad9-a542-609804b02cc9
|
||||
[MS-PAC]: https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-pac/166d8064-c863-41e1-9c23-edaaa5f36962
|
||||
[CVE-2020-25721]: https://www.samba.org/samba/security/CVE-2020-25721.html
|
||||
|
||||
|
||||
@@ -32,3 +32,5 @@ FreeIPA design documentation
|
||||
passkeys.md
|
||||
prci_checker.md
|
||||
rbcd.md
|
||||
id-mapping.md
|
||||
audit-ipa-api.md
|
||||
+396
-313
@@ -1,12 +1,10 @@
|
||||
# IPA Migration
|
||||
|
||||
## Overview
|
||||
## Overview of the old plugin-based migration
|
||||
|
||||
IPA has had a plugin-based migration for remote LDAP servers since
|
||||
version 2.0.0. It will only migrate users and groups.
|
||||
IPA has had a plugin-based migration for remote LDAP servers since version 2.0.0. It will only migrate users and groups.
|
||||
|
||||
It has some powerful capabilities for working around eccentricities in
|
||||
the remote server including:
|
||||
It has some powerful capabilities for working around eccentricities in the remote server including:
|
||||
|
||||
* it is idempotent
|
||||
* support for both RFC2307 and 2307bis
|
||||
@@ -18,198 +16,272 @@ This is insufficient for the following reasons:
|
||||
|
||||
* It severely limits IPA-to-IPA migration as all other entry types are lost
|
||||
* User-private groups are not maintained
|
||||
* Syntax errors can cause migration to fail with the only resolution
|
||||
being to skip broken entries or fix the remote LDAP server
|
||||
* It is executed as a server-side plugin and if it runs long enough the
|
||||
client may disconnect
|
||||
* Syntax errors can cause migration to fail with the only resolution being to skip broken entries or fix the remote LDAP server
|
||||
* It is executed as a server-side plugin and if it runs long enough the client may disconnect
|
||||
* There is no feedback during the migration beyond watching the logs
|
||||
* There is no migration-specific log
|
||||
|
||||
The basic operation includes:
|
||||
|
||||
* loop through the remote user and group containers examining each entry
|
||||
* if it looks like a user or group (based on objectclass) try to
|
||||
migrate it
|
||||
* if it looks like a user or group (based on objectclass) try to migrate it
|
||||
* convert group membership from the detected or provided RFC
|
||||
* retain passwords if the provider LDAP bind user can read them
|
||||
* drop conflicting attributes (like kerberos)
|
||||
* skip duplicates so it can be rerun multiple times
|
||||
* convert groups to IPA POSIX groups
|
||||
|
||||
## Use Cases
|
||||
### Use cases for a new IPA to IPA migration tool
|
||||
|
||||
There are two use-cases driving a re-implementation (or extension) of
|
||||
migration:
|
||||
There are two use-cases driving a re-implementation (or extension) of migration:
|
||||
|
||||
1. Addressing bugs in current LDAP-only migration code
|
||||
|
||||
2. Adding IPA-to-IPA migration including all entry types.
|
||||
|
||||
## Migration basics
|
||||
## IPA to IPA migration
|
||||
|
||||
### General considerations
|
||||
### Terminology
|
||||
|
||||
LDAP stores information in a tree structure known as the Directory
|
||||
Information Tree (DIT). Each node in the tree is an entry and each
|
||||
entry contains information in an attribute=value form. It is a very
|
||||
different storage model than a relational database as there is no
|
||||
"join" capability between entries (among other things).
|
||||
In the following sections we will describe the IPA server that has the data we are pulling the migration data from as the **remote server**, and the **local server** is the new IPA server that will receive this data. So the **local server** is the new server and also the server where the migration tool will be run from. In other words the migration tool pulls from the remote server and applies it to the local server.
|
||||
|
||||
The organization of the entries depends on schema which defines which
|
||||
object types (objectclasses) and attributes (attributetypes) are
|
||||
allowed. https://ldap.com/ has a pretty decent introduction to LDAP,
|
||||
schemas, etc. The important thing to understand is that in order for
|
||||
an attribute to be in an entry, there must be a corresponding
|
||||
objectclass which allows it, either as a MUST or MAY. The schema defines
|
||||
a syntax for that attribute which defines what type of data may be
|
||||
stored in it. Some LDAP servers do a better job than others at enforcing
|
||||
this syntax.
|
||||
### Prerequisites
|
||||
|
||||
### pure LDAP migration
|
||||
You must install IPA on the new system (local server), and the domain/suffix must be the final expected values. The remote data will be converted to match the new local server. Typically it is expected that this installation be bare. The tool was not designed to merge two different installations (although it might work).
|
||||
|
||||
While it is easy to see a remote LDAP migration as a subset of IPA to
|
||||
IPA it is better to keep them as separate for the following reasons:
|
||||
### IPA to IPA migration design
|
||||
|
||||
1. It represents a small subset of a typical IPA installation: only
|
||||
users and groups.
|
||||
2. The remote schema and DIT are unknown and different for each migration so it will require more care.
|
||||
3. The quality of the data is unknown. 389-ds, used by IPA, enforces proper syntax where other servers may not.
|
||||
- IPA-to-IPA migration will be implemented as an *AdminTool* standalone client tool: **/usr/sbin/ipa-migrate**
|
||||
- Migration will consist of three areas:
|
||||
- Schema - the LDAP schema (objectclasses and attributes)
|
||||
- Config - the LDAP configuration under **cn=config** (dse.ldif)
|
||||
- Database - the main LDAP database
|
||||
- Allow online (LDAP over the network) or offline (LDIF file) migration. You can mix and match
|
||||
LDIF (offline) with LDAP (online)
|
||||
|
||||
### IPA to IPA migration
|
||||
### Online migration
|
||||
|
||||
For an IPA to IPA migration it is possible to make certain assumptions
|
||||
that aren't possible in a pure LDAP migration including:
|
||||
Online migration consists of contacting the remote server over the network and pulling in all the required information. With very large databases this could impact the tool's performance
|
||||
|
||||
1. The DIT is understood and consistent
|
||||
2. Unique UID/GID/login/group name(s) so additional checking is not necessary
|
||||
3. A consistent schema (perhaps variability for users and groups)
|
||||
### Offline migration
|
||||
|
||||
## Design
|
||||
Offline migration consists of using LDIF files from the remote server
|
||||
- Config - the DS config file: **/etc/dirsrv/slapd-YOUR_LDAP_INSTANCE/dse.ldif**
|
||||
- Schema - all the schema files found under **/etc/dirsrv/schema/** and **/etc/dirsrv/slapd-YOUR_LDAP_INSTANCE/schema/**
|
||||
- Database - You need to export the **userroot** database to an ldif file
|
||||
|
||||
### IPA to IPA migration
|
||||
Then copy these LDIF files to the new **local server**
|
||||
|
||||
It would be risky to hardcode the data to pull in. It is far simpler to
|
||||
iterate the things you *don't* want to migrate. This should also improve
|
||||
the chances for future-proofing and preventing bugs like not migrating
|
||||
some new feature area. It will require that versions only migrate up or
|
||||
sideways and not backwards.
|
||||
### Mixing online and offline methods
|
||||
|
||||
IPA-to-IPA migration will be implemented as an AdminTool standalone
|
||||
client.
|
||||
You are allowed to mix and match both approaches. The most advantageous reason to mix and match these approaches would be if you have a very large database. You can do the config and schema migration online and then use a database LDIF from the remote server for the database migration. This will perform faster than reading and comparing thousands of entries over the network. It will also be more stable. You won't have to worry about network issues breaking the process mid-migration.
|
||||
|
||||
#### Prerequisites
|
||||
### Dry-run migrations
|
||||
|
||||
A migration should only be done to a single IPA server. This will greatly
|
||||
simplify certain things, particularly performance. We may want an option
|
||||
to allow this and deny it by default.
|
||||
You can also do a dry-run of the migration to see what would be migrated to the new local server. In addition to a dry-run you can also record to an LDIF file what LDAP operation would be performed during the migration. This LDIF file can be inspected to see fine grained details about what the migration would do, "replayed" to perform the migration at later date, or to reuse a common deployment on multiple IPA servers.
|
||||
|
||||
If DNS is enabled on the remote server it will need to be enabled in
|
||||
the local one in order to migrate DNS data. It would need to be determined
|
||||
if the DNS DIT exists in a migrated server would allow bind to be
|
||||
configured (I suspect it will) but we don't want to get into a situation
|
||||
where the migrated DNS data is unusable.
|
||||
### Migration modes
|
||||
|
||||
#### Retain REALM/domain
|
||||
As of right now there are two migration modes, but in the future there could be more. Migration modes allow for easier use of the tool. the mode will define what is migrated and what is not.
|
||||
|
||||
It will be optional to retain the same REALM and domain. This will make the
|
||||
use-cases of staging and development server migration possible.
|
||||
#### Production mode
|
||||
|
||||
#### Retain ranges
|
||||
In production mode basically everything is brought over. It is assumed that the *remote server* is/was fully functional and the database and entry states are valid. This means things like DNA ranges, IDs, and SIDs (ipantsecurityidentifier) will be migrated as is.
|
||||
|
||||
This is TBD. I don't know of a reason why a user wouldn't want to retain the
|
||||
ranges as it would affect owned files, etc, but *someone* may want to make a
|
||||
clean break. I don't believe it would be difficult to deal with as we would
|
||||
just set the magic values for DNA generation.
|
||||
#### Staging mode
|
||||
|
||||
In order to retain the range then the remote and local ranges would need to
|
||||
be compared and the migration rejected if they do not overlap (perhaps allowed
|
||||
with --force).
|
||||
In this mode it is assumed that the remote server was in a staging environment and that things like the DNA ranges and ID attributes (uidNumber, gidNumber, etc) should **not** be migrated as is. The DNA entry attributes will be reset to the *magic regen* value.
|
||||
|
||||
As for DNA, if the range remains some effort will need to be made to set the
|
||||
local DNS configuration to match what is available remotely.
|
||||
### Migration content
|
||||
|
||||
This may not be ideal. The basic process would be:
|
||||
This section will describe how various entries & attributes will be migrated to the local server.
|
||||
|
||||
* connect to all servers in the remote IPA installation
|
||||
* collect the DNA and on-deck ranges
|
||||
* determine the starting point
|
||||
* set the local DNA range to this starting point + the range end - 1
|
||||
#### REALM/Domain
|
||||
|
||||
This could well leave huge holes of allocated values within the range but the
|
||||
DNA plugin should be able to handle that.
|
||||
For realm/domain/suffixes all the remote data will be converted to match the the new local server. This will apply to all the entries and subtrees. This is an automatic process and can not be adjusted or disabled. This is why it's important when you install the new local server that you set the realm/domain/database suffix to the expected production values.
|
||||
|
||||
subids TBD. It may be fine to just straight migrate the range and records.
|
||||
#### ID ranges
|
||||
|
||||
#### Discovery of IPA objects
|
||||
The migration mode (production or staging) will determine if the ID ranges are migrated or not. You can still skip the ID range migration in production mode using a CLI option and the tool will reset the DNA attributes (uidNumber, gidNumber, etc) to the magic regeneration value.
|
||||
|
||||
The objects to migrate can be determined by examining the local API.
|
||||
It can be iterated to discover the available class objects which contain
|
||||
the container_dn and other useful information. Not all objects need
|
||||
to be migrated either because they are internal objects, represent
|
||||
schema or represent commands that have no underlying storage.
|
||||
Initialize the API and iterate over Object:
|
||||
#### Skipped attributes
|
||||
|
||||
api.bootstrap(in_server=True, context='migrate')
|
||||
api.finalize()
|
||||
When adding new entries to the local server the following attributes are skipped ...
|
||||
|
||||
for obj in api.Object:
|
||||
work()
|
||||
Operational attributes
|
||||
- modifiersname
|
||||
- modifytimestamp
|
||||
- creatorsname
|
||||
- createtimestamp
|
||||
- nsuniqueid
|
||||
- dsentrydn
|
||||
- entryuuid
|
||||
|
||||
This gives us an alphabetized list of objects. An implicit relationship
|
||||
can be determined by examining the member* values in `obj.attribute_members`
|
||||
if it is present.
|
||||
Standard attributes
|
||||
- krbextradata
|
||||
- krblastfailedauth
|
||||
- krblastpwdchange
|
||||
- krbloginfailedcount
|
||||
- krbticketflags
|
||||
- krbmkey
|
||||
- ipasshpubkey --> We do keep the public key for users
|
||||
- mepmanagedentry
|
||||
- memberof
|
||||
- krbprincipalkey
|
||||
- memberofindirect
|
||||
- memberindirect
|
||||
- memberofindirect
|
||||
- memberindirect
|
||||
- userCertificate --> if issued by the remote IPA server
|
||||
|
||||
Using this list we know which are "leaf" entries which have no dependencies.
|
||||
These are migrated first. Then the list is iterated again looking for
|
||||
those with dependencies that are already satisfied and those are migrated.
|
||||
And so on.
|
||||
#### Ignored attributes
|
||||
|
||||
So for example users and hosts are migrated. Then groups, which has a dependency on
|
||||
users. Then hostgroups can then be migrated.
|
||||
When comparing entries (not when adding entries) the following attributes are ignored and the value that exists on the local server will remain intact
|
||||
|
||||
Some objects, like hostgroups, can be nested. These will need to be deferred until
|
||||
all of their members are added. If a running list of objects that have been migrated
|
||||
is maintained then it can be easily calculated which are ready for migration and
|
||||
which are not without having to refer to LDAP.
|
||||
- description
|
||||
- ipasshpubkey
|
||||
- ipantsecurityidentifier --> except in production mode
|
||||
- ipantflatname
|
||||
- ipamigrationenabled
|
||||
- ipauniqueid
|
||||
- serverhostname
|
||||
- krbpasswordexpiration
|
||||
- krblastadminunlock
|
||||
|
||||
#### Objects to ignore
|
||||
#### DNS records
|
||||
|
||||
The IPA API object list includes an number of internal-only objects and
|
||||
some classes that represent informational commands so they can be skipped
|
||||
for migration.
|
||||
By default all DNS entries are migrated, but you can skip the DNS records via a CLI option.
|
||||
|
||||
| Object | Reason |
|
||||
| --- | ----- |
|
||||
| certreq | no storage |
|
||||
| class | schema |
|
||||
| command | internal |
|
||||
| cosentry | migration not needed |
|
||||
| dns_system_records | no storage |
|
||||
| metaobject | schema |
|
||||
| param | internal |
|
||||
| pkinit | no storage |
|
||||
| topic | internal |
|
||||
| userstatus | no storage |
|
||||
#### Limited migration
|
||||
|
||||
#### Objects TBD
|
||||
If there are cases where you don't want to migrate the configuration or schema there are CLI options to skip each of these areas.
|
||||
|
||||
Each DNS record type is represented as a separate class. It is probably safe
|
||||
to just bulk import all dns records as there isn't any IPA-specific information in them.
|
||||
#### Non-IPA content
|
||||
|
||||
So basically add them to the ignore list.
|
||||
Some Administrators store non-IPA content in the database tree. In order for this content to be migrated it must be specified via a CLI option.
|
||||
|
||||
#### Other Containers
|
||||
### Configuration migration design
|
||||
|
||||
Not all data in IPA can be manipulated with the API.
|
||||
The following sections of the Directory Server configuration will be migrated
|
||||
|
||||
cn=certificates,cn=ipa,cn=etc,$SUFFIX contains any user-provided certificates.
|
||||
I think we can skip this and require that any necessary certificates be
|
||||
re-added. Otherwise, to be safe, we should validatate the trust and expiration
|
||||
dates on all of them.
|
||||
#### Core configuration (cn=config)
|
||||
|
||||
DNA ranges. Depending on the local ranges we could try to recover them.
|
||||
The core configuration attributes will be migrated to the core server. Things like performance tuning, security settings, and log rotation settings.
|
||||
|
||||
#### Items to NOT be migrated
|
||||
#### Database settings
|
||||
|
||||
The various look through limits, ID scan limits, import cache size, backend indexes, and encrypted attributes are migrated.
|
||||
|
||||
#### Plugins
|
||||
|
||||
The following plugins are migrated
|
||||
|
||||
- Attribute Uniqueness plugins
|
||||
- DNA Plugins
|
||||
- MemberOf plugin
|
||||
- Referential integrity plugin
|
||||
- Retro Changelog plugin
|
||||
- SASL Mapping plugins
|
||||
- IPA DNS plugin
|
||||
- IPA Enrollment plugin
|
||||
- IPA Extdom plugin
|
||||
- IPA Graceperiod plugin
|
||||
- IPA Lockout plugin
|
||||
- IPA Password Policy plugin
|
||||
- IPA Topology plugin
|
||||
- IPA Unique ID plugins
|
||||
- IPA Winsync plugin
|
||||
- Schema Compatibility plugin
|
||||
- Slapi NIS plugin
|
||||
|
||||
### Schema migration design
|
||||
|
||||
By default any *missing* objectclasses and attributes are migrated. There is also a CLI option to completely overwrite the existing schema on the local server with the remote server's schema.
|
||||
|
||||
### Database migration design
|
||||
|
||||
The following subtrees and entries are migrated. Any missing entries will be added, and any existing entries will be compared and any differences will be merged into the new local server
|
||||
|
||||
#### Plugin entries
|
||||
|
||||
- Automember Definitions (subtree of ```cn=automember,cn=etc,$SUFFIX```)
|
||||
- DNA Ranges (subtree of ```cn=ranges,cn=etc,$SUFFIX```)
|
||||
- DNA Posix IDs (```cn=automember,cn=etc,$SUFFIX```)
|
||||
- DNA SubIDs (```cn=subordinate-ids,cn=dna,cn=ipa,cn=etc,$SUFFIX```)
|
||||
- MEP Templates (subtree of ```cn=templates,cn=managed entries,cn=etc,$SUFFIX```)
|
||||
- MEP Definitions (subtree of ```cn=definitions,cn=managed entries,cn=etc,$SUFFIX```)
|
||||
|
||||
#### Etc entries
|
||||
|
||||
- Anonymous Limits (```cn=anonymous-limits,cn=etc,$SUFFIX```)
|
||||
- CA (```cn=ca,$SUFFIX```)
|
||||
- IPA Config (```cn=ipaconfig,cn=etc,$SUFFIX```)
|
||||
- Sys Accounts (subtree of ```cn=sysaccounts,cn=etc,$SUFFIX```)
|
||||
- Topology (subtree of ```cn=topology,cn=ipa,cn=etc,$SUFFIX```)
|
||||
- Certmap (```cn=certmap,$SUFFIX```)
|
||||
- Certmap Rules (subtree of ```cn=certmaprules,cn=certmap,$SUFFIX```)
|
||||
- s4u2proxy (subtree of ```cn=s4u2proxy,cn=etc,$SUFFIX```)
|
||||
- Passkey Config (```cn=passkeyconfig,cn=etc,$SUFFIX```)
|
||||
- Desktop Profile (```cn=desktop-profile,$SUFFIX```)
|
||||
- OTP (```cn=otp,cn=etc,$SUFFIX``)
|
||||
- Realm (subtree of ```cn=realm domains,cn=ipa,cn=etc,$SUFFIX```)
|
||||
- AD (subtree of ```cn=ad,cn=etc,$SUFFIX```)
|
||||
- Master Configurations (subtree of ```cn=masters,cn=ipa,cn=etc,$SUFFIX```)
|
||||
- Domain Configuration (```cn=domain level,cn=ipa,cn=etc,$SUFFIX```)
|
||||
|
||||
#### Accounts
|
||||
|
||||
- Computers (subtree of ```cn=computers,cn=accounts,$SUFFIX```)
|
||||
- Administrator (uid=admin,cn=users,cn=accounts,$SUFFIX)
|
||||
- Users (subtree of ```cn=users,cn=accounts,$SUFFIX```)
|
||||
- Groups (subtree of ```cn=groups,cn=accounts,$SUFFIX```)
|
||||
- Roles (subtree of ```cn=roles,cn=accounts,$SUFFIX```)
|
||||
- Host Groups (subtree of ```cn=hostgroups,cn=accounts,$SUFFIX```)
|
||||
- Services (subtree of ```cn=services,cn=accounts,$SUFFIX```)
|
||||
- Views (subtree of ```cn=views,cn=accounts,$SUFFIX```)
|
||||
- IP Services (subtree of ```cn=ipservices,cn=accounts,$SUFFIX```)
|
||||
- Sub IDs (subtree of ```cn=subids,cn=accounts,$SUFFIX```)
|
||||
|
||||
#### HBAC & PBAC
|
||||
|
||||
- HBAC Services (subtree of ```cn=hbacservices,cn=hbac,$SUFFIX```)
|
||||
- HBAC Service Groups (subtree of ```cn=hbacservicegroups,cn=hbac,$SUFFIX```)
|
||||
- PBAC Privileges (subtree of ```cn=privileges,cn=pbac,$SUFFIX```)
|
||||
- PBAC Permissions (subtree of ```cn=permissions,cn=pbac,$SUFFIX```)
|
||||
|
||||
#### Sudo
|
||||
|
||||
- Sudo Rules (subtree of ```cn=sudorules,cn=sudo,$SUFFIX```)
|
||||
- Sudo Commands (subtree of ```cn=sudocmds,cn=sudo,$SUFFIX```)
|
||||
- Sudo Command Groups (subtree of ```cn=sudocmdgroups,cn=sudo,$SUFFIX```)
|
||||
|
||||
#### DNS
|
||||
|
||||
- DNS Records (subtree of ```cn=dns,$SUFFIX```)
|
||||
- DNS Servers (subtree of ```cn=servers,cn=dns,$SUFFIX```)
|
||||
|
||||
#### Kerberos
|
||||
|
||||
- Kerberos Realm & Policy (subtree of ```cn=kerberos,$SUFFIX```)
|
||||
- Kerberos Password Policy (```cn=global_policy,cn=$REALM,cn=kerberos,$SUFFIX```)
|
||||
- Kerberos Default Password Policy (```cn=default kerberos service password policy,cn=$REALM,cn=kerberos,$SUFFIX```)
|
||||
|
||||
#### Misc
|
||||
|
||||
- Automounts & Automount Maps (subtree of ```cn=automount,$SUFFIX```)
|
||||
- Trusts (subtree of ```cn=trusts,$SUFFIX```)
|
||||
- Provisioning (subtree of ```cn=accounts,cn=provisioning,$SUFFIX```)
|
||||
- SELinux Usermaps (subtree of ```cn=usermap,cn=selinux,$SUFFIX```)
|
||||
- DUA Config Profiles (subtree of ```ou=profile,$SUFFIX```)
|
||||
- CA Certificates (subtree of ```cn=certificates,cn=ipa,cn=etc,$SUFFIX```)
|
||||
|
||||
#### Excluded Subtrees
|
||||
|
||||
- All Class Of Service (COS) entries
|
||||
- ```cn=sec,cn=dns,$SUFFIX```
|
||||
- ```cn=custodia,cn=ipa,cn=etc,$SUFFIX```
|
||||
|
||||
#### Entries NOT be to migrated
|
||||
|
||||
We will not have migrate existing keys from the remote IPA server so the
|
||||
following will not be migrated:
|
||||
@@ -221,127 +293,11 @@ following will not be migrated:
|
||||
* DNSSEC keys
|
||||
* admin password
|
||||
* DM password
|
||||
* Basically anything in Custodia
|
||||
* Anything in Custodia
|
||||
|
||||
#### Migrating conflicting data
|
||||
### Migration steps
|
||||
|
||||
For new entries (users, groups, hosts, etc) we can do a straight LDAP
|
||||
ADD after massaging necessary attributes (see Attributes to not migrate).
|
||||
|
||||
That is the easy part.
|
||||
|
||||
There are some parts of IPA that are default and will exist in both servers
|
||||
such as the configuration (config commands), allow_all HBAC rule,
|
||||
permissions, privileges, roles, selinux mappings, ipaservers hostgroup and more.
|
||||
|
||||
The strategy for this will be use the existing entry comparison in ldap2 and
|
||||
modify the new IPA instation to match the original minus some specific things like
|
||||
the CA subject base in the config. So in most cases we favor the remote IPA
|
||||
installation except for things which are installation-dependent.
|
||||
|
||||
The downside of this is that it could interpret "fresher" defaults in the
|
||||
new IPA server with stale ones from the old. We will need good logging around
|
||||
these types of entries.
|
||||
|
||||
Standard boilerplate will not be evaluated, things like cosTemplates. These are not
|
||||
user-modifiable and if a user decides to mess with them, they can correct it
|
||||
after migration.
|
||||
|
||||
#### Migrating cn=config
|
||||
|
||||
A number of plugins are optional in IPA: nis, compat, ACME.
|
||||
|
||||
These will be enabled in the new IPA server if enabled in the remote at
|
||||
the end of migration:
|
||||
|
||||
* cn=Schema Compatibility, cn=plugins, cn=config
|
||||
* cn=NIS Server, cn=plugins, cn=config
|
||||
|
||||
Schema compatibility will be disabled during migration as it can cause
|
||||
performance issues and we don't want to try to migrate anything in
|
||||
cn=compat since it is generated data.
|
||||
|
||||
ACME is a feature of the CA. We should be able to import ipa_acme_manage
|
||||
and call enable/disable within the migration if it is remotely enabled.
|
||||
|
||||
#### Configuration to not migrate
|
||||
|
||||
These will not be migrated:
|
||||
|
||||
* AD Trusts
|
||||
* Winsync configuration (though the entries will)
|
||||
* CA, KRA
|
||||
* DNSSEC
|
||||
|
||||
#### Special migration
|
||||
|
||||
cn=kerberos contains the Kerberos master key as well as the default
|
||||
ticket policy. Only the policy can be migrated.
|
||||
|
||||
#### Attributes to not migrate
|
||||
|
||||
Some attributes we will need to completely drop because they contain key or
|
||||
server-specific information for the remote IPA server. This may also
|
||||
include objectClasses that may need to be dropped if the last attribute
|
||||
is removed.
|
||||
|
||||
* krbPrincipalKey (will require dropping objectclasses)
|
||||
* krbExtraData
|
||||
* ipaNTSecurityIdentifier
|
||||
* memberOf (will be reconstructed after the migration)
|
||||
* userCertificate if issued by the remote IPA server
|
||||
|
||||
#### Attributes to update to new REALM
|
||||
|
||||
If a REALM change is part of migration then these will need to be updated
|
||||
to reflect the new installation.
|
||||
|
||||
* krbPrincipalKey
|
||||
* krbCanonicalName
|
||||
|
||||
#### Attributes to change basedn
|
||||
|
||||
Prior to writing a new value any DN syntax attributes will need to be
|
||||
examined to see if they contain the remote baseDN if it is different. If
|
||||
so then the following need to be updated:
|
||||
|
||||
* mepManagedEntry
|
||||
* mepManagedBy
|
||||
|
||||
#### Migrating custom schema
|
||||
|
||||
Schema is stored in LDAP so we *should* be able to use our ldap library
|
||||
to discover differences. We probably want to do only ADD and bail if we
|
||||
determine something will be a MOD or DEL.
|
||||
|
||||
#### Excluding specific entries
|
||||
|
||||
The ignore user/group options of migrate-ds were introduced so that non-compliant
|
||||
entries could be skipped to not block the migration. We can ignore these for now.
|
||||
|
||||
#### Performance considerations
|
||||
|
||||
Some benefits would be likely if common entry types were added in
|
||||
batches rather than individually. This would not easily allow for a
|
||||
"stop on failure" approach but could be considerably faster.
|
||||
|
||||
The basic idea is that added entries of the same type (user, group, etc)
|
||||
would be accumulated and added together. The result would be iterated
|
||||
through and logged.
|
||||
|
||||
The memberOf plugin should be disabled. A fixup task can be run post-migration
|
||||
to calculate the memberships.
|
||||
|
||||
#### Idempotency
|
||||
|
||||
The migrate-ds plugin manages idempotency because it skips over entries
|
||||
that already have been migrated (or exist). This new migration will handle
|
||||
it by merging the remote and local entries. This may not be desirable in
|
||||
all cases.
|
||||
|
||||
#### Migration steps
|
||||
|
||||
A simplistic view of the steps
|
||||
A simplistic view of the steps (the following might be outdated/unnecessary - TODO)
|
||||
|
||||
Set migration mode=True
|
||||
Disable compat
|
||||
@@ -358,44 +314,167 @@ Enable compat
|
||||
Restart world
|
||||
Run ipa-server-upgrade
|
||||
|
||||
### pure LDAP migration
|
||||
#### Supported Migration Scenarios
|
||||
|
||||
The current code generally works minus a few bugs and RFEs, some of which are resolved
|
||||
by doing an IPA-to-IPA migration instead. It should be maintained for now and migrated
|
||||
to the standalone client in the future.
|
||||
There are quite a few ways that migration can be done, both for testing and production uses. Any scenario not specifically mentioned here should be considered unsupported.
|
||||
|
||||
These bugs should be considered for the existing plugin.
|
||||
These scenarios are not enforced by code. At best we can prompt the user
|
||||
for confirmation if we believe that they are non-conformant but I choose
|
||||
the trade-off of allowing for unsupported migrations to the flexibility
|
||||
of not trying to force square pegs into round holes.
|
||||
|
||||
* https://pagure.io/freeipa/issue/3096 - error when migrating unknown schema
|
||||
* https://pagure.io/freeipa/issue/3100 - Check for userPassword in migration
|
||||
* https://pagure.io/freeipa/issue/4738 - [RFE] ipa migrate-ds should provide option for creating UPG from posixGroup objectClass
|
||||
* https://pagure.io/freeipa/issue/5020 - migrate-ds: does not show migrated users if an error happened during group migration
|
||||
* https://pagure.io/freeipa/issue/5693 - Passwords become "expired" when migrating from directory server to IPA
|
||||
* https://pagure.io/freeipa/issue/6105 - migrate-ds is not completely ignoring attributes.
|
||||
* https://pagure.io/freeipa/issue/6360 - ipa migrate-ds does not rename uniquemember/member attributes properly
|
||||
* https://pagure.io/freeipa/issue/6380 - ipa migrate-ds should print warning for referrals
|
||||
* https://pagure.io/freeipa/issue/7368 - ipa migrate-ds converts groupofuniquenames objects to groupofnames, but leaves groupofuniquenames objectclass present
|
||||
* https://pagure.io/freeipa/issue/7749 - `ipa migrate-ds` fails to migrate user and group data from directory server to IDM.
|
||||
There are a few points to consider.
|
||||
|
||||
**Replicas**
|
||||
|
||||
All references to replicas in the existing deployment will not be migrated. There is no mechanism to one-by-one replace each server with a new one using migration. One will be migrated and new replicas will need to be manually added directly to that using ipa-replica-install.
|
||||
|
||||
**Kerberos**
|
||||
|
||||
The Kerberos master key will not be migrated. Kerberos principals are retained but the keys are not.
|
||||
|
||||
**Certificates**
|
||||
|
||||
The existing CA will be abandoned in favor of a CA on the new installation.
|
||||
|
||||
If the realm, domain and CA subject base (default is O=REALM) are identical between the two installations then there is no way, other than the CA private key, to distinguish between the original CA and the new CA. Therefore no certificates will be maintained in the migration. All certificates other than those already present in the new IPA server as part of installation will need to be re-issued.
|
||||
|
||||
If one of these doesn't match then the certificates will be retained but the backing PKI will be lost so there will be no possibility of renewals or revocation (no OCSP or CRL).
|
||||
|
||||
|
||||
##### Scenario 1 - Production to new production
|
||||
|
||||
Preconditions:
|
||||
* There is a existing production IPA server (or servers)
|
||||
* There is a new IPA server installation with the same realm and domain
|
||||
|
||||
Optional:
|
||||
* If desired the realm and domain may be changed. The migrated data will accommodate these changes but this will require reconfiguration of all clients, etc. beyond just re-enrollment.
|
||||
|
||||
Result:
|
||||
* All valid IPA entries will be migrated
|
||||
* All ids (uid, gid, SID, etc) will be maintained
|
||||
* All certificates issued from the previous CA will be dropped unless the CA subject base DN, or the realm, is changed in the new deployment.
|
||||
* All clients must re-enroll to the new deployment
|
||||
* Users will have to migrate their passwords to generate Kerberos and other keys
|
||||
|
||||
##### Scenario 2 - Production to new staging
|
||||
|
||||
Preconditions:
|
||||
* There is an existing production IPA server (or servers)
|
||||
* There is a new IPA server installation with a different realm and domain (e.g. staging.example.test)
|
||||
|
||||
Result:
|
||||
* All valid IPA entries will be migrated
|
||||
* All ids (uid, gid, SID, etc) will be re-generated
|
||||
* All certificates from the previous CA will be preserved
|
||||
* Given this is a new staging deployment there will be no enrolled clients. The host entries from the production deployment will exist but all keys are dropped.
|
||||
* Users will have to migrate their passwords to generate Kerberos and other keys
|
||||
|
||||
##### Scenario 3 - Staging to new production
|
||||
|
||||
Preconditions:
|
||||
* There is an existing production IPA server (or servers)
|
||||
* There is a new IPA server installation with a different realm and domain (e.g. staging.example.test)
|
||||
|
||||
Result:
|
||||
* All valid IPA entries will be migrated
|
||||
* All ids (uid, gid, SID, etc) will be re-generated
|
||||
* All certificates from the previous CA will be removed
|
||||
* Users will have to migrate their passwords to generate Kerberos and other keys
|
||||
|
||||
|
||||
##### Scenario 4 - From IPA backup
|
||||
|
||||
The migration tool will have the capability to do offline migration using an LDIF file. An IPA backup is a tar ball that contains the IPA data in EXAMPLE-TEST-userRoot.ldif
|
||||
|
||||
Preconditions:
|
||||
* A backup from an existing IPA installation exists
|
||||
* There is a new IPA server installation with the same realm and domain
|
||||
|
||||
Result:
|
||||
* All valid IPA entries will be migrated
|
||||
* All ids (uid, gid, SID, etc) will be maintained
|
||||
* All certificates issued from the previous CA will be dropped unless the CA subject base DN, or the realm, is changed in the new deployment.
|
||||
* All clients must re-enroll to the new deployment
|
||||
* Users will have to migrate their passwords to generate Kerberos and other keys
|
||||
|
||||
## Logging
|
||||
|
||||
By default the log file will be /var/log/ipa-migrate.log and will be appended to
|
||||
and not overwritten. This is so it can reflect multiple runs if they are required.
|
||||
|
||||
At least the DN of all entries written should be logged (pkey may be sufficient).
|
||||
### Standard logging
|
||||
|
||||
Logging by object type could be handy and should natural since this is how the
|
||||
objects will be sorted.
|
||||
Here is an example of the standard logging
|
||||
|
||||
DEBUG logging may want to show gory details, particularly when merging entries.
|
||||
```
|
||||
024-02-27T17:10:03Z DEBUG ================================================================================
|
||||
2024-02-27T17:10:03Z INFO IPA to IPA migration starting ...
|
||||
2024-02-27T17:10:03Z DEBUG Migration options:
|
||||
2024-02-27T17:10:03Z DEBUG --mode=prod-mode
|
||||
2024-02-27T17:10:03Z DEBUG --hostname=hpe-dl385gen8-01.hpe2.lab.eng.bos.redhat.com
|
||||
2024-02-27T17:10:03Z DEBUG --verbose=False
|
||||
2024-02-27T17:10:03Z DEBUG --bind-dn=cn=directory manager
|
||||
2024-02-27T17:10:03Z DEBUG --bind-pw-file=None
|
||||
2024-02-27T17:10:03Z DEBUG --cacertfile=None
|
||||
2024-02-27T17:10:03Z DEBUG --subtree=[]
|
||||
2024-02-27T17:10:03Z DEBUG --log-file=/var/log/ipa-migrate.log
|
||||
2024-02-27T17:10:03Z DEBUG --skip-schema=False
|
||||
2024-02-27T17:10:03Z DEBUG --skip-config=False
|
||||
2024-02-27T17:10:03Z DEBUG --migrate-dns=False
|
||||
2024-02-27T17:10:03Z DEBUG --dryrun=True
|
||||
2024-02-27T17:10:03Z DEBUG --dryrun-record=None
|
||||
2024-02-27T17:10:03Z DEBUG --force=False
|
||||
2024-02-27T17:10:03Z DEBUG --version=False
|
||||
2024-02-27T17:10:03Z DEBUG --quiet=False
|
||||
2024-02-27T17:10:03Z DEBUG --schema-overwrite=False
|
||||
2024-02-27T17:10:03Z DEBUG --reset-range=False
|
||||
2024-02-27T17:10:03Z DEBUG --db-ldif=None
|
||||
2024-02-27T17:10:03Z DEBUG --schema-ldif=None
|
||||
2024-02-27T17:10:03Z DEBUG --config-ldif=None
|
||||
2024-02-27T17:10:03Z DEBUG --no-prompt=False
|
||||
2024-02-27T17:10:03Z DEBUG flushing ldapi://%2Frun%2Fslapd-HPE2-LAB-ENG-BOS-REDHAT-COM.socket from SchemaCache
|
||||
2024-02-27T17:10:03Z DEBUG retrieving schema for SchemaCache url=ldapi://%2Frun%2Fslapd-HPE2-LAB-ENG-BOS-REDHAT-COM.socket conn=<ldap.ldapobject.SimpleLDAPObject object at 0x7f5a1eb68210>
|
||||
2024-02-27T17:10:03Z DEBUG retrieving schema for SchemaCache url=ldap://hpe-dl385gen8-01.hpe2.lab.eng.bos.redhat.com conn=<ldap.ldapobject.SimpleLDAPObject object at 0x7f5a1e7eba10>
|
||||
2024-02-27T17:10:04Z DEBUG Found realm from remote server: HPE2.LAB.ENG.BOS.REDHAT.COM
|
||||
2024-02-27T17:10:04Z INFO Migrating schema ...
|
||||
2024-02-27T17:10:04Z DEBUG Getting schema from the remote server ...
|
||||
2024-02-27T17:10:04Z DEBUG Retrieved 1556 attributes and 349 objectClasses
|
||||
2024-02-27T17:10:07Z DEBUG Migrated 0 attributes and 0 objectClasses
|
||||
2024-02-27T17:10:07Z DEBUG Skipped 1556 attributes and 349 objectClasses
|
||||
2024-02-27T17:10:07Z INFO Migrating configuration ...
|
||||
2024-02-27T17:10:07Z DEBUG Getting config from the remote server ...
|
||||
2024-02-27T17:10:08Z DEBUG flushing ldapi://%2Frun%2Fslapd-HPE2-LAB-ENG-BOS-REDHAT-COM.socket from SchemaCache
|
||||
2024-02-27T17:10:08Z DEBUG retrieving schema for SchemaCache url=ldapi://%2Frun%2Fslapd-HPE2-LAB-ENG-BOS-REDHAT-COM.socket conn=<ldap.ldapobject.SimpleLDAPObject object at 0x7f5a1eb68210>
|
||||
2024-02-27T17:10:09Z INFO Migrating database ... (this make take a while)
|
||||
2024-02-27T17:10:11Z DEBUG Removed IPA issued userCertificate from: krbprincipalname=ldap/hpe-dl385gen8-01.hpe2.lab.eng.bos.redhat.com@HPE2.LAB.ENG.BOS.REDHAT.COM,cn=services,cn=accounts,dc=hpe2,dc=lab,dc=eng,dc=bos,dc=redhat,dc=com
|
||||
2024-02-27T17:10:11Z DEBUG Skipping remote certificate entry: 'cn=HPE2.LAB.ENG.BOS.REDHAT.COM IPA CA,cn=certificates,cn=ipa,cn=etc,dc=hpe2,dc=lab,dc=eng,dc=bos,dc=redhat,dc=com' Issuer: CN=Certificate Authority,O=HPE2.LAB.ENG.BOS.REDHAT.COM
|
||||
2024-02-27T17:10:11Z DEBUG Removed IPA issued userCertificate from: krbprincipalname=HTTP/hpe-dl385gen8-01.hpe2.lab.eng.bos.redhat.com@HPE2.LAB.ENG.BOS.REDHAT.COM,cn=services,cn=accounts,dc=hpe2,dc=lab,dc=eng,dc=bos,dc=redhat,dc=com
|
||||
2024-02-27T17:10:16Z INFO Running ipa-server-upgrade ... (this make take a while)
|
||||
2024-02-27T17:10:16Z INFO Skipping ipa-server-upgrade in dryrun mode.
|
||||
2024-02-27T17:10:16Z INFO Running SIDGEN task ...
|
||||
2024-02-27T17:10:16Z INFO Skipping SIDGEN task in dryrun mode.
|
||||
2024-02-27T17:10:16Z INFO Migration complete!
|
||||
```
|
||||
|
||||
## Implementation
|
||||
### Verbose logging
|
||||
|
||||
The command will be named ipa-migrate. It must determine whether the remote server is
|
||||
actually an IPA server or not. ipaclient/discovery.py::ipacheckldap may be re-usable.
|
||||
If get the verbose logging you simply just use the **--verbose, -v** CLI option. Here you will see the exact operations there were performed. Here is an example showing the additional information that is logged.
|
||||
|
||||
|
||||
```
|
||||
...
|
||||
...
|
||||
2024-02-28T15:30:53Z INFO Migrating database ... (this make take a while)
|
||||
2024-02-28T15:30:53Z INFO Entry is different and will be updated: 'uid=admin,cn=users,cn=accounts,dc=hpe2,dc=lab,dc=eng,dc=bos,dc=redhat,dc=com' attribute 'ipaNTSecurityIdentifier' replaced with val 'S-1-5-21-404865364-1326736403-3398440945-501' old value: ['S-1-5-21-404865364-1326736403-3398440945-500']
|
||||
2024-02-28T15:30:53Z INFO Add db entry 'uid=mark,cn=users,cn=accounts,dc=hpe2,dc=lab,dc=eng,dc=bos,dc=redhat,dc=com - users'
|
||||
2024-02-28T15:30:53Z INFO Entry is different and will be updated: 'cn=HPE2.LAB.ENG.BOS.REDHAT.COM_id_range,cn=ranges,cn=etc,dc=hpe2,dc=lab,dc=eng,dc=bos,dc=redhat,dc=com' attribute 'ipaBaseID' replaced with val '9000999' old value: ['90000000']
|
||||
2024-02-28T15:30:53Z INFO Entry is different and will be updated: 'cn=HPE2.LAB.ENG.BOS.REDHAT.COM_subid_range,cn=ranges,cn=etc,dc=hpe2,dc=lab,dc=eng,dc=bos,dc=redhat,dc=com' attribute 'ipaIDRangeSize' replaced with val '2147352575' old value: ['2147352576']
|
||||
...
|
||||
...
|
||||
```
|
||||
|
||||
The standalone client should use a unique context, migration. This will
|
||||
allow for a separate configuration file.
|
||||
|
||||
## Feature Management
|
||||
|
||||
@@ -405,51 +484,55 @@ No UI option will be provided. This is command-line client only.
|
||||
|
||||
### CLI
|
||||
|
||||
Overview of the CLI commands.
|
||||
Overview of the CLI usage
|
||||
|
||||
#### IPA to IPA
|
||||
|
||||
Advance knowledge of the DIT substantially reduces the number of options
|
||||
necessary for migration.
|
||||
ipa-migrate <prod-mode|stage-mode> <HOSTNAME> [options]
|
||||
|
||||
| Option | Description |
|
||||
| --- | --- |
|
||||
| --dry-run | try the migration without writing data |
|
||||
| --force | ignore errors and keep going |
|
||||
| --version | version of the tool |
|
||||
| --quiet | output only errors |
|
||||
| --log-file | log to the given file |
|
||||
| --help | this message |
|
||||
| --bind-dn, -D | The bind DN to use for authentication to the remote IPA server (default is "cn=directory manager") |
|
||||
| --bind-pw, -w | The password for the bind DN |
|
||||
| --bind-pw-file, -j | A file that contains the password |
|
||||
| --cacertfile, -Z | The CA cert file |
|
||||
| --skip-schema, -S | Do not migrate the schema |
|
||||
| --skip-config, -C | Do not migrate the DS configuration |
|
||||
| --schema-overwrite, -O | Completely overwrite schema |
|
||||
| --reset-range, -r | Reset all the DNA attributes (uidNumber, etc) to the magic regen value (-1) |
|
||||
| --db-ldif, -f | An LDIF file containing the export of the userRoot database |
|
||||
| --schema-ldif, -m | An LDIF file containing the schema from the remote server |
|
||||
| --config-ldif, -g | The DS config file dse.ldif |
|
||||
| --migrate-dns, -B | Migrate the DNS records in the database |
|
||||
| --subtree, -s | Non standard IPA subtree to include in the migration |
|
||||
| --dryrun, -x | try the migration without writing data |
|
||||
| --dryrun-record, -o | Perform dryrun but record all the LDAP changes to a LDIF file |
|
||||
| --force. -F | ignore errors and keep going |
|
||||
| --version, -V | version of the tool |
|
||||
| --quiet, -q | output only errors |
|
||||
| --no-prompt, -n | Do not do a confirmation prompt about starting the migration |
|
||||
| --log-file, -l | log to the given file |
|
||||
| --verbose, -v | Display verbose output |
|
||||
| --help, -h | this message |
|
||||
|
||||
The DM password will be prompted for interactively.
|
||||
If the DM password is not provided then you will be prompted for it.
|
||||
|
||||
| Argument | Description |
|
||||
| url | ldap url for remote IPA server |
|
||||
#### Examples
|
||||
|
||||
#### pure LDAP
|
||||
# ipa-migrate prod-mode remote.server.com
|
||||
|
||||
# ipa-migrate prod-mode remote.server.com --dryrun
|
||||
|
||||
# ipa-migrate prod-mode remote.server.com -D "cn=directory manager" -j ./passwd.txt
|
||||
|
||||
# ipa-migrate prod-mode remote.server.com --db-ldif=/tmp/remote-userroot.ldif
|
||||
|
||||
# ipa-migrate prod-mode remote.server.com --skip-config --skip-schema
|
||||
|
||||
# ipa-migrate stage-mode remote.server.com --dryrun-record=/tmp/dryrun-ops.ldif
|
||||
|
||||
# ipa-migrate stage-mode remote.server.com --config-ldif=/tmp/dse.ldif --schema-ldif=/tmp/schema.ldif --db-ldif=/tmp/remote-userroot.ldif
|
||||
|
||||
Will remain unchanged unless one of the bug fixes requires it (perhaps for the UPG
|
||||
ticket).
|
||||
# ipa-migrate stage-mode remote.server.com --subtree="ou=my own data,dc=ipa,dc=com"
|
||||
|
||||
### Configuration
|
||||
|
||||
N/A
|
||||
|
||||
## Upgrade
|
||||
|
||||
N/A
|
||||
|
||||
## Test plan
|
||||
|
||||
There are currently no tests for migration.
|
||||
|
||||
Some simplisitic approaches for starting testing might include:
|
||||
* Count the number of entries that will be migrated and ensure they were migrated, by type (hosts, groups, etc).
|
||||
* Verify that the services enabled on the remote side are enabled after migration (NIS, ACME, etc).
|
||||
* Double-check, perhaps spot-checking, memberOf
|
||||
* Migrate a password to ensure it was imported properly
|
||||
|
||||
We have a data generation script in freeipa-tools that may be leveraged to generate the data but it currently generates a LOT of entries which is likely too much for automation.
|
||||
|
||||
## Troubleshooting and debugging
|
||||
|
||||
|
||||
@@ -173,6 +173,7 @@ any user. However, to make it usable for S4U2Proxy (constrained delegation),
|
||||
the service ticket must be forwardable. In such case the Kerberos service would
|
||||
be able to impersonate user and requires an explicit administrative permission.
|
||||
|
||||
|
||||
IPA API provides a way to record this permission in both host and service
|
||||
command families. The following commands have option
|
||||
`--ok-to-auth-as-delegate=BOOL`:
|
||||
@@ -183,6 +184,23 @@ command families. The following commands have option
|
||||
This flag is equivalent to MS-SFU's `TrustedToAuthenticationForDelegation`
|
||||
boolean setting.
|
||||
|
||||
The behavior of FreeIPA regarding S4U2Self-granted tickets differs depending of
|
||||
the krb5 version that was used to compile:
|
||||
|
||||
* **krb5 1.20+**: KDC will always respond to S4U2Self TGS-REQ with forwardable
|
||||
tickets, except if the requester principal is set as impersonator service in
|
||||
at least one general constrained delegation rule (even if the rule has no
|
||||
target set)
|
||||
* **krb5 1.19-**: KDC will respond to all S4U2Self TGS-REQs with non-forwardable
|
||||
tickets
|
||||
|
||||
In both cases, granting the `ok-to-auth-as-delegate` permission to a principal
|
||||
will override this default behavior and allow it to obtain forwardable tickets
|
||||
to itself. In practice, it means the `ok-to-auth-as-delegate` permission is
|
||||
required if you want to grant a service the special privilege to impersonate
|
||||
any user against services configured as targets in a general constrained
|
||||
delegation rule.
|
||||
|
||||
### General constrained delegation design
|
||||
|
||||
General constrained delegation uses two objects: a rule and a target.
|
||||
|
||||
@@ -11,6 +11,7 @@ m2r2
|
||||
## ipa dependencies
|
||||
dnspython
|
||||
jwcrypto
|
||||
ifaddr
|
||||
netaddr
|
||||
qrcode
|
||||
six
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
..
|
||||
.. _1-server-install:
|
||||
|
||||
Copyright 2015-2018 Red Hat, Inc.
|
||||
|
||||
This work is licensed under the Creative Commons Attribution 4.0
|
||||
@@ -157,8 +158,8 @@ more!
|
||||
|
||||
To prepare for the next unit, exit the ``server`` SSH session (but
|
||||
do not shut the VM down). The next essential unit is
|
||||
`Unit 2: Enrolling client machines <2-client-install.rst>`_.
|
||||
:ref:`Unit 2: Enrolling client machines <2-client-install>`.
|
||||
|
||||
Alternatively, if you would like to immediately install a replica
|
||||
server (essential for production deployments), you can take a detour
|
||||
to `Unit 7: Replica installation <7-replica-install.rst>`_.
|
||||
to :ref:`Unit 7: Replica installation <7-replica-install>`.
|
||||
@@ -1,9 +1,11 @@
|
||||
.. _10-ssh-key-management:
|
||||
|
||||
Unit 10: SSH user and host key management
|
||||
=========================================
|
||||
|
||||
**Prerequisites:**
|
||||
|
||||
- `Unit 3: User management and Kerberos authentication <3-user-management.rst>`_
|
||||
- :ref:`Unit 3: User management and Kerberos authentication <3-user-management>`
|
||||
|
||||
In this module you will explore how to use FreeIPA as a backend
|
||||
provider for SSH keys. Instead of distributing ``authorized_keys``
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
.. _11-kerberos-ticket-policy:
|
||||
|
||||
Unit 11: Kerberos ticket policy
|
||||
=========================================
|
||||
|
||||
**Prerequisites:**
|
||||
|
||||
- `Unit 3: User management and Kerberos authentication <3-user-management.rst>`_
|
||||
- :ref:`Unit 3: User management and Kerberos authentication <3-user-management>`
|
||||
|
||||
In this module you will explore how to manage properties of Kerberos
|
||||
authentication and authorization in FreeIPA.
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
.. _12-external-idp-support:
|
||||
|
||||
Unit 12: Authentication against external Identity Providers
|
||||
===========================================================
|
||||
|
||||
**Prerequisites:**
|
||||
|
||||
- `Unit 11: Kerberos ticket policy <11-kerberos-ticket-policy.rst>`_
|
||||
- :ref:`Unit 11: Kerberos ticket policy <11-kerberos-ticket-policy>`
|
||||
|
||||
In this module you will explore how to manage use external OAuth 2.0 servers to
|
||||
authorize issuance of Kerberos tickets in FreeIPA.
|
||||
@@ -389,6 +391,11 @@ for Keycloak or Red Hat SSO IdPs. The template expects both Keycloak's realm
|
||||
typically deployed as a part of a larger solution. These options may not be
|
||||
needed for other pre-defined templates like Google or Github.
|
||||
|
||||
The `openid` scope is mandatory since
|
||||
[Keycloak 19.0.2](https://www.keycloak.org/docs/latest/upgrading/index.html#userinfo-endpoint-changes).
|
||||
Without the `openid` scope, Keycloak refuses userinfo requests with HTTP
|
||||
response 403: `invalid_scope` `Missing openid scope`.
|
||||
|
||||
Associate IdP reference with IPA user
|
||||
-------------------------------------
|
||||
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
.. _2-client-install:
|
||||
|
||||
Unit 2: Enrolling client machines
|
||||
===================================
|
||||
|
||||
**Prerequisites**:
|
||||
|
||||
- `Unit 1: Installing the FreeIPA server <1-server-install.rst>`_
|
||||
- :ref:`Unit 1: Installing the FreeIPA server <1-server-install>`
|
||||
|
||||
In this unit, you will enrol a *host* as a client of your FreeIPA
|
||||
domain. This means that *users* in your FreeIPA realm (or Active
|
||||
@@ -57,4 +59,4 @@ logged onto the host can also acquire Kerberos tickets for accessing
|
||||
*services* in your domain.
|
||||
|
||||
You can now move on to
|
||||
`Unit 3: User management and Kerberos authentication <3-user-management.rst>`_.
|
||||
:ref:`Unit 3: User management and Kerberos authentication <3-user-management>`.
|
||||
@@ -1,3 +1,5 @@
|
||||
.. _3-user-management:
|
||||
|
||||
Unit 3: User management and Kerberos authentication
|
||||
=====================================================
|
||||
|
||||
@@ -124,8 +126,8 @@ is a true *single sign-on* protocol!
|
||||
|
||||
Now that you have created some users, it's time to define some
|
||||
access policies. Proceed to
|
||||
`Unit 4: Host-based access control (HBAC) <4-hbac.rst>`_.
|
||||
:ref:`Unit 4: Host-based access control (HBAC) <4-hbac>`.
|
||||
|
||||
Alternatively, if you are interested in SSH public key management
|
||||
for users and hosts, jump ahead to
|
||||
`Unit 10: SSH user and host key management <10-ssh-key-management.rst>`_.
|
||||
:ref:`Unit 10: SSH user and host key management <10-ssh-key-management>`.
|
||||
@@ -1,9 +1,11 @@
|
||||
.. _4-hbac:
|
||||
|
||||
Unit 4: Host-based access control (HBAC)
|
||||
==========================================
|
||||
|
||||
**Prerequisites:**
|
||||
|
||||
- `Unit 3: User management and Kerberos authentication <3-user-management.rst>`_
|
||||
- :ref:`Unit 3: User management and Kerberos authentication <3-user-management>`
|
||||
|
||||
FreeIPA's *host-based access control* (HBAC) feature allows you to
|
||||
define policies that restrict access to hosts or services based on
|
||||
@@ -142,7 +144,7 @@ Now try ``alice``::
|
||||
This was the final mandatory unit in the workshop. From here, there
|
||||
are several optional units you can choose from. You can proceed
|
||||
directly to
|
||||
`Unit 5: Web application authentication and authorisation <5-web-app-authnz.rst>`_.
|
||||
:ref:`Unit 5: Web application authentication and authorisation <5-web-app-authnz>`.
|
||||
Otherwise,
|
||||
`return to the curriculum overview <workshop.rst#curriculum-overview>`_
|
||||
:ref:`return to the curriculum overview <curriculum-overview>`
|
||||
to see all the options.
|
||||
@@ -1,10 +1,12 @@
|
||||
.. _5-web-app-authnz:
|
||||
|
||||
Unit 5: Web application authentication and authorisation
|
||||
==========================================================
|
||||
|
||||
**Prerequisites**:
|
||||
|
||||
- `Unit 3: User management and Kerberos authentication <3-user-management.rst>`_
|
||||
- `Unit 4: Host-based access control (HBAC) <4-hbac.rst>`_
|
||||
- :ref:`Unit 3: User management and Kerberos authentication <3-user-management>`
|
||||
- :ref:`Unit 4: Host-based access control (HBAC) <4-hbac>`
|
||||
|
||||
You can configure many kinds of applications to rely on FreeIPA's
|
||||
centralised authentication, including web applications. In this
|
||||
@@ -309,4 +311,4 @@ authenticated as ``bob`` instead?
|
||||
This unit is now concluded. Now that you have mastered web app
|
||||
authentication, you'll want to configure TLS for your site. Proceed
|
||||
to
|
||||
`Unit 6: Service certificates <6-cert-management.rst>`_.
|
||||
:ref:`Unit 6: Service certificates <6-cert-management>`.
|
||||
@@ -1,3 +1,5 @@
|
||||
.. _6-cert-management:
|
||||
|
||||
Unit 6: Service certificates
|
||||
================================
|
||||
|
||||
@@ -126,9 +128,9 @@ Restart Apache and make a request to the app over HTTPS::
|
||||
|
||||
|
||||
You can now proceed to
|
||||
`Unit 7: Replica installation <7-replica-install.rst>`_
|
||||
:ref:`Unit 7: Replica installation <7-replica-install>`
|
||||
or
|
||||
`Unit 8: Sudo rule management <8-sudorule.rst>`_.
|
||||
:ref:`Unit 8: Sudo rule management <8-sudorule>`.
|
||||
Otherwise,
|
||||
`return to the curriculum overview <workshop.rst#curriculum-overview>`_
|
||||
:ref:`return to the curriculum overview <curriculum-overview>`
|
||||
to see all the options.
|
||||
@@ -1,9 +1,11 @@
|
||||
.. _7-replica-install:
|
||||
|
||||
Unit 7: Replica installation
|
||||
==============================
|
||||
|
||||
**Prerequisites**:
|
||||
|
||||
- `Unit 1: Installing the FreeIPA server <1-server-install.rst>`_
|
||||
- :ref:`Unit 1: Installing the FreeIPA server <1-server-install>`
|
||||
|
||||
FreeIPA is designed to be run in a replicated multi-master
|
||||
environment. In this unit, we will install a replica of the
|
||||
@@ -19,7 +21,7 @@ Client installation
|
||||
|
||||
The first step of replica creation is to enrol the machine that will
|
||||
become the replica. SSH to the ``replica`` VM and enrol it per
|
||||
`Unit 2: Enrolling client machines <2-client-install.rst>`_
|
||||
:ref:`Unit 2: Enrolling client machines <2-client-install>`
|
||||
|
||||
Replica promotion
|
||||
-----------------
|
||||
@@ -56,7 +58,7 @@ After ``ipa-replica-install`` finishes, the replica is operational.
|
||||
LDAP changes on any server will be replicated to all other servers.
|
||||
|
||||
You can proceed to
|
||||
`Unit 8: Sudo rule management <8-sudorule.rst>`_
|
||||
:ref:`Unit 8: Sudo rule management <8-sudorule>`
|
||||
or
|
||||
`return to the curriculum overview <workshop.rst#curriculum-overview>`_
|
||||
:ref:`return to the curriculum overview <curriculum-overview>`
|
||||
to see all the available topics.
|
||||
@@ -1,10 +1,12 @@
|
||||
.. _8-sudorule:
|
||||
|
||||
Unit 8: Sudo rule management
|
||||
============================
|
||||
|
||||
**Prerequisites**:
|
||||
|
||||
- `Unit 3: User management and Kerberos authentication <3-user-management.rst>`_
|
||||
- `Unit 4: Host-based access control (HBAC) <4-hbac.rst>`_
|
||||
- :ref:`Unit 3: User management and Kerberos authentication <3-user-management>`
|
||||
- :ref:`Unit 4: Host-based access control (HBAC) <4-hbac>`
|
||||
|
||||
Sudo is a program that allows users to run programs as another user
|
||||
with different privileges (possibly ``root``). Sudo rules provide
|
||||
@@ -241,4 +243,4 @@ restart (or start) Apache, but not run other commands via ``sudo``::
|
||||
|
||||
|
||||
This concludes the unit. Now that you have Sudo rules working,
|
||||
proceed to `Unit 9: SELinux User Maps <9-selinux-user-map.rst>`_.
|
||||
proceed to :ref:`Unit 9: SELinux User Maps <9-selinux-user-map>`.
|
||||
@@ -1,11 +1,13 @@
|
||||
.. _9-selinux-user-map:
|
||||
|
||||
Unit 9: SELinux User Maps
|
||||
=========================
|
||||
|
||||
**Prerequisites**:
|
||||
|
||||
- `Unit 3: User management and Kerberos authentication <3-user-management.rst>`_
|
||||
- `Unit 4: Host-based access control (HBAC) <4-hbac.rst>`_
|
||||
- `Unit 8: Sudo rule management <8-sudorule.rst>`_
|
||||
- :ref:`Unit 3: User management and Kerberos authentication <3-user-management>`
|
||||
- :ref:`Unit 4: Host-based access control (HBAC) <4-hbac>`
|
||||
- :ref:`Unit 8: Sudo rule management <8-sudorule>`
|
||||
|
||||
SELinux is a *mandatory access controls* mechanism for Linux,
|
||||
providing more powerful and flexible access control than traditional
|
||||
@@ -43,7 +45,7 @@ will automatically be used.
|
||||
|
||||
Recall that members of the ``sysadmin`` User Group already have
|
||||
access to ``webservers`` via the ``sysadmin_webservers`` rule that
|
||||
was created in `Unit 4: Host-based access control (HBAC)`_. Create
|
||||
was created in :ref:`Unit 4: Host-based access control (HBAC)<4-hbac>`. Create
|
||||
the SELinux User Map::
|
||||
|
||||
[client]$ ipa selinuxusermap-add sysadmin_staff_t \
|
||||
@@ -135,7 +137,7 @@ the program being run::
|
||||
Hello, world!
|
||||
|
||||
This concludes the unit. You can now proceed to
|
||||
`Unit 10: SSH user and host key management <10-ssh-key-management.rst>`_
|
||||
:ref:`Unit 10: SSH user and host key management <10-ssh-key-management>`
|
||||
or
|
||||
`return to the curriculum overview <workshop.rst#curriculum-overview>`_
|
||||
:ref:`return to the curriculum overview <curriculum-overview>`
|
||||
to see all the available topics.
|
||||
@@ -1,3 +1,5 @@
|
||||
.. _building:
|
||||
|
||||
Building Vagrant box images
|
||||
===========================
|
||||
|
||||
|
||||
@@ -30,6 +30,6 @@ Example: https://goo.gl/forms/UOkcsVROqV
|
||||
Vagrant boxes
|
||||
-------------
|
||||
|
||||
See ``building.rst`` for instructions on building Vagrant boxes.
|
||||
See :ref:`building` for instructions on building Vagrant boxes.
|
||||
At time of writing, the ``freeipa/freeipa-workshop`` box is
|
||||
FreeIPA 4.9.3 / Fedora 34, for the VirtualBox and libvirt providers.
|
||||
+16
-14
@@ -1,4 +1,5 @@
|
||||
..
|
||||
.. _workshop:
|
||||
|
||||
Copyright 2015, 2016 Red Hat, Inc.
|
||||
|
||||
This work is licensed under the Creative Commons Attribution 4.0
|
||||
@@ -18,27 +19,28 @@ X.509 certificates for services.
|
||||
|
||||
.. _FreeIPA: http://www.freeipa.org/page/Main_Page
|
||||
|
||||
.. _curriculum-overview:
|
||||
|
||||
Curriculum overview
|
||||
-------------------
|
||||
|
||||
Mandatory:
|
||||
|
||||
- `Unit 1: Installing the FreeIPA server <1-server-install.rst>`_
|
||||
- `Unit 2: Enrolling client machines <2-client-install.rst>`_
|
||||
- `Unit 3: User management and Kerberos authentication <3-user-management.rst>`_
|
||||
- `Unit 4: Host-based access control (HBAC) <4-hbac.rst>`_
|
||||
- :ref:`Unit 1: Installing the FreeIPA server <1-server-install>`
|
||||
- :ref:`Unit 2: Enrolling client machines <2-client-install>`
|
||||
- :ref:`Unit 3: User management and Kerberos authentication <3-user-management>`
|
||||
- :ref:`Unit 4: Host-based access control (HBAC) <4-hbac>`
|
||||
|
||||
Optional units—choose the topics that are relevant to you:
|
||||
|
||||
- `Unit 5: Web application authentication and authorisation <5-web-app-authnz.rst>`_
|
||||
- `Unit 6: Service certificates <6-cert-management.rst>`_
|
||||
- `Unit 7: Replica installation <7-replica-install.rst>`_
|
||||
- `Unit 8: Sudo rule management <8-sudorule.rst>`_
|
||||
- `Unit 9: SELinux User Maps <9-selinux-user-map.rst>`_
|
||||
- `Unit 10: SSH user and host key management <10-ssh-key-management.rst>`_
|
||||
- `Unit 11: Kerberos ticket policy <11-kerberos-ticket-policy.rst>`_
|
||||
- `Unit 12: External IdP support <12-external-idp-support.rst>`_
|
||||
- :ref:`Unit 5: Web application authentication and authorisation <5-web-app-authnz>`
|
||||
- :ref:`Unit 6: Service certificates <6-cert-management>`
|
||||
- :ref:`Unit 7: Replica installation <7-replica-install>`
|
||||
- :ref:`Unit 8: Sudo rule management <8-sudorule>`
|
||||
- :ref:`Unit 9: SELinux User Maps <9-selinux-user-map>`
|
||||
- :ref:`Unit 10: SSH user and host key management <10-ssh-key-management>`
|
||||
- :ref:`Unit 11: Kerberos ticket policy <11-kerberos-ticket-policy>`
|
||||
- :ref:`Unit 12: External IdP support <12-external-idp-support>`
|
||||
|
||||
Editing files on VMs
|
||||
--------------------
|
||||
@@ -241,7 +243,7 @@ Next step
|
||||
---------
|
||||
|
||||
You are ready to begin the workshop. Continue to
|
||||
`Unit 1: Installing the FreeIPA server <1-server-install.rst>`_.
|
||||
:ref:`Unit 1: Installing the FreeIPA server <1-server-install>`.
|
||||
|
||||
|
||||
After the workshop
|
||||
|
||||
+139
-15
@@ -78,8 +78,8 @@
|
||||
%global ds_version 1.4.3.16-12
|
||||
%global selinux_policy_version 3.14.3-107
|
||||
%else
|
||||
# DNA interval enabled
|
||||
%global ds_version 2.0.5-1
|
||||
# version supporting LMDB and lib389.cli_ctl.dblib.run_dbscan utility
|
||||
%global ds_version 2.1.0
|
||||
%global selinux_policy_version 38.1.1-1
|
||||
%endif
|
||||
|
||||
@@ -124,10 +124,11 @@
|
||||
|
||||
# Make sure to use 389-ds-base versions that fix https://github.com/389ds/389-ds-base/issues/4700
|
||||
# and has DNA interval enabled
|
||||
# version supporting LMDB and lib389.cli_ctl.dblib.run_dbscan utility
|
||||
%if 0%{?fedora} < 34
|
||||
%global ds_version 1.4.4.16-1
|
||||
%else
|
||||
%global ds_version 2.0.7-1
|
||||
%global ds_version 2.1.0
|
||||
%endif
|
||||
|
||||
# Fix for TLS 1.3 PHA, RHBZ#1775146
|
||||
@@ -173,7 +174,7 @@
|
||||
%endif
|
||||
|
||||
# RHEL 8.3+, F32+ has 0.79.13
|
||||
%global certmonger_version 0.79.7-3
|
||||
%global certmonger_version 0.79.17-1
|
||||
|
||||
# RHEL 8.2+, F32+ has 3.58
|
||||
%global nss_version 3.44.0-4
|
||||
@@ -289,6 +290,7 @@ BuildRequires: gettext
|
||||
BuildRequires: gettext-devel
|
||||
BuildRequires: python3-devel
|
||||
BuildRequires: python3-setuptools
|
||||
BuildRequires: python3-argcomplete
|
||||
BuildRequires: systemd >= %{systemd_version}
|
||||
# systemd-tmpfiles which is executed from make install requires apache user
|
||||
BuildRequires: httpd
|
||||
@@ -306,9 +308,12 @@ BuildRequires: libpwquality-devel
|
||||
BuildRequires: libsss_idmap-devel
|
||||
BuildRequires: libsss_certmap-devel
|
||||
BuildRequires: libsss_nss_idmap-devel >= %{sssd_version}
|
||||
%if 0%{?fedora} >= 39 || 0%{?rhel} >= 10
|
||||
%if 0%{?fedora} >= 41
|
||||
# Do not use nodejs22 on fedora < 41, https://pagure.io/freeipa/issue/9643
|
||||
BuildRequires: nodejs(abi)
|
||||
%elif 0%{?fedora} >= 39 || 0%{?rhel} >= 10
|
||||
# Do not use nodejs20 on fedora < 39, https://pagure.io/freeipa/issue/9374
|
||||
BuildRequires: nodejs(abi)
|
||||
BuildRequires: nodejs(abi) < 127
|
||||
%else
|
||||
BuildRequires: nodejs(abi) < 111
|
||||
%endif
|
||||
@@ -401,7 +406,7 @@ BuildRequires: python3-libipa_hbac
|
||||
BuildRequires: python3-libsss_nss_idmap
|
||||
BuildRequires: python3-lxml
|
||||
BuildRequires: python3-netaddr >= %{python_netaddr_version}
|
||||
BuildRequires: python3-netifaces
|
||||
BuildRequires: python3-ifaddr
|
||||
BuildRequires: python3-pki >= %{pki_version}
|
||||
BuildRequires: python3-polib
|
||||
BuildRequires: python3-pyasn1
|
||||
@@ -714,7 +719,7 @@ Requires: oddjob-mkhomedir
|
||||
Requires: libsss_autofs
|
||||
Requires: autofs
|
||||
Requires: libnfsidmap
|
||||
Requires: nfs-utils
|
||||
Requires: (nfs-utils or nfsv4-client-utils)
|
||||
Requires: sssd-tools >= %{sssd_version}
|
||||
Requires(post): policycoreutils
|
||||
|
||||
@@ -873,6 +878,7 @@ Requires: %{name}-common = %{version}-%{release}
|
||||
Requires(pre): python3-ldap >= %{python_ldap_version}
|
||||
Requires: gnupg2
|
||||
Requires: keyutils
|
||||
Requires: python3-argcomplete
|
||||
Requires: python3-cffi
|
||||
Requires: python3-cryptography >= 1.6
|
||||
Requires: python3-dateutil
|
||||
@@ -882,7 +888,7 @@ Requires: python3-gssapi >= 1.2.0
|
||||
Requires: python3-jwcrypto >= 0.4.2
|
||||
Requires: python3-libipa_hbac
|
||||
Requires: python3-netaddr >= %{python_netaddr_version}
|
||||
Requires: python3-netifaces >= 0.10.4
|
||||
Requires: python3-ifaddr
|
||||
Requires: python3-pyasn1 >= 0.3.2-2
|
||||
Requires: python3-pyasn1-modules >= 0.3.2-2
|
||||
Requires: python3-pyusb
|
||||
@@ -902,6 +908,7 @@ Requires: python3-urllib3 >= 1.24.2-3
|
||||
# For urllib3.util.ssl_match_hostname
|
||||
Requires: python3-urllib3 >= 1.25.8
|
||||
%endif
|
||||
Requires: python3-systemd
|
||||
|
||||
%description -n python3-ipalib
|
||||
IPA is an integrated solution to provide centrally managed Identity (users,
|
||||
@@ -993,6 +1000,26 @@ Requires(post): selinux-policy-%{selinuxtype}
|
||||
|
||||
%description selinux
|
||||
Custom SELinux policy module for FreeIPA
|
||||
|
||||
%package selinux-nfast
|
||||
Summary: FreeIPA SELinux policy for nCipher nfast HSMs
|
||||
BuildArch: noarch
|
||||
Requires: selinux-policy-%{selinuxtype}
|
||||
Requires(post): selinux-policy-%{selinuxtype}
|
||||
%{?selinux_requires}
|
||||
|
||||
%description selinux-nfast
|
||||
Custom SELinux policy module for nCipher nfast HSMs
|
||||
|
||||
%package selinux-luna
|
||||
Summary: FreeIPA SELinux policy for Thales Luna HSMs
|
||||
BuildArch: noarch
|
||||
Requires: selinux-policy-%{selinuxtype}
|
||||
Requires(post): selinux-policy-%{selinuxtype}
|
||||
%{?selinux_requires}
|
||||
|
||||
%description selinux-luna
|
||||
Custom SELinux policy module for Thales Luna HSMs
|
||||
# with selinux
|
||||
%endif
|
||||
|
||||
@@ -1079,6 +1106,15 @@ rm -f %{buildroot}%{_usr}/share/ipa/ui/images/product-name.png
|
||||
%endif
|
||||
# RHEL spec file only: END
|
||||
|
||||
%if 0%{?fedora} >= 38
|
||||
# Register CLI tools for bash completion (fedora only)
|
||||
for clitool in ipa-migrate
|
||||
do
|
||||
register-python-argcomplete "${clitool}" > "${clitool}"
|
||||
install -p -m 0644 -D -t '%{buildroot}%{bash_completions_dir}' "${clitool}"
|
||||
done
|
||||
%endif
|
||||
|
||||
%find_lang %{gettext_domain}
|
||||
|
||||
%if ! %{ONLY_CLIENT}
|
||||
@@ -1142,6 +1178,10 @@ fi
|
||||
/bin/systemctl reload-or-try-restart oddjobd
|
||||
|
||||
%tmpfiles_create ipa.conf
|
||||
%journal_catalog_update
|
||||
|
||||
%postun server
|
||||
%journal_catalog_update
|
||||
|
||||
%posttrans server
|
||||
# don't execute upgrade and restart of IPA when server is not installed
|
||||
@@ -1278,13 +1318,23 @@ if [ $1 -gt 1 ] ; then
|
||||
cp /etc/ipa/ca.crt /var/lib/ipa-client/pki/kdc-ca-bundle.pem
|
||||
cp /etc/ipa/ca.crt /var/lib/ipa-client/pki/ca-bundle.pem
|
||||
fi
|
||||
|
||||
%{__python3} -c 'from ipaclient.install.client import configure_krb5_snippet; configure_krb5_snippet()' >>/var/log/ipaupgrade.log 2>&1
|
||||
%{__python3} -c 'from ipaclient.install.client import update_ipa_nssdb; update_ipa_nssdb()' >>/var/log/ipaupgrade.log 2>&1
|
||||
chmod 0600 /var/log/ipaupgrade.log
|
||||
SSH_CLIENT_SYSTEM_CONF="/etc/ssh/ssh_config"
|
||||
if [ -f "$SSH_CLIENT_SYSTEM_CONF" ]; then
|
||||
sed -E --in-place=.orig 's/^(HostKeyAlgorithms ssh-rsa,ssh-dss)$/# disabled by ipa-client update\n# \1/' "$SSH_CLIENT_SYSTEM_CONF"
|
||||
if grep -E -q '^HostKeyAlgorithms ssh-rsa,ssh-dss' $SSH_CLIENT_SYSTEM_CONF 2>/dev/null; then
|
||||
sed -E --in-place=.orig 's/^(HostKeyAlgorithms ssh-rsa,ssh-dss)$/# disabled by ipa-client update\n# \1/' "$SSH_CLIENT_SYSTEM_CONF"
|
||||
fi
|
||||
# https://pagure.io/freeipa/issue/9536
|
||||
# replace sss_ssh_knownhostsproxy with sss_ssh_knownhosts
|
||||
if [ -f '/usr/bin/sss_ssh_knownhosts' ]; then
|
||||
if grep -E -q 'Include' $SSH_CLIENT_SYSTEM_CONF 2>/dev/null ; then
|
||||
SSH_CLIENT_SYSTEM_CONF="/etc/ssh/ssh_config.d/04-ipa.conf"
|
||||
fi
|
||||
sed -E --in-place=.orig 's/^(GlobalKnownHostsFile \/var\/lib\/sss\/pubconf\/known_hosts)$/# disabled by ipa-client update\n# \1/' $SSH_CLIENT_SYSTEM_CONF
|
||||
sed -E --in-place=.orig 's/(ProxyCommand \/usr\/bin\/sss_ssh_knownhostsproxy -p \%p \%h)/# replaced by ipa-client update\n KnownHostsCommand \/usr\/bin\/sss_ssh_knownhosts \%H/' $SSH_CLIENT_SYSTEM_CONF
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
@@ -1300,17 +1350,76 @@ fi
|
||||
semodule -d ipa_custodia &> /dev/null || true;
|
||||
%selinux_modules_install -s %{selinuxtype} %{_datadir}/selinux/packages/%{selinuxtype}/%{modulename}.pp.bz2
|
||||
|
||||
%post selinux-nfast
|
||||
%selinux_modules_install -s %{selinuxtype} %{_datadir}/selinux/packages/%{selinuxtype}/%{modulename}-nfast.pp.bz2
|
||||
|
||||
%post selinux-luna
|
||||
%selinux_modules_install -s %{selinuxtype} %{_datadir}/selinux/packages/%{selinuxtype}/%{modulename}-luna.pp.bz2
|
||||
|
||||
%postun selinux
|
||||
if [ $1 -eq 0 ]; then
|
||||
%selinux_modules_uninstall -s %{selinuxtype} %{modulename}
|
||||
semodule -e ipa_custodia &> /dev/null || true;
|
||||
fi
|
||||
|
||||
%postun selinux-nfast
|
||||
if [ $1 -eq 0 ]; then
|
||||
%selinux_modules_uninstall -s %{selinuxtype} %{modulename}-nfast
|
||||
fi
|
||||
|
||||
%postun selinux-luna
|
||||
if [ $1 -eq 0 ]; then
|
||||
%selinux_modules_uninstall -s %{selinuxtype} %{modulename}-luna
|
||||
fi
|
||||
|
||||
%posttrans selinux
|
||||
%selinux_relabel_post -s %{selinuxtype}
|
||||
# with_selinux
|
||||
%endif
|
||||
|
||||
%triggerin client -- sssd-common < 2.10
|
||||
# Has the client been configured?
|
||||
restore=0
|
||||
test -f '/var/lib/ipa-client/sysrestore/sysrestore.index' && restore=$(wc -l '/var/lib/ipa-client/sysrestore/sysrestore.index' | awk '{print $1}')
|
||||
|
||||
if [ -f '/etc/ssh/sshd_config' -a $restore -ge 2 ]; then
|
||||
SSH_CLIENT_SYSTEM_CONF="/etc/ssh/ssh_config"
|
||||
if [ -f "$SSH_CLIENT_SYSTEM_CONF" ]; then
|
||||
# https://pagure.io/freeipa/issue/9536
|
||||
# downgrade sss_ssh_knownhosts with sss_ssh_knownhostsproxy
|
||||
if [ -f '/usr/bin/sss_ssh_knownhosts' ]; then
|
||||
if grep -E -q 'Include' $SSH_CLIENT_SYSTEM_CONF 2>/dev/null ; then
|
||||
SSH_CLIENT_SYSTEM_CONF="/etc/ssh/ssh_config.d/04-ipa.conf"
|
||||
fi
|
||||
GLOBALKNOWNHOSTFILE="GlobalKnownHostsFile /var/lib/sss/pubconf/known_hosts/"
|
||||
grep -qF '$GLOBALKNOWNHOSTFILE' $SSH_CLIENT_SYSTEM_CONF
|
||||
if [ $? -ne 0 ]; then
|
||||
sed -E --in-place=.orig '/(# IPA-related configuration changes to ssh_config)/a # added by ipa-client update\n'"$GLOBALKNOWNHOSTFILE"'' $SSH_CLIENT_SYSTEM_CONF
|
||||
fi
|
||||
sed -E --in-place=.orig 's/(KnownHostsCommand \/usr\/bin\/sss_ssh_knownhosts \%H)/ProxyCommand \/usr\/bin\/sss_ssh_knownhostsproxy -p \%p \%h/' $SSH_CLIENT_SYSTEM_CONF
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
%triggerin client -- sssd-common >= 2.10
|
||||
# Has the client been configured?
|
||||
restore=0
|
||||
test -f '/var/lib/ipa-client/sysrestore/sysrestore.index' && restore=$(wc -l '/var/lib/ipa-client/sysrestore/sysrestore.index' | awk '{print $1}')
|
||||
|
||||
if [ -f '/etc/ssh/sshd_config' -a $restore -ge 2 ]; then
|
||||
SSH_CLIENT_SYSTEM_CONF="/etc/ssh/ssh_config"
|
||||
if [ -f "$SSH_CLIENT_SYSTEM_CONF" ]; then
|
||||
# https://pagure.io/freeipa/issue/9536
|
||||
# upgrade sss_ssh_knownhostsproxy with sss_ssh_knownhosts
|
||||
if [ -f '/usr/bin/sss_ssh_knownhosts' ]; then
|
||||
if grep -E -q 'Include' $SSH_CLIENT_SYSTEM_CONF 2>/dev/null ; then
|
||||
SSH_CLIENT_SYSTEM_CONF="/etc/ssh/ssh_config.d/04-ipa.conf"
|
||||
fi
|
||||
sed -E --in-place=.orig 's/^(GlobalKnownHostsFile \/var\/lib\/sss\/pubconf\/known_hosts)$/# disabled by ipa-client update\n# \1/' $SSH_CLIENT_SYSTEM_CONF
|
||||
sed -E --in-place=.orig 's/(ProxyCommand \/usr\/bin\/sss_ssh_knownhostsproxy -p \%p \%h)/# replaced by ipa-client update\n KnownHostsCommand \/usr\/bin\/sss_ssh_knownhosts \%H/' $SSH_CLIENT_SYSTEM_CONF
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
%triggerin client -- openssh-server < 8.2
|
||||
# Has the client been configured?
|
||||
@@ -1409,6 +1518,10 @@ fi
|
||||
%{_sbindir}/ipa-crlgen-manage
|
||||
%{_sbindir}/ipa-cert-fix
|
||||
%{_sbindir}/ipa-acme-manage
|
||||
%{_sbindir}/ipa-migrate
|
||||
%if 0%{?fedora} >= 38
|
||||
%{bash_completions_dir}/ipa-migrate
|
||||
%endif
|
||||
%{_libexecdir}/certmonger/dogtag-ipa-ca-renew-agent-submit
|
||||
%{_libexecdir}/certmonger/ipa-server-guard
|
||||
%dir %{_libexecdir}/ipa
|
||||
@@ -1441,6 +1554,7 @@ fi
|
||||
%attr(644,root,root) %{_unitdir}/ipa-otpd@.service
|
||||
%attr(644,root,root) %{_unitdir}/ipa-ccache-sweep.service
|
||||
%attr(644,root,root) %{_unitdir}/ipa-ccache-sweep.timer
|
||||
%attr(644,root,root) %{_journalcatalogdir}/ipa.catalog
|
||||
# END
|
||||
%attr(755,root,root) %{plugin_dir}/libipa_pwd_extop.so
|
||||
%attr(755,root,root) %{plugin_dir}/libipa_enrollment_extop.so
|
||||
@@ -1483,6 +1597,7 @@ fi
|
||||
%{_mandir}/man1/ipa-crlgen-manage.1*
|
||||
%{_mandir}/man1/ipa-cert-fix.1*
|
||||
%{_mandir}/man1/ipa-acme-manage.1*
|
||||
%{_mandir}/man1/ipa-migrate.1*
|
||||
|
||||
|
||||
%files -n python3-ipaserver
|
||||
@@ -1555,10 +1670,11 @@ fi
|
||||
%dir %{_sysconfdir}/ipa/html
|
||||
%config(noreplace) %{_sysconfdir}/ipa/html/ssbrowser.html
|
||||
%config(noreplace) %{_sysconfdir}/ipa/html/unauthorized.html
|
||||
%ghost %attr(0644,root,root) %config(noreplace) %{_sysconfdir}/httpd/conf.d/ipa-rewrite.conf
|
||||
%ghost %attr(0644,root,root) %config(noreplace) %{_sysconfdir}/httpd/conf.d/ipa.conf
|
||||
%ghost %attr(0644,root,root) %config(noreplace) %{_sysconfdir}/httpd/conf.d/ipa-kdc-proxy.conf
|
||||
%ghost %attr(0640,root,root) %config(noreplace) %{_sysconfdir}/httpd/conf.d/ipa-pki-proxy.conf
|
||||
%dir %{_sysconfdir}/ipa/http
|
||||
%ghost %attr(0644,root,root) %config(noreplace) %{_sysconfdir}/ipa/httpd/ipa-rewrite.conf
|
||||
%ghost %attr(0644,root,root) %config(noreplace) %{_sysconfdir}/ipa/httpd/ipa.conf
|
||||
%ghost %attr(0644,root,root) %config(noreplace) %{_sysconfdir}/ipa/httpd/ipa-kdc-proxy.conf
|
||||
%ghost %attr(0640,root,root) %config(noreplace) %{_sysconfdir}/ipa/httpd/ipa-pki-proxy.conf
|
||||
%ghost %attr(0644,root,root) %config(noreplace) %{_sysconfdir}/ipa/kdcproxy/ipa-kdc-proxy.conf
|
||||
%ghost %attr(0644,root,root) %config(noreplace) %{_usr}/share/ipa/html/ca.crt
|
||||
%ghost %attr(0640,root,named) %config(noreplace) %{_sysconfdir}/named/ipa-ext.conf
|
||||
@@ -1757,6 +1873,14 @@ fi
|
||||
%files selinux
|
||||
%{_datadir}/selinux/packages/%{selinuxtype}/%{modulename}.pp.*
|
||||
%ghost %verify(not md5 size mode mtime) %{_sharedstatedir}/selinux/%{selinuxtype}/active/modules/200/%{modulename}
|
||||
|
||||
%files selinux-nfast
|
||||
%{_datadir}/selinux/packages/%{selinuxtype}/%{modulename}-nfast.pp.*
|
||||
%ghost %verify(not md5 size mode mtime) %{_sharedstatedir}/selinux/%{selinuxtype}/active/modules/200/%{modulename}-nfast
|
||||
|
||||
%files selinux-luna
|
||||
%{_datadir}/selinux/packages/%{selinuxtype}/%{modulename}-luna.pp.*
|
||||
%ghost %verify(not md5 size mode mtime) %{_sharedstatedir}/selinux/%{selinuxtype}/active/modules/200/%{modulename}-luna
|
||||
# with selinux
|
||||
%endif
|
||||
|
||||
|
||||
@@ -18,6 +18,10 @@ systemdsystemunit_DATA = \
|
||||
ipa-ccache-sweep.timer \
|
||||
$(NULL)
|
||||
|
||||
dist_systemdcatalog_DATA = \
|
||||
ipa.catalog \
|
||||
$(NULL)
|
||||
|
||||
CLEANFILES = $(systemdsystemunit_DATA)
|
||||
|
||||
%: %.in Makefile
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
#
|
||||
#
|
||||
#
|
||||
-- 6d70f1b493df36478bc3499257cd3b17
|
||||
Subject: IPA API command was executed and result of its execution was audited
|
||||
Defined-by: FreeIPA
|
||||
Support: https://lists.fedorahosted.org/archives/list/freeipa-users@lists.fedorahosted.org/
|
||||
Documentation: man:ipa(1)
|
||||
Documentation: https://freeipa.readthedocs.io/en/latest/api/index.html
|
||||
Documentation: https://freeipa.readthedocs.io/en/latest/api/@IPA_API_COMMAND@.html
|
||||
|
||||
FreeIPA provides an extensive API that allows to manage all aspects of IPA deployments.
|
||||
|
||||
The following information about the API command executed is available:
|
||||
|
||||
@MESSAGE@
|
||||
|
||||
The command was executed by '@SYSLOG_IDENTIFIER@' utility. If the utility name
|
||||
is '/mod_wsgi`, then this API command came from a remote source through the IPA
|
||||
API end-point.
|
||||
|
||||
The message includes following fields:
|
||||
|
||||
- executable name and PID ('/mod_wsgi' for HTTP end-point; in this case it
|
||||
was '@SYSLOG_IDENTIFIER@' command)
|
||||
|
||||
- '[IPA.API]' marker to allow searches with 'journalctl -g IPA.API'
|
||||
|
||||
- authenticated Kerberos principal or '[autobind]' marker for LDAPI-based
|
||||
access as root. In this case it was '@IPA_API_ACTOR@'
|
||||
|
||||
- name of the command executed, in this case '@IPA_API_COMMAND@'
|
||||
|
||||
- result of execution: `SUCCESS` or an exception name. In this case it was
|
||||
'@IPA_API_RESULT@'
|
||||
|
||||
- LDAP backend instance identifier. The identifier will be the same for all
|
||||
operations performed under the same request. This allows to identify operations
|
||||
which were executed as a part of the same API request instance. For API
|
||||
operations that didn't result in LDAP access, there will be
|
||||
'[no_connection_id]' marker.
|
||||
|
||||
- finally, a list of arguments and options passed to the command is provided
|
||||
in JSON format.
|
||||
|
||||
---------
|
||||
The following list of arguments and options were passed to the command
|
||||
'@IPA_API_COMMAND@' by the '@IPA_API_ACTOR@' actor:
|
||||
|
||||
@IPA_API_PARAMS@
|
||||
---------
|
||||
|
||||
A detailed information about FreeIPA API can be found at upstream documentation API reference:
|
||||
https://freeipa.readthedocs.io/en/latest/api/index.html
|
||||
|
||||
For details on the IPA API command '@IPA_API_COMMAND@' see
|
||||
https://freeipa.readthedocs.io/en/latest/api/@IPA_API_COMMAND@.html
|
||||
|
||||
@@ -42,7 +42,7 @@ import six
|
||||
from ipalib.install.kinit import kinit_keytab
|
||||
from ipapython import ipautil
|
||||
from ipapython.dn import DN
|
||||
from ipalib import api, errors, x509
|
||||
from ipalib import api, errors, x509, sysrestore
|
||||
from ipaplatform.paths import paths
|
||||
from ipaserver.plugins.ldap2 import ldap2
|
||||
from ipaserver.install import ca, cainstance, dsinstance, certs
|
||||
@@ -383,7 +383,20 @@ def retrieve_cert_continuous(reuse_existing, **kwargs):
|
||||
return result
|
||||
|
||||
new_cert = x509.load_pem_x509_certificate(result[1].encode('ascii'))
|
||||
nickname = get_nickname()
|
||||
if new_cert == old_cert:
|
||||
sstore = sysrestore.StateFile(paths.SYSRESTORE)
|
||||
if (
|
||||
sstore.get_state('pki_hsm', 'enabled')
|
||||
and sstore.get_state('pki_hsm', 'token_name')
|
||||
and nickname != 'ipaCert'
|
||||
):
|
||||
# HSMs must be networked so the cert is already present
|
||||
# exception of the RA Agent certificate.
|
||||
return (
|
||||
ISSUED,
|
||||
new_cert.public_bytes(x509.Encoding.PEM).decode("ascii"),
|
||||
)
|
||||
syslog.syslog(syslog.LOG_INFO, "Updated certificate not available")
|
||||
# No cert available yet, tell certmonger to wait another 8 hours
|
||||
return (WAIT_WITH_DELAY, 8 * 60 * 60, '')
|
||||
|
||||
@@ -15,6 +15,7 @@ import six
|
||||
import gssapi
|
||||
|
||||
from ipalib.install.kinit import kinit_keytab, kinit_password
|
||||
from ipapython.admintool import admin_cleanup_global_argv
|
||||
|
||||
if six.PY3:
|
||||
unicode = str
|
||||
@@ -52,11 +53,13 @@ def parse_options():
|
||||
"--password",
|
||||
action="store",
|
||||
dest="password",
|
||||
help="Display debugging information",
|
||||
help="Password for Active Directory administrator",
|
||||
sensitive=True
|
||||
)
|
||||
|
||||
options, args = parser.parse_args()
|
||||
safe_options = parser.get_safe_opts(options)
|
||||
admin_cleanup_global_argv(parser, options, sys.argv)
|
||||
|
||||
# We only use first argument of the passed args but as D-BUS interface
|
||||
# in oddjobd cannot expose optional, we fill in empty slots from IPA side
|
||||
|
||||
@@ -28,7 +28,6 @@ import shutil
|
||||
import traceback
|
||||
|
||||
from ipalib.install import certstore
|
||||
from ipapython import directivesetter
|
||||
from ipapython import ipautil
|
||||
from ipalib import api, errors
|
||||
from ipalib import x509
|
||||
@@ -37,7 +36,7 @@ from ipaserver.install import certs, cainstance
|
||||
from ipaserver.plugins.ldap2 import ldap2
|
||||
from ipaplatform import services
|
||||
from ipaplatform.paths import paths
|
||||
from ipapython.certdb import TrustFlags
|
||||
from ipapython.certdb import TrustFlags, get_ca_nickname
|
||||
|
||||
|
||||
def _main():
|
||||
@@ -50,6 +49,8 @@ def _main():
|
||||
|
||||
dogtag_service = services.knownservices['pki_tomcatd']
|
||||
|
||||
ca = cainstance.CAInstance(host_name=api.env.host)
|
||||
|
||||
# dogtag opens its NSS database in read/write mode so we need it
|
||||
# shut down so certmonger can open it read/write mode. This avoids
|
||||
# database corruption. It should already be stopped by the pre-command
|
||||
@@ -83,13 +84,15 @@ def _main():
|
||||
|
||||
api.Backend.ldap2.connect()
|
||||
|
||||
ca = cainstance.CAInstance(host_name=api.env.host)
|
||||
ca.update_cert_config(nickname, cert)
|
||||
if ca.is_renewal_master():
|
||||
cainstance.update_people_entry(cert)
|
||||
cainstance.update_authority_entry(cert)
|
||||
|
||||
if nickname == 'auditSigningCert cert-pki-ca':
|
||||
if nickname in (
|
||||
'auditSigningCert cert-pki-ca',
|
||||
'auditSigningCert cert-pki-kra',
|
||||
):
|
||||
# Fix trust on the audit cert
|
||||
try:
|
||||
db.run_certutil(['-M',
|
||||
@@ -105,26 +108,9 @@ def _main():
|
||||
"Updating trust on certificate %s failed in %s" %
|
||||
(nickname, db.secdir))
|
||||
elif nickname == 'caSigningCert cert-pki-ca':
|
||||
# Update CS.cfg
|
||||
cfg_path = paths.CA_CS_CFG_PATH
|
||||
config = directivesetter.get_directive(
|
||||
cfg_path, 'subsystem.select', '=')
|
||||
if config == 'New':
|
||||
syslog.syslog(syslog.LOG_NOTICE, "Updating CS.cfg")
|
||||
if cert.is_self_signed():
|
||||
directivesetter.set_directive(
|
||||
cfg_path, 'hierarchy.select', 'Root',
|
||||
quotes=False, separator='=')
|
||||
else:
|
||||
directivesetter.set_directive(
|
||||
cfg_path, 'hierarchy.select', 'Subordinate',
|
||||
quotes=False, separator='=')
|
||||
else:
|
||||
syslog.syslog(syslog.LOG_NOTICE, "Not updating CS.cfg")
|
||||
|
||||
# Remove old external CA certificates
|
||||
for ca_nick, ca_flags in db.list_certs():
|
||||
if ca_flags.has_key:
|
||||
if ca_flags.has_key or not ca_flags.ca:
|
||||
continue
|
||||
# Delete *all* certificates that use the nickname
|
||||
while True:
|
||||
@@ -168,8 +154,11 @@ def _main():
|
||||
"%s" % e)
|
||||
ca_certs = []
|
||||
|
||||
realm_nickname = get_ca_nickname(api.env.realm)
|
||||
for ca_cert, ca_nick, ca_flags in ca_certs:
|
||||
try:
|
||||
if ca_nick == realm_nickname:
|
||||
ca_nick = 'caSigningCert cert-pki-ca'
|
||||
db.add_cert(ca_cert, ca_nick, ca_flags)
|
||||
except ipautil.CalledProcessError as e:
|
||||
syslog.syslog(
|
||||
|
||||
@@ -8,6 +8,7 @@ attributeTypes: (2.16.840.1.113730.3.8.21.1.6 NAME 'ipaCaId' DESC 'Dogtag Author
|
||||
attributeTypes: (2.16.840.1.113730.3.8.21.1.7 NAME 'ipaCaIssuerDN' DESC 'Issuer DN' SUP distinguishedName X-ORIGIN 'IPA v4.4 Lightweight CAs' )
|
||||
attributeTypes: (2.16.840.1.113730.3.8.21.1.8 NAME 'ipaCaSubjectDN' DESC 'Subject DN' SUP distinguishedName X-ORIGIN 'IPA v4.4 Lightweight CAs' )
|
||||
attributeTypes: (2.16.840.1.113730.3.8.21.1.9 NAME 'ipaCaRandomSerialNumberVersion' DESC 'Random Serial Number Version' EQUALITY integerMatch ORDERING integerOrderingMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE X-ORIGIN 'IPA v4.9 RSNv3' )
|
||||
attributeTypes: (2.16.840.1.113730.3.8.21.1.10 NAME 'ipaCaHSMConfiguration' DESC 'HSM Configuration' EQUALITY caseIgnoreMatch ORDERING caseIgnoreOrderingMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE X-ORIGIN 'IPA v4.10 HSM' )
|
||||
objectClasses: (2.16.840.1.113730.3.8.21.2.1 NAME 'ipaCertProfile' SUP top STRUCTURAL MUST ( cn $ description $ ipaCertProfileStoreIssued ) X-ORIGIN 'IPA v4.2' )
|
||||
objectClasses: (2.16.840.1.113730.3.8.21.2.2 NAME 'ipaCaAcl' SUP ipaAssociation STRUCTURAL MUST cn MAY ( ipaCaCategory $ ipaCertProfileCategory $ userCategory $ hostCategory $ serviceCategory $ ipaMemberCa $ ipaMemberCertProfile $ memberService ) X-ORIGIN 'IPA v4.2' )
|
||||
objectClasses: (2.16.840.1.113730.3.8.21.2.3 NAME 'ipaCa' SUP top STRUCTURAL MUST ( cn $ ipaCaId $ ipaCaSubjectDN $ ipaCaIssuerDN ) MAY ( description $ ipaCaRandomSerialNumberVersion ) X-ORIGIN 'IPA v4.4 Lightweight CAs' )
|
||||
objectClasses: (2.16.840.1.113730.3.8.21.2.3 NAME 'ipaCa' SUP top STRUCTURAL MUST ( cn $ ipaCaId $ ipaCaSubjectDN $ ipaCaIssuerDN ) MAY ( description $ ipaCaRandomSerialNumberVersion $ ipaCaHSMConfiguration) X-ORIGIN 'IPA v4.4 Lightweight CAs' )
|
||||
@@ -38,7 +38,6 @@ dist_app_DATA = \
|
||||
default-trust-view.ldif \
|
||||
delegation.ldif \
|
||||
replica-acis.ldif \
|
||||
replica-prevent-time-skew.ldif \
|
||||
ds-nfiles.ldif \
|
||||
ds-ipa-env.conf.template \
|
||||
dns.ldif \
|
||||
@@ -101,6 +100,7 @@ dist_app_DATA = \
|
||||
ipa-kdc-proxy.conf.template \
|
||||
ipa-pki-proxy.conf.template \
|
||||
ipa-rewrite.conf.template \
|
||||
ipa-vhost.conf.template \
|
||||
ipaca_default.ini \
|
||||
ipaca_customize.ini \
|
||||
ipaca_softhsm2.ini \
|
||||
|
||||
@@ -239,6 +239,7 @@ objectClass: ipasshuser
|
||||
uid: admin
|
||||
krbPrincipalName: admin@$REALM
|
||||
krbPrincipalName: root@$REALM
|
||||
krbCanonicalName: admin@$REALM
|
||||
cn: Administrator
|
||||
sn: Administrator
|
||||
uidNumber: $IDSTART
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
#
|
||||
# VERSION 32 - DO NOT REMOVE THIS LINE
|
||||
#
|
||||
# This file may be overwritten on upgrades.
|
||||
#
|
||||
|
||||
# FIXME: WSGISocketPrefix is a server-scope directive. The mod_wsgi package
|
||||
# should really be fixed by adding this its /etc/httpd/conf.d/wsgi.conf:
|
||||
WSGISocketPrefix $WSGI_PREFIX_DIR
|
||||
|
||||
# Configure mod_wsgi handler for /ipa
|
||||
WSGIDaemonProcess ipa processes=$WSGI_PROCESSES threads=1 maximum-requests=500 \
|
||||
user=ipaapi group=ipaapi display-name=%{GROUP} socket-timeout=2147483647 \
|
||||
lang=C.UTF-8 locale=C.UTF-8
|
||||
|
||||
<VirtualHost *:80>
|
||||
ServerName $FQDN
|
||||
ServerAlias ipa-ca.$DOMAIN
|
||||
|
||||
# Logging
|
||||
CustomLog /var/log/httpd/ipa-access.log combined
|
||||
ErrorLog /var/log/httpd/ipa-error.log
|
||||
|
||||
IncludeOptional $HTTPD_IPA_PKI_PROXY_CONF
|
||||
Include $HTTPD_IPA_REWRITE_CONF
|
||||
Include $HTTPD_IPA_CONF
|
||||
</VirtualHost>
|
||||
|
||||
# the below section is only need if you have above 3 lines and want SSL
|
||||
<VirtualHost *:443>
|
||||
ServerName $FQDN
|
||||
ServerAlias ipa-ca.$DOMAIN
|
||||
|
||||
# Logging
|
||||
CustomLog /var/log/httpd/ipa-access.log combined
|
||||
ErrorLog /var/log/httpd/ipa-error.log
|
||||
|
||||
# SSL
|
||||
SSLEngine on
|
||||
SSLCertificateFile $HTTPD_CERT_FILE
|
||||
SSLCertificateKeyFile $HTTPD_KEY_FILE
|
||||
SSLCACertificateFile $IPA_CA_CRT
|
||||
SSLVerifyDepth $MOD_SSL_VERIFY_DEPTH
|
||||
|
||||
# FreeIPA
|
||||
IncludeOptional $HTTPD_IPA_KDCPROXY_CONF_SYMLINK
|
||||
IncludeOptional $HTTPD_IPA_PKI_PROXY_CONF
|
||||
Include $HTTPD_IPA_REWRITE_CONF
|
||||
Include $HTTPD_IPA_CONF
|
||||
</VirtualHost>
|
||||
@@ -35,19 +35,11 @@ AddOutputFilterByType DEFLATE text/html text/plain text/xml \
|
||||
application/javascript application/json text/css \
|
||||
application/x-font-ttf
|
||||
|
||||
# FIXME: WSGISocketPrefix is a server-scope directive. The mod_wsgi package
|
||||
# should really be fixed by adding this its /etc/httpd/conf.d/wsgi.conf:
|
||||
WSGISocketPrefix $WSGI_PREFIX_DIR
|
||||
|
||||
# Configure mod_wsgi handler for /ipa
|
||||
WSGIDaemonProcess ipa processes=$WSGI_PROCESSES threads=1 maximum-requests=500 \
|
||||
user=ipaapi group=ipaapi display-name=%{GROUP} socket-timeout=2147483647 \
|
||||
lang=C.UTF-8 locale=C.UTF-8
|
||||
WSGIScriptAlias /ipa /usr/share/ipa/wsgi.py process-group=ipa \
|
||||
application-group=%{GLOBAL}
|
||||
WSGIScriptReloading Off
|
||||
|
||||
|
||||
# Turn off mod_msgi handler for errors, config, crl:
|
||||
<Location "/ipa/errors">
|
||||
SetHandler None
|
||||
|
||||
@@ -87,8 +87,6 @@ pki_sslserver_subject_dn=cn=%(ipa_fqdn)s,%(ipa_subject_base)s
|
||||
pki_subsystem_nickname=subsystemCert cert-pki-ca
|
||||
pki_subsystem_subject_dn=cn=CA Subsystem,%(ipa_subject_base)s
|
||||
|
||||
pki_theme_enable=True
|
||||
pki_theme_server_dir=/usr/share/pki/common-ui
|
||||
pki_audit_group=pkiaudit
|
||||
pki_group=pkiuser
|
||||
pki_user=pkiuser
|
||||
@@ -166,3 +164,6 @@ pki_audit_signing_subject_dn=cn=KRA Audit,%(ipa_subject_base)s
|
||||
# We will use the dbuser created for the CA.
|
||||
pki_share_db=True
|
||||
pki_share_dbuser_dn=uid=pkidbuser,ou=people,o=ipaca
|
||||
|
||||
# KRA padding, set RSA-OAEP in FIPS mode
|
||||
pki_use_oaep_rsa_keywrap=%(fips_use_oaep_rsa_keywrap)s
|
||||
@@ -1,4 +0,0 @@
|
||||
dn: cn=config
|
||||
changetype: modify
|
||||
replace: nsslapd-ignore-time-skew
|
||||
nsslapd-ignore-time-skew: $SKEWVALUE
|
||||
@@ -1,34 +1,16 @@
|
||||
dn: cn=krbPrincipalName uniqueness,cn=plugins,cn=config
|
||||
dn: cn=kerberos name uniqueness,cn=plugins,cn=config
|
||||
changetype: add
|
||||
objectClass: top
|
||||
objectClass: nsSlapdPlugin
|
||||
objectClass: extensibleObject
|
||||
cn: krbPrincipalName uniqueness
|
||||
cn: kerberos name uniqueness
|
||||
nsslapd-pluginPath: libattr-unique-plugin
|
||||
nsslapd-pluginInitfunc: NSUniqueAttr_Init
|
||||
nsslapd-pluginType: preoperation
|
||||
nsslapd-pluginEnabled: on
|
||||
uniqueness-attribute-name: krbPrincipalName
|
||||
nsslapd-plugin-depends-on-type: database
|
||||
nsslapd-pluginId: NSUniqueAttr
|
||||
nsslapd-pluginVersion: 1.1.0
|
||||
nsslapd-pluginVendor: Fedora Project
|
||||
nsslapd-pluginDescription: Enforce unique attribute values
|
||||
uniqueness-subtrees: $SUFFIX
|
||||
uniqueness-exclude-subtrees: cn=staged users,cn=accounts,cn=provisioning,$SUFFIX
|
||||
uniqueness-across-all-subtrees: on
|
||||
|
||||
dn: cn=krbCanonicalName uniqueness,cn=plugins,cn=config
|
||||
changetype: add
|
||||
objectClass: top
|
||||
objectClass: nsSlapdPlugin
|
||||
objectClass: extensibleObject
|
||||
cn: krbCanonicalName uniqueness
|
||||
nsslapd-pluginPath: libattr-unique-plugin
|
||||
nsslapd-pluginInitfunc: NSUniqueAttr_Init
|
||||
nsslapd-pluginType: preoperation
|
||||
nsslapd-pluginEnabled: on
|
||||
uniqueness-attribute-name: krbCanonicalName
|
||||
uniqueness-attribute-name: krbPrincipalName:CaseIgnoreMatch:
|
||||
uniqueness-attribute-name: krbPrincipalAlias:CaseIgnoreMatch:
|
||||
uniqueness-attribute-name: krbCanonicalName:CaseIgnoreMatch:
|
||||
nsslapd-plugin-depends-on-type: database
|
||||
nsslapd-pluginId: NSUniqueAttr
|
||||
nsslapd-pluginVersion: 1.1.0
|
||||
|
||||
@@ -39,6 +39,7 @@ dist_noinst_DATA = \
|
||||
ipa-pki-wait-running.in \
|
||||
ipa-acme-manage.in \
|
||||
ipa-subids.in \
|
||||
ipa-migrate.in \
|
||||
$(NULL)
|
||||
|
||||
nodist_sbin_SCRIPTS = \
|
||||
@@ -68,6 +69,7 @@ nodist_sbin_SCRIPTS = \
|
||||
ipa-crlgen-manage \
|
||||
ipa-cert-fix \
|
||||
ipa-acme-manage \
|
||||
ipa-migrate \
|
||||
$(NULL)
|
||||
|
||||
appdir = $(libexecdir)/ipa/
|
||||
|
||||
@@ -29,19 +29,17 @@ import sys
|
||||
|
||||
import six
|
||||
|
||||
from optparse import SUPPRESS_HELP # pylint: disable=deprecated-module
|
||||
|
||||
from ipalib.install import sysrestore
|
||||
from ipaserver.install import adtrust, service
|
||||
from ipaserver.install.installutils import (
|
||||
read_password,
|
||||
check_server_configuration,
|
||||
run_script)
|
||||
from ipapython.admintool import ScriptError
|
||||
from ipapython.admintool import ScriptError, admin_cleanup_global_argv
|
||||
from ipapython import version
|
||||
from ipapython import ipautil
|
||||
from ipalib import api, errors, krb_utils
|
||||
from ipapython.config import IPAOptionParser
|
||||
from ipapython.config import IPAOptionParser, SUPPRESS_HELP
|
||||
from ipaplatform.paths import paths
|
||||
from ipapython.ipa_log_manager import standard_logging_setup
|
||||
|
||||
@@ -95,6 +93,7 @@ def parse_options():
|
||||
|
||||
options, _args = parser.parse_args()
|
||||
safe_options = parser.get_safe_opts(options)
|
||||
admin_cleanup_global_argv(parser, options, sys.argv)
|
||||
|
||||
return safe_options, options
|
||||
|
||||
|
||||
@@ -42,6 +42,7 @@ from ipalib.constants import DOMAIN_LEVEL_1
|
||||
from ipapython.config import IPAOptionParser
|
||||
from ipapython.ipa_log_manager import standard_logging_setup
|
||||
from ipaplatform.paths import paths
|
||||
from ipapython.admintool import admin_cleanup_global_argv
|
||||
|
||||
logger = logging.getLogger(os.path.basename(__file__))
|
||||
|
||||
@@ -101,6 +102,26 @@ def parse_options():
|
||||
"The CA certificate subject DN "
|
||||
"(default CN=Certificate Authority,O=<realm-name>). "
|
||||
"RDNs are in LDAP order (most specific RDN first)."))
|
||||
parser.add_option("--token-name", dest="token_name",
|
||||
default=None,
|
||||
help=(
|
||||
"The PKCS#11 token name if using an HSM to store "
|
||||
"and generate private keys."))
|
||||
parser.add_option("--token-library-path", dest="token_library_path",
|
||||
default=None,
|
||||
help=(
|
||||
"The full path to the PKCS#11 shared library "
|
||||
"needed to access the HSM device."))
|
||||
parser.add_option("--token-password", dest="token_password",
|
||||
default=None,
|
||||
sensitive=True,
|
||||
help=(
|
||||
"The PKCS#11 token password for the HSM."))
|
||||
parser.add_option("--token-password-file", dest="token_password_file",
|
||||
default=None,
|
||||
help=(
|
||||
"The full path to a file containing the PKCS#11 "
|
||||
" token password."))
|
||||
|
||||
parser.add_option("--pki-config-override", dest="pki_config_override",
|
||||
default=None,
|
||||
@@ -112,6 +133,7 @@ def parse_options():
|
||||
|
||||
options, args = parser.parse_args()
|
||||
safe_options = parser.get_safe_opts(options)
|
||||
admin_cleanup_global_argv(parser, options, sys.argv)
|
||||
|
||||
if args:
|
||||
parser.error("Too many arguments provided")
|
||||
@@ -166,7 +188,9 @@ def install_replica(safe_options, options):
|
||||
# Run ipa-certupdate to ensure we have the CA cert. This is
|
||||
# necessary if the admin has just promoted the topology from
|
||||
# CA-less to CA-ful, and ipa-certupdate has not been run yet.
|
||||
print("Running ipa-certupdate...", end="", flush=True)
|
||||
ipa_certupdate.run_with_args(api)
|
||||
print("done")
|
||||
|
||||
# CertUpdate restarts DS causing broken pipe on the original
|
||||
# connection, so reconnect the backend.
|
||||
|
||||
@@ -24,13 +24,13 @@ from __future__ import print_function
|
||||
import sys
|
||||
from ipaplatform.paths import paths
|
||||
try:
|
||||
from optparse import OptionParser # pylint: disable=deprecated-module
|
||||
from ipapython import ipautil, config
|
||||
from ipaserver.install import installutils
|
||||
from ipaserver.install.ldapupdate import LDAPUpdate
|
||||
from ipalib import api, errors
|
||||
from ipapython.ipa_log_manager import standard_logging_setup
|
||||
from ipapython.dn import DN
|
||||
from ipapython.admintool import admin_cleanup_global_argv
|
||||
except ImportError as e:
|
||||
print("""\
|
||||
There was a problem importing one of the required Python modules. The
|
||||
@@ -46,7 +46,8 @@ nis_config_dn = DN(('cn', 'NIS Server'), ('cn', 'plugins'), ('cn', 'config'))
|
||||
def parse_options():
|
||||
usage = "%prog [options] <enable|disable|status>\n"
|
||||
usage += "%prog [options]\n"
|
||||
parser = OptionParser(usage=usage, formatter=config.IPAFormatter())
|
||||
parser = config.IPAOptionParser(usage=usage,
|
||||
formatter=config.IPAFormatter())
|
||||
|
||||
parser.add_option("-d", "--debug", action="store_true", dest="debug",
|
||||
help="Display debugging information about the update(s)")
|
||||
@@ -55,6 +56,7 @@ def parse_options():
|
||||
|
||||
config.add_standard_options(parser)
|
||||
options, args = parser.parse_args()
|
||||
admin_cleanup_global_argv(parser, options, sys.argv)
|
||||
|
||||
return options, args
|
||||
|
||||
|
||||
@@ -32,8 +32,8 @@ from ipaserver.install import (replication, installutils, bindinstance,
|
||||
from ipalib import api, errors
|
||||
from ipalib.constants import FQDN
|
||||
from ipalib.util import has_managed_topology, print_replication_status
|
||||
from ipapython import ipautil, ipaldap, version
|
||||
from ipapython.admintool import ScriptError
|
||||
from ipapython import ipautil, ipaldap, version, config
|
||||
from ipapython.admintool import admin_cleanup_global_argv, ScriptError
|
||||
from ipapython.dn import DN
|
||||
|
||||
logger = logging.getLogger(os.path.basename(__file__))
|
||||
@@ -54,11 +54,10 @@ commands = {
|
||||
|
||||
|
||||
def parse_options():
|
||||
from optparse import OptionParser # pylint: disable=deprecated-module
|
||||
|
||||
parser = OptionParser(version=version.VERSION)
|
||||
parser = config.IPAOptionParser(version=version.VERSION)
|
||||
parser.add_option("-H", "--host", dest="host", help="starting host")
|
||||
parser.add_option("-p", "--password", dest="dirman_passwd", help="Directory Manager password")
|
||||
parser.add_option("-p", "--password", dest="dirman_passwd", sensitive=True,
|
||||
help="Directory Manager password")
|
||||
parser.add_option("-v", "--verbose", dest="verbose", action="store_true", default=False,
|
||||
help="provide additional information")
|
||||
parser.add_option("-f", "--force", dest="force", action="store_true", default=False,
|
||||
@@ -66,6 +65,7 @@ def parse_options():
|
||||
parser.add_option("--from", dest="fromhost", help="Host to get data from")
|
||||
|
||||
options, args = parser.parse_args()
|
||||
admin_cleanup_global_argv(parser, options, sys.argv)
|
||||
|
||||
valid_syntax = False
|
||||
|
||||
|
||||
@@ -192,10 +192,10 @@ class IPACustodiaTester:
|
||||
usage, IPA_CUSTODIA_KEYFILE
|
||||
))
|
||||
|
||||
if pkey.key_id != self.host_spn:
|
||||
if pkey.get('kid') != self.host_spn:
|
||||
raise self.error( # pylint: disable=raising-bad-type, #4772
|
||||
"KID '{}' != host service principal name '{}' "
|
||||
"(usage: {})".format(pkey.key_id, self.host_spn, usage),
|
||||
"(usage: {})".format(pkey.get('kid'), self.host_spn, usage),
|
||||
fatal=True
|
||||
)
|
||||
else:
|
||||
|
||||
@@ -24,7 +24,6 @@ import logging
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
from optparse import OptionParser # pylint: disable=deprecated-module
|
||||
|
||||
from ipaplatform.paths import paths
|
||||
from ipapython import config
|
||||
@@ -32,6 +31,7 @@ from ipaserver.install import installutils
|
||||
from ipalib import api, errors
|
||||
from ipapython.ipa_log_manager import standard_logging_setup
|
||||
from ipapython.dn import DN
|
||||
from ipapython.admintool import admin_cleanup_global_argv
|
||||
|
||||
logger = logging.getLogger(os.path.basename(__file__))
|
||||
|
||||
@@ -39,7 +39,8 @@ logger = logging.getLogger(os.path.basename(__file__))
|
||||
def parse_options():
|
||||
usage = "%prog [options] <status|enable|disable>\n"
|
||||
usage += "%prog [options]\n"
|
||||
parser = OptionParser(usage=usage, formatter=config.IPAFormatter())
|
||||
parser = config.IPAOptionParser(usage=usage,
|
||||
formatter=config.IPAFormatter())
|
||||
|
||||
parser.add_option("-d", "--debug", action="store_true", dest="debug",
|
||||
help="Display debugging information about the update(s)")
|
||||
@@ -50,9 +51,10 @@ def parse_options():
|
||||
action="store_true",
|
||||
help="List available Managed Entries")
|
||||
parser.add_option("-p", "--password", dest="dirman_password",
|
||||
help="Directory Manager password")
|
||||
sensitive=True, help="Directory Manager password")
|
||||
|
||||
options, args = parser.parse_args()
|
||||
admin_cleanup_global_argv(parser, options, sys.argv)
|
||||
|
||||
return options, args
|
||||
|
||||
|
||||
Executable
+10
@@ -0,0 +1,10 @@
|
||||
#!/usr/bin/python3
|
||||
#
|
||||
# Copyright (C) 2023 FreeIPA Contributors see COPYING for license
|
||||
#
|
||||
# PYTHON_ARGCOMPLETE_OK
|
||||
|
||||
from ipaserver.install.ipa_migrate import IPAMigrate
|
||||
|
||||
ipa_migrate = IPAMigrate()
|
||||
ipa_migrate.run()
|
||||
@@ -23,15 +23,15 @@ from __future__ import print_function
|
||||
import logging
|
||||
|
||||
from ipapython import ipachangeconf
|
||||
from ipapython.config import IPAOptionParser
|
||||
from ipapython.config import (IPAOptionParser, OptionGroup,
|
||||
OptionValueError)
|
||||
from ipapython.admintool import admin_cleanup_global_argv
|
||||
from ipapython.dn import DN
|
||||
from ipapython import version
|
||||
from ipapython import ipautil, certdb
|
||||
from ipalib import api, errors, x509
|
||||
from ipalib.constants import FQDN
|
||||
from ipaserver.install import installutils
|
||||
# pylint: disable=deprecated-module
|
||||
from optparse import OptionGroup, OptionValueError
|
||||
# pylint: enable=deprecated-module
|
||||
from ipapython.ipa_log_manager import standard_logging_setup
|
||||
import copy
|
||||
@@ -189,6 +189,7 @@ def parse_options():
|
||||
|
||||
options, _args = parser.parse_args()
|
||||
safe_options = parser.get_safe_opts(options)
|
||||
admin_cleanup_global_argv(parser, options, sys.argv)
|
||||
|
||||
if options.master and options.replica:
|
||||
parser.error("on-master and on-replica options are mutually exclusive!")
|
||||
|
||||
@@ -26,7 +26,6 @@ import os
|
||||
import re
|
||||
import socket
|
||||
import traceback
|
||||
from urllib.parse import urlparse
|
||||
from xmlrpc.client import MAXINT
|
||||
|
||||
import ldap
|
||||
@@ -44,6 +43,7 @@ from ipalib.util import (
|
||||
print_replication_status,
|
||||
verify_host_resolvable,
|
||||
)
|
||||
from ipapython.admintool import admin_cleanup_global_argv
|
||||
from ipapython.ipa_log_manager import standard_logging_setup
|
||||
from ipapython.dn import DN
|
||||
from ipapython.config import IPAOptionParser
|
||||
@@ -85,7 +85,8 @@ class NoRUVsFound(Exception):
|
||||
def parse_options():
|
||||
parser = IPAOptionParser(version=version.VERSION)
|
||||
parser.add_option("-H", "--host", dest="host", help="starting host")
|
||||
parser.add_option("-p", "--password", dest="dirman_passwd", help="Directory Manager password")
|
||||
parser.add_option("-p", "--password", dest="dirman_passwd", sensitive=True,
|
||||
help="Directory Manager password")
|
||||
parser.add_option("-v", "--verbose", dest="verbose", action="store_true", default=False,
|
||||
help="provide additional information")
|
||||
parser.add_option("-d", "--debug", dest="debug", action="store_true", default=False,
|
||||
@@ -96,7 +97,7 @@ def parse_options():
|
||||
help="DANGER: clean up references to a ghost master")
|
||||
parser.add_option("--binddn", dest="binddn", default=None, type="dn",
|
||||
help="Bind DN to use with remote server")
|
||||
parser.add_option("--bindpw", dest="bindpw", default=None,
|
||||
parser.add_option("--bindpw", dest="bindpw", default=None, sensitive=True,
|
||||
help="Password for Bind DN to use with remote server")
|
||||
parser.add_option("--winsync", dest="winsync", action="store_true", default=False,
|
||||
help="This is a Windows Sync Agreement")
|
||||
@@ -104,13 +105,15 @@ def parse_options():
|
||||
help="Full path and filename of CA certificate to use with TLS/SSL to the remote server")
|
||||
parser.add_option("--win-subtree", dest="win_subtree", default=None,
|
||||
help="DN of Windows subtree containing the users you want to sync (default cn=Users,<domain suffix)")
|
||||
parser.add_option("--passsync", dest="passsync", default=None,
|
||||
parser.add_option("--passsync", dest="passsync",
|
||||
default=None, sensitive=True,
|
||||
help="Password for the IPA system user used by the Windows PassSync plugin to synchronize passwords")
|
||||
parser.add_option("--from", dest="fromhost", help="Host to get data from")
|
||||
parser.add_option("--no-lookup", dest="nolookup", action="store_true", default=False,
|
||||
help="do not perform DNS lookup checks")
|
||||
|
||||
options, args = parser.parse_args()
|
||||
admin_cleanup_global_argv(parser, options, sys.argv)
|
||||
|
||||
valid_syntax = False
|
||||
|
||||
@@ -358,9 +361,14 @@ def del_link(realm, replica1, replica2, dirman_passwd, force=False):
|
||||
|
||||
return True
|
||||
|
||||
def get_ruv(realm, host, dirman_passwd, nolookup=False, ca=False):
|
||||
|
||||
def get_ruv(realm, host, dirman_passwd, nolookup=False, ca=False,
|
||||
strict=True):
|
||||
"""
|
||||
Return the RUV entries as a list of tuples: (hostname, rid)
|
||||
|
||||
If strict is True then the RUV must contain the ldap url, otherwise it is
|
||||
ok to proceed with just the rid
|
||||
"""
|
||||
|
||||
if not nolookup:
|
||||
@@ -371,10 +379,9 @@ def get_ruv(realm, host, dirman_passwd, nolookup=False, ca=False):
|
||||
thisrepl = replication.get_cs_replication_manager(realm, host, dirman_passwd)
|
||||
else:
|
||||
thisrepl = replication.ReplicationManager(realm, host, dirman_passwd)
|
||||
except Exception as e:
|
||||
except Exception as ex:
|
||||
logger.debug("%s", traceback.format_exc())
|
||||
raise RuntimeError("Failed to connect to server {host}: {err}"
|
||||
.format(host=host, err=e))
|
||||
raise RuntimeError(f"Failed to connect to server {host}: {ex}")
|
||||
|
||||
search_filter = '(&(nsuniqueid=ffffffff-ffffffff-ffffffff-ffffffff)(objectclass=nstombstone))'
|
||||
try:
|
||||
@@ -386,24 +393,42 @@ def get_ruv(realm, host, dirman_passwd, nolookup=False, ca=False):
|
||||
raise NoRUVsFound("No RUV records found.")
|
||||
|
||||
servers = []
|
||||
for e in entries:
|
||||
for ruv in e['nsds50ruv']:
|
||||
for entry in entries:
|
||||
for ruv in entry['nsds50ruv']:
|
||||
if ruv.startswith('{replicageneration'):
|
||||
continue
|
||||
data = re.match('\{replica (\d+) (ldap://.*:\d+)\}(\s+\w+\s+\w*){0,1}', ruv)
|
||||
if data:
|
||||
rid = data.group(1)
|
||||
(
|
||||
_scheme, netloc, _path, _params, _query, _fragment
|
||||
) = urlparse(data.group(2))
|
||||
servers.append((netloc, rid))
|
||||
|
||||
# Get the RID, this is required in all cases
|
||||
rid_data = re.match(
|
||||
r'\{replica (\d+)',
|
||||
ruv
|
||||
)
|
||||
if rid_data:
|
||||
rid = rid_data.group(1)
|
||||
else:
|
||||
print("unable to decode: %s" % ruv)
|
||||
print(f"unable to decode: {ruv} --> missing replica ID")
|
||||
continue
|
||||
|
||||
# Attempt to extract ldap url from ruv (it's not always present)
|
||||
netloc = "unknown host"
|
||||
host_data = re.match(
|
||||
r'(\{\w+\s+\d+\s+)ldap://(.+:\d+)',
|
||||
ruv
|
||||
)
|
||||
if host_data:
|
||||
netloc = host_data.group(2)
|
||||
elif strict:
|
||||
print(f"unable to decode: {ruv} --> missing LDAP url")
|
||||
continue
|
||||
|
||||
# Ok update server list
|
||||
servers.append((netloc, rid))
|
||||
|
||||
return servers
|
||||
|
||||
|
||||
def get_ruv_both_suffixes(realm, host, dirman_passwd, verbose, nolookup=False):
|
||||
def get_ruv_both_suffixes(realm, host, dirman_passwd, verbose, nolookup=False,
|
||||
strict=True):
|
||||
"""
|
||||
Get RUVs for both domain and ipaca suffixes
|
||||
"""
|
||||
@@ -411,19 +436,20 @@ def get_ruv_both_suffixes(realm, host, dirman_passwd, verbose, nolookup=False):
|
||||
fail_gracefully = True
|
||||
|
||||
try:
|
||||
ruvs['ca'] = get_ruv(realm, host, dirman_passwd, nolookup, True)
|
||||
ruvs['ca'] = get_ruv(realm, host, dirman_passwd, nolookup, True,
|
||||
strict)
|
||||
except (NoRUVsFound, RuntimeError) as e:
|
||||
err = "Failed to get CS-RUVs from {host}: {err}".format(host=host,
|
||||
err=e)
|
||||
err = f"Failed to get CS-RUVs from {host}: {e}"
|
||||
if isinstance(e, RuntimeError):
|
||||
fail_gracefully = False
|
||||
if verbose:
|
||||
print(err)
|
||||
logger.debug('%s', err)
|
||||
try:
|
||||
ruvs['domain'] = get_ruv(realm, host, dirman_passwd, nolookup)
|
||||
ruvs['domain'] = get_ruv(realm, host, dirman_passwd, nolookup, False,
|
||||
strict)
|
||||
except (NoRUVsFound, RuntimeError) as e:
|
||||
err = "Failed to get RUVs from {host}: {err}".format(host=host, err=e)
|
||||
err = f"Failed to get RUVs from {host}: {e}"
|
||||
if isinstance(e, RuntimeError):
|
||||
if not fail_gracefully:
|
||||
raise
|
||||
@@ -495,7 +521,8 @@ def clean_ruv(realm, ruv, options):
|
||||
servers = get_ruv_both_suffixes(realm, options.host,
|
||||
options.dirman_passwd,
|
||||
options.verbose,
|
||||
options.nolookup)
|
||||
options.nolookup,
|
||||
strict=False)
|
||||
except (NoRUVsFound, RuntimeError) as e:
|
||||
print(e)
|
||||
sys.exit(0 if isinstance(e, NoRUVsFound) else 1)
|
||||
@@ -551,7 +578,8 @@ def abort_clean_ruv(realm, ruv, options):
|
||||
servers = get_ruv_both_suffixes(realm, options.host,
|
||||
options.dirman_passwd,
|
||||
options.verbose,
|
||||
options.nolookup)
|
||||
options.nolookup,
|
||||
strict=False)
|
||||
except (NoRUVsFound, RuntimeError) as e:
|
||||
print(e)
|
||||
sys.exit(0 if isinstance(e, NoRUVsFound) else 1)
|
||||
@@ -710,7 +738,8 @@ def clean_dangling_ruvs(realm, host, options):
|
||||
ruv_dict = get_ruv_both_suffixes(realm, master_cn,
|
||||
options.dirman_passwd,
|
||||
options.verbose,
|
||||
options.nolookup)
|
||||
options.nolookup,
|
||||
strict=False)
|
||||
except (RuntimeError, NoRUVsFound):
|
||||
continue
|
||||
|
||||
@@ -718,12 +747,12 @@ def clean_dangling_ruvs(realm, host, options):
|
||||
# This needs needs to be split off
|
||||
if ruv_dict.get('domain'):
|
||||
master_info['ruvs'] = {
|
||||
(re.sub(':\d+', '', x), y)
|
||||
(re.sub(r':\d+', '', x), y)
|
||||
for (x, y) in ruv_dict['domain']
|
||||
}
|
||||
if ruv_dict.get('ca'):
|
||||
master_info['csruvs'] = {
|
||||
(re.sub(':\d+', '', x), y)
|
||||
(re.sub(r':\d+', '', x), y)
|
||||
for (x, y) in ruv_dict['ca']
|
||||
}
|
||||
except Exception as e:
|
||||
@@ -1237,12 +1266,12 @@ def force_sync(realm, thishost, fromhost, dirman_passwd, nolookup=False):
|
||||
repl.force_sync(repl.conn, fromhost)
|
||||
else:
|
||||
ds = dsinstance.DsInstance(realm_name=realm)
|
||||
ds.replica_manage_time_skew(prevent=False)
|
||||
ds.replica_ignore_initial_time_skew()
|
||||
repl = replication.ReplicationManager(realm, fromhost, dirman_passwd)
|
||||
repl.force_sync(repl.conn, thishost)
|
||||
agreement = repl.get_replication_agreement(thishost)
|
||||
repl.wait_for_repl_update(repl.conn, agreement.dn)
|
||||
ds.replica_manage_time_skew(prevent=True)
|
||||
ds.replica_revert_time_skew()
|
||||
|
||||
def show_DNA_ranges(hostname, master, realm, dirman_passwd, nextrange=False,
|
||||
nolookup=False):
|
||||
|
||||
@@ -30,7 +30,8 @@ dist_man1_MANS = \
|
||||
ipa-crlgen-manage.1 \
|
||||
ipa-cert-fix.1 \
|
||||
ipa-acme-manage.1 \
|
||||
$(NULL)
|
||||
ipa-migrate.1 \
|
||||
$(NULL)
|
||||
|
||||
dist_man8_MANS = \
|
||||
ipactl.8 \
|
||||
|
||||
@@ -27,7 +27,7 @@ Provides customized advice for various IPA configuration issues.
|
||||
For the list of possible ADVICEs available, run the ipa\-advise with no arguments.
|
||||
.SH "OPTIONS"
|
||||
.TP
|
||||
\fB\-\-v\fR, \fB\-\-verbose\fR
|
||||
\fB\-v\fR, \fB\-\-verbose\fR
|
||||
Print debugging information
|
||||
.TP
|
||||
\fB\-d\fR, \fB\-\-debug\fR
|
||||
|
||||
@@ -54,7 +54,7 @@ Perform the backup on\-line. Requires the \-\-data option.
|
||||
\fB\-\-disable\-role\-check\fR
|
||||
Perform the backup even if this host does not have all the roles in use in the cluster. This is not recommended.
|
||||
.TP
|
||||
\fB\-\-v\fR, \fB\-\-verbose\fR
|
||||
\fB\-v\fR, \fB\-\-verbose\fR
|
||||
Print debugging information
|
||||
.TP
|
||||
\fB\-d\fR, \fB\-\-debug\fR
|
||||
|
||||
@@ -85,6 +85,18 @@ Do not use DNS for hostname lookup during installation
|
||||
\fB\-\-random\-serial\-numbers\fR
|
||||
Enable Random Serial Numbers. Random serial numbers cannot be used in a mixed environment. Either all CA's have it enabled or none do.
|
||||
.TP
|
||||
\fB\-\-token\-name\fR=\fITOKEN_NAME\fR
|
||||
The PKCS#11 token name if using an HSM to store and generate private keys.
|
||||
.TP
|
||||
\fB\-\-token\-library\-path\fR=\fITOKEN_LIBRARY_PATH\fR
|
||||
The full path to the PKCS#11 shared library needed to access the HSM device.
|
||||
.TP
|
||||
\fB\-\-token\-password\fR=\fITOKEN_PASSWORD\fR
|
||||
The PKCS#11 token password for the HSM.
|
||||
.TP
|
||||
\fB\-\-token\-password\-file\fR=\fITOKEN_PASSWORD_FILE\fR
|
||||
The full path to a file containing the PKCS#11 token password.
|
||||
.TP
|
||||
\fB\-\-skip\-conncheck\fR
|
||||
Skip connection check to remote master
|
||||
.TP
|
||||
|
||||
Loaded 100 of 351 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user