1 /* 2 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 3 * Use is subject to license terms. 4 */ 5 6 7 #include <string.h> 8 9 #include "k5-int.h" 10 #include <kadm5/admin.h> 11 #include <client_internal.h> 12 #include "auth_con.h" 13 #include <locale.h> 14 15 16 krb5_error_code 17 krb5int_mk_chpw_req( 18 krb5_context context, 19 krb5_auth_context auth_context, 20 krb5_data *ap_req, 21 char *passwd, 22 krb5_data *packet) 23 { 24 krb5_error_code ret = 0; 25 krb5_data clearpw; 26 krb5_data cipherpw; 27 krb5_replay_data replay; 28 char *ptr; 29 30 cipherpw.data = NULL; 31 32 if ((ret = krb5_auth_con_setflags(context, auth_context, 33 KRB5_AUTH_CONTEXT_DO_SEQUENCE))) 34 goto cleanup; 35 36 clearpw.length = strlen(passwd); 37 clearpw.data = passwd; 38 39 if ((ret = krb5_mk_priv(context, auth_context, 40 &clearpw, &cipherpw, &replay))) 41 goto cleanup; 42 43 packet->length = 6 + ap_req->length + cipherpw.length; 44 packet->data = (char *) malloc(packet->length); 45 if (packet->data == NULL) 46 { 47 ret = ENOMEM; 48 goto cleanup; 49 } 50 ptr = packet->data; 51 52 /* length */ 53 54 *ptr++ = (packet->length>> 8) & 0xff; 55 *ptr++ = packet->length & 0xff; 56 57 /* version == 0x0001 big-endian 58 * NOTE: when MS and MIT start supporting the latest 59 * version of the passwd change protocol (v2), 60 * this value will change to 2. 61 */ 62 *ptr++ = 0; 63 *ptr++ = 1; 64 65 /* ap_req length, big-endian */ 66 67 *ptr++ = (ap_req->length>>8) & 0xff; 68 *ptr++ = ap_req->length & 0xff; 69 70 /* ap-req data */ 71 72 memcpy(ptr, ap_req->data, ap_req->length); 73 ptr += ap_req->length; 74 75 /* krb-priv of password */ 76 77 memcpy(ptr, cipherpw.data, cipherpw.length); 78 79 cleanup: 80 if(cipherpw.data != NULL) /* allocated by krb5_mk_priv */ 81 free(cipherpw.data); 82 83 return(ret); 84 } 85 86 krb5_error_code 87 krb5int_rd_chpw_rep(krb5_context context, krb5_auth_context auth_context, krb5_data *packet, int *result_code, krb5_data *result_data) 88 { 89 char *ptr; 90 int plen, vno; 91 krb5_data ap_rep; 92 krb5_ap_rep_enc_part *ap_rep_enc; 93 krb5_error_code ret; 94 krb5_data cipherresult; 95 krb5_data clearresult; 96 krb5_error *krberror; 97 krb5_replay_data replay; 98 krb5_keyblock *tmp; 99 int local_result_code; 100 101 if (packet->length < 4) 102 /* either this, or the server is printing bad messages, 103 or the caller passed in garbage */ 104 return(KRB5KRB_AP_ERR_MODIFIED); 105 106 ptr = packet->data; 107 108 /* verify length */ 109 110 plen = (*ptr++ & 0xff); 111 plen = (plen<<8) | (*ptr++ & 0xff); 112 113 if (plen != packet->length) 114 { 115 /* 116 * MS KDCs *may* send back a KRB_ERROR. Although 117 * not 100% correct via RFC3244, it's something 118 * we can workaround here. 119 */ 120 if (krb5_is_krb_error(packet)) { 121 122 if ((ret = krb5_rd_error(context, packet, &krberror))) 123 return(ret); 124 125 if (krberror->e_data.data == NULL) { 126 ret = ERROR_TABLE_BASE_krb5 + (krb5_error_code) krberror->error; 127 krb5_free_error(context, krberror); 128 return (ret); 129 } 130 } 131 else 132 { 133 return(KRB5KRB_AP_ERR_MODIFIED); 134 } 135 } 136 137 138 /* verify version number */ 139 140 vno = (*ptr++ & 0xff); 141 vno = (vno<<8) | (*ptr++ & 0xff); 142 143 /* 144 * when the servers update to v2 of the protocol, 145 * "2" will be a valid version number here 146 */ 147 if (vno != 1 && vno != 2) 148 return (KRB5KDC_ERR_BAD_PVNO); 149 150 /* read, check ap-rep length */ 151 152 ap_rep.length = (*ptr++ & 0xff); 153 ap_rep.length = (ap_rep.length<<8) | (*ptr++ & 0xff); 154 155 if (ptr + ap_rep.length >= packet->data + packet->length) 156 return(KRB5KRB_AP_ERR_MODIFIED); 157 158 if (ap_rep.length) { 159 /* verify ap_rep */ 160 ap_rep.data = ptr; 161 ptr += ap_rep.length; 162 163 /* 164 * Save send_subkey to later smash recv_subkey. 165 */ 166 ret = krb5_auth_con_getsendsubkey(context, auth_context, &tmp); 167 if (ret) 168 return ret; 169 170 ret = krb5_rd_rep(context, auth_context, &ap_rep, &ap_rep_enc); 171 if (ret) { 172 krb5_free_keyblock(context, tmp); 173 return(ret); 174 } 175 176 krb5_free_ap_rep_enc_part(context, ap_rep_enc); 177 178 /* extract and decrypt the result */ 179 180 cipherresult.data = ptr; 181 cipherresult.length = (packet->data + packet->length) - ptr; 182 183 /* 184 * Smash recv_subkey to be send_subkey, per spec. 185 */ 186 ret = krb5_auth_con_setrecvsubkey(context, auth_context, tmp); 187 krb5_free_keyblock(context, tmp); 188 if (ret) 189 return ret; 190 191 ret = krb5_rd_priv(context, auth_context, &cipherresult, &clearresult, 192 &replay); 193 194 if (ret) 195 return(ret); 196 } else { 197 cipherresult.data = ptr; 198 cipherresult.length = (packet->data + packet->length) - ptr; 199 200 if ((ret = krb5_rd_error(context, &cipherresult, &krberror))) 201 return(ret); 202 203 clearresult = krberror->e_data; 204 } 205 206 if (clearresult.length < 2) { 207 ret = KRB5KRB_AP_ERR_MODIFIED; 208 goto cleanup; 209 } 210 211 ptr = clearresult.data; 212 213 local_result_code = (*ptr++ & 0xff); 214 local_result_code = (local_result_code<<8) | (*ptr++ & 0xff); 215 216 if (result_code) 217 *result_code = local_result_code; 218 219 /* 220 * Make sure the result code is in range for this 221 * protocol. 222 */ 223 if ((local_result_code < KRB5_KPASSWD_SUCCESS) || 224 (local_result_code > KRB5_KPASSWD_ETYPE_NOSUPP)) { 225 ret = KRB5KRB_AP_ERR_MODIFIED; 226 goto cleanup; 227 } 228 229 /* all success replies should be authenticated/encrypted */ 230 231 if ((ap_rep.length == 0) && (local_result_code == KRB5_KPASSWD_SUCCESS)) { 232 ret = KRB5KRB_AP_ERR_MODIFIED; 233 goto cleanup; 234 } 235 236 result_data->length = (clearresult.data + clearresult.length) - ptr; 237 238 if (result_data->length) { 239 result_data->data = (char *) malloc(result_data->length); 240 if (result_data->data == NULL) { 241 ret = ENOMEM; 242 goto cleanup; 243 } 244 memcpy(result_data->data, ptr, result_data->length); 245 } else { 246 result_data->data = NULL; 247 } 248 249 ret = 0; 250 251 cleanup: 252 if (ap_rep.length) { 253 krb5_xfree(clearresult.data); 254 } else { 255 krb5_free_error(context, krberror); 256 } 257 258 return(ret); 259 } 260