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