dmake: do not set MAKEFLAGS=k
[unleashed/tickless.git] / usr / src / cmd / krb5 / krb5kdc / do_as_req.c
blob1afac1cd3b706d09b53688ff2aa5816d457c0d21
1 /*
2 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
3 * Use is subject to license terms.
4 */
7 /*
8 * kdc/do_as_req.c
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
36 #define NEED_SOCKETS
37 #include "k5-int.h"
38 #include "com_err.h"
40 #include <syslog.h>
41 #ifdef HAVE_NETINET_IN_H
42 #include <sys/types.h>
43 #include <netinet/in.h>
44 #ifndef hpux
45 #include <arpa/inet.h>
46 #endif /* hpux */
47 #endif /* HAVE_NETINET_IN_H */
49 #include "kdc_util.h"
50 #include "policy.h"
51 #include "adm.h"
52 #include "adm_proto.h"
53 #include "extern.h"
55 static krb5_error_code prepare_error_as (krb5_kdc_req *, int, krb5_data *,
56 krb5_data **, const char *);
58 /*ARGSUSED*/
59 krb5_error_code
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;
64 krb5_kdc_rep reply;
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;
70 krb5_boolean more;
71 krb5_timestamp kdc_time, authtime, etime = 0;
72 krb5_keyblock session_key;
73 krb5_keyblock encrypting_key;
74 const char *status;
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 */
80 krb5_data e_data;
81 register int i;
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;
86 char ktypestr[128];
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;
93 e_data.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),
106 &from_in4,
107 fromstringbuf, sizeof(fromstringbuf));
108 if (!fromstring)
109 fromstring = "<unknown>";
111 if (!request->client) {
112 status = "NULL_CLIENT";
113 errcode = KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN;
114 goto errout;
116 if ((errcode = krb5_unparse_name(kdc_context, request->client, &cname))) {
117 status = "UNPARSING_CLIENT";
118 goto errout;
120 limit_string(cname);
121 if (!request->server) {
122 status = "NULL_SERVER";
123 errcode = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
124 goto errout;
126 if ((errcode = krb5_unparse_name(kdc_context, request->server, &sname))) {
127 status = "UNPARSING_SERVER";
128 goto errout;
130 limit_string(sname);
132 c_nprincs = 1;
133 if ((errcode = krb5_db_get_principal(kdc_context, request->client,
134 &client, &c_nprincs, &more))) {
135 status = "LOOKING_UP_CLIENT";
136 c_nprincs = 0;
137 goto errout;
139 if (more) {
140 status = "NON-UNIQUE_CLIENT";
141 errcode = KRB5KDC_ERR_PRINCIPAL_NOT_UNIQUE;
142 goto errout;
143 } else if (c_nprincs != 1) {
144 status = "CLIENT_NOT_FOUND";
145 #ifdef KRBCONF_VAGUE_ERRORS
146 errcode = KRB5KRB_ERR_GENERIC;
147 #else
148 errcode = KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN;
149 #endif
150 goto errout;
153 s_nprincs = 1;
154 if ((errcode = krb5_db_get_principal(kdc_context, request->server, &server,
155 &s_nprincs, &more))) {
156 status = "LOOKING_UP_SERVER";
157 goto errout;
159 if (more) {
160 status = "NON-UNIQUE_SERVER";
161 errcode = KRB5KDC_ERR_PRINCIPAL_NOT_UNIQUE;
162 goto errout;
163 } else if (s_nprincs != 1) {
164 status = "SERVER_NOT_FOUND";
165 errcode = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
166 goto errout;
169 if ((errcode = krb5_timeofday(kdc_context, &kdc_time))) {
170 status = "TIMEOFDAY";
171 goto errout;
174 if ((errcode = validate_as_request(request, client, server,
175 kdc_time, &status))) {
176 if (!status)
177 status = "UNKNOWN_REASON";
178 errcode += ERROR_TABLE_BASE_krb5;
179 goto errout;
183 * Select the keytype for the ticket session key.
185 if ((useenctype = select_session_keytype(kdc_context, &server,
186 request->nktypes,
187 request->ktype)) == 0) {
188 /* unsupported ktype */
189 status = "BAD_ENCRYPTION_TYPE";
190 errcode = KRB5KDC_ERR_ETYPE_NOSUPP;
191 goto errout;
194 if ((errcode = krb5_c_make_random_key(kdc_context, useenctype,
195 &session_key))) {
196 /* random key failed */
197 status = "RANDOM_KEY_FAILED";
198 goto errout;
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;
230 } else
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 =
246 min(until,
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))));
278 } else
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);
295 if (errcode) {
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;
309 update_client = 1;
310 #endif
311 status = "PREAUTH_FAILED";
312 #ifdef KRBCONF_VAGUE_ERRORS
313 errcode = KRB5KRB_ERR_GENERIC;
314 #endif
315 goto errout;
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);
325 if (status) {
326 errcode = KRB5KDC_ERR_PREAUTH_REQUIRED;
327 get_preauth_hint_list(request, &client, &server, &e_data);
328 goto errout;
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 */
340 &server_key))) {
341 status = "FINDING_SERVER_KEY";
342 goto errout;
345 /* convert server.key into a real key (it may be encrypted
346 in the database) */
347 if ((errcode = krb5_dbekd_decrypt_key_data(kdc_context, &master_keyblock,
348 server_key, &encrypting_key,
349 NULL))) {
350 status = "DECRYPT_SERVER_KEY";
351 goto errout;
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;
357 if (errcode) {
358 status = "ENCRYPTING_TICKET";
359 goto errout;
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))
371 continue;
373 if (!krb5_dbe_find_enctype(kdc_context, &client, useenctype, -1,
374 0, &client_key))
375 break;
377 if (!(client_key)) {
378 /* Cannot find an appropriate key */
379 status = "CANT_FIND_CLIENT_KEY";
380 errcode = KRB5KDC_ERR_ETYPE_NOSUPP;
381 goto errout;
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,
387 NULL))) {
388 status = "DECRYPT_CLIENT_KEY";
389 goto errout;
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";
400 goto errout;
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);
409 else
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
417 is used for ktime */
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);
426 if (errcode) {
427 status = "KDC_RETURN_PADATA";
428 goto errout;
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;
441 if (errcode) {
442 status = "ENCODE_KDC_REP";
443 goto errout;
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);
451 /* SUNW14resync:
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,
456 cname, sname, 0);
457 rep_etypes2str(rep_etypestr, sizeof(rep_etypestr), &reply);
458 krb5_klog_syslog(LOG_INFO,
459 "AS_REQ (%s) %s: ISSUE: authtime %d, "
460 "%s, %s for %s",
461 ktypestr,
462 fromstring, authtime,
463 rep_etypestr,
464 cname, sname);
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;
472 update_client = 1;
473 #endif /* KRBCONF_KDC_MODIFIES_KDB */
475 errout:
476 if (pa_context)
477 free_padata_context(kdc_context, &pa_context);
479 if (status) {
480 const char *emsg = NULL;
481 if (errcode)
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",
487 ktypestr,
488 fromstring, status,
489 cname ? cname : "<unknown client>",
490 sname ? sname : "<unknown server>",
491 errcode ? ", " : "",
492 errcode ? emsg : "");
493 if (errcode)
494 krb5_free_error_message (kdc_context, emsg);
496 if (errcode) {
497 int got_err = 0;
498 if (status == 0) {
499 status = krb5_get_error_message (kdc_context, errcode);
500 got_err = 1;
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,
507 status);
508 if (got_err) {
509 krb5_free_error_message (kdc_context, status);
510 status = 0;
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);
518 if (reply.padata)
519 krb5_free_pa_data(kdc_context, reply.padata);
521 free(cname);
522 free(sname);
523 if (c_nprincs) {
524 #ifdef KRBCONF_KDC_MODIFIES_KDB
525 if (update_client) {
526 krb5_db_put_principal(kdc_context, &client, &c_nprincs);
528 * ptooey. We want krb5_db_sync() or something like that.
530 krb5_db_fini(kdc_context);
531 if (kdc_active_realm->realm_dbname)
532 krb5_db_set_name(kdc_active_realm->realm_context,
533 kdc_active_realm->realm_dbname);
534 krb5_db_init(kdc_context);
535 /* Reset master key */
536 krb5_db_set_mkey(kdc_context, &kdc_active_realm->realm_mkey);
538 #endif /* KRBCONF_KDC_MODIFIES_KDB */
539 krb5_db_free_principal(kdc_context, &client, c_nprincs);
541 if (s_nprincs)
542 krb5_db_free_principal(kdc_context, &server, s_nprincs);
543 if (session_key.contents)
544 krb5_free_keyblock_contents(kdc_context, &session_key);
545 if (ticket_reply.enc_part.ciphertext.data) {
546 memset(ticket_reply.enc_part.ciphertext.data , 0,
547 ticket_reply.enc_part.ciphertext.length);
548 free(ticket_reply.enc_part.ciphertext.data);
551 krb5_free_data_contents(kdc_context, &e_data);
553 return errcode;
556 static krb5_error_code
557 prepare_error_as (krb5_kdc_req *request, int error, krb5_data *e_data,
558 krb5_data **response, const char *status)
560 krb5_error errpkt;
561 krb5_error_code retval;
562 krb5_data *scratch;
564 errpkt.ctime = request->nonce;
565 errpkt.cusec = 0;
567 if ((retval = krb5_us_timeofday(kdc_context, &errpkt.stime,
568 &errpkt.susec)))
569 return(retval);
570 errpkt.error = error;
571 errpkt.server = request->server;
572 errpkt.client = request->client;
573 errpkt.text.length = strlen(status)+1;
574 if (!(errpkt.text.data = malloc(errpkt.text.length)))
575 return ENOMEM;
576 (void) strcpy(errpkt.text.data, status);
578 if (!(scratch = (krb5_data *)malloc(sizeof(*scratch)))) {
579 free(errpkt.text.data);
580 return ENOMEM;
582 if (e_data && e_data->data) {
583 errpkt.e_data = *e_data;
584 } else {
585 errpkt.e_data.length = 0;
586 errpkt.e_data.data = 0;
589 retval = krb5_mk_error(kdc_context, &errpkt, scratch);
590 free(errpkt.text.data);
591 if (retval)
592 free(scratch);
593 else
594 *response = scratch;
596 return retval;