1 /* 2 * Copyright 2004 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 if (ret = krb5_rd_rep(context, auth_context, &ap_rep, 159 &ap_rep_enc)) 160 return (ret); 161 162 krb5_free_ap_rep_enc_part(context, ap_rep_enc); 163 164 /* extract and decrypt the result */ 165 cipherresult.data = ptr; 166 cipherresult.length = (packet->data + packet->length) - ptr; 167 168 /* 169 * XXX there's no api to do this right. The problem is that 170 * if there's a remote subkey, it will be used. This is 171 * not what the spec requires 172 */ 173 tmp = auth_context->recv_subkey; 174 auth_context->recv_subkey = NULL; 175 176 ret = krb5_rd_priv(context, auth_context, &cipherresult, 177 &clearresult, &replay); 178 179 auth_context->recv_subkey = tmp; 180 181 if (ret) 182 return (ret); 183 } else { 184 cipherresult.data = ptr; 185 cipherresult.length = (packet->data + packet->length) - ptr; 186 187 if (ret = krb5_rd_error(context, &cipherresult, &krberror)) 188 return (ret); 189 190 clearresult = krberror->e_data; 191 } 192 193 if (clearresult.length < 2) { 194 ret = KRB5KRB_AP_ERR_MODIFIED; 195 goto cleanup; 196 } 197 198 ptr = clearresult.data; 199 200 local_result_code = (*ptr++ & 0xff); 201 local_result_code = (local_result_code<<8) | (*ptr++ & 0xff); 202 203 if (result_code) 204 *result_code = local_result_code; 205 206 /* 207 * Make sure the result code is in range for this 208 * protocol. 209 */ 210 if ((local_result_code < KRB5_KPASSWD_SUCCESS) || 211 (local_result_code > KRB5_KPASSWD_ETYPE_NOSUPP)) { 212 ret = KRB5KRB_AP_ERR_MODIFIED; 213 goto cleanup; 214 } 215 216 217 /* all success replies should be authenticated/encrypted */ 218 if ((ap_rep.length == 0) && 219 (local_result_code == KRB5_KPASSWD_SUCCESS)) { 220 ret = KRB5KRB_AP_ERR_MODIFIED; 221 goto cleanup; 222 } 223 224 result_data->length = (clearresult.data + clearresult.length) - ptr; 225 226 if (result_data->length) { 227 result_data->data = (char *)malloc(result_data->length); 228 if (result_data->data == NULL) { 229 ret = ENOMEM; 230 goto cleanup; 231 } 232 memcpy(result_data->data, ptr, result_data->length); 233 } else { 234 result_data->data = NULL; 235 } 236 237 ret = 0; 238 239 cleanup: 240 if (ap_rep.length) { 241 krb5_xfree(clearresult.data); 242 } else { 243 krb5_free_error(context, krberror); 244 } 245 246 return (ret); 247 } 248