17c478bd9Sstevel@tonic-gate /* 2*159d09a2SMark Phalan * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 37c478bd9Sstevel@tonic-gate * Use is subject to license terms. 47c478bd9Sstevel@tonic-gate */ 57c478bd9Sstevel@tonic-gate 67c478bd9Sstevel@tonic-gate 77c478bd9Sstevel@tonic-gate #include <string.h> 87c478bd9Sstevel@tonic-gate 9*159d09a2SMark Phalan #include "k5-int.h" 107c478bd9Sstevel@tonic-gate #include <kadm5/admin.h> 117c478bd9Sstevel@tonic-gate #include <client_internal.h> 12*159d09a2SMark Phalan #include "auth_con.h" 137c478bd9Sstevel@tonic-gate #include <locale.h> 147c478bd9Sstevel@tonic-gate 15*159d09a2SMark Phalan 16*159d09a2SMark Phalan krb5_error_code 17*159d09a2SMark Phalan krb5int_mk_chpw_req( 18*159d09a2SMark Phalan krb5_context context, 19*159d09a2SMark Phalan krb5_auth_context auth_context, 20*159d09a2SMark Phalan krb5_data *ap_req, 21*159d09a2SMark Phalan char *passwd, 22*159d09a2SMark Phalan krb5_data *packet) 237c478bd9Sstevel@tonic-gate { 247c478bd9Sstevel@tonic-gate krb5_error_code ret = 0; 257c478bd9Sstevel@tonic-gate krb5_data clearpw; 267c478bd9Sstevel@tonic-gate krb5_data cipherpw; 277c478bd9Sstevel@tonic-gate krb5_replay_data replay; 287c478bd9Sstevel@tonic-gate char *ptr; 297c478bd9Sstevel@tonic-gate 307c478bd9Sstevel@tonic-gate cipherpw.data = NULL; 317c478bd9Sstevel@tonic-gate 32*159d09a2SMark Phalan if ((ret = krb5_auth_con_setflags(context, auth_context, 33*159d09a2SMark Phalan KRB5_AUTH_CONTEXT_DO_SEQUENCE))) 347c478bd9Sstevel@tonic-gate goto cleanup; 357c478bd9Sstevel@tonic-gate 367c478bd9Sstevel@tonic-gate clearpw.length = strlen(passwd); 377c478bd9Sstevel@tonic-gate clearpw.data = passwd; 387c478bd9Sstevel@tonic-gate 39*159d09a2SMark Phalan if ((ret = krb5_mk_priv(context, auth_context, 40*159d09a2SMark Phalan &clearpw, &cipherpw, &replay))) 417c478bd9Sstevel@tonic-gate goto cleanup; 427c478bd9Sstevel@tonic-gate 437c478bd9Sstevel@tonic-gate packet->length = 6 + ap_req->length + cipherpw.length; 447c478bd9Sstevel@tonic-gate packet->data = (char *) malloc(packet->length); 457c478bd9Sstevel@tonic-gate if (packet->data == NULL) 467c478bd9Sstevel@tonic-gate { 477c478bd9Sstevel@tonic-gate ret = ENOMEM; 487c478bd9Sstevel@tonic-gate goto cleanup; 497c478bd9Sstevel@tonic-gate } 507c478bd9Sstevel@tonic-gate ptr = packet->data; 517c478bd9Sstevel@tonic-gate 527c478bd9Sstevel@tonic-gate /* length */ 53*159d09a2SMark Phalan 547c478bd9Sstevel@tonic-gate *ptr++ = (packet->length>> 8) & 0xff; 557c478bd9Sstevel@tonic-gate *ptr++ = packet->length & 0xff; 567c478bd9Sstevel@tonic-gate 57*159d09a2SMark Phalan /* version == 0x0001 big-endian 587c478bd9Sstevel@tonic-gate * NOTE: when MS and MIT start supporting the latest 597c478bd9Sstevel@tonic-gate * version of the passwd change protocol (v2), 607c478bd9Sstevel@tonic-gate * this value will change to 2. 617c478bd9Sstevel@tonic-gate */ 627c478bd9Sstevel@tonic-gate *ptr++ = 0; 637c478bd9Sstevel@tonic-gate *ptr++ = 1; 647c478bd9Sstevel@tonic-gate 657c478bd9Sstevel@tonic-gate /* ap_req length, big-endian */ 66*159d09a2SMark Phalan 677c478bd9Sstevel@tonic-gate *ptr++ = (ap_req->length>>8) & 0xff; 687c478bd9Sstevel@tonic-gate *ptr++ = ap_req->length & 0xff; 697c478bd9Sstevel@tonic-gate 707c478bd9Sstevel@tonic-gate /* ap-req data */ 71*159d09a2SMark Phalan 727c478bd9Sstevel@tonic-gate memcpy(ptr, ap_req->data, ap_req->length); 737c478bd9Sstevel@tonic-gate ptr += ap_req->length; 747c478bd9Sstevel@tonic-gate 757c478bd9Sstevel@tonic-gate /* krb-priv of password */ 76*159d09a2SMark Phalan 777c478bd9Sstevel@tonic-gate memcpy(ptr, cipherpw.data, cipherpw.length); 787c478bd9Sstevel@tonic-gate 797c478bd9Sstevel@tonic-gate cleanup: 807c478bd9Sstevel@tonic-gate if(cipherpw.data != NULL) /* allocated by krb5_mk_priv */ 817c478bd9Sstevel@tonic-gate free(cipherpw.data); 827c478bd9Sstevel@tonic-gate 837c478bd9Sstevel@tonic-gate return(ret); 847c478bd9Sstevel@tonic-gate } 857c478bd9Sstevel@tonic-gate 86*159d09a2SMark Phalan krb5_error_code 87*159d09a2SMark Phalan krb5int_rd_chpw_rep(krb5_context context, krb5_auth_context auth_context, krb5_data *packet, int *result_code, krb5_data *result_data) 887c478bd9Sstevel@tonic-gate { 897c478bd9Sstevel@tonic-gate char *ptr; 907c478bd9Sstevel@tonic-gate int plen, vno; 917c478bd9Sstevel@tonic-gate krb5_data ap_rep; 927c478bd9Sstevel@tonic-gate krb5_ap_rep_enc_part *ap_rep_enc; 937c478bd9Sstevel@tonic-gate krb5_error_code ret; 947c478bd9Sstevel@tonic-gate krb5_data cipherresult; 957c478bd9Sstevel@tonic-gate krb5_data clearresult; 967c478bd9Sstevel@tonic-gate krb5_error *krberror; 977c478bd9Sstevel@tonic-gate krb5_replay_data replay; 987c478bd9Sstevel@tonic-gate krb5_keyblock *tmp; 997c478bd9Sstevel@tonic-gate int local_result_code; 1007c478bd9Sstevel@tonic-gate 1017c478bd9Sstevel@tonic-gate if (packet->length < 4) 102*159d09a2SMark Phalan /* either this, or the server is printing bad messages, 103*159d09a2SMark Phalan or the caller passed in garbage */ 1047c478bd9Sstevel@tonic-gate return(KRB5KRB_AP_ERR_MODIFIED); 1057c478bd9Sstevel@tonic-gate 1067c478bd9Sstevel@tonic-gate ptr = packet->data; 1077c478bd9Sstevel@tonic-gate 1087c478bd9Sstevel@tonic-gate /* verify length */ 109*159d09a2SMark Phalan 1107c478bd9Sstevel@tonic-gate plen = (*ptr++ & 0xff); 1117c478bd9Sstevel@tonic-gate plen = (plen<<8) | (*ptr++ & 0xff); 1127c478bd9Sstevel@tonic-gate 1137c478bd9Sstevel@tonic-gate if (plen != packet->length) 114*159d09a2SMark Phalan { 115*159d09a2SMark Phalan /* 116*159d09a2SMark Phalan * MS KDCs *may* send back a KRB_ERROR. Although 117*159d09a2SMark Phalan * not 100% correct via RFC3244, it's something 118*159d09a2SMark Phalan * we can workaround here. 119*159d09a2SMark Phalan */ 120*159d09a2SMark Phalan if (krb5_is_krb_error(packet)) { 121*159d09a2SMark Phalan 122*159d09a2SMark Phalan if ((ret = krb5_rd_error(context, packet, &krberror))) 123*159d09a2SMark Phalan return(ret); 124*159d09a2SMark Phalan 125*159d09a2SMark Phalan if (krberror->e_data.data == NULL) { 126*159d09a2SMark Phalan ret = ERROR_TABLE_BASE_krb5 + (krb5_error_code) krberror->error; 127*159d09a2SMark Phalan krb5_free_error(context, krberror); 128*159d09a2SMark Phalan return (ret); 129*159d09a2SMark Phalan } 130*159d09a2SMark Phalan } 131*159d09a2SMark Phalan else 132*159d09a2SMark Phalan { 1337c478bd9Sstevel@tonic-gate return(KRB5KRB_AP_ERR_MODIFIED); 134*159d09a2SMark Phalan } 135*159d09a2SMark Phalan } 136*159d09a2SMark Phalan 1377c478bd9Sstevel@tonic-gate 1387c478bd9Sstevel@tonic-gate /* verify version number */ 139*159d09a2SMark Phalan 1407c478bd9Sstevel@tonic-gate vno = (*ptr++ & 0xff); 1417c478bd9Sstevel@tonic-gate vno = (vno<<8) | (*ptr++ & 0xff); 1427c478bd9Sstevel@tonic-gate 1437c478bd9Sstevel@tonic-gate /* 1447c478bd9Sstevel@tonic-gate * when the servers update to v2 of the protocol, 1457c478bd9Sstevel@tonic-gate * "2" will be a valid version number here 1467c478bd9Sstevel@tonic-gate */ 1477c478bd9Sstevel@tonic-gate if (vno != 1 && vno != 2) 1487c478bd9Sstevel@tonic-gate return (KRB5KDC_ERR_BAD_PVNO); 1497c478bd9Sstevel@tonic-gate 1507c478bd9Sstevel@tonic-gate /* read, check ap-rep length */ 151*159d09a2SMark Phalan 1527c478bd9Sstevel@tonic-gate ap_rep.length = (*ptr++ & 0xff); 1537c478bd9Sstevel@tonic-gate ap_rep.length = (ap_rep.length<<8) | (*ptr++ & 0xff); 1547c478bd9Sstevel@tonic-gate 1557c478bd9Sstevel@tonic-gate if (ptr + ap_rep.length >= packet->data + packet->length) 1567c478bd9Sstevel@tonic-gate return(KRB5KRB_AP_ERR_MODIFIED); 1577c478bd9Sstevel@tonic-gate 1587c478bd9Sstevel@tonic-gate if (ap_rep.length) { 1597c478bd9Sstevel@tonic-gate /* verify ap_rep */ 1607c478bd9Sstevel@tonic-gate ap_rep.data = ptr; 1617c478bd9Sstevel@tonic-gate ptr += ap_rep.length; 1627c478bd9Sstevel@tonic-gate 16345526e97Ssemery /* 16445526e97Ssemery * Save send_subkey to later smash recv_subkey. 16545526e97Ssemery */ 16645526e97Ssemery ret = krb5_auth_con_getsendsubkey(context, auth_context, &tmp); 16745526e97Ssemery if (ret) 168*159d09a2SMark Phalan return ret; 1697c478bd9Sstevel@tonic-gate 170*159d09a2SMark Phalan ret = krb5_rd_rep(context, auth_context, &ap_rep, &ap_rep_enc); 171*159d09a2SMark Phalan if (ret) { 17245526e97Ssemery krb5_free_keyblock(context, tmp); 17345526e97Ssemery return(ret); 17445526e97Ssemery } 17545526e97Ssemery 1767c478bd9Sstevel@tonic-gate krb5_free_ap_rep_enc_part(context, ap_rep_enc); 1777c478bd9Sstevel@tonic-gate 1787c478bd9Sstevel@tonic-gate /* extract and decrypt the result */ 179*159d09a2SMark Phalan 1807c478bd9Sstevel@tonic-gate cipherresult.data = ptr; 1817c478bd9Sstevel@tonic-gate cipherresult.length = (packet->data + packet->length) - ptr; 1827c478bd9Sstevel@tonic-gate 1837c478bd9Sstevel@tonic-gate /* 18445526e97Ssemery * Smash recv_subkey to be send_subkey, per spec. 1857c478bd9Sstevel@tonic-gate */ 18645526e97Ssemery ret = krb5_auth_con_setrecvsubkey(context, auth_context, tmp); 18745526e97Ssemery krb5_free_keyblock(context, tmp); 18845526e97Ssemery if (ret) 189*159d09a2SMark Phalan return ret; 1907c478bd9Sstevel@tonic-gate 191*159d09a2SMark Phalan ret = krb5_rd_priv(context, auth_context, &cipherresult, &clearresult, 192*159d09a2SMark Phalan &replay); 1937c478bd9Sstevel@tonic-gate 1947c478bd9Sstevel@tonic-gate if (ret) 1957c478bd9Sstevel@tonic-gate return(ret); 1967c478bd9Sstevel@tonic-gate } else { 1977c478bd9Sstevel@tonic-gate cipherresult.data = ptr; 1987c478bd9Sstevel@tonic-gate cipherresult.length = (packet->data + packet->length) - ptr; 1997c478bd9Sstevel@tonic-gate 200*159d09a2SMark Phalan if ((ret = krb5_rd_error(context, &cipherresult, &krberror))) 2017c478bd9Sstevel@tonic-gate return(ret); 2027c478bd9Sstevel@tonic-gate 2037c478bd9Sstevel@tonic-gate clearresult = krberror->e_data; 2047c478bd9Sstevel@tonic-gate } 2057c478bd9Sstevel@tonic-gate 2067c478bd9Sstevel@tonic-gate if (clearresult.length < 2) { 2077c478bd9Sstevel@tonic-gate ret = KRB5KRB_AP_ERR_MODIFIED; 2087c478bd9Sstevel@tonic-gate goto cleanup; 2097c478bd9Sstevel@tonic-gate } 2107c478bd9Sstevel@tonic-gate 2117c478bd9Sstevel@tonic-gate ptr = clearresult.data; 2127c478bd9Sstevel@tonic-gate 2137c478bd9Sstevel@tonic-gate local_result_code = (*ptr++ & 0xff); 2147c478bd9Sstevel@tonic-gate local_result_code = (local_result_code<<8) | (*ptr++ & 0xff); 2157c478bd9Sstevel@tonic-gate 2167c478bd9Sstevel@tonic-gate if (result_code) 2177c478bd9Sstevel@tonic-gate *result_code = local_result_code; 2187c478bd9Sstevel@tonic-gate 2197c478bd9Sstevel@tonic-gate /* 2207c478bd9Sstevel@tonic-gate * Make sure the result code is in range for this 2217c478bd9Sstevel@tonic-gate * protocol. 2227c478bd9Sstevel@tonic-gate */ 2237c478bd9Sstevel@tonic-gate if ((local_result_code < KRB5_KPASSWD_SUCCESS) || 2247c478bd9Sstevel@tonic-gate (local_result_code > KRB5_KPASSWD_ETYPE_NOSUPP)) { 2257c478bd9Sstevel@tonic-gate ret = KRB5KRB_AP_ERR_MODIFIED; 2267c478bd9Sstevel@tonic-gate goto cleanup; 2277c478bd9Sstevel@tonic-gate } 2287c478bd9Sstevel@tonic-gate 2297c478bd9Sstevel@tonic-gate /* all success replies should be authenticated/encrypted */ 230*159d09a2SMark Phalan 231*159d09a2SMark Phalan if ((ap_rep.length == 0) && (local_result_code == KRB5_KPASSWD_SUCCESS)) { 2327c478bd9Sstevel@tonic-gate ret = KRB5KRB_AP_ERR_MODIFIED; 2337c478bd9Sstevel@tonic-gate goto cleanup; 2347c478bd9Sstevel@tonic-gate } 2357c478bd9Sstevel@tonic-gate 2367c478bd9Sstevel@tonic-gate result_data->length = (clearresult.data + clearresult.length) - ptr; 2377c478bd9Sstevel@tonic-gate 2387c478bd9Sstevel@tonic-gate if (result_data->length) { 2397c478bd9Sstevel@tonic-gate result_data->data = (char *) malloc(result_data->length); 2407c478bd9Sstevel@tonic-gate if (result_data->data == NULL) { 2417c478bd9Sstevel@tonic-gate ret = ENOMEM; 2427c478bd9Sstevel@tonic-gate goto cleanup; 2437c478bd9Sstevel@tonic-gate } 2447c478bd9Sstevel@tonic-gate memcpy(result_data->data, ptr, result_data->length); 2457c478bd9Sstevel@tonic-gate } else { 2467c478bd9Sstevel@tonic-gate result_data->data = NULL; 2477c478bd9Sstevel@tonic-gate } 2487c478bd9Sstevel@tonic-gate 2497c478bd9Sstevel@tonic-gate ret = 0; 2507c478bd9Sstevel@tonic-gate 2517c478bd9Sstevel@tonic-gate cleanup: 2527c478bd9Sstevel@tonic-gate if (ap_rep.length) { 2537c478bd9Sstevel@tonic-gate krb5_xfree(clearresult.data); 2547c478bd9Sstevel@tonic-gate } else { 2557c478bd9Sstevel@tonic-gate krb5_free_error(context, krberror); 2567c478bd9Sstevel@tonic-gate } 2577c478bd9Sstevel@tonic-gate 2587c478bd9Sstevel@tonic-gate return(ret); 2597c478bd9Sstevel@tonic-gate } 260