1 # Unix SMB/CIFS implementation.
2 # Copyright © Catalyst IT 2023
4 # This program is free software; you can redistribute it and/or modify
5 # it under the terms of the GNU General Public License as published by
6 # the Free Software Foundation; either version 3 of the License, or
7 # (at your option) any later version.
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
14 # You should have received a copy of the GNU General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
17 """An API for creating arbitrary security tokens."""
20 from samba
.dcerpc
import security
27 security
.dom_sid
: 0x0005,
33 def list_to_claim(k
, v
, case_sensitive
=False):
34 if isinstance(v
, security
.CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1
):
38 if isinstance(v
, (str, int)):
40 if not isinstance(v
, list):
41 raise TypeError(f
"expected list of claim values for '{k}', "
42 f
"not {v!r} of type {type(v)}")
44 c
= security
.CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1()
48 c
.value_type
= CLAIM_VAL_TYPES
[t
]
50 if type(val
) is not t
:
51 raise TypeError(f
"claim values for '{k}' "
52 "should all be the same type")
54 # pick an arbitrary type
55 c
.value_type
= CLAIM_VAL_TYPES
['uint']
58 c
.value_count
= len(v
)
60 c
.flags |
= security
.CLAIM_SECURITY_ATTRIBUTE_VALUE_CASE_SENSITIVE
62 # The claims made here will not have the
63 # CLAIM_SECURITY_ATTRIBUTE_UNIQUE_AND_SORTED flag set, which makes
64 # them like resource attribute claims rather than real wire
65 # claims. It shouldn't matter much, as they will just be sorted
66 # and checked as if they were resource attribute claims.
70 def _normalise_claims(args
):
71 if isinstance(args
, security
.CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1
):
74 if args
is None or len(args
) == 0:
77 if isinstance(args
, list):
79 if not isinstance(x
, security
.CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1
):
80 raise TypeError(f
"list should be of claims, not '{type(x)}'")
85 if isinstance(args
, dict):
86 # the key is the name and the value is a list of claim values
87 for k
, v
in args
.items():
88 c
= list_to_claim(k
, v
)
96 # These are a subset of two letter aliases that don't need a
97 # domain SID or other magic. (c.f. sid_strings test).
98 'AA': security
.SID_BUILTIN_ACCESS_CONTROL_ASSISTANCE_OPS
, # S-1-5-32-579
99 'AC': security
.SID_SECURITY_BUILTIN_PACKAGE_ANY_PACKAGE
, # S-1-15-2-1
100 'AN': security
.SID_NT_ANONYMOUS
, # S-1-5-7
101 'AO': security
.SID_BUILTIN_ACCOUNT_OPERATORS
, # S-1-5-32-548
102 'AS': security
.SID_AUTHENTICATION_AUTHORITY_ASSERTED_IDENTITY
, # S-1-18-1
103 'AU': security
.SID_NT_AUTHENTICATED_USERS
, # S-1-5-11
104 'BA': security
.SID_BUILTIN_ADMINISTRATORS
, # S-1-5-32-544
105 'BG': security
.SID_BUILTIN_GUESTS
, # S-1-5-32-546
106 'BO': security
.SID_BUILTIN_BACKUP_OPERATORS
, # S-1-5-32-551
107 'BU': security
.SID_BUILTIN_USERS
, # S-1-5-32-545
108 'CD': security
.SID_BUILTIN_CERT_SERV_DCOM_ACCESS
, # S-1-5-32-574
109 'CG': security
.SID_CREATOR_GROUP
, # S-1-3-1
110 'CO': security
.SID_CREATOR_OWNER
, # S-1-3-0
111 'CY': security
.SID_BUILTIN_CRYPTO_OPERATORS
, # S-1-5-32-569
112 'ED': security
.SID_NT_ENTERPRISE_DCS
, # S-1-5-9
113 'ER': security
.SID_BUILTIN_EVENT_LOG_READERS
, # S-1-5-32-573
114 'ES': security
.SID_BUILTIN_RDS_ENDPOINT_SERVERS
, # S-1-5-32-576
115 'HA': security
.SID_BUILTIN_HYPER_V_ADMINS
, # S-1-5-32-578
116 'HI': security
.SID_SECURITY_MANDATORY_HIGH
, # S-1-16-12288
117 'IS': security
.SID_BUILTIN_IUSERS
, # S-1-5-32-568
118 'IU': security
.SID_NT_INTERACTIVE
, # S-1-5-4
119 'LS': security
.SID_NT_LOCAL_SERVICE
, # S-1-5-19
120 'LU': security
.SID_BUILTIN_PERFLOG_USERS
, # S-1-5-32-559
121 'LW': security
.SID_SECURITY_MANDATORY_LOW
, # S-1-16-4096
122 'ME': security
.SID_SECURITY_MANDATORY_MEDIUM
, # S-1-16-8192
123 'MP': security
.SID_SECURITY_MANDATORY_MEDIUM_PLUS
, # S-1-16-8448
124 'MS': security
.SID_BUILTIN_RDS_MANAGEMENT_SERVERS
, # S-1-5-32-577
125 'MU': security
.SID_BUILTIN_PERFMON_USERS
, # S-1-5-32-558
126 'NO': security
.SID_BUILTIN_NETWORK_CONF_OPERATORS
, # S-1-5-32-556
127 'NS': security
.SID_NT_NETWORK_SERVICE
, # S-1-5-20
128 'NU': security
.SID_NT_NETWORK
, # S-1-5-2
129 'OW': security
.SID_OWNER_RIGHTS
, # S-1-3-4
130 'PO': security
.SID_BUILTIN_PRINT_OPERATORS
, # S-1-5-32-550
131 'PS': security
.SID_NT_SELF
, # S-1-5-10
132 'PU': security
.SID_BUILTIN_POWER_USERS
, # S-1-5-32-547
133 'RA': security
.SID_BUILTIN_RDS_REMOTE_ACCESS_SERVERS
, # S-1-5-32-575
134 'RC': security
.SID_NT_RESTRICTED
, # S-1-5-12
135 'RD': security
.SID_BUILTIN_REMOTE_DESKTOP_USERS
, # S-1-5-32-555
136 'RE': security
.SID_BUILTIN_REPLICATOR
, # S-1-5-32-552
137 'RM': security
.SID_BUILTIN_REMOTE_MANAGEMENT_USERS
, # S-1-5-32-580
138 'RU': security
.SID_BUILTIN_PREW2K
, # S-1-5-32-554
139 'SI': security
.SID_SECURITY_MANDATORY_SYSTEM
, # S-1-16-16384
140 'SO': security
.SID_BUILTIN_SERVER_OPERATORS
, # S-1-5-32-549
141 'SS': security
.SID_SERVICE_ASSERTED_IDENTITY
, # S-1-18-2
142 'SU': security
.SID_NT_SERVICE
, # S-1-5-6
143 'SY': security
.SID_NT_SYSTEM
, # S-1-5-18
144 'WD': security
.SID_WORLD
, # S-1-1-0
145 'WR': security
.SID_SECURITY_RESTRICTED_CODE
, # S-1-5-33
149 return security
.dom_sid(s
)
152 def _normalise_sids(args
):
153 if isinstance(args
, security
.dom_sid
):
155 if isinstance(args
, str):
156 return [str_to_sid(args
)]
158 if not isinstance(args
, list):
159 raise TypeError("expected a SID, sid string, or list of SIDs, "
160 f
"not'{type(args)}'")
164 if isinstance(s
, str):
166 elif not isinstance(s
, security
.dom_sid
):
167 raise TypeError(f
"expected a SID, not'{type(s)}'")
173 def _normalise_mask(mask
, mask_type
):
174 if isinstance(mask
, int):
177 if not isinstance(mask
, list):
178 raise TypeError("expected int mask or list of flags")
180 if mask_type
== 'privileges':
183 elif mask_type
== 'rights':
184 prefix
= 'LSA_POLICY_MODE_'
187 raise ValueError(f
"unknown mask_type value: {mask_type}")
192 if isinstance(x
, str) and x
.startswith(prefix
):
193 if not x
.endswith(tail
):
194 # we don't want security.SEC_PRIV_SHUTDOWN (19),
195 # we want security.SEC_PRIV_SHUTDOWN_BIT (1 << 20)
196 # but you can write "SEC_PRIV_SHUTDOWN"
198 x
= getattr(security
, x
)
204 def token(sids
=None, **kwargs
):
205 """Return a security token with the specified attributes.
207 The security.token API is annoying and fragile; here we wrap it in
210 In general the arguments can either be objects of the correct
211 type, or Python strings or structures that clearly convert to that
212 type. For example, there two are equivalent:
214 >>> t = token([security.dom_sid("S-1-2")])
215 >>> t = token(["S-1-2"])
217 To add claims and device SIDs you do something like this:
219 >>> t = token(["AA", "WD"],
221 user_claims={"Title": ["PM"],
222 "ClearanceLevel": [1]}
225 claims_kws
= ['device_claims',
229 sid_kws
= ['sids', 'device_sids']
231 mask_kws
= ['privileges',
235 kwargs
['sids'] = sids
239 for k
, v
in kwargs
.items():
241 norm_args
[k
] = _normalise_claims(v
)
243 norm_args
[k
] = _normalise_mask(v
, k
)
245 norm_args
[k
] = _normalise_sids(v
)
247 raise TypeError(f
"{k} is an invalid keyword argument")
249 t
= security
.token(evaluate_claims
=security
.CLAIMS_EVALUATION_ALWAYS
)
251 for k
, v
in norm_args
.items():
253 if isinstance(v
, list):
254 setattr(t
, 'num_' + k
, len(v
))