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