4
* Copyright (C) 2007 Apple Inc. All Rights Reserved.
5
* Copyright (C) 2008 by the Massachusetts Institute of Technology.
7
* Export of this software from the United States of America may
8
* require a specific license from the United States Government.
9
* It is the responsibility of any person or organization contemplating
10
* export to obtain such a license before exporting.
12
* WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
13
* distribute this software and its documentation for any purpose and
14
* without fee is hereby granted, provided that the above copyright
15
* notice appear in all copies and that both that copyright notice and
16
* this permission notice appear in supporting documentation, and that
17
* the name of M.I.T. not be used in advertising or publicity pertaining
18
* to distribution of the software without specific, written prior
19
* permission. Furthermore if you modify this software you must label
20
* your software as modified software and not distribute it in such a
21
* fashion that it might be confused with the original M.I.T. software.
22
* M.I.T. makes no representations about the suitability of
23
* this software for any purpose. It is provided "as is" without express
24
* or implied warranty.
26
* AuthorizationData routines for the KDC.
33
#include "adm_proto.h"
38
#include "../include/krb5/authdata_plugin.h"
41
static const char *objdirs[] = { KRB5_AUTHDATA_PLUGIN_BUNDLE_DIR, LIBDIR "/krb5/plugins/authdata", NULL }; /* should be a list */
43
static const char *objdirs[] = { LIBDIR "/krb5/plugins/authdata", NULL };
46
/* MIT Kerberos 1.6 (V0) authdata plugin callback */
47
typedef krb5_error_code (*authdata_proc_0)
48
(krb5_context, krb5_db_entry *client,
50
krb5_kdc_req *request,
51
krb5_enc_tkt_part * enc_tkt_reply);
52
/* MIT Kerberos 1.7 (V1) authdata plugin callback */
53
typedef krb5_error_code (*authdata_proc_1)
54
(krb5_context, unsigned int flags,
55
krb5_db_entry *client, krb5_db_entry *server,
56
krb5_db_entry *krbtgt,
57
krb5_keyblock *client_key,
58
krb5_keyblock *server_key,
60
krb5_kdc_req *request,
61
krb5_const_principal for_user_princ,
62
krb5_enc_tkt_part *enc_tkt_request,
63
krb5_enc_tkt_part *enc_tkt_reply);
64
typedef krb5_error_code (*init_proc)
65
(krb5_context, void **);
66
typedef void (*fini_proc)
67
(krb5_context, void *);
69
/* Internal authdata system for copying TGS-REQ authdata to ticket */
70
static krb5_error_code handle_request_authdata
71
(krb5_context context,
73
krb5_db_entry *client,
74
krb5_db_entry *server,
75
krb5_db_entry *krbtgt,
76
krb5_keyblock *client_key,
77
krb5_keyblock *server_key,
79
krb5_kdc_req *request,
80
krb5_const_principal for_user_princ,
81
krb5_enc_tkt_part *enc_tkt_request,
82
krb5_enc_tkt_part *enc_tkt_reply);
84
/* Internal authdata system for handling KDC-issued authdata */
85
static krb5_error_code handle_tgt_authdata
86
(krb5_context context,
88
krb5_db_entry *client,
89
krb5_db_entry *server,
90
krb5_db_entry *krbtgt,
91
krb5_keyblock *client_key,
92
krb5_keyblock *server_key,
94
krb5_kdc_req *request,
95
krb5_const_principal for_user_princ,
96
krb5_enc_tkt_part *enc_tkt_request,
97
krb5_enc_tkt_part *enc_tkt_reply);
99
typedef struct _krb5_authdata_systems {
101
#define AUTHDATA_SYSTEM_UNKNOWN -1
102
#define AUTHDATA_SYSTEM_V0 0
103
#define AUTHDATA_SYSTEM_V1 1
105
#define AUTHDATA_FLAG_CRITICAL 0x1
107
void *plugin_context;
114
} krb5_authdata_systems;
116
static krb5_authdata_systems static_authdata_systems[] = {
117
{ "tgs_req", AUTHDATA_SYSTEM_V1, AUTHDATA_FLAG_CRITICAL, NULL, NULL, NULL, { handle_request_authdata } },
118
{ "tgt", AUTHDATA_SYSTEM_V1, AUTHDATA_FLAG_CRITICAL, NULL, NULL, NULL, { handle_tgt_authdata } },
121
static krb5_authdata_systems *authdata_systems;
122
static int n_authdata_systems;
123
static struct plugin_dir_handle authdata_plugins;
125
/* Load both v0 and v1 authdata plugins */
127
load_authdata_plugins(krb5_context context)
129
void **authdata_plugins_ftables_v0 = NULL;
130
void **authdata_plugins_ftables_v1 = NULL;
133
init_proc server_init_proc = NULL;
134
krb5_error_code code;
136
/* Attempt to load all of the authdata plugins we can find. */
137
PLUGIN_DIR_INIT(&authdata_plugins);
138
if (PLUGIN_DIR_OPEN(&authdata_plugins) == 0) {
139
if (krb5int_open_plugin_dirs(objdirs, NULL,
140
&authdata_plugins, &context->err) != 0) {
141
return KRB5_PLUGIN_NO_HANDLE;
145
/* Get the method tables provided by the loaded plugins. */
146
authdata_plugins_ftables_v0 = NULL;
147
authdata_plugins_ftables_v1 = NULL;
148
n_authdata_systems = 0;
150
if (krb5int_get_plugin_dir_data(&authdata_plugins,
152
&authdata_plugins_ftables_v1, &context->err) != 0 ||
153
krb5int_get_plugin_dir_data(&authdata_plugins,
155
&authdata_plugins_ftables_v0, &context->err) != 0) {
156
code = KRB5_PLUGIN_NO_HANDLE;
160
/* Count the valid modules. */
161
module_count = sizeof(static_authdata_systems)
162
/ sizeof(static_authdata_systems[0]);
164
if (authdata_plugins_ftables_v1 != NULL) {
165
struct krb5plugin_authdata_ftable_v1 *ftable;
167
for (i = 0; authdata_plugins_ftables_v1[i] != NULL; i++) {
168
ftable = authdata_plugins_ftables_v1[i];
169
if (ftable->authdata_proc != NULL)
174
if (authdata_plugins_ftables_v0 != NULL) {
175
struct krb5plugin_authdata_ftable_v0 *ftable;
177
for (i = 0; authdata_plugins_ftables_v0[i] != NULL; i++) {
178
ftable = authdata_plugins_ftables_v0[i];
179
if (ftable->authdata_proc != NULL)
184
/* Build the complete list of supported authdata options, and
185
* leave room for a terminator entry. */
186
authdata_systems = calloc(module_count + 1, sizeof(krb5_authdata_systems));
187
if (authdata_systems == NULL) {
192
/* Add the locally-supplied mechanisms to the dynamic list first. */
194
i < sizeof(static_authdata_systems) / sizeof(static_authdata_systems[0]);
196
authdata_systems[k] = static_authdata_systems[i];
197
/* Try to initialize the authdata system. If it fails, we'll remove it
198
* from the list of systems we'll be using. */
199
server_init_proc = static_authdata_systems[i].init;
200
if ((server_init_proc != NULL) &&
201
((*server_init_proc)(context, &authdata_systems[k].plugin_context) != 0)) {
202
memset(&authdata_systems[k], 0, sizeof(authdata_systems[k]));
208
/* Add dynamically loaded V1 plugins */
209
if (authdata_plugins_ftables_v1 != NULL) {
210
struct krb5plugin_authdata_ftable_v1 *ftable;
212
for (i = 0; authdata_plugins_ftables_v1[i] != NULL; i++) {
213
krb5_error_code initerr;
216
ftable = authdata_plugins_ftables_v1[i];
217
if ((ftable->authdata_proc == NULL)) {
220
server_init_proc = ftable->init_proc;
221
if ((server_init_proc != NULL) &&
222
((initerr = (*server_init_proc)(context, &pctx)) != 0)) {
224
emsg = krb5_get_error_message(context, initerr);
226
krb5_klog_syslog(LOG_ERR,
227
"authdata %s failed to initialize: %s",
229
krb5_free_error_message(context, emsg);
231
memset(&authdata_systems[k], 0, sizeof(authdata_systems[k]));
236
authdata_systems[k].name = ftable->name;
237
authdata_systems[k].type = AUTHDATA_SYSTEM_V1;
238
authdata_systems[k].init = server_init_proc;
239
authdata_systems[k].fini = ftable->fini_proc;
240
authdata_systems[k].handle_authdata.v1 = ftable->authdata_proc;
241
authdata_systems[k].plugin_context = pctx;
246
/* Add dynamically loaded V0 plugins */
247
if (authdata_plugins_ftables_v0 != NULL) {
248
struct krb5plugin_authdata_ftable_v0 *ftable;
250
for (i = 0; authdata_plugins_ftables_v0[i] != NULL; i++) {
251
krb5_error_code initerr;
254
ftable = authdata_plugins_ftables_v0[i];
255
if ((ftable->authdata_proc == NULL)) {
258
server_init_proc = ftable->init_proc;
259
if ((server_init_proc != NULL) &&
260
((initerr = (*server_init_proc)(context, &pctx)) != 0)) {
262
emsg = krb5_get_error_message(context, initerr);
264
krb5_klog_syslog(LOG_ERR,
265
"authdata %s failed to initialize: %s",
267
krb5_free_error_message(context, emsg);
269
memset(&authdata_systems[k], 0, sizeof(authdata_systems[k]));
274
authdata_systems[k].name = ftable->name;
275
authdata_systems[k].type = AUTHDATA_SYSTEM_V0;
276
authdata_systems[k].init = server_init_proc;
277
authdata_systems[k].fini = ftable->fini_proc;
278
authdata_systems[k].handle_authdata.v0 = ftable->authdata_proc;
279
authdata_systems[k].plugin_context = pctx;
284
n_authdata_systems = k;
285
/* Add the end-of-list marker. */
286
authdata_systems[k].name = "[end]";
287
authdata_systems[k].type = AUTHDATA_SYSTEM_UNKNOWN;
291
if (authdata_plugins_ftables_v1 != NULL)
292
krb5int_free_plugin_dir_data(authdata_plugins_ftables_v1);
293
if (authdata_plugins_ftables_v0 != NULL)
294
krb5int_free_plugin_dir_data(authdata_plugins_ftables_v0);
300
unload_authdata_plugins(krb5_context context)
303
if (authdata_systems != NULL) {
304
for (i = 0; i < n_authdata_systems; i++) {
305
if (authdata_systems[i].fini != NULL) {
306
(*authdata_systems[i].fini)(context,
307
authdata_systems[i].plugin_context);
309
memset(&authdata_systems[i], 0, sizeof(authdata_systems[i]));
311
free(authdata_systems);
312
authdata_systems = NULL;
313
n_authdata_systems = 0;
314
krb5int_close_plugin_dirs(&authdata_plugins);
319
/* Merge authdata. If copy == 0, in_authdata is invalid on return */
320
static krb5_error_code
321
merge_authdata (krb5_context context,
322
krb5_authdata **in_authdata,
323
krb5_authdata ***out_authdata,
326
size_t i, nadata = 0;
327
krb5_authdata **authdata = *out_authdata;
329
if (in_authdata == NULL || in_authdata[0] == NULL)
332
if (authdata != NULL) {
333
for (nadata = 0; authdata[nadata] != NULL; nadata++)
337
for (i = 0; in_authdata[i] != NULL; i++)
340
if (authdata == NULL) {
341
authdata = (krb5_authdata **)calloc(i + 1, sizeof(krb5_authdata *));
343
authdata = (krb5_authdata **)realloc(authdata,
344
((nadata + i + 1) * sizeof(krb5_authdata *)));
346
if (authdata == NULL)
350
krb5_error_code code;
353
code = krb5_copy_authdata(context, in_authdata, &tmp);
360
for (i = 0; in_authdata[i] != NULL; i++)
361
authdata[nadata + i] = in_authdata[i];
363
authdata[nadata + i] = NULL;
367
*out_authdata = authdata;
372
/* Handle copying TGS-REQ authorization data into reply */
373
static krb5_error_code
374
handle_request_authdata (krb5_context context,
376
krb5_db_entry *client,
377
krb5_db_entry *server,
378
krb5_db_entry *krbtgt,
379
krb5_keyblock *client_key,
380
krb5_keyblock *server_key,
382
krb5_kdc_req *request,
383
krb5_const_principal for_user_princ,
384
krb5_enc_tkt_part *enc_tkt_request,
385
krb5_enc_tkt_part *enc_tkt_reply)
387
krb5_error_code code;
390
if (request->msg_type != KRB5_TGS_REQ ||
391
request->authorization_data.ciphertext.data == NULL)
394
assert(enc_tkt_request != NULL);
396
scratch.length = request->authorization_data.ciphertext.length;
397
scratch.data = malloc(scratch.length);
398
if (scratch.data == NULL)
401
code = krb5_c_decrypt(context,
402
enc_tkt_request->session,
403
KRB5_KEYUSAGE_TGS_REQ_AD_SESSKEY,
404
0, &request->authorization_data,
407
code = krb5_c_decrypt(context,
409
KRB5_KEYUSAGE_TGS_REQ_AD_SUBKEY,
410
0, &request->authorization_data,
418
/* scratch now has the authorization data, so we decode it, and make
419
* it available to subsequent authdata plugins */
420
code = decode_krb5_authdata(&scratch, &request->unenc_authdata);
428
code = merge_authdata(context, request->unenc_authdata,
429
&enc_tkt_reply->authorization_data, TRUE /* copy */);
434
/* Handle backend-managed authorization data */
435
static krb5_error_code
436
handle_tgt_authdata (krb5_context context,
438
krb5_db_entry *client,
439
krb5_db_entry *server,
440
krb5_db_entry *krbtgt,
441
krb5_keyblock *client_key,
442
krb5_keyblock *server_key,
444
krb5_kdc_req *request,
445
krb5_const_principal for_user_princ,
446
krb5_enc_tkt_part *enc_tkt_request,
447
krb5_enc_tkt_part *enc_tkt_reply)
449
krb5_error_code code;
450
krb5_authdata **db_authdata = NULL;
451
krb5_db_entry ad_entry;
453
krb5_boolean tgs_req = (request->msg_type == KRB5_TGS_REQ);
454
krb5_const_principal actual_client;
457
* Check whether KDC issued authorization data should be included.
458
* A server can explicitly disable the inclusion of authorization
459
* data by setting the KRB5_KDB_NO_AUTH_DATA_REQUIRED flag on its
460
* principal entry. Otherwise authorization data will be included
461
* if it was present in the TGT, the client is from another realm
462
* or protocol transition/constrained delegation was used, or, in
463
* the AS-REQ case, if the pre-auth data indicated the PAC should
466
* We permit sign_authorization_data() to return a krb5_db_entry
467
* representing the principal associated with the authorization
468
* data, in case that principal is not local to our realm and we
469
* need to perform additional checks (such as disabling delegation
470
* for cross-realm protocol transition below).
473
assert(enc_tkt_request != NULL);
475
if (isflagset(server->attributes, KRB5_KDB_NO_AUTH_DATA_REQUIRED))
478
if (enc_tkt_request->authorization_data == NULL &&
479
!isflagset(flags, KRB5_KDB_FLAG_CROSS_REALM | KRB5_KDB_FLAGS_S4U))
482
assert(enc_tkt_reply->times.authtime == enc_tkt_request->times.authtime);
484
if (!isflagset(flags, KRB5_KDB_FLAG_INCLUDE_PAC))
489
* We have this special case for protocol transition, because for
490
* cross-realm protocol transition the ticket reply client will
491
* not be changed until the final hop.
493
if (isflagset(flags, KRB5_KDB_FLAG_PROTOCOL_TRANSITION))
494
actual_client = for_user_princ;
496
actual_client = enc_tkt_reply->client;
499
* If the backend does not implement the sign authdata method, then
500
* just copy the TGT authorization data into the reply, except for
501
* the constrained delegation case (which requires special handling
502
* because it will promote untrusted auth data to KDC issued auth
503
* data; this requires backend-specific code)
505
* Presently this interface does not support using request auth data
506
* to influence (eg. possibly restrict) the reply auth data.
508
code = sign_db_authdata(context,
515
server_key, /* U2U or server key */
516
enc_tkt_reply->times.authtime,
517
tgs_req ? enc_tkt_request->authorization_data : NULL,
521
if (code == KRB5_KDB_DBTYPE_NOSUP) {
522
assert(ad_nprincs == 0);
523
assert(db_authdata == NULL);
525
if (isflagset(flags, KRB5_KDB_FLAG_CONSTRAINED_DELEGATION))
526
return KRB5KDC_ERR_POLICY;
529
return merge_authdata(context, enc_tkt_request->authorization_data,
530
&enc_tkt_reply->authorization_data, TRUE);
535
if (ad_nprincs != 0) {
536
if (isflagset(flags, KRB5_KDB_FLAG_PROTOCOL_TRANSITION) &&
537
isflagset(ad_entry.attributes, KRB5_KDB_DISALLOW_FORWARDABLE))
538
clear(enc_tkt_reply->flags, TKT_FLG_FORWARDABLE);
540
krb5_db_free_principal(context, &ad_entry, ad_nprincs);
542
if (ad_nprincs != 1) {
543
if (db_authdata != NULL)
544
krb5_free_authdata(context, db_authdata);
545
return KRB5KDC_ERR_PRINCIPAL_NOT_UNIQUE;
549
if (db_authdata != NULL) {
550
code = merge_authdata(context, db_authdata,
551
&enc_tkt_reply->authorization_data,
554
krb5_free_authdata(context, db_authdata);
561
handle_authdata (krb5_context context,
563
krb5_db_entry *client,
564
krb5_db_entry *server,
565
krb5_db_entry *krbtgt,
566
krb5_keyblock *client_key,
567
krb5_keyblock *server_key,
569
krb5_kdc_req *request,
570
krb5_const_principal for_user_princ,
571
krb5_enc_tkt_part *enc_tkt_request,
572
krb5_enc_tkt_part *enc_tkt_reply)
574
krb5_error_code code = 0;
577
assert(enc_tkt_reply->authorization_data == NULL);
578
for (i = 0; i < n_authdata_systems; i++) {
579
const krb5_authdata_systems *asys = &authdata_systems[i];
581
switch (asys->type) {
582
case AUTHDATA_SYSTEM_V0:
583
/* V0 was only in AS-REQ code path */
584
if (request->msg_type != KRB5_AS_REQ)
587
code = (*asys->handle_authdata.v0)(context, client, req_pkt,
588
request, enc_tkt_reply);
590
case AUTHDATA_SYSTEM_V1:
591
code = (*asys->handle_authdata.v1)(context, flags,
592
client, server, krbtgt,
593
client_key, server_key,
594
req_pkt, request, for_user_princ,
605
emsg = krb5_get_error_message (context, code);
606
krb5_klog_syslog (LOG_INFO,
607
"authdata (%s) handling failure: %s",
609
krb5_free_error_message (context, emsg);
611
if (asys->flags & AUTHDATA_FLAG_CRITICAL)