1 | /*
|
---|
2 | Unix SMB/CIFS implementation.
|
---|
3 |
|
---|
4 | Database Glue between Samba and the KDC
|
---|
5 |
|
---|
6 | Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005-2009
|
---|
7 | Copyright (C) Simo Sorce <idra@samba.org> 2010
|
---|
8 |
|
---|
9 | This program is free software; you can redistribute it and/or modify
|
---|
10 | it under the terms of the GNU General Public License as published by
|
---|
11 | the Free Software Foundation; either version 3 of the License, or
|
---|
12 | (at your option) any later version.
|
---|
13 |
|
---|
14 | This program is distributed in the hope that it will be useful,
|
---|
15 | but WITHOUT ANY WARRANTY; without even the implied warranty of
|
---|
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
---|
17 | GNU General Public License for more details.
|
---|
18 |
|
---|
19 |
|
---|
20 | You should have received a copy of the GNU General Public License
|
---|
21 | along with this program. If not, see <http://www.gnu.org/licenses/>.
|
---|
22 | */
|
---|
23 |
|
---|
24 | #include "includes.h"
|
---|
25 | #include "libcli/security/security.h"
|
---|
26 | #include "auth/auth.h"
|
---|
27 | #include "auth/auth_sam.h"
|
---|
28 | #include "dsdb/samdb/samdb.h"
|
---|
29 | #include "dsdb/common/util.h"
|
---|
30 | #include "librpc/gen_ndr/ndr_drsblobs.h"
|
---|
31 | #include "param/param.h"
|
---|
32 | #include "../lib/crypto/md4.h"
|
---|
33 | #include "system/kerberos.h"
|
---|
34 | #include "auth/kerberos/kerberos.h"
|
---|
35 | #include "kdc/sdb.h"
|
---|
36 | #include "kdc/samba_kdc.h"
|
---|
37 | #include "kdc/db-glue.h"
|
---|
38 |
|
---|
39 | #define SAMBA_KVNO_GET_KRBTGT(kvno) \
|
---|
40 | ((uint16_t)(((uint32_t)kvno) >> 16))
|
---|
41 |
|
---|
42 | #define SAMBA_KVNO_AND_KRBTGT(kvno, krbtgt) \
|
---|
43 | ((krb5_kvno)((((uint32_t)kvno) & 0xFFFF) | \
|
---|
44 | ((((uint32_t)krbtgt) << 16) & 0xFFFF0000)))
|
---|
45 |
|
---|
46 | enum samba_kdc_ent_type
|
---|
47 | { SAMBA_KDC_ENT_TYPE_CLIENT, SAMBA_KDC_ENT_TYPE_SERVER,
|
---|
48 | SAMBA_KDC_ENT_TYPE_KRBTGT, SAMBA_KDC_ENT_TYPE_TRUST, SAMBA_KDC_ENT_TYPE_ANY };
|
---|
49 |
|
---|
50 | enum trust_direction {
|
---|
51 | UNKNOWN = 0,
|
---|
52 | INBOUND = LSA_TRUST_DIRECTION_INBOUND,
|
---|
53 | OUTBOUND = LSA_TRUST_DIRECTION_OUTBOUND
|
---|
54 | };
|
---|
55 |
|
---|
56 | static const char *trust_attrs[] = {
|
---|
57 | "trustPartner",
|
---|
58 | "trustAuthIncoming",
|
---|
59 | "trustAuthOutgoing",
|
---|
60 | "whenCreated",
|
---|
61 | "msDS-SupportedEncryptionTypes",
|
---|
62 | "trustAttributes",
|
---|
63 | "trustDirection",
|
---|
64 | "trustType",
|
---|
65 | NULL
|
---|
66 | };
|
---|
67 |
|
---|
68 |
|
---|
69 | static time_t ldb_msg_find_krb5time_ldap_time(struct ldb_message *msg, const char *attr, time_t default_val)
|
---|
70 | {
|
---|
71 | const char *tmp;
|
---|
72 | const char *gentime;
|
---|
73 | struct tm tm;
|
---|
74 |
|
---|
75 | gentime = ldb_msg_find_attr_as_string(msg, attr, NULL);
|
---|
76 | if (!gentime)
|
---|
77 | return default_val;
|
---|
78 |
|
---|
79 | tmp = strptime(gentime, "%Y%m%d%H%M%SZ", &tm);
|
---|
80 | if (tmp == NULL) {
|
---|
81 | return default_val;
|
---|
82 | }
|
---|
83 |
|
---|
84 | return timegm(&tm);
|
---|
85 | }
|
---|
86 |
|
---|
87 | static struct SDBFlags uf2SDBFlags(krb5_context context, uint32_t userAccountControl, enum samba_kdc_ent_type ent_type)
|
---|
88 | {
|
---|
89 | struct SDBFlags flags = int2SDBFlags(0);
|
---|
90 |
|
---|
91 | /* we don't allow kadmin deletes */
|
---|
92 | flags.immutable = 1;
|
---|
93 |
|
---|
94 | /* mark the principal as invalid to start with */
|
---|
95 | flags.invalid = 1;
|
---|
96 |
|
---|
97 | flags.renewable = 1;
|
---|
98 |
|
---|
99 | /* All accounts are servers, but this may be disabled again in the caller */
|
---|
100 | flags.server = 1;
|
---|
101 |
|
---|
102 | /* Account types - clear the invalid bit if it turns out to be valid */
|
---|
103 | if (userAccountControl & UF_NORMAL_ACCOUNT) {
|
---|
104 | if (ent_type == SAMBA_KDC_ENT_TYPE_CLIENT || ent_type == SAMBA_KDC_ENT_TYPE_ANY) {
|
---|
105 | flags.client = 1;
|
---|
106 | }
|
---|
107 | flags.invalid = 0;
|
---|
108 | }
|
---|
109 |
|
---|
110 | if (userAccountControl & UF_INTERDOMAIN_TRUST_ACCOUNT) {
|
---|
111 | if (ent_type == SAMBA_KDC_ENT_TYPE_CLIENT || ent_type == SAMBA_KDC_ENT_TYPE_ANY) {
|
---|
112 | flags.client = 1;
|
---|
113 | }
|
---|
114 | flags.invalid = 0;
|
---|
115 | }
|
---|
116 | if (userAccountControl & UF_WORKSTATION_TRUST_ACCOUNT) {
|
---|
117 | if (ent_type == SAMBA_KDC_ENT_TYPE_CLIENT || ent_type == SAMBA_KDC_ENT_TYPE_ANY) {
|
---|
118 | flags.client = 1;
|
---|
119 | }
|
---|
120 | flags.invalid = 0;
|
---|
121 | }
|
---|
122 | if (userAccountControl & UF_SERVER_TRUST_ACCOUNT) {
|
---|
123 | if (ent_type == SAMBA_KDC_ENT_TYPE_CLIENT || ent_type == SAMBA_KDC_ENT_TYPE_ANY) {
|
---|
124 | flags.client = 1;
|
---|
125 | }
|
---|
126 | flags.invalid = 0;
|
---|
127 | }
|
---|
128 |
|
---|
129 | /* Not permitted to act as a client if disabled */
|
---|
130 | if (userAccountControl & UF_ACCOUNTDISABLE) {
|
---|
131 | flags.client = 0;
|
---|
132 | }
|
---|
133 | if (userAccountControl & UF_LOCKOUT) {
|
---|
134 | flags.locked_out = 1;
|
---|
135 | }
|
---|
136 | /*
|
---|
137 | if (userAccountControl & UF_PASSWORD_NOTREQD) {
|
---|
138 | flags.invalid = 1;
|
---|
139 | }
|
---|
140 | */
|
---|
141 | /*
|
---|
142 | UF_PASSWORD_CANT_CHANGE and UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED are irrelevent
|
---|
143 | */
|
---|
144 | if (userAccountControl & UF_TEMP_DUPLICATE_ACCOUNT) {
|
---|
145 | flags.invalid = 1;
|
---|
146 | }
|
---|
147 |
|
---|
148 | /* UF_DONT_EXPIRE_PASSWD and UF_USE_DES_KEY_ONLY handled in samba_kdc_message2entry() */
|
---|
149 |
|
---|
150 | /*
|
---|
151 | if (userAccountControl & UF_MNS_LOGON_ACCOUNT) {
|
---|
152 | flags.invalid = 1;
|
---|
153 | }
|
---|
154 | */
|
---|
155 | if (userAccountControl & UF_SMARTCARD_REQUIRED) {
|
---|
156 | flags.require_hwauth = 1;
|
---|
157 | }
|
---|
158 | if (userAccountControl & UF_TRUSTED_FOR_DELEGATION) {
|
---|
159 | flags.ok_as_delegate = 1;
|
---|
160 | }
|
---|
161 | if (userAccountControl & UF_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION) {
|
---|
162 | /*
|
---|
163 | * this is confusing...
|
---|
164 | *
|
---|
165 | * UF_TRUSTED_FOR_DELEGATION
|
---|
166 | * => ok_as_delegate
|
---|
167 | *
|
---|
168 | * and
|
---|
169 | *
|
---|
170 | * UF_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION
|
---|
171 | * => trusted_for_delegation
|
---|
172 | */
|
---|
173 | flags.trusted_for_delegation = 1;
|
---|
174 | }
|
---|
175 | if (!(userAccountControl & UF_NOT_DELEGATED)) {
|
---|
176 | flags.forwardable = 1;
|
---|
177 | flags.proxiable = 1;
|
---|
178 | }
|
---|
179 |
|
---|
180 | if (userAccountControl & UF_DONT_REQUIRE_PREAUTH) {
|
---|
181 | flags.require_preauth = 0;
|
---|
182 | } else {
|
---|
183 | flags.require_preauth = 1;
|
---|
184 |
|
---|
185 | }
|
---|
186 | return flags;
|
---|
187 | }
|
---|
188 |
|
---|
189 | static int samba_kdc_entry_destructor(struct samba_kdc_entry *p)
|
---|
190 | {
|
---|
191 | if (p->entry_ex != NULL) {
|
---|
192 | struct sdb_entry_ex *entry_ex = p->entry_ex;
|
---|
193 | free_sdb_entry(&entry_ex->entry);
|
---|
194 | }
|
---|
195 |
|
---|
196 | return 0;
|
---|
197 | }
|
---|
198 |
|
---|
199 | static krb5_error_code samba_kdc_message2entry_keys(krb5_context context,
|
---|
200 | struct samba_kdc_db_context *kdc_db_ctx,
|
---|
201 | TALLOC_CTX *mem_ctx,
|
---|
202 | struct ldb_message *msg,
|
---|
203 | uint32_t rid,
|
---|
204 | bool is_rodc,
|
---|
205 | uint32_t userAccountControl,
|
---|
206 | enum samba_kdc_ent_type ent_type,
|
---|
207 | struct sdb_entry_ex *entry_ex)
|
---|
208 | {
|
---|
209 | krb5_error_code ret = 0;
|
---|
210 | enum ndr_err_code ndr_err;
|
---|
211 | struct samr_Password *hash;
|
---|
212 | const struct ldb_val *sc_val;
|
---|
213 | struct supplementalCredentialsBlob scb;
|
---|
214 | struct supplementalCredentialsPackage *scpk = NULL;
|
---|
215 | bool newer_keys = false;
|
---|
216 | struct package_PrimaryKerberosBlob _pkb;
|
---|
217 | struct package_PrimaryKerberosCtr3 *pkb3 = NULL;
|
---|
218 | struct package_PrimaryKerberosCtr4 *pkb4 = NULL;
|
---|
219 | uint16_t i;
|
---|
220 | uint16_t allocated_keys = 0;
|
---|
221 | int rodc_krbtgt_number = 0;
|
---|
222 | int kvno = 0;
|
---|
223 | uint32_t supported_enctypes
|
---|
224 | = ldb_msg_find_attr_as_uint(msg,
|
---|
225 | "msDS-SupportedEncryptionTypes",
|
---|
226 | 0);
|
---|
227 |
|
---|
228 | if (rid == DOMAIN_RID_KRBTGT || is_rodc) {
|
---|
229 | /* KDCs (and KDCs on RODCs) use AES */
|
---|
230 | supported_enctypes |= ENC_HMAC_SHA1_96_AES128 | ENC_HMAC_SHA1_96_AES256;
|
---|
231 | } else if (userAccountControl & (UF_PARTIAL_SECRETS_ACCOUNT|UF_SERVER_TRUST_ACCOUNT)) {
|
---|
232 | /* DCs and RODCs comptuer accounts use AES */
|
---|
233 | supported_enctypes |= ENC_HMAC_SHA1_96_AES128 | ENC_HMAC_SHA1_96_AES256;
|
---|
234 | } else if (ent_type == SAMBA_KDC_ENT_TYPE_CLIENT ||
|
---|
235 | (ent_type == SAMBA_KDC_ENT_TYPE_ANY)) {
|
---|
236 | /* for AS-REQ the client chooses the enc types it
|
---|
237 | * supports, and this will vary between computers a
|
---|
238 | * user logs in from.
|
---|
239 | *
|
---|
240 | * likewise for 'any' return as much as is supported,
|
---|
241 | * to export into a keytab */
|
---|
242 | supported_enctypes = ENC_ALL_TYPES;
|
---|
243 | }
|
---|
244 |
|
---|
245 | /* If UF_USE_DES_KEY_ONLY has been set, then don't allow use of the newer enc types */
|
---|
246 | if (userAccountControl & UF_USE_DES_KEY_ONLY) {
|
---|
247 | supported_enctypes = ENC_CRC32|ENC_RSA_MD5;
|
---|
248 | } else {
|
---|
249 | /* Otherwise, add in the default enc types */
|
---|
250 | supported_enctypes |= ENC_CRC32 | ENC_RSA_MD5 | ENC_RC4_HMAC_MD5;
|
---|
251 | }
|
---|
252 |
|
---|
253 | /* Is this the krbtgt or a RODC krbtgt */
|
---|
254 | if (is_rodc) {
|
---|
255 | rodc_krbtgt_number = ldb_msg_find_attr_as_int(msg, "msDS-SecondaryKrbTgtNumber", -1);
|
---|
256 |
|
---|
257 | if (rodc_krbtgt_number == -1) {
|
---|
258 | return EINVAL;
|
---|
259 | }
|
---|
260 | }
|
---|
261 |
|
---|
262 | entry_ex->entry.keys.val = NULL;
|
---|
263 | entry_ex->entry.keys.len = 0;
|
---|
264 |
|
---|
265 | kvno = ldb_msg_find_attr_as_int(msg, "msDS-KeyVersionNumber", 0);
|
---|
266 | if (is_rodc) {
|
---|
267 | kvno = SAMBA_KVNO_AND_KRBTGT(kvno, rodc_krbtgt_number);
|
---|
268 | }
|
---|
269 | entry_ex->entry.kvno = kvno;
|
---|
270 |
|
---|
271 | /* Get keys from the db */
|
---|
272 |
|
---|
273 | hash = samdb_result_hash(mem_ctx, msg, "unicodePwd");
|
---|
274 | sc_val = ldb_msg_find_ldb_val(msg, "supplementalCredentials");
|
---|
275 |
|
---|
276 | /* unicodePwd for enctype 0x17 (23) if present */
|
---|
277 | if (hash) {
|
---|
278 | allocated_keys++;
|
---|
279 | }
|
---|
280 |
|
---|
281 | /* supplementalCredentials if present */
|
---|
282 | if (sc_val) {
|
---|
283 | ndr_err = ndr_pull_struct_blob_all(sc_val, mem_ctx, &scb,
|
---|
284 | (ndr_pull_flags_fn_t)ndr_pull_supplementalCredentialsBlob);
|
---|
285 | if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
|
---|
286 | dump_data(0, sc_val->data, sc_val->length);
|
---|
287 | ret = EINVAL;
|
---|
288 | goto out;
|
---|
289 | }
|
---|
290 |
|
---|
291 | if (scb.sub.signature != SUPPLEMENTAL_CREDENTIALS_SIGNATURE) {
|
---|
292 | NDR_PRINT_DEBUG(supplementalCredentialsBlob, &scb);
|
---|
293 | ret = EINVAL;
|
---|
294 | goto out;
|
---|
295 | }
|
---|
296 |
|
---|
297 | for (i=0; i < scb.sub.num_packages; i++) {
|
---|
298 | if (strcmp("Primary:Kerberos-Newer-Keys", scb.sub.packages[i].name) == 0) {
|
---|
299 | scpk = &scb.sub.packages[i];
|
---|
300 | if (!scpk->data || !scpk->data[0]) {
|
---|
301 | scpk = NULL;
|
---|
302 | continue;
|
---|
303 | }
|
---|
304 | newer_keys = true;
|
---|
305 | break;
|
---|
306 | } else if (strcmp("Primary:Kerberos", scb.sub.packages[i].name) == 0) {
|
---|
307 | scpk = &scb.sub.packages[i];
|
---|
308 | if (!scpk->data || !scpk->data[0]) {
|
---|
309 | scpk = NULL;
|
---|
310 | }
|
---|
311 | /*
|
---|
312 | * we don't break here in hope to find
|
---|
313 | * a Kerberos-Newer-Keys package
|
---|
314 | */
|
---|
315 | }
|
---|
316 | }
|
---|
317 | }
|
---|
318 | /*
|
---|
319 | * Primary:Kerberos-Newer-Keys or Primary:Kerberos element
|
---|
320 | * of supplementalCredentials
|
---|
321 | */
|
---|
322 | if (scpk) {
|
---|
323 | DATA_BLOB blob;
|
---|
324 |
|
---|
325 | blob = strhex_to_data_blob(mem_ctx, scpk->data);
|
---|
326 | if (!blob.data) {
|
---|
327 | ret = ENOMEM;
|
---|
328 | goto out;
|
---|
329 | }
|
---|
330 |
|
---|
331 | /* we cannot use ndr_pull_struct_blob_all() here, as w2k and w2k3 add padding bytes */
|
---|
332 | ndr_err = ndr_pull_struct_blob(&blob, mem_ctx, &_pkb,
|
---|
333 | (ndr_pull_flags_fn_t)ndr_pull_package_PrimaryKerberosBlob);
|
---|
334 | if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
|
---|
335 | ret = EINVAL;
|
---|
336 | krb5_set_error_message(context, ret, "samba_kdc_message2entry_keys: could not parse package_PrimaryKerberosBlob");
|
---|
337 | krb5_warnx(context, "samba_kdc_message2entry_keys: could not parse package_PrimaryKerberosBlob");
|
---|
338 | goto out;
|
---|
339 | }
|
---|
340 |
|
---|
341 | if (newer_keys && _pkb.version != 4) {
|
---|
342 | ret = EINVAL;
|
---|
343 | krb5_set_error_message(context, ret, "samba_kdc_message2entry_keys: Primary:Kerberos-Newer-Keys not version 4");
|
---|
344 | krb5_warnx(context, "samba_kdc_message2entry_keys: Primary:Kerberos-Newer-Keys not version 4");
|
---|
345 | goto out;
|
---|
346 | }
|
---|
347 |
|
---|
348 | if (!newer_keys && _pkb.version != 3) {
|
---|
349 | ret = EINVAL;
|
---|
350 | krb5_set_error_message(context, ret, "samba_kdc_message2entry_keys: could not parse Primary:Kerberos not version 3");
|
---|
351 | krb5_warnx(context, "samba_kdc_message2entry_keys: could not parse Primary:Kerberos not version 3");
|
---|
352 | goto out;
|
---|
353 | }
|
---|
354 |
|
---|
355 | if (_pkb.version == 4) {
|
---|
356 | pkb4 = &_pkb.ctr.ctr4;
|
---|
357 | allocated_keys += pkb4->num_keys;
|
---|
358 | } else if (_pkb.version == 3) {
|
---|
359 | pkb3 = &_pkb.ctr.ctr3;
|
---|
360 | allocated_keys += pkb3->num_keys;
|
---|
361 | }
|
---|
362 | }
|
---|
363 |
|
---|
364 | if (allocated_keys == 0) {
|
---|
365 | if (kdc_db_ctx->rodc) {
|
---|
366 | /* We are on an RODC, but don't have keys for this account. Signal this to the caller */
|
---|
367 | /* TODO: We need to call a generalised version of auth_sam_trigger_repl_secret from here */
|
---|
368 | return SDB_ERR_NOT_FOUND_HERE;
|
---|
369 | }
|
---|
370 |
|
---|
371 | /* oh, no password. Apparently (comment in
|
---|
372 | * hdb-ldap.c) this violates the ASN.1, but this
|
---|
373 | * allows an entry with no keys (yet). */
|
---|
374 | return 0;
|
---|
375 | }
|
---|
376 |
|
---|
377 | /* allocate space to decode into */
|
---|
378 | entry_ex->entry.keys.len = 0;
|
---|
379 | entry_ex->entry.keys.val = calloc(allocated_keys, sizeof(struct sdb_key));
|
---|
380 | if (entry_ex->entry.keys.val == NULL) {
|
---|
381 | ret = ENOMEM;
|
---|
382 | goto out;
|
---|
383 | }
|
---|
384 |
|
---|
385 | if (hash && (supported_enctypes & ENC_RC4_HMAC_MD5)) {
|
---|
386 | struct sdb_key key = {};
|
---|
387 |
|
---|
388 | ret = smb_krb5_keyblock_init_contents(context,
|
---|
389 | ENCTYPE_ARCFOUR_HMAC,
|
---|
390 | hash->hash,
|
---|
391 | sizeof(hash->hash),
|
---|
392 | &key.key);
|
---|
393 | if (ret) {
|
---|
394 | goto out;
|
---|
395 | }
|
---|
396 |
|
---|
397 | entry_ex->entry.keys.val[entry_ex->entry.keys.len] = key;
|
---|
398 | entry_ex->entry.keys.len++;
|
---|
399 | }
|
---|
400 |
|
---|
401 | if (pkb4) {
|
---|
402 | for (i=0; i < pkb4->num_keys; i++) {
|
---|
403 | struct sdb_key key = {};
|
---|
404 |
|
---|
405 | if (!pkb4->keys[i].value) continue;
|
---|
406 |
|
---|
407 | if (!(kerberos_enctype_to_bitmap(pkb4->keys[i].keytype) & supported_enctypes)) {
|
---|
408 | continue;
|
---|
409 | }
|
---|
410 |
|
---|
411 | if (pkb4->salt.string) {
|
---|
412 | DATA_BLOB salt;
|
---|
413 |
|
---|
414 | salt = data_blob_string_const(pkb4->salt.string);
|
---|
415 |
|
---|
416 | key.salt = calloc(1, sizeof(*key.salt));
|
---|
417 | if (key.salt == NULL) {
|
---|
418 | ret = ENOMEM;
|
---|
419 | goto out;
|
---|
420 | }
|
---|
421 |
|
---|
422 | key.salt->type = KRB5_PW_SALT;
|
---|
423 |
|
---|
424 | ret = krb5_copy_data_contents(&key.salt->salt,
|
---|
425 | salt.data,
|
---|
426 | salt.length);
|
---|
427 | if (ret) {
|
---|
428 | free(key.salt);
|
---|
429 | key.salt = NULL;
|
---|
430 | goto out;
|
---|
431 | }
|
---|
432 | }
|
---|
433 |
|
---|
434 | /* TODO: maybe pass the iteration_count somehow... */
|
---|
435 |
|
---|
436 | ret = smb_krb5_keyblock_init_contents(context,
|
---|
437 | pkb4->keys[i].keytype,
|
---|
438 | pkb4->keys[i].value->data,
|
---|
439 | pkb4->keys[i].value->length,
|
---|
440 | &key.key);
|
---|
441 | if (ret == KRB5_PROG_ETYPE_NOSUPP) {
|
---|
442 | DEBUG(2,("Unsupported keytype ignored - type %u\n",
|
---|
443 | pkb4->keys[i].keytype));
|
---|
444 | ret = 0;
|
---|
445 | continue;
|
---|
446 | }
|
---|
447 | if (ret) {
|
---|
448 | if (key.salt) {
|
---|
449 | kerberos_free_data_contents(context, &key.salt->salt);
|
---|
450 | free(key.salt);
|
---|
451 | key.salt = NULL;
|
---|
452 | }
|
---|
453 | goto out;
|
---|
454 | }
|
---|
455 |
|
---|
456 | entry_ex->entry.keys.val[entry_ex->entry.keys.len] = key;
|
---|
457 | entry_ex->entry.keys.len++;
|
---|
458 | }
|
---|
459 | } else if (pkb3) {
|
---|
460 | for (i=0; i < pkb3->num_keys; i++) {
|
---|
461 | struct sdb_key key = {};
|
---|
462 |
|
---|
463 | if (!pkb3->keys[i].value) continue;
|
---|
464 |
|
---|
465 | if (!(kerberos_enctype_to_bitmap(pkb3->keys[i].keytype) & supported_enctypes)) {
|
---|
466 | continue;
|
---|
467 | }
|
---|
468 |
|
---|
469 | if (pkb3->salt.string) {
|
---|
470 | DATA_BLOB salt;
|
---|
471 |
|
---|
472 | salt = data_blob_string_const(pkb3->salt.string);
|
---|
473 |
|
---|
474 | key.salt = calloc(1, sizeof(*key.salt));
|
---|
475 | if (key.salt == NULL) {
|
---|
476 | ret = ENOMEM;
|
---|
477 | goto out;
|
---|
478 | }
|
---|
479 |
|
---|
480 | key.salt->type = KRB5_PW_SALT;
|
---|
481 |
|
---|
482 | ret = krb5_copy_data_contents(&key.salt->salt,
|
---|
483 | salt.data,
|
---|
484 | salt.length);
|
---|
485 | if (ret) {
|
---|
486 | free(key.salt);
|
---|
487 | key.salt = NULL;
|
---|
488 | goto out;
|
---|
489 | }
|
---|
490 | }
|
---|
491 |
|
---|
492 | ret = smb_krb5_keyblock_init_contents(context,
|
---|
493 | pkb3->keys[i].keytype,
|
---|
494 | pkb3->keys[i].value->data,
|
---|
495 | pkb3->keys[i].value->length,
|
---|
496 | &key.key);
|
---|
497 | if (ret) {
|
---|
498 | if (key.salt) {
|
---|
499 | kerberos_free_data_contents(context, &key.salt->salt);
|
---|
500 | free(key.salt);
|
---|
501 | key.salt = NULL;
|
---|
502 | }
|
---|
503 | goto out;
|
---|
504 | }
|
---|
505 |
|
---|
506 | entry_ex->entry.keys.val[entry_ex->entry.keys.len] = key;
|
---|
507 | entry_ex->entry.keys.len++;
|
---|
508 | }
|
---|
509 | }
|
---|
510 |
|
---|
511 | out:
|
---|
512 | if (ret != 0) {
|
---|
513 | entry_ex->entry.keys.len = 0;
|
---|
514 | }
|
---|
515 | if (entry_ex->entry.keys.len == 0 && entry_ex->entry.keys.val) {
|
---|
516 | free(entry_ex->entry.keys.val);
|
---|
517 | entry_ex->entry.keys.val = NULL;
|
---|
518 | }
|
---|
519 | return ret;
|
---|
520 | }
|
---|
521 |
|
---|
522 | static int principal_comp_strcmp_int(krb5_context context,
|
---|
523 | krb5_const_principal principal,
|
---|
524 | unsigned int component,
|
---|
525 | const char *string,
|
---|
526 | bool do_strcasecmp)
|
---|
527 | {
|
---|
528 | const char *p;
|
---|
529 | size_t len;
|
---|
530 |
|
---|
531 | #if defined(HAVE_KRB5_PRINCIPAL_GET_COMP_STRING)
|
---|
532 | p = krb5_principal_get_comp_string(context, principal, component);
|
---|
533 | if (p == NULL) {
|
---|
534 | return -1;
|
---|
535 | }
|
---|
536 | len = strlen(p);
|
---|
537 | #else
|
---|
538 | krb5_data *d;
|
---|
539 | if (component >= krb5_princ_size(context, principal)) {
|
---|
540 | return -1;
|
---|
541 | }
|
---|
542 |
|
---|
543 | d = krb5_princ_component(context, principal, component);
|
---|
544 | if (d == NULL) {
|
---|
545 | return -1;
|
---|
546 | }
|
---|
547 |
|
---|
548 | p = d->data;
|
---|
549 | len = d->length;
|
---|
550 | #endif
|
---|
551 | if (do_strcasecmp) {
|
---|
552 | return strncasecmp(p, string, len);
|
---|
553 | } else {
|
---|
554 | return strncmp(p, string, len);
|
---|
555 | }
|
---|
556 | }
|
---|
557 |
|
---|
558 | static int principal_comp_strcasecmp(krb5_context context,
|
---|
559 | krb5_const_principal principal,
|
---|
560 | unsigned int component,
|
---|
561 | const char *string)
|
---|
562 | {
|
---|
563 | return principal_comp_strcmp_int(context, principal,
|
---|
564 | component, string, true);
|
---|
565 | }
|
---|
566 |
|
---|
567 | static int principal_comp_strcmp(krb5_context context,
|
---|
568 | krb5_const_principal principal,
|
---|
569 | unsigned int component,
|
---|
570 | const char *string)
|
---|
571 | {
|
---|
572 | return principal_comp_strcmp_int(context, principal,
|
---|
573 | component, string, false);
|
---|
574 | }
|
---|
575 |
|
---|
576 | /*
|
---|
577 | * Construct an hdb_entry from a directory entry.
|
---|
578 | */
|
---|
579 | static krb5_error_code samba_kdc_message2entry(krb5_context context,
|
---|
580 | struct samba_kdc_db_context *kdc_db_ctx,
|
---|
581 | TALLOC_CTX *mem_ctx,
|
---|
582 | krb5_const_principal principal,
|
---|
583 | enum samba_kdc_ent_type ent_type,
|
---|
584 | unsigned flags,
|
---|
585 | struct ldb_dn *realm_dn,
|
---|
586 | struct ldb_message *msg,
|
---|
587 | struct sdb_entry_ex *entry_ex)
|
---|
588 | {
|
---|
589 | struct loadparm_context *lp_ctx = kdc_db_ctx->lp_ctx;
|
---|
590 | uint32_t userAccountControl;
|
---|
591 | uint32_t msDS_User_Account_Control_Computed;
|
---|
592 | unsigned int i;
|
---|
593 | krb5_error_code ret = 0;
|
---|
594 | krb5_boolean is_computer = FALSE;
|
---|
595 |
|
---|
596 | struct samba_kdc_entry *p;
|
---|
597 | NTTIME acct_expiry;
|
---|
598 | NTSTATUS status;
|
---|
599 |
|
---|
600 | uint32_t rid;
|
---|
601 | bool is_rodc = false;
|
---|
602 | struct ldb_message_element *objectclasses;
|
---|
603 | struct ldb_val computer_val;
|
---|
604 | const char *samAccountName = ldb_msg_find_attr_as_string(msg, "samAccountName", NULL);
|
---|
605 | computer_val.data = discard_const_p(uint8_t,"computer");
|
---|
606 | computer_val.length = strlen((const char *)computer_val.data);
|
---|
607 |
|
---|
608 | if (ldb_msg_find_element(msg, "msDS-SecondaryKrbTgtNumber")) {
|
---|
609 | is_rodc = true;
|
---|
610 | }
|
---|
611 |
|
---|
612 | if (!samAccountName) {
|
---|
613 | ret = ENOENT;
|
---|
614 | krb5_set_error_message(context, ret, "samba_kdc_message2entry: no samAccountName present");
|
---|
615 | goto out;
|
---|
616 | }
|
---|
617 |
|
---|
618 | objectclasses = ldb_msg_find_element(msg, "objectClass");
|
---|
619 |
|
---|
620 | if (objectclasses && ldb_msg_find_val(objectclasses, &computer_val)) {
|
---|
621 | is_computer = TRUE;
|
---|
622 | }
|
---|
623 |
|
---|
624 | ZERO_STRUCTP(entry_ex);
|
---|
625 |
|
---|
626 | p = talloc_zero(mem_ctx, struct samba_kdc_entry);
|
---|
627 | if (!p) {
|
---|
628 | ret = ENOMEM;
|
---|
629 | goto out;
|
---|
630 | }
|
---|
631 |
|
---|
632 | p->kdc_db_ctx = kdc_db_ctx;
|
---|
633 | p->realm_dn = talloc_reference(p, realm_dn);
|
---|
634 | if (!p->realm_dn) {
|
---|
635 | ret = ENOMEM;
|
---|
636 | goto out;
|
---|
637 | }
|
---|
638 |
|
---|
639 | talloc_set_destructor(p, samba_kdc_entry_destructor);
|
---|
640 |
|
---|
641 | entry_ex->ctx = p;
|
---|
642 |
|
---|
643 | userAccountControl = ldb_msg_find_attr_as_uint(msg, "userAccountControl", 0);
|
---|
644 |
|
---|
645 | msDS_User_Account_Control_Computed
|
---|
646 | = ldb_msg_find_attr_as_uint(msg,
|
---|
647 | "msDS-User-Account-Control-Computed",
|
---|
648 | UF_ACCOUNTDISABLE);
|
---|
649 |
|
---|
650 | /*
|
---|
651 | * This brings in the lockout flag, block the account if not
|
---|
652 | * found. We need the weird UF_ACCOUNTDISABLE check because
|
---|
653 | * we do not want to fail open if the value is not returned,
|
---|
654 | * but 0 is a valid value (all OK)
|
---|
655 | */
|
---|
656 | if (msDS_User_Account_Control_Computed == UF_ACCOUNTDISABLE) {
|
---|
657 | ret = EINVAL;
|
---|
658 | krb5_set_error_message(context, ret, "samba_kdc_message2entry: "
|
---|
659 | "no msDS-User-Account-Control-Computed present");
|
---|
660 | goto out;
|
---|
661 | } else {
|
---|
662 | userAccountControl |= msDS_User_Account_Control_Computed;
|
---|
663 | }
|
---|
664 |
|
---|
665 | /*
|
---|
666 | * If we are set to canonicalize, we get back the fixed UPPER
|
---|
667 | * case realm, and the real username (ie matching LDAP
|
---|
668 | * samAccountName)
|
---|
669 | *
|
---|
670 | * Otherwise, if we are set to enterprise, we
|
---|
671 | * get back the whole principal as-sent
|
---|
672 | *
|
---|
673 | * Finally, if we are not set to canonicalize, we get back the
|
---|
674 | * fixed UPPER case realm, but the as-sent username
|
---|
675 | */
|
---|
676 |
|
---|
677 | if (ent_type == SAMBA_KDC_ENT_TYPE_KRBTGT) {
|
---|
678 | if (flags & (SDB_F_CANON)) {
|
---|
679 | /*
|
---|
680 | * When requested to do so, ensure that the
|
---|
681 | * both realm values in the principal are set
|
---|
682 | * to the upper case, canonical realm
|
---|
683 | */
|
---|
684 | ret = smb_krb5_make_principal(context, &entry_ex->entry.principal,
|
---|
685 | lpcfg_realm(lp_ctx), "krbtgt",
|
---|
686 | lpcfg_realm(lp_ctx), NULL);
|
---|
687 | if (ret) {
|
---|
688 | krb5_clear_error_message(context);
|
---|
689 | goto out;
|
---|
690 | }
|
---|
691 | smb_krb5_principal_set_type(context, entry_ex->entry.principal, KRB5_NT_SRV_INST);
|
---|
692 | } else {
|
---|
693 | ret = krb5_copy_principal(context, principal, &entry_ex->entry.principal);
|
---|
694 | if (ret) {
|
---|
695 | krb5_clear_error_message(context);
|
---|
696 | goto out;
|
---|
697 | }
|
---|
698 | /*
|
---|
699 | * this appears to be required regardless of
|
---|
700 | * the canonicalize flag from the client
|
---|
701 | */
|
---|
702 | ret = smb_krb5_principal_set_realm(context, entry_ex->entry.principal, lpcfg_realm(lp_ctx));
|
---|
703 | if (ret) {
|
---|
704 | krb5_clear_error_message(context);
|
---|
705 | goto out;
|
---|
706 | }
|
---|
707 | }
|
---|
708 |
|
---|
709 | } else if (ent_type == SAMBA_KDC_ENT_TYPE_ANY && principal == NULL) {
|
---|
710 | ret = smb_krb5_make_principal(context, &entry_ex->entry.principal, lpcfg_realm(lp_ctx), samAccountName, NULL);
|
---|
711 | if (ret) {
|
---|
712 | krb5_clear_error_message(context);
|
---|
713 | goto out;
|
---|
714 | }
|
---|
715 | } else if (flags & SDB_F_CANON && flags & SDB_F_FOR_AS_REQ) {
|
---|
716 | /*
|
---|
717 | * SDB_F_CANON maps from the canonicalize flag in the
|
---|
718 | * packet, and has a different meaning between AS-REQ
|
---|
719 | * and TGS-REQ. We only change the principal in the AS-REQ case
|
---|
720 | */
|
---|
721 | ret = smb_krb5_make_principal(context, &entry_ex->entry.principal, lpcfg_realm(lp_ctx), samAccountName, NULL);
|
---|
722 | if (ret) {
|
---|
723 | krb5_clear_error_message(context);
|
---|
724 | goto out;
|
---|
725 | }
|
---|
726 | } else {
|
---|
727 | ret = krb5_copy_principal(context, principal, &entry_ex->entry.principal);
|
---|
728 | if (ret) {
|
---|
729 | krb5_clear_error_message(context);
|
---|
730 | goto out;
|
---|
731 | }
|
---|
732 |
|
---|
733 | if (smb_krb5_principal_get_type(context, principal) != KRB5_NT_ENTERPRISE_PRINCIPAL) {
|
---|
734 | /* While we have copied the client principal, tests
|
---|
735 | * show that Win2k3 returns the 'corrected' realm, not
|
---|
736 | * the client-specified realm. This code attempts to
|
---|
737 | * replace the client principal's realm with the one
|
---|
738 | * we determine from our records */
|
---|
739 |
|
---|
740 | /* this has to be with malloc() */
|
---|
741 | ret = smb_krb5_principal_set_realm(context, entry_ex->entry.principal, lpcfg_realm(lp_ctx));
|
---|
742 | if (ret) {
|
---|
743 | krb5_clear_error_message(context);
|
---|
744 | goto out;
|
---|
745 | }
|
---|
746 | }
|
---|
747 | }
|
---|
748 |
|
---|
749 | /* First try and figure out the flags based on the userAccountControl */
|
---|
750 | entry_ex->entry.flags = uf2SDBFlags(context, userAccountControl, ent_type);
|
---|
751 |
|
---|
752 | /* Windows 2008 seems to enforce this (very sensible) rule by
|
---|
753 | * default - don't allow offline attacks on a user's password
|
---|
754 | * by asking for a ticket to them as a service (encrypted with
|
---|
755 | * their probably patheticly insecure password) */
|
---|
756 |
|
---|
757 | if (entry_ex->entry.flags.server
|
---|
758 | && lpcfg_parm_bool(lp_ctx, NULL, "kdc", "require spn for service", true)) {
|
---|
759 | if (!is_computer && !ldb_msg_find_attr_as_string(msg, "servicePrincipalName", NULL)) {
|
---|
760 | entry_ex->entry.flags.server = 0;
|
---|
761 | }
|
---|
762 | }
|
---|
763 | /*
|
---|
764 | * To give the correct type of error to the client, we must
|
---|
765 | * not just return the entry without .server set, we must
|
---|
766 | * pretend the principal does not exist. Otherwise we may
|
---|
767 | * return ERR_POLICY instead of
|
---|
768 | * KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN
|
---|
769 | */
|
---|
770 | if (ent_type == SAMBA_KDC_ENT_TYPE_SERVER && entry_ex->entry.flags.server == 0) {
|
---|
771 | ret = SDB_ERR_NOENTRY;
|
---|
772 | krb5_set_error_message(context, ret, "samba_kdc_message2entry: no servicePrincipalName present for this server, refusing with no-such-entry");
|
---|
773 | goto out;
|
---|
774 | }
|
---|
775 | if (flags & SDB_F_ADMIN_DATA) {
|
---|
776 | /* These (created_by, modified_by) parts of the entry are not relevant for Samba4's use
|
---|
777 | * of the Heimdal KDC. They are stored in a the traditional
|
---|
778 | * DB for audit purposes, and still form part of the structure
|
---|
779 | * we must return */
|
---|
780 |
|
---|
781 | /* use 'whenCreated' */
|
---|
782 | entry_ex->entry.created_by.time = ldb_msg_find_krb5time_ldap_time(msg, "whenCreated", 0);
|
---|
783 | /* use 'kadmin' for now (needed by mit_samba) */
|
---|
784 |
|
---|
785 | ret = smb_krb5_make_principal(context,
|
---|
786 | &entry_ex->entry.created_by.principal,
|
---|
787 | lpcfg_realm(lp_ctx), "kadmin", NULL);
|
---|
788 | if (ret) {
|
---|
789 | krb5_clear_error_message(context);
|
---|
790 | goto out;
|
---|
791 | }
|
---|
792 |
|
---|
793 | entry_ex->entry.modified_by = (struct sdb_event *) malloc(sizeof(struct sdb_event));
|
---|
794 | if (entry_ex->entry.modified_by == NULL) {
|
---|
795 | ret = ENOMEM;
|
---|
796 | krb5_set_error_message(context, ret, "malloc: out of memory");
|
---|
797 | goto out;
|
---|
798 | }
|
---|
799 |
|
---|
800 | /* use 'whenChanged' */
|
---|
801 | entry_ex->entry.modified_by->time = ldb_msg_find_krb5time_ldap_time(msg, "whenChanged", 0);
|
---|
802 | /* use 'kadmin' for now (needed by mit_samba) */
|
---|
803 | ret = smb_krb5_make_principal(context,
|
---|
804 | &entry_ex->entry.modified_by->principal,
|
---|
805 | lpcfg_realm(lp_ctx), "kadmin", NULL);
|
---|
806 | if (ret) {
|
---|
807 | krb5_clear_error_message(context);
|
---|
808 | goto out;
|
---|
809 | }
|
---|
810 | }
|
---|
811 |
|
---|
812 |
|
---|
813 | /* The lack of password controls etc applies to krbtgt by
|
---|
814 | * virtue of being that particular RID */
|
---|
815 | status = dom_sid_split_rid(NULL, samdb_result_dom_sid(mem_ctx, msg, "objectSid"), NULL, &rid);
|
---|
816 |
|
---|
817 | if (!NT_STATUS_IS_OK(status)) {
|
---|
818 | ret = EINVAL;
|
---|
819 | goto out;
|
---|
820 | }
|
---|
821 |
|
---|
822 | if (rid == DOMAIN_RID_KRBTGT) {
|
---|
823 | char *realm = NULL;
|
---|
824 |
|
---|
825 | entry_ex->entry.valid_end = NULL;
|
---|
826 | entry_ex->entry.pw_end = NULL;
|
---|
827 |
|
---|
828 | entry_ex->entry.flags.invalid = 0;
|
---|
829 | entry_ex->entry.flags.server = 1;
|
---|
830 |
|
---|
831 | realm = smb_krb5_principal_get_realm(context, principal);
|
---|
832 | if (realm == NULL) {
|
---|
833 | ret = ENOMEM;
|
---|
834 | goto out;
|
---|
835 | }
|
---|
836 |
|
---|
837 | /* Don't mark all requests for the krbtgt/realm as
|
---|
838 | * 'change password', as otherwise we could get into
|
---|
839 | * trouble, and not enforce the password expirty.
|
---|
840 | * Instead, only do it when request is for the kpasswd service */
|
---|
841 | if (ent_type == SAMBA_KDC_ENT_TYPE_SERVER
|
---|
842 | && krb5_princ_size(context, principal) == 2
|
---|
843 | && (principal_comp_strcmp(context, principal, 0, "kadmin") == 0)
|
---|
844 | && (principal_comp_strcmp(context, principal, 1, "changepw") == 0)
|
---|
845 | && lpcfg_is_my_domain_or_realm(lp_ctx, realm)) {
|
---|
846 | entry_ex->entry.flags.change_pw = 1;
|
---|
847 | }
|
---|
848 |
|
---|
849 | SAFE_FREE(realm);
|
---|
850 |
|
---|
851 | entry_ex->entry.flags.client = 0;
|
---|
852 | entry_ex->entry.flags.forwardable = 1;
|
---|
853 | entry_ex->entry.flags.ok_as_delegate = 1;
|
---|
854 | } else if (is_rodc) {
|
---|
855 | /* The RODC krbtgt account is like the main krbtgt,
|
---|
856 | * but it does not have a changepw or kadmin
|
---|
857 | * service */
|
---|
858 |
|
---|
859 | entry_ex->entry.valid_end = NULL;
|
---|
860 | entry_ex->entry.pw_end = NULL;
|
---|
861 |
|
---|
862 | /* Also don't allow the RODC krbtgt to be a client (it should not be needed) */
|
---|
863 | entry_ex->entry.flags.client = 0;
|
---|
864 | entry_ex->entry.flags.invalid = 0;
|
---|
865 | entry_ex->entry.flags.server = 1;
|
---|
866 |
|
---|
867 | entry_ex->entry.flags.client = 0;
|
---|
868 | entry_ex->entry.flags.forwardable = 1;
|
---|
869 | entry_ex->entry.flags.ok_as_delegate = 0;
|
---|
870 | } else if (entry_ex->entry.flags.server && ent_type == SAMBA_KDC_ENT_TYPE_SERVER) {
|
---|
871 | /* The account/password expiry only applies when the account is used as a
|
---|
872 | * client (ie password login), not when used as a server */
|
---|
873 |
|
---|
874 | /* Make very well sure we don't use this for a client,
|
---|
875 | * it could bypass the password restrictions */
|
---|
876 | entry_ex->entry.flags.client = 0;
|
---|
877 |
|
---|
878 | entry_ex->entry.valid_end = NULL;
|
---|
879 | entry_ex->entry.pw_end = NULL;
|
---|
880 |
|
---|
881 | } else {
|
---|
882 | NTTIME must_change_time
|
---|
883 | = samdb_result_force_password_change(kdc_db_ctx->samdb, mem_ctx,
|
---|
884 | realm_dn, msg);
|
---|
885 | if (must_change_time == 0x7FFFFFFFFFFFFFFFULL) {
|
---|
886 | entry_ex->entry.pw_end = NULL;
|
---|
887 | } else {
|
---|
888 | entry_ex->entry.pw_end = malloc(sizeof(*entry_ex->entry.pw_end));
|
---|
889 | if (entry_ex->entry.pw_end == NULL) {
|
---|
890 | ret = ENOMEM;
|
---|
891 | goto out;
|
---|
892 | }
|
---|
893 | *entry_ex->entry.pw_end = nt_time_to_unix(must_change_time);
|
---|
894 | }
|
---|
895 |
|
---|
896 | acct_expiry = samdb_result_account_expires(msg);
|
---|
897 | if (acct_expiry == 0x7FFFFFFFFFFFFFFFULL) {
|
---|
898 | entry_ex->entry.valid_end = NULL;
|
---|
899 | } else {
|
---|
900 | entry_ex->entry.valid_end = malloc(sizeof(*entry_ex->entry.valid_end));
|
---|
901 | if (entry_ex->entry.valid_end == NULL) {
|
---|
902 | ret = ENOMEM;
|
---|
903 | goto out;
|
---|
904 | }
|
---|
905 | *entry_ex->entry.valid_end = nt_time_to_unix(acct_expiry);
|
---|
906 | }
|
---|
907 | }
|
---|
908 |
|
---|
909 | entry_ex->entry.valid_start = NULL;
|
---|
910 |
|
---|
911 | entry_ex->entry.max_life = malloc(sizeof(*entry_ex->entry.max_life));
|
---|
912 | if (entry_ex->entry.max_life == NULL) {
|
---|
913 | ret = ENOMEM;
|
---|
914 | goto out;
|
---|
915 | }
|
---|
916 |
|
---|
917 | if (ent_type == SAMBA_KDC_ENT_TYPE_SERVER) {
|
---|
918 | *entry_ex->entry.max_life = kdc_db_ctx->policy.svc_tkt_lifetime;
|
---|
919 | } else if (ent_type == SAMBA_KDC_ENT_TYPE_KRBTGT || ent_type == SAMBA_KDC_ENT_TYPE_CLIENT) {
|
---|
920 | *entry_ex->entry.max_life = kdc_db_ctx->policy.usr_tkt_lifetime;
|
---|
921 | } else {
|
---|
922 | *entry_ex->entry.max_life = MIN(kdc_db_ctx->policy.svc_tkt_lifetime,
|
---|
923 | kdc_db_ctx->policy.usr_tkt_lifetime);
|
---|
924 | }
|
---|
925 |
|
---|
926 | entry_ex->entry.max_renew = malloc(sizeof(*entry_ex->entry.max_life));
|
---|
927 | if (entry_ex->entry.max_renew == NULL) {
|
---|
928 | ret = ENOMEM;
|
---|
929 | goto out;
|
---|
930 | }
|
---|
931 |
|
---|
932 | *entry_ex->entry.max_renew = kdc_db_ctx->policy.renewal_lifetime;
|
---|
933 |
|
---|
934 | /* Get keys from the db */
|
---|
935 | ret = samba_kdc_message2entry_keys(context, kdc_db_ctx, p, msg,
|
---|
936 | rid, is_rodc, userAccountControl,
|
---|
937 | ent_type, entry_ex);
|
---|
938 | if (ret) {
|
---|
939 | /* Could be bogus data in the entry, or out of memory */
|
---|
940 | goto out;
|
---|
941 | }
|
---|
942 |
|
---|
943 | entry_ex->entry.etypes = malloc(sizeof(*(entry_ex->entry.etypes)));
|
---|
944 | if (entry_ex->entry.etypes == NULL) {
|
---|
945 | krb5_clear_error_message(context);
|
---|
946 | ret = ENOMEM;
|
---|
947 | goto out;
|
---|
948 | }
|
---|
949 | entry_ex->entry.etypes->len = entry_ex->entry.keys.len;
|
---|
950 | entry_ex->entry.etypes->val = calloc(entry_ex->entry.etypes->len, sizeof(int));
|
---|
951 | if (entry_ex->entry.etypes->val == NULL) {
|
---|
952 | krb5_clear_error_message(context);
|
---|
953 | ret = ENOMEM;
|
---|
954 | goto out;
|
---|
955 | }
|
---|
956 | for (i=0; i < entry_ex->entry.etypes->len; i++) {
|
---|
957 | entry_ex->entry.etypes->val[i] = KRB5_KEY_TYPE(&entry_ex->entry.keys.val[i].key);
|
---|
958 | }
|
---|
959 |
|
---|
960 |
|
---|
961 | p->msg = talloc_steal(p, msg);
|
---|
962 |
|
---|
963 | out:
|
---|
964 | if (ret != 0) {
|
---|
965 | /* This doesn't free ent itself, that is for the eventual caller to do */
|
---|
966 | sdb_free_entry(entry_ex);
|
---|
967 | ZERO_STRUCTP(entry_ex);
|
---|
968 | } else {
|
---|
969 | talloc_steal(kdc_db_ctx, entry_ex->ctx);
|
---|
970 | }
|
---|
971 |
|
---|
972 | return ret;
|
---|
973 | }
|
---|
974 |
|
---|
975 | /*
|
---|
976 | * Construct an hdb_entry from a directory entry.
|
---|
977 | * The kvno is what the remote client asked for
|
---|
978 | */
|
---|
979 | static krb5_error_code samba_kdc_trust_message2entry(krb5_context context,
|
---|
980 | struct samba_kdc_db_context *kdc_db_ctx,
|
---|
981 | TALLOC_CTX *mem_ctx, krb5_const_principal principal,
|
---|
982 | enum trust_direction direction,
|
---|
983 | struct ldb_dn *realm_dn,
|
---|
984 | unsigned flags,
|
---|
985 | uint32_t kvno,
|
---|
986 | struct ldb_message *msg,
|
---|
987 | struct sdb_entry_ex *entry_ex)
|
---|
988 | {
|
---|
989 | struct loadparm_context *lp_ctx = kdc_db_ctx->lp_ctx;
|
---|
990 | const char *our_realm = lpcfg_realm(lp_ctx);
|
---|
991 | const char *dnsdomain = NULL;
|
---|
992 | char *partner_realm = NULL;
|
---|
993 | const char *realm = NULL;
|
---|
994 | const char *krbtgt_realm = NULL;
|
---|
995 | DATA_BLOB password_utf16 = data_blob_null;
|
---|
996 | DATA_BLOB password_utf8 = data_blob_null;
|
---|
997 | struct samr_Password _password_hash;
|
---|
998 | const struct samr_Password *password_hash = NULL;
|
---|
999 | const struct ldb_val *password_val;
|
---|
1000 | struct trustAuthInOutBlob password_blob;
|
---|
1001 | struct samba_kdc_entry *p;
|
---|
1002 | bool use_previous = false;
|
---|
1003 | uint32_t current_kvno;
|
---|
1004 | uint32_t previous_kvno;
|
---|
1005 | uint32_t num_keys = 0;
|
---|
1006 | enum ndr_err_code ndr_err;
|
---|
1007 | int ret, trust_direction_flags;
|
---|
1008 | unsigned int i;
|
---|
1009 | struct AuthenticationInformationArray *auth_array;
|
---|
1010 | struct timeval tv;
|
---|
1011 | NTTIME an_hour_ago;
|
---|
1012 | uint32_t *auth_kvno;
|
---|
1013 | bool preferr_current = false;
|
---|
1014 | uint32_t supported_enctypes = ENC_RC4_HMAC_MD5;
|
---|
1015 |
|
---|
1016 | if (dsdb_functional_level(kdc_db_ctx->samdb) >= DS_DOMAIN_FUNCTION_2008) {
|
---|
1017 | supported_enctypes = ldb_msg_find_attr_as_uint(msg,
|
---|
1018 | "msDS-SupportedEncryptionTypes",
|
---|
1019 | supported_enctypes);
|
---|
1020 | }
|
---|
1021 |
|
---|
1022 | trust_direction_flags = ldb_msg_find_attr_as_int(msg, "trustDirection", 0);
|
---|
1023 | if (!(trust_direction_flags & direction)) {
|
---|
1024 | krb5_clear_error_message(context);
|
---|
1025 | ret = SDB_ERR_NOENTRY;
|
---|
1026 | goto out;
|
---|
1027 | }
|
---|
1028 |
|
---|
1029 | dnsdomain = ldb_msg_find_attr_as_string(msg, "trustPartner", NULL);
|
---|
1030 | if (dnsdomain == NULL) {
|
---|
1031 | krb5_clear_error_message(context);
|
---|
1032 | ret = SDB_ERR_NOENTRY;
|
---|
1033 | goto out;
|
---|
1034 | }
|
---|
1035 | partner_realm = strupper_talloc(mem_ctx, dnsdomain);
|
---|
1036 | if (partner_realm == NULL) {
|
---|
1037 | krb5_clear_error_message(context);
|
---|
1038 | ret = ENOMEM;
|
---|
1039 | goto out;
|
---|
1040 | }
|
---|
1041 |
|
---|
1042 | if (direction == INBOUND) {
|
---|
1043 | realm = our_realm;
|
---|
1044 | krbtgt_realm = partner_realm;
|
---|
1045 |
|
---|
1046 | password_val = ldb_msg_find_ldb_val(msg, "trustAuthIncoming");
|
---|
1047 | } else { /* OUTBOUND */
|
---|
1048 | realm = partner_realm;
|
---|
1049 | krbtgt_realm = our_realm;
|
---|
1050 |
|
---|
1051 | password_val = ldb_msg_find_ldb_val(msg, "trustAuthOutgoing");
|
---|
1052 | }
|
---|
1053 |
|
---|
1054 | if (password_val == NULL) {
|
---|
1055 | krb5_clear_error_message(context);
|
---|
1056 | ret = SDB_ERR_NOENTRY;
|
---|
1057 | goto out;
|
---|
1058 | }
|
---|
1059 |
|
---|
1060 | ndr_err = ndr_pull_struct_blob(password_val, mem_ctx, &password_blob,
|
---|
1061 | (ndr_pull_flags_fn_t)ndr_pull_trustAuthInOutBlob);
|
---|
1062 | if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
|
---|
1063 | krb5_clear_error_message(context);
|
---|
1064 | ret = EINVAL;
|
---|
1065 | goto out;
|
---|
1066 | }
|
---|
1067 |
|
---|
1068 | p = talloc(mem_ctx, struct samba_kdc_entry);
|
---|
1069 | if (!p) {
|
---|
1070 | ret = ENOMEM;
|
---|
1071 | goto out;
|
---|
1072 | }
|
---|
1073 |
|
---|
1074 | p->kdc_db_ctx = kdc_db_ctx;
|
---|
1075 | p->realm_dn = realm_dn;
|
---|
1076 |
|
---|
1077 | talloc_set_destructor(p, samba_kdc_entry_destructor);
|
---|
1078 |
|
---|
1079 | /* make sure we do not have bogus data in there */
|
---|
1080 | memset(&entry_ex->entry, 0, sizeof(struct sdb_entry));
|
---|
1081 |
|
---|
1082 | entry_ex->ctx = p;
|
---|
1083 |
|
---|
1084 | /* use 'whenCreated' */
|
---|
1085 | entry_ex->entry.created_by.time = ldb_msg_find_krb5time_ldap_time(msg, "whenCreated", 0);
|
---|
1086 | /* use 'kadmin' for now (needed by mit_samba) */
|
---|
1087 | ret = smb_krb5_make_principal(context,
|
---|
1088 | &entry_ex->entry.created_by.principal,
|
---|
1089 | realm, "kadmin", NULL);
|
---|
1090 | if (ret) {
|
---|
1091 | krb5_clear_error_message(context);
|
---|
1092 | goto out;
|
---|
1093 | }
|
---|
1094 |
|
---|
1095 | /*
|
---|
1096 | * We always need to generate the canonicalized principal
|
---|
1097 | * with the values of our database.
|
---|
1098 | */
|
---|
1099 | ret = smb_krb5_make_principal(context, &entry_ex->entry.principal, realm,
|
---|
1100 | "krbtgt", krbtgt_realm, NULL);
|
---|
1101 | if (ret) {
|
---|
1102 | krb5_clear_error_message(context);
|
---|
1103 | goto out;
|
---|
1104 | }
|
---|
1105 | smb_krb5_principal_set_type(context, entry_ex->entry.principal,
|
---|
1106 | KRB5_NT_SRV_INST);
|
---|
1107 |
|
---|
1108 | entry_ex->entry.valid_start = NULL;
|
---|
1109 |
|
---|
1110 | /* we need to work out if we are going to use the current or
|
---|
1111 | * the previous password hash.
|
---|
1112 | * We base this on the kvno the client passes in. If the kvno
|
---|
1113 | * passed in is equal to the current kvno in our database then
|
---|
1114 | * we use the current structure. If it is the current kvno-1,
|
---|
1115 | * then we use the previous substrucure.
|
---|
1116 | */
|
---|
1117 |
|
---|
1118 | /*
|
---|
1119 | * Windows preferrs the previous key for one hour.
|
---|
1120 | */
|
---|
1121 | tv = timeval_current();
|
---|
1122 | if (tv.tv_sec > 3600) {
|
---|
1123 | tv.tv_sec -= 3600;
|
---|
1124 | }
|
---|
1125 | an_hour_ago = timeval_to_nttime(&tv);
|
---|
1126 |
|
---|
1127 | /* first work out the current kvno */
|
---|
1128 | current_kvno = 0;
|
---|
1129 | for (i=0; i < password_blob.count; i++) {
|
---|
1130 | struct AuthenticationInformation *a =
|
---|
1131 | &password_blob.current.array[i];
|
---|
1132 |
|
---|
1133 | if (a->LastUpdateTime <= an_hour_ago) {
|
---|
1134 | preferr_current = true;
|
---|
1135 | }
|
---|
1136 |
|
---|
1137 | if (a->AuthType == TRUST_AUTH_TYPE_VERSION) {
|
---|
1138 | current_kvno = a->AuthInfo.version.version;
|
---|
1139 | }
|
---|
1140 | }
|
---|
1141 | if (current_kvno == 0) {
|
---|
1142 | previous_kvno = 255;
|
---|
1143 | } else {
|
---|
1144 | previous_kvno = current_kvno - 1;
|
---|
1145 | }
|
---|
1146 | for (i=0; i < password_blob.count; i++) {
|
---|
1147 | struct AuthenticationInformation *a =
|
---|
1148 | &password_blob.previous.array[i];
|
---|
1149 |
|
---|
1150 | if (a->AuthType == TRUST_AUTH_TYPE_VERSION) {
|
---|
1151 | previous_kvno = a->AuthInfo.version.version;
|
---|
1152 | }
|
---|
1153 | }
|
---|
1154 |
|
---|
1155 | /* work out whether we will use the previous or current
|
---|
1156 | password */
|
---|
1157 | if (password_blob.previous.count == 0) {
|
---|
1158 | /* there is no previous password */
|
---|
1159 | use_previous = false;
|
---|
1160 | } else if (!(flags & SDB_F_KVNO_SPECIFIED)) {
|
---|
1161 | /*
|
---|
1162 | * If not specified we use the lowest kvno
|
---|
1163 | * for the first hour after an update.
|
---|
1164 | */
|
---|
1165 | if (preferr_current) {
|
---|
1166 | use_previous = false;
|
---|
1167 | } else if (previous_kvno < current_kvno) {
|
---|
1168 | use_previous = true;
|
---|
1169 | } else {
|
---|
1170 | use_previous = false;
|
---|
1171 | }
|
---|
1172 | } else if (kvno == current_kvno) {
|
---|
1173 | /*
|
---|
1174 | * Exact match ...
|
---|
1175 | */
|
---|
1176 | use_previous = false;
|
---|
1177 | } else if (kvno == previous_kvno) {
|
---|
1178 | /*
|
---|
1179 | * Exact match ...
|
---|
1180 | */
|
---|
1181 | use_previous = true;
|
---|
1182 | } else {
|
---|
1183 | /*
|
---|
1184 | * Fallback to the current one for anything else
|
---|
1185 | */
|
---|
1186 | use_previous = false;
|
---|
1187 | }
|
---|
1188 |
|
---|
1189 | if (use_previous) {
|
---|
1190 | auth_array = &password_blob.previous;
|
---|
1191 | auth_kvno = &previous_kvno;
|
---|
1192 | } else {
|
---|
1193 | auth_array = &password_blob.current;
|
---|
1194 | auth_kvno = ¤t_kvno;
|
---|
1195 | }
|
---|
1196 |
|
---|
1197 | /* use the kvno the client specified, if available */
|
---|
1198 | if (flags & SDB_F_KVNO_SPECIFIED) {
|
---|
1199 | entry_ex->entry.kvno = kvno;
|
---|
1200 | } else {
|
---|
1201 | entry_ex->entry.kvno = *auth_kvno;
|
---|
1202 | }
|
---|
1203 |
|
---|
1204 | for (i=0; i < auth_array->count; i++) {
|
---|
1205 | if (auth_array->array[i].AuthType == TRUST_AUTH_TYPE_CLEAR) {
|
---|
1206 | bool ok;
|
---|
1207 |
|
---|
1208 | password_utf16 = data_blob_const(auth_array->array[i].AuthInfo.clear.password,
|
---|
1209 | auth_array->array[i].AuthInfo.clear.size);
|
---|
1210 | if (password_utf16.length == 0) {
|
---|
1211 | break;
|
---|
1212 | }
|
---|
1213 |
|
---|
1214 | if (supported_enctypes & ENC_RC4_HMAC_MD5) {
|
---|
1215 | mdfour(_password_hash.hash, password_utf16.data, password_utf16.length);
|
---|
1216 | if (password_hash == NULL) {
|
---|
1217 | num_keys += 1;
|
---|
1218 | }
|
---|
1219 | password_hash = &_password_hash;
|
---|
1220 | }
|
---|
1221 |
|
---|
1222 | if (!(supported_enctypes & (ENC_HMAC_SHA1_96_AES128|ENC_HMAC_SHA1_96_AES256))) {
|
---|
1223 | break;
|
---|
1224 | }
|
---|
1225 |
|
---|
1226 | ok = convert_string_talloc(mem_ctx,
|
---|
1227 | CH_UTF16MUNGED, CH_UTF8,
|
---|
1228 | password_utf16.data,
|
---|
1229 | password_utf16.length,
|
---|
1230 | (void *)&password_utf8.data,
|
---|
1231 | &password_utf8.length);
|
---|
1232 | if (!ok) {
|
---|
1233 | krb5_clear_error_message(context);
|
---|
1234 | ret = ENOMEM;
|
---|
1235 | goto out;
|
---|
1236 | }
|
---|
1237 |
|
---|
1238 | if (supported_enctypes & ENC_HMAC_SHA1_96_AES128) {
|
---|
1239 | num_keys += 1;
|
---|
1240 | }
|
---|
1241 | if (supported_enctypes & ENC_HMAC_SHA1_96_AES256) {
|
---|
1242 | num_keys += 1;
|
---|
1243 | }
|
---|
1244 | break;
|
---|
1245 | } else if (auth_array->array[i].AuthType == TRUST_AUTH_TYPE_NT4OWF) {
|
---|
1246 | if (supported_enctypes & ENC_RC4_HMAC_MD5) {
|
---|
1247 | password_hash = &auth_array->array[i].AuthInfo.nt4owf.password;
|
---|
1248 | num_keys += 1;
|
---|
1249 | }
|
---|
1250 | }
|
---|
1251 | }
|
---|
1252 |
|
---|
1253 | /* Must have found a cleartext or MD4 password */
|
---|
1254 | if (num_keys == 0) {
|
---|
1255 | DEBUG(1,(__location__ ": no usable key found\n"));
|
---|
1256 | krb5_clear_error_message(context);
|
---|
1257 | ret = SDB_ERR_NOENTRY;
|
---|
1258 | goto out;
|
---|
1259 | }
|
---|
1260 |
|
---|
1261 | entry_ex->entry.keys.val = calloc(num_keys, sizeof(struct sdb_key));
|
---|
1262 | if (entry_ex->entry.keys.val == NULL) {
|
---|
1263 | krb5_clear_error_message(context);
|
---|
1264 | ret = ENOMEM;
|
---|
1265 | goto out;
|
---|
1266 | }
|
---|
1267 |
|
---|
1268 | if (password_utf8.length != 0) {
|
---|
1269 | struct sdb_key key = {};
|
---|
1270 | krb5_const_principal salt_principal = entry_ex->entry.principal;
|
---|
1271 | krb5_data salt;
|
---|
1272 | krb5_data cleartext_data;
|
---|
1273 |
|
---|
1274 | cleartext_data.data = discard_const_p(char, password_utf8.data);
|
---|
1275 | cleartext_data.length = password_utf8.length;
|
---|
1276 |
|
---|
1277 | ret = smb_krb5_get_pw_salt(context,
|
---|
1278 | salt_principal,
|
---|
1279 | &salt);
|
---|
1280 | if (ret != 0) {
|
---|
1281 | goto out;
|
---|
1282 | }
|
---|
1283 |
|
---|
1284 | if (supported_enctypes & ENC_HMAC_SHA1_96_AES256) {
|
---|
1285 | ret = smb_krb5_create_key_from_string(context,
|
---|
1286 | salt_principal,
|
---|
1287 | &salt,
|
---|
1288 | &cleartext_data,
|
---|
1289 | ENCTYPE_AES256_CTS_HMAC_SHA1_96,
|
---|
1290 | &key.key);
|
---|
1291 | if (ret != 0) {
|
---|
1292 | kerberos_free_data_contents(context, &salt);
|
---|
1293 | goto out;
|
---|
1294 | }
|
---|
1295 |
|
---|
1296 | entry_ex->entry.keys.val[entry_ex->entry.keys.len] = key;
|
---|
1297 | entry_ex->entry.keys.len++;
|
---|
1298 | }
|
---|
1299 |
|
---|
1300 | if (supported_enctypes & ENC_HMAC_SHA1_96_AES128) {
|
---|
1301 | ret = smb_krb5_create_key_from_string(context,
|
---|
1302 | salt_principal,
|
---|
1303 | &salt,
|
---|
1304 | &cleartext_data,
|
---|
1305 | ENCTYPE_AES128_CTS_HMAC_SHA1_96,
|
---|
1306 | &key.key);
|
---|
1307 | if (ret != 0) {
|
---|
1308 | kerberos_free_data_contents(context, &salt);
|
---|
1309 | goto out;
|
---|
1310 | }
|
---|
1311 |
|
---|
1312 | entry_ex->entry.keys.val[entry_ex->entry.keys.len] = key;
|
---|
1313 | entry_ex->entry.keys.len++;
|
---|
1314 | }
|
---|
1315 |
|
---|
1316 | kerberos_free_data_contents(context, &salt);
|
---|
1317 | }
|
---|
1318 |
|
---|
1319 | if (password_hash != NULL) {
|
---|
1320 | struct sdb_key key = {};
|
---|
1321 |
|
---|
1322 | ret = smb_krb5_keyblock_init_contents(context,
|
---|
1323 | ENCTYPE_ARCFOUR_HMAC,
|
---|
1324 | password_hash->hash,
|
---|
1325 | sizeof(password_hash->hash),
|
---|
1326 | &key.key);
|
---|
1327 | if (ret != 0) {
|
---|
1328 | goto out;
|
---|
1329 | }
|
---|
1330 |
|
---|
1331 | entry_ex->entry.keys.val[entry_ex->entry.keys.len] = key;
|
---|
1332 | entry_ex->entry.keys.len++;
|
---|
1333 | }
|
---|
1334 |
|
---|
1335 | entry_ex->entry.flags = int2SDBFlags(0);
|
---|
1336 | entry_ex->entry.flags.immutable = 1;
|
---|
1337 | entry_ex->entry.flags.invalid = 0;
|
---|
1338 | entry_ex->entry.flags.server = 1;
|
---|
1339 | entry_ex->entry.flags.require_preauth = 1;
|
---|
1340 |
|
---|
1341 | entry_ex->entry.pw_end = NULL;
|
---|
1342 |
|
---|
1343 | entry_ex->entry.max_life = NULL;
|
---|
1344 |
|
---|
1345 | entry_ex->entry.max_renew = NULL;
|
---|
1346 |
|
---|
1347 | entry_ex->entry.etypes = malloc(sizeof(*(entry_ex->entry.etypes)));
|
---|
1348 | if (entry_ex->entry.etypes == NULL) {
|
---|
1349 | krb5_clear_error_message(context);
|
---|
1350 | ret = ENOMEM;
|
---|
1351 | goto out;
|
---|
1352 | }
|
---|
1353 | entry_ex->entry.etypes->len = entry_ex->entry.keys.len;
|
---|
1354 | entry_ex->entry.etypes->val = calloc(entry_ex->entry.etypes->len, sizeof(int));
|
---|
1355 | if (entry_ex->entry.etypes->val == NULL) {
|
---|
1356 | krb5_clear_error_message(context);
|
---|
1357 | ret = ENOMEM;
|
---|
1358 | goto out;
|
---|
1359 | }
|
---|
1360 | for (i=0; i < entry_ex->entry.etypes->len; i++) {
|
---|
1361 | entry_ex->entry.etypes->val[i] = KRB5_KEY_TYPE(&entry_ex->entry.keys.val[i].key);
|
---|
1362 | }
|
---|
1363 |
|
---|
1364 | p->msg = talloc_steal(p, msg);
|
---|
1365 |
|
---|
1366 | out:
|
---|
1367 | TALLOC_FREE(partner_realm);
|
---|
1368 |
|
---|
1369 | if (ret != 0) {
|
---|
1370 | /* This doesn't free ent itself, that is for the eventual caller to do */
|
---|
1371 | sdb_free_entry(entry_ex);
|
---|
1372 | } else {
|
---|
1373 | talloc_steal(kdc_db_ctx, entry_ex->ctx);
|
---|
1374 | }
|
---|
1375 |
|
---|
1376 | return ret;
|
---|
1377 |
|
---|
1378 | }
|
---|
1379 |
|
---|
1380 | static krb5_error_code samba_kdc_lookup_trust(krb5_context context, struct ldb_context *ldb_ctx,
|
---|
1381 | TALLOC_CTX *mem_ctx,
|
---|
1382 | const char *realm,
|
---|
1383 | struct ldb_dn *realm_dn,
|
---|
1384 | struct ldb_message **pmsg)
|
---|
1385 | {
|
---|
1386 | NTSTATUS status;
|
---|
1387 | const char * const *attrs = trust_attrs;
|
---|
1388 |
|
---|
1389 | status = dsdb_trust_search_tdo(ldb_ctx, realm, realm,
|
---|
1390 | attrs, mem_ctx, pmsg);
|
---|
1391 | if (NT_STATUS_IS_OK(status)) {
|
---|
1392 | return 0;
|
---|
1393 | } else if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
|
---|
1394 | return SDB_ERR_NOENTRY;
|
---|
1395 | } else if (NT_STATUS_EQUAL(status, NT_STATUS_NO_MEMORY)) {
|
---|
1396 | int ret = ENOMEM;
|
---|
1397 | krb5_set_error_message(context, ret, "get_sam_result_trust: out of memory");
|
---|
1398 | return ret;
|
---|
1399 | } else {
|
---|
1400 | int ret = EINVAL;
|
---|
1401 | krb5_set_error_message(context, ret, "get_sam_result_trust: %s", nt_errstr(status));
|
---|
1402 | return ret;
|
---|
1403 | }
|
---|
1404 | }
|
---|
1405 |
|
---|
1406 | static krb5_error_code samba_kdc_lookup_client(krb5_context context,
|
---|
1407 | struct samba_kdc_db_context *kdc_db_ctx,
|
---|
1408 | TALLOC_CTX *mem_ctx,
|
---|
1409 | krb5_const_principal principal,
|
---|
1410 | const char **attrs,
|
---|
1411 | struct ldb_dn **realm_dn,
|
---|
1412 | struct ldb_message **msg)
|
---|
1413 | {
|
---|
1414 | NTSTATUS nt_status;
|
---|
1415 | char *principal_string = NULL;
|
---|
1416 |
|
---|
1417 | if (smb_krb5_principal_get_type(context, principal) == KRB5_NT_ENTERPRISE_PRINCIPAL) {
|
---|
1418 | principal_string = smb_krb5_principal_get_comp_string(mem_ctx, context,
|
---|
1419 | principal, 0);
|
---|
1420 | if (principal_string == NULL) {
|
---|
1421 | return ENOMEM;
|
---|
1422 | }
|
---|
1423 | } else {
|
---|
1424 | char *principal_string_m = NULL;
|
---|
1425 | krb5_error_code ret;
|
---|
1426 |
|
---|
1427 | ret = krb5_unparse_name(context, principal, &principal_string_m);
|
---|
1428 | if (ret != 0) {
|
---|
1429 | return ret;
|
---|
1430 | }
|
---|
1431 |
|
---|
1432 | principal_string = talloc_strdup(mem_ctx, principal_string_m);
|
---|
1433 | SAFE_FREE(principal_string_m);
|
---|
1434 | if (principal_string == NULL) {
|
---|
1435 | return ENOMEM;
|
---|
1436 | }
|
---|
1437 | }
|
---|
1438 |
|
---|
1439 | nt_status = sam_get_results_principal(kdc_db_ctx->samdb,
|
---|
1440 | mem_ctx, principal_string, attrs,
|
---|
1441 | realm_dn, msg);
|
---|
1442 | if (NT_STATUS_EQUAL(nt_status, NT_STATUS_NO_SUCH_USER)) {
|
---|
1443 | krb5_principal fallback_principal = NULL;
|
---|
1444 | unsigned int num_comp;
|
---|
1445 | char *fallback_realm = NULL;
|
---|
1446 | char *fallback_account = NULL;
|
---|
1447 | krb5_error_code ret;
|
---|
1448 |
|
---|
1449 | ret = krb5_parse_name(context, principal_string,
|
---|
1450 | &fallback_principal);
|
---|
1451 | TALLOC_FREE(principal_string);
|
---|
1452 | if (ret != 0) {
|
---|
1453 | return ret;
|
---|
1454 | }
|
---|
1455 |
|
---|
1456 | num_comp = krb5_princ_size(context, fallback_principal);
|
---|
1457 | fallback_realm = smb_krb5_principal_get_realm(context,
|
---|
1458 | fallback_principal);
|
---|
1459 | if (fallback_realm == NULL) {
|
---|
1460 | krb5_free_principal(context, fallback_principal);
|
---|
1461 | return ENOMEM;
|
---|
1462 | }
|
---|
1463 |
|
---|
1464 | if (num_comp == 1) {
|
---|
1465 | size_t len;
|
---|
1466 |
|
---|
1467 | fallback_account = smb_krb5_principal_get_comp_string(mem_ctx,
|
---|
1468 | context, fallback_principal, 0);
|
---|
1469 | if (fallback_account == NULL) {
|
---|
1470 | krb5_free_principal(context, fallback_principal);
|
---|
1471 | SAFE_FREE(fallback_realm);
|
---|
1472 | return ENOMEM;
|
---|
1473 | }
|
---|
1474 |
|
---|
1475 | len = strlen(fallback_account);
|
---|
1476 | if (len >= 2 && fallback_account[len - 1] == '$') {
|
---|
1477 | TALLOC_FREE(fallback_account);
|
---|
1478 | }
|
---|
1479 | }
|
---|
1480 | krb5_free_principal(context, fallback_principal);
|
---|
1481 | fallback_principal = NULL;
|
---|
1482 |
|
---|
1483 | if (fallback_account != NULL) {
|
---|
1484 | char *with_dollar;
|
---|
1485 |
|
---|
1486 | with_dollar = talloc_asprintf(mem_ctx, "%s$",
|
---|
1487 | fallback_account);
|
---|
1488 | if (with_dollar == NULL) {
|
---|
1489 | SAFE_FREE(fallback_realm);
|
---|
1490 | return ENOMEM;
|
---|
1491 | }
|
---|
1492 | TALLOC_FREE(fallback_account);
|
---|
1493 |
|
---|
1494 | ret = smb_krb5_make_principal(context,
|
---|
1495 | &fallback_principal,
|
---|
1496 | fallback_realm,
|
---|
1497 | with_dollar, NULL);
|
---|
1498 | TALLOC_FREE(with_dollar);
|
---|
1499 | if (ret != 0) {
|
---|
1500 | SAFE_FREE(fallback_realm);
|
---|
1501 | return ret;
|
---|
1502 | }
|
---|
1503 | }
|
---|
1504 | SAFE_FREE(fallback_realm);
|
---|
1505 |
|
---|
1506 | if (fallback_principal != NULL) {
|
---|
1507 | char *fallback_string = NULL;
|
---|
1508 |
|
---|
1509 | ret = krb5_unparse_name(context,
|
---|
1510 | fallback_principal,
|
---|
1511 | &fallback_string);
|
---|
1512 | if (ret != 0) {
|
---|
1513 | krb5_free_principal(context, fallback_principal);
|
---|
1514 | return ret;
|
---|
1515 | }
|
---|
1516 |
|
---|
1517 | nt_status = sam_get_results_principal(kdc_db_ctx->samdb,
|
---|
1518 | mem_ctx,
|
---|
1519 | fallback_string,
|
---|
1520 | attrs,
|
---|
1521 | realm_dn, msg);
|
---|
1522 | SAFE_FREE(fallback_string);
|
---|
1523 | }
|
---|
1524 | krb5_free_principal(context, fallback_principal);
|
---|
1525 | fallback_principal = NULL;
|
---|
1526 | }
|
---|
1527 | TALLOC_FREE(principal_string);
|
---|
1528 |
|
---|
1529 | if (NT_STATUS_EQUAL(nt_status, NT_STATUS_NO_SUCH_USER)) {
|
---|
1530 | return SDB_ERR_NOENTRY;
|
---|
1531 | } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_NO_MEMORY)) {
|
---|
1532 | return ENOMEM;
|
---|
1533 | } else if (!NT_STATUS_IS_OK(nt_status)) {
|
---|
1534 | return EINVAL;
|
---|
1535 | }
|
---|
1536 |
|
---|
1537 | return 0;
|
---|
1538 | }
|
---|
1539 |
|
---|
1540 | static krb5_error_code samba_kdc_fetch_client(krb5_context context,
|
---|
1541 | struct samba_kdc_db_context *kdc_db_ctx,
|
---|
1542 | TALLOC_CTX *mem_ctx,
|
---|
1543 | krb5_const_principal principal,
|
---|
1544 | unsigned flags,
|
---|
1545 | struct sdb_entry_ex *entry_ex) {
|
---|
1546 | struct ldb_dn *realm_dn;
|
---|
1547 | krb5_error_code ret;
|
---|
1548 | struct ldb_message *msg = NULL;
|
---|
1549 |
|
---|
1550 | ret = samba_kdc_lookup_client(context, kdc_db_ctx,
|
---|
1551 | mem_ctx, principal, user_attrs,
|
---|
1552 | &realm_dn, &msg);
|
---|
1553 | if (ret != 0) {
|
---|
1554 | return ret;
|
---|
1555 | }
|
---|
1556 |
|
---|
1557 | ret = samba_kdc_message2entry(context, kdc_db_ctx, mem_ctx,
|
---|
1558 | principal, SAMBA_KDC_ENT_TYPE_CLIENT,
|
---|
1559 | flags,
|
---|
1560 | realm_dn, msg, entry_ex);
|
---|
1561 | return ret;
|
---|
1562 | }
|
---|
1563 |
|
---|
1564 | static krb5_error_code samba_kdc_fetch_krbtgt(krb5_context context,
|
---|
1565 | struct samba_kdc_db_context *kdc_db_ctx,
|
---|
1566 | TALLOC_CTX *mem_ctx,
|
---|
1567 | krb5_const_principal principal,
|
---|
1568 | unsigned flags,
|
---|
1569 | uint32_t kvno,
|
---|
1570 | struct sdb_entry_ex *entry_ex)
|
---|
1571 | {
|
---|
1572 | struct loadparm_context *lp_ctx = kdc_db_ctx->lp_ctx;
|
---|
1573 | krb5_error_code ret;
|
---|
1574 | struct ldb_message *msg = NULL;
|
---|
1575 | struct ldb_dn *realm_dn = ldb_get_default_basedn(kdc_db_ctx->samdb);
|
---|
1576 | char *realm_from_princ, *realm_from_princ_malloc;
|
---|
1577 | char *realm_princ_comp = smb_krb5_principal_get_comp_string(mem_ctx, context, principal, 1);
|
---|
1578 |
|
---|
1579 | realm_from_princ_malloc = smb_krb5_principal_get_realm(context, principal);
|
---|
1580 | if (realm_from_princ_malloc == NULL) {
|
---|
1581 | /* can't happen */
|
---|
1582 | return SDB_ERR_NOENTRY;
|
---|
1583 | }
|
---|
1584 | realm_from_princ = talloc_strdup(mem_ctx, realm_from_princ_malloc);
|
---|
1585 | free(realm_from_princ_malloc);
|
---|
1586 | if (realm_from_princ == NULL) {
|
---|
1587 | return SDB_ERR_NOENTRY;
|
---|
1588 | }
|
---|
1589 |
|
---|
1590 | if (krb5_princ_size(context, principal) != 2
|
---|
1591 | || (principal_comp_strcmp(context, principal, 0, KRB5_TGS_NAME) != 0)) {
|
---|
1592 | /* Not a krbtgt */
|
---|
1593 | return SDB_ERR_NOENTRY;
|
---|
1594 | }
|
---|
1595 |
|
---|
1596 | /* krbtgt case. Either us or a trusted realm */
|
---|
1597 |
|
---|
1598 | if (lpcfg_is_my_domain_or_realm(lp_ctx, realm_from_princ)
|
---|
1599 | && lpcfg_is_my_domain_or_realm(lp_ctx, realm_princ_comp)) {
|
---|
1600 | /* us, or someone quite like us */
|
---|
1601 | /* Cludge, cludge cludge. If the realm part of krbtgt/realm,
|
---|
1602 | * is in our db, then direct the caller at our primary
|
---|
1603 | * krbtgt */
|
---|
1604 |
|
---|
1605 | int lret;
|
---|
1606 | unsigned int krbtgt_number;
|
---|
1607 | /* w2k8r2 sometimes gives us a kvno of 255 for inter-domain
|
---|
1608 | trust tickets. We don't yet know what this means, but we do
|
---|
1609 | seem to need to treat it as unspecified */
|
---|
1610 | if (flags & SDB_F_KVNO_SPECIFIED) {
|
---|
1611 | krbtgt_number = SAMBA_KVNO_GET_KRBTGT(kvno);
|
---|
1612 | if (kdc_db_ctx->rodc) {
|
---|
1613 | if (krbtgt_number != kdc_db_ctx->my_krbtgt_number) {
|
---|
1614 | return SDB_ERR_NOT_FOUND_HERE;
|
---|
1615 | }
|
---|
1616 | }
|
---|
1617 | } else {
|
---|
1618 | krbtgt_number = kdc_db_ctx->my_krbtgt_number;
|
---|
1619 | }
|
---|
1620 |
|
---|
1621 | if (krbtgt_number == kdc_db_ctx->my_krbtgt_number) {
|
---|
1622 | lret = dsdb_search_one(kdc_db_ctx->samdb, mem_ctx,
|
---|
1623 | &msg, kdc_db_ctx->krbtgt_dn, LDB_SCOPE_BASE,
|
---|
1624 | krbtgt_attrs, DSDB_SEARCH_NO_GLOBAL_CATALOG,
|
---|
1625 | "(objectClass=user)");
|
---|
1626 | } else {
|
---|
1627 | /* We need to look up an RODC krbtgt (perhaps
|
---|
1628 | * ours, if we are an RODC, perhaps another
|
---|
1629 | * RODC if we are a read-write DC */
|
---|
1630 | lret = dsdb_search_one(kdc_db_ctx->samdb, mem_ctx,
|
---|
1631 | &msg, realm_dn, LDB_SCOPE_SUBTREE,
|
---|
1632 | krbtgt_attrs,
|
---|
1633 | DSDB_SEARCH_SHOW_EXTENDED_DN | DSDB_SEARCH_NO_GLOBAL_CATALOG,
|
---|
1634 | "(&(objectClass=user)(msDS-SecondaryKrbTgtNumber=%u))", (unsigned)(krbtgt_number));
|
---|
1635 | }
|
---|
1636 |
|
---|
1637 | if (lret == LDB_ERR_NO_SUCH_OBJECT) {
|
---|
1638 | krb5_warnx(context, "samba_kdc_fetch: could not find KRBTGT number %u in DB!",
|
---|
1639 | (unsigned)(krbtgt_number));
|
---|
1640 | krb5_set_error_message(context, SDB_ERR_NOENTRY,
|
---|
1641 | "samba_kdc_fetch: could not find KRBTGT number %u in DB!",
|
---|
1642 | (unsigned)(krbtgt_number));
|
---|
1643 | return SDB_ERR_NOENTRY;
|
---|
1644 | } else if (lret != LDB_SUCCESS) {
|
---|
1645 | krb5_warnx(context, "samba_kdc_fetch: could not find KRBTGT number %u in DB!",
|
---|
1646 | (unsigned)(krbtgt_number));
|
---|
1647 | krb5_set_error_message(context, SDB_ERR_NOENTRY,
|
---|
1648 | "samba_kdc_fetch: could not find KRBTGT number %u in DB!",
|
---|
1649 | (unsigned)(krbtgt_number));
|
---|
1650 | return SDB_ERR_NOENTRY;
|
---|
1651 | }
|
---|
1652 |
|
---|
1653 | ret = samba_kdc_message2entry(context, kdc_db_ctx, mem_ctx,
|
---|
1654 | principal, SAMBA_KDC_ENT_TYPE_KRBTGT,
|
---|
1655 | flags, realm_dn, msg, entry_ex);
|
---|
1656 | if (ret != 0) {
|
---|
1657 | krb5_warnx(context, "samba_kdc_fetch: self krbtgt message2entry failed");
|
---|
1658 | }
|
---|
1659 | return ret;
|
---|
1660 |
|
---|
1661 | } else {
|
---|
1662 | enum trust_direction direction = UNKNOWN;
|
---|
1663 | const char *realm = NULL;
|
---|
1664 |
|
---|
1665 | /* Either an inbound or outbound trust */
|
---|
1666 |
|
---|
1667 | if (strcasecmp(lpcfg_realm(lp_ctx), realm_from_princ) == 0) {
|
---|
1668 | /* look for inbound trust */
|
---|
1669 | direction = INBOUND;
|
---|
1670 | realm = realm_princ_comp;
|
---|
1671 | } else if (principal_comp_strcasecmp(context, principal, 1, lpcfg_realm(lp_ctx)) == 0) {
|
---|
1672 | /* look for outbound trust */
|
---|
1673 | direction = OUTBOUND;
|
---|
1674 | realm = realm_from_princ;
|
---|
1675 | } else {
|
---|
1676 | krb5_warnx(context, "samba_kdc_fetch: not our realm for trusts ('%s', '%s')",
|
---|
1677 | realm_from_princ,
|
---|
1678 | realm_princ_comp);
|
---|
1679 | krb5_set_error_message(context, SDB_ERR_NOENTRY, "samba_kdc_fetch: not our realm for trusts ('%s', '%s')",
|
---|
1680 | realm_from_princ,
|
---|
1681 | realm_princ_comp);
|
---|
1682 | return SDB_ERR_NOENTRY;
|
---|
1683 | }
|
---|
1684 |
|
---|
1685 | /* Trusted domains are under CN=system */
|
---|
1686 |
|
---|
1687 | ret = samba_kdc_lookup_trust(context, kdc_db_ctx->samdb,
|
---|
1688 | mem_ctx,
|
---|
1689 | realm, realm_dn, &msg);
|
---|
1690 |
|
---|
1691 | if (ret != 0) {
|
---|
1692 | krb5_warnx(context, "samba_kdc_fetch: could not find principal in DB");
|
---|
1693 | krb5_set_error_message(context, ret, "samba_kdc_fetch: could not find principal in DB");
|
---|
1694 | return ret;
|
---|
1695 | }
|
---|
1696 |
|
---|
1697 | ret = samba_kdc_trust_message2entry(context, kdc_db_ctx, mem_ctx,
|
---|
1698 | principal, direction,
|
---|
1699 | realm_dn, flags, kvno, msg, entry_ex);
|
---|
1700 | if (ret != 0) {
|
---|
1701 | krb5_warnx(context, "samba_kdc_fetch: trust_message2entry failed for %s",
|
---|
1702 | ldb_dn_get_linearized(msg->dn));
|
---|
1703 | krb5_set_error_message(context, ret, "samba_kdc_fetch: "
|
---|
1704 | "trust_message2entry failed for %s",
|
---|
1705 | ldb_dn_get_linearized(msg->dn));
|
---|
1706 | }
|
---|
1707 | return ret;
|
---|
1708 | }
|
---|
1709 |
|
---|
1710 | }
|
---|
1711 |
|
---|
1712 | static krb5_error_code samba_kdc_lookup_server(krb5_context context,
|
---|
1713 | struct samba_kdc_db_context *kdc_db_ctx,
|
---|
1714 | TALLOC_CTX *mem_ctx,
|
---|
1715 | krb5_const_principal principal,
|
---|
1716 | unsigned flags,
|
---|
1717 | const char **attrs,
|
---|
1718 | struct ldb_dn **realm_dn,
|
---|
1719 | struct ldb_message **msg)
|
---|
1720 | {
|
---|
1721 | krb5_error_code ret;
|
---|
1722 | if ((smb_krb5_principal_get_type(context, principal) != KRB5_NT_ENTERPRISE_PRINCIPAL)
|
---|
1723 | && krb5_princ_size(context, principal) >= 2) {
|
---|
1724 | /* 'normal server' case */
|
---|
1725 | int ldb_ret;
|
---|
1726 | NTSTATUS nt_status;
|
---|
1727 | struct ldb_dn *user_dn;
|
---|
1728 | char *principal_string;
|
---|
1729 |
|
---|
1730 | ret = krb5_unparse_name_flags(context, principal,
|
---|
1731 | KRB5_PRINCIPAL_UNPARSE_NO_REALM,
|
---|
1732 | &principal_string);
|
---|
1733 | if (ret != 0) {
|
---|
1734 | return ret;
|
---|
1735 | }
|
---|
1736 |
|
---|
1737 | /* At this point we may find the host is known to be
|
---|
1738 | * in a different realm, so we should generate a
|
---|
1739 | * referral instead */
|
---|
1740 | nt_status = crack_service_principal_name(kdc_db_ctx->samdb,
|
---|
1741 | mem_ctx, principal_string,
|
---|
1742 | &user_dn, realm_dn);
|
---|
1743 | free(principal_string);
|
---|
1744 |
|
---|
1745 | if (!NT_STATUS_IS_OK(nt_status)) {
|
---|
1746 | return SDB_ERR_NOENTRY;
|
---|
1747 | }
|
---|
1748 |
|
---|
1749 | ldb_ret = dsdb_search_one(kdc_db_ctx->samdb,
|
---|
1750 | mem_ctx,
|
---|
1751 | msg, user_dn, LDB_SCOPE_BASE,
|
---|
1752 | attrs,
|
---|
1753 | DSDB_SEARCH_SHOW_EXTENDED_DN | DSDB_SEARCH_NO_GLOBAL_CATALOG,
|
---|
1754 | "(objectClass=*)");
|
---|
1755 | if (ldb_ret != LDB_SUCCESS) {
|
---|
1756 | return SDB_ERR_NOENTRY;
|
---|
1757 | }
|
---|
1758 | return 0;
|
---|
1759 | } else if (!(flags & SDB_F_FOR_AS_REQ)
|
---|
1760 | && smb_krb5_principal_get_type(context, principal) == KRB5_NT_ENTERPRISE_PRINCIPAL) {
|
---|
1761 | /*
|
---|
1762 | * The behaviour of accepting an
|
---|
1763 | * KRB5_NT_ENTERPRISE_PRINCIPAL server principal
|
---|
1764 | * containing a UPN only applies to TGS-REQ packets,
|
---|
1765 | * not AS-REQ packets.
|
---|
1766 | */
|
---|
1767 | return samba_kdc_lookup_client(context, kdc_db_ctx,
|
---|
1768 | mem_ctx, principal, attrs,
|
---|
1769 | realm_dn, msg);
|
---|
1770 | } else {
|
---|
1771 | /*
|
---|
1772 | * This case is for:
|
---|
1773 | * - the AS-REQ, where we only accept
|
---|
1774 | * samAccountName based lookups for the server, no
|
---|
1775 | * matter if the name is an
|
---|
1776 | * KRB5_NT_ENTERPRISE_PRINCIPAL or not
|
---|
1777 | * - for the TGS-REQ when we are not given an
|
---|
1778 | * KRB5_NT_ENTERPRISE_PRINCIPAL, which also must
|
---|
1779 | * only lookup samAccountName based names.
|
---|
1780 | */
|
---|
1781 | int lret;
|
---|
1782 | char *short_princ;
|
---|
1783 | krb5_principal enterprise_principal = NULL;
|
---|
1784 | krb5_const_principal used_principal = NULL;
|
---|
1785 | char *name1 = NULL;
|
---|
1786 | size_t len1 = 0;
|
---|
1787 | char *filter = NULL;
|
---|
1788 |
|
---|
1789 | if (smb_krb5_principal_get_type(context, principal) == KRB5_NT_ENTERPRISE_PRINCIPAL) {
|
---|
1790 | char *str = NULL;
|
---|
1791 | /* Need to reparse the enterprise principal to find the real target */
|
---|
1792 | if (krb5_princ_size(context, principal) != 1) {
|
---|
1793 | ret = KRB5_PARSE_MALFORMED;
|
---|
1794 | krb5_set_error_message(context, ret, "samba_kdc_lookup_server: request for an "
|
---|
1795 | "enterprise principal with wrong (%d) number of components",
|
---|
1796 | krb5_princ_size(context, principal));
|
---|
1797 | return ret;
|
---|
1798 | }
|
---|
1799 | str = smb_krb5_principal_get_comp_string(mem_ctx, context, principal, 0);
|
---|
1800 | if (str == NULL) {
|
---|
1801 | return KRB5_PARSE_MALFORMED;
|
---|
1802 | }
|
---|
1803 | ret = krb5_parse_name(context, str,
|
---|
1804 | &enterprise_principal);
|
---|
1805 | talloc_free(str);
|
---|
1806 | if (ret) {
|
---|
1807 | return ret;
|
---|
1808 | }
|
---|
1809 | used_principal = enterprise_principal;
|
---|
1810 | } else {
|
---|
1811 | used_principal = principal;
|
---|
1812 | }
|
---|
1813 |
|
---|
1814 | /* server as client principal case, but we must not lookup userPrincipalNames */
|
---|
1815 | *realm_dn = ldb_get_default_basedn(kdc_db_ctx->samdb);
|
---|
1816 |
|
---|
1817 | /* TODO: Check if it is our realm, otherwise give referral */
|
---|
1818 |
|
---|
1819 | ret = krb5_unparse_name_flags(context, used_principal,
|
---|
1820 | KRB5_PRINCIPAL_UNPARSE_NO_REALM |
|
---|
1821 | KRB5_PRINCIPAL_UNPARSE_DISPLAY,
|
---|
1822 | &short_princ);
|
---|
1823 | used_principal = NULL;
|
---|
1824 | krb5_free_principal(context, enterprise_principal);
|
---|
1825 | enterprise_principal = NULL;
|
---|
1826 |
|
---|
1827 | if (ret != 0) {
|
---|
1828 | krb5_set_error_message(context, ret, "samba_kdc_lookup_principal: could not parse principal");
|
---|
1829 | krb5_warnx(context, "samba_kdc_lookup_principal: could not parse principal");
|
---|
1830 | return ret;
|
---|
1831 | }
|
---|
1832 |
|
---|
1833 | name1 = ldb_binary_encode_string(mem_ctx, short_princ);
|
---|
1834 | SAFE_FREE(short_princ);
|
---|
1835 | if (name1 == NULL) {
|
---|
1836 | return ENOMEM;
|
---|
1837 | }
|
---|
1838 | len1 = strlen(name1);
|
---|
1839 | if (len1 >= 1 && name1[len1 - 1] != '$') {
|
---|
1840 | filter = talloc_asprintf(mem_ctx,
|
---|
1841 | "(&(objectClass=user)(|(samAccountName=%s)(samAccountName=%s$)))",
|
---|
1842 | name1, name1);
|
---|
1843 | if (filter == NULL) {
|
---|
1844 | return ENOMEM;
|
---|
1845 | }
|
---|
1846 | } else {
|
---|
1847 | filter = talloc_asprintf(mem_ctx,
|
---|
1848 | "(&(objectClass=user)(samAccountName=%s))",
|
---|
1849 | name1);
|
---|
1850 | if (filter == NULL) {
|
---|
1851 | return ENOMEM;
|
---|
1852 | }
|
---|
1853 | }
|
---|
1854 |
|
---|
1855 | lret = dsdb_search_one(kdc_db_ctx->samdb, mem_ctx, msg,
|
---|
1856 | *realm_dn, LDB_SCOPE_SUBTREE,
|
---|
1857 | attrs,
|
---|
1858 | DSDB_SEARCH_SHOW_EXTENDED_DN | DSDB_SEARCH_NO_GLOBAL_CATALOG,
|
---|
1859 | "%s", filter);
|
---|
1860 | if (lret == LDB_ERR_NO_SUCH_OBJECT) {
|
---|
1861 | DEBUG(10, ("Failed to find an entry for %s filter:%s\n",
|
---|
1862 | name1, filter));
|
---|
1863 | return SDB_ERR_NOENTRY;
|
---|
1864 | }
|
---|
1865 | if (lret == LDB_ERR_CONSTRAINT_VIOLATION) {
|
---|
1866 | DEBUG(10, ("Failed to find unique entry for %s filter:%s\n",
|
---|
1867 | name1, filter));
|
---|
1868 | return SDB_ERR_NOENTRY;
|
---|
1869 | }
|
---|
1870 | if (lret != LDB_SUCCESS) {
|
---|
1871 | DEBUG(0, ("Failed single search for %s - %s\n",
|
---|
1872 | name1, ldb_errstring(kdc_db_ctx->samdb)));
|
---|
1873 | return SDB_ERR_NOENTRY;
|
---|
1874 | }
|
---|
1875 | return 0;
|
---|
1876 | }
|
---|
1877 | return SDB_ERR_NOENTRY;
|
---|
1878 | }
|
---|
1879 |
|
---|
1880 |
|
---|
1881 |
|
---|
1882 | static krb5_error_code samba_kdc_fetch_server(krb5_context context,
|
---|
1883 | struct samba_kdc_db_context *kdc_db_ctx,
|
---|
1884 | TALLOC_CTX *mem_ctx,
|
---|
1885 | krb5_const_principal principal,
|
---|
1886 | unsigned flags,
|
---|
1887 | struct sdb_entry_ex *entry_ex)
|
---|
1888 | {
|
---|
1889 | krb5_error_code ret;
|
---|
1890 | struct ldb_dn *realm_dn;
|
---|
1891 | struct ldb_message *msg;
|
---|
1892 |
|
---|
1893 | ret = samba_kdc_lookup_server(context, kdc_db_ctx, mem_ctx, principal,
|
---|
1894 | flags, server_attrs, &realm_dn, &msg);
|
---|
1895 | if (ret != 0) {
|
---|
1896 | return ret;
|
---|
1897 | }
|
---|
1898 |
|
---|
1899 | ret = samba_kdc_message2entry(context, kdc_db_ctx, mem_ctx,
|
---|
1900 | principal, SAMBA_KDC_ENT_TYPE_SERVER,
|
---|
1901 | flags,
|
---|
1902 | realm_dn, msg, entry_ex);
|
---|
1903 | if (ret != 0) {
|
---|
1904 | krb5_warnx(context, "samba_kdc_fetch: message2entry failed");
|
---|
1905 | }
|
---|
1906 |
|
---|
1907 | return ret;
|
---|
1908 | }
|
---|
1909 |
|
---|
1910 | static krb5_error_code samba_kdc_lookup_realm(krb5_context context,
|
---|
1911 | struct samba_kdc_db_context *kdc_db_ctx,
|
---|
1912 | TALLOC_CTX *mem_ctx,
|
---|
1913 | krb5_const_principal principal,
|
---|
1914 | unsigned flags,
|
---|
1915 | struct sdb_entry_ex *entry_ex)
|
---|
1916 | {
|
---|
1917 | TALLOC_CTX *frame = talloc_stackframe();
|
---|
1918 | NTSTATUS status;
|
---|
1919 | krb5_error_code ret;
|
---|
1920 | char *_realm = NULL;
|
---|
1921 | bool check_realm = false;
|
---|
1922 | const char *realm = NULL;
|
---|
1923 | struct dsdb_trust_routing_table *trt = NULL;
|
---|
1924 | const struct lsa_TrustDomainInfoInfoEx *tdo = NULL;
|
---|
1925 | unsigned int num_comp;
|
---|
1926 | bool ok;
|
---|
1927 | char *upper = NULL;
|
---|
1928 |
|
---|
1929 | num_comp = krb5_princ_size(context, principal);
|
---|
1930 |
|
---|
1931 | if (flags & SDB_F_GET_CLIENT) {
|
---|
1932 | if (flags & SDB_F_FOR_AS_REQ) {
|
---|
1933 | check_realm = true;
|
---|
1934 | }
|
---|
1935 | }
|
---|
1936 | if (flags & SDB_F_GET_SERVER) {
|
---|
1937 | if (flags & SDB_F_FOR_TGS_REQ) {
|
---|
1938 | check_realm = true;
|
---|
1939 | }
|
---|
1940 | }
|
---|
1941 |
|
---|
1942 | if (!check_realm) {
|
---|
1943 | TALLOC_FREE(frame);
|
---|
1944 | return 0;
|
---|
1945 | }
|
---|
1946 |
|
---|
1947 | _realm = smb_krb5_principal_get_realm(context, principal);
|
---|
1948 | if (_realm == NULL) {
|
---|
1949 | TALLOC_FREE(frame);
|
---|
1950 | return ENOMEM;
|
---|
1951 | }
|
---|
1952 |
|
---|
1953 | /*
|
---|
1954 | * The requested realm needs to be our own
|
---|
1955 | */
|
---|
1956 | ok = lpcfg_is_my_domain_or_realm(kdc_db_ctx->lp_ctx, _realm);
|
---|
1957 | if (!ok) {
|
---|
1958 | /*
|
---|
1959 | * The request is not for us...
|
---|
1960 | */
|
---|
1961 | SAFE_FREE(_realm);
|
---|
1962 | TALLOC_FREE(frame);
|
---|
1963 | return SDB_ERR_NOENTRY;
|
---|
1964 | }
|
---|
1965 |
|
---|
1966 | realm = talloc_strdup(frame, _realm);
|
---|
1967 | SAFE_FREE(_realm);
|
---|
1968 | if (realm == NULL) {
|
---|
1969 | TALLOC_FREE(frame);
|
---|
1970 | return ENOMEM;
|
---|
1971 | }
|
---|
1972 |
|
---|
1973 | if (smb_krb5_principal_get_type(context, principal) == KRB5_NT_ENTERPRISE_PRINCIPAL) {
|
---|
1974 | char *principal_string = NULL;
|
---|
1975 | krb5_principal enterprise_principal = NULL;
|
---|
1976 | char *enterprise_realm = NULL;
|
---|
1977 |
|
---|
1978 | if (num_comp != 1) {
|
---|
1979 | TALLOC_FREE(frame);
|
---|
1980 | return SDB_ERR_NOENTRY;
|
---|
1981 | }
|
---|
1982 |
|
---|
1983 | principal_string = smb_krb5_principal_get_comp_string(frame, context,
|
---|
1984 | principal, 0);
|
---|
1985 | if (principal_string == NULL) {
|
---|
1986 | TALLOC_FREE(frame);
|
---|
1987 | return ENOMEM;
|
---|
1988 | }
|
---|
1989 |
|
---|
1990 | ret = krb5_parse_name(context, principal_string,
|
---|
1991 | &enterprise_principal);
|
---|
1992 | TALLOC_FREE(principal_string);
|
---|
1993 | if (ret) {
|
---|
1994 | TALLOC_FREE(frame);
|
---|
1995 | return ret;
|
---|
1996 | }
|
---|
1997 |
|
---|
1998 | enterprise_realm = smb_krb5_principal_get_realm(context,
|
---|
1999 | enterprise_principal);
|
---|
2000 | krb5_free_principal(context, enterprise_principal);
|
---|
2001 | if (enterprise_realm != NULL) {
|
---|
2002 | realm = talloc_strdup(frame, enterprise_realm);
|
---|
2003 | SAFE_FREE(enterprise_realm);
|
---|
2004 | if (realm == NULL) {
|
---|
2005 | TALLOC_FREE(frame);
|
---|
2006 | return ENOMEM;
|
---|
2007 | }
|
---|
2008 | }
|
---|
2009 | }
|
---|
2010 |
|
---|
2011 | if (flags & SDB_F_GET_SERVER) {
|
---|
2012 | char *service_realm = NULL;
|
---|
2013 |
|
---|
2014 | ret = principal_comp_strcmp(context, principal, 0, KRB5_TGS_NAME);
|
---|
2015 | if (ret == 0) {
|
---|
2016 | /*
|
---|
2017 | * we need to search krbtgt/ locally
|
---|
2018 | */
|
---|
2019 | TALLOC_FREE(frame);
|
---|
2020 | return 0;
|
---|
2021 | }
|
---|
2022 |
|
---|
2023 | /*
|
---|
2024 | * We need to check the last component against the routing table.
|
---|
2025 | *
|
---|
2026 | * Note this works only with 2 or 3 component principals, e.g:
|
---|
2027 | *
|
---|
2028 | * servicePrincipalName: ldap/W2K8R2-219.bla.base
|
---|
2029 | * servicePrincipalName: ldap/W2K8R2-219.bla.base/bla.base
|
---|
2030 | * servicePrincipalName: ldap/W2K8R2-219.bla.base/ForestDnsZones.bla.base
|
---|
2031 | * servicePrincipalName: ldap/W2K8R2-219.bla.base/DomainDnsZones.bla.base
|
---|
2032 | */
|
---|
2033 |
|
---|
2034 | if (num_comp == 2 || num_comp == 3) {
|
---|
2035 | service_realm = smb_krb5_principal_get_comp_string(frame,
|
---|
2036 | context,
|
---|
2037 | principal,
|
---|
2038 | num_comp - 1);
|
---|
2039 | }
|
---|
2040 |
|
---|
2041 | if (service_realm != NULL) {
|
---|
2042 | realm = service_realm;
|
---|
2043 | }
|
---|
2044 | }
|
---|
2045 |
|
---|
2046 | ok = lpcfg_is_my_domain_or_realm(kdc_db_ctx->lp_ctx, realm);
|
---|
2047 | if (ok) {
|
---|
2048 | /*
|
---|
2049 | * skip the expensive routing lookup
|
---|
2050 | */
|
---|
2051 | TALLOC_FREE(frame);
|
---|
2052 | return 0;
|
---|
2053 | }
|
---|
2054 |
|
---|
2055 | status = dsdb_trust_routing_table_load(kdc_db_ctx->samdb,
|
---|
2056 | frame, &trt);
|
---|
2057 | if (!NT_STATUS_IS_OK(status)) {
|
---|
2058 | TALLOC_FREE(frame);
|
---|
2059 | return EINVAL;
|
---|
2060 | }
|
---|
2061 |
|
---|
2062 | tdo = dsdb_trust_routing_by_name(trt, realm);
|
---|
2063 | if (tdo == NULL) {
|
---|
2064 | /*
|
---|
2065 | * This principal has to be local
|
---|
2066 | */
|
---|
2067 | TALLOC_FREE(frame);
|
---|
2068 | return 0;
|
---|
2069 | }
|
---|
2070 |
|
---|
2071 | if (tdo->trust_attributes & LSA_TRUST_ATTRIBUTE_WITHIN_FOREST) {
|
---|
2072 | /*
|
---|
2073 | * TODO: handle the routing within the forest
|
---|
2074 | *
|
---|
2075 | * This should likely be handled in
|
---|
2076 | * samba_kdc_message2entry() in case we're
|
---|
2077 | * a global catalog. We'd need to check
|
---|
2078 | * if realm_dn is our own domain and derive
|
---|
2079 | * the dns domain name from realm_dn and check that
|
---|
2080 | * against the routing table or fallback to
|
---|
2081 | * the tdo we found here.
|
---|
2082 | *
|
---|
2083 | * But for now we don't support multiple domains
|
---|
2084 | * in our forest correctly anyway.
|
---|
2085 | *
|
---|
2086 | * Just search in our local database.
|
---|
2087 | */
|
---|
2088 | TALLOC_FREE(frame);
|
---|
2089 | return 0;
|
---|
2090 | }
|
---|
2091 |
|
---|
2092 | ZERO_STRUCT(entry_ex->entry);
|
---|
2093 |
|
---|
2094 | ret = krb5_copy_principal(context, principal,
|
---|
2095 | &entry_ex->entry.principal);
|
---|
2096 | if (ret) {
|
---|
2097 | TALLOC_FREE(frame);
|
---|
2098 | return ret;
|
---|
2099 | }
|
---|
2100 |
|
---|
2101 | upper = strupper_talloc(frame, tdo->domain_name.string);
|
---|
2102 | if (upper == NULL) {
|
---|
2103 | TALLOC_FREE(frame);
|
---|
2104 | return ENOMEM;
|
---|
2105 | }
|
---|
2106 |
|
---|
2107 | ret = smb_krb5_principal_set_realm(context,
|
---|
2108 | entry_ex->entry.principal,
|
---|
2109 | upper);
|
---|
2110 | if (ret) {
|
---|
2111 | TALLOC_FREE(frame);
|
---|
2112 | return ret;
|
---|
2113 | }
|
---|
2114 |
|
---|
2115 | TALLOC_FREE(frame);
|
---|
2116 | return SDB_ERR_WRONG_REALM;
|
---|
2117 | }
|
---|
2118 |
|
---|
2119 | krb5_error_code samba_kdc_fetch(krb5_context context,
|
---|
2120 | struct samba_kdc_db_context *kdc_db_ctx,
|
---|
2121 | krb5_const_principal principal,
|
---|
2122 | unsigned flags,
|
---|
2123 | krb5_kvno kvno,
|
---|
2124 | struct sdb_entry_ex *entry_ex)
|
---|
2125 | {
|
---|
2126 | krb5_error_code ret = SDB_ERR_NOENTRY;
|
---|
2127 | TALLOC_CTX *mem_ctx;
|
---|
2128 |
|
---|
2129 | mem_ctx = talloc_named(kdc_db_ctx, 0, "samba_kdc_fetch context");
|
---|
2130 | if (!mem_ctx) {
|
---|
2131 | ret = ENOMEM;
|
---|
2132 | krb5_set_error_message(context, ret, "samba_kdc_fetch: talloc_named() failed!");
|
---|
2133 | return ret;
|
---|
2134 | }
|
---|
2135 |
|
---|
2136 | ret = samba_kdc_lookup_realm(context, kdc_db_ctx, mem_ctx,
|
---|
2137 | principal, flags, entry_ex);
|
---|
2138 | if (ret != 0) {
|
---|
2139 | goto done;
|
---|
2140 | }
|
---|
2141 |
|
---|
2142 | ret = SDB_ERR_NOENTRY;
|
---|
2143 |
|
---|
2144 | if (flags & SDB_F_GET_CLIENT) {
|
---|
2145 | ret = samba_kdc_fetch_client(context, kdc_db_ctx, mem_ctx, principal, flags, entry_ex);
|
---|
2146 | if (ret != SDB_ERR_NOENTRY) goto done;
|
---|
2147 | }
|
---|
2148 | if (flags & SDB_F_GET_SERVER) {
|
---|
2149 | /* krbtgt fits into this situation for trusted realms, and for resolving different versions of our own realm name */
|
---|
2150 | ret = samba_kdc_fetch_krbtgt(context, kdc_db_ctx, mem_ctx, principal, flags, kvno, entry_ex);
|
---|
2151 | if (ret != SDB_ERR_NOENTRY) goto done;
|
---|
2152 |
|
---|
2153 | /* We return 'no entry' if it does not start with krbtgt/, so move to the common case quickly */
|
---|
2154 | ret = samba_kdc_fetch_server(context, kdc_db_ctx, mem_ctx, principal, flags, entry_ex);
|
---|
2155 | if (ret != SDB_ERR_NOENTRY) goto done;
|
---|
2156 | }
|
---|
2157 | if (flags & SDB_F_GET_KRBTGT) {
|
---|
2158 | ret = samba_kdc_fetch_krbtgt(context, kdc_db_ctx, mem_ctx, principal, flags, kvno, entry_ex);
|
---|
2159 | if (ret != SDB_ERR_NOENTRY) goto done;
|
---|
2160 | }
|
---|
2161 |
|
---|
2162 | done:
|
---|
2163 | talloc_free(mem_ctx);
|
---|
2164 | return ret;
|
---|
2165 | }
|
---|
2166 |
|
---|
2167 | struct samba_kdc_seq {
|
---|
2168 | unsigned int index;
|
---|
2169 | unsigned int count;
|
---|
2170 | struct ldb_message **msgs;
|
---|
2171 | struct ldb_dn *realm_dn;
|
---|
2172 | };
|
---|
2173 |
|
---|
2174 | static krb5_error_code samba_kdc_seq(krb5_context context,
|
---|
2175 | struct samba_kdc_db_context *kdc_db_ctx,
|
---|
2176 | struct sdb_entry_ex *entry)
|
---|
2177 | {
|
---|
2178 | krb5_error_code ret;
|
---|
2179 | struct samba_kdc_seq *priv = kdc_db_ctx->seq_ctx;
|
---|
2180 | const char *realm = lpcfg_realm(kdc_db_ctx->lp_ctx);
|
---|
2181 | struct ldb_message *msg = NULL;
|
---|
2182 | const char *sAMAccountName = NULL;
|
---|
2183 | krb5_principal principal = NULL;
|
---|
2184 | TALLOC_CTX *mem_ctx;
|
---|
2185 |
|
---|
2186 | if (!priv) {
|
---|
2187 | return SDB_ERR_NOENTRY;
|
---|
2188 | }
|
---|
2189 |
|
---|
2190 | mem_ctx = talloc_named(priv, 0, "samba_kdc_seq context");
|
---|
2191 |
|
---|
2192 | if (!mem_ctx) {
|
---|
2193 | ret = ENOMEM;
|
---|
2194 | krb5_set_error_message(context, ret, "samba_kdc_seq: talloc_named() failed!");
|
---|
2195 | return ret;
|
---|
2196 | }
|
---|
2197 |
|
---|
2198 | while (priv->index < priv->count) {
|
---|
2199 | msg = priv->msgs[priv->index++];
|
---|
2200 |
|
---|
2201 | sAMAccountName = ldb_msg_find_attr_as_string(msg, "sAMAccountName", NULL);
|
---|
2202 | if (sAMAccountName != NULL) {
|
---|
2203 | break;
|
---|
2204 | }
|
---|
2205 | }
|
---|
2206 |
|
---|
2207 | if (sAMAccountName == NULL) {
|
---|
2208 | ret = SDB_ERR_NOENTRY;
|
---|
2209 | goto out;
|
---|
2210 | }
|
---|
2211 |
|
---|
2212 | ret = smb_krb5_make_principal(context, &principal,
|
---|
2213 | realm, sAMAccountName, NULL);
|
---|
2214 | if (ret != 0) {
|
---|
2215 | goto out;
|
---|
2216 | }
|
---|
2217 |
|
---|
2218 | ret = samba_kdc_message2entry(context, kdc_db_ctx, mem_ctx,
|
---|
2219 | principal, SAMBA_KDC_ENT_TYPE_ANY,
|
---|
2220 | SDB_F_ADMIN_DATA|SDB_F_GET_ANY,
|
---|
2221 | priv->realm_dn, msg, entry);
|
---|
2222 |
|
---|
2223 | out:
|
---|
2224 | if (principal != NULL) {
|
---|
2225 | krb5_free_principal(context, principal);
|
---|
2226 | }
|
---|
2227 |
|
---|
2228 | if (ret != 0) {
|
---|
2229 | TALLOC_FREE(priv);
|
---|
2230 | kdc_db_ctx->seq_ctx = NULL;
|
---|
2231 | } else {
|
---|
2232 | talloc_free(mem_ctx);
|
---|
2233 | }
|
---|
2234 |
|
---|
2235 | return ret;
|
---|
2236 | }
|
---|
2237 |
|
---|
2238 | krb5_error_code samba_kdc_firstkey(krb5_context context,
|
---|
2239 | struct samba_kdc_db_context *kdc_db_ctx,
|
---|
2240 | struct sdb_entry_ex *entry)
|
---|
2241 | {
|
---|
2242 | struct ldb_context *ldb_ctx = kdc_db_ctx->samdb;
|
---|
2243 | struct samba_kdc_seq *priv = kdc_db_ctx->seq_ctx;
|
---|
2244 | char *realm;
|
---|
2245 | struct ldb_result *res = NULL;
|
---|
2246 | krb5_error_code ret;
|
---|
2247 | TALLOC_CTX *mem_ctx;
|
---|
2248 | int lret;
|
---|
2249 |
|
---|
2250 | if (priv) {
|
---|
2251 | TALLOC_FREE(priv);
|
---|
2252 | kdc_db_ctx->seq_ctx = NULL;
|
---|
2253 | }
|
---|
2254 |
|
---|
2255 | priv = (struct samba_kdc_seq *) talloc(kdc_db_ctx, struct samba_kdc_seq);
|
---|
2256 | if (!priv) {
|
---|
2257 | ret = ENOMEM;
|
---|
2258 | krb5_set_error_message(context, ret, "talloc: out of memory");
|
---|
2259 | return ret;
|
---|
2260 | }
|
---|
2261 |
|
---|
2262 | priv->index = 0;
|
---|
2263 | priv->msgs = NULL;
|
---|
2264 | priv->realm_dn = ldb_get_default_basedn(ldb_ctx);
|
---|
2265 | priv->count = 0;
|
---|
2266 |
|
---|
2267 | mem_ctx = talloc_named(priv, 0, "samba_kdc_firstkey context");
|
---|
2268 |
|
---|
2269 | if (!mem_ctx) {
|
---|
2270 | ret = ENOMEM;
|
---|
2271 | krb5_set_error_message(context, ret, "samba_kdc_firstkey: talloc_named() failed!");
|
---|
2272 | return ret;
|
---|
2273 | }
|
---|
2274 |
|
---|
2275 | ret = krb5_get_default_realm(context, &realm);
|
---|
2276 | if (ret != 0) {
|
---|
2277 | TALLOC_FREE(priv);
|
---|
2278 | return ret;
|
---|
2279 | }
|
---|
2280 | krb5_free_default_realm(context, realm);
|
---|
2281 |
|
---|
2282 | lret = dsdb_search(ldb_ctx, priv, &res,
|
---|
2283 | priv->realm_dn, LDB_SCOPE_SUBTREE, user_attrs,
|
---|
2284 | DSDB_SEARCH_NO_GLOBAL_CATALOG,
|
---|
2285 | "(objectClass=user)");
|
---|
2286 |
|
---|
2287 | if (lret != LDB_SUCCESS) {
|
---|
2288 | TALLOC_FREE(priv);
|
---|
2289 | return SDB_ERR_NOENTRY;
|
---|
2290 | }
|
---|
2291 |
|
---|
2292 | priv->count = res->count;
|
---|
2293 | priv->msgs = talloc_steal(priv, res->msgs);
|
---|
2294 | talloc_free(res);
|
---|
2295 |
|
---|
2296 | kdc_db_ctx->seq_ctx = priv;
|
---|
2297 |
|
---|
2298 | ret = samba_kdc_seq(context, kdc_db_ctx, entry);
|
---|
2299 |
|
---|
2300 | if (ret != 0) {
|
---|
2301 | TALLOC_FREE(priv);
|
---|
2302 | kdc_db_ctx->seq_ctx = NULL;
|
---|
2303 | } else {
|
---|
2304 | talloc_free(mem_ctx);
|
---|
2305 | }
|
---|
2306 | return ret;
|
---|
2307 | }
|
---|
2308 |
|
---|
2309 | krb5_error_code samba_kdc_nextkey(krb5_context context,
|
---|
2310 | struct samba_kdc_db_context *kdc_db_ctx,
|
---|
2311 | struct sdb_entry_ex *entry)
|
---|
2312 | {
|
---|
2313 | return samba_kdc_seq(context, kdc_db_ctx, entry);
|
---|
2314 | }
|
---|
2315 |
|
---|
2316 | /* Check if a given entry may delegate or do s4u2self to this target principal
|
---|
2317 | *
|
---|
2318 | * This is currently a very nasty hack - allowing only delegation to itself.
|
---|
2319 | */
|
---|
2320 | krb5_error_code
|
---|
2321 | samba_kdc_check_s4u2self(krb5_context context,
|
---|
2322 | struct samba_kdc_db_context *kdc_db_ctx,
|
---|
2323 | struct samba_kdc_entry *skdc_entry,
|
---|
2324 | krb5_const_principal target_principal)
|
---|
2325 | {
|
---|
2326 | krb5_error_code ret;
|
---|
2327 | struct ldb_dn *realm_dn;
|
---|
2328 | struct ldb_message *msg;
|
---|
2329 | struct dom_sid *orig_sid;
|
---|
2330 | struct dom_sid *target_sid;
|
---|
2331 | const char *delegation_check_attrs[] = {
|
---|
2332 | "objectSid", NULL
|
---|
2333 | };
|
---|
2334 |
|
---|
2335 | TALLOC_CTX *mem_ctx = talloc_named(kdc_db_ctx, 0, "samba_kdc_check_s4u2self");
|
---|
2336 |
|
---|
2337 | if (!mem_ctx) {
|
---|
2338 | ret = ENOMEM;
|
---|
2339 | krb5_set_error_message(context, ret, "samba_kdc_check_s4u2self: talloc_named() failed!");
|
---|
2340 | return ret;
|
---|
2341 | }
|
---|
2342 |
|
---|
2343 | ret = samba_kdc_lookup_server(context, kdc_db_ctx, mem_ctx, target_principal,
|
---|
2344 | SDB_F_GET_CLIENT|SDB_F_GET_SERVER,
|
---|
2345 | delegation_check_attrs, &realm_dn, &msg);
|
---|
2346 |
|
---|
2347 | if (ret != 0) {
|
---|
2348 | talloc_free(mem_ctx);
|
---|
2349 | return ret;
|
---|
2350 | }
|
---|
2351 |
|
---|
2352 | orig_sid = samdb_result_dom_sid(mem_ctx, skdc_entry->msg, "objectSid");
|
---|
2353 | target_sid = samdb_result_dom_sid(mem_ctx, msg, "objectSid");
|
---|
2354 |
|
---|
2355 | /* Allow delegation to the same principal, even if by a different
|
---|
2356 | * name. The easy and safe way to prove this is by SID
|
---|
2357 | * comparison */
|
---|
2358 | if (!(orig_sid && target_sid && dom_sid_equal(orig_sid, target_sid))) {
|
---|
2359 | talloc_free(mem_ctx);
|
---|
2360 | return KRB5KDC_ERR_BADOPTION;
|
---|
2361 | }
|
---|
2362 |
|
---|
2363 | talloc_free(mem_ctx);
|
---|
2364 | return ret;
|
---|
2365 | }
|
---|
2366 |
|
---|
2367 | /* Certificates printed by a the Certificate Authority might have a
|
---|
2368 | * slightly different form of the user principal name to that in the
|
---|
2369 | * database. Allow a mismatch where they both refer to the same
|
---|
2370 | * SID */
|
---|
2371 |
|
---|
2372 | krb5_error_code
|
---|
2373 | samba_kdc_check_pkinit_ms_upn_match(krb5_context context,
|
---|
2374 | struct samba_kdc_db_context *kdc_db_ctx,
|
---|
2375 | struct samba_kdc_entry *skdc_entry,
|
---|
2376 | krb5_const_principal certificate_principal)
|
---|
2377 | {
|
---|
2378 | krb5_error_code ret;
|
---|
2379 | struct ldb_dn *realm_dn;
|
---|
2380 | struct ldb_message *msg;
|
---|
2381 | struct dom_sid *orig_sid;
|
---|
2382 | struct dom_sid *target_sid;
|
---|
2383 | const char *ms_upn_check_attrs[] = {
|
---|
2384 | "objectSid", NULL
|
---|
2385 | };
|
---|
2386 |
|
---|
2387 | TALLOC_CTX *mem_ctx = talloc_named(kdc_db_ctx, 0, "samba_kdc_check_pkinit_ms_upn_match");
|
---|
2388 |
|
---|
2389 | if (!mem_ctx) {
|
---|
2390 | ret = ENOMEM;
|
---|
2391 | krb5_set_error_message(context, ret, "samba_kdc_fetch: talloc_named() failed!");
|
---|
2392 | return ret;
|
---|
2393 | }
|
---|
2394 |
|
---|
2395 | ret = samba_kdc_lookup_client(context, kdc_db_ctx,
|
---|
2396 | mem_ctx, certificate_principal,
|
---|
2397 | ms_upn_check_attrs, &realm_dn, &msg);
|
---|
2398 |
|
---|
2399 | if (ret != 0) {
|
---|
2400 | talloc_free(mem_ctx);
|
---|
2401 | return ret;
|
---|
2402 | }
|
---|
2403 |
|
---|
2404 | orig_sid = samdb_result_dom_sid(mem_ctx, skdc_entry->msg, "objectSid");
|
---|
2405 | target_sid = samdb_result_dom_sid(mem_ctx, msg, "objectSid");
|
---|
2406 |
|
---|
2407 | /* Consider these to be the same principal, even if by a different
|
---|
2408 | * name. The easy and safe way to prove this is by SID
|
---|
2409 | * comparison */
|
---|
2410 | if (!(orig_sid && target_sid && dom_sid_equal(orig_sid, target_sid))) {
|
---|
2411 | talloc_free(mem_ctx);
|
---|
2412 | #ifdef KRB5_KDC_ERR_CLIENT_NAME_MISMATCH /* Heimdal */
|
---|
2413 | return KRB5_KDC_ERR_CLIENT_NAME_MISMATCH;
|
---|
2414 | #elif defined(KRB5KDC_ERR_CLIENT_NAME_MISMATCH) /* MIT */
|
---|
2415 | return KRB5KDC_ERR_CLIENT_NAME_MISMATCH;
|
---|
2416 | #endif
|
---|
2417 | }
|
---|
2418 |
|
---|
2419 | talloc_free(mem_ctx);
|
---|
2420 | return ret;
|
---|
2421 | }
|
---|
2422 |
|
---|
2423 | /*
|
---|
2424 | * Check if a given entry may delegate to this target principal
|
---|
2425 | * with S4U2Proxy.
|
---|
2426 | */
|
---|
2427 | krb5_error_code
|
---|
2428 | samba_kdc_check_s4u2proxy(krb5_context context,
|
---|
2429 | struct samba_kdc_db_context *kdc_db_ctx,
|
---|
2430 | struct samba_kdc_entry *skdc_entry,
|
---|
2431 | krb5_const_principal target_principal)
|
---|
2432 | {
|
---|
2433 | krb5_error_code ret;
|
---|
2434 | char *tmp = NULL;
|
---|
2435 | const char *client_dn = NULL;
|
---|
2436 | const char *target_principal_name = NULL;
|
---|
2437 | struct ldb_message_element *el;
|
---|
2438 | struct ldb_val val;
|
---|
2439 | unsigned int i;
|
---|
2440 | bool found = false;
|
---|
2441 |
|
---|
2442 | TALLOC_CTX *mem_ctx = talloc_named(kdc_db_ctx, 0, "samba_kdc_check_s4u2proxy");
|
---|
2443 |
|
---|
2444 | if (!mem_ctx) {
|
---|
2445 | ret = ENOMEM;
|
---|
2446 | krb5_set_error_message(context, ret,
|
---|
2447 | "samba_kdc_check_s4u2proxy:"
|
---|
2448 | " talloc_named() failed!");
|
---|
2449 | return ret;
|
---|
2450 | }
|
---|
2451 |
|
---|
2452 | client_dn = ldb_dn_get_linearized(skdc_entry->msg->dn);
|
---|
2453 | if (!client_dn) {
|
---|
2454 | if (errno == 0) {
|
---|
2455 | errno = ENOMEM;
|
---|
2456 | }
|
---|
2457 | ret = errno;
|
---|
2458 | krb5_set_error_message(context, ret,
|
---|
2459 | "samba_kdc_check_s4u2proxy:"
|
---|
2460 | " ldb_dn_get_linearized() failed!");
|
---|
2461 | return ret;
|
---|
2462 | }
|
---|
2463 |
|
---|
2464 | /*
|
---|
2465 | * The main heimdal code already checked that the target_principal
|
---|
2466 | * belongs to the same realm as the client.
|
---|
2467 | *
|
---|
2468 | * So we just need the principal without the realm,
|
---|
2469 | * as that is what is configured in the "msDS-AllowedToDelegateTo"
|
---|
2470 | * attribute.
|
---|
2471 | */
|
---|
2472 | ret = krb5_unparse_name_flags(context, target_principal,
|
---|
2473 | KRB5_PRINCIPAL_UNPARSE_NO_REALM, &tmp);
|
---|
2474 | if (ret) {
|
---|
2475 | talloc_free(mem_ctx);
|
---|
2476 | krb5_set_error_message(context, ret,
|
---|
2477 | "samba_kdc_check_s4u2proxy:"
|
---|
2478 | " krb5_unparse_name() failed!");
|
---|
2479 | return ret;
|
---|
2480 | }
|
---|
2481 | DEBUG(10,("samba_kdc_check_s4u2proxy: client[%s] for target[%s]\n",
|
---|
2482 | client_dn, tmp));
|
---|
2483 |
|
---|
2484 | target_principal_name = talloc_strdup(mem_ctx, tmp);
|
---|
2485 | SAFE_FREE(tmp);
|
---|
2486 | if (target_principal_name == NULL) {
|
---|
2487 | ret = ENOMEM;
|
---|
2488 | krb5_set_error_message(context, ret,
|
---|
2489 | "samba_kdc_check_s4u2proxy:"
|
---|
2490 | " talloc_strdup() failed!");
|
---|
2491 | return ret;
|
---|
2492 | }
|
---|
2493 |
|
---|
2494 | el = ldb_msg_find_element(skdc_entry->msg, "msDS-AllowedToDelegateTo");
|
---|
2495 | if (el == NULL) {
|
---|
2496 | goto bad_option;
|
---|
2497 | }
|
---|
2498 |
|
---|
2499 | val = data_blob_string_const(target_principal_name);
|
---|
2500 |
|
---|
2501 | for (i=0; i<el->num_values; i++) {
|
---|
2502 | struct ldb_val *val1 = &val;
|
---|
2503 | struct ldb_val *val2 = &el->values[i];
|
---|
2504 | int cmp;
|
---|
2505 |
|
---|
2506 | if (val1->length != val2->length) {
|
---|
2507 | continue;
|
---|
2508 | }
|
---|
2509 |
|
---|
2510 | cmp = strncasecmp((const char *)val1->data,
|
---|
2511 | (const char *)val2->data,
|
---|
2512 | val1->length);
|
---|
2513 | if (cmp != 0) {
|
---|
2514 | continue;
|
---|
2515 | }
|
---|
2516 |
|
---|
2517 | found = true;
|
---|
2518 | break;
|
---|
2519 | }
|
---|
2520 |
|
---|
2521 | if (!found) {
|
---|
2522 | goto bad_option;
|
---|
2523 | }
|
---|
2524 |
|
---|
2525 | DEBUG(10,("samba_kdc_check_s4u2proxy: client[%s] allowed target[%s]\n",
|
---|
2526 | client_dn, tmp));
|
---|
2527 | talloc_free(mem_ctx);
|
---|
2528 | return 0;
|
---|
2529 |
|
---|
2530 | bad_option:
|
---|
2531 | krb5_set_error_message(context, ret,
|
---|
2532 | "samba_kdc_check_s4u2proxy: client[%s] "
|
---|
2533 | "not allowed for delegation to target[%s]",
|
---|
2534 | client_dn,
|
---|
2535 | target_principal_name);
|
---|
2536 | talloc_free(mem_ctx);
|
---|
2537 | return KRB5KDC_ERR_BADOPTION;
|
---|
2538 | }
|
---|
2539 |
|
---|
2540 | NTSTATUS samba_kdc_setup_db_ctx(TALLOC_CTX *mem_ctx, struct samba_kdc_base_context *base_ctx,
|
---|
2541 | struct samba_kdc_db_context **kdc_db_ctx_out)
|
---|
2542 | {
|
---|
2543 | int ldb_ret;
|
---|
2544 | struct ldb_message *msg;
|
---|
2545 | struct auth_session_info *session_info;
|
---|
2546 | struct samba_kdc_db_context *kdc_db_ctx;
|
---|
2547 | /* The idea here is very simple. Using Kerberos to
|
---|
2548 | * authenticate the KDC to the LDAP server is higly likely to
|
---|
2549 | * be circular.
|
---|
2550 | *
|
---|
2551 | * In future we may set this up to use EXERNAL and SSL
|
---|
2552 | * certificates, for now it will almost certainly be NTLMSSP_SET_USERNAME
|
---|
2553 | */
|
---|
2554 |
|
---|
2555 | kdc_db_ctx = talloc_zero(mem_ctx, struct samba_kdc_db_context);
|
---|
2556 | if (kdc_db_ctx == NULL) {
|
---|
2557 | return NT_STATUS_NO_MEMORY;
|
---|
2558 | }
|
---|
2559 | kdc_db_ctx->ev_ctx = base_ctx->ev_ctx;
|
---|
2560 | kdc_db_ctx->lp_ctx = base_ctx->lp_ctx;
|
---|
2561 |
|
---|
2562 | /* get default kdc policy */
|
---|
2563 | lpcfg_default_kdc_policy(base_ctx->lp_ctx,
|
---|
2564 | &kdc_db_ctx->policy.svc_tkt_lifetime,
|
---|
2565 | &kdc_db_ctx->policy.usr_tkt_lifetime,
|
---|
2566 | &kdc_db_ctx->policy.renewal_lifetime);
|
---|
2567 |
|
---|
2568 | session_info = system_session(kdc_db_ctx->lp_ctx);
|
---|
2569 | if (session_info == NULL) {
|
---|
2570 | return NT_STATUS_INTERNAL_ERROR;
|
---|
2571 | }
|
---|
2572 |
|
---|
2573 | /* Setup the link to LDB */
|
---|
2574 | kdc_db_ctx->samdb = samdb_connect(kdc_db_ctx, base_ctx->ev_ctx,
|
---|
2575 | base_ctx->lp_ctx, session_info, 0);
|
---|
2576 | if (kdc_db_ctx->samdb == NULL) {
|
---|
2577 | DEBUG(1, ("samba_kdc_setup_db_ctx: Cannot open samdb for KDC backend!"));
|
---|
2578 | talloc_free(kdc_db_ctx);
|
---|
2579 | return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
|
---|
2580 | }
|
---|
2581 |
|
---|
2582 | /* Find out our own krbtgt kvno */
|
---|
2583 | ldb_ret = samdb_rodc(kdc_db_ctx->samdb, &kdc_db_ctx->rodc);
|
---|
2584 | if (ldb_ret != LDB_SUCCESS) {
|
---|
2585 | DEBUG(1, ("samba_kdc_setup_db_ctx: Cannot determine if we are an RODC in KDC backend: %s\n",
|
---|
2586 | ldb_errstring(kdc_db_ctx->samdb)));
|
---|
2587 | talloc_free(kdc_db_ctx);
|
---|
2588 | return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
|
---|
2589 | }
|
---|
2590 | if (kdc_db_ctx->rodc) {
|
---|
2591 | int my_krbtgt_number;
|
---|
2592 | const char *secondary_keytab[] = { "msDS-SecondaryKrbTgtNumber", NULL };
|
---|
2593 | struct ldb_dn *account_dn;
|
---|
2594 | struct ldb_dn *server_dn = samdb_server_dn(kdc_db_ctx->samdb, kdc_db_ctx);
|
---|
2595 | if (!server_dn) {
|
---|
2596 | DEBUG(1, ("samba_kdc_setup_db_ctx: Cannot determine server DN in KDC backend: %s\n",
|
---|
2597 | ldb_errstring(kdc_db_ctx->samdb)));
|
---|
2598 | talloc_free(kdc_db_ctx);
|
---|
2599 | return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
|
---|
2600 | }
|
---|
2601 |
|
---|
2602 | ldb_ret = samdb_reference_dn(kdc_db_ctx->samdb, kdc_db_ctx, server_dn,
|
---|
2603 | "serverReference", &account_dn);
|
---|
2604 | if (ldb_ret != LDB_SUCCESS) {
|
---|
2605 | DEBUG(1, ("samba_kdc_setup_db_ctx: Cannot determine server account in KDC backend: %s\n",
|
---|
2606 | ldb_errstring(kdc_db_ctx->samdb)));
|
---|
2607 | talloc_free(kdc_db_ctx);
|
---|
2608 | return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
|
---|
2609 | }
|
---|
2610 |
|
---|
2611 | ldb_ret = samdb_reference_dn(kdc_db_ctx->samdb, kdc_db_ctx, account_dn,
|
---|
2612 | "msDS-KrbTgtLink", &kdc_db_ctx->krbtgt_dn);
|
---|
2613 | talloc_free(account_dn);
|
---|
2614 | if (ldb_ret != LDB_SUCCESS) {
|
---|
2615 | DEBUG(1, ("samba_kdc_setup_db_ctx: Cannot determine RODC krbtgt account in KDC backend: %s\n",
|
---|
2616 | ldb_errstring(kdc_db_ctx->samdb)));
|
---|
2617 | talloc_free(kdc_db_ctx);
|
---|
2618 | return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
|
---|
2619 | }
|
---|
2620 |
|
---|
2621 | ldb_ret = dsdb_search_one(kdc_db_ctx->samdb, kdc_db_ctx,
|
---|
2622 | &msg, kdc_db_ctx->krbtgt_dn, LDB_SCOPE_BASE,
|
---|
2623 | secondary_keytab,
|
---|
2624 | DSDB_SEARCH_NO_GLOBAL_CATALOG,
|
---|
2625 | "(&(objectClass=user)(msDS-SecondaryKrbTgtNumber=*))");
|
---|
2626 | if (ldb_ret != LDB_SUCCESS) {
|
---|
2627 | DEBUG(1, ("samba_kdc_setup_db_ctx: Cannot read krbtgt account %s in KDC backend to get msDS-SecondaryKrbTgtNumber: %s: %s\n",
|
---|
2628 | ldb_dn_get_linearized(kdc_db_ctx->krbtgt_dn),
|
---|
2629 | ldb_errstring(kdc_db_ctx->samdb),
|
---|
2630 | ldb_strerror(ldb_ret)));
|
---|
2631 | talloc_free(kdc_db_ctx);
|
---|
2632 | return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
|
---|
2633 | }
|
---|
2634 | my_krbtgt_number = ldb_msg_find_attr_as_int(msg, "msDS-SecondaryKrbTgtNumber", -1);
|
---|
2635 | if (my_krbtgt_number == -1) {
|
---|
2636 | DEBUG(1, ("samba_kdc_setup_db_ctx: Cannot read msDS-SecondaryKrbTgtNumber from krbtgt account %s in KDC backend: got %d\n",
|
---|
2637 | ldb_dn_get_linearized(kdc_db_ctx->krbtgt_dn),
|
---|
2638 | my_krbtgt_number));
|
---|
2639 | talloc_free(kdc_db_ctx);
|
---|
2640 | return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
|
---|
2641 | }
|
---|
2642 | kdc_db_ctx->my_krbtgt_number = my_krbtgt_number;
|
---|
2643 |
|
---|
2644 | } else {
|
---|
2645 | kdc_db_ctx->my_krbtgt_number = 0;
|
---|
2646 | ldb_ret = dsdb_search_one(kdc_db_ctx->samdb, kdc_db_ctx,
|
---|
2647 | &msg,
|
---|
2648 | ldb_get_default_basedn(kdc_db_ctx->samdb),
|
---|
2649 | LDB_SCOPE_SUBTREE,
|
---|
2650 | krbtgt_attrs,
|
---|
2651 | DSDB_SEARCH_NO_GLOBAL_CATALOG,
|
---|
2652 | "(&(objectClass=user)(samAccountName=krbtgt))");
|
---|
2653 |
|
---|
2654 | if (ldb_ret != LDB_SUCCESS) {
|
---|
2655 | DEBUG(1, ("samba_kdc_fetch: could not find own KRBTGT in DB: %s\n", ldb_errstring(kdc_db_ctx->samdb)));
|
---|
2656 | talloc_free(kdc_db_ctx);
|
---|
2657 | return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
|
---|
2658 | }
|
---|
2659 | kdc_db_ctx->krbtgt_dn = talloc_steal(kdc_db_ctx, msg->dn);
|
---|
2660 | kdc_db_ctx->my_krbtgt_number = 0;
|
---|
2661 | talloc_free(msg);
|
---|
2662 | }
|
---|
2663 | *kdc_db_ctx_out = kdc_db_ctx;
|
---|
2664 | return NT_STATUS_OK;
|
---|
2665 | }
|
---|