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