dmake: do not set MAKEFLAGS=k
[unleashed/tickless.git] / usr / src / cmd / krb5 / krb5kdc / kdc_util.c
blobadf54ea3fce3bec1b7c19a5fc76531bdbdf6e68c
1 /*
2 * kdc/kdc_util.c
4 * Copyright 1990,1991 by the Massachusetts Institute of Technology.
5 * All Rights Reserved.
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.
27 * Utility functions for the KDC implementation.
32 #include "k5-int.h"
33 #include "kdc_util.h"
34 #include "extern.h"
35 #include <stdio.h>
36 #include <ctype.h>
37 #include <syslog.h>
38 #include "adm.h"
39 #include "adm_proto.h"
40 #include <limits.h>
42 #ifdef USE_RCACHE
43 static char *kdc_current_rcname = (char *) NULL;
44 krb5_deltat rc_lifetime; /* See kdc_initialize_rcache() */
45 #endif
47 #ifdef USE_RCACHE
49 * initialize the replay cache.
51 krb5_error_code
52 kdc_initialize_rcache(krb5_context kcontext, char *rcache_name)
54 krb5_error_code retval;
55 char *rcname;
56 char *sname;
58 rcname = (rcache_name) ? rcache_name : kdc_current_rcname;
60 /* rc_lifetime used elsewhere to verify we're not */
61 /* replaying really old data */
62 rc_lifetime = kcontext->clockskew;
64 if (!rcname)
65 rcname = KDCRCACHE;
66 if (!(retval = krb5_rc_resolve_full(kcontext, &kdc_rcache, rcname))) {
67 /* Recover or initialize the replay cache */
68 if (!(retval = krb5_rc_recover(kcontext, kdc_rcache)) ||
69 !(retval = krb5_rc_initialize(kcontext,
70 kdc_rcache,
71 kcontext->clockskew))
72 ) {
73 /* Expunge the replay cache */
74 if (!(retval = krb5_rc_expunge(kcontext, kdc_rcache))) {
75 sname = kdc_current_rcname;
76 kdc_current_rcname = strdup(rcname);
77 free(sname);
80 if (retval)
81 krb5_rc_close(kcontext, kdc_rcache);
83 return(retval);
85 #endif
88 * concatenate first two authdata arrays, returning an allocated replacement.
89 * The replacement should be freed with krb5_free_authdata().
91 krb5_error_code
92 concat_authorization_data(krb5_authdata **first, krb5_authdata **second,
93 krb5_authdata ***output)
95 register int i, j;
96 register krb5_authdata **ptr, **retdata;
98 /* count up the entries */
99 i = 0;
100 if (first)
101 for (ptr = first; *ptr; ptr++)
102 i++;
103 if (second)
104 for (ptr = second; *ptr; ptr++)
105 i++;
107 retdata = (krb5_authdata **)malloc((i+1)*sizeof(*retdata));
108 if (!retdata)
109 return ENOMEM;
110 retdata[i] = 0; /* null-terminated array */
111 for (i = 0, j = 0, ptr = first; j < 2 ; ptr = second, j++)
112 while (ptr && *ptr) {
113 /* now walk & copy */
114 retdata[i] = (krb5_authdata *)malloc(sizeof(*retdata[i]));
115 if (!retdata[i]) {
116 krb5_free_authdata(kdc_context, retdata);
117 return ENOMEM;
119 *retdata[i] = **ptr;
120 if (!(retdata[i]->contents =
121 (krb5_octet *)malloc(retdata[i]->length))) {
122 free((char *)retdata[i]);
123 retdata[i] = 0;
124 krb5_free_authdata(kdc_context, retdata);
125 return ENOMEM;
127 memcpy((char *) retdata[i]->contents,
128 (char *)(*ptr)->contents,
129 retdata[i]->length);
131 ptr++;
132 i++;
134 *output = retdata;
135 return 0;
138 krb5_boolean
139 realm_compare(krb5_principal princ1, krb5_principal princ2)
141 krb5_data *realm1 = krb5_princ_realm(kdc_context, princ1);
142 krb5_data *realm2 = krb5_princ_realm(kdc_context, princ2);
144 return((realm1->length == realm2->length) &&
145 !memcmp(realm1->data, realm2->data, realm1->length));
149 * Returns TRUE if the kerberos principal is the name of a Kerberos ticket
150 * service.
152 krb5_boolean krb5_is_tgs_principal(krb5_principal principal)
154 if ((krb5_princ_size(kdc_context, principal) > 0) &&
155 (krb5_princ_component(kdc_context, principal, 0)->length ==
156 KRB5_TGS_NAME_SIZE) &&
157 (!memcmp(krb5_princ_component(kdc_context, principal, 0)->data,
158 KRB5_TGS_NAME, KRB5_TGS_NAME_SIZE)))
159 return TRUE;
160 return FALSE;
164 * given authentication data (provides seed for checksum), verify checksum
165 * for source data.
167 static krb5_error_code
168 comp_cksum(krb5_context kcontext, krb5_data *source, krb5_ticket *ticket,
169 krb5_checksum *his_cksum)
171 krb5_error_code retval;
172 krb5_boolean valid;
174 if (!krb5_c_valid_cksumtype(his_cksum->checksum_type))
175 return KRB5KDC_ERR_SUMTYPE_NOSUPP;
177 /* must be collision proof */
178 if (!krb5_c_is_coll_proof_cksum(his_cksum->checksum_type))
179 return KRB5KRB_AP_ERR_INAPP_CKSUM;
181 /* verify checksum */
182 if ((retval = krb5_c_verify_checksum(kcontext, ticket->enc_part2->session,
183 KRB5_KEYUSAGE_TGS_REQ_AUTH_CKSUM,
184 source, his_cksum, &valid)))
185 return(retval);
187 if (!valid)
188 return(KRB5KRB_AP_ERR_BAD_INTEGRITY);
190 return(0);
193 krb5_error_code
194 kdc_process_tgs_req(krb5_kdc_req *request, const krb5_fulladdr *from,
195 krb5_data *pkt, krb5_ticket **ticket,
196 krb5_keyblock **subkey)
198 krb5_pa_data ** tmppa;
199 krb5_ap_req * apreq;
200 krb5_error_code retval;
201 krb5_data scratch1;
202 krb5_data * scratch = NULL;
203 krb5_boolean foreign_server = FALSE;
204 krb5_auth_context auth_context = NULL;
205 krb5_authenticator * authenticator = NULL;
206 krb5_checksum * his_cksum = NULL;
207 /* krb5_keyblock * key = NULL;*/
208 /* krb5_kvno kvno = 0;*/
210 if (!request->padata)
211 return KRB5KDC_ERR_PADATA_TYPE_NOSUPP;
212 for (tmppa = request->padata; *tmppa; tmppa++) {
213 if ((*tmppa)->pa_type == KRB5_PADATA_AP_REQ)
214 break;
216 if (!*tmppa) /* cannot find any AP_REQ */
217 return KRB5KDC_ERR_PADATA_TYPE_NOSUPP;
219 scratch1.length = (*tmppa)->length;
220 scratch1.data = (char *)(*tmppa)->contents;
221 if ((retval = decode_krb5_ap_req(&scratch1, &apreq)))
222 return retval;
224 if (isflagset(apreq->ap_options, AP_OPTS_USE_SESSION_KEY) ||
225 isflagset(apreq->ap_options, AP_OPTS_MUTUAL_REQUIRED)) {
226 krb5_klog_syslog(LOG_INFO, "TGS_REQ: SESSION KEY or MUTUAL");
227 retval = KRB5KDC_ERR_POLICY;
228 goto cleanup;
231 /* If the "server" principal in the ticket is not something
232 in the local realm, then we must refuse to service the request
233 if the client claims to be from the local realm.
235 If we don't do this, then some other realm's nasty KDC can
236 claim to be authenticating a client from our realm, and we'll
237 give out tickets concurring with it!
239 we set a flag here for checking below.
241 if ((krb5_princ_realm(kdc_context, apreq->ticket->server)->length !=
242 krb5_princ_realm(kdc_context, tgs_server)->length) ||
243 memcmp(krb5_princ_realm(kdc_context, apreq->ticket->server)->data,
244 krb5_princ_realm(kdc_context, tgs_server)->data,
245 krb5_princ_realm(kdc_context, tgs_server)->length))
246 foreign_server = TRUE;
248 if ((retval = krb5_auth_con_init(kdc_context, &auth_context)))
249 goto cleanup;
251 if ((retval = krb5_auth_con_setaddrs(kdc_context, auth_context, NULL,
252 from->address)) )
253 goto cleanup_auth_context;
254 #ifdef USE_RCACHE
255 if ((retval = krb5_auth_con_setrcache(kdc_context, auth_context,
256 kdc_rcache)))
257 goto cleanup_auth_context;
258 #endif
261 if ((retval = kdc_get_server_key(apreq->ticket, &key, &kvno)))
262 goto cleanup_auth_context;
266 * XXX This is currently wrong but to fix it will require making a
267 * new keytab for groveling over the kdb.
270 retval = krb5_auth_con_setuseruserkey(kdc_context, auth_context, key);
271 krb5_free_keyblock(kdc_context, key);
272 if (retval)
273 goto cleanup_auth_context;
276 if ((retval = krb5_rd_req_decoded_anyflag(kdc_context, &auth_context, apreq,
277 apreq->ticket->server,
278 kdc_active_realm->realm_keytab,
279 NULL, ticket))) {
280 #ifdef USE_RCACHE
282 * I'm not so sure that this is right, but it's better than nothing
283 * at all.
285 * If we choke in the rd_req because of the replay cache, then attempt
286 * to reinitialize the replay cache because somebody could have deleted
287 * it from underneath us (e.g. a cron job)
289 if ((retval == KRB5_RC_IO_IO) ||
290 (retval == KRB5_RC_IO_UNKNOWN)) {
291 (void) krb5_rc_close(kdc_context, kdc_rcache);
292 kdc_rcache = (krb5_rcache) NULL;
293 if (!(retval = kdc_initialize_rcache(kdc_context, (char *) NULL))) {
294 if ((retval = krb5_auth_con_setrcache(kdc_context, auth_context,
295 kdc_rcache)) ||
296 (retval = krb5_rd_req_decoded_anyflag(kdc_context, &auth_context,
297 apreq, apreq->ticket->server,
298 kdc_active_realm->realm_keytab,
299 NULL, ticket))
301 goto cleanup_auth_context;
303 } else
304 goto cleanup_auth_context;
305 #else
306 goto cleanup_auth_context;
307 #endif
310 /* "invalid flag" tickets can must be used to validate */
311 if (isflagset((*ticket)->enc_part2->flags, TKT_FLG_INVALID)
312 && !isflagset(request->kdc_options, KDC_OPT_VALIDATE)) {
313 retval = KRB5KRB_AP_ERR_TKT_INVALID;
314 goto cleanup_auth_context;
317 if ((retval = krb5_auth_con_getrecvsubkey(kdc_context,
318 auth_context, subkey)))
319 goto cleanup_auth_context;
321 if ((retval = krb5_auth_con_getauthenticator(kdc_context, auth_context,
322 &authenticator)))
323 goto cleanup_auth_context;
325 /* Check for a checksum */
326 if (!(his_cksum = authenticator->checksum)) {
327 retval = KRB5KRB_AP_ERR_INAPP_CKSUM;
328 goto cleanup_authenticator;
331 /* make sure the client is of proper lineage (see above) */
332 if (foreign_server) {
333 krb5_data *tkt_realm = krb5_princ_realm(kdc_context,
334 (*ticket)->enc_part2->client);
335 krb5_data *tgs_realm = krb5_princ_realm(kdc_context, tgs_server);
336 if (tkt_realm->length == tgs_realm->length &&
337 !memcmp(tkt_realm->data, tgs_realm->data, tgs_realm->length)) {
338 /* someone in a foreign realm claiming to be local */
339 krb5_klog_syslog(LOG_INFO, "PROCESS_TGS: failed lineage check");
340 retval = KRB5KDC_ERR_POLICY;
341 goto cleanup_authenticator;
346 * Check application checksum vs. tgs request
348 * We try checksumming the req-body two different ways: first we
349 * try reaching into the raw asn.1 stream (if available), and
350 * checksum that directly; if that fails, then we try encoding
351 * using our local asn.1 library.
353 if (pkt && (fetch_asn1_field((unsigned char *) pkt->data,
354 1, 4, &scratch1) >= 0)) {
355 if (comp_cksum(kdc_context, &scratch1, *ticket, his_cksum)) {
356 if (!(retval = encode_krb5_kdc_req_body(request, &scratch)))
357 retval = comp_cksum(kdc_context, scratch, *ticket, his_cksum);
358 krb5_free_data(kdc_context, scratch);
362 cleanup_authenticator:
363 krb5_free_authenticator(kdc_context, authenticator);
365 cleanup_auth_context:
366 /* We do not want the free of the auth_context to close the rcache */
367 #ifdef USE_RCACHE
368 (void) krb5_auth_con_setrcache(kdc_context, auth_context, 0);
369 #endif
370 krb5_auth_con_free(kdc_context, auth_context);
372 cleanup:
373 krb5_free_ap_req(kdc_context, apreq);
374 return retval;
377 /* XXX This function should no longer be necessary.
378 * The KDC should take the keytab associated with the realm and pass that to
379 * the krb5_rd_req_decode(). --proven
381 * It's actually still used by do_tgs_req() for u2u auth, and not too
382 * much else. -- tlyu
384 krb5_error_code
385 kdc_get_server_key(krb5_ticket *ticket, krb5_keyblock **key, krb5_kvno *kvno)
387 krb5_error_code retval;
388 krb5_db_entry server;
389 krb5_boolean more;
390 int nprincs;
391 krb5_key_data * server_key;
393 nprincs = 1;
395 if ((retval = krb5_db_get_principal(kdc_context, ticket->server,
396 &server, &nprincs,
397 &more))) {
398 return(retval);
400 if (more) {
401 krb5_db_free_principal(kdc_context, &server, nprincs);
402 return(KRB5KDC_ERR_PRINCIPAL_NOT_UNIQUE);
403 } else if (nprincs != 1) {
404 char *sname;
406 krb5_db_free_principal(kdc_context, &server, nprincs);
407 if (!krb5_unparse_name(kdc_context, ticket->server, &sname)) {
408 limit_string(sname);
409 krb5_klog_syslog(LOG_ERR,"TGS_REQ: UNKNOWN SERVER: server='%s'",
410 sname);
411 free(sname);
413 return(KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN);
415 retval = krb5_dbe_find_enctype(kdc_context, &server,
416 ticket->enc_part.enctype, -1,
417 ticket->enc_part.kvno, &server_key);
418 if (retval)
419 goto errout;
420 if (!server_key) {
421 retval = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
422 goto errout;
424 *kvno = server_key->key_data_kvno;
425 if ((*key = (krb5_keyblock *)malloc(sizeof **key))) {
426 retval = krb5_dbekd_decrypt_key_data(kdc_context, &master_keyblock,
427 server_key,
428 *key, NULL);
429 } else
430 retval = ENOMEM;
431 errout:
432 krb5_db_free_principal(kdc_context, &server, nprincs);
433 return retval;
436 /* This probably wants to be updated if you support last_req stuff */
438 static krb5_last_req_entry nolrentry = { KV5M_LAST_REQ_ENTRY, KRB5_LRQ_NONE, 0 };
439 static krb5_last_req_entry *nolrarray[] = { &nolrentry, 0 };
441 krb5_error_code
442 fetch_last_req_info(krb5_db_entry *dbentry, krb5_last_req_entry ***lrentry)
444 *lrentry = nolrarray;
445 return 0;
449 /* XXX! This is a temporary place-holder */
451 krb5_error_code
452 check_hot_list(krb5_ticket *ticket)
454 return 0;
458 #define MAX_REALM_LN 500
462 * subrealm - determine if r2 is a subrealm of r1
464 * SUBREALM takes two realms, r1 and r2, and
465 * determines if r2 is a subrealm of r1.
466 * r2 is a subrealm of r1 if (r1 is a prefix
467 * of r2 AND r1 and r2 begin with a /) or if
468 * (r1 is a suffix of r2 and neither r1 nor r2
469 * begin with a /).
471 * RETURNS: If r2 is a subrealm, and r1 is a prefix, the number
472 * of characters in the suffix of r2 is returned as a
473 * negative number.
475 * If r2 is a subrealm, and r1 is a suffix, the number
476 * of characters in the prefix of r2 is returned as a
477 * positive number.
479 * If r2 is not a subrealm, SUBREALM returns 0.
481 static int
482 subrealm(char *r1, char *r2)
484 size_t l1,l2;
485 l1 = strlen(r1);
486 l2 = strlen(r2);
487 if(l2 <= l1) return(0);
488 if((*r1 == '/') && (*r2 == '/') && (strncmp(r1,r2,l1) == 0)) return(l1-l2);
489 if((*r1 != '/') && (*r2 != '/') && (strncmp(r1,r2+l2-l1,l1) == 0))
490 return(l2-l1);
491 return(0);
495 * add_to_transited Adds the name of the realm which issued the
496 * ticket granting ticket on which the new ticket to
497 * be issued is based (note that this is the same as
498 * the realm of the server listed in the ticket
499 * granting ticket.
501 * ASSUMPTIONS: This procedure assumes that the transited field from
502 * the existing ticket granting ticket already appears
503 * in compressed form. It will add the new realm while
504 * maintaining that form. As long as each successive
505 * realm is added using this (or a similar) routine, the
506 * transited field will be in compressed form. The
507 * basis step is an empty transited field which is, by
508 * its nature, in its most compressed form.
510 * ARGUMENTS: krb5_data *tgt_trans Transited field from TGT
511 * krb5_data *new_trans The transited field for the new ticket
512 * krb5_principal tgs Name of ticket granting server
513 * This includes the realm of the KDC
514 * that issued the ticket granting
515 * ticket. This is the realm that is
516 * to be added to the transited field.
517 * krb5_principal client Name of the client
518 * krb5_principal server The name of the requested server.
519 * This may be the an intermediate
520 * ticket granting server.
522 * The last two argument are needed since they are
523 * implicitly part of the transited field of the new ticket
524 * even though they are not explicitly listed.
526 * RETURNS: krb5_error_code - Success, or out of memory
528 * MODIFIES: new_trans: ->length will contain the length of the new
529 * transited field.
531 * If ->data was not null when this procedure
532 * is called, the memory referenced by ->data
533 * will be deallocated.
535 * Memory will be allocated for the new transited field
536 * ->data will be updated to point to the newly
537 * allocated memory.
539 * BUGS: The space allocated for the new transited field is the
540 * maximum that might be needed given the old transited field,
541 * and the realm to be added. This length is calculated
542 * assuming that no compression of the new realm is possible.
543 * This has no adverse consequences other than the allocation
544 * of more space than required.
546 * This procedure will not yet use the null subfield notation,
547 * and it will get confused if it sees it.
549 * This procedure does not check for quoted commas in realm
550 * names.
553 static char *
554 data2string (krb5_data *d)
556 char *s;
557 s = malloc(d->length + 1);
558 if (s) {
559 memcpy(s, d->data, d->length);
560 s[d->length] = 0;
562 return s;
565 krb5_error_code
566 add_to_transited(krb5_data *tgt_trans, krb5_data *new_trans,
567 krb5_principal tgs, krb5_principal client,
568 krb5_principal server)
570 krb5_error_code retval;
571 char *realm;
572 char *trans;
573 char *otrans, *otrans_ptr;
575 /* The following are for stepping through the transited field */
577 char prev[MAX_REALM_LN];
578 char next[MAX_REALM_LN];
579 char current[MAX_REALM_LN];
580 char exp[MAX_REALM_LN]; /* Expanded current realm name */
582 int i;
583 int clst, nlst; /* count of last character in current and next */
584 int pl, pl1; /* prefix length */
585 int added; /* TRUE = new realm has been added */
587 realm = data2string(krb5_princ_realm(kdc_context, tgs));
588 if (realm == NULL)
589 return(ENOMEM);
591 otrans = data2string(tgt_trans);
592 if (otrans == NULL) {
593 free(realm);
594 return(ENOMEM);
596 /* Keep track of start so we can free */
597 otrans_ptr = otrans;
599 /* +1 for null,
600 +1 for extra comma which may be added between
601 +1 for potential space when leading slash in realm */
602 if (!(trans = (char *) malloc(strlen(realm) + strlen(otrans) + 3))) {
603 retval = ENOMEM;
604 goto fail;
607 free(new_trans->data);
608 new_trans->data = trans;
609 new_trans->length = 0;
611 trans[0] = '\0';
613 /* For the purpose of appending, the realm preceding the first */
614 /* realm in the transited field is considered the null realm */
616 prev[0] = '\0';
618 /* read field into current */
619 for (i = 0; *otrans != '\0';) {
620 if (*otrans == '\\') {
621 if (*(++otrans) == '\0')
622 break;
623 else
624 continue;
626 if (*otrans == ',') {
627 otrans++;
628 break;
630 current[i++] = *otrans++;
631 if (i >= MAX_REALM_LN) {
632 retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
633 goto fail;
636 current[i] = '\0';
638 added = (krb5_princ_realm(kdc_context, client)->length == strlen(realm) &&
639 !strncmp(krb5_princ_realm(kdc_context, client)->data, realm, strlen(realm))) ||
640 (krb5_princ_realm(kdc_context, server)->length == strlen(realm) &&
641 !strncmp(krb5_princ_realm(kdc_context, server)->data, realm, strlen(realm)));
643 while (current[0]) {
645 /* figure out expanded form of current name */
647 clst = strlen(current) - 1;
648 if (current[0] == ' ') {
649 strncpy(exp, current+1, sizeof(exp) - 1);
650 exp[sizeof(exp) - 1] = '\0';
652 else if ((current[0] == '/') && (prev[0] == '/')) {
653 strncpy(exp, prev, sizeof(exp) - 1);
654 exp[sizeof(exp) - 1] = '\0';
655 if (strlen(exp) + strlen(current) + 1 >= MAX_REALM_LN) {
656 retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
657 goto fail;
659 strncat(exp, current, sizeof(exp) - 1 - strlen(exp));
661 else if (current[clst] == '.') {
662 strncpy(exp, current, sizeof(exp) - 1);
663 exp[sizeof(exp) - 1] = '\0';
664 if (strlen(exp) + strlen(prev) + 1 >= MAX_REALM_LN) {
665 retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
666 goto fail;
668 strncat(exp, prev, sizeof(exp) - 1 - strlen(exp));
670 else {
671 strncpy(exp, current, sizeof(exp) - 1);
672 exp[sizeof(exp) - 1] = '\0';
675 /* read field into next */
676 for (i = 0; *otrans != '\0';) {
677 if (*otrans == '\\') {
678 if (*(++otrans) == '\0')
679 break;
680 else
681 continue;
683 if (*otrans == ',') {
684 otrans++;
685 break;
687 next[i++] = *otrans++;
688 if (i >= MAX_REALM_LN) {
689 retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
690 goto fail;
693 next[i] = '\0';
694 nlst = i - 1;
696 if (!strcmp(exp, realm)) added = TRUE;
698 /* If we still have to insert the new realm */
700 if (!added) {
702 /* Is the next field compressed? If not, and if the new */
703 /* realm is a subrealm of the current realm, compress */
704 /* the new realm, and insert immediately following the */
705 /* current one. Note that we can not do this if the next*/
706 /* field is already compressed since it would mess up */
707 /* what has already been done. In most cases, this is */
708 /* not a problem because the realm to be added will be a */
709 /* subrealm of the next field too, and we will catch */
710 /* it in a future iteration. */
712 /* Note that the second test here is an unsigned comparison,
713 so the first half (or a cast) is also required. */
714 assert(nlst < 0 || nlst < sizeof(next));
715 if ((nlst < 0 || next[nlst] != '.') &&
716 (next[0] != '/') &&
717 (pl = subrealm(exp, realm))) {
718 added = TRUE;
719 current[sizeof(current) - 1] = '\0';
720 if (strlen(current) + (pl>0?pl:-pl) + 2 >= MAX_REALM_LN) {
721 retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
722 goto fail;
724 strncat(current, ",", sizeof(current) - 1 - strlen(current));
725 if (pl > 0) {
726 strncat(current, realm, (unsigned) pl);
728 else {
729 strncat(current, realm+strlen(realm)+pl, (unsigned) (-pl));
733 /* Whether or not the next field is compressed, if the */
734 /* realm to be added is a superrealm of the current realm,*/
735 /* then the current realm can be compressed. First the */
736 /* realm to be added must be compressed relative to the */
737 /* previous realm (if possible), and then the current */
738 /* realm compressed relative to the new realm. Note that */
739 /* if the realm to be added is also a superrealm of the */
740 /* previous realm, it would have been added earlier, and */
741 /* we would not reach this step this time around. */
743 else if ((pl = subrealm(realm, exp))) {
744 added = TRUE;
745 current[0] = '\0';
746 if ((pl1 = subrealm(prev,realm))) {
747 if (strlen(current) + (pl1>0?pl1:-pl1) + 1 >= MAX_REALM_LN) {
748 retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
749 goto fail;
751 if (pl1 > 0) {
752 strncat(current, realm, (unsigned) pl1);
754 else {
755 strncat(current, realm+strlen(realm)+pl1, (unsigned) (-pl1));
758 else { /* If not a subrealm */
759 if ((realm[0] == '/') && prev[0]) {
760 if (strlen(current) + 2 >= MAX_REALM_LN) {
761 retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
762 goto fail;
764 strncat(current, " ", sizeof(current) - 1 - strlen(current));
765 current[sizeof(current) - 1] = '\0';
767 if (strlen(current) + strlen(realm) + 1 >= MAX_REALM_LN) {
768 retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
769 goto fail;
771 strncat(current, realm, sizeof(current) - 1 - strlen(current));
772 current[sizeof(current) - 1] = '\0';
774 if (strlen(current) + (pl>0?pl:-pl) + 2 >= MAX_REALM_LN) {
775 retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
776 goto fail;
778 strncat(current,",", sizeof(current) - 1 - strlen(current));
779 current[sizeof(current) - 1] = '\0';
780 if (pl > 0) {
781 strncat(current, exp, (unsigned) pl);
783 else {
784 strncat(current, exp+strlen(exp)+pl, (unsigned)(-pl));
789 if (new_trans->length != 0) {
790 if (strlen(trans) + 2 >= MAX_REALM_LN) {
791 retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
792 goto fail;
794 strcat(trans, ",");
796 if (strlen(trans) + strlen(current) + 1 >= MAX_REALM_LN) {
797 retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
798 goto fail;
800 strcat(trans, current);
801 new_trans->length = strlen(trans);
803 strncpy(prev, exp, sizeof(prev) - 1);
804 prev[sizeof(prev) - 1] = '\0';
805 strncpy(current, next, sizeof(current) - 1);
806 current[sizeof(current) - 1] = '\0';
809 if (!added) {
810 if (new_trans->length != 0) {
811 if (strlen(trans) + 2 >= MAX_REALM_LN) {
812 retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
813 goto fail;
815 strcat(trans, ",");
817 if((realm[0] == '/') && trans[0]) {
818 if (strlen(trans) + 2 >= MAX_REALM_LN) {
819 retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
820 goto fail;
822 strcat(trans, " ");
824 if (strlen(trans) + strlen(realm) + 1 >= MAX_REALM_LN) {
825 retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
826 goto fail;
828 strcat(trans, realm);
829 new_trans->length = strlen(trans);
832 retval = 0;
833 fail:
834 free(realm);
835 free(otrans_ptr);
836 return (retval);
840 * Routines that validate a AS request; checks a lot of things. :-)
842 * Returns a Kerberos protocol error number, which is _not_ the same
843 * as a com_err error number!
845 #define AS_INVALID_OPTIONS (KDC_OPT_FORWARDED | KDC_OPT_PROXY |\
846 KDC_OPT_VALIDATE | KDC_OPT_RENEW | KDC_OPT_ENC_TKT_IN_SKEY)
848 validate_as_request(register krb5_kdc_req *request, krb5_db_entry client,
849 krb5_db_entry server, krb5_timestamp kdc_time,
850 const char **status)
852 int errcode;
855 * If an option is set that is only allowed in TGS requests, complain.
857 if (request->kdc_options & AS_INVALID_OPTIONS) {
858 *status = "INVALID AS OPTIONS";
859 return KDC_ERR_BADOPTION;
862 /* The client's password must not be expired, unless the server is
863 a KRB5_KDC_PWCHANGE_SERVICE. */
864 if (client.pw_expiration && client.pw_expiration < kdc_time &&
865 !isflagset(server.attributes, KRB5_KDB_PWCHANGE_SERVICE)) {
866 *status = "CLIENT KEY EXPIRED";
867 #ifdef KRBCONF_VAGUE_ERRORS
868 return(KRB_ERR_GENERIC);
869 #else
870 return(KDC_ERR_KEY_EXP);
871 #endif
874 /* The client must not be expired */
875 if (client.expiration && client.expiration < kdc_time) {
876 *status = "CLIENT EXPIRED";
877 #ifdef KRBCONF_VAGUE_ERRORS
878 return(KRB_ERR_GENERIC);
879 #else
880 return(KDC_ERR_NAME_EXP);
881 #endif
884 /* The server must not be expired */
885 if (server.expiration && server.expiration < kdc_time) {
886 *status = "SERVICE EXPIRED";
887 return(KDC_ERR_SERVICE_EXP);
891 * If the client requires password changing, then only allow the
892 * pwchange service.
894 if (isflagset(client.attributes, KRB5_KDB_REQUIRES_PWCHANGE) &&
895 !isflagset(server.attributes, KRB5_KDB_PWCHANGE_SERVICE)) {
896 *status = "REQUIRED PWCHANGE";
897 return(KDC_ERR_KEY_EXP);
900 /* Client and server must allow postdating tickets */
901 if ((isflagset(request->kdc_options, KDC_OPT_ALLOW_POSTDATE) ||
902 isflagset(request->kdc_options, KDC_OPT_POSTDATED)) &&
903 (isflagset(client.attributes, KRB5_KDB_DISALLOW_POSTDATED) ||
904 isflagset(server.attributes, KRB5_KDB_DISALLOW_POSTDATED))) {
905 *status = "POSTDATE NOT ALLOWED";
906 return(KDC_ERR_CANNOT_POSTDATE);
909 /* Client and server must allow forwardable tickets */
910 if (isflagset(request->kdc_options, KDC_OPT_FORWARDABLE) &&
911 (isflagset(client.attributes, KRB5_KDB_DISALLOW_FORWARDABLE) ||
912 isflagset(server.attributes, KRB5_KDB_DISALLOW_FORWARDABLE))) {
913 *status = "FORWARDABLE NOT ALLOWED";
914 return(KDC_ERR_POLICY);
917 /* Client and server must allow renewable tickets */
918 if (isflagset(request->kdc_options, KDC_OPT_RENEWABLE) &&
919 (isflagset(client.attributes, KRB5_KDB_DISALLOW_RENEWABLE) ||
920 isflagset(server.attributes, KRB5_KDB_DISALLOW_RENEWABLE))) {
921 *status = "RENEWABLE NOT ALLOWED";
922 return(KDC_ERR_POLICY);
925 /* Client and server must allow proxiable tickets */
926 if (isflagset(request->kdc_options, KDC_OPT_PROXIABLE) &&
927 (isflagset(client.attributes, KRB5_KDB_DISALLOW_PROXIABLE) ||
928 isflagset(server.attributes, KRB5_KDB_DISALLOW_PROXIABLE))) {
929 *status = "PROXIABLE NOT ALLOWED";
930 return(KDC_ERR_POLICY);
933 /* Check to see if client is locked out */
934 if (isflagset(client.attributes, KRB5_KDB_DISALLOW_ALL_TIX)) {
935 *status = "CLIENT LOCKED OUT";
936 return(KDC_ERR_C_PRINCIPAL_UNKNOWN);
939 /* Check to see if server is locked out */
940 if (isflagset(server.attributes, KRB5_KDB_DISALLOW_ALL_TIX)) {
941 *status = "SERVICE LOCKED OUT";
942 return(KDC_ERR_S_PRINCIPAL_UNKNOWN);
945 /* Check to see if server is allowed to be a service */
946 if (isflagset(server.attributes, KRB5_KDB_DISALLOW_SVR)) {
947 *status = "SERVICE NOT ALLOWED";
948 return(KDC_ERR_S_PRINCIPAL_UNKNOWN);
952 * Check against local policy
954 errcode = against_local_policy_as(request, server, client,
955 kdc_time, status);
956 if (errcode)
957 return errcode;
959 return 0;
962 #define ASN1_ID_CLASS (0xc0)
963 #define ASN1_ID_TYPE (0x20)
964 #define ASN1_ID_TAG (0x1f)
965 #define ASN1_CLASS_UNIV (0)
966 #define ASN1_CLASS_APP (1)
967 #define ASN1_CLASS_CTX (2)
968 #define ASN1_CLASS_PRIV (3)
969 #define asn1_id_constructed(x) (x & ASN1_ID_TYPE)
970 #define asn1_id_primitive(x) (!asn1_id_constructed(x))
971 #define asn1_id_class(x) ((x & ASN1_ID_CLASS) >> 6)
972 #define asn1_id_tag(x) (x & ASN1_ID_TAG)
975 * asn1length - return encoded length of value.
977 * passed a pointer into the asn.1 stream, which is updated
978 * to point right after the length bits.
980 * returns -1 on failure.
982 static int
983 asn1length(unsigned char **astream)
985 int length; /* resulting length */
986 int sublen; /* sublengths */
987 int blen; /* bytes of length */
988 unsigned char *p; /* substring searching */
990 if (**astream & 0x80) {
991 blen = **astream & 0x7f;
992 if (blen > 3) {
993 return(-1);
995 for (++*astream, length = 0; blen; ++*astream, blen--) {
996 length = (length << 8) | **astream;
998 if (length == 0) {
999 /* indefinite length, figure out by hand */
1000 p = *astream;
1001 p++;
1002 while (1) {
1003 /* compute value length. */
1004 if ((sublen = asn1length(&p)) < 0) {
1005 return(-1);
1007 p += sublen;
1008 /* check for termination */
1009 if ((!*p++) && (!*p)) {
1010 p++;
1011 break;
1014 length = p - *astream;
1016 } else {
1017 length = **astream;
1018 ++*astream;
1020 return(length);
1024 * fetch_asn1_field - return raw asn.1 stream of subfield.
1026 * this routine is passed a context-dependent tag number and "level" and returns
1027 * the size and length of the corresponding level subfield.
1029 * levels and are numbered starting from 1.
1031 * returns 0 on success, -1 otherwise.
1034 fetch_asn1_field(unsigned char *astream, unsigned int level,
1035 unsigned int field, krb5_data *data)
1037 unsigned char *estream; /* end of stream */
1038 int classes; /* # classes seen so far this level */
1039 unsigned int levels = 0; /* levels seen so far */
1040 int lastlevel = 1000; /* last level seen */
1041 int length; /* various lengths */
1042 int tag; /* tag number */
1043 unsigned char savelen; /* saved length of our field */
1045 classes = -1;
1046 /* we assume that the first identifier/length will tell us
1047 how long the entire stream is. */
1048 astream++;
1049 estream = astream;
1050 if ((length = asn1length(&astream)) < 0) {
1051 return(-1);
1053 estream += length;
1054 /* search down the stream, checking identifiers. we process identifiers
1055 until we hit the "level" we want, and then process that level for our
1056 subfield, always making sure we don't go off the end of the stream. */
1057 while (astream < estream) {
1058 if (!asn1_id_constructed(*astream)) {
1059 return(-1);
1061 if (asn1_id_class(*astream) == ASN1_CLASS_CTX) {
1062 if ((tag = (int)asn1_id_tag(*astream)) <= lastlevel) {
1063 levels++;
1064 classes = -1;
1066 lastlevel = tag;
1067 if (levels == level) {
1068 /* in our context-dependent class, is this the one we're looking for ? */
1069 if (tag == field) {
1070 /* return length and data */
1071 astream++;
1072 savelen = *astream;
1073 if ((data->length = asn1length(&astream)) < 0) {
1074 return(-1);
1076 /* if the field length is indefinite, we will have to subtract two
1077 (terminating octets) from the length returned since we don't want
1078 to pass any info from the "wrapper" back. asn1length will always return
1079 the *total* length of the field, not just what's contained in it */
1080 if ((savelen & 0xff) == 0x80) {
1081 data->length -=2 ;
1083 data->data = (char *)astream;
1084 return(0);
1085 } else if (tag <= classes) {
1086 /* we've seen this class before, something must be wrong */
1087 return(-1);
1088 } else {
1089 classes = tag;
1093 /* if we're not on our level yet, process this value. otherwise skip over it */
1094 astream++;
1095 if ((length = asn1length(&astream)) < 0) {
1096 return(-1);
1098 if (levels == level) {
1099 astream += length;
1102 return(-1);
1106 * Routines that validate a TGS request; checks a lot of things. :-)
1108 * Returns a Kerberos protocol error number, which is _not_ the same
1109 * as a com_err error number!
1111 #define TGS_OPTIONS_HANDLED (KDC_OPT_FORWARDABLE | KDC_OPT_FORWARDED | \
1112 KDC_OPT_PROXIABLE | KDC_OPT_PROXY | \
1113 KDC_OPT_ALLOW_POSTDATE | KDC_OPT_POSTDATED | \
1114 KDC_OPT_RENEWABLE | KDC_OPT_RENEWABLE_OK | \
1115 KDC_OPT_ENC_TKT_IN_SKEY | KDC_OPT_RENEW | \
1116 KDC_OPT_VALIDATE)
1118 #define NO_TGT_OPTION (KDC_OPT_FORWARDED | KDC_OPT_PROXY | KDC_OPT_RENEW | \
1119 KDC_OPT_VALIDATE)
1122 validate_tgs_request(register krb5_kdc_req *request, krb5_db_entry server,
1123 krb5_ticket *ticket, krb5_timestamp kdc_time,
1124 const char **status)
1126 int errcode;
1127 int st_idx = 0;
1130 * If an illegal option is set, ignore it.
1132 request->kdc_options &= TGS_OPTIONS_HANDLED;
1134 /* Check to see if server has expired */
1135 if (server.expiration && server.expiration < kdc_time) {
1136 *status = "SERVICE EXPIRED";
1137 return(KDC_ERR_SERVICE_EXP);
1141 * Verify that the server principal in authdat->ticket is correct
1142 * (either the ticket granting service or the service that was
1143 * originally requested)
1145 if (request->kdc_options & NO_TGT_OPTION) {
1146 if (!krb5_principal_compare(kdc_context, ticket->server, request->server)) {
1147 *status = "SERVER DIDN'T MATCH TICKET FOR RENEW/FORWARD/ETC";
1148 return(KDC_ERR_SERVER_NOMATCH);
1150 } else {
1152 * OK, we need to validate the krbtgt service in the ticket.
1154 * The krbtgt service is of the form:
1155 * krbtgt/realm-A@realm-B
1157 * Realm A is the "server realm"; the realm of the
1158 * server of the requested ticket must match this realm.
1159 * Of course, it should be a realm serviced by this KDC.
1161 * Realm B is the "client realm"; this is what should be
1162 * added to the transited field. (which is done elsewhere)
1165 /* Make sure there are two components... */
1166 if (krb5_princ_size(kdc_context, ticket->server) != 2) {
1167 *status = "BAD TGS SERVER LENGTH";
1168 return KRB_AP_ERR_NOT_US;
1170 /* ...that the first component is krbtgt... */
1171 if (!krb5_is_tgs_principal(ticket->server)) {
1172 *status = "BAD TGS SERVER NAME";
1173 return KRB_AP_ERR_NOT_US;
1175 /* ...and that the second component matches the server realm... */
1176 if ((krb5_princ_size(kdc_context, ticket->server) <= 1) ||
1177 (krb5_princ_component(kdc_context, ticket->server, 1)->length !=
1178 krb5_princ_realm(kdc_context, request->server)->length) ||
1179 memcmp(krb5_princ_component(kdc_context, ticket->server, 1)->data,
1180 krb5_princ_realm(kdc_context, request->server)->data,
1181 krb5_princ_realm(kdc_context, request->server)->length)) {
1182 *status = "BAD TGS SERVER INSTANCE";
1183 return KRB_AP_ERR_NOT_US;
1185 /* XXX add check that second component must match locally
1186 * supported realm?
1189 /* Server must allow TGS based issuances */
1190 if (isflagset(server.attributes, KRB5_KDB_DISALLOW_TGT_BASED)) {
1191 *status = "TGT BASED NOT ALLOWED";
1192 return(KDC_ERR_POLICY);
1196 /* TGS must be forwardable to get forwarded or forwardable ticket */
1197 if ((isflagset(request->kdc_options, KDC_OPT_FORWARDED) ||
1198 isflagset(request->kdc_options, KDC_OPT_FORWARDABLE)) &&
1199 !isflagset(ticket->enc_part2->flags, TKT_FLG_FORWARDABLE)) {
1200 *status = "TGT NOT FORWARDABLE";
1202 return KDC_ERR_BADOPTION;
1205 /* TGS must be proxiable to get proxiable ticket */
1206 if ((isflagset(request->kdc_options, KDC_OPT_PROXY) ||
1207 isflagset(request->kdc_options, KDC_OPT_PROXIABLE)) &&
1208 !isflagset(ticket->enc_part2->flags, TKT_FLG_PROXIABLE)) {
1209 *status = "TGT NOT PROXIABLE";
1210 return KDC_ERR_BADOPTION;
1213 /* TGS must allow postdating to get postdated ticket */
1214 if ((isflagset(request->kdc_options, KDC_OPT_ALLOW_POSTDATE) ||
1215 isflagset(request->kdc_options, KDC_OPT_POSTDATED)) &&
1216 !isflagset(ticket->enc_part2->flags, TKT_FLG_MAY_POSTDATE)) {
1217 *status = "TGT NOT POSTDATABLE";
1218 return KDC_ERR_BADOPTION;
1221 /* can only validate invalid tix */
1222 if (isflagset(request->kdc_options, KDC_OPT_VALIDATE) &&
1223 !isflagset(ticket->enc_part2->flags, TKT_FLG_INVALID)) {
1224 *status = "VALIDATE VALID TICKET";
1225 return KDC_ERR_BADOPTION;
1228 /* can only renew renewable tix */
1229 if ((isflagset(request->kdc_options, KDC_OPT_RENEW) ||
1230 isflagset(request->kdc_options, KDC_OPT_RENEWABLE)) &&
1231 !isflagset(ticket->enc_part2->flags, TKT_FLG_RENEWABLE)) {
1232 *status = "TICKET NOT RENEWABLE";
1233 return KDC_ERR_BADOPTION;
1236 /* can not proxy ticket granting tickets */
1237 if (isflagset(request->kdc_options, KDC_OPT_PROXY) &&
1238 (!request->server->data ||
1239 request->server->data[0].length != KRB5_TGS_NAME_SIZE ||
1240 memcmp(request->server->data[0].data, KRB5_TGS_NAME,
1241 KRB5_TGS_NAME_SIZE))) {
1242 *status = "CAN'T PROXY TGT";
1243 return KDC_ERR_BADOPTION;
1246 /* Server must allow forwardable tickets */
1247 if (isflagset(request->kdc_options, KDC_OPT_FORWARDABLE) &&
1248 isflagset(server.attributes, KRB5_KDB_DISALLOW_FORWARDABLE)) {
1249 *status = "NON-FORWARDABLE TICKET";
1250 return(KDC_ERR_POLICY);
1253 /* Server must allow renewable tickets */
1254 if (isflagset(request->kdc_options, KDC_OPT_RENEWABLE) &&
1255 isflagset(server.attributes, KRB5_KDB_DISALLOW_RENEWABLE)) {
1256 *status = "NON-RENEWABLE TICKET";
1257 return(KDC_ERR_POLICY);
1260 /* Server must allow proxiable tickets */
1261 if (isflagset(request->kdc_options, KDC_OPT_PROXIABLE) &&
1262 isflagset(server.attributes, KRB5_KDB_DISALLOW_PROXIABLE)) {
1263 *status = "NON-PROXIABLE TICKET";
1264 return(KDC_ERR_POLICY);
1267 /* Server must allow postdated tickets */
1268 if (isflagset(request->kdc_options, KDC_OPT_ALLOW_POSTDATE) &&
1269 isflagset(server.attributes, KRB5_KDB_DISALLOW_POSTDATED)) {
1270 *status = "NON-POSTDATABLE TICKET";
1271 return(KDC_ERR_CANNOT_POSTDATE);
1274 /* Server must allow DUP SKEY requests */
1275 if (isflagset(request->kdc_options, KDC_OPT_ENC_TKT_IN_SKEY) &&
1276 isflagset(server.attributes, KRB5_KDB_DISALLOW_DUP_SKEY)) {
1277 *status = "DUP_SKEY DISALLOWED";
1278 return(KDC_ERR_POLICY);
1281 /* Server must not be locked out */
1282 if (isflagset(server.attributes, KRB5_KDB_DISALLOW_ALL_TIX)) {
1283 *status = "SERVER LOCKED OUT";
1284 return(KDC_ERR_S_PRINCIPAL_UNKNOWN);
1287 /* Server must be allowed to be a service */
1288 if (isflagset(server.attributes, KRB5_KDB_DISALLOW_SVR)) {
1289 *status = "SERVER NOT ALLOWED";
1290 return(KDC_ERR_S_PRINCIPAL_UNKNOWN);
1293 /* Check the hot list */
1294 if (check_hot_list(ticket)) {
1295 *status = "HOT_LIST";
1296 return(KRB_AP_ERR_REPEAT);
1299 /* Check the start time vs. the KDC time */
1300 if (isflagset(request->kdc_options, KDC_OPT_VALIDATE)) {
1301 if (ticket->enc_part2->times.starttime > kdc_time) {
1302 *status = "NOT_YET_VALID";
1303 return(KRB_AP_ERR_TKT_NYV);
1308 * Check the renew_till time. The endtime was already
1309 * been checked in the initial authentication check.
1311 if (isflagset(request->kdc_options, KDC_OPT_RENEW) &&
1312 (ticket->enc_part2->times.renew_till < kdc_time)) {
1313 *status = "TKT_EXPIRED";
1314 return(KRB_AP_ERR_TKT_EXPIRED);
1318 * Checks for ENC_TKT_IN_SKEY:
1320 * (1) Make sure the second ticket exists
1321 * (2) Make sure it is a ticket granting ticket
1323 if (isflagset(request->kdc_options, KDC_OPT_ENC_TKT_IN_SKEY)) {
1324 if (!request->second_ticket ||
1325 !request->second_ticket[st_idx]) {
1326 *status = "NO_2ND_TKT";
1327 return(KDC_ERR_BADOPTION);
1329 if (!krb5_principal_compare(kdc_context, request->second_ticket[st_idx]->server,
1330 tgs_server)) {
1331 *status = "2ND_TKT_NOT_TGS";
1332 return(KDC_ERR_POLICY);
1334 st_idx++;
1337 /* Check for hardware preauthentication */
1338 if (isflagset(server.attributes, KRB5_KDB_REQUIRES_HW_AUTH) &&
1339 !isflagset(ticket->enc_part2->flags,TKT_FLG_HW_AUTH)) {
1340 *status = "NO HW PREAUTH";
1341 return KRB_ERR_GENERIC;
1344 /* Check for any kind of preauthentication */
1345 if (isflagset(server.attributes, KRB5_KDB_REQUIRES_PRE_AUTH) &&
1346 !isflagset(ticket->enc_part2->flags, TKT_FLG_PRE_AUTH)) {
1347 *status = "NO PREAUTH";
1348 return KRB_ERR_GENERIC;
1352 * Check local policy
1354 errcode = against_local_policy_tgs(request, server, ticket, status);
1355 if (errcode)
1356 return errcode;
1359 return 0;
1363 * This function returns 1 if the dbentry has a key for a specified
1364 * keytype, and 0 if not.
1367 dbentry_has_key_for_enctype(krb5_context context, krb5_db_entry *client,
1368 krb5_enctype enctype)
1370 krb5_error_code retval;
1371 krb5_key_data *datap;
1373 retval = krb5_dbe_find_enctype(context, client, enctype,
1374 -1, 0, &datap);
1375 if (retval)
1376 return 0;
1377 else
1378 return 1;
1382 * This function returns 1 if the entity referenced by this
1383 * structure can support the a particular encryption system, and 0 if
1384 * not.
1386 * XXX eventually this information should be looked up in the
1387 * database. Since it isn't, we use some hueristics and attribute
1388 * options bits for now.
1391 dbentry_supports_enctype(krb5_context context, krb5_db_entry *client,
1392 krb5_enctype enctype)
1395 * If it's DES_CBC_MD5, there's a bit in the attribute mask which
1396 * checks to see if we support it.
1398 * In theory everything's supposed to support DES_CBC_MD5, but
1399 * that's not the reality....
1403 * We are assuming that all entries can support MD5; this information
1404 * need not be kept in the database.
1408 if (enctype == ENCTYPE_DES_CBC_MD5)
1409 return 1;
1412 * XXX we assume everything can understand DES_CBC_CRC
1414 if (enctype == ENCTYPE_DES_CBC_CRC)
1415 return 1;
1418 * If we have a key for the encryption system, we assume it's
1419 * supported.
1421 return dbentry_has_key_for_enctype(context, client, enctype);
1425 * This function returns the keytype which should be selected for the
1426 * session key. It is based on the ordered list which the user
1427 * requested, and what the KDC and the application server can support.
1429 krb5_enctype
1430 select_session_keytype(krb5_context context, krb5_db_entry *server,
1431 int nktypes, krb5_enctype *ktype)
1433 int i;
1435 for (i = 0; i < nktypes; i++) {
1436 if (!krb5_c_valid_enctype(ktype[i]))
1437 continue;
1439 if (!krb5_is_permitted_enctype(context, ktype[i]))
1440 continue;
1442 if (dbentry_supports_enctype(context, server, ktype[i]))
1443 return ktype[i];
1445 return 0;
1449 * This function returns salt information for a particular client_key
1451 krb5_error_code
1452 get_salt_from_key(krb5_context context, krb5_principal client,
1453 krb5_key_data *client_key, krb5_data *salt)
1455 krb5_error_code retval;
1456 krb5_data * realm;
1458 salt->data = 0;
1459 salt->length = SALT_TYPE_NO_LENGTH;
1461 if (client_key->key_data_ver == 1)
1462 return 0;
1464 switch (client_key->key_data_type[1]) {
1465 case KRB5_KDB_SALTTYPE_NORMAL:
1466 break;
1467 case KRB5_KDB_SALTTYPE_V4:
1468 /* send an empty (V4) salt */
1469 salt->data = 0;
1470 salt->length = 0;
1471 break;
1472 case KRB5_KDB_SALTTYPE_NOREALM:
1473 if ((retval = krb5_principal2salt_norealm(context, client, salt)))
1474 return retval;
1475 break;
1476 case KRB5_KDB_SALTTYPE_AFS3:
1477 /* send the same salt as with onlyrealm - but with no type info,
1478 we just hope they figure it out on the other end. */
1479 /* fall through to onlyrealm: */
1480 case KRB5_KDB_SALTTYPE_ONLYREALM:
1481 realm = krb5_princ_realm(context, client);
1482 salt->length = realm->length;
1483 if ((salt->data = malloc(realm->length)) == NULL)
1484 return ENOMEM;
1485 memcpy(salt->data, realm->data, realm->length);
1486 break;
1487 case KRB5_KDB_SALTTYPE_SPECIAL:
1488 salt->length = client_key->key_data_length[1];
1489 if ((salt->data = malloc(salt->length)) == NULL)
1490 return ENOMEM;
1491 memcpy(salt->data, client_key->key_data_contents[1], salt->length);
1492 break;
1494 return 0;
1498 * Limit strings to a "reasonable" length to prevent crowding out of
1499 * other useful information in the log entry
1501 #define NAME_LENGTH_LIMIT 128
1503 void limit_string(char *name)
1505 int i;
1507 if (!name)
1508 return;
1510 if (strlen(name) < NAME_LENGTH_LIMIT)
1511 return;
1513 i = NAME_LENGTH_LIMIT-4;
1514 name[i++] = '.';
1515 name[i++] = '.';
1516 name[i++] = '.';
1517 name[i] = '\0';
1518 return;
1522 * L10_2 = log10(2**x), rounded up; log10(2) ~= 0.301.
1524 #define L10_2(x) ((int)(((x * 301) + 999) / 1000))
1527 * Max length of sprintf("%ld") for an int of type T; includes leading
1528 * minus sign and terminating NUL.
1530 #define D_LEN(t) (L10_2(sizeof(t) * CHAR_BIT) + 2)
1532 void
1533 ktypes2str(char *s, size_t len, int nktypes, krb5_enctype *ktype)
1535 int i;
1536 char stmp[D_LEN(krb5_enctype) + 1];
1537 char *p;
1539 if (nktypes < 0
1540 || len < (sizeof(" etypes {...}") + D_LEN(int))) {
1541 *s = '\0';
1542 return;
1545 sprintf(s, "%d etypes {", nktypes);
1546 for (i = 0; i < nktypes; i++) {
1547 sprintf(stmp, "%s%ld", i ? " " : "", (long)ktype[i]);
1548 if (strlen(s) + strlen(stmp) + sizeof("}") > len)
1549 break;
1550 strcat(s, stmp);
1552 if (i < nktypes) {
1554 * We broke out of the loop. Try to truncate the list.
1556 p = s + strlen(s);
1557 while (p - s + sizeof("...}") > len) {
1558 while (p > s && *p != ' ' && *p != '{')
1559 *p-- = '\0';
1560 if (p > s && *p == ' ') {
1561 *p-- = '\0';
1562 continue;
1565 strcat(s, "...");
1567 strcat(s, "}");
1568 return;
1571 void
1572 rep_etypes2str(char *s, size_t len, krb5_kdc_rep *rep)
1574 char stmp[sizeof("ses=") + D_LEN(krb5_enctype)];
1576 if (len < (3 * D_LEN(krb5_enctype)
1577 + sizeof("etypes {rep= tkt= ses=}"))) {
1578 *s = '\0';
1579 return;
1582 sprintf(s, "etypes {rep=%ld", (long)rep->enc_part.enctype);
1584 if (rep->ticket != NULL) {
1585 sprintf(stmp, " tkt=%ld", (long)rep->ticket->enc_part.enctype);
1586 strcat(s, stmp);
1589 if (rep->ticket != NULL
1590 && rep->ticket->enc_part2 != NULL
1591 && rep->ticket->enc_part2->session != NULL) {
1592 sprintf(stmp, " ses=%ld",
1593 (long)rep->ticket->enc_part2->session->enctype);
1594 strcat(s, stmp);
1596 strcat(s, "}");
1597 return;