Line data Source code
1 : /*
2 : Unix SMB/CIFS implementation.
3 :
4 : Samba KDB plugin for MIT Kerberos
5 :
6 : Copyright (c) 2010 Simo Sorce <idra@samba.org>.
7 : Copyright (c) 2014 Andreas Schneider <asn@samba.org>
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 : You should have received a copy of the GNU General Public License
20 : along with this program. If not, see <http://www.gnu.org/licenses/>.
21 : */
22 :
23 : #include "includes.h"
24 :
25 : #include "system/kerberos.h"
26 :
27 : #include <profile.h>
28 : #include <kdb.h>
29 :
30 : #include "kdc/mit_samba.h"
31 : #include "kdb_samba.h"
32 :
33 : #undef DBGC_CLASS
34 : #define DBGC_CLASS DBGC_KERBEROS
35 :
36 : /* FIXME: This is a krb5 function which is exported, but in no header */
37 : extern krb5_error_code decode_krb5_padata_sequence(const krb5_data *output,
38 : krb5_pa_data ***rep);
39 :
40 0 : static krb5_error_code ks_get_netbios_name(krb5_address **addrs, char **name)
41 : {
42 0 : char *nb_name = NULL;
43 : int len, i;
44 :
45 0 : for (i = 0; addrs[i]; i++) {
46 0 : if (addrs[i]->addrtype != ADDRTYPE_NETBIOS) {
47 0 : continue;
48 : }
49 0 : len = MIN(addrs[i]->length, 15);
50 0 : nb_name = strndup((const char *)addrs[i]->contents, len);
51 0 : if (!nb_name) {
52 0 : return ENOMEM;
53 : }
54 0 : break;
55 : }
56 :
57 0 : if (nb_name) {
58 : /* Strip space padding */
59 0 : i = strlen(nb_name) - 1;
60 0 : for (i = strlen(nb_name) - 1;
61 0 : i > 0 && nb_name[i] == ' ';
62 0 : i--) {
63 0 : nb_name[i] = '\0';
64 : }
65 : }
66 :
67 0 : *name = nb_name;
68 :
69 0 : return 0;
70 : }
71 :
72 0 : krb5_error_code kdb_samba_db_check_policy_as(krb5_context context,
73 : krb5_kdc_req *kdcreq,
74 : krb5_db_entry *client,
75 : krb5_db_entry *server,
76 : krb5_timestamp kdc_time,
77 : const char **status,
78 : krb5_pa_data ***e_data_out)
79 : {
80 : struct mit_samba_context *mit_ctx;
81 : krb5_error_code code;
82 0 : char *client_name = NULL;
83 0 : char *server_name = NULL;
84 0 : char *netbios_name = NULL;
85 0 : char *realm = NULL;
86 0 : bool password_change = false;
87 : krb5_const_principal client_princ;
88 0 : DATA_BLOB int_data = { NULL, 0 };
89 : krb5_data d;
90 : krb5_pa_data **e_data;
91 :
92 0 : mit_ctx = ks_get_context(context);
93 0 : if (mit_ctx == NULL) {
94 0 : return KRB5_KDB_DBNOTINITED;
95 : }
96 :
97 : /* Prefer canonicalised name from client entry */
98 0 : client_princ = client ? client->princ : kdcreq->client;
99 :
100 0 : if (client_princ == NULL || ks_is_kadmin(context, client_princ)) {
101 0 : return KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN;
102 : }
103 :
104 0 : if (krb5_princ_size(context, kdcreq->server) == 2 &&
105 0 : ks_is_kadmin_changepw(context, kdcreq->server)) {
106 0 : code = krb5_get_default_realm(context, &realm);
107 0 : if (code) {
108 0 : goto done;
109 : }
110 :
111 0 : if (ks_data_eq_string(kdcreq->server->realm, realm)) {
112 0 : password_change = true;
113 : }
114 : }
115 :
116 0 : code = krb5_unparse_name(context, kdcreq->server, &server_name);
117 0 : if (code) {
118 0 : goto done;
119 : }
120 :
121 0 : code = krb5_unparse_name(context, client_princ, &client_name);
122 0 : if (code) {
123 0 : goto done;
124 : }
125 :
126 0 : if (kdcreq->addresses) {
127 0 : code = ks_get_netbios_name(kdcreq->addresses, &netbios_name);
128 0 : if (code) {
129 0 : goto done;
130 : }
131 : }
132 :
133 0 : code = mit_samba_check_client_access(mit_ctx,
134 : client,
135 : client_name,
136 : server,
137 : server_name,
138 : netbios_name,
139 : password_change,
140 : &int_data);
141 :
142 0 : if (int_data.length && int_data.data) {
143 :
144 : /* make sure the mapped return code is returned - gd */
145 : int code_tmp;
146 :
147 0 : d = ks_make_data(int_data.data, int_data.length);
148 :
149 0 : code_tmp = decode_krb5_padata_sequence(&d, &e_data);
150 0 : if (code_tmp == 0) {
151 0 : *e_data_out = e_data;
152 : }
153 : }
154 0 : done:
155 0 : free(realm);
156 0 : free(server_name);
157 0 : free(client_name);
158 0 : free(netbios_name);
159 :
160 0 : return code;
161 : }
162 :
163 0 : static krb5_error_code ks_get_pac(krb5_context context,
164 : krb5_db_entry *client,
165 : krb5_keyblock *client_key,
166 : krb5_pac *pac)
167 : {
168 : struct mit_samba_context *mit_ctx;
169 : krb5_error_code code;
170 :
171 0 : mit_ctx = ks_get_context(context);
172 0 : if (mit_ctx == NULL) {
173 0 : return KRB5_KDB_DBNOTINITED;
174 : }
175 :
176 0 : code = mit_samba_get_pac(mit_ctx,
177 : context,
178 : client,
179 : client_key,
180 : pac);
181 0 : if (code != 0) {
182 0 : return code;
183 : }
184 :
185 0 : return code;
186 : }
187 :
188 0 : static krb5_error_code ks_verify_pac(krb5_context context,
189 : unsigned int flags,
190 : krb5_const_principal client_princ,
191 : krb5_db_entry *client,
192 : krb5_db_entry *server,
193 : krb5_db_entry *krbtgt,
194 : krb5_keyblock *server_key,
195 : krb5_keyblock *krbtgt_key,
196 : krb5_timestamp authtime,
197 : krb5_authdata **tgt_auth_data,
198 : krb5_pac *pac)
199 : {
200 : struct mit_samba_context *mit_ctx;
201 0 : krb5_authdata **authdata = NULL;
202 0 : krb5_pac ipac = NULL;
203 0 : DATA_BLOB logon_data = { NULL, 0 };
204 : krb5_error_code code;
205 :
206 0 : mit_ctx = ks_get_context(context);
207 0 : if (mit_ctx == NULL) {
208 0 : return KRB5_KDB_DBNOTINITED;
209 : }
210 :
211 : /* find the existing PAC, if present */
212 0 : code = krb5_find_authdata(context,
213 : tgt_auth_data,
214 : NULL,
215 : KRB5_AUTHDATA_WIN2K_PAC,
216 : &authdata);
217 0 : if (code != 0) {
218 0 : return code;
219 : }
220 :
221 : /* no pac data */
222 0 : if (authdata == NULL) {
223 0 : return 0;
224 : }
225 :
226 0 : SMB_ASSERT(authdata[0] != NULL);
227 :
228 0 : if (authdata[1] != NULL) {
229 0 : code = KRB5KDC_ERR_BADOPTION; /* XXX */
230 0 : goto done;
231 : }
232 :
233 0 : code = krb5_pac_parse(context,
234 0 : authdata[0]->contents,
235 0 : authdata[0]->length,
236 : &ipac);
237 0 : if (code != 0) {
238 0 : goto done;
239 : }
240 :
241 : /* TODO: verify this is correct
242 : *
243 : * In the constrained delegation case, the PAC is from a service
244 : * ticket rather than a TGT; we must verify the server and KDC
245 : * signatures to assert that the server did not forge the PAC.
246 : */
247 0 : if (flags & KRB5_KDB_FLAG_CONSTRAINED_DELEGATION) {
248 0 : code = krb5_pac_verify(context,
249 : ipac,
250 : authtime,
251 : client_princ,
252 : server_key,
253 : krbtgt_key);
254 : } else {
255 0 : code = krb5_pac_verify(context,
256 : ipac,
257 : authtime,
258 : client_princ,
259 : krbtgt_key,
260 : NULL);
261 : }
262 0 : if (code != 0) {
263 0 : goto done;
264 : }
265 :
266 : /* check and update PAC */
267 0 : code = krb5_pac_parse(context,
268 0 : authdata[0]->contents,
269 0 : authdata[0]->length,
270 : pac);
271 0 : if (code != 0) {
272 0 : goto done;
273 : }
274 :
275 0 : code = mit_samba_reget_pac(mit_ctx,
276 : context,
277 : flags,
278 : client_princ,
279 : client,
280 : server,
281 : krbtgt,
282 : krbtgt_key,
283 : pac);
284 :
285 0 : done:
286 0 : krb5_free_authdata(context, authdata);
287 0 : krb5_pac_free(context, ipac);
288 0 : free(logon_data.data);
289 :
290 0 : return code;
291 : }
292 :
293 0 : krb5_error_code kdb_samba_db_sign_auth_data(krb5_context context,
294 : unsigned int flags,
295 : krb5_const_principal client_princ,
296 : krb5_const_principal server_princ,
297 : krb5_db_entry *client,
298 : krb5_db_entry *server,
299 : krb5_db_entry *krbtgt,
300 : krb5_db_entry *local_krbtgt,
301 : krb5_keyblock *client_key,
302 : krb5_keyblock *server_key,
303 : krb5_keyblock *krbtgt_key,
304 : krb5_keyblock *local_krbtgt_key,
305 : krb5_keyblock *session_key,
306 : krb5_timestamp authtime,
307 : krb5_authdata **tgt_auth_data,
308 : void *authdata_info,
309 : krb5_data ***auth_indicators,
310 : krb5_authdata ***signed_auth_data)
311 : {
312 0 : krb5_authdata **authdata = NULL;
313 : krb5_boolean is_as_req;
314 : krb5_error_code code;
315 0 : krb5_pac pac = NULL;
316 : krb5_data pac_data;
317 :
318 0 : krbtgt = krbtgt == NULL ? local_krbtgt : krbtgt;
319 0 : krbtgt_key = krbtgt_key == NULL ? local_krbtgt_key : krbtgt_key;
320 :
321 : /* FIXME: We don't support S4U yet */
322 0 : if (flags & KRB5_KDB_FLAGS_S4U) {
323 0 : return KRB5_KDB_DBTYPE_NOSUP;
324 : }
325 :
326 0 : is_as_req = ((flags & KRB5_KDB_FLAG_CLIENT_REFERRALS_ONLY) != 0);
327 :
328 0 : if (is_as_req && (flags & KRB5_KDB_FLAG_INCLUDE_PAC)) {
329 0 : code = ks_get_pac(context, client, client_key, &pac);
330 0 : if (code != 0) {
331 0 : goto done;
332 : }
333 : }
334 :
335 0 : if (!is_as_req) {
336 0 : code = ks_verify_pac(context,
337 : flags,
338 : client_princ,
339 : client,
340 : server,
341 : krbtgt,
342 : server_key,
343 : krbtgt_key,
344 : authtime,
345 : tgt_auth_data,
346 : &pac);
347 0 : if (code != 0) {
348 0 : goto done;
349 : }
350 : }
351 :
352 0 : if (pac == NULL && client != NULL) {
353 :
354 0 : code = ks_get_pac(context, client, client_key, &pac);
355 0 : if (code != 0) {
356 0 : goto done;
357 : }
358 : }
359 :
360 0 : if (pac == NULL) {
361 0 : code = KRB5_KDB_DBTYPE_NOSUP;
362 0 : goto done;
363 : }
364 :
365 0 : code = krb5_pac_sign(context, pac, authtime, client_princ,
366 : server_key, krbtgt_key, &pac_data);
367 0 : if (code != 0) {
368 0 : DBG_ERR("krb5_pac_sign failed: %d\n", code);
369 0 : goto done;
370 : }
371 :
372 0 : authdata = calloc(2, sizeof(krb5_authdata *));
373 0 : if (authdata == NULL) {
374 0 : goto done;
375 : }
376 :
377 0 : authdata[0] = malloc(sizeof(krb5_authdata));
378 0 : if (authdata[0] == NULL) {
379 0 : goto done;
380 : }
381 :
382 : /* put in signed data */
383 0 : authdata[0]->magic = KV5M_AUTHDATA;
384 0 : authdata[0]->ad_type = KRB5_AUTHDATA_WIN2K_PAC;
385 0 : authdata[0]->contents = (krb5_octet *)pac_data.data;
386 0 : authdata[0]->length = pac_data.length;
387 :
388 0 : code = krb5_encode_authdata_container(context,
389 : KRB5_AUTHDATA_IF_RELEVANT,
390 : authdata,
391 : signed_auth_data);
392 0 : if (code != 0) {
393 0 : goto done;
394 : }
395 :
396 0 : code = 0;
397 :
398 0 : done:
399 0 : krb5_pac_free(context, pac);
400 0 : krb5_free_authdata(context, authdata);
401 :
402 0 : return code;
403 : }
404 :
405 0 : krb5_error_code kdb_samba_db_check_allowed_to_delegate(krb5_context context,
406 : krb5_const_principal client,
407 : const krb5_db_entry *server,
408 : krb5_const_principal proxy)
409 : {
410 : struct mit_samba_context *mit_ctx;
411 :
412 : /*
413 : * Names are quite odd and confusing in the current implementation.
414 : * The following mappings should help understanding what is what.
415 : * client -> client to impersonate
416 : * server; -> delegating service
417 : * proxy; -> target principal
418 : */
419 0 : krb5_db_entry *delegating_service = discard_const_p(krb5_db_entry, server);
420 :
421 0 : char *target_name = NULL;
422 : bool is_enterprise;
423 : krb5_error_code code;
424 :
425 0 : mit_ctx = ks_get_context(context);
426 0 : if (mit_ctx == NULL) {
427 0 : return KRB5_KDB_DBNOTINITED;
428 : }
429 :
430 0 : code = krb5_unparse_name(context, proxy, &target_name);
431 0 : if (code) {
432 0 : goto done;
433 : }
434 :
435 0 : is_enterprise = (proxy->type == KRB5_NT_ENTERPRISE_PRINCIPAL);
436 :
437 0 : code = mit_samba_check_s4u2proxy(mit_ctx,
438 : delegating_service,
439 : target_name,
440 : is_enterprise);
441 :
442 0 : done:
443 0 : free(target_name);
444 0 : return code;
445 : }
446 :
447 :
448 0 : static void samba_bad_password_count(krb5_db_entry *client,
449 : krb5_error_code error_code)
450 : {
451 0 : switch (error_code) {
452 0 : case 0:
453 0 : mit_samba_zero_bad_password_count(client);
454 0 : break;
455 0 : case KRB5KDC_ERR_PREAUTH_FAILED:
456 : case KRB5KRB_AP_ERR_BAD_INTEGRITY:
457 0 : mit_samba_update_bad_password_count(client);
458 0 : break;
459 : }
460 0 : }
461 :
462 0 : void kdb_samba_db_audit_as_req(krb5_context context,
463 : krb5_kdc_req *request,
464 : const krb5_address *local_addr,
465 : const krb5_address *remote_addr,
466 : krb5_db_entry *client,
467 : krb5_db_entry *server,
468 : krb5_timestamp authtime,
469 : krb5_error_code error_code)
470 : {
471 : /*
472 : * FIXME: This segfaulted with a FAST test
473 : * FIND_FAST: <unknown client> for <unknown server>, Unknown FAST armor type 0
474 : */
475 0 : if (client == NULL) {
476 0 : return;
477 : }
478 :
479 0 : samba_bad_password_count(client, error_code);
480 :
481 : /* TODO: perform proper audit logging for addresses */
482 : }
|