/* Replaces krb5_build_principal_ext(), with varargs length == 2 (svc, host), ** because I dont't know how to stub varargs. ** Returns krb5_error_code == ENOMEM on alloc error, otherwise ** passes back newly constructed principal, which should be freed by caller. */ krb5_error_code kssl_build_principal_2( /* UPDATE */ krb5_context context, /* OUT */ krb5_principal *princ, /* IN */ int rlen, const char *realm, /* IN */ int slen, const char *svc, /* IN */ int hlen, const char *host) { krb5_data *p_data = NULL; krb5_principal new_p = NULL; char *new_r = NULL; if ((p_data = (krb5_data *)calloc(2, sizeof(krb5_data))) == NULL || (new_p = (krb5_principal)calloc(1, sizeof(krb5_principal_data))) == NULL) goto err; new_p->length = 2; new_p->data = p_data; if ((new_r = calloc(1, rlen + 1)) == NULL) goto err; memcpy(new_r, realm, rlen); krb5_princ_set_realm_length(context, new_p, rlen); krb5_princ_set_realm_data(context, new_p, new_r); if ((new_p->data[0].data = calloc(1, slen + 1)) == NULL) goto err; memcpy(new_p->data[0].data, svc, slen); new_p->data[0].length = slen; if ((new_p->data[1].data = calloc(1, hlen + 1)) == NULL) goto err; memcpy(new_p->data[1].data, host, hlen); new_p->data[1].length = hlen; krb5_princ_type(context, new_p) = KRB5_NT_UNKNOWN; *princ = new_p; return 0; err: if (new_p && new_p[0].data) free(new_p[0].data); if (new_p && new_p[1].data) free(new_p[1].data); if (new_p) free(new_p); if (new_r) free(new_r); return ENOMEM; }
/* * Check whether the request satisfies the conditions for generating a referral * TGT. The caller checks whether the hostname component looks like a FQDN. */ static krb5_boolean is_referral_req(kdc_realm_t *kdc_active_realm, krb5_kdc_req *request) { krb5_boolean ret = FALSE; char *stype = NULL; char *ref_services = kdc_active_realm->realm_host_based_services; char *nonref_services = kdc_active_realm->realm_no_host_referral; if (!(request->kdc_options & KDC_OPT_CANONICALIZE)) return FALSE; if (request->kdc_options & KDC_OPT_ENC_TKT_IN_SKEY) return FALSE; if (krb5_princ_size(kdc_context, request->server) != 2) return FALSE; stype = data2string(krb5_princ_component(kdc_context, request->server, 0)); if (stype == NULL) return FALSE; switch (krb5_princ_type(kdc_context, request->server)) { case KRB5_NT_UNKNOWN: /* Allow referrals for NT-UNKNOWN principals, if configured. */ if (kdc_active_realm->realm_host_based_services != NULL) { if (!krb5_match_config_pattern(ref_services, stype) && !krb5_match_config_pattern(ref_services, KRB5_CONF_ASTERISK)) goto cleanup; } else goto cleanup; /* FALLTHROUGH */ case KRB5_NT_SRV_HST: case KRB5_NT_SRV_INST: /* Deny referrals for specific service types, if configured. */ if (kdc_active_realm->realm_no_host_referral != NULL) { if (krb5_match_config_pattern(nonref_services, stype)) goto cleanup; if (krb5_match_config_pattern(nonref_services, KRB5_CONF_ASTERISK)) goto cleanup; } ret = TRUE; break; default: goto cleanup; } cleanup: free(stype); return ret; }
/* * Check whether the request satisfies the conditions for generating a referral * TGT. The caller checks whether the hostname component looks like a FQDN. */ static krb5_boolean is_referral_req(kdc_realm_t *kdc_active_realm, krb5_kdc_req *request) { krb5_boolean ret = FALSE; char *stype = NULL; char *hostbased = kdc_active_realm->realm_hostbased; char *no_referral = kdc_active_realm->realm_no_referral; if (!(request->kdc_options & KDC_OPT_CANONICALIZE)) return FALSE; if (request->kdc_options & KDC_OPT_ENC_TKT_IN_SKEY) return FALSE; if (krb5_princ_size(kdc_context, request->server) != 2) return FALSE; stype = data2string(krb5_princ_component(kdc_context, request->server, 0)); if (stype == NULL) return FALSE; switch (krb5_princ_type(kdc_context, request->server)) { case KRB5_NT_UNKNOWN: /* Allow referrals for NT-UNKNOWN principals, if configured. */ if (!in_list(hostbased, stype) && !in_list(hostbased, "*")) goto cleanup; /* FALLTHROUGH */ case KRB5_NT_SRV_HST: case KRB5_NT_SRV_INST: /* Deny referrals for specific service types, if configured. */ if (in_list(no_referral, stype) || in_list(no_referral, "*")) goto cleanup; ret = TRUE; break; default: goto cleanup; } cleanup: free(stype); return ret; }
/* Get a TGT for use at the remote host */ krb5_error_code KRB5_CALLCONV krb5_fwd_tgt_creds(krb5_context context, krb5_auth_context auth_context, char *rhost, krb5_principal client, krb5_principal server, krb5_ccache cc, int forwardable, krb5_data *outbuf) /* Should forwarded TGT also be forwardable? */ { krb5_replay_data replaydata; krb5_data * scratch = 0; krb5_address **addrs = NULL; krb5_error_code retval; krb5_creds creds, tgt; krb5_creds *pcreds; krb5_flags kdcoptions; int close_cc = 0; int free_rhost = 0; krb5_enctype enctype = 0; krb5_keyblock *session_key; krb5_boolean old_use_conf_ktypes = context->use_conf_ktypes; memset((char *)&creds, 0, sizeof(creds)); memset((char *)&tgt, 0, sizeof(creds)); if (cc == 0) { if ((retval = krb5int_cc_default(context, &cc))) goto errout; close_cc = 1; } retval = krb5_auth_con_getkey (context, auth_context, &session_key); if (retval) goto errout; if (session_key) { enctype = session_key->enctype; krb5_free_keyblock (context, session_key); session_key = NULL; } else if (server) { /* must server be non-NULL when rhost is given? */ /* Try getting credentials to see what the remote side supports. Not bulletproof, just a heuristic. */ krb5_creds in, *out = 0; memset (&in, 0, sizeof(in)); retval = krb5_copy_principal (context, server, &in.server); if (retval) goto punt; retval = krb5_copy_principal (context, client, &in.client); if (retval) goto punt; retval = krb5_get_credentials (context, 0, cc, &in, &out); if (retval) goto punt; /* Got the credentials. Okay, now record the enctype and throw them away. */ enctype = out->keyblock.enctype; krb5_free_creds (context, out); punt: krb5_free_cred_contents (context, &in); } if ((retval = krb5_copy_principal(context, client, &creds.client))) goto errout; if ((retval = krb5_build_principal_ext(context, &creds.server, client->realm.length, client->realm.data, KRB5_TGS_NAME_SIZE, KRB5_TGS_NAME, client->realm.length, client->realm.data, 0))) goto errout; /* fetch tgt directly from cache */ context->use_conf_ktypes = 1; retval = krb5_cc_retrieve_cred (context, cc, KRB5_TC_SUPPORTED_KTYPES, &creds, &tgt); context->use_conf_ktypes = old_use_conf_ktypes; if (retval) goto errout; /* tgt->client must be equal to creds.client */ if (!krb5_principal_compare(context, tgt.client, creds.client)) { /* Solaris Kerberos */ char *r_name = NULL; char *t_name = NULL; krb5_error_code r_err, t_err; t_err = krb5_unparse_name(context, tgt.client, &t_name); r_err = krb5_unparse_name(context, creds.client, &r_name); krb5_set_error_message(context, KRB5_PRINC_NOMATCH, dgettext(TEXT_DOMAIN, "Requested principal and ticket don't match: Requested principal is '%s' and TGT principal is '%s'"), r_err ? "unknown" : r_name, t_err ? "unknown" : t_name); if (r_name) krb5_free_unparsed_name(context, r_name); if (t_name) krb5_free_unparsed_name(context, t_name); retval = KRB5_PRINC_NOMATCH; goto errout; } if (!tgt.ticket.length) { retval = KRB5_NO_TKT_SUPPLIED; goto errout; } if (tgt.addresses && *tgt.addresses) { if (rhost == NULL) { if (krb5_princ_type(context, server) != KRB5_NT_SRV_HST) { retval = KRB5_FWD_BAD_PRINCIPAL; goto errout; } if (krb5_princ_size(context, server) < 2){ retval = KRB5_CC_BADNAME; goto errout; } rhost = malloc(server->data[1].length+1); if (!rhost) { retval = ENOMEM; goto errout; } free_rhost = 1; /* Solaris Kerberos */ (void) memcpy(rhost, server->data[1].data, server->data[1].length); rhost[server->data[1].length] = '\0'; } retval = krb5_os_hostaddr(context, rhost, &addrs); if (retval) goto errout; } creds.keyblock.enctype = enctype; creds.times = tgt.times; creds.times.starttime = 0; kdcoptions = flags2options(tgt.ticket_flags)|KDC_OPT_FORWARDED; if (!forwardable) /* Reset KDC_OPT_FORWARDABLE */ kdcoptions &= ~(KDC_OPT_FORWARDABLE); if ((retval = krb5_get_cred_via_tkt(context, &tgt, kdcoptions, addrs, &creds, &pcreds))) { if (enctype) { creds.keyblock.enctype = 0; if ((retval = krb5_get_cred_via_tkt(context, &tgt, kdcoptions, addrs, &creds, &pcreds))) goto errout; } else goto errout; } retval = krb5_mk_1cred(context, auth_context, pcreds, &scratch, &replaydata); krb5_free_creds(context, pcreds); /* * Solaris Kerberos: changed this logic from the MIT 1.2.1 version to be * more robust. */ if (scratch) { if (retval) krb5_free_data(context, scratch); else { *outbuf = *scratch; krb5_xfree(scratch); } } errout: if (addrs) krb5_free_addresses(context, addrs); /* Solaris Kerberos */ if (close_cc) (void) krb5_cc_close(context, cc); if (free_rhost) free(rhost); krb5_free_cred_contents(context, &creds); krb5_free_cred_contents(context, &tgt); return retval; }
static krb5_int32 prep_reprocess_req(krb5_kdc_req *request, krb5_principal *krbtgt_princ) { krb5_error_code retval = KRB5KRB_AP_ERR_BADMATCH; size_t len = 0; char **realms, **cpp, *temp_buf=NULL; krb5_data *comp1 = NULL, *comp2 = NULL; char *comp1_str = NULL; /* By now we know that server principal name is unknown. * If CANONICALIZE flag is set in the request * If req is not U2U authn. req * the requested server princ. has exactly two components * either * the name type is NT-SRV-HST * or name type is NT-UNKNOWN and * the 1st component is listed in conf file under host_based_services * the 1st component is not in a list in conf under "no_host_referral" * the 2d component looks like fully-qualified domain name (FQDN) * If all of these conditions are satisfied - try mapping the FQDN and * re-process the request as if client had asked for cross-realm TGT. */ if (isflagset(request->kdc_options, KDC_OPT_CANONICALIZE) && !isflagset(request->kdc_options, KDC_OPT_ENC_TKT_IN_SKEY) && krb5_princ_size(kdc_context, request->server) == 2) { comp1 = krb5_princ_component(kdc_context, request->server, 0); comp2 = krb5_princ_component(kdc_context, request->server, 1); comp1_str = calloc(1,comp1->length+1); if (!comp1_str) { retval = ENOMEM; goto cleanup; } strlcpy(comp1_str,comp1->data,comp1->length+1); if ((krb5_princ_type(kdc_context, request->server) == KRB5_NT_SRV_HST || (krb5_princ_type(kdc_context, request->server) == KRB5_NT_UNKNOWN && kdc_active_realm->realm_host_based_services != NULL && (krb5_match_config_pattern(kdc_active_realm->realm_host_based_services, comp1_str) == TRUE || krb5_match_config_pattern(kdc_active_realm->realm_host_based_services, KRB5_CONF_ASTERISK) == TRUE))) && (kdc_active_realm->realm_no_host_referral == NULL || (krb5_match_config_pattern(kdc_active_realm->realm_no_host_referral, KRB5_CONF_ASTERISK) == FALSE && krb5_match_config_pattern(kdc_active_realm->realm_no_host_referral, comp1_str) == FALSE))) { for (len=0; len < comp2->length; len++) { if (comp2->data[len] == '.') break; } if (len == comp2->length) goto cleanup; temp_buf = calloc(1, comp2->length+1); if (!temp_buf){ retval = ENOMEM; goto cleanup; } strlcpy(temp_buf, comp2->data,comp2->length+1); retval = krb5int_get_domain_realm_mapping(kdc_context, temp_buf, &realms); free(temp_buf); if (retval) { /* no match found */ kdc_err(kdc_context, retval, 0); goto cleanup; } if (realms == 0) { retval = KRB5KRB_AP_ERR_BADMATCH; goto cleanup; } if (realms[0] == 0) { free(realms); retval = KRB5KRB_AP_ERR_BADMATCH; goto cleanup; } /* Modify request. * Construct cross-realm tgt : krbtgt/REMOTE_REALM@LOCAL_REALM * and use it as a principal in this req. */ retval = krb5_build_principal(kdc_context, krbtgt_princ, (*request->server).realm.length, (*request->server).realm.data, "krbtgt", realms[0], (char *)0); for (cpp = realms; *cpp; cpp++) free(*cpp); } } cleanup: free(comp1_str); return retval; }
/** * @brief * Get a TGT for use at the remote host. * * @param[in] context - The Kerberos context. * @param[in] auth_context - Authentication context * @param[in] client - Principal to be copied * @param[in] server - Server of type krb5_principal * @param[in] cc - Credential cache handle * @param[out] outbuf - Replay cache data (NULL if not needed) * * @return krb5_error_code * @retval 0 - success * @retval !=0 - failure */ static krb5_error_code fwd_tgt_creds(krb5_context context, krb5_auth_context auth_context, krb5_principal client, krb5_principal server, krb5_ccache cc, krb5_data outbuf) { krb5_replay_data replaydata; krb5_data *scratch = 0; krb5_address **addrs = 0; krb5_flags kdcoptions; krb5_error_code retval; krb5_creds creds, tgt; krb5_creds *pcreds; int free_rhost = 0; char *rhost; memset((char *)&creds, 0, sizeof(creds)); memset((char *)&tgt, 0, sizeof(creds)); if (krb5_princ_type(context, server) != KRB5_NT_SRV_HST) return KRB5_FWD_BAD_PRINCIPAL; if (krb5_princ_size(context, server) < 2) return KRB5_CC_BADNAME; rhost = malloc(server->data[1].length+1); if (!rhost) return ENOMEM; free_rhost = 1; memcpy(rhost, server->data[1].data, server->data[1].length); rhost[server->data[1].length] = '\0'; retval = krb5_os_hostaddr(context, rhost, &addrs); if (retval) goto errout; if ((retval = krb5_copy_principal(context, client, &creds.client))) goto errout; if ((retval = krb5_build_principal_ext(context, &creds.server, client->realm.length, client->realm.data, KRB5_TGS_NAME_SIZE, KRB5_TGS_NAME, client->realm.length, client->realm.data, 0))) goto errout; /* fetch tgt directly from cache */ retval = krb5_cc_retrieve_cred(context, cc, KRB5_TC_SUPPORTED_KTYPES, &creds, &tgt); if (retval) goto errout; /* tgt->client must be equal to creds.client */ if (!krb5_principal_compare(context, tgt.client, creds.client)) { retval = KRB5_PRINC_NOMATCH; goto errout; } if (!tgt.ticket.length) { retval = KRB5_NO_TKT_SUPPLIED; goto errout; } kdcoptions = flags2options(tgt.ticket_flags)| KDC_OPT_FORWARDED; if ((retval = krb5_get_cred_via_tkt(context, &tgt, kdcoptions, addrs, &creds, &pcreds))) goto errout; retval = krb5_mk_1cred(context, auth_context, pcreds, &scratch, &replaydata); krb5_free_creds(context, pcreds); if (retval) { if (scratch) krb5_free_data(context, scratch); } else { *outbuf = *scratch; free(scratch); } errout: if (addrs) krb5_free_addresses(context, addrs); if (free_rhost) free(rhost); krb5_free_cred_contents(context, &creds); krb5_free_cred_contents(context, &tgt); return retval; }
/* * May the fleas of a thousand camels infest the ISO, they who think * that arbitrarily large multi-component names are a Good Thing..... */ static krb5_error_code k5_parse_name(krb5_context context, const char *name, int flags, krb5_principal *nprincipal) { register const char *cp; register char *q; register int i,c,size; int components = 0; const char *parsed_realm = NULL; int fcompsize[FCOMPNUM]; unsigned int realmsize = 0; char *default_realm = NULL; int default_realm_size = 0; char *tmpdata; krb5_principal principal; krb5_error_code retval; unsigned int enterprise = (flags & KRB5_PRINCIPAL_PARSE_ENTERPRISE); int first_at; /* * Pass 1. Find out how many components there are to the name, * and get string sizes for the first FCOMPNUM components. For * enterprise principal names (UPNs), there is only a single * component. */ size = 0; for (i=0,cp = name, first_at = 1; (c = *cp); cp++) { if (c == QUOTECHAR) { cp++; if (!(c = *cp)) /* * QUOTECHAR can't be at the last * character of the name! */ return(KRB5_PARSE_MALFORMED); size++; continue; } else if (c == COMPONENT_SEP && !enterprise) { if (parsed_realm) /* * Shouldn't see a component separator * after we've parsed out the realm name! */ return(KRB5_PARSE_MALFORMED); if (i < FCOMPNUM) { fcompsize[i] = size; } size = 0; i++; } else if (c == REALM_SEP && (!enterprise || !first_at)) { if (parsed_realm) /* * Multiple realm separaters * not allowed; zero-length realms are. */ return(KRB5_PARSE_MALFORMED); parsed_realm = cp + 1; if (i < FCOMPNUM) { fcompsize[i] = size; } size = 0; } else { if (c == REALM_SEP && enterprise && first_at) first_at = 0; size++; } } if (parsed_realm != NULL) realmsize = size; else if (i < FCOMPNUM) fcompsize[i] = size; components = i + 1; /* * Now, we allocate the principal structure and all of its * component pieces */ principal = (krb5_principal)malloc(sizeof(krb5_principal_data)); if (principal == NULL) { return(ENOMEM); } principal->data = (krb5_data *) malloc(sizeof(krb5_data) * components); if (principal->data == NULL) { krb5_xfree((char *)principal); return ENOMEM; } principal->length = components; /* * If a realm was not found, then use the default realm, unless * KRB5_PRINCIPAL_PARSE_NO_REALM was specified in which case the * realm will be empty. */ if (!parsed_realm) { if (flags & KRB5_PRINCIPAL_PARSE_REQUIRE_REALM) { krb5_set_error_message(context, KRB5_PARSE_MALFORMED, "Principal %s is missing required realm", name); krb5_xfree(principal->data); krb5_xfree(principal); return KRB5_PARSE_MALFORMED; } if (!default_realm && (flags & KRB5_PRINCIPAL_PARSE_NO_REALM) == 0) { retval = krb5_get_default_realm(context, &default_realm); if (retval) { krb5_xfree(principal->data); krb5_xfree((char *)principal); return(retval); } default_realm_size = strlen(default_realm); } realmsize = default_realm_size; } else if (flags & KRB5_PRINCIPAL_PARSE_NO_REALM) { krb5_set_error_message(context, KRB5_PARSE_MALFORMED, "Principal %s has realm present", name); krb5_xfree(principal->data); krb5_xfree(principal); return KRB5_PARSE_MALFORMED; } /* * Pass 2. Happens only if there were more than FCOMPNUM * component; if this happens, someone should be shot * immediately. Nevertheless, we will attempt to handle said * case..... <martyred sigh> */ if (components >= FCOMPNUM) { size = 0; parsed_realm = NULL; for (i=0,cp = name; (c = *cp); cp++) { if (c == QUOTECHAR) { cp++; size++; } else if (c == COMPONENT_SEP) { if (krb5_princ_size(context, principal) > i) krb5_princ_component(context, principal, i)->length = size; size = 0; i++; } else if (c == REALM_SEP) { if (krb5_princ_size(context, principal) > i) krb5_princ_component(context, principal, i)->length = size; size = 0; parsed_realm = cp+1; } else size++; } if (parsed_realm) krb5_princ_realm(context, principal)->length = size; else if (krb5_princ_size(context, principal) > i) krb5_princ_component(context, principal, i)->length = size; if (i + 1 != components) { #if !defined(_WIN32) fprintf(stderr, "Programming error in krb5_parse_name!"); #endif assert(i + 1 == components); abort(); } } else { /* * If there were fewer than FCOMPSIZE components (the * usual case), then just copy the sizes to the * principal structure */ for (i=0; i < components; i++) krb5_princ_component(context, principal, i)->length = fcompsize[i]; } /* * Now, we need to allocate the space for the strings themselves..... */ tmpdata = malloc(realmsize + 1); if (tmpdata == 0) { krb5_xfree(principal->data); krb5_xfree(principal); krb5_xfree(default_realm); return ENOMEM; } krb5_princ_set_realm_length(context, principal, realmsize); krb5_princ_set_realm_data(context, principal, tmpdata); for (i=0; i < components; i++) { char *tmpdata2 = malloc(krb5_princ_component(context, principal, i)->length + 1); if (tmpdata2 == NULL) { for (i--; i >= 0; i--) krb5_xfree(krb5_princ_component(context, principal, i)->data); krb5_xfree(krb5_princ_realm(context, principal)->data); krb5_xfree(principal->data); krb5_xfree(principal); krb5_xfree(default_realm); return(ENOMEM); } krb5_princ_component(context, principal, i)->data = tmpdata2; krb5_princ_component(context, principal, i)->magic = KV5M_DATA; } /* * Pass 3. Now we go through the string a *third* time, this * time filling in the krb5_principal structure which we just * allocated. */ q = krb5_princ_component(context, principal, 0)->data; for (i=0,cp = name, first_at = 1; (c = *cp); cp++) { if (c == QUOTECHAR) { cp++; switch (c = *cp) { case 'n': *q++ = '\n'; break; case 't': *q++ = '\t'; break; case 'b': *q++ = '\b'; break; case '0': *q++ = '\0'; break; default: *q++ = c; break; } } else if (c == COMPONENT_SEP && !enterprise) { i++; *q++ = '\0'; q = krb5_princ_component(context, principal, i)->data; } else if (c == REALM_SEP && (!enterprise || !first_at)) { i++; *q++ = '\0'; q = krb5_princ_realm(context, principal)->data; } else { if (c == REALM_SEP && enterprise && first_at) first_at = 0; *q++ = c; } } *q++ = '\0'; if (!parsed_realm) { if (flags & KRB5_PRINCIPAL_PARSE_NO_REALM) (krb5_princ_realm(context, principal)->data)[0] = '\0'; else strlcpy(krb5_princ_realm(context, principal)->data, default_realm, realmsize+1); } /* * Alright, we're done. Now stuff a pointer to this monstrosity * into the return variable, and let's get out of here. */ if (enterprise) krb5_princ_type(context, principal) = KRB5_NT_ENTERPRISE_PRINCIPAL; else krb5_princ_type(context, principal) = KRB5_NT_PRINCIPAL; principal->magic = KV5M_PRINCIPAL; principal->realm.magic = KV5M_DATA; *nprincipal = principal; if (default_realm != NULL) krb5_xfree(default_realm); return(0); }
krb5_error_code KRB5_CALLCONV krb5_sname_to_principal(krb5_context context, const char *hostname, const char *sname, krb5_int32 type, krb5_principal *ret_princ) { char **hrealms, *realm, *remote_host; krb5_error_code retval; register char *cp; char localname[MAXHOSTNAMELEN]; #ifdef DEBUG_REFERRALS printf("krb5_sname_to_principal(host=%s, sname=%s, type=%d)\n",hostname,sname,type); printf(" name types: 0=unknown, 3=srv_host\n"); #endif if ((type == KRB5_NT_UNKNOWN) || (type == KRB5_NT_SRV_HST)) { /* if hostname is NULL, use local hostname */ if (! hostname) { if (gethostname(localname, MAXHOSTNAMELEN)) return SOCKET_ERRNO; hostname = localname; } /* if sname is NULL, use "host" */ if (! sname) sname = "host"; /* copy the hostname into non-volatile storage */ if (type == KRB5_NT_SRV_HST) { struct addrinfo *ai, hints; int err; char hnamebuf[NI_MAXHOST]; /* Note that the old code would accept numeric addresses, and if the gethostbyaddr step could convert them to real hostnames, you could actually get reasonable results. If the mapping failed, you'd get dotted triples as realm names. *sigh* The latter has been fixed in hst_realm.c, but we should keep supporting numeric addresses if they do have hostnames associated. */ memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_INET; hints.ai_flags = AI_CANONNAME; try_getaddrinfo_again: err = getaddrinfo(hostname, 0, &hints, &ai); if (err) { #ifdef DEBUG_REFERRALS printf("sname_to_princ: probably punting due to bad hostname of %s\n",hostname); #endif if (hints.ai_family == AF_INET) { /* Just in case it's an IPv6-only name. */ hints.ai_family = 0; goto try_getaddrinfo_again; } return KRB5_ERR_BAD_HOSTNAME; } remote_host = strdup(ai->ai_canonname ? ai->ai_canonname : hostname); if (!remote_host) { freeaddrinfo(ai); return ENOMEM; } if (maybe_use_reverse_dns(context, DEFAULT_RDNS_LOOKUP)) { /* * Do a reverse resolution to get the full name, just in * case there's some funny business going on. If there * isn't an in-addr record, give up. */ /* XXX: This is *so* bogus. There are several cases where this won't get us the canonical name of the host, but this is what we've trained people to expect. We'll probably fix it at some point, but let's try to preserve the current behavior and only shake things up once when it comes time to fix this lossage. */ err = getnameinfo(ai->ai_addr, ai->ai_addrlen, hnamebuf, sizeof(hnamebuf), 0, 0, NI_NAMEREQD); freeaddrinfo(ai); if (err == 0) { free(remote_host); remote_host = strdup(hnamebuf); if (!remote_host) return ENOMEM; } } else freeaddrinfo(ai); } else { /* type == KRB5_NT_UNKNOWN */ remote_host = strdup(hostname); } if (!remote_host) return ENOMEM; #ifdef DEBUG_REFERRALS printf("sname_to_princ: hostname <%s> after rdns processing\n",remote_host); #endif if (type == KRB5_NT_SRV_HST) for (cp = remote_host; *cp; cp++) if (isupper((unsigned char) (*cp))) *cp = tolower((unsigned char) (*cp)); /* * Windows NT5's broken resolver gratuitously tacks on a * trailing period to the hostname (at least it does in * Beta2). Find and remove it. */ if (remote_host[0]) { cp = remote_host + strlen(remote_host)-1; if (*cp == '.') *cp = 0; } if ((retval = krb5_get_host_realm(context, remote_host, &hrealms))) { free(remote_host); return retval; } #ifdef DEBUG_REFERRALS printf("sname_to_princ: realm <%s> after krb5_get_host_realm\n",hrealms[0]); #endif if (!hrealms[0]) { free(remote_host); free(hrealms); return KRB5_ERR_HOST_REALM_UNKNOWN; } realm = hrealms[0]; retval = krb5_build_principal(context, ret_princ, strlen(realm), realm, sname, remote_host, (char *)0); if (retval == 0) krb5_princ_type(context, *ret_princ) = type; #ifdef DEBUG_REFERRALS printf("krb5_sname_to_principal returning\n"); printf("realm: <%s>, sname: <%s>, remote_host: <%s>\n", realm,sname,remote_host); krb5int_dbgref_dump_principal("krb5_sname_to_principal",*ret_princ); #endif free(remote_host); krb5_free_host_realm(context, hrealms); return retval; } else { return KRB5_SNAME_UNSUPP_NAMETYPE; } }
/* Given d2i_-decoded asn1ticket, allocate and return a new krb5_ticket. ** Return Kerberos error code and kssl_err struct on error. ** Allocates krb5_ticket and krb5_principal; caller should free these. ** ** 20010410 VRS Implemented krb5_decode_ticket() as ** old_krb5_decode_ticket(). Missing from MIT1.0.6. ** 20010615 VRS Re-cast as openssl/asn1 d2i_*() functions. ** Re-used some of the old krb5_decode_ticket() ** code here. This tkt should alloc/free just ** like the real thing. */ static krb5_error_code kssl_TKT2tkt( /* IN */ krb5_context krb5context, /* IN */ KRB5_TKTBODY *asn1ticket, /* OUT */ krb5_ticket **krb5ticket, /* OUT */ KSSL_ERR *kssl_err ) { krb5_error_code krb5rc = KRB5KRB_ERR_GENERIC; krb5_ticket *new5ticket = NULL; ASN1_GENERALSTRING *gstr_svc, *gstr_host; *krb5ticket = NULL; if (asn1ticket == NULL || asn1ticket->realm == NULL || asn1ticket->sname == NULL || sk_ASN1_GENERALSTRING_num(asn1ticket->sname->namestring) < 2) { (void) snprintf(kssl_err->text, KSSL_ERR_MAX, "Null field in asn1ticket.\n"); kssl_err->reason = SSL_R_KRB5_S_RD_REQ; return KRB5KRB_ERR_GENERIC; } if ((new5ticket = (krb5_ticket *)calloc(1, sizeof(krb5_ticket))) == NULL) { (void) snprintf(kssl_err->text, KSSL_ERR_MAX, "Unable to allocate new krb5_ticket.\n"); kssl_err->reason = SSL_R_KRB5_S_RD_REQ; return ENOMEM; /* or KRB5KRB_ERR_GENERIC; */ } gstr_svc = sk_ASN1_GENERALSTRING_value(asn1ticket->sname->namestring, 0); gstr_host = sk_ASN1_GENERALSTRING_value(asn1ticket->sname->namestring, 1); if ((krb5rc = kssl_build_principal_2(krb5context, &new5ticket->server, asn1ticket->realm->length, (char *)asn1ticket->realm->data, gstr_svc->length, (char *)gstr_svc->data, gstr_host->length, (char *)gstr_host->data)) != 0) { free(new5ticket); (void) snprintf(kssl_err->text, KSSL_ERR_MAX, "Error building ticket server principal.\n"); kssl_err->reason = SSL_R_KRB5_S_RD_REQ; return krb5rc; /* or KRB5KRB_ERR_GENERIC; */ } krb5_princ_type(krb5context, new5ticket->server) = asn1ticket->sname->nametype->data[0]; new5ticket->enc_part.enctype = asn1ticket->encdata->etype->data[0]; new5ticket->enc_part.kvno = asn1ticket->encdata->kvno->data[0]; new5ticket->enc_part.ciphertext.length = asn1ticket->encdata->cipher->length; if ((new5ticket->enc_part.ciphertext.data = calloc(1, asn1ticket->encdata->cipher->length)) == NULL) { free(new5ticket); (void) snprintf(kssl_err->text, KSSL_ERR_MAX, "Error allocating cipher in krb5ticket.\n"); kssl_err->reason = SSL_R_KRB5_S_RD_REQ; return KRB5KRB_ERR_GENERIC; } else { memcpy(new5ticket->enc_part.ciphertext.data, asn1ticket->encdata->cipher->data, asn1ticket->encdata->cipher->length); } *krb5ticket = new5ticket; return 0; }
/*ARGSUSED*/ void process_as_req(krb5_kdc_req *request, krb5_data *req_pkt, const krb5_fulladdr *from, verto_ctx *vctx, loop_respond_fn respond, void *arg) { krb5_error_code errcode; krb5_timestamp rtime; unsigned int s_flags = 0; krb5_principal_data client_princ; krb5_data encoded_req_body; krb5_enctype useenctype; struct as_req_state *state; state = malloc(sizeof(*state)); if (!state) { (*respond)(arg, ENOMEM, NULL); return; } state->session_key.contents = 0; state->enc_tkt_reply.authorization_data = NULL; state->reply.padata = 0; memset(&state->reply, 0, sizeof(state->reply)); state->respond = respond; state->arg = arg; state->ticket_reply.enc_part.ciphertext.data = 0; state->server_keyblock.contents = NULL; state->client_keyblock.contents = NULL; state->reply_encpart.enc_padata = 0; state->client = NULL; state->server = NULL; state->request = request; state->e_data = NULL; state->typed_e_data = FALSE; state->authtime = 0; state->c_flags = 0; state->req_pkt = req_pkt; state->rstate = NULL; state->sname = 0; state->cname = 0; state->pa_context = NULL; state->from = from; memset(&state->rock, 0, sizeof(state->rock)); #if APPLE_PKINIT asReqDebug("process_as_req top realm %s name %s\n", request->client->realm.data, request->client->data->data); #endif /* APPLE_PKINIT */ if (state->request->msg_type != KRB5_AS_REQ) { state->status = "msg_type mismatch"; errcode = KRB5_BADMSGTYPE; goto errout; } errcode = kdc_make_rstate(&state->rstate); if (errcode != 0) { state->status = "constructing state"; goto errout; } if (fetch_asn1_field((unsigned char *) req_pkt->data, 1, 4, &encoded_req_body) != 0) { errcode = ASN1_BAD_ID; state->status = "Finding req_body"; goto errout; } errcode = kdc_find_fast(&state->request, &encoded_req_body, NULL, NULL, state->rstate, &state->inner_body); if (errcode) { state->status = "error decoding FAST"; goto errout; } if (state->inner_body == NULL) { /* Not a FAST request; copy the encoded request body. */ errcode = krb5_copy_data(kdc_context, &encoded_req_body, &state->inner_body); if (errcode) { state->status = "storing req body"; goto errout; } } state->rock.request = state->request; state->rock.inner_body = state->inner_body; state->rock.rstate = state->rstate; state->rock.vctx = vctx; if (!state->request->client) { state->status = "NULL_CLIENT"; errcode = KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN; goto errout; } if ((errcode = krb5_unparse_name(kdc_context, state->request->client, &state->cname))) { state->status = "UNPARSING_CLIENT"; goto errout; } limit_string(state->cname); if (!state->request->server) { state->status = "NULL_SERVER"; errcode = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN; goto errout; } if ((errcode = krb5_unparse_name(kdc_context, state->request->server, &state->sname))) { state->status = "UNPARSING_SERVER"; goto errout; } limit_string(state->sname); /* * We set KRB5_KDB_FLAG_CLIENT_REFERRALS_ONLY as a hint * to the backend to return naming information in lieu * of cross realm TGS entries. */ setflag(state->c_flags, KRB5_KDB_FLAG_CLIENT_REFERRALS_ONLY); /* * Note that according to the referrals draft we should * always canonicalize enterprise principal names. */ if (isflagset(state->request->kdc_options, KDC_OPT_CANONICALIZE) || state->request->client->type == KRB5_NT_ENTERPRISE_PRINCIPAL) { setflag(state->c_flags, KRB5_KDB_FLAG_CANONICALIZE); setflag(state->c_flags, KRB5_KDB_FLAG_ALIAS_OK); } if (include_pac_p(kdc_context, state->request)) { setflag(state->c_flags, KRB5_KDB_FLAG_INCLUDE_PAC); } errcode = krb5_db_get_principal(kdc_context, state->request->client, state->c_flags, &state->client); if (errcode == KRB5_KDB_NOENTRY) { state->status = "CLIENT_NOT_FOUND"; if (vague_errors) errcode = KRB5KRB_ERR_GENERIC; else errcode = KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN; goto errout; } else if (errcode) { state->status = "LOOKING_UP_CLIENT"; goto errout; } state->rock.client = state->client; /* * If the backend returned a principal that is not in the local * realm, then we need to refer the client to that realm. */ if (!is_local_principal(state->client->princ)) { /* Entry is a referral to another realm */ state->status = "REFERRAL"; errcode = KRB5KDC_ERR_WRONG_REALM; goto errout; } #if 0 /* * Turn off canonicalization if client is marked DES only * (unless enterprise principal name was requested) */ if (isflagset(client->attributes, KRB5_KDB_NON_MS_PRINCIPAL) && krb5_princ_type(kdc_context, request->client) != KRB5_NT_ENTERPRISE_PRINCIPAL) { clear(c_flags, KRB5_KDB_FLAG_CANONICALIZE); } #endif s_flags = 0; setflag(s_flags, KRB5_KDB_FLAG_ALIAS_OK); if (isflagset(state->request->kdc_options, KDC_OPT_CANONICALIZE)) { setflag(s_flags, KRB5_KDB_FLAG_CANONICALIZE); } errcode = krb5_db_get_principal(kdc_context, state->request->server, s_flags, &state->server); if (errcode == KRB5_KDB_NOENTRY) { state->status = "SERVER_NOT_FOUND"; errcode = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN; goto errout; } else if (errcode) { state->status = "LOOKING_UP_SERVER"; goto errout; } if ((errcode = krb5_timeofday(kdc_context, &state->kdc_time))) { state->status = "TIMEOFDAY"; goto errout; } state->authtime = state->kdc_time; /* for audit_as_request() */ if ((errcode = validate_as_request(state->request, *state->client, *state->server, state->kdc_time, &state->status, &state->e_data))) { if (!state->status) state->status = "UNKNOWN_REASON"; errcode += ERROR_TABLE_BASE_krb5; goto errout; } /* * Select the keytype for the ticket session key. */ if ((useenctype = select_session_keytype(kdc_context, state->server, state->request->nktypes, state->request->ktype)) == 0) { /* unsupported ktype */ state->status = "BAD_ENCRYPTION_TYPE"; errcode = KRB5KDC_ERR_ETYPE_NOSUPP; goto errout; } if ((errcode = krb5_c_make_random_key(kdc_context, useenctype, &state->session_key))) { state->status = "RANDOM_KEY_FAILED"; goto errout; } /* * Canonicalization is only effective if we are issuing a TGT * (the intention is to allow support for Windows "short" realm * aliases, nothing more). */ if (isflagset(s_flags, KRB5_KDB_FLAG_CANONICALIZE) && krb5_is_tgs_principal(state->request->server) && krb5_is_tgs_principal(state->server->princ)) { state->ticket_reply.server = state->server->princ; } else { state->ticket_reply.server = state->request->server; } state->enc_tkt_reply.flags = 0; state->enc_tkt_reply.times.authtime = state->authtime; setflag(state->enc_tkt_reply.flags, TKT_FLG_INITIAL); setflag(state->enc_tkt_reply.flags, TKT_FLG_ENC_PA_REP); /* * It should be noted that local policy may affect the * processing of any of these flags. For example, some * realms may refuse to issue renewable tickets */ if (isflagset(state->request->kdc_options, KDC_OPT_FORWARDABLE)) setflag(state->enc_tkt_reply.flags, TKT_FLG_FORWARDABLE); if (isflagset(state->request->kdc_options, KDC_OPT_PROXIABLE)) setflag(state->enc_tkt_reply.flags, TKT_FLG_PROXIABLE); if (isflagset(state->request->kdc_options, KDC_OPT_ALLOW_POSTDATE)) setflag(state->enc_tkt_reply.flags, TKT_FLG_MAY_POSTDATE); state->enc_tkt_reply.session = &state->session_key; if (isflagset(state->c_flags, KRB5_KDB_FLAG_CANONICALIZE)) { client_princ = *(state->client->princ); } else { client_princ = *(state->request->client); /* The realm is always canonicalized */ client_princ.realm = state->client->princ->realm; } state->enc_tkt_reply.client = &client_princ; state->enc_tkt_reply.transited.tr_type = KRB5_DOMAIN_X500_COMPRESS; state->enc_tkt_reply.transited.tr_contents = empty_string; if (isflagset(state->request->kdc_options, KDC_OPT_POSTDATED)) { setflag(state->enc_tkt_reply.flags, TKT_FLG_POSTDATED); setflag(state->enc_tkt_reply.flags, TKT_FLG_INVALID); state->enc_tkt_reply.times.starttime = state->request->from; } else state->enc_tkt_reply.times.starttime = state->kdc_time; kdc_get_ticket_endtime(kdc_context, state->enc_tkt_reply.times.starttime, kdc_infinity, state->request->till, state->client, state->server, &state->enc_tkt_reply.times.endtime); if (isflagset(state->request->kdc_options, KDC_OPT_RENEWABLE_OK) && !isflagset(state->client->attributes, KRB5_KDB_DISALLOW_RENEWABLE) && (state->enc_tkt_reply.times.endtime < state->request->till)) { /* we set the RENEWABLE option for later processing */ setflag(state->request->kdc_options, KDC_OPT_RENEWABLE); state->request->rtime = state->request->till; } rtime = (state->request->rtime == 0) ? kdc_infinity : state->request->rtime; if (isflagset(state->request->kdc_options, KDC_OPT_RENEWABLE)) { /* * XXX Should we squelch the output renew_till to be no * earlier than the endtime of the ticket? */ setflag(state->enc_tkt_reply.flags, TKT_FLG_RENEWABLE); state->enc_tkt_reply.times.renew_till = min(rtime, state->enc_tkt_reply.times.starttime + min(state->client->max_renewable_life, min(state->server->max_renewable_life, max_renewable_life_for_realm))); } else state->enc_tkt_reply.times.renew_till = 0; /* XXX */ /* * starttime is optional, and treated as authtime if not present. * so we can nuke it if it matches */ if (state->enc_tkt_reply.times.starttime == state->enc_tkt_reply.times.authtime) state->enc_tkt_reply.times.starttime = 0; state->enc_tkt_reply.caddrs = state->request->addresses; state->enc_tkt_reply.authorization_data = 0; /* If anonymous requests are being used, adjust the realm of the client * principal. */ if (isflagset(state->request->kdc_options, KDC_OPT_REQUEST_ANONYMOUS)) { if (!krb5_principal_compare_any_realm(kdc_context, state->request->client, krb5_anonymous_principal())) { errcode = KRB5KDC_ERR_BADOPTION; state->status = "Anonymous requested but anonymous " "principal not used."; goto errout; } setflag(state->enc_tkt_reply.flags, TKT_FLG_ANONYMOUS); krb5_free_principal(kdc_context, state->request->client); errcode = krb5_copy_principal(kdc_context, krb5_anonymous_principal(), &state->request->client); if (errcode) { state->status = "Copying anonymous principal"; goto errout; } state->enc_tkt_reply.client = state->request->client; setflag(state->client->attributes, KRB5_KDB_REQUIRES_PRE_AUTH); } /* * Check the preauthentication if it is there. */ if (state->request->padata) { check_padata(kdc_context, &state->rock, state->req_pkt, state->request, &state->enc_tkt_reply, &state->pa_context, &state->e_data, &state->typed_e_data, finish_preauth, state); } else finish_preauth(state, 0); return; errout: finish_process_as_req(state, errcode); }
static void do_v5_kvno (int count, char *names[], char * ccachestr, char *etypestr, char *keytab_name, char *sname, int canon, int unknown) { krb5_error_code ret; int i, errors; krb5_enctype etype; krb5_ccache ccache; krb5_principal me; krb5_creds in_creds, *out_creds; krb5_ticket *ticket; char *princ; krb5_keytab keytab = NULL; ret = krb5_init_context(&context); if (ret) { com_err(prog, ret, "while initializing krb5 library"); exit(1); } if (etypestr) { ret = krb5_string_to_enctype(etypestr, &etype); if (ret) { com_err(prog, ret, "while converting etype"); exit(1); } } else { etype = 0; } if (ccachestr) ret = krb5_cc_resolve(context, ccachestr, &ccache); else ret = krb5_cc_default(context, &ccache); if (ret) { com_err(prog, ret, "while opening ccache"); exit(1); } if (keytab_name) { ret = krb5_kt_resolve(context, keytab_name, &keytab); if (ret) { com_err(prog, ret, "resolving keytab %s", keytab_name); exit(1); } } ret = krb5_cc_get_principal(context, ccache, &me); if (ret) { com_err(prog, ret, "while getting client principal name"); exit(1); } errors = 0; for (i = 0; i < count; i++) { memset(&in_creds, 0, sizeof(in_creds)); in_creds.client = me; if (sname != NULL) { ret = krb5_sname_to_principal(context, names[i], sname, KRB5_NT_SRV_HST, &in_creds.server); } else { ret = krb5_parse_name(context, names[i], &in_creds.server); } if (ret) { if (!quiet) com_err(prog, ret, "while parsing principal name %s", names[i]); errors++; continue; } if (unknown == 1) { krb5_princ_type(context, in_creds.server) = KRB5_NT_UNKNOWN; } ret = krb5_unparse_name(context, in_creds.server, &princ); if (ret) { com_err(prog, ret, "while formatting parsed principal name for '%s'", names[i]); errors++; continue; } in_creds.keyblock.enctype = etype; ret = krb5_get_credentials(context, canon ? KRB5_GC_CANONICALIZE : 0, ccache, &in_creds, &out_creds); krb5_free_principal(context, in_creds.server); if (ret) { com_err(prog, ret, "while getting credentials for %s", princ); krb5_free_unparsed_name(context, princ); errors++; continue; } /* we need a native ticket */ ret = krb5_decode_ticket(&out_creds->ticket, &ticket); if (ret) { com_err(prog, ret, "while decoding ticket for %s", princ); krb5_free_creds(context, out_creds); krb5_free_unparsed_name(context, princ); errors++; continue; } if (keytab) { ret = krb5_server_decrypt_ticket_keytab(context, keytab, ticket); if (ret) { if (!quiet) printf("%s: kvno = %d, keytab entry invalid", princ, ticket->enc_part.kvno); com_err(prog, ret, "while decrypting ticket for %s", princ); krb5_free_ticket(context, ticket); krb5_free_creds(context, out_creds); krb5_free_unparsed_name(context, princ); errors++; continue; } if (!quiet) printf("%s: kvno = %d, keytab entry valid\n", princ, ticket->enc_part.kvno); } else { if (!quiet) printf("%s: kvno = %d\n", princ, ticket->enc_part.kvno); } krb5_free_creds(context, out_creds); krb5_free_unparsed_name(context, princ); } if (keytab) krb5_kt_close(context, keytab); krb5_free_principal(context, me); krb5_cc_close(context, ccache); krb5_free_context(context); if (errors) exit(1); exit(0); }