krb5_error_code krb5int_generate_and_save_subkey(krb5_context context, krb5_auth_context auth_context, krb5_keyblock *keyblock, krb5_enctype enctype) { /* Provide some more fodder for random number code. This isn't strong cryptographically; the point here is not to guarantee randomness, but to make it less likely that multiple sessions could pick the same subkey. */ struct { krb5_int32 sec, usec; } rnd_data; krb5_data d; krb5_error_code retval; krb5_keyblock *kb = NULL; if (krb5_crypto_us_timeofday(&rnd_data.sec, &rnd_data.usec) == 0) { d.length = sizeof(rnd_data); d.data = (char *) &rnd_data; krb5_c_random_add_entropy(context, KRB5_C_RANDSOURCE_TIMING, &d); } retval = krb5_generate_subkey_extended(context, keyblock, enctype, &kb); if (retval) return retval; retval = krb5_auth_con_setsendsubkey(context, auth_context, kb); if (retval) goto cleanup; retval = krb5_auth_con_setrecvsubkey(context, auth_context, kb); if (retval) goto cleanup; cleanup: if (retval) { (void) krb5_auth_con_setsendsubkey(context, auth_context, NULL); (void) krb5_auth_con_setrecvsubkey(context, auth_context, NULL); } krb5_free_keyblock(context, kb); return retval; }
krb5_error_code krb5_generate_seq_number(krb5_context context, const krb5_keyblock *key, krb5_ui_4 *seqno) { krb5_data seed; krb5_error_code retval; #if 0 /* * Solaris Kerberos: Don't bother with this PRNG stuff, * we have /dev/random and PKCS#11 to handle Random Numbers. */ seed.length = key->length; seed.data = key->contents; if ((retval = krb5_c_random_add_entropy(context, KRB5_C_RANDSOURCE_TRUSTEDPARTY, &seed))) return(retval); #endif /* 0 */ seed.length = sizeof(*seqno); seed.data = (char *) seqno; retval = krb5_c_random_make_octets(context, &seed); if (retval) return retval; /* * Work around implementation incompatibilities by not generating * initial sequence numbers greater than 2^30. Previous MIT * implementations use signed sequence numbers, so initial * sequence numbers 2^31 to 2^32-1 inclusive will be rejected. * Letting the maximum initial sequence number be 2^30-1 allows * for about 2^30 messages to be sent before wrapping into * "negative" numbers. */ *seqno &= 0x3fffffff; if (*seqno == 0) *seqno = 1; return 0; }
krb5_error_code KRB5_CALLCONV krb5_init_context_profile(profile_t profile, krb5_flags flags, krb5_context *context_out) { krb5_context ctx = 0; krb5_error_code retval; struct { krb5_timestamp now; krb5_int32 now_usec; long pid; } seed_data; krb5_data seed; int tmp; char *plugin_dir = NULL; /* Verify some assumptions. If the assumptions hold and the compiler is optimizing, this should result in no code being executed. If we're guessing "unsigned long long" instead of using uint64_t, the possibility does exist that we're wrong. */ { uint64_t i64; assert(sizeof(i64) == 8); i64 = 0, i64--, i64 >>= 62; assert(i64 == 3); i64 = 1, i64 <<= 31, i64 <<= 31, i64 <<= 1; assert(i64 != 0); i64 <<= 1; assert(i64 == 0); } retval = krb5int_initialize_library(); if (retval) return retval; #if (defined(_WIN32)) /* * Load the krbcc32.dll if necessary. We do this here so that * we know to use API: later on during initialization. * The context being NULL is ok. */ krb5_win_ccdll_load(ctx); /* * krb5_vercheck() is defined in win_glue.c, and this is * where we handle the timebomb and version server checks. */ retval = krb5_vercheck(); if (retval) return retval; #endif *context_out = NULL; ctx = calloc(1, sizeof(struct _krb5_context)); if (!ctx) return ENOMEM; ctx->magic = KV5M_CONTEXT; ctx->profile_secure = (flags & KRB5_INIT_CONTEXT_SECURE) != 0; retval = k5_os_init_context(ctx, profile, flags); if (retval) goto cleanup; ctx->trace_callback = NULL; #ifndef DISABLE_TRACING if (!ctx->profile_secure) k5_init_trace(ctx); #endif retval = get_boolean(ctx, KRB5_CONF_ALLOW_WEAK_CRYPTO, 0, &tmp); if (retval) goto cleanup; ctx->allow_weak_crypto = tmp; retval = get_boolean(ctx, KRB5_CONF_IGNORE_ACCEPTOR_HOSTNAME, 0, &tmp); if (retval) goto cleanup; ctx->ignore_acceptor_hostname = tmp; retval = get_tristate(ctx, KRB5_CONF_DNS_CANONICALIZE_HOSTNAME, "fallback", CANONHOST_FALLBACK, 1, &tmp); if (retval) goto cleanup; ctx->dns_canonicalize_hostname = tmp; /* initialize the prng (not well, but passable) */ if ((retval = krb5_c_random_os_entropy( ctx, 0, NULL)) !=0) goto cleanup; if ((retval = krb5_crypto_us_timeofday(&seed_data.now, &seed_data.now_usec))) goto cleanup; seed_data.pid = getpid (); seed.length = sizeof(seed_data); seed.data = (char *) &seed_data; if ((retval = krb5_c_random_add_entropy(ctx, KRB5_C_RANDSOURCE_TIMING, &seed))) goto cleanup; ctx->default_realm = 0; get_integer(ctx, KRB5_CONF_CLOCKSKEW, DEFAULT_CLOCKSKEW, &tmp); ctx->clockskew = tmp; /* DCE 1.1 and below only support CKSUMTYPE_RSA_MD4 (2) */ /* DCE add kdc_req_checksum_type = 2 to krb5.conf */ get_integer(ctx, KRB5_CONF_KDC_REQ_CHECKSUM_TYPE, CKSUMTYPE_RSA_MD5, &tmp); ctx->kdc_req_sumtype = tmp; get_integer(ctx, KRB5_CONF_AP_REQ_CHECKSUM_TYPE, 0, &tmp); ctx->default_ap_req_sumtype = tmp; get_integer(ctx, KRB5_CONF_SAFE_CHECKSUM_TYPE, CKSUMTYPE_RSA_MD5_DES, &tmp); ctx->default_safe_sumtype = tmp; get_integer(ctx, KRB5_CONF_KDC_DEFAULT_OPTIONS, KDC_OPT_RENEWABLE_OK, &tmp); ctx->kdc_default_options = tmp; #define DEFAULT_KDC_TIMESYNC 1 get_integer(ctx, KRB5_CONF_KDC_TIMESYNC, DEFAULT_KDC_TIMESYNC, &tmp); ctx->library_options = tmp ? KRB5_LIBOPT_SYNC_KDCTIME : 0; retval = profile_get_string(ctx->profile, KRB5_CONF_LIBDEFAULTS, KRB5_CONF_PLUGIN_BASE_DIR, 0, DEFAULT_PLUGIN_BASE_DIR, &plugin_dir); if (!retval) retval = k5_expand_path_tokens(ctx, plugin_dir, &ctx->plugin_base_dir); if (retval) { TRACE_PROFILE_ERR(ctx, KRB5_CONF_PLUGIN_BASE_DIR, KRB5_CONF_LIBDEFAULTS, retval); goto cleanup; } /* * We use a default file credentials cache of 3. See * lib/krb5/krb/ccache/file/fcc.h for a description of the * credentials cache types. * * Note: DCE 1.0.3a only supports a cache type of 1 * DCE 1.1 supports a cache type of 2. */ #define DEFAULT_CCACHE_TYPE 4 get_integer(ctx, KRB5_CONF_CCACHE_TYPE, DEFAULT_CCACHE_TYPE, &tmp); ctx->fcc_default_format = tmp + 0x0500; ctx->prompt_types = 0; ctx->use_conf_ktypes = 0; ctx->udp_pref_limit = -1; /* It's OK if this fails */ (void)profile_get_string(ctx->profile, KRB5_CONF_LIBDEFAULTS, KRB5_CONF_ERR_FMT, NULL, NULL, &ctx->err_fmt); *context_out = ctx; ctx = NULL; cleanup: profile_release_string(plugin_dir); krb5_free_context(ctx); return retval; }
/* * Initialize a realm control structure from the alternate profile or from * the specified defaults. * * After we're complete here, the essence of the realm is embodied in the * realm data and we should be all set to begin operation for that realm. */ static krb5_error_code init_realm(kdc_realm_t *rdp, char *realm, char *def_mpname, krb5_enctype def_enctype, char *def_udp_ports, char *def_tcp_ports, krb5_boolean def_manual, krb5_boolean def_restrict_anon, char **db_args, char *no_refrls, char *host_based_srvcs) { krb5_error_code kret; krb5_boolean manual; krb5_realm_params *rparams; int kdb_open_flags; krb5_kvno mkvno = IGNORE_VNO; memset(rdp, 0, sizeof(kdc_realm_t)); if (!realm) { kret = EINVAL; goto whoops; } rdp->realm_name = strdup(realm); if (rdp->realm_name == NULL) { kret = ENOMEM; goto whoops; } kret = krb5int_init_context_kdc(&rdp->realm_context); if (kret) { kdc_err(NULL, kret, _("while getting context for realm %s"), realm); goto whoops; } kret = krb5_read_realm_params(rdp->realm_context, rdp->realm_name, &rparams); if (kret) { kdc_err(rdp->realm_context, kret, _("while reading realm parameters")); goto whoops; } /* Handle profile file name */ if (rparams && rparams->realm_profile) { rdp->realm_profile = strdup(rparams->realm_profile); if (!rdp->realm_profile) { kret = ENOMEM; goto whoops; } } /* Handle master key name */ if (rparams && rparams->realm_mkey_name) rdp->realm_mpname = strdup(rparams->realm_mkey_name); else rdp->realm_mpname = (def_mpname) ? strdup(def_mpname) : strdup(KRB5_KDB_M_NAME); if (!rdp->realm_mpname) { kret = ENOMEM; goto whoops; } /* Handle KDC ports */ if (rparams && rparams->realm_kdc_ports) rdp->realm_ports = strdup(rparams->realm_kdc_ports); else rdp->realm_ports = strdup(def_udp_ports); if (!rdp->realm_ports) { kret = ENOMEM; goto whoops; } if (rparams && rparams->realm_kdc_tcp_ports) rdp->realm_tcp_ports = strdup(rparams->realm_kdc_tcp_ports); else rdp->realm_tcp_ports = strdup(def_tcp_ports); if (!rdp->realm_tcp_ports) { kret = ENOMEM; goto whoops; } /* Handle stash file */ if (rparams && rparams->realm_stash_file) { rdp->realm_stash = strdup(rparams->realm_stash_file); if (!rdp->realm_stash) { kret = ENOMEM; goto whoops; } manual = FALSE; } else manual = def_manual; if (rparams && rparams->realm_restrict_anon_valid) rdp->realm_restrict_anon = rparams->realm_restrict_anon; else rdp->realm_restrict_anon = def_restrict_anon; /* Handle master key type */ if (rparams && rparams->realm_enctype_valid) rdp->realm_mkey.enctype = (krb5_enctype) rparams->realm_enctype; else rdp->realm_mkey.enctype = manual ? def_enctype : ENCTYPE_UNKNOWN; /* Handle reject-bad-transit flag */ if (rparams && rparams->realm_reject_bad_transit_valid) rdp->realm_reject_bad_transit = rparams->realm_reject_bad_transit; else rdp->realm_reject_bad_transit = 1; /* Handle ticket maximum life */ rdp->realm_maxlife = (rparams && rparams->realm_max_life_valid) ? rparams->realm_max_life : KRB5_KDB_MAX_LIFE; /* Handle ticket renewable maximum life */ rdp->realm_maxrlife = (rparams && rparams->realm_max_rlife_valid) ? rparams->realm_max_rlife : KRB5_KDB_MAX_RLIFE; /* Handle KDC referrals */ kret = handle_referral_params(rparams, no_refrls, host_based_srvcs, rdp); if (kret == ENOMEM) goto whoops; if (rparams) krb5_free_realm_params(rdp->realm_context, rparams); /* * We've got our parameters, now go and setup our realm context. */ /* Set the default realm of this context */ if ((kret = krb5_set_default_realm(rdp->realm_context, realm))) { kdc_err(rdp->realm_context, kret, _("while setting default realm to %s"), realm); goto whoops; } /* first open the database before doing anything */ kdb_open_flags = KRB5_KDB_OPEN_RW | KRB5_KDB_SRV_TYPE_KDC; if ((kret = krb5_db_open(rdp->realm_context, db_args, kdb_open_flags))) { kdc_err(rdp->realm_context, kret, _("while initializing database for realm %s"), realm); goto whoops; } /* Assemble and parse the master key name */ if ((kret = krb5_db_setup_mkey_name(rdp->realm_context, rdp->realm_mpname, rdp->realm_name, (char **) NULL, &rdp->realm_mprinc))) { kdc_err(rdp->realm_context, kret, _("while setting up master key name %s for realm %s"), rdp->realm_mpname, realm); goto whoops; } /* * Get the master key (note, may not be the most current mkey). */ if ((kret = krb5_db_fetch_mkey(rdp->realm_context, rdp->realm_mprinc, rdp->realm_mkey.enctype, manual, FALSE, rdp->realm_stash, &mkvno, NULL, &rdp->realm_mkey))) { kdc_err(rdp->realm_context, kret, _("while fetching master key %s for realm %s"), rdp->realm_mpname, realm); goto whoops; } if ((kret = krb5_db_fetch_mkey_list(rdp->realm_context, rdp->realm_mprinc, &rdp->realm_mkey, mkvno, &rdp->mkey_list))) { kdc_err(rdp->realm_context, kret, _("while fetching master keys list for realm %s"), realm); goto whoops; } /* Set up the keytab */ if ((kret = krb5_ktkdb_resolve(rdp->realm_context, NULL, &rdp->realm_keytab))) { kdc_err(rdp->realm_context, kret, _("while resolving kdb keytab for realm %s"), realm); goto whoops; } /* Preformat the TGS name */ if ((kret = krb5_build_principal(rdp->realm_context, &rdp->realm_tgsprinc, strlen(realm), realm, KRB5_TGS_NAME, realm, (char *) NULL))) { kdc_err(rdp->realm_context, kret, _("while building TGS name for realm %s"), realm); goto whoops; } if (!rkey_init_done) { krb5_data seed; /* * If all that worked, then initialize the random key * generators. */ seed.length = rdp->realm_mkey.length; seed.data = (char *)rdp->realm_mkey.contents; if ((kret = krb5_c_random_add_entropy(rdp->realm_context, KRB5_C_RANDSOURCE_TRUSTEDPARTY, &seed))) goto whoops; rkey_init_done = 1; } whoops: /* * If we choked, then clean up any dirt we may have dropped on the floor. */ if (kret) { finish_realm(rdp); } return(kret); }
krb5_error_code dispatch(krb5_data *pkt, const krb5_fulladdr *from, krb5_data **response) { krb5_error_code retval; krb5_kdc_req *as_req; krb5_int32 now, now_usec; /* decode incoming packet, and dispatch */ #ifndef NOCACHE /* try the replay lookaside buffer */ if (kdc_check_lookaside(pkt, response)) { /* a hit! */ const char *name = 0; char buf[46]; name = inet_ntop (ADDRTYPE2FAMILY (from->address->addrtype), from->address->contents, buf, sizeof (buf)); if (name == 0) name = "[unknown address type]"; krb5_klog_syslog(LOG_INFO, "DISPATCH: repeated (retransmitted?) request from %s, resending previous response", name); return 0; } #endif retval = krb5_crypto_us_timeofday(&now, &now_usec); if (retval == 0) { krb5_int32 usec_difference = now_usec-last_usec; krb5_data data; if(last_os_random == 0) last_os_random = now; /* Grab random data from OS every hour*/ if(now-last_os_random >= 60*60) { krb5_c_random_os_entropy(kdc_context, 0, NULL); last_os_random = now; } data.length = sizeof(krb5_int32); data.data = (void *) &usec_difference; krb5_c_random_add_entropy(kdc_context, KRB5_C_RANDSOURCE_TIMING, &data); last_usec = now_usec; } /* try TGS_REQ first; they are more common! */ if (krb5_is_tgs_req(pkt)) { retval = process_tgs_req(pkt, from, response); } else if (krb5_is_as_req(pkt)) { if (!(retval = decode_krb5_as_req(pkt, &as_req))) { /* * setup_server_realm() sets up the global realm-specific data * pointer. * process_as_req frees the request if it is called */ if (!(retval = setup_server_realm(as_req->server))) { retval = process_as_req(as_req, pkt, from, response); } else krb5_free_kdc_req(kdc_context, as_req); } } else retval = KRB5KRB_AP_ERR_MSG_TYPE; #ifndef NOCACHE /* put the response into the lookaside buffer */ if (!retval && *response != NULL) kdc_insert_lookaside(pkt, *response); #endif return retval; }
static krb5_error_code init_common (krb5_context *context, krb5_boolean secure, krb5_boolean kdc) { krb5_context ctx = 0; krb5_error_code retval; struct { krb5_int32 now, now_usec; long pid; } seed_data; krb5_data seed; int tmp; /* Verify some assumptions. If the assumptions hold and the compiler is optimizing, this should result in no code being executed. If we're guessing "unsigned long long" instead of using uint64_t, the possibility does exist that we're wrong. */ { krb5_ui_8 i64; assert(sizeof(i64) == 8); i64 = 0, i64--, i64 >>= 62; assert(i64 == 3); i64 = 1, i64 <<= 31, i64 <<= 31, i64 <<= 1; assert(i64 != 0); i64 <<= 1; assert(i64 == 0); } retval = krb5int_initialize_library(); if (retval) return retval; #if (defined(_WIN32)) /* * Load the krbcc32.dll if necessary. We do this here so that * we know to use API: later on during initialization. * The context being NULL is ok. */ krb5_win_ccdll_load(ctx); /* * krb5_vercheck() is defined in win_glue.c, and this is * where we handle the timebomb and version server checks. */ retval = krb5_vercheck(); if (retval) return retval; #endif *context = 0; ctx = calloc(1, sizeof(struct _krb5_context)); if (!ctx) return ENOMEM; ctx->magic = KV5M_CONTEXT; ctx->profile_secure = secure; /* Set the default encryption types, possible defined in krb5/conf */ if ((retval = krb5_set_default_in_tkt_ktypes(ctx, NULL))) goto cleanup; if ((retval = krb5_set_default_tgs_ktypes(ctx, NULL))) goto cleanup; if ((retval = krb5_os_init_context(ctx, kdc))) goto cleanup; /* initialize the prng (not well, but passable) */ { static pid_t done_seeding = 0; static pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER; int success = 0; pthread_mutex_lock(&m); if (done_seeding != getpid()) { retval = krb5_c_random_os_entropy( ctx, 0, &success); if (retval == 0 && success) done_seeding = getpid(); } pthread_mutex_unlock(&m); if (retval) goto cleanup; } if ((retval = krb5_crypto_us_timeofday(&seed_data.now, &seed_data.now_usec))) goto cleanup; seed_data.pid = getpid (); seed.length = sizeof(seed_data); seed.data = (char *) &seed_data; if ((retval = krb5_c_random_add_entropy(ctx, KRB5_C_RANDSOURCE_TIMING, &seed))) goto cleanup; ctx->default_realm = 0; profile_get_integer(ctx->profile, "libdefaults", "clockskew", 0, 5 * 60, &tmp); ctx->clockskew = tmp; #if 0 /* Default ticket lifetime is currently not supported */ profile_get_integer(ctx->profile, "libdefaults", "tkt_lifetime", 0, 10 * 60 * 60, &tmp); ctx->tkt_lifetime = tmp; #endif /* DCE 1.1 and below only support CKSUMTYPE_RSA_MD4 (2) */ /* DCE add kdc_req_checksum_type = 2 to krb5.conf */ profile_get_integer(ctx->profile, "libdefaults", "kdc_req_checksum_type", 0, CKSUMTYPE_RSA_MD5, &tmp); ctx->kdc_req_sumtype = tmp; profile_get_integer(ctx->profile, "libdefaults", "ap_req_checksum_type", 0, CKSUMTYPE_RSA_MD5, &tmp); ctx->default_ap_req_sumtype = tmp; profile_get_integer(ctx->profile, "libdefaults", "safe_checksum_type", 0, CKSUMTYPE_RSA_MD5_DES, &tmp); ctx->default_safe_sumtype = tmp; profile_get_integer(ctx->profile, "libdefaults", "kdc_default_options", 0, KDC_OPT_RENEWABLE_OK, &tmp); ctx->kdc_default_options = tmp; #define DEFAULT_KDC_TIMESYNC 1 profile_get_integer(ctx->profile, "libdefaults", "kdc_timesync", 0, DEFAULT_KDC_TIMESYNC, &tmp); ctx->library_options = tmp ? KRB5_LIBOPT_SYNC_KDCTIME : 0; /* * We use a default file credentials cache of 3. See * lib/krb5/krb/ccache/file/fcc.h for a description of the * credentials cache types. * * Note: DCE 1.0.3a only supports a cache type of 1 * DCE 1.1 supports a cache type of 2. */ #define DEFAULT_CCACHE_TYPE 4 profile_get_integer(ctx->profile, "libdefaults", "ccache_type", 0, DEFAULT_CCACHE_TYPE, &tmp); ctx->fcc_default_format = tmp + 0x0500; ctx->prompt_types = 0; ctx->use_conf_ktypes = 0; ctx->udp_pref_limit = -1; *context = ctx; return 0; cleanup: krb5_free_context(ctx); return retval; }
/* * Initialize a realm control structure from the alternate profile or from * the specified defaults. * * After we're complete here, the essence of the realm is embodied in the * realm data and we should be all set to begin operation for that realm. */ static krb5_error_code init_realm(kdc_realm_t * rdp, krb5_pointer aprof, char *realm, char *def_mpname, krb5_enctype def_enctype, char *def_udp_listen, char *def_tcp_listen, krb5_boolean def_manual, krb5_boolean def_restrict_anon, char **db_args, char *no_referral, char *hostbased) { krb5_error_code kret; krb5_boolean manual; int kdb_open_flags; char *svalue = NULL; const char *hierarchy[4]; krb5_kvno mkvno = IGNORE_VNO; memset(rdp, 0, sizeof(kdc_realm_t)); if (!realm) { kret = EINVAL; goto whoops; } hierarchy[0] = KRB5_CONF_REALMS; hierarchy[1] = realm; hierarchy[3] = NULL; rdp->realm_name = strdup(realm); if (rdp->realm_name == NULL) { kret = ENOMEM; goto whoops; } kret = krb5int_init_context_kdc(&rdp->realm_context); if (kret) { kdc_err(NULL, kret, _("while getting context for realm %s"), realm); goto whoops; } if (time_offset != 0) (void)krb5_set_time_offsets(rdp->realm_context, time_offset, 0); /* Handle master key name */ hierarchy[2] = KRB5_CONF_MASTER_KEY_NAME; if (krb5_aprof_get_string(aprof, hierarchy, TRUE, &rdp->realm_mpname)) { rdp->realm_mpname = (def_mpname) ? strdup(def_mpname) : strdup(KRB5_KDB_M_NAME); } if (!rdp->realm_mpname) { kret = ENOMEM; goto whoops; } /* Handle KDC addresses/ports */ hierarchy[2] = KRB5_CONF_KDC_LISTEN; if (krb5_aprof_get_string(aprof, hierarchy, TRUE, &rdp->realm_listen)) { /* Try the old kdc_ports configuration option. */ hierarchy[2] = KRB5_CONF_KDC_PORTS; if (krb5_aprof_get_string(aprof, hierarchy, TRUE, &rdp->realm_listen)) rdp->realm_listen = strdup(def_udp_listen); } if (!rdp->realm_listen) { kret = ENOMEM; goto whoops; } hierarchy[2] = KRB5_CONF_KDC_TCP_LISTEN; if (krb5_aprof_get_string(aprof, hierarchy, TRUE, &rdp->realm_tcp_listen)) { /* Try the old kdc_tcp_ports configuration option. */ hierarchy[2] = KRB5_CONF_KDC_TCP_PORTS; if (krb5_aprof_get_string(aprof, hierarchy, TRUE, &rdp->realm_tcp_listen)) rdp->realm_tcp_listen = strdup(def_tcp_listen); } if (!rdp->realm_tcp_listen) { kret = ENOMEM; goto whoops; } /* Handle stash file */ hierarchy[2] = KRB5_CONF_KEY_STASH_FILE; if (krb5_aprof_get_string(aprof, hierarchy, TRUE, &rdp->realm_stash)) manual = def_manual; else manual = FALSE; hierarchy[2] = KRB5_CONF_RESTRICT_ANONYMOUS_TO_TGT; if (krb5_aprof_get_boolean(aprof, hierarchy, TRUE, &rdp->realm_restrict_anon)) rdp->realm_restrict_anon = def_restrict_anon; /* Handle master key type */ hierarchy[2] = KRB5_CONF_MASTER_KEY_TYPE; if (krb5_aprof_get_string(aprof, hierarchy, TRUE, &svalue) || krb5_string_to_enctype(svalue, &rdp->realm_mkey.enctype)) rdp->realm_mkey.enctype = manual ? def_enctype : ENCTYPE_UNKNOWN; free(svalue); svalue = NULL; /* Handle reject-bad-transit flag */ hierarchy[2] = KRB5_CONF_REJECT_BAD_TRANSIT; if (krb5_aprof_get_boolean(aprof, hierarchy, TRUE, &rdp->realm_reject_bad_transit)) rdp->realm_reject_bad_transit = TRUE; /* Handle assume des-cbc-crc is supported for session keys */ hierarchy[2] = KRB5_CONF_DES_CRC_SESSION_SUPPORTED; if (krb5_aprof_get_boolean(aprof, hierarchy, TRUE, &rdp->realm_assume_des_crc_sess)) rdp->realm_assume_des_crc_sess = TRUE; /* Handle ticket maximum life */ hierarchy[2] = KRB5_CONF_MAX_LIFE; if (krb5_aprof_get_deltat(aprof, hierarchy, TRUE, &rdp->realm_maxlife)) rdp->realm_maxlife = KRB5_KDB_MAX_LIFE; /* Handle ticket renewable maximum life */ hierarchy[2] = KRB5_CONF_MAX_RENEWABLE_LIFE; if (krb5_aprof_get_deltat(aprof, hierarchy, TRUE, &rdp->realm_maxrlife)) rdp->realm_maxrlife = KRB5_KDB_MAX_RLIFE; /* Handle KDC referrals */ hierarchy[2] = KRB5_CONF_NO_HOST_REFERRAL; (void)krb5_aprof_get_string_all(aprof, hierarchy, &svalue); kret = combine(no_referral, svalue, &rdp->realm_no_referral); if (kret) goto whoops; free(svalue); svalue = NULL; hierarchy[2] = KRB5_CONF_HOST_BASED_SERVICES; (void)krb5_aprof_get_string_all(aprof, hierarchy, &svalue); kret = combine(hostbased, svalue, &rdp->realm_hostbased); if (kret) goto whoops; free(svalue); svalue = NULL; /* * We've got our parameters, now go and setup our realm context. */ /* Set the default realm of this context */ if ((kret = krb5_set_default_realm(rdp->realm_context, realm))) { kdc_err(rdp->realm_context, kret, _("while setting default realm to %s"), realm); goto whoops; } /* first open the database before doing anything */ kdb_open_flags = KRB5_KDB_OPEN_RW | KRB5_KDB_SRV_TYPE_KDC; if ((kret = krb5_db_open(rdp->realm_context, db_args, kdb_open_flags))) { kdc_err(rdp->realm_context, kret, _("while initializing database for realm %s"), realm); goto whoops; } /* Assemble and parse the master key name */ if ((kret = krb5_db_setup_mkey_name(rdp->realm_context, rdp->realm_mpname, rdp->realm_name, (char **) NULL, &rdp->realm_mprinc))) { kdc_err(rdp->realm_context, kret, _("while setting up master key name %s for realm %s"), rdp->realm_mpname, realm); goto whoops; } /* * Get the master key (note, may not be the most current mkey). */ if ((kret = krb5_db_fetch_mkey(rdp->realm_context, rdp->realm_mprinc, rdp->realm_mkey.enctype, manual, FALSE, rdp->realm_stash, &mkvno, NULL, &rdp->realm_mkey))) { kdc_err(rdp->realm_context, kret, _("while fetching master key %s for realm %s"), rdp->realm_mpname, realm); goto whoops; } if ((kret = krb5_db_fetch_mkey_list(rdp->realm_context, rdp->realm_mprinc, &rdp->realm_mkey))) { kdc_err(rdp->realm_context, kret, _("while fetching master keys list for realm %s"), realm); goto whoops; } /* Set up the keytab */ if ((kret = krb5_ktkdb_resolve(rdp->realm_context, NULL, &rdp->realm_keytab))) { kdc_err(rdp->realm_context, kret, _("while resolving kdb keytab for realm %s"), realm); goto whoops; } /* Preformat the TGS name */ if ((kret = krb5_build_principal(rdp->realm_context, &rdp->realm_tgsprinc, strlen(realm), realm, KRB5_TGS_NAME, realm, (char *) NULL))) { kdc_err(rdp->realm_context, kret, _("while building TGS name for realm %s"), realm); goto whoops; } if (!rkey_init_done) { krb5_data seed; /* * If all that worked, then initialize the random key * generators. */ seed.length = rdp->realm_mkey.length; seed.data = (char *)rdp->realm_mkey.contents; if ((kret = krb5_c_random_add_entropy(rdp->realm_context, KRB5_C_RANDSOURCE_TRUSTEDPARTY, &seed))) goto whoops; rkey_init_done = 1; } whoops: /* * If we choked, then clean up any dirt we may have dropped on the floor. */ if (kret) { finish_realm(rdp); } return(kret); }
krb5_error_code KRB5_CALLCONV krb5_sendauth(krb5_context context, krb5_auth_context *auth_context, krb5_pointer fd, char *appl_version, krb5_principal client, krb5_principal server, krb5_flags ap_req_options, krb5_data *in_data, krb5_creds *in_creds, krb5_ccache ccache, krb5_error **error, krb5_ap_rep_enc_part **rep_result, krb5_creds **out_creds) { krb5_octet result; krb5_creds creds; krb5_creds * credsp = NULL; krb5_creds * credspout = NULL; krb5_error_code retval = 0; krb5_data inbuf, outbuf[2]; int len; krb5_ccache use_ccache = 0; if (error) *error = 0; /* * First, send over the length of the sendauth version string; * then, we send over the sendauth version. Next, we send * over the length of the application version strings followed * by the string itself. */ outbuf[0].length = strlen(sendauth_version) + 1; outbuf[0].data = (char *) sendauth_version; outbuf[1].length = strlen(appl_version) + 1; outbuf[1].data = appl_version; if ((retval = krb5int_write_messages(context, fd, outbuf, 2))) return(retval); /* * Now, read back a byte: 0 means no error, 1 means bad sendauth * version, 2 means bad application version */ if ((len = krb5_net_read(context, *((int *) fd), (char *)&result, 1)) != 1) return((len < 0) ? errno : ECONNABORTED); if (result == 1) return(KRB5_SENDAUTH_BADAUTHVERS); else if (result == 2) return(KRB5_SENDAUTH_BADAPPLVERS); else if (result != 0) return(KRB5_SENDAUTH_BADRESPONSE); /* * We're finished with the initial negotiations; let's get and * send over the authentication header. (The AP_REQ message) */ /* * If no credentials were provided, try getting it from the * credentials cache. */ memset(&creds, 0, sizeof(creds)); /* * See if we need to access the credentials cache */ if (!in_creds || !in_creds->ticket.length) { if (ccache) use_ccache = ccache; else if ((retval = krb5int_cc_default(context, &use_ccache))) goto error_return; } if (!in_creds) { if ((retval = krb5_copy_principal(context, server, &creds.server))) goto error_return; if (client) retval = krb5_copy_principal(context, client, &creds.client); else retval = krb5_cc_get_principal(context, use_ccache, &creds.client); if (retval) { krb5_free_principal(context, creds.server); goto error_return; } /* creds.times.endtime = 0; -- memset 0 takes care of this zero means "as long as possible" */ /* creds.keyblock.enctype = 0; -- as well as this. zero means no session enctype preference */ in_creds = &creds; } if (!in_creds->ticket.length) { if ((retval = krb5_get_credentials(context, 0, use_ccache, in_creds, &credsp))) goto error_return; credspout = credsp; } else { credsp = in_creds; } if (ap_req_options & AP_OPTS_USE_SUBKEY) { /* Provide some more fodder for random number code. This isn't strong cryptographically; the point here is not to guarantee randomness, but to make it less likely that multiple sessions could pick the same subkey. */ char rnd_data[1024]; GETPEERNAME_ARG3_TYPE len2; krb5_data d; d.length = sizeof (rnd_data); d.data = rnd_data; len2 = sizeof (rnd_data); if (getpeername (*(int*)fd, (GETPEERNAME_ARG2_TYPE *) rnd_data, &len2) == 0) { d.length = len2; (void) krb5_c_random_add_entropy (context, KRB5_C_RANDSOURCE_EXTERNAL_PROTOCOL, &d); } len2 = sizeof (rnd_data); if (getsockname (*(int*)fd, (GETSOCKNAME_ARG2_TYPE *) rnd_data, &len2) == 0) { d.length = len2; (void) krb5_c_random_add_entropy (context, KRB5_C_RANDSOURCE_EXTERNAL_PROTOCOL, &d); } } outbuf[0].data = NULL; /* Coverity is confused otherwise */ if ((retval = krb5_mk_req_extended(context, auth_context, ap_req_options, in_data, credsp, &outbuf[0]))) goto error_return; /* * First write the length of the AP_REQ message, then write * the message itself. */ retval = krb5_write_message(context, fd, &outbuf[0]); free(outbuf[0].data); if (retval) goto error_return; /* * Now, read back a message. If it was a null message (the * length was zero) then there was no error. If not, we the * authentication was rejected, and we need to return the * error structure. */ if ((retval = krb5_read_message(context, fd, &inbuf))) goto error_return; if (inbuf.length) { if (error) { if ((retval = krb5_rd_error(context, &inbuf, error))) { free(inbuf.data); goto error_return; } } retval = KRB5_SENDAUTH_REJECTED; free(inbuf.data); goto error_return; } /* * If we asked for mutual authentication, we should now get a * length field, followed by a AP_REP message */ if ((ap_req_options & AP_OPTS_MUTUAL_REQUIRED)) { krb5_ap_rep_enc_part *repl = 0; if ((retval = krb5_read_message(context, fd, &inbuf))) goto error_return; if ((retval = krb5_rd_rep(context, *auth_context, &inbuf, &repl))) { if (repl) krb5_free_ap_rep_enc_part(context, repl); free(inbuf.data); goto error_return; } free(inbuf.data); /* * If the user wants to look at the AP_REP message, * copy it for him */ if (rep_result) *rep_result = repl; else krb5_free_ap_rep_enc_part(context, repl); } retval = 0; /* Normal return */ if (out_creds) { *out_creds = credsp; credspout = NULL; } error_return: krb5_free_cred_contents(context, &creds); if (credspout != NULL) krb5_free_creds(context, credspout); if (!ccache && use_ccache) krb5_cc_close(context, use_ccache); return(retval); }
/* * Initialize a realm control structure from the alternate profile or from * the specified defaults. * * After we're complete here, the essence of the realm is embodied in the * realm data and we should be all set to begin operation for that realm. */ static krb5_error_code init_realm(krb5_context kcontext, char *progname, kdc_realm_t *rdp, char *realm, char *def_mpname, krb5_enctype def_enctype, char *def_udp_ports, char *def_tcp_ports, krb5_boolean def_manual, char **db_args) { krb5_error_code kret; krb5_boolean manual; krb5_realm_params *rparams; memset((char *) rdp, 0, sizeof(kdc_realm_t)); if (!realm) { kret = EINVAL; goto whoops; } rdp->realm_name = realm; kret = krb5int_init_context_kdc(&rdp->realm_context); if (kret) { com_err(progname, kret, gettext("while getting context for realm %s"), realm); goto whoops; } /* * Solaris Kerberos: * Set the current context to that of the realm being init'ed */ krb5_klog_set_context(rdp->realm_context); kret = krb5_read_realm_params(rdp->realm_context, rdp->realm_name, &rparams); if (kret) { com_err(progname, kret, gettext("while reading realm parameters")); goto whoops; } /* Handle profile file name */ if (rparams && rparams->realm_profile) rdp->realm_profile = strdup(rparams->realm_profile); /* Handle master key name */ if (rparams && rparams->realm_mkey_name) rdp->realm_mpname = strdup(rparams->realm_mkey_name); else rdp->realm_mpname = (def_mpname) ? strdup(def_mpname) : strdup(KRB5_KDB_M_NAME); /* Handle KDC ports */ if (rparams && rparams->realm_kdc_ports) rdp->realm_ports = strdup(rparams->realm_kdc_ports); else rdp->realm_ports = strdup(def_udp_ports); if (rparams && rparams->realm_kdc_tcp_ports) rdp->realm_tcp_ports = strdup(rparams->realm_kdc_tcp_ports); else rdp->realm_tcp_ports = strdup(def_tcp_ports); /* Handle stash file */ if (rparams && rparams->realm_stash_file) { rdp->realm_stash = strdup(rparams->realm_stash_file); manual = FALSE; } else manual = def_manual; /* Handle master key type */ if (rparams && rparams->realm_enctype_valid) rdp->realm_mkey.enctype = (krb5_enctype) rparams->realm_enctype; else rdp->realm_mkey.enctype = manual ? def_enctype : ENCTYPE_UNKNOWN; /* Handle reject-bad-transit flag */ if (rparams && rparams->realm_reject_bad_transit_valid) rdp->realm_reject_bad_transit = rparams->realm_reject_bad_transit; else rdp->realm_reject_bad_transit = 1; /* Handle ticket maximum life */ rdp->realm_maxlife = (rparams && rparams->realm_max_life_valid) ? rparams->realm_max_life : KRB5_KDB_MAX_LIFE; /* Handle ticket renewable maximum life */ rdp->realm_maxrlife = (rparams && rparams->realm_max_rlife_valid) ? rparams->realm_max_rlife : KRB5_KDB_MAX_RLIFE; if (rparams) krb5_free_realm_params(rdp->realm_context, rparams); /* * We've got our parameters, now go and setup our realm context. */ /* Set the default realm of this context */ if ((kret = krb5_set_default_realm(rdp->realm_context, realm))) { com_err(progname, kret, gettext("while setting default realm to %s"), realm); goto whoops; } /* first open the database before doing anything */ #ifdef KRBCONF_KDC_MODIFIES_KDB if ((kret = krb5_db_open(rdp->realm_context, db_args, KRB5_KDB_OPEN_RW | KRB5_KDB_SRV_TYPE_KDC))) { #else if ((kret = krb5_db_open(rdp->realm_context, db_args, KRB5_KDB_OPEN_RO | KRB5_KDB_SRV_TYPE_KDC))) { #endif /* * Solaris Kerberos: * Make sure that error messages are printed using gettext */ com_err(progname, kret, gettext("while initializing database for realm %s"), realm); goto whoops; } /* Assemble and parse the master key name */ if ((kret = krb5_db_setup_mkey_name(rdp->realm_context, rdp->realm_mpname, rdp->realm_name, (char **) NULL, &rdp->realm_mprinc))) { com_err(progname, kret, gettext("while setting up master key name %s for realm %s"), rdp->realm_mpname, realm); goto whoops; } /* * Get the master key. */ if ((kret = krb5_db_fetch_mkey(rdp->realm_context, rdp->realm_mprinc, rdp->realm_mkey.enctype, manual, FALSE, rdp->realm_stash, 0, &rdp->realm_mkey))) { com_err(progname, kret, gettext("while fetching master key %s for realm %s"), rdp->realm_mpname, realm); goto whoops; } /* Verify the master key */ if ((kret = krb5_db_verify_master_key(rdp->realm_context, rdp->realm_mprinc, &rdp->realm_mkey))) { com_err(progname, kret, gettext("while verifying master key for realm %s"), realm); goto whoops; } if ((kret = krb5_db_set_mkey(rdp->realm_context, &rdp->realm_mkey))) { com_err(progname, kret, gettext("while processing master key for realm %s"), realm); goto whoops; } /* Set up the keytab */ if ((kret = krb5_ktkdb_resolve(rdp->realm_context, NULL, &rdp->realm_keytab))) { com_err(progname, kret, gettext("while resolving kdb keytab for realm %s"), realm); goto whoops; } /* Preformat the TGS name */ if ((kret = krb5_build_principal(rdp->realm_context, &rdp->realm_tgsprinc, strlen(realm), realm, KRB5_TGS_NAME, realm, (char *) NULL))) { com_err(progname, kret, gettext("while building TGS name for realm %s"), realm); goto whoops; } if (!rkey_init_done) { krb5_data seed; #ifdef KRB5_KRB4_COMPAT krb5_keyblock temp_key; #endif /* * If all that worked, then initialize the random key * generators. */ seed.length = rdp->realm_mkey.length; seed.data = (char *)rdp->realm_mkey.contents; /* SUNW14resync - XXX */ #if 0 if ((kret = krb5_c_random_add_entropy(rdp->realm_context, KRB5_C_RANDSOURCE_TRUSTEDPARTY, &seed))) goto whoops; #endif #ifdef KRB5_KRB4_COMPAT if ((kret = krb5_c_make_random_key(rdp->realm_context, ENCTYPE_DES_CBC_CRC, &temp_key))) { com_err(progname, kret, "while initializing V4 random key generator"); goto whoops; } (void) des_init_random_number_generator(temp_key.contents); krb5_free_keyblock_contents(rdp->realm_context, &temp_key); #endif rkey_init_done = 1; } whoops: /* * If we choked, then clean up any dirt we may have dropped on the floor. */ if (kret) { finish_realm(rdp); } /* * Solaris Kerberos: * Set the current context back to the general context */ krb5_klog_set_context(kcontext); return(kret); } krb5_sigtype request_exit(int signo) { signal_requests_exit = 1; #ifdef POSIX_SIGTYPE return; #else return(0); #endif }
void dispatch(void *cb, struct sockaddr *local_saddr, const krb5_fulladdr *from, krb5_data *pkt, int is_tcp, verto_ctx *vctx, loop_respond_fn respond, void *arg) { krb5_error_code retval; krb5_kdc_req *as_req; krb5_int32 now, now_usec; krb5_data *response = NULL; struct dispatch_state *state; state = malloc(sizeof(*state)); if (!state) { (*respond)(arg, ENOMEM, NULL); return; } state->respond = respond; state->arg = arg; state->request = pkt; state->is_tcp = is_tcp; /* decode incoming packet, and dispatch */ #ifndef NOCACHE /* try the replay lookaside buffer */ if (kdc_check_lookaside(pkt, &response)) { /* a hit! */ const char *name = 0; char buf[46]; if (!response || is_tcp != 0 || response->length <= max_dgram_reply_size) { name = inet_ntop (ADDRTYPE2FAMILY (from->address->addrtype), from->address->contents, buf, sizeof (buf)); if (name == 0) name = "[unknown address type]"; if (response) krb5_klog_syslog(LOG_INFO, "DISPATCH: repeated (retransmitted?) request " "from %s, resending previous response", name); else krb5_klog_syslog(LOG_INFO, "DISPATCH: repeated (retransmitted?) request " "from %s during request processing, dropping " "repeated request", name); } finish_dispatch(state, response ? 0 : KRB5KDC_ERR_DISCARD, response); return; } /* Insert a NULL entry into the lookaside to indicate that this request * is currently being processed. */ kdc_insert_lookaside(pkt, NULL); #endif retval = krb5_crypto_us_timeofday(&now, &now_usec); if (retval == 0) { krb5_int32 usec_difference = now_usec-last_usec; krb5_data data; if(last_os_random == 0) last_os_random = now; /* Grab random data from OS every hour*/ if(now-last_os_random >= 60*60) { krb5_c_random_os_entropy(kdc_context, 0, NULL); last_os_random = now; } data.length = sizeof(krb5_int32); data.data = (void *) &usec_difference; krb5_c_random_add_entropy(kdc_context, KRB5_C_RANDSOURCE_TIMING, &data); last_usec = now_usec; } /* try TGS_REQ first; they are more common! */ if (krb5_is_tgs_req(pkt)) { retval = process_tgs_req(pkt, from, &response); } else if (krb5_is_as_req(pkt)) { if (!(retval = decode_krb5_as_req(pkt, &as_req))) { /* * setup_server_realm() sets up the global realm-specific data * pointer. * process_as_req frees the request if it is called */ if (!(retval = setup_server_realm(as_req->server))) { process_as_req(as_req, pkt, from, vctx, finish_dispatch, state); return; } else krb5_free_kdc_req(kdc_context, as_req); } } else retval = KRB5KRB_AP_ERR_MSG_TYPE; finish_dispatch(state, retval, response); }