1 #include "k5-int.h" 2 #include "int-proto.h" 3 4 static void 5 init_common(krb5_get_init_creds_opt *opt) 6 { 7 opt->flags |= KRB5_GET_INIT_CREDS_OPT_CHG_PWD_PRMPT; 8 } 9 10 void KRB5_CALLCONV 11 krb5_get_init_creds_opt_init(krb5_get_init_creds_opt *opt) 12 { 13 opt->flags = 0; 14 init_common(opt); 15 } 16 17 void KRB5_CALLCONV 18 krb5_get_init_creds_opt_set_tkt_life(krb5_get_init_creds_opt *opt, krb5_deltat tkt_life) 19 { 20 opt->flags |= KRB5_GET_INIT_CREDS_OPT_TKT_LIFE; 21 opt->tkt_life = tkt_life; 22 } 23 24 void KRB5_CALLCONV 25 krb5_get_init_creds_opt_set_renew_life(krb5_get_init_creds_opt *opt, krb5_deltat renew_life) 26 { 27 opt->flags |= KRB5_GET_INIT_CREDS_OPT_RENEW_LIFE; 28 opt->renew_life = renew_life; 29 } 30 31 void KRB5_CALLCONV 32 krb5_get_init_creds_opt_set_forwardable(krb5_get_init_creds_opt *opt, int forwardable) 33 { 34 opt->flags |= KRB5_GET_INIT_CREDS_OPT_FORWARDABLE; 35 opt->forwardable = forwardable; 36 } 37 38 void KRB5_CALLCONV 39 krb5_get_init_creds_opt_set_proxiable(krb5_get_init_creds_opt *opt, int proxiable) 40 { 41 opt->flags |= KRB5_GET_INIT_CREDS_OPT_PROXIABLE; 42 opt->proxiable = proxiable; 43 } 44 45 void KRB5_CALLCONV 46 krb5_get_init_creds_opt_set_etype_list(krb5_get_init_creds_opt *opt, krb5_enctype *etype_list, int etype_list_length) 47 { 48 opt->flags |= KRB5_GET_INIT_CREDS_OPT_ETYPE_LIST; 49 opt->etype_list = etype_list; 50 opt->etype_list_length = etype_list_length; 51 } 52 53 void KRB5_CALLCONV 54 krb5_get_init_creds_opt_set_address_list(krb5_get_init_creds_opt *opt, krb5_address **addresses) 55 { 56 opt->flags |= KRB5_GET_INIT_CREDS_OPT_ADDRESS_LIST; 57 opt->address_list = addresses; 58 } 59 60 void KRB5_CALLCONV 61 krb5_get_init_creds_opt_set_preauth_list(krb5_get_init_creds_opt *opt, krb5_preauthtype *preauth_list, int preauth_list_length) 62 { 63 opt->flags |= KRB5_GET_INIT_CREDS_OPT_PREAUTH_LIST; 64 opt->preauth_list = preauth_list; 65 opt->preauth_list_length = preauth_list_length; 66 } 67 68 void KRB5_CALLCONV 69 krb5_get_init_creds_opt_set_salt(krb5_get_init_creds_opt *opt, krb5_data *salt) 70 { 71 opt->flags |= KRB5_GET_INIT_CREDS_OPT_SALT; 72 opt->salt = salt; 73 } 74 75 void KRB5_CALLCONV 76 krb5_get_init_creds_opt_set_change_password_prompt(krb5_get_init_creds_opt *opt, int prompt) 77 { 78 if (prompt) 79 opt->flags |= KRB5_GET_INIT_CREDS_OPT_CHG_PWD_PRMPT; 80 else 81 opt->flags &= ~KRB5_GET_INIT_CREDS_OPT_CHG_PWD_PRMPT; 82 } 83 84 /* 85 * Extending the krb5_get_init_creds_opt structure. The original 86 * krb5_get_init_creds_opt structure is defined publicly. The 87 * new extended version is private. The original interface 88 * assumed a pre-allocated structure which was passed to 89 * krb5_get_init_creds_init(). The new interface assumes that 90 * the caller will call krb5_get_init_creds_alloc() and 91 * krb5_get_init_creds_free(). 92 * 93 * Callers MUST NOT call krb5_get_init_creds_init() after allocating an 94 * opts structure using krb5_get_init_creds_alloc(). To do so will 95 * introduce memory leaks. Unfortunately, there is no way to enforce 96 * this behavior. 97 * 98 * Two private flags are added for backward compatibility. 99 * KRB5_GET_INIT_CREDS_OPT_EXTENDED says that the structure was allocated 100 * with the new krb5_get_init_creds_opt_alloc() function. 101 * KRB5_GET_INIT_CREDS_OPT_SHADOWED is set to indicate that the extended 102 * structure is a shadow copy of an original krb5_get_init_creds_opt 103 * structure. 104 * If KRB5_GET_INIT_CREDS_OPT_SHADOWED is set after a call to 105 * krb5int_gic_opt_to_opte(), the resulting extended structure should be 106 * freed (using krb5_get_init_creds_free). Otherwise, the original 107 * structure was already extended and there is no need to free it. 108 */ 109 110 /* Forward prototype */ 111 static void 112 free_gic_opt_ext_preauth_data(krb5_context context, 113 krb5_gic_opt_ext *opte); 114 115 static krb5_error_code 116 krb5int_gic_opte_private_alloc(krb5_context context, krb5_gic_opt_ext *opte) 117 { 118 if (NULL == opte || !krb5_gic_opt_is_extended(opte)) 119 return EINVAL; 120 121 opte->opt_private = calloc(1, sizeof(*opte->opt_private)); 122 if (NULL == opte->opt_private) { 123 return ENOMEM; 124 } 125 /* Allocate any private stuff */ 126 opte->opt_private->num_preauth_data = 0; 127 opte->opt_private->preauth_data = NULL; 128 return 0; 129 } 130 131 static krb5_error_code 132 krb5int_gic_opte_private_free(krb5_context context, krb5_gic_opt_ext *opte) 133 { 134 if (NULL == opte || !krb5_gic_opt_is_extended(opte)) 135 return EINVAL; 136 137 /* Free up any private stuff */ 138 if (opte->opt_private->preauth_data != NULL) 139 free_gic_opt_ext_preauth_data(context, opte); 140 free(opte->opt_private); 141 opte->opt_private = NULL; 142 return 0; 143 } 144 145 static krb5_gic_opt_ext * 146 krb5int_gic_opte_alloc(krb5_context context) 147 { 148 krb5_gic_opt_ext *opte; 149 krb5_error_code code; 150 151 opte = calloc(1, sizeof(*opte)); 152 if (NULL == opte) 153 return NULL; 154 opte->flags = KRB5_GET_INIT_CREDS_OPT_EXTENDED; 155 156 code = krb5int_gic_opte_private_alloc(context, opte); 157 if (code) { 158 krb5int_set_error(&context->err, code, 159 "krb5int_gic_opte_alloc: krb5int_gic_opte_private_alloc failed"); 160 free(opte); 161 return NULL; 162 } 163 return(opte); 164 } 165 166 krb5_error_code KRB5_CALLCONV 167 krb5_get_init_creds_opt_alloc(krb5_context context, 168 krb5_get_init_creds_opt **opt) 169 { 170 krb5_gic_opt_ext *opte; 171 172 if (NULL == opt) 173 return EINVAL; 174 *opt = NULL; 175 176 /* 177 * We return a new extended structure cast as a krb5_get_init_creds_opt 178 */ 179 opte = krb5int_gic_opte_alloc(context); 180 if (NULL == opte) 181 return ENOMEM; 182 183 *opt = (krb5_get_init_creds_opt *) opte; 184 init_common(*opt); 185 return 0; 186 } 187 188 void KRB5_CALLCONV 189 krb5_get_init_creds_opt_free(krb5_context context, 190 krb5_get_init_creds_opt *opt) 191 { 192 krb5_gic_opt_ext *opte; 193 194 if (NULL == opt) 195 return; 196 197 /* Don't touch it if we didn't allocate it */ 198 if (!krb5_gic_opt_is_extended(opt)) 199 return; 200 201 opte = (krb5_gic_opt_ext *)opt; 202 if (opte->opt_private) 203 krb5int_gic_opte_private_free(context, opte); 204 205 free(opte); 206 } 207 208 static krb5_error_code 209 krb5int_gic_opte_copy(krb5_context context, 210 krb5_get_init_creds_opt *opt, 211 krb5_gic_opt_ext **opte) 212 { 213 krb5_gic_opt_ext *oe; 214 215 oe = krb5int_gic_opte_alloc(context); 216 if (NULL == oe) 217 return ENOMEM; 218 219 if (opt) 220 memcpy(oe, opt, sizeof(*opt)); 221 222 /* 223 * Fix the flags -- the EXTENDED flag would have been 224 * overwritten by the copy if there was one. The 225 * SHADOWED flag is necessary to ensure that the 226 * krb5_gic_opt_ext structure that was allocated 227 * here will be freed by the library because the 228 * application is unaware of its existence. 229 */ 230 oe->flags |= ( KRB5_GET_INIT_CREDS_OPT_EXTENDED | 231 KRB5_GET_INIT_CREDS_OPT_SHADOWED); 232 233 *opte = oe; 234 return 0; 235 } 236 237 /* 238 * Convert a krb5_get_init_creds_opt pointer to a pointer to 239 * an extended, krb5_gic_opt_ext pointer. If the original 240 * pointer already points to an extended structure, then simply 241 * return the original pointer. Otherwise, if 'force' is non-zero, 242 * allocate an extended structure and copy the original over it. 243 * If the original pointer did not point to an extended structure 244 * and 'force' is zero, then return an error. This is used in 245 * cases where the original *should* be an extended structure. 246 */ 247 krb5_error_code 248 krb5int_gic_opt_to_opte(krb5_context context, 249 krb5_get_init_creds_opt *opt, 250 krb5_gic_opt_ext **opte, 251 unsigned int force, 252 const char *where) 253 { 254 if (!krb5_gic_opt_is_extended(opt)) { 255 if (force) { 256 return krb5int_gic_opte_copy(context, opt, opte); 257 } else { 258 krb5int_set_error(&context->err, EINVAL, 259 "%s: attempt to convert non-extended krb5_get_init_creds_opt", 260 where); 261 return EINVAL; 262 } 263 } 264 /* If it is already extended, just return it */ 265 *opte = (krb5_gic_opt_ext *)opt; 266 return 0; 267 } 268 269 static void 270 free_gic_opt_ext_preauth_data(krb5_context context, 271 krb5_gic_opt_ext *opte) 272 { 273 int i; 274 275 if (NULL == opte || !krb5_gic_opt_is_extended(opte)) 276 return; 277 if (NULL == opte->opt_private || NULL == opte->opt_private->preauth_data) 278 return; 279 280 for (i = 0; i < opte->opt_private->num_preauth_data; i++) { 281 if (opte->opt_private->preauth_data[i].attr != NULL) 282 free(opte->opt_private->preauth_data[i].attr); 283 if (opte->opt_private->preauth_data[i].value != NULL) 284 free(opte->opt_private->preauth_data[i].value); 285 } 286 free(opte->opt_private->preauth_data); 287 opte->opt_private->preauth_data = NULL; 288 opte->opt_private->num_preauth_data = 0; 289 } 290 291 static krb5_error_code 292 add_gic_opt_ext_preauth_data(krb5_context context, 293 krb5_gic_opt_ext *opte, 294 const char *attr, 295 const char *value) 296 { 297 size_t newsize; 298 int i; 299 krb5_gic_opt_pa_data *newpad; 300 301 newsize = opte->opt_private->num_preauth_data + 1; 302 newsize = newsize * sizeof(*opte->opt_private->preauth_data); 303 if (opte->opt_private->preauth_data == NULL) 304 newpad = malloc(newsize); 305 else 306 newpad = realloc(opte->opt_private->preauth_data, newsize); 307 if (newpad == NULL) 308 return ENOMEM; 309 310 i = opte->opt_private->num_preauth_data; 311 newpad[i].attr = strdup(attr); 312 if (newpad[i].attr == NULL) 313 return ENOMEM; 314 newpad[i].value = strdup(value); 315 if (newpad[i].value == NULL) { 316 free(newpad[i].attr); 317 return ENOMEM; 318 } 319 opte->opt_private->num_preauth_data += 1; 320 opte->opt_private->preauth_data = newpad; 321 return 0; 322 } 323 324 /* 325 * This function allows the caller to supply options to preauth 326 * plugins. Preauth plugin modules are given a chance to look 327 * at each option at the time this function is called in ordre 328 * to check the validity of the option. 329 * The 'opt' pointer supplied to this function must have been 330 * obtained using krb5_get_init_creds_opt_alloc() 331 */ 332 krb5_error_code KRB5_CALLCONV 333 krb5_get_init_creds_opt_set_pa(krb5_context context, 334 krb5_get_init_creds_opt *opt, 335 const char *attr, 336 const char *value) 337 { 338 krb5_error_code retval; 339 krb5_gic_opt_ext *opte; 340 341 retval = krb5int_gic_opt_to_opte(context, opt, &opte, 0, 342 "krb5_get_init_creds_opt_set_pa"); 343 if (retval) 344 return retval; 345 346 /* 347 * Copy the option into the extended get_init_creds_opt structure 348 */ 349 retval = add_gic_opt_ext_preauth_data(context, opte, attr, value); 350 if (retval) 351 return retval; 352 353 /* 354 * Give the plugins a chance to look at the option now. 355 */ 356 retval = krb5_preauth_supply_preauth_data(context, opte, attr, value); 357 return retval; 358 } 359 360 /* 361 * This function allows a preauth plugin to obtain preauth 362 * options. The preauth_data returned from this function 363 * should be freed by calling krb5_get_init_creds_opt_free_pa(). 364 * 365 * The 'opt' pointer supplied to this function must have been 366 * obtained using krb5_get_init_creds_opt_alloc() 367 */ 368 krb5_error_code KRB5_CALLCONV 369 krb5_get_init_creds_opt_get_pa(krb5_context context, 370 krb5_get_init_creds_opt *opt, 371 int *num_preauth_data, 372 krb5_gic_opt_pa_data **preauth_data) 373 { 374 krb5_error_code retval; 375 krb5_gic_opt_ext *opte; 376 krb5_gic_opt_pa_data *p = NULL; 377 int i; 378 size_t allocsize; 379 380 retval = krb5int_gic_opt_to_opte(context, opt, &opte, 0, 381 "krb5_get_init_creds_opt_get_pa"); 382 if (retval) 383 return retval; 384 385 if (num_preauth_data == NULL || preauth_data == NULL) 386 return EINVAL; 387 388 *num_preauth_data = 0; 389 *preauth_data = NULL; 390 391 if (opte->opt_private->num_preauth_data == 0) 392 return 0; 393 394 allocsize = 395 opte->opt_private->num_preauth_data * sizeof(krb5_gic_opt_pa_data); 396 p = malloc(allocsize); 397 if (p == NULL) 398 return ENOMEM; 399 400 /* Init these to make cleanup easier */ 401 for (i = 0; i < opte->opt_private->num_preauth_data; i++) { 402 p[i].attr = NULL; 403 p[i].value = NULL; 404 } 405 406 for (i = 0; i < opte->opt_private->num_preauth_data; i++) { 407 p[i].attr = strdup(opte->opt_private->preauth_data[i].attr); 408 p[i].value = strdup(opte->opt_private->preauth_data[i].value); 409 if (p[i].attr == NULL || p[i].value == NULL) 410 goto cleanup; 411 } 412 *num_preauth_data = i; 413 *preauth_data = p; 414 return 0; 415 cleanup: 416 for (i = 0; i < opte->opt_private->num_preauth_data; i++) { 417 if (p[i].attr != NULL) 418 free(p[i].attr); 419 if (p[i].value != NULL) 420 free(p[i].value); 421 } 422 free(p); 423 return ENOMEM; 424 } 425 426 /* 427 * This function frees the preauth_data that was returned by 428 * krb5_get_init_creds_opt_get_pa(). 429 */ 430 void KRB5_CALLCONV 431 krb5_get_init_creds_opt_free_pa(krb5_context context, 432 int num_preauth_data, 433 krb5_gic_opt_pa_data *preauth_data) 434 { 435 int i; 436 437 if (num_preauth_data <= 0 || preauth_data == NULL) 438 return; 439 440 for (i = 0; i < num_preauth_data; i++) { 441 if (preauth_data[i].attr != NULL) 442 free(preauth_data[i].attr); 443 if (preauth_data[i].value != NULL) 444 free(preauth_data[i].value); 445 } 446 free(preauth_data); 447 } 448