1 #include "k5-int.h"
2 #include "int-proto.h"
3
4 static void
init_common(krb5_get_init_creds_opt * opt)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
krb5_get_init_creds_opt_init(krb5_get_init_creds_opt * opt)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
krb5_get_init_creds_opt_set_tkt_life(krb5_get_init_creds_opt * opt,krb5_deltat tkt_life)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
krb5_get_init_creds_opt_set_renew_life(krb5_get_init_creds_opt * opt,krb5_deltat renew_life)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
krb5_get_init_creds_opt_set_forwardable(krb5_get_init_creds_opt * opt,int forwardable)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
krb5_get_init_creds_opt_set_proxiable(krb5_get_init_creds_opt * opt,int proxiable)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
krb5_get_init_creds_opt_set_etype_list(krb5_get_init_creds_opt * opt,krb5_enctype * etype_list,int etype_list_length)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
krb5_get_init_creds_opt_set_address_list(krb5_get_init_creds_opt * opt,krb5_address ** addresses)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
krb5_get_init_creds_opt_set_preauth_list(krb5_get_init_creds_opt * opt,krb5_preauthtype * preauth_list,int preauth_list_length)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
krb5_get_init_creds_opt_set_salt(krb5_get_init_creds_opt * opt,krb5_data * salt)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
krb5_get_init_creds_opt_set_change_password_prompt(krb5_get_init_creds_opt * opt,int prompt)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
krb5int_gic_opte_private_alloc(krb5_context context,krb5_gic_opt_ext * opte)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
krb5int_gic_opte_private_free(krb5_context context,krb5_gic_opt_ext * opte)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 *
krb5int_gic_opte_alloc(krb5_context context)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
krb5_get_init_creds_opt_alloc(krb5_context context,krb5_get_init_creds_opt ** opt)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
krb5_get_init_creds_opt_free(krb5_context context,krb5_get_init_creds_opt * opt)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
krb5int_gic_opte_copy(krb5_context context,krb5_get_init_creds_opt * opt,krb5_gic_opt_ext ** opte)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
krb5int_gic_opt_to_opte(krb5_context context,krb5_get_init_creds_opt * opt,krb5_gic_opt_ext ** opte,unsigned int force,const char * where)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
free_gic_opt_ext_preauth_data(krb5_context context,krb5_gic_opt_ext * opte)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
add_gic_opt_ext_preauth_data(krb5_context context,krb5_gic_opt_ext * opte,const char * attr,const char * value)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
krb5_get_init_creds_opt_set_pa(krb5_context context,krb5_get_init_creds_opt * opt,const char * attr,const char * value)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
krb5_get_init_creds_opt_get_pa(krb5_context context,krb5_get_init_creds_opt * opt,int * num_preauth_data,krb5_gic_opt_pa_data ** preauth_data)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
krb5_get_init_creds_opt_free_pa(krb5_context context,int num_preauth_data,krb5_gic_opt_pa_data * preauth_data)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