1 /* 2 * Copyright 2006 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 /* 9 ** set password functions added by Paul W. Nelson, Thursby Software Systems, Inc. 10 */ 11 #include <string.h> 12 13 #include "k5-int.h" 14 /* #include "krb5_err.h" gtb */ 15 #include "auth_con.h" 16 17 18 krb5_error_code 19 krb5int_mk_chpw_req(krb5_context context, krb5_auth_context auth_context, krb5_data *ap_req, char *passwd, krb5_data *packet) 20 { 21 krb5_error_code ret = 0; 22 krb5_data clearpw; 23 krb5_data cipherpw; 24 krb5_replay_data replay; 25 char *ptr; 26 27 cipherpw.data = NULL; 28 29 if ((ret = krb5_auth_con_setflags(context, auth_context, 30 KRB5_AUTH_CONTEXT_DO_SEQUENCE))) 31 goto cleanup; 32 33 clearpw.length = strlen(passwd); 34 clearpw.data = passwd; 35 36 if ((ret = krb5_mk_priv(context, auth_context, 37 &clearpw, &cipherpw, &replay))) 38 goto cleanup; 39 40 packet->length = 6 + ap_req->length + cipherpw.length; 41 packet->data = (char *) malloc(packet->length); 42 if (packet->data == NULL) 43 { 44 ret = ENOMEM; 45 goto cleanup; 46 } 47 ptr = packet->data; 48 49 /* length */ 50 51 *ptr++ = (packet->length>>8) & 0xff; 52 *ptr++ = packet->length & 0xff; 53 54 /* version == 0x0001 big-endian */ 55 56 *ptr++ = 0; 57 *ptr++ = 1; 58 59 /* ap_req length, big-endian */ 60 61 *ptr++ = (ap_req->length>>8) & 0xff; 62 *ptr++ = ap_req->length & 0xff; 63 64 /* ap-req data */ 65 66 memcpy(ptr, ap_req->data, ap_req->length); 67 ptr += ap_req->length; 68 69 /* krb-priv of password */ 70 71 memcpy(ptr, cipherpw.data, cipherpw.length); 72 73 cleanup: 74 if(cipherpw.data != NULL) /* allocated by krb5_mk_priv */ 75 free(cipherpw.data); 76 77 return(ret); 78 } 79 80 krb5_error_code 81 krb5int_rd_chpw_rep(krb5_context context, krb5_auth_context auth_context, krb5_data *packet, int *result_code, krb5_data *result_data) 82 { 83 char *ptr; 84 int plen, vno; 85 krb5_data ap_rep; 86 krb5_ap_rep_enc_part *ap_rep_enc; 87 krb5_error_code ret; 88 krb5_data cipherresult; 89 krb5_data clearresult; 90 krb5_error *krberror; 91 krb5_replay_data replay; 92 krb5_keyblock *tmp; 93 94 if (packet->length < 4) 95 /* either this, or the server is printing bad messages, 96 or the caller passed in garbage */ 97 return(KRB5KRB_AP_ERR_MODIFIED); 98 99 ptr = packet->data; 100 101 /* verify length */ 102 103 plen = (*ptr++ & 0xff); 104 plen = (plen<<8) | (*ptr++ & 0xff); 105 106 if (plen != packet->length) 107 return(KRB5KRB_AP_ERR_MODIFIED); 108 109 /* verify version number */ 110 111 vno = (*ptr++ & 0xff); 112 vno = (vno<<8) | (*ptr++ & 0xff); 113 114 if (vno != 1) 115 return(KRB5KDC_ERR_BAD_PVNO); 116 117 /* read, check ap-rep length */ 118 119 ap_rep.length = (*ptr++ & 0xff); 120 ap_rep.length = (ap_rep.length<<8) | (*ptr++ & 0xff); 121 122 if (ptr + ap_rep.length >= packet->data + packet->length) 123 return(KRB5KRB_AP_ERR_MODIFIED); 124 125 if (ap_rep.length) { 126 /* verify ap_rep */ 127 ap_rep.data = ptr; 128 ptr += ap_rep.length; 129 130 /* 131 * Save send_subkey to later smash recv_subkey. 132 */ 133 ret = krb5_auth_con_getsendsubkey(context, auth_context, &tmp); 134 if (ret) 135 return ret; 136 137 ret = krb5_rd_rep(context, auth_context, &ap_rep, &ap_rep_enc); 138 if (ret) { 139 krb5_free_keyblock(context, tmp); 140 return(ret); 141 } 142 143 krb5_free_ap_rep_enc_part(context, ap_rep_enc); 144 145 /* extract and decrypt the result */ 146 147 cipherresult.data = ptr; 148 cipherresult.length = (packet->data + packet->length) - ptr; 149 150 /* 151 * Smash recv_subkey to be send_subkey, per spec. 152 */ 153 ret = krb5_auth_con_setrecvsubkey(context, auth_context, tmp); 154 krb5_free_keyblock(context, tmp); 155 if (ret) 156 return ret; 157 158 ret = krb5_rd_priv(context, auth_context, &cipherresult, &clearresult, 159 &replay); 160 161 if (ret) 162 return(ret); 163 } else { 164 cipherresult.data = ptr; 165 cipherresult.length = (packet->data + packet->length) - ptr; 166 167 if ((ret = krb5_rd_error(context, &cipherresult, &krberror))) 168 return(ret); 169 170 clearresult = krberror->e_data; 171 } 172 173 if (clearresult.length < 2) { 174 ret = KRB5KRB_AP_ERR_MODIFIED; 175 goto cleanup; 176 } 177 178 ptr = clearresult.data; 179 180 *result_code = (*ptr++ & 0xff); 181 *result_code = (*result_code<<8) | (*ptr++ & 0xff); 182 183 if ((*result_code < KRB5_KPASSWD_SUCCESS) || 184 (*result_code > KRB5_KPASSWD_INITIAL_FLAG_NEEDED)) { 185 ret = KRB5KRB_AP_ERR_MODIFIED; 186 goto cleanup; 187 } 188 189 /* all success replies should be authenticated/encrypted */ 190 191 if ((ap_rep.length == 0) && (*result_code == KRB5_KPASSWD_SUCCESS)) { 192 ret = KRB5KRB_AP_ERR_MODIFIED; 193 goto cleanup; 194 } 195 196 result_data->length = (clearresult.data + clearresult.length) - ptr; 197 198 if (result_data->length) { 199 result_data->data = (char *) malloc(result_data->length); 200 if (result_data->data == NULL) { 201 ret = ENOMEM; 202 goto cleanup; 203 } 204 memcpy(result_data->data, ptr, result_data->length); 205 } else { 206 result_data->data = NULL; 207 } 208 209 ret = 0; 210 211 cleanup: 212 if (ap_rep.length) { 213 krb5_xfree(clearresult.data); 214 } else { 215 krb5_free_error(context, krberror); 216 } 217 218 return(ret); 219 } 220 221 krb5_error_code KRB5_CALLCONV 222 krb5_chpw_result_code_string(krb5_context context, int result_code, char **code_string) 223 { 224 switch (result_code) { 225 case KRB5_KPASSWD_MALFORMED: 226 *code_string = "Malformed request error"; 227 break; 228 case KRB5_KPASSWD_HARDERROR: 229 *code_string = "Server error"; 230 break; 231 case KRB5_KPASSWD_AUTHERROR: 232 *code_string = "Authentication error"; 233 break; 234 case KRB5_KPASSWD_SOFTERROR: 235 *code_string = "Password change rejected"; 236 break; 237 default: 238 *code_string = "Password change failed"; 239 break; 240 } 241 242 return(0); 243 } 244 245 krb5_error_code 246 krb5int_mk_setpw_req( 247 krb5_context context, 248 krb5_auth_context auth_context, 249 krb5_data *ap_req, 250 krb5_principal targprinc, 251 char *passwd, 252 krb5_data *packet ) 253 { 254 krb5_error_code ret; 255 krb5_data cipherpw; 256 krb5_data *encoded_setpw; 257 258 char *ptr; 259 260 cipherpw.data = NULL; 261 cipherpw.length = 0; 262 263 if ((ret = krb5_auth_con_setflags(context, auth_context, 264 KRB5_AUTH_CONTEXT_DO_SEQUENCE))) 265 return(ret); 266 267 ret = encode_krb5_setpw_req(targprinc, passwd, &encoded_setpw); 268 if (ret) { 269 return ret; 270 } 271 272 if ( (ret = krb5_mk_priv(context, auth_context, encoded_setpw, &cipherpw, NULL)) != 0) { 273 krb5_free_data( context, encoded_setpw); 274 return(ret); 275 } 276 krb5_free_data( context, encoded_setpw); 277 278 279 packet->length = 6 + ap_req->length + cipherpw.length; 280 packet->data = (char *) malloc(packet->length); 281 if (packet->data == NULL) { 282 ret = ENOMEM; 283 goto cleanup; 284 } 285 ptr = packet->data; 286 /* 287 ** build the packet - 288 */ 289 /* put in the length */ 290 *ptr++ = (packet->length>>8) & 0xff; 291 *ptr++ = packet->length & 0xff; 292 /* put in the version */ 293 *ptr++ = (char)0xff; 294 *ptr++ = (char)0x80; 295 /* the ap_req length is big endian */ 296 *ptr++ = (ap_req->length>>8) & 0xff; 297 *ptr++ = ap_req->length & 0xff; 298 /* put in the request data */ 299 memcpy(ptr, ap_req->data, ap_req->length); 300 ptr += ap_req->length; 301 /* 302 ** put in the "private" password data - 303 */ 304 memcpy(ptr, cipherpw.data, cipherpw.length); 305 ret = 0; 306 cleanup: 307 if (cipherpw.data) 308 krb5_free_data_contents(context, &cipherpw); 309 if ((ret != 0) && packet->data) { 310 free( packet->data); 311 packet->data = NULL; 312 } 313 return ret; 314 } 315 316 krb5_error_code 317 krb5int_rd_setpw_rep( krb5_context context, krb5_auth_context auth_context, krb5_data *packet, 318 int *result_code, krb5_data *result_data ) 319 { 320 char *ptr; 321 unsigned int message_length, version_number; 322 krb5_data ap_rep; 323 krb5_ap_rep_enc_part *ap_rep_enc; 324 krb5_error_code ret; 325 krb5_data cipherresult; 326 krb5_data clearresult; 327 krb5_keyblock *tmpkey; 328 /* 329 ** validate the packet length - 330 */ 331 if (packet->length < 4) 332 return(KRB5KRB_AP_ERR_MODIFIED); 333 334 ptr = packet->data; 335 336 /* 337 ** see if it is an error 338 */ 339 if (krb5_is_krb_error(packet)) { 340 krb5_error *krberror; 341 if ((ret = krb5_rd_error(context, packet, &krberror))) 342 return(ret); 343 if (krberror->e_data.data == NULL) { 344 ret = ERROR_TABLE_BASE_krb5 + (krb5_error_code) krberror->error; 345 krb5_free_error(context, krberror); 346 return (ret); 347 } 348 clearresult = krberror->e_data; 349 krberror->e_data.data = NULL; /*So we can free it later*/ 350 krberror->e_data.length = 0; 351 krb5_free_error(context, krberror); 352 353 } else { /* Not an error*/ 354 355 /* 356 ** validate the message length - 357 ** length is big endian 358 */ 359 message_length = (((ptr[0] << 8)&0xff) | (ptr[1]&0xff)); 360 ptr += 2; 361 /* 362 ** make sure the message length and packet length agree - 363 */ 364 if (message_length != packet->length) 365 return(KRB5KRB_AP_ERR_MODIFIED); 366 /* 367 ** get the version number - 368 */ 369 version_number = (((ptr[0] << 8)&0xff) | (ptr[1]&0xff)); 370 ptr += 2; 371 /* 372 ** make sure we support the version returned - 373 */ 374 /* 375 ** set password version is 0xff80, change password version is 1 376 */ 377 if (version_number != 0xff80 && version_number != 1) 378 return(KRB5KDC_ERR_BAD_PVNO); 379 /* 380 ** now fill in ap_rep with the reply - 381 */ 382 /* 383 ** get the reply length - 384 */ 385 ap_rep.length = (((ptr[0] << 8)&0xff) | (ptr[1]&0xff)); 386 ptr += 2; 387 /* 388 ** validate ap_rep length agrees with the packet length - 389 */ 390 if (ptr + ap_rep.length >= packet->data + packet->length) 391 return(KRB5KRB_AP_ERR_MODIFIED); 392 /* 393 ** if data was returned, set the ap_rep ptr - 394 */ 395 if( ap_rep.length ) { 396 ap_rep.data = ptr; 397 ptr += ap_rep.length; 398 399 /* 400 * Save send_subkey to later smash recv_subkey. 401 */ 402 ret = krb5_auth_con_getsendsubkey(context, auth_context, &tmpkey); 403 if (ret) 404 return ret; 405 406 ret = krb5_rd_rep(context, auth_context, &ap_rep, &ap_rep_enc); 407 if (ret) { 408 krb5_free_keyblock(context, tmpkey); 409 return(ret); 410 } 411 412 krb5_free_ap_rep_enc_part(context, ap_rep_enc); 413 /* 414 ** now decrypt the result - 415 */ 416 cipherresult.data = ptr; 417 cipherresult.length = (packet->data + packet->length) - ptr; 418 419 /* 420 * Smash recv_subkey to be send_subkey, per spec. 421 */ 422 ret = krb5_auth_con_setrecvsubkey(context, auth_context, tmpkey); 423 krb5_free_keyblock(context, tmpkey); 424 if (ret) 425 return ret; 426 427 ret = krb5_rd_priv(context, auth_context, &cipherresult, &clearresult, 428 NULL); 429 if (ret) 430 return(ret); 431 } /*We got an ap_rep*/ 432 else 433 return (KRB5KRB_AP_ERR_MODIFIED); 434 } /*Response instead of error*/ 435 436 /* 437 ** validate the cleartext length 438 */ 439 if (clearresult.length < 2) { 440 ret = KRB5KRB_AP_ERR_MODIFIED; 441 goto cleanup; 442 } 443 /* 444 ** now decode the result - 445 */ 446 ptr = clearresult.data; 447 448 *result_code = (((ptr[0] << 8)&0xff) | (ptr[1]&0xff)); 449 ptr += 2; 450 451 /* 452 ** result code 5 is access denied 453 */ 454 if ((*result_code < KRB5_KPASSWD_SUCCESS) || (*result_code > 5)) 455 { 456 ret = KRB5KRB_AP_ERR_MODIFIED; 457 goto cleanup; 458 } 459 /* 460 ** all success replies should be authenticated/encrypted 461 */ 462 if( (ap_rep.length == 0) && (*result_code == KRB5_KPASSWD_SUCCESS) ) 463 { 464 ret = KRB5KRB_AP_ERR_MODIFIED; 465 goto cleanup; 466 } 467 468 if (result_data) { 469 result_data->length = (clearresult.data + clearresult.length) - ptr; 470 471 if (result_data->length) 472 { 473 result_data->data = (char *) malloc(result_data->length); 474 if (result_data->data) 475 memcpy(result_data->data, ptr, result_data->length); 476 } 477 else 478 result_data->data = NULL; 479 } 480 ret = 0; 481 482 cleanup: 483 krb5_free_data_contents(context, &clearresult); 484 return(ret); 485 } 486 487 krb5_error_code 488 krb5int_setpw_result_code_string( krb5_context context, int result_code, const char **code_string ) 489 { 490 switch (result_code) 491 { 492 case KRB5_KPASSWD_MALFORMED: 493 *code_string = "Malformed request error"; 494 break; 495 case KRB5_KPASSWD_HARDERROR: 496 *code_string = "Server error"; 497 break; 498 case KRB5_KPASSWD_AUTHERROR: 499 *code_string = "Authentication error"; 500 break; 501 case KRB5_KPASSWD_SOFTERROR: 502 *code_string = "Password change rejected"; 503 break; 504 case 5: /* access denied */ 505 *code_string = "Access denied"; 506 break; 507 case 6: /* bad version */ 508 *code_string = "Wrong protocol version"; 509 break; 510 case 7: /* initial flag is needed */ 511 *code_string = "Initial password required"; 512 break; 513 case 0: 514 *code_string = "Success"; 515 default: 516 *code_string = "Password change failed"; 517 break; 518 } 519 520 return(0); 521 } 522 523