static krb5_error_code fast_armor_ap_request(krb5_context context, struct krb5int_fast_request_state *state, krb5_ccache ccache, krb5_principal target_principal) { krb5_error_code retval = 0; krb5_creds creds, *out_creds = NULL; krb5_auth_context authcontext = NULL; krb5_data encoded_authenticator; krb5_fast_armor *armor = NULL; krb5_keyblock *subkey = NULL, *armor_key = NULL; encoded_authenticator.data = NULL; memset(&creds, 0, sizeof(creds)); creds.server = target_principal; retval = krb5_cc_get_principal(context, ccache, &creds.client); if (retval == 0) retval = krb5_get_credentials(context, 0, ccache, &creds, &out_creds); if (retval == 0) { TRACE_FAST_ARMOR_CCACHE_KEY(context, &out_creds->keyblock); retval = krb5_mk_req_extended(context, &authcontext, AP_OPTS_USE_SUBKEY, NULL /*data*/, out_creds, &encoded_authenticator); } if (retval == 0) retval = krb5_auth_con_getsendsubkey(context, authcontext, &subkey); if (retval == 0) retval = krb5_c_fx_cf2_simple(context, subkey, "subkeyarmor", &out_creds->keyblock, "ticketarmor", &armor_key); if (retval == 0) { TRACE_FAST_ARMOR_KEY(context, armor_key); armor = calloc(1, sizeof(krb5_fast_armor)); if (armor == NULL) retval = ENOMEM; } if (retval == 0) { armor->armor_type = KRB5_FAST_ARMOR_AP_REQUEST; armor->armor_value = encoded_authenticator; encoded_authenticator.data = NULL; encoded_authenticator.length = 0; state->armor = armor; armor = NULL; state->armor_key = armor_key; armor_key = NULL; } krb5_free_keyblock(context, armor_key); krb5_free_keyblock(context, subkey); if (out_creds) krb5_free_creds(context, out_creds); /* target_principal is owned by caller. */ creds.server = NULL; krb5_free_cred_contents(context, &creds); if (encoded_authenticator.data) krb5_free_data_contents(context, &encoded_authenticator); krb5_auth_con_free(context, authcontext); return retval; }
krb5_error_code KRB5_CALLCONV krb5_auth_con_getlocalsubkey(krb5_context context, krb5_auth_context auth_context, krb5_keyblock **keyblock) { return krb5_auth_con_getsendsubkey(context, auth_context, keyblock); }
krb5_error_code krb5int_rd_chpw_rep(krb5_context context, krb5_auth_context auth_context, krb5_data *packet, int *result_code, krb5_data *result_data) { char *ptr; int plen, vno; krb5_data ap_rep; krb5_ap_rep_enc_part *ap_rep_enc; krb5_error_code ret; krb5_data cipherresult; krb5_data clearresult; krb5_error *krberror; krb5_replay_data replay; krb5_keyblock *tmp; if (packet->length < 4) /* either this, or the server is printing bad messages, or the caller passed in garbage */ return(KRB5KRB_AP_ERR_MODIFIED); ptr = packet->data; /* verify length */ plen = (*ptr++ & 0xff); plen = (plen<<8) | (*ptr++ & 0xff); if (plen != packet->length) { /* * MS KDCs *may* send back a KRB_ERROR. Although * not 100% correct via RFC3244, it's something * we can workaround here. */ if (krb5_is_krb_error(packet)) { if ((ret = krb5_rd_error(context, packet, &krberror))) return(ret); if (krberror->e_data.data == NULL) { ret = ERROR_TABLE_BASE_krb5 + (krb5_error_code) krberror->error; krb5_free_error(context, krberror); return (ret); } } else { return(KRB5KRB_AP_ERR_MODIFIED); } } /* verify version number */ vno = (*ptr++ & 0xff); vno = (vno<<8) | (*ptr++ & 0xff); if (vno != 1) return(KRB5KDC_ERR_BAD_PVNO); /* read, check ap-rep length */ ap_rep.length = (*ptr++ & 0xff); ap_rep.length = (ap_rep.length<<8) | (*ptr++ & 0xff); if (ptr + ap_rep.length >= packet->data + packet->length) return(KRB5KRB_AP_ERR_MODIFIED); if (ap_rep.length) { /* verify ap_rep */ ap_rep.data = ptr; ptr += ap_rep.length; /* * Save send_subkey to later smash recv_subkey. */ ret = krb5_auth_con_getsendsubkey(context, auth_context, &tmp); if (ret) return ret; ret = krb5_rd_rep(context, auth_context, &ap_rep, &ap_rep_enc); if (ret) { krb5_free_keyblock(context, tmp); return(ret); } krb5_free_ap_rep_enc_part(context, ap_rep_enc); /* extract and decrypt the result */ cipherresult.data = ptr; cipherresult.length = (packet->data + packet->length) - ptr; /* * Smash recv_subkey to be send_subkey, per spec. */ ret = krb5_auth_con_setrecvsubkey(context, auth_context, tmp); krb5_free_keyblock(context, tmp); if (ret) return ret; ret = krb5_rd_priv(context, auth_context, &cipherresult, &clearresult, &replay); if (ret) return(ret); } else { cipherresult.data = ptr; cipherresult.length = (packet->data + packet->length) - ptr; if ((ret = krb5_rd_error(context, &cipherresult, &krberror))) return(ret); clearresult = krberror->e_data; } if (clearresult.length < 2) { ret = KRB5KRB_AP_ERR_MODIFIED; goto cleanup; } ptr = clearresult.data; *result_code = (*ptr++ & 0xff); *result_code = (*result_code<<8) | (*ptr++ & 0xff); if ((*result_code < KRB5_KPASSWD_SUCCESS) || (*result_code > KRB5_KPASSWD_INITIAL_FLAG_NEEDED)) { ret = KRB5KRB_AP_ERR_MODIFIED; goto cleanup; } /* all success replies should be authenticated/encrypted */ if ((ap_rep.length == 0) && (*result_code == KRB5_KPASSWD_SUCCESS)) { ret = KRB5KRB_AP_ERR_MODIFIED; goto cleanup; } result_data->length = (clearresult.data + clearresult.length) - ptr; if (result_data->length) { result_data->data = (char *) malloc(result_data->length); if (result_data->data == NULL) { ret = ENOMEM; goto cleanup; } memcpy(result_data->data, ptr, result_data->length); } else { result_data->data = NULL; } ret = 0; cleanup: if (ap_rep.length) { krb5_xfree(clearresult.data); } else { krb5_free_error(context, krberror); } return(ret); }
static int cifs_krb5_get_req(const char *host, const char *ccname, DATA_BLOB * mechtoken, DATA_BLOB * sess_key) { krb5_error_code ret; krb5_keyblock *tokb; krb5_context context; krb5_ccache ccache; krb5_creds in_creds, *out_creds; krb5_data apreq_pkt, in_data; krb5_auth_context auth_context = NULL; #if defined(HAVE_KRB5_AUTH_CON_SETADDRS) && defined(HAVE_KRB5_AUTH_CON_SET_REQ_CKSUMTYPE) static const uint8_t gss_cksum[24] = { 0x10, 0x00, /* ... */}; #endif ret = krb5_init_context(&context); if (ret) { syslog(LOG_DEBUG, "%s: unable to init krb5 context", __func__); return ret; } ret = krb5_cc_resolve(context, ccname, &ccache); if (ret) { syslog(LOG_DEBUG, "%s: unable to resolve %s to ccache\n", __func__, ccname); goto out_free_context; } memset(&in_creds, 0, sizeof(in_creds)); ret = krb5_cc_get_principal(context, ccache, &in_creds.client); if (ret) { syslog(LOG_DEBUG, "%s: unable to get client principal name", __func__); goto out_free_ccache; } ret = krb5_sname_to_principal(context, host, "cifs", KRB5_NT_UNKNOWN, &in_creds.server); if (ret) { syslog(LOG_DEBUG, "%s: unable to convert sname to princ (%s).", __func__, host); goto out_free_principal; } ret = krb5_get_credentials(context, 0, ccache, &in_creds, &out_creds); krb5_free_principal(context, in_creds.server); if (ret) { syslog(LOG_DEBUG, "%s: unable to get credentials for %s", __func__, host); goto out_free_principal; } in_data.length = 0; in_data.data = NULL; ret = krb5_auth_con_init(context, &auth_context); if (ret) { syslog(LOG_DEBUG, "%s: unable to create auth_context: %d", __func__, ret); goto out_free_creds; } #if defined(HAVE_KRB5_AUTH_CON_SETADDRS) && defined(HAVE_KRB5_AUTH_CON_SET_REQ_CKSUMTYPE) /* Ensure we will get an addressless ticket. */ ret = krb5_auth_con_setaddrs(context, auth_context, NULL, NULL); if (ret) { syslog(LOG_DEBUG, "%s: unable to set NULL addrs: %d", __func__, ret); goto out_free_auth; } /* * Create a GSSAPI checksum (0x8003), see RFC 4121. * * The current layout is * * 0x10, 0x00, 0x00, 0x00 - length = 16 * 0x00, 0x00, 0x00, 0x00 - channel binding info - 16 zero bytes * 0x00, 0x00, 0x00, 0x00 * 0x00, 0x00, 0x00, 0x00 * 0x00, 0x00, 0x00, 0x00 * 0x00, 0x00, 0x00, 0x00 - flags * * GSS_C_NO_CHANNEL_BINDINGS means 16 zero bytes, * this is needed to work against some closed source * SMB servers. * * See https://bugzilla.samba.org/show_bug.cgi?id=7890 */ in_data.data = discard_const_p(char, gss_cksum); in_data.length = 24; /* MIT krb5 < 1.7 is missing the prototype, but still has the symbol */ #if !HAVE_DECL_KRB5_AUTH_CON_SET_REQ_CKSUMTYPE krb5_error_code krb5_auth_con_set_req_cksumtype( krb5_context context, krb5_auth_context auth_context, krb5_cksumtype cksumtype); #endif ret = krb5_auth_con_set_req_cksumtype(context, auth_context, 0x8003); if (ret) { syslog(LOG_DEBUG, "%s: unable to set 0x8003 checksum", __func__); goto out_free_auth; } #endif apreq_pkt.length = 0; apreq_pkt.data = NULL; ret = krb5_mk_req_extended(context, &auth_context, AP_OPTS_USE_SUBKEY, &in_data, out_creds, &apreq_pkt); if (ret) { syslog(LOG_DEBUG, "%s: unable to make AP-REQ for %s", __func__, host); goto out_free_auth; } ret = krb5_auth_con_getsendsubkey(context, auth_context, &tokb); if (ret) { syslog(LOG_DEBUG, "%s: unable to get session key for %s", __func__, host); goto out_free_auth; } *mechtoken = data_blob(apreq_pkt.data, apreq_pkt.length); *sess_key = data_blob(KRB5_KEY_DATA(tokb), KRB5_KEY_LENGTH(tokb)); krb5_free_keyblock(context, tokb); out_free_auth: krb5_auth_con_free(context, auth_context); out_free_creds: krb5_free_creds(context, out_creds); out_free_principal: krb5_free_principal(context, in_creds.client); out_free_ccache: #if defined(KRB5_TC_OPENCLOSE) krb5_cc_set_flags(context, ccache, KRB5_TC_OPENCLOSE); #endif krb5_cc_close(context, ccache); out_free_context: krb5_free_context(context); return ret; }
krb5_error_code krb5int_rd_setpw_rep( krb5_context context, krb5_auth_context auth_context, krb5_data *packet, int *result_code, krb5_data *result_data ) { char *ptr; unsigned int message_length, version_number; krb5_data ap_rep; krb5_ap_rep_enc_part *ap_rep_enc; krb5_error_code ret; krb5_data cipherresult; krb5_data clearresult; krb5_keyblock *tmpkey; /* ** validate the packet length - */ if (packet->length < 4) return(KRB5KRB_AP_ERR_MODIFIED); ptr = packet->data; /* ** see if it is an error */ if (krb5_is_krb_error(packet)) { krb5_error *krberror; if ((ret = krb5_rd_error(context, packet, &krberror))) return(ret); if (krberror->e_data.data == NULL) { ret = ERROR_TABLE_BASE_krb5 + (krb5_error_code) krberror->error; krb5_free_error(context, krberror); return (ret); } clearresult = krberror->e_data; krberror->e_data.data = NULL; /*So we can free it later*/ krberror->e_data.length = 0; krb5_free_error(context, krberror); } else { /* Not an error*/ /* ** validate the message length - ** length is big endian */ message_length = (((ptr[0] << 8)&0xff) | (ptr[1]&0xff)); ptr += 2; /* ** make sure the message length and packet length agree - */ if (message_length != packet->length) return(KRB5KRB_AP_ERR_MODIFIED); /* ** get the version number - */ version_number = (((ptr[0] << 8)&0xff) | (ptr[1]&0xff)); ptr += 2; /* ** make sure we support the version returned - */ /* ** set password version is 0xff80, change password version is 1 */ if (version_number != 1 && version_number != 0xff80) return(KRB5KDC_ERR_BAD_PVNO); /* ** now fill in ap_rep with the reply - */ /* ** get the reply length - */ ap_rep.length = (((ptr[0] << 8)&0xff) | (ptr[1]&0xff)); ptr += 2; /* ** validate ap_rep length agrees with the packet length - */ if (ptr + ap_rep.length >= packet->data + packet->length) return(KRB5KRB_AP_ERR_MODIFIED); /* ** if data was returned, set the ap_rep ptr - */ if( ap_rep.length ) { ap_rep.data = ptr; ptr += ap_rep.length; /* * Save send_subkey to later smash recv_subkey. */ ret = krb5_auth_con_getsendsubkey(context, auth_context, &tmpkey); if (ret) return ret; ret = krb5_rd_rep(context, auth_context, &ap_rep, &ap_rep_enc); if (ret) { krb5_free_keyblock(context, tmpkey); return(ret); } krb5_free_ap_rep_enc_part(context, ap_rep_enc); /* ** now decrypt the result - */ cipherresult.data = ptr; cipherresult.length = (packet->data + packet->length) - ptr; /* * Smash recv_subkey to be send_subkey, per spec. */ ret = krb5_auth_con_setrecvsubkey(context, auth_context, tmpkey); krb5_free_keyblock(context, tmpkey); if (ret) return ret; ret = krb5_rd_priv(context, auth_context, &cipherresult, &clearresult, NULL); if (ret) return(ret); } /*We got an ap_rep*/ else return (KRB5KRB_AP_ERR_MODIFIED); } /*Response instead of error*/ /* ** validate the cleartext length */ if (clearresult.length < 2) { ret = KRB5KRB_AP_ERR_MODIFIED; goto cleanup; } /* ** now decode the result - */ ptr = clearresult.data; *result_code = (((ptr[0] << 8)&0xff) | (ptr[1]&0xff)); ptr += 2; /* ** result code 5 is access denied */ if ((*result_code < KRB5_KPASSWD_SUCCESS) || (*result_code > 5)) { ret = KRB5KRB_AP_ERR_MODIFIED; goto cleanup; } /* ** all success replies should be authenticated/encrypted */ if( (ap_rep.length == 0) && (*result_code == KRB5_KPASSWD_SUCCESS) ) { ret = KRB5KRB_AP_ERR_MODIFIED; goto cleanup; } if (result_data) { result_data->length = (clearresult.data + clearresult.length) - ptr; if (result_data->length) { result_data->data = (char *) malloc(result_data->length); if (result_data->data) memcpy(result_data->data, ptr, result_data->length); } else result_data->data = NULL; } ret = 0; cleanup: krb5_free_data_contents(context, &clearresult); return(ret); }