KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_verify_authenticator_checksum(krb5_context context, krb5_auth_context ac, void *data, size_t len) { krb5_error_code ret; krb5_keyblock *key = NULL; krb5_authenticator authenticator; krb5_crypto crypto; ret = krb5_auth_con_getauthenticator(context, ac, &authenticator); if (ret) return ret; if (authenticator->cksum == NULL) { ret = -17; goto out; } ret = krb5_auth_con_getkey(context, ac, &key); if (ret) goto out; ret = krb5_crypto_init(context, key, 0, &crypto); if (ret) goto out; ret = krb5_verify_checksum(context, crypto, KRB5_KU_AP_REQ_AUTH_CKSUM, data, len, authenticator->cksum); krb5_crypto_destroy(context, crypto); out: krb5_free_authenticator(context, &authenticator); krb5_free_keyblock(context, key); 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_error_code tgs_parse_request(krb5_context context, krb5_kdc_configuration *config, KDC_REQ_BODY *b, const PA_DATA *tgs_req, hdb_entry_ex **krbtgt, krb5_enctype *krbtgt_etype, krb5_ticket **ticket, const char **e_text, const char *from, const struct sockaddr *from_addr, time_t **csec, int **cusec, AuthorizationData **auth_data) { krb5_ap_req ap_req; krb5_error_code ret; krb5_principal princ; krb5_auth_context ac = NULL; krb5_flags ap_req_options; krb5_flags verify_ap_req_flags; krb5_crypto crypto; Key *tkey; *auth_data = NULL; *csec = NULL; *cusec = NULL; memset(&ap_req, 0, sizeof(ap_req)); ret = krb5_decode_ap_req(context, &tgs_req->padata_value, &ap_req); if(ret){ kdc_log(context, config, 0, "Failed to decode AP-REQ: %s", krb5_get_err_text(context, ret)); goto out; } if(!get_krbtgt_realm(&ap_req.ticket.sname)){ /* XXX check for ticket.sname == req.sname */ kdc_log(context, config, 0, "PA-DATA is not a ticket-granting ticket"); ret = KRB5KDC_ERR_POLICY; /* ? */ goto out; } _krb5_principalname2krb5_principal(context, &princ, ap_req.ticket.sname, ap_req.ticket.realm); ret = _kdc_db_fetch(context, config, princ, HDB_F_GET_KRBTGT, NULL, krbtgt); if(ret) { char *p; ret = krb5_unparse_name(context, princ, &p); if (ret != 0) p = "<unparse_name failed>"; krb5_free_principal(context, princ); kdc_log(context, config, 0, "Ticket-granting ticket not found in database: %s: %s", p, krb5_get_err_text(context, ret)); if (ret == 0) free(p); ret = KRB5KRB_AP_ERR_NOT_US; goto out; } if(ap_req.ticket.enc_part.kvno && *ap_req.ticket.enc_part.kvno != (*krbtgt)->entry.kvno){ char *p; ret = krb5_unparse_name (context, princ, &p); krb5_free_principal(context, princ); if (ret != 0) p = "<unparse_name failed>"; kdc_log(context, config, 0, "Ticket kvno = %d, DB kvno = %d (%s)", *ap_req.ticket.enc_part.kvno, (*krbtgt)->entry.kvno, p); if (ret == 0) free (p); ret = KRB5KRB_AP_ERR_BADKEYVER; goto out; } *krbtgt_etype = ap_req.ticket.enc_part.etype; ret = hdb_enctype2key(context, &(*krbtgt)->entry, ap_req.ticket.enc_part.etype, &tkey); if(ret){ char *str, *p; krb5_enctype_to_string(context, ap_req.ticket.enc_part.etype, &str); krb5_unparse_name(context, princ, &p); kdc_log(context, config, 0, "No server key with enctype %s found for %s", str, p); free(str); free(p); ret = KRB5KRB_AP_ERR_BADKEYVER; goto out; } if (b->kdc_options.validate) verify_ap_req_flags = KRB5_VERIFY_AP_REQ_IGNORE_INVALID; else verify_ap_req_flags = 0; ret = krb5_verify_ap_req2(context, &ac, &ap_req, princ, &tkey->key, verify_ap_req_flags, &ap_req_options, ticket, KRB5_KU_TGS_REQ_AUTH); krb5_free_principal(context, princ); if(ret) { kdc_log(context, config, 0, "Failed to verify AP-REQ: %s", krb5_get_err_text(context, ret)); goto out; } { krb5_authenticator auth; ret = krb5_auth_con_getauthenticator(context, ac, &auth); if (ret == 0) { *csec = malloc(sizeof(**csec)); if (*csec == NULL) { krb5_free_authenticator(context, &auth); kdc_log(context, config, 0, "malloc failed"); goto out; } **csec = auth->ctime; *cusec = malloc(sizeof(**cusec)); if (*cusec == NULL) { krb5_free_authenticator(context, &auth); kdc_log(context, config, 0, "malloc failed"); goto out; } **cusec = auth->cusec; krb5_free_authenticator(context, &auth); } } ret = tgs_check_authenticator(context, config, ac, b, e_text, &(*ticket)->ticket.key); if (ret) { krb5_auth_con_free(context, ac); goto out; } if (b->enc_authorization_data) { krb5_keyblock *subkey; krb5_data ad; ret = krb5_auth_con_getremotesubkey(context, ac, &subkey); if(ret){ krb5_auth_con_free(context, ac); kdc_log(context, config, 0, "Failed to get remote subkey: %s", krb5_get_err_text(context, ret)); goto out; } if(subkey == NULL){ ret = krb5_auth_con_getkey(context, ac, &subkey); if(ret) { krb5_auth_con_free(context, ac); kdc_log(context, config, 0, "Failed to get session key: %s", krb5_get_err_text(context, ret)); goto out; } } if(subkey == NULL){ krb5_auth_con_free(context, ac); kdc_log(context, config, 0, "Failed to get key for enc-authorization-data"); ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; /* ? */ goto out; } ret = krb5_crypto_init(context, subkey, 0, &crypto); if (ret) { krb5_auth_con_free(context, ac); kdc_log(context, config, 0, "krb5_crypto_init failed: %s", krb5_get_err_text(context, ret)); goto out; } ret = krb5_decrypt_EncryptedData (context, crypto, KRB5_KU_TGS_REQ_AUTH_DAT_SUBKEY, b->enc_authorization_data, &ad); krb5_crypto_destroy(context, crypto); if(ret){ krb5_auth_con_free(context, ac); kdc_log(context, config, 0, "Failed to decrypt enc-authorization-data"); ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; /* ? */ goto out; } krb5_free_keyblock(context, subkey); ALLOC(*auth_data); if (*auth_data == NULL) { krb5_auth_con_free(context, ac); ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; /* ? */ goto out; } ret = decode_AuthorizationData(ad.data, ad.length, *auth_data, NULL); if(ret){ krb5_auth_con_free(context, ac); free(*auth_data); *auth_data = NULL; kdc_log(context, config, 0, "Failed to decode authorization data"); ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; /* ? */ goto out; } } krb5_auth_con_free(context, ac); out: free_AP_REQ(&ap_req); return ret; }
void kerberos5_reply(Authenticator *ap, unsigned char *data, int cnt) { static int mutual_complete = 0; if (cnt-- < 1) return; switch (*data++) { case KRB_REJECT: if (cnt > 0) { printf("[ Kerberos V5 refuses authentication because %.*s ]\r\n", cnt, data); } else printf("[ Kerberos V5 refuses authentication ]\r\n"); auth_send_retry(); return; case KRB_ACCEPT: { krb5_error_code ret; Session_Key skey; krb5_keyblock *keyblock; if ((ap->way & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL && !mutual_complete) { printf("[ Kerberos V5 accepted you, but didn't provide mutual authentication! ]\r\n"); auth_send_retry(); return; } if (cnt) printf("[ Kerberos V5 accepts you as ``%.*s'' ]\r\n", cnt, data); else printf("[ Kerberos V5 accepts you ]\r\n"); ret = krb5_auth_con_getlocalsubkey (context, auth_context, &keyblock); if (ret) ret = krb5_auth_con_getkey (context, auth_context, &keyblock); if(ret) { printf("[ krb5_auth_con_getkey: %s ]\r\n", krb5_get_err_text(context, ret)); auth_send_retry(); return; } skey.type = SK_DES; skey.length = 8; skey.data = keyblock->keyvalue.data; encrypt_session_key(&skey, 0); krb5_free_keyblock_contents (context, keyblock); auth_finished(ap, AUTH_USER); #ifdef FORWARD if (forward_flags & OPTS_FORWARD_CREDS) kerberos5_forward(ap); #endif /* FORWARD */ break; } case KRB_RESPONSE: if ((ap->way & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL) { /* the rest of the reply should contain a krb_ap_rep */ krb5_ap_rep_enc_part *reply; krb5_data inbuf; krb5_error_code ret; inbuf.length = cnt; inbuf.data = (char *)data; ret = krb5_rd_rep(context, auth_context, &inbuf, &reply); if (ret) { printf("[ Mutual authentication failed: %s ]\r\n", krb5_get_err_text (context, ret)); auth_send_retry(); return; } krb5_free_ap_rep_enc_part(context, reply); mutual_complete = 1; } return; #ifdef FORWARD case KRB_FORWARD_ACCEPT: printf("[ Kerberos V5 accepted forwarded credentials ]\r\n"); return; case KRB_FORWARD_REJECT: printf("[ Kerberos V5 refuses forwarded credentials because %.*s ]\r\n", cnt, data); return; #endif /* FORWARD */ default: if (auth_debug_mode) printf("Unknown Kerberos option %d\r\n", data[-1]); return; } }
void kerberos5_is(Authenticator *ap, unsigned char *data, int cnt) { krb5_error_code ret; krb5_data outbuf; krb5_keyblock *key_block; char *name; krb5_principal server; krb5_authenticator authenticator; int zero = 0; if (cnt-- < 1) return; switch (*data++) { case KRB_AUTH: auth.data = (char *)data; auth.length = cnt; auth_context = NULL; ret = krb5_auth_con_init (context, &auth_context); if (ret) { Data(ap, KRB_REJECT, "krb5_auth_con_init failed", -1); auth_finished(ap, AUTH_REJECT); if (auth_debug_mode) printf("Kerberos V5: krb5_auth_con_init failed (%s)\r\n", krb5_get_err_text(context, ret)); return; } ret = krb5_auth_con_setaddrs_from_fd (context, auth_context, &zero); if (ret) { Data(ap, KRB_REJECT, "krb5_auth_con_setaddrs_from_fd failed", -1); auth_finished(ap, AUTH_REJECT); if (auth_debug_mode) printf("Kerberos V5: " "krb5_auth_con_setaddrs_from_fd failed (%s)\r\n", krb5_get_err_text(context, ret)); return; } ret = krb5_sock_to_principal (context, 0, "host", KRB5_NT_SRV_HST, &server); if (ret) { Data(ap, KRB_REJECT, "krb5_sock_to_principal failed", -1); auth_finished(ap, AUTH_REJECT); if (auth_debug_mode) printf("Kerberos V5: " "krb5_sock_to_principal failed (%s)\r\n", krb5_get_err_text(context, ret)); return; } ret = krb5_rd_req(context, &auth_context, &auth, server, NULL, NULL, &ticket); krb5_free_principal (context, server); if (ret) { char *errbuf; asprintf(&errbuf, "Read req failed: %s", krb5_get_err_text(context, ret)); Data(ap, KRB_REJECT, errbuf, -1); if (auth_debug_mode) printf("%s\r\n", errbuf); free (errbuf); return; } ret = krb5_auth_con_getkey(context, auth_context, &key_block); if (ret) { Data(ap, KRB_REJECT, "krb5_auth_con_getkey failed", -1); auth_finished(ap, AUTH_REJECT); if (auth_debug_mode) printf("Kerberos V5: " "krb5_auth_con_getkey failed (%s)\r\n", krb5_get_err_text(context, ret)); return; } ret = krb5_auth_getauthenticator (context, auth_context, &authenticator); if (ret) { Data(ap, KRB_REJECT, "krb5_auth_getauthenticator failed", -1); auth_finished(ap, AUTH_REJECT); if (auth_debug_mode) printf("Kerberos V5: " "krb5_auth_getauthenticator failed (%s)\r\n", krb5_get_err_text(context, ret)); return; } if (authenticator->cksum) { char foo[2]; foo[0] = ap->type; foo[1] = ap->way; ret = krb5_verify_checksum (context, foo, sizeof(foo), key_block, authenticator->cksum); if (ret) { Data(ap, KRB_REJECT, "No checksum", -1); if (auth_debug_mode) printf ("No checksum\r\n"); krb5_free_authenticator (context, &authenticator); return; } } krb5_free_authenticator (context, &authenticator); ret = krb5_auth_con_getremotesubkey (context, auth_context, &key_block); if (ret) { Data(ap, KRB_REJECT, "krb5_auth_con_getremotesubkey failed", -1); auth_finished(ap, AUTH_REJECT); if (auth_debug_mode) printf("Kerberos V5: " "krb5_auth_con_getremotesubkey failed (%s)\r\n", krb5_get_err_text(context, ret)); return; } if ((ap->way & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL) { ret = krb5_mk_rep(context, &auth_context, &outbuf); if (ret) { Data(ap, KRB_REJECT, "krb5_mk_rep failed", -1); auth_finished(ap, AUTH_REJECT); if (auth_debug_mode) printf("Kerberos V5: " "krb5_mk_rep failed (%s)\r\n", krb5_get_err_text(context, ret)); return; } Data(ap, KRB_RESPONSE, outbuf.data, outbuf.length); } if (krb5_unparse_name(context, ticket->client, &name)) name = 0; if(UserNameRequested && krb5_kuserok(context, ticket->client, UserNameRequested)) { Data(ap, KRB_ACCEPT, name, name ? -1 : 0); if (auth_debug_mode) { printf("Kerberos5 identifies him as ``%s''\r\n", name ? name : ""); } if(key_block->keytype == KEYTYPE_DES) { Session_Key skey; skey.type = SK_DES; skey.length = 8; skey.data = key_block->keyvalue.data; encrypt_session_key(&skey, 0); } } else { char *msg; asprintf (&msg, "user `%s' is not authorized to " "login as `%s'", name ? name : "<unknown>", UserNameRequested ? UserNameRequested : "<nobody>"); if (msg == NULL) Data(ap, KRB_REJECT, NULL, 0); else { Data(ap, KRB_REJECT, (void *)msg, -1); free(msg); } } auth_finished(ap, AUTH_USER); krb5_free_keyblock_contents(context, key_block); break; #ifdef FORWARD case KRB_FORWARD: { struct passwd *pwd; char ccname[1024]; /* XXX */ krb5_data inbuf; krb5_ccache ccache; inbuf.data = (char *)data; inbuf.length = cnt; pwd = getpwnam (UserNameRequested); if (pwd == NULL) break; snprintf (ccname, sizeof(ccname), "FILE:/tmp/krb5cc_%u", pwd->pw_uid); ret = krb5_cc_resolve (context, ccname, &ccache); if (ret) { if (auth_debug_mode) printf ("Kerberos V5: could not get ccache: %s\r\n", krb5_get_err_text(context, ret)); break; } ret = krb5_cc_initialize (context, ccache, ticket->client); if (ret) { if (auth_debug_mode) printf ("Kerberos V5: could not init ccache: %s\r\n", krb5_get_err_text(context, ret)); break; } ret = krb5_rd_cred (context, auth_context, ccache, &inbuf); if(ret) { char *errbuf; asprintf (&errbuf, "Read forwarded creds failed: %s", krb5_get_err_text (context, ret)); if(errbuf == NULL) Data(ap, KRB_FORWARD_REJECT, NULL, 0); else Data(ap, KRB_FORWARD_REJECT, errbuf, -1); if (auth_debug_mode) printf("Could not read forwarded credentials: %s\r\n", errbuf); free (errbuf); } else Data(ap, KRB_FORWARD_ACCEPT, 0, 0); chown (ccname + 5, pwd->pw_uid, -1); if (auth_debug_mode) printf("Forwarded credentials obtained\r\n"); break; } #endif /* FORWARD */ default: if (auth_debug_mode) printf("Unknown Kerberos option %d\r\n", data[-1]); Data(ap, KRB_REJECT, 0, 0); break; } }
static int recv_krb5_auth (int s, u_char *buf, struct sockaddr *thisaddr, struct sockaddr *thataddr, char **client_username, char **server_username, char **cmd) { uint32_t len; krb5_auth_context auth_context = NULL; krb5_ticket *ticket; krb5_error_code status; krb5_data cksum_data; krb5_principal server; char *str; if (memcmp (buf, "\x00\x00\x00\x13", 4) != 0) return -1; len = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | (buf[3]); if (net_read(s, buf, len) != len) syslog_and_die ("reading auth info: %s", strerror(errno)); if (len != sizeof(KRB5_SENDAUTH_VERSION) || memcmp (buf, KRB5_SENDAUTH_VERSION, len) != 0) syslog_and_die ("bad sendauth version: %.8s", buf); status = krb5_sock_to_principal (context, s, "host", KRB5_NT_SRV_HST, &server); if (status) syslog_and_die ("krb5_sock_to_principal: %s", krb5_get_err_text(context, status)); status = krb5_recvauth_match_version(context, &auth_context, &s, match_kcmd_version, NULL, server, KRB5_RECVAUTH_IGNORE_VERSION, NULL, &ticket); krb5_free_principal (context, server); if (status) syslog_and_die ("krb5_recvauth: %s", krb5_get_err_text(context, status)); *server_username = read_str (s, USERNAME_SZ, "remote username"); *cmd = read_str (s, ARG_MAX + 1, "command"); *client_username = read_str (s, ARG_MAX + 1, "local username"); if(protocol_version == 2) { status = krb5_auth_con_getremotesubkey(context, auth_context, &keyblock); if(status != 0 || keyblock == NULL) syslog_and_die("failed to get remote subkey"); } else if(protocol_version == 1) { status = krb5_auth_con_getkey (context, auth_context, &keyblock); if(status != 0 || keyblock == NULL) syslog_and_die("failed to get key"); } if (status != 0 || keyblock == NULL) syslog_and_die ("krb5_auth_con_getkey: %s", krb5_get_err_text(context, status)); status = krb5_crypto_init(context, keyblock, 0, &crypto); if(status) syslog_and_die("krb5_crypto_init: %s", krb5_get_err_text(context, status)); cksum_data.length = asprintf (&str, "%u:%s%s", ntohs(socket_get_port (thisaddr)), *cmd, *server_username); if (str == NULL) syslog_and_die ("asprintf: out of memory"); cksum_data.data = str; status = krb5_verify_authenticator_checksum(context, auth_context, cksum_data.data, cksum_data.length); if (status) syslog_and_die ("krb5_verify_authenticator_checksum: %s", krb5_get_err_text(context, status)); free (cksum_data.data); if (strncmp (*client_username, "-u ", 3) == 0) { do_unique_tkfile = 1; memmove (*client_username, *client_username + 3, strlen(*client_username) - 2); } if (strncmp (*client_username, "-U ", 3) == 0) { char *end, *temp_tkfile; do_unique_tkfile = 1; if (strncmp (*client_username + 3, "FILE:", 5) == 0) { temp_tkfile = tkfile; } else { strlcpy (tkfile, "FILE:", sizeof(tkfile)); temp_tkfile = tkfile + 5; } end = strchr(*client_username + 3,' '); if (end == NULL) syslog_and_die("missing argument after -U"); snprintf(temp_tkfile, sizeof(tkfile) - (temp_tkfile - tkfile), "%.*s", (int)(end - *client_username - 3), *client_username + 3); memmove (*client_username, end + 1, strlen(end+1)+1); } kerberos_status = save_krb5_creds (s, auth_context, ticket->client); if(!krb5_kuserok (context, ticket->client, *server_username)) fatal (s, NULL, "Permission denied."); if (strncmp (*cmd, "-x ", 3) == 0) { do_encrypt = 1; memmove (*cmd, *cmd + 3, strlen(*cmd) - 2); } else { if(do_encrypt) fatal (s, NULL, "Encryption is required."); do_encrypt = 0; } { char *name; if (krb5_unparse_name (context, ticket->client, &name) == 0) { char addr_str[256]; if (inet_ntop (thataddr->sa_family, socket_get_address (thataddr), addr_str, sizeof(addr_str)) == NULL) strlcpy (addr_str, "unknown address", sizeof(addr_str)); syslog(LOG_INFO|LOG_AUTH, "kerberos v5 shell from %s on %s as %s, cmd '%.80s'", name, addr_str, *server_username, *cmd); free (name); } } krb5_auth_con_free(context, auth_context); return 0; }
static int send_krb5_auth(int s, struct sockaddr *thisaddr, struct sockaddr *thataddr, const char *hostname, const char *remote_user, const char *local_user, size_t cmd_len, const char *cmd) { krb5_principal server; krb5_data cksum_data; int status; size_t len; krb5_auth_context auth_context = NULL; const char *protocol_string = NULL; krb5_flags ap_opts; char *str; status = krb5_sname_to_principal(context, hostname, "host", KRB5_NT_SRV_HST, &server); if (status) { warnx ("%s: %s", hostname, krb5_get_err_text(context, status)); return 1; } if(do_encrypt == -1) { krb5_appdefault_boolean(context, NULL, krb5_principal_get_realm(context, server), "encrypt", FALSE, &do_encrypt); } cksum_data.length = asprintf (&str, "%u:%s%s%s", ntohs(socket_get_port(thataddr)), do_encrypt ? "-x " : "", cmd, remote_user); if (str == NULL) { warnx ("%s: failed to allocate command", hostname); return 1; } cksum_data.data = str; ap_opts = 0; if(do_encrypt) ap_opts |= AP_OPTS_MUTUAL_REQUIRED; switch(protocol_version) { case 2: ap_opts |= AP_OPTS_USE_SUBKEY; protocol_string = KCMD_NEW_VERSION; break; case 1: protocol_string = KCMD_OLD_VERSION; key_usage = KRB5_KU_OTHER_ENCRYPTED; break; default: abort(); } status = krb5_sendauth (context, &auth_context, &s, protocol_string, NULL, server, ap_opts, &cksum_data, NULL, NULL, NULL, NULL, NULL); /* do this while we have a principal */ if(do_forward == -1 || do_forwardable == -1) { krb5_const_realm realm = krb5_principal_get_realm(context, server); if (do_forwardable == -1) krb5_appdefault_boolean(context, NULL, realm, "forwardable", FALSE, &do_forwardable); if (do_forward == -1) krb5_appdefault_boolean(context, NULL, realm, "forward", FALSE, &do_forward); } krb5_free_principal(context, server); krb5_data_free(&cksum_data); if (status) { if(status == KRB5_SENDAUTH_REJECTED && protocol_version == 2 && protocol_version_str == NULL) sendauth_version_error = 1; else krb5_warn(context, status, "%s", hostname); return 1; } status = krb5_auth_con_getlocalsubkey (context, auth_context, &keyblock); if(keyblock == NULL) status = krb5_auth_con_getkey (context, auth_context, &keyblock); if (status) { warnx ("krb5_auth_con_getkey: %s", krb5_get_err_text(context, status)); return 1; } status = krb5_auth_con_setaddrs_from_fd (context, auth_context, &s); if (status) { warnx("krb5_auth_con_setaddrs_from_fd: %s", krb5_get_err_text(context, status)); return(1); } status = krb5_crypto_init(context, keyblock, 0, &crypto); if(status) { warnx ("krb5_crypto_init: %s", krb5_get_err_text(context, status)); return 1; } len = strlen(remote_user) + 1; if (net_write (s, remote_user, len) != len) { warn ("write"); return 1; } if (do_encrypt && net_write (s, "-x ", 3) != 3) { warn ("write"); return 1; } if (net_write (s, cmd, cmd_len) != cmd_len) { warn ("write"); return 1; } if (do_unique_tkfile) { if (net_write (s, tkfile, strlen(tkfile)) != strlen(tkfile)) { warn ("write"); return 1; } } len = strlen(local_user) + 1; if (net_write (s, local_user, len) != len) { warn ("write"); return 1; } if (!do_forward || krb5_forward_cred (auth_context, s, hostname, do_forwardable)) { /* Empty forwarding info */ u_char zero[4] = {0, 0, 0, 0}; write (s, &zero, 4); } krb5_auth_con_free (context, auth_context); return 0; }
Code_t ZCheckSrvAuthentication(ZNotice_t *notice, struct sockaddr_in *from, char *realm) { #ifdef HAVE_KRB5 unsigned char *authbuf; krb5_principal princ; krb5_data packet; krb5_ticket *tkt; char *name; krb5_error_code result; krb5_principal server; krb5_keytab keytabid = 0; krb5_auth_context authctx; krb5_keyblock *keyblock; krb5_enctype enctype; krb5_cksumtype cksumtype; krb5_data cksumbuf; int valid; char *cksum0_base, *cksum1_base = NULL, *cksum2_base; char *x; unsigned char *asn1_data, *key_data, *cksum_data; int asn1_len, key_len, cksum0_len = 0, cksum1_len = 0, cksum2_len = 0; KRB5_AUTH_CON_FLAGS_TYPE acflags; #ifdef KRB5_AUTH_CON_GETAUTHENTICATOR_TAKES_DOUBLE_POINTER krb5_authenticator *authenticator; #define KRB5AUTHENT authenticator #else krb5_authenticator authenticator; #define KRB5AUTHENT &authenticator #endif int len; char *sender; char rlmprincipal[MAX_PRINCIPAL_SIZE]; if (!notice->z_auth) return ZAUTH_NO; /* Check for bogus authentication data length. */ if (notice->z_authent_len <= 0) { syslog(LOG_DEBUG, "ZCheckSrvAuthentication: bogus authenticator length"); return ZAUTH_FAILED; } #ifdef HAVE_KRB4 if (notice->z_ascii_authent[0] != 'Z' && realm == NULL) return ZCheckAuthentication4(notice, from); #endif len = strlen(notice->z_ascii_authent)+1; authbuf = malloc(len); /* Read in the authentication data. */ if (ZReadZcode((unsigned char *)notice->z_ascii_authent, authbuf, len, &len) == ZERR_BADFIELD) { syslog(LOG_DEBUG, "ZCheckSrvAuthentication: ZReadZcode: Improperly formatted field"); return ZAUTH_FAILED; } if (realm == NULL) { sender = notice->z_sender; } else { (void) snprintf(rlmprincipal, MAX_PRINCIPAL_SIZE, "%s/%s@%s", SERVER_SERVICE, SERVER_INSTANCE, realm); sender = rlmprincipal; } packet.length = len; packet.data = (char *)authbuf; result = krb5_kt_resolve(Z_krb5_ctx, keytab_file, &keytabid); if (result) { free(authbuf); syslog(LOG_DEBUG, "ZCheckSrvAuthentication: krb5_kt_resolve: %s", error_message(result)); return ZAUTH_FAILED; } /* HOLDING: authbuf, keytabid */ /* Create the auth context */ result = krb5_auth_con_init(Z_krb5_ctx, &authctx); if (result) { krb5_kt_close(Z_krb5_ctx, keytabid); free(authbuf); syslog(LOG_DEBUG, "ZCheckSrvAuthentication: krb5_auth_con_init: %s", error_message(result)); return ZAUTH_FAILED; } /* HOLDING: authbuf, keytabid, authctx */ result = krb5_auth_con_getflags(Z_krb5_ctx, authctx, &acflags); if (result) { krb5_auth_con_free(Z_krb5_ctx, authctx); krb5_kt_close(Z_krb5_ctx, keytabid); free(authbuf); syslog(LOG_DEBUG, "ZCheckSrvAuthentication: krb5_auth_con_getflags: %s", error_message(result)); return ZAUTH_FAILED; } acflags &= ~KRB5_AUTH_CONTEXT_DO_TIME; result = krb5_auth_con_setflags(Z_krb5_ctx, authctx, acflags); if (result) { krb5_auth_con_free(Z_krb5_ctx, authctx); krb5_kt_close(Z_krb5_ctx, keytabid); free(authbuf); syslog(LOG_DEBUG, "ZCheckSrvAuthentication: krb5_auth_con_setflags: %s", error_message(result)); return ZAUTH_FAILED; } result = krb5_build_principal(Z_krb5_ctx, &server, strlen(__Zephyr_realm), __Zephyr_realm, SERVER_SERVICE, SERVER_INSTANCE, NULL); if (!result) { result = krb5_rd_req(Z_krb5_ctx, &authctx, &packet, server, keytabid, NULL, &tkt); krb5_free_principal(Z_krb5_ctx, server); } krb5_kt_close(Z_krb5_ctx, keytabid); /* HOLDING: authbuf, authctx */ if (result) { if (result == KRB5KRB_AP_ERR_REPEAT) syslog(LOG_DEBUG, "ZCheckSrvAuthentication: k5 auth failed: %s", error_message(result)); else syslog(LOG_WARNING,"ZCheckSrvAuthentication: k5 auth failed: %s", error_message(result)); free(authbuf); krb5_auth_con_free(Z_krb5_ctx, authctx); return ZAUTH_FAILED; } /* HOLDING: authbuf, authctx, tkt */ if (tkt == 0 || !Z_tktprincp(tkt)) { if (tkt) krb5_free_ticket(Z_krb5_ctx, tkt); free(authbuf); krb5_auth_con_free(Z_krb5_ctx, authctx); syslog(LOG_WARNING, "ZCheckSrvAuthentication: No Ticket"); return ZAUTH_FAILED; } princ = Z_tktprinc(tkt); if (princ == 0) { krb5_free_ticket(Z_krb5_ctx, tkt); free(authbuf); krb5_auth_con_free(Z_krb5_ctx, authctx); syslog(LOG_WARNING, "ZCheckSrvAuthentication: No... Ticket?"); return ZAUTH_FAILED; } /* HOLDING: authbuf, authctx, tkt */ result = krb5_unparse_name(Z_krb5_ctx, princ, &name); if (result) { syslog(LOG_WARNING, "ZCheckSrvAuthentication: krb5_unparse_name failed: %s", error_message(result)); free(authbuf); krb5_auth_con_free(Z_krb5_ctx, authctx); krb5_free_ticket(Z_krb5_ctx, tkt); return ZAUTH_FAILED; } krb5_free_ticket(Z_krb5_ctx, tkt); /* HOLDING: authbuf, authctx, name */ if (strcmp(name, sender)) { syslog(LOG_WARNING, "ZCheckSrvAuthentication: name mismatch: '%s' vs '%s'", name, sender); krb5_auth_con_free(Z_krb5_ctx, authctx); #ifdef HAVE_KRB5_FREE_UNPARSED_NAME krb5_free_unparsed_name(Z_krb5_ctx, name); #else free(name); #endif free(authbuf); return ZAUTH_FAILED; } #ifdef HAVE_KRB5_FREE_UNPARSED_NAME krb5_free_unparsed_name(Z_krb5_ctx, name); #else free(name); #endif free(authbuf); /* HOLDING: authctx */ /* Get an authenticator so we can get the keyblock */ result = krb5_auth_con_getauthenticator (Z_krb5_ctx, authctx, &authenticator); if (result) { krb5_auth_con_free(Z_krb5_ctx, authctx); syslog(LOG_WARNING, "ZCheckSrvAuthentication: krb5_auth_con_getauthenticator failed: %s", error_message(result)); return ZAUTH_FAILED; } /* HOLDING: authctx, authenticator */ result = krb5_auth_con_getkey(Z_krb5_ctx, authctx, &keyblock); if (result) { krb5_auth_con_free(Z_krb5_ctx, authctx); krb5_free_authenticator(Z_krb5_ctx, KRB5AUTHENT); syslog(LOG_WARNING, "ZCheckSrvAuthentication: krb5_auth_con_getkey failed: %s", error_message(result)); return (ZAUTH_FAILED); } /* HOLDING: authctx, authenticator, keyblock */ /* Figure out what checksum type to use */ key_data = Z_keydata(keyblock); key_len = Z_keylen(keyblock); result = Z_ExtractEncCksum(keyblock, &enctype, &cksumtype); if (result) { krb5_free_keyblock(Z_krb5_ctx, keyblock); krb5_auth_con_free(Z_krb5_ctx, authctx); krb5_free_authenticator(Z_krb5_ctx, KRB5AUTHENT); syslog(LOG_WARNING, "ZCheckSrvAuthentication: Z_ExtractEncCksum failed: %s", error_message(result)); return (ZAUTH_FAILED); } /* HOLDING: authctx, authenticator, keyblock */ if (realm == NULL) ZSetSession(keyblock); /* Assemble the things to be checksummed */ /* first part is from start of packet through z_default_format: * - z_version * - z_num_other_fields * - z_kind * - z_uid * - z_port * - z_auth * - z_authent_len * - z_ascii_authent * - z_class * - z_class_inst * - z_opcode * - z_sender * - z_recipient * - z_default_format */ cksum0_base = notice->z_packet; x = notice->z_default_format; cksum0_len = x + strlen(x) + 1 - cksum0_base; /* second part is from z_multinotice through other fields: * - z_multinotice * - z_multiuid * - z_sender_(sock)addr * - z_charset * - z_other_fields[] */ if (notice->z_num_hdr_fields > 15 ) { cksum1_base = notice->z_multinotice; if (notice->z_num_other_fields) x = notice->z_other_fields[notice->z_num_other_fields - 1]; else { /* see also ZCheckRealmAuthentication and lib/ZCkZaut.c:ZCheckZcodeAuthentication */ /* XXXXXXXXXXXXXXXXXXXXXXX */ if (notice->z_num_hdr_fields > 16) x = cksum1_base + strlen(cksum1_base) + 1; /* multinotice */ if (notice->z_num_hdr_fields > 17) x = x + strlen(x) + 1; /* multiuid */ if (notice->z_num_hdr_fields > 18) x = x + strlen(x) + 1; /* sender */ } cksum1_len = x + strlen(x) + 1 - cksum1_base; /* charset / extra field */ } /* last part is the message body */ cksum2_base = notice->z_message; cksum2_len = notice->z_message_len; /*XXX we may wish to ditch this code someday?*/ if ((!notice->z_ascii_checksum || *notice->z_ascii_checksum != 'Z') && key_len == 8 && (enctype == (krb5_enctype)ENCTYPE_DES_CBC_CRC || enctype == (krb5_enctype)ENCTYPE_DES_CBC_MD4 || enctype == (krb5_enctype)ENCTYPE_DES_CBC_MD5)) { /* try old-format checksum (covers cksum0 only) */ ZChecksum_t our_checksum; if (realm == NULL) our_checksum = compute_checksum(notice, key_data); else our_checksum = compute_rlm_checksum(notice, key_data); krb5_free_keyblock(Z_krb5_ctx, keyblock); krb5_auth_con_free(Z_krb5_ctx, authctx); krb5_free_authenticator(Z_krb5_ctx, KRB5AUTHENT); if (our_checksum == notice->z_checksum) { return ZAUTH_YES; } else { syslog(LOG_DEBUG, "ZCheckSrvAuthentication: des quad checksum mismatch"); return ZAUTH_FAILED; } } /* HOLDING: authctx, authenticator */ cksumbuf.length = cksum0_len + cksum1_len + cksum2_len; cksumbuf.data = malloc(cksumbuf.length); if (!cksumbuf.data) { krb5_free_keyblock(Z_krb5_ctx, keyblock); krb5_auth_con_free(Z_krb5_ctx, authctx); krb5_free_authenticator(Z_krb5_ctx, KRB5AUTHENT); syslog(LOG_ERR, "ZCheckSrvAuthentication: malloc(cksumbuf.data): %m"); return ZAUTH_FAILED; } /* HOLDING: authctx, authenticator, cksumbuf.data */ cksum_data = (unsigned char *)cksumbuf.data; memcpy(cksum_data, cksum0_base, cksum0_len); if (cksum1_len) memcpy(cksum_data + cksum0_len, cksum1_base, cksum1_len); memcpy(cksum_data + cksum0_len + cksum1_len, cksum2_base, cksum2_len); /* decode zcoded checksum */ /* The encoded form is always longer than the original */ asn1_len = strlen(notice->z_ascii_checksum) + 1; asn1_data = malloc(asn1_len); if (!asn1_data) { krb5_free_keyblock(Z_krb5_ctx, keyblock); krb5_auth_con_free(Z_krb5_ctx, authctx); krb5_free_authenticator(Z_krb5_ctx, KRB5AUTHENT); free(cksumbuf.data); syslog(LOG_ERR, "ZCheckSrvAuthentication: malloc(asn1_data): %m"); return ZAUTH_FAILED; } /* HOLDING: authctx, authenticator, cksumbuf.data, asn1_data */ result = ZReadZcode((unsigned char *)notice->z_ascii_checksum, asn1_data, asn1_len, &asn1_len); if (result != ZERR_NONE) { krb5_free_keyblock(Z_krb5_ctx, keyblock); krb5_auth_con_free(Z_krb5_ctx, authctx); krb5_free_authenticator(Z_krb5_ctx, KRB5AUTHENT); free(asn1_data); free(cksumbuf.data); syslog(LOG_WARNING, "ZCheckSrvAuthentication: ZReadZcode: %s", error_message(result)); return ZAUTH_FAILED; } /* HOLDING: asn1_data, cksumbuf.data, authctx, authenticator */ valid = Z_krb5_verify_cksum(keyblock, &cksumbuf, cksumtype, Z_KEYUSAGE_CLT_CKSUM, asn1_data, asn1_len); /* XXX compatibility with unreleased interrealm krb5; drop in 3.1 */ if (!valid && realm) valid = Z_krb5_verify_cksum(keyblock, &cksumbuf, cksumtype, Z_KEYUSAGE_SRV_CKSUM, asn1_data, asn1_len); free(asn1_data); krb5_auth_con_free(Z_krb5_ctx, authctx); krb5_free_authenticator(Z_krb5_ctx, KRB5AUTHENT); krb5_free_keyblock(Z_krb5_ctx, keyblock); free(cksumbuf.data); if (valid) { return ZAUTH_YES; } else { syslog(LOG_DEBUG, "ZCheckSrvAuthentication: Z_krb5_verify_cksum: failed"); return ZAUTH_FAILED; } #else return (notice->z_auth) ? ZAUTH_YES : ZAUTH_NO; #endif }
int kerberos5_is_auth (TN_Authenticator * ap, unsigned char *data, int cnt, char *errbuf, int errbuflen) { int r = 0; krb5_keytab keytabid = 0; krb5_authenticator *authenticator; char *name; krb5_data outbuf; krb5_keyblock *newkey = NULL; krb5_principal server; # ifdef ENCRYPTION Session_Key skey; # endif auth.data = (char *) data; auth.length = cnt; if (!r && !auth_context) r = krb5_auth_con_init (telnet_context, &auth_context); if (!r) { krb5_rcache rcache; r = krb5_auth_con_getrcache (telnet_context, auth_context, &rcache); if (!r && !rcache) { r = krb5_sname_to_principal (telnet_context, 0, 0, KRB5_NT_SRV_HST, &server); if (!r) { r = krb5_get_server_rcache (telnet_context, krb5_princ_component (telnet_context, server, 0), &rcache); krb5_free_principal (telnet_context, server); } } if (!r) r = krb5_auth_con_setrcache (telnet_context, auth_context, rcache); } if (!r && telnet_srvtab) r = krb5_kt_resolve (telnet_context, telnet_srvtab, &keytabid); if (!r) r = krb5_rd_req (telnet_context, &auth_context, &auth, NULL, keytabid, NULL, &ticket); if (r) { snprintf (errbuf, errbuflen, "krb5_rd_req failed: %s", error_message (r)); return r; } /* 256 bytes should be much larger than any reasonable first component of a service name especially since the default is of length 4. */ if (krb5_princ_component (telnet_context, ticket->server, 0)->length < 256) { char princ[256]; strncpy (princ, krb5_princ_component (telnet_context, ticket->server, 0)->data, krb5_princ_component (telnet_context, ticket->server, 0)->length); princ[krb5_princ_component (telnet_context, ticket->server, 0)-> length] = '\0'; if (strcmp ("host", princ)) { snprintf (errbuf, errbuflen, "incorrect service name: \"%s\" != \"host\"", princ); return 1; } } else { strncpy (errbuf, "service name too long", errbuflen); return 1; } r = krb5_auth_con_getauthenticator (telnet_context, auth_context, &authenticator); if (r) { snprintf (errbuf, errbuflen, "krb5_auth_con_getauthenticator failed: %s", error_message (r)); return 1; } # ifdef AUTH_ENCRYPT_MASK if ((ap->way & AUTH_ENCRYPT_MASK) == AUTH_ENCRYPT_ON && !authenticator->checksum) { snprintf (errbuf, errbuflen, "authenticator is missing required checksum"); return 1; } # endif if (authenticator->checksum) { char type_check[2]; krb5_checksum *cksum = authenticator->checksum; krb5_keyblock *key; type_check[0] = ap->type; type_check[1] = ap->way; r = krb5_auth_con_getkey (telnet_context, auth_context, &key); if (r) { snprintf (errbuf, errbuflen, "krb5_auth_con_getkey failed: %s", error_message (r)); return 1; } r = krb5_verify_checksum (telnet_context, cksum->checksum_type, cksum, &type_check, 2, key->contents, key->length); if (r) { snprintf (errbuf, errbuflen, "checksum verification failed: %s", error_message (r)); return 1; } krb5_free_keyblock (telnet_context, key); } krb5_free_authenticator (telnet_context, authenticator); if ((ap->way & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL) { if ((r = krb5_mk_rep (telnet_context, auth_context, &outbuf))) { snprintf (errbuf, errbuflen, "Make reply failed: %s", error_message (r)); return 1; } Data (ap, KRB_RESPONSE, outbuf.data, outbuf.length); } if (krb5_unparse_name (telnet_context, ticket->enc_part2->client, &name)) name = 0; Data (ap, KRB_ACCEPT, name, name ? -1 : 0); DEBUG (("telnetd: Kerberos5 identifies him as ``%s''\r\n", name ? name : "")); auth_finished (ap, AUTH_USER); free (name); krb5_auth_con_getremotesubkey (telnet_context, auth_context, &newkey); if (session_key) { krb5_free_keyblock (telnet_context, session_key); session_key = 0; } if (newkey) { krb5_copy_keyblock (telnet_context, newkey, &session_key); krb5_free_keyblock (telnet_context, newkey); } else { krb5_copy_keyblock (telnet_context, ticket->enc_part2->session, &session_key); } telnet_encrypt_key (&skey); return 0; }