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; }
static krb5_error_code tgs_check_authenticator(krb5_context context, krb5_kdc_configuration *config, krb5_auth_context ac, KDC_REQ_BODY *b, const char **e_text, krb5_keyblock *key) { krb5_authenticator auth; size_t len; unsigned char *buf; size_t buf_size; krb5_error_code ret; krb5_crypto crypto; krb5_auth_con_getauthenticator(context, ac, &auth); if(auth->cksum == NULL){ kdc_log(context, config, 0, "No authenticator in request"); ret = KRB5KRB_AP_ERR_INAPP_CKSUM; goto out; } /* * according to RFC1510 it doesn't need to be keyed, * but according to the latest draft it needs to. */ if ( #if 0 !krb5_checksum_is_keyed(context, auth->cksum->cksumtype) || #endif !krb5_checksum_is_collision_proof(context, auth->cksum->cksumtype)) { kdc_log(context, config, 0, "Bad checksum type in authenticator: %d", auth->cksum->cksumtype); ret = KRB5KRB_AP_ERR_INAPP_CKSUM; goto out; } /* XXX should not re-encode this */ ASN1_MALLOC_ENCODE(KDC_REQ_BODY, buf, buf_size, b, &len, ret); if(ret){ kdc_log(context, config, 0, "Failed to encode KDC-REQ-BODY: %s", krb5_get_err_text(context, ret)); goto out; } if(buf_size != len) { free(buf); kdc_log(context, config, 0, "Internal error in ASN.1 encoder"); *e_text = "KDC internal error"; ret = KRB5KRB_ERR_GENERIC; goto out; } ret = krb5_crypto_init(context, key, 0, &crypto); if (ret) { free(buf); kdc_log(context, config, 0, "krb5_crypto_init failed: %s", krb5_get_err_text(context, ret)); goto out; } ret = krb5_verify_checksum(context, crypto, KRB5_KU_TGS_REQ_AUTH_CKSUM, buf, len, auth->cksum); free(buf); krb5_crypto_destroy(context, crypto); if(ret){ kdc_log(context, config, 0, "Failed to verify authenticator checksum: %s", krb5_get_err_text(context, ret)); } out: free_Authenticator(auth); free(auth); return ret; }
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; }
int main(int argc, char **argv) { krb5_error_code ret; krb5_context context; krb5_auth_context ac = NULL; krb5_principal c1, c2; krb5_authenticator authent; krb5_keytab keytab; krb5_socket_t sock = rk_INVALID_SOCKET; HDB *db = NULL; int optidx = 0; char *tmp_db; krb5_log_facility *fac; int nprincs; setprogname(argv[0]); ret = krb5_init_context(&context); if(ret) exit(1); ret = krb5_openlog(context, "hpropd", &fac); if(ret) errx(1, "krb5_openlog"); krb5_set_warn_dest(context, fac); if(getarg(args, num_args, argc, argv, &optidx)) usage(1); if(local_realm != NULL) krb5_set_default_realm(context, local_realm); if(help_flag) usage(0); if(version_flag) { print_version(NULL); exit(0); } argc -= optidx; argv += optidx; if (argc != 0) usage(1); if (database == NULL) database = hdb_default_db(context); if(from_stdin) { sock = STDIN_FILENO; } else { struct sockaddr_storage ss; struct sockaddr *sa = (struct sockaddr *)&ss; socklen_t sin_len = sizeof(ss); char addr_name[256]; krb5_ticket *ticket; char *server; sock = STDIN_FILENO; #ifdef SUPPORT_INETD if (inetd_flag == -1) { if (getpeername (sock, sa, &sin_len) < 0) { inetd_flag = 0; } else { inetd_flag = 1; } } #else inetd_flag = 0; #endif if (!inetd_flag) { mini_inetd (krb5_getportbyname (context, "hprop", "tcp", HPROP_PORT), &sock); } sin_len = sizeof(ss); if(getpeername(sock, sa, &sin_len) < 0) krb5_err(context, 1, errno, "getpeername"); if (inet_ntop(sa->sa_family, socket_get_address (sa), addr_name, sizeof(addr_name)) == NULL) strlcpy (addr_name, "unknown address", sizeof(addr_name)); krb5_log(context, fac, 0, "Connection from %s", addr_name); ret = krb5_kt_register(context, &hdb_kt_ops); if(ret) krb5_err(context, 1, ret, "krb5_kt_register"); if (ktname != NULL) { ret = krb5_kt_resolve(context, ktname, &keytab); if (ret) krb5_err (context, 1, ret, "krb5_kt_resolve %s", ktname); } else { ret = krb5_kt_default (context, &keytab); if (ret) krb5_err (context, 1, ret, "krb5_kt_default"); } ret = krb5_recvauth(context, &ac, &sock, HPROP_VERSION, NULL, 0, keytab, &ticket); if(ret) krb5_err(context, 1, ret, "krb5_recvauth"); ret = krb5_unparse_name(context, ticket->server, &server); if (ret) krb5_err(context, 1, ret, "krb5_unparse_name"); if (strncmp(server, "hprop/", 5) != 0) krb5_errx(context, 1, "ticket not for hprop (%s)", server); free(server); krb5_free_ticket (context, ticket); ret = krb5_auth_con_getauthenticator(context, ac, &authent); if(ret) krb5_err(context, 1, ret, "krb5_auth_con_getauthenticator"); ret = krb5_make_principal(context, &c1, NULL, "kadmin", "hprop", NULL); if(ret) krb5_err(context, 1, ret, "krb5_make_principal"); _krb5_principalname2krb5_principal(context, &c2, authent->cname, authent->crealm); if(!krb5_principal_compare(context, c1, c2)) { char *s; ret = krb5_unparse_name(context, c2, &s); if (ret) s = unparseable_name; krb5_errx(context, 1, "Unauthorized connection from %s", s); } krb5_free_principal(context, c1); krb5_free_principal(context, c2); ret = krb5_kt_close(context, keytab); if(ret) krb5_err(context, 1, ret, "krb5_kt_close"); } if(!print_dump) { asprintf(&tmp_db, "%s~", database); ret = hdb_create(context, &db, tmp_db); if(ret) krb5_err(context, 1, ret, "hdb_create(%s)", tmp_db); ret = db->hdb_open(context, db, O_RDWR | O_CREAT | O_TRUNC, 0600); if(ret) krb5_err(context, 1, ret, "hdb_open(%s)", tmp_db); } nprincs = 0; while(1){ krb5_data data; hdb_entry_ex entry; if(from_stdin) { ret = krb5_read_message(context, &sock, &data); if(ret != 0 && ret != HEIM_ERR_EOF) krb5_err(context, 1, ret, "krb5_read_message"); } else { ret = krb5_read_priv_message(context, ac, &sock, &data); if(ret) krb5_err(context, 1, ret, "krb5_read_priv_message"); } if(ret == HEIM_ERR_EOF || data.length == 0) { if(!from_stdin) { data.data = NULL; data.length = 0; krb5_write_priv_message(context, ac, &sock, &data); } if(!print_dump) { ret = db->hdb_close(context, db); if(ret) krb5_err(context, 1, ret, "db_close"); ret = db->hdb_rename(context, db, database); if(ret) krb5_err(context, 1, ret, "db_rename"); } break; } memset(&entry, 0, sizeof(entry)); ret = hdb_value2entry(context, &data, &entry.entry); krb5_data_free(&data); if(ret) krb5_err(context, 1, ret, "hdb_value2entry"); if(print_dump) hdb_print_entry(context, db, &entry, stdout); else { ret = db->hdb_store(context, db, 0, &entry); if(ret == HDB_ERR_EXISTS) { char *s; ret = krb5_unparse_name(context, entry.entry.principal, &s); if (ret) s = strdup(unparseable_name); krb5_warnx(context, "Entry exists: %s", s); free(s); } else if(ret) krb5_err(context, 1, ret, "db_store"); else nprincs++; } hdb_free_entry(context, &entry); } if (!print_dump) krb5_log(context, fac, 0, "Received %d principals", nprincs); if (inetd_flag == 0) rk_closesocket(sock); exit(0); }
int do_krb5_login (int infd, struct auth_data *ap, const char **err_msg) { krb5_auth_context auth_ctx = NULL; krb5_error_code status; krb5_data inbuf; krb5_data version; krb5_authenticator *authenticator; krb5_rcache rcache; krb5_keyblock *key; krb5_ticket *ticket; struct sockaddr_in laddr; int len; struct passwd *pwd; char *name; if (status = krb5_init_context (&ap->context)) { syslog (LOG_ERR, "Error initializing krb5: %s", error_message (status)); return status; } if ((status = krb5_auth_con_init (ap->context, &auth_ctx)) || (status = krb5_auth_con_genaddrs (ap->context, auth_ctx, infd, KRB5_AUTH_CONTEXT_GENERATE_REMOTE_FULL_ADDR)) || (status = krb5_auth_con_getrcache (ap->context, auth_ctx, &rcache))) return status; if (!rcache) { krb5_principal server; status = krb5_sname_to_principal (ap->context, 0, 0, KRB5_NT_SRV_HST, &server); if (status) return status; status = krb5_get_server_rcache (ap->context, krb5_princ_component (ap->context, server, 0), &rcache); krb5_free_principal (ap->context, server); if (status) return status; status = krb5_auth_con_setrcache (ap->context, auth_ctx, rcache); if (status) return status; } len = sizeof (laddr); if (getsockname (infd, (struct sockaddr *) &laddr, &len)) return errno; status = krb5_recvauth (ap->context, &auth_ctx, &infd, NULL, 0, 0, ap->keytab, &ticket); if (status) return status; if ((status = krb5_auth_con_getauthenticator (ap->context, auth_ctx, &authenticator))) return status; getstr (infd, &ap->lusername, NULL); getstr (infd, &ap->term, "TERM="); pwd = getpwnam (ap->lusername); if (pwd == NULL) { *err_msg = "getpwnam failed"; syslog (LOG_ERR, "getpwnam failed: %m"); return 1; } getstr (infd, &ap->rusername, NULL); if ((status = krb5_copy_principal (ap->context, ticket->enc_part2->client, &ap->client))) return status; /*OK:: */ if (ap->client && !krb5_kuserok (ap->context, ap->client, ap->lusername)) return 1; krb5_unparse_name (ap->context, ap->client, &name); syslog (LOG_INFO | LOG_AUTH, "%sKerberos V login from %s on %s\n", (pwd->pw_uid == 0) ? "ROOT " : "", name, ap->hostname); free (name); return 0; }
static OM_uint32 gsskrb5_acceptor_start(OM_uint32 * minor_status, gsskrb5_ctx ctx, krb5_context context, const gss_cred_id_t acceptor_cred_handle, const gss_buffer_t input_token_buffer, const gss_channel_bindings_t input_chan_bindings, gss_name_t * src_name, gss_OID * mech_type, gss_buffer_t output_token, OM_uint32 * ret_flags, OM_uint32 * time_rec, gss_cred_id_t * delegated_cred_handle) { krb5_error_code kret; OM_uint32 ret = GSS_S_COMPLETE; krb5_data indata; krb5_flags ap_options; krb5_keytab keytab = NULL; int is_cfx = 0; const gsskrb5_cred acceptor_cred = (gsskrb5_cred)acceptor_cred_handle; /* * We may, or may not, have an escapsulation. */ ret = _gsskrb5_decapsulate (minor_status, input_token_buffer, &indata, "\x01\x00", GSS_KRB5_MECHANISM); if (ret) { /* Assume that there is no OID wrapping. */ indata.length = input_token_buffer->length; indata.data = input_token_buffer->value; } /* * We need to get our keytab */ if (acceptor_cred == NULL) { if (_gsskrb5_keytab != NULL) keytab = _gsskrb5_keytab; } else if (acceptor_cred->keytab != NULL) { keytab = acceptor_cred->keytab; } /* * We need to check the ticket and create the AP-REP packet */ { krb5_rd_req_in_ctx in = NULL; krb5_rd_req_out_ctx out = NULL; krb5_principal server = NULL; if (acceptor_cred) server = acceptor_cred->principal; kret = krb5_rd_req_in_ctx_alloc(context, &in); if (kret == 0) kret = krb5_rd_req_in_set_keytab(context, in, keytab); if (kret) { if (in) krb5_rd_req_in_ctx_free(context, in); *minor_status = kret; return GSS_S_FAILURE; } kret = krb5_rd_req_ctx(context, &ctx->auth_context, &indata, server, in, &out); krb5_rd_req_in_ctx_free(context, in); if (kret == KRB5KRB_AP_ERR_SKEW || kret == KRB5KRB_AP_ERR_TKT_NYV) { /* * No reply in non-MUTUAL mode, but we don't know that its * non-MUTUAL mode yet, thats inside the 8003 checksum, so * lets only send the error token on clock skew, that * limit when send error token for non-MUTUAL. */ return send_error_token(minor_status, context, kret, server, &indata, output_token); } else if (kret) { *minor_status = kret; return GSS_S_FAILURE; } /* * we need to remember some data on the context_handle. */ kret = krb5_rd_req_out_get_ap_req_options(context, out, &ap_options); if (kret == 0) kret = krb5_rd_req_out_get_ticket(context, out, &ctx->ticket); if (kret == 0) kret = krb5_rd_req_out_get_keyblock(context, out, &ctx->service_keyblock); ctx->lifetime = ctx->ticket->ticket.endtime; krb5_rd_req_out_ctx_free(context, out); if (kret) { ret = GSS_S_FAILURE; *minor_status = kret; return ret; } } /* * We need to copy the principal names to the context and the * calling layer. */ kret = krb5_copy_principal(context, ctx->ticket->client, &ctx->source); if (kret) { ret = GSS_S_FAILURE; *minor_status = kret; } kret = krb5_copy_principal(context, ctx->ticket->server, &ctx->target); if (kret) { ret = GSS_S_FAILURE; *minor_status = kret; return ret; } /* * We need to setup some compat stuff, this assumes that * context_handle->target is already set. */ ret = _gss_DES3_get_mic_compat(minor_status, ctx, context); if (ret) return ret; if (src_name != NULL) { kret = krb5_copy_principal (context, ctx->ticket->client, (gsskrb5_name*)src_name); if (kret) { ret = GSS_S_FAILURE; *minor_status = kret; return ret; } } /* * We need to get the flags out of the 8003 checksum. */ { krb5_authenticator authenticator; kret = krb5_auth_con_getauthenticator(context, ctx->auth_context, &authenticator); if(kret) { ret = GSS_S_FAILURE; *minor_status = kret; return ret; } if (authenticator->cksum == NULL) { krb5_free_authenticator(context, &authenticator); *minor_status = 0; return GSS_S_BAD_BINDINGS; } if (authenticator->cksum->cksumtype == CKSUMTYPE_GSSAPI) { ret = _gsskrb5_verify_8003_checksum(minor_status, input_chan_bindings, authenticator->cksum, &ctx->flags, &ctx->fwd_data); krb5_free_authenticator(context, &authenticator); if (ret) { return ret; } } else { krb5_crypto crypto; kret = krb5_crypto_init(context, ctx->auth_context->keyblock, 0, &crypto); if(kret) { krb5_free_authenticator(context, &authenticator); ret = GSS_S_FAILURE; *minor_status = kret; return ret; } /* * Windows accepts Samba3's use of a kerberos, rather than * GSSAPI checksum here */ kret = krb5_verify_checksum(context, crypto, KRB5_KU_AP_REQ_AUTH_CKSUM, NULL, 0, authenticator->cksum); krb5_free_authenticator(context, &authenticator); krb5_crypto_destroy(context, crypto); if(kret) { ret = GSS_S_BAD_SIG; *minor_status = kret; return ret; } /* * Samba style get some flags (but not DCE-STYLE), use * ap_options to guess the mutual flag. */ ctx->flags = GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG; if (ap_options & AP_OPTS_MUTUAL_REQUIRED) ctx->flags |= GSS_C_MUTUAL_FLAG; } } if(ctx->flags & GSS_C_MUTUAL_FLAG) { krb5_data outbuf; int use_subkey = 0; _gsskrb5i_is_cfx(context, ctx, 1); is_cfx = (ctx->more_flags & IS_CFX); if (is_cfx || (ap_options & AP_OPTS_USE_SUBKEY)) { use_subkey = 1; } else { krb5_keyblock *rkey; /* * If there is a initiator subkey, copy that to acceptor * subkey to match Windows behavior */ kret = krb5_auth_con_getremotesubkey(context, ctx->auth_context, &rkey); if (kret == 0) { kret = krb5_auth_con_setlocalsubkey(context, ctx->auth_context, rkey); if (kret == 0) use_subkey = 1; krb5_free_keyblock(context, rkey); } } if (use_subkey) { ctx->more_flags |= ACCEPTOR_SUBKEY; krb5_auth_con_addflags(context, ctx->auth_context, KRB5_AUTH_CONTEXT_USE_SUBKEY, NULL); } kret = krb5_mk_rep(context, ctx->auth_context, &outbuf); if (kret) { *minor_status = kret; return GSS_S_FAILURE; } if (IS_DCE_STYLE(ctx)) { output_token->length = outbuf.length; output_token->value = outbuf.data; } else { ret = _gsskrb5_encapsulate(minor_status, &outbuf, output_token, "\x02\x00", GSS_KRB5_MECHANISM); krb5_data_free (&outbuf); if (ret) return ret; } } ctx->flags |= GSS_C_TRANS_FLAG; /* Remember the flags */ ctx->lifetime = ctx->ticket->ticket.endtime; ctx->more_flags |= OPEN; if (mech_type) *mech_type = GSS_KRB5_MECHANISM; if (time_rec) { ret = _gsskrb5_lifetime_left(minor_status, context, ctx->lifetime, time_rec); if (ret) { return ret; } } /* * When GSS_C_DCE_STYLE is in use, we need ask for a AP-REP from * the client. */ if (IS_DCE_STYLE(ctx)) { /* * Return flags to caller, but we haven't processed * delgations yet */ if (ret_flags) *ret_flags = (ctx->flags & ~GSS_C_DELEG_FLAG); ctx->state = ACCEPTOR_WAIT_FOR_DCESTYLE; return GSS_S_CONTINUE_NEEDED; } ret = gsskrb5_acceptor_ready(minor_status, ctx, context, delegated_cred_handle); if (ret_flags) *ret_flags = ctx->flags; return ret; }
static OM_uint32 gsskrb5_acceptor_start(OM_uint32 * minor_status, gsskrb5_ctx ctx, krb5_context context, const gss_cred_id_t acceptor_cred_handle, const gss_buffer_t input_token_buffer, const gss_channel_bindings_t input_chan_bindings, gss_name_t * src_name, gss_OID * mech_type, gss_buffer_t output_token, OM_uint32 * ret_flags, OM_uint32 * time_rec, gss_cred_id_t * delegated_cred_handle) { krb5_error_code kret; OM_uint32 ret = GSS_S_COMPLETE; krb5_data indata; krb5_flags ap_options; krb5_keytab keytab = NULL; int is_cfx = 0; const gsskrb5_cred acceptor_cred = (gsskrb5_cred)acceptor_cred_handle; krb5_boolean is_hostbased_service = FALSE; /* * We may, or may not, have an escapsulation. */ ret = _gsskrb5_decapsulate (minor_status, input_token_buffer, &indata, "\x01\x00", ctx->mech); if (ret) { /* Assume that there is no OID wrapping. */ indata.length = input_token_buffer->length; indata.data = input_token_buffer->value; } /* * We need to get our keytab */ if (acceptor_cred == NULL) { if (_gsskrb5_keytab != NULL) keytab = _gsskrb5_keytab; } else if (acceptor_cred->keytab != NULL) { keytab = acceptor_cred->keytab; } is_hostbased_service = (acceptor_cred && acceptor_cred->principal && krb5_principal_is_gss_hostbased_service(context, acceptor_cred->principal)); /* * We need to check the ticket and create the AP-REP packet */ { krb5_rd_req_in_ctx in = NULL; krb5_rd_req_out_ctx out = NULL; krb5_principal server = NULL; if (acceptor_cred && !is_hostbased_service) server = acceptor_cred->principal; kret = krb5_rd_req_in_ctx_alloc(context, &in); if (kret == 0) kret = krb5_rd_req_in_set_keytab(context, in, keytab); if (kret) { if (in) krb5_rd_req_in_ctx_free(context, in); *minor_status = kret; return GSS_S_FAILURE; } kret = krb5_rd_req_ctx(context, &ctx->auth_context, &indata, server, in, &out); krb5_rd_req_in_ctx_free(context, in); if (ret && _gss_mg_log_level(5)) { const char *e = krb5_get_error_message(context, ret); char *s = NULL; if (server) (void)krb5_unparse_name(context, server, &s); _gss_mg_log(5, "gss-asc: rd_req (server: %s) failed with: %d: %s", s ? s : "<not specified>", ret, e); krb5_free_error_message(context, e); if (s) krb5_xfree(s); } switch (kret) { case 0: break; case KRB5KRB_AP_ERR_SKEW: case KRB5KRB_AP_ERR_TKT_NYV: /* * No reply in non-MUTUAL mode, but we don't know that its * non-MUTUAL mode yet, thats inside the 8003 checksum, so * lets only send the error token on clock skew, that * limit when send error token for non-MUTUAL. */ return send_error_token(minor_status, context, kret, server, &indata, ctx->mech, output_token); case KRB5KRB_AP_ERR_MODIFIED: case KRB5_KT_NOTFOUND: case KRB5_KT_END: /* * If the error is on the keytab entry missing or bad * decryption, lets assume that the keytab version was * wrong and tell the client that. */ return send_error_token(minor_status, context, KRB5KRB_AP_ERR_MODIFIED, server, NULL, ctx->mech, output_token); default: *minor_status = kret; return GSS_S_FAILURE; } /* * we need to remember some data on the context_handle. */ kret = krb5_rd_req_out_get_ap_req_options(context, out, &ap_options); if (kret == 0) kret = krb5_rd_req_out_get_ticket(context, out, &ctx->ticket); if (kret == 0) kret = krb5_rd_req_out_get_keyblock(context, out, &ctx->service_keyblock); if (kret == 0) { int flags; flags = krb5_rd_req_out_get_flags(context, out); if (flags & KRB5_RD_REQ_OUT_PAC_VALID) ctx->more_flags |= PAC_VALID; } if (kret == 0 && is_hostbased_service) { krb5_principal sp = ctx->ticket->server; if (sp->name.name_string.len < 1 || strcmp(sp->name.name_string.val[0], acceptor_cred->principal->name.name_string.val[0]) != 0) { kret = KRB5KRB_AP_WRONG_PRINC; krb5_set_error_message(context, ret, "Expecting service %s but got %s", acceptor_cred->principal->name.name_string.val[0], sp->name.name_string.val[0]); } } ctx->endtime = ctx->ticket->ticket.endtime; krb5_rd_req_out_ctx_free(context, out); if (kret) { ret = GSS_S_FAILURE; *minor_status = kret; return ret; } } /* * We need to copy the principal names to the context and the * calling layer. */ kret = krb5_copy_principal(context, ctx->ticket->client, &ctx->source); if (kret) { *minor_status = kret; return GSS_S_FAILURE; } kret = krb5_copy_principal(context, ctx->ticket->server, &ctx->target); if (kret) { ret = GSS_S_FAILURE; *minor_status = kret; return ret; } /* * We need to setup some compat stuff, this assumes that * context_handle->target is already set. */ ret = _gss_DES3_get_mic_compat(minor_status, ctx, context); if (ret) return ret; if (src_name != NULL) { kret = krb5_copy_principal (context, ctx->ticket->client, (gsskrb5_name*)src_name); if (kret) { ret = GSS_S_FAILURE; *minor_status = kret; return ret; } } /* * We need to get the flags out of the 8003 checksum. */ { krb5_authenticator authenticator; kret = krb5_auth_con_getauthenticator(context, ctx->auth_context, &authenticator); if(kret) { ret = GSS_S_FAILURE; *minor_status = kret; return ret; } if (authenticator->cksum == NULL) { krb5_free_authenticator(context, &authenticator); *minor_status = 0; return GSS_S_BAD_BINDINGS; } if (authenticator->cksum->cksumtype == CKSUMTYPE_GSSAPI) { krb5_data finished_data; krb5_crypto crypto = NULL; if (ctx->auth_context->remote_subkey) { kret = krb5_crypto_init(context, ctx->auth_context->remote_subkey, 0, &crypto); if (kret) { *minor_status = kret; return GSS_S_FAILURE; } } krb5_data_zero(&finished_data); ret = _gsskrb5_verify_8003_checksum(minor_status, context, crypto, input_chan_bindings, authenticator->cksum, &ctx->flags, &ctx->fwd_data, &finished_data); krb5_free_authenticator(context, &authenticator); if (ret) { krb5_crypto_destroy(context, crypto); return ret; } if (finished_data.length) { GSS_KRB5_FINISHED finished; krb5_data pkt; memset(&finished, 0, sizeof(finished)); if (ctx->messages == NULL) { krb5_crypto_destroy(context, crypto); krb5_data_free(&finished_data); *minor_status = 0; return GSS_S_BAD_SIG; } kret = krb5_storage_to_data(ctx->messages, &pkt); if (kret) { krb5_crypto_destroy(context, crypto); krb5_data_free(&finished_data); *minor_status = kret; return GSS_S_FAILURE; } if (ctx->auth_context->remote_subkey == NULL) { krb5_crypto_destroy(context, crypto); krb5_data_free(&finished_data); krb5_data_free(&pkt); *minor_status = 0; return GSS_S_BAD_SIG; } kret = decode_GSS_KRB5_FINISHED(finished_data.data, finished_data.length, &finished, NULL); krb5_data_free(&finished_data); if (kret) { krb5_crypto_destroy(context, crypto); krb5_data_free(&pkt); *minor_status = kret; return GSS_S_FAILURE; } kret = krb5_verify_checksum(context, crypto, KRB5_KU_FINISHED, pkt.data, pkt.length, &finished.gss_mic); free_GSS_KRB5_FINISHED(&finished); krb5_data_free(&pkt); if (kret) { krb5_crypto_destroy(context, crypto); *minor_status = kret; return GSS_S_FAILURE; } } krb5_crypto_destroy(context, crypto); } else { krb5_crypto crypto; kret = krb5_crypto_init(context, ctx->auth_context->keyblock, 0, &crypto); if(kret) { krb5_free_authenticator(context, &authenticator); ret = GSS_S_FAILURE; *minor_status = kret; return ret; } /* * Windows accepts Samba3's use of a kerberos, rather than * GSSAPI checksum here */ kret = krb5_verify_checksum(context, crypto, KRB5_KU_AP_REQ_AUTH_CKSUM, NULL, 0, authenticator->cksum); krb5_free_authenticator(context, &authenticator); krb5_crypto_destroy(context, crypto); if(kret) { ret = GSS_S_BAD_SIG; *minor_status = kret; return ret; } /* * Samba style get some flags (but not DCE-STYLE), use * ap_options to guess the mutual flag. */ ctx->flags = GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG; if (ap_options & AP_OPTS_MUTUAL_REQUIRED) ctx->flags |= GSS_C_MUTUAL_FLAG; } } if(ctx->flags & GSS_C_MUTUAL_FLAG) { krb5_data outbuf; int use_subkey = 0; _gsskrb5i_is_cfx(context, ctx, 1); is_cfx = (ctx->more_flags & IS_CFX); if (is_cfx || (ap_options & AP_OPTS_USE_SUBKEY)) { use_subkey = 1; } else { krb5_keyblock *rkey; /* * If there is a initiator subkey, copy that to acceptor * subkey to match Windows behavior */ kret = krb5_auth_con_getremotesubkey(context, ctx->auth_context, &rkey); if (kret == 0) { kret = krb5_auth_con_setlocalsubkey(context, ctx->auth_context, rkey); if (kret == 0) use_subkey = 1; krb5_free_keyblock(context, rkey); } } if (use_subkey) { ctx->gk5c.flags |= GK5C_ACCEPTOR_SUBKEY; krb5_auth_con_addflags(context, ctx->auth_context, KRB5_AUTH_CONTEXT_USE_SUBKEY, NULL); } kret = krb5_mk_rep(context, ctx->auth_context, &outbuf); if (kret) { *minor_status = kret; return GSS_S_FAILURE; } if (IS_DCE_STYLE(ctx)) { output_token->length = outbuf.length; output_token->value = outbuf.data; } else { ret = _gsskrb5_encapsulate(minor_status, &outbuf, output_token, "\x02\x00", ctx->mech); krb5_data_free (&outbuf); if (ret) return ret; } } ctx->flags |= GSS_C_TRANS_FLAG; /* Remember the flags */ ctx->endtime = ctx->ticket->ticket.endtime; ctx->more_flags |= OPEN; if (mech_type) *mech_type = ctx->mech; if (time_rec) { ret = _gsskrb5_lifetime_left(minor_status, context, ctx->endtime, time_rec); if (ret) { return ret; } } /* * When GSS_C_DCE_STYLE is in use, we need ask for a AP-REP from * the client. */ if (IS_DCE_STYLE(ctx)) { /* * Return flags to caller, but we haven't processed * delgations yet */ if (ret_flags) *ret_flags = (ctx->flags & ~GSS_C_DELEG_FLAG); ctx->acceptor_state = acceptor_wait_for_dcestyle; return GSS_S_CONTINUE_NEEDED; } ret = gsskrb5_acceptor_ready(minor_status, ctx, context, delegated_cred_handle); if (ret_flags) *ret_flags = ctx->flags; return ret; }
static krb5_error_code recvauth(int f, krb5_context krb_context, unsigned int *valid_checksum, krb5_ticket **ticket, int *auth_type, krb5_principal *client, int encr_flag, krb5_keytab keytab) { krb5_error_code status = 0; krb5_auth_context auth_context = NULL; krb5_rcache rcache; krb5_authenticator *authenticator; krb5_data inbuf; krb5_data auth_version; *valid_checksum = 0; if ((status = krb5_auth_con_init(krb_context, &auth_context))) return (status); /* Only need remote address for rd_cred() to verify client */ if ((status = krb5_auth_con_genaddrs(krb_context, auth_context, f, KRB5_AUTH_CONTEXT_GENERATE_REMOTE_FULL_ADDR))) return (status); status = krb5_auth_con_getrcache(krb_context, auth_context, &rcache); if (status) return (status); if (!rcache) { krb5_principal server; status = krb5_sname_to_principal(krb_context, 0, 0, KRB5_NT_SRV_HST, &server); if (status) return (status); status = krb5_get_server_rcache(krb_context, krb5_princ_component(krb_context, server, 0), &rcache); krb5_free_principal(krb_context, server); if (status) return (status); status = krb5_auth_con_setrcache(krb_context, auth_context, rcache); if (status) return (status); } if ((status = krb5_compat_recvauth(krb_context, &auth_context, &f, NULL, /* Specify daemon principal */ 0, /* no flags */ keytab, /* NULL to use v5srvtab */ ticket, /* return ticket */ auth_type, /* authentication system */ &auth_version))) { if (*auth_type == KRB5_RECVAUTH_V5) { /* * clean up before exiting */ getstr(f, rusername, sizeof (rusername), "remuser"); getstr(f, lusername, sizeof (lusername), "locuser"); getstr(f, term, sizeof (term), "Terminal type"); } return (status); } getstr(f, lusername, sizeof (lusername), "locuser"); getstr(f, term, sizeof (term), "Terminal type"); kcmd_protocol = KCMD_UNKNOWN_PROTOCOL; if (auth_version.length != 9 || auth_version.data == NULL) { syslog(LOG_ERR, "Bad application protocol version length in " "KRB5 exchange, exiting"); fatal(f, "Bad application version length, exiting."); } /* * Determine which Kerberos CMD protocol was used. */ if (strncmp(auth_version.data, "KCMDV0.1", 9) == 0) { kcmd_protocol = KCMD_OLD_PROTOCOL; } else if (strncmp(auth_version.data, "KCMDV0.2", 9) == 0) { kcmd_protocol = KCMD_NEW_PROTOCOL; } else { syslog(LOG_ERR, "Unrecognized KCMD protocol (%s), exiting", (char *)auth_version.data); fatal(f, "Unrecognized KCMD protocol, exiting"); } if ((*auth_type == KRB5_RECVAUTH_V5) && chksum_flag && kcmd_protocol == KCMD_OLD_PROTOCOL) { if ((status = krb5_auth_con_getauthenticator(krb_context, auth_context, &authenticator))) return (status); if (authenticator->checksum) { struct sockaddr_storage adr; int adr_length = sizeof (adr); int buflen; krb5_data input; krb5_keyblock key; char *chksumbuf; /* * Define the lenght of the chksum buffer. * chksum string = "[portnum]:termstr:username" * The extra 32 is to hold a integer string for * the portnumber. */ buflen = strlen(term) + strlen(lusername) + 32; chksumbuf = (char *)malloc(buflen); if (chksumbuf == 0) { krb5_free_authenticator(krb_context, authenticator); fatal(f, "Out of memory error"); } if (getsockname(f, (struct sockaddr *)&adr, &adr_length) != 0) { krb5_free_authenticator(krb_context, authenticator); fatal(f, "getsockname error"); } (void) snprintf(chksumbuf, buflen, "%u:%s%s", ntohs(SOCK_PORT(adr)), term, lusername); input.data = chksumbuf; input.length = strlen(chksumbuf); key.contents = (*ticket)->enc_part2->session->contents; key.length = (*ticket)->enc_part2->session->length; status = krb5_c_verify_checksum(krb_context, &key, 0, &input, authenticator->checksum, valid_checksum); if (status == 0 && *valid_checksum == 0) status = KRB5KRB_AP_ERR_BAD_INTEGRITY; if (chksumbuf) krb5_xfree(chksumbuf); if (status) { krb5_free_authenticator(krb_context, authenticator); return (status); } } krb5_free_authenticator(krb_context, authenticator); } if ((status = krb5_copy_principal(krb_context, (*ticket)->enc_part2->client, client))) return (status); /* Get the Unix username of the remote user */ getstr(f, rusername, sizeof (rusername), "remuser"); /* Get the Kerberos principal name string of the remote user */ if ((status = krb5_unparse_name(krb_context, *client, &krusername))) return (status); #ifdef DEBUG syslog(LOG_DEBUG | LOG_AUTH, "rlogind: got krb5 credentials for %s", (krusername != NULL ? krusername : "******")); #endif if (encr_flag) { status = krb5_auth_con_getremotesubkey(krb_context, auth_context, &session_key); if (status) { syslog(LOG_ERR, "Error getting KRB5 session " "subkey, exiting"); fatal(f, "Error getting KRB5 session subkey, exiting"); } /* * The "new" protocol requires that a subkey be sent. */ if (session_key == NULL && kcmd_protocol == KCMD_NEW_PROTOCOL) { syslog(LOG_ERR, "No KRB5 session subkey sent, exiting"); fatal(f, "No KRB5 session subkey sent, exiting"); } /* * The "old" protocol does not permit an authenticator subkey. * The key is taken from the ticket instead (see below). */ if (session_key != NULL && kcmd_protocol == KCMD_OLD_PROTOCOL) { syslog(LOG_ERR, "KRB5 session subkey not permitted " "with old KCMD protocol, exiting"); fatal(f, "KRB5 session subkey not permitted " "with old KCMD protocol, exiting"); } /* * If no key at this point, use the session key from * the ticket. */ if (session_key == NULL) { /* * Save the session key so we can configure the crypto * module later. */ status = krb5_copy_keyblock(krb_context, (*ticket)->enc_part2->session, &session_key); if (status) { syslog(LOG_ERR, "krb5_copy_keyblock failed"); fatal(f, "krb5_copy_keyblock failed"); } } /* * If session key still cannot be found, we must * exit because encryption is required here * when encr_flag (-x) is set. */ if (session_key == NULL) { syslog(LOG_ERR, "Could not find an encryption key," "exiting"); fatal(f, "Encryption required but key not found, " "exiting"); } } /* * Use krb5_read_message to read the principal stuff. */ if ((status = krb5_read_message(krb_context, (krb5_pointer)&f, &inbuf))) fatal(f, "Error reading krb5 message"); if (inbuf.length) { /* Forwarding being done, read creds */ krb5_creds **creds = NULL; if (status = krb5_rd_cred(krb_context, auth_context, &inbuf, &creds, NULL)) { if (rcache) (void) krb5_rc_close(krb_context, rcache); krb5_free_creds(krb_context, *creds); fatal(f, "Can't get forwarded credentials"); } /* Store the forwarded creds in the ccache */ if (status = store_forw_creds(krb_context, creds, *ticket, lusername, &ccache)) { if (rcache) (void) krb5_rc_close(krb_context, rcache); krb5_free_creds(krb_context, *creds); fatal(f, "Can't store forwarded credentials"); } krb5_free_creds(krb_context, *creds); } if (rcache) (void) krb5_rc_close(krb_context, rcache); return (status); }
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; }