2 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
3 * Use is subject to license terms.
10 * Copyright 1990,1991 by the Massachusetts Institute of Technology.
11 * All Rights Reserved.
13 * Export of this software from the United States of America may
14 * require a specific license from the United States Government.
15 * It is the responsibility of any person or organization contemplating
16 * export to obtain such a license before exporting.
18 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
19 * distribute this software and its documentation for any purpose and
20 * without fee is hereby granted, provided that the above copyright
21 * notice appear in all copies and that both that copyright notice and
22 * this permission notice appear in supporting documentation, and that
23 * the name of M.I.T. not be used in advertising or publicity pertaining
24 * to distribution of the software without specific, written prior
25 * permission. Furthermore if you modify this software you must label
26 * your software as modified software and not distribute it in such a
27 * fashion that it might be confused with the original M.I.T. software.
28 * M.I.T. makes no representations about the suitability of
29 * this software for any purpose. It is provided "as is" without express
30 * or implied warranty.
33 * KDC Routines to deal with AS_REQ's
41 #ifdef HAVE_NETINET_IN_H
42 #include <sys/types.h>
43 #include <netinet/in.h>
45 #include <arpa/inet.h>
47 #endif /* HAVE_NETINET_IN_H */
52 #include "adm_proto.h"
55 static krb5_error_code
prepare_error_as (krb5_kdc_req
*, int, krb5_data
*,
56 krb5_data
**, const char *);
60 process_as_req(krb5_kdc_req
*request
, krb5_data
*req_pkt
,
61 const krb5_fulladdr
*from
, krb5_data
**response
)
63 krb5_db_entry client
, server
;
65 krb5_enc_kdc_rep_part reply_encpart
;
66 krb5_ticket ticket_reply
;
67 krb5_enc_tkt_part enc_tkt_reply
;
68 krb5_error_code errcode
;
69 int c_nprincs
= 0, s_nprincs
= 0;
71 krb5_timestamp kdc_time
, authtime
, etime
= 0;
72 krb5_keyblock session_key
;
73 krb5_keyblock encrypting_key
;
75 krb5_key_data
*server_key
, *client_key
;
76 krb5_enctype useenctype
;
77 #ifdef KRBCONF_KDC_MODIFIES_KDB
78 krb5_boolean update_client
= 0;
79 #endif /* KRBCONF_KDC_MODIFIES_KDB */
82 krb5_timestamp until
, rtime
;
83 long long tmp_client_times
, tmp_server_times
, tmp_realm_times
;
84 char *cname
= 0, *sname
= 0;
85 const char *fromstring
= 0;
87 char rep_etypestr
[128];
88 char fromstringbuf
[70];
89 void *pa_context
= NULL
;
90 struct in_addr from_in4
; /* IPv4 address of sender */
92 ticket_reply
.enc_part
.ciphertext
.data
= 0;
94 (void) memset(&encrypting_key
, 0, sizeof(krb5_keyblock
));
95 reply
.padata
= 0; /* avoid bogus free in error_out */
96 (void) memset(&session_key
, 0, sizeof(krb5_keyblock
));
97 enc_tkt_reply
.authorization_data
= NULL
;
99 ktypes2str(ktypestr
, sizeof(ktypestr
),
100 request
->nktypes
, request
->ktype
);
102 (void) memcpy(&from_in4
, from
->address
->contents
, /* SUNW */
103 sizeof (struct in_addr
));
105 fromstring
= inet_ntop(ADDRTYPE2FAMILY (from
->address
->addrtype
),
107 fromstringbuf
, sizeof(fromstringbuf
));
109 fromstring
= "<unknown>";
111 if (!request
->client
) {
112 status
= "NULL_CLIENT";
113 errcode
= KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN
;
116 if ((errcode
= krb5_unparse_name(kdc_context
, request
->client
, &cname
))) {
117 status
= "UNPARSING_CLIENT";
121 if (!request
->server
) {
122 status
= "NULL_SERVER";
123 errcode
= KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN
;
126 if ((errcode
= krb5_unparse_name(kdc_context
, request
->server
, &sname
))) {
127 status
= "UNPARSING_SERVER";
133 if ((errcode
= krb5_db_get_principal(kdc_context
, request
->client
,
134 &client
, &c_nprincs
, &more
))) {
135 status
= "LOOKING_UP_CLIENT";
140 status
= "NON-UNIQUE_CLIENT";
141 errcode
= KRB5KDC_ERR_PRINCIPAL_NOT_UNIQUE
;
143 } else if (c_nprincs
!= 1) {
144 status
= "CLIENT_NOT_FOUND";
145 #ifdef KRBCONF_VAGUE_ERRORS
146 errcode
= KRB5KRB_ERR_GENERIC
;
148 errcode
= KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN
;
154 if ((errcode
= krb5_db_get_principal(kdc_context
, request
->server
, &server
,
155 &s_nprincs
, &more
))) {
156 status
= "LOOKING_UP_SERVER";
160 status
= "NON-UNIQUE_SERVER";
161 errcode
= KRB5KDC_ERR_PRINCIPAL_NOT_UNIQUE
;
163 } else if (s_nprincs
!= 1) {
164 status
= "SERVER_NOT_FOUND";
165 errcode
= KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN
;
169 if ((errcode
= krb5_timeofday(kdc_context
, &kdc_time
))) {
170 status
= "TIMEOFDAY";
174 if ((errcode
= validate_as_request(request
, client
, server
,
175 kdc_time
, &status
))) {
177 status
= "UNKNOWN_REASON";
178 errcode
+= ERROR_TABLE_BASE_krb5
;
183 * Select the keytype for the ticket session key.
185 if ((useenctype
= select_session_keytype(kdc_context
, &server
,
187 request
->ktype
)) == 0) {
188 /* unsupported ktype */
189 status
= "BAD_ENCRYPTION_TYPE";
190 errcode
= KRB5KDC_ERR_ETYPE_NOSUPP
;
194 if ((errcode
= krb5_c_make_random_key(kdc_context
, useenctype
,
196 /* random key failed */
197 status
= "RANDOM_KEY_FAILED";
201 ticket_reply
.server
= request
->server
;
203 enc_tkt_reply
.flags
= 0;
204 setflag(enc_tkt_reply
.flags
, TKT_FLG_INITIAL
);
206 /* It should be noted that local policy may affect the */
207 /* processing of any of these flags. For example, some */
208 /* realms may refuse to issue renewable tickets */
210 if (isflagset(request
->kdc_options
, KDC_OPT_FORWARDABLE
))
211 setflag(enc_tkt_reply
.flags
, TKT_FLG_FORWARDABLE
);
213 if (isflagset(request
->kdc_options
, KDC_OPT_PROXIABLE
))
214 setflag(enc_tkt_reply
.flags
, TKT_FLG_PROXIABLE
);
216 if (isflagset(request
->kdc_options
, KDC_OPT_ALLOW_POSTDATE
))
217 setflag(enc_tkt_reply
.flags
, TKT_FLG_MAY_POSTDATE
);
219 enc_tkt_reply
.session
= &session_key
;
220 enc_tkt_reply
.client
= request
->client
;
221 enc_tkt_reply
.transited
.tr_type
= KRB5_DOMAIN_X500_COMPRESS
;
222 enc_tkt_reply
.transited
.tr_contents
= empty_string
; /* equivalent of "" */
224 enc_tkt_reply
.times
.authtime
= kdc_time
;
226 if (isflagset(request
->kdc_options
, KDC_OPT_POSTDATED
)) {
227 setflag(enc_tkt_reply
.flags
, TKT_FLG_POSTDATED
);
228 setflag(enc_tkt_reply
.flags
, TKT_FLG_INVALID
);
229 enc_tkt_reply
.times
.starttime
= request
->from
;
231 enc_tkt_reply
.times
.starttime
= kdc_time
;
233 until
= (request
->till
== 0) ? kdc_infinity
: request
->till
;
234 /* These numbers could easily be large
235 * use long long variables to ensure that they don't
236 * result in negative values when added.
239 tmp_client_times
= (long long) enc_tkt_reply
.times
.starttime
+ client
.max_life
;
241 tmp_server_times
= (long long) enc_tkt_reply
.times
.starttime
+ server
.max_life
;
243 tmp_realm_times
= (long long) enc_tkt_reply
.times
.starttime
+ max_life_for_realm
;
245 enc_tkt_reply
.times
.endtime
=
247 min(tmp_client_times
,
248 min(tmp_server_times
,
249 min(tmp_realm_times
,KRB5_KDB_EXPIRATION
))));
251 if (isflagset(request
->kdc_options
, KDC_OPT_RENEWABLE_OK
) &&
252 !isflagset(client
.attributes
, KRB5_KDB_DISALLOW_RENEWABLE
) &&
253 (enc_tkt_reply
.times
.endtime
< request
->till
)) {
255 /* we set the RENEWABLE option for later processing */
257 setflag(request
->kdc_options
, KDC_OPT_RENEWABLE
);
258 request
->rtime
= request
->till
;
260 rtime
= (request
->rtime
== 0) ? kdc_infinity
: request
->rtime
;
262 if (isflagset(request
->kdc_options
, KDC_OPT_RENEWABLE
)) {
264 * XXX Should we squelch the output renew_till to be no
265 * earlier than the endtime of the ticket?
267 setflag(enc_tkt_reply
.flags
, TKT_FLG_RENEWABLE
);
268 tmp_client_times
= (double) enc_tkt_reply
.times
.starttime
+ client
.max_renewable_life
;
270 tmp_server_times
= (double) enc_tkt_reply
.times
.starttime
+ server
.max_renewable_life
;
272 tmp_realm_times
= (double) enc_tkt_reply
.times
.starttime
+ max_renewable_life_for_realm
;
274 enc_tkt_reply
.times
.renew_till
=
275 min(rtime
, min(tmp_client_times
,
276 min(tmp_server_times
,
277 min(tmp_realm_times
,KRB5_KDB_EXPIRATION
))));
279 enc_tkt_reply
.times
.renew_till
= 0; /* XXX */
281 /* starttime is optional, and treated as authtime if not present.
282 so we can nuke it if it matches */
283 if (enc_tkt_reply
.times
.starttime
== enc_tkt_reply
.times
.authtime
)
284 enc_tkt_reply
.times
.starttime
= 0;
286 enc_tkt_reply
.caddrs
= request
->addresses
;
287 enc_tkt_reply
.authorization_data
= 0;
290 * Check the preauthentication if it is there.
292 if (request
->padata
) {
293 errcode
= check_padata(kdc_context
, &client
, req_pkt
, request
,
294 &enc_tkt_reply
, &pa_context
, &e_data
);
296 #ifdef KRBCONF_KDC_MODIFIES_KDB
298 * Note: this doesn't work if you're using slave servers!!!
299 * It also causes the database to be modified (and thus
300 * need to be locked) frequently.
302 if (client
.fail_auth_count
< KRB5_MAX_FAIL_COUNT
) {
303 client
.fail_auth_count
= client
.fail_auth_count
+ 1;
304 if (client
.fail_auth_count
== KRB5_MAX_FAIL_COUNT
) {
305 client
.attributes
|= KRB5_KDB_DISALLOW_ALL_TIX
;
308 client
.last_failed
= kdc_time
;
311 status
= "PREAUTH_FAILED";
312 #ifdef KRBCONF_VAGUE_ERRORS
313 errcode
= KRB5KRB_ERR_GENERIC
;
320 * Final check before handing out ticket: If the client requires
321 * preauthentication, verify that the proper kind of
322 * preauthentication was carried out.
324 status
= missing_required_preauth(&client
, &server
, &enc_tkt_reply
);
326 errcode
= KRB5KDC_ERR_PREAUTH_REQUIRED
;
327 get_preauth_hint_list(request
, &client
, &server
, &e_data
);
331 ticket_reply
.enc_part2
= &enc_tkt_reply
;
334 * Find the server key
336 if ((errcode
= krb5_dbe_find_enctype(kdc_context
, &server
,
337 -1, /* ignore keytype */
338 -1, /* Ignore salttype */
339 0, /* Get highest kvno */
341 status
= "FINDING_SERVER_KEY";
345 /* convert server.key into a real key (it may be encrypted
347 if ((errcode
= krb5_dbekd_decrypt_key_data(kdc_context
, &master_keyblock
,
348 server_key
, &encrypting_key
,
350 status
= "DECRYPT_SERVER_KEY";
354 errcode
= krb5_encrypt_tkt_part(kdc_context
, &encrypting_key
, &ticket_reply
);
355 krb5_free_keyblock_contents(kdc_context
, &encrypting_key
);
356 encrypting_key
.contents
= 0;
358 status
= "ENCRYPTING_TICKET";
361 ticket_reply
.enc_part
.kvno
= server_key
->key_data_kvno
;
364 * Find the appropriate client key. We search in the order specified
365 * by request keytype list.
367 client_key
= (krb5_key_data
*) NULL
;
368 for (i
= 0; i
< request
->nktypes
; i
++) {
369 useenctype
= request
->ktype
[i
];
370 if (!krb5_c_valid_enctype(useenctype
))
373 if (!krb5_dbe_find_enctype(kdc_context
, &client
, useenctype
, -1,
378 /* Cannot find an appropriate key */
379 status
= "CANT_FIND_CLIENT_KEY";
380 errcode
= KRB5KDC_ERR_ETYPE_NOSUPP
;
384 /* convert client.key_data into a real key */
385 if ((errcode
= krb5_dbekd_decrypt_key_data(kdc_context
, &master_keyblock
,
386 client_key
, &encrypting_key
,
388 status
= "DECRYPT_CLIENT_KEY";
391 encrypting_key
.enctype
= useenctype
;
393 /* Start assembling the response */
394 reply
.msg_type
= KRB5_AS_REP
;
395 reply
.client
= request
->client
;
396 reply
.ticket
= &ticket_reply
;
397 reply_encpart
.session
= &session_key
;
398 if ((errcode
= fetch_last_req_info(&client
, &reply_encpart
.last_req
))) {
399 status
= "FETCH_LAST_REQ";
402 reply_encpart
.nonce
= request
->nonce
;
405 * Take the minimum of expiration or pw_expiration if not zero.
407 if (client
.expiration
!= 0 && client
.pw_expiration
!= 0)
408 etime
= min(client
.expiration
, client
.pw_expiration
);
410 etime
= client
.expiration
? client
.expiration
: client
.pw_expiration
;
412 reply_encpart
.key_exp
= etime
;
413 reply_encpart
.flags
= enc_tkt_reply
.flags
;
414 reply_encpart
.server
= ticket_reply
.server
;
416 /* copy the time fields EXCEPT for authtime; it's location
418 reply_encpart
.times
= enc_tkt_reply
.times
;
419 reply_encpart
.times
.authtime
= authtime
= kdc_time
;
421 reply_encpart
.caddrs
= enc_tkt_reply
.caddrs
;
423 /* Fetch the padata info to be returned */
424 errcode
= return_padata(kdc_context
, &client
, req_pkt
, request
,
425 &reply
, client_key
, &encrypting_key
, &pa_context
);
427 status
= "KDC_RETURN_PADATA";
431 /* now encode/encrypt the response */
433 reply
.enc_part
.enctype
= encrypting_key
.enctype
;
435 errcode
= krb5_encode_kdc_rep(kdc_context
, KRB5_AS_REP
, &reply_encpart
,
436 0, &encrypting_key
, &reply
, response
);
437 krb5_free_keyblock_contents(kdc_context
, &encrypting_key
);
438 encrypting_key
.contents
= 0;
439 reply
.enc_part
.kvno
= client_key
->key_data_kvno
;
442 status
= "ENCODE_KDC_REP";
446 /* these parts are left on as a courtesy from krb5_encode_kdc_rep so we
447 can use them in raw form if needed. But, we don't... */
448 memset(reply
.enc_part
.ciphertext
.data
, 0, reply
.enc_part
.ciphertext
.length
);
449 free(reply
.enc_part
.ciphertext
.data
);
452 * The third argument to audit_krb5kdc_as_req() is zero as the local
453 * portnumber is no longer passed to process_as_req().
455 audit_krb5kdc_as_req(&from_in4
, (in_port_t
)from
->port
, 0,
457 rep_etypes2str(rep_etypestr
, sizeof(rep_etypestr
), &reply
);
458 krb5_klog_syslog(LOG_INFO
,
459 "AS_REQ (%s) %s: ISSUE: authtime %d, "
462 fromstring
, authtime
,
466 #ifdef KRBCONF_KDC_MODIFIES_KDB
468 * If we get this far, we successfully did the AS_REQ.
470 client
.last_success
= kdc_time
;
471 client
.fail_auth_count
= 0;
473 #endif /* KRBCONF_KDC_MODIFIES_KDB */
477 free_padata_context(kdc_context
, &pa_context
);
480 const char * emsg
= 0;
482 emsg
= krb5_get_error_message (kdc_context
, errcode
);
484 audit_krb5kdc_as_req(&from_in4
, (in_port_t
)from
->port
,
485 0, cname
, sname
, errcode
);
486 krb5_klog_syslog(LOG_INFO
, "AS_REQ (%s) %s: %s: %s for %s%s%s",
489 cname
? cname
: "<unknown client>",
490 sname
? sname
: "<unknown server>",
492 errcode
? emsg
: "");
494 krb5_free_error_message (kdc_context
, emsg
);
499 status
= krb5_get_error_message (kdc_context
, errcode
);
502 errcode
-= ERROR_TABLE_BASE_krb5
;
503 if (errcode
< 0 || errcode
> 128)
504 errcode
= KRB_ERR_GENERIC
;
506 errcode
= prepare_error_as(request
, errcode
, &e_data
, response
,
509 krb5_free_error_message (kdc_context
, status
);
514 if (enc_tkt_reply
.authorization_data
!= NULL
)
515 krb5_free_authdata(kdc_context
, enc_tkt_reply
.authorization_data
);
516 if (encrypting_key
.contents
)
517 krb5_free_keyblock_contents(kdc_context
, &encrypting_key
);
519 krb5_free_pa_data(kdc_context
, reply
.padata
);
526 #ifdef KRBCONF_KDC_MODIFIES_KDB
528 krb5_db_put_principal(kdc_context
, &client
, &c_nprincs
);
530 * ptooey. We want krb5_db_sync() or something like that.
532 krb5_db_fini(kdc_context
);
533 if (kdc_active_realm
->realm_dbname
)
534 krb5_db_set_name(kdc_active_realm
->realm_context
,
535 kdc_active_realm
->realm_dbname
);
536 krb5_db_init(kdc_context
);
537 /* Reset master key */
538 krb5_db_set_mkey(kdc_context
, &kdc_active_realm
->realm_mkey
);
540 #endif /* KRBCONF_KDC_MODIFIES_KDB */
541 krb5_db_free_principal(kdc_context
, &client
, c_nprincs
);
544 krb5_db_free_principal(kdc_context
, &server
, s_nprincs
);
545 if (session_key
.contents
)
546 krb5_free_keyblock_contents(kdc_context
, &session_key
);
547 if (ticket_reply
.enc_part
.ciphertext
.data
) {
548 memset(ticket_reply
.enc_part
.ciphertext
.data
, 0,
549 ticket_reply
.enc_part
.ciphertext
.length
);
550 free(ticket_reply
.enc_part
.ciphertext
.data
);
553 krb5_free_data_contents(kdc_context
, &e_data
);
558 static krb5_error_code
559 prepare_error_as (krb5_kdc_req
*request
, int error
, krb5_data
*e_data
,
560 krb5_data
**response
, const char *status
)
563 krb5_error_code retval
;
566 errpkt
.ctime
= request
->nonce
;
569 if ((retval
= krb5_us_timeofday(kdc_context
, &errpkt
.stime
,
572 errpkt
.error
= error
;
573 errpkt
.server
= request
->server
;
574 errpkt
.client
= request
->client
;
575 errpkt
.text
.length
= strlen(status
)+1;
576 if (!(errpkt
.text
.data
= malloc(errpkt
.text
.length
)))
578 (void) strcpy(errpkt
.text
.data
, status
);
580 if (!(scratch
= (krb5_data
*)malloc(sizeof(*scratch
)))) {
581 free(errpkt
.text
.data
);
584 if (e_data
&& e_data
->data
) {
585 errpkt
.e_data
= *e_data
;
587 errpkt
.e_data
.length
= 0;
588 errpkt
.e_data
.data
= 0;
591 retval
= krb5_mk_error(kdc_context
, &errpkt
, scratch
);
592 free(errpkt
.text
.data
);