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