8322 nl: misleading-indentation
[unleashed/tickless.git] / usr / src / cmd / krb5 / krb5kdc / kdc_util.c
bloba8488e2446e271bc273da8fd2a6c97d614918512
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 if (sname)
78 free(sname);
81 if (retval)
82 krb5_rc_close(kcontext, kdc_rcache);
84 return(retval);
86 #endif
89 * concatenate first two authdata arrays, returning an allocated replacement.
90 * The replacement should be freed with krb5_free_authdata().
92 krb5_error_code
93 concat_authorization_data(krb5_authdata **first, krb5_authdata **second,
94 krb5_authdata ***output)
96 register int i, j;
97 register krb5_authdata **ptr, **retdata;
99 /* count up the entries */
100 i = 0;
101 if (first)
102 for (ptr = first; *ptr; ptr++)
103 i++;
104 if (second)
105 for (ptr = second; *ptr; ptr++)
106 i++;
108 retdata = (krb5_authdata **)malloc((i+1)*sizeof(*retdata));
109 if (!retdata)
110 return ENOMEM;
111 retdata[i] = 0; /* null-terminated array */
112 for (i = 0, j = 0, ptr = first; j < 2 ; ptr = second, j++)
113 while (ptr && *ptr) {
114 /* now walk & copy */
115 retdata[i] = (krb5_authdata *)malloc(sizeof(*retdata[i]));
116 if (!retdata[i]) {
117 krb5_free_authdata(kdc_context, retdata);
118 return ENOMEM;
120 *retdata[i] = **ptr;
121 if (!(retdata[i]->contents =
122 (krb5_octet *)malloc(retdata[i]->length))) {
123 free((char *)retdata[i]);
124 retdata[i] = 0;
125 krb5_free_authdata(kdc_context, retdata);
126 return ENOMEM;
128 memcpy((char *) retdata[i]->contents,
129 (char *)(*ptr)->contents,
130 retdata[i]->length);
132 ptr++;
133 i++;
135 *output = retdata;
136 return 0;
139 krb5_boolean
140 realm_compare(krb5_principal princ1, krb5_principal princ2)
142 krb5_data *realm1 = krb5_princ_realm(kdc_context, princ1);
143 krb5_data *realm2 = krb5_princ_realm(kdc_context, princ2);
145 return((realm1->length == realm2->length) &&
146 !memcmp(realm1->data, realm2->data, realm1->length));
150 * Returns TRUE if the kerberos principal is the name of a Kerberos ticket
151 * service.
153 krb5_boolean krb5_is_tgs_principal(krb5_principal principal)
155 if ((krb5_princ_size(kdc_context, principal) > 0) &&
156 (krb5_princ_component(kdc_context, principal, 0)->length ==
157 KRB5_TGS_NAME_SIZE) &&
158 (!memcmp(krb5_princ_component(kdc_context, principal, 0)->data,
159 KRB5_TGS_NAME, KRB5_TGS_NAME_SIZE)))
160 return TRUE;
161 return FALSE;
165 * given authentication data (provides seed for checksum), verify checksum
166 * for source data.
168 static krb5_error_code
169 comp_cksum(krb5_context kcontext, krb5_data *source, krb5_ticket *ticket,
170 krb5_checksum *his_cksum)
172 krb5_error_code retval;
173 krb5_boolean valid;
175 if (!krb5_c_valid_cksumtype(his_cksum->checksum_type))
176 return KRB5KDC_ERR_SUMTYPE_NOSUPP;
178 /* must be collision proof */
179 if (!krb5_c_is_coll_proof_cksum(his_cksum->checksum_type))
180 return KRB5KRB_AP_ERR_INAPP_CKSUM;
182 /* verify checksum */
183 if ((retval = krb5_c_verify_checksum(kcontext, ticket->enc_part2->session,
184 KRB5_KEYUSAGE_TGS_REQ_AUTH_CKSUM,
185 source, his_cksum, &valid)))
186 return(retval);
188 if (!valid)
189 return(KRB5KRB_AP_ERR_BAD_INTEGRITY);
191 return(0);
194 krb5_error_code
195 kdc_process_tgs_req(krb5_kdc_req *request, const krb5_fulladdr *from,
196 krb5_data *pkt, krb5_ticket **ticket,
197 krb5_keyblock **subkey)
199 krb5_pa_data ** tmppa;
200 krb5_ap_req * apreq;
201 krb5_error_code retval;
202 krb5_data scratch1;
203 krb5_data * scratch = NULL;
204 krb5_boolean foreign_server = FALSE;
205 krb5_auth_context auth_context = NULL;
206 krb5_authenticator * authenticator = NULL;
207 krb5_checksum * his_cksum = NULL;
208 /* krb5_keyblock * key = NULL;*/
209 /* krb5_kvno kvno = 0;*/
211 if (!request->padata)
212 return KRB5KDC_ERR_PADATA_TYPE_NOSUPP;
213 for (tmppa = request->padata; *tmppa; tmppa++) {
214 if ((*tmppa)->pa_type == KRB5_PADATA_AP_REQ)
215 break;
217 if (!*tmppa) /* cannot find any AP_REQ */
218 return KRB5KDC_ERR_PADATA_TYPE_NOSUPP;
220 scratch1.length = (*tmppa)->length;
221 scratch1.data = (char *)(*tmppa)->contents;
222 if ((retval = decode_krb5_ap_req(&scratch1, &apreq)))
223 return retval;
225 if (isflagset(apreq->ap_options, AP_OPTS_USE_SESSION_KEY) ||
226 isflagset(apreq->ap_options, AP_OPTS_MUTUAL_REQUIRED)) {
227 krb5_klog_syslog(LOG_INFO, "TGS_REQ: SESSION KEY or MUTUAL");
228 retval = KRB5KDC_ERR_POLICY;
229 goto cleanup;
232 /* If the "server" principal in the ticket is not something
233 in the local realm, then we must refuse to service the request
234 if the client claims to be from the local realm.
236 If we don't do this, then some other realm's nasty KDC can
237 claim to be authenticating a client from our realm, and we'll
238 give out tickets concurring with it!
240 we set a flag here for checking below.
242 if ((krb5_princ_realm(kdc_context, apreq->ticket->server)->length !=
243 krb5_princ_realm(kdc_context, tgs_server)->length) ||
244 memcmp(krb5_princ_realm(kdc_context, apreq->ticket->server)->data,
245 krb5_princ_realm(kdc_context, tgs_server)->data,
246 krb5_princ_realm(kdc_context, tgs_server)->length))
247 foreign_server = TRUE;
249 if ((retval = krb5_auth_con_init(kdc_context, &auth_context)))
250 goto cleanup;
252 if ((retval = krb5_auth_con_setaddrs(kdc_context, auth_context, NULL,
253 from->address)) )
254 goto cleanup_auth_context;
255 #ifdef USE_RCACHE
256 if ((retval = krb5_auth_con_setrcache(kdc_context, auth_context,
257 kdc_rcache)))
258 goto cleanup_auth_context;
259 #endif
262 if ((retval = kdc_get_server_key(apreq->ticket, &key, &kvno)))
263 goto cleanup_auth_context;
267 * XXX This is currently wrong but to fix it will require making a
268 * new keytab for groveling over the kdb.
271 retval = krb5_auth_con_setuseruserkey(kdc_context, auth_context, key);
272 krb5_free_keyblock(kdc_context, key);
273 if (retval)
274 goto cleanup_auth_context;
277 if ((retval = krb5_rd_req_decoded_anyflag(kdc_context, &auth_context, apreq,
278 apreq->ticket->server,
279 kdc_active_realm->realm_keytab,
280 NULL, ticket))) {
281 #ifdef USE_RCACHE
283 * I'm not so sure that this is right, but it's better than nothing
284 * at all.
286 * If we choke in the rd_req because of the replay cache, then attempt
287 * to reinitialize the replay cache because somebody could have deleted
288 * it from underneath us (e.g. a cron job)
290 if ((retval == KRB5_RC_IO_IO) ||
291 (retval == KRB5_RC_IO_UNKNOWN)) {
292 (void) krb5_rc_close(kdc_context, kdc_rcache);
293 kdc_rcache = (krb5_rcache) NULL;
294 if (!(retval = kdc_initialize_rcache(kdc_context, (char *) NULL))) {
295 if ((retval = krb5_auth_con_setrcache(kdc_context, auth_context,
296 kdc_rcache)) ||
297 (retval = krb5_rd_req_decoded_anyflag(kdc_context, &auth_context,
298 apreq, apreq->ticket->server,
299 kdc_active_realm->realm_keytab,
300 NULL, ticket))
302 goto cleanup_auth_context;
304 } else
305 goto cleanup_auth_context;
306 #else
307 goto cleanup_auth_context;
308 #endif
311 /* "invalid flag" tickets can must be used to validate */
312 if (isflagset((*ticket)->enc_part2->flags, TKT_FLG_INVALID)
313 && !isflagset(request->kdc_options, KDC_OPT_VALIDATE)) {
314 retval = KRB5KRB_AP_ERR_TKT_INVALID;
315 goto cleanup_auth_context;
318 if ((retval = krb5_auth_con_getrecvsubkey(kdc_context,
319 auth_context, subkey)))
320 goto cleanup_auth_context;
322 if ((retval = krb5_auth_con_getauthenticator(kdc_context, auth_context,
323 &authenticator)))
324 goto cleanup_auth_context;
326 /* Check for a checksum */
327 if (!(his_cksum = authenticator->checksum)) {
328 retval = KRB5KRB_AP_ERR_INAPP_CKSUM;
329 goto cleanup_authenticator;
332 /* make sure the client is of proper lineage (see above) */
333 if (foreign_server) {
334 krb5_data *tkt_realm = krb5_princ_realm(kdc_context,
335 (*ticket)->enc_part2->client);
336 krb5_data *tgs_realm = krb5_princ_realm(kdc_context, tgs_server);
337 if (tkt_realm->length == tgs_realm->length &&
338 !memcmp(tkt_realm->data, tgs_realm->data, tgs_realm->length)) {
339 /* someone in a foreign realm claiming to be local */
340 krb5_klog_syslog(LOG_INFO, "PROCESS_TGS: failed lineage check");
341 retval = KRB5KDC_ERR_POLICY;
342 goto cleanup_authenticator;
347 * Check application checksum vs. tgs request
349 * We try checksumming the req-body two different ways: first we
350 * try reaching into the raw asn.1 stream (if available), and
351 * checksum that directly; if that fails, then we try encoding
352 * using our local asn.1 library.
354 if (pkt && (fetch_asn1_field((unsigned char *) pkt->data,
355 1, 4, &scratch1) >= 0)) {
356 if (comp_cksum(kdc_context, &scratch1, *ticket, his_cksum)) {
357 if (!(retval = encode_krb5_kdc_req_body(request, &scratch)))
358 retval = comp_cksum(kdc_context, scratch, *ticket, his_cksum);
359 krb5_free_data(kdc_context, scratch);
363 cleanup_authenticator:
364 krb5_free_authenticator(kdc_context, authenticator);
366 cleanup_auth_context:
367 /* We do not want the free of the auth_context to close the rcache */
368 #ifdef USE_RCACHE
369 (void) krb5_auth_con_setrcache(kdc_context, auth_context, 0);
370 #endif
371 krb5_auth_con_free(kdc_context, auth_context);
373 cleanup:
374 krb5_free_ap_req(kdc_context, apreq);
375 return retval;
378 /* XXX This function should no longer be necessary.
379 * The KDC should take the keytab associated with the realm and pass that to
380 * the krb5_rd_req_decode(). --proven
382 * It's actually still used by do_tgs_req() for u2u auth, and not too
383 * much else. -- tlyu
385 krb5_error_code
386 kdc_get_server_key(krb5_ticket *ticket, krb5_keyblock **key, krb5_kvno *kvno)
388 krb5_error_code retval;
389 krb5_db_entry server;
390 krb5_boolean more;
391 int nprincs;
392 krb5_key_data * server_key;
394 nprincs = 1;
396 if ((retval = krb5_db_get_principal(kdc_context, ticket->server,
397 &server, &nprincs,
398 &more))) {
399 return(retval);
401 if (more) {
402 krb5_db_free_principal(kdc_context, &server, nprincs);
403 return(KRB5KDC_ERR_PRINCIPAL_NOT_UNIQUE);
404 } else if (nprincs != 1) {
405 char *sname;
407 krb5_db_free_principal(kdc_context, &server, nprincs);
408 if (!krb5_unparse_name(kdc_context, ticket->server, &sname)) {
409 limit_string(sname);
410 krb5_klog_syslog(LOG_ERR,"TGS_REQ: UNKNOWN SERVER: server='%s'",
411 sname);
412 free(sname);
414 return(KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN);
416 retval = krb5_dbe_find_enctype(kdc_context, &server,
417 ticket->enc_part.enctype, -1,
418 ticket->enc_part.kvno, &server_key);
419 if (retval)
420 goto errout;
421 if (!server_key) {
422 retval = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
423 goto errout;
425 *kvno = server_key->key_data_kvno;
426 if ((*key = (krb5_keyblock *)malloc(sizeof **key))) {
427 retval = krb5_dbekd_decrypt_key_data(kdc_context, &master_keyblock,
428 server_key,
429 *key, NULL);
430 } else
431 retval = ENOMEM;
432 errout:
433 krb5_db_free_principal(kdc_context, &server, nprincs);
434 return retval;
437 /* This probably wants to be updated if you support last_req stuff */
439 static krb5_last_req_entry nolrentry = { KV5M_LAST_REQ_ENTRY, KRB5_LRQ_NONE, 0 };
440 static krb5_last_req_entry *nolrarray[] = { &nolrentry, 0 };
442 krb5_error_code
443 fetch_last_req_info(krb5_db_entry *dbentry, krb5_last_req_entry ***lrentry)
445 *lrentry = nolrarray;
446 return 0;
450 /* XXX! This is a temporary place-holder */
452 krb5_error_code
453 check_hot_list(krb5_ticket *ticket)
455 return 0;
459 #define MAX_REALM_LN 500
463 * subrealm - determine if r2 is a subrealm of r1
465 * SUBREALM takes two realms, r1 and r2, and
466 * determines if r2 is a subrealm of r1.
467 * r2 is a subrealm of r1 if (r1 is a prefix
468 * of r2 AND r1 and r2 begin with a /) or if
469 * (r1 is a suffix of r2 and neither r1 nor r2
470 * begin with a /).
472 * RETURNS: If r2 is a subrealm, and r1 is a prefix, the number
473 * of characters in the suffix of r2 is returned as a
474 * negative number.
476 * If r2 is a subrealm, and r1 is a suffix, the number
477 * of characters in the prefix of r2 is returned as a
478 * positive number.
480 * If r2 is not a subrealm, SUBREALM returns 0.
482 static int
483 subrealm(char *r1, char *r2)
485 size_t l1,l2;
486 l1 = strlen(r1);
487 l2 = strlen(r2);
488 if(l2 <= l1) return(0);
489 if((*r1 == '/') && (*r2 == '/') && (strncmp(r1,r2,l1) == 0)) return(l1-l2);
490 if((*r1 != '/') && (*r2 != '/') && (strncmp(r1,r2+l2-l1,l1) == 0))
491 return(l2-l1);
492 return(0);
496 * add_to_transited Adds the name of the realm which issued the
497 * ticket granting ticket on which the new ticket to
498 * be issued is based (note that this is the same as
499 * the realm of the server listed in the ticket
500 * granting ticket.
502 * ASSUMPTIONS: This procedure assumes that the transited field from
503 * the existing ticket granting ticket already appears
504 * in compressed form. It will add the new realm while
505 * maintaining that form. As long as each successive
506 * realm is added using this (or a similar) routine, the
507 * transited field will be in compressed form. The
508 * basis step is an empty transited field which is, by
509 * its nature, in its most compressed form.
511 * ARGUMENTS: krb5_data *tgt_trans Transited field from TGT
512 * krb5_data *new_trans The transited field for the new ticket
513 * krb5_principal tgs Name of ticket granting server
514 * This includes the realm of the KDC
515 * that issued the ticket granting
516 * ticket. This is the realm that is
517 * to be added to the transited field.
518 * krb5_principal client Name of the client
519 * krb5_principal server The name of the requested server.
520 * This may be the an intermediate
521 * ticket granting server.
523 * The last two argument are needed since they are
524 * implicitly part of the transited field of the new ticket
525 * even though they are not explicitly listed.
527 * RETURNS: krb5_error_code - Success, or out of memory
529 * MODIFIES: new_trans: ->length will contain the length of the new
530 * transited field.
532 * If ->data was not null when this procedure
533 * is called, the memory referenced by ->data
534 * will be deallocated.
536 * Memory will be allocated for the new transited field
537 * ->data will be updated to point to the newly
538 * allocated memory.
540 * BUGS: The space allocated for the new transited field is the
541 * maximum that might be needed given the old transited field,
542 * and the realm to be added. This length is calculated
543 * assuming that no compression of the new realm is possible.
544 * This has no adverse consequences other than the allocation
545 * of more space than required.
547 * This procedure will not yet use the null subfield notation,
548 * and it will get confused if it sees it.
550 * This procedure does not check for quoted commas in realm
551 * names.
554 static char *
555 data2string (krb5_data *d)
557 char *s;
558 s = malloc(d->length + 1);
559 if (s) {
560 memcpy(s, d->data, d->length);
561 s[d->length] = 0;
563 return s;
566 krb5_error_code
567 add_to_transited(krb5_data *tgt_trans, krb5_data *new_trans,
568 krb5_principal tgs, krb5_principal client,
569 krb5_principal server)
571 krb5_error_code retval;
572 char *realm;
573 char *trans;
574 char *otrans, *otrans_ptr;
576 /* The following are for stepping through the transited field */
578 char prev[MAX_REALM_LN];
579 char next[MAX_REALM_LN];
580 char current[MAX_REALM_LN];
581 char exp[MAX_REALM_LN]; /* Expanded current realm name */
583 int i;
584 int clst, nlst; /* count of last character in current and next */
585 int pl, pl1; /* prefix length */
586 int added; /* TRUE = new realm has been added */
588 realm = data2string(krb5_princ_realm(kdc_context, tgs));
589 if (realm == NULL)
590 return(ENOMEM);
592 otrans = data2string(tgt_trans);
593 if (otrans == NULL) {
594 free(realm);
595 return(ENOMEM);
597 /* Keep track of start so we can free */
598 otrans_ptr = otrans;
600 /* +1 for null,
601 +1 for extra comma which may be added between
602 +1 for potential space when leading slash in realm */
603 if (!(trans = (char *) malloc(strlen(realm) + strlen(otrans) + 3))) {
604 retval = ENOMEM;
605 goto fail;
608 if (new_trans->data) free(new_trans->data);
609 new_trans->data = trans;
610 new_trans->length = 0;
612 trans[0] = '\0';
614 /* For the purpose of appending, the realm preceding the first */
615 /* realm in the transited field is considered the null realm */
617 prev[0] = '\0';
619 /* read field into current */
620 for (i = 0; *otrans != '\0';) {
621 if (*otrans == '\\') {
622 if (*(++otrans) == '\0')
623 break;
624 else
625 continue;
627 if (*otrans == ',') {
628 otrans++;
629 break;
631 current[i++] = *otrans++;
632 if (i >= MAX_REALM_LN) {
633 retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
634 goto fail;
637 current[i] = '\0';
639 added = (krb5_princ_realm(kdc_context, client)->length == strlen(realm) &&
640 !strncmp(krb5_princ_realm(kdc_context, client)->data, realm, strlen(realm))) ||
641 (krb5_princ_realm(kdc_context, server)->length == strlen(realm) &&
642 !strncmp(krb5_princ_realm(kdc_context, server)->data, realm, strlen(realm)));
644 while (current[0]) {
646 /* figure out expanded form of current name */
648 clst = strlen(current) - 1;
649 if (current[0] == ' ') {
650 strncpy(exp, current+1, sizeof(exp) - 1);
651 exp[sizeof(exp) - 1] = '\0';
653 else if ((current[0] == '/') && (prev[0] == '/')) {
654 strncpy(exp, prev, sizeof(exp) - 1);
655 exp[sizeof(exp) - 1] = '\0';
656 if (strlen(exp) + strlen(current) + 1 >= MAX_REALM_LN) {
657 retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
658 goto fail;
660 strncat(exp, current, sizeof(exp) - 1 - strlen(exp));
662 else if (current[clst] == '.') {
663 strncpy(exp, current, sizeof(exp) - 1);
664 exp[sizeof(exp) - 1] = '\0';
665 if (strlen(exp) + strlen(prev) + 1 >= MAX_REALM_LN) {
666 retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
667 goto fail;
669 strncat(exp, prev, sizeof(exp) - 1 - strlen(exp));
671 else {
672 strncpy(exp, current, sizeof(exp) - 1);
673 exp[sizeof(exp) - 1] = '\0';
676 /* read field into next */
677 for (i = 0; *otrans != '\0';) {
678 if (*otrans == '\\') {
679 if (*(++otrans) == '\0')
680 break;
681 else
682 continue;
684 if (*otrans == ',') {
685 otrans++;
686 break;
688 next[i++] = *otrans++;
689 if (i >= MAX_REALM_LN) {
690 retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
691 goto fail;
694 next[i] = '\0';
695 nlst = i - 1;
697 if (!strcmp(exp, realm)) added = TRUE;
699 /* If we still have to insert the new realm */
701 if (!added) {
703 /* Is the next field compressed? If not, and if the new */
704 /* realm is a subrealm of the current realm, compress */
705 /* the new realm, and insert immediately following the */
706 /* current one. Note that we can not do this if the next*/
707 /* field is already compressed since it would mess up */
708 /* what has already been done. In most cases, this is */
709 /* not a problem because the realm to be added will be a */
710 /* subrealm of the next field too, and we will catch */
711 /* it in a future iteration. */
713 /* Note that the second test here is an unsigned comparison,
714 so the first half (or a cast) is also required. */
715 assert(nlst < 0 || nlst < sizeof(next));
716 if ((nlst < 0 || next[nlst] != '.') &&
717 (next[0] != '/') &&
718 (pl = subrealm(exp, realm))) {
719 added = TRUE;
720 current[sizeof(current) - 1] = '\0';
721 if (strlen(current) + (pl>0?pl:-pl) + 2 >= MAX_REALM_LN) {
722 retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
723 goto fail;
725 strncat(current, ",", sizeof(current) - 1 - strlen(current));
726 if (pl > 0) {
727 strncat(current, realm, (unsigned) pl);
729 else {
730 strncat(current, realm+strlen(realm)+pl, (unsigned) (-pl));
734 /* Whether or not the next field is compressed, if the */
735 /* realm to be added is a superrealm of the current realm,*/
736 /* then the current realm can be compressed. First the */
737 /* realm to be added must be compressed relative to the */
738 /* previous realm (if possible), and then the current */
739 /* realm compressed relative to the new realm. Note that */
740 /* if the realm to be added is also a superrealm of the */
741 /* previous realm, it would have been added earlier, and */
742 /* we would not reach this step this time around. */
744 else if ((pl = subrealm(realm, exp))) {
745 added = TRUE;
746 current[0] = '\0';
747 if ((pl1 = subrealm(prev,realm))) {
748 if (strlen(current) + (pl1>0?pl1:-pl1) + 1 >= MAX_REALM_LN) {
749 retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
750 goto fail;
752 if (pl1 > 0) {
753 strncat(current, realm, (unsigned) pl1);
755 else {
756 strncat(current, realm+strlen(realm)+pl1, (unsigned) (-pl1));
759 else { /* If not a subrealm */
760 if ((realm[0] == '/') && prev[0]) {
761 if (strlen(current) + 2 >= MAX_REALM_LN) {
762 retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
763 goto fail;
765 strncat(current, " ", sizeof(current) - 1 - strlen(current));
766 current[sizeof(current) - 1] = '\0';
768 if (strlen(current) + strlen(realm) + 1 >= MAX_REALM_LN) {
769 retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
770 goto fail;
772 strncat(current, realm, sizeof(current) - 1 - strlen(current));
773 current[sizeof(current) - 1] = '\0';
775 if (strlen(current) + (pl>0?pl:-pl) + 2 >= MAX_REALM_LN) {
776 retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
777 goto fail;
779 strncat(current,",", sizeof(current) - 1 - strlen(current));
780 current[sizeof(current) - 1] = '\0';
781 if (pl > 0) {
782 strncat(current, exp, (unsigned) pl);
784 else {
785 strncat(current, exp+strlen(exp)+pl, (unsigned)(-pl));
790 if (new_trans->length != 0) {
791 if (strlen(trans) + 2 >= MAX_REALM_LN) {
792 retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
793 goto fail;
795 strcat(trans, ",");
797 if (strlen(trans) + strlen(current) + 1 >= MAX_REALM_LN) {
798 retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
799 goto fail;
801 strcat(trans, current);
802 new_trans->length = strlen(trans);
804 strncpy(prev, exp, sizeof(prev) - 1);
805 prev[sizeof(prev) - 1] = '\0';
806 strncpy(current, next, sizeof(current) - 1);
807 current[sizeof(current) - 1] = '\0';
810 if (!added) {
811 if (new_trans->length != 0) {
812 if (strlen(trans) + 2 >= MAX_REALM_LN) {
813 retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
814 goto fail;
816 strcat(trans, ",");
818 if((realm[0] == '/') && trans[0]) {
819 if (strlen(trans) + 2 >= MAX_REALM_LN) {
820 retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
821 goto fail;
823 strcat(trans, " ");
825 if (strlen(trans) + strlen(realm) + 1 >= MAX_REALM_LN) {
826 retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
827 goto fail;
829 strcat(trans, realm);
830 new_trans->length = strlen(trans);
833 retval = 0;
834 fail:
835 free(realm);
836 free(otrans_ptr);
837 return (retval);
841 * Routines that validate a AS request; checks a lot of things. :-)
843 * Returns a Kerberos protocol error number, which is _not_ the same
844 * as a com_err error number!
846 #define AS_INVALID_OPTIONS (KDC_OPT_FORWARDED | KDC_OPT_PROXY |\
847 KDC_OPT_VALIDATE | KDC_OPT_RENEW | KDC_OPT_ENC_TKT_IN_SKEY)
849 validate_as_request(register krb5_kdc_req *request, krb5_db_entry client,
850 krb5_db_entry server, krb5_timestamp kdc_time,
851 const char **status)
853 int errcode;
856 * If an option is set that is only allowed in TGS requests, complain.
858 if (request->kdc_options & AS_INVALID_OPTIONS) {
859 *status = "INVALID AS OPTIONS";
860 return KDC_ERR_BADOPTION;
863 /* The client's password must not be expired, unless the server is
864 a KRB5_KDC_PWCHANGE_SERVICE. */
865 if (client.pw_expiration && client.pw_expiration < kdc_time &&
866 !isflagset(server.attributes, KRB5_KDB_PWCHANGE_SERVICE)) {
867 *status = "CLIENT KEY EXPIRED";
868 #ifdef KRBCONF_VAGUE_ERRORS
869 return(KRB_ERR_GENERIC);
870 #else
871 return(KDC_ERR_KEY_EXP);
872 #endif
875 /* The client must not be expired */
876 if (client.expiration && client.expiration < kdc_time) {
877 *status = "CLIENT EXPIRED";
878 #ifdef KRBCONF_VAGUE_ERRORS
879 return(KRB_ERR_GENERIC);
880 #else
881 return(KDC_ERR_NAME_EXP);
882 #endif
885 /* The server must not be expired */
886 if (server.expiration && server.expiration < kdc_time) {
887 *status = "SERVICE EXPIRED";
888 return(KDC_ERR_SERVICE_EXP);
892 * If the client requires password changing, then only allow the
893 * pwchange service.
895 if (isflagset(client.attributes, KRB5_KDB_REQUIRES_PWCHANGE) &&
896 !isflagset(server.attributes, KRB5_KDB_PWCHANGE_SERVICE)) {
897 *status = "REQUIRED PWCHANGE";
898 return(KDC_ERR_KEY_EXP);
901 /* Client and server must allow postdating tickets */
902 if ((isflagset(request->kdc_options, KDC_OPT_ALLOW_POSTDATE) ||
903 isflagset(request->kdc_options, KDC_OPT_POSTDATED)) &&
904 (isflagset(client.attributes, KRB5_KDB_DISALLOW_POSTDATED) ||
905 isflagset(server.attributes, KRB5_KDB_DISALLOW_POSTDATED))) {
906 *status = "POSTDATE NOT ALLOWED";
907 return(KDC_ERR_CANNOT_POSTDATE);
910 /* Client and server must allow forwardable tickets */
911 if (isflagset(request->kdc_options, KDC_OPT_FORWARDABLE) &&
912 (isflagset(client.attributes, KRB5_KDB_DISALLOW_FORWARDABLE) ||
913 isflagset(server.attributes, KRB5_KDB_DISALLOW_FORWARDABLE))) {
914 *status = "FORWARDABLE NOT ALLOWED";
915 return(KDC_ERR_POLICY);
918 /* Client and server must allow renewable tickets */
919 if (isflagset(request->kdc_options, KDC_OPT_RENEWABLE) &&
920 (isflagset(client.attributes, KRB5_KDB_DISALLOW_RENEWABLE) ||
921 isflagset(server.attributes, KRB5_KDB_DISALLOW_RENEWABLE))) {
922 *status = "RENEWABLE NOT ALLOWED";
923 return(KDC_ERR_POLICY);
926 /* Client and server must allow proxiable tickets */
927 if (isflagset(request->kdc_options, KDC_OPT_PROXIABLE) &&
928 (isflagset(client.attributes, KRB5_KDB_DISALLOW_PROXIABLE) ||
929 isflagset(server.attributes, KRB5_KDB_DISALLOW_PROXIABLE))) {
930 *status = "PROXIABLE NOT ALLOWED";
931 return(KDC_ERR_POLICY);
934 /* Check to see if client is locked out */
935 if (isflagset(client.attributes, KRB5_KDB_DISALLOW_ALL_TIX)) {
936 *status = "CLIENT LOCKED OUT";
937 return(KDC_ERR_C_PRINCIPAL_UNKNOWN);
940 /* Check to see if server is locked out */
941 if (isflagset(server.attributes, KRB5_KDB_DISALLOW_ALL_TIX)) {
942 *status = "SERVICE LOCKED OUT";
943 return(KDC_ERR_S_PRINCIPAL_UNKNOWN);
946 /* Check to see if server is allowed to be a service */
947 if (isflagset(server.attributes, KRB5_KDB_DISALLOW_SVR)) {
948 *status = "SERVICE NOT ALLOWED";
949 return(KDC_ERR_S_PRINCIPAL_UNKNOWN);
953 * Check against local policy
955 errcode = against_local_policy_as(request, server, client,
956 kdc_time, status);
957 if (errcode)
958 return errcode;
960 return 0;
963 #define ASN1_ID_CLASS (0xc0)
964 #define ASN1_ID_TYPE (0x20)
965 #define ASN1_ID_TAG (0x1f)
966 #define ASN1_CLASS_UNIV (0)
967 #define ASN1_CLASS_APP (1)
968 #define ASN1_CLASS_CTX (2)
969 #define ASN1_CLASS_PRIV (3)
970 #define asn1_id_constructed(x) (x & ASN1_ID_TYPE)
971 #define asn1_id_primitive(x) (!asn1_id_constructed(x))
972 #define asn1_id_class(x) ((x & ASN1_ID_CLASS) >> 6)
973 #define asn1_id_tag(x) (x & ASN1_ID_TAG)
976 * asn1length - return encoded length of value.
978 * passed a pointer into the asn.1 stream, which is updated
979 * to point right after the length bits.
981 * returns -1 on failure.
983 static int
984 asn1length(unsigned char **astream)
986 int length; /* resulting length */
987 int sublen; /* sublengths */
988 int blen; /* bytes of length */
989 unsigned char *p; /* substring searching */
991 if (**astream & 0x80) {
992 blen = **astream & 0x7f;
993 if (blen > 3) {
994 return(-1);
996 for (++*astream, length = 0; blen; ++*astream, blen--) {
997 length = (length << 8) | **astream;
999 if (length == 0) {
1000 /* indefinite length, figure out by hand */
1001 p = *astream;
1002 p++;
1003 while (1) {
1004 /* compute value length. */
1005 if ((sublen = asn1length(&p)) < 0) {
1006 return(-1);
1008 p += sublen;
1009 /* check for termination */
1010 if ((!*p++) && (!*p)) {
1011 p++;
1012 break;
1015 length = p - *astream;
1017 } else {
1018 length = **astream;
1019 ++*astream;
1021 return(length);
1025 * fetch_asn1_field - return raw asn.1 stream of subfield.
1027 * this routine is passed a context-dependent tag number and "level" and returns
1028 * the size and length of the corresponding level subfield.
1030 * levels and are numbered starting from 1.
1032 * returns 0 on success, -1 otherwise.
1035 fetch_asn1_field(unsigned char *astream, unsigned int level,
1036 unsigned int field, krb5_data *data)
1038 unsigned char *estream; /* end of stream */
1039 int classes; /* # classes seen so far this level */
1040 unsigned int levels = 0; /* levels seen so far */
1041 int lastlevel = 1000; /* last level seen */
1042 int length; /* various lengths */
1043 int tag; /* tag number */
1044 unsigned char savelen; /* saved length of our field */
1046 classes = -1;
1047 /* we assume that the first identifier/length will tell us
1048 how long the entire stream is. */
1049 astream++;
1050 estream = astream;
1051 if ((length = asn1length(&astream)) < 0) {
1052 return(-1);
1054 estream += length;
1055 /* search down the stream, checking identifiers. we process identifiers
1056 until we hit the "level" we want, and then process that level for our
1057 subfield, always making sure we don't go off the end of the stream. */
1058 while (astream < estream) {
1059 if (!asn1_id_constructed(*astream)) {
1060 return(-1);
1062 if (asn1_id_class(*astream) == ASN1_CLASS_CTX) {
1063 if ((tag = (int)asn1_id_tag(*astream)) <= lastlevel) {
1064 levels++;
1065 classes = -1;
1067 lastlevel = tag;
1068 if (levels == level) {
1069 /* in our context-dependent class, is this the one we're looking for ? */
1070 if (tag == field) {
1071 /* return length and data */
1072 astream++;
1073 savelen = *astream;
1074 if ((data->length = asn1length(&astream)) < 0) {
1075 return(-1);
1077 /* if the field length is indefinite, we will have to subtract two
1078 (terminating octets) from the length returned since we don't want
1079 to pass any info from the "wrapper" back. asn1length will always return
1080 the *total* length of the field, not just what's contained in it */
1081 if ((savelen & 0xff) == 0x80) {
1082 data->length -=2 ;
1084 data->data = (char *)astream;
1085 return(0);
1086 } else if (tag <= classes) {
1087 /* we've seen this class before, something must be wrong */
1088 return(-1);
1089 } else {
1090 classes = tag;
1094 /* if we're not on our level yet, process this value. otherwise skip over it */
1095 astream++;
1096 if ((length = asn1length(&astream)) < 0) {
1097 return(-1);
1099 if (levels == level) {
1100 astream += length;
1103 return(-1);
1107 * Routines that validate a TGS request; checks a lot of things. :-)
1109 * Returns a Kerberos protocol error number, which is _not_ the same
1110 * as a com_err error number!
1112 #define TGS_OPTIONS_HANDLED (KDC_OPT_FORWARDABLE | KDC_OPT_FORWARDED | \
1113 KDC_OPT_PROXIABLE | KDC_OPT_PROXY | \
1114 KDC_OPT_ALLOW_POSTDATE | KDC_OPT_POSTDATED | \
1115 KDC_OPT_RENEWABLE | KDC_OPT_RENEWABLE_OK | \
1116 KDC_OPT_ENC_TKT_IN_SKEY | KDC_OPT_RENEW | \
1117 KDC_OPT_VALIDATE)
1119 #define NO_TGT_OPTION (KDC_OPT_FORWARDED | KDC_OPT_PROXY | KDC_OPT_RENEW | \
1120 KDC_OPT_VALIDATE)
1123 validate_tgs_request(register krb5_kdc_req *request, krb5_db_entry server,
1124 krb5_ticket *ticket, krb5_timestamp kdc_time,
1125 const char **status)
1127 int errcode;
1128 int st_idx = 0;
1131 * If an illegal option is set, ignore it.
1133 request->kdc_options &= TGS_OPTIONS_HANDLED;
1135 /* Check to see if server has expired */
1136 if (server.expiration && server.expiration < kdc_time) {
1137 *status = "SERVICE EXPIRED";
1138 return(KDC_ERR_SERVICE_EXP);
1142 * Verify that the server principal in authdat->ticket is correct
1143 * (either the ticket granting service or the service that was
1144 * originally requested)
1146 if (request->kdc_options & NO_TGT_OPTION) {
1147 if (!krb5_principal_compare(kdc_context, ticket->server, request->server)) {
1148 *status = "SERVER DIDN'T MATCH TICKET FOR RENEW/FORWARD/ETC";
1149 return(KDC_ERR_SERVER_NOMATCH);
1151 } else {
1153 * OK, we need to validate the krbtgt service in the ticket.
1155 * The krbtgt service is of the form:
1156 * krbtgt/realm-A@realm-B
1158 * Realm A is the "server realm"; the realm of the
1159 * server of the requested ticket must match this realm.
1160 * Of course, it should be a realm serviced by this KDC.
1162 * Realm B is the "client realm"; this is what should be
1163 * added to the transited field. (which is done elsewhere)
1166 /* Make sure there are two components... */
1167 if (krb5_princ_size(kdc_context, ticket->server) != 2) {
1168 *status = "BAD TGS SERVER LENGTH";
1169 return KRB_AP_ERR_NOT_US;
1171 /* ...that the first component is krbtgt... */
1172 if (!krb5_is_tgs_principal(ticket->server)) {
1173 *status = "BAD TGS SERVER NAME";
1174 return KRB_AP_ERR_NOT_US;
1176 /* ...and that the second component matches the server realm... */
1177 if ((krb5_princ_size(kdc_context, ticket->server) <= 1) ||
1178 (krb5_princ_component(kdc_context, ticket->server, 1)->length !=
1179 krb5_princ_realm(kdc_context, request->server)->length) ||
1180 memcmp(krb5_princ_component(kdc_context, ticket->server, 1)->data,
1181 krb5_princ_realm(kdc_context, request->server)->data,
1182 krb5_princ_realm(kdc_context, request->server)->length)) {
1183 *status = "BAD TGS SERVER INSTANCE";
1184 return KRB_AP_ERR_NOT_US;
1186 /* XXX add check that second component must match locally
1187 * supported realm?
1190 /* Server must allow TGS based issuances */
1191 if (isflagset(server.attributes, KRB5_KDB_DISALLOW_TGT_BASED)) {
1192 *status = "TGT BASED NOT ALLOWED";
1193 return(KDC_ERR_POLICY);
1197 /* TGS must be forwardable to get forwarded or forwardable ticket */
1198 if ((isflagset(request->kdc_options, KDC_OPT_FORWARDED) ||
1199 isflagset(request->kdc_options, KDC_OPT_FORWARDABLE)) &&
1200 !isflagset(ticket->enc_part2->flags, TKT_FLG_FORWARDABLE)) {
1201 *status = "TGT NOT FORWARDABLE";
1203 return KDC_ERR_BADOPTION;
1206 /* TGS must be proxiable to get proxiable ticket */
1207 if ((isflagset(request->kdc_options, KDC_OPT_PROXY) ||
1208 isflagset(request->kdc_options, KDC_OPT_PROXIABLE)) &&
1209 !isflagset(ticket->enc_part2->flags, TKT_FLG_PROXIABLE)) {
1210 *status = "TGT NOT PROXIABLE";
1211 return KDC_ERR_BADOPTION;
1214 /* TGS must allow postdating to get postdated ticket */
1215 if ((isflagset(request->kdc_options, KDC_OPT_ALLOW_POSTDATE) ||
1216 isflagset(request->kdc_options, KDC_OPT_POSTDATED)) &&
1217 !isflagset(ticket->enc_part2->flags, TKT_FLG_MAY_POSTDATE)) {
1218 *status = "TGT NOT POSTDATABLE";
1219 return KDC_ERR_BADOPTION;
1222 /* can only validate invalid tix */
1223 if (isflagset(request->kdc_options, KDC_OPT_VALIDATE) &&
1224 !isflagset(ticket->enc_part2->flags, TKT_FLG_INVALID)) {
1225 *status = "VALIDATE VALID TICKET";
1226 return KDC_ERR_BADOPTION;
1229 /* can only renew renewable tix */
1230 if ((isflagset(request->kdc_options, KDC_OPT_RENEW) ||
1231 isflagset(request->kdc_options, KDC_OPT_RENEWABLE)) &&
1232 !isflagset(ticket->enc_part2->flags, TKT_FLG_RENEWABLE)) {
1233 *status = "TICKET NOT RENEWABLE";
1234 return KDC_ERR_BADOPTION;
1237 /* can not proxy ticket granting tickets */
1238 if (isflagset(request->kdc_options, KDC_OPT_PROXY) &&
1239 (!request->server->data ||
1240 request->server->data[0].length != KRB5_TGS_NAME_SIZE ||
1241 memcmp(request->server->data[0].data, KRB5_TGS_NAME,
1242 KRB5_TGS_NAME_SIZE))) {
1243 *status = "CAN'T PROXY TGT";
1244 return KDC_ERR_BADOPTION;
1247 /* Server must allow forwardable tickets */
1248 if (isflagset(request->kdc_options, KDC_OPT_FORWARDABLE) &&
1249 isflagset(server.attributes, KRB5_KDB_DISALLOW_FORWARDABLE)) {
1250 *status = "NON-FORWARDABLE TICKET";
1251 return(KDC_ERR_POLICY);
1254 /* Server must allow renewable tickets */
1255 if (isflagset(request->kdc_options, KDC_OPT_RENEWABLE) &&
1256 isflagset(server.attributes, KRB5_KDB_DISALLOW_RENEWABLE)) {
1257 *status = "NON-RENEWABLE TICKET";
1258 return(KDC_ERR_POLICY);
1261 /* Server must allow proxiable tickets */
1262 if (isflagset(request->kdc_options, KDC_OPT_PROXIABLE) &&
1263 isflagset(server.attributes, KRB5_KDB_DISALLOW_PROXIABLE)) {
1264 *status = "NON-PROXIABLE TICKET";
1265 return(KDC_ERR_POLICY);
1268 /* Server must allow postdated tickets */
1269 if (isflagset(request->kdc_options, KDC_OPT_ALLOW_POSTDATE) &&
1270 isflagset(server.attributes, KRB5_KDB_DISALLOW_POSTDATED)) {
1271 *status = "NON-POSTDATABLE TICKET";
1272 return(KDC_ERR_CANNOT_POSTDATE);
1275 /* Server must allow DUP SKEY requests */
1276 if (isflagset(request->kdc_options, KDC_OPT_ENC_TKT_IN_SKEY) &&
1277 isflagset(server.attributes, KRB5_KDB_DISALLOW_DUP_SKEY)) {
1278 *status = "DUP_SKEY DISALLOWED";
1279 return(KDC_ERR_POLICY);
1282 /* Server must not be locked out */
1283 if (isflagset(server.attributes, KRB5_KDB_DISALLOW_ALL_TIX)) {
1284 *status = "SERVER LOCKED OUT";
1285 return(KDC_ERR_S_PRINCIPAL_UNKNOWN);
1288 /* Server must be allowed to be a service */
1289 if (isflagset(server.attributes, KRB5_KDB_DISALLOW_SVR)) {
1290 *status = "SERVER NOT ALLOWED";
1291 return(KDC_ERR_S_PRINCIPAL_UNKNOWN);
1294 /* Check the hot list */
1295 if (check_hot_list(ticket)) {
1296 *status = "HOT_LIST";
1297 return(KRB_AP_ERR_REPEAT);
1300 /* Check the start time vs. the KDC time */
1301 if (isflagset(request->kdc_options, KDC_OPT_VALIDATE)) {
1302 if (ticket->enc_part2->times.starttime > kdc_time) {
1303 *status = "NOT_YET_VALID";
1304 return(KRB_AP_ERR_TKT_NYV);
1309 * Check the renew_till time. The endtime was already
1310 * been checked in the initial authentication check.
1312 if (isflagset(request->kdc_options, KDC_OPT_RENEW) &&
1313 (ticket->enc_part2->times.renew_till < kdc_time)) {
1314 *status = "TKT_EXPIRED";
1315 return(KRB_AP_ERR_TKT_EXPIRED);
1319 * Checks for ENC_TKT_IN_SKEY:
1321 * (1) Make sure the second ticket exists
1322 * (2) Make sure it is a ticket granting ticket
1324 if (isflagset(request->kdc_options, KDC_OPT_ENC_TKT_IN_SKEY)) {
1325 if (!request->second_ticket ||
1326 !request->second_ticket[st_idx]) {
1327 *status = "NO_2ND_TKT";
1328 return(KDC_ERR_BADOPTION);
1330 if (!krb5_principal_compare(kdc_context, request->second_ticket[st_idx]->server,
1331 tgs_server)) {
1332 *status = "2ND_TKT_NOT_TGS";
1333 return(KDC_ERR_POLICY);
1335 st_idx++;
1338 /* Check for hardware preauthentication */
1339 if (isflagset(server.attributes, KRB5_KDB_REQUIRES_HW_AUTH) &&
1340 !isflagset(ticket->enc_part2->flags,TKT_FLG_HW_AUTH)) {
1341 *status = "NO HW PREAUTH";
1342 return KRB_ERR_GENERIC;
1345 /* Check for any kind of preauthentication */
1346 if (isflagset(server.attributes, KRB5_KDB_REQUIRES_PRE_AUTH) &&
1347 !isflagset(ticket->enc_part2->flags, TKT_FLG_PRE_AUTH)) {
1348 *status = "NO PREAUTH";
1349 return KRB_ERR_GENERIC;
1353 * Check local policy
1355 errcode = against_local_policy_tgs(request, server, ticket, status);
1356 if (errcode)
1357 return errcode;
1360 return 0;
1364 * This function returns 1 if the dbentry has a key for a specified
1365 * keytype, and 0 if not.
1368 dbentry_has_key_for_enctype(krb5_context context, krb5_db_entry *client,
1369 krb5_enctype enctype)
1371 krb5_error_code retval;
1372 krb5_key_data *datap;
1374 retval = krb5_dbe_find_enctype(context, client, enctype,
1375 -1, 0, &datap);
1376 if (retval)
1377 return 0;
1378 else
1379 return 1;
1383 * This function returns 1 if the entity referenced by this
1384 * structure can support the a particular encryption system, and 0 if
1385 * not.
1387 * XXX eventually this information should be looked up in the
1388 * database. Since it isn't, we use some hueristics and attribute
1389 * options bits for now.
1392 dbentry_supports_enctype(krb5_context context, krb5_db_entry *client,
1393 krb5_enctype enctype)
1396 * If it's DES_CBC_MD5, there's a bit in the attribute mask which
1397 * checks to see if we support it.
1399 * In theory everything's supposed to support DES_CBC_MD5, but
1400 * that's not the reality....
1404 * We are assuming that all entries can support MD5; this information
1405 * need not be kept in the database.
1409 if (enctype == ENCTYPE_DES_CBC_MD5)
1410 return 1;
1413 * XXX we assume everything can understand DES_CBC_CRC
1415 if (enctype == ENCTYPE_DES_CBC_CRC)
1416 return 1;
1419 * If we have a key for the encryption system, we assume it's
1420 * supported.
1422 return dbentry_has_key_for_enctype(context, client, enctype);
1426 * This function returns the keytype which should be selected for the
1427 * session key. It is based on the ordered list which the user
1428 * requested, and what the KDC and the application server can support.
1430 krb5_enctype
1431 select_session_keytype(krb5_context context, krb5_db_entry *server,
1432 int nktypes, krb5_enctype *ktype)
1434 int i;
1436 for (i = 0; i < nktypes; i++) {
1437 if (!krb5_c_valid_enctype(ktype[i]))
1438 continue;
1440 if (!krb5_is_permitted_enctype(context, ktype[i]))
1441 continue;
1443 if (dbentry_supports_enctype(context, server, ktype[i]))
1444 return ktype[i];
1446 return 0;
1450 * This function returns salt information for a particular client_key
1452 krb5_error_code
1453 get_salt_from_key(krb5_context context, krb5_principal client,
1454 krb5_key_data *client_key, krb5_data *salt)
1456 krb5_error_code retval;
1457 krb5_data * realm;
1459 salt->data = 0;
1460 salt->length = SALT_TYPE_NO_LENGTH;
1462 if (client_key->key_data_ver == 1)
1463 return 0;
1465 switch (client_key->key_data_type[1]) {
1466 case KRB5_KDB_SALTTYPE_NORMAL:
1467 break;
1468 case KRB5_KDB_SALTTYPE_V4:
1469 /* send an empty (V4) salt */
1470 salt->data = 0;
1471 salt->length = 0;
1472 break;
1473 case KRB5_KDB_SALTTYPE_NOREALM:
1474 if ((retval = krb5_principal2salt_norealm(context, client, salt)))
1475 return retval;
1476 break;
1477 case KRB5_KDB_SALTTYPE_AFS3:
1478 /* send the same salt as with onlyrealm - but with no type info,
1479 we just hope they figure it out on the other end. */
1480 /* fall through to onlyrealm: */
1481 case KRB5_KDB_SALTTYPE_ONLYREALM:
1482 realm = krb5_princ_realm(context, client);
1483 salt->length = realm->length;
1484 if ((salt->data = malloc(realm->length)) == NULL)
1485 return ENOMEM;
1486 memcpy(salt->data, realm->data, realm->length);
1487 break;
1488 case KRB5_KDB_SALTTYPE_SPECIAL:
1489 salt->length = client_key->key_data_length[1];
1490 if ((salt->data = malloc(salt->length)) == NULL)
1491 return ENOMEM;
1492 memcpy(salt->data, client_key->key_data_contents[1], salt->length);
1493 break;
1495 return 0;
1499 * Limit strings to a "reasonable" length to prevent crowding out of
1500 * other useful information in the log entry
1502 #define NAME_LENGTH_LIMIT 128
1504 void limit_string(char *name)
1506 int i;
1508 if (!name)
1509 return;
1511 if (strlen(name) < NAME_LENGTH_LIMIT)
1512 return;
1514 i = NAME_LENGTH_LIMIT-4;
1515 name[i++] = '.';
1516 name[i++] = '.';
1517 name[i++] = '.';
1518 name[i] = '\0';
1519 return;
1523 * L10_2 = log10(2**x), rounded up; log10(2) ~= 0.301.
1525 #define L10_2(x) ((int)(((x * 301) + 999) / 1000))
1528 * Max length of sprintf("%ld") for an int of type T; includes leading
1529 * minus sign and terminating NUL.
1531 #define D_LEN(t) (L10_2(sizeof(t) * CHAR_BIT) + 2)
1533 void
1534 ktypes2str(char *s, size_t len, int nktypes, krb5_enctype *ktype)
1536 int i;
1537 char stmp[D_LEN(krb5_enctype) + 1];
1538 char *p;
1540 if (nktypes < 0
1541 || len < (sizeof(" etypes {...}") + D_LEN(int))) {
1542 *s = '\0';
1543 return;
1546 sprintf(s, "%d etypes {", nktypes);
1547 for (i = 0; i < nktypes; i++) {
1548 sprintf(stmp, "%s%ld", i ? " " : "", (long)ktype[i]);
1549 if (strlen(s) + strlen(stmp) + sizeof("}") > len)
1550 break;
1551 strcat(s, stmp);
1553 if (i < nktypes) {
1555 * We broke out of the loop. Try to truncate the list.
1557 p = s + strlen(s);
1558 while (p - s + sizeof("...}") > len) {
1559 while (p > s && *p != ' ' && *p != '{')
1560 *p-- = '\0';
1561 if (p > s && *p == ' ') {
1562 *p-- = '\0';
1563 continue;
1566 strcat(s, "...");
1568 strcat(s, "}");
1569 return;
1572 void
1573 rep_etypes2str(char *s, size_t len, krb5_kdc_rep *rep)
1575 char stmp[sizeof("ses=") + D_LEN(krb5_enctype)];
1577 if (len < (3 * D_LEN(krb5_enctype)
1578 + sizeof("etypes {rep= tkt= ses=}"))) {
1579 *s = '\0';
1580 return;
1583 sprintf(s, "etypes {rep=%ld", (long)rep->enc_part.enctype);
1585 if (rep->ticket != NULL) {
1586 sprintf(stmp, " tkt=%ld", (long)rep->ticket->enc_part.enctype);
1587 strcat(s, stmp);
1590 if (rep->ticket != NULL
1591 && rep->ticket->enc_part2 != NULL
1592 && rep->ticket->enc_part2->session != NULL) {
1593 sprintf(stmp, " ses=%ld",
1594 (long)rep->ticket->enc_part2->session->enctype);
1595 strcat(s, stmp);
1597 strcat(s, "}");
1598 return;